@barqel/barq 1.0.0 → 1.0.2

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 (59) hide show
  1. package/README.md +8 -4
  2. package/dist/app.d.ts +0 -2
  3. package/dist/app.d.ts.map +1 -1
  4. package/dist/context.d.ts +0 -2
  5. package/dist/context.d.ts.map +1 -1
  6. package/dist/cookies.d.ts.map +1 -1
  7. package/dist/cors.d.ts.map +1 -1
  8. package/dist/errors.d.ts.map +1 -1
  9. package/dist/http.d.ts.map +1 -1
  10. package/dist/index.d.ts +1 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +4870 -10
  13. package/dist/index.js.map +24 -1
  14. package/dist/internal/ip.d.ts.map +1 -1
  15. package/dist/internal/node-compat.d.ts +2 -0
  16. package/dist/internal/node-compat.d.ts.map +1 -0
  17. package/dist/internal/pathname.d.ts.map +1 -1
  18. package/dist/internal/query.d.ts.map +1 -1
  19. package/dist/internal/scan.d.ts.map +1 -1
  20. package/dist/internal/shutdown.d.ts.map +1 -1
  21. package/dist/internal/trie.d.ts.map +1 -1
  22. package/dist/middleware.d.ts.map +1 -1
  23. package/dist/node-patch.d.ts +3 -0
  24. package/dist/node-patch.d.ts.map +1 -0
  25. package/dist/node-patch.js +4870 -0
  26. package/dist/node-patch.js.map +24 -0
  27. package/dist/types.d.ts.map +1 -1
  28. package/dist/validation.d.ts.map +1 -1
  29. package/package.json +72 -58
  30. package/dist/app.js +0 -1370
  31. package/dist/app.js.map +0 -1
  32. package/dist/context.js +0 -261
  33. package/dist/context.js.map +0 -1
  34. package/dist/cookies.js +0 -234
  35. package/dist/cookies.js.map +0 -1
  36. package/dist/cors.js +0 -82
  37. package/dist/cors.js.map +0 -1
  38. package/dist/errors.js +0 -81
  39. package/dist/errors.js.map +0 -1
  40. package/dist/http.js +0 -127
  41. package/dist/http.js.map +0 -1
  42. package/dist/internal/ip.js +0 -151
  43. package/dist/internal/ip.js.map +0 -1
  44. package/dist/internal/pathname.js +0 -158
  45. package/dist/internal/pathname.js.map +0 -1
  46. package/dist/internal/query.js +0 -126
  47. package/dist/internal/query.js.map +0 -1
  48. package/dist/internal/scan.js +0 -1069
  49. package/dist/internal/scan.js.map +0 -1
  50. package/dist/internal/shutdown.js +0 -30
  51. package/dist/internal/shutdown.js.map +0 -1
  52. package/dist/internal/trie.js +0 -560
  53. package/dist/internal/trie.js.map +0 -1
  54. package/dist/middleware.js +0 -269
  55. package/dist/middleware.js.map +0 -1
  56. package/dist/types.js +0 -2
  57. package/dist/types.js.map +0 -1
  58. package/dist/validation.js +0 -32
  59. package/dist/validation.js.map +0 -1
