@aventara/core 0.1.0-pilot.4 → 1.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1478 -0
- package/README.md +275 -11
- package/dist/adapters/adapter-discovery-diagnostic.d.ts +4 -4
- package/dist/adapters/adapter-field.d.ts +11 -11
- package/dist/adapters/adapter-identifier.d.ts +6 -6
- package/dist/adapters/adapter-model.d.ts +17 -9
- package/dist/adapters/adapter-operation-executor.interface.d.ts +4 -4
- package/dist/adapters/adapter-operation.d.ts +3 -3
- package/dist/adapters/adapter-resource.d.ts +18 -9
- package/dist/adapters/adapter-sql-statement.d.ts +19 -0
- package/dist/adapters/adapter-transaction-executor.interface.d.ts +19 -0
- package/dist/adapters/adapter.interface.d.ts +49 -25
- package/dist/avz/avz-check.factory.d.ts +83 -0
- package/dist/avz/avz-check.factory.js +116 -0
- package/dist/avz/avz-check.registry.d.ts +35 -0
- package/dist/avz/avz-check.registry.js +9 -0
- package/dist/avz/avz.d.ts +203 -0
- package/dist/avz/avz.js +354 -0
- package/dist/avz/index.d.ts +7 -0
- package/dist/avz/index.js +1 -0
- package/dist/avz/json-shape.registry.d.ts +22 -0
- package/dist/avz/json-shape.registry.js +16 -0
- package/dist/avz/zod-issue.translator.d.ts +57 -0
- package/dist/avz/zod-issue.translator.js +149 -0
- package/dist/compiler/contract-compiler-diagnostic.d.ts +37 -5
- package/dist/compiler/contract.compiler.d.ts +18 -18
- package/dist/compiler/contract.compiler.js +595 -421
- package/dist/compiler/create-requirements.projector.d.ts +49 -0
- package/dist/compiler/create-requirements.projector.js +423 -0
- package/dist/compiler/field-validator.compiler.d.ts +47 -0
- package/dist/compiler/field-validator.compiler.js +238 -0
- package/dist/config/any-framework-config.d.ts +19 -12
- package/dist/config/config-diagnostic.d.ts +11 -4
- package/dist/config/config-layer.d.ts +15 -6
- package/dist/config/config.resolver.d.ts +75 -29
- package/dist/config/config.resolver.js +109 -44
- package/dist/config/config.validator.d.ts +7 -7
- package/dist/config/config.validator.js +586 -71
- package/dist/config/define-resource.d.ts +57 -0
- package/dist/config/define-resource.js +14 -0
- package/dist/config/effective-fields.d.ts +19 -23
- package/dist/config/entrypoint.normalizer.d.ts +1 -1
- package/dist/config/exact-framework-config.d.ts +280 -0
- package/dist/config/field-compute.resolver.d.ts +19 -0
- package/dist/config/field-compute.resolver.js +115 -0
- package/dist/config/field-pipeline-config.d.ts +47 -0
- package/dist/config/forced-operation.validator.d.ts +10 -0
- package/dist/config/forced-operation.validator.js +66 -0
- package/dist/config/framework-config.d.ts +111 -37
- package/dist/config/framework-exemptions-config.d.ts +91 -0
- package/dist/config/framework-exemptions-config.js +1 -0
- package/dist/config/framework-field-definition.d.ts +78 -0
- package/dist/config/framework-field-definition.js +28 -0
- package/dist/config/framework-resources-config.d.ts +116 -0
- package/dist/config/framework-resources-config.js +1 -0
- package/dist/config/pipeline-config.d.ts +56 -20
- package/dist/config/pipeline-tree.resolver.d.ts +79 -0
- package/dist/config/pipeline-tree.resolver.js +220 -0
- package/dist/config/stage-type.eraser.d.ts +65 -0
- package/dist/config/stage-type.eraser.js +1 -0
- package/dist/contracts/capability.d.ts +11 -11
- package/dist/contracts/client-contract-structure.validator.d.ts +344 -29
- package/dist/contracts/client-contract-structure.validator.js +248 -7
- package/dist/contracts/contract-canonicalizer.d.ts +19 -3
- package/dist/contracts/contract-canonicalizer.js +172 -27
- package/dist/contracts/contract-hash.d.ts +12 -7
- package/dist/contracts/contract-hash.js +3 -2
- package/dist/contracts/contract-limits.d.ts +4 -1
- package/dist/contracts/contract-limits.js +1 -0
- package/dist/contracts/contract-lookup.d.ts +5 -5
- package/dist/contracts/contract-protocol.d.ts +4 -4
- package/dist/contracts/contract.d.ts +31 -16
- package/dist/contracts/create-requirements.d.ts +35 -0
- package/dist/contracts/create-requirements.js +1 -0
- package/dist/contracts/enum-definition.d.ts +1 -1
- package/dist/contracts/field-check.d.ts +101 -0
- package/dist/contracts/field-check.js +1 -0
- package/dist/contracts/field.d.ts +61 -41
- package/dist/contracts/identifier.d.ts +4 -4
- package/dist/contracts/json-shape.d.ts +76 -0
- package/dist/contracts/json-shape.js +1 -0
- package/dist/contracts/lifecycle-trait.d.ts +2 -2
- package/dist/contracts/reference-write-limit.d.ts +7 -0
- package/dist/contracts/reference-write-limit.js +1 -0
- package/dist/contracts/resource-operations.d.ts +28 -28
- package/dist/contracts/resource.d.ts +15 -9
- package/dist/contracts/scalar-value-type.d.ts +25 -14
- package/dist/contracts/scalar.d.ts +3 -3
- package/dist/contracts/transaction-capability.d.ts +1 -1
- package/dist/contracts/wire-scalar-format.d.ts +4 -4
- package/dist/framework/contract-view.d.ts +234 -63
- package/dist/framework/framework-construction.error.d.ts +10 -10
- package/dist/framework/framework-construction.error.js +2 -2
- package/dist/framework/framework.d.ts +50 -24
- package/dist/framework/framework.factory.d.ts +8 -6
- package/dist/framework/framework.factory.js +95 -32
- package/dist/framework/operation-facade.d.ts +123 -52
- package/dist/framework/operation-facade.js +9 -1
- package/dist/index.d.ts +55 -82
- package/dist/index.js +6 -26
- package/dist/internal.d.ts +40 -0
- package/dist/internal.js +18 -0
- package/dist/operations/operation-arguments.d.ts +316 -201
- package/dist/operations/operation-call.d.ts +27 -28
- package/dist/operations/operation-identity.d.ts +12 -10
- package/dist/operations/operation-identity.js +3 -0
- package/dist/operations/operation-result.d.ts +21 -21
- package/dist/operations/operation-validator-core.d.ts +6 -6
- package/dist/operations/operation-validator-core.js +41 -12
- package/dist/operations/operation-validator.d.ts +24 -9
- package/dist/operations/operation-validator.js +26 -10
- package/dist/operations/projection-scope.resolver.d.ts +7 -7
- package/dist/operations/structural-limits.walker.d.ts +19 -0
- package/dist/operations/structural-limits.walker.js +489 -0
- package/dist/operations/validation-code.d.ts +11 -6
- package/dist/operations/validation-code.js +4 -0
- package/dist/operations/write-scope.resolver.d.ts +6 -6
- package/dist/protocol/absent-route.responder.d.ts +13 -11
- package/dist/protocol/absent-route.responder.js +42 -16
- package/dist/protocol/contract-document.responder.d.ts +3 -3
- package/dist/protocol/contract-document.responder.js +2 -2
- package/dist/protocol/http-request.decoder.d.ts +23 -13
- package/dist/protocol/http-request.decoder.js +19 -11
- package/dist/protocol/http-request.reader.d.ts +3 -3
- package/dist/protocol/http-response.encoder.d.ts +4 -4
- package/dist/protocol/http-response.encoder.js +94 -55
- package/dist/protocol/http-status.table.d.ts +2 -2
- package/dist/protocol/operation-response.guard.d.ts +2 -2
- package/dist/protocol/protocol-surface.d.ts +4 -4
- package/dist/protocol/protocol.binder.d.ts +52 -27
- package/dist/protocol/protocol.binder.js +14 -9
- package/dist/protocol/protocol.handler.d.ts +28 -15
- package/dist/protocol/protocol.handler.js +55 -10
- package/dist/protocol/unmounted-route.error.d.ts +19 -0
- package/dist/protocol/unmounted-route.error.js +10 -0
- package/dist/runtime/caller-data.copier.d.ts +59 -0
- package/dist/runtime/caller-data.copier.js +470 -0
- package/dist/runtime/caller-data.door.d.ts +56 -0
- package/dist/runtime/caller-data.door.js +67 -0
- package/dist/runtime/computed-read-runtime.d.ts +7 -6
- package/dist/runtime/computed-read-runtime.js +61 -55
- package/dist/runtime/computed-value.error.d.ts +13 -0
- package/dist/runtime/computed-value.error.js +9 -0
- package/dist/runtime/context-executor.runtime.d.ts +59 -0
- package/dist/runtime/context-executor.runtime.js +275 -0
- package/dist/runtime/create-requirement.d.ts +22 -57
- package/dist/runtime/create-requirement.js +140 -37
- package/dist/runtime/decimal.d.ts +2 -2
- package/dist/runtime/decimal.js +1 -1
- package/dist/runtime/diagnostics-reporter.d.ts +64 -22
- package/dist/runtime/diagnostics-reporter.js +35 -4
- package/dist/runtime/error-boundary.d.ts +73 -8
- package/dist/runtime/error-boundary.js +104 -48
- package/dist/runtime/execution-options.d.ts +10 -2
- package/dist/runtime/field-check.evaluator.d.ts +29 -0
- package/dist/runtime/field-check.evaluator.js +521 -0
- package/dist/runtime/field-compute.d.ts +64 -0
- package/dist/runtime/field-compute.js +1 -0
- package/dist/runtime/field-stage.plan.d.ts +15 -0
- package/dist/runtime/field-stage.plan.js +27 -0
- package/dist/runtime/field-stage.runtime.d.ts +35 -0
- package/dist/runtime/field-stage.runtime.js +143 -0
- package/dist/runtime/field-validator.runtime.d.ts +11 -0
- package/dist/runtime/field-validator.runtime.js +87 -0
- package/dist/runtime/framework-error-class.table.d.ts +33 -0
- package/dist/runtime/framework-error-class.table.js +25 -0
- package/dist/runtime/framework-error.d.ts +79 -7
- package/dist/runtime/framework-error.factory.d.ts +4 -0
- package/dist/runtime/framework-error.factory.js +15 -0
- package/dist/runtime/framework-error.js +23 -2
- package/dist/runtime/interceptor-next.error.d.ts +5 -0
- package/dist/runtime/interceptor-next.error.js +6 -0
- package/dist/runtime/json-value.d.ts +3 -3
- package/dist/runtime/level-lifecycle.runtime.d.ts +25 -0
- package/dist/runtime/level-lifecycle.runtime.js +42 -0
- package/dist/runtime/misuse-reporter.d.ts +32 -0
- package/dist/runtime/misuse-reporter.js +19 -0
- package/dist/runtime/mutation-planner.d.ts +19 -10
- package/dist/runtime/mutation-planner.js +62 -36
- package/dist/runtime/nested-write.walker.d.ts +32 -0
- package/dist/runtime/nested-write.walker.js +73 -0
- package/dist/runtime/operation-request.d.ts +35 -15
- package/dist/runtime/operation-response.d.ts +17 -17
- package/dist/runtime/operation-runtime.d.ts +125 -30
- package/dist/runtime/operation-runtime.js +423 -83
- package/dist/runtime/outcome-mapper.d.ts +16 -5
- package/dist/runtime/outcome-mapper.js +35 -8
- package/dist/runtime/output-validator.d.ts +16 -6
- package/dist/runtime/output-validator.js +12 -1
- package/dist/runtime/owned-execution.registry.d.ts +13 -0
- package/dist/runtime/owned-execution.registry.js +7 -0
- package/dist/runtime/pipe-output.validator.d.ts +33 -18
- package/dist/runtime/pipe-output.validator.js +12 -12
- package/dist/runtime/pipeline.d.ts +202 -28
- package/dist/runtime/readonly-value.d.ts +32 -0
- package/dist/runtime/readonly-value.js +141 -0
- package/dist/runtime/referential-action.d.ts +7 -0
- package/dist/runtime/request-envelope.validator.d.ts +5 -0
- package/dist/runtime/request-envelope.validator.js +123 -0
- package/dist/runtime/request-id.d.ts +1 -1
- package/dist/runtime/request-state.d.ts +14 -0
- package/dist/runtime/request-state.js +1 -0
- package/dist/runtime/request-state.resolver.d.ts +12 -0
- package/dist/runtime/request-state.resolver.js +20 -0
- package/dist/runtime/scalar-runtime.d.ts +56 -13
- package/dist/runtime/scalar-runtime.js +167 -86
- package/dist/transactions/deferred-operation.d.ts +35 -20
- package/dist/transactions/operation-arguments-encoder.d.ts +3 -3
- package/dist/transactions/operation-arguments-encoder.js +2 -2
- package/dist/transactions/sql-statement.d.ts +77 -0
- package/dist/transactions/sql-statement.js +47 -0
- package/dist/transactions/sql-statement.validator.d.ts +44 -0
- package/dist/transactions/sql-statement.validator.js +134 -0
- package/dist/transactions/transaction-envelope.d.ts +4 -4
- package/dist/transactions/transaction-integrity-error.d.ts +6 -6
- package/dist/transactions/transaction-integrity-error.js +5 -2
- package/dist/transactions/transaction-plan-assembler.d.ts +23 -9
- package/dist/transactions/transaction-plan-assembler.js +195 -82
- package/dist/transactions/transaction-plan-validator.d.ts +53 -21
- package/dist/transactions/transaction-plan-validator.js +246 -163
- package/dist/transactions/transaction-reference-collector.d.ts +30 -0
- package/dist/transactions/transaction-reference-collector.js +204 -0
- package/dist/transactions/transaction-reference-resolver.d.ts +19 -2
- package/dist/transactions/transaction-reference-resolver.js +27 -10
- package/dist/transactions/transaction-reference-validator.d.ts +34 -4
- package/dist/transactions/transaction-reference-validator.js +82 -43
- package/dist/transactions/transaction-reference.d.ts +30 -58
- package/dist/transactions/transaction-request.d.ts +20 -7
- package/dist/transactions/transaction-response.d.ts +12 -7
- package/dist/transactions/transaction-response.js +24 -1
- package/dist/transactions/transaction-runtime.d.ts +11 -8
- package/dist/transactions/transaction-runtime.js +167 -50
- package/dist/transactions/transaction-settlement.runtime.d.ts +51 -0
- package/dist/transactions/transaction-settlement.runtime.js +120 -0
- package/dist/transactions/transaction-slot-path-error.d.ts +4 -4
- package/dist/transactions/transaction-slot-path-error.js +2 -2
- package/dist/transactions/transaction-slot-writer.js +2 -2
- package/dist/transactions/transaction-stage.runtime.d.ts +81 -0
- package/dist/transactions/transaction-stage.runtime.js +123 -0
- package/package.json +18 -3
- package/src/runtime/field-check.evaluator.ts +720 -0
- package/dist/config/define-fields.d.ts +0 -35
- package/dist/config/define-fields.js +0 -3
- package/dist/config/framework-behaviors-config.d.ts +0 -30
- package/dist/config/framework-fields-config.d.ts +0 -36
- package/dist/config/framework-restrictions-config.d.ts +0 -142
- package/dist/operations/operation-boolean-nodes.d.ts +0 -11
- package/dist/operations/operation-boolean-nodes.js +0 -283
- package/dist/operations/operation-nesting.d.ts +0 -8
- package/dist/operations/operation-nesting.js +0 -307
- package/dist/runtime/field-behavior.d.ts +0 -48
- /package/dist/{config/framework-behaviors-config.js → adapters/adapter-sql-statement.js} +0 -0
- /package/dist/{config/framework-fields-config.js → adapters/adapter-transaction-executor.interface.js} +0 -0
- /package/dist/config/{framework-restrictions-config.js → exact-framework-config.js} +0 -0
- /package/dist/{runtime/field-behavior.js → config/field-pipeline-config.js} +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PROJECTION_SCOPES, WRITE_SCOPES } from "./capability.js";
|
|
2
2
|
import { AVENTARA_PROTOCOL_VERSION } from "./contract-protocol.js";
|
|
3
3
|
import { LIFECYCLE_TRAITS } from "./lifecycle-trait.js";
|
|
4
|
+
import { REFERENCE_WRITE_LIMITS } from "./reference-write-limit.js";
|
|
4
5
|
import { TRANSACTION_CAPABILITIES } from "./transaction-capability.js";
|
|
5
6
|
const STRING = { is: "string" };
|
|
6
7
|
const NUMBER = { is: "number" };
|
|
@@ -9,13 +10,16 @@ function literal(values) {
|
|
|
9
10
|
return { is: "literal", values };
|
|
10
11
|
}
|
|
11
12
|
function list(item) {
|
|
12
|
-
return { is: "list", item,
|
|
13
|
+
return { is: "list", item, minimum: 0, unique: false };
|
|
13
14
|
}
|
|
14
15
|
function nonEmptyList(item) {
|
|
15
|
-
return { is: "list", item,
|
|
16
|
+
return { is: "list", item, minimum: 1, unique: false };
|
|
17
|
+
}
|
|
18
|
+
function pairOrMoreList(item) {
|
|
19
|
+
return { is: "list", item, minimum: 2, unique: false };
|
|
16
20
|
}
|
|
17
21
|
function uniqueList(item) {
|
|
18
|
-
return { is: "list", item,
|
|
22
|
+
return { is: "list", item, minimum: 0, unique: true };
|
|
19
23
|
}
|
|
20
24
|
function registry(entry) {
|
|
21
25
|
return { is: "registry", entry };
|
|
@@ -92,6 +96,127 @@ const RELATION_MUTATION = union(list(literal(RELATION_MUTATION_DIRECTIVES)), obj
|
|
|
92
96
|
directives: list(literal(RELATION_MUTATION_DIRECTIVES)),
|
|
93
97
|
scopes: WRITE_SCOPE_LIST,
|
|
94
98
|
}));
|
|
99
|
+
const NAMED_FORMATS = [
|
|
100
|
+
"lowercase",
|
|
101
|
+
"uppercase",
|
|
102
|
+
"email",
|
|
103
|
+
"url",
|
|
104
|
+
"emoji",
|
|
105
|
+
"uuid",
|
|
106
|
+
"guid",
|
|
107
|
+
"nanoid",
|
|
108
|
+
"cuid",
|
|
109
|
+
"cuid2",
|
|
110
|
+
"ulid",
|
|
111
|
+
"xid",
|
|
112
|
+
"ksuid",
|
|
113
|
+
"datetime",
|
|
114
|
+
"date",
|
|
115
|
+
"time",
|
|
116
|
+
"duration",
|
|
117
|
+
"ipv4",
|
|
118
|
+
"ipv6",
|
|
119
|
+
"cidrv4",
|
|
120
|
+
"cidrv6",
|
|
121
|
+
"base64",
|
|
122
|
+
"base64url",
|
|
123
|
+
"json_string",
|
|
124
|
+
"e164",
|
|
125
|
+
"credit_card",
|
|
126
|
+
"iban",
|
|
127
|
+
"jwt",
|
|
128
|
+
];
|
|
129
|
+
const STRING_FORMAT_CHECK = union(object({
|
|
130
|
+
check: literal(["string_format"]),
|
|
131
|
+
format: literal(["regex"]),
|
|
132
|
+
pattern: object({ source: STRING, flags: STRING }),
|
|
133
|
+
}), object({
|
|
134
|
+
check: literal(["string_format"]),
|
|
135
|
+
format: literal(["includes"]),
|
|
136
|
+
includes: STRING,
|
|
137
|
+
}), object({
|
|
138
|
+
check: literal(["string_format"]),
|
|
139
|
+
format: literal(["starts_with"]),
|
|
140
|
+
prefix: STRING,
|
|
141
|
+
}), object({
|
|
142
|
+
check: literal(["string_format"]),
|
|
143
|
+
format: literal(["ends_with"]),
|
|
144
|
+
suffix: STRING,
|
|
145
|
+
}), object({
|
|
146
|
+
check: literal(["string_format"]),
|
|
147
|
+
format: literal(NAMED_FORMATS),
|
|
148
|
+
}));
|
|
149
|
+
const STRING_LENGTH_CHECKS = {
|
|
150
|
+
max_length: object({ check: literal(["max_length"]), maximum: NUMBER }),
|
|
151
|
+
min_length: object({ check: literal(["min_length"]), minimum: NUMBER }),
|
|
152
|
+
length_equals: object({ check: literal(["length_equals"]), length: NUMBER }),
|
|
153
|
+
string_format: STRING_FORMAT_CHECK,
|
|
154
|
+
};
|
|
155
|
+
const FIELD_CHECK = {
|
|
156
|
+
is: "variants",
|
|
157
|
+
key: "check",
|
|
158
|
+
arms: {
|
|
159
|
+
less_than: object({
|
|
160
|
+
check: literal(["less_than"]),
|
|
161
|
+
value: union(NUMBER, STRING),
|
|
162
|
+
inclusive: BOOLEAN,
|
|
163
|
+
}),
|
|
164
|
+
greater_than: object({
|
|
165
|
+
check: literal(["greater_than"]),
|
|
166
|
+
value: union(NUMBER, STRING),
|
|
167
|
+
inclusive: BOOLEAN,
|
|
168
|
+
}),
|
|
169
|
+
multiple_of: object({
|
|
170
|
+
check: literal(["multiple_of"]),
|
|
171
|
+
value: union(NUMBER, STRING),
|
|
172
|
+
}),
|
|
173
|
+
max_length: object({ check: literal(["max_length"]), maximum: NUMBER }, { list: literal([true]) }),
|
|
174
|
+
min_length: object({ check: literal(["min_length"]), minimum: NUMBER }, { list: literal([true]) }),
|
|
175
|
+
length_equals: STRING_LENGTH_CHECKS.length_equals,
|
|
176
|
+
string_format: STRING_FORMAT_CHECK,
|
|
177
|
+
max_size: object({ check: literal(["max_size"]), maximum: NUMBER }),
|
|
178
|
+
min_size: object({ check: literal(["min_size"]), minimum: NUMBER }),
|
|
179
|
+
size_equals: object({ check: literal(["size_equals"]), size: NUMBER }),
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
const JSON_STRING_CHECK = {
|
|
183
|
+
is: "variants",
|
|
184
|
+
key: "check",
|
|
185
|
+
arms: STRING_LENGTH_CHECKS,
|
|
186
|
+
};
|
|
187
|
+
const JSON_NUMBER_CHECK = {
|
|
188
|
+
is: "variants",
|
|
189
|
+
key: "check",
|
|
190
|
+
arms: {
|
|
191
|
+
less_than: object({
|
|
192
|
+
check: literal(["less_than"]),
|
|
193
|
+
value: NUMBER,
|
|
194
|
+
inclusive: BOOLEAN,
|
|
195
|
+
}),
|
|
196
|
+
greater_than: object({
|
|
197
|
+
check: literal(["greater_than"]),
|
|
198
|
+
value: NUMBER,
|
|
199
|
+
inclusive: BOOLEAN,
|
|
200
|
+
}),
|
|
201
|
+
multiple_of: object({ check: literal(["multiple_of"]), value: NUMBER }),
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
const JSON_LIST_CHECK = {
|
|
205
|
+
is: "variants",
|
|
206
|
+
key: "check",
|
|
207
|
+
arms: {
|
|
208
|
+
max_length: object({
|
|
209
|
+
check: literal(["max_length"]),
|
|
210
|
+
maximum: NUMBER,
|
|
211
|
+
list: literal([true]),
|
|
212
|
+
}),
|
|
213
|
+
min_length: object({
|
|
214
|
+
check: literal(["min_length"]),
|
|
215
|
+
minimum: NUMBER,
|
|
216
|
+
list: literal([true]),
|
|
217
|
+
}),
|
|
218
|
+
},
|
|
219
|
+
};
|
|
95
220
|
const SCALAR_FIELD = object({
|
|
96
221
|
kind: literal(["scalar"]),
|
|
97
222
|
type: union(object({ scalar: STRING }), object({ enum: STRING })),
|
|
@@ -106,7 +231,11 @@ const SCALAR_FIELD = object({
|
|
|
106
231
|
create: list(literal(SCALAR_MUTATION_DIRECTIVES)),
|
|
107
232
|
update: list(literal(SCALAR_MUTATION_DIRECTIVES)),
|
|
108
233
|
}),
|
|
109
|
-
}, {
|
|
234
|
+
}, {
|
|
235
|
+
format: STRING,
|
|
236
|
+
json: { is: "jsonSchema" },
|
|
237
|
+
validators: list(FIELD_CHECK),
|
|
238
|
+
});
|
|
110
239
|
const RELATION_FIELD = object({
|
|
111
240
|
kind: literal(["relation"]),
|
|
112
241
|
target: STRING,
|
|
@@ -124,6 +253,7 @@ const RELATION_FIELD = object({
|
|
|
124
253
|
})),
|
|
125
254
|
create: RELATION_MUTATION,
|
|
126
255
|
update: RELATION_MUTATION,
|
|
256
|
+
nestedWriteFilter: literal(["scalar"]),
|
|
127
257
|
}),
|
|
128
258
|
});
|
|
129
259
|
const OPERATION_AVAILABILITY = union(literal([true]), object({ projection: literal(PROJECTION_SCOPES) }, { write: literal(WRITE_SCOPES) }), object({ write: literal(WRITE_SCOPES) }, { projection: literal(PROJECTION_SCOPES) }));
|
|
@@ -160,6 +290,12 @@ const RESOURCE = object({
|
|
|
160
290
|
}),
|
|
161
291
|
upsert: object({}, { unique: OPERATION_AVAILABILITY }),
|
|
162
292
|
}),
|
|
293
|
+
}, {
|
|
294
|
+
createRequirements: object({}, {
|
|
295
|
+
required: nonEmptyList(STRING),
|
|
296
|
+
oneOf: nonEmptyList(pairOrMoreList(STRING)),
|
|
297
|
+
parentSupplies: registry(registry(nonEmptyList(STRING))),
|
|
298
|
+
}),
|
|
163
299
|
});
|
|
164
300
|
export const CLIENT_CONTRACT_STRUCTURE = object({
|
|
165
301
|
scalars: registry(object({ builtin: literal([true]) })),
|
|
@@ -171,13 +307,14 @@ export const CLIENT_CONTRACT_STRUCTURE = object({
|
|
|
171
307
|
maxListLimit: NUMBER,
|
|
172
308
|
maxTransactionOperations: NUMBER,
|
|
173
309
|
maxBooleanNodes: NUMBER,
|
|
310
|
+
maxJsonDepth: NUMBER,
|
|
174
311
|
}),
|
|
175
312
|
transactions: literal(TRANSACTION_CAPABILITIES),
|
|
176
313
|
protocol: object({
|
|
177
314
|
version: literal([AVENTARA_PROTOCOL_VERSION]),
|
|
178
315
|
hash: { is: "prefixed", prefix: "sha256:" },
|
|
179
316
|
}),
|
|
180
|
-
});
|
|
317
|
+
}, { referenceWrites: literal(REFERENCE_WRITE_LIMITS) });
|
|
181
318
|
export function validateClientContractStructure(value) {
|
|
182
319
|
const defect = defectOf(CLIENT_CONTRACT_STRUCTURE, value, "$", 0);
|
|
183
320
|
if (defect !== undefined) {
|
|
@@ -233,6 +370,108 @@ function defectOf(node, value, path, depth) {
|
|
|
233
370
|
}
|
|
234
371
|
case "union":
|
|
235
372
|
return unionDefect(node.arms, value, path, depth);
|
|
373
|
+
case "jsonSchema":
|
|
374
|
+
return jsonSchemaDefect(value, path, depth);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
const JSON_SCHEMA_SCALAR_TYPES = [
|
|
378
|
+
"string",
|
|
379
|
+
"number",
|
|
380
|
+
"integer",
|
|
381
|
+
"boolean",
|
|
382
|
+
"null",
|
|
383
|
+
];
|
|
384
|
+
const JSON_PRIMITIVE = union(STRING, NUMBER, BOOLEAN);
|
|
385
|
+
function jsonSchemaDefect(value, path, depth) {
|
|
386
|
+
const kind = kindDefect(value, "object", path, depth);
|
|
387
|
+
if (kind !== undefined)
|
|
388
|
+
return kind;
|
|
389
|
+
const schema = value;
|
|
390
|
+
const keys = Object.keys(schema).sort().join(",");
|
|
391
|
+
const child = (member, entry, at = `${path}.${member}`) => jsonSchemaDefect(entry, at, depth + 1);
|
|
392
|
+
const children = (member, minimum) => {
|
|
393
|
+
const listKind = kindDefect(schema[member], "array", `${path}.${member}`, depth + 1);
|
|
394
|
+
if (listKind !== undefined)
|
|
395
|
+
return listKind;
|
|
396
|
+
const entries = schema[member];
|
|
397
|
+
if (entries.length < minimum) {
|
|
398
|
+
return at(`${path}.${member}`, depth + 1, `needs at least ${minimum} entries`);
|
|
399
|
+
}
|
|
400
|
+
for (let index = 0; index < entries.length; index += 1) {
|
|
401
|
+
const defect = child(member, entries[index], `${path}.${member}[${index}]`);
|
|
402
|
+
if (defect !== undefined)
|
|
403
|
+
return defect;
|
|
404
|
+
}
|
|
405
|
+
return undefined;
|
|
406
|
+
};
|
|
407
|
+
switch (keys) {
|
|
408
|
+
case "type":
|
|
409
|
+
return literalDefect(JSON_SCHEMA_SCALAR_TYPES, schema.type, `${path}.type`, depth + 1);
|
|
410
|
+
case "checks,type":
|
|
411
|
+
return (literalDefect(["string", "number", "integer"], schema.type, `${path}.type`, depth + 1) ??
|
|
412
|
+
defectOf(list(schema.type === "string" ? JSON_STRING_CHECK : JSON_NUMBER_CHECK), schema.checks, `${path}.checks`, depth + 1));
|
|
413
|
+
case "checks,items,type":
|
|
414
|
+
return (literalDefect(["array"], schema.type, `${path}.type`, depth + 1) ??
|
|
415
|
+
child("items", schema.items) ??
|
|
416
|
+
defectOf(list(JSON_LIST_CHECK), schema.checks, `${path}.checks`, depth + 1));
|
|
417
|
+
case "const":
|
|
418
|
+
return Object.is(schema.const, null)
|
|
419
|
+
? undefined
|
|
420
|
+
: defectOf(JSON_PRIMITIVE, schema.const, `${path}.const`, depth + 1);
|
|
421
|
+
case "enum,type":
|
|
422
|
+
return (literalDefect(["string"], schema.type, `${path}.type`, depth + 1) ??
|
|
423
|
+
defectOf({ is: "list", item: STRING, minimum: 1, unique: true }, schema.enum, `${path}.enum`, depth + 1));
|
|
424
|
+
case "additionalProperties,properties,type":
|
|
425
|
+
case "additionalProperties,properties,required,type": {
|
|
426
|
+
const typeDefect = literalDefect(["object"], schema.type, `${path}.type`, depth + 1) ??
|
|
427
|
+
literalDefect([false], schema.additionalProperties, `${path}.additionalProperties`, depth + 1);
|
|
428
|
+
if (typeDefect !== undefined)
|
|
429
|
+
return typeDefect;
|
|
430
|
+
const propertiesKind = kindDefect(schema.properties, "object", `${path}.properties`, depth + 1);
|
|
431
|
+
if (propertiesKind !== undefined)
|
|
432
|
+
return propertiesKind;
|
|
433
|
+
const properties = schema.properties;
|
|
434
|
+
for (const name of Object.keys(properties)) {
|
|
435
|
+
const defect = child("properties", properties[name], `${path}.properties.${name}`);
|
|
436
|
+
if (defect !== undefined)
|
|
437
|
+
return defect;
|
|
438
|
+
}
|
|
439
|
+
if (!Object.hasOwn(schema, "required"))
|
|
440
|
+
return undefined;
|
|
441
|
+
const requiredDefect = defectOf({ is: "list", item: STRING, minimum: 0, unique: true }, schema.required, `${path}.required`, depth + 1);
|
|
442
|
+
if (requiredDefect !== undefined)
|
|
443
|
+
return requiredDefect;
|
|
444
|
+
const undeclared = schema.required.findIndex((name) => !Object.hasOwn(properties, name));
|
|
445
|
+
return undeclared === -1
|
|
446
|
+
? undefined
|
|
447
|
+
: at(`${path}.required[${undeclared}]`, depth + 1, "names a key that is not one of the object's properties");
|
|
448
|
+
}
|
|
449
|
+
case "additionalProperties,type":
|
|
450
|
+
return (literalDefect(["object"], schema.type, `${path}.type`, depth + 1) ??
|
|
451
|
+
child("additionalProperties", schema.additionalProperties));
|
|
452
|
+
case "items,type":
|
|
453
|
+
return (literalDefect(["array"], schema.type, `${path}.type`, depth + 1) ??
|
|
454
|
+
child("items", schema.items));
|
|
455
|
+
case "items,maxItems,minItems,prefixItems,type": {
|
|
456
|
+
const defect = literalDefect(["array"], schema.type, `${path}.type`, depth + 1) ??
|
|
457
|
+
literalDefect([false], schema.items, `${path}.items`, depth + 1) ??
|
|
458
|
+
children("prefixItems", 1);
|
|
459
|
+
if (defect !== undefined)
|
|
460
|
+
return defect;
|
|
461
|
+
const length = schema.prefixItems.length;
|
|
462
|
+
for (const bound of ["minItems", "maxItems"]) {
|
|
463
|
+
if (schema[bound] !== length) {
|
|
464
|
+
return at(`${path}.${bound}`, depth + 1, `is ${JSON.stringify(schema[bound])}, not the tuple's length ${length}`);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return undefined;
|
|
468
|
+
}
|
|
469
|
+
case "anyOf":
|
|
470
|
+
return children("anyOf", 2);
|
|
471
|
+
case "oneOf":
|
|
472
|
+
return children("oneOf", 1);
|
|
473
|
+
default:
|
|
474
|
+
return at(path, depth, `is not a Json Shape schema: its keywords {${keys}} are not one of the forms Aventara emits`);
|
|
236
475
|
}
|
|
237
476
|
}
|
|
238
477
|
function literalDefect(values, value, path, depth) {
|
|
@@ -251,8 +490,10 @@ function listDefect(node, value, path, depth) {
|
|
|
251
490
|
if (kind !== undefined)
|
|
252
491
|
return kind;
|
|
253
492
|
const entries = value;
|
|
254
|
-
if (
|
|
255
|
-
return at(path, depth,
|
|
493
|
+
if (entries.length < node.minimum) {
|
|
494
|
+
return at(path, depth, entries.length === 0
|
|
495
|
+
? `is an empty JSON array, and needs at least ${node.minimum === 1 ? "one entry" : "two entries"}`
|
|
496
|
+
: "is a JSON array of one entry, and needs at least two");
|
|
256
497
|
}
|
|
257
498
|
const firstIndexOf = new Map();
|
|
258
499
|
for (let index = 0; index < entries.length; index += 1) {
|
|
@@ -1,11 +1,27 @@
|
|
|
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
|
+
/** One top-level member of a canonical Contract: its key and its value's canonical text. */
|
|
11
|
+
export type CanonicalContractMember = {
|
|
12
|
+
readonly key: string;
|
|
13
|
+
readonly text: string;
|
|
14
|
+
};
|
|
15
|
+
/** The Contract as canonical JSON (RFC 8785), one top-level member at a time, in order. */
|
|
16
|
+
export declare function canonicalizeContractMembers<C extends AvContract>(contract: C): readonly CanonicalContractMember[];
|
|
17
|
+
/** The canonical object text the members write: `{"key":text,…}`, in their order. */
|
|
18
|
+
export declare function joinCanonicalContractMembers(members: readonly CanonicalContractMember[]): string;
|
|
10
19
|
/** The Contract as canonical JSON (RFC 8785): the same bytes for the same Contract. */
|
|
11
|
-
export declare function canonicalizeContract<C extends
|
|
20
|
+
export declare function canonicalizeContract<C extends AvContract>(contract: C): string;
|
|
21
|
+
/** Records `text` as the canonical text `contract` was parsed from. */
|
|
22
|
+
export declare function recordCanonicalContractText(contract: AvContract, text: string): void;
|
|
23
|
+
/**
|
|
24
|
+
* The Contract's canonical text: the text it was identified from, when the compiler
|
|
25
|
+
* recorded one, else written now. Both are the same bytes for the same Contract.
|
|
26
|
+
*/
|
|
27
|
+
export declare function canonicalContractTextOf<C extends AvContract>(contract: C): string;
|
|
@@ -3,7 +3,7 @@ import { LIFECYCLE_TRAITS } from "./lifecycle-trait.js";
|
|
|
3
3
|
import { projectionScopeOf, writeScopeOf } from "./resource-operations.js";
|
|
4
4
|
const PAGINATION_ORDER = ["limit", "offset", "cursor"];
|
|
5
5
|
const REDUCER_ORDER = ["$count"];
|
|
6
|
-
export class
|
|
6
|
+
export class AvCanonicalJsonError extends TypeError {
|
|
7
7
|
}
|
|
8
8
|
function compareCodeUnits(left, right) {
|
|
9
9
|
if (left === right) {
|
|
@@ -83,10 +83,42 @@ function normalizeCapabilities(capabilities) {
|
|
|
83
83
|
}
|
|
84
84
|
return normalized;
|
|
85
85
|
}
|
|
86
|
+
function normalizeJsonSchema(schema) {
|
|
87
|
+
if (Array.isArray(schema)) {
|
|
88
|
+
return schema.map(normalizeJsonSchema);
|
|
89
|
+
}
|
|
90
|
+
if (typeof schema !== "object" || schema === null) {
|
|
91
|
+
return schema;
|
|
92
|
+
}
|
|
93
|
+
const normalized = {};
|
|
94
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
95
|
+
if (key === "required" && Array.isArray(value)) {
|
|
96
|
+
normalized[key] = [...value].sort((left, right) => compareCodeUnits(String(left), String(right)));
|
|
97
|
+
}
|
|
98
|
+
else if (key === "properties" &&
|
|
99
|
+
typeof value === "object" &&
|
|
100
|
+
value !== null) {
|
|
101
|
+
normalized[key] = Object.fromEntries(Object.entries(value).map(([name, child]) => [
|
|
102
|
+
name,
|
|
103
|
+
normalizeJsonSchema(child),
|
|
104
|
+
]));
|
|
105
|
+
}
|
|
106
|
+
else if (key === "const" || key === "enum" || key === "checks") {
|
|
107
|
+
normalized[key] = value;
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
normalized[key] = normalizeJsonSchema(value);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return normalized;
|
|
114
|
+
}
|
|
86
115
|
function normalizeField(field) {
|
|
87
116
|
if (field.kind === "scalar") {
|
|
88
117
|
return {
|
|
89
118
|
...field,
|
|
119
|
+
...(field.json !== undefined
|
|
120
|
+
? { json: normalizeJsonSchema(field.json) }
|
|
121
|
+
: {}),
|
|
90
122
|
lifecycle: normalizeStringSet(field.lifecycle, LIFECYCLE_TRAITS),
|
|
91
123
|
capabilities: normalizeCapabilities(field.capabilities),
|
|
92
124
|
};
|
|
@@ -141,8 +173,46 @@ function normalizeResource(resource) {
|
|
|
141
173
|
fields,
|
|
142
174
|
identifiers,
|
|
143
175
|
operations: normalizeOperations(resource.operations),
|
|
176
|
+
...(resource.createRequirements === undefined
|
|
177
|
+
? {}
|
|
178
|
+
: {
|
|
179
|
+
createRequirements: normalizeCreateRequirements(resource.createRequirements),
|
|
180
|
+
}),
|
|
144
181
|
};
|
|
145
182
|
}
|
|
183
|
+
function isJsonObject(value) {
|
|
184
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
185
|
+
}
|
|
186
|
+
function normalizeCreateRequirements(requirements) {
|
|
187
|
+
if (!isJsonObject(requirements)) {
|
|
188
|
+
return requirements;
|
|
189
|
+
}
|
|
190
|
+
const normalized = { ...requirements };
|
|
191
|
+
if (Array.isArray(requirements.required)) {
|
|
192
|
+
normalized.required = normalizeStringSet(requirements.required);
|
|
193
|
+
}
|
|
194
|
+
if (Array.isArray(requirements.oneOf)) {
|
|
195
|
+
normalized.oneOf = requirements.oneOf
|
|
196
|
+
.map((group) => Array.isArray(group) && group.length > 0
|
|
197
|
+
? [group[0], ...normalizeStringSet(group.slice(1))]
|
|
198
|
+
: group)
|
|
199
|
+
.sort((left, right) => compareCodeUnits(canonicalizeJson(left), canonicalizeJson(right)));
|
|
200
|
+
}
|
|
201
|
+
if (isJsonObject(requirements.parentSupplies)) {
|
|
202
|
+
normalized.parentSupplies = Object.fromEntries(Object.entries(requirements.parentSupplies).map(([parent, byRelation]) => [
|
|
203
|
+
parent,
|
|
204
|
+
isJsonObject(byRelation)
|
|
205
|
+
? Object.fromEntries(Object.entries(byRelation).map(([relation, members]) => [
|
|
206
|
+
relation,
|
|
207
|
+
Array.isArray(members)
|
|
208
|
+
? normalizeStringSet(members)
|
|
209
|
+
: members,
|
|
210
|
+
]))
|
|
211
|
+
: byRelation,
|
|
212
|
+
]));
|
|
213
|
+
}
|
|
214
|
+
return normalized;
|
|
215
|
+
}
|
|
146
216
|
function normalizeContract(contract) {
|
|
147
217
|
const scalars = Object.fromEntries(Object.keys(contract.scalars)
|
|
148
218
|
.sort(compareCodeUnits)
|
|
@@ -169,17 +239,24 @@ function assertValidUnicode(value, path) {
|
|
|
169
239
|
if (code >= 0xd800 && code <= 0xdbff) {
|
|
170
240
|
const next = value.charCodeAt(index + 1);
|
|
171
241
|
if (!(next >= 0xdc00 && next <= 0xdfff)) {
|
|
172
|
-
throw new
|
|
242
|
+
throw new AvCanonicalJsonError(`invalid Unicode surrogate at ${path()}`);
|
|
173
243
|
}
|
|
174
244
|
index += 1;
|
|
175
245
|
continue;
|
|
176
246
|
}
|
|
177
247
|
if (code >= 0xdc00 && code <= 0xdfff) {
|
|
178
|
-
throw new
|
|
248
|
+
throw new AvCanonicalJsonError(`invalid Unicode surrogate at ${path()}`);
|
|
179
249
|
}
|
|
180
250
|
}
|
|
181
251
|
}
|
|
182
|
-
function
|
|
252
|
+
function pathOf(link) {
|
|
253
|
+
const segments = [];
|
|
254
|
+
for (let current = link; current !== undefined; current = current.parent) {
|
|
255
|
+
segments.push(current.segment);
|
|
256
|
+
}
|
|
257
|
+
return `$${segments.reverse().join("")}`;
|
|
258
|
+
}
|
|
259
|
+
function canonicalizePrimitive(value, link) {
|
|
183
260
|
if (value === null) {
|
|
184
261
|
return "null";
|
|
185
262
|
}
|
|
@@ -188,47 +265,115 @@ function canonicalizeValue(value, path) {
|
|
|
188
265
|
return value ? "true" : "false";
|
|
189
266
|
case "number": {
|
|
190
267
|
if (!Number.isFinite(value)) {
|
|
191
|
-
throw new
|
|
268
|
+
throw new AvCanonicalJsonError(`non-finite number at ${pathOf(link)}`);
|
|
192
269
|
}
|
|
193
270
|
const serialized = JSON.stringify(value);
|
|
194
271
|
if (serialized === undefined) {
|
|
195
|
-
throw new
|
|
272
|
+
throw new AvCanonicalJsonError(`number at ${pathOf(link)} is not JSON serializable`);
|
|
196
273
|
}
|
|
197
274
|
return serialized;
|
|
198
275
|
}
|
|
199
276
|
case "string":
|
|
200
|
-
assertValidUnicode(value,
|
|
277
|
+
assertValidUnicode(value, () => pathOf(link));
|
|
201
278
|
return JSON.stringify(value);
|
|
202
279
|
case "undefined":
|
|
203
|
-
throw new
|
|
280
|
+
throw new AvCanonicalJsonError(`undefined value at ${pathOf(link)}`);
|
|
204
281
|
case "bigint":
|
|
205
282
|
case "function":
|
|
206
283
|
case "symbol":
|
|
207
|
-
throw new
|
|
208
|
-
|
|
209
|
-
|
|
284
|
+
throw new AvCanonicalJsonError(`${typeof value} value at ${pathOf(link)} is not JSON serializable`);
|
|
285
|
+
default:
|
|
286
|
+
return undefined;
|
|
210
287
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
288
|
+
}
|
|
289
|
+
function canonicalizeValue(value, link = undefined) {
|
|
290
|
+
const output = [];
|
|
291
|
+
const stack = [{ kind: "value", value, link }];
|
|
292
|
+
while (stack.length > 0) {
|
|
293
|
+
const task = stack.pop();
|
|
294
|
+
if (task.kind === "text") {
|
|
295
|
+
output.push(task.text);
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
if (task.kind === "key") {
|
|
299
|
+
assertValidUnicode(task.key, () => `${pathOf(task.link)} key`);
|
|
300
|
+
output.push(`${JSON.stringify(task.key)}:`);
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
const { value: current, link } = task;
|
|
304
|
+
const primitive = canonicalizePrimitive(current, link);
|
|
305
|
+
if (primitive !== undefined) {
|
|
306
|
+
output.push(primitive);
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
if (Array.isArray(current)) {
|
|
310
|
+
output.push("[");
|
|
311
|
+
stack.push({ kind: "text", text: "]" });
|
|
312
|
+
for (let index = current.length - 1; index >= 0; index -= 1) {
|
|
313
|
+
if (index in current) {
|
|
314
|
+
stack.push({
|
|
315
|
+
kind: "value",
|
|
316
|
+
value: current[index],
|
|
317
|
+
link: { parent: link, segment: `[${index}]` },
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
if (index > 0) {
|
|
321
|
+
stack.push({ kind: "text", text: "," });
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
const prototype = Object.getPrototypeOf(current);
|
|
327
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
328
|
+
throw new AvCanonicalJsonError(`non-plain object at ${pathOf(link)} is not canonical JSON`);
|
|
329
|
+
}
|
|
330
|
+
const record = current;
|
|
331
|
+
const keys = Object.keys(record).sort(compareCodeUnits);
|
|
332
|
+
output.push("{");
|
|
333
|
+
stack.push({ kind: "text", text: "}" });
|
|
334
|
+
for (let index = keys.length - 1; index >= 0; index -= 1) {
|
|
335
|
+
const key = keys[index];
|
|
336
|
+
stack.push({
|
|
337
|
+
kind: "value",
|
|
338
|
+
value: record[key],
|
|
339
|
+
link: { parent: link, segment: `.${key}` },
|
|
340
|
+
});
|
|
341
|
+
stack.push({ kind: "key", key, link });
|
|
342
|
+
if (index > 0) {
|
|
343
|
+
stack.push({ kind: "text", text: "," });
|
|
344
|
+
}
|
|
345
|
+
}
|
|
219
346
|
}
|
|
220
|
-
|
|
221
|
-
|
|
347
|
+
return output.join("");
|
|
348
|
+
}
|
|
349
|
+
export function canonicalizeJson(value) {
|
|
350
|
+
return canonicalizeValue(value);
|
|
351
|
+
}
|
|
352
|
+
export function canonicalizeContractMembers(contract) {
|
|
353
|
+
const record = normalizeContract(contract);
|
|
354
|
+
return Object.keys(record)
|
|
222
355
|
.sort(compareCodeUnits)
|
|
223
356
|
.map((key) => {
|
|
224
|
-
assertValidUnicode(key,
|
|
225
|
-
return
|
|
357
|
+
assertValidUnicode(key, () => "$ key");
|
|
358
|
+
return {
|
|
359
|
+
key,
|
|
360
|
+
text: canonicalizeValue(record[key], {
|
|
361
|
+
parent: undefined,
|
|
362
|
+
segment: `.${key}`,
|
|
363
|
+
}),
|
|
364
|
+
};
|
|
226
365
|
});
|
|
227
|
-
return `{${entries.join(",")}}`;
|
|
228
366
|
}
|
|
229
|
-
export function
|
|
230
|
-
return
|
|
367
|
+
export function joinCanonicalContractMembers(members) {
|
|
368
|
+
return `{${members.map(({ key, text }) => `${JSON.stringify(key)}:${text}`).join(",")}}`;
|
|
231
369
|
}
|
|
232
370
|
export function canonicalizeContract(contract) {
|
|
233
|
-
return
|
|
371
|
+
return joinCanonicalContractMembers(canonicalizeContractMembers(contract));
|
|
372
|
+
}
|
|
373
|
+
const canonicalTexts = new WeakMap();
|
|
374
|
+
export function recordCanonicalContractText(contract, text) {
|
|
375
|
+
canonicalTexts.set(contract, text);
|
|
376
|
+
}
|
|
377
|
+
export function canonicalContractTextOf(contract) {
|
|
378
|
+
return canonicalTexts.get(contract) ?? canonicalizeContract(contract);
|
|
234
379
|
}
|
|
@@ -1,11 +1,16 @@
|
|
|
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:
|
|
7
|
+
export declare function createClientContractHashInput(contract: AvContractClient | AvContractClientHashInput): AvContractClientHashInput;
|
|
8
8
|
/** Computes a ClientContract's `sha256:` hash, ignoring `protocol.hash` itself. */
|
|
9
|
-
export declare function computeClientContractHash(contract:
|
|
9
|
+
export declare function computeClientContractHash(contract: AvContractClient | AvContractClientHashInput): Promise<AvContractHash>;
|
|
10
|
+
/**
|
|
11
|
+
* The `sha256:` hash of a hash input's canonical text, already written: what
|
|
12
|
+
* {@link computeClientContractHash} answers for the input that text is of.
|
|
13
|
+
*/
|
|
14
|
+
export declare function hashCanonicalContractText(canonical: string): Promise<AvContractHash>;
|
|
10
15
|
/** Whether a ClientContract's `protocol.hash` matches its content. */
|
|
11
|
-
export declare function verifyClientContractHash(contract:
|
|
16
|
+
export declare function verifyClientContractHash(contract: AvContractClient): Promise<boolean>;
|
|
@@ -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)}`;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** The request limits a Contract enforces. */
|
|
2
|
-
export type
|
|
2
|
+
export type AvContractLimits = {
|
|
3
3
|
/** The largest request body accepted, in bytes. */
|
|
4
4
|
readonly maxRequestBytes: number;
|
|
5
5
|
/** How deeply arguments may nest. */
|
|
@@ -10,6 +10,8 @@ export type ContractLimits = {
|
|
|
10
10
|
readonly maxTransactionOperations: number;
|
|
11
11
|
/** The most boolean and filter nodes one request may hold. */
|
|
12
12
|
readonly maxBooleanNodes: number;
|
|
13
|
+
/** How deeply a `json` field's value may nest: its arrays and objects, counted from the value. */
|
|
14
|
+
readonly maxJsonDepth: number;
|
|
13
15
|
};
|
|
14
16
|
/** The limits in force when the configuration sets none. */
|
|
15
17
|
export declare const DEFAULT_CONTRACT_LIMITS: {
|
|
@@ -18,4 +20,5 @@ export declare const DEFAULT_CONTRACT_LIMITS: {
|
|
|
18
20
|
readonly maxListLimit: 250;
|
|
19
21
|
readonly maxTransactionOperations: 20;
|
|
20
22
|
readonly maxBooleanNodes: 50;
|
|
23
|
+
readonly maxJsonDepth: 64;
|
|
21
24
|
};
|