@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.
- package/LICENSE +21 -0
- package/README.md +541 -0
- package/dist/actions/index.d.ts +743 -0
- package/dist/actions/index.js +3 -0
- package/dist/actions/index.js.map +1 -0
- package/dist/adapters/index.d.ts +502 -0
- package/dist/adapters/index.js +3 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/cache/index.d.ts +191 -0
- package/dist/cache/index.js +3 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/chunk-62HISNA3.js +354 -0
- package/dist/chunk-62HISNA3.js.map +1 -0
- package/dist/chunk-63LWTEDQ.js +341 -0
- package/dist/chunk-63LWTEDQ.js.map +1 -0
- package/dist/chunk-63SCEXD7.js +3 -0
- package/dist/chunk-63SCEXD7.js.map +1 -0
- package/dist/chunk-72MYOTUB.js +667 -0
- package/dist/chunk-72MYOTUB.js.map +1 -0
- package/dist/chunk-7CNW24MQ.js +257 -0
- package/dist/chunk-7CNW24MQ.js.map +1 -0
- package/dist/chunk-7WIEAUJT.js +300 -0
- package/dist/chunk-7WIEAUJT.js.map +1 -0
- package/dist/chunk-7ZZF4ULK.js +259 -0
- package/dist/chunk-7ZZF4ULK.js.map +1 -0
- package/dist/chunk-AE3JTS73.js +222 -0
- package/dist/chunk-AE3JTS73.js.map +1 -0
- package/dist/chunk-AP5NLUSB.js +258 -0
- package/dist/chunk-AP5NLUSB.js.map +1 -0
- package/dist/chunk-C37YQQI7.js +221 -0
- package/dist/chunk-C37YQQI7.js.map +1 -0
- package/dist/chunk-DCLVXFVH.js +225 -0
- package/dist/chunk-DCLVXFVH.js.map +1 -0
- package/dist/chunk-DZMWWDFD.js +223 -0
- package/dist/chunk-DZMWWDFD.js.map +1 -0
- package/dist/chunk-GCQZ4FHI.js +245 -0
- package/dist/chunk-GCQZ4FHI.js.map +1 -0
- package/dist/chunk-IPP44XY6.js +47 -0
- package/dist/chunk-IPP44XY6.js.map +1 -0
- package/dist/chunk-IW7FTQQX.js +267 -0
- package/dist/chunk-IW7FTQQX.js.map +1 -0
- package/dist/chunk-JX4YSCBH.js +428 -0
- package/dist/chunk-JX4YSCBH.js.map +1 -0
- package/dist/chunk-KX6UYWWR.js +229 -0
- package/dist/chunk-KX6UYWWR.js.map +1 -0
- package/dist/chunk-LWVETFJV.js +46 -0
- package/dist/chunk-LWVETFJV.js.map +1 -0
- package/dist/chunk-MCL2MCA2.js +285 -0
- package/dist/chunk-MCL2MCA2.js.map +1 -0
- package/dist/chunk-MZXCF35B.js +205 -0
- package/dist/chunk-MZXCF35B.js.map +1 -0
- package/dist/chunk-NCGPUFWV.js +96 -0
- package/dist/chunk-NCGPUFWV.js.map +1 -0
- package/dist/chunk-OEJMIE2Q.js +351 -0
- package/dist/chunk-OEJMIE2Q.js.map +1 -0
- package/dist/chunk-OYF2OAKS.js +394 -0
- package/dist/chunk-OYF2OAKS.js.map +1 -0
- package/dist/chunk-P6S43FYZ.js +316 -0
- package/dist/chunk-P6S43FYZ.js.map +1 -0
- package/dist/chunk-PL37KFRJ.js +3 -0
- package/dist/chunk-PL37KFRJ.js.map +1 -0
- package/dist/chunk-Q7BS5QC5.js +197 -0
- package/dist/chunk-Q7BS5QC5.js.map +1 -0
- package/dist/chunk-SDYPG3JD.js +288 -0
- package/dist/chunk-SDYPG3JD.js.map +1 -0
- package/dist/chunk-SUG56SZO.js +256 -0
- package/dist/chunk-SUG56SZO.js.map +1 -0
- package/dist/chunk-UVH5XJRP.js +164 -0
- package/dist/chunk-UVH5XJRP.js.map +1 -0
- package/dist/chunk-WZIJKCL3.js +282 -0
- package/dist/chunk-WZIJKCL3.js.map +1 -0
- package/dist/chunk-Y22AMGTM.js +3 -0
- package/dist/chunk-Y22AMGTM.js.map +1 -0
- package/dist/chunk-Z7G23XWU.js +200 -0
- package/dist/chunk-Z7G23XWU.js.map +1 -0
- package/dist/chunk-ZJU5M4IB.js +125 -0
- package/dist/chunk-ZJU5M4IB.js.map +1 -0
- package/dist/chunk-ZVC3ZWLM.js +52 -0
- package/dist/chunk-ZVC3ZWLM.js.map +1 -0
- package/dist/chunk-ZZZML7Y3.js +310 -0
- package/dist/chunk-ZZZML7Y3.js.map +1 -0
- package/dist/client.d.ts +25 -0
- package/dist/client.js +16 -0
- package/dist/client.js.map +1 -0
- package/dist/config/index.d.ts +170 -0
- package/dist/config/index.js +3 -0
- package/dist/config/index.js.map +1 -0
- package/dist/errors/index.d.ts +267 -0
- package/dist/errors/index.js +4 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/file-router/index.d.ts +184 -0
- package/dist/file-router/index.js +3 -0
- package/dist/file-router/index.js.map +1 -0
- package/dist/file-router/streaming-hints.d.ts +129 -0
- package/dist/file-router/streaming-hints.js +3 -0
- package/dist/file-router/streaming-hints.js.map +1 -0
- package/dist/handlers/index.d.ts +59 -0
- package/dist/handlers/index.js +3 -0
- package/dist/handlers/index.js.map +1 -0
- package/dist/index.d.ts +588 -0
- package/dist/index.js +886 -0
- package/dist/index.js.map +1 -0
- package/dist/islands/index.d.ts +234 -0
- package/dist/islands/index.js +3 -0
- package/dist/islands/index.js.map +1 -0
- package/dist/middleware/index.d.ts +305 -0
- package/dist/middleware/index.js +3 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/react/index.d.ts +73 -0
- package/dist/react/index.js +52 -0
- package/dist/react/index.js.map +1 -0
- package/dist/render/index.d.ts +131 -0
- package/dist/render/index.js +3 -0
- package/dist/render/index.js.map +1 -0
- package/dist/router/index.d.ts +65 -0
- package/dist/router/index.js +3 -0
- package/dist/router/index.js.map +1 -0
- 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 +232 -0
- package/dist/rsc/index.js +15 -0
- package/dist/rsc/index.js.map +1 -0
- package/dist/rsc/legacy.d.ts +155 -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 +162 -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/server/index.d.ts +135 -0
- package/dist/server/index.js +6 -0
- package/dist/server/index.js.map +1 -0
- package/dist/streaming/adapters/index.d.ts +223 -0
- package/dist/streaming/adapters/index.js +3 -0
- package/dist/streaming/adapters/index.js.map +1 -0
- package/dist/streaming/conditional.d.ts +130 -0
- package/dist/streaming/conditional.js +3 -0
- package/dist/streaming/conditional.js.map +1 -0
- package/dist/streaming/index.d.ts +177 -0
- package/dist/streaming/index.js +3 -0
- package/dist/streaming/index.js.map +1 -0
- package/dist/streaming/observability.d.ts +201 -0
- package/dist/streaming/observability.js +4 -0
- package/dist/streaming/observability.js.map +1 -0
- package/dist/streaming/priority.d.ts +103 -0
- package/dist/streaming/priority.js +3 -0
- package/dist/streaming/priority.js.map +1 -0
- package/dist/utils/index.d.ts +42 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/index.js.map +1 -0
- 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
|