47 lines
5.7 KiB
Markdown
47 lines
5.7 KiB
Markdown
---
|
|
type: bug
|
|
summary: A live read through the published `useShape` never starts against the deployed broker — its `orm_start_graph` expects a `config` argument the published `@ng-org/orm` does not send, so the subscription stays empty and pending for ever with one console error as the only trace
|
|
severity: major
|
|
opened: 2026-08-17
|
|
last_checked: 2026-09-16
|
|
---
|
|
|
|
# `useShape` opens no live read against the deployed broker
|
|
|
|
The published `useShape` is a passthrough to `@ng-org/orm`'s hook (`packages/polyfill/src/surface/use-shape.ts`) — this package adds the read filter and forwards. Against the broker deployed today, the subscription that hook opens **never starts**: its set stays empty, its readiness never settles, no engine update ever arrives, and the only trace is one `console.error`. An application sees "this scope holds nothing", indefinitely, which is the exact confusion the rest of the read surface is built to prevent.
|
|
|
|
The cause is upstream, in the disagreement between the deployed broker and the published client packages. **Nothing in this package can work around it.**
|
|
|
|
## The mechanism — an ARGUMENT, not a name
|
|
|
|
MEASURED 2026-09-16 against the real broker by `packages/polyfill/e2e/orm-live-read.ts`, which drives the published `useShape` over the real `@ng-org/orm`. The engine answers:
|
|
|
|
```
|
|
Deserialization error of config Error: invalid type: JsValue(Function), expected any valid JSON value
|
|
```
|
|
|
|
A live ORM read is one call and it is streamed: `OrmSubscription`'s constructor issues `ng.orm_start_graph(graphs, subjects, shapeType, session_id, onBackendMessage)` — **five** arguments, callback last — and the engine pushes into that callback rather than answering once. What comes back instead names a parameter, `config`, that the client never sent.
|
|
|
|
The same measurement establishes where that parameter sits, by calling the SDK member directly with `{}` in position 4: **six arguments are ACCEPTED**, and the engine then streams back. So the deployed signature is `(graph_scope, subject_scope, shapeType, session_id, config, callback)`, and the published client's five-argument call lands its **callback** exactly where `config` is expected — which is precisely what the error describes.
|
|
|
|
**The direction is the opposite of what this leaf said until 2026-09-16.** The earlier reading — a broker built before `orm_start` was renamed `orm_start_graph` (upstream `d5ecd0fd`, 2026-01-16) — is **refuted**: the name resolves, and the call fails *inside* the function, on an argument. That arity exists in **no** commit of `nextgraph-rs` at `213338f6`: the one window that carried a `config` at all (`c38aa371` 2026-04-12 → `7c8abd0a` 2026-04-21, *"rm unused config param"*) put it **last**, after the callback, where a five-argument call would leave it merely absent. The deployment is therefore built from a source **ahead of** that checkout, and it is the published `@ng-org/orm@0.1.2-alpha.19` / `@ng-org/web@0.1.2-alpha.13` this package pins that are behind it.
|
|
|
|
**Why it fails in silence:** `orm_start_graph` is awaited inside a `try` whose `catch` is `console.error(e)`, and `resolveReady()` is called on one path only — the arrival of initial data (`GraphOrmSubscription.ts:handleInitialResponse`). A rejected start leaves the readiness promise pending for ever and the set permanently empty. This is the same upstream property already recorded in `docs/api-contract.md` (a failed read *is* an eternal pending upstream), reached here through a different door.
|
|
|
|
## What is affected, and what is not
|
|
|
|
- **Affected:** the published `useShape`, and only it. By the same argument, `insertObject` and `getObjects` — both built on `OrmSubscription` — cannot start either, so **no ORM write or deletion can be exercised against this deployment**.
|
|
- **Not affected:** `watchShape`, `subscribeDoc`, `subscribeDocs`, `readUnion`. These open one `doc_subscribe` per document, and `doc_subscribe: 2` has been in the method tables continuously under a stable name and arity. `watchShape` is built on that path, not on the ORM subscription — it is a working reactive read today.
|
|
|
|
That is why the library's own suites and the reference application do not show it: nothing in this package calls `useShape` internally.
|
|
|
|
## What to do
|
|
|
|
- **Do not build on live `useShape`.** Use `watchShape` for a reactive read, `readUnion` for a one-shot one. This is stated in the contract's `## Non-guarantees` so a consumer meets it without reading this leaf.
|
|
- **The fix is not ours, and it is an ALIGNMENT, not an upgrade in one direction.** Either the deployment rolls to a build whose `orm_start_graph` matches the published packages, or `@ng-org/orm` / `@ng-org/web` are republished against the deployed signature. Report it; do not patch around it, and do not pass a `config` argument from here — a client that guesses an unpublished arity breaks the day the two ends agree.
|
|
- **To check whether it is closed:** `bun run e2e/orm-live-read.ts` from `packages/polyfill` (it needs `@ng-org/orm`, an optional peer `bun install` does not fetch). It exits non-zero while this is open and names what the engine expected. Green means the two ends agree and this leaf can go.
|
|
|
|
## The shape worth remembering
|
|
|
|
A method **signature** — name and arity together — duplicated across three independently-deployed artifacts, with a **silent** failure when they disagree, is a defect no single repository's tests can catch. Pinning the client packages does not help: the broker-side half (`@ng-org/api-web`, `private: true`, shipped only inside the broker build) is pinnable by nobody, so the two ends drift in either direction and the symptom is identical both ways. Anything this package routes through a streamed method inherits the same exposure — which is the reason `subscribeDocs` composing per-document `doc_subscribe` is worth more than it looks.
|