@flight-framework/core 0.1.0 → 0.2.1
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/LICENSE +21 -0
- package/dist/{chunk-5KF3QQWZ.js → chunk-3ZSSRE6M.js} +5 -19
- package/dist/chunk-3ZSSRE6M.js.map +1 -0
- package/dist/chunk-4S5JWTGN.js +218 -0
- package/dist/chunk-4S5JWTGN.js.map +1 -0
- package/dist/chunk-62C7LX2E.js +205 -0
- package/dist/chunk-62C7LX2E.js.map +1 -0
- package/dist/chunk-63SCEXD7.js +3 -0
- package/dist/chunk-63SCEXD7.js.map +1 -0
- package/dist/{chunk-YIOQC3DC.js → chunk-6BDCTUQY.js} +3 -3
- package/dist/{chunk-YIOQC3DC.js.map → chunk-6BDCTUQY.js.map} +1 -1
- package/dist/chunk-6Z2RYHEU.js +212 -0
- package/dist/chunk-6Z2RYHEU.js.map +1 -0
- package/dist/chunk-HAX5OAB6.js +324 -0
- package/dist/chunk-HAX5OAB6.js.map +1 -0
- package/dist/chunk-K2CQZPCG.js +257 -0
- package/dist/chunk-K2CQZPCG.js.map +1 -0
- package/dist/chunk-N3TUOEV4.js +213 -0
- package/dist/chunk-N3TUOEV4.js.map +1 -0
- package/dist/chunk-NWMJYTMB.js +300 -0
- package/dist/chunk-NWMJYTMB.js.map +1 -0
- package/dist/chunk-OCVLNLTI.js +252 -0
- package/dist/chunk-OCVLNLTI.js.map +1 -0
- package/dist/chunk-PDW5WCMW.js +258 -0
- package/dist/chunk-PDW5WCMW.js.map +1 -0
- package/dist/chunk-PVUMB632.js +256 -0
- package/dist/chunk-PVUMB632.js.map +1 -0
- package/dist/{chunk-6WSPUG5L.js → chunk-RSVA2EYO.js} +2 -2
- package/dist/chunk-RSVA2EYO.js.map +1 -0
- package/dist/chunk-T4Z4HM4W.js +285 -0
- package/dist/chunk-T4Z4HM4W.js.map +1 -0
- package/dist/{chunk-OBNYNJB5.js → chunk-WOEIJWGJ.js} +6 -8
- package/dist/chunk-WOEIJWGJ.js.map +1 -0
- package/dist/{chunk-I2B4WSHC.js → chunk-XSY5AAXT.js} +3 -4
- package/dist/chunk-XSY5AAXT.js.map +1 -0
- package/dist/chunk-Y22AMGTM.js +3 -0
- package/dist/chunk-Y22AMGTM.js.map +1 -0
- package/dist/chunk-ZIE56LCA.js +341 -0
- package/dist/chunk-ZIE56LCA.js.map +1 -0
- package/dist/file-router/streaming-hints.d.ts +1 -1
- package/dist/file-router/streaming-hints.js +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +19 -7
- package/dist/index.js.map +1 -1
- package/dist/rsc/adapters/index.d.ts +8 -0
- package/dist/rsc/adapters/index.js +7 -0
- package/dist/rsc/adapters/index.js.map +1 -0
- package/dist/rsc/adapters/preact.d.ts +97 -0
- package/dist/rsc/adapters/preact.js +3 -0
- package/dist/rsc/adapters/preact.js.map +1 -0
- package/dist/rsc/adapters/react.d.ts +82 -0
- package/dist/rsc/adapters/react.js +3 -0
- package/dist/rsc/adapters/react.js.map +1 -0
- package/dist/rsc/adapters/solid.d.ts +84 -0
- package/dist/rsc/adapters/solid.js +3 -0
- package/dist/rsc/adapters/solid.js.map +1 -0
- package/dist/rsc/adapters/vue.d.ts +80 -0
- package/dist/rsc/adapters/vue.js +3 -0
- package/dist/rsc/adapters/vue.js.map +1 -0
- package/dist/rsc/boundaries.d.ts +182 -0
- package/dist/rsc/boundaries.js +3 -0
- package/dist/rsc/boundaries.js.map +1 -0
- package/dist/rsc/context.d.ts +201 -0
- package/dist/rsc/context.js +3 -0
- package/dist/rsc/context.js.map +1 -0
- package/dist/rsc/index.d.ts +20 -124
- package/dist/rsc/index.js +13 -1
- package/dist/rsc/legacy.d.ts +131 -0
- package/dist/rsc/legacy.js +3 -0
- package/dist/rsc/legacy.js.map +1 -0
- package/dist/rsc/payload.d.ts +262 -0
- package/dist/rsc/payload.js +3 -0
- package/dist/rsc/payload.js.map +1 -0
- package/dist/rsc/plugins/esbuild.d.ts +124 -0
- package/dist/rsc/plugins/esbuild.js +4 -0
- package/dist/rsc/plugins/esbuild.js.map +1 -0
- package/dist/rsc/plugins/index.d.ts +4 -0
- package/dist/rsc/plugins/index.js +6 -0
- package/dist/rsc/plugins/index.js.map +1 -0
- package/dist/rsc/plugins/rollup.d.ts +103 -0
- package/dist/rsc/plugins/rollup.js +4 -0
- package/dist/rsc/plugins/rollup.js.map +1 -0
- package/dist/rsc/renderer.d.ts +160 -0
- package/dist/rsc/renderer.js +5 -0
- package/dist/rsc/renderer.js.map +1 -0
- package/dist/rsc/stream.d.ts +129 -0
- package/dist/rsc/stream.js +3 -0
- package/dist/rsc/stream.js.map +1 -0
- package/dist/rsc/vite-plugin.d.ts +78 -0
- package/dist/rsc/vite-plugin.js +4 -0
- package/dist/rsc/vite-plugin.js.map +1 -0
- package/dist/streaming/index.js +1 -1
- package/dist/streaming/observability.js +2 -2
- package/dist/streaming/priority.js +1 -1
- package/package.json +180 -124
- package/dist/chunk-5KF3QQWZ.js.map +0 -1
- package/dist/chunk-6WSPUG5L.js.map +0 -1
- package/dist/chunk-I2B4WSHC.js.map +0 -1
- package/dist/chunk-OBNYNJB5.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/rsc/boundaries.ts"],"names":["exports"],"mappings":"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","file":"chunk-PDW5WCMW.js","sourcesContent":["/**\r\n * @flight-framework/core - Component Boundary Detection\r\n * \r\n * Sistema de detección de boundaries client/server para Server Components.\r\n * Soporta directivas 'use client' y 'use server' tanto a nivel de archivo\r\n * como inline en funciones.\r\n * \r\n * Filosofía Flight:\r\n * - Detección en build-time (no runtime overhead)\r\n * - Soporte para directivas estándar de React\r\n * - Extensible para otros frameworks\r\n * - Zero dependencies\r\n * \r\n * @module @flight-framework/core/rsc/boundaries\r\n */\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\n/**\r\n * Tipo de boundary de componente\r\n */\r\nexport type BoundaryType = 'server' | 'client' | 'shared';\r\n\r\n/**\r\n * Información de un boundary detectado\r\n */\r\nexport interface ComponentBoundary {\r\n /** Tipo de boundary */\r\n type: BoundaryType;\r\n /** ID del módulo (path relativo o absoluto) */\r\n moduleId: string;\r\n /** Nombre del export */\r\n exportName: string;\r\n /** Línea donde se detectó la directiva */\r\n line?: number;\r\n /** Es una directiva inline (dentro de función) */\r\n inline?: boolean;\r\n /** Metadata adicional */\r\n meta?: Record<string, unknown>;\r\n}\r\n\r\n/**\r\n * Resultado del análisis de un módulo\r\n */\r\nexport interface ModuleAnalysis {\r\n /** Path del módulo */\r\n moduleId: string;\r\n /** Directiva a nivel de archivo ('use client' o 'use server') */\r\n fileDirective?: 'client' | 'server';\r\n /** Boundaries detectados */\r\n boundaries: ComponentBoundary[];\r\n /** Exports que son Server Actions */\r\n serverActions: string[];\r\n /** Exports que son Client Components */\r\n clientComponents: string[];\r\n /** El módulo tiene componentes async */\r\n hasAsyncComponents: boolean;\r\n}\r\n\r\n/**\r\n * Referencia a un Client Component\r\n */\r\nexport interface ClientReference<T = unknown> {\r\n $$typeof: symbol;\r\n $$id: string;\r\n $$async: boolean;\r\n __flight_client: true;\r\n __flight_module: string;\r\n __flight_export: string;\r\n // Type-safe component signature\r\n (props: T extends (props: infer P) => unknown ? P : unknown): unknown;\r\n}\r\n\r\n/**\r\n * Referencia a una Server Function (Action)\r\n */\r\nexport interface ServerReference<T extends (...args: unknown[]) => unknown = (...args: unknown[]) => unknown> {\r\n $$typeof: symbol;\r\n $$id: string;\r\n $$bound: unknown[] | null;\r\n __flight_server: true;\r\n __flight_action: string;\r\n // Type-safe function signature\r\n (...args: Parameters<T>): Promise<ReturnType<T>>;\r\n}\r\n\r\n// Symbols for reference detection\r\nexport const CLIENT_REFERENCE = Symbol.for('flight.client.reference');\r\nexport const SERVER_REFERENCE = Symbol.for('flight.server.reference');\r\n\r\n// ============================================================================\r\n// Directive Detection\r\n// ============================================================================\r\n\r\n/**\r\n * Regex patterns para detección de directivas\r\n */\r\nconst PATTERNS = {\r\n // 'use client'; o \"use client\";\r\n USE_CLIENT: /^['\"]use client['\"];?\\s*$/,\r\n // 'use server'; o \"use server\";\r\n USE_SERVER: /^['\"]use server['\"];?\\s*$/,\r\n // Inline 'use server' dentro de función\r\n INLINE_USE_SERVER: /['\"]use server['\"];?/,\r\n // Detección de función async\r\n ASYNC_FUNCTION: /(?:^|\\s)async\\s+function\\s+(\\w+)/,\r\n // Detección de arrow function async\r\n ASYNC_ARROW: /(?:const|let|var)\\s+(\\w+)\\s*=\\s*async\\s*(?:\\([^)]*\\)|[^=])\\s*=>/,\r\n // Export statement\r\n EXPORT: /export\\s+(?:default\\s+)?(?:async\\s+)?(?:function|const|let|class)\\s+(\\w+)/g,\r\n // Export default\r\n EXPORT_DEFAULT: /export\\s+default\\s+/,\r\n} as const;\r\n\r\n/**\r\n * Detecta si un archivo tiene la directiva 'use client'\r\n */\r\nexport function hasUseClientDirective(source: string): boolean {\r\n const lines = source.trim().split('\\n');\r\n // Check first non-empty, non-comment line\r\n for (const line of lines) {\r\n const trimmed = line.trim();\r\n if (trimmed === '' || trimmed.startsWith('//') || trimmed.startsWith('/*')) {\r\n continue;\r\n }\r\n return PATTERNS.USE_CLIENT.test(trimmed);\r\n }\r\n return false;\r\n}\r\n\r\n/**\r\n * Detecta si un archivo tiene la directiva 'use server' a nivel de archivo\r\n */\r\nexport function hasUseServerDirective(source: string): boolean {\r\n const lines = source.trim().split('\\n');\r\n for (const line of lines) {\r\n const trimmed = line.trim();\r\n if (trimmed === '' || trimmed.startsWith('//') || trimmed.startsWith('/*')) {\r\n continue;\r\n }\r\n return PATTERNS.USE_SERVER.test(trimmed);\r\n }\r\n return false;\r\n}\r\n\r\n/**\r\n * Detecta el tipo de componente basado en directivas\r\n */\r\nexport function detectBoundaryType(source: string): BoundaryType {\r\n if (hasUseClientDirective(source)) return 'client';\r\n if (hasUseServerDirective(source)) return 'server';\r\n return 'shared';\r\n}\r\n\r\n/**\r\n * Detecta Server Actions inline en el código\r\n * Busca funciones que contienen 'use server' dentro de su cuerpo\r\n */\r\nexport function detectInlineServerActions(source: string): string[] {\r\n const actions: string[] = [];\r\n\r\n // Regex para encontrar funciones con 'use server' inline\r\n // Soporta:\r\n // - async function name() { 'use server'; ... }\r\n // - const name = async () => { 'use server'; ... }\r\n // - const name = async function() { 'use server'; ... }\r\n\r\n const functionPatterns = [\r\n // async function name() { 'use server';\r\n /async\\s+function\\s+(\\w+)\\s*\\([^)]*\\)\\s*\\{[\\s\\n]*['\"]use server['\"]/g,\r\n // const name = async () => { 'use server';\r\n /(?:const|let|var)\\s+(\\w+)\\s*=\\s*async\\s*\\([^)]*\\)\\s*=>\\s*\\{[\\s\\n]*['\"]use server['\"]/g,\r\n // const name = async function() { 'use server';\r\n /(?:const|let|var)\\s+(\\w+)\\s*=\\s*async\\s+function\\s*\\([^)]*\\)\\s*\\{[\\s\\n]*['\"]use server['\"]/g,\r\n ];\r\n\r\n for (const pattern of functionPatterns) {\r\n let match;\r\n while ((match = pattern.exec(source)) !== null) {\r\n if (match[1]) {\r\n actions.push(match[1]);\r\n }\r\n }\r\n }\r\n\r\n return [...new Set(actions)]; // Deduplicar\r\n}\r\n\r\n/**\r\n * Detecta componentes async (Server Components potenciales)\r\n */\r\nexport function detectAsyncComponents(source: string): string[] {\r\n const components: string[] = [];\r\n\r\n // Buscar exports de funciones async\r\n const patterns = [\r\n // export async function ComponentName\r\n /export\\s+(?:default\\s+)?async\\s+function\\s+(\\w+)/g,\r\n // export const ComponentName = async\r\n /export\\s+(?:const|let)\\s+(\\w+)\\s*=\\s*async/g,\r\n ];\r\n\r\n for (const pattern of patterns) {\r\n let match;\r\n while ((match = pattern.exec(source)) !== null) {\r\n if (match[1] && /^[A-Z]/.test(match[1])) { // PascalCase = component\r\n components.push(match[1]);\r\n }\r\n }\r\n }\r\n\r\n return [...new Set(components)];\r\n}\r\n\r\n/**\r\n * Análisis completo de un módulo\r\n */\r\nexport function analyzeModule(source: string, moduleId: string): ModuleAnalysis {\r\n const boundaries: ComponentBoundary[] = [];\r\n const serverActions: string[] = [];\r\n const clientComponents: string[] = [];\r\n\r\n // Detectar directiva a nivel de archivo\r\n let fileDirective: 'client' | 'server' | undefined;\r\n if (hasUseClientDirective(source)) {\r\n fileDirective = 'client';\r\n } else if (hasUseServerDirective(source)) {\r\n fileDirective = 'server';\r\n }\r\n\r\n // Si es 'use client', todo el módulo es client\r\n if (fileDirective === 'client') {\r\n boundaries.push({\r\n type: 'client',\r\n moduleId,\r\n exportName: '*',\r\n line: 1,\r\n });\r\n\r\n // Buscar todos los exports como client components\r\n const exports = extractExports(source);\r\n clientComponents.push(...exports);\r\n }\r\n\r\n // Si es 'use server' a nivel de archivo, todos los exports son server actions\r\n if (fileDirective === 'server') {\r\n boundaries.push({\r\n type: 'server',\r\n moduleId,\r\n exportName: '*',\r\n line: 1,\r\n });\r\n\r\n const exports = extractExports(source);\r\n serverActions.push(...exports);\r\n }\r\n\r\n // Detectar server actions inline (incluso en módulos sin directiva de archivo)\r\n if (fileDirective !== 'client') {\r\n const inlineActions = detectInlineServerActions(source);\r\n for (const action of inlineActions) {\r\n if (!serverActions.includes(action)) {\r\n serverActions.push(action);\r\n boundaries.push({\r\n type: 'server',\r\n moduleId,\r\n exportName: action,\r\n inline: true,\r\n });\r\n }\r\n }\r\n }\r\n\r\n // Detectar async components\r\n const asyncComponents = detectAsyncComponents(source);\r\n\r\n return {\r\n moduleId,\r\n fileDirective,\r\n boundaries,\r\n serverActions,\r\n clientComponents,\r\n hasAsyncComponents: asyncComponents.length > 0,\r\n };\r\n}\r\n\r\n/**\r\n * Extrae nombres de exports de un módulo\r\n */\r\nfunction extractExports(source: string): string[] {\r\n const exports: string[] = [];\r\n\r\n const patterns = [\r\n // export function/const/let/class Name\r\n /export\\s+(?:async\\s+)?(?:function|const|let|var|class)\\s+(\\w+)/g,\r\n // export default function Name\r\n /export\\s+default\\s+(?:async\\s+)?(?:function|class)\\s+(\\w+)/g,\r\n // export { name }\r\n /export\\s*\\{\\s*([^}]+)\\s*\\}/g,\r\n ];\r\n\r\n for (const pattern of patterns) {\r\n let match;\r\n while ((match = pattern.exec(source)) !== null) {\r\n if (match[1]) {\r\n // Handle export { a, b, c }\r\n const names = match[1].split(',').map(n => n.trim().split(/\\s+as\\s+/).pop()!.trim());\r\n exports.push(...names.filter(n => n && n !== 'default'));\r\n }\r\n }\r\n }\r\n\r\n // Check for export default\r\n if (/export\\s+default\\s+/.test(source)) {\r\n exports.push('default');\r\n }\r\n\r\n return [...new Set(exports)];\r\n}\r\n\r\n// ============================================================================\r\n// Reference Creation\r\n// ============================================================================\r\n\r\n/**\r\n * Crea una referencia a un Client Component\r\n * Para uso en build-time (transformación de código)\r\n */\r\nexport function createClientReference<T>(\r\n moduleId: string,\r\n exportName: string,\r\n isAsync = false\r\n): ClientReference<T> {\r\n const id = `${moduleId}#${exportName}`;\r\n\r\n const reference = function (_props: unknown): never {\r\n throw new Error(\r\n `Attempted to call Client Component \"${exportName}\" from \"${moduleId}\" on the server. ` +\r\n 'Client Components can only be rendered on the client.'\r\n );\r\n } as unknown as ClientReference<T>;\r\n\r\n reference.$$typeof = CLIENT_REFERENCE;\r\n reference.$$id = id;\r\n reference.$$async = isAsync;\r\n reference.__flight_client = true;\r\n reference.__flight_module = moduleId;\r\n reference.__flight_export = exportName;\r\n\r\n return reference;\r\n}\r\n\r\n/**\r\n * Crea una referencia a una Server Function (Action)\r\n * Para uso en build-time (transformación de código)\r\n */\r\nexport function createServerReference<T extends (...args: unknown[]) => unknown>(\r\n actionId: string,\r\n boundArgs?: unknown[]\r\n): ServerReference<T> {\r\n const reference = async function (..._args: unknown[]) {\r\n // Esta función será reemplazada por el bundler plugin\r\n // para hacer una llamada RPC al servidor\r\n throw new Error(\r\n `Server Action \"${actionId}\" was called but no RPC handler is configured. ` +\r\n 'Make sure the Flight bundler plugin is properly configured.'\r\n );\r\n } as unknown as ServerReference<T>;\r\n\r\n reference.$$typeof = SERVER_REFERENCE;\r\n reference.$$id = actionId;\r\n reference.$$bound = boundArgs ?? null;\r\n reference.__flight_server = true;\r\n reference.__flight_action = actionId;\r\n\r\n return reference;\r\n}\r\n\r\n/**\r\n * Verifica si un valor es una Client Reference\r\n */\r\nexport function isClientReference(value: unknown): value is ClientReference {\r\n return (\r\n typeof value === 'function' &&\r\n '$$typeof' in value &&\r\n value.$$typeof === CLIENT_REFERENCE\r\n );\r\n}\r\n\r\n/**\r\n * Verifica si un valor es una Server Reference\r\n */\r\nexport function isServerReference(value: unknown): value is ServerReference {\r\n return (\r\n typeof value === 'function' &&\r\n '$$typeof' in value &&\r\n value.$$typeof === SERVER_REFERENCE\r\n );\r\n}\r\n\r\n/**\r\n * Obtiene el ID de una referencia\r\n */\r\nexport function getReferenceId(ref: ClientReference | ServerReference): string {\r\n return ref.$$id;\r\n}\r\n\r\n// ============================================================================\r\n// Boundary Registry\r\n// ============================================================================\r\n\r\n/**\r\n * Registry de boundaries para uso en runtime\r\n */\r\nexport class BoundaryRegistry {\r\n private clientModules = new Map<string, () => Promise<unknown>>();\r\n private serverActions = new Map<string, (...args: unknown[]) => Promise<unknown>>();\r\n\r\n /**\r\n * Registra un Client Component\r\n */\r\n registerClient(id: string, loader: () => Promise<unknown>): void {\r\n this.clientModules.set(id, loader);\r\n }\r\n\r\n /**\r\n * Registra una Server Action\r\n */\r\n registerServer(id: string, action: (...args: unknown[]) => Promise<unknown>): void {\r\n this.serverActions.set(id, action);\r\n }\r\n\r\n /**\r\n * Obtiene un Client Component loader\r\n */\r\n getClient(id: string): (() => Promise<unknown>) | undefined {\r\n return this.clientModules.get(id);\r\n }\r\n\r\n /**\r\n * Obtiene una Server Action\r\n */\r\n getServer(id: string): ((...args: unknown[]) => Promise<unknown>) | undefined {\r\n return this.serverActions.get(id);\r\n }\r\n\r\n /**\r\n * Verifica si un client module está registrado\r\n */\r\n hasClient(id: string): boolean {\r\n return this.clientModules.has(id);\r\n }\r\n\r\n /**\r\n * Verifica si una server action está registrada\r\n */\r\n hasServer(id: string): boolean {\r\n return this.serverActions.has(id);\r\n }\r\n\r\n /**\r\n * Obtiene todos los IDs de client modules\r\n */\r\n getClientIds(): string[] {\r\n return [...this.clientModules.keys()];\r\n }\r\n\r\n /**\r\n * Obtiene todos los IDs de server actions\r\n */\r\n getServerIds(): string[] {\r\n return [...this.serverActions.keys()];\r\n }\r\n\r\n /**\r\n * Limpia el registry\r\n */\r\n clear(): void {\r\n this.clientModules.clear();\r\n this.serverActions.clear();\r\n }\r\n}\r\n\r\n/**\r\n * Registry global (singleton)\r\n */\r\nexport const globalRegistry = new BoundaryRegistry();\r\n\r\n/**\r\n * Registra un Client Component en el registry global\r\n */\r\nexport function registerClientComponent(id: string, loader: () => Promise<unknown>): void {\r\n globalRegistry.registerClient(id, loader);\r\n}\r\n\r\n/**\r\n * Registra una Server Action en el registry global\r\n */\r\nexport function registerServerAction(id: string, action: (...args: unknown[]) => Promise<unknown>): void {\r\n globalRegistry.registerServer(id, action);\r\n}\r\n"]}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
// src/rsc/stream.ts
|
|
2
|
+
function createFlightStream(chunks, options = {}) {
|
|
3
|
+
const { onChunk, onComplete, onError } = options;
|
|
4
|
+
const encoder = new TextEncoder();
|
|
5
|
+
return new ReadableStream({
|
|
6
|
+
async start(controller) {
|
|
7
|
+
try {
|
|
8
|
+
for await (const chunk of chunks) {
|
|
9
|
+
const line = JSON.stringify(chunk) + "\n";
|
|
10
|
+
controller.enqueue(encoder.encode(line));
|
|
11
|
+
onChunk?.(chunk);
|
|
12
|
+
}
|
|
13
|
+
controller.close();
|
|
14
|
+
onComplete?.();
|
|
15
|
+
} catch (error) {
|
|
16
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
17
|
+
onError?.(err);
|
|
18
|
+
controller.error(err);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function createFlightStreamFromArray(chunks, options = {}) {
|
|
24
|
+
async function* generator() {
|
|
25
|
+
for (const chunk of chunks) {
|
|
26
|
+
yield chunk;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return createFlightStream(generator(), options);
|
|
30
|
+
}
|
|
31
|
+
async function* parseFlightStream(stream) {
|
|
32
|
+
const reader = stream.getReader();
|
|
33
|
+
const decoder = new TextDecoder();
|
|
34
|
+
let buffer = "";
|
|
35
|
+
try {
|
|
36
|
+
while (true) {
|
|
37
|
+
const { done, value } = await reader.read();
|
|
38
|
+
if (done) {
|
|
39
|
+
if (buffer.trim()) {
|
|
40
|
+
yield parseChunk(buffer);
|
|
41
|
+
}
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
buffer += decoder.decode(value, { stream: true });
|
|
45
|
+
const lines = buffer.split("\n");
|
|
46
|
+
buffer = lines.pop() ?? "";
|
|
47
|
+
for (const line of lines) {
|
|
48
|
+
if (line.trim()) {
|
|
49
|
+
yield parseChunk(line);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
} finally {
|
|
54
|
+
reader.releaseLock();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function parseFlightStreamSync(data) {
|
|
58
|
+
const chunks = [];
|
|
59
|
+
for (const line of data.split("\n")) {
|
|
60
|
+
if (line.trim()) {
|
|
61
|
+
chunks.push(parseChunk(line));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return chunks;
|
|
65
|
+
}
|
|
66
|
+
function parseChunk(line) {
|
|
67
|
+
try {
|
|
68
|
+
return JSON.parse(line);
|
|
69
|
+
} catch {
|
|
70
|
+
throw new FlightStreamError(
|
|
71
|
+
`Failed to parse Flight chunk: ${line.slice(0, 100)}${line.length > 100 ? "..." : ""}`,
|
|
72
|
+
line
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function consumeFlightStream(stream) {
|
|
77
|
+
const startTime = Date.now();
|
|
78
|
+
const chunks = [];
|
|
79
|
+
const errors = [];
|
|
80
|
+
let root;
|
|
81
|
+
for await (const chunk of parseFlightStream(stream)) {
|
|
82
|
+
chunks.push(chunk);
|
|
83
|
+
if (chunk.type === "S" && chunk.id === "root") {
|
|
84
|
+
root = chunk;
|
|
85
|
+
}
|
|
86
|
+
if (chunk.type === "E") {
|
|
87
|
+
errors.push(chunk);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
chunks,
|
|
92
|
+
root,
|
|
93
|
+
errors,
|
|
94
|
+
duration: Date.now() - startTime
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function createFlightResponse(chunks, options = {}) {
|
|
98
|
+
const { status = 200, headers = {} } = options;
|
|
99
|
+
return new Response(createFlightStream(chunks), {
|
|
100
|
+
status,
|
|
101
|
+
headers: {
|
|
102
|
+
"Content-Type": "text/x-flight",
|
|
103
|
+
"Transfer-Encoding": "chunked",
|
|
104
|
+
"Cache-Control": "no-cache, no-store",
|
|
105
|
+
"X-Content-Type-Options": "nosniff",
|
|
106
|
+
...headers
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function createHybridResponse(htmlStream, flightChunks, options = {}) {
|
|
111
|
+
const { status = 200, headers = {} } = options;
|
|
112
|
+
const encoder = new TextEncoder();
|
|
113
|
+
const flightPayload = JSON.stringify(flightChunks);
|
|
114
|
+
const inlineScript = `<script type="text/x-flight">${escapeScript(flightPayload)}</script>`;
|
|
115
|
+
const transform = new TransformStream({
|
|
116
|
+
transform(chunk, controller) {
|
|
117
|
+
controller.enqueue(chunk);
|
|
118
|
+
},
|
|
119
|
+
flush(controller) {
|
|
120
|
+
controller.enqueue(encoder.encode(inlineScript));
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
const combinedStream = htmlStream.pipeThrough(transform);
|
|
124
|
+
return new Response(combinedStream, {
|
|
125
|
+
status,
|
|
126
|
+
headers: {
|
|
127
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
128
|
+
"Transfer-Encoding": "chunked",
|
|
129
|
+
"X-Content-Type-Options": "nosniff",
|
|
130
|
+
...headers
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
function escapeScript(content) {
|
|
135
|
+
return content.replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026");
|
|
136
|
+
}
|
|
137
|
+
function createStreamController() {
|
|
138
|
+
const encoder = new TextEncoder();
|
|
139
|
+
let controller;
|
|
140
|
+
const stream = new ReadableStream({
|
|
141
|
+
start(ctrl) {
|
|
142
|
+
controller = ctrl;
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
return {
|
|
146
|
+
stream,
|
|
147
|
+
enqueue(chunk) {
|
|
148
|
+
const line = JSON.stringify(chunk) + "\n";
|
|
149
|
+
controller.enqueue(encoder.encode(line));
|
|
150
|
+
},
|
|
151
|
+
error(err) {
|
|
152
|
+
controller.error(err);
|
|
153
|
+
},
|
|
154
|
+
close() {
|
|
155
|
+
controller.close();
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function mergeFlightStreams(...streams) {
|
|
160
|
+
const readers = streams.map((s) => s.getReader());
|
|
161
|
+
return new ReadableStream({
|
|
162
|
+
async pull(controller) {
|
|
163
|
+
const promises = readers.map(async (reader, index2) => {
|
|
164
|
+
const result2 = await reader.read();
|
|
165
|
+
return { result: result2, index: index2 };
|
|
166
|
+
});
|
|
167
|
+
const { result, index } = await Promise.race(promises);
|
|
168
|
+
if (result.done) {
|
|
169
|
+
readers.splice(index, 1);
|
|
170
|
+
if (readers.length === 0) {
|
|
171
|
+
controller.close();
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
controller.enqueue(result.value);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
function transformFlightStream(stream, transform) {
|
|
180
|
+
const encoder = new TextEncoder();
|
|
181
|
+
return new ReadableStream({
|
|
182
|
+
async start(controller) {
|
|
183
|
+
try {
|
|
184
|
+
for await (const chunk of parseFlightStream(stream)) {
|
|
185
|
+
const transformed = await transform(chunk);
|
|
186
|
+
if (transformed) {
|
|
187
|
+
const line = JSON.stringify(transformed) + "\n";
|
|
188
|
+
controller.enqueue(encoder.encode(line));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
controller.close();
|
|
192
|
+
} catch (error) {
|
|
193
|
+
controller.error(error);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
function prettyPrintChunks(chunks) {
|
|
199
|
+
const lines = [];
|
|
200
|
+
for (const chunk of chunks) {
|
|
201
|
+
const prefix = getChunkTypeLabel(chunk.type);
|
|
202
|
+
const id = "id" in chunk ? chunk.id : "boundary" in chunk ? chunk.boundary : "?";
|
|
203
|
+
lines.push(`[${prefix}] ${id}`);
|
|
204
|
+
if (chunk.type === "S" && chunk.tree) {
|
|
205
|
+
lines.push(` \u2514\u2500 Tree: ${prettyPrintElement(chunk.tree)}`);
|
|
206
|
+
}
|
|
207
|
+
if (chunk.type === "C") {
|
|
208
|
+
lines.push(` \u2514\u2500 Module: ${chunk.module}#${chunk.export}`);
|
|
209
|
+
}
|
|
210
|
+
if (chunk.type === "E") {
|
|
211
|
+
lines.push(` \u2514\u2500 Error: ${chunk.message}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return lines.join("\n");
|
|
215
|
+
}
|
|
216
|
+
function getChunkTypeLabel(type) {
|
|
217
|
+
switch (type) {
|
|
218
|
+
case "S":
|
|
219
|
+
return "SERVER";
|
|
220
|
+
case "C":
|
|
221
|
+
return "CLIENT";
|
|
222
|
+
case "A":
|
|
223
|
+
return "ACTION";
|
|
224
|
+
case "E":
|
|
225
|
+
return "ERROR ";
|
|
226
|
+
case "H":
|
|
227
|
+
return "HINT ";
|
|
228
|
+
case "M":
|
|
229
|
+
return "META ";
|
|
230
|
+
default:
|
|
231
|
+
return "UNKNWN";
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function prettyPrintElement(element) {
|
|
235
|
+
if (!element || typeof element !== "object") return String(element);
|
|
236
|
+
const el = element;
|
|
237
|
+
if (el.$$type === "host") return `<${el.tag}>...`;
|
|
238
|
+
if (el.$$type === "text") return '"text"';
|
|
239
|
+
if (el.$$type === "fragment") return "<>...</>";
|
|
240
|
+
if (el.$$type === "suspense") return "<Suspense>...</Suspense>";
|
|
241
|
+
if (el.$$type === "client") return `<ClientRef:${el.ref}>`;
|
|
242
|
+
if (el.$$type === "lazy") return "<Lazy...>";
|
|
243
|
+
if (el.$$type === "null") return "null";
|
|
244
|
+
return JSON.stringify(element).slice(0, 50);
|
|
245
|
+
}
|
|
246
|
+
var FlightStreamError = class extends Error {
|
|
247
|
+
constructor(message, rawData) {
|
|
248
|
+
super(message);
|
|
249
|
+
this.rawData = rawData;
|
|
250
|
+
this.name = "FlightStreamError";
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
export { FlightStreamError, consumeFlightStream, createFlightResponse, createFlightStream, createFlightStreamFromArray, createHybridResponse, createStreamController, mergeFlightStreams, parseFlightStream, parseFlightStreamSync, prettyPrintChunks, transformFlightStream };
|
|
255
|
+
//# sourceMappingURL=chunk-PVUMB632.js.map
|
|
256
|
+
//# sourceMappingURL=chunk-PVUMB632.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/rsc/stream.ts"],"names":["index","result"],"mappings":";AAyEO,SAAS,kBAAA,CACZ,MAAA,EACA,OAAA,GAA+B,EAAC,EACN;AAC1B,EAAA,MAAM,EAAE,OAAA,EAAS,UAAA,EAAY,OAAA,EAAQ,GAAI,OAAA;AACzC,EAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAEhC,EAAA,OAAO,IAAI,cAAA,CAAe;AAAA,IACtB,MAAM,MAAM,UAAA,EAAY;AACpB,MAAA,IAAI;AACA,QAAA,WAAA,MAAiB,SAAS,MAAA,EAAQ;AAE9B,UAAA,MAAM,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,KAAK,CAAA,GAAI,IAAA;AACrC,UAAA,UAAA,CAAW,OAAA,CAAQ,OAAA,CAAQ,MAAA,CAAO,IAAI,CAAC,CAAA;AAEvC,UAAA,OAAA,GAAU,KAAK,CAAA;AAAA,QACnB;AAEA,QAAA,UAAA,CAAW,KAAA,EAAM;AACjB,QAAA,UAAA,IAAa;AAAA,MACjB,SAAS,KAAA,EAAO;AACZ,QAAA,MAAM,GAAA,GAAM,iBAAiB,KAAA,GAAQ,KAAA,GAAQ,IAAI,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AACpE,QAAA,OAAA,GAAU,GAAG,CAAA;AACb,QAAA,UAAA,CAAW,MAAM,GAAG,CAAA;AAAA,MACxB;AAAA,IACJ;AAAA,GACH,CAAA;AACL;AAKO,SAAS,2BAAA,CACZ,MAAA,EACA,OAAA,GAA+B,EAAC,EACN;AAC1B,EAAA,gBAAgB,SAAA,GAAY;AACxB,IAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AACxB,MAAA,MAAM,KAAA;AAAA,IACV;AAAA,EACJ;AACA,EAAA,OAAO,kBAAA,CAAmB,SAAA,EAAU,EAAG,OAAO,CAAA;AAClD;AAoBA,gBAAuB,kBACnB,MAAA,EAC2B;AAC3B,EAAA,MAAM,MAAA,GAAS,OAAO,SAAA,EAAU;AAChC,EAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAChC,EAAA,IAAI,MAAA,GAAS,EAAA;AAEb,EAAA,IAAI;AACA,IAAA,OAAO,IAAA,EAAM;AACT,MAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAM,GAAI,MAAM,OAAO,IAAA,EAAK;AAE1C,MAAA,IAAI,IAAA,EAAM;AAEN,QAAA,IAAI,MAAA,CAAO,MAAK,EAAG;AACf,UAAA,MAAM,WAAW,MAAM,CAAA;AAAA,QAC3B;AACA,QAAA;AAAA,MACJ;AAEA,MAAA,MAAA,IAAU,QAAQ,MAAA,CAAO,KAAA,EAAO,EAAE,MAAA,EAAQ,MAAM,CAAA;AAGhD,MAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,KAAA,CAAM,IAAI,CAAA;AAC/B,MAAA,MAAA,GAAS,KAAA,CAAM,KAAI,IAAK,EAAA;AAExB,MAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACtB,QAAA,IAAI,IAAA,CAAK,MAAK,EAAG;AACb,UAAA,MAAM,WAAW,IAAI,CAAA;AAAA,QACzB;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ,CAAA,SAAE;AACE,IAAA,MAAA,CAAO,WAAA,EAAY;AAAA,EACvB;AACJ;AAKO,SAAS,sBAAsB,IAAA,EAA6B;AAC/D,EAAA,MAAM,SAAwB,EAAC;AAE/B,EAAA,KAAA,MAAW,IAAA,IAAQ,IAAA,CAAK,KAAA,CAAM,IAAI,CAAA,EAAG;AACjC,IAAA,IAAI,IAAA,CAAK,MAAK,EAAG;AACb,MAAA,MAAA,CAAO,IAAA,CAAK,UAAA,CAAW,IAAI,CAAC,CAAA;AAAA,IAChC;AAAA,EACJ;AAEA,EAAA,OAAO,MAAA;AACX;AAKA,SAAS,WAAW,IAAA,EAA2B;AAC3C,EAAA,IAAI;AACA,IAAA,OAAO,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,EAC1B,CAAA,CAAA,MAAQ;AACJ,IAAA,MAAM,IAAI,iBAAA;AAAA,MACN,CAAA,8BAAA,EAAiC,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,GAAG,CAAC,CAAA,EAAG,IAAA,CAAK,MAAA,GAAS,GAAA,GAAM,KAAA,GAAQ,EAAE,CAAA,CAAA;AAAA,MACpF;AAAA,KACJ;AAAA,EACJ;AACJ;AAKA,eAAsB,oBAClB,MAAA,EAC2B;AAC3B,EAAA,MAAM,SAAA,GAAY,KAAK,GAAA,EAAI;AAC3B,EAAA,MAAM,SAAwB,EAAC;AAC/B,EAAA,MAAM,SAA+B,EAAC;AACtC,EAAA,IAAI,IAAA;AAEJ,EAAA,WAAA,MAAiB,KAAA,IAAS,iBAAA,CAAkB,MAAM,CAAA,EAAG;AACjD,IAAA,MAAA,CAAO,KAAK,KAAK,CAAA;AAEjB,IAAA,IAAI,KAAA,CAAM,IAAA,KAAS,GAAA,IAAO,KAAA,CAAM,OAAO,MAAA,EAAQ;AAC3C,MAAA,IAAA,GAAO,KAAA;AAAA,IACX;AAEA,IAAA,IAAI,KAAA,CAAM,SAAS,GAAA,EAAK;AACpB,MAAA,MAAA,CAAO,KAAK,KAAK,CAAA;AAAA,IACrB;AAAA,EACJ;AAEA,EAAA,OAAO;AAAA,IACH,MAAA;AAAA,IACA,IAAA;AAAA,IACA,MAAA;AAAA,IACA,QAAA,EAAU,IAAA,CAAK,GAAA,EAAI,GAAI;AAAA,GAC3B;AACJ;AASO,SAAS,oBAAA,CACZ,MAAA,EACA,OAAA,GAAiC,EAAC,EAC1B;AACR,EAAA,MAAM,EAAE,MAAA,GAAS,GAAA,EAAK,OAAA,GAAU,IAAG,GAAI,OAAA;AAEvC,EAAA,OAAO,IAAI,QAAA,CAAS,kBAAA,CAAmB,MAAM,CAAA,EAAG;AAAA,IAC5C,MAAA;AAAA,IACA,OAAA,EAAS;AAAA,MACL,cAAA,EAAgB,eAAA;AAAA,MAChB,mBAAA,EAAqB,SAAA;AAAA,MACrB,eAAA,EAAiB,oBAAA;AAAA,MACjB,wBAAA,EAA0B,SAAA;AAAA,MAC1B,GAAG;AAAA;AACP,GACH,CAAA;AACL;AAUO,SAAS,oBAAA,CACZ,UAAA,EACA,YAAA,EACA,OAAA,GAAiC,EAAC,EAC1B;AACR,EAAA,MAAM,EAAE,MAAA,GAAS,GAAA,EAAK,OAAA,GAAU,IAAG,GAAI,OAAA;AAGvC,EAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAChC,EAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,SAAA,CAAU,YAAY,CAAA;AACjD,EAAA,MAAM,YAAA,GAAe,CAAA,6BAAA,EAAgC,YAAA,CAAa,aAAa,CAAC,CAAA,SAAA,CAAA;AAGhF,EAAA,MAAM,SAAA,GAAY,IAAI,eAAA,CAAwC;AAAA,IAC1D,SAAA,CAAU,OAAO,UAAA,EAAY;AACzB,MAAA,UAAA,CAAW,QAAQ,KAAK,CAAA;AAAA,IAC5B,CAAA;AAAA,IACA,MAAM,UAAA,EAAY;AACd,MAAA,UAAA,CAAW,OAAA,CAAQ,OAAA,CAAQ,MAAA,CAAO,YAAY,CAAC,CAAA;AAAA,IACnD;AAAA,GACH,CAAA;AAED,EAAA,MAAM,cAAA,GAAiB,UAAA,CAAW,WAAA,CAAY,SAAS,CAAA;AAEvD,EAAA,OAAO,IAAI,SAAS,cAAA,EAAgB;AAAA,IAChC,MAAA;AAAA,IACA,OAAA,EAAS;AAAA,MACL,cAAA,EAAgB,0BAAA;AAAA,MAChB,mBAAA,EAAqB,SAAA;AAAA,MACrB,wBAAA,EAA0B,SAAA;AAAA,MAC1B,GAAG;AAAA;AACP,GACH,CAAA;AACL;AAKA,SAAS,aAAa,OAAA,EAAyB;AAC3C,EAAA,OAAO,OAAA,CACF,OAAA,CAAQ,IAAA,EAAM,SAAS,CAAA,CACvB,OAAA,CAAQ,IAAA,EAAM,SAAS,CAAA,CACvB,OAAA,CAAQ,IAAA,EAAM,SAAS,CAAA;AAChC;AASO,SAAS,sBAAA,GAKd;AACE,EAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAChC,EAAA,IAAI,UAAA;AAEJ,EAAA,MAAM,MAAA,GAAS,IAAI,cAAA,CAA2B;AAAA,IAC1C,MAAM,IAAA,EAAM;AACR,MAAA,UAAA,GAAa,IAAA;AAAA,IACjB;AAAA,GACH,CAAA;AAED,EAAA,OAAO;AAAA,IACH,MAAA;AAAA,IACA,QAAQ,KAAA,EAAoB;AACxB,MAAA,MAAM,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,KAAK,CAAA,GAAI,IAAA;AACrC,MAAA,UAAA,CAAW,OAAA,CAAQ,OAAA,CAAQ,MAAA,CAAO,IAAI,CAAC,CAAA;AAAA,IAC3C,CAAA;AAAA,IACA,MAAM,GAAA,EAAY;AACd,MAAA,UAAA,CAAW,MAAM,GAAG,CAAA;AAAA,IACxB,CAAA;AAAA,IACA,KAAA,GAAQ;AACJ,MAAA,UAAA,CAAW,KAAA,EAAM;AAAA,IACrB;AAAA,GACJ;AACJ;AAKO,SAAS,sBACT,OAAA,EACuB;AAC1B,EAAA,MAAM,UAAU,OAAA,CAAQ,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,WAAW,CAAA;AAE9C,EAAA,OAAO,IAAI,cAAA,CAAe;AAAA,IACtB,MAAM,KAAK,UAAA,EAAY;AAEnB,MAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,GAAA,CAAI,OAAO,QAAQA,MAAAA,KAAU;AAClD,QAAA,MAAMC,OAAAA,GAAS,MAAM,MAAA,CAAO,IAAA,EAAK;AACjC,QAAA,OAAO,EAAE,MAAA,EAAAA,OAAAA,EAAQ,KAAA,EAAAD,MAAAA,EAAM;AAAA,MAC3B,CAAC,CAAA;AAED,MAAA,MAAM,EAAE,MAAA,EAAQ,KAAA,KAAU,MAAM,OAAA,CAAQ,KAAK,QAAQ,CAAA;AAErD,MAAA,IAAI,OAAO,IAAA,EAAM;AAEb,QAAA,OAAA,CAAQ,MAAA,CAAO,OAAO,CAAC,CAAA;AAEvB,QAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AACtB,UAAA,UAAA,CAAW,KAAA,EAAM;AAAA,QACrB;AAAA,MACJ,CAAA,MAAO;AACH,QAAA,UAAA,CAAW,OAAA,CAAQ,OAAO,KAAK,CAAA;AAAA,MACnC;AAAA,IACJ;AAAA,GACH,CAAA;AACL;AAKO,SAAS,qBAAA,CACZ,QACA,SAAA,EAC0B;AAC1B,EAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAEhC,EAAA,OAAO,IAAI,cAAA,CAAe;AAAA,IACtB,MAAM,MAAM,UAAA,EAAY;AACpB,MAAA,IAAI;AACA,QAAA,WAAA,MAAiB,KAAA,IAAS,iBAAA,CAAkB,MAAM,CAAA,EAAG;AACjD,UAAA,MAAM,WAAA,GAAc,MAAM,SAAA,CAAU,KAAK,CAAA;AACzC,UAAA,IAAI,WAAA,EAAa;AACb,YAAA,MAAM,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,WAAW,CAAA,GAAI,IAAA;AAC3C,YAAA,UAAA,CAAW,OAAA,CAAQ,OAAA,CAAQ,MAAA,CAAO,IAAI,CAAC,CAAA;AAAA,UAC3C;AAAA,QACJ;AACA,QAAA,UAAA,CAAW,KAAA,EAAM;AAAA,MACrB,SAAS,KAAA,EAAO;AACZ,QAAA,UAAA,CAAW,MAAM,KAAK,CAAA;AAAA,MAC1B;AAAA,IACJ;AAAA,GACH,CAAA;AACL;AASO,SAAS,kBAAkB,MAAA,EAA+B;AAC7D,EAAA,MAAM,QAAkB,EAAC;AAEzB,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AACxB,IAAA,MAAM,MAAA,GAAS,iBAAA,CAAkB,KAAA,CAAM,IAAI,CAAA;AAC3C,IAAA,MAAM,EAAA,GAAK,QAAQ,KAAA,GAAQ,KAAA,CAAM,KAAK,UAAA,IAAc,KAAA,GAAQ,MAAM,QAAA,GAAW,GAAA;AAE7E,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,CAAA,EAAI,MAAM,CAAA,EAAA,EAAK,EAAE,CAAA,CAAE,CAAA;AAE9B,IAAA,IAAI,KAAA,CAAM,IAAA,KAAS,GAAA,IAAO,KAAA,CAAM,IAAA,EAAM;AAClC,MAAA,KAAA,CAAM,KAAK,CAAA,uBAAA,EAAgB,kBAAA,CAAmB,KAAA,CAAM,IAAI,CAAC,CAAA,CAAE,CAAA;AAAA,IAC/D;AACA,IAAA,IAAI,KAAA,CAAM,SAAS,GAAA,EAAK;AACpB,MAAA,KAAA,CAAM,KAAK,CAAA,yBAAA,EAAkB,KAAA,CAAM,MAAM,CAAA,CAAA,EAAI,KAAA,CAAM,MAAM,CAAA,CAAE,CAAA;AAAA,IAC/D;AACA,IAAA,IAAI,KAAA,CAAM,SAAS,GAAA,EAAK;AACpB,MAAA,KAAA,CAAM,IAAA,CAAK,CAAA,wBAAA,EAAiB,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAAA,IAC/C;AAAA,EACJ;AAEA,EAAA,OAAO,KAAA,CAAM,KAAK,IAAI,CAAA;AAC1B;AAEA,SAAS,kBAAkB,IAAA,EAAmC;AAC1D,EAAA,QAAQ,IAAA;AAAM,IACV,KAAK,GAAA;AAAK,MAAA,OAAO,QAAA;AAAA,IACjB,KAAK,GAAA;AAAK,MAAA,OAAO,QAAA;AAAA,IACjB,KAAK,GAAA;AAAK,MAAA,OAAO,QAAA;AAAA,IACjB,KAAK,GAAA;AAAK,MAAA,OAAO,QAAA;AAAA,IACjB,KAAK,GAAA;AAAK,MAAA,OAAO,QAAA;AAAA,IACjB,KAAK,GAAA;AAAK,MAAA,OAAO,QAAA;AAAA,IACjB;AAAS,MAAA,OAAO,QAAA;AAAA;AAExB;AAEA,SAAS,mBAAmB,OAAA,EAA0B;AAClD,EAAA,IAAI,CAAC,OAAA,IAAW,OAAO,YAAY,QAAA,EAAU,OAAO,OAAO,OAAO,CAAA;AAElE,EAAA,MAAM,EAAA,GAAK,OAAA;AAEX,EAAA,IAAI,GAAG,MAAA,KAAW,MAAA,EAAQ,OAAO,CAAA,CAAA,EAAI,GAAG,GAAG,CAAA,IAAA,CAAA;AAC3C,EAAA,IAAI,EAAA,CAAG,MAAA,KAAW,MAAA,EAAQ,OAAO,QAAA;AACjC,EAAA,IAAI,EAAA,CAAG,MAAA,KAAW,UAAA,EAAY,OAAO,UAAA;AACrC,EAAA,IAAI,EAAA,CAAG,MAAA,KAAW,UAAA,EAAY,OAAO,0BAAA;AACrC,EAAA,IAAI,GAAG,MAAA,KAAW,QAAA,EAAU,OAAO,CAAA,WAAA,EAAc,GAAG,GAAG,CAAA,CAAA,CAAA;AACvD,EAAA,IAAI,EAAA,CAAG,MAAA,KAAW,MAAA,EAAQ,OAAO,WAAA;AACjC,EAAA,IAAI,EAAA,CAAG,MAAA,KAAW,MAAA,EAAQ,OAAO,MAAA;AAEjC,EAAA,OAAO,KAAK,SAAA,CAAU,OAAO,CAAA,CAAE,KAAA,CAAM,GAAG,EAAE,CAAA;AAC9C;AASO,IAAM,iBAAA,GAAN,cAAgC,KAAA,CAAM;AAAA,EACzC,WAAA,CACI,SACgB,OAAA,EAClB;AACE,IAAA,KAAA,CAAM,OAAO,CAAA;AAFG,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAGhB,IAAA,IAAA,CAAK,IAAA,GAAO,mBAAA;AAAA,EAChB;AACJ","file":"chunk-PVUMB632.js","sourcesContent":["/**\r\n * @flight-framework/core - Flight Stream\r\n * \r\n * Protocolo de streaming para Server Components.\r\n * Usa NDJSON (Newline Delimited JSON) para máxima debuggabilidad.\r\n * \r\n * Filosofía Flight:\r\n * - Formato abierto y documentado\r\n * - Works con cualquier runtime (Node, Deno, Bun, Edge)\r\n * - Fácil de debuggear (text-based)\r\n * - Sin dependencias\r\n * \r\n * @module @flight-framework/core/rsc/stream\r\n */\r\n\r\nimport type {\r\n FlightChunk,\r\n ServerComponentChunk,\r\n ErrorBoundaryChunk,\r\n} from './payload.js';\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\n/**\r\n * Opciones para crear un FlightStream\r\n */\r\nexport interface FlightStreamOptions {\r\n /** Incluir timestamps en chunks */\r\n timestamps?: boolean;\r\n /** Prefix para IDs generados */\r\n idPrefix?: string;\r\n /** Callback cuando un chunk es enviado */\r\n onChunk?: (chunk: FlightChunk) => void;\r\n /** Callback cuando el stream termina */\r\n onComplete?: () => void;\r\n /** Callback en caso de error */\r\n onError?: (error: Error) => void;\r\n}\r\n\r\n/**\r\n * Resultado del parsing de un FlightStream\r\n */\r\nexport interface FlightStreamResult {\r\n /** Chunks recibidos */\r\n chunks: FlightChunk[];\r\n /** Componente raíz */\r\n root?: ServerComponentChunk;\r\n /** Errores encontrados */\r\n errors: ErrorBoundaryChunk[];\r\n /** Tiempo total de streaming en ms */\r\n duration: number;\r\n}\r\n\r\n// ============================================================================\r\n// Stream Creation\r\n// ============================================================================\r\n\r\n/**\r\n * Crea un ReadableStream desde un AsyncIterable de chunks\r\n * \r\n * @example\r\n * ```typescript\r\n * async function* renderApp() {\r\n * yield createServerChunk('root', tree);\r\n * yield createClientChunk('counter', './Counter', 'default', { initial: 0 });\r\n * }\r\n * \r\n * const stream = createFlightStream(renderApp());\r\n * return new Response(stream, { headers: { 'Content-Type': 'text/x-flight' } });\r\n * ```\r\n */\r\nexport function createFlightStream(\r\n chunks: AsyncIterable<FlightChunk>,\r\n options: FlightStreamOptions = {}\r\n): ReadableStream<Uint8Array> {\r\n const { onChunk, onComplete, onError } = options;\r\n const encoder = new TextEncoder();\r\n\r\n return new ReadableStream({\r\n async start(controller) {\r\n try {\r\n for await (const chunk of chunks) {\r\n // Serialize chunk to NDJSON line\r\n const line = JSON.stringify(chunk) + '\\n';\r\n controller.enqueue(encoder.encode(line));\r\n\r\n onChunk?.(chunk);\r\n }\r\n\r\n controller.close();\r\n onComplete?.();\r\n } catch (error) {\r\n const err = error instanceof Error ? error : new Error(String(error));\r\n onError?.(err);\r\n controller.error(err);\r\n }\r\n },\r\n });\r\n}\r\n\r\n/**\r\n * Crea un FlightStream desde un array de chunks\r\n */\r\nexport function createFlightStreamFromArray(\r\n chunks: FlightChunk[],\r\n options: FlightStreamOptions = {}\r\n): ReadableStream<Uint8Array> {\r\n async function* generator() {\r\n for (const chunk of chunks) {\r\n yield chunk;\r\n }\r\n }\r\n return createFlightStream(generator(), options);\r\n}\r\n\r\n// ============================================================================\r\n// Stream Parsing\r\n// ============================================================================\r\n\r\n/**\r\n * Parsea un FlightStream a chunks individuales\r\n * \r\n * @example\r\n * ```typescript\r\n * // En el cliente\r\n * const response = await fetch('/page');\r\n * const chunks = parseFlightStream(response.body!);\r\n * \r\n * for await (const chunk of chunks) {\r\n * handleChunk(chunk);\r\n * }\r\n * ```\r\n */\r\nexport async function* parseFlightStream(\r\n stream: ReadableStream<Uint8Array>\r\n): AsyncGenerator<FlightChunk> {\r\n const reader = stream.getReader();\r\n const decoder = new TextDecoder();\r\n let buffer = '';\r\n\r\n try {\r\n while (true) {\r\n const { done, value } = await reader.read();\r\n\r\n if (done) {\r\n // Process remaining buffer\r\n if (buffer.trim()) {\r\n yield parseChunk(buffer);\r\n }\r\n break;\r\n }\r\n\r\n buffer += decoder.decode(value, { stream: true });\r\n\r\n // Split by newlines and process complete lines\r\n const lines = buffer.split('\\n');\r\n buffer = lines.pop() ?? '';\r\n\r\n for (const line of lines) {\r\n if (line.trim()) {\r\n yield parseChunk(line);\r\n }\r\n }\r\n }\r\n } finally {\r\n reader.releaseLock();\r\n }\r\n}\r\n\r\n/**\r\n * Parsea un string completo de FlightStream\r\n */\r\nexport function parseFlightStreamSync(data: string): FlightChunk[] {\r\n const chunks: FlightChunk[] = [];\r\n\r\n for (const line of data.split('\\n')) {\r\n if (line.trim()) {\r\n chunks.push(parseChunk(line));\r\n }\r\n }\r\n\r\n return chunks;\r\n}\r\n\r\n/**\r\n * Parsea una línea individual a un chunk\r\n */\r\nfunction parseChunk(line: string): FlightChunk {\r\n try {\r\n return JSON.parse(line) as FlightChunk;\r\n } catch {\r\n throw new FlightStreamError(\r\n `Failed to parse Flight chunk: ${line.slice(0, 100)}${line.length > 100 ? '...' : ''}`,\r\n line\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Consume un FlightStream y retorna todos los chunks\r\n */\r\nexport async function consumeFlightStream(\r\n stream: ReadableStream<Uint8Array>\r\n): Promise<FlightStreamResult> {\r\n const startTime = Date.now();\r\n const chunks: FlightChunk[] = [];\r\n const errors: ErrorBoundaryChunk[] = [];\r\n let root: ServerComponentChunk | undefined;\r\n\r\n for await (const chunk of parseFlightStream(stream)) {\r\n chunks.push(chunk);\r\n\r\n if (chunk.type === 'S' && chunk.id === 'root') {\r\n root = chunk;\r\n }\r\n\r\n if (chunk.type === 'E') {\r\n errors.push(chunk);\r\n }\r\n }\r\n\r\n return {\r\n chunks,\r\n root,\r\n errors,\r\n duration: Date.now() - startTime,\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// Response Helpers\r\n// ============================================================================\r\n\r\n/**\r\n * Crea una Response con Flight payload\r\n */\r\nexport function createFlightResponse(\r\n chunks: AsyncIterable<FlightChunk>,\r\n options: FlightResponseOptions = {}\r\n): Response {\r\n const { status = 200, headers = {} } = options;\r\n\r\n return new Response(createFlightStream(chunks), {\r\n status,\r\n headers: {\r\n 'Content-Type': 'text/x-flight',\r\n 'Transfer-Encoding': 'chunked',\r\n 'Cache-Control': 'no-cache, no-store',\r\n 'X-Content-Type-Options': 'nosniff',\r\n ...headers,\r\n },\r\n });\r\n}\r\n\r\nexport interface FlightResponseOptions {\r\n status?: number;\r\n headers?: Record<string, string>;\r\n}\r\n\r\n/**\r\n * Crea una Response HTML con streaming SSR + Flight payload embebido\r\n */\r\nexport function createHybridResponse(\r\n htmlStream: ReadableStream<Uint8Array>,\r\n flightChunks: FlightChunk[],\r\n options: FlightResponseOptions = {}\r\n): Response {\r\n const { status = 200, headers = {} } = options;\r\n\r\n // Combine HTML stream with Flight payload as inline script\r\n const encoder = new TextEncoder();\r\n const flightPayload = JSON.stringify(flightChunks);\r\n const inlineScript = `<script type=\"text/x-flight\">${escapeScript(flightPayload)}</script>`;\r\n\r\n // Create a TransformStream to inject Flight payload before closing body\r\n const transform = new TransformStream<Uint8Array, Uint8Array>({\r\n transform(chunk, controller) {\r\n controller.enqueue(chunk);\r\n },\r\n flush(controller) {\r\n controller.enqueue(encoder.encode(inlineScript));\r\n },\r\n });\r\n\r\n const combinedStream = htmlStream.pipeThrough(transform);\r\n\r\n return new Response(combinedStream, {\r\n status,\r\n headers: {\r\n 'Content-Type': 'text/html; charset=utf-8',\r\n 'Transfer-Encoding': 'chunked',\r\n 'X-Content-Type-Options': 'nosniff',\r\n ...headers,\r\n },\r\n });\r\n}\r\n\r\n/**\r\n * Escapa contenido para uso seguro en script tags\r\n */\r\nfunction escapeScript(content: string): string {\r\n return content\r\n .replace(/</g, '\\\\u003c')\r\n .replace(/>/g, '\\\\u003e')\r\n .replace(/&/g, '\\\\u0026');\r\n}\r\n\r\n// ============================================================================\r\n// Streaming Utilities\r\n// ============================================================================\r\n\r\n/**\r\n * Crea un stream controller para enviar chunks progresivamente\r\n */\r\nexport function createStreamController(): {\r\n stream: ReadableStream<Uint8Array>;\r\n enqueue: (chunk: FlightChunk) => void;\r\n error: (error: Error) => void;\r\n close: () => void;\r\n} {\r\n const encoder = new TextEncoder();\r\n let controller: ReadableStreamDefaultController<Uint8Array>;\r\n\r\n const stream = new ReadableStream<Uint8Array>({\r\n start(ctrl) {\r\n controller = ctrl;\r\n },\r\n });\r\n\r\n return {\r\n stream,\r\n enqueue(chunk: FlightChunk) {\r\n const line = JSON.stringify(chunk) + '\\n';\r\n controller.enqueue(encoder.encode(line));\r\n },\r\n error(err: Error) {\r\n controller.error(err);\r\n },\r\n close() {\r\n controller.close();\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Merge múltiples streams en uno solo (para rendering paralelo)\r\n */\r\nexport function mergeFlightStreams(\r\n ...streams: ReadableStream<Uint8Array>[]\r\n): ReadableStream<Uint8Array> {\r\n const readers = streams.map(s => s.getReader());\r\n\r\n return new ReadableStream({\r\n async pull(controller) {\r\n // Race all readers\r\n const promises = readers.map(async (reader, index) => {\r\n const result = await reader.read();\r\n return { result, index };\r\n });\r\n\r\n const { result, index } = await Promise.race(promises);\r\n\r\n if (result.done) {\r\n // Remove completed reader\r\n readers.splice(index, 1);\r\n\r\n if (readers.length === 0) {\r\n controller.close();\r\n }\r\n } else {\r\n controller.enqueue(result.value);\r\n }\r\n },\r\n });\r\n}\r\n\r\n/**\r\n * Pipe a flight stream through a transform\r\n */\r\nexport function transformFlightStream(\r\n stream: ReadableStream<Uint8Array>,\r\n transform: (chunk: FlightChunk) => FlightChunk | null | Promise<FlightChunk | null>\r\n): ReadableStream<Uint8Array> {\r\n const encoder = new TextEncoder();\r\n\r\n return new ReadableStream({\r\n async start(controller) {\r\n try {\r\n for await (const chunk of parseFlightStream(stream)) {\r\n const transformed = await transform(chunk);\r\n if (transformed) {\r\n const line = JSON.stringify(transformed) + '\\n';\r\n controller.enqueue(encoder.encode(line));\r\n }\r\n }\r\n controller.close();\r\n } catch (error) {\r\n controller.error(error);\r\n }\r\n },\r\n });\r\n}\r\n\r\n// ============================================================================\r\n// Debug Utilities\r\n// ============================================================================\r\n\r\n/**\r\n * Pretty print Flight chunks para debugging\r\n */\r\nexport function prettyPrintChunks(chunks: FlightChunk[]): string {\r\n const lines: string[] = [];\r\n\r\n for (const chunk of chunks) {\r\n const prefix = getChunkTypeLabel(chunk.type);\r\n const id = 'id' in chunk ? chunk.id : 'boundary' in chunk ? chunk.boundary : '?';\r\n\r\n lines.push(`[${prefix}] ${id}`);\r\n\r\n if (chunk.type === 'S' && chunk.tree) {\r\n lines.push(` └─ Tree: ${prettyPrintElement(chunk.tree)}`);\r\n }\r\n if (chunk.type === 'C') {\r\n lines.push(` └─ Module: ${chunk.module}#${chunk.export}`);\r\n }\r\n if (chunk.type === 'E') {\r\n lines.push(` └─ Error: ${chunk.message}`);\r\n }\r\n }\r\n\r\n return lines.join('\\n');\r\n}\r\n\r\nfunction getChunkTypeLabel(type: FlightChunk['type']): string {\r\n switch (type) {\r\n case 'S': return 'SERVER';\r\n case 'C': return 'CLIENT';\r\n case 'A': return 'ACTION';\r\n case 'E': return 'ERROR ';\r\n case 'H': return 'HINT ';\r\n case 'M': return 'META ';\r\n default: return 'UNKNWN';\r\n }\r\n}\r\n\r\nfunction prettyPrintElement(element: unknown): string {\r\n if (!element || typeof element !== 'object') return String(element);\r\n\r\n const el = element as { $$type?: string; tag?: string; ref?: string };\r\n\r\n if (el.$$type === 'host') return `<${el.tag}>...`;\r\n if (el.$$type === 'text') return '\"text\"';\r\n if (el.$$type === 'fragment') return '<>...</>';\r\n if (el.$$type === 'suspense') return '<Suspense>...</Suspense>';\r\n if (el.$$type === 'client') return `<ClientRef:${el.ref}>`;\r\n if (el.$$type === 'lazy') return '<Lazy...>';\r\n if (el.$$type === 'null') return 'null';\r\n\r\n return JSON.stringify(element).slice(0, 50);\r\n}\r\n\r\n// ============================================================================\r\n// Custom Errors\r\n// ============================================================================\r\n\r\n/**\r\n * Error durante parsing de FlightStream\r\n */\r\nexport class FlightStreamError extends Error {\r\n constructor(\r\n message: string,\r\n public readonly rawData?: string\r\n ) {\r\n super(message);\r\n this.name = 'FlightStreamError';\r\n }\r\n}\r\n"]}
|
|
@@ -217,5 +217,5 @@ async function streamSequential(boundaries) {
|
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
export { createLazyContent, createStreamingResponse, createStreamingSSR, renderWithStreaming, streamParallel, streamSequential };
|
|
220
|
-
//# sourceMappingURL=chunk-
|
|
221
|
-
//# sourceMappingURL=chunk-
|
|
220
|
+
//# sourceMappingURL=chunk-RSVA2EYO.js.map
|
|
221
|
+
//# sourceMappingURL=chunk-RSVA2EYO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/streaming/index.ts"],"names":[],"mappings":"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","file":"chunk-RSVA2EYO.js","sourcesContent":["/**\r\n * @flight-framework/core - Streaming SSR\r\n * \r\n * Full streaming server-side rendering implementation following React 18+/19 patterns.\r\n * Supports both Node.js (renderToPipeableStream) and Edge (renderToReadableStream) environments.\r\n * \r\n * Best Practices 2025/2026:\r\n * - Progressive HTML streaming with Suspense boundaries\r\n * - Shell-first rendering for fast TTFB\r\n * - Nested Suspense for granular loading states\r\n * - Error boundaries for graceful degradation\r\n * - Web Streams API for Edge runtime compatibility\r\n */\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\n/**\r\n * Streaming render options following React's conventions\r\n */\r\nexport interface StreamingRenderOptions {\r\n /** Bootstrap scripts to load on client */\r\n bootstrapScripts?: string[];\r\n /** Bootstrap ES modules */\r\n bootstrapModules?: string[];\r\n /** Inline script content */\r\n bootstrapScriptContent?: string;\r\n /** Prefix for React IDs (useId) */\r\n identifierPrefix?: string;\r\n /** Nonce for CSP */\r\n nonce?: string;\r\n /** Callback when shell is ready (main content before Suspense) */\r\n onShellReady?: () => void;\r\n /** Callback when shell errors */\r\n onShellError?: (error: Error) => void;\r\n /** Callback when all content is ready */\r\n onAllReady?: () => void;\r\n /** Callback for any error */\r\n onError?: (error: Error) => void;\r\n /** Timeout before aborting stream */\r\n timeoutMs?: number;\r\n /** Progressive hydration enabled */\r\n progressiveHydration?: boolean;\r\n}\r\n\r\n/**\r\n * Streaming render result\r\n */\r\nexport interface StreamingRenderResult {\r\n /** The readable stream to pipe to response */\r\n stream: ReadableStream<Uint8Array>;\r\n /** Abort the stream */\r\n abort: () => void;\r\n /** Promise that resolves when shell is ready */\r\n shellReady: Promise<void>;\r\n /** Promise that resolves when all content is ready */\r\n allReady: Promise<void>;\r\n}\r\n\r\n/**\r\n * Suspense boundary configuration\r\n */\r\nexport interface SuspenseBoundaryConfig {\r\n /** Unique ID for this boundary */\r\n id: string;\r\n /** Fallback HTML to show while loading */\r\n fallback: string;\r\n /** Content resolver promise */\r\n contentPromise: Promise<string>;\r\n /** \r\n * IDs of boundaries that must resolve before this one.\r\n * Enables dependency-aware streaming for complex data relationships.\r\n * @example ['user'] - Wait for 'user' boundary before starting\r\n */\r\n dependsOn?: string[];\r\n /** Custom metadata for observability */\r\n meta?: Record<string, unknown>;\r\n}\r\n\r\n// ============================================================================\r\n// Streaming Renderer\r\n// ============================================================================\r\n\r\n/**\r\n * Create a streaming SSR response using Web Streams API.\r\n * Compatible with Edge Runtime (Cloudflare, Vercel Edge, Deno).\r\n * \r\n * @example\r\n * ```typescript\r\n * const result = await createStreamingSSR({\r\n * shell: '<html><body><div id=\"root\">',\r\n * shellEnd: '</div></body></html>',\r\n * suspenseBoundaries: [\r\n * {\r\n * id: 'posts',\r\n * fallback: '<div>Loading posts...</div>',\r\n * contentPromise: fetchAndRenderPosts(),\r\n * }\r\n * ],\r\n * bootstrapScripts: ['/client.js'],\r\n * });\r\n * \r\n * return new Response(result.stream, {\r\n * headers: { 'Content-Type': 'text/html' },\r\n * });\r\n * ```\r\n */\r\nexport async function createStreamingSSR(config: {\r\n /** Initial HTML shell (before suspense content) */\r\n shell: string;\r\n /** Closing HTML shell */\r\n shellEnd: string;\r\n /** Suspense boundaries with async content */\r\n suspenseBoundaries?: SuspenseBoundaryConfig[];\r\n /** Streaming options */\r\n options?: StreamingRenderOptions;\r\n}): Promise<StreamingRenderResult> {\r\n const { shell, shellEnd, suspenseBoundaries = [], options = {} } = config;\r\n\r\n let shellResolved = false;\r\n let allResolved = false;\r\n let abortController: AbortController | null = null;\r\n\r\n // Shell ready promise\r\n let resolveShell: () => void;\r\n const shellReady = new Promise<void>((resolve) => {\r\n resolveShell = resolve;\r\n });\r\n\r\n // All ready promise\r\n let resolveAll: () => void;\r\n const allReady = new Promise<void>((resolve) => {\r\n resolveAll = resolve;\r\n });\r\n\r\n const encoder = new TextEncoder();\r\n\r\n const stream = new ReadableStream<Uint8Array>({\r\n async start(controller) {\r\n try {\r\n // 1. Send the shell immediately (fast TTFB)\r\n const shellWithPlaceholders = buildShellWithPlaceholders(\r\n shell,\r\n suspenseBoundaries,\r\n shellEnd,\r\n options\r\n );\r\n\r\n controller.enqueue(encoder.encode(shellWithPlaceholders));\r\n\r\n shellResolved = true;\r\n options.onShellReady?.();\r\n resolveShell!();\r\n\r\n // 2. Stream Suspense boundary contents as they resolve\r\n if (suspenseBoundaries.length > 0) {\r\n await streamSuspenseContent(controller, encoder, suspenseBoundaries, options);\r\n }\r\n\r\n // 3. Send hydration script if needed\r\n if (options.bootstrapScripts?.length || options.bootstrapModules?.length) {\r\n const hydrationScript = buildHydrationScript(options);\r\n controller.enqueue(encoder.encode(hydrationScript));\r\n }\r\n\r\n allResolved = true;\r\n options.onAllReady?.();\r\n resolveAll!();\r\n\r\n controller.close();\r\n } catch (error) {\r\n if (!shellResolved) {\r\n options.onShellError?.(error as Error);\r\n }\r\n options.onError?.(error as Error);\r\n controller.error(error);\r\n }\r\n },\r\n\r\n cancel() {\r\n abortController?.abort();\r\n },\r\n });\r\n\r\n const abort = () => {\r\n abortController?.abort();\r\n };\r\n\r\n // Timeout handling\r\n if (options.timeoutMs) {\r\n abortController = new AbortController();\r\n setTimeout(() => {\r\n if (!allResolved) {\r\n abort();\r\n }\r\n }, options.timeoutMs);\r\n }\r\n\r\n return {\r\n stream,\r\n abort,\r\n shellReady,\r\n allReady,\r\n };\r\n}\r\n\r\n/**\r\n * Build shell HTML with Suspense placeholders\r\n */\r\nfunction buildShellWithPlaceholders(\r\n shell: string,\r\n boundaries: SuspenseBoundaryConfig[],\r\n shellEnd: string,\r\n options: StreamingRenderOptions\r\n): string {\r\n let html = shell;\r\n\r\n // Add inline bootstrap script if provided\r\n if (options.bootstrapScriptContent) {\r\n html += `<script>${options.bootstrapScriptContent}</script>`;\r\n }\r\n\r\n // Add Suspense fallbacks with placeholder markers\r\n for (const boundary of boundaries) {\r\n html += `\r\n<!--$?--><template id=\"B:${boundary.id}\"></template>\r\n${boundary.fallback}\r\n<!--/$-->`;\r\n }\r\n\r\n html += shellEnd;\r\n\r\n return html;\r\n}\r\n\r\n/**\r\n * Stream Suspense content as promises resolve\r\n */\r\nasync function streamSuspenseContent(\r\n controller: ReadableStreamDefaultController<Uint8Array>,\r\n encoder: TextEncoder,\r\n boundaries: SuspenseBoundaryConfig[],\r\n options: StreamingRenderOptions\r\n): Promise<void> {\r\n // Race all boundaries - stream each as it completes\r\n const pending = boundaries.map(async (boundary) => {\r\n try {\r\n const content = await boundary.contentPromise;\r\n return { boundary, content, error: null };\r\n } catch (error) {\r\n return { boundary, content: null, error: error as Error };\r\n }\r\n });\r\n\r\n // Process as each resolves\r\n const results = await Promise.allSettled(pending);\r\n\r\n for (const result of results) {\r\n if (result.status === 'fulfilled') {\r\n const { boundary, content, error } = result.value;\r\n\r\n if (error) {\r\n // Send error replacement\r\n const errorScript = buildErrorReplacement(boundary.id, error.message);\r\n controller.enqueue(encoder.encode(errorScript));\r\n options.onError?.(error);\r\n } else if (content) {\r\n // Send content replacement script\r\n const replacementScript = buildContentReplacement(boundary.id, content);\r\n controller.enqueue(encoder.encode(replacementScript));\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Build script to replace Suspense placeholder with actual content\r\n * This follows React's streaming hydration pattern\r\n */\r\nfunction buildContentReplacement(id: string, content: string): string {\r\n // Escape script-breaking characters\r\n const escaped = content\r\n .replace(/\\\\/g, '\\\\\\\\')\r\n .replace(/</g, '\\\\u003c')\r\n .replace(/>/g, '\\\\u003e')\r\n .replace(/'/g, \"\\\\'\")\r\n .replace(/\"/g, '\\\\\"')\r\n .replace(/\\n/g, '\\\\n')\r\n .replace(/\\r/g, '\\\\r');\r\n\r\n return `\r\n<script>\r\n(function(){\r\n var b=document.getElementById(\"B:${id}\");\r\n if(b){\r\n var p=b.previousSibling;\r\n while(p&&p.nodeType===8&&p.data===\"$?\")p=p.previousSibling;\r\n var n=b.nextSibling;\r\n var f=document.createDocumentFragment();\r\n var t=document.createElement(\"template\");\r\n t.innerHTML=\"${escaped}\";\r\n while(t.content.firstChild)f.appendChild(t.content.firstChild);\r\n if(n&&n.nodeType===8&&n.data===\"/$\"){\r\n var s=n.nextSibling;\r\n while(s&&s!==b){var x=s.nextSibling;s.parentNode.removeChild(s);s=x;}\r\n }\r\n b.parentNode.replaceChild(f,b);\r\n }\r\n})();\r\n</script>`;\r\n}\r\n\r\n/**\r\n * Build script to show error in place of Suspense content\r\n */\r\nfunction buildErrorReplacement(id: string, message: string): string {\r\n const escaped = message.replace(/'/g, \"\\\\'\").replace(/\"/g, '\\\\\"');\r\n\r\n return `\r\n<script>\r\n(function(){\r\n var b=document.getElementById(\"B:${id}\");\r\n if(b){\r\n var t=document.createElement(\"div\");\r\n t.className=\"streaming-error\";\r\n t.textContent=\"Error: ${escaped}\";\r\n b.parentNode.replaceChild(t,b);\r\n }\r\n})();\r\n</script>`;\r\n}\r\n\r\n/**\r\n * Build hydration bootstrap script\r\n */\r\nfunction buildHydrationScript(options: StreamingRenderOptions): string {\r\n let scripts = '';\r\n\r\n if (options.bootstrapScripts?.length) {\r\n for (const src of options.bootstrapScripts) {\r\n const nonceAttr = options.nonce ? ` nonce=\"${options.nonce}\"` : '';\r\n scripts += `<script src=\"${src}\"${nonceAttr} async></script>`;\r\n }\r\n }\r\n\r\n if (options.bootstrapModules?.length) {\r\n for (const src of options.bootstrapModules) {\r\n const nonceAttr = options.nonce ? ` nonce=\"${options.nonce}\"` : '';\r\n scripts += `<script type=\"module\" src=\"${src}\"${nonceAttr}></script>`;\r\n }\r\n }\r\n\r\n return scripts;\r\n}\r\n\r\n// ============================================================================\r\n// Streaming Response Helpers\r\n// ============================================================================\r\n\r\n/**\r\n * Create a streaming HTML Response object\r\n */\r\nexport function createStreamingResponse(\r\n stream: ReadableStream<Uint8Array>,\r\n options: {\r\n status?: number;\r\n headers?: Record<string, string>;\r\n } = {}\r\n): Response {\r\n return new Response(stream, {\r\n status: options.status || 200,\r\n headers: {\r\n 'Content-Type': 'text/html; charset=utf-8',\r\n 'Transfer-Encoding': 'chunked',\r\n 'X-Content-Type-Options': 'nosniff',\r\n ...options.headers,\r\n },\r\n });\r\n}\r\n\r\n/**\r\n * Higher-level API: Render page with Suspense boundaries\r\n * \r\n * @example\r\n * ```typescript\r\n * return renderWithStreaming({\r\n * layout: ({ children }) => `\r\n * <html>\r\n * <head><title>My App</title></head>\r\n * <body><div id=\"root\">${children}</div></body>\r\n * </html>\r\n * `,\r\n * page: async () => '<h1>Welcome</h1>',\r\n * suspense: {\r\n * posts: {\r\n * fallback: '<div class=\"skeleton\">Loading...</div>',\r\n * content: fetchPosts().then(renderPosts),\r\n * },\r\n * },\r\n * });\r\n * ```\r\n */\r\nexport async function renderWithStreaming(config: {\r\n /** Layout wrapper */\r\n layout: (props: { children: string }) => string;\r\n /** Main page content (sync part) */\r\n page: () => string | Promise<string>;\r\n /** Suspense boundaries keyed by ID */\r\n suspense?: Record<string, { fallback: string; content: Promise<string> }>;\r\n /** Bootstrap scripts */\r\n bootstrapScripts?: string[];\r\n /** Timeout in ms */\r\n timeoutMs?: number;\r\n}): Promise<Response> {\r\n const { layout, page, suspense = {}, bootstrapScripts, timeoutMs } = config;\r\n\r\n // Get synchronous page content\r\n const pageContent = await page();\r\n\r\n // Build suspense boundaries\r\n const boundaries: SuspenseBoundaryConfig[] = Object.entries(suspense).map(\r\n ([id, { fallback, content }]) => ({\r\n id,\r\n fallback,\r\n contentPromise: content,\r\n })\r\n );\r\n\r\n // Create streaming result\r\n const result = await createStreamingSSR({\r\n shell: layout({ children: pageContent }),\r\n shellEnd: '',\r\n suspenseBoundaries: boundaries,\r\n options: {\r\n bootstrapScripts,\r\n timeoutMs,\r\n },\r\n });\r\n\r\n return createStreamingResponse(result.stream);\r\n}\r\n\r\n// ============================================================================\r\n// Progressive Rendering Utilities\r\n// ============================================================================\r\n\r\n/**\r\n * Create a lazy component that streams its content\r\n */\r\nexport function createLazyContent<T>(\r\n fetcher: () => Promise<T>,\r\n renderer: (data: T) => string,\r\n fallback: string\r\n): { fallback: string; content: Promise<string> } {\r\n return {\r\n fallback,\r\n content: fetcher().then(renderer),\r\n };\r\n}\r\n\r\n/**\r\n * Parallel streaming: resolve multiple boundaries simultaneously\r\n */\r\nexport async function streamParallel(\r\n boundaries: Array<{\r\n id: string;\r\n fallback: string;\r\n content: () => Promise<string>;\r\n }>\r\n): Promise<SuspenseBoundaryConfig[]> {\r\n return boundaries.map((b) => ({\r\n id: b.id,\r\n fallback: b.fallback,\r\n contentPromise: b.content(),\r\n }));\r\n}\r\n\r\n/**\r\n * Sequential streaming: resolve boundaries in order\r\n */\r\nexport async function streamSequential(\r\n boundaries: Array<{\r\n id: string;\r\n fallback: string;\r\n content: () => Promise<string>;\r\n }>\r\n): Promise<SuspenseBoundaryConfig[]> {\r\n const result: SuspenseBoundaryConfig[] = [];\r\n\r\n for (const b of boundaries) {\r\n result.push({\r\n id: b.id,\r\n fallback: b.fallback,\r\n contentPromise: b.content(),\r\n });\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ============================================================================\r\n// Export for use with React\r\n// ============================================================================\r\n\r\n/**\r\n * NOTE: For React-specific streaming with renderToPipeableStream or \r\n * renderToReadableStream, use the dedicated React adapter:\r\n * \r\n * ```typescript\r\n * import { renderToReadableStream } from 'react-dom/server';\r\n * import { createStreamingResponse } from '@flight-framework/core/streaming';\r\n * \r\n * const stream = await renderToReadableStream(<App />, {\r\n * bootstrapScripts: ['/client.js'],\r\n * });\r\n * \r\n * return createStreamingResponse(stream);\r\n * ```\r\n * \r\n * This module provides framework-agnostic streaming primitives that work\r\n * with any UI library or custom HTML generation.\r\n */\r\n"]}
|