@metreeca/keep 0.10.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,28 +1,26 @@
1
1
  import type { ResourceShape } from "@metreeca/blue/resource";
2
- import type { Lazy } from "@metreeca/core";
2
+ import type { Draft, Match, Model, Slice } from "@metreeca/blue/value";
3
+ import type { Lazy, Optional } from "@metreeca/core";
4
+ import type { Some } from "@metreeca/core/arrays";
5
+ import type { Awaitable } from "@metreeca/core/async";
3
6
  import type { Tag } from "@metreeca/core/language";
4
- import { type Reference, Resource } from "@metreeca/qest/resource";
5
- import { Instance, Template } from "@metreeca/qest/template";
7
+ import { type Reference } from "@metreeca/qest/state";
6
8
  /**
7
9
  * Model-driven resource store.
8
10
  *
9
- * Extends {@link StoreClient} with management facilities — mutation events, transactional execution, and lifecycle —
10
- * to form the store surface produced by factories like
11
- * {@link createManagingStore}. The data surface is factored into a standalone
12
- * {@link StoreClient} so it can be implemented and passed around on its own (for example, the inner client handed to
13
- * {@link Store.execute execute} carries no `execute` of its own), while consumers of a store reach both
14
- * surfaces through the same object.
11
+ * Extends {@link StoreClient} with management facilities: mutation events, transactional execution, and lifecycle.
12
+ * This is the store surface returned by connector factories and by wrappers like {@link createManagingStore}. The data
13
+ * surface stays a standalone {@link StoreClient}, so it can be implemented and passed around on its own: for example,
14
+ * the client handed to an {@link Store.execute execute} task carries no `execute` of its own.
15
15
  *
16
- * Data methods on a store — {@link StoreClient.lookup lookup}, {@link StoreClient.create create},
17
- * {@link StoreClient.update update}, {@link StoreClient.delete delete}, {@link StoreClient.insert insert}, and
18
- * {@link StoreClient.remove remove} — are individually atomic even when executed outside
19
- * {@link Store.execute execute}, regardless of the number of server round-trips they may internally require.
16
+ * Every data method on a store is individually atomic, even when called outside {@link Store.execute execute} and
17
+ * regardless of the number of server round-trips it may require.
20
18
  *
21
19
  * > [!IMPORTANT]
22
- * > Implementations provide best-effort transaction isolation, targeting snapshot isolation where the backend
23
- * > supports it and degrading gracefully to the maximum level achievable by the underlying storage, down to no
24
- * > isolation at all. Each implementation **must** document its supported isolation level. Implementations not
25
- * > supporting atomic updates natively **must** emulate them by buffering and deferring mutations until commit time.
20
+ * > Implementations provide best-effort transaction atomicity and isolation, targeting all-or-nothing commit and
21
+ * > snapshot isolation where the backend supports them and degrading gracefully to the maximum level achievable by
22
+ * > the underlying storage, down to eagerly applied mutations with no rollback and no isolation at all. Each
23
+ * > implementation **must** document its supported isolation level and whether a failing transaction rolls back.
26
24
  *
27
25
  * > [!IMPORTANT]
28
26
  * > Implementations provide best-effort mutation event signalling, targeting notification for all mutations on the
@@ -33,77 +31,83 @@ export interface Store extends StoreClient {
33
31
  /**
34
32
  * Observe mutation events.
35
33
  *
36
- * Each call mints an independent registration; multiple registrations of the same observer coexist and
37
- * fire independently — the observer is invoked once per matching registration per mutation batch. The
38
- * returned handle detaches **only** that registration; calling it more than once is a no-op and other
39
- * registrations of the same observer are unaffected. Detachment is the only supported way to stop
40
- * receiving events — repeated `observe` calls do not replace any prior registration.
34
+ * Each call creates an independent registration. Multiple registrations of the same observer coexist and fire
35
+ * independently: the observer is invoked once per matching registration for each mutation batch. The returned
36
+ * handle detaches **only** its own registration. Calling it more than once is a no-op, and other registrations of
37
+ * the same observer are unaffected. Detaching is the only way to stop receiving events: a repeated `observe` call
38
+ * never replaces an earlier registration.
41
39
  *
42
40
  * `resources` is the filter for the registration:
43
41
  *
44
42
  * - `undefined` (omitted) — no filter; the observer fires for every mutation
45
43
  * - a single {@link Reference} — fires for mutations to that resource and its
46
44
  * {@link @metreeca/core!isNestedIRI | descendants}
47
- * - an array of references — fires for mutations matching any element and its descendants
48
- * - an empty array — ignored; no registration is created and the returned handle is a no-op
45
+ * - a collection of references — an array, a set, an iterator, or any other iterable; fires for mutations
46
+ * matching any element and its descendants
47
+ * - an empty collection — ignored; no registration is created and the returned handle is a no-op
48
+ *
49
+ * The filter is read once, when the registration is created. Single-pass iterables are safe to pass, and later
50
+ * changes to the collection leave the registration untouched.
49
51
  *
50
52
  * > [!NOTE]
51
- * > The observer receives only resource identifiers and an existence flag — not the mutated state.
52
- * > This keeps event payloads small even when many resources are mutated within a single transaction,
53
- * > and lets each observer fetch whatever data envelope it needs via {@link StoreClient.lookup lookup}.
53
+ * > The observer receives only resource identifiers and an existence flag, not the mutated state. Event payloads
54
+ * > stay small even when a single transaction mutates many resources, and each observer can fetch the data it
55
+ * > needs with {@link StoreClient.lookup lookup}.
54
56
  *
55
57
  * @param observer - Mutation observer invoked with each batch of matching mutations
56
- * @param resources - Filter (single reference, array of references, or omitted for no filter)
58
+ * @param resources - Filter (a single reference, a collection of references, or omitted for no filter)
57
59
  *
58
60
  * @returns A function that detaches **this** registration
59
61
  *
60
- * @throws `Error` if the store has been {@link Store.close closed}
62
+ * @throws {@link !Error Error} if the store has been {@link Store.close closed}
61
63
  */
