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.
This commit is contained in:
@@ -138,3 +138,14 @@ test("readUnion drops a doc whose cap the holder does not hold", async () => {
|
||||
setCurrentUser("bob");
|
||||
expect(await readUnion(both)).toEqual([]);
|
||||
});
|
||||
|
||||
test("readUnion tolerates holes in the list, and refuses a malformed reference", async () => {
|
||||
// Two different things that must not be conflated, and conflating them broke a whole
|
||||
// reconnect run: an EMPTY entry is absence — a scope index can carry one, and a caller
|
||||
// assembling a list from optional values should not have to compact it — while a
|
||||
// non-reference is a caller mistake worth a loud error. Validating before filtering
|
||||
// turned the first into the second.
|
||||
inject();
|
||||
await expect(readUnion(["", null as never, undefined as never])).resolves.toEqual([]);
|
||||
await expect(readUnion(["not-a-nuri"])).rejects.toThrow(/not a NextGraph reference/i);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user