@aventara/core 0.1.0-pilot.4 → 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 (255) hide show
  1. package/CHANGELOG.md +1478 -0
  2. package/README.md +275 -11
  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 +17 -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 -25
  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/avz/avz.js +354 -0
  19. package/dist/avz/index.d.ts +7 -0
  20. package/dist/avz/index.js +1 -0
  21. package/dist/avz/json-shape.registry.d.ts +22 -0
  22. package/dist/avz/json-shape.registry.js +16 -0
  23. package/dist/avz/zod-issue.translator.d.ts +57 -0
  24. package/dist/avz/zod-issue.translator.js +149 -0
  25. package/dist/compiler/contract-compiler-diagnostic.d.ts +37 -5
  26. package/dist/compiler/contract.compiler.d.ts +18 -18
  27. package/dist/compiler/contract.compiler.js +595 -421
  28. package/dist/compiler/create-requirements.projector.d.ts +49 -0
  29. package/dist/compiler/create-requirements.projector.js +423 -0
  30. package/dist/compiler/field-validator.compiler.d.ts +47 -0
  31. package/dist/compiler/field-validator.compiler.js +238 -0
  32. package/dist/config/any-framework-config.d.ts +19 -12
  33. package/dist/config/config-diagnostic.d.ts +11 -4
  34. package/dist/config/config-layer.d.ts +15 -6
  35. package/dist/config/config.resolver.d.ts +75 -29
  36. package/dist/config/config.resolver.js +109 -44
  37. package/dist/config/config.validator.d.ts +7 -7
  38. package/dist/config/config.validator.js +586 -71
  39. package/dist/config/define-resource.d.ts +57 -0
  40. package/dist/config/define-resource.js +14 -0
  41. package/dist/config/effective-fields.d.ts +19 -23
  42. package/dist/config/entrypoint.normalizer.d.ts +1 -1
  43. package/dist/config/exact-framework-config.d.ts +280 -0
  44. package/dist/config/field-compute.resolver.d.ts +19 -0
  45. package/dist/config/field-compute.resolver.js +115 -0
  46. package/dist/config/field-pipeline-config.d.ts +47 -0
  47. package/dist/config/forced-operation.validator.d.ts +10 -0
  48. package/dist/config/forced-operation.validator.js +66 -0
  49. package/dist/config/framework-config.d.ts +111 -37
  50. package/dist/config/framework-exemptions-config.d.ts +91 -0
  51. package/dist/config/framework-exemptions-config.js +1 -0
  52. package/dist/config/framework-field-definition.d.ts +78 -0
  53. package/dist/config/framework-field-definition.js +28 -0
  54. package/dist/config/framework-resources-config.d.ts +116 -0
  55. package/dist/config/framework-resources-config.js +1 -0
  56. package/dist/config/pipeline-config.d.ts +56 -20
  57. package/dist/config/pipeline-tree.resolver.d.ts +79 -0
  58. package/dist/config/pipeline-tree.resolver.js +220 -0
  59. package/dist/config/stage-type.eraser.d.ts +65 -0
  60. package/dist/config/stage-type.eraser.js +1 -0
  61. package/dist/contracts/capability.d.ts +11 -11
  62. package/dist/contracts/client-contract-structure.validator.d.ts +344 -29
  63. package/dist/contracts/client-contract-structure.validator.js +248 -7
  64. package/dist/contracts/contract-canonicalizer.d.ts +19 -3
  65. package/dist/contracts/contract-canonicalizer.js +172 -27
  66. package/dist/contracts/contract-hash.d.ts +12 -7
  67. package/dist/contracts/contract-hash.js +3 -2
  68. package/dist/contracts/contract-limits.d.ts +4 -1
  69. package/dist/contracts/contract-limits.js +1 -0
  70. package/dist/contracts/contract-lookup.d.ts +5 -5
  71. package/dist/contracts/contract-protocol.d.ts +4 -4
  72. package/dist/contracts/contract.d.ts +31 -16
  73. package/dist/contracts/create-requirements.d.ts +35 -0
  74. package/dist/contracts/create-requirements.js +1 -0
  75. package/dist/contracts/enum-definition.d.ts +1 -1
  76. package/dist/contracts/field-check.d.ts +101 -0
  77. package/dist/contracts/field-check.js +1 -0
  78. package/dist/contracts/field.d.ts +61 -41
  79. package/dist/contracts/identifier.d.ts +4 -4
  80. package/dist/contracts/json-shape.d.ts +76 -0
  81. package/dist/contracts/json-shape.js +1 -0
  82. package/dist/contracts/lifecycle-trait.d.ts +2 -2
  83. package/dist/contracts/reference-write-limit.d.ts +7 -0
  84. package/dist/contracts/reference-write-limit.js +1 -0
  85. package/dist/contracts/resource-operations.d.ts +28 -28
  86. package/dist/contracts/resource.d.ts +15 -9
  87. package/dist/contracts/scalar-value-type.d.ts +25 -14
  88. package/dist/contracts/scalar.d.ts +3 -3
  89. package/dist/contracts/transaction-capability.d.ts +1 -1
  90. package/dist/contracts/wire-scalar-format.d.ts +4 -4
  91. package/dist/framework/contract-view.d.ts +234 -63
  92. package/dist/framework/framework-construction.error.d.ts +10 -10
  93. package/dist/framework/framework-construction.error.js +2 -2
  94. package/dist/framework/framework.d.ts +50 -24
  95. package/dist/framework/framework.factory.d.ts +8 -6
  96. package/dist/framework/framework.factory.js +95 -32
  97. package/dist/framework/operation-facade.d.ts +123 -52
  98. package/dist/framework/operation-facade.js +9 -1
  99. package/dist/index.d.ts +55 -82
  100. package/dist/index.js +6 -26
  101. package/dist/internal.d.ts +40 -0
  102. package/dist/internal.js +18 -0
  103. package/dist/operations/operation-arguments.d.ts +316 -201
  104. package/dist/operations/operation-call.d.ts +27 -28
  105. package/dist/operations/operation-identity.d.ts +12 -10
  106. package/dist/operations/operation-identity.js +3 -0
  107. package/dist/operations/operation-result.d.ts +21 -21
  108. package/dist/operations/operation-validator-core.d.ts +6 -6
  109. package/dist/operations/operation-validator-core.js +41 -12
  110. package/dist/operations/operation-validator.d.ts +24 -9
  111. package/dist/operations/operation-validator.js +26 -10
  112. package/dist/operations/projection-scope.resolver.d.ts +7 -7
  113. package/dist/operations/structural-limits.walker.d.ts +19 -0
  114. package/dist/operations/structural-limits.walker.js +489 -0
  115. package/dist/operations/validation-code.d.ts +11 -6
  116. package/dist/operations/validation-code.js +4 -0
  117. package/dist/operations/write-scope.resolver.d.ts +6 -6
  118. package/dist/protocol/absent-route.responder.d.ts +13 -11
  119. package/dist/protocol/absent-route.responder.js +42 -16
  120. package/dist/protocol/contract-document.responder.d.ts +3 -3
  121. package/dist/protocol/contract-document.responder.js +2 -2
  122. package/dist/protocol/http-request.decoder.d.ts +23 -13
  123. package/dist/protocol/http-request.decoder.js +19 -11
  124. package/dist/protocol/http-request.reader.d.ts +3 -3
  125. package/dist/protocol/http-response.encoder.d.ts +4 -4
  126. package/dist/protocol/http-response.encoder.js +94 -55
  127. package/dist/protocol/http-status.table.d.ts +2 -2
  128. package/dist/protocol/operation-response.guard.d.ts +2 -2
  129. package/dist/protocol/protocol-surface.d.ts +4 -4
  130. package/dist/protocol/protocol.binder.d.ts +52 -27
  131. package/dist/protocol/protocol.binder.js +14 -9
  132. package/dist/protocol/protocol.handler.d.ts +28 -15
  133. package/dist/protocol/protocol.handler.js +55 -10
  134. package/dist/protocol/unmounted-route.error.d.ts +19 -0
  135. package/dist/protocol/unmounted-route.error.js +10 -0
  136. package/dist/runtime/caller-data.copier.d.ts +59 -0
  137. package/dist/runtime/caller-data.copier.js +470 -0
  138. package/dist/runtime/caller-data.door.d.ts +56 -0
  139. package/dist/runtime/caller-data.door.js +67 -0
  140. package/dist/runtime/computed-read-runtime.d.ts +7 -6
  141. package/dist/runtime/computed-read-runtime.js +61 -55
  142. package/dist/runtime/computed-value.error.d.ts +13 -0
  143. package/dist/runtime/computed-value.error.js +9 -0
  144. package/dist/runtime/context-executor.runtime.d.ts +59 -0
  145. package/dist/runtime/context-executor.runtime.js +275 -0
  146. package/dist/runtime/create-requirement.d.ts +22 -57
  147. package/dist/runtime/create-requirement.js +140 -37
  148. package/dist/runtime/decimal.d.ts +2 -2
  149. package/dist/runtime/decimal.js +1 -1
  150. package/dist/runtime/diagnostics-reporter.d.ts +64 -22
  151. package/dist/runtime/diagnostics-reporter.js +35 -4
  152. package/dist/runtime/error-boundary.d.ts +73 -8
  153. package/dist/runtime/error-boundary.js +104 -48
  154. package/dist/runtime/execution-options.d.ts +10 -2
  155. package/dist/runtime/field-check.evaluator.d.ts +29 -0
  156. package/dist/runtime/field-check.evaluator.js +521 -0
  157. package/dist/runtime/field-compute.d.ts +64 -0
  158. package/dist/runtime/field-compute.js +1 -0
  159. package/dist/runtime/field-stage.plan.d.ts +15 -0
  160. package/dist/runtime/field-stage.plan.js +27 -0
  161. package/dist/runtime/field-stage.runtime.d.ts +35 -0
  162. package/dist/runtime/field-stage.runtime.js +143 -0
  163. package/dist/runtime/field-validator.runtime.d.ts +11 -0
  164. package/dist/runtime/field-validator.runtime.js +87 -0
  165. package/dist/runtime/framework-error-class.table.d.ts +33 -0
  166. package/dist/runtime/framework-error-class.table.js +25 -0
  167. package/dist/runtime/framework-error.d.ts +79 -7
  168. package/dist/runtime/framework-error.factory.d.ts +4 -0
  169. package/dist/runtime/framework-error.factory.js +15 -0
  170. package/dist/runtime/framework-error.js +23 -2
  171. package/dist/runtime/interceptor-next.error.d.ts +5 -0
  172. package/dist/runtime/interceptor-next.error.js +6 -0
  173. package/dist/runtime/json-value.d.ts +3 -3
  174. package/dist/runtime/level-lifecycle.runtime.d.ts +25 -0
  175. package/dist/runtime/level-lifecycle.runtime.js +42 -0
  176. package/dist/runtime/misuse-reporter.d.ts +32 -0
  177. package/dist/runtime/misuse-reporter.js +19 -0
  178. package/dist/runtime/mutation-planner.d.ts +19 -10
  179. package/dist/runtime/mutation-planner.js +62 -36
  180. package/dist/runtime/nested-write.walker.d.ts +32 -0
  181. package/dist/runtime/nested-write.walker.js +73 -0
  182. package/dist/runtime/operation-request.d.ts +35 -15
  183. package/dist/runtime/operation-response.d.ts +17 -17
  184. package/dist/runtime/operation-runtime.d.ts +125 -30
  185. package/dist/runtime/operation-runtime.js +423 -83
  186. package/dist/runtime/outcome-mapper.d.ts +16 -5
  187. package/dist/runtime/outcome-mapper.js +35 -8
  188. package/dist/runtime/output-validator.d.ts +16 -6
  189. package/dist/runtime/output-validator.js +12 -1
  190. package/dist/runtime/owned-execution.registry.d.ts +13 -0
  191. package/dist/runtime/owned-execution.registry.js +7 -0
  192. package/dist/runtime/pipe-output.validator.d.ts +33 -18
  193. package/dist/runtime/pipe-output.validator.js +12 -12
  194. package/dist/runtime/pipeline.d.ts +202 -28
  195. package/dist/runtime/readonly-value.d.ts +32 -0
  196. package/dist/runtime/readonly-value.js +141 -0
  197. package/dist/runtime/referential-action.d.ts +7 -0
  198. package/dist/runtime/request-envelope.validator.d.ts +5 -0
  199. package/dist/runtime/request-envelope.validator.js +123 -0
  200. package/dist/runtime/request-id.d.ts +1 -1
  201. package/dist/runtime/request-state.d.ts +14 -0
  202. package/dist/runtime/request-state.js +1 -0
  203. package/dist/runtime/request-state.resolver.d.ts +12 -0
  204. package/dist/runtime/request-state.resolver.js +20 -0
  205. package/dist/runtime/scalar-runtime.d.ts +56 -13
  206. package/dist/runtime/scalar-runtime.js +167 -86
  207. package/dist/transactions/deferred-operation.d.ts +35 -20
  208. package/dist/transactions/operation-arguments-encoder.d.ts +3 -3
  209. package/dist/transactions/operation-arguments-encoder.js +2 -2
  210. package/dist/transactions/sql-statement.d.ts +77 -0
  211. package/dist/transactions/sql-statement.js +47 -0
  212. package/dist/transactions/sql-statement.validator.d.ts +44 -0
  213. package/dist/transactions/sql-statement.validator.js +134 -0
  214. package/dist/transactions/transaction-envelope.d.ts +4 -4
  215. package/dist/transactions/transaction-integrity-error.d.ts +6 -6
  216. package/dist/transactions/transaction-integrity-error.js +5 -2
  217. package/dist/transactions/transaction-plan-assembler.d.ts +23 -9
  218. package/dist/transactions/transaction-plan-assembler.js +195 -82
  219. package/dist/transactions/transaction-plan-validator.d.ts +53 -21
  220. package/dist/transactions/transaction-plan-validator.js +246 -163
  221. package/dist/transactions/transaction-reference-collector.d.ts +30 -0
  222. package/dist/transactions/transaction-reference-collector.js +204 -0
  223. package/dist/transactions/transaction-reference-resolver.d.ts +19 -2
  224. package/dist/transactions/transaction-reference-resolver.js +27 -10
  225. package/dist/transactions/transaction-reference-validator.d.ts +34 -4
  226. package/dist/transactions/transaction-reference-validator.js +82 -43
  227. package/dist/transactions/transaction-reference.d.ts +30 -58
  228. package/dist/transactions/transaction-request.d.ts +20 -7
  229. package/dist/transactions/transaction-response.d.ts +12 -7
  230. package/dist/transactions/transaction-response.js +24 -1
  231. package/dist/transactions/transaction-runtime.d.ts +11 -8
  232. package/dist/transactions/transaction-runtime.js +167 -50
  233. package/dist/transactions/transaction-settlement.runtime.d.ts +51 -0
  234. package/dist/transactions/transaction-settlement.runtime.js +120 -0
  235. package/dist/transactions/transaction-slot-path-error.d.ts +4 -4
  236. package/dist/transactions/transaction-slot-path-error.js +2 -2
  237. package/dist/transactions/transaction-slot-writer.js +2 -2
  238. package/dist/transactions/transaction-stage.runtime.d.ts +81 -0
  239. package/dist/transactions/transaction-stage.runtime.js +123 -0
  240. package/package.json +18 -3
  241. package/src/runtime/field-check.evaluator.ts +720 -0
  242. package/dist/config/define-fields.d.ts +0 -35
  243. package/dist/config/define-fields.js +0 -3
  244. package/dist/config/framework-behaviors-config.d.ts +0 -30
  245. package/dist/config/framework-fields-config.d.ts +0 -36
  246. package/dist/config/framework-restrictions-config.d.ts +0 -142
  247. package/dist/operations/operation-boolean-nodes.d.ts +0 -11
  248. package/dist/operations/operation-boolean-nodes.js +0 -283
  249. package/dist/operations/operation-nesting.d.ts +0 -8
  250. package/dist/operations/operation-nesting.js +0 -307
  251. package/dist/runtime/field-behavior.d.ts +0 -48
  252. /package/dist/{config/framework-behaviors-config.js → adapters/adapter-sql-statement.js} +0 -0
  253. /package/dist/{config/framework-fields-config.js → adapters/adapter-transaction-executor.interface.js} +0 -0
  254. /package/dist/config/{framework-restrictions-config.js → exact-framework-config.js} +0 -0
  255. /package/dist/{runtime/field-behavior.js → config/field-pipeline-config.js} +0 -0
