@aventara/core 1.0.0-rc.0 → 1.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/CHANGELOG.md +1140 -0
  2. package/README.md +224 -57
  3. package/dist/adapters/adapter-discovery-diagnostic.d.ts +4 -4
  4. package/dist/adapters/adapter-field.d.ts +11 -11
  5. package/dist/adapters/adapter-identifier.d.ts +6 -6
  6. package/dist/adapters/adapter-model.d.ts +9 -9
  7. package/dist/adapters/adapter-operation-executor.interface.d.ts +4 -4
  8. package/dist/adapters/adapter-operation.d.ts +3 -3
  9. package/dist/adapters/adapter-resource.d.ts +18 -9
  10. package/dist/adapters/adapter-sql-statement.d.ts +19 -0
  11. package/dist/adapters/adapter-transaction-executor.interface.d.ts +19 -0
  12. package/dist/adapters/adapter.interface.d.ts +49 -29
  13. package/dist/avz/avz-check.factory.d.ts +83 -0
  14. package/dist/avz/avz-check.factory.js +116 -0
  15. package/dist/avz/avz-check.registry.d.ts +35 -0
  16. package/dist/avz/avz-check.registry.js +9 -0
  17. package/dist/avz/avz.d.ts +203 -0
  18. package/dist/{json → avz}/avz.js +150 -8
  19. package/dist/{json → avz}/index.d.ts +2 -1
  20. package/dist/{json → avz}/json-shape.registry.d.ts +7 -7
  21. package/dist/{json → avz}/zod-issue.translator.d.ts +8 -2
  22. package/dist/{json → avz}/zod-issue.translator.js +7 -0
  23. package/dist/compiler/contract-compiler-diagnostic.d.ts +31 -5
  24. package/dist/compiler/contract.compiler.d.ts +18 -18
  25. package/dist/compiler/contract.compiler.js +548 -441
  26. package/dist/compiler/create-requirements.projector.d.ts +49 -0
  27. package/dist/compiler/create-requirements.projector.js +423 -0
  28. package/dist/compiler/field-validator.compiler.d.ts +47 -0
  29. package/dist/compiler/field-validator.compiler.js +238 -0
  30. package/dist/config/any-framework-config.d.ts +19 -12
  31. package/dist/config/config-diagnostic.d.ts +11 -4
  32. package/dist/config/config-layer.d.ts +15 -6
  33. package/dist/config/config.resolver.d.ts +74 -31
  34. package/dist/config/config.resolver.js +108 -44
  35. package/dist/config/config.validator.d.ts +7 -7
  36. package/dist/config/config.validator.js +581 -72
  37. package/dist/config/define-resource.d.ts +57 -0
  38. package/dist/config/define-resource.js +14 -0
  39. package/dist/config/effective-fields.d.ts +19 -23
  40. package/dist/config/entrypoint.normalizer.d.ts +1 -1
  41. package/dist/config/exact-framework-config.d.ts +280 -0
  42. package/dist/config/field-compute.resolver.d.ts +19 -0
  43. package/dist/config/field-compute.resolver.js +115 -0
  44. package/dist/config/field-pipeline-config.d.ts +47 -0
  45. package/dist/config/forced-operation.validator.d.ts +10 -0
  46. package/dist/config/forced-operation.validator.js +66 -0
  47. package/dist/config/framework-config.d.ts +110 -39
  48. package/dist/config/framework-exemptions-config.d.ts +91 -0
  49. package/dist/config/framework-exemptions-config.js +1 -0
  50. package/dist/config/framework-field-definition.d.ts +78 -0
  51. package/dist/config/framework-field-definition.js +28 -0
  52. package/dist/config/framework-resources-config.d.ts +116 -0
  53. package/dist/config/framework-resources-config.js +1 -0
  54. package/dist/config/pipeline-config.d.ts +56 -20
  55. package/dist/config/pipeline-tree.resolver.d.ts +79 -0
  56. package/dist/config/pipeline-tree.resolver.js +220 -0
  57. package/dist/config/stage-type.eraser.d.ts +65 -0
  58. package/dist/config/stage-type.eraser.js +1 -0
  59. package/dist/contracts/capability.d.ts +11 -11
  60. package/dist/contracts/client-contract-structure.validator.d.ts +323 -28
  61. package/dist/contracts/client-contract-structure.validator.js +152 -8
  62. package/dist/contracts/contract-canonicalizer.d.ts +19 -3
  63. package/dist/contracts/contract-canonicalizer.js +141 -28
  64. package/dist/contracts/contract-hash.d.ts +12 -7
  65. package/dist/contracts/contract-hash.js +3 -2
  66. package/dist/contracts/contract-limits.d.ts +4 -1
  67. package/dist/contracts/contract-limits.js +1 -0
  68. package/dist/contracts/contract-lookup.d.ts +5 -5
  69. package/dist/contracts/contract-protocol.d.ts +4 -4
  70. package/dist/contracts/contract.d.ts +24 -16
  71. package/dist/contracts/create-requirements.d.ts +35 -0
  72. package/dist/contracts/create-requirements.js +1 -0
  73. package/dist/contracts/enum-definition.d.ts +1 -1
  74. package/dist/contracts/field-check.d.ts +101 -0
  75. package/dist/contracts/field-check.js +1 -0
  76. package/dist/contracts/field.d.ts +52 -42
  77. package/dist/contracts/identifier.d.ts +4 -4
  78. package/dist/contracts/json-shape.d.ts +12 -1
  79. package/dist/contracts/lifecycle-trait.d.ts +2 -2
  80. package/dist/contracts/resource-operations.d.ts +28 -28
  81. package/dist/contracts/resource.d.ts +15 -9
  82. package/dist/contracts/scalar-value-type.d.ts +15 -15
  83. package/dist/contracts/scalar.d.ts +3 -3
  84. package/dist/contracts/transaction-capability.d.ts +1 -1
  85. package/dist/contracts/wire-scalar-format.d.ts +4 -4
  86. package/dist/framework/contract-view.d.ts +213 -147
  87. package/dist/framework/framework-construction.error.d.ts +10 -10
  88. package/dist/framework/framework-construction.error.js +2 -2
  89. package/dist/framework/framework.d.ts +47 -23
  90. package/dist/framework/framework.factory.d.ts +8 -6
  91. package/dist/framework/framework.factory.js +40 -20
  92. package/dist/framework/operation-facade.d.ts +123 -52
  93. package/dist/framework/operation-facade.js +9 -1
  94. package/dist/index.d.ts +55 -50
  95. package/dist/index.js +6 -6
  96. package/dist/internal.d.ts +15 -10
  97. package/dist/internal.js +3 -1
  98. package/dist/operations/operation-arguments.d.ts +267 -202
  99. package/dist/operations/operation-call.d.ts +27 -28
  100. package/dist/operations/operation-identity.d.ts +10 -10
  101. package/dist/operations/operation-result.d.ts +21 -21
  102. package/dist/operations/operation-validator-core.d.ts +6 -6
  103. package/dist/operations/operation-validator-core.js +13 -2
  104. package/dist/operations/operation-validator.d.ts +9 -9
  105. package/dist/operations/projection-scope.resolver.d.ts +6 -6
  106. package/dist/operations/structural-limits.walker.d.ts +6 -6
  107. package/dist/operations/structural-limits.walker.js +145 -0
  108. package/dist/operations/validation-code.d.ts +11 -6
  109. package/dist/operations/validation-code.js +4 -0
  110. package/dist/operations/write-scope.resolver.d.ts +6 -6
  111. package/dist/protocol/absent-route.responder.d.ts +13 -12
  112. package/dist/protocol/absent-route.responder.js +19 -19
  113. package/dist/protocol/contract-document.responder.d.ts +3 -3
  114. package/dist/protocol/contract-document.responder.js +2 -2
  115. package/dist/protocol/http-request.decoder.d.ts +17 -13
  116. package/dist/protocol/http-request.decoder.js +10 -10
  117. package/dist/protocol/http-request.reader.d.ts +3 -3
  118. package/dist/protocol/http-response.encoder.d.ts +4 -4
  119. package/dist/protocol/http-response.encoder.js +94 -55
  120. package/dist/protocol/http-status.table.d.ts +2 -2
  121. package/dist/protocol/operation-response.guard.d.ts +2 -2
  122. package/dist/protocol/protocol-surface.d.ts +4 -4
  123. package/dist/protocol/protocol.binder.d.ts +50 -27
  124. package/dist/protocol/protocol.binder.js +12 -9
  125. package/dist/protocol/protocol.handler.d.ts +22 -13
  126. package/dist/protocol/protocol.handler.js +44 -9
  127. package/dist/protocol/unmounted-route.error.d.ts +4 -4
  128. package/dist/protocol/unmounted-route.error.js +2 -2
  129. package/dist/runtime/caller-data.copier.d.ts +5 -5
  130. package/dist/runtime/caller-data.copier.js +13 -13
  131. package/dist/runtime/caller-data.door.d.ts +6 -6
  132. package/dist/runtime/caller-data.door.js +6 -4
  133. package/dist/runtime/computed-read-runtime.d.ts +7 -6
  134. package/dist/runtime/computed-read-runtime.js +61 -48
  135. package/dist/runtime/computed-value.error.d.ts +6 -6
  136. package/dist/runtime/computed-value.error.js +3 -3
  137. package/dist/runtime/context-executor.runtime.d.ts +59 -0
  138. package/dist/runtime/context-executor.runtime.js +275 -0
  139. package/dist/runtime/create-requirement.d.ts +10 -81
  140. package/dist/runtime/create-requirement.js +80 -57
  141. package/dist/runtime/decimal.d.ts +2 -2
  142. package/dist/runtime/decimal.js +1 -1
  143. package/dist/runtime/diagnostics-reporter.d.ts +53 -27
  144. package/dist/runtime/diagnostics-reporter.js +24 -0
  145. package/dist/runtime/error-boundary.d.ts +65 -8
  146. package/dist/runtime/error-boundary.js +45 -19
  147. package/dist/runtime/execution-options.d.ts +10 -2
  148. package/dist/runtime/field-check.evaluator.d.ts +29 -0
  149. package/dist/runtime/field-check.evaluator.js +521 -0
  150. package/dist/runtime/field-compute.d.ts +64 -0
  151. package/dist/runtime/field-compute.js +1 -0
  152. package/dist/runtime/field-stage.plan.d.ts +15 -0
  153. package/dist/runtime/field-stage.plan.js +27 -0
  154. package/dist/runtime/field-stage.runtime.d.ts +35 -0
  155. package/dist/runtime/field-stage.runtime.js +143 -0
  156. package/dist/runtime/field-validator.runtime.d.ts +11 -0
  157. package/dist/runtime/field-validator.runtime.js +87 -0
  158. package/dist/runtime/framework-error-class.table.d.ts +33 -0
  159. package/dist/runtime/framework-error-class.table.js +25 -0
  160. package/dist/runtime/framework-error.d.ts +68 -8
  161. package/dist/runtime/framework-error.factory.d.ts +4 -0
  162. package/dist/runtime/framework-error.factory.js +15 -0
  163. package/dist/runtime/framework-error.js +23 -2
  164. package/dist/runtime/interceptor-next.error.d.ts +5 -0
  165. package/dist/runtime/interceptor-next.error.js +6 -0
  166. package/dist/runtime/json-value.d.ts +3 -3
  167. package/dist/runtime/level-lifecycle.runtime.d.ts +25 -0
  168. package/dist/runtime/level-lifecycle.runtime.js +42 -0
  169. package/dist/runtime/misuse-reporter.d.ts +12 -12
  170. package/dist/runtime/mutation-planner.d.ts +19 -10
  171. package/dist/runtime/mutation-planner.js +54 -30
  172. package/dist/runtime/nested-write.walker.d.ts +32 -0
  173. package/dist/runtime/nested-write.walker.js +73 -0
  174. package/dist/runtime/operation-request.d.ts +33 -6
  175. package/dist/runtime/operation-response.d.ts +17 -17
  176. package/dist/runtime/operation-runtime.d.ts +113 -25
  177. package/dist/runtime/operation-runtime.js +395 -74
  178. package/dist/runtime/outcome-mapper.d.ts +16 -5
  179. package/dist/runtime/outcome-mapper.js +23 -6
  180. package/dist/runtime/output-validator.d.ts +10 -10
  181. package/dist/runtime/output-validator.js +3 -3
  182. package/dist/runtime/owned-execution.registry.d.ts +6 -6
  183. package/dist/runtime/pipe-output.validator.d.ts +33 -18
  184. package/dist/runtime/pipe-output.validator.js +12 -12
  185. package/dist/runtime/pipeline.d.ts +196 -34
  186. package/dist/runtime/readonly-value.d.ts +32 -0
  187. package/dist/runtime/readonly-value.js +141 -0
  188. package/dist/runtime/request-envelope.validator.d.ts +3 -3
  189. package/dist/runtime/request-envelope.validator.js +12 -7
  190. package/dist/runtime/request-id.d.ts +1 -1
  191. package/dist/runtime/request-state.d.ts +14 -0
  192. package/dist/runtime/request-state.js +1 -0
  193. package/dist/runtime/request-state.resolver.d.ts +12 -0
  194. package/dist/runtime/request-state.resolver.js +20 -0
  195. package/dist/runtime/scalar-runtime.d.ts +31 -16
  196. package/dist/runtime/scalar-runtime.js +16 -18
  197. package/dist/transactions/deferred-operation.d.ts +35 -20
  198. package/dist/transactions/operation-arguments-encoder.d.ts +3 -3
  199. package/dist/transactions/operation-arguments-encoder.js +2 -2
  200. package/dist/transactions/sql-statement.d.ts +77 -0
  201. package/dist/transactions/sql-statement.js +47 -0
  202. package/dist/transactions/sql-statement.validator.d.ts +44 -0
  203. package/dist/transactions/sql-statement.validator.js +134 -0
  204. package/dist/transactions/transaction-envelope.d.ts +4 -4
  205. package/dist/transactions/transaction-integrity-error.d.ts +6 -6
  206. package/dist/transactions/transaction-integrity-error.js +5 -2
  207. package/dist/transactions/transaction-plan-assembler.d.ts +21 -11
  208. package/dist/transactions/transaction-plan-assembler.js +148 -34
  209. package/dist/transactions/transaction-plan-validator.d.ts +29 -20
  210. package/dist/transactions/transaction-plan-validator.js +70 -38
  211. package/dist/transactions/transaction-reference-collector.d.ts +5 -5
  212. package/dist/transactions/transaction-reference-resolver.d.ts +19 -2
  213. package/dist/transactions/transaction-reference-resolver.js +26 -14
  214. package/dist/transactions/transaction-reference-validator.d.ts +34 -4
  215. package/dist/transactions/transaction-reference-validator.js +58 -26
  216. package/dist/transactions/transaction-reference.d.ts +13 -13
  217. package/dist/transactions/transaction-request.d.ts +18 -5
  218. package/dist/transactions/transaction-response.d.ts +6 -6
  219. package/dist/transactions/transaction-runtime.d.ts +8 -7
  220. package/dist/transactions/transaction-runtime.js +166 -50
  221. package/dist/transactions/transaction-settlement.runtime.d.ts +51 -0
  222. package/dist/transactions/transaction-settlement.runtime.js +120 -0
  223. package/dist/transactions/transaction-slot-path-error.d.ts +4 -4
  224. package/dist/transactions/transaction-slot-path-error.js +2 -2
  225. package/dist/transactions/transaction-slot-writer.js +2 -2
  226. package/dist/transactions/transaction-stage.runtime.d.ts +81 -0
  227. package/dist/transactions/transaction-stage.runtime.js +123 -0
  228. package/package.json +6 -5
  229. package/src/runtime/field-check.evaluator.ts +720 -0
  230. package/dist/config/define-fields.d.ts +0 -42
  231. package/dist/config/define-fields.js +0 -3
  232. package/dist/config/framework-behaviors-config.d.ts +0 -30
  233. package/dist/config/framework-fields-config.d.ts +0 -42
  234. package/dist/config/framework-restrictions-config.d.ts +0 -142
  235. package/dist/json/avz.d.ts +0 -96
  236. package/dist/runtime/field-behavior.d.ts +0 -48
  237. /package/dist/{config/framework-behaviors-config.js → adapters/adapter-sql-statement.js} +0 -0
  238. /package/dist/{config/framework-fields-config.js → adapters/adapter-transaction-executor.interface.js} +0 -0
  239. /package/dist/{json → avz}/index.js +0 -0
  240. /package/dist/{json → avz}/json-shape.registry.js +0 -0
  241. /package/dist/config/{framework-restrictions-config.js → exact-framework-config.js} +0 -0
  242. /package/dist/{runtime/field-behavior.js → config/field-pipeline-config.js} +0 -0
