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

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 (267) hide show
  1. package/CHANGELOG.md +1434 -0
  2. package/ERRORS.md +142 -0
  3. package/README.md +247 -57
  4. package/dist/adapters/adapter-discovery-diagnostic.d.ts +4 -4
  5. package/dist/adapters/adapter-discovery-diagnostic.guidance.d.ts +6 -0
  6. package/dist/adapters/adapter-discovery-diagnostic.guidance.js +7 -0
  7. package/dist/adapters/adapter-field.d.ts +25 -11
  8. package/dist/adapters/adapter-identifier.d.ts +6 -6
  9. package/dist/adapters/adapter-model.d.ts +18 -9
  10. package/dist/adapters/adapter-operation-executor.interface.d.ts +4 -4
  11. package/dist/adapters/adapter-operation.d.ts +3 -3
  12. package/dist/adapters/adapter-resource.d.ts +18 -9
  13. package/dist/adapters/adapter-sql-statement.d.ts +19 -0
  14. package/dist/adapters/adapter-transaction-executor.interface.d.ts +19 -0
  15. package/dist/adapters/adapter.interface.d.ts +59 -29
  16. package/dist/avz/avz-check.factory.d.ts +83 -0
  17. package/dist/avz/avz-check.factory.js +116 -0
  18. package/dist/avz/avz-check.registry.d.ts +35 -0
  19. package/dist/avz/avz-check.registry.js +9 -0
  20. package/dist/avz/avz.d.ts +203 -0
  21. package/dist/{json → avz}/avz.js +150 -8
  22. package/dist/{json → avz}/index.d.ts +2 -1
  23. package/dist/{json → avz}/json-shape.registry.d.ts +7 -7
  24. package/dist/{json → avz}/zod-issue.translator.d.ts +8 -2
  25. package/dist/{json → avz}/zod-issue.translator.js +7 -0
  26. package/dist/compiler/contract-compiler-diagnostic.d.ts +31 -5
  27. package/dist/compiler/contract-compiler-diagnostic.guidance.d.ts +6 -0
  28. package/dist/compiler/contract-compiler-diagnostic.guidance.js +97 -0
  29. package/dist/compiler/contract.compiler.d.ts +18 -18
  30. package/dist/compiler/contract.compiler.js +637 -442
  31. package/dist/compiler/create-requirements.projector.d.ts +49 -0
  32. package/dist/compiler/create-requirements.projector.js +423 -0
  33. package/dist/compiler/field-validator.compiler.d.ts +47 -0
  34. package/dist/compiler/field-validator.compiler.js +238 -0
  35. package/dist/config/any-framework-config.d.ts +19 -12
  36. package/dist/config/computed-fields.d.ts +62 -0
  37. package/dist/config/computed-fields.js +80 -0
  38. package/dist/config/config-diagnostic.d.ts +11 -4
  39. package/dist/config/config-diagnostic.guidance.d.ts +6 -0
  40. package/dist/config/config-diagnostic.guidance.js +32 -0
  41. package/dist/config/config-layer.d.ts +15 -6
  42. package/dist/config/config.resolver.d.ts +74 -31
  43. package/dist/config/config.resolver.js +108 -44
  44. package/dist/config/config.validator.d.ts +7 -7
  45. package/dist/config/config.validator.js +581 -72
  46. package/dist/config/define-resource.d.ts +57 -0
  47. package/dist/config/define-resource.js +14 -0
  48. package/dist/config/effective-fields.d.ts +19 -23
  49. package/dist/config/entrypoint.normalizer.d.ts +1 -1
  50. package/dist/config/exact-framework-config.d.ts +308 -0
  51. package/dist/config/field-compute.resolver.d.ts +21 -0
  52. package/dist/config/field-compute.resolver.js +123 -0
  53. package/dist/config/field-pipeline-config.d.ts +47 -0
  54. package/dist/config/forced-operation.validator.d.ts +10 -0
  55. package/dist/config/forced-operation.validator.js +66 -0
  56. package/dist/config/framework-config.d.ts +111 -39
  57. package/dist/config/framework-exemptions-config.d.ts +91 -0
  58. package/dist/config/framework-exemptions-config.js +1 -0
  59. package/dist/config/framework-field-definition.d.ts +78 -0
  60. package/dist/config/framework-field-definition.js +28 -0
  61. package/dist/config/framework-resources-config.d.ts +122 -0
  62. package/dist/config/framework-resources-config.js +1 -0
  63. package/dist/config/pipeline-config.d.ts +56 -20
  64. package/dist/config/pipeline-tree.resolver.d.ts +79 -0
  65. package/dist/config/pipeline-tree.resolver.js +220 -0
  66. package/dist/config/stage-type.eraser.d.ts +65 -0
  67. package/dist/config/stage-type.eraser.js +1 -0
  68. package/dist/contracts/capability.d.ts +21 -12
  69. package/dist/contracts/capability.js +2 -0
  70. package/dist/contracts/client-contract-structure.validator.d.ts +388 -63
  71. package/dist/contracts/client-contract-structure.validator.js +156 -10
  72. package/dist/contracts/contract-canonicalizer.d.ts +26 -3
  73. package/dist/contracts/contract-canonicalizer.js +156 -29
  74. package/dist/contracts/contract-hash.d.ts +12 -7
  75. package/dist/contracts/contract-hash.js +3 -2
  76. package/dist/contracts/contract-limits.d.ts +4 -1
  77. package/dist/contracts/contract-limits.js +1 -0
  78. package/dist/contracts/contract-lookup.d.ts +5 -5
  79. package/dist/contracts/contract-protocol.d.ts +4 -4
  80. package/dist/contracts/contract.d.ts +24 -16
  81. package/dist/contracts/create-requirements.d.ts +35 -0
  82. package/dist/contracts/create-requirements.js +1 -0
  83. package/dist/contracts/enum-definition.d.ts +1 -1
  84. package/dist/contracts/field-check.d.ts +101 -0
  85. package/dist/contracts/field-check.js +1 -0
  86. package/dist/contracts/field.d.ts +81 -42
  87. package/dist/contracts/identifier.d.ts +4 -4
  88. package/dist/contracts/json-shape.d.ts +12 -1
  89. package/dist/contracts/lifecycle-trait.d.ts +2 -2
  90. package/dist/contracts/resource-operations.d.ts +28 -28
  91. package/dist/contracts/resource.d.ts +15 -9
  92. package/dist/contracts/scalar-value-type.d.ts +15 -15
  93. package/dist/contracts/scalar.d.ts +3 -3
  94. package/dist/contracts/transaction-capability.d.ts +1 -1
  95. package/dist/contracts/wire-scalar-format.d.ts +4 -4
  96. package/dist/framework/contract-view.d.ts +213 -147
  97. package/dist/framework/framework-construction.error.d.ts +19 -10
  98. package/dist/framework/framework-construction.error.js +7 -3
  99. package/dist/framework/framework.d.ts +47 -23
  100. package/dist/framework/framework.factory.d.ts +8 -6
  101. package/dist/framework/framework.factory.js +49 -22
  102. package/dist/framework/operation-facade.d.ts +123 -52
  103. package/dist/framework/operation-facade.js +9 -1
  104. package/dist/index.d.ts +57 -50
  105. package/dist/index.js +7 -6
  106. package/dist/internal.d.ts +17 -10
  107. package/dist/internal.js +4 -1
  108. package/dist/operations/database-refusal.d.ts +51 -0
  109. package/dist/operations/database-refusal.js +45 -0
  110. package/dist/operations/operation-arguments.d.ts +343 -207
  111. package/dist/operations/operation-call.d.ts +27 -28
  112. package/dist/operations/operation-identity.d.ts +10 -10
  113. package/dist/operations/operation-result.d.ts +21 -21
  114. package/dist/operations/operation-validator-core.d.ts +6 -6
  115. package/dist/operations/operation-validator-core.js +99 -6
  116. package/dist/operations/operation-validator.d.ts +9 -9
  117. package/dist/operations/projection-scope.resolver.d.ts +6 -6
  118. package/dist/operations/structural-limits.walker.d.ts +6 -6
  119. package/dist/operations/structural-limits.walker.js +145 -0
  120. package/dist/operations/validation-code.d.ts +11 -6
  121. package/dist/operations/validation-code.guidance.d.ts +6 -0
  122. package/dist/operations/validation-code.guidance.js +90 -0
  123. package/dist/operations/validation-code.js +4 -0
  124. package/dist/operations/write-scope.resolver.d.ts +10 -6
  125. package/dist/operations/write-scope.resolver.js +2 -0
  126. package/dist/protocol/absent-route.responder.d.ts +13 -12
  127. package/dist/protocol/absent-route.responder.js +19 -19
  128. package/dist/protocol/contract-document.responder.d.ts +3 -3
  129. package/dist/protocol/contract-document.responder.js +2 -2
  130. package/dist/protocol/http-request.decoder.d.ts +17 -13
  131. package/dist/protocol/http-request.decoder.js +10 -10
  132. package/dist/protocol/http-request.reader.d.ts +3 -3
  133. package/dist/protocol/http-response.encoder.d.ts +4 -4
  134. package/dist/protocol/http-response.encoder.js +94 -55
  135. package/dist/protocol/http-status.table.d.ts +2 -2
  136. package/dist/protocol/operation-response.guard.d.ts +2 -2
  137. package/dist/protocol/protocol-surface.d.ts +4 -4
  138. package/dist/protocol/protocol.binder.d.ts +50 -27
  139. package/dist/protocol/protocol.binder.js +12 -9
  140. package/dist/protocol/protocol.handler.d.ts +22 -13
  141. package/dist/protocol/protocol.handler.js +91 -17
  142. package/dist/protocol/unmounted-route.error.d.ts +4 -4
  143. package/dist/protocol/unmounted-route.error.js +2 -2
  144. package/dist/runtime/caller-data.copier.d.ts +22 -5
  145. package/dist/runtime/caller-data.copier.js +31 -14
  146. package/dist/runtime/caller-data.door.d.ts +17 -11
  147. package/dist/runtime/caller-data.door.js +6 -4
  148. package/dist/runtime/computed-read-runtime.d.ts +7 -6
  149. package/dist/runtime/computed-read-runtime.js +76 -50
  150. package/dist/runtime/computed-value.error.d.ts +7 -6
  151. package/dist/runtime/computed-value.error.js +5 -4
  152. package/dist/runtime/context-executor.runtime.d.ts +59 -0
  153. package/dist/runtime/context-executor.runtime.js +275 -0
  154. package/dist/runtime/create-requirement.d.ts +44 -79
  155. package/dist/runtime/create-requirement.js +213 -73
  156. package/dist/runtime/decimal.d.ts +2 -2
  157. package/dist/runtime/decimal.js +1 -1
  158. package/dist/runtime/diagnostics-reporter.d.ts +54 -27
  159. package/dist/runtime/diagnostics-reporter.js +24 -0
  160. package/dist/runtime/error-boundary.d.ts +65 -8
  161. package/dist/runtime/error-boundary.js +49 -19
  162. package/dist/runtime/error-guidance.d.ts +12 -0
  163. package/dist/runtime/error-guidance.js +1 -0
  164. package/dist/runtime/error.catalog.d.ts +49 -0
  165. package/dist/runtime/error.catalog.js +49 -0
  166. package/dist/runtime/execution-options.d.ts +10 -2
  167. package/dist/runtime/field-check.evaluator.d.ts +29 -0
  168. package/dist/runtime/field-check.evaluator.js +521 -0
  169. package/dist/runtime/field-compute.d.ts +64 -0
  170. package/dist/runtime/field-compute.js +1 -0
  171. package/dist/runtime/field-stage.plan.d.ts +15 -0
  172. package/dist/runtime/field-stage.plan.js +27 -0
  173. package/dist/runtime/field-stage.runtime.d.ts +37 -0
  174. package/dist/runtime/field-stage.runtime.js +147 -0
  175. package/dist/runtime/field-validator.runtime.d.ts +19 -0
  176. package/dist/runtime/field-validator.runtime.js +92 -0
  177. package/dist/runtime/framework-error-class.table.d.ts +33 -0
  178. package/dist/runtime/framework-error-class.table.js +25 -0
  179. package/dist/runtime/framework-error.d.ts +68 -8
  180. package/dist/runtime/framework-error.factory.d.ts +4 -0
  181. package/dist/runtime/framework-error.factory.js +15 -0
  182. package/dist/runtime/framework-error.js +23 -2
  183. package/dist/runtime/interceptor-next.error.d.ts +5 -0
  184. package/dist/runtime/interceptor-next.error.js +6 -0
  185. package/dist/runtime/json-value.d.ts +11 -6
  186. package/dist/runtime/level-lifecycle.runtime.d.ts +25 -0
  187. package/dist/runtime/level-lifecycle.runtime.js +39 -0
  188. package/dist/runtime/misuse-reporter.d.ts +12 -12
  189. package/dist/runtime/mutation-planner.d.ts +19 -10
  190. package/dist/runtime/mutation-planner.js +48 -35
  191. package/dist/runtime/nested-write.walker.d.ts +32 -0
  192. package/dist/runtime/nested-write.walker.js +73 -0
  193. package/dist/runtime/operation-code.guidance.d.ts +6 -0
  194. package/dist/runtime/operation-code.guidance.js +167 -0
  195. package/dist/runtime/operation-request.d.ts +33 -6
  196. package/dist/runtime/operation-response.d.ts +17 -17
  197. package/dist/runtime/operation-runtime.d.ts +127 -25
  198. package/dist/runtime/operation-runtime.js +441 -78
  199. package/dist/runtime/outcome-mapper.d.ts +16 -5
  200. package/dist/runtime/outcome-mapper.js +23 -6
  201. package/dist/runtime/output-validator.d.ts +10 -10
  202. package/dist/runtime/output-validator.js +3 -3
  203. package/dist/runtime/owned-execution.registry.d.ts +16 -6
  204. package/dist/runtime/pipe-output.validator.d.ts +43 -18
  205. package/dist/runtime/pipe-output.validator.js +15 -13
  206. package/dist/runtime/pipeline.d.ts +223 -34
  207. package/dist/runtime/readonly-value.d.ts +43 -0
  208. package/dist/runtime/readonly-value.js +150 -0
  209. package/dist/runtime/request-envelope.validator.d.ts +3 -3
  210. package/dist/runtime/request-envelope.validator.js +12 -7
  211. package/dist/runtime/request-id.d.ts +1 -1
  212. package/dist/runtime/request-state.d.ts +14 -0
  213. package/dist/runtime/request-state.js +1 -0
  214. package/dist/runtime/request-state.resolver.d.ts +12 -0
  215. package/dist/runtime/request-state.resolver.js +20 -0
  216. package/dist/runtime/scalar-runtime.d.ts +35 -16
  217. package/dist/runtime/scalar-runtime.js +102 -34
  218. package/dist/runtime/unsent-value.normalizer.d.ts +15 -0
  219. package/dist/runtime/unsent-value.normalizer.js +128 -0
  220. package/dist/transactions/deferred-operation.d.ts +35 -20
  221. package/dist/transactions/operation-arguments-encoder.d.ts +3 -3
  222. package/dist/transactions/operation-arguments-encoder.js +2 -2
  223. package/dist/transactions/operation-fingerprint.js +2 -2
  224. package/dist/transactions/sql-statement.d.ts +85 -0
  225. package/dist/transactions/sql-statement.js +47 -0
  226. package/dist/transactions/sql-statement.validator.d.ts +44 -0
  227. package/dist/transactions/sql-statement.validator.js +167 -0
  228. package/dist/transactions/transaction-envelope.d.ts +4 -4
  229. package/dist/transactions/transaction-integrity-error.d.ts +6 -6
  230. package/dist/transactions/transaction-integrity-error.js +5 -2
  231. package/dist/transactions/transaction-plan-assembler.d.ts +21 -11
  232. package/dist/transactions/transaction-plan-assembler.js +169 -32
  233. package/dist/transactions/transaction-plan-validator.d.ts +45 -20
  234. package/dist/transactions/transaction-plan-validator.js +96 -38
  235. package/dist/transactions/transaction-reference-collector.d.ts +5 -5
  236. package/dist/transactions/transaction-reference-resolver.d.ts +19 -2
  237. package/dist/transactions/transaction-reference-resolver.js +26 -14
  238. package/dist/transactions/transaction-reference-validator.d.ts +76 -4
  239. package/dist/transactions/transaction-reference-validator.js +129 -71
  240. package/dist/transactions/transaction-reference.d.ts +13 -13
  241. package/dist/transactions/transaction-reference.js +5 -0
  242. package/dist/transactions/transaction-request.d.ts +18 -5
  243. package/dist/transactions/transaction-response.d.ts +6 -6
  244. package/dist/transactions/transaction-runtime.d.ts +8 -7
  245. package/dist/transactions/transaction-runtime.js +191 -50
  246. package/dist/transactions/transaction-settlement.runtime.d.ts +51 -0
  247. package/dist/transactions/transaction-settlement.runtime.js +120 -0
  248. package/dist/transactions/transaction-slot-path-error.d.ts +4 -4
  249. package/dist/transactions/transaction-slot-path-error.js +2 -2
  250. package/dist/transactions/transaction-slot-writer.js +2 -2
  251. package/dist/transactions/transaction-stage.runtime.d.ts +82 -0
  252. package/dist/transactions/transaction-stage.runtime.js +126 -0
  253. package/package.json +9 -7
  254. package/src/runtime/field-check.evaluator.ts +720 -0
  255. package/dist/config/define-fields.d.ts +0 -42
  256. package/dist/config/define-fields.js +0 -3
  257. package/dist/config/framework-behaviors-config.d.ts +0 -30
  258. package/dist/config/framework-fields-config.d.ts +0 -42
  259. package/dist/config/framework-restrictions-config.d.ts +0 -142
  260. package/dist/json/avz.d.ts +0 -96
  261. package/dist/runtime/field-behavior.d.ts +0 -48
  262. /package/dist/{config/framework-behaviors-config.js → adapters/adapter-sql-statement.js} +0 -0
  263. /package/dist/{config/framework-fields-config.js → adapters/adapter-transaction-executor.interface.js} +0 -0
  264. /package/dist/{json → avz}/index.js +0 -0
  265. /package/dist/{json → avz}/json-shape.registry.js +0 -0
  266. /package/dist/config/{framework-restrictions-config.js → exact-framework-config.js} +0 -0
  267. /package/dist/{runtime/field-behavior.js → config/field-pipeline-config.js} +0 -0