@@ -1,6 +1,7 @@
1
1
  import { PROJECTION_SCOPES, WRITE_SCOPES } from "./capability.js";
2
2
  import { AVENTARA_PROTOCOL_VERSION } from "./contract-protocol.js";
3
3
  import { LIFECYCLE_TRAITS } from "./lifecycle-trait.js";
4
+ import { REFERENCE_WRITE_LIMITS } from "./reference-write-limit.js";
4
5
  import { TRANSACTION_CAPABILITIES } from "./transaction-capability.js";
5
6
  const STRING = { is: "string" };
6
7
  const NUMBER = { is: "number" };
@@ -9,13 +10,16 @@ function literal(values) {
9
10
  return { is: "literal", values };
10
11
  }
11
12
  function list(item) {
12
- return { is: "list", item, nonEmpty: false, unique: false };
13
+ return { is: "list", item, minimum: 0, unique: false };
13
14
  }
14
15
  function nonEmptyList(item) {
15
- return { is: "list", item, nonEmpty: true, unique: false };
16
+ return { is: "list", item, minimum: 1, unique: false };
17
+ }
18
+ function pairOrMoreList(item) {
19
+ return { is: "list", item, minimum: 2, unique: false };
16
20
  }
17
21
  function uniqueList(item) {
18
- return { is: "list", item, nonEmpty: false, unique: true };
22
+ return { is: "list", item, minimum: 0, unique: true };
19
23
  }
