@astrale-os/kernel-client 0.6.0-beta.15 → 0.6.0-beta.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/graph/provenance.d.ts +2 -0
- package/dist/graph/provenance.js +5 -0
- package/dist/store/memory.d.ts +2 -1
- package/dist/store/memory.js +24 -12
- package/dist/store/query-key.d.ts +2 -1
- package/dist/store/query-key.js +7 -2
- package/dist/store/shape.d.ts +3 -1
- package/dist/store/shape.js +3 -1
- package/dist/store/store.js +5 -3
- package/package.json +1 -1
|
@@ -8,5 +8,7 @@ export interface QueryProvenance {
|
|
|
8
8
|
}
|
|
9
9
|
/** Retain authenticated query provenance without adding serialized response metadata. */
|
|
10
10
|
export declare function retainQueryProvenance(response: QueryResponse, closure: readonly DomainDependency[]): QueryResponse;
|
|
11
|
+
/** Reattach the exact authenticated closure when Store materialization replaces response objects. */
|
|
12
|
+
export declare function retainDerivedQueryProvenance(source: QueryResponse, derived: QueryResponse): QueryResponse;
|
|
11
13
|
export declare function provenanceOfGraphValue(value: Node | Edge): QueryProvenance | undefined;
|
|
12
14
|
export declare function provenanceOfResponse(value: QueryResponse): QueryProvenance | undefined;
|
package/dist/graph/provenance.js
CHANGED
|
@@ -29,6 +29,11 @@ export function retainQueryProvenance(response, closure) {
|
|
|
29
29
|
}
|
|
30
30
|
return response;
|
|
31
31
|
}
|
|
32
|
+
/** Reattach the exact authenticated closure when Store materialization replaces response objects. */
|
|
33
|
+
export function retainDerivedQueryProvenance(source, derived) {
|
|
34
|
+
const provenance = responses.get(source);
|
|
35
|
+
return provenance === undefined ? derived : retainQueryProvenance(derived, provenance.closure);
|
|
36
|
+
}
|
|
32
37
|
export function provenanceOfGraphValue(value) {
|
|
33
38
|
return graphValues.get(value);
|
|
34
39
|
}
|
package/dist/store/memory.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { QueryAST } from '@astrale-os/kernel-core/graph/query';
|
|
2
|
+
import type { DomainDependency } from '@astrale-os/kernel-dsl/v1/schema';
|
|
2
3
|
import type { QueryPageRequest } from '../graph/index.js';
|
|
3
4
|
import type { GraphApi } from '../graph/index.js';
|
|
4
5
|
import type { StoreQueryResponse, StoreReadOptions, StoreSnapshot } from './store.js';
|
|
@@ -24,7 +25,7 @@ export declare class GraphMemory {
|
|
|
24
25
|
private readonly pending;
|
|
25
26
|
private active;
|
|
26
27
|
constructor(options: GraphMemoryOptions);
|
|
27
|
-
ensure(key: string, ast: QueryAST, page: QueryPageRequest): QueryShape;
|
|
28
|
+
ensure(key: string, ast: QueryAST, page: QueryPageRequest, expected: readonly DomainDependency[] | undefined): QueryShape;
|
|
28
29
|
get(key: string): QueryShape | undefined;
|
|
29
30
|
read(shape: QueryShape, options: StoreReadOptions): Promise<StoreQueryResponse>;
|
|
30
31
|
snapshot(shape: QueryShape | undefined): StoreSnapshot;
|
package/dist/store/memory.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { encodeEdgeKey } from '@astrale-os/kernel-core/graph/edge';
|
|
2
2
|
import { ClientError } from '../errors/index.js';
|
|
3
|
+
import { retainDerivedQueryProvenance } from '../graph/provenance.js';
|
|
3
4
|
import { edgeIdentity, EntityIndex } from './entity-index.js';
|
|
4
5
|
import { OptimisticOverlay } from './optimistic.js';
|
|
5
6
|
import { COLD_SNAPSHOT, QueryShape } from './shape.js';
|
|
@@ -13,7 +14,7 @@ export class GraphMemory {
|
|
|
13
14
|
constructor(options) {
|
|
14
15
|
this.options = options;
|
|
15
16
|
}
|
|
16
|
-
ensure(key, ast, page) {
|
|
17
|
+
ensure(key, ast, page, expected) {
|
|
17
18
|
const existing = this.shapes.get(key);
|
|
18
19
|
const now = this.options.now();
|
|
19
20
|
if (existing !== undefined) {
|
|
@@ -26,7 +27,7 @@ export class GraphMemory {
|
|
|
26
27
|
if (this.shapes.size >= this.options.maximumShapes) {
|
|
27
28
|
throw new ClientError('GraphStore shape capacity is exhausted by observed or in-flight queries.');
|
|
28
29
|
}
|
|
29
|
-
const shape = new QueryShape(key, ast, page, now);
|
|
30
|
+
const shape = new QueryShape(key, ast, page, expected, now);
|
|
30
31
|
shape.releasedAt = now;
|
|
31
32
|
this.shapes.set(key, shape);
|
|
32
33
|
return shape;
|
|
@@ -52,7 +53,7 @@ export class GraphMemory {
|
|
|
52
53
|
const prior = shape.response;
|
|
53
54
|
this.publish(shape, prior === undefined ? 'loading' : this.overlay.hasAny() ? 'optimistic' : 'refreshing');
|
|
54
55
|
const inflight = this.schedule(async () => {
|
|
55
|
-
const result = await this.options.graph.query(shape.ast, queryOptions(shape.page, this.options.lifecycle));
|
|
56
|
+
const result = await this.options.graph.query(shape.ast, queryOptions(shape.page, this.options.lifecycle, shape.expected));
|
|
56
57
|
this.options.requireOpen();
|
|
57
58
|
const integration = this.entities.integrate(result.result);
|
|
58
59
|
shape.references = integration.references;
|
|
@@ -182,10 +183,11 @@ export class GraphMemory {
|
|
|
182
183
|
this.pending.shift()?.reject(error);
|
|
183
184
|
}
|
|
184
185
|
materialize(template, references) {
|
|
185
|
-
|
|
186
|
+
const response = Object.freeze({
|
|
186
187
|
result: this.materializeResult(template.result, references),
|
|
187
188
|
page: template.page,
|
|
188
189
|
});
|
|
190
|
+
return retainDerivedQueryProvenance(template, response);
|
|
189
191
|
}
|
|
190
192
|
materializeResult(template, references) {
|
|
191
193
|
if (template.kind === 'nodes') {
|
|
@@ -196,8 +198,9 @@ export class GraphMemory {
|
|
|
196
198
|
continue;
|
|
197
199
|
}
|
|
198
200
|
const node = this.overlay.node(projection.value.id, this.entities);
|
|
199
|
-
if (node !== undefined)
|
|
200
|
-
nodes.push(Object.freeze({ kind: 'value', value: node }));
|
|
201
|
+
if (node !== undefined) {
|
|
202
|
+
nodes.push(Object.freeze({ kind: 'value', value: materializeNode(node) }));
|
|
203
|
+
}
|
|
201
204
|
}
|
|
202
205
|
return Object.freeze({
|
|
203
206
|
kind: 'nodes',
|
|
@@ -213,7 +216,7 @@ export class GraphMemory {
|
|
|
213
216
|
const value = this.entities.edge(edgeIdentity(item.edge.value));
|
|
214
217
|
return value === undefined
|
|
215
218
|
? undefined
|
|
216
|
-
: Object.freeze({ kind: 'value', value });
|
|
219
|
+
: Object.freeze({ kind: 'value', value: materializeEdge(value) });
|
|
217
220
|
})();
|
|
218
221
|
const source = item.source.kind === 'reference'
|
|
219
222
|
? item.source
|
|
@@ -221,7 +224,7 @@ export class GraphMemory {
|
|
|
221
224
|
const value = this.overlay.node(item.source.value.id, this.entities);
|
|
222
225
|
return value === undefined
|
|
223
226
|
? undefined
|
|
224
|
-
: Object.freeze({ kind: 'value', value });
|
|
227
|
+
: Object.freeze({ kind: 'value', value: materializeNode(value) });
|
|
225
228
|
})();
|
|
226
229
|
const target = item.target.kind === 'reference'
|
|
227
230
|
? item.target
|
|
@@ -229,7 +232,7 @@ export class GraphMemory {
|
|
|
229
232
|
const value = this.overlay.node(item.target.value.id, this.entities);
|
|
230
233
|
return value === undefined
|
|
231
234
|
? undefined
|
|
232
|
-
: Object.freeze({ kind: 'value', value });
|
|
235
|
+
: Object.freeze({ kind: 'value', value: materializeNode(value) });
|
|
233
236
|
})();
|
|
234
237
|
if (edge !== undefined && source !== undefined && target !== undefined) {
|
|
235
238
|
edges.push(Object.freeze({ edge, source, target }));
|
|
@@ -242,11 +245,13 @@ export class GraphMemory {
|
|
|
242
245
|
}
|
|
243
246
|
const nodes = references.nodes
|
|
244
247
|
.map((id) => this.overlay.node(id, this.entities))
|
|
245
|
-
.filter((node) => node !== undefined)
|
|
248
|
+
.filter((node) => node !== undefined)
|
|
249
|
+
.map(materializeNode);
|
|
246
250
|
const visibleNodeIds = new Set(nodes.map(({ id }) => id));
|
|
247
251
|
const edges = references.edges
|
|
248
252
|
.map((key) => this.entities.edge(key))
|
|
249
|
-
.filter((edge) => edge !== undefined && visibleNodeIds.has(edge.source) && visibleNodeIds.has(edge.target))
|
|
253
|
+
.filter((edge) => edge !== undefined && visibleNodeIds.has(edge.source) && visibleNodeIds.has(edge.target))
|
|
254
|
+
.map(materializeEdge);
|
|
250
255
|
const graph = Object.freeze({ nodes: Object.freeze(nodes), edges: Object.freeze(edges) });
|
|
251
256
|
const selection = materializeSelection(template.selection, visibleNodeIds, edges);
|
|
252
257
|
return selection.kind === 'node'
|
|
@@ -297,6 +302,12 @@ export class GraphMemory {
|
|
|
297
302
|
}
|
|
298
303
|
}
|
|
299
304
|
}
|
|
305
|
+
function materializeNode(value) {
|
|
306
|
+
return Object.freeze({ ...value });
|
|
307
|
+
}
|
|
308
|
+
function materializeEdge(value) {
|
|
309
|
+
return Object.freeze({ ...value });
|
|
310
|
+
}
|
|
300
311
|
function materializeSelection(selection, visibleNodes, visibleEdges) {
|
|
301
312
|
if (selection.kind === 'node') {
|
|
302
313
|
return Object.freeze({
|
|
@@ -310,10 +321,11 @@ function materializeSelection(selection, visibleNodes, visibleEdges) {
|
|
|
310
321
|
keys: Object.freeze(selection.keys.filter((key) => edges.has(encodeEdgeKey(key)))),
|
|
311
322
|
});
|
|
312
323
|
}
|
|
313
|
-
function queryOptions(page, lifecycle) {
|
|
324
|
+
function queryOptions(page, lifecycle, expected) {
|
|
314
325
|
return Object.freeze({
|
|
315
326
|
signal: lifecycle,
|
|
316
327
|
page,
|
|
328
|
+
...(expected === undefined ? {} : { expected }),
|
|
317
329
|
});
|
|
318
330
|
}
|
|
319
331
|
function observeRead(pending, options, lifecycle) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { QueryAST as QueryASTValue } from '@astrale-os/kernel-core/graph/query';
|
|
2
|
+
import type { DomainDependency } from '@astrale-os/kernel-dsl/v1/schema';
|
|
2
3
|
import type { QueryPageRequest } from '../graph/index.js';
|
|
3
4
|
export declare function admitQuery(input: QueryASTValue): QueryASTValue;
|
|
4
5
|
export declare function admitPage(input: QueryPageRequest): QueryPageRequest;
|
|
5
|
-
export declare function queryKey(ast: QueryASTValue, page: QueryPageRequest): string;
|
|
6
|
+
export declare function queryKey(ast: QueryASTValue, page: QueryPageRequest, expected: readonly DomainDependency[] | undefined): string;
|
package/dist/store/query-key.js
CHANGED
|
@@ -17,6 +17,11 @@ export function admitPage(input) {
|
|
|
17
17
|
...(input.after === undefined ? {} : { after: input.after }),
|
|
18
18
|
});
|
|
19
19
|
}
|
|
20
|
-
export function queryKey(ast, page) {
|
|
21
|
-
return JSON.stringify([
|
|
20
|
+
export function queryKey(ast, page, expected) {
|
|
21
|
+
return JSON.stringify([
|
|
22
|
+
QueryAST.fingerprint(ast),
|
|
23
|
+
page.size,
|
|
24
|
+
page.after ?? null,
|
|
25
|
+
expected ?? null,
|
|
26
|
+
]);
|
|
22
27
|
}
|
package/dist/store/shape.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { NodeId } from '@astrale-os/kernel-core/graph/node';
|
|
2
2
|
import type { QueryAST } from '@astrale-os/kernel-core/graph/query';
|
|
3
|
+
import type { DomainDependency } from '@astrale-os/kernel-dsl/v1/schema';
|
|
3
4
|
import type { QueryPageRequest } from '../graph/index.js';
|
|
4
5
|
import type { FactReferences } from './entity-index.js';
|
|
5
6
|
import type { StoreQueryResponse, StoreSnapshot } from './store.js';
|
|
@@ -8,6 +9,7 @@ export declare class QueryShape {
|
|
|
8
9
|
readonly key: string;
|
|
9
10
|
readonly ast: QueryAST;
|
|
10
11
|
readonly page: QueryPageRequest;
|
|
12
|
+
readonly expected: readonly DomainDependency[] | undefined;
|
|
11
13
|
readonly listeners: Set<() => void>;
|
|
12
14
|
state: ShapeState;
|
|
13
15
|
confirmed?: StoreQueryResponse;
|
|
@@ -18,7 +20,7 @@ export declare class QueryShape {
|
|
|
18
20
|
accessedAt: number;
|
|
19
21
|
refreshedAt?: number;
|
|
20
22
|
snapshot: StoreSnapshot;
|
|
21
|
-
constructor(key: string, ast: QueryAST, page: QueryPageRequest, now: number);
|
|
23
|
+
constructor(key: string, ast: QueryAST, page: QueryPageRequest, expected: readonly DomainDependency[] | undefined, now: number);
|
|
22
24
|
get observed(): boolean;
|
|
23
25
|
referencesAny(nodes: ReadonlySet<NodeId>, edges: ReadonlySet<string>): boolean;
|
|
24
26
|
}
|
package/dist/store/shape.js
CHANGED
|
@@ -2,6 +2,7 @@ export class QueryShape {
|
|
|
2
2
|
key;
|
|
3
3
|
ast;
|
|
4
4
|
page;
|
|
5
|
+
expected;
|
|
5
6
|
listeners = new Set();
|
|
6
7
|
state = 'cold';
|
|
7
8
|
confirmed;
|
|
@@ -12,10 +13,11 @@ export class QueryShape {
|
|
|
12
13
|
accessedAt;
|
|
13
14
|
refreshedAt;
|
|
14
15
|
snapshot = COLD_SNAPSHOT;
|
|
15
|
-
constructor(key, ast, page, now) {
|
|
16
|
+
constructor(key, ast, page, expected, now) {
|
|
16
17
|
this.key = key;
|
|
17
18
|
this.ast = ast;
|
|
18
19
|
this.page = page;
|
|
20
|
+
this.expected = expected;
|
|
19
21
|
this.accessedAt = now;
|
|
20
22
|
}
|
|
21
23
|
get observed() {
|
package/dist/store/store.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ClientError } from '../errors/index.js';
|
|
2
|
+
import { captureExpected } from '../graph/expectation.js';
|
|
2
3
|
import { GraphMemory } from './memory.js';
|
|
3
4
|
import { mutateStore } from './mutation.js';
|
|
4
5
|
import { observeStore } from './observe.js';
|
|
@@ -105,7 +106,7 @@ export class GraphStore {
|
|
|
105
106
|
const identity = this.identity(query, options);
|
|
106
107
|
return {
|
|
107
108
|
...identity,
|
|
108
|
-
shape: this.memory.ensure(identity.key, identity.ast, identity.page),
|
|
109
|
+
shape: this.memory.ensure(identity.key, identity.ast, identity.page, identity.expected),
|
|
109
110
|
options: options,
|
|
110
111
|
};
|
|
111
112
|
}
|
|
@@ -121,6 +122,7 @@ export class GraphStore {
|
|
|
121
122
|
throw new TypeError('Store freshness option is invalid.');
|
|
122
123
|
}
|
|
123
124
|
const page = admitPage(options.page);
|
|
125
|
+
const expected = captureExpected(options.expected);
|
|
124
126
|
if (options.timeoutMs !== undefined &&
|
|
125
127
|
(!Number.isSafeInteger(options.timeoutMs) ||
|
|
126
128
|
options.timeoutMs < 1 ||
|
|
@@ -128,8 +130,8 @@ export class GraphStore {
|
|
|
128
130
|
throw new TypeError('Store read timeoutMs must be a positive platform integer.');
|
|
129
131
|
}
|
|
130
132
|
const ast = admitQuery(query);
|
|
131
|
-
const key = queryKey(ast, page);
|
|
132
|
-
return { key, ast, page };
|
|
133
|
+
const key = queryKey(ast, page, expected);
|
|
134
|
+
return { key, ast, page, expected };
|
|
133
135
|
}
|
|
134
136
|
requireOpen() {
|
|
135
137
|
if (this.closed)
|