discovery-media-player 0.1.151 → 0.1.152
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 +19 -1
- package/package.json +1 -1
- package/server/retention.js +35 -9
package/docs/HOST-CONTRACT.md
CHANGED
|
@@ -487,7 +487,7 @@ above: an unheard-of action answered *no* narrows this view rather than breaking
|
|
|
487
487
|
gains a `purge` block counting the rows that still carry a reader IP or a raw User-Agent:
|
|
488
488
|
|
|
489
489
|
"purge": { "borne": 5000, "tronque": false, "lignes": { "sessions": 1908, "vues": 3200 },
|
|
490
|
-
"sessionsIp": 0, "sessionsUa": 0, "vuesUa": 0, "vide": true }
|
|
490
|
+
"sessionsIp": 0, "sessionsUa": 0, "vuesUa": 0, "vide": true, "voie": "bornee" }
|
|
491
491
|
|
|
492
492
|
`vide` is the reading that matters: `true` means nothing of that legacy is left **on this
|
|
493
493
|
instance's live rows** — the condition under which those columns can eventually be dropped —
|
|
@@ -545,6 +545,24 @@ portability rule refuses.
|
|
|
545
545
|
column. Anything that is not a non-negative integer (a string, a float, `undefined`, `NaN`) is read
|
|
546
546
|
as "no answer" and the player falls back rather than believing it.
|
|
547
547
|
|
|
548
|
+
⚠️ **And `voie` tells you which route produced the numbers, because the numbers cannot.** `"exact"`,
|
|
549
|
+
`"bornee"`, or `"mixte"` when both served the same read — which happens for real: a host mid-purge
|
|
550
|
+
whose column is already dropped makes `count` throw on the filtered paths and answer on the same
|
|
551
|
+
table's totals.
|
|
552
|
+
|
|
553
|
+
This field exists because two hosts, independently and on the same day, found they could not verify
|
|
554
|
+
their own seam. An exact count and an untruncated bounded count render an **identical** block —
|
|
555
|
+
same numbers, same `tronque`, same `vide`. So a `db.count` that returns a string, or a float, falls
|
|
556
|
+
back **silently** and the card looks exactly like a working one: you believe your seam is wired
|
|
557
|
+
when it is not. One host caught it only by luck of volume — their table held 1655 rows, and the
|
|
558
|
+
bounded route is structurally incapable of exceeding the 1000-row ceiling, so the number itself
|
|
559
|
+
happened to be proof. Under a thousand rows there is no such luck, and the other host, whose
|
|
560
|
+
volumes are small, could not tell at all.
|
|
561
|
+
|
|
562
|
+
**Check `voie`, not a value.** A control that distinguishes two *mechanisms* must rest on something
|
|
563
|
+
one can do and the other cannot; a value both could return proves nothing, and a value written down
|
|
564
|
+
from yesterday's measurement rots without anyone touching anything.
|
|
565
|
+
|
|
548
566
|
⚠️ **The two alternatives were measured at a host, not assumed here** — recorded so nobody proposes
|
|
549
567
|
them again in six months believing they were never tried. **`?select=count()` is dead**:
|
|
550
568
|
`db-aggregates-enabled` is `false` by default, verified on two distinct Supabase projects, and the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "discovery-media-player",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.152",
|
|
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
|
@@ -477,8 +477,21 @@ const TABLES_RESTE = [["sessions", "commercial_doc_sessions", "session_id"],
|
|
|
477
477
|
*/
|
|
478
478
|
const COLONNE_ABSENTE = "42703";
|
|
479
479
|
|
|
480
|
-
/**
|
|
481
|
-
|
|
480
|
+
/**
|
|
481
|
+
* `{ n, tronque, voie }` — `n` nul veut dire indéterminé, jamais zéro.
|
|
482
|
+
*
|
|
483
|
+
* ⚠️ ET `voie` NOMME LE MÉCANISME QUI A PRODUIT LE NOMBRE, parce que le nombre seul ne le dit pas.
|
|
484
|
+
* Un compte exact et un compte borné NON tronqué rendent le même JSON : deux hôtes l'ont relevé le
|
|
485
|
+
* même jour, l'un en constatant qu'il ne pouvait pas vérifier sa propre couture, l'autre en
|
|
486
|
+
* écrivant un contrôle qui n'a marché que par chance de volume — sa table dépassait mille, donc la
|
|
487
|
+
* voie par lignes était structurellement incapable de rendre son chiffre. Sous mille, personne ne
|
|
488
|
+
* peut trancher, et un `db.count` qui rend une chaîne retombe SILENCIEUSEMENT sur la voie bornée :
|
|
489
|
+
* l'hôte croit sa couture branchée alors qu'elle ne sert pas.
|
|
490
|
+
*/
|
|
491
|
+
const compte = (n, tronque, voie) => ({ n, tronque, voie });
|
|
492
|
+
|
|
493
|
+
const VOIE_EXACTE = "exact";
|
|
494
|
+
const VOIE_BORNEE = "bornee";
|
|
482
495
|
|
|
483
496
|
/**
|
|
484
497
|
* ⚠️ « MOINS QUE DEMANDÉ » NE PROUVE PAS LA FIN — ET C'EST UN HÔTE RÉEL QUI L'A MONTRÉ.
|
|
@@ -577,23 +590,24 @@ async function compterBorne(chemin, cle) {
|
|
|
577
590
|
// méthode par lignes, pas celle-ci. `tronque: false` garde donc le sens qu'il a partout —
|
|
578
591
|
// « lisez ce nombre comme exact » — au lieu d'en prendre un second selon la voie employée.
|
|
579
592
|
const exact = await compteExact(chemin);
|
|
580
|
-
if (exact !== null) return compte(exact, false);
|
|
593
|
+
if (exact !== null) return compte(exact, false, VOIE_EXACTE);
|
|
581
594
|
try {
|
|
582
595
|
// ⚠️ BORNE + 1 : la ligne excédentaire ne sert qu'à PROUVER qu'il en reste. On ne la publie pas.
|
|
583
596
|
// ⚠️ ET L'ORDRE N'EST PAS DÉCORATIF : sans lui, « la dernière ligne reçue » ne désigne aucune
|
|
584
597
|
// frontière, et le curseur de la sonde ne voudrait rien dire.
|
|
585
598
|
const lignes = await PLAYER.db.request(
|
|
586
599
|
`${chemin}&order=${cle}.asc&limit=${BORNE_RESTE + 1}`, { timeoutMs: 8000 });
|
|
587
|
-
if (!Array.isArray(lignes)) return compte(null, false);
|
|
600
|
+
if (!Array.isArray(lignes)) return compte(null, false, VOIE_BORNEE);
|
|
588
601
|
// Notre propre borne atteinte : la preuve est dans la ligne excédentaire, rien à demander.
|
|
589
|
-
if (lignes.length > BORNE_RESTE) return compte(BORNE_RESTE, true);
|
|
602
|
+
if (lignes.length > BORNE_RESTE) return compte(BORNE_RESTE, true, VOIE_BORNEE);
|
|
590
603
|
// Zéro ligne : la sonde au-delà rendrait zéro elle aussi et n'apprendrait rien — y compris sous
|
|
591
604
|
// un plafond à zéro, que ni l'une ni l'autre ne distingue d'une table vide.
|
|
592
|
-
if (!lignes.length) return compte(0, false);
|
|
593
|
-
return compte(lignes.length, await resteApres(chemin, cle, lignes[lignes.length - 1][cle])
|
|
605
|
+
if (!lignes.length) return compte(0, false, VOIE_BORNEE);
|
|
606
|
+
return compte(lignes.length, await resteApres(chemin, cle, lignes[lignes.length - 1][cle]),
|
|
607
|
+
VOIE_BORNEE);
|
|
594
608
|
} catch (e) {
|
|
595
|
-
if (e && e.details && e.details.code === COLONNE_ABSENTE) return compte(0, false);
|
|
596
|
-
return compte(null, false); // indéterminé — surtout pas zéro
|
|
609
|
+
if (e && e.details && e.details.code === COLONNE_ABSENTE) return compte(0, false, VOIE_BORNEE);
|
|
610
|
+
return compte(null, false, VOIE_BORNEE); // indéterminé — surtout pas zéro
|
|
597
611
|
}
|
|
598
612
|
}
|
|
599
613
|
|
|
@@ -636,6 +650,18 @@ async function resteDeLaPurge() {
|
|
|
636
650
|
// confiance aux autres, alors que la borne est commune et que la question ne l'est pas.
|
|
637
651
|
out.tronque = [...comptes, ...totaux].some((c) => c.tronque);
|
|
638
652
|
out.lignes = parTable;
|
|
653
|
+
// ⚠️ UNE SEULE RÉPONSE POUR LES CINQ COMPTES, ET TROIS ÉTATS PLUTÔT QUE DEUX. La question qu'un
|
|
654
|
+
// hôte se pose est « ma couture sert-elle ? », pas « laquelle des cinq ». `"mixte"` n'est pas une
|
|
655
|
+
// commodité : il arrive vraiment — un `count` qui lève sur le chemin d'une colonne supprimée et
|
|
656
|
+
// répond sur le total de la même table — et c'est précisément le cas qu'un drapeau binaire
|
|
657
|
+
// aurait dû arrondir dans un sens ou dans l'autre, donc mentir.
|
|
658
|
+
//
|
|
659
|
+
// ⚠️ CE CHAMP NE DIT RIEN SUR LA JUSTESSE DES NOMBRES, seulement sur leur provenance. Il ne
|
|
660
|
+
// double aucun autre : `tronque` vaut `false` sur les DEUX voies, c'est même toute la raison
|
|
661
|
+
// d'être de cette ligne.
|
|
662
|
+
const voies = [...comptes, ...totaux].map((c) => c.voie);
|
|
663
|
+
out.voie = voies.every((v) => v === VOIE_EXACTE) ? VOIE_EXACTE
|
|
664
|
+
: voies.every((v) => v === VOIE_BORNEE) ? VOIE_BORNEE : "mixte";
|
|
639
665
|
SONDES_RESTE.forEach(([nom], i) => { out[nom] = comptes[i].n; });
|
|
640
666
|
// ⚠️ TROIS ÉTATS, PAS DEUX. `true` : plus rien, le retrait des colonnes est permis ICI. `false` :
|
|
641
667
|
// il reste des lignes. `null` : au moins une sonde n'a pas répondu — on ne sait pas, et « on ne
|