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.
Files changed (169) hide show
  1. package/README.md +89 -136
  2. package/dist/bun.cjs +69 -0
  3. package/dist/bun.cjs.map +1 -0
  4. package/dist/bun.d.cts +8 -0
  5. package/dist/bun.d.mts +8 -0
  6. package/dist/bun.mjs +13 -0
  7. package/dist/bun.mjs.map +1 -0
  8. package/dist/cloudflare.cjs +69 -0
  9. package/dist/cloudflare.cjs.map +1 -0
  10. package/dist/cloudflare.d.cts +8 -0
  11. package/dist/cloudflare.d.mts +8 -0
  12. package/dist/cloudflare.mjs +13 -0
  13. package/dist/cloudflare.mjs.map +1 -0
  14. package/dist/deno.cjs +69 -0
  15. package/dist/deno.cjs.map +1 -0
  16. package/dist/deno.d.cts +8 -0
  17. package/dist/deno.d.mts +8 -0
  18. package/dist/deno.mjs +13 -0
  19. package/dist/deno.mjs.map +1 -0
  20. package/dist/generic.cjs +69 -0
  21. package/dist/generic.cjs.map +1 -0
  22. package/dist/generic.d.cts +8 -0
  23. package/dist/generic.d.mts +8 -0
  24. package/dist/generic.mjs +13 -0
  25. package/dist/generic.mjs.map +1 -0
  26. package/dist/index-DN3wy86V.d.mts +841 -0
  27. package/dist/index-Dm50O_sj.d.cts +841 -0
  28. package/dist/node.cjs +73 -0
  29. package/dist/node.cjs.map +1 -0
  30. package/dist/node.d.cts +11 -0
  31. package/dist/node.d.mts +11 -0
  32. package/dist/node.mjs +16 -0
  33. package/dist/node.mjs.map +1 -0
  34. package/dist/service-worker.cjs +69 -0
  35. package/dist/service-worker.cjs.map +1 -0
  36. package/dist/service-worker.d.cts +10 -0
  37. package/dist/service-worker.d.mts +10 -0
  38. package/dist/service-worker.mjs +13 -0
  39. package/dist/service-worker.mjs.map +1 -0
  40. package/dist/src-CpLmo5Po.mjs +1602 -0
  41. package/dist/src-CpLmo5Po.mjs.map +1 -0
  42. package/dist/src-D_4IPMmD.cjs +1960 -0
  43. package/dist/src-D_4IPMmD.cjs.map +1 -0
  44. package/package.json +75 -34
  45. package/dist/adapters/index.d.ts +0 -3
  46. package/dist/adapters/node/index.d.ts +0 -1
  47. package/dist/adapters/node/module.d.ts +0 -6
  48. package/dist/adapters/raw/header.d.ts +0 -3
  49. package/dist/adapters/raw/index.d.ts +0 -3
  50. package/dist/adapters/raw/module.d.ts +0 -4
  51. package/dist/adapters/raw/type.d.ts +0 -18
  52. package/dist/adapters/web/index.d.ts +0 -2
  53. package/dist/adapters/web/module.d.ts +0 -4
  54. package/dist/adapters/web/type.d.ts +0 -3
  55. package/dist/constants.d.ts +0 -42
  56. package/dist/dispatcher/event/dispatch.d.ts +0 -4
  57. package/dist/dispatcher/event/error.d.ts +0 -5
  58. package/dist/dispatcher/event/index.d.ts +0 -5
  59. package/dist/dispatcher/event/is.d.ts +0 -3
  60. package/dist/dispatcher/event/module.d.ts +0 -56
  61. package/dist/dispatcher/event/types.d.ts +0 -9
  62. package/dist/dispatcher/index.d.ts +0 -2
  63. package/dist/dispatcher/type.d.ts +0 -4
  64. package/dist/error/create.d.ts +0 -11
  65. package/dist/error/index.d.ts +0 -3
  66. package/dist/error/is.d.ts +0 -2
  67. package/dist/error/module.d.ts +0 -3
  68. package/dist/handler/constants.d.ts +0 -5
  69. package/dist/handler/core/define.d.ts +0 -4
  70. package/dist/handler/core/index.d.ts +0 -2
  71. package/dist/handler/core/types.d.ts +0 -10
  72. package/dist/handler/error/define.d.ts +0 -4
  73. package/dist/handler/error/index.d.ts +0 -2
  74. package/dist/handler/error/types.d.ts +0 -11
  75. package/dist/handler/index.d.ts +0 -7
  76. package/dist/handler/is.d.ts +0 -4
  77. package/dist/handler/module.d.ts +0 -23
  78. package/dist/handler/types-base.d.ts +0 -10
  79. package/dist/handler/types.d.ts +0 -4
  80. package/dist/hook/constants.d.ts +0 -8
  81. package/dist/hook/index.d.ts +0 -3
  82. package/dist/hook/module.d.ts +0 -19
  83. package/dist/hook/types.d.ts +0 -5
  84. package/dist/index.cjs +0 -2314
  85. package/dist/index.cjs.map +0 -1
  86. package/dist/index.d.ts +0 -11
  87. package/dist/index.mjs +0 -2235
  88. package/dist/index.mjs.map +0 -1
  89. package/dist/path/index.d.ts +0 -3
  90. package/dist/path/matcher.d.ts +0 -11
  91. package/dist/path/type.d.ts +0 -7
  92. package/dist/path/utils.d.ts +0 -2
  93. package/dist/plugin/index.d.ts +0 -2
  94. package/dist/plugin/is.d.ts +0 -2
  95. package/dist/plugin/types.d.ts +0 -23
  96. package/dist/request/helpers/cache.d.ts +0 -2
  97. package/dist/request/helpers/env.d.ts +0 -6
  98. package/dist/request/helpers/header-accept-charset.d.ts +0 -3
  99. package/dist/request/helpers/header-accept-encoding.d.ts +0 -3
  100. package/dist/request/helpers/header-accept-language.d.ts +0 -3
  101. package/dist/request/helpers/header-accept.d.ts +0 -3
  102. package/dist/request/helpers/header-content-type.d.ts +0 -2
  103. package/dist/request/helpers/header.d.ts +0 -4
  104. package/dist/request/helpers/hostname.d.ts +0 -7
  105. package/dist/request/helpers/http2.d.ts +0 -2
  106. package/dist/request/helpers/index.d.ts +0 -17
  107. package/dist/request/helpers/ip.d.ts +0 -7
  108. package/dist/request/helpers/mount-path.d.ts +0 -3
  109. package/dist/request/helpers/negotiator.d.ts +0 -3
  110. package/dist/request/helpers/params.d.ts +0 -5
  111. package/dist/request/helpers/path.d.ts +0 -2
  112. package/dist/request/helpers/protocol.d.ts +0 -8
  113. package/dist/request/helpers/router.d.ts +0 -3
  114. package/dist/request/index.d.ts +0 -3
  115. package/dist/request/module.d.ts +0 -3
  116. package/dist/request/types.d.ts +0 -12
  117. package/dist/response/helpers/cache.d.ts +0 -7
  118. package/dist/response/helpers/event-stream/factory.d.ts +0 -3
  119. package/dist/response/helpers/event-stream/index.d.ts +0 -2
  120. package/dist/response/helpers/event-stream/module.d.ts +0 -17
  121. package/dist/response/helpers/event-stream/types.d.ts +0 -24
  122. package/dist/response/helpers/event-stream/utils.d.ts +0 -2
  123. package/dist/response/helpers/gone.d.ts +0 -3
  124. package/dist/response/helpers/header-attachment.d.ts +0 -2
  125. package/dist/response/helpers/header-content-type.d.ts +0 -2
  126. package/dist/response/helpers/header.d.ts +0 -4
  127. package/dist/response/helpers/index.d.ts +0 -16
  128. package/dist/response/helpers/send-accepted.d.ts +0 -2
  129. package/dist/response/helpers/send-created.d.ts +0 -2
  130. package/dist/response/helpers/send-file.d.ts +0 -17
  131. package/dist/response/helpers/send-format.d.ts +0 -7
  132. package/dist/response/helpers/send-redirect.d.ts +0 -2
  133. package/dist/response/helpers/send-stream.d.ts +0 -3
  134. package/dist/response/helpers/send-web-blob.d.ts +0 -3
  135. package/dist/response/helpers/send-web-response.d.ts +0 -3
  136. package/dist/response/helpers/send.d.ts +0 -2
  137. package/dist/response/helpers/utils.d.ts +0 -2
  138. package/dist/response/index.d.ts +0 -3
  139. package/dist/response/module.d.ts +0 -3
  140. package/dist/response/types.d.ts +0 -3
  141. package/dist/router/constants.d.ts +0 -9
  142. package/dist/router/index.d.ts +0 -1
  143. package/dist/router/module.d.ts +0 -89
  144. package/dist/router/types.d.ts +0 -7
  145. package/dist/router/utils.d.ts +0 -3
  146. package/dist/router-options/index.d.ts +0 -2
  147. package/dist/router-options/module.d.ts +0 -4
  148. package/dist/router-options/transform.d.ts +0 -2
  149. package/dist/router-options/type.d.ts +0 -41
  150. package/dist/types.d.ts +0 -9
  151. package/dist/utils/cookie.d.ts +0 -1
  152. package/dist/utils/etag/index.d.ts +0 -3
  153. package/dist/utils/etag/module.d.ts +0 -11
  154. package/dist/utils/etag/type.d.ts +0 -15
  155. package/dist/utils/etag/utils.d.ts +0 -2
  156. package/dist/utils/index.d.ts +0 -13
  157. package/dist/utils/is-instance.d.ts +0 -1
  158. package/dist/utils/method.d.ts +0 -3
  159. package/dist/utils/mime.d.ts +0 -2
  160. package/dist/utils/next.d.ts +0 -2
  161. package/dist/utils/object.d.ts +0 -3
  162. package/dist/utils/path.d.ts +0 -5
  163. package/dist/utils/promise.d.ts +0 -1
  164. package/dist/utils/stream.d.ts +0 -6
  165. package/dist/utils/trust-proxy/index.d.ts +0 -2
  166. package/dist/utils/trust-proxy/module.d.ts +0 -2
  167. package/dist/utils/trust-proxy/type.d.ts +0 -2
  168. package/dist/utils/url.d.ts +0 -7
  169. 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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
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