Le nom @ng-eventually/sdk entrait en collision avec le SDK de NextGraph, dont
ce paquet est justement un polyfill. Impossible d'écrire « le SDK » sans lever
l'ambiguïté à chaque phrase — et le contrat publié, lu par une application,
était le pire endroit pour laisser traîner ça.
packages/sdk → packages/polyfill, @ng-eventually/sdk → @ng-eventually/polyfill,
contract_sdk-surface → contract_polyfill-surface, e2e/sdk-entry.ts →
e2e/polyfill-entry.ts, docs/sdk-reference.md → docs/polyfill-reference.md.
Les occurrences de « SDK » qui désignent celui de NextGraph restent intactes,
y compris les chemins dans nextgraph-rs (sdk/js/orm, sdk/js/web). Le tri s'est
fait occurrence par occurrence, pas par substitution.
Le contrat énonce désormais son identité en une phrase : « This package is a
polyfill of NextGraph's SDK. »
L'objectif acté était deux appels spécifiques au polyfill, voire un. Il en publiait
quatre. Chacun des trois de trop était une raison que la BIBLIOTHÈQUE a, pas un besoin
qu'une application a :
- **`configureStoreRegistry`** existait parce qu'il y a deux internes à câbler — le SDK
injecté d'un côté, la session de l'autre. Vu de l'appelant, les deux disent « voici ce
qu'il te faut pour tourner ». Replié dans `configure`, qui prend désormais
`getSession` / `normalizeId` / `pointerGuard`.
- **`setCurrentUser`** n'a plus lieu d'être publié depuis que le portail d'accès est
passé dans le polyfill : c'est lui qui pose l'identité. Et une application qui nomme
sa propre identité est exactement le geste qui inverse le modèle — il ne doit pas
exister d'appel publié vers lequel se tourner. Le harnais e2e, lui, joue plusieurs
identités sur une même page ; il y accède par le chemin interne, ce qu'un harnais a
le droit de faire et une application non.
- **`connectedUser`** est maintenant attendu DANS `ensureIdentity`. Ce n'était pas une
commodité : la suite applicative avait montré qu'une app devait l'attendre elle-même,
sinon une note qu'on venait de lui partager se lisait comme illisible. J'avais traité
le symptôme dans l'app d'exemple ; le défaut était côté bibliothèque. En amont, ouvrir
la session EST la connexion — aucune application n'attend un second appel.
Reste donc `configure({ … })`, plus `await ensureIdentity()` dont le site d'appel
survit à la migration : une application attendra toujours une session avant de rendre.
Le test étendu hier a fait son travail : les deux contrôles de contrat sont passés au
rouge sur `configureStoreRegistry`, `connectedUser` et `StoreRegistryDeps` dès que la
surface a bougé.
180 tests unitaires, e2e 40/40 (3,4 min) et applicatif 10/10 (0,8 min).
Une seconde suite e2e, `packages/sdk/e2e/notebook.ts` (`test:e2e:app`), qui pilote
`examples/notebook` par le DOM — une page de navigateur par identité — contre le même
broker réel.
**Pourquoi une seconde suite plutôt qu'un ajout dans la première.** `run.ts` parle à un
sac de méthodes sur `window.__sdk` : cela prouve que les fonctions TOURNENT, jamais
qu'une application peut s'écrire avec. L'écart a déjà coûté un défaut livré — l'inbox
d'un document était verte ici et inutilisable en pratique, parce que le harnais pouvait
faire traverser une adresse d'une identité à l'autre par une variable, canal qu'aucune
application n'a. Ici, rien ne traverse que ce qui traverse dans la vie : la RÉFÉRENCE
d'une note, recopiée d'un écran, et un identifiant tapé dans un champ.
Quatre parcours, qui se lisent comme des parcours :
- Bob lit la note publique d'Alice depuis sa seule référence — la propriété pour
laquelle l'émulation du store public existe, vérifiée bout en bout et sans qu'aucune
clé ne circule ;
- la note protégée d'Alice reste fermée jusqu'à ce qu'elle la partage — même geste côté
Bob, issue opposée, décidée par où la note se trouve ;
- Bob laisse un message sur la note d'Alice, et seule Alice le lit — il TROUVE l'adresse
depuis la note, personne ne la lui donne ;
- la liste de chacun ne contient que ses notes.
**Deux étapes quittent `run.ts`** (`documentInboxDeposit`, `capsShareCap`), avec un
commentaire disant où elles sont parties : ce sont des parcours, et ils valent plus joués
sur deux écrans que sur deux appels d'une même page. Ce qui reste là-bas est ce qu'une
application ne fait pas : primitives, caractérisation, régressions de démarrage à froid.
**Trois défauts trouvés en écrivant la suite**, tous côté application et invisibles pour
le harnais : `connectedUser()` devait être attendu à la connexion (sinon une note qu'on
vient de vous partager se lit comme illisible — ce qui ressemble à un problème de droits
alors que c'est un problème de moment) ; une réponse périmée restait affichée à côté
d'une question fraîche ; et changer de portée ne rafraîchissait pas la liste. L'app
affiche désormais la référence de chaque note — ce qu'aucun écran ne montre, aucun
utilisateur ne peut le faire circuler.
Corrigé au passage : le `.gitignore` pointait encore `packages/client/`, si bien que le
commit de renommage a embarqué le profil Playwright de la suite e2e (226 fichiers). Les
chemins sont réalignés et le commit précédent a été amendé — rien n'était poussé.
179 tests unitaires, e2e 40/40 (3,2 min) et applicatif 10/10 (0,7 min).
Deux mouvements de surface, aucun changement de comportement.
**`packages/client` → `packages/sdk`, `@ng-eventually/client` → `@ng-eventually/sdk`.**
« client » ne disait rien : ce paquet EST le SDK que l'application appelle, et c'est
tout ce qu'elle appelle. L'ancien nom reste comme mot-clé de recherche dans
`docs/source-layout-by-fate.md` et le tableau des paquets du README.
**Une seule entrée.** L'entrée `./polyfill` disparaît ; ses symboles applicatifs —
`configure`, `configureStoreRegistry`, `setCurrentUser`, `connectedUser` et leurs types
— vivent dans un bloc `POLYFILL-ERA` de `src/index.ts`.
Ce que la seconde porte portait mérite d'être nommé avant d'être retiré : *ce qu'on
importe de ce chemin est exactement ce qu'on supprimera à la migration*. Une seule
porte perd ce signal — rien à la ligne d'import ne distingue `configure`, qui part, de
`docs`, que le vrai SDK remplace sur place. Trois choses le portent désormais : le bloc
lui-même, l'inventaire d'exports de `docs/api-contract.md` (épinglé par
`test/vocabulary.test.ts`, donc il ne peut pas rancir en silence), et le contrôle de
vocabulaire sur les noms publiés.
**Six symboles quittent la surface au passage**, et la fusion est ce qui a rendu le
choix visible plutôt qu'hérité :
- `getConfig` / `getStoreRegistryDeps` — câblage interne, atteint par
`shared-wallet/bootstrap` ;
- `resetConfig` / `resetStoreRegistry` / `resetCaps` — remises à zéro de test, atteintes
par leur chemin interne, ce qui est leur raison d'être ;
- le `share` direct — `inbox.share` a toujours été la même fonction, et la publier deux
fois brouillait la frontière qu'elle servait à marquer.
Corrections d'affirmations fausses trouvées en chemin : le contrat annonçait `isNuri` /
`hasReadCap` sur la porte SDK alors qu'ils ne sont plus exportés depuis le passage au
permissif en entrée (`NuriLike` validé à la porte) ; le README du paquet documentait
`capFor`, `shareCap`, `getCaps` et `publishRepoLink`, dont aucun n'existe ; et le README
de l'app d'exemple affirmait que la suite e2e la pilote, ce qui reste à faire.
179 tests unitaires, typecheck bibliothèque / exemple / harnais, e2e 42/42 contre le
broker en ligne — mesuré une fois après le renommage, une fois après la fusion.
Le harnais e2e parlait à un sac de méthodes posé sur `window.__sdk`. Il prouvait
que les fonctions s'exécutaient, jamais qu'on pouvait écrire une application avec
— et cet écart a livré un vrai défaut : l'inbox d'un document était verte en test
et inutilisable en vrai, parce que le harnais faisait traverser une adresse d'une
identité à l'autre par une variable, ce qu'aucune application ne peut faire.
`examples/notebook` est une application minimale en DOM natif, qui résout
`@ng-eventually/client` comme un consommateur externe (workspace, dépendance
déclarée, aucun import privilégié). Elle ne peut faire que ce qu'une application
peut faire.
Elle s'est déjà payée deux fois pendant son écriture :
- `UnionSubject.subject` et `.graph` étaient typés `string` alors que ce sont
toujours des références de document. Un consommateur devait donc caster ce
qu'il venait de lire avant de le repasser — un cast à cet endroit précis
rouvre la confusion que les types template literal existent pour fermer.
- l'écran d'accès normalisait ce que l'utilisateur SAISIT mais pas ce que l'URL
porte, si bien qu'un lien `?ng-id=@Erin` ouvrait un espace différent de celui
de la même personne tapant `erin`. Une seule normalisation désormais, celle
du registre.
Le domaine est volontairement mince — des notes — mais suffit à exercer le
placement par scope, la possession de caps, le partage dirigé, les inbox par
document et la lecture réactive.
170 tests unitaires, typecheck vert sur la lib, l'exemple et le harnais.