@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.js
CHANGED
|
@@ -208,13 +208,25 @@ var RadixRouter = class _RadixRouter {
|
|
|
208
208
|
// src/engine/AOTRouter.ts
|
|
209
209
|
var AOTRouter = class {
|
|
210
210
|
// Static route cache: "METHOD:PATH" -> RouteMetadata
|
|
211
|
+
/** @internal */
|
|
211
212
|
staticRoutes = /* @__PURE__ */ new Map();
|
|
212
213
|
// Dynamic route handler (Radix Tree)
|
|
213
214
|
dynamicRouter = new RadixRouter();
|
|
215
|
+
// Store all route definitions to support mounting/merging
|
|
216
|
+
/** @internal */
|
|
217
|
+
routeDefinitions = [];
|
|
214
218
|
// Global middleware (applies to all routes)
|
|
219
|
+
/** @internal */
|
|
215
220
|
globalMiddleware = [];
|
|
216
221
|
// Path-based middleware: pattern -> middleware[]
|
|
222
|
+
/** @internal */
|
|
217
223
|
pathMiddleware = /* @__PURE__ */ new Map();
|
|
224
|
+
// Dynamic route patterns: handler function -> route pattern
|
|
225
|
+
// 用於追蹤動態路由的模式,防止高基數問題
|
|
226
|
+
dynamicRoutePatterns = /* @__PURE__ */ new Map();
|
|
227
|
+
middlewareCache = /* @__PURE__ */ new Map();
|
|
228
|
+
cacheMaxSize = 1e3;
|
|
229
|
+
version = 0;
|
|
218
230
|
/**
|
|
219
231
|
* Register a route
|
|
220
232
|
*
|
|
@@ -228,6 +240,7 @@ var AOTRouter = class {
|
|
|
228
240
|
* @param middleware - Route-specific middleware
|
|
229
241
|
*/
|
|
230
242
|
add(method, path, handler, middleware = []) {
|
|
243
|
+
this.routeDefinitions.push({ method, path, handler, middleware });
|
|
231
244
|
const normalizedMethod = method.toLowerCase();
|
|
232
245
|
if (this.isStaticPath(path)) {
|
|
233
246
|
const key = `${normalizedMethod}:${path}`;
|
|
@@ -235,11 +248,47 @@ var AOTRouter = class {
|
|
|
235
248
|
} else {
|
|
236
249
|
const wrappedHandler = handler;
|
|
237
250
|
this.dynamicRouter.add(normalizedMethod, path, [wrappedHandler]);
|
|
251
|
+
this.dynamicRoutePatterns.set(wrappedHandler, path);
|
|
238
252
|
if (middleware.length > 0) {
|
|
239
253
|
this.pathMiddleware.set(`${normalizedMethod}:${path}`, middleware);
|
|
240
254
|
}
|
|
241
255
|
}
|
|
242
256
|
}
|
|
257
|
+
/**
|
|
258
|
+
* Mount another router at a prefix
|
|
259
|
+
*/
|
|
260
|
+
mount(prefix, other) {
|
|
261
|
+
if (other.globalMiddleware.length > 0) {
|
|
262
|
+
this.usePattern(prefix, ...other.globalMiddleware);
|
|
263
|
+
const wildcard = prefix === "/" ? "/*" : `${prefix}/*`;
|
|
264
|
+
this.usePattern(wildcard, ...other.globalMiddleware);
|
|
265
|
+
}
|
|
266
|
+
for (const [pattern, mws] of other.pathMiddleware) {
|
|
267
|
+
if (pattern.includes(":")) {
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
let newPattern;
|
|
271
|
+
if (pattern === "*") {
|
|
272
|
+
newPattern = prefix === "/" ? "/*" : `${prefix}/*`;
|
|
273
|
+
} else if (pattern.startsWith("/")) {
|
|
274
|
+
newPattern = prefix === "/" ? pattern : `${prefix}${pattern}`;
|
|
275
|
+
} else {
|
|
276
|
+
newPattern = prefix === "/" ? `/${pattern}` : `${prefix}/${pattern}`;
|
|
277
|
+
}
|
|
278
|
+
this.usePattern(newPattern, ...mws);
|
|
279
|
+
}
|
|
280
|
+
for (const def of other.routeDefinitions) {
|
|
281
|
+
let newPath;
|
|
282
|
+
if (prefix === "/") {
|
|
283
|
+
newPath = def.path;
|
|
284
|
+
} else if (def.path === "/") {
|
|
285
|
+
newPath = prefix;
|
|
286
|
+
} else {
|
|
287
|
+
newPath = `${prefix}${def.path}`;
|
|
288
|
+
}
|
|
289
|
+
this.add(def.method, newPath, def.handler, def.middleware);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
243
292
|
/**
|
|
244
293
|
* Add global middleware
|
|
245
294
|
*
|
|
@@ -249,6 +298,7 @@ var AOTRouter = class {
|
|
|
249
298
|
*/
|
|
250
299
|
use(...middleware) {
|
|
251
300
|
this.globalMiddleware.push(...middleware);
|
|
301
|
+
this.version++;
|
|
252
302
|
}
|
|
253
303
|
/**
|
|
254
304
|
* Add path-based middleware
|
|
@@ -259,8 +309,13 @@ var AOTRouter = class {
|
|
|
259
309
|
* @param middleware - Middleware functions
|
|
260
310
|
*/
|
|
261
311
|
usePattern(pattern, ...middleware) {
|
|
262
|
-
|
|
263
|
-
|
|
312
|
+
if (pattern === "*") {
|
|
313
|
+
this.globalMiddleware.push(...middleware);
|
|
314
|
+
} else {
|
|
315
|
+
const existing = this.pathMiddleware.get(pattern) ?? [];
|
|
316
|
+
this.pathMiddleware.set(pattern, [...existing, ...middleware]);
|
|
317
|
+
}
|
|
318
|
+
this.version++;
|
|
264
319
|
}
|
|
265
320
|
/**
|
|
266
321
|
* Match a request to a route
|
|
@@ -279,18 +334,22 @@ var AOTRouter = class {
|
|
|
279
334
|
return {
|
|
280
335
|
handler: staticRoute.handler,
|
|
281
336
|
params: {},
|
|
282
|
-
middleware: this.collectMiddleware(path, staticRoute.middleware)
|
|
337
|
+
middleware: this.collectMiddleware(path, staticRoute.middleware),
|
|
338
|
+
routePattern: path
|
|
283
339
|
};
|
|
284
340
|
}
|
|
285
341
|
const match = this.dynamicRouter.match(normalizedMethod, path);
|
|
286
342
|
if (match && match.handlers.length > 0) {
|
|
287
343
|
const handler = match.handlers[0];
|
|
288
|
-
const
|
|
344
|
+
const wrappedHandler = match.handlers[0];
|
|
345
|
+
const routePattern = this.dynamicRoutePatterns.get(wrappedHandler);
|
|
346
|
+
const routeKey = routePattern ? `${normalizedMethod}:${routePattern}` : null;
|
|
289
347
|
const routeMiddleware = routeKey ? this.pathMiddleware.get(routeKey) ?? [] : [];
|
|
290
348
|
return {
|
|
291
349
|
handler,
|
|
292
350
|
params: match.params,
|
|
293
|
-
middleware: this.collectMiddleware(path, routeMiddleware)
|
|
351
|
+
middleware: this.collectMiddleware(path, routeMiddleware),
|
|
352
|
+
routePattern
|
|
294
353
|
};
|
|
295
354
|
}
|
|
296
355
|
return {
|
|
@@ -318,13 +377,20 @@ var AOTRouter = class {
|
|
|
318
377
|
if (this.globalMiddleware.length === 0 && this.pathMiddleware.size === 0 && routeMiddleware.length === 0) {
|
|
319
378
|
return [];
|
|
320
379
|
}
|
|
380
|
+
const cacheKey = `${path}:${routeMiddleware.length}`;
|
|
381
|
+
const cached = this.middlewareCache.get(cacheKey);
|
|
382
|
+
if (cached !== void 0 && cached.version === this.version) {
|
|
383
|
+
return cached.data;
|
|
384
|
+
}
|
|
321
385
|
const middleware = [];
|
|
322
386
|
if (this.globalMiddleware.length > 0) {
|
|
323
387
|
middleware.push(...this.globalMiddleware);
|
|
324
388
|
}
|
|
325
389
|
if (this.pathMiddleware.size > 0) {
|
|
326
390
|
for (const [pattern, mw] of this.pathMiddleware) {
|
|
327
|
-
if (pattern.includes(":"))
|
|
391
|
+
if (pattern.includes(":")) {
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
328
394
|
if (this.matchPattern(pattern, path)) {
|
|
329
395
|
middleware.push(...mw);
|
|
330
396
|
}
|
|
@@ -333,6 +399,9 @@ var AOTRouter = class {
|
|
|
333
399
|
if (routeMiddleware.length > 0) {
|
|
334
400
|
middleware.push(...routeMiddleware);
|
|
335
401
|
}
|
|
402
|
+
if (this.middlewareCache.size < this.cacheMaxSize) {
|
|
403
|
+
this.middlewareCache.set(cacheKey, { data: middleware, version: this.version });
|
|
404
|
+
}
|
|
336
405
|
return middleware;
|
|
337
406
|
}
|
|
338
407
|
/**
|
|
@@ -353,32 +422,18 @@ var AOTRouter = class {
|
|
|
353
422
|
* @returns True if pattern matches
|
|
354
423
|
*/
|
|
355
424
|
matchPattern(pattern, path) {
|
|
356
|
-
if (pattern === "*")
|
|
357
|
-
|
|
425
|
+
if (pattern === "*") {
|
|
426
|
+
return true;
|
|
427
|
+
}
|
|
428
|
+
if (pattern === path) {
|
|
429
|
+
return true;
|
|
430
|
+
}
|
|
358
431
|
if (pattern.endsWith("/*")) {
|
|
359
432
|
const prefix = pattern.slice(0, -2);
|
|
360
433
|
return path.startsWith(prefix);
|
|
361
434
|
}
|
|
362
435
|
return false;
|
|
363
436
|
}
|
|
364
|
-
/**
|
|
365
|
-
* Find the original route key for a matched dynamic route
|
|
366
|
-
*
|
|
367
|
-
* This is needed to look up route-specific middleware.
|
|
368
|
-
* It's a bit of a hack, but avoids storing duplicate data.
|
|
369
|
-
*
|
|
370
|
-
* @param method - HTTP method
|
|
371
|
-
* @param path - Matched path
|
|
372
|
-
* @returns Route key or null
|
|
373
|
-
*/
|
|
374
|
-
findDynamicRouteKey(method, _path) {
|
|
375
|
-
for (const key of this.pathMiddleware.keys()) {
|
|
376
|
-
if (key.startsWith(`${method}:`)) {
|
|
377
|
-
return key;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
return null;
|
|
381
|
-
}
|
|
382
437
|
/**
|
|
383
438
|
* Get all registered routes (for debugging)
|
|
384
439
|
*/
|
|
@@ -419,48 +474,111 @@ function getOptimalContextType(analysis) {
|
|
|
419
474
|
return "fast";
|
|
420
475
|
}
|
|
421
476
|
|
|
477
|
+
// src/engine/constants.ts
|
|
478
|
+
var encoder = new TextEncoder();
|
|
479
|
+
var CACHED_RESPONSES = {
|
|
480
|
+
NOT_FOUND: encoder.encode('{"error":"Not Found"}'),
|
|
481
|
+
INTERNAL_ERROR: encoder.encode('{"error":"Internal Server Error"}'),
|
|
482
|
+
OK: encoder.encode('{"ok":true}'),
|
|
483
|
+
EMPTY: new Uint8Array(0)
|
|
484
|
+
};
|
|
485
|
+
var HEADERS = {
|
|
486
|
+
JSON: { "Content-Type": "application/json; charset=utf-8" },
|
|
487
|
+
TEXT: { "Content-Type": "text/plain; charset=utf-8" },
|
|
488
|
+
HTML: { "Content-Type": "text/html; charset=utf-8" }
|
|
489
|
+
};
|
|
490
|
+
|
|
422
491
|
// src/engine/FastContext.ts
|
|
423
492
|
var FastRequestImpl = class {
|
|
424
493
|
_request;
|
|
425
494
|
_params;
|
|
426
|
-
|
|
427
|
-
|
|
495
|
+
_path;
|
|
496
|
+
_routePattern;
|
|
497
|
+
_url = null;
|
|
428
498
|
_query = null;
|
|
429
499
|
_headers = null;
|
|
500
|
+
_cachedJson = void 0;
|
|
501
|
+
_jsonParsed = false;
|
|
502
|
+
// Back-reference for release check optimization
|
|
503
|
+
_ctx;
|
|
504
|
+
constructor(ctx) {
|
|
505
|
+
this._ctx = ctx;
|
|
506
|
+
}
|
|
430
507
|
/**
|
|
431
|
-
*
|
|
508
|
+
* Initialize for new request
|
|
432
509
|
*/
|
|
433
|
-
|
|
510
|
+
init(request, params = {}, path = "", routePattern) {
|
|
434
511
|
this._request = request;
|
|
435
512
|
this._params = params;
|
|
436
|
-
this.
|
|
513
|
+
this._path = path;
|
|
514
|
+
this._routePattern = routePattern;
|
|
515
|
+
this._url = null;
|
|
437
516
|
this._query = null;
|
|
438
517
|
this._headers = null;
|
|
518
|
+
this._cachedJson = void 0;
|
|
519
|
+
this._jsonParsed = false;
|
|
520
|
+
return this;
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Reset for pooling
|
|
524
|
+
*/
|
|
525
|
+
reset() {
|
|
526
|
+
this._request = void 0;
|
|
527
|
+
this._params = void 0;
|
|
528
|
+
this._url = null;
|
|
529
|
+
this._query = null;
|
|
530
|
+
this._headers = null;
|
|
531
|
+
this._cachedJson = void 0;
|
|
532
|
+
this._jsonParsed = false;
|
|
533
|
+
}
|
|
534
|
+
checkReleased() {
|
|
535
|
+
if (this._ctx._isReleased) {
|
|
536
|
+
throw new Error(
|
|
537
|
+
"FastContext usage after release detected! (Object Pool Strict Lifecycle Guard)"
|
|
538
|
+
);
|
|
539
|
+
}
|
|
439
540
|
}
|
|
440
541
|
get url() {
|
|
542
|
+
this.checkReleased();
|
|
441
543
|
return this._request.url;
|
|
442
544
|
}
|
|
443
545
|
get method() {
|
|
546
|
+
this.checkReleased();
|
|
444
547
|
return this._request.method;
|
|
445
548
|
}
|
|
446
549
|
get path() {
|
|
447
|
-
|
|
550
|
+
this.checkReleased();
|
|
551
|
+
return this._path;
|
|
552
|
+
}
|
|
553
|
+
get routePattern() {
|
|
554
|
+
this.checkReleased();
|
|
555
|
+
return this._routePattern;
|
|
448
556
|
}
|
|
449
557
|
param(name) {
|
|
558
|
+
this.checkReleased();
|
|
450
559
|
return this._params[name];
|
|
451
560
|
}
|
|
452
561
|
params() {
|
|
562
|
+
this.checkReleased();
|
|
453
563
|
return { ...this._params };
|
|
454
564
|
}
|
|
565
|
+
getUrl() {
|
|
566
|
+
if (!this._url) {
|
|
567
|
+
this._url = new URL(this._request.url);
|
|
568
|
+
}
|
|
569
|
+
return this._url;
|
|
570
|
+
}
|
|
455
571
|
query(name) {
|
|
572
|
+
this.checkReleased();
|
|
456
573
|
if (!this._query) {
|
|
457
|
-
this._query = this.
|
|
574
|
+
this._query = this.getUrl().searchParams;
|
|
458
575
|
}
|
|
459
576
|
return this._query.get(name) ?? void 0;
|
|
460
577
|
}
|
|
461
578
|
queries() {
|
|
579
|
+
this.checkReleased();
|
|
462
580
|
if (!this._query) {
|
|
463
|
-
this._query = this.
|
|
581
|
+
this._query = this.getUrl().searchParams;
|
|
464
582
|
}
|
|
465
583
|
const result = {};
|
|
466
584
|
for (const [key, value] of this._query.entries()) {
|
|
@@ -476,9 +594,11 @@ var FastRequestImpl = class {
|
|
|
476
594
|
return result;
|
|
477
595
|
}
|
|
478
596
|
header(name) {
|
|
597
|
+
this.checkReleased();
|
|
479
598
|
return this._request.headers.get(name) ?? void 0;
|
|
480
599
|
}
|
|
481
600
|
headers() {
|
|
601
|
+
this.checkReleased();
|
|
482
602
|
if (!this._headers) {
|
|
483
603
|
this._headers = {};
|
|
484
604
|
for (const [key, value] of this._request.headers.entries()) {
|
|
@@ -488,41 +608,70 @@ var FastRequestImpl = class {
|
|
|
488
608
|
return { ...this._headers };
|
|
489
609
|
}
|
|
490
610
|
async json() {
|
|
491
|
-
|
|
611
|
+
this.checkReleased();
|
|
612
|
+
if (!this._jsonParsed) {
|
|
613
|
+
this._cachedJson = await this._request.json();
|
|
614
|
+
this._jsonParsed = true;
|
|
615
|
+
}
|
|
616
|
+
return this._cachedJson;
|
|
492
617
|
}
|
|
493
618
|
async text() {
|
|
619
|
+
this.checkReleased();
|
|
494
620
|
return this._request.text();
|
|
495
621
|
}
|
|
496
622
|
async formData() {
|
|
623
|
+
this.checkReleased();
|
|
497
624
|
return this._request.formData();
|
|
498
625
|
}
|
|
499
626
|
get raw() {
|
|
627
|
+
this.checkReleased();
|
|
500
628
|
return this._request;
|
|
501
629
|
}
|
|
502
630
|
};
|
|
503
631
|
var FastContext = class {
|
|
504
|
-
|
|
632
|
+
req = new FastRequestImpl(this);
|
|
505
633
|
// private _statusCode = 200
|
|
506
634
|
_headers = new Headers();
|
|
507
635
|
// Reuse this object
|
|
636
|
+
_isReleased = false;
|
|
637
|
+
// Made public for internal check access
|
|
508
638
|
/**
|
|
509
|
-
*
|
|
639
|
+
* Initialize context for a new request
|
|
510
640
|
*
|
|
511
641
|
* This is called when acquiring from the pool.
|
|
512
|
-
* Must clear all state from previous request.
|
|
513
642
|
*/
|
|
514
|
-
|
|
515
|
-
this.
|
|
643
|
+
init(request, params = {}, path = "", routePattern) {
|
|
644
|
+
this._isReleased = false;
|
|
645
|
+
this.req.init(request, params, path, routePattern);
|
|
516
646
|
this._headers = new Headers();
|
|
517
647
|
return this;
|
|
518
648
|
}
|
|
519
|
-
|
|
520
|
-
|
|
649
|
+
/**
|
|
650
|
+
* Reset context for pooling (Cleanup)
|
|
651
|
+
*
|
|
652
|
+
* This is called when releasing back to the pool.
|
|
653
|
+
* Implements "Deep-Reset Protocol" and "Release Guard".
|
|
654
|
+
*/
|
|
655
|
+
reset() {
|
|
656
|
+
this._isReleased = true;
|
|
657
|
+
this.req.reset();
|
|
658
|
+
this._store.clear();
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Check if context is released
|
|
662
|
+
*/
|
|
663
|
+
checkReleased() {
|
|
664
|
+
if (this._isReleased) {
|
|
665
|
+
throw new Error(
|
|
666
|
+
"FastContext usage after release detected! (Object Pool Strict Lifecycle Guard)"
|
|
667
|
+
);
|
|
668
|
+
}
|
|
521
669
|
}
|
|
522
670
|
// ─────────────────────────────────────────────────────────────────────────
|
|
523
671
|
// Response Helpers
|
|
524
672
|
// ─────────────────────────────────────────────────────────────────────────
|
|
525
673
|
json(data, status = 200) {
|
|
674
|
+
this.checkReleased();
|
|
526
675
|
this._headers.set("Content-Type", "application/json; charset=utf-8");
|
|
527
676
|
return new Response(JSON.stringify(data), {
|
|
528
677
|
status,
|
|
@@ -530,6 +679,7 @@ var FastContext = class {
|
|
|
530
679
|
});
|
|
531
680
|
}
|
|
532
681
|
text(text, status = 200) {
|
|
682
|
+
this.checkReleased();
|
|
533
683
|
this._headers.set("Content-Type", "text/plain; charset=utf-8");
|
|
534
684
|
return new Response(text, {
|
|
535
685
|
status,
|
|
@@ -537,6 +687,7 @@ var FastContext = class {
|
|
|
537
687
|
});
|
|
538
688
|
}
|
|
539
689
|
html(html, status = 200) {
|
|
690
|
+
this.checkReleased();
|
|
540
691
|
this._headers.set("Content-Type", "text/html; charset=utf-8");
|
|
541
692
|
return new Response(html, {
|
|
542
693
|
status,
|
|
@@ -544,6 +695,7 @@ var FastContext = class {
|
|
|
544
695
|
});
|
|
545
696
|
}
|
|
546
697
|
redirect(url, status = 302) {
|
|
698
|
+
this.checkReleased();
|
|
547
699
|
this._headers.set("Location", url);
|
|
548
700
|
return new Response(null, {
|
|
549
701
|
status,
|
|
@@ -551,28 +703,89 @@ var FastContext = class {
|
|
|
551
703
|
});
|
|
552
704
|
}
|
|
553
705
|
body(data, status = 200) {
|
|
706
|
+
this.checkReleased();
|
|
554
707
|
return new Response(data, {
|
|
555
708
|
status,
|
|
556
709
|
headers: this._headers
|
|
557
710
|
});
|
|
558
711
|
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
712
|
+
stream(stream, status = 200) {
|
|
713
|
+
this.checkReleased();
|
|
714
|
+
this._headers.set("Content-Type", "application/octet-stream");
|
|
715
|
+
return new Response(stream, {
|
|
716
|
+
status,
|
|
717
|
+
headers: this._headers
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
notFound(message = "Not Found") {
|
|
721
|
+
return this.text(message, 404);
|
|
722
|
+
}
|
|
723
|
+
forbidden(message = "Forbidden") {
|
|
724
|
+
return this.text(message, 403);
|
|
725
|
+
}
|
|
726
|
+
unauthorized(message = "Unauthorized") {
|
|
727
|
+
return this.text(message, 401);
|
|
728
|
+
}
|
|
729
|
+
badRequest(message = "Bad Request") {
|
|
730
|
+
return this.text(message, 400);
|
|
731
|
+
}
|
|
732
|
+
async forward(target, _options = {}) {
|
|
733
|
+
this.checkReleased();
|
|
734
|
+
const url = new URL(this.req.url);
|
|
735
|
+
const targetUrl = new URL(
|
|
736
|
+
target.startsWith("http") ? target : `${url.protocol}//${target}${this.req.path}`
|
|
737
|
+
);
|
|
738
|
+
const searchParams = new URLSearchParams(url.search);
|
|
739
|
+
searchParams.forEach((v, k) => {
|
|
740
|
+
targetUrl.searchParams.set(k, v);
|
|
741
|
+
});
|
|
742
|
+
return fetch(targetUrl.toString(), {
|
|
743
|
+
method: this.req.method,
|
|
744
|
+
headers: this.req.raw.headers,
|
|
745
|
+
body: this.req.method !== "GET" && this.req.method !== "HEAD" ? this.req.raw.body : null,
|
|
746
|
+
// @ts-expect-error - Bun/Fetch specific
|
|
747
|
+
duplex: "half"
|
|
748
|
+
});
|
|
749
|
+
}
|
|
562
750
|
header(name, value) {
|
|
563
|
-
this.
|
|
751
|
+
this.checkReleased();
|
|
752
|
+
if (value !== void 0) {
|
|
753
|
+
this._headers.set(name, value);
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
return this.req.header(name);
|
|
564
757
|
}
|
|
565
758
|
status(_code) {
|
|
759
|
+
this.checkReleased();
|
|
760
|
+
}
|
|
761
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
762
|
+
// Context Variables
|
|
763
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
764
|
+
_store = /* @__PURE__ */ new Map();
|
|
765
|
+
get(key) {
|
|
766
|
+
return this._store.get(key);
|
|
767
|
+
}
|
|
768
|
+
set(key, value) {
|
|
769
|
+
this._store.set(key, value);
|
|
770
|
+
}
|
|
771
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
772
|
+
// Lifecycle helpers
|
|
773
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
774
|
+
route = () => "";
|
|
775
|
+
get native() {
|
|
776
|
+
return this;
|
|
566
777
|
}
|
|
567
778
|
};
|
|
568
779
|
|
|
569
780
|
// src/engine/MinimalContext.ts
|
|
570
781
|
var MinimalRequest = class {
|
|
571
|
-
constructor(_request, _params, _path) {
|
|
782
|
+
constructor(_request, _params, _path, _routePattern) {
|
|
572
783
|
this._request = _request;
|
|
573
784
|
this._params = _params;
|
|
574
785
|
this._path = _path;
|
|
786
|
+
this._routePattern = _routePattern;
|
|
575
787
|
}
|
|
788
|
+
_searchParams = null;
|
|
576
789
|
get url() {
|
|
577
790
|
return this._request.url;
|
|
578
791
|
}
|
|
@@ -582,20 +795,39 @@ var MinimalRequest = class {
|
|
|
582
795
|
get path() {
|
|
583
796
|
return this._path;
|
|
584
797
|
}
|
|
798
|
+
get routePattern() {
|
|
799
|
+
return this._routePattern;
|
|
800
|
+
}
|
|
585
801
|
param(name) {
|
|
586
802
|
return this._params[name];
|
|
587
803
|
}
|
|
588
804
|
params() {
|
|
589
805
|
return { ...this._params };
|
|
590
806
|
}
|
|
807
|
+
/**
|
|
808
|
+
* Lazy-initialize searchParams, only parse once
|
|
809
|
+
*/
|
|
810
|
+
getSearchParams() {
|
|
811
|
+
if (this._searchParams === null) {
|
|
812
|
+
const url = this._request.url;
|
|
813
|
+
const queryStart = url.indexOf("?");
|
|
814
|
+
if (queryStart === -1) {
|
|
815
|
+
this._searchParams = new URLSearchParams();
|
|
816
|
+
} else {
|
|
817
|
+
const hashStart = url.indexOf("#", queryStart);
|
|
818
|
+
const queryString = hashStart === -1 ? url.slice(queryStart + 1) : url.slice(queryStart + 1, hashStart);
|
|
819
|
+
this._searchParams = new URLSearchParams(queryString);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
return this._searchParams;
|
|
823
|
+
}
|
|
591
824
|
query(name) {
|
|
592
|
-
|
|
593
|
-
return url.searchParams.get(name) ?? void 0;
|
|
825
|
+
return this.getSearchParams().get(name) ?? void 0;
|
|
594
826
|
}
|
|
595
827
|
queries() {
|
|
596
|
-
const
|
|
828
|
+
const params = this.getSearchParams();
|
|
597
829
|
const result = {};
|
|
598
|
-
for (const [key, value] of
|
|
830
|
+
for (const [key, value] of params.entries()) {
|
|
599
831
|
const existing = result[key];
|
|
600
832
|
if (existing === void 0) {
|
|
601
833
|
result[key] = value;
|
|
@@ -631,49 +863,103 @@ var MinimalRequest = class {
|
|
|
631
863
|
}
|
|
632
864
|
};
|
|
633
865
|
var MinimalContext = class {
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
866
|
+
req;
|
|
867
|
+
_resHeaders = {};
|
|
868
|
+
constructor(request, params, path, routePattern) {
|
|
869
|
+
this.req = new MinimalRequest(request, params, path, routePattern);
|
|
637
870
|
}
|
|
638
|
-
get req() {
|
|
639
|
-
|
|
871
|
+
// get req(): FastRequest {
|
|
872
|
+
// return this._req
|
|
873
|
+
// }
|
|
874
|
+
// Response helpers - merge custom headers with defaults
|
|
875
|
+
getHeaders(contentType) {
|
|
876
|
+
return {
|
|
877
|
+
...this._resHeaders,
|
|
878
|
+
"Content-Type": contentType
|
|
879
|
+
};
|
|
640
880
|
}
|
|
641
|
-
// Response helpers - create headers inline (no reuse overhead)
|
|
642
881
|
json(data, status = 200) {
|
|
643
882
|
return new Response(JSON.stringify(data), {
|
|
644
883
|
status,
|
|
645
|
-
headers:
|
|
884
|
+
headers: this.getHeaders("application/json; charset=utf-8")
|
|
646
885
|
});
|
|
647
886
|
}
|
|
648
887
|
text(text, status = 200) {
|
|
649
888
|
return new Response(text, {
|
|
650
889
|
status,
|
|
651
|
-
headers:
|
|
890
|
+
headers: this.getHeaders("text/plain; charset=utf-8")
|
|
652
891
|
});
|
|
653
892
|
}
|
|
654
893
|
html(html, status = 200) {
|
|
655
894
|
return new Response(html, {
|
|
656
895
|
status,
|
|
657
|
-
headers:
|
|
896
|
+
headers: this.getHeaders("text/html; charset=utf-8")
|
|
658
897
|
});
|
|
659
898
|
}
|
|
660
899
|
redirect(url, status = 302) {
|
|
661
900
|
return new Response(null, {
|
|
662
901
|
status,
|
|
663
|
-
headers: { Location: url }
|
|
902
|
+
headers: { ...this._resHeaders, Location: url }
|
|
664
903
|
});
|
|
665
904
|
}
|
|
666
905
|
body(data, status = 200) {
|
|
667
|
-
return new Response(data, {
|
|
906
|
+
return new Response(data, {
|
|
907
|
+
status,
|
|
908
|
+
headers: this._resHeaders
|
|
909
|
+
});
|
|
668
910
|
}
|
|
669
|
-
header(
|
|
670
|
-
|
|
911
|
+
header(name, value) {
|
|
912
|
+
if (value !== void 0) {
|
|
913
|
+
this._resHeaders[name] = value;
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
return this.req.header(name);
|
|
671
917
|
}
|
|
672
918
|
status(_code) {
|
|
673
919
|
}
|
|
920
|
+
stream(stream, status = 200) {
|
|
921
|
+
return new Response(stream, {
|
|
922
|
+
status,
|
|
923
|
+
headers: this.getHeaders("application/octet-stream")
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
notFound(message = "Not Found") {
|
|
927
|
+
return this.text(message, 404);
|
|
928
|
+
}
|
|
929
|
+
forbidden(message = "Forbidden") {
|
|
930
|
+
return this.text(message, 403);
|
|
931
|
+
}
|
|
932
|
+
unauthorized(message = "Unauthorized") {
|
|
933
|
+
return this.text(message, 401);
|
|
934
|
+
}
|
|
935
|
+
badRequest(message = "Bad Request") {
|
|
936
|
+
return this.text(message, 400);
|
|
937
|
+
}
|
|
938
|
+
async forward(target, _options = {}) {
|
|
939
|
+
const url = new URL(this.req.url);
|
|
940
|
+
const targetUrl = new URL(
|
|
941
|
+
target.startsWith("http") ? target : `${url.protocol}//${target}${this.req.path}`
|
|
942
|
+
);
|
|
943
|
+
return fetch(targetUrl.toString(), {
|
|
944
|
+
method: this.req.method,
|
|
945
|
+
headers: this.req.raw.headers
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
get(_key) {
|
|
949
|
+
return void 0;
|
|
950
|
+
}
|
|
951
|
+
set(_key, _value) {
|
|
952
|
+
}
|
|
953
|
+
route = () => "";
|
|
954
|
+
get native() {
|
|
955
|
+
return this;
|
|
956
|
+
}
|
|
674
957
|
// Required for interface compatibility
|
|
675
|
-
|
|
676
|
-
throw new Error("MinimalContext does not support
|
|
958
|
+
init(_request, _params, _path) {
|
|
959
|
+
throw new Error("MinimalContext does not support init. Create a new instance instead.");
|
|
960
|
+
}
|
|
961
|
+
// Required for interface compatibility
|
|
962
|
+
reset() {
|
|
677
963
|
}
|
|
678
964
|
};
|
|
679
965
|
|
|
@@ -777,16 +1063,40 @@ var ObjectPool = class {
|
|
|
777
1063
|
};
|
|
778
1064
|
|
|
779
1065
|
// src/engine/Gravito.ts
|
|
1066
|
+
function compileMiddlewareChain(middleware, handler) {
|
|
1067
|
+
if (middleware.length === 0) {
|
|
1068
|
+
return handler;
|
|
1069
|
+
}
|
|
1070
|
+
let compiled = handler;
|
|
1071
|
+
for (let i = middleware.length - 1; i >= 0; i--) {
|
|
1072
|
+
const mw = middleware[i];
|
|
1073
|
+
const nextHandler = compiled;
|
|
1074
|
+
compiled = async (ctx) => {
|
|
1075
|
+
let nextResult;
|
|
1076
|
+
const next = async () => {
|
|
1077
|
+
nextResult = await nextHandler(ctx);
|
|
1078
|
+
return nextResult;
|
|
1079
|
+
};
|
|
1080
|
+
const result = await mw(ctx, next);
|
|
1081
|
+
return result ?? nextResult;
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
return compiled;
|
|
1085
|
+
}
|
|
780
1086
|
var Gravito = class {
|
|
781
1087
|
router = new AOTRouter();
|
|
782
1088
|
contextPool;
|
|
783
1089
|
errorHandler;
|
|
784
1090
|
notFoundHandler;
|
|
785
1091
|
// Direct reference to static routes Map (O(1) access)
|
|
786
|
-
|
|
1092
|
+
/** @internal */
|
|
787
1093
|
staticRoutes;
|
|
788
1094
|
// Flag: pure static app (no middleware at all) allows ultra-fast path
|
|
789
1095
|
isPureStaticApp = true;
|
|
1096
|
+
// Cache for precompiled dynamic routes
|
|
1097
|
+
compiledDynamicRoutes = /* @__PURE__ */ new Map();
|
|
1098
|
+
// Version tracking for cache invalidation
|
|
1099
|
+
middlewareVersion = 0;
|
|
790
1100
|
/**
|
|
791
1101
|
* Create a new Gravito instance
|
|
792
1102
|
*
|
|
@@ -796,7 +1106,7 @@ var Gravito = class {
|
|
|
796
1106
|
const poolSize = options.poolSize ?? 256;
|
|
797
1107
|
this.contextPool = new ObjectPool(
|
|
798
1108
|
() => new FastContext(),
|
|
799
|
-
(ctx) => ctx.reset(
|
|
1109
|
+
(ctx) => ctx.reset(),
|
|
800
1110
|
poolSize
|
|
801
1111
|
);
|
|
802
1112
|
this.contextPool.prewarm(Math.min(32, poolSize));
|
|
@@ -840,7 +1150,7 @@ var Gravito = class {
|
|
|
840
1150
|
return this.addRoute("delete", path, handlers);
|
|
841
1151
|
}
|
|
842
1152
|
/**
|
|
843
|
-
* Register a
|
|
1153
|
+
* Register a PDF route
|
|
844
1154
|
*/
|
|
845
1155
|
patch(path, ...handlers) {
|
|
846
1156
|
return this.addRoute("patch", path, handlers);
|
|
@@ -874,6 +1184,8 @@ var Gravito = class {
|
|
|
874
1184
|
} else {
|
|
875
1185
|
this.router.use(pathOrMiddleware, ...middleware);
|
|
876
1186
|
}
|
|
1187
|
+
this.middlewareVersion++;
|
|
1188
|
+
this.compileRoutes();
|
|
877
1189
|
return this;
|
|
878
1190
|
}
|
|
879
1191
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -881,19 +1193,10 @@ var Gravito = class {
|
|
|
881
1193
|
// ─────────────────────────────────────────────────────────────────────────
|
|
882
1194
|
/**
|
|
883
1195
|
* Mount a sub-application at a path prefix
|
|
884
|
-
*
|
|
885
|
-
* @example
|
|
886
|
-
* ```typescript
|
|
887
|
-
* const api = new Gravito()
|
|
888
|
-
* api.get('/users', (c) => c.json({ users: [] }))
|
|
889
|
-
*
|
|
890
|
-
* const app = new Gravito()
|
|
891
|
-
* app.route('/api', api)
|
|
892
|
-
* // Now accessible at /api/users
|
|
893
|
-
* ```
|
|
894
1196
|
*/
|
|
895
|
-
route(
|
|
896
|
-
|
|
1197
|
+
route(path, app) {
|
|
1198
|
+
this.router.mount(path, app.router);
|
|
1199
|
+
this.compileRoutes();
|
|
897
1200
|
return this;
|
|
898
1201
|
}
|
|
899
1202
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -901,14 +1204,6 @@ var Gravito = class {
|
|
|
901
1204
|
// ─────────────────────────────────────────────────────────────────────────
|
|
902
1205
|
/**
|
|
903
1206
|
* Set custom error handler
|
|
904
|
-
*
|
|
905
|
-
* @example
|
|
906
|
-
* ```typescript
|
|
907
|
-
* app.onError((err, c) => {
|
|
908
|
-
* console.error(err)
|
|
909
|
-
* return c.json({ error: err.message }, 500)
|
|
910
|
-
* })
|
|
911
|
-
* ```
|
|
912
1207
|
*/
|
|
913
1208
|
onError(handler) {
|
|
914
1209
|
this.errorHandler = handler;
|
|
@@ -916,13 +1211,6 @@ var Gravito = class {
|
|
|
916
1211
|
}
|
|
917
1212
|
/**
|
|
918
1213
|
* Set custom 404 handler
|
|
919
|
-
*
|
|
920
|
-
* @example
|
|
921
|
-
* ```typescript
|
|
922
|
-
* app.notFound((c) => {
|
|
923
|
-
* return c.json({ error: 'Not Found' }, 404)
|
|
924
|
-
* })
|
|
925
|
-
* ```
|
|
926
1214
|
*/
|
|
927
1215
|
notFound(handler) {
|
|
928
1216
|
this.notFoundHandler = handler;
|
|
@@ -932,35 +1220,44 @@ var Gravito = class {
|
|
|
932
1220
|
// Request Handling (Bun.serve integration)
|
|
933
1221
|
// ─────────────────────────────────────────────────────────────────────────
|
|
934
1222
|
/**
|
|
935
|
-
*
|
|
1223
|
+
* Predictive Route Warming (JIT Optimization)
|
|
936
1224
|
*
|
|
937
|
-
*
|
|
938
|
-
*
|
|
1225
|
+
* Simulates requests to specified routes to trigger JIT compilation (FTL)
|
|
1226
|
+
* before real traffic arrives.
|
|
939
1227
|
*
|
|
940
|
-
* @param
|
|
941
|
-
|
|
1228
|
+
* @param paths List of paths to warm up (e.g. ['/api/users', '/health'])
|
|
1229
|
+
*/
|
|
1230
|
+
async warmup(paths) {
|
|
1231
|
+
const dummyReqOpts = { headers: { "User-Agent": "Gravito-Warmup/1.0" } };
|
|
1232
|
+
for (const path of paths) {
|
|
1233
|
+
const req = new Request(`http://localhost${path}`, dummyReqOpts);
|
|
1234
|
+
await this.fetch(req);
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1238
|
+
* Handle an incoming request
|
|
942
1239
|
*/
|
|
943
|
-
fetch = (request) => {
|
|
1240
|
+
fetch = async (request) => {
|
|
944
1241
|
const path = extractPath(request.url);
|
|
945
1242
|
const method = request.method.toLowerCase();
|
|
946
1243
|
const staticKey = `${method}:${path}`;
|
|
947
1244
|
const staticRoute = this.staticRoutes.get(staticKey);
|
|
948
1245
|
if (staticRoute) {
|
|
949
1246
|
if (staticRoute.useMinimal) {
|
|
950
|
-
const ctx = new MinimalContext(request, {}, path);
|
|
1247
|
+
const ctx = new MinimalContext(request, {}, path, path);
|
|
951
1248
|
try {
|
|
952
1249
|
const result = staticRoute.handler(ctx);
|
|
953
1250
|
if (result instanceof Response) {
|
|
954
1251
|
return result;
|
|
955
1252
|
}
|
|
956
|
-
return result;
|
|
1253
|
+
return await result;
|
|
957
1254
|
} catch (error) {
|
|
958
1255
|
return this.handleErrorSync(error, request, path);
|
|
959
1256
|
}
|
|
960
1257
|
}
|
|
961
|
-
return this.handleWithMiddleware(request, path, staticRoute);
|
|
1258
|
+
return await this.handleWithMiddleware(request, path, staticRoute);
|
|
962
1259
|
}
|
|
963
|
-
return this.handleDynamicRoute(request, method, path);
|
|
1260
|
+
return await this.handleDynamicRoute(request, method, path);
|
|
964
1261
|
};
|
|
965
1262
|
/**
|
|
966
1263
|
* Handle routes with middleware (async path)
|
|
@@ -968,7 +1265,10 @@ var Gravito = class {
|
|
|
968
1265
|
async handleWithMiddleware(request, path, route) {
|
|
969
1266
|
const ctx = this.contextPool.acquire();
|
|
970
1267
|
try {
|
|
971
|
-
ctx.
|
|
1268
|
+
ctx.init(request, {}, path, path);
|
|
1269
|
+
if (route.compiled) {
|
|
1270
|
+
return await route.compiled(ctx);
|
|
1271
|
+
}
|
|
972
1272
|
const middleware = this.collectMiddlewareForPath(path, route.middleware);
|
|
973
1273
|
if (middleware.length === 0) {
|
|
974
1274
|
return await route.handler(ctx);
|
|
@@ -988,14 +1288,21 @@ var Gravito = class {
|
|
|
988
1288
|
if (!match.handler) {
|
|
989
1289
|
return this.handleNotFoundSync(request, path);
|
|
990
1290
|
}
|
|
1291
|
+
const cacheKey = `${method}:${match.routePattern ?? path}`;
|
|
1292
|
+
let entry = this.compiledDynamicRoutes.get(cacheKey);
|
|
1293
|
+
if (!entry || entry.version !== this.middlewareVersion) {
|
|
1294
|
+
const compiled = compileMiddlewareChain(match.middleware, match.handler);
|
|
1295
|
+
if (this.compiledDynamicRoutes.size > 1e3) {
|
|
1296
|
+
this.compiledDynamicRoutes.clear();
|
|
1297
|
+
}
|
|
1298
|
+
entry = { compiled, version: this.middlewareVersion };
|
|
1299
|
+
this.compiledDynamicRoutes.set(cacheKey, entry);
|
|
1300
|
+
}
|
|
991
1301
|
const ctx = this.contextPool.acquire();
|
|
992
1302
|
const execute = async () => {
|
|
993
1303
|
try {
|
|
994
|
-
ctx.
|
|
995
|
-
|
|
996
|
-
return await match.handler(ctx);
|
|
997
|
-
}
|
|
998
|
-
return await this.executeMiddleware(ctx, match.middleware, match.handler);
|
|
1304
|
+
ctx.init(request, match.params, path, match.routePattern);
|
|
1305
|
+
return await entry?.compiled(ctx);
|
|
999
1306
|
} catch (error) {
|
|
1000
1307
|
return await this.handleError(error, ctx);
|
|
1001
1308
|
} finally {
|
|
@@ -1017,16 +1324,10 @@ var Gravito = class {
|
|
|
1017
1324
|
return result;
|
|
1018
1325
|
}
|
|
1019
1326
|
console.error("Unhandled error:", error);
|
|
1020
|
-
return new Response(
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
}),
|
|
1025
|
-
{
|
|
1026
|
-
status: 500,
|
|
1027
|
-
headers: { "Content-Type": "application/json" }
|
|
1028
|
-
}
|
|
1029
|
-
);
|
|
1327
|
+
return new Response(CACHED_RESPONSES.INTERNAL_ERROR, {
|
|
1328
|
+
status: 500,
|
|
1329
|
+
headers: HEADERS.JSON
|
|
1330
|
+
});
|
|
1030
1331
|
}
|
|
1031
1332
|
/**
|
|
1032
1333
|
* Sync 404 handler (for ultra-fast path)
|
|
@@ -1040,14 +1341,13 @@ var Gravito = class {
|
|
|
1040
1341
|
}
|
|
1041
1342
|
return result;
|
|
1042
1343
|
}
|
|
1043
|
-
return new Response(
|
|
1344
|
+
return new Response(CACHED_RESPONSES.NOT_FOUND, {
|
|
1044
1345
|
status: 404,
|
|
1045
|
-
headers:
|
|
1346
|
+
headers: HEADERS.JSON
|
|
1046
1347
|
});
|
|
1047
1348
|
}
|
|
1048
1349
|
/**
|
|
1049
1350
|
* Collect middleware for a specific path
|
|
1050
|
-
* (Simplified version - assumes we've already checked for pure static)
|
|
1051
1351
|
*/
|
|
1052
1352
|
collectMiddlewareForPath(path, routeMiddleware) {
|
|
1053
1353
|
if (this.router.globalMiddleware.length === 0 && this.router.pathMiddleware.size === 0) {
|
|
@@ -1057,22 +1357,26 @@ var Gravito = class {
|
|
|
1057
1357
|
}
|
|
1058
1358
|
/**
|
|
1059
1359
|
* Compile routes for optimization
|
|
1060
|
-
* Called once during initialization and when routes change
|
|
1061
1360
|
*/
|
|
1062
1361
|
compileRoutes() {
|
|
1063
1362
|
this.staticRoutes = this.router.staticRoutes;
|
|
1064
1363
|
const hasGlobalMiddleware = this.router.globalMiddleware.length > 0;
|
|
1065
1364
|
const hasPathMiddleware = this.router.pathMiddleware.size > 0;
|
|
1066
1365
|
this.isPureStaticApp = !hasGlobalMiddleware && !hasPathMiddleware;
|
|
1067
|
-
for (const [
|
|
1366
|
+
for (const [key, route] of this.staticRoutes) {
|
|
1367
|
+
if (route.compiledVersion === this.middlewareVersion) {
|
|
1368
|
+
continue;
|
|
1369
|
+
}
|
|
1068
1370
|
const analysis = analyzeHandler(route.handler);
|
|
1069
1371
|
const optimalType = getOptimalContextType(analysis);
|
|
1070
1372
|
route.useMinimal = this.isPureStaticApp && route.middleware.length === 0 && optimalType === "minimal";
|
|
1373
|
+
if (!route.useMinimal) {
|
|
1374
|
+
const allMiddleware = this.collectMiddlewareForPath(key.split(":")[1], route.middleware);
|
|
1375
|
+
route.compiled = compileMiddlewareChain(allMiddleware, route.handler);
|
|
1376
|
+
}
|
|
1377
|
+
route.compiledVersion = this.middlewareVersion;
|
|
1071
1378
|
}
|
|
1072
1379
|
}
|
|
1073
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1074
|
-
// Internal Methods
|
|
1075
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
1076
1380
|
/**
|
|
1077
1381
|
* Add a route to the router
|
|
1078
1382
|
*/
|
|
@@ -1088,9 +1392,6 @@ var Gravito = class {
|
|
|
1088
1392
|
}
|
|
1089
1393
|
/**
|
|
1090
1394
|
* Execute middleware chain followed by handler
|
|
1091
|
-
*
|
|
1092
|
-
* Implements the standard middleware pattern:
|
|
1093
|
-
* Each middleware can call next() to continue the chain.
|
|
1094
1395
|
*/
|
|
1095
1396
|
async executeMiddleware(ctx, middleware, handler) {
|
|
1096
1397
|
let index = 0;
|
|
@@ -1107,16 +1408,6 @@ var Gravito = class {
|
|
|
1107
1408
|
}
|
|
1108
1409
|
return await handler(ctx);
|
|
1109
1410
|
}
|
|
1110
|
-
/*
|
|
1111
|
-
* Handle 404 Not Found (Async version for dynamic/middleware paths)
|
|
1112
|
-
* Note: Currently unused as we handle 404s via handleNotFoundSync or inline
|
|
1113
|
-
*/
|
|
1114
|
-
// private async handleNotFound(ctx: FastContext): Promise<Response> {
|
|
1115
|
-
// if (this.notFoundHandler) {
|
|
1116
|
-
// return await this.notFoundHandler(ctx)
|
|
1117
|
-
// }
|
|
1118
|
-
// return ctx.json({ error: 'Not Found' }, 404)
|
|
1119
|
-
// }
|
|
1120
1411
|
/**
|
|
1121
1412
|
* Handle errors (Async version for dynamic/middleware paths)
|
|
1122
1413
|
*/
|