@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
|
@@ -9,14 +9,8 @@ import { mapValidationOutcome } from "../runtime/outcome-mapper.js";
|
|
|
9
9
|
import { transactionsUnavailableFailure } from "../transactions/transaction-plan-validator.js";
|
|
10
10
|
import { CONTRACT_ROUTE, GET_ONLY, isProtocolPath, operationRouteSegments, POST_ONLY, readRequest, TRANSACTIONS_ROUTE, } from "./http-request.reader.js";
|
|
11
11
|
import { encodeOperationResponse, } from "./http-response.encoder.js";
|
|
12
|
-
import { transportFailure } from "./protocol-failure.js";
|
|
13
|
-
|
|
14
|
-
* An operation route `/_resources/<resource>/<family>/<variant>` under one
|
|
15
|
-
* client protocol identity: version → hash → Resource → operation. A Resource
|
|
16
|
-
* key is matched exactly (§12.6) and only as the ClientContract's own key; a
|
|
17
|
-
* family/variant pair only when it is a standard operation the Resource
|
|
18
|
-
* advertises, read through `isOperationVariantAvailable` alone (§4).
|
|
19
|
-
*/
|
|
12
|
+
import { identityHeadersMissing, transportFailure, } from "./protocol-failure.js";
|
|
13
|
+
import { missingIdentityHeaders } from "./protocol-headers.js";
|
|
20
14
|
export function resolveOperationRoute(contracts, protocol, route) {
|
|
21
15
|
const identityFailure = protocolIdentityFailure(contracts, protocol);
|
|
22
16
|
if (identityFailure !== undefined) {
|
|
@@ -40,33 +34,20 @@ export function resolveOperationRoute(contracts, protocol, route) {
|
|
|
40
34
|
}
|
|
41
35
|
return { ok: true, operation };
|
|
42
36
|
}
|
|
43
|
-
/**
|
|
44
|
-
* The `/_transactions` route under one client protocol identity: version →
|
|
45
|
-
* hash → the ClientContract's transaction capability. The route exists only
|
|
46
|
-
* when that capability is `"interactive"` (A4) — tested against the value
|
|
47
|
-
* itself, as `protocol.surface()` tests it — and is otherwise absent with
|
|
48
|
-
* core's own refusal of a plan (`A2002`, `V1006` at `["operations"]`, M17).
|
|
49
|
-
* `undefined` when the route is there.
|
|
50
|
-
*/
|
|
51
37
|
export function transactionsRouteFailure(contracts, protocol) {
|
|
52
38
|
return (protocolIdentityFailure(contracts, protocol) ??
|
|
53
39
|
(contracts.client.transactions === "interactive"
|
|
54
40
|
? undefined
|
|
55
41
|
: transactionsUnavailableFailure()));
|
|
56
42
|
}
|
|
57
|
-
/**
|
|
58
|
-
* An operation route's checks before its body (decoder steps 2–4): the path
|
|
59
|
-
* (`A2000` unless `/_resources/<r>/<f>/<v>`, each segment decodable and none
|
|
60
|
-
* empty), the identity headers' presence (`A2000`), then
|
|
61
|
-
* {@link resolveOperationRoute} — version, hash, Resource, operation — and the
|
|
62
|
-
* method (`A2012`, `Allow: POST`). One sequence for both route styles (Q12):
|
|
63
|
-
* `decodeOperation` reads the body only after it passes, `answerAbsent` never.
|
|
64
|
-
*/
|
|
65
43
|
export function checkOperationRoute(contracts, read) {
|
|
66
44
|
const segments = operationRouteSegments(read.request.path);
|
|
67
|
-
if (segments === undefined
|
|
45
|
+
if (segments === undefined) {
|
|
68
46
|
return refusedWith(transportFailure("A2000", read.requestId));
|
|
69
47
|
}
|
|
48
|
+
if (read.protocol === undefined) {
|
|
49
|
+
return refusedWith(identityFailureOf(read));
|
|
50
|
+
}
|
|
70
51
|
const route = resolveOperationRoute(contracts, read.protocol, segments);
|
|
71
52
|
if (!route.ok) {
|
|
72
53
|
return refusedWith(encodeOperationResponse(route.failure, read.requestId));
|
|
@@ -80,16 +61,13 @@ export function checkOperationRoute(contracts, read) {
|
|
|
80
61
|
}
|
|
81
62
|
: refusedWith(transportFailure("A2012", read.requestId, POST_ONLY));
|
|
82
63
|
}
|
|
83
|
-
/**
|
|
84
|
-
* `/_transactions`' checks before its body: the path (`A2000` unless exactly
|
|
85
|
-
* `/_transactions`), the identity headers' presence (`A2000`), then
|
|
86
|
-
* {@link transactionsRouteFailure} — version, hash, `"interactive"` — and the
|
|
87
|
-
* method (`A2012`, `Allow: POST`).
|
|
88
|
-
*/
|
|
89
64
|
export function checkTransactionsRoute(contracts, read) {
|
|
90
|
-
if (read.request.path !== TRANSACTIONS_ROUTE
|
|
65
|
+
if (read.request.path !== TRANSACTIONS_ROUTE) {
|
|
91
66
|
return refusedWith(transportFailure("A2000", read.requestId));
|
|
92
67
|
}
|
|
68
|
+
if (read.protocol === undefined) {
|
|
69
|
+
return refusedWith(identityFailureOf(read));
|
|
70
|
+
}
|
|
93
71
|
const absent = transactionsRouteFailure(contracts, read.protocol);
|
|
94
72
|
if (absent !== undefined) {
|
|
95
73
|
return refusedWith(encodeOperationResponse(absent, read.requestId));
|
|
@@ -98,33 +76,12 @@ export function checkTransactionsRoute(contracts, read) {
|
|
|
98
76
|
? { ok: true, protocol: read.protocol }
|
|
99
77
|
: refusedWith(transportFailure("A2012", read.requestId, POST_ONLY));
|
|
100
78
|
}
|
|
79
|
+
function identityFailureOf(read) {
|
|
80
|
+
return identityHeadersMissing(missingIdentityHeaders(read.headers), read.requestId);
|
|
81
|
+
}
|
|
101
82
|
function refusedWith(response) {
|
|
102
83
|
return { ok: false, response };
|
|
103
84
|
}
|
|
104
|
-
/**
|
|
105
|
-
* `protocol.answerAbsent(request)` (§5.9, Q2's O3; S10) — the fallback a host
|
|
106
|
-
* that mounts one route per `protocol.surface()` entry mounts for every path
|
|
107
|
-
* it did not. Never a catch-all into framework handling: it reads no body,
|
|
108
|
-
* runs no pipeline, calls no Adapter, and takes only the compiled Contracts.
|
|
109
|
-
* In order:
|
|
110
|
-
*
|
|
111
|
-
* 1. a value that is not an `AvProtocolRequest` → `400 A2000`, minted id;
|
|
112
|
-
* 2. a path outside the protocol's underscore namespace (§12.1) →
|
|
113
|
-
* `undefined`: the host answers it itself — `/favicon.ico` is not the
|
|
114
|
-
* framework's (the architect, 2026-10-05);
|
|
115
|
-
* 3. `/_contract`: any method but `GET` → `405 A2012` with `Allow: GET`; no
|
|
116
|
-
* identity header is read (§12.4);
|
|
117
|
-
* 4. `/_transactions`, or else an operation route: exactly what the catch-all
|
|
118
|
-
* host's decoder answers before its body — path syntax and identity
|
|
119
|
-
* presence (`A2000`), version (`A2006`), hash (`A2005`), Resource
|
|
120
|
-
* (`A2001`), operation or the `"none"` capability (`A2002`), method
|
|
121
|
-
* (`A2012`, `Allow: POST`) — each core's own envelope;
|
|
122
|
-
* 5. a route that EXISTS, reached with its own method — the host mounted
|
|
123
|
-
* only part of the surface — → `500 A3000`: a host wiring bug made
|
|
124
|
-
* visible, never a silent 404 (the architect, 2026-10-05).
|
|
125
|
-
*
|
|
126
|
-
* Every framework answer echoes `Aventara-Request-Id` (Q13).
|
|
127
|
-
*/
|
|
128
85
|
export function answerAbsentRequest(contracts, received) {
|
|
129
86
|
const read = readRequest(received);
|
|
130
87
|
if (!read.ok) {
|
|
@@ -144,7 +101,6 @@ export function answerAbsentRequest(contracts, received) {
|
|
|
144
101
|
: checkOperationRoute(contracts, read);
|
|
145
102
|
return route.ok ? unmounted(read.requestId) : route.response;
|
|
146
103
|
}
|
|
147
|
-
/** A mounted route's request at the fallback: the framework failed to route it. */
|
|
148
104
|
function unmounted(requestId) {
|
|
149
105
|
return encodeOperationResponse(operationFailure("A3000", { message: INTERNAL_FAILURE_MESSAGE }), requestId);
|
|
150
106
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The members of `AvProtocol`, as this module's own named exports. The entry
|
|
3
|
-
* (`./index.ts`) publishes this module whole as the namespace `AvProtocol
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* (`./index.ts`) publishes this module whole as the namespace `AvProtocol`: the
|
|
4
|
+
* language freezes a module namespace, and a bundler drops the members a consumer
|
|
5
|
+
* does not read.
|
|
6
6
|
*
|
|
7
|
-
* Re-exports only — no logic lives here. Every member is pinned, by name and
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Re-exports only — no logic lives here. Every member is pinned, by name and by
|
|
8
|
+
* type, in `./index.test-d.ts`, whose exact `keyof` set fails when a member is
|
|
9
|
+
* added or removed without a row.
|
|
10
10
|
*/
|
|
11
11
|
export { normalizeEntrypoint } from "../config/entrypoint.normalizer.js";
|
|
12
12
|
export { scalarFormats } from "../contracts/wire-scalar-format.js";
|
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The members of `AvProtocol`, as this module's own named exports. The entry
|
|
3
|
-
* (`./index.ts`) publishes this module whole as the namespace `AvProtocol`
|
|
4
|
-
* (P5): the language freezes a module namespace, and a bundler drops the
|
|
5
|
-
* members a consumer does not read.
|
|
6
|
-
*
|
|
7
|
-
* Re-exports only — no logic lives here. Every member is pinned, by name and
|
|
8
|
-
* by type, in `./index.test-d.ts`, whose exact `keyof` set fails when a member
|
|
9
|
-
* is added or removed without a row.
|
|
10
|
-
*/
|
|
11
1
|
export { normalizeEntrypoint } from "../config/entrypoint.normalizer.js";
|
|
12
2
|
export { scalarFormats } from "../contracts/wire-scalar-format.js";
|
|
13
3
|
export { httpStatus } from "./http-status.table.js";
|
|
@@ -2,10 +2,9 @@ import type { ClientContract } from "../contracts/contract.js";
|
|
|
2
2
|
import type { AvProtocolRequest } from "./http-request.decoder.js";
|
|
3
3
|
import type { AvProtocolResponse } from "./http-response.encoder.js";
|
|
4
4
|
/**
|
|
5
|
-
* The served ClientContract, written once per binding: its JCS-canonical
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* it (M22).
|
|
5
|
+
* The served ClientContract, written once per binding: its JCS-canonical bytes and
|
|
6
|
+
* its entity tag, the quoted contract hash. The bytes include `protocol.hash`,
|
|
7
|
+
* although the hash's own input excludes it.
|
|
9
8
|
*/
|
|
10
9
|
export type ContractDocument = {
|
|
11
10
|
readonly body: string;
|
|
@@ -14,22 +13,20 @@ export type ContractDocument = {
|
|
|
14
13
|
/** The bound ClientContract as the document `_contract` serves. */
|
|
15
14
|
export declare function contractDocumentOf(client: ClientContract): ContractDocument;
|
|
16
15
|
/**
|
|
17
|
-
* `protocol.encodeContract(request)
|
|
18
|
-
*
|
|
16
|
+
* `protocol.encodeContract(request)`: the `GET <entrypoint>/_contract` responder.
|
|
17
|
+
* In order:
|
|
19
18
|
*
|
|
20
|
-
* 1. a value that is not an `AvProtocolRequest` → `400 A2000` under a minted
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* cannot read, as a decoder answers one);
|
|
19
|
+
* 1. a value that is not an `AvProtocolRequest` → `400 A2000` under a minted id
|
|
20
|
+
* (as the decoders answer it);
|
|
21
|
+
* 3. a path other than `/_contract` → `400 A2000` (path syntax this route cannot
|
|
22
|
+
* read, as a decoder answers one);
|
|
25
23
|
* 4. a method other than `GET` → `405 A2012` with `Allow: GET`;
|
|
26
|
-
* 5. `If-None-Match` matching the document's entity tag — RFC 9110 §13.1.2's
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
24
|
+
* 5. `If-None-Match` matching the document's entity tag — RFC 9110 §13.1.2's weak
|
|
25
|
+
* comparison, over a comma-separated list, or `*` — → `304 Not Modified`, no
|
|
26
|
+
* body, still carrying `ETag` and `Cache-Control`; otherwise `200` with the
|
|
27
|
+
* canonical bytes, `ETag`, `Cache-Control: no-cache` and `Content-Type:
|
|
28
|
+
* application/json`.
|
|
31
29
|
*
|
|
32
|
-
* No identity header is required or read
|
|
33
|
-
* discovery mechanism), and the body is never read.
|
|
30
|
+
* No identity header is required or read, and the body is never read.
|
|
34
31
|
*/
|
|
35
32
|
export declare function encodeContractRequest(document: ContractDocument, received: AvProtocolRequest): AvProtocolResponse;
|
|
@@ -2,32 +2,12 @@ import { canonicalizeContract } from "../contracts/contract-canonicalizer.js";
|
|
|
2
2
|
import { CONTRACT_ROUTE, GET_ONLY, readRequest, } from "./http-request.reader.js";
|
|
3
3
|
import { transportFailure } from "./protocol-failure.js";
|
|
4
4
|
import { headers, headerValue } from "./protocol-headers.js";
|
|
5
|
-
/** The bound ClientContract as the document `_contract` serves. */
|
|
6
5
|
export function contractDocumentOf(client) {
|
|
7
6
|
return Object.freeze({
|
|
8
7
|
body: canonicalizeContract(client),
|
|
9
8
|
etag: `"${client.protocol.hash}"`,
|
|
10
9
|
});
|
|
11
10
|
}
|
|
12
|
-
/**
|
|
13
|
-
* `protocol.encodeContract(request)` (§5.9, Q8; S11): the `GET
|
|
14
|
-
* <entrypoint>/_contract` responder. In order:
|
|
15
|
-
*
|
|
16
|
-
* 1. a value that is not an `AvProtocolRequest` → `400 A2000` under a minted
|
|
17
|
-
* id (as the decoders answer it);
|
|
18
|
-
* 2. the request id: `Aventara-Request-Id` adopted per Q13, else minted;
|
|
19
|
-
* 3. a path other than `/_contract` → `400 A2000` (path syntax this route
|
|
20
|
-
* cannot read, as a decoder answers one);
|
|
21
|
-
* 4. a method other than `GET` → `405 A2012` with `Allow: GET`;
|
|
22
|
-
* 5. `If-None-Match` matching the document's entity tag — RFC 9110 §13.1.2's
|
|
23
|
-
* weak comparison, over a comma-separated list, or `*` — → `304 Not
|
|
24
|
-
* Modified`, no body, still carrying `ETag` and `Cache-Control` (§15.4.5);
|
|
25
|
-
* otherwise `200` with the canonical bytes, `ETag`, `Cache-Control:
|
|
26
|
-
* no-cache` and `Content-Type: application/json`.
|
|
27
|
-
*
|
|
28
|
-
* No identity header is required or read (§12.4: the contract GET is the
|
|
29
|
-
* discovery mechanism), and the body is never read.
|
|
30
|
-
*/
|
|
31
11
|
export function encodeContractRequest(document, received) {
|
|
32
12
|
const read = readRequest(received);
|
|
33
13
|
if (!read.ok) {
|
|
@@ -53,20 +33,7 @@ export function encodeContractRequest(document, received) {
|
|
|
53
33
|
body: document.body,
|
|
54
34
|
};
|
|
55
35
|
}
|
|
56
|
-
/**
|
|
57
|
-
* One member of an `If-None-Match` list (RFC 9110 §5.6.1, §8.8.3): optional
|
|
58
|
-
* whitespace, an optional entity tag — `W/` optional, then an opaque tag of
|
|
59
|
-
* `etagc` between quotes — optional whitespace, then a comma or the end. An
|
|
60
|
-
* empty member is allowed, as the list rule allows it.
|
|
61
|
-
*/
|
|
62
36
|
const IF_NONE_MATCH_MEMBER = /[ \t]*(?:(?:W\/)?("[\x21\x23-\x7e\x80-\xff]*"))?[ \t]*(,|$)/y;
|
|
63
|
-
/**
|
|
64
|
-
* Whether `If-None-Match` names the current representation: `*`, or a list
|
|
65
|
-
* holding an entity tag whose opaque tag equals `etag`'s — the WEAK
|
|
66
|
-
* comparison (RFC 9110 §8.8.3.2), which `If-None-Match` uses (§13.1.2), so a
|
|
67
|
-
* `W/` prefix does not matter. A value that is not a well-formed list matches
|
|
68
|
-
* nothing; an absent header matches nothing.
|
|
69
|
-
*/
|
|
70
37
|
function noneMatchHolds(value, etag) {
|
|
71
38
|
if (value === undefined) {
|
|
72
39
|
return false;
|
|
@@ -3,20 +3,19 @@ import type { ClientOperationRequest } from "../runtime/operation-request.js";
|
|
|
3
3
|
import type { ClientTransactionRequest } from "../transactions/transaction-request.js";
|
|
4
4
|
import type { AvProtocolResponse } from "./http-response.encoder.js";
|
|
5
5
|
/**
|
|
6
|
-
* One HTTP request as a host hands it to the protocol: what the decoders
|
|
7
|
-
*
|
|
8
|
-
* row of the fixture corpus carries as its `request`.
|
|
6
|
+
* One HTTP request as a host hands it to the protocol: what the decoders read, and
|
|
7
|
+
* what each row of the fixture corpus carries as its `request`.
|
|
9
8
|
*
|
|
10
|
-
* Host-independent by construction: no host's request object, only the four
|
|
11
|
-
*
|
|
9
|
+
* Host-independent by construction: no host's request object, only the four facts
|
|
10
|
+
* every host can supply.
|
|
12
11
|
*/
|
|
13
12
|
export type AvProtocolRequest = {
|
|
14
13
|
/** The HTTP method as received. Anything but `POST` on a framework route is `A2012`. */
|
|
15
14
|
readonly method: string;
|
|
16
15
|
/**
|
|
17
|
-
* The request path RELATIVE to the configured entrypoint, as received (not
|
|
18
|
-
*
|
|
19
|
-
*
|
|
16
|
+
* The request path RELATIVE to the configured entrypoint, as received (not yet
|
|
17
|
+
* URL-decoded): `/_resources/users/find/many`, `/_transactions`, `/_contract` —
|
|
18
|
+
* the space `protocol.surface()`'s paths live in.
|
|
20
19
|
*/
|
|
21
20
|
readonly path: string;
|
|
22
21
|
/**
|
|
@@ -26,16 +25,15 @@ export type AvProtocolRequest = {
|
|
|
26
25
|
*/
|
|
27
26
|
readonly headers: Readonly<Record<string, string | readonly string[] | undefined>>;
|
|
28
27
|
/**
|
|
29
|
-
* The body, in one of two explicitly named arms
|
|
30
|
-
*
|
|
28
|
+
* The body, in one of two explicitly named arms — the decoder never guesses which
|
|
29
|
+
* one it was given:
|
|
31
30
|
*
|
|
32
|
-
* - `raw`: the decoded body
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* decoder ran; the decoder still refuses a non-object shape (`A2000`).
|
|
31
|
+
* - `raw`: the decoded body as received, bytes or text. The decoder enforces all
|
|
32
|
+
* of it: the size limit (`A2010`), the content type (`A2011`), JSON syntax and
|
|
33
|
+
* the body's shape (`A2000`). An absent body is zero bytes (`""`).
|
|
34
|
+
* - `parsed`: a value the host's own JSON parser already produced. Size, content
|
|
35
|
+
* type and JSON syntax were the host's to enforce before the decoder ran; the
|
|
36
|
+
* decoder still refuses a non-object shape (`A2000`).
|
|
39
37
|
*/
|
|
40
38
|
readonly body: {
|
|
41
39
|
readonly kind: "raw";
|
|
@@ -46,10 +44,10 @@ export type AvProtocolRequest = {
|
|
|
46
44
|
};
|
|
47
45
|
};
|
|
48
46
|
/**
|
|
49
|
-
* What a decoder answers
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
47
|
+
* What a decoder answers: the request core executes, or the framework response
|
|
48
|
+
* that answers the HTTP request instead — and, either way, the request id it runs
|
|
49
|
+
* or is answered under, which the host passes to the encoder:
|
|
50
|
+
* `protocol.encodeOperation(response, decoded.requestId)`.
|
|
53
51
|
*/
|
|
54
52
|
type Decoded<Request> = {
|
|
55
53
|
readonly ok: true;
|
|
@@ -69,55 +67,51 @@ export type DecodedOperationRequest = Extract<DecodedOperation, {
|
|
|
69
67
|
/** What `protocol.decodeTransaction` answers. */
|
|
70
68
|
export type DecodedTransaction = Decoded<ClientTransactionRequest>;
|
|
71
69
|
/**
|
|
72
|
-
* `protocol.decodeOperation(request)` — one operation route's HTTP request as
|
|
73
|
-
*
|
|
70
|
+
* `protocol.decodeOperation(request)` — one operation route's HTTP request as the
|
|
71
|
+
* client-origin request `framework.execute` takes, or its answer.
|
|
74
72
|
*
|
|
75
|
-
* Total over ANY value
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
73
|
+
* Total over ANY value: a value that is not an `AvProtocolRequest` at all — not an
|
|
74
|
+
* object, a missing or mistyped member, a member behind a getter or trap that
|
|
75
|
+
* throws — is malformed, `A2000` under a minted id, before anything else is read;
|
|
76
|
+
* nothing it carries is believed. Otherwise, in order, each step answering before
|
|
77
|
+
* the next runs:
|
|
80
78
|
*
|
|
81
|
-
* 1. the request id: `Aventara-Request-Id` adopted when it is 1–128 visible
|
|
82
|
-
*
|
|
83
|
-
* refusal (Q13, Q10);
|
|
79
|
+
* 1. the request id: `Aventara-Request-Id` adopted when it is 1–128 visible ASCII
|
|
80
|
+
* characters, otherwise minted by core's one helper — never a refusal;
|
|
84
81
|
* 2. the path: `/_resources/<resource>/<family>/<variant>`, each segment
|
|
85
82
|
* URL-decoded once and none empty, or `A2000`;
|
|
86
|
-
* 3. identity and absence, with no body read: a request missing either
|
|
87
|
-
*
|
|
88
|
-
* the
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* host's by construction: a parser that turns an absent body into `{}`
|
|
104
|
-
* makes absence indistinguishable from `{}`, which is valid.
|
|
83
|
+
* 3. identity and absence, with no body read: a request missing either identity
|
|
84
|
+
* header is malformed; then the protocol version (`A2006`), the ClientContract
|
|
85
|
+
* hash (`A2005`), the Resource (`A2001`) and the operation (`A2002`), each
|
|
86
|
+
* core's own envelope — so an unadvertised triple never reaches `execute`, in
|
|
87
|
+
* either route style;
|
|
88
|
+
* 4. the method: anything but `POST` is `A2012`, carrying `Allow: POST` (RFC 9110
|
|
89
|
+
* §15.5.6);
|
|
90
|
+
* 5. the body. On the RAW arm the decoder enforces all of it: the content type
|
|
91
|
+
* must be `application/json` (`A2011`; parameters are not read), the decoded
|
|
92
|
+
* byte count must not exceed the ClientContract's `limits.maxRequestBytes`, and
|
|
93
|
+
* the bytes must be UTF-8 JSON (`A2000`). On the PARSED arm the host's own
|
|
94
|
+
* parser already read the bytes, so size, content type and JSON syntax were the
|
|
95
|
+
* HOST's to enforce — answering its parser's failures with `protocol.failure` —
|
|
96
|
+
* and the decoder checks none of them. Both arms then refuse a body that is not
|
|
97
|
+
* a JSON object — an array, a scalar, `null`, or none (`A2000`). One gap is the
|
|
98
|
+
* host's by construction: a parser that turns an absent body into `{}` makes
|
|
99
|
+
* absence indistinguishable from `{}`, which is valid.
|
|
105
100
|
*
|
|
106
|
-
* The request is client origin, always
|
|
107
|
-
* application origin. `transport` carries `POST` and the request's headers
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
101
|
+
* The request is client origin, always: nothing the caller sends spells
|
|
102
|
+
* application origin. `transport` carries `POST` and the request's headers. The
|
|
103
|
+
* body is the operation's arguments verbatim: the decoder never reads into it, so
|
|
104
|
+
* routing metadata in it is core's to refuse, and a parsed body reaches core as
|
|
105
|
+
* the same object.
|
|
111
106
|
*/
|
|
112
107
|
export declare function decodeOperationRequest(contracts: FrameworkContracts, received: AvProtocolRequest): DecodedOperation;
|
|
113
108
|
/**
|
|
114
109
|
* `protocol.decodeTransaction(request)` — the `/_transactions` request as the
|
|
115
|
-
* client-origin plan request `framework.executeTransaction` takes, or its
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
* origin and cannot say otherwise. The body is the plan verbatim.
|
|
110
|
+
* client-origin plan request `framework.executeTransaction` takes, or its answer.
|
|
111
|
+
* The same steps as {@link decodeOperationRequest}, with the path exactly
|
|
112
|
+
* `/_transactions` and its absence being a ClientContract without interactive
|
|
113
|
+
* transactions. Every step of the plan then runs under ClientContract rules: the
|
|
114
|
+
* plan is client origin and cannot say otherwise. The body is the plan verbatim.
|
|
121
115
|
*/
|
|
122
116
|
export declare function decodeTransactionRequest(contracts: FrameworkContracts, received: AvProtocolRequest): DecodedTransaction;
|
|
123
117
|
export {};
|
|
@@ -2,47 +2,6 @@ import { checkOperationRoute, checkTransactionsRoute, } from "./absent-route.res
|
|
|
2
2
|
import { readRequest } from "./http-request.reader.js";
|
|
3
3
|
import { transportFailure, } from "./protocol-failure.js";
|
|
4
4
|
import { headerValue } from "./protocol-headers.js";
|
|
5
|
-
/**
|
|
6
|
-
* `protocol.decodeOperation(request)` — one operation route's HTTP request as
|
|
7
|
-
* the client-origin request `framework.execute` takes, or its answer.
|
|
8
|
-
*
|
|
9
|
-
* Total over ANY value (the director, 2026-10-05): a value that is not an
|
|
10
|
-
* `AvProtocolRequest` at all — not an object, a missing or mistyped member, a
|
|
11
|
-
* member behind a getter or trap that throws — is malformed, `A2000` under a
|
|
12
|
-
* minted id, before anything else is read; nothing it carries is believed.
|
|
13
|
-
* Otherwise, in order, each step answering before the next runs:
|
|
14
|
-
*
|
|
15
|
-
* 1. the request id: `Aventara-Request-Id` adopted when it is 1–128 visible
|
|
16
|
-
* ASCII characters, otherwise minted by core's one helper — never a
|
|
17
|
-
* refusal (Q13, Q10);
|
|
18
|
-
* 2. the path: `/_resources/<resource>/<family>/<variant>`, each segment
|
|
19
|
-
* URL-decoded once and none empty, or `A2000`;
|
|
20
|
-
* 3. identity and absence, with no body read: a request missing either
|
|
21
|
-
* identity header is malformed (`A2000`; the architect, 2026-10-05); then
|
|
22
|
-
* the protocol version (`A2006`), the ClientContract hash (`A2005`), the
|
|
23
|
-
* Resource (`A2001`) and the operation (`A2002`), each core's own
|
|
24
|
-
* envelope — so an unadvertised triple never reaches `execute`, in either
|
|
25
|
-
* route style (Q12, Q2's O3);
|
|
26
|
-
* 4. the method: anything but `POST` is `A2012`, carrying `Allow: POST`
|
|
27
|
-
* (RFC 9110 §15.5.6);
|
|
28
|
-
* 5. the body (Q11). On the RAW arm the decoder enforces all of it: the
|
|
29
|
-
* content type must be `application/json` (`A2011`; parameters are not
|
|
30
|
-
* read), the decoded byte count must not exceed the ClientContract's
|
|
31
|
-
* `limits.maxRequestBytes` (`A2010`, §20.1), and the bytes must be UTF-8
|
|
32
|
-
* JSON (`A2000`). On the PARSED arm the host's own parser already read the
|
|
33
|
-
* bytes, so size, content type and JSON syntax were the HOST's to enforce —
|
|
34
|
-
* answering its parser's failures with `protocol.failure` — and the decoder
|
|
35
|
-
* checks none of them. Both arms then refuse a body that is not a JSON
|
|
36
|
-
* object — an array, a scalar, `null`, or none (`A2000`). One gap is the
|
|
37
|
-
* host's by construction: a parser that turns an absent body into `{}`
|
|
38
|
-
* makes absence indistinguishable from `{}`, which is valid.
|
|
39
|
-
*
|
|
40
|
-
* The request is client origin, always (P4): nothing the caller sends spells
|
|
41
|
-
* application origin. `transport` carries `POST` and the request's headers
|
|
42
|
-
* (Q7). The body is the operation's arguments verbatim (§12.3): the decoder
|
|
43
|
-
* never reads into it, so routing metadata in it is core's to refuse (N16),
|
|
44
|
-
* and a parsed body reaches core as the same object.
|
|
45
|
-
*/
|
|
46
5
|
export function decodeOperationRequest(contracts, received) {
|
|
47
6
|
const read = readRequest(received);
|
|
48
7
|
if (!read.ok) {
|
|
@@ -69,15 +28,6 @@ export function decodeOperationRequest(contracts, received) {
|
|
|
69
28
|
}
|
|
70
29
|
: refused(body.response, read.requestId);
|
|
71
30
|
}
|
|
72
|
-
/**
|
|
73
|
-
* `protocol.decodeTransaction(request)` — the `/_transactions` request as the
|
|
74
|
-
* client-origin plan request `framework.executeTransaction` takes, or its
|
|
75
|
-
* answer. The same steps as {@link decodeOperationRequest}, with the path
|
|
76
|
-
* exactly `/_transactions` and its absence being a ClientContract without
|
|
77
|
-
* interactive transactions (`A2002` at `["operations"]`, M17). Every step of
|
|
78
|
-
* the plan then runs under ClientContract rules (R1): the plan is client
|
|
79
|
-
* origin and cannot say otherwise. The body is the plan verbatim.
|
|
80
|
-
*/
|
|
81
31
|
export function decodeTransactionRequest(contracts, received) {
|
|
82
32
|
const read = readRequest(received);
|
|
83
33
|
if (!read.ok) {
|
|
@@ -105,21 +55,15 @@ export function decodeTransactionRequest(contracts, received) {
|
|
|
105
55
|
function refused(response, requestId) {
|
|
106
56
|
return { ok: false, response, requestId };
|
|
107
57
|
}
|
|
108
|
-
/** Q7: every client request carries `POST` and its headers, one spelling. */
|
|
109
58
|
function transportOf(read) {
|
|
110
59
|
return { method: "POST", headers: read.headers };
|
|
111
60
|
}
|
|
112
|
-
/**
|
|
113
|
-
* Step 5, after the route checks passed: the body as a JSON object, or the
|
|
114
|
-
* answer refusing it. Only the raw arm is measured, typed and parsed (Q11).
|
|
115
|
-
*/
|
|
116
61
|
function readBody(read, contracts) {
|
|
117
62
|
const code = bodyValue(read.request.body, headerValue(read.headers, "content-type"), contracts.client.limits.maxRequestBytes);
|
|
118
63
|
return typeof code === "string"
|
|
119
64
|
? { ok: false, response: transportFailure(code, read.requestId) }
|
|
120
65
|
: { ok: true, value: code.value };
|
|
121
66
|
}
|
|
122
|
-
/** The body's JSON object, or the transport code that refuses it. */
|
|
123
67
|
function bodyValue(body, contentType, maxRequestBytes) {
|
|
124
68
|
if (body.kind === "parsed") {
|
|
125
69
|
return isJsonObject(body.value) ? { value: body.value } : "A2000";
|
|
@@ -143,18 +87,12 @@ function bodyValue(body, contentType, maxRequestBytes) {
|
|
|
143
87
|
}
|
|
144
88
|
return isJsonObject(value) ? { value } : "A2000";
|
|
145
89
|
}
|
|
146
|
-
/** `application/json`, in any case; parameters (`; charset=…`) are not read. */
|
|
147
90
|
function isJsonMediaType(contentType) {
|
|
148
91
|
return (contentType
|
|
149
92
|
?.split(";")[0]
|
|
150
93
|
?.replace(/^[ \t]+|[ \t]+$/g, "")
|
|
151
94
|
.toLowerCase() === "application/json");
|
|
152
95
|
}
|
|
153
|
-
/**
|
|
154
|
-
* An object that is not a list (§12.6: a JSON array, scalar, `null` or absent
|
|
155
|
-
* body is invalid). Read without touching the value's own members, so a
|
|
156
|
-
* parsed body is never traversed; a revoked proxy is no object.
|
|
157
|
-
*/
|
|
158
96
|
function isJsonObject(value) {
|
|
159
97
|
try {
|
|
160
98
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -2,27 +2,27 @@ import type { ClientProtocolIdentity } from "../runtime/operation-request.js";
|
|
|
2
2
|
import type { OperationRouteSegments } from "./absent-route.responder.js";
|
|
3
3
|
import type { AvProtocolRequest } from "./http-request.decoder.js";
|
|
4
4
|
import type { AvProtocolResponse } from "./http-response.encoder.js";
|
|
5
|
-
/** `POST <entrypoint>/_transactions
|
|
5
|
+
/** `POST <entrypoint>/_transactions`. */
|
|
6
6
|
export declare const TRANSACTIONS_ROUTE = "/_transactions";
|
|
7
|
-
/** `GET <entrypoint>/_contract
|
|
7
|
+
/** `GET <entrypoint>/_contract`. */
|
|
8
8
|
export declare const CONTRACT_ROUTE = "/_contract";
|
|
9
|
-
/** The methods an operation or `/_transactions` route accepts: `POST`, only
|
|
9
|
+
/** The methods an operation or `/_transactions` route accepts: `POST`, only. */
|
|
10
10
|
export declare const POST_ONLY: readonly string[];
|
|
11
|
-
/** The methods the contract route accepts: `GET`, only
|
|
11
|
+
/** The methods the contract route accepts: `GET`, only. */
|
|
12
12
|
export declare const GET_ONLY: readonly string[];
|
|
13
13
|
/**
|
|
14
14
|
* Whether a path lies in the protocol's namespace: underscore-prefixed paths
|
|
15
|
-
* beneath the entrypoint are reserved for framework facilities; every other
|
|
16
|
-
*
|
|
15
|
+
* beneath the entrypoint are reserved for framework facilities; every other path
|
|
16
|
+
* is the host's.
|
|
17
17
|
*/
|
|
18
18
|
export declare function isProtocolPath(path: string): boolean;
|
|
19
19
|
/** A request the protocol could read, with what every route reads off it first. */
|
|
20
20
|
export type ReadRequest = {
|
|
21
21
|
readonly ok: true;
|
|
22
22
|
readonly request: AvProtocolRequest;
|
|
23
|
-
/** Names lower-cased, repeated values joined
|
|
23
|
+
/** Names lower-cased, repeated values joined. */
|
|
24
24
|
readonly headers: Readonly<Record<string, string>>;
|
|
25
|
-
/** Adopted from `Aventara-Request-Id
|
|
25
|
+
/** Adopted from `Aventara-Request-Id`, else minted. */
|
|
26
26
|
readonly requestId: string;
|
|
27
27
|
/** `undefined` when either identity header is absent (`A2000`). */
|
|
28
28
|
readonly protocol: ClientProtocolIdentity | undefined;
|
|
@@ -34,13 +34,12 @@ export type UnreadableRequest = {
|
|
|
34
34
|
readonly requestId: string;
|
|
35
35
|
};
|
|
36
36
|
/**
|
|
37
|
-
* The host's value read as a request, or — when it is not an
|
|
38
|
-
*
|
|
39
|
-
* id (the director, 2026-10-05).
|
|
37
|
+
* The host's value read as a request, or — when it is not an `AvProtocolRequest`
|
|
38
|
+
* at all — the `400 A2000` answering it, under a minted id.
|
|
40
39
|
*/
|
|
41
40
|
export declare function readRequest(received: unknown): ReadRequest | UnreadableRequest;
|
|
42
41
|
/**
|
|
43
|
-
* The three segments of an operation route, each URL-decoded and none empty
|
|
44
|
-
*
|
|
42
|
+
* The three segments of an operation route, each URL-decoded and none empty;
|
|
43
|
+
* `undefined` when it is not one.
|
|
45
44
|
*/
|
|
46
45
|
export declare function operationRouteSegments(path: string): OperationRouteSegments | undefined;
|