62
- observe(observer: StoreObserver, resources?: Reference | readonly Reference[]): () => void;
64
+ observe(observer: StoreObserver, resources?: Some<Reference>): () => void;
63
65
  /**
64
66
  * Execute a task within a store transaction.
65
67
  *
66
- * All operations performed during the task are executed atomically. If the task completes successfully, all
68
+ * All operations performed by the task form a single unit of work. If the task completes successfully, all
67
69
  * mutations are committed and {@link Store.observe registered observers} receive a single mutation event
68
- * containing all affected resources. If the task throws or rejects — including logic errors (`RangeError`,
69
- * {@link TraceError}) raised by inner {@link StoreClient} calls — no mutations are executed, no events are
70
- * notified, and the error is propagated to the caller as a promise rejection.
70
+ * listing all affected resources. If the task throws or rejects, no events are notified and, where the
71
+ * implementation provides atomicity, no mutations are committed. This includes logic errors
72
+ * ({@link !RangeError RangeError}, {@link TraceError}) raised by inner {@link StoreClient} calls. The error is
73
+ * propagated to the caller as a promise rejection.
71
74
  *
72
- * The task is handled its own {@link StoreClient} for the transaction. Operations performed through it belong to
73
- * the transaction and commit together, kept separate from any other `execute` running at the same time.
75
+ * The task receives its own {@link StoreClient} for the transaction. Operations performed through it belong to
76
+ * the transaction, separately from any other `execute` running at the same time.
74
77
  *
75
78
  * > [!WARNING]
76
- * > `execute` is not re-entrant: transaction boundaries are flat, so a task cannot open a nested transaction
77
- * > and compositions must share a single outer call site.
79
+ * > `execute` is not re-entrant. Transaction boundaries are flat: a task cannot open a nested transaction, and
80
+ * > composed operations must share a single outer call.
78
81
  *
79
82
  * > [!WARNING]
80
- * > The task MUST NOT retain or use the {@link StoreClient} it receives after `execute` settles: implementations
81
- * > may back it with transaction-scoped state (buffered mutations, a bound backend scope) that is flushed or
82
- * > discarded on completion, so any later call has undefined behaviour.
83
+ * > The task MUST NOT retain or use the {@link StoreClient} it receives after `execute` settles. Implementations
84
+ * > may back it with transaction-scoped state, such as buffered mutations or a bound backend scope, that is
85
+ * > flushed or discarded on completion, so any later call has undefined behaviour.
83
86
  *
84
87
  * > [!WARNING]
85
- * > **Atomicity is mandatory; isolation is best-effort.** The task's mutations and their events always commit
86
- * > all-or-nothing. `SNAPSHOT` is the suggested read-isolation level; each implementation declares the level it
87
- * > actually provides. An implementation with neither native transactions nor `SNAPSHOT` isolation MUST buffer
88
- * > the mutations and events, commit them as one batch on success, and drop the buffer on failure (a synchronous
89
- * > throw or a rejected promise).
88
+ * > **Atomicity and isolation are best-effort.** Implementations target all-or-nothing commit of the task's
89
+ * > mutations and `SNAPSHOT` read isolation; each implementation declares what it actually provides. An
90
+ * > implementation with no native transactions SHOULD emulate atomicity by buffering the mutations, committing
91
+ * > them as one batch on success and dropping the buffer on failure (a synchronous throw or a rejected promise).
92
+ * > Where the backend makes that infeasible, mutations apply eagerly, and a failing task may leave earlier
93
+ * > mutations in place, with no event notified for them.
90
94
  *
91
95
  * @typeParam V - Return type of the task
92
96
  *
93
97
  * @param task - Async or sync function performing store operations within the transaction
94
98
  *
95
99
  * @returns A promise resolving to the value returned by `task`; rejects with any error raised or
96
- * propagated by `task`, including a `RangeError`/{@link TraceError} from inner Store validation or a
100
+ * propagated by `task`, including a {@link !RangeError RangeError} or {@link TraceError} from inner validation or a
97
101
  * {@link Problem} from a transactional, network, storage, or other processing failure
98
102
  *
99
- * @throws `Error` if the store has been {@link Store.close closed}
103
+ * @throws {@link !Error Error} if the store has been {@link Store.close closed}
100
104
  */
