@flightdev/core 0.6.7

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 (187) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +541 -0
  3. package/dist/actions/index.d.ts +743 -0
  4. package/dist/actions/index.js +3 -0
  5. package/dist/actions/index.js.map +1 -0
  6. package/dist/adapters/index.d.ts +502 -0
  7. package/dist/adapters/index.js +3 -0
  8. package/dist/adapters/index.js.map +1 -0
  9. package/dist/cache/index.d.ts +191 -0
  10. package/dist/cache/index.js +3 -0
  11. package/dist/cache/index.js.map +1 -0
  12. package/dist/chunk-62HISNA3.js +354 -0
  13. package/dist/chunk-62HISNA3.js.map +1 -0
  14. package/dist/chunk-63LWTEDQ.js +341 -0
  15. package/dist/chunk-63LWTEDQ.js.map +1 -0
  16. package/dist/chunk-63SCEXD7.js +3 -0
  17. package/dist/chunk-63SCEXD7.js.map +1 -0
  18. package/dist/chunk-72MYOTUB.js +667 -0
  19. package/dist/chunk-72MYOTUB.js.map +1 -0
  20. package/dist/chunk-7CNW24MQ.js +257 -0
  21. package/dist/chunk-7CNW24MQ.js.map +1 -0
  22. package/dist/chunk-7WIEAUJT.js +300 -0
  23. package/dist/chunk-7WIEAUJT.js.map +1 -0
  24. package/dist/chunk-7ZZF4ULK.js +259 -0
  25. package/dist/chunk-7ZZF4ULK.js.map +1 -0
  26. package/dist/chunk-AE3JTS73.js +222 -0
  27. package/dist/chunk-AE3JTS73.js.map +1 -0
  28. package/dist/chunk-AP5NLUSB.js +258 -0
  29. package/dist/chunk-AP5NLUSB.js.map +1 -0
  30. package/dist/chunk-C37YQQI7.js +221 -0
  31. package/dist/chunk-C37YQQI7.js.map +1 -0
  32. package/dist/chunk-DCLVXFVH.js +225 -0
  33. package/dist/chunk-DCLVXFVH.js.map +1 -0
  34. package/dist/chunk-DZMWWDFD.js +223 -0
  35. package/dist/chunk-DZMWWDFD.js.map +1 -0
  36. package/dist/chunk-GCQZ4FHI.js +245 -0
  37. package/dist/chunk-GCQZ4FHI.js.map +1 -0
  38. package/dist/chunk-IPP44XY6.js +47 -0
  39. package/dist/chunk-IPP44XY6.js.map +1 -0
  40. package/dist/chunk-IW7FTQQX.js +267 -0
  41. package/dist/chunk-IW7FTQQX.js.map +1 -0
  42. package/dist/chunk-JX4YSCBH.js +428 -0
  43. package/dist/chunk-JX4YSCBH.js.map +1 -0
  44. package/dist/chunk-KX6UYWWR.js +229 -0
  45. package/dist/chunk-KX6UYWWR.js.map +1 -0
  46. package/dist/chunk-LWVETFJV.js +46 -0
  47. package/dist/chunk-LWVETFJV.js.map +1 -0
  48. package/dist/chunk-MCL2MCA2.js +285 -0
  49. package/dist/chunk-MCL2MCA2.js.map +1 -0
  50. package/dist/chunk-MZXCF35B.js +205 -0
  51. package/dist/chunk-MZXCF35B.js.map +1 -0
  52. package/dist/chunk-NCGPUFWV.js +96 -0
  53. package/dist/chunk-NCGPUFWV.js.map +1 -0
  54. package/dist/chunk-OEJMIE2Q.js +351 -0
  55. package/dist/chunk-OEJMIE2Q.js.map +1 -0
  56. package/dist/chunk-OYF2OAKS.js +394 -0
  57. package/dist/chunk-OYF2OAKS.js.map +1 -0
  58. package/dist/chunk-P6S43FYZ.js +316 -0
  59. package/dist/chunk-P6S43FYZ.js.map +1 -0
  60. package/dist/chunk-PL37KFRJ.js +3 -0
  61. package/dist/chunk-PL37KFRJ.js.map +1 -0
  62. package/dist/chunk-Q7BS5QC5.js +197 -0
  63. package/dist/chunk-Q7BS5QC5.js.map +1 -0
  64. package/dist/chunk-SDYPG3JD.js +288 -0
  65. package/dist/chunk-SDYPG3JD.js.map +1 -0
  66. package/dist/chunk-SUG56SZO.js +256 -0
  67. package/dist/chunk-SUG56SZO.js.map +1 -0
  68. package/dist/chunk-UVH5XJRP.js +164 -0
  69. package/dist/chunk-UVH5XJRP.js.map +1 -0
  70. package/dist/chunk-WZIJKCL3.js +282 -0
  71. package/dist/chunk-WZIJKCL3.js.map +1 -0
  72. package/dist/chunk-Y22AMGTM.js +3 -0
  73. package/dist/chunk-Y22AMGTM.js.map +1 -0
  74. package/dist/chunk-Z7G23XWU.js +200 -0
  75. package/dist/chunk-Z7G23XWU.js.map +1 -0
  76. package/dist/chunk-ZJU5M4IB.js +125 -0
  77. package/dist/chunk-ZJU5M4IB.js.map +1 -0
  78. package/dist/chunk-ZVC3ZWLM.js +52 -0
  79. package/dist/chunk-ZVC3ZWLM.js.map +1 -0
  80. package/dist/chunk-ZZZML7Y3.js +310 -0
  81. package/dist/chunk-ZZZML7Y3.js.map +1 -0
  82. package/dist/client.d.ts +25 -0
  83. package/dist/client.js +16 -0
  84. package/dist/client.js.map +1 -0
  85. package/dist/config/index.d.ts +170 -0
  86. package/dist/config/index.js +3 -0
  87. package/dist/config/index.js.map +1 -0
  88. package/dist/errors/index.d.ts +267 -0
  89. package/dist/errors/index.js +4 -0
  90. package/dist/errors/index.js.map +1 -0
  91. package/dist/file-router/index.d.ts +184 -0
  92. package/dist/file-router/index.js +3 -0
  93. package/dist/file-router/index.js.map +1 -0
  94. package/dist/file-router/streaming-hints.d.ts +129 -0
  95. package/dist/file-router/streaming-hints.js +3 -0
  96. package/dist/file-router/streaming-hints.js.map +1 -0
  97. package/dist/handlers/index.d.ts +59 -0
  98. package/dist/handlers/index.js +3 -0
  99. package/dist/handlers/index.js.map +1 -0
  100. package/dist/index.d.ts +588 -0
  101. package/dist/index.js +886 -0
  102. package/dist/index.js.map +1 -0
  103. package/dist/islands/index.d.ts +234 -0
  104. package/dist/islands/index.js +3 -0
  105. package/dist/islands/index.js.map +1 -0
  106. package/dist/middleware/index.d.ts +305 -0
  107. package/dist/middleware/index.js +3 -0
  108. package/dist/middleware/index.js.map +1 -0
  109. package/dist/react/index.d.ts +73 -0
  110. package/dist/react/index.js +52 -0
  111. package/dist/react/index.js.map +1 -0
  112. package/dist/render/index.d.ts +131 -0
  113. package/dist/render/index.js +3 -0
  114. package/dist/render/index.js.map +1 -0
  115. package/dist/router/index.d.ts +65 -0
  116. package/dist/router/index.js +3 -0
  117. package/dist/router/index.js.map +1 -0
  118. package/dist/rsc/adapters/index.d.ts +8 -0
  119. package/dist/rsc/adapters/index.js +7 -0
  120. package/dist/rsc/adapters/index.js.map +1 -0
  121. package/dist/rsc/adapters/preact.d.ts +97 -0
  122. package/dist/rsc/adapters/preact.js +3 -0
  123. package/dist/rsc/adapters/preact.js.map +1 -0
  124. package/dist/rsc/adapters/react.d.ts +82 -0
  125. package/dist/rsc/adapters/react.js +3 -0
  126. package/dist/rsc/adapters/react.js.map +1 -0
  127. package/dist/rsc/adapters/solid.d.ts +84 -0
  128. package/dist/rsc/adapters/solid.js +3 -0
  129. package/dist/rsc/adapters/solid.js.map +1 -0
  130. package/dist/rsc/adapters/vue.d.ts +80 -0
  131. package/dist/rsc/adapters/vue.js +3 -0
  132. package/dist/rsc/adapters/vue.js.map +1 -0
  133. package/dist/rsc/boundaries.d.ts +182 -0
  134. package/dist/rsc/boundaries.js +3 -0
  135. package/dist/rsc/boundaries.js.map +1 -0
  136. package/dist/rsc/context.d.ts +201 -0
  137. package/dist/rsc/context.js +3 -0
  138. package/dist/rsc/context.js.map +1 -0
  139. package/dist/rsc/index.d.ts +232 -0
  140. package/dist/rsc/index.js +15 -0
  141. package/dist/rsc/index.js.map +1 -0
  142. package/dist/rsc/legacy.d.ts +155 -0
  143. package/dist/rsc/legacy.js +3 -0
  144. package/dist/rsc/legacy.js.map +1 -0
  145. package/dist/rsc/payload.d.ts +262 -0
  146. package/dist/rsc/payload.js +3 -0
  147. package/dist/rsc/payload.js.map +1 -0
  148. package/dist/rsc/plugins/esbuild.d.ts +124 -0
  149. package/dist/rsc/plugins/esbuild.js +4 -0
  150. package/dist/rsc/plugins/esbuild.js.map +1 -0
  151. package/dist/rsc/plugins/index.d.ts +4 -0
  152. package/dist/rsc/plugins/index.js +6 -0
  153. package/dist/rsc/plugins/index.js.map +1 -0
  154. package/dist/rsc/plugins/rollup.d.ts +103 -0
  155. package/dist/rsc/plugins/rollup.js +4 -0
  156. package/dist/rsc/plugins/rollup.js.map +1 -0
  157. package/dist/rsc/renderer.d.ts +162 -0
  158. package/dist/rsc/renderer.js +5 -0
  159. package/dist/rsc/renderer.js.map +1 -0
  160. package/dist/rsc/stream.d.ts +129 -0
  161. package/dist/rsc/stream.js +3 -0
  162. package/dist/rsc/stream.js.map +1 -0
  163. package/dist/rsc/vite-plugin.d.ts +78 -0
  164. package/dist/rsc/vite-plugin.js +4 -0
  165. package/dist/rsc/vite-plugin.js.map +1 -0
  166. package/dist/server/index.d.ts +135 -0
  167. package/dist/server/index.js +6 -0
  168. package/dist/server/index.js.map +1 -0
  169. package/dist/streaming/adapters/index.d.ts +223 -0
  170. package/dist/streaming/adapters/index.js +3 -0
  171. package/dist/streaming/adapters/index.js.map +1 -0
  172. package/dist/streaming/conditional.d.ts +130 -0
  173. package/dist/streaming/conditional.js +3 -0
  174. package/dist/streaming/conditional.js.map +1 -0
  175. package/dist/streaming/index.d.ts +177 -0
  176. package/dist/streaming/index.js +3 -0
  177. package/dist/streaming/index.js.map +1 -0
  178. package/dist/streaming/observability.d.ts +201 -0
  179. package/dist/streaming/observability.js +4 -0
  180. package/dist/streaming/observability.js.map +1 -0
  181. package/dist/streaming/priority.d.ts +103 -0
  182. package/dist/streaming/priority.js +3 -0
  183. package/dist/streaming/priority.js.map +1 -0
  184. package/dist/utils/index.d.ts +42 -0
  185. package/dist/utils/index.js +4 -0
  186. package/dist/utils/index.js.map +1 -0
  187. package/package.json +228 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2026 Flight Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,541 @@
