@gravito/core 1.2.1 → 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.
@@ -0,0 +1,1467 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/engine/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AOTRouter: () => AOTRouter,
24
+ FastContextImpl: () => FastContext,
25
+ Gravito: () => Gravito,
26
+ MinimalContext: () => MinimalContext,
27
+ ObjectPool: () => ObjectPool,
28
+ extractPath: () => extractPath
29
+ });
30
+ module.exports = __toCommonJS(index_exports);
31
+
32
+ // src/adapters/bun/RadixNode.ts
33
+ var RadixNode = class _RadixNode {
34
+ // Path segment for this node (e.g., "users", ":id")
35
+ segment;
36
+ // Node type (Static, Param, Wildcard)
37
+ type;
38
+ // Children nodes (mapped by segment for fast lookup)
39
+ children = /* @__PURE__ */ new Map();
40
+ // Specialized child for parameter node (only one per level allowed usually to avoid ambiguity, though some routers support multiple)
41
+ paramChild = null;
42
+ // Specialized child for wildcard node
43
+ wildcardChild = null;
44
+ // Handlers registered at this node (keyed by HTTP method)
45
+ handlers = /* @__PURE__ */ new Map();
46
+ // Parameter name if this is a PARAM node (e.g., "id" for ":id")
47
+ paramName = null;
48
+ // Parameter constraints (regex) - only applicable if this is a PARAM node
49
+ // If we support per-route constraints, they might need to be stored differently,
50
+ // but for now assume constraints are defined at node level (uncommon) or checked at match time.
51
+ // Laravel allows global pattern constraints or per-route.
52
+ // Ideally, constraints should be stored with the handler or part of matching logic.
53
+ // For a Radix tree, if we have constraints, we might need to backtrack if constraint fails?
54
+ // Or simply store constraint with the param node.
55
+ regex = null;
56
+ constructor(segment = "", type = 0 /* STATIC */) {
57
+ this.segment = segment;
58
+ this.type = type;
59
+ }
60
+ toJSON() {
61
+ return {
62
+ segment: this.segment,
63
+ type: this.type,
64
+ children: Array.from(this.children.entries()).map(([k, v]) => [k, v.toJSON()]),
65
+ paramChild: this.paramChild?.toJSON() || null,
66
+ wildcardChild: this.wildcardChild?.toJSON() || null,
67
+ paramName: this.paramName,
68
+ regex: this.regex ? this.regex.source : null
69
+ };
70
+ }
71
+ static fromJSON(json) {
72
+ const node = new _RadixNode(json.segment, json.type);
73
+ node.paramName = json.paramName;
74
+ if (json.regex) {
75
+ node.regex = new RegExp(json.regex);
76
+ }
77
+ if (json.children) {
78
+ for (const [key, childJson] of json.children) {
79
+ node.children.set(key, _RadixNode.fromJSON(childJson));
80
+ }
81
+ }
82
+ if (json.paramChild) {
83
+ node.paramChild = _RadixNode.fromJSON(json.paramChild);
84
+ }
85
+ if (json.wildcardChild) {
86
+ node.wildcardChild = _RadixNode.fromJSON(json.wildcardChild);
87
+ }
88
+ return node;
89
+ }
90
+ };
91
+
92
+ // src/adapters/bun/RadixRouter.ts
93
+ var RadixRouter = class _RadixRouter {
94
+ root = new RadixNode();
95
+ // Global parameter constraints (e.g., id => /^\d+$/)
96
+ globalConstraints = /* @__PURE__ */ new Map();
97
+ /**
98
+ * Add a generic parameter constraint
99
+ */
100
+ where(param, regex) {
101
+ this.globalConstraints.set(param, regex);
102
+ }
103
+ /**
104
+ * Register a route
105
+ */
106
+ add(method, path, handlers) {
107
+ let node = this.root;
108
+ const segments = this.splitPath(path);
109
+ for (let i = 0; i < segments.length; i++) {
110
+ const segment = segments[i];
111
+ if (segment === "*") {
112
+ if (!node.wildcardChild) {
113
+ node.wildcardChild = new RadixNode("*", 2 /* WILDCARD */);
114
+ }
115
+ node = node.wildcardChild;
116
+ break;
117
+ } else if (segment.startsWith(":")) {
118
+ const paramName = segment.slice(1);
119
+ if (!node.paramChild) {
120
+ const child = new RadixNode(segment, 1 /* PARAM */);
121
+ child.paramName = paramName;
122
+ const constraint = this.globalConstraints.get(paramName);
123
+ if (constraint) {
124
+ child.regex = constraint;
125
+ }
126
+ node.paramChild = child;
127
+ }
128
+ node = node.paramChild;
129
+ } else {
130
+ if (!node.children.has(segment)) {
131
+ node.children.set(segment, new RadixNode(segment, 0 /* STATIC */));
132
+ }
133
+ node = node.children.get(segment);
134
+ }
135
+ }
136
+ node.handlers.set(method.toLowerCase(), handlers);
137
+ }
138
+ /**
139
+ * Match a request
140
+ */
141
+ match(method, path) {
142
+ const normalizedMethod = method.toLowerCase();
143
+ if (path === "/" || path === "") {
144
+ const handlers = this.root.handlers.get(normalizedMethod);
145
+ if (handlers) {
146
+ return { handlers, params: {} };
147
+ }
148
+ return null;
149
+ }
150
+ const searchPath = path.startsWith("/") ? path.slice(1) : path;
151
+ const segments = searchPath.split("/");
152
+ return this.matchRecursive(this.root, segments, 0, {}, normalizedMethod);
153
+ }
154
+ matchRecursive(node, segments, depth, params, method) {
155
+ if (depth >= segments.length) {
156
+ let handlers = node.handlers.get(method);
157
+ if (!handlers) {
158
+ handlers = node.handlers.get("all");
159
+ }
160
+ if (handlers) {
161
+ return { handlers, params };
162
+ }
163
+ return null;
164
+ }
165
+ const segment = segments[depth];
166
+ const staticChild = node.children.get(segment);
167
+ if (staticChild) {
168
+ const match = this.matchRecursive(staticChild, segments, depth + 1, params, method);
169
+ if (match) {
170
+ return match;
171
+ }
172
+ }
173
+ const paramChild = node.paramChild;
174
+ if (paramChild) {
175
+ if (paramChild.regex && !paramChild.regex.test(segment)) {
176
+ } else {
177
+ if (paramChild.paramName) {
178
+ params[paramChild.paramName] = decodeURIComponent(segment);
179
+ const match = this.matchRecursive(paramChild, segments, depth + 1, params, method);
180
+ if (match) {
181
+ return match;
182
+ }
183
+ delete params[paramChild.paramName];
184
+ }
185
+ }
186
+ }
187
+ if (node.wildcardChild) {
188
+ let handlers = node.wildcardChild.handlers.get(method);
189
+ if (!handlers) {
190
+ handlers = node.wildcardChild.handlers.get("all");
191
+ }
192
+ if (handlers) {
193
+ return { handlers, params };
194
+ }
195
+ }
196
+ return null;
197
+ }
198
+ splitPath(path) {
199
+ if (path === "/" || path === "") {
200
+ return [];
201
+ }
202
+ let p = path;
203
+ if (p.startsWith("/")) {
204
+ p = p.slice(1);
205
+ }
206
+ if (p.endsWith("/")) {
207
+ p = p.slice(0, -1);
208
+ }
209
+ return p.split("/");
210
+ }
211
+ /**
212
+ * Serialize the router to a JSON string
213
+ */
214
+ serialize() {
215
+ return JSON.stringify({
216
+ root: this.root.toJSON(),
217
+ globalConstraints: Array.from(this.globalConstraints.entries()).map(([k, v]) => [
218
+ k,
219
+ v.source
220
+ ])
221
+ });
222
+ }
223
+ /**
224
+ * Restore a router from a serialized JSON string
225
+ */
226
+ static fromSerialized(json) {
227
+ const data = JSON.parse(json);
228
+ const router = new _RadixRouter();
229
+ router.root = RadixNode.fromJSON(data.root);
230
+ if (data.globalConstraints) {
231
+ for (const [key, source] of data.globalConstraints) {
232
+ router.globalConstraints.set(key, new RegExp(source));
233
+ }
234
+ }
235
+ return router;
236
+ }
237
+ };
238
+
239
+ // src/engine/AOTRouter.ts
240
+ var AOTRouter = class {
241
+ // Static route cache: "METHOD:PATH" -> RouteMetadata
242
+ /** @internal */
243
+ staticRoutes = /* @__PURE__ */ new Map();
244
+ // Dynamic route handler (Radix Tree)
245
+ dynamicRouter = new RadixRouter();
246
+ // Store all route definitions to support mounting/merging
247
+ /** @internal */
248
+ routeDefinitions = [];
249
+ // Global middleware (applies to all routes)
250
+ /** @internal */
251
+ globalMiddleware = [];
252
+ // Path-based middleware: pattern -> middleware[]
253
+ /** @internal */
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;
261
+ /**
262
+ * Register a route
263
+ *
264
+ * Automatically determines if route is static or dynamic.
265
+ * Static routes are stored in a Map for O(1) lookup.
266
+ * Dynamic routes use the Radix Tree.
267
+ *
268
+ * @param method - HTTP method
269
+ * @param path - Route path
270
+ * @param handler - Route handler
271
+ * @param middleware - Route-specific middleware
272
+ */
273
+ add(method, path, handler, middleware = []) {
274
+ this.routeDefinitions.push({ method, path, handler, middleware });
275
+ const normalizedMethod = method.toLowerCase();
276
+ if (this.isStaticPath(path)) {
277
+ const key = `${normalizedMethod}:${path}`;
278
+ this.staticRoutes.set(key, { handler, middleware });
279
+ } else {
280
+ const wrappedHandler = handler;
281
+ this.dynamicRouter.add(normalizedMethod, path, [wrappedHandler]);
282
+ this.dynamicRoutePatterns.set(wrappedHandler, path);
283
+ if (middleware.length > 0) {
284
+ this.pathMiddleware.set(`${normalizedMethod}:${path}`, middleware);
285
+ }
286
+ }
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
+ }
323
+ /**
324
+ * Add global middleware
325
+ *
326
+ * These run for every request, before route-specific middleware.
327
+ *
328
+ * @param middleware - Middleware functions
329
+ */
330
+ use(...middleware) {
331
+ this.globalMiddleware.push(...middleware);
332
+ this.version++;
333
+ }
334
+ /**
335
+ * Add path-based middleware
336
+ *
337
+ * Supports wildcard patterns like '/api/*'
338
+ *
339
+ * @param pattern - Path pattern
340
+ * @param middleware - Middleware functions
341
+ */
342
+ usePattern(pattern, ...middleware) {
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++;
350
+ }
351
+ /**
352
+ * Match a request to a route
353
+ *
354
+ * Returns the handler, params, and all applicable middleware.
355
+ *
356
+ * @param method - HTTP method
357
+ * @param path - Request path
358
+ * @returns Route match or null if not found
359
+ */
360
+ match(method, path) {
361
+ const normalizedMethod = method.toLowerCase();
362
+ const staticKey = `${normalizedMethod}:${path}`;
363
+ const staticRoute = this.staticRoutes.get(staticKey);
364
+ if (staticRoute) {
365
+ return {
366
+ handler: staticRoute.handler,
367
+ params: {},
368
+ middleware: this.collectMiddleware(path, staticRoute.middleware),
369
+ routePattern: path
370
+ };
371
+ }
372
+ const match = this.dynamicRouter.match(normalizedMethod, path);
373
+ if (match && match.handlers.length > 0) {
374
+ const handler = match.handlers[0];
375
+ const wrappedHandler = match.handlers[0];
376
+ const routePattern = this.dynamicRoutePatterns.get(wrappedHandler);
377
+ const routeKey = routePattern ? `${normalizedMethod}:${routePattern}` : null;
378
+ const routeMiddleware = routeKey ? this.pathMiddleware.get(routeKey) ?? [] : [];
379
+ return {
380
+ handler,
381
+ params: match.params,
382
+ middleware: this.collectMiddleware(path, routeMiddleware),
383
+ routePattern
384
+ };
385
+ }
386
+ return {
387
+ handler: null,
388
+ params: {},
389
+ middleware: []
390
+ };
391
+ }
392
+ /**
393
+ * Public wrapper for collectMiddleware (used by Gravito for optimization)
394
+ */
395
+ collectMiddlewarePublic(path, routeMiddleware) {
396
+ return this.collectMiddleware(path, routeMiddleware);
397
+ }
398
+ /**
399
+ * Collect all applicable middleware for a path
400
+ *
401
+ * Order: global -> pattern-based -> route-specific
402
+ *
403
+ * @param path - Request path
404
+ * @param routeMiddleware - Route-specific middleware
405
+ * @returns Combined middleware array
406
+ */
407
+ collectMiddleware(path, routeMiddleware) {
408
+ if (this.globalMiddleware.length === 0 && this.pathMiddleware.size === 0 && routeMiddleware.length === 0) {
409
+ return [];
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
+ }
416
+ const middleware = [];
417
+ if (this.globalMiddleware.length > 0) {
418
+ middleware.push(...this.globalMiddleware);
419
+ }
420
+ if (this.pathMiddleware.size > 0) {
421
+ for (const [pattern, mw] of this.pathMiddleware) {
422
+ if (pattern.includes(":")) {
423
+ continue;
424
+ }
425
+ if (this.matchPattern(pattern, path)) {
426
+ middleware.push(...mw);
427
+ }
428
+ }
429
+ }
430
+ if (routeMiddleware.length > 0) {
431
+ middleware.push(...routeMiddleware);
432
+ }
433
+ if (this.middlewareCache.size < this.cacheMaxSize) {
434
+ this.middlewareCache.set(cacheKey, { data: middleware, version: this.version });
435
+ }
436
+ return middleware;
437
+ }
438
+ /**
439
+ * Check if a path is static (no parameters or wildcards)
440
+ */
441
+ isStaticPath(path) {
442
+ return !path.includes(":") && !path.includes("*");
443
+ }
444
+ /**
445
+ * Match a pattern against a path
446
+ *
447
+ * Supports:
448
+ * - Exact match: '/api/users'
449
+ * - Wildcard suffix: '/api/*'
450
+ *
451
+ * @param pattern - Pattern to match
452
+ * @param path - Path to test
453
+ * @returns True if pattern matches
454
+ */
455
+ matchPattern(pattern, path) {
456
+ if (pattern === "*") {
457
+ return true;
458
+ }
459
+ if (pattern === path) {
460
+ return true;
461
+ }
462
+ if (pattern.endsWith("/*")) {
463
+ const prefix = pattern.slice(0, -2);
464
+ return path.startsWith(prefix);
465
+ }
466
+ return false;
467
+ }
468
+ /**
469
+ * Get all registered routes (for debugging)
470
+ */
471
+ getRoutes() {
472
+ const routes = [];
473
+ for (const key of this.staticRoutes.keys()) {
474
+ const [method, path] = key.split(":");
475
+ routes.push({ method, path, type: "static" });
476
+ }
477
+ return routes;
478
+ }
479
+ };
480
+
481
+ // src/engine/analyzer.ts
482
+ function analyzeHandler(handler) {
483
+ const source = handler.toString();
484
+ return {
485
+ usesHeaders: source.includes(".header(") || source.includes(".header)") || source.includes(".headers(") || source.includes(".headers)"),
486
+ usesQuery: source.includes(".query(") || source.includes(".query)") || source.includes(".queries(") || source.includes(".queries)"),
487
+ usesBody: source.includes(".json()") || source.includes(".text()") || source.includes(".formData()") || source.includes(".body"),
488
+ usesParams: source.includes(".param(") || source.includes(".param)") || source.includes(".params(") || source.includes(".params)"),
489
+ isAsync: source.includes("async") || source.includes("await")
490
+ };
491
+ }
492
+ function getOptimalContextType(analysis) {
493
+ if (analysis.usesHeaders) {
494
+ return "fast";
495
+ }
496
+ if (!analysis.usesQuery && !analysis.usesBody && !analysis.usesParams) {
497
+ return "minimal";
498
+ }
499
+ if (!analysis.usesQuery && !analysis.usesBody && analysis.usesParams) {
500
+ return "minimal";
501
+ }
502
+ if (analysis.usesBody) {
503
+ return "full";
504
+ }
505
+ return "fast";
506
+ }
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
+
522
+ // src/engine/FastContext.ts
523
+ var FastRequestImpl = class {
524
+ _request;
525
+ _params;
526
+ _path;
527
+ _routePattern;
528
+ _url = null;
529
+ _query = null;
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
+ }
538
+ /**
539
+ * Initialize for new request
540
+ */
541
+ init(request, params = {}, path = "", routePattern) {
542
+ this._request = request;
543
+ this._params = params;
544
+ this._path = path;
545
+ this._routePattern = routePattern;
546
+ this._url = null;
547
+ this._query = null;
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
+ }
571
+ }
572
+ get url() {
573
+ this.checkReleased();
574
+ return this._request.url;
575
+ }
576
+ get method() {
577
+ this.checkReleased();
578
+ return this._request.method;
579
+ }
580
+ get path() {
581
+ this.checkReleased();
582
+ return this._path;
583
+ }
584
+ get routePattern() {
585
+ this.checkReleased();
586
+ return this._routePattern;
587
+ }
588
+ param(name) {
589
+ this.checkReleased();
590
+ return this._params[name];
591
+ }
592
+ params() {
593
+ this.checkReleased();
594
+ return { ...this._params };
595
+ }
596
+ getUrl() {
597
+ if (!this._url) {
598
+ this._url = new URL(this._request.url);
599
+ }
600
+ return this._url;
601
+ }
602
+ query(name) {
603
+ this.checkReleased();
604
+ if (!this._query) {
605
+ this._query = this.getUrl().searchParams;
606
+ }
607
+ return this._query.get(name) ?? void 0;
608
+ }
609
+ queries() {
610
+ this.checkReleased();
611
+ if (!this._query) {
612
+ this._query = this.getUrl().searchParams;
613
+ }
614
+ const result = {};
615
+ for (const [key, value] of this._query.entries()) {
616
+ const existing = result[key];
617
+ if (existing === void 0) {
618
+ result[key] = value;
619
+ } else if (Array.isArray(existing)) {
620
+ existing.push(value);
621
+ } else {
622
+ result[key] = [existing, value];
623
+ }
624
+ }
625
+ return result;
626
+ }
627
+ header(name) {
628
+ this.checkReleased();
629
+ return this._request.headers.get(name) ?? void 0;
630
+ }
631
+ headers() {
632
+ this.checkReleased();
633
+ if (!this._headers) {
634
+ this._headers = {};
635
+ for (const [key, value] of this._request.headers.entries()) {
636
+ this._headers[key] = value;
637
+ }
638
+ }
639
+ return { ...this._headers };
640
+ }
641
+ async json() {
642
+ this.checkReleased();
643
+ if (!this._jsonParsed) {
644
+ this._cachedJson = await this._request.json();
645
+ this._jsonParsed = true;
646
+ }
647
+ return this._cachedJson;
648
+ }
649
+ async text() {
650
+ this.checkReleased();
651
+ return this._request.text();
652
+ }
653
+ async formData() {
654
+ this.checkReleased();
655
+ return this._request.formData();
656
+ }
657
+ get raw() {
658
+ this.checkReleased();
659
+ return this._request;
660
+ }
661
+ };
662
+ var FastContext = class {
663
+ req = new FastRequestImpl(this);
664
+ // private _statusCode = 200
665
+ _headers = new Headers();
666
+ // Reuse this object
667
+ _isReleased = false;
668
+ // Made public for internal check access
669
+ /**
670
+ * Initialize context for a new request
671
+ *
672
+ * This is called when acquiring from the pool.
673
+ */
674
+ init(request, params = {}, path = "", routePattern) {
675
+ this._isReleased = false;
676
+ this.req.init(request, params, path, routePattern);
677
+ this._headers = new Headers();
678
+ return this;
679
+ }
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
+ }
700
+ }
701
+ // ─────────────────────────────────────────────────────────────────────────
702
+ // Response Helpers
703
+ // ─────────────────────────────────────────────────────────────────────────
704
+ json(data, status = 200) {
705
+ this.checkReleased();
706
+ this._headers.set("Content-Type", "application/json; charset=utf-8");
707
+ return new Response(JSON.stringify(data), {
708
+ status,
709
+ headers: this._headers
710
+ });
711
+ }
712
+ text(text, status = 200) {
713
+ this.checkReleased();
714
+ this._headers.set("Content-Type", "text/plain; charset=utf-8");
715
+ return new Response(text, {
716
+ status,
717
+ headers: this._headers
718
+ });
719
+ }
720
+ html(html, status = 200) {
721
+ this.checkReleased();
722
+ this._headers.set("Content-Type", "text/html; charset=utf-8");
723
+ return new Response(html, {
724
+ status,
725
+ headers: this._headers
726
+ });
727
+ }
728
+ redirect(url, status = 302) {
729
+ this.checkReleased();
730
+ this._headers.set("Location", url);
731
+ return new Response(null, {
732
+ status,
733
+ headers: this._headers
734
+ });
735
+ }
736
+ body(data, status = 200) {
737
+ this.checkReleased();
738
+ return new Response(data, {
739
+ status,
740
+ headers: this._headers
741
+ });
742
+ }
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
+ }
781
+ header(name, value) {
782
+ this.checkReleased();
783
+ if (value !== void 0) {
784
+ this._headers.set(name, value);
785
+ return;
786
+ }
787
+ return this.req.header(name);
788
+ }
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;
808
+ }
809
+ };
810
+
811
+ // src/engine/MinimalContext.ts
812
+ var MinimalRequest = class {
813
+ constructor(_request, _params, _path, _routePattern) {
814
+ this._request = _request;
815
+ this._params = _params;
816
+ this._path = _path;
817
+ this._routePattern = _routePattern;
818
+ }
819
+ _searchParams = null;
820
+ get url() {
821
+ return this._request.url;
822
+ }
823
+ get method() {
824
+ return this._request.method;
825
+ }
826
+ get path() {
827
+ return this._path;
828
+ }
829
+ get routePattern() {
830
+ return this._routePattern;
831
+ }
832
+ param(name) {
833
+ return this._params[name];
834
+ }
835
+ params() {
836
+ return { ...this._params };
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
+ }
855
+ query(name) {
856
+ return this.getSearchParams().get(name) ?? void 0;
857
+ }
858
+ queries() {
859
+ const params = this.getSearchParams();
860
+ const result = {};
861
+ for (const [key, value] of params.entries()) {
862
+ const existing = result[key];
863
+ if (existing === void 0) {
864
+ result[key] = value;
865
+ } else if (Array.isArray(existing)) {
866
+ existing.push(value);
867
+ } else {
868
+ result[key] = [existing, value];
869
+ }
870
+ }
871
+ return result;
872
+ }
873
+ header(name) {
874
+ return this._request.headers.get(name) ?? void 0;
875
+ }
876
+ headers() {
877
+ const result = {};
878
+ for (const [key, value] of this._request.headers.entries()) {
879
+ result[key] = value;
880
+ }
881
+ return result;
882
+ }
883
+ async json() {
884
+ return this._request.json();
885
+ }
886
+ async text() {
887
+ return this._request.text();
888
+ }
889
+ async formData() {
890
+ return this._request.formData();
891
+ }
892
+ get raw() {
893
+ return this._request;
894
+ }
895
+ };
896
+ var MinimalContext = class {
897
+ req;
898
+ _resHeaders = {};
899
+ constructor(request, params, path, routePattern) {
900
+ this.req = new MinimalRequest(request, params, path, routePattern);
901
+ }
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
+ };
911
+ }
912
+ json(data, status = 200) {
913
+ return new Response(JSON.stringify(data), {
914
+ status,
915
+ headers: this.getHeaders("application/json; charset=utf-8")
916
+ });
917
+ }
918
+ text(text, status = 200) {
919
+ return new Response(text, {
920
+ status,
921
+ headers: this.getHeaders("text/plain; charset=utf-8")
922
+ });
923
+ }
924
+ html(html, status = 200) {
925
+ return new Response(html, {
926
+ status,
927
+ headers: this.getHeaders("text/html; charset=utf-8")
928
+ });
929
+ }
930
+ redirect(url, status = 302) {
931
+ return new Response(null, {
932
+ status,
933
+ headers: { ...this._resHeaders, Location: url }
934
+ });
935
+ }
936
+ body(data, status = 200) {
937
+ return new Response(data, {
938
+ status,
939
+ headers: this._resHeaders
940
+ });
941
+ }
942
+ header(name, value) {
943
+ if (value !== void 0) {
944
+ this._resHeaders[name] = value;
945
+ return;
946
+ }
947
+ return this.req.header(name);
948
+ }
949
+ status(_code) {
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
+ }
988
+ // Required for interface compatibility
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() {
994
+ }
995
+ };
996
+
997
+ // src/engine/path.ts
998
+ function extractPath(url) {
999
+ const protocolEnd = url.indexOf("://");
1000
+ const searchStart = protocolEnd === -1 ? 0 : protocolEnd + 3;
1001
+ const pathStart = url.indexOf("/", searchStart);
1002
+ if (pathStart === -1) {
1003
+ return "/";
1004
+ }
1005
+ const queryStart = url.indexOf("?", pathStart);
1006
+ if (queryStart === -1) {
1007
+ return url.slice(pathStart);
1008
+ }
1009
+ return url.slice(pathStart, queryStart);
1010
+ }
1011
+
1012
+ // src/engine/pool.ts
1013
+ var ObjectPool = class {
1014
+ pool = [];
1015
+ factory;
1016
+ reset;
1017
+ maxSize;
1018
+ /**
1019
+ * Create a new object pool
1020
+ *
1021
+ * @param factory - Function to create new objects
1022
+ * @param reset - Function to reset objects before reuse
1023
+ * @param maxSize - Maximum pool size (default: 256)
1024
+ */
1025
+ constructor(factory, reset, maxSize = 256) {
1026
+ this.factory = factory;
1027
+ this.reset = reset;
1028
+ this.maxSize = maxSize;
1029
+ }
1030
+ /**
1031
+ * Acquire an object from the pool
1032
+ *
1033
+ * If the pool is empty, creates a new object (overflow strategy).
1034
+ * This ensures the pool never blocks under high load.
1035
+ *
1036
+ * @returns Object from pool or newly created
1037
+ */
1038
+ acquire() {
1039
+ const obj = this.pool.pop();
1040
+ if (obj !== void 0) {
1041
+ return obj;
1042
+ }
1043
+ return this.factory();
1044
+ }
1045
+ /**
1046
+ * Release an object back to the pool
1047
+ *
1048
+ * If the pool is full, the object is discarded (will be GC'd).
1049
+ * This prevents unbounded memory growth.
1050
+ *
1051
+ * @param obj - Object to release
1052
+ */
1053
+ release(obj) {
1054
+ if (this.pool.length < this.maxSize) {
1055
+ this.reset(obj);
1056
+ this.pool.push(obj);
1057
+ }
1058
+ }
1059
+ /**
1060
+ * Clear all objects from the pool
1061
+ *
1062
+ * Useful for testing or when you need to force a clean slate.
1063
+ */
1064
+ clear() {
1065
+ this.pool = [];
1066
+ }
1067
+ /**
1068
+ * Get current pool size
1069
+ */
1070
+ get size() {
1071
+ return this.pool.length;
1072
+ }
1073
+ /**
1074
+ * Get maximum pool size
1075
+ */
1076
+ get capacity() {
1077
+ return this.maxSize;
1078
+ }
1079
+ /**
1080
+ * Pre-warm the pool by creating objects in advance
1081
+ *
1082
+ * This can reduce latency for the first N requests.
1083
+ *
1084
+ * @param count - Number of objects to pre-create
1085
+ */
1086
+ prewarm(count) {
1087
+ const targetSize = Math.min(count, this.maxSize);
1088
+ while (this.pool.length < targetSize) {
1089
+ const obj = this.factory();
1090
+ this.reset(obj);
1091
+ this.pool.push(obj);
1092
+ }
1093
+ }
1094
+ };
1095
+
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
+ }
1117
+ var Gravito = class {
1118
+ router = new AOTRouter();
1119
+ contextPool;
1120
+ errorHandler;
1121
+ notFoundHandler;
1122
+ // Direct reference to static routes Map (O(1) access)
1123
+ /** @internal */
1124
+ staticRoutes;
1125
+ // Flag: pure static app (no middleware at all) allows ultra-fast path
1126
+ isPureStaticApp = true;
1127
+ // Cache for precompiled dynamic routes
1128
+ compiledDynamicRoutes = /* @__PURE__ */ new Map();
1129
+ // Version tracking for cache invalidation
1130
+ middlewareVersion = 0;
1131
+ /**
1132
+ * Create a new Gravito instance
1133
+ *
1134
+ * @param options - Engine configuration options
1135
+ */
1136
+ constructor(options = {}) {
1137
+ const poolSize = options.poolSize ?? 256;
1138
+ this.contextPool = new ObjectPool(
1139
+ () => new FastContext(),
1140
+ (ctx) => ctx.reset(),
1141
+ poolSize
1142
+ );
1143
+ this.contextPool.prewarm(Math.min(32, poolSize));
1144
+ if (options.onError) {
1145
+ this.errorHandler = options.onError;
1146
+ }
1147
+ if (options.onNotFound) {
1148
+ this.notFoundHandler = options.onNotFound;
1149
+ }
1150
+ this.compileRoutes();
1151
+ }
1152
+ // ─────────────────────────────────────────────────────────────────────────
1153
+ // HTTP Method Registration
1154
+ // ─────────────────────────────────────────────────────────────────────────
1155
+ /**
1156
+ * Register a GET route
1157
+ *
1158
+ * @param path - Route path (e.g., '/users/:id')
1159
+ * @param handlers - Handler and optional middleware
1160
+ * @returns This instance for chaining
1161
+ */
1162
+ get(path, ...handlers) {
1163
+ return this.addRoute("get", path, handlers);
1164
+ }
1165
+ /**
1166
+ * Register a POST route
1167
+ */
1168
+ post(path, ...handlers) {
1169
+ return this.addRoute("post", path, handlers);
1170
+ }
1171
+ /**
1172
+ * Register a PUT route
1173
+ */
1174
+ put(path, ...handlers) {
1175
+ return this.addRoute("put", path, handlers);
1176
+ }
1177
+ /**
1178
+ * Register a DELETE route
1179
+ */
1180
+ delete(path, ...handlers) {
1181
+ return this.addRoute("delete", path, handlers);
1182
+ }
1183
+ /**
1184
+ * Register a PDF route
1185
+ */
1186
+ patch(path, ...handlers) {
1187
+ return this.addRoute("patch", path, handlers);
1188
+ }
1189
+ /**
1190
+ * Register an OPTIONS route
1191
+ */
1192
+ options(path, ...handlers) {
1193
+ return this.addRoute("options", path, handlers);
1194
+ }
1195
+ /**
1196
+ * Register a HEAD route
1197
+ */
1198
+ head(path, ...handlers) {
1199
+ return this.addRoute("head", path, handlers);
1200
+ }
1201
+ /**
1202
+ * Register a route for all HTTP methods
1203
+ */
1204
+ all(path, ...handlers) {
1205
+ const methods = ["get", "post", "put", "delete", "patch", "options", "head"];
1206
+ for (const method of methods) {
1207
+ this.addRoute(method, path, handlers);
1208
+ }
1209
+ return this;
1210
+ }
1211
+ use(pathOrMiddleware, ...middleware) {
1212
+ this.isPureStaticApp = false;
1213
+ if (typeof pathOrMiddleware === "string") {
1214
+ this.router.usePattern(pathOrMiddleware, ...middleware);
1215
+ } else {
1216
+ this.router.use(pathOrMiddleware, ...middleware);
1217
+ }
1218
+ this.middlewareVersion++;
1219
+ this.compileRoutes();
1220
+ return this;
1221
+ }
1222
+ // ─────────────────────────────────────────────────────────────────────────
1223
+ // Route Grouping
1224
+ // ─────────────────────────────────────────────────────────────────────────
1225
+ /**
1226
+ * Mount a sub-application at a path prefix
1227
+ */
1228
+ route(path, app) {
1229
+ this.router.mount(path, app.router);
1230
+ this.compileRoutes();
1231
+ return this;
1232
+ }
1233
+ // ─────────────────────────────────────────────────────────────────────────
1234
+ // Error Handling
1235
+ // ─────────────────────────────────────────────────────────────────────────
1236
+ /**
1237
+ * Set custom error handler
1238
+ */
1239
+ onError(handler) {
1240
+ this.errorHandler = handler;
1241
+ return this;
1242
+ }
1243
+ /**
1244
+ * Set custom 404 handler
1245
+ */
1246
+ notFound(handler) {
1247
+ this.notFoundHandler = handler;
1248
+ return this;
1249
+ }
1250
+ // ─────────────────────────────────────────────────────────────────────────
1251
+ // Request Handling (Bun.serve integration)
1252
+ // ─────────────────────────────────────────────────────────────────────────
1253
+ /**
1254
+ * Predictive Route Warming (JIT Optimization)
1255
+ *
1256
+ * Simulates requests to specified routes to trigger JIT compilation (FTL)
1257
+ * before real traffic arrives.
1258
+ *
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
1270
+ */
1271
+ fetch = async (request) => {
1272
+ const path = extractPath(request.url);
1273
+ const method = request.method.toLowerCase();
1274
+ const staticKey = `${method}:${path}`;
1275
+ const staticRoute = this.staticRoutes.get(staticKey);
1276
+ if (staticRoute) {
1277
+ if (staticRoute.useMinimal) {
1278
+ const ctx = new MinimalContext(request, {}, path, path);
1279
+ try {
1280
+ const result = staticRoute.handler(ctx);
1281
+ if (result instanceof Response) {
1282
+ return result;
1283
+ }
1284
+ return await result;
1285
+ } catch (error) {
1286
+ return this.handleErrorSync(error, request, path);
1287
+ }
1288
+ }
1289
+ return await this.handleWithMiddleware(request, path, staticRoute);
1290
+ }
1291
+ return await this.handleDynamicRoute(request, method, path);
1292
+ };
1293
+ /**
1294
+ * Handle routes with middleware (async path)
1295
+ */
1296
+ async handleWithMiddleware(request, path, route) {
1297
+ const ctx = this.contextPool.acquire();
1298
+ try {
1299
+ ctx.init(request, {}, path, path);
1300
+ if (route.compiled) {
1301
+ return await route.compiled(ctx);
1302
+ }
1303
+ const middleware = this.collectMiddlewareForPath(path, route.middleware);
1304
+ if (middleware.length === 0) {
1305
+ return await route.handler(ctx);
1306
+ }
1307
+ return await this.executeMiddleware(ctx, middleware, route.handler);
1308
+ } catch (error) {
1309
+ return await this.handleError(error, ctx);
1310
+ } finally {
1311
+ this.contextPool.release(ctx);
1312
+ }
1313
+ }
1314
+ /**
1315
+ * Handle dynamic routes (Radix Tree lookup)
1316
+ */
1317
+ handleDynamicRoute(request, method, path) {
1318
+ const match = this.router.match(method.toUpperCase(), path);
1319
+ if (!match.handler) {
1320
+ return this.handleNotFoundSync(request, path);
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
+ }
1332
+ const ctx = this.contextPool.acquire();
1333
+ const execute = async () => {
1334
+ try {
1335
+ ctx.init(request, match.params, path, match.routePattern);
1336
+ return await entry?.compiled(ctx);
1337
+ } catch (error) {
1338
+ return await this.handleError(error, ctx);
1339
+ } finally {
1340
+ this.contextPool.release(ctx);
1341
+ }
1342
+ };
1343
+ return execute();
1344
+ }
1345
+ /**
1346
+ * Sync error handler (for ultra-fast path)
1347
+ */
1348
+ handleErrorSync(error, request, path) {
1349
+ if (this.errorHandler) {
1350
+ const ctx = new MinimalContext(request, {}, path);
1351
+ const result = this.errorHandler(error, ctx);
1352
+ if (result instanceof Response) {
1353
+ return result;
1354
+ }
1355
+ return result;
1356
+ }
1357
+ console.error("Unhandled error:", error);
1358
+ return new Response(CACHED_RESPONSES.INTERNAL_ERROR, {
1359
+ status: 500,
1360
+ headers: HEADERS.JSON
1361
+ });
1362
+ }
1363
+ /**
1364
+ * Sync 404 handler (for ultra-fast path)
1365
+ */
1366
+ handleNotFoundSync(request, path) {
1367
+ if (this.notFoundHandler) {
1368
+ const ctx = new MinimalContext(request, {}, path);
1369
+ const result = this.notFoundHandler(ctx);
1370
+ if (result instanceof Response) {
1371
+ return result;
1372
+ }
1373
+ return result;
1374
+ }
1375
+ return new Response(CACHED_RESPONSES.NOT_FOUND, {
1376
+ status: 404,
1377
+ headers: HEADERS.JSON
1378
+ });
1379
+ }
1380
+ /**
1381
+ * Collect middleware for a specific path
1382
+ */
1383
+ collectMiddlewareForPath(path, routeMiddleware) {
1384
+ if (this.router.globalMiddleware.length === 0 && this.router.pathMiddleware.size === 0) {
1385
+ return routeMiddleware;
1386
+ }
1387
+ return this.router.collectMiddlewarePublic(path, routeMiddleware);
1388
+ }
1389
+ /**
1390
+ * Compile routes for optimization
1391
+ */
1392
+ compileRoutes() {
1393
+ this.staticRoutes = this.router.staticRoutes;
1394
+ const hasGlobalMiddleware = this.router.globalMiddleware.length > 0;
1395
+ const hasPathMiddleware = this.router.pathMiddleware.size > 0;
1396
+ this.isPureStaticApp = !hasGlobalMiddleware && !hasPathMiddleware;
1397
+ for (const [key, route] of this.staticRoutes) {
1398
+ if (route.compiledVersion === this.middlewareVersion) {
1399
+ continue;
1400
+ }
1401
+ const analysis = analyzeHandler(route.handler);
1402
+ const optimalType = getOptimalContextType(analysis);
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;
1409
+ }
1410
+ }
1411
+ /**
1412
+ * Add a route to the router
1413
+ */
1414
+ addRoute(method, path, handlers) {
1415
+ if (handlers.length === 0) {
1416
+ throw new Error(`No handler provided for ${method.toUpperCase()} ${path}`);
1417
+ }
1418
+ const handler = handlers[handlers.length - 1];
1419
+ const middleware = handlers.slice(0, -1);
1420
+ this.router.add(method, path, handler, middleware);
1421
+ this.compileRoutes();
1422
+ return this;
1423
+ }
1424
+ /**
1425
+ * Execute middleware chain followed by handler
1426
+ */
1427
+ async executeMiddleware(ctx, middleware, handler) {
1428
+ let index = 0;
1429
+ const next = async () => {
1430
+ if (index < middleware.length) {
1431
+ const mw = middleware[index++];
1432
+ return await mw(ctx, next);
1433
+ }
1434
+ return void 0;
1435
+ };
1436
+ const result = await next();
1437
+ if (result instanceof Response) {
1438
+ return result;
1439
+ }
1440
+ return await handler(ctx);
1441
+ }
1442
+ /**
1443
+ * Handle errors (Async version for dynamic/middleware paths)
1444
+ */
1445
+ async handleError(error, ctx) {
1446
+ if (this.errorHandler) {
1447
+ return await this.errorHandler(error, ctx);
1448
+ }
1449
+ console.error("Unhandled error:", error);
1450
+ return ctx.json(
1451
+ {
1452
+ error: "Internal Server Error",
1453
+ message: error.message
1454
+ },
1455
+ 500
1456
+ );
1457
+ }
1458
+ };
1459
+ // Annotate the CommonJS export names for ESM import in node:
1460
+ 0 && (module.exports = {
1461
+ AOTRouter,
1462
+ FastContextImpl,
1463
+ Gravito,
1464
+ MinimalContext,
1465
+ ObjectPool,
1466
+ extractPath
1467
+ });