@_linked/core 2.26.0 → 2.28.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/CHANGELOG.md +25 -0
- package/README.md +33 -0
- package/lib/esm/index.d.ts +9 -0
- package/lib/esm/index.js +8 -0
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/interfaces/IDataset.d.ts +15 -0
- package/lib/esm/interfaces/IDataset.js.map +1 -1
- package/lib/esm/interfaces/IFileStore.d.ts +6 -0
- package/lib/esm/interfaces/IFileStore.js.map +1 -1
- package/lib/esm/live/LiveQueryStore.d.ts +240 -0
- package/lib/esm/live/LiveQueryStore.js +729 -0
- package/lib/esm/live/LiveQueryStore.js.map +1 -0
- package/lib/esm/live/changes.d.ts +30 -0
- package/lib/esm/live/changes.js +30 -0
- package/lib/esm/live/changes.js.map +1 -0
- package/lib/esm/live/keys.d.ts +54 -0
- package/lib/esm/live/keys.js +112 -0
- package/lib/esm/live/keys.js.map +1 -0
- package/lib/esm/live/matcher.d.ts +43 -0
- package/lib/esm/live/matcher.js +115 -0
- package/lib/esm/live/matcher.js.map +1 -0
- package/lib/esm/live/registry.d.ts +24 -0
- package/lib/esm/live/registry.js +41 -0
- package/lib/esm/live/registry.js.map +1 -0
- package/lib/esm/live.d.ts +12 -0
- package/lib/esm/live.js +15 -0
- package/lib/esm/live.js.map +1 -0
- package/lib/esm/queries/AskBuilder.d.ts +10 -0
- package/lib/esm/queries/AskBuilder.js +14 -0
- package/lib/esm/queries/AskBuilder.js.map +1 -1
- package/lib/esm/queries/CountBuilder.d.ts +10 -0
- package/lib/esm/queries/CountBuilder.js +14 -0
- package/lib/esm/queries/CountBuilder.js.map +1 -1
- package/lib/esm/queries/QueryBuilder.d.ts +10 -0
- package/lib/esm/queries/QueryBuilder.js +14 -0
- package/lib/esm/queries/QueryBuilder.js.map +1 -1
- package/lib/esm/queries/QueryContext.js +9 -2
- package/lib/esm/queries/QueryContext.js.map +1 -1
- package/lib/esm/queries/SelectQuery.d.ts +8 -8
- package/lib/esm/queries/SelectQuery.js.map +1 -1
- package/lib/esm/queries/queryDependencies.d.ts +58 -0
- package/lib/esm/queries/queryDependencies.js +494 -0
- package/lib/esm/queries/queryDependencies.js.map +1 -0
- package/lib/esm/queries/queryDispatch.d.ts +46 -0
- package/lib/esm/queries/queryDispatch.js +88 -3
- package/lib/esm/queries/queryDispatch.js.map +1 -1
- package/lib/esm/shapes/nodeShapeData.d.ts +16 -0
- package/lib/esm/shapes/nodeShapeData.js +41 -1
- package/lib/esm/shapes/nodeShapeData.js.map +1 -1
- package/lib/esm/sparql/irToAlgebra.js +1 -32
- package/lib/esm/sparql/irToAlgebra.js.map +1 -1
- package/lib/esm/utils/LinkedStorage.d.ts +6 -0
- package/lib/esm/utils/LinkedStorage.js +32 -6
- package/lib/esm/utils/LinkedStorage.js.map +1 -1
- package/package.json +6 -1
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Dependency helpers for live queries.
|
|
8
|
+
*
|
|
9
|
+
* Two pure functions that describe, in terms of **predicate IRIs**, what a query
|
|
10
|
+
* reads ({@link queryDependencies}) and what a mutation writes
|
|
11
|
+
* ({@link mutationEffects}). A live-query store feeds both into its change matcher:
|
|
12
|
+
* a query is re-run when a mutation writes a predicate the query depends on, on a
|
|
13
|
+
* node the query can see.
|
|
14
|
+
*
|
|
15
|
+
* Both work on the canonical IR (`lower(query)`), where every property is a
|
|
16
|
+
* property-SHAPE id. Each one is resolved to the predicate(s) it is declared with
|
|
17
|
+
* (`sh:path`), so two shapes that share a predicate — `Person.name` and `Team.name`
|
|
18
|
+
* both on `schema:name` — describe the same dependency, and a structured path
|
|
19
|
+
* (`^member`, `knows/name`) contributes every predicate it touches.
|
|
20
|
+
*/
|
|
21
|
+
import { lower } from './lower.js';
|
|
22
|
+
import { getSimplePathId } from '../paths/normalizePropertyPath.js';
|
|
23
|
+
import { collectPathUris } from '../paths/pathExprToSparql.js';
|
|
24
|
+
import { findPropertyShapeById, getPropertyShapes, } from '../shapes/nodeShapeData.js';
|
|
25
|
+
import { getAllNodeShapes, getNodeShape, getRegistryVersion, getShapeClass, } from '../utils/ShapeClass.js';
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Predicate resolution
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
/**
|
|
30
|
+
* The predicate IRI(s) a property-shape id reads or writes.
|
|
31
|
+
*
|
|
32
|
+
* An inline `pathExpr` wins (it IS the path). Otherwise the registered property
|
|
33
|
+
* shape's `sh:path`: a simple path is one predicate, a structured path every IRI
|
|
34
|
+
* it mentions. An unknown id falls back to the id itself — never empty, so a
|
|
35
|
+
* dependency is never silently dropped.
|
|
36
|
+
*/
|
|
37
|
+
function predicatesOf(propertyShapeId, pathExpr) {
|
|
38
|
+
if (pathExpr) {
|
|
39
|
+
const uris = collectPathUris(pathExpr);
|
|
40
|
+
return uris.length ? uris : [propertyShapeId];
|
|
41
|
+
}
|
|
42
|
+
const propertyShape = findPropertyShapeById(propertyShapeId);
|
|
43
|
+
if (!propertyShape)
|
|
44
|
+
return [propertyShapeId];
|
|
45
|
+
const simple = getSimplePathId(propertyShape.path);
|
|
46
|
+
if (simple !== null)
|
|
47
|
+
return [simple];
|
|
48
|
+
const uris = collectPathUris(propertyShape.path);
|
|
49
|
+
return uris.length ? uris : [propertyShapeId];
|
|
50
|
+
}
|
|
51
|
+
/** The value shape a property-shape id points at, if it is an object property. */
|
|
52
|
+
function valueShapeOf(propertyShapeId) {
|
|
53
|
+
var _a, _b;
|
|
54
|
+
return (_b = (_a = findPropertyShapeById(propertyShapeId)) === null || _a === void 0 ? void 0 : _a.valueShape) === null || _b === void 0 ? void 0 : _b.id;
|
|
55
|
+
}
|
|
56
|
+
// Owner lookups, cached on the same terms as `findPropertyShapeById`: successful
|
|
57
|
+
// resolutions only, invalidated by the registry version.
|
|
58
|
+
const declaringShapeCache = new Map();
|
|
59
|
+
let declaringShapeCacheVersion = -1;
|
|
60
|
+
/**
|
|
61
|
+
* The node shape that declares a property shape as its own.
|
|
62
|
+
*
|
|
63
|
+
* Reading a property pins the query to the shape it is declared on, even when the
|
|
64
|
+
* IR carries no scan for it: `p.friends.as(Employee).employeeId` lowers to a plain
|
|
65
|
+
* traversal plus a property read — the cast is type-level only — and the only
|
|
66
|
+
* trace of `Employee` left is that `employeeId` is declared there.
|
|
67
|
+
*/
|
|
68
|
+
function declaringShapeOf(propertyShapeId) {
|
|
69
|
+
const version = getRegistryVersion();
|
|
70
|
+
if (version !== declaringShapeCacheVersion) {
|
|
71
|
+
declaringShapeCache.clear();
|
|
72
|
+
declaringShapeCacheVersion = version;
|
|
73
|
+
}
|
|
74
|
+
const cached = declaringShapeCache.get(propertyShapeId);
|
|
75
|
+
if (cached)
|
|
76
|
+
return cached;
|
|
77
|
+
for (const nodeShape of getAllNodeShapes().values()) {
|
|
78
|
+
if (getPropertyShapes(nodeShape, false).some((ps) => ps.id === propertyShapeId)) {
|
|
79
|
+
declaringShapeCache.set(propertyShapeId, nodeShape.id);
|
|
80
|
+
return nodeShape.id;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
const addAll = (target, values) => {
|
|
86
|
+
for (const v of values)
|
|
87
|
+
target.add(v);
|
|
88
|
+
};
|
|
89
|
+
class DependencyCollector {
|
|
90
|
+
constructor() {
|
|
91
|
+
this.deps = {
|
|
92
|
+
narrow: new Set(),
|
|
93
|
+
filter: new Set(),
|
|
94
|
+
hidden: new Set(),
|
|
95
|
+
shapes: new Set(),
|
|
96
|
+
unbound: true,
|
|
97
|
+
};
|
|
98
|
+
/** Every traverse, keyed by the alias it binds. */
|
|
99
|
+
this.traverseByTo = new Map();
|
|
100
|
+
/** Traverses whose predicates were attributed to a set by a projection or a filter. */
|
|
101
|
+
this.reached = new Set();
|
|
102
|
+
/** Filter expressions found while walking patterns, with the alias their scope starts at. */
|
|
103
|
+
this.scopedFilters = [];
|
|
104
|
+
}
|
|
105
|
+
// --- pass 1: patterns -------------------------------------------------------
|
|
106
|
+
walkPattern(pattern, inFilter) {
|
|
107
|
+
var _a;
|
|
108
|
+
switch (pattern.kind) {
|
|
109
|
+
case 'shape_scan':
|
|
110
|
+
this.deps.shapes.add(pattern.shape);
|
|
111
|
+
return;
|
|
112
|
+
case 'traverse': {
|
|
113
|
+
this.traverseByTo.set(pattern.to, { pattern, inFilter });
|
|
114
|
+
this.recordShapesOf(pattern.property);
|
|
115
|
+
if (pattern.filter) {
|
|
116
|
+
// A scoped where (`t.members.where(m => …)`) decides which related rows
|
|
117
|
+
// take part; its chain stops at the traversal it scopes, whose own
|
|
118
|
+
// predicate is attributed wherever that traversal lands.
|
|
119
|
+
this.scopedFilters.push({ expr: pattern.filter, scope: pattern.to });
|
|
120
|
+
}
|
|
121
|
+
for (const order of (_a = pattern.innerOrderBy) !== null && _a !== void 0 ? _a : []) {
|
|
122
|
+
addAll(this.deps.filter, predicatesOf(order.property));
|
|
123
|
+
}
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
case 'join':
|
|
127
|
+
for (const p of pattern.patterns)
|
|
128
|
+
this.walkPattern(p, inFilter);
|
|
129
|
+
return;
|
|
130
|
+
case 'optional':
|
|
131
|
+
this.walkPattern(pattern.pattern, inFilter);
|
|
132
|
+
return;
|
|
133
|
+
case 'union':
|
|
134
|
+
for (const p of pattern.branches)
|
|
135
|
+
this.walkPattern(p, inFilter);
|
|
136
|
+
return;
|
|
137
|
+
case 'exists':
|
|
138
|
+
this.walkPattern(pattern.pattern, true);
|
|
139
|
+
return;
|
|
140
|
+
case 'minus':
|
|
141
|
+
this.walkPattern(pattern.pattern, true);
|
|
142
|
+
if (pattern.filter)
|
|
143
|
+
this.scopedFilters.push({ expr: pattern.filter });
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// --- pass 2: attribution -----------------------------------------------------
|
|
148
|
+
/**
|
|
149
|
+
* The predicates of every traversal from `alias` back towards the root, stopping
|
|
150
|
+
* before `stopAt` when given. Marks each traversal as reached.
|
|
151
|
+
*/
|
|
152
|
+
chain(alias, stopAt) {
|
|
153
|
+
const preds = [];
|
|
154
|
+
const visited = new Set();
|
|
155
|
+
let current = alias;
|
|
156
|
+
while (current !== stopAt && !visited.has(current)) {
|
|
157
|
+
visited.add(current);
|
|
158
|
+
const info = this.traverseByTo.get(current);
|
|
159
|
+
if (!info)
|
|
160
|
+
break;
|
|
161
|
+
this.reached.add(info.pattern);
|
|
162
|
+
preds.push(...predicatesOf(info.pattern.property, info.pattern.pathExpr));
|
|
163
|
+
current = info.pattern.from;
|
|
164
|
+
}
|
|
165
|
+
return preds;
|
|
166
|
+
}
|
|
167
|
+
/** The shapes a property read pins the query to: its declaring shape and its value shape. */
|
|
168
|
+
recordShapesOf(propertyShapeId) {
|
|
169
|
+
const declaring = declaringShapeOf(propertyShapeId);
|
|
170
|
+
if (declaring)
|
|
171
|
+
this.deps.shapes.add(declaring);
|
|
172
|
+
const valueShape = valueShapeOf(propertyShapeId);
|
|
173
|
+
if (valueShape)
|
|
174
|
+
this.deps.shapes.add(valueShape);
|
|
175
|
+
}
|
|
176
|
+
propertyExprPredicates(expr, scope) {
|
|
177
|
+
this.recordShapesOf(expr.property);
|
|
178
|
+
return [...predicatesOf(expr.property, expr.pathExpr), ...this.chain(expr.sourceAlias, scope)];
|
|
179
|
+
}
|
|
180
|
+
/** Every predicate an expression decides membership or order by. */
|
|
181
|
+
collectFilter(expr, scope) {
|
|
182
|
+
switch (expr.kind) {
|
|
183
|
+
case 'property_expr':
|
|
184
|
+
addAll(this.deps.filter, this.propertyExprPredicates(expr, scope));
|
|
185
|
+
return;
|
|
186
|
+
case 'context_property_expr':
|
|
187
|
+
addAll(this.deps.filter, predicatesOf(expr.property));
|
|
188
|
+
return;
|
|
189
|
+
case 'alias_expr':
|
|
190
|
+
addAll(this.deps.filter, this.chain(expr.alias, scope));
|
|
191
|
+
return;
|
|
192
|
+
case 'exists_expr':
|
|
193
|
+
this.walkPattern(expr.pattern, true);
|
|
194
|
+
if (expr.filter)
|
|
195
|
+
this.collectFilter(expr.filter, scope);
|
|
196
|
+
return;
|
|
197
|
+
default:
|
|
198
|
+
for (const child of childExpressions(expr))
|
|
199
|
+
this.collectFilter(child, scope);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/** A projected expression: plain paths are narrow, computed values hidden. */
|
|
203
|
+
collectProjection(expr) {
|
|
204
|
+
switch (expr.kind) {
|
|
205
|
+
case 'property_expr':
|
|
206
|
+
addAll(this.deps.narrow, this.propertyExprPredicates(expr));
|
|
207
|
+
return;
|
|
208
|
+
case 'alias_expr':
|
|
209
|
+
addAll(this.deps.narrow, this.chain(expr.alias));
|
|
210
|
+
return;
|
|
211
|
+
default:
|
|
212
|
+
this.collectHidden(expr);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
collectHidden(expr) {
|
|
216
|
+
switch (expr.kind) {
|
|
217
|
+
case 'property_expr':
|
|
218
|
+
addAll(this.deps.hidden, this.propertyExprPredicates(expr));
|
|
219
|
+
return;
|
|
220
|
+
case 'alias_expr':
|
|
221
|
+
addAll(this.deps.hidden, this.chain(expr.alias));
|
|
222
|
+
return;
|
|
223
|
+
case 'context_property_expr':
|
|
224
|
+
addAll(this.deps.filter, predicatesOf(expr.property));
|
|
225
|
+
return;
|
|
226
|
+
case 'exists_expr':
|
|
227
|
+
this.collectFilter(expr);
|
|
228
|
+
return;
|
|
229
|
+
default:
|
|
230
|
+
for (const child of childExpressions(expr))
|
|
231
|
+
this.collectHidden(child);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/** Filters gathered while walking patterns (scoped where, minus filter). */
|
|
235
|
+
flushScopedFilters() {
|
|
236
|
+
// `collectFilter` on an exists expression may walk new patterns and push new
|
|
237
|
+
// scoped filters, so drain until stable.
|
|
238
|
+
while (this.scopedFilters.length) {
|
|
239
|
+
const { expr, scope } = this.scopedFilters.shift();
|
|
240
|
+
this.collectFilter(expr, scope);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/** Traversals no projection or filter attributed: read, but their ids never surface. */
|
|
244
|
+
attributeUnreached() {
|
|
245
|
+
for (const { pattern, inFilter } of this.traverseByTo.values()) {
|
|
246
|
+
if (this.reached.has(pattern))
|
|
247
|
+
continue;
|
|
248
|
+
const target = inFilter ? this.deps.filter : this.deps.hidden;
|
|
249
|
+
addAll(target, predicatesOf(pattern.property, pattern.pathExpr));
|
|
250
|
+
// The traversal leading up to it is read on the same terms.
|
|
251
|
+
addAll(target, this.chain(pattern.from));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/** The immediate sub-expressions of a compound expression (none for leaves). */
|
|
256
|
+
function childExpressions(expr) {
|
|
257
|
+
switch (expr.kind) {
|
|
258
|
+
case 'binary_expr':
|
|
259
|
+
return [expr.left, expr.right];
|
|
260
|
+
case 'logical_expr':
|
|
261
|
+
return expr.expressions;
|
|
262
|
+
case 'not_expr':
|
|
263
|
+
return [expr.expression];
|
|
264
|
+
case 'function_expr':
|
|
265
|
+
case 'aggregate_expr':
|
|
266
|
+
return expr.args;
|
|
267
|
+
case 'in_expr':
|
|
268
|
+
return [expr.value, ...expr.source.list];
|
|
269
|
+
default:
|
|
270
|
+
return [];
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/** `lower()` is overloaded per builder kind; dispatch on the discriminator. */
|
|
274
|
+
function lowerQuery(query) {
|
|
275
|
+
switch (query.__queryKind) {
|
|
276
|
+
case 'select':
|
|
277
|
+
return lower(query);
|
|
278
|
+
case 'count':
|
|
279
|
+
return lower(query);
|
|
280
|
+
case 'ask':
|
|
281
|
+
return lower(query);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
function isBound(ir) {
|
|
285
|
+
return !!(ir.subjectId || (ir.subjectIds && ir.subjectIds.length));
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* What a query reads, as predicate IRIs and shape IRIs.
|
|
289
|
+
*
|
|
290
|
+
* - `narrow`: read on nodes whose ids are in the result (root rows and projected
|
|
291
|
+
* relation rows) — a change to one of these on a visible node changes the result.
|
|
292
|
+
* - `filter`: decide which nodes are in the result and in what order.
|
|
293
|
+
* - `hidden`: read on nodes the result does not identify (counted relations,
|
|
294
|
+
* computed values over traversals), so a change there can only be matched by
|
|
295
|
+
* predicate, not by id.
|
|
296
|
+
* - `shapes`: every shape whose instance set the query scans or traverses into.
|
|
297
|
+
* - `unbound`: `true` unless the query is pinned to known subjects.
|
|
298
|
+
*/
|
|
299
|
+
export function queryDependencies(query) {
|
|
300
|
+
var _a;
|
|
301
|
+
const ir = lowerQuery(query);
|
|
302
|
+
const collector = new DependencyCollector();
|
|
303
|
+
const deps = collector.deps;
|
|
304
|
+
if (!ir.root) {
|
|
305
|
+
// Shapeless ask: no rdf:type constraint, no properties — nothing is read but
|
|
306
|
+
// the subject itself.
|
|
307
|
+
deps.unbound = !ir.subjectId;
|
|
308
|
+
return deps;
|
|
309
|
+
}
|
|
310
|
+
collector.walkPattern(ir.root, false);
|
|
311
|
+
for (const pattern of ir.patterns)
|
|
312
|
+
collector.walkPattern(pattern, false);
|
|
313
|
+
if (ir.kind === 'select') {
|
|
314
|
+
for (const item of ir.projection)
|
|
315
|
+
collector.collectProjection(item.expression);
|
|
316
|
+
}
|
|
317
|
+
if (ir.where)
|
|
318
|
+
collector.collectFilter(ir.where);
|
|
319
|
+
if (ir.kind === 'select') {
|
|
320
|
+
for (const order of (_a = ir.orderBy) !== null && _a !== void 0 ? _a : [])
|
|
321
|
+
collector.collectFilter(order.expression);
|
|
322
|
+
}
|
|
323
|
+
collector.flushScopedFilters();
|
|
324
|
+
collector.attributeUnreached();
|
|
325
|
+
deps.unbound = !isBound(ir);
|
|
326
|
+
return deps;
|
|
327
|
+
}
|
|
328
|
+
// ---------------------------------------------------------------------------
|
|
329
|
+
// mutationEffects
|
|
330
|
+
// ---------------------------------------------------------------------------
|
|
331
|
+
const isNodeData = (value) => !!value && typeof value === 'object' && 'fields' in value && 'shape' in value;
|
|
332
|
+
const isSetModification = (value) => !!value && typeof value === 'object' && ('add' in value || 'remove' in value) && !('kind' in value);
|
|
333
|
+
class EffectCollector {
|
|
334
|
+
constructor() {
|
|
335
|
+
this.props = new Set();
|
|
336
|
+
this.ids = new Set();
|
|
337
|
+
this.membership = new Set();
|
|
338
|
+
}
|
|
339
|
+
/** Every field at every depth writes its predicate; nested nodes may be created. */
|
|
340
|
+
walkNodeData(data, nested) {
|
|
341
|
+
if (nested) {
|
|
342
|
+
this.membership.add(data.shape);
|
|
343
|
+
if (data.id)
|
|
344
|
+
this.ids.add(data.id);
|
|
345
|
+
}
|
|
346
|
+
else if (data.id) {
|
|
347
|
+
this.ids.add(data.id);
|
|
348
|
+
}
|
|
349
|
+
for (const field of data.fields) {
|
|
350
|
+
addAll(this.props, predicatesOf(field.property));
|
|
351
|
+
this.walkValue(field.value);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
walkValue(value) {
|
|
355
|
+
var _a, _b;
|
|
356
|
+
if (value === null || value === undefined)
|
|
357
|
+
return;
|
|
358
|
+
if (Array.isArray(value)) {
|
|
359
|
+
for (const item of value)
|
|
360
|
+
this.walkValue(item);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
if (typeof value !== 'object' || value instanceof Date)
|
|
364
|
+
return;
|
|
365
|
+
if (isNodeData(value)) {
|
|
366
|
+
this.walkNodeData(value, true);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (isSetModification(value)) {
|
|
370
|
+
for (const item of (_a = value.add) !== null && _a !== void 0 ? _a : [])
|
|
371
|
+
this.walkValue(item);
|
|
372
|
+
for (const ref of (_b = value.remove) !== null && _b !== void 0 ? _b : []) {
|
|
373
|
+
if (ref === null || ref === void 0 ? void 0 : ref.id)
|
|
374
|
+
this.ids.add(ref.id);
|
|
375
|
+
}
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
// A `{id}` reference or an expression: neither writes a predicate of its own,
|
|
379
|
+
// and a referenced node is not itself modified.
|
|
380
|
+
}
|
|
381
|
+
/** Every string under a key named `id`, anywhere in a store result. */
|
|
382
|
+
walkResult(value, seen = new Set()) {
|
|
383
|
+
if (!value || typeof value !== 'object' || value instanceof Date)
|
|
384
|
+
return;
|
|
385
|
+
if (seen.has(value))
|
|
386
|
+
return;
|
|
387
|
+
seen.add(value);
|
|
388
|
+
if (Array.isArray(value)) {
|
|
389
|
+
for (const item of value)
|
|
390
|
+
this.walkResult(item, seen);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
for (const [key, child] of Object.entries(value)) {
|
|
394
|
+
if (key === 'id' && typeof child === 'string')
|
|
395
|
+
this.ids.add(child);
|
|
396
|
+
else
|
|
397
|
+
this.walkResult(child, seen);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
/** `lower()` is overloaded per builder kind; dispatch on the discriminator. */
|
|
402
|
+
function lowerMutation(mutation) {
|
|
403
|
+
switch (mutation.__queryKind) {
|
|
404
|
+
case 'create':
|
|
405
|
+
return lower(mutation);
|
|
406
|
+
case 'update':
|
|
407
|
+
return lower(mutation);
|
|
408
|
+
case 'delete':
|
|
409
|
+
return lower(mutation);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
/** The shape metadata for a shape IRI, class-backed or data-only. */
|
|
413
|
+
function resolveShapeData(shapeId) {
|
|
414
|
+
var _a, _b;
|
|
415
|
+
return (_b = (_a = getShapeClass(shapeId)) === null || _a === void 0 ? void 0 : _a.shape) !== null && _b !== void 0 ? _b : getNodeShape(shapeId);
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* What a mutation writes, as predicate IRIs, affected node ids and the shapes
|
|
419
|
+
* whose instance sets may have changed.
|
|
420
|
+
*
|
|
421
|
+
* `result` — what the store returned — is optional and only widens `ids`: a create
|
|
422
|
+
* learns its generated id from it, and an add/remove learns the ids of nested
|
|
423
|
+
* creates. It is never consulted for an op whose target set is unknown
|
|
424
|
+
* (`update_where`, `delete_all`, `delete_where`), where `ids` stays `undefined`.
|
|
425
|
+
*/
|
|
426
|
+
export function mutationEffects(mutation, result) {
|
|
427
|
+
const ir = lowerMutation(mutation);
|
|
428
|
+
const collector = new EffectCollector();
|
|
429
|
+
switch (ir.kind) {
|
|
430
|
+
case 'create': {
|
|
431
|
+
collector.membership.add(ir.shape);
|
|
432
|
+
collector.walkNodeData(ir.data, false);
|
|
433
|
+
collector.walkResult(result);
|
|
434
|
+
return {
|
|
435
|
+
op: 'create',
|
|
436
|
+
shape: ir.shape,
|
|
437
|
+
props: collector.props,
|
|
438
|
+
ids: collector.ids,
|
|
439
|
+
membership: collector.membership,
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
case 'update':
|
|
443
|
+
case 'upsert': {
|
|
444
|
+
if (ir.kind === 'upsert')
|
|
445
|
+
collector.membership.add(ir.shape);
|
|
446
|
+
collector.ids.add(ir.id);
|
|
447
|
+
collector.walkNodeData(ir.data, false);
|
|
448
|
+
collector.walkResult(result);
|
|
449
|
+
return {
|
|
450
|
+
op: ir.kind,
|
|
451
|
+
shape: ir.shape,
|
|
452
|
+
props: collector.props,
|
|
453
|
+
ids: collector.ids,
|
|
454
|
+
membership: collector.membership,
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
case 'update_where': {
|
|
458
|
+
collector.walkNodeData(ir.data, false);
|
|
459
|
+
return {
|
|
460
|
+
op: 'update',
|
|
461
|
+
shape: ir.shape,
|
|
462
|
+
props: collector.props,
|
|
463
|
+
ids: undefined,
|
|
464
|
+
membership: collector.membership,
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
case 'delete':
|
|
468
|
+
case 'delete_all':
|
|
469
|
+
case 'delete_where': {
|
|
470
|
+
collector.membership.add(ir.shape);
|
|
471
|
+
const shapeData = resolveShapeData(ir.shape);
|
|
472
|
+
if (shapeData) {
|
|
473
|
+
for (const ps of getPropertyShapes(shapeData, true)) {
|
|
474
|
+
addAll(collector.props, predicatesOf(ps.id));
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if (ir.kind === 'delete') {
|
|
478
|
+
for (const ref of ir.ids) {
|
|
479
|
+
if (ref === null || ref === void 0 ? void 0 : ref.id)
|
|
480
|
+
collector.ids.add(ref.id);
|
|
481
|
+
}
|
|
482
|
+
collector.walkResult(result);
|
|
483
|
+
}
|
|
484
|
+
return {
|
|
485
|
+
op: 'delete',
|
|
486
|
+
shape: ir.shape,
|
|
487
|
+
props: collector.props,
|
|
488
|
+
ids: ir.kind === 'delete' ? collector.ids : undefined,
|
|
489
|
+
membership: collector.membership,
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
//# sourceMappingURL=queryDependencies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queryDependencies.js","sourceRoot":"","sources":["../../../src/queries/queryDependencies.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n/**\n * Dependency helpers for live queries.\n *\n * Two pure functions that describe, in terms of **predicate IRIs**, what a query\n * reads ({@link queryDependencies}) and what a mutation writes\n * ({@link mutationEffects}). A live-query store feeds both into its change matcher:\n * a query is re-run when a mutation writes a predicate the query depends on, on a\n * node the query can see.\n *\n * Both work on the canonical IR (`lower(query)`), where every property is a\n * property-SHAPE id. Each one is resolved to the predicate(s) it is declared with\n * (`sh:path`), so two shapes that share a predicate — `Person.name` and `Team.name`\n * both on `schema:name` — describe the same dependency, and a structured path\n * (`^member`, `knows/name`) contributes every predicate it touches.\n */\nimport {lower} from './lower.js';\nimport type {\n LowerableAsk,\n LowerableCount,\n LowerableCreate,\n LowerableDelete,\n LowerableSelect,\n LowerableUpdate,\n} from './lower.js';\nimport type {\n IRAskQuery,\n IRCountQuery,\n IRExpression,\n IRFieldValue,\n IRGraphPattern,\n IRNodeData,\n IRPropertyExpression,\n IRSelectQuery,\n IRTraversePattern,\n} from './IntermediateRepresentation.js';\nimport type {IRCreateQuery} from './CreateQuery.js';\nimport type {IRUpdateQuery} from './UpdateQuery.js';\nimport type {IRDeleteQuery} from './DeleteQuery.js';\nimport type {PathExpr} from '../paths/PropertyPathExpr.js';\nimport {getSimplePathId} from '../paths/normalizePropertyPath.js';\nimport {collectPathUris} from '../paths/pathExprToSparql.js';\nimport {\n findPropertyShapeById,\n getPropertyShapes,\n type NodeShapeData,\n} from '../shapes/nodeShapeData.js';\nimport {\n getAllNodeShapes,\n getNodeShape,\n getRegistryVersion,\n getShapeClass,\n} from '../utils/ShapeClass.js';\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\nexport type QueryDependencies = {\n /** Predicates read on nodes whose ids appear in the result (root row + projected relation rows). */\n narrow: Set<string>;\n /** Predicates deciding membership/order: where, scoped where, minus, exists, sortBy, inner orderBy. */\n filter: Set<string>;\n /**\n * Predicates read on nodes whose ids are NOT projected: `size()` / aggregates over\n * relations, computed values over traversals, traversals with no projected id.\n */\n hidden: Set<string>;\n /**\n * Shape IRIs the query is pinned to: the root scan, nested shape scans (minus by\n * shape), and — for every property read — the shape declaring it and the value\n * shape it points at.\n */\n shapes: Set<string>;\n /** `false` when the query is bound to subjects (`.for` / `.forAll` / a resolved context). */\n unbound: boolean;\n};\n\nexport type MutationEffects = {\n op: 'create' | 'update' | 'upsert' | 'delete';\n /** Routing shape IRI. */\n shape: string;\n /** Predicates written (nested node descriptions and add/remove relations included); for delete: every predicate of the shape. */\n props: Set<string>;\n /**\n * Affected node ids: the target id(s), nested created nodes, removed references, and every\n * `id` found anywhere in `result` (conservative: a reference echoed in the result counts).\n * `undefined` = unknown (`update_where`, `delete_all`, `delete_where`).\n */\n ids?: Set<string>;\n /** Shapes whose instance set may have changed: the target shape for create/upsert/delete + shapes of nested created nodes; empty for a plain update. */\n membership: Set<string>;\n};\n\n// ---------------------------------------------------------------------------\n// Predicate resolution\n// ---------------------------------------------------------------------------\n\n/**\n * The predicate IRI(s) a property-shape id reads or writes.\n *\n * An inline `pathExpr` wins (it IS the path). Otherwise the registered property\n * shape's `sh:path`: a simple path is one predicate, a structured path every IRI\n * it mentions. An unknown id falls back to the id itself — never empty, so a\n * dependency is never silently dropped.\n */\nfunction predicatesOf(propertyShapeId: string, pathExpr?: PathExpr): string[] {\n if (pathExpr) {\n const uris = collectPathUris(pathExpr);\n return uris.length ? uris : [propertyShapeId];\n }\n const propertyShape = findPropertyShapeById(propertyShapeId);\n if (!propertyShape) return [propertyShapeId];\n const simple = getSimplePathId(propertyShape.path);\n if (simple !== null) return [simple];\n const uris = collectPathUris(propertyShape.path);\n return uris.length ? uris : [propertyShapeId];\n}\n\n/** The value shape a property-shape id points at, if it is an object property. */\nfunction valueShapeOf(propertyShapeId: string): string | undefined {\n return findPropertyShapeById(propertyShapeId)?.valueShape?.id;\n}\n\n// Owner lookups, cached on the same terms as `findPropertyShapeById`: successful\n// resolutions only, invalidated by the registry version.\nconst declaringShapeCache = new Map<string, string>();\nlet declaringShapeCacheVersion = -1;\n\n/**\n * The node shape that declares a property shape as its own.\n *\n * Reading a property pins the query to the shape it is declared on, even when the\n * IR carries no scan for it: `p.friends.as(Employee).employeeId` lowers to a plain\n * traversal plus a property read — the cast is type-level only — and the only\n * trace of `Employee` left is that `employeeId` is declared there.\n */\nfunction declaringShapeOf(propertyShapeId: string): string | undefined {\n const version = getRegistryVersion();\n if (version !== declaringShapeCacheVersion) {\n declaringShapeCache.clear();\n declaringShapeCacheVersion = version;\n }\n const cached = declaringShapeCache.get(propertyShapeId);\n if (cached) return cached;\n for (const nodeShape of getAllNodeShapes().values()) {\n if (getPropertyShapes(nodeShape, false).some((ps) => ps.id === propertyShapeId)) {\n declaringShapeCache.set(propertyShapeId, nodeShape.id);\n return nodeShape.id;\n }\n }\n return undefined;\n}\n\nconst addAll = (target: Set<string>, values: Iterable<string>): void => {\n for (const v of values) target.add(v);\n};\n\n// ---------------------------------------------------------------------------\n// queryDependencies\n// ---------------------------------------------------------------------------\n\ntype TraverseInfo = {\n pattern: IRTraversePattern;\n /** Sits under a `minus` / `exists` pattern: its predicates decide membership. */\n inFilter: boolean;\n};\n\nclass DependencyCollector {\n readonly deps: QueryDependencies = {\n narrow: new Set(),\n filter: new Set(),\n hidden: new Set(),\n shapes: new Set(),\n unbound: true,\n };\n /** Every traverse, keyed by the alias it binds. */\n private readonly traverseByTo = new Map<string, TraverseInfo>();\n /** Traverses whose predicates were attributed to a set by a projection or a filter. */\n private readonly reached = new Set<IRTraversePattern>();\n /** Filter expressions found while walking patterns, with the alias their scope starts at. */\n private readonly scopedFilters: {expr: IRExpression; scope?: string}[] = [];\n\n // --- pass 1: patterns -------------------------------------------------------\n\n walkPattern(pattern: IRGraphPattern, inFilter: boolean): void {\n switch (pattern.kind) {\n case 'shape_scan':\n this.deps.shapes.add(pattern.shape);\n return;\n case 'traverse': {\n this.traverseByTo.set(pattern.to, {pattern, inFilter});\n this.recordShapesOf(pattern.property);\n if (pattern.filter) {\n // A scoped where (`t.members.where(m => …)`) decides which related rows\n // take part; its chain stops at the traversal it scopes, whose own\n // predicate is attributed wherever that traversal lands.\n this.scopedFilters.push({expr: pattern.filter, scope: pattern.to});\n }\n for (const order of pattern.innerOrderBy ?? []) {\n addAll(this.deps.filter, predicatesOf(order.property));\n }\n return;\n }\n case 'join':\n for (const p of pattern.patterns) this.walkPattern(p, inFilter);\n return;\n case 'optional':\n this.walkPattern(pattern.pattern, inFilter);\n return;\n case 'union':\n for (const p of pattern.branches) this.walkPattern(p, inFilter);\n return;\n case 'exists':\n this.walkPattern(pattern.pattern, true);\n return;\n case 'minus':\n this.walkPattern(pattern.pattern, true);\n if (pattern.filter) this.scopedFilters.push({expr: pattern.filter});\n return;\n }\n }\n\n // --- pass 2: attribution -----------------------------------------------------\n\n /**\n * The predicates of every traversal from `alias` back towards the root, stopping\n * before `stopAt` when given. Marks each traversal as reached.\n */\n chain(alias: string, stopAt?: string): string[] {\n const preds: string[] = [];\n const visited = new Set<string>();\n let current = alias;\n while (current !== stopAt && !visited.has(current)) {\n visited.add(current);\n const info = this.traverseByTo.get(current);\n if (!info) break;\n this.reached.add(info.pattern);\n preds.push(...predicatesOf(info.pattern.property, info.pattern.pathExpr));\n current = info.pattern.from;\n }\n return preds;\n }\n\n /** The shapes a property read pins the query to: its declaring shape and its value shape. */\n private recordShapesOf(propertyShapeId: string): void {\n const declaring = declaringShapeOf(propertyShapeId);\n if (declaring) this.deps.shapes.add(declaring);\n const valueShape = valueShapeOf(propertyShapeId);\n if (valueShape) this.deps.shapes.add(valueShape);\n }\n\n private propertyExprPredicates(expr: IRPropertyExpression, scope?: string): string[] {\n this.recordShapesOf(expr.property);\n return [...predicatesOf(expr.property, expr.pathExpr), ...this.chain(expr.sourceAlias, scope)];\n }\n\n /** Every predicate an expression decides membership or order by. */\n collectFilter(expr: IRExpression, scope?: string): void {\n switch (expr.kind) {\n case 'property_expr':\n addAll(this.deps.filter, this.propertyExprPredicates(expr, scope));\n return;\n case 'context_property_expr':\n addAll(this.deps.filter, predicatesOf(expr.property));\n return;\n case 'alias_expr':\n addAll(this.deps.filter, this.chain(expr.alias, scope));\n return;\n case 'exists_expr':\n this.walkPattern(expr.pattern, true);\n if (expr.filter) this.collectFilter(expr.filter, scope);\n return;\n default:\n for (const child of childExpressions(expr)) this.collectFilter(child, scope);\n }\n }\n\n /** A projected expression: plain paths are narrow, computed values hidden. */\n collectProjection(expr: IRExpression): void {\n switch (expr.kind) {\n case 'property_expr':\n addAll(this.deps.narrow, this.propertyExprPredicates(expr));\n return;\n case 'alias_expr':\n addAll(this.deps.narrow, this.chain(expr.alias));\n return;\n default:\n this.collectHidden(expr);\n }\n }\n\n private collectHidden(expr: IRExpression): void {\n switch (expr.kind) {\n case 'property_expr':\n addAll(this.deps.hidden, this.propertyExprPredicates(expr));\n return;\n case 'alias_expr':\n addAll(this.deps.hidden, this.chain(expr.alias));\n return;\n case 'context_property_expr':\n addAll(this.deps.filter, predicatesOf(expr.property));\n return;\n case 'exists_expr':\n this.collectFilter(expr);\n return;\n default:\n for (const child of childExpressions(expr)) this.collectHidden(child);\n }\n }\n\n /** Filters gathered while walking patterns (scoped where, minus filter). */\n flushScopedFilters(): void {\n // `collectFilter` on an exists expression may walk new patterns and push new\n // scoped filters, so drain until stable.\n while (this.scopedFilters.length) {\n const {expr, scope} = this.scopedFilters.shift()!;\n this.collectFilter(expr, scope);\n }\n }\n\n /** Traversals no projection or filter attributed: read, but their ids never surface. */\n attributeUnreached(): void {\n for (const {pattern, inFilter} of this.traverseByTo.values()) {\n if (this.reached.has(pattern)) continue;\n const target = inFilter ? this.deps.filter : this.deps.hidden;\n addAll(target, predicatesOf(pattern.property, pattern.pathExpr));\n // The traversal leading up to it is read on the same terms.\n addAll(target, this.chain(pattern.from));\n }\n }\n}\n\n/** The immediate sub-expressions of a compound expression (none for leaves). */\nfunction childExpressions(expr: IRExpression): IRExpression[] {\n switch (expr.kind) {\n case 'binary_expr':\n return [expr.left, expr.right];\n case 'logical_expr':\n return expr.expressions;\n case 'not_expr':\n return [expr.expression];\n case 'function_expr':\n case 'aggregate_expr':\n return expr.args;\n case 'in_expr':\n return [expr.value, ...expr.source.list];\n default:\n return [];\n }\n}\n\n/** `lower()` is overloaded per builder kind; dispatch on the discriminator. */\nfunction lowerQuery(\n query: LowerableSelect | LowerableCount | LowerableAsk,\n): IRSelectQuery | IRCountQuery | IRAskQuery {\n switch (query.__queryKind) {\n case 'select':\n return lower(query);\n case 'count':\n return lower(query);\n case 'ask':\n return lower(query);\n }\n}\n\nfunction isBound(ir: {subjectId?: string; subjectIds?: string[]}): boolean {\n return !!(ir.subjectId || (ir.subjectIds && ir.subjectIds.length));\n}\n\n/**\n * What a query reads, as predicate IRIs and shape IRIs.\n *\n * - `narrow`: read on nodes whose ids are in the result (root rows and projected\n * relation rows) — a change to one of these on a visible node changes the result.\n * - `filter`: decide which nodes are in the result and in what order.\n * - `hidden`: read on nodes the result does not identify (counted relations,\n * computed values over traversals), so a change there can only be matched by\n * predicate, not by id.\n * - `shapes`: every shape whose instance set the query scans or traverses into.\n * - `unbound`: `true` unless the query is pinned to known subjects.\n */\nexport function queryDependencies(\n query: LowerableSelect | LowerableCount | LowerableAsk,\n): QueryDependencies {\n const ir = lowerQuery(query);\n const collector = new DependencyCollector();\n const deps = collector.deps;\n\n if (!ir.root) {\n // Shapeless ask: no rdf:type constraint, no properties — nothing is read but\n // the subject itself.\n deps.unbound = !ir.subjectId;\n return deps;\n }\n\n collector.walkPattern(ir.root, false);\n for (const pattern of ir.patterns) collector.walkPattern(pattern, false);\n\n if (ir.kind === 'select') {\n for (const item of ir.projection) collector.collectProjection(item.expression);\n }\n if (ir.where) collector.collectFilter(ir.where);\n if (ir.kind === 'select') {\n for (const order of ir.orderBy ?? []) collector.collectFilter(order.expression);\n }\n collector.flushScopedFilters();\n collector.attributeUnreached();\n\n deps.unbound = !isBound(ir);\n return deps;\n}\n\n// ---------------------------------------------------------------------------\n// mutationEffects\n// ---------------------------------------------------------------------------\n\nconst isNodeData = (value: unknown): value is IRNodeData =>\n !!value && typeof value === 'object' && 'fields' in value && 'shape' in value;\n\nconst isSetModification = (value: unknown): value is {add?: IRFieldValue[]; remove?: {id: string}[]} =>\n !!value && typeof value === 'object' && ('add' in value || 'remove' in value) && !('kind' in value);\n\nclass EffectCollector {\n readonly props = new Set<string>();\n readonly ids = new Set<string>();\n readonly membership = new Set<string>();\n\n /** Every field at every depth writes its predicate; nested nodes may be created. */\n walkNodeData(data: IRNodeData, nested: boolean): void {\n if (nested) {\n this.membership.add(data.shape);\n if (data.id) this.ids.add(data.id);\n } else if (data.id) {\n this.ids.add(data.id);\n }\n for (const field of data.fields) {\n addAll(this.props, predicatesOf(field.property));\n this.walkValue(field.value);\n }\n }\n\n private walkValue(value: IRFieldValue): void {\n if (value === null || value === undefined) return;\n if (Array.isArray(value)) {\n for (const item of value) this.walkValue(item);\n return;\n }\n if (typeof value !== 'object' || value instanceof Date) return;\n if (isNodeData(value)) {\n this.walkNodeData(value, true);\n return;\n }\n if (isSetModification(value)) {\n for (const item of value.add ?? []) this.walkValue(item);\n for (const ref of value.remove ?? []) {\n if (ref?.id) this.ids.add(ref.id);\n }\n return;\n }\n // A `{id}` reference or an expression: neither writes a predicate of its own,\n // and a referenced node is not itself modified.\n }\n\n /** Every string under a key named `id`, anywhere in a store result. */\n walkResult(value: unknown, seen = new Set<object>()): void {\n if (!value || typeof value !== 'object' || value instanceof Date) return;\n if (seen.has(value)) return;\n seen.add(value);\n if (Array.isArray(value)) {\n for (const item of value) this.walkResult(item, seen);\n return;\n }\n for (const [key, child] of Object.entries(value as Record<string, unknown>)) {\n if (key === 'id' && typeof child === 'string') this.ids.add(child);\n else this.walkResult(child, seen);\n }\n }\n}\n\n/** `lower()` is overloaded per builder kind; dispatch on the discriminator. */\nfunction lowerMutation(\n mutation: LowerableCreate | LowerableUpdate | LowerableDelete,\n): IRCreateQuery | IRUpdateQuery | IRDeleteQuery {\n switch (mutation.__queryKind) {\n case 'create':\n return lower(mutation);\n case 'update':\n return lower(mutation);\n case 'delete':\n return lower(mutation);\n }\n}\n\n/** The shape metadata for a shape IRI, class-backed or data-only. */\nfunction resolveShapeData(shapeId: string): NodeShapeData | undefined {\n return getShapeClass(shapeId)?.shape ?? getNodeShape(shapeId);\n}\n\n/**\n * What a mutation writes, as predicate IRIs, affected node ids and the shapes\n * whose instance sets may have changed.\n *\n * `result` — what the store returned — is optional and only widens `ids`: a create\n * learns its generated id from it, and an add/remove learns the ids of nested\n * creates. It is never consulted for an op whose target set is unknown\n * (`update_where`, `delete_all`, `delete_where`), where `ids` stays `undefined`.\n */\nexport function mutationEffects(\n mutation: LowerableCreate | LowerableUpdate | LowerableDelete,\n result?: unknown,\n): MutationEffects {\n const ir = lowerMutation(mutation);\n const collector = new EffectCollector();\n\n switch (ir.kind) {\n case 'create': {\n collector.membership.add(ir.shape);\n collector.walkNodeData(ir.data, false);\n collector.walkResult(result);\n return {\n op: 'create',\n shape: ir.shape,\n props: collector.props,\n ids: collector.ids,\n membership: collector.membership,\n };\n }\n case 'update':\n case 'upsert': {\n if (ir.kind === 'upsert') collector.membership.add(ir.shape);\n collector.ids.add(ir.id);\n collector.walkNodeData(ir.data, false);\n collector.walkResult(result);\n return {\n op: ir.kind,\n shape: ir.shape,\n props: collector.props,\n ids: collector.ids,\n membership: collector.membership,\n };\n }\n case 'update_where': {\n collector.walkNodeData(ir.data, false);\n return {\n op: 'update',\n shape: ir.shape,\n props: collector.props,\n ids: undefined,\n membership: collector.membership,\n };\n }\n case 'delete':\n case 'delete_all':\n case 'delete_where': {\n collector.membership.add(ir.shape);\n const shapeData = resolveShapeData(ir.shape);\n if (shapeData) {\n for (const ps of getPropertyShapes(shapeData, true)) {\n addAll(collector.props, predicatesOf(ps.id));\n }\n }\n if (ir.kind === 'delete') {\n for (const ref of ir.ids) {\n if (ref?.id) collector.ids.add(ref.id);\n }\n collector.walkResult(result);\n }\n return {\n op: 'delete',\n shape: ir.shape,\n props: collector.props,\n ids: ir.kind === 'delete' ? collector.ids : undefined,\n membership: collector.membership,\n };\n }\n }\n}\n"]}
|
|
@@ -86,6 +86,52 @@ type CountTarget = {
|
|
|
86
86
|
* count and answered with rows is caught by the same check.
|
|
87
87
|
*/
|
|
88
88
|
export declare function resolveCount(target: CountTarget, query: CountQuery): Promise<number>;
|
|
89
|
+
/**
|
|
90
|
+
* One query passing through the dispatch, reported to {@link subscribeQueryDispatch}
|
|
91
|
+
* listeners. `query` is the very builder handed to the store (same identity), and
|
|
92
|
+
* `result` is the store's answer as a promise — not yet settled when the event fires.
|
|
93
|
+
*/
|
|
94
|
+
export type QueryDispatchEvent = ({
|
|
95
|
+
kind: 'select';
|
|
96
|
+
query: SelectQuery | CountQuery;
|
|
97
|
+
result: Promise<unknown>;
|
|
98
|
+
} | {
|
|
99
|
+
kind: 'ask';
|
|
100
|
+
query: AskQuery;
|
|
101
|
+
result: Promise<boolean>;
|
|
102
|
+
} | {
|
|
103
|
+
kind: 'create';
|
|
104
|
+
query: CreateQuery;
|
|
105
|
+
result: Promise<unknown>;
|
|
106
|
+
} | {
|
|
107
|
+
kind: 'update';
|
|
108
|
+
query: UpdateQuery;
|
|
109
|
+
result: Promise<unknown>;
|
|
110
|
+
} | {
|
|
111
|
+
kind: 'delete';
|
|
112
|
+
query: DeleteQuery;
|
|
113
|
+
result: Promise<DeleteResponse>;
|
|
114
|
+
}) & {
|
|
115
|
+
/** The explicit dataset of an `exec(target)` call; absent for the global dispatch. */
|
|
116
|
+
target?: IDataset;
|
|
117
|
+
};
|
|
118
|
+
/** Listener notified for every query that runs through the dispatch. */
|
|
119
|
+
export type QueryDispatchListener = (event: QueryDispatchEvent) => void;
|
|
120
|
+
/**
|
|
121
|
+
* Observe every query that runs through the dispatch. Returns an unsubscribe function.
|
|
122
|
+
*
|
|
123
|
+
* Listeners are notified **synchronously at call time**, as soon as the store has
|
|
124
|
+
* been handed the query, with the result promise attached — so an observer can both
|
|
125
|
+
* see what was asked and await what came back. Listeners never alter the result:
|
|
126
|
+
* the promise the caller receives is the store's own, whatever any listener does
|
|
127
|
+
* with it. A listener that throws is logged (`console.error`) and isolated — it
|
|
128
|
+
* neither rejects the query nor stops later listeners.
|
|
129
|
+
*
|
|
130
|
+
* The listener set lives on `globalThis`, beside `current`, for the same reason the
|
|
131
|
+
* dispatch itself does: core can be evaluated twice in dev, and a subscription made
|
|
132
|
+
* through one copy must see queries dispatched through the other.
|
|
133
|
+
*/
|
|
134
|
+
export declare function subscribeQueryDispatch(listener: QueryDispatchListener): () => void;
|
|
89
135
|
export declare function setQueryDispatch(d: QueryDispatch): void;
|
|
90
136
|
export declare function getQueryDispatch(): QueryDispatch;
|
|
91
137
|
/** The mutating query kinds — the `IDataset` methods that are optional per-store. */
|