@aventara/nest 0.1.0-pilot.1 → 0.1.0-pilot.3
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 +15 -0
- package/dist/aventara.module.d.ts +16 -17
- package/dist/aventara.module.js +8 -20
- package/dist/express-protocol.mounter.d.ts +14 -16
- package/dist/express-protocol.mounter.js +0 -13
- package/dist/fastify-protocol.mounter.d.ts +24 -29
- package/dist/fastify-protocol.mounter.js +0 -20
- package/dist/framework.token.d.ts +4 -5
- package/dist/framework.token.js +0 -8
- package/dist/host-bootstrap.validator.d.ts +1 -1
- package/dist/host-bootstrap.validator.js +0 -15
- package/dist/internal-failure.logger.d.ts +8 -0
- package/dist/internal-failure.logger.js +46 -0
- package/dist/node-exchange.translator.d.ts +8 -13
- package/dist/node-exchange.translator.js +0 -9
- package/dist/protocol-host.factory.d.ts +12 -15
- package/dist/protocol-host.factory.js +0 -32
- package/dist/request-body.reader.d.ts +15 -18
- package/dist/request-body.reader.js +0 -25
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -126,6 +126,21 @@ own when it mounts them, through Nest's `Logger` (context `AventaraModule`; `log
|
|
|
126
126
|
[Nest] LOG [AventaraModule] Aventara mounted 10 operations at /api (+ GET /api/_contract, POST /api/_transactions)
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
+
An internal failure (`A3000`–`A3004`, answered as a 500) is logged as one error line through Nest's `Logger`
|
|
130
|
+
(context `Aventara`): the code, the operation, the scope and the request id, and the thrown value's class name — never
|
|
131
|
+
its message, which can hold SQL or a connection string. The raw error still goes to your `diagnostics` sink, when you
|
|
132
|
+
configure one:
|
|
133
|
+
|
|
134
|
+
```text
|
|
135
|
+
[Nest] ERROR [Aventara] A3001 on Post.create.one (client scope, request 3f1c…): PrismaClientValidationError. The raw error goes to the framework's diagnostics sink.
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
For `A3004` — one of your pipes returned arguments the server contract refuses — the line also names the pipe, the
|
|
139
|
+
operation, the offending field and its V-code; the caller only ever sees the generic message.
|
|
140
|
+
|
|
141
|
+
The line is added when the module builds the framework from `config`; a framework you build yourself and pass as
|
|
142
|
+
`framework` keeps exactly the `diagnostics` sink you gave it.
|
|
143
|
+
|
|
129
144
|
The framework's validation is outermost:
|
|
130
145
|
|
|
131
146
|
| | Express | Fastify |
|
|
@@ -2,17 +2,16 @@ import { type AdapterModel, type AnyFrameworkConfig, type Framework, type Framew
|
|
|
2
2
|
import { type DynamicModule, type FactoryProvider, type ModuleMetadata, type NestModule, type OnModuleInit } from "@nestjs/common";
|
|
3
3
|
import { type PendingProtocolHost } from "./protocol-host.factory.js";
|
|
4
4
|
/**
|
|
5
|
-
* How the application's Framework enters Nest
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* How the application's Framework enters Nest: either the developer's own instance
|
|
6
|
+
* — they called `createFramework` — or the configuration the module hands to
|
|
7
|
+
* `createFramework` itself. Exactly one arm; the `?: never` halves make both a
|
|
8
|
+
* type error.
|
|
9
9
|
*
|
|
10
|
-
* The `config` arm is typed exactly as `createFramework`'s parameter
|
|
11
|
-
* (`Cfg & FrameworkConfig<M, Cfg>`; U12, architect 2026-10-05), so a
|
|
10
|
+
* The `config` arm is typed exactly as `createFramework`'s parameter, so a
|
|
12
11
|
* configuration annotated `FrameworkConfig<M>`, written inline, inferred or
|
|
13
|
-
* checked with `satisfies` is accepted — and checked against its own Adapter
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* checked with `satisfies` is accepted — and checked against its own Adapter model
|
|
13
|
+
* — wherever `createFramework` would accept it. The defaults are the untyped
|
|
14
|
+
* forms.
|
|
16
15
|
*/
|
|
17
16
|
export type AventaraModuleOptions<M extends AdapterModel = AdapterModel, Cfg = AnyFrameworkConfig> = {
|
|
18
17
|
readonly framework: Framework;
|
|
@@ -22,9 +21,9 @@ export type AventaraModuleOptions<M extends AdapterModel = AdapterModel, Cfg = A
|
|
|
22
21
|
readonly framework?: never;
|
|
23
22
|
};
|
|
24
23
|
/**
|
|
25
|
-
* `forRootAsync`'s options: the Nest idiom when what builds the Framework
|
|
26
|
-
*
|
|
27
|
-
*
|
|
24
|
+
* `forRootAsync`'s options: the Nest idiom when what builds the Framework (a
|
|
25
|
+
* Prisma client, a configuration service) is itself a provider. The factory
|
|
26
|
+
* resolves to either arm of {@link AventaraModuleOptions}.
|
|
28
27
|
*/
|
|
29
28
|
export type AventaraModuleAsyncOptions<M extends AdapterModel = AdapterModel, Cfg = AnyFrameworkConfig> = {
|
|
30
29
|
readonly imports?: ModuleMetadata["imports"];
|
|
@@ -32,16 +31,16 @@ export type AventaraModuleAsyncOptions<M extends AdapterModel = AdapterModel, Cf
|
|
|
32
31
|
readonly useFactory: (...deps: never[]) => AventaraModuleOptions<M, Cfg> | Promise<AventaraModuleOptions<M, Cfg>>;
|
|
33
32
|
};
|
|
34
33
|
/**
|
|
35
|
-
* The global module that hosts one Aventara Framework
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
34
|
+
* The global module that hosts one Aventara Framework, and mounts its protocol on
|
|
35
|
+
* the application's platform from `configure()`: after the developer's `app.use` /
|
|
36
|
+
* `enableCors`, before `MiddlewareConsumer` middleware and every controller, so
|
|
37
|
+
* the reserved `_` namespace cannot be shadowed.
|
|
39
38
|
*/
|
|
40
39
|
export declare class AventaraModule implements NestModule, OnModuleInit {
|
|
41
40
|
private readonly host;
|
|
42
41
|
constructor(host: PendingProtocolHost);
|
|
43
42
|
configure(): void;
|
|
44
|
-
/** An application context never receives a platform: refused here, at its init
|
|
43
|
+
/** An application context never receives a platform: refused here, at its init. */
|
|
45
44
|
onModuleInit(): void;
|
|
46
45
|
static forRoot<M extends AdapterModel, const Cfg = AnyFrameworkConfig>(options: AventaraModuleOptions<M, Cfg>): DynamicModule;
|
|
47
46
|
static forRootAsync<M extends AdapterModel, const Cfg = AnyFrameworkConfig>(options: AventaraModuleAsyncOptions<M, Cfg>): DynamicModule;
|
package/dist/aventara.module.js
CHANGED
|
@@ -3,32 +3,21 @@ import { Inject, Module, } from "@nestjs/common";
|
|
|
3
3
|
import { HttpAdapterHost } from "@nestjs/core";
|
|
4
4
|
import { AVENTARA_FRAMEWORK } from "./framework.token.js";
|
|
5
5
|
import { checkedOptions, recordMount } from "./host-bootstrap.validator.js";
|
|
6
|
+
import { withInternalFailureLog } from "./internal-failure.logger.js";
|
|
6
7
|
import { createPendingProtocolHost, } from "./protocol-host.factory.js";
|
|
7
|
-
/**
|
|
8
|
-
* One value per `forRoot`/`forRootAsync` call, carried in the module's
|
|
9
|
-
* metadata: under Nest's opt-in `deep-hash` module ids, two calls whose
|
|
10
|
-
* metadata serialized alike would collapse into one module and the second
|
|
11
|
-
* mount would vanish instead of being refused (Q18).
|
|
12
|
-
*/
|
|
13
8
|
const MOUNT = Symbol("AVENTARA_MOUNT");
|
|
14
9
|
let mounts = 0;
|
|
15
|
-
/** The bound protocol and its mounter for this application's platform. */
|
|
16
10
|
const PROTOCOL_HOST = Symbol("AVENTARA_PROTOCOL_HOST");
|
|
17
|
-
/**
|
|
18
|
-
* The Framework the options name: the developer's instance, or the one
|
|
19
|
-
* `createFramework` builds from the configuration — whose
|
|
20
|
-
* `FrameworkConstructionError` propagates unchanged (Q1).
|
|
21
|
-
*/
|
|
22
11
|
async function frameworkOf(options) {
|
|
23
12
|
const checked = checkedOptions(options);
|
|
24
|
-
|
|
13
|
+
if (checked.framework !== undefined) {
|
|
14
|
+
return checked.framework;
|
|
15
|
+
}
|
|
16
|
+
return createFramework({
|
|
17
|
+
...checked.config,
|
|
18
|
+
diagnostics: withInternalFailureLog(checked.config.diagnostics),
|
|
19
|
+
});
|
|
25
20
|
}
|
|
26
|
-
/**
|
|
27
|
-
* The global module that hosts one Aventara Framework (Q2, Q3), and mounts its
|
|
28
|
-
* protocol on the application's platform from `configure()` (Q5): after the
|
|
29
|
-
* developer's `app.use` / `enableCors`, before `MiddlewareConsumer` middleware
|
|
30
|
-
* and every controller, so the reserved `_` namespace cannot be shadowed.
|
|
31
|
-
*/
|
|
32
21
|
export class AventaraModule {
|
|
33
22
|
host;
|
|
34
23
|
constructor(host) {
|
|
@@ -37,7 +26,6 @@ export class AventaraModule {
|
|
|
37
26
|
configure() {
|
|
38
27
|
this.host.mount();
|
|
39
28
|
}
|
|
40
|
-
/** An application context never receives a platform: refused here, at its init (N1). */
|
|
41
29
|
onModuleInit() {
|
|
42
30
|
this.host.assertReady();
|
|
43
31
|
}
|
|
@@ -2,33 +2,31 @@ import type { AvProtocolBinding } from "@aventara/core/protocol";
|
|
|
2
2
|
import { type NodeIncoming, type NodeOutgoing } from "./node-exchange.translator.js";
|
|
3
3
|
import type { ProtocolHost } from "./protocol-host.factory.js";
|
|
4
4
|
/**
|
|
5
|
-
* The protocol on Nest's Express platform
|
|
5
|
+
* The protocol on Nest's Express platform.
|
|
6
6
|
*
|
|
7
|
-
* - **The pre-parser
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* - **The routes**, mounted from the module's `configure()
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
7
|
+
* - **The pre-parser**, installed while Nest instantiates providers — before
|
|
8
|
+
* Nest's own body parsers: a request under `<entrypoint>/_` has its body read
|
|
9
|
+
* raw, bounded and decoded, and kept for its route. Its stream is then finished,
|
|
10
|
+
* so Nest's parsers skip it; every other request keeps Nest's parsed body. It
|
|
11
|
+
* reads; it answers nothing.
|
|
12
|
+
* - **The routes**, mounted from the module's `configure()`: one platform route
|
|
13
|
+
* per `protocol.surface()` entry, each at its escaped literal path under the
|
|
14
|
+
* entrypoint, then one fallback — `protocol.answerAbsent`, the absence handler,
|
|
15
|
+
* not a catch-all: it executes nothing, and a path it does not own (`undefined`)
|
|
16
|
+
* goes on to Nest.
|
|
17
17
|
*
|
|
18
|
-
* Its whole job is translation: the Express request into an
|
|
19
|
-
*
|
|
20
|
-
* types are the slices below (P4: no `@types/node`, no `@types/express`).
|
|
18
|
+
* Its whole job is translation: the Express request into an `AvProtocolRequest`,
|
|
19
|
+
* the `AvProtocolResponse` back onto the wire.
|
|
21
20
|
*/
|
|
22
21
|
/** Express's request: Node's `IncomingMessage`, plus `originalUrl`. */
|
|
23
22
|
type ExpressRequest = NodeIncoming & {
|
|
24
23
|
readonly method: string;
|
|
25
24
|
readonly originalUrl: string;
|
|
26
25
|
};
|
|
27
|
-
/** Express's response, written through Node's own `writeHead
|
|
26
|
+
/** Express's response, written through Node's own `writeHead`. */
|
|
28
27
|
type ExpressResponse = NodeOutgoing;
|
|
29
28
|
/** Express passes `next`; Nest's handler type declares it as an optional `Function`. */
|
|
30
29
|
type ExpressHandler = (request: ExpressRequest, response: ExpressResponse, next?: unknown) => unknown;
|
|
31
|
-
/** The slice of Nest's `ExpressAdapter` this mounter registers on. */
|
|
32
30
|
export type ExpressPlatform = {
|
|
33
31
|
get(path: string, handler: ExpressHandler): unknown;
|
|
34
32
|
post(path: string, handler: ExpressHandler): unknown;
|
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
import { NO_BYTES, pathnameOf, protocolRequest, writeAnswer, } from "./node-exchange.translator.js";
|
|
2
2
|
import { PROTOCOL_ROUTES } from "./protocol-route.table.js";
|
|
3
3
|
import { readRequestBody, unreadBodyAnswer, } from "./request-body.reader.js";
|
|
4
|
-
/** The platform method that registers a route of each surface method. */
|
|
5
4
|
const REGISTER = { GET: "get", POST: "post" };
|
|
6
|
-
/** A request the pre-parser did not read: zero bytes. */
|
|
7
5
|
const NOTHING_READ = { kind: "read", content: NO_BYTES };
|
|
8
|
-
/**
|
|
9
|
-
* Installs the pre-parser on `platform` now, and returns the host whose
|
|
10
|
-
* `mount` registers the routes and the absence fallback under `entrypoint`.
|
|
11
|
-
*/
|
|
12
6
|
export function createExpressProtocolHost(platform, binding, entrypoint, maxRequestBytes) {
|
|
13
7
|
const bodies = new WeakMap();
|
|
14
8
|
const bodyOf = (request) => bodies.get(request) ?? NOTHING_READ;
|
|
@@ -45,21 +39,14 @@ export function createExpressProtocolHost(platform, binding, entrypoint, maxRequ
|
|
|
45
39
|
},
|
|
46
40
|
};
|
|
47
41
|
}
|
|
48
|
-
/** Hands the request on to whatever Express runs next — Nest's own routes. */
|
|
49
42
|
function handOn(next) {
|
|
50
43
|
if (typeof next === "function") {
|
|
51
44
|
next();
|
|
52
45
|
}
|
|
53
46
|
}
|
|
54
|
-
/**
|
|
55
|
-
* The route path as a literal for Express's router (`path-to-regexp` 8):
|
|
56
|
-
* every character it reads as syntax is escaped, so a Resource key never
|
|
57
|
-
* mounts a parameter or a wildcard (H11, DA-13).
|
|
58
|
-
*/
|
|
59
47
|
function literalPath(path) {
|
|
60
48
|
return path.replace(/[()[\]{}*+?!:\\]/g, "\\$&");
|
|
61
49
|
}
|
|
62
|
-
/** The Express request as the protocol reads it. */
|
|
63
50
|
function translated(request, entrypoint, content) {
|
|
64
51
|
return protocolRequest(request.method, request.originalUrl, request, entrypoint, content);
|
|
65
52
|
}
|
|
@@ -2,37 +2,33 @@ import type { AvProtocolBinding } from "@aventara/core/protocol";
|
|
|
2
2
|
import { type NodeIncoming, type NodeOutgoing, type PlatformHeaders } from "./node-exchange.translator.js";
|
|
3
3
|
import type { ProtocolHost } from "./protocol-host.factory.js";
|
|
4
4
|
/**
|
|
5
|
-
* The protocol on Nest's Fastify platform
|
|
6
|
-
*
|
|
5
|
+
* The protocol on Nest's Fastify platform, mounted from the module's
|
|
6
|
+
* `configure()`.
|
|
7
7
|
*
|
|
8
8
|
* - **One Fastify route per `protocol.surface()` entry**, at its literal path
|
|
9
|
-
* under the entrypoint, with Fastify's automatic `HEAD` route off
|
|
10
|
-
*
|
|
9
|
+
* under the entrypoint, with Fastify's automatic `HEAD` route off, so `HEAD
|
|
10
|
+
* /_contract` reaches the protocol's `405` as on Express.
|
|
11
11
|
* - **Each route is answered from its own `onRequest` hook** — Fastify's first
|
|
12
|
-
* step, before its content-type check and body parsing: the body is read raw
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* `Content-Type` it cannot parse) never run on a protocol route, so every
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* - **The absence fallback** is a root `onRequest` hook acting only on a
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* headers Fastify hooks already set (CORS) beneath the protocol's own —
|
|
33
|
-
* what Node's `writeHead` does on Express (director, 2026-10-05).
|
|
34
|
-
*
|
|
35
|
-
* Fastify's types are the slices below (P4).
|
|
12
|
+
* step, before its content-type check and body parsing: the body is read raw off
|
|
13
|
+
* Node's request by the bounded reader, the bound member answers, and the reply
|
|
14
|
+
* is hijacked. Fastify's parsers, `bodyLimit` and early `415` (on a
|
|
15
|
+
* `Content-Type` it cannot parse) never run on a protocol route, so every body
|
|
16
|
+
* failure is the protocol's own answer, as on Express. The route handler is
|
|
17
|
+
* never reached.
|
|
18
|
+
* - **The absence fallback** is a root `onRequest` hook acting only on a request
|
|
19
|
+
* Fastify matched to no route (`request.is404`) under `<entrypoint>/`:
|
|
20
|
+
* `protocol.answerAbsent`, and `undefined` hands the request on to Nest's own
|
|
21
|
+
* not-found handler. A not-found handler of our own could not hand back: inside
|
|
22
|
+
* one, Fastify answers its plain-text 404, not Nest's.
|
|
23
|
+
* - **Literal paths**: each route is registered `decodeURI`-ed, as find-my-way
|
|
24
|
+
* decodes a request path before matching it (`café`, `a[b]`). A path find-my-way
|
|
25
|
+
* cannot match as a literal — a `*` (its wildcard) or a reserved escape (it
|
|
26
|
+
* keeps those encoded, and never matches them against a static route) — is
|
|
27
|
+
* refused while Nest builds the application, naming the Resource keys; Express
|
|
28
|
+
* mounts them all.
|
|
29
|
+
* - **Writes** go through Node's response after `reply.hijack()`, with the headers
|
|
30
|
+
* Fastify hooks already set (CORS) beneath the protocol's own — what Node's
|
|
31
|
+
* `writeHead` does on Express.
|
|
36
32
|
*/
|
|
37
33
|
/** Fastify's request: Node's own under `raw`, and whether a route matched. */
|
|
38
34
|
type FastifyRequest = {
|
|
@@ -49,7 +45,6 @@ type FastifyReply = {
|
|
|
49
45
|
readonly raw: NodeOutgoing;
|
|
50
46
|
};
|
|
51
47
|
type OnRequestHook = (request: FastifyRequest, reply: FastifyReply) => Promise<FastifyReply | undefined>;
|
|
52
|
-
/** The slice of the Fastify instance (`httpAdapter.getInstance()`) this mounter registers on. */
|
|
53
48
|
export type FastifyPlatform = {
|
|
54
49
|
route(options: {
|
|
55
50
|
readonly method: string;
|
|
@@ -1,17 +1,7 @@
|
|
|
1
1
|
import { NO_BYTES, pathnameOf, protocolRequest, writeAnswer, } from "./node-exchange.translator.js";
|
|
2
2
|
import { PROTOCOL_ROUTES } from "./protocol-route.table.js";
|
|
3
3
|
import { readRequestBody, unreadBodyAnswer } from "./request-body.reader.js";
|
|
4
|
-
/**
|
|
5
|
-
* What find-my-way cannot match in a static route path: its wildcard, and the
|
|
6
|
-
* escapes of the characters `decodeURI` leaves encoded (: + # $ & , / ; = ? @).
|
|
7
|
-
*/
|
|
8
4
|
const UNMATCHABLE = /\*|%(?:3A|2B|23|24|26|2C|2F|3B|3D|3F|40)/i;
|
|
9
|
-
/**
|
|
10
|
-
* Refuses an entrypoint find-my-way cannot match literally — a `*`, its
|
|
11
|
-
* wildcard; every other character a canonical entrypoint may carry raw
|
|
12
|
-
* mounts (measured S6; a `:` is doubled) — then, naming them, the Resource
|
|
13
|
-
* keys whose routes it cannot match literally.
|
|
14
|
-
*/
|
|
15
5
|
function checkLiteralRoutes(binding, entrypoint) {
|
|
16
6
|
if (entrypoint.includes("*")) {
|
|
17
7
|
throw new Error(`AventaraModule cannot mount the entrypoint \`${entrypoint}\` as a literal path on Nest's Fastify platform: it may not carry \`*\`. Change the entrypoint, or host on Express.`);
|
|
@@ -26,10 +16,6 @@ function checkLiteralRoutes(binding, entrypoint) {
|
|
|
26
16
|
throw new Error(`AventaraModule cannot mount Resource ${refused.map((key) => `\`${key}\``).join(", ")} as literal routes on Nest's Fastify platform: a route path may not carry \`*\` or a reserved character (: + # $ & , / ; = ? @). Rename the Resource, or host on Express.`);
|
|
27
17
|
}
|
|
28
18
|
}
|
|
29
|
-
/**
|
|
30
|
-
* The host whose `mount` registers the routes and the absence hook under
|
|
31
|
-
* `entrypoint` — after refusing, now, a surface it cannot mount literally.
|
|
32
|
-
*/
|
|
33
19
|
export function createFastifyProtocolHost(platform, binding, entrypoint, maxRequestBytes) {
|
|
34
20
|
checkLiteralRoutes(binding, entrypoint);
|
|
35
21
|
const translated = (request, content) => protocolRequest(request.raw.method, request.raw.url, request.raw, entrypoint, content);
|
|
@@ -68,15 +54,9 @@ export function createFastifyProtocolHost(platform, binding, entrypoint, maxRequ
|
|
|
68
54
|
},
|
|
69
55
|
};
|
|
70
56
|
}
|
|
71
|
-
/**
|
|
72
|
-
* The route path as find-my-way matches it: `decodeURI`-ed, as it decodes a
|
|
73
|
-
* request path before the lookup, and every `:` (raw only in the entrypoint —
|
|
74
|
-
* the surface encodes it) doubled, find-my-way's escape for a literal colon.
|
|
75
|
-
*/
|
|
76
57
|
function literalPath(path) {
|
|
77
58
|
return decodeURI(path).replaceAll(":", "::");
|
|
78
59
|
}
|
|
79
|
-
/** The protocol's answer on the hijacked reply, the hooks' headers beneath it. */
|
|
80
60
|
function write(reply, answer) {
|
|
81
61
|
reply.hijack();
|
|
82
62
|
writeAnswer(reply.raw, answer, reply.getHeaders());
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The DI token under which `AventaraModule` provides the application's
|
|
3
|
-
* `Framework
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* own alias).
|
|
3
|
+
* `Framework`. A symbol, so injection is exact: an abstract-class token would type
|
|
4
|
+
* the injected value as an empty class, not the application's own `Framework<M,
|
|
5
|
+
* Cfg>`.
|
|
7
6
|
*/
|
|
8
7
|
export declare const AVENTARA_FRAMEWORK: unique symbol;
|
|
9
|
-
/** `@InjectFramework()` — `@Inject(AVENTARA_FRAMEWORK)` in one call
|
|
8
|
+
/** `@InjectFramework()` — `@Inject(AVENTARA_FRAMEWORK)` in one call. */
|
|
10
9
|
export declare function InjectFramework(): ParameterDecorator;
|
package/dist/framework.token.js
CHANGED
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
import { Inject } from "@nestjs/common";
|
|
2
|
-
/**
|
|
3
|
-
* The DI token under which `AventaraModule` provides the application's
|
|
4
|
-
* `Framework` (Q2). A symbol, so injection is exact: an abstract-class token
|
|
5
|
-
* would type the injected value as an empty class, not the application's own
|
|
6
|
-
* `Framework<M, Cfg>` (Q3 — the application types the injection with its
|
|
7
|
-
* own alias).
|
|
8
|
-
*/
|
|
9
2
|
export const AVENTARA_FRAMEWORK = Symbol("AVENTARA_FRAMEWORK");
|
|
10
|
-
/** `@InjectFramework()` — `@Inject(AVENTARA_FRAMEWORK)` in one call (Q2). */
|
|
11
3
|
export function InjectFramework() {
|
|
12
4
|
return Inject(AVENTARA_FRAMEWORK);
|
|
13
5
|
}
|
|
@@ -3,7 +3,7 @@ import type { AventaraModuleOptions } from "./aventara.module.js";
|
|
|
3
3
|
export declare function checkedOptions(options: unknown): AventaraModuleOptions;
|
|
4
4
|
/** Records one mount in `application`; a second is refused. */
|
|
5
5
|
export declare function recordMount(application: object): void;
|
|
6
|
-
/** The Nest HTTP platforms the module mounts the protocol on
|
|
6
|
+
/** The Nest HTTP platforms the module mounts the protocol on. */
|
|
7
7
|
export type HostPlatform = "express" | "fastify";
|
|
8
8
|
/** The application's platform, as `httpAdapter.getType()` names it — or a refusal. */
|
|
9
9
|
export declare function checkedPlatform(type: string | undefined): HostPlatform;
|
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Q18's bootstrap checks, all at module setup, so `NestFactory.create`
|
|
3
|
-
* rejects before the application listens: plain `Error`s with fixed messages
|
|
4
|
-
* (no new public name — Q18 a). A `FrameworkConstructionError` is core's and
|
|
5
|
-
* passes through untouched; nothing here wraps it.
|
|
6
|
-
*/
|
|
7
1
|
const NEITHER_ARM = "AventaraModule: the options carry neither `framework` nor `config`; pass exactly one.";
|
|
8
2
|
const BOTH_ARMS = "AventaraModule: the options carry both `framework` and `config`; pass exactly one.";
|
|
9
3
|
const NOT_A_FRAMEWORK = "AventaraModule: `framework` is not an Aventara Framework (it has no string `entrypoint` and no `contracts.client`); pass what `createFramework` resolved to.";
|
|
10
4
|
const MOUNTED_TWICE = "AventaraModule is mounted twice in one application; import AventaraModule.forRoot or forRootAsync once.";
|
|
11
|
-
/** The options as given — `forRootAsync`'s factory may resolve anything at runtime. */
|
|
12
5
|
export function checkedOptions(options) {
|
|
13
6
|
const record = typeof options === "object" && options !== null
|
|
14
7
|
? options
|
|
@@ -26,7 +19,6 @@ export function checkedOptions(options) {
|
|
|
26
19
|
}
|
|
27
20
|
return record;
|
|
28
21
|
}
|
|
29
|
-
/** What the host reads of a Framework: its entrypoint, and its compiled ClientContract. */
|
|
30
22
|
function isFramework(value) {
|
|
31
23
|
if (typeof value !== "object" || value === null) {
|
|
32
24
|
return false;
|
|
@@ -36,13 +28,7 @@ function isFramework(value) {
|
|
|
36
28
|
typeof candidate.contracts?.client === "object" &&
|
|
37
29
|
candidate.contracts.client !== null);
|
|
38
30
|
}
|
|
39
|
-
/**
|
|
40
|
-
* Applications that already mount the module, keyed by an object Nest creates
|
|
41
|
-
* once per application (its `HttpAdapterHost`). Weak, so a closed
|
|
42
|
-
* application's entry goes with it.
|
|
43
|
-
*/
|
|
44
31
|
const mounted = new WeakSet();
|
|
45
|
-
/** Records one mount in `application`; a second is refused. */
|
|
46
32
|
export function recordMount(application) {
|
|
47
33
|
if (mounted.has(application)) {
|
|
48
34
|
throw new Error(MOUNTED_TWICE);
|
|
@@ -50,7 +36,6 @@ export function recordMount(application) {
|
|
|
50
36
|
mounted.add(application);
|
|
51
37
|
}
|
|
52
38
|
const HOST_PLATFORMS = ["express", "fastify"];
|
|
53
|
-
/** The application's platform, as `httpAdapter.getType()` names it — or a refusal. */
|
|
54
39
|
export function checkedPlatform(type) {
|
|
55
40
|
const platform = HOST_PLATFORMS.find((supported) => supported === type);
|
|
56
41
|
if (platform === undefined) {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FrameworkDiagnostic, FrameworkDiagnosticsSink } from "@aventara/core";
|
|
2
|
+
/** Wraps the application's sink (if any): log an internal failure, then forward. */
|
|
3
|
+
export declare function withInternalFailureLog(sink: FrameworkDiagnosticsSink | undefined): FrameworkDiagnosticsSink;
|
|
4
|
+
/**
|
|
5
|
+
* e.g. `A3001 on Post.create.one (client scope, request 7f3c…): Error. The raw
|
|
6
|
+
* error goes to the framework's diagnostics sink.`
|
|
7
|
+
*/
|
|
8
|
+
export declare function internalFailureLine(diagnostic: FrameworkDiagnostic): string;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Logger } from "@nestjs/common";
|
|
2
|
+
const INTERNAL_FAILURE_LOGGER = new Logger("Aventara");
|
|
3
|
+
export function withInternalFailureLog(sink) {
|
|
4
|
+
return (diagnostic) => {
|
|
5
|
+
if (diagnostic.code.startsWith("A3")) {
|
|
6
|
+
try {
|
|
7
|
+
INTERNAL_FAILURE_LOGGER.error(internalFailureLine(diagnostic));
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return sink?.(diagnostic);
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function internalFailureLine(diagnostic) {
|
|
16
|
+
const subject = diagnostic.kind === "operation"
|
|
17
|
+
? `${diagnostic.resource}.${diagnostic.family}.${diagnostic.variant}`
|
|
18
|
+
: "a transaction";
|
|
19
|
+
const step = diagnostic.operation === undefined
|
|
20
|
+
? ""
|
|
21
|
+
: `, plan operation ${diagnostic.operation}`;
|
|
22
|
+
return `${diagnostic.code} on ${subject} (${diagnostic.scope} scope, request ${diagnostic.requestId}${step}): ${thrownKind(diagnostic.error)}${frameworkDetail(diagnostic.error)}. The raw error goes to the framework's diagnostics sink.`;
|
|
23
|
+
}
|
|
24
|
+
function frameworkDetail(error) {
|
|
25
|
+
try {
|
|
26
|
+
if (error instanceof Error &&
|
|
27
|
+
error.name === "PipeOutputError" &&
|
|
28
|
+
typeof error.message === "string") {
|
|
29
|
+
return ` — ${error.message}`;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
}
|
|
34
|
+
return "";
|
|
35
|
+
}
|
|
36
|
+
function thrownKind(error) {
|
|
37
|
+
try {
|
|
38
|
+
const name = error instanceof Error ? error.constructor.name : undefined;
|
|
39
|
+
return typeof name === "string" && /^[A-Za-z_$][\w$]{0,63}$/.test(name)
|
|
40
|
+
? name
|
|
41
|
+
: "a non-Error value";
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return "an unreadable value";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import type { AvProtocolRequest, AvProtocolResponse } from "@aventara/core/protocol";
|
|
2
|
-
/**
|
|
3
|
-
* The translation both platforms share, because both hand the host Node's own
|
|
4
|
-
* request and response (Q10: shared only as measured — S4 ran the corpus on
|
|
5
|
-
* Express and Fastify through it). Node's types are the slices below (P4).
|
|
6
|
-
*/
|
|
7
|
-
/** The slice of Node's `IncomingMessage` the host reads. */
|
|
8
2
|
export type NodeIncoming = AsyncIterable<Uint8Array> & {
|
|
9
3
|
readonly headersDistinct: Readonly<Record<string, readonly string[]>>;
|
|
10
4
|
};
|
|
11
|
-
/** The slice of Node's `ServerResponse` the host writes (P1: no platform reply path). */
|
|
12
5
|
export type NodeOutgoing = {
|
|
13
6
|
writeHead(status: number, headers: Readonly<Record<string, string | number | readonly string[]>>): unknown;
|
|
14
7
|
end(body: string): unknown;
|
|
@@ -17,14 +10,16 @@ export type NodeOutgoing = {
|
|
|
17
10
|
export type PlatformHeaders = Readonly<Record<string, string | number | readonly string[] | undefined>>;
|
|
18
11
|
/** Zero bytes: the body of a request whose body the protocol does not read. */
|
|
19
12
|
export declare const NO_BYTES: Uint8Array<ArrayBuffer>;
|
|
20
|
-
/** A request URL's raw pathname — query dropped
|
|
13
|
+
/** A request URL's raw pathname — query dropped, not URL-decoded. */
|
|
21
14
|
export declare function pathnameOf(url: string): string;
|
|
22
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* The request as the protocol reads it: path relative to the entrypoint,
|
|
17
|
+
* `headersDistinct`, the raw arm.
|
|
18
|
+
*/
|
|
23
19
|
export declare function protocolRequest(method: string, url: string, incoming: NodeIncoming, entrypoint: string, content: Uint8Array): AvProtocolRequest;
|
|
24
20
|
/**
|
|
25
|
-
* The protocol's answer, written as it is
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* Express.
|
|
21
|
+
* The protocol's answer, written as it is. Headers the platform's hooks already
|
|
22
|
+
* set (CORS, say) go beneath it; a protocol header of the same name, in any case,
|
|
23
|
+
* wins — what Node's `writeHead` does with `setHeader`'s on Express.
|
|
29
24
|
*/
|
|
30
25
|
export declare function writeAnswer(response: NodeOutgoing, answer: AvProtocolResponse, beneath?: PlatformHeaders): void;
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
/** Zero bytes: the body of a request whose body the protocol does not read. */
|
|
2
1
|
export const NO_BYTES = new Uint8Array();
|
|
3
|
-
/** A request URL's raw pathname — query dropped (Q12), not URL-decoded (P2). */
|
|
4
2
|
export function pathnameOf(url) {
|
|
5
3
|
const query = url.indexOf("?");
|
|
6
4
|
return query === -1 ? url : url.slice(0, query);
|
|
7
5
|
}
|
|
8
|
-
/** The request as the protocol reads it: path relative to the entrypoint, `headersDistinct` (Q11), the raw arm (Q8). */
|
|
9
6
|
export function protocolRequest(method, url, incoming, entrypoint, content) {
|
|
10
7
|
return {
|
|
11
8
|
method,
|
|
@@ -14,12 +11,6 @@ export function protocolRequest(method, url, incoming, entrypoint, content) {
|
|
|
14
11
|
body: { kind: "raw", content },
|
|
15
12
|
};
|
|
16
13
|
}
|
|
17
|
-
/**
|
|
18
|
-
* The protocol's answer, written as it is (P1). Headers the platform's hooks
|
|
19
|
-
* already set (CORS, say) go beneath it; a protocol header of the same name,
|
|
20
|
-
* in any case, wins — what Node's `writeHead` does with `setHeader`'s on
|
|
21
|
-
* Express.
|
|
22
|
-
*/
|
|
23
14
|
export function writeAnswer(response, answer, beneath = {}) {
|
|
24
15
|
const own = new Set(Object.keys(answer.headers).map((name) => name.toLowerCase()));
|
|
25
16
|
const headers = {};
|
|
@@ -7,9 +7,9 @@ export type ProtocolHost = {
|
|
|
7
7
|
};
|
|
8
8
|
/**
|
|
9
9
|
* Binds `framework`'s protocol for the application's platform, refusing an
|
|
10
|
-
* unsupported platform while providers are instantiated — so
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* unsupported platform while providers are instantiated — so `NestFactory.create`
|
|
11
|
+
* rejects before the application listens. The routes are mounted later, from the
|
|
12
|
+
* module's `configure()`.
|
|
13
13
|
*/
|
|
14
14
|
export declare function createProtocolHost(adapter: AbstractHttpAdapter | undefined, framework: Framework): ProtocolHost;
|
|
15
15
|
/**
|
|
@@ -32,17 +32,14 @@ export type AdapterHostView = {
|
|
|
32
32
|
};
|
|
33
33
|
};
|
|
34
34
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* `
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* is kept and raised by `mount()` or `assertReady()`, from `app.init()` — still
|
|
45
|
-
* before the application listens. With no adapter at all
|
|
46
|
-
* (`createApplicationContext`), `assertReady()` refuses from `onModuleInit`.
|
|
35
|
+
* `NestFactory.create` sets the adapter first: the host is created at once, and an
|
|
36
|
+
* unsupported platform rejects then, as before. Nest's testing module compiles
|
|
37
|
+
* first and receives its adapter from `createNestApplication()`, which sets it on
|
|
38
|
+
* the `HttpAdapterHost` — `init$` fires — before `app.init()` registers Nest's
|
|
39
|
+
* body parsers: the host is created at that moment, so a platform's body reader is
|
|
40
|
+
* still installed ahead of them. A refusal there is kept and raised by `mount()`
|
|
41
|
+
* or `assertReady()`, from `app.init()` — still before the application listens.
|
|
42
|
+
* With no adapter at all (`createApplicationContext`), `assertReady()` refuses
|
|
43
|
+
* from `onModuleInit`.
|
|
47
44
|
*/
|
|
48
45
|
export declare function createPendingProtocolHost(application: AdapterHostView, framework: Framework): PendingProtocolHost;
|
|
@@ -11,12 +11,6 @@ const PLATFORM_MOUNTERS = {
|
|
|
11
11
|
}, binding, entrypoint, maxRequestBytes),
|
|
12
12
|
fastify: (adapter, binding, entrypoint, maxRequestBytes) => createFastifyProtocolHost(adapter.getInstance(), binding, entrypoint, maxRequestBytes),
|
|
13
13
|
};
|
|
14
|
-
/**
|
|
15
|
-
* Binds `framework`'s protocol for the application's platform, refusing an
|
|
16
|
-
* unsupported platform while providers are instantiated — so
|
|
17
|
-
* `NestFactory.create` rejects before the application listens (Q18). The
|
|
18
|
-
* routes are mounted later, from the module's `configure()` (Q5).
|
|
19
|
-
*/
|
|
20
14
|
export function createProtocolHost(adapter, framework) {
|
|
21
15
|
const platform = checkedPlatform(adapter?.getType());
|
|
22
16
|
const binding = AvProtocol.bind(framework);
|
|
@@ -28,18 +22,7 @@ export function createProtocolHost(adapter, framework) {
|
|
|
28
22
|
},
|
|
29
23
|
};
|
|
30
24
|
}
|
|
31
|
-
/**
|
|
32
|
-
* pilot.1 — the protocol's routes are platform routes, which Nest's
|
|
33
|
-
* `RouterExplorer` never logs; this is the one line that says they exist.
|
|
34
|
-
* Through Nest's `Logger`, so the application's logger settings govern it.
|
|
35
|
-
*/
|
|
36
25
|
const MOUNT_LOGGER = new Logger("AventaraModule");
|
|
37
|
-
/**
|
|
38
|
-
* What one mount registered, read off `protocol.surface()`: the operation
|
|
39
|
-
* count under the entrypoint, then each framework route by method and path —
|
|
40
|
-
* e.g. `Aventara mounted 5 operations at /api (+ GET /api/_contract, POST
|
|
41
|
-
* /api/_transactions)`.
|
|
42
|
-
*/
|
|
43
26
|
export function mountedLine(routes, entrypoint) {
|
|
44
27
|
const operations = routes.filter((route) => route.kind === "operation");
|
|
45
28
|
const framework = routes
|
|
@@ -47,20 +30,6 @@ export function mountedLine(routes, entrypoint) {
|
|
|
47
30
|
.map((route) => `${route.method} ${entrypoint}${route.path}`);
|
|
48
31
|
return `Aventara mounted ${operations.length} ${operations.length === 1 ? "operation" : "operations"} at ${entrypoint === "" ? "/" : entrypoint} (+ ${framework.join(", ")})`;
|
|
49
32
|
}
|
|
50
|
-
/**
|
|
51
|
-
* N1 (plan B23) — a host for an application whose HTTP adapter may arrive after
|
|
52
|
-
* the providers are instantiated.
|
|
53
|
-
*
|
|
54
|
-
* `NestFactory.create` sets the adapter first: the host is created at once, and
|
|
55
|
-
* an unsupported platform rejects then, as before (Q18). Nest's testing module
|
|
56
|
-
* compiles first and receives its adapter from `createNestApplication()`, which
|
|
57
|
-
* sets it on the `HttpAdapterHost` — `init$` fires — before `app.init()`
|
|
58
|
-
* registers Nest's body parsers: the host is created at that moment, so a
|
|
59
|
-
* platform's body reader is still installed ahead of them (Q8). A refusal there
|
|
60
|
-
* is kept and raised by `mount()` or `assertReady()`, from `app.init()` — still
|
|
61
|
-
* before the application listens. With no adapter at all
|
|
62
|
-
* (`createApplicationContext`), `assertReady()` refuses from `onModuleInit`.
|
|
63
|
-
*/
|
|
64
33
|
export function createPendingProtocolHost(application, framework) {
|
|
65
34
|
if (application.httpAdapter !== undefined) {
|
|
66
35
|
const host = createProtocolHost(application.httpAdapter, framework);
|
|
@@ -80,7 +49,6 @@ export function createPendingProtocolHost(application, framework) {
|
|
|
80
49
|
if (refusal !== undefined) {
|
|
81
50
|
throw refusal;
|
|
82
51
|
}
|
|
83
|
-
// No adapter ever arrived: refused with the same sentence as at creation.
|
|
84
52
|
return host ?? createProtocolHost(undefined, framework);
|
|
85
53
|
};
|
|
86
54
|
return {
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import type { AvProtocolBinding, AvProtocolResponse } from "@aventara/core/protocol";
|
|
2
2
|
/**
|
|
3
|
-
* The raw arm's body reader
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
3
|
+
* The raw arm's body reader: a request body read off a Node stream, decoded per
|
|
4
|
+
* its `Content-Encoding`, and stored up to `maxRequestBytes + 1` DECODED bytes —
|
|
5
|
+
* one more than the limit is all the decoder needs to answer `A2010`, and counting
|
|
6
|
+
* decoded bytes is also the decompression-bomb bound. Past the bound the stream is
|
|
7
|
+
* still read to its end and discarded, so the connection stays usable; nothing
|
|
8
|
+
* more is inflated.
|
|
9
9
|
*
|
|
10
|
-
* Platform-free: it reads an async iterable of bytes, as Node's
|
|
11
|
-
*
|
|
12
|
-
* not resolve, typed by the slice below (P4: no `@types/node`).
|
|
10
|
+
* Platform-free: it reads an async iterable of bytes, as Node's `IncomingMessage`
|
|
11
|
+
* is.
|
|
13
12
|
*/
|
|
14
13
|
/**
|
|
15
|
-
* What the reader hands on: the stored bytes; an unsupported coding
|
|
16
|
-
*
|
|
17
|
-
* a malformed request (director, 2026-10-05: answered `A2000`).
|
|
14
|
+
* What the reader hands on: the stored bytes; an unsupported coding; or a body
|
|
15
|
+
* that does not decode in the coding it names — a malformed request.
|
|
18
16
|
*/
|
|
19
17
|
export type RequestBodyRead = {
|
|
20
18
|
readonly kind: "read";
|
|
@@ -25,12 +23,11 @@ export type RequestBodyRead = {
|
|
|
25
23
|
readonly kind: "undecodable";
|
|
26
24
|
};
|
|
27
25
|
/**
|
|
28
|
-
* The answer to a body the reader could not hand on, through the bound
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* (register row, S8).
|
|
26
|
+
* The answer to a body the reader could not hand on, through the bound `failure`
|
|
27
|
+
* member — the one place the host names an A-code: an unsupported coding is `415
|
|
28
|
+
* A2011`; a body that does not decode in its coding is malformed, `400 A2000`. The
|
|
29
|
+
* request id is minted by core: it exposes no adoption of the client's id to a
|
|
30
|
+
* host.
|
|
34
31
|
*/
|
|
35
32
|
export declare function unreadBodyAnswer(binding: AvProtocolBinding, read: Exclude<RequestBodyRead, {
|
|
36
33
|
readonly kind: "read";
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The answer to a body the reader could not hand on, through the bound
|
|
3
|
-
* `failure` member — the one place the host names an A-code (Q9): an
|
|
4
|
-
* unsupported coding is `415 A2011`; a body that does not decode in its
|
|
5
|
-
* coding is malformed, `400 A2000` (director, 2026-10-05). The request id is
|
|
6
|
-
* minted by core: it exposes no adoption of the client's id to a host
|
|
7
|
-
* (register row, S8).
|
|
8
|
-
*/
|
|
9
1
|
export function unreadBodyAnswer(binding, read) {
|
|
10
2
|
return read.kind === "unsupported-coding"
|
|
11
3
|
? binding.failure("A2011")
|
|
@@ -22,19 +14,9 @@ const zlib = () => {
|
|
|
22
14
|
zlibModule ??= import(specifier);
|
|
23
15
|
return zlibModule;
|
|
24
16
|
};
|
|
25
|
-
/**
|
|
26
|
-
* Reads `source` to its end. `contentEncoding` is the header's values as
|
|
27
|
-
* received; absent, empty or `identity` is no coding; one of `gzip`,
|
|
28
|
-
* `deflate`, `br` (any case) is inflated; anything else — a stacked coding
|
|
29
|
-
* included — is unsupported. A body that does not decode in the coding it
|
|
30
|
-
* names is undecodable. Either way the stream is still read to its end.
|
|
31
|
-
*/
|
|
32
17
|
export async function readRequestBody(source, contentEncoding, cap) {
|
|
33
18
|
const coding = codingOf(contentEncoding);
|
|
34
19
|
if (coding === undefined) {
|
|
35
|
-
// Read to the end and discarded all the same: the stream finished is
|
|
36
|
-
// what keeps the platform's own parsers off it (Q8), and the
|
|
37
|
-
// connection usable.
|
|
38
20
|
for await (const _ of source) {
|
|
39
21
|
}
|
|
40
22
|
return { kind: "unsupported-coding" };
|
|
@@ -55,11 +37,6 @@ export async function readRequestBody(source, contentEncoding, cap) {
|
|
|
55
37
|
}
|
|
56
38
|
}
|
|
57
39
|
})();
|
|
58
|
-
// Settles when the inflater stops — at its end, at the bound (destroyed),
|
|
59
|
-
// or at a decoding error. Node calls no write callback once an inflater
|
|
60
|
-
// has failed, so each write also waits on this, never on the callback
|
|
61
|
-
// alone. Marking it handled here keeps an early decoding error from being
|
|
62
|
-
// reported as unhandled before it is awaited below.
|
|
63
40
|
const stopped = inflated.then(() => undefined, () => undefined);
|
|
64
41
|
for await (const chunk of source) {
|
|
65
42
|
if (!inflater.destroyed) {
|
|
@@ -82,7 +59,6 @@ export async function readRequestBody(source, contentEncoding, cap) {
|
|
|
82
59
|
}
|
|
83
60
|
return { kind: "read", content: stored.content() };
|
|
84
61
|
}
|
|
85
|
-
/** The coding the header names, `identity` for none, `undefined` for an unsupported one. */
|
|
86
62
|
function codingOf(contentEncoding) {
|
|
87
63
|
const named = (contentEncoding ?? [])
|
|
88
64
|
.join(",")
|
|
@@ -103,7 +79,6 @@ function codingOf(contentEncoding) {
|
|
|
103
79
|
? only
|
|
104
80
|
: undefined;
|
|
105
81
|
}
|
|
106
|
-
/** Stores bytes up to `limit`; `add` answers whether there is room for more. */
|
|
107
82
|
function boundedStore(limit) {
|
|
108
83
|
const chunks = [];
|
|
109
84
|
let size = 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aventara/nest",
|
|
3
|
-
"version": "0.1.0-pilot.
|
|
3
|
+
"version": "0.1.0-pilot.3",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"description": "NestJS host integration for Aventara.",
|
|
6
6
|
"type": "module",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"LICENSE-ADDITIONAL-PERMISSION.md"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@aventara/core": "0.1.0-pilot.
|
|
24
|
+
"@aventara/core": "0.1.0-pilot.3"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"@nestjs/common": "^12.0.0",
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"access": "public"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@aventara/prisma7-adapter": "0.1.0-pilot.
|
|
35
|
-
"@aventara/testing": "0.1.0-pilot.
|
|
34
|
+
"@aventara/prisma7-adapter": "0.1.0-pilot.3",
|
|
35
|
+
"@aventara/testing": "0.1.0-pilot.3",
|
|
36
36
|
"@nestjs/common": "^12.1.2",
|
|
37
37
|
"@nestjs/core": "^12.1.2",
|
|
38
38
|
"@nestjs/platform-express": "^12.1.2",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"rxjs": "^7.8.2"
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|
|
46
|
-
"build": "tsc -p tsconfig.json",
|
|
46
|
+
"build": "tsc -p tsconfig.json --removeComments --declaration false && tsc -p tsconfig.json --emitDeclarationOnly && node ../../scripts/build/declaration-comments.sanitizer.ts dist",
|
|
47
47
|
"typecheck": "tsc -p tsconfig.typecheck.json --noEmit",
|
|
48
48
|
"test": "vitest run --typecheck --config ../../vitest.config.mts --root .",
|
|
49
49
|
"lint": "biome lint src"
|