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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/CHANGELOG.md +1140 -0
  2. package/README.md +224 -57
  3. package/dist/adapters/adapter-discovery-diagnostic.d.ts +4 -4
  4. package/dist/adapters/adapter-field.d.ts +11 -11
  5. package/dist/adapters/adapter-identifier.d.ts +6 -6
  6. package/dist/adapters/adapter-model.d.ts +9 -9
  7. package/dist/adapters/adapter-operation-executor.interface.d.ts +4 -4
  8. package/dist/adapters/adapter-operation.d.ts +3 -3
  9. package/dist/adapters/adapter-resource.d.ts +18 -9
  10. package/dist/adapters/adapter-sql-statement.d.ts +19 -0
  11. package/dist/adapters/adapter-transaction-executor.interface.d.ts +19 -0
  12. package/dist/adapters/adapter.interface.d.ts +49 -29
  13. package/dist/avz/avz-check.factory.d.ts +83 -0
  14. package/dist/avz/avz-check.factory.js +116 -0
  15. package/dist/avz/avz-check.registry.d.ts +35 -0
  16. package/dist/avz/avz-check.registry.js +9 -0
  17. package/dist/avz/avz.d.ts +203 -0
  18. package/dist/{json → avz}/avz.js +150 -8
  19. package/dist/{json → avz}/index.d.ts +2 -1
  20. package/dist/{json → avz}/json-shape.registry.d.ts +7 -7
  21. package/dist/{json → avz}/zod-issue.translator.d.ts +8 -2
  22. package/dist/{json → avz}/zod-issue.translator.js +7 -0
  23. package/dist/compiler/contract-compiler-diagnostic.d.ts +31 -5
  24. package/dist/compiler/contract.compiler.d.ts +18 -18
  25. package/dist/compiler/contract.compiler.js +548 -441
  26. package/dist/compiler/create-requirements.projector.d.ts +49 -0
  27. package/dist/compiler/create-requirements.projector.js +423 -0
  28. package/dist/compiler/field-validator.compiler.d.ts +47 -0
  29. package/dist/compiler/field-validator.compiler.js +238 -0
  30. package/dist/config/any-framework-config.d.ts +19 -12
  31. package/dist/config/config-diagnostic.d.ts +11 -4
  32. package/dist/config/config-layer.d.ts +15 -6
  33. package/dist/config/config.resolver.d.ts +74 -31
  34. package/dist/config/config.resolver.js +108 -44
  35. package/dist/config/config.validator.d.ts +7 -7
  36. package/dist/config/config.validator.js +581 -72
  37. package/dist/config/define-resource.d.ts +57 -0
  38. package/dist/config/define-resource.js +14 -0
  39. package/dist/config/effective-fields.d.ts +19 -23
  40. package/dist/config/entrypoint.normalizer.d.ts +1 -1
  41. package/dist/config/exact-framework-config.d.ts +280 -0
  42. package/dist/config/field-compute.resolver.d.ts +19 -0
  43. package/dist/config/field-compute.resolver.js +115 -0
  44. package/dist/config/field-pipeline-config.d.ts +47 -0
  45. package/dist/config/forced-operation.validator.d.ts +10 -0
  46. package/dist/config/forced-operation.validator.js +66 -0
  47. package/dist/config/framework-config.d.ts +110 -39
  48. package/dist/config/framework-exemptions-config.d.ts +91 -0
  49. package/dist/config/framework-exemptions-config.js +1 -0
  50. package/dist/config/framework-field-definition.d.ts +78 -0
  51. package/dist/config/framework-field-definition.js +28 -0
  52. package/dist/config/framework-resources-config.d.ts +116 -0
  53. package/dist/config/framework-resources-config.js +1 -0
  54. package/dist/config/pipeline-config.d.ts +56 -20
  55. package/dist/config/pipeline-tree.resolver.d.ts +79 -0
  56. package/dist/config/pipeline-tree.resolver.js +220 -0
  57. package/dist/config/stage-type.eraser.d.ts +65 -0
  58. package/dist/config/stage-type.eraser.js +1 -0
  59. package/dist/contracts/capability.d.ts +11 -11
  60. package/dist/contracts/client-contract-structure.validator.d.ts +323 -28
  61. package/dist/contracts/client-contract-structure.validator.js +152 -8
  62. package/dist/contracts/contract-canonicalizer.d.ts +19 -3
  63. package/dist/contracts/contract-canonicalizer.js +141 -28
  64. package/dist/contracts/contract-hash.d.ts +12 -7
  65. package/dist/contracts/contract-hash.js +3 -2
  66. package/dist/contracts/contract-limits.d.ts +4 -1
  67. package/dist/contracts/contract-limits.js +1 -0
  68. package/dist/contracts/contract-lookup.d.ts +5 -5
  69. package/dist/contracts/contract-protocol.d.ts +4 -4
  70. package/dist/contracts/contract.d.ts +24 -16
  71. package/dist/contracts/create-requirements.d.ts +35 -0
  72. package/dist/contracts/create-requirements.js +1 -0
  73. package/dist/contracts/enum-definition.d.ts +1 -1
  74. package/dist/contracts/field-check.d.ts +101 -0
  75. package/dist/contracts/field-check.js +1 -0
  76. package/dist/contracts/field.d.ts +52 -42
  77. package/dist/contracts/identifier.d.ts +4 -4
  78. package/dist/contracts/json-shape.d.ts +12 -1
  79. package/dist/contracts/lifecycle-trait.d.ts +2 -2
  80. package/dist/contracts/resource-operations.d.ts +28 -28
  81. package/dist/contracts/resource.d.ts +15 -9
  82. package/dist/contracts/scalar-value-type.d.ts +15 -15
  83. package/dist/contracts/scalar.d.ts +3 -3
  84. package/dist/contracts/transaction-capability.d.ts +1 -1
  85. package/dist/contracts/wire-scalar-format.d.ts +4 -4
  86. package/dist/framework/contract-view.d.ts +213 -147
  87. package/dist/framework/framework-construction.error.d.ts +10 -10
  88. package/dist/framework/framework-construction.error.js +2 -2
  89. package/dist/framework/framework.d.ts +47 -23
  90. package/dist/framework/framework.factory.d.ts +8 -6
  91. package/dist/framework/framework.factory.js +40 -20
  92. package/dist/framework/operation-facade.d.ts +123 -52
  93. package/dist/framework/operation-facade.js +9 -1
  94. package/dist/index.d.ts +55 -50
  95. package/dist/index.js +6 -6
  96. package/dist/internal.d.ts +15 -10
  97. package/dist/internal.js +3 -1
  98. package/dist/operations/operation-arguments.d.ts +267 -202
  99. package/dist/operations/operation-call.d.ts +27 -28
  100. package/dist/operations/operation-identity.d.ts +10 -10
  101. package/dist/operations/operation-result.d.ts +21 -21
  102. package/dist/operations/operation-validator-core.d.ts +6 -6
  103. package/dist/operations/operation-validator-core.js +13 -2
  104. package/dist/operations/operation-validator.d.ts +9 -9
  105. package/dist/operations/projection-scope.resolver.d.ts +6 -6
  106. package/dist/operations/structural-limits.walker.d.ts +6 -6
  107. package/dist/operations/structural-limits.walker.js +145 -0
  108. package/dist/operations/validation-code.d.ts +11 -6
  109. package/dist/operations/validation-code.js +4 -0
  110. package/dist/operations/write-scope.resolver.d.ts +6 -6
  111. package/dist/protocol/absent-route.responder.d.ts +13 -12
  112. package/dist/protocol/absent-route.responder.js +19 -19
  113. package/dist/protocol/contract-document.responder.d.ts +3 -3
  114. package/dist/protocol/contract-document.responder.js +2 -2
  115. package/dist/protocol/http-request.decoder.d.ts +17 -13
  116. package/dist/protocol/http-request.decoder.js +10 -10
  117. package/dist/protocol/http-request.reader.d.ts +3 -3
  118. package/dist/protocol/http-response.encoder.d.ts +4 -4
  119. package/dist/protocol/http-response.encoder.js +94 -55
  120. package/dist/protocol/http-status.table.d.ts +2 -2
  121. package/dist/protocol/operation-response.guard.d.ts +2 -2
  122. package/dist/protocol/protocol-surface.d.ts +4 -4
  123. package/dist/protocol/protocol.binder.d.ts +50 -27
  124. package/dist/protocol/protocol.binder.js +12 -9
  125. package/dist/protocol/protocol.handler.d.ts +22 -13
  126. package/dist/protocol/protocol.handler.js +44 -9
  127. package/dist/protocol/unmounted-route.error.d.ts +4 -4
  128. package/dist/protocol/unmounted-route.error.js +2 -2
  129. package/dist/runtime/caller-data.copier.d.ts +5 -5
  130. package/dist/runtime/caller-data.copier.js +13 -13
  131. package/dist/runtime/caller-data.door.d.ts +6 -6
  132. package/dist/runtime/caller-data.door.js +6 -4
  133. package/dist/runtime/computed-read-runtime.d.ts +7 -6
  134. package/dist/runtime/computed-read-runtime.js +61 -48
  135. package/dist/runtime/computed-value.error.d.ts +6 -6
  136. package/dist/runtime/computed-value.error.js +3 -3
  137. package/dist/runtime/context-executor.runtime.d.ts +59 -0
  138. package/dist/runtime/context-executor.runtime.js +275 -0
  139. package/dist/runtime/create-requirement.d.ts +10 -81
  140. package/dist/runtime/create-requirement.js +80 -57
  141. package/dist/runtime/decimal.d.ts +2 -2
  142. package/dist/runtime/decimal.js +1 -1
  143. package/dist/runtime/diagnostics-reporter.d.ts +53 -27
  144. package/dist/runtime/diagnostics-reporter.js +24 -0
  145. package/dist/runtime/error-boundary.d.ts +65 -8
  146. package/dist/runtime/error-boundary.js +45 -19
  147. package/dist/runtime/execution-options.d.ts +10 -2
  148. package/dist/runtime/field-check.evaluator.d.ts +29 -0
  149. package/dist/runtime/field-check.evaluator.js +521 -0
  150. package/dist/runtime/field-compute.d.ts +64 -0
  151. package/dist/runtime/field-compute.js +1 -0
  152. package/dist/runtime/field-stage.plan.d.ts +15 -0
  153. package/dist/runtime/field-stage.plan.js +27 -0
  154. package/dist/runtime/field-stage.runtime.d.ts +35 -0
  155. package/dist/runtime/field-stage.runtime.js +143 -0
  156. package/dist/runtime/field-validator.runtime.d.ts +11 -0
  157. package/dist/runtime/field-validator.runtime.js +87 -0
  158. package/dist/runtime/framework-error-class.table.d.ts +33 -0
  159. package/dist/runtime/framework-error-class.table.js +25 -0
  160. package/dist/runtime/framework-error.d.ts +68 -8
  161. package/dist/runtime/framework-error.factory.d.ts +4 -0
  162. package/dist/runtime/framework-error.factory.js +15 -0
  163. package/dist/runtime/framework-error.js +23 -2
  164. package/dist/runtime/interceptor-next.error.d.ts +5 -0
  165. package/dist/runtime/interceptor-next.error.js +6 -0
  166. package/dist/runtime/json-value.d.ts +3 -3
  167. package/dist/runtime/level-lifecycle.runtime.d.ts +25 -0
  168. package/dist/runtime/level-lifecycle.runtime.js +42 -0
  169. package/dist/runtime/misuse-reporter.d.ts +12 -12
  170. package/dist/runtime/mutation-planner.d.ts +19 -10
  171. package/dist/runtime/mutation-planner.js +54 -30
  172. package/dist/runtime/nested-write.walker.d.ts +32 -0
  173. package/dist/runtime/nested-write.walker.js +73 -0
  174. package/dist/runtime/operation-request.d.ts +33 -6
  175. package/dist/runtime/operation-response.d.ts +17 -17
  176. package/dist/runtime/operation-runtime.d.ts +113 -25
  177. package/dist/runtime/operation-runtime.js +395 -74
  178. package/dist/runtime/outcome-mapper.d.ts +16 -5
  179. package/dist/runtime/outcome-mapper.js +23 -6
  180. package/dist/runtime/output-validator.d.ts +10 -10
  181. package/dist/runtime/output-validator.js +3 -3
  182. package/dist/runtime/owned-execution.registry.d.ts +6 -6
  183. package/dist/runtime/pipe-output.validator.d.ts +33 -18
  184. package/dist/runtime/pipe-output.validator.js +12 -12
  185. package/dist/runtime/pipeline.d.ts +196 -34
  186. package/dist/runtime/readonly-value.d.ts +32 -0
  187. package/dist/runtime/readonly-value.js +141 -0
  188. package/dist/runtime/request-envelope.validator.d.ts +3 -3
  189. package/dist/runtime/request-envelope.validator.js +12 -7
  190. package/dist/runtime/request-id.d.ts +1 -1
  191. package/dist/runtime/request-state.d.ts +14 -0
  192. package/dist/runtime/request-state.js +1 -0
  193. package/dist/runtime/request-state.resolver.d.ts +12 -0
  194. package/dist/runtime/request-state.resolver.js +20 -0
  195. package/dist/runtime/scalar-runtime.d.ts +31 -16
  196. package/dist/runtime/scalar-runtime.js +16 -18
  197. package/dist/transactions/deferred-operation.d.ts +35 -20
  198. package/dist/transactions/operation-arguments-encoder.d.ts +3 -3
  199. package/dist/transactions/operation-arguments-encoder.js +2 -2
  200. package/dist/transactions/sql-statement.d.ts +77 -0
  201. package/dist/transactions/sql-statement.js +47 -0
  202. package/dist/transactions/sql-statement.validator.d.ts +44 -0
  203. package/dist/transactions/sql-statement.validator.js +134 -0
  204. package/dist/transactions/transaction-envelope.d.ts +4 -4
  205. package/dist/transactions/transaction-integrity-error.d.ts +6 -6
  206. package/dist/transactions/transaction-integrity-error.js +5 -2
  207. package/dist/transactions/transaction-plan-assembler.d.ts +21 -11
  208. package/dist/transactions/transaction-plan-assembler.js +148 -34
  209. package/dist/transactions/transaction-plan-validator.d.ts +29 -20
  210. package/dist/transactions/transaction-plan-validator.js +70 -38
  211. package/dist/transactions/transaction-reference-collector.d.ts +5 -5
  212. package/dist/transactions/transaction-reference-resolver.d.ts +19 -2
  213. package/dist/transactions/transaction-reference-resolver.js +26 -14
  214. package/dist/transactions/transaction-reference-validator.d.ts +34 -4
  215. package/dist/transactions/transaction-reference-validator.js +58 -26
  216. package/dist/transactions/transaction-reference.d.ts +13 -13
  217. package/dist/transactions/transaction-request.d.ts +18 -5
  218. package/dist/transactions/transaction-response.d.ts +6 -6
  219. package/dist/transactions/transaction-runtime.d.ts +8 -7
  220. package/dist/transactions/transaction-runtime.js +166 -50
  221. package/dist/transactions/transaction-settlement.runtime.d.ts +51 -0
  222. package/dist/transactions/transaction-settlement.runtime.js +120 -0
  223. package/dist/transactions/transaction-slot-path-error.d.ts +4 -4
  224. package/dist/transactions/transaction-slot-path-error.js +2 -2
  225. package/dist/transactions/transaction-slot-writer.js +2 -2
  226. package/dist/transactions/transaction-stage.runtime.d.ts +81 -0
  227. package/dist/transactions/transaction-stage.runtime.js +123 -0
  228. package/package.json +6 -5
  229. package/src/runtime/field-check.evaluator.ts +720 -0
  230. package/dist/config/define-fields.d.ts +0 -42
  231. package/dist/config/define-fields.js +0 -3
  232. package/dist/config/framework-behaviors-config.d.ts +0 -30
  233. package/dist/config/framework-fields-config.d.ts +0 -42
  234. package/dist/config/framework-restrictions-config.d.ts +0 -142
  235. package/dist/json/avz.d.ts +0 -96
  236. package/dist/runtime/field-behavior.d.ts +0 -48
  237. /package/dist/{config/framework-behaviors-config.js → adapters/adapter-sql-statement.js} +0 -0
  238. /package/dist/{config/framework-fields-config.js → adapters/adapter-transaction-executor.interface.js} +0 -0
  239. /package/dist/{json → avz}/index.js +0 -0
  240. /package/dist/{json → avz}/json-shape.registry.js +0 -0
  241. /package/dist/config/{framework-restrictions-config.js → exact-framework-config.js} +0 -0
  242. /package/dist/{runtime/field-behavior.js → config/field-pipeline-config.js} +0 -0
