@orkestrel/middleware 0.0.19 → 0.0.20

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.
@@ -1,10 +1,10 @@
1
- import { Encoding } from '@orkestrel/server';
2
- import { FileHandle } from 'node:fs/promises';
1
+ import type { Encoding } from '@orkestrel/server';
2
+ import type { FileHandle } from 'node:fs/promises';
3
3
  import { HTTPError } from '@orkestrel/server';
4
- import { MiddlewareHandler } from '@orkestrel/server';
5
- import { MultipartBody } from '@orkestrel/middleware';
6
- import { MultipartFile } from '@orkestrel/middleware';
7
- import { MultipartState } from '@orkestrel/middleware';
4
+ import type { MiddlewareHandler } from '@orkestrel/server';
5
+ import type { MultipartBody } from '@orkestrel/middleware';
6
+ import type { MultipartFile } from '@orkestrel/middleware';
7
+ import type { MultipartState } from '@orkestrel/middleware';
8
8
 
9
9
  /**
10
10
  * Describes one in-memory asset representation returned by an {@link AssetSourceInterface}.
@@ -23,7 +23,7 @@ export declare interface Asset {
23
23
  * Configures `createAssets` — in-memory identity/Brotli asset serving.
24
24
  *
25
25
  * @remarks
26
- * - `source` — the required in-memory asset reader. It MUST answer a bounded
26
+ * - `source` — the required in-memory asset reader. It must answer a bounded
27
27
  * key set and return `undefined` for every key outside it, because
28
28
  * `createAssets` retains every successful result for the factory's lifetime
29
29
  * and evicts nothing. A `source` that synthesizes a representation for an
@@ -40,12 +40,12 @@ export declare interface AssetOptions {
40
40
  * @remarks
41
41
  * A successful result is cached by `createAssets`; later source changes do
42
42
  * not alter that path's response. A miss may be read again on a later request.
43
- * `read` therefore owes a BOUNDED key set: that cache retains every
43
+ * `read` therefore owes a bounded key set: that cache retains every
44
44
  * successful result for the factory's lifetime and evicts nothing.
45
45
  */
