fix: les trois citations que les paquets installes invalidaient

Recherche menee sur les 94 citations vers engine/ et sdk/, en comparant
l'arbre cite aux artefacts publies. Onze citations de niveau 2
(doc_subscribe, sparql_update, sparql_query, doc_create) sont
identiques de part et d'autre, ainsi que ShapeType, Schema et
DeepSignalSet. Trois ne l'etaient pas.

- Scope : la revision citee declare `graphs?` OPTIONNEL,
  @ng-org/orm@0.1.2-alpha.21 le rend requis - et c'est le paquet
  installe qui est reexporte ici.
- BaseType : la revision citee garde "@graph" en commentaire derriere un
  TODO, @ng-org/shex-orm@0.1.2-alpha.9 le declare.
- NG : le compte de membres disait 88 en neuf endroits, dont un marque
  VERIFIED et un autre annoncant une mesure contre alpha.14. Le paquet
  installe en declare 90 - 84 fonctions et 6 classes RDF - et deux des
  quatre-vingt-dix, graph_orm_next_page et graph_orm_previous_page, sont
  absents de l'arbre cite. Le compte vit desormais a un seul endroit,
  avec la version a laquelle il appartient ; ailleurs il disparait, parce
  que la phrase disait "tous les membres" et que le chiffre n'y ajoutait
  qu'une occasion de perimer.

