expediate 0.0.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/router.js ADDED
@@ -0,0 +1,502 @@
1
+ /* Copyright 2021 Fabien Bavent
2
+ *
3
+ * Permission is hereby granted, free of charge, to any person obtaining a
4
+ * copy of this software and associated documentation files (the "Software"),
5
+ * to deal in the Software without restriction, including without limitation
6
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
7
+ * and/or sell copies of the Software, and to permit persons to whom the
8
+ * Software is furnished to do so, subject to the following conditions:
9
+ *
10
+ * The above copyright notice and this permission notice shall be included
11
+ * in all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19
+ * DEALINGS IN THE SOFTWARE.
20
+ */
21
+ 'use strict';
22
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
23
+ if (k2 === undefined) k2 = k;
24
+ var desc = Object.getOwnPropertyDescriptor(m, k);
25
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
26
+ desc = { enumerable: true, get: function() { return m[k]; } };
27
+ }
28
+ Object.defineProperty(o, k2, desc);
29
+ }) : (function(o, m, k, k2) {
30
+ if (k2 === undefined) k2 = k;
31
+ o[k2] = m[k];
32
+ }));
33
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
34
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
35
+ }) : function(o, v) {
36
+ o["default"] = v;
37
+ });
38
+ var __importStar = (this && this.__importStar) || (function () {
39
+ var ownKeys = function(o) {
40
+ ownKeys = Object.getOwnPropertyNames || function (o) {
41
+ var ar = [];
42
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
43
+ return ar;
44
+ };
45
+ return ownKeys(o);
46
+ };
47
+ return function (mod) {
48
+ if (mod && mod.__esModule) return mod;
49
+ var result = {};
50
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
51
+ __setModuleDefault(result, mod);
52
+ return result;
53
+ };
54
+ })();
55
+ Object.defineProperty(exports, "__esModule", { value: true });
56
+ const http = __importStar(require("http"));
57
+ const https = __importStar(require("https"));
58
+ // ---------------------------------------------------------------------------
59
+ // Pattern compilation
60
+ // ---------------------------------------------------------------------------
61
+ /**
62
+ * Determine whether a path string contains glob characters (`*` or `?`).
63
+ *
64
+ * A pattern is considered a glob when it contains at least one unescaped
65
+ * `*` or `?` character, following the same convention as `.gitignore`.
66
+ *
67
+ * @param pattern - The path string to inspect.
68
+ * @returns `true` if the pattern should be treated as a glob.
69
+ */
70
+ function isGlobPattern(pattern) {
71
+ return /(?<!\\)[*?]/.test(pattern);
72
+ }
73
+ /**
74
+ * Compile a `.gitignore`-style glob string into a prefix-anchored `RegExp`.
75
+ *
76
+ * Supported wildcard syntax:
77
+ * - `?` — matches exactly one character that is not `/`.
78
+ * - `*` — matches zero or more characters that are not `/`.
79
+ * - `**` — matches zero or more path segments (any characters including `/`).
80
+ *
81
+ * The returned expression is anchored at the start (`^`) so it always matches
82
+ * a prefix of the current path; the matched portion is stripped by
83
+ * `matchRouteLayer` when `layer.stripPath` is `true`.
84
+ *
85
+ * @param glob - The glob pattern to compile.
86
+ * @returns A prefix-anchored `RegExp`.
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * compileGlob('/**\/*.php').test('/admin/index.php'); // true
91
+ * compileGlob('/api/*') .test('/api/users'); // true
92
+ * compileGlob('/api/*') .test('/api/users/123'); // false
93
+ * ```
94
+ */
95
+ function compileGlob(glob) {
96
+ // Escape all regex special characters, leaving our wildcard characters intact.
97
+ let src = glob.replace(/[.+^${}()|[\]\\]/g, '\\$&');
98
+ // Replace in order: `**` must be handled before `*`.
99
+ src = src
100
+ .replace(/\*\*/g, '\x00GLOBSTAR\x00') // temporary placeholder
101
+ .replace(/\*/g, '[^/]*') // single-segment wildcard
102
+ .replace(/\?/g, '[^/]') // single-character wildcard
103
+ .replace(/\x00GLOBSTAR\x00/g, '.*'); // cross-segment wildcard
104
+ return new RegExp('^' + src);
105
+ }
106
+ /**
107
+ * Compile a plain path string with optional `:name` parameter segments into a
108
+ * prefix-anchored `RegExp` that uses named capture groups.
109
+ *
110
+ * Each `:name` segment is converted to `(?<name>[^/]+)`, making named
111
+ * captures available directly on the `RegExp` match result.
112
+ * Literal segments are escaped and matched exactly.
113
+ *
114
+ * The expression matches up to a segment boundary (`/` or end-of-string) so
115
+ * that `/users` never inadvertently matches `/users-admin`.
116
+ *
117
+ * @param path - A plain path string such as `'/users/:id/posts'`.
118
+ * @returns A prefix-anchored `RegExp` with named groups for each parameter.
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * const re = compilePlainPath('/users/:id');
123
+ * re.exec('/users/42/comments')?.groups; // { id: '42' }
124
+ * ```
125
+ */
126
+ function compilePlainPath(path) {
127
+ const segments = path.split('/').filter((s) => s.length > 0);
128
+ const src = segments
129
+ .map((seg) => seg.startsWith(':')
130
+ ? `(?<${seg.slice(1)}>[^/]+)` // named parameter segment
131
+ : seg.replace(/[.+^${}()|[\]\\]/g, '\\$&'))
132
+ .join('/');
133
+ // Allow a trailing slash or an additional path segment after the prefix.
134
+ return new RegExp('^/?' + src + '(?=/|$)');
135
+ }
136
+ /**
137
+ * Convert any supported path pattern into the single canonical `RegExp`
138
+ * used by `matchRouteLayer`.
139
+ *
140
+ * | Input type | Strategy |
141
+ * |--------------|-------------------------------------------------------|
142
+ * | Glob string | {@link compileGlob} — `.gitignore`-style wildcards |
143
+ * | Plain string | {@link compilePlainPath} — `:name` → named groups |
144
+ * | `RegExp` | Used as-is; named groups are surfaced as params |
145
+ *
146
+ * @param path - The raw path pattern supplied by the caller.
147
+ * @returns A `RegExp` suitable for use in `matchRouteLayer`.
148
+ */
149
+ function compilePattern(path) {
150
+ if (path instanceof RegExp)
151
+ return path;
152
+ if (isGlobPattern(path))
153
+ return compileGlob(path);
154
+ return compilePlainPath(path);
155
+ }
156
+ // ---------------------------------------------------------------------------
157
+ // Layer construction
158
+ // ---------------------------------------------------------------------------
159
+ /**
160
+ * Build a `Layer` that represents a single registered route entry.
161
+ *
162
+ * The `path` argument may be a plain string with `:name` parameters, a glob
163
+ * string, or a `RegExp`. In all three cases the pattern is pre-compiled into
164
+ * a single `RegExp` stored as `layer.regex`.
165
+ *
166
+ * @param method - HTTP method to restrict this layer to (uppercased), or
167
+ * `null` to match any method.
168
+ * @param path - URL path pattern (plain string, glob, or `RegExp`).
169
+ * @param middleware - The middleware function to invoke on a match.
170
+ * @param stripPath - When `true`, the matched prefix is stripped from
171
+ * `req.path` before the middleware runs. Pass `true` for
172
+ * prefix/`use` registrations and `false` for exact-method
173
+ * routes so that chained middlewares sharing the same path
174
+ * can each match in turn.
175
+ * @returns A fully initialised `Layer` ready to be pushed into the route table.
176
+ * @throws {TypeError} When `middleware` is not a callable function.
177
+ */
178
+ function buildRouteLayer(method, path, middleware, stripPath) {
179
+ if (typeof middleware !== 'function')
180
+ throw new TypeError('Incorrect middleware type: expected a function');
181
+ return { method, path, regex: compilePattern(path), stripPath, middleware };
182
+ }
183
+ // ---------------------------------------------------------------------------
184
+ // Layer matching
185
+ // ---------------------------------------------------------------------------
186
+ /**
187
+ * Test whether an incoming request matches the given layer and, on success,
188
+ * mutate `req` to reflect the match.
189
+ *
190
+ * All pattern types (plain string, glob, `RegExp`) are handled identically
191
+ * through `layer.regex`. Named capture groups in the match result are merged
192
+ * into `req.params` and `req.queries.route`.
193
+ *
194
+ * Path stripping is conditional on `layer.stripPath`:
195
+ * - `true` (prefix routes) — the matched prefix is removed from `req.path`
196
+ * so nested routers only see the remaining suffix.
197
+ * - `false` (exact-method routes) — `req.path` is left unchanged so that
198
+ * subsequent chained middlewares sharing the same pattern can still match.
199
+ *
200
+ * @param layer - The layer to test.
201
+ * @param req - The incoming request (mutated in-place on a successful match).
202
+ * @param path - The current value of `req.path` to test against.
203
+ * @returns `true` if the layer matches and `req` has been updated;
204
+ * `false` otherwise.
205
+ */
206
+ function matchRouteLayer(layer, req, path) {
207
+ if (layer.method && layer.method !== req.method)
208
+ return false;
209
+ const m = layer.regex.exec(path);
210
+ if (m === null)
211
+ return false;
212
+ const captured = m.groups ?? {};
213
+ // Only rewrite req.path for prefix-style (use) registrations.
214
+ // Exact-method routes leave req.path intact so chained middlewares
215
+ // sharing the same path each see the full, unmodified path.
216
+ if (layer.stripPath) {
217
+ req.path = path.slice(m[0].length) || '/';
218
+ }
219
+ req.queries.route = captured;
220
+ Object.assign(req.params, captured);
221
+ return true;
222
+ }
223
+ // ---------------------------------------------------------------------------
224
+ // HTTP object augmentation
225
+ // ---------------------------------------------------------------------------
226
+ /**
227
+ * Augment a raw `http.IncomingMessage` / `http.ServerResponse` pair with the
228
+ * additional fields and helpers expected by router middleware.
229
+ *
230
+ * This function is idempotent — it exits immediately when `req.queries` is
231
+ * already defined, so it is safe to call multiple times on the same pair.
232
+ *
233
+ * **Fields added to `req`:**
234
+ * - `originalUrl` — the unmodified URL string.
235
+ * - `path` — the pathname portion of the URL.
236
+ * - `params` — merged map initialised from URL query parameters.
237
+ * - `queries` — structured query buckets (`url`, `route`).
238
+ * - `cookies` — parsed `Cookie` header values.
239
+ *
240
+ * **Helpers added to `res`:**
241
+ * - `send(data?)` — write `data` and end the response.
242
+ * - `status(code, headers?)` — set the status code and optional headers.
243
+ * - `redirect(url)` — issue a 302 redirect.
244
+ * - `cookie(name, val, opts)` — append a `Set-Cookie` header.
245
+ *
246
+ * @param req - The raw incoming message to augment.
247
+ * @param res - The raw server response to augment.
248
+ */
249
+ function updateHttpObjects(req, res) {
250
+ const rReq = req;
251
+ const rRes = res;
252
+ if (rReq.queries)
253
+ return; // Already augmented.
254
+ rReq.queries = {};
255
+ const qry = new URL(`http://${req.headers.host}${req.url}`);
256
+ rReq.originalUrl = req.url;
257
+ rReq.path = qry.pathname;
258
+ // Parse URL query parameters.
259
+ const urlParams = {};
260
+ for (const [key, value] of qry.searchParams.entries())
261
+ urlParams[key] = value;
262
+ rReq.queries.url = urlParams;
263
+ rReq.params = { ...urlParams };
264
+ // Parse cookies.
265
+ if (rReq.cookies == null) {
266
+ rReq.cookies = {};
267
+ if (req.headers.cookie) {
268
+ for (const raw of req.headers.cookie.split(';')) {
269
+ const eqIdx = raw.indexOf('=');
270
+ if (eqIdx === -1)
271
+ continue;
272
+ rReq.cookies[raw.slice(0, eqIdx).trim()] = raw.slice(eqIdx + 1).trim();
273
+ // TODO: 's:' prefix → signed cookie, 'j:' prefix → JSON cookie
274
+ }
275
+ }
276
+ }
277
+ rRes.setHeader('X-Powered-By', 'Expediate');
278
+ rRes.send = (data) => {
279
+ if (data)
280
+ res.write(data);
281
+ res.end();
282
+ };
283
+ rRes.status = (code, headers) => {
284
+ res.statusCode = code;
285
+ if (headers)
286
+ for (const [k, v] of Object.entries(headers))
287
+ res.setHeader(k, v);
288
+ return rRes;
289
+ };
290
+ rRes.redirect = (url) => {
291
+ res.setHeader('location', url);
292
+ res.writeHead(302);
293
+ res.write(`Found. Redirecting to ${url}`);
294
+ res.end();
295
+ };
296
+ rRes.cookie = (name, value, options) => {
297
+ const opts = options ?? {};
298
+ if (opts.signed && !req.secret)
299
+ throw new Error('cookieParser("secret") required for signed cookies');
300
+ let val = typeof value === 'object' ? 'j:' + JSON.stringify(value) : String(value);
301
+ if (opts.signed)
302
+ val = 's:' + val; // sign() integration point
303
+ let txt = `${name}=${val}`;
304
+ if (opts.maxAge != null) {
305
+ opts.expires = new Date(Date.now() + opts.maxAge);
306
+ opts.maxAge = Math.floor(opts.maxAge / 1000);
307
+ txt += `; Max-Age=${opts.maxAge}`;
308
+ }
309
+ if (opts.expires)
310
+ txt += `; Expires=${opts.expires.toUTCString()}`;
311
+ txt += `; Path=${opts.path ?? '/'}`;
312
+ res.setHeader('Set-Cookie', txt);
313
+ return rRes;
314
+ };
315
+ }
316
+ // ---------------------------------------------------------------------------
317
+ // Route registration
318
+ // ---------------------------------------------------------------------------
319
+ /**
320
+ * Recursively resolve a `MiddlewareArg` value down to individual `Middleware`
321
+ * functions and push one `Layer` per function into the route table.
322
+ *
323
+ * Accepted input shapes (processed recursively):
324
+ * - A `Middleware` function → pushed directly as a layer.
325
+ * - A `Router` instance → its `listener` is unwrapped and pushed.
326
+ * - An array of either → each element is processed recursively.
327
+ *
328
+ * @param routes - The mutable route table to push into.
329
+ * @param method - HTTP method string or `null` for method-agnostic layers.
330
+ * @param path - URL pattern (plain string, glob, or `RegExp`).
331
+ * @param arg - The middleware value(s) to register.
332
+ * @param stripPath - Forwarded to `buildRouteLayer`. Pass `true` for prefix-
333
+ * style registrations (`use`) so the matched prefix
334
+ * is stripped from `req.path`, and `false` for exact-method
335
+ * routes so that chained middlewares sharing the same path
336
+ * each see the unmodified path.
337
+ * @throws {TypeError} When `arg` contains a value that cannot be resolved to
338
+ * a `Middleware` function.
339
+ */
340
+ function registerRoute(routes, method, path, arg, stripPath) {
341
+ if (Array.isArray(arg)) {
342
+ for (const item of arg)
343
+ registerRoute(routes, method, path, item, stripPath);
344
+ }
345
+ else if (typeof arg === 'function') {
346
+ routes.push(buildRouteLayer(method, path, arg, stripPath));
347
+ }
348
+ else if (arg && typeof arg.listener === 'function') {
349
+ // Router instance — unwrap its listener.
350
+ routes.push(buildRouteLayer(method, path, arg.listener, stripPath));
351
+ }
352
+ else {
353
+ throw new TypeError('Unexpected value registered as middleware: expected a Middleware ' +
354
+ 'function, a Router instance, or an array of either');
355
+ }
356
+ }
357
+ // ---------------------------------------------------------------------------
358
+ // Router factory
359
+ // ---------------------------------------------------------------------------
360
+ /**
361
+ * Create and return a new `Router` instance.
362
+ *
363
+ * The returned object exposes an Express-compatible routing API and doubles
364
+ * as a middleware function itself (via `router.listener`), so it can be
365
+ * mounted inside another router:
366
+ * ```ts
367
+ * parent.use('/api', child);
368
+ * ```
369
+ *
370
+ * **Path patterns** accepted by all route-registration methods:
371
+ * - Plain strings with optional `:name` segments — e.g. `'/users/:id'`.
372
+ * Each `:name` is compiled to a named capture group and exposed in
373
+ * `req.params` on a match.
374
+ * - Glob strings following `.gitignore` rules — e.g. `'/**\/*.php'`.
375
+ * Supported wildcards: `?` (one non-slash char), `*` (any non-slash chars),
376
+ * `**` (any chars, including slashes).
377
+ * - `RegExp` objects — used directly; named groups become route parameters.
378
+ *
379
+ * **Middleware arguments** accept any number of variadic `MiddlewareArg`
380
+ * values, each of which may be:
381
+ * - A `Middleware` function.
382
+ * - A `Router` instance (its `listener` is registered automatically).
383
+ * - An array of either of the above.
384
+ *
385
+ * **Path stripping behaviour:**
386
+ * - `use` — strip the matched path prefix from `req.path` before
387
+ * invoking middleware. Nested routers therefore only see the remaining suffix.
388
+ * - `all` / `get` / `post` / `put` / `delete` / `patch` — leave `req.path` intact
389
+ * so that multiple middlewares registered for the same exact path can each
390
+ * match and be invoked in sequence via `next()`.
391
+ *
392
+ * @returns A fully initialised `Router` ready to register routes and
393
+ * optionally start an HTTP or HTTPS server.
394
+ *
395
+ * @example
396
+ * ```ts
397
+ * const auth = createRouter();
398
+ * auth.post('/login', handleLogin);
399
+ * auth.post('/logout', handleLogout);
400
+ *
401
+ * const app = createRouter();
402
+ * app.use('/auth', auth); // mount a sub-router
403
+ * app.get('/users/:id', requireAuth, getUser); // multiple middleware
404
+ * app.get('/**\/*.php', (req, res) => // glob pattern
405
+ * res.status(403).send('Forbidden'));
406
+ *
407
+ * app.listen(3000, () => console.log('Listening on :3000'));
408
+ * ```
409
+ */
410
+ function createRouter() {
411
+ const routes = [];
412
+ /**
413
+ * Core dispatch function. Walks the route table in registration order and
414
+ * invokes the first layer that matches the current request. Falls back to a
415
+ * 404 response when no layer matches and no upstream `done` callback is set.
416
+ */
417
+ const listener = (req, res, done) => {
418
+ const method = req.method;
419
+ const url = req.url;
420
+ let idx = 0;
421
+ updateHttpObjects(req, res);
422
+ const next = () => {
423
+ while (idx < routes.length) {
424
+ const layer = routes[idx++];
425
+ const pathBefore = req.path;
426
+ if (matchRouteLayer(layer, req, req.path)) {
427
+ if (layer.stripPath) {
428
+ // For prefix layers (use), save the pre-strip path so we can
429
+ // restore it if the sub-router calls done() and control returns here.
430
+ // Without restoration, subsequent layers in this router would see
431
+ // the truncated path and fail to match their own patterns.
432
+ const strippedPath = req.path;
433
+ return layer.middleware(req, res, () => {
434
+ req.path = pathBefore; // restore for the next sibling layer
435
+ next();
436
+ });
437
+ }
438
+ return layer.middleware(req, res, next);
439
+ }
440
+ }
441
+ if (done)
442
+ return done();
443
+ res.status(404).end(`Cannot ${method} ${url}`);
444
+ };
445
+ try {
446
+ next();
447
+ }
448
+ catch (e) {
449
+ console.warn(e);
450
+ res.status(500).end(`Error ${method} ${url}`);
451
+ }
452
+ };
453
+ // -------------------------------------------------------------------------
454
+ // Internal helper — produce the uniform registration function for a method.
455
+ // -------------------------------------------------------------------------
456
+ /**
457
+ * Return the route-registration function used by all HTTP-method helpers.
458
+ *
459
+ * The produced function accepts a mandatory `path` followed by any number
460
+ * of `MiddlewareArg` values (functions, `Router` instances, or arrays
461
+ * thereof), and delegates each to `registerRoute`.
462
+ *
463
+ * @param method - HTTP method to restrict layers to, or `null` for any.
464
+ * @param stripPath - Whether the matched path prefix should be stripped from
465
+ * `req.path` before middleware is invoked. `true` for
466
+ * prefix-style registrations, `false` for exact-method ones.
467
+ * @returns A variadic route-registration function.
468
+ */
469
+ function makeRegister(method, stripPath) {
470
+ return (path, ...args) => {
471
+ for (const arg of args)
472
+ registerRoute(routes, method, path, arg, stripPath);
473
+ };
474
+ }
475
+ // -------------------------------------------------------------------------
476
+ // Public router API
477
+ // -------------------------------------------------------------------------
478
+ const router = {
479
+ listener,
480
+ use: makeRegister(null, true), // prefix — strip path
481
+ all: makeRegister(null, false), // exact — keep path
482
+ get: makeRegister('GET', false),
483
+ put: makeRegister('PUT', false),
484
+ post: makeRegister('POST', false),
485
+ delete: makeRegister('DELETE', false),
486
+ patch: makeRegister('PATCH', false),
487
+ listen(port, opts, cb) {
488
+ if (typeof opts === 'function') {
489
+ cb = opts;
490
+ opts = undefined;
491
+ }
492
+ const rawListener = listener;
493
+ if (opts && opts.key && opts.cert)
494
+ https.createServer(opts, rawListener).listen(port, cb);
495
+ else
496
+ http.createServer(rawListener).listen(port, cb);
497
+ },
498
+ };
499
+ return router;
500
+ }
501
+ exports.default = createRouter;
502
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,164 @@
1
+ import type { RouterRequest, RouterResponse, Middleware } from './router';
2
+ export type Mime = {
3
+ lookup: (path: string, fallback: string | null) => string;
4
+ charsets: (mimeType: string) => string | null;
5
+ };
6
+ export declare const mime: Mime;
7
+ /** HTTP header map used both in options and as argument to res.status(). */
8
+ type HeaderMap = Record<string, string>;
9
+ /**
10
+ * Options accepted by {@link serveStatic}, {@link serveFile}, and
11
+ * {@link sendFile}.
12
+ */
13
+ export interface StaticOptions {
14
+ /**
15
+ * Extra HTTP headers added to every response (merged with the built-in
16
+ * security headers `Content-Security-Policy` and `X-Content-Type-Options`).
17
+ */
18
+ headers?: HeaderMap;
19
+ /**
20
+ * When `true`, unhandled requests (wrong method, missing file) are passed
21
+ * to the next middleware via `next()` instead of sending an error response.
22
+ * Defaults to `false`.
23
+ */
24
+ fallthrough?: boolean;
25
+ /**
26
+ * Browser cache lifetime for served files, in **milliseconds**.
27
+ * Translated to a `Cache-Control: public, max-age=<seconds>` header.
28
+ * Defaults to `0` (no caching).
29
+ */
30
+ maxage?: number;
31
+ /** Alias for {@link maxage}. */
32
+ maxAge?: number;
33
+ /**
34
+ * When `true`, appends `, immutable` to the `Cache-Control` header,
35
+ * signalling that the response will never change during its `max-age`
36
+ * window. Only meaningful when {@link maxage} is non-zero.
37
+ */
38
+ immutable?: boolean;
39
+ /**
40
+ * When `true` (default), the server generates and sends a weak `ETag`
41
+ * header based on the file's size and last-modification time.
42
+ */
43
+ etag?: boolean;
44
+ /**
45
+ * When `true` (default), the server sends a `Last-Modified` header
46
+ * derived from the file's `mtime`.
47
+ */
48
+ lastModified?: boolean;
49
+ /**
50
+ * Override the auto-detected `Content-Type`. When set, MIME detection via
51
+ * the `mime` package is skipped entirely.
52
+ */
53
+ contentType?: string | null;
54
+ /**
55
+ * Controls how dot-files (files or directories whose name starts with `.`)
56
+ * are handled:
57
+ * - `'allow'` — serve them like any other file.
58
+ * - `'deny'` — respond with 403 Forbidden.
59
+ * - `'hide'` — respond with 404 Not Found (default).
60
+ */
61
+ dotfiles?: 'allow' | 'deny' | 'hide';
62
+ /**
63
+ * When `true` (default), a request for a directory path is transparently
64
+ * redirected to `index.html` inside that directory.
65
+ * When `false`, directory requests receive a 404 response.
66
+ */
67
+ redirect?: boolean;
68
+ /**
69
+ * When `true`, a request for a directory path that has no `index.html`
70
+ * renders an Apache-style directory listing.
71
+ * Defaults to `false`.
72
+ */
73
+ indexOf?: boolean;
74
+ }
75
+ /**
76
+ * Fully resolved options used internally after merging caller-supplied values
77
+ * with {@link DEFAULT_OPTIONS}. All optional fields from {@link StaticOptions}
78
+ * are guaranteed to be present.
79
+ */
80
+ interface ResolvedOptions extends Required<Omit<StaticOptions, 'maxAge'>> {
81
+ /** Absolute, normalised path to the root directory or file. */
82
+ root: string;
83
+ }
84
+ /**
85
+ * Send a single file from the filesystem as an HTTP response.
86
+ *
87
+ * Unlike the {@link serveStatic} middleware, this function resolves the
88
+ * response for a **specific** `pathname` that has already been determined by
89
+ * the caller. It is useful for custom routing logic where the file path is
90
+ * computed dynamically.
91
+ *
92
+ * Behaviour:
93
+ * - When `pathname` refers to a **regular file**, it is served via
94
+ * {@link sendIt} (including ETag, Last-Modified, conditional GET, and
95
+ * MIME-type detection).
96
+ * - When `pathname` refers to a **directory** and `opts.indexOf` is `true`,
97
+ * an Apache-style directory listing is rendered.
98
+ * - Otherwise, a 404 Not Found response is sent.
99
+ *
100
+ * @param req - The incoming router request.
101
+ * @param res - The outgoing router response.
102
+ * @param pathname - Absolute filesystem path of the file to send.
103
+ * @param opts - Fully resolved static-serving options.
104
+ */
105
+ export declare function sendFile(req: RouterRequest, res: RouterResponse, pathname: string, opts: ResolvedOptions): void;
106
+ /**
107
+ * Middleware factory that serves files from a directory on the filesystem.
108
+ *
109
+ * Mount this middleware with a path prefix to expose a public directory:
110
+ * ```ts
111
+ * app.use('/public', serveStatic('./dist'));
112
+ * ```
113
+ *
114
+ * Features:
115
+ * - **Method filtering** — only `GET` and `HEAD` are served; other methods
116
+ * receive 405 Method Not Allowed (or are forwarded to `next()` when
117
+ * `fallthrough` is `true`).
118
+ * - **Directory traversal protection** — any path containing `..` is
119
+ * rejected with 403 Forbidden.
120
+ * - **Dot-file handling** — configurable via the `dotfiles` option
121
+ * (`'allow'`, `'deny'`, or `'hide'`).
122
+ * - **Directory redirect** — a path resolving to a directory is automatically
123
+ * redirected to its `index.html` when `redirect` is `true`.
124
+ * - **Conditional GET** — ETag and Last-Modified headers enable browser and
125
+ * proxy caching with proper revalidation.
126
+ * - **Cache-Control** — configurable via `maxage` / `immutable` options.
127
+ * - **MIME-type detection** — via the `mime` npm package.
128
+ *
129
+ * @param root - Path to the directory containing the files to serve.
130
+ * Resolved to an absolute path internally.
131
+ * @param options - Optional configuration (see {@link StaticOptions}).
132
+ * @returns An Express-compatible middleware function.
133
+ * @throws {TypeError} When `root` is missing or not a string.
134
+ */
135
+ export declare function serveStatic(root: string, options?: StaticOptions): Middleware;
136
+ /**
137
+ * Middleware factory that serves a **single, fixed file** as the response to
138
+ * every request, regardless of the request path.
139
+ *
140
+ * Use this when you want every route to return the same file — for example,
141
+ * serving a compiled single-page application's `index.html` as a catch-all:
142
+ * ```ts
143
+ * app.get('/**', serveFile('./dist/index.html'));
144
+ * ```
145
+ *
146
+ * Features:
147
+ * - **Method filtering** — only `GET` and `HEAD` are served.
148
+ * - **Conditional GET** — ETag and Last-Modified are generated from the file
149
+ * metadata on every request.
150
+ * - **MIME-type detection** — via the `mime` npm package.
151
+ *
152
+ * @param filePath - Path to the file to serve. Resolved to an absolute path.
153
+ * @param options - Optional configuration (see {@link StaticOptions}).
154
+ * @returns An Express-compatible middleware function.
155
+ * @throws {TypeError} When `filePath` is missing or not a string.
156
+ */
157
+ export declare function serveFile(filePath: string, options?: StaticOptions): Middleware;
158
+ declare const _default: {
159
+ serveStatic: typeof serveStatic;
160
+ serveFile: typeof serveFile;
161
+ sendFile: typeof sendFile;
162
+ };
163
+ export default _default;
164
+ //# sourceMappingURL=static.d.ts.map