20
24
  function registry(entry) {
21
25
  return { is: "registry", entry };
@@ -92,6 +96,127 @@ const RELATION_MUTATION = union(list(literal(RELATION_MUTATION_DIRECTIVES)), obj
92
96
  directives: list(literal(RELATION_MUTATION_DIRECTIVES)),
93
97
  scopes: WRITE_SCOPE_LIST,
94
98
  }));
99
+ const NAMED_FORMATS = [
100
+ "lowercase",
101
+ "uppercase",
102
+ "email",
103
+ "url",
104
+ "emoji",
105
+ "uuid",
106
+ "guid",
107
+ "nanoid",
108
+ "cuid",
109
+ "cuid2",
110
+ "ulid",
111
+ "xid",
112
+ "ksuid",
113
+ "datetime",
114
+ "date",
115
+ "time",
116
+ "duration",
117
+ "ipv4",
118
+ "ipv6",
119
+ "cidrv4",
120
+ "cidrv6",
121
+ "base64",
122
+ "base64url",
123
+ "json_string",
124
+ "e164",
125
+ "credit_card",
126
+ "iban",
127
+ "jwt",
128
+ ];
129
+ const STRING_FORMAT_CHECK = union(object({
130
+ check: literal(["string_format"]),
131
+ format: literal(["regex"]),
132
+ pattern: object({ source: STRING, flags: STRING }),
133
+ }), object({
134
+ check: literal(["string_format"]),
135
+ format: literal(["includes"]),
136
+ includes: STRING,
137
+ }), object({
138
+ check: literal(["string_format"]),
139
+ format: literal(["starts_with"]),
140
+ prefix: STRING,
141
+ }), object({
142
+ check: literal(["string_format"]),
143
+ format: literal(["ends_with"]),
144
+ suffix: STRING,
145
+ }), object({
146
+ check: literal(["string_format"]),
147
+ format: literal(NAMED_FORMATS),
148
+ }));
149
+ const STRING_LENGTH_CHECKS = {
150
+ max_length: object({ check: literal(["max_length"]), maximum: NUMBER }),
151
+ min_length: object({ check: literal(["min_length"]), minimum: NUMBER }),
152
+ length_equals: object({ check: literal(["length_equals"]), length: NUMBER }),
153
+ string_format: STRING_FORMAT_CHECK,
154
+ };
155
+ const FIELD_CHECK = {
156
+ is: "variants",
157
+ key: "check",
158
+ arms: {
159
+ less_than: object({
160
+ check: literal(["less_than"]),
161
+ value: union(NUMBER, STRING),
162
+ inclusive: BOOLEAN,
163
+ }),
164
+ greater_than: object({
165
+ check: literal(["greater_than"]),
166
+ value: union(NUMBER, STRING),
167
+ inclusive: BOOLEAN,
168
+ }),
169
+ multiple_of: object({
170
+ check: literal(["multiple_of"]),
171
+ value: union(NUMBER, STRING),
172
+ }),
173
+ max_length: object({ check: literal(["max_length"]), maximum: NUMBER }, { list: literal([true]) }),
174
+ min_length: object({ check: literal(["min_length"]), minimum: NUMBER }, { list: literal([true]) }),
175
+ length_equals: STRING_LENGTH_CHECKS.length_equals,
176
+ string_format: STRING_FORMAT_CHECK,
177
+ max_size: object({ check: literal(["max_size"]), maximum: NUMBER }),
178
+ min_size: object({ check: literal(["min_size"]), minimum: NUMBER }),
179
+ size_equals: object({ check: literal(["size_equals"]), size: NUMBER }),
180
+ },
181
+ };
182
+ const JSON_STRING_CHECK = {
183
+ is: "variants",
184
+ key: "check",
185
+ arms: STRING_LENGTH_CHECKS,
186
+ };
187
+ const JSON_NUMBER_CHECK = {
188
+ is: "variants",
189
+ key: "check",
190
+ arms: {
191
+ less_than: object({
192
+ check: literal(["less_than"]),
193
+ value: NUMBER,
194
+ inclusive: BOOLEAN,
195
+ }),
196
+ greater_than: object({
197
+ check: literal(["greater_than"]),
198
+ value: NUMBER,
199
+ inclusive: BOOLEAN,
200
+ }),
201
+ multiple_of: object({ check: literal(["multiple_of"]), value: NUMBER }),
202
+ },
203
+ };
204
+ const JSON_LIST_CHECK = {
205
+ is: "variants",
206
+ key: "check",
207
+ arms: {
208
+ max_length: object({
209
+ check: literal(["max_length"]),
210
+ maximum: NUMBER,
211
+ list: literal([true]),
212
+ }),
213
+ min_length: object({
214
+ check: literal(["min_length"]),
215
+ minimum: NUMBER,
216
+ list: literal([true]),
217
+ }),
218
+ },
219
+ };
95
220
  const SCALAR_FIELD = object({
96
221
  kind: literal(["scalar"]),
97
222
  type: union(object({ scalar: STRING }), object({ enum: STRING })),
@@ -106,7 +231,11 @@ const SCALAR_FIELD = object({
106
231
  create: list(literal(SCALAR_MUTATION_DIRECTIVES)),
107
232
  update: list(literal(SCALAR_MUTATION_DIRECTIVES)),
108
233
  }),
109
- }, { format: STRING });
234
+ }, {
235
+ format: STRING,
236
+ json: { is: "jsonSchema" },
237
+ validators: list(FIELD_CHECK),
238
+ });
110
239
  const RELATION_FIELD = object({
111
240
  kind: literal(["relation"]),
112
241
  target: STRING,
@@ -124,6 +253,7 @@ const RELATION_FIELD = object({
124
253
  })),
125
254
  create: RELATION_MUTATION,
126
255
  update: RELATION_MUTATION,
256
+ nestedWriteFilter: literal(["scalar"]),
127
257
  }),
