@aventara/core 0.1.0-pilot.0 → 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.
Files changed (140) hide show
  1. package/README.md +3 -3
  2. package/dist/adapters/adapter-field.d.ts +4 -4
  3. package/dist/adapters/adapter-resource.d.ts +10 -12
  4. package/dist/adapters/adapter.interface.d.ts +20 -10
  5. package/dist/compiler/contract-compiler-diagnostic.d.ts +3 -4
  6. package/dist/compiler/contract.compiler.d.ts +5 -4
  7. package/dist/compiler/contract.compiler.js +0 -132
  8. package/dist/config/config.resolver.d.ts +2 -2
  9. package/dist/config/config.resolver.js +0 -5
  10. package/dist/config/config.validator.d.ts +3 -3
  11. package/dist/config/config.validator.js +0 -8
  12. package/dist/config/define-fields.d.ts +2 -7
  13. package/dist/config/define-fields.js +0 -15
  14. package/dist/config/effective-fields.d.ts +4 -8
  15. package/dist/config/entrypoint.normalizer.d.ts +7 -7
  16. package/dist/config/entrypoint.normalizer.js +0 -22
  17. package/dist/config/framework-behaviors-config.d.ts +6 -6
  18. package/dist/config/framework-config.d.ts +3 -5
  19. package/dist/config/framework-restrictions-config.d.ts +19 -29
  20. package/dist/config/pipeline-config.d.ts +8 -18
  21. package/dist/contracts/capability.d.ts +18 -21
  22. package/dist/contracts/capability.js +0 -75
  23. package/dist/contracts/client-contract-structure.validator.d.ts +15 -22
  24. package/dist/contracts/client-contract-structure.validator.js +0 -27
  25. package/dist/contracts/contract-canonicalizer.js +0 -96
  26. package/dist/contracts/contract-hash.js +0 -2
  27. package/dist/contracts/contract-limits.js +0 -1
  28. package/dist/contracts/contract-lookup.js +0 -10
  29. package/dist/contracts/contract-protocol.js +0 -10
  30. package/dist/contracts/field.d.ts +23 -26
  31. package/dist/contracts/field.js +0 -107
  32. package/dist/contracts/lifecycle-trait.js +0 -5
  33. package/dist/contracts/resource-operations.d.ts +17 -18
  34. package/dist/contracts/resource-operations.js +0 -63
  35. package/dist/contracts/scalar-value-type.d.ts +12 -22
  36. package/dist/contracts/scalar.js +0 -1
  37. package/dist/contracts/wire-scalar-format.d.ts +20 -20
  38. package/dist/contracts/wire-scalar-format.js +0 -26
  39. package/dist/framework/contract-view.d.ts +7 -8
  40. package/dist/framework/framework-construction.error.js +0 -1
  41. package/dist/framework/framework.d.ts +26 -27
  42. package/dist/framework/framework.factory.js +0 -49
  43. package/dist/framework/operation-facade.js +0 -17
  44. package/dist/operations/operation-arguments.d.ts +36 -46
  45. package/dist/operations/operation-boolean-nodes.js +0 -16
  46. package/dist/operations/operation-call.d.ts +4 -9
  47. package/dist/operations/operation-identity.js +0 -1
  48. package/dist/operations/operation-nesting.js +0 -5
  49. package/dist/operations/operation-result.d.ts +4 -4
  50. package/dist/operations/operation-validator-core.d.ts +2 -2
  51. package/dist/operations/operation-validator-core.js +6 -71
  52. package/dist/operations/operation-validator.d.ts +4 -4
  53. package/dist/operations/operation-validator.js +0 -12
  54. package/dist/operations/projection-scope.resolver.d.ts +8 -10
  55. package/dist/operations/projection-scope.resolver.js +0 -35
  56. package/dist/operations/validation-code.d.ts +11 -18
  57. package/dist/operations/validation-code.js +0 -24
  58. package/dist/operations/write-scope.resolver.d.ts +4 -7
  59. package/dist/operations/write-scope.resolver.js +0 -37
  60. package/dist/protocol/absent-route.responder.d.ts +33 -48
  61. package/dist/protocol/absent-route.responder.js +13 -57
  62. package/dist/protocol/av-protocol.d.ts +6 -6
  63. package/dist/protocol/av-protocol.js +0 -10
  64. package/dist/protocol/contract-document.responder.d.ts +15 -18
  65. package/dist/protocol/contract-document.responder.js +0 -33
  66. package/dist/protocol/http-request.decoder.d.ts +55 -61
  67. package/dist/protocol/http-request.decoder.js +0 -62
  68. package/dist/protocol/http-request.reader.d.ts +12 -13
  69. package/dist/protocol/http-request.reader.js +0 -45
  70. package/dist/protocol/http-response.encoder.d.ts +17 -18
  71. package/dist/protocol/http-response.encoder.js +0 -59
  72. package/dist/protocol/http-status.table.d.ts +7 -11
  73. package/dist/protocol/http-status.table.js +1 -15
  74. package/dist/protocol/index.d.ts +5 -5
  75. package/dist/protocol/index.js +0 -9
  76. package/dist/protocol/operation-response.guard.d.ts +12 -19
  77. package/dist/protocol/operation-response.guard.js +0 -29
  78. package/dist/protocol/protocol-failure.d.ts +26 -23
  79. package/dist/protocol/protocol-failure.js +6 -6
  80. package/dist/protocol/protocol-headers.d.ts +30 -27
  81. package/dist/protocol/protocol-headers.js +3 -61
  82. package/dist/protocol/protocol-surface.d.ts +25 -29
  83. package/dist/protocol/protocol-surface.js +0 -19
  84. package/dist/protocol/protocol.binder.d.ts +58 -71
  85. package/dist/protocol/protocol.binder.js +0 -8
  86. package/dist/protocol/protocol.handler.d.ts +28 -32
  87. package/dist/protocol/protocol.handler.js +0 -43
  88. package/dist/runtime/computed-read-runtime.js +0 -29
  89. package/dist/runtime/create-requirement.d.ts +65 -0
  90. package/dist/runtime/create-requirement.js +141 -0
  91. package/dist/runtime/decimal.d.ts +2 -2
  92. package/dist/runtime/decimal.js +0 -8
  93. package/dist/runtime/diagnostics-reporter.d.ts +6 -6
  94. package/dist/runtime/diagnostics-reporter.js +0 -35
  95. package/dist/runtime/error-boundary.d.ts +3 -3
  96. package/dist/runtime/error-boundary.js +2 -167
  97. package/dist/runtime/execution-options.d.ts +4 -4
  98. package/dist/runtime/field-behavior.d.ts +6 -9
  99. package/dist/runtime/framework-error.js +0 -4
  100. package/dist/runtime/mutation-planner.d.ts +5 -6
  101. package/dist/runtime/mutation-planner.js +0 -23
  102. package/dist/runtime/operation-response.d.ts +1 -1
  103. package/dist/runtime/operation-response.js +1 -17
  104. package/dist/runtime/operation-runtime.d.ts +24 -22
  105. package/dist/runtime/operation-runtime.js +18 -52
  106. package/dist/runtime/outcome-mapper.js +0 -22
  107. package/dist/runtime/output-validator.js +0 -1
  108. package/dist/runtime/pipe-output.validator.d.ts +48 -0
  109. package/dist/runtime/pipe-output.validator.js +79 -0
  110. package/dist/runtime/pipeline.d.ts +20 -26
  111. package/dist/runtime/referential-action.d.ts +14 -19
  112. package/dist/runtime/referential-action.js +0 -23
  113. package/dist/runtime/request-id.js +0 -21
  114. package/dist/runtime/scalar-runtime.d.ts +14 -16
  115. package/dist/runtime/scalar-runtime.js +12 -57
  116. package/dist/transactions/deferred-operation.d.ts +9 -14
  117. package/dist/transactions/operation-arguments-encoder.d.ts +0 -1
  118. package/dist/transactions/operation-arguments-encoder.js +0 -14
  119. package/dist/transactions/operation-fingerprint.js +0 -1
  120. package/dist/transactions/transaction-envelope.js +0 -14
  121. package/dist/transactions/transaction-integrity-error.d.ts +10 -11
  122. package/dist/transactions/transaction-integrity-error.js +0 -5
  123. package/dist/transactions/transaction-plan-assembler.d.ts +0 -1
  124. package/dist/transactions/transaction-plan-assembler.js +0 -35
  125. package/dist/transactions/transaction-plan-validator.d.ts +5 -5
  126. package/dist/transactions/transaction-plan-validator.js +0 -63
  127. package/dist/transactions/transaction-reference-resolver.d.ts +12 -13
  128. package/dist/transactions/transaction-reference-resolver.js +0 -16
  129. package/dist/transactions/transaction-reference-validator.js +0 -8
  130. package/dist/transactions/transaction-reference.d.ts +19 -43
  131. package/dist/transactions/transaction-reference.js +0 -11
  132. package/dist/transactions/transaction-request.d.ts +4 -5
  133. package/dist/transactions/transaction-response.d.ts +1 -1
  134. package/dist/transactions/transaction-runtime.d.ts +2 -3
  135. package/dist/transactions/transaction-runtime.js +5 -44
  136. package/dist/transactions/transaction-slot-path-error.d.ts +10 -11
  137. package/dist/transactions/transaction-slot-path-error.js +0 -7
  138. package/dist/transactions/transaction-slot-writer.d.ts +12 -12
  139. package/dist/transactions/transaction-slot-writer.js +0 -34
  140. package/package.json +2 -2
