@aventara/core 0.1.0-pilot.1 → 0.1.0-pilot.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/adapter-field.d.ts +4 -4
- package/dist/adapters/adapter-resource.d.ts +10 -12
- package/dist/adapters/adapter.interface.d.ts +20 -10
- package/dist/compiler/contract-compiler-diagnostic.d.ts +3 -4
- package/dist/compiler/contract.compiler.d.ts +5 -4
- package/dist/compiler/contract.compiler.js +0 -132
- package/dist/config/config.resolver.d.ts +2 -2
- package/dist/config/config.resolver.js +0 -5
- package/dist/config/config.validator.d.ts +3 -3
- package/dist/config/config.validator.js +0 -8
- package/dist/config/define-fields.d.ts +2 -7
- package/dist/config/define-fields.js +0 -15
- package/dist/config/effective-fields.d.ts +4 -8
- package/dist/config/entrypoint.normalizer.d.ts +7 -7
- package/dist/config/entrypoint.normalizer.js +0 -22
- package/dist/config/framework-behaviors-config.d.ts +6 -6
- package/dist/config/framework-config.d.ts +3 -5
- package/dist/config/framework-restrictions-config.d.ts +19 -29
- package/dist/config/pipeline-config.d.ts +8 -18
- package/dist/contracts/capability.d.ts +18 -21
- package/dist/contracts/capability.js +0 -75
- package/dist/contracts/client-contract-structure.validator.d.ts +15 -22
- package/dist/contracts/client-contract-structure.validator.js +0 -27
- package/dist/contracts/contract-canonicalizer.js +0 -96
- package/dist/contracts/contract-hash.js +0 -2
- package/dist/contracts/contract-limits.js +0 -1
- package/dist/contracts/contract-lookup.js +0 -10
- package/dist/contracts/contract-protocol.js +0 -10
- package/dist/contracts/field.d.ts +23 -26
- package/dist/contracts/field.js +0 -107
- package/dist/contracts/lifecycle-trait.js +0 -5
- package/dist/contracts/resource-operations.d.ts +17 -18
- package/dist/contracts/resource-operations.js +0 -63
- package/dist/contracts/scalar-value-type.d.ts +12 -22
- package/dist/contracts/scalar.js +0 -1
- package/dist/contracts/wire-scalar-format.d.ts +20 -20
- package/dist/contracts/wire-scalar-format.js +0 -26
- package/dist/framework/contract-view.d.ts +7 -8
- package/dist/framework/framework-construction.error.js +0 -1
- package/dist/framework/framework.d.ts +26 -27
- package/dist/framework/framework.factory.js +0 -49
- package/dist/framework/operation-facade.js +0 -17
- package/dist/operations/operation-arguments.d.ts +36 -46
- package/dist/operations/operation-boolean-nodes.js +0 -16
- package/dist/operations/operation-call.d.ts +4 -9
- package/dist/operations/operation-identity.js +0 -1
- package/dist/operations/operation-nesting.js +0 -5
- package/dist/operations/operation-result.d.ts +4 -4
- package/dist/operations/operation-validator-core.d.ts +2 -2
- package/dist/operations/operation-validator-core.js +6 -71
- package/dist/operations/operation-validator.d.ts +4 -4
- package/dist/operations/operation-validator.js +0 -12
- package/dist/operations/projection-scope.resolver.d.ts +8 -10
- package/dist/operations/projection-scope.resolver.js +0 -35
- package/dist/operations/validation-code.d.ts +11 -18
- package/dist/operations/validation-code.js +0 -24
- package/dist/operations/write-scope.resolver.d.ts +4 -7
- package/dist/operations/write-scope.resolver.js +0 -37
- package/dist/protocol/absent-route.responder.d.ts +33 -48
- package/dist/protocol/absent-route.responder.js +0 -55
- package/dist/protocol/av-protocol.d.ts +6 -6
- package/dist/protocol/av-protocol.js +0 -10
- package/dist/protocol/contract-document.responder.d.ts +15 -18
- package/dist/protocol/contract-document.responder.js +0 -33
- package/dist/protocol/http-request.decoder.d.ts +55 -61
- package/dist/protocol/http-request.decoder.js +0 -62
- package/dist/protocol/http-request.reader.d.ts +12 -13
- package/dist/protocol/http-request.reader.js +0 -45
- package/dist/protocol/http-response.encoder.d.ts +17 -18
- package/dist/protocol/http-response.encoder.js +0 -59
- package/dist/protocol/http-status.table.d.ts +7 -11
- package/dist/protocol/http-status.table.js +1 -15
- package/dist/protocol/index.d.ts +5 -5
- package/dist/protocol/index.js +0 -9
- package/dist/protocol/operation-response.guard.d.ts +12 -19
- package/dist/protocol/operation-response.guard.js +0 -29
- package/dist/protocol/protocol-failure.d.ts +21 -26
- package/dist/protocol/protocol-failure.js +0 -12
- package/dist/protocol/protocol-headers.d.ts +25 -30
- package/dist/protocol/protocol-headers.js +0 -66
- package/dist/protocol/protocol-surface.d.ts +25 -29
- package/dist/protocol/protocol-surface.js +0 -19
- package/dist/protocol/protocol.binder.d.ts +58 -71
- package/dist/protocol/protocol.binder.js +0 -8
- package/dist/protocol/protocol.handler.d.ts +28 -32
- package/dist/protocol/protocol.handler.js +0 -43
- package/dist/runtime/computed-read-runtime.js +0 -29
- package/dist/runtime/create-requirement.d.ts +65 -0
- package/dist/runtime/create-requirement.js +141 -0
- package/dist/runtime/decimal.d.ts +2 -2
- package/dist/runtime/decimal.js +0 -8
- package/dist/runtime/diagnostics-reporter.d.ts +6 -6
- package/dist/runtime/diagnostics-reporter.js +0 -35
- package/dist/runtime/error-boundary.d.ts +3 -3
- package/dist/runtime/error-boundary.js +2 -167
- package/dist/runtime/execution-options.d.ts +4 -4
- package/dist/runtime/field-behavior.d.ts +6 -9
- package/dist/runtime/framework-error.js +0 -4
- package/dist/runtime/mutation-planner.d.ts +5 -6
- package/dist/runtime/mutation-planner.js +0 -23
- package/dist/runtime/operation-response.d.ts +1 -1
- package/dist/runtime/operation-response.js +1 -17
- package/dist/runtime/operation-runtime.d.ts +24 -22
- package/dist/runtime/operation-runtime.js +18 -52
- package/dist/runtime/outcome-mapper.js +0 -22
- package/dist/runtime/output-validator.js +0 -1
- package/dist/runtime/pipe-output.validator.d.ts +48 -0
- package/dist/runtime/pipe-output.validator.js +79 -0
- package/dist/runtime/pipeline.d.ts +20 -26
- package/dist/runtime/referential-action.d.ts +14 -19
- package/dist/runtime/referential-action.js +0 -23
- package/dist/runtime/request-id.js +0 -21
- package/dist/runtime/scalar-runtime.d.ts +14 -16
- package/dist/runtime/scalar-runtime.js +12 -57
- package/dist/transactions/deferred-operation.d.ts +9 -14
- package/dist/transactions/operation-arguments-encoder.d.ts +0 -1
- package/dist/transactions/operation-arguments-encoder.js +0 -14
- package/dist/transactions/operation-fingerprint.js +0 -1
- package/dist/transactions/transaction-envelope.js +0 -14
- package/dist/transactions/transaction-integrity-error.d.ts +10 -11
- package/dist/transactions/transaction-integrity-error.js +0 -5
- package/dist/transactions/transaction-plan-assembler.d.ts +0 -1
- package/dist/transactions/transaction-plan-assembler.js +0 -35
- package/dist/transactions/transaction-plan-validator.d.ts +5 -5
- package/dist/transactions/transaction-plan-validator.js +0 -63
- package/dist/transactions/transaction-reference-resolver.d.ts +12 -13
- package/dist/transactions/transaction-reference-resolver.js +0 -16
- package/dist/transactions/transaction-reference-validator.js +0 -8
- package/dist/transactions/transaction-reference.d.ts +19 -43
- package/dist/transactions/transaction-reference.js +0 -11
- package/dist/transactions/transaction-request.d.ts +4 -5
- package/dist/transactions/transaction-response.d.ts +1 -1
- package/dist/transactions/transaction-runtime.d.ts +2 -3
- package/dist/transactions/transaction-runtime.js +5 -44
- package/dist/transactions/transaction-slot-path-error.d.ts +10 -11
- package/dist/transactions/transaction-slot-path-error.js +0 -7
- package/dist/transactions/transaction-slot-writer.d.ts +12 -12
- package/dist/transactions/transaction-slot-writer.js +0 -34
- package/package.json +2 -2
|
@@ -13,11 +13,9 @@ export type FrameworkLimitsConfig = Partial<ContractLimits>;
|
|
|
13
13
|
/**
|
|
14
14
|
* Configuration available at root and at application/client scopes.
|
|
15
15
|
*
|
|
16
|
-
* `Cfg` is the authored configuration literal this scope belongs to.
|
|
17
|
-
* `
|
|
18
|
-
*
|
|
19
|
-
* `AnyFrameworkConfig` the effective key set degenerates to `string`, so an
|
|
20
|
-
* unannotated `FrameworkScopeConfig<M>` behaves exactly as it did before.
|
|
16
|
+
* `Cfg` is the authored configuration literal this scope belongs to. Under the
|
|
17
|
+
* default `AnyFrameworkConfig` the effective key set degenerates to `string`, so
|
|
18
|
+
* an unannotated `FrameworkScopeConfig<M>` behaves exactly as it did before.
|
|
21
19
|
*/
|
|
22
20
|
export type FrameworkScopeConfig<M extends AdapterModel = AdapterModel, Cfg = AnyFrameworkConfig> = {
|
|
23
21
|
readonly fields?: FrameworkFieldsConfig<M>;
|
|
@@ -7,27 +7,21 @@ import type { FrameworkResourceKey } from "./framework-fields-config.js";
|
|
|
7
7
|
export type FrameworkCapabilitySetRestriction<T extends string> = boolean | readonly T[];
|
|
8
8
|
/**
|
|
9
9
|
* A restriction on a scope-qualified projection member: `select` and `include` on
|
|
10
|
-
* a relation, where the capability is a POSITION list and carries no tokens at
|
|
10
|
+
* a relation, where the capability is a POSITION list and carries no tokens at
|
|
11
|
+
* all.
|
|
11
12
|
*
|
|
12
13
|
* Three operations, and they are three because narrowing a qualified capability is
|
|
13
14
|
* not one thing:
|
|
14
15
|
*
|
|
15
16
|
* - `false` **denies** the capability — the member leaves the projection.
|
|
16
17
|
* - a scope list **removes positions** — the member survives, reachable in fewer
|
|
17
|
-
* argument positions.
|
|
18
|
-
*
|
|
19
|
-
* is `COMPILER_UNAVAILABLE_CAPABILITY`, a compiler diagnostic rather than a type
|
|
20
|
-
* error, because a Contract is compiled from configuration that may be assembled
|
|
21
|
-
* at runtime.
|
|
22
|
-
* - `true` **restores** the baseline member, qualifier included. Restoring a
|
|
23
|
-
* qualified member is not widening it — the same distinction S2b drew on the
|
|
24
|
-
* operations axis, where preserving a descriptor is narrowing-safe and replacing
|
|
25
|
-
* one with the unqualified arm is not.
|
|
18
|
+
* argument positions.
|
|
19
|
+
* - `true` **restores** the baseline member, qualifier included.
|
|
26
20
|
*
|
|
27
21
|
* The **empty** list is the fourth operation written as a degenerate third: a
|
|
28
22
|
* capability narrowed to no position is a capability that is not available, and
|
|
29
23
|
* `ProjectionScopes` cannot represent an empty list, so the compiler denies the
|
|
30
|
-
* member rather than emitting a second encoding of "absent"
|
|
24
|
+
* member rather than emitting a second encoding of "absent".
|
|
31
25
|
*/
|
|
32
26
|
export type FrameworkProjectionScopeRestriction = boolean | readonly ProjectionScope[];
|
|
33
27
|
/**
|
|
@@ -128,34 +122,30 @@ type AdapterFieldRestrictionsFor<TField> = TField extends {
|
|
|
128
122
|
readonly kind: "relation";
|
|
129
123
|
} ? FrameworkRelationFieldRestrictions : FrameworkFieldRestrictions;
|
|
130
124
|
/**
|
|
131
|
-
* The restriction shape one effective field name admits. Three cases, in
|
|
132
|
-
* order.
|
|
125
|
+
* The restriction shape one effective field name admits. Three cases, in order.
|
|
133
126
|
*
|
|
134
|
-
* `string extends F` is the wide default. Under `AnyFrameworkConfig`,
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
127
|
+
* `string extends F` is the wide default. Under `AnyFrameworkConfig`, {@link
|
|
128
|
+
* EffectiveFieldKey} degenerates to `string`, the mapped type below collapses to a
|
|
129
|
+
* single index signature and no field identity survives to discriminate on.
|
|
130
|
+
* Today's wide acceptance is preserved there deliberately: narrowing below the
|
|
131
|
+
* resource key is opt-in, never imposed.
|
|
139
132
|
*
|
|
140
133
|
* An adapter-discovered name is discriminated by the model's own `kind`.
|
|
141
134
|
*
|
|
142
|
-
* Anything else is a name the configuration's own `fields` block added. Per
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
* undiscriminated {@link FrameworkFieldRestrictions}: falling back to that
|
|
147
|
-
* union would reopen F-602 for exactly the fields `Cfg` was threaded here to
|
|
148
|
-
* make nameable.
|
|
135
|
+
* Anything else is a name the configuration's own `fields` block added. Per the
|
|
136
|
+
* target type graph an added field is an added *scalar* field — there is no
|
|
137
|
+
* configuration surface that authors a relation — so the answer is {@link
|
|
138
|
+
* FrameworkScalarFieldRestrictions}.
|
|
149
139
|
*/
|
|
150
140
|
type FieldRestrictionsFor<M extends AdapterModel, R extends FrameworkResourceKey<M>, F extends string> = string extends F ? FrameworkFieldRestrictions : F extends AdapterFieldKey<M, R> ? AdapterFieldRestrictionsFor<M["resources"][R]["fields"][F]> : FrameworkScalarFieldRestrictions;
|
|
151
141
|
/**
|
|
152
142
|
* Projection policy for one Resource's fields, keyed on the single effective
|
|
153
143
|
* field-key answer.
|
|
154
144
|
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
145
|
+
* This type used to intersect the mapped half with `Readonly<Record<string,
|
|
146
|
+
* FrameworkFieldRestrictions>>`, whose index signature absorbed every field name
|
|
147
|
+
* the model does not declare. The catch-all is gone: a name must be an adapter
|
|
148
|
+
* field or one `Cfg` adds.
|
|
159
149
|
*/
|
|
160
150
|
export type FrameworkResourceFieldRestrictions<M extends AdapterModel, Cfg, R extends FrameworkResourceKey<M>> = Readonly<Partial<{
|
|
161
151
|
[F in EffectiveFieldKey<M, Cfg, R>]: FieldRestrictionsFor<M, R, F>;
|
|
@@ -3,9 +3,7 @@ import type { AfterHook, BeforeHook, Filter, Guard, Interceptor, Pipe } from "..
|
|
|
3
3
|
/**
|
|
4
4
|
* Pipeline registrations for one configuration scope.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* is handed is field-keyed, so the authored configuration literal has nothing
|
|
8
|
-
* to narrow here. See {@link PipelineContext}.
|
|
6
|
+
* See {@link PipelineContext}.
|
|
9
7
|
*/
|
|
10
8
|
export type PipelineConfig<M extends AdapterModel = AdapterModel> = {
|
|
11
9
|
readonly guards?: readonly Guard<M>[];
|
|
@@ -20,22 +18,14 @@ export type PipelineConfig<M extends AdapterModel = AdapterModel> = {
|
|
|
20
18
|
/**
|
|
21
19
|
* Fully normalized pipeline registration lists after scope composition.
|
|
22
20
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* a `string` off the wire, so it can never recover which literal it is
|
|
27
|
-
* holding — the same reason `ResolvedFrameworkScopeConfig.behaviors` holds
|
|
28
|
-
* the erased `FrameworkBehaviorsConfig`, and the same boundary ADR 0009 puts
|
|
29
|
-
* contract compilation behind. Parameterizing this type would let `M` flow
|
|
30
|
-
* past `resolveFrameworkConfig` into a runtime that cannot honour it, and
|
|
31
|
-
* would force a cast at the context factory to manufacture a literal the
|
|
32
|
-
* request does not carry.
|
|
21
|
+
* Parameterizing this type would let `M` flow past `resolveFrameworkConfig` into a
|
|
22
|
+
* runtime that cannot honour it, and would force a cast at the context factory to
|
|
23
|
+
* manufacture a literal the request does not carry.
|
|
33
24
|
*
|
|
34
|
-
* Unlike `behaviors`, erasing here costs nothing at the type level: `M`
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* cast at all.
|
|
25
|
+
* Unlike `behaviors`, erasing here costs nothing at the type level: `M` occurs in
|
|
26
|
+
* these callbacks *only* in parameter position, never as a mapped-type key, so
|
|
27
|
+
* `PipelineConfig<M>` is accepted where the erased form is required and
|
|
28
|
+
* `resolvePipelines` composes the authored lists with no cast at all.
|
|
39
29
|
*/
|
|
40
30
|
export type ResolvedPipelineConfig = {
|
|
41
31
|
readonly guards: readonly Guard[];
|
|
@@ -7,24 +7,22 @@ export type RelationReducer = "$count";
|
|
|
7
7
|
/**
|
|
8
8
|
* Where a projected field may be asked for: the kind of argument POSITION a
|
|
9
9
|
* projection sits in, not the operation that carries it. A variant states the
|
|
10
|
-
* position its projection arguments sit in (`OperationScopes`); a field
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* position its projection arguments sit in (`OperationScopes`); a field states the
|
|
11
|
+
* positions it is projectable in. The axis is closed at two because two is what
|
|
12
|
+
* both pilot engines answer — a third position would have to be measured, not
|
|
13
|
+
* supposed.
|
|
14
14
|
*
|
|
15
15
|
* **A scope qualifies the TOP LEVEL of a projection only. It does not narrow
|
|
16
16
|
* recursively.** Measured at runtime on both pilot engines, 2026-10-03, not
|
|
17
|
-
* inferred from types: a bulk-returning write accepted
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* in.
|
|
17
|
+
* inferred from types: a bulk-returning write accepted `include: { <owned to-one>:
|
|
18
|
+
* { include: { <to-many>: true } } }` and populated the rows (the nested to-many
|
|
19
|
+
* returned its 3 rows and a full reducer object), while the SAME to-many asked for
|
|
20
|
+
* at depth 1 was refused outright. The reason is visible in the generated types: a
|
|
21
|
+
* bulk projection types an admitted relation as the *unrestricted* projection
|
|
22
|
+
* argument, so the full surface returns one level in.
|
|
24
23
|
*
|
|
25
24
|
* A reader who assumes a scope narrows recursively will write a rule that refuses
|
|
26
|
-
* what the ORM accepts.
|
|
27
|
-
* under-advertising is the same defect as over-advertising with the sign flipped.
|
|
25
|
+
* what the ORM accepts.
|
|
28
26
|
*
|
|
29
27
|
* # Why the axis is a value and the union is derived from it
|
|
30
28
|
*
|
|
@@ -32,11 +30,10 @@ export type RelationReducer = "$count";
|
|
|
32
30
|
* `TRANSACTION_CAPABILITIES` are, because one reader needs its **cardinality** at
|
|
33
31
|
* runtime and not merely its member names: a scope list naming every member of the
|
|
34
32
|
* axis says exactly what the unqualified arm says, so the canonicalizer collapses
|
|
35
|
-
* it
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* tell — so the derivation is load-bearing, not tidiness.
|
|
33
|
+
* it. That reader asks "does this list name every member of the axis?" against
|
|
34
|
+
* this array. A hardcoded two would answer *yes* for a two-of-three list the day a
|
|
35
|
+
* third position is measured, which widens every Contract carrying it and nothing
|
|
36
|
+
* downstream can tell — so the derivation is load-bearing, not tidiness.
|
|
40
37
|
*
|
|
41
38
|
* Declaration order is registry order and the canonicalizer orders scope lists by
|
|
42
39
|
* it, the way it orders `pagination`.
|
|
@@ -47,8 +44,8 @@ export type ProjectionScope = (typeof PROJECTION_SCOPES)[number];
|
|
|
47
44
|
/**
|
|
48
45
|
* Where a field may be written: the kind of argument POSITION a write sits in, not
|
|
49
46
|
* the operation that carries it. The mirror of {@link ProjectionScope} on the
|
|
50
|
-
* mutation axis, and a separate union because the two axes are measured
|
|
51
|
-
*
|
|
47
|
+
* mutation axis, and a separate union because the two axes are measured separately
|
|
48
|
+
* — an adapter may narrow one without narrowing the other.
|
|
52
49
|
*
|
|
53
50
|
* **A scope qualifies the TOP LEVEL of a write argument only. It does not narrow
|
|
54
51
|
* recursively**, for the same reason {@link ProjectionScope} does not: past an
|
|
@@ -56,7 +53,7 @@ export type ProjectionScope = (typeof PROJECTION_SCOPES)[number];
|
|
|
56
53
|
* `"bulkWrite"` admits no nested write at all, so today the rule has no admitted
|
|
57
54
|
* top-level entry to apply beneath — it is stated because an adapter that admits
|
|
58
55
|
* one must not then be narrowed a second level down, which would refuse what the
|
|
59
|
-
* ORM accepts
|
|
56
|
+
* ORM accepts.
|
|
60
57
|
*
|
|
61
58
|
* A value for the same reason {@link PROJECTION_SCOPES} is one, and a **separate**
|
|
62
59
|
* array because the two axes are separate: a write scope list that names every
|
|
@@ -1,83 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Where a projected field may be asked for: the kind of argument POSITION a
|
|
3
|
-
* projection sits in, not the operation that carries it. A variant states the
|
|
4
|
-
* position its projection arguments sit in (`OperationScopes`); a field
|
|
5
|
-
* states the positions it is projectable in. The axis is closed at two because
|
|
6
|
-
* two is what both pilot engines answer — a third position would have to be
|
|
7
|
-
* measured, not supposed.
|
|
8
|
-
*
|
|
9
|
-
* **A scope qualifies the TOP LEVEL of a projection only. It does not narrow
|
|
10
|
-
* recursively.** Measured at runtime on both pilot engines, 2026-10-03, not
|
|
11
|
-
* inferred from types: a bulk-returning write accepted
|
|
12
|
-
* `include: { <owned to-one>: { include: { <to-many>: true } } }` and populated
|
|
13
|
-
* the rows (the nested to-many returned its 3 rows and a full reducer object),
|
|
14
|
-
* while the SAME to-many asked for at depth 1 was refused outright. The reason is
|
|
15
|
-
* visible in the generated types: a bulk projection types an admitted relation as
|
|
16
|
-
* the *unrestricted* projection argument, so the full surface returns one level
|
|
17
|
-
* in.
|
|
18
|
-
*
|
|
19
|
-
* A reader who assumes a scope narrows recursively will write a rule that refuses
|
|
20
|
-
* what the ORM accepts. That breaches C-855 in the other direction, and
|
|
21
|
-
* under-advertising is the same defect as over-advertising with the sign flipped.
|
|
22
|
-
*
|
|
23
|
-
* # Why the axis is a value and the union is derived from it
|
|
24
|
-
*
|
|
25
|
-
* The axis is written down exactly once, as `LIFECYCLE_TRAITS` and
|
|
26
|
-
* `TRANSACTION_CAPABILITIES` are, because one reader needs its **cardinality** at
|
|
27
|
-
* runtime and not merely its member names: a scope list naming every member of the
|
|
28
|
-
* axis says exactly what the unqualified arm says, so the canonicalizer collapses
|
|
29
|
-
* it (C-843, and §4's rejection of one fact with two hashes). That reader asks
|
|
30
|
-
* "does this list name every member of the axis?" against this array. A hardcoded
|
|
31
|
-
* two would answer *yes* for a two-of-three list the day a third position is
|
|
32
|
-
* measured, which widens every Contract carrying it and nothing downstream can
|
|
33
|
-
* tell — so the derivation is load-bearing, not tidiness.
|
|
34
|
-
*
|
|
35
|
-
* Declaration order is registry order and the canonicalizer orders scope lists by
|
|
36
|
-
* it, the way it orders `pagination`.
|
|
37
|
-
*/
|
|
38
1
|
export const PROJECTION_SCOPES = [
|
|
39
|
-
/**
|
|
40
|
-
* The unrestricted projection position: every row-returning read, and every
|
|
41
|
-
* row-returning write of one addressed record.
|
|
42
|
-
*/
|
|
43
2
|
"record",
|
|
44
|
-
/**
|
|
45
|
-
* The projection of the rows a BULK write returns, where the reachable set is
|
|
46
|
-
* narrower at the top level — on both pilot engines, only a relation the written
|
|
47
|
-
* row itself resolves. Named for the write being bulk and not for the row count:
|
|
48
|
-
* a multi-row READ projects in `"record"`.
|
|
49
|
-
*/
|
|
50
3
|
"bulkWrite",
|
|
51
4
|
];
|
|
52
|
-
/**
|
|
53
|
-
* Where a field may be written: the kind of argument POSITION a write sits in, not
|
|
54
|
-
* the operation that carries it. The mirror of {@link ProjectionScope} on the
|
|
55
|
-
* mutation axis, and a separate union because the two axes are measured
|
|
56
|
-
* separately — an adapter may narrow one without narrowing the other.
|
|
57
|
-
*
|
|
58
|
-
* **A scope qualifies the TOP LEVEL of a write argument only. It does not narrow
|
|
59
|
-
* recursively**, for the same reason {@link ProjectionScope} does not: past an
|
|
60
|
-
* admitted entry the unrestricted surface returns. On both pilot engines
|
|
61
|
-
* `"bulkWrite"` admits no nested write at all, so today the rule has no admitted
|
|
62
|
-
* top-level entry to apply beneath — it is stated because an adapter that admits
|
|
63
|
-
* one must not then be narrowed a second level down, which would refuse what the
|
|
64
|
-
* ORM accepts (C-855, sign flipped).
|
|
65
|
-
*
|
|
66
|
-
* A value for the same reason {@link PROJECTION_SCOPES} is one, and a **separate**
|
|
67
|
-
* array because the two axes are separate: a write scope list that names every
|
|
68
|
-
* write position is degenerate, a write scope list that happens to have as many
|
|
69
|
-
* members as the projection axis is not.
|
|
70
|
-
*/
|
|
71
5
|
export const WRITE_SCOPES = [
|
|
72
|
-
/**
|
|
73
|
-
* The unrestricted write position: a write argument that admits nested writes of
|
|
74
|
-
* related records. `"nested"` is this repository's established word for "through
|
|
75
|
-
* a relation" — see {@link RelationFilterCapability}.
|
|
76
|
-
*/
|
|
77
6
|
"nested",
|
|
78
|
-
/**
|
|
79
|
-
* The data argument of a BULK write: the record's own columns and its own
|
|
80
|
-
* reference fields only.
|
|
81
|
-
*/
|
|
82
7
|
"bulkWrite",
|
|
83
8
|
];
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import type { ClientContract } from "./contract.js";
|
|
2
2
|
/**
|
|
3
|
-
* §15.3's *"validate ClientContract structure"*: whether an untrusted value IS a
|
|
4
|
-
* `ClientContract` — every member present, of its JSON kind, inside its closed
|
|
5
|
-
* vocabulary, and nothing else — before anything reads it as one.
|
|
6
|
-
*
|
|
7
3
|
* # Why core owns it
|
|
8
4
|
*
|
|
9
5
|
* The canonicalizer reads Contract members without validating them (it owns
|
|
@@ -15,30 +11,27 @@ import type { ClientContract } from "./contract.js";
|
|
|
15
11
|
*
|
|
16
12
|
* # One source of truth: the schema IS the type
|
|
17
13
|
*
|
|
18
|
-
* {@link CLIENT_CONTRACT_STRUCTURE} is a passive description, and
|
|
19
|
-
*
|
|
20
|
-
*
|
|
14
|
+
* {@link CLIENT_CONTRACT_STRUCTURE} is a passive description, and {@link
|
|
15
|
+
* Structured} decodes it into a TypeScript type. That decoded type must be
|
|
16
|
+
* `ClientContract` exactly, in both directions:
|
|
21
17
|
*
|
|
22
|
-
* - **schema → type** is enforced here, at build time: the accepted arm hands
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* - **type → schema** is enforced by
|
|
26
|
-
* a schema that is stricter
|
|
27
|
-
*
|
|
18
|
+
* - **schema → type** is enforced here, at build time: the accepted arm hands back
|
|
19
|
+
* the decoded value as a `ClientContract` with no cast, so a schema that admits
|
|
20
|
+
* something the type does not fails to compile.
|
|
21
|
+
* - **type → schema** is enforced by
|
|
22
|
+
* `client-contract-structure.validator.test-d.ts`: a schema that is stricter
|
|
23
|
+
* than the type — a vocabulary token missed, an optional member written as
|
|
24
|
+
* required — fails the type test.
|
|
28
25
|
*
|
|
29
26
|
* So a member added to, removed from or re-typed in core's ClientContract types
|
|
30
27
|
* breaks the build or the type test until this schema says the same thing.
|
|
31
28
|
*
|
|
32
29
|
* # Where the line is
|
|
33
30
|
*
|
|
34
|
-
* The line is the TYPE, judged at the erased `ClientContract
|
|
35
|
-
*
|
|
31
|
+
* The line is the TYPE, judged at the erased `ClientContract`: what the type
|
|
32
|
+
* admits is accepted, what it rules out is refused. Two consequences are
|
|
36
33
|
* deliberate:
|
|
37
34
|
*
|
|
38
|
-
* - **Canonical-JSON encodability is not structure.** `number` admits `Infinity`
|
|
39
|
-
* and `string` admits a lone surrogate, so both pass here; canonicalization
|
|
40
|
-
* refuses them with `CanonicalJsonError` and its path, which is the existing
|
|
41
|
-
* §19.2 refusal (S0).
|
|
42
35
|
* - **References are not structure.** At the erased type a relation `target`, a
|
|
43
36
|
* field's `{ scalar }`/`{ enum }` and an identifier's field keys are `string`,
|
|
44
37
|
* so a dangling reference passes. Referential consistency is a separate,
|
|
@@ -48,9 +41,9 @@ import type { ClientContract } from "./contract.js";
|
|
|
48
41
|
* **an enum's values are distinct.** `readonly string[]` admits a repeat, but an
|
|
49
42
|
* enum with one names a member twice — the generated client's `as const` object
|
|
50
43
|
* would declare one key twice (TS1117) — so a repeat is refused at the second
|
|
51
|
-
* occurrence, naming the first. The list node carries it as `unique`, which
|
|
52
|
-
*
|
|
53
|
-
*
|
|
44
|
+
* occurrence, naming the first. The list node carries it as `unique`, which {@link
|
|
45
|
+
* Structured} ignores, so the decoded type is still `ClientContract` exactly.
|
|
46
|
+
* Every reader judges it here, once, rather than each re-checking it.
|
|
54
47
|
*
|
|
55
48
|
* The validator reads every member it judges and never throws on any input: a
|
|
56
49
|
* value that is not JSON at all is reported, not crashed on.
|
|
@@ -14,7 +14,6 @@ function list(item) {
|
|
|
14
14
|
function nonEmptyList(item) {
|
|
15
15
|
return { is: "list", item, nonEmpty: true, unique: false };
|
|
16
16
|
}
|
|
17
|
-
/** A list of primitives in which no entry repeats an earlier one. */
|
|
18
17
|
function uniqueList(item) {
|
|
19
18
|
return { is: "list", item, nonEmpty: false, unique: true };
|
|
20
19
|
}
|
|
@@ -27,12 +26,6 @@ function object(required, optional = {}) {
|
|
|
27
26
|
function union(...arms) {
|
|
28
27
|
return { is: "union", arms };
|
|
29
28
|
}
|
|
30
|
-
/*
|
|
31
|
-
* The closed vocabularies `capability.ts` states as types only. Each array is
|
|
32
|
-
* tied to its union by the type → schema check: a token missing here makes the
|
|
33
|
-
* schema stricter than `ClientContract`, which fails the type test; a token not
|
|
34
|
-
* in the union makes it looser, which fails the build.
|
|
35
|
-
*/
|
|
36
29
|
const SCALAR_FILTER_OPERATORS = [
|
|
37
30
|
"equals",
|
|
38
31
|
"not",
|
|
@@ -168,7 +161,6 @@ const RESOURCE = object({
|
|
|
168
161
|
upsert: object({}, { unique: OPERATION_AVAILABILITY }),
|
|
169
162
|
}),
|
|
170
163
|
});
|
|
171
|
-
/** The structure of a ClientContract. Decodes to exactly `ClientContract`. */
|
|
172
164
|
export const CLIENT_CONTRACT_STRUCTURE = object({
|
|
173
165
|
scalars: registry(object({ builtin: literal([true]) })),
|
|
174
166
|
enums: registry(object({ values: uniqueList(STRING) })),
|
|
@@ -186,14 +178,11 @@ export const CLIENT_CONTRACT_STRUCTURE = object({
|
|
|
186
178
|
hash: { is: "prefixed", prefix: "sha256:" },
|
|
187
179
|
}),
|
|
188
180
|
});
|
|
189
|
-
/** Judge whether `value` is a ClientContract. Never throws. */
|
|
190
181
|
export function validateClientContractStructure(value) {
|
|
191
182
|
const defect = defectOf(CLIENT_CONTRACT_STRUCTURE, value, "$", 0);
|
|
192
183
|
if (defect !== undefined) {
|
|
193
184
|
return { valid: false, path: defect.path, message: defect.message };
|
|
194
185
|
}
|
|
195
|
-
// No cast to ClientContract: the decoded schema type must be assignable to it,
|
|
196
|
-
// which is the schema → type half of the one-source-of-truth check.
|
|
197
186
|
const contract = value;
|
|
198
187
|
return { valid: true, contract };
|
|
199
188
|
}
|
|
@@ -265,7 +254,6 @@ function listDefect(node, value, path, depth) {
|
|
|
265
254
|
if (node.nonEmpty && entries.length === 0) {
|
|
266
255
|
return at(path, depth, "is an empty JSON array, and needs at least one entry");
|
|
267
256
|
}
|
|
268
|
-
/** Each value's first index; read only when the list is `unique`. */
|
|
269
257
|
const firstIndexOf = new Map();
|
|
270
258
|
for (let index = 0; index < entries.length; index += 1) {
|
|
271
259
|
const entryPath = `${path}[${index}]`;
|
|
@@ -283,13 +271,6 @@ function listDefect(node, value, path, depth) {
|
|
|
283
271
|
}
|
|
284
272
|
return undefined;
|
|
285
273
|
}
|
|
286
|
-
/**
|
|
287
|
-
* A closed member set: every required member present, every present member
|
|
288
|
-
* judged, and no member the type does not have. An unknown key is refused
|
|
289
|
-
* rather than ignored — a reader that silently skipped part of an exact
|
|
290
|
-
* Contract would be reading a different Contract, and keys outside
|
|
291
|
-
* `protocol.version` are outside the hash, so nothing after this would see them.
|
|
292
|
-
*/
|
|
293
274
|
function objectDefect(required, optional, value, path, depth) {
|
|
294
275
|
const kind = kindDefect(value, "object", path, depth);
|
|
295
276
|
if (kind !== undefined)
|
|
@@ -315,13 +296,6 @@ function objectDefect(required, optional, value, path, depth) {
|
|
|
315
296
|
? undefined
|
|
316
297
|
: at(`${path}.${unknown}`, depth, "is not a member ClientContract has here");
|
|
317
298
|
}
|
|
318
|
-
/**
|
|
319
|
-
* Accepted when any alternative accepts. An alternative whose JSON kind the value
|
|
320
|
-
* does not have does not apply; among those that do, the refusal is the defect of
|
|
321
|
-
* the one the value got furthest into, and where two got equally far no single
|
|
322
|
-
* one is the better guess, so it names the position and lists both. When none
|
|
323
|
-
* applies, the refusal names every kind that would have.
|
|
324
|
-
*/
|
|
325
299
|
function unionDefect(arms, value, path, depth) {
|
|
326
300
|
const defects = [];
|
|
327
301
|
for (const arm of arms) {
|
|
@@ -351,7 +325,6 @@ function kindDefect(value, expected, path, depth) {
|
|
|
351
325
|
? undefined
|
|
352
326
|
: mismatchAt(path, depth, describeValueKind(value), describeKind(expected));
|
|
353
327
|
}
|
|
354
|
-
/** Uses only `typeof` and `Array.isArray`, so it reads no property of the value. */
|
|
355
328
|
function jsonKindOf(value) {
|
|
356
329
|
if (value === null)
|
|
357
330
|
return "null";
|
|
@@ -3,12 +3,6 @@ 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
|
-
/**
|
|
7
|
-
* Framework-owned rejection of a value JCS cannot represent. It extends
|
|
8
|
-
* `TypeError` and keeps its inherited `name`, so existing `TypeError`
|
|
9
|
-
* consumers and error text are unchanged, while callers that must attribute
|
|
10
|
-
* a canonicalization failure to its input can recognize this class exactly.
|
|
11
|
-
*/
|
|
12
6
|
export class CanonicalJsonError extends TypeError {
|
|
13
7
|
}
|
|
14
8
|
function compareCodeUnits(left, right) {
|
|
@@ -34,25 +28,6 @@ function normalizeStringSet(values, order) {
|
|
|
34
28
|
return compareCodeUnits(left, right);
|
|
35
29
|
});
|
|
36
30
|
}
|
|
37
|
-
/**
|
|
38
|
-
* A scope list in canonical form: ordered as a set, or **`true`** where it names
|
|
39
|
-
* every member of its axis.
|
|
40
|
-
*
|
|
41
|
-
* The collapse is the half of C-843 the type system cannot carry. A capability's
|
|
42
|
-
* qualified arm and its unqualified arm are both live — an adapter that has not
|
|
43
|
-
* measured the axis says `true`, and a Contract that predates the measurement keeps
|
|
44
|
-
* its meaning — so a list naming every position says exactly what `true` says. Two
|
|
45
|
-
* encodings of one semantic fact hash two ways, which is the ground the architect
|
|
46
|
-
* rejected the flat-or-per-variant shape on, so canonical form picks one arm and it
|
|
47
|
-
* is the unqualified one.
|
|
48
|
-
*
|
|
49
|
-
* "Names every member" is asked of the **axis array**, never of a literal count.
|
|
50
|
-
* A hardcoded two would read a two-of-three list as unqualified the day a third
|
|
51
|
-
* position is measured, silently widening every Contract that carried it — and
|
|
52
|
-
* membership is tested as set EQUALITY rather than coverage, so a token that is not
|
|
53
|
-
* on the axis at all (possible: this reads Contract data, not a checked type)
|
|
54
|
-
* blocks the collapse instead of being absorbed by it.
|
|
55
|
-
*/
|
|
56
31
|
function normalizeScopes(scopes, axis) {
|
|
57
32
|
const normalized = normalizeStringSet(scopes, axis);
|
|
58
33
|
const named = new Set(normalized);
|
|
@@ -62,20 +37,6 @@ function normalizeScopes(scopes, axis) {
|
|
|
62
37
|
function isScopedArm(value) {
|
|
63
38
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
64
39
|
}
|
|
65
|
-
/**
|
|
66
|
-
* One scope-qualified capability arm in canonical form: its token list ordered as
|
|
67
|
-
* the set it is, and its scope list ordered — or collapsed, which replaces the arm
|
|
68
|
-
* with the bare token list, because that is this member's unqualified arm.
|
|
69
|
-
*
|
|
70
|
-
* The token list survives the collapse rather than being dropped: `directives: []`
|
|
71
|
-
* means *"writable with no directive"* against an absent member's *"not writable
|
|
72
|
-
* during this stage"*, two answers core already distinguishes, and the scope
|
|
73
|
-
* qualifier may not merge them.
|
|
74
|
-
*
|
|
75
|
-
* Anything that is not this shape is returned untouched. The canonicalizer reads
|
|
76
|
-
* Contract data and owns ordering, not validation — a member it does not recognize
|
|
77
|
-
* is not a member it may rewrite.
|
|
78
|
-
*/
|
|
79
40
|
function normalizeScopedArm(arm, tokenKey, tokenOrder, axis) {
|
|
80
41
|
const tokens = arm[tokenKey];
|
|
81
42
|
const scopes = arm.scopes;
|
|
@@ -90,8 +51,6 @@ function normalizeScopedArm(arm, tokenKey, tokenOrder, axis) {
|
|
|
90
51
|
}
|
|
91
52
|
function normalizeCapabilities(capabilities) {
|
|
92
53
|
const normalized = { ...capabilities };
|
|
93
|
-
// Projection members: `boolean` unqualified, a scope list qualified. The
|
|
94
|
-
// boolean arm is already canonical, and a scalar `select` is only ever boolean.
|
|
95
54
|
for (const key of ["select", "include"]) {
|
|
96
55
|
const value = normalized[key];
|
|
97
56
|
if (Array.isArray(value)) {
|
|
@@ -102,8 +61,6 @@ function normalizeCapabilities(capabilities) {
|
|
|
102
61
|
if (Array.isArray(filter)) {
|
|
103
62
|
normalized.filter = normalizeStringSet(filter);
|
|
104
63
|
}
|
|
105
|
-
// Mutation stages: a bare directive list unqualified, `{ directives, scopes }`
|
|
106
|
-
// qualified. Scalar capabilities only ever carry the bare list.
|
|
107
64
|
for (const key of ["create", "update"]) {
|
|
108
65
|
const value = normalized[key];
|
|
109
66
|
if (Array.isArray(value)) {
|
|
@@ -139,47 +96,11 @@ function normalizeField(field) {
|
|
|
139
96
|
capabilities: normalizeCapabilities(field.capabilities),
|
|
140
97
|
};
|
|
141
98
|
}
|
|
142
|
-
/**
|
|
143
|
-
* Every key an operation availability descriptor may state, each paired with the
|
|
144
|
-
* reader that answers what that key MEANS.
|
|
145
|
-
*
|
|
146
|
-
* The readers are the pair core already validates with, so this table is a list of
|
|
147
|
-
* the axes the canonicalizer understands rather than a second definition of them.
|
|
148
|
-
* A descriptor key absent from it is a key whose meaning this file does not know.
|
|
149
|
-
*/
|
|
150
99
|
const VARIANT_SCOPE_READERS = [
|
|
151
100
|
["projection", projectionScopeOf],
|
|
152
101
|
["write", writeScopeOf],
|
|
153
102
|
];
|
|
154
103
|
const VARIANT_SCOPE_KEYS = new Set(VARIANT_SCOPE_READERS.map(([key]) => key));
|
|
155
|
-
/**
|
|
156
|
-
* One operation availability leaf in canonical form: **`true`** where the descriptor
|
|
157
|
-
* states nothing beyond what `true` states, and otherwise the descriptor untouched.
|
|
158
|
-
*
|
|
159
|
-
* This is S4's collapse on the operations axis. `OperationAvailability` is
|
|
160
|
-
* `true | OperationVariantScopes`, both arms are live — an adapter that has not
|
|
161
|
-
* measured the axes says `true`, and a Contract that predates the measurement keeps
|
|
162
|
-
* its meaning — so a descriptor whose scopes are the ones `true` already means is a
|
|
163
|
-
* second encoding of one semantic fact, and two encodings hash two ways. That is the
|
|
164
|
-
* ground §4 rejected the flat-or-per-variant shape on, so canonical form picks one
|
|
165
|
-
* arm and it is the unqualified one.
|
|
166
|
-
*
|
|
167
|
-
* **Redundant is asked of the READERS, never of a scope literal or an axis
|
|
168
|
-
* cardinality.** `projectionScopeOf` and `writeScopeOf` are what a descriptor means
|
|
169
|
-
* to everyone who consults it, so a descriptor says nothing beyond `true` exactly
|
|
170
|
-
* when both answer for it what they answer for `true`. Deriving it this way is what
|
|
171
|
-
* makes the eager-collapse failure mode unreachable: a descriptor stating any
|
|
172
|
-
* narrower position — `projection: "bulkWrite"`, `write: "bulkWrite"`, a third
|
|
173
|
-
* position measured later, or a token that is not on its axis at all (possible: this
|
|
174
|
-
* reads Contract data, not a checked type) — moves a reader's answer and therefore
|
|
175
|
-
* blocks the collapse, with nothing here to keep in sync. A collapse keyed on the
|
|
176
|
-
* literal `"record"`/`"nested"` pair, or on "the descriptor fills every key, so it is
|
|
177
|
-
* fully specified", would widen Contracts and leave nothing downstream able to tell.
|
|
178
|
-
*
|
|
179
|
-
* A descriptor stating a key no reader covers is returned untouched for the reason
|
|
180
|
-
* {@link normalizeScopedArm} gives: the canonicalizer owns ordering, not validation,
|
|
181
|
-
* and a key it does not recognize is not a key it may decide is redundant.
|
|
182
|
-
*/
|
|
183
104
|
function normalizeOperationAvailability(availability) {
|
|
184
105
|
if (!isScopedArm(availability)) {
|
|
185
106
|
return availability;
|
|
@@ -192,17 +113,6 @@ function normalizeOperationAvailability(availability) {
|
|
|
192
113
|
const saysNothingBeyondTrue = VARIANT_SCOPE_READERS.every(([, scopeOf]) => scopeOf(availability) === scopeOf(true));
|
|
193
114
|
return saysNothingBeyondTrue ? true : availability;
|
|
194
115
|
}
|
|
195
|
-
/**
|
|
196
|
-
* A Resource's advertised operations in canonical form: every availability leaf
|
|
197
|
-
* canonicalized, and the tree otherwise as it was.
|
|
198
|
-
*
|
|
199
|
-
* Nothing here is sorted. The family and variant keys are object keys, which JCS
|
|
200
|
-
* orders at encoding time, and the descriptor's own two keys are object keys for the
|
|
201
|
-
* same reason — key order was never the defect, a redundant *encoding* was.
|
|
202
|
-
*
|
|
203
|
-
* A family that is not a plain object is returned untouched, which keeps this a
|
|
204
|
-
* canonicalizer and not a validator.
|
|
205
|
-
*/
|
|
206
116
|
function normalizeOperations(operations) {
|
|
207
117
|
if (!isScopedArm(operations)) {
|
|
208
118
|
return operations;
|
|
@@ -233,10 +143,6 @@ function normalizeResource(resource) {
|
|
|
233
143
|
operations: normalizeOperations(resource.operations),
|
|
234
144
|
};
|
|
235
145
|
}
|
|
236
|
-
/**
|
|
237
|
-
* Normalize semantic sets and registry order while preserving arrays whose
|
|
238
|
-
* order changes meaning, such as enum values and identifier field tuples.
|
|
239
|
-
*/
|
|
240
146
|
function normalizeContract(contract) {
|
|
241
147
|
const scalars = Object.fromEntries(Object.keys(contract.scalars)
|
|
242
148
|
.sort(compareCodeUnits)
|
|
@@ -320,11 +226,9 @@ function canonicalizeValue(value, path) {
|
|
|
320
226
|
});
|
|
321
227
|
return `{${entries.join(",")}}`;
|
|
322
228
|
}
|
|
323
|
-
/** RFC 8785/JCS-compatible canonical encoding for JSON-safe Aventara data. */
|
|
324
229
|
export function canonicalizeJson(value) {
|
|
325
230
|
return canonicalizeValue(value, "$");
|
|
326
231
|
}
|
|
327
|
-
/** Canonical semantic representation used for deterministic Contract output. */
|
|
328
232
|
export function canonicalizeContract(contract) {
|
|
329
233
|
return canonicalizeJson(normalizeContract(contract));
|
|
330
234
|
}
|
|
@@ -9,7 +9,6 @@ export function createClientContractHashInput(contract) {
|
|
|
9
9
|
function digestHex(digest) {
|
|
10
10
|
return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
11
11
|
}
|
|
12
|
-
/** Compute exact ClientContract identity with protocol.hash excluded. */
|
|
13
12
|
export async function computeClientContractHash(contract) {
|
|
14
13
|
const input = createClientContractHashInput(contract);
|
|
15
14
|
const canonical = canonicalizeContract(input);
|
|
@@ -17,7 +16,6 @@ export async function computeClientContractHash(contract) {
|
|
|
17
16
|
const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
|
|
18
17
|
return `sha256:${digestHex(digest)}`;
|
|
19
18
|
}
|
|
20
|
-
/** Recompute and compare the advertised ClientContract identity. */
|
|
21
19
|
export async function verifyClientContractHash(contract) {
|
|
22
20
|
return (await computeClientContractHash(contract)) === contract.protocol.hash;
|
|
23
21
|
}
|
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Own-property lookups over Contract registries.
|
|
3
|
-
*
|
|
4
|
-
* Registry keys arrive from untrusted requests. A plain-object registry
|
|
5
|
-
* resolves keys such as `__proto__`, `constructor` or `toString` up
|
|
6
|
-
* `Object.prototype`, so a bare `registry[key]` returns a function or
|
|
7
|
-
* `Object.prototype` itself and passes an `=== undefined` guard as if it
|
|
8
|
-
* were a Resource or Field. Every lookup that takes a caller-supplied key
|
|
9
|
-
* must go through here so an unknown key is unknown, whatever its name.
|
|
10
|
-
*/
|
|
11
1
|
export function lookupOwn(registry, key) {
|
|
12
2
|
return registry !== undefined && Object.hasOwn(registry, key)
|
|
13
3
|
? registry[key]
|