@aventara/core 0.1.0-pilot.4 → 1.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1478 -0
- package/README.md +275 -11
- package/dist/adapters/adapter-discovery-diagnostic.d.ts +4 -4
- package/dist/adapters/adapter-field.d.ts +11 -11
- package/dist/adapters/adapter-identifier.d.ts +6 -6
- package/dist/adapters/adapter-model.d.ts +17 -9
- package/dist/adapters/adapter-operation-executor.interface.d.ts +4 -4
- package/dist/adapters/adapter-operation.d.ts +3 -3
- package/dist/adapters/adapter-resource.d.ts +18 -9
- package/dist/adapters/adapter-sql-statement.d.ts +19 -0
- package/dist/adapters/adapter-transaction-executor.interface.d.ts +19 -0
- package/dist/adapters/adapter.interface.d.ts +49 -25
- package/dist/avz/avz-check.factory.d.ts +83 -0
- package/dist/avz/avz-check.factory.js +116 -0
- package/dist/avz/avz-check.registry.d.ts +35 -0
- package/dist/avz/avz-check.registry.js +9 -0
- package/dist/avz/avz.d.ts +203 -0
- package/dist/avz/avz.js +354 -0
- package/dist/avz/index.d.ts +7 -0
- package/dist/avz/index.js +1 -0
- package/dist/avz/json-shape.registry.d.ts +22 -0
- package/dist/avz/json-shape.registry.js +16 -0
- package/dist/avz/zod-issue.translator.d.ts +57 -0
- package/dist/avz/zod-issue.translator.js +149 -0
- package/dist/compiler/contract-compiler-diagnostic.d.ts +37 -5
- package/dist/compiler/contract.compiler.d.ts +18 -18
- package/dist/compiler/contract.compiler.js +595 -421
- package/dist/compiler/create-requirements.projector.d.ts +49 -0
- package/dist/compiler/create-requirements.projector.js +423 -0
- package/dist/compiler/field-validator.compiler.d.ts +47 -0
- package/dist/compiler/field-validator.compiler.js +238 -0
- package/dist/config/any-framework-config.d.ts +19 -12
- package/dist/config/config-diagnostic.d.ts +11 -4
- package/dist/config/config-layer.d.ts +15 -6
- package/dist/config/config.resolver.d.ts +75 -29
- package/dist/config/config.resolver.js +109 -44
- package/dist/config/config.validator.d.ts +7 -7
- package/dist/config/config.validator.js +586 -71
- package/dist/config/define-resource.d.ts +57 -0
- package/dist/config/define-resource.js +14 -0
- package/dist/config/effective-fields.d.ts +19 -23
- package/dist/config/entrypoint.normalizer.d.ts +1 -1
- package/dist/config/exact-framework-config.d.ts +280 -0
- package/dist/config/field-compute.resolver.d.ts +19 -0
- package/dist/config/field-compute.resolver.js +115 -0
- package/dist/config/field-pipeline-config.d.ts +47 -0
- package/dist/config/forced-operation.validator.d.ts +10 -0
- package/dist/config/forced-operation.validator.js +66 -0
- package/dist/config/framework-config.d.ts +111 -37
- package/dist/config/framework-exemptions-config.d.ts +91 -0
- package/dist/config/framework-exemptions-config.js +1 -0
- package/dist/config/framework-field-definition.d.ts +78 -0
- package/dist/config/framework-field-definition.js +28 -0
- package/dist/config/framework-resources-config.d.ts +116 -0
- package/dist/config/framework-resources-config.js +1 -0
- package/dist/config/pipeline-config.d.ts +56 -20
- package/dist/config/pipeline-tree.resolver.d.ts +79 -0
- package/dist/config/pipeline-tree.resolver.js +220 -0
- package/dist/config/stage-type.eraser.d.ts +65 -0
- package/dist/config/stage-type.eraser.js +1 -0
- package/dist/contracts/capability.d.ts +11 -11
- package/dist/contracts/client-contract-structure.validator.d.ts +344 -29
- package/dist/contracts/client-contract-structure.validator.js +248 -7
- package/dist/contracts/contract-canonicalizer.d.ts +19 -3
- package/dist/contracts/contract-canonicalizer.js +172 -27
- package/dist/contracts/contract-hash.d.ts +12 -7
- package/dist/contracts/contract-hash.js +3 -2
- package/dist/contracts/contract-limits.d.ts +4 -1
- package/dist/contracts/contract-limits.js +1 -0
- package/dist/contracts/contract-lookup.d.ts +5 -5
- package/dist/contracts/contract-protocol.d.ts +4 -4
- package/dist/contracts/contract.d.ts +31 -16
- package/dist/contracts/create-requirements.d.ts +35 -0
- package/dist/contracts/create-requirements.js +1 -0
- package/dist/contracts/enum-definition.d.ts +1 -1
- package/dist/contracts/field-check.d.ts +101 -0
- package/dist/contracts/field-check.js +1 -0
- package/dist/contracts/field.d.ts +61 -41
- package/dist/contracts/identifier.d.ts +4 -4
- package/dist/contracts/json-shape.d.ts +76 -0
- package/dist/contracts/json-shape.js +1 -0
- package/dist/contracts/lifecycle-trait.d.ts +2 -2
- package/dist/contracts/reference-write-limit.d.ts +7 -0
- package/dist/contracts/reference-write-limit.js +1 -0
- package/dist/contracts/resource-operations.d.ts +28 -28
- package/dist/contracts/resource.d.ts +15 -9
- package/dist/contracts/scalar-value-type.d.ts +25 -14
- package/dist/contracts/scalar.d.ts +3 -3
- package/dist/contracts/transaction-capability.d.ts +1 -1
- package/dist/contracts/wire-scalar-format.d.ts +4 -4
- package/dist/framework/contract-view.d.ts +234 -63
- package/dist/framework/framework-construction.error.d.ts +10 -10
- package/dist/framework/framework-construction.error.js +2 -2
- package/dist/framework/framework.d.ts +50 -24
- package/dist/framework/framework.factory.d.ts +8 -6
- package/dist/framework/framework.factory.js +95 -32
- package/dist/framework/operation-facade.d.ts +123 -52
- package/dist/framework/operation-facade.js +9 -1
- package/dist/index.d.ts +55 -82
- package/dist/index.js +6 -26
- package/dist/internal.d.ts +40 -0
- package/dist/internal.js +18 -0
- package/dist/operations/operation-arguments.d.ts +316 -201
- package/dist/operations/operation-call.d.ts +27 -28
- package/dist/operations/operation-identity.d.ts +12 -10
- package/dist/operations/operation-identity.js +3 -0
- package/dist/operations/operation-result.d.ts +21 -21
- package/dist/operations/operation-validator-core.d.ts +6 -6
- package/dist/operations/operation-validator-core.js +41 -12
- package/dist/operations/operation-validator.d.ts +24 -9
- package/dist/operations/operation-validator.js +26 -10
- package/dist/operations/projection-scope.resolver.d.ts +7 -7
- package/dist/operations/structural-limits.walker.d.ts +19 -0
- package/dist/operations/structural-limits.walker.js +489 -0
- package/dist/operations/validation-code.d.ts +11 -6
- package/dist/operations/validation-code.js +4 -0
- package/dist/operations/write-scope.resolver.d.ts +6 -6
- package/dist/protocol/absent-route.responder.d.ts +13 -11
- package/dist/protocol/absent-route.responder.js +42 -16
- package/dist/protocol/contract-document.responder.d.ts +3 -3
- package/dist/protocol/contract-document.responder.js +2 -2
- package/dist/protocol/http-request.decoder.d.ts +23 -13
- package/dist/protocol/http-request.decoder.js +19 -11
- package/dist/protocol/http-request.reader.d.ts +3 -3
- package/dist/protocol/http-response.encoder.d.ts +4 -4
- package/dist/protocol/http-response.encoder.js +94 -55
- package/dist/protocol/http-status.table.d.ts +2 -2
- package/dist/protocol/operation-response.guard.d.ts +2 -2
- package/dist/protocol/protocol-surface.d.ts +4 -4
- package/dist/protocol/protocol.binder.d.ts +52 -27
- package/dist/protocol/protocol.binder.js +14 -9
- package/dist/protocol/protocol.handler.d.ts +28 -15
- package/dist/protocol/protocol.handler.js +55 -10
- package/dist/protocol/unmounted-route.error.d.ts +19 -0
- package/dist/protocol/unmounted-route.error.js +10 -0
- package/dist/runtime/caller-data.copier.d.ts +59 -0
- package/dist/runtime/caller-data.copier.js +470 -0
- package/dist/runtime/caller-data.door.d.ts +56 -0
- package/dist/runtime/caller-data.door.js +67 -0
- package/dist/runtime/computed-read-runtime.d.ts +7 -6
- package/dist/runtime/computed-read-runtime.js +61 -55
- package/dist/runtime/computed-value.error.d.ts +13 -0
- package/dist/runtime/computed-value.error.js +9 -0
- package/dist/runtime/context-executor.runtime.d.ts +59 -0
- package/dist/runtime/context-executor.runtime.js +275 -0
- package/dist/runtime/create-requirement.d.ts +22 -57
- package/dist/runtime/create-requirement.js +140 -37
- package/dist/runtime/decimal.d.ts +2 -2
- package/dist/runtime/decimal.js +1 -1
- package/dist/runtime/diagnostics-reporter.d.ts +64 -22
- package/dist/runtime/diagnostics-reporter.js +35 -4
- package/dist/runtime/error-boundary.d.ts +73 -8
- package/dist/runtime/error-boundary.js +104 -48
- package/dist/runtime/execution-options.d.ts +10 -2
- package/dist/runtime/field-check.evaluator.d.ts +29 -0
- package/dist/runtime/field-check.evaluator.js +521 -0
- package/dist/runtime/field-compute.d.ts +64 -0
- package/dist/runtime/field-compute.js +1 -0
- package/dist/runtime/field-stage.plan.d.ts +15 -0
- package/dist/runtime/field-stage.plan.js +27 -0
- package/dist/runtime/field-stage.runtime.d.ts +35 -0
- package/dist/runtime/field-stage.runtime.js +143 -0
- package/dist/runtime/field-validator.runtime.d.ts +11 -0
- package/dist/runtime/field-validator.runtime.js +87 -0
- package/dist/runtime/framework-error-class.table.d.ts +33 -0
- package/dist/runtime/framework-error-class.table.js +25 -0
- package/dist/runtime/framework-error.d.ts +79 -7
- package/dist/runtime/framework-error.factory.d.ts +4 -0
- package/dist/runtime/framework-error.factory.js +15 -0
- package/dist/runtime/framework-error.js +23 -2
- package/dist/runtime/interceptor-next.error.d.ts +5 -0
- package/dist/runtime/interceptor-next.error.js +6 -0
- package/dist/runtime/json-value.d.ts +3 -3
- package/dist/runtime/level-lifecycle.runtime.d.ts +25 -0
- package/dist/runtime/level-lifecycle.runtime.js +42 -0
- package/dist/runtime/misuse-reporter.d.ts +32 -0
- package/dist/runtime/misuse-reporter.js +19 -0
- package/dist/runtime/mutation-planner.d.ts +19 -10
- package/dist/runtime/mutation-planner.js +62 -36
- package/dist/runtime/nested-write.walker.d.ts +32 -0
- package/dist/runtime/nested-write.walker.js +73 -0
- package/dist/runtime/operation-request.d.ts +35 -15
- package/dist/runtime/operation-response.d.ts +17 -17
- package/dist/runtime/operation-runtime.d.ts +125 -30
- package/dist/runtime/operation-runtime.js +423 -83
- package/dist/runtime/outcome-mapper.d.ts +16 -5
- package/dist/runtime/outcome-mapper.js +35 -8
- package/dist/runtime/output-validator.d.ts +16 -6
- package/dist/runtime/output-validator.js +12 -1
- package/dist/runtime/owned-execution.registry.d.ts +13 -0
- package/dist/runtime/owned-execution.registry.js +7 -0
- package/dist/runtime/pipe-output.validator.d.ts +33 -18
- package/dist/runtime/pipe-output.validator.js +12 -12
- package/dist/runtime/pipeline.d.ts +202 -28
- package/dist/runtime/readonly-value.d.ts +32 -0
- package/dist/runtime/readonly-value.js +141 -0
- package/dist/runtime/referential-action.d.ts +7 -0
- package/dist/runtime/request-envelope.validator.d.ts +5 -0
- package/dist/runtime/request-envelope.validator.js +123 -0
- package/dist/runtime/request-id.d.ts +1 -1
- package/dist/runtime/request-state.d.ts +14 -0
- package/dist/runtime/request-state.js +1 -0
- package/dist/runtime/request-state.resolver.d.ts +12 -0
- package/dist/runtime/request-state.resolver.js +20 -0
- package/dist/runtime/scalar-runtime.d.ts +56 -13
- package/dist/runtime/scalar-runtime.js +167 -86
- package/dist/transactions/deferred-operation.d.ts +35 -20
- package/dist/transactions/operation-arguments-encoder.d.ts +3 -3
- package/dist/transactions/operation-arguments-encoder.js +2 -2
- package/dist/transactions/sql-statement.d.ts +77 -0
- package/dist/transactions/sql-statement.js +47 -0
- package/dist/transactions/sql-statement.validator.d.ts +44 -0
- package/dist/transactions/sql-statement.validator.js +134 -0
- package/dist/transactions/transaction-envelope.d.ts +4 -4
- package/dist/transactions/transaction-integrity-error.d.ts +6 -6
- package/dist/transactions/transaction-integrity-error.js +5 -2
- package/dist/transactions/transaction-plan-assembler.d.ts +23 -9
- package/dist/transactions/transaction-plan-assembler.js +195 -82
- package/dist/transactions/transaction-plan-validator.d.ts +53 -21
- package/dist/transactions/transaction-plan-validator.js +246 -163
- package/dist/transactions/transaction-reference-collector.d.ts +30 -0
- package/dist/transactions/transaction-reference-collector.js +204 -0
- package/dist/transactions/transaction-reference-resolver.d.ts +19 -2
- package/dist/transactions/transaction-reference-resolver.js +27 -10
- package/dist/transactions/transaction-reference-validator.d.ts +34 -4
- package/dist/transactions/transaction-reference-validator.js +82 -43
- package/dist/transactions/transaction-reference.d.ts +30 -58
- package/dist/transactions/transaction-request.d.ts +20 -7
- package/dist/transactions/transaction-response.d.ts +12 -7
- package/dist/transactions/transaction-response.js +24 -1
- package/dist/transactions/transaction-runtime.d.ts +11 -8
- package/dist/transactions/transaction-runtime.js +167 -50
- package/dist/transactions/transaction-settlement.runtime.d.ts +51 -0
- package/dist/transactions/transaction-settlement.runtime.js +120 -0
- package/dist/transactions/transaction-slot-path-error.d.ts +4 -4
- package/dist/transactions/transaction-slot-path-error.js +2 -2
- package/dist/transactions/transaction-slot-writer.js +2 -2
- package/dist/transactions/transaction-stage.runtime.d.ts +81 -0
- package/dist/transactions/transaction-stage.runtime.js +123 -0
- package/package.json +18 -3
- package/src/runtime/field-check.evaluator.ts +720 -0
- package/dist/config/define-fields.d.ts +0 -35
- package/dist/config/define-fields.js +0 -3
- package/dist/config/framework-behaviors-config.d.ts +0 -30
- package/dist/config/framework-fields-config.d.ts +0 -36
- package/dist/config/framework-restrictions-config.d.ts +0 -142
- package/dist/operations/operation-boolean-nodes.d.ts +0 -11
- package/dist/operations/operation-boolean-nodes.js +0 -283
- package/dist/operations/operation-nesting.d.ts +0 -8
- package/dist/operations/operation-nesting.js +0 -307
- package/dist/runtime/field-behavior.d.ts +0 -48
- /package/dist/{config/framework-behaviors-config.js → adapters/adapter-sql-statement.js} +0 -0
- /package/dist/{config/framework-fields-config.js → adapters/adapter-transaction-executor.interface.js} +0 -0
- /package/dist/config/{framework-restrictions-config.js → exact-framework-config.js} +0 -0
- /package/dist/{runtime/field-behavior.js → config/field-pipeline-config.js} +0 -0
|
@@ -1,33 +1,76 @@
|
|
|
1
1
|
import { TRANSACTION_CAPABILITIES } from "../contracts/transaction-capability.js";
|
|
2
2
|
import { normalizeEntrypoint } from "./entrypoint.normalizer.js";
|
|
3
|
-
|
|
4
|
-
|
|
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",
|
|
9
|
+
"misuse",
|
|
13
10
|
"application",
|
|
14
11
|
"client",
|
|
12
|
+
"agent",
|
|
15
13
|
]);
|
|
16
|
-
const
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
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",
|
|
20
23
|
"limits",
|
|
21
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",
|
|
22
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",
|
|
23
63
|
]);
|
|
64
|
+
const OPERATION_VARIANTS = new Set(["first", "unique", "many", "count", "one"]);
|
|
24
65
|
const TRANSACTION_CAPABILITY_SET = new Set(TRANSACTION_CAPABILITIES);
|
|
66
|
+
const AVZ_FORMAT_SET = new Set(AVZ_FORMATS);
|
|
25
67
|
const LIMIT_PROPERTIES = new Set([
|
|
26
68
|
"maxRequestBytes",
|
|
27
69
|
"maxNestingDepth",
|
|
28
70
|
"maxListLimit",
|
|
29
71
|
"maxTransactionOperations",
|
|
30
72
|
"maxBooleanNodes",
|
|
73
|
+
"maxJsonDepth",
|
|
31
74
|
]);
|
|
32
75
|
const PIPELINE_PROPERTIES = new Set([
|
|
33
76
|
"guards",
|
|
@@ -47,20 +90,21 @@ function isPlainRecord(value) {
|
|
|
47
90
|
function diagnostic(code, path, message) {
|
|
48
91
|
return { code, path, message };
|
|
49
92
|
}
|
|
50
|
-
|
|
51
|
-
return
|
|
93
|
+
function childPath(path, key) {
|
|
94
|
+
return path === "" ? key : `${path}.${key}`;
|
|
52
95
|
}
|
|
53
|
-
function
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (
|
|
58
|
-
|
|
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;
|
|
59
102
|
}
|
|
103
|
+
diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", path, `${path} must be an object`));
|
|
104
|
+
return false;
|
|
60
105
|
}
|
|
61
106
|
function validateLimits(value, path, diagnostics) {
|
|
62
|
-
if (!
|
|
63
|
-
diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", path, `${path} must be an object`));
|
|
107
|
+
if (!requireRecord(value, path, diagnostics)) {
|
|
64
108
|
return;
|
|
65
109
|
}
|
|
66
110
|
for (const [key, limit] of Object.entries(value)) {
|
|
@@ -77,7 +121,7 @@ function validateLimits(value, path, diagnostics) {
|
|
|
77
121
|
}
|
|
78
122
|
}
|
|
79
123
|
}
|
|
80
|
-
function
|
|
124
|
+
function validateArray(value, key, path, diagnostics) {
|
|
81
125
|
if (!Object.hasOwn(value, key)) {
|
|
82
126
|
return;
|
|
83
127
|
}
|
|
@@ -87,8 +131,7 @@ function validatePipelineArray(value, key, path, diagnostics) {
|
|
|
87
131
|
}
|
|
88
132
|
}
|
|
89
133
|
function validatePipelineHooks(value, path, diagnostics) {
|
|
90
|
-
if (!
|
|
91
|
-
diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", path, `${path} must be an object`));
|
|
134
|
+
if (!requireRecord(value, path, diagnostics)) {
|
|
92
135
|
return;
|
|
93
136
|
}
|
|
94
137
|
for (const key of Object.keys(value)) {
|
|
@@ -96,48 +139,432 @@ function validatePipelineHooks(value, path, diagnostics) {
|
|
|
96
139
|
diagnostics.push(diagnostic("CONFIG_UNKNOWN_PROPERTY", `${path}.${key}`, `unknown pipeline hook property: ${key}`));
|
|
97
140
|
}
|
|
98
141
|
}
|
|
99
|
-
|
|
100
|
-
|
|
142
|
+
validateArray(value, "before", path, diagnostics);
|
|
143
|
+
validateArray(value, "after", path, diagnostics);
|
|
101
144
|
}
|
|
102
145
|
function validatePipelines(value, path, diagnostics) {
|
|
103
|
-
if (!
|
|
104
|
-
diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", path, `${path} must be an object`));
|
|
146
|
+
if (!requireRecord(value, path, diagnostics)) {
|
|
105
147
|
return;
|
|
106
148
|
}
|
|
107
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
|
+
}
|
|
108
154
|
if (!PIPELINE_PROPERTIES.has(key)) {
|
|
109
155
|
diagnostics.push(diagnostic("CONFIG_UNKNOWN_PROPERTY", `${path}.${key}`, `unknown pipeline property: ${key}`));
|
|
110
156
|
}
|
|
111
157
|
}
|
|
112
158
|
for (const key of ["guards", "pipes", "interceptors", "filters"]) {
|
|
113
|
-
|
|
159
|
+
validateArray(value, key, path, diagnostics);
|
|
114
160
|
}
|
|
115
161
|
if (Object.hasOwn(value, "hooks")) {
|
|
116
162
|
validatePipelineHooks(value.hooks, `${path}.hooks`, diagnostics);
|
|
117
163
|
}
|
|
118
164
|
}
|
|
119
|
-
function
|
|
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
|
+
}
|
|
120
465
|
if (!isPlainRecord(value)) {
|
|
121
|
-
diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", path, `${path} must be
|
|
466
|
+
diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", path, `${path} must be true, false or { operations, fields }; a Resource takes no list`));
|
|
122
467
|
return;
|
|
123
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";
|
|
124
511
|
for (const key of Object.keys(value)) {
|
|
125
|
-
|
|
126
|
-
|
|
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;
|
|
127
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"`));
|
|
540
|
+
}
|
|
541
|
+
}
|
|
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"`));
|
|
128
559
|
}
|
|
129
|
-
validateNamedObject(value, "fields", `${path}.fields`, diagnostics);
|
|
130
|
-
validateNamedObject(value, "behaviors", `${path}.behaviors`, diagnostics);
|
|
131
|
-
validateNamedObject(value, "restrictions", `${path}.restrictions`, diagnostics);
|
|
132
560
|
if (Object.hasOwn(value, "limits")) {
|
|
133
|
-
validateLimits(value.limits,
|
|
561
|
+
validateLimits(value.limits, childPath(path, "limits"), diagnostics);
|
|
134
562
|
}
|
|
135
563
|
if (Object.hasOwn(value, "pipelines")) {
|
|
136
|
-
validatePipelines(value.pipelines,
|
|
564
|
+
validatePipelines(value.pipelines, childPath(path, "pipelines"), diagnostics);
|
|
137
565
|
}
|
|
138
|
-
if (Object.hasOwn(value, "
|
|
139
|
-
|
|
140
|
-
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);
|
|
141
568
|
}
|
|
142
569
|
}
|
|
143
570
|
function validateAdapter(value, diagnostics) {
|
|
@@ -165,52 +592,140 @@ export function validateFrameworkConfig(value) {
|
|
|
165
592
|
diagnostic("CONFIG_INVALID_TYPE", "$", "framework configuration must be an object"),
|
|
166
593
|
];
|
|
167
594
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
595
|
+
validateLayer(value, "", "root", diagnostics);
|
|
596
|
+
let api;
|
|
597
|
+
if (!Object.hasOwn(value, "api")) {
|
|
598
|
+
diagnostics.push(diagnostic("CONFIG_REQUIRED", "api", "api is required"));
|
|
172
599
|
}
|
|
173
|
-
if (
|
|
174
|
-
diagnostics.push(diagnostic("
|
|
175
|
-
}
|
|
176
|
-
else if (typeof value.entrypoint !== "string") {
|
|
177
|
-
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"));
|
|
178
602
|
}
|
|
179
603
|
else {
|
|
180
|
-
const normalized = normalizeEntrypoint(value.
|
|
181
|
-
if (
|
|
182
|
-
|
|
604
|
+
const normalized = normalizeEntrypoint(value.api);
|
|
605
|
+
if (normalized.ok) {
|
|
606
|
+
api = normalized.path;
|
|
607
|
+
}
|
|
608
|
+
else {
|
|
609
|
+
diagnostics.push(mountPathRefusalDiagnostic("api", normalized.message));
|
|
183
610
|
}
|
|
184
611
|
}
|
|
612
|
+
if (Object.hasOwn(value, "mcp")) {
|
|
613
|
+
validateMcp(value.mcp, api, diagnostics);
|
|
614
|
+
}
|
|
185
615
|
if (!Object.hasOwn(value, "adapter")) {
|
|
186
616
|
diagnostics.push(diagnostic("CONFIG_REQUIRED", "adapter", "adapter is required"));
|
|
187
617
|
}
|
|
188
618
|
else {
|
|
189
619
|
validateAdapter(value.adapter, diagnostics);
|
|
190
620
|
}
|
|
191
|
-
validateNamedObject(value, "fields", "fields", diagnostics);
|
|
192
|
-
validateNamedObject(value, "behaviors", "behaviors", diagnostics);
|
|
193
|
-
validateNamedObject(value, "restrictions", "restrictions", diagnostics);
|
|
194
|
-
if (Object.hasOwn(value, "limits")) {
|
|
195
|
-
validateLimits(value.limits, "limits", diagnostics);
|
|
196
|
-
}
|
|
197
|
-
if (Object.hasOwn(value, "pipelines")) {
|
|
198
|
-
validatePipelines(value.pipelines, "pipelines", diagnostics);
|
|
199
|
-
}
|
|
200
|
-
if (Object.hasOwn(value, "transactions") &&
|
|
201
|
-
!TRANSACTION_CAPABILITY_SET.has(value.transactions)) {
|
|
202
|
-
diagnostics.push(diagnostic("CONFIG_INVALID_VALUE", "transactions", 'transactions must be "none" or "interactive"'));
|
|
203
|
-
}
|
|
204
621
|
if (Object.hasOwn(value, "diagnostics") &&
|
|
205
622
|
value.diagnostics !== undefined &&
|
|
206
623
|
typeof value.diagnostics !== "function") {
|
|
207
624
|
diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", "diagnostics", "diagnostics must be a function when provided"));
|
|
208
625
|
}
|
|
209
|
-
if (Object.hasOwn(value, "
|
|
210
|
-
|
|
626
|
+
if (Object.hasOwn(value, "misuse") &&
|
|
627
|
+
value.misuse !== undefined &&
|
|
628
|
+
typeof value.misuse !== "function") {
|
|
629
|
+
diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", "misuse", "misuse must be a function when provided"));
|
|
211
630
|
}
|
|
212
|
-
|
|
213
|
-
|
|
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
|
+
}
|
|
214
639
|
}
|
|
215
640
|
return diagnostics;
|
|
216
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
|
+
}
|