@@ -1,34 +1,76 @@
1
1
  import { TRANSACTION_CAPABILITIES } from "../contracts/transaction-capability.js";
2
2
  import { normalizeEntrypoint } from "./entrypoint.normalizer.js";
3
- const ROOT_PROPERTIES = new Set([
4
- "entrypoint",
3
+ import { AVZ_FORMATS } from "./framework-field-definition.js";
4
+ const ROOT_ONLY_PROPERTIES = new Set([
5
+ "api",
6
+ "mcp",
5
7
  "adapter",
6
- "fields",
7
- "behaviors",
8
- "restrictions",
9
- "limits",
10
- "pipelines",
11
- "transactions",
12
8
  "diagnostics",
13
9
  "misuse",
14
10
  "application",
15
11
  "client",
12
+ "agent",
16
13
  ]);
17
- const SCOPE_PROPERTIES = new Set([
18
- "fields",
19
- "behaviors",
20
- "restrictions",
14
+ const MOVED_ROOT_PROPERTIES = new Map([
15
+ ["entrypoint", "api"],
16
+ ]);
17
+ const AGENT_PARENTS = new Set(["root", "application", "client"]);
18
+ const MCP_PROPERTIES = new Set(["path", "authorization"]);
19
+ const MCP_AUTHORIZATION_PROPERTIES = new Set(["servers", "verify"]);
20
+ const LAYER_PROPERTIES = new Set([
21
+ "exposed",
22
+ "transactionsStyle",
21
23
  "limits",
22
24
  "pipelines",
25
+ "resources",
26
+ "exemptions",
27
+ ]);
28
+ const MOVED_LAYER_PROPERTIES = new Map([
29
+ ["restrictions", ["exposed", "exemptions"]],
30
+ ["behaviors", ["resources.<Resource>.fields.<field>.pipelines.compute"]],
31
+ ["fields", ["resources.<Resource>.fields.<field>.definition"]],
32
+ ["transactions", ["transactionsStyle"]],
33
+ ]);
34
+ const RESOURCE_PROPERTIES = new Set([
35
+ "pipelines",
36
+ "operations",
23
37
  "transactions",
38
+ "fields",
39
+ ]);
40
+ const FIELD_PROPERTIES = new Set(["definition", "pipelines"]);
41
+ const DEFINITION_PROPERTIES = new Set([
42
+ "type",
43
+ "nullable",
44
+ "list",
45
+ "lifecycle",
46
+ "format",
47
+ "capabilities",
48
+ "json",
49
+ ]);
50
+ const COMPUTE_PHASES = new Set(["create", "read", "update"]);
51
+ const FIELD_PIPELINE_PROPERTIES = new Set([
52
+ "validators",
53
+ "pipes",
54
+ "compute",
55
+ "guards",
56
+ ]);
57
+ const OPERATION_FAMILIES = new Set([
58
+ "find",
59
+ "create",
60
+ "update",
61
+ "delete",
62
+ "upsert",
24
63
  ]);
64
+ const OPERATION_VARIANTS = new Set(["first", "unique", "many", "count", "one"]);
25
65
  const TRANSACTION_CAPABILITY_SET = new Set(TRANSACTION_CAPABILITIES);
66
+ const AVZ_FORMAT_SET = new Set(AVZ_FORMATS);
26
67
  const LIMIT_PROPERTIES = new Set([
27
68
  "maxRequestBytes",
28
69
  "maxNestingDepth",
29
70
  "maxListLimit",
30
71
  "maxTransactionOperations",
31
72
  "maxBooleanNodes",
73
+ "maxJsonDepth",
32
74
  ]);
33
75
  const PIPELINE_PROPERTIES = new Set([
34
76
  "guards",
@@ -48,20 +90,21 @@ function isPlainRecord(value) {
48
90
  function diagnostic(code, path, message) {
49
91
  return { code, path, message };
50
92
  }
51
- export function entrypointRefusalDiagnostic(message) {
52
- return diagnostic("CONFIG_INVALID_VALUE", "entrypoint", `entrypoint ${message}`);
93
+ function childPath(path, key) {
94
+ return path === "" ? key : `${path}.${key}`;
53
95
  }
54
- function validateNamedObject(owner, key, path, diagnostics) {
55
- if (!Object.hasOwn(owner, key)) {
56
- return;
57
- }
58
- if (!isPlainRecord(owner[key])) {
59
- diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", path, `${path} must be an object`));
96
+ export function mountPathRefusalDiagnostic(path, message) {
97
+ return diagnostic("CONFIG_INVALID_VALUE", path, `${path} ${message}`);
98
+ }
99
+ function requireRecord(value, path, diagnostics) {
100
+ if (isPlainRecord(value)) {
101
+ return true;
60
102
  }
103
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", path, `${path} must be an object`));
104
+ return false;
61
105
  }
62
106
  function validateLimits(value, path, diagnostics) {
63
- if (!isPlainRecord(value)) {
64
- diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", path, `${path} must be an object`));
107
+ if (!requireRecord(value, path, diagnostics)) {
65
108
  return;
66
109
  }
67
110
  for (const [key, limit] of Object.entries(value)) {
@@ -78,7 +121,7 @@ function validateLimits(value, path, diagnostics) {
78
121
  }
79
122
  }
80
123
  }
81
- function validatePipelineArray(value, key, path, diagnostics) {
124
+ function validateArray(value, key, path, diagnostics) {
82
125
  if (!Object.hasOwn(value, key)) {
83
126
  return;
84
127
  }
@@ -88,8 +131,7 @@ function validatePipelineArray(value, key, path, diagnostics) {
88
131
  }
89
132
  }
90
133
  function validatePipelineHooks(value, path, diagnostics) {
91
- if (!isPlainRecord(value)) {
92
- diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", path, `${path} must be an object`));
134
+ if (!requireRecord(value, path, diagnostics)) {
93
135
  return;
94
136
  }
95
137
  for (const key of Object.keys(value)) {
@@ -97,48 +139,432 @@ function validatePipelineHooks(value, path, diagnostics) {
97
139
  diagnostics.push(diagnostic("CONFIG_UNKNOWN_PROPERTY", `${path}.${key}`, `unknown pipeline hook property: ${key}`));
98
140
  }
99
141
  }
100
- validatePipelineArray(value, "before", path, diagnostics);
101
- validatePipelineArray(value, "after", path, diagnostics);
142
+ validateArray(value, "before", path, diagnostics);
143
+ validateArray(value, "after", path, diagnostics);
102
144
  }
103
145
  function validatePipelines(value, path, diagnostics) {
104
- if (!isPlainRecord(value)) {
105
- diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", path, `${path} must be an object`));
146
+ if (!requireRecord(value, path, diagnostics)) {
106
147
  return;
107
148
  }
108
149
  for (const key of Object.keys(value)) {
150
+ if (PIPELINE_HOOK_PROPERTIES.has(key)) {
151
+ diagnostics.push(diagnostic("CONFIG_MOVED_PROPERTY", `${path}.${key}`, `${path}.${key} moved in rc.1: hooks are ${path}.hooks.${key}`));
152
+ continue;
153
+ }
109
154
  if (!PIPELINE_PROPERTIES.has(key)) {
110
155
  diagnostics.push(diagnostic("CONFIG_UNKNOWN_PROPERTY", `${path}.${key}`, `unknown pipeline property: ${key}`));
111
156
  }
112
157
  }
113
158
  for (const key of ["guards", "pipes", "interceptors", "filters"]) {
114
- validatePipelineArray(value, key, path, diagnostics);
159
+ validateArray(value, key, path, diagnostics);
115
160
  }
116
161
  if (Object.hasOwn(value, "hooks")) {
117
162
  validatePipelineHooks(value.hooks, `${path}.hooks`, diagnostics);
118
163
  }
119
164
  }
120
- function validateScope(value, path, diagnostics) {
165
+ function validateForceTransaction(value, path, diagnostics) {
166
+ if (!Object.hasOwn(value, "forceTransaction")) {
167
+ return;
168
+ }
169
+ const propertyPath = `${path}.forceTransaction`;
170
+ if (typeof value.forceTransaction !== "boolean") {
171
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", propertyPath, `${propertyPath} must be a boolean`));
172
+ }
173
+ }
174
+ function validateOperationLevel(value, path, isFamily, diagnostics) {
175
+ if (!requireRecord(value, path, diagnostics)) {
176
+ return;
177
+ }
178
+ for (const [key, child] of Object.entries(value)) {
179
+ const keyPath = `${path}.${key}`;
180
+ if (key === "pipelines") {
181
+ validatePipelines(child, keyPath, diagnostics);
182
+ continue;
183
+ }
184
+ if (key === "forceTransaction") {
185
+ continue;
186
+ }
187
+ if (isFamily && OPERATION_VARIANTS.has(key)) {
188
+ validateOperationLevel(child, keyPath, false, diagnostics);
189
+ continue;
190
+ }
191
+ diagnostics.push(diagnostic("CONFIG_UNKNOWN_PROPERTY", keyPath, isFamily
192
+ ? `unknown operation family property: ${key}; a family takes pipelines, forceTransaction and its variants by name`
193
+ : `unknown operation variant property: ${key}; a variant takes pipelines and forceTransaction`));
194
+ }
195
+ validateForceTransaction(value, path, diagnostics);
196
+ }
197
+ function validateOperations(value, path, diagnostics) {
198
+ if (!requireRecord(value, path, diagnostics)) {
199
+ return;
200
+ }
201
+ for (const [family, level] of Object.entries(value)) {
202
+ const familyPath = `${path}.${family}`;
203
+ if (!OPERATION_FAMILIES.has(family)) {
204
+ diagnostics.push(diagnostic("CONFIG_UNKNOWN_PROPERTY", familyPath, `unknown operation family: ${family}`));
205
+ continue;
206
+ }
207
+ validateOperationLevel(level, familyPath, true, diagnostics);
208
+ }
209
+ }
210
+ function validateResourceTransactions(value, path, diagnostics) {
211
+ if (!requireRecord(value, path, diagnostics)) {
212
+ return;
213
+ }
214
+ for (const [key, child] of Object.entries(value)) {
215
+ const keyPath = `${path}.${key}`;
216
+ if (key !== "pipelines") {
217
+ diagnostics.push(diagnostic("CONFIG_UNKNOWN_PROPERTY", keyPath, `unknown Resource transactions property: ${key}`));
218
+ continue;
219
+ }
220
+ validatePipelines(child, keyPath, diagnostics);
221
+ }
222
+ }
223
+ function validateDefinition(value, path, diagnostics) {
224
+ if (!requireRecord(value, path, diagnostics)) {
225
+ return;
226
+ }
227
+ for (const key of Object.keys(value)) {
228
+ if (!DEFINITION_PROPERTIES.has(key)) {
229
+ diagnostics.push(diagnostic("CONFIG_UNKNOWN_PROPERTY", `${path}.${key}`, `unknown field definition property: ${key}`));
230
+ }
231
+ }
232
+ if (Object.hasOwn(value, "format") && !AVZ_FORMAT_SET.has(value.format)) {
233
+ diagnostics.push(diagnostic("CONFIG_INVALID_VALUE", `${path}.format`, `${path}.format must be one of zod's named string formats (${AVZ_FORMATS.join(", ")})`));
234
+ }
235
+ }
236
+ function validateComputeStage(value, path, phase, diagnostics) {
237
+ if (typeof value === "function") {
238
+ return;
239
+ }
240
+ if (phase === "read" && isPlainRecord(value)) {
241
+ for (const key of Object.keys(value)) {
242
+ if (key !== "compute" && key !== "dependsOn") {
243
+ diagnostics.push(diagnostic("CONFIG_UNKNOWN_PROPERTY", `${path}.${key}`, `unknown computed read property: ${key}`));
244
+ }
245
+ }
246
+ if (typeof value.compute !== "function") {
247
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", `${path}.compute`, `${path}.compute must be a function`));
248
+ }
249
+ validateArray(value, "dependsOn", path, diagnostics);
250
+ return;
251
+ }
252
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", path, value === false
253
+ ? `${path} must be a function: compute stages chain across layers, and none is disabled from below`
254
+ : phase === "read"
255
+ ? `${path} must be a function or { compute, dependsOn }`
256
+ : `${path} must be a function`));
257
+ }
258
+ function validateCompute(value, path, diagnostics) {
259
+ if (!requireRecord(value, path, diagnostics)) {
260
+ return;
261
+ }
262
+ for (const [phase, stage] of Object.entries(value)) {
263
+ const phasePath = `${path}.${phase}`;
264
+ if (!COMPUTE_PHASES.has(phase)) {
265
+ diagnostics.push(diagnostic("CONFIG_UNKNOWN_PROPERTY", phasePath, `unknown compute phase: ${phase}; the phases are create, read and update`));
266
+ continue;
267
+ }
268
+ if (stage !== undefined) {
269
+ validateComputeStage(stage, phasePath, phase, diagnostics);
270
+ }
271
+ }
272
+ }
273
+ function validateFieldPipelines(value, path, diagnostics) {
274
+ if (!requireRecord(value, path, diagnostics)) {
275
+ return;
276
+ }
277
+ for (const [key, child] of Object.entries(value)) {
278
+ const keyPath = `${path}.${key}`;
279
+ if (!FIELD_PIPELINE_PROPERTIES.has(key)) {
280
+ diagnostics.push(diagnostic("CONFIG_UNKNOWN_PROPERTY", keyPath, `unknown field pipeline property: ${key}`));
281
+ continue;
282
+ }
283
+ if (key === "compute") {
284
+ validateCompute(child, keyPath, diagnostics);
285
+ continue;
286
+ }
287
+ validateArray(value, key, path, diagnostics);
288
+ }
289
+ }
290
+ function movedFieldKey(key) {
291
+ if (DEFINITION_PROPERTIES.has(key)) {
292
+ return `definition.${key}`;
293
+ }
294
+ if (COMPUTE_PHASES.has(key)) {
295
+ return `pipelines.compute.${key}`;
296
+ }
297
+ return undefined;
298
+ }
299
+ function validateField(value, path, isRoot, resourcePath, diagnostics) {
300
+ if (!requireRecord(value, path, diagnostics)) {
301
+ return;
302
+ }
303
+ for (const [key, child] of Object.entries(value)) {
304
+ const keyPath = `${path}.${key}`;
305
+ if (key === "definition") {
306
+ if (!isRoot) {
307
+ diagnostics.push(diagnostic("CONFIG_FORBIDDEN_IN_OVERLAY", keyPath, `${keyPath}: definition is only allowed at root; a field is defined once and overlays only choose its visibility (define it at ${resourcePath})`));
308
+ continue;
309
+ }
310
+ validateDefinition(child, keyPath, diagnostics);
311
+ continue;
312
+ }
313
+ if (key === "pipelines") {
314
+ validateFieldPipelines(child, keyPath, diagnostics);
315
+ continue;
316
+ }
317
+ if (key === "hidden") {
318
+ diagnostics.push(movedHidden(keyPath));
319
+ continue;
320
+ }
321
+ const moved = movedFieldKey(key);
322
+ if (moved !== undefined) {
323
+ diagnostics.push(diagnostic("CONFIG_MOVED_PROPERTY", keyPath, `${keyPath} moved in rc.1: it is ${path}.${moved}`));
324
+ continue;
325
+ }
326
+ if (!FIELD_PROPERTIES.has(key)) {
327
+ diagnostics.push(diagnostic("CONFIG_UNKNOWN_PROPERTY", keyPath, `unknown field property: ${key}; a field takes definition and pipelines`));
328
+ }
329
+ }
330
+ }
331
+ function validateResource(value, path, resource, isRoot, diagnostics) {
332
+ if (!requireRecord(value, path, diagnostics)) {
333
+ return;
334
+ }
335
+ for (const [key, child] of Object.entries(value)) {
336
+ const keyPath = `${path}.${key}`;
337
+ if (key === "hidden") {
338
+ diagnostics.push(movedHidden(keyPath));
339
+ continue;
340
+ }
341
+ if (!RESOURCE_PROPERTIES.has(key)) {
342
+ diagnostics.push(diagnostic("CONFIG_UNKNOWN_PROPERTY", keyPath, `unknown Resource property: ${key}; a Resource takes pipelines, operations, transactions and fields`));
343
+ continue;
344
+ }
345
+ if (key === "pipelines") {
346
+ validatePipelines(child, keyPath, diagnostics);
347
+ continue;
348
+ }
349
+ if (key === "operations") {
350
+ validateOperations(child, keyPath, diagnostics);
351
+ continue;
352
+ }
353
+ if (key === "transactions") {
354
+ validateResourceTransactions(child, keyPath, diagnostics);
355
+ continue;
356
+ }
357
+ if (!requireRecord(child, keyPath, diagnostics)) {
358
+ continue;
359
+ }
360
+ for (const [field, entry] of Object.entries(child)) {
361
+ validateField(entry, `${keyPath}.${field}`, isRoot, `resources.${resource}.fields.${field}.definition`, diagnostics);
362
+ }
363
+ }
364
+ }
365
+ function validateResources(value, path, isRoot, diagnostics) {
366
+ if (!requireRecord(value, path, diagnostics)) {
367
+ return;
368
+ }
369
+ for (const [resource, entry] of Object.entries(value)) {
370
+ validateResource(entry, `${path}.${resource}`, resource, isRoot, diagnostics);
371
+ }
372
+ }
373
+ const EXEMPTIONS_PROPERTIES = new Set(["resources"]);
374
+ const RESOURCE_EXEMPTION_PROPERTIES = new Set(["operations", "fields"]);
375
+ const FLAG_CAPABILITIES = new Set(["select", "include", "order", "orderNulls"]);
376
+ function movedHidden(path) {
377
+ return diagnostic("CONFIG_MOVED_PROPERTY", path, `${path} moved in rc.1: an entry is hidden with \`true\` under exposed: true`);
378
+ }
379
+ function validateNameList(value, path, what, diagnostics) {
380
+ value.forEach((entry, index) => {
381
+ if (typeof entry !== "string") {
382
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", `${path}[${index}]`, `${path}[${index}] must be ${what}`));
383
+ }
384
+ });
385
+ }
386
+ function validateExemptionValue(value, path, what, diagnostics) {
387
+ if (typeof value === "boolean") {
388
+ return "boolean";
389
+ }
390
+ if (Array.isArray(value)) {
391
+ validateNameList(value, path, what, diagnostics);
392
+ return "list";
393
+ }
394
+ if (isPlainRecord(value)) {
395
+ if (Object.hasOwn(value, "hidden")) {
396
+ diagnostics.push(movedHidden(`${path}.hidden`));
397
+ }
398
+ return "object";
399
+ }
400
+ return undefined;
401
+ }
402
+ function validateOperationsExemption(value, path, diagnostics) {
403
+ const kind = validateExemptionValue(value, path, "an operation family name", diagnostics);
404
+ if (kind === undefined) {
405
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", path, `${path} must be true, false, a list of operation families, or an object of them`));
406
+ return;
407
+ }
408
+ if (kind !== "object") {
409
+ return;
410
+ }
411
+ for (const [family, variants] of Object.entries(value)) {
412
+ if (family === "hidden") {
413
+ continue;
414
+ }
415
+ const familyPath = `${path}.${family}`;
416
+ const familyKind = validateExemptionValue(variants, familyPath, "an operation variant name", diagnostics);
417
+ if (familyKind === undefined) {
418
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", familyPath, `${familyPath} must be true, false, a list of variants, or an object of them`));
419
+ continue;
420
+ }
421
+ if (familyKind !== "object") {
422
+ continue;
423
+ }
424
+ for (const [variant, flag] of Object.entries(variants)) {
425
+ if (variant !== "hidden" && typeof flag !== "boolean") {
426
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", `${familyPath}.${variant}`, `${familyPath}.${variant} must be a boolean`));
427
+ }
428
+ }
429
+ }
430
+ }
431
+ function validateFieldExemption(value, path, diagnostics) {
432
+ const kind = validateExemptionValue(value, path, "a capability name", diagnostics);
433
+ if (kind === undefined) {
434
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", path, `${path} must be true, false, a list of capability names, or an object of them`));
435
+ return;
436
+ }
437
+ if (kind !== "object") {
438
+ return;
439
+ }
440
+ for (const [capability, entry] of Object.entries(value)) {
441
+ if (capability === "hidden") {
442
+ continue;
443
+ }
444
+ const capabilityPath = `${path}.${capability}`;
445
+ if (capability === "capabilities") {
446
+ diagnostics.push(diagnostic("CONFIG_MOVED_PROPERTY", capabilityPath, `${capabilityPath} moved in rc.1: capabilities are named directly on the field's exemption`));
447
+ continue;
448
+ }
449
+ if (typeof entry === "boolean") {
450
+ continue;
451
+ }
452
+ if (Array.isArray(entry) && !FLAG_CAPABILITIES.has(capability)) {
453
+ validateNameList(entry, capabilityPath, "a token", diagnostics);
454
+ continue;
455
+ }
456
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", capabilityPath, FLAG_CAPABILITIES.has(capability)
457
+ ? `${capabilityPath} must be a boolean`
458
+ : `${capabilityPath} must be a boolean or a list of tokens`));
459
+ }
460
+ }
461
+ function validateResourceExemption(value, path, diagnostics) {
462
+ if (typeof value === "boolean") {
463
+ return;
464
+ }
121
465
  if (!isPlainRecord(value)) {
122
- diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", path, `${path} must be an object`));
466
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", path, `${path} must be true, false or { operations, fields }; a Resource takes no list`));
123
467
  return;
124
468
  }
469
+ for (const [key, entry] of Object.entries(value)) {
470
+ const keyPath = `${path}.${key}`;
471
+ if (key === "hidden") {
472
+ diagnostics.push(movedHidden(keyPath));
473
+ continue;
474
+ }
475
+ if (!RESOURCE_EXEMPTION_PROPERTIES.has(key)) {
476
+ diagnostics.push(diagnostic("CONFIG_UNKNOWN_PROPERTY", keyPath, `unknown Resource exemption property: ${key}; a Resource's exemption takes operations and fields`));
477
+ continue;
478
+ }
479
+ if (key === "operations") {
480
+ validateOperationsExemption(entry, keyPath, diagnostics);
481
+ continue;
482
+ }
483
+ if (!requireRecord(entry, keyPath, diagnostics)) {
484
+ continue;
485
+ }
486
+ for (const [field, fieldEntry] of Object.entries(entry)) {
487
+ validateFieldExemption(fieldEntry, `${keyPath}.${field}`, diagnostics);
488
+ }
489
+ }
490
+ }
491
+ function validateExemptions(value, path, diagnostics) {
492
+ if (!requireRecord(value, path, diagnostics)) {
493
+ return;
494
+ }
495
+ for (const [key, entry] of Object.entries(value)) {
496
+ const keyPath = `${path}.${key}`;
497
+ if (!EXEMPTIONS_PROPERTIES.has(key)) {
498
+ diagnostics.push(diagnostic("CONFIG_UNKNOWN_PROPERTY", keyPath, `unknown exemptions property: ${key}; exemptions take resources`));
499
+ continue;
500
+ }
501
+ if (!requireRecord(entry, keyPath, diagnostics)) {
502
+ continue;
503
+ }
504
+ for (const [resource, resourceEntry] of Object.entries(entry)) {
505
+ validateResourceExemption(resourceEntry, `${keyPath}.${resource}`, diagnostics);
506
+ }
507
+ }
508
+ }
509
+ function validateLayer(value, path, kind, diagnostics) {
510
+ const isRoot = kind === "root";
125
511
  for (const key of Object.keys(value)) {
126
- if (!SCOPE_PROPERTIES.has(key)) {
127
- diagnostics.push(diagnostic("CONFIG_UNKNOWN_PROPERTY", `${path}.${key}`, `unknown scoped configuration property: ${key}`));
512
+ const keyPath = childPath(path, key);
513
+ const moved = MOVED_LAYER_PROPERTIES.get(key);
514
+ if (moved !== undefined) {
515
+ diagnostics.push(diagnostic("CONFIG_MOVED_PROPERTY", keyPath, `${keyPath} moved in rc.1: use ${moved.map((next) => childPath(path, next)).join(" + ")}`));
516
+ continue;
517
+ }
518
+ const movedRootKey = isRoot ? MOVED_ROOT_PROPERTIES.get(key) : undefined;
519
+ if (movedRootKey !== undefined) {
520
+ diagnostics.push(diagnostic("CONFIG_MOVED_PROPERTY", keyPath, `${keyPath} moved in rc.1: use ${movedRootKey}`));
521
+ continue;
522
+ }
523
+ if (LAYER_PROPERTIES.has(key) ||
524
+ (isRoot && ROOT_ONLY_PROPERTIES.has(key)) ||
525
+ (kind === "agent" && key === "extends")) {
526
+ continue;
527
+ }
528
+ diagnostics.push(diagnostic("CONFIG_UNKNOWN_PROPERTY", keyPath, key === "extends"
529
+ ? `${keyPath}: extends is only allowed in the agent layer (agent.extends)`
530
+ : !isRoot && ROOT_ONLY_PROPERTIES.has(key)
531
+ ? `${keyPath}: ${key} is a root-only configuration property`
532
+ : isRoot
533
+ ? `unknown configuration property: ${key}`
534
+ : `unknown scoped configuration property: ${key}`));
535
+ }
536
+ if (kind === "agent" && Object.hasOwn(value, "extends")) {
537
+ if (!AGENT_PARENTS.has(value.extends)) {
538
+ const extendsPath = childPath(path, "extends");
539
+ diagnostics.push(diagnostic("CONFIG_INVALID_VALUE", extendsPath, `${extendsPath} must be "client", "application" or "root"`));
128
540
  }
129
541
  }
130
- validateNamedObject(value, "fields", `${path}.fields`, diagnostics);
131
- validateNamedObject(value, "behaviors", `${path}.behaviors`, diagnostics);
132
- validateNamedObject(value, "restrictions", `${path}.restrictions`, diagnostics);
542
+ const exposedPath = childPath(path, "exposed");
543
+ if (Object.hasOwn(value, "exposed")) {
544
+ if (typeof value.exposed !== "boolean") {
545
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", exposedPath, `${exposedPath} must be a boolean`));
546
+ }
547
+ }
548
+ if (Object.hasOwn(value, "exemptions")) {
549
+ const exemptionsPath = childPath(path, "exemptions");
550
+ if (!isRoot && !Object.hasOwn(value, "exposed")) {
551
+ diagnostics.push(diagnostic("CONFIG_REQUIRED", exposedPath, `${exposedPath} is required when ${exemptionsPath} is set: true makes the exemptions hide, false makes them the only entries exposed`));
552
+ }
553
+ validateExemptions(value.exemptions, exemptionsPath, diagnostics);
554
+ }
555
+ if (Object.hasOwn(value, "transactionsStyle") &&
556
+ !TRANSACTION_CAPABILITY_SET.has(value.transactionsStyle)) {
557
+ const stylePath = childPath(path, "transactionsStyle");
558
+ diagnostics.push(diagnostic("CONFIG_INVALID_VALUE", stylePath, `${stylePath} must be "none" or "interactive"`));
559
+ }
133
560
  if (Object.hasOwn(value, "limits")) {
134
- validateLimits(value.limits, `${path}.limits`, diagnostics);
561
+ validateLimits(value.limits, childPath(path, "limits"), diagnostics);
135
562
  }
136
563
  if (Object.hasOwn(value, "pipelines")) {
137
- validatePipelines(value.pipelines, `${path}.pipelines`, diagnostics);
564
+ validatePipelines(value.pipelines, childPath(path, "pipelines"), diagnostics);
138
565
  }
139
- if (Object.hasOwn(value, "transactions") &&
140
- !TRANSACTION_CAPABILITY_SET.has(value.transactions)) {
141
- diagnostics.push(diagnostic("CONFIG_INVALID_VALUE", `${path}.transactions`, `${path}.transactions must be "none" or "interactive"`));
566
+ if (Object.hasOwn(value, "resources")) {
567
+ validateResources(value.resources, childPath(path, "resources"), isRoot, diagnostics);
142
568
  }
143
569
  }
144
570
  function validateAdapter(value, diagnostics) {
@@ -166,22 +592,25 @@ export function validateFrameworkConfig(value) {
166
592
  diagnostic("CONFIG_INVALID_TYPE", "$", "framework configuration must be an object"),
167
593
  ];
168
594
  }
169
- for (const key of Object.keys(value)) {
170
- if (!ROOT_PROPERTIES.has(key)) {
171
- diagnostics.push(diagnostic("CONFIG_UNKNOWN_PROPERTY", key, `unknown configuration property: ${key}`));
172
- }
173
- }
174
- if (!Object.hasOwn(value, "entrypoint")) {
175
- diagnostics.push(diagnostic("CONFIG_REQUIRED", "entrypoint", "entrypoint is required"));
595
+ validateLayer(value, "", "root", diagnostics);
596
+ let api;
597
+ if (!Object.hasOwn(value, "api")) {
598
+ diagnostics.push(diagnostic("CONFIG_REQUIRED", "api", "api is required"));
176
599
  }
177
- else if (typeof value.entrypoint !== "string") {
178
- diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", "entrypoint", "entrypoint must be a string"));
600
+ else if (typeof value.api !== "string") {
601
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", "api", "api must be a string"));
179
602
  }
180
603
  else {
181
- const normalized = normalizeEntrypoint(value.entrypoint);
182
- if (!normalized.ok) {
183
- diagnostics.push(entrypointRefusalDiagnostic(normalized.message));
604
+ const normalized = normalizeEntrypoint(value.api);
605
+ if (normalized.ok) {
606
+ api = normalized.path;
184
607
  }
608
+ else {
609
+ diagnostics.push(mountPathRefusalDiagnostic("api", normalized.message));
610
+ }
611
+ }
612
+ if (Object.hasOwn(value, "mcp")) {
613
+ validateMcp(value.mcp, api, diagnostics);
185
614
  }
186
615
  if (!Object.hasOwn(value, "adapter")) {
187
616
  diagnostics.push(diagnostic("CONFIG_REQUIRED", "adapter", "adapter is required"));
@@ -189,19 +618,6 @@ export function validateFrameworkConfig(value) {
189
618
  else {
190
619
  validateAdapter(value.adapter, diagnostics);
191
620
  }
192
- validateNamedObject(value, "fields", "fields", diagnostics);
193
- validateNamedObject(value, "behaviors", "behaviors", diagnostics);
194
- validateNamedObject(value, "restrictions", "restrictions", diagnostics);
195
- if (Object.hasOwn(value, "limits")) {
196
- validateLimits(value.limits, "limits", diagnostics);
197
- }
198
- if (Object.hasOwn(value, "pipelines")) {
199
- validatePipelines(value.pipelines, "pipelines", diagnostics);
200
- }
201
- if (Object.hasOwn(value, "transactions") &&
202
- !TRANSACTION_CAPABILITY_SET.has(value.transactions)) {
203
- diagnostics.push(diagnostic("CONFIG_INVALID_VALUE", "transactions", 'transactions must be "none" or "interactive"'));
204
- }
205
621
  if (Object.hasOwn(value, "diagnostics") &&
206
622
  value.diagnostics !== undefined &&
207
623
  typeof value.diagnostics !== "function") {
@@ -212,11 +628,104 @@ export function validateFrameworkConfig(value) {
212
628
  typeof value.misuse !== "function") {
213
629
  diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", "misuse", "misuse must be a function when provided"));
214
630
  }
215
- if (Object.hasOwn(value, "application")) {
216
- validateScope(value.application, "application", diagnostics);
217
- }
218
- if (Object.hasOwn(value, "client")) {
219
- validateScope(value.client, "client", diagnostics);
631
+ for (const overlay of ["application", "client", "agent"]) {
632
+ if (!Object.hasOwn(value, overlay)) {
633
+ continue;
634
+ }
635
+ const layer = value[overlay];
636
+ if (requireRecord(layer, overlay, diagnostics)) {
637
+ validateLayer(layer, overlay, overlay === "agent" ? "agent" : "overlay", diagnostics);
638
+ }
220
639
  }
221
640
  return diagnostics;
222
641
  }
642
+ function validateMcp(value, api, diagnostics) {
643
+ if (typeof value === "string") {
644
+ validateMcpPath(value, "mcp", api, diagnostics);
645
+ return;
646
+ }
647
+ if (!isPlainRecord(value)) {
648
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", "mcp", "mcp must be a mount path or { path, authorization? }"));
649
+ return;
650
+ }
651
+ for (const key of Object.keys(value)) {
652
+ if (!MCP_PROPERTIES.has(key)) {
653
+ diagnostics.push(diagnostic("CONFIG_UNKNOWN_PROPERTY", `mcp.${key}`, `unknown mcp property: ${key}`));
654
+ }
655
+ }
656
+ if (!Object.hasOwn(value, "path")) {
657
+ diagnostics.push(diagnostic("CONFIG_REQUIRED", "mcp.path", "mcp.path is required"));
658
+ }
659
+ else if (typeof value.path !== "string") {
660
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", "mcp.path", "mcp.path must be a string"));
661
+ }
662
+ else {
663
+ validateMcpPath(value.path, "mcp.path", api, diagnostics);
664
+ }
665
+ if (Object.hasOwn(value, "authorization")) {
666
+ validateMcpAuthorization(value.authorization, diagnostics);
667
+ }
668
+ }
669
+ function validateMcpPath(value, path, api, diagnostics) {
670
+ const normalized = normalizeEntrypoint(value);
671
+ if (!normalized.ok) {
672
+ diagnostics.push(mountPathRefusalDiagnostic(path, normalized.message));
673
+ return;
674
+ }
675
+ if (api === undefined) {
676
+ return;
677
+ }
678
+ if (normalized.path === api) {
679
+ diagnostics.push(diagnostic("CONFIG_INVALID_VALUE", path, `${path} is the mount path api already has; mount the MCP endpoint at a path of its own`));
680
+ }
681
+ else if (normalized.path.startsWith(`${api}/_`)) {
682
+ diagnostics.push(diagnostic("CONFIG_INVALID_VALUE", path, `${path} is inside the protocol's namespace (the "_" segments under api); mount the MCP endpoint outside it`));
683
+ }
684
+ }
685
+ function validateMcpAuthorization(value, diagnostics) {
686
+ const path = "mcp.authorization";
687
+ if (!requireRecord(value, path, diagnostics)) {
688
+ return;
689
+ }
690
+ for (const key of Object.keys(value)) {
691
+ if (!MCP_AUTHORIZATION_PROPERTIES.has(key)) {
692
+ diagnostics.push(diagnostic("CONFIG_UNKNOWN_PROPERTY", `${path}.${key}`, `unknown mcp.authorization property: ${key}`));
693
+ }
694
+ }
695
+ const serversPath = `${path}.servers`;
696
+ if (!Object.hasOwn(value, "servers")) {
697
+ diagnostics.push(diagnostic("CONFIG_REQUIRED", serversPath, `${serversPath} is required`));
698
+ }
699
+ else if (!Array.isArray(value.servers)) {
700
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", serversPath, `${serversPath} must be a list of https URLs`));
701
+ }
702
+ else if (value.servers.length === 0) {
703
+ diagnostics.push(diagnostic("CONFIG_INVALID_VALUE", serversPath, `${serversPath} must name at least one authorization server`));
704
+ }
705
+ else {
706
+ value.servers.forEach((server, index) => {
707
+ if (!isHttpsUrl(server)) {
708
+ const serverPath = `${serversPath}[${index}]`;
709
+ diagnostics.push(diagnostic("CONFIG_INVALID_VALUE", serverPath, `${serverPath} must be an absolute https URL`));
710
+ }
711
+ });
712
+ }
713
+ const verifyPath = `${path}.verify`;
714
+ if (!Object.hasOwn(value, "verify")) {
715
+ diagnostics.push(diagnostic("CONFIG_REQUIRED", verifyPath, `${verifyPath} is required`));
716
+ }
717
+ else if (typeof value.verify !== "function") {
718
+ diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", verifyPath, `${verifyPath} must be a function: token in, the caller or null out`));
719
+ }
720
+ }
721
+ function isHttpsUrl(value) {
722
+ if (typeof value !== "string") {
723
+ return false;
724
+ }
725
+ try {
726
+ return new URL(value).protocol === "https:";
727
+ }
728
+ catch {
729
+ return false;
730
+ }
731
+ }