@_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,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the live-query store registers itself.
|
|
3
|
+
*
|
|
4
|
+
* `globalThis`-backed for the same reason the query dispatch and the routing
|
|
5
|
+
* table are (see `docs/architecture/runtime-instances.md`): a second physical
|
|
6
|
+
* copy of this module must find the same store rather than fork it.
|
|
7
|
+
*/
|
|
8
|
+
export const LIVE_STORE_KEY = '__linkedLiveQueryStore';
|
|
9
|
+
const registryGlobal = typeof globalThis !== 'undefined' ? globalThis : {};
|
|
10
|
+
/**
|
|
11
|
+
* The registered live-query store, or `undefined` when `./LiveQueryStore.js`
|
|
12
|
+
* has not been evaluated yet.
|
|
13
|
+
*
|
|
14
|
+
* The builders reach the store through this tiny module — never by importing
|
|
15
|
+
* `LiveQueryStore.ts` — so a bundle that only builds and forwards queries never
|
|
16
|
+
* pulls in the store, and with it the IR lowering the store needs for its
|
|
17
|
+
* dependency analysis. Importing the package root (`@_linked/core`) or
|
|
18
|
+
* `@_linked/core/live` registers the store.
|
|
19
|
+
*/
|
|
20
|
+
export function peekLiveQueryStore() {
|
|
21
|
+
return registryGlobal[LIVE_STORE_KEY];
|
|
22
|
+
}
|
|
23
|
+
/** The registered store; throws when live queries have not been loaded. */
|
|
24
|
+
export function requireLiveQueryStore() {
|
|
25
|
+
const store = peekLiveQueryStore();
|
|
26
|
+
if (!store) {
|
|
27
|
+
throw new Error("Live queries are not loaded. Import '@_linked/core' (the package root) or " +
|
|
28
|
+
"'@_linked/core/live' once before calling .live() on a query.");
|
|
29
|
+
}
|
|
30
|
+
return store;
|
|
31
|
+
}
|
|
32
|
+
/** @internal Register (or replace) the store — called by `LiveQueryStore.ts` on load and by `resetLiveQueryStore()`. */
|
|
33
|
+
export function setLiveQueryStore(store) {
|
|
34
|
+
if (store === undefined) {
|
|
35
|
+
delete registryGlobal[LIVE_STORE_KEY];
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
registryGlobal[LIVE_STORE_KEY] = store;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/live/registry.ts"],"names":[],"mappings":"AAOA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,wBAAwB,CAAC;AAEvD,MAAM,cAAc,GAClB,OAAO,UAAU,KAAK,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAE,EAAU,CAAC;AAE/D;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB;IAChC,OAAO,cAAc,CAAC,cAAc,CAA+B,CAAC;AACtE,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,qBAAqB;IACnC,MAAM,KAAK,GAAG,kBAAkB,EAAE,CAAC;IACnC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,4EAA4E;YAC1E,8DAA8D,CACjE,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,wHAAwH;AACxH,MAAM,UAAU,iBAAiB,CAAC,KAAiC;IACjE,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,cAAc,CAAC,cAAc,CAAC,CAAC;IACxC,CAAC;SAAM,CAAC;QACN,cAAc,CAAC,cAAc,CAAC,GAAG,KAAK,CAAC;IACzC,CAAC;AACH,CAAC","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 */\nimport type {LiveQueryStore} from './LiveQueryStore.js';\n\n/**\n * Where the live-query store registers itself.\n *\n * `globalThis`-backed for the same reason the query dispatch and the routing\n * table are (see `docs/architecture/runtime-instances.md`): a second physical\n * copy of this module must find the same store rather than fork it.\n */\nexport const LIVE_STORE_KEY = '__linkedLiveQueryStore';\n\nconst registryGlobal: any =\n typeof globalThis !== 'undefined' ? globalThis : ({} as any);\n\n/**\n * The registered live-query store, or `undefined` when `./LiveQueryStore.js`\n * has not been evaluated yet.\n *\n * The builders reach the store through this tiny module — never by importing\n * `LiveQueryStore.ts` — so a bundle that only builds and forwards queries never\n * pulls in the store, and with it the IR lowering the store needs for its\n * dependency analysis. Importing the package root (`@_linked/core`) or\n * `@_linked/core/live` registers the store.\n */\nexport function peekLiveQueryStore(): LiveQueryStore | undefined {\n return registryGlobal[LIVE_STORE_KEY] as LiveQueryStore | undefined;\n}\n\n/** The registered store; throws when live queries have not been loaded. */\nexport function requireLiveQueryStore(): LiveQueryStore {\n const store = peekLiveQueryStore();\n if (!store) {\n throw new Error(\n \"Live queries are not loaded. Import '@_linked/core' (the package root) or \" +\n \"'@_linked/core/live' once before calling .live() on a query.\",\n );\n }\n return store;\n}\n\n/** @internal Register (or replace) the store — called by `LiveQueryStore.ts` on load and by `resetLiveQueryStore()`. */\nexport function setLiveQueryStore(store: LiveQueryStore | undefined): void {\n if (store === undefined) {\n delete registryGlobal[LIVE_STORE_KEY];\n } else {\n registryGlobal[LIVE_STORE_KEY] = store;\n }\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live queries: `@_linked/core/live`.
|
|
3
|
+
*
|
|
4
|
+
* Importing this module registers the live-query store for the runtime, which
|
|
5
|
+
* is what makes `query.live()` work. The package root imports it too, so an app
|
|
6
|
+
* that imports `@_linked/core` needs nothing else.
|
|
7
|
+
*/
|
|
8
|
+
export { LiveQueryStore, getLiveQueryStore, resetLiveQueryStore, publishChange, invalidate, } from './live/LiveQueryStore.js';
|
|
9
|
+
export type { ChangeEvent } from './live/changes.js';
|
|
10
|
+
export type { LiveQuery, LiveState, LiveStatus, LiveListener, LiveQueryOptions, LiveQueryStoreOptions, Template, Instance, } from './live/LiveQueryStore.js';
|
|
11
|
+
export type { InstanceParams, LiveBuilder, LiveQueryKind } from './live/keys.js';
|
|
12
|
+
export { splitQuery, templateKey, paramsKey, stripSubjects } from './live/keys.js';
|
package/lib/esm/live.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
* Live queries: `@_linked/core/live`.
|
|
8
|
+
*
|
|
9
|
+
* Importing this module registers the live-query store for the runtime, which
|
|
10
|
+
* is what makes `query.live()` work. The package root imports it too, so an app
|
|
11
|
+
* that imports `@_linked/core` needs nothing else.
|
|
12
|
+
*/
|
|
13
|
+
export { LiveQueryStore, getLiveQueryStore, resetLiveQueryStore, publishChange, invalidate, } from './live/LiveQueryStore.js';
|
|
14
|
+
export { splitQuery, templateKey, paramsKey, stripSubjects } from './live/keys.js';
|
|
15
|
+
//# sourceMappingURL=live.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live.js","sourceRoot":"","sources":["../../src/live.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH;;;;;;GAMG;AACH,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,mBAAmB,EACnB,aAAa,EACb,UAAU,GACX,MAAM,0BAA0B,CAAC;AAalC,OAAO,EAAC,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAC,MAAM,gBAAgB,CAAC","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 * Live queries: `@_linked/core/live`.\n *\n * Importing this module registers the live-query store for the runtime, which\n * is what makes `query.live()` work. The package root imports it too, so an app\n * that imports `@_linked/core` needs nothing else.\n */\nexport {\n LiveQueryStore,\n getLiveQueryStore,\n resetLiveQueryStore,\n publishChange,\n invalidate,\n} from './live/LiveQueryStore.js';\nexport type {ChangeEvent} from './live/changes.js';\nexport type {\n LiveQuery,\n LiveState,\n LiveStatus,\n LiveListener,\n LiveQueryOptions,\n LiveQueryStoreOptions,\n Template,\n Instance,\n} from './live/LiveQueryStore.js';\nexport type {InstanceParams, LiveBuilder, LiveQueryKind} from './live/keys.js';\nexport {splitQuery, templateKey, paramsKey, stripSubjects} from './live/keys.js';\n"]}
|
|
@@ -14,6 +14,7 @@ import type { NodeReferenceValue } from './QueryFactory.js';
|
|
|
14
14
|
import type { IDataset } from '../interfaces/IDataset.js';
|
|
15
15
|
import { PendingQueryContext } from './QueryContext.js';
|
|
16
16
|
import type { AskQuery, AskQueryJSON, RawAskInput } from './AskQuery.js';
|
|
17
|
+
import type { LiveListener, LiveQuery, LiveQueryOptions } from '../live/LiveQueryStore.js';
|
|
17
18
|
/** Everything an ask can carry. Assembled by `.exists()` or by `Shape.exists()`. */
|
|
18
19
|
export type AskSpec = {
|
|
19
20
|
/** The shape class whose instances are asked about; omitted = shapeless. */
|
|
@@ -52,6 +53,15 @@ export declare class AskBuilder implements PromiseLike<boolean>, Promise<boolean
|
|
|
52
53
|
* no id exist?" has a correct total answer, and it is `false`.
|
|
53
54
|
*/
|
|
54
55
|
exec(target?: IDataset): Promise<boolean>;
|
|
56
|
+
/**
|
|
57
|
+
* Go live: subscribe to this query and keep its result current.
|
|
58
|
+
*
|
|
59
|
+
* Returns a {@link LiveQuery} handle. `await` it for the first result,
|
|
60
|
+
* `subscribe()` for every later one, `close()` when done. A listener may be
|
|
61
|
+
* passed directly as the first argument. Requires the live-query store to be
|
|
62
|
+
* loaded, which importing `@_linked/core` (the package root) does.
|
|
63
|
+
*/
|
|
64
|
+
live<R = boolean>(listenerOrOptions?: LiveListener<R> | LiveQueryOptions, options?: LiveQueryOptions): LiveQuery<R>;
|
|
55
65
|
/** `await` triggers execution. */
|
|
56
66
|
then<TResult1 = boolean, TResult2 = never>(onfulfilled?: ((value: boolean) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
|
57
67
|
catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null): Promise<boolean | TResult>;
|
|
@@ -19,6 +19,7 @@ import { WIRE_VERSION, assertWireVersion } from './wireVersion.js';
|
|
|
19
19
|
import { getQueryDispatch, resolveExistence } from './queryDispatch.js';
|
|
20
20
|
import { resolveUriOrThrow } from '../utils/NodeReference.js';
|
|
21
21
|
import { serializeWherePath, serializeRawMinusEntry, deserializeWherePath, deserializeRawMinusEntry, } from './QueryBuilderSerialization.js';
|
|
22
|
+
import { requireLiveQueryStore } from '../live/registry.js';
|
|
22
23
|
export class AskBuilder {
|
|
23
24
|
constructor(spec) {
|
|
24
25
|
/** Discriminator for the free `lower()` function and dataset routing. */
|
|
@@ -157,6 +158,19 @@ export class AskBuilder {
|
|
|
157
158
|
return resolveExistence(dispatch, this);
|
|
158
159
|
});
|
|
159
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* Go live: subscribe to this query and keep its result current.
|
|
163
|
+
*
|
|
164
|
+
* Returns a {@link LiveQuery} handle. `await` it for the first result,
|
|
165
|
+
* `subscribe()` for every later one, `close()` when done. A listener may be
|
|
166
|
+
* passed directly as the first argument. Requires the live-query store to be
|
|
167
|
+
* loaded, which importing `@_linked/core` (the package root) does.
|
|
168
|
+
*/
|
|
169
|
+
live(listenerOrOptions, options) {
|
|
170
|
+
const listener = typeof listenerOrOptions === 'function' ? listenerOrOptions : undefined;
|
|
171
|
+
const opts = typeof listenerOrOptions === 'function' ? options : listenerOrOptions;
|
|
172
|
+
return requireLiveQueryStore().subscribe(this, listener, opts);
|
|
173
|
+
}
|
|
160
174
|
/** `await` triggers execution. */
|
|
161
175
|
then(onfulfilled, onrejected) {
|
|
162
176
|
return this.exec().then(onfulfilled, onrejected);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AskBuilder.js","sourceRoot":"","sources":["../../../src/queries/AskBuilder.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 * `AskBuilder` — a query whose answer is a boolean.\n *\n * A small sibling of `SelectBuilder` rather than a mode of it. It can only hold a\n * pattern (shape, subject(s), where, minus), so the normalisation an existence\n * check needs is a property of the *type*: there is no projection, sorting or\n * pagination to drop, because none can be set.\n */\nimport {Shape, type ShapeConstructor} from '../shapes/Shape.js';\nimport {resolveShape} from './resolveShape.js';\nimport type {WherePath, WhereClause} from './SelectQuery.js';\nimport {processWhereClause} from './SelectQuery.js';\nimport type {RawMinusEntry} from './IRDesugar.js';\nimport type {NodeShapeData} from '../shapes/SHACL.js';\nimport type {NodeReferenceValue} from './QueryFactory.js';\nimport type {IDataset} from '../interfaces/IDataset.js';\nimport {PendingQueryContext} from './QueryContext.js';\nimport {encodeContextRef, isContextRefJSON} from './ContextRef.js';\nimport {WIRE_VERSION, assertWireVersion} from './wireVersion.js';\nimport {getQueryDispatch, resolveExistence} from './queryDispatch.js';\nimport {resolveUriOrThrow} from '../utils/NodeReference.js';\nimport {\n serializeWherePath,\n serializeRawMinusEntry,\n deserializeWherePath,\n deserializeRawMinusEntry,\n} from './QueryBuilderSerialization.js';\nimport type {AskQuery, AskQueryJSON, RawAskInput} from './AskQuery.js';\n\n/** Everything an ask can carry. Assembled by `.exists()` or by `Shape.exists()`. */\nexport type AskSpec = {\n /** The shape class whose instances are asked about; omitted = shapeless. */\n shapeClass?: ShapeConstructor<any>;\n subject?: NodeReferenceValue | PendingQueryContext;\n subjects?: NodeReferenceValue[];\n where?: WherePath;\n minusEntries?: RawMinusEntry[];\n /** `.for(null)` was called — there is no subject to ask about. */\n nullSubject?: boolean;\n};\n\nexport class AskBuilder implements PromiseLike<boolean>, Promise<boolean> {\n private readonly _spec: AskSpec;\n\n private constructor(spec: AskSpec) {\n this._spec = spec;\n }\n\n /** Build from an assembled spec — used by `.exists()` and `Shape.exists()`. */\n static of(spec: AskSpec): AskBuilder {\n return new AskBuilder(spec);\n }\n\n /**\n * A shapeless ask: does a node with this IRI exist at all, under any type or\n * none? Lowers to `ASK { <iri> ?p ?o }`.\n */\n static forNode(\n id: string | NodeReferenceValue | PendingQueryContext | null | undefined,\n ): AskBuilder {\n if (id === null || id === undefined) {\n return new AskBuilder({nullSubject: true});\n }\n if (id instanceof PendingQueryContext) {\n return new AskBuilder({subject: id});\n }\n const ref =\n typeof id === 'string' ? {id: resolveUriOrThrow(id)} : id;\n return new AskBuilder({subject: ref});\n }\n\n /** Discriminator for the free `lower()` function and dataset routing. */\n readonly __queryKind = 'ask' as const;\n\n /** The shape asked about — `undefined` for a shapeless ask. */\n get shape(): NodeShapeData | undefined {\n return this._spec.shapeClass?.shape;\n }\n\n toRawInput(): RawAskInput {\n const {shapeClass, subject, subjects, where, minusEntries, nullSubject} =\n this._spec;\n const input: RawAskInput = {};\n // Carried so lowering can reject it. `exec()` answers `false` before\n // dispatching, but a store handed the builder directly (e.g. after\n // `fromJSON`) would otherwise lower a subject-less query that matches every\n // instance of the shape and answer `true`.\n if (nullSubject) input.nullSubject = true;\n if (shapeClass) input.shape = shapeClass as any;\n if (subject) input.subject = subject;\n if (subjects && subjects.length > 0) input.subjects = subjects;\n if (where) input.where = where;\n if (minusEntries && minusEntries.length > 0) input.minusEntries = minusEntries;\n return input;\n }\n\n toJSON(): AskQueryJSON {\n const {shapeClass, subject, subjects, where, minusEntries, nullSubject} =\n this._spec;\n const json: AskQueryJSON = {v: WIRE_VERSION, op: 'ask'};\n\n if (shapeClass) {\n json.shape = shapeClass.shape?.id || '';\n }\n if (subject instanceof PendingQueryContext) {\n // Carry the reference, not its (possibly unresolved) id, so the receiver\n // resolves it against its own context map at lowering time.\n json.subject = encodeContextRef(subject.contextName);\n } else if (subject && typeof subject === 'object' && 'id' in subject) {\n json.subject = (subject as NodeReferenceValue).id;\n }\n if (subjects && subjects.length > 0) {\n json.subjects = subjects.map((s) => s.id);\n }\n // A shapeless ask carries neither — both name properties, which only a shape\n // can resolve — so `shapeClass` is present whenever these are.\n if (where && shapeClass) {\n json.where = serializeWherePath(where, shapeClass.shape);\n }\n if (minusEntries && minusEntries.length > 0 && shapeClass) {\n json.minusEntries = minusEntries.map((e) =>\n serializeRawMinusEntry(e, shapeClass.shape),\n );\n }\n if (nullSubject) {\n json.nullSubject = true;\n }\n return json;\n }\n\n static fromJSON(json: AskQueryJSON): AskBuilder {\n assertWireVersion(json.v);\n const shapeClass = json.shape\n ? (resolveShape(json.shape as any) as ShapeConstructor<any>)\n : undefined;\n const spec: AskSpec = {};\n if (shapeClass) spec.shapeClass = shapeClass;\n\n if (json.subject !== undefined) {\n spec.subject = isContextRefJSON(json.subject)\n ? new PendingQueryContext(json.subject['@ctx'])\n : {id: json.subject as string};\n }\n if (json.subjects && json.subjects.length > 0) {\n spec.subjects = json.subjects.map((id) => ({id}));\n }\n if (json.where || json.minusEntries?.length) {\n // A where clause or minus entry names properties, and a property is only\n // resolvable through a shape. A shapeless ask cannot carry either.\n if (!shapeClass) {\n throw new Error(\n 'An ask envelope with a `where` or `minusEntries` must name a `shape`: ' +\n 'the clause references properties, which are resolved through the shape.',\n );\n }\n if (json.where) {\n spec.where = deserializeWherePath(shapeClass.shape, json.where);\n }\n if (json.minusEntries?.length) {\n spec.minusEntries = json.minusEntries.map((e) =>\n deserializeRawMinusEntry(shapeClass.shape, e),\n );\n }\n }\n if (json.nullSubject) spec.nullSubject = true;\n return new AskBuilder(spec);\n }\n\n /**\n * Execute and resolve to a real boolean.\n *\n * Never `null`, never a row. A store, transport or lowering failure **rejects**;\n * it is never reported as `false`. The one case that resolves `false` without\n * querying is a query with no subject to ask about — `.for(null)`, or a\n * `PendingQueryContext` as the subject that has not landed: \"does the node with\n * no id exist?\" has a correct total answer, and it is `false`.\n */\n async exec(target?: IDataset): Promise<boolean> {\n const {nullSubject, subject} = this._spec;\n if (nullSubject) return false;\n if (subject instanceof PendingQueryContext && !subject.id) return false;\n // `async` ensures a missing global dispatch rejects rather than throwing\n // synchronously past the caller's `.catch()`.\n const dispatch = target ?? getQueryDispatch();\n return resolveExistence(dispatch as any, this);\n }\n\n /** `await` triggers execution. */\n then<TResult1 = boolean, TResult2 = never>(\n onfulfilled?: ((value: boolean) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n return this.exec().then(onfulfilled, onrejected);\n }\n\n catch<TResult = never>(\n onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null,\n ): Promise<boolean | TResult> {\n return this.then().catch(onrejected);\n }\n\n finally(onfinally?: (() => void) | null): Promise<boolean> {\n return this.then().finally(onfinally);\n }\n\n get [Symbol.toStringTag](): string {\n return 'AskBuilder';\n }\n}\n\n/** Narrow an unknown query object to an ask. */\nexport function isAskQuery(query: unknown): query is AskQuery {\n return (\n !!query && (query as {__queryKind?: string}).__queryKind === 'ask'\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"AskBuilder.js","sourceRoot":"","sources":["../../../src/queries/AskBuilder.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 * `AskBuilder` — a query whose answer is a boolean.\n *\n * A small sibling of `SelectBuilder` rather than a mode of it. It can only hold a\n * pattern (shape, subject(s), where, minus), so the normalisation an existence\n * check needs is a property of the *type*: there is no projection, sorting or\n * pagination to drop, because none can be set.\n */\nimport {Shape, type ShapeConstructor} from '../shapes/Shape.js';\nimport {resolveShape} from './resolveShape.js';\nimport type {WherePath, WhereClause} from './SelectQuery.js';\nimport {processWhereClause} from './SelectQuery.js';\nimport type {RawMinusEntry} from './IRDesugar.js';\nimport type {NodeShapeData} from '../shapes/SHACL.js';\nimport type {NodeReferenceValue} from './QueryFactory.js';\nimport type {IDataset} from '../interfaces/IDataset.js';\nimport {PendingQueryContext} from './QueryContext.js';\nimport {encodeContextRef, isContextRefJSON} from './ContextRef.js';\nimport {WIRE_VERSION, assertWireVersion} from './wireVersion.js';\nimport {getQueryDispatch, resolveExistence} from './queryDispatch.js';\nimport {resolveUriOrThrow} from '../utils/NodeReference.js';\nimport {\n serializeWherePath,\n serializeRawMinusEntry,\n deserializeWherePath,\n deserializeRawMinusEntry,\n} from './QueryBuilderSerialization.js';\nimport type {AskQuery, AskQueryJSON, RawAskInput} from './AskQuery.js';\nimport {requireLiveQueryStore} from '../live/registry.js';\nimport type {LiveListener, LiveQuery, LiveQueryOptions} from '../live/LiveQueryStore.js';\n\n/** Everything an ask can carry. Assembled by `.exists()` or by `Shape.exists()`. */\nexport type AskSpec = {\n /** The shape class whose instances are asked about; omitted = shapeless. */\n shapeClass?: ShapeConstructor<any>;\n subject?: NodeReferenceValue | PendingQueryContext;\n subjects?: NodeReferenceValue[];\n where?: WherePath;\n minusEntries?: RawMinusEntry[];\n /** `.for(null)` was called — there is no subject to ask about. */\n nullSubject?: boolean;\n};\n\nexport class AskBuilder implements PromiseLike<boolean>, Promise<boolean> {\n private readonly _spec: AskSpec;\n\n private constructor(spec: AskSpec) {\n this._spec = spec;\n }\n\n /** Build from an assembled spec — used by `.exists()` and `Shape.exists()`. */\n static of(spec: AskSpec): AskBuilder {\n return new AskBuilder(spec);\n }\n\n /**\n * A shapeless ask: does a node with this IRI exist at all, under any type or\n * none? Lowers to `ASK { <iri> ?p ?o }`.\n */\n static forNode(\n id: string | NodeReferenceValue | PendingQueryContext | null | undefined,\n ): AskBuilder {\n if (id === null || id === undefined) {\n return new AskBuilder({nullSubject: true});\n }\n if (id instanceof PendingQueryContext) {\n return new AskBuilder({subject: id});\n }\n const ref =\n typeof id === 'string' ? {id: resolveUriOrThrow(id)} : id;\n return new AskBuilder({subject: ref});\n }\n\n /** Discriminator for the free `lower()` function and dataset routing. */\n readonly __queryKind = 'ask' as const;\n\n /** The shape asked about — `undefined` for a shapeless ask. */\n get shape(): NodeShapeData | undefined {\n return this._spec.shapeClass?.shape;\n }\n\n toRawInput(): RawAskInput {\n const {shapeClass, subject, subjects, where, minusEntries, nullSubject} =\n this._spec;\n const input: RawAskInput = {};\n // Carried so lowering can reject it. `exec()` answers `false` before\n // dispatching, but a store handed the builder directly (e.g. after\n // `fromJSON`) would otherwise lower a subject-less query that matches every\n // instance of the shape and answer `true`.\n if (nullSubject) input.nullSubject = true;\n if (shapeClass) input.shape = shapeClass as any;\n if (subject) input.subject = subject;\n if (subjects && subjects.length > 0) input.subjects = subjects;\n if (where) input.where = where;\n if (minusEntries && minusEntries.length > 0) input.minusEntries = minusEntries;\n return input;\n }\n\n toJSON(): AskQueryJSON {\n const {shapeClass, subject, subjects, where, minusEntries, nullSubject} =\n this._spec;\n const json: AskQueryJSON = {v: WIRE_VERSION, op: 'ask'};\n\n if (shapeClass) {\n json.shape = shapeClass.shape?.id || '';\n }\n if (subject instanceof PendingQueryContext) {\n // Carry the reference, not its (possibly unresolved) id, so the receiver\n // resolves it against its own context map at lowering time.\n json.subject = encodeContextRef(subject.contextName);\n } else if (subject && typeof subject === 'object' && 'id' in subject) {\n json.subject = (subject as NodeReferenceValue).id;\n }\n if (subjects && subjects.length > 0) {\n json.subjects = subjects.map((s) => s.id);\n }\n // A shapeless ask carries neither — both name properties, which only a shape\n // can resolve — so `shapeClass` is present whenever these are.\n if (where && shapeClass) {\n json.where = serializeWherePath(where, shapeClass.shape);\n }\n if (minusEntries && minusEntries.length > 0 && shapeClass) {\n json.minusEntries = minusEntries.map((e) =>\n serializeRawMinusEntry(e, shapeClass.shape),\n );\n }\n if (nullSubject) {\n json.nullSubject = true;\n }\n return json;\n }\n\n static fromJSON(json: AskQueryJSON): AskBuilder {\n assertWireVersion(json.v);\n const shapeClass = json.shape\n ? (resolveShape(json.shape as any) as ShapeConstructor<any>)\n : undefined;\n const spec: AskSpec = {};\n if (shapeClass) spec.shapeClass = shapeClass;\n\n if (json.subject !== undefined) {\n spec.subject = isContextRefJSON(json.subject)\n ? new PendingQueryContext(json.subject['@ctx'])\n : {id: json.subject as string};\n }\n if (json.subjects && json.subjects.length > 0) {\n spec.subjects = json.subjects.map((id) => ({id}));\n }\n if (json.where || json.minusEntries?.length) {\n // A where clause or minus entry names properties, and a property is only\n // resolvable through a shape. A shapeless ask cannot carry either.\n if (!shapeClass) {\n throw new Error(\n 'An ask envelope with a `where` or `minusEntries` must name a `shape`: ' +\n 'the clause references properties, which are resolved through the shape.',\n );\n }\n if (json.where) {\n spec.where = deserializeWherePath(shapeClass.shape, json.where);\n }\n if (json.minusEntries?.length) {\n spec.minusEntries = json.minusEntries.map((e) =>\n deserializeRawMinusEntry(shapeClass.shape, e),\n );\n }\n }\n if (json.nullSubject) spec.nullSubject = true;\n return new AskBuilder(spec);\n }\n\n /**\n * Execute and resolve to a real boolean.\n *\n * Never `null`, never a row. A store, transport or lowering failure **rejects**;\n * it is never reported as `false`. The one case that resolves `false` without\n * querying is a query with no subject to ask about — `.for(null)`, or a\n * `PendingQueryContext` as the subject that has not landed: \"does the node with\n * no id exist?\" has a correct total answer, and it is `false`.\n */\n async exec(target?: IDataset): Promise<boolean> {\n const {nullSubject, subject} = this._spec;\n if (nullSubject) return false;\n if (subject instanceof PendingQueryContext && !subject.id) return false;\n // `async` ensures a missing global dispatch rejects rather than throwing\n // synchronously past the caller's `.catch()`.\n const dispatch = target ?? getQueryDispatch();\n return resolveExistence(dispatch as any, this);\n }\n\n\n /**\n * Go live: subscribe to this query and keep its result current.\n *\n * Returns a {@link LiveQuery} handle. `await` it for the first result,\n * `subscribe()` for every later one, `close()` when done. A listener may be\n * passed directly as the first argument. Requires the live-query store to be\n * loaded, which importing `@_linked/core` (the package root) does.\n */\n live<R = boolean>(listenerOrOptions?: LiveListener<R> | LiveQueryOptions, options?: LiveQueryOptions): LiveQuery<R> {\n const listener = typeof listenerOrOptions === 'function' ? listenerOrOptions : undefined;\n const opts = typeof listenerOrOptions === 'function' ? options : listenerOrOptions;\n return requireLiveQueryStore().subscribe<R>(this as any, listener, opts);\n }\n\n /** `await` triggers execution. */\n then<TResult1 = boolean, TResult2 = never>(\n onfulfilled?: ((value: boolean) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n return this.exec().then(onfulfilled, onrejected);\n }\n\n catch<TResult = never>(\n onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null,\n ): Promise<boolean | TResult> {\n return this.then().catch(onrejected);\n }\n\n finally(onfinally?: (() => void) | null): Promise<boolean> {\n return this.then().finally(onfinally);\n }\n\n get [Symbol.toStringTag](): string {\n return 'AskBuilder';\n }\n}\n\n/** Narrow an unknown query object to an ask. */\nexport function isAskQuery(query: unknown): query is AskQuery {\n return (\n !!query && (query as {__queryKind?: string}).__queryKind === 'ask'\n );\n}\n"]}
|
|
@@ -19,6 +19,7 @@ import type { NodeReferenceValue } from './QueryFactory.js';
|
|
|
19
19
|
import type { IDataset } from '../interfaces/IDataset.js';
|
|
20
20
|
import { PendingQueryContext } from './QueryContext.js';
|
|
21
21
|
import type { CountQuery, CountQueryJSON, RawCountInput } from './CountQuery.js';
|
|
22
|
+
import type { LiveListener, LiveQuery, LiveQueryOptions } from '../live/LiveQueryStore.js';
|
|
22
23
|
/**
|
|
23
24
|
* Everything a count can carry. Assembled by `SelectBuilder.toCount()` or
|
|
24
25
|
* `Shape.count()`.
|
|
@@ -59,6 +60,15 @@ export declare class CountBuilder implements PromiseLike<number>, Promise<number
|
|
|
59
60
|
* is `0`.
|
|
60
61
|
*/
|
|
61
62
|
exec(target?: IDataset): Promise<number>;
|
|
63
|
+
/**
|
|
64
|
+
* Go live: subscribe to this query and keep its result current.
|
|
65
|
+
*
|
|
66
|
+
* Returns a {@link LiveQuery} handle. `await` it for the first result,
|
|
67
|
+
* `subscribe()` for every later one, `close()` when done. A listener may be
|
|
68
|
+
* passed directly as the first argument. Requires the live-query store to be
|
|
69
|
+
* loaded, which importing `@_linked/core` (the package root) does.
|
|
70
|
+
*/
|
|
71
|
+
live<R = number>(listenerOrOptions?: LiveListener<R> | LiveQueryOptions, options?: LiveQueryOptions): LiveQuery<R>;
|
|
62
72
|
/** `await` triggers execution. */
|
|
63
73
|
then<TResult1 = number, TResult2 = never>(onfulfilled?: ((value: number) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
|
64
74
|
catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null): Promise<number | TResult>;
|
|
@@ -18,6 +18,7 @@ import { encodeContextRef, isContextRefJSON } from './ContextRef.js';
|
|
|
18
18
|
import { WIRE_VERSION, assertWireVersion } from './wireVersion.js';
|
|
19
19
|
import { getQueryDispatch, resolveCount } from './queryDispatch.js';
|
|
20
20
|
import { serializeWherePath, serializeRawMinusEntry, deserializeWherePath, deserializeRawMinusEntry, } from './QueryBuilderSerialization.js';
|
|
21
|
+
import { requireLiveQueryStore } from '../live/registry.js';
|
|
21
22
|
export class CountBuilder {
|
|
22
23
|
constructor(spec) {
|
|
23
24
|
/** Discriminator for the free `lower()` function and dataset routing. */
|
|
@@ -144,6 +145,19 @@ export class CountBuilder {
|
|
|
144
145
|
return resolveCount(dispatch, this);
|
|
145
146
|
});
|
|
146
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Go live: subscribe to this query and keep its result current.
|
|
150
|
+
*
|
|
151
|
+
* Returns a {@link LiveQuery} handle. `await` it for the first result,
|
|
152
|
+
* `subscribe()` for every later one, `close()` when done. A listener may be
|
|
153
|
+
* passed directly as the first argument. Requires the live-query store to be
|
|
154
|
+
* loaded, which importing `@_linked/core` (the package root) does.
|
|
155
|
+
*/
|
|
156
|
+
live(listenerOrOptions, options) {
|
|
157
|
+
const listener = typeof listenerOrOptions === 'function' ? listenerOrOptions : undefined;
|
|
158
|
+
const opts = typeof listenerOrOptions === 'function' ? options : listenerOrOptions;
|
|
159
|
+
return requireLiveQueryStore().subscribe(this, listener, opts);
|
|
160
|
+
}
|
|
147
161
|
/** `await` triggers execution. */
|
|
148
162
|
then(onfulfilled, onrejected) {
|
|
149
163
|
return this.exec().then(onfulfilled, onrejected);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CountBuilder.js","sourceRoot":"","sources":["../../../src/queries/CountBuilder.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 * `CountBuilder` — a query whose answer is a number.\n *\n * A small sibling of `SelectBuilder` rather than a mode of it, for the same reason\n * {@link AskBuilder} is: it can only hold a pattern (shape, subject(s), where,\n * minus), so the normalisation a count needs is a property of the *type*. There is\n * no projection, ordering or **pagination** to drop, because none can be set — and\n * a count of a windowed query is meaningless, so that is the point.\n *\n * Deliberately **non-generic**. `SelectBuilder<S, R, Result>` is heavily inferred;\n * a count answers `number` no matter what the select's projection was, so this type\n * takes part in none of that inference and cannot perturb it.\n */\nimport type {ShapeConstructor} from '../shapes/Shape.js';\nimport {resolveShape} from './resolveShape.js';\nimport type {WherePath} from './SelectQuery.js';\nimport type {RawMinusEntry} from './IRDesugar.js';\nimport type {NodeShapeData} from '../shapes/SHACL.js';\nimport type {NodeReferenceValue} from './QueryFactory.js';\nimport type {IDataset} from '../interfaces/IDataset.js';\nimport {PendingQueryContext} from './QueryContext.js';\nimport {encodeContextRef, isContextRefJSON} from './ContextRef.js';\nimport {WIRE_VERSION, assertWireVersion} from './wireVersion.js';\nimport {getQueryDispatch, resolveCount} from './queryDispatch.js';\nimport {\n serializeWherePath,\n serializeRawMinusEntry,\n deserializeWherePath,\n deserializeRawMinusEntry,\n} from './QueryBuilderSerialization.js';\nimport type {CountQuery, CountQueryJSON, RawCountInput} from './CountQuery.js';\n\n/**\n * Everything a count can carry. Assembled by `SelectBuilder.toCount()` or\n * `Shape.count()`.\n *\n * `shapeClass` is required — a shapeless count would count every node in the store.\n */\nexport type CountSpec = {\n shapeClass: ShapeConstructor<any>;\n subject?: NodeReferenceValue | PendingQueryContext;\n subjects?: NodeReferenceValue[];\n where?: WherePath;\n minusEntries?: RawMinusEntry[];\n /** `.for(null)` was called — there is no subject to count. */\n nullSubject?: boolean;\n};\n\nexport class CountBuilder implements PromiseLike<number>, Promise<number> {\n private readonly _spec: CountSpec;\n\n private constructor(spec: CountSpec) {\n this._spec = spec;\n }\n\n /** Build from an assembled spec — used by `.toCount()` and `Shape.count()`. */\n static of(spec: CountSpec): CountBuilder {\n return new CountBuilder(spec);\n }\n\n /** Discriminator for the free `lower()` function and dataset routing. */\n readonly __queryKind = 'count' as const;\n\n /** The shape whose matching instances are counted — also the routing key. */\n get shape(): NodeShapeData {\n return this._spec.shapeClass.shape;\n }\n\n toRawInput(): RawCountInput {\n const {shapeClass, subject, subjects, where, minusEntries, nullSubject} =\n this._spec;\n const input: RawCountInput = {shape: shapeClass as any};\n // Carried so lowering can reject it. `exec()` answers `0` before dispatching,\n // but a store handed the builder directly (e.g. after `fromJSON`) would\n // otherwise lower a subject-less query that matches every instance of the\n // shape and report that as the count of one node.\n if (nullSubject) input.nullSubject = true;\n if (subject) input.subject = subject;\n if (subjects && subjects.length > 0) input.subjects = subjects;\n if (where) input.where = where;\n if (minusEntries && minusEntries.length > 0) input.minusEntries = minusEntries;\n return input;\n }\n\n toJSON(): CountQueryJSON {\n const {shapeClass, subject, subjects, where, minusEntries, nullSubject} =\n this._spec;\n const shapeId = shapeClass.shape?.id;\n if (!shapeId) {\n // Refuse here rather than emitting `shape: ''` for the receiver's `fromJSON`\n // to reject: the caller who holds the shape can act on this, and a peer\n // across a wire cannot.\n throw new Error(\n 'Cannot serialize a count query whose shape has no id. A count envelope must ' +\n 'name a shape — a shapeless count would count every node in the store.',\n );\n }\n const json: CountQueryJSON = {\n v: WIRE_VERSION,\n op: 'count',\n shape: shapeId,\n };\n\n if (subject instanceof PendingQueryContext) {\n // Carry the reference, not its (possibly unresolved) id, so the receiver\n // resolves it against its own context map at lowering time.\n json.subject = encodeContextRef(subject.contextName);\n } else if (subject && typeof subject === 'object' && 'id' in subject) {\n json.subject = (subject as NodeReferenceValue).id;\n }\n if (subjects && subjects.length > 0) {\n json.subjects = subjects.map((s) => s.id);\n }\n if (where) {\n json.where = serializeWherePath(where, shapeClass.shape);\n }\n if (minusEntries && minusEntries.length > 0) {\n json.minusEntries = minusEntries.map((e) =>\n serializeRawMinusEntry(e, shapeClass.shape),\n );\n }\n if (nullSubject) {\n json.nullSubject = true;\n }\n return json;\n }\n\n static fromJSON(json: CountQueryJSON): CountBuilder {\n assertWireVersion(json.v);\n if (!json.shape) {\n throw new Error(\n 'A count envelope must name a `shape`. A shapeless count would count every ' +\n 'node in the store, under any type or none.',\n );\n }\n const shapeClass = resolveShape(json.shape as any) as ShapeConstructor<any>;\n const spec: CountSpec = {shapeClass};\n\n if (json.subject !== undefined) {\n spec.subject = isContextRefJSON(json.subject)\n ? new PendingQueryContext(json.subject['@ctx'])\n : {id: json.subject as string};\n }\n if (json.subjects && json.subjects.length > 0) {\n spec.subjects = json.subjects.map((id) => ({id}));\n }\n if (json.where) {\n spec.where = deserializeWherePath(shapeClass.shape, json.where);\n }\n if (json.minusEntries?.length) {\n spec.minusEntries = json.minusEntries.map((e) =>\n deserializeRawMinusEntry(shapeClass.shape, e),\n );\n }\n if (json.nullSubject) spec.nullSubject = true;\n return new CountBuilder(spec);\n }\n\n /**\n * Execute and resolve to a real number.\n *\n * **A failure rejects — it is never reported as `0`.** A count of `0` renders an\n * empty table and is indistinguishable from real data, so a broken count that\n * answered `0` would hide rather than fail. Do not wrap this in `.catch(() => 0)`.\n *\n * The one case that resolves `0` without querying is a query with no subject to\n * count — `.for(null)`, or a `PendingQueryContext` as the subject that has not\n * landed: \"how many nodes with no id match?\" has a correct total answer, and it\n * is `0`.\n */\n async exec(target?: IDataset): Promise<number> {\n const {nullSubject, subject} = this._spec;\n if (nullSubject) return 0;\n if (subject instanceof PendingQueryContext && !subject.id) return 0;\n // `async` ensures a missing global dispatch rejects rather than throwing\n // synchronously past the caller's `.catch()`.\n const dispatch = target ?? getQueryDispatch();\n // `resolveCount` dispatches over the SELECT channel — a count is a select with\n // an aggregate projection, not a query form of its own — and checks the answer\n // is a real non-negative integer before it reaches the caller.\n return resolveCount(dispatch as any, this);\n }\n\n /** `await` triggers execution. */\n then<TResult1 = number, TResult2 = never>(\n onfulfilled?: ((value: number) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n return this.exec().then(onfulfilled, onrejected);\n }\n\n catch<TResult = never>(\n onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null,\n ): Promise<number | TResult> {\n return this.then().catch(onrejected);\n }\n\n finally(onfinally?: (() => void) | null): Promise<number> {\n return this.then().finally(onfinally);\n }\n\n get [Symbol.toStringTag](): string {\n return 'CountBuilder';\n }\n}\n\n/** Narrow an unknown query object to a count. */\nexport function isCountQuery(query: unknown): query is CountQuery {\n return !!query && (query as {__queryKind?: string}).__queryKind === 'count';\n}\n"]}
|
|
1
|
+
{"version":3,"file":"CountBuilder.js","sourceRoot":"","sources":["../../../src/queries/CountBuilder.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 * `CountBuilder` — a query whose answer is a number.\n *\n * A small sibling of `SelectBuilder` rather than a mode of it, for the same reason\n * {@link AskBuilder} is: it can only hold a pattern (shape, subject(s), where,\n * minus), so the normalisation a count needs is a property of the *type*. There is\n * no projection, ordering or **pagination** to drop, because none can be set — and\n * a count of a windowed query is meaningless, so that is the point.\n *\n * Deliberately **non-generic**. `SelectBuilder<S, R, Result>` is heavily inferred;\n * a count answers `number` no matter what the select's projection was, so this type\n * takes part in none of that inference and cannot perturb it.\n */\nimport type {ShapeConstructor} from '../shapes/Shape.js';\nimport {resolveShape} from './resolveShape.js';\nimport type {WherePath} from './SelectQuery.js';\nimport type {RawMinusEntry} from './IRDesugar.js';\nimport type {NodeShapeData} from '../shapes/SHACL.js';\nimport type {NodeReferenceValue} from './QueryFactory.js';\nimport type {IDataset} from '../interfaces/IDataset.js';\nimport {PendingQueryContext} from './QueryContext.js';\nimport {encodeContextRef, isContextRefJSON} from './ContextRef.js';\nimport {WIRE_VERSION, assertWireVersion} from './wireVersion.js';\nimport {getQueryDispatch, resolveCount} from './queryDispatch.js';\nimport {\n serializeWherePath,\n serializeRawMinusEntry,\n deserializeWherePath,\n deserializeRawMinusEntry,\n} from './QueryBuilderSerialization.js';\nimport type {CountQuery, CountQueryJSON, RawCountInput} from './CountQuery.js';\nimport {requireLiveQueryStore} from '../live/registry.js';\nimport type {LiveListener, LiveQuery, LiveQueryOptions} from '../live/LiveQueryStore.js';\n\n/**\n * Everything a count can carry. Assembled by `SelectBuilder.toCount()` or\n * `Shape.count()`.\n *\n * `shapeClass` is required — a shapeless count would count every node in the store.\n */\nexport type CountSpec = {\n shapeClass: ShapeConstructor<any>;\n subject?: NodeReferenceValue | PendingQueryContext;\n subjects?: NodeReferenceValue[];\n where?: WherePath;\n minusEntries?: RawMinusEntry[];\n /** `.for(null)` was called — there is no subject to count. */\n nullSubject?: boolean;\n};\n\nexport class CountBuilder implements PromiseLike<number>, Promise<number> {\n private readonly _spec: CountSpec;\n\n private constructor(spec: CountSpec) {\n this._spec = spec;\n }\n\n /** Build from an assembled spec — used by `.toCount()` and `Shape.count()`. */\n static of(spec: CountSpec): CountBuilder {\n return new CountBuilder(spec);\n }\n\n /** Discriminator for the free `lower()` function and dataset routing. */\n readonly __queryKind = 'count' as const;\n\n /** The shape whose matching instances are counted — also the routing key. */\n get shape(): NodeShapeData {\n return this._spec.shapeClass.shape;\n }\n\n toRawInput(): RawCountInput {\n const {shapeClass, subject, subjects, where, minusEntries, nullSubject} =\n this._spec;\n const input: RawCountInput = {shape: shapeClass as any};\n // Carried so lowering can reject it. `exec()` answers `0` before dispatching,\n // but a store handed the builder directly (e.g. after `fromJSON`) would\n // otherwise lower a subject-less query that matches every instance of the\n // shape and report that as the count of one node.\n if (nullSubject) input.nullSubject = true;\n if (subject) input.subject = subject;\n if (subjects && subjects.length > 0) input.subjects = subjects;\n if (where) input.where = where;\n if (minusEntries && minusEntries.length > 0) input.minusEntries = minusEntries;\n return input;\n }\n\n toJSON(): CountQueryJSON {\n const {shapeClass, subject, subjects, where, minusEntries, nullSubject} =\n this._spec;\n const shapeId = shapeClass.shape?.id;\n if (!shapeId) {\n // Refuse here rather than emitting `shape: ''` for the receiver's `fromJSON`\n // to reject: the caller who holds the shape can act on this, and a peer\n // across a wire cannot.\n throw new Error(\n 'Cannot serialize a count query whose shape has no id. A count envelope must ' +\n 'name a shape — a shapeless count would count every node in the store.',\n );\n }\n const json: CountQueryJSON = {\n v: WIRE_VERSION,\n op: 'count',\n shape: shapeId,\n };\n\n if (subject instanceof PendingQueryContext) {\n // Carry the reference, not its (possibly unresolved) id, so the receiver\n // resolves it against its own context map at lowering time.\n json.subject = encodeContextRef(subject.contextName);\n } else if (subject && typeof subject === 'object' && 'id' in subject) {\n json.subject = (subject as NodeReferenceValue).id;\n }\n if (subjects && subjects.length > 0) {\n json.subjects = subjects.map((s) => s.id);\n }\n if (where) {\n json.where = serializeWherePath(where, shapeClass.shape);\n }\n if (minusEntries && minusEntries.length > 0) {\n json.minusEntries = minusEntries.map((e) =>\n serializeRawMinusEntry(e, shapeClass.shape),\n );\n }\n if (nullSubject) {\n json.nullSubject = true;\n }\n return json;\n }\n\n static fromJSON(json: CountQueryJSON): CountBuilder {\n assertWireVersion(json.v);\n if (!json.shape) {\n throw new Error(\n 'A count envelope must name a `shape`. A shapeless count would count every ' +\n 'node in the store, under any type or none.',\n );\n }\n const shapeClass = resolveShape(json.shape as any) as ShapeConstructor<any>;\n const spec: CountSpec = {shapeClass};\n\n if (json.subject !== undefined) {\n spec.subject = isContextRefJSON(json.subject)\n ? new PendingQueryContext(json.subject['@ctx'])\n : {id: json.subject as string};\n }\n if (json.subjects && json.subjects.length > 0) {\n spec.subjects = json.subjects.map((id) => ({id}));\n }\n if (json.where) {\n spec.where = deserializeWherePath(shapeClass.shape, json.where);\n }\n if (json.minusEntries?.length) {\n spec.minusEntries = json.minusEntries.map((e) =>\n deserializeRawMinusEntry(shapeClass.shape, e),\n );\n }\n if (json.nullSubject) spec.nullSubject = true;\n return new CountBuilder(spec);\n }\n\n /**\n * Execute and resolve to a real number.\n *\n * **A failure rejects — it is never reported as `0`.** A count of `0` renders an\n * empty table and is indistinguishable from real data, so a broken count that\n * answered `0` would hide rather than fail. Do not wrap this in `.catch(() => 0)`.\n *\n * The one case that resolves `0` without querying is a query with no subject to\n * count — `.for(null)`, or a `PendingQueryContext` as the subject that has not\n * landed: \"how many nodes with no id match?\" has a correct total answer, and it\n * is `0`.\n */\n async exec(target?: IDataset): Promise<number> {\n const {nullSubject, subject} = this._spec;\n if (nullSubject) return 0;\n if (subject instanceof PendingQueryContext && !subject.id) return 0;\n // `async` ensures a missing global dispatch rejects rather than throwing\n // synchronously past the caller's `.catch()`.\n const dispatch = target ?? getQueryDispatch();\n // `resolveCount` dispatches over the SELECT channel — a count is a select with\n // an aggregate projection, not a query form of its own — and checks the answer\n // is a real non-negative integer before it reaches the caller.\n return resolveCount(dispatch as any, this);\n }\n\n\n /**\n * Go live: subscribe to this query and keep its result current.\n *\n * Returns a {@link LiveQuery} handle. `await` it for the first result,\n * `subscribe()` for every later one, `close()` when done. A listener may be\n * passed directly as the first argument. Requires the live-query store to be\n * loaded, which importing `@_linked/core` (the package root) does.\n */\n live<R = number>(listenerOrOptions?: LiveListener<R> | LiveQueryOptions, options?: LiveQueryOptions): LiveQuery<R> {\n const listener = typeof listenerOrOptions === 'function' ? listenerOrOptions : undefined;\n const opts = typeof listenerOrOptions === 'function' ? options : listenerOrOptions;\n return requireLiveQueryStore().subscribe<R>(this as any, listener, opts);\n }\n\n /** `await` triggers execution. */\n then<TResult1 = number, TResult2 = never>(\n onfulfilled?: ((value: number) => TResult1 | PromiseLike<TResult1>) | null,\n onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null,\n ): Promise<TResult1 | TResult2> {\n return this.exec().then(onfulfilled, onrejected);\n }\n\n catch<TResult = never>(\n onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null,\n ): Promise<number | TResult> {\n return this.then().catch(onrejected);\n }\n\n finally(onfinally?: (() => void) | null): Promise<number> {\n return this.then().finally(onfinally);\n }\n\n get [Symbol.toStringTag](): string {\n return 'CountBuilder';\n }\n}\n\n/** Narrow an unknown query object to a count. */\nexport function isCountQuery(query: unknown): query is CountQuery {\n return !!query && (query as {__queryKind?: string}).__queryKind === 'count';\n}\n"]}
|
|
@@ -9,6 +9,7 @@ import { FieldSet, type FieldSetFieldJSON } from './FieldSet.js';
|
|
|
9
9
|
import { PendingQueryContext } from './QueryContext.js';
|
|
10
10
|
import { type ContextRefJSON } from './ContextRef.js';
|
|
11
11
|
import { type WherePathJSON, type SortByPathJSON, type RawMinusEntryJSON } from './QueryBuilderSerialization.js';
|
|
12
|
+
import type { LiveListener, LiveQuery, LiveQueryOptions } from '../live/LiveQueryStore.js';
|
|
12
13
|
/** JSON representation of a SelectBuilder. */
|
|
13
14
|
export type QueryBuilderJSON = {
|
|
14
15
|
/** DSL-JSON wire-format version. */
|
|
@@ -325,6 +326,15 @@ export declare class SelectBuilder<S extends Shape = Shape, R = any, Result = an
|
|
|
325
326
|
* false: it must not turn "could not ask" into a boolean.
|
|
326
327
|
*/
|
|
327
328
|
private _run;
|
|
329
|
+
/**
|
|
330
|
+
* Go live: subscribe to this query and keep its result current.
|
|
331
|
+
*
|
|
332
|
+
* Returns a {@link LiveQuery} handle. `await` it for the first result,
|
|
333
|
+
* `subscribe()` for every later one, `close()` when done. A listener may be
|
|
334
|
+
* passed directly as the first argument. Requires the live-query store to be
|
|
335
|
+
* loaded, which importing `@_linked/core` (the package root) does.
|
|
336
|
+
*/
|
|
337
|
+
live<R = Result>(listenerOrOptions?: LiveListener<R> | LiveQueryOptions, options?: LiveQueryOptions): LiveQuery<R>;
|
|
328
338
|
/** `await` triggers execution. */
|
|
329
339
|
then<TResult1 = Result, TResult2 = never>(onfulfilled?: ((value: Result) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
|
330
340
|
/** Catch errors from execution. Chain off then() to avoid re-executing. */
|
|
@@ -20,6 +20,7 @@ import { PendingQueryContext, UnresolvedContextError } from './QueryContext.js';
|
|
|
20
20
|
import { encodeContextRef, isContextRefJSON } from './ContextRef.js';
|
|
21
21
|
import { createProxiedPathBuilder } from './ProxiedPathBuilder.js';
|
|
22
22
|
import { serializeWherePath, serializeSortByPath, serializeRawMinusEntry, deserializeWherePath, deserializeSortByPath, deserializeRawMinusEntry, } from './QueryBuilderSerialization.js';
|
|
23
|
+
import { requireLiveQueryStore } from '../live/registry.js';
|
|
23
24
|
/**
|
|
24
25
|
* An immutable, fluent query builder for select queries.
|
|
25
26
|
*
|
|
@@ -699,6 +700,19 @@ export class SelectBuilder {
|
|
|
699
700
|
}
|
|
700
701
|
});
|
|
701
702
|
}
|
|
703
|
+
/**
|
|
704
|
+
* Go live: subscribe to this query and keep its result current.
|
|
705
|
+
*
|
|
706
|
+
* Returns a {@link LiveQuery} handle. `await` it for the first result,
|
|
707
|
+
* `subscribe()` for every later one, `close()` when done. A listener may be
|
|
708
|
+
* passed directly as the first argument. Requires the live-query store to be
|
|
709
|
+
* loaded, which importing `@_linked/core` (the package root) does.
|
|
710
|
+
*/
|
|
711
|
+
live(listenerOrOptions, options) {
|
|
712
|
+
const listener = typeof listenerOrOptions === 'function' ? listenerOrOptions : undefined;
|
|
713
|
+
const opts = typeof listenerOrOptions === 'function' ? options : listenerOrOptions;
|
|
714
|
+
return requireLiveQueryStore().subscribe(this, listener, opts);
|
|
715
|
+
}
|
|
702
716
|
// ---------------------------------------------------------------------------
|
|
703
717
|
// Promise-compatible interface
|
|
704
718
|
// ---------------------------------------------------------------------------
|