routup 4.1.0 → 5.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -136
- package/dist/bun.cjs +69 -0
- package/dist/bun.cjs.map +1 -0
- package/dist/bun.d.cts +8 -0
- package/dist/bun.d.mts +8 -0
- package/dist/bun.mjs +13 -0
- package/dist/bun.mjs.map +1 -0
- package/dist/cloudflare.cjs +69 -0
- package/dist/cloudflare.cjs.map +1 -0
- package/dist/cloudflare.d.cts +8 -0
- package/dist/cloudflare.d.mts +8 -0
- package/dist/cloudflare.mjs +13 -0
- package/dist/cloudflare.mjs.map +1 -0
- package/dist/deno.cjs +69 -0
- package/dist/deno.cjs.map +1 -0
- package/dist/deno.d.cts +8 -0
- package/dist/deno.d.mts +8 -0
- package/dist/deno.mjs +13 -0
- package/dist/deno.mjs.map +1 -0
- package/dist/generic.cjs +69 -0
- package/dist/generic.cjs.map +1 -0
- package/dist/generic.d.cts +8 -0
- package/dist/generic.d.mts +8 -0
- package/dist/generic.mjs +13 -0
- package/dist/generic.mjs.map +1 -0
- package/dist/index-DN3wy86V.d.mts +841 -0
- package/dist/index-Dm50O_sj.d.cts +841 -0
- package/dist/node.cjs +73 -0
- package/dist/node.cjs.map +1 -0
- package/dist/node.d.cts +11 -0
- package/dist/node.d.mts +11 -0
- package/dist/node.mjs +16 -0
- package/dist/node.mjs.map +1 -0
- package/dist/service-worker.cjs +69 -0
- package/dist/service-worker.cjs.map +1 -0
- package/dist/service-worker.d.cts +10 -0
- package/dist/service-worker.d.mts +10 -0
- package/dist/service-worker.mjs +13 -0
- package/dist/service-worker.mjs.map +1 -0
- package/dist/src-CpLmo5Po.mjs +1602 -0
- package/dist/src-CpLmo5Po.mjs.map +1 -0
- package/dist/src-D_4IPMmD.cjs +1960 -0
- package/dist/src-D_4IPMmD.cjs.map +1 -0
- package/package.json +75 -34
- package/dist/adapters/index.d.ts +0 -3
- package/dist/adapters/node/index.d.ts +0 -1
- package/dist/adapters/node/module.d.ts +0 -6
- package/dist/adapters/raw/header.d.ts +0 -3
- package/dist/adapters/raw/index.d.ts +0 -3
- package/dist/adapters/raw/module.d.ts +0 -4
- package/dist/adapters/raw/type.d.ts +0 -18
- package/dist/adapters/web/index.d.ts +0 -2
- package/dist/adapters/web/module.d.ts +0 -4
- package/dist/adapters/web/type.d.ts +0 -3
- package/dist/constants.d.ts +0 -42
- package/dist/dispatcher/event/dispatch.d.ts +0 -4
- package/dist/dispatcher/event/error.d.ts +0 -5
- package/dist/dispatcher/event/index.d.ts +0 -5
- package/dist/dispatcher/event/is.d.ts +0 -3
- package/dist/dispatcher/event/module.d.ts +0 -56
- package/dist/dispatcher/event/types.d.ts +0 -9
- package/dist/dispatcher/index.d.ts +0 -2
- package/dist/dispatcher/type.d.ts +0 -4
- package/dist/error/create.d.ts +0 -11
- package/dist/error/index.d.ts +0 -3
- package/dist/error/is.d.ts +0 -2
- package/dist/error/module.d.ts +0 -3
- package/dist/handler/constants.d.ts +0 -5
- package/dist/handler/core/define.d.ts +0 -4
- package/dist/handler/core/index.d.ts +0 -2
- package/dist/handler/core/types.d.ts +0 -10
- package/dist/handler/error/define.d.ts +0 -4
- package/dist/handler/error/index.d.ts +0 -2
- package/dist/handler/error/types.d.ts +0 -11
- package/dist/handler/index.d.ts +0 -7
- package/dist/handler/is.d.ts +0 -4
- package/dist/handler/module.d.ts +0 -23
- package/dist/handler/types-base.d.ts +0 -10
- package/dist/handler/types.d.ts +0 -4
- package/dist/hook/constants.d.ts +0 -8
- package/dist/hook/index.d.ts +0 -3
- package/dist/hook/module.d.ts +0 -19
- package/dist/hook/types.d.ts +0 -5
- package/dist/index.cjs +0 -2314
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.ts +0 -11
- package/dist/index.mjs +0 -2235
- package/dist/index.mjs.map +0 -1
- package/dist/path/index.d.ts +0 -3
- package/dist/path/matcher.d.ts +0 -11
- package/dist/path/type.d.ts +0 -7
- package/dist/path/utils.d.ts +0 -2
- package/dist/plugin/index.d.ts +0 -2
- package/dist/plugin/is.d.ts +0 -2
- package/dist/plugin/types.d.ts +0 -23
- package/dist/request/helpers/cache.d.ts +0 -2
- package/dist/request/helpers/env.d.ts +0 -6
- package/dist/request/helpers/header-accept-charset.d.ts +0 -3
- package/dist/request/helpers/header-accept-encoding.d.ts +0 -3
- package/dist/request/helpers/header-accept-language.d.ts +0 -3
- package/dist/request/helpers/header-accept.d.ts +0 -3
- package/dist/request/helpers/header-content-type.d.ts +0 -2
- package/dist/request/helpers/header.d.ts +0 -4
- package/dist/request/helpers/hostname.d.ts +0 -7
- package/dist/request/helpers/http2.d.ts +0 -2
- package/dist/request/helpers/index.d.ts +0 -17
- package/dist/request/helpers/ip.d.ts +0 -7
- package/dist/request/helpers/mount-path.d.ts +0 -3
- package/dist/request/helpers/negotiator.d.ts +0 -3
- package/dist/request/helpers/params.d.ts +0 -5
- package/dist/request/helpers/path.d.ts +0 -2
- package/dist/request/helpers/protocol.d.ts +0 -8
- package/dist/request/helpers/router.d.ts +0 -3
- package/dist/request/index.d.ts +0 -3
- package/dist/request/module.d.ts +0 -3
- package/dist/request/types.d.ts +0 -12
- package/dist/response/helpers/cache.d.ts +0 -7
- package/dist/response/helpers/event-stream/factory.d.ts +0 -3
- package/dist/response/helpers/event-stream/index.d.ts +0 -2
- package/dist/response/helpers/event-stream/module.d.ts +0 -17
- package/dist/response/helpers/event-stream/types.d.ts +0 -24
- package/dist/response/helpers/event-stream/utils.d.ts +0 -2
- package/dist/response/helpers/gone.d.ts +0 -3
- package/dist/response/helpers/header-attachment.d.ts +0 -2
- package/dist/response/helpers/header-content-type.d.ts +0 -2
- package/dist/response/helpers/header.d.ts +0 -4
- package/dist/response/helpers/index.d.ts +0 -16
- package/dist/response/helpers/send-accepted.d.ts +0 -2
- package/dist/response/helpers/send-created.d.ts +0 -2
- package/dist/response/helpers/send-file.d.ts +0 -17
- package/dist/response/helpers/send-format.d.ts +0 -7
- package/dist/response/helpers/send-redirect.d.ts +0 -2
- package/dist/response/helpers/send-stream.d.ts +0 -3
- package/dist/response/helpers/send-web-blob.d.ts +0 -3
- package/dist/response/helpers/send-web-response.d.ts +0 -3
- package/dist/response/helpers/send.d.ts +0 -2
- package/dist/response/helpers/utils.d.ts +0 -2
- package/dist/response/index.d.ts +0 -3
- package/dist/response/module.d.ts +0 -3
- package/dist/response/types.d.ts +0 -3
- package/dist/router/constants.d.ts +0 -9
- package/dist/router/index.d.ts +0 -1
- package/dist/router/module.d.ts +0 -89
- package/dist/router/types.d.ts +0 -7
- package/dist/router/utils.d.ts +0 -3
- package/dist/router-options/index.d.ts +0 -2
- package/dist/router-options/module.d.ts +0 -4
- package/dist/router-options/transform.d.ts +0 -2
- package/dist/router-options/type.d.ts +0 -41
- package/dist/types.d.ts +0 -9
- package/dist/utils/cookie.d.ts +0 -1
- package/dist/utils/etag/index.d.ts +0 -3
- package/dist/utils/etag/module.d.ts +0 -11
- package/dist/utils/etag/type.d.ts +0 -15
- package/dist/utils/etag/utils.d.ts +0 -2
- package/dist/utils/index.d.ts +0 -13
- package/dist/utils/is-instance.d.ts +0 -1
- package/dist/utils/method.d.ts +0 -3
- package/dist/utils/mime.d.ts +0 -2
- package/dist/utils/next.d.ts +0 -2
- package/dist/utils/object.d.ts +0 -3
- package/dist/utils/path.d.ts +0 -5
- package/dist/utils/promise.d.ts +0 -1
- package/dist/utils/stream.d.ts +0 -6
- package/dist/utils/trust-proxy/index.d.ts +0 -2
- package/dist/utils/trust-proxy/module.d.ts +0 -2
- package/dist/utils/trust-proxy/type.d.ts +0 -2
- package/dist/utils/url.d.ts +0 -7
- package/dist/utils/web.d.ts +0 -3
|
@@ -0,0 +1,1602 @@
|
|
|
1
|
+
import { HTTPError, isHTTPError } from "@ebec/http";
|
|
2
|
+
import { Buffer } from "node:buffer";
|
|
3
|
+
import { subtle } from "uncrypto";
|
|
4
|
+
import { distinctArray, hasOwnProperty, merge } from "smob";
|
|
5
|
+
import { compile } from "proxy-addr";
|
|
6
|
+
import { get, getType } from "mime-explorer";
|
|
7
|
+
import { FastURL } from "srvx";
|
|
8
|
+
import { pathToRegexp } from "path-to-regexp";
|
|
9
|
+
import Negotiator from "negotiator";
|
|
10
|
+
//#region src/constants.ts
|
|
11
|
+
let MethodName = /* @__PURE__ */ function(MethodName) {
|
|
12
|
+
MethodName["GET"] = "GET";
|
|
13
|
+
MethodName["POST"] = "POST";
|
|
14
|
+
MethodName["PUT"] = "PUT";
|
|
15
|
+
MethodName["PATCH"] = "PATCH";
|
|
16
|
+
MethodName["DELETE"] = "DELETE";
|
|
17
|
+
MethodName["OPTIONS"] = "OPTIONS";
|
|
18
|
+
MethodName["HEAD"] = "HEAD";
|
|
19
|
+
return MethodName;
|
|
20
|
+
}({});
|
|
21
|
+
let HeaderName = /* @__PURE__ */ function(HeaderName) {
|
|
22
|
+
HeaderName["ACCEPT"] = "accept";
|
|
23
|
+
HeaderName["ACCEPT_CHARSET"] = "accept-charset";
|
|
24
|
+
HeaderName["ACCEPT_ENCODING"] = "accept-encoding";
|
|
25
|
+
HeaderName["ACCEPT_LANGUAGE"] = "accept-language";
|
|
26
|
+
HeaderName["ACCEPT_RANGES"] = "accept-ranges";
|
|
27
|
+
HeaderName["ALLOW"] = "allow";
|
|
28
|
+
HeaderName["CACHE_CONTROL"] = "cache-control";
|
|
29
|
+
HeaderName["CONTENT_DISPOSITION"] = "content-disposition";
|
|
30
|
+
HeaderName["CONTENT_ENCODING"] = "content-encoding";
|
|
31
|
+
HeaderName["CONTENT_LENGTH"] = "content-length";
|
|
32
|
+
HeaderName["CONTENT_RANGE"] = "content-range";
|
|
33
|
+
HeaderName["CONTENT_TYPE"] = "content-type";
|
|
34
|
+
HeaderName["CONNECTION"] = "connection";
|
|
35
|
+
HeaderName["COOKIE"] = "cookie";
|
|
36
|
+
HeaderName["ETag"] = "etag";
|
|
37
|
+
HeaderName["HOST"] = "host";
|
|
38
|
+
HeaderName["IF_MODIFIED_SINCE"] = "if-modified-since";
|
|
39
|
+
HeaderName["IF_NONE_MATCH"] = "if-none-match";
|
|
40
|
+
HeaderName["LAST_MODIFIED"] = "last-modified";
|
|
41
|
+
HeaderName["LOCATION"] = "location";
|
|
42
|
+
HeaderName["RANGE"] = "range";
|
|
43
|
+
HeaderName["RATE_LIMIT_LIMIT"] = "ratelimit-limit";
|
|
44
|
+
HeaderName["RATE_LIMIT_REMAINING"] = "ratelimit-remaining";
|
|
45
|
+
HeaderName["RATE_LIMIT_RESET"] = "ratelimit-reset";
|
|
46
|
+
HeaderName["RETRY_AFTER"] = "retry-after";
|
|
47
|
+
HeaderName["SET_COOKIE"] = "set-cookie";
|
|
48
|
+
HeaderName["TRANSFER_ENCODING"] = "transfer-encoding";
|
|
49
|
+
HeaderName["X_ACCEL_BUFFERING"] = "x-accel-buffering";
|
|
50
|
+
HeaderName["X_FORWARDED_HOST"] = "x-forwarded-host";
|
|
51
|
+
HeaderName["X_FORWARDED_FOR"] = "x-forwarded-for";
|
|
52
|
+
HeaderName["X_FORWARDED_PROTO"] = "x-forwarded-proto";
|
|
53
|
+
return HeaderName;
|
|
54
|
+
}({});
|
|
55
|
+
//#endregion
|
|
56
|
+
//#region src/utils/header.ts
|
|
57
|
+
function sanitizeHeaderValue(value) {
|
|
58
|
+
return value.replace(/[\r\n]/g, "");
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/utils/object.ts
|
|
62
|
+
function isObject(item) {
|
|
63
|
+
return !!item && typeof item === "object" && !Array.isArray(item);
|
|
64
|
+
}
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region src/utils/etag/module.ts
|
|
67
|
+
/**
|
|
68
|
+
* Determine if object is a Stats object.
|
|
69
|
+
*
|
|
70
|
+
* @param {object} obj
|
|
71
|
+
* @return {boolean}
|
|
72
|
+
* @api private
|
|
73
|
+
*/
|
|
74
|
+
function isStatsObject(obj) {
|
|
75
|
+
return isObject(obj) && "ctime" in obj && Object.prototype.toString.call(obj.ctime) === "[object Date]" && "mtime" in obj && Object.prototype.toString.call(obj.mtime) === "[object Date]" && "ino" in obj && typeof obj.ino === "number" && "size" in obj && typeof obj.size === "number";
|
|
76
|
+
}
|
|
77
|
+
async function sha1(str) {
|
|
78
|
+
const enc = new TextEncoder();
|
|
79
|
+
const hash = await subtle.digest("SHA-1", enc.encode(str));
|
|
80
|
+
return btoa(String.fromCharCode(...new Uint8Array(hash)));
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Generate an ETag.
|
|
84
|
+
*/
|
|
85
|
+
async function generateETag(input) {
|
|
86
|
+
if (isStatsObject(input)) {
|
|
87
|
+
const mtime = input.mtime.getTime().toString(16);
|
|
88
|
+
return `"${input.size.toString(16)}-${mtime}"`;
|
|
89
|
+
}
|
|
90
|
+
if (input.length === 0) return "\"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk\"";
|
|
91
|
+
const entity = Buffer.isBuffer(input) ? input.toString("utf-8") : input;
|
|
92
|
+
const hash = await sha1(entity);
|
|
93
|
+
return `"${entity.length.toString(16)}-${hash.substring(0, 27)}"`;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Create a simple ETag.
|
|
97
|
+
*/
|
|
98
|
+
async function createEtag(input, options) {
|
|
99
|
+
options = options || {};
|
|
100
|
+
const weak = typeof options.weak === "boolean" ? options.weak : isStatsObject(input);
|
|
101
|
+
const tag = await generateETag(input);
|
|
102
|
+
return weak ? `W/${tag}` : tag;
|
|
103
|
+
}
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region src/utils/etag/utils.ts
|
|
106
|
+
function buildEtagFn(input) {
|
|
107
|
+
if (typeof input === "function") return input;
|
|
108
|
+
input = input ?? true;
|
|
109
|
+
if (input === false) return () => Promise.resolve(void 0);
|
|
110
|
+
let options = { weak: true };
|
|
111
|
+
if (isObject(input)) options = merge(input, options);
|
|
112
|
+
return async (body, encoding, size) => {
|
|
113
|
+
const buff = Buffer.isBuffer(body) ? body : Buffer.from(body, encoding);
|
|
114
|
+
if (typeof options.threshold !== "undefined") {
|
|
115
|
+
size = size ?? Buffer.byteLength(buff);
|
|
116
|
+
if (size <= options.threshold) return;
|
|
117
|
+
}
|
|
118
|
+
return createEtag(buff, options);
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
122
|
+
//#region src/utils/trust-proxy/module.ts
|
|
123
|
+
function buildTrustProxyFn(input) {
|
|
124
|
+
if (typeof input === "function") return input;
|
|
125
|
+
if (input === true) return () => true;
|
|
126
|
+
if (typeof input === "number") return (_address, hop) => hop < input;
|
|
127
|
+
if (typeof input === "string") input = input.split(",").map((value) => value.trim());
|
|
128
|
+
return compile(input || []);
|
|
129
|
+
}
|
|
130
|
+
//#endregion
|
|
131
|
+
//#region src/utils/is-instance.ts
|
|
132
|
+
function isInstance(input, sym) {
|
|
133
|
+
if (!isObject(input)) return false;
|
|
134
|
+
return input["@instanceof"] === sym;
|
|
135
|
+
}
|
|
136
|
+
//#endregion
|
|
137
|
+
//#region src/utils/mime.ts
|
|
138
|
+
function getMimeType(type) {
|
|
139
|
+
if (type.includes("/")) return type;
|
|
140
|
+
return getType(type);
|
|
141
|
+
}
|
|
142
|
+
function getCharsetForMimeType(type) {
|
|
143
|
+
if (/^text\/|^application\/(javascript|json)/.test(type)) return "utf-8";
|
|
144
|
+
const meta = get(type);
|
|
145
|
+
if (meta && meta.charset) return meta.charset.toLowerCase();
|
|
146
|
+
}
|
|
147
|
+
//#endregion
|
|
148
|
+
//#region src/utils/method.ts
|
|
149
|
+
function toMethodName(input, alt) {
|
|
150
|
+
if (input) return input.toUpperCase();
|
|
151
|
+
return alt;
|
|
152
|
+
}
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region src/utils/path.ts
|
|
155
|
+
/**
|
|
156
|
+
* Based on https://github.com/unjs/pathe v1.1.1 (055f50a6f1131f4e5c56cf259dd8816168fba329)
|
|
157
|
+
*/
|
|
158
|
+
function normalizeWindowsPath(input = "") {
|
|
159
|
+
if (!input || !input.includes("\\")) return input;
|
|
160
|
+
return input.replace(/\\/g, "/");
|
|
161
|
+
}
|
|
162
|
+
const EXTNAME_RE = /.(\.[^./]+)$/;
|
|
163
|
+
function extname(input) {
|
|
164
|
+
const match = EXTNAME_RE.exec(normalizeWindowsPath(input));
|
|
165
|
+
return match && match[1] || "";
|
|
166
|
+
}
|
|
167
|
+
function basename(input, extension) {
|
|
168
|
+
const lastSegment = normalizeWindowsPath(input).split("/").pop();
|
|
169
|
+
if (!lastSegment) return input;
|
|
170
|
+
return extension && lastSegment.endsWith(extension) ? lastSegment.slice(0, -extension.length) : lastSegment;
|
|
171
|
+
}
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region src/utils/url.ts
|
|
174
|
+
const TRAILING_SLASH_RE = /\/$|\/\?/;
|
|
175
|
+
function hasTrailingSlash(input = "", queryParams = false) {
|
|
176
|
+
if (!queryParams) return input.endsWith("/");
|
|
177
|
+
return TRAILING_SLASH_RE.test(input);
|
|
178
|
+
}
|
|
179
|
+
function withoutTrailingSlash(input = "", queryParams = false) {
|
|
180
|
+
if (!queryParams) return (hasTrailingSlash(input) ? input.slice(0, -1) : input) || "/";
|
|
181
|
+
if (!hasTrailingSlash(input, true)) return input || "/";
|
|
182
|
+
const [s0, ...s] = input.split("?");
|
|
183
|
+
return (s0.slice(0, -1) || "/") + (s.length ? `?${s.join("?")}` : "");
|
|
184
|
+
}
|
|
185
|
+
function hasLeadingSlash(input = "") {
|
|
186
|
+
return input.startsWith("/");
|
|
187
|
+
}
|
|
188
|
+
function withLeadingSlash(input = "") {
|
|
189
|
+
return hasLeadingSlash(input) ? input : `/${input}`;
|
|
190
|
+
}
|
|
191
|
+
function cleanDoubleSlashes(input = "") {
|
|
192
|
+
if (input.includes("://")) return input.split("://").map((str) => cleanDoubleSlashes(str)).join("://");
|
|
193
|
+
return input.replace(/\/+/g, "/");
|
|
194
|
+
}
|
|
195
|
+
//#endregion
|
|
196
|
+
//#region src/error/is.ts
|
|
197
|
+
function isError(input) {
|
|
198
|
+
if (!isHTTPError(input)) return false;
|
|
199
|
+
return input.name === "RoutupError";
|
|
200
|
+
}
|
|
201
|
+
//#endregion
|
|
202
|
+
//#region src/error/module.ts
|
|
203
|
+
var RoutupError = class extends HTTPError {
|
|
204
|
+
constructor(input = {}) {
|
|
205
|
+
super(input);
|
|
206
|
+
this.name = "RoutupError";
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
//#endregion
|
|
210
|
+
//#region src/error/create.ts
|
|
211
|
+
function isNativeError(input) {
|
|
212
|
+
return isObject(input) && typeof input.message === "string" && typeof input.name === "string";
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Create an internal error object by
|
|
216
|
+
* - an existing RoutupError (returned as-is)
|
|
217
|
+
* - an HTTPError (wrapped into a RoutupError preserving status)
|
|
218
|
+
* - an Error (wrapped preserving message and cause)
|
|
219
|
+
* - an options object (statusCode, statusMessage, etc.)
|
|
220
|
+
* - a message string
|
|
221
|
+
*
|
|
222
|
+
* @param input
|
|
223
|
+
*/
|
|
224
|
+
function createError(input) {
|
|
225
|
+
if (isError(input)) return input;
|
|
226
|
+
if (typeof input === "string") return new RoutupError(input);
|
|
227
|
+
if (isHTTPError(input)) return new RoutupError({
|
|
228
|
+
message: input.message,
|
|
229
|
+
code: input.code,
|
|
230
|
+
statusCode: input.statusCode,
|
|
231
|
+
statusMessage: input.statusMessage,
|
|
232
|
+
redirectURL: input.redirectURL,
|
|
233
|
+
cause: input
|
|
234
|
+
});
|
|
235
|
+
if (isNativeError(input)) return new RoutupError({
|
|
236
|
+
message: input.message,
|
|
237
|
+
cause: input
|
|
238
|
+
});
|
|
239
|
+
if (!isObject(input)) return new RoutupError();
|
|
240
|
+
const options = { ...input };
|
|
241
|
+
if (options.cause === void 0) options.cause = input;
|
|
242
|
+
return new RoutupError(options);
|
|
243
|
+
}
|
|
244
|
+
//#endregion
|
|
245
|
+
//#region src/event/module.ts
|
|
246
|
+
var RoutupEvent = class {
|
|
247
|
+
request;
|
|
248
|
+
params;
|
|
249
|
+
path;
|
|
250
|
+
method;
|
|
251
|
+
mountPath;
|
|
252
|
+
error;
|
|
253
|
+
routerPath;
|
|
254
|
+
/**
|
|
255
|
+
* Collected allowed methods (for OPTIONS).
|
|
256
|
+
*/
|
|
257
|
+
methodsAllowed;
|
|
258
|
+
store;
|
|
259
|
+
_dispatched;
|
|
260
|
+
_response;
|
|
261
|
+
/**
|
|
262
|
+
* Cached parsed URL (avoids double-parsing).
|
|
263
|
+
*/
|
|
264
|
+
_url;
|
|
265
|
+
_searchParams;
|
|
266
|
+
/**
|
|
267
|
+
* Continuation function for middleware onion model.
|
|
268
|
+
*/
|
|
269
|
+
_next;
|
|
270
|
+
/**
|
|
271
|
+
* Whether _next has already been called (guard against double-invocation).
|
|
272
|
+
*/
|
|
273
|
+
_nextCalled;
|
|
274
|
+
/**
|
|
275
|
+
* The cached result of the next handler.
|
|
276
|
+
*/
|
|
277
|
+
_nextResult;
|
|
278
|
+
constructor(request) {
|
|
279
|
+
this.request = request;
|
|
280
|
+
this._url = new FastURL(request.url);
|
|
281
|
+
this.method = request.method;
|
|
282
|
+
this.path = this._url.pathname;
|
|
283
|
+
this.mountPath = "/";
|
|
284
|
+
this.params = {};
|
|
285
|
+
this.routerPath = [];
|
|
286
|
+
this.methodsAllowed = [];
|
|
287
|
+
this.store = Object.create(null);
|
|
288
|
+
this._dispatched = false;
|
|
289
|
+
this._nextCalled = false;
|
|
290
|
+
}
|
|
291
|
+
get headers() {
|
|
292
|
+
return this.request.headers;
|
|
293
|
+
}
|
|
294
|
+
get searchParams() {
|
|
295
|
+
if (!this._searchParams) this._searchParams = new URLSearchParams(this._url.search);
|
|
296
|
+
return this._searchParams;
|
|
297
|
+
}
|
|
298
|
+
get response() {
|
|
299
|
+
if (!this._response) this._response = {
|
|
300
|
+
status: 200,
|
|
301
|
+
headers: new Headers()
|
|
302
|
+
};
|
|
303
|
+
return this._response;
|
|
304
|
+
}
|
|
305
|
+
get dispatched() {
|
|
306
|
+
return this._dispatched;
|
|
307
|
+
}
|
|
308
|
+
set dispatched(value) {
|
|
309
|
+
this._dispatched = value;
|
|
310
|
+
}
|
|
311
|
+
async next() {
|
|
312
|
+
if (this._nextCalled) return this._nextResult;
|
|
313
|
+
this._nextCalled = true;
|
|
314
|
+
if (this._next) this._nextResult = this._next();
|
|
315
|
+
return this._nextResult;
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
//#endregion
|
|
319
|
+
//#region src/handler/constants.ts
|
|
320
|
+
let HandlerType = /* @__PURE__ */ function(HandlerType) {
|
|
321
|
+
HandlerType["CORE"] = "core";
|
|
322
|
+
HandlerType["ERROR"] = "error";
|
|
323
|
+
return HandlerType;
|
|
324
|
+
}({});
|
|
325
|
+
const HandlerSymbol = /* @__PURE__ */ Symbol.for("Handler");
|
|
326
|
+
//#endregion
|
|
327
|
+
//#region src/hook/constants.ts
|
|
328
|
+
let HookName = /* @__PURE__ */ function(HookName) {
|
|
329
|
+
HookName["REQUEST"] = "request";
|
|
330
|
+
HookName["RESPONSE"] = "response";
|
|
331
|
+
HookName["ERROR"] = "error";
|
|
332
|
+
HookName["CHILD_MATCH"] = "childMatch";
|
|
333
|
+
HookName["CHILD_DISPATCH_BEFORE"] = "childDispatchBefore";
|
|
334
|
+
HookName["CHILD_DISPATCH_AFTER"] = "childDispatchAfter";
|
|
335
|
+
return HookName;
|
|
336
|
+
}({});
|
|
337
|
+
//#endregion
|
|
338
|
+
//#region src/hook/module.ts
|
|
339
|
+
var HookManager = class {
|
|
340
|
+
items;
|
|
341
|
+
constructor() {
|
|
342
|
+
this.items = {};
|
|
343
|
+
}
|
|
344
|
+
addListener(name, fn) {
|
|
345
|
+
this.items[name] = this.items[name] || [];
|
|
346
|
+
this.items[name].push(fn);
|
|
347
|
+
return () => {
|
|
348
|
+
this.removeListener(name, fn);
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
removeListener(name, fn) {
|
|
352
|
+
if (!this.items[name]) return;
|
|
353
|
+
if (typeof fn === "undefined") {
|
|
354
|
+
delete this.items[name];
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
if (typeof fn === "function") {
|
|
358
|
+
const index = this.items[name].indexOf(fn);
|
|
359
|
+
if (index !== -1) this.items[name].splice(index, 1);
|
|
360
|
+
}
|
|
361
|
+
if (this.items[name].length === 0) delete this.items[name];
|
|
362
|
+
}
|
|
363
|
+
async trigger(name, event) {
|
|
364
|
+
if (!this.items[name] || this.items[name].length === 0) return;
|
|
365
|
+
try {
|
|
366
|
+
for (let i = 0; i < this.items[name].length; i++) {
|
|
367
|
+
const listener = this.items[name][i];
|
|
368
|
+
await this.triggerListener(name, event, listener);
|
|
369
|
+
if (event.dispatched) {
|
|
370
|
+
if (event.error) event.error = void 0;
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
} catch (e) {
|
|
375
|
+
event.error = e;
|
|
376
|
+
if (!this.isErrorListenerHook(name)) {
|
|
377
|
+
await this.trigger(HookName.ERROR, event);
|
|
378
|
+
if (event.dispatched) {
|
|
379
|
+
if (event.error) event.error = void 0;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
triggerListener(name, event, listener) {
|
|
385
|
+
if (this.isErrorListenerHook(name)) {
|
|
386
|
+
if (event.error) return listener(event);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
return listener(event);
|
|
390
|
+
}
|
|
391
|
+
isErrorListenerHook(input) {
|
|
392
|
+
return input === HookName.ERROR;
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
//#endregion
|
|
396
|
+
//#region src/path/matcher.ts
|
|
397
|
+
function decodeParam(val) {
|
|
398
|
+
/* istanbul ignore next */
|
|
399
|
+
if (typeof val !== "string" || val.length === 0) return val;
|
|
400
|
+
try {
|
|
401
|
+
return decodeURIComponent(val);
|
|
402
|
+
} catch {
|
|
403
|
+
return val;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
var PathMatcher = class {
|
|
407
|
+
path;
|
|
408
|
+
regexp;
|
|
409
|
+
regexpKeys = [];
|
|
410
|
+
regexpOptions;
|
|
411
|
+
constructor(path, options) {
|
|
412
|
+
this.path = path;
|
|
413
|
+
this.regexpOptions = options || {};
|
|
414
|
+
const regexp = pathToRegexp(path, options);
|
|
415
|
+
this.regexp = regexp.regexp;
|
|
416
|
+
this.regexpKeys = regexp.keys;
|
|
417
|
+
}
|
|
418
|
+
test(path) {
|
|
419
|
+
return this.regexp.test(path);
|
|
420
|
+
}
|
|
421
|
+
exec(path) {
|
|
422
|
+
if (this.path === "/" && this.regexpOptions.end === false) return {
|
|
423
|
+
path: "/",
|
|
424
|
+
params: Object.create(null)
|
|
425
|
+
};
|
|
426
|
+
const match = this.regexp.exec(path);
|
|
427
|
+
if (!match) return;
|
|
428
|
+
const params = Object.create(null);
|
|
429
|
+
for (let i = 1; i < match.length; i++) {
|
|
430
|
+
const key = this.regexpKeys[i - 1];
|
|
431
|
+
if (!key) continue;
|
|
432
|
+
const prop = key.name;
|
|
433
|
+
const val = decodeParam(match[i]);
|
|
434
|
+
if (typeof val !== "undefined") params[prop] = val;
|
|
435
|
+
}
|
|
436
|
+
return {
|
|
437
|
+
path: match[0],
|
|
438
|
+
params
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
//#endregion
|
|
443
|
+
//#region src/path/utils.ts
|
|
444
|
+
function isPath(input) {
|
|
445
|
+
return typeof input === "string";
|
|
446
|
+
}
|
|
447
|
+
//#endregion
|
|
448
|
+
//#region src/response/helpers/cache.ts
|
|
449
|
+
function setResponseCacheHeaders(event, options) {
|
|
450
|
+
options = options || {};
|
|
451
|
+
const cacheControls = ["public"].concat(options.cacheControls || []);
|
|
452
|
+
if (options.maxAge !== void 0) cacheControls.push(`max-age=${+options.maxAge}`, `s-maxage=${+options.maxAge}`);
|
|
453
|
+
if (options.modifiedTime) {
|
|
454
|
+
const modifiedTime = typeof options.modifiedTime === "string" ? new Date(options.modifiedTime) : options.modifiedTime;
|
|
455
|
+
event.response.headers.set("last-modified", modifiedTime.toUTCString());
|
|
456
|
+
}
|
|
457
|
+
event.response.headers.set("cache-control", cacheControls.join(", "));
|
|
458
|
+
}
|
|
459
|
+
//#endregion
|
|
460
|
+
//#region src/response/helpers/event-stream/utils.ts
|
|
461
|
+
function stripNewlines(value) {
|
|
462
|
+
return value.replace(/[\r\n]/g, "");
|
|
463
|
+
}
|
|
464
|
+
function serializeEventStreamMessage(message) {
|
|
465
|
+
let result = "";
|
|
466
|
+
if (message.id) result += `id: ${stripNewlines(message.id)}\n`;
|
|
467
|
+
if (message.event) result += `event: ${stripNewlines(message.event)}\n`;
|
|
468
|
+
if (typeof message.retry === "number" && Number.isInteger(message.retry)) result += `retry: ${message.retry}\n`;
|
|
469
|
+
const lines = message.data.replace(/\r/g, "").split("\n");
|
|
470
|
+
for (const line of lines) result += `data: ${line}\n`;
|
|
471
|
+
result += "\n";
|
|
472
|
+
return result;
|
|
473
|
+
}
|
|
474
|
+
//#endregion
|
|
475
|
+
//#region src/response/helpers/event-stream/module.ts
|
|
476
|
+
function createEventStream(event, options) {
|
|
477
|
+
if (options?.maxMessageSize !== void 0) {
|
|
478
|
+
if (!Number.isInteger(options.maxMessageSize) || options.maxMessageSize < 0) throw new RoutupError("maxMessageSize must be a non-negative integer.");
|
|
479
|
+
}
|
|
480
|
+
let controller;
|
|
481
|
+
let closed = false;
|
|
482
|
+
const encoder = new TextEncoder();
|
|
483
|
+
const stream = new ReadableStream({
|
|
484
|
+
start(ctrl) {
|
|
485
|
+
controller = ctrl;
|
|
486
|
+
},
|
|
487
|
+
cancel() {
|
|
488
|
+
closed = true;
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
const headers = new Headers(event.response.headers);
|
|
492
|
+
headers.set(HeaderName.CONTENT_TYPE, "text/event-stream");
|
|
493
|
+
headers.set(HeaderName.CACHE_CONTROL, "private, no-cache, no-store, no-transform, must-revalidate, max-age=0");
|
|
494
|
+
headers.set(HeaderName.X_ACCEL_BUFFERING, "no");
|
|
495
|
+
headers.set(HeaderName.CONNECTION, "keep-alive");
|
|
496
|
+
const handle = {
|
|
497
|
+
write(message) {
|
|
498
|
+
if (closed) return;
|
|
499
|
+
if (typeof message === "string") {
|
|
500
|
+
handle.write({ data: message });
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
const serialized = serializeEventStreamMessage(message);
|
|
504
|
+
if (options?.maxMessageSize !== void 0) {
|
|
505
|
+
if (encoder.encode(serialized).byteLength > options.maxMessageSize) return;
|
|
506
|
+
}
|
|
507
|
+
controller.enqueue(encoder.encode(serialized));
|
|
508
|
+
},
|
|
509
|
+
end() {
|
|
510
|
+
if (closed) return;
|
|
511
|
+
closed = true;
|
|
512
|
+
controller.close();
|
|
513
|
+
},
|
|
514
|
+
response: new Response(stream, {
|
|
515
|
+
status: event.response.status,
|
|
516
|
+
statusText: event.response.statusText,
|
|
517
|
+
headers
|
|
518
|
+
})
|
|
519
|
+
};
|
|
520
|
+
return handle;
|
|
521
|
+
}
|
|
522
|
+
//#endregion
|
|
523
|
+
//#region src/response/helpers/gone.ts
|
|
524
|
+
function isResponseGone(event) {
|
|
525
|
+
return event.dispatched;
|
|
526
|
+
}
|
|
527
|
+
function setResponseGone(event) {
|
|
528
|
+
event.dispatched = true;
|
|
529
|
+
}
|
|
530
|
+
//#endregion
|
|
531
|
+
//#region src/response/helpers/header.ts
|
|
532
|
+
function appendResponseHeader(event, name, value) {
|
|
533
|
+
const { headers } = event.response;
|
|
534
|
+
if (Array.isArray(value)) for (const v of value) headers.append(name, sanitizeHeaderValue(v));
|
|
535
|
+
else headers.append(name, sanitizeHeaderValue(value));
|
|
536
|
+
}
|
|
537
|
+
function appendResponseHeaderDirective(event, name, value) {
|
|
538
|
+
const { headers } = event.response;
|
|
539
|
+
const existing = headers.get(name);
|
|
540
|
+
if (!existing) {
|
|
541
|
+
if (Array.isArray(value)) headers.set(name, sanitizeHeaderValue(value.join("; ")));
|
|
542
|
+
else headers.set(name, sanitizeHeaderValue(value));
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
const directives = existing.split("; ");
|
|
546
|
+
if (Array.isArray(value)) directives.push(...value);
|
|
547
|
+
else directives.push(value);
|
|
548
|
+
const unique = [...new Set(directives)];
|
|
549
|
+
headers.set(name, sanitizeHeaderValue(unique.join("; ")));
|
|
550
|
+
}
|
|
551
|
+
//#endregion
|
|
552
|
+
//#region src/response/helpers/utils.ts
|
|
553
|
+
function setResponseContentTypeByFileName(event, fileName) {
|
|
554
|
+
const ext = extname(fileName);
|
|
555
|
+
if (ext) {
|
|
556
|
+
let type = getMimeType(ext.substring(1));
|
|
557
|
+
if (type) {
|
|
558
|
+
const charset = getCharsetForMimeType(type);
|
|
559
|
+
if (charset) type += `; charset=${charset}`;
|
|
560
|
+
event.response.headers.set(HeaderName.CONTENT_TYPE, type);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
//#endregion
|
|
565
|
+
//#region src/response/helpers/header-attachment.ts
|
|
566
|
+
function sanitizeFilename(filename) {
|
|
567
|
+
return filename.replace(/[\r\n]/g, "");
|
|
568
|
+
}
|
|
569
|
+
function toAsciiFilename(filename) {
|
|
570
|
+
return filename.replace(/[^\x20-\x7E]/g, "").replace(/"/g, "\\\"");
|
|
571
|
+
}
|
|
572
|
+
function encodeRfc5987(filename) {
|
|
573
|
+
return encodeURIComponent(filename).replace(/['()]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`).replace(/\*/g, "%2A");
|
|
574
|
+
}
|
|
575
|
+
function setResponseHeaderAttachment(event, filename) {
|
|
576
|
+
if (typeof filename === "string") setResponseContentTypeByFileName(event, filename);
|
|
577
|
+
let disposition = "attachment";
|
|
578
|
+
if (filename) {
|
|
579
|
+
const sanitized = sanitizeFilename(filename);
|
|
580
|
+
const ascii = toAsciiFilename(sanitized);
|
|
581
|
+
disposition += `; filename="${ascii}"`;
|
|
582
|
+
disposition += `; filename*=UTF-8''${encodeRfc5987(sanitized)}`;
|
|
583
|
+
}
|
|
584
|
+
event.response.headers.set(HeaderName.CONTENT_DISPOSITION, disposition);
|
|
585
|
+
}
|
|
586
|
+
//#endregion
|
|
587
|
+
//#region src/response/helpers/header-content-type.ts
|
|
588
|
+
function setResponseHeaderContentType(event, input, ifNotExists) {
|
|
589
|
+
if (ifNotExists) {
|
|
590
|
+
if (event.response.headers.get(HeaderName.CONTENT_TYPE)) return;
|
|
591
|
+
}
|
|
592
|
+
const contentType = getMimeType(input);
|
|
593
|
+
if (contentType) event.response.headers.set(HeaderName.CONTENT_TYPE, contentType);
|
|
594
|
+
}
|
|
595
|
+
//#endregion
|
|
596
|
+
//#region src/router-options/module.ts
|
|
597
|
+
const defaults = {
|
|
598
|
+
trustProxy: () => false,
|
|
599
|
+
subdomainOffset: 2,
|
|
600
|
+
etag: buildEtagFn(),
|
|
601
|
+
proxyIpMax: 0
|
|
602
|
+
};
|
|
603
|
+
const instances = {};
|
|
604
|
+
function setRouterOptions(id, input) {
|
|
605
|
+
instances[id] = input;
|
|
606
|
+
}
|
|
607
|
+
function findRouterOption(key, path) {
|
|
608
|
+
if (!path || path.length === 0) return defaults[key];
|
|
609
|
+
if (path.length > 0) for (let i = path.length; i >= 0; i--) {
|
|
610
|
+
const segment = path[i];
|
|
611
|
+
if (segment !== void 0 && hasOwnProperty(instances, segment) && typeof instances[segment][key] !== "undefined") return instances[segment][key];
|
|
612
|
+
}
|
|
613
|
+
return defaults[key];
|
|
614
|
+
}
|
|
615
|
+
//#endregion
|
|
616
|
+
//#region src/response/to-response.ts
|
|
617
|
+
function stripWeakPrefix(etag) {
|
|
618
|
+
return etag.startsWith("W/") ? etag.slice(2) : etag;
|
|
619
|
+
}
|
|
620
|
+
async function applyEtag(body, event, headers) {
|
|
621
|
+
const etagFn = findRouterOption("etag", event.routerPath);
|
|
622
|
+
if (!etagFn) return void 0;
|
|
623
|
+
const etag = await etagFn(body);
|
|
624
|
+
if (!etag) return void 0;
|
|
625
|
+
headers.set("etag", etag);
|
|
626
|
+
const ifNoneMatch = event.headers.get("if-none-match");
|
|
627
|
+
if (ifNoneMatch && (ifNoneMatch === "*" || ifNoneMatch.split(",").some((t) => stripWeakPrefix(t.trim()) === stripWeakPrefix(etag)))) return new Response(null, {
|
|
628
|
+
status: 304,
|
|
629
|
+
headers
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
async function toResponse(value, event) {
|
|
633
|
+
if (value === void 0) return;
|
|
634
|
+
if (value === null) return new Response(null, {
|
|
635
|
+
status: event.response.status,
|
|
636
|
+
statusText: event.response.statusText,
|
|
637
|
+
headers: event.response.headers
|
|
638
|
+
});
|
|
639
|
+
if (value instanceof Response) return value;
|
|
640
|
+
const { status, headers, statusText } = event.response;
|
|
641
|
+
if (typeof value === "string") {
|
|
642
|
+
if (!headers.has("content-type")) headers.set("content-type", "text/plain; charset=utf-8");
|
|
643
|
+
const cached = await applyEtag(value, event, headers);
|
|
644
|
+
if (cached) return cached;
|
|
645
|
+
return new Response(value, {
|
|
646
|
+
status,
|
|
647
|
+
statusText,
|
|
648
|
+
headers
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
if (value instanceof ArrayBuffer || value instanceof Uint8Array) {
|
|
652
|
+
if (!headers.has("content-type")) headers.set("content-type", "application/octet-stream");
|
|
653
|
+
return new Response(value, {
|
|
654
|
+
status,
|
|
655
|
+
statusText,
|
|
656
|
+
headers
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
if (value instanceof ReadableStream) return new Response(value, {
|
|
660
|
+
status,
|
|
661
|
+
statusText,
|
|
662
|
+
headers
|
|
663
|
+
});
|
|
664
|
+
if (value instanceof Blob) {
|
|
665
|
+
if (!headers.has("content-type")) headers.set("content-type", value.type || "application/octet-stream");
|
|
666
|
+
return new Response(value, {
|
|
667
|
+
status,
|
|
668
|
+
statusText,
|
|
669
|
+
headers
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
if (!headers.has("content-type")) headers.set("content-type", "application/json; charset=utf-8");
|
|
673
|
+
const json = JSON.stringify(value);
|
|
674
|
+
const cached = await applyEtag(json, event, headers);
|
|
675
|
+
if (cached) return cached;
|
|
676
|
+
return new Response(json, {
|
|
677
|
+
status,
|
|
678
|
+
statusText,
|
|
679
|
+
headers
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
//#endregion
|
|
683
|
+
//#region src/response/helpers/send-accepted.ts
|
|
684
|
+
async function sendAccepted(event, data) {
|
|
685
|
+
event.response.status = 202;
|
|
686
|
+
event.response.statusText = "Accepted";
|
|
687
|
+
event.dispatched = true;
|
|
688
|
+
return await toResponse(data ?? "", event);
|
|
689
|
+
}
|
|
690
|
+
//#endregion
|
|
691
|
+
//#region src/response/helpers/send-created.ts
|
|
692
|
+
async function sendCreated(event, data) {
|
|
693
|
+
event.response.status = 201;
|
|
694
|
+
event.response.statusText = "Created";
|
|
695
|
+
event.dispatched = true;
|
|
696
|
+
return await toResponse(data ?? "", event);
|
|
697
|
+
}
|
|
698
|
+
//#endregion
|
|
699
|
+
//#region src/response/helpers/send-file.ts
|
|
700
|
+
async function sendFile(event, options) {
|
|
701
|
+
const stats = await options.stats();
|
|
702
|
+
const name = options.name || stats.name;
|
|
703
|
+
const { headers } = event.response;
|
|
704
|
+
if (name) {
|
|
705
|
+
const fileName = basename(name);
|
|
706
|
+
if (options.attachment) {
|
|
707
|
+
if (!headers.get(HeaderName.CONTENT_DISPOSITION)) setResponseHeaderAttachment(event, fileName);
|
|
708
|
+
} else setResponseContentTypeByFileName(event, fileName);
|
|
709
|
+
}
|
|
710
|
+
const contentOptions = {};
|
|
711
|
+
let statusCode = event.response.status;
|
|
712
|
+
if (stats.size) {
|
|
713
|
+
const rangeHeader = event.headers.get(HeaderName.RANGE);
|
|
714
|
+
if (rangeHeader) {
|
|
715
|
+
const [x, y] = rangeHeader.replace("bytes=", "").split("-");
|
|
716
|
+
const parsedStart = Number.parseInt(x, 10);
|
|
717
|
+
const parsedEnd = Number.parseInt(y, 10);
|
|
718
|
+
contentOptions.start = Number.isFinite(parsedStart) && parsedStart >= 0 ? parsedStart : 0;
|
|
719
|
+
contentOptions.end = Number.isFinite(parsedEnd) && parsedEnd >= 0 ? Math.min(parsedEnd, stats.size - 1) : stats.size - 1;
|
|
720
|
+
if (contentOptions.start >= stats.size || contentOptions.start > contentOptions.end) {
|
|
721
|
+
event.dispatched = true;
|
|
722
|
+
const rangeHeaders = new Headers(headers);
|
|
723
|
+
rangeHeaders.set(HeaderName.CONTENT_RANGE, `bytes */${stats.size}`);
|
|
724
|
+
return new Response(null, {
|
|
725
|
+
status: 416,
|
|
726
|
+
statusText: event.response.statusText,
|
|
727
|
+
headers: rangeHeaders
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
headers.set(HeaderName.CONTENT_RANGE, `bytes ${contentOptions.start}-${contentOptions.end}/${stats.size}`);
|
|
731
|
+
headers.set(HeaderName.CONTENT_LENGTH, `${contentOptions.end - contentOptions.start + 1}`);
|
|
732
|
+
statusCode = 206;
|
|
733
|
+
} else headers.set(HeaderName.CONTENT_LENGTH, `${stats.size}`);
|
|
734
|
+
headers.set(HeaderName.ACCEPT_RANGES, "bytes");
|
|
735
|
+
if (stats.mtime) {
|
|
736
|
+
const mtime = new Date(stats.mtime);
|
|
737
|
+
headers.set(HeaderName.LAST_MODIFIED, mtime.toUTCString());
|
|
738
|
+
headers.set(HeaderName.ETag, `W/"${stats.size}-${mtime.getTime()}"`);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
const content = await options.content(contentOptions);
|
|
742
|
+
event.dispatched = true;
|
|
743
|
+
return new Response(content, {
|
|
744
|
+
status: statusCode,
|
|
745
|
+
statusText: event.response.statusText,
|
|
746
|
+
headers
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
//#endregion
|
|
750
|
+
//#region src/request/helpers/header.ts
|
|
751
|
+
function getRequestHeader(event, name) {
|
|
752
|
+
return event.headers.get(name);
|
|
753
|
+
}
|
|
754
|
+
//#endregion
|
|
755
|
+
//#region src/request/helpers/negotiator.ts
|
|
756
|
+
const NEGOTIATOR_KEY = /* @__PURE__ */ Symbol.for("routup:negotiator");
|
|
757
|
+
function headersToPlainObject(headers) {
|
|
758
|
+
const result = {};
|
|
759
|
+
headers.forEach((value, key) => {
|
|
760
|
+
result[key] = value;
|
|
761
|
+
});
|
|
762
|
+
return result;
|
|
763
|
+
}
|
|
764
|
+
function useRequestNegotiator(event) {
|
|
765
|
+
let value = event.store[NEGOTIATOR_KEY];
|
|
766
|
+
if (value) return value;
|
|
767
|
+
value = new Negotiator({ headers: headersToPlainObject(event.headers) });
|
|
768
|
+
event.store[NEGOTIATOR_KEY] = value;
|
|
769
|
+
return value;
|
|
770
|
+
}
|
|
771
|
+
//#endregion
|
|
772
|
+
//#region src/request/helpers/header-accept.ts
|
|
773
|
+
function getRequestAcceptableContentTypes(event) {
|
|
774
|
+
return useRequestNegotiator(event).mediaTypes();
|
|
775
|
+
}
|
|
776
|
+
function getRequestAcceptableContentType(event, input) {
|
|
777
|
+
input = input || [];
|
|
778
|
+
const items = Array.isArray(input) ? input : [input];
|
|
779
|
+
if (items.length === 0) return getRequestAcceptableContentTypes(event).shift();
|
|
780
|
+
if (!getRequestHeader(event, HeaderName.ACCEPT)) return items[0];
|
|
781
|
+
let polluted = false;
|
|
782
|
+
const mimeTypes = [];
|
|
783
|
+
for (const item of items) {
|
|
784
|
+
const mimeType = getMimeType(item);
|
|
785
|
+
if (mimeType) mimeTypes.push(mimeType);
|
|
786
|
+
else polluted = true;
|
|
787
|
+
}
|
|
788
|
+
const matches = useRequestNegotiator(event).mediaTypes(mimeTypes);
|
|
789
|
+
if (matches.length > 0) {
|
|
790
|
+
if (polluted) return items[0];
|
|
791
|
+
return items[mimeTypes.indexOf(matches[0])];
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
//#endregion
|
|
795
|
+
//#region src/response/helpers/send-format.ts
|
|
796
|
+
function sendFormat(event, input) {
|
|
797
|
+
const { default: formatDefault, ...formats } = input;
|
|
798
|
+
const contentTypes = Object.keys(formats);
|
|
799
|
+
if (contentTypes.length === 0) return formatDefault();
|
|
800
|
+
const contentType = getRequestAcceptableContentType(event, contentTypes);
|
|
801
|
+
if (contentType && formats[contentType]) return formats[contentType]();
|
|
802
|
+
return formatDefault();
|
|
803
|
+
}
|
|
804
|
+
//#endregion
|
|
805
|
+
//#region src/response/helpers/send-redirect.ts
|
|
806
|
+
function escapeHtml(str) {
|
|
807
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
808
|
+
}
|
|
809
|
+
function isAllowedRedirectUrl(location) {
|
|
810
|
+
if (location.startsWith("//")) return false;
|
|
811
|
+
if (location.startsWith("/") || location.startsWith(".")) return true;
|
|
812
|
+
try {
|
|
813
|
+
const url = new URL(location);
|
|
814
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
815
|
+
} catch {
|
|
816
|
+
return true;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
function sendRedirect(event, location, statusCode = 302) {
|
|
820
|
+
if (!isAllowedRedirectUrl(location)) throw new RoutupError({
|
|
821
|
+
statusCode: 400,
|
|
822
|
+
statusMessage: "Invalid redirect URL scheme."
|
|
823
|
+
});
|
|
824
|
+
const sanitizedLocation = sanitizeHeaderValue(location);
|
|
825
|
+
const html = `<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=${escapeHtml(location)}"></head></html>`;
|
|
826
|
+
const headers = new Headers(event.response.headers);
|
|
827
|
+
headers.set("location", sanitizedLocation);
|
|
828
|
+
headers.set("content-type", "text/html; charset=utf-8");
|
|
829
|
+
headers.delete("content-length");
|
|
830
|
+
const response = new Response(html, {
|
|
831
|
+
status: statusCode,
|
|
832
|
+
statusText: event.response.statusText,
|
|
833
|
+
headers
|
|
834
|
+
});
|
|
835
|
+
event.dispatched = true;
|
|
836
|
+
return response;
|
|
837
|
+
}
|
|
838
|
+
//#endregion
|
|
839
|
+
//#region src/response/helpers/send-stream.ts
|
|
840
|
+
function sendStream(event, stream) {
|
|
841
|
+
event.dispatched = true;
|
|
842
|
+
const { status, statusText, headers } = event.response;
|
|
843
|
+
return new Response(stream, {
|
|
844
|
+
status,
|
|
845
|
+
statusText,
|
|
846
|
+
headers
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
//#endregion
|
|
850
|
+
//#region src/handler/module.ts
|
|
851
|
+
var Handler = class {
|
|
852
|
+
"@instanceof" = HandlerSymbol;
|
|
853
|
+
config;
|
|
854
|
+
hookManager;
|
|
855
|
+
pathMatcher;
|
|
856
|
+
_method;
|
|
857
|
+
constructor(handler) {
|
|
858
|
+
this.config = handler;
|
|
859
|
+
this.hookManager = new HookManager();
|
|
860
|
+
this.mountHooks();
|
|
861
|
+
this.setPath(handler.path);
|
|
862
|
+
}
|
|
863
|
+
get type() {
|
|
864
|
+
return this.config.type;
|
|
865
|
+
}
|
|
866
|
+
get path() {
|
|
867
|
+
return this.config.path;
|
|
868
|
+
}
|
|
869
|
+
get method() {
|
|
870
|
+
if (this._method || !this.config.method) return this._method;
|
|
871
|
+
this._method = toMethodName(this.config.method);
|
|
872
|
+
return this._method;
|
|
873
|
+
}
|
|
874
|
+
async dispatch(event) {
|
|
875
|
+
if (this.pathMatcher) {
|
|
876
|
+
const pathMatch = this.pathMatcher.exec(event.path);
|
|
877
|
+
if (pathMatch) event.params = {
|
|
878
|
+
...event.params,
|
|
879
|
+
...pathMatch.params
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
await this.hookManager.trigger(HookName.CHILD_DISPATCH_BEFORE, event);
|
|
883
|
+
if (event.dispatched) return;
|
|
884
|
+
let response;
|
|
885
|
+
try {
|
|
886
|
+
let result;
|
|
887
|
+
if (this.config.type === HandlerType.ERROR) {
|
|
888
|
+
if (event.error) result = await this.config.fn(event.error, event);
|
|
889
|
+
} else result = await this.config.fn(event);
|
|
890
|
+
response = await toResponse(result, event);
|
|
891
|
+
if (response) event.dispatched = true;
|
|
892
|
+
} catch (e) {
|
|
893
|
+
event.error = isError(e) ? e : createError(e);
|
|
894
|
+
await this.hookManager.trigger(HookName.ERROR, event);
|
|
895
|
+
if (event.dispatched) event.error = void 0;
|
|
896
|
+
else throw event.error;
|
|
897
|
+
}
|
|
898
|
+
await this.hookManager.trigger(HookName.CHILD_DISPATCH_AFTER, event);
|
|
899
|
+
return response;
|
|
900
|
+
}
|
|
901
|
+
matchPath(path) {
|
|
902
|
+
if (!this.pathMatcher) return true;
|
|
903
|
+
return this.pathMatcher.test(path);
|
|
904
|
+
}
|
|
905
|
+
setPath(path) {
|
|
906
|
+
if (typeof path === "string") path = withLeadingSlash(path);
|
|
907
|
+
this.config.path = path;
|
|
908
|
+
if (typeof path === "undefined") {
|
|
909
|
+
this.pathMatcher = void 0;
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
this.pathMatcher = new PathMatcher(path, { end: !!this.config.method });
|
|
913
|
+
}
|
|
914
|
+
matchMethod(method) {
|
|
915
|
+
return !this.method || method === this.method || method === MethodName.HEAD && this.method === MethodName.GET;
|
|
916
|
+
}
|
|
917
|
+
setMethod(input) {
|
|
918
|
+
const method = toMethodName(input);
|
|
919
|
+
this.config.method = method;
|
|
920
|
+
this._method = method;
|
|
921
|
+
}
|
|
922
|
+
mountHooks() {
|
|
923
|
+
if (this.config.onBefore) this.hookManager.addListener(HookName.CHILD_DISPATCH_BEFORE, this.config.onBefore);
|
|
924
|
+
if (this.config.onAfter) this.hookManager.addListener(HookName.CHILD_DISPATCH_AFTER, this.config.onAfter);
|
|
925
|
+
if (this.config.onError) this.hookManager.addListener(HookName.ERROR, this.config.onError);
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
//#endregion
|
|
929
|
+
//#region src/handler/core/define.ts
|
|
930
|
+
function defineCoreHandler(input) {
|
|
931
|
+
if (typeof input === "function") return new Handler({
|
|
932
|
+
type: HandlerType.CORE,
|
|
933
|
+
fn: input
|
|
934
|
+
});
|
|
935
|
+
return new Handler({
|
|
936
|
+
type: HandlerType.CORE,
|
|
937
|
+
...input
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
//#endregion
|
|
941
|
+
//#region src/handler/error/define.ts
|
|
942
|
+
function defineErrorHandler(input) {
|
|
943
|
+
if (typeof input === "function") return new Handler({
|
|
944
|
+
type: HandlerType.ERROR,
|
|
945
|
+
fn: input
|
|
946
|
+
});
|
|
947
|
+
return new Handler({
|
|
948
|
+
type: HandlerType.ERROR,
|
|
949
|
+
...input
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
//#endregion
|
|
953
|
+
//#region src/handler/adapters/node/define.ts
|
|
954
|
+
const kHandled = /* @__PURE__ */ Symbol("handled");
|
|
955
|
+
function callHandler(handler, req, res) {
|
|
956
|
+
return new Promise((resolve, reject) => {
|
|
957
|
+
let settled = false;
|
|
958
|
+
const onClose = () => settle(kHandled);
|
|
959
|
+
const onFinish = () => settle(kHandled);
|
|
960
|
+
const onError = (error) => fail(error);
|
|
961
|
+
function cleanup() {
|
|
962
|
+
res.removeListener("close", onClose);
|
|
963
|
+
res.removeListener("finish", onFinish);
|
|
964
|
+
res.removeListener("error", onError);
|
|
965
|
+
}
|
|
966
|
+
function settle(value) {
|
|
967
|
+
if (settled) return;
|
|
968
|
+
settled = true;
|
|
969
|
+
cleanup();
|
|
970
|
+
resolve(value);
|
|
971
|
+
}
|
|
972
|
+
function fail(error) {
|
|
973
|
+
if (settled) return;
|
|
974
|
+
settled = true;
|
|
975
|
+
cleanup();
|
|
976
|
+
reject(error);
|
|
977
|
+
}
|
|
978
|
+
res.once("close", onClose);
|
|
979
|
+
res.once("finish", onFinish);
|
|
980
|
+
res.once("error", onError);
|
|
981
|
+
try {
|
|
982
|
+
Promise.resolve(handler(req, res)).then(() => settle(kHandled)).catch(fail);
|
|
983
|
+
} catch (error) {
|
|
984
|
+
fail(error);
|
|
985
|
+
}
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
function callMiddleware(handler, req, res) {
|
|
989
|
+
return new Promise((resolve, reject) => {
|
|
990
|
+
let settled = false;
|
|
991
|
+
const onClose = () => settle(kHandled);
|
|
992
|
+
const onFinish = () => settle(kHandled);
|
|
993
|
+
const onError = (error) => fail(error);
|
|
994
|
+
function cleanup() {
|
|
995
|
+
res.removeListener("close", onClose);
|
|
996
|
+
res.removeListener("finish", onFinish);
|
|
997
|
+
res.removeListener("error", onError);
|
|
998
|
+
}
|
|
999
|
+
function settle(value) {
|
|
1000
|
+
if (settled) return;
|
|
1001
|
+
settled = true;
|
|
1002
|
+
cleanup();
|
|
1003
|
+
resolve(value);
|
|
1004
|
+
}
|
|
1005
|
+
function fail(error) {
|
|
1006
|
+
if (settled) return;
|
|
1007
|
+
settled = true;
|
|
1008
|
+
cleanup();
|
|
1009
|
+
reject(error);
|
|
1010
|
+
}
|
|
1011
|
+
res.once("close", onClose);
|
|
1012
|
+
res.once("finish", onFinish);
|
|
1013
|
+
res.once("error", onError);
|
|
1014
|
+
try {
|
|
1015
|
+
Promise.resolve(handler(req, res, (error) => {
|
|
1016
|
+
if (error) fail(error);
|
|
1017
|
+
else settle(res.writableEnded || res.destroyed ? kHandled : void 0);
|
|
1018
|
+
})).catch(fail);
|
|
1019
|
+
} catch (error) {
|
|
1020
|
+
fail(error);
|
|
1021
|
+
}
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
function createNodeBridge(handler, isMiddleware) {
|
|
1025
|
+
if (typeof handler !== "function") throw new RoutupError("fromNodeHandler/fromNodeMiddleware expects a function.");
|
|
1026
|
+
return defineCoreHandler({ fn: (async (event) => {
|
|
1027
|
+
const node = event.request.runtime?.node;
|
|
1028
|
+
if (!node?.req || !node?.res) throw new RoutupError("fromNodeHandler/fromNodeMiddleware requires a Node.js runtime.");
|
|
1029
|
+
const req = node.req;
|
|
1030
|
+
const res = node.res;
|
|
1031
|
+
if ((isMiddleware ? await callMiddleware(handler, req, res) : await callHandler(handler, req, res)) === kHandled) event.dispatched = true;
|
|
1032
|
+
}) });
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Wraps a Node.js `(req, res)` handler for use in the routup pipeline.
|
|
1036
|
+
*
|
|
1037
|
+
* @example
|
|
1038
|
+
* ```typescript
|
|
1039
|
+
* import { fromNodeHandler } from 'routup/node';
|
|
1040
|
+
*
|
|
1041
|
+
* router.use(fromNodeHandler((req, res) => {
|
|
1042
|
+
* res.end('Hello');
|
|
1043
|
+
* }));
|
|
1044
|
+
* ```
|
|
1045
|
+
*/
|
|
1046
|
+
function fromNodeHandler(handler) {
|
|
1047
|
+
return createNodeBridge(handler, false);
|
|
1048
|
+
}
|
|
1049
|
+
/**
|
|
1050
|
+
* Wraps a Node.js `(req, res, next)` middleware for use in the routup pipeline.
|
|
1051
|
+
*
|
|
1052
|
+
* @example
|
|
1053
|
+
* ```typescript
|
|
1054
|
+
* import cors from 'cors';
|
|
1055
|
+
* import { fromNodeMiddleware } from 'routup/node';
|
|
1056
|
+
*
|
|
1057
|
+
* router.use(fromNodeMiddleware(cors()));
|
|
1058
|
+
* ```
|
|
1059
|
+
*/
|
|
1060
|
+
function fromNodeMiddleware(handler) {
|
|
1061
|
+
return createNodeBridge(handler, true);
|
|
1062
|
+
}
|
|
1063
|
+
//#endregion
|
|
1064
|
+
//#region src/handler/adapters/web/is.ts
|
|
1065
|
+
function isWebHandlerProvider(input) {
|
|
1066
|
+
return isObject(input) && typeof input.fetch === "function";
|
|
1067
|
+
}
|
|
1068
|
+
function isWebHandler(input) {
|
|
1069
|
+
return typeof input === "function";
|
|
1070
|
+
}
|
|
1071
|
+
//#endregion
|
|
1072
|
+
//#region src/handler/adapters/web/define.ts
|
|
1073
|
+
function fromWebHandler(input) {
|
|
1074
|
+
if (isWebHandlerProvider(input)) return fromWebHandler(input.fetch.bind(input));
|
|
1075
|
+
if (typeof input !== "function") throw new RoutupError("fromWebHandler expects a function or an object with a fetch method.");
|
|
1076
|
+
return defineCoreHandler({ fn: (event) => input(event.request) });
|
|
1077
|
+
}
|
|
1078
|
+
//#endregion
|
|
1079
|
+
//#region src/handler/is.ts
|
|
1080
|
+
function isHandlerOptions(input) {
|
|
1081
|
+
return isObject(input) && typeof input.fn === "function" && typeof input.type === "string";
|
|
1082
|
+
}
|
|
1083
|
+
function isHandler(input) {
|
|
1084
|
+
return isInstance(input, HandlerSymbol);
|
|
1085
|
+
}
|
|
1086
|
+
//#endregion
|
|
1087
|
+
//#region src/request/helpers/body.ts
|
|
1088
|
+
const BODY_KEY = /* @__PURE__ */ Symbol.for("routup:body");
|
|
1089
|
+
/**
|
|
1090
|
+
* Read and parse the request body.
|
|
1091
|
+
*
|
|
1092
|
+
* - `application/x-www-form-urlencoded` → plain object (duplicate keys become arrays)
|
|
1093
|
+
* - `application/json` or other → attempts JSON parse, returns undefined on failure
|
|
1094
|
+
*
|
|
1095
|
+
* The result is cached on the event store — calling `readBody()` multiple
|
|
1096
|
+
* times returns the same parsed result.
|
|
1097
|
+
*
|
|
1098
|
+
* For binary or streaming access, use `event.request.arrayBuffer()`,
|
|
1099
|
+
* `event.request.blob()`, or `event.request.body` directly.
|
|
1100
|
+
*
|
|
1101
|
+
* @experimental
|
|
1102
|
+
*/
|
|
1103
|
+
async function readBody(event) {
|
|
1104
|
+
if (BODY_KEY in event.store) return event.store[BODY_KEY];
|
|
1105
|
+
const text = await event.request.text();
|
|
1106
|
+
let result;
|
|
1107
|
+
if ((event.headers.get("content-type") || "").includes("application/x-www-form-urlencoded")) result = parseURLEncodedBody(text);
|
|
1108
|
+
else try {
|
|
1109
|
+
result = JSON.parse(text);
|
|
1110
|
+
} catch {
|
|
1111
|
+
result = void 0;
|
|
1112
|
+
}
|
|
1113
|
+
event.store[BODY_KEY] = result;
|
|
1114
|
+
return result;
|
|
1115
|
+
}
|
|
1116
|
+
function parseURLEncodedBody(body) {
|
|
1117
|
+
const form = new URLSearchParams(body);
|
|
1118
|
+
const parsed = Object.create(null);
|
|
1119
|
+
for (const [key, value] of form.entries()) {
|
|
1120
|
+
const existing = parsed[key];
|
|
1121
|
+
if (existing !== void 0) if (Array.isArray(existing)) existing.push(value);
|
|
1122
|
+
else parsed[key] = [existing, value];
|
|
1123
|
+
else parsed[key] = value;
|
|
1124
|
+
}
|
|
1125
|
+
return parsed;
|
|
1126
|
+
}
|
|
1127
|
+
//#endregion
|
|
1128
|
+
//#region src/request/helpers/cache.ts
|
|
1129
|
+
function isRequestCacheable(event, modifiedTime) {
|
|
1130
|
+
const modifiedSince = event.headers.get(HeaderName.IF_MODIFIED_SINCE);
|
|
1131
|
+
if (!modifiedSince) return false;
|
|
1132
|
+
modifiedTime = typeof modifiedTime === "string" ? new Date(modifiedTime) : modifiedTime;
|
|
1133
|
+
const sinceDate = new Date(modifiedSince);
|
|
1134
|
+
if (Number.isNaN(sinceDate.getTime()) || Number.isNaN(modifiedTime.getTime())) return false;
|
|
1135
|
+
return sinceDate >= modifiedTime;
|
|
1136
|
+
}
|
|
1137
|
+
//#endregion
|
|
1138
|
+
//#region src/request/helpers/header-accept-charset.ts
|
|
1139
|
+
function getRequestAcceptableCharsets(event) {
|
|
1140
|
+
return useRequestNegotiator(event).charsets();
|
|
1141
|
+
}
|
|
1142
|
+
function getRequestAcceptableCharset(event, input) {
|
|
1143
|
+
input = input || [];
|
|
1144
|
+
const items = Array.isArray(input) ? input : [input];
|
|
1145
|
+
if (items.length === 0) return getRequestAcceptableCharsets(event).shift();
|
|
1146
|
+
return useRequestNegotiator(event).charsets(items).shift() || void 0;
|
|
1147
|
+
}
|
|
1148
|
+
//#endregion
|
|
1149
|
+
//#region src/request/helpers/header-accept-encoding.ts
|
|
1150
|
+
function getRequestAcceptableEncodings(event) {
|
|
1151
|
+
return useRequestNegotiator(event).encodings();
|
|
1152
|
+
}
|
|
1153
|
+
function getRequestAcceptableEncoding(event, input) {
|
|
1154
|
+
input = input || [];
|
|
1155
|
+
const items = Array.isArray(input) ? input : [input];
|
|
1156
|
+
if (items.length === 0) return getRequestAcceptableEncodings(event).shift();
|
|
1157
|
+
return useRequestNegotiator(event).encodings(items).shift() || void 0;
|
|
1158
|
+
}
|
|
1159
|
+
//#endregion
|
|
1160
|
+
//#region src/request/helpers/header-accept-language.ts
|
|
1161
|
+
function getRequestAcceptableLanguages(event) {
|
|
1162
|
+
return useRequestNegotiator(event).languages();
|
|
1163
|
+
}
|
|
1164
|
+
function getRequestAcceptableLanguage(event, input) {
|
|
1165
|
+
input = input || [];
|
|
1166
|
+
const items = Array.isArray(input) ? input : [input];
|
|
1167
|
+
if (items.length === 0) return getRequestAcceptableLanguages(event).shift();
|
|
1168
|
+
return useRequestNegotiator(event).languages(items).shift() || void 0;
|
|
1169
|
+
}
|
|
1170
|
+
//#endregion
|
|
1171
|
+
//#region src/request/helpers/header-content-type.ts
|
|
1172
|
+
function matchRequestContentType(event, contentType) {
|
|
1173
|
+
const header = getRequestHeader(event, HeaderName.CONTENT_TYPE);
|
|
1174
|
+
if (!header) return true;
|
|
1175
|
+
return header.split(";")[0].trim() === getMimeType(contentType);
|
|
1176
|
+
}
|
|
1177
|
+
//#endregion
|
|
1178
|
+
//#region src/request/helpers/hostname.ts
|
|
1179
|
+
function getRequestHostName(event, options = {}) {
|
|
1180
|
+
let trustProxy;
|
|
1181
|
+
if (typeof options.trustProxy !== "undefined") trustProxy = buildTrustProxyFn(options.trustProxy);
|
|
1182
|
+
else trustProxy = findRouterOption("trustProxy", event.routerPath);
|
|
1183
|
+
let hostname = event.headers.get(HeaderName.X_FORWARDED_HOST);
|
|
1184
|
+
if (!hostname || !event.request.ip || !trustProxy(event.request.ip, 0)) hostname = event.headers.get(HeaderName.HOST);
|
|
1185
|
+
else if (hostname && hostname.includes(",")) hostname = hostname.substring(0, hostname.indexOf(",")).trimEnd();
|
|
1186
|
+
if (!hostname) return;
|
|
1187
|
+
const offset = hostname[0] === "[" ? hostname.indexOf("]") + 1 : 0;
|
|
1188
|
+
const index = hostname.indexOf(":", offset);
|
|
1189
|
+
const result = index !== -1 ? hostname.substring(0, index) : hostname;
|
|
1190
|
+
if (/[\x00-\x1F\x7F\s/@\\]/.test(result)) return;
|
|
1191
|
+
return result;
|
|
1192
|
+
}
|
|
1193
|
+
//#endregion
|
|
1194
|
+
//#region src/request/helpers/ip.ts
|
|
1195
|
+
function getRequestIP(event, options = {}) {
|
|
1196
|
+
if (options.trustProxy) {
|
|
1197
|
+
const forwarded = event.headers.get(HeaderName.X_FORWARDED_FOR);
|
|
1198
|
+
if (forwarded) {
|
|
1199
|
+
const first = forwarded.split(",")[0];
|
|
1200
|
+
if (first) return first.trim();
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
const request = event.request;
|
|
1204
|
+
if (request.ip) return request.ip;
|
|
1205
|
+
}
|
|
1206
|
+
//#endregion
|
|
1207
|
+
//#region src/request/helpers/protocol.ts
|
|
1208
|
+
function getRequestProtocol(event, options = {}) {
|
|
1209
|
+
let trustProxy;
|
|
1210
|
+
if (typeof options.trustProxy !== "undefined") trustProxy = buildTrustProxyFn(options.trustProxy);
|
|
1211
|
+
else trustProxy = findRouterOption("trustProxy", event.routerPath);
|
|
1212
|
+
let protocol;
|
|
1213
|
+
try {
|
|
1214
|
+
if (new URL(event.request.url).protocol === "https:") protocol = "https";
|
|
1215
|
+
else protocol = "http";
|
|
1216
|
+
} catch {
|
|
1217
|
+
protocol = options.default || "http";
|
|
1218
|
+
}
|
|
1219
|
+
if (!event.request.ip || !trustProxy(event.request.ip, 0)) return protocol;
|
|
1220
|
+
const header = event.headers.get(HeaderName.X_FORWARDED_PROTO);
|
|
1221
|
+
if (!header) return protocol;
|
|
1222
|
+
const index = header.indexOf(",");
|
|
1223
|
+
const forwarded = index !== -1 ? header.substring(0, index).trim().toLowerCase() : header.trim().toLowerCase();
|
|
1224
|
+
if (forwarded === "http" || forwarded === "https") return forwarded;
|
|
1225
|
+
return protocol;
|
|
1226
|
+
}
|
|
1227
|
+
//#endregion
|
|
1228
|
+
//#region src/plugin/is.ts
|
|
1229
|
+
function isPlugin(input) {
|
|
1230
|
+
if (!isObject(input)) return false;
|
|
1231
|
+
if (typeof input.name !== "undefined" && typeof input.name !== "string") return false;
|
|
1232
|
+
return typeof input.install === "function" && input.install.length === 1;
|
|
1233
|
+
}
|
|
1234
|
+
//#endregion
|
|
1235
|
+
//#region src/router-options/normalize.ts
|
|
1236
|
+
function normalizeRouterOptions(input) {
|
|
1237
|
+
if (typeof input.etag !== "undefined") input.etag = buildEtagFn(input.etag);
|
|
1238
|
+
if (typeof input.trustProxy !== "undefined") input.trustProxy = buildTrustProxyFn(input.trustProxy);
|
|
1239
|
+
return input;
|
|
1240
|
+
}
|
|
1241
|
+
//#endregion
|
|
1242
|
+
//#region src/router/constants.ts
|
|
1243
|
+
const RouterSymbol = /* @__PURE__ */ Symbol.for("Router");
|
|
1244
|
+
let RouterPipelineStep = /* @__PURE__ */ function(RouterPipelineStep) {
|
|
1245
|
+
RouterPipelineStep[RouterPipelineStep["START"] = 0] = "START";
|
|
1246
|
+
RouterPipelineStep[RouterPipelineStep["LOOKUP"] = 1] = "LOOKUP";
|
|
1247
|
+
RouterPipelineStep[RouterPipelineStep["CHILD_BEFORE"] = 2] = "CHILD_BEFORE";
|
|
1248
|
+
RouterPipelineStep[RouterPipelineStep["CHILD_DISPATCH"] = 3] = "CHILD_DISPATCH";
|
|
1249
|
+
RouterPipelineStep[RouterPipelineStep["CHILD_AFTER"] = 4] = "CHILD_AFTER";
|
|
1250
|
+
RouterPipelineStep[RouterPipelineStep["FINISH"] = 5] = "FINISH";
|
|
1251
|
+
return RouterPipelineStep;
|
|
1252
|
+
}({});
|
|
1253
|
+
//#endregion
|
|
1254
|
+
//#region src/router/utils.ts
|
|
1255
|
+
let nextId = 0;
|
|
1256
|
+
function generateRouterID() {
|
|
1257
|
+
return ++nextId;
|
|
1258
|
+
}
|
|
1259
|
+
function isRouterInstance(input) {
|
|
1260
|
+
return isInstance(input, RouterSymbol);
|
|
1261
|
+
}
|
|
1262
|
+
/**
|
|
1263
|
+
* Check if the request accepts JSON responses.
|
|
1264
|
+
* Matches application/json and +json suffixes (e.g. application/vnd.api+json).
|
|
1265
|
+
* Returns true if no Accept header is present (API-first default).
|
|
1266
|
+
*/
|
|
1267
|
+
function acceptsJson(request) {
|
|
1268
|
+
const accept = request.headers.get("accept");
|
|
1269
|
+
if (!accept) return true;
|
|
1270
|
+
return accept.includes("application/json") || accept.includes("+json") || accept.includes("*/*");
|
|
1271
|
+
}
|
|
1272
|
+
//#endregion
|
|
1273
|
+
//#region src/router/module.ts
|
|
1274
|
+
var Router = class Router {
|
|
1275
|
+
"@instanceof" = RouterSymbol;
|
|
1276
|
+
/**
|
|
1277
|
+
* An identifier for the router instance.
|
|
1278
|
+
*/
|
|
1279
|
+
id;
|
|
1280
|
+
/**
|
|
1281
|
+
* A label for the router instance.
|
|
1282
|
+
*/
|
|
1283
|
+
name;
|
|
1284
|
+
/**
|
|
1285
|
+
* Array of mounted layers, routes & routers.
|
|
1286
|
+
*
|
|
1287
|
+
* @protected
|
|
1288
|
+
*/
|
|
1289
|
+
stack = [];
|
|
1290
|
+
/**
|
|
1291
|
+
* Path matcher for the current mount path.
|
|
1292
|
+
*
|
|
1293
|
+
* @protected
|
|
1294
|
+
*/
|
|
1295
|
+
pathMatcher;
|
|
1296
|
+
/**
|
|
1297
|
+
* A hook manager.
|
|
1298
|
+
*
|
|
1299
|
+
* @protected
|
|
1300
|
+
*/
|
|
1301
|
+
hookManager;
|
|
1302
|
+
constructor(options = {}) {
|
|
1303
|
+
this.id = generateRouterID();
|
|
1304
|
+
this.name = options.name;
|
|
1305
|
+
this.hookManager = new HookManager();
|
|
1306
|
+
this.setPath(options.path);
|
|
1307
|
+
setRouterOptions(this.id, normalizeRouterOptions(options));
|
|
1308
|
+
}
|
|
1309
|
+
matchPath(path) {
|
|
1310
|
+
if (this.pathMatcher) return this.pathMatcher.test(path);
|
|
1311
|
+
return true;
|
|
1312
|
+
}
|
|
1313
|
+
setPath(value) {
|
|
1314
|
+
if (value === "/" || typeof value === "undefined") {
|
|
1315
|
+
this.pathMatcher = void 0;
|
|
1316
|
+
return;
|
|
1317
|
+
}
|
|
1318
|
+
this.pathMatcher = new PathMatcher(withLeadingSlash(withoutTrailingSlash(`${value}`)), { end: false });
|
|
1319
|
+
}
|
|
1320
|
+
/**
|
|
1321
|
+
* Public entry point — creates a RoutupEvent from the request,
|
|
1322
|
+
* runs the pipeline, and returns a Response (with 404/500 fallbacks).
|
|
1323
|
+
*/
|
|
1324
|
+
async fetch(request) {
|
|
1325
|
+
const event = new RoutupEvent(request);
|
|
1326
|
+
let response;
|
|
1327
|
+
try {
|
|
1328
|
+
response = await this.dispatch(event);
|
|
1329
|
+
} catch (e) {
|
|
1330
|
+
event.error = createError(e);
|
|
1331
|
+
}
|
|
1332
|
+
if (response) return response;
|
|
1333
|
+
if (event.error) {
|
|
1334
|
+
const status = event.error.statusCode || 500;
|
|
1335
|
+
const message = event.error.statusMessage || "Internal Server Error";
|
|
1336
|
+
return this.buildFallbackResponse(request, event, status, message);
|
|
1337
|
+
}
|
|
1338
|
+
return this.buildFallbackResponse(request, event, 404, "Not Found");
|
|
1339
|
+
}
|
|
1340
|
+
buildFallbackResponse(request, event, status, message) {
|
|
1341
|
+
const headers = new Headers(event.response.headers);
|
|
1342
|
+
if (acceptsJson(request)) {
|
|
1343
|
+
headers.set("content-type", "application/json; charset=utf-8");
|
|
1344
|
+
return new Response(JSON.stringify({
|
|
1345
|
+
status,
|
|
1346
|
+
message
|
|
1347
|
+
}), {
|
|
1348
|
+
status,
|
|
1349
|
+
statusText: message,
|
|
1350
|
+
headers
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
headers.set("content-type", "text/plain; charset=utf-8");
|
|
1354
|
+
return new Response(message, {
|
|
1355
|
+
status,
|
|
1356
|
+
statusText: message,
|
|
1357
|
+
headers
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
async executePipelineStep(context) {
|
|
1361
|
+
switch (context.step) {
|
|
1362
|
+
case RouterPipelineStep.START: return this.executePipelineStepStart(context);
|
|
1363
|
+
case RouterPipelineStep.LOOKUP: return this.executePipelineStepLookup(context);
|
|
1364
|
+
case RouterPipelineStep.CHILD_BEFORE: return this.executePipelineStepChildBefore(context);
|
|
1365
|
+
case RouterPipelineStep.CHILD_DISPATCH: return this.executePipelineStepChildDispatch(context);
|
|
1366
|
+
case RouterPipelineStep.CHILD_AFTER: return this.executePipelineStepChildAfter(context);
|
|
1367
|
+
case RouterPipelineStep.FINISH:
|
|
1368
|
+
default: return this.executePipelineStepFinish(context);
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
async executePipelineStepStart(context) {
|
|
1372
|
+
await this.hookManager.trigger(HookName.REQUEST, context.event);
|
|
1373
|
+
if (context.event.dispatched) context.step = RouterPipelineStep.FINISH;
|
|
1374
|
+
else context.step = RouterPipelineStep.LOOKUP;
|
|
1375
|
+
return this.executePipelineStep(context);
|
|
1376
|
+
}
|
|
1377
|
+
async executePipelineStepLookup(context) {
|
|
1378
|
+
while (!context.event.dispatched && context.stackIndex < this.stack.length) {
|
|
1379
|
+
const item = this.stack[context.stackIndex];
|
|
1380
|
+
if (isHandler(item)) {
|
|
1381
|
+
if (context.event.error && item.type === HandlerType.CORE || !context.event.error && item.type === HandlerType.ERROR) {
|
|
1382
|
+
context.stackIndex++;
|
|
1383
|
+
continue;
|
|
1384
|
+
}
|
|
1385
|
+
if (item.matchPath(context.event.path)) {
|
|
1386
|
+
if (item.method) context.event.methodsAllowed.push(item.method);
|
|
1387
|
+
if (item.matchMethod(context.event.method)) {
|
|
1388
|
+
await this.hookManager.trigger(HookName.CHILD_MATCH, context.event);
|
|
1389
|
+
if (context.event.dispatched) context.step = RouterPipelineStep.FINISH;
|
|
1390
|
+
else context.step = RouterPipelineStep.CHILD_BEFORE;
|
|
1391
|
+
return this.executePipelineStep(context);
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
context.stackIndex++;
|
|
1395
|
+
continue;
|
|
1396
|
+
}
|
|
1397
|
+
if (item.matchPath(context.event.path)) {
|
|
1398
|
+
await this.hookManager.trigger(HookName.CHILD_MATCH, context.event);
|
|
1399
|
+
if (context.event.dispatched) context.step = RouterPipelineStep.FINISH;
|
|
1400
|
+
else context.step = RouterPipelineStep.CHILD_BEFORE;
|
|
1401
|
+
return this.executePipelineStep(context);
|
|
1402
|
+
}
|
|
1403
|
+
context.stackIndex++;
|
|
1404
|
+
}
|
|
1405
|
+
context.step = RouterPipelineStep.FINISH;
|
|
1406
|
+
return this.executePipelineStep(context);
|
|
1407
|
+
}
|
|
1408
|
+
async executePipelineStepChildBefore(context) {
|
|
1409
|
+
await this.hookManager.trigger(HookName.CHILD_DISPATCH_BEFORE, context.event);
|
|
1410
|
+
if (context.event.dispatched) context.step = RouterPipelineStep.FINISH;
|
|
1411
|
+
else context.step = RouterPipelineStep.CHILD_DISPATCH;
|
|
1412
|
+
return this.executePipelineStep(context);
|
|
1413
|
+
}
|
|
1414
|
+
async executePipelineStepChildAfter(context) {
|
|
1415
|
+
await this.hookManager.trigger(HookName.CHILD_DISPATCH_AFTER, context.event);
|
|
1416
|
+
if (context.event.dispatched) context.step = RouterPipelineStep.FINISH;
|
|
1417
|
+
else context.step = RouterPipelineStep.LOOKUP;
|
|
1418
|
+
return this.executePipelineStep(context);
|
|
1419
|
+
}
|
|
1420
|
+
async executePipelineStepChildDispatch(context) {
|
|
1421
|
+
if (context.event.dispatched || typeof this.stack[context.stackIndex] === "undefined") {
|
|
1422
|
+
context.step = RouterPipelineStep.FINISH;
|
|
1423
|
+
return this.executePipelineStep(context);
|
|
1424
|
+
}
|
|
1425
|
+
const item = this.stack[context.stackIndex];
|
|
1426
|
+
const { event } = context;
|
|
1427
|
+
const savedNext = event._next;
|
|
1428
|
+
const savedNextCalled = event._nextCalled;
|
|
1429
|
+
try {
|
|
1430
|
+
event._nextCalled = false;
|
|
1431
|
+
event._next = async () => {
|
|
1432
|
+
const nextContext = {
|
|
1433
|
+
step: RouterPipelineStep.LOOKUP,
|
|
1434
|
+
event,
|
|
1435
|
+
stackIndex: context.stackIndex + 1,
|
|
1436
|
+
response: void 0
|
|
1437
|
+
};
|
|
1438
|
+
event.routerPath.push(this.id);
|
|
1439
|
+
try {
|
|
1440
|
+
await this.executePipelineStep(nextContext);
|
|
1441
|
+
} finally {
|
|
1442
|
+
event.routerPath.pop();
|
|
1443
|
+
}
|
|
1444
|
+
return nextContext.response;
|
|
1445
|
+
};
|
|
1446
|
+
const response = await item.dispatch(event);
|
|
1447
|
+
if (response) {
|
|
1448
|
+
context.response = response;
|
|
1449
|
+
event.dispatched = true;
|
|
1450
|
+
}
|
|
1451
|
+
} catch (e) {
|
|
1452
|
+
event.error = createError(e);
|
|
1453
|
+
await this.hookManager.trigger(HookName.ERROR, event);
|
|
1454
|
+
} finally {
|
|
1455
|
+
event._next = savedNext;
|
|
1456
|
+
event._nextCalled = savedNextCalled;
|
|
1457
|
+
}
|
|
1458
|
+
context.stackIndex++;
|
|
1459
|
+
context.step = RouterPipelineStep.CHILD_AFTER;
|
|
1460
|
+
return this.executePipelineStep(context);
|
|
1461
|
+
}
|
|
1462
|
+
async executePipelineStepFinish(context) {
|
|
1463
|
+
if (context.event.error || context.event.dispatched) return this.hookManager.trigger(HookName.RESPONSE, context.event);
|
|
1464
|
+
if (!context.event.dispatched && context.event.routerPath.length === 1 && context.event.method && context.event.method === MethodName.OPTIONS) {
|
|
1465
|
+
if (context.event.methodsAllowed.includes(MethodName.GET)) context.event.methodsAllowed.push(MethodName.HEAD);
|
|
1466
|
+
distinctArray(context.event.methodsAllowed);
|
|
1467
|
+
const options = context.event.methodsAllowed.map((key) => key.toUpperCase()).join(",");
|
|
1468
|
+
const optionsHeaders = new Headers(context.event.response.headers);
|
|
1469
|
+
optionsHeaders.set(HeaderName.ALLOW, options);
|
|
1470
|
+
context.response = new Response(options, {
|
|
1471
|
+
status: context.event.response.status || 200,
|
|
1472
|
+
statusText: context.event.response.statusText,
|
|
1473
|
+
headers: optionsHeaders
|
|
1474
|
+
});
|
|
1475
|
+
context.event.dispatched = true;
|
|
1476
|
+
}
|
|
1477
|
+
return this.hookManager.trigger(HookName.RESPONSE, context.event);
|
|
1478
|
+
}
|
|
1479
|
+
async dispatch(event) {
|
|
1480
|
+
if (this.pathMatcher) {
|
|
1481
|
+
const output = this.pathMatcher.exec(event.path);
|
|
1482
|
+
if (typeof output !== "undefined") {
|
|
1483
|
+
event.mountPath = cleanDoubleSlashes(`${event.mountPath}/${output.path}`);
|
|
1484
|
+
if (event.path === output.path) event.path = "/";
|
|
1485
|
+
else event.path = withLeadingSlash(event.path.substring(output.path.length));
|
|
1486
|
+
event.params = {
|
|
1487
|
+
...event.params,
|
|
1488
|
+
...output.params
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
const context = {
|
|
1493
|
+
step: RouterPipelineStep.START,
|
|
1494
|
+
event,
|
|
1495
|
+
stackIndex: 0
|
|
1496
|
+
};
|
|
1497
|
+
event.routerPath.push(this.id);
|
|
1498
|
+
try {
|
|
1499
|
+
await this.executePipelineStep(context);
|
|
1500
|
+
} finally {
|
|
1501
|
+
event.routerPath.pop();
|
|
1502
|
+
}
|
|
1503
|
+
return context.response;
|
|
1504
|
+
}
|
|
1505
|
+
delete(...input) {
|
|
1506
|
+
this.useForMethod(MethodName.DELETE, ...input);
|
|
1507
|
+
return this;
|
|
1508
|
+
}
|
|
1509
|
+
get(...input) {
|
|
1510
|
+
this.useForMethod(MethodName.GET, ...input);
|
|
1511
|
+
return this;
|
|
1512
|
+
}
|
|
1513
|
+
post(...input) {
|
|
1514
|
+
this.useForMethod(MethodName.POST, ...input);
|
|
1515
|
+
return this;
|
|
1516
|
+
}
|
|
1517
|
+
put(...input) {
|
|
1518
|
+
this.useForMethod(MethodName.PUT, ...input);
|
|
1519
|
+
return this;
|
|
1520
|
+
}
|
|
1521
|
+
patch(...input) {
|
|
1522
|
+
this.useForMethod(MethodName.PATCH, ...input);
|
|
1523
|
+
return this;
|
|
1524
|
+
}
|
|
1525
|
+
head(...input) {
|
|
1526
|
+
this.useForMethod(MethodName.HEAD, ...input);
|
|
1527
|
+
return this;
|
|
1528
|
+
}
|
|
1529
|
+
options(...input) {
|
|
1530
|
+
this.useForMethod(MethodName.OPTIONS, ...input);
|
|
1531
|
+
return this;
|
|
1532
|
+
}
|
|
1533
|
+
useForMethod(method, ...input) {
|
|
1534
|
+
let path;
|
|
1535
|
+
for (const element of input) {
|
|
1536
|
+
if (isPath(element)) {
|
|
1537
|
+
path = element;
|
|
1538
|
+
continue;
|
|
1539
|
+
}
|
|
1540
|
+
if (isHandlerOptions(element)) {
|
|
1541
|
+
if (path) element.path = path;
|
|
1542
|
+
element.method = method;
|
|
1543
|
+
this.use(element);
|
|
1544
|
+
continue;
|
|
1545
|
+
}
|
|
1546
|
+
if (isHandler(element)) {
|
|
1547
|
+
if (path) element.setPath(path);
|
|
1548
|
+
element.setMethod(method);
|
|
1549
|
+
this.use(element);
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
use(...input) {
|
|
1554
|
+
let path;
|
|
1555
|
+
for (const item of input) {
|
|
1556
|
+
if (isPath(item)) {
|
|
1557
|
+
path = withLeadingSlash(item);
|
|
1558
|
+
continue;
|
|
1559
|
+
}
|
|
1560
|
+
if (isRouterInstance(item)) {
|
|
1561
|
+
if (path) item.setPath(path);
|
|
1562
|
+
this.stack.push(item);
|
|
1563
|
+
continue;
|
|
1564
|
+
}
|
|
1565
|
+
if (isHandlerOptions(item)) {
|
|
1566
|
+
item.path = path || item.path;
|
|
1567
|
+
this.stack.push(new Handler(item));
|
|
1568
|
+
continue;
|
|
1569
|
+
}
|
|
1570
|
+
if (isHandler(item)) {
|
|
1571
|
+
item.setPath(path || item.path);
|
|
1572
|
+
this.stack.push(item);
|
|
1573
|
+
continue;
|
|
1574
|
+
}
|
|
1575
|
+
if (isPlugin(item)) if (path) this.install(item, { path });
|
|
1576
|
+
else this.install(item);
|
|
1577
|
+
}
|
|
1578
|
+
return this;
|
|
1579
|
+
}
|
|
1580
|
+
install(plugin, context = {}) {
|
|
1581
|
+
const router = new Router({ name: context.name || plugin.name });
|
|
1582
|
+
plugin.install(router);
|
|
1583
|
+
if (context.path) this.use(context.path, router);
|
|
1584
|
+
else this.use(router);
|
|
1585
|
+
return this;
|
|
1586
|
+
}
|
|
1587
|
+
on(name, fn) {
|
|
1588
|
+
return this.hookManager.addListener(name, fn);
|
|
1589
|
+
}
|
|
1590
|
+
off(name, fn) {
|
|
1591
|
+
if (typeof fn === "undefined") {
|
|
1592
|
+
this.hookManager.removeListener(name);
|
|
1593
|
+
return this;
|
|
1594
|
+
}
|
|
1595
|
+
this.hookManager.removeListener(name, fn);
|
|
1596
|
+
return this;
|
|
1597
|
+
}
|
|
1598
|
+
};
|
|
1599
|
+
//#endregion
|
|
1600
|
+
export { isError as $, useRequestNegotiator as A, appendResponseHeaderDirective as B, defineCoreHandler as C, sendFormat as D, sendRedirect as E, toResponse as F, setResponseCacheHeaders as G, setResponseGone as H, setResponseHeaderContentType as I, HandlerSymbol as J, isPath as K, setResponseHeaderAttachment as L, sendFile as M, sendCreated as N, getRequestAcceptableContentType as O, sendAccepted as P, RoutupError as Q, setResponseContentTypeByFileName as R, defineErrorHandler as S, sendStream as T, createEventStream as U, isResponseGone as V, serializeEventStreamMessage as W, RoutupEvent as X, HandlerType as Y, createError as Z, fromWebHandler as _, getRequestHostName as a, fromNodeHandler as b, getRequestAcceptableLanguages as c, getRequestAcceptableCharset as d, HeaderName as et, getRequestAcceptableCharsets as f, isHandlerOptions as g, isHandler as h, getRequestIP as i, getRequestHeader as j, getRequestAcceptableContentTypes as k, getRequestAcceptableEncoding as l, readBody as m, isPlugin as n, matchRequestContentType as o, isRequestCacheable as p, PathMatcher as q, getRequestProtocol as r, getRequestAcceptableLanguage as s, Router as t, MethodName as tt, getRequestAcceptableEncodings as u, isWebHandler as v, Handler as w, fromNodeMiddleware as x, isWebHandlerProvider as y, appendResponseHeader as z };
|
|
1601
|
+
|
|
1602
|
+
//# sourceMappingURL=src-CpLmo5Po.mjs.map
|