128
258
  });
129
259
  const OPERATION_AVAILABILITY = union(literal([true]), object({ projection: literal(PROJECTION_SCOPES) }, { write: literal(WRITE_SCOPES) }), object({ write: literal(WRITE_SCOPES) }, { projection: literal(PROJECTION_SCOPES) }));
@@ -160,6 +290,12 @@ const RESOURCE = object({
160
290
  }),
161
291
  upsert: object({}, { unique: OPERATION_AVAILABILITY }),
162
292
  }),
293
+ }, {
294
+ createRequirements: object({}, {
295
+ required: nonEmptyList(STRING),
296
+ oneOf: nonEmptyList(pairOrMoreList(STRING)),
297
+ parentSupplies: registry(registry(nonEmptyList(STRING))),
298
+ }),
163
299
  });
164
300
  export const CLIENT_CONTRACT_STRUCTURE = object({
165
301
  scalars: registry(object({ builtin: literal([true]) })),
@@ -171,13 +307,14 @@ export const CLIENT_CONTRACT_STRUCTURE = object({
171
307
  maxListLimit: NUMBER,
172
308
  maxTransactionOperations: NUMBER,
173
309
  maxBooleanNodes: NUMBER,
310
+ maxJsonDepth: NUMBER,
174
311
  }),
175
312
  transactions: literal(TRANSACTION_CAPABILITIES),
176
313
  protocol: object({
177
314
  version: literal([AVENTARA_PROTOCOL_VERSION]),
178
315
  hash: { is: "prefixed", prefix: "sha256:" },
179
316
  }),
180
- });
317
+ }, { referenceWrites: literal(REFERENCE_WRITE_LIMITS) });
181
318
  export function validateClientContractStructure(value) {
182
319
  const defect = defectOf(CLIENT_CONTRACT_STRUCTURE, value, "$", 0);
183
320
  if (defect !== undefined) {
@@ -233,6 +370,108 @@ function defectOf(node, value, path, depth) {
233
370
  }
234
371
  case "union":
235
372
  return unionDefect(node.arms, value, path, depth);
373
+ case "jsonSchema":
374
+ return jsonSchemaDefect(value, path, depth);
375
+ }
376
+ }
377
+ const JSON_SCHEMA_SCALAR_TYPES = [
378
+ "string",
379
+ "number",
380
+ "integer",
381
+ "boolean",
382
+ "null",
383
+ ];
384
+ const JSON_PRIMITIVE = union(STRING, NUMBER, BOOLEAN);
385
+ function jsonSchemaDefect(value, path, depth) {
386
+ const kind = kindDefect(value, "object", path, depth);
387
+ if (kind !== undefined)
388
+ return kind;
389
+ const schema = value;
390
+ const keys = Object.keys(schema).sort().join(",");
391
+ const child = (member, entry, at = `${path}.${member}`) => jsonSchemaDefect(entry, at, depth + 1);
392
+ const children = (member, minimum) => {
393
+ const listKind = kindDefect(schema[member], "array", `${path}.${member}`, depth + 1);
394
+ if (listKind !== undefined)
395
+ return listKind;
396
+ const entries = schema[member];
397
+ if (entries.length < minimum) {
398
+ return at(`${path}.${member}`, depth + 1, `needs at least ${minimum} entries`);
399
+ }
400
+ for (let index = 0; index < entries.length; index += 1) {
401
+ const defect = child(member, entries[index], `${path}.${member}[${index}]`);
402
+ if (defect !== undefined)
403
+ return defect;
404
+ }
405
+ return undefined;
406
+ };
407
+ switch (keys) {
408
+ case "type":
409
+ return literalDefect(JSON_SCHEMA_SCALAR_TYPES, schema.type, `${path}.type`, depth + 1);
410
+ case "checks,type":
411
+ return (literalDefect(["string", "number", "integer"], schema.type, `${path}.type`, depth + 1) ??
412
+ defectOf(list(schema.type === "string" ? JSON_STRING_CHECK : JSON_NUMBER_CHECK), schema.checks, `${path}.checks`, depth + 1));
413
+ case "checks,items,type":
414
+ return (literalDefect(["array"], schema.type, `${path}.type`, depth + 1) ??
415
+ child("items", schema.items) ??
416
+ defectOf(list(JSON_LIST_CHECK), schema.checks, `${path}.checks`, depth + 1));
417
+ case "const":
418
+ return Object.is(schema.const, null)
419
+ ? undefined
420
+ : defectOf(JSON_PRIMITIVE, schema.const, `${path}.const`, depth + 1);
421
+ case "enum,type":
422
+ return (literalDefect(["string"], schema.type, `${path}.type`, depth + 1) ??
423
+ defectOf({ is: "list", item: STRING, minimum: 1, unique: true }, schema.enum, `${path}.enum`, depth + 1));
424
+ case "additionalProperties,properties,type":
425
+ case "additionalProperties,properties,required,type": {
426
+ const typeDefect = literalDefect(["object"], schema.type, `${path}.type`, depth + 1) ??
427
+ literalDefect([false], schema.additionalProperties, `${path}.additionalProperties`, depth + 1);
428
+ if (typeDefect !== undefined)
429
+ return typeDefect;
430
+ const propertiesKind = kindDefect(schema.properties, "object", `${path}.properties`, depth + 1);
431
+ if (propertiesKind !== undefined)
432
+ return propertiesKind;
433
+ const properties = schema.properties;
434
+ for (const name of Object.keys(properties)) {
435
+ const defect = child("properties", properties[name], `${path}.properties.${name}`);
436
+ if (defect !== undefined)
437
+ return defect;
438
+ }
439
+ if (!Object.hasOwn(schema, "required"))
440
+ return undefined;
441
+ const requiredDefect = defectOf({ is: "list", item: STRING, minimum: 0, unique: true }, schema.required, `${path}.required`, depth + 1);
442
+ if (requiredDefect !== undefined)
443
+ return requiredDefect;
444
+ const undeclared = schema.required.findIndex((name) => !Object.hasOwn(properties, name));
445
+ return undeclared === -1
446
+ ? undefined
447
+ : at(`${path}.required[${undeclared}]`, depth + 1, "names a key that is not one of the object's properties");
448
+ }
449
+ case "additionalProperties,type":
450
+ return (literalDefect(["object"], schema.type, `${path}.type`, depth + 1) ??
451
+ child("additionalProperties", schema.additionalProperties));
452
+ case "items,type":
453
+ return (literalDefect(["array"], schema.type, `${path}.type`, depth + 1) ??
454
+ child("items", schema.items));
455
+ case "items,maxItems,minItems,prefixItems,type": {
456
+ const defect = literalDefect(["array"], schema.type, `${path}.type`, depth + 1) ??
457
+ literalDefect([false], schema.items, `${path}.items`, depth + 1) ??
458
+ children("prefixItems", 1);
459
+ if (defect !== undefined)
460
+ return defect;
461
+ const length = schema.prefixItems.length;
462
+ for (const bound of ["minItems", "maxItems"]) {
463
+ if (schema[bound] !== length) {
464
+ return at(`${path}.${bound}`, depth + 1, `is ${JSON.stringify(schema[bound])}, not the tuple's length ${length}`);
465
+ }
466
+ }
467
+ return undefined;
468
+ }
469
+ case "anyOf":
470
+ return children("anyOf", 2);
471
+ case "oneOf":
472
+ return children("oneOf", 1);
473
+ default:
474
+ return at(path, depth, `is not a Json Shape schema: its keywords {${keys}} are not one of the forms Aventara emits`);
236
475
  }
237
476
  }