101
- execute<V>(task: (store: StoreClient) => V | Promise<V>): Promise<V>;
105
+ execute<V>(task: (store: StoreClient) => Awaitable<V>): Promise<V>;
102
106
  /**
103
107
  * Release resources held by this store.
104
108
  *
105
109
  * Frees underlying resources such as database connections or file handles. Calling `close` on an
106
- * already-closed store has no effect; implementations with no resources to release MAY return a
110
+ * already-closed store has no effect. Implementations with no resources to release MAY return a
107
111
  * resolved no-op.
108
112
  *
109
113
  * @returns A promise resolving when all resources have been released; rejects with a {@link Problem} if a
@@ -114,94 +118,86 @@ export interface Store extends StoreClient {
114
118
  /**
115
119
  * Model-driven resource CRUD operations.
116
120
  *
117
- * Persists and retrieves linked data resources as shape-validated states and query projections.
121
+ * Persists and retrieves linked data resources as shape-validated states and query projections. This is the data
122
+ * surface a connector implements against its backend, and the client a {@link Store.execute execute} task works
123
+ * through.
118
124
  */
119
125
  export interface StoreClient {
120
126
  /**
121
- * Retrieve a resource.
127
+ * Look up a resource.
128
+ *
129
+ * Retrieves the resource identified by `entry`. The result holds only the members named by the `model`
130
+ * {@link Template}, and expands the linked resources the template reaches. Values are typed after the `shape`:
131
+ * the template states which members are wanted, not what they are.
122
132
  *
123
- * The result is shaped by the `model` {@link Template}: plain identifier properties are resolved from the
124
- * shape's {@link Instance}\<T\> type, while computed bindings are derived from the template value.
133
+ * Multi-valued members are retrieved as collections. Filtering, ordering and pagination constraints are stated
134
+ * under the member name, next to its retrieval keys. A {@link Projection} stated there in place of a template
135
+ * returns rows of computed values, keyed by the names of its bindings. A collection with no matching item is
136
+ * omitted from the result, like any other member without a value.
125
137
  *
126
138
  * > [!NOTE]
127
- * > `shape` and `model` are kept distinct so that a single `shape` can serve many retrieval templates —
128
- * > for example, a server wiring one `shape` at startup and accepting any admissible `model` decoded
129
- * > from the client request on each call. Callers wanting the shape's own model as template MUST pass
130
- * > it explicitly via {@link @metreeca/blue/value!model}.
139
+ * > `shape` and `model` are kept distinct so that a single `shape` can serve many retrieval templates. For
140
+ * > example, a server may wire one `shape` at startup and accept any admissible `model` decoded from each client
141
+ * > request. A template retrieving every member the shape declares MUST be written out explicitly.
131
142
  *
132
143
  * > [!CAUTION]
133
144
  * > By default, `model` templates support the full query language, including aggregate transforms and nested
134
- * > expansion. When exposing retrieval to untrusted clients, restrict query complexity as required by setting
135
- * > `plain` to `true`, `depth` to `0` or a positive value, and/or `limit` to a maximum result set size.
136
- *
137
- * @typeParam T - The retrieval template type
138
- *
139
- * @param request - Retrieval specifications
140
- * @param request.entry - Absolute identifier of the resource to be retrieved
141
- * @param request.shape - Resource shape driving the operation
142
- * @param request.model - Retrieval template defining the data envelope
143
- * @param opts - Optional retrieval options
144
- * @param opts.locale - {@link Tag} priority list driving language negotiation for localised content; entries are
145
- * matched in order of preference against the language tags available for each localised value. Implementations
146
- * default this to `["und"]` when omitted
147
- * @param opts.plain - When `true`, rejects `model` templates carrying aggregate transforms (`count`, `sum`, `min`,
148
- * `max`, `avg`); defaults to `false`, admitting the full query language
149
- * @param opts.depth - Maximum depth admitted for nested `model` expansion and query probe paths, each nesting
150
- * level or path segment counting against the budget; `0` rejects any nested template while still accepting IRI
151
- * references; omission leaves expansion unbounded
152
- * @param opts.limit - Maximum value admitted for the `#` pagination constraint in `model` selections; a positive
153
- * value caps the result set, rejecting any `#` exceeding it or set to `0` (unbounded) and injecting itself as a
154
- * default where `#` is absent; omission, like `0`, leaves result sets unbounded
155
- *
156
- * @returns A promise resolving to an immutable copy of the resource data matching the specified model,
157
- * or to `undefined` if the resource is not present in the store; rejects with a `RangeError` if `entry`
158
- * is not an absolute IRI, a {@link TraceError} if `model` doesn't {@link validate} against the shape,
145
+ * > expansion. When exposing retrieval to untrusted clients, restrict query complexity through the
146
+ * > {@link StoreScope | scope}: set `plain` to `true`, `depth` to `0` or a positive value, and/or `limit` to a
147
+ * > maximum result set size.
148
+ *
149
+ * @typeParam S - The shape driving the retrieval
150
+ * @typeParam T - The retrieval template, naming only members the shape carries
151
+ *
152
+ * @param request - The resource to retrieve, the shape describing it and the template narrowing it
153
+ * @param scope - The {@link StoreScope | retrieval scope}, bounding `model` and setting the locale priority
154
+ *
155
+ * @returns A promise resolving to an immutable copy of the resource data narrowed to the members `model` names,
156
+ * or to `undefined` if the resource is not present in the store; rejects with a {@link !RangeError RangeError} if
157
+ * `entry` is not an absolute IRI, a {@link TraceError} if `model` doesn't {@link validate} against the shape,
159
158
  * or a {@link Problem} on network, storage, or other processing errors
160
159
  *
161
- * @throws `Error` if the store has been {@link Store.close closed}
160
+ * @throws {@link !Error Error} if the store has been {@link Store.close closed}
162
161
  */
163
- lookup<T extends Template>(request: {
164
- readonly entry: Reference;
165
- readonly shape: Lazy<ResourceShape>;
166
- readonly model: T;
167
- }, opts?: {
168
- locale?: readonly Tag[];
169
- plain?: boolean;
170
- depth?: number;
171
- limit?: number;
172
- }): Promise<undefined | Instance<T>>;
162
+ lookup<S extends Lazy<ResourceShape>, T extends Model<S, T>>(request: StoreLookup<S, T>, scope?: StoreScope): Promise<Optional<Match<S, T>>>;
173
163
  /**
174
164
  * Create a resource.
175
165
  *
176
- * Stores the resource's own data if the resource doesn't already exist. Specific reference kinds are handled
177
- * as follows:
166
+ * Adds a new resource to a collection held by the resource identified by `entry`. The collecting property is the
167
+ * single multi-valued property named by `model`, and the new resource is stored as described by the shape that
168
+ * property ranges over. Nothing is created if a resource already exists under the identifier of the new one.
169
+ *
170
+ * The new resource is identified by the `id` of its `state`, which must be nested under `entry`. If `id` is left
171
+ * out, the store mints one under `entry`, following the identifier {@link ResourceShape.pattern | pattern} declared
172
+ * by the collected shape, if any. Each `{name}` slot is filled with the like-named member of `state`, which must
173
+ * be a single path segment. Any other slot is filled with an opaque segment. A remote store may leave the choice
174
+ * of identifier to the service holding the collection.
175
+ *
176
+ * Specific reference kinds are handled as follows:
178
177
  *
179
178
  * - {@link ResourceShape | embedded references} — cascades recursively with the same semantics
180
- * - {@link @metreeca/blue/reference!ReferenceConstraints.captive | captive references} — accepted only as bare
181
- * IRI references; inline captive batches are rejected, as `state` is always validated at depth `0`. Use
182
- * {@link StoreClient.insert insert} to embed a captive tree in a single batch
183
- * - {@link @metreeca/blue/reference!ReferenceConstraints.foreign | foreign references} — skipped, as their
179
+ * - {@link @metreeca/blue/resource!PropertyConstraints.captive | captive references} — accepted only as bare
180
+ * IRI references; inline captive batches are rejected, as `state` is always validated at depth `0`
181
+ * - {@link @metreeca/blue/resource!PropertyConstraints.foreign | foreign references} — skipped, as their
184
182
  * data is owned by the defining resource
185
183
  *
186
- * @param request - Creation specifications
187
- * @param request.entry - Absolute identifier of the target resource
188
- * @param request.shape - Resource shape driving the operation
189
- * @param request.state - Initial property values for the new resource
184
+ * @typeParam S - The shape driving the creation, describing the collecting resource
185
+ * @typeParam T - The collection slice, naming the collecting property
190
186
  *
191
- * @returns A promise resolving to the `entry` {@link Reference} of the created resource, or to `undefined`
192
- * if the resource already exists; rejects with a `RangeError` if `entry` is not an absolute IRI or if
193
- * `state` carries an `id` differing from `entry`, a {@link TraceError} if `state` doesn't {@link validate}
187
+ * @param request - The resource collecting the new one, the shape describing it, the slice naming the collecting
188
+ * property and the initial state of the new resource
189
+ *
190
+ * @returns A promise resolving to the {@link Reference} identifying the created resource, or to `undefined`
191
+ * if a resource already exists under that identifier; rejects with a {@link !RangeError RangeError} if `entry` is
192
+ * not an absolute IRI, if `state` carries an `id` not nested under `entry` or if a member filling an identifier
193
+ * slot is not a single path segment, a {@link TraceError} if `model` or `state` doesn't {@link validate}
194
194
  * against the shape, or a {@link Problem} on network, storage, or other processing errors
195
195
  *
196
- * @throws `Error` if the store has been {@link Store.close closed}
196
+ * @throws {@link !Error Error} if the store has been {@link Store.close closed}
197
197
  *
198
- * @see {@link StoreClient.insert insert} for unconditional insertion
198
+ * @see {@link StoreClient.insert insert} for unconditional insertion under a known identifier
199
199
  */
200
- create(request: {
201
- readonly entry: Reference;
202
- readonly shape: Lazy<ResourceShape>;
203
- readonly state: Resource;
204
- }): Promise<undefined | Reference>;
200
+ create<S extends Lazy<ResourceShape>, T extends Slice<S, T>>(request: StoreCreate<S, T>): Promise<Optional<Reference>>;
205
201
  /**
206
202
  * Update a resource.
207
203
  *
@@ -209,31 +205,25 @@ export interface StoreClient {
209
205
  * embedded data. Specific reference kinds are handled as follows:
210
206
  *
211
207
  * - {@link ResourceShape | embedded references} — cascades recursively with the same semantics
212
- * - {@link @metreeca/blue/reference!ReferenceConstraints.captive | captive references} — accepted only as bare
213
- * IRI references; inline captive batches are rejected, as `state` is always validated at depth `0`. Use
214
- * {@link StoreClient.insert insert} to embed a captive tree in a single batch
215
- * - {@link @metreeca/blue/reference!ReferenceConstraints.foreign | foreign references} — skipped, as their
208
+ * - {@link @metreeca/blue/resource!PropertyConstraints.captive | captive references} — accepted only as bare
209
+ * IRI references; inline captive batches are rejected, as `state` is always validated at depth `0`
210
+ * - {@link @metreeca/blue/resource!PropertyConstraints.foreign | foreign references} — skipped, as their
216
211
  * data is owned by the defining resource
217
212
  *
218
- * @param request - Update specifications
219
- * @param request.entry - Absolute identifier of the target resource
220
- * @param request.shape - Resource shape driving the operation
221
- * @param request.state - Complete replacement state for the resource
213
+ * @typeParam S - The shape driving the update, describing the state
214
+ *
215
+ * @param request - The resource to update, the shape validating it and its replacement state
222
216
  *
223
217
  * @returns A promise resolving to the `entry` {@link Reference} of the updated resource, or to `undefined`
224
- * if the resource doesn't exist; rejects with a `RangeError` if `entry` is not an absolute IRI or if
225
- * `state` carries an `id` differing from `entry`, a {@link TraceError} if `state` doesn't {@link validate}
218
+ * if the resource doesn't exist; rejects with a {@link !RangeError RangeError} if `entry` is not an absolute IRI
219
+ * or if `state` carries an `id` differing from `entry`, a {@link TraceError} if `state` doesn't {@link validate}
226
220
  * against the shape, or a {@link Problem} on network, storage, or other processing errors
227
221
  *
228
- * @throws `Error` if the store has been {@link Store.close closed}
222
+ * @throws {@link !Error Error} if the store has been {@link Store.close closed}
229
223
  *
230
224
  * @see {@link StoreClient.insert insert} for unconditional insertion
231
225
  */
232
- update(request: {
233
- readonly entry: Reference;
234
- readonly shape: Lazy<ResourceShape>;
235
- readonly state: Resource;
236
- }): Promise<undefined | Reference>;
226
+ update<S extends Lazy<ResourceShape>>(request: StoreUpdate<S>): Promise<Optional<Reference>>;
237
227
  /**
238
228
  * Delete a resource.
239
229
  *
@@ -241,25 +231,22 @@ export interface StoreClient {
241
231
  * Specific reference kinds are handled as follows:
242
232
  *
243
233
  * - {@link ResourceShape | embedded references} — cascades recursively with the same semantics
244
- * - {@link @metreeca/blue/reference!ReferenceConstraints.captive | captive references} — cascade-deleted with
234
+ * - {@link @metreeca/blue/resource!PropertyConstraints.captive | captive references} — cascade-deleted with
245
235
  * the same semantics
246
236
  *
247
- * @param request - Deletion specifications
248
- * @param request.entry - Absolute identifier of the target resource
249
- * @param request.shape - Resource shape driving the operation
237
+ * @typeParam S - The shape driving the deletion, identifying the data cascaded with the resource
238
+ *
239
+ * @param request - The resource to delete and the shape identifying the data cascade-deleted with it
250
240
  *
251
241
  * @returns A promise resolving to the `entry` {@link Reference} of the deleted resource, or to `undefined`
252
- * if the resource doesn't exist; rejects with a `RangeError` if `entry` is not an absolute IRI, or a
253
- * {@link Problem} on network, storage, or other processing errors
242
+ * if the resource doesn't exist; rejects with a {@link !RangeError RangeError} if `entry` is not an absolute IRI,
243
+ * or a {@link Problem} on network, storage, or other processing errors
254
244
  *
255
- * @throws `Error` if the store has been {@link Store.close closed}
245
+ * @throws {@link !Error Error} if the store has been {@link Store.close closed}
256
246
  *
257
247
  * @see {@link StoreClient.remove remove} for unconditional removal
258
248
  */
259
- delete(request: {
260
- readonly entry: Reference;
261
- readonly shape: Lazy<ResourceShape>;
262
- }): Promise<undefined | Reference>;
249
+ delete<S extends Lazy<ResourceShape>>(request: StoreDelete<S>): Promise<Optional<Reference>>;
263
250
  /**
264
251
  * Insert a resource.
265
252
  *
@@ -267,42 +254,26 @@ export interface StoreClient {
267
254
  * data. Specific reference kinds are handled as follows:
268
255
  *
269
256
  * - {@link ResourceShape | embedded references} — cascades recursively with the same semantics
270
- * - {@link @metreeca/blue/reference!ReferenceConstraints.captive | captive references} — accepted as bare IRI
271
- * references or, up to `opts.depth` nesting levels, as inline batches creating or updating the captive tree
272
- * - {@link @metreeca/blue/reference!ReferenceConstraints.foreign | foreign references} — skipped, as their
257
+ * - {@link @metreeca/blue/resource!PropertyConstraints.captive | captive references} — accepted only as bare
258
+ * IRI references; inline captive batches are rejected, as `state` is always validated at depth `0`
259
+ * - {@link @metreeca/blue/resource!PropertyConstraints.foreign | foreign references} — skipped, as their
273
260
  * data is owned by the defining resource
274
261
  *
275
- * > [!CAUTION]
276
- * > By default, resources accept captive reference expansion to unbounded depth. To enforce a strict insertion
277
- * > process that admits only bare references, set `opts.depth` to `0` to reject all expansion; set it to a positive
278
- * > value to cap the nesting depth admitted.
279
- *
280
- * @param request - Insertion specifications
281
- * @param request.entry - Absolute identifier of the target resource
282
- * @param request.shape - Resource shape driving the operation
283
- * @param request.state - Complete resource state to be inserted
284
- * @param opts - Optional insertion options
285
- * @param opts.depth - Maximum nesting depth for expanding `captive` reference values as inline target resource
286
- * states, each expansion level counting against the budget; `0` rejects all expansion, accepting bare IRI
287
- * references only; omission leaves expansion unbounded
262
+ * @typeParam S - The shape driving the insertion, describing the state
263
+ *
264
+ * @param request - The resource to insert, the shape validating it and its state
288
265
  *
289
266
  * @returns A promise resolving to the `entry` {@link Reference} of the inserted resource; rejects with a
290
- * `RangeError` if `entry` is not an absolute IRI or if `state` carries an `id` differing from `entry`, a
291
- * {@link TraceError} if `state` doesn't {@link validate} against the shape, or a {@link Problem} on
267
+ * {@link !RangeError RangeError} if `entry` is not an absolute IRI or if `state` carries an `id` differing from
268
+ * `entry`, a {@link TraceError} if `state` doesn't {@link validate} against the shape, or a {@link Problem} on
292
269
  * network, storage, or other processing errors
293
270
  *
294
- * @throws `Error` if the store has been {@link Store.close closed}
271
+ * @throws {@link !Error Error} if the store has been {@link Store.close closed}
295
272
  *
296
273
  * @see {@link StoreClient.create create} for conditional creation
297
274
  * @see {@link StoreClient.update update} for conditional replacement
298
275
  */
299
- insert(request: {
300
- readonly entry: Reference;
301
- readonly shape: Lazy<ResourceShape>;
302
- readonly state: Resource;
303
- }, opts?: {
304
- readonly depth?: number;
305
- }): Promise<Reference>;
276
+ insert<S extends Lazy<ResourceShape>>(request: StoreUpdate<S>): Promise<Reference>;
306
277
  /**
307
278
  * Remove a resource.
308
279
  *
@@ -310,26 +281,65 @@ export interface StoreClient {
310
281
  * reference kinds are handled as follows:
311
282
  *
312
283
  * - {@link ResourceShape | embedded references} — cascades recursively with the same semantics
313
- * - {@link @metreeca/blue/reference!ReferenceConstraints.captive | captive references} — cascade-removed with
284
+ * - {@link @metreeca/blue/resource!PropertyConstraints.captive | captive references} — cascade-removed with
314
285
  * the same semantics
315
286
  *
316
- * @param request - Removal specifications
317
- * @param request.entry - Absolute identifier of the target resource
318
- * @param request.shape - Resource shape driving the operation
287
+ * @typeParam S - The shape driving the removal, identifying the data cascaded with the resource
288
+ *
289
+ * @param request - The resource to remove and the shape identifying the data cascade-removed with it
319
290
  *
320
291
  * @returns A promise resolving to the `entry` {@link Reference} of the removed resource; rejects with a
321
- * `RangeError` if `entry` is not an absolute IRI, or a {@link Problem} on network, storage, or other
322
- * processing errors
292
+ * {@link !RangeError RangeError} if `entry` is not an absolute IRI, or a {@link Problem} on network, storage, or
293
+ * other processing errors
323
294
  *
324
- * @throws `Error` if the store has been {@link Store.close closed}
295
+ * @throws {@link !Error Error} if the store has been {@link Store.close closed}
325
296
  *
326
297
  * @see {@link StoreClient.delete delete} for conditional removal
327
298
  */
328
- remove(request: {
329
- readonly entry: Reference;
330
- readonly shape: Lazy<ResourceShape>;
331
- }): Promise<Reference>;
299
+ remove<S extends Lazy<ResourceShape>>(request: StoreDelete<S>): Promise<Reference>;
332
300
  }