@@ -0,0 +1,134 @@
1
+ import { validationIssue } from "../operations/validation-code.js";
2
+ import { refuseCallerData } from "../runtime/caller-data.door.js";
3
+ import { AvDecimal } from "../runtime/decimal.js";
4
+ import { planStepPath } from "../runtime/error-boundary.js";
5
+ import { operationFailure } from "../runtime/operation-response.js";
6
+ import { mapValidationOutcome } from "../runtime/outcome-mapper.js";
7
+ import { isPlainRecord, normalizeJsonValue, } from "../runtime/scalar-runtime.js";
8
+ import { invalidSourceIndexIssue, referenceSourceAt, referenceSourceField, } from "./transaction-reference-validator.js";
9
+ import { transactionOperationFailure } from "./transaction-response.js";
10
+ function refused(issue, index) {
11
+ return {
12
+ ok: false,
13
+ response: transactionOperationFailure(mapValidationOutcome([issue]), index),
14
+ };
15
+ }
16
+ export function normalizeSqlValue(value, path) {
17
+ if (value === null ||
18
+ typeof value === "string" ||
19
+ typeof value === "boolean" ||
20
+ typeof value === "bigint" ||
21
+ value instanceof AvDecimal ||
22
+ value instanceof Uint8Array) {
23
+ return { ok: true, value };
24
+ }
25
+ if (typeof value === "number") {
26
+ return Number.isFinite(value)
27
+ ? { ok: true, value }
28
+ : {
29
+ ok: false,
30
+ issues: [
31
+ validationIssue("V1004", path, "Value must be a finite number."),
32
+ ],
33
+ };
34
+ }
35
+ if (value instanceof Date) {
36
+ return Number.isNaN(value.getTime())
37
+ ? {
38
+ ok: false,
39
+ issues: [
40
+ validationIssue("V1003", path, "Value must use a valid Date value."),
41
+ ],
42
+ }
43
+ : { ok: true, value };
44
+ }
45
+ if (Array.isArray(value) || isPlainRecord(value)) {
46
+ const json = normalizeJsonValue(value, path, new Set());
47
+ return json.ok
48
+ ? { ok: true, value: json.value }
49
+ : { ok: false, issues: json.issues };
50
+ }
51
+ return {
52
+ ok: false,
53
+ issues: [
54
+ validationIssue("V1001", path, "Value must be a string, number, boolean, bigint, null, Date, Decimal, Uint8Array or JSON value."),
55
+ ],
56
+ };
57
+ }
58
+ export function validateSqlStatement(statement, context) {
59
+ const { index, earlier } = context;
60
+ if (!context.available) {
61
+ const message = "SQL statements are not available on this Adapter.";
62
+ return {
63
+ ok: false,
64
+ response: transactionOperationFailure(operationFailure("A2002", {
65
+ message,
66
+ issues: [validationIssue("V1006", [], message)],
67
+ }), index),
68
+ };
69
+ }
70
+ if (statement.strings === undefined) {
71
+ return refused(validationIssue("V1001", ["strings"], "SQL must be written as a tagged template: framework.appTx.$sql`…`."), index);
72
+ }
73
+ if (statement.refused !== undefined) {
74
+ return refused(refuseCallerData(statement.refused, [...planStepPath(index), "values"], { origin: "application", operation: index }, context.misuse), index);
75
+ }
76
+ const values = [];
77
+ const issues = [];
78
+ for (const [position, planned] of statement.values.entries()) {
79
+ const path = ["values", position];
80
+ switch (planned.kind) {
81
+ case "misplaced-reference":
82
+ return refused(validationIssue("V1010", [...path, ...planned.path, "$ref"], "A transaction reference in a SQL step must be a whole value."), index);
83
+ case "reference": {
84
+ const source = referenceSourceAt(earlier, planned.operation, index);
85
+ if (source === undefined) {
86
+ return refused(invalidSourceIndexIssue([...path, "$ref", "operation"]), index);
87
+ }
88
+ const read = referenceSourceField(source, planned.field, [
89
+ ...path,
90
+ "$ref",
91
+ "path",
92
+ ]);
93
+ if (!read.ok) {
94
+ return refused(read.issue, index);
95
+ }
96
+ if (read.field.kind !== "scalar" || read.field.list) {
97
+ return refused(validationIssue("V1011", [...path, "$ref", "path"], "Transaction reference source is not a single scalar field."), index);
98
+ }
99
+ values.push({
100
+ kind: "reference",
101
+ operation: planned.operation,
102
+ field: planned.field,
103
+ source: read.field,
104
+ });
105
+ break;
106
+ }
107
+ case "value": {
108
+ const normalized = normalizeSqlValue(planned.value, path);
109
+ if (normalized.ok) {
110
+ values.push({ kind: "value", value: normalized.value });
111
+ }
112
+ else {
113
+ issues.push(...normalized.issues);
114
+ }
115
+ break;
116
+ }
117
+ }
118
+ }
119
+ if (issues.length > 0) {
120
+ return {
121
+ ok: false,
122
+ response: transactionOperationFailure(mapValidationOutcome(issues), index),
123
+ };
124
+ }
125
+ return {
126
+ ok: true,
127
+ statement: {
128
+ kind: "sql",
129
+ index,
130
+ strings: statement.strings,
131
+ values,
132
+ },
133
+ };
134
+ }
@@ -1,4 +1,4 @@
1
- import type { ValidationIssue } from "../operations/validation-code.js";
1
+ import type { AvValidationIssue } from "../operations/validation-code.js";
2
2
  export type TransactionEnvelopeSchema = {
3
3
  readonly required: readonly string[];
4
4
  readonly optional?: readonly string[];
@@ -10,7 +10,7 @@ export type TransactionEnvelopeInspection = {
10
10
  readonly values: ReadonlyMap<string, unknown>;
11
11
  } | {
12
12
  readonly ok: false;
13
- readonly issue: ValidationIssue;
13
+ readonly issue: AvValidationIssue;
14
14
  };
15
15
  export type TransactionOperationsInspection = {
16
16
  readonly ok: true;
@@ -18,14 +18,14 @@ export type TransactionOperationsInspection = {
18
18
  readonly length: number;
19
19
  } | {
20
20
  readonly ok: false;
21
- readonly issue: ValidationIssue;
21
+ readonly issue: AvValidationIssue;
22
22
  };
23
23
  export type TransactionOperationEntryInspection = {
24
24
  readonly ok: true;
25
25
  readonly value: unknown;
26
26
  } | {
27
27
  readonly ok: false;
28
- readonly issue: ValidationIssue;
28
+ readonly issue: AvValidationIssue;
29
29
  };
30
30
  /**
31
31
  * The caller uses the returned length and per-index inspector rather than
@@ -1,13 +1,13 @@
1
1
  /**
2
- * The ways an Adapter transaction can break the framework's transaction
3
- * contract. Each is answered `A3002`.
2
+ * The ways an Adapter transaction, or a stage running inside it, can break the
3
+ * framework's transaction contract. Each is answered `A3002` and rolls back.
4
4
  */
5
- export type TransactionIntegrityViolation = "transaction-unavailable" | "resolved-after-abort" | "resolved-incomplete" | "foreign-abort";
5
+ export type AvTransactionIntegrityViolation = "transaction-unavailable" | "resolved-after-abort" | "resolved-incomplete" | "foreign-abort" | "statement-unavailable" | "next-called-twice" | "steps-not-run";
6
6
  /**
7
7
  * Reported to diagnostics when an Adapter transaction breaks the framework's
8
8
  * transaction contract. The caller receives `A3002`.
9
9
  */
10
- export declare class TransactionIntegrityError extends Error {
11
- readonly violation: TransactionIntegrityViolation;
12
- constructor(violation: TransactionIntegrityViolation);
10
+ export declare class AvTransactionIntegrityError extends Error {
11
+ readonly violation: AvTransactionIntegrityViolation;
12
+ constructor(violation: AvTransactionIntegrityViolation);
13
13
  }
@@ -3,12 +3,15 @@ const TRANSACTION_INTEGRITY_MESSAGES = {
3
3
  "resolved-after-abort": "Adapter transaction resolved after the framework requested rollback.",
4
4
  "resolved-incomplete": "Adapter transaction resolved before the plan completed.",
5
5
  "foreign-abort": "Adapter transaction rejected with a rollback request this plan never made.",
6
+ "statement-unavailable": "Adapter declares sqlStatements but its transaction executor has no executeSql.",
7
+ "next-called-twice": "A transaction interceptor called next() more than once; next() runs the plan's steps once.",
8
+ "steps-not-run": "A transaction interceptor resolved without calling next(); the plan's steps never ran.",
6
9
  };
7
- export class TransactionIntegrityError extends Error {
10
+ export class AvTransactionIntegrityError extends Error {
8
11
  violation;
9
12
  constructor(violation) {
10
13
  super(TRANSACTION_INTEGRITY_MESSAGES[violation]);
11
- this.name = "TransactionIntegrityError";
14
+ this.name = "AvTransactionIntegrityError";
12
15
  this.violation = violation;
13
16
  }
14
17
  }
@@ -1,12 +1,14 @@
1
- import type { FrameworkContracts } from "../compiler/contract.compiler.js";
2
- import type { RequestOrigin } from "../config/config-layer.js";
3
- import type { OperationIdentity } from "../operations/operation-identity.js";
4
- import type { CallerDataError } from "../runtime/caller-data.copier.js";
1
+ import type { AvContracts } from "../compiler/contract.compiler.js";
2
+ import type { AvOperationIdentity } from "../operations/operation-identity.js";
3
+ import type { AvCallerDataError } from "../runtime/caller-data.copier.js";
5
4
  import type { MisuseReporter } from "../runtime/misuse-reporter.js";
6
5
  import type { ClientProtocolIdentity } from "../runtime/operation-request.js";
7
- import type { OperationFailureResponse } from "../runtime/operation-response.js";
8
- import type { Operation, OperationFingerprint } from "./deferred-operation.js";
6
+ import type { AvOperationFailureResponse } from "../runtime/operation-response.js";
7
+ import type { AvOperation, OperationFingerprint } from "./deferred-operation.js";
8
+ import type { PlannedSqlStatement } from "./sql-statement.js";
9
9
  import type { TransactionOperationOrigin } from "./transaction-request.js";
10
+ /** The origin a handle's builder gives it. */
11
+ type HandleOrigin = "application" | "client";
10
12
  /** One wire node, exactly as the transaction runtime receives it. */
11
13
  type WireOperationNode = {
12
14
  readonly resource: string;
@@ -15,22 +17,30 @@ type WireOperationNode = {
15
17
  readonly args: unknown;
16
18
  readonly fingerprint: OperationFingerprint | null;
17
19
  };
20
+ declare const SQL_STEP_NODE: Readonly<{
21
+ fingerprint: null;
22
+ }>;
23
+ type PlanNode = WireOperationNode | typeof SQL_STEP_NODE;
18
24
  export type AssembledTransactionPlan = {
19
25
  readonly ok: true;
20
26
  readonly plan: {
21
- readonly operations: readonly WireOperationNode[];
27
+ readonly operations: readonly PlanNode[];
22
28
  };
23
29
  /** Each operation's own origin, by index (mixed plans). */
24
30
  readonly origins: readonly TransactionOperationOrigin[];
25
31
  /** The steps whose arguments their builder could not copy, by index. */
26
- readonly refused: ReadonlyMap<number, CallerDataError>;
32
+ readonly refused: ReadonlyMap<number, AvCallerDataError>;
33
+ /** The `appTx.$sql` steps, by index, with each `$ref` bound to its source's position. */
34
+ readonly statements: ReadonlyMap<number, PlannedSqlStatement>;
27
35
  } | {
28
36
  readonly ok: false;
29
- readonly response: OperationFailureResponse;
37
+ readonly response: AvOperationFailureResponse;
30
38
  };
31
39
  export type TransactionPlanAssembler = {
32
40
  /** Defers one operation into a handle; nothing runs. */
33
- defer(origin: RequestOrigin, resource: string, identity: OperationIdentity, args: unknown): Operation<unknown>;
41
+ defer(origin: HandleOrigin, resource: string, identity: AvOperationIdentity, args: unknown): AvOperation<unknown>;
42
+ /** Defers one `appTx.$sql` statement into a handle; nothing runs. */
43
+ deferSql(strings: unknown, values: readonly unknown[]): AvOperation<unknown>;
34
44
  /** Binds a list of handles into the passive plan, or refuses it. */
35
45
  assemble(operations: unknown): Promise<AssembledTransactionPlan>;
36
46
  };
@@ -39,5 +49,5 @@ export type TransactionPlanAssembler = {
39
49
  * handle, so a handle another instance built — typed against another model,
40
50
  * bound to other Contracts — is refused rather than run.
41
51
  */
42
- export declare function createTransactionPlanAssembler(contracts: FrameworkContracts, protocol: ClientProtocolIdentity, misuse: MisuseReporter): TransactionPlanAssembler;
52
+ export declare function createTransactionPlanAssembler(contracts: AvContracts, protocol: ClientProtocolIdentity, misuse: MisuseReporter): TransactionPlanAssembler;
43
53
  export {};
@@ -1,10 +1,12 @@
1
- import { validationIssue } from "../operations/validation-code.js";
1
+ import { VALIDATION_CODE_MESSAGES, validationIssue, } from "../operations/validation-code.js";
2
2
  import { copyAtDoor, refuseCallerData } from "../runtime/caller-data.door.js";
3
3
  import { operationFailure } from "../runtime/operation-response.js";
4
4
  import { mapValidationOutcome } from "../runtime/outcome-mapper.js";
5
5
  import { encodeOperationArgumentsForWire } from "./operation-arguments-encoder.js";
6
6
  import { computeOperationFingerprint } from "./operation-fingerprint.js";
7
+ import { readSqlTemplate } from "./sql-statement.js";
7
8
  import { transactionOperationFailure } from "./transaction-response.js";
9
+ const SQL_STEP_NODE = Object.freeze({ fingerprint: null });
8
10
  function isPlainRecord(value) {
9
11
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
10
12
  return false;
@@ -13,26 +15,73 @@ function isPlainRecord(value) {
13
15
  return prototype === Object.prototype || prototype === null;
14
16
  }
15
17
  const KEEP = Symbol("keep");
16
- function rebuildArguments(value, path, leaf, ancestors = new Set()) {
17
- const replaced = leaf(value, path);
18
- if (replaced !== KEEP) {
19
- return replaced;
20
- }
21
- if (!Array.isArray(value) && !isPlainRecord(value)) {
22
- return value;
18
+ function rebuildArguments(root, leaf) {
19
+ const pathOf = (parent, key) => () => {
20
+ const segments = [];
21
+ if (key !== undefined) {
22
+ segments.push(key);
23
+ }
24
+ for (let frame = parent; frame?.key !== undefined; frame = frame.parent) {
25
+ segments.push(frame.key);
26
+ }
27
+ return segments.reverse();
28
+ };
29
+ const ancestors = new Set();
30
+ const finish = (frame) => {
31
+ ancestors.delete(frame.source);
32
+ if (!Array.isArray(frame.source)) {
33
+ return Object.fromEntries(frame.built);
34
+ }
35
+ const rebuilt = new Array(frame.source.length);
36
+ for (const [index, value] of frame.built) {
37
+ rebuilt[index] = value;
38
+ }
39
+ return rebuilt;
40
+ };
41
+ const enter = (value, parent, key) => {
42
+ const replaced = leaf(value, pathOf(parent, key));
43
+ if (replaced !== KEEP) {
44
+ return { value: replaced };
45
+ }
46
+ if (!Array.isArray(value) && !isPlainRecord(value)) {
47
+ return { value };
48
+ }
49
+ if (ancestors.has(value)) {
50
+ return { value };
51
+ }
52
+ ancestors.add(value);
53
+ const keys = Array.isArray(value)
54
+ ? Object.keys(value).map(Number)
55
+ : Object.keys(value);
56
+ return { source: value, keys, built: [], parent, key, next: 0 };
57
+ };
58
+ const first = enter(root, undefined, undefined);
59
+ if (!("source" in first)) {
60
+ return first.value;
23
61
  }
24
- if (ancestors.has(value)) {
25
- return value;
62
+ let frame = first;
63
+ for (;;) {
64
+ if (frame.next < frame.keys.length) {
65
+ const key = frame.keys[frame.next];
66
+ frame.next += 1;
67
+ const entry = frame.source[key];
68
+ const child = enter(entry, frame, key);
69
+ if ("source" in child) {
70
+ frame = child;
71
+ }
72
+ else {
73
+ frame.built.push([key, child.value]);
74
+ }
75
+ continue;
76
+ }
77
+ const rebuilt = finish(frame);
78
+ const parent = frame.parent;
79
+ if (parent === undefined) {
80
+ return rebuilt;
81
+ }
82
+ parent.built.push([frame.key, rebuilt]);
83
+ frame = parent;
26
84
  }
27
- ancestors.add(value);
28
- const rebuilt = Array.isArray(value)
29
- ? value.map((entry, index) => rebuildArguments(entry, [...path, index], leaf, ancestors))
30
- : Object.fromEntries(Object.entries(value).map(([key, entry]) => [
31
- key,
32
- rebuildArguments(entry, [...path, key], leaf, ancestors),
33
- ]));
34
- ancestors.delete(value);
35
- return rebuilt;
36
85
  }
37
86
  function operationEntryFailure(operation, message) {
38
87
  return transactionOperationFailure(operationFailure("A2004", {
@@ -57,19 +106,19 @@ export function createTransactionPlanAssembler(contracts, protocol, misuse) {
57
106
  };
58
107
  const placeholderSites = (args) => {
59
108
  const sites = [];
60
- rebuildArguments(args, [], (value, path) => {
109
+ rebuildArguments(args, (value, pathOf) => {
61
110
  const placeholder = typeof value === "object" && value !== null
62
111
  ? placeholders.get(value)
63
112
  : undefined;
64
113
  if (placeholder === undefined) {
65
114
  return KEEP;
66
115
  }
67
- sites.push({ path, object: value, placeholder });
116
+ sites.push({ path: pathOf(), object: value, placeholder });
68
117
  return value;
69
118
  });
70
119
  return sites;
71
120
  };
72
- const defer = (origin, resource, identity, args) => {
121
+ const createHandle = (record) => {
73
122
  const handle = Object.freeze({
74
123
  $ref: (field) => {
75
124
  const placeholder = Object.freeze({
@@ -79,14 +128,48 @@ export function createTransactionPlanAssembler(contracts, protocol, misuse) {
79
128
  return placeholder;
80
129
  },
81
130
  });
82
- handles.set(handle, {
83
- origin,
84
- resource,
85
- identity,
86
- ...snapshot(args),
87
- });
131
+ handles.set(handle, record);
88
132
  return handle;
89
133
  };
134
+ const defer = (origin, resource, identity, args) => createHandle({
135
+ kind: "operation",
136
+ origin,
137
+ resource,
138
+ identity,
139
+ ...snapshot(args),
140
+ });
141
+ const deferSql = (strings, values) => {
142
+ const copy = snapshot(values);
143
+ return createHandle({
144
+ kind: "sql",
145
+ strings: readSqlTemplate(strings, values.length),
146
+ values: Array.isArray(copy.args) ? copy.args : [],
147
+ ...(copy.refused === undefined ? {} : { refused: copy.refused }),
148
+ });
149
+ };
150
+ const recordPlaceholderSites = (record) => placeholderSites(record.kind === "sql" ? record.values : record.args);
151
+ const planSqlStatement = (record, positions) => {
152
+ const values = record.values.map((value) => {
153
+ const sites = placeholderSites(value);
154
+ const [site] = sites;
155
+ if (site === undefined) {
156
+ return { kind: "value", value };
157
+ }
158
+ if (site.path.length > 0) {
159
+ return { kind: "misplaced-reference", path: site.path };
160
+ }
161
+ return {
162
+ kind: "reference",
163
+ operation: positions.get(site.placeholder.source),
164
+ field: site.placeholder.field,
165
+ };
166
+ });
167
+ return {
168
+ strings: record.strings,
169
+ values,
170
+ ...(record.refused === undefined ? {} : { refused: record.refused }),
171
+ };
172
+ };
90
173
  const assemble = async (input) => {
91
174
  const list = copyAtDoor(input, { keep: (value) => value !== input });
92
175
  if (!list.ok) {
@@ -131,12 +214,35 @@ export function createTransactionPlanAssembler(contracts, protocol, misuse) {
131
214
  positions.set(entry, index);
132
215
  records.push(record);
133
216
  }
217
+ const originOfRecord = (record) => record.kind === "sql" ? "application" : record.origin;
218
+ const [first] = records;
219
+ if (first !== undefined) {
220
+ const planOrigin = originOfRecord(first);
221
+ const other = records.findIndex((record) => originOfRecord(record) !== planOrigin);
222
+ if (other !== -1) {
223
+ return {
224
+ ok: false,
225
+ response: transactionOperationFailure(operationFailure("A2004", {
226
+ message: "Transaction plan failed framework validation.",
227
+ issues: [
228
+ validationIssue("V1020", [], VALIDATION_CODE_MESSAGES.V1020),
229
+ ],
230
+ }), other),
231
+ };
232
+ }
233
+ }
134
234
  for (const [index, record] of records.entries()) {
135
- for (const { path, placeholder } of placeholderSites(record.args)) {
235
+ for (const { path, placeholder } of recordPlaceholderSites(record)) {
136
236
  if (!positions.has(placeholder.source)) {
137
237
  return {
138
238
  ok: false,
139
- response: referenceFailure(validationIssue("V1010", ["operations", index, "args", ...path, "$ref"], "Transaction reference names an operation that is not in this transaction."), index),
239
+ response: referenceFailure(validationIssue("V1010", [
240
+ "operations",
241
+ index,
242
+ record.kind === "sql" ? "values" : "args",
243
+ ...path,
244
+ "$ref",
245
+ ], "Transaction reference names an operation that is not in this transaction."), index),
140
246
  };
141
247
  }
142
248
  }
@@ -152,6 +258,9 @@ export function createTransactionPlanAssembler(contracts, protocol, misuse) {
152
258
  };
153
259
  const materialize = async (index) => {
154
260
  const record = records[index];
261
+ if (record.kind === "sql") {
262
+ return SQL_STEP_NODE;
263
+ }
155
264
  const bound = new Map();
156
265
  await Promise.all(placeholderSites(record.args).map(async ({ object, placeholder }) => {
157
266
  const operation = positions.get(placeholder.source);
@@ -160,7 +269,7 @@ export function createTransactionPlanAssembler(contracts, protocol, misuse) {
160
269
  $ref: { operation, fingerprint, path: [placeholder.field] },
161
270
  });
162
271
  }));
163
- const args = rebuildArguments(record.args, [], (value) => typeof value === "object" && value !== null && bound.has(value)
272
+ const args = rebuildArguments(record.args, (value) => typeof value === "object" && value !== null && bound.has(value)
164
273
  ? bound.get(value)
165
274
  : KEEP);
166
275
  return {
@@ -192,9 +301,14 @@ export function createTransactionPlanAssembler(contracts, protocol, misuse) {
192
301
  plan: {
193
302
  operations: await Promise.all(records.map((_, index) => nodeAt(index))),
194
303
  },
195
- origins: records.map((record) => originOf(record.origin)),
196
- refused: new Map(records.flatMap((record, index) => record.refused === undefined ? [] : [[index, record.refused]])),
304
+ origins: records.map((record) => originOf(record.kind === "sql" ? "application" : record.origin)),
305
+ refused: new Map(records.flatMap((record, index) => record.kind === "sql" || record.refused === undefined
306
+ ? []
307
+ : [[index, record.refused]])),
308
+ statements: new Map(records.flatMap((record, index) => record.kind === "sql"
309
+ ? [[index, planSqlStatement(record, positions)]]
310
+ : [])),
197
311
  };
198
312
  };
199
- return { defer, assemble };
313
+ return { defer, deferSql, assemble };
200
314
  }
@@ -1,15 +1,16 @@
1
- import type { AdapterModel } from "../adapters/adapter-model.js";
2
- import type { Contract } from "../contracts/contract.js";
3
- import type { ScalarField } from "../contracts/field.js";
1
+ import type { AvAdapterModel } from "../adapters/adapter-model.js";
2
+ import type { AvContract } from "../contracts/contract.js";
3
+ import type { AvScalarField } from "../contracts/field.js";
4
4
  import type { SemanticOperationArguments } from "../operations/operation-validator.js";
5
- import { CallerDataError } from "../runtime/caller-data.copier.js";
5
+ import { AvCallerDataError } from "../runtime/caller-data.copier.js";
6
6
  import { type ReadProjectionPlan } from "../runtime/computed-read-runtime.js";
7
7
  import type { FrameworkOperationRequest } from "../runtime/operation-request.js";
8
- import type { OperationFailureResponse } from "../runtime/operation-response.js";
8
+ import type { AvOperationFailureResponse } from "../runtime/operation-response.js";
9
9
  import type { OperationPipelineDependencies } from "../runtime/operation-runtime.js";
10
10
  import type { CascadeClosure } from "../runtime/referential-action.js";
11
11
  import type { OperationFingerprint } from "./deferred-operation.js";
12
- import type { TransactionReference } from "./transaction-reference.js";
12
+ import type { PlannedSqlStatement, ValidatedSqlStatement } from "./sql-statement.js";
13
+ import type { AvTransactionReference } from "./transaction-reference.js";
13
14
  import { type TransactionSlotShape } from "./transaction-reference.js";
14
15
  import { type TransactionReferenceCandidateSlot } from "./transaction-reference-collector.js";
15
16
  import type { FrameworkTransactionRequest, TransactionOperationOrigin } from "./transaction-request.js";
@@ -22,26 +23,29 @@ import type { FrameworkTransactionRequest, TransactionOperationOrigin } from "./
22
23
  * await, so a step revoked or changed after the call is never read again;
23
24
  * - `"owned"` — a plan the protocol decoder parsed, or the typed runner assembled
24
25
  * from its builders' copies; `refused` names the steps whose arguments a builder
25
- * could not copy, answered at their turn.
26
+ * could not copy, answered at their turn, and `statements` the steps that are
27
+ * `appTx.$sql` statements, which only the typed runner builds: they have no wire
28
+ * form, so no plan a caller or a remote client hands in can hold one.
26
29
  */
27
30
  export type PlanArguments = {
28
31
  readonly source: "caller";
29
32
  } | {
30
33
  readonly source: "owned";
31
- readonly refused?: ReadonlyMap<number, CallerDataError>;
34
+ readonly refused?: ReadonlyMap<number, AvCallerDataError>;
35
+ readonly statements?: ReadonlyMap<number, PlannedSqlStatement>;
32
36
  };
33
37
  export type { TransactionReferenceCandidateSlot } from "./transaction-reference-collector.js";
34
38
  /** One validated Transaction Slot: where it is, what it holds, and its reference. */
35
39
  export type TransactionReferenceSlot = {
36
40
  readonly path: readonly (string | number)[];
37
41
  /** The destination field. */
38
- readonly field: ScalarField<Contract>;
42
+ readonly field: AvScalarField<AvContract>;
39
43
  readonly shape: TransactionSlotShape;
40
44
  /** The slot is a scalar value of mutation data: what it resolves to is written. */
41
45
  readonly written: boolean;
42
46
  /** The source field, under its own step's Contract: what the slot receives. */
43
- readonly source: ScalarField<Contract>;
44
- readonly reference: TransactionReference;
47
+ readonly source: AvScalarField<AvContract>;
48
+ readonly reference: AvTransactionReference;
45
49
  };
46
50
  export type CandidateTransactionOperation = {
47
51
  readonly index: number;
@@ -52,21 +56,24 @@ export type CandidateTransactionOperation = {
52
56
  readonly candidates: readonly TransactionReferenceCandidateSlot[];
53
57
  };
54
58
  export type ValidatedTransactionOperation = {
59
+ readonly kind: "operation";
55
60
  readonly index: number;
56
61
  readonly request: FrameworkOperationRequest;
57
62
  /**
58
- * The Contract of this operation's own origin: the one it was validated
59
- * against, the one it runs under, and the one a later `$ref` reads its source
60
- * field from. One plan may carry steps of both origins.
63
+ * The Contract of this operation's origin, which is the plan's: the one it was
64
+ * validated against, the one it runs under, and the one a later `$ref` reads its
65
+ * source field from.
61
66
  */
62
- readonly contract: Contract;
67
+ readonly contract: AvContract;
63
68
  readonly arguments: SemanticOperationArguments;
64
69
  readonly projection: ReadProjectionPlan | undefined;
65
70
  readonly fingerprint: OperationFingerprint;
66
71
  readonly references: readonly TransactionReferenceSlot[];
67
72
  };
73
+ /** One validated plan step: a standard operation, or an `appTx.$sql` statement. */
74
+ export type ValidatedTransactionStep = ValidatedTransactionOperation | ValidatedSqlStatement;
68
75
  export type ValidatedTransactionPlan = {
69
- readonly operations: readonly ValidatedTransactionOperation[];
76
+ readonly operations: readonly ValidatedTransactionStep[];
70
77
  };
71
78
  /**
72
79
  * What validating a plan needs beyond one operation's pipeline: the cascade
@@ -74,23 +81,25 @@ export type ValidatedTransactionPlan = {
74
81
  * Framework is constructed. The record-write facts are not among them: they
75
82
  * are checked when each operation runs, after its pipes.
76
83
  */
77
- export type TransactionPlanDependencies<M extends AdapterModel> = Omit<OperationPipelineDependencies<M>, "recordWrites"> & {
84
+ export type TransactionPlanDependencies<M extends AvAdapterModel> = Omit<OperationPipelineDependencies<M>, "recordWrites" | "contexts" | "fieldStages"> & {
78
85
  readonly cascades: CascadeClosure;
86
+ /** Whether the Adapter declares `sqlStatements`: read once, at construction. */
87
+ readonly sqlStatements: boolean;
79
88
  };
80
89
  export type TransactionPlanValidationResult = {
81
90
  readonly ok: true;
82
91
  readonly plan: ValidatedTransactionPlan;
83
92
  } | {
84
93
  readonly ok: false;
85
- readonly response: OperationFailureResponse;
94
+ readonly response: AvOperationFailureResponse;
86
95
  };
87
96
  /**
88
97
  * The refusal of a plan on a Contract without transaction plans (`A2002`, one `V1006`
89
98
  * at `["operations"]`). The one spelling — the protocol's absence answer for
90
99
  * `/_transactions` is this envelope too.
91
100
  */
92
- export declare function transactionsUnavailableFailure(): OperationFailureResponse;
101
+ export declare function transactionsUnavailableFailure(): AvOperationFailureResponse;
93
102
  /**
94
103
  * Whole-plan Framework Validation Layer for transaction structure and identity.
95
104
  */
96
- export declare function validateTransactionPlan<M extends AdapterModel>(dependencies: TransactionPlanDependencies<M>, request: FrameworkTransactionRequest, origins?: readonly TransactionOperationOrigin[], planArguments?: PlanArguments): Promise<TransactionPlanValidationResult>;
105
+ export declare function validateTransactionPlan<M extends AvAdapterModel>(dependencies: TransactionPlanDependencies<M>, request: FrameworkTransactionRequest, origins?: readonly TransactionOperationOrigin[], planArguments?: PlanArguments): Promise<TransactionPlanValidationResult>;