discovery-media-player 0.1.153 → 0.1.155

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.
@@ -25,7 +25,7 @@ need.
25
25
  it the same day, and remove it when the release lands.
26
26
  6. **Describe what you do — including what you think is trivial.** Not *"report deviations"*: you
27
27
  cannot know what one is, because that would mean knowing this page better than we do. ⚠️ The
28
- over-specified sentence in [the `tts-cache` section](#four-things-that-will-bite) stood for
28
+ over-specified sentence in [the `tts-cache` section](#what-will-bite) stood for
29
29
  weeks, and it took a host mentioning its own naming **as a curiosity** for anyone to look. Its
30
30
  own account of it, on 27/08: *"je ne l'ai décrite que parce que je citais `preview-fr-v2` comme
31
31
  une curiosité, sans savoir que c'était un écart. Si j'avais su que votre page l'interdisait, je
@@ -745,7 +745,7 @@ The rule underneath, safer than the list: **never fall back on a refusal of *acc
745
745
  back on an inability to *reach*.** And "do not fall back" applies to what you **offer** — an
746
746
  "Open ↗" button left in place is falling back one second later.
747
747
 
748
- ## Four things that will bite
748
+ ## What will bite
749
749
 
750
750
  **Your document-opening doors reappear.** A host has more than one place that opens a file, and new
751
751
  ones get written. Keep the list and hunt it periodically — and note that **your search criteria
@@ -893,6 +893,39 @@ cannot. Setting the number spares you that reasoning entirely.
893
893
  but *did you measure exactly what fails*. Two true statements about the same instance can describe
894
894
  different responses.
895
895
 
896
+ **⚠️ The ceiling on your requests is set by the role that OPENS the connection, not the one they run
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
904
+ `count` on a large table: each is one statement, so each gets the whole budget and no more,
905
+ whatever the batch size.
906
+
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
913
+ number do not produce a wrong answer here — both routes fall back to `null`, and a bench proves it —
914
+ but they make the *cause* undecidable: when our abort wins the race, the server's `57014` never
915
+ reaches us and "too slow" becomes indistinguishable from "the network died".
916
+
917
+ **⚠️ And `safeupdate` refuses an unrestricted write even under `service_role` — read that backwards.**
918
+ The same host has it preloaded on `authenticator`: a `DELETE` or `UPDATE` with no restricting clause
919
+ is rejected outright, and the error message does not name `safeupdate`, so the refusal arrives
920
+ without its reason. They paid for that once, on one of our functions.
921
+
922
+ The trap is not the refusal. **`safeupdate` is the net.** At a host that has it, an unfiltered write
923
+ fails loudly; at a host that does not — and nothing in this contract requires it — the very same
924
+ line succeeds and empties the table. The defect is therefore silent exactly where it is severe. A
925
+ guard in this repository now refuses any `DELETE`, `PATCH` or `PUT` we write without a restricting
926
+ predicate, and it counts `?select=…` as a projection rather than a filter, because that is the shape
927
+ that survives a review.
928
+
896
929
  ## What you can see and we cannot
897
930
 
898
931
  **Read this only if you run this player somewhere.** If you are evaluating it, skip to Versioning —
@@ -941,6 +974,23 @@ unaudited, said so plainly, and that sentence is the only reason we know those r
941
974
  cannot measure your code. We can only know what someone wrote down. *"Not measured"* and *"nothing
942
975
  found"* are different sentences, and only one of them is honest when you have not looked.
943
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
+
944
994
  **What not to send.** Shapes and counts, never contents. No row data, no reader IPs or User-Agents —
945
995
  those are the columns half this contract exists to get rid of — no keys, tokens, connection strings,
946
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.153",
3
+ "version": "0.1.155",
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",
@@ -455,6 +455,35 @@ function tick() {
455
455
  // pas une opinion sur ce qu'un hôte peut avoir.
456
456
  const BORNE_RESTE = 5000;
457
457
 
458
+ // ⚠️ DOUZE SECONDES PAR DÉFAUT, ET LE NOMBRE VIENT D'ÉVITER UNE ÉGALITÉ, PAS D'UN GOÛT. Il valait
459
+ // 8000 — très exactement le `statement_timeout` que DEUX hôtes ont mesuré sur leur rôle
460
+ // `authenticator`, où il est le réglage par défaut de la plateforme et non une particularité. Deux
461
+ // minuteries réglées sur la même valeur ne rendent pas un résultat faux ici (les deux voies
462
+ // retombent sur `null`, un banc l'éprouve), mais elles rendent la CAUSE indécidable : quand notre
463
+ // abandon gagne la course, le `57014` du serveur ne nous parvient jamais, et « la requête était trop
464
+ // lente » devient indistinguable de « le réseau est tombé ».
465
+ //
466
+ // ⚠️ ET C'EST RÉGLABLE PARCE QU'UNE CONSTANTE CHOISIE CONTRE UN CAS CONNU PORTE LA DATE DE CE CAS.
467
+ // 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
468
+ // votre minuterie qu'il faudra ajuster — c'est le fait qu'elle soit une constante ». Corriger le
469
+ // nombre aurait reproduit le défaut avec une mèche plus longue, exactement comme corriger un nombre
470
+ // nu dans de la prose en produit un autre. `config.retention.delaiLectureMs` laisse l'hôte qui
471
+ // CONNAÎT son plafond le dire ; son absence rend le comportement d'aujourd'hui, à l'octet près.
472
+ const DELAI_LECTURE = 12000;
473
+ const DELAI_MIN = 1000, DELAI_MAX = 120000;
474
+
475
+ /**
476
+ * ⚠️ UNE VALEUR INVALIDE RETOMBE SUR LE DÉFAUT — elle ne lève PAS, à la différence des fenêtres de
477
+ * rétention juste au-dessus, et la différence est de conséquence : une fenêtre fausse SUPPRIME des
478
+ * lignes, un délai faux fait au pire attendre. Refuser de rendre la carte parce qu'un délai est mal
479
+ * tapé punirait le lecteur pour un réglage sans danger.
480
+ */
481
+ function delaiLecture() {
482
+ const r = PLAYER.config && PLAYER.config.retention;
483
+ const v = r && Number(r.delaiLectureMs);
484
+ return Number.isFinite(v) && v >= DELAI_MIN && v <= DELAI_MAX ? Math.trunc(v) : DELAI_LECTURE;
485
+ }
486
+
458
487
  const SONDES_RESTE = [
459
488
  ["sessionsIp", "commercial_doc_sessions", "session_id", "ip"],
460
489
  ["sessionsUa", "commercial_doc_sessions", "session_id", "ua"],
@@ -543,7 +572,7 @@ async function resteApres(chemin, cle, dernier) {
543
572
  if (dernier == null) return true;
544
573
  try {
545
574
  const suite = await PLAYER.db.request(
546
- `${chemin}&${cle}=gt.${enc(String(dernier))}&order=${cle}.asc&limit=1`, { timeoutMs: 8000 });
575
+ `${chemin}&${cle}=gt.${enc(String(dernier))}&order=${cle}.asc&limit=1`, { timeoutMs: delaiLecture() });
547
576
  // Pas de réponse analysable ⇒ on ne sait pas ⇒ « au moins ». Se tromper vers le minorant ne
548
577
  // fait que sous-estimer ; se tromper vers l'exactitude fait conclure.
549
578
  return !Array.isArray(suite) || suite.length > 0;
@@ -596,7 +625,7 @@ async function compterBorne(chemin, cle) {
596
625
  // ⚠️ ET L'ORDRE N'EST PAS DÉCORATIF : sans lui, « la dernière ligne reçue » ne désigne aucune
597
626
  // frontière, et le curseur de la sonde ne voudrait rien dire.
598
627
  const lignes = await PLAYER.db.request(
599
- `${chemin}&order=${cle}.asc&limit=${BORNE_RESTE + 1}`, { timeoutMs: 8000 });
628
+ `${chemin}&order=${cle}.asc&limit=${BORNE_RESTE + 1}`, { timeoutMs: delaiLecture() });
600
629
  if (!Array.isArray(lignes)) return compte(null, false, VOIE_BORNEE);
601
630
  // Notre propre borne atteinte : la preuve est dans la ligne excédentaire, rien à demander.
602
631
  if (lignes.length > BORNE_RESTE) return compte(BORNE_RESTE, true, VOIE_BORNEE);