301
+ /**
302
+ * Store retrieval scope.
303
+ *
304
+ * Bounds the aggregates, nesting depth and page size a {@link StoreClient.lookup lookup} admits in its `model`, and
305
+ * sets the locale priority for its localised content. Every member is optional. An empty scope admits any `model`
306
+ * the shape validates, and resolves localised content against the undetermined `und` tag alone.
307
+ */
308
+ export type StoreScope = {
309
+ /**
310
+ * {@link Tag} priority list driving language negotiation for localised content.
311
+ *
312
+ * Entries are matched in order of preference against the language tags available for each localised value.
313
+ *
314
+ * @defaultValue `["und"]`
315
+ */
316
+ locale?: readonly Tag[];
317
+ /**
318
+ * Whether to reject `model` templates carrying aggregate transforms (`count`, `sum`, `min`, `max`, `avg`),
319
+ * admitting retrieval but not computation.
320
+ *
321
+ * @defaultValue `false`, admitting the full query language
322
+ */
323
+ plain?: boolean;
324
+ /**
325
+ * Maximum nesting admitted for `model` expansion and query probe paths.
326
+ *
327
+ * Each nested resource or path segment counts against the budget. `0` rejects any nested template, but still
328
+ * admits IRI references.
329
+ *
330
+ * @defaultValue Unbounded
331
+ */
332
+ depth?: number;
333
+ /**
334
+ * Maximum page size admitted for the `#` pagination constraint on `model` collections.
335
+ *
336
+ * A positive value caps the result set. A `#` exceeding it, or set to `0` (unbounded), is rejected, and a
337
+ * collection with no `#` is held to it. `0` leaves result sets unbounded.
338
+ *
339
+ * @defaultValue `0`
340
+ */
341
+ limit?: number;
342
+ };
333
343
  /**
334
344
  * Store mutation observer.
335
345
  *
@@ -353,6 +363,102 @@ export interface StoreObserver {
353
363
  */
