discovery-media-player 0.1.154 → 0.1.156
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 +30 -6
- package/package.json +1 -1
- package/server/retention.js +37 -10
package/docs/HOST-CONTRACT.md
CHANGED
|
@@ -894,15 +894,22 @@ but *did you measure exactly what fails*. Two true statements about the same ins
|
|
|
894
894
|
different responses.
|
|
895
895
|
|
|
896
896
|
**⚠️ The ceiling on your requests is set by the role that OPENS the connection, not the one they run
|
|
897
|
-
as.**
|
|
898
|
-
`statement_timeout`; PostgREST's `SET ROLE` does **not** reset it, so code running as
|
|
899
|
-
inherits it — while `service_role` itself shows no setting at all, so nothing our code
|
|
900
|
-
suggests a limit exists.
|
|
901
|
-
|
|
897
|
+
as.** Two hosts measured this independently, and it is invisible from our side. Their `authenticator`
|
|
898
|
+
role carries a `statement_timeout`; PostgREST's `SET ROLE` does **not** reset it, so code running as
|
|
899
|
+
`service_role` inherits it — while `service_role` itself shows no setting at all, so nothing our code
|
|
900
|
+
can read suggests a limit exists. Both measured **8 seconds** on `authenticator` and on
|
|
901
|
+
`authenticated`, and **3 seconds** on `anon`: this is the platform default on Supabase, not a
|
|
902
|
+
peculiarity of one installation, so assume you have it until you have looked. Every request we issue
|
|
903
|
+
is capped at that value, and a lock waited on for longer than it fails. Your paginated `selectAll`, a batched retention sweep, a
|
|
902
904
|
`count` on a large table: each is one statement, so each gets the whole budget and no more,
|
|
903
905
|
whatever the batch size.
|
|
904
906
|
|
|
905
|
-
Our own client abort no longer sits at that same value, deliberately
|
|
907
|
+
Our own client abort no longer sits at that same value, deliberately, and it is no longer a
|
|
908
|
+
constant: `config.retention.delaiLectureMs` (1 000–120 000 ms, default 12 000) lets a host who knows
|
|
909
|
+
their ceiling say so. A host put the reason better than we had seen it — *a constant chosen against
|
|
910
|
+
a known case carries the date of that case; the day a host announces 15 s, it is not the timer that
|
|
911
|
+
needs adjusting, it is the fact that it is a constant.* An invalid value falls back to the default
|
|
912
|
+
rather than refusing: a wrong retention window deletes rows, a wrong timeout at worst waits. Two timers set to the same
|
|
906
913
|
number do not produce a wrong answer here — both routes fall back to `null`, and a bench proves it —
|
|
907
914
|
but they make the *cause* undecidable: when our abort wins the race, the server's `57014` never
|
|
908
915
|
reaches us and "too slow" becomes indistinguishable from "the network died".
|
|
@@ -967,6 +974,23 @@ unaudited, said so plainly, and that sentence is the only reason we know those r
|
|
|
967
974
|
cannot measure your code. We can only know what someone wrote down. *"Not measured"* and *"nothing
|
|
968
975
|
found"* are different sentences, and only one of them is honest when you have not looked.
|
|
969
976
|
|
|
977
|
+
⚠️ **Say which of the two kinds it is: *not measured yet*, or *not measurable here*.** A host asked
|
|
978
|
+
for this distinction in as many words, and they were right that a contract which conflates them
|
|
979
|
+
waits forever for an answer that will never come. *Not measured yet* is a debt: someone will pay it.
|
|
980
|
+
*Not measurable here* is a structural property of that installation — no measurement available to
|
|
981
|
+
them can produce the phenomenon at all.
|
|
982
|
+
|
|
983
|
+
Their own case is the clean one. `db-max-rows` caps a response at 1000 rows; their largest table
|
|
984
|
+
holds 356, and the largest one readable by `anon` on their application database holds 836. Those are
|
|
985
|
+
usage volumes, not configuration — they cannot make them bigger to see the ceiling, and the only way
|
|
986
|
+
to reach it would be to widen production grants for a diagnosis, which they will not do and should
|
|
987
|
+
not. So that ceiling will never be observed there. **The other host's 1651 rows published as 1000 is
|
|
988
|
+
the only proof of it anyone will produce, and that is final** — which is also why we record where a
|
|
989
|
+
measurement came from rather than only what it said.
|
|
990
|
+
|
|
991
|
+
The two kinds want opposite things from us: a debt should be chased, a structural limit should be
|
|
992
|
+
written down and stopped being asked about.
|
|
993
|
+
|
|
970
994
|
**What not to send.** Shapes and counts, never contents. No row data, no reader IPs or User-Agents —
|
|
971
995
|
those are the columns half this contract exists to get rid of — no keys, tokens, connection strings,
|
|
972
996
|
or private hostnames. *"A table of ~1600 rows returned 1000"* is the whole of what we needed to fix
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "discovery-media-player",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.156",
|
|
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",
|
package/server/retention.js
CHANGED
|
@@ -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
|
|
@@ -455,15 +463,34 @@ function tick() {
|
|
|
455
463
|
// pas une opinion sur ce qu'un hôte peut avoir.
|
|
456
464
|
const BORNE_RESTE = 5000;
|
|
457
465
|
|
|
458
|
-
// ⚠️ DOUZE SECONDES, ET LE NOMBRE VIENT D'ÉVITER UNE ÉGALITÉ, PAS D'UN GOÛT. Il valait
|
|
459
|
-
// exactement le `statement_timeout`
|
|
460
|
-
//
|
|
461
|
-
//
|
|
462
|
-
//
|
|
463
|
-
//
|
|
464
|
-
//
|
|
465
|
-
//
|
|
466
|
+
// ⚠️ DOUZE SECONDES PAR DÉFAUT, ET LE NOMBRE VIENT D'ÉVITER UNE ÉGALITÉ, PAS D'UN GOÛT. Il valait
|
|
467
|
+
// 8000 — très exactement le `statement_timeout` que DEUX hôtes ont mesuré sur leur rôle
|
|
468
|
+
// `authenticator`, où il est le réglage par défaut de la plateforme et non une particularité. Deux
|
|
469
|
+
// minuteries réglées sur la même valeur ne rendent pas un résultat faux ici (les deux voies
|
|
470
|
+
// retombent sur `null`, un banc l'éprouve), mais elles rendent la CAUSE indécidable : quand notre
|
|
471
|
+
// abandon gagne la course, le `57014` du serveur ne nous parvient jamais, et « la requête était trop
|
|
472
|
+
// lente » devient indistinguable de « le réseau est tombé ».
|
|
473
|
+
//
|
|
474
|
+
// ⚠️ ET C'EST RÉGLABLE PARCE QU'UNE CONSTANTE CHOISIE CONTRE UN CAS CONNU PORTE LA DATE DE CE CAS.
|
|
475
|
+
// Un hôte l'a formulé mieux que nous ne l'avions vu : « le jour où un hôte annonce 15 s, ce n'est pas
|
|
476
|
+
// votre minuterie qu'il faudra ajuster — c'est le fait qu'elle soit une constante ». Corriger le
|
|
477
|
+
// nombre aurait reproduit le défaut avec une mèche plus longue, exactement comme corriger un nombre
|
|
478
|
+
// nu dans de la prose en produit un autre. `config.retention.delaiLectureMs` laisse l'hôte qui
|
|
479
|
+
// CONNAÎT son plafond le dire ; son absence rend le comportement d'aujourd'hui, à l'octet près.
|
|
466
480
|
const DELAI_LECTURE = 12000;
|
|
481
|
+
const DELAI_MIN = 1000, DELAI_MAX = 120000;
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* ⚠️ UNE VALEUR INVALIDE RETOMBE SUR LE DÉFAUT — elle ne lève PAS, à la différence des fenêtres de
|
|
485
|
+
* rétention juste au-dessus, et la différence est de conséquence : une fenêtre fausse SUPPRIME des
|
|
486
|
+
* lignes, un délai faux fait au pire attendre. Refuser de rendre la carte parce qu'un délai est mal
|
|
487
|
+
* tapé punirait le lecteur pour un réglage sans danger.
|
|
488
|
+
*/
|
|
489
|
+
function delaiLecture() {
|
|
490
|
+
const r = PLAYER.config && PLAYER.config.retention;
|
|
491
|
+
const v = r && Number(r.delaiLectureMs);
|
|
492
|
+
return Number.isFinite(v) && v >= DELAI_MIN && v <= DELAI_MAX ? Math.trunc(v) : DELAI_LECTURE;
|
|
493
|
+
}
|
|
467
494
|
|
|
468
495
|
const SONDES_RESTE = [
|
|
469
496
|
["sessionsIp", "commercial_doc_sessions", "session_id", "ip"],
|
|
@@ -553,7 +580,7 @@ async function resteApres(chemin, cle, dernier) {
|
|
|
553
580
|
if (dernier == null) return true;
|
|
554
581
|
try {
|
|
555
582
|
const suite = await PLAYER.db.request(
|
|
556
|
-
`${chemin}&${cle}=gt.${enc(String(dernier))}&order=${cle}.asc&limit=1`, { timeoutMs:
|
|
583
|
+
`${chemin}&${cle}=gt.${enc(String(dernier))}&order=${cle}.asc&limit=1`, { timeoutMs: delaiLecture() });
|
|
557
584
|
// Pas de réponse analysable ⇒ on ne sait pas ⇒ « au moins ». Se tromper vers le minorant ne
|
|
558
585
|
// fait que sous-estimer ; se tromper vers l'exactitude fait conclure.
|
|
559
586
|
return !Array.isArray(suite) || suite.length > 0;
|
|
@@ -606,7 +633,7 @@ async function compterBorne(chemin, cle) {
|
|
|
606
633
|
// ⚠️ ET L'ORDRE N'EST PAS DÉCORATIF : sans lui, « la dernière ligne reçue » ne désigne aucune
|
|
607
634
|
// frontière, et le curseur de la sonde ne voudrait rien dire.
|
|
608
635
|
const lignes = await PLAYER.db.request(
|
|
609
|
-
`${chemin}&order=${cle}.asc&limit=${BORNE_RESTE + 1}`, { timeoutMs:
|
|
636
|
+
`${chemin}&order=${cle}.asc&limit=${BORNE_RESTE + 1}`, { timeoutMs: delaiLecture() });
|
|
610
637
|
if (!Array.isArray(lignes)) return compte(null, false, VOIE_BORNEE);
|
|
611
638
|
// Notre propre borne atteinte : la preuve est dans la ligne excédentaire, rien à demander.
|
|
612
639
|
if (lignes.length > BORNE_RESTE) return compte(BORNE_RESTE, true, VOIE_BORNEE);
|