discovery-media-player 0.1.155 → 0.1.157

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.
@@ -947,6 +947,20 @@ previous version claimed nothing. Four hours after the release, a host measured
947
947
  deployment caps, times out, paginates, or rewrites anything between us and your database, that
948
948
  limit is invisible from here and our arithmetic is probably wrong about it.
949
949
 
950
+ ⚠️ **If you sweep your own reads for this, sort them by what the value BECOMES, not by how many rows
951
+ it holds.** This criterion is not ours: a host swept theirs, closed all of them, and reported that
952
+ the row count had been the wrong axis the whole time. The expensive ones were the **aggregations** —
953
+ a credit balance summed with a `reduce`, a read counter taken as `rows.length`, a visit tally
954
+ incremented per row. A truncated sum does not look truncated: **it is wrong *and* plausible**, which
955
+ is worse than the ceiling's usual symptom, a list that at least renders visibly stale. Their credit
956
+ ledger stood at 503 rows and grows on every AI call — the invoice would have gone wrong before the
957
+ table ever looked big enough to suspect.
958
+
959
+ So the useful question about one of your reads is not *"could this exceed 1000?"* but **"is this
960
+ value aggregated, or displayed?"** A displayed list degrades visibly. An aggregate degrades into a
961
+ number someone will believe. Truncation you have decided to keep is fine and cheap to make honest —
962
+ theirs kept one, bounded to 1000 and stated rather than assumed.
963
+
950
964
  **2. Something this card asserts that you can check against your own database.** Not "does it look
951
965
  right" — *does this number match what a query returns right now*. The purge card is the obvious one:
952
966
  `vide: true` is what authorises dropping a column, so a wrong `true` is expensive and a wrong `0`
@@ -991,6 +1005,51 @@ measurement came from rather than only what it said.
991
1005
  The two kinds want opposite things from us: a debt should be chased, a structural limit should be
992
1006
  written down and stopped being asked about.
993
1007
 
1008
+ ⚠️ **And there is a third kind, which we proposed as a false choice and a host corrected.** We asked
1009
+ another host to sort two backup figures — the PITR window, and the age of the oldest snapshot — into
1010
+ debt or structural limit. Their answer was neither, and the distinction is operational rather than
1011
+ pedantic:
1012
+
1013
+ - **Not measurable by a session.** Checked against two independent toolsets — a second host's and
1014
+ their own — neither the platform API nor the MCP tools expose either figure. The one tool with a
1015
+ near-enough name restores a *paused* project.
1016
+ - **Measurable by a human.** An authenticated dashboard session displays both.
1017
+
1018
+ So it is a debt whose payer is *necessarily a person*, and that is what makes it its own kind: it
1019
+ behaves like a structural limit toward every automated agent (chasing it changes nothing, no session
1020
+ will ever produce it), and like a debt toward the installation (someone can pay it, and until they
1021
+ do, the purge is not datable end to end). Their own phrasing, which we adopt: *"no session can
1022
+ render them; a human can, and until one has, the purge is not datable end to end."* They had raised
1023
+ it with theirs four times.
1024
+
1025
+ The practical consequence for us is a rule about **who we are asking**, which we had never written:
1026
+ a question a host cannot answer with the tools they run on is not answered by asking again. Either
1027
+ it reaches a person, or it should be recorded and dropped.
1028
+
1029
+ ⚠️ **Where the substitution seam is, because a host asked and the answer was not written anywhere.**
1030
+ Their question came from their own defect: they had written a pagination helper after an incident,
1031
+ with its reason at the top, and it was called *nowhere*. The cause turned up only when they tried to
1032
+ use it — it called the **local binding** of their database client, while their benches replace the
1033
+ **export**, so adopting it broke the very bench that covered it. Their sentence is the part worth
1034
+ keeping: *"nobody writes «I don't use it because it breaks my doubles» — you give up in silence."*
1035
+ A non-substitutable helper produces no red, no complaint, no trace. It produces an absence of use,
1036
+ which nothing distinguishes from a need that never existed.
1037
+
1038
+ Asked of us, the answer has two halves and only one of them is a promise:
1039
+
1040
+ - **The injected context is substitutable, and that is the seam.** Every call reads `PLAYER.<member>`
1041
+ at the moment it fires; nothing captures it into a local binding at module load, before you have
1042
+ injected anything. Measured on 2026-09-05 across `server/` and `context/`: **144 calls, 0
1043
+ captures.** Your double of `db.request` is the one that runs. `tools/couture-substituable.mjs`
1044
+ now refuses a capture, so this stays true rather than happening to be true today.
1045
+ - **Our exports are not substitutable among themselves, and we do not promise they are.** Same date,
1046
+ same zones: **64 of 75 exported names are also called internally through their local binding.**
1047
+ If you stub `getShareBySlug` on the module we export and expect `overview()` to see your stub, it
1048
+ will not. That is ordinary CommonJS and we are not changing 64 call sites for it — but you would
1049
+ have discovered it the expensive way, so it is written here instead.
1050
+
1051
+ Bring us the seam you need and cannot get, rather than working around a missing one in silence.
1052
+
994
1053
  **What not to send.** Shapes and counts, never contents. No row data, no reader IPs or User-Agents —
