@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
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { avzCheckOf, } from "../avz/avz-check.registry.js";
|
|
2
|
+
import { FIELD_CHECK_SUBJECTS, } from "../runtime/field-check.evaluator.js";
|
|
3
|
+
function isRecord(value) {
|
|
4
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5
|
+
}
|
|
6
|
+
function own(record, key) {
|
|
7
|
+
return Object.hasOwn(record, key) ? record[key] : undefined;
|
|
8
|
+
}
|
|
9
|
+
function scalarOf(field) {
|
|
10
|
+
const type = field.type;
|
|
11
|
+
return typeof type?.scalar === "string" ? type.scalar : undefined;
|
|
12
|
+
}
|
|
13
|
+
function subjectOf(field) {
|
|
14
|
+
const scalar = scalarOf(field);
|
|
15
|
+
return scalar === undefined ? undefined : own(FIELD_CHECK_SUBJECTS, scalar);
|
|
16
|
+
}
|
|
17
|
+
function describeField(field) {
|
|
18
|
+
const scalar = scalarOf(field) ?? "enum";
|
|
19
|
+
const noun = `${scalar}${field.list === true ? " list" : ""} field`;
|
|
20
|
+
return `${/^[aeiou]/.test(scalar) ? "an" : "a"} ${noun}`;
|
|
21
|
+
}
|
|
22
|
+
function describeCheck(check) {
|
|
23
|
+
switch (check.check) {
|
|
24
|
+
case "less_than":
|
|
25
|
+
return check.inclusive ? "AvZ.lte" : "AvZ.lt";
|
|
26
|
+
case "greater_than":
|
|
27
|
+
return check.inclusive ? "AvZ.gte" : "AvZ.gt";
|
|
28
|
+
case "multiple_of":
|
|
29
|
+
return "AvZ.multipleOf";
|
|
30
|
+
case "max_length":
|
|
31
|
+
return check.list === true ? "AvZ.maxItems" : "AvZ.maxLength";
|
|
32
|
+
case "min_length":
|
|
33
|
+
return check.list === true ? "AvZ.minItems" : "AvZ.minLength";
|
|
34
|
+
case "length_equals":
|
|
35
|
+
return "AvZ.length";
|
|
36
|
+
case "string_format":
|
|
37
|
+
return `a string format check (${check.format})`;
|
|
38
|
+
case "max_size":
|
|
39
|
+
return "AvZ.maxSize";
|
|
40
|
+
case "min_size":
|
|
41
|
+
return "AvZ.minSize";
|
|
42
|
+
case "size_equals":
|
|
43
|
+
return "AvZ.size";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function boundFor(value, subject, multiple) {
|
|
47
|
+
switch (subject) {
|
|
48
|
+
case "number":
|
|
49
|
+
return typeof value === "number"
|
|
50
|
+
? { value }
|
|
51
|
+
: { fault: "an int or float field's bound is a number" };
|
|
52
|
+
case "decimal":
|
|
53
|
+
return value instanceof Date
|
|
54
|
+
? { fault: "a decimal field's bound is a number or a bigint" }
|
|
55
|
+
: { value: String(value) };
|
|
56
|
+
case "bigint":
|
|
57
|
+
return typeof value === "bigint" ||
|
|
58
|
+
(typeof value === "number" && Number.isSafeInteger(value))
|
|
59
|
+
? { value: String(value) }
|
|
60
|
+
: { fault: "a bigint field's bound is a bigint" };
|
|
61
|
+
case "datetime":
|
|
62
|
+
return multiple
|
|
63
|
+
? { fault: "a datetime takes bounds, not multiples" }
|
|
64
|
+
: value instanceof Date
|
|
65
|
+
? { value: value.toISOString() }
|
|
66
|
+
: { fault: "a datetime field's bound is a Date" };
|
|
67
|
+
default:
|
|
68
|
+
return {
|
|
69
|
+
fault: "bounds apply to number, bigint, decimal and datetime fields",
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function compileCheck(check, field) {
|
|
74
|
+
const subject = subjectOf(field);
|
|
75
|
+
switch (check.check) {
|
|
76
|
+
case "less_than":
|
|
77
|
+
case "greater_than":
|
|
78
|
+
case "multiple_of": {
|
|
79
|
+
const written = boundFor(check.value, subject, check.check === "multiple_of");
|
|
80
|
+
if ("fault" in written) {
|
|
81
|
+
return written;
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
check: check.check === "multiple_of"
|
|
85
|
+
? { check: "multiple_of", value: written.value }
|
|
86
|
+
: {
|
|
87
|
+
check: check.check,
|
|
88
|
+
value: written.value,
|
|
89
|
+
inclusive: check.inclusive,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
case "max_length":
|
|
94
|
+
case "min_length":
|
|
95
|
+
if (check.list === true) {
|
|
96
|
+
return field.list === true
|
|
97
|
+
? { check }
|
|
98
|
+
: {
|
|
99
|
+
fault: "AvZ.minItems and AvZ.maxItems check a list's length; the field is not a list",
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return subject === "string"
|
|
103
|
+
? { check }
|
|
104
|
+
: { fault: "length checks apply to string fields" };
|
|
105
|
+
case "length_equals":
|
|
106
|
+
case "string_format":
|
|
107
|
+
return subject === "string"
|
|
108
|
+
? { check }
|
|
109
|
+
: { fault: "string checks apply to string fields" };
|
|
110
|
+
case "max_size":
|
|
111
|
+
case "min_size":
|
|
112
|
+
case "size_equals":
|
|
113
|
+
return subject === "bytes"
|
|
114
|
+
? { check }
|
|
115
|
+
: { fault: "size checks apply to bytes fields" };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function label(resource, field) {
|
|
119
|
+
return `${resource}.${field}`;
|
|
120
|
+
}
|
|
121
|
+
export function checkLayerValidators(resources, layer, tree, diagnostics) {
|
|
122
|
+
if (!isRecord(tree)) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
for (const [resourceName, entry] of Object.entries(tree)) {
|
|
126
|
+
const resource = own(resources, resourceName);
|
|
127
|
+
if (resource === undefined || !isRecord(entry) || !isRecord(entry.fields)) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
for (const [fieldName, fieldEntry] of Object.entries(entry.fields)) {
|
|
131
|
+
const field = own(resource.fields, fieldName);
|
|
132
|
+
if (field === undefined ||
|
|
133
|
+
!isRecord(fieldEntry) ||
|
|
134
|
+
!isRecord(fieldEntry.pipelines) ||
|
|
135
|
+
!Object.hasOwn(fieldEntry.pipelines, "validators")) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const path = `${layer}.resources.${resourceName}.fields.${fieldName}.pipelines.validators`;
|
|
139
|
+
const refuse = (at, message) => diagnostics.push({
|
|
140
|
+
code: "COMPILER_INVALID_VALIDATOR",
|
|
141
|
+
path: at,
|
|
142
|
+
message,
|
|
143
|
+
});
|
|
144
|
+
if (field.kind === "relation") {
|
|
145
|
+
refuse(path, `relation field "${label(resourceName, fieldName)}" takes write guards only; validators check a scalar field's value`);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (scalarOf(field) === "json") {
|
|
149
|
+
refuse(path, `field "${label(resourceName, fieldName)}" is a Json field: Json fields carry their checks in definition.json (AvZ.string().check(AvZ.maxLength(80)), …)`);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const validators = fieldEntry.pipelines.validators;
|
|
153
|
+
if (!Array.isArray(validators)) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
for (const [index, value] of validators.entries()) {
|
|
157
|
+
const at = `${path}[${index}]`;
|
|
158
|
+
const check = avzCheckOf(value);
|
|
159
|
+
if (check === undefined) {
|
|
160
|
+
refuse(at, `field "${label(resourceName, fieldName)}": a validator must be a check built with AvZ from "@aventara/core/avz" (AvZ.maxLength(80), AvZ.email(), …); a Zod check or any other value is not accepted`);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const compiled = compileCheck(check, field);
|
|
164
|
+
if ("fault" in compiled) {
|
|
165
|
+
refuse(at, `${describeCheck(check)} does not fit field "${label(resourceName, fieldName)}", ${describeField(field)}: ${compiled.fault}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
export function compileValidatorTable(resources, tree) {
|
|
172
|
+
const table = {};
|
|
173
|
+
for (const [resourceName, pipelines] of Object.entries(tree.resources)) {
|
|
174
|
+
const resource = own(resources, resourceName);
|
|
175
|
+
if (resource === undefined) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
for (const [fieldName, field] of Object.entries(resource.fields)) {
|
|
179
|
+
const stages = own(pipelines.fields, fieldName);
|
|
180
|
+
if (stages === undefined ||
|
|
181
|
+
field.kind !== "scalar" ||
|
|
182
|
+
scalarOf(field) === "json" ||
|
|
183
|
+
stages.validators.length === 0) {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const checks = stages.validators.flatMap((value) => {
|
|
187
|
+
const check = avzCheckOf(value);
|
|
188
|
+
const compiled = check === undefined ? undefined : compileCheck(check, field);
|
|
189
|
+
return compiled !== undefined && "check" in compiled
|
|
190
|
+
? [compiled.check]
|
|
191
|
+
: [];
|
|
192
|
+
});
|
|
193
|
+
if (checks.length > 0) {
|
|
194
|
+
table[resourceName] ??= {};
|
|
195
|
+
table[resourceName][fieldName] = {
|
|
196
|
+
checks,
|
|
197
|
+
subject: subjectOf(field),
|
|
198
|
+
list: field.list === true,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return table;
|
|
204
|
+
}
|
|
205
|
+
export function applyValidators(resources, table) {
|
|
206
|
+
for (const [resourceName, fields] of Object.entries(table)) {
|
|
207
|
+
const resource = own(resources, resourceName);
|
|
208
|
+
for (const [fieldName, compiled] of Object.entries(fields)) {
|
|
209
|
+
const field = resource === undefined ? undefined : own(resource.fields, fieldName);
|
|
210
|
+
if (field !== undefined && field.kind === "scalar") {
|
|
211
|
+
field.validators = compiled.checks.map((check) => ({ ...check }));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const tables = new WeakMap();
|
|
217
|
+
export function bindFieldValidators(contract, table) {
|
|
218
|
+
tables.set(contract, table);
|
|
219
|
+
}
|
|
220
|
+
const shaped = new WeakMap();
|
|
221
|
+
function hasShapedField(contract) {
|
|
222
|
+
let found = shaped.get(contract);
|
|
223
|
+
if (found === undefined) {
|
|
224
|
+
found = Object.values(contract.resources).some((resource) => Object.values(resource.fields).some((field) => field.kind === "scalar" && field.json !== undefined));
|
|
225
|
+
shaped.set(contract, found);
|
|
226
|
+
}
|
|
227
|
+
return found;
|
|
228
|
+
}
|
|
229
|
+
export function hasFieldValidators(contract) {
|
|
230
|
+
const table = tables.get(contract);
|
|
231
|
+
return ((table !== undefined && Object.keys(table).length > 0) ||
|
|
232
|
+
hasShapedField(contract));
|
|
233
|
+
}
|
|
234
|
+
const NO_FIELDS = Object.freeze({});
|
|
235
|
+
export function validatedFieldsOf(contract, resource) {
|
|
236
|
+
const table = tables.get(contract);
|
|
237
|
+
return (table === undefined ? undefined : own(table, resource)) ?? NO_FIELDS;
|
|
238
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { AvAdapterModel } from "../adapters/adapter-model.js";
|
|
2
|
+
import type { AvContractCompilerDiagnostic } from "./contract-compiler-diagnostic.js";
|
|
3
|
+
type ProjectedField = Record<string, unknown> & {
|
|
4
|
+
kind: "scalar" | "relation";
|
|
5
|
+
};
|
|
6
|
+
type ProjectedResource = {
|
|
7
|
+
fields: Record<string, ProjectedField>;
|
|
8
|
+
operations: Record<string, unknown>;
|
|
9
|
+
schema?: string;
|
|
10
|
+
managedElsewhere?: true;
|
|
11
|
+
};
|
|
12
|
+
type ProjectedContract = {
|
|
13
|
+
resources: Record<string, ProjectedResource>;
|
|
14
|
+
};
|
|
15
|
+
/** The Resources the root's `resources` tree marks `managedElsewhere: true`. */
|
|
16
|
+
export declare function managedElsewhereOf(rootTree: unknown): ReadonlySet<string>;
|
|
17
|
+
/**
|
|
18
|
+
* Takes every write of a managed Resource away from `baseline`: its write
|
|
19
|
+
* families, its fields' `create`/`update`, and the directives of every relation
|
|
20
|
+
* into it that would write its rows.
|
|
21
|
+
*/
|
|
22
|
+
export declare function withholdManagedWrites(baseline: ProjectedContract, managed: ReadonlySet<string>): void;
|
|
23
|
+
/**
|
|
24
|
+
* Leaves every managed Resource out of `view`, with every relation into it: the
|
|
25
|
+
* client's and the agent's parent view.
|
|
26
|
+
*/
|
|
27
|
+
export declare function hideManagedElsewhere(view: ProjectedContract, managed: ReadonlySet<string>): void;
|
|
28
|
+
/**
|
|
29
|
+
* Leaves out of `view` (a client's or the agent's projection) each enum the
|
|
30
|
+
* managed Resources' fields use in `unhidden` (the view before they were
|
|
31
|
+
* hidden) and no field of `view` uses.
|
|
32
|
+
*/
|
|
33
|
+
export declare function pruneManagedEnums(view: ProjectedContract & {
|
|
34
|
+
enums: Record<string, unknown>;
|
|
35
|
+
}, unhidden: ProjectedContract, managed: ReadonlySet<string>): void;
|
|
36
|
+
/**
|
|
37
|
+
* The Core Contract's Resource facts: the `schema` the adapter states, and
|
|
38
|
+
* `managedElsewhere: true` on each managed Resource.
|
|
39
|
+
*/
|
|
40
|
+
export declare function markCoreResources(projection: ProjectedContract, model: AvAdapterModel, managed: ReadonlySet<string>): void;
|
|
41
|
+
/**
|
|
42
|
+
* Why a write family of a managed Resource is unavailable, or `undefined` when
|
|
43
|
+
* the Resource is not managed or the family is not a write.
|
|
44
|
+
*/
|
|
45
|
+
export declare function managedWriteReason(managed: ReadonlySet<string>, resource: string, family: string): string | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* Adds to each create-requirement refusal from `from` on whose relation points
|
|
48
|
+
* at a managed Resource why that relation cannot be written, and the reference
|
|
49
|
+
* fields that can: exposing the relation, the refusal's own advice, is not open.
|
|
50
|
+
*/
|
|
51
|
+
export declare function explainManagedRefusals(diagnostics: AvContractCompilerDiagnostic[], from: number, layer: string, universe: ProjectedContract, managed: ReadonlySet<string>): void;
|
|
52
|
+
export {};
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
const WRITE_FAMILIES = [
|
|
2
|
+
"create",
|
|
3
|
+
"update",
|
|
4
|
+
"delete",
|
|
5
|
+
"upsert",
|
|
6
|
+
];
|
|
7
|
+
const SOURCE_ROW_DIRECTIVES = [
|
|
8
|
+
"$connect",
|
|
9
|
+
"$disconnect",
|
|
10
|
+
];
|
|
11
|
+
function isRecord(value) {
|
|
12
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13
|
+
}
|
|
14
|
+
export function managedElsewhereOf(rootTree) {
|
|
15
|
+
const managed = new Set();
|
|
16
|
+
if (!isRecord(rootTree)) {
|
|
17
|
+
return managed;
|
|
18
|
+
}
|
|
19
|
+
for (const [name, entry] of Object.entries(rootTree)) {
|
|
20
|
+
if (isRecord(entry) && entry.managedElsewhere === true) {
|
|
21
|
+
managed.add(name);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return managed;
|
|
25
|
+
}
|
|
26
|
+
function keptDirectives(capability, kept) {
|
|
27
|
+
if (Array.isArray(capability)) {
|
|
28
|
+
const left = capability.filter((directive) => kept.includes(directive));
|
|
29
|
+
return left.length > 0 ? left : undefined;
|
|
30
|
+
}
|
|
31
|
+
if (isRecord(capability) && Array.isArray(capability.directives)) {
|
|
32
|
+
const left = capability.directives.filter((directive) => kept.includes(directive));
|
|
33
|
+
return left.length > 0 ? { ...capability, directives: left } : undefined;
|
|
34
|
+
}
|
|
35
|
+
return capability;
|
|
36
|
+
}
|
|
37
|
+
function narrowWrites(field, kept) {
|
|
38
|
+
const capabilities = field.capabilities;
|
|
39
|
+
if (!isRecord(capabilities)) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
for (const stage of ["create", "update"]) {
|
|
43
|
+
if (!Object.hasOwn(capabilities, stage)) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const narrowed = keptDirectives(capabilities[stage], kept);
|
|
47
|
+
if (narrowed === undefined) {
|
|
48
|
+
delete capabilities[stage];
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
capabilities[stage] = narrowed;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export function withholdManagedWrites(baseline, managed) {
|
|
56
|
+
if (managed.size === 0) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
for (const [name, resource] of Object.entries(baseline.resources)) {
|
|
60
|
+
const isManaged = managed.has(name);
|
|
61
|
+
if (isManaged) {
|
|
62
|
+
for (const family of WRITE_FAMILIES) {
|
|
63
|
+
delete resource.operations[family];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
for (const field of Object.values(resource.fields)) {
|
|
67
|
+
if (isManaged) {
|
|
68
|
+
narrowWrites(field, []);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (field.kind !== "relation" || !managed.has(String(field.target))) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
narrowWrites(field, Array.isArray(field.referenceFields) && field.referenceFields.length > 0
|
|
75
|
+
? SOURCE_ROW_DIRECTIVES
|
|
76
|
+
: []);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
export function hideManagedElsewhere(view, managed) {
|
|
81
|
+
for (const name of managed) {
|
|
82
|
+
delete view.resources[name];
|
|
83
|
+
}
|
|
84
|
+
if (managed.size === 0) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
for (const resource of Object.values(view.resources)) {
|
|
88
|
+
for (const [fieldName, field] of Object.entries(resource.fields)) {
|
|
89
|
+
if (field.kind === "relation" && managed.has(String(field.target))) {
|
|
90
|
+
delete resource.fields[fieldName];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function enumOf(field) {
|
|
96
|
+
const type = field.kind === "scalar" ? field.type : undefined;
|
|
97
|
+
return typeof type === "object" &&
|
|
98
|
+
type !== null &&
|
|
99
|
+
typeof type.enum === "string"
|
|
100
|
+
? type.enum
|
|
101
|
+
: undefined;
|
|
102
|
+
}
|
|
103
|
+
function enumsUsed(resources) {
|
|
104
|
+
const used = new Set();
|
|
105
|
+
for (const resource of resources) {
|
|
106
|
+
for (const field of Object.values(resource.fields)) {
|
|
107
|
+
const name = enumOf(field);
|
|
108
|
+
if (name !== undefined) {
|
|
109
|
+
used.add(name);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return used;
|
|
114
|
+
}
|
|
115
|
+
export function pruneManagedEnums(view, unhidden, managed) {
|
|
116
|
+
const managedUse = enumsUsed([...managed].flatMap((name) => Object.hasOwn(unhidden.resources, name)
|
|
117
|
+
? [unhidden.resources[name]]
|
|
118
|
+
: []));
|
|
119
|
+
if (managedUse.size === 0) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const served = enumsUsed(Object.values(view.resources));
|
|
123
|
+
for (const name of managedUse) {
|
|
124
|
+
if (!served.has(name)) {
|
|
125
|
+
delete view.enums[name];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
export function markCoreResources(projection, model, managed) {
|
|
130
|
+
for (const [name, resource] of Object.entries(projection.resources)) {
|
|
131
|
+
const schema = Object.hasOwn(model.resources, name)
|
|
132
|
+
? model.resources[name]?.schema
|
|
133
|
+
: undefined;
|
|
134
|
+
if (typeof schema === "string") {
|
|
135
|
+
resource.schema = schema;
|
|
136
|
+
}
|
|
137
|
+
if (managed.has(name)) {
|
|
138
|
+
resource.managedElsewhere = true;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
export function managedWriteReason(managed, resource, family) {
|
|
143
|
+
return managed.has(resource) && WRITE_FAMILIES.includes(family)
|
|
144
|
+
? `operation family "${family}" is not available for resource "${resource}": it is managed elsewhere (resources.${resource}.managedElsewhere), read and joined, never written`
|
|
145
|
+
: undefined;
|
|
146
|
+
}
|
|
147
|
+
export function explainManagedRefusals(diagnostics, from, layer, universe, managed) {
|
|
148
|
+
if (managed.size === 0) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const prefix = `${layer}.exemptions.resources.`;
|
|
152
|
+
for (let index = from; index < diagnostics.length; index += 1) {
|
|
153
|
+
const diagnostic = diagnostics[index];
|
|
154
|
+
if (diagnostic?.code !== "COMPILER_HIDDEN_REQUIRED_FIELD" ||
|
|
155
|
+
!diagnostic.path.startsWith(prefix)) {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const [resource, fieldsKey, field] = diagnostic.path
|
|
159
|
+
.slice(prefix.length)
|
|
160
|
+
.split(".");
|
|
161
|
+
const relation = resource !== undefined &&
|
|
162
|
+
fieldsKey === "fields" &&
|
|
163
|
+
field !== undefined &&
|
|
164
|
+
Object.hasOwn(universe.resources, resource)
|
|
165
|
+
? universe.resources[resource]?.fields[field]
|
|
166
|
+
: undefined;
|
|
167
|
+
const target = String(relation?.target);
|
|
168
|
+
if (relation?.kind !== "relation" || !managed.has(target)) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
const references = Array.isArray(relation.referenceFields)
|
|
172
|
+
? relation.referenceFields.map((name) => `"${String(name)}"`)
|
|
173
|
+
: [];
|
|
174
|
+
diagnostics[index] = {
|
|
175
|
+
...diagnostic,
|
|
176
|
+
message: `${diagnostic.message}; "${field}" points at resource "${target}", which is managed elsewhere (resources.${target}.managedElsewhere): the ${layer} layer never writes it${references.length === 0 ? "" : `, so expose ${references.join(" and ")}`}`,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AvConfigDiagnostic } from "../config/config-diagnostic.js";
|
|
2
|
+
import type { ReferentialActionTable } from "../runtime/referential-action.js";
|
|
3
|
+
import type { AvContracts } from "./contract.compiler.js";
|
|
4
|
+
/**
|
|
5
|
+
* One `CONFIG_INVALID_VALUE` per deletable Resource whose delete — directly, or
|
|
6
|
+
* through the cascades it sets off — does `cascade`, `setNull` or `setDefault`
|
|
7
|
+
* to the rows of a Resource managed elsewhere.
|
|
8
|
+
*/
|
|
9
|
+
export declare function validateManagedDeletes(contracts: AvContracts, referentialActions: ReferentialActionTable | undefined): readonly AvConfigDiagnostic[];
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { advertisedDirectives } from "../contracts/field.js";
|
|
2
|
+
function deletable(contracts) {
|
|
3
|
+
const names = new Set();
|
|
4
|
+
for (const contract of contracts) {
|
|
5
|
+
for (const [name, resource] of Object.entries(contract.resources)) {
|
|
6
|
+
if (Object.hasOwn(resource.operations, "delete")) {
|
|
7
|
+
names.add(name);
|
|
8
|
+
}
|
|
9
|
+
for (const field of Object.values(resource.fields)) {
|
|
10
|
+
if (field.kind !== "relation") {
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
const { create, update } = field.capabilities;
|
|
14
|
+
if (advertisedDirectives(create)?.includes("$delete") === true ||
|
|
15
|
+
advertisedDirectives(update)?.includes("$delete") === true) {
|
|
16
|
+
names.add(field.target);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return names;
|
|
22
|
+
}
|
|
23
|
+
function hopLabel(resource, edge) {
|
|
24
|
+
return `${resource}.${edge.field} → ${edge.target}`;
|
|
25
|
+
}
|
|
26
|
+
export function validateManagedDeletes(contracts, referentialActions) {
|
|
27
|
+
const core = contracts.core;
|
|
28
|
+
const managed = new Set(Object.entries(core.resources)
|
|
29
|
+
.filter(([, resource]) => resource.managedElsewhere === true)
|
|
30
|
+
.map(([name]) => name));
|
|
31
|
+
if (managed.size === 0 || referentialActions === undefined) {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
const firedBy = new Map();
|
|
35
|
+
for (const [resource, edges] of Object.entries(referentialActions)) {
|
|
36
|
+
for (const edge of edges) {
|
|
37
|
+
firedBy.set(edge.target, [
|
|
38
|
+
...(firedBy.get(edge.target) ?? []),
|
|
39
|
+
{ edge, from: resource },
|
|
40
|
+
]);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const diagnostics = [];
|
|
44
|
+
const deleters = [
|
|
45
|
+
...deletable([contracts.application, contracts.client, contracts.agent]),
|
|
46
|
+
]
|
|
47
|
+
.filter((name) => !managed.has(name))
|
|
48
|
+
.sort();
|
|
49
|
+
for (const deleted of deleters) {
|
|
50
|
+
const reachedBy = new Map([[deleted, []]]);
|
|
51
|
+
const queue = [deleted];
|
|
52
|
+
for (let next = queue.shift(); next !== undefined; next = queue.shift()) {
|
|
53
|
+
const chain = reachedBy.get(next) ?? [];
|
|
54
|
+
for (const { edge, from } of firedBy.get(next) ?? []) {
|
|
55
|
+
const hop = [hopLabel(from, edge), ...chain];
|
|
56
|
+
if (managed.has(from)) {
|
|
57
|
+
if (edge.onDelete !== "restrict" && edge.onDelete !== "noAction") {
|
|
58
|
+
diagnostics.push({
|
|
59
|
+
code: "CONFIG_INVALID_VALUE",
|
|
60
|
+
path: `resources.${from}.managedElsewhere`,
|
|
61
|
+
message: `resources.${from}.managedElsewhere: deleting a "${deleted}" row does ${edge.onDelete} to "${from}" rows (${hop.join(", ")}), and another service owns "${from}"; take every delete of "${deleted}" away at the root (exemptions.resources.${deleted}.operations: ["delete"], and every $delete into it), or make the reference refuse the delete (onDelete: Restrict or NoAction)`,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (edge.onDelete === "cascade" && !reachedBy.has(from)) {
|
|
67
|
+
reachedBy.set(from, hop);
|
|
68
|
+
queue.push(from);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return diagnostics;
|
|
74
|
+
}
|
|
@@ -1,19 +1,26 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/**
|
|
2
|
+
* A `resources` block whose Resource and field names are both unconstrained:
|
|
3
|
+
* each Resource entry may carry a `fields` record of any names.
|
|
4
|
+
*/
|
|
5
|
+
export type AvConfigResourcesAny = Readonly<Record<string, {
|
|
5
6
|
readonly [key: string]: unknown;
|
|
6
|
-
readonly fields?:
|
|
7
|
+
readonly fields?: Readonly<Record<string, unknown>> | undefined;
|
|
8
|
+
} | undefined>>;
|
|
9
|
+
/** The widest layer overlay: a `resources` block, and anything else. */
|
|
10
|
+
export type AvConfigLayerAny = {
|
|
11
|
+
readonly [key: string | symbol]: unknown;
|
|
12
|
+
readonly resources?: AvConfigResourcesAny;
|
|
7
13
|
};
|
|
8
14
|
/**
|
|
9
|
-
* The widest configuration type: every
|
|
10
|
-
* is the default `Cfg` of `
|
|
15
|
+
* The widest configuration type: every Resource admits every field name. It
|
|
16
|
+
* is the default `Cfg` of `AvConfig`, `AvFramework` and
|
|
11
17
|
* `createFramework`; pass your own configuration's type instead to get field
|
|
12
18
|
* names checked.
|
|
13
19
|
*/
|
|
14
|
-
export type
|
|
15
|
-
readonly [key: string]: unknown;
|
|
16
|
-
readonly
|
|
17
|
-
readonly application?:
|
|
18
|
-
readonly client?:
|
|
20
|
+
export type AvConfigAny = {
|
|
21
|
+
readonly [key: string | symbol]: unknown;
|
|
22
|
+
readonly resources?: AvConfigResourcesAny;
|
|
23
|
+
readonly application?: AvConfigLayerAny;
|
|
24
|
+
readonly client?: AvConfigLayerAny;
|
|
25
|
+
readonly agent?: AvConfigLayerAny;
|
|
19
26
|
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { AvConfigAgentParent, AvConfigLayerName } from "./config-layer.js";
|
|
2
|
+
/** The fields one layer's write compute fills, per Resource and phase. */
|
|
3
|
+
type LayerComputedFields = Readonly<Record<string, {
|
|
4
|
+
/** Fields with a `compute.create` stage, sorted. */
|
|
5
|
+
readonly create?: readonly string[];
|
|
6
|
+
/** Fields with a `compute.update` stage, sorted. */
|
|
7
|
+
readonly update?: readonly string[];
|
|
8
|
+
}>>;
|
|
9
|
+
/**
|
|
10
|
+
* Which fields a configuration's write compute fills: per layer, the
|
|
11
|
+
* Resources, and per Resource the fields with a `compute.create` and a
|
|
12
|
+
* `compute.update` stage, each list sorted; and the layer the agent layer
|
|
13
|
+
* extends. Written by an adapter's generate step from the configuration's type,
|
|
14
|
+
* and compared with the configuration at startup.
|
|
15
|
+
*/
|
|
16
|
+
export type AvConfigComputedFields = {
|
|
17
|
+
readonly agent: LayerComputedFields;
|
|
18
|
+
/** The agent layer's `extends`: `"client"` when the configuration leaves it out. */
|
|
19
|
+
readonly agentExtends: Exclude<AvConfigLayerName, "agent">;
|
|
20
|
+
readonly application: LayerComputedFields;
|
|
21
|
+
readonly client: LayerComputedFields;
|
|
22
|
+
readonly root: LayerComputedFields;
|
|
23
|
+
};
|
|
24
|
+
/** The layers whose compute may have filled a field when a stage of layer `L` runs. */
|
|
25
|
+
type ComputingLayers<Facts extends AvConfigComputedFields, L extends AvConfigLayerName> = L extends "root" ? AvConfigLayerName : L extends "agent" ? "root" | "agent" | Exclude<Facts["agentExtends"], "root"> : "root" | L | (Facts["agentExtends"] extends L ? "agent" : never);
|
|
26
|
+
/** `"<Resource>.<field>"` for every field a layer's `compute.create` fills. */
|
|
27
|
+
type CreateComputedOf<Layer> = Layer extends object ? {
|
|
28
|
+
[R in keyof Layer]: Layer[R] extends {
|
|
29
|
+
readonly create?: readonly (infer Field extends string)[];
|
|
30
|
+
} ? `${R & string}.${Field}` : never;
|
|
31
|
+
}[keyof Layer] : never;
|
|
32
|
+
/**
|
|
33
|
+
* The create fields a variant's guards, pipes and filters of layer `L` may be
|
|
34
|
+
* told without, as `"<Resource>.<field>"`: those a layer whose compute runs for
|
|
35
|
+
* the stage's origins fills on create. `string` (every field) when the model's
|
|
36
|
+
* type carries no statement of them.
|
|
37
|
+
*/
|
|
38
|
+
export type AvPreComputeLift<M, L extends AvConfigLayerName> = M extends {
|
|
39
|
+
readonly "~computedFields"?: infer Facts;
|
|
40
|
+
} ? Exclude<Facts, undefined> extends infer Stated extends AvConfigComputedFields ? string extends keyof Stated["root"] ? string : CreateComputedOf<Stated[ComputingLayers<Stated, L>]> : string : string;
|
|
41
|
+
/** The configuration's layers' `resources` trees and the agent's parent. */
|
|
42
|
+
type ComputedFieldsSource = {
|
|
43
|
+
readonly trees: Readonly<Record<AvConfigLayerName, unknown>>;
|
|
44
|
+
readonly agentExtends: AvConfigAgentParent;
|
|
45
|
+
};
|
|
46
|
+
/** The computed fields of a configuration, in the form a generate step writes them. */
|
|
47
|
+
export declare function computedFieldsOf(source: ComputedFieldsSource): AvConfigComputedFields;
|
|
48
|
+
/**
|
|
49
|
+
* How the configuration's computed fields differ from the ones the generated
|
|
50
|
+
* types were written with, one sentence per difference; empty when they agree.
|
|
51
|
+
*/
|
|
52
|
+
export declare function computedFieldsDrift(generated: AvConfigComputedFields, configured: AvConfigComputedFields): string[];
|
|
53
|
+
/** The warning's code, as `process.emitWarning` reports it. */
|
|
54
|
+
export declare const STALE_GENERATED_TYPES_WARNING = "AVENTARA_STALE_GENERATED_TYPES";
|
|
55
|
+
/**
|
|
56
|
+
* Warns, through `process.emitWarning` (code
|
|
57
|
+
* {@link STALE_GENERATED_TYPES_WARNING}), when the generated types' computed
|
|
58
|
+
* fields differ from the configuration's. Nothing to compare (an Adapter that
|
|
59
|
+
* carries none) warns nothing.
|
|
60
|
+
*/
|
|
61
|
+
export declare function warnOnStaleComputedFields(generated: AvConfigComputedFields | undefined, configured: AvConfigComputedFields): void;
|
|
62
|
+
export {};
|