@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.
Files changed (108) hide show
  1. package/CHANGELOG.md +338 -0
  2. package/README.md +100 -3
  3. package/dist/adapters/adapter-model.d.ts +8 -0
  4. package/dist/adapters/adapter.interface.d.ts +6 -2
  5. package/dist/compiler/contract-compiler-diagnostic.d.ts +6 -0
  6. package/dist/compiler/contract.compiler.js +67 -0
  7. package/dist/config/config.resolver.d.ts +3 -0
  8. package/dist/config/config.resolver.js +1 -0
  9. package/dist/config/config.validator.js +6 -0
  10. package/dist/config/define-fields.d.ts +10 -3
  11. package/dist/config/framework-config.d.ts +4 -1
  12. package/dist/config/framework-fields-config.d.ts +6 -0
  13. package/dist/contracts/client-contract-structure.validator.d.ts +21 -1
  14. package/dist/contracts/client-contract-structure.validator.js +99 -2
  15. package/dist/contracts/contract-canonicalizer.js +32 -0
  16. package/dist/contracts/contract.d.ts +7 -0
  17. package/dist/contracts/field.d.ts +10 -0
  18. package/dist/contracts/json-shape.d.ts +65 -0
  19. package/dist/contracts/json-shape.js +1 -0
  20. package/dist/contracts/reference-write-limit.d.ts +7 -0
  21. package/dist/contracts/reference-write-limit.js +1 -0
  22. package/dist/contracts/scalar-value-type.d.ts +12 -1
  23. package/dist/framework/contract-view.d.ts +126 -21
  24. package/dist/framework/framework.d.ts +5 -3
  25. package/dist/framework/framework.factory.js +56 -13
  26. package/dist/index.d.ts +8 -40
  27. package/dist/index.js +0 -20
  28. package/dist/internal.d.ts +35 -0
  29. package/dist/internal.js +16 -0
  30. package/dist/json/avz.d.ts +96 -0
  31. package/dist/json/avz.js +212 -0
  32. package/dist/json/index.d.ts +6 -0
  33. package/dist/json/index.js +1 -0
  34. package/dist/json/json-shape.registry.d.ts +22 -0
  35. package/dist/json/json-shape.registry.js +16 -0
  36. package/dist/json/zod-issue.translator.d.ts +51 -0
  37. package/dist/json/zod-issue.translator.js +142 -0
  38. package/dist/operations/operation-arguments.d.ts +151 -101
  39. package/dist/operations/operation-identity.d.ts +2 -0
  40. package/dist/operations/operation-identity.js +3 -0
  41. package/dist/operations/operation-validator-core.d.ts +1 -1
  42. package/dist/operations/operation-validator-core.js +28 -10
  43. package/dist/operations/operation-validator.d.ts +20 -5
  44. package/dist/operations/operation-validator.js +26 -10
  45. package/dist/operations/projection-scope.resolver.d.ts +1 -1
  46. package/dist/operations/structural-limits.walker.d.ts +19 -0
  47. package/dist/operations/structural-limits.walker.js +344 -0
  48. package/dist/protocol/absent-route.responder.d.ts +2 -1
  49. package/dist/protocol/absent-route.responder.js +32 -6
  50. package/dist/protocol/http-request.decoder.d.ts +6 -0
  51. package/dist/protocol/http-request.decoder.js +11 -3
  52. package/dist/protocol/protocol.binder.d.ts +4 -2
  53. package/dist/protocol/protocol.binder.js +3 -1
  54. package/dist/protocol/protocol.handler.d.ts +6 -2
  55. package/dist/protocol/protocol.handler.js +14 -4
  56. package/dist/protocol/unmounted-route.error.d.ts +19 -0
  57. package/dist/protocol/unmounted-route.error.js +10 -0
  58. package/dist/runtime/caller-data.copier.d.ts +59 -0
  59. package/dist/runtime/caller-data.copier.js +470 -0
  60. package/dist/runtime/caller-data.door.d.ts +56 -0
  61. package/dist/runtime/caller-data.door.js +65 -0
  62. package/dist/runtime/computed-read-runtime.js +3 -10
  63. package/dist/runtime/computed-value.error.d.ts +13 -0
  64. package/dist/runtime/computed-value.error.js +9 -0
  65. package/dist/runtime/create-requirement.d.ts +43 -7
  66. package/dist/runtime/create-requirement.js +89 -9
  67. package/dist/runtime/diagnostics-reporter.d.ts +20 -4
  68. package/dist/runtime/diagnostics-reporter.js +11 -4
  69. package/dist/runtime/error-boundary.d.ts +8 -0
  70. package/dist/runtime/error-boundary.js +65 -35
  71. package/dist/runtime/field-behavior.d.ts +2 -2
  72. package/dist/runtime/framework-error.d.ts +12 -0
  73. package/dist/runtime/misuse-reporter.d.ts +32 -0
  74. package/dist/runtime/misuse-reporter.js +19 -0
  75. package/dist/runtime/mutation-planner.js +13 -11
  76. package/dist/runtime/operation-request.d.ts +4 -11
  77. package/dist/runtime/operation-runtime.d.ts +14 -7
  78. package/dist/runtime/operation-runtime.js +37 -18
  79. package/dist/runtime/outcome-mapper.js +12 -2
  80. package/dist/runtime/output-validator.d.ts +10 -0
  81. package/dist/runtime/output-validator.js +11 -0
  82. package/dist/runtime/owned-execution.registry.d.ts +13 -0
  83. package/dist/runtime/owned-execution.registry.js +7 -0
  84. package/dist/runtime/pipeline.d.ts +19 -7
  85. package/dist/runtime/referential-action.d.ts +7 -0
  86. package/dist/runtime/request-envelope.validator.d.ts +5 -0
  87. package/dist/runtime/request-envelope.validator.js +118 -0
  88. package/dist/runtime/scalar-runtime.d.ts +30 -2
  89. package/dist/runtime/scalar-runtime.js +162 -79
  90. package/dist/transactions/transaction-plan-assembler.d.ts +5 -1
  91. package/dist/transactions/transaction-plan-assembler.js +74 -75
  92. package/dist/transactions/transaction-plan-validator.d.ts +30 -7
  93. package/dist/transactions/transaction-plan-validator.js +204 -153
  94. package/dist/transactions/transaction-reference-collector.d.ts +30 -0
  95. package/dist/transactions/transaction-reference-collector.js +204 -0
  96. package/dist/transactions/transaction-reference-resolver.js +10 -5
  97. package/dist/transactions/transaction-reference-validator.js +24 -17
  98. package/dist/transactions/transaction-reference.d.ts +23 -51
  99. package/dist/transactions/transaction-request.d.ts +3 -3
  100. package/dist/transactions/transaction-response.d.ts +6 -1
  101. package/dist/transactions/transaction-response.js +24 -1
  102. package/dist/transactions/transaction-runtime.d.ts +4 -2
  103. package/dist/transactions/transaction-runtime.js +4 -3
  104. package/package.json +17 -3
  105. package/dist/operations/operation-boolean-nodes.d.ts +0 -11
  106. package/dist/operations/operation-boolean-nodes.js +0 -283
  107. package/dist/operations/operation-nesting.d.ts +0 -8
  108. 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
