83575349d7
Les quatre paquets @ng-org sont deja a leur derniere version publiee
(web alpha.14, orm alpha.21, shex-orm alpha.9, alien-deepsignals
alpha.13) : le refresh ne deplace rien. Ce qui restait faux, c'est la
doctrine, laissee en arriere par le refresh precedent.
Le point de fond : le checkout epingle de nextgraph-rs (213338f6)
declare orm alpha.18, donc la source citee est TROIS versions derriere
les paquets contre lesquels ce depot compile. La ou les deux divergent,
les leaves nomment desormais les deux.
Faits corriges, tous verifies dans node_modules :
- les versions installees annoncees en tete de leaf (alpha.19/13/11)
- OrmSubscription est renomme RdfOrmSubscription dans alpha.21, fichier
compris (connector/RdfOrmSubscription.ts)
- alpha.21 exporte removeObject(graphNuri, subjectIri), absent de la
source ; sa propre documentation se contredit sur sa portee
- getOrCreate prend un RdfOrmConfig, plus un Scope nu, et l'instance
gagne mode/isReady/addChangeListener/nextPage/previousPage
- Scope.graphs est REQUIS dans alpha.21, et les deux axes lisent [] en
sens INVERSE : graphs [] = ensemble vide, subjects [] = aucun filtre.
La table le dit explicitement, c'est le piege deja paye une fois
- la lecture ORM live DEMARRE contre le deploiement (5 passed, 0 failed,
pret en 13ms) ; le lien mort vers bug_useshape-live-read-needs-a-newer
-broker disparait, ainsi que le "exit non-zero attendu" de la suite
Ce qui n'est PAS affirme : que le where/orderBy/pageSize d'alpha.21
atteigne le moteur. A 213338f6 start_orm construit encore son config a
partir de json!({}) derriere un TODO, donc la richesse est exprimable au
niveau 3 et jetee au niveau 1. Non mesure, donc non ecrit.
73 lines
8.2 KiB
Markdown
73 lines
8.2 KiB
Markdown
---
|
|
type: knowledge
|
|
summary: What the target offers for reading many objects — a graph is one document, a store's graph holds only its containment triples, and scoping a read by store is not expressible at all
|
|
last_checked: 2026-09-21
|
|
---
|
|
|
|
# What a read scope is upstream
|
|
|
|
Read at the source in `nextgraph-rs` (`213338f6`). Every claim points at a symbol or a module rather than a line, so `git grep` settles any doubt after the code moves. The question answered here is the one a caller actually asks: **how do I ask for many objects at once, and what can the request say?**
|
|
|
|
## The type, and the two axes it filters on
|
|
|
|
`Scope = { graphs?: string[] | string; subjects?: string[] }` (`sdk/js/orm/src/types.ts` at `213338f6`), normalised by `normalizeScope` into `{ graphs: string[]; subjects: string[] }`. **In the installed `@ng-org/orm@0.1.2-alpha.21`, `graphs` is REQUIRED** (`graphs: string[] | string`, `dist/types.d.ts`); `subjects?` stays optional. A caller that omitted `graphs` no longer compiles.
|
|
|
|
**The two axes read `[]` in OPPOSITE directions, and this is the trap.** Upstream documents each field separately, and they do not agree:
|
|
|
|
| field | `["did:ng:i"]` or `[""]` | `[]` or `undefined` |
|
|
|---|---|---|
|
|
| `graphs` | the whole dataset | **no objects at all** - the EMPTY set |
|
|
| `subjects` | n/a | **no filtering** - every subject |
|
|
|
|
So `{ graphs: [], subjects: [] }` returns nothing, and it is `graphs` alone that decides it. Reading the pair as symmetrical is how a read that should have been unscoped becomes a read that matches nothing, silently, since a shape query matching nothing is indistinguishable from a scope holding nothing. Two axes, no third.
|
|
|
|
## A `graphs` entry is one document's graph, never a set of them
|
|
|
|
At the binding, `orm_start_graph` (`sdk/js/lib-wasm/src/lib.rs`) passes each string through `NuriV0::new_from` (`engine/net/src/app_protocol.rs`). That function accepts exactly five forms and rejects everything else: `did:ng:i`; a repo id `did:ng:o:<repo>`; a file read-cap `did:ng:j:<id>:k:<key>`; a repo graph `did:ng:o:<repo>:v:<overlay>`; a branch `did:ng:o:<repo>:v:<overlay>:b:<branch>`. **No string parses to a store target.** And `nuri_to_string` (`engine/verifier/src/orm/graph/utils.rs`) then collapses every non-`UserSite` NURI to a single repo graph name, `did:ng:o:<repo>:v:<overlay>` — one document's graph, whatever went in.
|
|
|
|
## Naming a store's graph reaches its containment triples, not its documents
|
|
|
|
`Verifier::doc_create` (`engine/verifier/src/request_processor.rs`) writes `<store> <http://www.w3.org/ns/ldp#contains> <did:ng:o:doc>` into the **store's** graph, while the new document's own triples live in **its** repo graph. The object of that triple is the bare repo-id form, not even a graph name.
|
|
|
|
So the obvious formulation — *"search my private store"* — resolves to the store's graph, matches the containment triples, and returns **an empty set for the shape being asked about**. Silently: no error exists for it, because a shape query matching nothing is indistinguishable from a scope holding nothing.
|
|
|
|
## `did:ng:i` and `""` replace the whole list
|
|
|
|
In `orm_start_graph`, an entry equal to `""` or `did:ng:i` **discards every other entry** and substitutes `NuriV0::new_entire_user_site()`; `nuri_to_string` renders that back as the string `did:ng:i`. Downstream, `query_quads_for_shape` and `query_graph_subjects` (`engine/verifier/src/orm/graph/query.rs`) test `nuris[0] == "did:ng:i"` — positional, which holds because the binding has already collapsed the list to a singleton — and when it does, they **drop the graph filter entirely**: the emitted SELECT carries no `FILTER(?g …)` and runs over the whole local dataset.
|
|
|
|
Nothing is added to the query to narrow it by what the reader may open. `query_sparql_select` parses the SPARQL and runs it against the verifier's dataset as-is. The only bound is what the verifier has **materialised locally** — documents opened from a held `ReadCap`, `load_repo_from_read_cap` (`engine/verifier/src/verifier.rs`) — not a check performed when the query runs.
|
|
|
|
## The graph filter is literal
|
|
|
|
`schema_shape_to_sparql` (`engine/verifier/src/orm/graph/query.rs`) emits `FILTER(?g IN (<g1>, <g2>, …))` from the list verbatim. **No prefix, pattern or hierarchy matching**: a graph is in the list or it is not. `StorageReader::repos_in_store` (`engine/oxigraph/src/oxigraph/storage/mod.rs`) could enumerate a store's repos — and has **zero callers**.
|
|
|
|
## `entire_store` is the shape to align on, and nothing wires it
|
|
|
|
`NuriV0.entire_store` (`engine/net/src/app_protocol.rs`) is documented *"If it is a store, will include all the docs belonging to the store"*. Every constructor sets it `false`, **nothing anywhere sets it `true`**, and its only three readings are guards (`is_branch_identifier`, `is_valid_for_sparql_update`, `is_valid_for_discrete_update`) that require it false. No code expands it into a graph list.
|
|
|
|
That is `declared-not-wired` in the taxonomy of [[no-divergence-from-nextgraph]]: the target has written down the shape of the question even though nothing answers it yet — and a declared shape is the strongest thing to align on, far stronger than the absence of an implementation is evidence against it.
|
|
|
|
## `subjects` filters the root shape only
|
|
|
|
`query_quads_for_shape` seeds the caller's subject list as the pending set of the **root** shape; every nested shape gets its subjects from what the root query discovered, not from the caller. The emitted filter is `FILTER(?s IN (…))` over root-subject IRIs.
|
|
|
|
## The engine takes more than the surface hands it
|
|
|
|
`OrmConfig` (`engine/net/src/orm.rs`) carries `where_`, `order_by`, `select`, `page_size` and `max_active_pages`, and `schema_shape_to_sparql` accepts a where config, an order-by config and a `(limit, offset)` pair — it emits `ORDER BY`, `LIMIT` and `OFFSET` from them. But `Verifier::start_orm` (`engine/verifier/src/orm/graph/initialize.rs`) builds its config from `json!({})` behind a `// TODO`, so the config every query reads is always empty: **no caller can reach any of it**. The richness is real and unreachable.
|
|
|
|
## "Not ready" and "ready and empty" are distinguishable — one level down
|
|
|
|
`OrmSubscription` (`sdk/js/orm/src/connector/GraphOrmSubscription.ts`) is exported from the package entry — `core.ts`, which `@ng-org/orm` resolves `.` to — and carries a public `readyPromise` getter. In the installed alpha.21 both the symbol and its file are renamed, to `RdfOrmSubscription` and `connector/RdfOrmSubscription.ts`, and it gains an `isReady` getter that answers the same question without awaiting. So at that level the two states **are** distinguishable. The React `useShape` adapter drops the distinction: it returns a `DeepSignalSet` and documents it as *"the orm objects or an empty set, if still loading"*. The gap is between level 3's class and level 3's hook, not between us and the target.
|
|
|
|
## What this package does instead
|
|
|
|
`useShape` here is a passthrough (`packages/polyfill/src/surface/use-shape.ts`, annotated `kind=passthrough level=3`): **the scope goes to the injected SDK hook unchanged**, and the rights filter is applied to the *results* before they reach the caller. `makeReadFilteredView` (`packages/polyfill/src/emulated-verifier/read-filter.ts`) keeps only items whose document — their `@graph` — the current holder has a cap for, annotated `kind=aligned level=1 ref=engine/verifier/src/verifier.rs:load_repo_from_read_cap`.
|
|
|
|
That is the emulation of what the broker will bound **at delivery**, done where the target does it: per document, on what comes back. It disappears at migration, with the module — see [[knowledge_what-an-app-deletes-at-migration]].
|
|
|
|
## What follows
|
|
|
|
**Scoping a read by *store* is a question the SDK cannot express.** No string names a store as a read target; the store's own graph holds containment triples and nothing else; the one field that would mean it is declared and unwired. Nor is it a question an application needs to ask: an application names the documents it holds — it cannot discover, it can only follow a reference ([[knowledge_what-an-iri-says-upstream]] for what those references say). **Enumerating documents is only forced if you insist on asking by store.**
|
|
|
|
Where the target does answer, it answers on two axes — by document (`graphs`) and by subject (`subjects`) — and those are the two to build on.
|