docs: ce que le hook du SDK rend vraiment, la ou le depot disait un set nu

Six endroits affirmaient au present que `useShape` rend un `DeepSignalSet` nu.
C-est vrai de la revision citee, faux de celle contre laquelle ce paquet est
construit - et deux de ces six PORTAIENT une decision de provenance :

- `ShapeQuery kind=invention` etait justifie par "aucun type amont ne porte
  d-etat de chargement", alors que `isLoading` en porte un. Le kind tient, mais
  pour l-autre raison : ce qui n-a de contrepartie nulle part est le troisieme
  etat, la lecture ECHOUEE, parce qu-un `orm_start_graph` refuse n-est qu-un
  `console.error` et laisse une attente eternelle a tous les niveaux.
- l-annotation `announced-not-written` de `useShapeQuery` posait la meme
  premisse fausse. La direction annoncee ne bouge pas ; sa premisse si.

Ajoutee aussi l-instruction d-injection qui ne resolvait pas : l-export `.` de
`@ng-org/orm` pointe sur `core`, qui n-exporte aucun `useShape`. Il est sur
`/react` (export par defaut) ou `reactUseShape` sur `/all`.
This commit is contained in:
Sylvain Duchesne
2026-09-24 18:28:24 +02:00
parent 3a8941159f
commit e16fd6a707
6 changed files with 53 additions and 28 deletions
+21 -6
View File
@@ -47,7 +47,10 @@ The prose labels above map onto the annotation's closed `kind` set, and where th
// shared-wallet/bootstrap.ts
export interface EventuallyConfig {
ng: NgLike; // the REAL @ng-org/web ng
useShape: UseShapeLike; // the REAL @ng-org/orm useShape
useShape: UseShapeLike; // the REAL React hook: `@ng-org/orm/react`'s
// default export, or `reactUseShape` from
// `@ng-org/orm/all`. NOT on the main entry:
// `.` resolves to `core`, which has no `useShape`.
ormSubscription?: OrmSubscriptionLike; // the REAL @ng-org/orm RdfOrmSubscription.getOrCreate
pointerGuard?: { attempts?: number; baseMs?: number; maxStepMs?: number };
sharedWallet?: SharedWalletConfig; // the gate's, § 2bis
@@ -192,18 +195,21 @@ The overrides:
### Today — `@ng-eventually/polyfill`
```ts
// use-shape.ts:12
export function useShape(shapeType: unknown, scope: unknown): unknown;
// use-shape.ts
export function useShape<T extends BaseType>(
shapeType: ShapeType<T>,
conf: RdfOrmConfig<T> | string | undefined,
): ShapeResult; // { data, promise, subscription, isLoading, nextPage?, previousPage? }
// type re-exports, index.ts:48-49
export type { ShapeType, BaseType, Schema } from "@ng-org/shex-orm";
export type { DeepSignalSet } from "@ng-org/alien-deepsignals";
```
Behaviour: forwards to the injected real `useShape`; once any emulated cap exists (`caps.isEnforcing()`), the returned set is wrapped in a read-filtered view keeping only items whose document cap the current holder has.
Behaviour: forwards to the injected real hook; once any emulated cap exists (`caps.isEnforcing()`), the RESULT OBJECT is wrapped in a read-filtered view. `data` and `promise` yield only items whose document cap the current holder has, `subscription` is refused (it is the ORM's own handle, which this surface does not publish), `isLoading` and the two page commands pass through, and any other member is refused unless what is behind it is a plain scalar - the rule is the value's type, so a member a later release adds is decided before anyone has seen it.
### Target
**PASSTHROUGH (level 3, VERIFIED), with a signature the wrapper widens.** The real hook:
**PASSTHROUGH (level 3, VERIFIED).** The real hook, as the CITED revision writes it:
```ts
// level 3 — @ng-org/orm/react: sdk/js/orm/src/frontendAdapters/react/useShape.ts:86-124
@@ -212,6 +218,15 @@ const useShape = <T extends BaseType>(
scope: Scope | string | undefined
) => DeepSignalSet<T>;
// …and as the BUILD ships it (`dist/frontendAdapters/react/useShape.d.ts`), which is the
// shape the wrapper mirrors: the collection moved inside a result object, and the second
// parameter widened from a bare `Scope` to the config. The gap between the two columns is
// the one the provenance anchor records.
const useShape = <ST extends ShapeType<T>, const CONF extends RdfOrmConfig<T>, T extends BaseType>(
shape: ST,
conf: CONF | string | undefined
) => UseShapeResult<ST, CONF, T>; // { data, promise, subscription, isLoading, nextPage, previousPage }
// its Scope — sdk/js/orm/src/types.ts:25-38 (NOT this lib's Scope, see § 12)
// *(Corrected 2026-08-16: this said `sdk/js/orm/src/model/types.ts`, a path that does not
// exist in the clone. The type is at `sdk/js/orm/src/types.ts:25`. A citation nobody can
@@ -257,7 +272,7 @@ export function useShapeQuery<T extends BaseType = BaseType>(
): ShapeObservable<T>;
```
**What it reads THROUGH, since `1.0.0-dev.6`: the SDK's own shape query.** `RdfOrmSubscription.getOrCreate(shapeType, scope)`, injected at `configure` as `ormSubscription` (§ 1). Four steps went with the change and they were one mechanism: resolving the logical scope to a document set (`listMyEntityDocs`), opening those documents through `ensureReposOpen` for a sync barrier, an anchored read over each, and filtering the result by the shape's `@type` IRI. All four existed because the parameter had to be TRANSLATED; once it is upstream's own `Scope`, the question goes to the thing that answers it, and `graphs` / `subjects` / `did:ng:i` are read by the engine literally. It does NOT delegate to the SDK's `useShape` — that is React's hook and its `DeepSignalSet` cannot carry the request state, which is the whole reason this symbol exists. What is taken from the SDK is the QUERY; the observable, the snapshot and the state tracking stay here, and so does the capability filter (`emulated-verifier/read-filter.ts`, applied per `@graph` on what comes back, exactly where § 4 applies it).
**What it reads THROUGH, since `1.0.0-dev.6`: the SDK's own shape query.** `RdfOrmSubscription.getOrCreate(shapeType, scope)`, injected at `configure` as `ormSubscription` (§ 1). Four steps went with the change and they were one mechanism: resolving the logical scope to a document set (`listMyEntityDocs`), opening those documents through `ensureReposOpen` for a sync barrier, an anchored read over each, and filtering the result by the shape's `@type` IRI. All four existed because the parameter had to be TRANSLATED; once it is upstream's own `Scope`, the question goes to the thing that answers it, and `graphs` / `subjects` / `did:ng:i` are read by the engine literally. It does NOT delegate to the SDK's `useShape` - that is React's hook, callable only from a render, and its result carries two states where this one publishes three: `isLoading` has no value for a read that will never answer, and there is no error state beside it. What is taken from the SDK is the QUERY; the observable, the snapshot and the state tracking stay here, and so does the capability filter (`emulated-verifier/read-filter.ts`, applied per `@graph` on what comes back, exactly where § 4 applies it).
**Consequences a caller sees.** `data` items are now the SHAPE's own objects — `@id`, `@graph` and the shape's readable predicates — where they were the property bags the then-published `readUnion` returned; `shapeType` is typed `ShapeType<T>` instead of `unknown`, so `T` follows the shape and the snapshot is typed without a cast; and the read no longer opens a document of its own, so a public-store document whose cap has not been fetched by some other read does not appear until it has been (the same limit § 4 carries, and the same `CapRegistry.onChange` recovery). *(Corrected 2026-09-21, `1.0.0-dev.9`: that last clause now holds only for `graphs: "did:ng:i"`. A document NAMED in `scope.graphs` is put through the same public-store fetch and sync barrier `docs.sparqlQuery` applies to its anchor, before the query opens - and is refused out loud rather than silently emptied when its cap cannot be obtained.)*