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

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 (338) hide show
  1. package/CHANGELOG.md +2068 -1
  2. package/ERRORS.md +142 -0
  3. package/README.md +485 -60
  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 +33 -11
  8. package/dist/adapters/adapter-identifier.d.ts +15 -6
  9. package/dist/adapters/adapter-identifier.js +6 -1
  10. package/dist/adapters/adapter-model.d.ts +18 -9
  11. package/dist/adapters/adapter-operation-executor.interface.d.ts +4 -4
  12. package/dist/adapters/adapter-operation.d.ts +21 -6
  13. package/dist/adapters/adapter-resource.d.ts +23 -9
  14. package/dist/adapters/adapter-sql-statement.d.ts +19 -0
  15. package/dist/adapters/adapter-transaction-executor.interface.d.ts +19 -0
  16. package/dist/adapters/adapter.interface.d.ts +59 -29
  17. package/dist/avz/avz-check.factory.d.ts +83 -0
  18. package/dist/avz/avz-check.factory.js +116 -0
  19. package/dist/avz/avz-check.registry.d.ts +35 -0
  20. package/dist/avz/avz-check.registry.js +9 -0
  21. package/dist/avz/avz.d.ts +203 -0
  22. package/dist/{json → avz}/avz.js +150 -8
  23. package/dist/{json → avz}/index.d.ts +2 -1
  24. package/dist/{json → avz}/json-shape.registry.d.ts +9 -8
  25. package/dist/{json → avz}/zod-issue.translator.d.ts +8 -2
  26. package/dist/{json → avz}/zod-issue.translator.js +7 -0
  27. package/dist/compiler/contract-compiler-diagnostic.d.ts +31 -5
  28. package/dist/compiler/contract-compiler-diagnostic.guidance.d.ts +6 -0
  29. package/dist/compiler/contract-compiler-diagnostic.guidance.js +97 -0
  30. package/dist/compiler/contract.compiler.d.ts +23 -17
  31. package/dist/compiler/contract.compiler.js +753 -443
  32. package/dist/compiler/create-requirements.projector.d.ts +49 -0
  33. package/dist/compiler/create-requirements.projector.js +423 -0
  34. package/dist/compiler/field-validator.compiler.d.ts +47 -0
  35. package/dist/compiler/field-validator.compiler.js +238 -0
  36. package/dist/compiler/managed-elsewhere.projector.d.ts +52 -0
  37. package/dist/compiler/managed-elsewhere.projector.js +179 -0
  38. package/dist/compiler/managed-elsewhere.validator.d.ts +9 -0
  39. package/dist/compiler/managed-elsewhere.validator.js +74 -0
  40. package/dist/config/any-framework-config.d.ts +19 -12
  41. package/dist/config/computed-fields.d.ts +62 -0
  42. package/dist/config/computed-fields.js +73 -0
  43. package/dist/config/config-diagnostic.d.ts +11 -4
  44. package/dist/config/config-diagnostic.guidance.d.ts +6 -0
  45. package/dist/config/config-diagnostic.guidance.js +32 -0
  46. package/dist/config/config-layer.d.ts +15 -6
  47. package/dist/config/config.resolver.d.ts +98 -31
  48. package/dist/config/config.resolver.js +173 -44
  49. package/dist/config/config.validator.d.ts +7 -7
  50. package/dist/config/config.validator.js +792 -69
  51. package/dist/config/define-resource.d.ts +53 -0
  52. package/dist/config/define-resource.js +14 -0
  53. package/dist/config/effective-fields.d.ts +19 -23
  54. package/dist/config/entrypoint.normalizer.d.ts +4 -3
  55. package/dist/config/exact-framework-config.d.ts +325 -0
  56. package/dist/config/field-compute.resolver.d.ts +21 -0
  57. package/dist/config/field-compute.resolver.js +123 -0
  58. package/dist/config/field-pipeline-config.d.ts +47 -0
  59. package/dist/config/forced-operation.validator.d.ts +10 -0
  60. package/dist/config/forced-operation.validator.js +66 -0
  61. package/dist/config/framework-config.d.ts +148 -39
  62. package/dist/config/framework-exemptions-config.d.ts +91 -0
  63. package/dist/config/framework-exemptions-config.js +1 -0
  64. package/dist/config/framework-field-definition.d.ts +78 -0
  65. package/dist/config/framework-field-definition.js +28 -0
  66. package/dist/config/framework-resources-config.d.ts +127 -0
  67. package/dist/config/framework-resources-config.js +1 -0
  68. package/dist/config/pipeline-config.d.ts +56 -20
  69. package/dist/config/pipeline-tree.resolver.d.ts +79 -0
  70. package/dist/config/pipeline-tree.resolver.js +220 -0
  71. package/dist/config/stage-type.eraser.d.ts +65 -0
  72. package/dist/config/stage-type.eraser.js +1 -0
  73. package/dist/contracts/access-route.rules.d.ts +10 -0
  74. package/dist/contracts/access-route.rules.js +45 -0
  75. package/dist/contracts/capability.d.ts +37 -12
  76. package/dist/contracts/capability.js +20 -0
  77. package/dist/contracts/client-contract-structure.validator.d.ts +621 -114
  78. package/dist/contracts/client-contract-structure.validator.js +189 -10
  79. package/dist/contracts/contract-access.d.ts +29 -0
  80. package/dist/contracts/contract-access.js +1 -0
  81. package/dist/contracts/contract-canonicalizer.d.ts +27 -3
  82. package/dist/contracts/contract-canonicalizer.js +168 -29
  83. package/dist/contracts/contract-hash.d.ts +11 -7
  84. package/dist/contracts/contract-hash.js +4 -3
  85. package/dist/contracts/contract-limits.d.ts +4 -1
  86. package/dist/contracts/contract-limits.js +1 -0
  87. package/dist/contracts/contract-lookup.d.ts +5 -5
  88. package/dist/contracts/contract-protocol.d.ts +4 -4
  89. package/dist/contracts/contract.d.ts +51 -17
  90. package/dist/contracts/create-requirements.d.ts +35 -0
  91. package/dist/contracts/create-requirements.js +1 -0
  92. package/dist/contracts/enum-definition.d.ts +1 -1
  93. package/dist/contracts/field-check.d.ts +101 -0
  94. package/dist/contracts/field-check.js +1 -0
  95. package/dist/contracts/field.d.ts +109 -42
  96. package/dist/contracts/identifier.d.ts +4 -4
  97. package/dist/contracts/json-schema.projector.d.ts +11 -0
  98. package/dist/contracts/json-schema.projector.js +78 -0
  99. package/dist/contracts/json-shape.d.ts +12 -1
  100. package/dist/contracts/lifecycle-trait.d.ts +2 -2
  101. package/dist/contracts/resource-operations.d.ts +28 -28
  102. package/dist/contracts/resource.d.ts +15 -9
  103. package/dist/contracts/scalar-value-type.d.ts +17 -15
  104. package/dist/contracts/scalar.d.ts +3 -3
  105. package/dist/contracts/transaction-capability.d.ts +1 -1
  106. package/dist/contracts/wire-scalar-format.d.ts +4 -4
  107. package/dist/framework/access-facade.d.ts +116 -0
  108. package/dist/framework/access-facade.js +120 -0
  109. package/dist/framework/contract-view.d.ts +315 -145
  110. package/dist/framework/framework-construction.error.d.ts +21 -11
  111. package/dist/framework/framework-construction.error.js +20 -4
  112. package/dist/framework/framework.d.ts +71 -26
  113. package/dist/framework/framework.factory.d.ts +8 -6
  114. package/dist/framework/framework.factory.js +150 -38
  115. package/dist/framework/operation-facade.d.ts +192 -54
  116. package/dist/framework/operation-facade.js +10 -1
  117. package/dist/index.d.ts +67 -50
  118. package/dist/index.js +7 -6
  119. package/dist/internal.d.ts +20 -10
  120. package/dist/internal.js +9 -1
  121. package/dist/operations/database-refusal.d.ts +51 -0
  122. package/dist/operations/database-refusal.js +45 -0
  123. package/dist/operations/operation-arguments.d.ts +381 -228
  124. package/dist/operations/operation-call.d.ts +31 -28
  125. package/dist/operations/operation-identity.d.ts +10 -10
  126. package/dist/operations/operation-result.d.ts +25 -23
  127. package/dist/operations/operation-validator-core.d.ts +6 -6
  128. package/dist/operations/operation-validator-core.js +172 -38
  129. package/dist/operations/operation-validator.d.ts +10 -10
  130. package/dist/operations/projection-scope.resolver.d.ts +6 -6
  131. package/dist/operations/structural-limits.walker.d.ts +7 -7
  132. package/dist/operations/structural-limits.walker.js +145 -0
  133. package/dist/operations/validation-code.d.ts +11 -6
  134. package/dist/operations/validation-code.guidance.d.ts +6 -0
  135. package/dist/operations/validation-code.guidance.js +90 -0
  136. package/dist/operations/validation-code.js +4 -0
  137. package/dist/operations/write-scope.resolver.d.ts +10 -6
  138. package/dist/operations/write-scope.resolver.js +2 -0
  139. package/dist/policy/access-provider.interface.d.ts +91 -0
  140. package/dist/policy/access-provider.interface.js +1 -0
  141. package/dist/policy/access-provider.registry.d.ts +17 -0
  142. package/dist/policy/access-provider.registry.js +15 -0
  143. package/dist/policy/access-question.d.ts +20 -0
  144. package/dist/policy/access-question.js +1 -0
  145. package/dist/policy/access-question.registry.d.ts +5 -0
  146. package/dist/policy/access-question.registry.js +19 -0
  147. package/dist/policy/access-question.runtime.d.ts +36 -0
  148. package/dist/policy/access-question.runtime.js +297 -0
  149. package/dist/policy/access-verb.table.d.ts +56 -0
  150. package/dist/policy/access-verb.table.js +21 -0
  151. package/dist/policy/access-verbs.d.ts +46 -0
  152. package/dist/policy/access-verbs.js +1 -0
  153. package/dist/policy/policy-admission.d.ts +20 -0
  154. package/dist/policy/policy-admission.js +409 -0
  155. package/dist/policy/policy-after-update.runtime.d.ts +79 -0
  156. package/dist/policy/policy-after-update.runtime.js +208 -0
  157. package/dist/policy/policy-condition.d.ts +15 -0
  158. package/dist/policy/policy-condition.js +36 -0
  159. package/dist/policy/policy-engine.d.ts +205 -0
  160. package/dist/policy/policy-engine.js +1 -0
  161. package/dist/policy/policy-named-rows.d.ts +18 -0
  162. package/dist/policy/policy-named-rows.js +51 -0
  163. package/dist/policy/policy-nested-write.runtime.d.ts +44 -0
  164. package/dist/policy/policy-nested-write.runtime.js +545 -0
  165. package/dist/policy/policy-read.runtime.d.ts +81 -0
  166. package/dist/policy/policy-read.runtime.js +503 -0
  167. package/dist/policy/policy-reference-write.runtime.d.ts +51 -0
  168. package/dist/policy/policy-reference-write.runtime.js +345 -0
  169. package/dist/policy/policy-request.d.ts +41 -0
  170. package/dist/policy/policy-request.js +63 -0
  171. package/dist/policy/policy-session.d.ts +106 -0
  172. package/dist/policy/policy-session.js +295 -0
  173. package/dist/policy/policy-startup.validator.d.ts +10 -0
  174. package/dist/policy/policy-startup.validator.js +112 -0
  175. package/dist/policy/policy-write.runtime.d.ts +39 -0
  176. package/dist/policy/policy-write.runtime.js +532 -0
  177. package/dist/prebuilt/core-version.generated.d.ts +2 -0
  178. package/dist/prebuilt/core-version.generated.js +1 -0
  179. package/dist/prebuilt/prebuilt-changes.d.ts +29 -0
  180. package/dist/prebuilt/prebuilt-changes.js +65 -0
  181. package/dist/prebuilt/prebuilt-contracts.d.ts +40 -0
  182. package/dist/prebuilt/prebuilt-contracts.emitter.d.ts +16 -0
  183. package/dist/prebuilt/prebuilt-contracts.emitter.js +50 -0
  184. package/dist/prebuilt/prebuilt-contracts.js +1 -0
  185. package/dist/prebuilt/prebuilt-contracts.loader.d.ts +19 -0
  186. package/dist/prebuilt/prebuilt-contracts.loader.js +77 -0
  187. package/dist/prebuilt/prebuilt-inputs.fingerprint.d.ts +16 -0
  188. package/dist/prebuilt/prebuilt-inputs.fingerprint.js +229 -0
  189. package/dist/protocol/absent-route.responder.d.ts +17 -34
  190. package/dist/protocol/absent-route.responder.js +53 -20
  191. package/dist/protocol/contract-document.responder.d.ts +5 -4
  192. package/dist/protocol/contract-document.responder.js +2 -2
  193. package/dist/protocol/http-request.decoder.d.ts +33 -13
  194. package/dist/protocol/http-request.decoder.js +34 -11
  195. package/dist/protocol/http-request.reader.d.ts +9 -6
  196. package/dist/protocol/http-request.reader.js +1 -0
  197. package/dist/protocol/http-response.encoder.d.ts +4 -4
  198. package/dist/protocol/http-response.encoder.js +94 -55
  199. package/dist/protocol/http-status.table.d.ts +2 -2
  200. package/dist/protocol/operation-response.guard.d.ts +2 -2
  201. package/dist/protocol/protocol-headers.d.ts +3 -3
  202. package/dist/protocol/protocol-surface.d.ts +10 -5
  203. package/dist/protocol/protocol-surface.js +7 -0
  204. package/dist/protocol/protocol.binder.d.ts +66 -28
  205. package/dist/protocol/protocol.binder.js +16 -10
  206. package/dist/protocol/protocol.handler.d.ts +34 -14
  207. package/dist/protocol/protocol.handler.js +122 -18
  208. package/dist/protocol/unmounted-route.error.d.ts +4 -4
  209. package/dist/protocol/unmounted-route.error.js +2 -2
  210. package/dist/runtime/caller-data.copier.d.ts +22 -7
  211. package/dist/runtime/caller-data.copier.js +30 -17
  212. package/dist/runtime/caller-data.door.d.ts +18 -11
  213. package/dist/runtime/caller-data.door.js +6 -4
  214. package/dist/runtime/computed-read-runtime.d.ts +12 -6
  215. package/dist/runtime/computed-read-runtime.js +271 -71
  216. package/dist/runtime/computed-value.error.d.ts +7 -6
  217. package/dist/runtime/computed-value.error.js +5 -4
  218. package/dist/runtime/context-executor.runtime.d.ts +60 -0
  219. package/dist/runtime/context-executor.runtime.js +277 -0
  220. package/dist/runtime/core-class.brand.d.ts +10 -0
  221. package/dist/runtime/core-class.brand.js +21 -0
  222. package/dist/runtime/create-requirement.d.ts +34 -79
  223. package/dist/runtime/create-requirement.js +205 -74
  224. package/dist/runtime/decimal.d.ts +16 -2
  225. package/dist/runtime/decimal.js +22 -1
  226. package/dist/runtime/diagnostics-reporter.d.ts +91 -30
  227. package/dist/runtime/diagnostics-reporter.js +37 -1
  228. package/dist/runtime/error-boundary.d.ts +73 -9
  229. package/dist/runtime/error-boundary.js +57 -19
  230. package/dist/runtime/error-guidance.d.ts +12 -0
  231. package/dist/runtime/error-guidance.js +1 -0
  232. package/dist/runtime/error.catalog.d.ts +49 -0
  233. package/dist/runtime/error.catalog.js +49 -0
  234. package/dist/runtime/execution-options.d.ts +18 -2
  235. package/dist/runtime/field-check.evaluator.d.ts +27 -0
  236. package/dist/runtime/field-check.evaluator.js +521 -0
  237. package/dist/runtime/field-compute.d.ts +65 -0
  238. package/dist/runtime/field-compute.js +1 -0
  239. package/dist/runtime/field-stage.plan.d.ts +15 -0
  240. package/dist/runtime/field-stage.plan.js +27 -0
  241. package/dist/runtime/field-stage.runtime.d.ts +37 -0
  242. package/dist/runtime/field-stage.runtime.js +147 -0
  243. package/dist/runtime/field-validator.runtime.d.ts +29 -0
  244. package/dist/runtime/field-validator.runtime.js +126 -0
  245. package/dist/runtime/framework-error-class.table.d.ts +33 -0
  246. package/dist/runtime/framework-error-class.table.js +25 -0
  247. package/dist/runtime/framework-error.d.ts +68 -8
  248. package/dist/runtime/framework-error.factory.d.ts +4 -0
  249. package/dist/runtime/framework-error.factory.js +15 -0
  250. package/dist/runtime/framework-error.js +32 -2
  251. package/dist/runtime/framework-warning.emitter.d.ts +6 -0
  252. package/dist/runtime/framework-warning.emitter.js +8 -0
  253. package/dist/runtime/interceptor-next.error.d.ts +5 -0
  254. package/dist/runtime/interceptor-next.error.js +6 -0
  255. package/dist/runtime/json-value.d.ts +11 -6
  256. package/dist/runtime/level-lifecycle.runtime.d.ts +26 -0
  257. package/dist/runtime/level-lifecycle.runtime.js +40 -0
  258. package/dist/runtime/misuse-reporter.d.ts +12 -12
  259. package/dist/runtime/mutation-planner.d.ts +20 -16
  260. package/dist/runtime/mutation-planner.js +48 -35
  261. package/dist/runtime/nested-write.walker.d.ts +32 -0
  262. package/dist/runtime/nested-write.walker.js +73 -0
  263. package/dist/runtime/operation-code.guidance.d.ts +6 -0
  264. package/dist/runtime/operation-code.guidance.js +167 -0
  265. package/dist/runtime/operation-request.d.ts +45 -11
  266. package/dist/runtime/operation-response.d.ts +17 -17
  267. package/dist/runtime/operation-runtime.d.ts +133 -27
  268. package/dist/runtime/operation-runtime.js +576 -84
  269. package/dist/runtime/outcome-mapper.d.ts +17 -5
  270. package/dist/runtime/outcome-mapper.js +23 -6
  271. package/dist/runtime/output-validator.d.ts +11 -11
  272. package/dist/runtime/output-validator.js +3 -3
  273. package/dist/runtime/owned-execution.registry.d.ts +16 -6
  274. package/dist/runtime/pipe-output.validator.d.ts +43 -18
  275. package/dist/runtime/pipe-output.validator.js +15 -13
  276. package/dist/runtime/pipeline.d.ts +221 -34
  277. package/dist/runtime/readonly-value.d.ts +47 -0
  278. package/dist/runtime/readonly-value.js +189 -0
  279. package/dist/runtime/referential-action.d.ts +1 -1
  280. package/dist/runtime/request-envelope.validator.d.ts +3 -3
  281. package/dist/runtime/request-envelope.validator.js +12 -7
  282. package/dist/runtime/request-id.d.ts +1 -1
  283. package/dist/runtime/request-state.d.ts +14 -0
  284. package/dist/runtime/request-state.js +1 -0
  285. package/dist/runtime/request-state.resolver.d.ts +12 -0
  286. package/dist/runtime/request-state.resolver.js +20 -0
  287. package/dist/runtime/scalar-runtime.d.ts +33 -25
  288. package/dist/runtime/scalar-runtime.js +105 -38
  289. package/dist/runtime/unsent-value.normalizer.d.ts +15 -0
  290. package/dist/runtime/unsent-value.normalizer.js +128 -0
  291. package/dist/transactions/deferred-operation.d.ts +35 -20
  292. package/dist/transactions/operation-arguments-encoder.d.ts +3 -3
  293. package/dist/transactions/operation-arguments-encoder.js +8 -3
  294. package/dist/transactions/operation-fingerprint.js +2 -2
  295. package/dist/transactions/sql-statement.d.ts +85 -0
  296. package/dist/transactions/sql-statement.js +47 -0
  297. package/dist/transactions/sql-statement.validator.d.ts +44 -0
  298. package/dist/transactions/sql-statement.validator.js +167 -0
  299. package/dist/transactions/transaction-envelope.d.ts +9 -8
  300. package/dist/transactions/transaction-integrity-error.d.ts +6 -6
  301. package/dist/transactions/transaction-integrity-error.js +7 -2
  302. package/dist/transactions/transaction-plan-assembler.d.ts +30 -13
  303. package/dist/transactions/transaction-plan-assembler.js +184 -32
  304. package/dist/transactions/transaction-plan-validator.d.ts +50 -25
  305. package/dist/transactions/transaction-plan-validator.js +96 -38
  306. package/dist/transactions/transaction-reference-collector.d.ts +5 -5
  307. package/dist/transactions/transaction-reference-resolver.d.ts +21 -3
  308. package/dist/transactions/transaction-reference-resolver.js +26 -14
  309. package/dist/transactions/transaction-reference-validator.d.ts +74 -4
  310. package/dist/transactions/transaction-reference-validator.js +129 -71
  311. package/dist/transactions/transaction-reference.d.ts +14 -14
  312. package/dist/transactions/transaction-reference.js +5 -0
  313. package/dist/transactions/transaction-request.d.ts +22 -9
  314. package/dist/transactions/transaction-response.d.ts +6 -6
  315. package/dist/transactions/transaction-runtime.d.ts +12 -8
  316. package/dist/transactions/transaction-runtime.js +218 -50
  317. package/dist/transactions/transaction-settlement.runtime.d.ts +52 -0
  318. package/dist/transactions/transaction-settlement.runtime.js +120 -0
  319. package/dist/transactions/transaction-slot-path-error.d.ts +4 -4
  320. package/dist/transactions/transaction-slot-path-error.js +4 -2
  321. package/dist/transactions/transaction-slot-writer.js +2 -2
  322. package/dist/transactions/transaction-stage.runtime.d.ts +83 -0
  323. package/dist/transactions/transaction-stage.runtime.js +126 -0
  324. package/package.json +11 -9
  325. package/src/runtime/field-check.evaluator.ts +720 -0
  326. package/dist/config/define-fields.d.ts +0 -42
  327. package/dist/config/define-fields.js +0 -3
  328. package/dist/config/framework-behaviors-config.d.ts +0 -30
  329. package/dist/config/framework-fields-config.d.ts +0 -42
  330. package/dist/config/framework-restrictions-config.d.ts +0 -142
  331. package/dist/json/avz.d.ts +0 -96
  332. package/dist/runtime/field-behavior.d.ts +0 -48
  333. /package/dist/{config/framework-behaviors-config.js → adapters/adapter-sql-statement.js} +0 -0
  334. /package/dist/{config/framework-fields-config.js → adapters/adapter-transaction-executor.interface.js} +0 -0
  335. /package/dist/{json → avz}/index.js +0 -0
  336. /package/dist/{json → avz}/json-shape.registry.js +0 -0
  337. /package/dist/config/{framework-restrictions-config.js → exact-framework-config.js} +0 -0
  338. /package/dist/{runtime/field-behavior.js → config/field-pipeline-config.js} +0 -0