238
477
  function literalDefect(values, value, path, depth) {
@@ -251,8 +490,10 @@ function listDefect(node, value, path, depth) {
251
490
  if (kind !== undefined)
252
491
  return kind;
253
492
  const entries = value;
254
- if (node.nonEmpty && entries.length === 0) {
255
- return at(path, depth, "is an empty JSON array, and needs at least one entry");
493
+ if (entries.length < node.minimum) {
494
+ return at(path, depth, entries.length === 0
495
+ ? `is an empty JSON array, and needs at least ${node.minimum === 1 ? "one entry" : "two entries"}`
496
+ : "is a JSON array of one entry, and needs at least two");
256
497
  }
257
498
  const firstIndexOf = new Map();
258
499
  for (let index = 0; index < entries.length; index += 1) {
@@ -1,11 +1,27 @@
1
- import type { Contract } from "./contract.js";
1
+ import type { AvContract } from "./contract.js";
2
2
  /**
3
3
  * Thrown when a value cannot be written as canonical JSON (RFC 8785). It is a
4
4
  * `TypeError`.
5
5
  */
6
- export declare class CanonicalJsonError extends TypeError {
6
+ export declare class AvCanonicalJsonError extends TypeError {
7
7
  }
8
8
  /** Canonical JSON (RFC 8785) for a JSON-safe value. */
9
9
  export declare function canonicalizeJson(value: unknown): string;
10
+ /** One top-level member of a canonical Contract: its key and its value's canonical text. */
11
+ export type CanonicalContractMember = {
12
+ readonly key: string;
13
+ readonly text: string;
14
+ };
15
+ /** The Contract as canonical JSON (RFC 8785), one top-level member at a time, in order. */
16
+ export declare function canonicalizeContractMembers<C extends AvContract>(contract: C): readonly CanonicalContractMember[];
17
+ /** The canonical object text the members write: `{"key":text,…}`, in their order. */
18
+ export declare function joinCanonicalContractMembers(members: readonly CanonicalContractMember[]): string;
10
19
  /** The Contract as canonical JSON (RFC 8785): the same bytes for the same Contract. */
11
- export declare function canonicalizeContract<C extends Contract>(contract: C): string;
20
+ export declare function canonicalizeContract<C extends AvContract>(contract: C): string;
21
+ /** Records `text` as the canonical text `contract` was parsed from. */
22
+ export declare function recordCanonicalContractText(contract: AvContract, text: string): void;
23
+ /**
24
+ * The Contract's canonical text: the text it was identified from, when the compiler
25
+ * recorded one, else written now. Both are the same bytes for the same Contract.
26
+ */
27
+ export declare function canonicalContractTextOf<C extends AvContract>(contract: C): string;
@@ -3,7 +3,7 @@ import { LIFECYCLE_TRAITS } from "./lifecycle-trait.js";
3
3
  import { projectionScopeOf, writeScopeOf } from "./resource-operations.js";
4
4
  const PAGINATION_ORDER = ["limit", "offset", "cursor"];
5
5
  const REDUCER_ORDER = ["$count"];
6
- export class CanonicalJsonError extends TypeError {
6
+ export class AvCanonicalJsonError extends TypeError {
7
7
  }
8
8
  function compareCodeUnits(left, right) {
9
9
  if (left === right) {
@@ -83,10 +83,42 @@ function normalizeCapabilities(capabilities) {
83
83
  }
84
84
  return normalized;
85
85
  }
86
+ function normalizeJsonSchema(schema) {
87
+ if (Array.isArray(schema)) {
88
+ return schema.map(normalizeJsonSchema);
89
+ }
90
+ if (typeof schema !== "object" || schema === null) {
91
+ return schema;
92
+ }
93
+ const normalized = {};
94
+ for (const [key, value] of Object.entries(schema)) {
95
+ if (key === "required" && Array.isArray(value)) {
96
+ normalized[key] = [...value].sort((left, right) => compareCodeUnits(String(left), String(right)));
97
+ }
98
+ else if (key === "properties" &&
99
+ typeof value === "object" &&
100
+ value !== null) {
101
+ normalized[key] = Object.fromEntries(Object.entries(value).map(([name, child]) => [
102
+ name,
103
+ normalizeJsonSchema(child),
104
+ ]));
105
+ }
106
+ else if (key === "const" || key === "enum" || key === "checks") {
107
+ normalized[key] = value;
108
+ }
109
+ else {
110
+ normalized[key] = normalizeJsonSchema(value);
111
+ }
112
+ }
113
+ return normalized;
114
+ }
86
115
  function normalizeField(field) {
87
116
  if (field.kind === "scalar") {
88
117
  return {
89
118
  ...field,
119
+ ...(field.json !== undefined
120
+ ? { json: normalizeJsonSchema(field.json) }
121
+ : {}),
90
122
  lifecycle: normalizeStringSet(field.lifecycle, LIFECYCLE_TRAITS),
91
123
  capabilities: normalizeCapabilities(field.capabilities),
92
124
  };
@@ -141,8 +173,46 @@ function normalizeResource(resource) {
141
173
  fields,
142
174
  identifiers,
143
175
  operations: normalizeOperations(resource.operations),
176
+ ...(resource.createRequirements === undefined
177
+ ? {}
178
+ : {
179
+ createRequirements: normalizeCreateRequirements(resource.createRequirements),
180
+ }),
144
181
  };
145
182
  }
183
+ function isJsonObject(value) {
184
+ return typeof value === "object" && value !== null && !Array.isArray(value);
185
+ }
186
+ function normalizeCreateRequirements(requirements) {
187
+ if (!isJsonObject(requirements)) {
188
+ return requirements;
189
+ }
190
+ const normalized = { ...requirements };
191
+ if (Array.isArray(requirements.required)) {
192
+ normalized.required = normalizeStringSet(requirements.required);
193
+ }
194
+ if (Array.isArray(requirements.oneOf)) {
195
+ normalized.oneOf = requirements.oneOf
196
+ .map((group) => Array.isArray(group) && group.length > 0
197
+ ? [group[0], ...normalizeStringSet(group.slice(1))]
198
+ : group)
199
+ .sort((left, right) => compareCodeUnits(canonicalizeJson(left), canonicalizeJson(right)));
200
+ }
201
+ if (isJsonObject(requirements.parentSupplies)) {
202
+ normalized.parentSupplies = Object.fromEntries(Object.entries(requirements.parentSupplies).map(([parent, byRelation]) => [
203
+ parent,
204
+ isJsonObject(byRelation)
205
+ ? Object.fromEntries(Object.entries(byRelation).map(([relation, members]) => [
206
+ relation,
207
+ Array.isArray(members)
208
+ ? normalizeStringSet(members)
209
+ : members,
210
+ ]))
211
+ : byRelation,
212
+ ]));
213
+ }
214
+ return normalized;
215
+ }
146
216
  function normalizeContract(contract) {
147
217
  const scalars = Object.fromEntries(Object.keys(contract.scalars)
148
218
  .sort(compareCodeUnits)
@@ -169,17 +239,24 @@ function assertValidUnicode(value, path) {
169
239
  if (code >= 0xd800 && code <= 0xdbff) {
170
240
  const next = value.charCodeAt(index + 1);
171
241
  if (!(next >= 0xdc00 && next <= 0xdfff)) {
172
- throw new CanonicalJsonError(`invalid Unicode surrogate at ${path}`);
242
+ throw new AvCanonicalJsonError(`invalid Unicode surrogate at ${path()}`);
173
243
  }
174
244
  index += 1;
175
245
  continue;
176
246
  }
177
247
  if (code >= 0xdc00 && code <= 0xdfff) {
178
- throw new CanonicalJsonError(`invalid Unicode surrogate at ${path}`);
248
+ throw new AvCanonicalJsonError(`invalid Unicode surrogate at ${path()}`);
179
249
  }
180
250
  }
181
251
  }
182
- function canonicalizeValue(value, path) {
252
+ function pathOf(link) {
253
+ const segments = [];
254
+ for (let current = link; current !== undefined; current = current.parent) {
255
+ segments.push(current.segment);
256
+ }
257
+ return `$${segments.reverse().join("")}`;
258
+ }
259
+ function canonicalizePrimitive(value, link) {
183
260
  if (value === null) {
184
261
  return "null";
185
262
  }
@@ -188,47 +265,115 @@ function canonicalizeValue(value, path) {
188
265
  return value ? "true" : "false";
189
266
  case "number": {
190
267
  if (!Number.isFinite(value)) {
191
- throw new CanonicalJsonError(`non-finite number at ${path}`);
268
+ throw new AvCanonicalJsonError(`non-finite number at ${pathOf(link)}`);
192
269
  }
193
270
  const serialized = JSON.stringify(value);
194
271
  if (serialized === undefined) {
195
- throw new CanonicalJsonError(`number at ${path} is not JSON serializable`);
272
+ throw new AvCanonicalJsonError(`number at ${pathOf(link)} is not JSON serializable`);
196
273
  }
197
274
  return serialized;
198
275
  }
199
276
  case "string":
200
- assertValidUnicode(value, path);
277
+ assertValidUnicode(value, () => pathOf(link));
201
278
  return JSON.stringify(value);
202
279
  case "undefined":
203
- throw new CanonicalJsonError(`undefined value at ${path}`);
280
+ throw new AvCanonicalJsonError(`undefined value at ${pathOf(link)}`);
204
281
  case "bigint":
205
282
  case "function":
206
283
  case "symbol":
207
- throw new CanonicalJsonError(`${typeof value} value at ${path} is not JSON serializable`);
208
- case "object":
209
- break;
284
+ throw new AvCanonicalJsonError(`${typeof value} value at ${pathOf(link)} is not JSON serializable`);
285
+ default:
286
+ return undefined;
210
287
  }
211
- if (Array.isArray(value)) {
212
- return `[${Array.prototype.map
213
- .call(value, (entry, index) => canonicalizeValue(entry, `${path}[${index}]`))
214
- .join(",")}]`;
215
- }
216
- const prototype = Object.getPrototypeOf(value);
217
- if (prototype !== Object.prototype && prototype !== null) {
218
- throw new CanonicalJsonError(`non-plain object at ${path} is not canonical JSON`);
288
+ }
289
+ function canonicalizeValue(value, link = undefined) {
290
+ const output = [];
291
+ const stack = [{ kind: "value", value, link }];
292
+ while (stack.length > 0) {
293
+ const task = stack.pop();
294
+ if (task.kind === "text") {
295
+ output.push(task.text);
296
+ continue;
297
+ }
298
+ if (task.kind === "key") {
299
+ assertValidUnicode(task.key, () => `${pathOf(task.link)} key`);
300
+ output.push(`${JSON.stringify(task.key)}:`);
301
+ continue;
302
+ }
303
+ const { value: current, link } = task;
304
+ const primitive = canonicalizePrimitive(current, link);
305
+ if (primitive !== undefined) {
306
+ output.push(primitive);
307
+ continue;
308
+ }
309
+ if (Array.isArray(current)) {
310
+ output.push("[");
311
+ stack.push({ kind: "text", text: "]" });
312
+ for (let index = current.length - 1; index >= 0; index -= 1) {
313
+ if (index in current) {
314
+ stack.push({
315
+ kind: "value",
316
+ value: current[index],
317
+ link: { parent: link, segment: `[${index}]` },
318
+ });
319
+ }
320
+ if (index > 0) {
321
+ stack.push({ kind: "text", text: "," });
322
+ }
323
+ }
324
+ continue;
325
+ }
326
+ const prototype = Object.getPrototypeOf(current);
327
+ if (prototype !== Object.prototype && prototype !== null) {
328
+ throw new AvCanonicalJsonError(`non-plain object at ${pathOf(link)} is not canonical JSON`);
329
+ }
330
+ const record = current;
331
+ const keys = Object.keys(record).sort(compareCodeUnits);
332
+ output.push("{");
333
+ stack.push({ kind: "text", text: "}" });
334
+ for (let index = keys.length - 1; index >= 0; index -= 1) {
335
+ const key = keys[index];
336
+ stack.push({
337
+ kind: "value",
338
+ value: record[key],
339
+ link: { parent: link, segment: `.${key}` },
340
+ });
341
+ stack.push({ kind: "key", key, link });
342
+ if (index > 0) {
343
+ stack.push({ kind: "text", text: "," });
344
+ }
345
+ }
219
346
  }
220
- const record = value;
221
- const entries = Object.keys(record)
347
+ return output.join("");
348
+ }
349
+ export function canonicalizeJson(value) {
350
+ return canonicalizeValue(value);
351
+ }
352
+ export function canonicalizeContractMembers(contract) {
353
+ const record = normalizeContract(contract);
354
+ return Object.keys(record)
222
355
  .sort(compareCodeUnits)
223
356
  .map((key) => {
224
- assertValidUnicode(key, `${path} key`);
225
- return `${JSON.stringify(key)}:${canonicalizeValue(record[key], `${path}.${key}`)}`;
357
+ assertValidUnicode(key, () => "$ key");
358
+ return {
359
+ key,
360
+ text: canonicalizeValue(record[key], {
361
+ parent: undefined,
362
+ segment: `.${key}`,
363
+ }),
364
+ };
226
365
  });
227
- return `{${entries.join(",")}}`;
228
366
  }
229
- export function canonicalizeJson(value) {
230
- return canonicalizeValue(value, "$");
367
+ export function joinCanonicalContractMembers(members) {
368
+ return `{${members.map(({ key, text }) => `${JSON.stringify(key)}:${text}`).join(",")}}`;
231
369
  }
232
370
  export function canonicalizeContract(contract) {
233
- return canonicalizeJson(normalizeContract(contract));
371
+ return joinCanonicalContractMembers(canonicalizeContractMembers(contract));
372
+ }
373
+ const canonicalTexts = new WeakMap();
374
+ export function recordCanonicalContractText(contract, text) {
375
+ canonicalTexts.set(contract, text);
376
+ }
377
+ export function canonicalContractTextOf(contract) {
378
+ return canonicalTexts.get(contract) ?? canonicalizeContract(contract);
234
379
  }
@@ -1,11 +1,16 @@
1
- import type { ClientContract } from "./contract.js";
2
- import type { ContractHash, ContractProtocol } from "./contract-protocol.js";
1
+ import type { AvContractClient } from "./contract.js";
2
+ import type { AvContractHash, AvContractProtocol } from "./contract-protocol.js";
3
3
  /** A ClientContract whose `protocol` does not carry the hash yet. */
4
- export type ClientContractHashInput = Omit<ClientContract, "protocol"> & {
5
- readonly protocol: ContractProtocol;
4
+ export type AvContractClientHashInput = Omit<AvContractClient, "protocol"> & {
5
+ readonly protocol: AvContractProtocol;
6
6
  };
7
- export declare function createClientContractHashInput(contract: ClientContract | ClientContractHashInput): ClientContractHashInput;
7
+ export declare function createClientContractHashInput(contract: AvContractClient | AvContractClientHashInput): AvContractClientHashInput;
8
8
  /** Computes a ClientContract's `sha256:` hash, ignoring `protocol.hash` itself. */
9
- export declare function computeClientContractHash(contract: ClientContract | ClientContractHashInput): Promise<ContractHash>;
9
+ export declare function computeClientContractHash(contract: AvContractClient | AvContractClientHashInput): Promise<AvContractHash>;
10
+ /**
11
+ * The `sha256:` hash of a hash input's canonical text, already written: what
12
+ * {@link computeClientContractHash} answers for the input that text is of.
13
+ */
14
+ export declare function hashCanonicalContractText(canonical: string): Promise<AvContractHash>;
10
15
  /** Whether a ClientContract's `protocol.hash` matches its content. */
11
- export declare function verifyClientContractHash(contract: ClientContract): Promise<boolean>;
16
+ export declare function verifyClientContractHash(contract: AvContractClient): Promise<boolean>;
@@ -10,8 +10,9 @@ function digestHex(digest) {
10
10
  return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
11
11
  }
12
12
  export async function computeClientContractHash(contract) {
13
- const input = createClientContractHashInput(contract);
14
- const canonical = canonicalizeContract(input);
13
+ return hashCanonicalContractText(canonicalizeContract(createClientContractHashInput(contract)));
14
+ }
15
+ export async function hashCanonicalContractText(canonical) {
15
16
  const bytes = new TextEncoder().encode(canonical);
16
17
  const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
17
18
  return `sha256:${digestHex(digest)}`;
@@ -1,5 +1,5 @@
1
1
  /** The request limits a Contract enforces. */
2
- export type ContractLimits = {
2
+ export type AvContractLimits = {
3
3
  /** The largest request body accepted, in bytes. */
4
4
  readonly maxRequestBytes: number;
5
5
  /** How deeply arguments may nest. */
@@ -10,6 +10,8 @@ export type ContractLimits = {
10
10
  readonly maxTransactionOperations: number;
11
11
  /** The most boolean and filter nodes one request may hold. */
12
12
  readonly maxBooleanNodes: number;
13
+ /** How deeply a `json` field's value may nest: its arrays and objects, counted from the value. */
14
+ readonly maxJsonDepth: number;
13
15
  };
14
16
  /** The limits in force when the configuration sets none. */
15
17
  export declare const DEFAULT_CONTRACT_LIMITS: {
@@ -18,4 +20,5 @@ export declare const DEFAULT_CONTRACT_LIMITS: {
18
20
  readonly maxListLimit: 250;
19
21
  readonly maxTransactionOperations: 20;
20
22
  readonly maxBooleanNodes: 50;
23
+ readonly maxJsonDepth: 64;
21
24
  };