@aventara/core 0.1.0-pilot.0 → 0.1.0-pilot.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/protocol/absent-route.responder.js +14 -3
- package/dist/protocol/protocol-failure.d.ts +8 -0
- package/dist/protocol/protocol-failure.js +12 -0
- package/dist/protocol/protocol-headers.d.ts +8 -0
- package/dist/protocol/protocol-headers.js +8 -0
- package/package.json +1 -1
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.
|
|
@@ -9,7 +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";
|
|
12
|
+
import { identityHeadersMissing, transportFailure, } from "./protocol-failure.js";
|
|
13
|
+
import { missingIdentityHeaders } from "./protocol-headers.js";
|
|
13
14
|
/**
|
|
14
15
|
* An operation route `/_resources/<resource>/<family>/<variant>` under one
|
|
15
16
|
* client protocol identity: version → hash → Resource → operation. A Resource
|
|
@@ -64,9 +65,12 @@ export function transactionsRouteFailure(contracts, protocol) {
|
|
|
64
65
|
*/
|
|
65
66
|
export function checkOperationRoute(contracts, read) {
|
|
66
67
|
const segments = operationRouteSegments(read.request.path);
|
|
67
|
-
if (segments === undefined
|
|
68
|
+
if (segments === undefined) {
|
|
68
69
|
return refusedWith(transportFailure("A2000", read.requestId));
|
|
69
70
|
}
|
|
71
|
+
if (read.protocol === undefined) {
|
|
72
|
+
return refusedWith(identityFailureOf(read));
|
|
73
|
+
}
|
|
70
74
|
const route = resolveOperationRoute(contracts, read.protocol, segments);
|
|
71
75
|
if (!route.ok) {
|
|
72
76
|
return refusedWith(encodeOperationResponse(route.failure, read.requestId));
|
|
@@ -87,9 +91,12 @@ export function checkOperationRoute(contracts, read) {
|
|
|
87
91
|
* method (`A2012`, `Allow: POST`).
|
|
88
92
|
*/
|
|
89
93
|
export function checkTransactionsRoute(contracts, read) {
|
|
90
|
-
if (read.request.path !== TRANSACTIONS_ROUTE
|
|
94
|
+
if (read.request.path !== TRANSACTIONS_ROUTE) {
|
|
91
95
|
return refusedWith(transportFailure("A2000", read.requestId));
|
|
92
96
|
}
|
|
97
|
+
if (read.protocol === undefined) {
|
|
98
|
+
return refusedWith(identityFailureOf(read));
|
|
99
|
+
}
|
|
93
100
|
const absent = transactionsRouteFailure(contracts, read.protocol);
|
|
94
101
|
if (absent !== undefined) {
|
|
95
102
|
return refusedWith(encodeOperationResponse(absent, read.requestId));
|
|
@@ -98,6 +105,10 @@ export function checkTransactionsRoute(contracts, read) {
|
|
|
98
105
|
? { ok: true, protocol: read.protocol }
|
|
99
106
|
: refusedWith(transportFailure("A2012", read.requestId, POST_ONLY));
|
|
100
107
|
}
|
|
108
|
+
/** The `A2000` for a request whose identity headers are not all there, naming the missing ones (pilot.1). */
|
|
109
|
+
function identityFailureOf(read) {
|
|
110
|
+
return identityHeadersMissing(missingIdentityHeaders(read.headers), read.requestId);
|
|
111
|
+
}
|
|
101
112
|
function refusedWith(response) {
|
|
102
113
|
return { ok: false, response };
|
|
103
114
|
}
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { type AvProtocolResponse } from "./http-response.encoder.js";
|
|
2
|
+
import type { IdentityHeaderName } from "./protocol-headers.js";
|
|
2
3
|
/**
|
|
3
4
|
* The four transport codes (§13.2): failures of the HTTP request itself, which
|
|
4
5
|
* the protocol answers before any operation exists, so no execution path
|
|
5
6
|
* produces them (N4). Module type — not an entry export.
|
|
6
7
|
*/
|
|
7
8
|
export type TransportFailureCode = "A2000" | "A2010" | "A2011" | "A2012";
|
|
9
|
+
/**
|
|
10
|
+
* pilot.1 — the `400 A2000` answering a request that did not send an identity
|
|
11
|
+
* header (§12.4; the architect, 2026-10-05): its message names the missing
|
|
12
|
+
* header or headers, and echoes nothing the request sent (§13.5) — so a
|
|
13
|
+
* developer calling the API by hand learns what to add.
|
|
14
|
+
*/
|
|
15
|
+
export declare function identityHeadersMissing(missing: readonly IdentityHeaderName[], requestId: string): AvProtocolResponse;
|
|
8
16
|
/**
|
|
9
17
|
* `protocol.failure(code, requestId?)` / `protocol.failure("A2012", requestId,
|
|
10
18
|
* allow)` (§5.9; a bound member, ruled 2026-10-05): a transport code as the
|
|
@@ -12,6 +12,18 @@ const TRANSPORT_FAILURE_MESSAGES = Object.freeze({
|
|
|
12
12
|
A2011: "The request content type is not supported; send application/json.",
|
|
13
13
|
A2012: "The HTTP method is not allowed on this framework route.",
|
|
14
14
|
});
|
|
15
|
+
/**
|
|
16
|
+
* pilot.1 — the `400 A2000` answering a request that did not send an identity
|
|
17
|
+
* header (§12.4; the architect, 2026-10-05): its message names the missing
|
|
18
|
+
* header or headers, and echoes nothing the request sent (§13.5) — so a
|
|
19
|
+
* developer calling the API by hand learns what to add.
|
|
20
|
+
*/
|
|
21
|
+
export function identityHeadersMissing(missing, requestId) {
|
|
22
|
+
const named = missing.length === 1
|
|
23
|
+
? `The ${missing[0]} header is`
|
|
24
|
+
: `The ${missing.join(" and ")} headers are`;
|
|
25
|
+
return encodeOperationResponse(operationFailure("A2000", { message: `${named} missing.` }), requestIdOf({ requestId }));
|
|
26
|
+
}
|
|
15
27
|
/** An HTTP method as RFC 9110 §9.1 spells one: a `token` (§5.6.2). */
|
|
16
28
|
const METHOD_TOKEN = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
17
29
|
export function transportFailure(code, requestId, allow) {
|
|
@@ -56,6 +56,14 @@ export declare function headerValue(normalized: Readonly<Record<string, string>>
|
|
|
56
56
|
* Present includes empty: a header sent with no value was still sent.
|
|
57
57
|
*/
|
|
58
58
|
export declare function protocolIdentityOf(normalized: Readonly<Record<string, string>>): ClientProtocolIdentity | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* The identity headers (§12.4) a request did not send, in the order the
|
|
61
|
+
* framework sends them — what the `A2000` answering it names (pilot.1).
|
|
62
|
+
* Empty exactly when {@link protocolIdentityOf} reads an identity.
|
|
63
|
+
*/
|
|
64
|
+
export declare function missingIdentityHeaders(normalized: Readonly<Record<string, string>>): readonly IdentityHeaderName[];
|
|
65
|
+
/** The two identity headers' names, as the framework spells them. */
|
|
66
|
+
export type IdentityHeaderName = (typeof headers)["protocolVersion"] | (typeof headers)["contractHash"];
|
|
59
67
|
/**
|
|
60
68
|
* Q13 (the architect's, 2026-10-04): the request's `Aventara-Request-Id` when
|
|
61
69
|
* it is 1–128 characters of visible ASCII; otherwise `undefined` — the header
|
|
@@ -88,6 +88,14 @@ export function protocolIdentityOf(normalized) {
|
|
|
88
88
|
hash,
|
|
89
89
|
};
|
|
90
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* The identity headers (§12.4) a request did not send, in the order the
|
|
93
|
+
* framework sends them — what the `A2000` answering it names (pilot.1).
|
|
94
|
+
* Empty exactly when {@link protocolIdentityOf} reads an identity.
|
|
95
|
+
*/
|
|
96
|
+
export function missingIdentityHeaders(normalized) {
|
|
97
|
+
return [headers.protocolVersion, headers.contractHash].filter((name) => headerValue(normalized, name) === undefined);
|
|
98
|
+
}
|
|
91
99
|
/** Q13's adoptable correlation id: 1–128 characters of visible ASCII (`0x21`–`0x7E`). */
|
|
92
100
|
const ADOPTABLE_REQUEST_ID = /^[\x21-\x7e]{1,128}$/;
|
|
93
101
|
/**
|