@@ -1,12 +1,15 @@
1
- import { PROJECTION_SCOPES, WRITE_SCOPES, } from "../contracts/capability.js";
1
+ import { bindShapedField, jsonShapeOf } from "../avz/json-shape.registry.js";
2
+ import { FILTER_MODES, PROJECTION_SCOPES, WRITE_SCOPES, } from "../contracts/capability.js";
2
3
  import { validateClientContractStructure } from "../contracts/client-contract-structure.validator.js";
3
- import { canonicalizeContract } from "../contracts/contract-canonicalizer.js";
4
- import { computeClientContractHash, } from "../contracts/contract-hash.js";
4
+ import { canonicalizeContract, canonicalizeContractMembers, canonicalizeJson, joinCanonicalContractMembers, recordCanonicalContractText, } from "../contracts/contract-canonicalizer.js";
5
+ import { hashCanonicalContractText, } from "../contracts/contract-hash.js";
5
6
  import { DEFAULT_CONTRACT_LIMITS, } from "../contracts/contract-limits.js";
6
7
  import { AVENTARA_PROTOCOL_VERSION } from "../contracts/contract-protocol.js";
7
- import { advertisedDirectives, advertisedDirectivesAt, advertisedReducers, advertisedReducersAt, holdsAt, holdsInAnyScope, } from "../contracts/field.js";
8
+ import { advertisedDirectives, advertisedReducers, holdsInAnyScope, } from "../contracts/field.js";
8
9
  import { isOperationVariantAvailable, } from "../contracts/resource-operations.js";
