@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
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { lookupField, lookupOwn, lookupResource, } from "../contracts/contract-lookup.js";
|
|
2
|
+
import { planOperationArgumentsPath } from "../runtime/error-boundary.js";
|
|
3
|
+
import { isPlainRecord, referenceOperandShape, } from "../runtime/scalar-runtime.js";
|
|
4
|
+
import { routeTransactionReferenceCandidate, } from "./transaction-reference.js";
|
|
5
|
+
function isObject(value) {
|
|
6
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7
|
+
}
|
|
8
|
+
function at(position, ...segments) {
|
|
9
|
+
return { ...position, path: [...position.path, ...segments] };
|
|
10
|
+
}
|
|
11
|
+
function collectValue(field, shape, value, path, candidates, written = false) {
|
|
12
|
+
const route = routeTransactionReferenceCandidate(value);
|
|
13
|
+
if (route.kind === "ordinary") {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
candidates.push({
|
|
17
|
+
path,
|
|
18
|
+
field,
|
|
19
|
+
shape,
|
|
20
|
+
written,
|
|
21
|
+
candidate: route.candidate,
|
|
22
|
+
});
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
function collectKey(position, selector, candidates) {
|
|
26
|
+
if (!isObject(selector)) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const resource = lookupResource(position.contract, position.resource);
|
|
30
|
+
for (const [fieldName, value] of Object.entries(selector)) {
|
|
31
|
+
const field = lookupField(resource, fieldName);
|
|
32
|
+
if (field?.kind === "scalar") {
|
|
33
|
+
collectValue(field, "nonNullValue", value, [...position.path, fieldName], candidates);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function collectKeys(position, selectors, candidates) {
|
|
38
|
+
if (Array.isArray(selectors)) {
|
|
39
|
+
selectors.forEach((selector, index) => {
|
|
40
|
+
collectKey(at(position, index), selector, candidates);
|
|
41
|
+
});
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
collectKey(position, selectors, candidates);
|
|
45
|
+
}
|
|
46
|
+
function collectScalarFilter(field, filter, path, candidates) {
|
|
47
|
+
if (collectValue(field, "value", filter, path, candidates) ||
|
|
48
|
+
!isPlainRecord(filter)) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
for (const [operator, operand] of Object.entries(filter)) {
|
|
52
|
+
const operandPath = [...path, operator];
|
|
53
|
+
if (operator === "not") {
|
|
54
|
+
collectScalarFilter(field, operand, operandPath, candidates);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const shape = referenceOperandShape(operator);
|
|
58
|
+
if (shape !== undefined) {
|
|
59
|
+
collectValue(field, shape, operand, operandPath, candidates);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function collectWhere(position, where, candidates) {
|
|
64
|
+
if (!isObject(where)) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const resource = lookupResource(position.contract, position.resource);
|
|
68
|
+
for (const [key, value] of Object.entries(where)) {
|
|
69
|
+
if (key === "AND" || key === "OR") {
|
|
70
|
+
if (Array.isArray(value)) {
|
|
71
|
+
value.forEach((entry, index) => {
|
|
72
|
+
collectWhere(at(position, key, index), entry, candidates);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (key === "NOT") {
|
|
78
|
+
collectWhere(at(position, key), value, candidates);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const field = lookupField(resource, key);
|
|
82
|
+
if (field?.kind === "scalar") {
|
|
83
|
+
collectScalarFilter(field, value, [...position.path, key], candidates);
|
|
84
|
+
}
|
|
85
|
+
else if (field?.kind === "relation") {
|
|
86
|
+
const target = { ...at(position, key), resource: field.target };
|
|
87
|
+
if (field.cardinality === "one") {
|
|
88
|
+
collectWhere(target, value, candidates);
|
|
89
|
+
}
|
|
90
|
+
else if (isObject(value)) {
|
|
91
|
+
for (const operator of ["some", "every", "none"]) {
|
|
92
|
+
if (Object.hasOwn(value, operator)) {
|
|
93
|
+
collectWhere(at(target, operator), value[operator], candidates);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function eachEntry(position, operand, visit) {
|
|
101
|
+
if (Array.isArray(operand)) {
|
|
102
|
+
operand.forEach((entry, index) => {
|
|
103
|
+
visit(at(position, index), entry);
|
|
104
|
+
});
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
visit(position, operand);
|
|
108
|
+
}
|
|
109
|
+
function collectRelationDirectives(position, edge, value, candidates) {
|
|
110
|
+
if (!isObject(value)) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const target = { ...position, resource: edge.target };
|
|
114
|
+
for (const [directive, operand] of Object.entries(value)) {
|
|
115
|
+
const directivePosition = at(target, directive);
|
|
116
|
+
switch (directive) {
|
|
117
|
+
case "$connect":
|
|
118
|
+
case "$disconnect":
|
|
119
|
+
case "$set":
|
|
120
|
+
collectKeys(directivePosition, operand, candidates);
|
|
121
|
+
break;
|
|
122
|
+
case "$create":
|
|
123
|
+
eachEntry(directivePosition, operand, (entryPosition, entry) => {
|
|
124
|
+
collectData(entryPosition, entry, candidates);
|
|
125
|
+
});
|
|
126
|
+
break;
|
|
127
|
+
case "$connectOrCreate":
|
|
128
|
+
case "$update":
|
|
129
|
+
case "$delete":
|
|
130
|
+
case "$upsert":
|
|
131
|
+
eachEntry(directivePosition, operand, (entryPosition, entry) => {
|
|
132
|
+
if (!isObject(entry)) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const where = at(entryPosition, "where");
|
|
136
|
+
if (directive === "$update" || directive === "$delete") {
|
|
137
|
+
collectWhere(where, entry.where, candidates);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
collectKey(where, entry.where, candidates);
|
|
141
|
+
}
|
|
142
|
+
for (const branch of ["create", "update", "data"]) {
|
|
143
|
+
if (Object.hasOwn(entry, branch)) {
|
|
144
|
+
collectData(at(entryPosition, branch), entry[branch], candidates);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function collectData(position, data, candidates) {
|
|
153
|
+
if (!isObject(data)) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const resource = lookupResource(position.contract, position.resource);
|
|
157
|
+
for (const [fieldName, value] of Object.entries(data)) {
|
|
158
|
+
const field = lookupField(resource, fieldName);
|
|
159
|
+
if (field?.kind === "scalar") {
|
|
160
|
+
collectValue(field, "value", value, [...position.path, fieldName], candidates, true);
|
|
161
|
+
}
|
|
162
|
+
else if (field?.kind === "relation") {
|
|
163
|
+
collectRelationDirectives(at(position, fieldName), field, value, candidates);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
export function collectTransactionReferenceCandidates(contract, request, index, args) {
|
|
168
|
+
const candidates = [];
|
|
169
|
+
const root = {
|
|
170
|
+
contract,
|
|
171
|
+
resource: request.resource,
|
|
172
|
+
path: planOperationArgumentsPath(index),
|
|
173
|
+
};
|
|
174
|
+
const where = lookupOwn(args, "where");
|
|
175
|
+
if (request.variant === "unique") {
|
|
176
|
+
collectKey(at(root, "where"), where, candidates);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
collectWhere(at(root, "where"), where, candidates);
|
|
180
|
+
}
|
|
181
|
+
collectKey(at(root, "cursor"), lookupOwn(args, "cursor"), candidates);
|
|
182
|
+
if (request.family === "create") {
|
|
183
|
+
const data = lookupOwn(args, "data");
|
|
184
|
+
if (request.variant === "many" || request.variant === "count") {
|
|
185
|
+
if (Array.isArray(data)) {
|
|
186
|
+
data.forEach((entry, dataIndex) => {
|
|
187
|
+
collectData(at(root, "data", dataIndex), entry, candidates);
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
collectData(at(root, "data"), data, candidates);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
else if (request.family === "update") {
|
|
196
|
+
collectData(at(root, "data"), lookupOwn(args, "data"), candidates);
|
|
197
|
+
}
|
|
198
|
+
else if (request.family === "upsert") {
|
|
199
|
+
for (const stage of ["create", "update"]) {
|
|
200
|
+
collectData(at(root, stage), lookupOwn(args, stage), candidates);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return candidates;
|
|
204
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { validationIssue } from "../operations/validation-code.js";
|
|
2
|
-
import { copyScalarFieldValue } from "../runtime/scalar-runtime.js";
|
|
2
|
+
import { copyScalarFieldValue, jsonShapeIssues, } from "../runtime/scalar-runtime.js";
|
|
3
3
|
import { replaceTransactionSlotValues } from "./transaction-slot-writer.js";
|
|
4
4
|
function isRecord(value) {
|
|
5
5
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
6
|
}
|
|
7
7
|
export function resolveTransactionReferences(operation, results) {
|
|
8
8
|
const substitutions = [];
|
|
9
|
+
const shapeIssues = [];
|
|
9
10
|
for (const slot of operation.references) {
|
|
10
11
|
const { operation: source, path: [segment], } = slot.reference.$ref;
|
|
11
12
|
const row = results[source];
|
|
@@ -17,10 +18,14 @@ export function resolveTransactionReferences(operation, results) {
|
|
|
17
18
|
],
|
|
18
19
|
};
|
|
19
20
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
21
|
+
const value = copyScalarFieldValue(slot.source, row[segment]);
|
|
22
|
+
if (slot.written) {
|
|
23
|
+
shapeIssues.push(...jsonShapeIssues(slot.field, value, slot.path));
|
|
24
|
+
}
|
|
25
|
+
substitutions.push({ path: slot.path, value });
|
|
26
|
+
}
|
|
27
|
+
if (shapeIssues.length > 0) {
|
|
28
|
+
return { ok: false, issues: shapeIssues };
|
|
24
29
|
}
|
|
25
30
|
return {
|
|
26
31
|
ok: true,
|
|
@@ -141,25 +141,28 @@ function containsUnpairedSurrogate(value) {
|
|
|
141
141
|
}
|
|
142
142
|
return false;
|
|
143
143
|
}
|
|
144
|
-
function
|
|
144
|
+
function sameScalar(source, destination) {
|
|
145
145
|
if ("scalar" in source.type) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
if (source.type.scalar !== destination.type.scalar) {
|
|
150
|
-
return false;
|
|
151
|
-
}
|
|
146
|
+
return ("scalar" in destination.type &&
|
|
147
|
+
source.type.scalar === destination.type.scalar);
|
|
152
148
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
149
|
+
return ("enum" in destination.type && source.type.enum === destination.type.enum);
|
|
150
|
+
}
|
|
151
|
+
function isAssignable(source, destination, shape) {
|
|
152
|
+
if (!sameScalar(source, destination)) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
switch (shape) {
|
|
156
|
+
case "value":
|
|
157
|
+
return (source.list === destination.list &&
|
|
158
|
+
!(source.nullable && !destination.nullable));
|
|
159
|
+
case "nonNullValue":
|
|
160
|
+
return source.list === destination.list && !source.nullable;
|
|
161
|
+
case "entry":
|
|
162
|
+
return !source.list && !source.nullable;
|
|
163
|
+
case "entries":
|
|
164
|
+
return source.list && !source.nullable;
|
|
160
165
|
}
|
|
161
|
-
return (source.list === destination.list &&
|
|
162
|
-
!(source.nullable && !destination.nullable));
|
|
163
166
|
}
|
|
164
167
|
const CANDIDATE_KEYS = new Set(["$ref"]);
|
|
165
168
|
const REFERENCE_KEYS = new Set(["operation", "fingerprint", "path"]);
|
|
@@ -234,7 +237,8 @@ export function validateTransactionReferences(operation, earlier) {
|
|
|
234
237
|
if (sourceField === undefined) {
|
|
235
238
|
return failure(validationIssue("V1010", pathPath, "Transaction reference path is not present in the source projection."), operation.index);
|
|
236
239
|
}
|
|
237
|
-
if (sourceField.kind !== "scalar" ||
|
|
240
|
+
if (sourceField.kind !== "scalar" ||
|
|
241
|
+
!isAssignable(sourceField, slot.field, slot.shape)) {
|
|
238
242
|
return failure(validationIssue("V1011", pathPath, "Transaction reference source type is not assignable to its destination."), operation.index);
|
|
239
243
|
}
|
|
240
244
|
const reference = Object.freeze({
|
|
@@ -247,6 +251,9 @@ export function validateTransactionReferences(operation, earlier) {
|
|
|
247
251
|
references.push({
|
|
248
252
|
path: Object.freeze([...slot.path]),
|
|
249
253
|
field: slot.field,
|
|
254
|
+
shape: slot.shape,
|
|
255
|
+
written: slot.written,
|
|
256
|
+
source: sourceField,
|
|
250
257
|
reference,
|
|
251
258
|
});
|
|
252
259
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { WriteScope } from "../contracts/capability.js";
|
|
2
2
|
import type { Contract } from "../contracts/contract.js";
|
|
3
3
|
import type { ScalarForms } from "../contracts/scalar-value-type.js";
|
|
4
|
-
import type { CreateDataInput, OperationArgumentsFor, ResourceKey, UpdateDataInput
|
|
4
|
+
import type { CreateDataInput, OperationArgumentsFor, ResourceKey, UpdateDataInput } from "../operations/operation-arguments.js";
|
|
5
5
|
import type { OperationIdentity } from "../operations/operation-identity.js";
|
|
6
6
|
import type { OperationFingerprint } from "./deferred-operation.js";
|
|
7
7
|
declare const REFERENCE_VALUE: unique symbol;
|
|
@@ -45,61 +45,33 @@ export declare function isTransactionReferenceObject(value: unknown): value is T
|
|
|
45
45
|
*/
|
|
46
46
|
export type TransactionScalarValue<Value> = Value | TransactionReference<Value>;
|
|
47
47
|
/**
|
|
48
|
-
*
|
|
49
|
-
* reference
|
|
50
|
-
*
|
|
48
|
+
* What a Transaction Slot holds of its destination field, which decides the source
|
|
49
|
+
* fields a reference there accepts — always the same scalar or enum, no coercion:
|
|
50
|
+
*
|
|
51
|
+
* - `"value"` — the field's whole value, where a `null` means something: a mutation
|
|
52
|
+
* value, or an equality filter (`null` matches the rows the field is unset on). A
|
|
53
|
+
* nullable source only into a nullable field.
|
|
54
|
+
* - `"nonNullValue"` — the field's whole value, where a `null` identifies or orders
|
|
55
|
+
* nothing: a unique key, a `cursor`, an ordering or text comparison. The source is
|
|
56
|
+
* never nullable.
|
|
57
|
+
* - `"entry"` — one entry of a list field (`has`): a non-null, non-list source.
|
|
58
|
+
* - `"entries"` — a list of entries (`in`, `notIn`, `hasEvery`, `hasSome`): a
|
|
59
|
+
* non-null list source.
|
|
51
60
|
*/
|
|
52
|
-
type
|
|
53
|
-
/** `Data`'s member `Key`, or `never` when it has none. */
|
|
54
|
-
type MemberOf<Data, Key> = Key extends keyof Data ? Data[Key] : never;
|
|
61
|
+
export type TransactionSlotShape = "value" | "nonNullValue" | "entry" | "entries";
|
|
55
62
|
/**
|
|
56
|
-
* Create data
|
|
57
|
-
*
|
|
63
|
+
* Create data inside a transaction plan, at one write position: its scalar values
|
|
64
|
+
* and the unique keys its relation directives name may hold a `$ref`. The default
|
|
65
|
+
* is the unrestricted position, as on `CreateDataInput`.
|
|
58
66
|
*/
|
|
59
|
-
export type
|
|
60
|
-
|
|
61
|
-
|
|
67
|
+
export type TransactionCreateDataInput<C extends Contract, K extends ResourceKey<C>, Scope extends WriteScope = "nested"> = CreateDataInput<C, K, Scope, ScalarForms, ScalarForms>;
|
|
68
|
+
/** Update data inside a transaction plan. See {@link TransactionCreateDataInput}. */
|
|
69
|
+
export type TransactionUpdateDataInput<C extends Contract, K extends ResourceKey<C>, Scope extends WriteScope = "nested"> = UpdateDataInput<C, K, Scope, ScalarForms, ScalarForms>;
|
|
62
70
|
/**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
71
|
+
* Operation arguments inside a transaction plan, under one value form and one
|
|
72
|
+
* reference form: `OperationArgumentsFor` with its reference positions opened.
|
|
65
73
|
*/
|
|
66
|
-
export type
|
|
67
|
-
[F in keyof UpdateDataInput<C, K, Scope, Forms>]: F extends WritableRelationKey<C, K, "update", Scope> ? UpdateDataInput<C, K, Scope, Forms>[F] : ReferenceableValue<UpdateDataInput<C, K, Scope, Forms>[F], MemberOf<UpdateDataInput<C, K, Scope, ReferenceForms>, F>>;
|
|
68
|
-
}>;
|
|
69
|
-
/**
|
|
70
|
-
* Only top-level scalar fields accept a `$ref`; a relation field keeps its relation
|
|
71
|
-
* mutation action, where a `$ref` is an ordinary invalid value.
|
|
72
|
-
*/
|
|
73
|
-
export type TransactionCreateDataInput<C extends Contract, K extends ResourceKey<C>, Scope extends WriteScope = "nested"> = ReferenceableCreateData<C, K, Scope, ScalarForms, ScalarForms>;
|
|
74
|
-
/** See {@link TransactionCreateDataInput}. */
|
|
75
|
-
export type TransactionUpdateDataInput<C extends Contract, K extends ResourceKey<C>, Scope extends WriteScope = "nested"> = ReferenceableUpdateData<C, K, Scope, ScalarForms, ScalarForms>;
|
|
76
|
-
/** Operation arguments inside a transaction plan, under stated scalar forms. */
|
|
77
|
-
export type ReferenceableArgumentsFor<C extends Contract, K extends ResourceKey<C>, I extends OperationIdentity, Forms extends ScalarForms, ReferenceForms extends ScalarForms> = I extends {
|
|
78
|
-
family: "create";
|
|
79
|
-
variant: "one";
|
|
80
|
-
} ? Omit<OperationArgumentsFor<C, K, I, Forms>, "data"> & {
|
|
81
|
-
data: ReferenceableCreateData<C, K, "nested", Forms, ReferenceForms>;
|
|
82
|
-
} : I extends {
|
|
83
|
-
family: "create";
|
|
84
|
-
variant: "many" | "count";
|
|
85
|
-
} ? Omit<OperationArgumentsFor<C, K, I, Forms>, "data"> & {
|
|
86
|
-
data: readonly ReferenceableCreateData<C, K, "bulkWrite", Forms, ReferenceForms>[];
|
|
87
|
-
} : I extends {
|
|
88
|
-
family: "update";
|
|
89
|
-
variant: "first" | "unique";
|
|
90
|
-
} ? Omit<OperationArgumentsFor<C, K, I, Forms>, "data"> & {
|
|
91
|
-
data: ReferenceableUpdateData<C, K, "nested", Forms, ReferenceForms>;
|
|
92
|
-
} : I extends {
|
|
93
|
-
family: "update";
|
|
94
|
-
variant: "many" | "count";
|
|
95
|
-
} ? Omit<OperationArgumentsFor<C, K, I, Forms>, "data"> & {
|
|
96
|
-
data: ReferenceableUpdateData<C, K, "bulkWrite", Forms, ReferenceForms>;
|
|
97
|
-
} : I extends {
|
|
98
|
-
family: "upsert";
|
|
99
|
-
} ? Omit<OperationArgumentsFor<C, K, I, Forms>, "create" | "update"> & {
|
|
100
|
-
create: ReferenceableCreateData<C, K, "nested", Forms, ReferenceForms>;
|
|
101
|
-
update: ReferenceableUpdateData<C, K, "nested", Forms, ReferenceForms>;
|
|
102
|
-
} : OperationArgumentsFor<C, K, I, Forms>;
|
|
74
|
+
export type ReferenceableArgumentsFor<C extends Contract, K extends ResourceKey<C>, I extends OperationIdentity, Forms extends ScalarForms, ReferenceForms extends ScalarForms> = OperationArgumentsFor<C, K, I, Forms, ReferenceForms>;
|
|
103
75
|
/** Operation arguments inside a transaction plan, no scalar form stated. */
|
|
104
76
|
export type TransactionOperationArgumentsFor<C extends Contract, K extends ResourceKey<C>, I extends OperationIdentity> = ReferenceableArgumentsFor<C, K, I, ScalarForms, ScalarForms>;
|
|
105
77
|
export {};
|
|
@@ -6,13 +6,13 @@ type TransactionRequestBase = {
|
|
|
6
6
|
};
|
|
7
7
|
/** An in-process transaction request, run under the Application Contract. */
|
|
8
8
|
export type ApplicationTransactionRequest = TransactionRequestBase & {
|
|
9
|
-
readonly
|
|
9
|
+
readonly origin: "application";
|
|
10
10
|
readonly protocol?: never;
|
|
11
11
|
readonly transport?: never;
|
|
12
12
|
};
|
|
13
13
|
/** A remote transaction request, run under the Client Contract. */
|
|
14
14
|
export type ClientTransactionRequest = TransactionRequestBase & {
|
|
15
|
-
readonly
|
|
15
|
+
readonly origin: "client";
|
|
16
16
|
readonly protocol: ClientProtocolIdentity;
|
|
17
17
|
readonly transport?: PipelineTransportContext;
|
|
18
18
|
};
|
|
@@ -22,5 +22,5 @@ export type FrameworkTransactionRequest = ApplicationTransactionRequest | Client
|
|
|
22
22
|
* The origin one plan operation runs under: its own layer's Contract, its own
|
|
23
23
|
* pipelines, and — for a client step — the protocol identity it is accepted with.
|
|
24
24
|
*/
|
|
25
|
-
export type TransactionOperationOrigin = Pick<ApplicationTransactionRequest, "
|
|
25
|
+
export type TransactionOperationOrigin = Pick<ApplicationTransactionRequest, "origin"> | Pick<ClientTransactionRequest, "origin" | "protocol" | "transport">;
|
|
26
26
|
export {};
|
|
@@ -9,7 +9,12 @@ export type TransactionCommittedResponse<T = readonly unknown[]> = {
|
|
|
9
9
|
export type TransactionResponse = TransactionCommittedResponse | OperationFailureResponse;
|
|
10
10
|
/** Builds the committed response. */
|
|
11
11
|
export declare function transactionCommitted(results: readonly unknown[]): TransactionCommittedResponse;
|
|
12
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Builds the failure response of a plan step: the step named in
|
|
14
|
+
* `cause.operation`, and each issue at its full plan location —
|
|
15
|
+
* `["operations", operation, "args", …]` for the step's arguments,
|
|
16
|
+
* `["operations", operation, …]` for the step itself.
|
|
17
|
+
*/
|
|
13
18
|
export declare function transactionOperationFailure(failure: OperationFailureResponse, operation: number): OperationFailureResponse;
|
|
14
19
|
/** Builds the `A3002` response for a transaction that itself failed. */
|
|
15
20
|
export declare function transactionInfrastructureFailure(operation?: number): OperationFailureResponse;
|
|
@@ -1,13 +1,36 @@
|
|
|
1
|
+
import { OPERATION_ARGUMENTS_PATH, planOperationArgumentsPath, planStepPath, } from "../runtime/error-boundary.js";
|
|
1
2
|
import { operationFailure } from "../runtime/operation-response.js";
|
|
2
3
|
export function transactionCommitted(results) {
|
|
3
4
|
return { data: results, code: "A1009", cause: null };
|
|
4
5
|
}
|
|
5
6
|
export function transactionOperationFailure(failure, operation) {
|
|
7
|
+
const { issues, ...cause } = failure.cause;
|
|
6
8
|
return operationFailure(failure.code, {
|
|
7
|
-
...
|
|
9
|
+
...cause,
|
|
10
|
+
...(issues === undefined
|
|
11
|
+
? {}
|
|
12
|
+
: { issues: issues.map((issue) => atPlanLocation(issue, operation)) }),
|
|
8
13
|
operation,
|
|
9
14
|
});
|
|
10
15
|
}
|
|
16
|
+
function isPlanLocated(path) {
|
|
17
|
+
return path[0] === "operations" && typeof path[1] === "number";
|
|
18
|
+
}
|
|
19
|
+
function atPlanLocation(issue, operation) {
|
|
20
|
+
const { path } = issue;
|
|
21
|
+
if (path === undefined || isPlanLocated(path)) {
|
|
22
|
+
return issue;
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
...issue,
|
|
26
|
+
path: path[0] === OPERATION_ARGUMENTS_PATH[0]
|
|
27
|
+
? [
|
|
28
|
+
...planOperationArgumentsPath(operation),
|
|
29
|
+
...path.slice(OPERATION_ARGUMENTS_PATH.length),
|
|
30
|
+
]
|
|
31
|
+
: [...planStepPath(operation), ...path],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
11
34
|
export function transactionInfrastructureFailure(operation) {
|
|
12
35
|
return operationFailure("A3002", {
|
|
13
36
|
message: "Transaction failed.",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AdapterModel } from "../adapters/adapter-model.js";
|
|
2
2
|
import type { TransactionDiagnosticsChannel } from "../runtime/diagnostics-reporter.js";
|
|
3
3
|
import type { OperationRuntimeDependencies } from "../runtime/operation-runtime.js";
|
|
4
|
+
import type { PlanArguments } from "./transaction-plan-validator.js";
|
|
4
5
|
import type { FrameworkTransactionRequest, TransactionOperationOrigin } from "./transaction-request.js";
|
|
5
6
|
import type { TransactionResponse } from "./transaction-response.js";
|
|
6
7
|
/**
|
|
@@ -9,9 +10,10 @@ import type { TransactionResponse } from "./transaction-response.js";
|
|
|
9
10
|
* hands it in, so every report of a plan travels the same route.
|
|
10
11
|
*
|
|
11
12
|
* `origins`, when given, states each operation's own origin (the typed
|
|
12
|
-
* runner's mixed plans)
|
|
13
|
+
* runner's mixed plans), and `planArguments` whether the steps' arguments are
|
|
14
|
+
* copied at the door; see `validateTransactionPlan`.
|
|
13
15
|
*/
|
|
14
|
-
export type FrameworkTransactionExecutor = (request: FrameworkTransactionRequest, diagnostics: TransactionDiagnosticsChannel, origins
|
|
16
|
+
export type FrameworkTransactionExecutor = (request: FrameworkTransactionRequest, diagnostics: TransactionDiagnosticsChannel, origins: readonly TransactionOperationOrigin[] | undefined, planArguments: PlanArguments) => Promise<TransactionResponse>;
|
|
15
17
|
/**
|
|
16
18
|
* Validates a whole plan through the Framework Validation Layer, then runs it inside
|
|
17
19
|
* exactly one Adapter transaction. Guards, pipes, hooks and interceptors of every plan
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { recordWriteFactsOf } from "../runtime/create-requirement.js";
|
|
1
2
|
import { planOperationArgumentsPath } from "../runtime/error-boundary.js";
|
|
2
3
|
import { internalFailure } from "../runtime/operation-response.js";
|
|
3
4
|
import { executeValidatedOperation } from "../runtime/operation-runtime.js";
|
|
@@ -12,14 +13,14 @@ export function createTransactionRuntime(dependencies) {
|
|
|
12
13
|
const { adapter, ...runtimeDependencies } = dependencies;
|
|
13
14
|
const pipelineDependencies = {
|
|
14
15
|
...runtimeDependencies,
|
|
15
|
-
|
|
16
|
+
recordWrites: recordWriteFactsOf(adapter),
|
|
16
17
|
};
|
|
17
18
|
const planDependencies = {
|
|
18
19
|
...pipelineDependencies,
|
|
19
20
|
cascades: resolveCascadeClosure(adapter.referentialActions),
|
|
20
21
|
};
|
|
21
|
-
return async (request, diagnostics, origins) => {
|
|
22
|
-
const validation = await validateTransactionPlan(planDependencies, request, origins);
|
|
22
|
+
return async (request, diagnostics, origins, planArguments) => {
|
|
23
|
+
const validation = await validateTransactionPlan(planDependencies, request, origins, planArguments);
|
|
23
24
|
if (!validation.ok) {
|
|
24
25
|
return validation.response;
|
|
25
26
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aventara/core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0-rc.0",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"description": "Core runtime, contract compiler, and extension APIs for Aventara.",
|
|
6
6
|
"type": "module",
|
|
@@ -19,17 +19,31 @@
|
|
|
19
19
|
"types": "./dist/protocol/index.d.ts",
|
|
20
20
|
"import": "./dist/protocol/index.js",
|
|
21
21
|
"default": "./dist/protocol/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./internal": {
|
|
24
|
+
"types": "./dist/internal.d.ts",
|
|
25
|
+
"import": "./dist/internal.js",
|
|
26
|
+
"default": "./dist/internal.js"
|
|
27
|
+
},
|
|
28
|
+
"./json": {
|
|
29
|
+
"types": "./dist/json/index.d.ts",
|
|
30
|
+
"import": "./dist/json/index.js",
|
|
31
|
+
"default": "./dist/json/index.js"
|
|
22
32
|
}
|
|
23
33
|
},
|
|
24
34
|
"files": [
|
|
25
35
|
"dist",
|
|
26
|
-
"LICENSE-ADDITIONAL-PERMISSION.md"
|
|
36
|
+
"LICENSE-ADDITIONAL-PERMISSION.md",
|
|
37
|
+
"CHANGELOG.md"
|
|
27
38
|
],
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"zod": "^4.6.5"
|
|
41
|
+
},
|
|
28
42
|
"publishConfig": {
|
|
29
43
|
"access": "public"
|
|
30
44
|
},
|
|
31
45
|
"scripts": {
|
|
32
|
-
"build": "tsc -p tsconfig.json --removeComments --declaration false && tsc -p tsconfig.json --emitDeclarationOnly && node ../../scripts/build/declaration-comments.sanitizer.ts dist",
|
|
46
|
+
"build": "node ../../scripts/build/build-output.cleaner.ts dist && tsc -p tsconfig.json --removeComments --declaration false && tsc -p tsconfig.json --emitDeclarationOnly && node ../../scripts/build/declaration-comments.sanitizer.ts dist",
|
|
33
47
|
"typecheck": "tsc -p tsconfig.typecheck.json --noEmit",
|
|
34
48
|
"test": "vitest run --typecheck --config ../../vitest.config.mts --root .",
|
|
35
49
|
"lint": "biome lint src"
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { Contract } from "../contracts/contract.js";
|
|
2
|
-
import type { OperationIdentity } from "./operation-identity.js";
|
|
3
|
-
import type { ValidationIssue } from "./validation-code.js";
|
|
4
|
-
/**
|
|
5
|
-
* Enforces maxBooleanNodes across every general where tree in one semantic
|
|
6
|
-
* operation, including root filters, returned-relation filters, and nested
|
|
7
|
-
* relation mutation filters. Unique selectors are intentionally excluded.
|
|
8
|
-
*/
|
|
9
|
-
export declare function findOperationBooleanNodeIssue(contract: Contract, request: OperationIdentity & {
|
|
10
|
-
readonly resource: string;
|
|
11
|
-
}, args: Readonly<Record<string, unknown>>): ValidationIssue | undefined;
|