v0.1.137
One host-visible fix, one tightened declaration, one new field on the identity card. Measured on
the two tarballs rather than claimed: 0 files added, 0 removed, 35 changed — and 30 of those
35 differ only by their two SPDX licence lines. The five that carry real changes are
server/routes-agent.js, server/handler.js, docs/HOST-CONTRACT.md, package.json, and
server/browser.generated.js — the last of which changed only in its embedded source digest: the
browser bundle a visitor actually executes is byte-identical across the two releases
(sha256 c399acaed0caf66e…, 15 863 bytes).
Fixed
⚠️ bot-ttsreturned 500 on every call, on any runtime whose globalcryptohas no
createHash. The lot-3 extraction moved the route out ofhandler.jswithout bringing
require("node:crypto")with it, so it leaned onglobalThis.crypto— whose shape varies by
runtime. Where only WebCrypto is exposed, the route threw. The import is
now explicit, and the bench fails without it.⚠️ These notes first said "on the first synthesis". That was wrong, and wrong in the direction
that matters — it suggested cached calls still worked. They did not:keyFor()builds the
cache key, so it runs before the cache is read. The throw always precedes the lookup, and no
cache hit is ever reached. Corrected on 25/08 after an integrating host measured the ordering
in the version they were running. If you haveplugins.botset, the bot's voice was entirely
out of service from 0.1.135 until this release, not degraded.
Changed
⚠️ engines.nodeis now>=22.13.0, up from>=22. This is a correction, not a new
requirement:pdfjs-dist@6.2.108— the player's only production dependency, the one that renders
documents — has always declared>=22.13.0 || >=24. Between Node 22.0 and 22.12 the package
said it was supported and ran its rendering engine on a version that engine calls unsupported.
npm never stopped it:engine-strictisfalseby default, so it prints anEBADENGINEline in
the noise of an install and installs anyway.- If you self-host on Node 22.0–22.12, nothing about the player changed — but you were
already outsidepdfjs-dist's support, andnpm installwill now say so. Move to 22.13 or
later. - The number is derived from the lockfile, not chosen.
node tools/plancher-de-node.mjs
recomputes it.
- If you self-host on Node 22.0–22.12, nothing about the player changed — but you were
Added
- A guard confronting the declared Node floor with the real one. CI could not have caught the
above by simply running:node-version: "22"resolves to the latest 22.x, so the runner always
lands above the floor, whatever it is. A rule the verifying environment satisfies by construction
is assumed, not verified — so the guard reads the version ranges instead of testing them by its
own presence. It works frompackage-lock.jsonalone: nonode_modules, no network, and it
measures what will actually be installed rather than what happens to sit in a folder. - The development floor is now written where a contributor reads it, and kept honest. It is
higher than the package's and unrelated to it (jsdomrequires `^22.22.2 || ^24.15.0 ||=26.0.0
); below itvitestdoes not start, and what it prints instead is aStartup Errorabout an npm bug advising you to deletepackage-lock.json— advice that edits a tracked file for a problem that is a Node version. Measured on a host running 20.18.1.CONTRIBUTING.md`
carries the number and the guard refuses if it drifts from the lockfile. - The identity card now reports the runtime.
GET /api/doc?contract=1gains
runtime: { node, nodeRequired }— what the process is executing on, and the floor the package
declares. Additive, so thecontractnumber does not move (rule 2).⚠️ A configured runtime is an intention, and reading it back does not tell you what ran.
Measured on 25/08 at an integrating host: the project setting said24.xwhile the deployment
serving production rannodejs 22. They could not tell from the outside, and they were right
that nothing let them — no route anywhere renderedprocess.version, ours included.- Two numbers, no verdict. The card does not say "supported": that would put a semver range
evaluator in the server, and this repository has twice paid for parsing a structured format by
hand. Compare them with your own semver. - The patch level is given, not just major.minor — the floor is patch-level (
>=22.13.0), so a
truncated version would not answer the one question the field exists for.
- The three example wirings declare the real floor, and the rule that checks them derives it.
It used to demand the literal string">=22", inexamples/demoalone — so the moment the floor
moved it refused the correct value and named only one of three files. It now compares
intervals againstpackage.json#engines: stricter than us is fine, more permissive is not.
An example is copied verbatim into an integrator's project; the floor it announces has to be the
package's, not the one true on the day the rule was written. - The production floor is written in the document a host actually receives.
enginesis
machine-readable and npm only warns below it; the only human-readable statement was a
shields.io badge in the README — a remote image, invisible offline and insidenode_modules,
which is exactly where a self-hoster reads.docs/HOST-CONTRACT.md— the page hosts pin — did
not contain the word "node". It now carries the floor, and the same guard refuses if that number
drifts from the lockfile.
What changed in the package, by zone — 0.1.136 → 0.1.137
Measured on the two tarballs, by the release workflow. Not written by hand.
| Zone | What it is | Added | Removed | Changed |
|---|---|---|---|---|
documents |
what a human reads | 0 | 0 | 1 |
manifest |
package.json — version, exports, dependencies | 0 | 0 | 1 |
server |
the code the host executes | 0 | 0 | 26 |
context |
the injected-context implementations | 0 | 0 | 2 |
browser-types |
the declarations the host's tsc reads for « ./bridge » — breaks a build, never a page | 0 | 0 | 0 |
browser |
what the visitors' page executes | 0 | 0 | 1 |
cli |
the command-line entry point | 0 | 0 | 1 |
types |
declarations for the server and context entry points — breaks a build, never runtime | 0 | 0 | 3 |
database |
the schema and the migrations the host applies itself | 0 | 0 | 0 |
The files themselves
~ docs/HOST-CONTRACT.md
~ package.json
~ server/appelant.js
~ server/brands.js
~ server/browser.generated.js
~ server/cache.js
~ server/erreurs-base.js
~ server/gabarit-agent.js
~ server/gabarit-carte.js
~ server/gabarit-legal.js
~ server/gabarit-live.js
~ server/handler.js
~ server/page-audience.js
~ server/page-mur.js
~ server/page-visionneuse.js
~ server/presentations.js
~ server/publier.js
~ server/retention.js
~ server/routes-agent.js
~ server/routes-direct.js
~ server/routes-liens.js
~ server/routes-visiteur.js
~ server/schema.js
~ server/session-cles.js
~ server/shared.generated.js
~ server/shares.js
~ server/texte.js
~ server/tiers.js
~ context/standalone.js
~ context/storage.js
~ dist/bridge.js
~ bin/serve.js
~ types/context.d.ts
~ types/index.d.ts
~ types/standalone.d.ts