@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.
@@ -39,11 +39,26 @@ interface FastContext$1 {
39
39
  html(html: string, status?: number): Response;
40
40
  redirect(url: string, status?: 301 | 302 | 303 | 307 | 308): Response;
41
41
  body(data: BodyInit | null, status?: number): Response;
42
+ stream(stream: ReadableStream, status?: number): Response;
43
+ notFound(message?: string): Response;
44
+ forbidden(message?: string): Response;
45
+ unauthorized(message?: string): Response;
46
+ badRequest(message?: string): Response;
47
+ forward(target: string, options?: any): Promise<Response>;
42
48
  /** Header management */
49
+ header(name: string): string | undefined;
43
50
  header(name: string, value: string): void;
44
51
  status(code: number): void;
45
- /** Internal reset for pooling */
46
- reset(request: Request, params?: Record<string, string>): this;
52
+ /** Context Variables */
53
+ get<T>(key: string): T;
54
+ set(key: string, value: any): void;
55
+ /** Lifecycle helpers */
56
+ route: (name: string, params?: any, query?: any) => string;
57
+ readonly native: any;
58
+ /** Internal initialization for pooling */
59
+ init(request: Request, params?: Record<string, string>, path?: string, routePattern?: string): this;
60
+ /** Internal cleanup for pooling */
61
+ reset(): void;
47
62
  }
48
63
  /**
49
64
  * FastRequest - Minimal request interface
@@ -55,6 +70,11 @@ interface FastRequest {
55
70
  readonly method: string;
56
71
  /** Path without query */
57
72
  readonly path: string;
73
+ /**
74
+ * Route pattern (e.g., /users/:id) for metrics labeling
75
+ * Prevents high cardinality issues in monitoring systems
76
+ */
77
+ readonly routePattern?: string;
58
78
  /** Get route parameter */
59
79
  param(name: string): string | undefined;
60
80
  /** Get all route parameters */
@@ -92,6 +112,20 @@ type ErrorHandler = (error: Error, ctx: FastContext$1) => Response | Promise<Res
92
112
  * Not found handler function
93
113
  */
94
114
  type NotFoundHandler = (ctx: FastContext$1) => Response | Promise<Response>;
115
+ /**
116
+ * Route metadata for middleware management
117
+ */
118
+ interface RouteMetadata {
119
+ handler: Handler;
120
+ middleware: Middleware[];
121
+ compiled?: CompiledHandler;
122
+ useMinimal?: boolean;
123
+ compiledVersion?: number;
124
+ }
125
+ /**
126
+ * Compiled handler function
127
+ */
128
+ type CompiledHandler = (ctx: FastContext$1) => Promise<Response>;
95
129
  /**
96
130
  * Route match result from router
97
131
  */
@@ -102,6 +136,8 @@ interface RouteMatch {
102
136
  params: Record<string, string>;
103
137
  /** Middleware to execute */
104
138
  middleware: Middleware[];
139
+ /** Optional stable route pattern for caching */
140
+ routePattern?: string;
105
141
  }
106
142
  /**
107
143
  * Engine configuration options
@@ -134,29 +170,17 @@ interface EngineOptions {
134
170
 
135
171
  /**
136
172
  * Gravito - The High-Performance Web Engine
137
- *
138
- * @example
139
- * ```typescript
140
- * import { Gravito } from '@gravito/core/engine'
141
- *
142
- * const app = new Gravito()
143
- *
144
- * app.get('/', (c) => c.json({ message: 'Hello, World!' }))
145
- * app.get('/users/:id', (c) => {
146
- * const id = c.req.param('id')
147
- * return c.json({ id })
148
- * })
149
- *
150
- * export default app
151
- * ```
152
173
  */
