Le régime des contrats est devenu bidirectionnel : chaque partie rédige sa position dans son dépôt et tire celle de l'autre. Le fournisseur écrit son engagement, chaque consommateur écrit sa déclaration — ce qu'il appelle vraiment, sous quelles contraintes, et avec quelles frictions. Ce dernier point change nos usages : la section Frictions est le chemin par lequel un consommateur expose un problème, le canal hors-bande ne portant que le signal. Les huit points que Festipod nous a remontés auraient dû arriver par là, et notre réponse appartient à l'engagement plutôt qu'à un échange qui s'évapore. Trois interfaces existaient, une seule était documentée. Elles ont maintenant leur dossier et leurs deux voix : la surface du polyfill, ng-e2e-helpers, et la couche d'indexation. On a écrit les engagements dont nous sommes l'auteur et les déclarations des consommateurs qui sont à nous — délibérément AUCUN usage_festipod : ce n'est pas notre dépôt, et décider à leur place ce qu'ils consomment viderait le mécanisme de son sens. Et la Change policy s'inverse. Elle disait « ce paquet n'offre pas de stabilité sémantique ». Refuser un schéma ne ralentit pas le mouvement — ça retire au consommateur le seul outil pour le gérer, alors qu'on faisait déjà le travail d'une publication versionnée sans lui en donner l'étiquette. Semver, donc, et les majeurs sont le cas NORMAL : une surface qui converge vers une cible qu'elle n'atteint pas encore casse souvent, et c'est le signal vrai. Le texte dit les déclencheurs plutôt que le nom du schéma — retirer un symbole publié ou resserrer un appel est un majeur, ajouter est un mineur. Version pleine sur main, pré-version sur une branche : Festipod peut épingler aujourd'hui sans qu'on lui promette du stable, et sans qu'on ait à lui retirer son adresse à la fusion. Au passage, la signature publiée de watchShape était fausse — elle annonçait un argument unique là où l'appel en prend deux. Rapporté par Festipod, confirmé indépendamment.
5.7 KiB
type, summary, against
| type | summary | against |
|---|---|---|
| usage | What the polyfill's own end-to-end suites call from ng-e2e-helpers, under which constraints, and where the fit still costs them code | ng-e2e-helpers@1.0.0-dev.1 |
usage_polyfill-e2e — the polyfill's end-to-end suites
The consumer is the end-to-end suite of @ng-eventually/polyfill: four entry points under that package's e2e/ — a batch of SDK checks against the broker, an applicative suite driving the example application, a reactivity probe, and a cold-start reproduction — plus the page module they share.
This consumer lives in the same repository as the provider. That changes nothing about what is declared here: the engagement is what these suites are written against, and anything they call that is NOT listed below is theirs to fix if it moves.
Consumed surface
Bounds — within, armSuiteDeadline, closeQuietly, enclosingBound, firstLine. within is the most-called entry of the whole package here: every bridge call into the application iframe goes through it.
Measurement — measured. Nothing reads record, timingsWanted or printTimings directly; the E2E_TIMINGS=1 output is consumed as printed output, not as a call.
Browser and profiles — launchWatchedContext, closeContext, newPage, and the type RunProfile. newRunProfile and isAlive are never called: profiles are always obtained through a wallet call.
Wallet — mintWalletProfile (the batch wallet, minted once per run), mintWalletProfileKeepingContext (the cold-start reproduction, which needs the very first application session over a never-used wallet), emptyProfileContext + importWalletFile (the reconnection journey), exportWalletBytes (serving the wallet from the application's own bundle), and the type WalletCredentials.
Broker crossing — setupBrokerPage in all four suites, completeBrokerLogin in the applicative one.
Serving — serveOnEphemeralPort, for both the SDK page and the example application's bundle.
Known failure modes — browserTrouble, frameTrouble.
Report — declareSuite and the types JourneyDeclaration, Prerequisite. Used by the applicative suite only.
Constants — BROKER_ROUND_TRIP_MS, NEW_PAGE_MS, both as inputs to enclosingBound rather than as waits of their own.
Everything else the package exports is offered and NOT consumed here — the screen inventory (BROKER_SCREENS, WALLET_APP, WALLET_CREATION, WALLET_IMPORT, brokerRedirectFor, and the screen types), createWalletInContext, exportWalletFile, mintWalletBytes, DEFAULT_WALLET_NAME, browserLost, lossDeclared, the exported error classes, and the remaining *_MS constants. It is safely evolvable as far as these suites are concerned.
Constraints
One Playwright, and the browser types come from the helpers. These suites import Frame, Page and BrowserContext from playwright directly today, which holds only because the package resolves the same copy. The safer form — deriving them from the helpers that return them — is what a consumer in another repository must do, and what this one should adopt.
Every suite mints its own physical wallet, per run. Nothing is carried between runs and no wallet is reused, so a run's result never depends on a previous one. The batch wallet's NAME is stable and its identity is not: two runs sharing the name share nothing else.
The password is supplied, never defaulted. These suites also read the barrier's displayed password back off its own screen and pass it to importWalletFile, which is the only way to prove that what the barrier shows is what opens the file. A defaulted password would make that check vacuous.
An enclosing bound is at least the sum of what it encloses. These suites compute every enclosure with enclosingBound rather than picking a round number, because an enclosure that fires first reports "the journey timed out" and never names the step that hung.
A wait must not be entered unbounded. Playwright's frame.evaluate waits forever, so no bridge call is made outside within.
Frictions
frame.evaluate has no bound, and three suites each wrote the same wrapper. A sdk(frame, method, …args) helper — within(…, BRIDGE_MS, () => frame.evaluate(…)) — is duplicated verbatim in three of the four entry points. The bound is the single most important one in each of them, and the package offers nothing to obtain it from. A bounded evaluate helper here would delete the duplication and stop each new suite re-deriving it.
"Measured and bounded" is one intent and two calls. Sizing a bound from its own measurement is the discipline this package prescribes, yet the applicative suite composes measured(what, ms, (bound) => within(what, bound, task)) by hand — and the indexing layer's suite, in another repository, wrote the same line byte for byte. A helper that pairs them belongs on the engagement.
declareSuite fits a journey suite and not a check batch. Only the applicative suite uses it; the other three declare a local record/check pair and print their own summary, because their shape is a flat list of checks discovered as the run goes rather than named journeys declared up front. The up-front declaration is exactly what makes the report bounded, so this is not a request to relax it — but three of four suites falling outside it suggests the package covers one of two real shapes.
The screen inventory is exported and unreachable in practice. BROKER_SCREENS and the WALLET_* page descriptions are published, but a suite that crosses the broker with setupBrokerPage never needs them, and one that wanted to extend the crossing has no supported way to hand a modified inventory back in. As it stands they read as internals that happen to be exported.