@astrale-os/sdk 0.6.0-beta.40 → 0.6.0-beta.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -2
- package/dist/application/action/context.d.ts +49 -18
- package/dist/application/action/index.d.ts +1 -1
- package/dist/application/error/error.d.ts +16 -1
- package/dist/application/error/error.js +39 -5
- package/dist/application/error/index.d.ts +1 -1
- package/dist/application/error/index.js +1 -1
- package/dist/application/mutation/authoring/builder.d.ts +81 -33
- package/dist/application/mutation/authoring/builder.js +100 -30
- package/dist/application/mutation/authoring/guard.d.ts +37 -0
- package/dist/application/mutation/authoring/guard.js +150 -0
- package/dist/application/mutation/authoring/index.d.ts +5 -2
- package/dist/application/mutation/authoring/index.js +2 -1
- package/dist/application/mutation/authoring/names.d.ts +19 -0
- package/dist/application/mutation/authoring/names.js +83 -0
- package/dist/application/mutation/authoring/transition.d.ts +3 -2
- package/dist/application/mutation/define.d.ts +24 -10
- package/dist/application/mutation/define.js +49 -53
- package/dist/application/mutation/execution/execute.d.ts +2 -2
- package/dist/application/mutation/execution/execute.js +78 -7
- package/dist/application/mutation/execution/executor.d.ts +4 -1
- package/dist/application/mutation/execution/executor.js +6 -1
- package/dist/application/mutation/failure.d.ts +11 -1
- package/dist/application/mutation/failure.js +18 -3
- package/dist/application/mutation/index.d.ts +8 -4
- package/dist/application/mutation/index.js +5 -2
- package/dist/application/mutation/legacy/v6/builder.d.ts +84 -0
- package/dist/application/mutation/legacy/v6/define.d.ts +18 -0
- package/dist/application/mutation/legacy/v6/define.js +56 -0
- package/dist/application/mutation/legacy/v6/mutation.d.ts +28 -0
- package/dist/application/mutation/legacy/v6/mutation.js +1 -0
- package/dist/application/mutation/{authoring → legacy/v6}/precondition.d.ts +4 -0
- package/dist/application/mutation/mutation.d.ts +30 -19
- package/dist/application/query/answer/answer.d.ts +117 -0
- package/dist/application/query/answer/answer.js +1 -0
- package/dist/application/query/answer/conformance.d.ts +4 -0
- package/dist/application/query/answer/conformance.js +27 -0
- package/dist/application/query/answer/facts.d.ts +17 -0
- package/dist/application/query/answer/facts.js +57 -0
- package/dist/application/query/composite/builder.d.ts +1 -1
- package/dist/application/query/composite/builder.js +1 -1
- package/dist/application/query/composite/plan.d.ts +1 -1
- package/dist/application/query/composite/plan.js +1 -1
- package/dist/application/query/composite/query.d.ts +1 -2
- package/dist/application/query/define.d.ts +2 -33
- package/dist/application/query/define.js +3 -130
- package/dist/application/query/definition.d.ts +12 -0
- package/dist/application/query/definition.js +47 -0
- package/dist/application/query/execution/bounds.d.ts +20 -0
- package/dist/application/query/execution/bounds.js +86 -0
- package/dist/application/query/execution/crossings.d.ts +25 -0
- package/dist/application/query/execution/crossings.js +169 -0
- package/dist/application/query/execution/error.d.ts +68 -0
- package/dist/application/query/execution/error.js +50 -0
- package/dist/application/query/execution/execute.d.ts +2 -7
- package/dist/application/query/execution/execute.js +3 -51
- package/dist/application/query/execution/recipe.d.ts +70 -0
- package/dist/application/query/execution/recipe.js +276 -0
- package/dist/application/query/execution/refusal.d.ts +37 -0
- package/dist/application/query/execution/refusal.js +80 -0
- package/dist/application/query/execution/rows.d.ts +42 -0
- package/dist/application/query/execution/rows.js +308 -0
- package/dist/application/query/execution/split.d.ts +24 -0
- package/dist/application/query/execution/split.js +190 -0
- package/dist/application/query/handles/build.d.ts +10 -0
- package/dist/application/query/handles/build.js +53 -0
- package/dist/application/query/handles/conditions.d.ts +94 -0
- package/dist/application/query/handles/conditions.js +1 -0
- package/dist/application/query/handles/domain.d.ts +157 -0
- package/dist/application/query/handles/domain.js +1 -0
- package/dist/application/query/handles/handles.d.ts +268 -0
- package/dist/application/query/handles/handles.js +1 -0
- package/dist/application/query/handles/model.d.ts +96 -0
- package/dist/application/query/handles/model.js +246 -0
- package/dist/application/query/handles/plan.d.ts +99 -0
- package/dist/application/query/handles/plan.js +37 -0
- package/dist/application/query/handles/reads.d.ts +106 -0
- package/dist/application/query/handles/reads.js +839 -0
- package/dist/application/query/handles/relations.d.ts +100 -0
- package/dist/application/query/handles/relations.js +1 -0
- package/dist/application/query/handles/rows.d.ts +94 -0
- package/dist/application/query/handles/rows.js +4 -0
- package/dist/application/query/handles/terms.d.ts +95 -0
- package/dist/application/query/handles/terms.js +265 -0
- package/dist/application/query/index.d.ts +10 -7
- package/dist/application/query/index.js +9 -4
- package/dist/application/query/language/index.d.ts +2 -0
- package/dist/application/query/language/index.js +2 -0
- package/dist/application/query/language/query.d.ts +323 -0
- package/dist/application/query/language/query.js +2 -0
- package/dist/application/query/{collection → legacy/v6/collection}/identity.d.ts +2 -2
- package/dist/application/query/{collection → legacy/v6/collection}/metadata.d.ts +1 -1
- package/dist/application/query/{collection → legacy/v6/collection}/union.d.ts +3 -3
- package/dist/application/query/{collection → legacy/v6/collection}/union.js +1 -1
- package/dist/application/query/{conformance.d.ts → legacy/v6/conformance.d.ts} +3 -3
- package/dist/application/query/{conformance.js → legacy/v6/conformance.js} +1 -1
- package/dist/application/query/legacy/v6/define.d.ts +37 -0
- package/dist/application/query/legacy/v6/define.js +90 -0
- package/dist/application/query/legacy/v6/execution/execute.d.ts +16 -0
- package/dist/application/query/legacy/v6/execution/execute.js +50 -0
- package/dist/application/query/legacy/v6/index.d.ts +6 -0
- package/dist/application/query/legacy/v6/index.js +3 -0
- package/dist/application/query/legacy/v6/kernel.d.ts +6 -0
- package/dist/application/query/legacy/v6/kernel.js +5 -0
- package/dist/application/query/{single → legacy/v6/single}/pagination.js +1 -1
- package/dist/application/query/{single → legacy/v6/single}/query.d.ts +2 -14
- package/dist/application/query/legacy/v6/single/query.js +1 -0
- package/dist/application/query/query.d.ts +16 -1
- package/dist/application/query/recipe/define.d.ts +45 -0
- package/dist/application/query/recipe/define.js +102 -0
- package/dist/application/query/recipe/recipe.d.ts +134 -0
- package/dist/application/query/recipe/recipe.js +1 -0
- package/dist/application/workflow/durable.d.ts +46 -20
- package/dist/execution/invocation/capabilities/bind.d.ts +6 -4
- package/dist/execution/invocation/capabilities/bind.js +39 -3
- package/dist/execution/invocation/dispatch/action.js +4 -2
- package/dist/execution/invocation/dispatch/context.js +1 -1
- package/dist/execution/invocation/dispatch/workflow.js +4 -2
- package/dist/execution/invocation/result/index.d.ts +1 -0
- package/dist/execution/invocation/result/index.js +1 -0
- package/dist/execution/invocation/result/recipe.d.ts +6 -0
- package/dist/execution/invocation/result/recipe.js +17 -0
- package/dist/execution/service/create.js +1 -0
- package/dist/execution/workflows/attempts.d.ts +87 -0
- package/dist/execution/workflows/attempts.js +205 -0
- package/dist/execution/workflows/engine.d.ts +6 -12
- package/dist/execution/workflows/failure.d.ts +27 -17
- package/dist/execution/workflows/failure.js +87 -44
- package/dist/execution/workflows/index.d.ts +1 -1
- package/dist/execution/workflows/options.d.ts +21 -6
- package/dist/execution/workflows/options.js +28 -18
- package/dist/execution/workflows/receiver.d.ts +3 -0
- package/dist/execution/workflows/receiver.js +1 -0
- package/dist/execution/workflows/run.d.ts +2 -0
- package/dist/execution/workflows/run.js +130 -82
- package/dist/execution/workflows/steps.d.ts +10 -1
- package/dist/execution/workflows/steps.js +34 -8
- package/dist/platform/schema/kernel-compiled.d.ts +2 -0
- package/dist/platform/schema/kernel-compiled.js +2 -0
- package/dist/testing/durable/engine.d.ts +18 -11
- package/dist/testing/durable/engine.js +43 -38
- package/dist/testing/durable/index.d.ts +2 -2
- package/dist/testing/durable/index.js +1 -1
- package/dist/testing/durable/steps.js +9 -4
- package/dist/testing/index.d.ts +4 -2
- package/dist/testing/index.js +2 -1
- package/dist/testing/recipe.d.ts +17 -0
- package/dist/testing/recipe.js +29 -0
- package/dist/tooling/cli/orchestrate.js +6 -2
- package/dist/tooling/linter/adapters/typescript/index.d.ts +2 -1
- package/dist/tooling/linter/adapters/typescript/index.js +2 -1
- package/dist/tooling/linter/adapters/typescript/query-observation.js +37 -12
- package/dist/tooling/linter/adapters/typescript/recipe-definitions.d.ts +34 -0
- package/dist/tooling/linter/adapters/typescript/recipe-definitions.js +88 -0
- package/dist/tooling/linter/adapters/typescript/type-analysis.d.ts +2 -0
- package/dist/tooling/linter/adapters/typescript/type-analysis.js +10 -0
- package/dist/tooling/linter/implementations/source/global.js +15 -9
- package/dist/tooling/linter/implementations/source/mutations.js +128 -19
- package/dist/tooling/linter/implementations/source/policy-edges.d.ts +2 -0
- package/dist/tooling/linter/implementations/source/policy-edges.js +113 -0
- package/dist/tooling/linter/implementations/source/recipes.d.ts +2 -0
- package/dist/tooling/linter/implementations/source/recipes.js +409 -0
- package/dist/tooling/linter/implementations/source/shared.d.ts +7 -1
- package/dist/tooling/linter/implementations/source/shared.js +31 -10
- package/dist/tooling/linter/implementations/source/verifiers.js +4 -0
- package/dist/tooling/linter/implementations/source/workflows.js +0 -1
- package/dist/tooling/linter/policy/generated.js +2 -2
- package/dist/tooling/linter/requirements/registry.js +4 -0
- package/docs/durable-workflows.md +117 -62
- package/docs/handler-dependencies.md +76 -4
- package/docs/legacy/query-v6.md +62 -0
- package/docs/queries.md +438 -0
- package/package.json +11 -8
- /package/dist/application/{query/single/query.js → mutation/legacy/v6/builder.js} +0 -0
- /package/dist/application/mutation/{authoring → legacy/v6}/precondition.js +0 -0
- /package/dist/application/query/{collection → legacy/v6/collection}/identity.js +0 -0
- /package/dist/application/query/{collection → legacy/v6/collection}/limits.d.ts +0 -0
- /package/dist/application/query/{collection → legacy/v6/collection}/limits.js +0 -0
- /package/dist/application/query/{collection → legacy/v6/collection}/metadata.js +0 -0
- /package/dist/application/query/{execution → legacy/v6/execution}/pages.d.ts +0 -0
- /package/dist/application/query/{execution → legacy/v6/execution}/pages.js +0 -0
- /package/dist/application/query/{single → legacy/v6/single}/index.d.ts +0 -0
- /package/dist/application/query/{single → legacy/v6/single}/index.js +0 -0
- /package/dist/application/query/{single → legacy/v6/single}/node.d.ts +0 -0
- /package/dist/application/query/{single → legacy/v6/single}/node.js +0 -0
- /package/dist/application/query/{single → legacy/v6/single}/pagination.d.ts +0 -0
- /package/dist/application/query/{single → legacy/v6/single}/selection.d.ts +0 -0
- /package/dist/application/query/{single → legacy/v6/single}/selection.js +0 -0
|
@@ -10,7 +10,11 @@ export function stateTransitionFailure(from, event) {
|
|
|
10
10
|
localStateTransitionFailures.add(failure);
|
|
11
11
|
return failure;
|
|
12
12
|
}
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* The Mutation refusal `input` carries, if any. `nameAt` names the precondition at an index of
|
|
15
|
+
* the built Mutation's `preconditions`, the index a precondition refusal points at.
|
|
16
|
+
*/
|
|
17
|
+
export function mutationFailure(input, nameAt) {
|
|
14
18
|
if (!(input instanceof Error))
|
|
15
19
|
return undefined;
|
|
16
20
|
const reason = mutationReason(input);
|
|
@@ -30,15 +34,20 @@ export function mutationFailure(input) {
|
|
|
30
34
|
return input;
|
|
31
35
|
}
|
|
32
36
|
const rule = code === 'MUTATION_POST_STATE_INVALID' ? reason.rule : undefined;
|
|
37
|
+
const precondition = code === 'MUTATION_PRECONDITION_FAILED' && reason.precondition !== undefined
|
|
38
|
+
? nameAt?.(reason.precondition)
|
|
39
|
+
: undefined;
|
|
33
40
|
return Object.assign(new Error(input.message, { cause: input }), {
|
|
34
41
|
code,
|
|
35
42
|
outcomeKnown: explicit !== false && !UNKNOWN_OUTCOME_CODES.has(code),
|
|
36
43
|
...(rule === undefined ? {} : { rule }),
|
|
44
|
+
...(precondition === undefined ? {} : { precondition }),
|
|
37
45
|
});
|
|
38
46
|
}
|
|
39
47
|
/**
|
|
40
48
|
* The Mutation refusal an error carries, on itself, as its Kernel reason, or as its protocol data:
|
|
41
|
-
* its code and, from the same carrier's details, the post-state rule it names
|
|
49
|
+
* its code and, from the same carrier's details, the post-state rule it names and the index of the
|
|
50
|
+
* precondition it points at.
|
|
42
51
|
*/
|
|
43
52
|
function mutationReason(input) {
|
|
44
53
|
const value = input;
|
|
@@ -48,14 +57,20 @@ function mutationReason(input) {
|
|
|
48
57
|
const code = typeof carrier === 'string' ? carrier : field(carrier, 'code');
|
|
49
58
|
if (typeof code !== 'string' || !code.startsWith('MUTATION_'))
|
|
50
59
|
continue;
|
|
51
|
-
const
|
|
60
|
+
const details = field(carrier, 'details');
|
|
61
|
+
const rule = field(details, 'rule');
|
|
62
|
+
const precondition = field(details, 'precondition');
|
|
52
63
|
return {
|
|
53
64
|
code: code,
|
|
54
65
|
...(typeof rule === 'string' ? { rule } : {}),
|
|
66
|
+
...(isIndex(precondition) ? { precondition } : {}),
|
|
55
67
|
};
|
|
56
68
|
}
|
|
57
69
|
return undefined;
|
|
58
70
|
}
|
|
71
|
+
function isIndex(input) {
|
|
72
|
+
return typeof input === 'number' && Number.isSafeInteger(input) && input >= 0;
|
|
73
|
+
}
|
|
59
74
|
function field(input, key) {
|
|
60
75
|
return input !== null && typeof input === 'object' && !Array.isArray(input)
|
|
61
76
|
? Reflect.get(input, key)
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
export { defineMutation } from './define.js';
|
|
2
2
|
export { executeMutation, type MutationClient, type MutationExecutor } from './execution/index.js';
|
|
3
3
|
export type { MutationFailure } from './failure.js';
|
|
4
|
-
export type { Mutation, MutationDefinition, MutationInputOf, MutationOutputOf } from './mutation.js';
|
|
5
|
-
export type { CreatedNode, EdgeSelector, ExistingEndpoint, MutationEndpoint, QueryPrecondition, RichMutationBuilder, RichPropertyConditions, RichPropertyDelta, RichPropertyInput, RichPropertyName, } from './authoring/index.js';
|
|
6
|
-
/**
|
|
7
|
-
|
|
4
|
+
export type { Mutation, MutationDefinition, MutationInputOf, MutationOutputOf, MutationRecipe, MutationRecipeDefinitions, } from './mutation.js';
|
|
5
|
+
export type { ClosureMutationBuilder, CountBounds, CreatedNode, EdgeSelector, ExistingEndpoint, GuardIdentity, GuardRange, MutationEndpoint, PreconditionOptions, QueryPrecondition, RecipeMutationBuilder, RichMutationBuilder, RichPropertyConditions, RichPropertyDelta, RichPropertyInput, RichPropertyName, } from './authoring/index.js';
|
|
6
|
+
/**
|
|
7
|
+
* Canonical low-level Mutation documents remain available as the explicit escape hatch, listed one
|
|
8
|
+
* by one as the SDK has exported them, so a newer Kernel's Mutation internals never reach it.
|
|
9
|
+
*/
|
|
10
|
+
export { MutationAST, MutationASTDecodeError, MutationResult, MutationResultDecodeError, } from '@astrale-os/kernel-core/graph/mutate';
|
|
11
|
+
export type { EdgeCreateOperation, EdgeDeleteOperation, EdgeUpdateOperation, MutationBuilder, MutationEdgeSelector, MutationInput, MutationInputEdgeSelector, MutationInputEndpoint, MutationInputOperation, MutationInputPrecondition, MutationOperation, MutationPrecondition, MutationPropertyConditions, MutationPropertyConditionsInput, MutationProps, MutationPropsInput, MutationQueryPrecondition, MutationQueryPreconditionInput, MutationStructuralLimits, NodeCreateOperation, NodeDeleteOperation, NodeUpdateOperation, QueryExpectedSelection, } from '@astrale-os/kernel-core/graph/mutate';
|
|
8
12
|
export type { LocalAlias } from '../../platform/graph/alias.js';
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export { defineMutation } from './define.js';
|
|
2
2
|
export { executeMutation } from './execution/index.js';
|
|
3
|
-
/**
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Canonical low-level Mutation documents remain available as the explicit escape hatch, listed one
|
|
5
|
+
* by one as the SDK has exported them, so a newer Kernel's Mutation internals never reach it.
|
|
6
|
+
*/
|
|
7
|
+
export { MutationAST, MutationASTDecodeError, MutationResult, MutationResultDecodeError, } from '@astrale-os/kernel-core/graph/mutate';
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { EdgeSlug } from '@astrale-os/kernel-core/graph/edge';
|
|
2
|
+
import type { QueryEdgeAST, QueryNodeAST } from '@astrale-os/kernel-core/graph/query';
|
|
3
|
+
import type { ResolvedClass } from '@astrale-os/kernel-dsl/v1';
|
|
4
|
+
import type { CreatedNode, ExistingEndpoint, MutationEndpoint } from '../../authoring/endpoint.js';
|
|
5
|
+
import type { CountBounds, GuardIdentity, GuardRange, PreconditionOptions } from '../../authoring/guard.js';
|
|
6
|
+
import type { RichPropertyConditions, RichPropertyDelta, RichPropertyInput } from '../../authoring/properties.js';
|
|
7
|
+
import type { EventOfTopology, StateOfTopology, StatePropertyName, StatePropertyTopology, StateTransitionInput } from '../../authoring/transition.js';
|
|
8
|
+
import type { QueryPrecondition } from './precondition.js';
|
|
9
|
+
/**
|
|
10
|
+
* The builder of the closure form (`defineMutation<Schema>()((domain) => …)`), deprecated: each
|
|
11
|
+
* verb names its Class as resolved, `domain.classes.<Class>`. Each precondition verb (`expect.*`)
|
|
12
|
+
* takes `{ name }` last: a refusal of the Mutation because that precondition fails carries the
|
|
13
|
+
* name as `failure.precondition`. A name is unique in its Mutation.
|
|
14
|
+
*/
|
|
15
|
+
export interface ClosureMutationBuilder {
|
|
16
|
+
readonly expect: {
|
|
17
|
+
/** The Node exists, of this Class, and its Properties meet `props`. */
|
|
18
|
+
node<Class extends ResolvedClass<'node'>>(input: {
|
|
19
|
+
readonly node: ExistingEndpoint;
|
|
20
|
+
readonly class: Class;
|
|
21
|
+
readonly props?: RichPropertyConditions<Class>;
|
|
22
|
+
}, options?: PreconditionOptions): ClosureMutationBuilder;
|
|
23
|
+
/** The Edge exists, its slug is `slug`, and its Properties meet `props`. */
|
|
24
|
+
edge<Class extends ResolvedClass<'edge'>>(input: EdgeSelector<Class> & {
|
|
25
|
+
readonly slug?: EdgeSlug | null;
|
|
26
|
+
readonly props?: RichPropertyConditions<Class>;
|
|
27
|
+
}, options?: PreconditionOptions): ClosureMutationBuilder;
|
|
28
|
+
/**
|
|
29
|
+
* A guard (Mutation v4): no root of the range matches. It takes a range built from the handles
|
|
30
|
+
* `build` receives, `d.<Class>.where(…)`, or a canonical v7 Query.
|
|
31
|
+
*/
|
|
32
|
+
none<S, K extends string>(range: GuardRange<S, K>, options?: PreconditionOptions): ClosureMutationBuilder;
|
|
33
|
+
/** A guard (Mutation v4): a root of the range matches. */
|
|
34
|
+
exists<S, K extends string>(range: GuardRange<S, K>, options?: PreconditionOptions): ClosureMutationBuilder;
|
|
35
|
+
/**
|
|
36
|
+
* A guard (Mutation v4): the number of the range's roots is `bounds`, or lies within
|
|
37
|
+
* `{ atLeast?, atMost? }`, bounded at least once.
|
|
38
|
+
*/
|
|
39
|
+
count<S, K extends string>(range: GuardRange<S, K>, bounds: CountBounds, options?: PreconditionOptions): ClosureMutationBuilder;
|
|
40
|
+
/**
|
|
41
|
+
* A guard (Mutation v4): the range's roots, in its `orderBy`, are exactly these identities, in
|
|
42
|
+
* the order a read of them returned them: a `NodeId` per Node, an `EdgeKey` per Edge.
|
|
43
|
+
*/
|
|
44
|
+
unchanged<S, K extends string>(range: GuardRange<S, K>, ids: readonly GuardIdentity[], options?: PreconditionOptions): ClosureMutationBuilder;
|
|
45
|
+
/**
|
|
46
|
+
* @deprecated Query V6 precondition (Mutation v3): write a guard on a range,
|
|
47
|
+
* `expect.unchanged(range, ids)`. A Mutation holds guards or V6 Query preconditions,
|
|
48
|
+
* never both.
|
|
49
|
+
*/
|
|
50
|
+
query<Query extends QueryNodeAST | QueryEdgeAST>(input: QueryPrecondition<Query>): ClosureMutationBuilder;
|
|
51
|
+
};
|
|
52
|
+
createNode<Class extends ResolvedClass<'node'>>(input: {
|
|
53
|
+
readonly as: string;
|
|
54
|
+
readonly class: Class;
|
|
55
|
+
readonly props?: RichPropertyInput<Class>;
|
|
56
|
+
}): CreatedNode<Class>;
|
|
57
|
+
updateNode<Class extends ResolvedClass<'node'>>(input: {
|
|
58
|
+
readonly node: ExistingEndpoint;
|
|
59
|
+
readonly class: Class;
|
|
60
|
+
readonly props: RichPropertyDelta<Class>;
|
|
61
|
+
}): this;
|
|
62
|
+
transition<Class extends ResolvedClass<'node'>, Property extends StatePropertyName<Class>, From extends StateOfTopology<StatePropertyTopology<Class, Property>>, Event extends EventOfTopology<StatePropertyTopology<Class, Property>, From>>(input: StateTransitionInput<Class, Property, From, Event>): this;
|
|
63
|
+
deleteNode<Class extends ResolvedClass<'node'>>(input: {
|
|
64
|
+
readonly node: ExistingEndpoint;
|
|
65
|
+
readonly class: Class;
|
|
66
|
+
}): this;
|
|
67
|
+
createEdge<Class extends ResolvedClass<'edge'>>(input: {
|
|
68
|
+
readonly class: Class;
|
|
69
|
+
readonly source: MutationEndpoint;
|
|
70
|
+
readonly target: MutationEndpoint;
|
|
71
|
+
readonly slug?: EdgeSlug;
|
|
72
|
+
readonly props?: RichPropertyInput<Class>;
|
|
73
|
+
}): this;
|
|
74
|
+
updateEdge<Class extends ResolvedClass<'edge'>>(input: EdgeSelector<Class> & {
|
|
75
|
+
readonly slug?: EdgeSlug | null;
|
|
76
|
+
readonly props?: RichPropertyDelta<Class>;
|
|
77
|
+
}): this;
|
|
78
|
+
deleteEdge<Class extends ResolvedClass<'edge'>>(input: EdgeSelector<Class>): this;
|
|
79
|
+
}
|
|
80
|
+
export interface EdgeSelector<Class extends ResolvedClass<'edge'>> {
|
|
81
|
+
readonly source: ExistingEndpoint;
|
|
82
|
+
readonly target: ExistingEndpoint;
|
|
83
|
+
readonly class: Class;
|
|
84
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { MutationResult } from '@astrale-os/kernel-core/graph/mutate';
|
|
2
|
+
import type { Domain, schema } from '../../../../platform/schema/index.js';
|
|
3
|
+
import type { Handles } from '../../../query/handles/handles.js';
|
|
4
|
+
import type { Mutation, MutationDefinition } from './mutation.js';
|
|
5
|
+
type ResolvedDomainOf<Schema extends schema.DomainSchema> = Extract<schema.DomainOf<Schema>, Domain>;
|
|
6
|
+
type AuthoringDomainOf<Schema extends schema.DomainSchema> = ResolvedDomainOf<Schema>;
|
|
7
|
+
export interface MutationProjector<Schema extends schema.DomainSchema> {
|
|
8
|
+
/**
|
|
9
|
+
* @deprecated The closure form: define the Mutation as a recipe,
|
|
10
|
+
* `defineMutation<Schema>()(id, { build(input, d, mutation), output?, reject?, errors? })`. A
|
|
11
|
+
* Mutation of this form called without `as` writes as the executor.
|
|
12
|
+
*/
|
|
13
|
+
<Input, Output = MutationResult>(project: (domain: AuthoringDomainOf<Schema>) => MutationDefinition<Input, Output, Handles<Schema>>): Mutation<ResolvedDomainOf<Schema>, Input, Output>;
|
|
14
|
+
}
|
|
15
|
+
/** Define one inert Mutation of the closure form, projected from its exact loaded Domain. */
|
|
16
|
+
export declare function defineMutation<Schema extends schema.DomainSchema>(): MutationProjector<Schema>;
|
|
17
|
+
export declare function realizeMutation<DomainValue extends Domain, Input, Output>(mutation: Mutation<DomainValue, Input, Output>, domain: DomainValue): MutationDefinition<Input, Output>;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { isDomain } from '@astrale-os/kernel-dsl/v1/domain';
|
|
2
|
+
const projectors = new WeakMap();
|
|
3
|
+
const realized = new WeakMap();
|
|
4
|
+
/** Define one inert Mutation of the closure form, projected from its exact loaded Domain. */
|
|
5
|
+
export function defineMutation() {
|
|
6
|
+
return ((project) => {
|
|
7
|
+
if (typeof project !== 'function')
|
|
8
|
+
throw new TypeError('Mutation projection must be a function.');
|
|
9
|
+
const mutation = Object.freeze({ kind: 'mutation' });
|
|
10
|
+
projectors.set(mutation, project);
|
|
11
|
+
return mutation;
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
export function realizeMutation(mutation, domain) {
|
|
15
|
+
const project = projectors.get(mutation);
|
|
16
|
+
if (project === undefined)
|
|
17
|
+
throw new TypeError('Mutation must be produced by defineMutation.');
|
|
18
|
+
if (!isDomain(domain))
|
|
19
|
+
throw new TypeError('Mutation projection requires an admitted Domain.');
|
|
20
|
+
let byDomain = realized.get(mutation);
|
|
21
|
+
const cached = byDomain?.get(domain);
|
|
22
|
+
if (cached !== undefined)
|
|
23
|
+
return cached;
|
|
24
|
+
const authored = project(domain);
|
|
25
|
+
if (isPromiseLike(authored))
|
|
26
|
+
throw new TypeError('Mutation projection must be synchronous.');
|
|
27
|
+
const admitted = admitMutation(authored);
|
|
28
|
+
if (byDomain === undefined) {
|
|
29
|
+
byDomain = new WeakMap();
|
|
30
|
+
realized.set(mutation, byDomain);
|
|
31
|
+
}
|
|
32
|
+
byDomain.set(domain, admitted);
|
|
33
|
+
return admitted;
|
|
34
|
+
}
|
|
35
|
+
function admitMutation(definition) {
|
|
36
|
+
if (definition === null || typeof definition !== 'object' || Array.isArray(definition))
|
|
37
|
+
invalid();
|
|
38
|
+
if (typeof definition.id !== 'string' ||
|
|
39
|
+
definition.id.length === 0 ||
|
|
40
|
+
definition.id.trim() !== definition.id ||
|
|
41
|
+
typeof definition.build !== 'function' ||
|
|
42
|
+
(definition.project !== undefined && typeof definition.project !== 'function') ||
|
|
43
|
+
(definition.reject !== undefined && typeof definition.reject !== 'function') ||
|
|
44
|
+
Reflect.ownKeys(definition).some((key) => key !== 'id' && key !== 'build' && key !== 'project' && key !== 'reject')) {
|
|
45
|
+
invalid();
|
|
46
|
+
}
|
|
47
|
+
return Object.freeze({ ...definition });
|
|
48
|
+
}
|
|
49
|
+
function invalid() {
|
|
50
|
+
throw new TypeError('Mutation definition is invalid.');
|
|
51
|
+
}
|
|
52
|
+
function isPromiseLike(input) {
|
|
53
|
+
return (input !== null &&
|
|
54
|
+
(typeof input === 'object' || typeof input === 'function') &&
|
|
55
|
+
typeof input.then === 'function');
|
|
56
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { MutationResult } from '@astrale-os/kernel-core/graph/mutate';
|
|
2
|
+
import type { Domain } from '../../../../platform/schema/index.js';
|
|
3
|
+
import type { MutationFailure } from '../../failure.js';
|
|
4
|
+
import type { ClosureMutationBuilder } from './builder.js';
|
|
5
|
+
declare const MUTATION_DOMAIN: unique symbol;
|
|
6
|
+
declare const MUTATION_INPUT: unique symbol;
|
|
7
|
+
declare const MUTATION_OUTPUT: unique symbol;
|
|
8
|
+
/**
|
|
9
|
+
* A Mutation of the closure form, deprecated: inert and Domain-bound. Its projector is retained
|
|
10
|
+
* privately by the SDK.
|
|
11
|
+
*/
|
|
12
|
+
export interface Mutation<DomainValue extends Domain = Domain, Input = unknown, Output = MutationResult> {
|
|
13
|
+
readonly kind: 'mutation';
|
|
14
|
+
readonly [MUTATION_DOMAIN]: (domain: DomainValue) => DomainValue;
|
|
15
|
+
readonly [MUTATION_INPUT]?: Input;
|
|
16
|
+
readonly [MUTATION_OUTPUT]?: Output;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Canonical admitted operation produced by realizing one Mutation of the closure form. `build`
|
|
20
|
+
* receives the handles `D` a recipe's `build` does, for the ranges its guards take.
|
|
21
|
+
*/
|
|
22
|
+
export interface MutationDefinition<Input, Output = MutationResult, D = unknown> {
|
|
23
|
+
readonly id: string;
|
|
24
|
+
build(input: Input, mutation: ClosureMutationBuilder, d: D): undefined;
|
|
25
|
+
project?(result: MutationResult, input: NoInfer<Input>): Output;
|
|
26
|
+
reject?(failure: MutationFailure): Output;
|
|
27
|
+
}
|
|
28
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { MutationBuilder } from '@astrale-os/kernel-core/graph/mutate';
|
|
2
2
|
import type { QueryEdgeAST, QueryNodeAST, QueryResultFor, QuerySelectionFor } from '@astrale-os/kernel-core/graph/query';
|
|
3
|
+
/**
|
|
4
|
+
* @deprecated Query V6 precondition (Mutation v3): write a guard on a Query v7,
|
|
5
|
+
* `expect.unchanged(query, identities)`, `expect.none`, `expect.exists` or `expect.count`.
|
|
6
|
+
*/
|
|
3
7
|
export interface QueryPrecondition<Query extends QueryNodeAST | QueryEdgeAST> {
|
|
4
8
|
readonly query: Query;
|
|
5
9
|
readonly equals: QuerySelectionFor<QueryResultFor<Query>>;
|
|
@@ -1,24 +1,35 @@
|
|
|
1
1
|
import type { MutationResult } from '@astrale-os/kernel-core/graph/mutate';
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
2
|
+
import type { Handles } from '../query/handles/handles.js';
|
|
3
|
+
import type { Errors, OutputContext } from '../query/recipe/recipe.js';
|
|
4
|
+
import type { RecipeMutationBuilder } from './authoring/builder.js';
|
|
4
5
|
import type { MutationFailure } from './failure.js';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
import type { Mutation, MutationDefinition } from './legacy/v6/mutation.js';
|
|
7
|
+
export type { Mutation, MutationDefinition } from './legacy/v6/mutation.js';
|
|
8
|
+
/**
|
|
9
|
+
* A Mutation of the recipe form (D31): inert, its id and the errors its `reject` may raise are
|
|
10
|
+
* public, and what it writes is kept for the runtime. A call names its authority,
|
|
11
|
+
* `mutate(recipe, input, { as: 'executor' | 'caller' })`.
|
|
12
|
+
*/
|
|
13
|
+
export interface MutationRecipe<Id extends string = string, Input = unknown, Output = unknown, E extends Errors = Errors> {
|
|
10
14
|
readonly kind: 'mutation';
|
|
11
|
-
readonly
|
|
12
|
-
readonly
|
|
13
|
-
readonly
|
|
15
|
+
readonly id: Id;
|
|
16
|
+
readonly errors: E;
|
|
17
|
+
readonly '~input'?: Input;
|
|
18
|
+
readonly '~output'?: Output;
|
|
14
19
|
}
|
|
15
|
-
/**
|
|
16
|
-
export interface
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
/** `defineMutation<Schema>()`'s recipe form: a string id first. */
|
|
21
|
+
export interface MutationRecipeDefinitions<S> {
|
|
22
|
+
/**
|
|
23
|
+
* `build` writes the Mutation with the builder, naming each Class by its handle, `d.<Class>`;
|
|
24
|
+
* `output` maps the committed result, `reject` an expected refusal, and may raise a declared
|
|
25
|
+
* error through `context.error`, which a Function that calls the Mutation re-declares.
|
|
26
|
+
*/
|
|
27
|
+
<const Id extends string, Input, Output = MutationResult, E extends Errors = {}>(id: Id, spec: {
|
|
28
|
+
readonly build: (input: Input, d: Handles<S>, mutation: RecipeMutationBuilder<S>) => undefined;
|
|
29
|
+
readonly output?: (result: MutationResult, input: Input) => Output;
|
|
30
|
+
readonly reject?: (failure: MutationFailure, input: Input, context: OutputContext<S, E>) => NoInfer<Output>;
|
|
31
|
+
readonly errors?: E;
|
|
32
|
+
}): MutationRecipe<Id, Input, Output, E>;
|
|
21
33
|
}
|
|
22
|
-
export type MutationInputOf<Value> = Value extends Mutation<infer _Domain, infer Input, infer _Output> ? Input : Value extends MutationDefinition<infer Input, infer _Output> ? Input : never;
|
|
23
|
-
export type MutationOutputOf<Value> = Value extends Mutation<infer _Domain, infer _Input, infer Output> ? Output : Value extends MutationDefinition<infer _Input, infer Output> ? Output : never;
|
|
24
|
-
export {};
|
|
34
|
+
export type MutationInputOf<Value> = Value extends MutationRecipe<string, infer Input, infer _Output, Errors> ? Input : Value extends Mutation<infer _Domain, infer Input, infer _Output> ? Input : Value extends MutationDefinition<infer Input, infer _Output> ? Input : never;
|
|
35
|
+
export type MutationOutputOf<Value> = Value extends MutationRecipe<string, infer _Input, infer Output, Errors> ? Output : Value extends Mutation<infer _Domain, infer _Input, infer Output> ? Output : Value extends MutationDefinition<infer _Input, infer Output> ? Output : never;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a v7 Query answers (page "Fondations Query v7", § Résultats), typed by its Query: the
|
|
3
|
+
* Kernel's answer as it arrives, never reshaped. A list answers a tree of references (its Page,
|
|
4
|
+
* each item's Branches) and one table of the facts it renders as values; an `exists` a boolean;
|
|
5
|
+
* a `count` a bounded number.
|
|
6
|
+
*
|
|
7
|
+
* The types read the Query's own JSON type: the Branch names, their modes and their nesting give
|
|
8
|
+
* the shape of every item; the Domain gives the type of every value by its Property key. A fact
|
|
9
|
+
* carries no Class, so a value is typed by its key alone; a Node item carries its Class (K-C),
|
|
10
|
+
* which a Kernel before K-C leaves out.
|
|
11
|
+
*/
|
|
12
|
+
import type { EdgeKey } from '../../../platform/graph/edge/index.js';
|
|
13
|
+
import type { NodeId } from '../../../platform/graph/node/index.js';
|
|
14
|
+
import type { QueryPropertyKey, QueryPropertyValues, QueryRendering } from '../language/query.js';
|
|
15
|
+
/** On request (W8): the roots the caller's view cannot decide, up to the fold's bound. */
|
|
16
|
+
export interface QueryUndecided {
|
|
17
|
+
readonly count: number;
|
|
18
|
+
readonly more: boolean;
|
|
19
|
+
}
|
|
20
|
+
/** One Page of a list: the exact prefix of its order, and the cursor that continues it (W3). */
|
|
21
|
+
export type QueryPageOf<Item> = {
|
|
22
|
+
readonly items: readonly Item[];
|
|
23
|
+
readonly undecided?: QueryUndecided;
|
|
24
|
+
} & ({
|
|
25
|
+
readonly complete: true;
|
|
26
|
+
} | {
|
|
27
|
+
readonly complete: false;
|
|
28
|
+
readonly cursor: string;
|
|
29
|
+
});
|
|
30
|
+
/** Bounded so a Query typed only by its constraint still resolves: 8 nested lists. */
|
|
31
|
+
type Depth = readonly unknown[];
|
|
32
|
+
type Deeper<D extends Depth> = readonly [...D, 0];
|
|
33
|
+
type Branches<W> = W extends readonly unknown[] ? W[number] : never;
|
|
34
|
+
/** The answer of one Branch: a `count`, or the Page of its list. */
|
|
35
|
+
export type QueryBranchAnswer<B, D extends Depth = []> = B extends {
|
|
36
|
+
readonly mode: 'count';
|
|
37
|
+
} ? {
|
|
38
|
+
readonly count: number;
|
|
39
|
+
readonly more: boolean;
|
|
40
|
+
readonly undecided?: QueryUndecided;
|
|
41
|
+
} : QueryPageOf<QueryBranchItem<B, D>>;
|
|
42
|
+
/** The Branches of an item, by name. */
|
|
43
|
+
export type QueryBranchesOf<W, D extends Depth = []> = D['length'] extends 8 ? {} : {
|
|
44
|
+
readonly [B in Branches<W> as B extends {
|
|
45
|
+
readonly name: infer N extends string;
|
|
46
|
+
} ? N : never]: QueryBranchAnswer<B, Deeper<D>>;
|
|
47
|
+
};
|
|
48
|
+
/** One item of a Branch list: the Node it reached, the Edge when the Branch selects it. */
|
|
49
|
+
export type QueryBranchItem<B, D extends Depth = []> = {
|
|
50
|
+
readonly fact: NodeId;
|
|
51
|
+
readonly class?: string;
|
|
52
|
+
readonly branches: QueryBranchesOf<B extends {
|
|
53
|
+
readonly with?: infer W;
|
|
54
|
+
} ? W : never, D>;
|
|
55
|
+
} & (B extends {
|
|
56
|
+
readonly selectEdge: QueryRendering;
|
|
57
|
+
} ? {
|
|
58
|
+
readonly edge: EdgeKey;
|
|
59
|
+
} : {
|
|
60
|
+
readonly edge?: never;
|
|
61
|
+
});
|
|
62
|
+
/** One item of the root list: a Node with its Branches, or an Edge with its rendered ends. */
|
|
63
|
+
export type QueryRootItem<R> = R extends {
|
|
64
|
+
readonly nodes: unknown;
|
|
65
|
+
} ? {
|
|
66
|
+
readonly fact: NodeId;
|
|
67
|
+
readonly class?: string;
|
|
68
|
+
readonly branches: QueryBranchesOf<R extends {
|
|
69
|
+
readonly with?: infer W;
|
|
70
|
+
} ? W : never>;
|
|
71
|
+
} : {
|
|
72
|
+
readonly fact: EdgeKey;
|
|
73
|
+
readonly source?: {
|
|
74
|
+
readonly fact: NodeId;
|
|
75
|
+
readonly class?: string;
|
|
76
|
+
readonly branches: {};
|
|
77
|
+
};
|
|
78
|
+
readonly target?: {
|
|
79
|
+
readonly fact: NodeId;
|
|
80
|
+
readonly class?: string;
|
|
81
|
+
readonly branches: {};
|
|
82
|
+
};
|
|
83
|
+
readonly branches: {};
|
|
84
|
+
};
|
|
85
|
+
/** One fact a list renders as values: its readable values and the keys it withholds (R9). */
|
|
86
|
+
export interface QueryFact<Source> {
|
|
87
|
+
readonly fact: NodeId | EdgeKey;
|
|
88
|
+
/** By Property key; a key the Domain does not define may appear (a newer Schema). */
|
|
89
|
+
readonly values: QueryPropertyValues<Source>;
|
|
90
|
+
readonly withheld: readonly QueryPropertyKey<Source>[];
|
|
91
|
+
}
|
|
92
|
+
/** The answer of a `list`: its Page, the facts it renders, the revision it read (W6). */
|
|
93
|
+
export interface QueryListAnswer<Source, Q> {
|
|
94
|
+
readonly list: QueryPageOf<QueryRootItem<Q extends {
|
|
95
|
+
readonly list: infer R;
|
|
96
|
+
} ? R : never>>;
|
|
97
|
+
readonly facts: readonly QueryFact<Source>[];
|
|
98
|
+
readonly revision: string;
|
|
99
|
+
/** A continuation crossed a revision: its Page reads a later snapshot than its first. */
|
|
100
|
+
readonly revised?: true;
|
|
101
|
+
}
|
|
102
|
+
export interface QueryExistsAnswer {
|
|
103
|
+
readonly exists: boolean;
|
|
104
|
+
readonly undecided?: boolean;
|
|
105
|
+
}
|
|
106
|
+
export interface QueryCountAnswer {
|
|
107
|
+
readonly count: number;
|
|
108
|
+
readonly more: boolean;
|
|
109
|
+
readonly undecided?: QueryUndecided;
|
|
110
|
+
}
|
|
111
|
+
/** What a Query answers, by its family (the name of its root fold). */
|
|
112
|
+
export type QueryAnswerOf<Source, Q> = Q extends {
|
|
113
|
+
readonly list: unknown;
|
|
114
|
+
} ? QueryListAnswer<Source, Q> : Q extends {
|
|
115
|
+
readonly exists: unknown;
|
|
116
|
+
} ? QueryExistsAnswer : QueryCountAnswer;
|
|
117
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Domain } from '../../../platform/schema/index.js';
|
|
2
|
+
import type { QueryListAnswer } from './answer.js';
|
|
3
|
+
/** Throw `QueryValueMismatchError` for the first value that does not match its Property. */
|
|
4
|
+
export declare function requireAnswerConformance(domain: Domain, answer: QueryListAnswer<unknown, unknown>): void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { definedClass } from '@astrale-os/kernel-client/graph';
|
|
2
|
+
import { QueryValueMismatchError } from '../failure.js';
|
|
3
|
+
import { factKey } from './facts.js';
|
|
4
|
+
const PROPERTY = '.property.';
|
|
5
|
+
/** The Property a key names in `domain`, or `undefined` when `domain` does not define it. */
|
|
6
|
+
function propertyOf(domain, key) {
|
|
7
|
+
const at = key.lastIndexOf(PROPERTY);
|
|
8
|
+
if (at <= 0)
|
|
9
|
+
return undefined;
|
|
10
|
+
const owner = definedClass(domain, key.slice(0, at));
|
|
11
|
+
return owner?.properties.get(key);
|
|
12
|
+
}
|
|
13
|
+
/** Throw `QueryValueMismatchError` for the first value that does not match its Property. */
|
|
14
|
+
export function requireAnswerConformance(domain, answer) {
|
|
15
|
+
for (const fact of answer.facts) {
|
|
16
|
+
const values = fact.values;
|
|
17
|
+
for (const key of Object.keys(values)) {
|
|
18
|
+
const property = propertyOf(domain, key);
|
|
19
|
+
if (property === undefined)
|
|
20
|
+
continue;
|
|
21
|
+
const [diagnostic] = property.validate(values[key]);
|
|
22
|
+
if (diagnostic !== undefined) {
|
|
23
|
+
throw new QueryValueMismatchError(`The value of ${key} on ${factKey(fact.fact)} does not match its Property in this Domain: ${diagnostic.message}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The facts table of a list answer, by identity: one index per answer, and the Pages of a root
|
|
3
|
+
* list joined into one complete answer.
|
|
4
|
+
*/
|
|
5
|
+
import type { QueryFact, QueryListAnswer } from './answer.js';
|
|
6
|
+
type FactId = QueryFact<unknown>['fact'];
|
|
7
|
+
/** The text that names a fact: a NodeId as itself, an EdgeKey by its fields in key order. */
|
|
8
|
+
export declare function factKey(fact: FactId): string;
|
|
9
|
+
/** The facts of a list answer by identity, indexed once per answer. */
|
|
10
|
+
export declare function factsOf(answer: QueryListAnswer<unknown, unknown>): ReadonlyMap<string, QueryFact<unknown>>;
|
|
11
|
+
/**
|
|
12
|
+
* The Pages of one root list, joined: their items in order, their facts by identity (a later
|
|
13
|
+
* Page's row wins: it was read at a later revision), the last Page's revision, `revised` when a
|
|
14
|
+
* continuation crossed a revision. The last Page is complete; Branch lists stay as cut.
|
|
15
|
+
*/
|
|
16
|
+
export declare function joinPages<A extends QueryListAnswer<unknown, unknown>>(pages: readonly [A, ...A[]]): A;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** The text that names a fact: a NodeId as itself, an EdgeKey by its fields in key order. */
|
|
2
|
+
export function factKey(fact) {
|
|
3
|
+
if (typeof fact === 'string')
|
|
4
|
+
return fact;
|
|
5
|
+
const fields = fact;
|
|
6
|
+
return JSON.stringify(Object.keys(fields)
|
|
7
|
+
.sort()
|
|
8
|
+
.map((name) => [name, fields[name]]));
|
|
9
|
+
}
|
|
10
|
+
const indexes = new WeakMap();
|
|
11
|
+
/** The facts of a list answer by identity, indexed once per answer. */
|
|
12
|
+
export function factsOf(answer) {
|
|
13
|
+
const known = indexes.get(answer);
|
|
14
|
+
if (known !== undefined)
|
|
15
|
+
return known;
|
|
16
|
+
const index = new Map();
|
|
17
|
+
for (const fact of answer.facts)
|
|
18
|
+
index.set(factKey(fact.fact), fact);
|
|
19
|
+
indexes.set(answer, index);
|
|
20
|
+
return index;
|
|
21
|
+
}
|
|
22
|
+
function undecidedOf(pages) {
|
|
23
|
+
const reported = pages.flatMap((page) => page.list.undecided === undefined ? [] : [page.list.undecided]);
|
|
24
|
+
if (reported.length === 0)
|
|
25
|
+
return undefined;
|
|
26
|
+
return Object.freeze({
|
|
27
|
+
count: reported.reduce((total, undecided) => total + undecided.count, 0),
|
|
28
|
+
more: reported.some((undecided) => undecided.more),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The Pages of one root list, joined: their items in order, their facts by identity (a later
|
|
33
|
+
* Page's row wins: it was read at a later revision), the last Page's revision, `revised` when a
|
|
34
|
+
* continuation crossed a revision. The last Page is complete; Branch lists stay as cut.
|
|
35
|
+
*/
|
|
36
|
+
export function joinPages(pages) {
|
|
37
|
+
if (pages.length === 1)
|
|
38
|
+
return pages[0];
|
|
39
|
+
const last = pages[pages.length - 1];
|
|
40
|
+
const facts = new Map();
|
|
41
|
+
for (const page of pages)
|
|
42
|
+
for (const fact of page.facts)
|
|
43
|
+
facts.set(factKey(fact.fact), fact);
|
|
44
|
+
const undecided = undecidedOf(pages);
|
|
45
|
+
const revised = pages.some((page) => page.revised === true) ||
|
|
46
|
+
pages.some((page) => page.revision !== pages[0].revision);
|
|
47
|
+
return Object.freeze({
|
|
48
|
+
list: Object.freeze({
|
|
49
|
+
items: Object.freeze(pages.flatMap((page) => page.list.items)),
|
|
50
|
+
complete: true,
|
|
51
|
+
...(undecided === undefined ? {} : { undecided }),
|
|
52
|
+
}),
|
|
53
|
+
facts: Object.freeze([...facts.values()]),
|
|
54
|
+
revision: last.revision,
|
|
55
|
+
...(revised ? { revised: true } : {}),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Domain } from '../../../platform/schema/index.js';
|
|
2
|
-
import type { CollectionDefinitionRecord } from '../collection/union.js';
|
|
2
|
+
import type { CollectionDefinitionRecord } from '../legacy/v6/collection/union.js';
|
|
3
3
|
import type { SingleQueryDefinition } from '../query.js';
|
|
4
4
|
import type { QueryComposition, QueryPlan } from './query.js';
|
|
5
5
|
declare const PLAN: unique symbol;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { admitCollectionDefinitions } from '../collection/union.js';
|
|
1
|
+
import { admitCollectionDefinitions } from '../legacy/v6/collection/union.js';
|
|
2
2
|
import { QueryCompositionError } from './errors.js';
|
|
3
3
|
const PLAN = Symbol('astrale.sdk.query.plan');
|
|
4
4
|
const REFERENCE = Symbol('astrale.sdk.query.reference');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Domain } from '../../../platform/schema/index.js';
|
|
2
|
-
import type { CollectionDefinitionRecord } from '../collection/union.js';
|
|
2
|
+
import type { CollectionDefinitionRecord } from '../legacy/v6/collection/union.js';
|
|
3
3
|
import type { SingleQueryDefinition } from '../query.js';
|
|
4
4
|
import type { InternalPlan } from './builder.js';
|
|
5
5
|
export declare const QUERY_COMPOSITION_LIMITS: Readonly<{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { COLLECTION_QUERY_LIMITS } from '../collection/limits.js';
|
|
1
|
+
import { COLLECTION_QUERY_LIMITS } from '../legacy/v6/collection/limits.js';
|
|
2
2
|
import { inspectPlan, referencesIn, resolveInput } from './builder.js';
|
|
3
3
|
import { QueryCompositionError } from './errors.js';
|
|
4
4
|
export const QUERY_COMPOSITION_LIMITS = Object.freeze({
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { ResolvedClass } from '@astrale-os/kernel-dsl/v1';
|
|
2
2
|
import type { Domain } from '../../../platform/schema/index.js';
|
|
3
|
-
import type { CollectionQueryDefinition, QueryOutputOf, SingleQueryDefinition } from '../query.js';
|
|
4
|
-
import type { QueryLifecycle } from '../single/index.js';
|
|
3
|
+
import type { CollectionQueryDefinition, QueryLifecycle, QueryOutputOf, SingleQueryDefinition } from '../query.js';
|
|
5
4
|
declare const QUERY_PLAN_OUTPUT: unique symbol;
|
|
6
5
|
declare const QUERY_PLAN_VALUE: unique symbol;
|
|
7
6
|
export type QueryPlanValue<Value> = Readonly<{
|