@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.
Files changed (37) hide show
  1. package/README.md +228 -24
  2. package/dist/clients/batching.core.d.ts +49 -0
  3. package/dist/clients/batching.core.d.ts.map +1 -0
  4. package/dist/clients/batching.core.js +159 -0
  5. package/dist/clients/batching.core.js.map +1 -0
  6. package/dist/clients/batching.d.ts +218 -0
  7. package/dist/clients/batching.d.ts.map +1 -0
  8. package/dist/clients/batching.js +109 -0
  9. package/dist/clients/batching.js.map +1 -0
  10. package/dist/clients/validating.d.ts +41 -0
  11. package/dist/clients/validating.d.ts.map +1 -0
  12. package/dist/clients/validating.js +150 -0
  13. package/dist/clients/validating.js.map +1 -0
  14. package/dist/index.d.ts +464 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/{states/index.js → index.js} +1 -1
  17. package/dist/index.js.map +1 -0
  18. package/dist/stores/caching.d.ts +93 -0
  19. package/dist/stores/caching.d.ts.map +1 -0
  20. package/dist/stores/caching.js +320 -0
  21. package/dist/stores/caching.js.map +1 -0
  22. package/dist/stores/managing.d.ts +57 -0
  23. package/dist/stores/managing.d.ts.map +1 -0
  24. package/dist/stores/managing.js +147 -0
  25. package/dist/stores/managing.js.map +1 -0
  26. package/package.json +34 -18
  27. package/dist/index/index.d.ts +0 -2
  28. package/dist/index/index.d.ts.map +0 -1
  29. package/dist/index/index.js +0 -17
  30. package/dist/index/index.js.map +0 -1
  31. package/dist/states/index.d.ts +0 -2
  32. package/dist/states/index.d.ts.map +0 -1
  33. package/dist/states/index.js.map +0 -1
  34. package/dist/stores/index.d.ts +0 -2
  35. package/dist/stores/index.d.ts.map +0 -1
  36. package/dist/stores/index.js +0 -17
  37. package/dist/stores/index.js.map +0 -1