@@ -1,13 +1,20 @@
1
+ import { shapeOfField } from "../avz/json-shape.registry.js";
2
+ import { AVZ_FORMATS } from "../config/framework-field-definition.js";
3
+ import { FILTER_MODES, } from "../contracts/capability.js";
1
4
  import { lookupField, lookupOwn, lookupResource, } from "../contracts/contract-lookup.js";
2
5
  import { decodeBase64, WIRE_SCALAR_PATTERNS, } from "../contracts/wire-scalar-format.js";
3
- import { shapeOfField } from "../json/json-shape.registry.js";
4
6
  import { routeTransactionReferenceCandidate, } from "../transactions/transaction-reference.js";
5
- import { Decimal } from "./decimal.js";
7
+ import { AvDecimal } from "./decimal.js";
8
+ import { fieldCheckIssues } from "./field-check.evaluator.js";
6
9
  const INT_MIN = -(2 ** 31);
7
10
  const INT_MAX = 2 ** 31 - 1;
8
11
  const BIGINT_MIN = -(2n ** 63n);
9
12
  const BIGINT_MAX = 2n ** 63n - 1n;
10
13
  const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
14
+ const NAMED_FORMATS = new Set(AVZ_FORMATS);
15
+ function isNamedFormat(format) {
16
+ return NAMED_FORMATS.has(format);
17
+ }
11
18
  function isRecord(value) {
12
19
  return value !== null && typeof value === "object" && !Array.isArray(value);
13
20
  }
