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.
@@ -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.151",
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",
@@ -477,8 +477,21 @@ const TABLES_RESTE = [["sessions", "commercial_doc_sessions", "session_id"],
477
477
  */
478
478
  const COLONNE_ABSENTE = "42703";
479
479
 
480
- /** `{ n, tronque }` — `n` nul veut dire indéterminé, jamais zéro. */
481
- const compte = (n, tronque) => ({ n, tronque });
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