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