package/dist/app.js DELETED
@@ -1,1370 +0,0 @@
1
- import { cpus } from "node:os";
2
- import { Context } from "./context";
3
- import { applyPendingCookies } from "./cookies";
4
- import { errorToResponse, HttpError } from "./errors";
5
- import { compileTrustProxy, isIpRange } from "./internal/ip";
6
- import { scanPathname } from "./internal/pathname";
7
- import { findQueryParam, findQueryParamAll } from "./internal/query";
8
- import { analyzeHandler, } from "./internal/scan";
9
- import { stopServer } from "./internal/shutdown";
10
- import { MATCH_HANDLER, MATCH_NOT_ALLOWED, SegmentTrie } from "./internal/trie";
11
- import { composeMiddlewareDispatcher, composeMiddlewares, isThenable, unwindAfter, } from "./middleware";
12
- const METHODS = [
13
- "GET",
14
- "POST",
15
- "PUT",
16
- "DELETE",
17
- "PATCH",
18
- "HEAD",
19
- "OPTIONS",
20
- ];
21
- const MAX_ROUTE_SEGMENTS = 256;
22
- const EMPTY_PARAMS = Object.freeze(Object.create(null));
23
- // Prebuilt `Headers` instances rather than plain objects: the `Response`
24
- // constructor has to parse/validate header names and values on every call, and
25
- // handing it an already-built `Headers` skips that work. Measured ~285ns per
26
- // response versus a frozen plain object on Bun 1.4.
27
- //
28
- // Safe to share: `new Response(body, { headers })` copies the entries into a
29
- // fresh `Headers` on the response, so mutating `response.headers` later (cookie
30
- // application, `stripBody`) never touches these. Never mutate them here.
31
- const FAST_JSON_HEADERS = new Headers({
32
- "content-type": "application/json; charset=utf-8",
33
- });
34
- const FAST_TEXT_HEADERS = new Headers({
35
- "content-type": "text/plain; charset=utf-8",
36
- });
37
- const _FAST_JSON_INIT = Object.freeze({
38
- headers: FAST_JSON_HEADERS,
39
- });
40
- const _FAST_TEXT_INIT = Object.freeze({
41
- headers: FAST_TEXT_HEADERS,
42
- });
43
- class ReadonlySetView {
44
- source;
45
- constructor(source) {
46
- this.source = source;
47
- }
48
- get size() {
49
- return this.source.size;
50
- }
51
- has(value) {
52
- return this.source.has(value);
53
- }
54
- forEach(callback, thisArg) {
55
- this.source.forEach((value) => {
56
- callback.call(thisArg, value, value, this);
57
- });
58
- }
59
- entries() {
60
- return this.source.entries();
61
- }
62
- keys() {
63
- return this.source.keys();
64
- }
65
- values() {
66
- return this.source.values();
67
- }
68
- [Symbol.iterator]() {
69
- return this.source[Symbol.iterator]();
70
- }
71
- get [Symbol.toStringTag]() {
72
- return "Set";
73
- }
74
- }
75
- function createReadonlySet(source) {
76
- return new ReadonlySetView(source);
77
- }
78
- const EMPTY_REDIRECT_HOSTS = createReadonlySet(new Set());
79
- function expandOptionalPatterns(pattern) {
80
- if (!pattern.includes("?"))
81
- return [pattern];
82
- const segments = pattern.split("/").filter(Boolean);
83
- const optIndices = [];
84
- for (let i = 0; i < segments.length; i++) {
85
- const seg = segments[i];
86
- if (seg.startsWith(":") && seg.endsWith("?")) {
87
- optIndices.push(i);
88
- }
89
- }
90
- if (optIndices.length === 0)
91
- return [pattern];
92
- const variants = [];
93
- const count = 1 << optIndices.length;
94
- for (let mask = 0; mask < count; mask++) {
95
- const parts = [];
96
- for (let i = 0; i < segments.length; i++) {
97
- const optPos = optIndices.indexOf(i);
98
- if (optPos >= 0) {
99
- if (mask & (1 << optPos)) {
100
- parts.push(segments[i].slice(0, -1));
101
- }
102
- }
103
- else {
104
- parts.push(segments[i]);
105
- }
106
- }
107
- variants.push(parts.length === 0 ? "/" : `/${parts.join("/")}`);
108
- }
109
- return variants;
110
- }
111
- const LIGHT_SUPPORTED_PROPS = new Set([
112
- "req",
113
- "params",
114
- "pathname",
115
- "queryParam",
116
- "queryParamAll",
117
- "state",
118
- "stream",
119
- "bodyStream",
120
- ]);
121
- function classifyProps(props) {
122
- for (const p of props) {
123
- if (!LIGHT_SUPPORTED_PROPS.has(p)) {
124
- return {
125
- isContextFree: false,
126
- isLightweight: false,
127
- isParamsOnly: false,
128
- isJsonBodyOnly: false,
129
- };
130
- }
131
- }
132
- const hasParams = props.has("params") || props.size === 0;
133
- const hasQuery = props.has("queryParam") || props.has("queryParamAll");
134
- if (hasParams && props.size === 1) {
135
- return {
136
- isContextFree: false,
137
- isLightweight: true,
138
- isParamsOnly: true,
139
- isJsonBodyOnly: false,
140
- };
141
- }
142
- if (hasParams || hasQuery || props.size > 0) {
143
- return {
144
- isContextFree: false,
145
- isLightweight: true,
146
- isParamsOnly: false,
147
- isJsonBodyOnly: false,
148
- };
149
- }
150
- return {
151
- isContextFree: false,
152
- isLightweight: false,
153
- isParamsOnly: false,
154
- isJsonBodyOnly: false,
155
- };
156
- }
157
- function classify(analysis) {
158
- if (analysis.isNoArg) {
159
- return {
160
- isContextFree: true,
161
- isLightweight: false,
162
- isParamsOnly: false,
163
- isJsonBodyOnly: false,
164
- };
165
- }
166
- if (analysis.arglessJsonCall &&
167
- !analysis.isDestructured &&
168
- analysis.contextProps.size === 1 &&
169
- analysis.contextProps.has("json")) {
170
- return {
171
- isContextFree: false,
172
- isLightweight: false,
173
- isParamsOnly: false,
174
- isJsonBodyOnly: true,
175
- };
176
- }
177
- if (analysis.isDestructured) {
178
- return classifyProps(analysis.destructuredProps);
179
- }
180
- if (analysis.hasDynamicAccess) {
181
- return {
182
- isContextFree: false,
183
- isLightweight: false,
184
- isParamsOnly: false,
185
- isJsonBodyOnly: false,
186
- };
187
- }
188
- return classifyProps(analysis.contextProps);
189
- }
190
- function detectHandlerNeeds(fn) {
191
- try {
192
- return classify(analyzeHandler(fn));
193
- }
194
- catch {
195
- return {
196
- isContextFree: false,
197
- isLightweight: false,
198
- isParamsOnly: false,
199
- isJsonBodyOnly: false,
200
- };
201
- }
202
- }
203
- class FastLightContext {
204
- req;
205
- params;
206
- _pathname;
207
- _state;
208
- _hasPendingCookies = false;
209
- _url;
210
- _qPos = -2;
211
- constructor(req, params) {
212
- this.req = req;
213
- this.params = params;
214
- }
215
- init(req, params) {
216
- this.req = req;
217
- this.params = params;
218
- this._pathname = undefined;
219
- this._state = undefined;
220
- this._hasPendingCookies = false;
221
- this._url = undefined;
222
- this._qPos = -2;
223
- return this;
224
- }
225
- get url() {
226
- // biome-ignore lint/suspicious/noAssignInExpressions: Preserve the lazy-cache fast path.
227
- return this._url ?? (this._url = this.req.url);
228
- }
229
- get pathname() {
230
- if (this._pathname !== undefined)
231
- return this._pathname;
232
- // biome-ignore lint/suspicious/noAssignInExpressions: Preserve the lazy-cache fast path.
233
- return (this._pathname = scanPathname(this.url));
234
- }
235
- queryParam(name) {
236
- let u = this._url;
237
- if (u === undefined) {
238
- u = this.req.url;
239
- this._url = u;
240
- }
241
- let q = this._qPos;
242
- if (q === -2)
243
- this._qPos = q = u.indexOf("?");
244
- return findQueryParam(u, name, q);
245
- }
246
- queryParamAll(name) {
247
- return findQueryParamAll(this.url, name);
248
- }
249
- get cookies() {
250
- throw new Error("LightweightContext does not support cookies.");
251
- }
252
- get state() {
253
- let state = this._state;
254
- if (state === undefined) {
255
- state = Object.create(null);
256
- this._state = state;
257
- }
258
- return state;
259
- }
260
- stream() {
261
- return this.req.body;
262
- }
263
- get bodyStream() {
264
- return this.req.body;
265
- }
266
- }
267
- function reportWebSocketError(handler, ws, error) {
268
- if (!handler) {
269
- console.error("Unhandled WebSocket callback error:", error);
270
- return;
271
- }
272
- try {
273
- const result = handler(ws, error);
274
- if (isThenable(result)) {
275
- Promise.resolve(result).catch((handlerError) => console.error("WebSocket error handler failed:", handlerError));
276
- }
277
- }
278
- catch (handlerError) {
279
- console.error("WebSocket error handler failed:", handlerError);
280
- }
281
- }
282
- function dispatchWebSocketCallback(callback, args, onError) {
283
- if (!callback)
284
- return;
285
- try {
286
- const len = args.length;
287
- let result;
288
- if (len === 1) {
289
- result = callback(args[0]);
290
- }
291
- else if (len === 2) {
292
- result = callback(args[0], args[1]);
293
- }
294
- else if (len === 3) {
295
- result = callback(args[0], args[1], args[2]);
296
- }
297
- else {
298
- result = Reflect.apply(callback, undefined, args);
299
- }
300
- if (isThenable(result)) {
301
- Promise.resolve(result).catch(onError);
302
- }
303
- }
304
- catch (error) {
305
- onError(error);
306
- }
307
- }
308
- function getInternalWebSocketHandlers(ws) {
309
- const data = ws.data;
310
- return data?._barqInternal === true ? data.handlers : undefined;
311
- }
312
- function parseRouteArgs(args) {
313
- if (args.length === 0) {
314
- throw new Error("Route requires a handler function");
315
- }
316
- const last = args[args.length - 1];
317
- if (typeof last !== "function") {
318
- throw new Error("The final argument in route definition must be a Handler function");
319
- }
320
- const handler = last;
321
- if (args.length === 1) {
322
- return { middlewares: [], handler };
323
- }
324
- const middlewares = [];
325
- for (let i = 0; i < args.length - 1; i++) {
326
- const item = args[i];
327
- if (Array.isArray(item)) {
328
- for (let j = 0; j < item.length; j++) {
329
- middlewares.push(item[j]);
330
- }
331
- }
332
- else if (item && typeof item === "object") {
333
- middlewares.push(item);
334
- }
335
- else {
336
- throw new TypeError("Route middleware must be a Middleware object or array of Middleware");
337
- }
338
- }
339
- return { middlewares, handler };
340
- }
341
- function normalizePrefix(prefix) {
342
- let p = prefix.trim();
343
- if (p.length === 0 || p === "/")
344
- return "";
345
- if (p.charCodeAt(0) !== 47)
346
- p = `/${p}`;
347
- if (p.charCodeAt(p.length - 1) === 47)
348
- p = p.slice(0, -1);
349
- return p;
350
- }
351
- function joinPrefix(prefix, pattern) {
352
- if (!prefix)
353
- return pattern;
354
- let p = pattern.trim();
355
- if (p.length === 0 || p === "/")
356
- return prefix;
357
- if (p.charCodeAt(0) !== 47)
358
- p = `/${p}`;
359
- return prefix + p;
360
- }
361
- function canonicalizeRoutePattern(pattern) {
362
- let p = pattern.trim();
363
- if (p.length === 0)
364
- p = "/";
365
- if (p.charCodeAt(0) !== 47)
366
- p = `/${p}`;
367
- if (p.length > 1 && p.charCodeAt(p.length - 1) === 47) {
368
- p = p.slice(0, -1);
369
- }
370
- return p;
371
- }
372
- function validateRoutePattern(pattern) {
373
- if (pattern.length === 0 || pattern.charCodeAt(0) !== 47) {
374
- throw new Error(`Invalid route pattern "${pattern}": must start with '/'`);
375
- }
376
- if (pattern === "/")
377
- return;
378
- if (pattern.indexOf("#") !== -1) {
379
- throw new Error(`Invalid route pattern "${pattern}": must not contain '#'`);
380
- }
381
- const segments = pattern.split("/");
382
- if (segments.length - 1 > MAX_ROUTE_SEGMENTS) {
383
- throw new RangeError(`Route pattern exceeds the limit of ${MAX_ROUTE_SEGMENTS} segments`);
384
- }
385
- for (let i = 1; i < segments.length; i++) {
386
- const seg = segments[i];
387
- if (seg.length === 0) {
388
- throw new Error(`Invalid route pattern "${pattern}": empty segment ('//')`);
389
- }
390
- const decoded = decodeSegment(seg);
391
- if (decoded === "." || decoded === "..") {
392
- throw new Error(`Invalid route pattern "${pattern}": dot segments '.', '..' not allowed in route patterns`);
393
- }
394
- }
395
- }
396
- function decodeSegment(seg) {
397
- if (seg.indexOf("%") === -1)
398
- return seg;
399
- try {
400
- return decodeURIComponent(seg);
401
- }
402
- catch {
403
- return seg;
404
- }
405
- }
406
- function toResponse(value) {
407
- if (value instanceof Response)
408
- return value;
409
- if (isThenable(value)) {
410
- return Promise.resolve(value).then((resolved) => toResponse(resolved));
411
- }
412
- return finalizeValue(value);
413
- }
414
- function finalizeValue(value) {
415
- if (typeof value === "object" && value !== null)
416
- return new Response(JSON.stringify(value), { headers: FAST_JSON_HEADERS });
417
- if (typeof value === "string")
418
- return new Response(value, { headers: FAST_TEXT_HEADERS });
419
- return new Response(String(value), { headers: FAST_TEXT_HEADERS });
420
- }
421
- async function stopClusterProcesses(processes, hardTimeoutMs) {
422
- const running = processes.filter((process) => !process.killed);
423
- if (running.length === 0)
424
- return false;
425
- for (const process of running)
426
- process.kill("SIGTERM");
427
- const exited = Promise.all(running.map((process) => process.exited));
428
- let timer;
429
- const forced = await Promise.race([
430
- exited.then(() => false),
431
- new Promise((resolve) => {
432
- timer = setTimeout(() => resolve(true), hardTimeoutMs);
433
- timer.unref?.();
434
- }),
435
- ]);
436
- if (timer !== undefined)
437
- clearTimeout(timer);
438
- if (forced) {
439
- for (const process of running) {
440
- if (!process.killed)
441
- process.kill("SIGKILL");
442
- }
443
- await exited;
444
- }
445
- return forced;
446
- }
447
- export class RouteGroup {
448
- app;
449
- prefix;
450
- middlewares;
451
- constructor(app, prefix, middlewares = []) {
452
- this.app = app;
453
- this.prefix = prefix;
454
- this.middlewares = middlewares;
455
- }
456
- use(...middlewares) {
457
- for (let i = 0; i < middlewares.length; i++) {
458
- this.middlewares.push(middlewares[i]);
459
- }
460
- return this;
461
- }
462
- plugin(pluginFn, options) {
463
- pluginFn(this, options);
464
- return this;
465
- }
466
- _register(method, pattern, args) {
467
- return this._registerMethods([method], pattern, args);
468
- }
469
- _registerMethods(methods, pattern, args) {
470
- const fullPattern = joinPrefix(this.prefix, pattern);
471
- const { middlewares, handler } = parseRouteArgs(args);
472
- const allMiddlewares = this.middlewares.length === 0
473
- ? middlewares
474
- : [...this.middlewares, ...middlewares];
475
- this.app._registerMethods(methods, fullPattern, allMiddlewares.length === 0 ? [handler] : [...allMiddlewares, handler]);
476
- return this;
477
- }
478
- add(method, pattern, ...args) {
479
- return this._register(method, pattern, args);
480
- }
481
- get(pattern, ...args) {
482
- return this._register("GET", pattern, args);
483
- }
484
- post(pattern, ...args) {
485
- return this._register("POST", pattern, args);
486
- }
487
- put(pattern, ...args) {
488
- return this._register("PUT", pattern, args);
489
- }
490
- delete(pattern, ...args) {
491
- return this._register("DELETE", pattern, args);
492
- }
493
- patch(pattern, ...args) {
494
- return this._register("PATCH", pattern, args);
495
- }
496
- head(pattern, ...args) {
497
- return this._register("HEAD", pattern, args);
498
- }
499
- options(pattern, ...args) {
500
- return this._register("OPTIONS", pattern, args);
501
- }
502
- all(pattern, ...args) {
503
- return this._registerMethods(METHODS, pattern, args);
504
- }
505
- ws(pattern, handlers, beforeUpgrade) {
506
- this.app.ws(joinPrefix(this.prefix, pattern), handlers, beforeUpgrade
507
- ? (ctx) => beforeUpgrade(ctx)
508
- : undefined);
509
- return this;
510
- }
511
- group(prefix, ...args) {
512
- const nestedPrefix = joinPrefix(this.prefix, prefix);
513
- const last = args[args.length - 1];
514
- if (typeof last !== "function") {
515
- throw new Error("Route group callback must be a function");
516
- }
517
- const callback = last;
518
- const groupMws = [...this.middlewares];
519
- for (let i = 0; i < args.length - 1; i++) {
520
- const item = args[i];
521
- if (Array.isArray(item)) {
522
- for (let j = 0; j < item.length; j++)
523
- groupMws.push(item[j]);
524
- }
525
- else if (item && typeof item === "object") {
526
- groupMws.push(item);
527
- }
528
- }
529
- const nestedGroup = new RouteGroup(this.app, nestedPrefix, groupMws);
530
- callback(nestedGroup);
531
- return this;
532
- }
533
- }
534
- export class App {
535
- fetchHandler = (req, srv) => this.handle(req, srv);
536
- trie = new SegmentTrie();
537
- middlewares = [];
538
- nativeRoutes = [];
539
- server = null;
540
- clusterProcesses = null;
541
- _options;
542
- // biome-ignore lint/correctness/noUnusedPrivateClassMembers: Accessed through ContextApplication.
543
- redirectHosts;
544
- trustProxy;
545
- trustedProxyChecker;
546
- stopPromise = null;
547
- notFoundHandler = null;
548
- errorHandler = null;
549
- errorResolver = undefined;
550
- globalPipeline = composeMiddlewareDispatcher([]);
551
- hasMiddlewares = false;
552
- hasWebSockets = false;
553
- development;
554
- cookieSecret;
555
- constructor(options = {}) {
556
- if (options.port !== undefined &&
557
- (!Number.isInteger(options.port) ||
558
- options.port < 0 ||
559
- options.port > 65535)) {
560
- throw new RangeError("port must be an integer between 0 and 65535");
561
- }
562
- if (options.maxRequestBodySize !== undefined &&
563
- (!Number.isSafeInteger(options.maxRequestBodySize) ||
564
- options.maxRequestBodySize < 0)) {
565
- throw new RangeError("maxRequestBodySize must be a non-negative safe integer");
566
- }
567
- if (options.idleTimeout !== undefined &&
568
- (!Number.isFinite(options.idleTimeout) ||
569
- options.idleTimeout < 0 ||
570
- options.idleTimeout > 255)) {
571
- throw new RangeError("idleTimeout must be between 0 and 255 seconds");
572
- }
573
- for (const [name, value] of [
574
- ["websocket.maxPayloadLength", options.websocket?.maxPayloadLength],
575
- ["websocket.backpressureLimit", options.websocket?.backpressureLimit],
576
- ]) {
577
- if (value !== undefined && (!Number.isSafeInteger(value) || value < 1)) {
578
- throw new RangeError(`${name} must be a positive safe integer`);
579
- }
580
- }
581
- if (options.websocket?.idleTimeout !== undefined &&
582
- (!Number.isFinite(options.websocket.idleTimeout) ||
583
- options.websocket.idleTimeout < 0 ||
584
- options.websocket.idleTimeout > 255)) {
585
- throw new RangeError("websocket.idleTimeout must be between 0 and 255 seconds");
586
- }
587
- const trustedProxyRanges = Array.isArray(options.trustProxy)
588
- ? options.trustProxy
589
- : typeof options.trustProxy === "string"
590
- ? [options.trustProxy]
591
- : [];
592
- for (const range of trustedProxyRanges) {
593
- if (!isIpRange(range)) {
594
- throw new TypeError(`Invalid trustProxy IP range: ${range}`);
595
- }
596
- }
597
- if (options.cookieSecret !== undefined && !options.cookieSecret.trim()) {
598
- throw new TypeError("cookieSecret must be non-empty when provided");
599
- }
600
- this._options = options;
601
- this.development =
602
- options.development ?? process.env.NODE_ENV === "development";
603
- this.trustProxy = Array.isArray(options.trustProxy)
604
- ? Object.freeze([...options.trustProxy])
605
- : (options.trustProxy ?? false);
606
- this.trustedProxyChecker = compileTrustProxy(options.trustProxy);
607
- this.cookieSecret = options.cookieSecret;
608
- this.redirectHosts = options.allowedRedirectHosts
609
- ? createReadonlySet(new Set(options.allowedRedirectHosts.map((h) => h.toLowerCase())))
610
- : EMPTY_REDIRECT_HOSTS;
611
- }
612
- plugin(pluginFn, options) {
613
- pluginFn(this, options);
614
- return this;
615
- }
616
- notFound(handler) {
617
- this.notFoundHandler = handler;
618
- return this;
619
- }
620
- onError(handler) {
621
- this.errorHandler = handler;
622
- this.errorResolver = (err, ctx) => handler(err, ctx);
623
- if (this.server !== null) {
624
- this.globalPipeline = composeMiddlewareDispatcher(this.middlewares, this.errorResolver);
625
- }
626
- return this;
627
- }
628
- /** @internal */
629
- _register(method, pattern, args) {
630
- return this._registerMethods([method], pattern, args);
631
- }
632
- /** @internal */
633
- _registerMethods(methods, pattern, args) {
634
- const { middlewares, handler } = parseRouteArgs(args);
635
- const responseHandler = (ctx) => toResponse(handler(ctx));
636
- const finalHandler = middlewares.length === 0
637
- ? responseHandler
638
- : composeMiddlewares(middlewares, responseHandler);
639
- const canonicalPattern = canonicalizeRoutePattern(pattern);
640
- validateRoutePattern(canonicalPattern);
641
- const routes = methods.map((method) => ({
642
- method,
643
- pattern: canonicalPattern,
644
- handler: finalHandler,
645
- }));
646
- this.trie.addBatch(routes);
647
- const needs = detectHandlerNeeds(handler);
648
- const hasRouteMws = middlewares.length > 0;
649
- for (const r of routes) {
650
- const variants = expandOptionalPatterns(r.pattern);
651
- for (const variant of variants) {
652
- this.nativeRoutes.push({
653
- method: r.method,
654
- pattern: variant,
655
- handler: r.handler,
656
- rawHandler: handler,
657
- isDynamic: variant.includes(":") || variant.includes("*"),
658
- isContextFree: needs.isContextFree && !hasRouteMws,
659
- isLightweight: needs.isLightweight && !hasRouteMws,
660
- isParamsOnly: needs.isParamsOnly && !hasRouteMws,
661
- isJsonBodyOnly: needs.isJsonBodyOnly && !hasRouteMws,
662
- hasRouteMiddlewares: hasRouteMws,
663
- });
664
- }
665
- }
666
- return this;
667
- }
668
- add(method, pattern, ...args) {
669
- return this._register(method, pattern, args);
670
- }
671
- get(pattern, ...args) {
672
- return this._register("GET", pattern, args);
673
- }
674
- post(pattern, ...args) {
675
- return this._register("POST", pattern, args);
676
- }
677
- put(pattern, ...args) {
678
- return this._register("PUT", pattern, args);
679
- }
680
- delete(pattern, ...args) {
681
- return this._register("DELETE", pattern, args);
682
- }
683
- patch(pattern, ...args) {
684
- return this._register("PATCH", pattern, args);
685
- }
686
- head(pattern, ...args) {
687
- return this._register("HEAD", pattern, args);
688
- }
689
- options(pattern, ...args) {
690
- return this._register("OPTIONS", pattern, args);
691
- }
692
- all(pattern, ...args) {
693
- return this._registerMethods(METHODS, pattern, args);
694
- }
695
- ws(pattern, handlers, beforeUpgrade) {
696
- this.hasWebSockets = true;
697
- return this._registerMethods(["GET"], pattern, [
698
- async (ctx) => {
699
- if (ctx.req.headers.get("upgrade")?.toLowerCase() !== "websocket") {
700
- return new Response("Upgrade Required", { status: 426 });
701
- }
702
- let customData;
703
- if (beforeUpgrade) {
704
- const check = await beforeUpgrade(ctx);
705
- if (!check)
706
- return new Response("Unauthorized", { status: 401 });
707
- if (typeof check === "object" && check !== null && "data" in check) {
708
- customData = check.data;
709
- }
710
- }
711
- const upgradeData = customData !== undefined &&
712
- typeof customData === "object" &&
713
- customData !== null
714
- ? { ...customData, handlers, _barqInternal: true }
715
- : { handlers, _barqInternal: true, data: customData };
716
- const upgraded = ctx.upgrade(upgradeData);
717
- return upgraded
718
- ? new Response(null)
719
- : new Response("WebSocket upgrade failed", { status: 400 });
720
- },
721
- ]);
722
- }
723
- group(prefix, ...args) {
724
- const last = args[args.length - 1];
725
- if (typeof last !== "function") {
726
- throw new Error("Route group callback must be a function");
727
- }
728
- const callback = last;
729
- const groupMws = [];
730
- for (let i = 0; i < args.length - 1; i++) {
731
- const item = args[i];
732
- if (Array.isArray(item)) {
733
- for (let j = 0; j < item.length; j++)
734
- groupMws.push(item[j]);
735
- }
736
- else if (item && typeof item === "object") {
737
- groupMws.push(item);
738
- }
739
- }
740
- const normalizedPrefix = normalizePrefix(prefix);
741
- const group = new RouteGroup(this, normalizedPrefix, groupMws);
742
- callback(group);
743
- return this;
744
- }
745
- use(...middlewares) {
746
- for (let i = 0; i < middlewares.length; i++) {
747
- this.middlewares.push(middlewares[i]);
748
- }
749
- this.hasMiddlewares = this.middlewares.length > 0;
750
- if (this.server !== null) {
751
- this.globalPipeline = composeMiddlewareDispatcher(this.middlewares, this.errorResolver);
752
- }
753
- return this;
754
- }
755
- listen(port) {
756
- const listenPort = port ?? this._options.port ?? 0;
757
- if (!Number.isInteger(listenPort) || listenPort < 0 || listenPort > 65535) {
758
- throw new RangeError("port must be an integer between 0 and 65535");
759
- }
760
- if (this.stopPromise !== null) {
761
- throw new Error("App cannot listen while shutdown is in progress");
762
- }
763
- if (this.server !== null) {
764
- throw new Error("App is already listening");
765
- }
766
- const userWs = this._options.websocket;
767
- this.globalPipeline = composeMiddlewareDispatcher(this.middlewares, this.errorResolver);
768
- const contextApplication = this;
769
- const opts = {
770
- fetch: this.fetchHandler,
771
- development: this.development,
772
- };
773
- if (this._options.idleTimeout !== undefined) {
774
- opts.idleTimeout = this._options.idleTimeout;
775
- }
776
- if (this._options.maxRequestBodySize !== undefined) {
777
- opts.maxRequestBodySize = this._options.maxRequestBodySize;
778
- }
779
- opts.error = (err) => errorToResponse(err, this.development);
780
- if (Bun.env.BARQ_IS_WORKER === "true")
781
- opts.reusePort = true;
782
- if (this.hasWebSockets || userWs !== undefined) {
783
- opts.websocket = {
784
- ...userWs,
785
- message: (ws, message) => {
786
- const internal = getInternalWebSocketHandlers(ws);
787
- dispatchWebSocketCallback(internal?.message, [ws, message], (err) => reportWebSocketError(internal?.error, ws, err));
788
- dispatchWebSocketCallback(userWs?.message, [ws, message], (err) => reportWebSocketError(userWs?.error, ws, err));
789
- },
790
- open: (ws) => {
791
- const internal = getInternalWebSocketHandlers(ws);
792
- dispatchWebSocketCallback(internal?.open, [ws], (err) => reportWebSocketError(internal?.error, ws, err));
793
- dispatchWebSocketCallback(userWs?.open, [ws], (err) => reportWebSocketError(userWs?.error, ws, err));
794
- },
795
- close: (ws, code, reason) => {
796
- const internal = getInternalWebSocketHandlers(ws);
797
- dispatchWebSocketCallback(internal?.close, [ws, code, reason], (err) => reportWebSocketError(internal?.error, ws, err));
798
- dispatchWebSocketCallback(userWs?.close, [ws, code, reason], (err) => reportWebSocketError(userWs?.error, ws, err));
799
- },
800
- drain: (ws) => {
801
- const internal = getInternalWebSocketHandlers(ws);
802
- dispatchWebSocketCallback(internal?.drain, [ws], (err) => reportWebSocketError(internal?.error, ws, err));
803
- dispatchWebSocketCallback(userWs?.drain, [ws], (err) => reportWebSocketError(userWs?.error, ws, err));
804
- },
805
- error: (ws, err) => {
806
- const internal = getInternalWebSocketHandlers(ws);
807
- if (internal?.error === undefined && userWs?.error === undefined) {
808
- console.error("Unhandled WebSocket error:", err);
809
- return;
810
- }
811
- if (internal?.error)
812
- reportWebSocketError(internal.error, ws, err);
813
- if (userWs?.error)
814
- reportWebSocketError(userWs.error, ws, err);
815
- },
816
- };
817
- }
818
- if (this.nativeRoutes.length > 0) {
819
- const systemRoutes = Object.create(null);
820
- const hasGlobalPipeline = this.hasMiddlewares || this.errorResolver !== undefined;
821
- const routesByPattern = new Map();
822
- for (const nr of this.nativeRoutes) {
823
- let list = routesByPattern.get(nr.pattern);
824
- if (!list) {
825
- list = [];
826
- routesByPattern.set(nr.pattern, list);
827
- }
828
- list.push(nr);
829
- }
830
- for (const [pattern, list] of routesByPattern) {
831
- const isDynamic = pattern.includes(":") || pattern.includes("*");
832
- const methodMap = Object.create(null);
833
- for (const nr of list) {
834
- const h = nr.handler;
835
- const isDyn = nr.isDynamic;
836
- const raw = nr.rawHandler;
837
- if (hasGlobalPipeline || nr.hasRouteMiddlewares) {
838
- methodMap[nr.method] = (req) => {
839
- try {
840
- const params = isDyn
841
- ? (req
842
- .params ?? EMPTY_PARAMS)
843
- : EMPTY_PARAMS;
844
- const ctx = new Context(req, this.server, params, contextApplication);
845
- const result = this.globalPipeline(ctx, h);
846
- return finalizeResponse(ctx, result, req.method === "HEAD");
847
- }
848
- catch (err) {
849
- return errorToResponse(err, this.development);
850
- }
851
- };
852
- }
853
- else if (nr.isContextFree && raw) {
854
- const analysis = analyzeHandler(raw);
855
- const literal = analysis.literal;
856
- if (literal !== null) {
857
- if (literal.kind === "string") {
858
- methodMap[nr.method] = new Response(literal.value);
859
- if (nr.method === "GET" && !isDyn)
860
- methodMap.HEAD = new Response(null);
861
- }
862
- else {
863
- methodMap[nr.method] = new Response(literal.value, {
864
- headers: FAST_JSON_HEADERS,
865
- });
866
- if (nr.method === "GET" && !isDyn)
867
- methodMap.HEAD = new Response(null, {
868
- headers: FAST_JSON_HEADERS,
869
- });
870
- }
871
- }
872
- else {
873
- methodMap[nr.method] = () => {
874
- const res = raw();
875
- if (res instanceof Response)
876
- return res;
877
- if (isThenable(res)) {
878
- return Promise.resolve(res).then((r) => {
879
- if (r instanceof Response)
880
- return r;
881
- if (typeof r === "object" && r !== null)
882
- return Response.json(r);
883
- if (typeof r === "string")
884
- return new Response(r, { headers: FAST_TEXT_HEADERS });
885
- return finalizeValue(r);
886
- });
887
- }
888
- if (typeof res === "object" && res !== null)
889
- return Response.json(res);
890
- if (typeof res === "string")
891
- return new Response(res, { headers: FAST_TEXT_HEADERS });
892
- return Response.json(res);
893
- };
894
- if (nr.method === "GET" && !isDyn) {
895
- methodMap.HEAD = () => {
896
- const res = raw();
897
- const r = res instanceof Response
898
- ? res
899
- : finalizeValue(res);
900
- return stripBody(r);
901
- };
902
- }
903
- }
904
- }
905
- else if (nr.isJsonBodyOnly && raw) {
906
- methodMap[nr.method] = (req) => new Response(req.body, { headers: FAST_JSON_HEADERS });
907
- if (nr.method === "GET" && !isDyn) {
908
- methodMap.HEAD = (req) => req.json().then(() => new Response(null, { headers: FAST_JSON_HEADERS }), (err) => errorToResponse(err, this.development));
909
- }
910
- }
911
- else if (nr.isParamsOnly && raw) {
912
- const analysis = analyzeHandler(raw);
913
- if (analysis.paramSerializer &&
914
- raw.constructor.name !== "AsyncFunction") {
915
- const serializer = analysis.paramSerializer;
916
- const responseInit = { headers: FAST_JSON_HEADERS };
917
- methodMap[nr.method] = (req) => {
918
- const params = req.params ?? EMPTY_PARAMS;
919
- return new Response(serializer(params), responseInit);
920
- };
921
- }
922
- else {
923
- methodMap[nr.method] = (req) => {
924
- const p = req.params;
925
- const res = raw((p ? req : { params: EMPTY_PARAMS, req }));
926
- if (typeof res === "object" && res !== null) {
927
- if (res instanceof Response)
928
- return res;
929
- return Response.json(res);
930
- }
931
- if (typeof res === "string")
932
- return new Response(res, { headers: FAST_TEXT_HEADERS });
933
- if (isThenable(res)) {
934
- return Promise.resolve(res).then((r) => {
935
- if (r instanceof Response)
936
- return r;
937
- if (typeof r === "object" && r !== null)
938
- return new Response(JSON.stringify(r), {
939
- headers: FAST_JSON_HEADERS,
940
- });
941
- if (typeof r === "string")
942
- return new Response(r, { headers: FAST_TEXT_HEADERS });
943
- return finalizeValue(r);
944
- });
945
- }
946
- return new Response(JSON.stringify(res), {
947
- headers: FAST_JSON_HEADERS,
948
- });
949
- };
950
- if (nr.method === "GET" && !isDyn)
951
- methodMap.HEAD = () => new Response(null, { headers: FAST_JSON_HEADERS });
952
- }
953
- }
954
- else if (nr.isLightweight && raw) {
955
- const isAsync = raw.constructor.name === "AsyncFunction";
956
- const reusedLightCtx = new FastLightContext(null, EMPTY_PARAMS);
957
- let lightCtxInUse = false;
958
- methodMap[nr.method] = (req) => {
959
- const params = isDyn
960
- ? (req
961
- .params ?? EMPTY_PARAMS)
962
- : EMPTY_PARAMS;
963
- const useFresh = isAsync || lightCtxInUse;
964
- const ctx = useFresh
965
- ? new FastLightContext(req, params)
966
- : reusedLightCtx.init(req, params);
967
- if (!useFresh)
968
- lightCtxInUse = true;
969
- let res;
970
- try {
971
- res = raw(ctx);
972
- }
973
- catch (err) {
974
- if (!useFresh)
975
- lightCtxInUse = false;
976
- return errorToResponse(err, this.development);
977
- }
978
- if (typeof res === "object" && res !== null) {
979
- if (res instanceof Response) {
980
- if (!useFresh)
981
- lightCtxInUse = false;
982
- return res;
983
- }
984
- if (!isThenable(res)) {
985
- if (!useFresh)
986
- lightCtxInUse = false;
987
- return Response.json(res);
988
- }
989
- }
990
- else if (typeof res === "string") {
991
- if (!useFresh)
992
- lightCtxInUse = false;
993
- return new Response(res, { headers: FAST_TEXT_HEADERS });
994
- }
995
- else if (!isThenable(res)) {
996
- if (!useFresh)
997
- lightCtxInUse = false;
998
- return new Response(JSON.stringify(res), {
999
- headers: FAST_JSON_HEADERS,
1000
- });
1001
- }
1002
- return Promise.resolve(res)
1003
- .then((r) => {
1004
- if (!useFresh)
1005
- lightCtxInUse = false;
1006
- if (r instanceof Response)
1007
- return r;
1008
- if (typeof r === "object" && r !== null)
1009
- return new Response(JSON.stringify(r), {
1010
- headers: FAST_JSON_HEADERS,
1011
- });
1012
- if (typeof r === "string")
1013
- return new Response(r, { headers: FAST_TEXT_HEADERS });
1014
- return finalizeValue(r);
1015
- })
1016
- .catch((err) => {
1017
- if (!useFresh)
1018
- lightCtxInUse = false;
1019
- return errorToResponse(err, this.development);
1020
- });
1021
- };
1022
- if (nr.method === "GET" && !isDyn)
1023
- methodMap.HEAD = () => new Response(null, { headers: FAST_JSON_HEADERS });
1024
- }
1025
- else {
1026
- methodMap[nr.method] = (req) => {
1027
- const isHead = nr.method === "GET" && req.method === "HEAD";
1028
- try {
1029
- const params = isDyn
1030
- ? (req
1031
- .params ?? EMPTY_PARAMS)
1032
- : EMPTY_PARAMS;
1033
- const ctx = new Context(req, this.server, params, contextApplication);
1034
- const res = raw ? raw(ctx) : h(ctx);
1035
- if (res instanceof Response) {
1036
- if (!isHead && !ctx._hasPendingCookies)
1037
- return res;
1038
- return finalizeResponse(ctx, res, isHead);
1039
- }
1040
- if (!isThenable(res))
1041
- return finalizeResponse(ctx, toResponse(res), isHead);
1042
- return Promise.resolve(res)
1043
- .then((value) => finalizeResponse(ctx, toResponse(value), isHead))
1044
- .catch((err) => finalizeResponse(ctx, errorToResponse(err, this.development), isHead));
1045
- }
1046
- catch (err) {
1047
- return errorToResponse(err, this.development);
1048
- }
1049
- };
1050
- }
1051
- }
1052
- const allowMethods = Object.keys(methodMap);
1053
- const allowHeader = allowMethods.includes("GET") && !allowMethods.includes("HEAD")
1054
- ? [...allowMethods, "HEAD"].join(", ")
1055
- : allowMethods.join(", ");
1056
- if (isDynamic) {
1057
- const getFn = methodMap.GET;
1058
- if (allowMethods.length === 1 &&
1059
- typeof getFn === "function" &&
1060
- allowMethods[0] === "GET") {
1061
- systemRoutes[pattern] = methodMap;
1062
- }
1063
- else {
1064
- systemRoutes[pattern] = (req) => {
1065
- const matched = methodMap[req.method];
1066
- if (typeof matched === "function")
1067
- return matched(req);
1068
- if (matched instanceof Response)
1069
- return req.method === "HEAD" ? stripBody(matched) : matched;
1070
- if (req.method === "HEAD") {
1071
- const gh = methodMap.GET;
1072
- if (typeof gh === "function") {
1073
- const res = gh(req);
1074
- return isThenable(res)
1075
- ? res.then(stripBody)
1076
- : stripBody(res);
1077
- }
1078
- }
1079
- return new Response(`{"error":"Method Not Allowed"}`, {
1080
- status: 405,
1081
- headers: {
1082
- "content-type": "application/json; charset=utf-8",
1083
- Allow: allowHeader,
1084
- },
1085
- });
1086
- };
1087
- }
1088
- }
1089
- else {
1090
- systemRoutes[pattern] = methodMap;
1091
- }
1092
- }
1093
- opts.routes = systemRoutes;
1094
- }
1095
- opts.port = listenPort;
1096
- if (this._options.hostname !== undefined)
1097
- opts.hostname = this._options.hostname;
1098
- if (this._options.tls !== undefined)
1099
- opts.tls = this._options.tls;
1100
- this.server = Bun.serve(opts);
1101
- return this.server;
1102
- }
1103
- handle(req, srv) {
1104
- const isHead = req.method === "HEAD";
1105
- const serverInstance = srv ?? this.server;
1106
- const contextApplication = this;
1107
- let currentCtx = null;
1108
- let pathname;
1109
- try {
1110
- pathname = scanPathname(req.url);
1111
- const matched = this.trie.match(req.method, pathname);
1112
- if (matched === null) {
1113
- if (this.notFoundHandler === null &&
1114
- !this.hasMiddlewares &&
1115
- this.errorResolver === undefined) {
1116
- return new Response('{"error":"Not Found"}', {
1117
- status: 404,
1118
- headers: { "content-type": "application/json; charset=utf-8" },
1119
- });
1120
- }
1121
- currentCtx = new Context(req, serverInstance, EMPTY_PARAMS, contextApplication, pathname);
1122
- const notFoundHandler = this.notFoundHandler !== null
1123
- ? this.notFoundHandler
1124
- : () => new Response('{"error":"Not Found"}', {
1125
- status: 404,
1126
- headers: {
1127
- "content-type": "application/json; charset=utf-8",
1128
- },
1129
- });
1130
- const result = this.globalPipeline(currentCtx, notFoundHandler);
1131
- return finalizeResponse(currentCtx, result, isHead);
1132
- }
1133
- if (!this.hasMiddlewares && this.errorResolver === undefined) {
1134
- if (typeof matched === "function") {
1135
- if (matched.length === 0) {
1136
- const res = matched();
1137
- if (!isHead && !isThenable(res))
1138
- return res;
1139
- if (isThenable(res)) {
1140
- return res.then((r) => (isHead ? stripBody(r) : r), (err) => errorToResponse(err, this.development));
1141
- }
1142
- return isHead ? stripBody(res) : res;
1143
- }
1144
- currentCtx = new Context(req, serverInstance, EMPTY_PARAMS, contextApplication, pathname);
1145
- const res = matched(currentCtx);
1146
- if (!isHead && !isThenable(res)) {
1147
- if (typeof res === "object" &&
1148
- res !== null &&
1149
- !(res instanceof Response)) {
1150
- return new Response(JSON.stringify(res), {
1151
- headers: FAST_JSON_HEADERS,
1152
- });
1153
- }
1154
- if (!currentCtx._hasPendingCookies)
1155
- return res;
1156
- }
1157
- return finalizeResponse(currentCtx, guardRejections(res, this.development), isHead);
1158
- }
1159
- const handler = typeof matched === "object" && MATCH_HANDLER in matched
1160
- ? matched[MATCH_HANDLER]
1161
- : undefined;
1162
- if (handler !== undefined) {
1163
- const params = matched;
1164
- currentCtx = new Context(req, serverInstance, params, contextApplication, pathname);
1165
- const res = handler(currentCtx);
1166
- if (!isHead && !isThenable(res)) {
1167
- if (!currentCtx._hasPendingCookies)
1168
- return res;
1169
- }
1170
- return finalizeResponse(currentCtx, guardRejections(res, this.development), isHead);
1171
- }
1172
- }
1173
- if (typeof matched === "function") {
1174
- currentCtx = new Context(req, serverInstance, EMPTY_PARAMS, contextApplication, pathname);
1175
- const result = this.globalPipeline(currentCtx, matched);
1176
- return finalizeResponse(currentCtx, result, isHead);
1177
- }
1178
- if (MATCH_NOT_ALLOWED in matched) {
1179
- const allow = matched[MATCH_NOT_ALLOWED];
1180
- if (this.errorHandler === null &&
1181
- !this.hasMiddlewares &&
1182
- this.errorResolver === undefined) {
1183
- return new Response('{"error":"Method Not Allowed"}', {
1184
- status: 405,
1185
- headers: {
1186
- "content-type": "application/json; charset=utf-8",
1187
- Allow: allow,
1188
- },
1189
- });
1190
- }
1191
- currentCtx = new Context(req, serverInstance, EMPTY_PARAMS, contextApplication, pathname);
1192
- const notAllowedHandler = () => errorToResponse(new HttpError(405, { error: "Method Not Allowed" }, { Allow: allow }), this.development);
1193
- const result = this.globalPipeline(currentCtx, notAllowedHandler);
1194
- return finalizeResponse(currentCtx, result, isHead);
1195
- }
1196
- const params = matched;
1197
- const handler = params[MATCH_HANDLER];
1198
- currentCtx = new Context(req, serverInstance, params, contextApplication, pathname);
1199
- const result = this.globalPipeline(currentCtx, handler);
1200
- return finalizeResponse(currentCtx, result, isHead);
1201
- }
1202
- catch (err) {
1203
- const activeCtx = currentCtx ??
1204
- new Context(req, serverInstance, EMPTY_PARAMS, contextApplication, pathname);
1205
- if (this.errorHandler !== null) {
1206
- try {
1207
- const custom = this.errorHandler(err, activeCtx);
1208
- const result = isThenable(custom)
1209
- ? custom.then((r) => unwindAfter(this.middlewares, activeCtx, r, 0, this.errorResolver))
1210
- : unwindAfter(this.middlewares, activeCtx, custom, 0, this.errorResolver);
1211
- return finalizeResponse(activeCtx, result, isHead);
1212
- }
1213
- catch (e2) {
1214
- const fallback = errorToResponse(e2, this.development);
1215
- const result = unwindAfter(this.middlewares, activeCtx, fallback, 0, this.errorResolver);
1216
- return finalizeResponse(activeCtx, result, isHead);
1217
- }
1218
- }
1219
- const routingErr = errorToResponse(err, this.development);
1220
- const result = unwindAfter(this.middlewares, activeCtx, routingErr, 0, this.errorResolver);
1221
- return finalizeResponse(activeCtx, result, isHead);
1222
- }
1223
- }
1224
- cluster(port) {
1225
- const listenPort = port ?? this._options.port ?? 3000;
1226
- const isWorker = Bun.env.BARQ_IS_WORKER === "true";
1227
- if (!Number.isInteger(listenPort) || listenPort < 0 || listenPort > 65535) {
1228
- throw new RangeError("port must be an integer between 0 and 65535");
1229
- }
1230
- if (isWorker || process.platform !== "linux") {
1231
- return this.listen(listenPort);
1232
- }
1233
- if (listenPort === 0) {
1234
- throw new RangeError("cluster requires a fixed, non-zero port");
1235
- }
1236
- if (this.clusterProcesses !== null || this.server !== null) {
1237
- throw new Error("App is already listening or clustering");
1238
- }
1239
- const entry = process.argv[1];
1240
- if (!entry) {
1241
- throw new Error("Cannot start cluster without an application entry file");
1242
- }
1243
- const processes = [];
1244
- const spawnWorker = () => {
1245
- const proc = Bun.spawn([process.execPath, "run", entry], {
1246
- env: {
1247
- ...process.env,
1248
- BARQ_IS_WORKER: "true",
1249
- PORT: String(listenPort),
1250
- },
1251
- stdio: ["inherit", "inherit", "inherit"],
1252
- });
1253
- proc.exited.then((_code) => {
1254
- if (this.stopPromise === null && this.clusterProcesses !== null) {
1255
- const idx = processes.indexOf(proc);
1256
- if (idx !== -1) {
1257
- processes.splice(idx, 1);
1258
- }
1259
- processes.push(spawnWorker());
1260
- }
1261
- });
1262
- return proc;
1263
- };
1264
- try {
1265
- for (let i = 0; i < cpus().length; i++) {
1266
- processes.push(spawnWorker());
1267
- }
1268
- }
1269
- catch (error) {
1270
- for (const process of processes)
1271
- process.kill("SIGTERM");
1272
- throw error;
1273
- }
1274
- this.clusterProcesses = processes;
1275
- return;
1276
- }
1277
- async stop(hardTimeoutMs = 5000) {
1278
- if (!Number.isFinite(hardTimeoutMs) || hardTimeoutMs < 0) {
1279
- throw new RangeError("hardTimeoutMs must be a finite non-negative number");
1280
- }
1281
- if (this.stopPromise !== null) {
1282
- return this.stopPromise;
1283
- }
1284
- const server = this.server;
1285
- const clusterProcesses = this.clusterProcesses;
1286
- this.server = null;
1287
- this.clusterProcesses = null;
1288
- if (server === null && clusterProcesses === null) {
1289
- return { forced: false, pending: 0 };
1290
- }
1291
- this.stopPromise = (async () => {
1292
- let serverResult = { forced: false, pending: 0 };
1293
- let workersForced = false;
1294
- try {
1295
- if (server !== null) {
1296
- serverResult = await stopServer(server, hardTimeoutMs);
1297
- }
1298
- }
1299
- finally {
1300
- if (clusterProcesses !== null) {
1301
- workersForced = await stopClusterProcesses(clusterProcesses, hardTimeoutMs);
1302
- }
1303
- }
1304
- return {
1305
- forced: serverResult.forced || workersForced,
1306
- pending: serverResult.pending,
1307
- };
1308
- })();
1309
- try {
1310
- return await this.stopPromise;
1311
- }
1312
- finally {
1313
- this.stopPromise = null;
1314
- }
1315
- }
1316
- }
1317
- function guardRejections(res, development) {
1318
- return isThenable(res)
1319
- ? res.catch((err) => errorToResponse(err, development))
1320
- : res;
1321
- }
1322
- function applyCookies(ctx, res) {
1323
- if (!ctx._hasPendingCookies)
1324
- return res;
1325
- const cookies = ctx._cookieJarIfCreated();
1326
- if (cookies === null)
1327
- return res;
1328
- const headers = new Headers(res.headers);
1329
- applyPendingCookies(cookies, headers);
1330
- ctx._hasPendingCookies = false;
1331
- return new Response(res.body, {
1332
- status: res.status,
1333
- statusText: res.statusText,
1334
- headers,
1335
- });
1336
- }
1337
- function finalizeResponse(ctx, res, isHead) {
1338
- if (res instanceof Response) {
1339
- if (!isHead && !ctx._hasPendingCookies)
1340
- return res;
1341
- const withCookies = applyCookies(ctx, res);
1342
- return isHead ? stripBody(withCookies) : withCookies;
1343
- }
1344
- if (isThenable(res)) {
1345
- return res.then((response) => {
1346
- const withCookies = applyCookies(ctx, response);
1347
- return isHead ? stripBody(withCookies) : withCookies;
1348
- });
1349
- }
1350
- if (!isHead) {
1351
- if (!ctx._hasPendingCookies)
1352
- return res;
1353
- return applyCookies(ctx, res);
1354
- }
1355
- return stripBody(applyCookies(ctx, res));
1356
- }
1357
- function stripBody(res) {
1358
- if (res.body === null)
1359
- return res;
1360
- try {
1361
- res.body.cancel?.().catch?.(() => { });
1362
- }
1363
- catch { }
1364
- return new Response(null, {
1365
- status: res.status,
1366
- statusText: res.statusText,
1367
- headers: res.headers,
1368
- });
1369
- }
1370
- //# sourceMappingURL=app.js.map