@flight-framework/core 0.0.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.
Files changed (65) hide show
  1. package/LICENSE +21 -0
  2. package/dist/actions/index.d.ts +108 -0
  3. package/dist/actions/index.js +3 -0
  4. package/dist/actions/index.js.map +1 -0
  5. package/dist/adapters/index.d.ts +243 -0
  6. package/dist/adapters/index.js +3 -0
  7. package/dist/adapters/index.js.map +1 -0
  8. package/dist/cache/index.d.ts +76 -0
  9. package/dist/cache/index.js +3 -0
  10. package/dist/cache/index.js.map +1 -0
  11. package/dist/chunk-3AIQVGTM.js +120 -0
  12. package/dist/chunk-3AIQVGTM.js.map +1 -0
  13. package/dist/chunk-AFSKXC6V.js +188 -0
  14. package/dist/chunk-AFSKXC6V.js.map +1 -0
  15. package/dist/chunk-AJ3IBYXT.js +47 -0
  16. package/dist/chunk-AJ3IBYXT.js.map +1 -0
  17. package/dist/chunk-GCQZ4FHI.js +245 -0
  18. package/dist/chunk-GCQZ4FHI.js.map +1 -0
  19. package/dist/chunk-I5RHYGX6.js +167 -0
  20. package/dist/chunk-I5RHYGX6.js.map +1 -0
  21. package/dist/chunk-KWFX6WHG.js +311 -0
  22. package/dist/chunk-KWFX6WHG.js.map +1 -0
  23. package/dist/chunk-Q4C5CCHK.js +13 -0
  24. package/dist/chunk-Q4C5CCHK.js.map +1 -0
  25. package/dist/chunk-QEFGUHYD.js +221 -0
  26. package/dist/chunk-QEFGUHYD.js.map +1 -0
  27. package/dist/chunk-TKXN7KGE.js +145 -0
  28. package/dist/chunk-TKXN7KGE.js.map +1 -0
  29. package/dist/chunk-WAGCTWGY.js +93 -0
  30. package/dist/chunk-WAGCTWGY.js.map +1 -0
  31. package/dist/chunk-Y22KEW2F.js +223 -0
  32. package/dist/chunk-Y22KEW2F.js.map +1 -0
  33. package/dist/chunk-ZVC3ZWLM.js +52 -0
  34. package/dist/chunk-ZVC3ZWLM.js.map +1 -0
  35. package/dist/config/index.d.ts +146 -0
  36. package/dist/config/index.js +3 -0
  37. package/dist/config/index.js.map +1 -0
  38. package/dist/file-router/index.d.ts +71 -0
  39. package/dist/file-router/index.js +3 -0
  40. package/dist/file-router/index.js.map +1 -0
  41. package/dist/handlers/index.d.ts +59 -0
  42. package/dist/handlers/index.js +3 -0
  43. package/dist/handlers/index.js.map +1 -0
  44. package/dist/index.d.ts +23 -0
  45. package/dist/index.js +19 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/middleware/index.d.ts +152 -0
  48. package/dist/middleware/index.js +3 -0
  49. package/dist/middleware/index.js.map +1 -0
  50. package/dist/render/index.d.ts +131 -0
  51. package/dist/render/index.js +3 -0
  52. package/dist/render/index.js.map +1 -0
  53. package/dist/router/index.d.ts +65 -0
  54. package/dist/router/index.js +3 -0
  55. package/dist/router/index.js.map +1 -0
  56. package/dist/rsc/index.d.ts +131 -0
  57. package/dist/rsc/index.js +3 -0
  58. package/dist/rsc/index.js.map +1 -0
  59. package/dist/server/index.d.ts +135 -0
  60. package/dist/server/index.js +6 -0
  61. package/dist/server/index.js.map +1 -0
  62. package/dist/streaming/index.d.ts +169 -0
  63. package/dist/streaming/index.js +3 -0
  64. package/dist/streaming/index.js.map +1 -0
  65. package/package.json +100 -0
