@barqel/barq 0.0.0-stage → 1.0.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 (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +89 -2
  3. package/dist/app.d.ts +159 -0
  4. package/dist/app.d.ts.map +1 -0
  5. package/dist/context.d.ts +55 -0
  6. package/dist/context.d.ts.map +1 -0
  7. package/dist/cookies.d.ts +5 -0
  8. package/dist/cookies.d.ts.map +1 -0
  9. package/dist/cors.d.ts +11 -0
  10. package/dist/cors.d.ts.map +1 -0
  11. package/dist/errors.d.ts +10 -0
  12. package/dist/errors.d.ts.map +1 -0
  13. package/dist/http.d.ts +7 -0
  14. package/dist/http.d.ts.map +1 -0
  15. package/dist/index.d.ts +17 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +4304 -0
  18. package/dist/index.js.map +24 -0
  19. package/dist/internal/ip.d.ts +6 -0
  20. package/dist/internal/ip.d.ts.map +1 -0
  21. package/dist/internal/node-compat.d.ts +2 -0
  22. package/dist/internal/node-compat.d.ts.map +1 -0
  23. package/dist/internal/pathname.d.ts +9 -0
  24. package/dist/internal/pathname.d.ts.map +1 -0
  25. package/dist/internal/query.d.ts +5 -0
  26. package/dist/internal/query.d.ts.map +1 -0
  27. package/dist/internal/scan.d.ts +33 -0
  28. package/dist/internal/scan.d.ts.map +1 -0
  29. package/dist/internal/shutdown.d.ts +7 -0
  30. package/dist/internal/shutdown.d.ts.map +1 -0
  31. package/dist/internal/trie.d.ts +43 -0
  32. package/dist/internal/trie.d.ts.map +1 -0
  33. package/dist/middleware.d.ts +13 -0
  34. package/dist/middleware.d.ts.map +1 -0
  35. package/dist/node-patch.d.ts +3 -0
  36. package/dist/node-patch.d.ts.map +1 -0
  37. package/dist/node-patch.js +4304 -0
  38. package/dist/node-patch.js.map +24 -0
  39. package/dist/types.d.ts +59 -0
  40. package/dist/types.d.ts.map +1 -0
  41. package/dist/validation.d.ts +27 -0
  42. package/dist/validation.d.ts.map +1 -0
  43. package/package.json +67 -4
package/dist/index.js ADDED
@@ -0,0 +1,4304 @@
1
+ // src/internal/node-compat.ts
2
+ import crypto from "node:crypto";
3
+ import http from "node:http";
4
+ import { Readable } from "node:stream";
5
+
6
+ // src/app.ts
7
+ import { cpus } from "node:os";
8
+
9
+ // src/cookies.ts
10
+ import { timingSafeEqual } from "node:crypto";
11
+
12
+ class CookieJarImpl {
13
+ secret;
14
+ onMutation;
15
+ state;
16
+ constructor(secret, requestHeaders, onMutation) {
17
+ this.secret = secret;
18
+ this.onMutation = onMutation;
19
+ this.state = { cookies: null, requestHeaders, pending: null };
20
+ }
21
+ get(name) {
22
+ return getCookies(this.state).get(name);
23
+ }
24
+ getSigned(name, customSecret) {
25
+ const signed = getCookies(this.state).get(name);
26
+ if (!signed)
27
+ return null;
28
+ const signingSecret = customSecret ?? this.secret;
29
+ if (!signingSecret)
30
+ return null;
31
+ return unsign(signed, signingSecret);
32
+ }
33
+ set(name, value, options) {
34
+ const cookieOptions = snapshotOptions(options);
35
+ validateCookie(name, cookieOptions);
36
+ getCookies(this.state).set(name, value);
37
+ getPending(this.state).push({
38
+ name,
39
+ headerString: buildCookieString(name, value, cookieOptions)
40
+ });
41
+ this.onMutation?.();
42
+ }
43
+ setSigned(name, value, options, customSecret) {
44
+ const signingSecret = customSecret ?? this.secret;
45
+ if (!signingSecret) {
46
+ throw new Error("A non-empty secret is required to set a signed cookie");
47
+ }
48
+ const cookieOptions = snapshotOptions(options);
49
+ validateCookie(name, cookieOptions);
50
+ const signed = sign(value, signingSecret);
51
+ getCookies(this.state).set(name, signed);
52
+ getPending(this.state).push({
53
+ name,
54
+ headerString: buildCookieString(name, signed, cookieOptions)
55
+ });
56
+ this.onMutation?.();
57
+ }
58
+ delete(name, options) {
59
+ const cookieOptions = snapshotOptions(options);
60
+ validateCookie(name, cookieOptions);
61
+ getCookies(this.state).delete(name);
62
+ getPending(this.state).push({
63
+ name,
64
+ headerString: buildCookieString(name, "", {
65
+ ...cookieOptions,
66
+ maxAge: 0,
67
+ expires: new Date(0)
68
+ })
69
+ });
70
+ this.onMutation?.();
71
+ }
72
+ hasPending() {
73
+ return (this.state.pending?.length ?? 0) > 0;
74
+ }
75
+ apply(headers) {
76
+ const pending = this.state.pending;
77
+ if (pending === null)
78
+ return;
79
+ for (const cookie of pending) {
80
+ headers.append("set-cookie", cookie.headerString);
81
+ }
82
+ pending.length = 0;
83
+ }
84
+ }
85
+ function normalizeSameSite(value) {
86
+ if (!value)
87
+ return "Lax";
88
+ const lower = value.toLowerCase();
89
+ if (lower === "strict")
90
+ return "Strict";
91
+ if (lower === "lax")
92
+ return "Lax";
93
+ if (lower === "none")
94
+ return "None";
95
+ return value;
96
+ }
97
+ function buildCookieString(name, value, options) {
98
+ validateCookie(name, options);
99
+ let cookie = `${name}=${encodeURIComponent(value)}`;
100
+ if (options.path)
101
+ cookie += `; Path=${options.path}`;
102
+ if (options.domain)
103
+ cookie += `; Domain=${options.domain}`;
104
+ if (options.maxAge !== undefined)
105
+ cookie += `; Max-Age=${options.maxAge}`;
106
+ if (options.expires)
107
+ cookie += `; Expires=${options.expires.toUTCString()}`;
108
+ if (options.httpOnly !== false)
109
+ cookie += "; HttpOnly";
110
+ if (options.secure)
111
+ cookie += "; Secure";
112
+ const sameSite = normalizeSameSite(options.sameSite);
113
+ if (sameSite)
114
+ cookie += `; SameSite=${sameSite}`;
115
+ return cookie;
116
+ }
117
+ function sign(value, secret) {
118
+ const hasher = new Bun.CryptoHasher("sha256", secret);
119
+ hasher.update(value);
120
+ const sig = hasher.digest("base64url");
121
+ return `${value}.${sig}`;
122
+ }
123
+ function unsign(signedValue, secret) {
124
+ const lastDot = signedValue.lastIndexOf(".");
125
+ if (lastDot <= 0 || lastDot === signedValue.length - 1)
126
+ return null;
127
+ const value = signedValue.slice(0, lastDot);
128
+ const sig = signedValue.slice(lastDot + 1);
129
+ const hasher = new Bun.CryptoHasher("sha256", secret);
130
+ hasher.update(value);
131
+ const expected = hasher.digest("base64url");
132
+ const actualBytes = Buffer.from(sig);
133
+ const expectedBytes = Buffer.from(expected);
134
+ if (actualBytes.length !== expectedBytes.length)
135
+ return null;
136
+ if (!timingSafeEqual(actualBytes, expectedBytes))
137
+ return null;
138
+ return value;
139
+ }
140
+ function decodeCookieValue(value) {
141
+ try {
142
+ return decodeURIComponent(value);
143
+ } catch {
144
+ return value;
145
+ }
146
+ }
147
+ function validateCookie(name, options) {
148
+ if (!/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/.test(name)) {
149
+ throw new TypeError("Cookie name must be a valid HTTP token");
150
+ }
151
+ if (options.path !== undefined && (!options.path.startsWith("/") || /[;\r\n\0]/.test(options.path))) {
152
+ throw new TypeError("Cookie path must start with '/' and contain no controls");
153
+ }
154
+ if (options.domain !== undefined && (!/^(?:\.?[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?)(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?)*$/.test(options.domain) || options.domain.includes(".."))) {
155
+ throw new TypeError("Cookie domain is invalid");
156
+ }
157
+ if (options.maxAge !== undefined && (!Number.isSafeInteger(options.maxAge) || options.maxAge < 0)) {
158
+ throw new TypeError("Cookie maxAge must be a non-negative safe integer");
159
+ }
160
+ if (options.expires !== undefined && !Number.isFinite(options.expires.getTime())) {
161
+ throw new TypeError("Cookie expires must be a valid Date");
162
+ }
163
+ if (options.sameSite !== undefined && !["strict", "lax", "none"].includes(options.sameSite.toLowerCase())) {
164
+ throw new TypeError("Cookie sameSite must be Strict, Lax, or None");
165
+ }
166
+ if (options.sameSite?.toLowerCase() === "none" && !options.secure) {
167
+ throw new TypeError("SameSite=None cookies must also be Secure");
168
+ }
169
+ if (name.startsWith("__Secure-") && !options.secure) {
170
+ throw new TypeError("__Secure- cookies must also be Secure");
171
+ }
172
+ if (name.startsWith("__Host-") && (!options.secure || options.path !== "/" || options.domain !== undefined)) {
173
+ throw new TypeError("__Host- cookies must be Secure, use Path=/, and omit Domain");
174
+ }
175
+ }
176
+ function snapshotOptions(options) {
177
+ if (!options)
178
+ return {};
179
+ return {
180
+ ...options,
181
+ ...options.expires === undefined ? {} : { expires: new Date(options.expires.getTime()) }
182
+ };
183
+ }
184
+ function parseCookieHeader(header) {
185
+ const cookies = new Map;
186
+ let pos = 0;
187
+ const len = header.length;
188
+ while (pos < len) {
189
+ while (pos < len && header.charCodeAt(pos) === 32)
190
+ pos++;
191
+ if (pos >= len)
192
+ break;
193
+ const eq = header.indexOf("=", pos);
194
+ if (eq === -1)
195
+ break;
196
+ const name = header.slice(pos, eq).trim();
197
+ pos = eq + 1;
198
+ let end = header.indexOf(";", pos);
199
+ if (end === -1)
200
+ end = len;
201
+ const value = header.slice(pos, end);
202
+ if (name)
203
+ cookies.set(name, decodeCookieValue(value));
204
+ pos = end + 1;
205
+ }
206
+ return cookies;
207
+ }
208
+ function getCookies(state) {
209
+ if (state.cookies !== null)
210
+ return state.cookies;
211
+ const header = state.requestHeaders?.get("cookie") ?? "";
212
+ state.requestHeaders = undefined;
213
+ state.cookies = header.length === 0 ? new Map : parseCookieHeader(header);
214
+ return state.cookies;
215
+ }
216
+ function getPending(state) {
217
+ if (state.pending === null)
218
+ state.pending = [];
219
+ return state.pending;
220
+ }
221
+ function createCookieJar(secret, requestHeaders, onMutation) {
222
+ return new CookieJarImpl(secret ?? "", requestHeaders, onMutation);
223
+ }
224
+ function applyPendingCookies(cookieJar, headers) {
225
+ if (!(cookieJar instanceof CookieJarImpl)) {
226
+ throw new TypeError("CookieJar was not created by Barq");
227
+ }
228
+ cookieJar.apply(headers);
229
+ }
230
+
231
+ // src/http.ts
232
+ var NULL_BODY_STATUSES = new Set([204, 205, 304]);
233
+ var DEFAULT_JSON_HEADERS = Object.freeze({
234
+ "content-type": "application/json; charset=utf-8"
235
+ });
236
+ var DEFAULT_JSON_INIT = Object.freeze({
237
+ status: 200,
238
+ headers: DEFAULT_JSON_HEADERS
239
+ });
240
+ var DEFAULT_TEXT_HEADERS = Object.freeze({
241
+ "content-type": "text/plain; charset=utf-8"
242
+ });
243
+ function isNullBodyStatus(status) {
244
+ return status >= 100 && status < 200 || NULL_BODY_STATUSES.has(status);
245
+ }
246
+ function createJsonResponse(data, init) {
247
+ if (data === undefined || typeof data === "function" || typeof data === "symbol") {
248
+ return new Response(JSON.stringify(data), init);
249
+ }
250
+ return Response.json(data, init);
251
+ }
252
+ function json(data, init) {
253
+ if (init === undefined) {
254
+ if (typeof data === "object" && data !== null) {
255
+ return new Response(JSON.stringify(data), DEFAULT_JSON_INIT);
256
+ }
257
+ return createJsonResponse(data, DEFAULT_JSON_INIT);
258
+ }
259
+ if (typeof init === "number") {
260
+ if (isNullBodyStatus(init)) {
261
+ return new Response(null, { status: init });
262
+ }
263
+ return createJsonResponse(data, {
264
+ status: init,
265
+ headers: DEFAULT_JSON_HEADERS
266
+ });
267
+ }
268
+ const status = init.status ?? 200;
269
+ if (isNullBodyStatus(status)) {
270
+ return new Response(null, { ...init, status });
271
+ }
272
+ if (init.headers === undefined) {
273
+ return createJsonResponse(data, {
274
+ ...init,
275
+ status,
276
+ headers: DEFAULT_JSON_HEADERS
277
+ });
278
+ }
279
+ const headers = new Headers(init.headers);
280
+ if (!headers.has("content-type")) {
281
+ headers.set("content-type", "application/json; charset=utf-8");
282
+ }
283
+ return createJsonResponse(data, { ...init, status, headers });
284
+ }
285
+ function text(body, init) {
286
+ if (init === undefined) {
287
+ return new Response(body);
288
+ }
289
+ if (typeof init === "number") {
290
+ if (isNullBodyStatus(init)) {
291
+ return new Response(null, { status: init });
292
+ }
293
+ return new Response(body, {
294
+ status: init,
295
+ headers: DEFAULT_TEXT_HEADERS
296
+ });
297
+ }
298
+ const status = init.status ?? 200;
299
+ if (isNullBodyStatus(status)) {
300
+ return new Response(null, { ...init, status });
301
+ }
302
+ if (init.headers === undefined) {
303
+ return new Response(body, {
304
+ ...init,
305
+ status,
306
+ headers: DEFAULT_TEXT_HEADERS
307
+ });
308
+ }
309
+ const headers = new Headers(init.headers);
310
+ if (!headers.has("content-type")) {
311
+ headers.set("content-type", "text/plain; charset=utf-8");
312
+ }
313
+ return new Response(body, { ...init, status, headers });
314
+ }
315
+ function empty(status = 204, headers) {
316
+ const init = headers !== undefined ? { status, headers } : { status };
317
+ return new Response(null, init);
318
+ }
319
+ var CONTROL_CHARS = /[\u0000-\u001f\u007f]/;
320
+ var ALLOWED_REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
321
+ function redirect(url, status = 302, allowlist) {
322
+ if (!ALLOWED_REDIRECT_STATUSES.has(status)) {
323
+ throw new TypeError(`redirect status must be one of: ${Array.from(ALLOWED_REDIRECT_STATUSES).join(", ")}`);
324
+ }
325
+ if (CONTROL_CHARS.test(url) || url.includes("\\")) {
326
+ throw new TypeError("redirect target contains control or backslash characters");
327
+ }
328
+ if (url.charCodeAt(0) === 47) {
329
+ if (url.charCodeAt(1) === 47) {
330
+ throw new TypeError("redirect target must not be protocol-relative");
331
+ }
332
+ } else {
333
+ let parsed;
334
+ try {
335
+ parsed = new URL(url);
336
+ } catch {
337
+ throw new TypeError("redirect target must be a root-relative path or an absolute URL");
338
+ }
339
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
340
+ throw new TypeError(`redirect target protocol is not allowed: ${parsed.protocol}`);
341
+ }
342
+ const host = parsed.host.toLowerCase();
343
+ if (allowlist !== undefined && !allowlist.has(host)) {
344
+ throw new TypeError(`redirect target host is not allowlisted: ${host}`);
345
+ }
346
+ }
347
+ const res = new Response(null, { status });
348
+ res.headers.set("location", url);
349
+ return res;
350
+ }
351
+
352
+ // src/internal/ip.ts
353
+ import { isIP } from "node:net";
354
+ function ipv4ToNumber(bytes) {
355
+ return (bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3]) >>> 0;
356
+ }
357
+ function compileTrustProxy(trust) {
358
+ if (!trust)
359
+ return () => false;
360
+ if (trust === true)
361
+ return () => true;
362
+ if (typeof trust === "function")
363
+ return trust;
364
+ const ranges = typeof trust === "string" ? [trust] : trust;
365
+ if (ranges.length === 0)
366
+ return () => false;
367
+ const matchers = [];
368
+ for (const range of ranges) {
369
+ if (range === "loopback") {
370
+ matchers.push((ip) => {
371
+ const addr = parseIpAddress(ip);
372
+ if (addr === null)
373
+ return false;
374
+ if (addr.length === 4)
375
+ return addr[0] === 127;
376
+ return addr.slice(0, 15).every((b) => b === 0) && addr[15] === 1;
377
+ });
378
+ continue;
379
+ }
380
+ const slash = range.indexOf("/");
381
+ const netText = slash === -1 ? range : range.slice(0, slash);
382
+ const netBytes = parseIpAddress(netText);
383
+ if (!netBytes)
384
+ continue;
385
+ const bits = slash === -1 ? netBytes.length * 8 : Number(range.slice(slash + 1));
386
+ if (netBytes.length === 4) {
387
+ const netNum = ipv4ToNumber(netBytes);
388
+ const mask = bits === 0 ? 0 : ~0 << 32 - bits >>> 0;
389
+ const expected = (netNum & mask) >>> 0;
390
+ matchers.push((ip) => {
391
+ const addr = parseIpAddress(ip);
392
+ if (addr?.length !== 4)
393
+ return false;
394
+ return (ipv4ToNumber(addr) & mask) >>> 0 === expected;
395
+ });
396
+ } else {
397
+ matchers.push((ip) => ipMatchesRange(ip, range));
398
+ }
399
+ }
400
+ return (ip) => matchers.some((m) => m(ip));
401
+ }
402
+ function isIpRange(value) {
403
+ if (value === "loopback")
404
+ return true;
405
+ const slash = value.indexOf("/");
406
+ if (slash !== value.lastIndexOf("/"))
407
+ return false;
408
+ const address = slash === -1 ? value : value.slice(0, slash);
409
+ const bytes = parseIpAddress(address);
410
+ if (bytes === null)
411
+ return false;
412
+ if (slash === -1)
413
+ return true;
414
+ const bitsText = value.slice(slash + 1);
415
+ if (!/^\d+$/.test(bitsText))
416
+ return false;
417
+ const bits = Number(bitsText);
418
+ return Number.isInteger(bits) && bits >= 0 && bits <= bytes.length * 8;
419
+ }
420
+ function ipMatchesRange(ip, range) {
421
+ const address = parseIpAddress(ip);
422
+ if (address === null)
423
+ return false;
424
+ if (range === "loopback") {
425
+ if (address.length === 4)
426
+ return address[0] === 127;
427
+ return address.slice(0, 15).every((byte) => byte === 0) && address[15] === 1;
428
+ }
429
+ const slash = range.indexOf("/");
430
+ const networkText = slash === -1 ? range : range.slice(0, slash);
431
+ const network = parseIpAddress(networkText);
432
+ if (network === null || address.length !== network.length)
433
+ return false;
434
+ const bits = slash === -1 ? network.length * 8 : Number(range.slice(slash + 1));
435
+ if (!Number.isInteger(bits) || bits < 0 || bits > network.length * 8) {
436
+ return false;
437
+ }
438
+ const fullBytes = Math.floor(bits / 8);
439
+ for (let i = 0;i < fullBytes; i++) {
440
+ if (address[i] !== network[i])
441
+ return false;
442
+ }
443
+ const remainingBits = bits % 8;
444
+ if (remainingBits === 0)
445
+ return true;
446
+ const mask = 255 << 8 - remainingBits & 255;
447
+ return (address[fullBytes] & mask) === (network[fullBytes] & mask);
448
+ }
449
+ function isValidIpAddress(value) {
450
+ return parseIpAddress(value) !== null;
451
+ }
452
+ function parseIpAddress(value) {
453
+ const version = isIP(value);
454
+ if (version === 4) {
455
+ const bytes = value.split(".").map(Number);
456
+ if (bytes.length !== 4)
457
+ return null;
458
+ return Uint8Array.from(bytes);
459
+ }
460
+ if (version !== 6)
461
+ return null;
462
+ let address = value.toLowerCase();
463
+ if (address.includes(".")) {
464
+ const separator = address.lastIndexOf(":");
465
+ const ipv4 = address.slice(separator + 1).split(".").map(Number);
466
+ if (separator === -1 || ipv4.length !== 4)
467
+ return null;
468
+ const high = (ipv4[0] << 8 | ipv4[1]).toString(16);
469
+ const low = (ipv4[2] << 8 | ipv4[3]).toString(16);
470
+ address = `${address.slice(0, separator + 1)}${high}:${low}`;
471
+ }
472
+ const halves = address.split("::");
473
+ if (halves.length > 2)
474
+ return null;
475
+ const left = halves[0] ? halves[0].split(":") : [];
476
+ const right = halves.length === 2 && halves[1] ? halves[1].split(":") : [];
477
+ const missing = 8 - left.length - right.length;
478
+ if (halves.length === 1 && missing !== 0 || halves.length === 2 && missing < 1) {
479
+ return null;
480
+ }
481
+ const groups = [
482
+ ...left,
483
+ ...new Array(halves.length === 2 ? missing : 0).fill("0"),
484
+ ...right
485
+ ];
486
+ if (groups.length !== 8 || groups.some((group) => !/^[0-9a-f]{1,4}$/.test(group))) {
487
+ return null;
488
+ }
489
+ const bytes = new Uint8Array(16);
490
+ for (let i = 0;i < groups.length; i++) {
491
+ const group = Number.parseInt(groups[i], 16);
492
+ bytes[i * 2] = group >>> 8;
493
+ bytes[i * 2 + 1] = group & 255;
494
+ }
495
+ return bytes;
496
+ }
497
+
498
+ // src/internal/pathname.ts
499
+ function decodeSegment(seg) {
500
+ if (seg.indexOf("%") === -1)
501
+ return seg;
502
+ try {
503
+ return decodeURIComponent(seg);
504
+ } catch {
505
+ return seg;
506
+ }
507
+ }
508
+ function decodePercentSegment(seg) {
509
+ try {
510
+ return decodeURIComponent(seg);
511
+ } catch {
512
+ return seg;
513
+ }
514
+ }
515
+ function isDotSegment(seg) {
516
+ return seg === "." || seg === "..";
517
+ }
518
+ function hasDotSegment(path) {
519
+ let i = 0;
520
+ const len = path.length;
521
+ while (i < len) {
522
+ while (i < len && path.charCodeAt(i) === 47)
523
+ i++;
524
+ if (i >= len)
525
+ break;
526
+ const endIdx = path.indexOf("/", i);
527
+ const segEnd = endIdx === -1 ? len : endIdx;
528
+ const segmentLength = segEnd - i;
529
+ if (segmentLength === 1 && path.charCodeAt(i) === 46 || segmentLength === 2 && path.charCodeAt(i) === 46 && path.charCodeAt(i + 1) === 46) {
530
+ return true;
531
+ }
532
+ const percent = path.indexOf("%", i);
533
+ if (percent !== -1 && percent < segEnd) {
534
+ const decoded = decodePercentSegment(path.slice(i, segEnd));
535
+ if (isDotSegment(decoded))
536
+ return true;
537
+ }
538
+ if (endIdx === -1)
539
+ break;
540
+ i = endIdx + 1;
541
+ }
542
+ return false;
543
+ }
544
+ function hasEncodedSlash(path) {
545
+ return path.includes("%2f") || path.includes("%2F");
546
+ }
547
+ function targetStart(url) {
548
+ if (url.charCodeAt(0) === 47)
549
+ return 0;
550
+ if (url.charCodeAt(4) === 58 && url.charCodeAt(5) === 47 && url.charCodeAt(6) === 47)
551
+ return 7;
552
+ if (url.charCodeAt(5) === 58 && url.charCodeAt(6) === 47 && url.charCodeAt(7) === 47)
553
+ return 8;
554
+ const scheme = url.indexOf("://");
555
+ return scheme === -1 ? 0 : scheme + 3;
556
+ }
557
+ function normalizeDotSegments(path) {
558
+ const len = path.length;
559
+ const segments = [];
560
+ let start = 0;
561
+ while (start < len) {
562
+ while (start < len && path.charCodeAt(start) === 47)
563
+ start++;
564
+ if (start >= len)
565
+ break;
566
+ const end = path.indexOf("/", start);
567
+ const segEnd = end === -1 ? len : end;
568
+ const rawSeg = path.slice(start, segEnd);
569
+ const decodedSeg = decodeSegment(rawSeg);
570
+ if (decodedSeg === ".") {} else if (decodedSeg === "..") {
571
+ segments.pop();
572
+ } else {
573
+ segments.push(rawSeg);
574
+ }
575
+ if (end === -1)
576
+ break;
577
+ start = end + 1;
578
+ }
579
+ const res = `/${segments.join("/")}`;
580
+ const endsWithSlash = path.endsWith("/") || path.endsWith("/.") || path.endsWith("/..");
581
+ return path.length > 1 && endsWithSlash && res !== "/" ? `${res}/` : res;
582
+ }
583
+ function scanPathname(url) {
584
+ const s = targetStart(url);
585
+ const slash = url.indexOf("/", s);
586
+ if (slash === -1)
587
+ return "/";
588
+ let end = url.indexOf("?", slash);
589
+ if (end === -1) {
590
+ end = url.indexOf("#", slash);
591
+ if (end === -1)
592
+ end = url.length;
593
+ } else {
594
+ const hash = url.indexOf("#", slash);
595
+ if (hash !== -1 && hash < end)
596
+ end = hash;
597
+ }
598
+ const raw = url.slice(slash, end);
599
+ if (raw.indexOf(".") === -1 && raw.indexOf("%") === -1)
600
+ return raw;
601
+ if (hasEncodedSlash(raw)) {
602
+ return raw;
603
+ }
604
+ return hasDotSegment(raw) ? normalizeDotSegments(raw) : raw;
605
+ }
606
+ function parseRequestTarget(url) {
607
+ const s = targetStart(url);
608
+ const slash = url.indexOf("/", s);
609
+ let pathname;
610
+ let query;
611
+ if (slash === -1) {
612
+ pathname = "/";
613
+ const q = url.indexOf("?", s);
614
+ const h = url.indexOf("#", s);
615
+ if (q !== -1 && (h === -1 || q < h)) {
616
+ query = h === -1 ? url.slice(q + 1) : url.slice(q + 1, h);
617
+ }
618
+ } else {
619
+ let end = url.indexOf("?", slash);
620
+ const hash = url.indexOf("#", slash);
621
+ if (hash !== -1 && (end === -1 || hash < end))
622
+ end = hash;
623
+ if (end === -1) {
624
+ end = url.length;
625
+ } else if (url.charCodeAt(end) === 63) {
626
+ query = hash !== -1 && hash > end ? url.slice(end + 1, hash) : url.slice(end + 1);
627
+ }
628
+ const raw = url.slice(slash, end);
629
+ if (hasEncodedSlash(raw)) {
630
+ pathname = raw;
631
+ } else {
632
+ pathname = hasDotSegment(raw) ? normalizeDotSegments(raw) : raw;
633
+ }
634
+ }
635
+ return { pathname, query };
636
+ }
637
+
638
+ // src/internal/query.ts
639
+ function decodeQueryComponent(value) {
640
+ const hasPlus = value.indexOf("+") !== -1;
641
+ const hasPercent = value.indexOf("%") !== -1;
642
+ if (!hasPlus && !hasPercent)
643
+ return value;
644
+ const formValue = hasPlus ? value.replace(/\+/g, " ") : value;
645
+ if (!hasPercent)
646
+ return formValue;
647
+ try {
648
+ return decodeURIComponent(formValue);
649
+ } catch {
650
+ return new URLSearchParams(`v=${value}`).get("v") ?? "";
651
+ }
652
+ }
653
+ function findQueryParam(url, name, startPos) {
654
+ const queryStart = startPos !== undefined && startPos >= 0 ? startPos : url.indexOf("?");
655
+ if (queryStart === -1)
656
+ return null;
657
+ let s = queryStart + 1;
658
+ const len = url.length;
659
+ const nameLen = name.length;
660
+ while (s < len) {
661
+ if (url.startsWith(name, s)) {
662
+ const eqPos = s + nameLen;
663
+ const nextChar = url.charCodeAt(eqPos);
664
+ if (nextChar === 61) {
665
+ const valStart = eqPos + 1;
666
+ let amp = url.indexOf("&", valStart);
667
+ if (amp === -1)
668
+ amp = len;
669
+ const val = url.slice(valStart, amp);
670
+ if (val.indexOf("+") === -1 && val.indexOf("%") === -1) {
671
+ return val;
672
+ }
673
+ return decodeQueryComponent(val);
674
+ }
675
+ if (nextChar === 38 || eqPos === len) {
676
+ return "";
677
+ }
678
+ }
679
+ const amp = url.indexOf("&", s);
680
+ if (amp === -1)
681
+ break;
682
+ s = amp + 1;
683
+ }
684
+ return null;
685
+ }
686
+ function findQueryParamAll(url, name) {
687
+ const res = [];
688
+ const queryStart = url.indexOf("?");
689
+ if (queryStart === -1)
690
+ return res;
691
+ const len = url.length;
692
+ let s = queryStart + 1;
693
+ const nLen = name.length;
694
+ while (s < len) {
695
+ let amp = url.indexOf("&", s);
696
+ if (amp === -1)
697
+ amp = len;
698
+ let eq = url.indexOf("=", s);
699
+ if (eq === -1 || eq > amp)
700
+ eq = amp;
701
+ if (eq - s === nLen && url.startsWith(name, s)) {
702
+ const val = eq < amp ? url.slice(eq + 1, amp) : "";
703
+ if (val.indexOf("%") === -1 && val.indexOf("+") === -1) {
704
+ res.push(val);
705
+ } else {
706
+ try {
707
+ res.push(decodeURIComponent(val.replace(/\+/g, " ")));
708
+ } catch {
709
+ res.push(val);
710
+ }
711
+ }
712
+ }
713
+ s = amp + 1;
714
+ }
715
+ return res;
716
+ }
717
+ function parseAllQueryParams(url) {
718
+ const qIdx = url.indexOf("?");
719
+ if (qIdx === -1)
720
+ return Object.create(null);
721
+ const len = url.length;
722
+ const res = Object.create(null);
723
+ let s = qIdx + 1;
724
+ while (s < len) {
725
+ let amp = url.indexOf("&", s);
726
+ if (amp === -1)
727
+ amp = len;
728
+ let eq = url.indexOf("=", s);
729
+ if (eq === -1 || eq > amp)
730
+ eq = amp;
731
+ const k = decodeQueryComponent(url.slice(s, eq));
732
+ const v = eq < amp ? decodeQueryComponent(url.slice(eq + 1, amp)) : "";
733
+ if (res[k] === undefined)
734
+ res[k] = v;
735
+ s = amp + 1;
736
+ }
737
+ return res;
738
+ }
739
+
740
+ // src/context.ts
741
+ var TEXT_DECODER = new TextDecoder;
742
+
743
+ class Context {
744
+ req;
745
+ params;
746
+ _hasPendingCookies = false;
747
+ server;
748
+ application;
749
+ _pathname;
750
+ _query = null;
751
+ _url = null;
752
+ _queryParams = null;
753
+ _lazy = null;
754
+ constructor(req, server, params, application, pathname) {
755
+ this.req = req;
756
+ this.server = server;
757
+ this.params = params;
758
+ this.application = application;
759
+ this._pathname = pathname ?? null;
760
+ this._query = null;
761
+ }
762
+ _getLazy() {
763
+ let lazy = this._lazy;
764
+ if (lazy === null) {
765
+ lazy = {};
766
+ this._lazy = lazy;
767
+ }
768
+ return lazy;
769
+ }
770
+ get development() {
771
+ return this.application.development;
772
+ }
773
+ get redirectHosts() {
774
+ return this.application.redirectHosts;
775
+ }
776
+ get trustProxy() {
777
+ return this.application.trustProxy;
778
+ }
779
+ get cookies() {
780
+ const lazy = this._getLazy();
781
+ if (!lazy.cookies) {
782
+ lazy.cookies = createCookieJar(this.application.cookieSecret ?? "", this.req.headers, () => {
783
+ this._hasPendingCookies = true;
784
+ });
785
+ }
786
+ return lazy.cookies;
787
+ }
788
+ _cookieJarIfCreated() {
789
+ return this._lazy?.cookies ?? null;
790
+ }
791
+ get state() {
792
+ const lazy = this._getLazy();
793
+ if (!lazy.state) {
794
+ lazy.state = Object.create(null);
795
+ }
796
+ return lazy.state;
797
+ }
798
+ get valid() {
799
+ const lazy = this._lazy;
800
+ if (lazy && lazy.valid !== undefined)
801
+ return lazy.valid;
802
+ return lazy?.state?.valid;
803
+ }
804
+ set valid(val) {
805
+ this._getLazy().valid = val;
806
+ this.state.valid = val;
807
+ }
808
+ get pathname() {
809
+ if (this._pathname !== null)
810
+ return this._pathname;
811
+ const p = scanPathname(this.req.url);
812
+ this._pathname = p;
813
+ return p;
814
+ }
815
+ get query() {
816
+ if (this._query)
817
+ return this._query;
818
+ const url = this.req.url;
819
+ const qIdx = url.indexOf("?");
820
+ if (qIdx === -1) {
821
+ const sp = new URLSearchParams("");
822
+ this._query = sp;
823
+ return sp;
824
+ }
825
+ const hashIdx = url.indexOf("#", qIdx);
826
+ const qStr = hashIdx === -1 ? url.slice(qIdx + 1) : url.slice(qIdx + 1, hashIdx);
827
+ const sp = new URLSearchParams(qStr);
828
+ this._query = sp;
829
+ return sp;
830
+ }
831
+ queryParam(name) {
832
+ if (this._query)
833
+ return this._query.get(name);
834
+ if (this._queryParams === null) {
835
+ if (this._url === null)
836
+ this._url = this.req.url;
837
+ this._queryParams = parseAllQueryParams(this._url);
838
+ }
839
+ return this._queryParams[name] ?? null;
840
+ }
841
+ queryParamAll(name) {
842
+ return this.query.getAll(name);
843
+ }
844
+ get ip() {
845
+ const lazy = this._lazy;
846
+ if (lazy && typeof lazy.ip === "string")
847
+ return lazy.ip;
848
+ const directIp = this.getDirectIp();
849
+ if (!this.isTrustedProxy(directIp)) {
850
+ this._getLazy().ip = directIp;
851
+ return directIp;
852
+ }
853
+ const forwarded = this.req.headers.get("x-forwarded-for");
854
+ if (forwarded) {
855
+ const hops = forwarded.split(",").map((hop) => hop.trim());
856
+ if (hops.every(isValidIpAddress)) {
857
+ let clientIp = directIp;
858
+ for (let i = hops.length - 1;i >= 0; i--) {
859
+ if (clientIp && !this.isTrustedProxy(clientIp))
860
+ break;
861
+ clientIp = hops[i];
862
+ }
863
+ this._getLazy().ip = clientIp;
864
+ return clientIp;
865
+ }
866
+ this._getLazy().ip = directIp;
867
+ return directIp;
868
+ }
869
+ for (const name of ["cf-connecting-ip", "x-real-ip"]) {
870
+ const candidate = this.req.headers.get(name)?.trim();
871
+ if (candidate && isValidIpAddress(candidate)) {
872
+ this._getLazy().ip = candidate;
873
+ return candidate;
874
+ }
875
+ }
876
+ this._getLazy().ip = directIp;
877
+ return directIp;
878
+ }
879
+ isTrustedProxy(ip) {
880
+ const trust = this.trustProxy;
881
+ if (trust === true)
882
+ return true;
883
+ if (trust === false || !ip)
884
+ return false;
885
+ if (this.application.trustedProxyChecker) {
886
+ return this.application.trustedProxyChecker(ip);
887
+ }
888
+ if (typeof trust === "function")
889
+ return trust(ip);
890
+ if (typeof trust !== "string") {
891
+ return trust.some((range) => ipMatchesRange(ip, range));
892
+ }
893
+ return ipMatchesRange(ip, trust);
894
+ }
895
+ getDirectIp() {
896
+ const info = this.server?.requestIP(this.req);
897
+ return info?.address ?? "";
898
+ }
899
+ get bodyStream() {
900
+ return this.req.body;
901
+ }
902
+ stream() {
903
+ return this.req.body;
904
+ }
905
+ _getBodyBuffer() {
906
+ const lazy = this._getLazy();
907
+ if (!lazy.bodyBufferPromise) {
908
+ lazy.bodyBufferPromise = this.req.arrayBuffer().catch((err) => {
909
+ lazy.bodyBufferPromise = null;
910
+ throw err;
911
+ });
912
+ }
913
+ return lazy.bodyBufferPromise;
914
+ }
915
+ arrayBuffer() {
916
+ return this._getBodyBuffer();
917
+ }
918
+ text() {
919
+ const lazy = this._getLazy();
920
+ if (!lazy.textPromise) {
921
+ lazy.textPromise = this._getBodyBuffer().then((buf) => TEXT_DECODER.decode(buf)).catch((err) => {
922
+ lazy.textPromise = null;
923
+ throw err;
924
+ });
925
+ }
926
+ return lazy.textPromise;
927
+ }
928
+ json() {
929
+ const lazy = this._getLazy();
930
+ if (!lazy.jsonPromise) {
931
+ lazy.jsonPromise = this._getBodyBuffer().then((buf) => JSON.parse(TEXT_DECODER.decode(buf))).catch((err) => {
932
+ lazy.jsonPromise = null;
933
+ throw err;
934
+ });
935
+ }
936
+ return lazy.jsonPromise;
937
+ }
938
+ blob() {
939
+ const lazy = this._getLazy();
940
+ if (!lazy.blobPromise) {
941
+ const ct = this.req.headers.get("content-type") ?? "application/octet-stream";
942
+ lazy.blobPromise = this._getBodyBuffer().then((buf) => new Blob([buf], { type: ct })).catch((err) => {
943
+ lazy.blobPromise = null;
944
+ throw err;
945
+ });
946
+ }
947
+ return lazy.blobPromise;
948
+ }
949
+ formData() {
950
+ const lazy = this._getLazy();
951
+ if (!lazy.formDataPromise) {
952
+ lazy.formDataPromise = this._getBodyBuffer().then((buf) => {
953
+ const ct = this.req.headers.get("content-type") ?? "";
954
+ const tempRes = new Response(buf, {
955
+ headers: { "content-type": ct }
956
+ });
957
+ return tempRes.formData();
958
+ }).catch((err) => {
959
+ lazy.formDataPromise = null;
960
+ throw err;
961
+ });
962
+ }
963
+ return lazy.formDataPromise;
964
+ }
965
+ redirect(url, status = 302) {
966
+ return redirect(url, status, this.redirectHosts);
967
+ }
968
+ upgrade(data) {
969
+ if (!this.server) {
970
+ throw new Error("Cannot upgrade WebSocket: server instance is not available");
971
+ }
972
+ return this.server.upgrade(this.req, { data });
973
+ }
974
+ timeout(seconds) {
975
+ if (this.server) {
976
+ this.server.timeout(this.req, seconds);
977
+ }
978
+ return this;
979
+ }
980
+ disableTimeout() {
981
+ if (this.server) {
982
+ this.server.timeout(this.req, 0);
983
+ }
984
+ return this;
985
+ }
986
+ }
987
+
988
+ // src/errors.ts
989
+ class HttpError extends Error {
990
+ status;
991
+ body;
992
+ headers;
993
+ constructor(status, body, headers) {
994
+ if (!Number.isInteger(status) || status < 100 || status > 599) {
995
+ throw new Error(`Invalid HTTP status code: ${status}. Must be an integer between 100 and 599.`);
996
+ }
997
+ super(typeof body === "string" ? body : `HTTP ${status}`);
998
+ this.name = "HttpError";
999
+ this.status = status;
1000
+ this.body = body ?? { error: defaultStatusText(status) };
1001
+ if (headers !== undefined)
1002
+ this.headers = headers;
1003
+ }
1004
+ }
1005
+ function defaultStatusText(status) {
1006
+ switch (status) {
1007
+ case 400:
1008
+ return "Bad Request";
1009
+ case 401:
1010
+ return "Unauthorized";
1011
+ case 403:
1012
+ return "Forbidden";
1013
+ case 404:
1014
+ return "Not Found";
1015
+ case 405:
1016
+ return "Method Not Allowed";
1017
+ case 408:
1018
+ return "Request Timeout";
1019
+ case 409:
1020
+ return "Conflict";
1021
+ case 413:
1022
+ return "Payload Too Large";
1023
+ case 418:
1024
+ return "I'm a teapot";
1025
+ case 422:
1026
+ return "Unprocessable Entity";
1027
+ case 429:
1028
+ return "Too Many Requests";
1029
+ case 500:
1030
+ return "Internal Server Error";
1031
+ case 502:
1032
+ return "Bad Gateway";
1033
+ case 503:
1034
+ return "Service Unavailable";
1035
+ case 504:
1036
+ return "Gateway Timeout";
1037
+ default:
1038
+ return status >= 500 ? "Internal Server Error" : "Error";
1039
+ }
1040
+ }
1041
+ function safeJson(status, body, headers) {
1042
+ try {
1043
+ const init = headers !== undefined ? { status, headers } : { status };
1044
+ return json(body, init);
1045
+ } catch {
1046
+ return new Response("Internal Server Error", {
1047
+ status: 500,
1048
+ headers: { "content-type": "text/plain; charset=utf-8" }
1049
+ });
1050
+ }
1051
+ }
1052
+ function errorToResponse(err, development) {
1053
+ if (err instanceof Response) {
1054
+ return err;
1055
+ }
1056
+ if (err instanceof HttpError) {
1057
+ return safeJson(err.status, err.body, err.headers);
1058
+ }
1059
+ if (development) {
1060
+ const message = err instanceof Error ? err.message : String(err);
1061
+ const stack = err instanceof Error ? err.stack : undefined;
1062
+ return safeJson(500, { error: "Internal Server Error", message, stack });
1063
+ }
1064
+ return safeJson(500, { error: "Internal Server Error" });
1065
+ }
1066
+ var createErrorResponse = errorToResponse;
1067
+
1068
+ // src/internal/scan.ts
1069
+ var ESCAPE_JSON_CHARS_RE = /[\u0000-\u001f"\\]/;
1070
+ function escapeJsonString(value) {
1071
+ if (!ESCAPE_JSON_CHARS_RE.test(value))
1072
+ return value;
1073
+ return JSON.stringify(value).slice(1, -1);
1074
+ }
1075
+ function isIdentStart(c) {
1076
+ return c >= 97 && c <= 122 || c >= 65 && c <= 90 || c === 95 || c === 36 || c > 127;
1077
+ }
1078
+ function isIdentPart(c) {
1079
+ return isIdentStart(c) || c >= 48 && c <= 57;
1080
+ }
1081
+ function isSpace(c) {
1082
+ return c === 32 || c === 9 || c === 10 || c === 13 || c === 12 || c === 11;
1083
+ }
1084
+ var KEYWORDS_BEFORE_REGEX = new Set([
1085
+ "return",
1086
+ "typeof",
1087
+ "instanceof",
1088
+ "in",
1089
+ "of",
1090
+ "new",
1091
+ "delete",
1092
+ "void",
1093
+ "throw",
1094
+ "case",
1095
+ "do",
1096
+ "else",
1097
+ "yield",
1098
+ "await"
1099
+ ]);
1100
+ var PUNCT_BEFORE_REGEX = new Set([
1101
+ "(",
1102
+ "[",
1103
+ "{",
1104
+ ",",
1105
+ ";",
1106
+ ":",
1107
+ "?",
1108
+ "!",
1109
+ "=",
1110
+ "==",
1111
+ "===",
1112
+ "!=",
1113
+ "!==",
1114
+ "&&",
1115
+ "||",
1116
+ "??",
1117
+ "+",
1118
+ "-",
1119
+ "*",
1120
+ "/",
1121
+ "%",
1122
+ "&",
1123
+ "|",
1124
+ "^",
1125
+ "~",
1126
+ "<",
1127
+ ">",
1128
+ "<=",
1129
+ ">=",
1130
+ "=>",
1131
+ "+=",
1132
+ "-=",
1133
+ "*=",
1134
+ "/=",
1135
+ "%=",
1136
+ "&=",
1137
+ "|=",
1138
+ "^=",
1139
+ "&&=",
1140
+ "||=",
1141
+ "??=",
1142
+ "<<",
1143
+ ">>",
1144
+ ">>>",
1145
+ "<<=",
1146
+ ">>=",
1147
+ ">>>="
1148
+ ]);
1149
+ function tokenize(src) {
1150
+ const tokens = [];
1151
+ const len = src.length;
1152
+ let i = 0;
1153
+ let last = "";
1154
+ while (i < len) {
1155
+ const c = src.charCodeAt(i);
1156
+ if (isSpace(c)) {
1157
+ i++;
1158
+ continue;
1159
+ }
1160
+ if (c === 47 && src.charCodeAt(i + 1) === 47) {
1161
+ i += 2;
1162
+ while (i < len && src.charCodeAt(i) !== 10)
1163
+ i++;
1164
+ continue;
1165
+ }
1166
+ if (c === 47 && src.charCodeAt(i + 1) === 42) {
1167
+ i += 2;
1168
+ while (i < len && !(src.charCodeAt(i) === 42 && src.charCodeAt(i + 1) === 47))
1169
+ i++;
1170
+ i += 2;
1171
+ continue;
1172
+ }
1173
+ if (c === 34 || c === 39) {
1174
+ const q = c;
1175
+ const start = i;
1176
+ i++;
1177
+ while (i < len) {
1178
+ const cc = src.charCodeAt(i);
1179
+ if (cc === 92) {
1180
+ i += 2;
1181
+ continue;
1182
+ }
1183
+ if (cc === q) {
1184
+ i++;
1185
+ break;
1186
+ }
1187
+ if (cc === 10)
1188
+ break;
1189
+ i++;
1190
+ }
1191
+ tokens.push({
1192
+ type: "str",
1193
+ value: src.slice(start + 1, i - 1),
1194
+ start,
1195
+ end: i
1196
+ });
1197
+ last = "str";
1198
+ continue;
1199
+ }
1200
+ if (c === 96) {
1201
+ i = tokenizeTemplate(src, i, tokens);
1202
+ last = "template";
1203
+ continue;
1204
+ }
1205
+ if (c === 47) {
1206
+ let isRegex;
1207
+ if (last === "" || last === "regex" || last === "num")
1208
+ isRegex = true;
1209
+ else if (last === "str" || last === "template")
1210
+ isRegex = false;
1211
+ else if (last === "ident")
1212
+ isRegex = KEYWORDS_BEFORE_REGEX.has(last);
1213
+ else
1214
+ isRegex = PUNCT_BEFORE_REGEX.has(last);
1215
+ if (isRegex) {
1216
+ const start = i;
1217
+ i++;
1218
+ let inClass = false;
1219
+ while (i < len) {
1220
+ const cc = src.charCodeAt(i);
1221
+ if (cc === 92) {
1222
+ i += 2;
1223
+ continue;
1224
+ }
1225
+ if (cc === 91) {
1226
+ inClass = true;
1227
+ i++;
1228
+ continue;
1229
+ }
1230
+ if (cc === 93) {
1231
+ inClass = false;
1232
+ i++;
1233
+ continue;
1234
+ }
1235
+ if (cc === 47 && !inClass) {
1236
+ i++;
1237
+ break;
1238
+ }
1239
+ if (cc === 10)
1240
+ break;
1241
+ i++;
1242
+ }
1243
+ while (i < len && isIdentPart(src.charCodeAt(i)))
1244
+ i++;
1245
+ tokens.push({ type: "regex", value: "", start, end: i });
1246
+ last = "regex";
1247
+ continue;
1248
+ }
1249
+ tokens.push({ type: "punct", value: "/", start: i, end: i + 1 });
1250
+ last = "/";
1251
+ i++;
1252
+ continue;
1253
+ }
1254
+ if (isIdentStart(c)) {
1255
+ const start = i;
1256
+ i++;
1257
+ while (i < len && isIdentPart(src.charCodeAt(i)))
1258
+ i++;
1259
+ const value = src.slice(start, i);
1260
+ tokens.push({ type: "ident", value, start, end: i });
1261
+ last = value;
1262
+ continue;
1263
+ }
1264
+ if (c >= 48 && c <= 57) {
1265
+ const start = i;
1266
+ i++;
1267
+ while (i < len) {
1268
+ const cc = src.charCodeAt(i);
1269
+ if (cc >= 48 && cc <= 57 || cc === 46 || cc === 95 || cc >= 97 && cc <= 122 || cc >= 65 && cc <= 90)
1270
+ i++;
1271
+ else if ((cc === 43 || cc === 45) && (src.charCodeAt(i - 1) === 101 || src.charCodeAt(i - 1) === 69))
1272
+ i++;
1273
+ else
1274
+ break;
1275
+ }
1276
+ tokens.push({ type: "num", value: src.slice(start, i), start, end: i });
1277
+ last = "num";
1278
+ continue;
1279
+ }
1280
+ const start = i;
1281
+ const cc2 = src.charCodeAt(i + 1);
1282
+ const cc3 = src.charCodeAt(i + 2);
1283
+ const cc4 = src.charCodeAt(i + 3);
1284
+ let op = src[i];
1285
+ if (c === 62 && cc2 === 62 && cc3 === 62 && cc4 === 61)
1286
+ op = ">>>=";
1287
+ else if (c === 61 && cc2 === 61 && cc3 === 61)
1288
+ op = "===";
1289
+ else if (c === 33 && cc2 === 61 && cc3 === 61)
1290
+ op = "!==";
1291
+ else if (c === 42 && cc2 === 42 && cc3 === 61)
1292
+ op = "**=";
1293
+ else if (c === 60 && cc2 === 60 && cc3 === 61)
1294
+ op = "<<=";
1295
+ else if (c === 62 && cc2 === 62 && cc3 === 61)
1296
+ op = ">>=";
1297
+ else if (c === 62 && cc2 === 62 && cc3 === 62)
1298
+ op = ">>>";
1299
+ else if (c === 46 && cc2 === 46 && cc3 === 46)
1300
+ op = "...";
1301
+ else if (c === 63 && cc2 === 63 && cc3 === 61)
1302
+ op = "??=";
1303
+ else if (c === 38 && cc2 === 38 && cc3 === 61)
1304
+ op = "&&=";
1305
+ else if (c === 124 && cc2 === 124 && cc3 === 61)
1306
+ op = "||=";
1307
+ else if (c === 61 && cc2 === 62)
1308
+ op = "=>";
1309
+ else if (c === 61 && cc2 === 61)
1310
+ op = "==";
1311
+ else if (c === 33 && cc2 === 61)
1312
+ op = "!=";
1313
+ else if (c === 60 && cc2 === 61)
1314
+ op = "<=";
1315
+ else if (c === 62 && cc2 === 61)
1316
+ op = ">=";
1317
+ else if (c === 38 && cc2 === 38)
1318
+ op = "&&";
1319
+ else if (c === 124 && cc2 === 124)
1320
+ op = "||";
1321
+ else if (c === 63 && cc2 === 46)
1322
+ op = "?.";
1323
+ else if (c === 63 && cc2 === 63)
1324
+ op = "??";
1325
+ else if (c === 43 && cc2 === 43)
1326
+ op = "++";
1327
+ else if (c === 45 && cc2 === 45)
1328
+ op = "--";
1329
+ else if (c === 42 && cc2 === 42)
1330
+ op = "**";
1331
+ else if (c === 60 && cc2 === 60)
1332
+ op = "<<";
1333
+ else if (c === 62 && cc2 === 62)
1334
+ op = ">>";
1335
+ else if (c === 43 && cc2 === 61)
1336
+ op = "+=";
1337
+ else if (c === 45 && cc2 === 61)
1338
+ op = "-=";
1339
+ else if (c === 42 && cc2 === 61)
1340
+ op = "*=";
1341
+ else if (c === 47 && cc2 === 61)
1342
+ op = "/=";
1343
+ else if (c === 37 && cc2 === 61)
1344
+ op = "%=";
1345
+ else if (c === 38 && cc2 === 61)
1346
+ op = "&=";
1347
+ else if (c === 124 && cc2 === 61)
1348
+ op = "|=";
1349
+ else if (c === 94 && cc2 === 61)
1350
+ op = "^=";
1351
+ i += op.length;
1352
+ tokens.push({ type: "punct", value: op, start, end: i });
1353
+ last = op;
1354
+ }
1355
+ tokens.push({ type: "eof", value: "", start: len, end: len });
1356
+ return tokens;
1357
+ }
1358
+ function tokenizeTemplate(src, start, out) {
1359
+ let i = start + 1;
1360
+ const len = src.length;
1361
+ let strStart = i;
1362
+ while (i < len) {
1363
+ const c = src.charCodeAt(i);
1364
+ if (c === 92) {
1365
+ i += 2;
1366
+ continue;
1367
+ }
1368
+ if (c === 96) {
1369
+ if (i > strStart) {
1370
+ out.push({
1371
+ type: "str",
1372
+ value: src.slice(strStart, i),
1373
+ start: strStart,
1374
+ end: i
1375
+ });
1376
+ }
1377
+ return i + 1;
1378
+ }
1379
+ if (c === 36 && src.charCodeAt(i + 1) === 123) {
1380
+ if (i > strStart) {
1381
+ out.push({
1382
+ type: "str",
1383
+ value: src.slice(strStart, i),
1384
+ start: strStart,
1385
+ end: i
1386
+ });
1387
+ }
1388
+ out.push({ type: "punct", value: "${", start: i, end: i + 2 });
1389
+ i += 2;
1390
+ const exprStart = i;
1391
+ let depth = 1;
1392
+ while (i < len && depth > 0) {
1393
+ const cc = src.charCodeAt(i);
1394
+ if (cc === 92) {
1395
+ i += 2;
1396
+ continue;
1397
+ }
1398
+ if (cc === 34 || cc === 39) {
1399
+ const q = cc;
1400
+ i++;
1401
+ while (i < len) {
1402
+ const c2 = src.charCodeAt(i);
1403
+ if (c2 === 92) {
1404
+ i += 2;
1405
+ continue;
1406
+ }
1407
+ if (c2 === q) {
1408
+ i++;
1409
+ break;
1410
+ }
1411
+ i++;
1412
+ }
1413
+ continue;
1414
+ }
1415
+ if (cc === 96) {
1416
+ i = tokenizeTemplate(src, i, out);
1417
+ continue;
1418
+ }
1419
+ if (cc === 123) {
1420
+ depth++;
1421
+ i++;
1422
+ continue;
1423
+ }
1424
+ if (cc === 125) {
1425
+ depth--;
1426
+ if (depth === 0)
1427
+ break;
1428
+ i++;
1429
+ continue;
1430
+ }
1431
+ i++;
1432
+ }
1433
+ const inner = src.slice(exprStart, i);
1434
+ const innerTokens = tokenize(inner);
1435
+ for (const t of innerTokens) {
1436
+ if (t.type === "eof")
1437
+ continue;
1438
+ out.push({ ...t, start: t.start + exprStart, end: t.end + exprStart });
1439
+ }
1440
+ out.push({ type: "punct", value: "}", start: i, end: i + 1 });
1441
+ i++;
1442
+ strStart = i;
1443
+ continue;
1444
+ }
1445
+ i++;
1446
+ }
1447
+ if (i > strStart) {
1448
+ out.push({
1449
+ type: "str",
1450
+ value: src.slice(strStart, i),
1451
+ start: strStart,
1452
+ end: i
1453
+ });
1454
+ }
1455
+ return i;
1456
+ }
1457
+ function analyzeHandler(fn) {
1458
+ let src;
1459
+ try {
1460
+ src = fn.toString();
1461
+ } catch {
1462
+ return makeUnknown();
1463
+ }
1464
+ const tokens = tokenize(src);
1465
+ let i = 0;
1466
+ if (tokens[i]?.type === "ident" && tokens[i].value === "async")
1467
+ i++;
1468
+ if (tokens[i]?.type === "ident" && tokens[i].value === "function") {
1469
+ i++;
1470
+ if (tokens[i]?.type === "ident")
1471
+ i++;
1472
+ }
1473
+ if (tokens[i]?.type === "punct" && tokens[i].value === "(") {
1474
+ i++;
1475
+ const first = tokens[i];
1476
+ if (first?.type === "punct" && first.value === ")") {
1477
+ i++;
1478
+ const body = walkBody(tokens, i, null);
1479
+ return {
1480
+ isNoArg: true,
1481
+ contextProps: new Set,
1482
+ hasDynamicAccess: false,
1483
+ isDestructured: false,
1484
+ destructuredProps: new Set,
1485
+ literal: body.literal,
1486
+ arglessJsonCall: false,
1487
+ paramSerializer: null,
1488
+ paramParts: null
1489
+ };
1490
+ }
1491
+ if (first?.type === "punct" && first.value === "{") {
1492
+ const { names, next } = parseDestructure(tokens, i);
1493
+ const body = walkBody(tokens, next, null);
1494
+ return {
1495
+ isNoArg: false,
1496
+ contextProps: new Set,
1497
+ hasDynamicAccess: false,
1498
+ isDestructured: true,
1499
+ destructuredProps: names,
1500
+ literal: body.literal,
1501
+ arglessJsonCall: false,
1502
+ paramSerializer: body.paramSerializer,
1503
+ paramParts: body.paramParts
1504
+ };
1505
+ }
1506
+ if (first?.type === "ident") {
1507
+ const name = first.value;
1508
+ i++;
1509
+ const body = walkBody(tokens, i, name);
1510
+ return {
1511
+ isNoArg: false,
1512
+ contextProps: body.props,
1513
+ hasDynamicAccess: body.hasDynamic,
1514
+ isDestructured: false,
1515
+ destructuredProps: new Set,
1516
+ literal: body.literal,
1517
+ arglessJsonCall: body.arglessJsonCall,
1518
+ paramSerializer: body.paramSerializer,
1519
+ paramParts: body.paramParts
1520
+ };
1521
+ }
1522
+ return makeUnknown();
1523
+ }
1524
+ if (tokens[i]?.type === "ident") {
1525
+ const name = tokens[i].value;
1526
+ i++;
1527
+ const body = walkBody(tokens, i, name);
1528
+ return {
1529
+ isNoArg: false,
1530
+ contextProps: body.props,
1531
+ hasDynamicAccess: body.hasDynamic,
1532
+ isDestructured: false,
1533
+ destructuredProps: new Set,
1534
+ literal: body.literal,
1535
+ arglessJsonCall: body.arglessJsonCall,
1536
+ paramSerializer: body.paramSerializer,
1537
+ paramParts: body.paramParts
1538
+ };
1539
+ }
1540
+ return makeUnknown();
1541
+ }
1542
+ function parseDestructure(tokens, braceStart) {
1543
+ const names = new Set;
1544
+ let i = braceStart + 1;
1545
+ let depth = 1;
1546
+ while (i < tokens.length) {
1547
+ const t = tokens[i];
1548
+ if (t.type === "eof")
1549
+ break;
1550
+ if (t.type === "punct") {
1551
+ if (t.value === "{")
1552
+ depth++;
1553
+ else if (t.value === "}") {
1554
+ depth--;
1555
+ i++;
1556
+ if (depth === 0)
1557
+ break;
1558
+ continue;
1559
+ } else if (t.value === "[")
1560
+ depth += 100;
1561
+ else if (t.value === "]")
1562
+ depth -= 100;
1563
+ }
1564
+ if (t.type === "ident" && depth === 1) {
1565
+ const prev = tokens[i - 1];
1566
+ const next = tokens[i + 1];
1567
+ if (prev?.type === "punct" && prev.value === ":") {
1568
+ names.add(t.value);
1569
+ } else if (next?.type !== "punct" || next.value !== ":") {
1570
+ names.add(t.value);
1571
+ }
1572
+ }
1573
+ i++;
1574
+ }
1575
+ while (i < tokens.length) {
1576
+ const t = tokens[i];
1577
+ if (t.type === "eof")
1578
+ break;
1579
+ if (t.type === "punct" && t.value === ")") {
1580
+ i++;
1581
+ break;
1582
+ }
1583
+ i++;
1584
+ }
1585
+ return { names, next: i };
1586
+ }
1587
+ function walkBody(tokens, from, paramName) {
1588
+ const props = new Set;
1589
+ let hasDynamic = false;
1590
+ let arglessJsonCall = false;
1591
+ let bodyStart = -1;
1592
+ let i = from;
1593
+ let parenDepth = 0;
1594
+ if (tokens[from]?.type === "punct" && tokens[from].value === "{") {
1595
+ bodyStart = from + 1;
1596
+ } else {
1597
+ while (i < tokens.length) {
1598
+ const t = tokens[i];
1599
+ if (t.type === "eof")
1600
+ break;
1601
+ if (t.type === "punct") {
1602
+ if (t.value === "(" || t.value === "[" || t.value === "{") {
1603
+ parenDepth++;
1604
+ } else if (t.value === ")" || t.value === "]" || t.value === "}") {
1605
+ if (parenDepth > 0)
1606
+ parenDepth--;
1607
+ } else if (t.value === "=>" && parenDepth === 0) {
1608
+ const next = tokens[i + 1];
1609
+ if (next?.type === "punct" && next.value === "{") {
1610
+ bodyStart = i + 2;
1611
+ } else {
1612
+ bodyStart = i + 1;
1613
+ }
1614
+ break;
1615
+ }
1616
+ if (t.value === ")" && parenDepth === 0 && i + 1 < tokens.length) {
1617
+ const next = tokens[i + 1];
1618
+ if (next?.type === "punct" && next.value === "{") {
1619
+ bodyStart = i + 2;
1620
+ break;
1621
+ }
1622
+ }
1623
+ }
1624
+ i++;
1625
+ }
1626
+ }
1627
+ if (bodyStart < 0) {
1628
+ return {
1629
+ props,
1630
+ hasDynamic,
1631
+ literal: null,
1632
+ arglessJsonCall: false,
1633
+ paramSerializer: null,
1634
+ paramParts: null
1635
+ };
1636
+ }
1637
+ if (paramName !== null) {
1638
+ for (let j = bodyStart;j < tokens.length; j++) {
1639
+ const t = tokens[j];
1640
+ if (t.type === "eof")
1641
+ break;
1642
+ if (t.type !== "ident" || t.value !== paramName)
1643
+ continue;
1644
+ const n = tokens[j + 1];
1645
+ if (!n)
1646
+ continue;
1647
+ if (n.type === "punct" && (n.value === "." || n.value === "?.")) {
1648
+ const key = tokens[j + 2];
1649
+ if (key?.type === "ident") {
1650
+ props.add(key.value);
1651
+ if (key.value === "json") {
1652
+ const pOpen = tokens[j + 3];
1653
+ const pClose = tokens[j + 4];
1654
+ if (pOpen?.type === "punct" && pOpen.value === "(" && pClose?.type === "punct" && pClose.value === ")") {
1655
+ const prev1 = tokens[j - 1];
1656
+ const prev2 = tokens[j - 2];
1657
+ const isPrecededByReturn = prev1?.type === "punct" && prev1.value === "=>" || prev1?.type === "ident" && prev1.value === "return" || prev1?.type === "ident" && prev1.value === "await" && (prev2?.type === "punct" && prev2.value === "=>" || prev2?.type === "ident" && prev2.value === "return");
1658
+ const next1 = tokens[j + 5];
1659
+ const next2 = tokens[j + 6];
1660
+ const isEndOfBody = isBodyEnd(next1) && (next1?.value !== ";" || isBodyEnd(next2));
1661
+ if (isPrecededByReturn && isEndOfBody) {
1662
+ arglessJsonCall = true;
1663
+ }
1664
+ }
1665
+ }
1666
+ j += 2;
1667
+ continue;
1668
+ }
1669
+ hasDynamic = true;
1670
+ continue;
1671
+ }
1672
+ if (n.type === "punct" && n.value === "[") {
1673
+ const key = tokens[j + 2];
1674
+ const close = tokens[j + 3];
1675
+ if (key?.type === "str" && close?.type === "punct" && close.value === "]") {
1676
+ props.add(key.value);
1677
+ j += 3;
1678
+ continue;
1679
+ }
1680
+ hasDynamic = true;
1681
+ continue;
1682
+ }
1683
+ const prev = tokens[j - 1];
1684
+ if (prev?.type === "punct" && prev.value === "...") {
1685
+ hasDynamic = true;
1686
+ }
1687
+ }
1688
+ }
1689
+ const literal = tryExtractLiteral(tokens, bodyStart);
1690
+ const paramExtraction = literal === null ? tryExtractParamSerializer(tokens, bodyStart, paramName) : null;
1691
+ const paramSerializer = paramExtraction?.serializer ?? null;
1692
+ const paramParts = paramExtraction?.parts ?? null;
1693
+ return {
1694
+ props,
1695
+ hasDynamic,
1696
+ literal,
1697
+ arglessJsonCall,
1698
+ paramSerializer,
1699
+ paramParts
1700
+ };
1701
+ }
1702
+ function isBodyEnd(t) {
1703
+ if (!t)
1704
+ return true;
1705
+ if (t.type === "eof")
1706
+ return true;
1707
+ if (t.type === "punct" && (t.value === ";" || t.value === "}"))
1708
+ return true;
1709
+ return false;
1710
+ }
1711
+ function tryExtractLiteral(tokens, bodyStart) {
1712
+ const t = tokens[bodyStart];
1713
+ if (!t || t.type === "eof")
1714
+ return null;
1715
+ if (t.type === "ident" && t.value === "return") {
1716
+ return tryExtractLiteral(tokens, bodyStart + 1);
1717
+ }
1718
+ if (t.type === "str") {
1719
+ if (isBodyEnd(tokens[bodyStart + 1])) {
1720
+ return { kind: "string", value: t.value };
1721
+ }
1722
+ return null;
1723
+ }
1724
+ if (t.type === "punct" && t.value === "(") {
1725
+ const inner = tokens[bodyStart + 1];
1726
+ if (inner?.type === "punct" && inner.value === "{") {
1727
+ const obj = extractFlatObject(tokens, bodyStart + 1);
1728
+ if (!obj.ok)
1729
+ return null;
1730
+ const afterBrace = tokens[obj.end];
1731
+ if (afterBrace?.type !== "punct" || afterBrace.value !== ")")
1732
+ return null;
1733
+ if (!isBodyEnd(tokens[obj.end + 1]))
1734
+ return null;
1735
+ return { kind: "json", value: JSON.stringify(obj.value) };
1736
+ }
1737
+ return null;
1738
+ }
1739
+ if (t.type === "punct" && t.value === "{") {
1740
+ const next = tokens[bodyStart + 1];
1741
+ if (next && (next.type === "ident" || next.type === "str" || next.type === "num") && tokens[bodyStart + 2]?.type === "punct" && tokens[bodyStart + 2].value === ":") {
1742
+ const obj = extractFlatObject(tokens, bodyStart);
1743
+ if (obj.ok) {
1744
+ if (isBodyEnd(tokens[obj.end])) {
1745
+ return { kind: "json", value: JSON.stringify(obj.value) };
1746
+ }
1747
+ return null;
1748
+ }
1749
+ return null;
1750
+ }
1751
+ let j = bodyStart + 1;
1752
+ while (j < tokens.length) {
1753
+ const tt = tokens[j];
1754
+ if (tt.type === "eof")
1755
+ return null;
1756
+ if (tt.type === "punct" && tt.value === "}")
1757
+ return null;
1758
+ if (tt.type === "ident" && tt.value === "return") {
1759
+ return tryExtractLiteral(tokens, j + 1);
1760
+ }
1761
+ if (tt.type === "punct" && tt.value === ";")
1762
+ return null;
1763
+ if (tt.type === "ident" && tt.value !== "return")
1764
+ return null;
1765
+ j++;
1766
+ }
1767
+ return null;
1768
+ }
1769
+ return null;
1770
+ }
1771
+ function extractFlatObject(tokens, braceStart) {
1772
+ let i = braceStart + 1;
1773
+ const obj = {};
1774
+ let expectKey = true;
1775
+ let key = null;
1776
+ while (i < tokens.length) {
1777
+ const t = tokens[i];
1778
+ if (t.type === "eof")
1779
+ return { ok: false };
1780
+ if (t.type === "punct" && t.value === "}") {
1781
+ if (!expectKey)
1782
+ return { ok: false };
1783
+ return { ok: true, end: i + 1, value: obj };
1784
+ }
1785
+ if (expectKey) {
1786
+ if (t.type === "ident" || t.type === "str" || t.type === "num")
1787
+ key = t.value;
1788
+ else
1789
+ return { ok: false };
1790
+ i++;
1791
+ const colon = tokens[i];
1792
+ if (colon?.type !== "punct" || colon.value !== ":")
1793
+ return { ok: false };
1794
+ i++;
1795
+ expectKey = false;
1796
+ continue;
1797
+ }
1798
+ if (t.type === "str") {
1799
+ obj[key] = t.value;
1800
+ i++;
1801
+ } else if (t.type === "num") {
1802
+ obj[key] = Number(t.value);
1803
+ i++;
1804
+ } else if (t.type === "ident") {
1805
+ if (t.value === "true")
1806
+ obj[key] = true;
1807
+ else if (t.value === "false")
1808
+ obj[key] = false;
1809
+ else if (t.value === "null")
1810
+ obj[key] = null;
1811
+ else
1812
+ return { ok: false };
1813
+ i++;
1814
+ } else if (t.type === "punct" && t.value === "!" && tokens[i + 1]?.type === "num") {
1815
+ const n = tokens[i + 1].value;
1816
+ obj[key] = n === "1";
1817
+ i += 2;
1818
+ } else if (t.type === "punct" && t.value === "-" && tokens[i + 1]?.type === "num") {
1819
+ obj[key] = -Number(tokens[i + 1].value);
1820
+ i += 2;
1821
+ } else {
1822
+ return { ok: false };
1823
+ }
1824
+ const sep = tokens[i];
1825
+ if (sep?.type === "punct" && sep.value === ",") {
1826
+ i++;
1827
+ expectKey = true;
1828
+ continue;
1829
+ }
1830
+ if (sep?.type === "punct" && sep.value === "}") {
1831
+ return { ok: true, end: i + 1, value: obj };
1832
+ }
1833
+ return { ok: false };
1834
+ }
1835
+ return { ok: false };
1836
+ }
1837
+ function extractParamObject(tokens, braceStart, paramName) {
1838
+ let i = braceStart + 1;
1839
+ const parts = [];
1840
+ let expectKey = true;
1841
+ let key = null;
1842
+ let hasParam = false;
1843
+ while (i < tokens.length) {
1844
+ const t = tokens[i];
1845
+ if (t.type === "eof")
1846
+ return { ok: false };
1847
+ if (t.type === "punct" && t.value === "}") {
1848
+ if (!expectKey)
1849
+ return { ok: false };
1850
+ return hasParam ? { ok: true, end: i + 1, parts } : { ok: false };
1851
+ }
1852
+ if (expectKey) {
1853
+ if (t.type === "ident" || t.type === "str" || t.type === "num") {
1854
+ key = t.value;
1855
+ } else {
1856
+ return { ok: false };
1857
+ }
1858
+ i++;
1859
+ const colon = tokens[i];
1860
+ if (colon?.type !== "punct" || colon.value !== ":")
1861
+ return { ok: false };
1862
+ i++;
1863
+ expectKey = false;
1864
+ continue;
1865
+ }
1866
+ if (paramName !== null && t.type === "ident" && t.value === paramName) {
1867
+ const dot1 = tokens[i + 1];
1868
+ const pIdent = tokens[i + 2];
1869
+ const dot2 = tokens[i + 3];
1870
+ const pKey = tokens[i + 4];
1871
+ if (dot1?.type === "punct" && (dot1.value === "." || dot1.value === "?.") && pIdent?.type === "ident" && pIdent.value === "params" && dot2?.type === "punct" && (dot2.value === "." || dot2.value === "?.") && pKey?.type === "ident") {
1872
+ hasParam = true;
1873
+ parts.push({ key, isParam: true, paramKey: pKey.value });
1874
+ i += 5;
1875
+ const sep = tokens[i];
1876
+ if (sep?.type === "punct" && sep.value === ",") {
1877
+ i++;
1878
+ expectKey = true;
1879
+ continue;
1880
+ }
1881
+ if (sep?.type === "punct" && sep.value === "}") {
1882
+ return { ok: true, end: i + 1, parts };
1883
+ }
1884
+ return { ok: false };
1885
+ }
1886
+ }
1887
+ if (t.type === "ident" && t.value === "params") {
1888
+ const dot = tokens[i + 1];
1889
+ const pKey = tokens[i + 2];
1890
+ if (dot?.type === "punct" && (dot.value === "." || dot.value === "?.") && pKey?.type === "ident") {
1891
+ hasParam = true;
1892
+ parts.push({ key, isParam: true, paramKey: pKey.value });
1893
+ i += 3;
1894
+ const sep = tokens[i];
1895
+ if (sep?.type === "punct" && sep.value === ",") {
1896
+ i++;
1897
+ expectKey = true;
1898
+ continue;
1899
+ }
1900
+ if (sep?.type === "punct" && sep.value === "}") {
1901
+ return { ok: true, end: i + 1, parts };
1902
+ }
1903
+ return { ok: false };
1904
+ }
1905
+ }
1906
+ if (t.type === "str") {
1907
+ parts.push({
1908
+ key,
1909
+ isParam: false,
1910
+ literalValue: JSON.stringify(t.value)
1911
+ });
1912
+ i++;
1913
+ } else if (t.type === "num") {
1914
+ parts.push({
1915
+ key,
1916
+ isParam: false,
1917
+ literalValue: t.value
1918
+ });
1919
+ i++;
1920
+ } else if (t.type === "ident") {
1921
+ if (t.value === "true" || t.value === "false" || t.value === "null") {
1922
+ parts.push({
1923
+ key,
1924
+ isParam: false,
1925
+ literalValue: t.value
1926
+ });
1927
+ i++;
1928
+ } else {
1929
+ return { ok: false };
1930
+ }
1931
+ } else if (t.type === "punct" && t.value === "!" && tokens[i + 1]?.type === "num") {
1932
+ parts.push({
1933
+ key,
1934
+ isParam: false,
1935
+ literalValue: tokens[i + 1].value === "1" ? "true" : "false"
1936
+ });
1937
+ i += 2;
1938
+ } else if (t.type === "punct" && t.value === "-" && tokens[i + 1]?.type === "num") {
1939
+ parts.push({
1940
+ key,
1941
+ isParam: false,
1942
+ literalValue: `-${tokens[i + 1].value}`
1943
+ });
1944
+ i += 2;
1945
+ } else {
1946
+ return { ok: false };
1947
+ }
1948
+ const sep = tokens[i];
1949
+ if (sep?.type === "punct" && sep.value === ",") {
1950
+ i++;
1951
+ expectKey = true;
1952
+ continue;
1953
+ }
1954
+ if (sep?.type === "punct" && sep.value === "}") {
1955
+ return hasParam ? { ok: true, end: i + 1, parts } : { ok: false };
1956
+ }
1957
+ return { ok: false };
1958
+ }
1959
+ return { ok: false };
1960
+ }
1961
+ function buildParamSerializer(parts) {
1962
+ const chunks = [];
1963
+ const keys = [];
1964
+ let current = "{";
1965
+ for (let i = 0;i < parts.length; i++) {
1966
+ const part = parts[i];
1967
+ if (i > 0)
1968
+ current += ",";
1969
+ current += `${JSON.stringify(part.key)}:`;
1970
+ if (part.isParam) {
1971
+ current += '"';
1972
+ chunks.push(current);
1973
+ keys.push(part.paramKey);
1974
+ current = '"';
1975
+ } else {
1976
+ current += part.literalValue;
1977
+ }
1978
+ }
1979
+ current += "}";
1980
+ chunks.push(current);
1981
+ if (keys.length === 1) {
1982
+ const c0 = chunks[0];
1983
+ const c1 = chunks[1];
1984
+ const k0 = keys[0];
1985
+ return (p) => c0 + escapeJsonString(typeof p === "string" ? p : p[k0] ?? "") + c1;
1986
+ }
1987
+ if (keys.length === 2) {
1988
+ const c0 = chunks[0];
1989
+ const c1 = chunks[1];
1990
+ const c2 = chunks[2];
1991
+ const k0 = keys[0];
1992
+ const k1 = keys[1];
1993
+ return (p) => `${c0}${escapeJsonString(typeof p === "string" ? p : p[k0] ?? "")}${c1}${typeof p === "string" ? "" : escapeJsonString(p[k1] ?? "")}${c2}`;
1994
+ }
1995
+ return (p) => {
1996
+ let out = chunks[0];
1997
+ for (let j = 0;j < keys.length; j++) {
1998
+ out += escapeJsonString(typeof p === "string" ? j === 0 ? p : "" : p[keys[j]] ?? "") + chunks[j + 1];
1999
+ }
2000
+ return out;
2001
+ };
2002
+ }
2003
+ function tryExtractParamSerializer(tokens, bodyStart, paramName) {
2004
+ const t = tokens[bodyStart];
2005
+ if (!t || t.type === "eof")
2006
+ return null;
2007
+ if (t.type === "punct" && t.value === "(") {
2008
+ const inner = tokens[bodyStart + 1];
2009
+ if (inner?.type === "punct" && inner.value === "{") {
2010
+ const res = extractParamObject(tokens, bodyStart + 1, paramName);
2011
+ if (!res.ok)
2012
+ return null;
2013
+ const afterBrace = tokens[res.end];
2014
+ if (afterBrace?.type !== "punct" || afterBrace.value !== ")")
2015
+ return null;
2016
+ if (!isBodyEnd(tokens[res.end + 1]))
2017
+ return null;
2018
+ return { serializer: buildParamSerializer(res.parts), parts: res.parts };
2019
+ }
2020
+ return null;
2021
+ }
2022
+ if (t.type === "punct" && t.value === "{") {
2023
+ const next = tokens[bodyStart + 1];
2024
+ if (next && (next.type === "ident" || next.type === "str" || next.type === "num") && tokens[bodyStart + 2]?.type === "punct" && tokens[bodyStart + 2].value === ":") {
2025
+ const res = extractParamObject(tokens, bodyStart, paramName);
2026
+ if (res.ok && isBodyEnd(tokens[res.end])) {
2027
+ return {
2028
+ serializer: buildParamSerializer(res.parts),
2029
+ parts: res.parts
2030
+ };
2031
+ }
2032
+ return null;
2033
+ }
2034
+ let j = bodyStart + 1;
2035
+ while (j < tokens.length) {
2036
+ const tt = tokens[j];
2037
+ if (tt.type === "eof")
2038
+ return null;
2039
+ if (tt.type === "punct" && tt.value === "}")
2040
+ return null;
2041
+ if (tt.type === "ident" && tt.value === "return") {
2042
+ return tryExtractParamSerializer(tokens, j + 1, paramName);
2043
+ }
2044
+ if (tt.type === "punct" && tt.value === ";")
2045
+ return null;
2046
+ if (tt.type === "ident" && tt.value !== "return")
2047
+ return null;
2048
+ j++;
2049
+ }
2050
+ return null;
2051
+ }
2052
+ return null;
2053
+ }
2054
+ function makeUnknown() {
2055
+ return {
2056
+ isNoArg: false,
2057
+ contextProps: new Set,
2058
+ hasDynamicAccess: true,
2059
+ isDestructured: false,
2060
+ destructuredProps: new Set,
2061
+ literal: null,
2062
+ arglessJsonCall: false,
2063
+ paramSerializer: null,
2064
+ paramParts: null
2065
+ };
2066
+ }
2067
+
2068
+ // src/internal/shutdown.ts
2069
+ async function stopServer(server, hardTimeoutMs) {
2070
+ let timer;
2071
+ let forced = false;
2072
+ let stopPromise = null;
2073
+ try {
2074
+ stopPromise = server.stop().then(() => {});
2075
+ await Promise.race([
2076
+ stopPromise,
2077
+ new Promise((resolve) => {
2078
+ timer = setTimeout(() => {
2079
+ forced = true;
2080
+ stopPromise = server.stop(true).then(() => {});
2081
+ resolve();
2082
+ }, hardTimeoutMs);
2083
+ timer?.unref?.();
2084
+ })
2085
+ ]);
2086
+ } finally {
2087
+ if (timer !== undefined) {
2088
+ clearTimeout(timer);
2089
+ }
2090
+ }
2091
+ await stopPromise;
2092
+ return {
2093
+ forced,
2094
+ pending: 0
2095
+ };
2096
+ }
2097
+
2098
+ // src/internal/trie.ts
2099
+ var MATCH_HANDLER = Symbol("barq.match.handler");
2100
+ var MATCH_NOT_ALLOWED = Symbol("barq.match.not-allowed");
2101
+ var METHOD_BITS = {
2102
+ GET: 1 << 0,
2103
+ POST: 1 << 1,
2104
+ PUT: 1 << 2,
2105
+ DELETE: 1 << 3,
2106
+ PATCH: 1 << 4,
2107
+ HEAD: 1 << 5,
2108
+ OPTIONS: 1 << 6
2109
+ };
2110
+ var _ALL_METHODS_BITMASK = (1 << 7) - 1;
2111
+ var STATIC_CHILD_SCAN_THRESHOLD = 4;
2112
+ function methodBit(method) {
2113
+ return METHOD_BITS[method] ?? 0;
2114
+ }
2115
+ function createNode() {
2116
+ return {
2117
+ staticChildren: null,
2118
+ staticChildKeys: [],
2119
+ staticChildNodes: [],
2120
+ paramChildren: null,
2121
+ paramChild: null,
2122
+ paramName: null,
2123
+ wildcardChild: null,
2124
+ wildcardName: null,
2125
+ handlers: null,
2126
+ allow: null,
2127
+ notAllowed: null
2128
+ };
2129
+ }
2130
+ function cloneNode(node) {
2131
+ const staticChildren = node.staticChildren ? new Map(Array.from(node.staticChildren, ([key, child]) => [
2132
+ key,
2133
+ cloneNode(child)
2134
+ ])) : null;
2135
+ const staticChildKeys = node.staticChildKeys.slice();
2136
+ const staticChildNodes = staticChildren ? staticChildKeys.map((key) => staticChildren.get(key)) : [];
2137
+ const paramChildren = node.paramChildren ? new Map(Array.from(node.paramChildren, ([name, child]) => [
2138
+ name,
2139
+ cloneNode(child)
2140
+ ])) : null;
2141
+ let handlers = null;
2142
+ if (node.handlers !== null) {
2143
+ handlers = Object.assign(Object.create(null), node.handlers);
2144
+ }
2145
+ return {
2146
+ staticChildren,
2147
+ staticChildKeys,
2148
+ staticChildNodes,
2149
+ paramChildren,
2150
+ paramChild: node.paramChild ? cloneNode(node.paramChild) : null,
2151
+ paramName: node.paramName,
2152
+ wildcardChild: node.wildcardChild ? cloneNode(node.wildcardChild) : null,
2153
+ wildcardName: node.wildcardName,
2154
+ handlers,
2155
+ allow: node.allow,
2156
+ notAllowed: node.notAllowed
2157
+ };
2158
+ }
2159
+ var MAX_OPTIONAL_PARAMS = 8;
2160
+ function parsePattern(pattern) {
2161
+ if (pattern.length === 0 || pattern.charCodeAt(0) !== 47) {
2162
+ throw new Error(`Invalid route pattern "${pattern}": must start with '/'`);
2163
+ }
2164
+ if (pattern.indexOf("#") !== -1) {
2165
+ throw new Error(`Invalid route pattern "${pattern}": must not contain '#'`);
2166
+ }
2167
+ let p = pattern;
2168
+ if (p.length > 1 && p.charCodeAt(p.length - 1) === 47) {
2169
+ p = p.slice(0, -1);
2170
+ }
2171
+ const segments = [];
2172
+ const names = new Set;
2173
+ let i = 1;
2174
+ while (i < p.length) {
2175
+ const slash = p.indexOf("/", i);
2176
+ const end = slash === -1 ? p.length : slash;
2177
+ const seg = p.slice(i, end);
2178
+ if (seg.length === 0) {
2179
+ throw new Error(`Invalid route pattern "${pattern}": empty segment ('//')`);
2180
+ }
2181
+ const c0 = seg.charCodeAt(0);
2182
+ if (seg.includes("?") && !(c0 === 58 && seg.endsWith("?") && seg.indexOf("?") === seg.length - 1)) {
2183
+ throw new Error(`Invalid route pattern "${pattern}": malformed optional parameter`);
2184
+ }
2185
+ if (c0 === 58) {
2186
+ const optional = seg.endsWith("?");
2187
+ const name = optional ? seg.slice(1, -1) : seg.slice(1);
2188
+ if (name.length === 0) {
2189
+ throw new Error(`Invalid route pattern "${pattern}": empty parameter name in segment ":${name}"`);
2190
+ }
2191
+ if (names.has(name)) {
2192
+ throw new Error(`Invalid route pattern "${pattern}": duplicate parameter name "${name}"`);
2193
+ }
2194
+ names.add(name);
2195
+ if (name.includes("?")) {
2196
+ throw new Error(`Invalid route pattern "${pattern}": malformed optional parameter`);
2197
+ }
2198
+ segments.push(optional ? { kind: "param", name, optional: true } : { kind: "param", name });
2199
+ } else if (c0 === 42) {
2200
+ if (end !== p.length) {
2201
+ throw new Error(`Invalid route pattern "${pattern}": wildcard '*' must be the final segment`);
2202
+ }
2203
+ const name = seg.length > 1 ? seg.slice(1) : null;
2204
+ if (name !== null) {
2205
+ if (names.has(name)) {
2206
+ throw new Error(`Invalid route pattern "${pattern}": duplicate parameter name "${name}"`);
2207
+ }
2208
+ names.add(name);
2209
+ }
2210
+ segments.push({ kind: "wildcard", name });
2211
+ } else {
2212
+ segments.push({ kind: "static", value: seg });
2213
+ }
2214
+ i = end + 1;
2215
+ }
2216
+ const optionalCount = segments.reduce((count, segment) => count + (segment.kind === "param" && segment.optional ? 1 : 0), 0);
2217
+ if (optionalCount > MAX_OPTIONAL_PARAMS) {
2218
+ throw new RangeError(`Route pattern exceeds the limit of ${MAX_OPTIONAL_PARAMS} optional parameters`);
2219
+ }
2220
+ return segments;
2221
+ }
2222
+ function _segmentEquals(path, start, end, key) {
2223
+ const n = end - start;
2224
+ if (key.length !== n)
2225
+ return false;
2226
+ for (let i = 0;i < n; i++) {
2227
+ if (path.charCodeAt(start + i) !== key.charCodeAt(i))
2228
+ return false;
2229
+ }
2230
+ return true;
2231
+ }
2232
+ function decodeParam(raw) {
2233
+ if (raw.indexOf("%") === -1)
2234
+ return raw;
2235
+ try {
2236
+ return decodeURIComponent(raw);
2237
+ } catch {
2238
+ throw new HttpError(400, { error: "Bad Request" });
2239
+ }
2240
+ }
2241
+ function matchNode(rootNode, pathname, startI, method, paramNames, paramValues, startDepth) {
2242
+ let node = rootNode;
2243
+ let i = startI;
2244
+ const depth = startDepth;
2245
+ while (true) {
2246
+ const len = pathname.length;
2247
+ if (i >= len) {
2248
+ const handlers = node.handlers;
2249
+ if (handlers === null)
2250
+ return null;
2251
+ let handler = handlers[method];
2252
+ if (handler === undefined && method === "HEAD") {
2253
+ handler = handlers.GET;
2254
+ }
2255
+ if (handler === undefined) {
2256
+ return node.notAllowed;
2257
+ }
2258
+ if (depth === 0)
2259
+ return handler;
2260
+ const p = Object.create(null);
2261
+ p[MATCH_HANDLER] = handler;
2262
+ for (let k = 0;k < depth; k++) {
2263
+ p[paramNames[k]] = paramValues[k];
2264
+ }
2265
+ return p;
2266
+ }
2267
+ const slash = pathname.indexOf("/", i);
2268
+ const segEnd = slash === -1 ? len : slash;
2269
+ if (segEnd === i) {
2270
+ if (slash === len - 1) {
2271
+ return matchNode(node, pathname, len, method, paramNames, paramValues, depth);
2272
+ }
2273
+ return null;
2274
+ }
2275
+ const nextI = slash === -1 ? len : slash + 1;
2276
+ let notAllowedCandidate = null;
2277
+ const staticChildren = node.staticChildren;
2278
+ if (staticChildren !== null) {
2279
+ let child;
2280
+ if (staticChildren.size <= STATIC_CHILD_SCAN_THRESHOLD) {
2281
+ const keys = node.staticChildKeys;
2282
+ const nodes = node.staticChildNodes;
2283
+ for (let childIndex = 0;childIndex < keys.length; childIndex++) {
2284
+ if (_segmentEquals(pathname, i, segEnd, keys[childIndex])) {
2285
+ child = nodes[childIndex];
2286
+ break;
2287
+ }
2288
+ }
2289
+ } else {
2290
+ child = staticChildren.get(pathname.slice(i, segEnd));
2291
+ }
2292
+ if (child !== undefined) {
2293
+ const hasParamChildren = node.paramChildren !== null && node.paramChildren.size > 0;
2294
+ if (!hasParamChildren && node.wildcardChild === null) {
2295
+ node = child;
2296
+ i = nextI;
2297
+ continue;
2298
+ }
2299
+ const res = matchNode(child, pathname, nextI, method, paramNames, paramValues, depth);
2300
+ if (res !== null) {
2301
+ if (MATCH_NOT_ALLOWED in res) {
2302
+ notAllowedCandidate = res;
2303
+ } else {
2304
+ return res;
2305
+ }
2306
+ }
2307
+ }
2308
+ }
2309
+ const paramChildren = node.paramChildren ?? (node.paramChild !== null && node.paramName !== null ? new Map([[node.paramName, node.paramChild]]) : null);
2310
+ if (paramChildren !== null && paramChildren.size > 0) {
2311
+ for (const [paramName, child] of paramChildren) {
2312
+ if (nextI >= len && child.handlers !== null) {
2313
+ let leafH = child.handlers[method];
2314
+ if (leafH === undefined && method === "HEAD")
2315
+ leafH = child.handlers.GET;
2316
+ if (leafH !== undefined) {
2317
+ const paramVal = pathname.indexOf("%", i) !== -1 ? decodeParam(pathname.slice(i, segEnd)) : pathname.slice(i, segEnd);
2318
+ const p = {
2319
+ [paramName]: paramVal,
2320
+ [MATCH_HANDLER]: leafH
2321
+ };
2322
+ if (depth === 0)
2323
+ return p;
2324
+ for (let k = 0;k < depth; k++) {
2325
+ p[paramNames[k]] = paramValues[k];
2326
+ }
2327
+ return p;
2328
+ }
2329
+ }
2330
+ paramNames[depth] = paramName;
2331
+ paramValues[depth] = decodeParam(pathname.slice(i, segEnd));
2332
+ const res = matchNode(child, pathname, nextI, method, paramNames, paramValues, depth + 1);
2333
+ if (res !== null) {
2334
+ if (MATCH_NOT_ALLOWED in res) {
2335
+ notAllowedCandidate ??= res;
2336
+ } else {
2337
+ return res;
2338
+ }
2339
+ }
2340
+ }
2341
+ }
2342
+ if (node.wildcardChild !== null) {
2343
+ let wildcardDepth = depth;
2344
+ if (node.wildcardName !== null) {
2345
+ let rest = pathname.slice(i);
2346
+ if (rest.length > 1 && rest.charCodeAt(rest.length - 1) === 47) {
2347
+ rest = rest.slice(0, -1);
2348
+ }
2349
+ paramNames[wildcardDepth] = node.wildcardName;
2350
+ paramValues[wildcardDepth] = decodeParam(rest);
2351
+ wildcardDepth++;
2352
+ }
2353
+ const handlers = node.wildcardChild.handlers;
2354
+ if (handlers !== null) {
2355
+ let handler = handlers[method];
2356
+ if (handler === undefined && method === "HEAD") {
2357
+ handler = handlers.GET;
2358
+ }
2359
+ if (handler === undefined) {
2360
+ return node.wildcardChild.notAllowed;
2361
+ }
2362
+ if (wildcardDepth === 0)
2363
+ return handler;
2364
+ const p = Object.create(null);
2365
+ for (let k = 0;k < wildcardDepth; k++) {
2366
+ p[paramNames[k]] = paramValues[k];
2367
+ }
2368
+ p[MATCH_HANDLER] = handler;
2369
+ return p;
2370
+ }
2371
+ }
2372
+ return notAllowedCandidate;
2373
+ }
2374
+ }
2375
+
2376
+ class SegmentTrie {
2377
+ root = createNode();
2378
+ staticRoutes = new Map;
2379
+ staticNotAllowed = new Map;
2380
+ maxStaticLength = 0;
2381
+ paramNameBuffer = [];
2382
+ paramValueBuffer = [];
2383
+ addBatch(routes) {
2384
+ if (routes.length === 0)
2385
+ return;
2386
+ if (routes.length === 1) {
2387
+ const route = routes[0];
2388
+ this.add(route.method, route.pattern, route.handler);
2389
+ return;
2390
+ }
2391
+ const staged = this.clone();
2392
+ const parsedPatterns = new Map;
2393
+ for (const route of routes) {
2394
+ let segments = parsedPatterns.get(route.pattern);
2395
+ if (segments === undefined) {
2396
+ segments = parsePattern(route.pattern);
2397
+ parsedPatterns.set(route.pattern, segments);
2398
+ }
2399
+ staged.addWithSegments(route.method, route.pattern, segments, route.handler);
2400
+ }
2401
+ this.root = staged.root;
2402
+ this.staticRoutes = staged.staticRoutes;
2403
+ this.staticNotAllowed = staged.staticNotAllowed;
2404
+ this.maxStaticLength = staged.maxStaticLength;
2405
+ }
2406
+ clone() {
2407
+ const copy = new SegmentTrie;
2408
+ copy.root = cloneNode(this.root);
2409
+ copy.staticRoutes = new Map(Array.from(this.staticRoutes, ([path, handlers]) => [
2410
+ path,
2411
+ Object.assign(Object.create(null), handlers)
2412
+ ]));
2413
+ copy.staticNotAllowed = new Map(this.staticNotAllowed);
2414
+ copy.maxStaticLength = this.maxStaticLength;
2415
+ return copy;
2416
+ }
2417
+ add(method, pattern, handler) {
2418
+ const segments = parsePattern(pattern);
2419
+ let optionalCount = 0;
2420
+ for (const segment of segments) {
2421
+ if (segment.kind === "param" && segment.optional)
2422
+ optionalCount++;
2423
+ }
2424
+ if (optionalCount === 0) {
2425
+ this.addParsed(method, pattern, segments, handler, false);
2426
+ return;
2427
+ }
2428
+ const staged = this.clone();
2429
+ staged.addWithSegments(method, pattern, segments, handler);
2430
+ this.root = staged.root;
2431
+ this.staticRoutes = staged.staticRoutes;
2432
+ this.staticNotAllowed = staged.staticNotAllowed;
2433
+ }
2434
+ addWithSegments(method, pattern, segments, handler) {
2435
+ let optionalCount = 0;
2436
+ for (const segment of segments) {
2437
+ if (segment.kind === "param" && segment.optional)
2438
+ optionalCount++;
2439
+ }
2440
+ if (optionalCount === 0) {
2441
+ this.addParsed(method, pattern, segments, handler, false);
2442
+ return;
2443
+ }
2444
+ const selected = [];
2445
+ const addVariants = (index) => {
2446
+ if (index === segments.length) {
2447
+ const variant = selected.length === 0 ? "/" : `/${selected.map((segment) => segment.kind === "static" ? segment.value : segment.kind === "param" ? `:${segment.name}` : `*${segment.name ?? ""}`).join("/")}`;
2448
+ this.addParsed(method, variant, selected, handler, true);
2449
+ return;
2450
+ }
2451
+ const segment = segments[index];
2452
+ if (segment.kind === "param" && segment.optional) {
2453
+ selected.push({ kind: "param", name: segment.name });
2454
+ addVariants(index + 1);
2455
+ selected.pop();
2456
+ addVariants(index + 1);
2457
+ return;
2458
+ }
2459
+ selected.push(segment);
2460
+ addVariants(index + 1);
2461
+ selected.pop();
2462
+ };
2463
+ addVariants(0);
2464
+ }
2465
+ addParsed(method, pattern, segments, handler, _allowConflictingParamNames) {
2466
+ const bit = methodBit(method);
2467
+ if (bit === 0) {
2468
+ throw new Error(`Unsupported HTTP method "${method}" for route "${pattern}"`);
2469
+ }
2470
+ const isPureStatic = segments.every((s) => s.kind === "static");
2471
+ let cleanPattern;
2472
+ if (isPureStatic) {
2473
+ cleanPattern = pattern.length > 1 && pattern.charCodeAt(pattern.length - 1) === 47 ? pattern.slice(0, -1) : pattern;
2474
+ if (cleanPattern.length > this.maxStaticLength) {
2475
+ this.maxStaticLength = cleanPattern.length;
2476
+ }
2477
+ }
2478
+ for (const seg of segments) {
2479
+ if (seg.kind === "static") {} else if (seg.kind === "param") {
2480
+ if (isPureStatic) {
2481
+ throw new Error(`Internal error: param segment in pure static route`);
2482
+ }
2483
+ } else {
2484
+ if (isPureStatic) {
2485
+ throw new Error(`Internal error: wildcard segment in pure static route`);
2486
+ }
2487
+ }
2488
+ }
2489
+ let node = this.root;
2490
+ for (const seg of segments) {
2491
+ if (seg.kind === "static") {
2492
+ if (node.staticChildren === null) {
2493
+ node.staticChildren = new Map;
2494
+ }
2495
+ let child = node.staticChildren.get(seg.value);
2496
+ if (child === undefined) {
2497
+ child = createNode();
2498
+ node.staticChildren.set(seg.value, child);
2499
+ node.staticChildKeys.push(seg.value);
2500
+ node.staticChildNodes.push(child);
2501
+ }
2502
+ node = child;
2503
+ } else if (seg.kind === "param") {
2504
+ if (node.wildcardChild !== null) {
2505
+ throw new Error(`Ambiguous routes: wildcard "${node.wildcardName ?? "*"}" and param ":${seg.name}" compete at the same position in "${pattern}"`);
2506
+ }
2507
+ if (node.paramChildren === null) {
2508
+ node.paramChildren = new Map;
2509
+ }
2510
+ let child = node.paramChildren.get(seg.name);
2511
+ if (child === undefined) {
2512
+ child = createNode();
2513
+ node.paramChildren.set(seg.name, child);
2514
+ if (node.paramChild === null && node.paramChildren.size === 1) {
2515
+ node.paramChild = child;
2516
+ node.paramName = seg.name;
2517
+ } else {
2518
+ node.paramChild = null;
2519
+ node.paramName = null;
2520
+ }
2521
+ }
2522
+ node = child;
2523
+ } else {
2524
+ if (node.wildcardChild !== null) {
2525
+ if (node.wildcardName !== seg.name) {
2526
+ throw new Error(`Conflicting wildcards at the same position: "*${node.wildcardName ?? ""}" vs "*${seg.name ?? ""}" (route "${pattern}")`);
2527
+ }
2528
+ } else {
2529
+ if (node.paramChildren !== null && node.paramChildren.size > 0) {
2530
+ const existingParam = node.paramChildren.keys().next().value;
2531
+ if (existingParam !== undefined) {
2532
+ throw new Error(`Ambiguous routes: param ":${existingParam}" and wildcard "*${seg.name ?? ""}" compete at the same position in "${pattern}"`);
2533
+ }
2534
+ }
2535
+ node.wildcardChild = createNode();
2536
+ node.wildcardName = seg.name;
2537
+ }
2538
+ node = node.wildcardChild;
2539
+ }
2540
+ }
2541
+ if (node.handlers?.[method] !== undefined) {
2542
+ throw new Error(`Duplicate route: ${method} ${pattern} is already registered`);
2543
+ }
2544
+ if (node.handlers === null)
2545
+ node.handlers = Object.create(null);
2546
+ node.handlers[method] = handler;
2547
+ node.allow = SegmentTrie.computeAllow(node.handlers);
2548
+ node.notAllowed = Object.freeze({
2549
+ [MATCH_NOT_ALLOWED]: node.allow
2550
+ });
2551
+ if (isPureStatic) {
2552
+ let sHandlers = this.staticRoutes.get(cleanPattern);
2553
+ if (sHandlers === undefined) {
2554
+ sHandlers = Object.create(null);
2555
+ this.staticRoutes.set(cleanPattern, sHandlers);
2556
+ }
2557
+ sHandlers[method] = handler;
2558
+ const sAllow = SegmentTrie.computeAllow(sHandlers);
2559
+ this.staticNotAllowed.set(cleanPattern, Object.freeze({ [MATCH_NOT_ALLOWED]: sAllow }));
2560
+ }
2561
+ }
2562
+ static computeAllow(handlers) {
2563
+ const methods = Object.keys(handlers);
2564
+ if (methods.includes("GET") && !methods.includes("HEAD")) {
2565
+ return [...methods, "HEAD"].join(", ");
2566
+ }
2567
+ return methods.join(", ");
2568
+ }
2569
+ match(method, pathname) {
2570
+ const len = pathname.length;
2571
+ const cleanPath = len > 1 && pathname.charCodeAt(len - 1) === 47 ? pathname.slice(0, -1) : pathname;
2572
+ if (cleanPath.length <= this.maxStaticLength) {
2573
+ const sHandlers = this.staticRoutes.get(cleanPath);
2574
+ if (sHandlers !== undefined) {
2575
+ let handler = sHandlers[method];
2576
+ if (handler === undefined && method === "HEAD") {
2577
+ handler = sHandlers.GET;
2578
+ }
2579
+ if (handler !== undefined) {
2580
+ return handler;
2581
+ }
2582
+ const dynamicMatch = matchNode(this.root, pathname, 1, method, this.paramNameBuffer, this.paramValueBuffer, 0);
2583
+ if (this.paramNameBuffer.length > 0) {
2584
+ this.paramNameBuffer.length = 0;
2585
+ this.paramValueBuffer.length = 0;
2586
+ }
2587
+ if (dynamicMatch !== null && !(MATCH_NOT_ALLOWED in dynamicMatch)) {
2588
+ return dynamicMatch;
2589
+ }
2590
+ return this.staticNotAllowed.get(cleanPath);
2591
+ }
2592
+ }
2593
+ const res = matchNode(this.root, pathname, 1, method, this.paramNameBuffer, this.paramValueBuffer, 0);
2594
+ if (this.paramNameBuffer.length > 0) {
2595
+ this.paramNameBuffer.length = 0;
2596
+ this.paramValueBuffer.length = 0;
2597
+ }
2598
+ return res;
2599
+ }
2600
+ }
2601
+
2602
+ // src/middleware.ts
2603
+ function isThenable(value) {
2604
+ return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.then === "function";
2605
+ }
2606
+ function mergeState(ctx, value) {
2607
+ if (typeof value === "object" && value !== null) {
2608
+ Object.assign(ctx.state, value);
2609
+ }
2610
+ }
2611
+ function composeMiddlewareDispatcher(mws, errorResolver) {
2612
+ if (mws.length > 64 || mws.some((middleware) => middleware.after !== undefined)) {
2613
+ return (ctx, handler) => runPipeline(mws, ctx, handler, errorResolver);
2614
+ }
2615
+ let next = (ctx, handler) => {
2616
+ let result;
2617
+ try {
2618
+ result = handler(ctx);
2619
+ } catch (error) {
2620
+ return handlePipelineError(mws, ctx, error, mws.length, errorResolver);
2621
+ }
2622
+ if (isThenable(result)) {
2623
+ return Promise.resolve(result).catch((error) => handlePipelineError(mws, ctx, error, mws.length, errorResolver));
2624
+ }
2625
+ return result;
2626
+ };
2627
+ for (let i = mws.length - 1;i >= 0; i--) {
2628
+ const middleware = mws[i];
2629
+ const hook = middleware.before;
2630
+ const following = next;
2631
+ next = (ctx, handler) => {
2632
+ if (!hook)
2633
+ return following(ctx, handler);
2634
+ let result;
2635
+ try {
2636
+ result = hook(ctx);
2637
+ } catch (error) {
2638
+ return handlePipelineError(mws, ctx, error, i, errorResolver);
2639
+ }
2640
+ if (result === undefined)
2641
+ return following(ctx, handler);
2642
+ if (isThenable(result)) {
2643
+ return Promise.resolve(result).then((value) => {
2644
+ if (value instanceof Response)
2645
+ return value;
2646
+ mergeState(ctx, value);
2647
+ return following(ctx, handler);
2648
+ }, (error) => handlePipelineError(mws, ctx, error, i, errorResolver));
2649
+ }
2650
+ if (result instanceof Response)
2651
+ return result;
2652
+ mergeState(ctx, result);
2653
+ return following(ctx, handler);
2654
+ };
2655
+ }
2656
+ return next;
2657
+ }
2658
+ function composeMiddlewares(mws, handler, errorResolver) {
2659
+ const dispatch = composeMiddlewareDispatcher(mws, errorResolver);
2660
+ return (ctx) => dispatch(ctx, handler);
2661
+ }
2662
+ function runPipeline(mws, ctx, handler, errorResolver) {
2663
+ let i = 0;
2664
+ const len = mws.length;
2665
+ while (i < len) {
2666
+ const hook = mws[i].before;
2667
+ if (hook) {
2668
+ let r;
2669
+ try {
2670
+ r = hook(ctx);
2671
+ } catch (err) {
2672
+ return handlePipelineError(mws, ctx, err, i, errorResolver);
2673
+ }
2674
+ if (r !== undefined) {
2675
+ if (r instanceof Response) {
2676
+ return unwindAfter(mws, ctx, r, i + 1, errorResolver);
2677
+ }
2678
+ if (isThenable(r)) {
2679
+ return resumeBefore(mws, ctx, handler, r, i, errorResolver);
2680
+ }
2681
+ mergeState(ctx, r);
2682
+ }
2683
+ }
2684
+ i++;
2685
+ }
2686
+ return invokeHandler(mws, ctx, handler, len, errorResolver);
2687
+ }
2688
+ function continueBefore(mws, ctx, handler, startIndex, errorResolver) {
2689
+ let i = startIndex;
2690
+ const len = mws.length;
2691
+ while (i < len) {
2692
+ const hook = mws[i].before;
2693
+ if (hook) {
2694
+ let r;
2695
+ try {
2696
+ r = hook(ctx);
2697
+ } catch (err) {
2698
+ return handlePipelineError(mws, ctx, err, i, errorResolver);
2699
+ }
2700
+ if (r !== undefined) {
2701
+ if (r instanceof Response) {
2702
+ return unwindAfter(mws, ctx, r, i + 1, errorResolver);
2703
+ }
2704
+ if (isThenable(r)) {
2705
+ return resumeBefore(mws, ctx, handler, r, i, errorResolver);
2706
+ }
2707
+ mergeState(ctx, r);
2708
+ }
2709
+ }
2710
+ i++;
2711
+ }
2712
+ return invokeHandler(mws, ctx, handler, len, errorResolver);
2713
+ }
2714
+ function invokeHandler(mws, ctx, handler, entered, errorResolver) {
2715
+ let res;
2716
+ try {
2717
+ res = handler(ctx);
2718
+ } catch (err) {
2719
+ return handlePipelineError(mws, ctx, err, entered, errorResolver);
2720
+ }
2721
+ if (isThenable(res)) {
2722
+ return resumeHandler(mws, ctx, res, entered, errorResolver);
2723
+ }
2724
+ return unwindAfter(mws, ctx, res, entered, errorResolver);
2725
+ }
2726
+ function resumeBefore(mws, ctx, handler, promise, index, errorResolver) {
2727
+ return Promise.resolve(promise).then((r) => {
2728
+ if (r instanceof Response) {
2729
+ return unwindAfter(mws, ctx, r, index + 1, errorResolver);
2730
+ }
2731
+ mergeState(ctx, r);
2732
+ return continueBefore(mws, ctx, handler, index + 1, errorResolver);
2733
+ }, (err) => handlePipelineError(mws, ctx, err, index, errorResolver));
2734
+ }
2735
+ function resumeHandler(mws, ctx, promise, entered, errorResolver) {
2736
+ return Promise.resolve(promise).then((res) => unwindAfter(mws, ctx, res, entered, errorResolver), (err) => handlePipelineError(mws, ctx, err, entered, errorResolver));
2737
+ }
2738
+ function handlePipelineError(mws, ctx, err, entered, errorResolver) {
2739
+ if (errorResolver !== undefined) {
2740
+ let res;
2741
+ try {
2742
+ res = errorResolver(err, ctx);
2743
+ } catch (resolverErr) {
2744
+ const fallback = errorToResponse(resolverErr, ctx.development);
2745
+ return unwindAfter(mws, ctx, fallback, entered, errorResolver);
2746
+ }
2747
+ if (isThenable(res)) {
2748
+ return res.then((resolved) => unwindAfter(mws, ctx, resolved, entered, errorResolver), (resolverErr) => {
2749
+ const fallback = errorToResponse(resolverErr, ctx.development);
2750
+ return unwindAfter(mws, ctx, fallback, entered, errorResolver);
2751
+ });
2752
+ }
2753
+ return unwindAfter(mws, ctx, res, entered, errorResolver);
2754
+ }
2755
+ const res = errorToResponse(err, ctx.development);
2756
+ return unwindAfter(mws, ctx, res, entered, errorResolver);
2757
+ }
2758
+ function unwindAfter(mws, ctx, res, enteredCount, errorResolver) {
2759
+ let currentRes = res;
2760
+ for (let j = enteredCount - 1;j >= 0; j--) {
2761
+ const hook = mws[j].after;
2762
+ if (hook) {
2763
+ let r;
2764
+ try {
2765
+ r = hook(ctx, currentRes);
2766
+ } catch (err) {
2767
+ if (errorResolver !== undefined) {
2768
+ try {
2769
+ const resolved = errorResolver(err, ctx);
2770
+ if (isThenable(resolved)) {
2771
+ return resolved.then((resolvedRes) => continueUnwind(mws, ctx, j - 1, resolvedRes, errorResolver), (resolverErr) => continueUnwind(mws, ctx, j - 1, errorToResponse(resolverErr, ctx.development), errorResolver));
2772
+ }
2773
+ currentRes = resolved;
2774
+ continue;
2775
+ } catch (resolverErr) {
2776
+ currentRes = errorToResponse(resolverErr, ctx.development);
2777
+ continue;
2778
+ }
2779
+ }
2780
+ currentRes = errorToResponse(err, ctx.development);
2781
+ continue;
2782
+ }
2783
+ if (r !== undefined) {
2784
+ if (isThenable(r)) {
2785
+ return resumeUnwind(mws, ctx, r, j - 1, currentRes, errorResolver);
2786
+ }
2787
+ if (r instanceof Response) {
2788
+ currentRes = r;
2789
+ }
2790
+ }
2791
+ }
2792
+ }
2793
+ return currentRes;
2794
+ }
2795
+ function resumeUnwind(mws, ctx, promise, nextIndex, currentRes, errorResolver) {
2796
+ return Promise.resolve(promise).then((r) => {
2797
+ const next = r instanceof Response ? r : currentRes;
2798
+ return continueUnwind(mws, ctx, nextIndex, next, errorResolver);
2799
+ }, (err) => {
2800
+ if (errorResolver !== undefined) {
2801
+ try {
2802
+ const resolved = errorResolver(err, ctx);
2803
+ if (isThenable(resolved)) {
2804
+ return resolved.then((resolvedRes) => continueUnwind(mws, ctx, nextIndex, resolvedRes, errorResolver), (resolverErr) => continueUnwind(mws, ctx, nextIndex, errorToResponse(resolverErr, ctx.development), errorResolver));
2805
+ }
2806
+ return continueUnwind(mws, ctx, nextIndex, resolved, errorResolver);
2807
+ } catch (resolverErr) {
2808
+ return continueUnwind(mws, ctx, nextIndex, errorToResponse(resolverErr, ctx.development), errorResolver);
2809
+ }
2810
+ }
2811
+ const next = errorToResponse(err, ctx.development);
2812
+ return continueUnwind(mws, ctx, nextIndex, next, errorResolver);
2813
+ });
2814
+ }
2815
+ function continueUnwind(mws, ctx, startIndex, currentRes, errorResolver) {
2816
+ let cur = currentRes;
2817
+ for (let j = startIndex;j >= 0; j--) {
2818
+ const hook = mws[j].after;
2819
+ if (hook) {
2820
+ let r;
2821
+ try {
2822
+ r = hook(ctx, cur);
2823
+ } catch (err) {
2824
+ if (errorResolver !== undefined) {
2825
+ try {
2826
+ const resolved = errorResolver(err, ctx);
2827
+ if (isThenable(resolved)) {
2828
+ return resolved.then((resolvedRes) => continueUnwind(mws, ctx, j - 1, resolvedRes, errorResolver), (resolverErr) => continueUnwind(mws, ctx, j - 1, errorToResponse(resolverErr, ctx.development), errorResolver));
2829
+ }
2830
+ cur = resolved;
2831
+ continue;
2832
+ } catch (resolverErr) {
2833
+ cur = errorToResponse(resolverErr, ctx.development);
2834
+ continue;
2835
+ }
2836
+ }
2837
+ cur = errorToResponse(err, ctx.development);
2838
+ continue;
2839
+ }
2840
+ if (r !== undefined) {
2841
+ if (isThenable(r)) {
2842
+ return resumeUnwind(mws, ctx, r, j - 1, cur, errorResolver);
2843
+ }
2844
+ if (r instanceof Response) {
2845
+ cur = r;
2846
+ }
2847
+ }
2848
+ }
2849
+ }
2850
+ return cur;
2851
+ }
2852
+
2853
+ // src/app.ts
2854
+ var METHODS = [
2855
+ "GET",
2856
+ "POST",
2857
+ "PUT",
2858
+ "DELETE",
2859
+ "PATCH",
2860
+ "HEAD",
2861
+ "OPTIONS"
2862
+ ];
2863
+ var MAX_ROUTE_SEGMENTS = 256;
2864
+ var EMPTY_PARAMS = Object.freeze(Object.create(null));
2865
+ var FAST_JSON_HEADERS = new Headers({
2866
+ "content-type": "application/json; charset=utf-8"
2867
+ });
2868
+ var FAST_TEXT_HEADERS = new Headers({
2869
+ "content-type": "text/plain; charset=utf-8"
2870
+ });
2871
+ var _FAST_JSON_INIT = Object.freeze({
2872
+ headers: FAST_JSON_HEADERS
2873
+ });
2874
+ var _FAST_TEXT_INIT = Object.freeze({
2875
+ headers: FAST_TEXT_HEADERS
2876
+ });
2877
+
2878
+ class ReadonlySetView {
2879
+ source;
2880
+ constructor(source) {
2881
+ this.source = source;
2882
+ }
2883
+ get size() {
2884
+ return this.source.size;
2885
+ }
2886
+ has(value) {
2887
+ return this.source.has(value);
2888
+ }
2889
+ forEach(callback, thisArg) {
2890
+ this.source.forEach((value) => {
2891
+ callback.call(thisArg, value, value, this);
2892
+ });
2893
+ }
2894
+ entries() {
2895
+ return this.source.entries();
2896
+ }
2897
+ keys() {
2898
+ return this.source.keys();
2899
+ }
2900
+ values() {
2901
+ return this.source.values();
2902
+ }
2903
+ [Symbol.iterator]() {
2904
+ return this.source[Symbol.iterator]();
2905
+ }
2906
+ get [Symbol.toStringTag]() {
2907
+ return "Set";
2908
+ }
2909
+ }
2910
+ function createReadonlySet(source) {
2911
+ return new ReadonlySetView(source);
2912
+ }
2913
+ var EMPTY_REDIRECT_HOSTS = createReadonlySet(new Set);
2914
+ function expandOptionalPatterns(pattern) {
2915
+ if (!pattern.includes("?"))
2916
+ return [pattern];
2917
+ const segments = pattern.split("/").filter(Boolean);
2918
+ const optIndices = [];
2919
+ for (let i = 0;i < segments.length; i++) {
2920
+ const seg = segments[i];
2921
+ if (seg.startsWith(":") && seg.endsWith("?")) {
2922
+ optIndices.push(i);
2923
+ }
2924
+ }
2925
+ if (optIndices.length === 0)
2926
+ return [pattern];
2927
+ const variants = [];
2928
+ const count = 1 << optIndices.length;
2929
+ for (let mask = 0;mask < count; mask++) {
2930
+ const parts = [];
2931
+ for (let i = 0;i < segments.length; i++) {
2932
+ const optPos = optIndices.indexOf(i);
2933
+ if (optPos >= 0) {
2934
+ if (mask & 1 << optPos) {
2935
+ parts.push(segments[i].slice(0, -1));
2936
+ }
2937
+ } else {
2938
+ parts.push(segments[i]);
2939
+ }
2940
+ }
2941
+ variants.push(parts.length === 0 ? "/" : `/${parts.join("/")}`);
2942
+ }
2943
+ return variants;
2944
+ }
2945
+ var LIGHT_SUPPORTED_PROPS = new Set([
2946
+ "req",
2947
+ "params",
2948
+ "pathname",
2949
+ "queryParam",
2950
+ "queryParamAll",
2951
+ "state",
2952
+ "stream",
2953
+ "bodyStream"
2954
+ ]);
2955
+ function classifyProps(props) {
2956
+ for (const p of props) {
2957
+ if (!LIGHT_SUPPORTED_PROPS.has(p)) {
2958
+ return {
2959
+ isContextFree: false,
2960
+ isLightweight: false,
2961
+ isParamsOnly: false,
2962
+ isJsonBodyOnly: false
2963
+ };
2964
+ }
2965
+ }
2966
+ const hasParams = props.has("params") || props.size === 0;
2967
+ const hasQuery = props.has("queryParam") || props.has("queryParamAll");
2968
+ if (hasParams && props.size === 1) {
2969
+ return {
2970
+ isContextFree: false,
2971
+ isLightweight: true,
2972
+ isParamsOnly: true,
2973
+ isJsonBodyOnly: false
2974
+ };
2975
+ }
2976
+ if (hasParams || hasQuery || props.size > 0) {
2977
+ return {
2978
+ isContextFree: false,
2979
+ isLightweight: true,
2980
+ isParamsOnly: false,
2981
+ isJsonBodyOnly: false
2982
+ };
2983
+ }
2984
+ return {
2985
+ isContextFree: false,
2986
+ isLightweight: false,
2987
+ isParamsOnly: false,
2988
+ isJsonBodyOnly: false
2989
+ };
2990
+ }
2991
+ function classify(analysis) {
2992
+ if (analysis.isNoArg) {
2993
+ return {
2994
+ isContextFree: true,
2995
+ isLightweight: false,
2996
+ isParamsOnly: false,
2997
+ isJsonBodyOnly: false
2998
+ };
2999
+ }
3000
+ if (analysis.arglessJsonCall && !analysis.isDestructured && analysis.contextProps.size === 1 && analysis.contextProps.has("json")) {
3001
+ return {
3002
+ isContextFree: false,
3003
+ isLightweight: false,
3004
+ isParamsOnly: false,
3005
+ isJsonBodyOnly: true
3006
+ };
3007
+ }
3008
+ if (analysis.isDestructured) {
3009
+ return classifyProps(analysis.destructuredProps);
3010
+ }
3011
+ if (analysis.hasDynamicAccess) {
3012
+ return {
3013
+ isContextFree: false,
3014
+ isLightweight: false,
3015
+ isParamsOnly: false,
3016
+ isJsonBodyOnly: false
3017
+ };
3018
+ }
3019
+ return classifyProps(analysis.contextProps);
3020
+ }
3021
+ function detectHandlerNeeds(fn) {
3022
+ try {
3023
+ return classify(analyzeHandler(fn));
3024
+ } catch {
3025
+ return {
3026
+ isContextFree: false,
3027
+ isLightweight: false,
3028
+ isParamsOnly: false,
3029
+ isJsonBodyOnly: false
3030
+ };
3031
+ }
3032
+ }
3033
+
3034
+ class FastLightContext {
3035
+ req;
3036
+ params;
3037
+ _pathname;
3038
+ _state;
3039
+ _hasPendingCookies = false;
3040
+ _url;
3041
+ _qPos = -2;
3042
+ constructor(req, params) {
3043
+ this.req = req;
3044
+ this.params = params;
3045
+ }
3046
+ init(req, params) {
3047
+ this.req = req;
3048
+ this.params = params;
3049
+ this._pathname = undefined;
3050
+ this._state = undefined;
3051
+ this._hasPendingCookies = false;
3052
+ this._url = undefined;
3053
+ this._qPos = -2;
3054
+ return this;
3055
+ }
3056
+ get url() {
3057
+ return this._url ?? (this._url = this.req.url);
3058
+ }
3059
+ get pathname() {
3060
+ if (this._pathname !== undefined)
3061
+ return this._pathname;
3062
+ return this._pathname = scanPathname(this.url);
3063
+ }
3064
+ queryParam(name) {
3065
+ let u = this._url;
3066
+ if (u === undefined) {
3067
+ u = this.req.url;
3068
+ this._url = u;
3069
+ }
3070
+ let q = this._qPos;
3071
+ if (q === -2)
3072
+ this._qPos = q = u.indexOf("?");
3073
+ return findQueryParam(u, name, q);
3074
+ }
3075
+ queryParamAll(name) {
3076
+ return findQueryParamAll(this.url, name);
3077
+ }
3078
+ get cookies() {
3079
+ throw new Error("LightweightContext does not support cookies.");
3080
+ }
3081
+ get state() {
3082
+ let state = this._state;
3083
+ if (state === undefined) {
3084
+ state = Object.create(null);
3085
+ this._state = state;
3086
+ }
3087
+ return state;
3088
+ }
3089
+ stream() {
3090
+ return this.req.body;
3091
+ }
3092
+ get bodyStream() {
3093
+ return this.req.body;
3094
+ }
3095
+ }
3096
+ function reportWebSocketError(handler, ws, error) {
3097
+ if (!handler) {
3098
+ console.error("Unhandled WebSocket callback error:", error);
3099
+ return;
3100
+ }
3101
+ try {
3102
+ const result = handler(ws, error);
3103
+ if (isThenable(result)) {
3104
+ Promise.resolve(result).catch((handlerError) => console.error("WebSocket error handler failed:", handlerError));
3105
+ }
3106
+ } catch (handlerError) {
3107
+ console.error("WebSocket error handler failed:", handlerError);
3108
+ }
3109
+ }
3110
+ function dispatchWebSocketCallback(callback, args, onError) {
3111
+ if (!callback)
3112
+ return;
3113
+ try {
3114
+ const len = args.length;
3115
+ let result;
3116
+ if (len === 1) {
3117
+ result = callback(args[0]);
3118
+ } else if (len === 2) {
3119
+ result = callback(args[0], args[1]);
3120
+ } else if (len === 3) {
3121
+ result = callback(args[0], args[1], args[2]);
3122
+ } else {
3123
+ result = Reflect.apply(callback, undefined, args);
3124
+ }
3125
+ if (isThenable(result)) {
3126
+ Promise.resolve(result).catch(onError);
3127
+ }
3128
+ } catch (error) {
3129
+ onError(error);
3130
+ }
3131
+ }
3132
+ function getInternalWebSocketHandlers(ws) {
3133
+ const data = ws.data;
3134
+ return data?._barqInternal === true ? data.handlers : undefined;
3135
+ }
3136
+ function parseRouteArgs(args) {
3137
+ if (args.length === 0) {
3138
+ throw new Error("Route requires a handler function");
3139
+ }
3140
+ const last = args[args.length - 1];
3141
+ if (typeof last !== "function") {
3142
+ throw new Error("The final argument in route definition must be a Handler function");
3143
+ }
3144
+ const handler = last;
3145
+ if (args.length === 1) {
3146
+ return { middlewares: [], handler };
3147
+ }
3148
+ const middlewares = [];
3149
+ for (let i = 0;i < args.length - 1; i++) {
3150
+ const item = args[i];
3151
+ if (Array.isArray(item)) {
3152
+ for (let j = 0;j < item.length; j++) {
3153
+ middlewares.push(item[j]);
3154
+ }
3155
+ } else if (item && typeof item === "object") {
3156
+ middlewares.push(item);
3157
+ } else {
3158
+ throw new TypeError("Route middleware must be a Middleware object or array of Middleware");
3159
+ }
3160
+ }
3161
+ return { middlewares, handler };
3162
+ }
3163
+ function normalizePrefix(prefix) {
3164
+ let p = prefix.trim();
3165
+ if (p.length === 0 || p === "/")
3166
+ return "";
3167
+ if (p.charCodeAt(0) !== 47)
3168
+ p = `/${p}`;
3169
+ if (p.charCodeAt(p.length - 1) === 47)
3170
+ p = p.slice(0, -1);
3171
+ return p;
3172
+ }
3173
+ function joinPrefix(prefix, pattern) {
3174
+ if (!prefix)
3175
+ return pattern;
3176
+ let p = pattern.trim();
3177
+ if (p.length === 0 || p === "/")
3178
+ return prefix;
3179
+ if (p.charCodeAt(0) !== 47)
3180
+ p = `/${p}`;
3181
+ return prefix + p;
3182
+ }
3183
+ function canonicalizeRoutePattern(pattern) {
3184
+ let p = pattern.trim();
3185
+ if (p.length === 0)
3186
+ p = "/";
3187
+ if (p.charCodeAt(0) !== 47)
3188
+ p = `/${p}`;
3189
+ if (p.length > 1 && p.charCodeAt(p.length - 1) === 47) {
3190
+ p = p.slice(0, -1);
3191
+ }
3192
+ return p;
3193
+ }
3194
+ function validateRoutePattern(pattern) {
3195
+ if (pattern.length === 0 || pattern.charCodeAt(0) !== 47) {
3196
+ throw new Error(`Invalid route pattern "${pattern}": must start with '/'`);
3197
+ }
3198
+ if (pattern === "/")
3199
+ return;
3200
+ if (pattern.indexOf("#") !== -1) {
3201
+ throw new Error(`Invalid route pattern "${pattern}": must not contain '#'`);
3202
+ }
3203
+ const segments = pattern.split("/");
3204
+ if (segments.length - 1 > MAX_ROUTE_SEGMENTS) {
3205
+ throw new RangeError(`Route pattern exceeds the limit of ${MAX_ROUTE_SEGMENTS} segments`);
3206
+ }
3207
+ for (let i = 1;i < segments.length; i++) {
3208
+ const seg = segments[i];
3209
+ if (seg.length === 0) {
3210
+ throw new Error(`Invalid route pattern "${pattern}": empty segment ('//')`);
3211
+ }
3212
+ const decoded = decodeSegment2(seg);
3213
+ if (decoded === "." || decoded === "..") {
3214
+ throw new Error(`Invalid route pattern "${pattern}": dot segments '.', '..' not allowed in route patterns`);
3215
+ }
3216
+ }
3217
+ }
3218
+ function decodeSegment2(seg) {
3219
+ if (seg.indexOf("%") === -1)
3220
+ return seg;
3221
+ try {
3222
+ return decodeURIComponent(seg);
3223
+ } catch {
3224
+ return seg;
3225
+ }
3226
+ }
3227
+ function toResponse(value) {
3228
+ if (value instanceof Response)
3229
+ return value;
3230
+ if (isThenable(value)) {
3231
+ return Promise.resolve(value).then((resolved) => toResponse(resolved));
3232
+ }
3233
+ return finalizeValue(value);
3234
+ }
3235
+ function finalizeValue(value) {
3236
+ if (typeof value === "object" && value !== null)
3237
+ return new Response(JSON.stringify(value), { headers: FAST_JSON_HEADERS });
3238
+ if (typeof value === "string")
3239
+ return new Response(value, { headers: FAST_TEXT_HEADERS });
3240
+ return new Response(String(value), { headers: FAST_TEXT_HEADERS });
3241
+ }
3242
+ async function stopClusterProcesses(processes, hardTimeoutMs) {
3243
+ const running = processes.filter((process2) => !process2.killed);
3244
+ if (running.length === 0)
3245
+ return false;
3246
+ for (const process2 of running)
3247
+ process2.kill("SIGTERM");
3248
+ const exited = Promise.all(running.map((process2) => process2.exited));
3249
+ let timer;
3250
+ const forced = await Promise.race([
3251
+ exited.then(() => false),
3252
+ new Promise((resolve) => {
3253
+ timer = setTimeout(() => resolve(true), hardTimeoutMs);
3254
+ timer.unref?.();
3255
+ })
3256
+ ]);
3257
+ if (timer !== undefined)
3258
+ clearTimeout(timer);
3259
+ if (forced) {
3260
+ for (const process2 of running) {
3261
+ if (!process2.killed)
3262
+ process2.kill("SIGKILL");
3263
+ }
3264
+ await exited;
3265
+ }
3266
+ return forced;
3267
+ }
3268
+
3269
+ class RouteGroup {
3270
+ app;
3271
+ prefix;
3272
+ middlewares;
3273
+ constructor(app, prefix, middlewares = []) {
3274
+ this.app = app;
3275
+ this.prefix = prefix;
3276
+ this.middlewares = middlewares;
3277
+ }
3278
+ use(...middlewares) {
3279
+ for (let i = 0;i < middlewares.length; i++) {
3280
+ this.middlewares.push(middlewares[i]);
3281
+ }
3282
+ return this;
3283
+ }
3284
+ plugin(pluginFn, options) {
3285
+ pluginFn(this, options);
3286
+ return this;
3287
+ }
3288
+ _register(method, pattern, args) {
3289
+ return this._registerMethods([method], pattern, args);
3290
+ }
3291
+ _registerMethods(methods, pattern, args) {
3292
+ const fullPattern = joinPrefix(this.prefix, pattern);
3293
+ const { middlewares, handler } = parseRouteArgs(args);
3294
+ const allMiddlewares = this.middlewares.length === 0 ? middlewares : [...this.middlewares, ...middlewares];
3295
+ this.app._registerMethods(methods, fullPattern, allMiddlewares.length === 0 ? [handler] : [...allMiddlewares, handler]);
3296
+ return this;
3297
+ }
3298
+ add(method, pattern, ...args) {
3299
+ return this._register(method, pattern, args);
3300
+ }
3301
+ get(pattern, ...args) {
3302
+ return this._register("GET", pattern, args);
3303
+ }
3304
+ post(pattern, ...args) {
3305
+ return this._register("POST", pattern, args);
3306
+ }
3307
+ put(pattern, ...args) {
3308
+ return this._register("PUT", pattern, args);
3309
+ }
3310
+ delete(pattern, ...args) {
3311
+ return this._register("DELETE", pattern, args);
3312
+ }
3313
+ patch(pattern, ...args) {
3314
+ return this._register("PATCH", pattern, args);
3315
+ }
3316
+ head(pattern, ...args) {
3317
+ return this._register("HEAD", pattern, args);
3318
+ }
3319
+ options(pattern, ...args) {
3320
+ return this._register("OPTIONS", pattern, args);
3321
+ }
3322
+ all(pattern, ...args) {
3323
+ return this._registerMethods(METHODS, pattern, args);
3324
+ }
3325
+ ws(pattern, handlers, beforeUpgrade) {
3326
+ this.app.ws(joinPrefix(this.prefix, pattern), handlers, beforeUpgrade ? (ctx) => beforeUpgrade(ctx) : undefined);
3327
+ return this;
3328
+ }
3329
+ group(prefix, ...args) {
3330
+ const nestedPrefix = joinPrefix(this.prefix, prefix);
3331
+ const last = args[args.length - 1];
3332
+ if (typeof last !== "function") {
3333
+ throw new Error("Route group callback must be a function");
3334
+ }
3335
+ const callback = last;
3336
+ const groupMws = [...this.middlewares];
3337
+ for (let i = 0;i < args.length - 1; i++) {
3338
+ const item = args[i];
3339
+ if (Array.isArray(item)) {
3340
+ for (let j = 0;j < item.length; j++)
3341
+ groupMws.push(item[j]);
3342
+ } else if (item && typeof item === "object") {
3343
+ groupMws.push(item);
3344
+ }
3345
+ }
3346
+ const nestedGroup = new RouteGroup(this.app, nestedPrefix, groupMws);
3347
+ callback(nestedGroup);
3348
+ return this;
3349
+ }
3350
+ }
3351
+
3352
+ class App {
3353
+ fetchHandler = (req, srv) => this.handle(req, srv);
3354
+ trie = new SegmentTrie;
3355
+ middlewares = [];
3356
+ nativeRoutes = [];
3357
+ server = null;
3358
+ clusterProcesses = null;
3359
+ _options;
3360
+ redirectHosts;
3361
+ trustProxy;
3362
+ trustedProxyChecker;
3363
+ stopPromise = null;
3364
+ notFoundHandler = null;
3365
+ errorHandler = null;
3366
+ errorResolver = undefined;
3367
+ globalPipeline = composeMiddlewareDispatcher([]);
3368
+ hasMiddlewares = false;
3369
+ hasWebSockets = false;
3370
+ development;
3371
+ cookieSecret;
3372
+ constructor(options = {}) {
3373
+ if (options.port !== undefined && (!Number.isInteger(options.port) || options.port < 0 || options.port > 65535)) {
3374
+ throw new RangeError("port must be an integer between 0 and 65535");
3375
+ }
3376
+ if (options.maxRequestBodySize !== undefined && (!Number.isSafeInteger(options.maxRequestBodySize) || options.maxRequestBodySize < 0)) {
3377
+ throw new RangeError("maxRequestBodySize must be a non-negative safe integer");
3378
+ }
3379
+ if (options.idleTimeout !== undefined && (!Number.isFinite(options.idleTimeout) || options.idleTimeout < 0 || options.idleTimeout > 255)) {
3380
+ throw new RangeError("idleTimeout must be between 0 and 255 seconds");
3381
+ }
3382
+ for (const [name, value] of [
3383
+ ["websocket.maxPayloadLength", options.websocket?.maxPayloadLength],
3384
+ ["websocket.backpressureLimit", options.websocket?.backpressureLimit]
3385
+ ]) {
3386
+ if (value !== undefined && (!Number.isSafeInteger(value) || value < 1)) {
3387
+ throw new RangeError(`${name} must be a positive safe integer`);
3388
+ }
3389
+ }
3390
+ if (options.websocket?.idleTimeout !== undefined && (!Number.isFinite(options.websocket.idleTimeout) || options.websocket.idleTimeout < 0 || options.websocket.idleTimeout > 255)) {
3391
+ throw new RangeError("websocket.idleTimeout must be between 0 and 255 seconds");
3392
+ }
3393
+ const trustedProxyRanges = Array.isArray(options.trustProxy) ? options.trustProxy : typeof options.trustProxy === "string" ? [options.trustProxy] : [];
3394
+ for (const range of trustedProxyRanges) {
3395
+ if (!isIpRange(range)) {
3396
+ throw new TypeError(`Invalid trustProxy IP range: ${range}`);
3397
+ }
3398
+ }
3399
+ if (options.cookieSecret !== undefined && !options.cookieSecret.trim()) {
3400
+ throw new TypeError("cookieSecret must be non-empty when provided");
3401
+ }
3402
+ this._options = options;
3403
+ this.development = options.development ?? true;
3404
+ this.trustProxy = Array.isArray(options.trustProxy) ? Object.freeze([...options.trustProxy]) : options.trustProxy ?? false;
3405
+ this.trustedProxyChecker = compileTrustProxy(options.trustProxy);
3406
+ this.cookieSecret = options.cookieSecret;
3407
+ this.redirectHosts = options.allowedRedirectHosts ? createReadonlySet(new Set(options.allowedRedirectHosts.map((h) => h.toLowerCase()))) : EMPTY_REDIRECT_HOSTS;
3408
+ }
3409
+ plugin(pluginFn, options) {
3410
+ pluginFn(this, options);
3411
+ return this;
3412
+ }
3413
+ notFound(handler) {
3414
+ this.notFoundHandler = handler;
3415
+ return this;
3416
+ }
3417
+ onError(handler) {
3418
+ this.errorHandler = handler;
3419
+ this.errorResolver = (err, ctx) => handler(err, ctx);
3420
+ if (this.server !== null) {
3421
+ this.globalPipeline = composeMiddlewareDispatcher(this.middlewares, this.errorResolver);
3422
+ }
3423
+ return this;
3424
+ }
3425
+ _register(method, pattern, args) {
3426
+ return this._registerMethods([method], pattern, args);
3427
+ }
3428
+ _registerMethods(methods, pattern, args) {
3429
+ const { middlewares, handler } = parseRouteArgs(args);
3430
+ const responseHandler = (ctx) => toResponse(handler(ctx));
3431
+ const finalHandler = middlewares.length === 0 ? responseHandler : composeMiddlewares(middlewares, responseHandler);
3432
+ const canonicalPattern = canonicalizeRoutePattern(pattern);
3433
+ validateRoutePattern(canonicalPattern);
3434
+ const routes = methods.map((method) => ({
3435
+ method,
3436
+ pattern: canonicalPattern,
3437
+ handler: finalHandler
3438
+ }));
3439
+ this.trie.addBatch(routes);
3440
+ const needs = detectHandlerNeeds(handler);
3441
+ const hasRouteMws = middlewares.length > 0;
3442
+ for (const r of routes) {
3443
+ const variants = expandOptionalPatterns(r.pattern);
3444
+ for (const variant of variants) {
3445
+ this.nativeRoutes.push({
3446
+ method: r.method,
3447
+ pattern: variant,
3448
+ handler: r.handler,
3449
+ rawHandler: handler,
3450
+ isDynamic: variant.includes(":") || variant.includes("*"),
3451
+ isContextFree: needs.isContextFree && !hasRouteMws,
3452
+ isLightweight: needs.isLightweight && !hasRouteMws,
3453
+ isParamsOnly: needs.isParamsOnly && !hasRouteMws,
3454
+ isJsonBodyOnly: needs.isJsonBodyOnly && !hasRouteMws,
3455
+ hasRouteMiddlewares: hasRouteMws
3456
+ });
3457
+ }
3458
+ }
3459
+ return this;
3460
+ }
3461
+ add(method, pattern, ...args) {
3462
+ return this._register(method, pattern, args);
3463
+ }
3464
+ get(pattern, ...args) {
3465
+ return this._register("GET", pattern, args);
3466
+ }
3467
+ post(pattern, ...args) {
3468
+ return this._register("POST", pattern, args);
3469
+ }
3470
+ put(pattern, ...args) {
3471
+ return this._register("PUT", pattern, args);
3472
+ }
3473
+ delete(pattern, ...args) {
3474
+ return this._register("DELETE", pattern, args);
3475
+ }
3476
+ patch(pattern, ...args) {
3477
+ return this._register("PATCH", pattern, args);
3478
+ }
3479
+ head(pattern, ...args) {
3480
+ return this._register("HEAD", pattern, args);
3481
+ }
3482
+ options(pattern, ...args) {
3483
+ return this._register("OPTIONS", pattern, args);
3484
+ }
3485
+ all(pattern, ...args) {
3486
+ return this._registerMethods(METHODS, pattern, args);
3487
+ }
3488
+ ws(pattern, handlers, beforeUpgrade) {
3489
+ this.hasWebSockets = true;
3490
+ return this._registerMethods(["GET"], pattern, [
3491
+ async (ctx) => {
3492
+ if (ctx.req.headers.get("upgrade")?.toLowerCase() !== "websocket") {
3493
+ return new Response("Upgrade Required", { status: 426 });
3494
+ }
3495
+ let customData;
3496
+ if (beforeUpgrade) {
3497
+ const check = await beforeUpgrade(ctx);
3498
+ if (!check)
3499
+ return new Response("Unauthorized", { status: 401 });
3500
+ if (typeof check === "object" && check !== null && "data" in check) {
3501
+ customData = check.data;
3502
+ }
3503
+ }
3504
+ const upgradeData = customData !== undefined && typeof customData === "object" && customData !== null ? { ...customData, handlers, _barqInternal: true } : { handlers, _barqInternal: true, data: customData };
3505
+ const upgraded = ctx.upgrade(upgradeData);
3506
+ return upgraded ? new Response(null) : new Response("WebSocket upgrade failed", { status: 400 });
3507
+ }
3508
+ ]);
3509
+ }
3510
+ group(prefix, ...args) {
3511
+ const last = args[args.length - 1];
3512
+ if (typeof last !== "function") {
3513
+ throw new Error("Route group callback must be a function");
3514
+ }
3515
+ const callback = last;
3516
+ const groupMws = [];
3517
+ for (let i = 0;i < args.length - 1; i++) {
3518
+ const item = args[i];
3519
+ if (Array.isArray(item)) {
3520
+ for (let j = 0;j < item.length; j++)
3521
+ groupMws.push(item[j]);
3522
+ } else if (item && typeof item === "object") {
3523
+ groupMws.push(item);
3524
+ }
3525
+ }
3526
+ const normalizedPrefix = normalizePrefix(prefix);
3527
+ const group = new RouteGroup(this, normalizedPrefix, groupMws);
3528
+ callback(group);
3529
+ return this;
3530
+ }
3531
+ use(...middlewares) {
3532
+ for (let i = 0;i < middlewares.length; i++) {
3533
+ this.middlewares.push(middlewares[i]);
3534
+ }
3535
+ this.hasMiddlewares = this.middlewares.length > 0;
3536
+ if (this.server !== null) {
3537
+ this.globalPipeline = composeMiddlewareDispatcher(this.middlewares, this.errorResolver);
3538
+ }
3539
+ return this;
3540
+ }
3541
+ listen(port) {
3542
+ const listenPort = port ?? this._options.port ?? 0;
3543
+ if (!Number.isInteger(listenPort) || listenPort < 0 || listenPort > 65535) {
3544
+ throw new RangeError("port must be an integer between 0 and 65535");
3545
+ }
3546
+ if (this.stopPromise !== null) {
3547
+ throw new Error("App cannot listen while shutdown is in progress");
3548
+ }
3549
+ if (this.server !== null) {
3550
+ throw new Error("App is already listening");
3551
+ }
3552
+ const userWs = this._options.websocket;
3553
+ this.globalPipeline = composeMiddlewareDispatcher(this.middlewares, this.errorResolver);
3554
+ const contextApplication = this;
3555
+ const opts = {
3556
+ fetch: this.fetchHandler,
3557
+ development: this.development
3558
+ };
3559
+ if (this._options.idleTimeout !== undefined) {
3560
+ opts.idleTimeout = this._options.idleTimeout;
3561
+ }
3562
+ if (this._options.maxRequestBodySize !== undefined) {
3563
+ opts.maxRequestBodySize = this._options.maxRequestBodySize;
3564
+ }
3565
+ opts.error = (err) => errorToResponse(err, this.development);
3566
+ if (Bun.env.BARQ_IS_WORKER === "true")
3567
+ opts.reusePort = true;
3568
+ if (this.hasWebSockets || userWs !== undefined) {
3569
+ opts.websocket = {
3570
+ ...userWs,
3571
+ message: (ws, message) => {
3572
+ const internal = getInternalWebSocketHandlers(ws);
3573
+ dispatchWebSocketCallback(internal?.message, [ws, message], (err) => reportWebSocketError(internal?.error, ws, err));
3574
+ dispatchWebSocketCallback(userWs?.message, [ws, message], (err) => reportWebSocketError(userWs?.error, ws, err));
3575
+ },
3576
+ open: (ws) => {
3577
+ const internal = getInternalWebSocketHandlers(ws);
3578
+ dispatchWebSocketCallback(internal?.open, [ws], (err) => reportWebSocketError(internal?.error, ws, err));
3579
+ dispatchWebSocketCallback(userWs?.open, [ws], (err) => reportWebSocketError(userWs?.error, ws, err));
3580
+ },
3581
+ close: (ws, code, reason) => {
3582
+ const internal = getInternalWebSocketHandlers(ws);
3583
+ dispatchWebSocketCallback(internal?.close, [ws, code, reason], (err) => reportWebSocketError(internal?.error, ws, err));
3584
+ dispatchWebSocketCallback(userWs?.close, [ws, code, reason], (err) => reportWebSocketError(userWs?.error, ws, err));
3585
+ },
3586
+ drain: (ws) => {
3587
+ const internal = getInternalWebSocketHandlers(ws);
3588
+ dispatchWebSocketCallback(internal?.drain, [ws], (err) => reportWebSocketError(internal?.error, ws, err));
3589
+ dispatchWebSocketCallback(userWs?.drain, [ws], (err) => reportWebSocketError(userWs?.error, ws, err));
3590
+ },
3591
+ error: (ws, err) => {
3592
+ const internal = getInternalWebSocketHandlers(ws);
3593
+ if (internal?.error === undefined && userWs?.error === undefined) {
3594
+ console.error("Unhandled WebSocket error:", err);
3595
+ return;
3596
+ }
3597
+ if (internal?.error)
3598
+ reportWebSocketError(internal.error, ws, err);
3599
+ if (userWs?.error)
3600
+ reportWebSocketError(userWs.error, ws, err);
3601
+ }
3602
+ };
3603
+ }
3604
+ if (this.nativeRoutes.length > 0) {
3605
+ const systemRoutes = Object.create(null);
3606
+ const hasGlobalPipeline = this.hasMiddlewares || this.errorResolver !== undefined;
3607
+ const routesByPattern = new Map;
3608
+ for (const nr of this.nativeRoutes) {
3609
+ let list = routesByPattern.get(nr.pattern);
3610
+ if (!list) {
3611
+ list = [];
3612
+ routesByPattern.set(nr.pattern, list);
3613
+ }
3614
+ list.push(nr);
3615
+ }
3616
+ for (const [pattern, list] of routesByPattern) {
3617
+ const isDynamic = pattern.includes(":") || pattern.includes("*");
3618
+ const methodMap = Object.create(null);
3619
+ for (const nr of list) {
3620
+ const h = nr.handler;
3621
+ const isDyn = nr.isDynamic;
3622
+ const raw = nr.rawHandler;
3623
+ if (hasGlobalPipeline || nr.hasRouteMiddlewares) {
3624
+ methodMap[nr.method] = (req) => {
3625
+ try {
3626
+ const params = isDyn ? req.params ?? EMPTY_PARAMS : EMPTY_PARAMS;
3627
+ const ctx = new Context(req, this.server, params, contextApplication);
3628
+ const result = this.globalPipeline(ctx, h);
3629
+ return finalizeResponse(ctx, result, req.method === "HEAD");
3630
+ } catch (err) {
3631
+ return errorToResponse(err, this.development);
3632
+ }
3633
+ };
3634
+ } else if (nr.isContextFree && raw) {
3635
+ const analysis = analyzeHandler(raw);
3636
+ const literal = analysis.literal;
3637
+ if (literal !== null) {
3638
+ if (literal.kind === "string") {
3639
+ methodMap[nr.method] = new Response(literal.value);
3640
+ if (nr.method === "GET" && !isDyn)
3641
+ methodMap.HEAD = new Response(null);
3642
+ } else {
3643
+ methodMap[nr.method] = new Response(literal.value, {
3644
+ headers: FAST_JSON_HEADERS
3645
+ });
3646
+ if (nr.method === "GET" && !isDyn)
3647
+ methodMap.HEAD = new Response(null, {
3648
+ headers: FAST_JSON_HEADERS
3649
+ });
3650
+ }
3651
+ } else {
3652
+ methodMap[nr.method] = () => {
3653
+ const res = raw();
3654
+ if (res instanceof Response)
3655
+ return res;
3656
+ if (isThenable(res)) {
3657
+ return Promise.resolve(res).then((r) => {
3658
+ if (r instanceof Response)
3659
+ return r;
3660
+ if (typeof r === "object" && r !== null)
3661
+ return Response.json(r);
3662
+ if (typeof r === "string")
3663
+ return new Response(r, { headers: FAST_TEXT_HEADERS });
3664
+ return finalizeValue(r);
3665
+ });
3666
+ }
3667
+ if (typeof res === "object" && res !== null)
3668
+ return Response.json(res);
3669
+ if (typeof res === "string")
3670
+ return new Response(res, { headers: FAST_TEXT_HEADERS });
3671
+ return Response.json(res);
3672
+ };
3673
+ if (nr.method === "GET" && !isDyn) {
3674
+ methodMap.HEAD = () => {
3675
+ const res = raw();
3676
+ const r = res instanceof Response ? res : finalizeValue(res);
3677
+ return stripBody(r);
3678
+ };
3679
+ }
3680
+ }
3681
+ } else if (nr.isJsonBodyOnly && raw) {
3682
+ methodMap[nr.method] = (req) => new Response(req.body, { headers: FAST_JSON_HEADERS });
3683
+ if (nr.method === "GET" && !isDyn) {
3684
+ methodMap.HEAD = (req) => req.json().then(() => new Response(null, { headers: FAST_JSON_HEADERS }), (err) => errorToResponse(err, this.development));
3685
+ }
3686
+ } else if (nr.isParamsOnly && raw) {
3687
+ const analysis = analyzeHandler(raw);
3688
+ if (analysis.paramSerializer && raw.constructor.name !== "AsyncFunction") {
3689
+ const serializer = analysis.paramSerializer;
3690
+ const responseInit = { headers: FAST_JSON_HEADERS };
3691
+ methodMap[nr.method] = (req) => {
3692
+ const params = req.params ?? EMPTY_PARAMS;
3693
+ return new Response(serializer(params), responseInit);
3694
+ };
3695
+ } else {
3696
+ methodMap[nr.method] = (req) => {
3697
+ const p = req.params;
3698
+ const res = raw(p ? req : { params: EMPTY_PARAMS, req });
3699
+ if (typeof res === "object" && res !== null) {
3700
+ if (res instanceof Response)
3701
+ return res;
3702
+ return Response.json(res);
3703
+ }
3704
+ if (typeof res === "string")
3705
+ return new Response(res, { headers: FAST_TEXT_HEADERS });
3706
+ if (isThenable(res)) {
3707
+ return Promise.resolve(res).then((r) => {
3708
+ if (r instanceof Response)
3709
+ return r;
3710
+ if (typeof r === "object" && r !== null)
3711
+ return new Response(JSON.stringify(r), {
3712
+ headers: FAST_JSON_HEADERS
3713
+ });
3714
+ if (typeof r === "string")
3715
+ return new Response(r, { headers: FAST_TEXT_HEADERS });
3716
+ return finalizeValue(r);
3717
+ });
3718
+ }
3719
+ return new Response(JSON.stringify(res), {
3720
+ headers: FAST_JSON_HEADERS
3721
+ });
3722
+ };
3723
+ if (nr.method === "GET" && !isDyn)
3724
+ methodMap.HEAD = () => new Response(null, { headers: FAST_JSON_HEADERS });
3725
+ }
3726
+ } else if (nr.isLightweight && raw) {
3727
+ const isAsync = raw.constructor.name === "AsyncFunction";
3728
+ const reusedLightCtx = new FastLightContext(null, EMPTY_PARAMS);
3729
+ let lightCtxInUse = false;
3730
+ methodMap[nr.method] = (req) => {
3731
+ const params = isDyn ? req.params ?? EMPTY_PARAMS : EMPTY_PARAMS;
3732
+ const useFresh = isAsync || lightCtxInUse;
3733
+ const ctx = useFresh ? new FastLightContext(req, params) : reusedLightCtx.init(req, params);
3734
+ if (!useFresh)
3735
+ lightCtxInUse = true;
3736
+ let res;
3737
+ try {
3738
+ res = raw(ctx);
3739
+ } catch (err) {
3740
+ if (!useFresh)
3741
+ lightCtxInUse = false;
3742
+ return errorToResponse(err, this.development);
3743
+ }
3744
+ if (typeof res === "object" && res !== null) {
3745
+ if (res instanceof Response) {
3746
+ if (!useFresh)
3747
+ lightCtxInUse = false;
3748
+ return res;
3749
+ }
3750
+ if (!isThenable(res)) {
3751
+ if (!useFresh)
3752
+ lightCtxInUse = false;
3753
+ return Response.json(res);
3754
+ }
3755
+ } else if (typeof res === "string") {
3756
+ if (!useFresh)
3757
+ lightCtxInUse = false;
3758
+ return new Response(res, { headers: FAST_TEXT_HEADERS });
3759
+ } else if (!isThenable(res)) {
3760
+ if (!useFresh)
3761
+ lightCtxInUse = false;
3762
+ return new Response(JSON.stringify(res), {
3763
+ headers: FAST_JSON_HEADERS
3764
+ });
3765
+ }
3766
+ return Promise.resolve(res).then((r) => {
3767
+ if (!useFresh)
3768
+ lightCtxInUse = false;
3769
+ if (r instanceof Response)
3770
+ return r;
3771
+ if (typeof r === "object" && r !== null)
3772
+ return new Response(JSON.stringify(r), {
3773
+ headers: FAST_JSON_HEADERS
3774
+ });
3775
+ if (typeof r === "string")
3776
+ return new Response(r, { headers: FAST_TEXT_HEADERS });
3777
+ return finalizeValue(r);
3778
+ }).catch((err) => {
3779
+ if (!useFresh)
3780
+ lightCtxInUse = false;
3781
+ return errorToResponse(err, this.development);
3782
+ });
3783
+ };
3784
+ if (nr.method === "GET" && !isDyn)
3785
+ methodMap.HEAD = () => new Response(null, { headers: FAST_JSON_HEADERS });
3786
+ } else {
3787
+ methodMap[nr.method] = (req) => {
3788
+ const isHead = nr.method === "GET" && req.method === "HEAD";
3789
+ try {
3790
+ const params = isDyn ? req.params ?? EMPTY_PARAMS : EMPTY_PARAMS;
3791
+ const ctx = new Context(req, this.server, params, contextApplication);
3792
+ const res = raw ? raw(ctx) : h(ctx);
3793
+ if (res instanceof Response) {
3794
+ if (!isHead && !ctx._hasPendingCookies)
3795
+ return res;
3796
+ return finalizeResponse(ctx, res, isHead);
3797
+ }
3798
+ if (!isThenable(res))
3799
+ return finalizeResponse(ctx, toResponse(res), isHead);
3800
+ return Promise.resolve(res).then((value) => finalizeResponse(ctx, toResponse(value), isHead)).catch((err) => finalizeResponse(ctx, errorToResponse(err, this.development), isHead));
3801
+ } catch (err) {
3802
+ return errorToResponse(err, this.development);
3803
+ }
3804
+ };
3805
+ }
3806
+ }
3807
+ const allowMethods = Object.keys(methodMap);
3808
+ const allowHeader = allowMethods.includes("GET") && !allowMethods.includes("HEAD") ? [...allowMethods, "HEAD"].join(", ") : allowMethods.join(", ");
3809
+ if (isDynamic) {
3810
+ const getFn = methodMap.GET;
3811
+ if (allowMethods.length === 1 && typeof getFn === "function" && allowMethods[0] === "GET") {
3812
+ systemRoutes[pattern] = methodMap;
3813
+ } else {
3814
+ systemRoutes[pattern] = (req) => {
3815
+ const matched = methodMap[req.method];
3816
+ if (typeof matched === "function")
3817
+ return matched(req);
3818
+ if (matched instanceof Response)
3819
+ return req.method === "HEAD" ? stripBody(matched) : matched;
3820
+ if (req.method === "HEAD") {
3821
+ const gh = methodMap.GET;
3822
+ if (typeof gh === "function") {
3823
+ const res = gh(req);
3824
+ return isThenable(res) ? res.then(stripBody) : stripBody(res);
3825
+ }
3826
+ }
3827
+ return new Response(`{"error":"Method Not Allowed"}`, {
3828
+ status: 405,
3829
+ headers: {
3830
+ "content-type": "application/json; charset=utf-8",
3831
+ Allow: allowHeader
3832
+ }
3833
+ });
3834
+ };
3835
+ }
3836
+ } else {
3837
+ systemRoutes[pattern] = methodMap;
3838
+ }
3839
+ }
3840
+ opts.routes = systemRoutes;
3841
+ }
3842
+ opts.port = listenPort;
3843
+ if (this._options.hostname !== undefined)
3844
+ opts.hostname = this._options.hostname;
3845
+ if (this._options.tls !== undefined)
3846
+ opts.tls = this._options.tls;
3847
+ this.server = Bun.serve(opts);
3848
+ return this.server;
3849
+ }
3850
+ handle(req, srv) {
3851
+ const isHead = req.method === "HEAD";
3852
+ const serverInstance = srv ?? this.server;
3853
+ const contextApplication = this;
3854
+ let currentCtx = null;
3855
+ let pathname;
3856
+ try {
3857
+ pathname = scanPathname(req.url);
3858
+ const matched = this.trie.match(req.method, pathname);
3859
+ if (matched === null) {
3860
+ if (this.notFoundHandler === null && !this.hasMiddlewares && this.errorResolver === undefined) {
3861
+ return new Response('{"error":"Not Found"}', {
3862
+ status: 404,
3863
+ headers: { "content-type": "application/json; charset=utf-8" }
3864
+ });
3865
+ }
3866
+ currentCtx = new Context(req, serverInstance, EMPTY_PARAMS, contextApplication, pathname);
3867
+ const notFoundHandler = this.notFoundHandler !== null ? this.notFoundHandler : () => new Response('{"error":"Not Found"}', {
3868
+ status: 404,
3869
+ headers: {
3870
+ "content-type": "application/json; charset=utf-8"
3871
+ }
3872
+ });
3873
+ const result = this.globalPipeline(currentCtx, notFoundHandler);
3874
+ return finalizeResponse(currentCtx, result, isHead);
3875
+ }
3876
+ if (!this.hasMiddlewares && this.errorResolver === undefined) {
3877
+ if (typeof matched === "function") {
3878
+ if (matched.length === 0) {
3879
+ const res = matched();
3880
+ if (!isHead && !isThenable(res))
3881
+ return res;
3882
+ if (isThenable(res)) {
3883
+ return res.then((r) => isHead ? stripBody(r) : r, (err) => errorToResponse(err, this.development));
3884
+ }
3885
+ return isHead ? stripBody(res) : res;
3886
+ }
3887
+ currentCtx = new Context(req, serverInstance, EMPTY_PARAMS, contextApplication, pathname);
3888
+ const res = matched(currentCtx);
3889
+ if (!isHead && !isThenable(res)) {
3890
+ if (typeof res === "object" && res !== null && !(res instanceof Response)) {
3891
+ return new Response(JSON.stringify(res), {
3892
+ headers: FAST_JSON_HEADERS
3893
+ });
3894
+ }
3895
+ if (!currentCtx._hasPendingCookies)
3896
+ return res;
3897
+ }
3898
+ return finalizeResponse(currentCtx, guardRejections(res, this.development), isHead);
3899
+ }
3900
+ const handler = typeof matched === "object" && MATCH_HANDLER in matched ? matched[MATCH_HANDLER] : undefined;
3901
+ if (handler !== undefined) {
3902
+ const params = matched;
3903
+ currentCtx = new Context(req, serverInstance, params, contextApplication, pathname);
3904
+ const res = handler(currentCtx);
3905
+ if (!isHead && !isThenable(res)) {
3906
+ if (!currentCtx._hasPendingCookies)
3907
+ return res;
3908
+ }
3909
+ return finalizeResponse(currentCtx, guardRejections(res, this.development), isHead);
3910
+ }
3911
+ }
3912
+ if (typeof matched === "function") {
3913
+ currentCtx = new Context(req, serverInstance, EMPTY_PARAMS, contextApplication, pathname);
3914
+ const result = this.globalPipeline(currentCtx, matched);
3915
+ return finalizeResponse(currentCtx, result, isHead);
3916
+ }
3917
+ if (MATCH_NOT_ALLOWED in matched) {
3918
+ const allow = matched[MATCH_NOT_ALLOWED];
3919
+ if (this.errorHandler === null && !this.hasMiddlewares && this.errorResolver === undefined) {
3920
+ return new Response('{"error":"Method Not Allowed"}', {
3921
+ status: 405,
3922
+ headers: {
3923
+ "content-type": "application/json; charset=utf-8",
3924
+ Allow: allow
3925
+ }
3926
+ });
3927
+ }
3928
+ currentCtx = new Context(req, serverInstance, EMPTY_PARAMS, contextApplication, pathname);
3929
+ const notAllowedHandler = () => errorToResponse(new HttpError(405, { error: "Method Not Allowed" }, { Allow: allow }), this.development);
3930
+ const result = this.globalPipeline(currentCtx, notAllowedHandler);
3931
+ return finalizeResponse(currentCtx, result, isHead);
3932
+ }
3933
+ const params = matched;
3934
+ const handler = params[MATCH_HANDLER];
3935
+ currentCtx = new Context(req, serverInstance, params, contextApplication, pathname);
3936
+ const result = this.globalPipeline(currentCtx, handler);
3937
+ return finalizeResponse(currentCtx, result, isHead);
3938
+ } catch (err) {
3939
+ const activeCtx = currentCtx ?? new Context(req, serverInstance, EMPTY_PARAMS, contextApplication, pathname);
3940
+ if (this.errorHandler !== null) {
3941
+ try {
3942
+ const custom = this.errorHandler(err, activeCtx);
3943
+ const result = isThenable(custom) ? custom.then((r) => unwindAfter(this.middlewares, activeCtx, r, 0, this.errorResolver)) : unwindAfter(this.middlewares, activeCtx, custom, 0, this.errorResolver);
3944
+ return finalizeResponse(activeCtx, result, isHead);
3945
+ } catch (e2) {
3946
+ const fallback = errorToResponse(e2, this.development);
3947
+ const result = unwindAfter(this.middlewares, activeCtx, fallback, 0, this.errorResolver);
3948
+ return finalizeResponse(activeCtx, result, isHead);
3949
+ }
3950
+ }
3951
+ const routingErr = errorToResponse(err, this.development);
3952
+ const result = unwindAfter(this.middlewares, activeCtx, routingErr, 0, this.errorResolver);
3953
+ return finalizeResponse(activeCtx, result, isHead);
3954
+ }
3955
+ }
3956
+ cluster(port) {
3957
+ const listenPort = port ?? this._options.port ?? 3000;
3958
+ const isWorker = Bun.env.BARQ_IS_WORKER === "true";
3959
+ if (!Number.isInteger(listenPort) || listenPort < 0 || listenPort > 65535) {
3960
+ throw new RangeError("port must be an integer between 0 and 65535");
3961
+ }
3962
+ if (isWorker || process.platform !== "linux") {
3963
+ return this.listen(listenPort);
3964
+ }
3965
+ if (listenPort === 0) {
3966
+ throw new RangeError("cluster requires a fixed, non-zero port");
3967
+ }
3968
+ if (this.clusterProcesses !== null || this.server !== null) {
3969
+ throw new Error("App is already listening or clustering");
3970
+ }
3971
+ const entry = process.argv[1];
3972
+ if (!entry) {
3973
+ throw new Error("Cannot start cluster without an application entry file");
3974
+ }
3975
+ const processes = [];
3976
+ const spawnWorker = () => {
3977
+ const proc = Bun.spawn([process.execPath, "run", entry], {
3978
+ env: {
3979
+ ...process.env,
3980
+ BARQ_IS_WORKER: "true",
3981
+ PORT: String(listenPort)
3982
+ },
3983
+ stdio: ["inherit", "inherit", "inherit"]
3984
+ });
3985
+ proc.exited.then((_code) => {
3986
+ if (this.stopPromise === null && this.clusterProcesses !== null) {
3987
+ const idx = processes.indexOf(proc);
3988
+ if (idx !== -1) {
3989
+ processes.splice(idx, 1);
3990
+ }
3991
+ processes.push(spawnWorker());
3992
+ }
3993
+ });
3994
+ return proc;
3995
+ };
3996
+ try {
3997
+ for (let i = 0;i < cpus().length; i++) {
3998
+ processes.push(spawnWorker());
3999
+ }
4000
+ } catch (error) {
4001
+ for (const process2 of processes)
4002
+ process2.kill("SIGTERM");
4003
+ throw error;
4004
+ }
4005
+ this.clusterProcesses = processes;
4006
+ return;
4007
+ }
4008
+ async stop(hardTimeoutMs = 5000) {
4009
+ if (!Number.isFinite(hardTimeoutMs) || hardTimeoutMs < 0) {
4010
+ throw new RangeError("hardTimeoutMs must be a finite non-negative number");
4011
+ }
4012
+ if (this.stopPromise !== null) {
4013
+ return this.stopPromise;
4014
+ }
4015
+ const server = this.server;
4016
+ const clusterProcesses = this.clusterProcesses;
4017
+ this.server = null;
4018
+ this.clusterProcesses = null;
4019
+ if (server === null && clusterProcesses === null) {
4020
+ return { forced: false, pending: 0 };
4021
+ }
4022
+ this.stopPromise = (async () => {
4023
+ let serverResult = { forced: false, pending: 0 };
4024
+ let workersForced = false;
4025
+ try {
4026
+ if (server !== null) {
4027
+ serverResult = await stopServer(server, hardTimeoutMs);
4028
+ }
4029
+ } finally {
4030
+ if (clusterProcesses !== null) {
4031
+ workersForced = await stopClusterProcesses(clusterProcesses, hardTimeoutMs);
4032
+ }
4033
+ }
4034
+ return {
4035
+ forced: serverResult.forced || workersForced,
4036
+ pending: serverResult.pending
4037
+ };
4038
+ })();
4039
+ try {
4040
+ return await this.stopPromise;
4041
+ } finally {
4042
+ this.stopPromise = null;
4043
+ }
4044
+ }
4045
+ }
4046
+ function guardRejections(res, development) {
4047
+ return isThenable(res) ? res.catch((err) => errorToResponse(err, development)) : res;
4048
+ }
4049
+ function applyCookies(ctx, res) {
4050
+ if (!ctx._hasPendingCookies)
4051
+ return res;
4052
+ const cookies = ctx._cookieJarIfCreated();
4053
+ if (cookies === null)
4054
+ return res;
4055
+ const headers = new Headers(res.headers);
4056
+ applyPendingCookies(cookies, headers);
4057
+ ctx._hasPendingCookies = false;
4058
+ return new Response(res.body, {
4059
+ status: res.status,
4060
+ statusText: res.statusText,
4061
+ headers
4062
+ });
4063
+ }
4064
+ function finalizeResponse(ctx, res, isHead) {
4065
+ if (res instanceof Response) {
4066
+ if (!isHead && !ctx._hasPendingCookies)
4067
+ return res;
4068
+ const withCookies = applyCookies(ctx, res);
4069
+ return isHead ? stripBody(withCookies) : withCookies;
4070
+ }
4071
+ if (isThenable(res)) {
4072
+ return res.then((response) => {
4073
+ const withCookies = applyCookies(ctx, response);
4074
+ return isHead ? stripBody(withCookies) : withCookies;
4075
+ });
4076
+ }
4077
+ if (!isHead) {
4078
+ if (!ctx._hasPendingCookies)
4079
+ return res;
4080
+ return applyCookies(ctx, res);
4081
+ }
4082
+ return stripBody(applyCookies(ctx, res));
4083
+ }
4084
+ function stripBody(res) {
4085
+ if (res.body === null)
4086
+ return res;
4087
+ try {
4088
+ res.body.cancel?.().catch?.(() => {});
4089
+ } catch {}
4090
+ return new Response(null, {
4091
+ status: res.status,
4092
+ statusText: res.statusText,
4093
+ headers: res.headers
4094
+ });
4095
+ }
4096
+
4097
+ // src/internal/node-compat.ts
4098
+ var isNode = typeof Bun === "undefined";
4099
+ if (isNode) {
4100
+ if (typeof globalThis.Bun === "undefined") {
4101
+ Object.defineProperty(globalThis, "Bun", {
4102
+ configurable: true,
4103
+ value: {
4104
+ env: process.env,
4105
+ CryptoHasher: class {
4106
+ hmac;
4107
+ constructor(_algo, secret) {
4108
+ this.hmac = crypto.createHmac("sha256", secret);
4109
+ }
4110
+ update(data) {
4111
+ this.hmac.update(data);
4112
+ return this;
4113
+ }
4114
+ digest(encoding) {
4115
+ return this.hmac.digest(encoding);
4116
+ }
4117
+ }
4118
+ }
4119
+ });
4120
+ }
4121
+ App.prototype.listen = function(port) {
4122
+ const rawPort = port ?? this["_options"]?.port ?? 3000;
4123
+ const hostname = this["_options"]?.hostname ?? "0.0.0.0";
4124
+ const listenPort = rawPort === 0 ? Math.floor(Math.random() * 20000) + 30000 : rawPort;
4125
+ const server = http.createServer(async (req, res) => {
4126
+ try {
4127
+ const protocol = req.socket?.encrypted ? "https" : "http";
4128
+ const host = req.headers.host || "localhost";
4129
+ const url = `${protocol}://${host}${req.url || "/"}`;
4130
+ const method = req.method || "GET";
4131
+ let reqBody;
4132
+ if (method !== "GET" && method !== "HEAD") {
4133
+ reqBody = Readable.toWeb(req);
4134
+ }
4135
+ const webReq = new Request(url, {
4136
+ method,
4137
+ headers: req.headers,
4138
+ body: reqBody,
4139
+ duplex: "half"
4140
+ });
4141
+ const remoteIp = req.socket.remoteAddress || "";
4142
+ const webRes = await this["handle"](webReq, {
4143
+ requestIP: () => remoteIp ? { address: remoteIp } : null
4144
+ });
4145
+ res.statusCode = webRes.status;
4146
+ res.statusMessage = webRes.statusText;
4147
+ webRes.headers.forEach((val, key) => {
4148
+ res.setHeader(key, val);
4149
+ });
4150
+ if (!webRes.body) {
4151
+ res.end();
4152
+ return;
4153
+ }
4154
+ const nodeStream = Readable.fromWeb(webRes.body);
4155
+ nodeStream.pipe(res);
4156
+ } catch (err) {
4157
+ console.error("Barq Node Adapter Error:", err);
4158
+ if (!res.headersSent) {
4159
+ res.statusCode = 500;
4160
+ res.setHeader("Content-Type", "application/json");
4161
+ res.end('{"error":"Internal Server Error"}');
4162
+ }
4163
+ }
4164
+ });
4165
+ server.listen(listenPort, hostname);
4166
+ return {
4167
+ port: listenPort,
4168
+ hostname,
4169
+ stop: () => new Promise((resolve) => {
4170
+ server.close(() => resolve());
4171
+ if (typeof server.closeAllConnections === "function") {
4172
+ server.closeAllConnections();
4173
+ }
4174
+ })
4175
+ };
4176
+ };
4177
+ }
4178
+ // src/cors.ts
4179
+ function cors(options = {}) {
4180
+ const methods = (options.methods ?? ["GET", "HEAD", "PUT", "POST", "DELETE", "PATCH"]).join(", ");
4181
+ const allowedHeaders = options.allowedHeaders?.join(", ");
4182
+ const exposedHeaders = options.exposedHeaders?.join(", ");
4183
+ const maxAge = options.maxAge !== undefined ? String(options.maxAge) : null;
4184
+ const credentials = options.credentials === true;
4185
+ function resolveOrigin(reqOrigin) {
4186
+ if (!options.origin || options.origin === "*") {
4187
+ return credentials ? reqOrigin ?? "*" : "*";
4188
+ }
4189
+ if (!reqOrigin)
4190
+ return null;
4191
+ if (typeof options.origin === "string") {
4192
+ return options.origin === reqOrigin ? reqOrigin : null;
4193
+ }
4194
+ if (Array.isArray(options.origin)) {
4195
+ return options.origin.includes(reqOrigin) ? reqOrigin : null;
4196
+ }
4197
+ if (typeof options.origin === "function") {
4198
+ const result = options.origin(reqOrigin);
4199
+ if (typeof result === "string")
4200
+ return result;
4201
+ return result ? reqOrigin : null;
4202
+ }
4203
+ return null;
4204
+ }
4205
+ function setCorsHeaders(headers, matchedOrigin) {
4206
+ if (matchedOrigin) {
4207
+ headers.set("access-control-allow-origin", matchedOrigin);
4208
+ if (matchedOrigin !== "*") {
4209
+ headers.append("vary", "Origin");
4210
+ }
4211
+ }
4212
+ if (credentials) {
4213
+ headers.set("access-control-allow-credentials", "true");
4214
+ }
4215
+ if (exposedHeaders) {
4216
+ headers.set("access-control-expose-headers", exposedHeaders);
4217
+ }
4218
+ }
4219
+ return {
4220
+ before(ctx) {
4221
+ const reqOrigin = ctx.req.headers.get("origin");
4222
+ const matchedOrigin = resolveOrigin(reqOrigin);
4223
+ if (ctx.req.method === "OPTIONS") {
4224
+ const headers = new Headers;
4225
+ setCorsHeaders(headers, matchedOrigin);
4226
+ headers.set("access-control-allow-methods", methods);
4227
+ if (allowedHeaders) {
4228
+ headers.set("access-control-allow-headers", allowedHeaders);
4229
+ } else {
4230
+ const reqHeaders = ctx.req.headers.get("access-control-request-headers");
4231
+ if (reqHeaders) {
4232
+ headers.set("access-control-allow-headers", reqHeaders);
4233
+ }
4234
+ }
4235
+ if (maxAge) {
4236
+ headers.set("access-control-max-age", maxAge);
4237
+ }
4238
+ headers.set("content-length", "0");
4239
+ return new Response(null, { status: 204, headers });
4240
+ }
4241
+ ctx.state._corsOrigin = matchedOrigin;
4242
+ },
4243
+ after(ctx, res) {
4244
+ const matchedOrigin = ctx.state._corsOrigin;
4245
+ if (matchedOrigin !== undefined) {
4246
+ const headers = new Headers(res.headers);
4247
+ setCorsHeaders(headers, matchedOrigin);
4248
+ return new Response(res.body, {
4249
+ status: res.status,
4250
+ statusText: res.statusText,
4251
+ headers
4252
+ });
4253
+ }
4254
+ return res;
4255
+ }
4256
+ };
4257
+ }
4258
+ // src/validation.ts
4259
+ function validateJson(validator, handler) {
4260
+ return async (ctx) => {
4261
+ const raw = await ctx.json();
4262
+ let body;
4263
+ if (typeof validator === "object" && validator !== null && "~standard" in validator) {
4264
+ const res = await validator["~standard"].validate(raw);
4265
+ if (res.issues) {
4266
+ throw new HttpError(422, {
4267
+ error: "Validation Failed",
4268
+ issues: res.issues
4269
+ });
4270
+ }
4271
+ body = res.value;
4272
+ } else if (typeof validator === "function") {
4273
+ body = await validator(raw);
4274
+ } else if (typeof validator === "object" && validator !== null && "parse" in validator) {
4275
+ body = validator.parse(raw);
4276
+ } else {
4277
+ throw new TypeError("Invalid validator provided to validateJson");
4278
+ }
4279
+ return handler(ctx, body);
4280
+ };
4281
+ }
4282
+ export {
4283
+ App,
4284
+ Context,
4285
+ HttpError,
4286
+ RouteGroup,
4287
+ composeMiddlewareDispatcher,
4288
+ composeMiddlewares,
4289
+ cors,
4290
+ createCookieJar,
4291
+ createErrorResponse,
4292
+ empty,
4293
+ errorToResponse,
4294
+ isThenable,
4295
+ json,
4296
+ parseRequestTarget,
4297
+ redirect,
4298
+ runPipeline,
4299
+ text,
4300
+ unwindAfter,
4301
+ validateJson
4302
+ };
4303
+
4304
+ //# debugId=488AD569EC96D0E864756E2164756E21