@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 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 || read.protocol === 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 || read.protocol === undefined) {
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
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aventara/core",
3
- "version": "0.1.0-pilot.0",
3
+ "version": "0.1.0-pilot.1",
4
4
  "license": "SEE LICENSE IN LICENSE",
5
5
  "description": "Core runtime, contract compiler, and extension APIs for Aventara.",
6
6
  "type": "module",