discovery-media-player 0.1.147 → 0.1.149
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 +50 -0
- package/docs/RETENTION.md +64 -16
- package/package.json +1 -1
- package/server/handler.js +12 -0
- package/server/retention.js +137 -1
package/docs/HOST-CONTRACT.md
CHANGED
|
@@ -482,6 +482,56 @@ forwarded re-shares of their own links included, because they caused those readi
|
|
|
482
482
|
changes for a role you already answer *yes* to. If your table predates `list.all`, see the warning
|
|
483
483
|
above: an unheard-of action answered *no* narrows this view rather than breaking it.
|
|
484
484
|
|
|
485
|
+
⚠️ **`?contract=1&schema=1` now tells you what is still stored, not only what you may purge.**
|
|
486
|
+
`retentionSweep` says the instance *can* purge; it says nothing about what has piled up. The card
|
|
487
|
+
gains a `purge` block counting the rows that still carry a reader IP or a raw User-Agent:
|
|
488
|
+
|
|
489
|
+
"purge": { "borne": 5000, "tronque": false, "lignes": { "sessions": 1908, "vues": 3200 },
|
|
490
|
+
"sessionsIp": 0, "sessionsUa": 0, "vuesUa": 0, "vide": true }
|
|
491
|
+
|
|
492
|
+
`vide` is the reading that matters: `true` means nothing of that legacy is left **on this
|
|
493
|
+
instance's live rows** — the condition under which those columns can eventually be dropped —
|
|
494
|
+
`false` means rows remain, and **`null` means at least one probe did not answer**. A count is
|
|
495
|
+
`null` for the same reason: a failed probe must never read as a zero, because zero is the answer
|
|
496
|
+
that authorises a deletion.
|
|
497
|
+
|
|
498
|
+
`lignes` is what the counter **looked at**, per table. A bare `0` cannot tell "purged" from "never
|
|
499
|
+
written" from "the probe is aimed wrong"; the denominator separates them — *0 of 1908* means there
|
|
500
|
+
was something to look at, *0 of 0* means the table is empty or out of reach and the zero proves
|
|
501
|
+
nothing. It is `null` on the same terms as the counts.
|
|
502
|
+
|
|
503
|
+
The counts are **bounded** at `borne` rows and read one small column. ⚠️ **`tronque` says whether
|
|
504
|
+
that bound was reached**: when it is `true`, every number in the block is a *lower bound*, not a
|
|
505
|
+
count. Without it a saturated `5000` would be indistinguishable from an exact five thousand — a
|
|
506
|
+
wrong number that reads as right, which is worse than an absent one, because an absence makes you
|
|
507
|
+
look and a number makes you conclude. `vide` stays correct either way: saturation can only make it
|
|
508
|
+
`false`, never wrongly `true`.
|
|
509
|
+
|
|
510
|
+
They run only under `&schema=1`, the mode where you have asked for the database.
|
|
511
|
+
|
|
512
|
+
⚠️ **The purge attestation is a commitment, not a convenience.** Every column this player empties
|
|
513
|
+
carries a `comment on column` whose text **begins with the exact marker**:
|
|
514
|
+
|
|
515
|
+
VIDE ET PLUS JAMAIS ECRITE depuis la <migration number>.
|
|
516
|
+
|
|
517
|
+
Read it through `col_description()`. It is what *proves* a purge was applied — a count of zero does
|
|
518
|
+
not, since it cannot tell "purged" from "never written". **We commit to two things**: to post it on
|
|
519
|
+
every column a future migration empties, and not to reword that prefix. It is deliberately plain
|
|
520
|
+
ASCII, without accent or apostrophe, so it survives encodings and needs no escaping.
|
|
521
|
+
|
|
522
|
+
This used to be a convenience, designed for a person proving a purge. A host told us its inventory
|
|
523
|
+
now reads it **mechanically**, crossing it with the residual counts to raise an alarm when values
|
|
524
|
+
reappear beside an attestation. That is the moment an artefact becomes an interface — and the reason
|
|
525
|
+
to commit is the failure mode: if we quietly stopped posting it, that alarm would go **silent
|
|
526
|
+
without saying so**, a failure caused here and invisible there. A guard in this repository refuses
|
|
527
|
+
any migration that empties a column without the marker, so undoing the commitment turns something
|
|
528
|
+
red rather than turning something quiet.
|
|
529
|
+
|
|
530
|
+
⚠️ **Why this exists at all:** our tables live in *your* database, and your audit enumerates *your*
|
|
531
|
+
tables — a dependency's schema occupies a zone nobody's inventory visits. Two integrating hosts
|
|
532
|
+
found 2361 rows still carrying these columns, and they found them because a third party asked a
|
|
533
|
+
question about its own database, not because anything told them.
|
|
534
|
+
|
|
485
535
|
⚠️ **The reader IP is erased, and a direct query of your own will start seeing nothing.** The
|
|
486
536
|
sessions table carried `ip` in the clear. `0.1.147` stops serving it — no player path reads it back,
|
|
487
537
|
so nothing in this contract changes — stops writing it, and ships migration **0026**, which erases
|
package/docs/RETENTION.md
CHANGED
|
@@ -177,11 +177,35 @@ end on a populated database: 200 rows kept, 200 addresses gone after a routine v
|
|
|
177
177
|
replayable with no further effect. **The erasure is therefore complete today.** What is deferred is
|
|
178
178
|
the shape of the schema, not the data.
|
|
179
179
|
|
|
180
|
-
**
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
180
|
+
**Dropping the three columns is YOUR decision, not a migration we will ship.** This section used to
|
|
181
|
+
say the removal would come "in a later release", which was misleading: **we cannot know which player
|
|
182
|
+
version runs against your database, and you can.** A `DROP` is only safe once every instance writing
|
|
183
|
+
to that database is on `0.1.147` or later; on `0.1.145` and earlier PostgREST would reject every
|
|
184
|
+
session and view write, with an error naming a column rather than a version. Shipping that `DROP` in
|
|
185
|
+
`supabase/migrations/`, which every host replays, would hand the same irreversible gesture to hosts
|
|
186
|
+
whose deployment we have never seen. So it stays where the answer is known — with you.
|
|
187
|
+
|
|
188
|
+
**How to know the moment has come.** `?contract=1&schema=1` reports `purge.vide`. When it is `true`
|
|
189
|
+
on every instance pointing at that database, and every one of them is on `0.1.147` or later, nothing
|
|
190
|
+
writes those columns any more. Then, if you want the schema tidied:
|
|
191
|
+
|
|
192
|
+
```sql
|
|
193
|
+
alter table public.commercial_doc_sessions drop column if exists ip;
|
|
194
|
+
alter table public.commercial_doc_sessions drop column if exists ua;
|
|
195
|
+
alter table public.commercial_doc_views drop column if exists ua;
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
⚠️ **This buys tidiness, not erasure — the erasure already happened.** `0026` and `0027` are what
|
|
199
|
+
removed the values, and routine autovacuum is what removed them from the pages (measured on a real
|
|
200
|
+
host: four seconds after the second migration, no lock, nothing triggered by hand). A `DROP` on
|
|
201
|
+
already-empty columns rewrites nothing and frees nothing. Run it because a schema should say what it
|
|
202
|
+
holds, not because anything is still there.
|
|
203
|
+
|
|
204
|
+
⚠️ **And you lose the attestation with the column.** The comment carried by each column — readable
|
|
205
|
+
through `col_description()` — is what proves the purge was applied; a count of zero does not, since
|
|
206
|
+
it cannot tell "purged" from "never written". Capture that proof before dropping if you may need to
|
|
207
|
+
show it. After the drop, `purge.vide` still reads `true`: an absent column is a known state, not an
|
|
208
|
+
unknown one.
|
|
185
209
|
|
|
186
210
|
**Why the column itself survives, for now.** A migration here must be safe to apply *while the
|
|
187
211
|
previous version of the player is running* — that rule is what makes the deployment order harmless,
|
|
@@ -218,23 +242,47 @@ is a number.
|
|
|
218
242
|
**1. The rows.** Reading logs are deleted **13 months** after `at` / `last_at` by default. A host
|
|
219
243
|
changes that through `config.retention` — whole months in `[1, 120]`.
|
|
220
244
|
|
|
221
|
-
⚠️ **But the automatic sweep is strictly opt-in
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
245
|
+
⚠️ **But the automatic sweep is strictly opt-in**, and there are **three** states, not two — an
|
|
246
|
+
integrating host measured the one we had left out:
|
|
247
|
+
|
|
248
|
+
| state | how to tell | what you may claim |
|
|
249
|
+
|---|---|---|
|
|
250
|
+
| **off** | `retentionSweep` false, and no `retention.run` in your logs | nothing has ever been deleted; the window is a policy you have not applied |
|
|
251
|
+
| **armed, never exercised** | armed, but no row has yet reached the window — check the age of your oldest row against it | nothing has ever been deleted **either**, and not for want of configuration |
|
|
252
|
+
| **armed, and has deleted** | armed, and a run reported non-zero counts | the window is an *event*, and only here |
|
|
253
|
+
|
|
254
|
+
⚠️ **The middle state is the misleading one**, because it has every appearance of the good one: armed,
|
|
255
|
+
correct, and indistinguishable in its effects from being off — no deletion, no log, no evidence it
|
|
256
|
+
works. A host reported exactly this: sweep armed, oldest row 63 days old, **zero rows past 13 months
|
|
257
|
+
out of 1908**. Its first real execution will be roughly **eighteen months after it was armed**, on
|
|
258
|
+
data nobody will have looked at, never having run in anger. Treat it as what it is — a guard that has
|
|
259
|
+
never been exercised, with a deadline — and exercise it deliberately before then, on a copy or with
|
|
260
|
+
`retention.run` and a short window, rather than discovering its behaviour the day it matters.
|
|
261
|
+
|
|
262
|
+
Anyone attesting a retention period should establish which of the **three** is true of the
|
|
263
|
+
installation in front of them, rather than quoting the default.
|
|
226
264
|
|
|
227
265
|
**2. The values inside surviving rows.** Erased by 0026 and 0027 as soon as they are applied, and
|
|
228
266
|
physically gone from the table once routine autovacuum has passed — no operator action, typically
|
|
229
267
|
minutes to hours on an active table. This part does not wait for the 13 months.
|
|
230
268
|
|
|
231
269
|
**3. Backups, write-ahead logs, exports and migration dumps.** **Outside this player's reach, and we
|
|
232
|
-
neither set nor observe them.** They follow the hosting platform's own settings —
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
270
|
+
neither set nor observe them.** They follow the hosting platform's own settings — typically a
|
|
271
|
+
point-in-time-recovery window plus a snapshot schedule, each with its own retention. Ask the platform
|
|
272
|
+
for **the PITR window and the age of the oldest retained snapshot**; until they have rolled past the
|
|
273
|
+
day 0026/0027 were applied, earlier copies still hold the erased values.
|
|
274
|
+
|
|
275
|
+
⚠️ **Neither number is exposed by any API, and that matters more than it looks.** Two integrating
|
|
276
|
+
hosts checked independently, on two different toolsets: the provider's API and its MCP tools return
|
|
277
|
+
region, status and engine version — nothing about backups. **A human has to read them from the
|
|
278
|
+
dashboard.** This is written here because the instruction above is *executable in appearance*: an
|
|
279
|
+
agent following it will look for a tool, find none, and then either stop — or, the real risk, report
|
|
280
|
+
the purge complete having skipped the one step it could not measure. If you cannot produce these two
|
|
281
|
+
numbers, say so; do not round the sentence.
|
|
282
|
+
|
|
283
|
+
⚠️ **And it is not always "the later of two".** An option that is not subscribed retains nothing, so
|
|
284
|
+
it defers nothing. A host with no PITR and eight daily snapshots has **one** deadline, not two: the
|
|
285
|
+
age of its oldest snapshot. Take the later of the deadlines that *exist*.
|
|
238
286
|
|
|
239
287
|
## Limits stated rather than left unsaid
|
|
240
288
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "discovery-media-player",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.149",
|
|
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/handler.js
CHANGED
|
@@ -801,6 +801,18 @@ async function handlerMesure(req, res) {
|
|
|
801
801
|
...(String(q.schema || "") === "1"
|
|
802
802
|
? await require("./schema").sonderTout()
|
|
803
803
|
: require("./schema").etatDuSchema()) },
|
|
804
|
+
// ⚠️ CE QUI S'ACCUMULE, PAS SEULEMENT CE QU'ON PEUT PURGER. `retentionSweep` ci-dessus dit
|
|
805
|
+
// « je PEUX purger » ; il ne dit rien de ce qui est là. Nos tables vivent dans la base de
|
|
806
|
+
// l'hôte, et l'audit d'un hôte énumère SES tables : le schéma d'une dépendance occupe une
|
|
807
|
+
// zone que les inventaires de personne ne visitent. Deux hôtes ont trouvé 2361 lignes
|
|
808
|
+
// portant encore une adresse ou un agent brut — parce qu'un TIERS avait posé une question
|
|
809
|
+
// sur SA base, pas parce que quoi que ce soit le leur avait dit.
|
|
810
|
+
//
|
|
811
|
+
// Sous `&schema=1` seulement : c'est une lecture de la base, et sans le paramètre cette
|
|
812
|
+
// carte garde sa propriété de répondre quand plus rien ne répond.
|
|
813
|
+
...(String(q.schema || "") === "1"
|
|
814
|
+
? { purge: await require("./retention").resteDeLaPurge() }
|
|
815
|
+
: {}),
|
|
804
816
|
// « L'hôte peut-il créer un lien en son nom propre ? » — configuré, pas seulement
|
|
805
817
|
// possible. Un hôte qui oublie le secret reçoit un 401 qui ressemble à un droit
|
|
806
818
|
// manquant ; ce booléen le lui dit sans qu'il ait à essayer.
|
package/server/retention.js
CHANGED
|
@@ -391,4 +391,140 @@ function tick() {
|
|
|
391
391
|
.catch((e) => { try { PLAYER.errors.capture(e, { route: "retention", benin: true }); } catch { /* jamais bloquant */ } });
|
|
392
392
|
}
|
|
393
393
|
|
|
394
|
-
|
|
394
|
+
/**
|
|
395
|
+
* CE QUI RESTE DE L'HÉRITAGE, CHEZ CET HÔTE — les lignes qui portent encore une adresse IP ou un
|
|
396
|
+
* User-Agent brut.
|
|
397
|
+
*
|
|
398
|
+
* ⚠️ POURQUOI CE COMPTEUR EXISTE, ET C'EST UN HÔTE QUI L'A DIT. Nos tables vivent dans la base de
|
|
399
|
+
* nos hôtes, et l'audit d'un hôte énumère SES tables : le schéma d'une dépendance occupe une zone
|
|
400
|
+
* que les inventaires de personne ne visitent. Deux hôtes ont découvert 2361 lignes portant ces
|
|
401
|
+
* colonnes — non pas en surveillant, mais parce qu'un TIERS avait posé une question sur SA base.
|
|
402
|
+
* `retentionSweep` dit « je PEUX purger » ; il ne dit pas CE QUI S'ACCUMULE. Ce compteur le dit,
|
|
403
|
+
* chez chacun, sans que personne ait à y penser.
|
|
404
|
+
*
|
|
405
|
+
* ⚠️ ET IL RÉPOND À LA QUESTION QUI DÉCIDE DU RETRAIT DES COLONNES. `0026` et `0027` VIDENT sans
|
|
406
|
+
* supprimer, parce qu'une migration doit rester sûre pendant que la version précédente du code
|
|
407
|
+
* tourne. Le retrait attend que plus aucune version supportée ne les écrive — une condition qu'on
|
|
408
|
+
* ne peut aujourd'hui que SUPPOSER, en croyant savoir quelle version tourne chez qui. `vide` la
|
|
409
|
+
* rend LISIBLE.
|
|
410
|
+
*
|
|
411
|
+
* ⚠️ ON COMPTE DES LIGNES, PAS UN `count=exact`. La capacité `db` de l'hôte rend le corps de la
|
|
412
|
+
* réponse, pas ses en-têtes : le compte de PostgREST voyage dans `Content-Range`, donc il serait
|
|
413
|
+
* illisible sans élargir le contrat d'hôte — ce qu'un compteur de diagnostic ne justifie pas.
|
|
414
|
+
* D'où un comptage BORNÉ : au plus `BORNE_RESTE` identifiants, une seule petite colonne.
|
|
415
|
+
*
|
|
416
|
+
* ⚠️ ET LA SATURATION SE DIT, ELLE NE SE DEVINE PAS — deux hôtes ont trouvé ce défaut dans la
|
|
417
|
+
* première version, le même jour, indépendamment. Elle demandait `limit=BORNE` et publiait
|
|
418
|
+
* `lignes.length` : sur une base portant cinq mille adresses, elle rendait `1000`, que rien ne
|
|
419
|
+
* distinguait d'un compte exact de mille. Un nombre faux qui se lit comme juste — pire qu'un
|
|
420
|
+
* nombre absent, parce que l'absence fait chercher et que le nombre fait conclure.
|
|
421
|
+
*
|
|
422
|
+
* Le remède vivait à trois cents lignes d'ici : `purgerRetention` rend `tronque` depuis toujours,
|
|
423
|
+
* pour exactement cette raison. On demande donc `BORNE + 1` : en recevoir autant prouve qu'il en
|
|
424
|
+
* reste, sans coûter une ligne de plus. `n` reste plafonné à la borne, et `tronque` dit qu'il faut
|
|
425
|
+
* le lire « au moins ».
|
|
426
|
+
*
|
|
427
|
+
* ⚠️ ET LE COÛT EST INVERSE DE L'INTUITION, donc il est dit plutôt que caché : quand il reste
|
|
428
|
+
* beaucoup de lignes, la base s'arrête à la borne et c'est rapide ; quand il n'en reste AUCUNE,
|
|
429
|
+
* elle parcourt la table pour ne rien trouver. Le cas cher est le cas terminal — celui où ce
|
|
430
|
+
* compteur a fini son office et disparaîtra avec les colonnes qu'il surveille. Il ne s'exécute
|
|
431
|
+
* d'ailleurs que sur `?contract=1&schema=1`, le seul mode où l'appelant demande la base.
|
|
432
|
+
*
|
|
433
|
+
* ⚠️ UN ÉCHEC REND `null`, JAMAIS ZÉRO. Zéro est la réponse qui autorise à supprimer une colonne :
|
|
434
|
+
* la fabriquer à partir d'une sonde en panne serait le pire mensonge que cette carte puisse faire.
|
|
435
|
+
*/
|
|
436
|
+
// ⚠️ CINQ MILLE, ET LE NOMBRE VIENT D'UNE MESURE. Il valait mille, et le banc écrit avec les
|
|
437
|
+
// volumes RÉELS d'un hôte l'a fait rougir : sa table de vues en portait 1651. La borne saturait
|
|
438
|
+
// donc dès le premier jour chez lui, et un compteur qui plafonne sous les volumes qu'il est censé
|
|
439
|
+
// décrire ne décrit rien. Cinq mille couvre les deux hôtes connus avec de la marge, reste une
|
|
440
|
+
// seule petite colonne à transférer, et `tronque` dit le reste. La borne est un plafond de COÛT,
|
|
441
|
+
// pas une opinion sur ce qu'un hôte peut avoir.
|
|
442
|
+
const BORNE_RESTE = 5000;
|
|
443
|
+
|
|
444
|
+
const SONDES_RESTE = [
|
|
445
|
+
["sessionsIp", "commercial_doc_sessions", "session_id", "ip"],
|
|
446
|
+
["sessionsUa", "commercial_doc_sessions", "session_id", "ua"],
|
|
447
|
+
["vuesUa", "commercial_doc_views", "id", "ua"],
|
|
448
|
+
];
|
|
449
|
+
|
|
450
|
+
/** Les tables regardées, pour le dénominateur — une par table, pas une par sonde. */
|
|
451
|
+
const TABLES_RESTE = [["sessions", "commercial_doc_sessions", "session_id"],
|
|
452
|
+
["vues", "commercial_doc_views", "id"]];
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* ⚠️ ET LA COLONNE DISPARUE EST UN ÉTAT CONNU, PAS UNE PANNE. Le jour où un exploitant supprime ces
|
|
456
|
+
* colonnes — le geste que ce compteur sert à autoriser — la requête échoue avec le
|
|
457
|
+
* `42703` de PostgreSQL, « colonne inexistante ». Rendre `null` ferait alors lire « on ne sait
|
|
458
|
+
* pas » au moment EXACT où l'on sait le mieux : plus rien ne peut porter une colonne qui n'existe
|
|
459
|
+
* plus. Le compteur deviendrait aveugle précisément quand son sujet est réglé.
|
|
460
|
+
*
|
|
461
|
+
* Toute autre erreur reste `null`. Et un hôte dont la capacité `db` ne rend pas le corps analysé
|
|
462
|
+
* retombe sur `null` : ne pas savoir est le côté sûr, puisque zéro est ce qui autorise à supprimer.
|
|
463
|
+
*/
|
|
464
|
+
const COLONNE_ABSENTE = "42703";
|
|
465
|
+
|
|
466
|
+
/** `{ n, tronque }` — `n` nul veut dire indéterminé, jamais zéro. */
|
|
467
|
+
const compte = (n, tronque) => ({ n, tronque });
|
|
468
|
+
|
|
469
|
+
async function compterBorne(chemin) {
|
|
470
|
+
try {
|
|
471
|
+
// ⚠️ BORNE + 1 : la ligne excédentaire ne sert qu'à PROUVER qu'il en reste. On ne la publie pas.
|
|
472
|
+
const lignes = await PLAYER.db.request(`${chemin}&limit=${BORNE_RESTE + 1}`, { timeoutMs: 8000 });
|
|
473
|
+
if (!Array.isArray(lignes)) return compte(null, false);
|
|
474
|
+
return compte(Math.min(lignes.length, BORNE_RESTE), lignes.length > BORNE_RESTE);
|
|
475
|
+
} catch (e) {
|
|
476
|
+
if (e && e.details && e.details.code === COLONNE_ABSENTE) return compte(0, false);
|
|
477
|
+
return compte(null, false); // indéterminé — surtout pas zéro
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const compterReste = (table, cle, colonne) =>
|
|
482
|
+
compterBorne(`${table}?select=${cle}&${colonne}=not.is.null`);
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* ⚠️ ET LE COMPTEUR PORTE CE QU'IL A REGARDÉ — un hôte nous l'a demandé, et il avait raison.
|
|
486
|
+
*
|
|
487
|
+
* `sessionsIp: 0` ne distingue pas trois choses : « purgé », « jamais écrit », et « la sonde vise à
|
|
488
|
+
* côté ». Les deux premières se valent pour qui veut supprimer une colonne ; la troisième est un
|
|
489
|
+
* mensonge. Le dénominateur les sépare : « 0 sur 1908 lignes examinées » dit qu'il y avait quelque
|
|
490
|
+
* chose à regarder, « 0 sur 0 » dit que la table est vide ou hors d'atteinte et que le zéro ne
|
|
491
|
+
* prouve rien.
|
|
492
|
+
*
|
|
493
|
+
* C'est notre propre règle anti-vacuité — un plancher compte la FORME RECONNUE, pas les choses
|
|
494
|
+
* comptées — appliquée partout dans `tools/` et absente d'ici jusqu'à ce qu'un lecteur la réclame.
|
|
495
|
+
*
|
|
496
|
+
* ⚠️ ET IL NE COÛTE PRESQUE RIEN, à l'inverse du compte filtré : sans filtre, la base s'arrête à la
|
|
497
|
+
* borne dès les premières lignes. Une par TABLE, pas une par sonde — deux des trois colonnes vivent
|
|
498
|
+
* dans la même.
|
|
499
|
+
*/
|
|
500
|
+
const compterLignes = (table, cle) => compterBorne(`${table}?select=${cle}`);
|
|
501
|
+
|
|
502
|
+
async function resteDeLaPurge() {
|
|
503
|
+
const [comptes, totaux] = await Promise.all([
|
|
504
|
+
Promise.all(SONDES_RESTE.map(([, t, c, col]) => compterReste(t, c, col))),
|
|
505
|
+
Promise.all(TABLES_RESTE.map(([, t, c]) => compterLignes(t, c))),
|
|
506
|
+
]);
|
|
507
|
+
// ⚠️ ACCUMULATEURS NUS, comme celui de `fenetresValidees` plus haut et pour la même raison : la
|
|
508
|
+
// garde de forme reconnaît `Object.create(null)`, et une écriture indexée par autre chose qu'un
|
|
509
|
+
// littéral n'a alors aucun prototype à polluer. Les clés viennent ici de constantes du fichier,
|
|
510
|
+
// mais un objet nu ne coûte rien et la propriété se lit sans avoir à remonter leur provenance.
|
|
511
|
+
const parTable = Object.create(null);
|
|
512
|
+
TABLES_RESTE.forEach(([nom], i) => { parTable[nom] = totaux[i].n; });
|
|
513
|
+
const out = Object.create(null);
|
|
514
|
+
out.borne = BORNE_RESTE;
|
|
515
|
+
// ⚠️ UN SEUL DRAPEAU POUR TOUT LE BLOC, parce qu'il ne sert qu'à une chose : dire au lecteur que
|
|
516
|
+
// les nombres qu'il voit sont des minorants. Un drapeau par compte suggérerait qu'on peut faire
|
|
517
|
+
// confiance aux autres, alors que la borne est commune et que la question ne l'est pas.
|
|
518
|
+
out.tronque = [...comptes, ...totaux].some((c) => c.tronque);
|
|
519
|
+
out.lignes = parTable;
|
|
520
|
+
SONDES_RESTE.forEach(([nom], i) => { out[nom] = comptes[i].n; });
|
|
521
|
+
// ⚠️ TROIS ÉTATS, PAS DEUX. `true` : plus rien, le retrait des colonnes est permis ICI. `false` :
|
|
522
|
+
// il reste des lignes. `null` : au moins une sonde n'a pas répondu — on ne sait pas, et « on ne
|
|
523
|
+
// sait pas » ne doit jamais se lire comme « c'est bon ».
|
|
524
|
+
// ⚠️ `vide` RESTE JUSTE MÊME SATURÉ, et c'est ce qui compte : c'est le champ qui autorise le
|
|
525
|
+
// retrait d'une colonne, et la saturation ne peut le rendre que FAUX — jamais vrai à tort.
|
|
526
|
+
out.vide = comptes.some((c) => c.n === null) ? null : comptes.every((c) => c.n === 0);
|
|
527
|
+
return out;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
module.exports = { init, purgerRetention, tick, borne, resteDeLaPurge, BORNE_RESTE };
|