995
1054
  those are the columns half this contract exists to get rid of — no keys, tokens, connection strings,
996
1055
  or private hostnames. *"A table of ~1600 rows returned 1000"* is the whole of what we needed to fix
@@ -1006,3 +1065,18 @@ it exists, and the answer will name you.
1006
1065
  Semantic versioning on the package, independent of the `contract` number. Pin an **exact** version:
1007
1066
  the player and its hosts deploy separately, and a range brings in a version nobody decided to
1008
1067
  deploy, on a day someone ran `npm install` for another reason.
1068
+
1069
+ ⚠️ **And please keep that pin current — one train behind at most.** A host asked whether this
1070
+ mattered, having verified a release without moving onto it: the diff was comments only, no
1071
+ migration, and their argument was that what makes a verification useful is the verification, not the
1072
+ pin. That reasoning is sound, and the answer is still yes, for a reason that is ours rather than
1073
+ theirs: **a report we cannot reproduce is a report we cannot act on.** Every finding in this
1074
+ document arrived as "we measured X" — the ceiling, the platform timeout, the truncated aggregate.
1075
+ Each was worth something because we could stand the same version up beside it. When installations
1076
+ drift apart by several versions, a measurement stops being about the player and starts being about
1077
+ which player, and the first thing we spend on any report is establishing that.
1078
+
1079
+ It is a request, not a requirement: nothing here refuses to run on an older version, the contract
1080
+ number has not moved, and a release whose notes say it changes nothing for you genuinely changes
1081
+ nothing for you. But we are asking for the alignment explicitly rather than assuming it — which is
1082
+ the point of writing it down at all.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "discovery-media-player",
3
- "version": "0.1.155",
3
+ "version": "0.1.157",
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",
@@ -29,6 +29,14 @@ const MIN_MOIS = 1, MAX_MOIS = 120;
29
29
  // négative calculerait une borne FUTURE (perte massive), zéro purgerait tout, une chaîne/NaN/
30
30
  // Infinity produirait une date invalide. On refuse AVANT le premier DELETE, en NOMMANT la clé.
31
31
  // Zéro n'est PAS une purge immédiate : ce serait un geste trop dangereux pour un défaut de config.
32
+ //
33
+ // ⚠️ ET LE FRÈRE DE CETTE FONCTION FAIT DÉLIBÉRÉMENT L'INVERSE — c'est dit ici parce qu'il est à
34
+ // quatre cent cinquante lignes d'ici et qu'un lecteur n'arrive jamais aux deux. `delaiLecture()`
35
+ // RETOMBE sur son défaut au lieu de lever. La sévérité se règle sur la CONSÉQUENCE DE L'ERREUR, pas
36
+ // sur la nature du réglage : une fenêtre fausse supprime des lignes, un délai faux fait au pire
37
+ // attendre. Un hôte a prédit le défaut de ne l'écrire qu'à un seul bout — « sans la phrase, le
38
+ // prochain lecteur harmonisera, dans un sens ou dans l'autre, et croira corriger une incohérence ».
39
+ // Uniformiser les deux serait donc une régression, quel que soit le sens choisi.
32
40
  function fenetresValidees() {
33
41
  const brut = { ...FENETRES, ...((PLAYER.config && PLAYER.config.retention) || {}) };
34
42
  const out = Object.create(null); // nu : la garde de forme reconnaît cet accumulateur