354
364
  (mutations: {
355
365
  readonly [entry: Reference]: boolean;
356
- }): void | Promise<void>;
366
+ }): Awaitable<void>;
357
367
  }
368
+ /**
369
+ * Store lookup request.
370
+ *
371
+ * Names the resource to retrieve with {@link StoreClient.lookup lookup}, the shape describing it and the template
372
+ * narrowing the result.
373
+ *
374
+ * @typeParam S - The shape driving the retrieval
375
+ * @typeParam T - The retrieval template, naming only members the shape carries
376
+ */
377
+ export type StoreLookup<S extends Lazy<ResourceShape>, T extends Model<S, T>> = {
378
+ /**
379
+ * Absolute identifier of the resource to be retrieved.
380
+ */
381
+ readonly entry: Reference;
382
+ /**
383
+ * Resource shape the retrieval is validated against, possibly deferred to break definition cycles.
384
+ */
385
+ readonly shape: S;
386
+ /**
387
+ * Retrieval template defining the data envelope of the result.
388
+ *
389
+ * The template names only members carried by `shape`, each in a form its range admits. Multi-valued members may
390
+ * also state filtering, ordering and pagination constraints, or a projection in place of a template.
391
+ */
392
+ readonly model: T;
393
+ };
394
+ /**
395
+ * Store creation request.
396
+ *
397
+ * Names the resource collecting a new one with {@link StoreClient.create create}, the property holding the
398
+ * collection and the initial state of the new resource.
399
+ *
400
+ * @typeParam S - The shape driving the creation, describing the collecting resource
401
+ * @typeParam T - The collection slice, naming the collecting property
402
+ */
403
+ export type StoreCreate<S extends Lazy<ResourceShape>, T extends Slice<S, T>> = {
404
+ /**
405
+ * Absolute identifier of the resource collecting the new one.
406
+ */
407
+ readonly entry: Reference;
408
+ /**
409
+ * Resource shape the creation is validated against, describing the collecting resource, possibly deferred to
410
+ * break definition cycles.
411
+ */
412
+ readonly shape: S;
413
+ /**
414
+ * Collection slice naming the multi-valued property of `shape` that collects the new resource.
415
+ */
416
+ readonly model: T;
417
+ /**
418
+ * Initial state of the new resource; an explicit `id` must be nested under `entry`.
419
+ */
420
+ readonly state: Draft<S, T>;
421
+ };
422
+ /**
423
+ * Store update request.
424
+ *
425
+ * Names the resource to store with {@link StoreClient.update update} or {@link StoreClient.insert insert}, the shape
426
+ * describing it and the complete state replacing its current data.
427
+ *
428
+ * @typeParam S - The shape driving the persistence, describing the state
429
+ */
430
+ export type StoreUpdate<S extends Lazy<ResourceShape>> = {
431
+ /**
432
+ * Absolute identifier of the resource to be persisted.
433
+ */
434
+ readonly entry: Reference;
435
+ /**
436
+ * Resource shape `state` is validated against, possibly deferred to break definition cycles.
437
+ */
438
+ readonly shape: S;
439
+ /**
440
+ * Complete state for the resource, as `shape` describes it, replacing any existing data; its `id`, if stated,
441
+ * must match `entry`.
442
+ */
443
+ readonly state: Draft<S>;
444
+ };
445
+ /**
446
+ * Store deletion request.
447
+ *
448
+ * Names the resource to discard with {@link StoreClient.delete delete} or {@link StoreClient.remove remove} and the
449
+ * shape identifying the data discarded with it.
450
+ *
451
+ * @typeParam S - The shape driving the deletion, identifying the data cascaded with the resource
452
+ */
453
+ export type StoreDelete<S extends Lazy<ResourceShape>> = {
454
+ /**
455
+ * Absolute identifier of the resource to be discarded.
456
+ */
457
+ readonly entry: Reference;
458
+ /**
459
+ * Resource shape identifying the embedded and captive data discarded with the resource, possibly deferred to
460
+ * break definition cycles.
461
+ */
462
+ readonly shape: S;
463
+ };
358
464
  //# sourceMappingURL=index.d.ts.map