@aventara/core 0.1.0-pilot.4 → 1.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +338 -0
- package/README.md +100 -3
- package/dist/adapters/adapter-model.d.ts +8 -0
- package/dist/adapters/adapter.interface.d.ts +6 -2
- package/dist/compiler/contract-compiler-diagnostic.d.ts +6 -0
- package/dist/compiler/contract.compiler.js +67 -0
- package/dist/config/config.resolver.d.ts +3 -0
- package/dist/config/config.resolver.js +1 -0
- package/dist/config/config.validator.js +6 -0
- package/dist/config/define-fields.d.ts +10 -3
- package/dist/config/framework-config.d.ts +4 -1
- package/dist/config/framework-fields-config.d.ts +6 -0
- package/dist/contracts/client-contract-structure.validator.d.ts +21 -1
- package/dist/contracts/client-contract-structure.validator.js +99 -2
- package/dist/contracts/contract-canonicalizer.js +32 -0
- package/dist/contracts/contract.d.ts +7 -0
- package/dist/contracts/field.d.ts +10 -0
- package/dist/contracts/json-shape.d.ts +65 -0
- package/dist/contracts/json-shape.js +1 -0
- package/dist/contracts/reference-write-limit.d.ts +7 -0
- package/dist/contracts/reference-write-limit.js +1 -0
- package/dist/contracts/scalar-value-type.d.ts +12 -1
- package/dist/framework/contract-view.d.ts +126 -21
- package/dist/framework/framework.d.ts +5 -3
- package/dist/framework/framework.factory.js +56 -13
- package/dist/index.d.ts +8 -40
- package/dist/index.js +0 -20
- package/dist/internal.d.ts +35 -0
- package/dist/internal.js +16 -0
- package/dist/json/avz.d.ts +96 -0
- package/dist/json/avz.js +212 -0
- package/dist/json/index.d.ts +6 -0
- package/dist/json/index.js +1 -0
- package/dist/json/json-shape.registry.d.ts +22 -0
- package/dist/json/json-shape.registry.js +16 -0
- package/dist/json/zod-issue.translator.d.ts +51 -0
- package/dist/json/zod-issue.translator.js +142 -0
- package/dist/operations/operation-arguments.d.ts +151 -101
- package/dist/operations/operation-identity.d.ts +2 -0
- package/dist/operations/operation-identity.js +3 -0
- package/dist/operations/operation-validator-core.d.ts +1 -1
- package/dist/operations/operation-validator-core.js +28 -10
- package/dist/operations/operation-validator.d.ts +20 -5
- package/dist/operations/operation-validator.js +26 -10
- package/dist/operations/projection-scope.resolver.d.ts +1 -1
- package/dist/operations/structural-limits.walker.d.ts +19 -0
- package/dist/operations/structural-limits.walker.js +344 -0
- package/dist/protocol/absent-route.responder.d.ts +2 -1
- package/dist/protocol/absent-route.responder.js +32 -6
- package/dist/protocol/http-request.decoder.d.ts +6 -0
- package/dist/protocol/http-request.decoder.js +11 -3
- package/dist/protocol/protocol.binder.d.ts +4 -2
- package/dist/protocol/protocol.binder.js +3 -1
- package/dist/protocol/protocol.handler.d.ts +6 -2
- package/dist/protocol/protocol.handler.js +14 -4
- package/dist/protocol/unmounted-route.error.d.ts +19 -0
- package/dist/protocol/unmounted-route.error.js +10 -0
- package/dist/runtime/caller-data.copier.d.ts +59 -0
- package/dist/runtime/caller-data.copier.js +470 -0
- package/dist/runtime/caller-data.door.d.ts +56 -0
- package/dist/runtime/caller-data.door.js +65 -0
- package/dist/runtime/computed-read-runtime.js +3 -10
- package/dist/runtime/computed-value.error.d.ts +13 -0
- package/dist/runtime/computed-value.error.js +9 -0
- package/dist/runtime/create-requirement.d.ts +43 -7
- package/dist/runtime/create-requirement.js +89 -9
- package/dist/runtime/diagnostics-reporter.d.ts +20 -4
- package/dist/runtime/diagnostics-reporter.js +11 -4
- package/dist/runtime/error-boundary.d.ts +8 -0
- package/dist/runtime/error-boundary.js +65 -35
- package/dist/runtime/field-behavior.d.ts +2 -2
- package/dist/runtime/framework-error.d.ts +12 -0
- package/dist/runtime/misuse-reporter.d.ts +32 -0
- package/dist/runtime/misuse-reporter.js +19 -0
- package/dist/runtime/mutation-planner.js +13 -11
- package/dist/runtime/operation-request.d.ts +4 -11
- package/dist/runtime/operation-runtime.d.ts +14 -7
- package/dist/runtime/operation-runtime.js +37 -18
- package/dist/runtime/outcome-mapper.js +12 -2
- package/dist/runtime/output-validator.d.ts +10 -0
- package/dist/runtime/output-validator.js +11 -0
- package/dist/runtime/owned-execution.registry.d.ts +13 -0
- package/dist/runtime/owned-execution.registry.js +7 -0
- package/dist/runtime/pipeline.d.ts +19 -7
- package/dist/runtime/referential-action.d.ts +7 -0
- package/dist/runtime/request-envelope.validator.d.ts +5 -0
- package/dist/runtime/request-envelope.validator.js +118 -0
- package/dist/runtime/scalar-runtime.d.ts +30 -2
- package/dist/runtime/scalar-runtime.js +162 -79
- package/dist/transactions/transaction-plan-assembler.d.ts +5 -1
- package/dist/transactions/transaction-plan-assembler.js +74 -75
- package/dist/transactions/transaction-plan-validator.d.ts +30 -7
- package/dist/transactions/transaction-plan-validator.js +204 -153
- package/dist/transactions/transaction-reference-collector.d.ts +30 -0
- package/dist/transactions/transaction-reference-collector.js +204 -0
- package/dist/transactions/transaction-reference-resolver.js +10 -5
- package/dist/transactions/transaction-reference-validator.js +24 -17
- package/dist/transactions/transaction-reference.d.ts +23 -51
- package/dist/transactions/transaction-request.d.ts +3 -3
- package/dist/transactions/transaction-response.d.ts +6 -1
- package/dist/transactions/transaction-response.js +24 -1
- package/dist/transactions/transaction-runtime.d.ts +4 -2
- package/dist/transactions/transaction-runtime.js +4 -3
- package/package.json +17 -3
- package/dist/operations/operation-boolean-nodes.d.ts +0 -11
- package/dist/operations/operation-boolean-nodes.js +0 -283
- package/dist/operations/operation-nesting.d.ts +0 -8
- package/dist/operations/operation-nesting.js +0 -307
|
@@ -1,17 +1,67 @@
|
|
|
1
1
|
import { CanonicalJsonError } from "../contracts/contract-canonicalizer.js";
|
|
2
|
-
import { lookupField,
|
|
2
|
+
import { lookupField, lookupResource } from "../contracts/contract-lookup.js";
|
|
3
3
|
import { parseOperationIdentity } from "../operations/operation-identity.js";
|
|
4
|
+
import { findOperationAvailabilityIssue, unavailableOperationKey, } from "../operations/operation-validator.js";
|
|
4
5
|
import { VALIDATION_CODE_MESSAGES, validationIssue, } from "../operations/validation-code.js";
|
|
6
|
+
import { CallerDataError, locateCallerDataError, } from "../runtime/caller-data.copier.js";
|
|
7
|
+
import { copyAtDoor, misuseIdentityOf, refuseCallerData, } from "../runtime/caller-data.door.js";
|
|
5
8
|
import { planReadOperation, } from "../runtime/computed-read-runtime.js";
|
|
6
|
-
import {
|
|
9
|
+
import { planOperationArgumentsPath, planStepPath, } from "../runtime/error-boundary.js";
|
|
10
|
+
import { createMisuseReporter } from "../runtime/misuse-reporter.js";
|
|
7
11
|
import { operationFailure } from "../runtime/operation-response.js";
|
|
8
12
|
import { activeContract, validateOperationRequest, validateProtocolIdentity, } from "../runtime/operation-runtime.js";
|
|
13
|
+
import { mapValidationOutcome } from "../runtime/outcome-mapper.js";
|
|
9
14
|
import { encodeOperationArgumentsForWire } from "./operation-arguments-encoder.js";
|
|
10
15
|
import { computeOperationFingerprint, isOperationFingerprint, } from "./operation-fingerprint.js";
|
|
11
16
|
import { inspectTransactionEnvelope, inspectTransactionOperationEntry, inspectTransactionOperations, } from "./transaction-envelope.js";
|
|
12
|
-
import { routeTransactionReferenceCandidate } from "./transaction-reference.js";
|
|
17
|
+
import { routeTransactionReferenceCandidate, } from "./transaction-reference.js";
|
|
18
|
+
import { collectTransactionReferenceCandidates, } from "./transaction-reference-collector.js";
|
|
13
19
|
import { validateTransactionReferences } from "./transaction-reference-validator.js";
|
|
14
20
|
import { transactionOperationFailure } from "./transaction-response.js";
|
|
21
|
+
const CALLER_PLAN = { source: "caller" };
|
|
22
|
+
const STEP_ARGUMENTS_POLICY = {
|
|
23
|
+
mirror: (value) => Object.hasOwn(value, "$ref"),
|
|
24
|
+
};
|
|
25
|
+
function captureSteps(operations, operationCount, planArguments) {
|
|
26
|
+
const steps = [];
|
|
27
|
+
for (let index = 0; index < operationCount; index += 1) {
|
|
28
|
+
const operationEntry = inspectTransactionOperationEntry(operations, index);
|
|
29
|
+
if (!operationEntry.ok) {
|
|
30
|
+
steps.push({
|
|
31
|
+
ok: false,
|
|
32
|
+
response: structuralFailure(operationEntry.issue),
|
|
33
|
+
});
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
const nodeEnvelope = inspectTransactionEnvelope(operationEntry.value, {
|
|
37
|
+
required: OPERATION_KEYS,
|
|
38
|
+
invalidContainerPath: [],
|
|
39
|
+
invalidContainerMessage: "Operation node must be an object.",
|
|
40
|
+
});
|
|
41
|
+
if (!nodeEnvelope.ok) {
|
|
42
|
+
steps.push({
|
|
43
|
+
ok: false,
|
|
44
|
+
response: structuralFailure(nodeEnvelope.issue, index),
|
|
45
|
+
});
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
const node = nodeEnvelope.values;
|
|
49
|
+
const args = node.get("args");
|
|
50
|
+
const refused = planArguments.source === "owned"
|
|
51
|
+
? planArguments.refused?.get(index)
|
|
52
|
+
: undefined;
|
|
53
|
+
const copy = refused !== undefined
|
|
54
|
+
? { ok: false, error: refused }
|
|
55
|
+
: planArguments.source === "owned"
|
|
56
|
+
? { ok: true, value: args }
|
|
57
|
+
: copyAtDoor(args, STEP_ARGUMENTS_POLICY);
|
|
58
|
+
steps.push({ ok: true, node, args: copy });
|
|
59
|
+
if (!copy.ok) {
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return steps;
|
|
64
|
+
}
|
|
15
65
|
const PLAN_KEYS = ["operations"];
|
|
16
66
|
const OPERATION_KEYS = [
|
|
17
67
|
"resource",
|
|
@@ -23,10 +73,23 @@ const OPERATION_KEYS = [
|
|
|
23
73
|
function isObject(value) {
|
|
24
74
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25
75
|
}
|
|
76
|
+
function operationKeyFailure(key, message, operation) {
|
|
77
|
+
return transactionOperationFailure(operationFailure("A2002", {
|
|
78
|
+
message: "Operation arguments failed framework validation.",
|
|
79
|
+
issues: [validationIssue("V1006", [key], message)],
|
|
80
|
+
}), operation);
|
|
81
|
+
}
|
|
26
82
|
function structuralFailure(rejection, operation) {
|
|
27
83
|
return operationFailure("A2004", {
|
|
28
84
|
message: "Transaction plan failed framework validation.",
|
|
29
|
-
issues: [
|
|
85
|
+
issues: [
|
|
86
|
+
operation === undefined || rejection.path === undefined
|
|
87
|
+
? rejection
|
|
88
|
+
: {
|
|
89
|
+
...rejection,
|
|
90
|
+
path: [...planStepPath(operation), ...rejection.path],
|
|
91
|
+
},
|
|
92
|
+
],
|
|
30
93
|
...(operation === undefined ? {} : { operation }),
|
|
31
94
|
});
|
|
32
95
|
}
|
|
@@ -37,11 +100,11 @@ function syntheticRequest(origin, requestId, resource, identity, args) {
|
|
|
37
100
|
...identity,
|
|
38
101
|
arguments: args,
|
|
39
102
|
};
|
|
40
|
-
return origin.
|
|
41
|
-
? { ...common,
|
|
103
|
+
return origin.origin === "application"
|
|
104
|
+
? { ...common, origin: "application" }
|
|
42
105
|
: {
|
|
43
106
|
...common,
|
|
44
|
-
|
|
107
|
+
origin: "client",
|
|
45
108
|
protocol: origin.protocol,
|
|
46
109
|
...(origin.transport === undefined
|
|
47
110
|
? {}
|
|
@@ -49,10 +112,10 @@ function syntheticRequest(origin, requestId, resource, identity, args) {
|
|
|
49
112
|
};
|
|
50
113
|
}
|
|
51
114
|
function requestOrigin(request) {
|
|
52
|
-
return request.
|
|
53
|
-
? {
|
|
115
|
+
return request.origin === "application"
|
|
116
|
+
? { origin: "application" }
|
|
54
117
|
: {
|
|
55
|
-
|
|
118
|
+
origin: "client",
|
|
56
119
|
protocol: request.protocol,
|
|
57
120
|
...(request.transport === undefined
|
|
58
121
|
? {}
|
|
@@ -60,55 +123,13 @@ function requestOrigin(request) {
|
|
|
60
123
|
};
|
|
61
124
|
}
|
|
62
125
|
function originContracts(contracts, origins) {
|
|
63
|
-
const layers = new Set(origins.map((origin) => origin
|
|
64
|
-
return [...layers].map((
|
|
65
|
-
}
|
|
66
|
-
function collectDataCandidates(contract, resourceName, data, path, candidates) {
|
|
67
|
-
if (!isObject(data)) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
const resource = lookupResource(contract, resourceName);
|
|
71
|
-
for (const [fieldName, value] of Object.entries(data)) {
|
|
72
|
-
const field = lookupField(resource, fieldName);
|
|
73
|
-
const route = routeTransactionReferenceCandidate(value);
|
|
74
|
-
if (field?.kind === "scalar" && route.kind !== "ordinary") {
|
|
75
|
-
candidates.push({
|
|
76
|
-
path: [...path, fieldName],
|
|
77
|
-
field,
|
|
78
|
-
candidate: route.candidate,
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
function collectCandidates(contract, request, index, args) {
|
|
84
|
-
const candidates = [];
|
|
85
|
-
const root = planOperationArgumentsPath(index);
|
|
86
|
-
if (request.family === "create") {
|
|
87
|
-
const data = lookupOwn(args, "data");
|
|
88
|
-
if (request.variant === "many" || request.variant === "count") {
|
|
89
|
-
if (Array.isArray(data)) {
|
|
90
|
-
data.forEach((entry, dataIndex) => {
|
|
91
|
-
collectDataCandidates(contract, request.resource, entry, [...root, "data", dataIndex], candidates);
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
else {
|
|
96
|
-
collectDataCandidates(contract, request.resource, data, [...root, "data"], candidates);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
else if (request.family === "update") {
|
|
100
|
-
collectDataCandidates(contract, request.resource, lookupOwn(args, "data"), [...root, "data"], candidates);
|
|
101
|
-
}
|
|
102
|
-
else if (request.family === "upsert") {
|
|
103
|
-
for (const stage of ["create", "update"]) {
|
|
104
|
-
collectDataCandidates(contract, request.resource, lookupOwn(args, stage), [...root, stage], candidates);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return candidates;
|
|
126
|
+
const layers = new Set(origins.map(({ origin }) => origin));
|
|
127
|
+
return [...layers].map((origin) => origin === "application" ? contracts.application : contracts.client);
|
|
108
128
|
}
|
|
109
129
|
function* relationTraversals(contract, resourceName, value, path) {
|
|
110
130
|
const resource = lookupResource(contract, resourceName);
|
|
111
|
-
if (resource === undefined
|
|
131
|
+
if (resource === undefined ||
|
|
132
|
+
routeTransactionReferenceCandidate(value).kind !== "ordinary") {
|
|
112
133
|
return;
|
|
113
134
|
}
|
|
114
135
|
if (Array.isArray(value)) {
|
|
@@ -182,7 +203,7 @@ export function transactionsUnavailableFailure() {
|
|
|
182
203
|
issues: [validationIssue("V1006", ["operations"], message)],
|
|
183
204
|
});
|
|
184
205
|
}
|
|
185
|
-
export async function validateTransactionPlan(dependencies, request, origins) {
|
|
206
|
+
export async function validateTransactionPlan(dependencies, request, origins, planArguments = CALLER_PLAN) {
|
|
186
207
|
const protocolFailure = validateProtocolIdentity(dependencies.contracts, request);
|
|
187
208
|
if (protocolFailure !== undefined) {
|
|
188
209
|
return { ok: false, response: protocolFailure };
|
|
@@ -229,30 +250,18 @@ export async function validateTransactionPlan(dependencies, request, origins) {
|
|
|
229
250
|
}),
|
|
230
251
|
};
|
|
231
252
|
}
|
|
253
|
+
const steps = captureSteps(operations, operationCount, planArguments);
|
|
254
|
+
const misuse = createMisuseReporter(dependencies.config.misuse);
|
|
232
255
|
const validated = [];
|
|
233
256
|
const cascaded = new Set();
|
|
234
257
|
for (let index = 0; index < operationCount; index += 1) {
|
|
235
|
-
const
|
|
236
|
-
if (!
|
|
237
|
-
return {
|
|
238
|
-
ok: false,
|
|
239
|
-
response: structuralFailure(operationEntry.issue),
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
const nodeEnvelope = inspectTransactionEnvelope(operationEntry.value, {
|
|
243
|
-
required: OPERATION_KEYS,
|
|
244
|
-
invalidContainerPath: ["operation"],
|
|
245
|
-
invalidContainerMessage: "Operation node must be an object.",
|
|
246
|
-
});
|
|
247
|
-
if (!nodeEnvelope.ok) {
|
|
248
|
-
return {
|
|
249
|
-
ok: false,
|
|
250
|
-
response: structuralFailure(nodeEnvelope.issue, index),
|
|
251
|
-
};
|
|
258
|
+
const step = steps[index];
|
|
259
|
+
if (!step.ok) {
|
|
260
|
+
return { ok: false, response: step.response };
|
|
252
261
|
}
|
|
253
|
-
const node =
|
|
262
|
+
const node = step.node;
|
|
254
263
|
const origin = originOf(index);
|
|
255
|
-
const contract = origin.
|
|
264
|
+
const contract = origin.origin === "application"
|
|
256
265
|
? dependencies.contracts.application
|
|
257
266
|
: dependencies.contracts.client;
|
|
258
267
|
const resourceValue = node.get("resource");
|
|
@@ -286,100 +295,142 @@ export async function validateTransactionPlan(dependencies, request, origins) {
|
|
|
286
295
|
if (identity === undefined) {
|
|
287
296
|
return {
|
|
288
297
|
ok: false,
|
|
289
|
-
response:
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
298
|
+
response: operationKeyFailure(typeof family === "string"
|
|
299
|
+
? unavailableOperationKey(contract, {
|
|
300
|
+
resource: resourceValue,
|
|
301
|
+
family,
|
|
302
|
+
})
|
|
303
|
+
: "family", "Operation is not part of the standard operation grammar.", index),
|
|
295
304
|
};
|
|
296
305
|
}
|
|
297
|
-
const
|
|
298
|
-
const
|
|
299
|
-
if (
|
|
306
|
+
const stepRequest = { resource: resourceValue, ...identity };
|
|
307
|
+
const unavailable = findOperationAvailabilityIssue(contract, stepRequest);
|
|
308
|
+
if (unavailable !== undefined) {
|
|
300
309
|
return {
|
|
301
310
|
ok: false,
|
|
302
|
-
response: transactionOperationFailure(
|
|
311
|
+
response: transactionOperationFailure(mapValidationOutcome([unavailable]), index),
|
|
303
312
|
};
|
|
304
313
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
314
|
+
if (!step.args.ok) {
|
|
315
|
+
return {
|
|
316
|
+
ok: false,
|
|
317
|
+
response: transactionOperationFailure(mapValidationOutcome([
|
|
318
|
+
refuseCallerData(step.args.error, planOperationArgumentsPath(index), {
|
|
319
|
+
...misuseIdentityOf({
|
|
320
|
+
...stepRequest,
|
|
321
|
+
origin: origin.origin,
|
|
322
|
+
}),
|
|
323
|
+
operation: index,
|
|
324
|
+
}, misuse),
|
|
325
|
+
]), index),
|
|
326
|
+
};
|
|
318
327
|
}
|
|
319
|
-
const validatedRequest = {
|
|
320
|
-
...candidate.request,
|
|
321
|
-
arguments: referenceValidation.requestArguments,
|
|
322
|
-
};
|
|
323
|
-
const validatedArguments = referenceValidation.arguments;
|
|
324
|
-
const wireArguments = origin.scope === "client"
|
|
325
|
-
? validatedRequest.arguments
|
|
326
|
-
: encodeOperationArgumentsForWire(contract, validatedRequest, validatedArguments);
|
|
327
|
-
let fingerprint;
|
|
328
328
|
try {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
329
|
+
const operationRequest = syntheticRequest(origin, request.requestId, resourceValue, identity, step.args.value);
|
|
330
|
+
const operationValidation = validateOperationRequest(contract, operationRequest, { transactionReferences: true });
|
|
331
|
+
if (!operationValidation.ok) {
|
|
332
|
+
return {
|
|
333
|
+
ok: false,
|
|
334
|
+
response: transactionOperationFailure(operationValidation.response, index),
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
const candidate = {
|
|
338
|
+
index,
|
|
339
|
+
request: operationRequest,
|
|
340
|
+
arguments: operationValidation.arguments,
|
|
341
|
+
projection: planReadOperation(dependencies.contracts.core, contract, (origin.origin === "application"
|
|
342
|
+
? dependencies.config.application
|
|
343
|
+
: dependencies.config.client).behaviors, operationRequest, operationValidation.arguments).projection,
|
|
344
|
+
declaredFingerprint: node.get("fingerprint"),
|
|
345
|
+
candidates: collectTransactionReferenceCandidates(contract, operationRequest, index, operationValidation.arguments),
|
|
346
|
+
};
|
|
347
|
+
const referenceValidation = validateTransactionReferences(candidate, validated);
|
|
348
|
+
if (!referenceValidation.ok) {
|
|
349
|
+
return referenceValidation;
|
|
350
|
+
}
|
|
351
|
+
const validatedRequest = {
|
|
352
|
+
...candidate.request,
|
|
353
|
+
arguments: referenceValidation.requestArguments,
|
|
354
|
+
};
|
|
355
|
+
const validatedArguments = referenceValidation.arguments;
|
|
356
|
+
const wireArguments = origin.origin === "client"
|
|
357
|
+
? validatedRequest.arguments
|
|
358
|
+
: encodeOperationArgumentsForWire(contract, validatedRequest, validatedArguments);
|
|
359
|
+
let fingerprint;
|
|
360
|
+
try {
|
|
361
|
+
fingerprint = await computeOperationFingerprint({
|
|
362
|
+
resource: validatedRequest.resource,
|
|
363
|
+
family: validatedRequest.family,
|
|
364
|
+
variant: validatedRequest.variant,
|
|
365
|
+
args: wireArguments,
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
catch (error) {
|
|
369
|
+
if (!(error instanceof CanonicalJsonError)) {
|
|
370
|
+
throw error;
|
|
371
|
+
}
|
|
372
|
+
return {
|
|
373
|
+
ok: false,
|
|
374
|
+
response: structuralFailure(validationIssue("V1001", ["args"], "Invalid runtime/wire type."), index),
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
if (!isOperationFingerprint(candidate.declaredFingerprint) ||
|
|
378
|
+
candidate.declaredFingerprint !== fingerprint) {
|
|
379
|
+
return {
|
|
380
|
+
ok: false,
|
|
381
|
+
response: transactionOperationFailure(operationFailure("A2007", {
|
|
382
|
+
message: "Operation fingerprint does not match its arguments.",
|
|
383
|
+
issues: [
|
|
384
|
+
validationIssue("V1017", ["fingerprint"], "Operation fingerprint does not match its arguments."),
|
|
385
|
+
],
|
|
386
|
+
}), index),
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
const dependency = cascadeDependency(contract, validatedRequest, index, cascaded);
|
|
390
|
+
if (dependency !== undefined) {
|
|
391
|
+
return {
|
|
392
|
+
ok: false,
|
|
393
|
+
response: transactionOperationFailure(operationFailure("A2004", {
|
|
394
|
+
message: "Transaction plan failed framework validation.",
|
|
395
|
+
issues: [
|
|
396
|
+
validationIssue("V1019", dependency, VALIDATION_CODE_MESSAGES.V1019),
|
|
397
|
+
],
|
|
398
|
+
}), index),
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
for (const resource of deletedResources(contract, validatedRequest, index)) {
|
|
402
|
+
for (const removed of dependencies.cascades(resource)) {
|
|
403
|
+
cascaded.add(removed);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
validated.push({
|
|
407
|
+
index: candidate.index,
|
|
408
|
+
request: validatedRequest,
|
|
409
|
+
contract,
|
|
410
|
+
arguments: validatedArguments,
|
|
411
|
+
projection: candidate.projection,
|
|
412
|
+
fingerprint,
|
|
413
|
+
references: referenceValidation.references,
|
|
334
414
|
});
|
|
335
415
|
}
|
|
336
416
|
catch (error) {
|
|
337
|
-
if (!(error instanceof
|
|
417
|
+
if (!(error instanceof CallerDataError)) {
|
|
338
418
|
throw error;
|
|
339
419
|
}
|
|
340
420
|
return {
|
|
341
421
|
ok: false,
|
|
342
|
-
response:
|
|
422
|
+
response: transactionOperationFailure(mapValidationOutcome([
|
|
423
|
+
refuseCallerData(locateCallerDataError(error, step.args.value), planOperationArgumentsPath(index), {
|
|
424
|
+
...misuseIdentityOf({
|
|
425
|
+
resource: resourceValue,
|
|
426
|
+
...identity,
|
|
427
|
+
origin: origin.origin,
|
|
428
|
+
}),
|
|
429
|
+
operation: index,
|
|
430
|
+
}, misuse),
|
|
431
|
+
]), index),
|
|
343
432
|
};
|
|
344
433
|
}
|
|
345
|
-
if (!isOperationFingerprint(candidate.declaredFingerprint) ||
|
|
346
|
-
candidate.declaredFingerprint !== fingerprint) {
|
|
347
|
-
return {
|
|
348
|
-
ok: false,
|
|
349
|
-
response: transactionOperationFailure(operationFailure("A2007", {
|
|
350
|
-
message: "Operation fingerprint does not match its arguments.",
|
|
351
|
-
issues: [
|
|
352
|
-
validationIssue("V1017", ["fingerprint"], "Operation fingerprint does not match its arguments."),
|
|
353
|
-
],
|
|
354
|
-
}), index),
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
const dependency = cascadeDependency(contract, validatedRequest, index, cascaded);
|
|
358
|
-
if (dependency !== undefined) {
|
|
359
|
-
return {
|
|
360
|
-
ok: false,
|
|
361
|
-
response: transactionOperationFailure(operationFailure("A2004", {
|
|
362
|
-
message: "Transaction plan failed framework validation.",
|
|
363
|
-
issues: [
|
|
364
|
-
validationIssue("V1019", dependency, VALIDATION_CODE_MESSAGES.V1019),
|
|
365
|
-
],
|
|
366
|
-
}), index),
|
|
367
|
-
};
|
|
368
|
-
}
|
|
369
|
-
for (const resource of deletedResources(contract, validatedRequest, index)) {
|
|
370
|
-
for (const removed of dependencies.cascades(resource)) {
|
|
371
|
-
cascaded.add(removed);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
validated.push({
|
|
375
|
-
index: candidate.index,
|
|
376
|
-
request: validatedRequest,
|
|
377
|
-
contract,
|
|
378
|
-
arguments: validatedArguments,
|
|
379
|
-
projection: candidate.projection,
|
|
380
|
-
fingerprint,
|
|
381
|
-
references: referenceValidation.references,
|
|
382
|
-
});
|
|
383
434
|
}
|
|
384
435
|
return { ok: true, plan: { operations: validated } };
|
|
385
436
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Contract } from "../contracts/contract.js";
|
|
2
|
+
import type { ScalarField } from "../contracts/field.js";
|
|
3
|
+
import type { OperationIdentity } from "../operations/operation-identity.js";
|
|
4
|
+
import type { SemanticOperationArguments } from "../operations/operation-validator.js";
|
|
5
|
+
import { type TransactionSlotShape } from "./transaction-reference.js";
|
|
6
|
+
type Path = readonly (string | number)[];
|
|
7
|
+
/** One `$ref` candidate found at a reference position, before it is validated. */
|
|
8
|
+
export type TransactionReferenceCandidateSlot = {
|
|
9
|
+
readonly path: Path;
|
|
10
|
+
readonly field: ScalarField<Contract>;
|
|
11
|
+
readonly shape: TransactionSlotShape;
|
|
12
|
+
/** The slot is a scalar value of mutation data: what it resolves to is written. */
|
|
13
|
+
readonly written: boolean;
|
|
14
|
+
readonly candidate: unknown;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Every `$ref` candidate at a reference position of one validated operation, with the
|
|
18
|
+
* full wire path, the destination field and the slot's shape. The positions: mutation
|
|
19
|
+
* data (`create`'s `data`, each `data[]` entry of a bulk create, `update`'s `data`,
|
|
20
|
+
* `upsert`'s `create`/`update`) at any depth — its scalar values, the unique keys its
|
|
21
|
+
* relation directives name, the `where` of `$update`/`$delete`/`$upsert`, and the data
|
|
22
|
+
* they nest — the `cursor`, and the step's own `where` at any depth.
|
|
23
|
+
*
|
|
24
|
+
* It reads the validated argument view, whose normalization keeps every key where
|
|
25
|
+
* the caller wrote it, so a slot path names the same member of the wire view.
|
|
26
|
+
*/
|
|
27
|
+
export declare function collectTransactionReferenceCandidates(contract: Contract, request: OperationIdentity & {
|
|
28
|
+
readonly resource: string;
|
|
29
|
+
}, index: number, args: SemanticOperationArguments): readonly TransactionReferenceCandidateSlot[];
|
|
30
|
+
export {};
|