@@ -10,13 +10,13 @@ export type FrameworkFieldBehaviorConfig<M extends AdapterModel = AdapterModel,
10
10
  readonly read?: false | FrameworkReadBehavior<M, Cfg, R>;
11
11
  };
12
12
  /**
13
- * Executable behavior bindings for one Resource, keyed on the single
14
- * effective field-key answer.
13
+ * Executable behavior bindings for one Resource, keyed on the single effective
14
+ * field-key answer.
15
15
  *
16
- * `Partial` is required, not cosmetic: a mapped type over a literal key set
17
- * would otherwise demand a binding for every field of the Resource. Under the
18
- * default `Cfg` the key set is `string`, the mapped half collapses to one
19
- * index signature and today's wide acceptance is preserved (B4).
16
+ * `Partial` is required, not cosmetic: a mapped type over a literal key set would
17
+ * otherwise demand a binding for every field of the Resource. Under the default
18
+ * `Cfg` the key set is `string`, the mapped half collapses to one index signature
19
+ * and today's wide acceptance is preserved.
20
20
  */
21
21
  export type FrameworkResourceBehaviorsConfig<M extends AdapterModel = AdapterModel, Cfg = AnyFrameworkConfig, R extends FrameworkResourceKey<M> = FrameworkResourceKey<M>> = {
22
22
  readonly fields: Readonly<Partial<{
@@ -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. Both
17
- * `restrictions` and `behaviors` read it, keying each Resource's field names
18
- * on `EffectiveFieldKey`; `pipelines` joins them at S7. Under the default
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 all.
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. Only positions the canonical baseline already advertises
18
- * may be named: C-858, *"later layers only narrow"*, and naming one it does not
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" (C-843).
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
- * {@link EffectiveFieldKey} degenerates to `string`, the mapped type below
136
- * collapses to a single index signature and no field identity survives to
137
- * discriminate on. Today's wide acceptance is preserved there deliberately
138
- * (B4): narrowing below the resource key is opt-in, never imposed.
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
- * the target type graph an added field is an added *scalar* field — there is
144
- * no configuration surface that authors a relation — so the answer is
145
- * {@link FrameworkScalarFieldRestrictions}. It is deliberately not the
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
- * F-602 closed here. This type used to intersect the mapped half with
156
- * `Readonly<Record<string, FrameworkFieldRestrictions>>`, whose index
157
- * signature absorbed every field name the model does not declare. The
158
- * catch-all is gone: a name must be an adapter field or one `Cfg` adds.
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
- * Takes `M` only, not `<M, Cfg>` as B8 proposed: nothing a pipeline callback
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
- * **Un-parameterized, deliberately, and not `ResolvedPipelineConfig<M, Cfg>`
24
- * as B12 proposed.** Resolution is where narrowing ends. The runtime builds
25
- * every pipeline context from `FrameworkOperationRequest.resource`, which is
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
- * occurs in these callbacks *only* in parameter position, never as a
36
- * mapped-type key, so `PipelineConfig<M>` is accepted where the erased form
37
- * is required and `resolvePipelines` composes the authored lists with no
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
- * states the positions it is projectable in. The axis is closed at two because
12
- * two is what both pilot engines answer — a third position would have to be
13
- * measured, not supposed.
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
- * `include: { <owned to-one>: { include: { <to-many>: true } } }` and populated
19
- * the rows (the nested to-many returned its 3 rows and a full reducer object),
20
- * while the SAME to-many asked for at depth 1 was refused outright. The reason is
21
- * visible in the generated types: a bulk projection types an admitted relation as
22
- * the *unrestricted* projection argument, so the full surface returns one level
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. That breaches C-855 in the other direction, and
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 (C-843, and §4's rejection of one fact with two hashes). That reader asks
36
- * "does this list name every member of the axis?" against this array. A hardcoded
37
- * two would answer *yes* for a two-of-three list the day a third position is
38
- * measured, which widens every Contract carrying it and nothing downstream can
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
- * separately — an adapter may narrow one without narrowing the other.
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 (C-855, sign flipped).
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
- * {@link Structured} decodes it into a TypeScript type. That decoded type must
20
- * be `ClientContract` exactly, in both directions:
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
- * back the decoded value as a `ClientContract` with no cast, so a schema that
24
- * admits something the type does not fails to compile.
25
- * - **type → schema** is enforced by `client-contract-structure.validator.test-d.ts`:
26
- * a schema that is stricter than the type — a vocabulary token missed, an
27
- * optional member written as required — fails the type test.
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` (ADR 0009): what the
35
- * type admits is accepted, what it rules out is refused. Two consequences are
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
- * {@link Structured} ignores, so the decoded type is still `ClientContract`
53
- * exactly. Every reader judges it here, once, rather than each re-checking it.
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
  }