feat(planning): grille hebdomadaire complète avec API et filtres

- Connexion API via proxy Angular (résolution CORS, base path /api)
- Import CSS ng-zorro global pour les modales et composants
- Filtres Camion/Show câblés sur l'affichage de la grille
- Camions affichés via TrucksService (linkés au show du même créneau)
- Panneau de détails : spectacles + camions du jour sélectionné
- Modale de création de spectacle stylisée avec fond et centrage
- Positionnement précis des events à la minute dans leur créneau
- Auto-scroll vers l'heure courante au chargement
- Ligne "maintenant" sur la colonne du jour actuel
- Régénération des services OpenAPI (nouveaux noms de types)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 20:36:03 +02:00
parent 150b97cd2e
commit 654b297e2e
3131 changed files with 149304 additions and 104334 deletions
+63 -33
View File
@@ -5,21 +5,14 @@ const { Minipass } = require('minipass')
const SPEC_ALGORITHMS = ['sha512', 'sha384', 'sha256']
const DEFAULT_ALGORITHMS = ['sha512']
const NODE_HASHES = crypto.getHashes()
// TODO: this should really be a hardcoded list of algorithms we support, rather than [a-z0-9].
// TODO: this should really be a hardcoded list of algorithms we support,
// rather than [a-z0-9].
const BASE64_REGEX = /^[a-z0-9+/]+(?:=?=?)$/i
const SRI_REGEX = /^([a-z0-9]+)-([^?]+)(\?[?\S*]*)?$/
const SRI_REGEX = /^([a-z0-9]+)-([^?]+)([?\S*]*)$/
const STRICT_SRI_REGEX = /^([a-z0-9]+)-([A-Za-z0-9+/=]{44,88})(\?[\x21-\x7E]*)?$/
const VCHAR_REGEX = /^[\x21-\x7E]+$/
// This is a Best Effort™ at a reasonable priority for hash algos
const DEFAULT_PRIORITY = [
'md5', 'whirlpool', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512',
// TODO - it's unclear _which_ of these Node will actually use as its name for the algorithm, so we guesswork it based on the OpenSSL names.
'sha3', 'sha3-256', 'sha3-384', 'sha3-512', 'sha3_256', 'sha3_384', 'sha3_512',
].filter(algo => NODE_HASHES.includes(algo))
const getOptString = options => options?.length ? `?${options.join('?')}` : ''
class IntegrityStream extends Minipass {
@@ -106,6 +99,7 @@ class IntegrityStream extends Minipass {
// Integrity verification mode
const match = this.goodSri && newSri.match(this.sri, this.opts)
if (typeof this.expectedSize === 'number' && this.size !== this.expectedSize) {
/* eslint-disable-next-line max-len */
const err = new Error(`stream size mismatch when checking ${this.sri}.\n Wanted: ${this.expectedSize}\n Found: ${this.size}`)
err.code = 'EBADSIZE'
err.found = this.size
@@ -113,6 +107,7 @@ class IntegrityStream extends Minipass {
err.sri = this.sri
this.emit('error', err)
} else if (this.sri && !match) {
/* eslint-disable-next-line max-len */
const err = new Error(`${this.sri} integrity checksum failed when using ${this.algorithm}: wanted ${this.digests} but got ${newSri}. (${this.size} bytes)`)
err.code = 'EINTEGRITY'
err.found = newSri
@@ -142,7 +137,8 @@ class Hash {
const strict = opts?.strict
this.source = hash.trim()
// set default values so that we make V8 happy to always see a familiar object template.
// set default values so that we make V8 happy to
// always see a familiar object template.
this.digest = ''
this.algorithm = ''
this.options = []
@@ -160,9 +156,6 @@ class Hash {
if (strict && !SPEC_ALGORITHMS.includes(match[1])) {
return
}
if (!NODE_HASHES.includes(match[1])) {
return
}
this.algorithm = match[1]
this.digest = match[2]
@@ -205,12 +198,15 @@ class Hash {
toString (opts) {
if (opts?.strict) {
// Strict mode enforces the standard as close to the foot of the letter as it can.
// Strict mode enforces the standard as close to the foot of the
// letter as it can.
if (!(
// The spec has very restricted productions for algorithms.
// https://www.w3.org/TR/CSP2/#source-list-syntax
SPEC_ALGORITHMS.includes(this.algorithm) &&
// Usually, if someone insists on using a "different" base64, we leave it as-is, since there are multiple standards, and the specified is not a URL-safe variant.
// Usually, if someone insists on using a "different" base64, we
// leave it as-is, since there's multiple standards, and the
// specified is not a URL-safe variant.
// https://www.w3.org/TR/CSP2/#base64_value
this.digest.match(BASE64_REGEX) &&
// Option syntax is strictly visual chars.
@@ -304,7 +300,8 @@ class Integrity {
return parse(this, { single: true }).hexDigest()
}
// add additional hashes to an integrity value, but prevent *changing* an existing integrity hash.
// add additional hashes to an integrity value, but prevent
// *changing* an existing integrity hash.
merge (integrity, opts) {
const other = parse(integrity, opts)
for (const algo in other) {
@@ -326,25 +323,33 @@ class Integrity {
return false
}
const algo = other.pickAlgorithm(opts, Object.keys(this))
return !!algo && this[algo].find(hash =>
other[algo].find(otherhash =>
hash.digest === otherhash.digest
return (
!!algo &&
this[algo] &&
other[algo] &&
this[algo].find(hash =>
other[algo].find(otherhash =>
hash.digest === otherhash.digest
)
)
) || false
}
// Pick the highest priority algorithm present, optionally also limited to a set of hashes found in another integrity.
// When limiting it may return nothing.
// Pick the highest priority algorithm present, optionally also limited to a
// set of hashes found in another integrity. When limiting it may return
// nothing.
pickAlgorithm (opts, hashes) {
const pickAlgorithm = opts?.pickAlgorithm || getPrioritizedHash
let keys = Object.keys(this)
if (hashes?.length) {
keys = keys.filter(k => hashes.includes(k))
}
const keys = Object.keys(this).filter(k => {
if (hashes?.length) {
return hashes.includes(k)
}
return true
})
if (keys.length) {
return keys.reduce((acc, algo) => pickAlgorithm(acc, algo) || acc)
}
// no intersection between this and hashes
// no intersection between this and hashes,
return null
}
}
@@ -375,7 +380,7 @@ function _parse (integrity, opts) {
const hash = new Hash(string, opts)
if (hash.algorithm && hash.digest) {
const algo = hash.algorithm
if (!Object.keys(acc).includes(algo)) {
if (!acc[algo]) {
acc[algo] = []
}
acc[algo].push(hash)
@@ -416,7 +421,9 @@ function fromData (data, opts) {
`${algo}-${digest}${optString}`,
opts
)
// istanbul ignore else - it would be VERY strange if the string we just calculated with an algo did not have an algo or digest.
/* istanbul ignore else - it would be VERY strange if the string we
* just calculated with an algo did not have an algo or digest.
*/
if (hash.algorithm && hash.digest) {
const hashAlgo = hash.algorithm
if (!acc[hashAlgo]) {
@@ -466,6 +473,7 @@ function checkData (data, sri, opts) {
if (match || !(opts.error)) {
return match
} else if (typeof opts.size === 'number' && (data.length !== opts.size)) {
/* eslint-disable-next-line max-len */
const err = new Error(`data size mismatch when checking ${sri}.\n Wanted: ${opts.size}\n Found: ${data.length}`)
err.code = 'EBADSIZE'
err.found = data.length
@@ -473,6 +481,7 @@ function checkData (data, sri, opts) {
err.sri = sri
throw err
} else {
/* eslint-disable-next-line max-len */
const err = new Error(`Integrity checksum failed when using ${algorithm}: Wanted ${sri}, but got ${newSri}. (${data.length} bytes)`)
err.code = 'EINTEGRITY'
err.found = newSri
@@ -529,11 +538,20 @@ function createIntegrity (opts) {
digest: function () {
const integrity = algorithms.reduce((acc, algo) => {
const digest = hashes.shift().digest('base64')
const hash = new Hash(`${algo}-${digest}${optString}`, opts)
if (!acc[hash.algorithm]) {
acc[hash.algorithm] = []
const hash = new Hash(
`${algo}-${digest}${optString}`,
opts
)
/* istanbul ignore else - it would be VERY strange if the hash we
* just calculated with an algo did not have an algo or digest.
*/
if (hash.algorithm && hash.digest) {
const hashAlgo = hash.algorithm
if (!acc[hashAlgo]) {
acc[hashAlgo] = []
}
acc[hashAlgo].push(hash)
}
acc[hash.algorithm].push(hash)
return acc
}, new Integrity())
@@ -542,6 +560,18 @@ function createIntegrity (opts) {
}
}
const NODE_HASHES = crypto.getHashes()
// This is a Best Effort™ at a reasonable priority for hash algos
const DEFAULT_PRIORITY = [
'md5', 'whirlpool', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512',
// TODO - it's unclear _which_ of these Node will actually use as its name
// for the algorithm, so we guesswork it based on the OpenSSL names.
'sha3',
'sha3-256', 'sha3-384', 'sha3-512',
'sha3_256', 'sha3_384', 'sha3_512',
].filter(algo => NODE_HASHES.includes(algo))
function getPrioritizedHash (algo1, algo2) {
/* eslint-disable-next-line max-len */
return DEFAULT_PRIORITY.indexOf(algo1.toLowerCase()) >= DEFAULT_PRIORITY.indexOf(algo2.toLowerCase())
+17 -17
View File
@@ -1,6 +1,6 @@
{
"name": "ssri",
"version": "13.0.1",
"version": "12.0.0",
"description": "Standard Subresource Integrity library -- parses, serializes, generates, and verifies integrity metadata according to the SRI spec.",
"main": "lib/index.js",
"files": [
@@ -11,16 +11,21 @@
"prerelease": "npm t",
"postrelease": "npm publish",
"posttest": "npm run lint",
"test": "node --test './test/**/*.js'",
"test": "tap",
"coverage": "tap",
"lint": "npm run eslint",
"postlint": "template-oss-check",
"template-oss-apply": "template-oss-apply --force",
"lintfix": "npm run eslint -- --fix",
"snap": "node --test --test-update-snapshots './test/**/*.js'",
"eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\"",
"test:node20": "node --test test",
"test:cover": "node --test --experimental-test-coverage --test-timeout=3000 --test-coverage-lines=100 --test-coverage-functions=100 --test-coverage-branches=100 './test/**/*.js'"
"snap": "tap",
"eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\""
},
"tap": {
"check-coverage": true,
"nyc-arg": [
"--exclude",
"tap-snapshots/**"
]
},
"repository": {
"type": "git",
@@ -46,21 +51,16 @@
"minipass": "^7.0.3"
},
"devDependencies": {
"@npmcli/eslint-config": "^6.0.0",
"@npmcli/template-oss": "4.28.1",
"benchmark": "^2.1.4"
"@npmcli/eslint-config": "^5.0.0",
"@npmcli/template-oss": "4.23.3",
"tap": "^16.0.1"
},
"engines": {
"node": "^20.17.0 || >=22.9.0"
"node": "^18.17.0 || >=20.5.0"
},
"templateOSS": {
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
"version": "4.28.1",
"publish": "true",
"allowPaths": [
"benchmarks/"
],
"testRunner": "node:test",
"latestVersion": 24
"version": "4.23.3",
"publish": "true"
}
}