@flight-framework/core 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -170,20 +170,153 @@ hydrateIslands({
170
170
 
171
171
  ### Middleware
172
172
 
173
- ```typescript
174
- import { createMiddlewareChain } from '@flight-framework/core/middleware';
173
+ Flight provides a composable middleware system for request/response handling.
174
+
175
+ #### Middleware Chain
175
176
 
176
- const middleware = createMiddlewareChain([
177
- async (ctx, next) => {
177
+ ```typescript
178
+ import {
179
+ createMiddlewareChain,
180
+ cors,
181
+ logger,
182
+ securityHeaders
183
+ } from '@flight-framework/core/middleware';
184
+
185
+ const chain = createMiddlewareChain();
186
+
187
+ chain
188
+ .use(logger())
189
+ .use(cors({ origin: ['https://app.example.com'] }))
190
+ .use(securityHeaders())
191
+ .use(async (ctx, next) => {
178
192
  const start = Date.now();
179
193
  await next();
180
194
  console.log(`Request took ${Date.now() - start}ms`);
195
+ });
196
+ ```
197
+
198
+ #### Error Handling
199
+
200
+ Centralized error handling with the `errorHandler` factory:
201
+
202
+ ```typescript
203
+ import { createMiddlewareChain, errorHandler } from '@flight-framework/core/middleware';
204
+
205
+ const chain = createMiddlewareChain();
206
+
207
+ // Place error handler first in the chain
208
+ chain.use(errorHandler({
209
+ expose: process.env.NODE_ENV === 'development',
210
+ emit: (error, ctx) => {
211
+ logger.error(`[${ctx.method}] ${ctx.url.pathname}:`, error);
212
+ errorTracker.capture(error);
213
+ },
214
+ }));
215
+
216
+ chain.use(authMiddleware);
217
+ chain.use(routeHandler);
218
+ ```
219
+
220
+ The error handler catches all downstream errors, sets appropriate status codes, and supports custom error handlers:
221
+
222
+ ```typescript
223
+ chain.use(errorHandler({
224
+ onError: async ({ error, status, ctx, timestamp }) => {
225
+ ctx.status = status;
226
+ ctx.responseBody = JSON.stringify({
227
+ error: error.message,
228
+ timestamp,
229
+ requestId: ctx.locals.requestId,
230
+ });
181
231
  },
182
- authMiddleware,
183
- loggingMiddleware,
184
- ]);
232
+ }));
185
233
  ```
186
234
 
235
+ #### Typed Context
236
+
237
+ Middleware context supports generics for type-safe data sharing:
238
+
239
+ ```typescript
240
+ import type { Middleware, MiddlewareContext } from '@flight-framework/core/middleware';
241
+
242
+ interface AppLocals {
243
+ user: { id: string; role: string };
244
+ requestId: string;
245
+ db: DatabaseClient;
246
+ }
247
+
248
+ const authMiddleware: Middleware<AppLocals> = async (ctx, next) => {
249
+ const token = ctx.headers.get('Authorization');
250
+ const user = await verifyToken(token);
251
+
252
+ ctx.locals.user = user;
253
+ ctx.locals.requestId = crypto.randomUUID();
254
+
255
+ await next();
256
+ };
257
+
258
+ // Type-safe access in subsequent middleware
259
+ const roleGuard: Middleware<AppLocals> = async (ctx, next) => {
260
+ if (ctx.locals.user.role !== 'admin') {
261
+ ctx.status = 403;
262
+ ctx.responseBody = 'Forbidden';
263
+ return;
264
+ }
265
+ await next();
266
+ };
267
+ ```
268
+
269
+ #### CORS
270
+
271
+ CORS middleware with dynamic origin validation and CDN compatibility:
272
+
273
+ ```typescript
274
+ import { cors } from '@flight-framework/core/middleware';
275
+
276
+ // Static origins
277
+ chain.use(cors({
278
+ origin: ['https://app.example.com', 'https://admin.example.com'],
279
+ methods: ['GET', 'POST', 'PUT', 'DELETE'],
280
+ credentials: true,
281
+ }));
282
+
283
+ // Async validation (database lookup)
284
+ chain.use(cors({
285
+ origin: async (requestOrigin) => {
286
+ return await db.allowedOrigins.exists(requestOrigin);
287
+ },
288
+ exposeHeaders: ['X-Request-Id', 'X-RateLimit-Remaining'],
289
+ }));
290
+ ```
291
+
292
+ Dynamic origins automatically set `Vary: Origin` for CDN/cache compatibility.
293
+
294
+ #### Built-in Middleware
295
+
296
+ | Middleware | Purpose |
297
+ |------------|---------|
298
+ | `cors(options?)` | Cross-origin resource sharing |
299
+ | `logger(options?)` | Request logging with configurable levels |
300
+ | `securityHeaders(options?)` | Security headers (CSP, X-Frame-Options, etc.) |
301
+ | `errorHandler(options?)` | Centralized error handling |
302
+ | `compress()` | Mark responses for compression |
303
+
304
+ #### Logger Configuration
305
+
306
+ ```typescript
307
+ import { logger } from '@flight-framework/core/middleware';
308
+
309
+ chain.use(logger({
310
+ level: 'info', // 'debug' | 'info' | 'warn' | 'error' | 'silent'
311
+ format: 'json', // 'pretty' | 'json' | 'combined' | 'common' | 'short'
312
+ skip: (ctx) => ctx.url.pathname === '/health',
313
+ writer: (entry, formatted) => externalLogger.log(entry),
314
+ }));
315
+ ```
316
+
317
+ The logger captures errors from downstream middleware before re-throwing, ensuring all requests are logged including failures.
318
+
319
+
187
320
  ### Error Handling
188
321
 
189
322
  ```typescript
@@ -341,6 +474,68 @@ import type {
341
474
  } from '@flight-framework/core';
