@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
|
@@ -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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"}
|