@znight/http 0.5.0 → 0.6.0
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/adapter-6qC95ViO.d.mts +2163 -0
- package/dist/compiler.d.mts +1 -1
- package/dist/compiler.mjs +1 -1
- package/dist/express.d.mts +1 -1
- package/dist/express.mjs +4 -1
- package/dist/express.mjs.map +1 -1
- package/dist/fastify.d.mts +1 -1
- package/dist/fastify.mjs +4 -1
- package/dist/fastify.mjs.map +1 -1
- package/dist/hono.d.mts +1 -1
- package/dist/hono.mjs +38 -3
- package/dist/hono.mjs.map +1 -1
- package/dist/index.d.mts +3 -1117
- package/dist/index.mjs +263 -269
- package/dist/index.mjs.map +1 -1
- package/dist/{node-DnYv_AMI.mjs → node-npGnu7IW.mjs} +5 -2
- package/dist/node-npGnu7IW.mjs.map +1 -0
- package/dist/node.d.mts +1 -1
- package/dist/node.mjs +1 -1
- package/dist/{node-io-BkdRSQeg.mjs → upgrade-io-dTMDDmBi.mjs} +625 -27
- package/dist/upgrade-io-dTMDDmBi.mjs.map +1 -0
- package/package.json +4 -4
- package/src/adapter.ts +7 -0
- package/src/adapters/express.ts +4 -0
- package/src/adapters/fastify.ts +4 -0
- package/src/adapters/hono.ts +48 -2
- package/src/adapters/node-io.ts +8 -0
- package/src/adapters/node.ts +4 -0
- package/src/adapters/upgrade-io.ts +255 -0
- package/src/index.ts +14 -0
- package/src/pipeline.ts +108 -2
- package/src/plugin-routes.ts +6 -0
- package/src/plugin.ts +197 -16
- package/src/upgrade.ts +341 -0
- package/dist/adapter-DmhQGqAR.d.mts +0 -895
- package/dist/node-DnYv_AMI.mjs.map +0 -1
- package/dist/node-io-BkdRSQeg.mjs.map +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,725 +1,6 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as BodyLimit, $n as InferSchemaOutput, $t as ContentDispositionType, A as ExceptionMapperRef, An as ConflictException, At as Validate, B as HstsOptions, Bn as NotFoundException, Bt as HttpInterceptorClass, C as PluginHttpUpgradesContext, Cn as ByteSize, Ct as RouteMetaEntry, D as PluginHttpRoutes, Dn as parseQuery, Dt as UploadedFiles, E as UpgradeHandlerOptions, En as QueryRecord, Et as UploadedFile, F as HttpRouteSummary, Fn as HttpErrorBody, Ft as ExceptionMapperClass, G as ParseSseOptions, Gn as UnauthorizedException, Gt as isHttpResponse, H as TrustProxy, Hn as PayloadTooLargeException, Ht as HttpResponseInit, I as HttpSseOptions, In as HttpException, It as ExceptionMapperResult, J as parseSseStream, Jn as ValidationException, Jt as SseEvent, K as SseItem, Kn as UnprocessableEntityException, Kt as StreamSource, L as InterceptorRef, Ln as HttpExceptionOptions, Lt as GuardClass, M as HttpCookieOptions, Mn as GatewayTimeoutException, Mt as CallHandler, N as HttpPlugin, Nn as GoneException, Nt as CanActivate, O as PluginHttpRoutesContext, On as BadGatewayException, Ot as UseGuard, P as HttpPluginOptions, Pn as HeaderValue, Pt as ExceptionMapper, Q as Body, Qn as InferSchemaInput, Qt as ByteRange, R as TrailingSlashPolicy, Rn as InternalServerErrorException, Rt as GuardEnforcement, S as PluginHttpUpgrades, Sn as uploads, St as RouteMetaCarrier, T as PluginUpgrades, Tn as parseByteSize, Tt as Sse, U as InjectSseResponse, Un as ServiceUnavailableException, Ut as RedirectStatus, V as SecurityHeadersOptions, Vn as NotImplementedException, Vt as HttpResponse, W as InjectSseWaitOptions, Wn as TooManyRequestsException, Wt as StreamResponseOptions, X as CorsOrigin, Xn as isHttpException, Xt as SseSource, Y as CorsOptions, Yn as ValidationIssueDetail, Yt as SseOptions, Z as All, Zn as toIssueDetail, Zt as encodeSseEvent, _ as BunUpgrade, _n as RequestId, _r as serializeClearCookie, _t as Res, a as AdapterRoute, an as HttpMetadata, ar as StandardSchemaV1Props, at as Get, b as NodeUpgrade, bn as UploadedFilesOptions, bt as RouteDecoratorInfo, c as HttpServerAddress, cn as HttpParamSource, cr as StandardSchemaV1Types, ct as Headers, d as InjectStreamOptions, dn as HttpSseDefinition, dr as CookieDefaults, dt as Param, en as FileBody, er as StandardSchemaV1, et as Controller, f as InjectStreamRequest, fn as HttpUploadDefinition, fr as CookieOptions, ft as Patch, g as BunSocketHandlers, gn as RequestAbortedError, gr as parseSetCookie, gt as Req, h as AdapterUpgrades, hn as RequestAbortKind, hr as parseCookieHeader, ht as Query, i as AdapterRequest, in as HttpControllerDefinition, ir as StandardSchemaV1PathSegment, it as Delete, j as GuardRef, jn as ForbiddenException, jt as createRouteDecorator, k as PluginRoute, kn as BadRequestException, kt as UseInterceptor, l as InjectRequest, ln as HttpRouteDefinition, lr as isStandardSchema, lt as HttpCode, m as ReplyBody, mn as HttpRouteInfo, mr as SetCookieInfo, mt as Put, n as AdapterHandler, nn as contentDisposition, nr as StandardSchemaV1Issue, nt as Cookie, o as FallbackHandler, on as HttpMethod, or as StandardSchemaV1Result, ot as Head, p as InjectStreamResponse, pn as HttpContext, pr as ResponseCookie, pt as Post, q as SseMessage, qn as UnsupportedMediaTypeException, qt as SseBody, r as AdapterReply, rn as parseRange, rr as StandardSchemaV1Options, rt as Ctx, s as HttpAdapter, sn as HttpParamDefinition, sr as StandardSchemaV1SuccessResult, st as Header, t as AdapterCloseOptions, tn as FileResponseOptions, tr as StandardSchemaV1FailureResult, tt as ControllerOptions, u as InjectResponse, un as HttpRouteGroupDefinition, ur as ClearCookieOptions, ut as Options, v as MemoryUpgrade, vn as UploadLimits, vr as serializeCookie, vt as RouteDecorator, w as PluginUpgradeRoute, wn as formatByteSize, wt as SignedCookie, x as PendingUpgrade, xn as Uploads, xt as RouteDecoratorOptions, y as NativeUpgrade, yn as UploadedFileOptions, yt as RouteDecoratorArgs, z as EnforcementMode, zn as MethodNotAllowedException, zt as HttpInterceptor } from "./adapter-6qC95ViO.mjs";
|
|
2
2
|
import { NodeAdapter, NodeAdapterOptions, nodeAdapter } from "./node.mjs";
|
|
3
|
-
import {
|
|
4
|
-
/**
|
|
5
|
-
* Origins allowed to read responses: `'*'` (any origin), an exact origin
|
|
6
|
-
* (`'https://app.example.com'`), an anchored `RegExp` (`/^https:\/\/[a-z0-9-]+\.example\.com$/`), a
|
|
7
|
-
* list of those, or a function (awaited; a function that throws or rejects denies the origin).
|
|
8
|
-
*/
|
|
9
|
-
type CorsOrigin = "*" | string | RegExp | readonly (string | RegExp)[] | ((origin: string) => boolean | Promise<boolean>);
|
|
10
|
-
/** The `cors` option of `HttpPlugin.configure`. */
|
|
11
|
-
interface CorsOptions {
|
|
12
|
-
/** Allowed origins (required). */
|
|
13
|
-
readonly origin: CorsOrigin;
|
|
14
|
-
/** `access-control-allow-credentials: true` (cookies, `Authorization`). Not with `'*'`. Default `false`. */
|
|
15
|
-
readonly credentials?: boolean;
|
|
16
|
-
/**
|
|
17
|
-
* `access-control-allow-methods` of preflights. Default: the methods routed on the requested path.
|
|
18
|
-
* `'*'` (any method) is refused with `credentials`, where browsers read it as a method named `*`.
|
|
19
|
-
*/
|
|
20
|
-
readonly methods?: readonly string[];
|
|
21
|
-
/**
|
|
22
|
-
* `access-control-allow-headers` of preflights. Default: the headers the browser asks for
|
|
23
|
-
* (`access-control-request-headers`). `'*'` is refused with `credentials`; it covers
|
|
24
|
-
* `Authorization` too (sent as `*, authorization` when the browser asks for it: browsers never let
|
|
25
|
-
* `*` alone cover it).
|
|
26
|
-
*/
|
|
27
|
-
readonly allowHeaders?: readonly string[] | "*";
|
|
28
|
-
/**
|
|
29
|
-
* `access-control-expose-headers` of replies. Default: the request id header (none when it is
|
|
30
|
-
* `false`). `'*'` (every header) is refused with `credentials`, where browsers read it literally.
|
|
31
|
-
*/
|
|
32
|
-
readonly exposeHeaders?: readonly string[];
|
|
33
|
-
/** `access-control-max-age` of preflights, in seconds (0 to 86400). Default `600`. */
|
|
34
|
-
readonly maxAge?: number;
|
|
35
|
-
}
|
|
36
|
-
/** How a file is presented to the client (`Content-Disposition`). */
|
|
37
|
-
type ContentDispositionType = "attachment" | "inline";
|
|
38
|
-
/** Options of `HttpResponse.file()`. */
|
|
39
|
-
interface FileResponseOptions {
|
|
40
|
-
/**
|
|
41
|
-
* Name proposed to the client in `Content-Disposition` (any Unicode name; path separators are
|
|
42
|
-
* removed). Setting it makes the response an `attachment` unless `disposition` says otherwise.
|
|
43
|
-
*/
|
|
44
|
-
readonly filename?: string;
|
|
45
|
-
/**
|
|
46
|
-
* Media type of the response. Default: the type of the `Blob`, else looked up from the extension
|
|
47
|
-
* of `filename` / of the path (small built-in table, text types with `charset=utf-8`), else
|
|
48
|
-
* `application/octet-stream`.
|
|
49
|
-
*/
|
|
50
|
-
readonly contentType?: string;
|
|
51
|
-
/**
|
|
52
|
-
* `attachment` (download) or `inline` (displayed by the browser). Default: `attachment` when
|
|
53
|
-
* `filename` is set, otherwise no `Content-Disposition` header. Without `filename`, the file name
|
|
54
|
-
* (path basename, `File.name`) is used.
|
|
55
|
-
*/
|
|
56
|
-
readonly disposition?: ContentDispositionType;
|
|
57
|
-
/**
|
|
58
|
-
* Directory the path is resolved in. The resolved file (symbolic links followed) must stay inside
|
|
59
|
-
* it: `..` segments, absolute paths and links escaping the directory answer 404. Always set it
|
|
60
|
-
* when the path comes from the request.
|
|
61
|
-
*/
|
|
62
|
-
readonly root?: string;
|
|
63
|
-
/**
|
|
64
|
-
* With `root`: whether files or directories whose name starts with a dot (`.env`, `.git/config`)
|
|
65
|
-
* may be sent. Default: `'deny'` (404).
|
|
66
|
-
*/
|
|
67
|
-
readonly dotfiles?: "allow" | "deny";
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* The body of `HttpResponse.file()`: a file on disk or a `Blob`, resolved by the pipeline when the
|
|
71
|
-
* response is sent (stat, conditional request, range).
|
|
72
|
-
*/
|
|
73
|
-
declare class FileBody {
|
|
74
|
-
/** Path (`string` / `file:` URL) or `Blob` / `File`. */
|
|
75
|
-
readonly source: string | URL | Blob;
|
|
76
|
-
readonly options: FileResponseOptions;
|
|
77
|
-
constructor(source: string | URL | Blob, options?: FileResponseOptions);
|
|
78
|
-
}
|
|
79
|
-
/** An inclusive byte range. */
|
|
80
|
-
interface ByteRange {
|
|
81
|
-
readonly start: number;
|
|
82
|
-
readonly end: number;
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Parses a `Range` header for a representation of `size` bytes. Only a single `bytes` range is
|
|
86
|
-
* honoured: other units, several ranges and malformed values return `undefined` (the whole
|
|
87
|
-
* representation is sent, as RFC 9110 allows); a valid range starting after the end returns
|
|
88
|
-
* `'unsatisfiable'` (416).
|
|
89
|
-
*/
|
|
90
|
-
export declare function parseRange(header: string, size: number): ByteRange | "unsatisfiable" | undefined;
|
|
91
|
-
/**
|
|
92
|
-
* Builds a `Content-Disposition` header value (RFC 6266): `attachment; filename="report.pdf"`, with
|
|
93
|
-
* an RFC 8187 / 5987 `filename*` parameter for names that are not plain ASCII
|
|
94
|
-
* (`attachment; filename="r_sum_.pdf"; filename*=UTF-8''r%C3%A9sum%C3%A9.pdf`). Directory parts and
|
|
95
|
-
* control characters of `filename` are removed.
|
|
96
|
-
*/
|
|
97
|
-
export declare function contentDisposition(filename?: string, type?: ContentDispositionType): string;
|
|
98
|
-
/** One event of an event stream. */
|
|
99
|
-
interface SseEvent<T = unknown> {
|
|
100
|
-
/** A string is sent as is; anything else goes through `JSON.stringify` (`toJSON` honoured). */
|
|
101
|
-
readonly data?: T;
|
|
102
|
-
/** Event type (`addEventListener(type)`). The client defaults to `message`. No CR or LF. */
|
|
103
|
-
readonly event?: string;
|
|
104
|
-
/** Last event id, echoed by the client as `Last-Event-ID`. No CR, LF or NUL. `''` resets it. */
|
|
105
|
-
readonly id?: string | number;
|
|
106
|
-
/** Reconnection delay in ms (a safe integer ≥ 0). */
|
|
107
|
-
readonly retry?: number;
|
|
108
|
-
/** Comment lines, ignored by `EventSource` (debugging, padding). */
|
|
109
|
-
readonly comment?: string;
|
|
110
|
-
}
|
|
111
|
-
/** Options of `@Sse()` and `HttpResponse.sse()`. */
|
|
112
|
-
interface SseOptions {
|
|
113
|
-
/**
|
|
114
|
-
* Milliseconds without an event after which a keep-alive comment (`:`) is sent (an integer
|
|
115
|
-
* from 1 000 to 2 147 483 647). `false` disables it. Default: the plugin option `sse.heartbeat`
|
|
116
|
-
* (8 000).
|
|
117
|
-
*/
|
|
118
|
-
readonly heartbeat?: number | false;
|
|
119
|
-
/**
|
|
120
|
-
* Client reconnection delay (`retry:`, ms), sent first. Default: the plugin option `sse.retry`
|
|
121
|
-
* (none: browsers use about 3 s).
|
|
122
|
-
*/
|
|
123
|
-
readonly retry?: number;
|
|
124
|
-
/**
|
|
125
|
-
* The server ends the stream gracefully after this many ms (load rebalancing, re-authentication):
|
|
126
|
-
* `ctx.signal` aborts (`'expired'`), then the client gets a jittered `retry` and reconnects. An
|
|
127
|
-
* integer from 1 000 to 2 147 483 647 (about 24.8 days, the longest timer). Default: none.
|
|
128
|
-
*/
|
|
129
|
-
readonly maxDuration?: number;
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* What `@Sse` handlers and `HttpResponse.sse()` accept: an async iterable (async generators,
|
|
133
|
-
* `EventChannel`, a Node.js `Readable` of raw bytes), an iterable (arrays, sync generators; not
|
|
134
|
-
* strings) or a `ReadableStream`, of `SseEvent` objects or `Uint8Array` chunks (already-framed SSE
|
|
135
|
-
* text, written as is: proxying an upstream event stream).
|
|
136
|
-
*/
|
|
137
|
-
type SseSource = AsyncIterable<SseEvent | Uint8Array> | Iterable<SseEvent> | ReadableStream<SseEvent | Uint8Array>;
|
|
138
|
-
/** The body of `HttpResponse.sse()`: an event source and its options (served by the plugin). */
|
|
139
|
-
declare class SseBody {
|
|
140
|
-
readonly source: SseSource;
|
|
141
|
-
readonly options: SseOptions;
|
|
142
|
-
constructor(source: SseSource, options?: SseOptions);
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
* Encodes one event (WHATWG framing): one `: <line>` per comment line, `id:`, `event:`, `retry:`,
|
|
146
|
-
* then one `data: <line>` per line of the data (split on CRLF, CR and LF, so data can never inject
|
|
147
|
-
* fields or end the event early), and an empty line. An `event` without `data` gets an empty
|
|
148
|
-
* `data:` line (otherwise `EventSource` would not dispatch it); events with only `id`, `retry` or
|
|
149
|
-
* `comment` have no data line.
|
|
150
|
-
*
|
|
151
|
-
* ```ts
|
|
152
|
-
* encodeSseEvent({ id: 41, event: 'status', data: { status: 'paid' } })
|
|
153
|
-
* // 'id: 41\nevent: status\ndata: {"status":"paid"}\n\n'
|
|
154
|
-
* ```
|
|
155
|
-
*
|
|
156
|
-
* @throws TypeError for anything but a plain object with the keys of {@link SseEvent} (strings,
|
|
157
|
-
* numbers, `null`: yield `{ data: value }`), an empty event, an `event` / `id` with CR or LF (NUL
|
|
158
|
-
* for `id`), a `retry` that is not an integer ≥ 0, a `data` JSON cannot serialise.
|
|
159
|
-
*/
|
|
160
|
-
export declare function encodeSseEvent(event: SseEvent): string;
|
|
161
|
-
/**
|
|
162
|
-
* A stream source accepted by `HttpResponse.stream()`: a Web `ReadableStream`, a Node.js `Readable`
|
|
163
|
-
* or any async iterable (async generator...) of `Uint8Array` / `string` chunks (strings are encoded
|
|
164
|
-
* as UTF-8).
|
|
165
|
-
*/
|
|
166
|
-
type StreamSource = ReadableStream<Uint8Array | string> | AsyncIterable<Uint8Array | string>;
|
|
167
|
-
/** Options of {@link HttpResponse.stream}. */
|
|
168
|
-
interface StreamResponseOptions {
|
|
169
|
-
/** Media type. Default: from the extension of `filename`, else `application/octet-stream`. */
|
|
170
|
-
readonly contentType?: string;
|
|
171
|
-
/** Name proposed to the client (`Content-Disposition: attachment` unless `disposition` says otherwise). */
|
|
172
|
-
readonly filename?: string;
|
|
173
|
-
/** `attachment` or `inline`. Default: `attachment` when `filename` is set, otherwise no header. */
|
|
174
|
-
readonly disposition?: ContentDispositionType;
|
|
175
|
-
}
|
|
176
|
-
/** Options of {@link HttpResponse}. */
|
|
177
|
-
interface HttpResponseInit {
|
|
178
|
-
/** Status code. Default: the route default (`@HttpCode`, 201 for POST, 204 without body, 200). */
|
|
179
|
-
readonly status?: number;
|
|
180
|
-
/** Response headers (names are case-insensitive; arrays send the header several times). */
|
|
181
|
-
readonly headers?: Readonly<Record<string, HeaderValue>>;
|
|
182
|
-
/** Cookies to set (see {@link HttpResponse.withCookie}), in order. */
|
|
183
|
-
readonly cookies?: readonly ResponseCookie[];
|
|
184
|
-
}
|
|
185
|
-
/** Redirect statuses accepted by {@link HttpResponse.redirect}. */
|
|
186
|
-
type RedirectStatus = 301 | 302 | 303 | 307 | 308;
|
|
187
|
-
/**
|
|
188
|
-
* A handler result carrying a status and headers next to the body. The body is serialised with the
|
|
189
|
-
* usual rules (objects → JSON, strings → text, bytes and streams → octet-stream).
|
|
190
|
-
*
|
|
191
|
-
* ```ts
|
|
192
|
-
* @Post('/')
|
|
193
|
-
* async create(@Body(CreateUser) input: CreateUser): Promise<HttpResponse<User>> {
|
|
194
|
-
* const user = await this.users.create(input)
|
|
195
|
-
* return HttpResponse.created(user, `/users/${user.id}`)
|
|
196
|
-
* }
|
|
197
|
-
* ```
|
|
198
|
-
*
|
|
199
|
-
* Status precedence: `@HttpCode` > `HttpResponse.status` > route default.
|
|
200
|
-
*/
|
|
201
|
-
export declare class HttpResponse<T = unknown> {
|
|
202
|
-
#private;
|
|
203
|
-
readonly body: T;
|
|
204
|
-
readonly status: number | undefined;
|
|
205
|
-
readonly headers: Readonly<Record<string, HeaderValue>>;
|
|
206
|
-
/** Cookies set by this response (`withCookie` / `withoutCookie`), in order; they win over `ctx.setCookie()`. */
|
|
207
|
-
readonly cookies: readonly ResponseCookie[];
|
|
208
|
-
constructor(body: T, init?: HttpResponseInit);
|
|
209
|
-
/** `200 OK` with `body`. */
|
|
210
|
-
static ok<T>(body: T, headers?: Readonly<Record<string, HeaderValue>>): HttpResponse<T>;
|
|
211
|
-
/**
|
|
212
|
-
* `201 Created`, with a `location` header when given. With a body, the response is typed with it
|
|
213
|
-
* (`Promise<HttpResponse<BookResponse>>` type-checks).
|
|
214
|
-
*/
|
|
215
|
-
static created(): HttpResponse<undefined>;
|
|
216
|
-
static created<T>(body: T, location?: string): HttpResponse<T>;
|
|
217
|
-
/** `202 Accepted`, typed with its body when there is one. */
|
|
218
|
-
static accepted(): HttpResponse<undefined>;
|
|
219
|
-
static accepted<T>(body: T): HttpResponse<T>;
|
|
220
|
-
/** `204 No Content`. */
|
|
221
|
-
static noContent(): HttpResponse<undefined>;
|
|
222
|
-
/** A redirect (`302 Found` by default) to `location`. */
|
|
223
|
-
static redirect(location: string, status?: RedirectStatus): HttpResponse<undefined>;
|
|
224
|
-
/**
|
|
225
|
-
* Sends a file from disk (`string` path or `file:` URL) or a `Blob` / `File`, streamed with
|
|
226
|
-
* `Content-Length`, `Content-Type` (from the extension, see `contentTypeFor`), `Last-Modified`,
|
|
227
|
-
* `ETag` and `Accept-Ranges`. Conditional requests answer 304 / 412 and single byte ranges 206
|
|
228
|
-
* (416 when unsatisfiable). A missing file answers 404.
|
|
229
|
-
*
|
|
230
|
-
* ```ts
|
|
231
|
-
* @Get('/reports/:name')
|
|
232
|
-
* download(@Param('name') name: string) {
|
|
233
|
-
* // `root` confines the path: '../../etc/passwd' answers 404
|
|
234
|
-
* return HttpResponse.file(name, { root: REPORTS_DIR, filename: `rapport ${name}` })
|
|
235
|
-
* }
|
|
236
|
-
* ```
|
|
237
|
-
*
|
|
238
|
-
* Never pass a path built from the request without `root`.
|
|
239
|
-
*/
|
|
240
|
-
static file(file: string | URL | Blob, options?: FileResponseOptions): HttpResponse<FileBody>;
|
|
241
|
-
/**
|
|
242
|
-
* Streams a Web `ReadableStream`, a Node.js `Readable` or an async iterable (async generator) of
|
|
243
|
-
* `Uint8Array` / `string` chunks. The source is read as the client receives the data
|
|
244
|
-
* (backpressure) and cancelled when the client disconnects; it runs in the request scope.
|
|
245
|
-
*
|
|
246
|
-
* ```ts
|
|
247
|
-
* @Get('/export.csv')
|
|
248
|
-
* export() {
|
|
249
|
-
* return HttpResponse.stream(this.rows.csvLines(), { filename: 'export.csv' }) // text/csv; charset=utf-8
|
|
250
|
-
* }
|
|
251
|
-
* ```
|
|
252
|
-
*/
|
|
253
|
-
static stream(source: StreamSource, options?: StreamResponseOptions): HttpResponse<ReadableStream<Uint8Array>>;
|
|
254
|
-
/**
|
|
255
|
-
* An event stream (Server-Sent Events) on any route and any method: `200` unless `@HttpCode` or
|
|
256
|
-
* {@link withStatus} say otherwise (`EventSource` requires 200; `fetch()` clients read any status),
|
|
257
|
-
* even on `POST` routes. Same source, framing and lifecycle as an `@Sse()` route.
|
|
258
|
-
*
|
|
259
|
-
* ```ts
|
|
260
|
-
* @Post('/:id/assistant')
|
|
261
|
-
* assistant(@Param('id') id: string, @Body(Prompt) prompt: Prompt, @Ctx() ctx: HttpContext) {
|
|
262
|
-
* return HttpResponse.sse(this.llm.complete(id, prompt, { signal: ctx.signal }))
|
|
263
|
-
* }
|
|
264
|
-
* ```
|
|
265
|
-
*/
|
|
266
|
-
static sse(source: SseSource, options?: SseOptions): HttpResponse<SseBody>;
|
|
267
|
-
/** A copy of this response with `name` set to `value`. */
|
|
268
|
-
withHeader(name: string, value: HeaderValue): HttpResponse<T>;
|
|
269
|
-
/** A copy of this response with another status. */
|
|
270
|
-
withStatus(status: number): HttpResponse<T>;
|
|
271
|
-
/**
|
|
272
|
-
* A copy of this response setting a cookie, with the secure defaults (`Path=/; Secure; HttpOnly;
|
|
273
|
-
* SameSite=Lax`, see the plugin option `cookies.defaults`). It replaces a cookie with the same name,
|
|
274
|
-
* domain and path set by `ctx.setCookie()` or an earlier `withCookie()`.
|
|
275
|
-
*
|
|
276
|
-
* ```ts
|
|
277
|
-
* return HttpResponse.ok({ id: user.id }).withCookie('__Host-uid', user.id, { signed: true, maxAge: 8 * 3600 })
|
|
278
|
-
* ```
|
|
279
|
-
*
|
|
280
|
-
* The name, the value, the lifetime, the domain, the path and the size are checked now; the rules
|
|
281
|
-
* depending on the plugin (`secure` default with a prefix or `SameSite=None`, signing, the final
|
|
282
|
-
* size) when the response is sent: a violation then answers a generic 500, logged as
|
|
283
|
-
* `Invalid cookie "name": …`.
|
|
284
|
-
*
|
|
285
|
-
* @throws TypeError / RangeError for an invalid cookie.
|
|
286
|
-
*/
|
|
287
|
-
withCookie(name: string, value: string, options?: CookieOptions): HttpResponse<T>;
|
|
288
|
-
/** A copy of this response clearing a cookie (see `ctx.clearCookie()`). */
|
|
289
|
-
withoutCookie(name: string, options?: ClearCookieOptions): HttpResponse<T>;
|
|
290
|
-
}
|
|
291
|
-
/** Whether `value` is an {@link HttpResponse} (also across duplicated package copies). */
|
|
292
|
-
export declare function isHttpResponse(value: unknown): value is HttpResponse;
|
|
293
|
-
/**
|
|
294
|
-
* A guard decides whether a request may reach the handler. Guards run in order: global (plugin
|
|
295
|
-
* options) → controller (`@UseGuard` on the class) → route. Returning `false` responds
|
|
296
|
-
* `403 Forbidden`; throw an `HttpException` (e.g. `UnauthorizedException`) for another response.
|
|
297
|
-
*
|
|
298
|
-
* ```ts
|
|
299
|
-
* @Injectable()
|
|
300
|
-
* export class RolesGuard implements CanActivate {
|
|
301
|
-
* canActivate(ctx: HttpContext): boolean {
|
|
302
|
-
* const roles = Roles.get(ctx) // route metadata from createRouteDecorator
|
|
303
|
-
* return roles === undefined || roles.some(role => currentUser(ctx).roles.includes(role))
|
|
304
|
-
* }
|
|
305
|
-
* }
|
|
306
|
-
* ```
|
|
307
|
-
*/
|
|
308
|
-
interface CanActivate {
|
|
309
|
-
canActivate(ctx: HttpContext): boolean | Promise<boolean>;
|
|
310
|
-
}
|
|
311
|
-
/** Runs the rest of the interceptor chain and the handler; resolves with the handler result. */
|
|
312
|
-
type CallHandler = () => Promise<unknown>;
|
|
313
|
-
/**
|
|
314
|
-
* Wraps the handler (after the guards). Interceptors run global → controller → route, the first one
|
|
315
|
-
* being the outermost. `next()` resolves with the handler result (before serialisation): return it,
|
|
316
|
-
* transform it, or return something else without calling `next()` (cache hit...).
|
|
317
|
-
*
|
|
318
|
-
* ```ts
|
|
319
|
-
* @Injectable()
|
|
320
|
-
* export class TimingInterceptor implements HttpInterceptor {
|
|
321
|
-
* async intercept(ctx: HttpContext, next: CallHandler) {
|
|
322
|
-
* const started = performance.now()
|
|
323
|
-
* try {
|
|
324
|
-
* return await next()
|
|
325
|
-
* } finally {
|
|
326
|
-
* ctx.setHeader('server-timing', `handler;dur=${performance.now() - started}`)
|
|
327
|
-
* }
|
|
328
|
-
* }
|
|
329
|
-
* }
|
|
330
|
-
* ```
|
|
331
|
-
*/
|
|
332
|
-
interface HttpInterceptor {
|
|
333
|
-
intercept(ctx: HttpContext, next: CallHandler): unknown;
|
|
334
|
-
}
|
|
335
|
-
/** What an {@link ExceptionMapper} may return: `undefined` lets the next mapper (or the default) handle the error. */
|
|
336
|
-
type ExceptionMapperResult = HttpException | HttpResponse | Response | undefined;
|
|
337
|
-
/**
|
|
338
|
-
* Converts errors to responses. Mappers (plugin option `exceptionMappers`) are tried in order for
|
|
339
|
-
* every error of the pipeline, including `404`/`405`; the first one returning something wins. Return
|
|
340
|
-
* an `HttpException` to reuse the default error body, an `HttpResponse` or a Web `Response` for full
|
|
341
|
-
* control, `undefined` to pass.
|
|
342
|
-
*
|
|
343
|
-
* ```ts
|
|
344
|
-
* @Injectable()
|
|
345
|
-
* export class DomainErrorMapper implements ExceptionMapper {
|
|
346
|
-
* map(error: unknown) {
|
|
347
|
-
* if (error instanceof UserNotFoundError) return new NotFoundException(error.message)
|
|
348
|
-
* return undefined
|
|
349
|
-
* }
|
|
350
|
-
* }
|
|
351
|
-
* ```
|
|
352
|
-
*/
|
|
353
|
-
interface ExceptionMapper {
|
|
354
|
-
map(error: unknown, ctx: HttpContext): ExceptionMapperResult | Promise<ExceptionMapperResult>;
|
|
355
|
-
}
|
|
356
|
-
/**
|
|
357
|
-
* Static side of a guard class: the route decorators whose decisions the guard enforces. Read by
|
|
358
|
-
* `HttpPlugin` at boot: a route whose decision belongs to a required group (or to a group some
|
|
359
|
-
* guard enforces) must have a guard enforcing that group in its chain (ZNIGHT023 otherwise).
|
|
360
|
-
*
|
|
361
|
-
* ```ts
|
|
362
|
-
* @Injectable()
|
|
363
|
-
* export class AuthGuard implements CanActivate {
|
|
364
|
-
* static readonly enforces = [Public, Authenticated, Roles]
|
|
365
|
-
* canActivate(ctx: HttpContext) { ... }
|
|
366
|
-
* }
|
|
367
|
-
* ```
|
|
368
|
-
*
|
|
369
|
-
* Typed with {@link RouteDecoratorInfo} (not `RouteDecorator<unknown>`) so that `[Public, Roles]`
|
|
370
|
-
* stays assignable under `strictFunctionTypes`.
|
|
371
|
-
*/
|
|
372
|
-
interface GuardEnforcement {
|
|
373
|
-
readonly enforces?: readonly RouteDecoratorInfo[];
|
|
374
|
-
}
|
|
375
|
-
/** A class producing {@link CanActivate} instances (for `@UseGuard` and plugin options). */
|
|
376
|
-
type GuardClass = (abstract new (...args: never[]) => CanActivate) & GuardEnforcement;
|
|
377
|
-
/** A class producing {@link HttpInterceptor} instances. */
|
|
378
|
-
type HttpInterceptorClass = abstract new (...args: never[]) => HttpInterceptor;
|
|
379
|
-
/** A class producing {@link ExceptionMapper} instances. */
|
|
380
|
-
type ExceptionMapperClass = abstract new (...args: never[]) => ExceptionMapper;
|
|
381
|
-
/** Options of {@link Controller} (static). */
|
|
382
|
-
interface ControllerOptions {
|
|
383
|
-
/** Base path of every route of the controller (`/users`). Default: `/`. */
|
|
384
|
-
readonly path?: string;
|
|
385
|
-
/** Provider scope. Default: `singleton`; `request` creates one controller per request. */
|
|
386
|
-
readonly scope?: Scope;
|
|
387
|
-
}
|
|
388
|
-
/**
|
|
389
|
-
* Declares a controller: a provider (singleton by default) whose decorated methods are routes.
|
|
390
|
-
*
|
|
391
|
-
* ```ts
|
|
392
|
-
* @Controller('/users')
|
|
393
|
-
* export class UserController {
|
|
394
|
-
* constructor(private readonly users: UserService) {}
|
|
395
|
-
*
|
|
396
|
-
* @Get('/:id')
|
|
397
|
-
* find(@Param('id') id: number) {
|
|
398
|
-
* return this.users.findById(id)
|
|
399
|
-
* }
|
|
400
|
-
* }
|
|
401
|
-
* ```
|
|
402
|
-
*
|
|
403
|
-
* @param pathOrOptions static: base path, or `{ path?, scope? }`.
|
|
404
|
-
*/
|
|
405
|
-
export declare function Controller(_pathOrOptions?: string | ControllerOptions): ClassMarker;
|
|
406
|
-
/** `GET` route. `path` (static) is relative to the controller path; default: the controller path. */
|
|
407
|
-
export declare function Get(_path?: string): MethodMarker;
|
|
408
|
-
/** `POST` route (default status 201). */
|
|
409
|
-
export declare function Post(_path?: string): MethodMarker;
|
|
410
|
-
/** `PUT` route. */
|
|
411
|
-
export declare function Put(_path?: string): MethodMarker;
|
|
412
|
-
/** `PATCH` route. */
|
|
413
|
-
export declare function Patch(_path?: string): MethodMarker;
|
|
414
|
-
/** `DELETE` route. */
|
|
415
|
-
export declare function Delete(_path?: string): MethodMarker;
|
|
416
|
-
/** `HEAD` route (`GET` routes already answer `HEAD` requests). */
|
|
417
|
-
export declare function Head(_path?: string): MethodMarker;
|
|
418
|
-
/** `OPTIONS` route (paths without one answer `OPTIONS` with `204` and an `allow` header). */
|
|
419
|
-
export declare function Options(_path?: string): MethodMarker;
|
|
420
|
-
/** Route for GET, HEAD, POST, PUT, PATCH, DELETE and OPTIONS (methods without a dedicated route on the same path). */
|
|
421
|
-
export declare function All(_path?: string): MethodMarker;
|
|
422
|
-
/**
|
|
423
|
-
* A `GET` route serving an event stream (Server-Sent Events). The handler returns an
|
|
424
|
-
* `AsyncIterable`, an `Iterable` or a `ReadableStream` of `SseEvent` (`{ data, event, id, retry,
|
|
425
|
-
* comment }`), or an `HttpResponse` / exception for other outcomes (204 stops `EventSource`). The
|
|
426
|
-
* plugin frames the events, sends heartbeats, stops the source when the client leaves (`ctx.signal`)
|
|
427
|
-
* and closes the stream gracefully on shutdown.
|
|
428
|
-
*
|
|
429
|
-
* ```ts
|
|
430
|
-
* @Sse('/:id/events', { retry: 5_000 })
|
|
431
|
-
* async events(@Param('id') id: string, @Ctx() ctx: HttpContext): Promise<AsyncIterable<SseEvent<OrderEvent>>> {
|
|
432
|
-
* if ((await this.orders.find(id)) === undefined) throw new NotFoundException() // pre-flight checks first
|
|
433
|
-
* return this.hub.subscribe(id, { signal: ctx.signal })
|
|
434
|
-
* }
|
|
435
|
-
* ```
|
|
436
|
-
*
|
|
437
|
-
* Prefer an `async` method returning the source over an `async *` generator: the body of a
|
|
438
|
-
* generator only runs once the 200 and the headers are sent.
|
|
439
|
-
*
|
|
440
|
-
* @param path static: route path (relative to the controller path).
|
|
441
|
-
* @param options static: `{ heartbeat?, retry?, maxDuration? }` (see `SseOptions`).
|
|
442
|
-
*/
|
|
443
|
-
export declare function Sse(path?: string, options?: SseOptions): MethodMarker;
|
|
444
|
-
export declare function Sse(options: SseOptions): MethodMarker;
|
|
445
|
-
/**
|
|
446
|
-
* Injects a route parameter, coerced to the parameter type (`string`, `number`, `boolean`,
|
|
447
|
-
* `bigint`, `Date`, literal unions) or validated with `schema`. Without arguments: every parameter
|
|
448
|
-
* (`Record<string, string>`).
|
|
449
|
-
*
|
|
450
|
-
* ```ts
|
|
451
|
-
* @Get('/:id')
|
|
452
|
-
* find(@Param('id') id: number) {}
|
|
453
|
-
* ```
|
|
454
|
-
*
|
|
455
|
-
* @param name static: name of a `:param` of the route path.
|
|
456
|
-
* @param schema value: a Standard Schema validating the raw string (its output is injected).
|
|
457
|
-
*/
|
|
458
|
-
export declare function Param(): ParameterMarker;
|
|
459
|
-
export declare function Param(name: string, schema?: StandardSchemaV1): ParameterMarker;
|
|
460
|
-
export declare function Param(schema: StandardSchemaV1): ParameterMarker;
|
|
461
|
-
/**
|
|
462
|
-
* Injects a query parameter (coerced to the parameter type; `T[]` accepts repeated and single
|
|
463
|
-
* values; optional when the parameter is optional or has a default) or, without name, the whole
|
|
464
|
-
* query (`Record<string, string | string[]>`), typically validated with a schema.
|
|
465
|
-
*
|
|
466
|
-
* ```ts
|
|
467
|
-
* list(@Query('page') page = 1, @Query('tag') tags: string[] = []) {}
|
|
468
|
-
* search(@Query(SearchQuery) query: SearchQuery) {}
|
|
469
|
-
* ```
|
|
470
|
-
*
|
|
471
|
-
* @param name static: query key.
|
|
472
|
-
* @param schema value: a Standard Schema (its output is injected).
|
|
473
|
-
*/
|
|
474
|
-
export declare function Query(): ParameterMarker;
|
|
475
|
-
export declare function Query(name: string, schema?: StandardSchemaV1): ParameterMarker;
|
|
476
|
-
export declare function Query(schema: StandardSchemaV1): ParameterMarker;
|
|
477
|
-
/**
|
|
478
|
-
* Injects a request cookie (decoded, coerced to the parameter type like `@Headers`: `string`,
|
|
479
|
-
* `number`, `boolean`, `bigint`, `Date`, literal unions, enums, branded primitives) or validated
|
|
480
|
-
* with `schema`; without name, every cookie (`Record<string, string>`). An absent cookie answers 400
|
|
481
|
-
* `Missing cookie "name"` unless the parameter is optional. Error messages never echo the value.
|
|
482
|
-
*
|
|
483
|
-
* ```ts
|
|
484
|
-
* // a stale or forged value falls back instead of answering 400 until the cookie expires
|
|
485
|
-
* export const Theme = z.enum(['light', 'dark']).catch('light')
|
|
486
|
-
*
|
|
487
|
-
* read(@Cookie('theme', Theme) theme: 'light' | 'dark', @Cookie('visits') visits = 0) {}
|
|
488
|
-
* ```
|
|
489
|
-
*
|
|
490
|
-
* @param name static: cookie name (an RFC 9110 token).
|
|
491
|
-
* @param schema value: a Standard Schema (its output is injected).
|
|
492
|
-
*/
|
|
493
|
-
export declare function Cookie(): ParameterMarker;
|
|
494
|
-
export declare function Cookie(name: string, schema?: StandardSchemaV1): ParameterMarker;
|
|
495
|
-
export declare function Cookie(schema: StandardSchemaV1): ParameterMarker;
|
|
496
|
-
/**
|
|
497
|
-
* Injects the **verified** value of a signed cookie (set with `{ signed: true }`, plugin option
|
|
498
|
-
* `cookies.secrets`): absent, tampered or signed with a retired key, the cookie does not exist
|
|
499
|
-
* (400 `Missing cookie "name"` unless the parameter is optional). Same coercion and schema rules as
|
|
500
|
-
* {@link Cookie}. A route reading a signed cookie fails the boot when `cookies.secrets` is not set.
|
|
501
|
-
*
|
|
502
|
-
* @param name static: cookie name.
|
|
503
|
-
* @param schema value: a Standard Schema (its output is injected).
|
|
504
|
-
*/
|
|
505
|
-
export declare function SignedCookie(_name: string, _schema?: StandardSchemaV1): ParameterMarker;
|
|
506
|
-
/**
|
|
507
|
-
* Injects the parsed request body, validated with `schema` when given (the schema output is
|
|
508
|
-
* injected: defaults, transforms, stripped keys). A non-primitive body without schema is reported
|
|
509
|
-
* by the compiler (ZNIGHT017).
|
|
510
|
-
*
|
|
511
|
-
* @param schema value: a Standard Schema (Zod, Valibot, ArkType...).
|
|
512
|
-
*/
|
|
513
|
-
export declare function Body(_schema?: StandardSchemaV1): ParameterMarker;
|
|
514
|
-
/**
|
|
515
|
-
* Injects a request header (case-insensitive `name`, coerced to the parameter type) or, without
|
|
516
|
-
* name, every header (`Record<string, string | undefined>`).
|
|
517
|
-
*
|
|
518
|
-
* @param name static: header name.
|
|
519
|
-
*/
|
|
520
|
-
export declare function Headers(_name?: string): ParameterMarker;
|
|
521
|
-
/**
|
|
522
|
-
* Injects a file of a `multipart/form-data` body as a standard `File` (`name`, `type`, `size`,
|
|
523
|
-
* `arrayBuffer()`, `text()`, `stream()`), held in memory. The route parses multipart bodies before
|
|
524
|
-
* its interceptors, enforcing the options while the body is read: a file above `maxSize` answers
|
|
525
|
-
* 413, a refused type 415, a second file in the field or a file in an undeclared field 400, a
|
|
526
|
-
* missing required file 400, a non-multipart body 415. `@Body()` receives the text fields.
|
|
527
|
-
*
|
|
528
|
-
* ```ts
|
|
529
|
-
* @Post('/avatar')
|
|
530
|
-
* upload(@UploadedFile('avatar', { maxSize: '2mb', mimeTypes: ['image/png', 'image/jpeg'] }) avatar: File) {}
|
|
531
|
-
*
|
|
532
|
-
* @Post('/attachment')
|
|
533
|
-
* attach(@UploadedFile('file') file?: File) {} // optional: undefined when absent
|
|
534
|
-
* ```
|
|
535
|
-
*
|
|
536
|
-
* `File.name` and `File.type` are sent by the client: never trust them (see the README, "Files").
|
|
537
|
-
*
|
|
538
|
-
* @param field static: the multipart field; omitted: the single file of the body, whatever its field.
|
|
539
|
-
* @param options static: `{ required?, maxSize?, mimeTypes? }`.
|
|
540
|
-
*/
|
|
541
|
-
export declare function UploadedFile(field?: string, options?: UploadedFileOptions): ParameterMarker;
|
|
542
|
-
export declare function UploadedFile(options: UploadedFileOptions): ParameterMarker;
|
|
543
|
-
/**
|
|
544
|
-
* Injects the files of a multipart field (`File[]`, in body order), or of every field without
|
|
545
|
-
* `field`. Same rules as {@link UploadedFile}; above `maxFiles` files the request answers 413. An
|
|
546
|
-
* optional parameter (`files?: File[]`, `required: false`) receives an empty array when no file is
|
|
547
|
-
* sent.
|
|
548
|
-
*
|
|
549
|
-
* ```ts
|
|
550
|
-
* @Post('/documents')
|
|
551
|
-
* add(@UploadedFiles('documents', { maxFiles: 5, maxSize: '20mb', mimeTypes: ['application/pdf'] }) documents: File[]) {}
|
|
552
|
-
* ```
|
|
553
|
-
*
|
|
554
|
-
* @param field static: the multipart field; omitted: the files of every field.
|
|
555
|
-
* @param options static: `{ required?, maxSize?, maxFiles?, mimeTypes? }`.
|
|
556
|
-
*/
|
|
557
|
-
export declare function UploadedFiles(field?: string, options?: UploadedFilesOptions): ParameterMarker;
|
|
558
|
-
export declare function UploadedFiles(options: UploadedFilesOptions): ParameterMarker;
|
|
559
|
-
/**
|
|
560
|
-
* Sets the maximum request body size of a route, or of every route of a controller (the route wins),
|
|
561
|
-
* over the plugin `bodyLimit` (and over the limit computed for upload routes). `Infinity` disables
|
|
562
|
-
* the limit, e.g. for a route streaming its body with `ctx.bodyStream()`.
|
|
563
|
-
*
|
|
564
|
-
* ```ts
|
|
565
|
-
* @Put('/files/:name')
|
|
566
|
-
* @BodyLimit('5gb')
|
|
567
|
-
* async store(@Param('name') name: string, @Ctx() ctx: HttpContext) {
|
|
568
|
-
* await pipeline(Readable.fromWeb(ctx.bodyStream()), createWriteStream(join(DIR, safeName(name))))
|
|
569
|
-
* }
|
|
570
|
-
* ```
|
|
571
|
-
*
|
|
572
|
-
* @param size static: bytes (`10_485_760`) or a size with a unit (`'10mb'`, `'512kb'`; `kb` = 1024 bytes).
|
|
573
|
-
*/
|
|
574
|
-
export declare function BodyLimit(_size: ByteSize): ClassMarker & MethodMarker;
|
|
575
|
-
/** Injects the native request of the adapter (escape hatch; the compiler warns). */
|
|
576
|
-
export declare function Req(): ParameterMarker;
|
|
577
|
-
/**
|
|
578
|
-
* Injects the native response of the adapter (escape hatch; the compiler warns). The handler then
|
|
579
|
-
* writes the response itself, and must have finished when its promise settles.
|
|
580
|
-
*/
|
|
581
|
-
export declare function Res(): ParameterMarker;
|
|
582
|
-
/** Injects the {@link HttpContext} of the request. */
|
|
583
|
-
export declare function Ctx(): ParameterMarker;
|
|
584
|
-
/**
|
|
585
|
-
* Sets the status of successful responses (wins over `HttpResponse.status` and the defaults).
|
|
586
|
-
*
|
|
587
|
-
* @param status static: an integer between 100 and 599.
|
|
588
|
-
*/
|
|
589
|
-
export declare function HttpCode(_status: number): MethodMarker;
|
|
590
|
-
/**
|
|
591
|
-
* Adds a header to successful responses. Repeat the decorator for several headers.
|
|
592
|
-
*
|
|
593
|
-
* @param name static: header name.
|
|
594
|
-
* @param value static: header value.
|
|
595
|
-
*/
|
|
596
|
-
export declare function Header(_name: string, _value: string): MethodMarker;
|
|
597
|
-
/**
|
|
598
|
-
* Protects a controller (every route) or a route with guards (providers implementing
|
|
599
|
-
* {@link CanActivate}). Order: global guards → controller guards → route guards, each list in
|
|
600
|
-
* declaration order.
|
|
601
|
-
*
|
|
602
|
-
* @param guards value: guard classes.
|
|
603
|
-
*/
|
|
604
|
-
export declare function UseGuard(..._guards: [GuardClass, ...GuardClass[]]): ClassMarker & MethodMarker;
|
|
605
|
-
/**
|
|
606
|
-
* Wraps a controller (every route) or a route with interceptors (providers implementing
|
|
607
|
-
* {@link HttpInterceptor}). Order: global → controller → route, the first one being the outermost.
|
|
608
|
-
*
|
|
609
|
-
* @param interceptors value: interceptor classes.
|
|
610
|
-
*/
|
|
611
|
-
export declare function UseInterceptor(..._interceptors: [HttpInterceptorClass, ...HttpInterceptorClass[]]): ClassMarker & MethodMarker;
|
|
612
|
-
/**
|
|
613
|
-
* Validates the request body with `schema`: sugar for `@Body(schema)` on the body parameter (using
|
|
614
|
-
* both is ZNIGHT005).
|
|
615
|
-
*
|
|
616
|
-
* @param schema value: a Standard Schema.
|
|
617
|
-
*/
|
|
618
|
-
export declare function Validate(_schema: StandardSchemaV1): MethodMarker;
|
|
619
|
-
/** Arguments of a route decorator: optional when `T` accepts `undefined` or is `true` (flag decorators). */
|
|
620
|
-
type RouteDecoratorArgs<T> = [T] extends [true] ? [value?: T] : undefined extends T ? [value?: T] : [value: T];
|
|
621
|
-
/** Options of {@link createRouteDecorator} (static). */
|
|
622
|
-
interface RouteDecoratorOptions {
|
|
623
|
-
/**
|
|
624
|
-
* Decision group. Decorators of a group are alternatives: one per controller or route, and the
|
|
625
|
-
* route's decision replaces the controller's. Default: the key (the decorator is its own group).
|
|
626
|
-
* A letter followed by letters, digits, `_`, `.` or `-`.
|
|
627
|
-
*/
|
|
628
|
-
readonly group?: string;
|
|
629
|
-
/**
|
|
630
|
-
* Every route must carry a decision of the group: ZNIGHT023 at build time when a route has none,
|
|
631
|
-
* and at boot when no guard of its chain enforces it (see {@link GuardClass}). All the decorators
|
|
632
|
-
* of a group must agree. Default `false`.
|
|
633
|
-
*/
|
|
634
|
-
readonly required?: boolean;
|
|
635
|
-
}
|
|
636
|
-
/** The data part of a route decorator: what guards list in `static enforces`. */
|
|
637
|
-
interface RouteDecoratorInfo {
|
|
638
|
-
/** Key of the value in `route.meta`. */
|
|
639
|
-
readonly key: string;
|
|
640
|
-
/** Decision group (`options.group ?? key`). */
|
|
641
|
-
readonly group: string;
|
|
642
|
-
/** Every route must carry a decision of the group. */
|
|
643
|
-
readonly required: boolean;
|
|
644
|
-
}
|
|
645
|
-
/**
|
|
646
|
-
* A route metadata entry given as data rather than as a decorator: the `meta` of plugin routes
|
|
647
|
-
* (`[Public.meta()]`). Built by {@link RouteDecorator.meta}.
|
|
648
|
-
*/
|
|
649
|
-
interface RouteMetaEntry<T = unknown> {
|
|
650
|
-
readonly key: string;
|
|
651
|
-
readonly value: T;
|
|
652
|
-
/** Decision group. Default: the key. */
|
|
653
|
-
readonly group?: string;
|
|
654
|
-
/** Default `false`. */
|
|
655
|
-
readonly required?: boolean;
|
|
656
|
-
}
|
|
657
|
-
/**
|
|
658
|
-
* Anything that carries effective route metadata: the {@link HttpContext} of a request, and the
|
|
659
|
-
* contexts of other transports that reuse route decorators (a GraphQL resolver context, a WebSocket
|
|
660
|
-
* gateway context). {@link RouteDecorator.get} reads `route.meta[key]`; nothing else is required.
|
|
661
|
-
*
|
|
662
|
-
* ```ts
|
|
663
|
-
* const carrier: RouteMetaCarrier = { route: { meta: { roles: ['admin'] } } }
|
|
664
|
-
* Roles.get(carrier) // ['admin']
|
|
665
|
-
* ```
|
|
666
|
-
*/
|
|
667
|
-
interface RouteMetaCarrier {
|
|
668
|
-
/** The matched route (or field, or handler), `undefined` when there is none. */
|
|
669
|
-
readonly route?: {
|
|
670
|
-
readonly meta: Readonly<Record<string, unknown>>;
|
|
671
|
-
} | undefined;
|
|
672
|
-
}
|
|
673
|
-
/** A user-defined route metadata decorator created with {@link createRouteDecorator}. */
|
|
674
|
-
interface RouteDecorator<T> extends RouteDecoratorInfo {
|
|
675
|
-
(...args: RouteDecoratorArgs<T>): ClassMarker & MethodMarker;
|
|
676
|
-
/**
|
|
677
|
-
* The value of the current route, if set. Reads the effective metadata: the route's decision of
|
|
678
|
-
* a group wins over the controller's. Accepts any {@link RouteMetaCarrier}: an
|
|
679
|
-
* {@link HttpContext}, or the context of another transport exposing `route.meta`.
|
|
680
|
-
*/
|
|
681
|
-
get(ctx: RouteMetaCarrier): T | undefined;
|
|
682
|
-
/**
|
|
683
|
-
* The same metadata as data (`{ key, value, group, required }`), for routes that are not compiled
|
|
684
|
-
* controllers (plugin routes: `OpenApiPlugin.configure({ meta: [Public.meta()] })`). The value is
|
|
685
|
-
* `true` when omitted, like `@Public()`.
|
|
686
|
-
*/
|
|
687
|
-
meta(...args: RouteDecoratorArgs<T>): RouteMetaEntry<T>;
|
|
688
|
-
}
|
|
689
|
-
/**
|
|
690
|
-
* Creates a metadata decorator for controllers and routes. The compiler stores its argument
|
|
691
|
-
* (`value`, statically evaluated or an exported reference; `true` when omitted) in
|
|
692
|
-
* `meta[key]`, read by guards and interceptors through `ctx.route.meta` or `decorator.get(ctx)`.
|
|
693
|
-
*
|
|
694
|
-
* ```ts
|
|
695
|
-
* export const Public = createRouteDecorator('public', { group: 'access', required: true })
|
|
696
|
-
* export const Authenticated = createRouteDecorator('authenticated', { group: 'access', required: true })
|
|
697
|
-
* export const Roles = createRouteDecorator<readonly string[]>('roles', { group: 'access', required: true })
|
|
698
|
-
*
|
|
699
|
-
* @Controller('/books')
|
|
700
|
-
* @Roles(['reader']) // the decision of every route of the controller...
|
|
701
|
-
* export class BookController {
|
|
702
|
-
* @Get('/:id/cover')
|
|
703
|
-
* @Public() // ...unless the route has its own decision of the group
|
|
704
|
-
* cover() {}
|
|
705
|
-
* }
|
|
706
|
-
*
|
|
707
|
-
* // in a guard
|
|
708
|
-
* if (Public.get(ctx)) return true
|
|
709
|
-
* const roles = Roles.get(ctx) ?? []
|
|
710
|
-
* ```
|
|
711
|
-
*
|
|
712
|
-
* Decorators of one `group` are alternatives: two of them on the same class or method are
|
|
713
|
-
* ZNIGHT005, and the route's decision replaces the controller's (without `group`, each decorator is
|
|
714
|
-
* its own group: the route value wins over the controller value). With `required: true`, a route
|
|
715
|
-
* without decision fails the build (ZNIGHT023).
|
|
716
|
-
*
|
|
717
|
-
* @param key static: unique metadata key.
|
|
718
|
-
* @param options static: `{ group?, required? }`.
|
|
719
|
-
*/
|
|
720
|
-
export declare function createRouteDecorator<T = true>(key: string, options?: RouteDecoratorOptions): RouteDecorator<T>;
|
|
721
|
-
/** How the boot check reports problems. */
|
|
722
|
-
type EnforcementMode = "error" | "warn";
|
|
3
|
+
import { TypedToken } from "@znight/core";
|
|
723
4
|
/**
|
|
724
5
|
* `EventChannel`: a bounded bridge from push-based producers (an event bus, an `EventEmitter`, a
|
|
725
6
|
* database listener) to a pull-based `AsyncIterable` (an SSE source, a GraphQL subscription), with
|
|
@@ -776,401 +57,6 @@ export declare class EventChannel<T> implements AsyncIterable<T> {
|
|
|
776
57
|
*/
|
|
777
58
|
[Symbol.asyncIterator](): AsyncIterator<T>;
|
|
778
59
|
}
|
|
779
|
-
/**
|
|
780
|
-
* Client side of Server-Sent Events: the WHATWG "interpreting an event stream" algorithm over a
|
|
781
|
-
* byte stream (`parseSseStream`), used by `injectSse()` and by tests over real sockets. Import-free.
|
|
782
|
-
*/
|
|
783
|
-
/** A dispatched event. */
|
|
784
|
-
interface SseMessage {
|
|
785
|
-
/** Event type: `message` by default. */
|
|
786
|
-
readonly event: string;
|
|
787
|
-
readonly data: string;
|
|
788
|
-
/** The last event id at dispatch time (what `EventSource` sends back as `Last-Event-ID`). */
|
|
789
|
-
readonly id: string;
|
|
790
|
-
/** `JSON.parse(data)`. */
|
|
791
|
-
json<T = unknown>(): T;
|
|
792
|
-
}
|
|
793
|
-
/** An item of an event stream: an event, a comment (heartbeats are `''`) or a reconnection delay. */
|
|
794
|
-
type SseItem = ({
|
|
795
|
-
readonly type: "event";
|
|
796
|
-
} & SseMessage) | {
|
|
797
|
-
readonly type: "comment";
|
|
798
|
-
readonly text: string;
|
|
799
|
-
} | {
|
|
800
|
-
readonly type: "retry";
|
|
801
|
-
readonly retry: number;
|
|
802
|
-
};
|
|
803
|
-
/** Options of {@link parseSseStream}. */
|
|
804
|
-
interface ParseSseOptions {
|
|
805
|
-
/** Stops reading (the body is cancelled) when aborted. */
|
|
806
|
-
readonly signal?: AbortSignal;
|
|
807
|
-
}
|
|
808
|
-
/**
|
|
809
|
-
* Turns an event stream body (a `fetch()` response body, `injectStream().body`) into its items.
|
|
810
|
-
* UTF-8 is decoded with the BOM stripped; an unterminated event at the end is discarded. Calling
|
|
811
|
-
* `return()` (leaving a `for await` loop) or aborting `signal` cancels the body.
|
|
812
|
-
*
|
|
813
|
-
* ```ts
|
|
814
|
-
* const response = await fetch(`${http.address}/orders/o-1/events`)
|
|
815
|
-
* for await (const item of parseSseStream(response.body!)) {
|
|
816
|
-
* if (item.type === 'event') console.log(item.event, item.json())
|
|
817
|
-
* }
|
|
818
|
-
* ```
|
|
819
|
-
*/
|
|
820
|
-
export declare function parseSseStream(body: ReadableStream<Uint8Array>, options?: ParseSseOptions): AsyncIterableIterator<SseItem>;
|
|
821
|
-
/** Options of {@link InjectSseResponse.next} and {@link InjectSseResponse.take}. */
|
|
822
|
-
interface InjectSseWaitOptions {
|
|
823
|
-
/** Maximum wait for each event (ms). Default: 2 000. */
|
|
824
|
-
readonly timeoutMs?: number;
|
|
825
|
-
}
|
|
826
|
-
/** The response of `injectSse()`: an event stream read in memory, like `EventSource` would. */
|
|
827
|
-
interface InjectSseResponse extends AsyncIterable<SseMessage>, AsyncDisposable {
|
|
828
|
-
readonly status: number;
|
|
829
|
-
readonly headers: Record<string, string | string[]>;
|
|
830
|
-
readonly cookies: Readonly<Record<string, SetCookieInfo>>;
|
|
831
|
-
/** `''` for an event stream; otherwise the whole body, read to its end (an error, a 204...). */
|
|
832
|
-
readonly body: string;
|
|
833
|
-
/** `JSON.parse(body)`. */
|
|
834
|
-
json<T = unknown>(): T;
|
|
835
|
-
/** The next event (comments skipped). Rejects on timeout (default 2 000 ms) or when the stream ended. */
|
|
836
|
-
next(options?: InjectSseWaitOptions): Promise<SseMessage>;
|
|
837
|
-
/** The next `count` events (the timeout applies to each). */
|
|
838
|
-
take(count: number, options?: InjectSseWaitOptions): Promise<SseMessage[]>;
|
|
839
|
-
/** Comments received so far (heartbeats are `''`). */
|
|
840
|
-
readonly comments: readonly string[];
|
|
841
|
-
/** What a browser would send as `Last-Event-ID` on reconnection. */
|
|
842
|
-
readonly lastEventId: string;
|
|
843
|
-
/** The last `retry` received (ms). */
|
|
844
|
-
readonly retry: number | undefined;
|
|
845
|
-
/** Resolves when the server ends the stream (reading the remaining events, which stay available to `next()`). */
|
|
846
|
-
readonly done: Promise<void>;
|
|
847
|
-
/** Disconnects (cancels the body): the server sees a client disconnect. Idempotent. */
|
|
848
|
-
close(): Promise<void>;
|
|
849
|
-
/** Reconnects like `EventSource`: closes this stream and sends the same request plus `last-event-id`. */
|
|
850
|
-
reconnect(): Promise<InjectSseResponse>;
|
|
851
|
-
}
|
|
852
|
-
/**
|
|
853
|
-
* The `trustProxy` option: `false` (default: the headers are ignored), a number of trusted hops
|
|
854
|
-
* (`1`: one load balancer in front of the server), or the addresses of the trusted proxies (IPv4 /
|
|
855
|
-
* IPv6 addresses, CIDR ranges and the aliases `'loopback'`, `'private'`, `'linklocal'`).
|
|
856
|
-
*/
|
|
857
|
-
type TrustProxy = false | number | readonly string[];
|
|
858
|
-
/** HSTS settings (`strict-transport-security`). */
|
|
859
|
-
interface HstsOptions {
|
|
860
|
-
/** `max-age`, in seconds. Default `31536000` (one year). */
|
|
861
|
-
readonly maxAge?: number;
|
|
862
|
-
/** `includeSubDomains`. Default `true`. */
|
|
863
|
-
readonly includeSubDomains?: boolean;
|
|
864
|
-
/** `preload` (needs `includeSubDomains` and a `maxAge` of at least one year). Default `false`. */
|
|
865
|
-
readonly preload?: boolean;
|
|
866
|
-
}
|
|
867
|
-
/** The `securityHeaders` option of `HttpPlugin.configure` (merged over the defaults). */
|
|
868
|
-
interface SecurityHeadersOptions {
|
|
869
|
-
/** `content-security-policy`. Default `"default-src 'none'; frame-ancestors 'none'"`. */
|
|
870
|
-
readonly contentSecurityPolicy?: string | false;
|
|
871
|
-
/** `x-frame-options`. Default `'DENY'`. */
|
|
872
|
-
readonly frameOptions?: "DENY" | "SAMEORIGIN" | false;
|
|
873
|
-
/** `x-content-type-options: nosniff`. Default `true`. */
|
|
874
|
-
readonly noSniff?: boolean;
|
|
875
|
-
/** `referrer-policy` (a policy or a comma-separated fallback list). Default `'no-referrer'`. */
|
|
876
|
-
readonly referrerPolicy?: string | false;
|
|
877
|
-
/** `strict-transport-security`. Default `false`; `true`: `max-age=31536000; includeSubDomains`. */
|
|
878
|
-
readonly hsts?: boolean | HstsOptions;
|
|
879
|
-
/** `cross-origin-resource-policy`. Default `false`. */
|
|
880
|
-
readonly crossOriginResourcePolicy?: "same-origin" | "same-site" | "cross-origin" | false;
|
|
881
|
-
/** `cache-control` of replies that do not set one (`'no-store'` for personal data). Default `false`. */
|
|
882
|
-
readonly cacheControl?: string | false;
|
|
883
|
-
}
|
|
884
|
-
/** How requests whose path ends with an extra `/` are handled. */
|
|
885
|
-
type TrailingSlashPolicy = "strict" | "ignore" | "redirect";
|
|
886
|
-
/** A global guard: a provider class (resolved with the container) or an instance. */
|
|
887
|
-
type GuardRef = GuardClass | CanActivate;
|
|
888
|
-
/** A global interceptor: a provider class or an instance. */
|
|
889
|
-
type InterceptorRef = HttpInterceptorClass | HttpInterceptor;
|
|
890
|
-
/** An exception mapper: a provider class or an instance. */
|
|
891
|
-
type ExceptionMapperRef = ExceptionMapperClass | ExceptionMapper;
|
|
892
|
-
/** Options of {@link HttpPlugin.configure}. */
|
|
893
|
-
interface HttpPluginOptions {
|
|
894
|
-
/** Server implementation. Default: `nodeAdapter()` (built-in, `node:http`). */
|
|
895
|
-
readonly adapter?: HttpAdapter;
|
|
896
|
-
/** Port to listen on (`0` picks a free port). Default: `3000`. */
|
|
897
|
-
readonly port?: number;
|
|
898
|
-
/** Interface to bind. Default: `0.0.0.0`. */
|
|
899
|
-
readonly host?: string;
|
|
900
|
-
/** Path prefix of every route (`/api`). Default: none. */
|
|
901
|
-
readonly prefix?: string;
|
|
902
|
-
/** Guards run before the controller and route guards of every route. */
|
|
903
|
-
readonly guards?: readonly GuardRef[];
|
|
904
|
-
/** Interceptors wrapping every route (outermost first). */
|
|
905
|
-
readonly interceptors?: readonly InterceptorRef[];
|
|
906
|
-
/** Error-to-response mappers, tried in order (see `ExceptionMapper`). */
|
|
907
|
-
readonly exceptionMappers?: readonly ExceptionMapperRef[];
|
|
908
|
-
/**
|
|
909
|
-
* `strict` (default): `/users/` does not match `/users`. `ignore`: it does. `redirect`: it is
|
|
910
|
-
* redirected (308) to `/users`.
|
|
911
|
-
*/
|
|
912
|
-
readonly trailingSlash?: TrailingSlashPolicy;
|
|
913
|
-
/** Send the message of unexpected errors in 500 responses (development only). Default: `false`. */
|
|
914
|
-
readonly exposeErrors?: boolean;
|
|
915
|
-
/**
|
|
916
|
-
* Maximum request body size (413 above): bytes or a size with a unit (`'10mb'`; `kb` = 1024
|
|
917
|
-
* bytes), `Infinity` for no limit. Routes override it with `@BodyLimit`; upload routes default to
|
|
918
|
-
* the sum of their file limits plus this value. Default: 1 MiB.
|
|
919
|
-
*/
|
|
920
|
-
readonly bodyLimit?: ByteSize;
|
|
921
|
-
/** Limits of multipart bodies and defaults of the upload parameters (see `UploadLimits`). */
|
|
922
|
-
readonly uploads?: UploadLimits;
|
|
923
|
-
/**
|
|
924
|
-
* Header carrying the request id: reused when the client sends a valid one (≤ 128 characters of
|
|
925
|
-
* `[A-Za-z0-9_-.:@/+=]`), echoed on every response. `false`: always generate, never echo.
|
|
926
|
-
* Default: `x-request-id`.
|
|
927
|
-
*/
|
|
928
|
-
readonly requestIdHeader?: string | false;
|
|
929
|
-
/** Request id generator. Default: `crypto.randomUUID()`. */
|
|
930
|
-
readonly generateRequestId?: () => string;
|
|
931
|
-
/**
|
|
932
|
-
* Maximum time `onStop` waits for in-flight requests before closing the remaining connections.
|
|
933
|
-
* Keep it below the application `shutdown.hookTimeoutMs`. `Infinity`: no limit of its own (the
|
|
934
|
-
* application's `hookTimeoutMs` still bounds `onStop`). Default: 4 000 ms.
|
|
935
|
-
*/
|
|
936
|
-
readonly drainTimeoutMs?: number;
|
|
937
|
-
/**
|
|
938
|
-
* CORS: preflights are answered before routing and guards (never by a route), and replies to
|
|
939
|
-
* allowed origins get `access-control-allow-origin`, identically on every adapter. Default: off.
|
|
940
|
-
*
|
|
941
|
-
* ```ts
|
|
942
|
-
* cors: { origin: ['https://app.example.com', /^https:\/\/[a-z0-9-]+\.preview\.example\.com$/], credentials: true }
|
|
943
|
-
* ```
|
|
944
|
-
*/
|
|
945
|
-
readonly cors?: CorsOptions | false;
|
|
946
|
-
/**
|
|
947
|
-
* Security headers added to every reply that does not set them (`@Res()` raw replies excepted):
|
|
948
|
-
* `true` (default) for the API defaults, an object merged over them, `false` for none.
|
|
949
|
-
*/
|
|
950
|
-
readonly securityHeaders?: boolean | SecurityHeadersOptions;
|
|
951
|
-
/**
|
|
952
|
-
* Proxies trusted for `x-forwarded-for` / `x-forwarded-proto` (`ctx.ip`, `ctx.protocol`): a number
|
|
953
|
-
* of hops, or addresses, CIDR ranges and the aliases `'loopback'`, `'private'`, `'linklocal'`.
|
|
954
|
-
* Default `false`: the headers are ignored and `ctx.ip` is the adapter's address.
|
|
955
|
-
*/
|
|
956
|
-
readonly trustProxy?: TrustProxy;
|
|
957
|
-
/**
|
|
958
|
-
* What the boot check of route decisions does when a route misses a decision of a required group,
|
|
959
|
-
* or when no guard of its chain enforces its decision (ZNIGHT023): `'error'` (default) fails the
|
|
960
|
-
* boot, `'warn'` logs one warning per group (tests that deliberately run without guards).
|
|
961
|
-
*/
|
|
962
|
-
readonly enforcement?: EnforcementMode;
|
|
963
|
-
/** Cookies: signing keys and defaults (see {@link HttpCookieOptions}). */
|
|
964
|
-
readonly cookies?: HttpCookieOptions;
|
|
965
|
-
/** Server-Sent Events defaults (see {@link HttpSseOptions}). */
|
|
966
|
-
readonly sse?: HttpSseOptions;
|
|
967
|
-
}
|
|
968
|
-
/** The plugin option `cookies`. */
|
|
969
|
-
interface HttpCookieOptions {
|
|
970
|
-
/**
|
|
971
|
-
* HMAC keys of signed cookies (`@SignedCookie`, `{ signed: true }`), at least 32 bytes each: the
|
|
972
|
-
* first signs, all verify (rotation: deploy `[next, current]`, wait for the longest cookie
|
|
973
|
-
* lifetime, then `[next]`). Default: none (signed cookies unavailable).
|
|
974
|
-
*/
|
|
975
|
-
readonly secrets?: string | Uint8Array | readonly (string | Uint8Array)[];
|
|
976
|
-
/**
|
|
977
|
-
* Defaults of every cookie set by the application, merged over the secure defaults
|
|
978
|
-
* `{ path: '/', secure: true, httpOnly: true, sameSite: 'lax' }` (e.g. `{ secure: false }` for
|
|
979
|
-
* plain `http://` development setups on a non-localhost host).
|
|
980
|
-
*/
|
|
981
|
-
readonly defaults?: CookieDefaults;
|
|
982
|
-
}
|
|
983
|
-
/** The plugin option `sse`. */
|
|
984
|
-
interface HttpSseOptions {
|
|
985
|
-
/** Default heartbeat (ms, an integer ≥ 1 000, or `false`). Default: 8 000. */
|
|
986
|
-
readonly heartbeat?: number | false;
|
|
987
|
-
/** Default `retry` sent first (ms). Default: none. */
|
|
988
|
-
readonly retry?: number;
|
|
989
|
-
/**
|
|
990
|
-
* Time an event source gets to finish after `ctx.signal` aborted on shutdown or `maxDuration` (ms),
|
|
991
|
-
* before the stream ends with a jittered `retry`. Must be lower than `drainTimeoutMs`. Default:
|
|
992
|
-
* 1 000.
|
|
993
|
-
*/
|
|
994
|
-
readonly shutdownGraceMs?: number;
|
|
995
|
-
}
|
|
996
|
-
/** A route registered by the plugin (see {@link HttpPlugin.routes}). */
|
|
997
|
-
interface HttpRouteSummary {
|
|
998
|
-
readonly method: string;
|
|
999
|
-
/** Registered path, prefix included. */
|
|
1000
|
-
readonly path: string;
|
|
1001
|
-
/** Controller display name, or the name of the plugin declaring the route. */
|
|
1002
|
-
readonly controller: string;
|
|
1003
|
-
readonly handler: string;
|
|
1004
|
-
/** `'controller'` for compiled routes, the plugin name for plugin routes. */
|
|
1005
|
-
readonly source: string;
|
|
1006
|
-
/** Effective route metadata (the nearest decision of each group). */
|
|
1007
|
-
readonly meta: Readonly<Record<string, unknown>>;
|
|
1008
|
-
/** Display names of the guards, in chain order (global, controller, route). */
|
|
1009
|
-
readonly guards: readonly string[];
|
|
1010
|
-
}
|
|
1011
|
-
/**
|
|
1012
|
-
* The HTTP plugin: serves the controllers compiled into `metadata.extensions.http` with the chosen
|
|
1013
|
-
* adapter.
|
|
1014
|
-
*
|
|
1015
|
-
* ```ts
|
|
1016
|
-
* const app = await Application.create({
|
|
1017
|
-
* metadata: __ZNIGHT_METADATA__,
|
|
1018
|
-
* plugins: [HttpPlugin.configure({ port: 3000, prefix: '/api' })],
|
|
1019
|
-
* })
|
|
1020
|
-
* await app.start()
|
|
1021
|
-
* ```
|
|
1022
|
-
*
|
|
1023
|
-
* Lifecycle: `register` declares the request-scoped externals `HttpContext` and `RequestId`;
|
|
1024
|
-
* `onBootstrap` resolves the options, controllers, guards and interceptors, collects the routes of
|
|
1025
|
-
* other plugins (`httpRoutes()`), checks route decisions and registers the routes; `onStart`
|
|
1026
|
-
* listens; `onStop` stops accepting connections and drains in-flight requests (`drainTimeoutMs`);
|
|
1027
|
-
* `onDispose` closes the server.
|
|
1028
|
-
*/
|
|
1029
|
-
export declare class HttpPlugin implements Plugin {
|
|
1030
|
-
#private;
|
|
1031
|
-
readonly name = "http";
|
|
1032
|
-
private constructor();
|
|
1033
|
-
/**
|
|
1034
|
-
* Creates the plugin. `options` may be a function of the container, evaluated in `onBootstrap`:
|
|
1035
|
-
* `HttpPlugin.configure(c => ({ port: c.get(AppConfig).port }))`.
|
|
1036
|
-
*/
|
|
1037
|
-
static configure(options?: PluginOptions<HttpPluginOptions>): HttpPlugin;
|
|
1038
|
-
/** The adapter (available after `onBootstrap`). */
|
|
1039
|
-
get adapter(): HttpAdapter;
|
|
1040
|
-
/** Address of the server once listening (`String(address)` is its URL), `undefined` otherwise. */
|
|
1041
|
-
get address(): HttpServerAddress | undefined;
|
|
1042
|
-
/**
|
|
1043
|
-
* The path prefix of every route (`''` without prefix, `/api`), for plugins that serve routes or
|
|
1044
|
-
* publish URLs. Readable once the plugin is registered (before or after its `onBootstrap`; at any
|
|
1045
|
-
* time when the options are not a function of the container). Throws the options error when
|
|
1046
|
-
* the options are invalid.
|
|
1047
|
-
*/
|
|
1048
|
-
get prefix(): string;
|
|
1049
|
-
/** Registered routes (after `onBootstrap`), in registration order. */
|
|
1050
|
-
get routes(): readonly HttpRouteSummary[];
|
|
1051
|
-
/** Number of requests being processed. */
|
|
1052
|
-
get inflight(): number;
|
|
1053
|
-
/** Number of open event streams (`@Sse`, `HttpResponse.sse()`): health checks, gauges, stream caps. */
|
|
1054
|
-
get streams(): number;
|
|
1055
|
-
/**
|
|
1056
|
-
* The signed wire value of a cookie (`<encoded value>.<signature>`, signed with the first secret of
|
|
1057
|
-
* `cookies.secrets`): what a browser would send back after `setCookie(name, value, { signed: true })`.
|
|
1058
|
-
* For gateways minting cookies and for tests. Available after `onBootstrap`.
|
|
1059
|
-
*
|
|
1060
|
-
* @throws TypeError for an invalid name or value; Error without `cookies.secrets`.
|
|
1061
|
-
*/
|
|
1062
|
-
signCookie(name: string, value: string): string;
|
|
1063
|
-
/**
|
|
1064
|
-
* The provider classes the plugin resolves in `onBootstrap` although no compiled metadata
|
|
1065
|
-
* references them: the classes of the `guards`, `interceptors` and `exceptionMappers` options
|
|
1066
|
-
* (instances are left out), in that order, without duplicates. `TestApp` from `@znight/testing`
|
|
1067
|
-
* creates them at boot with their dependencies (an async dependency cannot be created on demand);
|
|
1068
|
-
* pass them to `init: { only }` when restricting the boot with `Application.create()`.
|
|
1069
|
-
*
|
|
1070
|
-
* Options given as a function of the container cannot be read before the bootstrap: the result is
|
|
1071
|
-
* then empty. Never throws: invalid options are reported by `onBootstrap`.
|
|
1072
|
-
*/
|
|
1073
|
-
initRoots(): readonly ProviderKey[];
|
|
1074
|
-
/**
|
|
1075
|
-
* Sends a request in memory through the adapter and the whole pipeline (no socket; the server
|
|
1076
|
-
* does not need to listen). Available after `onBootstrap`.
|
|
1077
|
-
*
|
|
1078
|
-
* ```ts
|
|
1079
|
-
* const res = await http.inject({ method: 'POST', url: '/users', body: { name: 'Ada' } })
|
|
1080
|
-
* expect(res.status).toBe(201)
|
|
1081
|
-
* expect(res.json()).toEqual({ id: 1, name: 'Ada' })
|
|
1082
|
-
* ```
|
|
1083
|
-
*/
|
|
1084
|
-
inject(request: InjectRequest): Promise<InjectResponse>;
|
|
1085
|
-
/**
|
|
1086
|
-
* Sends a request in memory and resolves at the response headers, the body being read as it is
|
|
1087
|
-
* produced (`body`). Cancelling `body` (or aborting `signal`) disconnects the client. Requires an
|
|
1088
|
-
* adapter implementing `injectStream()` (the four built-in ones do).
|
|
1089
|
-
*/
|
|
1090
|
-
injectStream(request: InjectStreamOptions): Promise<InjectStreamResponse>;
|
|
1091
|
-
/**
|
|
1092
|
-
* Opens an event stream in memory (`accept: text/event-stream`) and reads its events like
|
|
1093
|
-
* `EventSource`: `next()`, `take()`, `comments`, `retry`, `lastEventId`, `reconnect()`, `close()`
|
|
1094
|
-
* (the server sees a client disconnect). A response that is not an event stream (404, 204...) is
|
|
1095
|
-
* read to its end (`body`). Resolves at the first chunk of the stream (an `@Sse` stream sends
|
|
1096
|
-
* `retry` or a comment at once), or after 2 000 ms for a stream that sends nothing yet; rejects,
|
|
1097
|
-
* naming the request (which is aborted), when no response headers arrive within 2 000 ms.
|
|
1098
|
-
*
|
|
1099
|
-
* ```ts
|
|
1100
|
-
* await using stream = await http.injectSse({ url: '/orders/o-1/events' })
|
|
1101
|
-
* const event = await stream.next() // rejects after 2 000 ms
|
|
1102
|
-
* expect(event.json()).toMatchObject({ status: 'paid' })
|
|
1103
|
-
* ```
|
|
1104
|
-
*/
|
|
1105
|
-
injectSse(request: InjectStreamOptions): Promise<InjectSseResponse>;
|
|
1106
|
-
register(ctx: PluginContext): void;
|
|
1107
|
-
onBootstrap(ctx: PluginContext): void;
|
|
1108
|
-
onStart(ctx: PluginContext): Promise<void>;
|
|
1109
|
-
onStop(ctx: PluginContext): Promise<void>;
|
|
1110
|
-
onDispose(): Promise<void>;
|
|
1111
|
-
}
|
|
1112
|
-
/**
|
|
1113
|
-
* A route served by `HttpPlugin` on behalf of another plugin. It runs through the same pipeline as
|
|
1114
|
-
* controller routes: `http:request` hook, request scope, global guards, then its own `guards`, the
|
|
1115
|
-
* handler, the exception mappers; global interceptors do not wrap it. The handler result is
|
|
1116
|
-
* serialised like a controller result (`HttpResponse`, objects as JSON, strings, streams...).
|
|
1117
|
-
*/
|
|
1118
|
-
interface PluginRoute {
|
|
1119
|
-
/** `GET` routes also answer `HEAD`; `ALL` registers every method without its own route. */
|
|
1120
|
-
readonly method: HttpMethod;
|
|
1121
|
-
/** Path relative to the `HttpPlugin` prefix, with the controller grammar (`/openapi.json`, `/docs/_assets/*`). */
|
|
1122
|
-
readonly path: string;
|
|
1123
|
-
/** Handler name, shown in `HttpPlugin.routes`, logs and errors (`document`). */
|
|
1124
|
-
readonly name: string;
|
|
1125
|
-
readonly handler: (ctx: HttpContext) => unknown;
|
|
1126
|
-
/**
|
|
1127
|
-
* Route metadata (`[Public.meta()]`): read by guards (`Public.get(ctx)`) and by the boot check of
|
|
1128
|
-
* required route decorator groups. One entry per group.
|
|
1129
|
-
*/
|
|
1130
|
-
readonly meta?: readonly RouteMetaEntry[];
|
|
1131
|
-
/** Guards of the route (provider classes or instances), run after the global guards. */
|
|
1132
|
-
readonly guards?: readonly GuardRef[];
|
|
1133
|
-
/**
|
|
1134
|
-
* Maximum body size of this route (`'100kb'`, `1048576`, `Infinity`), with the grammar of the
|
|
1135
|
-
* `HttpPlugin` option `bodyLimit`, which it replaces for this route only (lower or higher, like
|
|
1136
|
-
* `@BodyLimit` on a controller route). Past it, reading the body answers 413. Default: the
|
|
1137
|
-
* `HttpPlugin` option.
|
|
1138
|
-
*/
|
|
1139
|
-
readonly bodyLimit?: ByteSize;
|
|
1140
|
-
}
|
|
1141
|
-
/** What {@link PluginHttpRoutes.httpRoutes} receives. */
|
|
1142
|
-
interface PluginHttpRoutesContext {
|
|
1143
|
-
/** The `HttpPlugin` prefix (`''` or `/api`), already applied to the paths of the routes. */
|
|
1144
|
-
readonly prefix: string;
|
|
1145
|
-
/** The application container (initialised). */
|
|
1146
|
-
readonly container: Container;
|
|
1147
|
-
}
|
|
1148
|
-
/**
|
|
1149
|
-
* Implemented by a plugin that serves HTTP routes. `HttpPlugin` calls `httpRoutes()` once in its
|
|
1150
|
-
* `onBootstrap`, after `container.init()` and whatever the plugin order, then registers the routes
|
|
1151
|
-
* on its adapter. A path that breaks the grammar, or that collides with another route, is ZNIGHT007
|
|
1152
|
-
* naming the plugin.
|
|
1153
|
-
*
|
|
1154
|
-
* ```ts
|
|
1155
|
-
* export class StatusPlugin implements Plugin, PluginHttpRoutes {
|
|
1156
|
-
* readonly name = 'status'
|
|
1157
|
-
* httpRoutes(): readonly PluginRoute[] {
|
|
1158
|
-
* return [{ method: 'GET', path: '/status', name: 'status', handler: () => ({ up: true }), meta: [Public.meta()] }]
|
|
1159
|
-
* }
|
|
1160
|
-
* }
|
|
1161
|
-
* ```
|
|
1162
|
-
*/
|
|
1163
|
-
interface PluginHttpRoutes {
|
|
1164
|
-
httpRoutes(context: PluginHttpRoutesContext): readonly PluginRoute[];
|
|
1165
|
-
/**
|
|
1166
|
-
* Called once when `HttpPlugin` starts draining (its `onStop`, whatever the plugin order), after
|
|
1167
|
-
* it stopped accepting connections and started closing its event streams: end the long-lived
|
|
1168
|
-
* responses `HttpPlugin` does not know of (a stream another server produced), so that the drain
|
|
1169
|
-
* does not wait for them until `drainTimeoutMs`. Requests still in flight are served. The drain
|
|
1170
|
-
* waits for a returned promise too (within `drainTimeoutMs`); a failure is logged.
|
|
1171
|
-
*/
|
|
1172
|
-
onHttpDrain?(): void | Promise<void>;
|
|
1173
|
-
}
|
|
1174
60
|
/** A parsed `content-type` header. */
|
|
1175
61
|
interface MediaType {
|
|
1176
62
|
/** Lower-case `type/subtype` without parameters. */
|
|
@@ -1290,5 +176,5 @@ export declare function isThenable<T>(value: T): value is unknown extends T ? T
|
|
|
1290
176
|
* @throws TypeError when `schema` is not a Standard Schema v1 (a programming error → 500).
|
|
1291
177
|
*/
|
|
1292
178
|
export declare function validate<S extends StandardSchemaV1>(schema: S, value: unknown, location: string): InferSchemaOutput<S> | Promise<InferSchemaOutput<S>>;
|
|
1293
|
-
export { type AdapterCloseOptions, type AdapterHandler, type AdapterReply, type AdapterRequest, type AdapterRoute, BadGatewayException, BadRequestException, type ByteRange, type ByteSize, type CallHandler, type CanActivate, type ClearCookieOptions, type Coercer, type Coercers, ConflictException, type ContentDispositionType, type ControllerOptions, type CookieDefaults, type CookieOptions, type CorsOptions, type CorsOrigin, type EnforcementMode, type EventChannelOptions, type ExceptionMapper, type ExceptionMapperClass, type ExceptionMapperRef, type ExceptionMapperResult, type FallbackHandler, type FileBody, type FileResponseOptions, ForbiddenException, GatewayTimeoutException, GoneException, type GuardClass, type GuardEnforcement, type GuardRef, type HeaderValue, type HstsOptions, type HttpAdapter, type HttpContext, type HttpControllerDefinition, type HttpCookieOptions, type HttpErrorBody, HttpException, type HttpExceptionOptions, type HttpInterceptor, type HttpInterceptorClass, type HttpMetadata, type HttpMethod, type HttpParamDefinition, type HttpParamSource, type HttpPluginOptions, type HttpResponseInit, type HttpRouteDefinition, type HttpRouteGroupDefinition, type HttpRouteInfo, type HttpRouteSummary, type HttpServerAddress, type HttpSseDefinition, type HttpSseOptions, type HttpUploadDefinition, type InferSchemaInput, type InferSchemaOutput, type InjectRequest, type InjectResponse, type InjectSseResponse, type InjectSseWaitOptions, type InjectStreamOptions, type InjectStreamRequest, type InjectStreamResponse, type InterceptorRef, InternalServerErrorException, type MediaType, MethodNotAllowedException, type NodeAdapter, type NodeAdapterOptions, NotFoundException, NotImplementedException, type ParseSseOptions, PayloadTooLargeException, type PluginHttpRoutes, type PluginHttpRoutesContext, type PluginRoute, type QueryRecord, type RedirectStatus, type ReplyBody, type RequestAbortKind, RequestAbortedError, type RequestId, type ResponseCookie, type RouteDecorator, type RouteDecoratorArgs, type RouteDecoratorInfo, type RouteDecoratorOptions, type RouteMetaCarrier, type RouteMetaEntry, type SecurityHeadersOptions, ServiceUnavailableException, type SetCookieInfo, type SseBody, type SseEvent, type SseItem, type SseMessage, type SseOptions, type SseSource, type StandardSchemaV1, type StandardSchemaV1FailureResult, type StandardSchemaV1Issue, type StandardSchemaV1Options, type StandardSchemaV1PathSegment, type StandardSchemaV1Props, type StandardSchemaV1Result, type StandardSchemaV1SuccessResult, type StandardSchemaV1Types, type StreamResponseOptions, type StreamSource, TooManyRequestsException, type TrailingSlashPolicy, type TrustProxy, UnauthorizedException, UnprocessableEntityException, UnsupportedMediaTypeException, type UploadLimits, type UploadedFileOptions, type UploadedFilesOptions, type Uploads, ValidationException, type ValidationIssueDetail, formatByteSize, isHttpException, isStandardSchema, nodeAdapter, parseByteSize, parseCookieHeader, parseQuery, parseSetCookie, serializeClearCookie, serializeCookie, toIssueDetail, uploads };
|
|
179
|
+
export { type AdapterCloseOptions, type AdapterHandler, type AdapterReply, type AdapterRequest, type AdapterRoute, type AdapterUpgrades, All, BadGatewayException, BadRequestException, Body, BodyLimit, type BunSocketHandlers, type BunUpgrade, type ByteRange, type ByteSize, type CallHandler, type CanActivate, type ClearCookieOptions, type Coercer, type Coercers, ConflictException, type ContentDispositionType, Controller, type ControllerOptions, Cookie, type CookieDefaults, type CookieOptions, type CorsOptions, type CorsOrigin, Ctx, Delete, type EnforcementMode, type EventChannelOptions, type ExceptionMapper, type ExceptionMapperClass, type ExceptionMapperRef, type ExceptionMapperResult, type FallbackHandler, type FileBody, type FileResponseOptions, ForbiddenException, GatewayTimeoutException, Get, GoneException, type GuardClass, type GuardEnforcement, type GuardRef, Head, Header, type HeaderValue, Headers, type HstsOptions, type HttpAdapter, HttpCode, type HttpContext, type HttpControllerDefinition, type HttpCookieOptions, type HttpErrorBody, HttpException, type HttpExceptionOptions, type HttpInterceptor, type HttpInterceptorClass, type HttpMetadata, type HttpMethod, type HttpParamDefinition, type HttpParamSource, HttpPlugin, type HttpPluginOptions, HttpResponse, type HttpResponseInit, type HttpRouteDefinition, type HttpRouteGroupDefinition, type HttpRouteInfo, type HttpRouteSummary, type HttpServerAddress, type HttpSseDefinition, type HttpSseOptions, type HttpUploadDefinition, type InferSchemaInput, type InferSchemaOutput, type InjectRequest, type InjectResponse, type InjectSseResponse, type InjectSseWaitOptions, type InjectStreamOptions, type InjectStreamRequest, type InjectStreamResponse, type InterceptorRef, InternalServerErrorException, type MediaType, type MemoryUpgrade, MethodNotAllowedException, type NativeUpgrade, type NodeAdapter, type NodeAdapterOptions, type NodeUpgrade, NotFoundException, NotImplementedException, Options, Param, type ParseSseOptions, Patch, PayloadTooLargeException, type PendingUpgrade, type PluginHttpRoutes, type PluginHttpRoutesContext, type PluginHttpUpgrades, type PluginHttpUpgradesContext, type PluginRoute, type PluginUpgradeRoute, type PluginUpgrades, Post, Put, Query, type QueryRecord, type RedirectStatus, type ReplyBody, Req, type RequestAbortKind, RequestAbortedError, type RequestId, Res, type ResponseCookie, type RouteDecorator, type RouteDecoratorArgs, type RouteDecoratorInfo, type RouteDecoratorOptions, type RouteMetaCarrier, type RouteMetaEntry, type SecurityHeadersOptions, ServiceUnavailableException, type SetCookieInfo, SignedCookie, Sse, type SseBody, type SseEvent, type SseItem, type SseMessage, type SseOptions, type SseSource, type StandardSchemaV1, type StandardSchemaV1FailureResult, type StandardSchemaV1Issue, type StandardSchemaV1Options, type StandardSchemaV1PathSegment, type StandardSchemaV1Props, type StandardSchemaV1Result, type StandardSchemaV1SuccessResult, type StandardSchemaV1Types, type StreamResponseOptions, type StreamSource, TooManyRequestsException, type TrailingSlashPolicy, type TrustProxy, UnauthorizedException, UnprocessableEntityException, UnsupportedMediaTypeException, type UpgradeHandlerOptions, type UploadLimits, UploadedFile, type UploadedFileOptions, UploadedFiles, type UploadedFilesOptions, type Uploads, UseGuard, UseInterceptor, Validate, ValidationException, type ValidationIssueDetail, contentDisposition, createRouteDecorator, encodeSseEvent, formatByteSize, isHttpException, isHttpResponse, isStandardSchema, nodeAdapter, parseByteSize, parseCookieHeader, parseQuery, parseRange, parseSetCookie, parseSseStream, serializeClearCookie, serializeCookie, toIssueDetail, uploads };
|
|
1294
180
|
//# sourceMappingURL=index.d.mts.map
|