342
475
  ```
343
476
 
477
+ ## Build Plugins
478
+
479
+ ### Critical CSS Extraction
480
+
481
+ Extract and inline critical CSS for improved LCP:
482
+
483
+ ```bash
484
+ npm install critters
485
+ ```
486
+
487
+ ```typescript
488
+ // vite.config.ts
489
+ import { criticalCSS } from '@flight-framework/core/plugins';
490
+
491
+ export default defineConfig({
492
+ plugins: [
493
+ criticalCSS({
494
+ // Strategy for loading non-critical CSS
495
+ preload: 'swap', // 'body' | 'media' | 'swap' | 'swap-high' | 'js' | 'js-lazy'
496
+
497
+ // Remove inlined CSS from source
498
+ pruneSource: false,
499
+
500
+ // Routes to process
501
+ include: ['**/*.html'],
502
+ exclude: ['/api/**'],
503
+ }),
504
+ ],
505
+ });
506
+ ```
507
+
508
+ #### Preload Strategies
509
+
510
+ | Strategy | Description |
511
+ |----------|-------------|
512
+ | `swap` | Use rel="preload" and swap on load |
513
+ | `swap-high` | Like swap with fetchpriority="high" |
514
+ | `media` | Use media="print" and swap |
515
+ | `js` | Load via JavaScript |
516
+ | `js-lazy` | Load via JavaScript when idle |
517
+ | `body` | Move stylesheets to end of body |
518
+
519
+ #### CSS Utilities
520
+
521
+ ```typescript
522
+ import {
523
+ extractInlineStyles,
524
+ mergeCSS,
525
+ generatePreloadLink,
526
+ generateNoscriptFallback,
527
+ } from '@flight-framework/core/plugins/critical-css';
528
+
529
+ // Extract styles from HTML
530
+ const { html, styles } = extractInlineStyles(htmlString);
531
+
532
+ // Generate preload link
533
+ const preload = generatePreloadLink('/styles.css', 'swap');
534
+
535
+ // Generate noscript fallback
536
+ const fallback = generateNoscriptFallback('/styles.css');
537
+ ```
538
+
344
539
  ## License
345
540
 
346
541
  MIT
@@ -39,6 +39,11 @@ async function scanRoutes(options) {
39
39
  routes.push(...interceptRoutes);
40
40
  continue;
41
41
  }
42
+ const groupMatch = entry.name.match(/^\(([^.]+)\)$/);
43
+ if (groupMatch) {
44
+ await scanDir(fullPath, basePath);
45
+ continue;
46
+ }
42
47
  await scanDir(fullPath, `${basePath}/${entry.name}`);
43
48
  } else if (entry.isFile()) {
44
49
  const ext = extname(entry.name);
@@ -268,7 +273,156 @@ async function createFileRouter(options) {
268
273
  refresh
269
274
  };
270
275
  }
276
+ function resolveParallelSlots(routes, currentPath) {
277
+ const slots = {};
278
+ const normalizedPath = normalizePath(currentPath);
279
+ const slotNames = /* @__PURE__ */ new Set();
280
+ for (const route of routes) {
281
+ if (route.slot) {
282
+ slotNames.add(route.slot);
283
+ }
284
+ }
285
+ for (const slotName of slotNames) {
286
+ const slotRoutes = routes.filter((r) => r.slot === slotName);
287
+ let matchingRoute = slotRoutes.find((r) => {
288
+ const routePath = normalizePath(r.path);
289
+ return pathMatches(routePath, normalizedPath);
290
+ });
291
+ if (!matchingRoute) {
292
+ matchingRoute = slotRoutes.find(
293
+ (r) => r.path.endsWith("/default") || r.filePath.includes("default.page")
294
+ );
295
+ }
296
+ if (matchingRoute && matchingRoute.component) {
297
+ slots[slotName] = {
298
+ component: matchingRoute.component,
299
+ route: matchingRoute
300
+ };
301
+ } else {
302
+ slots[slotName] = null;
303
+ }
304
+ }
305
+ return slots;
306
+ }
307
+ function getSlotDefault(routes, slotName, basePath = "") {
308
+ const normalizedBase = normalizePath(basePath);
309
+ return routes.find(
310
+ (r) => r.slot === slotName && (r.filePath.includes("default.page") || r.path === `${normalizedBase}/default`)
311
+ ) || null;
312
+ }
313
+ function getSlotNames(routes) {
314
+ const names = /* @__PURE__ */ new Set();
315
+ for (const route of routes) {
316
+ if (route.slot) {
317
+ names.add(route.slot);
318
+ }
319
+ }
320
+ return Array.from(names);
321
+ }
322
+ function findInterceptingRoute(routes, fromPath, toPath) {
323
+ const normalizedFrom = normalizePath(fromPath);
324
+ const normalizedTo = normalizePath(toPath);
325
+ const interceptingRoutes = routes.filter((r) => r.interceptInfo);
326
+ for (const route of interceptingRoutes) {
327
+ const { interceptInfo } = route;
328
+ if (!interceptInfo) continue;
329
+ const interceptPath = normalizePath(interceptInfo.interceptPath);
330
+ if (!pathMatches(interceptPath, normalizedTo)) {
331
+ continue;
332
+ }
333
+ let validOrigin = false;
334
+ const routePathStr = normalizePath(route.path);
335
+ const interceptMarkerMatch = routePathStr.match(/\/\(\.+\)/);
336
+ if (interceptInfo.level >= 3) {
337
+ validOrigin = true;
338
+ } else if (interceptMarkerMatch && interceptMarkerMatch.index !== void 0) {
339
+ const routeBase = routePathStr.substring(0, interceptMarkerMatch.index);
340
+ if (interceptInfo.level === 1) {
341
+ validOrigin = normalizedFrom === routeBase || normalizedFrom.startsWith(routeBase + "/") || routeBase === "" && normalizedFrom.startsWith("/");
342
+ } else if (interceptInfo.level === 2) {
343
+ validOrigin = normalizedFrom === routeBase || normalizedFrom.startsWith(routeBase + "/");
344
+ }
345
+ }
346
+ if (validOrigin) {
347
+ return route;
348
+ }
349
+ }
350
+ return null;
351
+ }
352
+ function shouldDismissIntercept(currentRoute, toPath) {
353
+ if (!currentRoute || !currentRoute.interceptInfo) {
354
+ return false;
355
+ }
356
+ const normalizedTo = normalizePath(toPath);
357
+ const interceptPath = normalizePath(currentRoute.interceptInfo.interceptPath);
358
+ return !pathMatches(interceptPath, normalizedTo);
359
+ }
360
+ function extractRouteParams(routePath, actualPath) {
361
+ const routeParts = routePath.split("/").filter(Boolean);
362
+ const actualParts = actualPath.split("/").filter(Boolean);
363
+ if (routeParts.length !== actualParts.length) {
364
+ const hasCatchAll = routeParts.some((p) => p.endsWith("+") || p.endsWith("*"));
365
+ if (!hasCatchAll) {
366
+ return null;
367
+ }
368
+ }
369
+ const params = {};
370
+ for (let i = 0; i < routeParts.length; i++) {
371
+ const routePart = routeParts[i];
372
+ const actualPart = actualParts[i];
373
+ if (!routePart) continue;
374
+ if (routePart.startsWith(":")) {
375
+ const paramName = routePart.slice(1).replace(/[+*]$/, "");
376
+ if (routePart.endsWith("+") || routePart.endsWith("*")) {
377
+ params[paramName] = actualParts.slice(i).join("/");
378
+ break;
379
+ }
380
+ if (!actualPart) {
381
+ if (routePart.endsWith("*")) {
382
+ params[paramName] = "";
383
+ break;
384
+ }
385
+ return null;
386
+ }
387
+ params[paramName] = actualPart;
388
+ } else if (routePart !== actualPart) {
389
+ return null;
390
+ }
391
+ }
392
+ return params;
393
+ }
394
+ function normalizePath(path) {
395
+ let normalized = path.trim();
396
+ if (!normalized.startsWith("/")) {
397
+ normalized = "/" + normalized;
398
+ }
399
+ if (normalized !== "/" && normalized.endsWith("/")) {
400
+ normalized = normalized.slice(0, -1);
401
+ }
402
+ normalized = normalized.replace(/\/+/g, "/");
403
+ return normalized;
404
+ }
405
+ function pathMatches(routePath, actualPath) {
406
+ const routeParts = routePath.split("/").filter(Boolean);
407
+ const actualParts = actualPath.split("/").filter(Boolean);
408
+ for (let i = 0; i < routeParts.length; i++) {
409
+ const routePart = routeParts[i];
410
+ const actualPart = actualParts[i];
411
+ if (!routePart) continue;
412
+ if (routePart.startsWith(":") && (routePart.endsWith("+") || routePart.endsWith("*"))) {
413
+ return true;
414
+ }
415
+ if (routePart.startsWith(":")) {
416
+ if (!actualPart) return false;
417
+ continue;
418
+ }
419
+ if (routePart !== actualPart) {
420
+ return false;
421
+ }
422
+ }
423
+ return routeParts.length === actualParts.length;
424
+ }
271
425
 
272
- export { createFileRouter, loadRoutes, scanRoutes };
273
- //# sourceMappingURL=chunk-UGTETAJ2.js.map
274
- //# sourceMappingURL=chunk-UGTETAJ2.js.map
426
+ export { createFileRouter, extractRouteParams, findInterceptingRoute, getSlotDefault, getSlotNames, loadRoutes, resolveParallelSlots, scanRoutes, shouldDismissIntercept };
427
+ //# sourceMappingURL=chunk-4U7CJVNQ.js.map
428
+ //# sourceMappingURL=chunk-4U7CJVNQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/file-router/index.ts"],"names":[],"mappings":"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","file":"chunk-4U7CJVNQ.js","sourcesContent":["/**\n * @flight-framework/core - File Router\n * \n * Auto-discovery of routes from file system.\n * Similar to Next.js App Router and Nuxt server/api patterns.\n */\n\nimport { readdir } from 'node:fs/promises';\nimport { join, basename, extname } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';\n\nexport type Handler = (context: unknown) => Response | Promise<Response>;\nexport type Middleware = (context: unknown, next: () => Promise<Response>) => Response | Promise<Response>;\n\nexport interface FileRoute {\n /** HTTP method (GET, POST, etc) or 'ALL' */\n method: HttpMethod | 'ALL';\n /** Route path with params (e.g., /users/:id) */\n path: string;\n /** Original file path */\n filePath: string;\n /** Handler function (for APIs) */\n handler?: Handler;\n /** Route-specific middleware */\n middleware?: Middleware[];\n /** Route type: 'page' for SSR pages, 'api' for API endpoints */\n type: 'page' | 'api';\n /** Component function (for pages) */\n component?: () => unknown;\n /** Page metadata (title, description, etc) */\n meta?: Record<string, unknown>;\n /** Parallel route slot name (for @folder convention) */\n slot?: string;\n /** Intercepting route info (for (.) (..) (...) convention) */\n interceptInfo?: {\n /** Number of levels to intercept: 1 = same, 2 = parent, 3+ = root */\n level: number;\n /** Target route segment to intercept */\n target: string;\n /** Original path that triggers interception */\n interceptPath: string;\n };\n}\n\nexport interface FileRouterOptions {\n /** Root directory to scan (default: src/routes) */\n directory: string;\n /** File extensions to consider (default: ['.ts', '.js']) */\n extensions?: string[];\n /** Whether to watch for changes (default: false in prod) */\n watch?: boolean;\n /** \n * Custom module loader for development with Vite.\n * Pass vite.ssrLoadModule to load TSX files correctly.\n * Falls back to native import() if not provided.\n */\n moduleLoader?: (filePath: string) => Promise<any>;\n}\n\nexport interface ScanResult {\n routes: FileRoute[];\n errors: string[];\n}\n\n// ============================================================================\n// File Scanner\n// ============================================================================\n\n/**\n * Scan a directory for route files\n */\nexport async function scanRoutes(options: FileRouterOptions): Promise<ScanResult> {\n const {\n directory,\n extensions = ['.ts', '.js'],\n } = options;\n\n const routes: FileRoute[] = [];\n const errors: string[] = [];\n\n async function scanDir(dir: string, basePath: string = ''): Promise<void> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n\n if (entry.isDirectory()) {\n // Skip hidden directories and node_modules\n if (entry.name.startsWith('.') || entry.name === 'node_modules') {\n continue;\n }\n\n // Detect parallel route slots (@folder convention)\n if (entry.name.startsWith('@')) {\n const slotName = entry.name.slice(1);\n // Scan slot directory and mark routes with slot name\n const slotRoutes = await scanSlotDir(fullPath, basePath, slotName, extensions);\n routes.push(...slotRoutes);\n continue;\n }\n\n // Detect intercepting routes ((.) (..) (...) convention)\n const interceptMatch = entry.name.match(/^\\((\\.+)\\)(.+)$/);\n if (interceptMatch) {\n const level = interceptMatch[1].length;\n const targetSegment = interceptMatch[2];\n // Scan intercept directory and mark routes with interceptInfo\n const interceptRoutes = await scanInterceptDir(\n fullPath,\n basePath,\n level,\n targetSegment,\n extensions\n );\n routes.push(...interceptRoutes);\n continue;\n }\n\n // Detect route groups ((groupName) without dots - organizational only)\n // Route groups allow organizing routes without affecting URL structure\n // Example: (marketing)/about.page.tsx -> /about (not /marketing/about)\n const groupMatch = entry.name.match(/^\\(([^.]+)\\)$/);\n if (groupMatch) {\n // This is a route group - scan contents but don't add group to path\n await scanDir(fullPath, basePath);\n continue;\n }\n\n // Recurse into subdirectory\n await scanDir(fullPath, `${basePath}/${entry.name}`);\n } else if (entry.isFile()) {\n const ext = extname(entry.name);\n\n if (!extensions.includes(ext)) {\n continue;\n }\n\n // Parse route from filename\n const routeInfo = parseRouteFile(entry.name, basePath);\n\n if (routeInfo && routeInfo.method && routeInfo.path) {\n routes.push({\n method: routeInfo.method,\n path: routeInfo.path,\n filePath: fullPath,\n type: routeInfo.type,\n });\n }\n }\n }\n } catch (error) {\n errors.push(`Failed to scan ${dir}: ${error}`);\n }\n }\n\n await scanDir(directory);\n\n return { routes, errors };\n}\n\n/**\n * Scan a parallel route slot directory\n */\nasync function scanSlotDir(\n dir: string,\n basePath: string,\n slotName: string,\n extensions: string[] = ['.ts', '.js', '.tsx', '.jsx']\n): Promise<FileRoute[]> {\n const routes: FileRoute[] = [];\n\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n\n if (entry.isFile()) {\n const ext = extname(entry.name);\n\n if (!extensions.includes(ext)) {\n continue;\n }\n\n const routeInfo = parseRouteFile(entry.name, basePath);\n\n if (routeInfo && routeInfo.method && routeInfo.path) {\n routes.push({\n method: routeInfo.method,\n path: routeInfo.path,\n filePath: fullPath,\n type: routeInfo.type,\n slot: slotName,\n });\n }\n }\n }\n } catch (error) {\n console.error(`[Flight] Failed to scan slot @${slotName}:`, error);\n }\n\n return routes;\n}\n\n/**\n * Scan an intercepting route directory\n * Uses (.) (..) (...) convention similar to Next.js\n * - (.)segment - intercepts from same level\n * - (..)segment - intercepts from parent level\n * - (...)segment - intercepts from root\n */\nasync function scanInterceptDir(\n dir: string,\n basePath: string,\n level: number,\n targetSegment: string,\n extensions: string[] = ['.ts', '.js', '.tsx', '.jsx']\n): Promise<FileRoute[]> {\n const routes: FileRoute[] = [];\n\n // Calculate the intercept path based on level\n const pathParts = basePath.split('/').filter(Boolean);\n let interceptPath: string;\n\n if (level === 1) {\n // (.) - Same level, intercepts sibling route\n interceptPath = `${basePath}/${targetSegment}`;\n } else if (level === 2) {\n // (..) - Parent level\n pathParts.pop();\n interceptPath = `/${pathParts.join('/')}/${targetSegment}`;\n } else {\n // (...) - Root level (3 or more dots)\n interceptPath = `/${targetSegment}`;\n }\n\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n\n if (entry.isFile()) {\n const ext = extname(entry.name);\n\n if (!extensions.includes(ext)) {\n continue;\n }\n\n const routeInfo = parseRouteFile(entry.name, basePath);\n\n if (routeInfo && routeInfo.method && routeInfo.path) {\n routes.push({\n method: routeInfo.method,\n path: routeInfo.path,\n filePath: fullPath,\n type: routeInfo.type,\n interceptInfo: {\n level,\n target: targetSegment,\n interceptPath: interceptPath.replace(/\\/+/g, '/') || '/',\n },\n });\n }\n } else if (entry.isDirectory()) {\n // Recurse into subdirectories within intercept folder\n const subRoutes = await scanInterceptDir(\n fullPath,\n `${basePath}/${entry.name}`,\n level,\n targetSegment,\n extensions\n );\n routes.push(...subRoutes);\n }\n }\n } catch (error) {\n console.error(`[Flight] Failed to scan intercept (${'.'.repeat(level)})${targetSegment}:`, error);\n }\n\n return routes;\n}\n\n// ============================================================================\n// Route Parser\n// ============================================================================\n\ninterface ParsedRoute {\n method: HttpMethod | 'ALL';\n path: string;\n type: 'page' | 'api';\n}\n\n/**\n * Parse route information from filename and path\n * \n * Patterns:\n * - index.ts → /\n * - users.ts → /users\n * - users.get.ts → GET /users (API)\n * - users.post.ts → POST /users (API)\n * - about.page.tsx → GET /about (Page)\n * - blog/[slug].page.tsx → GET /blog/:slug (Page)\n * - [id].ts → /:id\n * - [...slug].ts → /* (catch-all)\n * - [[...slug]].ts → /* (optional catch-all)\n */\nfunction parseRouteFile(filename: string, basePath: string): ParsedRoute | null {\n const ext = extname(filename);\n const nameWithoutExt = basename(filename, ext);\n\n // Skip files starting with underscore (private)\n if (nameWithoutExt.startsWith('_')) {\n return null;\n }\n\n // Detect route type\n let type: 'page' | 'api' = 'api';\n let routeName = nameWithoutExt;\n let method: HttpMethod | 'ALL' = 'ALL';\n\n // Check for page suffix (e.g., about.page.tsx, index.page.tsx)\n const pageMatch = nameWithoutExt.match(/^(.+)?\\.page$/i);\n if (pageMatch) {\n type = 'page';\n method = 'GET'; // Pages are always GET\n routeName = pageMatch[1] || 'index';\n } else {\n // Check for method suffix (e.g., users.get.ts)\n const methodMatch = nameWithoutExt.match(/^(.+)\\.(get|post|put|delete|patch|head|options)$/i);\n if (methodMatch) {\n routeName = methodMatch[1] || nameWithoutExt;\n method = (methodMatch[2] || 'ALL').toUpperCase() as HttpMethod;\n }\n }\n\n // Also check if file is in /api/ directory\n if (basePath.startsWith('/api') || basePath.includes('/api/')) {\n type = 'api';\n }\n\n // Build route path\n let path = basePath;\n\n if (routeName !== 'index') {\n path = `${basePath}/${convertToRoutePath(routeName)}`;\n }\n\n // Ensure path starts with /\n if (!path.startsWith('/')) {\n path = '/' + path;\n }\n\n // Remove trailing slash (except for root)\n if (path !== '/' && path.endsWith('/')) {\n path = path.slice(0, -1);\n }\n\n return { method, path, type };\n}\n\n/**\n * Convert filename segment to route path segment\n * \n * - [id] → :id\n * - [...slug] → *\n * - [[...slug]] → *?\n */\nfunction convertToRoutePath(name: string): string {\n // Optional catch-all: [[...slug]]\n if (name.startsWith('[[...') && name.endsWith(']]')) {\n const paramName = name.slice(5, -2);\n return `:${paramName}*`;\n }\n\n // Catch-all: [...slug]\n if (name.startsWith('[...') && name.endsWith(']')) {\n const paramName = name.slice(4, -1);\n return `:${paramName}+`;\n }\n\n // Dynamic param: [id]\n if (name.startsWith('[') && name.endsWith(']')) {\n const paramName = name.slice(1, -1);\n return `:${paramName}`;\n }\n\n return name;\n}\n\n// ============================================================================\n// Route Loader\n// ============================================================================\n\n/**\n * Load routes with their handlers or components\n * @param scanResult - Result from scanRoutes\n * @param moduleLoader - Optional custom loader (use vite.ssrLoadModule for dev)\n */\nexport async function loadRoutes(\n scanResult: ScanResult,\n moduleLoader?: (filePath: string) => Promise<any>\n): Promise<FileRoute[]> {\n const loadedRoutes: FileRoute[] = [];\n\n for (const route of scanResult.routes) {\n try {\n // Use custom loader if provided (Vite ssrLoadModule for TSX)\n // Otherwise fall back to native import() for production\n let module: any;\n if (moduleLoader) {\n module = await moduleLoader(route.filePath);\n } else {\n // Convert to file:// URL for Windows ESM compatibility\n const fileUrl = pathToFileURL(route.filePath).href;\n module = await import(fileUrl);\n }\n\n // Handle PAGE routes\n if (route.type === 'page') {\n // Pages export default component\n const component = module.default;\n const meta = module.meta || module.metadata || {};\n\n if (component) {\n loadedRoutes.push({\n ...route,\n component,\n meta,\n });\n }\n continue;\n }\n\n // Handle API routes\n if (route.method === 'ALL') {\n // Look for named exports: GET, POST, PUT, DELETE, etc\n const methods: HttpMethod[] = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'];\n\n for (const method of methods) {\n if (typeof module[method] === 'function') {\n loadedRoutes.push({\n ...route,\n method,\n handler: module[method],\n });\n }\n }\n\n // Also check for default export as handler\n if (typeof module.default === 'function') {\n loadedRoutes.push({\n ...route,\n method: 'ALL',\n handler: module.default,\n });\n }\n } else {\n // Specific method from filename suffix\n const handler = module[route.method] || module.default;\n\n if (typeof handler === 'function') {\n loadedRoutes.push({\n ...route,\n handler,\n });\n }\n }\n } catch (error) {\n console.error(`[Flight] Failed to load route ${route.filePath}:`, error);\n }\n }\n\n return loadedRoutes;\n}\n\n// ============================================================================\n// File Router Factory\n// ============================================================================\n\nexport interface FileRouter {\n routes: FileRoute[];\n refresh: () => Promise<void>;\n}\n\n/**\n * Create a file-based router\n * \n * @example\n * ```typescript\n * import { createFileRouter } from '@flight-framework/core/file-router';\n * import { createServer } from '@flight-framework/http';\n * \n * const router = await createFileRouter({ directory: './src/routes' });\n * const app = createServer();\n * \n * // Register all discovered routes\n * for (const route of router.routes) {\n * app[route.method.toLowerCase()](route.path, route.handler);\n * }\n * ```\n */\nexport async function createFileRouter(options: FileRouterOptions): Promise<FileRouter> {\n let routes: FileRoute[] = [];\n const { moduleLoader } = options;\n\n async function refresh(): Promise<void> {\n const scanResult = await scanRoutes(options);\n\n if (scanResult.errors.length > 0) {\n console.warn('[Flight] Route scan errors:', scanResult.errors);\n }\n\n routes = await loadRoutes(scanResult, moduleLoader);\n\n console.log(`[Flight] Loaded ${routes.length} routes from ${options.directory}`);\n }\n\n // Initial load\n await refresh();\n\n return {\n get routes() {\n return routes;\n },\n refresh,\n };\n}\n\n// ============================================================================\n// Parallel Routes Resolution\n// ============================================================================\n\n/**\n * Resolved parallel route slots for a layout.\n * Each slot name maps to its resolved component or null if not matched.\n */\nexport interface ResolvedSlots {\n [slotName: string]: {\n /** The component to render */\n component: () => unknown;\n /** The full route information */\n route: FileRoute;\n } | null;\n}\n\n/**\n * Resolve parallel route slots for a given path.\n * \n * Parallel routes use the @folder convention to define named slots\n * that can render alongside the main content in a layout.\n * \n * @param routes - All loaded routes from the file router\n * @param currentPath - The current URL path to resolve slots for\n * @returns Object mapping slot names to their resolved components\n * \n * @example\n * ```typescript\n * // Given routes from @modal/ and @sidebar/ directories\n * const slots = resolveParallelSlots(router.routes, '/dashboard');\n * \n * // In layout:\n * if (slots.modal) {\n * renderModal(slots.modal.component);\n * }\n * ```\n */\nexport function resolveParallelSlots(\n routes: FileRoute[],\n currentPath: string\n): ResolvedSlots {\n const slots: ResolvedSlots = {};\n const normalizedPath = normalizePath(currentPath);\n\n // Get all unique slot names from routes\n const slotNames = new Set<string>();\n for (const route of routes) {\n if (route.slot) {\n slotNames.add(route.slot);\n }\n }\n\n // For each slot, find matching route or default\n for (const slotName of slotNames) {\n const slotRoutes = routes.filter(r => r.slot === slotName);\n\n // Find exact match for current path\n let matchingRoute = slotRoutes.find(r => {\n const routePath = normalizePath(r.path);\n return pathMatches(routePath, normalizedPath);\n });\n\n // If no match, look for default.page in the slot\n if (!matchingRoute) {\n matchingRoute = slotRoutes.find(r =>\n r.path.endsWith('/default') || r.filePath.includes('default.page')\n );\n }\n\n if (matchingRoute && matchingRoute.component) {\n slots[slotName] = {\n component: matchingRoute.component,\n route: matchingRoute,\n };\n } else {\n // Slot has no matching content for this path\n slots[slotName] = null;\n }\n }\n\n return slots;\n}\n\n/**\n * Get the default page for an unmatched slot.\n * \n * When a parallel route slot doesn't have a matching route for the current path,\n * it should render its default.page if one exists, or null.\n * \n * @param routes - All loaded routes\n * @param slotName - Name of the slot (without @)\n * @param basePath - Base path to search from\n * @returns The default route for the slot, or null\n */\nexport function getSlotDefault(\n routes: FileRoute[],\n slotName: string,\n basePath: string = ''\n): FileRoute | null {\n const normalizedBase = normalizePath(basePath);\n\n return routes.find(r =>\n r.slot === slotName &&\n (r.filePath.includes('default.page') || r.path === `${normalizedBase}/default`)\n ) || null;\n}\n\n/**\n * Get all slot names used in the routes.\n * \n * @param routes - All loaded routes\n * @returns Array of unique slot names\n */\nexport function getSlotNames(routes: FileRoute[]): string[] {\n const names = new Set<string>();\n for (const route of routes) {\n if (route.slot) {\n names.add(route.slot);\n }\n }\n return Array.from(names);\n}\n\n// ============================================================================\n// Intercepting Routes Resolution\n// ============================================================================\n\n/**\n * Check if a navigation should be intercepted by an intercepting route.\n * \n * Intercepting routes use the (.) (..) (...) convention:\n * - (.)segment - intercepts from same level\n * - (..)segment - intercepts from parent level \n * - (...)segment - intercepts from root\n * \n * @param routes - All loaded routes\n * @param fromPath - Current path where navigation originates\n * @param toPath - Target path the user wants to navigate to\n * @returns The intercepting route if found, null otherwise\n * \n * @example\n * ```typescript\n * // User is on /feed and clicks link to /photo/123\n * const intercepted = findInterceptingRoute(routes, '/feed', '/photo/123');\n * \n * if (intercepted) {\n * // Render intercepted.component as modal instead of navigating\n * showModal(intercepted);\n * }\n * ```\n */\nexport function findInterceptingRoute(\n routes: FileRoute[],\n fromPath: string,\n toPath: string\n): FileRoute | null {\n const normalizedFrom = normalizePath(fromPath);\n const normalizedTo = normalizePath(toPath);\n\n // Filter to only intercepting routes\n const interceptingRoutes = routes.filter(r => r.interceptInfo);\n\n for (const route of interceptingRoutes) {\n const { interceptInfo } = route;\n if (!interceptInfo) continue;\n\n // Check if the interceptPath matches the target path\n const interceptPath = normalizePath(interceptInfo.interceptPath);\n\n if (!pathMatches(interceptPath, normalizedTo)) {\n continue;\n }\n\n // Check if we're navigating from a valid origin based on level\n // For level 1 (.), the intercept route is at same level as navigation origin\n // For level 2 (..), the intercept route is one level up from navigation origin\n // For level 3+ (...), intercepts from anywhere\n\n let validOrigin = false;\n\n // Find the intercept marker position in route path\n // Route path example: /feed/(.)photo/:id -> base is /feed\n const routePathStr = normalizePath(route.path);\n const interceptMarkerMatch = routePathStr.match(/\\/\\(\\.+\\)/);\n\n if (interceptInfo.level >= 3) {\n // (...) - Root level: intercepts from anywhere\n validOrigin = true;\n } else if (interceptMarkerMatch && interceptMarkerMatch.index !== undefined) {\n // Extract the base path before the intercept marker\n const routeBase = routePathStr.substring(0, interceptMarkerMatch.index);\n\n if (interceptInfo.level === 1) {\n // (.) - Same level: origin should be at or under the route base\n // Route at /feed/(.)photo, origin should be /feed or /feed/*\n validOrigin = normalizedFrom === routeBase ||\n normalizedFrom.startsWith(routeBase + '/') ||\n (routeBase === '' && normalizedFrom.startsWith('/'));\n } else if (interceptInfo.level === 2) {\n // (..) - Parent level: origin is one level deeper\n // Route at /gallery/albums/(..)photo, origin should be /gallery/albums/*\n validOrigin = normalizedFrom === routeBase ||\n normalizedFrom.startsWith(routeBase + '/');\n }\n }\n\n if (validOrigin) {\n return route;\n }\n }\n\n return null;\n}\n\n/**\n * Check if an intercepting route should be dismissed on this navigation.\n * \n * @param currentRoute - The currently active intercepting route\n * @param toPath - The path being navigated to\n * @returns true if the interception should be dismissed\n */\nexport function shouldDismissIntercept(\n currentRoute: FileRoute | null,\n toPath: string\n): boolean {\n if (!currentRoute || !currentRoute.interceptInfo) {\n return false;\n }\n\n const normalizedTo = normalizePath(toPath);\n const interceptPath = normalizePath(currentRoute.interceptInfo.interceptPath);\n\n // Dismiss if navigating away from the intercepted path\n return !pathMatches(interceptPath, normalizedTo);\n}\n\n/**\n * Get route params from a path match.\n * \n * @param routePath - The route pattern with params (e.g., /photo/:id)\n * @param actualPath - The actual URL path (e.g., /photo/123)\n * @returns Object with matched params, or null if no match\n */\nexport function extractRouteParams(\n routePath: string,\n actualPath: string\n): Record<string, string> | null {\n const routeParts = routePath.split('/').filter(Boolean);\n const actualParts = actualPath.split('/').filter(Boolean);\n\n if (routeParts.length !== actualParts.length) {\n // Check for catch-all\n const hasCatchAll = routeParts.some(p => p.endsWith('+') || p.endsWith('*'));\n if (!hasCatchAll) {\n return null;\n }\n }\n\n const params: Record<string, string> = {};\n\n for (let i = 0; i < routeParts.length; i++) {\n const routePart = routeParts[i];\n const actualPart = actualParts[i];\n\n if (!routePart) continue;\n\n if (routePart.startsWith(':')) {\n // Dynamic param\n const paramName = routePart.slice(1).replace(/[+*]$/, '');\n\n if (routePart.endsWith('+') || routePart.endsWith('*')) {\n // Catch-all: collect remaining parts\n params[paramName] = actualParts.slice(i).join('/');\n break;\n }\n\n if (!actualPart) {\n if (routePart.endsWith('*')) {\n // Optional catch-all, can be empty\n params[paramName] = '';\n break;\n }\n return null;\n }\n\n params[paramName] = actualPart;\n } else if (routePart !== actualPart) {\n return null;\n }\n }\n\n return params;\n}\n\n// ============================================================================\n// Path Utilities\n// ============================================================================\n\n/**\n * Normalize a path for consistent comparison.\n */\nfunction normalizePath(path: string): string {\n let normalized = path.trim();\n\n // Ensure starts with /\n if (!normalized.startsWith('/')) {\n normalized = '/' + normalized;\n }\n\n // Remove trailing slash (except for root)\n if (normalized !== '/' && normalized.endsWith('/')) {\n normalized = normalized.slice(0, -1);\n }\n\n // Remove duplicate slashes\n normalized = normalized.replace(/\\/+/g, '/');\n\n return normalized;\n}\n\n/**\n * Check if a route path matches an actual path.\n * Handles dynamic segments (:param) and catch-alls.\n */\nfunction pathMatches(routePath: string, actualPath: string): boolean {\n const routeParts = routePath.split('/').filter(Boolean);\n const actualParts = actualPath.split('/').filter(Boolean);\n\n for (let i = 0; i < routeParts.length; i++) {\n const routePart = routeParts[i];\n const actualPart = actualParts[i];\n\n if (!routePart) continue;\n\n // Catch-all segment\n if (routePart.startsWith(':') && (routePart.endsWith('+') || routePart.endsWith('*'))) {\n return true; // Matches rest of path\n }\n\n // Dynamic segment\n if (routePart.startsWith(':')) {\n if (!actualPart) return false;\n continue; // Any value matches\n }\n\n // Static segment - must match exactly\n if (routePart !== actualPart) {\n return false;\n }\n }\n\n // Check lengths match (unless catch-all was present)\n return routeParts.length === actualParts.length;\n}\n\n"]}
@@ -139,6 +139,32 @@ function composeComponents(...components) {
139
139
  return results.join("");
140
140
  };
