discovery-media-player 0.1.167 → 0.1.168
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/HOST-CONTRACT.md +32 -4
- package/package.json +2 -1
- package/server/cache.js +18 -1
package/docs/HOST-CONTRACT.md
CHANGED
|
@@ -182,10 +182,10 @@ these numbers from their side.
|
|
|
182
182
|
| `fenetreS` | seconds this process has been running — **the window every total below was counted over** |
|
|
183
183
|
| `seauxMs` | the bucket ladder the percentiles are read off, published **with** the numbers |
|
|
184
184
|
| `familles` | **the denominator of `routes`** — every family this build measures, whether or not it was exercised. It does not move with traffic; that is what makes it a denominator |
|
|
185
|
-
| `routes` | one entry per family of work — `document`, `presentation`, `action`, `fichier`, `carte`, `autre`. Families absent from the object were never exercised in this process |
|
|
185
|
+
| `routes` | one entry per family of work — `document`, `presentation`, `action`, `fichier`, `carte`, `autre`. Families absent from the object were never exercised in this process. ⚠️ **`familles` is the scale, `routes` is the measure: `familles` never varies; what was seen is in `routes`.** A host displayed the scale as the measure for a whole day — "6 familles", permanently, on an instance that had just restarted — because nothing on the card said which of the two was which (STUDIO, 14/09) |
|
|
186
186
|
| `base` | the same shape, for calls through the `db` capability **you** supply — measured at the seam, so it covers every call, including ones nobody has written yet |
|
|
187
187
|
| `statuts` | responses by class: `ok` (<400), `refus4xx`, `debit429`, `occupe503`, `erreur5xx` |
|
|
188
|
-
| `memoireMio` | `rss`, `heap` (heap used), `tampons` (`arrayBuffers`) in MiB, read at the moment of the request. ⚠️ **Half a number**: it is only judicable against the memory ceiling of the process, which the player does not know and no platform serves the same way — on Lambda-based functions (Vercel included) read `AWS_LAMBDA_FUNCTION_MEMORY_SIZE`; in a container, the cgroup limit. A host spent half a day finding that its project API, its logs and its `vercel.json` all left it out (14/09). Display the ceiling beside the RSS, or the RSS says nothing about the relay ceiling you can afford |
|
|
188
|
+
| `memoireMio` | `rss`, `heap` (heap used), `tampons` (`arrayBuffers`) in MiB, read at the moment of the request. ⚠️ **Half a number**: it is only judicable against the memory ceiling of the process, which the player does not know and no platform serves the same way — on Lambda-based functions (Vercel included) read `AWS_LAMBDA_FUNCTION_MEMORY_SIZE`; in a container, the cgroup limit. A host spent half a day finding that its project API, its logs and its `vercel.json` all left it out (14/09). Display the ceiling beside the RSS, or the RSS says nothing about the relay ceiling you can afford. And the ceiling makes the RSS **comparable, not the question decidable**: whether 64 relays fit is settled only by a reading under load, never by a gauge read at rest (STUDIO, 14/09) |
|
|
189
189
|
| `boucleMs` | event-loop **delay** — `moyen` and `p99` in ms, with `n` samples and the sampler's `resolutionMs` |
|
|
190
190
|
|
|
191
191
|
⚠️ **A percentile over buckets is a bound, not a value.** `p95sousMs: 250` reads *"95% of calls
|
|
@@ -284,6 +284,15 @@ by the card, structured and dated, and never by a search through logs for `relai
|
|
|
284
284
|
log line stays for diagnosis, the card is the way to *notice*. Process-local like everything on this
|
|
285
285
|
card, and **never reset by `init`**, exactly like the counter of open relays.
|
|
286
286
|
|
|
287
|
+
⚠️ **A zero is informative only beside the traffic that could have produced the event — `fenetreS`
|
|
288
|
+
alone is not enough.** A host read `total: 0` over a 999-second window and showed *no relay
|
|
289
|
+
refused*, *no saturated read* in green; over that window `mesures.routes` carried only `action`
|
|
290
|
+
(3 calls) — not one file, not one presentation read, so neither counter had had a single occasion
|
|
291
|
+
(STUDIO, 14/09). Read `mesures.routes.fichier.n` next to `relaisRefuses` and
|
|
292
|
+
`mesures.routes.presentation.n` next to `lectureSaturee` — the families are `familleDe`'s, not
|
|
293
|
+
guessed — and the timer wraps the whole handler, so `n` counts the refused calls too: it is the
|
|
294
|
+
denominator you want. Both hosts' cards now say *no occasion yet* instead of green.
|
|
295
|
+
|
|
287
296
|
⚠️ **Before you upgrade, do not read `presenceDurcissement` or `presenceFusion`.** They are *reports
|
|
288
297
|
of execution*: on an instance where nothing is running they say `inconnu`, which means *nobody
|
|
289
298
|
looked* — not *the migration is there*. A pre-flight check built on one of them silently passes on
|
|
@@ -647,7 +656,21 @@ context shipped in this package already implements it** — if you build your co
|
|
|
647
656
|
`discovery-media-player/context/standalone`, you get it on your next upgrade and there is nothing
|
|
648
657
|
to decide or write. This section is for a host that implements the `db` capability itself. A host
|
|
649
658
|
asked which of the two it was, and the answer was missing from this page: *"the two look alike in
|
|
650
|
-
your code and not at all alike at your hosts."*
|
|
659
|
+
your code and not at all alike at your hosts."* ⚠️ **The same line decides which zone of a release
|
|
660
|
+
reaches you, and it is read per capability, not per host.** Three forms. A host that runs
|
|
661
|
+
`context/standalone` as is (ADV does, unchanged since August — **read in its public wiring
|
|
662
|
+
repository, not taken from a message**: for a host whose wiring is public, the file is the source,
|
|
663
|
+
and a replaced context would show in a commit before it showed in a message) executes every change to the `context`
|
|
664
|
+
zone — the environment pass-through, the journal helper — and its `errors.capture` is the player's
|
|
665
|
+
own. A host that **composes** its context from `createStandaloneContext` and replaces some
|
|
666
|
+
capabilities (the Vercel example in this repository does: `identity` and `branding` are its own,
|
|
667
|
+
everything else inherited) is reached by every change to a capability it inherits, and by none to a
|
|
668
|
+
capability it replaced; `creerLimites` is exported for exactly that host. A host whose context
|
|
669
|
+
imports nothing from `context/` (STUDIO) is touched by `server/` only. That line was missing from
|
|
670
|
+
what the player held about its hosts, and a release note told one of them a change to its own file
|
|
671
|
+
was "without effect on your side" (14/09); the first version of this paragraph was binary, and an
|
|
672
|
+
audit pointed at the repository's own example as the third case. Say your form once; it is the
|
|
673
|
+
line the notes are written from. If your `db` capability
|
|
651
674
|
exposes it, the player asks it first and publishes an **exact** count — no bound, no `tronque`, and
|
|
652
675
|
no rows transported at all. If it is absent, everything above still applies unchanged: the bounded
|
|
653
676
|
read with its cursor probe. **That fallback is the whole design.** Third-party hosts implement this
|
|
@@ -888,7 +911,12 @@ per process before answering **503 busy** to everyone. A database call that hang
|
|
|
888
911
|
"slow", it is an availability incident for the whole instance — the exact mechanism an audit
|
|
889
912
|
reproduced inside the test suite with a never-settling promise (13/09). Time out your own calls
|
|
890
913
|
(the standalone context bounds its own with `AbortSignal`), and never return a promise you cannot
|
|
891
|
-
guarantee will settle.
|
|
914
|
+
guarantee will settle. ⚠️ **The bound must cover the body, not only the headers.** `fetch` resolves
|
|
915
|
+
as soon as the headers arrive; `response.text()` then hangs on a stream left open, so a timeout
|
|
916
|
+
that stops at the headers bounds half the path. Pass the same `AbortSignal` to the fetch, which
|
|
917
|
+
aborts the body read too (the standalone context does), and if you retry an abandoned call, retry a
|
|
918
|
+
read only, never a write. A host (STUDIO, 13/09) found its own `db.request` bounded that way — at
|
|
919
|
+
the headers — and rewrote it; the rule is theirs.
|
|
892
920
|
|
|
893
921
|
**Your document-opening doors reappear.** A host has more than one place that opens a file, and new
|
|
894
922
|
ones get written. Keep the list and hunt it periodically — and note that **your search criteria
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "discovery-media-player",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.168",
|
|
4
4
|
"description": "Self-hosted document viewer: per-recipient tracked links, reading analytics, live presentation. The core knows nothing about the application hosting it — everything it borrows arrives through an injected context.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pdf-viewer",
|
|
@@ -84,6 +84,7 @@
|
|
|
84
84
|
"devDependencies": {
|
|
85
85
|
"@eslint/js": "^10.0.1",
|
|
86
86
|
"@vitest/coverage-v8": "^4.1.11",
|
|
87
|
+
"ajv": "8.20.0",
|
|
87
88
|
"axe-core": "^4.13.0",
|
|
88
89
|
"dockerfile-ast": "0.7.1",
|
|
89
90
|
"esbuild": "^0.28.2",
|
package/server/cache.js
CHANGED
|
@@ -85,6 +85,12 @@ function creerCache(options) {
|
|
|
85
85
|
const entrees = new Map();
|
|
86
86
|
let poidsTotal = 0;
|
|
87
87
|
let nEnVol = 0; // tenu à jour à chaque transition — compter la Map à la demande serait O(n)
|
|
88
|
+
// ⚠️ CE QUE LE CACHE A FAIT, PAS SEULEMENT CE QU'IL A REFUSÉ. `satures()` disait les refus ; rien ne
|
|
89
|
+
// disait combien de lectures avaient été servies de la mémoire, regroupées sur une production en
|
|
90
|
+
// vol, ou produites. L'artefact de charge (audit, 14/09) demande les trois et le pic en vol :
|
|
91
|
+
// sans eux, « le cache tient » est une phrase, pas une mesure. Compteurs de processus, comme tout
|
|
92
|
+
// ici : jamais remis à zéro, à lire en deltas.
|
|
93
|
+
let nServies = 0, nRegroupees = 0, nProduites = 0, picEnVol = 0;
|
|
88
94
|
|
|
89
95
|
const oublier = (k) => {
|
|
90
96
|
const e = entrees.get(k);
|
|
@@ -127,7 +133,10 @@ function creerCache(options) {
|
|
|
127
133
|
// qui décide : refuser d'abord ferait échouer des appelants que le regroupement pouvait servir
|
|
128
134
|
// gratuitement — la saturation punirait alors la rafale légitime, exactement ce que ce cache
|
|
129
135
|
// existe pour absorber. On ne refuse que ce qui coûterait une requête DE PLUS.
|
|
130
|
-
if (vue && (vue.enVol || vue.echeance > t))
|
|
136
|
+
if (vue && (vue.enVol || vue.echeance > t)) {
|
|
137
|
+
if (vue.enVol) nRegroupees += 1; else nServies += 1;
|
|
138
|
+
return vue.promesse;
|
|
139
|
+
}
|
|
131
140
|
|
|
132
141
|
if (nEnVol >= maxEnVol) {
|
|
133
142
|
nSatures += 1;
|
|
@@ -143,6 +152,8 @@ function creerCache(options) {
|
|
|
143
152
|
let placeRendue = false;
|
|
144
153
|
const rendrePlace = () => { if (!placeRendue) { placeRendue = true; nEnVol -= 1; } };
|
|
145
154
|
nEnVol += 1;
|
|
155
|
+
nProduites += 1;
|
|
156
|
+
if (nEnVol > picEnVol) picEnVol = nEnVol;
|
|
146
157
|
// ⚠️ L'ÉCHÉANCE PART DE LA RÉSOLUTION, PAS DE LA DEMANDE. Posée à la demande, elle expirait
|
|
147
158
|
// AVANT que la production ne réponde dès que celle-ci dépassait le TTL — et le regroupement ne
|
|
148
159
|
// servait alors plus à rien pour exactement les producteurs lents, les seuls qu'il valait la
|
|
@@ -193,6 +204,12 @@ function creerCache(options) {
|
|
|
193
204
|
poids: () => poidsTotal,
|
|
194
205
|
/** Demandes actuellement en vol — ce que le plafond d'admission borne. */
|
|
195
206
|
enVol: () => nEnVol,
|
|
207
|
+
/**
|
|
208
|
+
* Ce que ce cache a fait depuis le démarrage du processus : `hits` servies de la mémoire,
|
|
209
|
+
* `coalesced` regroupées sur une production déjà en vol, `misses` produites, `peakInFlight` le
|
|
210
|
+
* plus grand nombre de productions simultanées. Les refus sont dans `satures()`.
|
|
211
|
+
*/
|
|
212
|
+
compteurs: () => ({ hits: nServies, coalesced: nRegroupees, misses: nProduites, peakInFlight: picEnVol }),
|
|
196
213
|
/**
|
|
197
214
|
* Ce que le plafond a refusé depuis le démarrage de ce processus.
|
|
198
215
|
*
|