46
46
  export declare interface AssetSourceInterface {
47
47
  /**
48
- * Reads one asset representation.
48
+ * Reads one identity or Brotli asset representation for a validated relative key.
49
49
  *
50
50
  * @param path - The validated relative asset path
51
51
  * @returns The asset, or `undefined` when the path is absent
@@ -136,12 +136,11 @@ export declare function computeFileETag(size: number, mtimeMs: number): string;
136
136
  export declare function createAssets<TState>(options: AssetOptions): MiddlewareHandler<TState>;
137
137
 
138
138
  /**
139
- * Compresses response bodies through `node:zlib` the node-bound sibling of
140
- * the core face's `CompressionStream`-feature-detected `createCompression`,
141
- * guaranteed available on any Node runtime rather than dependent on the
142
- * WHATWG `CompressionStream` global. Ships as a SEPARATE package entry point
143
- * (`@orkestrel/middleware/server`) from the core face's `createCompression`,
144
- * so the shared name is unambiguous per consumer import path.
139
+ * Compresses response bodies through `node:zlib`, guaranteed on any Node runtime rather
140
+ * than dependent on the WHATWG `CompressionStream` global. This battery is the
141
+ * node-bound sibling of the core face's feature-detected `createCompression`, and it
142
+ * ships from a separate package entry point (`@orkestrel/middleware/server`) so the
143
+ * shared name is unambiguous per consumer import path.
145
144
  *
146
145
  * @remarks
147
146
  * Peer-type limitation, the same one the core face carries: the shipped
@@ -178,7 +177,7 @@ export declare function createCompression<TState>(options?: NodeCompressionOptio
178
177
  * {@link MultipartError} this battery's parser throws is re-thrown as an
179
178
  * {@link HTTPError} carrying the same status/message, so `createBoundary`
180
179
  * (or any HTTPError-aware renderer) maps it correctly without depending on
181
- * this node face's error type. Fail-closed on the DOWNSTREAM handler too: if
180
+ * this node face's error type. Fail-closed on the downstream handler too: if
182
181
  * `next()` throws, every still-`'staged'` uploaded file is unlinked
183
182
  * (best-effort) before the error is re-thrown, so an unhandled downstream
184
183
  * failure never leaks temp files. A normal return leaves staged files
@@ -200,16 +199,16 @@ export declare function createCompression<TState>(options?: NodeCompressionOptio
200
199
  export declare function createMultipart<TState extends MultipartState>(options?: MultipartOptions): MiddlewareHandler<TState>;
201
200
 
202
201
  /**
203
- * Serves static files from `options.root` over `node:fs` — the node-bound
204
- * static-file battery.
202
+ * Serves static files from `options.root` over `node:fs` — the node-bound static-file
203
+ * battery, answering conditional, ranged, and SPA-fallback requests.
205
204
  *
206
205
  * @remarks
207
- * Containment is enforced on CANONICAL paths, not merely the lexically
206
+ * Containment is enforced on canonical paths, not merely the lexically
208
207
  * resolved one: `options.root` is canonicalized once (memoized) and every
209
208
  * request's candidate path is re-canonicalized (`fs.realpath`) before it is
210
209
  * served, so a symlink whose target escapes `root` is refused (falls through
211
210
  * to `next()`) even though the lexical path resolved inside `root`. A
212
- * symlink that resolves to a target still INSIDE `root` is unaffected and
211
+ * symlink that resolves to a target still inside `root` is unaffected and
213
212
  * still serves normally. A dangling symlink (`realpath` throws `ENOENT`) or
214
213
  * any other `realpath` failure is treated as a miss — this battery never
215
214
  * throws or 500s on a symlink surprise. On a streamed response (a 200 or 206
@@ -246,31 +245,31 @@ export declare function createStatic<TState>(options: StaticOptions): Middleware
246
245
  */
247
246
  export declare function createUploadedFile(input: UploadedFile): UploadedFile;
248
247
 
249
- /** Names the MIME type served when a file extension has no known mapping. */
248
+ /** Names `'application/octet-stream'`, the MIME type served when a file extension has no known mapping. */
250
249
  export declare const DEFAULT_CONTENT_TYPE = "application/octet-stream";
251
250
 
252
- /** Holds `createMultipart`'s default maximum field-part count. */
251
+ /** Holds `100`, `createMultipart`'s default maximum field-part count. */
253
252
  export declare const DEFAULT_MULTIPART_FIELD_COUNT = 100;
254
253
 
255
- /** Holds `createMultipart`'s default per-field byte-size cap. */
254
+ /** Holds `65_536`, `createMultipart`'s default per-field byte-size cap. */
256
255
  export declare const DEFAULT_MULTIPART_FIELD_SIZE = 65536;
257
256
 
258
- /** Holds `createMultipart`'s default maximum file-part count. */
257
+ /** Holds `10`, `createMultipart`'s default maximum file-part count. */
259
258
  export declare const DEFAULT_MULTIPART_FILE_COUNT = 10;
260
259
 
261
- /** Holds `createMultipart`'s default per-file byte-size cap. */
260
+ /** Holds `10_485_760`, `createMultipart`'s default per-file byte-size cap. */
262
261
  export declare const DEFAULT_MULTIPART_FILE_SIZE = 10485760;
263
262
 
264
- /** Holds `createMultipart`'s default combined request-body byte-size cap. */
263
+ /** Holds `52_428_800`, `createMultipart`'s default combined request-body byte-size cap. */
265
264
  export declare const DEFAULT_MULTIPART_TOTAL = 52428800;
266
265
 
267
- /** Names `createStatic`'s default policy for a path carrying a dotfile segment. */
266
+ /** Names `'ignore'`, `createStatic`'s default policy for a path carrying a dotfile segment. */
268
267
  export declare const DEFAULT_STATIC_DOTFILES: NonNullable<StaticOptions['dotfiles']>;
269
268
 
270
- /** Names `createStatic`'s `fallback: true` default excluded path prefix. */
269
+ /** Names `'/api'`, `createStatic`'s `fallback: true` default excluded path prefix. */
271
270
  export declare const DEFAULT_STATIC_FALLBACK_EXCLUDE = "/api";
272
271
 
273
- /** Names `createStatic`'s default directory-index filename. */
272
+ /** Names `'index.html'`, `createStatic`'s default directory-index filename. */
274
273
  export declare const DEFAULT_STATIC_INDEX = "index.html";
275
274
 
276
275
  /**
@@ -316,11 +315,11 @@ export declare function extractMultipartBoundary(contentType: string | null): st
316
315
 
317
316
  /**
318
317
  * Checks whether `child` is `parent` itself or lies inside it on-disk — the
319
- * FILESYSTEM containment predicate `createStatic` applies to `fs.realpath`
318
+ * filesystem containment predicate `createStatic` applies to `fs.realpath`
320
319
  * output (never to a URL pathname — that is {@link isUnderPath}'s job).
321
320
  *
322
321
  * @remarks
323
- * Argument order is `(child, parent)` — deliberately the OPPOSITE conceptual
322
+ * Argument order is `(child, parent)` — deliberately the opposite conceptual
324
323
  * order from {@link isUnderPath}`(pathname, prefix)`, so a call site cannot
325
324
  * casually swap one predicate in for the other. Built on `path.relative`,
326
325
  * this is separator-correct on both POSIX (`/`) and win32 (`\`) — unlike a
@@ -384,7 +383,7 @@ export declare function isMultipartError(value: unknown): value is MultipartErro
384
383
  *
385
384
  * @remarks
386
385
  * Normalizes superscript digits (`¹²³` → `123`) first, strips trailing dots
387
- * and spaces (Windows drops them), takes the STEM before the first `.`,
386
+ * and spaces (Windows drops them), takes the stem before the first `.`,
388
387
  * upper-cases it, and tests it against {@link RESERVED_DEVICE_NAMES}
389
388
  * (`CON PRN AUX NUL COM1-9 LPT1-9`) — an exact-stem match only, so
390
389
  * `console.js` and `nullable.css` are never flagged.
@@ -402,7 +401,7 @@ export declare function isMultipartError(value: unknown): value is MultipartErro
402
401
  export declare function isReservedDeviceName(segment: string): boolean;
403
402
 
404
403
  /**
405
- * Checks whether `pathname` is `prefix` itself or lies under it on a SEGMENT
404
+ * Checks whether `pathname` is `prefix` itself or lies under it on a segment
406
405
  * boundary — the shared under-path test `resolveStaticPath`'s prefix strip
407
406
  * and `createStatic`'s SPA-fallback `exclude` both apply, so `exclude:
408
407
  * '/api'` matches `/api` and `/api/x` but never `/apifoo`.
@@ -476,13 +475,16 @@ export declare function moveUploadedFile(file: UploadedFile, destination: string
476
475
  */
477
476
  export declare const MULTIPART_ERROR_BRAND: unique symbol;
478
477
 
479
- /** Holds the maximum bytes a single multipart part's header block may occupy before it is malformed. */
478
+ /** Holds `16_384`, the maximum bytes a single multipart part's header block may occupy before it is malformed. */
480
479
  export declare const MULTIPART_MAX_HEADER_BLOCK = 16384;
481
480
 
482
- /** Holds the maximum bytes scanned before the first multipart boundary is found before it is malformed. */
481
+ /** Holds `65_536`, the maximum bytes scanned before the first multipart boundary is found before it is malformed. */
483
482
  export declare const MULTIPART_MAX_PREAMBLE = 65536;
484
483
 
485
- /** Holds the HTTP status `createMultipart` renders for each {@link MultipartErrorCode}. */
484
+ /**
485
+ * Holds the HTTP status `createMultipart` renders for each {@link MultipartErrorCode}:
486
+ * `'limit'` is 413, `'malformed'` is 400, and `'rejected'` is 415.
487
+ */
486
488
  export declare const MULTIPART_STATUS: Readonly<Record<MultipartErrorCode, number>>;
487
489
 
488
490
  /**
@@ -520,7 +522,7 @@ export declare class MultipartError extends HTTPError {
520
522
  export declare type MultipartErrorCode = 'limit' | 'malformed' | 'rejected';
521
523
 
522
524
  /**
523
- * Describes the per-category size/count caps `createMultipart` enforces MID-STREAM — the
525
+ * Describes the per-category size/count caps `createMultipart` enforces mid-stream — the
524
526
  * effective limits, every documented default already applied.
525
527
  *
526
528
  * @remarks
@@ -576,7 +578,7 @@ export declare interface MultipartLimitsInput {
576
578
  *
577
579
  * @remarks
578
580
  * - `limits` — see {@link MultipartLimitsInput}.
579
- * - `allowed` — a MIME allow-list validated against SNIFFED (not merely
581
+ * - `allowed` — a MIME allow-list validated against sniffed (not merely
580
582
  * declared) bytes; an empty array allows nothing. Omitted ⇒ no type
581
583
  * rejection.
582
584
  * - `directory` — the directory staged files are written to; defaults to
@@ -589,8 +591,8 @@ export declare interface MultipartOptions {
589
591
  }
590
592
 
591
593
  /**
592
- * Lists the content-codings the node face's `createCompression` offers — the two
593
- * `node:zlib` guarantees on every Node runtime, so this face never
594
+ * Lists `['gzip', 'deflate']`, the content-codings the node face's `createCompression`
595
+ * offers — what `node:zlib` guarantees on every Node runtime, so this face never
594
596
  * feature-detects.
595
597
  */
596
598
  export declare const NODE_COMPRESSION_ENCODINGS: readonly Encoding[];
@@ -619,30 +621,30 @@ export declare interface NodeCompressionOptions {
619
621
  *
620
622
  * @remarks
621
623
  * Reads `request.body` chunk by chunk through its `ReadableStream` reader —
622
- * NEVER buffers the whole body — enforcing every {@link MultipartLimits} cap
624
+ * never buffers the whole body — enforcing every {@link MultipartLimits} cap
623
625
  * the instant it is exceeded (reading stops, every already-staged temp file
624
626
  * is deleted, throws {@link MultipartError} with code `'limit'`). Each file
625
627
  * part streams to `join(directory, randomUUID())` — the client's declared
626
- * filename is METADATA ONLY, never a path component. A field OR file part
628
+ * filename is metadata only, never a path component. A field or file part
627
629
  * named `__proto__` / `constructor` / `prototype` is silently skipped and
628
630
  * never keyed onto the returned {@link MultipartBody} (a skipped file's
629
631
  * staged temp file is unlinked immediately, since it can never be
630
632
  * referenced). A file part with an empty declared filename (`filename=""`)
631
- * AND a zero-byte body — the browser convention for an unselected optional
633
+ * and a zero-byte body — the browser convention for an unselected optional
632
634
  * `<input type="file">` — is a silent no-op: its temp file is unlinked, it is
633
635
  * never counted against the `file.count` limit, and it never runs the
634
636
  * `allowed` check. A malformed
635
637
  * structure (missing/unterminated boundary, nameless part, an oversized
636
638
  * header block, or a preamble exceeding {@link MULTIPART_MAX_PREAMBLE} before
637
639
  * the first boundary) throws with code `'malformed'`. A file is accepted
638
- * against the configured `allowed` MIME list iff its SNIFFED bytes detect a
640
+ * against the configured `allowed` MIME list exactly when its sniffed bytes detect a
639
641
  * type present in the list — sniff-authoritative, independent of whether the
640
642
  * declared `Content-Type` matches (that agreement is exposed separately as
641
643
  * `validated`); otherwise throws with code `'rejected'`. A
642
644
  * request abort mid-upload triggers the same fail-closed cleanup as a limit
643
645
  * breach. Returns `undefined` for a non-multipart request (untouched).
644
646
  *
645
- * Staging defaults to a process-owned directory created ONCE (lazily,
647
+ * Staging defaults to a process-owned directory created once (lazily,
646
648
  * memoized across calls) with `mkdtemp` under `os.tmpdir()` and locked to
647
649
  * mode `0o700`; `options.directory` overrides it.
648
650
  *
@@ -753,7 +755,7 @@ export declare function resolveMultipartLimits(limits: MultipartLimitsInput | un
753
755
  * fallback.
754
756
  *
755
757
  * @remarks
756
- * `GET` and `HEAD` are both eligible, and resolve the SAME shell: `HEAD` is
758
+ * `GET` and `HEAD` are both eligible, and resolve the same shell: `HEAD` is
757
759
  * defined as `GET` without a body (RFC 9110 §9.3.2), so a navigation probe
758
760
  * that answered `404` while its `GET` answered `200` would report a resource
759
761
  * the very next request serves.
@@ -775,14 +777,14 @@ export declare function resolveMultipartLimits(limits: MultipartLimitsInput | un
775
777
  export declare function resolveStaticFallbackPath(root: string, index: string, exclude: string, method: string, pathname: string, accept: string): string | undefined;
776
778
 
777
779
  /**
778
- * Resolves a request pathname to an on-disk path UNDER `root`, or `undefined`
780
+ * Resolves a request pathname to an on-disk path under `root`, or `undefined`
779
781
  * when it cannot — the traversal guard, whose algorithm and order are exact:
780
782
  * strip `prefix` on a segment boundary → `decodeURIComponent` (a
781
783
  * malformed escape refuses, never throws) → reject a NUL byte → strip the
782
- * leading path separator FIRST (so a leading `..` survives `normalize` as a
783
- * genuine climbing segment) → `normalize` → refuse any Windows reserved-
784
- * device-name segment ({@link isReservedDeviceName}) → `resolve` and require
785
- * the result under `root`.
784
+ * leading path separator first (so a leading `..` survives `normalize` as a
785
+ * genuine climbing segment) → `normalize` → refuse any Windows
786
+ * reserved-device-name segment ({@link isReservedDeviceName}) → `resolve` and
787
+ * require the result under `root`.
786
788
  *
787
789
  * @param root - The absolute root directory every result must resolve under
788
790
  * @param prefix - An optional URL path prefix stripped on a segment boundary
@@ -802,14 +804,14 @@ export declare function resolveStaticPath(root: string, prefix: string | undefin
802
804
  * Configures `createStatic` — node `fs`-backed static file serving.
803
805
  *
804
806
  * @remarks
805
- * - `root` — the directory every request resolves under, resolved once at
806
- * construction. REQUIRED.
807
+ * - `root` — the required directory every request resolves under, resolved
808
+ * once at construction.
807
809
  * - `prefix` — a URL path prefix stripped (on a segment boundary) before
808
810
  * resolving under `root`.
809
811
  * - `index` — the filename served for a directory hit and by the SPA
810
812
  * fallback; defaults to {@link DEFAULT_STATIC_INDEX}. The fallback serves
811
- * it whatever `dotfiles` is set to, because this path is operator-
812
- * configured rather than request-derived.
813
+ * it whatever `dotfiles` is set to, because this path is
814
+ * operator-configured rather than request-derived.
813
815
  * - `dotfiles` — the policy for a path with a dotfile segment: `'ignore'`
814
816
  * (falls through to `next()`), `'deny'` (403), or `'allow'` (serves it);
815
817
  * defaults to {@link DEFAULT_STATIC_DOTFILES}.
@@ -818,7 +820,7 @@ export declare function resolveStaticPath(root: string, prefix: string | undefin
818
820
  * - `fallback` — SPA fallback: `false` (default, off), `true` (on, excluding
819
821
  * {@link DEFAULT_STATIC_FALLBACK_EXCLUDE}), or `{ exclude }` for a custom
820
822
  * excluded prefix. An eligible `GET` or `HEAD` navigation miss answers with
821
- * `index` through the SAME handle-`fstat` header block a directly requested
823
+ * `index` through the same handle-`fstat` header block a directly requested
822
824
  * file answers through, so `cache`, `etag`, conditional revalidation,
823
825
  * `HEAD`, and ranges are identical on both routes; `index` reaches the
824
826
  * client through this route whatever `dotfiles` is set to.
@@ -842,7 +844,7 @@ export declare interface StaticOptions {
842
844
  * response body routes through.
843
845
  *
844
846
  * @remarks
845
- * PULL-driven, not push-driven: the underlying node stream's async iterator
847
+ * Pull-driven, not push-driven: the underlying node stream's async iterator
846
848
  * is only advanced (`iterator.next()`) from inside `pull(controller)`, which
847
849
  * the web `ReadableStream` invokes exactly when its internal queue has room
848
850
  * for more data. Exactly one disk chunk is read and enqueued per `pull` —
@@ -911,13 +913,13 @@ export declare function unlinkStagedFiles(body: MultipartBody): Promise<void>;
911
913
  *
912
914
  * @remarks
913
915
  * - `field` — the multipart field name the file was submitted under.
914
- * - `name` — the client-declared filename (METADATA ONLY — never used to
916
+ * - `name` — the client-declared filename (metadata only — never used to
915
917
  * build a filesystem path).
916
918
  * - `size` — the file's byte size.
917
- * - `mime` — the SNIFFED (magic-byte-detected) MIME type when a signature
919
+ * - `mime` — the sniffed (magic-byte-detected) MIME type when a signature
918
920
  * matches; otherwise the part's declared `Content-Type`; otherwise
919
921
  * {@link DEFAULT_CONTENT_TYPE}. Read `validated` to tell which.
920
- * - `validated` — `true` when a signature matched AND the sniffed type equals
922
+ * - `validated` — `true` when a signature matched and the sniffed type equals
921
923
  * the declared `Content-Type`, so `mime` is the sniffed fact. `false` means
922
924
  * `mime` may be the client-declared value.
923
925
  * - `status` — see {@link UploadStatus}.