@@ -0,0 +1,218 @@
1
+ /**
2
+ * Batching store client.
3
+ *
4
+ * Lets a connector serve many concurrent store requests with few backend round-trips. Requests of the same kind are
5
+ * coalesced into a single batch, and the connector supplies one batch {@link Handler} per kind. The resulting
6
+ * {@link StoreClient} adds conditional CRUD semantics on top, so handlers apply each request unconditionally.
7
+ *
8
+ * Four request kinds partition the work, each with its own handler:
9
+ *
10
+ * - **`detect`** — probes whether each {@link Detect} entry exists in the store
11
+ * - **`detail`** — materialises each {@link Detail} resource against its `model`; multi-valued members reached
12
+ * by the model may be handed to the `select` handler through {@link Broker.select}
13
+ * - **`select`** — materialises each {@link Select} collection against its `query`; element resources reached
14
+ * by the query may be handed to the `detail` handler through {@link Broker.detail}
15
+ * - **`modify`** — creates, updates, or deletes each {@link Modify} target, or links it to a collection
16
+ *
17
+ * Retrieval is driven by the requested `model` and `query`, not by the stored graph. A `detail` handler issues a
18
+ * `select` for each multi-valued member its model reaches, and a `select` handler issues a `detail` for each element
19
+ * resource its query reaches. Each request thus descends exactly as deep as its `model` or `query` nests, however
20
+ * deep the underlying graph runs.
21
+ *
22
+ * > [!IMPORTANT]
23
+ * > Handlers run concurrently and exchange nested requests through the {@link Broker}. A handler must not assume any
24
+ * > ordering between handlers, and must await every nested request it issues before settling the request that
25
+ * > depends on it.
26
+ *
27
+ * @module
28
+ */
29
+ import type { Property, ResourceShape } from "@metreeca/blue/resource";
30
+ import { type Match } from "@metreeca/blue/value";
31
+ import { type Lazy } from "@metreeca/core";
32
+ import type { Tag } from "@metreeca/core/language";
33
+ import type { Query, Slot, Template } from "@metreeca/qest/model";
34
+ import type { Reference, Resource, Value } from "@metreeca/qest/state";
35
+ import type { StoreClient } from "../index.js";
36
+ /**
37
+ * Requests accepted by {@link Broker}.
38
+ */
39
+ export type Request = Detect | Detail | Select | Modify;
40
+ /**
41
+ * Result of running a {@link Request}, narrowed by request variant:
42
+ *
43
+ * - a {@link Detect} resolves to a `boolean` existence flag
44
+ * - a {@link Detail} resolves to the materialised resource, typed by its shape and model
45
+ * - a {@link Select} resolves to the {@link Value | values} its collection holds, untyped since its shape is
46
+ * carried as a value rather than as a type parameter
47
+ * - a {@link Modify} resolves to its mutated entry's {@link Reference}
48
+ *
49
+ * @typeParam R The request whose result type is selected
50
+ */
51
+ export type Response<R extends Request> = R extends Select ? readonly Value[] : R extends Detail<infer T, infer S> ? Match<S, T> : R extends Modify ? Reference : R extends Detect ? boolean : never;
52
+ /**
53
+ * Resource existence request.
54
+ *
55
+ * Carries the `entry` to probe, and resolves to whether that entry exists in the store.
56
+ */
57
+ export type Detect = {
58
+ readonly entry: Reference;
59
+ };
60
+ /**
61
+ * Resource retrieval request.
62
+ *
63
+ * Carries a single resource (`entry` of `shape`), the `model` to materialise it against, and the `locale` priority
64
+ * for coalescing its localised content (QEST §6.2). Resolves to the materialised resource.
65
+ *
66
+ * @typeParam T The resource model selecting the members of the result
67
+ * @typeParam S The shape describing the resource
68
+ */
69
+ export type Detail<T extends Template = Template, S extends Lazy<ResourceShape> = Lazy<ResourceShape>> = {
70
+ readonly entry: Reference;
71
+ readonly shape: S;
72
+ readonly model: T;
73
+ readonly locale: readonly Tag[];
74
+ };
75
+ /**
76
+ * Collection retrieval request.
77
+ *
78
+ * Carries a single multi-valued property (`field` on `entry` of `shape`), the `query` describing its values, and the
79
+ * `locale` priority for coalescing its localised content (QEST §6.2). Resolves to the materialised collection: items
80
+ * for a template query, rows for a projection.
81
+ *
82
+ * @typeParam T The collection query selecting the form of the result
83
+ */
84
+ export type Select<T extends Query<Slot> = Query<Slot>> = {
85
+ readonly entry: Reference;
86
+ readonly shape: Lazy<ResourceShape>;
87
+ readonly field: Property;
88
+ readonly query: T;
89
+ readonly locale: readonly Tag[];
90
+ };
91
+ /**
92
+ * Resource mutation request.
93
+ *
94
+ * Carries the `entry` to mutate (of `shape`) and the target `state` to persist. A present `state` creates or updates
95
+ * the entry, and an omitted `state` deletes it. A request carrying a `link` instead adds `item` to the collection
96
+ * held by `entry` under `property`, leaving the rest of the entry unchanged. Resolves to the {@link Reference} of the
97
+ * mutated entry.
98
+ */
99
+ export type Modify = {
100
+ readonly entry: Reference;
101
+ readonly shape: Lazy<ResourceShape>;
102
+ readonly state?: Resource;
103
+ readonly link?: {
104
+ readonly property: Property;
105
+ readonly item: Reference;
106
+ };
107
+ };
108
+ /**
109
+ * Batch handler for a single request type.
110
+ *
111
+ * Supplied by the connector to {@link createBatchingStore}, one per request type. A handler receives every request of
112
+ * its type accumulated since the previous batch, together with a {@link Broker} for issuing nested requests. It
113
+ * settles each {@link Deferred} in the batch with its result or error. The returned promise must resolve only once
114
+ * every request in the batch has settled, including any nested requests issued through the broker.
115
+ *
116
+ * Batches are never empty, so handlers need not guard against an empty one.
117
+ *
118
+ * @typeParam R The request variant handled
119
+ */
120
+ export type Handler<R extends Request> = {
121
+ (batch: readonly Deferred<R>[], broker: Broker): Promise<void>;
122
+ };
123
+ /**
124
+ * A queued request paired with its promise-resolution hooks.
125
+ *
126
+ * Each batch a {@link Handler} receives is an array of `Deferred` entries. For each entry, the handler reads the
127
+ * `request` and settles it, calling `resolve` with its result (typed by {@link Response | Response<R>}) or `reject`
128
+ * with an error.
129
+ *
130
+ * @typeParam R The queued request variant: a {@link Detect} probe, a {@link Detail} resource fetch,
131
+ * a {@link Select} collection fetch, or a {@link Modify} mutation
132
+ */
133
+ export type Deferred<R extends Request> = {
134
+ readonly request: R;
135
+ resolve(value: Response<R>): void;
136
+ reject(error: unknown): void;
137
+ };
138
+ /**
139
+ * Request-submission surface over the batching {@link Handler | handlers}.
140
+ *
141
+ * Routes each request (detection, detail, selection, modification) to the handler for its type. Each call queues the
142
+ * request for the next batch and returns a promise that settles with its result. Handlers receive a broker to hand
143
+ * nested work to a sibling handler.
144
+ */
145
+ export type Broker = {
146
+ /**
147
+ * Enqueue a {@link Detect} request.
148
+ *
149
+ * @param request The entry to probe
150
+ *
151
+ * @returns A promise resolving to `true` when the request's entry exists in the store; `false`
152
+ * otherwise
153
+ */
154
+ detect(request: Detect): Promise<boolean>;
155
+ /**
156
+ * Enqueue a {@link Detail} request.
157
+ *
158
+ * Carries no existence precondition: an entry with no stored state resolves to an empty instance of
159
+ * the request's `model` rather than rejecting.
160
+ *
161
+ * @param request The resource and model to materialise
162
+ *
163
+ * @returns A promise resolving to the resource materialised against the request's `model`, settled once the
164
+ * owning batch and any nested requests needed to assemble it have resolved
165
+ */
166
+ detail<S extends Lazy<ResourceShape>, T extends Template>(request: Detail<T, S>): Promise<Match<S, T>>;
167
+ /**
168
+ * Enqueue a {@link Select} request.
169
+ *
170
+ * Carries no existence precondition: an entry with no matching members resolves to an empty
171
+ * collection rather than rejecting.
172
+ *
173
+ * @param request The property and query to materialise
174
+ *
175
+ * @returns A promise resolving to the values the collection holds as the request's `query` narrows them,
176
+ * items for a template and rows for a projection, settled once the owning batch and any nested requests
177
+ * needed to assemble them have resolved
178
+ */
179
+ select<T extends Query<Slot>>(request: Select<T>): Promise<readonly Value[]>;
180
+ /**
181
+ * Enqueue a {@link Modify} request.
182
+ *
183
+ * Mutates unconditionally, with no existence precondition. A present `state` upserts the entry, creating it if
184
+ * absent. An omitted `state` deletes the entry, and is a no-op if the entry is absent.
185
+ *
186
+ * @param request The entry and target state to persist, or the collection link to add
187
+ *
188
+ * @returns A promise resolving to the {@link Reference} of the mutated entry, settled once the owning
189
+ * batch has completed
190
+ */
191
+ modify(request: Modify): Promise<Reference>;
192
+ };
193
+ /**
194
+ * Creates a batching store client backed by a set of request handlers.
195
+ *
196
+ * Coalesces concurrent {@link StoreClient} calls into batches, each served by the matching `handlers` entry. The
197
+ * client adds conditional CRUD semantics on top of the handlers: `create`, `update` and `delete` probe for existence
198
+ * through the `detect` handler before handing the mutation to `modify`. `create` mints the identifier of the new
199
+ * resource, if its state names none, and links it to the collecting resource.
200
+ *
201
+ * The `modify` handler receives only requests whose `state` carries no `id` other than their `entry`. A request
202
+ * failing this check is rejected on its own, and the rest of the batch still applies. If the `modify` handler throws,
203
+ * the error rejects every request in its batch.
204
+ *
205
+ * @param handlers The batch handlers, one per request type
206
+ *
207
+ * @returns An immutable {@link StoreClient} serving its requests in batches through `handlers`
208
+ *
209
+ * @throws {@link !RangeError RangeError} If an update or insert `state` carries an `id` that differs from its `entry`,
210
+ * or if a create `state` member filling an identifier slot is not a single path segment
211
+ */
212
+ export declare function createBatchingStore(handlers: {
213
+ detect: Handler<Detect>;
214
+ detail: Handler<Detail>;
215
+ select: Handler<Select>;
216
+ modify: Handler<Modify>;
217
+ }): StoreClient;
218
+ //# sourceMappingURL=batching.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batching.d.ts","sourceRoot":"","sources":["../../src/clients/batching.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,109 @@
1
+ /*
2
+ * Copyright © 2025-2026 Metreeca srl
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { blueprint, collection } from "@metreeca/blue/value";
17
+ import { isString } from "@metreeca/core";
18
+ import { immutable } from "@metreeca/core/values";
19
+ import { createBroker, mint } from "./batching.core.js";
20
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
21
+ /**
22
+ * Creates a batching store client backed by a set of request handlers.
23
+ *
24
+ * Coalesces concurrent {@link StoreClient} calls into batches, each served by the matching `handlers` entry. The
25
+ * client adds conditional CRUD semantics on top of the handlers: `create`, `update` and `delete` probe for existence
26
+ * through the `detect` handler before handing the mutation to `modify`. `create` mints the identifier of the new
27
+ * resource, if its state names none, and links it to the collecting resource.
28
+ *
29
+ * The `modify` handler receives only requests whose `state` carries no `id` other than their `entry`. A request
30
+ * failing this check is rejected on its own, and the rest of the batch still applies. If the `modify` handler throws,
31
+ * the error rejects every request in its batch.
32
+ *
33
+ * @param handlers The batch handlers, one per request type
34
+ *
35
+ * @returns An immutable {@link StoreClient} serving its requests in batches through `handlers`
36
+ *
37
+ * @throws {@link !RangeError RangeError} If an update or insert `state` carries an `id` that differs from its `entry`,
38
+ * or if a create `state` member filling an identifier slot is not a single path segment
39
+ */
40
+ export function createBatchingStore(handlers) {
41
+ const broker = createBroker({
42
+ ...handlers,
43
+ async modify(batch, broker) {
44
+ const items = batch.filter(({ request: { entry, state }, reject }) => {
45
+ // reject each request whose state id contradicts its entry on its own, so the
46
+ // rest of the batch still applies and the check stays out of storage-specific handlers
47
+ const matching = state?.id === undefined || state.id === entry;
48
+ if (!matching) {
49
+ reject(new RangeError(`mismatched state id <${String(state?.id)}> for entry <${entry}>`));
50
+ }
51
+ return matching;
52
+ });
53
+ if (items.length > 0) {
54
+ // on backend failure, reject the surviving batch rather than throw: the broker
55
+ // treats a handler throw as a run-wide fault and propagates it to sibling queues
56
+ try {
57
+ await handlers.modify(items, broker);
58
+ }
59
+ catch (error) {
60
+ items.forEach(({ reject }) => reject(error));
61
+ }
62
+ }
63
+ }
64
+ });
65
+ return immutable({
66
+ async lookup({ entry, shape, model }, { locale = ["und"] } = {}) {
67
+ return await broker.detect({ entry })
68
+ ? broker.detail({ entry, shape, model, locale })
69
+ : undefined;
70
+ },
71
+ async create({ entry, shape, model, state }) {
72
+ // the new resource is stored under the identifier its state names, or under one minted for it off the
73
+ // blueprint of the collection; either way it is the resource probed for existence and written, not the
74
+ // collection
75
+ const target = collection(shape, model);
76
+ const plan = blueprint(shape, model);
77
+ const { id } = state;
78
+ const child = isString(id) ? id : mint(entry, plan, state);
79
+ // a collection a plain property holds is linked to its new member; one exposed through
80
+ // a foreign property is a view over the member's own link and reaches it on its own
81
+ return await broker.detect({ entry: child }) ? undefined
82
+ : broker.modify({ entry: child, shape: plan, state }).then(created => target.foreign
83
+ ? created
84
+ : broker.modify({ entry, shape, link: { property: target, item: created } }).then(() => created));
85
+ },
86
+ async update({ entry, shape, state }) {
87
+ // the entry identifies the target: a state carrying a different id contradicts it
88
+ const { id } = state;
89
+ if (id !== undefined && id !== entry) {
90
+ throw new RangeError(`mismatched state id <${String(id)}> for entry <${entry}>`);
91
+ }
92
+ return await broker.detect({ entry })
93
+ ? broker.modify({ entry, shape, state })
94
+ : undefined;
95
+ },
96
+ async delete({ entry, shape }) {
97
+ return await broker.detect({ entry })
98
+ ? broker.modify({ entry, shape })
99
+ : undefined;
100
+ },
101
+ insert(insert) {
102
+ return broker.modify(insert);
103
+ },
104
+ remove(remove) {
105
+ return broker.modify(remove);
106
+ }
107
+ });
108
+ }
109
+ //# sourceMappingURL=batching.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batching.js","sourceRoot":"","sources":["../../src/clients/batching.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import type { StoreClient } from "../index.js";
2
+ /**
3
+ * Creates a validating store backed by a delegate.
4
+ *
5
+ * Each method validates its inputs against the supplied shape before forwarding the request to `store`:
6
+ *
7
+ * - `entry` must be an absolute IRI with no query string and no fragment; otherwise the request is rejected with a
8
+ * {@link !RangeError RangeError}
9
+ * - `model` is validated as a {@link @metreeca/blue!validate | template} for {@link StoreClient.lookup lookup},
10
+ * within the query-complexity bounds (`plain`, `depth`, `limit`) of the caller's
11
+ * {@link StoreScope | retrieval scope}, and as a collection slice for {@link StoreClient.create create}
12
+ * - `state.id`, if present, must be nested under `entry` for {@link StoreClient.create create}, and equal to `entry`
13
+ * for {@link StoreClient.update update} and {@link StoreClient.insert insert}; otherwise the request is rejected
14
+ * with a {@link !RangeError RangeError}
15
+ * - `state` is validated as a resource with captive expansion capped at depth `0`, so inline captive batches are
16
+ * rejected; {@link StoreClient.create create} validates it against the shape of the collected resources, and
17
+ * {@link StoreClient.update update} and {@link StoreClient.insert insert} against the supplied shape
18
+ *
19
+ * The `trusted` option controls whether {@link StoreClient.lookup lookup} responses are re-validated against the
20
+ * shape, as narrowed by the caller's `model`. It defaults to `false`, the safe choice for connectors whose source may
21
+ * deliver non-conforming data, such as a remote REST endpoint. Local stores that compute results themselves can pass
22
+ * `trusted: true` to skip the redundant check.
23
+ *
24
+ * Validation failures surface as {@link @metreeca/core!TraceError | TraceError} or {@link !RangeError RangeError}
25
+ * rejections, in line with the unified {@link Store} error channel.
26
+ *
27
+ * @param store - Inner StoreClient receiving the validated requests
28
+ * @param options - Optional validation options
29
+ *
30
+ * @returns An immutable {@link StoreClient} wrapping `store` with input validation
31
+ */
32
+ export declare function createValidatingStore(store: StoreClient, { trusted }?: {
33
+ /**
34
+ * Whether to skip re-validation of {@link StoreClient.lookup lookup} responses against the shape, as narrowed by
35
+ * the caller's `model`.
36
+ *
37
+ * @defaultValue `false`, treating the wrapped store as untrusted
38
+ */
39
+ readonly trusted?: boolean;
40
+ }): StoreClient;
41
+ //# sourceMappingURL=validating.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validating.d.ts","sourceRoot":"","sources":["../../src/clients/validating.ts"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAAS,WAAW,EAAc,MAAM,aAAa,CAAC;AAGlE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,WAAW,EAAE,EAEzD,OAAe,EAEf,GAAE;IAEF;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAErB,GAAG,WAAW,CAkJnB"}
@@ -0,0 +1,150 @@
1
+ /*
2
+ * Copyright © 2025-2026 Metreeca srl
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ /**
17
+ * Validating store wrapper.
18
+ *
19
+ * Guards a {@link StoreClient} against malformed requests, checking entries, models and states against the request
20
+ * shape before they reach the backend. Retrieval responses from untrusted sources can be re-validated as well.
21
+ *
22
+ * @module
23
+ */
24
+ import { validate } from "@metreeca/blue";
25
+ import { blueprint } from "@metreeca/blue/value";
26
+ import { error } from "@metreeca/core";
27
+ import { isNestedIRI } from "@metreeca/core/resource";
28
+ import { TraceError } from "@metreeca/core/trace";
29
+ import { immutable } from "@metreeca/core/values";
30
+ import { isReference } from "@metreeca/qest/state";
31
+ /**
32
+ * Creates a validating store backed by a delegate.
33
+ *
34
+ * Each method validates its inputs against the supplied shape before forwarding the request to `store`:
35
+ *
36
+ * - `entry` must be an absolute IRI with no query string and no fragment; otherwise the request is rejected with a
37
+ * {@link !RangeError RangeError}
38
+ * - `model` is validated as a {@link @metreeca/blue!validate | template} for {@link StoreClient.lookup lookup},
39
+ * within the query-complexity bounds (`plain`, `depth`, `limit`) of the caller's
40
+ * {@link StoreScope | retrieval scope}, and as a collection slice for {@link StoreClient.create create}
41
+ * - `state.id`, if present, must be nested under `entry` for {@link StoreClient.create create}, and equal to `entry`
42
+ * for {@link StoreClient.update update} and {@link StoreClient.insert insert}; otherwise the request is rejected
43
+ * with a {@link !RangeError RangeError}
44
+ * - `state` is validated as a resource with captive expansion capped at depth `0`, so inline captive batches are
45
+ * rejected; {@link StoreClient.create create} validates it against the shape of the collected resources, and
46
+ * {@link StoreClient.update update} and {@link StoreClient.insert insert} against the supplied shape
47
+ *
48
+ * The `trusted` option controls whether {@link StoreClient.lookup lookup} responses are re-validated against the
49
+ * shape, as narrowed by the caller's `model`. It defaults to `false`, the safe choice for connectors whose source may
50
+ * deliver non-conforming data, such as a remote REST endpoint. Local stores that compute results themselves can pass
51
+ * `trusted: true` to skip the redundant check.
52
+ *
53
+ * Validation failures surface as {@link @metreeca/core!TraceError | TraceError} or {@link !RangeError RangeError}
54
+ * rejections, in line with the unified {@link Store} error channel.
55
+ *
56
+ * @param store - Inner StoreClient receiving the validated requests
57
+ * @param options - Optional validation options
58
+ *
59
+ * @returns An immutable {@link StoreClient} wrapping `store` with input validation
60
+ */
61
+ export function createValidatingStore(store, { trusted = false } = {}) {
62
+ return immutable({
63
+ async lookup({ entry, shape, model }, opts) {
64
+ return store.lookup({
65
+ entry: assertEntry(entry),
66
+ shape,
67
+ model: assertModel(model, shape, opts)
68
+ }, opts).then(result => {
69
+ // ;(cast) blue types the validated copy by its own `Delivery` inference rather than by the `Match`
70
+ // the store states; the copy is returned, rather than the raw response, as it carries the verdict
71
+ return (trusted || result === undefined ? immutable(result) : validate(result, { shape, model })({
72
+ value: value => value,
73
+ trace: trace => { throw new TraceError("invalid response", trace ?? []); }
74
+ }));
75
+ });
76
+ },
77
+ async create({ entry, shape, model, state }) {
78
+ const $entry = assertEntry(entry);
79
+ const $model = validate(model, { shape, model: true })({
80
+ value: value => value,
81
+ trace: trace => { throw new TraceError("invalid model", trace ?? []); }
82
+ });
83
+ // the new resource is validated against the shape the collecting property ranges over, and a stated
84
+ // identifier is held to the collection it is created through
85
+ const plan = blueprint(shape, $model);
86
+ const { id } = state;
87
+ const $state = id === undefined || isReference(id) && isNestedIRI($entry, id) ? state
88
+ : error(new RangeError(`state id <${String(id)}> not nested under entry <${entry}>`));
89
+ // the validator hands back the state itself where it passes, so the typed input is relayed as it
90
+ // stands once the check has run, the blueprint being resolved at runtime rather than typed
91
+ validate($state, { shape: plan, depth: 0 })({
92
+ value: () => undefined,
93
+ trace: trace => { throw new TraceError("invalid state", trace ?? []); }
94
+ });
95
+ return store.create({ entry: $entry, shape, model: $model, state: $state });
96
+ },
97
+ async update({ entry, shape, state }) {
98
+ const $entry = assertEntry(entry);
99
+ return store.update({
100
+ entry: $entry,
101
+ shape,
102
+ state: validate(assertIdentity($entry, state), { shape, depth: 0 })({
103
+ value: () => state,
104
+ trace: trace => { throw new TraceError("invalid state", trace ?? []); }
105
+ })
106
+ });
107
+ },
108
+ async delete({ entry, shape }) {
109
+ return store.delete({ entry: assertEntry(entry), shape });
110
+ },
111
+ async insert({ entry, shape, state }) {
112
+ const $entry = assertEntry(entry);
113
+ return store.insert({
114
+ entry: $entry,
115
+ shape,
116
+ state: validate(assertIdentity($entry, state), { shape, depth: 0 })({
117
+ value: () => state,
118
+ trace: trace => { throw new TraceError("invalid state", trace ?? []); }
119
+ })
120
+ });
121
+ },
122
+ async remove({ entry, shape }) {
123
+ return store.remove({
124
+ entry: assertEntry(entry),
125
+ shape
126
+ });
127
+ }
128
+ });
129
+ function assertEntry(entry) {
130
+ if (!isReference(entry)) {
131
+ throw new RangeError(`expected absolute IRI <${entry}>`);
132
+ }
133
+ if (entry.includes("?") || entry.includes("#")) {
134
+ throw new RangeError(`unexpected query or fragment in <${entry}>`);
135
+ }
136
+ return entry;
137
+ }
138
+ function assertIdentity(entry, state) {
139
+ const { id } = state;
140
+ return id === undefined || id === entry ? state
141
+ : error(new RangeError(`mismatched state id <${String(id)}> for entry <${entry}>`));
142
+ }
143
+ function assertModel(model, shape, opts = {}) {
144
+ return validate(model, { ...opts, shape, model: true })({
145
+ value: value => value,
146
+ trace: trace => { throw new TraceError("invalid model", trace ?? []); }
147
+ });
148
+ }
149
+ }
150
+ //# sourceMappingURL=validating.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validating.js","sourceRoot":"","sources":["../../src/clients/validating.ts"],"names":[],"mappings":"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"}