- substitutions.push({
21
- path: slot.path,
22
- value: copyScalarFieldValue(slot.field, row[segment]),
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 sameType(source, destination) {
144
+ function sameScalar(source, destination) {
145
145
  if ("scalar" in source.type) {
146
- if (!("scalar" in destination.type)) {
147
- return false;
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
- else {
154
- if (!("enum" in destination.type)) {
155
- return false;
156
- }
157
- if (source.type.enum !== destination.type.enum) {
158
- return false;
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" || !sameType(sourceField, slot.field)) {
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, WritableRelationKey } from "../operations/operation-arguments.js";
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
- * A scalar value inside a plan, or a `$ref` to a field of the same type. A
49
- * reference is typed by the application form of its source field, because a
50
- * source's final data is always in application forms.
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 ReferenceableValue<Value, ReferenceValue> = Value | TransactionReference<ReferenceValue>;
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 whose top-level scalar fields may hold a `$ref`, under stated
57
- * scalar forms.
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 ReferenceableCreateData<C extends Contract, K extends ResourceKey<C>, Scope extends WriteScope, Forms extends ScalarForms, ReferenceForms extends ScalarForms> = Readonly<{
60
- [F in keyof CreateDataInput<C, K, Scope, Forms>]: F extends WritableRelationKey<C, K, "create", Scope> ? CreateDataInput<C, K, Scope, Forms>[F] : ReferenceableValue<CreateDataInput<C, K, Scope, Forms>[F], MemberOf<CreateDataInput<C, K, Scope, ReferenceForms>, F>>;
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
- * Update data whose top-level scalar fields may hold a `$ref`, under stated
64
- * scalar forms.
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 ReferenceableUpdateData<C extends Contract, K extends ResourceKey<C>, Scope extends WriteScope, Forms extends ScalarForms, ReferenceForms extends ScalarForms> = Readonly<{
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 scope: "application";
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 scope: "client";
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, "scope"> | Pick<ClientTransactionRequest, "scope" | "protocol" | "transport">;
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
- /** Builds the failure response of a plan step, naming the step in `cause.operation`. */
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
- ...failure.cause,
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); see `validateTransactionPlan`.
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?: readonly TransactionOperationOrigin[]) => Promise<TransactionResponse>;
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
- createRequirements: adapter.createRequirements ?? {},
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": "0.1.0-pilot.4",
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;