153
174
  declare class Gravito {
154
175
  private router;
155
176
  private contextPool;
156
177
  private errorHandler?;
157
178
  private notFoundHandler?;
158
- private staticRoutes;
179
+ /** @internal */
180
+ staticRoutes: Map<string, RouteMetadata>;
159
181
  private isPureStaticApp;
182
+ private compiledDynamicRoutes;
183
+ private middlewareVersion;
160
184
  /**
161
185
  * Create a new Gravito instance
162
186
  *
@@ -184,7 +208,7 @@ declare class Gravito {
184
208
  */
185
209
  delete(path: string, ...handlers: Handler[]): this;
186
210
  /**
187
- * Register a PATCH route
211
+ * Register a PDF route
188
212
  */
189
213
  patch(path: string, ...handlers: Handler[]): this;
190
214
  /**
@@ -201,71 +225,34 @@ declare class Gravito {
201
225
  all(path: string, ...handlers: Handler[]): this;
202
226
  /**
203
227
  * Register global or path-based middleware
204
- *
205
- * @example
206
- * ```typescript
207
- * // Global middleware
208
- * app.use(async (c, next) => {
209
- * console.log(`${c.req.method} ${c.req.path}`)
210
- * await next()
211
- * })
212
- *
213
- * // Path-based middleware
214
- * app.use('/api/*', async (c, next) => {
215
- * c.header('X-API-Version', '1.0')
216
- * await next()
217
- * })
218
- * ```
219
228
  */
220
229
  use(path: string, ...middleware: Middleware[]): this;
221
230
  use(...middleware: Middleware[]): this;
222
231
  /**
223
232
  * Mount a sub-application at a path prefix
224
- *
225
- * @example
226
- * ```typescript
227
- * const api = new Gravito()
228
- * api.get('/users', (c) => c.json({ users: [] }))
229
- *
230
- * const app = new Gravito()
231
- * app.route('/api', api)
232
- * // Now accessible at /api/users
233
- * ```
234
233
  */
235
- route(_path: string, _app: Gravito): this;
234
+ route(path: string, app: Gravito): this;
236
235
  /**
237
236
  * Set custom error handler
238
- *
239
- * @example
240
- * ```typescript
241
- * app.onError((err, c) => {
242
- * console.error(err)
243
- * return c.json({ error: err.message }, 500)
244
- * })
245
- * ```
246
237
  */
247
238
  onError(handler: ErrorHandler): this;
248
239
  /**
249
240
  * Set custom 404 handler
250
- *
251
- * @example
252
- * ```typescript
253
- * app.notFound((c) => {
254
- * return c.json({ error: 'Not Found' }, 404)
255
- * })
256
- * ```
257
241
  */
258
242
  notFound(handler: NotFoundHandler): this;
259
243
  /**
260
- * Handle an incoming request
244
+ * Predictive Route Warming (JIT Optimization)
261
245
  *
262
- * Optimized for minimal allocations and maximum throughput.
263
- * Uses sync/async dual-path strategy inspired by Hono.
246
+ * Simulates requests to specified routes to trigger JIT compilation (FTL)
247
+ * before real traffic arrives.
264
248
  *
265
- * @param request - Incoming Request object
266
- * @returns Response object (sync or async)
249
+ * @param paths List of paths to warm up (e.g. ['/api/users', '/health'])
250
+ */
251
+ warmup(paths: string[]): Promise<void>;
252
+ /**
253
+ * Handle an incoming request
267
254
  */
268
- fetch: (request: Request) => Response | Promise<Response>;
255
+ fetch: (request: Request) => Promise<Response>;
269
256
  /**
270
257
  * Handle routes with middleware (async path)
271
258
  */
@@ -284,12 +271,10 @@ declare class Gravito {
284
271
  private handleNotFoundSync;
285
272
  /**
286
273
  * Collect middleware for a specific path
287
- * (Simplified version - assumes we've already checked for pure static)
288
274
  */
289
275
  private collectMiddlewareForPath;
290
276
  /**
291
277
  * Compile routes for optimization
292
- * Called once during initialization and when routes change
293
278
  */
294
279
  private compileRoutes;
295
280
  /**
@@ -298,9 +283,6 @@ declare class Gravito {
298
283
  private addRoute;
299
284
  /**
300
285
  * Execute middleware chain followed by handler
301
- *
302
- * Implements the standard middleware pattern:
303
- * Each middleware can call next() to continue the chain.
304
286
  */
305
287
  private executeMiddleware;
306
288
  /**
@@ -322,19 +304,32 @@ declare class Gravito {
322
304
  * @module @gravito/core/engine
323
305
  */
324
306
 
307
+ /**
308
+ * Route definition for re-playing routes (mounting)
309
+ */
310
+ interface RouteDefinition {
311
+ method: HttpMethod;
312
+ path: string;
313
+ handler: Handler;
314
+ middleware: Middleware[];
315
+ }
325
316
  /**
326
317
  * AOT Router - Optimized for Bun
327
- *
328
- * Performance characteristics:
329
- * - Static routes: O(1) lookup via Map
330
- * - Dynamic routes: O(log n) via Radix Tree
331
- * - Middleware: O(m) where m = number of matching middleware
332
318
  */
333
319
  declare class AOTRouter {
334
- private staticRoutes;
320
+ /** @internal */
321
+ readonly staticRoutes: Map<string, RouteMetadata>;
335
322
  private dynamicRouter;
336
- private globalMiddleware;
337
- private pathMiddleware;
323
+ /** @internal */
324
+ readonly routeDefinitions: RouteDefinition[];
325
+ /** @internal */
326
+ readonly globalMiddleware: Middleware[];
327
+ /** @internal */
328
+ readonly pathMiddleware: Map<string, Middleware[]>;
329
+ private dynamicRoutePatterns;
330
+ private middlewareCache;
331
+ private cacheMaxSize;
332
+ private version;
338
333
  /**
339
334
  * Register a route
340
335
  *
@@ -348,6 +343,10 @@ declare class AOTRouter {
348
343
  * @param middleware - Route-specific middleware
349
344
  */
350
345
  add(method: HttpMethod, path: string, handler: Handler, middleware?: Middleware[]): void;
346
+ /**
347
+ * Mount another router at a prefix
348
+ */
349
+ mount(prefix: string, other: AOTRouter): void;
351
350
  /**
352
351
  * Add global middleware
353
352
  *
@@ -405,17 +404,6 @@ declare class AOTRouter {
405
404
  * @returns True if pattern matches
406
405
  */
407
406
  private matchPattern;
408
- /**
409
- * Find the original route key for a matched dynamic route
410
- *
411
- * This is needed to look up route-specific middleware.
412
- * It's a bit of a hack, but avoids storing duplicate data.
413
- *
414
- * @param method - HTTP method
415
- * @param path - Matched path
416
- * @returns Route key or null
417
- */
418
- private findDynamicRouteKey;
419
407
  /**
420
408
  * Get all registered routes (for debugging)
421
409
  */
@@ -435,6 +423,49 @@ declare class AOTRouter {
435
423
  * @module @gravito/core/engine
436
424
  */
437
425
 
426
+ /**
427
+ * Lazy-parsed request wrapper
428
+ *
429
+ * Delays parsing of query params, headers, and body until accessed.
430
+ * This is a key optimization for requests that don't need all data.
431
+ */
432
+ declare class FastRequestImpl implements FastRequest {
433
+ private _request;
434
+ private _params;
435
+ private _path;
436
+ private _routePattern?;
437
+ private _url;
438
+ private _query;
439
+ private _headers;
440
+ private _cachedJson;
441
+ private _jsonParsed;
442
+ private _ctx;
443
+ constructor(ctx: FastContext);
444
+ /**
445
+ * Initialize for new request
446
+ */
447
+ init(request: Request, params?: Record<string, string>, path?: string, routePattern?: string): this;
448
+ /**
449
+ * Reset for pooling
450
+ */
451
+ reset(): void;
452
+ private checkReleased;
453
+ get url(): string;
454
+ get method(): string;
455
+ get path(): string;
456
+ get routePattern(): string | undefined;
457
+ param(name: string): string | undefined;
458
+ params(): Record<string, string>;
459
+ private getUrl;
460
+ query(name: string): string | undefined;
461
+ queries(): Record<string, string | string[]>;
462
+ header(name: string): string | undefined;
463
+ headers(): Record<string, string>;
464
+ json<T = unknown>(): Promise<T>;
465
+ text(): Promise<string>;
466
+ formData(): Promise<FormData>;
467
+ get raw(): Request;
468
+ }
438
469
  /**
439
470
  * FastContext - Pooled request context
440
471
  *
@@ -442,23 +473,45 @@ declare class AOTRouter {
442
473
  * All response helpers create Response objects directly without intermediate wrappers.
443
474
  */
444
475
  declare class FastContext implements FastContext$1 {
445
- private _req;
476
+ readonly req: FastRequestImpl;
446
477
  private _headers;
478
+ _isReleased: boolean;
447
479
  /**
448
- * Reset context for pooling
480
+ * Initialize context for a new request
449
481
  *
450
482
  * This is called when acquiring from the pool.
451
- * Must clear all state from previous request.
452
483
  */
453
- reset(request: Request, params?: Record<string, string>): this;
454
- get req(): FastRequest;
484
+ init(request: Request, params?: Record<string, string>, path?: string, routePattern?: string): this;
485
+ /**
486
+ * Reset context for pooling (Cleanup)
487
+ *
488
+ * This is called when releasing back to the pool.
489
+ * Implements "Deep-Reset Protocol" and "Release Guard".
490
+ */
491
+ reset(): void;
492
+ /**
493
+ * Check if context is released
494
+ */
495
+ private checkReleased;
455
496
  json<T>(data: T, status?: number): Response;
456
497
  text(text: string, status?: number): Response;
457
498
  html(html: string, status?: number): Response;
458
499
  redirect(url: string, status?: 301 | 302 | 303 | 307 | 308): Response;
459
500
  body(data: BodyInit | null, status?: number): Response;
501
+ stream(stream: ReadableStream, status?: number): Response;
502
+ notFound(message?: string): Response;
503
+ forbidden(message?: string): Response;
504
+ unauthorized(message?: string): Response;
505
+ badRequest(message?: string): Response;
506
+ forward(target: string, _options?: any): Promise<Response>;
507
+ header(name: string): string | undefined;
460
508
  header(name: string, value: string): void;
461
509
  status(_code: number): void;
510
+ private _store;
511
+ get<T>(key: string): T;
512
+ set(key: string, value: any): void;
513
+ route: (name: string, params?: any, query?: any) => string;
514
+ get native(): this;
462
515
  }
463
516
 
464
517
  /**
@@ -475,6 +528,35 @@ declare class FastContext implements FastContext$1 {
475
528
  * @module @gravito/core/engine
476
529
  */
477
530
 
531
+ /**
532
+ * Minimal request wrapper
533
+ */
534
+ declare class MinimalRequest implements FastRequest {
535
+ private readonly _request;
536
+ private readonly _params;
537
+ private readonly _path;
538
+ private readonly _routePattern?;
539
+ private _searchParams;
540
+ constructor(_request: Request, _params: Record<string, string>, _path: string, _routePattern?: string | undefined);
541
+ get url(): string;
542
+ get method(): string;
543
+ get path(): string;
544
+ get routePattern(): string | undefined;
545
+ param(name: string): string | undefined;
546
+ params(): Record<string, string>;
547
+ /**
548
+ * Lazy-initialize searchParams, only parse once
549
+ */
550
+ private getSearchParams;
551
+ query(name: string): string | undefined;
552
+ queries(): Record<string, string | string[]>;
553
+ header(name: string): string | undefined;
554
+ headers(): Record<string, string>;
555
+ json<T = unknown>(): Promise<T>;
556
+ text(): Promise<string>;
557
+ formData(): Promise<FormData>;
558
+ get raw(): Request;
559
+ }
478
560
  /**
479
561
  * MinimalContext - Optimized for simple, fast responses
480
562
  *
@@ -485,17 +567,30 @@ declare class FastContext implements FastContext$1 {
485
567
  * - No custom headers needed
486
568
  */
487
569
  declare class MinimalContext implements FastContext$1 {
488
- private readonly _req;
489
- constructor(request: Request, params: Record<string, string>, path: string);
490
- get req(): FastRequest;
570
+ readonly req: MinimalRequest;
571
+ private _resHeaders;
572
+ constructor(request: Request, params: Record<string, string>, path: string, routePattern?: string);
573
+ private getHeaders;
491
574
  json<T>(data: T, status?: number): Response;
492
575
  text(text: string, status?: number): Response;
493
576
  html(html: string, status?: number): Response;
494
577
  redirect(url: string, status?: 301 | 302 | 303 | 307 | 308): Response;
495
578
  body(data: BodyInit | null, status?: number): Response;
496
- header(_name: string, _value: string): void;
579
+ header(name: string): string | undefined;
580
+ header(name: string, value: string): void;
497
581
  status(_code: number): void;
498
- reset(_request: Request, _params?: Record<string, string>): this;
582
+ stream(stream: ReadableStream, status?: number): Response;
583
+ notFound(message?: string): Response;
584
+ forbidden(message?: string): Response;
585
+ unauthorized(message?: string): Response;
586
+ badRequest(message?: string): Response;
587
+ forward(target: string, _options?: any): Promise<Response>;
588
+ get<T>(_key: string): T;
589
+ set(_key: string, _value: any): void;
590
+ route: (name: string, params?: any, query?: any) => string;
591
+ get native(): this;
592
+ init(_request: Request, _params?: Record<string, string>, _path?: string): this;
593
+ reset(): void;
499
594
  }
500
595
 
501
596
  /**