@@ -0,0 +1,221 @@
1
+ // src/streaming/index.ts
2
+ async function createStreamingSSR(config) {
3
+ const { shell, shellEnd, suspenseBoundaries = [], options = {} } = config;
4
+ let shellResolved = false;
5
+ let allResolved = false;
6
+ let abortController = null;
7
+ let resolveShell;
8
+ const shellReady = new Promise((resolve) => {
9
+ resolveShell = resolve;
10
+ });
11
+ let resolveAll;
12
+ const allReady = new Promise((resolve) => {
13
+ resolveAll = resolve;
14
+ });
15
+ const encoder = new TextEncoder();
16
+ const stream = new ReadableStream({
17
+ async start(controller) {
18
+ try {
19
+ const shellWithPlaceholders = buildShellWithPlaceholders(
20
+ shell,
21
+ suspenseBoundaries,
22
+ shellEnd,
23
+ options
24
+ );
25
+ controller.enqueue(encoder.encode(shellWithPlaceholders));
26
+ shellResolved = true;
27
+ options.onShellReady?.();
28
+ resolveShell();
29
+ if (suspenseBoundaries.length > 0) {
30
+ await streamSuspenseContent(controller, encoder, suspenseBoundaries, options);
31
+ }
32
+ if (options.bootstrapScripts?.length || options.bootstrapModules?.length) {
33
+ const hydrationScript = buildHydrationScript(options);
34
+ controller.enqueue(encoder.encode(hydrationScript));
35
+ }
36
+ allResolved = true;
37
+ options.onAllReady?.();
38
+ resolveAll();
39
+ controller.close();
40
+ } catch (error) {
41
+ if (!shellResolved) {
42
+ options.onShellError?.(error);
43
+ }
44
+ options.onError?.(error);
45
+ controller.error(error);
46
+ }
47
+ },
48
+ cancel() {
49
+ abortController?.abort();
50
+ }
51
+ });
52
+ const abort = () => {
53
+ abortController?.abort();
54
+ };
55
+ if (options.timeoutMs) {
56
+ abortController = new AbortController();
57
+ setTimeout(() => {
58
+ if (!allResolved) {
59
+ abort();
60
+ }
61
+ }, options.timeoutMs);
62
+ }
63
+ return {
64
+ stream,
65
+ abort,
66
+ shellReady,
67
+ allReady
68
+ };
69
+ }
70
+ function buildShellWithPlaceholders(shell, boundaries, shellEnd, options) {
71
+ let html = shell;
72
+ if (options.bootstrapScriptContent) {
73
+ html += `<script>${options.bootstrapScriptContent}</script>`;
74
+ }
75
+ for (const boundary of boundaries) {
76
+ html += `
77
+ <!--$?--><template id="B:${boundary.id}"></template>
78
+ ${boundary.fallback}
79
+ <!--/$-->`;
80
+ }
81
+ html += shellEnd;
82
+ return html;
83
+ }
84
+ async function streamSuspenseContent(controller, encoder, boundaries, options) {
85
+ const pending = boundaries.map(async (boundary) => {
86
+ try {
87
+ const content = await boundary.contentPromise;
88
+ return { boundary, content, error: null };
89
+ } catch (error) {
90
+ return { boundary, content: null, error };
91
+ }
92
+ });
93
+ const results = await Promise.allSettled(pending);
94
+ for (const result of results) {
95
+ if (result.status === "fulfilled") {
96
+ const { boundary, content, error } = result.value;
97
+ if (error) {
98
+ const errorScript = buildErrorReplacement(boundary.id, error.message);
99
+ controller.enqueue(encoder.encode(errorScript));
100
+ options.onError?.(error);
101
+ } else if (content) {
102
+ const replacementScript = buildContentReplacement(boundary.id, content);
103
+ controller.enqueue(encoder.encode(replacementScript));
104
+ }
105
+ }
106
+ }
107
+ }
108
+ function buildContentReplacement(id, content) {
109
+ const escaped = content.replace(/\\/g, "\\\\").replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/'/g, "\\'").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r");
110
+ return `
111
+ <script>
112
+ (function(){
113
+ var b=document.getElementById("B:${id}");
114
+ if(b){
115
+ var p=b.previousSibling;
116
+ while(p&&p.nodeType===8&&p.data==="$?")p=p.previousSibling;
117
+ var n=b.nextSibling;
118
+ var f=document.createDocumentFragment();
119
+ var t=document.createElement("template");
120
+ t.innerHTML="${escaped}";
121
+ while(t.content.firstChild)f.appendChild(t.content.firstChild);
122
+ if(n&&n.nodeType===8&&n.data==="/$"){
123
+ var s=n.nextSibling;
124
+ while(s&&s!==b){var x=s.nextSibling;s.parentNode.removeChild(s);s=x;}
125
+ }
126
+ b.parentNode.replaceChild(f,b);
127
+ }
128
+ })();
129
+ </script>`;
130
+ }
131
+ function buildErrorReplacement(id, message) {
132
+ const escaped = message.replace(/'/g, "\\'").replace(/"/g, '\\"');
133
+ return `
134
+ <script>
135
+ (function(){
136
+ var b=document.getElementById("B:${id}");
137
+ if(b){
138
+ var t=document.createElement("div");
139
+ t.className="streaming-error";
140
+ t.textContent="Error: ${escaped}";
141
+ b.parentNode.replaceChild(t,b);
142
+ }
143
+ })();
144
+ </script>`;
145
+ }
146
+ function buildHydrationScript(options) {
147
+ let scripts = "";
148
+ if (options.bootstrapScripts?.length) {
149
+ for (const src of options.bootstrapScripts) {
150
+ const nonceAttr = options.nonce ? ` nonce="${options.nonce}"` : "";
151
+ scripts += `<script src="${src}"${nonceAttr} async></script>`;
152
+ }
153
+ }
154
+ if (options.bootstrapModules?.length) {
155
+ for (const src of options.bootstrapModules) {
156
+ const nonceAttr = options.nonce ? ` nonce="${options.nonce}"` : "";
157
+ scripts += `<script type="module" src="${src}"${nonceAttr}></script>`;
158
+ }
159
+ }
160
+ return scripts;
161
+ }
162
+ function createStreamingResponse(stream, options = {}) {
163
+ return new Response(stream, {
164
+ status: options.status || 200,
165
+ headers: {
166
+ "Content-Type": "text/html; charset=utf-8",
167
+ "Transfer-Encoding": "chunked",
168
+ "X-Content-Type-Options": "nosniff",
169
+ ...options.headers
170
+ }
171
+ });
172
+ }
173
+ async function renderWithStreaming(config) {
174
+ const { layout, page, suspense = {}, bootstrapScripts, timeoutMs } = config;
175
+ const pageContent = await page();
176
+ const boundaries = Object.entries(suspense).map(
177
+ ([id, { fallback, content }]) => ({
178
+ id,
179
+ fallback,
180
+ contentPromise: content
181
+ })
182
+ );
183
+ const result = await createStreamingSSR({
184
+ shell: layout({ children: pageContent }),
185
+ shellEnd: "",
186
+ suspenseBoundaries: boundaries,
187
+ options: {
188
+ bootstrapScripts,
189
+ timeoutMs
190
+ }
191
+ });
192
+ return createStreamingResponse(result.stream);
193
+ }
194
+ function createLazyContent(fetcher, renderer, fallback) {
195
+ return {
196
+ fallback,
197
+ content: fetcher().then(renderer)
198
+ };
199
+ }
200
+ async function streamParallel(boundaries) {
201
+ return boundaries.map((b) => ({
202
+ id: b.id,
203
+ fallback: b.fallback,
204
+ contentPromise: b.content()
205
+ }));
206
+ }
207
+ async function streamSequential(boundaries) {
208
+ const result = [];
209
+ for (const b of boundaries) {
210
+ result.push({
211
+ id: b.id,
212
+ fallback: b.fallback,
213
+ contentPromise: b.content()
214
+ });
215
+ }
216
+ return result;
217
+ }
218
+
219
+ export { createLazyContent, createStreamingResponse, createStreamingSSR, renderWithStreaming, streamParallel, streamSequential };
220
+ //# sourceMappingURL=chunk-QEFGUHYD.js.map
221
+ //# sourceMappingURL=chunk-QEFGUHYD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/streaming/index.ts"],"names":[],"mappings":"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","file":"chunk-QEFGUHYD.js","sourcesContent":["/**\r\n * @flight/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\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 _aborted = 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 _aborted = true;\r\n abortController?.abort();\r\n },\r\n });\r\n\r\n // Abort function\r\n const abort = () => {\r\n _aborted = true;\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/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"]}
@@ -0,0 +1,145 @@
1
+ // src/actions/index.ts
2
+ var actionRegistry = /* @__PURE__ */ new Map();
3
+ function registerAction(action) {
4
+ actionRegistry.set(action.id, action);
5
+ }
6
+ function getAction(id) {
7
+ return actionRegistry.get(id);
8
+ }
9
+ function getAllActions() {
10
+ return Array.from(actionRegistry.values());
11
+ }
12
+ function clearActions() {
13
+ actionRegistry.clear();
14
+ }
15
+ async function executeAction(actionId, args) {
16
+ const action = getAction(actionId);
17
+ if (!action) {
18
+ return {
19
+ success: false,
20
+ error: `Action not found: ${actionId}`
21
+ };
22
+ }
23
+ try {
24
+ const result = await action.fn(...args);
25
+ return {
26
+ success: true,
27
+ data: result
28
+ };
29
+ } catch (error) {
30
+ if (isRedirectError(error)) {
31
+ throw error;
32
+ }
33
+ console.error(`[Flight] Action error (${actionId}):`, error);
34
+ return {
35
+ success: false,
36
+ error: error instanceof Error ? error.message : "Unknown error"
37
+ };
38
+ }
39
+ }
40
+ async function executeFormAction(actionId, formData) {
41
+ return executeAction(actionId, [formData]);
42
+ }
43
+ var actionCounter = 0;
44
+ function generateActionId(name, filePath) {
45
+ const hash = simpleHash(`${filePath}:${name}`);
46
+ return `action_${hash}_${actionCounter++}`;
47
+ }
48
+ function simpleHash(str) {
49
+ let hash = 0;
50
+ for (let i = 0; i < str.length; i++) {
51
+ const char = str.charCodeAt(i);
52
+ hash = (hash << 5) - hash + char;
53
+ hash = hash & hash;
54
+ }
55
+ return Math.abs(hash).toString(36);
56
+ }
57
+ function cookies() {
58
+ const cookieStore = globalThis.__flightCookies;
59
+ if (!cookieStore) {
60
+ console.warn("[Flight] Cookies not available outside of action context");
61
+ return {
62
+ get: () => void 0,
63
+ set: () => {
64
+ },
65
+ delete: () => {
66
+ }
67
+ };
68
+ }
69
+ return cookieStore;
70
+ }
71
+ function redirect(url) {
72
+ throw new RedirectError(url);
73
+ }
74
+ var RedirectError = class extends Error {
75
+ url;
76
+ constructor(url) {
77
+ super(`Redirect to ${url}`);
78
+ this.name = "RedirectError";
79
+ this.url = url;
80
+ }
81
+ };
82
+ function isRedirectError(error) {
83
+ return error instanceof RedirectError;
84
+ }
85
+ function parseFormData(formData) {
86
+ const result = {};
87
+ formData.forEach((value, key) => {
88
+ result[key] = String(value);
89
+ });
90
+ return result;
91
+ }
92
+ function createActionReference(actionId) {
93
+ return `/__flight_action/${actionId}`;
94
+ }
95
+ async function handleActionRequest(request) {
96
+ const url = new URL(request.url);
97
+ const actionPath = url.pathname;
98
+ const match = actionPath.match(/^\/__flight_action\/(.+)$/);
99
+ if (!match) {
100
+ return new Response(JSON.stringify({ error: "Invalid action path" }), {
101
+ status: 400,
102
+ headers: { "Content-Type": "application/json" }
103
+ });
104
+ }
105
+ const actionId = match[1];
106
+ if (!actionId) {
107
+ return new Response(JSON.stringify({ error: "Missing action ID" }), {
108
+ status: 400,
109
+ headers: { "Content-Type": "application/json" }
110
+ });
111
+ }
112
+ try {
113
+ let result;
114
+ const contentType = request.headers.get("content-type") || "";
115
+ if (contentType.includes("application/x-www-form-urlencoded") || contentType.includes("multipart/form-data")) {
116
+ const formData = await request.formData();
117
+ result = await executeFormAction(actionId, formData);
118
+ } else {
119
+ const args = await request.json();
120
+ result = await executeAction(actionId, Array.isArray(args) ? args : [args]);
121
+ }
122
+ return new Response(JSON.stringify(result), {
123
+ status: result.success ? 200 : 400,
124
+ headers: { "Content-Type": "application/json" }
125
+ });
126
+ } catch (error) {
127
+ if (isRedirectError(error)) {
128
+ return new Response(null, {
129
+ status: 303,
130
+ headers: { Location: error.url }
131
+ });
132
+ }
133
+ return new Response(JSON.stringify({
134
+ success: false,
135
+ error: error instanceof Error ? error.message : "Unknown error"
136
+ }), {
137
+ status: 500,
138
+ headers: { "Content-Type": "application/json" }
139
+ });
140
+ }
141
+ }
142
+
143
+ export { RedirectError, clearActions, cookies, createActionReference, executeAction, executeFormAction, generateActionId, getAction, getAllActions, handleActionRequest, isRedirectError, parseFormData, redirect, registerAction };
144
+ //# sourceMappingURL=chunk-TKXN7KGE.js.map
145
+ //# sourceMappingURL=chunk-TKXN7KGE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/actions/index.ts"],"names":[],"mappings":"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","file":"chunk-TKXN7KGE.js","sourcesContent":["/**\r\n * @flight/core - Server Actions\r\n * \r\n * Implementation of React 19 style Server Actions.\r\n * Functions marked with 'use server' run on the server.\r\n */\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\n/**\r\n * Server action metadata\r\n */\r\nexport interface ServerAction {\r\n /** Unique action ID */\r\n id: string;\r\n /** Original function name */\r\n name: string;\r\n /** File path where action is defined */\r\n filePath: string;\r\n /** The actual action function */\r\n fn: (...args: unknown[]) => Promise<unknown>;\r\n}\r\n\r\n/**\r\n * Server action result\r\n */\r\nexport interface ActionResult<T = unknown> {\r\n success: boolean;\r\n data?: T;\r\n error?: string;\r\n}\r\n\r\n/**\r\n * Form action data\r\n */\r\nexport interface FormActionData {\r\n actionId: string;\r\n formData: FormData;\r\n}\r\n\r\n// ============================================================================\r\n// Action Registry\r\n// ============================================================================\r\n\r\nconst actionRegistry = new Map<string, ServerAction>();\r\n\r\n/**\r\n * Register a server action\r\n */\r\nexport function registerAction(action: ServerAction): void {\r\n actionRegistry.set(action.id, action);\r\n}\r\n\r\n/**\r\n * Get a registered action by ID\r\n */\r\nexport function getAction(id: string): ServerAction | undefined {\r\n return actionRegistry.get(id);\r\n}\r\n\r\n/**\r\n * Get all registered actions\r\n */\r\nexport function getAllActions(): ServerAction[] {\r\n return Array.from(actionRegistry.values());\r\n}\r\n\r\n/**\r\n * Clear all registered actions (useful for testing)\r\n */\r\nexport function clearActions(): void {\r\n actionRegistry.clear();\r\n}\r\n\r\n// ============================================================================\r\n// Action Execution\r\n// ============================================================================\r\n\r\n/**\r\n * Execute a server action\r\n */\r\nexport async function executeAction<T = unknown>(\r\n actionId: string,\r\n args: unknown[]\r\n): Promise<ActionResult<T>> {\r\n const action = getAction(actionId);\r\n\r\n if (!action) {\r\n return {\r\n success: false,\r\n error: `Action not found: ${actionId}`,\r\n };\r\n }\r\n\r\n try {\r\n const result = await action.fn(...args);\r\n return {\r\n success: true,\r\n data: result as T,\r\n };\r\n } catch (error) {\r\n // Re-throw RedirectError so handleActionRequest can return 303\r\n if (isRedirectError(error)) {\r\n throw error;\r\n }\r\n console.error(`[Flight] Action error (${actionId}):`, error);\r\n return {\r\n success: false,\r\n error: error instanceof Error ? error.message : 'Unknown error',\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Execute a form action\r\n */\r\nexport async function executeFormAction<T = unknown>(\r\n actionId: string,\r\n formData: FormData\r\n): Promise<ActionResult<T>> {\r\n return executeAction<T>(actionId, [formData]);\r\n}\r\n\r\n// ============================================================================\r\n// Action ID Generation\r\n// ============================================================================\r\n\r\nlet actionCounter = 0;\r\n\r\n/**\r\n * Generate a unique action ID\r\n */\r\nexport function generateActionId(name: string, filePath: string): string {\r\n const hash = simpleHash(`${filePath}:${name}`);\r\n return `action_${hash}_${actionCounter++}`;\r\n}\r\n\r\n/**\r\n * Simple hash function for action IDs\r\n */\r\nfunction simpleHash(str: string): string {\r\n let hash = 0;\r\n for (let i = 0; i < str.length; i++) {\r\n const char = str.charCodeAt(i);\r\n hash = ((hash << 5) - hash) + char;\r\n hash = hash & hash; // Convert to 32bit integer\r\n }\r\n return Math.abs(hash).toString(36);\r\n}\r\n\r\n// ============================================================================\r\n// Action Helpers (for use inside actions)\r\n// ============================================================================\r\n\r\n/**\r\n * Get cookies in server action context\r\n */\r\nexport function cookies(): {\r\n get: (name: string) => string | undefined;\r\n set: (name: string, value: string, options?: CookieOptions) => void;\r\n delete: (name: string) => void;\r\n} {\r\n // This will be populated by the runtime\r\n const cookieStore = (globalThis as any).__flightCookies;\r\n\r\n if (!cookieStore) {\r\n console.warn('[Flight] Cookies not available outside of action context');\r\n return {\r\n get: () => undefined,\r\n set: () => { },\r\n delete: () => { },\r\n };\r\n }\r\n\r\n return cookieStore;\r\n}\r\n\r\nexport interface CookieOptions {\r\n maxAge?: number;\r\n expires?: Date;\r\n path?: string;\r\n domain?: string;\r\n secure?: boolean;\r\n httpOnly?: boolean;\r\n sameSite?: 'strict' | 'lax' | 'none';\r\n}\r\n\r\n/**\r\n * Redirect in server action context\r\n */\r\nexport function redirect(url: string): never {\r\n throw new RedirectError(url);\r\n}\r\n\r\n/**\r\n * Special error for redirects\r\n */\r\nexport class RedirectError extends Error {\r\n public readonly url: string;\r\n\r\n constructor(url: string) {\r\n super(`Redirect to ${url}`);\r\n this.name = 'RedirectError';\r\n this.url = url;\r\n }\r\n}\r\n\r\n/**\r\n * Check if error is a redirect\r\n */\r\nexport function isRedirectError(error: unknown): error is RedirectError {\r\n return error instanceof RedirectError;\r\n}\r\n\r\n// ============================================================================\r\n// Form Helpers\r\n// ============================================================================\r\n\r\n/**\r\n * Parse form data to typed object\r\n */\r\nexport function parseFormData<T extends Record<string, string>>(\r\n formData: FormData\r\n): T {\r\n const result: Record<string, string> = {};\r\n\r\n formData.forEach((value, key) => {\r\n result[key] = String(value);\r\n });\r\n\r\n return result as T;\r\n}\r\n\r\n/**\r\n * Create action reference for form action attribute\r\n */\r\nexport function createActionReference(actionId: string): string {\r\n return `/__flight_action/${actionId}`;\r\n}\r\n\r\n// ============================================================================\r\n// Action Handler (for HTTP endpoint)\r\n// ============================================================================\r\n\r\n/**\r\n * Handle action request from client\r\n */\r\nexport async function handleActionRequest(\r\n request: Request\r\n): Promise<Response> {\r\n const url = new URL(request.url);\r\n const actionPath = url.pathname;\r\n\r\n // Extract action ID from path: /__flight_action/{actionId}\r\n const match = actionPath.match(/^\\/__flight_action\\/(.+)$/);\r\n\r\n if (!match) {\r\n return new Response(JSON.stringify({ error: 'Invalid action path' }), {\r\n status: 400,\r\n headers: { 'Content-Type': 'application/json' },\r\n });\r\n }\r\n\r\n const actionId = match[1];\r\n\r\n if (!actionId) {\r\n return new Response(JSON.stringify({ error: 'Missing action ID' }), {\r\n status: 400,\r\n headers: { 'Content-Type': 'application/json' },\r\n });\r\n }\r\n\r\n try {\r\n let result: ActionResult;\r\n\r\n // Check content type for form vs JSON\r\n const contentType = request.headers.get('content-type') || '';\r\n\r\n if (contentType.includes('application/x-www-form-urlencoded') ||\r\n contentType.includes('multipart/form-data')) {\r\n const formData = await request.formData();\r\n result = await executeFormAction(actionId, formData);\r\n } else {\r\n const args = await request.json();\r\n result = await executeAction(actionId, Array.isArray(args) ? args : [args]);\r\n }\r\n\r\n return new Response(JSON.stringify(result), {\r\n status: result.success ? 200 : 400,\r\n headers: { 'Content-Type': 'application/json' },\r\n });\r\n } catch (error) {\r\n if (isRedirectError(error)) {\r\n return new Response(null, {\r\n status: 303,\r\n headers: { Location: error.url },\r\n });\r\n }\r\n\r\n return new Response(JSON.stringify({\r\n success: false,\r\n error: error instanceof Error ? error.message : 'Unknown error',\r\n }), {\r\n status: 500,\r\n headers: { 'Content-Type': 'application/json' },\r\n });\r\n }\r\n}\r\n"]}
@@ -0,0 +1,93 @@
1
+ // src/config/index.ts
2
+ var DEFAULT_CONFIG = {
3
+ adapter: null,
4
+ ui: {
5
+ framework: "vanilla"
6
+ },
7
+ rendering: {
8
+ default: "ssr",
9
+ routes: {}
10
+ },
11
+ dev: {
12
+ port: 5173,
13
+ host: "localhost",
14
+ open: false,
15
+ https: false,
16
+ proxy: {}
17
+ },
18
+ build: {
19
+ outDir: "dist",
20
+ srcDir: "src",
21
+ publicDir: "public",
22
+ routesDir: "src/routes",
23
+ sourcemap: false,
24
+ minify: true,
25
+ target: "es2022"
26
+ }};
27
+ function defineConfig(config) {
28
+ return config;
29
+ }
30
+ function resolveConfig(userConfig = {}) {
31
+ return {
32
+ root: userConfig.root ?? process.cwd(),
33
+ adapter: userConfig.adapter ?? DEFAULT_CONFIG.adapter,
34
+ ui: {
35
+ ...DEFAULT_CONFIG.ui,
36
+ ...userConfig.ui
37
+ },
38
+ rendering: {
39
+ ...DEFAULT_CONFIG.rendering,
40
+ ...userConfig.rendering
41
+ },
42
+ dev: {
43
+ ...DEFAULT_CONFIG.dev,
44
+ ...userConfig.dev
45
+ },
46
+ build: {
47
+ ...DEFAULT_CONFIG.build,
48
+ ...userConfig.build
49
+ },
50
+ vite: userConfig.vite,
51
+ plugins: userConfig.plugins ?? []
52
+ };
53
+ }
54
+ var CONFIG_FILES = [
55
+ "flight.config.ts",
56
+ "flight.config.js",
57
+ "flight.config.mjs",
58
+ "flight.config.mts"
59
+ ];
60
+ async function findConfigFile(root) {
61
+ const { existsSync } = await import('fs');
62
+ const { join } = await import('path');
63
+ for (const file of CONFIG_FILES) {
64
+ const path = join(root, file);
65
+ if (existsSync(path)) {
66
+ return path;
67
+ }
68
+ }
69
+ return null;
70
+ }
71
+ async function loadConfig(root = process.cwd()) {
72
+ const configFile = await findConfigFile(root);
73
+ if (!configFile) {
74
+ return resolveConfig({ root });
75
+ }
76
+ try {
77
+ const { pathToFileURL } = await import('url');
78
+ const configUrl = pathToFileURL(configFile).href;
79
+ const module = await import(configUrl);
80
+ const userConfig = module.default;
81
+ return resolveConfig({
82
+ ...userConfig,
83
+ root
84
+ });
85
+ } catch (error) {
86
+ console.error(`Failed to load config from ${configFile}:`, error);
87
+ return resolveConfig({ root });
88
+ }
89
+ }
90
+
91
+ export { defineConfig, findConfigFile, loadConfig, resolveConfig };
92
+ //# sourceMappingURL=chunk-WAGCTWGY.js.map
93
+ //# sourceMappingURL=chunk-WAGCTWGY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config/index.ts"],"names":[],"mappings":"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","file":"chunk-WAGCTWGY.js","sourcesContent":["/**\r\n * Flight Configuration - User configuration system\r\n */\r\n\r\nimport type { RenderMode } from '../render/index.js';\r\nimport type { FlightAdapter } from '../adapters/index.js';\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\n/** UI Framework configuration */\r\nexport interface UIConfig {\r\n /** The UI framework to use */\r\n framework?: 'react' | 'vue' | 'svelte' | 'solid' | 'preact' | 'vanilla' | string;\r\n /** Framework-specific options */\r\n options?: Record<string, unknown>;\r\n}\r\n\r\n/** Rendering configuration */\r\nexport interface RenderConfig {\r\n /** Default render mode */\r\n default?: RenderMode;\r\n /** Per-route render mode overrides */\r\n routes?: Record<string, RenderMode>;\r\n}\r\n\r\n/** Development server options */\r\nexport interface DevConfig {\r\n /** Port to run dev server on */\r\n port?: number;\r\n /** Host to bind to */\r\n host?: string | boolean;\r\n /** Open browser on start */\r\n open?: boolean;\r\n /** HTTPS configuration */\r\n https?: boolean | {\r\n key?: string;\r\n cert?: string;\r\n };\r\n /** Proxy configuration */\r\n proxy?: Record<string, string | {\r\n target: string;\r\n changeOrigin?: boolean;\r\n rewrite?: (path: string) => string;\r\n }>;\r\n}\r\n\r\n/** Build configuration */\r\nexport interface BuildConfig {\r\n /** Output directory */\r\n outDir?: string;\r\n /** Source directory */\r\n srcDir?: string;\r\n /** Public assets directory */\r\n publicDir?: string;\r\n /** Routes directory */\r\n routesDir?: string;\r\n /** Generate sourcemaps */\r\n sourcemap?: boolean | 'inline' | 'hidden';\r\n /** Minify output */\r\n minify?: boolean | 'terser' | 'esbuild';\r\n /** Target environments */\r\n target?: string | string[];\r\n}\r\n\r\n/** Full Flight configuration (resolved) */\r\nexport interface FlightConfig {\r\n /** Root directory */\r\n root: string;\r\n /** Deployment adapter */\r\n adapter: FlightAdapter | null;\r\n /** UI framework configuration */\r\n ui: UIConfig;\r\n /** Rendering configuration */\r\n rendering: RenderConfig;\r\n /** Development server options */\r\n dev: Required<DevConfig>;\r\n /** Build configuration */\r\n build: Required<BuildConfig>;\r\n /** Vite configuration overrides */\r\n vite?: Record<string, unknown>;\r\n /** Plugin configurations */\r\n plugins: FlightPlugin[];\r\n}\r\n\r\n/** Flight plugin interface */\r\nexport interface FlightPlugin {\r\n /** Plugin name */\r\n name: string;\r\n /** Hook into config resolution */\r\n config?: (config: FlightConfig) => FlightConfig | void | Promise<FlightConfig | void>;\r\n /** Hook into build start */\r\n buildStart?: () => void | Promise<void>;\r\n /** Hook into build end */\r\n buildEnd?: () => void | Promise<void>;\r\n /** Hook into dev server start */\r\n devStart?: () => void | Promise<void>;\r\n}\r\n\r\n/** User-provided configuration (partial) */\r\nexport interface FlightUserConfig {\r\n /** Root directory (defaults to process.cwd()) */\r\n root?: string;\r\n /** Deployment adapter */\r\n adapter?: FlightAdapter;\r\n /** UI framework configuration */\r\n ui?: UIConfig;\r\n /** Rendering configuration */\r\n rendering?: RenderConfig;\r\n /** Development server options */\r\n dev?: DevConfig;\r\n /** Build configuration */\r\n build?: BuildConfig;\r\n /** Vite configuration overrides */\r\n vite?: Record<string, unknown>;\r\n /** Plugins */\r\n plugins?: FlightPlugin[];\r\n}\r\n\r\n// ============================================================================\r\n// Configuration Factory\r\n// ============================================================================\r\n\r\n/** Default configuration values */\r\nconst DEFAULT_CONFIG: Omit<FlightConfig, 'root'> = {\r\n adapter: null,\r\n ui: {\r\n framework: 'vanilla',\r\n },\r\n rendering: {\r\n default: 'ssr',\r\n routes: {},\r\n },\r\n dev: {\r\n port: 5173,\r\n host: 'localhost',\r\n open: false,\r\n https: false,\r\n proxy: {},\r\n },\r\n build: {\r\n outDir: 'dist',\r\n srcDir: 'src',\r\n publicDir: 'public',\r\n routesDir: 'src/routes',\r\n sourcemap: false,\r\n minify: true,\r\n target: 'es2022',\r\n },\r\n plugins: [],\r\n};\r\n\r\n/**\r\n * Define Flight configuration\r\n * \r\n * @example\r\n * ```typescript\r\n * // flight.config.ts\r\n * import { defineConfig } from '@flight/core';\r\n * import node from '@flight/adapter-node';\r\n * \r\n * export default defineConfig({\r\n * adapter: node(),\r\n * ui: { framework: 'react' },\r\n * rendering: {\r\n * default: 'ssr',\r\n * routes: {\r\n * '/blog/*': 'ssg',\r\n * '/app/*': 'csr',\r\n * },\r\n * },\r\n * });\r\n * ```\r\n */\r\nexport function defineConfig(config: FlightUserConfig): FlightUserConfig {\r\n return config;\r\n}\r\n\r\n/**\r\n * Resolve user config to full config with defaults\r\n */\r\nexport function resolveConfig(userConfig: FlightUserConfig = {}): FlightConfig {\r\n return {\r\n root: userConfig.root ?? process.cwd(),\r\n adapter: userConfig.adapter ?? DEFAULT_CONFIG.adapter,\r\n ui: {\r\n ...DEFAULT_CONFIG.ui,\r\n ...userConfig.ui,\r\n },\r\n rendering: {\r\n ...DEFAULT_CONFIG.rendering,\r\n ...userConfig.rendering,\r\n },\r\n dev: {\r\n ...DEFAULT_CONFIG.dev,\r\n ...userConfig.dev,\r\n } as Required<DevConfig>,\r\n build: {\r\n ...DEFAULT_CONFIG.build,\r\n ...userConfig.build,\r\n } as Required<BuildConfig>,\r\n vite: userConfig.vite,\r\n plugins: userConfig.plugins ?? [],\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// Config Loading\r\n// ============================================================================\r\n\r\nconst CONFIG_FILES = [\r\n 'flight.config.ts',\r\n 'flight.config.js',\r\n 'flight.config.mjs',\r\n 'flight.config.mts',\r\n];\r\n\r\n/**\r\n * Find the config file in a directory\r\n */\r\nexport async function findConfigFile(root: string): Promise<string | null> {\r\n // Dynamic import for Node.js fs\r\n const { existsSync } = await import('node:fs');\r\n const { join } = await import('node:path');\r\n\r\n for (const file of CONFIG_FILES) {\r\n const path = join(root, file);\r\n if (existsSync(path)) {\r\n return path;\r\n }\r\n }\r\n\r\n return null;\r\n}\r\n\r\n/**\r\n * Load configuration from file\r\n */\r\nexport async function loadConfig(root: string = process.cwd()): Promise<FlightConfig> {\r\n const configFile = await findConfigFile(root);\r\n\r\n if (!configFile) {\r\n return resolveConfig({ root });\r\n }\r\n\r\n try {\r\n // Use dynamic import for the config file\r\n // On Windows, we need to convert the path to a file:// URL\r\n const { pathToFileURL } = await import('node:url');\r\n const configUrl = pathToFileURL(configFile).href;\r\n const module = await import(configUrl);\r\n const userConfig = module.default as FlightUserConfig;\r\n\r\n return resolveConfig({\r\n ...userConfig,\r\n root,\r\n });\r\n } catch (error) {\r\n console.error(`Failed to load config from ${configFile}:`, error);\r\n return resolveConfig({ root });\r\n }\r\n}\r\n"]}