9
- import { bindShapedField, jsonShapeOf } from "../json/json-shape.registry.js";
10
+ import { checkCreateRequirements, checkHiddenRequirements, projectCreateRequirements, } from "./create-requirements.projector.js";
11
+ import { applyValidators, bindFieldValidators, checkLayerValidators, compileValidatorTable, } from "./field-validator.compiler.js";
12
+ import { explainManagedRefusals, hideManagedElsewhere, managedElsewhereOf, managedWriteReason, markCoreResources, pruneManagedEnums, withholdManagedWrites, } from "./managed-elsewhere.projector.js";
10
13
  const UNIQUE_VARIANTS = [
11
14
  ["find", "unique"],
12
15
  ["update", "unique"],
@@ -39,6 +42,12 @@ function normalizeAdapterScalarCapabilities(capabilities) {
39
42
  ...(capabilities.filter !== undefined
40
43
  ? { filter: [...capabilities.filter] }
41
44
  : {}),
45
+ ...(capabilities.filterModes !== undefined
46
+ ? { filterModes: [...capabilities.filterModes] }
47
+ : {}),
48
+ ...(capabilities.pathFilterModes !== undefined
49
+ ? { pathFilterModes: [...capabilities.pathFilterModes] }
50
+ : {}),
42
51
  ...(capabilities.order !== undefined ? { order: capabilities.order } : {}),
43
52
  ...(capabilities.orderNulls !== undefined
44
53
  ? { orderNulls: capabilities.orderNulls }
@@ -69,8 +78,65 @@ function cloneAdapterField(field) {
69
78
  cardinality: field.cardinality,
70
79
  nullable: field.nullable,
71
80
  capabilities: cloneValue(field.capabilities),
81
+ ...(typeof field.inverse === "string" ? { inverse: field.inverse } : {}),
82
+ ...(Array.isArray(field.referenceFields) && field.referenceFields.length > 0
83
+ ? { referenceFields: [...field.referenceFields] }
84
+ : {}),
85
+ ...(Array.isArray(field.referenceFields) &&
86
+ Array.isArray(field.references) &&
87
+ field.references.length > 0 &&
88
+ field.references.length === field.referenceFields.length
89
+ ? { references: [...field.references] }
90
+ : {}),
72
91
  };
73
92
  }
93
+ function pruneHiddenPairings(projection, references) {
94
+ const resources = projection.resources;
95
+ for (const [name, resource] of Object.entries(resources)) {
96
+ for (const field of Object.values(resource.fields)) {
97
+ if (field.kind !== "relation") {
98
+ continue;
99
+ }
100
+ if (Array.isArray(field.references) &&
101
+ (references === "none" ||
102
+ !wholePairing(resource.fields, Object.hasOwn(resources, String(field.target))
103
+ ? resources[String(field.target)]?.fields
104
+ : undefined, field.referenceFields, field.references))) {
105
+ delete field.references;
106
+ }
107
+ if (Array.isArray(field.referenceFields)) {
108
+ const shown = field.referenceFields.filter((reference) => typeof reference === "string" &&
109
+ Object.hasOwn(resource.fields, reference) &&
110
+ resource.fields[reference]?.kind === "scalar");
111
+ if (shown.length > 0) {
112
+ field.referenceFields = shown;
113
+ }
114
+ else {
115
+ delete field.referenceFields;
116
+ }
117
+ }
118
+ if (typeof field.inverse !== "string") {
119
+ continue;
120
+ }
121
+ const other = Object.hasOwn(resources, String(field.target))
122
+ ? resources[String(field.target)]?.fields[field.inverse]
123
+ : undefined;
124
+ if (other?.kind !== "relation" || other.target !== name) {
125
+ delete field.inverse;
126
+ }
127
+ }
128
+ }
129
+ }
130
+ function wholePairing(fields, targetFields, referenceFields, references) {
131
+ const shownIn = (within, name) => typeof name === "string" &&
132
+ within !== undefined &&
133
+ Object.hasOwn(within, name) &&
134
+ within[name]?.kind === "scalar";
135
+ return (Array.isArray(referenceFields) &&
136
+ referenceFields.length === references.length &&
137
+ referenceFields.every((name) => shownIn(fields, name)) &&
138
+ references.every((name) => shownIn(targetFields, name)));
139
+ }
74
140
  function cloneOperations(operations) {
75
141
  return cloneValue(operations);
76
142
  }
@@ -84,8 +150,40 @@ function cloneAdapterResource(resource) {
84
150
  operations: cloneOperations(resource.operations),
85
151
  };
86
152
  }
153
+ function canonicalModes(modes) {
154
+ const stated = Array.isArray(modes) ? modes : [];
155
+ const known = FILTER_MODES.filter((mode) => mode === "default" || stated.includes(mode));
156
+ const unknown = stated.filter((mode) => !FILTER_MODES.includes(mode));
157
+ return [...known, ...unknown];
158
+ }
159
+ function qualifyFilterModes(projection) {
160
+ for (const resource of Object.values(projection.resources)) {
161
+ for (const field of Object.values(resource.fields)) {
162
+ if (field.kind !== "scalar") {
163
+ continue;
164
+ }
165
+ const capabilities = field.capabilities;
166
+ const filter = Array.isArray(capabilities.filter)
167
+ ? capabilities.filter
168
+ : [];
169
+ if (!filter.includes("mode")) {
170
+ delete capabilities.filterModes;
171
+ delete capabilities.pathFilterModes;
172
+ continue;
173
+ }
174
+ const stated = capabilities.filterModes;
175
+ capabilities.filterModes = canonicalModes(stated);
176
+ if (filter.includes("path")) {
177
+ capabilities.pathFilterModes = canonicalModes(capabilities.pathFilterModes ?? stated);
178
+ }
179
+ else {
180
+ delete capabilities.pathFilterModes;
181
+ }
182
+ }
183
+ }
184
+ }
87
185
  function createBaseline(model) {
88
- return {
186
+ const baseline = {
89
187
  scalars: Object.fromEntries(Object.entries(model.scalars).map(([key, value]) => [
90
188
  key,
91
189
  cloneValue(value),
@@ -104,9 +202,11 @@ function createBaseline(model) {
104
202
  ? {}
105
203
  : { referenceWrites: model.referenceWrites }),
106
204
  };
205
+ qualifyFilterModes(baseline);
206
+ return baseline;
107
207
  }
108
- function scalarFieldPath(scope, resource, field) {
109
- return `${scope}.resources.${resource}.fields.${field}`;
208
+ function definitionPath(resource, field) {
209
+ return `root.resources.${resource}.fields.${field}.definition`;
110
210
  }
111
211
  function structuralValueMatches(field, config, key) {
112
212
  if (config[key] === undefined) {
@@ -114,6 +214,13 @@ function structuralValueMatches(field, config, key) {
114
214
  }
115
215
  return JSON.stringify(field[key]) === JSON.stringify(config[key]);
116
216
  }
217
+ function configuredMember(capabilities, key) {
218
+ const value = capabilities?.[key];
219
+ if (value === undefined) {
220
+ return {};
221
+ }
222
+ return { [key]: Array.isArray(value) ? [...value] : cloneValue(value) };
223
+ }
117
224
  function addConfiguredScalarField(config, path, diagnostics) {
118
225
  if (config.json !== undefined) {
119
226
  refuseJsonShape(path, "is not a field of the adapter model", diagnostics);
@@ -128,7 +235,7 @@ function addConfiguredScalarField(config, path, diagnostics) {
128
235
  if (configuredLifecycle === undefined ||
129
236
  !configuredLifecycle.includes("VIRTUAL") ||
130
237
  configuredLifecycle.some((trait) => trait !== "VIRTUAL")) {
131
- diagnostics.push(diagnostic("COMPILER_INVALID_LIFECYCLE", `${path}.lifecycle`, "framework-added scalar field must declare only VIRTUAL lifecycle; generated lifecycle comes from adapters and computed lifecycle comes from behaviors"));
238
+ diagnostics.push(diagnostic("COMPILER_INVALID_LIFECYCLE", `${path}.lifecycle`, "framework-added scalar field must declare only VIRTUAL lifecycle; generated lifecycle comes from adapters and computed lifecycle comes from compute"));
132
239
  return undefined;
133
240
  }
134
241
  return {
@@ -140,89 +247,14 @@ function addConfiguredScalarField(config, path, diagnostics) {
140
247
  lifecycle: [...(config.lifecycle ?? [])],
141
248
  capabilities: {
142
249
  select: config.capabilities?.select ?? true,
143
- ...(config.capabilities?.filter !== undefined
144
- ? { filter: [...config.capabilities.filter] }
145
- : {}),
146
- ...(config.capabilities?.order !== undefined
147
- ? { order: config.capabilities.order }
148
- : {}),
149
- ...(config.capabilities?.orderNulls !== undefined
150
- ? { orderNulls: config.capabilities.orderNulls }
151
- : {}),
152
- ...(config.capabilities?.create !== undefined
153
- ? { create: [...config.capabilities.create] }
154
- : {}),
155
- ...(config.capabilities?.update !== undefined
156
- ? { update: [...config.capabilities.update] }
157
- : {}),
250
+ ...configuredMember(config.capabilities, "filter"),
251
+ ...configuredMember(config.capabilities, "order"),
252
+ ...configuredMember(config.capabilities, "orderNulls"),
253
+ ...configuredMember(config.capabilities, "create"),
254
+ ...configuredMember(config.capabilities, "update"),
158
255
  },
159
256
  };
160
257
  }
161
- export function capabilityAddsBaseline(fieldKind, baseline, key, configured) {
162
- const axis = capabilityAxisFor(fieldKind, key);
163
- if (axis === undefined) {
164
- return false;
165
- }
166
- const baselineValue = baseline[key];
167
- switch (axis.kind) {
168
- case "limit":
169
- return baselineValue !== undefined && configured !== baselineValue;
170
- case "flag":
171
- return configured === true && baselineValue !== true;
172
- case "tokens": {
173
- if (!isTokenList(configured)) {
174
- return false;
175
- }
176
- const advertised = isTokenList(baselineValue) ? baselineValue : [];
177
- return configured.some((token) => !advertised.includes(token));
178
- }
179
- case "scopes": {
180
- const claimed = configured === true
181
- ? axis.axis
182
- : isTokenList(configured)
183
- ? configured
184
- : undefined;
185
- return (claimed?.some((position) => axis.axis.includes(position) &&
186
- !holdsAt(baselineValue, position)) === true);
187
- }
188
- case "scopedTokens": {
189
- const claim = scopedTokenClaim(axis, configured);
190
- if (claim === undefined) {
191
- return false;
192
- }
193
- return claim.positions.some((position) => {
194
- if (!axis.axis.includes(position)) {
195
- return false;
196
- }
197
- const advertised = advertisedTokensAt(axis.tokenKey, baselineValue, position);
198
- return (advertised === undefined ||
199
- claim.tokens.some((token) => !advertised.includes(token)));
200
- });
201
- }
202
- }
203
- }
204
- function scopedTokenClaim(axis, configured) {
205
- if (isTokenList(configured)) {
206
- return { tokens: configured, positions: axis.axis };
207
- }
208
- if (isRecord(configured) &&
209
- isTokenList(configured[axis.tokenKey]) &&
210
- isTokenList(configured.scopes)) {
211
- return {
212
- tokens: configured[axis.tokenKey],
213
- positions: configured.scopes,
214
- };
215
- }
216
- return undefined;
217
- }
218
- function advertisedTokensAt(tokenKey, baselineValue, position) {
219
- if (!isTokenList(baselineValue) && !isRecord(baselineValue)) {
220
- return undefined;
221
- }
222
- return tokenKey === "directives"
223
- ? advertisedDirectivesAt(baselineValue, position)
224
- : advertisedReducersAt(baselineValue, position);
225
- }
226
258
  function fieldLabel(path) {
227
259
  const [, resource, , field] = path.split(".").slice(1);
228
260
  return `${resource}.${field}`;
@@ -238,7 +270,7 @@ function applyJsonShape(field, baselineField, shape, path, diagnostics) {
238
270
  }
239
271
  const runtime = jsonShapeOf(shape);
240
272
  if (runtime === undefined) {
241
- diagnostics.push(diagnostic("COMPILER_INVALID_JSON_SHAPE", `${path}.json`, `field "${fieldLabel(path)}": "json" must be a shape built with AvZ from "@aventara/core/json"; a Zod schema or any other value is not accepted`));
273
+ diagnostics.push(diagnostic("COMPILER_INVALID_JSON_SHAPE", `${path}.json`, `field "${fieldLabel(path)}": "json" must be a shape built with AvZ from "@aventara/core/avz"; a Zod schema or any other value is not accepted`));
242
274
  return;
243
275
  }
244
276
  if (runtime.validate(null).length === 0 ||
@@ -255,7 +287,7 @@ function applyExistingScalarConfig(field, baselineField, config, path, diagnosti
255
287
  }
256
288
  }
257
289
  if (config.format !== undefined) {
258
- field.format = config.format;
290
+ diagnostics.push(diagnostic("COMPILER_STRUCTURAL_FIELD_OVERRIDE", `${path}.format`, "a discovered field's format is the adapter's and cannot be set; format is for configured (VIRTUAL) fields"));
259
291
  }
260
292
  if (config.json !== undefined) {
261
293
  applyJsonShape(field, baselineField, config.json, path, diagnostics);
@@ -264,95 +296,39 @@ function applyExistingScalarConfig(field, baselineField, config, path, diagnosti
264
296
  diagnostics.push(diagnostic("COMPILER_INVALID_LIFECYCLE", `${path}.lifecycle`, "existing adapter field lifecycle cannot be redefined by framework field configuration"));
265
297
  }
266
298
  if (config.capabilities !== undefined) {
267
- const current = field.capabilities;
268
- const baseline = baselineField.capabilities;
269
- for (const [key, value] of Object.entries(config.capabilities)) {
270
- if (capabilityAddsBaseline(baselineField.kind, baseline, key, value)) {
271
- diagnostics.push(diagnostic("COMPILER_UNAVAILABLE_CAPABILITY", `${path}.capabilities.${key}`, `capability "${key}" is not available in the canonical baseline`));
272
- continue;
273
- }
274
- current[key] = cloneValue(value);
275
- }
276
- }
277
- }
278
- function applyFrameworkOwnedScalarConfig(field, config, path, diagnostics) {
279
- for (const key of ["type", "nullable", "list"]) {
280
- if (config[key] !== undefined) {
281
- field[key] = cloneValue(config[key]);
282
- }
283
- }
284
- if (config.json !== undefined) {
285
- refuseJsonShape(path, "is not a field of the adapter model", diagnostics);
286
- }
287
- if (config.format !== undefined) {
288
- field.format = config.format;
289
- }
290
- if (config.lifecycle !== undefined) {
291
- const configuredLifecycle = config.lifecycle;
292
- if (!configuredLifecycle.includes("VIRTUAL") ||
293
- configuredLifecycle.some((trait) => trait !== "VIRTUAL")) {
294
- diagnostics.push(diagnostic("COMPILER_INVALID_LIFECYCLE", `${path}.lifecycle`, "framework-owned scalar field may declare only VIRTUAL lifecycle; computed lifecycle comes from behaviors"));
295
- }
296
- else {
297
- field.lifecycle = [...configuredLifecycle];
298
- }
299
- }
300
- if (config.capabilities !== undefined) {
301
- const current = field.capabilities;
302
- for (const [key, value] of Object.entries(config.capabilities)) {
303
- current[key] = cloneValue(value);
304
- }
299
+ diagnostics.push(diagnostic("COMPILER_STRUCTURAL_FIELD_OVERRIDE", `${path}.capabilities`, "a discovered field's capabilities are the adapter's and cannot be set; narrow what it exposes with exemptions"));
305
300
  }
306
301
  }
307
- function applyFieldConfig(model, projection, scope, config, diagnostics, allowRestrictedMissing = false) {
308
- if (config === undefined) {
309
- return;
310
- }
302
+ function applyFieldDefinitions(model, projection, definitions, diagnostics) {
311
303
  const providerBaseline = createBaseline(model);
312
- for (const [resourceName, resourceConfig] of Object.entries(config)) {
313
- if (resourceConfig === undefined) {
314
- continue;
315
- }
304
+ for (const [resourceName, resourceDefinitions] of Object.entries(definitions)) {
316
305
  const resource = projection.resources[resourceName];
317
306
  const providerResource = providerBaseline.resources[resourceName];
318
307
  if (resource === undefined || providerResource === undefined) {
319
- if (allowRestrictedMissing && providerResource !== undefined) {
320
- continue;
321
- }
322
- diagnostics.push(diagnostic("COMPILER_UNKNOWN_RESOURCE", `${scope}.resources.${resourceName}`, `resource "${resourceName}" is not present in the canonical adapter model`));
323
308
  continue;
324
309
  }
325
- for (const [fieldName, rawConfig] of Object.entries(resourceConfig)) {
326
- const fieldConfig = rawConfig;
327
- const path = scalarFieldPath(scope, resourceName, fieldName);
310
+ for (const [fieldName, definition] of Object.entries(resourceDefinitions)) {
311
+ const path = definitionPath(resourceName, fieldName);
328
312
  const existing = resource.fields[fieldName];
329
313
  const providerField = providerResource.fields[fieldName];
330
314
  if (existing === undefined) {
331
- if (allowRestrictedMissing && providerField !== undefined) {
332
- continue;
333
- }
334
- const added = addConfiguredScalarField(fieldConfig, path, diagnostics);
315
+ const added = addConfiguredScalarField(definition, path, diagnostics);
335
316
  if (added !== undefined) {
336
317
  resource.fields[fieldName] = added;
337
318
  }
338
319
  continue;
339
320
  }
340
321
  if (existing.kind === "relation") {
341
- diagnostics.push(diagnostic("COMPILER_RELATION_FIELD_CONFIG_UNSUPPORTED", path, "framework field augmentation applies only to scalar fields"));
322
+ diagnostics.push(diagnostic("COMPILER_RELATION_FIELD_CONFIG_UNSUPPORTED", path, "a relation field takes no definition"));
342
323
  continue;
343
324
  }
344
325
  if (providerField?.kind === "scalar") {
345
- applyExistingScalarConfig(existing, providerField, fieldConfig, path, diagnostics);
346
- continue;
326
+ applyExistingScalarConfig(existing, providerField, definition, path, diagnostics);
347
327
  }
348
- applyFrameworkOwnedScalarConfig(existing, fieldConfig, path, diagnostics);
349
328
  }
350
329
  }
351
330
  }
352
- function isActiveBehaviorStage(value) {
353
- return value !== undefined && value !== false;
354
- }
355
- function readBehaviorDependencies(value) {
331
+ function readComputeDependencies(value) {
356
332
  if (!isRecord(value) || typeof value.compute !== "function") {
357
333
  return undefined;
358
334
  }
@@ -360,60 +336,135 @@ function readBehaviorDependencies(value) {
360
336
  ? value.dependsOn.filter((entry) => typeof entry === "string")
361
337
  : undefined;
362
338
  }
363
- function applyFieldBehavior(resource, fieldName, behavior, path, diagnostics, allowRestrictedMissing = false) {
364
- const field = resource.fields[fieldName];
365
- if (field === undefined || field.kind !== "scalar") {
366
- if (allowRestrictedMissing && field === undefined) {
367
- return;
339
+ function applyCompute(projection, compute) {
340
+ for (const [resourceName, fields] of Object.entries(compute)) {
341
+ const resource = projection.resources[resourceName];
342
+ if (resource === undefined) {
343
+ continue;
344
+ }
345
+ for (const [fieldName, fieldCompute] of Object.entries(fields)) {
346
+ const field = resource.fields[fieldName];
347
+ if (field?.kind !== "scalar") {
348
+ continue;
349
+ }
350
+ const lifecycle = field.lifecycle.filter((trait) => !COMPUTED_TRAITS.has(trait));
351
+ if (fieldCompute.create !== undefined) {
352
+ lifecycle.push("COMPUTED_ON_CREATE");
353
+ }
354
+ if (fieldCompute.update !== undefined) {
355
+ lifecycle.push("COMPUTED_ON_UPDATE");
356
+ }
357
+ if (fieldCompute.read !== undefined) {
358
+ lifecycle.push("COMPUTED_ON_READ");
359
+ }
360
+ field.lifecycle = lifecycle;
368
361
  }
369
- diagnostics.push(diagnostic("COMPILER_UNKNOWN_FIELD", path, `behavior field "${fieldName}" must resolve to a scalar field`));
370
- return;
371
- }
372
- const lifecycle = field.lifecycle.filter((trait) => !COMPUTED_TRAITS.has(trait));
373
- if (isActiveBehaviorStage(behavior.create)) {
374
- lifecycle.push("COMPUTED_ON_CREATE");
375
362
  }
376
- if (isActiveBehaviorStage(behavior.update)) {
377
- lifecycle.push("COMPUTED_ON_UPDATE");
363
+ }
364
+ function storedScalar(model, resource, field) {
365
+ const fields = Object.hasOwn(model.resources, resource)
366
+ ? model.resources[resource]?.fields
367
+ : undefined;
368
+ return (fields !== undefined &&
369
+ Object.hasOwn(fields, field) &&
370
+ fields[field]?.kind === "scalar");
371
+ }
372
+ function checkFieldCompute(model, resourceName, fieldName, field, compute, path, diagnostics) {
373
+ if (!isRecord(compute)) {
374
+ return;
378
375
  }
379
- if (isActiveBehaviorStage(behavior.read)) {
380
- lifecycle.push("COMPUTED_ON_READ");
376
+ if (field?.kind === "relation") {
377
+ diagnostics.push(diagnostic("COMPILER_RELATION_FIELD_CONFIG_UNSUPPORTED", path, `relation field "${fieldName}" takes write guards only; compute derives a scalar field's value`));
378
+ return;
381
379
  }
382
- field.lifecycle = lifecycle;
383
- for (const dependency of readBehaviorDependencies(behavior.read) ?? []) {
384
- const dependencyField = resource.fields[dependency];
385
- if (dependencyField === undefined || dependencyField.kind !== "scalar") {
386
- diagnostics.push(diagnostic("COMPILER_INVALID_BEHAVIOR_DEPENDENCY", `${path}.read.dependsOn`, `read behavior dependency "${dependency}" must reference a scalar field in the same Resource`));
380
+ for (const dependency of readComputeDependencies(compute.read) ?? []) {
381
+ if (!storedScalar(model, resourceName, dependency)) {
382
+ diagnostics.push(diagnostic("COMPILER_INVALID_COMPUTE_DEPENDENCY", `${path}.read.dependsOn`, `compute.read dependency "${dependency}" must name a scalar field the adapter stores on resource "${resourceName}"`));
387
383
  }
388
384
  }
389
385
  }
390
- function applyBehaviors(projection, scope, config, diagnostics, allowRestrictedMissing = false) {
391
- if (config === undefined) {
386
+ function checkResourceTree(model, baseline, scope, tree, definitions, managed, diagnostics) {
387
+ if (!isRecord(tree)) {
392
388
  return;
393
389
  }
394
- for (const [resourceName, rawResourceConfig] of Object.entries(config)) {
395
- if (rawResourceConfig === undefined) {
390
+ for (const [resourceName, entry] of Object.entries(tree)) {
391
+ const resourcePath = `${scope}.resources.${resourceName}`;
392
+ const resource = baseline.resources[resourceName];
393
+ if (resource === undefined) {
394
+ diagnostics.push(diagnostic("COMPILER_UNKNOWN_RESOURCE", resourcePath, `resource "${resourceName}" is not present in the canonical adapter model`));
396
395
  continue;
397
396
  }
398
- const resource = projection.resources[resourceName];
399
- if (resource === undefined) {
400
- if (allowRestrictedMissing) {
401
- continue;
397
+ if ((scope === "client" || scope === "agent") &&
398
+ managed.has(resourceName)) {
399
+ diagnostics.push(diagnostic("COMPILER_UNKNOWN_RESOURCE", resourcePath, `resource "${resourceName}" is managed elsewhere (resources.${resourceName}.managedElsewhere), so the ${scope} layer never has it: nothing configured for it here runs`));
400
+ continue;
401
+ }
402
+ if (!isRecord(entry)) {
403
+ continue;
404
+ }
405
+ if (isRecord(entry.operations)) {
406
+ for (const [family, level] of Object.entries(entry.operations)) {
407
+ const familyPath = `${resourcePath}.operations.${family}`;
408
+ const managedReason = managedWriteReason(managed, resourceName, family);
409
+ if (managedReason !== undefined) {
410
+ diagnostics.push(diagnostic("COMPILER_UNAVAILABLE_OPERATION", familyPath, managedReason));
411
+ continue;
412
+ }
413
+ const variants = resource.operations[family];
414
+ if (!isRecord(variants)) {
415
+ diagnostics.push(diagnostic("COMPILER_UNAVAILABLE_OPERATION", familyPath, `operation family "${family}" is not available for resource "${resourceName}"`));
416
+ continue;
417
+ }
418
+ if (!isRecord(level)) {
419
+ continue;
420
+ }
421
+ for (const variant of Object.keys(level)) {
422
+ if (variant === "pipelines" || variant === "forceTransaction") {
423
+ continue;
424
+ }
425
+ if (!isOperationVariantAvailable(variants[variant])) {
426
+ diagnostics.push(diagnostic("COMPILER_UNAVAILABLE_OPERATION", `${familyPath}.${variant}`, `operation "${family}.${variant}" is not available for resource "${resourceName}"`));
427
+ }
428
+ }
402
429
  }
403
- diagnostics.push(diagnostic("COMPILER_UNKNOWN_RESOURCE", `${scope}.resources.${resourceName}`, `resource "${resourceName}" is not present in the canonical adapter model`));
430
+ }
431
+ if (!isRecord(entry.fields)) {
404
432
  continue;
405
433
  }
406
- for (const [fieldName, behavior] of Object.entries(rawResourceConfig.fields)) {
407
- if (behavior === undefined) {
434
+ for (const [fieldName, fieldEntry] of Object.entries(entry.fields)) {
435
+ const field = resource.fields[fieldName];
436
+ if (field === undefined &&
437
+ definitions[resourceName]?.[fieldName] === undefined) {
438
+ diagnostics.push(diagnostic("COMPILER_UNKNOWN_FIELD", `${resourcePath}.fields.${fieldName}`, `field "${fieldName}" is neither discovered by the adapter nor defined at the root`));
439
+ continue;
440
+ }
441
+ if (!isRecord(fieldEntry) || !isRecord(fieldEntry.pipelines)) {
408
442
  continue;
409
443
  }
410
- applyFieldBehavior(resource, fieldName, behavior, `${scope}.resources.${resourceName}.fields.${fieldName}.behaviors`, diagnostics, allowRestrictedMissing);
444
+ const pipelinesPath = `${resourcePath}.fields.${fieldName}.pipelines`;
445
+ if (field?.kind === "relation" &&
446
+ fieldEntry.pipelines.pipes !== undefined) {
447
+ diagnostics.push(diagnostic("COMPILER_RELATION_FIELD_CONFIG_UNSUPPORTED", `${pipelinesPath}.pipes`, `relation field "${fieldName}" takes write guards only; a field pipe transforms a scalar field's value`));
448
+ }
449
+ checkFieldCompute(model, resourceName, fieldName, field, fieldEntry.pipelines.compute, `${pipelinesPath}.compute`, diagnostics);
411
450
  }
412
451
  }
413
452
  }
453
+ function checkReservedResourceNames(model, diagnostics) {
454
+ for (const resourceName of Object.keys(model.resources)) {
455
+ if (resourceName.startsWith("$")) {
456
+ diagnostics.push(diagnostic("COMPILER_RESERVED_RESOURCE_NAME", `adapter.model.resources.${resourceName}`, `resource "${resourceName}" starts with "$", which is reserved for the framework's own names`));
457
+ }
458
+ }
459
+ }
460
+ function isAdapterStated(axis) {
461
+ return axis?.kind === "limit" || axis?.kind === "adapterTokens";
462
+ }
414
463
  const SCALAR_CAPABILITY_AXES = {
415
464
  select: { kind: "flag" },
416
465
  filter: { kind: "tokens" },
466
+ filterModes: { kind: "adapterTokens", values: FILTER_MODES },
467
+ pathFilterModes: { kind: "adapterTokens", values: FILTER_MODES },
417
468
  order: { kind: "flag" },
418
469
  orderNulls: { kind: "flag" },
419
470
  create: { kind: "tokens" },
@@ -441,226 +492,366 @@ function capabilityAxisFor(kind, key) {
441
492
  function unavailableCapability(path, message, diagnostics) {
442
493
  diagnostics.push(diagnostic("COMPILER_UNAVAILABLE_CAPABILITY", path, message));
443
494
  }
444
- function narrowedTokens(baselineTokens, requested, key, path, diagnostics) {
445
- const unavailable = requested.filter((entry) => !baselineTokens.includes(entry));
446
- if (unavailable.length > 0) {
447
- unavailableCapability(path, `capability "${key}" requested unavailable values: ${unavailable.join(", ")}`, diagnostics);
448
- return undefined;
449
- }
450
- return [...requested];
495
+ function otherOverlay(layer) {
496
+ return layer === "client" ? "application" : "client";
451
497
  }
452
- function narrowedScopes(axis, holdsAtScope, requested, key, path, diagnostics) {
453
- if (requested.length === 0) {
454
- return { outcome: "denied" };
455
- }
456
- const unavailable = requested.filter((scope) => !axis.includes(scope) || !holdsAtScope(scope));
457
- if (unavailable.length > 0) {
458
- unavailableCapability(path, `capability "${key}" requested unavailable scopes: ${unavailable.join(", ")}`, diagnostics);
459
- return { outcome: "refused" };
498
+ function widening(walk, code, path, what) {
499
+ walk.diagnostics.push(diagnostic(code, path, walk.layer === "agent"
500
+ ? `${what} is hidden by ${walk.parent ?? "client"}, the layer agent extends, and the agent layer only narrows that view; expose it in ${walk.parent ?? "client"}, or extend a layer that exposes it`
501
+ : `${what} is hidden by the root, and an overlay only narrows the root's view; expose it at the root and hide it in ${otherOverlay(walk.layer)} instead`));
502
+ }
503
+ function exemptsWhole(entry) {
504
+ return entry === true;
505
+ }
506
+ function exemptsAnything(entry) {
507
+ return (entry === true ||
508
+ (Array.isArray(entry) && entry.length > 0) ||
509
+ isRecord(entry));
510
+ }
511
+ function listedNames(entry, path) {
512
+ if (!Array.isArray(entry)) {
513
+ return [];
460
514
  }
461
- return { outcome: "narrowed", scopes: [...requested] };
515
+ return entry.flatMap((name, index) => typeof name === "string"
516
+ ? [[name, `${path}[${index}]`]]
517
+ : []);
462
518
  }
463
- function applyScopeRestriction(axis, baselineValue, current, key, requested, path, diagnostics) {
464
- const baseline = baselineValue;
465
- const narrowing = narrowedScopes(axis, (scope) => holdsAt(baseline, scope), requested, key, path, diagnostics);
466
- if (narrowing.outcome === "refused") {
467
- return;
519
+ function memberOffered(axis, value) {
520
+ if (value === undefined || value === false) {
521
+ return false;
468
522
  }
469
- if (narrowing.outcome === "denied") {
470
- delete current[key];
471
- return;
523
+ switch (axis.kind) {
524
+ case "tokens":
525
+ return Array.isArray(value);
526
+ case "scopedTokens":
527
+ return memberTokens(axis, value) !== undefined;
528
+ case "scopes":
529
+ return holdsInAnyScope(value);
530
+ default:
531
+ return true;
472
532
  }
473
- current[key] = narrowing.scopes;
474
533
  }
475
- function applyScopedTokenRestriction(tokenKey, axis, baselineValue, current, key, value, path, diagnostics) {
476
- const requestedTokens = Array.isArray(value)
477
- ? value
478
- : isRecord(value)
479
- ? value[tokenKey]
480
- : undefined;
481
- const requestedScopes = isRecord(value) ? value.scopes : undefined;
482
- if ((requestedTokens !== undefined && !Array.isArray(requestedTokens)) ||
483
- (requestedScopes !== undefined && !Array.isArray(requestedScopes))) {
484
- return false;
534
+ function memberTokens(axis, value) {
535
+ if (axis.kind === "tokens") {
536
+ return Array.isArray(value) ? value : undefined;
485
537
  }
486
- const baselineTokens = tokenKey === "directives"
487
- ? advertisedDirectives(baselineValue)
488
- : advertisedReducers(baselineValue);
489
- if (baselineTokens === undefined) {
490
- unavailableCapability(path, `capability "${key}" is not available in the canonical baseline`, diagnostics);
491
- return true;
538
+ if (axis.kind === "scopedTokens") {
539
+ return axis.tokenKey === "directives"
540
+ ? advertisedDirectives(value)
541
+ : advertisedReducers(value);
492
542
  }
493
- const tokens = requestedTokens === undefined
494
- ? [...baselineTokens]
495
- : narrowedTokens(baselineTokens, requestedTokens, key, path, diagnostics);
496
- if (tokens === undefined) {
497
- return true;
543
+ return undefined;
544
+ }
545
+ function withTokens(axis, parentValue, tokens) {
546
+ if (axis.kind === "scopedTokens" &&
547
+ isRecord(parentValue) &&
548
+ Array.isArray(parentValue.scopes)) {
549
+ return {
550
+ [axis.tokenKey]: [...tokens],
551
+ scopes: [...parentValue.scopes],
552
+ };
498
553
  }
499
- if (requestedScopes === undefined) {
500
- const baselineScopes = isRecord(baselineValue) && Array.isArray(baselineValue.scopes)
501
- ? [...baselineValue.scopes]
502
- : undefined;
503
- current[key] =
504
- baselineScopes === undefined
505
- ? tokens
506
- : { [tokenKey]: tokens, scopes: baselineScopes };
507
- return true;
554
+ return [...tokens];
555
+ }
556
+ function fieldName(resource, field) {
557
+ return `"${resource}.${field}"`;
558
+ }
559
+ function exemptableAxis(walk, universeField, capability, label, path) {
560
+ const axis = capabilityAxisFor(universeField.kind, capability);
561
+ if (axis === undefined || isAdapterStated(axis)) {
562
+ const names = Object.entries(universeField.kind === "scalar"
563
+ ? SCALAR_CAPABILITY_AXES
564
+ : RELATION_CAPABILITY_AXES)
565
+ .filter(([, entry]) => !isAdapterStated(entry))
566
+ .map(([name]) => name);
567
+ unavailableCapability(path, `"${capability}" is not a capability a ${universeField.kind} field's exemption can name; it takes ${names.join(", ")}`, walk.diagnostics);
568
+ return undefined;
508
569
  }
509
- const narrowing = narrowedScopes(axis, (scope) => (tokenKey === "directives"
510
- ? advertisedDirectivesAt(baselineValue, scope)
511
- : advertisedReducersAt(baselineValue, scope)) !== undefined, requestedScopes, key, path, diagnostics);
512
- if (narrowing.outcome === "refused") {
513
- return true;
570
+ const capabilities = universeField.capabilities;
571
+ if (!memberOffered(axis, capabilities[capability])) {
572
+ unavailableCapability(path, `capability "${capability}" is not available for field ${label}: the adapter does not offer it`, walk.diagnostics);
573
+ return undefined;
514
574
  }
515
- if (narrowing.outcome === "denied") {
516
- delete current[key];
517
- return true;
575
+ return axis;
576
+ }
577
+ function offeredTokens(walk, axis, universeValue, listed, capability, label) {
578
+ const offered = memberTokens(axis, universeValue) ?? [];
579
+ const tokens = [];
580
+ for (const [token, tokenPath] of listed) {
581
+ if (!offered.includes(token)) {
582
+ unavailableCapability(tokenPath, `capability "${capability}" of field ${label} does not offer "${token}": the adapter does not offer it`, walk.diagnostics);
583
+ continue;
584
+ }
585
+ tokens.push(token);
518
586
  }
519
- current[key] = { [tokenKey]: tokens, scopes: narrowing.scopes };
520
- return true;
587
+ return tokens;
521
588
  }
522
- function applyCapabilityRestriction(fieldKind, inherited, restoration, current, key, value, path, diagnostics) {
523
- if (capabilityAxisFor(fieldKind, key)?.kind === "limit") {
524
- unavailableCapability(path, `capability "${key}" is an adapter limit and takes no restriction`, diagnostics);
589
+ function exemptCapability(walk, universeField, parentCapabilities, result, capability, entry, label, path) {
590
+ if (entry === undefined) {
525
591
  return;
526
592
  }
527
- if (value === false) {
528
- delete current[key];
593
+ const axis = exemptableAxis(walk, universeField, capability, label, path);
594
+ if (axis === undefined || entry === false) {
529
595
  return;
530
596
  }
531
- const axis = capabilityAxisFor(fieldKind, key);
532
- const baselineValue = inherited?.[key] ?? restoration[key];
533
- if (axis === undefined ||
534
- baselineValue === undefined ||
535
- baselineValue === false) {
536
- unavailableCapability(path, `capability "${key}" is not available in the canonical baseline`, diagnostics);
597
+ const isTokenAxis = axis.kind === "tokens" || axis.kind === "scopedTokens";
598
+ if (entry !== true && !(isTokenAxis && Array.isArray(entry))) {
599
+ unavailableCapability(path, isTokenAxis
600
+ ? `capability "${capability}" takes true, false or a list of its tokens`
601
+ : `capability "${capability}" takes true or false`, walk.diagnostics);
537
602
  return;
538
603
  }
539
- if (value === true) {
540
- current[key] = cloneValue(baselineValue);
604
+ const universeValue = universeField.capabilities[capability];
605
+ const tokens = entry === true
606
+ ? undefined
607
+ : offeredTokens(walk, axis, universeValue, listedNames(entry, path), capability, label);
608
+ if (tokens !== undefined && tokens.length !== entry.length) {
541
609
  return;
542
610
  }
543
- if (axis.kind === "tokens" &&
544
- Array.isArray(value) &&
545
- Array.isArray(baselineValue)) {
546
- const tokens = narrowedTokens(baselineValue, value, key, path, diagnostics);
547
- if (tokens !== undefined) {
548
- current[key] = tokens;
611
+ const parentValue = parentCapabilities[capability];
612
+ const parentHas = memberOffered(axis, parentValue);
613
+ if (walk.style === "omit") {
614
+ if (!parentHas) {
615
+ return;
616
+ }
617
+ if (tokens === undefined) {
618
+ delete result[capability];
619
+ return;
549
620
  }
621
+ const kept = (memberTokens(axis, parentValue) ?? []).filter((token) => !tokens.includes(token));
622
+ result[capability] = withTokens(axis, parentValue, kept);
550
623
  return;
551
624
  }
552
- if (axis.kind === "scopes" && Array.isArray(value)) {
553
- applyScopeRestriction(axis.axis, baselineValue, current, key, value, path, diagnostics);
625
+ if (tokens !== undefined && tokens.length === 0) {
554
626
  return;
555
627
  }
556
- if (axis.kind === "scopedTokens") {
557
- if (applyScopedTokenRestriction(axis.tokenKey, axis.axis, baselineValue, current, key, value, path, diagnostics)) {
558
- return;
559
- }
628
+ if (!parentHas) {
629
+ widening(walk, "COMPILER_UNAVAILABLE_CAPABILITY", path, `capability "${capability}" of field ${label}`);
630
+ return;
560
631
  }
561
- unavailableCapability(path, `capability "${key}" does not admit this restriction shape`, diagnostics);
562
- }
563
- function applyCapabilityRestrictions(inheritedField, restorationField, field, restrictions, path, diagnostics) {
564
- const capabilityRestrictions = restrictions.capabilities;
565
- if (capabilityRestrictions === undefined) {
632
+ if (tokens === undefined) {
633
+ result[capability] = cloneValue(parentValue);
566
634
  return;
567
635
  }
568
- const inherited = inheritedField?.capabilities;
569
- const restoration = restorationField.capabilities;
570
- const current = field.capabilities;
571
- for (const [key, value] of Object.entries(capabilityRestrictions)) {
572
- if (value === undefined) {
573
- continue;
636
+ const parentTokens = memberTokens(axis, parentValue) ?? [];
637
+ const hidden = tokens.filter((token) => !parentTokens.includes(token));
638
+ if (hidden.length > 0) {
639
+ widening(walk, "COMPILER_UNAVAILABLE_CAPABILITY", path, `${hidden.map((token) => `"${token}"`).join(", ")} of capability "${capability}" of field ${label}`);
640
+ return;
641
+ }
642
+ result[capability] = withTokens(axis, parentValue, parentTokens.filter((token) => tokens.includes(token)));
643
+ }
644
+ function exemptField(walk, universeField, parentField, entry, label, path) {
645
+ const parentCapabilities = (parentField?.capabilities ?? {});
646
+ const result = walk.style === "omit" ? cloneValue(parentCapabilities) : {};
647
+ if (walk.style === "pick") {
648
+ for (const [key, value] of Object.entries(parentCapabilities)) {
649
+ if (isAdapterStated(capabilityAxisFor(universeField.kind, key))) {
650
+ result[key] = cloneValue(value);
651
+ }
652
+ }
653
+ }
654
+ if (Array.isArray(entry)) {
655
+ for (const [capability, capabilityPath] of listedNames(entry, path)) {
656
+ exemptCapability(walk, universeField, parentCapabilities, result, capability, true, label, capabilityPath);
574
657
  }
575
- applyCapabilityRestriction(restorationField.kind, inherited, restoration, current, key, value, `${path}.capabilities.${key}`, diagnostics);
576
658
  }
659
+ else if (isRecord(entry)) {
660
+ for (const [capability, value] of Object.entries(entry)) {
661
+ exemptCapability(walk, universeField, parentCapabilities, result, capability, value, label, `${path}.${capability}`);
662
+ }
663
+ }
664
+ if (parentField === undefined) {
665
+ return undefined;
666
+ }
667
+ if (walk.style === "pick" &&
668
+ !Object.keys(result).some((key) => !isAdapterStated(capabilityAxisFor(universeField.kind, key)))) {
669
+ return undefined;
670
+ }
671
+ return { ...cloneValue(parentField), capabilities: result };
577
672
  }
578
- function applyOperationRestrictions(restoration, current, restrictions, path, diagnostics) {
579
- if (restrictions === undefined) {
580
- return;
673
+ function exemptFields(walk, resourceName, universe, parent, entries, path) {
674
+ const named = isRecord(entries) ? entries : {};
675
+ for (const fieldName of Object.keys(named)) {
676
+ if (universe.fields[fieldName] === undefined) {
677
+ walk.diagnostics.push(diagnostic("COMPILER_UNKNOWN_FIELD", `${path}.${fieldName}`, `field "${fieldName}" is neither discovered by the adapter nor defined at the root`));
678
+ }
581
679
  }
582
- for (const [family, variants] of Object.entries(restrictions)) {
583
- if (!isRecord(variants)) {
680
+ const result = {};
681
+ for (const [name, entry] of Object.entries(named)) {
682
+ const universeField = universe.fields[name];
683
+ if (universeField === undefined) {
584
684
  continue;
585
685
  }
586
- const baselineFamily = restoration[family];
587
- let currentFamily = current[family];
588
- if (!isRecord(currentFamily)) {
589
- currentFamily = {};
590
- current[family] = currentFamily;
686
+ const parentField = parent.fields[name];
687
+ const label = fieldName(resourceName, name);
688
+ const fieldPath = `${path}.${name}`;
689
+ if (walk.style === "pick" &&
690
+ exemptsAnything(entry) &&
691
+ parentField === undefined) {
692
+ widening(walk, "COMPILER_UNAVAILABLE_CAPABILITY", fieldPath, `field ${label}`);
693
+ continue;
591
694
  }
592
- for (const [variant, enabled] of Object.entries(variants)) {
593
- if (typeof enabled !== "boolean") {
594
- continue;
595
- }
596
- if (!enabled) {
597
- delete currentFamily[variant];
598
- continue;
695
+ if (exemptsWhole(entry) || !exemptsAnything(entry)) {
696
+ continue;
697
+ }
698
+ const exempted = exemptField(walk, universeField, parentField, entry, label, fieldPath);
699
+ if (exempted !== undefined) {
700
+ result[name] = exempted;
701
+ }
702
+ }
703
+ for (const [name, parentField] of Object.entries(parent.fields)) {
704
+ if (Object.hasOwn(result, name)) {
705
+ continue;
706
+ }
707
+ const entry = Object.hasOwn(named, name) ? named[name] : undefined;
708
+ if (walk.style === "omit" ? !exemptsWhole(entry) : exemptsWhole(entry)) {
709
+ result[name] = cloneValue(parentField);
710
+ }
711
+ }
712
+ return result;
713
+ }
714
+ function exemptVariants(walk, resourceName, family, universeFamily, parentFamily, entry, path) {
715
+ if (entry === true) {
716
+ return "all";
717
+ }
718
+ const named = Array.isArray(entry)
719
+ ? [...listedNames(entry, path)]
720
+ : isRecord(entry)
721
+ ? Object.keys(entry).map((variant) => [variant, `${path}.${variant}`])
722
+ : [];
723
+ const variants = [];
724
+ for (const [variant, variantPath] of named) {
725
+ if (!isOperationVariantAvailable(universeFamily[variant])) {
726
+ walk.diagnostics.push(diagnostic("COMPILER_UNAVAILABLE_OPERATION", variantPath, `operation "${family}.${variant}" is not available for resource "${resourceName}"`));
727
+ continue;
728
+ }
729
+ if (isRecord(entry)) {
730
+ const value = entry[variant];
731
+ if (value !== true && value !== false) {
732
+ walk.diagnostics.push(diagnostic("COMPILER_UNAVAILABLE_OPERATION", variantPath, "an operation variant takes true or false"));
599
733
  }
600
- const baselineAvailability = isRecord(baselineFamily)
601
- ? baselineFamily[variant]
602
- : undefined;
603
- if (!isOperationVariantAvailable(baselineAvailability)) {
604
- diagnostics.push(diagnostic("COMPILER_UNAVAILABLE_OPERATION", `${path}.${family}.${variant}`, `operation "${family}.${variant}" is not available in the canonical baseline`));
734
+ if (value !== true) {
605
735
  continue;
606
736
  }
607
- const target = currentFamily;
608
- if (!isOperationVariantAvailable(target[variant])) {
609
- target[variant] = cloneValue(baselineAvailability);
610
- }
611
737
  }
738
+ if (walk.style === "pick" &&
739
+ !isOperationVariantAvailable(parentFamily?.[variant])) {
740
+ widening(walk, "COMPILER_UNAVAILABLE_OPERATION", variantPath, `operation "${resourceName}.${family}.${variant}"`);
741
+ continue;
742
+ }
743
+ variants.push(variant);
612
744
  }
745
+ return variants;
613
746
  }
614
- function applyRestrictions(baselines, projection, scope, config, diagnostics) {
615
- if (config === undefined) {
616
- return;
617
- }
618
- for (const [resourceName, rawRestrictions] of Object.entries(config)) {
619
- if (rawRestrictions === undefined) {
747
+ function exemptOperations(walk, resourceName, universe, parent, entry, path) {
748
+ const omit = walk.style === "omit";
749
+ if (entry === true) {
750
+ return omit ? {} : cloneValue(parent);
751
+ }
752
+ const families = Array.isArray(entry)
753
+ ? listedNames(entry, path).map(([family, familyPath]) => [family, familyPath, true])
754
+ : isRecord(entry)
755
+ ? Object.entries(entry).map(([family, value]) => [family, `${path}.${family}`, value])
756
+ : [];
757
+ const result = omit ? cloneValue(parent) : {};
758
+ for (const [family, familyPath, value] of families) {
759
+ const universeFamily = universe[family];
760
+ if (!isRecord(universeFamily)) {
761
+ walk.diagnostics.push(diagnostic("COMPILER_UNAVAILABLE_OPERATION", familyPath, `operation family "${family}" is not available for resource "${resourceName}"`));
620
762
  continue;
621
763
  }
622
- const restrictions = rawRestrictions;
623
- const baselineResource = baselines.restoration.resources[resourceName];
624
- if (baselineResource === undefined) {
625
- diagnostics.push(diagnostic("COMPILER_UNKNOWN_RESOURCE", `${scope}.resources.${resourceName}`, `resource "${resourceName}" is not present in the canonical compilation baseline`));
764
+ if (value === false || value === undefined) {
626
765
  continue;
627
766
  }
628
- if (restrictions.hidden === true) {
629
- delete projection.resources[resourceName];
767
+ if (value !== true && !Array.isArray(value) && !isRecord(value)) {
768
+ walk.diagnostics.push(diagnostic("COMPILER_UNAVAILABLE_OPERATION", familyPath, "an operation family takes true, false, a list of its variants or an object of them"));
630
769
  continue;
631
770
  }
632
- if (restrictions.hidden === false &&
633
- projection.resources[resourceName] === undefined) {
634
- projection.resources[resourceName] = cloneValue(baselineResource);
771
+ const parentFamily = isRecord(parent[family])
772
+ ? parent[family]
773
+ : undefined;
774
+ if (value === true && !omit && parentFamily === undefined) {
775
+ widening(walk, "COMPILER_UNAVAILABLE_OPERATION", familyPath, `operation family "${resourceName}.${family}"`);
776
+ continue;
635
777
  }
636
- const resource = projection.resources[resourceName];
637
- if (resource === undefined) {
778
+ const variants = exemptVariants(walk, resourceName, family, universeFamily, parentFamily, value, familyPath);
779
+ if (parentFamily === undefined) {
638
780
  continue;
639
781
  }
640
- for (const [fieldName, rawFieldRestrictions] of Object.entries(restrictions.fields ?? {})) {
641
- const fieldRestrictions = rawFieldRestrictions;
642
- const baselineField = baselineResource.fields[fieldName];
643
- const path = `${scope}.resources.${resourceName}.fields.${fieldName}`;
644
- if (baselineField === undefined) {
645
- diagnostics.push(diagnostic("COMPILER_UNKNOWN_FIELD", path, `field "${fieldName}" is not present in the canonical compilation baseline`));
646
- continue;
647
- }
648
- if (fieldRestrictions.hidden === true) {
649
- delete resource.fields[fieldName];
650
- continue;
651
- }
652
- if (fieldRestrictions.hidden === false &&
653
- resource.fields[fieldName] === undefined) {
654
- resource.fields[fieldName] = cloneValue(baselineField);
782
+ const named = variants === "all" ? Object.keys(parentFamily) : variants;
783
+ if (omit) {
784
+ const target = result[family];
785
+ for (const variant of named) {
786
+ if (target !== undefined) {
787
+ delete target[variant];
788
+ }
655
789
  }
656
- const field = resource.fields[fieldName];
657
- if (field === undefined) {
658
- continue;
790
+ continue;
791
+ }
792
+ const target = (isRecord(result[family]) ? result[family] : {});
793
+ for (const variant of named) {
794
+ if (isOperationVariantAvailable(parentFamily[variant])) {
795
+ target[variant] = cloneValue(parentFamily[variant]);
659
796
  }
660
- applyCapabilityRestrictions(baselines.narrowing.resources[resourceName]?.fields[fieldName], baselineField, field, fieldRestrictions, path, diagnostics);
661
797
  }
662
- applyOperationRestrictions(baselineResource.operations, resource.operations, restrictions.operations, `${scope}.resources.${resourceName}.operations`, diagnostics);
798
+ result[family] = target;
663
799
  }
800
+ pruneEmptyOperationFamilies(result);
801
+ return result;
802
+ }
803
+ function exemptResource(walk, resourceName, universe, parent, entry, path) {
804
+ const resource = {
805
+ fields: exemptFields(walk, resourceName, universe, parent, entry.fields, `${path}.fields`),
806
+ identifiers: [],
807
+ operations: exemptOperations(walk, resourceName, universe.operations, parent.operations, entry.operations, `${path}.operations`),
808
+ };
809
+ reconcileIdentifiers(walk, resource, parent, path);
810
+ return resource;
811
+ }
812
+ function projectExposure(walk, universe, parent, exemptions) {
813
+ const path = `${walk.layer}.exemptions.resources`;
814
+ const entries = isRecord(exemptions) && isRecord(exemptions.resources)
815
+ ? exemptions.resources
816
+ : {};
817
+ const resources = {};
818
+ for (const [resourceName, entry] of Object.entries(entries)) {
819
+ const resourcePath = `${path}.${resourceName}`;
820
+ const universeResource = universe.resources[resourceName];
821
+ if (universeResource === undefined) {
822
+ walk.diagnostics.push(diagnostic("COMPILER_UNKNOWN_RESOURCE", resourcePath, `resource "${resourceName}" is not present in the canonical adapter model`));
823
+ continue;
824
+ }
825
+ const parentResource = parent.resources[resourceName];
826
+ if (walk.style === "pick" &&
827
+ exemptsAnything(entry) &&
828
+ parentResource === undefined) {
829
+ widening(walk, "COMPILER_UNAVAILABLE_CAPABILITY", resourcePath, `resource "${resourceName}"`);
830
+ continue;
831
+ }
832
+ if (!isRecord(entry)) {
833
+ continue;
834
+ }
835
+ const exempted = exemptResource(walk, resourceName, universeResource, parentResource ?? { fields: {}, identifiers: [], operations: {} }, entry, resourcePath);
836
+ if (parentResource !== undefined &&
837
+ (walk.style === "omit" ||
838
+ Object.keys(exempted.fields).length > 0 ||
839
+ Object.keys(exempted.operations).length > 0)) {
840
+ resources[resourceName] = exempted;
841
+ }
842
+ }
843
+ for (const [resourceName, parentResource] of Object.entries(parent.resources)) {
844
+ if (Object.hasOwn(resources, resourceName)) {
845
+ continue;
846
+ }
847
+ const entry = Object.hasOwn(entries, resourceName)
848
+ ? entries[resourceName]
849
+ : undefined;
850
+ if (walk.style === "omit" ? !exemptsWhole(entry) : exemptsWhole(entry)) {
851
+ resources[resourceName] = cloneValue(parentResource);
852
+ }
853
+ }
854
+ return { ...cloneValue({ ...parent, resources: {} }), resources };
664
855
  }
665
856
  function pruneEmptyOperationFamilies(operations) {
666
857
  for (const [family, variants] of Object.entries(operations)) {
@@ -669,45 +860,28 @@ function pruneEmptyOperationFamilies(operations) {
669
860
  }
670
861
  }
671
862
  }
672
- function operationIsExplicitlyDisabled(restrictions, family, variant) {
673
- if (restrictions === undefined || !isRecord(restrictions.operations)) {
674
- return false;
675
- }
676
- const familyRestrictions = restrictions.operations[family];
677
- return isRecord(familyRestrictions) && familyRestrictions[variant] === false;
863
+ function identifierLabel(identifier) {
864
+ return identifier.map((name) => `\`${name}\``).join(" and ");
678
865
  }
679
- function reconcileIdentifiers(resource, baseline, restrictions) {
680
- resource.identifiers = baseline.identifiers.filter((identifier) => identifier.every((fieldName) => {
681
- const field = resource.fields[fieldName];
682
- return field !== undefined && field.kind === "scalar";
683
- }));
866
+ function reconcileIdentifiers(walk, resource, parent, path) {
867
+ resource.identifiers = parent.identifiers
868
+ .filter((identifier) => identifier.every((name) => resource.fields[name]?.kind === "scalar"))
869
+ .map((identifier) => [...identifier]);
870
+ const first = parent.identifiers[0];
871
+ if (resource.identifiers.length > 0 || first === undefined) {
872
+ return;
873
+ }
684
874
  for (const [family, variant] of UNIQUE_VARIANTS) {
685
875
  const currentFamily = resource.operations[family];
686
- if (resource.identifiers.length === 0) {
687
- if (isRecord(currentFamily)) {
688
- delete currentFamily[variant];
689
- }
690
- continue;
691
- }
692
- if (operationIsExplicitlyDisabled(restrictions, family, variant)) {
693
- if (isRecord(currentFamily)) {
694
- delete currentFamily[variant];
695
- }
876
+ if (!isRecord(currentFamily) ||
877
+ !isOperationVariantAvailable(currentFamily[variant])) {
696
878
  continue;
697
879
  }
698
- const baselineFamily = baseline.operations[family];
699
- const baselineAvailability = isRecord(baselineFamily)
700
- ? baselineFamily[variant]
701
- : undefined;
702
- if (isOperationVariantAvailable(baselineAvailability)) {
703
- const targetFamily = isRecord(currentFamily) ? currentFamily : {};
704
- if (!isOperationVariantAvailable(targetFamily[variant])) {
705
- targetFamily[variant] = cloneValue(baselineAvailability);
706
- }
707
- resource.operations[family] = targetFamily;
708
- }
880
+ const variantPath = `${path}.operations.${family}.${variant}`;
881
+ walk.diagnostics.push(diagnostic("COMPILER_MISSING_IDENTIFIER", variantPath, walk.style === "pick"
882
+ ? `${variantPath} needs an identifier field; pick ${identifierLabel(first)} (or another complete identifier) too`
883
+ : `${variantPath} needs an identifier field, and the exemptions hide every complete identifier; keep ${identifierLabel(first)} (or another complete identifier) exposed, or exempt the operation too`));
709
884
  }
710
- pruneEmptyOperationFamilies(resource.operations);
711
885
  }
712
886
  function validateScalarReference(projection, field, path, diagnostics) {
713
887
  const type = field.type;
@@ -757,7 +931,7 @@ function validateFieldLifecycle(field, path, diagnostics) {
757
931
  if (lifecycle.includes("VIRTUAL") &&
758
932
  holdsInAnyScope(capabilities.select) &&
759
933
  !lifecycle.includes("COMPUTED_ON_READ")) {
760
- diagnostics.push(diagnostic("COMPILER_MISSING_READ_BEHAVIOR", path, "selectable VIRTUAL field requires an effective read behavior"));
934
+ diagnostics.push(diagnostic("COMPILER_MISSING_READ_COMPUTE", path, "selectable VIRTUAL field requires a compute.read stage (pipelines.compute.read)"));
761
935
  }
762
936
  }
763
937
  function isTokenList(value) {
@@ -778,6 +952,11 @@ function representationFault(axis, value) {
778
952
  return typeof value === "string" && axis.values.includes(value)
779
953
  ? undefined
780
954
  : `one of ${axis.values.join(", ")} is expected`;
955
+ case "adapterTokens":
956
+ return isTokenList(value) &&
957
+ value.every((token) => axis.values.includes(token))
958
+ ? undefined
959
+ : `a list of ${axis.values.join(", ")} is expected`;
781
960
  case "flag":
782
961
  return typeof value === "boolean" ? undefined : "a boolean is expected";
783
962
  case "tokens":
@@ -815,12 +994,8 @@ function validateCapabilityRepresentation(field, path, diagnostics) {
815
994
  }
816
995
  }
817
996
  }
818
- function validateProjection(projection, restoration, scope, restrictions, diagnostics) {
997
+ function validateProjection(projection, scope, diagnostics) {
819
998
  for (const [resourceName, resource] of Object.entries(projection.resources)) {
820
- const baselineResource = restoration.resources[resourceName];
821
- if (baselineResource !== undefined) {
822
- reconcileIdentifiers(resource, baselineResource, restrictions?.[resourceName]);
823
- }
824
999
  for (const [fieldName, field] of Object.entries(resource.fields)) {
825
1000
  const fieldPath = `${scope}.resources.${resourceName}.fields.${fieldName}`;
826
1001
  validateCapabilityRepresentation(field, fieldPath, diagnostics);
@@ -841,49 +1016,114 @@ function resolveLimits(scope) {
841
1016
  return { ...DEFAULT_CONTRACT_LIMITS, ...(scope.limits ?? {}) };
842
1017
  }
843
1018
  function resolveTransactions(model, scopeName, scope, diagnostics) {
844
- const requested = scope.transactions ?? model.transactions;
1019
+ const requested = scope.transactionsStyle ?? model.transactions;
845
1020
  if (requested === "interactive" && model.transactions === "none") {
846
- diagnostics.push(diagnostic("COMPILER_UNAVAILABLE_CAPABILITY", `${scopeName}.transactions`, 'transaction capability "interactive" is not available in the canonical adapter model'));
1021
+ diagnostics.push(diagnostic("COMPILER_UNAVAILABLE_CAPABILITY", `${scopeName}.transactionsStyle`, 'transaction capability "interactive" is not available in the canonical adapter model'));
847
1022
  }
848
1023
  return requested;
849
1024
  }
850
1025
  function semanticObject(contract) {
851
1026
  return JSON.parse(canonicalizeContract(contract));
852
1027
  }
853
- function projectCore(model, scope, diagnostics) {
1028
+ function exposureStyle(layer) {
1029
+ return layer.exposed === false ? "pick" : "omit";
1030
+ }
1031
+ function projectCore(model, config, diagnostics) {
1032
+ const scope = config.root;
1033
+ const managed = managedElsewhereOf(config.trees.root);
854
1034
  const providerBaseline = createBaseline(model);
855
1035
  const baseline = cloneValue(providerBaseline);
856
1036
  baseline.limits = resolveLimits(scope);
857
1037
  baseline.transactions = resolveTransactions(model, "root", scope, diagnostics);
858
- applyFieldConfig(model, baseline, "root", scope.fields, diagnostics);
859
- applyBehaviors(baseline, "root", scope.behaviors, diagnostics);
860
- const projection = cloneValue(baseline);
861
- applyRestrictions({ narrowing: baseline, restoration: baseline }, projection, "root", scope.restrictions, diagnostics);
862
- validateProjection(projection, baseline, "root", scope.restrictions, diagnostics);
863
- return { baseline, projection };
864
- }
865
- function projectDerivedScope(model, scopeName, core, scope, diagnostics) {
1038
+ checkResourceTree(model, baseline, "root", config.trees.root, config.definitions, managed, diagnostics);
1039
+ applyFieldDefinitions(model, baseline, config.definitions, diagnostics);
1040
+ const unwithheld = managed.size === 0 ? baseline : cloneValue(baseline);
1041
+ withholdManagedWrites(baseline, managed);
1042
+ markGovernedFields(baseline, config.policy?.governed);
1043
+ applyCompute(baseline, scope.compute);
1044
+ checkLayerValidators(baseline.resources, "root", config.trees.root, diagnostics);
1045
+ const validators = compileValidatorTable(baseline.resources, scope.pipelines);
1046
+ const projection = projectExposure({ layer: "root", style: exposureStyle(scope), diagnostics }, baseline, baseline, scope.exemptions);
1047
+ qualifyFilterModes(projection);
1048
+ pruneHiddenPairings(projection, "shown");
1049
+ projectCreateRequirements(model, projection.resources);
1050
+ const rootChecked = diagnostics.length;
1051
+ checkHiddenRequirements(model, "root", baseline.resources, unwithheld.resources, projection.resources, diagnostics);
1052
+ explainManagedRefusals(diagnostics, rootChecked, "root", baseline, managed);
1053
+ applyValidators(projection.resources, validators);
1054
+ validateProjection(projection, "root", diagnostics);
1055
+ return { baseline, projection, validators };
1056
+ }
1057
+ function markGovernedFields(baseline, governed) {
1058
+ if (governed === undefined) {
1059
+ return;
1060
+ }
1061
+ for (const [resourceName, names] of Object.entries(governed)) {
1062
+ const resource = Object.hasOwn(baseline.resources, resourceName)
1063
+ ? baseline.resources[resourceName]
1064
+ : undefined;
1065
+ if (resource === undefined) {
1066
+ continue;
1067
+ }
1068
+ for (const name of names) {
1069
+ const field = Object.hasOwn(resource.fields, name)
1070
+ ? resource.fields[name]
1071
+ : undefined;
1072
+ if (field !== undefined) {
1073
+ field.governed = true;
1074
+ }
1075
+ }
1076
+ }
1077
+ }
1078
+ function projectDerivedScope(model, scopeName, core, parentView, config, diagnostics) {
1079
+ const scope = config[scopeName];
1080
+ const managed = managedElsewhereOf(config.trees.root);
866
1081
  const baseline = cloneValue(core.baseline);
867
1082
  baseline.limits = resolveLimits(scope);
868
- baseline.transactions = resolveTransactions(model, scopeName, scope, diagnostics);
869
- applyFieldConfig(model, baseline, scopeName, scope.fields, diagnostics);
870
- applyBehaviors(baseline, scopeName, scope.behaviors, diagnostics);
871
- const projection = cloneValue(core.projection);
872
- projection.limits = resolveLimits(scope);
873
- projection.transactions = baseline.transactions;
874
- applyFieldConfig(model, projection, scopeName, scope.fields, diagnostics, true);
875
- applyBehaviors(projection, scopeName, scope.behaviors, diagnostics, true);
876
- applyRestrictions({ narrowing: cloneValue(projection), restoration: baseline }, projection, scopeName, scope.restrictions, diagnostics);
877
- validateProjection(projection, baseline, scopeName, scope.restrictions, diagnostics);
878
- return projection;
879
- }
880
- function bindShapedFields(contract, fields) {
1083
+ baseline.transactions = resolveTransactions(model, scopeName, scope, scopeName === "agent" &&
1084
+ scope.transactionsStyle === config[config.agent.parent].transactionsStyle
1085
+ ? []
1086
+ : diagnostics);
1087
+ checkResourceTree(model, baseline, scopeName, config.trees[scopeName], config.definitions, managed, diagnostics);
1088
+ applyCompute(baseline, scope.compute);
1089
+ checkLayerValidators(baseline.resources, scopeName, config.trees[scopeName], diagnostics);
1090
+ const validators = compileValidatorTable(baseline.resources, scope.pipelines);
1091
+ const parent = cloneValue(parentView);
1092
+ const unhidden = (scopeName === "client" || scopeName === "agent") && managed.size > 0
1093
+ ? cloneValue(parent)
1094
+ : parent;
1095
+ if (unhidden !== parent) {
1096
+ hideManagedElsewhere(parent, managed);
1097
+ }
1098
+ parent.limits = resolveLimits(scope);
1099
+ parent.transactions = baseline.transactions;
1100
+ applyCompute(parent, scope.compute);
1101
+ const projection = projectExposure({
1102
+ layer: scopeName,
1103
+ style: exposureStyle(scope),
1104
+ diagnostics,
1105
+ ...(scopeName === "agent" ? { parent: config.agent.parent } : {}),
1106
+ }, baseline, parent, scope.exemptions);
1107
+ qualifyFilterModes(projection);
1108
+ pruneHiddenPairings(projection, "none");
1109
+ if (unhidden !== parent) {
1110
+ pruneManagedEnums(projection, unhidden, managed);
1111
+ }
1112
+ projectCreateRequirements(model, projection.resources);
1113
+ const layerChecked = diagnostics.length;
1114
+ checkHiddenRequirements(model, scopeName, baseline.resources, unhidden.resources, projection.resources, diagnostics);
1115
+ explainManagedRefusals(diagnostics, layerChecked, scopeName, baseline, managed);
1116
+ applyValidators(projection.resources, validators);
1117
+ validateProjection(projection, scopeName, diagnostics);
1118
+ return { projection, validators };
1119
+ }
1120
+ function bindShapedFields(contract, definitions) {
881
1121
  for (const [resourceName, resource] of Object.entries(contract.resources)) {
882
1122
  for (const [fieldName, field] of Object.entries(resource.fields)) {
883
1123
  if (field.kind !== "scalar" || field.json === undefined) {
884
1124
  continue;
885
1125
  }
886
- const shape = jsonShapeOf(fields?.[resourceName]?.[fieldName]?.json);
1126
+ const shape = jsonShapeOf(definitions[resourceName]?.[fieldName]?.json);
887
1127
  if (shape === undefined) {
888
1128
  throw new Error(`the Contract compiler found no shape for shaped field "${resourceName}.${fieldName}"`);
889
1129
  }
@@ -894,37 +1134,107 @@ function bindShapedFields(contract, fields) {
894
1134
  export async function compileContracts(model, config) {
895
1135
  const compilerConfig = config;
896
1136
  const diagnostics = [];
897
- const coreCompilation = projectCore(model, compilerConfig.root, diagnostics);
898
- const applicationProjection = projectDerivedScope(model, "application", coreCompilation, compilerConfig.application, diagnostics);
899
- const clientProjection = projectDerivedScope(model, "client", coreCompilation, compilerConfig.client, diagnostics);
1137
+ checkReservedResourceNames(model, diagnostics);
1138
+ checkCreateRequirements(model, diagnostics);
1139
+ const coreCompilation = projectCore(model, compilerConfig, diagnostics);
1140
+ const applicationProjection = projectDerivedScope(model, "application", coreCompilation, coreCompilation.projection, compilerConfig, diagnostics);
1141
+ const clientProjection = projectDerivedScope(model, "client", coreCompilation, coreCompilation.projection, compilerConfig, diagnostics);
1142
+ const agentParent = compilerConfig.agent.parent;
1143
+ const agentProjection = projectDerivedScope(model, "agent", coreCompilation, agentParent === "root"
1144
+ ? coreCompilation.projection
1145
+ : agentParent === "application"
1146
+ ? applicationProjection.projection
1147
+ : clientProjection.projection, compilerConfig, diagnostics);
1148
+ markCoreResources(coreCompilation.projection, model, managedElsewhereOf(compilerConfig.trees.root));
900
1149
  if (diagnostics.length > 0) {
901
1150
  return { ok: false, diagnostics };
902
1151
  }
903
1152
  const core = semanticObject(coreCompilation.projection);
904
- const application = semanticObject(applicationProjection);
905
- const clientHashInput = semanticObject({
906
- ...clientProjection,
907
- protocol: { version: AVENTARA_PROTOCOL_VERSION },
1153
+ const application = semanticObject(applicationProjection.projection);
1154
+ const client = await identifiedContract(compilerConfig.access === undefined
1155
+ ? clientProjection.projection
1156
+ : { ...clientProjection.projection, access: compilerConfig.access });
1157
+ const agent = await identifiedContract(agentProjection.projection);
1158
+ for (const [layer, contract] of [
1159
+ ["client", client],
1160
+ ["agent", agent],
1161
+ ]) {
1162
+ const structure = validateClientContractStructure(contract);
1163
+ if (!structure.valid) {
1164
+ return {
1165
+ ok: false,
1166
+ diagnostics: [
1167
+ diagnostic("COMPILER_INVALID_CLIENT_CONTRACT", structure.path, layer === "client"
1168
+ ? structure.message
1169
+ : `Agent Contract: ${structure.message}`),
1170
+ ],
1171
+ };
1172
+ }
1173
+ }
1174
+ const contracts = { core, application, client, agent };
1175
+ bindRuntimePieces(contracts, compilerConfig.definitions, {
1176
+ core: coreCompilation.validators,
1177
+ application: applicationProjection.validators,
1178
+ client: clientProjection.validators,
1179
+ agent: agentProjection.validators,
908
1180
  });
909
- const hash = await computeClientContractHash(clientHashInput);
910
- const client = semanticObject({
911
- ...clientHashInput,
912
- protocol: { version: AVENTARA_PROTOCOL_VERSION, hash },
1181
+ return { ok: true, contracts };
1182
+ }
1183
+ function bindRuntimePieces(contracts, definitions, validators) {
1184
+ for (const origin of ["core", "application", "client", "agent"]) {
1185
+ bindShapedFields(contracts[origin], definitions);
1186
+ bindFieldValidators(contracts[origin], validators[origin]);
1187
+ }
1188
+ }
1189
+ export function bindPrebuiltContracts(model, config, contracts) {
1190
+ const compilerConfig = config;
1191
+ const fields = validatedFieldsView(model, compilerConfig.definitions);
1192
+ bindRuntimePieces(contracts, compilerConfig.definitions, {
1193
+ core: compileValidatorTable(fields, compilerConfig.root.pipelines),
1194
+ application: compileValidatorTable(fields, compilerConfig.application.pipelines),
1195
+ client: compileValidatorTable(fields, compilerConfig.client.pipelines),
1196
+ agent: compileValidatorTable(fields, compilerConfig.agent.pipelines),
913
1197
  });
914
- const structure = validateClientContractStructure(client);
915
- if (!structure.valid) {
916
- return {
917
- ok: false,
918
- diagnostics: [
919
- diagnostic("COMPILER_INVALID_CLIENT_CONTRACT", structure.path, structure.message),
920
- ],
921
- };
1198
+ }
1199
+ function validatedFieldsView(model, definitions) {
1200
+ const resources = { ...model.resources };
1201
+ for (const [resourceName, resourceDefinitions] of Object.entries(definitions)) {
1202
+ const resource = Object.hasOwn(model.resources, resourceName)
1203
+ ? model.resources[resourceName]
1204
+ : undefined;
1205
+ if (resource === undefined) {
1206
+ continue;
1207
+ }
1208
+ let added;
1209
+ for (const [fieldName, definition] of Object.entries(resourceDefinitions)) {
1210
+ if (Object.hasOwn(resource.fields, fieldName)) {
1211
+ continue;
1212
+ }
1213
+ added ??= {};
1214
+ added[fieldName] = {
1215
+ kind: "scalar",
1216
+ type: definition.type,
1217
+ list: definition.list,
1218
+ };
1219
+ }
1220
+ if (added !== undefined) {
1221
+ resources[resourceName] = { fields: { ...resource.fields, ...added } };
1222
+ }
922
1223
  }
923
- bindShapedFields(core, compilerConfig.root.fields);
924
- bindShapedFields(application, compilerConfig.application.fields);
925
- bindShapedFields(client, compilerConfig.client.fields);
926
- return {
927
- ok: true,
928
- contracts: { core, application, client },
929
- };
1224
+ return resources;
1225
+ }
1226
+ async function identifiedContract(projection) {
1227
+ const members = canonicalizeContractMembers({
1228
+ ...projection,
1229
+ protocol: { version: AVENTARA_PROTOCOL_VERSION },
1230
+ });
1231
+ const hash = await hashCanonicalContractText(joinCanonicalContractMembers(members));
1232
+ const protocol = canonicalizeJson({
1233
+ version: AVENTARA_PROTOCOL_VERSION,
1234
+ hash,
1235
+ });
1236
+ const text = joinCanonicalContractMembers(members.map((member) => member.key === "protocol" ? { key: "protocol", text: protocol } : member));
1237
+ const contract = JSON.parse(text);
1238
+ recordCanonicalContractText(contract, text);
1239
+ return contract;
930
1240
  }