@metreeca/keep 0.9.0 → 0.11.0
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/README.md +228 -24
- package/dist/clients/batching.core.d.ts +49 -0
- package/dist/clients/batching.core.d.ts.map +1 -0
- package/dist/clients/batching.core.js +159 -0
- package/dist/clients/batching.core.js.map +1 -0
- package/dist/clients/batching.d.ts +218 -0
- package/dist/clients/batching.d.ts.map +1 -0
- package/dist/clients/batching.js +109 -0
- package/dist/clients/batching.js.map +1 -0
- package/dist/clients/validating.d.ts +41 -0
- package/dist/clients/validating.d.ts.map +1 -0
- package/dist/clients/validating.js +150 -0
- package/dist/clients/validating.js.map +1 -0
- package/dist/index.d.ts +464 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/{states/index.js → index.js} +1 -1
- package/dist/index.js.map +1 -0
- package/dist/stores/caching.d.ts +93 -0
- package/dist/stores/caching.d.ts.map +1 -0
- package/dist/stores/caching.js +320 -0
- package/dist/stores/caching.js.map +1 -0
- package/dist/stores/managing.d.ts +57 -0
- package/dist/stores/managing.d.ts.map +1 -0
- package/dist/stores/managing.js +147 -0
- package/dist/stores/managing.js.map +1 -0
- package/package.json +34 -18
- package/dist/index/index.d.ts +0 -2
- package/dist/index/index.d.ts.map +0 -1
- package/dist/index/index.js +0 -17
- package/dist/index/index.js.map +0 -1
- package/dist/states/index.d.ts +0 -2
- package/dist/states/index.d.ts.map +0 -1
- package/dist/states/index.js.map +0 -1
- package/dist/stores/index.d.ts +0 -2
- package/dist/stores/index.d.ts.map +0 -1
- package/dist/stores/index.js +0 -17
- package/dist/stores/index.js.map +0 -1
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
import type { ResourceShape } from "@metreeca/blue/resource";
|
|
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";
|
|
6
|
+
import type { Tag } from "@metreeca/core/language";
|
|
7
|
+
import { type Reference } from "@metreeca/qest/state";
|
|
8
|
+
/**
|
|
9
|
+
* Model-driven resource store.
|
|
10
|
+
*
|
|
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
|
+
*
|
|
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.
|
|
18
|
+
*
|
|
19
|
+
* > [!IMPORTANT]
|
|
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.
|
|
24
|
+
*
|
|
25
|
+
* > [!IMPORTANT]
|
|
26
|
+
* > Implementations provide best-effort mutation event signalling, targeting notification for all mutations on the
|
|
27
|
+
* > backing storage layer and degrading gracefully to signalling only events generated by calls to the mutation
|
|
28
|
+
* > methods on the store. Each implementation **must** document its supported event scope.
|
|
29
|
+
*/
|
|
30
|
+
export interface Store extends StoreClient {
|
|
31
|
+
/**
|
|
32
|
+
* Observe mutation events.
|
|
33
|
+
*
|
|
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.
|
|
39
|
+
*
|
|
40
|
+
* `resources` is the filter for the registration:
|
|
41
|
+
*
|
|
42
|
+
* - `undefined` (omitted) — no filter; the observer fires for every mutation
|
|
43
|
+
* - a single {@link Reference} — fires for mutations to that resource and its
|
|
44
|
+
* {@link @metreeca/core!isNestedIRI | descendants}
|
|
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.
|
|
51
|
+
*
|
|
52
|
+
* > [!NOTE]
|
|
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}.
|
|
56
|
+
*
|
|
57
|
+
* @param observer - Mutation observer invoked with each batch of matching mutations
|
|
58
|
+
* @param resources - Filter (a single reference, a collection of references, or omitted for no filter)
|
|
59
|
+
*
|
|
60
|
+
* @returns A function that detaches **this** registration
|
|
61
|
+
*
|
|
62
|
+
* @throws {@link !Error Error} if the store has been {@link Store.close closed}
|
|
63
|
+
*/
|
|
64
|
+
observe(observer: StoreObserver, resources?: Some<Reference>): () => void;
|
|
65
|
+
/**
|
|
66
|
+
* Execute a task within a store transaction.
|
|
67
|
+
*
|
|
68
|
+
* All operations performed by the task form a single unit of work. If the task completes successfully, all
|
|
69
|
+
* mutations are committed and {@link Store.observe registered observers} receive a single mutation event
|
|
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.
|
|
74
|
+
*
|
|
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.
|
|
77
|
+
*
|
|
78
|
+
* > [!WARNING]
|
|
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.
|
|
81
|
+
*
|
|
82
|
+
* > [!WARNING]
|
|
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.
|
|
86
|
+
*
|
|
87
|
+
* > [!WARNING]
|
|
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.
|
|
94
|
+
*
|
|
95
|
+
* @typeParam V - Return type of the task
|
|
96
|
+
*
|
|
97
|
+
* @param task - Async or sync function performing store operations within the transaction
|
|
98
|
+
*
|
|
99
|
+
* @returns A promise resolving to the value returned by `task`; rejects with any error raised or
|
|
100
|
+
* propagated by `task`, including a {@link !RangeError RangeError} or {@link TraceError} from inner validation or a
|
|
101
|
+
* {@link Problem} from a transactional, network, storage, or other processing failure
|
|
102
|
+
*
|
|
103
|
+
* @throws {@link !Error Error} if the store has been {@link Store.close closed}
|
|
104
|
+
*/
|
|
105
|
+
execute<V>(task: (store: StoreClient) => Awaitable<V>): Promise<V>;
|
|
106
|
+
/**
|
|
107
|
+
* Release resources held by this store.
|
|
108
|
+
*
|
|
109
|
+
* Frees underlying resources such as database connections or file handles. Calling `close` on an
|
|
110
|
+
* already-closed store has no effect. Implementations with no resources to release MAY return a
|
|
111
|
+
* resolved no-op.
|
|
112
|
+
*
|
|
113
|
+
* @returns A promise resolving when all resources have been released; rejects with a {@link Problem} if a
|
|
114
|
+
* clean-up error occurs
|
|
115
|
+
*/
|
|
116
|
+
close(): Promise<void>;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Model-driven resource CRUD operations.
|
|
120
|
+
*
|
|
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.
|
|
124
|
+
*/
|
|
125
|
+
export interface StoreClient {
|
|
126
|
+
/**
|
|
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.
|
|
132
|
+
*
|
|
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.
|
|
137
|
+
*
|
|
138
|
+
* > [!NOTE]
|
|
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.
|
|
142
|
+
*
|
|
143
|
+
* > [!CAUTION]
|
|
144
|
+
* > By default, `model` templates support the full query language, including aggregate transforms and nested
|
|
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,
|
|
158
|
+
* or a {@link Problem} on network, storage, or other processing errors
|
|
159
|
+
*
|
|
160
|
+
* @throws {@link !Error Error} if the store has been {@link Store.close closed}
|
|
161
|
+
*/
|
|
162
|
+
lookup<S extends Lazy<ResourceShape>, T extends Model<S, T>>(request: StoreLookup<S, T>, scope?: StoreScope): Promise<Optional<Match<S, T>>>;
|
|
163
|
+
/**
|
|
164
|
+
* Create a resource.
|
|
165
|
+
*
|
|
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:
|
|
177
|
+
*
|
|
178
|
+
* - {@link ResourceShape | embedded references} — cascades recursively with the same semantics
|
|
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
|
|
182
|
+
* data is owned by the defining resource
|
|
183
|
+
*
|
|
184
|
+
* @typeParam S - The shape driving the creation, describing the collecting resource
|
|
185
|
+
* @typeParam T - The collection slice, naming the collecting property
|
|
186
|
+
*
|
|
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
|
+
* against the shape, or a {@link Problem} on network, storage, or other processing errors
|
|
195
|
+
*
|
|
196
|
+
* @throws {@link !Error Error} if the store has been {@link Store.close closed}
|
|
197
|
+
*
|
|
198
|
+
* @see {@link StoreClient.insert insert} for unconditional insertion under a known identifier
|
|
199
|
+
*/
|
|
200
|
+
create<S extends Lazy<ResourceShape>, T extends Slice<S, T>>(request: StoreCreate<S, T>): Promise<Optional<Reference>>;
|
|
201
|
+
/**
|
|
202
|
+
* Update a resource.
|
|
203
|
+
*
|
|
204
|
+
* Replaces the resource's own data if the resource already exists, fully removing any previously existing
|
|
205
|
+
* embedded data. Specific reference kinds are handled as follows:
|
|
206
|
+
*
|
|
207
|
+
* - {@link ResourceShape | embedded references} — cascades recursively with the same semantics
|
|
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
|
|
211
|
+
* data is owned by the defining resource
|
|
212
|
+
*
|
|
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
|
|
216
|
+
*
|
|
217
|
+
* @returns A promise resolving to the `entry` {@link Reference} of the updated resource, or to `undefined`
|
|
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}
|
|
220
|
+
* against the shape, or a {@link Problem} on network, storage, or other processing errors
|
|
221
|
+
*
|
|
222
|
+
* @throws {@link !Error Error} if the store has been {@link Store.close closed}
|
|
223
|
+
*
|
|
224
|
+
* @see {@link StoreClient.insert insert} for unconditional insertion
|
|
225
|
+
*/
|
|
226
|
+
update<S extends Lazy<ResourceShape>>(request: StoreUpdate<S>): Promise<Optional<Reference>>;
|
|
227
|
+
/**
|
|
228
|
+
* Delete a resource.
|
|
229
|
+
*
|
|
230
|
+
* Removes the resource's own data and clears references to it from other resources, if the resource exists.
|
|
231
|
+
* Specific reference kinds are handled as follows:
|
|
232
|
+
*
|
|
233
|
+
* - {@link ResourceShape | embedded references} — cascades recursively with the same semantics
|
|
234
|
+
* - {@link @metreeca/blue/resource!PropertyConstraints.captive | captive references} — cascade-deleted with
|
|
235
|
+
* the same semantics
|
|
236
|
+
*
|
|
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
|
|
240
|
+
*
|
|
241
|
+
* @returns A promise resolving to the `entry` {@link Reference} of the deleted resource, or to `undefined`
|
|
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
|
|
244
|
+
*
|
|
245
|
+
* @throws {@link !Error Error} if the store has been {@link Store.close closed}
|
|
246
|
+
*
|
|
247
|
+
* @see {@link StoreClient.remove remove} for unconditional removal
|
|
248
|
+
*/
|
|
249
|
+
delete<S extends Lazy<ResourceShape>>(request: StoreDelete<S>): Promise<Optional<Reference>>;
|
|
250
|
+
/**
|
|
251
|
+
* Insert a resource.
|
|
252
|
+
*
|
|
253
|
+
* Unconditionally inserts or replaces the resource's own data, fully removing any previously existing embedded
|
|
254
|
+
* data. Specific reference kinds are handled as follows:
|
|
255
|
+
*
|
|
256
|
+
* - {@link ResourceShape | embedded references} — cascades recursively with the same semantics
|
|
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
|
|
260
|
+
* data is owned by the defining resource
|
|
261
|
+
*
|
|
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
|
|
265
|
+
*
|
|
266
|
+
* @returns A promise resolving to the `entry` {@link Reference} of the inserted resource; rejects with a
|
|
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
|
|
269
|
+
* network, storage, or other processing errors
|
|
270
|
+
*
|
|
271
|
+
* @throws {@link !Error Error} if the store has been {@link Store.close closed}
|
|
272
|
+
*
|
|
273
|
+
* @see {@link StoreClient.create create} for conditional creation
|
|
274
|
+
* @see {@link StoreClient.update update} for conditional replacement
|
|
275
|
+
*/
|
|
276
|
+
insert<S extends Lazy<ResourceShape>>(request: StoreUpdate<S>): Promise<Reference>;
|
|
277
|
+
/**
|
|
278
|
+
* Remove a resource.
|
|
279
|
+
*
|
|
280
|
+
* Unconditionally removes the resource's own data and clears references to it from other resources. Specific
|
|
281
|
+
* reference kinds are handled as follows:
|
|
282
|
+
*
|
|
283
|
+
* - {@link ResourceShape | embedded references} — cascades recursively with the same semantics
|
|
284
|
+
* - {@link @metreeca/blue/resource!PropertyConstraints.captive | captive references} — cascade-removed with
|
|
285
|
+
* the same semantics
|
|
286
|
+
*
|
|
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
|
|
290
|
+
*
|
|
291
|
+
* @returns A promise resolving to the `entry` {@link Reference} of the removed resource; rejects with a
|
|
292
|
+
* {@link !RangeError RangeError} if `entry` is not an absolute IRI, or a {@link Problem} on network, storage, or
|
|
293
|
+
* other processing errors
|
|
294
|
+
*
|
|
295
|
+
* @throws {@link !Error Error} if the store has been {@link Store.close closed}
|
|
296
|
+
*
|
|
297
|
+
* @see {@link StoreClient.delete delete} for conditional removal
|
|
298
|
+
*/
|
|
299
|
+
remove<S extends Lazy<ResourceShape>>(request: StoreDelete<S>): Promise<Reference>;
|
|
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
|
+
};
|
|
343
|
+
/**
|
|
344
|
+
* Store mutation observer.
|
|
345
|
+
*
|
|
346
|
+
* Receives batched mutation events. Registered via {@link Store.observe}.
|
|
347
|
+
*/
|
|
348
|
+
export interface StoreObserver {
|
|
349
|
+
/**
|
|
350
|
+
* Receive a batch of mutation events.
|
|
351
|
+
*
|
|
352
|
+
* Each entry maps a mutated resource id to an existence flag: `true` if the resource was
|
|
353
|
+
* {@link StoreClient.create created}, {@link StoreClient.update updated}, or {@link StoreClient.insert inserted};
|
|
354
|
+
* `false` if it was {@link StoreClient.delete deleted} or {@link StoreClient.remove removed}. When the same
|
|
355
|
+
* resource is mutated multiple times within a batch, last-write-wins semantics apply.
|
|
356
|
+
*
|
|
357
|
+
* > [!NOTE]
|
|
358
|
+
* > Observers may be sync or async. Both synchronous throws and asynchronous rejections are caught and
|
|
359
|
+
* > silently ignored so that one faulty observer cannot break delivery to others.
|
|
360
|
+
*
|
|
361
|
+
* @param mutations - Record mapping resource identifiers to existence flags
|
|
362
|
+
* (`true` for upserted, `false` for removed)
|
|
363
|
+
*/
|
|
364
|
+
(mutations: {
|
|
365
|
+
readonly [entry: Reference]: boolean;
|
|
366
|
+
}): Awaitable<void>;
|
|
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
|
+
};
|
|
464
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { Reference } from "@metreeca/qest/state";
|
|
2
|
+
import type { Store } from "../index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Creates a caching store backed by a delegate.
|
|
5
|
+
*
|
|
6
|
+
* Serves repeated retrievals from an in-memory cache, sparing the delegate a round-trip whenever a prior result can
|
|
7
|
+
* be reused. This cuts latency and backend load for read-heavy workloads, while writes and eviction keep cached
|
|
8
|
+
* results coherent and bounded.
|
|
9
|
+
*
|
|
10
|
+
* Results are cached by `entry`, `model`, and the `locale` and `limit` of the {@link Store.lookup lookup} retrieval scope.
|
|
11
|
+
* Templates differing only in member or element order share a cached result.
|
|
12
|
+
*
|
|
13
|
+
* > [!IMPORTANT]
|
|
14
|
+
* > `shape` is not part of the cache key. A retrieval result MUST depend only on `entry`, `model`, `locale` and
|
|
15
|
+
* > `limit`, with `shape` supplying structural metadata that does not affect the returned payload.
|
|
16
|
+
*
|
|
17
|
+
* > [!IMPORTANT]
|
|
18
|
+
* > The scope `locale` is part of the cache key, because it selects which localised content a retrieval returns.
|
|
19
|
+
* > Unlike the model, the locale priority list is order-significant: `["en", "it"]` and `["it", "en"]` are cached
|
|
20
|
+
* > separately. An omitted and an empty locale list share a single cached result, distinct from any explicit list.
|
|
21
|
+
*
|
|
22
|
+
* > [!IMPORTANT]
|
|
23
|
+
* > The scope `limit` is part of the cache key, because it caps the page size of each collection and so changes the
|
|
24
|
+
* > returned payload. An omitted and a `0` limit share a single cached result. The scope `plain` and `depth` are not
|
|
25
|
+
* > part of the key: they only accept or reject a model, and never alter a successful payload.
|
|
26
|
+
*
|
|
27
|
+
* The cache offers three observable guarantees:
|
|
28
|
+
*
|
|
29
|
+
* - **coalescing** — concurrent retrievals for the same key resolve from a single delegate fetch, and a failed
|
|
30
|
+
* retrieval or one finding no resource is never cached, so a later retrieval re-fetches;
|
|
31
|
+
* - **read-after-write coherence** — a retrieval never returns content staler than the latest write made through this
|
|
32
|
+
* store: {@link Store.create create}, {@link Store.update update}, {@link Store.delete delete},
|
|
33
|
+
* {@link Store.insert insert} and {@link Store.remove remove} invalidate matching entries, and a
|
|
34
|
+
* retrieval overlapping an in-progress write to the same entry is served from the delegate without being cached;
|
|
35
|
+
* - **commit coherence** — writes committed through {@link Store.execute execute}, by other clients sharing the
|
|
36
|
+
* delegate, or signalled by the backend invalidate matching entries on commit, for as long as the store stays open
|
|
37
|
+
* ({@link Store.close close} ends further invalidation).
|
|
38
|
+
*
|
|
39
|
+
* The configurable `match` predicate selects which cached entries a write invalidates.
|
|
40
|
+
*
|
|
41
|
+
* > [!IMPORTANT]
|
|
42
|
+
* > **Transaction Isolation** — Inherited from the wrapped {@link Store}.
|
|
43
|
+
*
|
|
44
|
+
* > [!IMPORTANT]
|
|
45
|
+
* > **Mutation Events** — Inherited from the wrapped {@link Store}.
|
|
46
|
+
*
|
|
47
|
+
* @param store - The delegate store to cache
|
|
48
|
+
* @param options - Cache configuration options
|
|
49
|
+
*
|
|
50
|
+
* @returns An immutable store with caching behaviour
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* const cache = createCachingStore(restStore, { size: 1_000, ttl: 60_000 });
|
|
55
|
+
* const product = await cache.lookup({ entry, shape, model });
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export declare function createCachingStore(store: Store, { size, ttl, match }?: {
|
|
59
|
+
/**
|
|
60
|
+
* Maximum number of records to cache.
|
|
61
|
+
*
|
|
62
|
+
* When the cache exceeds this bound, the least-recently-used record is evicted after each miss.
|
|
63
|
+
*
|
|
64
|
+
* @defaultValue 0 (unlimited)
|
|
65
|
+
*/
|
|
66
|
+
readonly size?: number;
|
|
67
|
+
/**
|
|
68
|
+
* Maximum record age in milliseconds.
|
|
69
|
+
*
|
|
70
|
+
* Records older than this value are treated as misses on the next retrieval and evicted when a sibling miss
|
|
71
|
+
* triggers the TTL sweep. Expiration is checked on every hit, so a stale record is never returned even if the
|
|
72
|
+
* sweep hasn't run yet.
|
|
73
|
+
*
|
|
74
|
+
* @defaultValue 0 (no expiration)
|
|
75
|
+
*/
|
|
76
|
+
readonly ttl?: number;
|
|
77
|
+
/**
|
|
78
|
+
* Predicate deciding which cached records an invalidation event discards.
|
|
79
|
+
*
|
|
80
|
+
* Invoked for writes made through the wrapper and for mutation events signalled by the delegate. It receives the
|
|
81
|
+
* identifier of the mutated resource and the identifier of each cached record, and returns `true` if the record
|
|
82
|
+
* is to be evicted.
|
|
83
|
+
*
|
|
84
|
+
* @param entry - Identifier of the mutated resource
|
|
85
|
+
* @param cache - Identifier associated with a cached record
|
|
86
|
+
*
|
|
87
|
+
* @returns true if the record should be evicted; false otherwise
|
|
88
|
+
*
|
|
89
|
+
* @defaultValue `() => true` (every mutation clears the cache)
|
|
90
|
+
*/
|
|
91
|
+
readonly match?: (entry: Reference, cache: Reference) => boolean;
|
|
92
|
+
}): Store;
|
|
93
|
+
//# sourceMappingURL=caching.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"caching.d.ts","sourceRoot":"","sources":["../../src/stores/caching.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,EAAE,EAEhD,IAAQ,EACR,GAAO,EACP,KAAkB,EAElB,GAAE;IAEF;;;;;;OAMG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;;;;;OAQG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,KAAK,OAAO,CAAC;CAE5D,GAAG,KAAK,CA0Vb"}
|