1
+ # @flight-framework/core
2
+
3
+ Core primitives for Flight Framework, including configuration, routing, caching, streaming SSR, and server actions.
4
+
5
+ ## Features
6
+
7
+ - Multi-render mode: SSR, SSG, ISR, and streaming
8
+ - Framework support: React, Vue, Svelte, Solid, HTMX
9
+ - File-based routing with automatic route discovery
10
+ - Type-safe server actions with form support
11
+ - Streaming SSR with priority control
12
+ - Islands architecture for partial hydration
13
+ - Pluggable cache adapters with deduplication
14
+ - Structured error handling with type guards
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install @flight-framework/core
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ### Configuration
25
+
26
+ ```typescript
27
+ // flight.config.ts
28
+ import { defineConfig } from '@flight-framework/core';
29
+
30
+ export default defineConfig({
31
+ server: {
32
+ port: 3000,
33
+ },
34
+ render: {
35
+ defaultMode: 'ssr',
36
+ streaming: true,
37
+ },
38
+ });
39
+ ```
40
+
41
+ ### Create a Server
42
+
43
+ ```typescript
44
+ import { createServer } from '@flight-framework/core';
45
+
46
+ const server = createServer({
47
+ port: 3000,
48
+ routes: [
49
+ { path: '/', handler: homeHandler },
50
+ { path: '/api/*', handler: apiHandler },
51
+ ],
52
+ });
53
+
54
+ await server.start();
55
+ ```
56
+
57
+ ### File-Based Routing
58
+
59
+ ```typescript
60
+ import { createFileRouter, scanRoutes } from '@flight-framework/core';
61
+
62
+ const routes = await scanRoutes('./src/routes');
63
+ const router = createFileRouter({ routes });
64
+
65
+ // Routes are discovered automatically:
66
+ // src/routes/index.page.tsx -> /
67
+ // src/routes/about.page.tsx -> /about
68
+ // src/routes/blog/[slug].page.tsx -> /blog/:slug
69
+ ```
70
+
71
+ ## Modules
72
+
73
+ ### Router
74
+
75
+ ```typescript
76
+ import { createRouter } from '@flight-framework/core/router';
77
+
78
+ const router = createRouter();
79
+ router.add('GET', '/users/:id', userHandler);
80
+
81
+ const match = router.match('GET', '/users/123');
82
+ // { params: { id: '123' }, handler: userHandler }
83
+ ```
84
+
85
+ ### Cache
86
+
87
+ ```typescript
88
+ import { createCache, memory, cached, dedupe } from '@flight-framework/core/cache';
89
+
90
+ // Create a cache with memory adapter
91
+ const cache = createCache({
92
+ adapter: memory(),
93
+ ttl: 60000,
94
+ });
95
+
96
+ // Cache function results
97
+ const getUser = cached(
98
+ async (id: string) => fetchUser(id),
99
+ { ttl: 5000, key: (id) => `user:${id}` }
100
+ );
101
+
102
+ // Deduplicate concurrent requests
103
+ const getData = dedupe(fetchData);
104
+ ```
105
+
106
+ ### Server Actions
107
+
108
+ ```typescript
109
+ import { registerAction, executeAction, cookies } from '@flight-framework/core/actions';
110
+
111
+ // Register a server action
112
+ registerAction('createUser', async (formData: FormData) => {
113
+ const name = formData.get('name');
114
+ const user = await db.users.create({ name });
115
+
116
+ cookies().set('user_id', user.id);
117
+
118
+ return { success: true, user };
119
+ });
120
+
121
+ // Execute from client
122
+ const result = await executeAction('createUser', formData);
123
+ ```
124
+
125
+ ### Streaming SSR
126
+
127
+ ```typescript
128
+ import {
129
+ createStreamingSSR,
130
+ streamWithPriority
131
+ } from '@flight-framework/core/streaming';
132
+
133
+ // Basic streaming
134
+ const stream = await createStreamingSSR({
135
+ component: App,
136
+ props: { data },
137
+ });
138
+
139
+ // Priority-based streaming (out-of-order)
140
+ const result = await streamWithPriority({
141
+ boundaries: [
142
+ { id: 'header', priority: 100, render: Header },
143
+ { id: 'sidebar', priority: 50, render: Sidebar },
144
+ { id: 'content', priority: 75, render: Content },
145
+ ],
146
+ });
147
+ ```
148
+
149
+ ### Islands Architecture
150
+
151
+ ```typescript
152
+ import {
153
+ defineIsland,
154
+ hydrateIslands,
155
+ createReactIslandAdapter
156
+ } from '@flight-framework/core/islands';
157
+
158
+ // Define an island component
159
+ const Counter = defineIsland({
160
+ name: 'counter',
161
+ component: CounterComponent,
162
+ hydrate: 'visible', // 'load' | 'visible' | 'idle' | 'interaction'
163
+ });
164
+
165
+ // Client-side hydration
166
+ hydrateIslands({
167
+ adapter: createReactIslandAdapter(),
168
+ });
169
+ ```
170
+
171
+ ### Middleware
172
+
173
+ Flight provides a composable middleware system for request/response handling.
174
+
175
+ #### Middleware Chain
176
+
177
+ ```typescript
178
+ import {
179
+ createMiddlewareChain,
180
+ cors,
181
+ logger,
182
+ securityHeaders
183
+ } from '@flight-framework/core/middleware';
184
+
185
+ const chain = createMiddlewareChain();
186
+
187
+ chain
188
+ .use(logger())
189
+ .use(cors({ origin: ['https://app.example.com'] }))
190
+ .use(securityHeaders())
191
+ .use(async (ctx, next) => {
192
+ const start = Date.now();
193
+ await next();
194
+ console.log(`Request took ${Date.now() - start}ms`);
195
+ });
196
+ ```
197
+
198
+ #### Error Handling
199
+
200
+ Centralized error handling with the `errorHandler` factory:
201
+
202
+ ```typescript
203
+ import { createMiddlewareChain, errorHandler } from '@flight-framework/core/middleware';
204
+
205
+ const chain = createMiddlewareChain();
206
+
207
+ // Place error handler first in the chain
208
+ chain.use(errorHandler({
209
+ expose: process.env.NODE_ENV === 'development',
210
+ emit: (error, ctx) => {
211
+ logger.error(`[${ctx.method}] ${ctx.url.pathname}:`, error);
212
+ errorTracker.capture(error);
213
+ },
214
+ }));
215
+
216
+ chain.use(authMiddleware);
217
+ chain.use(routeHandler);
218
+ ```
219
+
220
+ The error handler catches all downstream errors, sets appropriate status codes, and supports custom error handlers:
221
+
222
+ ```typescript
223
+ chain.use(errorHandler({
224
+ onError: async ({ error, status, ctx, timestamp }) => {
225
+ ctx.status = status;
226
+ ctx.responseBody = JSON.stringify({
227
+ error: error.message,
228
+ timestamp,
229
+ requestId: ctx.locals.requestId,
230
+ });
231
+ },
232
+ }));
233
+ ```
234
+
235
+ #### Typed Context
236
+
237
+ Middleware context supports generics for type-safe data sharing:
238
+
239
+ ```typescript
240
+ import type { Middleware, MiddlewareContext } from '@flight-framework/core/middleware';
241
+
242
+ interface AppLocals {
243
+ user: { id: string; role: string };
244
+ requestId: string;
245
+ db: DatabaseClient;
246
+ }
247
+
248
+ const authMiddleware: Middleware<AppLocals> = async (ctx, next) => {
249
+ const token = ctx.headers.get('Authorization');
250
+ const user = await verifyToken(token);
251
+
252
+ ctx.locals.user = user;
253
+ ctx.locals.requestId = crypto.randomUUID();
254
+
255
+ await next();
256
+ };
257
+
258
+ // Type-safe access in subsequent middleware
259
+ const roleGuard: Middleware<AppLocals> = async (ctx, next) => {
260
+ if (ctx.locals.user.role !== 'admin') {
261
+ ctx.status = 403;
262
+ ctx.responseBody = 'Forbidden';
263
+ return;
264
+ }
265
+ await next();
266
+ };
267
+ ```
268
+
269
+ #### CORS
270
+
271
+ CORS middleware with dynamic origin validation and CDN compatibility:
272
+
273
+ ```typescript
274
+ import { cors } from '@flight-framework/core/middleware';
275
+
276
+ // Static origins
277
+ chain.use(cors({
278
+ origin: ['https://app.example.com', 'https://admin.example.com'],
279
+ methods: ['GET', 'POST', 'PUT', 'DELETE'],
280
+ credentials: true,
281
+ }));
282
+
283
+ // Async validation (database lookup)
284
+ chain.use(cors({
285
+ origin: async (requestOrigin) => {
286
+ return await db.allowedOrigins.exists(requestOrigin);
287
+ },
288
+ exposeHeaders: ['X-Request-Id', 'X-RateLimit-Remaining'],
289
+ }));
290
+ ```
291
+
292
+ Dynamic origins automatically set `Vary: Origin` for CDN/cache compatibility.
293
+
294
+ #### Built-in Middleware
295
+
296
+ | Middleware | Purpose |
297
+ |------------|---------|
298
+ | `cors(options?)` | Cross-origin resource sharing |
299
+ | `logger(options?)` | Request logging with configurable levels |
300
+ | `securityHeaders(options?)` | Security headers (CSP, X-Frame-Options, etc.) |
301
+ | `errorHandler(options?)` | Centralized error handling |
302
+ | `compress()` | Mark responses for compression |
303
+
304
+ #### Logger Configuration
305
+
306
+ ```typescript
307
+ import { logger } from '@flight-framework/core/middleware';
308
+
309
+ chain.use(logger({
310
+ level: 'info', // 'debug' | 'info' | 'warn' | 'error' | 'silent'
311
+ format: 'json', // 'pretty' | 'json' | 'combined' | 'common' | 'short'
312
+ skip: (ctx) => ctx.url.pathname === '/health',
313
+ writer: (entry, formatted) => externalLogger.log(entry),
314
+ }));
315
+ ```
316
+
317
+ The logger captures errors from downstream middleware before re-throwing, ensuring all requests are logged including failures.
318
+
319
+
320
+ ### Error Handling
321
+
322
+ ```typescript
323
+ import {
324
+ FlightError,
325
+ createError,
326
+ isFlightError,
327
+ createNotFound,
328
+ createForbidden,
329
+ } from '@flight-framework/core/errors';
330
+
331
+ // Create typed errors
332
+ throw createNotFound('Page not found');
333
+ throw createForbidden('Access denied');
334
+
335
+ // Create custom errors
336
+ throw createError({
337
+ statusCode: 422,
338
+ message: 'Validation failed',
339
+ data: { field: 'email', error: 'Invalid format' },
340
+ });
341
+
342
+ // Type guards
343
+ if (isFlightError(error)) {
344
+ console.log(error.statusCode, error.digest);
345
+ }
346
+ ```
347
+
348
+ ### React Integration
349
+
350
+ ```typescript
351
+ import { ErrorProvider, useError, useFlightError } from '@flight-framework/core/react';
352
+
353
+ // Wrap your app
354
+ <ErrorProvider onError={console.error}>
355
+ <App />
356
+ </ErrorProvider>
357
+
358
+ // Use in components
359
+ function MyComponent() {
360
+ const { error, clearError } = useFlightError();
361
+
362
+ if (error) {
363
+ return <ErrorDisplay error={error} onRetry={clearError} />;
364
+ }
365
+
366
+ return <Content />;
367
+ }
368
+ ```
369
+
370
+ ### Metadata (SEO)
371
+
372
+ ```typescript
373
+ import { renderMetadataToHead, type Metadata } from '@flight-framework/core';
374
+
375
+ const metadata: Metadata = {
376
+ title: 'My Page',
377
+ description: 'Page description',
378
+ openGraph: {
379
+ title: 'OG Title',
380
+ image: '/og-image.png',
381
+ },
382
+ };
383
+
384
+ const headHtml = renderMetadataToHead(metadata);
385
+ ```
386
+
387
+ ### Route Rules (ISR/SSG)
388
+
389
+ ```typescript
390
+ import { defineConfig } from '@flight-framework/core';
391
+
392
+ export default defineConfig({
393
+ routeRules: {
394
+ '/': { prerender: true },
395
+ '/blog/**': { isr: 3600 },
396
+ '/api/**': { ssr: true },
397
+ '/static/**': { static: true },
398
+ },
399
+ });
400
+ ```
401
+
402
+ ### Revalidation
403
+
404
+ ```typescript
405
+ import {
406
+ revalidatePath,
407
+ revalidateTag,
408
+ createRevalidateHandler
409
+ } from '@flight-framework/core';
410
+
411
+ // On-demand revalidation
412
+ await revalidatePath('/blog/my-post');
413
+ await revalidateTag('blog-posts');
414
+
415
+ // Create revalidation API handler
416
+ const handler = createRevalidateHandler({
417
+ secret: process.env.REVALIDATE_SECRET,
418
+ });
419
+ ```
420
+
421
+ ## Environment Utilities
422
+
423
+ ```typescript
424
+ import {
425
+ isServer,
426
+ isBrowser,
427
+ isProduction,
428
+ isDevelopment
429
+ } from '@flight-framework/core/utils';
430
+
431
+ if (isServer()) {
432
+ // Server-only code
433
+ }
434
+
435
+ if (isDevelopment()) {
436
+ console.log('Debug info');
437
+ }
438
+ ```
439
+
440
+ ## Module Exports
441
+
442
+ | Export | Description |
443
+ |--------|-------------|
444
+ | `@flight-framework/core` | Main entry with all primitives |
445
+ | `@flight-framework/core/router` | Routing utilities |
446
+ | `@flight-framework/core/cache` | Caching system |
447
+ | `@flight-framework/core/server` | HTTP server |
448
+ | `@flight-framework/core/render` | Rendering modes |
449
+ | `@flight-framework/core/middleware` | Middleware chain |
450
+ | `@flight-framework/core/actions` | Server actions |
451
+ | `@flight-framework/core/streaming` | Streaming SSR |
452
+ | `@flight-framework/core/islands` | Islands architecture |
453
+ | `@flight-framework/core/errors` | Error handling |
454
+ | `@flight-framework/core/react` | React integration |
455
+ | `@flight-framework/core/rsc` | React Server Components |
456
+ | `@flight-framework/core/config` | Configuration |
457
+ | `@flight-framework/core/utils` | Environment utilities |
458
+
459
+ ## TypeScript
460
+
461
+ Full TypeScript support with exported types for all APIs:
462
+
463
+ ```typescript
464
+ import type {
465
+ FlightConfig,
466
+ Route,
467
+ RouteMatch,
468
+ Cache,
469
+ CacheAdapter,
470
+ Middleware,
471
+ FlightError,
472
+ Metadata,
473
+ StreamingHints,
474
+ } from '@flight-framework/core';
475
+ ```
476
+
477
+ ## Build Plugins
478
+
479
+ ### Critical CSS Extraction
480
+
481
+ Extract and inline critical CSS for improved LCP:
482
+
483
+ ```bash
484
+ npm install critters
485
+ ```
486
+
487
+ ```typescript
488
+ // vite.config.ts
489
+ import { criticalCSS } from '@flight-framework/core/plugins';
490
+
491
+ export default defineConfig({
492
+ plugins: [
493
+ criticalCSS({
494
+ // Strategy for loading non-critical CSS
495
+ preload: 'swap', // 'body' | 'media' | 'swap' | 'swap-high' | 'js' | 'js-lazy'
496
+
497
+ // Remove inlined CSS from source
498
+ pruneSource: false,
499
+
500
+ // Routes to process
501
+ include: ['**/*.html'],
502
+ exclude: ['/api/**'],
503
+ }),
504
+ ],
505
+ });
506
+ ```
507
+
508
+ #### Preload Strategies
509
+
510
+ | Strategy | Description |
511
+ |----------|-------------|
512
+ | `swap` | Use rel="preload" and swap on load |
513
+ | `swap-high` | Like swap with fetchpriority="high" |
514
+ | `media` | Use media="print" and swap |
515
+ | `js` | Load via JavaScript |
516
+ | `js-lazy` | Load via JavaScript when idle |
517
+ | `body` | Move stylesheets to end of body |
518
+
519
+ #### CSS Utilities
520
+
521
+ ```typescript
522
+ import {
523
+ extractInlineStyles,
524
+ mergeCSS,
525
+ generatePreloadLink,
526
+ generateNoscriptFallback,
527
+ } from '@flight-framework/core/plugins/critical-css';
528
+
529
+ // Extract styles from HTML
530
+ const { html, styles } = extractInlineStyles(htmlString);
531
+
532
+ // Generate preload link
533
+ const preload = generatePreloadLink('/styles.css', 'swap');
534
+
535
+ // Generate noscript fallback
536
+ const fallback = generateNoscriptFallback('/styles.css');
537
+ ```
538
+
539
+ ## License
540
+
541
+ MIT