141
141
  }
142
+ function withAsyncErrorBoundary(component, options) {
143
+ const { fallback, onError, rethrowNavigation = true } = options;
144
+ return async (props, context) => {
145
+ try {
146
+ return await component(props, context);
147
+ } catch (error) {
148
+ if (rethrowNavigation) {
149
+ if (isNotFoundError(error)) {
150
+ throw error;
151
+ }
152
+ if (isRedirectError(error)) {
153
+ throw error;
154
+ }
155
+ }
156
+ if (onError) {
157
+ try {
158
+ onError(error, props, context);
159
+ } catch (callbackError) {
160
+ console.error("[Flight] Error in onError callback:", callbackError);
161
+ }
162
+ }
163
+ console.error("[Flight] Async component error:", error);
164
+ return fallback(error, props);
165
+ }
166
+ };
167
+ }
142
168
  function withErrorBoundary(component, errorFallback) {
143
169
  return async (props, context) => {
144
170
  try {
@@ -169,6 +195,6 @@ function isRedirectError(error) {
169
195
  return null;
170
196
  }
171
197
 
172
- export { composeComponents, createAsyncComponent, createClientBoundary, createRenderContext, deserializeProps, executeServerComponent, isNotFoundError, isRedirectError, notFound, redirect, revalidatePath, revalidateTag, serializeProps, serverFetch, withErrorBoundary };
173
- //# sourceMappingURL=chunk-2R23X5Z3.js.map
174
- //# sourceMappingURL=chunk-2R23X5Z3.js.map
198
+ export { composeComponents, createAsyncComponent, createClientBoundary, createRenderContext, deserializeProps, executeServerComponent, isNotFoundError, isRedirectError, notFound, redirect, revalidatePath, revalidateTag, serializeProps, serverFetch, withAsyncErrorBoundary, withErrorBoundary };
199
+ //# sourceMappingURL=chunk-6IG6XIXU.js.map
200
+ //# sourceMappingURL=chunk-6IG6XIXU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rsc/legacy.ts"],"names":[],"mappings":";AAoEA,eAAsB,sBAAA,CAClB,SAAA,EACA,KAAA,EACA,OAAA,EACe;AACf,EAAA,IAAI;AACA,IAAA,MAAM,MAAA,GAAS,MAAM,SAAA,CAAU,KAAA,EAAO,OAAO,CAAA;AAC7C,IAAA,OAAO,MAAA;AAAA,EACX,SAAS,KAAA,EAAO;AACZ,IAAA,OAAA,CAAQ,KAAA,CAAM,oCAAoC,KAAK,CAAA;AACvD,IAAA,MAAM,KAAA;AAAA,EACV;AACJ;AAMO,SAAS,mBAAA,CAAoB,OAAA,EAAkB,MAAA,GAAiC,EAAC,EAAkB;AACtG,EAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAG/B,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAoB;AACxC,EAAA,MAAM,YAAA,GAAe,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,QAAQ,CAAA,IAAK,EAAA;AACtD,EAAA,YAAA,CAAa,KAAA,CAAM,GAAG,CAAA,CAAE,OAAA,CAAQ,CAAA,MAAA,KAAU;AACtC,IAAA,MAAM,CAAC,KAAK,KAAK,CAAA,GAAI,OAAO,IAAA,EAAK,CAAE,MAAM,GAAG,CAAA;AAC5C,IAAA,IAAI,GAAA,IAAO,KAAA,EAAO,OAAA,CAAQ,GAAA,CAAI,KAAK,KAAK,CAAA;AAAA,EAC5C,CAAC,CAAA;AAED,EAAA,OAAO;AAAA,IACH,OAAA;AAAA,IACA,MAAA;AAAA,IACA,cAAc,GAAA,CAAI,YAAA;AAAA,IAClB,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB;AAAA,GACJ;AACJ;AASA,IAAM,UAAA,uBAAiB,GAAA,EAAkD;AAKzE,eAAsB,WAAA,CAClB,GAAA,EACA,OAAA,GAGI,EAAC,EACK;AACV,EAAA,MAAM,EAAE,UAAA,GAAa,EAAA,EAAI,GAAG,cAAa,GAAI,OAAA;AAC7C,EAAA,MAAM,WAAW,CAAA,EAAG,GAAG,IAAI,IAAA,CAAK,SAAA,CAAU,YAAY,CAAC,CAAA,CAAA;AAGvD,EAAA,MAAM,MAAA,GAAS,UAAA,CAAW,GAAA,CAAI,QAAQ,CAAA;AACtC,EAAA,IAAI,MAAA,EAAQ;AACR,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,EAAI,GAAI,MAAA,CAAO,SAAA;AAChC,IAAA,IAAI,UAAA,KAAe,KAAA,IAAS,GAAA,GAAO,UAAA,GAAa,GAAA,EAAO;AACnD,MAAA,OAAO,MAAA,CAAO,IAAA;AAAA,IAClB;AAAA,EACJ;AAGA,EAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA,EAAK,YAAY,CAAA;AAE9C,EAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AACd,IAAA,MAAM,IAAI,MAAM,CAAA,cAAA,EAAiB,QAAA,CAAS,MAAM,CAAA,CAAA,EAAI,QAAA,CAAS,UAAU,CAAA,CAAE,CAAA;AAAA,EAC7E;AAEA,EAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AAGjC,EAAA,IAAI,eAAe,KAAA,EAAO;AACtB,IAAA,UAAA,CAAW,IAAI,QAAA,EAAU;AAAA,MACrB,IAAA;AAAA,MACA,SAAA,EAAW,KAAK,GAAA;AAAI,KACvB,CAAA;AAAA,EACL;AAEA,EAAA,OAAO,IAAA;AACX;AAKO,SAAS,cAAc,GAAA,EAAmB;AAC7C,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,2BAAA,EAA8B,GAAG,CAAA,CAAE,CAAA;AACnD;AAKO,SAAS,eAAe,IAAA,EAAoB;AAC/C,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,4BAAA,EAA+B,IAAI,CAAA,CAAE,CAAA;AACrD;AAUO,SAAS,eAAe,KAAA,EAAwB;AACnD,EAAA,SAAS,WAAW,KAAA,EAAyB;AACzC,IAAA,IAAI,iBAAiB,IAAA,EAAM;AACvB,MAAA,OAAO,EAAE,MAAA,EAAQ,MAAA,EAAQ,KAAA,EAAO,KAAA,CAAM,aAAY,EAAE;AAAA,IACxD;AACA,IAAA,IAAI,iBAAiB,GAAA,EAAK;AACtB,MAAA,OAAO,EAAE,MAAA,EAAQ,KAAA,EAAO,KAAA,EAAO,KAAA,CAAM,KAAK,KAAA,CAAM,OAAA,EAAS,CAAA,CAAE,GAAA,CAAI,CAAC,CAAC,CAAA,EAAG,CAAC,CAAA,KAAM,CAAC,UAAA,CAAW,CAAC,GAAG,UAAA,CAAW,CAAC,CAAC,CAAC,CAAA,EAAE;AAAA,IAC/G;AACA,IAAA,IAAI,iBAAiB,GAAA,EAAK;AACtB,MAAA,OAAO,EAAE,MAAA,EAAQ,KAAA,EAAO,KAAA,EAAO,KAAA,CAAM,KAAK,KAAK,CAAA,CAAE,GAAA,CAAI,UAAU,CAAA,EAAE;AAAA,IACrE;AACA,IAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC3B,MAAA,OAAO,EAAE,MAAA,EAAQ,QAAA,EAAU,KAAA,EAAO,KAAA,CAAM,UAAS,EAAE;AAAA,IACvD;AACA,IAAA,IAAI,OAAO,UAAU,UAAA,EAAY;AAC7B,MAAA,OAAO,MAAA;AAAA,IACX;AACA,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACtB,MAAA,OAAO,KAAA,CAAM,IAAI,UAAU,CAAA;AAAA,IAC/B;AACA,IAAA,IAAI,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,EAAU;AACpC,MAAA,MAAM,SAAkC,EAAC;AACzC,MAAA,KAAA,MAAW,CAAC,CAAA,EAAG,CAAC,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,EAAG;AACxC,QAAA,MAAA,CAAO,CAAC,CAAA,GAAI,UAAA,CAAW,CAAC,CAAA;AAAA,MAC5B;AACA,MAAA,OAAO,MAAA;AAAA,IACX;AACA,IAAA,OAAO,KAAA;AAAA,EACX;AAEA,EAAA,OAAO,IAAA,CAAK,SAAA,CAAU,UAAA,CAAW,KAAK,CAAC,CAAA;AAC3C;AAMO,SAAS,iBAAoB,UAAA,EAAuB;AACvD,EAAA,OAAO,IAAA,CAAK,KAAA,CAAM,UAAA,EAAY,CAAC,MAAM,KAAA,KAAU;AAC3C,IAAA,IAAI,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,IAAY,YAAY,KAAA,EAAO;AACzD,MAAA,QAAQ,MAAM,MAAA;AAAQ,QAClB,KAAK,MAAA;AACD,UAAA,OAAO,IAAI,IAAA,CAAK,KAAA,CAAM,KAAK,CAAA;AAAA,QAC/B,KAAK,KAAA;AACD,UAAA,OAAO,IAAI,GAAA,CAAI,KAAA,CAAM,KAAK,CAAA;AAAA,QAC9B,KAAK,KAAA;AACD,UAAA,OAAO,IAAI,GAAA,CAAI,KAAA,CAAM,KAAK,CAAA;AAAA,QAC9B,KAAK,QAAA;AACD,UAAA,OAAO,MAAA,CAAO,MAAM,KAAK,CAAA;AAAA;AACjC,IACJ;AACA,IAAA,OAAO,KAAA;AAAA,EACX,CAAC,CAAA;AACL;AAUO,SAAS,oBAAA,CACZ,WAAA,EACA,KAAA,EACA,QAAA,EACM;AACN,EAAA,MAAM,eAAA,GAAkB,eAAe,KAAK,CAAA;AAE5C,EAAA,OAAO;AAAA,kBAAA,EACS,WAAW,CAAA;AAAA,4BAAA,EACD,WAAW,CAAA,qBAAA,EAAwB,eAAA,CAAgB,OAAA,CAAQ,IAAA,EAAM,OAAO,CAAC,CAAA;AAAA,IAAA,EACjG,YAAY,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA,wDAAA,EAKiB,WAAW,CAAA;AAAA,8BAAA,EACrC,eAAA,CAAgB,OAAA,CAAQ,IAAA,EAAM,KAAK,CAAC,CAAA;AAAA,sEAAA,EACI,WAAW,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAA,CAAA;AAQnF;AAUO,SAAS,oBAAA,CACZ,SACA,QAAA,EACkB;AAClB,EAAA,MAAM,SAAA,GAAgC,OAAO,KAAA,EAAO,OAAA,KAAY;AAC5D,IAAA,MAAM,IAAA,GAAO,MAAM,OAAA,CAAQ,KAAA,EAAO,OAAO,CAAA;AACzC,IAAA,OAAO,QAAA,CAAS,MAAM,KAAK,CAAA;AAAA,EAC/B,CAAA;AACA,EAAA,SAAA,CAAU,eAAA,GAAkB,IAAA;AAC5B,EAAA,OAAO,SAAA;AACX;AAMO,SAAS,qBACT,UAAA,EACkB;AACrB,EAAA,OAAO,YAAY;AACf,IAAA,MAAM,OAAA,GAAU,MAAM,OAAA,CAAQ,GAAA;AAAA,MAC1B,WAAW,GAAA,CAAI,OAAO,IAAA,KAAS,MAAM,MAAM;AAAA,KAC/C;AACA,IAAA,OAAO,OAAA,CAAQ,KAAK,EAAE,CAAA;AAAA,EAC1B,CAAA;AACJ;AA8BO,SAAS,sBAAA,CACZ,WACA,OAAA,EACgD;AAChD,EAAA,MAAM,EAAE,QAAA,EAAU,OAAA,EAAS,iBAAA,GAAoB,MAAK,GAAI,OAAA;AAExD,EAAA,OAAO,OAAO,OAAU,OAAA,KAAuC;AAC3D,IAAA,IAAI;AACA,MAAA,OAAO,MAAM,SAAA,CAAU,KAAA,EAAO,OAAO,CAAA;AAAA,IACzC,SAAS,KAAA,EAAO;AAEZ,MAAA,IAAI,iBAAA,EAAmB;AACnB,QAAA,IAAI,eAAA,CAAgB,KAAK,CAAA,EAAG;AACxB,UAAA,MAAM,KAAA;AAAA,QACV;AACA,QAAA,IAAI,eAAA,CAAgB,KAAK,CAAA,EAAG;AACxB,UAAA,MAAM,KAAA;AAAA,QACV;AAAA,MACJ;AAGA,MAAA,IAAI,OAAA,EAAS;AACT,QAAA,IAAI;AACA,UAAA,OAAA,CAAQ,KAAA,EAAgB,OAAO,OAAO,CAAA;AAAA,QAC1C,SAAS,aAAA,EAAe;AACpB,UAAA,OAAA,CAAQ,KAAA,CAAM,uCAAuC,aAAa,CAAA;AAAA,QACtE;AAAA,MACJ;AAGA,MAAA,OAAA,CAAQ,KAAA,CAAM,mCAAmC,KAAK,CAAA;AAGtD,MAAA,OAAO,QAAA,CAAS,OAAgB,KAAK,CAAA;AAAA,IACzC;AAAA,EACJ,CAAA;AACJ;AAMO,SAAS,iBAAA,CACZ,WACA,aAAA,EACkB;AAClB,EAAA,OAAO,OAAO,OAAU,OAAA,KAA2B;AAC/C,IAAA,IAAI;AACA,MAAA,OAAO,MAAM,SAAA,CAAU,KAAA,EAAO,OAAO,CAAA;AAAA,IACzC,SAAS,KAAA,EAAO;AACZ,MAAA,OAAA,CAAQ,KAAA,CAAM,oCAAoC,KAAK,CAAA;AACvD,MAAA,OAAO,cAAc,KAAc,CAAA;AAAA,IACvC;AAAA,EACJ,CAAA;AACJ;AAUO,SAAS,QAAA,GAAkB;AAC9B,EAAA,MAAM,KAAA,GAAQ,IAAI,KAAA,CAAM,WAAW,CAAA;AACnC,EAAC,MAAkD,kBAAA,GAAqB,IAAA;AACxE,EAAA,MAAM,KAAA;AACV;AAMO,SAAS,gBAAgB,KAAA,EAAyB;AACrD,EAAA,OAAO,KAAA,YAAiB,KAAA,IAAU,KAAA,CAAmD,kBAAA,KAAuB,IAAA;AAChH;AAMO,SAAS,QAAA,CAAS,GAAA,EAAa,IAAA,GAA2B,SAAA,EAAkB;AAC/E,EAAA,MAAM,KAAA,GAAQ,IAAI,KAAA,CAAM,CAAA,UAAA,EAAa,GAAG,CAAA,CAAE,CAAA;AAC1C,EAAC,KAAA,CAAuE,iBAAA,GAAoB,EAAE,GAAA,EAAK,IAAA,EAAK;AACxG,EAAA,MAAM,KAAA;AACV;AAMO,SAAS,gBAAgB,KAAA,EAAsD;AAClF,EAAA,IAAI,KAAA,YAAiB,KAAA,IAAU,KAAA,CAAkD,iBAAA,EAAmB;AAChG,IAAA,OAAQ,KAAA,CAAuE,iBAAA;AAAA,EACnF;AACA,EAAA,OAAO,IAAA;AACX","file":"chunk-6IG6XIXU.js","sourcesContent":["/**\n * @flight-framework/core - Legacy RSC Support\n * \n * Backward compatibility module for the original RSC implementation.\n * Use the new API from './index.js' for new features.\n * \n * @deprecated Use the new RSC API instead\n * @module @flight-framework/core/rsc/legacy\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Component rendering context\n * @deprecated Use ServerContext from './context.js' instead\n */\nexport interface RenderContext {\n /** Current request */\n request: Request;\n /** Route params */\n params: Record<string, string>;\n /** Search params */\n searchParams: URLSearchParams;\n /** Request headers */\n headers: Headers;\n /** Cookies */\n cookies: Map<string, string>;\n}\n\n/**\n * Server component definition\n * @deprecated Use ServerComponentFn from './index.js' instead\n */\nexport interface ServerComponent<P = unknown> {\n (props: P, context: RenderContext): Promise<string> | string;\n /** Mark as server component */\n __flight_server?: true;\n /** Dependencies for hydration */\n __flight_deps?: string[];\n}\n\n/**\n * Client component definition\n * @deprecated Use ClientReference from './boundaries.js' instead\n */\nexport interface ClientComponent<P = unknown> {\n (props: P): unknown;\n /** Mark as client component */\n __flight_client?: true;\n /** Client bundle path */\n __flight_bundle?: string;\n}\n\n/**\n * Component type detection\n */\nexport type ComponentType = 'server' | 'client' | 'hybrid';\n\n// ============================================================================\n// Server Component Execution\n// ============================================================================\n\n/**\n * Execute an async server component\n * @deprecated Use the new rendering pipeline instead\n */\nexport async function executeServerComponent<P>(\n component: ServerComponent<P>,\n props: P,\n context: RenderContext\n): Promise<string> {\n try {\n const result = await component(props, context);\n return result;\n } catch (error) {\n console.error('[Flight] Server component error:', error);\n throw error;\n }\n}\n\n/**\n * Create a render context from a Request\n * @deprecated Use createServerContext from './context.js' instead\n */\nexport function createRenderContext(request: Request, params: Record<string, string> = {}): RenderContext {\n const url = new URL(request.url);\n\n // Parse cookies\n const cookies = new Map<string, string>();\n const cookieHeader = request.headers.get('cookie') || '';\n cookieHeader.split(';').forEach(cookie => {\n const [key, value] = cookie.trim().split('=');\n if (key && value) cookies.set(key, value);\n });\n\n return {\n request,\n params,\n searchParams: url.searchParams,\n headers: request.headers,\n cookies,\n };\n}\n\n// ============================================================================\n// Data Fetching Helpers\n// ============================================================================\n\n/**\n * Cache for server-side fetch requests\n */\nconst fetchCache = new Map<string, { data: unknown; timestamp: number }>();\n\n/**\n * Server-side fetch with automatic caching\n */\nexport async function serverFetch<T>(\n url: string,\n options: RequestInit & {\n revalidate?: number | false;\n tags?: string[];\n } = {}\n): Promise<T> {\n const { revalidate = 60, ...fetchOptions } = options;\n const cacheKey = `${url}:${JSON.stringify(fetchOptions)}`;\n\n // Check cache\n const cached = fetchCache.get(cacheKey);\n if (cached) {\n const age = Date.now() - cached.timestamp;\n if (revalidate === false || age < (revalidate * 1000)) {\n return cached.data as T;\n }\n }\n\n // Fetch fresh data\n const response = await fetch(url, fetchOptions);\n\n if (!response.ok) {\n throw new Error(`Fetch failed: ${response.status} ${response.statusText}`);\n }\n\n const data = await response.json();\n\n // Cache the result\n if (revalidate !== false) {\n fetchCache.set(cacheKey, {\n data,\n timestamp: Date.now(),\n });\n }\n\n return data as T;\n}\n\n/**\n * Invalidate cache by tag\n */\nexport function revalidateTag(tag: string): void {\n console.log(`[Flight] Revalidating tag: ${tag}`);\n}\n\n/**\n * Invalidate cache by path\n */\nexport function revalidatePath(path: string): void {\n console.log(`[Flight] Revalidating path: ${path}`);\n}\n\n// ============================================================================\n// Component Serialization\n// ============================================================================\n\n/**\n * Serialize props for transmission to client\n * @deprecated Use serialize from './payload.js' instead\n */\nexport function serializeProps(props: unknown): string {\n function preProcess(value: unknown): unknown {\n if (value instanceof Date) {\n return { __type: 'Date', value: value.toISOString() };\n }\n if (value instanceof Map) {\n return { __type: 'Map', value: Array.from(value.entries()).map(([k, v]) => [preProcess(k), preProcess(v)]) };\n }\n if (value instanceof Set) {\n return { __type: 'Set', value: Array.from(value).map(preProcess) };\n }\n if (typeof value === 'bigint') {\n return { __type: 'BigInt', value: value.toString() };\n }\n if (typeof value === 'function') {\n return undefined;\n }\n if (Array.isArray(value)) {\n return value.map(preProcess);\n }\n if (value && typeof value === 'object') {\n const result: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value)) {\n result[k] = preProcess(v);\n }\n return result;\n }\n return value;\n }\n\n return JSON.stringify(preProcess(props));\n}\n\n/**\n * Deserialize props on client\n * @deprecated Use deserialize from './payload.js' instead\n */\nexport function deserializeProps<T>(serialized: string): T {\n return JSON.parse(serialized, (_key, value) => {\n if (value && typeof value === 'object' && '__type' in value) {\n switch (value.__type) {\n case 'Date':\n return new Date(value.value);\n case 'Map':\n return new Map(value.value);\n case 'Set':\n return new Set(value.value);\n case 'BigInt':\n return BigInt(value.value);\n }\n }\n return value;\n });\n}\n\n// ============================================================================\n// Client Component Boundary\n// ============================================================================\n\n/**\n * Create a client boundary placeholder\n * @deprecated Use clientRef from './payload.js' instead\n */\nexport function createClientBoundary(\n componentId: string,\n props: unknown,\n fallback?: string\n): string {\n const serializedProps = serializeProps(props);\n\n return `\n<!--flight-client:${componentId}-->\n<div data-flight-component=\"${componentId}\" data-flight-props='${serializedProps.replace(/'/g, \"&#39;\")}'>\n ${fallback || '<div>Loading...</div>'}\n</div>\n<!--/flight-client-->\n<script type=\"module\">\n(async function() {\n const component = await import('/_flight/components/${componentId}.js');\n const props = JSON.parse('${serializedProps.replace(/'/g, \"\\\\'\")}');\n const container = document.querySelector('[data-flight-component=\"${componentId}\"]');\n if (container && component.default) {\n if (typeof component.hydrate === 'function') {\n component.hydrate(container, props);\n }\n }\n})();\n</script>`;\n}\n\n// ============================================================================\n// Async Component Helpers\n// ============================================================================\n\n/**\n * Helper to create an async server component\n * @deprecated Use async Server Components directly\n */\nexport function createAsyncComponent<P, T>(\n fetcher: (props: P, context: RenderContext) => Promise<T>,\n renderer: (data: T, props: P) => string\n): ServerComponent<P> {\n const component: ServerComponent<P> = async (props, context) => {\n const data = await fetcher(props, context);\n return renderer(data, props);\n };\n component.__flight_server = true;\n return component;\n}\n\n/**\n * Compose multiple server components\n * @deprecated Use Promise.all with async components\n */\nexport function composeComponents(\n ...components: Array<() => Promise<string> | string>\n): () => Promise<string> {\n return async () => {\n const results = await Promise.all(\n components.map(async (comp) => await comp())\n );\n return results.join('');\n };\n}\n\n// ============================================================================\n// Error Boundary for Server Components\n// ============================================================================\n\n/**\n * Options for async error boundary\n */\nexport interface AsyncErrorBoundaryOptions<P, R> {\n /** Fallback to render on error */\n fallback: (error: Error, props: P) => R;\n /** Optional callback when error occurs */\n onError?: (error: Error, props: P, context: RenderContext) => void;\n /** Whether to rethrow certain errors (e.g., redirects, not found) */\n rethrowNavigation?: boolean;\n}\n\n/**\n * Wrap an async server component with comprehensive error handling.\n *\n * @example\n * ```typescript\n * const SafeUserPage = withAsyncErrorBoundary(UserPage, {\n * fallback: (error, props) => `<div>Error loading user ${props.id}</div>`,\n * onError: (error, props) => console.error(`Failed to load user ${props.id}:`, error),\n * rethrowNavigation: true,\n * });\n * ```\n */\nexport function withAsyncErrorBoundary<P, R = string>(\n component: (props: P, context: RenderContext) => Promise<R> | R,\n options: AsyncErrorBoundaryOptions<P, R>\n): (props: P, context: RenderContext) => Promise<R> {\n const { fallback, onError, rethrowNavigation = true } = options;\n\n return async (props: P, context: RenderContext): Promise<R> => {\n try {\n return await component(props, context);\n } catch (error) {\n // Rethrow navigation errors if configured\n if (rethrowNavigation) {\n if (isNotFoundError(error)) {\n throw error;\n }\n if (isRedirectError(error)) {\n throw error;\n }\n }\n\n // Call error callback if provided\n if (onError) {\n try {\n onError(error as Error, props, context);\n } catch (callbackError) {\n console.error('[Flight] Error in onError callback:', callbackError);\n }\n }\n\n // Log error\n console.error('[Flight] Async component error:', error);\n\n // Return fallback\n return fallback(error as Error, props);\n }\n };\n}\n\n/**\n * Wrap a server component with error handling\n * @deprecated Use withAsyncErrorBoundary instead\n */\nexport function withErrorBoundary<P>(\n component: ServerComponent<P>,\n errorFallback: (error: Error) => string\n): ServerComponent<P> {\n return async (props: P, context: RenderContext) => {\n try {\n return await component(props, context);\n } catch (error) {\n console.error('[Flight] Server component error:', error);\n return errorFallback(error as Error);\n }\n };\n}\n\n// ============================================================================\n// Not Found / Redirect Helpers\n// ============================================================================\n\n/**\n * Throw a not found error\n * @deprecated Use notFound from './context.js' instead\n */\nexport function notFound(): never {\n const error = new Error('Not Found');\n (error as Error & { __flight_not_found: boolean }).__flight_not_found = true;\n throw error;\n}\n\n/**\n * Check if error is not found\n * @deprecated Use isNotFoundError from './context.js' instead\n */\nexport function isNotFoundError(error: unknown): boolean {\n return error instanceof Error && (error as Error & { __flight_not_found?: boolean }).__flight_not_found === true;\n}\n\n/**\n * Server-side redirect\n * @deprecated Use redirect from './context.js' instead\n */\nexport function redirect(url: string, type: 'replace' | 'push' = 'replace'): never {\n const error = new Error(`Redirect: ${url}`);\n (error as Error & { __flight_redirect: { url: string; type: string } }).__flight_redirect = { url, type };\n throw error;\n}\n\n/**\n * Check if error is redirect\n * @deprecated Use isRedirectError from './context.js' instead\n */\nexport function isRedirectError(error: unknown): { url: string; type: string } | null {\n if (error instanceof Error && (error as Error & { __flight_redirect?: unknown }).__flight_redirect) {\n return (error as Error & { __flight_redirect: { url: string; type: string } }).__flight_redirect;\n }\n return null;\n}\n"]}
@@ -1,6 +1,6 @@
1
+ import { createMiddlewareChain, createContextFromRequest, createResponseFromContext } from './chunk-R7MEVVA4.js';
1
2
  import { resolveConfig } from './chunk-EHVUAFNH.js';
2
3
  import { createRouter } from './chunk-ARBKF6VI.js';
3
- import { createMiddlewareChain, createContextFromRequest, createResponseFromContext } from './chunk-JFUKVWSO.js';
4
4
 
5
5
  // src/server/index.ts
6
6
  function detectRuntime() {
@@ -219,5 +219,5 @@ function getRuntime() {
219
219
  }
220
220
 
221
221
  export { createServer, getRuntime, isFlightServer };
222
- //# sourceMappingURL=chunk-UL4Q5CIJ.js.map
223
- //# sourceMappingURL=chunk-UL4Q5CIJ.js.map
222
+ //# sourceMappingURL=chunk-KDEA64UX.js.map
223
+ //# sourceMappingURL=chunk-KDEA64UX.js.map