@aventara/core 1.0.0-rc.0 → 1.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1434 -0
- package/ERRORS.md +142 -0
- package/README.md +247 -57
- package/dist/adapters/adapter-discovery-diagnostic.d.ts +4 -4
- package/dist/adapters/adapter-discovery-diagnostic.guidance.d.ts +6 -0
- package/dist/adapters/adapter-discovery-diagnostic.guidance.js +7 -0
- package/dist/adapters/adapter-field.d.ts +25 -11
- package/dist/adapters/adapter-identifier.d.ts +6 -6
- package/dist/adapters/adapter-model.d.ts +18 -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 +59 -29
- 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/{json → avz}/avz.js +150 -8
- package/dist/{json → avz}/index.d.ts +2 -1
- package/dist/{json → avz}/json-shape.registry.d.ts +7 -7
- package/dist/{json → avz}/zod-issue.translator.d.ts +8 -2
- package/dist/{json → avz}/zod-issue.translator.js +7 -0
- package/dist/compiler/contract-compiler-diagnostic.d.ts +31 -5
- package/dist/compiler/contract-compiler-diagnostic.guidance.d.ts +6 -0
- package/dist/compiler/contract-compiler-diagnostic.guidance.js +97 -0
- package/dist/compiler/contract.compiler.d.ts +18 -18
- package/dist/compiler/contract.compiler.js +637 -442
- 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/computed-fields.d.ts +62 -0
- package/dist/config/computed-fields.js +80 -0
- package/dist/config/config-diagnostic.d.ts +11 -4
- package/dist/config/config-diagnostic.guidance.d.ts +6 -0
- package/dist/config/config-diagnostic.guidance.js +32 -0
- package/dist/config/config-layer.d.ts +15 -6
- package/dist/config/config.resolver.d.ts +74 -31
- package/dist/config/config.resolver.js +108 -44
- package/dist/config/config.validator.d.ts +7 -7
- package/dist/config/config.validator.js +581 -72
- 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 +308 -0
- package/dist/config/field-compute.resolver.d.ts +21 -0
- package/dist/config/field-compute.resolver.js +123 -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 -39
- 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 +122 -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 +21 -12
- package/dist/contracts/capability.js +2 -0
- package/dist/contracts/client-contract-structure.validator.d.ts +388 -63
- package/dist/contracts/client-contract-structure.validator.js +156 -10
- package/dist/contracts/contract-canonicalizer.d.ts +26 -3
- package/dist/contracts/contract-canonicalizer.js +156 -29
- 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 +24 -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 +81 -42
- package/dist/contracts/identifier.d.ts +4 -4
- package/dist/contracts/json-shape.d.ts +12 -1
- package/dist/contracts/lifecycle-trait.d.ts +2 -2
- 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 +15 -15
- 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 +213 -147
- package/dist/framework/framework-construction.error.d.ts +19 -10
- package/dist/framework/framework-construction.error.js +7 -3
- package/dist/framework/framework.d.ts +47 -23
- package/dist/framework/framework.factory.d.ts +8 -6
- package/dist/framework/framework.factory.js +49 -22
- package/dist/framework/operation-facade.d.ts +123 -52
- package/dist/framework/operation-facade.js +9 -1
- package/dist/index.d.ts +57 -50
- package/dist/index.js +7 -6
- package/dist/internal.d.ts +17 -10
- package/dist/internal.js +4 -1
- package/dist/operations/database-refusal.d.ts +51 -0
- package/dist/operations/database-refusal.js +45 -0
- package/dist/operations/operation-arguments.d.ts +343 -207
- package/dist/operations/operation-call.d.ts +27 -28
- package/dist/operations/operation-identity.d.ts +10 -10
- 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 +99 -6
- package/dist/operations/operation-validator.d.ts +9 -9
- package/dist/operations/projection-scope.resolver.d.ts +6 -6
- package/dist/operations/structural-limits.walker.d.ts +6 -6
- package/dist/operations/structural-limits.walker.js +145 -0
- package/dist/operations/validation-code.d.ts +11 -6
- package/dist/operations/validation-code.guidance.d.ts +6 -0
- package/dist/operations/validation-code.guidance.js +90 -0
- package/dist/operations/validation-code.js +4 -0
- package/dist/operations/write-scope.resolver.d.ts +10 -6
- package/dist/operations/write-scope.resolver.js +2 -0
- package/dist/protocol/absent-route.responder.d.ts +13 -12
- package/dist/protocol/absent-route.responder.js +19 -19
- 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 +17 -13
- package/dist/protocol/http-request.decoder.js +10 -10
- 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 +50 -27
- package/dist/protocol/protocol.binder.js +12 -9
- package/dist/protocol/protocol.handler.d.ts +22 -13
- package/dist/protocol/protocol.handler.js +91 -17
- package/dist/protocol/unmounted-route.error.d.ts +4 -4
- package/dist/protocol/unmounted-route.error.js +2 -2
- package/dist/runtime/caller-data.copier.d.ts +22 -5
- package/dist/runtime/caller-data.copier.js +31 -14
- package/dist/runtime/caller-data.door.d.ts +17 -11
- package/dist/runtime/caller-data.door.js +6 -4
- package/dist/runtime/computed-read-runtime.d.ts +7 -6
- package/dist/runtime/computed-read-runtime.js +76 -50
- package/dist/runtime/computed-value.error.d.ts +7 -6
- package/dist/runtime/computed-value.error.js +5 -4
- 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 +44 -79
- package/dist/runtime/create-requirement.js +213 -73
- package/dist/runtime/decimal.d.ts +2 -2
- package/dist/runtime/decimal.js +1 -1
- package/dist/runtime/diagnostics-reporter.d.ts +54 -27
- package/dist/runtime/diagnostics-reporter.js +24 -0
- package/dist/runtime/error-boundary.d.ts +65 -8
- package/dist/runtime/error-boundary.js +49 -19
- package/dist/runtime/error-guidance.d.ts +12 -0
- package/dist/runtime/error-guidance.js +1 -0
- package/dist/runtime/error.catalog.d.ts +49 -0
- package/dist/runtime/error.catalog.js +49 -0
- 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 +37 -0
- package/dist/runtime/field-stage.runtime.js +147 -0
- package/dist/runtime/field-validator.runtime.d.ts +19 -0
- package/dist/runtime/field-validator.runtime.js +92 -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 +68 -8
- 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 +11 -6
- package/dist/runtime/level-lifecycle.runtime.d.ts +25 -0
- package/dist/runtime/level-lifecycle.runtime.js +39 -0
- package/dist/runtime/misuse-reporter.d.ts +12 -12
- package/dist/runtime/mutation-planner.d.ts +19 -10
- package/dist/runtime/mutation-planner.js +48 -35
- package/dist/runtime/nested-write.walker.d.ts +32 -0
- package/dist/runtime/nested-write.walker.js +73 -0
- package/dist/runtime/operation-code.guidance.d.ts +6 -0
- package/dist/runtime/operation-code.guidance.js +167 -0
- package/dist/runtime/operation-request.d.ts +33 -6
- package/dist/runtime/operation-response.d.ts +17 -17
- package/dist/runtime/operation-runtime.d.ts +127 -25
- package/dist/runtime/operation-runtime.js +441 -78
- package/dist/runtime/outcome-mapper.d.ts +16 -5
- package/dist/runtime/outcome-mapper.js +23 -6
- package/dist/runtime/output-validator.d.ts +10 -10
- package/dist/runtime/output-validator.js +3 -3
- package/dist/runtime/owned-execution.registry.d.ts +16 -6
- package/dist/runtime/pipe-output.validator.d.ts +43 -18
- package/dist/runtime/pipe-output.validator.js +15 -13
- package/dist/runtime/pipeline.d.ts +223 -34
- package/dist/runtime/readonly-value.d.ts +43 -0
- package/dist/runtime/readonly-value.js +150 -0
- package/dist/runtime/request-envelope.validator.d.ts +3 -3
- package/dist/runtime/request-envelope.validator.js +12 -7
- 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 +35 -16
- package/dist/runtime/scalar-runtime.js +102 -34
- package/dist/runtime/unsent-value.normalizer.d.ts +15 -0
- package/dist/runtime/unsent-value.normalizer.js +128 -0
- 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/operation-fingerprint.js +2 -2
- package/dist/transactions/sql-statement.d.ts +85 -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 +167 -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 +21 -11
- package/dist/transactions/transaction-plan-assembler.js +169 -32
- package/dist/transactions/transaction-plan-validator.d.ts +45 -20
- package/dist/transactions/transaction-plan-validator.js +96 -38
- package/dist/transactions/transaction-reference-collector.d.ts +5 -5
- package/dist/transactions/transaction-reference-resolver.d.ts +19 -2
- package/dist/transactions/transaction-reference-resolver.js +26 -14
- package/dist/transactions/transaction-reference-validator.d.ts +76 -4
- package/dist/transactions/transaction-reference-validator.js +129 -71
- package/dist/transactions/transaction-reference.d.ts +13 -13
- package/dist/transactions/transaction-reference.js +5 -0
- package/dist/transactions/transaction-request.d.ts +18 -5
- package/dist/transactions/transaction-response.d.ts +6 -6
- package/dist/transactions/transaction-runtime.d.ts +8 -7
- package/dist/transactions/transaction-runtime.js +191 -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 +82 -0
- package/dist/transactions/transaction-stage.runtime.js +126 -0
- package/package.json +9 -7
- package/src/runtime/field-check.evaluator.ts +720 -0
- package/dist/config/define-fields.d.ts +0 -42
- 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 -42
- package/dist/config/framework-restrictions-config.d.ts +0 -142
- package/dist/json/avz.d.ts +0 -96
- 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/{json → avz}/index.js +0 -0
- /package/dist/{json → avz}/json-shape.registry.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,34 +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",
|
|
13
9
|
"misuse",
|
|
14
10
|
"application",
|
|
15
11
|
"client",
|
|
12
|
+
"agent",
|
|
16
13
|
]);
|
|
17
|
-
const
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
52
|
-
return
|
|
93
|
+
function childPath(path, key) {
|
|
94
|
+
return path === "" ? key : `${path}.${key}`;
|
|
53
95
|
}
|
|
54
|
-
function
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (
|
|
59
|
-
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
-
|
|
101
|
-
|
|
142
|
+
validateArray(value, "before", path, diagnostics);
|
|
143
|
+
validateArray(value, "after", path, diagnostics);
|
|
102
144
|
}
|
|
103
145
|
function validatePipelines(value, path, diagnostics) {
|
|
104
|
-
if (!
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
127
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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,
|
|
561
|
+
validateLimits(value.limits, childPath(path, "limits"), diagnostics);
|
|
135
562
|
}
|
|
136
563
|
if (Object.hasOwn(value, "pipelines")) {
|
|
137
|
-
validatePipelines(value.pipelines,
|
|
564
|
+
validatePipelines(value.pipelines, childPath(path, "pipelines"), diagnostics);
|
|
138
565
|
}
|
|
139
|
-
if (Object.hasOwn(value, "
|
|
140
|
-
|
|
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
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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.
|
|
178
|
-
diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", "
|
|
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.
|
|
182
|
-
if (
|
|
183
|
-
|
|
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
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
+
}
|