dcde2859fe
Pulls @ng-eventually/polyfill's engagement, version-pinned, and declares back what this package actually consumes — the contract pair that lets the two repositories coordinate without either reading the other's source. The declaration is the channel: its Frictions and Needs are standing requests the provider drains. The one that decides this package's shape: modifying and removing a record have no published path that is both shape-typed and awaitable, so Refine's update and deleteOne have nowhere to land. Reading, creating and both live channels do have one. The engagement describes 1.0.0-dev.14 and no tag carries it, so the pin is the branch it is authored on and `against:` is the commit it resolved to.
75 lines
8.8 KiB
Markdown
75 lines
8.8 KiB
Markdown
---
|
|
type: usage
|
|
summary: Binds a Refine application's resources to NextGraph documents — one shape per resource, one document per entity — through this engagement's shape reads, its shape-typed insert and its inbox
|
|
against: 4c1801f6ee1dba0ce193e693960a6788686d602e
|
|
---
|
|
|
|
# usage_refine-providers-eventually — `@ng-eventually/refine-providers`
|
|
|
|
This package is a set of [Refine](https://refine.dev/core/) providers — `dataProvider`, `authProvider`, `liveProvider` — that an application declares once and then drives through Refine's own hooks (`useTable`, `useForm`, `useShow`). It is a library, not an application: it declares this engagement a **peer**, so the application remains the one place the package is named.
|
|
|
|
It carries no compensation for the current state of NextGraph. Capability isolation, identity, inboxes, sharing, broker state: all of that is this engagement's. What this package does is translate — a Refine resource is one SHEX shape, a Refine `id` is the NURI of one document, and one document holds one business entity.
|
|
|
|
The pinned version is the engagement's own `1.0.0-dev.14`, reached through the branch it is authored on rather than a tag — see `## Frictions`.
|
|
|
|
## Consumed surface
|
|
|
|
**Bootstrap** (the application's, before it renders; this package only reads what it settled) — `configure`, `init`, `ensureIdentity`.
|
|
|
|
**Reading.** `getObjects(shapeType, config)` is the whole of the read path, on two scopes and no other:
|
|
|
|
- `graphs: ""` for a resource listing. There is no call that lists the documents of a store, so the per-document capability filter IS the listing: what comes back is what this identity holds of that shape, which is exactly what a listing should contain — the documents it created and the documents whose key it was given, with no gesture of its own to tell them apart.
|
|
- `graphs: [<nuri>]` for one record, or several.
|
|
|
|
`where` and `orderBy` carry Refine's filters and sorters. `docs.sparqlQuery`, anchored, is called before a named read when the document may be a public one this identity has never read — see `## Frictions`.
|
|
|
|
**Writing.** `storeRegistry.createEntityDoc(store?)` with `privateStore()` / `protectedStore()` / `publicStore()` to place it, then `insertObject(shapeType, object)` with the object carrying its `@graph` and an empty `@id`. `docs.sparqlUpdate` for modification and removal, for want of anything else — see `## Needs`.
|
|
|
|
**Reacting.** `useShapeQuery(shapeType, scope)` for a resource channel, through `getSnapshot` / `subscribe` / `refetch` — it is an observable rather than a hook, which is what makes it reachable from a provider. `subscribeDocs` + `docChangeType` for a channel over named ids.
|
|
|
|
**Sharing** (outside Refine's CRUD, reached through the provider's `custom` or through hooks this package exports) — `inbox.share`, `inbox.postToDocument`, `inbox.readForDocument`, `inbox.readSyncedForDocument`, `storeRegistry.openDocumentInbox`.
|
|
|
|
**Types** — `Nuri`, `NuriLike`, `Scope`, `RdfOrmConfig`, `ShapeType`, `BaseType`, `ShapeQuery`, `ShapeObservable`, `PrincipalId`.
|
|
|
|
`useShape` is NOT consumed: it is a React hook, and a `DataProvider` is not a component.
|
|
|
|
## Constraints
|
|
|
|
**A read under `graphs: ""` must keep answering what this identity holds, and nothing else.** This is the one mechanism that makes a resource listing both correct and complete, since nothing lists a store. If it ever widened to what the wallet physically carries, every application built on this package would show every identity's records with nothing signalling it.
|
|
|
|
**`useShapeQuery` must stay an observable**, and its `data` must keep carrying the shape's own objects (`@id`, `@graph`, the shape's predicates). Both are load-bearing: the first is what a non-React provider can subscribe to, the second is what lets an application move between this package and `@ng-org/refine-providers` without rewriting a single page — the records must have the same shape on both sides.
|
|
|
|
**`getObjects`'s collection kind must stay predictable from the config** — an array with `orderBy`, a `Set` otherwise. Refine needs a stable order and a count.
|
|
|
|
**`insertObject` must keep returning the object with its `@id` filled in.** Refine's `create` resolves with the created record; there is nothing else to build it from.
|
|
|
|
**One document per entity must remain the recommended placement**, and access must stay per document. A Refine `id` is a document's NURI; if access were granted at any other granularity, `id` would stop being the unit that can be read, shared and listed.
|
|
|
|
Relayed to the application, which owns its URL: `?ng-id=` must survive its router. Refine's `syncWithLocation` rewrites the query string, and a dropped parameter is a silent mis-identification rather than an error.
|
|
|
|
## Frictions
|
|
|
|
**The version this engagement tells us to anchor on is not referenceable.** `## Change policy` names the regime — `polyfill/vX.Y.Z`, package-prefixed — and names `1.0.0-dev.14` as what exists today, to be pinned exactly and anchored here as `against: @ng-eventually/polyfill@1.0.0-dev.14`. The only tags that exist are `polyfill/v1.0.0-dev.2` and `polyfill/v1.0.0-dev.3`, so that string names nothing: the pin is the branch the engagement is authored on and `against:` is the commit it resolved to. Nothing about the resolution is irregular — a canonical identity mapped onto a local checkout is what a shared machine is for, and the stamp records the canonical value either way. What is missing is only the tag, and until one carries this version the policy's own instruction cannot be followed, nor can "between two pre-releases nothing is promised" be acted on: there is no pre-release to compare against.
|
|
|
|
**Modifying and removing a record have no published path that is both shape-typed and awaitable.** `## Non-guarantees` routes removal through the LIVE set a shape query handed back, and that set is reachable only through `useShape` — a React hook, which a `DataProvider` cannot call. Worse for this shape of caller: a mutation of a live set is batched into a call NextGraph does not await, so a refusal surfaces as an unhandled rejection in the console rather than at the line that made the change. Refine's `update` and `deleteOne` must resolve with the record or reject; neither is possible on that channel. What is left is projecting the shape into SPARQL and calling `docs.sparqlUpdate` — re-implementing in this package what NextGraph's ORM already does, which is exactly the kind of compensation this package exists not to carry.
|
|
|
|
**A named read needs a priming call that has no other purpose.** `getObjects` spends no round-trip asking a public store for its key, so a reference received from elsewhere reads empty until the document has been read by name through `docs.sparqlQuery`. Reading one record is therefore two calls, the first of which exists only to make the second work. `useShapeQuery` has not had this since `1.0.0-dev.9` — it asks once per document named in `graphs` — so the asymmetry between the two reads is what costs the call.
|
|
|
|
**Naming several documents fails as a whole.** One unreachable document in `graphs` means nothing is answered, so a `getMany` over a heterogeneous list — some created here, some received, some public — cannot return the records that ARE readable. A Refine table that resolves relations in one call cannot use it.
|
|
|
|
**A listing has no count and no reachable pagination.** `getObjects`'s config is `Omit<RdfOrmConfig, "pageSize" | "maxActivePages">`, so the only way to a total is to read everything this identity holds of the shape and measure it. Refine paginates from a total, so every page costs the whole collection. `useShape` carries `nextPage` / `previousPage`; the one-shot read does not.
|
|
|
|
**`ShapeObservable` and `refetch()` are both open migration points**, by this engagement's own account — the first on the deletion list, coupled to the shared wallet, the second with no recorded counterpart and the only published way out of a read stuck pending. The live provider is written on both.
|
|
|
|
## Needs
|
|
|
|
**A shape-typed write and a shape-typed removal, awaitable, callable outside React.** The analogue of `insertObject` for modifying and removing — same posture: the object carries its `@graph`, the call rejects when refused, and the caller awaits it. This is the one need that decides the shape of this package's `dataProvider`; everything else here is a cost, this is a wall.
|
|
|
|
**A count, or a reachable page, on the one-shot read** — so a listing does not have to rapatriate everything an identity holds to know how many there are.
|
|
|
|
**A key asked for the documents named in `getObjects`'s `graphs`**, as `useShapeQuery` has done since `1.0.0-dev.9`. That would retire the priming `docs.sparqlQuery` and make one record cost one call.
|
|
|
|
**Per-document outcomes when several are named**, rather than an all-or-nothing answer — so a listing of mixed provenance can show what is readable.
|
|
|
|
**A tag for the version `## Change policy` says to pin.**
|