@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.
- package/README.md +3 -3
- 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 +13 -57
- 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 +26 -23
- package/dist/protocol/protocol-failure.js +6 -6
- package/dist/protocol/protocol-headers.d.ts +30 -27
- package/dist/protocol/protocol-headers.js +3 -61
- 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
package/README.md
CHANGED
|
@@ -11,9 +11,9 @@ Core is ORM- and transport-agnostic: it imports neither Prisma nor NestJS. An ap
|
|
|
11
11
|
## Install
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
npx @aventara/cli new my-api # a new NestJS project with Prisma and Aventara
|
|
15
|
-
npx @aventara/cli init # or: add Aventara to an existing NestJS 12 project
|
|
16
|
-
npm install @aventara/core # the package on its own
|
|
14
|
+
npx @aventara/cli@pilot new my-api # a new NestJS project with Prisma and Aventara
|
|
15
|
+
npx @aventara/cli@pilot init # or: add Aventara to an existing NestJS 12 project
|
|
16
|
+
npm install @aventara/core@pilot # the package on its own
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
Node `^22.18.0 || >=24.2.0`. ES modules, with a CommonJS `default` condition.
|
|
@@ -30,16 +30,16 @@ export type AdapterScalarField<M extends AdapterModel> = {
|
|
|
30
30
|
* qualifier included, and that sharing is the point: the ADAPTER is the only party
|
|
31
31
|
* that can measure which argument positions a relation is reachable in, because
|
|
32
32
|
* the answer depends on which provider call each operation variant is routed to.
|
|
33
|
-
* Core tabulating it would be a tabulated provider fact
|
|
34
|
-
*
|
|
33
|
+
* Core tabulating it would be a tabulated provider fact and would be wrong for the
|
|
34
|
+
* next adapter.
|
|
35
35
|
*
|
|
36
36
|
* The qualifier is OPTIONAL at this boundary, as it is in the Contract: an adapter
|
|
37
37
|
* that has not measured the axis states the unqualified answer and is believed,
|
|
38
38
|
* which is what keeps a surface that predates the measurement meaningful rather
|
|
39
39
|
* than silently narrowed.
|
|
40
40
|
*
|
|
41
|
-
* **A scope qualifies the TOP LEVEL of an argument only** — the measurement and
|
|
42
|
-
* reason are recorded on `ProjectionScope`, and a discoverer that narrows
|
|
41
|
+
* **A scope qualifies the TOP LEVEL of an argument only** — the measurement and
|
|
42
|
+
* the reason are recorded on `ProjectionScope`, and a discoverer that narrows
|
|
43
43
|
* recursively would advertise less than the provider accepts.
|
|
44
44
|
*/
|
|
45
45
|
export type AdapterRelationField<M extends AdapterModel> = {
|
|
@@ -21,20 +21,18 @@ export type AdapterFieldKey<M extends AdapterModel, R extends Extract<keyof M["r
|
|
|
21
21
|
* Provider-discovered facts for one Aventara Resource candidate.
|
|
22
22
|
*
|
|
23
23
|
* `TFields` is never supplied by a caller — every in-repo site writes
|
|
24
|
-
* `AdapterResource<M>` — but it is **load-bearing and must not be inlined**.
|
|
25
|
-
*
|
|
24
|
+
* `AdapterResource<M>` — but it is **load-bearing and must not be inlined**. It is
|
|
25
|
+
* the indirection that keeps `M` out of this type's body.
|
|
26
26
|
*
|
|
27
|
-
* `AdapterField<M>` is invariant in `M` (it reads `keyof M["scalars"]` and
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* AdapterResource<this>>>` constraint. Measured: 43 type errors across the
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* not the arity.
|
|
27
|
+
* `AdapterField<M>` is invariant in `M` (it reads `keyof M["scalars"]` and `keyof
|
|
28
|
+
* M["resources"]`). If `M` reached the body, `AdapterResource` would acquire an
|
|
29
|
+
* invariant variance marker, and every concrete model would stop satisfying the
|
|
30
|
+
* self-typed `AdapterModel.resources: Readonly<Record<string,
|
|
31
|
+
* AdapterResource<this>>>` constraint. Measured: 43 type errors across the adapter
|
|
32
|
+
* and index type tests, and identically 43 whether the parameter is deleted or
|
|
33
|
+
* merely bypassed — so it is the body occurrence that matters, not the arity.
|
|
35
34
|
*
|
|
36
|
-
*
|
|
37
|
-
* mistaken rather than actioned. Deleting it is not a cleanup.
|
|
35
|
+
* Deleting it is not a cleanup.
|
|
38
36
|
*/
|
|
39
37
|
export type AdapterResource<M extends AdapterModel, TFields extends AdapterFieldMap<M> = AdapterFieldMap<M>> = {
|
|
40
38
|
readonly fields: TFields;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { CreateRequirementTable } from "../runtime/create-requirement.js";
|
|
1
2
|
import type { ReferentialActionTable } from "../runtime/referential-action.js";
|
|
2
3
|
import type { AdapterModel } from "./adapter-model.js";
|
|
3
4
|
import type { AdapterOperationExecutor } from "./adapter-operation-executor.interface.js";
|
|
@@ -25,17 +26,26 @@ export interface Adapter<M extends AdapterModel = AdapterModel> extends AdapterO
|
|
|
25
26
|
*/
|
|
26
27
|
readonly referentialActions?: ReferentialActionTable;
|
|
27
28
|
/**
|
|
28
|
-
* Optional
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
29
|
+
* Optional create requirements: for each Resource, the values its store
|
|
30
|
+
* cannot create a row without, in Aventara's words
|
|
31
|
+
* (`runtime/create-requirement.ts`). Read once at framework construction,
|
|
32
|
+
* like `model`, and never part of a Contract or its hash.
|
|
33
|
+
*
|
|
34
|
+
* Optional so an Adapter that cannot state them is believed: core then checks
|
|
35
|
+
* nothing, and a missing value reaches the store as before.
|
|
36
|
+
*/
|
|
37
|
+
readonly createRequirements?: CreateRequirementTable;
|
|
38
|
+
/**
|
|
39
|
+
* Optional callback transaction capability, required exactly when the discovered
|
|
40
|
+
* model declares `transactions: "interactive"`. The callback receives only a
|
|
41
|
+
* transaction-scoped operation executor, never the provider client itself, and
|
|
42
|
+
* every operation of a plan executes through it.
|
|
32
43
|
*
|
|
33
|
-
* The implementation MUST reject with the exact value `work` threw — by
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* slow work (spec §14.5).
|
|
44
|
+
* The implementation MUST reject with the exact value `work` threw — by identity,
|
|
45
|
+
* never a wrapper — and MUST NOT resolve after `work` rejected; rollback is
|
|
46
|
+
* implied by that rejection. Framework guards, pipes, hooks and interceptors of
|
|
47
|
+
* every plan operation run inside `work` while the provider transaction is open,
|
|
48
|
+
* so they must not perform network I/O or unrelated slow work.
|
|
39
49
|
*/
|
|
40
50
|
transaction?<T>(work: (executor: AdapterOperationExecutor<M>) => Promise<T>): Promise<T>;
|
|
41
51
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
export type ContractCompilerDiagnosticCode = "COMPILER_UNKNOWN_RESOURCE" | "COMPILER_UNKNOWN_FIELD" | "COMPILER_RELATION_FIELD_CONFIG_UNSUPPORTED" | "COMPILER_INCOMPLETE_FIELD" | "COMPILER_STRUCTURAL_FIELD_OVERRIDE" | "COMPILER_MISSING_SCALAR" | "COMPILER_MISSING_ENUM" | "COMPILER_DANGLING_RELATION" | "COMPILER_UNAVAILABLE_OPERATION" | "COMPILER_UNAVAILABLE_CAPABILITY" | "COMPILER_INVALID_LIFECYCLE" | "COMPILER_MISSING_READ_BEHAVIOR" | "COMPILER_INVALID_BEHAVIOR_DEPENDENCY"
|
|
2
2
|
/**
|
|
3
3
|
* The finished, hashed client projection is not a structurally valid
|
|
4
|
-
* `ClientContract
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* inside the ClientContract (`$.enums.status.values[1]`).
|
|
4
|
+
* `ClientContract`. A framework-bug guard: the server never serves what its own
|
|
5
|
+
* generator would refuse. `path` is the JSONPath inside the ClientContract
|
|
6
|
+
* (`$.enums.status.values[1]`).
|
|
8
7
|
*/
|
|
9
8
|
| "COMPILER_INVALID_CLIENT_CONTRACT";
|
|
10
9
|
export type ContractCompilerDiagnostic = {
|
|
@@ -17,7 +17,7 @@ export type ContractCompilationResult = {
|
|
|
17
17
|
/**
|
|
18
18
|
* Whether writing `configured` VERBATIM as capability member `key` would advertise
|
|
19
19
|
* something `baseline` does not — the question the `fields` walk asks before it
|
|
20
|
-
* copies a value onto an adapter field
|
|
20
|
+
* copies a value onto an adapter field.
|
|
21
21
|
*
|
|
22
22
|
* Answered on the member's AXIS, off the same `satisfies`-pinned table the
|
|
23
23
|
* restriction walk and the output gate read, and the baseline is read through the
|
|
@@ -29,11 +29,12 @@ export type ContractCompilationResult = {
|
|
|
29
29
|
*
|
|
30
30
|
* The unqualified forms keep their meaning: `true` and a bare token list claim
|
|
31
31
|
* every position on the axis, so either one over a qualified baseline is an
|
|
32
|
-
* addition — writing it verbatim would widen the member
|
|
32
|
+
* addition — writing it verbatim would widen the member.
|
|
33
33
|
*
|
|
34
34
|
* A key no axis covers, and a value its axis does not admit, are not additions;
|
|
35
|
-
* they are unrepresentable, and the output gate ({@link
|
|
36
|
-
* is the one place that says so. So is a
|
|
35
|
+
* they are unrepresentable, and the output gate ({@link
|
|
36
|
+
* validateCapabilityRepresentation}) is the one place that says so. So is a
|
|
37
|
+
* position off the axis.
|
|
37
38
|
*
|
|
38
39
|
* Exported for its unit test only; it is not part of the package surface.
|
|
39
40
|
*/
|
|
@@ -150,36 +150,11 @@ function addConfiguredScalarField(config, path, diagnostics) {
|
|
|
150
150
|
},
|
|
151
151
|
};
|
|
152
152
|
}
|
|
153
|
-
/**
|
|
154
|
-
* Whether writing `configured` VERBATIM as capability member `key` would advertise
|
|
155
|
-
* something `baseline` does not — the question the `fields` walk asks before it
|
|
156
|
-
* copies a value onto an adapter field (F-817).
|
|
157
|
-
*
|
|
158
|
-
* Answered on the member's AXIS, off the same `satisfies`-pinned table the
|
|
159
|
-
* restriction walk and the output gate read, and the baseline is read through the
|
|
160
|
-
* shape's own owners: {@link holdsAt} for a position list, the `…At` readers for a
|
|
161
|
-
* scope-qualified token arm. Never off the runtime shape of the value: an
|
|
162
|
-
* object-shaped arm used to match no branch and pass unexamined, and `true` was
|
|
163
|
-
* compared to the baseline with `!== true`, which reads a qualified baseline as
|
|
164
|
-
* *not advertised* even where it holds in every position.
|
|
165
|
-
*
|
|
166
|
-
* The unqualified forms keep their meaning: `true` and a bare token list claim
|
|
167
|
-
* every position on the axis, so either one over a qualified baseline is an
|
|
168
|
-
* addition — writing it verbatim would widen the member (C-855).
|
|
169
|
-
*
|
|
170
|
-
* A key no axis covers, and a value its axis does not admit, are not additions;
|
|
171
|
-
* they are unrepresentable, and the output gate ({@link validateCapabilityRepresentation})
|
|
172
|
-
* is the one place that says so. So is a position off the axis.
|
|
173
|
-
*
|
|
174
|
-
* Exported for its unit test only; it is not part of the package surface.
|
|
175
|
-
*/
|
|
176
153
|
export function capabilityAddsBaseline(fieldKind, baseline, key, configured) {
|
|
177
154
|
const axis = capabilityAxisFor(fieldKind, key);
|
|
178
155
|
if (axis === undefined) {
|
|
179
156
|
return false;
|
|
180
157
|
}
|
|
181
|
-
// Every arm below answers `false` for a denial (`false`) and for no value at
|
|
182
|
-
// all: neither claims a token or a position.
|
|
183
158
|
const baselineValue = baseline[key];
|
|
184
159
|
switch (axis.kind) {
|
|
185
160
|
case "flag":
|
|
@@ -216,12 +191,6 @@ export function capabilityAddsBaseline(fieldKind, baseline, key, configured) {
|
|
|
216
191
|
}
|
|
217
192
|
}
|
|
218
193
|
}
|
|
219
|
-
/**
|
|
220
|
-
* The tokens a scope-qualified member value claims, and the positions it claims
|
|
221
|
-
* them in: a bare token list claims every position on the axis, a
|
|
222
|
-
* `{ <tokenKey>, scopes }` arm the positions it names. `undefined` for any other
|
|
223
|
-
* shape, which is the output gate's to refuse.
|
|
224
|
-
*/
|
|
225
194
|
function scopedTokenClaim(axis, configured) {
|
|
226
195
|
if (isTokenList(configured)) {
|
|
227
196
|
return { tokens: configured, positions: axis.axis };
|
|
@@ -236,11 +205,6 @@ function scopedTokenClaim(axis, configured) {
|
|
|
236
205
|
}
|
|
237
206
|
return undefined;
|
|
238
207
|
}
|
|
239
|
-
/**
|
|
240
|
-
* What a scope-qualified baseline member advertises at one position, through the
|
|
241
|
-
* shape's own `…At` owner for its token key. A baseline that is neither arm
|
|
242
|
-
* advertises nothing — the same answer an absent member gives.
|
|
243
|
-
*/
|
|
244
208
|
function advertisedTokensAt(tokenKey, baselineValue, position) {
|
|
245
209
|
if (!isTokenList(baselineValue) && !isRecord(baselineValue)) {
|
|
246
210
|
return undefined;
|
|
@@ -406,12 +370,6 @@ function applyBehaviors(projection, scope, config, diagnostics, allowRestrictedM
|
|
|
406
370
|
}
|
|
407
371
|
}
|
|
408
372
|
}
|
|
409
|
-
/**
|
|
410
|
-
* Pinned against the restriction type AND the Contract member type, so a capability
|
|
411
|
-
* member added to either surface fails to compile HERE rather than reaching the
|
|
412
|
-
* restriction walk with no axis and being skipped, or reaching the output gate with
|
|
413
|
-
* no axis and being refused.
|
|
414
|
-
*/
|
|
415
373
|
const SCALAR_CAPABILITY_AXES = {
|
|
416
374
|
select: { kind: "flag" },
|
|
417
375
|
filter: { kind: "tokens" },
|
|
@@ -420,7 +378,6 @@ const SCALAR_CAPABILITY_AXES = {
|
|
|
420
378
|
create: { kind: "tokens" },
|
|
421
379
|
update: { kind: "tokens" },
|
|
422
380
|
};
|
|
423
|
-
/** @see SCALAR_CAPABILITY_AXES — the same pin, on the relation surface. */
|
|
424
381
|
const RELATION_CAPABILITY_AXES = {
|
|
425
382
|
select: { kind: "scopes", axis: PROJECTION_SCOPES },
|
|
426
383
|
include: { kind: "scopes", axis: PROJECTION_SCOPES },
|
|
@@ -437,8 +394,6 @@ const RELATION_CAPABILITY_AXES = {
|
|
|
437
394
|
};
|
|
438
395
|
function capabilityAxisFor(kind, key) {
|
|
439
396
|
const axes = kind === "scalar" ? SCALAR_CAPABILITY_AXES : RELATION_CAPABILITY_AXES;
|
|
440
|
-
// An OWN key: the table is a plain object, and `axes["toString"]` would answer
|
|
441
|
-
// with a prototype method rather than with "no axis".
|
|
442
397
|
return Object.hasOwn(axes, key) ? axes[key] : undefined;
|
|
443
398
|
}
|
|
444
399
|
function unavailableCapability(path, message, diagnostics) {
|
|
@@ -456,10 +411,6 @@ function narrowedScopes(axis, holdsAtScope, requested, key, path, diagnostics) {
|
|
|
456
411
|
if (requested.length === 0) {
|
|
457
412
|
return { outcome: "denied" };
|
|
458
413
|
}
|
|
459
|
-
// Membership of the axis is asked first, and of the axis ARRAY rather than of a
|
|
460
|
-
// literal: the unqualified baseline holds at every position, so a position that
|
|
461
|
-
// is not a position at all would pass the `holdsAt` read and be written into the
|
|
462
|
-
// Contract. The type forbids it; a configuration assembled at runtime does not.
|
|
463
414
|
const unavailable = requested.filter((scope) => !axis.includes(scope) || !holdsAtScope(scope));
|
|
464
415
|
if (unavailable.length > 0) {
|
|
465
416
|
unavailableCapability(path, `capability "${key}" requested unavailable scopes: ${unavailable.join(", ")}`, diagnostics);
|
|
@@ -467,12 +418,6 @@ function narrowedScopes(axis, holdsAtScope, requested, key, path, diagnostics) {
|
|
|
467
418
|
}
|
|
468
419
|
return { outcome: "narrowed", scopes: [...requested] };
|
|
469
420
|
}
|
|
470
|
-
/**
|
|
471
|
-
* A projection member narrowed by position. The baseline is consulted through
|
|
472
|
-
* {@link holdsAt} rather than by inspecting the member's arm, so the unqualified
|
|
473
|
-
* baseline keeps meaning *holds in every position* and a narrowing against it is a
|
|
474
|
-
* narrowing rather than a mismatch.
|
|
475
|
-
*/
|
|
476
421
|
function applyScopeRestriction(axis, baselineValue, current, key, requested, path, diagnostics) {
|
|
477
422
|
const baseline = baselineValue;
|
|
478
423
|
const narrowing = narrowedScopes(axis, (scope) => holdsAt(baseline, scope), requested, key, path, diagnostics);
|
|
@@ -485,19 +430,6 @@ function applyScopeRestriction(axis, baselineValue, current, key, requested, pat
|
|
|
485
430
|
}
|
|
486
431
|
current[key] = narrowing.scopes;
|
|
487
432
|
}
|
|
488
|
-
/**
|
|
489
|
-
* A scope-qualified token member narrowed on either axis, or both. The tokens and
|
|
490
|
-
* the positions are read through the shape owners — {@link advertisedDirectives} /
|
|
491
|
-
* {@link advertisedReducers} for the token list, and the `…At` pair for the
|
|
492
|
-
* positions — so the two arms of the Contract member are never destructured here.
|
|
493
|
-
*
|
|
494
|
-
* A token-only narrowing **keeps the qualifier the baseline carries**: narrowing
|
|
495
|
-
* what may be written is not a statement about where it may be written, and
|
|
496
|
-
* dropping the qualifier would widen the member to every position (C-855).
|
|
497
|
-
*
|
|
498
|
-
* Answers whether the restriction was a shape this member admits at all. `false`
|
|
499
|
-
* leaves the diagnostic to the caller, which owns the one message for every axis.
|
|
500
|
-
*/
|
|
501
433
|
function applyScopedTokenRestriction(tokenKey, axis, baselineValue, current, key, value, path, diagnostics) {
|
|
502
434
|
const requestedTokens = Array.isArray(value)
|
|
503
435
|
? value
|
|
@@ -546,18 +478,11 @@ function applyScopedTokenRestriction(tokenKey, axis, baselineValue, current, key
|
|
|
546
478
|
return true;
|
|
547
479
|
}
|
|
548
480
|
function applyCapabilityRestriction(fieldKind, inherited, restoration, current, key, value, path, diagnostics) {
|
|
549
|
-
// Denial needs no baseline: a layer may always refuse to carry a capability
|
|
550
|
-
// forward, including one an earlier layer already removed.
|
|
551
481
|
if (value === false) {
|
|
552
482
|
delete current[key];
|
|
553
483
|
return;
|
|
554
484
|
}
|
|
555
485
|
const axis = capabilityAxisFor(fieldKind, key);
|
|
556
|
-
// The member is measured against what this layer INHERITED when it inherited
|
|
557
|
-
// one, and against the root baseline only when it inherited none — the one
|
|
558
|
-
// split, by role, that every arm below reads through (F-815, LayerBaselines).
|
|
559
|
-
// The operations axis draws the same line: an advertised variant keeps what it
|
|
560
|
-
// carries, an absent one is restored.
|
|
561
486
|
const baselineValue = inherited?.[key] ?? restoration[key];
|
|
562
487
|
if (axis === undefined ||
|
|
563
488
|
baselineValue === undefined ||
|
|
@@ -565,13 +490,6 @@ function applyCapabilityRestriction(fieldKind, inherited, restoration, current,
|
|
|
565
490
|
unavailableCapability(path, `capability "${key}" is not available in the canonical baseline`, diagnostics);
|
|
566
491
|
return;
|
|
567
492
|
}
|
|
568
|
-
// `true` takes the measured answer VERBATIM, qualifier included: what this layer
|
|
569
|
-
// inherited when there is one — *available, as narrowed above me* — and the
|
|
570
|
-
// root baseline's answer when the member is being restored. Preserving a
|
|
571
|
-
// qualified capability is not widening it — the distinction the operations axis
|
|
572
|
-
// draws between preserving a descriptor and replacing it with the unqualified
|
|
573
|
-
// arm — and the clone is of the ceiling this layer is measured against, so it
|
|
574
|
-
// can never exceed it.
|
|
575
493
|
if (value === true) {
|
|
576
494
|
current[key] = cloneValue(baselineValue);
|
|
577
495
|
return;
|
|
@@ -640,11 +558,6 @@ function applyOperationRestrictions(restoration, current, restrictions, path, di
|
|
|
640
558
|
diagnostics.push(diagnostic("COMPILER_UNAVAILABLE_OPERATION", `${path}.${family}.${variant}`, `operation "${family}.${variant}" is not available in the canonical baseline`));
|
|
641
559
|
continue;
|
|
642
560
|
}
|
|
643
|
-
// An already-advertised variant keeps the availability this projection
|
|
644
|
-
// carries: an earlier layer may have narrowed `true` to a descriptor, and
|
|
645
|
-
// later layers only narrow (C-858). Only a variant this scope restores
|
|
646
|
-
// takes the baseline's availability, cloned so that no two projections
|
|
647
|
-
// share one descriptor object.
|
|
648
561
|
const target = currentFamily;
|
|
649
562
|
if (!isOperationVariantAvailable(target[variant])) {
|
|
650
563
|
target[variant] = cloneValue(baselineAvailability);
|
|
@@ -742,9 +655,6 @@ function reconcileIdentifiers(resource, baseline, restrictions) {
|
|
|
742
655
|
: undefined;
|
|
743
656
|
if (isOperationVariantAvailable(baselineAvailability)) {
|
|
744
657
|
const targetFamily = isRecord(currentFamily) ? currentFamily : {};
|
|
745
|
-
// Restoring the variant must not restate its availability: a variant this
|
|
746
|
-
// projection still advertises keeps the descriptor it carries, and one
|
|
747
|
-
// identifiers bring back takes the baseline's, cloned per projection.
|
|
748
658
|
if (!isOperationVariantAvailable(targetFamily[variant])) {
|
|
749
659
|
targetFamily[variant] = cloneValue(baselineAvailability);
|
|
750
660
|
}
|
|
@@ -799,11 +709,6 @@ function validateFieldLifecycle(field, path, diagnostics) {
|
|
|
799
709
|
}
|
|
800
710
|
}
|
|
801
711
|
if (lifecycle.includes("VIRTUAL") &&
|
|
802
|
-
// Not `=== true`: a projection member may carry the scopes it holds in, and a
|
|
803
|
-
// field selectable in one scope needs an effective read behavior exactly as
|
|
804
|
-
// much as one selectable in every scope. The question is scope-blind because
|
|
805
|
-
// this check has no scope — `holdsInAnyScope` is the shape owner's name for
|
|
806
|
-
// that, so there is no hand-rolled comparison here either.
|
|
807
712
|
holdsInAnyScope(capabilities.select) &&
|
|
808
713
|
!lifecycle.includes("COMPUTED_ON_READ")) {
|
|
809
714
|
diagnostics.push(diagnostic("COMPILER_MISSING_READ_BEHAVIOR", path, "selectable VIRTUAL field requires an effective read behavior"));
|
|
@@ -812,13 +717,6 @@ function validateFieldLifecycle(field, path, diagnostics) {
|
|
|
812
717
|
function isTokenList(value) {
|
|
813
718
|
return (Array.isArray(value) && value.every((entry) => typeof entry === "string"));
|
|
814
719
|
}
|
|
815
|
-
/**
|
|
816
|
-
* Why a position list is not one the Contract can carry on this axis, or
|
|
817
|
-
* `undefined` when it is. Empty is refused for the reason `ProjectionScopes` and
|
|
818
|
-
* `WriteScopes` give — absent is already the one encoding of unavailable (C-843) —
|
|
819
|
-
* and membership is asked of the axis ARRAY, so a position from the other axis is
|
|
820
|
-
* as off-axis as a string that is no position at all.
|
|
821
|
-
*/
|
|
822
720
|
function positionListFault(axis, positions) {
|
|
823
721
|
if (positions.length === 0) {
|
|
824
722
|
return "an empty position list";
|
|
@@ -828,17 +726,6 @@ function positionListFault(axis, positions) {
|
|
|
828
726
|
? `positions not on its axis: ${offAxis.join(", ")}`
|
|
829
727
|
: undefined;
|
|
830
728
|
}
|
|
831
|
-
/**
|
|
832
|
-
* Why a member value is not a shape its axis admits, or `undefined` when it is —
|
|
833
|
-
* each arm the Contract type declares for that axis, and nothing else.
|
|
834
|
-
*
|
|
835
|
-
* Shape only. Whether the value is NARROWER than the layer it derives from is
|
|
836
|
-
* C-858's question, and the restriction walk already answers it against
|
|
837
|
-
* {@link LayerBaselines} (F-815); asking it again here would be the same rule in a
|
|
838
|
-
* second place. Token vocabularies are not checked either: the token unions are
|
|
839
|
-
* types with no runtime registry, and inventing one here would be a second source
|
|
840
|
-
* of truth for them.
|
|
841
|
-
*/
|
|
842
729
|
function representationFault(axis, value) {
|
|
843
730
|
switch (axis.kind) {
|
|
844
731
|
case "flag":
|
|
@@ -867,21 +754,8 @@ function representationFault(axis, value) {
|
|
|
867
754
|
}
|
|
868
755
|
}
|
|
869
756
|
}
|
|
870
|
-
/**
|
|
871
|
-
* The output gate (F-816): every capability member a finished projection carries
|
|
872
|
-
* is checked against the member-to-axis table before the projection can be hashed.
|
|
873
|
-
*
|
|
874
|
-
* The projection is assembled from configuration that may be built at runtime and
|
|
875
|
-
* from an adapter model that is a runtime value, and two of the walks that assemble
|
|
876
|
-
* it copy a member verbatim; the canonicalizer orders what it is handed and
|
|
877
|
-
* deliberately rewrites nothing it does not recognise. So the type is not the
|
|
878
|
-
* guard, the producers are not the guard, and without this the compiler emits —
|
|
879
|
-
* and hashes — whatever its walks assembled.
|
|
880
|
-
*/
|
|
881
757
|
function validateCapabilityRepresentation(field, path, diagnostics) {
|
|
882
758
|
for (const [key, value] of Object.entries(field.capabilities)) {
|
|
883
|
-
// No `undefined` skip: an own property holding no value is not canonical JSON,
|
|
884
|
-
// and skipping it here hands the canonicalizer a throw instead of a diagnostic.
|
|
885
759
|
const axis = capabilityAxisFor(field.kind, key);
|
|
886
760
|
const fault = axis === undefined
|
|
887
761
|
? "no capability axis covers it"
|
|
@@ -953,7 +827,6 @@ function projectDerivedScope(model, scopeName, core, scope, diagnostics) {
|
|
|
953
827
|
validateProjection(projection, baseline, scopeName, scope.restrictions, diagnostics);
|
|
954
828
|
return projection;
|
|
955
829
|
}
|
|
956
|
-
/** Compile Core first, then derive Application and Client from the Core compilation lineage. */
|
|
957
830
|
export async function compileContracts(model, config) {
|
|
958
831
|
const compilerConfig = config;
|
|
959
832
|
const diagnostics = [];
|
|
@@ -974,11 +847,6 @@ export async function compileContracts(model, config) {
|
|
|
974
847
|
...clientHashInput,
|
|
975
848
|
protocol: { version: AVENTARA_PROTOCOL_VERSION, hash },
|
|
976
849
|
});
|
|
977
|
-
// R3 (Phase 10): the server checks its own ClientContract before serving
|
|
978
|
-
// it — here, so construction fails before listen, and every consumer
|
|
979
|
-
// (`framework.client`, `/_transactions`, `/_contract`, the generator)
|
|
980
|
-
// reads a value that passed. A refusal is a framework bug or an Adapter
|
|
981
|
-
// defect the projection copied through, never a caller's.
|
|
982
850
|
const structure = validateClientContractStructure(client);
|
|
983
851
|
if (!structure.valid) {
|
|
984
852
|
return {
|
|
@@ -28,8 +28,8 @@ export type ResolvedFrameworkScopeConfig<M extends AdapterModel = AdapterModel>
|
|
|
28
28
|
};
|
|
29
29
|
export type ResolvedFrameworkConfig<M extends AdapterModel = AdapterModel> = {
|
|
30
30
|
/**
|
|
31
|
-
* The mount path in its canonical form
|
|
32
|
-
*
|
|
31
|
+
* The mount path in its canonical form: `""` at the root, otherwise `/seg(/seg)*`.
|
|
32
|
+
* Normalized once, here; every reader sees this.
|
|
33
33
|
*/
|
|
34
34
|
readonly entrypoint: string;
|
|
35
35
|
readonly adapter: Adapter<M>;
|
|
@@ -77,10 +77,6 @@ function resolveScope(root, scoped) {
|
|
|
77
77
|
pipelines: resolvePipelines(root.pipelines, scoped?.pipelines),
|
|
78
78
|
};
|
|
79
79
|
}
|
|
80
|
-
/**
|
|
81
|
-
* The entrypoint's canonical mount path. Validation refuses a non-canonical one
|
|
82
|
-
* first; a caller that resolves without validating meets the same diagnostic.
|
|
83
|
-
*/
|
|
84
80
|
function resolveEntrypoint(entrypoint) {
|
|
85
81
|
const normalized = normalizeEntrypoint(entrypoint);
|
|
86
82
|
if (!normalized.ok) {
|
|
@@ -90,7 +86,6 @@ function resolveEntrypoint(entrypoint) {
|
|
|
90
86
|
}
|
|
91
87
|
return normalized.path;
|
|
92
88
|
}
|
|
93
|
-
/** Resolve root, application, and client configuration without compiling Contracts. */
|
|
94
89
|
export function resolveFrameworkConfig(config) {
|
|
95
90
|
const root = {
|
|
96
91
|
...(hasOwn(config, "fields") ? { fields: config.fields } : {}),
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ConfigDiagnostic } from "./config-diagnostic.js";
|
|
2
2
|
/**
|
|
3
|
-
* The refusal of an entrypoint whose mount path is not canonical
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* The refusal of an entrypoint whose mount path is not canonical, from the
|
|
4
|
+
* normalizer's message: it names the defect and the rule — never the value, which
|
|
5
|
+
* may carry credentials.
|
|
6
6
|
*/
|
|
7
7
|
export declare function entrypointRefusalDiagnostic(message: string): ConfigDiagnostic;
|
|
8
8
|
/** Validate framework-owned authoring structure; later semantic leaves remain opaque. */
|
|
@@ -47,11 +47,6 @@ function isPlainRecord(value) {
|
|
|
47
47
|
function diagnostic(code, path, message) {
|
|
48
48
|
return { code, path, message };
|
|
49
49
|
}
|
|
50
|
-
/**
|
|
51
|
-
* The refusal of an entrypoint whose mount path is not canonical (R4, F-822,
|
|
52
|
-
* Q15), from the normalizer's message: it names the defect and the rule —
|
|
53
|
-
* never the value, which may carry credentials.
|
|
54
|
-
*/
|
|
55
50
|
export function entrypointRefusalDiagnostic(message) {
|
|
56
51
|
return diagnostic("CONFIG_INVALID_VALUE", "entrypoint", `entrypoint ${message}`);
|
|
57
52
|
}
|
|
@@ -151,8 +146,6 @@ function validateAdapter(value, diagnostics) {
|
|
|
151
146
|
return;
|
|
152
147
|
}
|
|
153
148
|
const adapter = value;
|
|
154
|
-
// One property read, not two: `adapter.model` is a property of the
|
|
155
|
-
// adapter, so validation must not observe it more times than it needs.
|
|
156
149
|
const model = adapter.model;
|
|
157
150
|
if (model === null || typeof model !== "object") {
|
|
158
151
|
diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", "adapter.model", "adapter.model must be an object"));
|
|
@@ -165,7 +158,6 @@ function validateAdapter(value, diagnostics) {
|
|
|
165
158
|
diagnostics.push(diagnostic("CONFIG_INVALID_TYPE", "adapter.transaction", "adapter.transaction must be a function when provided"));
|
|
166
159
|
}
|
|
167
160
|
}
|
|
168
|
-
/** Validate framework-owned authoring structure; later semantic leaves remain opaque. */
|
|
169
161
|
export function validateFrameworkConfig(value) {
|
|
170
162
|
const diagnostics = [];
|
|
171
163
|
if (!isPlainRecord(value)) {
|
|
@@ -28,13 +28,8 @@ export type AddedScalarFieldConfig<M extends AdapterModel = AdapterModel> = Omit
|
|
|
28
28
|
/**
|
|
29
29
|
* The shape one authored field name admits, by what the adapter knows of it.
|
|
30
30
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* abstract `AdapterModel` the discovered key set is erased to `string`, so
|
|
34
|
-
* every name would otherwise land in the adapter-known bucket and every
|
|
35
|
-
* added field would be rejected for carrying a `type`. Where there is no
|
|
36
|
-
* discovered key set to discriminate on, the answer is today's undivided
|
|
37
|
-
* {@link FrameworkScalarFieldConfig}.
|
|
31
|
+
* Where there is no discovered key set to discriminate on, the answer is today's
|
|
32
|
+
* undivided {@link FrameworkScalarFieldConfig}.
|
|
38
33
|
*/
|
|
39
34
|
type ScalarFieldConfigFor<M extends AdapterModel, R extends FrameworkResourceKey<M>, F> = string extends AdapterFieldKey<M, R> ? FrameworkScalarFieldConfig<M> : F extends AdapterFieldKey<M, R> ? M["resources"][R]["fields"][F] extends {
|
|
40
35
|
readonly kind: "relation";
|
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Authors stage 1 of a configuration: the `fields` block, checked against
|
|
3
|
-
* what the adapter discovered and returned unwidened.
|
|
4
|
-
*
|
|
5
|
-
* The returned literal is stage 2's `Cfg` fields block, so the names it adds
|
|
6
|
-
* are nameable from `restrictions`, `behaviors` and `dependsOn`.
|
|
7
|
-
*
|
|
8
|
-
* `M` is bound by the **model value**, not a type argument: TypeScript has
|
|
9
|
-
* no partial type-argument inference, so `defineFields<M>({ … })` would
|
|
10
|
-
* force `Self` to be written out by hand. The model is already a value at
|
|
11
|
-
* the authoring site, so binding it there costs nothing.
|
|
12
|
-
*
|
|
13
|
-
* The model argument is type-level only — the returned block is the authored
|
|
14
|
-
* one, unchanged.
|
|
15
|
-
*/
|
|
16
1
|
export function defineFields(_model, fields) {
|
|
17
2
|
return fields;
|
|
18
3
|
}
|
|
@@ -26,9 +26,8 @@ type FieldsBlockKey<Block, R extends string> = [Block] extends [never] ? never :
|
|
|
26
26
|
* The field names `Cfg`'s own root `fields` block adds for `R`.
|
|
27
27
|
*
|
|
28
28
|
* Under the default `Cfg` this degenerates to `string`: `AnyFieldsBlock` maps
|
|
29
|
-
* every resource name to an open record, so the effective key set widens back
|
|
30
|
-
*
|
|
31
|
-
* preserved (B4).
|
|
29
|
+
* every resource name to an open record, so the effective key set widens back to
|
|
30
|
+
* an index-signature-compatible `string` and today's wide acceptance is preserved.
|
|
32
31
|
*/
|
|
33
32
|
export type ConfigFieldKey<M extends AdapterModel, Cfg, R extends FrameworkResourceKey<M>> = FieldsBlockKey<FieldsBlockOf<Cfg>, R>;
|
|
34
33
|
/**
|
|
@@ -37,11 +36,8 @@ export type ConfigFieldKey<M extends AdapterModel, Cfg, R extends FrameworkResou
|
|
|
37
36
|
* Nothing else in the framework may compute a field-key set; `restrictions`,
|
|
38
37
|
* `behaviors` and `dependsOn` all key on this type.
|
|
39
38
|
*
|
|
40
|
-
* There is deliberately no subtraction here. A
|
|
41
|
-
*
|
|
42
|
-
* Consequences: restriction is projection policy applied at compilation, not
|
|
43
|
-
* a change to the resource's field identity. A `HiddenFieldKey` subtraction
|
|
44
|
-
* would contradict the Accepted ADR and must not be reintroduced.
|
|
39
|
+
* There is deliberately no subtraction here. A `HiddenFieldKey` subtraction would
|
|
40
|
+
* contradict the Accepted ADR and must not be reintroduced.
|
|
45
41
|
*/
|
|
46
42
|
export type EffectiveFieldKey<M extends AdapterModel, Cfg, R extends FrameworkResourceKey<M>> = AdapterFieldKey<M, R> | ConfigFieldKey<M, Cfg, R>;
|
|
47
43
|
/** The overlay `Cfg` carries for `Origin`, or `never` when it declares none. */
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The framework entrypoint's mount path, in its one canonical form
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* The framework entrypoint's mount path, in its one canonical form: `""` at the
|
|
3
|
+
* root, otherwise `/seg(/seg)*` — one leading slash, no trailing slash, no empty
|
|
4
|
+
* segment. Root is the empty string, not `"/"`, so a route joins onto it by
|
|
5
|
+
* concatenation with no special case.
|
|
6
6
|
*/
|
|
7
7
|
export type EntrypointPath = "" | `/${string}`;
|
|
8
8
|
/**
|
|
@@ -27,7 +27,7 @@ export type EntrypointNormalization = {
|
|
|
27
27
|
};
|
|
28
28
|
/**
|
|
29
29
|
* `AvProtocol.normalizeEntrypoint` — a mount path, as written, to its canonical
|
|
30
|
-
* form or a refusal
|
|
30
|
+
* form or a refusal.
|
|
31
31
|
*
|
|
32
32
|
* The one source of the rule: config resolution stores the canonical form as
|
|
33
33
|
* `framework.entrypoint`, config validation turns a refusal into a `CONFIG_`
|
|
@@ -36,8 +36,8 @@ export type EntrypointNormalization = {
|
|
|
36
36
|
* resolved or percent-encoded anything. Execution code reads it from here;
|
|
37
37
|
* `@aventara/core/protocol` re-exports it, and the main entry does not.
|
|
38
38
|
*
|
|
39
|
-
* Checked in order, so each value has one reason: whitespace, a scheme (`://`),
|
|
40
|
-
*
|
|
39
|
+
* Checked in order, so each value has one reason: whitespace, a scheme (`://`), a
|
|
40
|
+
* query, a fragment, a `..` or `.` segment (either separator, `%2e` spelling
|
|
41
41
|
* included), a backslash, a non-ASCII character, a `%`, and finally any other
|
|
42
42
|
* character outside RFC 3986's `pchar`.
|
|
43
43
|
*/
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/** What a canonical segment is, stated once for every refusal's message. */
|
|
2
1
|
const MOUNT_PATH_RULE = "a mount path is /-separated segments of ASCII letters, digits and -._~!$&'()*+,;=:@ only";
|
|
3
2
|
const DEFECTS = {
|
|
4
3
|
whitespace: "contains whitespace",
|
|
@@ -12,12 +11,7 @@ const DEFECTS = {
|
|
|
12
11
|
"percent-encoding": "contains a percent sign, and percent-encoding is refused",
|
|
13
12
|
character: "contains a character a path segment may not carry unencoded",
|
|
14
13
|
};
|
|
15
|
-
/**
|
|
16
|
-
* RFC 3986 `pchar` without `%` (Q15): unreserved, sub-delims, `:` and `@`. A
|
|
17
|
-
* canonical segment is one or more of these and nothing else.
|
|
18
|
-
*/
|
|
19
14
|
const CANONICAL_SEGMENT = /^[A-Za-z0-9\-._~!$&'()*+,;=:@]+$/u;
|
|
20
|
-
/** `.` or `..`, spelled as URL resolution recognises it: `.` or `%2e`, any case. */
|
|
21
15
|
const DOT_SEGMENT = /^(?:\.|%2e)$/iu;
|
|
22
16
|
const DOT_DOT_SEGMENT = /^(?:\.|%2e){2}$/iu;
|
|
23
17
|
function refusal(reason) {
|
|
@@ -27,22 +21,6 @@ function refusal(reason) {
|
|
|
27
21
|
message: `${DEFECTS[reason]}; ${MOUNT_PATH_RULE}`,
|
|
28
22
|
};
|
|
29
23
|
}
|
|
30
|
-
/**
|
|
31
|
-
* `AvProtocol.normalizeEntrypoint` — a mount path, as written, to its canonical
|
|
32
|
-
* form or a refusal (R4, F-822, Q15).
|
|
33
|
-
*
|
|
34
|
-
* The one source of the rule: config resolution stores the canonical form as
|
|
35
|
-
* `framework.entrypoint`, config validation turns a refusal into a `CONFIG_`
|
|
36
|
-
* diagnostic, and the client generator's `resolveEntrypoint` hands it the mount
|
|
37
|
-
* path of the URL it is given — as written, before a URL parser has trimmed,
|
|
38
|
-
* resolved or percent-encoded anything. Execution code reads it from here;
|
|
39
|
-
* `@aventara/core/protocol` re-exports it, and the main entry does not.
|
|
40
|
-
*
|
|
41
|
-
* Checked in order, so each value has one reason: whitespace, a scheme (`://`),
|
|
42
|
-
* a query, a fragment, a `..` or `.` segment (either separator, `%2e` spelling
|
|
43
|
-
* included), a backslash, a non-ASCII character, a `%`, and finally any other
|
|
44
|
-
* character outside RFC 3986's `pchar`.
|
|
45
|
-
*/
|
|
46
24
|
export function normalizeEntrypoint(value) {
|
|
47
25
|
if (/\s/u.test(value))
|
|
48
26
|
return refusal("whitespace");
|