Les deux premieres reprennent la forme que RdfOrmConfig et
OrmSubscriptionHandle portaient deja : une clause finale nommant la
version publiee la ou elle diverge de la revision citee.
This commit is contained in:
Sylvain Duchesne
2026-09-24 15:05:36 +02:00
parent 7bcb52a5fd
commit 139d21843a
7 changed files with 25 additions and 17 deletions
+3 -3
View File
@@ -121,7 +121,7 @@ export declare type Session = {
};
```
`initNg`'s argument in position 0 is substituted since `1.0.0-dev.7`, and it is the same kind of move for the same reason. That argument is what the ORM reaches the engine through afterwards - every subscription, every `insertObject`, every batched patch awaits the promise this call resolves - and an ORM write leaves by `ng.graph_orm_update`, which is where the emulated write guard now sits (§ 3). Handing the caller's raw SDK object over leaves that guard on a door nothing knocks on, and "pass the package's `ng` here" would be an ordering rule in prose: unenforceable, silent when missed, and in fact missed by both consumers in this repository. What is substituted is the package's own `ng` - the same object the entry publishes, forwarding all 88 members to the very object `configure()` received - so the ORM now reaches the engine through exactly what the application does.
`initNg`'s argument in position 0 is substituted since `1.0.0-dev.7`, and it is the same kind of move for the same reason. That argument is what the ORM reaches the engine through afterwards - every subscription, every `insertObject`, every batched patch awaits the promise this call resolves - and an ORM write leaves by `ng.graph_orm_update`, which is where the emulated write guard now sits (§ 3). Handing the caller's raw SDK object over leaves that guard on a door nothing knocks on, and "pass the package's `ng` here" would be an ordering rule in prose: unenforceable, silent when missed, and in fact missed by both consumers in this repository. What is substituted is the package's own `ng` - the same object the entry publishes, forwarding every member to the very object `configure()` received - so the ORM now reaches the engine through exactly what the application does.
Divergence: none in behaviour (pure forwarding), but the wrappers erase the parameter types. A consumer typing calls against them learns nothing it must unlearn - it just gets no compile-time help the real SDK would give.
@@ -173,11 +173,11 @@ export type { NG } from "@ng-org/web";
There is exactly one instance of that proxy for the whole package (`wrappedNg()`), because two things now hold it: the entry publishes it, and `initNg` hands it to the ORM (§ 2).
**The passthrough itself was broken until `1.0.0-dev.7`, for every one of the 88 members, and nothing noticed.** It read `Reflect.get(ng, prop).bind(ng)`. `@ng-org/web`'s `ng` is ITSELF a Proxy - an "async proxy" whose `get` trap answers EVERY key with a fresh path-carrying callable and whose reflection traps throw (`dist/ngweb.js`, the `l(e = [])` factory) - so `.bind` never reached `Function.prototype.bind`: it was just another key, calling the result posted a remote request named after the MEMBER carrying the bind arguments, and what came back was a **Promise**. A caller therefore got the SDK's own `<member> is not a function`. Two things hid it: this library's internals never go through this proxy (they call the injected `ng` directly), and every fake in the suite is a plain object, on which `bind` behaves. It surfaced the moment `initNg` handed the ORM this object - `orm_start_graph` was not a function, the ORM catches that and only `console.error`s it, so a shape read stayed `isPending` for ever, which is the failure mode § 5 describes. MEASURED against `@ng-org/web@0.1.2-alpha.14`. The member is now FORWARDED (`Reflect.apply(real, ng, args)`), which gives a plain object the receiver it needs and lets the async proxy use the path it already carries; `test/ng-proxy.test.ts` pins it against a stand-in shaped like the real one.
**The passthrough itself was broken until `1.0.0-dev.7`, for every one of its members, and nothing noticed.** It read `Reflect.get(ng, prop).bind(ng)`. `@ng-org/web`'s `ng` is ITSELF a Proxy - an "async proxy" whose `get` trap answers EVERY key with a fresh path-carrying callable and whose reflection traps throw (`dist/ngweb.js`, the `l(e = [])` factory) - so `.bind` never reached `Function.prototype.bind`: it was just another key, calling the result posted a remote request named after the MEMBER carrying the bind arguments, and what came back was a **Promise**. A caller therefore got the SDK's own `<member> is not a function`. Two things hid it: this library's internals never go through this proxy (they call the injected `ng` directly), and every fake in the suite is a plain object, on which `bind` behaves. It surfaced the moment `initNg` handed the ORM this object - `orm_start_graph` was not a function, the ORM catches that and only `console.error`s it, so a shape read stayed `isPending` for ever, which is the failure mode § 5 describes. MEASURED against `@ng-org/web@0.1.2-alpha.14`. The member is now FORWARDED (`Reflect.apply(real, ng, args)`), which gives a plain object the receiver it needs and lets the async proxy use the path it already carries; `test/ng-proxy.test.ts` pins it against a stand-in shaped like the real one.
### Target
**PASSTHROUGH (level 2, VERIFIED).** `export declare const ng: NG` with `NG = typeof NGModule`, **88** exported members (`index.d.ts:136-231`; the namespace body is `:140-231`). The surface is identical by construction — the proxy adds no member and removes none — **and identical at the type level too since 2026-08-14**, when the published `ng` stopped being declared `Record<string, any>` and took `NG`, upstream's own type. That escape hatch silently dropped all 88 signatures: an application got no completion, and a misspelt member typechecked.
**PASSTHROUGH (level 2, VERIFIED).** `export declare const ng: NG` with `NG = typeof NGModule`, **90** exported members in `@ng-org/web@0.1.2-alpha.14` - 84 functions and 6 RDF classes, counted in the `NGModule` namespace of the installed `index.d.ts`. The count is named with its release because it MOVES with one: this document recorded **88**, two short, and two of the ninety - `graph_orm_next_page` and `graph_orm_previous_page` - appear nowhere in the cited checkout's `sdk/js/lib-wasm/src/lib.rs`, the tree declaring the previous release `@ng-org/web@0.1.2-alpha.13`. The figure was never re-measured after the build moved. The surface is identical by construction — the proxy adds no member and removes none — **and identical at the type level too since 2026-08-14**, when the published `ng` stopped being declared `Record<string, any>` and took `NG`, upstream's own type. That escape hatch silently dropped every signature: an application got no completion, and a misspelt member typechecked.
The overrides:
+1 -1
View File
@@ -23,7 +23,7 @@ Builds the `ng` Proxy: forwards every property to the injected real `ng`, overri
- `wrappedNg()` exists because the proxy now has **two** holders: `index.ts` publishes it, and `surface/lifecycle.ts` hands it to the ORM through `initNg`, so the two must be the same object. It keeps no state (every property is answered from `getConfig()` at access time), so it survives a re-`configure()` and needs no reset.
- The `sparql_update` guard stands in for the engine's write-permission model (`verify_perm` inside `Commit::verify`, `engine/repo/src/commit.rs:892-899`) - same analysis as `docs/api-contract.md` § 3. It is deliberately left on the decorative write-cap list rather than on `assertMayWrite`: the library's own REGISTERS are written through this member (`emulated-verifier/register-write.ts`), and a store document is not owned in the authorship sense, so arming it there would refuse every register write. `graph_orm_update` has no such traffic, which is why the live predicate sits on it.
- ~~**Defect - the `login` arm fabricates a member (Findings F1).**~~ **FIXED 2026-08-03**, and this bullet had outlived it: `@ng-org/web` has no `login` - none among the exports of `index.d.ts`, no `fn login` in `sdk/js/lib-wasm/src/lib.rs` - and the arm that returned a function for `prop === "login"` was the one place the proxy added a member, against its own header and the surface contract's "adds no member and removes none" (§ 3). It is gone from `ng-proxy.ts`; the note stays as the record of what the proxy must never do again.
- **Defect - the passthrough could not survive the real `ng` (FIXED `1.0.0-dev.7`).** It was `Reflect.get(ng, prop).bind(ng)`, and `@ng-org/web`'s `ng` is itself a Proxy whose `get` trap answers every key - `bind` included - with a path-carrying callable returning a Promise. So the published `ng` handed back a promise for all 88 members and a caller got `<member> is not a function`. Invisible because the library's own internals bypass this proxy and every fake in the suite is a plain object. Now `Reflect.apply(real, ng, args)`; the stand-in in `test/ng-proxy.test.ts` is shaped like the real proxy, which is what makes the test one.
- **Defect - the passthrough could not survive the real `ng` (FIXED `1.0.0-dev.7`).** It was `Reflect.get(ng, prop).bind(ng)`, and `@ng-org/web`'s `ng` is itself a Proxy whose `get` trap answers every key - `bind` included - with a path-carrying callable returning a Promise. So the published `ng` handed back a promise for every member and a caller got `<member> is not a function`. Invisible because the library's own internals bypass this proxy and every fake in the suite is a plain object. Now `Reflect.apply(real, ng, args)`; the stand-in in `test/ng-proxy.test.ts` is shaped like the real proxy, which is what makes the test one.
- Disappears at migration (the whole module).
## 2. NURI internals — the unexported slice of `nuri.ts`, plus the minting point