@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
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flight Cache - Agnostic caching primitives
|
|
3
|
+
*
|
|
4
|
+
* Flight provides the interface, you choose the implementation.
|
|
5
|
+
* Use in-memory, Redis, Cloudflare KV, Upstash, or anything else.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { createCache } from '@flightdev/core';
|
|
10
|
+
* import { redis } from '@flightdev/cache-redis';
|
|
11
|
+
*
|
|
12
|
+
* const cache = createCache({
|
|
13
|
+
* adapter: redis({ url: process.env.REDIS_URL }),
|
|
14
|
+
* defaultTTL: 3600,
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* await cache.set('user:1', userData, { tags: ['users'] });
|
|
18
|
+
* await cache.invalidateTag('users');
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
interface CacheOptions {
|
|
22
|
+
/** Time-to-live in seconds */
|
|
23
|
+
ttl?: number;
|
|
24
|
+
/** Cache tags for invalidation */
|
|
25
|
+
tags?: string[];
|
|
26
|
+
/** Stale-while-revalidate time in seconds */
|
|
27
|
+
swr?: number;
|
|
28
|
+
}
|
|
29
|
+
interface CacheEntry<T> {
|
|
30
|
+
/** Cached value */
|
|
31
|
+
value: T;
|
|
32
|
+
/** When this entry expires (timestamp) */
|
|
33
|
+
expiresAt?: number;
|
|
34
|
+
/** When this entry becomes stale (timestamp) */
|
|
35
|
+
staleAt?: number;
|
|
36
|
+
/** Cache tags for invalidation */
|
|
37
|
+
tags?: string[];
|
|
38
|
+
/** When this entry was created */
|
|
39
|
+
createdAt: number;
|
|
40
|
+
}
|
|
41
|
+
interface Cache {
|
|
42
|
+
/** Get a value from cache */
|
|
43
|
+
get<T>(key: string): Promise<T | undefined>;
|
|
44
|
+
/** Set a value in cache */
|
|
45
|
+
set<T>(key: string, value: T, options?: CacheOptions): Promise<void>;
|
|
46
|
+
/** Delete a value from cache */
|
|
47
|
+
delete(key: string): Promise<boolean>;
|
|
48
|
+
/** Check if a key exists */
|
|
49
|
+
has(key: string): Promise<boolean>;
|
|
50
|
+
/** Clear all cache entries */
|
|
51
|
+
clear(): Promise<void>;
|
|
52
|
+
/** Invalidate entries by tag */
|
|
53
|
+
invalidateTag(tag: string): Promise<void>;
|
|
54
|
+
/** Invalidate entries by multiple tags */
|
|
55
|
+
invalidateTags(tags: string[]): Promise<void>;
|
|
56
|
+
/** Get or set with a factory function */
|
|
57
|
+
getOrSet<T>(key: string, factory: () => Promise<T>, options?: CacheOptions): Promise<T>;
|
|
58
|
+
/** Get multiple values at once */
|
|
59
|
+
getMany<T>(keys: string[]): Promise<Map<string, T | undefined>>;
|
|
60
|
+
/** Set multiple values at once */
|
|
61
|
+
setMany<T>(entries: Map<string, T>, options?: CacheOptions): Promise<void>;
|
|
62
|
+
/** Get cache statistics (if tracking enabled) */
|
|
63
|
+
getStats?(): CacheStats | undefined;
|
|
64
|
+
}
|
|
65
|
+
interface CacheStats {
|
|
66
|
+
hits: number;
|
|
67
|
+
misses: number;
|
|
68
|
+
sets: number;
|
|
69
|
+
deletes: number;
|
|
70
|
+
}
|
|
71
|
+
/** Adapter interface for external cache providers */
|
|
72
|
+
interface CacheAdapter {
|
|
73
|
+
/** Adapter name for identification */
|
|
74
|
+
readonly name: string;
|
|
75
|
+
/** Get an entry from cache */
|
|
76
|
+
get<T>(key: string): Promise<CacheEntry<T> | undefined>;
|
|
77
|
+
/** Set an entry in cache */
|
|
78
|
+
set<T>(key: string, entry: CacheEntry<T>): Promise<void>;
|
|
79
|
+
/** Delete an entry from cache */
|
|
80
|
+
delete(key: string): Promise<boolean>;
|
|
81
|
+
/** Check if key exists */
|
|
82
|
+
has(key: string): Promise<boolean>;
|
|
83
|
+
/** Clear all entries */
|
|
84
|
+
clear(): Promise<void>;
|
|
85
|
+
/** Get keys matching pattern (optional) */
|
|
86
|
+
keys?(pattern?: string): Promise<string[]>;
|
|
87
|
+
/** Get multiple entries at once (optional, for performance) */
|
|
88
|
+
getMany?<T>(keys: string[]): Promise<Map<string, CacheEntry<T> | undefined>>;
|
|
89
|
+
/** Set multiple entries at once (optional, for performance) */
|
|
90
|
+
setMany?<T>(entries: Map<string, CacheEntry<T>>): Promise<void>;
|
|
91
|
+
/** Whether this adapter supports distributed tags natively */
|
|
92
|
+
readonly supportsDistributedTags?: boolean;
|
|
93
|
+
/** Add a key to a tag set (for distributed tag management) */
|
|
94
|
+
addToTag?(tag: string, key: string): Promise<void>;
|
|
95
|
+
/** Get all keys associated with a tag */
|
|
96
|
+
getTagMembers?(tag: string): Promise<string[]>;
|
|
97
|
+
/** Delete a tag and return its members */
|
|
98
|
+
deleteTag?(tag: string): Promise<string[]>;
|
|
99
|
+
/** Close connection (for cleanup) */
|
|
100
|
+
close?(): Promise<void>;
|
|
101
|
+
/** Check if adapter is healthy */
|
|
102
|
+
ping?(): Promise<boolean>;
|
|
103
|
+
}
|
|
104
|
+
interface Serializer {
|
|
105
|
+
serialize(value: unknown): string;
|
|
106
|
+
deserialize<T>(data: string): T;
|
|
107
|
+
}
|
|
108
|
+
/** Default JSON serializer */
|
|
109
|
+
declare const jsonSerializer: Serializer;
|
|
110
|
+
interface MemoryCacheAdapterOptions {
|
|
111
|
+
/** Maximum number of entries (0 = unlimited) */
|
|
112
|
+
maxSize?: number;
|
|
113
|
+
/** Cleanup interval in ms (default: 60000) */
|
|
114
|
+
cleanupInterval?: number;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Create in-memory cache adapter
|
|
118
|
+
*/
|
|
119
|
+
declare function memory(options?: MemoryCacheAdapterOptions): CacheAdapter;
|
|
120
|
+
interface CreateCacheOptions {
|
|
121
|
+
/** Custom cache adapter (defaults to in-memory) */
|
|
122
|
+
adapter?: CacheAdapter;
|
|
123
|
+
/** Default TTL for all entries (seconds) */
|
|
124
|
+
defaultTTL?: number;
|
|
125
|
+
/** Key prefix for namespacing */
|
|
126
|
+
prefix?: string;
|
|
127
|
+
/** Custom serializer (for external adapters) */
|
|
128
|
+
serializer?: Serializer;
|
|
129
|
+
/** Track cache statistics */
|
|
130
|
+
trackStats?: boolean;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Create a new cache instance
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* // Simple in-memory cache
|
|
138
|
+
* const cache = createCache();
|
|
139
|
+
*
|
|
140
|
+
* // With Redis adapter
|
|
141
|
+
* import { redis } from '@flightdev/cache-redis';
|
|
142
|
+
* const cache = createCache({
|
|
143
|
+
* adapter: redis({ url: 'redis://localhost:6379' }),
|
|
144
|
+
* defaultTTL: 3600,
|
|
145
|
+
* prefix: 'myapp',
|
|
146
|
+
* });
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
declare function createCache(options?: CreateCacheOptions): Cache;
|
|
150
|
+
/**
|
|
151
|
+
* Create a cache key from multiple parts
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```typescript
|
|
155
|
+
* cacheKey('user', userId, 'profile'); // "user:123:profile"
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
declare function cacheKey(...parts: (string | number | boolean | undefined)[]): string;
|
|
159
|
+
/**
|
|
160
|
+
* Wrap a function with caching
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```typescript
|
|
164
|
+
* const getCachedUser = cached(cache, getUser, {
|
|
165
|
+
* ttl: 300,
|
|
166
|
+
* keyFn: (userId) => `user:${userId}`,
|
|
167
|
+
* });
|
|
168
|
+
*
|
|
169
|
+
* const user = await getCachedUser(123);
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
declare function cached<T extends (...args: unknown[]) => Promise<unknown>>(cache: Cache, fn: T, options?: CacheOptions & {
|
|
173
|
+
keyFn?: (...args: Parameters<T>) => string;
|
|
174
|
+
}): T;
|
|
175
|
+
/**
|
|
176
|
+
* Create a deduplication wrapper to prevent duplicate concurrent requests
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```typescript
|
|
180
|
+
* const dedupedFetch = dedupe(fetchUser);
|
|
181
|
+
*
|
|
182
|
+
* // These will share the same request
|
|
183
|
+
* const [user1, user2] = await Promise.all([
|
|
184
|
+
* dedupedFetch(123),
|
|
185
|
+
* dedupedFetch(123),
|
|
186
|
+
* ]);
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
declare function dedupe<T extends (...args: unknown[]) => Promise<unknown>>(fn: T, keyFn?: (...args: Parameters<T>) => string): T;
|
|
190
|
+
|
|
191
|
+
export { type Cache, type CacheAdapter, type CacheEntry, type CacheOptions, type CacheStats, type CreateCacheOptions, type MemoryCacheAdapterOptions, type Serializer, cacheKey, cached, createCache, dedupe, jsonSerializer, memory };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
// src/adapters/handler.ts
|
|
2
|
+
function createUniversalHandler(options = {}) {
|
|
3
|
+
const {
|
|
4
|
+
manifest,
|
|
5
|
+
routeRules = {},
|
|
6
|
+
basePath = "",
|
|
7
|
+
onError = defaultErrorHandler,
|
|
8
|
+
onNotFound = defaultNotFoundHandler,
|
|
9
|
+
hooks = {}
|
|
10
|
+
} = options;
|
|
11
|
+
return {
|
|
12
|
+
/**
|
|
13
|
+
* Handle incoming request
|
|
14
|
+
*/
|
|
15
|
+
async fetch(request, env, ctx) {
|
|
16
|
+
try {
|
|
17
|
+
if (hooks.beforeHandle) {
|
|
18
|
+
const modifiedRequest = await hooks.beforeHandle(request);
|
|
19
|
+
if (modifiedRequest) request = modifiedRequest;
|
|
20
|
+
}
|
|
21
|
+
const url = new URL(request.url);
|
|
22
|
+
let pathname = url.pathname;
|
|
23
|
+
if (basePath && pathname.startsWith(basePath)) {
|
|
24
|
+
pathname = pathname.slice(basePath.length) || "/";
|
|
25
|
+
}
|
|
26
|
+
const rules = matchRouteRules(pathname, routeRules);
|
|
27
|
+
if (rules?.redirect) {
|
|
28
|
+
return handleRedirect(rules.redirect);
|
|
29
|
+
}
|
|
30
|
+
if (rules?.proxy) {
|
|
31
|
+
return handleProxy(request, rules.proxy);
|
|
32
|
+
}
|
|
33
|
+
if (request.method === "OPTIONS" && rules?.cors) {
|
|
34
|
+
return handleCorsPreFlight(rules.cors);
|
|
35
|
+
}
|
|
36
|
+
const route = manifest?.routes?.find((r) => matchPath(pathname, r.path));
|
|
37
|
+
if (!route) {
|
|
38
|
+
return await onNotFound(request);
|
|
39
|
+
}
|
|
40
|
+
const context = {
|
|
41
|
+
url,
|
|
42
|
+
params: extractParams(pathname, route.path),
|
|
43
|
+
headers: request.headers,
|
|
44
|
+
method: request.method
|
|
45
|
+
};
|
|
46
|
+
if (hooks.beforeRender) {
|
|
47
|
+
await hooks.beforeRender(context);
|
|
48
|
+
}
|
|
49
|
+
let response;
|
|
50
|
+
switch (route.mode) {
|
|
51
|
+
case "ssr":
|
|
52
|
+
case "isr":
|
|
53
|
+
response = await handleSSR(context, options);
|
|
54
|
+
break;
|
|
55
|
+
case "ssg":
|
|
56
|
+
response = await handleSSG(context, options);
|
|
57
|
+
break;
|
|
58
|
+
case "csr":
|
|
59
|
+
response = handleCSR(options);
|
|
60
|
+
break;
|
|
61
|
+
default:
|
|
62
|
+
response = await handleSSR(context, options);
|
|
63
|
+
}
|
|
64
|
+
if (rules?.cache) {
|
|
65
|
+
response = applyCacheHeaders(response, rules.cache);
|
|
66
|
+
}
|
|
67
|
+
if (rules?.cors) {
|
|
68
|
+
response = applyCorsHeaders(response, rules.cors, request);
|
|
69
|
+
}
|
|
70
|
+
if (rules?.headers) {
|
|
71
|
+
response = applyCustomHeaders(response, rules.headers);
|
|
72
|
+
}
|
|
73
|
+
if (hooks.afterHandle) {
|
|
74
|
+
const modifiedResponse = await hooks.afterHandle(request, response);
|
|
75
|
+
if (modifiedResponse) response = modifiedResponse;
|
|
76
|
+
}
|
|
77
|
+
return response;
|
|
78
|
+
} catch (error) {
|
|
79
|
+
return onError(error, request);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
/**
|
|
83
|
+
* Check if this handler can handle the request
|
|
84
|
+
*/
|
|
85
|
+
canHandle(request) {
|
|
86
|
+
const url = new URL(request.url);
|
|
87
|
+
let pathname = url.pathname;
|
|
88
|
+
if (basePath && pathname.startsWith(basePath)) {
|
|
89
|
+
pathname = pathname.slice(basePath.length) || "/";
|
|
90
|
+
}
|
|
91
|
+
if (manifest?.routes) {
|
|
92
|
+
return manifest.routes.some((r) => matchPath(pathname, r.path));
|
|
93
|
+
}
|
|
94
|
+
return Object.keys(routeRules).some(
|
|
95
|
+
(pattern) => matchPath(pathname, pattern)
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function defaultErrorHandler(error, _request) {
|
|
101
|
+
console.error("[Flight] Request error:", error);
|
|
102
|
+
return new Response(
|
|
103
|
+
JSON.stringify({
|
|
104
|
+
error: "Internal Server Error",
|
|
105
|
+
message: process.env.NODE_ENV === "development" ? error.message : void 0
|
|
106
|
+
}),
|
|
107
|
+
{
|
|
108
|
+
status: 500,
|
|
109
|
+
headers: { "Content-Type": "application/json" }
|
|
110
|
+
}
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
function defaultNotFoundHandler(_request) {
|
|
114
|
+
return new Response("Not Found", { status: 404 });
|
|
115
|
+
}
|
|
116
|
+
function matchRouteRules(pathname, rules) {
|
|
117
|
+
for (const [pattern, config] of Object.entries(rules)) {
|
|
118
|
+
if (matchPath(pathname, pattern)) {
|
|
119
|
+
return config;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
function matchPath(pathname, pattern) {
|
|
125
|
+
if (pattern === pathname) return true;
|
|
126
|
+
if (pattern.endsWith("/**")) {
|
|
127
|
+
const prefix = pattern.slice(0, -3);
|
|
128
|
+
return pathname.startsWith(prefix);
|
|
129
|
+
}
|
|
130
|
+
if (pattern.endsWith("/*")) {
|
|
131
|
+
const prefix = pattern.slice(0, -2);
|
|
132
|
+
const rest = pathname.slice(prefix.length);
|
|
133
|
+
return pathname.startsWith(prefix) && !rest.includes("/");
|
|
134
|
+
}
|
|
135
|
+
const patternParts = pattern.split("/");
|
|
136
|
+
const pathParts = pathname.split("/");
|
|
137
|
+
if (patternParts.length !== pathParts.length) return false;
|
|
138
|
+
return patternParts.every((part, i) => {
|
|
139
|
+
if (part.startsWith("[") && part.endsWith("]")) return true;
|
|
140
|
+
if (part.startsWith(":")) return true;
|
|
141
|
+
return part === pathParts[i];
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
function extractParams(pathname, pattern) {
|
|
145
|
+
const params = {};
|
|
146
|
+
const patternParts = pattern.split("/");
|
|
147
|
+
const pathParts = pathname.split("/");
|
|
148
|
+
patternParts.forEach((part, i) => {
|
|
149
|
+
if (part.startsWith("[") && part.endsWith("]")) {
|
|
150
|
+
const key = part.slice(1, -1).replace("...", "");
|
|
151
|
+
params[key] = pathParts[i] ?? "";
|
|
152
|
+
} else if (part.startsWith(":")) {
|
|
153
|
+
params[part.slice(1)] = pathParts[i] ?? "";
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
return params;
|
|
157
|
+
}
|
|
158
|
+
function handleRedirect(redirect) {
|
|
159
|
+
const to = typeof redirect === "string" ? redirect : redirect.to;
|
|
160
|
+
const status = typeof redirect === "string" ? 302 : redirect.statusCode ?? 302;
|
|
161
|
+
return new Response(null, {
|
|
162
|
+
status,
|
|
163
|
+
headers: { Location: to }
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
async function handleProxy(request, target) {
|
|
167
|
+
const url = new URL(request.url);
|
|
168
|
+
const targetUrl = new URL(target);
|
|
169
|
+
targetUrl.pathname = url.pathname;
|
|
170
|
+
targetUrl.search = url.search;
|
|
171
|
+
const proxyRequest = new Request(targetUrl.toString(), {
|
|
172
|
+
method: request.method,
|
|
173
|
+
headers: request.headers,
|
|
174
|
+
body: request.body
|
|
175
|
+
});
|
|
176
|
+
return fetch(proxyRequest);
|
|
177
|
+
}
|
|
178
|
+
function handleCorsPreFlight(cors) {
|
|
179
|
+
const options = cors === true ? {} : cors || {};
|
|
180
|
+
return new Response(null, {
|
|
181
|
+
status: 204,
|
|
182
|
+
headers: {
|
|
183
|
+
"Access-Control-Allow-Origin": resolveOrigin(options.origin),
|
|
184
|
+
"Access-Control-Allow-Methods": (options.methods ?? ["GET", "POST", "PUT", "DELETE", "PATCH"]).join(", "),
|
|
185
|
+
"Access-Control-Allow-Headers": (options.allowedHeaders ?? ["Content-Type", "Authorization"]).join(", "),
|
|
186
|
+
"Access-Control-Max-Age": String(options.maxAge ?? 86400),
|
|
187
|
+
...options.credentials && { "Access-Control-Allow-Credentials": "true" }
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
function resolveOrigin(origin) {
|
|
192
|
+
if (origin === true || origin === void 0) return "*";
|
|
193
|
+
if (origin === false) return "*";
|
|
194
|
+
if (Array.isArray(origin)) return origin[0] ?? "*";
|
|
195
|
+
return origin;
|
|
196
|
+
}
|
|
197
|
+
function applyCacheHeaders(response, cache) {
|
|
198
|
+
const headers = new Headers(response.headers);
|
|
199
|
+
let cacheControl = `public, max-age=${cache.maxAge ?? 0}`;
|
|
200
|
+
if (cache.swr) {
|
|
201
|
+
cacheControl += `, stale-while-revalidate=${cache.swr}`;
|
|
202
|
+
}
|
|
203
|
+
headers.set("Cache-Control", cacheControl);
|
|
204
|
+
return new Response(response.body, {
|
|
205
|
+
status: response.status,
|
|
206
|
+
statusText: response.statusText,
|
|
207
|
+
headers
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
function applyCorsHeaders(response, cors, _request) {
|
|
211
|
+
const options = cors === true ? {} : cors || {};
|
|
212
|
+
const headers = new Headers(response.headers);
|
|
213
|
+
headers.set("Access-Control-Allow-Origin", resolveOrigin(options.origin));
|
|
214
|
+
if (options.credentials) {
|
|
215
|
+
headers.set("Access-Control-Allow-Credentials", "true");
|
|
216
|
+
}
|
|
217
|
+
if (options.exposedHeaders) {
|
|
218
|
+
headers.set("Access-Control-Expose-Headers", options.exposedHeaders.join(", "));
|
|
219
|
+
}
|
|
220
|
+
return new Response(response.body, {
|
|
221
|
+
status: response.status,
|
|
222
|
+
statusText: response.statusText,
|
|
223
|
+
headers
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
function applyCustomHeaders(response, customHeaders) {
|
|
227
|
+
const headers = new Headers(response.headers);
|
|
228
|
+
for (const [key, value] of Object.entries(customHeaders)) {
|
|
229
|
+
headers.set(key, value);
|
|
230
|
+
}
|
|
231
|
+
return new Response(response.body, {
|
|
232
|
+
status: response.status,
|
|
233
|
+
statusText: response.statusText,
|
|
234
|
+
headers
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
async function handleSSR(context, options) {
|
|
238
|
+
const { framework, hooks } = options;
|
|
239
|
+
if (!framework) {
|
|
240
|
+
return new Response(createPlaceholderHTML(context.url.pathname), {
|
|
241
|
+
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
let result = await framework.renderToString({}, context);
|
|
245
|
+
if (hooks?.afterRender) {
|
|
246
|
+
const modifiedResult = await hooks.afterRender(result);
|
|
247
|
+
if (modifiedResult) result = modifiedResult;
|
|
248
|
+
}
|
|
249
|
+
return new Response(result.html, {
|
|
250
|
+
status: result.status,
|
|
251
|
+
headers: result.headers
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
async function handleSSG(context, options) {
|
|
255
|
+
return handleSSR(context, options);
|
|
256
|
+
}
|
|
257
|
+
function handleCSR(options) {
|
|
258
|
+
const html = `<!DOCTYPE html>
|
|
259
|
+
<html>
|
|
260
|
+
<head>
|
|
261
|
+
<meta charset="UTF-8">
|
|
262
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
263
|
+
<title>Flight App</title>
|
|
264
|
+
</head>
|
|
265
|
+
<body>
|
|
266
|
+
<div id="app"></div>
|
|
267
|
+
<script type="module" src="/assets/client.js"></script>
|
|
268
|
+
</body>
|
|
269
|
+
</html>`;
|
|
270
|
+
return new Response(html, {
|
|
271
|
+
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
function createPlaceholderHTML(pathname) {
|
|
275
|
+
return `<!DOCTYPE html>
|
|
276
|
+
<html>
|
|
277
|
+
<head>
|
|
278
|
+
<meta charset="UTF-8">
|
|
279
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
280
|
+
<title>Flight</title>
|
|
281
|
+
<style>
|
|
282
|
+
body { font-family: system-ui, sans-serif; display: flex; align-items: center; justify-content: center; min-height: 100vh; margin: 0; background: #0a0a0a; color: #fafafa; }
|
|
283
|
+
.container { text-align: center; }
|
|
284
|
+
h1 { font-size: 3rem; margin-bottom: 0.5rem; }
|
|
285
|
+
p { color: #888; }
|
|
286
|
+
code { background: #1a1a1a; padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.875rem; }
|
|
287
|
+
</style>
|
|
288
|
+
</head>
|
|
289
|
+
<body>
|
|
290
|
+
<div class="container">
|
|
291
|
+
<h1>Flight</h1>
|
|
292
|
+
<p>Path: <code>${pathname}</code></p>
|
|
293
|
+
<p>Configure a UI framework adapter to enable SSR.</p>
|
|
294
|
+
</div>
|
|
295
|
+
</body>
|
|
296
|
+
</html>`;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// src/adapters/validation.ts
|
|
300
|
+
function createValidatedAdapter(name, schema, factory) {
|
|
301
|
+
return (rawOptions = {}) => {
|
|
302
|
+
const parseResult = schema.safeParse(rawOptions);
|
|
303
|
+
if (!parseResult.success) {
|
|
304
|
+
const errorMessage = formatValidationError(name, parseResult.error);
|
|
305
|
+
throw new Error(errorMessage);
|
|
306
|
+
}
|
|
307
|
+
const validatedOptions = parseResult.data;
|
|
308
|
+
const adapterConfig = factory(validatedOptions);
|
|
309
|
+
return {
|
|
310
|
+
name,
|
|
311
|
+
adapt: adapterConfig.adapt,
|
|
312
|
+
emulate: adapterConfig.emulate,
|
|
313
|
+
supports: adapterConfig.supports
|
|
314
|
+
};
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
function validateAdapterOptions(schema, options) {
|
|
318
|
+
const result = schema.safeParse(options);
|
|
319
|
+
return result.success ? result.data : null;
|
|
320
|
+
}
|
|
321
|
+
function assertValidOptions(name, schema, options) {
|
|
322
|
+
const result = schema.safeParse(options);
|
|
323
|
+
if (!result.success) {
|
|
324
|
+
throw new Error(formatValidationError(name, result.error));
|
|
325
|
+
}
|
|
326
|
+
return result.data;
|
|
327
|
+
}
|
|
328
|
+
function formatValidationError(adapterName, error) {
|
|
329
|
+
const prefix = `[${adapterName}] Invalid adapter options`;
|
|
330
|
+
if (error && typeof error === "object" && "issues" in error) {
|
|
331
|
+
const zodError = error;
|
|
332
|
+
const issues = zodError.issues.map((issue) => ` - ${issue.path.join(".")}: ${issue.message}`).join("\n");
|
|
333
|
+
return `${prefix}:
|
|
334
|
+
${issues}`;
|
|
335
|
+
}
|
|
336
|
+
if (error instanceof Error) {
|
|
337
|
+
return `${prefix}: ${error.message}`;
|
|
338
|
+
}
|
|
339
|
+
return `${prefix}: Unknown validation error`;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// src/adapters/index.ts
|
|
343
|
+
function createAdapter(options) {
|
|
344
|
+
return {
|
|
345
|
+
name: options.name,
|
|
346
|
+
adapt: options.adapt,
|
|
347
|
+
emulate: options.emulate,
|
|
348
|
+
supports: options.supports
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export { assertValidOptions, createAdapter, createUniversalHandler, createValidatedAdapter, validateAdapterOptions };
|
|
353
|
+
//# sourceMappingURL=chunk-62HISNA3.js.map
|
|
354
|
+
//# sourceMappingURL=chunk-62HISNA3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/handler.ts","../src/adapters/validation.ts","../src/adapters/index.ts"],"names":[],"mappings":"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","file":"chunk-62HISNA3.js","sourcesContent":["/**\r\n * Flight Universal Handler - Platform-agnostic request handler\r\n * \r\n * This is the core abstraction that allows Flight applications to run anywhere.\r\n * Adapters can use this handler, or implement their own - zero lock-in.\r\n * \r\n * @module @flightdev/core/adapters/handler\r\n */\r\n\r\nimport type { RenderContext, RenderResult, UIFrameworkAdapter } from '../render/index.js';\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\n/**\r\n * Route rules for per-path configuration\r\n * Similar to Nitro's routeRules - completely optional to use\r\n */\r\nexport interface RouteRules {\r\n [pattern: string]: RouteRuleConfig;\r\n}\r\n\r\nexport interface RouteRuleConfig {\r\n /** Cache configuration */\r\n cache?: {\r\n /** Max age in seconds */\r\n maxAge?: number;\r\n /** Stale-while-revalidate in seconds */\r\n swr?: number;\r\n /** Vary headers */\r\n vary?: string[];\r\n };\r\n\r\n /** CORS configuration */\r\n cors?: boolean | CorsOptions;\r\n\r\n /** Headers to add to response */\r\n headers?: Record<string, string>;\r\n\r\n /** Redirect configuration */\r\n redirect?: string | {\r\n to: string;\r\n statusCode?: 301 | 302 | 303 | 307 | 308;\r\n };\r\n\r\n /** Proxy to another URL */\r\n proxy?: string;\r\n\r\n /** Render mode override */\r\n render?: 'ssr' | 'ssg' | 'csr' | 'isr';\r\n\r\n /** ISR revalidation time in seconds */\r\n isr?: number;\r\n\r\n /** Prerender at build time */\r\n prerender?: boolean;\r\n}\r\n\r\nexport interface CorsOptions {\r\n origin?: string | string[] | boolean;\r\n methods?: string[];\r\n allowedHeaders?: string[];\r\n exposedHeaders?: string[];\r\n credentials?: boolean;\r\n maxAge?: number;\r\n}\r\n\r\n/**\r\n * Handler options - all optional, user configures what they need\r\n */\r\nexport interface UniversalHandlerOptions {\r\n /** Route manifest from build */\r\n manifest?: RouteManifest;\r\n\r\n /** UI framework adapter (optional - for SSR) */\r\n framework?: UIFrameworkAdapter;\r\n\r\n /** Route rules (optional) */\r\n routeRules?: RouteRules;\r\n\r\n /** Static files directory (optional) */\r\n staticDir?: string;\r\n\r\n /** Base path (optional) */\r\n basePath?: string;\r\n\r\n /** Custom error handler (optional) */\r\n onError?: (error: Error, request: Request) => Response | Promise<Response>;\r\n\r\n /** Custom not found handler (optional) */\r\n onNotFound?: (request: Request) => Response | Promise<Response>;\r\n\r\n /** Hooks for extensibility (optional) */\r\n hooks?: HandlerHooks;\r\n}\r\n\r\n/**\r\n * Handler hooks for extensibility - all optional\r\n */\r\nexport interface HandlerHooks {\r\n /** Before handling request */\r\n beforeHandle?: (request: Request) => Request | Promise<Request> | void;\r\n\r\n /** After getting response, before sending */\r\n afterHandle?: (request: Request, response: Response) => Response | Promise<Response> | void;\r\n\r\n /** Before SSR render */\r\n beforeRender?: (context: RenderContext) => void | Promise<void>;\r\n\r\n /** After SSR render */\r\n afterRender?: (result: RenderResult) => RenderResult | Promise<RenderResult>;\r\n}\r\n\r\n/**\r\n * Route manifest from build\r\n */\r\nexport interface RouteManifest {\r\n routes: Array<{\r\n path: string;\r\n component: string;\r\n mode: 'ssr' | 'ssg' | 'csr' | 'isr';\r\n handler?: string;\r\n }>;\r\n staticFiles?: string[];\r\n}\r\n\r\n/**\r\n * Universal handler interface\r\n */\r\nexport interface UniversalHandler {\r\n /** Handle a fetch request */\r\n fetch(request: Request, env?: unknown, ctx?: unknown): Promise<Response>;\r\n\r\n /** Check if this handler can handle the request */\r\n canHandle(request: Request): boolean;\r\n}\r\n\r\n// ============================================================================\r\n// Handler Factory\r\n// ============================================================================\r\n\r\n/**\r\n * Create a universal request handler\r\n * \r\n * This is the main abstraction for handling requests.\r\n * Adapters can use this, extend it, or ignore it completely.\r\n * \r\n * @example\r\n * ```typescript\r\n * // Using the handler (optional)\r\n * import { createUniversalHandler } from '@flightdev/core/adapters';\r\n * \r\n * const handler = createUniversalHandler({\r\n * manifest: await import('./manifest.json'),\r\n * routeRules: {\r\n * '/api/**': { cors: true },\r\n * '/blog/**': { cache: { maxAge: 3600 } },\r\n * },\r\n * });\r\n * \r\n * // In your adapter\r\n * export default {\r\n * fetch: handler.fetch,\r\n * };\r\n * ```\r\n */\r\nexport function createUniversalHandler(options: UniversalHandlerOptions = {}): UniversalHandler {\r\n const {\r\n manifest,\r\n routeRules = {},\r\n basePath = '',\r\n onError = defaultErrorHandler,\r\n onNotFound = defaultNotFoundHandler,\r\n hooks = {},\r\n } = options;\r\n\r\n return {\r\n /**\r\n * Handle incoming request\r\n */\r\n async fetch(request: Request, env?: unknown, ctx?: unknown): Promise<Response> {\r\n try {\r\n // Run beforeHandle hook\r\n if (hooks.beforeHandle) {\r\n const modifiedRequest = await hooks.beforeHandle(request);\r\n if (modifiedRequest) request = modifiedRequest;\r\n }\r\n\r\n const url = new URL(request.url);\r\n let pathname = url.pathname;\r\n\r\n // Strip base path if present\r\n if (basePath && pathname.startsWith(basePath)) {\r\n pathname = pathname.slice(basePath.length) || '/';\r\n }\r\n\r\n // Apply route rules\r\n const rules = matchRouteRules(pathname, routeRules);\r\n\r\n // Handle redirect rules\r\n if (rules?.redirect) {\r\n return handleRedirect(rules.redirect);\r\n }\r\n\r\n // Handle proxy rules\r\n if (rules?.proxy) {\r\n return handleProxy(request, rules.proxy);\r\n }\r\n\r\n // Handle CORS preflight\r\n if (request.method === 'OPTIONS' && rules?.cors) {\r\n return handleCorsPreFlight(rules.cors);\r\n }\r\n\r\n // Find matching route from manifest\r\n const route = manifest?.routes?.find(r => matchPath(pathname, r.path));\r\n\r\n // No matching route - try static or return 404\r\n if (!route) {\r\n return await onNotFound(request);\r\n }\r\n\r\n // Build render context\r\n const context: RenderContext = {\r\n url,\r\n params: extractParams(pathname, route.path),\r\n headers: request.headers,\r\n method: request.method,\r\n };\r\n\r\n // Run beforeRender hook\r\n if (hooks.beforeRender) {\r\n await hooks.beforeRender(context);\r\n }\r\n\r\n // Generate response based on route mode\r\n let response: Response;\r\n\r\n switch (route.mode) {\r\n case 'ssr':\r\n case 'isr':\r\n response = await handleSSR(context, options);\r\n break;\r\n case 'ssg':\r\n response = await handleSSG(context, options);\r\n break;\r\n case 'csr':\r\n response = handleCSR(options);\r\n break;\r\n default:\r\n response = await handleSSR(context, options);\r\n }\r\n\r\n // Apply cache headers from route rules\r\n if (rules?.cache) {\r\n response = applyCacheHeaders(response, rules.cache);\r\n }\r\n\r\n // Apply CORS headers\r\n if (rules?.cors) {\r\n response = applyCorsHeaders(response, rules.cors, request);\r\n }\r\n\r\n // Apply custom headers\r\n if (rules?.headers) {\r\n response = applyCustomHeaders(response, rules.headers);\r\n }\r\n\r\n // Run afterHandle hook\r\n if (hooks.afterHandle) {\r\n const modifiedResponse = await hooks.afterHandle(request, response);\r\n if (modifiedResponse) response = modifiedResponse;\r\n }\r\n\r\n return response;\r\n\r\n } catch (error) {\r\n return onError(error as Error, request);\r\n }\r\n },\r\n\r\n /**\r\n * Check if this handler can handle the request\r\n */\r\n canHandle(request: Request): boolean {\r\n const url = new URL(request.url);\r\n let pathname = url.pathname;\r\n\r\n if (basePath && pathname.startsWith(basePath)) {\r\n pathname = pathname.slice(basePath.length) || '/';\r\n }\r\n\r\n // Check manifest routes\r\n if (manifest?.routes) {\r\n return manifest.routes.some(r => matchPath(pathname, r.path));\r\n }\r\n\r\n // Check route rules\r\n return Object.keys(routeRules).some(pattern =>\r\n matchPath(pathname, pattern)\r\n );\r\n },\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// Internal Helpers\r\n// ============================================================================\r\n\r\nfunction defaultErrorHandler(error: Error, _request: Request): Response {\r\n console.error('[Flight] Request error:', error);\r\n\r\n return new Response(\r\n JSON.stringify({\r\n error: 'Internal Server Error',\r\n message: process.env.NODE_ENV === 'development' ? error.message : undefined,\r\n }),\r\n {\r\n status: 500,\r\n headers: { 'Content-Type': 'application/json' },\r\n }\r\n );\r\n}\r\n\r\nfunction defaultNotFoundHandler(_request: Request): Response {\r\n return new Response('Not Found', { status: 404 });\r\n}\r\n\r\nfunction matchRouteRules(pathname: string, rules: RouteRules): RouteRuleConfig | null {\r\n for (const [pattern, config] of Object.entries(rules)) {\r\n if (matchPath(pathname, pattern)) {\r\n return config;\r\n }\r\n }\r\n return null;\r\n}\r\n\r\nfunction matchPath(pathname: string, pattern: string): boolean {\r\n // Handle exact match\r\n if (pattern === pathname) return true;\r\n\r\n // Handle wildcard patterns\r\n if (pattern.endsWith('/**')) {\r\n const prefix = pattern.slice(0, -3);\r\n return pathname.startsWith(prefix);\r\n }\r\n\r\n if (pattern.endsWith('/*')) {\r\n const prefix = pattern.slice(0, -2);\r\n const rest = pathname.slice(prefix.length);\r\n return pathname.startsWith(prefix) && !rest.includes('/');\r\n }\r\n\r\n // Handle dynamic params\r\n const patternParts = pattern.split('/');\r\n const pathParts = pathname.split('/');\r\n\r\n if (patternParts.length !== pathParts.length) return false;\r\n\r\n return patternParts.every((part, i) => {\r\n if (part.startsWith('[') && part.endsWith(']')) return true;\r\n if (part.startsWith(':')) return true;\r\n return part === pathParts[i];\r\n });\r\n}\r\n\r\nfunction extractParams(pathname: string, pattern: string): Record<string, string> {\r\n const params: Record<string, string> = {};\r\n const patternParts = pattern.split('/');\r\n const pathParts = pathname.split('/');\r\n\r\n patternParts.forEach((part, i) => {\r\n if (part.startsWith('[') && part.endsWith(']')) {\r\n const key = part.slice(1, -1).replace('...', '');\r\n params[key] = pathParts[i] ?? '';\r\n } else if (part.startsWith(':')) {\r\n params[part.slice(1)] = pathParts[i] ?? '';\r\n }\r\n });\r\n\r\n return params;\r\n}\r\n\r\nfunction handleRedirect(redirect: string | { to: string; statusCode?: number }): Response {\r\n const to = typeof redirect === 'string' ? redirect : redirect.to;\r\n const status = typeof redirect === 'string' ? 302 : (redirect.statusCode ?? 302);\r\n\r\n return new Response(null, {\r\n status,\r\n headers: { Location: to },\r\n });\r\n}\r\n\r\nasync function handleProxy(request: Request, target: string): Promise<Response> {\r\n const url = new URL(request.url);\r\n const targetUrl = new URL(target);\r\n targetUrl.pathname = url.pathname;\r\n targetUrl.search = url.search;\r\n\r\n const proxyRequest = new Request(targetUrl.toString(), {\r\n method: request.method,\r\n headers: request.headers,\r\n body: request.body,\r\n });\r\n\r\n return fetch(proxyRequest);\r\n}\r\n\r\nfunction handleCorsPreFlight(cors: boolean | CorsOptions): Response {\r\n // Normalize CORS options - true means default options, object means custom\r\n const options: CorsOptions = cors === true ? {} : (cors || {});\r\n\r\n return new Response(null, {\r\n status: 204,\r\n headers: {\r\n 'Access-Control-Allow-Origin': resolveOrigin(options.origin),\r\n 'Access-Control-Allow-Methods': (options.methods ?? ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']).join(', '),\r\n 'Access-Control-Allow-Headers': (options.allowedHeaders ?? ['Content-Type', 'Authorization']).join(', '),\r\n 'Access-Control-Max-Age': String(options.maxAge ?? 86400),\r\n ...(options.credentials && { 'Access-Control-Allow-Credentials': 'true' }),\r\n },\r\n });\r\n}\r\n\r\nfunction resolveOrigin(origin?: string | string[] | boolean): string {\r\n if (origin === true || origin === undefined) return '*';\r\n if (origin === false) return '*';\r\n if (Array.isArray(origin)) return origin[0] ?? '*';\r\n return origin;\r\n}\r\n\r\nfunction applyCacheHeaders(response: Response, cache: { maxAge?: number; swr?: number }): Response {\r\n const headers = new Headers(response.headers);\r\n\r\n let cacheControl = `public, max-age=${cache.maxAge ?? 0}`;\r\n if (cache.swr) {\r\n cacheControl += `, stale-while-revalidate=${cache.swr}`;\r\n }\r\n\r\n headers.set('Cache-Control', cacheControl);\r\n\r\n return new Response(response.body, {\r\n status: response.status,\r\n statusText: response.statusText,\r\n headers,\r\n });\r\n}\r\n\r\nfunction applyCorsHeaders(response: Response, cors: boolean | CorsOptions, _request: Request): Response {\r\n // Normalize CORS options\r\n const options: CorsOptions = cors === true ? {} : (cors || {});\r\n const headers = new Headers(response.headers);\r\n\r\n headers.set('Access-Control-Allow-Origin', resolveOrigin(options.origin));\r\n\r\n if (options.credentials) {\r\n headers.set('Access-Control-Allow-Credentials', 'true');\r\n }\r\n\r\n if (options.exposedHeaders) {\r\n headers.set('Access-Control-Expose-Headers', options.exposedHeaders.join(', '));\r\n }\r\n\r\n return new Response(response.body, {\r\n status: response.status,\r\n statusText: response.statusText,\r\n headers,\r\n });\r\n}\r\n\r\nfunction applyCustomHeaders(response: Response, customHeaders: Record<string, string>): Response {\r\n const headers = new Headers(response.headers);\r\n\r\n for (const [key, value] of Object.entries(customHeaders)) {\r\n headers.set(key, value);\r\n }\r\n\r\n return new Response(response.body, {\r\n status: response.status,\r\n statusText: response.statusText,\r\n headers,\r\n });\r\n}\r\n\r\nasync function handleSSR(context: RenderContext, options: UniversalHandlerOptions): Promise<Response> {\r\n const { framework, hooks } = options;\r\n\r\n // If no framework adapter, return placeholder\r\n if (!framework) {\r\n return new Response(createPlaceholderHTML(context.url.pathname), {\r\n headers: { 'Content-Type': 'text/html; charset=utf-8' },\r\n });\r\n }\r\n\r\n // Render with framework adapter\r\n let result = await framework.renderToString({} as never, context);\r\n\r\n // Run afterRender hook\r\n if (hooks?.afterRender) {\r\n const modifiedResult = await hooks.afterRender(result);\r\n if (modifiedResult) result = modifiedResult;\r\n }\r\n\r\n return new Response(result.html, {\r\n status: result.status,\r\n headers: result.headers,\r\n });\r\n}\r\n\r\nasync function handleSSG(context: RenderContext, options: UniversalHandlerOptions): Promise<Response> {\r\n // For SSG, typically serve pre-rendered HTML\r\n // Fall back to SSR if not found\r\n return handleSSR(context, options);\r\n}\r\n\r\nfunction handleCSR(options: UniversalHandlerOptions): Response {\r\n const html = `<!DOCTYPE html>\r\n<html>\r\n <head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <title>Flight App</title>\r\n </head>\r\n <body>\r\n <div id=\"app\"></div>\r\n <script type=\"module\" src=\"/assets/client.js\"></script>\r\n </body>\r\n</html>`;\r\n\r\n return new Response(html, {\r\n headers: { 'Content-Type': 'text/html; charset=utf-8' },\r\n });\r\n}\r\n\r\nfunction createPlaceholderHTML(pathname: string): string {\r\n return `<!DOCTYPE html>\r\n<html>\r\n <head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <title>Flight</title>\r\n <style>\r\n body { font-family: system-ui, sans-serif; display: flex; align-items: center; justify-content: center; min-height: 100vh; margin: 0; background: #0a0a0a; color: #fafafa; }\r\n .container { text-align: center; }\r\n h1 { font-size: 3rem; margin-bottom: 0.5rem; }\r\n p { color: #888; }\r\n code { background: #1a1a1a; padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.875rem; }\r\n </style>\r\n </head>\r\n <body>\r\n <div class=\"container\">\r\n <h1>Flight</h1>\r\n <p>Path: <code>${pathname}</code></p>\r\n <p>Configure a UI framework adapter to enable SSR.</p>\r\n </div>\r\n </body>\r\n</html>`;\r\n}\r\n\r\n// Note: All exports are at the point of definition (export interface, export function)\r\n","/**\r\n * Flight Adapters - Optional Validation Helpers\r\n * \r\n * This module provides optional validation utilities for adapter options.\r\n * Zod is NOT required - these are only used if the developer chooses to\r\n * add runtime validation to their adapter.\r\n * \r\n * @example Without validation (default - no Zod needed)\r\n * ```typescript\r\n * import { createAdapter } from '@flightdev/core/adapters';\r\n * \r\n * export default function myAdapter(options = {}) {\r\n * return createAdapter({\r\n * name: 'my-adapter',\r\n * adapt: async (builder) => { ... }\r\n * });\r\n * }\r\n * ```\r\n * \r\n * @example With optional Zod validation (if developer wants it)\r\n * ```typescript\r\n * import { createValidatedAdapter } from '@flightdev/core/adapters';\r\n * import { z } from 'zod';\r\n * \r\n * const optionsSchema = z.object({\r\n * port: z.number().default(3000),\r\n * healthCheck: z.boolean().default(true),\r\n * });\r\n * \r\n * export default createValidatedAdapter(\r\n * 'my-adapter',\r\n * optionsSchema,\r\n * (options) => ({\r\n * adapt: async (builder) => { ... }\r\n * })\r\n * );\r\n * ```\r\n * \r\n * @module @flightdev/core/adapters/validation\r\n */\r\n\r\nimport type { FlightAdapter, AdapterBuilder } from './index.js';\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\n/**\r\n * Minimal Zod-like schema interface\r\n * This allows the validation helper to work with Zod or any compatible library\r\n * without requiring Zod as a direct dependency\r\n */\r\nexport interface ZodLikeSchema<T = unknown> {\r\n parse(data: unknown): T;\r\n safeParse(data: unknown): { success: true; data: T } | { success: false; error: unknown };\r\n}\r\n\r\n/**\r\n * Options for validated adapter factory\r\n */\r\nexport interface ValidatedAdapterOptions<T> {\r\n /** Parsed and validated options */\r\n adapt: (builder: AdapterBuilder) => Promise<void>;\r\n /** Optional emulate function */\r\n emulate?: FlightAdapter['emulate'];\r\n /** Optional supports declaration */\r\n supports?: FlightAdapter['supports'];\r\n}\r\n\r\n// ============================================================================\r\n// Validation Helpers\r\n// ============================================================================\r\n\r\n/**\r\n * Create a validated adapter factory\r\n * \r\n * This helper validates adapter options at runtime using a Zod-like schema.\r\n * The developer chooses whether to use this - it's completely optional.\r\n * \r\n * @param name - Adapter name\r\n * @param schema - Zod-like schema for options validation\r\n * @param factory - Factory function that receives validated options\r\n * @returns Adapter factory function\r\n * \r\n * @example\r\n * ```typescript\r\n * import { createValidatedAdapter } from '@flightdev/core/adapters';\r\n * import { z } from 'zod';\r\n * \r\n * const schema = z.object({\r\n * port: z.number().default(3000),\r\n * region: z.string().optional(),\r\n * });\r\n * \r\n * export default createValidatedAdapter('my-adapter', schema, (options) => ({\r\n * adapt: async (builder) => {\r\n * // options is fully typed: { port: number; region?: string }\r\n * console.log(`Using port ${options.port}`);\r\n * }\r\n * }));\r\n * ```\r\n */\r\nexport function createValidatedAdapter<TSchema extends ZodLikeSchema>(\r\n name: string,\r\n schema: TSchema,\r\n factory: (options: ReturnType<TSchema['parse']>) => ValidatedAdapterOptions<ReturnType<TSchema['parse']>>\r\n): (options?: unknown) => FlightAdapter {\r\n return (rawOptions: unknown = {}): FlightAdapter => {\r\n // Validate options using the provided schema\r\n const parseResult = schema.safeParse(rawOptions);\r\n\r\n if (!parseResult.success) {\r\n const errorMessage = formatValidationError(name, parseResult.error);\r\n throw new Error(errorMessage);\r\n }\r\n\r\n const validatedOptions = parseResult.data as ReturnType<TSchema['parse']>;\r\n const adapterConfig = factory(validatedOptions);\r\n\r\n return {\r\n name,\r\n adapt: adapterConfig.adapt,\r\n emulate: adapterConfig.emulate,\r\n supports: adapterConfig.supports,\r\n };\r\n };\r\n}\r\n\r\n/**\r\n * Validate adapter options without throwing\r\n * Returns null if validation fails, useful for optional validation\r\n * \r\n * @param schema - Zod-like schema\r\n * @param options - Options to validate\r\n * @returns Validated options or null\r\n */\r\nexport function validateAdapterOptions<TSchema extends ZodLikeSchema>(\r\n schema: TSchema,\r\n options: unknown\r\n): ReturnType<TSchema['parse']> | null {\r\n const result = schema.safeParse(options);\r\n return result.success ? (result.data as ReturnType<TSchema['parse']>) : null;\r\n}\r\n\r\n/**\r\n * Assert adapter options are valid, throwing detailed error if not\r\n * \r\n * @param name - Adapter name for error messages\r\n * @param schema - Zod-like schema\r\n * @param options - Options to validate\r\n * @returns Validated options\r\n * @throws Error with detailed message if validation fails\r\n */\r\nexport function assertValidOptions<TSchema extends ZodLikeSchema>(\r\n name: string,\r\n schema: TSchema,\r\n options: unknown\r\n): ReturnType<TSchema['parse']> {\r\n const result = schema.safeParse(options);\r\n\r\n if (!result.success) {\r\n throw new Error(formatValidationError(name, result.error));\r\n }\r\n\r\n return result.data as ReturnType<TSchema['parse']>;\r\n}\r\n\r\n// ============================================================================\r\n// Error Formatting\r\n// ============================================================================\r\n\r\n/**\r\n * Format a validation error into a readable message\r\n */\r\nfunction formatValidationError(adapterName: string, error: unknown): string {\r\n const prefix = `[${adapterName}] Invalid adapter options`;\r\n\r\n // Handle Zod errors\r\n if (error && typeof error === 'object' && 'issues' in error) {\r\n const zodError = error as { issues: Array<{ path: (string | number)[]; message: string }> };\r\n const issues = zodError.issues\r\n .map(issue => ` - ${issue.path.join('.')}: ${issue.message}`)\r\n .join('\\n');\r\n return `${prefix}:\\n${issues}`;\r\n }\r\n\r\n // Fallback for other error types\r\n if (error instanceof Error) {\r\n return `${prefix}: ${error.message}`;\r\n }\r\n\r\n return `${prefix}: Unknown validation error`;\r\n}\r\n\r\n// ============================================================================\r\n// Type Utilities\r\n// ============================================================================\r\n\r\n/**\r\n * Extract the inferred type from a Zod-like schema\r\n * Useful for typing adapter options in TypeScript\r\n */\r\nexport type InferSchema<T extends ZodLikeSchema> = ReturnType<T['parse']>;\r\n","/**\r\n * Flight Adapters - Universal deployment adapters\r\n * \r\n * Adapters transform Flight's output for different deployment targets.\r\n * The user chooses where to deploy - Flight provides the adapters.\r\n */\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\n/** Build manifest containing all generated assets */\r\nexport interface BuildManifest {\r\n /** Entry points by name */\r\n entries: Record<string, string>;\r\n /** All generated files */\r\n files: string[];\r\n /** Route information */\r\n routes: RouteManifestEntry[];\r\n /** Server entry point (if applicable) */\r\n serverEntry?: string;\r\n /** Client entry point */\r\n clientEntry?: string;\r\n}\r\n\r\n/** Route information in manifest */\r\nexport interface RouteManifestEntry {\r\n /** Route path pattern */\r\n path: string;\r\n /** Render mode for this route */\r\n mode: 'ssr' | 'ssg' | 'csr' | 'isr';\r\n /** Component/handler file path */\r\n component: string;\r\n /** Pre-rendered paths (for SSG/ISR) */\r\n prerendered?: string[];\r\n}\r\n\r\n/** Builder utilities passed to adapters */\r\nexport interface AdapterBuilder {\r\n /** Build manifest */\r\n manifest: BuildManifest;\r\n /** Project root directory */\r\n root: string;\r\n /** Build output directory */\r\n outDir: string;\r\n\r\n /** Read a file from the build output */\r\n readFile(path: string): Promise<string>;\r\n /** Write a file to the adapter output */\r\n writeFile(path: string, content: string): Promise<void>;\r\n /** Copy files from build to adapter output */\r\n copy(from: string, to: string): Promise<void>;\r\n /** Get all files matching a pattern */\r\n glob(pattern: string): Promise<string[]>;\r\n /** Compress files for production */\r\n compress?(files: string[]): Promise<void>;\r\n\r\n /** Log info message */\r\n log: {\r\n info(message: string): void;\r\n warn(message: string): void;\r\n error(message: string): void;\r\n };\r\n}\r\n\r\n/** Flight adapter interface */\r\nexport interface FlightAdapter {\r\n /** Adapter name */\r\n name: string;\r\n\r\n /** \r\n * Transform the build output for the target platform\r\n * This is called after Flight's build process completes\r\n */\r\n adapt(builder: AdapterBuilder): Promise<void>;\r\n\r\n /**\r\n * Optional: Start listening for requests (for Node.js/Bun adapters)\r\n */\r\n listen?(server: unknown, port: number): Promise<void>;\r\n\r\n /**\r\n * Optional: Emulate the platform during development\r\n * Useful for testing platform-specific behavior locally\r\n */\r\n emulate?(): {\r\n /** Platform-specific env vars */\r\n env?: Record<string, string>;\r\n /** Custom middleware for dev server */\r\n middleware?: unknown[];\r\n };\r\n\r\n /**\r\n * Optional: Declare what this adapter supports\r\n * Flight uses this to warn about incompatible features\r\n */\r\n supports?: {\r\n /** Can read files at runtime (for dynamic content) */\r\n read?: () => boolean;\r\n /** Supports streaming responses */\r\n streaming?: () => boolean;\r\n /** Supports WebSockets */\r\n websockets?: () => boolean;\r\n /** Supports edge runtime */\r\n edge?: () => boolean;\r\n /** Supports Node.js runtime */\r\n node?: () => boolean;\r\n };\r\n}\r\n\r\n\r\n// ============================================================================\r\n// Adapter Factory Helper\r\n// ============================================================================\r\n\r\nexport interface AdapterOptions {\r\n name: string;\r\n adapt: (builder: AdapterBuilder) => Promise<void>;\r\n emulate?: FlightAdapter['emulate'];\r\n supports?: FlightAdapter['supports'];\r\n}\r\n\r\n/**\r\n * Create a Flight adapter\r\n * \r\n * @example\r\n * ```typescript\r\n * import { createAdapter } from '@flightdev/core/adapters';\r\n * \r\n * export default function myAdapter(options = {}) {\r\n * return createAdapter({\r\n * name: 'my-adapter',\r\n * async adapt(builder) {\r\n * // Transform build output for your platform\r\n * },\r\n * });\r\n * }\r\n * ```\r\n */\r\nexport function createAdapter(options: AdapterOptions): FlightAdapter {\r\n return {\r\n name: options.name,\r\n adapt: options.adapt,\r\n emulate: options.emulate,\r\n supports: options.supports,\r\n };\r\n}\r\n\r\n// Re-export universal handler for adapters to use (optional)\r\nexport * from './handler.js';\r\n\r\n// Re-export optional validation helpers (Zod not required)\r\nexport * from './validation.js';\r\n\r\n// ============================================================================\r\n// Agnostic Service Adapters (Interfaces)\r\n// ============================================================================\r\n\r\n/**\r\n * Storage Adapter Interface\r\n * \r\n * Implement this to use any storage provider:\r\n * S3, Supabase Storage, Cloudflare R2, local filesystem, etc.\r\n */\r\nexport interface StorageAdapter {\r\n name: string;\r\n\r\n /** Upload a file */\r\n upload(path: string, data: Buffer | Uint8Array | string, options?: {\r\n contentType?: string;\r\n metadata?: Record<string, string>;\r\n }): Promise<{ url: string; path: string }>;\r\n\r\n /** Download a file */\r\n download(path: string): Promise<Buffer>;\r\n\r\n /** Delete a file */\r\n delete(path: string): Promise<void>;\r\n\r\n /** List files with optional prefix */\r\n list(prefix?: string): Promise<string[]>;\r\n\r\n /** Get a signed URL for direct access */\r\n getSignedUrl?(path: string, options?: {\r\n expiresIn?: number;\r\n operation?: 'read' | 'write';\r\n }): Promise<string>;\r\n}\r\n\r\n/**\r\n * Auth Adapter Interface\r\n * \r\n * Implement this to use any auth provider:\r\n * Better Auth, Supabase Auth, Lucia, Auth.js, etc.\r\n */\r\nexport interface AuthAdapter {\r\n name: string;\r\n\r\n /** Get the current user from a request */\r\n getUser(request: Request): Promise<AuthUser | null>;\r\n\r\n /** Verify a session token */\r\n verifySession(token: string): Promise<AuthSession | null>;\r\n\r\n /** Optional middleware for handling auth-specific routes (e.g., /api/auth/*) */\r\n middleware?: (req: Request) => Promise<Response | null>;\r\n}\r\n\r\nexport interface AuthUser {\r\n id: string;\r\n email?: string;\r\n name?: string;\r\n avatar?: string;\r\n metadata?: Record<string, unknown>;\r\n}\r\n\r\nexport interface AuthSession {\r\n user: AuthUser;\r\n expiresAt: Date;\r\n token: string;\r\n}\r\n\r\n/**\r\n * Email Adapter Interface\r\n * \r\n * Implement this to use any email provider:\r\n * Resend, SendGrid, Postmark, SMTP, etc.\r\n */\r\nexport interface EmailAdapter {\r\n name: string;\r\n\r\n /** Send an email */\r\n send(options: {\r\n to: string | string[];\r\n subject: string;\r\n html?: string;\r\n text?: string;\r\n from?: string;\r\n replyTo?: string;\r\n attachments?: Array<{\r\n filename: string;\r\n content: Buffer | string;\r\n contentType?: string;\r\n }>;\r\n }): Promise<{ id: string; success: boolean }>;\r\n}\r\n\r\n/**\r\n * Queue/Jobs Adapter Interface\r\n * \r\n * Implement this to use any job queue:\r\n * BullMQ, Quirrel, Inngest, custom, etc.\r\n */\r\nexport interface JobsAdapter {\r\n name: string;\r\n\r\n /** Add a job to the queue */\r\n enqueue<T>(jobName: string, data: T, options?: {\r\n delay?: number;\r\n priority?: number;\r\n retries?: number;\r\n }): Promise<{ id: string }>;\r\n\r\n /** Schedule a recurring job */\r\n schedule?(jobName: string, cron: string, data?: unknown): Promise<{ id: string }>;\r\n\r\n /** Cancel a job */\r\n cancel?(jobId: string): Promise<void>;\r\n}\r\n\r\n/**\r\n * Database Adapter Interface\r\n * \r\n * Note: This is intentionally minimal. \r\n * For databases, use your preferred ORM/query builder directly.\r\n * This interface is for Flight's internal use (sessions, cache, etc.)\r\n */\r\nexport interface DatabaseAdapter {\r\n name: string;\r\n\r\n /** Raw query execution */\r\n query<T = unknown>(sql: string, params?: unknown[]): Promise<T[]>;\r\n\r\n /** Execute a mutation */\r\n execute(sql: string, params?: unknown[]): Promise<{ rowsAffected: number }>;\r\n\r\n /** Transaction support */\r\n transaction?<T>(fn: (tx: DatabaseAdapter) => Promise<T>): Promise<T>;\r\n}\r\n"]}
|