@znight/http 0.0.0-stage → 0.1.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.
Files changed (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2333 -2
  3. package/dist/adapter-CGSceVEN.d.mts +876 -0
  4. package/dist/compiler.d.mts +156 -0
  5. package/dist/compiler.mjs +2180 -0
  6. package/dist/compiler.mjs.map +1 -0
  7. package/dist/express.d.mts +39 -0
  8. package/dist/express.mjs +135 -0
  9. package/dist/express.mjs.map +1 -0
  10. package/dist/fastify.d.mts +33 -0
  11. package/dist/fastify.mjs +191 -0
  12. package/dist/fastify.mjs.map +1 -0
  13. package/dist/hono.d.mts +49 -0
  14. package/dist/hono.mjs +319 -0
  15. package/dist/hono.mjs.map +1 -0
  16. package/dist/index.d.mts +1254 -0
  17. package/dist/index.mjs +5618 -0
  18. package/dist/index.mjs.map +1 -0
  19. package/dist/node-BUnkdSsa.mjs +183 -0
  20. package/dist/node-BUnkdSsa.mjs.map +1 -0
  21. package/dist/node-io-BY1ZDLRQ.mjs +1566 -0
  22. package/dist/node-io-BY1ZDLRQ.mjs.map +1 -0
  23. package/dist/node.d.mts +30 -0
  24. package/dist/node.mjs +1 -0
  25. package/dist/path-JI87C4k8.d.mts +109 -0
  26. package/dist/path.d.mts +2 -0
  27. package/dist/path.mjs +245 -0
  28. package/dist/path.mjs.map +1 -0
  29. package/dist/size-D8rsQcV0.mjs +184 -0
  30. package/dist/size-D8rsQcV0.mjs.map +1 -0
  31. package/dist/utils-DCbu3a4w.mjs +27 -0
  32. package/dist/utils-DCbu3a4w.mjs.map +1 -0
  33. package/package.json +142 -4
  34. package/src/adapter.ts +240 -0
  35. package/src/adapters/express.ts +201 -0
  36. package/src/adapters/fastify.ts +280 -0
  37. package/src/adapters/hono.ts +488 -0
  38. package/src/adapters/node-io.ts +368 -0
  39. package/src/adapters/node.ts +135 -0
  40. package/src/adapters/shared.ts +508 -0
  41. package/src/body.ts +135 -0
  42. package/src/coerce.ts +195 -0
  43. package/src/compiler/analyze.ts +1513 -0
  44. package/src/compiler/ast.ts +90 -0
  45. package/src/compiler/coercion.ts +267 -0
  46. package/src/compiler/generate.ts +374 -0
  47. package/src/compiler/index.ts +76 -0
  48. package/src/compiler/model.ts +260 -0
  49. package/src/compiler/route-decorators.ts +405 -0
  50. package/src/compiler/validate.ts +288 -0
  51. package/src/context.ts +533 -0
  52. package/src/cookie-settings.ts +68 -0
  53. package/src/cookie-signer.ts +106 -0
  54. package/src/cookies.ts +726 -0
  55. package/src/cors.ts +349 -0
  56. package/src/decisions.ts +331 -0
  57. package/src/decorators.ts +486 -0
  58. package/src/event-channel.ts +197 -0
  59. package/src/exceptions.ts +257 -0
  60. package/src/files.ts +429 -0
  61. package/src/headers.ts +39 -0
  62. package/src/index.ts +185 -0
  63. package/src/inject-sse.ts +309 -0
  64. package/src/interfaces.ts +101 -0
  65. package/src/metadata.ts +162 -0
  66. package/src/mime.ts +157 -0
  67. package/src/multipart.ts +597 -0
  68. package/src/option-error.ts +31 -0
  69. package/src/path.ts +288 -0
  70. package/src/pipeline.ts +918 -0
  71. package/src/plugin-routes.ts +188 -0
  72. package/src/plugin.ts +1432 -0
  73. package/src/proxy.ts +236 -0
  74. package/src/query.ts +30 -0
  75. package/src/response.ts +240 -0
  76. package/src/router.ts +119 -0
  77. package/src/security-headers.ts +161 -0
  78. package/src/serialize.ts +264 -0
  79. package/src/size.ts +66 -0
  80. package/src/sse-parse.ts +194 -0
  81. package/src/sse.ts +650 -0
  82. package/src/standard-schema.ts +81 -0
  83. package/src/status.ts +78 -0
  84. package/src/stream.ts +200 -0
  85. package/src/tokens.ts +21 -0
  86. package/src/uploads.ts +176 -0
  87. package/src/utils.ts +31 -0
  88. package/src/validate.ts +65 -0
@@ -0,0 +1,1254 @@
1
+ import { $ as PayloadTooLargeException, A as UploadedFileOptions, B as BadRequestException, C as HttpUploadDefinition, Ct as CookieOptions, D as RequestAbortedError, Dt as parseSetCookie, E as RequestAbortKind, Et as parseCookieHeader, F as formatByteSize, G as HeaderValue, H as ForbiddenException, I as parseByteSize, J as HttpExceptionOptions, K as HttpErrorBody, L as QueryRecord, M as Uploads, N as uploads, O as RequestId, Ot as serializeClearCookie, P as ByteSize, Q as NotImplementedException, R as parseQuery, S as HttpSseDefinition, St as CookieDefaults, T as HttpRouteInfo, Tt as SetCookieInfo, U as GatewayTimeoutException, V as ConflictException, W as GoneException, X as MethodNotAllowedException, Y as InternalServerErrorException, Z as NotFoundException, _ as HttpMethod, _t as StandardSchemaV1Result, a as AdapterRoute, at as ValidationException, b as HttpRouteDefinition, bt as isStandardSchema, c as HttpServerAddress, ct as toIssueDetail, d as InjectStreamOptions, dt as StandardSchemaV1, et as ServiceUnavailableException, f as InjectStreamRequest, ft as StandardSchemaV1FailureResult, g as HttpMetadata, gt as StandardSchemaV1Props, h as HttpControllerDefinition, ht as StandardSchemaV1PathSegment, i as AdapterRequest, it as UnsupportedMediaTypeException, j as UploadedFilesOptions, k as UploadLimits, kt as serializeCookie, l as InjectRequest, lt as InferSchemaInput, m as ReplyBody, mt as StandardSchemaV1Options, n as AdapterHandler, nt as UnauthorizedException, o as FallbackHandler, ot as ValidationIssueDetail, p as InjectStreamResponse, pt as StandardSchemaV1Issue, q as HttpException, r as AdapterReply, rt as UnprocessableEntityException, s as HttpAdapter, st as isHttpException, t as AdapterCloseOptions, tt as TooManyRequestsException, u as InjectResponse, ut as InferSchemaOutput, v as HttpParamDefinition, vt as StandardSchemaV1SuccessResult, w as HttpContext, wt as ResponseCookie, x as HttpRouteGroupDefinition, xt as ClearCookieOptions, y as HttpParamSource, yt as StandardSchemaV1Types, z as BadGatewayException } from "./adapter-CGSceVEN.mjs";
2
+ import { NodeAdapter, NodeAdapterOptions, nodeAdapter } from "./node.mjs";
3
+ import { ClassMarker, Container, MethodMarker, ParameterMarker, Plugin, PluginContext, PluginOptions, ProviderKey, Scope, TypedToken } from "@znight/core";
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
+ /** A user-defined route metadata decorator created with {@link createRouteDecorator}. */
658
+ interface RouteDecorator<T> extends RouteDecoratorInfo {
659
+ (...args: RouteDecoratorArgs<T>): ClassMarker & MethodMarker;
660
+ /**
661
+ * The value of the current route, if set. Reads the effective metadata: the route's decision of
662
+ * a group wins over the controller's.
663
+ */
664
+ get(ctx: HttpContext): T | undefined;
665
+ /**
666
+ * The same metadata as data (`{ key, value, group, required }`), for routes that are not compiled
667
+ * controllers (plugin routes: `OpenApiPlugin.configure({ meta: [Public.meta()] })`). The value is
668
+ * `true` when omitted, like `@Public()`.
669
+ */
670
+ meta(...args: RouteDecoratorArgs<T>): RouteMetaEntry<T>;
671
+ }
672
+ /**
673
+ * Creates a metadata decorator for controllers and routes. The compiler stores its argument
674
+ * (`value`, statically evaluated or an exported reference; `true` when omitted) in
675
+ * `meta[key]`, read by guards and interceptors through `ctx.route.meta` or `decorator.get(ctx)`.
676
+ *
677
+ * ```ts
678
+ * export const Public = createRouteDecorator('public', { group: 'access', required: true })
679
+ * export const Authenticated = createRouteDecorator('authenticated', { group: 'access', required: true })
680
+ * export const Roles = createRouteDecorator<readonly string[]>('roles', { group: 'access', required: true })
681
+ *
682
+ * @Controller('/books')
683
+ * @Roles(['reader']) // the decision of every route of the controller...
684
+ * export class BookController {
685
+ * @Get('/:id/cover')
686
+ * @Public() // ...unless the route has its own decision of the group
687
+ * cover() {}
688
+ * }
689
+ *
690
+ * // in a guard
691
+ * if (Public.get(ctx)) return true
692
+ * const roles = Roles.get(ctx) ?? []
693
+ * ```
694
+ *
695
+ * Decorators of one `group` are alternatives: two of them on the same class or method are
696
+ * ZNIGHT005, and the route's decision replaces the controller's (without `group`, each decorator is
697
+ * its own group: the route value wins over the controller value). With `required: true`, a route
698
+ * without decision fails the build (ZNIGHT023).
699
+ *
700
+ * @param key static: unique metadata key.
701
+ * @param options static: `{ group?, required? }`.
702
+ */
703
+ export declare function createRouteDecorator<T = true>(key: string, options?: RouteDecoratorOptions): RouteDecorator<T>;
704
+ /** How the boot check reports problems. */
705
+ type EnforcementMode = "error" | "warn";
706
+ /**
707
+ * `EventChannel`: a bounded bridge from push-based producers (an event bus, an `EventEmitter`, a
708
+ * database listener) to a pull-based `AsyncIterable` (an SSE source, a GraphQL subscription), with
709
+ * no dependency.
710
+ */
711
+ /** Options of {@link EventChannel}. */
712
+ interface EventChannelOptions {
713
+ /** Buffered items before `overflow` applies. Default: 1 000. */
714
+ readonly capacity?: number;
715
+ /**
716
+ * What happens when an item is pushed into a full buffer:
717
+ * - `'drop-oldest'` (default): the oldest buffered item is discarded;
718
+ * - `'drop-newest'`: the item is discarded (`push()` returns false);
719
+ * - `'close'`: the item is discarded and the channel closes after the buffered items (an SSE
720
+ * client then reconnects and resumes with `Last-Event-ID`).
721
+ */
722
+ readonly overflow?: "drop-oldest" | "drop-newest" | "close";
723
+ /** Closes the channel when aborted (pass `ctx.signal`). */
724
+ readonly signal?: AbortSignal;
725
+ }
726
+ /**
727
+ * A bounded, single-consumer queue that is an `AsyncIterable`: producers `push()`, the consumer
728
+ * iterates. A pending `next()` resolves with `{ done: true }` as soon as the channel closes for any
729
+ * reason, so an SSE handler built on a channel always stops when its client leaves.
730
+ *
731
+ * ```ts
732
+ * const channel = new EventChannel<SseEvent<Order>>({ capacity: 256, overflow: 'close', signal: ctx.signal })
733
+ * const unsubscribe = bus.on('order', order => channel.push({ event: 'order', data: order }))
734
+ * channel.onClose(unsubscribe)
735
+ * return channel // from an @Sse handler
736
+ * ```
737
+ */
738
+ export declare class EventChannel<T> implements AsyncIterable<T> {
739
+ #private;
740
+ constructor(options?: EventChannelOptions);
741
+ /** Buffered items. */
742
+ get size(): number;
743
+ /** Whether the channel no longer accepts items (closed, failed, overflowed, aborted, returned). */
744
+ get closed(): boolean;
745
+ /** Buffers an item (or hands it to a waiting consumer). Returns false when it was dropped or the channel is closed. */
746
+ push(item: T): boolean;
747
+ /** Ends the iteration after the buffered items. Idempotent. */
748
+ close(): void;
749
+ /** Makes the iteration throw `error` after the buffered items (ignored once closed). */
750
+ fail(error: unknown): void;
751
+ /**
752
+ * Calls `listener` once when the channel closes (close, fail, overflow, signal, consumer
753
+ * `return()`); immediately when it is already closed.
754
+ */
755
+ onClose(listener: () => void): void;
756
+ /**
757
+ * The iterator of the single consumer (a second call throws). Its `return()` closes the channel
758
+ * and resolves at once, even while a `next()` waits.
759
+ */
760
+ [Symbol.asyncIterator](): AsyncIterator<T>;
761
+ }
762
+ /**
763
+ * Client side of Server-Sent Events: the WHATWG "interpreting an event stream" algorithm over a
764
+ * byte stream (`parseSseStream`), used by `injectSse()` and by tests over real sockets. Import-free.
765
+ */
766
+ /** A dispatched event. */
767
+ interface SseMessage {
768
+ /** Event type: `message` by default. */
769
+ readonly event: string;
770
+ readonly data: string;
771
+ /** The last event id at dispatch time (what `EventSource` sends back as `Last-Event-ID`). */
772
+ readonly id: string;
773
+ /** `JSON.parse(data)`. */
774
+ json<T = unknown>(): T;
775
+ }
776
+ /** An item of an event stream: an event, a comment (heartbeats are `''`) or a reconnection delay. */
777
+ type SseItem = ({
778
+ readonly type: "event";
779
+ } & SseMessage) | {
780
+ readonly type: "comment";
781
+ readonly text: string;
782
+ } | {
783
+ readonly type: "retry";
784
+ readonly retry: number;
785
+ };
786
+ /** Options of {@link parseSseStream}. */
787
+ interface ParseSseOptions {
788
+ /** Stops reading (the body is cancelled) when aborted. */
789
+ readonly signal?: AbortSignal;
790
+ }
791
+ /**
792
+ * Turns an event stream body (a `fetch()` response body, `injectStream().body`) into its items.
793
+ * UTF-8 is decoded with the BOM stripped; an unterminated event at the end is discarded. Calling
794
+ * `return()` (leaving a `for await` loop) or aborting `signal` cancels the body.
795
+ *
796
+ * ```ts
797
+ * const response = await fetch(`${http.address}/orders/o-1/events`)
798
+ * for await (const item of parseSseStream(response.body!)) {
799
+ * if (item.type === 'event') console.log(item.event, item.json())
800
+ * }
801
+ * ```
802
+ */
803
+ export declare function parseSseStream(body: ReadableStream<Uint8Array>, options?: ParseSseOptions): AsyncIterableIterator<SseItem>;
804
+ /** Options of {@link InjectSseResponse.next} and {@link InjectSseResponse.take}. */
805
+ interface InjectSseWaitOptions {
806
+ /** Maximum wait for each event (ms). Default: 2 000. */
807
+ readonly timeoutMs?: number;
808
+ }
809
+ /** The response of `injectSse()`: an event stream read in memory, like `EventSource` would. */
810
+ interface InjectSseResponse extends AsyncIterable<SseMessage>, AsyncDisposable {
811
+ readonly status: number;
812
+ readonly headers: Record<string, string | string[]>;
813
+ readonly cookies: Readonly<Record<string, SetCookieInfo>>;
814
+ /** `''` for an event stream; otherwise the whole body, read to its end (an error, a 204...). */
815
+ readonly body: string;
816
+ /** `JSON.parse(body)`. */
817
+ json<T = unknown>(): T;
818
+ /** The next event (comments skipped). Rejects on timeout (default 2 000 ms) or when the stream ended. */
819
+ next(options?: InjectSseWaitOptions): Promise<SseMessage>;
820
+ /** The next `count` events (the timeout applies to each). */
821
+ take(count: number, options?: InjectSseWaitOptions): Promise<SseMessage[]>;
822
+ /** Comments received so far (heartbeats are `''`). */
823
+ readonly comments: readonly string[];
824
+ /** What a browser would send as `Last-Event-ID` on reconnection. */
825
+ readonly lastEventId: string;
826
+ /** The last `retry` received (ms). */
827
+ readonly retry: number | undefined;
828
+ /** Resolves when the server ends the stream (reading the remaining events, which stay available to `next()`). */
829
+ readonly done: Promise<void>;
830
+ /** Disconnects (cancels the body): the server sees a client disconnect. Idempotent. */
831
+ close(): Promise<void>;
832
+ /** Reconnects like `EventSource`: closes this stream and sends the same request plus `last-event-id`. */
833
+ reconnect(): Promise<InjectSseResponse>;
834
+ }
835
+ /**
836
+ * The `trustProxy` option: `false` (default: the headers are ignored), a number of trusted hops
837
+ * (`1`: one load balancer in front of the server), or the addresses of the trusted proxies (IPv4 /
838
+ * IPv6 addresses, CIDR ranges and the aliases `'loopback'`, `'private'`, `'linklocal'`).
839
+ */
840
+ type TrustProxy = false | number | readonly string[];
841
+ /** HSTS settings (`strict-transport-security`). */
842
+ interface HstsOptions {
843
+ /** `max-age`, in seconds. Default `31536000` (one year). */
844
+ readonly maxAge?: number;
845
+ /** `includeSubDomains`. Default `true`. */
846
+ readonly includeSubDomains?: boolean;
847
+ /** `preload` (needs `includeSubDomains` and a `maxAge` of at least one year). Default `false`. */
848
+ readonly preload?: boolean;
849
+ }
850
+ /** The `securityHeaders` option of `HttpPlugin.configure` (merged over the defaults). */
851
+ interface SecurityHeadersOptions {
852
+ /** `content-security-policy`. Default `"default-src 'none'; frame-ancestors 'none'"`. */
853
+ readonly contentSecurityPolicy?: string | false;
854
+ /** `x-frame-options`. Default `'DENY'`. */
855
+ readonly frameOptions?: "DENY" | "SAMEORIGIN" | false;
856
+ /** `x-content-type-options: nosniff`. Default `true`. */
857
+ readonly noSniff?: boolean;
858
+ /** `referrer-policy` (a policy or a comma-separated fallback list). Default `'no-referrer'`. */
859
+ readonly referrerPolicy?: string | false;
860
+ /** `strict-transport-security`. Default `false`; `true`: `max-age=31536000; includeSubDomains`. */
861
+ readonly hsts?: boolean | HstsOptions;
862
+ /** `cross-origin-resource-policy`. Default `false`. */
863
+ readonly crossOriginResourcePolicy?: "same-origin" | "same-site" | "cross-origin" | false;
864
+ /** `cache-control` of replies that do not set one (`'no-store'` for personal data). Default `false`. */
865
+ readonly cacheControl?: string | false;
866
+ }
867
+ /** How requests whose path ends with an extra `/` are handled. */
868
+ type TrailingSlashPolicy = "strict" | "ignore" | "redirect";
869
+ /** A global guard: a provider class (resolved with the container) or an instance. */
870
+ type GuardRef = GuardClass | CanActivate;
871
+ /** A global interceptor: a provider class or an instance. */
872
+ type InterceptorRef = HttpInterceptorClass | HttpInterceptor;
873
+ /** An exception mapper: a provider class or an instance. */
874
+ type ExceptionMapperRef = ExceptionMapperClass | ExceptionMapper;
875
+ /** Options of {@link HttpPlugin.configure}. */
876
+ interface HttpPluginOptions {
877
+ /** Server implementation. Default: `nodeAdapter()` (built-in, `node:http`). */
878
+ readonly adapter?: HttpAdapter;
879
+ /** Port to listen on (`0` picks a free port). Default: `3000`. */
880
+ readonly port?: number;
881
+ /** Interface to bind. Default: `0.0.0.0`. */
882
+ readonly host?: string;
883
+ /** Path prefix of every route (`/api`). Default: none. */
884
+ readonly prefix?: string;
885
+ /** Guards run before the controller and route guards of every route. */
886
+ readonly guards?: readonly GuardRef[];
887
+ /** Interceptors wrapping every route (outermost first). */
888
+ readonly interceptors?: readonly InterceptorRef[];
889
+ /** Error-to-response mappers, tried in order (see `ExceptionMapper`). */
890
+ readonly exceptionMappers?: readonly ExceptionMapperRef[];
891
+ /**
892
+ * `strict` (default): `/users/` does not match `/users`. `ignore`: it does. `redirect`: it is
893
+ * redirected (308) to `/users`.
894
+ */
895
+ readonly trailingSlash?: TrailingSlashPolicy;
896
+ /** Send the message of unexpected errors in 500 responses (development only). Default: `false`. */
897
+ readonly exposeErrors?: boolean;
898
+ /**
899
+ * Maximum request body size (413 above): bytes or a size with a unit (`'10mb'`; `kb` = 1024
900
+ * bytes), `Infinity` for no limit. Routes override it with `@BodyLimit`; upload routes default to
901
+ * the sum of their file limits plus this value. Default: 1 MiB.
902
+ */
903
+ readonly bodyLimit?: ByteSize;
904
+ /** Limits of multipart bodies and defaults of the upload parameters (see `UploadLimits`). */
905
+ readonly uploads?: UploadLimits;
906
+ /**
907
+ * Header carrying the request id: reused when the client sends a valid one (≤ 128 characters of
908
+ * `[A-Za-z0-9_-.:@/+=]`), echoed on every response. `false`: always generate, never echo.
909
+ * Default: `x-request-id`.
910
+ */
911
+ readonly requestIdHeader?: string | false;
912
+ /** Request id generator. Default: `crypto.randomUUID()`. */
913
+ readonly generateRequestId?: () => string;
914
+ /**
915
+ * Maximum time `onStop` waits for in-flight requests before closing the remaining connections.
916
+ * Keep it below the application `shutdown.hookTimeoutMs`. `Infinity`: no limit of its own (the
917
+ * application's `hookTimeoutMs` still bounds `onStop`). Default: 4 000 ms.
918
+ */
919
+ readonly drainTimeoutMs?: number;
920
+ /**
921
+ * CORS: preflights are answered before routing and guards (never by a route), and replies to
922
+ * allowed origins get `access-control-allow-origin`, identically on every adapter. Default: off.
923
+ *
924
+ * ```ts
925
+ * cors: { origin: ['https://app.example.com', /^https:\/\/[a-z0-9-]+\.preview\.example\.com$/], credentials: true }
926
+ * ```
927
+ */
928
+ readonly cors?: CorsOptions | false;
929
+ /**
930
+ * Security headers added to every reply that does not set them (`@Res()` raw replies excepted):
931
+ * `true` (default) for the API defaults, an object merged over them, `false` for none.
932
+ */
933
+ readonly securityHeaders?: boolean | SecurityHeadersOptions;
934
+ /**
935
+ * Proxies trusted for `x-forwarded-for` / `x-forwarded-proto` (`ctx.ip`, `ctx.protocol`): a number
936
+ * of hops, or addresses, CIDR ranges and the aliases `'loopback'`, `'private'`, `'linklocal'`.
937
+ * Default `false`: the headers are ignored and `ctx.ip` is the adapter's address.
938
+ */
939
+ readonly trustProxy?: TrustProxy;
940
+ /**
941
+ * What the boot check of route decisions does when a route misses a decision of a required group,
942
+ * or when no guard of its chain enforces its decision (ZNIGHT023): `'error'` (default) fails the
943
+ * boot, `'warn'` logs one warning per group (tests that deliberately run without guards).
944
+ */
945
+ readonly enforcement?: EnforcementMode;
946
+ /** Cookies: signing keys and defaults (see {@link HttpCookieOptions}). */
947
+ readonly cookies?: HttpCookieOptions;
948
+ /** Server-Sent Events defaults (see {@link HttpSseOptions}). */
949
+ readonly sse?: HttpSseOptions;
950
+ }
951
+ /** The plugin option `cookies`. */
952
+ interface HttpCookieOptions {
953
+ /**
954
+ * HMAC keys of signed cookies (`@SignedCookie`, `{ signed: true }`), at least 32 bytes each: the
955
+ * first signs, all verify (rotation: deploy `[next, current]`, wait for the longest cookie
956
+ * lifetime, then `[next]`). Default: none (signed cookies unavailable).
957
+ */
958
+ readonly secrets?: string | Uint8Array | readonly (string | Uint8Array)[];
959
+ /**
960
+ * Defaults of every cookie set by the application, merged over the secure defaults
961
+ * `{ path: '/', secure: true, httpOnly: true, sameSite: 'lax' }` (e.g. `{ secure: false }` for
962
+ * plain `http://` development setups on a non-localhost host).
963
+ */
964
+ readonly defaults?: CookieDefaults;
965
+ }
966
+ /** The plugin option `sse`. */
967
+ interface HttpSseOptions {
968
+ /** Default heartbeat (ms, an integer ≥ 1 000, or `false`). Default: 8 000. */
969
+ readonly heartbeat?: number | false;
970
+ /** Default `retry` sent first (ms). Default: none. */
971
+ readonly retry?: number;
972
+ /**
973
+ * Time an event source gets to finish after `ctx.signal` aborted on shutdown or `maxDuration` (ms),
974
+ * before the stream ends with a jittered `retry`. Must be lower than `drainTimeoutMs`. Default:
975
+ * 1 000.
976
+ */
977
+ readonly shutdownGraceMs?: number;
978
+ }
979
+ /** A route registered by the plugin (see {@link HttpPlugin.routes}). */
980
+ interface HttpRouteSummary {
981
+ readonly method: string;
982
+ /** Registered path, prefix included. */
983
+ readonly path: string;
984
+ /** Controller display name, or the name of the plugin declaring the route. */
985
+ readonly controller: string;
986
+ readonly handler: string;
987
+ /** `'controller'` for compiled routes, the plugin name for plugin routes. */
988
+ readonly source: string;
989
+ /** Effective route metadata (the nearest decision of each group). */
990
+ readonly meta: Readonly<Record<string, unknown>>;
991
+ /** Display names of the guards, in chain order (global, controller, route). */
992
+ readonly guards: readonly string[];
993
+ }
994
+ /**
995
+ * The HTTP plugin: serves the controllers compiled into `metadata.extensions.http` with the chosen
996
+ * adapter.
997
+ *
998
+ * ```ts
999
+ * const app = await Application.create({
1000
+ * metadata: __ZNIGHT_METADATA__,
1001
+ * plugins: [HttpPlugin.configure({ port: 3000, prefix: '/api' })],
1002
+ * })
1003
+ * await app.start()
1004
+ * ```
1005
+ *
1006
+ * Lifecycle: `register` declares the request-scoped externals `HttpContext` and `RequestId`;
1007
+ * `onBootstrap` resolves the options, controllers, guards and interceptors, collects the routes of
1008
+ * other plugins (`httpRoutes()`), checks route decisions and registers the routes; `onStart`
1009
+ * listens; `onStop` stops accepting connections and drains in-flight requests (`drainTimeoutMs`);
1010
+ * `onDispose` closes the server.
1011
+ */
1012
+ export declare class HttpPlugin implements Plugin {
1013
+ #private;
1014
+ readonly name = "http";
1015
+ private constructor();
1016
+ /**
1017
+ * Creates the plugin. `options` may be a function of the container, evaluated in `onBootstrap`:
1018
+ * `HttpPlugin.configure(c => ({ port: c.get(AppConfig).port }))`.
1019
+ */
1020
+ static configure(options?: PluginOptions<HttpPluginOptions>): HttpPlugin;
1021
+ /** The adapter (available after `onBootstrap`). */
1022
+ get adapter(): HttpAdapter;
1023
+ /** Address of the server once listening (`String(address)` is its URL), `undefined` otherwise. */
1024
+ get address(): HttpServerAddress | undefined;
1025
+ /**
1026
+ * The path prefix of every route (`''` without prefix, `/api`), for plugins that serve routes or
1027
+ * publish URLs. Readable once the plugin is registered (before or after its `onBootstrap`; at any
1028
+ * time when the options are not a function of the container). Throws the options error when
1029
+ * the options are invalid.
1030
+ */
1031
+ get prefix(): string;
1032
+ /** Registered routes (after `onBootstrap`), in registration order. */
1033
+ get routes(): readonly HttpRouteSummary[];
1034
+ /** Number of requests being processed. */
1035
+ get inflight(): number;
1036
+ /** Number of open event streams (`@Sse`, `HttpResponse.sse()`): health checks, gauges, stream caps. */
1037
+ get streams(): number;
1038
+ /**
1039
+ * The signed wire value of a cookie (`<encoded value>.<signature>`, signed with the first secret of
1040
+ * `cookies.secrets`): what a browser would send back after `setCookie(name, value, { signed: true })`.
1041
+ * For gateways minting cookies and for tests. Available after `onBootstrap`.
1042
+ *
1043
+ * @throws TypeError for an invalid name or value; Error without `cookies.secrets`.
1044
+ */
1045
+ signCookie(name: string, value: string): string;
1046
+ /**
1047
+ * The provider classes the plugin resolves in `onBootstrap` although no compiled metadata
1048
+ * references them: the classes of the `guards`, `interceptors` and `exceptionMappers` options
1049
+ * (instances are left out), in that order, without duplicates. `TestApp` from `@znight/testing`
1050
+ * creates them at boot with their dependencies (an async dependency cannot be created on demand);
1051
+ * pass them to `init: { only }` when restricting the boot with `Application.create()`.
1052
+ *
1053
+ * Options given as a function of the container cannot be read before the bootstrap: the result is
1054
+ * then empty. Never throws: invalid options are reported by `onBootstrap`.
1055
+ */
1056
+ initRoots(): readonly ProviderKey[];
1057
+ /**
1058
+ * Sends a request in memory through the adapter and the whole pipeline (no socket; the server
1059
+ * does not need to listen). Available after `onBootstrap`.
1060
+ *
1061
+ * ```ts
1062
+ * const res = await http.inject({ method: 'POST', url: '/users', body: { name: 'Ada' } })
1063
+ * expect(res.status).toBe(201)
1064
+ * expect(res.json()).toEqual({ id: 1, name: 'Ada' })
1065
+ * ```
1066
+ */
1067
+ inject(request: InjectRequest): Promise<InjectResponse>;
1068
+ /**
1069
+ * Sends a request in memory and resolves at the response headers, the body being read as it is
1070
+ * produced (`body`). Cancelling `body` (or aborting `signal`) disconnects the client. Requires an
1071
+ * adapter implementing `injectStream()` (the four built-in ones do).
1072
+ */
1073
+ injectStream(request: InjectStreamOptions): Promise<InjectStreamResponse>;
1074
+ /**
1075
+ * Opens an event stream in memory (`accept: text/event-stream`) and reads its events like
1076
+ * `EventSource`: `next()`, `take()`, `comments`, `retry`, `lastEventId`, `reconnect()`, `close()`
1077
+ * (the server sees a client disconnect). A response that is not an event stream (404, 204...) is
1078
+ * read to its end (`body`). Resolves at the first chunk of the stream (an `@Sse` stream sends
1079
+ * `retry` or a comment at once), or after 2 000 ms for a stream that sends nothing yet; rejects,
1080
+ * naming the request (which is aborted), when no response headers arrive within 2 000 ms.
1081
+ *
1082
+ * ```ts
1083
+ * await using stream = await http.injectSse({ url: '/orders/o-1/events' })
1084
+ * const event = await stream.next() // rejects after 2 000 ms
1085
+ * expect(event.json()).toMatchObject({ status: 'paid' })
1086
+ * ```
1087
+ */
1088
+ injectSse(request: InjectStreamOptions): Promise<InjectSseResponse>;
1089
+ register(ctx: PluginContext): void;
1090
+ onBootstrap(ctx: PluginContext): void;
1091
+ onStart(ctx: PluginContext): Promise<void>;
1092
+ onStop(ctx: PluginContext): Promise<void>;
1093
+ onDispose(): Promise<void>;
1094
+ }
1095
+ /**
1096
+ * A route served by `HttpPlugin` on behalf of another plugin. It runs through the same pipeline as
1097
+ * controller routes: `http:request` hook, request scope, global guards, then its own `guards`, the
1098
+ * handler, the exception mappers; global interceptors do not wrap it. The handler result is
1099
+ * serialised like a controller result (`HttpResponse`, objects as JSON, strings, streams...).
1100
+ */
1101
+ interface PluginRoute {
1102
+ /** `GET` routes also answer `HEAD`; `ALL` registers every method without its own route. */
1103
+ readonly method: HttpMethod;
1104
+ /** Path relative to the `HttpPlugin` prefix, with the controller grammar (`/openapi.json`, `/docs/_assets/*`). */
1105
+ readonly path: string;
1106
+ /** Handler name, shown in `HttpPlugin.routes`, logs and errors (`document`). */
1107
+ readonly name: string;
1108
+ readonly handler: (ctx: HttpContext) => unknown;
1109
+ /**
1110
+ * Route metadata (`[Public.meta()]`): read by guards (`Public.get(ctx)`) and by the boot check of
1111
+ * required route decorator groups. One entry per group.
1112
+ */
1113
+ readonly meta?: readonly RouteMetaEntry[];
1114
+ /** Guards of the route (provider classes or instances), run after the global guards. */
1115
+ readonly guards?: readonly GuardRef[];
1116
+ }
1117
+ /** What {@link PluginHttpRoutes.httpRoutes} receives. */
1118
+ interface PluginHttpRoutesContext {
1119
+ /** The `HttpPlugin` prefix (`''` or `/api`), already applied to the paths of the routes. */
1120
+ readonly prefix: string;
1121
+ /** The application container (initialised). */
1122
+ readonly container: Container;
1123
+ }
1124
+ /**
1125
+ * Implemented by a plugin that serves HTTP routes. `HttpPlugin` calls `httpRoutes()` once in its
1126
+ * `onBootstrap`, after `container.init()` and whatever the plugin order, then registers the routes
1127
+ * on its adapter. A path that breaks the grammar, or that collides with another route, is ZNIGHT007
1128
+ * naming the plugin.
1129
+ *
1130
+ * ```ts
1131
+ * export class StatusPlugin implements Plugin, PluginHttpRoutes {
1132
+ * readonly name = 'status'
1133
+ * httpRoutes(): readonly PluginRoute[] {
1134
+ * return [{ method: 'GET', path: '/status', name: 'status', handler: () => ({ up: true }), meta: [Public.meta()] }]
1135
+ * }
1136
+ * }
1137
+ * ```
1138
+ */
1139
+ interface PluginHttpRoutes {
1140
+ httpRoutes(context: PluginHttpRoutesContext): readonly PluginRoute[];
1141
+ }
1142
+ /** A parsed `content-type` header. */
1143
+ interface MediaType {
1144
+ /** Lower-case `type/subtype` without parameters. */
1145
+ readonly type: string;
1146
+ /** Lower-case `charset` parameter, when present. */
1147
+ readonly charset: string | undefined;
1148
+ }
1149
+ /** Parses a `content-type` header value (`application/json; charset=utf-8`). */
1150
+ export declare function parseMediaType(header: string | undefined): MediaType | undefined;
1151
+ /** Whether `type` is a JSON media type (`application/json`, `application/problem+json`...). */
1152
+ export declare function isJsonType(type: string): boolean;
1153
+ /**
1154
+ * Parses a buffered request body according to its `content-type` (low-level helper; the request
1155
+ * context parses bodies itself, multipart bodies as they arrive):
1156
+ *
1157
+ * | Content type | Result |
1158
+ * | ------------------------------------- | ------------------------------------ |
1159
+ * | empty body | `undefined` |
1160
+ * | `application/json`, `*\/*+json` | parsed JSON (400 when malformed) |
1161
+ * | `text/*` | string (charset honoured) |
1162
+ * | `application/x-www-form-urlencoded` | record (repeated keys → arrays) |
1163
+ * | `multipart/form-data` | `FormData` (every field and file) |
1164
+ * | anything else, or no content type | `Uint8Array` |
1165
+ *
1166
+ * A `content-encoding` other than `identity` is rejected with 415 (decompression is not supported).
1167
+ */
1168
+ export declare function parseBody(bytes: Uint8Array, contentType: string | undefined, contentEncoding?: string | undefined): Promise<unknown>;
1169
+ /**
1170
+ * Converts a raw request value (route parameter, query value, header) to `T`, or throws a
1171
+ * `400 Bad Request` naming `label` (`param "id"`, `query "page"`, `header "x-limit"`).
1172
+ * Called by the compiler-generated `invoke` functions.
1173
+ */
1174
+ type Coercer<T> = (value: unknown, label: string) => T;
1175
+ /** The coercion helpers called by generated code (see the "Compiler contract" of the README). */
1176
+ interface Coercers {
1177
+ /** A string. Rejects missing values and repeated query values. */
1178
+ readonly string: Coercer<string>;
1179
+ /** A finite decimal number (`12`, `-1.5`, `1e3`). Rejects `''`, hex, `NaN`, `Infinity`. */
1180
+ readonly number: Coercer<number>;
1181
+ /** A safe integer (`42`, `-7`). */
1182
+ readonly integer: Coercer<number>;
1183
+ /** `true`/`1` → `true`, `false`/`0` → `false` (case-insensitive). */
1184
+ readonly boolean: Coercer<boolean>;
1185
+ /** A bigint (`123`, `-9007199254740993`). */
1186
+ readonly bigint: Coercer<bigint>;
1187
+ /**
1188
+ * A `Date` from an ISO 8601 date (`2024-01-02`, UTC midnight), an ISO 8601 date-time with an
1189
+ * offset (`2024-01-02T03:04:05Z`, `2024-01-02T03:04+02:00`) or a millisecond timestamp.
1190
+ */
1191
+ readonly date: Coercer<Date>;
1192
+ /** One of `values` (string or number literals, compared after coercion). */
1193
+ enum<const T extends readonly (string | number | boolean)[]>(value: unknown, label: string, values: T): T[number];
1194
+ /** An array: a single value becomes a one-element array, each item is coerced with `item`. */
1195
+ array<T>(value: unknown, label: string, item: Coercer<T>): T[];
1196
+ /** `undefined` when the value is missing, otherwise `item(value, label)`. */
1197
+ optional<T>(value: unknown, label: string, item: Coercer<T>): T | undefined;
1198
+ }
1199
+ /**
1200
+ * Coercion helpers used by generated `invoke` functions for typed parameters without schema:
1201
+ *
1202
+ * ```ts
1203
+ * invoke: (c, ctx) => c.list(
1204
+ * coerce.number(ctx.params['id'], 'param "id"'),
1205
+ * coerce.optional(ctx.query['page'], 'query "page"', coerce.integer),
1206
+ * coerce.array(ctx.query['tag'], 'query "tag"', coerce.string),
1207
+ * )
1208
+ * ```
1209
+ *
1210
+ * Every failure is a `BadRequestException` (400) whose message names the input.
1211
+ */
1212
+ export declare const coerce: Coercers;
1213
+ /**
1214
+ * Media type of a file name from its extension (`report.pdf` → `application/pdf`, text types with
1215
+ * `; charset=utf-8`), `undefined` when the extension is unknown.
1216
+ */
1217
+ export declare function contentTypeFor(filename: string): string | undefined;
1218
+ /** Whether the media type `type` (parameters ignored) matches one of `patterns` (`image/png`, `image/*`, `*\/*`). */
1219
+ export declare function matchesMediaType(type: string, patterns: readonly string[]): boolean;
1220
+ /** Reason phrase of `status` (`404` → `Not Found`), or a generic one for unknown codes. */
1221
+ export declare function statusText(status: number): string;
1222
+ /** Whether `status` is an integer in the 100–599 range. */
1223
+ export declare function isValidStatus(status: unknown): status is number;
1224
+ /**
1225
+ * Tokens of the values `@znight/http` supplies to the container. `HttpContext` and `RequestId` are
1226
+ * request-scoped externals (declared in the package manifest): inject them in request-scoped
1227
+ * providers or through `Lazy<HttpContext>`.
1228
+ */
1229
+ export declare const HttpTokens: {
1230
+ /** The {@link HttpContext} of the current request (request scope). */
1231
+ readonly Context: TypedToken<HttpContext>;
1232
+ /** The id of the current request (request scope). */
1233
+ readonly RequestId: TypedToken<RequestId>;
1234
+ /** The {@link HttpPlugin} instance (singleton). */
1235
+ readonly Plugin: TypedToken<HttpPlugin>;
1236
+ };
1237
+ /**
1238
+ * Validates `value` with a [Standard Schema](https://standardschema.dev) (Zod, Valibot, ArkType...)
1239
+ * and returns the schema **output** (defaults applied, transforms run, unknown keys stripped when
1240
+ * the schema strips them).
1241
+ *
1242
+ * Returns synchronously when the schema validates synchronously, a promise otherwise (async
1243
+ * refinements): generated code always `await`s it.
1244
+ *
1245
+ * @param location what is validated, used in the error message: `body`, `query`, `query "page"`,
1246
+ * `param "id"`, `headers`, `cookie "sid"`, `cookies`...
1247
+ * @throws ValidationException (400, `details: [{ path, message }]`) when the value is invalid. For
1248
+ * cookies (`cookie "name"`, `cookies`) the issue messages are replaced with `Invalid value`: some
1249
+ * libraries quote the received value in them (Valibot, ArkType), and cookies often carry tokens.
1250
+ * @throws TypeError when `schema` is not a Standard Schema v1 (a programming error → 500).
1251
+ */
1252
+ export declare function validate<S extends StandardSchemaV1>(schema: S, value: unknown, location: string): InferSchemaOutput<S> | Promise<InferSchemaOutput<S>>;
1253
+ 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 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 };
1254
+ //# sourceMappingURL=index.d.mts.map