@aventara/core 1.0.0-rc.4 → 1.0.0-rc.9
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 +157 -0
- package/ERRORS.md +2 -2
- package/README.md +80 -7
- package/dist/adapters/adapter-field.d.ts +8 -0
- package/dist/adapters/adapter-identifier.d.ts +9 -0
- package/dist/adapters/adapter-identifier.js +6 -1
- package/dist/adapters/adapter-operation.d.ts +12 -6
- package/dist/compiler/contract-compiler-diagnostic.guidance.js +2 -2
- package/dist/compiler/contract.compiler.js +26 -3
- package/dist/config/config.resolver.d.ts +2 -0
- package/dist/config/config.resolver.js +7 -2
- package/dist/config/config.validator.js +36 -12
- package/dist/config/exact-framework-config.d.ts +11 -1
- package/dist/config/framework-config.d.ts +14 -1
- package/dist/contracts/contract-canonicalizer.js +2 -0
- package/dist/contracts/contract.d.ts +15 -1
- package/dist/contracts/field.d.ts +17 -0
- package/dist/contracts/scalar-value-type.d.ts +3 -1
- package/dist/framework/contract-view.d.ts +9 -1
- package/dist/framework/framework-construction.error.d.ts +2 -1
- package/dist/framework/framework-construction.error.js +13 -1
- package/dist/framework/framework.d.ts +26 -5
- package/dist/framework/framework.factory.js +29 -10
- package/dist/framework/operation-facade.d.ts +59 -12
- package/dist/index.d.ts +8 -4
- package/dist/internal.d.ts +2 -2
- package/dist/internal.js +2 -0
- package/dist/operations/operation-arguments.d.ts +43 -27
- package/dist/operations/operation-call.d.ts +2 -2
- package/dist/operations/operation-result.d.ts +1 -1
- package/dist/policy/access-provider.interface.d.ts +36 -0
- package/dist/policy/access-provider.interface.js +1 -0
- package/dist/policy/access-provider.registry.d.ts +9 -0
- package/dist/policy/access-provider.registry.js +10 -0
- package/dist/policy/policy-admission.js +12 -3
- package/dist/policy/policy-after-update.runtime.js +5 -4
- package/dist/policy/policy-engine.d.ts +11 -4
- package/dist/policy/policy-nested-write.runtime.js +11 -7
- package/dist/policy/policy-read.runtime.d.ts +0 -2
- package/dist/policy/policy-read.runtime.js +18 -9
- package/dist/policy/policy-request.d.ts +7 -3
- package/dist/policy/policy-request.js +14 -3
- package/dist/policy/policy-session.d.ts +14 -4
- package/dist/policy/policy-session.js +12 -11
- package/dist/policy/policy-startup.validator.js +13 -15
- package/dist/policy/policy-write.runtime.d.ts +7 -0
- package/dist/policy/policy-write.runtime.js +23 -8
- package/dist/prebuilt/core-version.generated.d.ts +1 -1
- package/dist/prebuilt/core-version.generated.js +1 -1
- package/dist/prebuilt/prebuilt-changes.d.ts +28 -0
- package/dist/prebuilt/prebuilt-changes.js +65 -0
- package/dist/prebuilt/prebuilt-contracts.loader.js +11 -56
- package/dist/prebuilt/prebuilt-inputs.fingerprint.js +16 -1
- package/dist/protocol/absent-route.responder.d.ts +4 -4
- package/dist/protocol/http-request.decoder.d.ts +2 -2
- package/dist/protocol/http-request.reader.d.ts +2 -2
- package/dist/protocol/http-response.encoder.js +2 -2
- package/dist/protocol/protocol-headers.d.ts +3 -3
- package/dist/runtime/caller-data.copier.js +3 -4
- package/dist/runtime/computed-read-runtime.js +2 -7
- package/dist/runtime/context-executor.runtime.d.ts +2 -2
- package/dist/runtime/core-class.brand.d.ts +10 -0
- package/dist/runtime/core-class.brand.js +21 -0
- package/dist/runtime/create-requirement.js +1 -1
- package/dist/runtime/decimal.d.ts +14 -0
- package/dist/runtime/decimal.js +21 -0
- package/dist/runtime/diagnostics-reporter.d.ts +5 -5
- package/dist/runtime/diagnostics-reporter.js +13 -1
- package/dist/runtime/framework-error.js +9 -0
- package/dist/runtime/operation-request.d.ts +9 -9
- package/dist/runtime/operation-runtime.d.ts +8 -8
- package/dist/runtime/operation-runtime.js +30 -9
- package/dist/runtime/owned-execution.registry.d.ts +2 -2
- package/dist/runtime/readonly-value.d.ts +1 -1
- package/dist/runtime/readonly-value.js +2 -2
- package/dist/runtime/referential-action.d.ts +1 -1
- package/dist/runtime/scalar-runtime.js +4 -5
- package/dist/transactions/operation-arguments-encoder.js +8 -3
- package/dist/transactions/sql-statement.d.ts +2 -2
- package/dist/transactions/sql-statement.validator.js +2 -2
- package/dist/transactions/transaction-integrity-error.js +2 -0
- package/dist/transactions/transaction-plan-assembler.d.ts +11 -4
- package/dist/transactions/transaction-plan-assembler.js +17 -2
- package/dist/transactions/transaction-plan-validator.d.ts +5 -5
- package/dist/transactions/transaction-request.d.ts +8 -8
- package/dist/transactions/transaction-runtime.d.ts +2 -2
- package/dist/transactions/transaction-runtime.js +12 -7
- package/dist/transactions/transaction-slot-path-error.js +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** The registered symbol every copy of this major brands class `name` with. */
|
|
2
|
+
export declare function coreBrandOf(name: string): symbol;
|
|
3
|
+
/** Whether `value` carries `brand`, wherever it was made: one `in`. */
|
|
4
|
+
export declare function hasCoreBrand(value: unknown, brand: symbol): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Brands `type`'s prototype as `name` and makes `instanceof type` read the brand.
|
|
7
|
+
* A developer's own subclass of `type` inherits the static `Symbol.hasInstance`, so
|
|
8
|
+
* on any constructor but `type` itself it answers as `instanceof` always does.
|
|
9
|
+
*/
|
|
10
|
+
export declare function brandCoreClass(type: abstract new (...args: never[]) => object, name: string): symbol;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { CORE_VERSION } from "../prebuilt/core-version.generated.js";
|
|
2
|
+
const CORE_MAJOR = CORE_VERSION.slice(0, CORE_VERSION.indexOf("."));
|
|
3
|
+
export function coreBrandOf(name) {
|
|
4
|
+
return Symbol.for(`aventara.core.${name}@${CORE_MAJOR}`);
|
|
5
|
+
}
|
|
6
|
+
export function hasCoreBrand(value, brand) {
|
|
7
|
+
return typeof value === "object" && value !== null && brand in value;
|
|
8
|
+
}
|
|
9
|
+
const ordinaryHasInstance = Function.prototype[Symbol.hasInstance];
|
|
10
|
+
export function brandCoreClass(type, name) {
|
|
11
|
+
const brand = coreBrandOf(name);
|
|
12
|
+
Object.defineProperty(type.prototype, brand, { value: true });
|
|
13
|
+
Object.defineProperty(type, Symbol.hasInstance, {
|
|
14
|
+
value: function hasInstance(value) {
|
|
15
|
+
return this === type
|
|
16
|
+
? hasCoreBrand(value, brand)
|
|
17
|
+
: ordinaryHasInstance.call(this, value);
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
return brand;
|
|
21
|
+
}
|
|
@@ -13,7 +13,7 @@ function edgesOf(table, resource) {
|
|
|
13
13
|
return Array.isArray(listed) ? listed : [];
|
|
14
14
|
}
|
|
15
15
|
function referencesOf(edge) {
|
|
16
|
-
return Array.isArray(edge.
|
|
16
|
+
return Array.isArray(edge.referenceFields) ? edge.referenceFields : [];
|
|
17
17
|
}
|
|
18
18
|
function creatableScalar(contract, resource, field) {
|
|
19
19
|
const found = lookupField(lookupResource(contract, resource), field);
|
|
@@ -12,3 +12,17 @@ export declare class AvDecimal {
|
|
|
12
12
|
*/
|
|
13
13
|
toJSON(): string;
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Whether `value` is an `AvDecimal`, made by this copy of core or by another of
|
|
17
|
+
* its major: one `in`, for the hot paths that ask about every value.
|
|
18
|
+
*/
|
|
19
|
+
export declare function isAvDecimal(value: unknown): value is AvDecimal;
|
|
20
|
+
/**
|
|
21
|
+
* The digits of `value`, read without running anything the caller could have put
|
|
22
|
+
* on it when this copy made it. Another copy's decimal is read through its own
|
|
23
|
+
* `toString`, which only that copy can answer; what it returns is checked as a
|
|
24
|
+
* decimal string, so the caller's copy keeps the grammar.
|
|
25
|
+
*
|
|
26
|
+
* @throws TypeError when another copy's decimal does not answer a decimal string.
|
|
27
|
+
*/
|
|
28
|
+
export declare function decimalDigitsOf(value: AvDecimal): string;
|
package/dist/runtime/decimal.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { WIRE_SCALAR_PATTERNS } from "../contracts/wire-scalar-format.js";
|
|
2
|
+
import { brandCoreClass, hasCoreBrand } from "./core-class.brand.js";
|
|
3
|
+
let ownDigitsOf;
|
|
2
4
|
export class AvDecimal {
|
|
3
5
|
#value;
|
|
6
|
+
static {
|
|
7
|
+
ownDigitsOf = (value) => (#value in value ? value.#value : undefined);
|
|
8
|
+
}
|
|
4
9
|
constructor(value) {
|
|
5
10
|
if (typeof value !== "string") {
|
|
6
11
|
throw new TypeError("Decimal value must be a string.");
|
|
@@ -17,3 +22,19 @@ export class AvDecimal {
|
|
|
17
22
|
return this.#value;
|
|
18
23
|
}
|
|
19
24
|
}
|
|
25
|
+
const DECIMAL_BRAND = brandCoreClass(AvDecimal, "AvDecimal");
|
|
26
|
+
export function isAvDecimal(value) {
|
|
27
|
+
return hasCoreBrand(value, DECIMAL_BRAND);
|
|
28
|
+
}
|
|
29
|
+
export function decimalDigitsOf(value) {
|
|
30
|
+
const own = ownDigitsOf(value);
|
|
31
|
+
if (own !== undefined) {
|
|
32
|
+
return own;
|
|
33
|
+
}
|
|
34
|
+
const digits = value.toString();
|
|
35
|
+
if (typeof digits !== "string" ||
|
|
36
|
+
!WIRE_SCALAR_PATTERNS.decimal.test(digits)) {
|
|
37
|
+
throw new TypeError("Decimal value must be a valid decimal string.");
|
|
38
|
+
}
|
|
39
|
+
return digits;
|
|
40
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AvRequestOrigin } from "../config/config-layer.js";
|
|
2
2
|
import type { AvOperationFamily } from "../operations/operation-identity.js";
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
3
|
+
import type { AvTransactionRequest } from "../transactions/transaction-request.js";
|
|
4
|
+
import type { AvOperationRequest } from "./operation-request.js";
|
|
5
5
|
import { type AvOperationErrorCode, type AvOperationFailureResponse } from "./operation-response.js";
|
|
6
6
|
/**
|
|
7
7
|
* One unplanned failure of an operation: the raw error, the code the caller
|
|
@@ -100,14 +100,14 @@ export declare function diagnosticsSinkOf(framework: object): AvDiagnosticsSink
|
|
|
100
100
|
* Binds the configured sink to one operation request. The sink is not awaited,
|
|
101
101
|
* and nothing it throws or rejects with reaches the response.
|
|
102
102
|
*/
|
|
103
|
-
export declare function createDiagnosticsReporter(sink: AvDiagnosticsSink | undefined, request:
|
|
103
|
+
export declare function createDiagnosticsReporter(sink: AvDiagnosticsSink | undefined, request: AvOperationRequest, operation?: number): DiagnosticsReporter;
|
|
104
104
|
/** The diagnostics channel of one transaction request. */
|
|
105
105
|
export type TransactionDiagnosticsChannel = {
|
|
106
106
|
/** A plan-level report; `operation` attributes it to a recorded failing index. */
|
|
107
107
|
readonly transaction: (operation?: number) => DiagnosticsReporter;
|
|
108
108
|
/** A report carrying one plan operation's own identity and index. */
|
|
109
|
-
readonly operation: (request:
|
|
109
|
+
readonly operation: (request: AvOperationRequest, operation: number) => DiagnosticsReporter;
|
|
110
110
|
};
|
|
111
111
|
/** Binds the configured sink to one transaction request and its plan operations. */
|
|
112
|
-
export declare function createTransactionDiagnosticsChannel(sink: AvDiagnosticsSink | undefined, request:
|
|
112
|
+
export declare function createTransactionDiagnosticsChannel(sink: AvDiagnosticsSink | undefined, request: AvTransactionRequest): TransactionDiagnosticsChannel;
|
|
113
113
|
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { coreBrandOf } from "./core-class.brand.js";
|
|
1
2
|
import { internalFailure, } from "./operation-response.js";
|
|
2
3
|
import { requestIdOf } from "./request-id.js";
|
|
3
4
|
const IGNORE_DIAGNOSTIC = () => undefined;
|
|
@@ -31,7 +32,18 @@ export function createReporter(sink, identity) {
|
|
|
31
32
|
}
|
|
32
33
|
};
|
|
33
34
|
}
|
|
34
|
-
const CONFIGURED_SINKS =
|
|
35
|
+
const CONFIGURED_SINKS = sharedSinkRegistry();
|
|
36
|
+
function sharedSinkRegistry() {
|
|
37
|
+
const key = coreBrandOf("diagnostics");
|
|
38
|
+
const holder = globalThis;
|
|
39
|
+
const existing = holder[key];
|
|
40
|
+
if (existing instanceof WeakMap) {
|
|
41
|
+
return existing;
|
|
42
|
+
}
|
|
43
|
+
const registry = new WeakMap();
|
|
44
|
+
Object.defineProperty(holder, key, { value: registry });
|
|
45
|
+
return registry;
|
|
46
|
+
}
|
|
35
47
|
export function mcpFailureReporterOf(framework) {
|
|
36
48
|
const sink = CONFIGURED_SINKS.get(framework);
|
|
37
49
|
return (error) => {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { brandCoreClass } from "./core-class.brand.js";
|
|
1
2
|
export class AvError extends Error {
|
|
2
3
|
name = "AvError";
|
|
3
4
|
code;
|
|
@@ -30,3 +31,11 @@ export class AvProtocolError extends AvError {
|
|
|
30
31
|
export class AvContractMismatchError extends AvError {
|
|
31
32
|
name = "AvContractMismatchError";
|
|
32
33
|
}
|
|
34
|
+
brandCoreClass(AvError, "AvError");
|
|
35
|
+
brandCoreClass(AvValidationError, "AvValidationError");
|
|
36
|
+
brandCoreClass(AvNotFoundError, "AvNotFoundError");
|
|
37
|
+
brandCoreClass(AvConflictError, "AvConflictError");
|
|
38
|
+
brandCoreClass(AvAuthError, "AvAuthError");
|
|
39
|
+
brandCoreClass(AvInternalError, "AvInternalError");
|
|
40
|
+
brandCoreClass(AvProtocolError, "AvProtocolError");
|
|
41
|
+
brandCoreClass(AvContractMismatchError, "AvContractMismatchError");
|
|
@@ -6,7 +6,7 @@ import type { AvState } from "./request-state.js";
|
|
|
6
6
|
* The protocol version and Contract hash a remote caller says it was built for:
|
|
7
7
|
* the Client Contract's for a client, the Agent Contract's for an agent.
|
|
8
8
|
*/
|
|
9
|
-
export type
|
|
9
|
+
export type AvRequestProtocol = {
|
|
10
10
|
readonly version: number;
|
|
11
11
|
readonly hash: string;
|
|
12
12
|
};
|
|
@@ -21,7 +21,7 @@ type RuntimeOperationBase = AvOperationIdentity & {
|
|
|
21
21
|
readonly state?: AvState;
|
|
22
22
|
};
|
|
23
23
|
/** An in-process request, run under the Application Contract. */
|
|
24
|
-
export type
|
|
24
|
+
export type AvApplicationOperationRequest = RuntimeOperationBase & {
|
|
25
25
|
readonly origin: "application";
|
|
26
26
|
readonly protocol?: never;
|
|
27
27
|
readonly transport?: never;
|
|
@@ -30,24 +30,24 @@ export type ApplicationOperationRequest = RuntimeOperationBase & {
|
|
|
30
30
|
* A remote request, run under the Client Contract; `protocol` is the identity
|
|
31
31
|
* the client sent.
|
|
32
32
|
*/
|
|
33
|
-
export type
|
|
33
|
+
export type AvClientOperationRequest = RuntimeOperationBase & {
|
|
34
34
|
readonly origin: "client";
|
|
35
|
-
readonly protocol:
|
|
35
|
+
readonly protocol: AvRequestProtocol;
|
|
36
36
|
readonly transport?: AvTransportContext;
|
|
37
37
|
};
|
|
38
38
|
/**
|
|
39
39
|
* An AI agent's request, run under the Agent Contract; `protocol` is the
|
|
40
40
|
* identity of the Agent Contract the caller (an MCP server) was built from.
|
|
41
41
|
*/
|
|
42
|
-
export type
|
|
42
|
+
export type AvAgentOperationRequest = RuntimeOperationBase & {
|
|
43
43
|
readonly origin: "agent";
|
|
44
|
-
readonly protocol:
|
|
44
|
+
readonly protocol: AvRequestProtocol;
|
|
45
45
|
readonly transport?: AvTransportContext;
|
|
46
46
|
};
|
|
47
47
|
/** A request from outside the server: a client's or an agent's. */
|
|
48
|
-
export type RemoteOperationRequest =
|
|
48
|
+
export type RemoteOperationRequest = AvClientOperationRequest | AvAgentOperationRequest;
|
|
49
49
|
/** One operation request, as `framework.execute` takes it. */
|
|
50
|
-
export type
|
|
50
|
+
export type AvOperationRequest = AvApplicationOperationRequest | AvClientOperationRequest | AvAgentOperationRequest;
|
|
51
51
|
type WithoutArguments<Request> = Request extends unknown ? Omit<Request, "arguments"> : never;
|
|
52
52
|
/**
|
|
53
53
|
* A request as every stage after the Framework Validation Layer sees it:
|
|
@@ -57,7 +57,7 @@ type WithoutArguments<Request> = Request extends unknown ? Omit<Request, "argume
|
|
|
57
57
|
* unreachable by type. Distributive, so the origin and family/variant
|
|
58
58
|
* correlations survive the omission.
|
|
59
59
|
*/
|
|
60
|
-
export type OperationExecutionRequest = WithoutArguments<
|
|
60
|
+
export type OperationExecutionRequest = WithoutArguments<AvOperationRequest>;
|
|
61
61
|
/**
|
|
62
62
|
* A request whose stages run: its `state` settled once, the seed or the `{}`
|
|
63
63
|
* the runtime made for it, so every stage is told the same object.
|
|
@@ -8,14 +8,14 @@ import type { AvRequestOrigin, RemoteRequestOrigin } from "../config/config-laye
|
|
|
8
8
|
import type { AvContract } from "../contracts/contract.js";
|
|
9
9
|
import type { OperationValidationOptions, SemanticOperationArguments } from "../operations/operation-validator.js";
|
|
10
10
|
import { type PolicyCaller } from "../policy/policy-request.js";
|
|
11
|
-
import type {
|
|
11
|
+
import type { AvTransactionRequest } from "../transactions/transaction-request.js";
|
|
12
12
|
import { type ArgumentsSource } from "./caller-data.door.js";
|
|
13
13
|
import { type ContextExecutor, type ExecutorScope } from "./context-executor.runtime.js";
|
|
14
14
|
import { type RecordWriteFacts } from "./create-requirement.js";
|
|
15
15
|
import { type DiagnosticsReporter } from "./diagnostics-reporter.js";
|
|
16
16
|
import { type OuterFailureLayer } from "./error-boundary.js";
|
|
17
17
|
import { type FieldStagePlan } from "./field-stage.plan.js";
|
|
18
|
-
import type {
|
|
18
|
+
import type { AvOperationRequest, AvRequestProtocol, StatefulOperationRequest } from "./operation-request.js";
|
|
19
19
|
import type { AvOperationFailureResponse, AvOperationResponse } from "./operation-response.js";
|
|
20
20
|
import type { AvState } from "./request-state.js";
|
|
21
21
|
import { type NormalizationWrite } from "./scalar-runtime.js";
|
|
@@ -70,7 +70,7 @@ export declare function withRequestState<Request extends {
|
|
|
70
70
|
* `request` with its policy session when the policy checks it, or the failure
|
|
71
71
|
* deriving its principal answered.
|
|
72
72
|
*/
|
|
73
|
-
export declare function withRequestPolicy<M extends AvAdapterModel>(dependencies: Pick<OperationRuntimeDependencies<M>, "adapter" | "config" | "contracts">, request: StatefulOperationRequest, caller: PolicyCaller | undefined, report: DiagnosticsReporter): StatefulOperationRequest | AvOperationFailureResponse
|
|
73
|
+
export declare function withRequestPolicy<M extends AvAdapterModel>(dependencies: Pick<OperationRuntimeDependencies<M>, "adapter" | "config" | "contracts">, request: StatefulOperationRequest, caller: PolicyCaller | undefined, report: DiagnosticsReporter): StatefulOperationRequest | AvOperationFailureResponse | Promise<StatefulOperationRequest | AvOperationFailureResponse>;
|
|
74
74
|
/**
|
|
75
75
|
* Builds what every operation's pipeline shares, the ctx executor included: its
|
|
76
76
|
* raw calls run the application's operation path with every developer stage
|
|
@@ -85,15 +85,15 @@ export declare function createPipelineDependencies<M extends AvAdapterModel>(dep
|
|
|
85
85
|
* request's arguments are the caller's own objects, copied at the door, or data
|
|
86
86
|
* no caller holds (see {@link ArgumentsSource}).
|
|
87
87
|
*/
|
|
88
|
-
export type FrameworkOperationExecutor = (request:
|
|
88
|
+
export type FrameworkOperationExecutor = (request: AvOperationRequest, report: DiagnosticsReporter, source: ArgumentsSource,
|
|
89
89
|
/** The principal an application call runs as (`{ as }`), when it names one. */
|
|
90
90
|
caller?: PolicyCaller) => Promise<AvOperationResponse>;
|
|
91
91
|
/** Selects the Contract of the request's origin. */
|
|
92
|
-
export declare function activeContract(contracts: AvContracts, request:
|
|
92
|
+
export declare function activeContract(contracts: AvContracts, request: AvOperationRequest | AvTransactionRequest): AvContract;
|
|
93
93
|
/** Creates the transport-independent operation executor owned by core. */
|
|
94
94
|
export declare function createOperationRuntime<M extends AvAdapterModel>(dependencies: OperationRuntimeDependencies<M>, pipelineDependencies: OperationPipelineDependencies<M>): FrameworkOperationExecutor;
|
|
95
95
|
/** Validates the request-level protocol identity. */
|
|
96
|
-
export declare function validateProtocolIdentity(contracts: AvContracts, request:
|
|
96
|
+
export declare function validateProtocolIdentity(contracts: AvContracts, request: AvOperationRequest | AvTransactionRequest): AvOperationFailureResponse | undefined;
|
|
97
97
|
/**
|
|
98
98
|
* The stale-client check on one remote protocol identity: the protocol version first
|
|
99
99
|
* (`A2006`), then the hash of the origin's Contract (`A2005`): the ClientContract's
|
|
@@ -102,7 +102,7 @@ export declare function validateProtocolIdentity(contracts: AvContracts, request
|
|
|
102
102
|
* (`@aventara/core/protocol`) call it too, so a stale client hears the same envelope
|
|
103
103
|
* whichever layer answers.
|
|
104
104
|
*/
|
|
105
|
-
export declare function protocolIdentityFailure(contracts: AvContracts, protocol:
|
|
105
|
+
export declare function protocolIdentityFailure(contracts: AvContracts, protocol: AvRequestProtocol, origin: RemoteRequestOrigin): AvOperationFailureResponse | undefined;
|
|
106
106
|
export type ValidatedOperationArguments = {
|
|
107
107
|
readonly ok: true;
|
|
108
108
|
readonly arguments: SemanticOperationArguments;
|
|
@@ -111,7 +111,7 @@ export type ValidatedOperationArguments = {
|
|
|
111
111
|
readonly response: AvOperationFailureResponse;
|
|
112
112
|
};
|
|
113
113
|
/** Validates one operation's arguments through the Framework Validation Layer. */
|
|
114
|
-
export declare function validateOperationRequest(contract: AvContract, request:
|
|
114
|
+
export declare function validateOperationRequest(contract: AvContract, request: AvOperationRequest, options?: OperationValidationOptions, write?: NormalizationWrite): ValidatedOperationArguments;
|
|
115
115
|
/** The subject a refused output of an operation's pipes is reported under at a plan step. */
|
|
116
116
|
export declare const STEP_PIPES = "Pipes";
|
|
117
117
|
/**
|
|
@@ -44,13 +44,19 @@ export function withRequestState(request) {
|
|
|
44
44
|
return { ...request, state: requestStateOf(request.state) };
|
|
45
45
|
}
|
|
46
46
|
export function withRequestPolicy(dependencies, request, caller, report) {
|
|
47
|
+
const failed = (error) => {
|
|
48
|
+
report(error, "A3000");
|
|
49
|
+
return internalFailure();
|
|
50
|
+
};
|
|
51
|
+
const withPolicy = (policy) => policy === undefined ? request : { ...request, policy };
|
|
47
52
|
try {
|
|
48
53
|
const policy = openRequestPolicy(dependencies.config.policy, dependencies.contracts.core, request, caller, dependencies.adapter);
|
|
49
|
-
return policy
|
|
54
|
+
return policy instanceof Promise
|
|
55
|
+
? policy.then(withPolicy, failed)
|
|
56
|
+
: withPolicy(policy);
|
|
50
57
|
}
|
|
51
58
|
catch (error) {
|
|
52
|
-
|
|
53
|
-
return internalFailure();
|
|
59
|
+
return failed(error);
|
|
54
60
|
}
|
|
55
61
|
}
|
|
56
62
|
export function createPipelineDependencies(dependencies, model) {
|
|
@@ -197,7 +203,8 @@ export function createOperationRuntime(dependencies, pipelineDependencies) {
|
|
|
197
203
|
if (!validated.ok) {
|
|
198
204
|
return validated.response;
|
|
199
205
|
}
|
|
200
|
-
const
|
|
206
|
+
const opened = withRequestPolicy(dependencies, withRequestState(request), caller, report);
|
|
207
|
+
const stateful = opened instanceof Promise ? await opened : opened;
|
|
201
208
|
if (!("state" in stateful)) {
|
|
202
209
|
return stateful;
|
|
203
210
|
}
|
|
@@ -612,8 +619,10 @@ export async function executeAdmittedOperation(dependencies, request, contract,
|
|
|
612
619
|
let operation = request;
|
|
613
620
|
let settle;
|
|
614
621
|
let writeMiss;
|
|
622
|
+
let failedWriteMiss;
|
|
615
623
|
if (policyWrite !== undefined && policyWrite.kind !== "refused") {
|
|
616
624
|
writeMiss = policyWrite.miss;
|
|
625
|
+
failedWriteMiss = policyWrite.failedMiss;
|
|
617
626
|
if (policyWrite.kind === "empty") {
|
|
618
627
|
if (writeMiss !== undefined && policyWrite.data === null) {
|
|
619
628
|
refuseWrite(await writeMiss());
|
|
@@ -643,11 +652,23 @@ export async function executeAdmittedOperation(dependencies, request, contract,
|
|
|
643
652
|
settle = read.settle;
|
|
644
653
|
}
|
|
645
654
|
const { policy: _session, ...identity } = operation;
|
|
646
|
-
let adapterData
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
655
|
+
let adapterData;
|
|
656
|
+
try {
|
|
657
|
+
adapterData = await adapterBoundary.execute(() => executor.execute({
|
|
658
|
+
...identity,
|
|
659
|
+
resource: request.resource,
|
|
660
|
+
arguments: adapterArguments,
|
|
661
|
+
}));
|
|
662
|
+
}
|
|
663
|
+
catch (error) {
|
|
664
|
+
const missed = failedWriteMiss === undefined
|
|
665
|
+
? undefined
|
|
666
|
+
: await failedWriteMiss().catch(() => undefined);
|
|
667
|
+
if (missed !== undefined) {
|
|
668
|
+
refuseWrite(missed);
|
|
669
|
+
}
|
|
670
|
+
throw error;
|
|
671
|
+
}
|
|
651
672
|
if (writeMiss !== undefined && adapterData === null) {
|
|
652
673
|
refuseWrite(await writeMiss());
|
|
653
674
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AvAgentTransactionRequest, AvClientTransactionRequest } from "../transactions/transaction-request.js";
|
|
2
2
|
import type { AvTransactionResponse } from "../transactions/transaction-response.js";
|
|
3
3
|
import type { AvCallerDataError } from "./caller-data.copier.js";
|
|
4
4
|
import type { RemoteOperationRequest } from "./operation-request.js";
|
|
@@ -15,7 +15,7 @@ export type OwnedExecution = {
|
|
|
15
15
|
* at the call, and these steps' arguments could not be copied there: each is
|
|
16
16
|
* answered at its turn as the door answers that failure.
|
|
17
17
|
*/
|
|
18
|
-
readonly executeTransaction: (request:
|
|
18
|
+
readonly executeTransaction: (request: AvClientTransactionRequest | AvAgentTransactionRequest, refused?: ReadonlyMap<number, AvCallerDataError>) => Promise<AvTransactionResponse>;
|
|
19
19
|
};
|
|
20
20
|
/** Records `framework`'s owned entries; called once, at construction. */
|
|
21
21
|
export declare function recordOwnedExecution(framework: object, execution: OwnedExecution): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AvDecimal } from "./decimal.js";
|
|
1
|
+
import { type AvDecimal } from "./decimal.js";
|
|
2
2
|
/** A value `AvDeepReadonly` leaves as it is: a primitive, a function, or a scalar instance. */
|
|
3
3
|
export type AvDeepReadonlyLeaf = string | number | boolean | bigint | symbol | null | undefined | Date | Uint8Array | AvDecimal | ((...args: never[]) => unknown);
|
|
4
4
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isAvDecimal } from "./decimal.js";
|
|
2
2
|
function isContainer(value) {
|
|
3
3
|
if (value === null || typeof value !== "object") {
|
|
4
4
|
return false;
|
|
@@ -6,7 +6,7 @@ function isContainer(value) {
|
|
|
6
6
|
if (Array.isArray(value)) {
|
|
7
7
|
return true;
|
|
8
8
|
}
|
|
9
|
-
if (value
|
|
9
|
+
if (isAvDecimal(value)) {
|
|
10
10
|
return false;
|
|
11
11
|
}
|
|
12
12
|
const prototype = Object.getPrototypeOf(value);
|
|
@@ -29,7 +29,7 @@ export type ReferentialEdge = {
|
|
|
29
29
|
* relation `field` and any of these names the reference twice and is refused;
|
|
30
30
|
* a required relation may be written through all of them instead.
|
|
31
31
|
*/
|
|
32
|
-
readonly
|
|
32
|
+
readonly referenceFields: readonly string[];
|
|
33
33
|
/** What deleting a `target` row does to rows of the ENCLOSING Resource. */
|
|
34
34
|
readonly onDelete: ReferentialDeleteEffect;
|
|
35
35
|
};
|
|
@@ -4,7 +4,7 @@ import { FILTER_MODES, } from "../contracts/capability.js";
|
|
|
4
4
|
import { lookupField, lookupOwn, lookupResource, } from "../contracts/contract-lookup.js";
|
|
5
5
|
import { decodeBase64, WIRE_SCALAR_PATTERNS, } from "../contracts/wire-scalar-format.js";
|
|
6
6
|
import { routeTransactionReferenceCandidate, } from "../transactions/transaction-reference.js";
|
|
7
|
-
import { AvDecimal } from "./decimal.js";
|
|
7
|
+
import { AvDecimal, isAvDecimal } from "./decimal.js";
|
|
8
8
|
import { fieldCheckIssues } from "./field-check.evaluator.js";
|
|
9
9
|
const INT_MIN = -(2 ** 31);
|
|
10
10
|
const INT_MAX = 2 ** 31 - 1;
|
|
@@ -238,10 +238,9 @@ function normalizeScalarAtom(contract, field, value, origin, path, allowNull) {
|
|
|
238
238
|
break;
|
|
239
239
|
case "decimal":
|
|
240
240
|
if (origin === "application") {
|
|
241
|
-
normalized =
|
|
242
|
-
value
|
|
243
|
-
|
|
244
|
-
: { ok: false, issues: [typeIssue(path, "an Aventara Decimal")] };
|
|
241
|
+
normalized = isAvDecimal(value)
|
|
242
|
+
? { ok: true, value }
|
|
243
|
+
: { ok: false, issues: [typeIssue(path, "an Aventara Decimal")] };
|
|
245
244
|
}
|
|
246
245
|
else if (typeof value !== "string") {
|
|
247
246
|
normalized = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { lookupField, lookupResource } from "../contracts/contract-lookup.js";
|
|
2
2
|
import { encodeBase64 } from "../contracts/wire-scalar-format.js";
|
|
3
|
-
import {
|
|
3
|
+
import { isAvDecimal } from "../runtime/decimal.js";
|
|
4
4
|
import { isRelationMutationDirective, RELATION_MUTATION_RECORD_SITES, } from "../runtime/mutation-planner.js";
|
|
5
5
|
import { isTransactionReferenceCandidate } from "./transaction-reference.js";
|
|
6
6
|
function isPlainRecord(value) {
|
|
@@ -27,9 +27,11 @@ function encodeScalarAtom(field, value) {
|
|
|
27
27
|
case "bigint":
|
|
28
28
|
return typeof value === "bigint" ? value.toString(10) : value;
|
|
29
29
|
case "decimal":
|
|
30
|
-
return value
|
|
30
|
+
return isAvDecimal(value) ? value.toString() : value;
|
|
31
31
|
case "datetime":
|
|
32
|
-
return value instanceof Date
|
|
32
|
+
return value instanceof Date && !Number.isNaN(value.getTime())
|
|
33
|
+
? value.toISOString()
|
|
34
|
+
: value;
|
|
33
35
|
case "bytes":
|
|
34
36
|
return value instanceof Uint8Array ? encodeBase64(value) : value;
|
|
35
37
|
default:
|
|
@@ -282,6 +284,9 @@ function isUniqueOperation(operation) {
|
|
|
282
284
|
operation.family === "upsert"));
|
|
283
285
|
}
|
|
284
286
|
export function encodeOperationArgumentsForWire(contract, operation, args) {
|
|
287
|
+
if (!isPlainRecord(args)) {
|
|
288
|
+
return args;
|
|
289
|
+
}
|
|
285
290
|
const encoded = cloneRecord(args);
|
|
286
291
|
if (hasOwnWireProperty(args, "where")) {
|
|
287
292
|
encoded.where = isUniqueOperation(operation)
|
|
@@ -10,7 +10,7 @@ import type { AvTransactionReference } from "./transaction-reference.js";
|
|
|
10
10
|
* `${value}::jsonb`), or a `$ref` to an earlier step's field, resolved when the
|
|
11
11
|
* earlier steps have run and then bound like any other value.
|
|
12
12
|
*/
|
|
13
|
-
export type
|
|
13
|
+
export type AvSqlStatementValue = AvAdapterSqlValue | AvTransactionReference;
|
|
14
14
|
/**
|
|
15
15
|
* `framework.appTx.$sql`: a developer SQL step, written only as a tagged template —
|
|
16
16
|
* ``framework.appTx.$sql`select pgmq.send('jobs', jsonb_build_object('id', ${ref}))` ``.
|
|
@@ -23,7 +23,7 @@ export type SqlStatementValue = AvAdapterSqlValue | AvTransactionReference;
|
|
|
23
23
|
* SQL step's rows. No guard, pipe, hook, interceptor or filter runs around it: it
|
|
24
24
|
* has no Resource or operation for one to wrap.
|
|
25
25
|
*/
|
|
26
|
-
export type
|
|
26
|
+
export type AvSqlStatementBuilder = <Row = unknown>(strings: TemplateStringsArray, ...values: readonly AvSqlStatementValue[]) => AvOperation<Row[], "application">;
|
|
27
27
|
/** One value of a planned SQL step, as the typed runner bound it. */
|
|
28
28
|
export type PlannedSqlValue = {
|
|
29
29
|
readonly kind: "value";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { validationIssue } from "../operations/validation-code.js";
|
|
2
2
|
import { AvCallerDataError, locateCallerDataError, } from "../runtime/caller-data.copier.js";
|
|
3
3
|
import { refuseCallerData } from "../runtime/caller-data.door.js";
|
|
4
|
-
import {
|
|
4
|
+
import { isAvDecimal } from "../runtime/decimal.js";
|
|
5
5
|
import { planStepPath } from "../runtime/error-boundary.js";
|
|
6
6
|
import { operationFailure } from "../runtime/operation-response.js";
|
|
7
7
|
import { mapValidationOutcome } from "../runtime/outcome-mapper.js";
|
|
@@ -19,7 +19,7 @@ export function normalizeSqlValue(value, path) {
|
|
|
19
19
|
typeof value === "string" ||
|
|
20
20
|
typeof value === "boolean" ||
|
|
21
21
|
typeof value === "bigint" ||
|
|
22
|
-
value
|
|
22
|
+
isAvDecimal(value) ||
|
|
23
23
|
value instanceof Uint8Array) {
|
|
24
24
|
return { ok: true, value };
|
|
25
25
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { brandCoreClass } from "../runtime/core-class.brand.js";
|
|
1
2
|
const TRANSACTION_INTEGRITY_MESSAGES = {
|
|
2
3
|
"transaction-unavailable": "Adapter transaction function is unavailable.",
|
|
3
4
|
"resolved-after-abort": "Adapter transaction resolved after the framework requested rollback.",
|
|
@@ -15,3 +16,4 @@ export class AvTransactionIntegrityError extends Error {
|
|
|
15
16
|
this.violation = violation;
|
|
16
17
|
}
|
|
17
18
|
}
|
|
19
|
+
brandCoreClass(AvTransactionIntegrityError, "AvTransactionIntegrityError");
|
|
@@ -2,13 +2,15 @@ import type { AvContracts } from "../compiler/contract.compiler.js";
|
|
|
2
2
|
import type { AvOperationIdentity } from "../operations/operation-identity.js";
|
|
3
3
|
import { AvCallerDataError } from "../runtime/caller-data.copier.js";
|
|
4
4
|
import type { MisuseReporter } from "../runtime/misuse-reporter.js";
|
|
5
|
-
import type {
|
|
5
|
+
import type { AvRequestProtocol } from "../runtime/operation-request.js";
|
|
6
6
|
import type { AvOperationFailureResponse } from "../runtime/operation-response.js";
|
|
7
7
|
import type { AvOperation, OperationFingerprint } from "./deferred-operation.js";
|
|
8
8
|
import type { PlannedSqlStatement } from "./sql-statement.js";
|
|
9
9
|
import type { TransactionOperationOrigin } from "./transaction-request.js";
|
|
10
10
|
/** The origin a handle's builder gives it. */
|
|
11
11
|
type HandleOrigin = "application" | "client";
|
|
12
|
+
/** The scalar forms a builder takes its arguments in. */
|
|
13
|
+
type ArgumentForms = "wire" | "application";
|
|
12
14
|
/** One wire node, exactly as the transaction runtime receives it. */
|
|
13
15
|
type WireOperationNode = {
|
|
14
16
|
readonly resource: string;
|
|
@@ -37,8 +39,13 @@ export type AssembledTransactionPlan = {
|
|
|
37
39
|
readonly response: AvOperationFailureResponse;
|
|
38
40
|
};
|
|
39
41
|
export type TransactionPlanAssembler = {
|
|
40
|
-
/**
|
|
41
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Defers one operation into a handle; nothing runs. `forms` says which scalar
|
|
44
|
+
* forms a client-origin handle's arguments are in: `"wire"` (`clientTx`, the
|
|
45
|
+
* default) or `"application"` (`serverClientTx`), encoded into the wire forms
|
|
46
|
+
* from the builder's copy.
|
|
47
|
+
*/
|
|
48
|
+
defer(origin: HandleOrigin, resource: string, identity: AvOperationIdentity, args: unknown, forms?: ArgumentForms): AvOperation<unknown>;
|
|
42
49
|
/** Defers one `appTx.$sql` statement into a handle; nothing runs. */
|
|
43
50
|
deferSql(strings: unknown, values: readonly unknown[]): AvOperation<unknown>;
|
|
44
51
|
/** Binds a list of handles into the passive plan, or refuses it. */
|
|
@@ -49,5 +56,5 @@ export type TransactionPlanAssembler = {
|
|
|
49
56
|
* handle, so a handle another instance built — typed against another model,
|
|
50
57
|
* bound to other Contracts — is refused rather than run.
|
|
51
58
|
*/
|
|
52
|
-
export declare function createTransactionPlanAssembler(contracts: AvContracts, protocol:
|
|
59
|
+
export declare function createTransactionPlanAssembler(contracts: AvContracts, protocol: AvRequestProtocol, misuse: MisuseReporter): TransactionPlanAssembler;
|
|
53
60
|
export {};
|
|
@@ -139,8 +139,23 @@ export function createTransactionPlanAssembler(contracts, protocol, misuse) {
|
|
|
139
139
|
handles.set(handle, record);
|
|
140
140
|
return handle;
|
|
141
141
|
};
|
|
142
|
-
const defer = (origin, resource, identity, args) => {
|
|
143
|
-
const
|
|
142
|
+
const defer = (origin, resource, identity, args, forms = "wire") => {
|
|
143
|
+
const snapped = snapshot(args, STEP_REFERENCE_MIRROR);
|
|
144
|
+
let copy = snapped;
|
|
145
|
+
if (origin === "client" &&
|
|
146
|
+
forms === "application" &&
|
|
147
|
+
snapped.refused === undefined) {
|
|
148
|
+
try {
|
|
149
|
+
copy = {
|
|
150
|
+
args: encodeOperationArgumentsForWire(contracts.client, { resource, ...identity }, snapped.args),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
if (!(error instanceof AvCallerDataError)) {
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
144
159
|
let sent = copy.args;
|
|
145
160
|
try {
|
|
146
161
|
sent = withoutUnsentWriteValues(origin === "application" ? contracts.application : contracts.client, { resource, family: identity.family }, copy.args);
|
|
@@ -4,7 +4,7 @@ import type { AvScalarField } from "../contracts/field.js";
|
|
|
4
4
|
import type { SemanticOperationArguments } from "../operations/operation-validator.js";
|
|
5
5
|
import { AvCallerDataError } from "../runtime/caller-data.copier.js";
|
|
6
6
|
import { type ReadProjectionPlan } from "../runtime/computed-read-runtime.js";
|
|
7
|
-
import type {
|
|
7
|
+
import type { AvOperationRequest } from "../runtime/operation-request.js";
|
|
8
8
|
import type { AvOperationFailureResponse } from "../runtime/operation-response.js";
|
|
9
9
|
import type { OperationPipelineDependencies } from "../runtime/operation-runtime.js";
|
|
10
10
|
import type { CascadeClosure } from "../runtime/referential-action.js";
|
|
@@ -13,7 +13,7 @@ import type { PlannedSqlStatement, ValidatedSqlStatement } from "./sql-statement
|
|
|
13
13
|
import type { AvTransactionReference } from "./transaction-reference.js";
|
|
14
14
|
import { type TransactionSlotShape } from "./transaction-reference.js";
|
|
15
15
|
import { type TransactionReferenceCandidateSlot } from "./transaction-reference-collector.js";
|
|
16
|
-
import type {
|
|
16
|
+
import type { AvTransactionRequest, TransactionOperationOrigin } from "./transaction-request.js";
|
|
17
17
|
/**
|
|
18
18
|
* Where a plan's step arguments came from, so the validator knows whether to copy
|
|
19
19
|
* them at the door (see `ArgumentsSource`):
|
|
@@ -65,7 +65,7 @@ export type TransactionReferenceSlot = {
|
|
|
65
65
|
};
|
|
66
66
|
export type CandidateTransactionOperation = {
|
|
67
67
|
readonly index: number;
|
|
68
|
-
readonly request:
|
|
68
|
+
readonly request: AvOperationRequest;
|
|
69
69
|
readonly arguments: SemanticOperationArguments;
|
|
70
70
|
readonly projection: ReadProjectionPlan | undefined;
|
|
71
71
|
readonly declaredFingerprint: unknown;
|
|
@@ -74,7 +74,7 @@ export type CandidateTransactionOperation = {
|
|
|
74
74
|
export type ValidatedTransactionOperation = {
|
|
75
75
|
readonly kind: "operation";
|
|
76
76
|
readonly index: number;
|
|
77
|
-
readonly request:
|
|
77
|
+
readonly request: AvOperationRequest;
|
|
78
78
|
/**
|
|
79
79
|
* The Contract of this operation's origin, which is the plan's: the one it was
|
|
80
80
|
* validated against, the one it runs under, and the one a later `$ref` reads its
|
|
@@ -118,4 +118,4 @@ export declare function transactionsUnavailableFailure(): AvOperationFailureResp
|
|
|
118
118
|
/**
|
|
119
119
|
* Whole-plan Framework Validation Layer for transaction structure and identity.
|
|
120
120
|
*/
|
|
121
|
-
export declare function validateTransactionPlan<M extends AvAdapterModel>(dependencies: TransactionPlanDependencies<M>, request:
|
|
121
|
+
export declare function validateTransactionPlan<M extends AvAdapterModel>(dependencies: TransactionPlanDependencies<M>, request: AvTransactionRequest, origins?: readonly TransactionOperationOrigin[], planArguments?: PlanArguments): Promise<TransactionPlanValidationResult>;
|