@aventara/core 1.0.0-rc.0 → 1.0.0-rc.1
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 +1140 -0
- package/README.md +224 -57
- package/dist/adapters/adapter-discovery-diagnostic.d.ts +4 -4
- package/dist/adapters/adapter-field.d.ts +11 -11
- package/dist/adapters/adapter-identifier.d.ts +6 -6
- package/dist/adapters/adapter-model.d.ts +9 -9
- package/dist/adapters/adapter-operation-executor.interface.d.ts +4 -4
- package/dist/adapters/adapter-operation.d.ts +3 -3
- package/dist/adapters/adapter-resource.d.ts +18 -9
- package/dist/adapters/adapter-sql-statement.d.ts +19 -0
- package/dist/adapters/adapter-transaction-executor.interface.d.ts +19 -0
- package/dist/adapters/adapter.interface.d.ts +49 -29
- package/dist/avz/avz-check.factory.d.ts +83 -0
- package/dist/avz/avz-check.factory.js +116 -0
- package/dist/avz/avz-check.registry.d.ts +35 -0
- package/dist/avz/avz-check.registry.js +9 -0
- package/dist/avz/avz.d.ts +203 -0
- package/dist/{json → avz}/avz.js +150 -8
- package/dist/{json → avz}/index.d.ts +2 -1
- package/dist/{json → avz}/json-shape.registry.d.ts +7 -7
- package/dist/{json → avz}/zod-issue.translator.d.ts +8 -2
- package/dist/{json → avz}/zod-issue.translator.js +7 -0
- package/dist/compiler/contract-compiler-diagnostic.d.ts +31 -5
- package/dist/compiler/contract.compiler.d.ts +18 -18
- package/dist/compiler/contract.compiler.js +548 -441
- package/dist/compiler/create-requirements.projector.d.ts +49 -0
- package/dist/compiler/create-requirements.projector.js +423 -0
- package/dist/compiler/field-validator.compiler.d.ts +47 -0
- package/dist/compiler/field-validator.compiler.js +238 -0
- package/dist/config/any-framework-config.d.ts +19 -12
- package/dist/config/config-diagnostic.d.ts +11 -4
- package/dist/config/config-layer.d.ts +15 -6
- package/dist/config/config.resolver.d.ts +74 -31
- package/dist/config/config.resolver.js +108 -44
- package/dist/config/config.validator.d.ts +7 -7
- package/dist/config/config.validator.js +581 -72
- package/dist/config/define-resource.d.ts +57 -0
- package/dist/config/define-resource.js +14 -0
- package/dist/config/effective-fields.d.ts +19 -23
- package/dist/config/entrypoint.normalizer.d.ts +1 -1
- package/dist/config/exact-framework-config.d.ts +280 -0
- package/dist/config/field-compute.resolver.d.ts +19 -0
- package/dist/config/field-compute.resolver.js +115 -0
- package/dist/config/field-pipeline-config.d.ts +47 -0
- package/dist/config/forced-operation.validator.d.ts +10 -0
- package/dist/config/forced-operation.validator.js +66 -0
- package/dist/config/framework-config.d.ts +110 -39
- package/dist/config/framework-exemptions-config.d.ts +91 -0
- package/dist/config/framework-exemptions-config.js +1 -0
- package/dist/config/framework-field-definition.d.ts +78 -0
- package/dist/config/framework-field-definition.js +28 -0
- package/dist/config/framework-resources-config.d.ts +116 -0
- package/dist/config/framework-resources-config.js +1 -0
- package/dist/config/pipeline-config.d.ts +56 -20
- package/dist/config/pipeline-tree.resolver.d.ts +79 -0
- package/dist/config/pipeline-tree.resolver.js +220 -0
- package/dist/config/stage-type.eraser.d.ts +65 -0
- package/dist/config/stage-type.eraser.js +1 -0
- package/dist/contracts/capability.d.ts +11 -11
- package/dist/contracts/client-contract-structure.validator.d.ts +323 -28
- package/dist/contracts/client-contract-structure.validator.js +152 -8
- package/dist/contracts/contract-canonicalizer.d.ts +19 -3
- package/dist/contracts/contract-canonicalizer.js +141 -28
- package/dist/contracts/contract-hash.d.ts +12 -7
- package/dist/contracts/contract-hash.js +3 -2
- package/dist/contracts/contract-limits.d.ts +4 -1
- package/dist/contracts/contract-limits.js +1 -0
- package/dist/contracts/contract-lookup.d.ts +5 -5
- package/dist/contracts/contract-protocol.d.ts +4 -4
- package/dist/contracts/contract.d.ts +24 -16
- package/dist/contracts/create-requirements.d.ts +35 -0
- package/dist/contracts/create-requirements.js +1 -0
- package/dist/contracts/enum-definition.d.ts +1 -1
- package/dist/contracts/field-check.d.ts +101 -0
- package/dist/contracts/field-check.js +1 -0
- package/dist/contracts/field.d.ts +52 -42
- package/dist/contracts/identifier.d.ts +4 -4
- package/dist/contracts/json-shape.d.ts +12 -1
- package/dist/contracts/lifecycle-trait.d.ts +2 -2
- package/dist/contracts/resource-operations.d.ts +28 -28
- package/dist/contracts/resource.d.ts +15 -9
- package/dist/contracts/scalar-value-type.d.ts +15 -15
- package/dist/contracts/scalar.d.ts +3 -3
- package/dist/contracts/transaction-capability.d.ts +1 -1
- package/dist/contracts/wire-scalar-format.d.ts +4 -4
- package/dist/framework/contract-view.d.ts +213 -147
- package/dist/framework/framework-construction.error.d.ts +10 -10
- package/dist/framework/framework-construction.error.js +2 -2
- package/dist/framework/framework.d.ts +47 -23
- package/dist/framework/framework.factory.d.ts +8 -6
- package/dist/framework/framework.factory.js +40 -20
- package/dist/framework/operation-facade.d.ts +123 -52
- package/dist/framework/operation-facade.js +9 -1
- package/dist/index.d.ts +55 -50
- package/dist/index.js +6 -6
- package/dist/internal.d.ts +15 -10
- package/dist/internal.js +3 -1
- package/dist/operations/operation-arguments.d.ts +267 -202
- package/dist/operations/operation-call.d.ts +27 -28
- package/dist/operations/operation-identity.d.ts +10 -10
- package/dist/operations/operation-result.d.ts +21 -21
- package/dist/operations/operation-validator-core.d.ts +6 -6
- package/dist/operations/operation-validator-core.js +13 -2
- package/dist/operations/operation-validator.d.ts +9 -9
- package/dist/operations/projection-scope.resolver.d.ts +6 -6
- package/dist/operations/structural-limits.walker.d.ts +6 -6
- package/dist/operations/structural-limits.walker.js +145 -0
- package/dist/operations/validation-code.d.ts +11 -6
- package/dist/operations/validation-code.js +4 -0
- package/dist/operations/write-scope.resolver.d.ts +6 -6
- package/dist/protocol/absent-route.responder.d.ts +13 -12
- package/dist/protocol/absent-route.responder.js +19 -19
- package/dist/protocol/contract-document.responder.d.ts +3 -3
- package/dist/protocol/contract-document.responder.js +2 -2
- package/dist/protocol/http-request.decoder.d.ts +17 -13
- package/dist/protocol/http-request.decoder.js +10 -10
- package/dist/protocol/http-request.reader.d.ts +3 -3
- package/dist/protocol/http-response.encoder.d.ts +4 -4
- package/dist/protocol/http-response.encoder.js +94 -55
- package/dist/protocol/http-status.table.d.ts +2 -2
- package/dist/protocol/operation-response.guard.d.ts +2 -2
- package/dist/protocol/protocol-surface.d.ts +4 -4
- package/dist/protocol/protocol.binder.d.ts +50 -27
- package/dist/protocol/protocol.binder.js +12 -9
- package/dist/protocol/protocol.handler.d.ts +22 -13
- package/dist/protocol/protocol.handler.js +44 -9
- package/dist/protocol/unmounted-route.error.d.ts +4 -4
- package/dist/protocol/unmounted-route.error.js +2 -2
- package/dist/runtime/caller-data.copier.d.ts +5 -5
- package/dist/runtime/caller-data.copier.js +13 -13
- package/dist/runtime/caller-data.door.d.ts +6 -6
- package/dist/runtime/caller-data.door.js +6 -4
- package/dist/runtime/computed-read-runtime.d.ts +7 -6
- package/dist/runtime/computed-read-runtime.js +61 -48
- package/dist/runtime/computed-value.error.d.ts +6 -6
- package/dist/runtime/computed-value.error.js +3 -3
- package/dist/runtime/context-executor.runtime.d.ts +59 -0
- package/dist/runtime/context-executor.runtime.js +275 -0
- package/dist/runtime/create-requirement.d.ts +10 -81
- package/dist/runtime/create-requirement.js +80 -57
- package/dist/runtime/decimal.d.ts +2 -2
- package/dist/runtime/decimal.js +1 -1
- package/dist/runtime/diagnostics-reporter.d.ts +53 -27
- package/dist/runtime/diagnostics-reporter.js +24 -0
- package/dist/runtime/error-boundary.d.ts +65 -8
- package/dist/runtime/error-boundary.js +45 -19
- package/dist/runtime/execution-options.d.ts +10 -2
- package/dist/runtime/field-check.evaluator.d.ts +29 -0
- package/dist/runtime/field-check.evaluator.js +521 -0
- package/dist/runtime/field-compute.d.ts +64 -0
- package/dist/runtime/field-compute.js +1 -0
- package/dist/runtime/field-stage.plan.d.ts +15 -0
- package/dist/runtime/field-stage.plan.js +27 -0
- package/dist/runtime/field-stage.runtime.d.ts +35 -0
- package/dist/runtime/field-stage.runtime.js +143 -0
- package/dist/runtime/field-validator.runtime.d.ts +11 -0
- package/dist/runtime/field-validator.runtime.js +87 -0
- package/dist/runtime/framework-error-class.table.d.ts +33 -0
- package/dist/runtime/framework-error-class.table.js +25 -0
- package/dist/runtime/framework-error.d.ts +68 -8
- package/dist/runtime/framework-error.factory.d.ts +4 -0
- package/dist/runtime/framework-error.factory.js +15 -0
- package/dist/runtime/framework-error.js +23 -2
- package/dist/runtime/interceptor-next.error.d.ts +5 -0
- package/dist/runtime/interceptor-next.error.js +6 -0
- package/dist/runtime/json-value.d.ts +3 -3
- package/dist/runtime/level-lifecycle.runtime.d.ts +25 -0
- package/dist/runtime/level-lifecycle.runtime.js +42 -0
- package/dist/runtime/misuse-reporter.d.ts +12 -12
- package/dist/runtime/mutation-planner.d.ts +19 -10
- package/dist/runtime/mutation-planner.js +54 -30
- package/dist/runtime/nested-write.walker.d.ts +32 -0
- package/dist/runtime/nested-write.walker.js +73 -0
- package/dist/runtime/operation-request.d.ts +33 -6
- package/dist/runtime/operation-response.d.ts +17 -17
- package/dist/runtime/operation-runtime.d.ts +113 -25
- package/dist/runtime/operation-runtime.js +395 -74
- package/dist/runtime/outcome-mapper.d.ts +16 -5
- package/dist/runtime/outcome-mapper.js +23 -6
- package/dist/runtime/output-validator.d.ts +10 -10
- package/dist/runtime/output-validator.js +3 -3
- package/dist/runtime/owned-execution.registry.d.ts +6 -6
- package/dist/runtime/pipe-output.validator.d.ts +33 -18
- package/dist/runtime/pipe-output.validator.js +12 -12
- package/dist/runtime/pipeline.d.ts +196 -34
- package/dist/runtime/readonly-value.d.ts +32 -0
- package/dist/runtime/readonly-value.js +141 -0
- package/dist/runtime/request-envelope.validator.d.ts +3 -3
- package/dist/runtime/request-envelope.validator.js +12 -7
- package/dist/runtime/request-id.d.ts +1 -1
- package/dist/runtime/request-state.d.ts +14 -0
- package/dist/runtime/request-state.js +1 -0
- package/dist/runtime/request-state.resolver.d.ts +12 -0
- package/dist/runtime/request-state.resolver.js +20 -0
- package/dist/runtime/scalar-runtime.d.ts +31 -16
- package/dist/runtime/scalar-runtime.js +16 -18
- package/dist/transactions/deferred-operation.d.ts +35 -20
- package/dist/transactions/operation-arguments-encoder.d.ts +3 -3
- package/dist/transactions/operation-arguments-encoder.js +2 -2
- package/dist/transactions/sql-statement.d.ts +77 -0
- package/dist/transactions/sql-statement.js +47 -0
- package/dist/transactions/sql-statement.validator.d.ts +44 -0
- package/dist/transactions/sql-statement.validator.js +134 -0
- package/dist/transactions/transaction-envelope.d.ts +4 -4
- package/dist/transactions/transaction-integrity-error.d.ts +6 -6
- package/dist/transactions/transaction-integrity-error.js +5 -2
- package/dist/transactions/transaction-plan-assembler.d.ts +21 -11
- package/dist/transactions/transaction-plan-assembler.js +148 -34
- package/dist/transactions/transaction-plan-validator.d.ts +29 -20
- package/dist/transactions/transaction-plan-validator.js +70 -38
- package/dist/transactions/transaction-reference-collector.d.ts +5 -5
- package/dist/transactions/transaction-reference-resolver.d.ts +19 -2
- package/dist/transactions/transaction-reference-resolver.js +26 -14
- package/dist/transactions/transaction-reference-validator.d.ts +34 -4
- package/dist/transactions/transaction-reference-validator.js +58 -26
- package/dist/transactions/transaction-reference.d.ts +13 -13
- package/dist/transactions/transaction-request.d.ts +18 -5
- package/dist/transactions/transaction-response.d.ts +6 -6
- package/dist/transactions/transaction-runtime.d.ts +8 -7
- package/dist/transactions/transaction-runtime.js +166 -50
- package/dist/transactions/transaction-settlement.runtime.d.ts +51 -0
- package/dist/transactions/transaction-settlement.runtime.js +120 -0
- package/dist/transactions/transaction-slot-path-error.d.ts +4 -4
- package/dist/transactions/transaction-slot-path-error.js +2 -2
- package/dist/transactions/transaction-slot-writer.js +2 -2
- package/dist/transactions/transaction-stage.runtime.d.ts +81 -0
- package/dist/transactions/transaction-stage.runtime.js +123 -0
- package/package.json +6 -5
- package/src/runtime/field-check.evaluator.ts +720 -0
- package/dist/config/define-fields.d.ts +0 -42
- package/dist/config/define-fields.js +0 -3
- package/dist/config/framework-behaviors-config.d.ts +0 -30
- package/dist/config/framework-fields-config.d.ts +0 -42
- package/dist/config/framework-restrictions-config.d.ts +0 -142
- package/dist/json/avz.d.ts +0 -96
- package/dist/runtime/field-behavior.d.ts +0 -48
- /package/dist/{config/framework-behaviors-config.js → adapters/adapter-sql-statement.js} +0 -0
- /package/dist/{config/framework-fields-config.js → adapters/adapter-transaction-executor.interface.js} +0 -0
- /package/dist/{json → avz}/index.js +0 -0
- /package/dist/{json → avz}/json-shape.registry.js +0 -0
- /package/dist/config/{framework-restrictions-config.js → exact-framework-config.js} +0 -0
- /package/dist/{runtime/field-behavior.js → config/field-pipeline-config.js} +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { frozenCopy } from "./readonly-value.js";
|
|
2
|
+
export async function runLevelLifecycle(levels, context, inner, settle, reuse, refuse) {
|
|
3
|
+
const once = (run) => {
|
|
4
|
+
let called = false;
|
|
5
|
+
return () => {
|
|
6
|
+
if (called) {
|
|
7
|
+
const refusal = refuse();
|
|
8
|
+
reuse.refusal ??= refusal;
|
|
9
|
+
return Promise.reject(refusal);
|
|
10
|
+
}
|
|
11
|
+
called = true;
|
|
12
|
+
return run();
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
const hooked = async () => {
|
|
16
|
+
for (const level of levels) {
|
|
17
|
+
for (const hook of level.hooks.before) {
|
|
18
|
+
await hook(context());
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const result = await settle(await inner());
|
|
22
|
+
const after = [...levels].reverse().flatMap((level) => level.hooks.after);
|
|
23
|
+
if (after.length > 0) {
|
|
24
|
+
const told = frozenCopy(result);
|
|
25
|
+
for (const hook of after) {
|
|
26
|
+
await hook(told, context());
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
};
|
|
31
|
+
const interceptors = levels.flatMap((level) => level.interceptors);
|
|
32
|
+
let next = hooked;
|
|
33
|
+
for (let index = interceptors.length - 1; index >= 0; index -= 1) {
|
|
34
|
+
const interceptor = interceptors[index];
|
|
35
|
+
if (interceptor === undefined) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const continuation = once(next);
|
|
39
|
+
next = () => interceptor(context(), async () => settle(await continuation()));
|
|
40
|
+
}
|
|
41
|
+
return next();
|
|
42
|
+
}
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { AvRequestOrigin } from "../config/config-layer.js";
|
|
2
|
+
import type { AvOperationFamily } from "../operations/operation-identity.js";
|
|
3
3
|
/**
|
|
4
4
|
* One caller value the framework refused because it could not be read
|
|
5
5
|
* (`"unreadable-value"`: a revoked Proxy, a throwing getter or trap) or is not plain
|
|
6
|
-
* data (`"non-plain-value"`: a class instance other than `Date`, `
|
|
6
|
+
* data (`"non-plain-value"`: a class instance other than `Date`, `AvDecimal` or
|
|
7
7
|
* `Uint8Array`). The caller received `A2004` with one `V1001` at `path`.
|
|
8
8
|
*
|
|
9
|
-
* The operation's identity is present when the framework could read it
|
|
10
|
-
* a request whose own envelope could not be read
|
|
9
|
+
* The operation's identity is present when the framework could read it (absent for
|
|
10
|
+
* a request whose own envelope could not be read), and `operation` is the step's
|
|
11
11
|
* index in a transaction plan.
|
|
12
12
|
*/
|
|
13
|
-
export type
|
|
13
|
+
export type AvMisuseEvent = {
|
|
14
14
|
readonly kind: "unreadable-value" | "non-plain-value";
|
|
15
15
|
/** The issue path the caller received. */
|
|
16
16
|
readonly path: readonly (string | number)[];
|
|
17
|
-
readonly origin?:
|
|
17
|
+
readonly origin?: AvRequestOrigin;
|
|
18
18
|
readonly resource?: string;
|
|
19
|
-
readonly family?:
|
|
19
|
+
readonly family?: AvOperationFamily;
|
|
20
20
|
readonly variant?: string;
|
|
21
21
|
readonly operation?: number;
|
|
22
22
|
};
|
|
23
23
|
/**
|
|
24
|
-
* Receives a {@link
|
|
24
|
+
* Receives a {@link AvMisuseEvent} for every caller value the framework
|
|
25
25
|
* refused as unreadable or not plain data. Set once, as the root configuration's
|
|
26
26
|
* `misuse`. What it returns, throws or rejects with never reaches the response.
|
|
27
27
|
*/
|
|
28
|
-
export type
|
|
28
|
+
export type AvMisuseSink = (event: AvMisuseEvent) => void | Promise<void>;
|
|
29
29
|
/** The channel as the runtime holds it: total, never awaited. */
|
|
30
|
-
export type MisuseReporter = (event:
|
|
30
|
+
export type MisuseReporter = (event: AvMisuseEvent) => void;
|
|
31
31
|
/** Binds the configured sink; without one, nothing is observed. */
|
|
32
|
-
export declare function createMisuseReporter(sink:
|
|
32
|
+
export declare function createMisuseReporter(sink: AvMisuseSink | undefined): MisuseReporter;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
1
|
+
import type { ResolvedComputeConfig } from "../config/field-compute.resolver.js";
|
|
2
|
+
import type { AvRelationMutationDirective } from "../contracts/capability.js";
|
|
3
|
+
import type { AvContract, AvContractCore } from "../contracts/contract.js";
|
|
4
|
+
import type { AvComputeContext } from "./field-compute.js";
|
|
5
|
+
import type { NestedWriteTarget } from "./nested-write.walker.js";
|
|
4
6
|
import type { OperationExecutionRequest } from "./operation-request.js";
|
|
5
|
-
import type { PipelineContext } from "./pipeline.js";
|
|
6
7
|
/**
|
|
7
8
|
* The two Contracts a write reads. The Core Contract decides what the Adapter is asked
|
|
8
9
|
* to store; the active Contract adds the fields its scope declared under
|
|
@@ -10,11 +11,11 @@ import type { PipelineContext } from "./pipeline.js";
|
|
|
10
11
|
* virtual: its behaviours run and its value is stripped before the Adapter call.
|
|
11
12
|
*/
|
|
12
13
|
export type WriteContracts = {
|
|
13
|
-
readonly core:
|
|
14
|
-
readonly active:
|
|
14
|
+
readonly core: AvContractCore;
|
|
15
|
+
readonly active: AvContract;
|
|
15
16
|
};
|
|
16
17
|
type WriteStage = "create" | "update";
|
|
17
|
-
type ContextFactory = (args: unknown) =>
|
|
18
|
+
type ContextFactory = (args: unknown) => AvComputeContext;
|
|
18
19
|
export type RelationRecordSite = {
|
|
19
20
|
readonly key: string | undefined;
|
|
20
21
|
readonly stage: WriteStage;
|
|
@@ -53,9 +54,17 @@ export declare const RELATION_MUTATION_RECORD_SITES: {
|
|
|
53
54
|
* membership, not an assertion: `Object.prototype` members resolve through the
|
|
54
55
|
* prototype chain and would otherwise reach a site loop with a non-iterable.
|
|
55
56
|
*/
|
|
56
|
-
export declare function isRelationMutationDirective(key: string): key is
|
|
57
|
-
/** Apply effective
|
|
58
|
-
export declare function
|
|
57
|
+
export declare function isRelationMutationDirective(key: string): key is AvRelationMutationDirective;
|
|
58
|
+
/** Apply the effective create/update compute stages to the post-pipe logical args. */
|
|
59
|
+
export declare function applyWriteCompute(contracts: WriteContracts, compute: ResolvedComputeConfig | undefined, request: OperationExecutionRequest, args: unknown, contextFactory: ContextFactory): Promise<unknown>;
|
|
60
|
+
/**
|
|
61
|
+
* Applies each nested target's write compute, in the targets' order, to its
|
|
62
|
+
* records: `compute.create` on a created row (an `$upsert`'s `create` branch
|
|
63
|
+
* too), `compute.update` on an updated one. Each target's stages are told
|
|
64
|
+
* `context(target, payload)`, the nested target's own context. The arguments
|
|
65
|
+
* are copied where a record changes, never written.
|
|
66
|
+
*/
|
|
67
|
+
export declare function applyNestedWriteCompute(contracts: WriteContracts, compute: ResolvedComputeConfig | undefined, args: unknown, targets: readonly NestedWriteTarget[], context: (target: NestedWriteTarget, payload: unknown) => AvComputeContext): Promise<unknown>;
|
|
59
68
|
/** Strip VIRTUAL scalar input only at the persistence boundary. */
|
|
60
69
|
export declare function stripVirtualMutationInput(contracts: WriteContracts, request: OperationExecutionRequest, args: unknown): Promise<unknown>;
|
|
61
70
|
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { lookupField, lookupResource } from "../contracts/contract-lookup.js";
|
|
2
|
-
import {
|
|
2
|
+
import { AvComputedValueError } from "./computed-value.error.js";
|
|
3
|
+
import { replaceAt, valueAt } from "./field-stage.runtime.js";
|
|
3
4
|
import { jsonShapeIssues, normalizeScalarFieldValue, } from "./scalar-runtime.js";
|
|
4
5
|
function writeScalarField(contracts, resourceName, fieldName) {
|
|
5
6
|
const field = lookupField(lookupResource(contracts.core, resourceName), fieldName) ??
|
|
@@ -33,7 +34,7 @@ function cloneRecord(value) {
|
|
|
33
34
|
function relationEntries(value) {
|
|
34
35
|
return Array.isArray(value) ? value : [value];
|
|
35
36
|
}
|
|
36
|
-
async function computeRecord(contracts,
|
|
37
|
+
async function computeRecord(contracts, compute, resourceName, stage, data, contextFactory) {
|
|
37
38
|
if (!isRecord(data)) {
|
|
38
39
|
return data;
|
|
39
40
|
}
|
|
@@ -42,13 +43,13 @@ async function computeRecord(contracts, behaviors, request, resourceName, stage,
|
|
|
42
43
|
return data;
|
|
43
44
|
}
|
|
44
45
|
const logicalData = cloneRecord(data);
|
|
45
|
-
const
|
|
46
|
-
if (
|
|
47
|
-
for (const [fieldName,
|
|
48
|
-
if (
|
|
46
|
+
const resourceCompute = compute[resourceName];
|
|
47
|
+
if (resourceCompute !== undefined) {
|
|
48
|
+
for (const [fieldName, fieldCompute] of Object.entries(resourceCompute)) {
|
|
49
|
+
if (fieldCompute === undefined) {
|
|
49
50
|
continue;
|
|
50
51
|
}
|
|
51
|
-
const callback =
|
|
52
|
+
const callback = fieldCompute[stage];
|
|
52
53
|
if (typeof callback !== "function") {
|
|
53
54
|
continue;
|
|
54
55
|
}
|
|
@@ -56,40 +57,43 @@ async function computeRecord(contracts, behaviors, request, resourceName, stage,
|
|
|
56
57
|
if (field === undefined) {
|
|
57
58
|
continue;
|
|
58
59
|
}
|
|
60
|
+
const snapshot = Object.freeze({ ...logicalData });
|
|
59
61
|
const computed = await callback({
|
|
60
|
-
origin: request.origin,
|
|
61
|
-
resource: resourceName,
|
|
62
62
|
field: fieldName,
|
|
63
|
-
value:
|
|
64
|
-
data:
|
|
65
|
-
|
|
63
|
+
value: snapshot[fieldName],
|
|
64
|
+
data: snapshot,
|
|
65
|
+
ctx: contextFactory(snapshot),
|
|
66
66
|
});
|
|
67
67
|
if (computed === undefined) {
|
|
68
68
|
continue;
|
|
69
69
|
}
|
|
70
|
-
const validation = normalizeScalarFieldValue(contracts.core, field, computed, "runtime", [
|
|
70
|
+
const validation = normalizeScalarFieldValue(contracts.core, field, computed, "runtime", [
|
|
71
|
+
"resources",
|
|
72
|
+
resourceName,
|
|
73
|
+
"fields",
|
|
74
|
+
fieldName,
|
|
75
|
+
"pipelines",
|
|
76
|
+
"compute",
|
|
77
|
+
stage,
|
|
78
|
+
]);
|
|
71
79
|
if (!validation.ok) {
|
|
72
|
-
throw new
|
|
80
|
+
throw new AvComputedValueError(resourceName, fieldName, stage, validation.issues);
|
|
73
81
|
}
|
|
74
82
|
const shapeIssues = jsonShapeIssues(field, validation.value, [
|
|
75
|
-
"
|
|
83
|
+
"resources",
|
|
76
84
|
resourceName,
|
|
85
|
+
"fields",
|
|
77
86
|
fieldName,
|
|
87
|
+
"pipelines",
|
|
88
|
+
"compute",
|
|
78
89
|
stage,
|
|
79
90
|
]);
|
|
80
91
|
if (shapeIssues.length > 0) {
|
|
81
|
-
throw new
|
|
92
|
+
throw new AvComputedValueError(resourceName, fieldName, stage, shapeIssues);
|
|
82
93
|
}
|
|
83
94
|
logicalData[fieldName] = validation.value;
|
|
84
95
|
}
|
|
85
96
|
}
|
|
86
|
-
for (const [fieldName, value] of Object.entries(logicalData)) {
|
|
87
|
-
const field = lookupField(resource, fieldName);
|
|
88
|
-
if (field?.kind !== "relation") {
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
logicalData[fieldName] = await mapRelationMutationRecords(field, value, (targetResource, nestedStage, record) => computeRecord(contracts, behaviors, request, targetResource, nestedStage, record, contextFactory));
|
|
92
|
-
}
|
|
93
97
|
return logicalData;
|
|
94
98
|
}
|
|
95
99
|
async function mapRelationMutationRecords(relation, value, leaf) {
|
|
@@ -125,29 +129,49 @@ async function mapRelationMutationRecords(relation, value, leaf) {
|
|
|
125
129
|
}
|
|
126
130
|
return normalized;
|
|
127
131
|
}
|
|
128
|
-
export async function
|
|
129
|
-
if (
|
|
132
|
+
export async function applyWriteCompute(contracts, compute, request, args, contextFactory) {
|
|
133
|
+
if (compute === undefined || !isRecord(args)) {
|
|
130
134
|
return args;
|
|
131
135
|
}
|
|
132
136
|
const logicalArgs = cloneRecord(args);
|
|
133
137
|
if (request.family === "create" && "data" in logicalArgs) {
|
|
134
138
|
logicalArgs.data = Array.isArray(logicalArgs.data)
|
|
135
|
-
? await Promise.all(logicalArgs.data.map((entry) => computeRecord(contracts,
|
|
136
|
-
: await computeRecord(contracts,
|
|
139
|
+
? await Promise.all(logicalArgs.data.map((entry) => computeRecord(contracts, compute, request.resource, "create", entry, contextFactory)))
|
|
140
|
+
: await computeRecord(contracts, compute, request.resource, "create", logicalArgs.data, contextFactory);
|
|
137
141
|
}
|
|
138
142
|
if (request.family === "update" && "data" in logicalArgs) {
|
|
139
|
-
logicalArgs.data = await computeRecord(contracts,
|
|
143
|
+
logicalArgs.data = await computeRecord(contracts, compute, request.resource, "update", logicalArgs.data, contextFactory);
|
|
140
144
|
}
|
|
141
145
|
if (request.family === "upsert") {
|
|
142
146
|
if ("create" in logicalArgs) {
|
|
143
|
-
logicalArgs.create = await computeRecord(contracts,
|
|
147
|
+
logicalArgs.create = await computeRecord(contracts, compute, request.resource, "create", logicalArgs.create, contextFactory);
|
|
144
148
|
}
|
|
145
149
|
if ("update" in logicalArgs) {
|
|
146
|
-
logicalArgs.update = await computeRecord(contracts,
|
|
150
|
+
logicalArgs.update = await computeRecord(contracts, compute, request.resource, "update", logicalArgs.update, contextFactory);
|
|
147
151
|
}
|
|
148
152
|
}
|
|
149
153
|
return logicalArgs;
|
|
150
154
|
}
|
|
155
|
+
export async function applyNestedWriteCompute(contracts, compute, args, targets, context) {
|
|
156
|
+
if (compute === undefined) {
|
|
157
|
+
return args;
|
|
158
|
+
}
|
|
159
|
+
let current = args;
|
|
160
|
+
for (const target of targets) {
|
|
161
|
+
if (!Object.hasOwn(compute, target.resource)) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
for (const site of target.records) {
|
|
165
|
+
const record = valueAt(current, site.path);
|
|
166
|
+
if (!isRecord(record)) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const computed = await computeRecord(contracts, compute, target.resource, site.stage, record, () => context(target, valueAt(current, target.path)));
|
|
170
|
+
current = replaceAt(current, site.path, computed);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return current;
|
|
174
|
+
}
|
|
151
175
|
async function stripVirtualRecord(contracts, resourceName, data) {
|
|
152
176
|
if (!isRecord(data)) {
|
|
153
177
|
return data;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { AvContract } from "../contracts/contract.js";
|
|
2
|
+
import type { AvOperationIdentity } from "../operations/operation-identity.js";
|
|
3
|
+
import { type Path } from "./field-stage.runtime.js";
|
|
4
|
+
import type { ReferentialActionTable } from "./referential-action.js";
|
|
5
|
+
/** The family a nested target's stages are told, and its guards are drawn from. */
|
|
6
|
+
export type NestedTargetFamily = "create" | "update" | "delete";
|
|
7
|
+
/** One record of a nested target's data, and the write phase it is. */
|
|
8
|
+
export type NestedRecordSite = {
|
|
9
|
+
/** Where the record sits in the operation's arguments. */
|
|
10
|
+
readonly path: Path;
|
|
11
|
+
/** `create` for a created row (an `$upsert`'s `create` branch too), `update` otherwise. */
|
|
12
|
+
readonly stage: "create" | "update";
|
|
13
|
+
};
|
|
14
|
+
/** One nested write's target, in data order. */
|
|
15
|
+
export type NestedWriteTarget = {
|
|
16
|
+
/** The target Resource. */
|
|
17
|
+
readonly resource: string;
|
|
18
|
+
readonly family: NestedTargetFamily;
|
|
19
|
+
/** Where the directive's entry — the nested payload — sits in the arguments. */
|
|
20
|
+
readonly path: Path;
|
|
21
|
+
/** The entry's data records the target's field stages run on. */
|
|
22
|
+
readonly records: readonly NestedRecordSite[];
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Every nested write target in an operation's arguments, depth first in data
|
|
26
|
+
* order: a target comes before the targets inside its own data, and before the
|
|
27
|
+
* next directive's. Empty for an operation with no write data. Total over
|
|
28
|
+
* malformed input.
|
|
29
|
+
*/
|
|
30
|
+
export declare function nestedWriteTargetsOf(contract: AvContract, references: ReferentialActionTable, request: AvOperationIdentity & {
|
|
31
|
+
readonly resource: string;
|
|
32
|
+
}, args: unknown): readonly NestedWriteTarget[];
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { lookupField, lookupResource } from "../contracts/contract-lookup.js";
|
|
2
|
+
import { isPlainRecord, valueAt, writeRecordPathsOf, } from "./field-stage.runtime.js";
|
|
3
|
+
import { isRelationMutationDirective, RELATION_MUTATION_RECORD_SITES, } from "./mutation-planner.js";
|
|
4
|
+
const DIRECTIVE_FAMILY = {
|
|
5
|
+
$create: "create",
|
|
6
|
+
$connectOrCreate: "create",
|
|
7
|
+
$update: "update",
|
|
8
|
+
$upsert: "update",
|
|
9
|
+
$delete: "delete",
|
|
10
|
+
$connect: "relation",
|
|
11
|
+
$disconnect: "relation",
|
|
12
|
+
$set: "relation",
|
|
13
|
+
};
|
|
14
|
+
function holdsReference(references, resource, relation) {
|
|
15
|
+
const edges = Object.hasOwn(references, resource)
|
|
16
|
+
? references[resource]
|
|
17
|
+
: undefined;
|
|
18
|
+
return (Array.isArray(edges) &&
|
|
19
|
+
edges.some((edge) => edge.field === relation));
|
|
20
|
+
}
|
|
21
|
+
function walkRecord(context, resource, path, record) {
|
|
22
|
+
if (!isPlainRecord(record)) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const owner = lookupResource(context.contract, resource);
|
|
26
|
+
for (const [key, value] of Object.entries(record)) {
|
|
27
|
+
const field = lookupField(owner, key);
|
|
28
|
+
if (field?.kind !== "relation" || !isPlainRecord(value)) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
for (const [directive, operand] of Object.entries(value)) {
|
|
32
|
+
if (!isRelationMutationDirective(directive)) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const mapped = DIRECTIVE_FAMILY[directive];
|
|
36
|
+
if (mapped === "relation" &&
|
|
37
|
+
field.cardinality === "one" &&
|
|
38
|
+
holdsReference(context.references, resource, key)) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const family = mapped === "relation" ? "update" : mapped;
|
|
42
|
+
const entries = directive !== "$set" && Array.isArray(operand)
|
|
43
|
+
? operand.map((_entry, index) => [index])
|
|
44
|
+
: [[]];
|
|
45
|
+
for (const at of entries) {
|
|
46
|
+
const entryPath = [...path, key, directive, ...at];
|
|
47
|
+
const records = [];
|
|
48
|
+
for (const site of RELATION_MUTATION_RECORD_SITES[directive]) {
|
|
49
|
+
const sitePath = site.key === undefined ? entryPath : [...entryPath, site.key];
|
|
50
|
+
if (isPlainRecord(valueAt(record, sitePath.slice(path.length)))) {
|
|
51
|
+
records.push({ path: sitePath, stage: site.stage });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
context.targets.push({
|
|
55
|
+
resource: field.target,
|
|
56
|
+
family,
|
|
57
|
+
path: entryPath,
|
|
58
|
+
records,
|
|
59
|
+
});
|
|
60
|
+
for (const site of records) {
|
|
61
|
+
walkRecord(context, field.target, site.path, valueAt(record, site.path.slice(path.length)));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export function nestedWriteTargetsOf(contract, references, request, args) {
|
|
68
|
+
const context = { contract, references, targets: [] };
|
|
69
|
+
for (const path of writeRecordPathsOf(request.family, args)) {
|
|
70
|
+
walkRecord(context, request.resource, path, valueAt(args, path));
|
|
71
|
+
}
|
|
72
|
+
return context.targets;
|
|
73
|
+
}
|
|
@@ -1,14 +1,23 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
|
|
1
|
+
import type { AvOperationIdentity } from "../operations/operation-identity.js";
|
|
2
|
+
import type { AvTransportContext } from "./pipeline.js";
|
|
3
|
+
import type { AvState } from "./request-state.js";
|
|
4
|
+
/**
|
|
5
|
+
* The protocol version and Contract hash a remote caller says it was built for:
|
|
6
|
+
* the Client Contract's for a client, the Agent Contract's for an agent.
|
|
7
|
+
*/
|
|
4
8
|
export type ClientProtocolIdentity = {
|
|
5
9
|
readonly version: number;
|
|
6
10
|
readonly hash: string;
|
|
7
11
|
};
|
|
8
|
-
type RuntimeOperationBase =
|
|
12
|
+
type RuntimeOperationBase = AvOperationIdentity & {
|
|
9
13
|
readonly resource: string;
|
|
10
14
|
readonly arguments: unknown;
|
|
11
15
|
readonly requestId: string;
|
|
16
|
+
/**
|
|
17
|
+
* The request's `ctx.$state`: every stage of it, and every ctx call they make,
|
|
18
|
+
* shares this one object. Omitted, it is a fresh `{}`.
|
|
19
|
+
*/
|
|
20
|
+
readonly state?: AvState;
|
|
12
21
|
};
|
|
13
22
|
/** An in-process request, run under the Application Contract. */
|
|
14
23
|
export type ApplicationOperationRequest = RuntimeOperationBase & {
|
|
@@ -23,10 +32,21 @@ export type ApplicationOperationRequest = RuntimeOperationBase & {
|
|
|
23
32
|
export type ClientOperationRequest = RuntimeOperationBase & {
|
|
24
33
|
readonly origin: "client";
|
|
25
34
|
readonly protocol: ClientProtocolIdentity;
|
|
26
|
-
readonly transport?:
|
|
35
|
+
readonly transport?: AvTransportContext;
|
|
27
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* An AI agent's request, run under the Agent Contract; `protocol` is the
|
|
39
|
+
* identity of the Agent Contract the caller (an MCP server) was built from.
|
|
40
|
+
*/
|
|
41
|
+
export type AgentOperationRequest = RuntimeOperationBase & {
|
|
42
|
+
readonly origin: "agent";
|
|
43
|
+
readonly protocol: ClientProtocolIdentity;
|
|
44
|
+
readonly transport?: AvTransportContext;
|
|
45
|
+
};
|
|
46
|
+
/** A request from outside the server: a client's or an agent's. */
|
|
47
|
+
export type RemoteOperationRequest = ClientOperationRequest | AgentOperationRequest;
|
|
28
48
|
/** One operation request, as `framework.execute` takes it. */
|
|
29
|
-
export type FrameworkOperationRequest = ApplicationOperationRequest | ClientOperationRequest;
|
|
49
|
+
export type FrameworkOperationRequest = ApplicationOperationRequest | ClientOperationRequest | AgentOperationRequest;
|
|
30
50
|
type WithoutArguments<Request> = Request extends unknown ? Omit<Request, "arguments"> : never;
|
|
31
51
|
/**
|
|
32
52
|
* A request as every stage after the Framework Validation Layer sees it:
|
|
@@ -37,4 +57,11 @@ type WithoutArguments<Request> = Request extends unknown ? Omit<Request, "argume
|
|
|
37
57
|
* correlations survive the omission.
|
|
38
58
|
*/
|
|
39
59
|
export type OperationExecutionRequest = WithoutArguments<FrameworkOperationRequest>;
|
|
60
|
+
/**
|
|
61
|
+
* A request whose stages run: its `state` settled once, the seed or the `{}`
|
|
62
|
+
* the runtime made for it, so every stage is told the same object.
|
|
63
|
+
*/
|
|
64
|
+
export type StatefulOperationRequest = OperationExecutionRequest & {
|
|
65
|
+
readonly state: AvState;
|
|
66
|
+
};
|
|
40
67
|
export {};
|
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AvValidationIssue } from "../operations/validation-code.js";
|
|
2
2
|
/** Every outcome code, as a runtime list. */
|
|
3
3
|
export declare const OPERATION_CODES: readonly ["A1000", "A1001", "A1002", "A1003", "A1004", "A1005", "A1006", "A1007", "A1008", "A1009", "A2000", "A2001", "A2002", "A2003", "A2004", "A2005", "A2006", "A2007", "A2008", "A2009", "A2010", "A2011", "A2012", "A2013", "A2014", "A3000", "A3001", "A3002", "A3003", "A3004", "A4000", "A4001", "A4002"];
|
|
4
4
|
/**
|
|
5
5
|
* Every outcome code: `A1xxx` success, `A2xxx` caller error, `A3xxx` internal
|
|
6
6
|
* failure, `A4xxx` authentication or permission denial.
|
|
7
7
|
*/
|
|
8
|
-
export type
|
|
8
|
+
export type AvOperationCode = (typeof OPERATION_CODES)[number];
|
|
9
9
|
/** The success codes, `A1xxx`. */
|
|
10
|
-
export type
|
|
10
|
+
export type AvOperationSuccessCode = Extract<AvOperationCode, `A1${string}`>;
|
|
11
11
|
/** Every outcome code that is not a success. */
|
|
12
|
-
export type
|
|
12
|
+
export type AvOperationErrorCode = Exclude<AvOperationCode, AvOperationSuccessCode>;
|
|
13
13
|
/** Internal-failure family: nothing in the cause is caller-actionable. */
|
|
14
|
-
export type OperationInternalErrorCode = Extract<
|
|
14
|
+
export type OperationInternalErrorCode = Extract<AvOperationErrorCode, `A3${string}`>;
|
|
15
15
|
/** Why a request failed: a message, and the validation issues when there are any. */
|
|
16
|
-
export type
|
|
16
|
+
export type AvCause = {
|
|
17
17
|
readonly message: string;
|
|
18
|
-
readonly issues?: readonly
|
|
18
|
+
readonly issues?: readonly AvValidationIssue[];
|
|
19
19
|
readonly operation?: number;
|
|
20
20
|
};
|
|
21
21
|
/** The response envelope: `{ data, code, cause }`. */
|
|
22
|
-
type OperationResponseEnvelope<TData, TCode extends
|
|
22
|
+
type OperationResponseEnvelope<TData, TCode extends AvOperationCode, TCause extends AvCause | null> = {
|
|
23
23
|
readonly data: TData;
|
|
24
24
|
readonly code: TCode;
|
|
25
25
|
readonly cause: TCause;
|
|
26
26
|
};
|
|
27
27
|
/** Success codes that carry response data (every success code but A1001). */
|
|
28
|
-
export type
|
|
28
|
+
export type AvOperationSuccessDataCode = Exclude<AvOperationSuccessCode, "A1001">;
|
|
29
29
|
/** A success with data: `cause` is `null`. */
|
|
30
|
-
export type
|
|
30
|
+
export type AvOperationSuccessResponse<T> = OperationResponseEnvelope<T, AvOperationSuccessDataCode, null>;
|
|
31
31
|
/** A success with no matching row (`A1001`): `data` and `cause` are `null`. */
|
|
32
|
-
export type
|
|
32
|
+
export type AvOperationNoMatchResponse = OperationResponseEnvelope<null, "A1001", null>;
|
|
33
33
|
/** A failure: `data` is `null` and `cause` says why. */
|
|
34
|
-
export type
|
|
34
|
+
export type AvOperationFailureResponse = OperationResponseEnvelope<null, AvOperationErrorCode, AvCause>;
|
|
35
35
|
/**
|
|
36
36
|
* What an operation answers: success with data, a no-match success (`A1001`),
|
|
37
37
|
* or a failure with a cause.
|
|
38
38
|
*/
|
|
39
|
-
export type
|
|
39
|
+
export type AvOperationResponse<T = unknown> = AvOperationSuccessResponse<T> | AvOperationNoMatchResponse | AvOperationFailureResponse;
|
|
40
40
|
/** Builds a success response with data. */
|
|
41
|
-
export declare function operationSuccess<T>(data: T, code:
|
|
41
|
+
export declare function operationSuccess<T>(data: T, code: AvOperationSuccessDataCode): AvOperationSuccessResponse<T>;
|
|
42
42
|
/** Builds the no-match success arm (A1001). */
|
|
43
|
-
export declare function operationNoMatch():
|
|
43
|
+
export declare function operationNoMatch(): AvOperationNoMatchResponse;
|
|
44
44
|
/** Builds a failure response. */
|
|
45
|
-
export declare function operationFailure(code:
|
|
45
|
+
export declare function operationFailure(code: AvOperationErrorCode, cause: AvCause): AvOperationFailureResponse;
|
|
46
46
|
/** The one generic internal-failure text; every A3000 carries it. */
|
|
47
47
|
export declare const INTERNAL_FAILURE_MESSAGE = "Internal framework error.";
|
|
48
48
|
/** The response every internal failure (`A3000`) answers with. */
|
|
49
|
-
export declare function internalFailure():
|
|
49
|
+
export declare function internalFailure(): AvOperationFailureResponse;
|
|
50
50
|
export {};
|