@flightdev/core
v0.6.8
Published
Core primitives for Flight Framework - routing, rendering, caching
Downloads
10
Maintainers
Readme
@flightdev/core
Core primitives for Flight Framework, including configuration, routing, caching, streaming SSR, and server actions.
Features
- Multi-render mode: SSR, SSG, ISR, and streaming
- Framework support: React, Vue, Svelte, Solid, HTMX
- File-based routing with automatic route discovery
- Type-safe server actions with form support
- Streaming SSR with priority control
- Islands architecture for partial hydration
- Pluggable cache adapters with deduplication
- Structured error handling with type guards
Installation
npm install @flightdev/coreQuick Start
Configuration
// flight.config.ts
import { defineConfig } from '@flightdev/core';
export default defineConfig({
server: {
port: 3000,
},
render: {
defaultMode: 'ssr',
streaming: true,
},
});Create a Server
import { createServer } from '@flightdev/core';
const server = createServer({
port: 3000,
routes: [
{ path: '/', handler: homeHandler },
{ path: '/api/*', handler: apiHandler },
],
});
await server.start();File-Based Routing
import { createFileRouter, scanRoutes } from '@flightdev/core';
const routes = await scanRoutes('./src/routes');
const router = createFileRouter({ routes });
// Routes are discovered automatically:
// src/routes/index.page.tsx -> /
// src/routes/about.page.tsx -> /about
// src/routes/blog/[slug].page.tsx -> /blog/:slugModules
Router
import { createRouter } from '@flightdev/core/router';
const router = createRouter();
router.add('GET', '/users/:id', userHandler);
const match = router.match('GET', '/users/123');
// { params: { id: '123' }, handler: userHandler }Cache
import { createCache, memory, cached, dedupe } from '@flightdev/core/cache';
// Create a cache with memory adapter
const cache = createCache({
adapter: memory(),
ttl: 60000,
});
// Cache function results
const getUser = cached(
async (id: string) => fetchUser(id),
{ ttl: 5000, key: (id) => `user:${id}` }
);
// Deduplicate concurrent requests
const getData = dedupe(fetchData);Server Actions
import { registerAction, executeAction, cookies } from '@flightdev/core/actions';
// Register a server action
registerAction('createUser', async (formData: FormData) => {
const name = formData.get('name');
const user = await db.users.create({ name });
cookies().set('user_id', user.id);
return { success: true, user };
});
// Execute from client
const result = await executeAction('createUser', formData);Streaming SSR
import {
createStreamingSSR,
streamWithPriority
} from '@flightdev/core/streaming';
// Basic streaming
const stream = await createStreamingSSR({
component: App,
props: { data },
});
// Priority-based streaming (out-of-order)
const result = await streamWithPriority({
boundaries: [
{ id: 'header', priority: 100, render: Header },
{ id: 'sidebar', priority: 50, render: Sidebar },
{ id: 'content', priority: 75, render: Content },
],
});Islands Architecture
import {
defineIsland,
hydrateIslands,
createReactIslandAdapter
} from '@flightdev/core/islands';
// Define an island component
const Counter = defineIsland({
name: 'counter',
component: CounterComponent,
hydrate: 'visible', // 'load' | 'visible' | 'idle' | 'interaction'
});
// Client-side hydration
hydrateIslands({
adapter: createReactIslandAdapter(),
});Middleware
Flight provides a composable middleware system for request/response handling.
Middleware Chain
import {
createMiddlewareChain,
cors,
logger,
securityHeaders
} from '@flightdev/core/middleware';
const chain = createMiddlewareChain();
chain
.use(logger())
.use(cors({ origin: ['https://app.example.com'] }))
.use(securityHeaders())
.use(async (ctx, next) => {
const start = Date.now();
await next();
console.log(`Request took ${Date.now() - start}ms`);
});Error Handling
Centralized error handling with the errorHandler factory:
import { createMiddlewareChain, errorHandler } from '@flightdev/core/middleware';
const chain = createMiddlewareChain();
// Place error handler first in the chain
chain.use(errorHandler({
expose: process.env.NODE_ENV === 'development',
emit: (error, ctx) => {
logger.error(`[${ctx.method}] ${ctx.url.pathname}:`, error);
errorTracker.capture(error);
},
}));
chain.use(authMiddleware);
chain.use(routeHandler);The error handler catches all downstream errors, sets appropriate status codes, and supports custom error handlers:
chain.use(errorHandler({
onError: async ({ error, status, ctx, timestamp }) => {
ctx.status = status;
ctx.responseBody = JSON.stringify({
error: error.message,
timestamp,
requestId: ctx.locals.requestId,
});
},
}));Typed Context
Middleware context supports generics for type-safe data sharing:
import type { Middleware, MiddlewareContext } from '@flightdev/core/middleware';
interface AppLocals {
user: { id: string; role: string };
requestId: string;
db: DatabaseClient;
}
const authMiddleware: Middleware<AppLocals> = async (ctx, next) => {
const token = ctx.headers.get('Authorization');
const user = await verifyToken(token);
ctx.locals.user = user;
ctx.locals.requestId = crypto.randomUUID();
await next();
};
// Type-safe access in subsequent middleware
const roleGuard: Middleware<AppLocals> = async (ctx, next) => {
if (ctx.locals.user.role !== 'admin') {
ctx.status = 403;
ctx.responseBody = 'Forbidden';
return;
}
await next();
};CORS
CORS middleware with dynamic origin validation and CDN compatibility:
import { cors } from '@flightdev/core/middleware';
// Static origins
chain.use(cors({
origin: ['https://app.example.com', 'https://admin.example.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true,
}));
// Async validation (database lookup)
chain.use(cors({
origin: async (requestOrigin) => {
return await db.allowedOrigins.exists(requestOrigin);
},
exposeHeaders: ['X-Request-Id', 'X-RateLimit-Remaining'],
}));Dynamic origins automatically set Vary: Origin for CDN/cache compatibility.
Built-in Middleware
| Middleware | Purpose |
|------------|---------|
| cors(options?) | Cross-origin resource sharing |
| logger(options?) | Request logging with configurable levels |
| securityHeaders(options?) | Security headers (CSP, X-Frame-Options, etc.) |
| errorHandler(options?) | Centralized error handling |
| compress() | Mark responses for compression |
Logger Configuration
import { logger } from '@flightdev/core/middleware';
chain.use(logger({
level: 'info', // 'debug' | 'info' | 'warn' | 'error' | 'silent'
format: 'json', // 'pretty' | 'json' | 'combined' | 'common' | 'short'
skip: (ctx) => ctx.url.pathname === '/health',
writer: (entry, formatted) => externalLogger.log(entry),
}));The logger captures errors from downstream middleware before re-throwing, ensuring all requests are logged including failures.
Error Handling
import {
FlightError,
createError,
isFlightError,
createNotFound,
createForbidden,
} from '@flightdev/core/errors';
// Create typed errors
throw createNotFound('Page not found');
throw createForbidden('Access denied');
// Create custom errors
throw createError({
statusCode: 422,
message: 'Validation failed',
data: { field: 'email', error: 'Invalid format' },
});
// Type guards
if (isFlightError(error)) {
console.log(error.statusCode, error.digest);
}React Integration
import { ErrorProvider, useError, useFlightError } from '@flightdev/core/react';
// Wrap your app
<ErrorProvider onError={console.error}>
<App />
</ErrorProvider>
// Use in components
function MyComponent() {
const { error, clearError } = useFlightError();
if (error) {
return <ErrorDisplay error={error} onRetry={clearError} />;
}
return <Content />;
}Metadata (SEO)
import { renderMetadataToHead, type Metadata } from '@flightdev/core';
const metadata: Metadata = {
title: 'My Page',
description: 'Page description',
openGraph: {
title: 'OG Title',
image: '/og-image.png',
},
};
const headHtml = renderMetadataToHead(metadata);Route Rules (ISR/SSG)
import { defineConfig } from '@flightdev/core';
export default defineConfig({
routeRules: {
'/': { prerender: true },
'/blog/**': { isr: 3600 },
'/api/**': { ssr: true },
'/static/**': { static: true },
},
});Revalidation
import {
revalidatePath,
revalidateTag,
createRevalidateHandler
} from '@flightdev/core';
// On-demand revalidation
await revalidatePath('/blog/my-post');
await revalidateTag('blog-posts');
// Create revalidation API handler
const handler = createRevalidateHandler({
secret: process.env.REVALIDATE_SECRET,
});Environment Utilities
import {
isServer,
isBrowser,
isProduction,
isDevelopment
} from '@flightdev/core/utils';
if (isServer()) {
// Server-only code
}
if (isDevelopment()) {
console.log('Debug info');
}Module Exports
| Export | Description |
|--------|-------------|
| @flightdev/core | Main entry with all primitives |
| @flightdev/core/router | Routing utilities |
| @flightdev/core/cache | Caching system |
| @flightdev/core/server | HTTP server |
| @flightdev/core/render | Rendering modes |
| @flightdev/core/middleware | Middleware chain |
| @flightdev/core/actions | Server actions |
| @flightdev/core/streaming | Streaming SSR |
| @flightdev/core/islands | Islands architecture |
| @flightdev/core/errors | Error handling |
| @flightdev/core/react | React integration |
| @flightdev/core/rsc | React Server Components |
| @flightdev/core/config | Configuration |
| @flightdev/core/utils | Environment utilities |
TypeScript
Full TypeScript support with exported types for all APIs:
import type {
FlightConfig,
Route,
RouteMatch,
Cache,
CacheAdapter,
Middleware,
FlightError,
Metadata,
StreamingHints,
} from '@flightdev/core';Build Plugins
Critical CSS Extraction
Extract and inline critical CSS for improved LCP:
npm install critters// vite.config.ts
import { criticalCSS } from '@flightdev/core/plugins';
export default defineConfig({
plugins: [
criticalCSS({
// Strategy for loading non-critical CSS
preload: 'swap', // 'body' | 'media' | 'swap' | 'swap-high' | 'js' | 'js-lazy'
// Remove inlined CSS from source
pruneSource: false,
// Routes to process
include: ['**/*.html'],
exclude: ['/api/**'],
}),
],
});Preload Strategies
| Strategy | Description |
|----------|-------------|
| swap | Use rel="preload" and swap on load |
| swap-high | Like swap with fetchpriority="high" |
| media | Use media="print" and swap |
| js | Load via JavaScript |
| js-lazy | Load via JavaScript when idle |
| body | Move stylesheets to end of body |
CSS Utilities
import {
extractInlineStyles,
mergeCSS,
generatePreloadLink,
generateNoscriptFallback,
} from '@flightdev/core/plugins/critical-css';
// Extract styles from HTML
const { html, styles } = extractInlineStyles(htmlString);
// Generate preload link
const preload = generatePreloadLink('/styles.css', 'swap');
// Generate noscript fallback
const fallback = generateNoscriptFallback('/styles.css');License
MIT