@@ -24,6 +31,10 @@ function issue(code, path, message) {
24
31
  function typeIssue(path, expected) {
25
32
  return issue("V1001", path, `Value must be ${expected}.`);
26
33
  }
34
+ export const UNSENT_READ_VALUE_MESSAGE = "Value must not be undefined: it would be left out, and the request would reach more rows.";
35
+ export function unsentReadValueIssue(path) {
36
+ return issue("V1001", path, UNSENT_READ_VALUE_MESSAGE);
37
+ }
27
38
  function formatIssue(path, expected) {
28
39
  return issue("V1003", path, `Value must use ${expected}.`);
29
40
  }
@@ -39,7 +50,7 @@ function materializeJsonPath(root, link) {
39
50
  segments.reverse();
40
51
  return [...root, ...segments];
41
52
  }
42
- function normalizeJsonValue(value, path, seen) {
53
+ export function normalizeJsonValue(value, path, seen) {
43
54
  const issues = [];
44
55
  let root;
45
56
  const stack = [
@@ -83,18 +94,14 @@ function normalizeJsonValue(value, path, seen) {
83
94
  const normalized = [];
84
95
  task.emit(normalized);
85
96
  stack.push({ kind: "leave", container: current });
86
- const entries = [];
87
- current.forEach((entry, index) => {
88
- entries.push([index, entry]);
89
- });
90
- for (let cursor = entries.length - 1; cursor >= 0; cursor -= 1) {
91
- const entry = entries[cursor];
97
+ for (let index = current.length - 1; index >= 0; index -= 1) {
98
+ const entry = current[index];
92
99
  stack.push({
93
100
  kind: "visit",
94
- value: entry[1],
95
- link: { parent: task.link, segment: entry[0] },
101
+ value: entry === undefined ? null : entry,
102
+ link: { parent: task.link, segment: index },
96
103
  emit: (child) => {
97
- normalized.push(child);
104
+ normalized[index] = child;
98
105
  },
99
106
  });
100
107
  }
@@ -112,7 +119,7 @@ function normalizeJsonValue(value, path, seen) {
112
119
  const normalized = {};
113
120
  task.emit(normalized);
114
121
  stack.push({ kind: "leave", container: current });
115
- const entries = Object.entries(current);
122
+ const entries = Object.entries(current).filter((entry) => entry[1] !== undefined);
116
123
  for (let cursor = entries.length - 1; cursor >= 0; cursor -= 1) {
117
124
  const entry = entries[cursor];
118
125
  stack.push({
@@ -232,7 +239,7 @@ function normalizeScalarAtom(contract, field, value, origin, path, allowNull) {
232
239
  case "decimal":
233
240
  if (origin === "application") {
234
241
  normalized =
235
- value instanceof Decimal
242
+ value instanceof AvDecimal
236
243
  ? { ok: true, value }
237
244
  : { ok: false, issues: [typeIssue(path, "an Aventara Decimal")] };
238
245
  }
@@ -244,7 +251,7 @@ function normalizeScalarAtom(contract, field, value, origin, path, allowNull) {
244
251
  }
245
252
  else {
246
253
  try {
247
- normalized = { ok: true, value: new Decimal(value) };
254
+ normalized = { ok: true, value: new AvDecimal(value) };
248
255
  }
249
256
  catch {
250
257
  normalized = {
@@ -359,9 +366,21 @@ export function normalizeScalarFieldValue(contract, field, value, origin, path)
359
366
  });
360
367
  return issues.length > 0
361
368
  ? { ok: false, issues }
362
- : { ok: true, value: normalized };
369
+ : heldToFormat(field, { ok: true, value: normalized }, origin, path);
370
+ }
371
+ return heldToFormat(field, normalizeScalarAtom(contract, field, value, origin === "runtime" ? "application" : origin, path, field.nullable), origin, path);
372
+ }
373
+ function heldToFormat(field, normalized, origin, path) {
374
+ const format = field.format;
375
+ if (origin !== "runtime" ||
376
+ !normalized.ok ||
377
+ format === undefined ||
378
+ format === "uuid" ||
379
+ !isNamedFormat(format)) {
380
+ return normalized;
363
381
  }
364
- return normalizeScalarAtom(contract, field, value, origin === "runtime" ? "application" : origin, path, field.nullable);
382
+ const issues = fieldCheckIssues([{ check: "string_format", format }], normalized.value, "string", path, field.list);
383
+ return issues.length === 0 ? normalized : { ok: false, issues };
365
384
  }
366
385
  export function jsonShapeIssues(field, value, path, position = "value") {
367
386
  if (field.json === undefined || value === null || value === undefined) {
@@ -385,7 +404,6 @@ function normalizeWrittenValue(contract, field, value, origin, path, issues) {
385
404
  issues.push(...result.issues);
386
405
  return undefined;
387
406
  }
388
- issues.push(...jsonShapeIssues(field, result.value, path));
389
407
  return result.value;
390
408
  }
391
409
  function normalizeWrittenElement(contract, field, value, origin, path, issues) {
@@ -394,7 +412,6 @@ function normalizeWrittenElement(contract, field, value, origin, path, issues) {
394
412
  issues.push(...result.issues);
395
413
  return undefined;
396
414
  }
397
- issues.push(...jsonShapeIssues(field, result.value, path, "entry"));
398
415
  return result.value;
399
416
  }
400
417
  const SCALAR_ATOM_COPIERS = {
@@ -429,6 +446,11 @@ export function copyScalarFieldValue(field, value) {
429
446
  function normalizeListElement(contract, field, value, origin, path) {
430
447
  return normalizeScalarAtom(contract, field, value, origin, path, false);
431
448
  }
449
+ function normalizeFilterEntry(contract, field, value, origin, path) {
450
+ return value === undefined
451
+ ? { ok: false, issues: [unsentReadValueIssue(path)] }
452
+ : normalizeListElement(contract, field, value, origin, path);
453
+ }
432
454
  function mergeResult(result, issues) {
433
455
  if (result.ok) {
434
456
  return result.value;
@@ -465,10 +487,40 @@ const NULL_OPERAND_OPERATORS = new Set([
465
487
  "arrayStartsWith",
466
488
  "arrayEndsWith",
467
489
  ]);
490
+ const KEYWORD_OPERANDS = Object.freeze({
491
+ mode: (operand, path, issues) => {
492
+ if (!FILTER_MODES.includes(operand)) {
493
+ issues.push(typeIssue(path, '"default", "sensitive" or "insensitive"'));
494
+ }
495
+ },
496
+ isEmpty: (operand, path, issues) => {
497
+ if (typeof operand !== "boolean") {
498
+ issues.push(typeIssue(path, "a boolean"));
499
+ }
500
+ },
501
+ path: (operand, path, issues) => {
502
+ if (!Array.isArray(operand)) {
503
+ issues.push(typeIssue(path, "an array of strings"));
504
+ return;
505
+ }
506
+ for (let index = 0; index < operand.length; index += 1) {
507
+ if (operand[index] === undefined) {
508
+ issues.push(unsentReadValueIssue([...path, index]));
509
+ }
510
+ else if (typeof operand[index] !== "string") {
511
+ issues.push(typeIssue([...path, index], "a string"));
512
+ }
513
+ }
514
+ },
515
+ });
468
516
  function normalizeScalarFilter(contract, field, value, origin, path, issues, references = false) {
469
517
  if (isKeptReference(references, value)) {
470
518
  return value;
471
519
  }
520
+ if (value === undefined) {
521
+ issues.push(unsentReadValueIssue(path));
522
+ return value;
523
+ }
472
524
  if (!isPlainRecord(value)) {
473
525
  return mergeResult(normalizeScalarFieldValue(contract, field, value, origin, path), issues);
474
526
  }
@@ -479,11 +531,17 @@ function normalizeScalarFilter(contract, field, value, origin, path, issues, ref
479
531
  const normalized = cloneRecord(value);
480
532
  for (const [operator, operand] of Object.entries(value)) {
481
533
  const operandPath = [...path, operator];
534
+ if (operand === undefined) {
535
+ issues.push(unsentReadValueIssue(operandPath));
536
+ continue;
537
+ }
482
538
  if (operand === null && !acceptsNullOperand(operator)) {
483
539
  issues.push(typeIssue(operandPath, "non-null"));
484
540
  continue;
485
541
  }
486
- if (operator === "mode" || operator === "isEmpty" || operator === "path") {
542
+ const keyword = lookupOwn(KEYWORD_OPERANDS, operator);
543
+ if (keyword !== undefined) {
544
+ keyword(operand, operandPath, issues);
487
545
  continue;
488
546
  }
489
547
  if (referenceOperandShape(operator) !== undefined &&
@@ -499,14 +557,14 @@ function normalizeScalarFilter(contract, field, value, origin, path, issues, ref
499
557
  issues.push(typeIssue(operandPath, "an array"));
500
558
  continue;
501
559
  }
502
- normalized[operator] = operand.map((entry, index) => mergeResult(normalizeListElement(contract, field, entry, origin, [
560
+ normalized[operator] = operand.map((entry, index) => mergeResult(normalizeFilterEntry(contract, field, entry, origin, [
503
561
  ...operandPath,
504
562
  index,
505
563
  ]), issues));
506
564
  continue;
507
565
  }
508
566
  if (operator === "has") {
509
- normalized[operator] = mergeResult(normalizeListElement(contract, field, operand, origin, operandPath), issues);
567
+ normalized[operator] = mergeResult(normalizeFilterEntry(contract, field, operand, origin, operandPath), issues);
510
568
  continue;
511
569
  }
512
570
  if (operator === "hasEvery" || operator === "hasSome") {
@@ -514,7 +572,7 @@ function normalizeScalarFilter(contract, field, value, origin, path, issues, ref
514
572
  issues.push(typeIssue(operandPath, "an array"));
515
573
  continue;
516
574
  }
517
- normalized[operator] = operand.map((entry, index) => mergeResult(normalizeListElement(contract, field, entry, origin, [
575
+ normalized[operator] = operand.map((entry, index) => mergeResult(normalizeFilterEntry(contract, field, entry, origin, [
518
576
  ...operandPath,
519
577
  index,
520
578
  ]), issues));
@@ -523,6 +581,12 @@ function normalizeScalarFilter(contract, field, value, origin, path, issues, ref
523
581
  if ("scalar" in field.type &&
524
582
  field.type.scalar === "json" &&
525
583
  (operator.startsWith("string") || operator.startsWith("array"))) {
584
+ if (operator.startsWith("string")) {
585
+ if (typeof operand !== "string") {
586
+ issues.push(typeIssue(operandPath, "a string"));
587
+ }
588
+ continue;
589
+ }
526
590
  const jsonResult = normalizeJsonValue(operand, operandPath, new Set());
527
591
  normalized[operator] = mergeResult(jsonResult, issues);
528
592
  continue;
@@ -587,6 +651,10 @@ function normalizeUniqueSelector(contract, resourceName, selector, origin, path,
587
651
  const normalized = cloneRecord(selector);
588
652
  for (const [fieldName, value] of Object.entries(selector)) {
589
653
  const field = lookupField(resource, fieldName);
654
+ if (field?.kind === "scalar" && value === undefined) {
655
+ issues.push(unsentReadValueIssue([...path, fieldName]));
656
+ continue;
657
+ }
590
658
  if (field?.kind === "scalar" && !isKeptReference(references, value)) {
591
659
  normalized[fieldName] = mergeResult(normalizeScalarFieldValue(contract, field, value, origin, [
592
660
  ...path,
@@ -679,9 +747,9 @@ function normalizeRelationMutation(contract, edge, value, origin, path, issues,
679
747
  const normalized = cloneRecord(value);
680
748
  for (const [directive, operand] of Object.entries(value)) {
681
749
  const directivePath = [...path, directive];
682
- const entries = edge.cardinality === "many" && Array.isArray(operand)
683
- ? operand
684
- : [operand];
750
+ const listed = edge.cardinality === "many" && Array.isArray(operand);
751
+ const entries = listed ? operand : [operand];
752
+ const entryPath = (index) => listed ? [...directivePath, index] : directivePath;
685
753
  if (directive === "$create") {
686
754
  normalized[directive] =
687
755
  edge.cardinality === "many" && Array.isArray(operand)
@@ -714,8 +782,8 @@ function normalizeRelationMutation(contract, edge, value, origin, path, issues,
714
782
  }
715
783
  return {
716
784
  ...entry,
717
- where: normalizeUniqueSelector(contract, edge.target, entry.where, origin, [...directivePath, index, "where"], issues, references),
718
- create: normalizeMutationData(contract, edge.target, entry.create, origin, [...directivePath, index, "create"], issues, references),
785
+ where: normalizeUniqueSelector(contract, edge.target, entry.where, origin, [...entryPath(index), "where"], issues, references),
786
+ create: normalizeMutationData(contract, edge.target, entry.create, origin, [...entryPath(index), "create"], issues, references),
719
787
  };
720
788
  });
721
789
  normalized[directive] =
@@ -733,10 +801,10 @@ function normalizeRelationMutation(contract, edge, value, origin, path, issues,
733
801
  ...entry,
734
802
  ...(entry.where !== undefined
735
803
  ? {
736
- where: normalizeWhere(contract, edge.target, entry.where, origin, [...directivePath, index, "where"], issues, references),
804
+ where: normalizeWhere(contract, edge.target, entry.where, origin, [...entryPath(index), "where"], issues, references),
737
805
  }
738
806
  : {}),
739
- data: normalizeMutationData(contract, edge.target, entry.data, origin, [...directivePath, index, "data"], issues, references),
807
+ data: normalizeMutationData(contract, edge.target, entry.data, origin, [...entryPath(index), "data"], issues, references),
740
808
  };
741
809
  });
742
810
  normalized[directive] =
@@ -752,7 +820,7 @@ function normalizeRelationMutation(contract, edge, value, origin, path, issues,
752
820
  }
753
821
  return {
754
822
  ...entry,
755
- where: normalizeWhere(contract, edge.target, entry.where, origin, [...directivePath, index, "where"], issues, references),
823
+ where: normalizeWhere(contract, edge.target, entry.where, origin, [...entryPath(index), "where"], issues, references),
756
824
  };
757
825
  });
758
826
  normalized[directive] =
@@ -770,11 +838,11 @@ function normalizeRelationMutation(contract, edge, value, origin, path, issues,
770
838
  ...entry,
771
839
  ...(entry.where !== undefined
772
840
  ? {
773
- where: normalizeUniqueSelector(contract, edge.target, entry.where, origin, [...directivePath, index, "where"], issues, references),
841
+ where: normalizeUniqueSelector(contract, edge.target, entry.where, origin, [...entryPath(index), "where"], issues, references),
774
842
  }
775
843
  : {}),
776
- create: normalizeMutationData(contract, edge.target, entry.create, origin, [...directivePath, index, "create"], issues, references),
777
- update: normalizeMutationData(contract, edge.target, entry.update, origin, [...directivePath, index, "update"], issues, references),
844
+ create: normalizeMutationData(contract, edge.target, entry.create, origin, [...entryPath(index), "create"], issues, references),
845
+ update: normalizeMutationData(contract, edge.target, entry.update, origin, [...entryPath(index), "update"], issues, references),
778
846
  };
779
847
  });
780
848
  normalized[directive] =
@@ -0,0 +1,15 @@
1
+ import type { AvContract } from "../contracts/contract.js";
2
+ import type { AvOperationFamily } from "../operations/operation-identity.js";
3
+ /**
4
+ * One operation's arguments with every write record's unsent fields removed — a
5
+ * field whose value is `undefined`, at any depth of nested write data the
6
+ * Contract's `maxNestingDepth` admits — and
7
+ * nothing else changed (see the module note): `where`, selectors and every other
8
+ * member are left as they are. The same object when nothing was unsent; otherwise
9
+ * a copy of each container on the way, never a write into `args`. Total over
10
+ * malformed input.
11
+ */
12
+ export declare function withoutUnsentWriteValues(contract: AvContract, request: {
13
+ readonly resource: string;
14
+ readonly family: AvOperationFamily;
15
+ }, args: unknown): unknown;
@@ -0,0 +1,128 @@
1
+ import { lookupField, lookupResource } from "../contracts/contract-lookup.js";
2
+ import { isPlainRecord, WRITE_KEYS } from "./field-stage.runtime.js";
3
+ import { isRelationMutationDirective, RELATION_MUTATION_RECORD_SITES, } from "./mutation-planner.js";
4
+ const REMOVE = Symbol("remove");
5
+ const DEPTH_CEILING = 256;
6
+ function rebuilt(record, changes) {
7
+ const copy = Object.getPrototypeOf(record) === null ? Object.create(null) : {};
8
+ for (const key of Object.keys(record)) {
9
+ const value = changes.has(key) ? changes.get(key) : record[key];
10
+ if (value === REMOVE) {
11
+ continue;
12
+ }
13
+ Object.defineProperty(copy, key, {
14
+ value,
15
+ writable: true,
16
+ enumerable: true,
17
+ configurable: true,
18
+ });
19
+ }
20
+ return copy;
21
+ }
22
+ function eachEntry(value, strip) {
23
+ if (!Array.isArray(value)) {
24
+ return strip(value);
25
+ }
26
+ let copy;
27
+ for (let index = 0; index < value.length; index += 1) {
28
+ if (!(index in value)) {
29
+ continue;
30
+ }
31
+ const entry = value[index];
32
+ const stripped = strip(entry);
33
+ if (stripped !== entry) {
34
+ copy ??= value.slice();
35
+ copy[index] = stripped;
36
+ }
37
+ }
38
+ return copy ?? value;
39
+ }
40
+ function stripRecord(walk, resourceName, record, depth) {
41
+ if (depth > walk.depth || !isPlainRecord(record)) {
42
+ return record;
43
+ }
44
+ const resource = lookupResource(walk.contract, resourceName);
45
+ let changes;
46
+ for (const key of Object.keys(record)) {
47
+ const value = record[key];
48
+ if (value === undefined) {
49
+ changes ??= new Map();
50
+ changes.set(key, REMOVE);
51
+ continue;
52
+ }
53
+ const field = lookupField(resource, key);
54
+ if (field?.kind !== "relation") {
55
+ continue;
56
+ }
57
+ const stripped = stripRelationWrite(walk, field.target, value, depth + 1);
58
+ if (stripped !== value) {
59
+ changes ??= new Map();
60
+ changes.set(key, stripped);
61
+ }
62
+ }
63
+ return changes === undefined ? record : rebuilt(record, changes);
64
+ }
65
+ function stripRelationWrite(walk, target, value, depth) {
66
+ if (!isPlainRecord(value)) {
67
+ return value;
68
+ }
69
+ let changes;
70
+ for (const directive of Object.keys(value)) {
71
+ if (!isRelationMutationDirective(directive)) {
72
+ continue;
73
+ }
74
+ const sites = RELATION_MUTATION_RECORD_SITES[directive];
75
+ if (sites.length === 0) {
76
+ continue;
77
+ }
78
+ const operand = value[directive];
79
+ const next = eachEntry(operand, (entry) => {
80
+ let stripped = entry;
81
+ for (const site of sites) {
82
+ if (site.key === undefined) {
83
+ stripped = stripRecord(walk, target, stripped, depth);
84
+ continue;
85
+ }
86
+ if (!isPlainRecord(stripped) || !Object.hasOwn(stripped, site.key)) {
87
+ continue;
88
+ }
89
+ const inner = stripped[site.key];
90
+ const record = stripRecord(walk, target, inner, depth);
91
+ if (record !== inner) {
92
+ stripped = rebuilt(stripped, new Map([[site.key, record]]));
93
+ }
94
+ }
95
+ return stripped;
96
+ });
97
+ if (next !== operand) {
98
+ changes ??= new Map();
99
+ changes.set(directive, next);
100
+ }
101
+ }
102
+ return changes === undefined ? value : rebuilt(value, changes);
103
+ }
104
+ export function withoutUnsentWriteValues(contract, request, args) {
105
+ const keys = Object.hasOwn(WRITE_KEYS, request.family)
106
+ ? WRITE_KEYS[request.family]
107
+ : undefined;
108
+ if (keys === undefined || !isPlainRecord(args)) {
109
+ return args;
110
+ }
111
+ const walk = {
112
+ contract,
113
+ depth: Math.min(contract.limits.maxNestingDepth, DEPTH_CEILING),
114
+ };
115
+ let changes;
116
+ for (const key of keys) {
117
+ if (!Object.hasOwn(args, key)) {
118
+ continue;
119
+ }
120
+ const value = args[key];
121
+ const next = eachEntry(value, (entry) => stripRecord(walk, request.resource, entry, 0));
122
+ if (next !== value) {
123
+ changes ??= new Map();
124
+ changes.set(key, next);
125
+ }
126
+ }
127
+ return changes === undefined ? args : rebuilt(args, changes);
128
+ }
@@ -1,12 +1,10 @@
1
- import type { OperationIdentity } from "../operations/operation-identity.js";
2
- import type { ExecutionOptions } from "../runtime/execution-options.js";
3
- import type { OperationFailureResponse } from "../runtime/operation-response.js";
4
- import type { TransactionReference } from "./transaction-reference.js";
5
- import type { TransactionCommittedResponse } from "./transaction-response.js";
1
+ import type { AvOperationIdentity } from "../operations/operation-identity.js";
2
+ import type { AvExecutionOptions } from "../runtime/execution-options.js";
3
+ import type { AvTransactionReference } from "./transaction-reference.js";
6
4
  /** An operation fingerprint: `fp1:` and a hash of the operation and its arguments. */
7
5
  export type OperationFingerprint = `fp1:${string}`;
8
6
  /** One serializable transaction-plan operation node. */
9
- export type DeferredOperation = OperationIdentity & {
7
+ export type DeferredOperation = AvOperationIdentity & {
10
8
  readonly resource: string;
11
9
  readonly args: unknown;
12
10
  readonly fingerprint: OperationFingerprint;
@@ -17,34 +15,51 @@ export type TransactionPlan = {
17
15
  };
18
16
  declare const OPERATION_RESULT: unique symbol;
19
17
  declare const OPERATION_HANDLE: unique symbol;
18
+ /** The origin a handle's builder gives it: `appTx`'s or `clientTx`'s. */
19
+ type HandleOrigin = "application" | "client";
20
+ type AnyOrigin = any;
20
21
  /** Standard-library shapes that are objects but never a projected result row. */
21
22
  export type BuiltinNonRow = Date | RegExp | ReadonlyMap<unknown, unknown> | ReadonlySet<unknown> | PromiseLike<unknown> | ArrayBufferView;
22
23
  /**
23
24
  * The single row behind a result type, or `never` when the result is not one
24
25
  * referenceable row.
25
26
  */
26
- export type SingleRowResult<T> = NonNullable<T> extends readonly unknown[] ? never : NonNullable<T> extends (...args: never[]) => unknown ? never : NonNullable<T> extends BuiltinNonRow ? never : NonNullable<T> extends object ? Extract<keyof NonNullable<T>, string> extends never ? never : NonNullable<T> : never;
27
+ export type AvSingleRowResult<T> = NonNullable<T> extends readonly unknown[] ? never : NonNullable<T> extends (...args: never[]) => unknown ? never : NonNullable<T> extends BuiltinNonRow ? never : NonNullable<T> extends object ? Extract<keyof NonNullable<T>, string> extends never ? never : NonNullable<T> : never;
27
28
  /** The field names a result row can be referenced by. */
28
- export type ReferenceableField<T> = Extract<keyof SingleRowResult<T>, string>;
29
+ export type AvReferenceableField<T> = Extract<keyof AvSingleRowResult<T>, string>;
29
30
  /** Builds a `$ref` to one field of this handle's row, typed as that field. */
30
- export type ReferenceBuilder<T> = <F extends ReferenceableField<T>>(field: F) => TransactionReference<SingleRowResult<T>[F]>;
31
+ export type AvReferenceBuilder<T> = <F extends AvReferenceableField<T>>(field: F) => AvTransactionReference<AvSingleRowResult<T>[F]>;
31
32
  /** Contributes `$ref` only where the result is a referenceable single row. */
32
- export type OperationReferenceSurface<T> = [SingleRowResult<T>] extends [never] ? {} : {
33
- readonly $ref: ReferenceBuilder<T>;
33
+ export type AvOperationReferenceSurface<T> = [AvSingleRowResult<T>] extends [
34
+ never
35
+ ] ? {} : {
36
+ readonly $ref: AvReferenceBuilder<T>;
34
37
  };
35
- /** An opaque deferred-operation handle. */
36
- export type Operation<T> = {
37
- readonly [OPERATION_HANDLE]: true;
38
+ /**
39
+ * The opaque part of every deferred-operation handle: the result type `T` its
40
+ * transaction slot resolves to, and the origin of the builder that made it
41
+ * (`appTx` is `"application"`, `clientTx` is `"client"`; left out, either).
42
+ * Type-level only: a handle holds neither at runtime.
43
+ */
44
+ export interface AvOperationHandle<T, Origin extends HandleOrigin = AnyOrigin> {
45
+ readonly [OPERATION_HANDLE]: Origin;
38
46
  readonly [OPERATION_RESULT]?: T;
39
- } & OperationReferenceSurface<T>;
47
+ }
48
+ /**
49
+ * An opaque deferred-operation handle: its result type and origin
50
+ * ({@link AvOperationHandle}), and `$ref` where the result is one row.
51
+ */
52
+ export type AvOperation<T, Origin extends HandleOrigin = AnyOrigin> = AvOperationHandle<T, Origin> & AvOperationReferenceSurface<T>;
40
53
  /** Ordered results of a tuple of handles, positionally typed. */
41
- export type TransactionResults<Ops extends readonly Operation<unknown>[]> = {
42
- readonly [I in keyof Ops]: Ops[I] extends Operation<infer R> ? R : never;
54
+ export type AvTransactionResults<Ops extends readonly AvOperation<unknown>[]> = {
55
+ readonly [I in keyof Ops]: Ops[I] extends AvOperation<infer R> ? R : never;
43
56
  };
44
57
  /**
45
58
  * `framework.transaction`'s type. It infers the handle tuple without `as const`
46
- * and resolves the committed response (`A1009`) with each handle's result in
47
- * plan order, or the failure response.
59
+ * and resolves each handle's result in plan order once the transaction
60
+ * committed, or throws its failure as the `AvError` subclass of the code's class
61
+ * (`cause.operation` names the failing step). The handles are all `appTx`
62
+ * handles or all `clientTx` handles: a list that mixes them is a type error.
48
63
  */
49
- export type TransactionRunner = <const Ops extends readonly Operation<unknown>[]>(operations: Ops, options?: ExecutionOptions) => Promise<TransactionCommittedResponse<TransactionResults<Ops>> | OperationFailureResponse>;
64
+ export type AvTransactionRunner = <const Ops extends readonly AvOperation<unknown, "application">[] | readonly AvOperation<unknown, "client">[]>(operations: Ops, options?: AvExecutionOptions) => Promise<AvTransactionResults<Ops>>;
50
65
  export {};
@@ -1,7 +1,7 @@
1
- import type { Contract } from "../contracts/contract.js";
2
- import type { OperationIdentity } from "../operations/operation-identity.js";
1
+ import type { AvContract } from "../contracts/contract.js";
2
+ import type { AvOperationIdentity } from "../operations/operation-identity.js";
3
3
  import type { SemanticOperationArguments } from "../operations/operation-validator.js";
4
4
  /** Encodes validated application arguments into the wire scalar form. */
5
- export declare function encodeOperationArgumentsForWire(contract: Contract, operation: OperationIdentity & {
5
+ export declare function encodeOperationArgumentsForWire(contract: AvContract, operation: AvOperationIdentity & {
6
6
  readonly resource: string;
7
7
  }, args: SemanticOperationArguments): unknown;
@@ -1,6 +1,6 @@
1
1
  import { lookupField, lookupResource } from "../contracts/contract-lookup.js";
2
2
  import { encodeBase64 } from "../contracts/wire-scalar-format.js";
3
- import { Decimal } from "../runtime/decimal.js";
3
+ import { AvDecimal } from "../runtime/decimal.js";
4
4
  import { isRelationMutationDirective, RELATION_MUTATION_RECORD_SITES, } from "../runtime/mutation-planner.js";
5
5
  import { isTransactionReferenceCandidate } from "./transaction-reference.js";
6
6
  function isPlainRecord(value) {
@@ -27,7 +27,7 @@ function encodeScalarAtom(field, value) {
27
27
  case "bigint":
28
28
  return typeof value === "bigint" ? value.toString(10) : value;
29
29
  case "decimal":
30
- return value instanceof Decimal ? value.toString() : value;
30
+ return value instanceof AvDecimal ? value.toString() : value;
31
31
  case "datetime":
32
32
  return value instanceof Date ? value.toISOString() : value;
33
33
  case "bytes":
@@ -1,4 +1,4 @@
1
- import { canonicalizeJson } from "../contracts/contract-canonicalizer.js";
1
+ import { canonicalizeWireJson } from "../contracts/contract-canonicalizer.js";
2
2
  const OPERATION_FINGERPRINT_PATTERN = /^fp1:[A-Za-z0-9_-]{22}$/;
3
3
  function encodeBase64Url(bytes) {
4
4
  let binary = "";
@@ -14,7 +14,7 @@ export function isOperationFingerprint(value) {
14
14
  return typeof value === "string" && OPERATION_FINGERPRINT_PATTERN.test(value);
15
15
  }
16
16
  export async function computeOperationFingerprint(node) {
17
- const canonical = canonicalizeJson({
17
+ const canonical = canonicalizeWireJson({
18
18
  resource: node.resource,
19
19
  family: node.family,
20
20
  variant: node.variant,