@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,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isAccessPath, isAccessUrl } from "./access-route.rules.js";
|
|
2
|
+
import { FILTER_MODES, PROJECTION_SCOPES, WRITE_SCOPES } from "./capability.js";
|
|
2
3
|
import { AVENTARA_PROTOCOL_VERSION } from "./contract-protocol.js";
|
|
3
4
|
import { LIFECYCLE_TRAITS } from "./lifecycle-trait.js";
|
|
4
5
|
import { REFERENCE_WRITE_LIMITS } from "./reference-write-limit.js";
|
|
@@ -10,13 +11,16 @@ function literal(values) {
|
|
|
10
11
|
return { is: "literal", values };
|
|
11
12
|
}
|
|
12
13
|
function list(item) {
|
|
13
|
-
return { is: "list", item,
|
|
14
|
+
return { is: "list", item, minimum: 0, unique: false };
|
|
14
15
|
}
|
|
15
16
|
function nonEmptyList(item) {
|
|
16
|
-
return { is: "list", item,
|
|
17
|
+
return { is: "list", item, minimum: 1, unique: false };
|
|
18
|
+
}
|
|
19
|
+
function pairOrMoreList(item) {
|
|
20
|
+
return { is: "list", item, minimum: 2, unique: false };
|
|
17
21
|
}
|
|
18
22
|
function uniqueList(item) {
|
|
19
|
-
return { is: "list", item,
|
|
23
|
+
return { is: "list", item, minimum: 0, unique: true };
|
|
20
24
|
}
|
|
21
25
|
function registry(entry) {
|
|
22
26
|
return { is: "registry", entry };
|
|
@@ -93,6 +97,127 @@ const RELATION_MUTATION = union(list(literal(RELATION_MUTATION_DIRECTIVES)), obj
|
|
|
93
97
|
directives: list(literal(RELATION_MUTATION_DIRECTIVES)),
|
|
94
98
|
scopes: WRITE_SCOPE_LIST,
|
|
95
99
|
}));
|
|
100
|
+
const NAMED_FORMATS = [
|
|
101
|
+
"lowercase",
|
|
102
|
+
"uppercase",
|
|
103
|
+
"email",
|
|
104
|
+
"url",
|
|
105
|
+
"emoji",
|
|
106
|
+
"uuid",
|
|
107
|
+
"guid",
|
|
108
|
+
"nanoid",
|
|
109
|
+
"cuid",
|
|
110
|
+
"cuid2",
|
|
111
|
+
"ulid",
|
|
112
|
+
"xid",
|
|
113
|
+
"ksuid",
|
|
114
|
+
"datetime",
|
|
115
|
+
"date",
|
|
116
|
+
"time",
|
|
117
|
+
"duration",
|
|
118
|
+
"ipv4",
|
|
119
|
+
"ipv6",
|
|
120
|
+
"cidrv4",
|
|
121
|
+
"cidrv6",
|
|
122
|
+
"base64",
|
|
123
|
+
"base64url",
|
|
124
|
+
"json_string",
|
|
125
|
+
"e164",
|
|
126
|
+
"credit_card",
|
|
127
|
+
"iban",
|
|
128
|
+
"jwt",
|
|
129
|
+
];
|
|
130
|
+
const STRING_FORMAT_CHECK = union(object({
|
|
131
|
+
check: literal(["string_format"]),
|
|
132
|
+
format: literal(["regex"]),
|
|
133
|
+
pattern: object({ source: STRING, flags: STRING }),
|
|
134
|
+
}), object({
|
|
135
|
+
check: literal(["string_format"]),
|
|
136
|
+
format: literal(["includes"]),
|
|
137
|
+
includes: STRING,
|
|
138
|
+
}), object({
|
|
139
|
+
check: literal(["string_format"]),
|
|
140
|
+
format: literal(["starts_with"]),
|
|
141
|
+
prefix: STRING,
|
|
142
|
+
}), object({
|
|
143
|
+
check: literal(["string_format"]),
|
|
144
|
+
format: literal(["ends_with"]),
|
|
145
|
+
suffix: STRING,
|
|
146
|
+
}), object({
|
|
147
|
+
check: literal(["string_format"]),
|
|
148
|
+
format: literal(NAMED_FORMATS),
|
|
149
|
+
}));
|
|
150
|
+
const STRING_LENGTH_CHECKS = {
|
|
151
|
+
max_length: object({ check: literal(["max_length"]), maximum: NUMBER }),
|
|
152
|
+
min_length: object({ check: literal(["min_length"]), minimum: NUMBER }),
|
|
153
|
+
length_equals: object({ check: literal(["length_equals"]), length: NUMBER }),
|
|
154
|
+
string_format: STRING_FORMAT_CHECK,
|
|
155
|
+
};
|
|
156
|
+
const FIELD_CHECK = {
|
|
157
|
+
is: "variants",
|
|
158
|
+
key: "check",
|
|
159
|
+
arms: {
|
|
160
|
+
less_than: object({
|
|
161
|
+
check: literal(["less_than"]),
|
|
162
|
+
value: union(NUMBER, STRING),
|
|
163
|
+
inclusive: BOOLEAN,
|
|
164
|
+
}),
|
|
165
|
+
greater_than: object({
|
|
166
|
+
check: literal(["greater_than"]),
|
|
167
|
+
value: union(NUMBER, STRING),
|
|
168
|
+
inclusive: BOOLEAN,
|
|
169
|
+
}),
|
|
170
|
+
multiple_of: object({
|
|
171
|
+
check: literal(["multiple_of"]),
|
|
172
|
+
value: union(NUMBER, STRING),
|
|
173
|
+
}),
|
|
174
|
+
max_length: object({ check: literal(["max_length"]), maximum: NUMBER }, { list: literal([true]) }),
|
|
175
|
+
min_length: object({ check: literal(["min_length"]), minimum: NUMBER }, { list: literal([true]) }),
|
|
176
|
+
length_equals: STRING_LENGTH_CHECKS.length_equals,
|
|
177
|
+
string_format: STRING_FORMAT_CHECK,
|
|
178
|
+
max_size: object({ check: literal(["max_size"]), maximum: NUMBER }),
|
|
179
|
+
min_size: object({ check: literal(["min_size"]), minimum: NUMBER }),
|
|
180
|
+
size_equals: object({ check: literal(["size_equals"]), size: NUMBER }),
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
const JSON_STRING_CHECK = {
|
|
184
|
+
is: "variants",
|
|
185
|
+
key: "check",
|
|
186
|
+
arms: STRING_LENGTH_CHECKS,
|
|
187
|
+
};
|
|
188
|
+
const JSON_NUMBER_CHECK = {
|
|
189
|
+
is: "variants",
|
|
190
|
+
key: "check",
|
|
191
|
+
arms: {
|
|
192
|
+
less_than: object({
|
|
193
|
+
check: literal(["less_than"]),
|
|
194
|
+
value: NUMBER,
|
|
195
|
+
inclusive: BOOLEAN,
|
|
196
|
+
}),
|
|
197
|
+
greater_than: object({
|
|
198
|
+
check: literal(["greater_than"]),
|
|
199
|
+
value: NUMBER,
|
|
200
|
+
inclusive: BOOLEAN,
|
|
201
|
+
}),
|
|
202
|
+
multiple_of: object({ check: literal(["multiple_of"]), value: NUMBER }),
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
const JSON_LIST_CHECK = {
|
|
206
|
+
is: "variants",
|
|
207
|
+
key: "check",
|
|
208
|
+
arms: {
|
|
209
|
+
max_length: object({
|
|
210
|
+
check: literal(["max_length"]),
|
|
211
|
+
maximum: NUMBER,
|
|
212
|
+
list: literal([true]),
|
|
213
|
+
}),
|
|
214
|
+
min_length: object({
|
|
215
|
+
check: literal(["min_length"]),
|
|
216
|
+
minimum: NUMBER,
|
|
217
|
+
list: literal([true]),
|
|
218
|
+
}),
|
|
219
|
+
},
|
|
220
|
+
};
|
|
96
221
|
const SCALAR_FIELD = object({
|
|
97
222
|
kind: literal(["scalar"]),
|
|
98
223
|
type: union(object({ scalar: STRING }), object({ enum: STRING })),
|
|
@@ -102,12 +227,19 @@ const SCALAR_FIELD = object({
|
|
|
102
227
|
capabilities: object({}, {
|
|
103
228
|
select: BOOLEAN,
|
|
104
229
|
filter: list(literal(SCALAR_FILTER_OPERATORS)),
|
|
230
|
+
filterModes: list(literal(FILTER_MODES)),
|
|
231
|
+
pathFilterModes: list(literal(FILTER_MODES)),
|
|
105
232
|
order: BOOLEAN,
|
|
106
233
|
orderNulls: BOOLEAN,
|
|
107
234
|
create: list(literal(SCALAR_MUTATION_DIRECTIVES)),
|
|
108
235
|
update: list(literal(SCALAR_MUTATION_DIRECTIVES)),
|
|
109
236
|
}),
|
|
110
|
-
}, {
|
|
237
|
+
}, {
|
|
238
|
+
format: STRING,
|
|
239
|
+
json: { is: "jsonSchema" },
|
|
240
|
+
validators: list(FIELD_CHECK),
|
|
241
|
+
governed: literal([true]),
|
|
242
|
+
});
|
|
111
243
|
const RELATION_FIELD = object({
|
|
112
244
|
kind: literal(["relation"]),
|
|
113
245
|
target: STRING,
|
|
@@ -127,6 +259,10 @@ const RELATION_FIELD = object({
|
|
|
127
259
|
update: RELATION_MUTATION,
|
|
128
260
|
nestedWriteFilter: literal(["scalar"]),
|
|
129
261
|
}),
|
|
262
|
+
}, {
|
|
263
|
+
inverse: STRING,
|
|
264
|
+
referenceFields: nonEmptyList(STRING),
|
|
265
|
+
governed: literal([true]),
|
|
130
266
|
});
|
|
131
267
|
const OPERATION_AVAILABILITY = union(literal([true]), object({ projection: literal(PROJECTION_SCOPES) }, { write: literal(WRITE_SCOPES) }), object({ write: literal(WRITE_SCOPES) }, { projection: literal(PROJECTION_SCOPES) }));
|
|
132
268
|
const RESOURCE = object({
|
|
@@ -162,6 +298,32 @@ const RESOURCE = object({
|
|
|
162
298
|
}),
|
|
163
299
|
upsert: object({}, { unique: OPERATION_AVAILABILITY }),
|
|
164
300
|
}),
|
|
301
|
+
}, {
|
|
302
|
+
createRequirements: object({}, {
|
|
303
|
+
required: nonEmptyList(STRING),
|
|
304
|
+
oneOf: nonEmptyList(pairOrMoreList(STRING)),
|
|
305
|
+
parentSupplies: registry(registry(nonEmptyList(STRING))),
|
|
306
|
+
}),
|
|
307
|
+
});
|
|
308
|
+
const ACCESS_VERB = object({
|
|
309
|
+
method: literal(["GET", "POST"]),
|
|
310
|
+
path: { is: "route", form: "path" },
|
|
311
|
+
result: { is: "jsonSchema" },
|
|
312
|
+
}, { args: { is: "jsonSchema" } });
|
|
313
|
+
const ACCESS = object({}, {
|
|
314
|
+
url: { is: "route", form: "url" },
|
|
315
|
+
verbs: object({}, {
|
|
316
|
+
signIn: ACCESS_VERB,
|
|
317
|
+
signUp: ACCESS_VERB,
|
|
318
|
+
signOut: ACCESS_VERB,
|
|
319
|
+
resetPassword: ACCESS_VERB,
|
|
320
|
+
me: ACCESS_VERB,
|
|
321
|
+
switchUser: ACCESS_VERB,
|
|
322
|
+
lock: ACCESS_VERB,
|
|
323
|
+
setPin: ACCESS_VERB,
|
|
324
|
+
enrollDevice: ACCESS_VERB,
|
|
325
|
+
revokeDevice: ACCESS_VERB,
|
|
326
|
+
}),
|
|
165
327
|
});
|
|
166
328
|
export const CLIENT_CONTRACT_STRUCTURE = object({
|
|
167
329
|
scalars: registry(object({ builtin: literal([true]) })),
|
|
@@ -173,13 +335,14 @@ export const CLIENT_CONTRACT_STRUCTURE = object({
|
|
|
173
335
|
maxListLimit: NUMBER,
|
|
174
336
|
maxTransactionOperations: NUMBER,
|
|
175
337
|
maxBooleanNodes: NUMBER,
|
|
338
|
+
maxJsonDepth: NUMBER,
|
|
176
339
|
}),
|
|
177
340
|
transactions: literal(TRANSACTION_CAPABILITIES),
|
|
178
341
|
protocol: object({
|
|
179
342
|
version: literal([AVENTARA_PROTOCOL_VERSION]),
|
|
180
343
|
hash: { is: "prefixed", prefix: "sha256:" },
|
|
181
344
|
}),
|
|
182
|
-
}, { referenceWrites: literal(REFERENCE_WRITE_LIMITS) });
|
|
345
|
+
}, { referenceWrites: literal(REFERENCE_WRITE_LIMITS), access: ACCESS });
|
|
183
346
|
export function validateClientContractStructure(value) {
|
|
184
347
|
const defect = defectOf(CLIENT_CONTRACT_STRUCTURE, value, "$", 0);
|
|
185
348
|
if (defect !== undefined) {
|
|
@@ -201,6 +364,13 @@ function defectOf(node, value, path, depth) {
|
|
|
201
364
|
(value.startsWith(node.prefix)
|
|
202
365
|
? undefined
|
|
203
366
|
: at(path, depth, `is ${JSON.stringify(value)}, which does not begin with ${JSON.stringify(node.prefix)}`)));
|
|
367
|
+
case "route":
|
|
368
|
+
return (kindDefect(value, "string", path, depth) ??
|
|
369
|
+
((node.form === "url" ? isAccessUrl : isAccessPath)(value)
|
|
370
|
+
? undefined
|
|
371
|
+
: at(path, depth, node.form === "url"
|
|
372
|
+
? "is no absolute http(s) URL and no root-relative path"
|
|
373
|
+
: 'is no path under the url, starting with one "/"')));
|
|
204
374
|
case "list":
|
|
205
375
|
return listDefect(node, value, path, depth);
|
|
206
376
|
case "registry": {
|
|
@@ -272,13 +442,20 @@ function jsonSchemaDefect(value, path, depth) {
|
|
|
272
442
|
switch (keys) {
|
|
273
443
|
case "type":
|
|
274
444
|
return literalDefect(JSON_SCHEMA_SCALAR_TYPES, schema.type, `${path}.type`, depth + 1);
|
|
445
|
+
case "checks,type":
|
|
446
|
+
return (literalDefect(["string", "number", "integer"], schema.type, `${path}.type`, depth + 1) ??
|
|
447
|
+
defectOf(list(schema.type === "string" ? JSON_STRING_CHECK : JSON_NUMBER_CHECK), schema.checks, `${path}.checks`, depth + 1));
|
|
448
|
+
case "checks,items,type":
|
|
449
|
+
return (literalDefect(["array"], schema.type, `${path}.type`, depth + 1) ??
|
|
450
|
+
child("items", schema.items) ??
|
|
451
|
+
defectOf(list(JSON_LIST_CHECK), schema.checks, `${path}.checks`, depth + 1));
|
|
275
452
|
case "const":
|
|
276
453
|
return Object.is(schema.const, null)
|
|
277
454
|
? undefined
|
|
278
455
|
: defectOf(JSON_PRIMITIVE, schema.const, `${path}.const`, depth + 1);
|
|
279
456
|
case "enum,type":
|
|
280
457
|
return (literalDefect(["string"], schema.type, `${path}.type`, depth + 1) ??
|
|
281
|
-
defectOf({ is: "list", item: STRING,
|
|
458
|
+
defectOf({ is: "list", item: STRING, minimum: 1, unique: true }, schema.enum, `${path}.enum`, depth + 1));
|
|
282
459
|
case "additionalProperties,properties,type":
|
|
283
460
|
case "additionalProperties,properties,required,type": {
|
|
284
461
|
const typeDefect = literalDefect(["object"], schema.type, `${path}.type`, depth + 1) ??
|
|
@@ -296,7 +473,7 @@ function jsonSchemaDefect(value, path, depth) {
|
|
|
296
473
|
}
|
|
297
474
|
if (!Object.hasOwn(schema, "required"))
|
|
298
475
|
return undefined;
|
|
299
|
-
const requiredDefect = defectOf({ is: "list", item: STRING,
|
|
476
|
+
const requiredDefect = defectOf({ is: "list", item: STRING, minimum: 0, unique: true }, schema.required, `${path}.required`, depth + 1);
|
|
300
477
|
if (requiredDefect !== undefined)
|
|
301
478
|
return requiredDefect;
|
|
302
479
|
const undeclared = schema.required.findIndex((name) => !Object.hasOwn(properties, name));
|
|
@@ -348,8 +525,10 @@ function listDefect(node, value, path, depth) {
|
|
|
348
525
|
if (kind !== undefined)
|
|
349
526
|
return kind;
|
|
350
527
|
const entries = value;
|
|
351
|
-
if (
|
|
352
|
-
return at(path, depth,
|
|
528
|
+
if (entries.length < node.minimum) {
|
|
529
|
+
return at(path, depth, entries.length === 0
|
|
530
|
+
? `is an empty JSON array, and needs at least ${node.minimum === 1 ? "one entry" : "two entries"}`
|
|
531
|
+
: "is a JSON array of one entry, and needs at least two");
|
|
353
532
|
}
|
|
354
533
|
const firstIndexOf = new Map();
|
|
355
534
|
for (let index = 0; index < entries.length; index += 1) {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { JsonSchema } from "./json-shape.js";
|
|
2
|
+
/**
|
|
3
|
+
* The ClientContract's `access` section: present exactly when the framework's
|
|
4
|
+
* `access` option is configured, and then it marks the `POST /_access/can`
|
|
5
|
+
* route. `url` and `verbs` say where Aventara Access is mounted and each of its
|
|
6
|
+
* `$access` verbs, when the provider states them.
|
|
7
|
+
*/
|
|
8
|
+
export type AvContractClientAccess = {
|
|
9
|
+
/** Where the app mounted Access: absolute, or root-relative (`"/api/auth"`). */
|
|
10
|
+
readonly url?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Each `$access` verb the provider offers: its route under `url`, and the
|
|
13
|
+
* JSON Schema of its arguments and result. A verb left out is no verb of
|
|
14
|
+
* `$access` (a `service` profile has no `signIn` or `signUp`; only a
|
|
15
|
+
* `kiosk` profile has `switchUser`, `lock`, `setPin`, `enrollDevice` and
|
|
16
|
+
* `revokeDevice`).
|
|
17
|
+
*/
|
|
18
|
+
readonly verbs?: {
|
|
19
|
+
readonly [Verb in "signIn" | "signUp" | "signOut" | "resetPassword" | "me" | "switchUser" | "lock" | "setPin" | "enrollDevice" | "revokeDevice"]?: {
|
|
20
|
+
readonly method: "GET" | "POST";
|
|
21
|
+
/** Under `url`: `"/sign-in/email"`. */
|
|
22
|
+
readonly path: string;
|
|
23
|
+
/** The arguments' JSON Schema; absent when the verb takes none. */
|
|
24
|
+
readonly args?: JsonSchema;
|
|
25
|
+
/** The result's JSON Schema (`me`: the value when someone is signed in). */
|
|
26
|
+
readonly result: JsonSchema;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,11 +1,35 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AvContract } from "./contract.js";
|
|
2
2
|
/**
|
|
3
3
|
* Thrown when a value cannot be written as canonical JSON (RFC 8785). It is a
|
|
4
4
|
* `TypeError`.
|
|
5
5
|
*/
|
|
6
|
-
export declare class
|
|
6
|
+
export declare class AvCanonicalJsonError extends TypeError {
|
|
7
7
|
}
|
|
8
8
|
/** Canonical JSON (RFC 8785) for a JSON-safe value. */
|
|
9
9
|
export declare function canonicalizeJson(value: unknown): string;
|
|
10
|
+
/**
|
|
11
|
+
* Canonical JSON (RFC 8785) for a value as JSON carries it: a key holding
|
|
12
|
+
* `undefined` is left out, and an array element holding it (or a hole) is
|
|
13
|
+
* `null`, as `JSON.stringify` writes them. Everything else as
|
|
14
|
+
* {@link canonicalizeJson}.
|
|
15
|
+
*/
|
|
16
|
+
export declare function canonicalizeWireJson(value: unknown): string;
|
|
17
|
+
/** One top-level member of a canonical Contract: its key and its value's canonical text. */
|
|
18
|
+
type CanonicalContractMember = {
|
|
19
|
+
readonly key: string;
|
|
20
|
+
readonly text: string;
|
|
21
|
+
};
|
|
22
|
+
/** The Contract as canonical JSON (RFC 8785), one top-level member at a time, in order. */
|
|
23
|
+
export declare function canonicalizeContractMembers<C extends AvContract>(contract: C): readonly CanonicalContractMember[];
|
|
24
|
+
/** The canonical object text the members write: `{"key":text,…}`, in their order. */
|
|
25
|
+
export declare function joinCanonicalContractMembers(members: readonly CanonicalContractMember[]): string;
|
|
10
26
|
/** The Contract as canonical JSON (RFC 8785): the same bytes for the same Contract. */
|
|
11
|
-
export declare function canonicalizeContract<C extends
|
|
27
|
+
export declare function canonicalizeContract<C extends AvContract>(contract: C): string;
|
|
28
|
+
/** Records `text` as the canonical text `contract` was parsed from. */
|
|
29
|
+
export declare function recordCanonicalContractText(contract: AvContract, text: string): void;
|
|
30
|
+
/**
|
|
31
|
+
* The Contract's canonical text: the text it was identified from, when the compiler
|
|
32
|
+
* recorded one, else written now. Both are the same bytes for the same Contract.
|
|
33
|
+
*/
|
|
34
|
+
export declare function canonicalContractTextOf<C extends AvContract>(contract: C): string;
|
|
35
|
+
export {};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { brandCoreClass } from "../runtime/core-class.brand.js";
|
|
2
|
+
import { FILTER_MODES, PROJECTION_SCOPES, WRITE_SCOPES } from "./capability.js";
|
|
2
3
|
import { LIFECYCLE_TRAITS } from "./lifecycle-trait.js";
|
|
3
4
|
import { projectionScopeOf, writeScopeOf } from "./resource-operations.js";
|
|
4
5
|
const PAGINATION_ORDER = ["limit", "offset", "cursor"];
|
|
5
6
|
const REDUCER_ORDER = ["$count"];
|
|
6
|
-
export class
|
|
7
|
+
export class AvCanonicalJsonError extends TypeError {
|
|
7
8
|
}
|
|
9
|
+
brandCoreClass(AvCanonicalJsonError, "AvCanonicalJsonError");
|
|
8
10
|
function compareCodeUnits(left, right) {
|
|
9
11
|
if (left === right) {
|
|
10
12
|
return 0;
|
|
@@ -61,6 +63,12 @@ function normalizeCapabilities(capabilities) {
|
|
|
61
63
|
if (Array.isArray(filter)) {
|
|
62
64
|
normalized.filter = normalizeStringSet(filter);
|
|
63
65
|
}
|
|
66
|
+
for (const key of ["filterModes", "pathFilterModes"]) {
|
|
67
|
+
const modes = normalized[key];
|
|
68
|
+
if (Array.isArray(modes)) {
|
|
69
|
+
normalized[key] = normalizeStringSet(modes, FILTER_MODES);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
64
72
|
for (const key of ["create", "update"]) {
|
|
65
73
|
const value = normalized[key];
|
|
66
74
|
if (Array.isArray(value)) {
|
|
@@ -103,7 +111,7 @@ function normalizeJsonSchema(schema) {
|
|
|
103
111
|
normalizeJsonSchema(child),
|
|
104
112
|
]));
|
|
105
113
|
}
|
|
106
|
-
else if (key === "const" || key === "enum") {
|
|
114
|
+
else if (key === "const" || key === "enum" || key === "checks") {
|
|
107
115
|
normalized[key] = value;
|
|
108
116
|
}
|
|
109
117
|
else {
|
|
@@ -173,8 +181,56 @@ function normalizeResource(resource) {
|
|
|
173
181
|
fields,
|
|
174
182
|
identifiers,
|
|
175
183
|
operations: normalizeOperations(resource.operations),
|
|
184
|
+
...(resource.createRequirements === undefined
|
|
185
|
+
? {}
|
|
186
|
+
: {
|
|
187
|
+
createRequirements: normalizeCreateRequirements(resource.createRequirements),
|
|
188
|
+
}),
|
|
189
|
+
...coreResourceFacts(resource),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function coreResourceFacts(resource) {
|
|
193
|
+
const facts = resource;
|
|
194
|
+
return {
|
|
195
|
+
...(facts.schema === undefined ? {} : { schema: facts.schema }),
|
|
196
|
+
...(facts.managedElsewhere === undefined
|
|
197
|
+
? {}
|
|
198
|
+
: { managedElsewhere: facts.managedElsewhere }),
|
|
176
199
|
};
|
|
177
200
|
}
|
|
201
|
+
function isJsonObject(value) {
|
|
202
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
203
|
+
}
|
|
204
|
+
function normalizeCreateRequirements(requirements) {
|
|
205
|
+
if (!isJsonObject(requirements)) {
|
|
206
|
+
return requirements;
|
|
207
|
+
}
|
|
208
|
+
const normalized = { ...requirements };
|
|
209
|
+
if (Array.isArray(requirements.required)) {
|
|
210
|
+
normalized.required = normalizeStringSet(requirements.required);
|
|
211
|
+
}
|
|
212
|
+
if (Array.isArray(requirements.oneOf)) {
|
|
213
|
+
normalized.oneOf = requirements.oneOf
|
|
214
|
+
.map((group) => Array.isArray(group) && group.length > 0
|
|
215
|
+
? [group[0], ...normalizeStringSet(group.slice(1))]
|
|
216
|
+
: group)
|
|
217
|
+
.sort((left, right) => compareCodeUnits(canonicalizeJson(left), canonicalizeJson(right)));
|
|
218
|
+
}
|
|
219
|
+
if (isJsonObject(requirements.parentSupplies)) {
|
|
220
|
+
normalized.parentSupplies = Object.fromEntries(Object.entries(requirements.parentSupplies).map(([parent, byRelation]) => [
|
|
221
|
+
parent,
|
|
222
|
+
isJsonObject(byRelation)
|
|
223
|
+
? Object.fromEntries(Object.entries(byRelation).map(([relation, members]) => [
|
|
224
|
+
relation,
|
|
225
|
+
Array.isArray(members)
|
|
226
|
+
? normalizeStringSet(members)
|
|
227
|
+
: members,
|
|
228
|
+
]))
|
|
229
|
+
: byRelation,
|
|
230
|
+
]));
|
|
231
|
+
}
|
|
232
|
+
return normalized;
|
|
233
|
+
}
|
|
178
234
|
function normalizeContract(contract) {
|
|
179
235
|
const scalars = Object.fromEntries(Object.keys(contract.scalars)
|
|
180
236
|
.sort(compareCodeUnits)
|
|
@@ -201,17 +257,24 @@ function assertValidUnicode(value, path) {
|
|
|
201
257
|
if (code >= 0xd800 && code <= 0xdbff) {
|
|
202
258
|
const next = value.charCodeAt(index + 1);
|
|
203
259
|
if (!(next >= 0xdc00 && next <= 0xdfff)) {
|
|
204
|
-
throw new
|
|
260
|
+
throw new AvCanonicalJsonError(`invalid Unicode surrogate at ${path()}`);
|
|
205
261
|
}
|
|
206
262
|
index += 1;
|
|
207
263
|
continue;
|
|
208
264
|
}
|
|
209
265
|
if (code >= 0xdc00 && code <= 0xdfff) {
|
|
210
|
-
throw new
|
|
266
|
+
throw new AvCanonicalJsonError(`invalid Unicode surrogate at ${path()}`);
|
|
211
267
|
}
|
|
212
268
|
}
|
|
213
269
|
}
|
|
214
|
-
function
|
|
270
|
+
function pathOf(link) {
|
|
271
|
+
const segments = [];
|
|
272
|
+
for (let current = link; current !== undefined; current = current.parent) {
|
|
273
|
+
segments.push(current.segment);
|
|
274
|
+
}
|
|
275
|
+
return `$${segments.reverse().join("")}`;
|
|
276
|
+
}
|
|
277
|
+
function canonicalizePrimitive(value, link) {
|
|
215
278
|
if (value === null) {
|
|
216
279
|
return "null";
|
|
217
280
|
}
|
|
@@ -220,47 +283,123 @@ function canonicalizeValue(value, path) {
|
|
|
220
283
|
return value ? "true" : "false";
|
|
221
284
|
case "number": {
|
|
222
285
|
if (!Number.isFinite(value)) {
|
|
223
|
-
throw new
|
|
286
|
+
throw new AvCanonicalJsonError(`non-finite number at ${pathOf(link)}`);
|
|
224
287
|
}
|
|
225
288
|
const serialized = JSON.stringify(value);
|
|
226
289
|
if (serialized === undefined) {
|
|
227
|
-
throw new
|
|
290
|
+
throw new AvCanonicalJsonError(`number at ${pathOf(link)} is not JSON serializable`);
|
|
228
291
|
}
|
|
229
292
|
return serialized;
|
|
230
293
|
}
|
|
231
294
|
case "string":
|
|
232
|
-
assertValidUnicode(value,
|
|
295
|
+
assertValidUnicode(value, () => pathOf(link));
|
|
233
296
|
return JSON.stringify(value);
|
|
234
297
|
case "undefined":
|
|
235
|
-
throw new
|
|
298
|
+
throw new AvCanonicalJsonError(`undefined value at ${pathOf(link)}`);
|
|
236
299
|
case "bigint":
|
|
237
300
|
case "function":
|
|
238
301
|
case "symbol":
|
|
239
|
-
throw new
|
|
240
|
-
|
|
241
|
-
|
|
302
|
+
throw new AvCanonicalJsonError(`${typeof value} value at ${pathOf(link)} is not JSON serializable`);
|
|
303
|
+
default:
|
|
304
|
+
return undefined;
|
|
242
305
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
306
|
+
}
|
|
307
|
+
function canonicalizeValue(value, link = undefined, undefinedAsJson = false) {
|
|
308
|
+
const output = [];
|
|
309
|
+
const stack = [{ kind: "value", value, link }];
|
|
310
|
+
while (stack.length > 0) {
|
|
311
|
+
const task = stack.pop();
|
|
312
|
+
if (task.kind === "text") {
|
|
313
|
+
output.push(task.text);
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
if (task.kind === "key") {
|
|
317
|
+
assertValidUnicode(task.key, () => `${pathOf(task.link)} key`);
|
|
318
|
+
output.push(`${JSON.stringify(task.key)}:`);
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
const { value: current, link } = task;
|
|
322
|
+
const primitive = canonicalizePrimitive(current, link);
|
|
323
|
+
if (primitive !== undefined) {
|
|
324
|
+
output.push(primitive);
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
if (Array.isArray(current)) {
|
|
328
|
+
output.push("[");
|
|
329
|
+
stack.push({ kind: "text", text: "]" });
|
|
330
|
+
for (let index = current.length - 1; index >= 0; index -= 1) {
|
|
331
|
+
if (undefinedAsJson && current[index] === undefined) {
|
|
332
|
+
stack.push({ kind: "text", text: "null" });
|
|
333
|
+
}
|
|
334
|
+
else if (index in current) {
|
|
335
|
+
stack.push({
|
|
336
|
+
kind: "value",
|
|
337
|
+
value: current[index],
|
|
338
|
+
link: { parent: link, segment: `[${index}]` },
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
if (index > 0) {
|
|
342
|
+
stack.push({ kind: "text", text: "," });
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
const prototype = Object.getPrototypeOf(current);
|
|
348
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
349
|
+
throw new AvCanonicalJsonError(`non-plain object at ${pathOf(link)} is not canonical JSON`);
|
|
350
|
+
}
|
|
351
|
+
const record = current;
|
|
352
|
+
const keys = Object.keys(record)
|
|
353
|
+
.filter((key) => !undefinedAsJson || record[key] !== undefined)
|
|
354
|
+
.sort(compareCodeUnits);
|
|
355
|
+
output.push("{");
|
|
356
|
+
stack.push({ kind: "text", text: "}" });
|
|
357
|
+
for (let index = keys.length - 1; index >= 0; index -= 1) {
|
|
358
|
+
const key = keys[index];
|
|
359
|
+
stack.push({
|
|
360
|
+
kind: "value",
|
|
361
|
+
value: record[key],
|
|
362
|
+
link: { parent: link, segment: `.${key}` },
|
|
363
|
+
});
|
|
364
|
+
stack.push({ kind: "key", key, link });
|
|
365
|
+
if (index > 0) {
|
|
366
|
+
stack.push({ kind: "text", text: "," });
|
|
367
|
+
}
|
|
368
|
+
}
|
|
251
369
|
}
|
|
252
|
-
|
|
253
|
-
|
|
370
|
+
return output.join("");
|
|
371
|
+
}
|
|
372
|
+
export function canonicalizeJson(value) {
|
|
373
|
+
return canonicalizeValue(value);
|
|
374
|
+
}
|
|
375
|
+
export function canonicalizeWireJson(value) {
|
|
376
|
+
return canonicalizeValue(value, undefined, true);
|
|
377
|
+
}
|
|
378
|
+
export function canonicalizeContractMembers(contract) {
|
|
379
|
+
const record = normalizeContract(contract);
|
|
380
|
+
return Object.keys(record)
|
|
254
381
|
.sort(compareCodeUnits)
|
|
255
382
|
.map((key) => {
|
|
256
|
-
assertValidUnicode(key,
|
|
257
|
-
return
|
|
383
|
+
assertValidUnicode(key, () => "$ key");
|
|
384
|
+
return {
|
|
385
|
+
key,
|
|
386
|
+
text: canonicalizeValue(record[key], {
|
|
387
|
+
parent: undefined,
|
|
388
|
+
segment: `.${key}`,
|
|
389
|
+
}),
|
|
390
|
+
};
|
|
258
391
|
});
|
|
259
|
-
return `{${entries.join(",")}}`;
|
|
260
392
|
}
|
|
261
|
-
export function
|
|
262
|
-
return
|
|
393
|
+
export function joinCanonicalContractMembers(members) {
|
|
394
|
+
return `{${members.map(({ key, text }) => `${JSON.stringify(key)}:${text}`).join(",")}}`;
|
|
263
395
|
}
|
|
264
396
|
export function canonicalizeContract(contract) {
|
|
265
|
-
return
|
|
397
|
+
return joinCanonicalContractMembers(canonicalizeContractMembers(contract));
|
|
398
|
+
}
|
|
399
|
+
const canonicalTexts = new WeakMap();
|
|
400
|
+
export function recordCanonicalContractText(contract, text) {
|
|
401
|
+
canonicalTexts.set(contract, text);
|
|
402
|
+
}
|
|
403
|
+
export function canonicalContractTextOf(contract) {
|
|
404
|
+
return canonicalTexts.get(contract) ?? canonicalizeContract(contract);
|
|
266
405
|
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { AvContractClient } from "./contract.js";
|
|
2
|
+
import type { AvContractHash, AvContractProtocol } from "./contract-protocol.js";
|
|
3
3
|
/** A ClientContract whose `protocol` does not carry the hash yet. */
|
|
4
|
-
export type
|
|
5
|
-
readonly protocol:
|
|
4
|
+
export type AvContractClientHashInput = Omit<AvContractClient, "protocol"> & {
|
|
5
|
+
readonly protocol: AvContractProtocol;
|
|
6
6
|
};
|
|
7
|
-
export declare function createClientContractHashInput(contract: ClientContract | ClientContractHashInput): ClientContractHashInput;
|
|
8
7
|
/** Computes a ClientContract's `sha256:` hash, ignoring `protocol.hash` itself. */
|
|
9
|
-
export declare function computeClientContractHash(contract:
|
|
8
|
+
export declare function computeClientContractHash(contract: AvContractClient | AvContractClientHashInput): Promise<AvContractHash>;
|
|
9
|
+
/**
|
|
10
|
+
* The `sha256:` hash of a hash input's canonical text, already written: what
|
|
11
|
+
* {@link computeClientContractHash} answers for the input that text is of.
|
|
12
|
+
*/
|
|
13
|
+
export declare function hashCanonicalContractText(canonical: string): Promise<AvContractHash>;
|
|
10
14
|
/** Whether a ClientContract's `protocol.hash` matches its content. */
|
|
11
|
-
export declare function verifyClientContractHash(contract:
|
|
15
|
+
export declare function verifyClientContractHash(contract: AvContractClient): Promise<boolean>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { canonicalizeContract } from "./contract-canonicalizer.js";
|
|
2
|
-
|
|
2
|
+
function createClientContractHashInput(contract) {
|
|
3
3
|
const { protocol, ...semantic } = contract;
|
|
4
4
|
return {
|
|
5
5
|
...semantic,
|
|
@@ -10,8 +10,9 @@ function digestHex(digest) {
|
|
|
10
10
|
return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
11
11
|
}
|
|
12
12
|
export async function computeClientContractHash(contract) {
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
return hashCanonicalContractText(canonicalizeContract(createClientContractHashInput(contract)));
|
|
14
|
+
}
|
|
15
|
+
export async function hashCanonicalContractText(canonical) {
|
|
15
16
|
const bytes = new TextEncoder().encode(canonical);
|
|
16
17
|
const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
|
|
17
18
|
return `sha256:${digestHex(digest)}`;
|