@gravito/core 1.2.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -6
- package/README.zh-TW.md +197 -6
- package/dist/compat-C4Src6NN.d.cts +357 -0
- package/dist/compat-C4Src6NN.d.ts +357 -0
- package/dist/compat.d.cts +1 -314
- package/dist/compat.d.ts +1 -314
- package/dist/engine/index.cjs +434 -143
- package/dist/engine/index.d.cts +195 -100
- package/dist/engine/index.d.ts +195 -100
- package/dist/engine/index.js +434 -143
- package/dist/index.cjs +8276 -3222
- package/dist/index.d.cts +7113 -1002
- package/dist/index.d.ts +7113 -1002
- package/dist/index.js +8235 -3219
- package/package.json +60 -6
package/dist/engine/index.cjs
CHANGED
|
@@ -239,13 +239,25 @@ var RadixRouter = class _RadixRouter {
|
|
|
239
239
|
// src/engine/AOTRouter.ts
|
|
240
240
|
var AOTRouter = class {
|
|
241
241
|
// Static route cache: "METHOD:PATH" -> RouteMetadata
|
|
242
|
+
/** @internal */
|
|
242
243
|
staticRoutes = /* @__PURE__ */ new Map();
|
|
243
244
|
// Dynamic route handler (Radix Tree)
|
|
244
245
|
dynamicRouter = new RadixRouter();
|
|
246
|
+
// Store all route definitions to support mounting/merging
|
|
247
|
+
/** @internal */
|
|
248
|
+
routeDefinitions = [];
|
|
245
249
|
// Global middleware (applies to all routes)
|
|
250
|
+
/** @internal */
|
|
246
251
|
globalMiddleware = [];
|
|
247
252
|
// Path-based middleware: pattern -> middleware[]
|
|
253
|
+
/** @internal */
|
|
248
254
|
pathMiddleware = /* @__PURE__ */ new Map();
|
|
255
|
+
// Dynamic route patterns: handler function -> route pattern
|
|
256
|
+
// 用於追蹤動態路由的模式,防止高基數問題
|
|
257
|
+
dynamicRoutePatterns = /* @__PURE__ */ new Map();
|
|
258
|
+
middlewareCache = /* @__PURE__ */ new Map();
|
|
259
|
+
cacheMaxSize = 1e3;
|
|
260
|
+
version = 0;
|
|
249
261
|
/**
|
|
250
262
|
* Register a route
|
|
251
263
|
*
|
|
@@ -259,6 +271,7 @@ var AOTRouter = class {
|
|
|
259
271
|
* @param middleware - Route-specific middleware
|
|
260
272
|
*/
|
|
261
273
|
add(method, path, handler, middleware = []) {
|
|
274
|
+
this.routeDefinitions.push({ method, path, handler, middleware });
|
|
262
275
|
const normalizedMethod = method.toLowerCase();
|
|
263
276
|
if (this.isStaticPath(path)) {
|
|
264
277
|
const key = `${normalizedMethod}:${path}`;
|
|
@@ -266,11 +279,47 @@ var AOTRouter = class {
|
|
|
266
279
|
} else {
|
|
267
280
|
const wrappedHandler = handler;
|
|
268
281
|
this.dynamicRouter.add(normalizedMethod, path, [wrappedHandler]);
|
|
282
|
+
this.dynamicRoutePatterns.set(wrappedHandler, path);
|
|
269
283
|
if (middleware.length > 0) {
|
|
270
284
|
this.pathMiddleware.set(`${normalizedMethod}:${path}`, middleware);
|
|
271
285
|
}
|
|
272
286
|
}
|
|
273
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* Mount another router at a prefix
|
|
290
|
+
*/
|
|
291
|
+
mount(prefix, other) {
|
|
292
|
+
if (other.globalMiddleware.length > 0) {
|
|
293
|
+
this.usePattern(prefix, ...other.globalMiddleware);
|
|
294
|
+
const wildcard = prefix === "/" ? "/*" : `${prefix}/*`;
|
|
295
|
+
this.usePattern(wildcard, ...other.globalMiddleware);
|
|
296
|
+
}
|
|
297
|
+
for (const [pattern, mws] of other.pathMiddleware) {
|
|
298
|
+
if (pattern.includes(":")) {
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
let newPattern;
|
|
302
|
+
if (pattern === "*") {
|
|
303
|
+
newPattern = prefix === "/" ? "/*" : `${prefix}/*`;
|
|
304
|
+
} else if (pattern.startsWith("/")) {
|
|
305
|
+
newPattern = prefix === "/" ? pattern : `${prefix}${pattern}`;
|
|
306
|
+
} else {
|
|
307
|
+
newPattern = prefix === "/" ? `/${pattern}` : `${prefix}/${pattern}`;
|
|
308
|
+
}
|
|
309
|
+
this.usePattern(newPattern, ...mws);
|
|
310
|
+
}
|
|
311
|
+
for (const def of other.routeDefinitions) {
|
|
312
|
+
let newPath;
|
|
313
|
+
if (prefix === "/") {
|
|
314
|
+
newPath = def.path;
|
|
315
|
+
} else if (def.path === "/") {
|
|
316
|
+
newPath = prefix;
|
|
317
|
+
} else {
|
|
318
|
+
newPath = `${prefix}${def.path}`;
|
|
319
|
+
}
|
|
320
|
+
this.add(def.method, newPath, def.handler, def.middleware);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
274
323
|
/**
|
|
275
324
|
* Add global middleware
|
|
276
325
|
*
|
|
@@ -280,6 +329,7 @@ var AOTRouter = class {
|
|
|
280
329
|
*/
|
|
281
330
|
use(...middleware) {
|
|
282
331
|
this.globalMiddleware.push(...middleware);
|
|
332
|
+
this.version++;
|
|
283
333
|
}
|
|
284
334
|
/**
|
|
285
335
|
* Add path-based middleware
|
|
@@ -290,8 +340,13 @@ var AOTRouter = class {
|
|
|
290
340
|
* @param middleware - Middleware functions
|
|
291
341
|
*/
|
|
292
342
|
usePattern(pattern, ...middleware) {
|
|
293
|
-
|
|
294
|
-
|
|
343
|
+
if (pattern === "*") {
|
|
344
|
+
this.globalMiddleware.push(...middleware);
|
|
345
|
+
} else {
|
|
346
|
+
const existing = this.pathMiddleware.get(pattern) ?? [];
|
|
347
|
+
this.pathMiddleware.set(pattern, [...existing, ...middleware]);
|
|
348
|
+
}
|
|
349
|
+
this.version++;
|
|
295
350
|
}
|
|
296
351
|
/**
|
|
297
352
|
* Match a request to a route
|
|
@@ -310,18 +365,22 @@ var AOTRouter = class {
|
|
|
310
365
|
return {
|
|
311
366
|
handler: staticRoute.handler,
|
|
312
367
|
params: {},
|
|
313
|
-
middleware: this.collectMiddleware(path, staticRoute.middleware)
|
|
368
|
+
middleware: this.collectMiddleware(path, staticRoute.middleware),
|
|
369
|
+
routePattern: path
|
|
314
370
|
};
|
|
315
371
|
}
|
|
316
372
|
const match = this.dynamicRouter.match(normalizedMethod, path);
|
|
317
373
|
if (match && match.handlers.length > 0) {
|
|
318
374
|
const handler = match.handlers[0];
|
|
319
|
-
const
|
|
375
|
+
const wrappedHandler = match.handlers[0];
|
|
376
|
+
const routePattern = this.dynamicRoutePatterns.get(wrappedHandler);
|
|
377
|
+
const routeKey = routePattern ? `${normalizedMethod}:${routePattern}` : null;
|
|
320
378
|
const routeMiddleware = routeKey ? this.pathMiddleware.get(routeKey) ?? [] : [];
|
|
321
379
|
return {
|
|
322
380
|
handler,
|
|
323
381
|
params: match.params,
|
|
324
|
-
middleware: this.collectMiddleware(path, routeMiddleware)
|
|
382
|
+
middleware: this.collectMiddleware(path, routeMiddleware),
|
|
383
|
+
routePattern
|
|
325
384
|
};
|
|
326
385
|
}
|
|
327
386
|
return {
|
|
@@ -349,13 +408,20 @@ var AOTRouter = class {
|
|
|
349
408
|
if (this.globalMiddleware.length === 0 && this.pathMiddleware.size === 0 && routeMiddleware.length === 0) {
|
|
350
409
|
return [];
|
|
351
410
|
}
|
|
411
|
+
const cacheKey = `${path}:${routeMiddleware.length}`;
|
|
412
|
+
const cached = this.middlewareCache.get(cacheKey);
|
|
413
|
+
if (cached !== void 0 && cached.version === this.version) {
|
|
414
|
+
return cached.data;
|
|
415
|
+
}
|
|
352
416
|
const middleware = [];
|
|
353
417
|
if (this.globalMiddleware.length > 0) {
|
|
354
418
|
middleware.push(...this.globalMiddleware);
|
|
355
419
|
}
|
|
356
420
|
if (this.pathMiddleware.size > 0) {
|
|
357
421
|
for (const [pattern, mw] of this.pathMiddleware) {
|
|
358
|
-
if (pattern.includes(":"))
|
|
422
|
+
if (pattern.includes(":")) {
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
359
425
|
if (this.matchPattern(pattern, path)) {
|
|
360
426
|
middleware.push(...mw);
|
|
361
427
|
}
|
|
@@ -364,6 +430,9 @@ var AOTRouter = class {
|
|
|
364
430
|
if (routeMiddleware.length > 0) {
|
|
365
431
|
middleware.push(...routeMiddleware);
|
|
366
432
|
}
|
|
433
|
+
if (this.middlewareCache.size < this.cacheMaxSize) {
|
|
434
|
+
this.middlewareCache.set(cacheKey, { data: middleware, version: this.version });
|
|
435
|
+
}
|
|
367
436
|
return middleware;
|
|
368
437
|
}
|
|
369
438
|
/**
|
|
@@ -384,32 +453,18 @@ var AOTRouter = class {
|
|
|
384
453
|
* @returns True if pattern matches
|
|
385
454
|
*/
|
|
386
455
|
matchPattern(pattern, path) {
|
|
387
|
-
if (pattern === "*")
|
|
388
|
-
|
|
456
|
+
if (pattern === "*") {
|
|
457
|
+
return true;
|
|
458
|
+
}
|
|
459
|
+
if (pattern === path) {
|
|
460
|
+
return true;
|
|
461
|
+
}
|
|
389
462
|
if (pattern.endsWith("/*")) {
|
|
390
463
|
const prefix = pattern.slice(0, -2);
|
|
391
464
|
return path.startsWith(prefix);
|
|
392
465
|
}
|
|
393
466
|
return false;
|
|
394
467
|
}
|
|
395
|
-
/**
|
|
396
|
-
* Find the original route key for a matched dynamic route
|
|
397
|
-
*
|
|
398
|
-
* This is needed to look up route-specific middleware.
|
|
399
|
-
* It's a bit of a hack, but avoids storing duplicate data.
|
|
400
|
-
*
|
|
401
|
-
* @param method - HTTP method
|
|
402
|
-
* @param path - Matched path
|
|
403
|
-
* @returns Route key or null
|
|
404
|
-
*/
|
|
405
|
-
findDynamicRouteKey(method, _path) {
|
|
406
|
-
for (const key of this.pathMiddleware.keys()) {
|
|
407
|
-
if (key.startsWith(`${method}:`)) {
|
|
408
|
-
return key;
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
return null;
|
|
412
|
-
}
|
|
413
468
|
/**
|
|
414
469
|
* Get all registered routes (for debugging)
|
|
415
470
|
*/
|
|
@@ -450,48 +505,111 @@ function getOptimalContextType(analysis) {
|
|
|
450
505
|
return "fast";
|
|
451
506
|
}
|
|
452
507
|
|
|
508
|
+
// src/engine/constants.ts
|
|
509
|
+
var encoder = new TextEncoder();
|
|
510
|
+
var CACHED_RESPONSES = {
|
|
511
|
+
NOT_FOUND: encoder.encode('{"error":"Not Found"}'),
|
|
512
|
+
INTERNAL_ERROR: encoder.encode('{"error":"Internal Server Error"}'),
|
|
513
|
+
OK: encoder.encode('{"ok":true}'),
|
|
514
|
+
EMPTY: new Uint8Array(0)
|
|
515
|
+
};
|
|
516
|
+
var HEADERS = {
|
|
517
|
+
JSON: { "Content-Type": "application/json; charset=utf-8" },
|
|
518
|
+
TEXT: { "Content-Type": "text/plain; charset=utf-8" },
|
|
519
|
+
HTML: { "Content-Type": "text/html; charset=utf-8" }
|
|
520
|
+
};
|
|
521
|
+
|
|
453
522
|
// src/engine/FastContext.ts
|
|
454
523
|
var FastRequestImpl = class {
|
|
455
524
|
_request;
|
|
456
525
|
_params;
|
|
457
|
-
|
|
458
|
-
|
|
526
|
+
_path;
|
|
527
|
+
_routePattern;
|
|
528
|
+
_url = null;
|
|
459
529
|
_query = null;
|
|
460
530
|
_headers = null;
|
|
531
|
+
_cachedJson = void 0;
|
|
532
|
+
_jsonParsed = false;
|
|
533
|
+
// Back-reference for release check optimization
|
|
534
|
+
_ctx;
|
|
535
|
+
constructor(ctx) {
|
|
536
|
+
this._ctx = ctx;
|
|
537
|
+
}
|
|
461
538
|
/**
|
|
462
|
-
*
|
|
539
|
+
* Initialize for new request
|
|
463
540
|
*/
|
|
464
|
-
|
|
541
|
+
init(request, params = {}, path = "", routePattern) {
|
|
465
542
|
this._request = request;
|
|
466
543
|
this._params = params;
|
|
467
|
-
this.
|
|
544
|
+
this._path = path;
|
|
545
|
+
this._routePattern = routePattern;
|
|
546
|
+
this._url = null;
|
|
468
547
|
this._query = null;
|
|
469
548
|
this._headers = null;
|
|
549
|
+
this._cachedJson = void 0;
|
|
550
|
+
this._jsonParsed = false;
|
|
551
|
+
return this;
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Reset for pooling
|
|
555
|
+
*/
|
|
556
|
+
reset() {
|
|
557
|
+
this._request = void 0;
|
|
558
|
+
this._params = void 0;
|
|
559
|
+
this._url = null;
|
|
560
|
+
this._query = null;
|
|
561
|
+
this._headers = null;
|
|
562
|
+
this._cachedJson = void 0;
|
|
563
|
+
this._jsonParsed = false;
|
|
564
|
+
}
|
|
565
|
+
checkReleased() {
|
|
566
|
+
if (this._ctx._isReleased) {
|
|
567
|
+
throw new Error(
|
|
568
|
+
"FastContext usage after release detected! (Object Pool Strict Lifecycle Guard)"
|
|
569
|
+
);
|
|
570
|
+
}
|
|
470
571
|
}
|
|
471
572
|
get url() {
|
|
573
|
+
this.checkReleased();
|
|
472
574
|
return this._request.url;
|
|
473
575
|
}
|
|
474
576
|
get method() {
|
|
577
|
+
this.checkReleased();
|
|
475
578
|
return this._request.method;
|
|
476
579
|
}
|
|
477
580
|
get path() {
|
|
478
|
-
|
|
581
|
+
this.checkReleased();
|
|
582
|
+
return this._path;
|
|
583
|
+
}
|
|
584
|
+
get routePattern() {
|
|
585
|
+
this.checkReleased();
|
|
586
|
+
return this._routePattern;
|
|
479
587
|
}
|
|
480
588
|
param(name) {
|
|
589
|
+
this.checkReleased();
|
|
481
590
|
return this._params[name];
|
|
482
591
|
}
|
|
483
592
|
params() {
|
|
593
|
+
this.checkReleased();
|
|
484
594
|
return { ...this._params };
|
|
485
595
|
}
|
|
596
|
+
getUrl() {
|
|
597
|
+
if (!this._url) {
|
|
598
|
+
this._url = new URL(this._request.url);
|
|
599
|
+
}
|
|
600
|
+
return this._url;
|
|
601
|
+
}
|
|
486
602
|
query(name) {
|
|
603
|
+
this.checkReleased();
|
|
487
604
|
if (!this._query) {
|
|
488
|
-
this._query = this.
|
|
605
|
+
this._query = this.getUrl().searchParams;
|
|
489
606
|
}
|
|
490
607
|
return this._query.get(name) ?? void 0;
|
|
491
608
|
}
|
|
492
609
|
queries() {
|
|
610
|
+
this.checkReleased();
|
|
493
611
|
if (!this._query) {
|
|
494
|
-
this._query = this.
|
|
612
|
+
this._query = this.getUrl().searchParams;
|
|
495
613
|
}
|
|
496
614
|
const result = {};
|
|
497
615
|
for (const [key, value] of this._query.entries()) {
|
|
@@ -507,9 +625,11 @@ var FastRequestImpl = class {
|
|
|
507
625
|
return result;
|
|
508
626
|
}
|
|
509
627
|
header(name) {
|
|
628
|
+
this.checkReleased();
|
|
510
629
|
return this._request.headers.get(name) ?? void 0;
|
|
511
630
|
}
|
|
512
631
|
headers() {
|
|
632
|
+
this.checkReleased();
|
|
513
633
|
if (!this._headers) {
|
|
514
634
|
this._headers = {};
|
|
515
635
|
for (const [key, value] of this._request.headers.entries()) {
|
|
@@ -519,41 +639,70 @@ var FastRequestImpl = class {
|
|
|
519
639
|
return { ...this._headers };
|
|
520
640
|
}
|
|
521
641
|
async json() {
|
|
522
|
-
|
|
642
|
+
this.checkReleased();
|
|
643
|
+
if (!this._jsonParsed) {
|
|
644
|
+
this._cachedJson = await this._request.json();
|
|
645
|
+
this._jsonParsed = true;
|
|
646
|
+
}
|
|
647
|
+
return this._cachedJson;
|
|
523
648
|
}
|
|
524
649
|
async text() {
|
|
650
|
+
this.checkReleased();
|
|
525
651
|
return this._request.text();
|
|
526
652
|
}
|
|
527
653
|
async formData() {
|
|
654
|
+
this.checkReleased();
|
|
528
655
|
return this._request.formData();
|
|
529
656
|
}
|
|
530
657
|
get raw() {
|
|
658
|
+
this.checkReleased();
|
|
531
659
|
return this._request;
|
|
532
660
|
}
|
|
533
661
|
};
|
|
534
662
|
var FastContext = class {
|
|
535
|
-
|
|
663
|
+
req = new FastRequestImpl(this);
|
|
536
664
|
// private _statusCode = 200
|
|
537
665
|
_headers = new Headers();
|
|
538
666
|
// Reuse this object
|
|
667
|
+
_isReleased = false;
|
|
668
|
+
// Made public for internal check access
|
|
539
669
|
/**
|
|
540
|
-
*
|
|
670
|
+
* Initialize context for a new request
|
|
541
671
|
*
|
|
542
672
|
* This is called when acquiring from the pool.
|
|
543
|
-
* Must clear all state from previous request.
|
|
544
673
|
*/
|
|
545
|
-
|
|
546
|
-
this.
|
|
674
|
+
init(request, params = {}, path = "", routePattern) {
|
|
675
|
+
this._isReleased = false;
|
|
676
|
+
this.req.init(request, params, path, routePattern);
|
|
547
677
|
this._headers = new Headers();
|
|
548
678
|
return this;
|
|
549
679
|
}
|
|
550
|
-
|
|
551
|
-
|
|
680
|
+
/**
|
|
681
|
+
* Reset context for pooling (Cleanup)
|
|
682
|
+
*
|
|
683
|
+
* This is called when releasing back to the pool.
|
|
684
|
+
* Implements "Deep-Reset Protocol" and "Release Guard".
|
|
685
|
+
*/
|
|
686
|
+
reset() {
|
|
687
|
+
this._isReleased = true;
|
|
688
|
+
this.req.reset();
|
|
689
|
+
this._store.clear();
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Check if context is released
|
|
693
|
+
*/
|
|
694
|
+
checkReleased() {
|
|
695
|
+
if (this._isReleased) {
|
|
696
|
+
throw new Error(
|
|
697
|
+
"FastContext usage after release detected! (Object Pool Strict Lifecycle Guard)"
|
|
698
|
+
);
|
|
699
|
+
}
|
|
552
700
|
}
|
|
553
701
|
// ─────────────────────────────────────────────────────────────────────────
|
|
554
702
|
// Response Helpers
|
|
555
703
|
// ─────────────────────────────────────────────────────────────────────────
|
|
556
704
|
json(data, status = 200) {
|
|
705
|
+
this.checkReleased();
|
|
557
706
|
this._headers.set("Content-Type", "application/json; charset=utf-8");
|
|
558
707
|
return new Response(JSON.stringify(data), {
|
|
559
708
|
status,
|
|
@@ -561,6 +710,7 @@ var FastContext = class {
|
|
|
561
710
|
});
|
|
562
711
|
}
|
|
563
712
|
text(text, status = 200) {
|
|
713
|
+
this.checkReleased();
|
|
564
714
|
this._headers.set("Content-Type", "text/plain; charset=utf-8");
|
|
565
715
|
return new Response(text, {
|
|
566
716
|
status,
|
|
@@ -568,6 +718,7 @@ var FastContext = class {
|
|
|
568
718
|
});
|
|
569
719
|
}
|
|
570
720
|
html(html, status = 200) {
|
|
721
|
+
this.checkReleased();
|
|
571
722
|
this._headers.set("Content-Type", "text/html; charset=utf-8");
|
|
572
723
|
return new Response(html, {
|
|
573
724
|
status,
|
|
@@ -575,6 +726,7 @@ var FastContext = class {
|
|
|
575
726
|
});
|
|
576
727
|
}
|
|
577
728
|
redirect(url, status = 302) {
|
|
729
|
+
this.checkReleased();
|
|
578
730
|
this._headers.set("Location", url);
|
|
579
731
|
return new Response(null, {
|
|
580
732
|
status,
|
|
@@ -582,28 +734,89 @@ var FastContext = class {
|
|
|
582
734
|
});
|
|
583
735
|
}
|
|
584
736
|
body(data, status = 200) {
|
|
737
|
+
this.checkReleased();
|
|
585
738
|
return new Response(data, {
|
|
586
739
|
status,
|
|
587
740
|
headers: this._headers
|
|
588
741
|
});
|
|
589
742
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
743
|
+
stream(stream, status = 200) {
|
|
744
|
+
this.checkReleased();
|
|
745
|
+
this._headers.set("Content-Type", "application/octet-stream");
|
|
746
|
+
return new Response(stream, {
|
|
747
|
+
status,
|
|
748
|
+
headers: this._headers
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
notFound(message = "Not Found") {
|
|
752
|
+
return this.text(message, 404);
|
|
753
|
+
}
|
|
754
|
+
forbidden(message = "Forbidden") {
|
|
755
|
+
return this.text(message, 403);
|
|
756
|
+
}
|
|
757
|
+
unauthorized(message = "Unauthorized") {
|
|
758
|
+
return this.text(message, 401);
|
|
759
|
+
}
|
|
760
|
+
badRequest(message = "Bad Request") {
|
|
761
|
+
return this.text(message, 400);
|
|
762
|
+
}
|
|
763
|
+
async forward(target, _options = {}) {
|
|
764
|
+
this.checkReleased();
|
|
765
|
+
const url = new URL(this.req.url);
|
|
766
|
+
const targetUrl = new URL(
|
|
767
|
+
target.startsWith("http") ? target : `${url.protocol}//${target}${this.req.path}`
|
|
768
|
+
);
|
|
769
|
+
const searchParams = new URLSearchParams(url.search);
|
|
770
|
+
searchParams.forEach((v, k) => {
|
|
771
|
+
targetUrl.searchParams.set(k, v);
|
|
772
|
+
});
|
|
773
|
+
return fetch(targetUrl.toString(), {
|
|
774
|
+
method: this.req.method,
|
|
775
|
+
headers: this.req.raw.headers,
|
|
776
|
+
body: this.req.method !== "GET" && this.req.method !== "HEAD" ? this.req.raw.body : null,
|
|
777
|
+
// @ts-expect-error - Bun/Fetch specific
|
|
778
|
+
duplex: "half"
|
|
779
|
+
});
|
|
780
|
+
}
|
|
593
781
|
header(name, value) {
|
|
594
|
-
this.
|
|
782
|
+
this.checkReleased();
|
|
783
|
+
if (value !== void 0) {
|
|
784
|
+
this._headers.set(name, value);
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
return this.req.header(name);
|
|
595
788
|
}
|
|
596
789
|
status(_code) {
|
|
790
|
+
this.checkReleased();
|
|
791
|
+
}
|
|
792
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
793
|
+
// Context Variables
|
|
794
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
795
|
+
_store = /* @__PURE__ */ new Map();
|
|
796
|
+
get(key) {
|
|
797
|
+
return this._store.get(key);
|
|
798
|
+
}
|
|
799
|
+
set(key, value) {
|
|
800
|
+
this._store.set(key, value);
|
|
801
|
+
}
|
|
802
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
803
|
+
// Lifecycle helpers
|
|
804
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
805
|
+
route = () => "";
|
|
806
|
+
get native() {
|
|
807
|
+
return this;
|
|
597
808
|
}
|
|
598
809
|
};
|
|
599
810
|
|
|
600
811
|
// src/engine/MinimalContext.ts
|
|
601
812
|
var MinimalRequest = class {
|
|
602
|
-
constructor(_request, _params, _path) {
|
|
813
|
+
constructor(_request, _params, _path, _routePattern) {
|
|
603
814
|
this._request = _request;
|
|
604
815
|
this._params = _params;
|
|
605
816
|
this._path = _path;
|
|
817
|
+
this._routePattern = _routePattern;
|
|
606
818
|
}
|
|
819
|
+
_searchParams = null;
|
|
607
820
|
get url() {
|
|
608
821
|
return this._request.url;
|
|
609
822
|
}
|
|
@@ -613,20 +826,39 @@ var MinimalRequest = class {
|
|
|
613
826
|
get path() {
|
|
614
827
|
return this._path;
|
|
615
828
|
}
|
|
829
|
+
get routePattern() {
|
|
830
|
+
return this._routePattern;
|
|
831
|
+
}
|
|
616
832
|
param(name) {
|
|
617
833
|
return this._params[name];
|
|
618
834
|
}
|
|
619
835
|
params() {
|
|
620
836
|
return { ...this._params };
|
|
621
837
|
}
|
|
838
|
+
/**
|
|
839
|
+
* Lazy-initialize searchParams, only parse once
|
|
840
|
+
*/
|
|
841
|
+
getSearchParams() {
|
|
842
|
+
if (this._searchParams === null) {
|
|
843
|
+
const url = this._request.url;
|
|
844
|
+
const queryStart = url.indexOf("?");
|
|
845
|
+
if (queryStart === -1) {
|
|
846
|
+
this._searchParams = new URLSearchParams();
|
|
847
|
+
} else {
|
|
848
|
+
const hashStart = url.indexOf("#", queryStart);
|
|
849
|
+
const queryString = hashStart === -1 ? url.slice(queryStart + 1) : url.slice(queryStart + 1, hashStart);
|
|
850
|
+
this._searchParams = new URLSearchParams(queryString);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
return this._searchParams;
|
|
854
|
+
}
|
|
622
855
|
query(name) {
|
|
623
|
-
|
|
624
|
-
return url.searchParams.get(name) ?? void 0;
|
|
856
|
+
return this.getSearchParams().get(name) ?? void 0;
|
|
625
857
|
}
|
|
626
858
|
queries() {
|
|
627
|
-
const
|
|
859
|
+
const params = this.getSearchParams();
|
|
628
860
|
const result = {};
|
|
629
|
-
for (const [key, value] of
|
|
861
|
+
for (const [key, value] of params.entries()) {
|
|
630
862
|
const existing = result[key];
|
|
631
863
|
if (existing === void 0) {
|
|
632
864
|
result[key] = value;
|
|
@@ -662,49 +894,103 @@ var MinimalRequest = class {
|
|
|
662
894
|
}
|
|
663
895
|
};
|
|
664
896
|
var MinimalContext = class {
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
897
|
+
req;
|
|
898
|
+
_resHeaders = {};
|
|
899
|
+
constructor(request, params, path, routePattern) {
|
|
900
|
+
this.req = new MinimalRequest(request, params, path, routePattern);
|
|
668
901
|
}
|
|
669
|
-
get req() {
|
|
670
|
-
|
|
902
|
+
// get req(): FastRequest {
|
|
903
|
+
// return this._req
|
|
904
|
+
// }
|
|
905
|
+
// Response helpers - merge custom headers with defaults
|
|
906
|
+
getHeaders(contentType) {
|
|
907
|
+
return {
|
|
908
|
+
...this._resHeaders,
|
|
909
|
+
"Content-Type": contentType
|
|
910
|
+
};
|
|
671
911
|
}
|
|
672
|
-
// Response helpers - create headers inline (no reuse overhead)
|
|
673
912
|
json(data, status = 200) {
|
|
674
913
|
return new Response(JSON.stringify(data), {
|
|
675
914
|
status,
|
|
676
|
-
headers:
|
|
915
|
+
headers: this.getHeaders("application/json; charset=utf-8")
|
|
677
916
|
});
|
|
678
917
|
}
|
|
679
918
|
text(text, status = 200) {
|
|
680
919
|
return new Response(text, {
|
|
681
920
|
status,
|
|
682
|
-
headers:
|
|
921
|
+
headers: this.getHeaders("text/plain; charset=utf-8")
|
|
683
922
|
});
|
|
684
923
|
}
|
|
685
924
|
html(html, status = 200) {
|
|
686
925
|
return new Response(html, {
|
|
687
926
|
status,
|
|
688
|
-
headers:
|
|
927
|
+
headers: this.getHeaders("text/html; charset=utf-8")
|
|
689
928
|
});
|
|
690
929
|
}
|
|
691
930
|
redirect(url, status = 302) {
|
|
692
931
|
return new Response(null, {
|
|
693
932
|
status,
|
|
694
|
-
headers: { Location: url }
|
|
933
|
+
headers: { ...this._resHeaders, Location: url }
|
|
695
934
|
});
|
|
696
935
|
}
|
|
697
936
|
body(data, status = 200) {
|
|
698
|
-
return new Response(data, {
|
|
937
|
+
return new Response(data, {
|
|
938
|
+
status,
|
|
939
|
+
headers: this._resHeaders
|
|
940
|
+
});
|
|
699
941
|
}
|
|
700
|
-
header(
|
|
701
|
-
|
|
942
|
+
header(name, value) {
|
|
943
|
+
if (value !== void 0) {
|
|
944
|
+
this._resHeaders[name] = value;
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
return this.req.header(name);
|
|
702
948
|
}
|
|
703
949
|
status(_code) {
|
|
704
950
|
}
|
|
951
|
+
stream(stream, status = 200) {
|
|
952
|
+
return new Response(stream, {
|
|
953
|
+
status,
|
|
954
|
+
headers: this.getHeaders("application/octet-stream")
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
notFound(message = "Not Found") {
|
|
958
|
+
return this.text(message, 404);
|
|
959
|
+
}
|
|
960
|
+
forbidden(message = "Forbidden") {
|
|
961
|
+
return this.text(message, 403);
|
|
962
|
+
}
|
|
963
|
+
unauthorized(message = "Unauthorized") {
|
|
964
|
+
return this.text(message, 401);
|
|
965
|
+
}
|
|
966
|
+
badRequest(message = "Bad Request") {
|
|
967
|
+
return this.text(message, 400);
|
|
968
|
+
}
|
|
969
|
+
async forward(target, _options = {}) {
|
|
970
|
+
const url = new URL(this.req.url);
|
|
971
|
+
const targetUrl = new URL(
|
|
972
|
+
target.startsWith("http") ? target : `${url.protocol}//${target}${this.req.path}`
|
|
973
|
+
);
|
|
974
|
+
return fetch(targetUrl.toString(), {
|
|
975
|
+
method: this.req.method,
|
|
976
|
+
headers: this.req.raw.headers
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
get(_key) {
|
|
980
|
+
return void 0;
|
|
981
|
+
}
|
|
982
|
+
set(_key, _value) {
|
|
983
|
+
}
|
|
984
|
+
route = () => "";
|
|
985
|
+
get native() {
|
|
986
|
+
return this;
|
|
987
|
+
}
|
|
705
988
|
// Required for interface compatibility
|
|
706
|
-
|
|
707
|
-
throw new Error("MinimalContext does not support
|
|
989
|
+
init(_request, _params, _path) {
|
|
990
|
+
throw new Error("MinimalContext does not support init. Create a new instance instead.");
|
|
991
|
+
}
|
|
992
|
+
// Required for interface compatibility
|
|
993
|
+
reset() {
|
|
708
994
|
}
|
|
709
995
|
};
|
|
710
996
|
|
|
@@ -808,16 +1094,40 @@ var ObjectPool = class {
|
|
|
808
1094
|
};
|
|
809
1095
|
|
|
810
1096
|
// src/engine/Gravito.ts
|
|
1097
|
+
function compileMiddlewareChain(middleware, handler) {
|
|
1098
|
+
if (middleware.length === 0) {
|
|
1099
|
+
return handler;
|
|
1100
|
+
}
|
|
1101
|
+
let compiled = handler;
|
|
1102
|
+
for (let i = middleware.length - 1; i >= 0; i--) {
|
|
1103
|
+
const mw = middleware[i];
|
|
1104
|
+
const nextHandler = compiled;
|
|
1105
|
+
compiled = async (ctx) => {
|
|
1106
|
+
let nextResult;
|
|
1107
|
+
const next = async () => {
|
|
1108
|
+
nextResult = await nextHandler(ctx);
|
|
1109
|
+
return nextResult;
|
|
1110
|
+
};
|
|
1111
|
+
const result = await mw(ctx, next);
|
|
1112
|
+
return result ?? nextResult;
|
|
1113
|
+
};
|
|
1114
|
+
}
|
|
1115
|
+
return compiled;
|
|
1116
|
+
}
|
|
811
1117
|
var Gravito = class {
|
|
812
1118
|
router = new AOTRouter();
|
|
813
1119
|
contextPool;
|
|
814
1120
|
errorHandler;
|
|
815
1121
|
notFoundHandler;
|
|
816
1122
|
// Direct reference to static routes Map (O(1) access)
|
|
817
|
-
|
|
1123
|
+
/** @internal */
|
|
818
1124
|
staticRoutes;
|
|
819
1125
|
// Flag: pure static app (no middleware at all) allows ultra-fast path
|
|
820
1126
|
isPureStaticApp = true;
|
|
1127
|
+
// Cache for precompiled dynamic routes
|
|
1128
|
+
compiledDynamicRoutes = /* @__PURE__ */ new Map();
|
|
1129
|
+
// Version tracking for cache invalidation
|
|
1130
|
+
middlewareVersion = 0;
|
|
821
1131
|
/**
|
|
822
1132
|
* Create a new Gravito instance
|
|
823
1133
|
*
|
|
@@ -827,7 +1137,7 @@ var Gravito = class {
|
|
|
827
1137
|
const poolSize = options.poolSize ?? 256;
|
|
828
1138
|
this.contextPool = new ObjectPool(
|
|
829
1139
|
() => new FastContext(),
|
|
830
|
-
(ctx) => ctx.reset(
|
|
1140
|
+
(ctx) => ctx.reset(),
|
|
831
1141
|
poolSize
|
|
832
1142
|
);
|
|
833
1143
|
this.contextPool.prewarm(Math.min(32, poolSize));
|
|
@@ -871,7 +1181,7 @@ var Gravito = class {
|
|
|
871
1181
|
return this.addRoute("delete", path, handlers);
|
|
872
1182
|
}
|
|
873
1183
|
/**
|
|
874
|
-
* Register a
|
|
1184
|
+
* Register a PDF route
|
|
875
1185
|
*/
|
|
876
1186
|
patch(path, ...handlers) {
|
|
877
1187
|
return this.addRoute("patch", path, handlers);
|
|
@@ -905,6 +1215,8 @@ var Gravito = class {
|
|
|
905
1215
|
} else {
|
|
906
1216
|
this.router.use(pathOrMiddleware, ...middleware);
|
|
907
1217
|
}
|
|
1218
|
+
this.middlewareVersion++;
|
|
1219
|
+
this.compileRoutes();
|
|
908
1220
|
return this;
|
|
909
1221
|
}
|
|
910
1222
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -912,19 +1224,10 @@ var Gravito = class {
|
|
|
912
1224
|
// ─────────────────────────────────────────────────────────────────────────
|
|
913
1225
|
/**
|
|
914
1226
|
* Mount a sub-application at a path prefix
|
|
915
|
-
*
|
|
916
|
-
* @example
|
|
917
|
-
* ```typescript
|
|
918
|
-
* const api = new Gravito()
|
|
919
|
-
* api.get('/users', (c) => c.json({ users: [] }))
|
|
920
|
-
*
|
|
921
|
-
* const app = new Gravito()
|
|
922
|
-
* app.route('/api', api)
|
|
923
|
-
* // Now accessible at /api/users
|
|
924
|
-
* ```
|
|
925
1227
|
*/
|
|
926
|
-
route(
|
|
927
|
-
|
|
1228
|
+
route(path, app) {
|
|
1229
|
+
this.router.mount(path, app.router);
|
|
1230
|
+
this.compileRoutes();
|
|
928
1231
|
return this;
|
|
929
1232
|
}
|
|
930
1233
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -932,14 +1235,6 @@ var Gravito = class {
|
|
|
932
1235
|
// ─────────────────────────────────────────────────────────────────────────
|
|
933
1236
|
/**
|
|
934
1237
|
* Set custom error handler
|
|
935
|
-
*
|
|
936
|
-
* @example
|
|
937
|
-
* ```typescript
|
|
938
|
-
* app.onError((err, c) => {
|
|
939
|
-
* console.error(err)
|
|
940
|
-
* return c.json({ error: err.message }, 500)
|
|
941
|
-
* })
|
|
942
|
-
* ```
|
|
943
1238
|
*/
|
|
944
1239
|
onError(handler) {
|
|
945
1240
|
this.errorHandler = handler;
|
|
@@ -947,13 +1242,6 @@ var Gravito = class {
|
|
|
947
1242
|
}
|
|
948
1243
|
/**
|
|
949
1244
|
* Set custom 404 handler
|
|
950
|
-
*
|
|
951
|
-
* @example
|
|
952
|
-
* ```typescript
|
|
953
|
-
* app.notFound((c) => {
|
|
954
|
-
* return c.json({ error: 'Not Found' }, 404)
|
|
955
|
-
* })
|
|
956
|
-
* ```
|
|
957
1245
|
*/
|
|
958
1246
|
notFound(handler) {
|
|
959
1247
|
this.notFoundHandler = handler;
|
|
@@ -963,35 +1251,44 @@ var Gravito = class {
|
|
|
963
1251
|
// Request Handling (Bun.serve integration)
|
|
964
1252
|
// ─────────────────────────────────────────────────────────────────────────
|
|
965
1253
|
/**
|
|
966
|
-
*
|
|
1254
|
+
* Predictive Route Warming (JIT Optimization)
|
|
967
1255
|
*
|
|
968
|
-
*
|
|
969
|
-
*
|
|
1256
|
+
* Simulates requests to specified routes to trigger JIT compilation (FTL)
|
|
1257
|
+
* before real traffic arrives.
|
|
970
1258
|
*
|
|
971
|
-
* @param
|
|
972
|
-
|
|
1259
|
+
* @param paths List of paths to warm up (e.g. ['/api/users', '/health'])
|
|
1260
|
+
*/
|
|
1261
|
+
async warmup(paths) {
|
|
1262
|
+
const dummyReqOpts = { headers: { "User-Agent": "Gravito-Warmup/1.0" } };
|
|
1263
|
+
for (const path of paths) {
|
|
1264
|
+
const req = new Request(`http://localhost${path}`, dummyReqOpts);
|
|
1265
|
+
await this.fetch(req);
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
/**
|
|
1269
|
+
* Handle an incoming request
|
|
973
1270
|
*/
|
|
974
|
-
fetch = (request) => {
|
|
1271
|
+
fetch = async (request) => {
|
|
975
1272
|
const path = extractPath(request.url);
|
|
976
1273
|
const method = request.method.toLowerCase();
|
|
977
1274
|
const staticKey = `${method}:${path}`;
|
|
978
1275
|
const staticRoute = this.staticRoutes.get(staticKey);
|
|
979
1276
|
if (staticRoute) {
|
|
980
1277
|
if (staticRoute.useMinimal) {
|
|
981
|
-
const ctx = new MinimalContext(request, {}, path);
|
|
1278
|
+
const ctx = new MinimalContext(request, {}, path, path);
|
|
982
1279
|
try {
|
|
983
1280
|
const result = staticRoute.handler(ctx);
|
|
984
1281
|
if (result instanceof Response) {
|
|
985
1282
|
return result;
|
|
986
1283
|
}
|
|
987
|
-
return result;
|
|
1284
|
+
return await result;
|
|
988
1285
|
} catch (error) {
|
|
989
1286
|
return this.handleErrorSync(error, request, path);
|
|
990
1287
|
}
|
|
991
1288
|
}
|
|
992
|
-
return this.handleWithMiddleware(request, path, staticRoute);
|
|
1289
|
+
return await this.handleWithMiddleware(request, path, staticRoute);
|
|
993
1290
|
}
|
|
994
|
-
return this.handleDynamicRoute(request, method, path);
|
|
1291
|
+
return await this.handleDynamicRoute(request, method, path);
|
|
995
1292
|
};
|
|
996
1293
|
/**
|
|
997
1294
|
* Handle routes with middleware (async path)
|
|
@@ -999,7 +1296,10 @@ var Gravito = class {
|
|
|
999
1296
|
async handleWithMiddleware(request, path, route) {
|
|
1000
1297
|
const ctx = this.contextPool.acquire();
|
|
1001
1298
|
try {
|
|
1002
|
-
ctx.
|
|
1299
|
+
ctx.init(request, {}, path, path);
|
|
1300
|
+
if (route.compiled) {
|
|
1301
|
+
return await route.compiled(ctx);
|
|
1302
|
+
}
|
|
1003
1303
|
const middleware = this.collectMiddlewareForPath(path, route.middleware);
|
|
1004
1304
|
if (middleware.length === 0) {
|
|
1005
1305
|
return await route.handler(ctx);
|
|
@@ -1019,14 +1319,21 @@ var Gravito = class {
|
|
|
1019
1319
|
if (!match.handler) {
|
|
1020
1320
|
return this.handleNotFoundSync(request, path);
|
|
1021
1321
|
}
|
|
1322
|
+
const cacheKey = `${method}:${match.routePattern ?? path}`;
|
|
1323
|
+
let entry = this.compiledDynamicRoutes.get(cacheKey);
|
|
1324
|
+
if (!entry || entry.version !== this.middlewareVersion) {
|
|
1325
|
+
const compiled = compileMiddlewareChain(match.middleware, match.handler);
|
|
1326
|
+
if (this.compiledDynamicRoutes.size > 1e3) {
|
|
1327
|
+
this.compiledDynamicRoutes.clear();
|
|
1328
|
+
}
|
|
1329
|
+
entry = { compiled, version: this.middlewareVersion };
|
|
1330
|
+
this.compiledDynamicRoutes.set(cacheKey, entry);
|
|
1331
|
+
}
|
|
1022
1332
|
const ctx = this.contextPool.acquire();
|
|
1023
1333
|
const execute = async () => {
|
|
1024
1334
|
try {
|
|
1025
|
-
ctx.
|
|
1026
|
-
|
|
1027
|
-
return await match.handler(ctx);
|
|
1028
|
-
}
|
|
1029
|
-
return await this.executeMiddleware(ctx, match.middleware, match.handler);
|
|
1335
|
+
ctx.init(request, match.params, path, match.routePattern);
|
|
1336
|
+
return await entry?.compiled(ctx);
|
|
1030
1337
|
} catch (error) {
|
|
1031
1338
|
return await this.handleError(error, ctx);
|
|
1032
1339
|
} finally {
|
|
@@ -1048,16 +1355,10 @@ var Gravito = class {
|
|
|
1048
1355
|
return result;
|
|
1049
1356
|
}
|
|
1050
1357
|
console.error("Unhandled error:", error);
|
|
1051
|
-
return new Response(
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
}),
|
|
1056
|
-
{
|
|
1057
|
-
status: 500,
|
|
1058
|
-
headers: { "Content-Type": "application/json" }
|
|
1059
|
-
}
|
|
1060
|
-
);
|
|
1358
|
+
return new Response(CACHED_RESPONSES.INTERNAL_ERROR, {
|
|
1359
|
+
status: 500,
|
|
1360
|
+
headers: HEADERS.JSON
|
|
1361
|
+
});
|
|
1061
1362
|
}
|
|
1062
1363
|
/**
|
|
1063
1364
|
* Sync 404 handler (for ultra-fast path)
|
|
@@ -1071,14 +1372,13 @@ var Gravito = class {
|
|
|
1071
1372
|
}
|
|
1072
1373
|
return result;
|
|
1073
1374
|
}
|
|
1074
|
-
return new Response(
|
|
1375
|
+
return new Response(CACHED_RESPONSES.NOT_FOUND, {
|
|
1075
1376
|
status: 404,
|
|
1076
|
-
headers:
|
|
1377
|
+
headers: HEADERS.JSON
|
|
1077
1378
|
});
|
|
1078
1379
|
}
|
|
1079
1380
|
/**
|
|
1080
1381
|
* Collect middleware for a specific path
|
|
1081
|
-
* (Simplified version - assumes we've already checked for pure static)
|
|
1082
1382
|
*/
|
|
1083
1383
|
collectMiddlewareForPath(path, routeMiddleware) {
|
|
1084
1384
|
if (this.router.globalMiddleware.length === 0 && this.router.pathMiddleware.size === 0) {
|
|
@@ -1088,22 +1388,26 @@ var Gravito = class {
|
|
|
1088
1388
|
}
|
|
1089
1389
|
/**
|
|
1090
1390
|
* Compile routes for optimization
|
|
1091
|
-
* Called once during initialization and when routes change
|
|
1092
1391
|
*/
|
|
1093
1392
|
compileRoutes() {
|
|
1094
1393
|
this.staticRoutes = this.router.staticRoutes;
|
|
1095
1394
|
const hasGlobalMiddleware = this.router.globalMiddleware.length > 0;
|
|
1096
1395
|
const hasPathMiddleware = this.router.pathMiddleware.size > 0;
|
|
1097
1396
|
this.isPureStaticApp = !hasGlobalMiddleware && !hasPathMiddleware;
|
|
1098
|
-
for (const [
|
|
1397
|
+
for (const [key, route] of this.staticRoutes) {
|
|
1398
|
+
if (route.compiledVersion === this.middlewareVersion) {
|
|
1399
|
+
continue;
|
|
1400
|
+
}
|
|
1099
1401
|
const analysis = analyzeHandler(route.handler);
|
|
1100
1402
|
const optimalType = getOptimalContextType(analysis);
|
|
1101
1403
|
route.useMinimal = this.isPureStaticApp && route.middleware.length === 0 && optimalType === "minimal";
|
|
1404
|
+
if (!route.useMinimal) {
|
|
1405
|
+
const allMiddleware = this.collectMiddlewareForPath(key.split(":")[1], route.middleware);
|
|
1406
|
+
route.compiled = compileMiddlewareChain(allMiddleware, route.handler);
|
|
1407
|
+
}
|
|
1408
|
+
route.compiledVersion = this.middlewareVersion;
|
|
1102
1409
|
}
|
|
1103
1410
|
}
|
|
1104
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1105
|
-
// Internal Methods
|
|
1106
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1107
1411
|
/**
|
|
1108
1412
|
* Add a route to the router
|
|
1109
1413
|
*/
|
|
@@ -1119,9 +1423,6 @@ var Gravito = class {
|
|
|
1119
1423
|
}
|
|
1120
1424
|
/**
|
|
1121
1425
|
* Execute middleware chain followed by handler
|
|
1122
|
-
*
|
|
1123
|
-
* Implements the standard middleware pattern:
|
|
1124
|
-
* Each middleware can call next() to continue the chain.
|
|
1125
1426
|
*/
|
|
1126
1427
|
async executeMiddleware(ctx, middleware, handler) {
|
|
1127
1428
|
let index = 0;
|
|
@@ -1138,16 +1439,6 @@ var Gravito = class {
|
|
|
1138
1439
|
}
|
|
1139
1440
|
return await handler(ctx);
|
|
1140
1441
|
}
|
|
1141
|
-
/*
|
|
1142
|
-
* Handle 404 Not Found (Async version for dynamic/middleware paths)
|
|
1143
|
-
* Note: Currently unused as we handle 404s via handleNotFoundSync or inline
|
|
1144
|
-
*/
|
|
1145
|
-
// private async handleNotFound(ctx: FastContext): Promise<Response> {
|
|
1146
|
-
// if (this.notFoundHandler) {
|
|
1147
|
-
// return await this.notFoundHandler(ctx)
|
|
1148
|
-
// }
|
|
1149
|
-
// return ctx.json({ error: 'Not Found' }, 404)
|
|
1150
|
-
// }
|
|
1151
1442
|
/**
|
|
1152
1443
|
* Handle errors (Async version for dynamic/middleware paths)
|
|
1153
1444
|
*/
|