discovery-media-player 0.1.153 → 0.1.154

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,32 @@ 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.** A host measured this and it is invisible from our side. Their `authenticator` role carries a
898
+ `statement_timeout`; PostgREST's `SET ROLE` does **not** reset it, so code running as `service_role`
899
+ inherits it — while `service_role` itself shows no setting at all, so nothing our code can read
900
+ suggests a limit exists. At that host every request we issue is capped at 8 seconds, and a lock
901
+ waited on for longer than that fails. Your paginated `selectAll`, a batched retention sweep, a
902
+ `count` on a large table: each is one statement, so each gets the whole budget and no more,
903
+ whatever the batch size.
904
+
905
+ Our own client abort no longer sits at that same value, deliberately. Two timers set to the same
906
+ number do not produce a wrong answer here — both routes fall back to `null`, and a bench proves it —
907
+ but they make the *cause* undecidable: when our abort wins the race, the server's `57014` never
908
+ reaches us and "too slow" becomes indistinguishable from "the network died".
909
+
910
+ **⚠️ And `safeupdate` refuses an unrestricted write even under `service_role` — read that backwards.**
911
+ The same host has it preloaded on `authenticator`: a `DELETE` or `UPDATE` with no restricting clause
912
+ is rejected outright, and the error message does not name `safeupdate`, so the refusal arrives
913
+ without its reason. They paid for that once, on one of our functions.
914
+
915
+ The trap is not the refusal. **`safeupdate` is the net.** At a host that has it, an unfiltered write
916
+ fails loudly; at a host that does not — and nothing in this contract requires it — the very same
917
+ line succeeds and empties the table. The defect is therefore silent exactly where it is severe. A
918
+ guard in this repository now refuses any `DELETE`, `PATCH` or `PUT` we write without a restricting
919
+ predicate, and it counts `?select=…` as a projection rather than a filter, because that is the shape
920
+ that survives a review.
921
+
896
922
  ## What you can see and we cannot
897
923
 
898
924
  **Read this only if you run this player somewhere.** If you are evaluating it, skip to Versioning —
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.154",
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,16 @@ 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, ET LE NOMBRE VIENT D'ÉVITER UNE ÉGALITÉ, PAS D'UN GOÛT. Il valait 8000 — très
459
+ // exactement le `statement_timeout` qu'un hôte a mesuré sur son rôle `authenticator`. Deux minuteries
460
+ // réglées sur la même valeur ne rendent pas un résultat faux ici (les deux voies retombent sur
461
+ // `null`, un banc l'éprouve), mais elles rendent la CAUSE indécidable : quand notre abandon gagne la
462
+ // course, le `57014` du serveur ne nous parvient jamais, et « la requête était trop lente » devient
463
+ // indistinguable de « le réseau est tombé ». Un plafond client strictement AU-DESSUS des plafonds
464
+ // serveur courants laisse le serveur expliquer son refus. C'est la règle de la marge sur le SUJET,
465
+ // appliquée à du code de production plutôt qu'à un banc.
466
+ const DELAI_LECTURE = 12000;
467
+
458
468
  const SONDES_RESTE = [
459
469
  ["sessionsIp", "commercial_doc_sessions", "session_id", "ip"],
460
470
  ["sessionsUa", "commercial_doc_sessions", "session_id", "ua"],
@@ -543,7 +553,7 @@ async function resteApres(chemin, cle, dernier) {
543
553
  if (dernier == null) return true;
544
554
  try {
545
555
  const suite = await PLAYER.db.request(
546
- `${chemin}&${cle}=gt.${enc(String(dernier))}&order=${cle}.asc&limit=1`, { timeoutMs: 8000 });
556
+ `${chemin}&${cle}=gt.${enc(String(dernier))}&order=${cle}.asc&limit=1`, { timeoutMs: DELAI_LECTURE });
547
557
  // Pas de réponse analysable ⇒ on ne sait pas ⇒ « au moins ». Se tromper vers le minorant ne
548
558
  // fait que sous-estimer ; se tromper vers l'exactitude fait conclure.
549
559
  return !Array.isArray(suite) || suite.length > 0;
@@ -596,7 +606,7 @@ async function compterBorne(chemin, cle) {
596
606
  // ⚠️ ET L'ORDRE N'EST PAS DÉCORATIF : sans lui, « la dernière ligne reçue » ne désigne aucune
597
607
  // frontière, et le curseur de la sonde ne voudrait rien dire.
598
608
  const lignes = await PLAYER.db.request(
599
- `${chemin}&order=${cle}.asc&limit=${BORNE_RESTE + 1}`, { timeoutMs: 8000 });
609
+ `${chemin}&order=${cle}.asc&limit=${BORNE_RESTE + 1}`, { timeoutMs: DELAI_LECTURE });
600
610
  if (!Array.isArray(lignes)) return compte(null, false, VOIE_BORNEE);
601
611
  // Notre propre borne atteinte : la preuve est dans la ligne excédentaire, rien à demander.
602
612
  if (lignes.length > BORNE_RESTE) return compte(BORNE_RESTE, true, VOIE_BORNEE);