Commit Graph

4 Commits

Author SHA1 Message Date
Sylvain Duchesne ebf866b1f2 refactor(api): précis en sortie, permissif en entrée — plus de guard publié
Un polyfill ne doit rien faire de plus que ce qui est prévu. `isNuri` /
`hasReadCap` et les utilitaires SPARQL `escapeLiteral` / `escapeIri` /
`assertNuri` n'ont de pendant à aucun niveau et n'en auront pas : le binding
prend `nuri: String`, le moteur est fortement typé en Rust et n'a besoin
d'aucun prédicat, l'ORM n'expose rien de tel. Le contrat les justifiait parce
qu'ils « restent utiles à n'importe quelle app » — c'est exactement le
raisonnement à refuser : utile n'est pas prévu, et chacun serait un appel à
réécrire le jour du SDK.

Le besoin d'un guard venait de notre propre signature : les entrées publiques
exigeaient `Nuri`, donc un consommateur devait narrower ce qu'il lisait d'une
URL ou du stockage. Elles prennent désormais `NuriLike` — n'importe quelle
chaîne — et valident à l'intérieur (`toNuri`). Ce que la bibliothèque REND
reste typé `Nuri` : l'app en profite gratuitement, et un type plus large ne
cassera rien quand le SDK rendra des chaînes.

Les guards et les utilitaires restent, internes, là où la validation se fait.

Un défaut introduit puis corrigé en chemin, qui valait le test qu'il a produit :
`readUnion` a toujours toléré les trous dans sa liste — un index de scope peut
porter une entrée blanche, et un appelant qui assemble depuis des valeurs
optionnelles n'a pas à compacter. Valider AVANT de filtrer a transformé cette
tolérance en exception. Vide est une absence, pas une référence malformée ; les
deux sont désormais distingués par un test.

170 tests unitaires, e2e 42/42 contre le broker, typecheck vert sur la
bibliothèque, l'exemple et le harnais.
2026-08-06 10:51:36 +02:00
Sylvain Duchesne 36c0148750 refactor(layout): extraire le magasin d'injection vers shared-wallet/bootstrap
L'entrée publique `/polyfill` portait le magasin d'injection (le `ng` injecté,
les dépendances du registre, QUI est connecté, le singleton `CapRegistry`). Tout
module interne l'importait donc pour atteindre la config — ce qui faisait de
l'entrée une dépendance du code qu'elle publie, avec les cycles
`polyfill` <-> `connect` et `polyfill` <-> `inbox`.

Le magasin rejoint `shared-wallet/` : rien n'est injecté en amont, l'app importe
le SDK et « qui suis-je » est la session — il n'y a pas de relais d'utilisateur
courant parce qu'un wallet a exactement un user. Ce module est la forme de cette
absence, il s'évapore en entier à la migration.

L'entrée ne fait plus que ré-exporter. Plus aucun module interne n'importe
`polyfill`.
2026-08-04 13:51:56 +02:00
Sylvain Duchesne cd096de2b0 refactor(layout): les portes non gardées en un seul module, mintCap chez le verifier
Trois extractions que le rangement par destin a rendues évidentes :

- `subscribePhysicalDoc` et `ensurePhysicalRepoOpen` rejoignent
  `shared-wallet/physical.ts`, qui devient l'API non gardée de la machinerie
  en un seul endroit — ce que sa propre doctrine réclamait (« des fonctions
  séparées, jamais des exemptions »). Elles vivaient jusqu'ici à côté de leurs
  jumelles gardées, à un import près d'être atteintes par erreur. Les deux
  cœurs sont désormais exportés sous un nom `Unguarded`, pour cet unique
  importateur.
- `mintCap` et sa valeur de remplacement quittent `model/nuri.ts` pour
  `emulated-verifier/caps.ts`. `model/` transcrit le vocabulaire d'adressage de
  la cible ; frapper une clé n'en fait pas partie — en amont le moteur frappe à
  la création du repo, et ensuite on cherche un cap dans ce qu'on détient ou on
  le reçoit. Sa présence dans le module modèle contredisait l'en-tête de ce
  module et logeait la seule valeur inventée de l'émulation dans le fichier qui
  se dit vocabulaire vérifié.

Un cycle est apparu au passage (`open-repo` ↔ `physical`) : résolu en faisant
appeler à `open-repo` le cœur non gardé plutôt que la porte de la machinerie.

157 tests unitaires, typecheck src/test/e2e vert.
2026-08-04 12:49:59 +02:00
Sylvain Duchesne 88914f50ae refactor(layout): ranger les modules par destin à la migration
Les 25 modules étaient à plat, nommés d'après ce qu'ils font mécaniquement
(`store-registry`, `read-model`, `reach`, `caps`). Rien dans l'arborescence ne
disait lesquels DEVIENDRONT le vrai SDK, lesquels tiennent lieu du travail que
le verifier fera nativement, et lesquels n'existent que parce qu'un wallet est
partagé — trois destins sans rapport.

Quatre dossiers, les deux fichiers d'entrée restant à la racine pour que
l'`exports` du paquet et le code du consommateur ne bougent pas :

- `model/` — le modèle d'adressage de la cible, transcrit : vocabulaire pur,
  pas d'I/O. Survit comme connaissance.
- `surface/` — ce que l'app touche, chaque symbole ayant un pendant cible
  documenté. Supprimé quand l'alias bascule ; le code de l'app est inchangé.
- `emulated-verifier/` — les doublures de ce que le verifier fait nativement :
  possession, dépôt des caps, frontière, non-livraison, traitement des inbox,
  registres de branche, ouverture de repo. **C'est le dossier où diverger du
  modèle est possible.** Le préfixe `emulated-` porte le sens : tient lieu de,
  jamais est — cette bibliothèque ne réside dans aucune couche de la cible,
  elle les référence.
- `shared-wallet/` — n'existe que parce qu'un wallet héberge toutes les
  identités. Aucun pendant, rien sur quoi s'aligner ; sa seule loi est de
  rester invisible depuis `surface/`. S'évapore, remplacé par rien.

`store-registry-api.ts` devient `surface/placement.ts` : il faisait déjà à la
main ce que la frontière de dossier fait structurellement — c'est la meilleure
preuve interne du bien-fondé de ce rangement.

Ce commit ne fait que déplacer et recâbler les imports (src, test, e2e). Les
scissions des modules à cheval suivent.

157 tests unitaires, typecheck src/test/e2e vert.
2026-08-04 12:46:44 +02:00