@flight-framework/core 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +347 -0
- package/dist/chunk-FSJNOPYE.js +197 -0
- package/dist/chunk-FSJNOPYE.js.map +1 -0
- package/dist/{chunk-VOBQDQKX.js → chunk-LBYDTULN.js} +3 -3
- package/dist/{chunk-VOBQDQKX.js.map → chunk-LBYDTULN.js.map} +1 -1
- package/dist/chunk-PL37KFRJ.js +3 -0
- package/dist/chunk-PL37KFRJ.js.map +1 -0
- package/dist/chunk-YHEVHRLH.js +46 -0
- package/dist/chunk-YHEVHRLH.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/index.d.ts +3 -308
- package/dist/index.js +15 -247
- package/dist/index.js.map +1 -1
- 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/rsc/adapters/index.js +1 -1
- package/dist/rsc/index.js +4 -4
- package/dist/server/index.js +2 -2
- 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 +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
# @flight-framework/core
|
|
2
|
+
|
|
3
|
+
Core primitives for Flight Framework - the agnostic full-stack framework with maximum flexibility and zero lock-in.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Multi-Render Mode** - SSR, SSG, ISR, and streaming in one unified API
|
|
8
|
+
- **Framework Agnostic** - Works with React, Vue, Svelte, Solid, HTMX, and more
|
|
9
|
+
- **File-Based Routing** - Automatic route discovery from filesystem
|
|
10
|
+
- **Server Actions** - Type-safe server functions with form support
|
|
11
|
+
- **Streaming SSR** - Out-of-order streaming with priority control
|
|
12
|
+
- **Islands Architecture** - Partial hydration with any UI framework
|
|
13
|
+
- **Caching System** - Pluggable cache adapters with deduplication
|
|
14
|
+
- **Error Handling** - Structured errors with type guards and React integration
|
|
15
|
+
- **Zero Lock-in** - Every component is replaceable
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @flight-framework/core
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
### Configuration
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// flight.config.ts
|
|
29
|
+
import { defineConfig } from '@flight-framework/core';
|
|
30
|
+
|
|
31
|
+
export default defineConfig({
|
|
32
|
+
server: {
|
|
33
|
+
port: 3000,
|
|
34
|
+
},
|
|
35
|
+
render: {
|
|
36
|
+
defaultMode: 'ssr',
|
|
37
|
+
streaming: true,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Create a Server
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { createServer } from '@flight-framework/core';
|
|
46
|
+
|
|
47
|
+
const server = createServer({
|
|
48
|
+
port: 3000,
|
|
49
|
+
routes: [
|
|
50
|
+
{ path: '/', handler: homeHandler },
|
|
51
|
+
{ path: '/api/*', handler: apiHandler },
|
|
52
|
+
],
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
await server.start();
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### File-Based Routing
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { createFileRouter, scanRoutes } from '@flight-framework/core';
|
|
62
|
+
|
|
63
|
+
const routes = await scanRoutes('./src/routes');
|
|
64
|
+
const router = createFileRouter({ routes });
|
|
65
|
+
|
|
66
|
+
// Routes are discovered automatically:
|
|
67
|
+
// src/routes/index.page.tsx -> /
|
|
68
|
+
// src/routes/about.page.tsx -> /about
|
|
69
|
+
// src/routes/blog/[slug].page.tsx -> /blog/:slug
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Modules
|
|
73
|
+
|
|
74
|
+
### Router
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { createRouter } from '@flight-framework/core/router';
|
|
78
|
+
|
|
79
|
+
const router = createRouter();
|
|
80
|
+
router.add('GET', '/users/:id', userHandler);
|
|
81
|
+
|
|
82
|
+
const match = router.match('GET', '/users/123');
|
|
83
|
+
// { params: { id: '123' }, handler: userHandler }
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Cache
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { createCache, memory, cached, dedupe } from '@flight-framework/core/cache';
|
|
90
|
+
|
|
91
|
+
// Create a cache with memory adapter
|
|
92
|
+
const cache = createCache({
|
|
93
|
+
adapter: memory(),
|
|
94
|
+
ttl: 60000,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Cache function results
|
|
98
|
+
const getUser = cached(
|
|
99
|
+
async (id: string) => fetchUser(id),
|
|
100
|
+
{ ttl: 5000, key: (id) => `user:${id}` }
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
// Deduplicate concurrent requests
|
|
104
|
+
const getData = dedupe(fetchData);
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Server Actions
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
import { registerAction, executeAction, cookies } from '@flight-framework/core/actions';
|
|
111
|
+
|
|
112
|
+
// Register a server action
|
|
113
|
+
registerAction('createUser', async (formData: FormData) => {
|
|
114
|
+
const name = formData.get('name');
|
|
115
|
+
const user = await db.users.create({ name });
|
|
116
|
+
|
|
117
|
+
cookies().set('user_id', user.id);
|
|
118
|
+
|
|
119
|
+
return { success: true, user };
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Execute from client
|
|
123
|
+
const result = await executeAction('createUser', formData);
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Streaming SSR
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import {
|
|
130
|
+
createStreamingSSR,
|
|
131
|
+
streamWithPriority
|
|
132
|
+
} from '@flight-framework/core/streaming';
|
|
133
|
+
|
|
134
|
+
// Basic streaming
|
|
135
|
+
const stream = await createStreamingSSR({
|
|
136
|
+
component: App,
|
|
137
|
+
props: { data },
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Priority-based streaming (out-of-order)
|
|
141
|
+
const result = await streamWithPriority({
|
|
142
|
+
boundaries: [
|
|
143
|
+
{ id: 'header', priority: 100, render: Header },
|
|
144
|
+
{ id: 'sidebar', priority: 50, render: Sidebar },
|
|
145
|
+
{ id: 'content', priority: 75, render: Content },
|
|
146
|
+
],
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Islands Architecture
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
import {
|
|
154
|
+
defineIsland,
|
|
155
|
+
hydrateIslands,
|
|
156
|
+
createReactIslandAdapter
|
|
157
|
+
} from '@flight-framework/core/islands';
|
|
158
|
+
|
|
159
|
+
// Define an island component
|
|
160
|
+
const Counter = defineIsland({
|
|
161
|
+
name: 'counter',
|
|
162
|
+
component: CounterComponent,
|
|
163
|
+
hydrate: 'visible', // 'load' | 'visible' | 'idle' | 'interaction'
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Client-side hydration
|
|
167
|
+
hydrateIslands({
|
|
168
|
+
adapter: createReactIslandAdapter(),
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Middleware
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { createMiddlewareChain } from '@flight-framework/core/middleware';
|
|
176
|
+
|
|
177
|
+
const middleware = createMiddlewareChain([
|
|
178
|
+
async (ctx, next) => {
|
|
179
|
+
const start = Date.now();
|
|
180
|
+
await next();
|
|
181
|
+
console.log(`Request took ${Date.now() - start}ms`);
|
|
182
|
+
},
|
|
183
|
+
authMiddleware,
|
|
184
|
+
loggingMiddleware,
|
|
185
|
+
]);
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Error Handling
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
import {
|
|
192
|
+
FlightError,
|
|
193
|
+
createError,
|
|
194
|
+
isFlightError,
|
|
195
|
+
createNotFound,
|
|
196
|
+
createForbidden,
|
|
197
|
+
} from '@flight-framework/core/errors';
|
|
198
|
+
|
|
199
|
+
// Create typed errors
|
|
200
|
+
throw createNotFound('Page not found');
|
|
201
|
+
throw createForbidden('Access denied');
|
|
202
|
+
|
|
203
|
+
// Create custom errors
|
|
204
|
+
throw createError({
|
|
205
|
+
statusCode: 422,
|
|
206
|
+
message: 'Validation failed',
|
|
207
|
+
data: { field: 'email', error: 'Invalid format' },
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// Type guards
|
|
211
|
+
if (isFlightError(error)) {
|
|
212
|
+
console.log(error.statusCode, error.digest);
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### React Integration
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
import { ErrorProvider, useError, useFlightError } from '@flight-framework/core/react';
|
|
220
|
+
|
|
221
|
+
// Wrap your app
|
|
222
|
+
<ErrorProvider onError={console.error}>
|
|
223
|
+
<App />
|
|
224
|
+
</ErrorProvider>
|
|
225
|
+
|
|
226
|
+
// Use in components
|
|
227
|
+
function MyComponent() {
|
|
228
|
+
const { error, clearError } = useFlightError();
|
|
229
|
+
|
|
230
|
+
if (error) {
|
|
231
|
+
return <ErrorDisplay error={error} onRetry={clearError} />;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return <Content />;
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Metadata (SEO)
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import { renderMetadataToHead, type Metadata } from '@flight-framework/core';
|
|
242
|
+
|
|
243
|
+
const metadata: Metadata = {
|
|
244
|
+
title: 'My Page',
|
|
245
|
+
description: 'Page description',
|
|
246
|
+
openGraph: {
|
|
247
|
+
title: 'OG Title',
|
|
248
|
+
image: '/og-image.png',
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const headHtml = renderMetadataToHead(metadata);
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Route Rules (ISR/SSG)
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
import { defineConfig } from '@flight-framework/core';
|
|
259
|
+
|
|
260
|
+
export default defineConfig({
|
|
261
|
+
routeRules: {
|
|
262
|
+
'/': { prerender: true },
|
|
263
|
+
'/blog/**': { isr: 3600 },
|
|
264
|
+
'/api/**': { ssr: true },
|
|
265
|
+
'/static/**': { static: true },
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Revalidation
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
import {
|
|
274
|
+
revalidatePath,
|
|
275
|
+
revalidateTag,
|
|
276
|
+
createRevalidateHandler
|
|
277
|
+
} from '@flight-framework/core';
|
|
278
|
+
|
|
279
|
+
// On-demand revalidation
|
|
280
|
+
await revalidatePath('/blog/my-post');
|
|
281
|
+
await revalidateTag('blog-posts');
|
|
282
|
+
|
|
283
|
+
// Create revalidation API handler
|
|
284
|
+
const handler = createRevalidateHandler({
|
|
285
|
+
secret: process.env.REVALIDATE_SECRET,
|
|
286
|
+
});
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Environment Utilities
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
import {
|
|
293
|
+
isServer,
|
|
294
|
+
isBrowser,
|
|
295
|
+
isProduction,
|
|
296
|
+
isDevelopment
|
|
297
|
+
} from '@flight-framework/core/utils';
|
|
298
|
+
|
|
299
|
+
if (isServer()) {
|
|
300
|
+
// Server-only code
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (isDevelopment()) {
|
|
304
|
+
console.log('Debug info');
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## Module Exports
|
|
309
|
+
|
|
310
|
+
| Export | Description |
|
|
311
|
+
|--------|-------------|
|
|
312
|
+
| `@flight-framework/core` | Main entry with all primitives |
|
|
313
|
+
| `@flight-framework/core/router` | Routing utilities |
|
|
314
|
+
| `@flight-framework/core/cache` | Caching system |
|
|
315
|
+
| `@flight-framework/core/server` | HTTP server |
|
|
316
|
+
| `@flight-framework/core/render` | Rendering modes |
|
|
317
|
+
| `@flight-framework/core/middleware` | Middleware chain |
|
|
318
|
+
| `@flight-framework/core/actions` | Server actions |
|
|
319
|
+
| `@flight-framework/core/streaming` | Streaming SSR |
|
|
320
|
+
| `@flight-framework/core/islands` | Islands architecture |
|
|
321
|
+
| `@flight-framework/core/errors` | Error handling |
|
|
322
|
+
| `@flight-framework/core/react` | React integration |
|
|
323
|
+
| `@flight-framework/core/rsc` | React Server Components |
|
|
324
|
+
| `@flight-framework/core/config` | Configuration |
|
|
325
|
+
| `@flight-framework/core/utils` | Environment utilities |
|
|
326
|
+
|
|
327
|
+
## TypeScript
|
|
328
|
+
|
|
329
|
+
Full TypeScript support with exported types for all APIs:
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
import type {
|
|
333
|
+
FlightConfig,
|
|
334
|
+
Route,
|
|
335
|
+
RouteMatch,
|
|
336
|
+
Cache,
|
|
337
|
+
CacheAdapter,
|
|
338
|
+
Middleware,
|
|
339
|
+
FlightError,
|
|
340
|
+
Metadata,
|
|
341
|
+
StreamingHints,
|
|
342
|
+
} from '@flight-framework/core';
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## License
|
|
346
|
+
|
|
347
|
+
MIT
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { isProduction, isDevelopment } from './chunk-YHEVHRLH.js';
|
|
2
|
+
|
|
3
|
+
// src/errors/index.ts
|
|
4
|
+
var FlightError = class extends Error {
|
|
5
|
+
/** HTTP status code */
|
|
6
|
+
statusCode;
|
|
7
|
+
/** Short status message */
|
|
8
|
+
statusMessage;
|
|
9
|
+
/** Additional error data */
|
|
10
|
+
data;
|
|
11
|
+
/** Whether this is a fatal error (shows full-screen) */
|
|
12
|
+
fatal;
|
|
13
|
+
/** Unique digest for production error correlation */
|
|
14
|
+
digest;
|
|
15
|
+
constructor(options) {
|
|
16
|
+
super(options.message || options.statusMessage || "An error occurred");
|
|
17
|
+
this.name = "FlightError";
|
|
18
|
+
this.statusCode = options.statusCode;
|
|
19
|
+
this.statusMessage = options.statusMessage || getDefaultStatusMessage(options.statusCode);
|
|
20
|
+
this.data = options.data;
|
|
21
|
+
this.fatal = options.fatal ?? false;
|
|
22
|
+
if (isProduction()) {
|
|
23
|
+
this.digest = generateDigest();
|
|
24
|
+
}
|
|
25
|
+
if (options.cause) {
|
|
26
|
+
this.cause = options.cause;
|
|
27
|
+
if (options.cause.stack) {
|
|
28
|
+
this.stack = `${this.stack}
|
|
29
|
+
Caused by: ${options.cause.stack}`;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Convert to plain object for serialization
|
|
35
|
+
*/
|
|
36
|
+
toJSON() {
|
|
37
|
+
return {
|
|
38
|
+
name: this.name,
|
|
39
|
+
message: this.message,
|
|
40
|
+
statusCode: this.statusCode,
|
|
41
|
+
statusMessage: this.statusMessage,
|
|
42
|
+
data: this.data,
|
|
43
|
+
fatal: this.fatal,
|
|
44
|
+
digest: this.digest
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
var BadRequestError = class extends FlightError {
|
|
49
|
+
constructor(message, data) {
|
|
50
|
+
super({ statusCode: 400, message, data });
|
|
51
|
+
this.name = "BadRequestError";
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
var UnauthorizedError = class extends FlightError {
|
|
55
|
+
constructor(message, data) {
|
|
56
|
+
super({ statusCode: 401, message: message || "Unauthorized", data });
|
|
57
|
+
this.name = "UnauthorizedError";
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
var ForbiddenError = class extends FlightError {
|
|
61
|
+
constructor(message, data) {
|
|
62
|
+
super({ statusCode: 403, message: message || "Forbidden", data });
|
|
63
|
+
this.name = "ForbiddenError";
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
var NotFoundError = class extends FlightError {
|
|
67
|
+
constructor(message, data) {
|
|
68
|
+
super({ statusCode: 404, message: message || "Not Found", data });
|
|
69
|
+
this.name = "NotFoundError";
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
var InternalError = class extends FlightError {
|
|
73
|
+
constructor(message, data) {
|
|
74
|
+
super({ statusCode: 500, message: message || "Internal Server Error", data });
|
|
75
|
+
this.name = "InternalError";
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
function createError(options) {
|
|
79
|
+
if (typeof options === "string") {
|
|
80
|
+
return new FlightError({ statusCode: 500, message: options });
|
|
81
|
+
}
|
|
82
|
+
return new FlightError(options);
|
|
83
|
+
}
|
|
84
|
+
function notFound(message, data) {
|
|
85
|
+
throw new NotFoundError(message, data);
|
|
86
|
+
}
|
|
87
|
+
function forbidden(message, data) {
|
|
88
|
+
throw new ForbiddenError(message, data);
|
|
89
|
+
}
|
|
90
|
+
function unauthorized(message, data) {
|
|
91
|
+
throw new UnauthorizedError(message, data);
|
|
92
|
+
}
|
|
93
|
+
function showError(error) {
|
|
94
|
+
let flightError;
|
|
95
|
+
if (typeof error === "string") {
|
|
96
|
+
flightError = new FlightError({ statusCode: 500, message: error });
|
|
97
|
+
} else if (error instanceof FlightError) {
|
|
98
|
+
flightError = error;
|
|
99
|
+
} else {
|
|
100
|
+
flightError = new FlightError(error);
|
|
101
|
+
}
|
|
102
|
+
if (typeof window !== "undefined") {
|
|
103
|
+
window.__FLIGHT_ERROR__ = flightError;
|
|
104
|
+
window.dispatchEvent(new CustomEvent("flight:error", {
|
|
105
|
+
detail: flightError,
|
|
106
|
+
bubbles: true
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function clearError(options) {
|
|
111
|
+
if (typeof window !== "undefined") {
|
|
112
|
+
window.__FLIGHT_ERROR__ = null;
|
|
113
|
+
window.dispatchEvent(new CustomEvent("flight:error-clear", { bubbles: true }));
|
|
114
|
+
if (options?.redirect) {
|
|
115
|
+
window.location.href = options.redirect;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function getError() {
|
|
120
|
+
if (typeof window !== "undefined") {
|
|
121
|
+
return window.__FLIGHT_ERROR__ ?? null;
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
function isFlightError(error) {
|
|
126
|
+
return error instanceof FlightError;
|
|
127
|
+
}
|
|
128
|
+
function isNotFoundError(error) {
|
|
129
|
+
return error instanceof NotFoundError || isFlightError(error) && error.statusCode === 404;
|
|
130
|
+
}
|
|
131
|
+
function isForbiddenError(error) {
|
|
132
|
+
return error instanceof ForbiddenError || isFlightError(error) && error.statusCode === 403;
|
|
133
|
+
}
|
|
134
|
+
function isUnauthorizedError(error) {
|
|
135
|
+
return error instanceof UnauthorizedError || isFlightError(error) && error.statusCode === 401;
|
|
136
|
+
}
|
|
137
|
+
function getErrorStatusCode(error) {
|
|
138
|
+
if (isFlightError(error)) {
|
|
139
|
+
return error.statusCode;
|
|
140
|
+
}
|
|
141
|
+
return 500;
|
|
142
|
+
}
|
|
143
|
+
function createErrorResponse(error) {
|
|
144
|
+
const flightError = isFlightError(error) ? error : new InternalError(error instanceof Error ? error.message : "Unknown error");
|
|
145
|
+
return new Response(
|
|
146
|
+
JSON.stringify({
|
|
147
|
+
error: flightError.statusMessage,
|
|
148
|
+
message: flightError.message,
|
|
149
|
+
statusCode: flightError.statusCode,
|
|
150
|
+
digest: flightError.digest,
|
|
151
|
+
...isDevelopment() && flightError.data ? { data: flightError.data } : {}
|
|
152
|
+
}),
|
|
153
|
+
{
|
|
154
|
+
status: flightError.statusCode,
|
|
155
|
+
headers: {
|
|
156
|
+
"Content-Type": "application/json"
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
function getDefaultStatusMessage(statusCode) {
|
|
162
|
+
const messages = {
|
|
163
|
+
400: "Bad Request",
|
|
164
|
+
401: "Unauthorized",
|
|
165
|
+
403: "Forbidden",
|
|
166
|
+
404: "Not Found",
|
|
167
|
+
405: "Method Not Allowed",
|
|
168
|
+
408: "Request Timeout",
|
|
169
|
+
409: "Conflict",
|
|
170
|
+
410: "Gone",
|
|
171
|
+
422: "Unprocessable Entity",
|
|
172
|
+
429: "Too Many Requests",
|
|
173
|
+
500: "Internal Server Error",
|
|
174
|
+
501: "Not Implemented",
|
|
175
|
+
502: "Bad Gateway",
|
|
176
|
+
503: "Service Unavailable",
|
|
177
|
+
504: "Gateway Timeout"
|
|
178
|
+
};
|
|
179
|
+
return messages[statusCode] || "Error";
|
|
180
|
+
}
|
|
181
|
+
function generateDigest() {
|
|
182
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
183
|
+
return crypto.randomUUID().slice(0, 8);
|
|
184
|
+
}
|
|
185
|
+
return Math.random().toString(36).slice(2, 10);
|
|
186
|
+
}
|
|
187
|
+
function wrapWithDigest(error) {
|
|
188
|
+
const wrapped = error;
|
|
189
|
+
if (!wrapped.digest) {
|
|
190
|
+
wrapped.digest = generateDigest();
|
|
191
|
+
}
|
|
192
|
+
return wrapped;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export { BadRequestError, FlightError, ForbiddenError, InternalError, NotFoundError, UnauthorizedError, clearError, createError, createErrorResponse, forbidden, getError, getErrorStatusCode, isFlightError, isForbiddenError, isNotFoundError, isUnauthorizedError, notFound, showError, unauthorized, wrapWithDigest };
|
|
196
|
+
//# sourceMappingURL=chunk-FSJNOPYE.js.map
|
|
197
|
+
//# sourceMappingURL=chunk-FSJNOPYE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors/index.ts"],"names":[],"mappings":"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","file":"chunk-FSJNOPYE.js","sourcesContent":["/**\r\n * @flight-framework/core - Error Handling\r\n * \r\n * Comprehensive error handling utilities for Flight applications.\r\n * All utilities are OPTIONAL - developers can use their own error handling.\r\n * \r\n * Philosophy: Flight OFFERS these utilities, but never IMPOSES them.\r\n * Using throw new Error() works perfectly fine - this is your choice.\r\n */\r\n\r\nimport { isProduction, isDevelopment } from '../utils/env.js';\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\n/**\r\n * Options for creating a Flight error\r\n */\r\nexport interface FlightErrorOptions {\r\n /** HTTP status code */\r\n statusCode: number;\r\n /** Short status message (e.g., \"Not Found\") */\r\n statusMessage?: string;\r\n /** Detailed error message */\r\n message?: string;\r\n /** Additional data to include with the error */\r\n data?: Record<string, unknown>;\r\n /** If true, shows full-screen error page instead of error boundary */\r\n fatal?: boolean;\r\n /** Original error that caused this error */\r\n cause?: Error;\r\n}\r\n\r\n/**\r\n * Extended error props with digest for production error correlation\r\n */\r\nexport interface FlightErrorProps {\r\n /** The error object */\r\n error: Error & { digest?: string };\r\n /** Function to attempt recovery by re-rendering */\r\n reset: () => void;\r\n}\r\n\r\n/**\r\n * Reset details provided to onReset callback\r\n */\r\nexport interface ResetDetails {\r\n /** Reason for the reset */\r\n reason: 'imperative-api' | 'keys';\r\n /** Arguments passed to resetErrorBoundary (if imperative) */\r\n args?: unknown[];\r\n /** Previous resetKeys values (if keys changed) */\r\n prev?: unknown[];\r\n /** New resetKeys values (if keys changed) */\r\n next?: unknown[];\r\n}\r\n\r\n/**\r\n * Options for error boundary behavior\r\n */\r\nexport interface ErrorBoundaryOptions {\r\n /** Keys that trigger automatic reset when changed */\r\n resetKeys?: unknown[];\r\n /** Callback when error boundary resets */\r\n onReset?: (details: ResetDetails) => void;\r\n /** Callback when error is caught */\r\n onError?: (error: Error, info: { componentStack?: string }) => void;\r\n}\r\n\r\n// ============================================================================\r\n// FlightError Class\r\n// ============================================================================\r\n\r\n/**\r\n * Custom error class with status code and metadata support.\r\n * \r\n * You can use this class or regular Error - Flight handles both.\r\n * \r\n * @example\r\n * ```typescript\r\n * throw new FlightError({\r\n * statusCode: 404,\r\n * message: 'User not found',\r\n * data: { userId: '123' }\r\n * });\r\n * ```\r\n */\r\nexport class FlightError extends Error {\r\n /** HTTP status code */\r\n readonly statusCode: number;\r\n /** Short status message */\r\n readonly statusMessage: string;\r\n /** Additional error data */\r\n readonly data?: Record<string, unknown>;\r\n /** Whether this is a fatal error (shows full-screen) */\r\n readonly fatal: boolean;\r\n /** Unique digest for production error correlation */\r\n readonly digest?: string;\r\n\r\n constructor(options: FlightErrorOptions) {\r\n super(options.message || options.statusMessage || 'An error occurred');\r\n\r\n this.name = 'FlightError';\r\n this.statusCode = options.statusCode;\r\n this.statusMessage = options.statusMessage || getDefaultStatusMessage(options.statusCode);\r\n this.data = options.data;\r\n this.fatal = options.fatal ?? false;\r\n\r\n // Generate digest in production for error correlation\r\n // Using typeof check for SSR/browser compatibility\r\n if (isProduction()) {\r\n this.digest = generateDigest();\r\n }\r\n\r\n // Preserve original error stack if cause provided\r\n if (options.cause) {\r\n this.cause = options.cause;\r\n if (options.cause.stack) {\r\n this.stack = `${this.stack}\\nCaused by: ${options.cause.stack}`;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Convert to plain object for serialization\r\n */\r\n toJSON(): Record<string, unknown> {\r\n return {\r\n name: this.name,\r\n message: this.message,\r\n statusCode: this.statusCode,\r\n statusMessage: this.statusMessage,\r\n data: this.data,\r\n fatal: this.fatal,\r\n digest: this.digest,\r\n };\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// HTTP Error Classes (Convenience)\r\n// ============================================================================\r\n\r\n/**\r\n * 400 Bad Request error\r\n */\r\nexport class BadRequestError extends FlightError {\r\n constructor(message?: string, data?: Record<string, unknown>) {\r\n super({ statusCode: 400, message, data });\r\n this.name = 'BadRequestError';\r\n }\r\n}\r\n\r\n/**\r\n * 401 Unauthorized error\r\n */\r\nexport class UnauthorizedError extends FlightError {\r\n constructor(message?: string, data?: Record<string, unknown>) {\r\n super({ statusCode: 401, message: message || 'Unauthorized', data });\r\n this.name = 'UnauthorizedError';\r\n }\r\n}\r\n\r\n/**\r\n * 403 Forbidden error\r\n */\r\nexport class ForbiddenError extends FlightError {\r\n constructor(message?: string, data?: Record<string, unknown>) {\r\n super({ statusCode: 403, message: message || 'Forbidden', data });\r\n this.name = 'ForbiddenError';\r\n }\r\n}\r\n\r\n/**\r\n * 404 Not Found error\r\n */\r\nexport class NotFoundError extends FlightError {\r\n constructor(message?: string, data?: Record<string, unknown>) {\r\n super({ statusCode: 404, message: message || 'Not Found', data });\r\n this.name = 'NotFoundError';\r\n }\r\n}\r\n\r\n/**\r\n * 500 Internal Server Error\r\n */\r\nexport class InternalError extends FlightError {\r\n constructor(message?: string, data?: Record<string, unknown>) {\r\n super({ statusCode: 500, message: message || 'Internal Server Error', data });\r\n this.name = 'InternalError';\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Factory Functions\r\n// ============================================================================\r\n\r\n/**\r\n * Create a FlightError with the specified options.\r\n * \r\n * This is a convenience function - you can also use `new FlightError()` directly\r\n * or just `throw new Error()` - Flight handles all cases.\r\n * \r\n * @example\r\n * ```typescript\r\n * // With full options\r\n * throw createError({\r\n * statusCode: 404,\r\n * message: 'Product not found',\r\n * data: { productId: 'abc123' }\r\n * });\r\n * \r\n * // Simple string shorthand (becomes 500 error)\r\n * throw createError('Something went wrong');\r\n * ```\r\n */\r\nexport function createError(options: FlightErrorOptions | string): FlightError {\r\n if (typeof options === 'string') {\r\n return new FlightError({ statusCode: 500, message: options });\r\n }\r\n return new FlightError(options);\r\n}\r\n\r\n/**\r\n * Create a 404 Not Found error.\r\n * Convenience function equivalent to `createError({ statusCode: 404, ... })`.\r\n * \r\n * @example\r\n * ```typescript\r\n * if (!user) {\r\n * throw notFound('User not found');\r\n * }\r\n * ```\r\n */\r\nexport function notFound(message?: string, data?: Record<string, unknown>): never {\r\n throw new NotFoundError(message, data);\r\n}\r\n\r\n/**\r\n * Create a 403 Forbidden error.\r\n * \r\n * @example\r\n * ```typescript\r\n * if (!user.isAdmin) {\r\n * throw forbidden('Admin access required');\r\n * }\r\n * ```\r\n */\r\nexport function forbidden(message?: string, data?: Record<string, unknown>): never {\r\n throw new ForbiddenError(message, data);\r\n}\r\n\r\n/**\r\n * Create a 401 Unauthorized error.\r\n * \r\n * @example\r\n * ```typescript\r\n * if (!session) {\r\n * throw unauthorized('Please log in to continue');\r\n * }\r\n * ```\r\n */\r\nexport function unauthorized(message?: string, data?: Record<string, unknown>): never {\r\n throw new UnauthorizedError(message, data);\r\n}\r\n\r\n// ============================================================================\r\n// Error State Management (Client-Side)\r\n// ============================================================================\r\n\r\n// Global error state for client-side\r\ndeclare global {\r\n interface Window {\r\n __FLIGHT_ERROR__?: FlightError | null;\r\n }\r\n}\r\n\r\n/**\r\n * Programmatically show an error page.\r\n * \r\n * This triggers the nearest error boundary or navigates to the error page.\r\n * Only works on the client side.\r\n * \r\n * @example\r\n * ```typescript\r\n * // Show error with full options\r\n * showError({\r\n * statusCode: 500,\r\n * message: 'Connection lost'\r\n * });\r\n * \r\n * // Simple string shorthand\r\n * showError('Something went wrong');\r\n * ```\r\n */\r\nexport function showError(error: FlightErrorOptions | FlightError | string): void {\r\n let flightError: FlightError;\r\n\r\n if (typeof error === 'string') {\r\n flightError = new FlightError({ statusCode: 500, message: error });\r\n } else if (error instanceof FlightError) {\r\n flightError = error;\r\n } else {\r\n flightError = new FlightError(error);\r\n }\r\n\r\n // Client-side: dispatch custom event for error boundaries\r\n if (typeof window !== 'undefined') {\r\n window.__FLIGHT_ERROR__ = flightError;\r\n window.dispatchEvent(new CustomEvent('flight:error', {\r\n detail: flightError,\r\n bubbles: true\r\n }));\r\n }\r\n}\r\n\r\n/**\r\n * Clear the current error state and optionally redirect.\r\n * \r\n * Use this to dismiss an error and return to normal state.\r\n * \r\n * @example\r\n * ```typescript\r\n * // Just clear the error\r\n * clearError();\r\n * \r\n * // Clear and redirect to home\r\n * clearError({ redirect: '/' });\r\n * ```\r\n */\r\nexport function clearError(options?: { redirect?: string }): void {\r\n if (typeof window !== 'undefined') {\r\n window.__FLIGHT_ERROR__ = null;\r\n window.dispatchEvent(new CustomEvent('flight:error-clear', { bubbles: true }));\r\n\r\n if (options?.redirect) {\r\n window.location.href = options.redirect;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Get the current error from global state.\r\n * Returns null if no error is active.\r\n */\r\nexport function getError(): FlightError | null {\r\n if (typeof window !== 'undefined') {\r\n return window.__FLIGHT_ERROR__ ?? null;\r\n }\r\n return null;\r\n}\r\n\r\n// ============================================================================\r\n// Type Guards\r\n// ============================================================================\r\n\r\n/**\r\n * Check if an error is a FlightError\r\n */\r\nexport function isFlightError(error: unknown): error is FlightError {\r\n return error instanceof FlightError;\r\n}\r\n\r\n/**\r\n * Check if an error is a NotFoundError (404)\r\n */\r\nexport function isNotFoundError(error: unknown): error is NotFoundError {\r\n return error instanceof NotFoundError ||\r\n (isFlightError(error) && error.statusCode === 404);\r\n}\r\n\r\n/**\r\n * Check if an error is a ForbiddenError (403)\r\n */\r\nexport function isForbiddenError(error: unknown): error is ForbiddenError {\r\n return error instanceof ForbiddenError ||\r\n (isFlightError(error) && error.statusCode === 403);\r\n}\r\n\r\n/**\r\n * Check if an error is an UnauthorizedError (401)\r\n */\r\nexport function isUnauthorizedError(error: unknown): error is UnauthorizedError {\r\n return error instanceof UnauthorizedError ||\r\n (isFlightError(error) && error.statusCode === 401);\r\n}\r\n\r\n/**\r\n * Get the status code from any error.\r\n * Returns 500 for non-FlightError errors.\r\n */\r\nexport function getErrorStatusCode(error: unknown): number {\r\n if (isFlightError(error)) {\r\n return error.statusCode;\r\n }\r\n return 500;\r\n}\r\n\r\n// ============================================================================\r\n// Response Helpers\r\n// ============================================================================\r\n\r\n/**\r\n * Create an error Response from a FlightError.\r\n * \r\n * @example\r\n * ```typescript\r\n * try {\r\n * // ... some operation\r\n * } catch (error) {\r\n * return createErrorResponse(error);\r\n * }\r\n * ```\r\n */\r\nexport function createErrorResponse(error: unknown): Response {\r\n const flightError = isFlightError(error)\r\n ? error\r\n : new InternalError(error instanceof Error ? error.message : 'Unknown error');\r\n\r\n return new Response(\r\n JSON.stringify({\r\n error: flightError.statusMessage,\r\n message: flightError.message,\r\n statusCode: flightError.statusCode,\r\n digest: flightError.digest,\r\n ...(isDevelopment() && flightError.data\r\n ? { data: flightError.data }\r\n : {}),\r\n }),\r\n {\r\n status: flightError.statusCode,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n }\r\n );\r\n}\r\n\r\n// ============================================================================\r\n// Utilities\r\n// ============================================================================\r\n\r\n/**\r\n * Get default status message for HTTP status code\r\n */\r\nfunction getDefaultStatusMessage(statusCode: number): string {\r\n const messages: Record<number, string> = {\r\n 400: 'Bad Request',\r\n 401: 'Unauthorized',\r\n 403: 'Forbidden',\r\n 404: 'Not Found',\r\n 405: 'Method Not Allowed',\r\n 408: 'Request Timeout',\r\n 409: 'Conflict',\r\n 410: 'Gone',\r\n 422: 'Unprocessable Entity',\r\n 429: 'Too Many Requests',\r\n 500: 'Internal Server Error',\r\n 501: 'Not Implemented',\r\n 502: 'Bad Gateway',\r\n 503: 'Service Unavailable',\r\n 504: 'Gateway Timeout',\r\n };\r\n return messages[statusCode] || 'Error';\r\n}\r\n\r\n/**\r\n * Generate a short digest for error correlation.\r\n * Used in production to correlate client errors with server logs\r\n * without exposing stack traces.\r\n */\r\nfunction generateDigest(): string {\r\n // Use crypto if available (Node.js, modern browsers)\r\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\r\n return crypto.randomUUID().slice(0, 8);\r\n }\r\n // Fallback for older environments\r\n return Math.random().toString(36).slice(2, 10);\r\n}\r\n\r\n/**\r\n * Wrap an error with a digest if it doesn't have one.\r\n * Useful for adding correlation IDs to third-party errors.\r\n */\r\nexport function wrapWithDigest<T extends Error>(error: T): T & { digest: string } {\r\n const wrapped = error as T & { digest: string };\r\n if (!wrapped.digest) {\r\n wrapped.digest = generateDigest();\r\n }\r\n return wrapped;\r\n}\r\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { resolveConfig } from './chunk-IXMD5QH2.js';
|
|
1
2
|
import { createRouter } from './chunk-GCQZ4FHI.js';
|
|
2
3
|
import { createMiddlewareChain, createContextFromRequest, createResponseFromContext } from './chunk-KWFX6WHG.js';
|
|
3
|
-
import { resolveConfig } from './chunk-IXMD5QH2.js';
|
|
4
4
|
|
|
5
5
|
// src/server/index.ts
|
|
6
6
|
function detectRuntime() {
|
|
@@ -219,5 +219,5 @@ function getRuntime() {
|
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
export { createServer, getRuntime, isFlightServer };
|
|
222
|
-
//# sourceMappingURL=chunk-
|
|
223
|
-
//# sourceMappingURL=chunk-
|
|
222
|
+
//# sourceMappingURL=chunk-LBYDTULN.js.map
|
|
223
|
+
//# sourceMappingURL=chunk-LBYDTULN.js.map
|