@flight-framework/core 0.4.0 → 0.6.0
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/dist/adapters/index.d.ts +123 -1
- package/dist/adapters/index.js +1 -1
- package/dist/chunk-56ZZNOJD.js +93 -0
- package/dist/chunk-56ZZNOJD.js.map +1 -0
- package/dist/{chunk-K5NYYNFS.js → chunk-77MJZCYD.js} +46 -3
- package/dist/chunk-77MJZCYD.js.map +1 -0
- package/dist/chunk-RFTE6JVG.js +88 -0
- package/dist/chunk-RFTE6JVG.js.map +1 -0
- package/dist/chunk-WF46NESN.js +67 -0
- package/dist/chunk-WF46NESN.js.map +1 -0
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/env-9do2c6yn.d.ts +42 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/plugins/env-plugin.d.ts +65 -0
- package/dist/plugins/env-plugin.js +3 -0
- package/dist/plugins/env-plugin.js.map +1 -0
- package/dist/plugins/index.d.ts +155 -0
- package/dist/plugins/index.js +150 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/server-boundary-plugin.d.ts +67 -0
- package/dist/plugins/server-boundary-plugin.js +3 -0
- package/dist/plugins/server-boundary-plugin.js.map +1 -0
- package/dist/utils/index.d.ts +83 -28
- package/dist/utils/index.js +1 -1
- package/package.json +13 -3
- package/dist/chunk-K5NYYNFS.js.map +0 -1
- package/dist/chunk-PL37KFRJ.js +0 -3
- package/dist/chunk-PL37KFRJ.js.map +0 -1
package/dist/adapters/index.d.ts
CHANGED
|
@@ -134,6 +134,128 @@ interface UniversalHandler {
|
|
|
134
134
|
*/
|
|
135
135
|
declare function createUniversalHandler(options?: UniversalHandlerOptions): UniversalHandler;
|
|
136
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Flight Adapters - Optional Validation Helpers
|
|
139
|
+
*
|
|
140
|
+
* This module provides optional validation utilities for adapter options.
|
|
141
|
+
* Zod is NOT required - these are only used if the developer chooses to
|
|
142
|
+
* add runtime validation to their adapter.
|
|
143
|
+
*
|
|
144
|
+
* @example Without validation (default - no Zod needed)
|
|
145
|
+
* ```typescript
|
|
146
|
+
* import { createAdapter } from '@flight-framework/core/adapters';
|
|
147
|
+
*
|
|
148
|
+
* export default function myAdapter(options = {}) {
|
|
149
|
+
* return createAdapter({
|
|
150
|
+
* name: 'my-adapter',
|
|
151
|
+
* adapt: async (builder) => { ... }
|
|
152
|
+
* });
|
|
153
|
+
* }
|
|
154
|
+
* ```
|
|
155
|
+
*
|
|
156
|
+
* @example With optional Zod validation (if developer wants it)
|
|
157
|
+
* ```typescript
|
|
158
|
+
* import { createValidatedAdapter } from '@flight-framework/core/adapters';
|
|
159
|
+
* import { z } from 'zod';
|
|
160
|
+
*
|
|
161
|
+
* const optionsSchema = z.object({
|
|
162
|
+
* port: z.number().default(3000),
|
|
163
|
+
* healthCheck: z.boolean().default(true),
|
|
164
|
+
* });
|
|
165
|
+
*
|
|
166
|
+
* export default createValidatedAdapter(
|
|
167
|
+
* 'my-adapter',
|
|
168
|
+
* optionsSchema,
|
|
169
|
+
* (options) => ({
|
|
170
|
+
* adapt: async (builder) => { ... }
|
|
171
|
+
* })
|
|
172
|
+
* );
|
|
173
|
+
* ```
|
|
174
|
+
*
|
|
175
|
+
* @module @flight-framework/core/adapters/validation
|
|
176
|
+
*/
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Minimal Zod-like schema interface
|
|
180
|
+
* This allows the validation helper to work with Zod or any compatible library
|
|
181
|
+
* without requiring Zod as a direct dependency
|
|
182
|
+
*/
|
|
183
|
+
interface ZodLikeSchema<T = unknown> {
|
|
184
|
+
parse(data: unknown): T;
|
|
185
|
+
safeParse(data: unknown): {
|
|
186
|
+
success: true;
|
|
187
|
+
data: T;
|
|
188
|
+
} | {
|
|
189
|
+
success: false;
|
|
190
|
+
error: unknown;
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Options for validated adapter factory
|
|
195
|
+
*/
|
|
196
|
+
interface ValidatedAdapterOptions<T> {
|
|
197
|
+
/** Parsed and validated options */
|
|
198
|
+
adapt: (builder: AdapterBuilder) => Promise<void>;
|
|
199
|
+
/** Optional emulate function */
|
|
200
|
+
emulate?: FlightAdapter['emulate'];
|
|
201
|
+
/** Optional supports declaration */
|
|
202
|
+
supports?: FlightAdapter['supports'];
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Create a validated adapter factory
|
|
206
|
+
*
|
|
207
|
+
* This helper validates adapter options at runtime using a Zod-like schema.
|
|
208
|
+
* The developer chooses whether to use this - it's completely optional.
|
|
209
|
+
*
|
|
210
|
+
* @param name - Adapter name
|
|
211
|
+
* @param schema - Zod-like schema for options validation
|
|
212
|
+
* @param factory - Factory function that receives validated options
|
|
213
|
+
* @returns Adapter factory function
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* ```typescript
|
|
217
|
+
* import { createValidatedAdapter } from '@flight-framework/core/adapters';
|
|
218
|
+
* import { z } from 'zod';
|
|
219
|
+
*
|
|
220
|
+
* const schema = z.object({
|
|
221
|
+
* port: z.number().default(3000),
|
|
222
|
+
* region: z.string().optional(),
|
|
223
|
+
* });
|
|
224
|
+
*
|
|
225
|
+
* export default createValidatedAdapter('my-adapter', schema, (options) => ({
|
|
226
|
+
* adapt: async (builder) => {
|
|
227
|
+
* // options is fully typed: { port: number; region?: string }
|
|
228
|
+
* console.log(`Using port ${options.port}`);
|
|
229
|
+
* }
|
|
230
|
+
* }));
|
|
231
|
+
* ```
|
|
232
|
+
*/
|
|
233
|
+
declare function createValidatedAdapter<TSchema extends ZodLikeSchema>(name: string, schema: TSchema, factory: (options: ReturnType<TSchema['parse']>) => ValidatedAdapterOptions<ReturnType<TSchema['parse']>>): (options?: unknown) => FlightAdapter;
|
|
234
|
+
/**
|
|
235
|
+
* Validate adapter options without throwing
|
|
236
|
+
* Returns null if validation fails, useful for optional validation
|
|
237
|
+
*
|
|
238
|
+
* @param schema - Zod-like schema
|
|
239
|
+
* @param options - Options to validate
|
|
240
|
+
* @returns Validated options or null
|
|
241
|
+
*/
|
|
242
|
+
declare function validateAdapterOptions<TSchema extends ZodLikeSchema>(schema: TSchema, options: unknown): ReturnType<TSchema['parse']> | null;
|
|
243
|
+
/**
|
|
244
|
+
* Assert adapter options are valid, throwing detailed error if not
|
|
245
|
+
*
|
|
246
|
+
* @param name - Adapter name for error messages
|
|
247
|
+
* @param schema - Zod-like schema
|
|
248
|
+
* @param options - Options to validate
|
|
249
|
+
* @returns Validated options
|
|
250
|
+
* @throws Error with detailed message if validation fails
|
|
251
|
+
*/
|
|
252
|
+
declare function assertValidOptions<TSchema extends ZodLikeSchema>(name: string, schema: TSchema, options: unknown): ReturnType<TSchema['parse']>;
|
|
253
|
+
/**
|
|
254
|
+
* Extract the inferred type from a Zod-like schema
|
|
255
|
+
* Useful for typing adapter options in TypeScript
|
|
256
|
+
*/
|
|
257
|
+
type InferSchema<T extends ZodLikeSchema> = ReturnType<T['parse']>;
|
|
258
|
+
|
|
137
259
|
/**
|
|
138
260
|
* Flight Adapters - Universal deployment adapters
|
|
139
261
|
*
|
|
@@ -377,4 +499,4 @@ interface DatabaseAdapter {
|
|
|
377
499
|
transaction?<T>(fn: (tx: DatabaseAdapter) => Promise<T>): Promise<T>;
|
|
378
500
|
}
|
|
379
501
|
|
|
380
|
-
export { type AdapterBuilder, type AdapterOptions, type AuthAdapter, type AuthSession, type AuthUser, type BuildManifest, type CorsOptions, type DatabaseAdapter, type EmailAdapter, type FlightAdapter, type HandlerHooks, type JobsAdapter, type RouteManifest, type RouteManifestEntry, type RouteRuleConfig, type RouteRules, type StorageAdapter, type UniversalHandler, type UniversalHandlerOptions, createAdapter, createUniversalHandler };
|
|
502
|
+
export { type AdapterBuilder, type AdapterOptions, type AuthAdapter, type AuthSession, type AuthUser, type BuildManifest, type CorsOptions, type DatabaseAdapter, type EmailAdapter, type FlightAdapter, type HandlerHooks, type InferSchema, type JobsAdapter, type RouteManifest, type RouteManifestEntry, type RouteRuleConfig, type RouteRules, type StorageAdapter, type UniversalHandler, type UniversalHandlerOptions, type ValidatedAdapterOptions, type ZodLikeSchema, assertValidOptions, createAdapter, createUniversalHandler, createValidatedAdapter, validateAdapterOptions };
|
package/dist/adapters/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { createAdapter, createUniversalHandler } from '../chunk-
|
|
1
|
+
export { assertValidOptions, createAdapter, createUniversalHandler, createValidatedAdapter, validateAdapterOptions } from '../chunk-77MJZCYD.js';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// src/plugins/server-boundary-plugin.ts
|
|
2
|
+
function serverBoundaryPlugin(options = {}) {
|
|
3
|
+
const {
|
|
4
|
+
serverFilePatterns = [".server.ts", ".server.tsx", ".server.js", ".server.jsx"],
|
|
5
|
+
serverDirPatterns = ["/server/", "/api/"],
|
|
6
|
+
violationBehavior = "error",
|
|
7
|
+
customErrorMessage,
|
|
8
|
+
exclude = ["**/*.test.*", "**/*.spec.*", "**/node_modules/**"]
|
|
9
|
+
} = options;
|
|
10
|
+
const filePatterns = serverFilePatterns.map(
|
|
11
|
+
(p) => new RegExp(p.replace(/\./g, "\\.") + "$", "i")
|
|
12
|
+
);
|
|
13
|
+
const dirPatterns = serverDirPatterns.map(
|
|
14
|
+
(p) => new RegExp(p.replace(/\//g, "[\\\\/]"), "i")
|
|
15
|
+
);
|
|
16
|
+
function isServerOnlyModule(id) {
|
|
17
|
+
for (const pattern of filePatterns) {
|
|
18
|
+
if (pattern.test(id)) return true;
|
|
19
|
+
}
|
|
20
|
+
for (const pattern of dirPatterns) {
|
|
21
|
+
if (pattern.test(id)) return true;
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
function isExcluded(id) {
|
|
26
|
+
for (const pattern of exclude) {
|
|
27
|
+
const regex = new RegExp(
|
|
28
|
+
pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/\\\\]*").replace(/\./g, "\\.")
|
|
29
|
+
);
|
|
30
|
+
if (regex.test(id)) return true;
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
function createErrorMessage(module, importer) {
|
|
35
|
+
if (customErrorMessage) {
|
|
36
|
+
return customErrorMessage(module, importer);
|
|
37
|
+
}
|
|
38
|
+
return `[Flight] Cannot import server-only module in client code.
|
|
39
|
+
|
|
40
|
+
Module: ${module}
|
|
41
|
+
Imported by: ${importer}
|
|
42
|
+
|
|
43
|
+
Solutions:
|
|
44
|
+
1. Move the import to a 'use server' file
|
|
45
|
+
2. Use an API route instead
|
|
46
|
+
3. Use dynamic import with typeof window check
|
|
47
|
+
4. Rename the importing file to *.server.ts
|
|
48
|
+
|
|
49
|
+
Learn more: https://flight-framework.dev/docs/server-client-boundaries`;
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
name: "flight:server-boundary",
|
|
53
|
+
enforce: "pre",
|
|
54
|
+
resolveId(source, importer, options2) {
|
|
55
|
+
if (options2?.ssr) return null;
|
|
56
|
+
if (!importer) return null;
|
|
57
|
+
if (isExcluded(importer)) return null;
|
|
58
|
+
if (isServerOnlyModule(importer)) return null;
|
|
59
|
+
if (isServerOnlyModule(source)) {
|
|
60
|
+
const message = createErrorMessage(source, importer);
|
|
61
|
+
if (violationBehavior === "error") {
|
|
62
|
+
this.error(message);
|
|
63
|
+
} else {
|
|
64
|
+
this.warn(message);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
},
|
|
69
|
+
transform(code, id, options2) {
|
|
70
|
+
if (options2?.ssr) return null;
|
|
71
|
+
if (isExcluded(id)) return null;
|
|
72
|
+
if (isServerOnlyModule(id)) return null;
|
|
73
|
+
const trimmed = code.trim();
|
|
74
|
+
if (trimmed.startsWith("'use server'") || trimmed.startsWith('"use server"')) {
|
|
75
|
+
const message = `[Flight] 'use server' file imported in client bundle.
|
|
76
|
+
|
|
77
|
+
File: ${id}
|
|
78
|
+
|
|
79
|
+
This file should only run on the server. Check your import graph to find where this is imported.`;
|
|
80
|
+
if (violationBehavior === "error") {
|
|
81
|
+
this.error(message);
|
|
82
|
+
} else {
|
|
83
|
+
this.warn(message);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export { serverBoundaryPlugin };
|
|
92
|
+
//# sourceMappingURL=chunk-56ZZNOJD.js.map
|
|
93
|
+
//# sourceMappingURL=chunk-56ZZNOJD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/plugins/server-boundary-plugin.ts"],"names":["options"],"mappings":"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","file":"chunk-56ZZNOJD.js","sourcesContent":["/**\r\n * @flight-framework/core - Server Boundary Plugin\r\n * \r\n * Vite plugin to detect server-only code imported in client bundles.\r\n * OPTIONAL - use if you want build-time protection.\r\n * \r\n * Features:\r\n * - Detects *.server.ts files imported in client code\r\n * - Detects /server/ and /api/ directory imports\r\n * - Configurable behavior (warn, error)\r\n * - Clear error messages with solutions\r\n * \r\n * @example\r\n * ```typescript\r\n * // vite.config.ts\r\n * import { serverBoundaryPlugin } from '@flight-framework/core/plugins';\r\n * \r\n * export default {\r\n * plugins: [serverBoundaryPlugin()],\r\n * };\r\n * ```\r\n */\r\n\r\nimport type { Plugin } from 'vite';\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport interface ServerBoundaryPluginOptions {\r\n /**\r\n * File patterns that are server-only.\r\n * @default ['.server.ts', '.server.tsx', '.server.js', '.server.jsx']\r\n */\r\n serverFilePatterns?: string[];\r\n\r\n /**\r\n * Directory patterns that are server-only.\r\n * @default ['/server/', '/api/']\r\n */\r\n serverDirPatterns?: string[];\r\n\r\n /**\r\n * How to handle violations.\r\n * - 'error': Build error (recommended for production)\r\n * - 'warn': Warning only (for migration)\r\n * @default 'error'\r\n */\r\n violationBehavior?: 'error' | 'warn';\r\n\r\n /**\r\n * Custom error message template.\r\n */\r\n customErrorMessage?: (module: string, importer: string) => string;\r\n\r\n /**\r\n * Files to exclude from checking (e.g., tests).\r\n * @default ['**\\/*.test.*', '**\\/*.spec.*']\r\n */\r\n exclude?: string[];\r\n}\r\n\r\n// ============================================================================\r\n// Plugin\r\n// ============================================================================\r\n\r\n/**\r\n * Vite plugin to detect server-only imports in client code.\r\n * \r\n * This plugin is OPTIONAL. Use it if you want build-time\r\n * detection of server code leaking to the client.\r\n * \r\n * By default, it will:\r\n * - Block imports of *.server.ts files in client code\r\n * - Block imports from /server/ and /api/ directories\r\n * - Throw build errors with clear solutions\r\n */\r\nexport function serverBoundaryPlugin(\r\n options: ServerBoundaryPluginOptions = {}\r\n): Plugin {\r\n const {\r\n serverFilePatterns = ['.server.ts', '.server.tsx', '.server.js', '.server.jsx'],\r\n serverDirPatterns = ['/server/', '/api/'],\r\n violationBehavior = 'error',\r\n customErrorMessage,\r\n exclude = ['**/*.test.*', '**/*.spec.*', '**/node_modules/**'],\r\n } = options;\r\n\r\n // Build regex patterns\r\n const filePatterns = serverFilePatterns.map(p =>\r\n new RegExp(p.replace(/\\./g, '\\\\.') + '$', 'i')\r\n );\r\n const dirPatterns = serverDirPatterns.map(p =>\r\n new RegExp(p.replace(/\\//g, '[\\\\\\\\/]'), 'i')\r\n );\r\n\r\n function isServerOnlyModule(id: string): boolean {\r\n // Check file patterns\r\n for (const pattern of filePatterns) {\r\n if (pattern.test(id)) return true;\r\n }\r\n\r\n // Check directory patterns\r\n for (const pattern of dirPatterns) {\r\n if (pattern.test(id)) return true;\r\n }\r\n\r\n return false;\r\n }\r\n\r\n function isExcluded(id: string): boolean {\r\n for (const pattern of exclude) {\r\n // Simple glob matching\r\n const regex = new RegExp(\r\n pattern\r\n .replace(/\\*\\*/g, '.*')\r\n .replace(/\\*/g, '[^/\\\\\\\\]*')\r\n .replace(/\\./g, '\\\\.')\r\n );\r\n if (regex.test(id)) return true;\r\n }\r\n return false;\r\n }\r\n\r\n function createErrorMessage(module: string, importer: string): string {\r\n if (customErrorMessage) {\r\n return customErrorMessage(module, importer);\r\n }\r\n\r\n return (\r\n `[Flight] Cannot import server-only module in client code.\\n\\n` +\r\n ` Module: ${module}\\n` +\r\n ` Imported by: ${importer}\\n\\n` +\r\n `Solutions:\\n` +\r\n ` 1. Move the import to a 'use server' file\\n` +\r\n ` 2. Use an API route instead\\n` +\r\n ` 3. Use dynamic import with typeof window check\\n` +\r\n ` 4. Rename the importing file to *.server.ts\\n\\n` +\r\n `Learn more: https://flight-framework.dev/docs/server-client-boundaries`\r\n );\r\n }\r\n\r\n return {\r\n name: 'flight:server-boundary',\r\n enforce: 'pre',\r\n\r\n resolveId(source, importer, options) {\r\n // Skip SSR builds - server can import anything\r\n if (options?.ssr) return null;\r\n\r\n // Need an importer to check\r\n if (!importer) return null;\r\n\r\n // Skip excluded files\r\n if (isExcluded(importer)) return null;\r\n\r\n // Skip if importer is also server-only\r\n if (isServerOnlyModule(importer)) return null;\r\n\r\n // Check if the import target is server-only\r\n // This is a simplified check - the resolved ID would be more accurate\r\n if (isServerOnlyModule(source)) {\r\n const message = createErrorMessage(source, importer);\r\n\r\n if (violationBehavior === 'error') {\r\n this.error(message);\r\n } else {\r\n this.warn(message);\r\n }\r\n }\r\n\r\n return null;\r\n },\r\n\r\n transform(code, id, options) {\r\n // Skip SSR builds\r\n if (options?.ssr) return null;\r\n\r\n // Skip excluded files\r\n if (isExcluded(id)) return null;\r\n\r\n // Skip server-only files (they shouldn't be in client bundle anyway)\r\n if (isServerOnlyModule(id)) return null;\r\n\r\n // Check for 'use server' directive at file level\r\n const trimmed = code.trim();\r\n if (trimmed.startsWith(\"'use server'\") || trimmed.startsWith('\"use server\"')) {\r\n const message =\r\n `[Flight] 'use server' file imported in client bundle.\\n\\n` +\r\n ` File: ${id}\\n\\n` +\r\n `This file should only run on the server. ` +\r\n `Check your import graph to find where this is imported.`;\r\n\r\n if (violationBehavior === 'error') {\r\n this.error(message);\r\n } else {\r\n this.warn(message);\r\n }\r\n }\r\n\r\n return null;\r\n },\r\n };\r\n}\r\n"]}
|
|
@@ -296,6 +296,49 @@ function createPlaceholderHTML(pathname) {
|
|
|
296
296
|
</html>`;
|
|
297
297
|
}
|
|
298
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
|
+
|
|
299
342
|
// src/adapters/index.ts
|
|
300
343
|
function createAdapter(options) {
|
|
301
344
|
return {
|
|
@@ -306,6 +349,6 @@ function createAdapter(options) {
|
|
|
306
349
|
};
|
|
307
350
|
}
|
|
308
351
|
|
|
309
|
-
export { createAdapter, createUniversalHandler };
|
|
310
|
-
//# sourceMappingURL=chunk-
|
|
311
|
-
//# sourceMappingURL=chunk-
|
|
352
|
+
export { assertValidOptions, createAdapter, createUniversalHandler, createValidatedAdapter, validateAdapterOptions };
|
|
353
|
+
//# sourceMappingURL=chunk-77MJZCYD.js.map
|
|
354
|
+
//# sourceMappingURL=chunk-77MJZCYD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/handler.ts","../src/adapters/validation.ts","../src/adapters/index.ts"],"names":[],"mappings":";AAuKO,SAAS,sBAAA,CAAuB,OAAA,GAAmC,EAAC,EAAqB;AAC5F,EAAA,MAAM;AAAA,IACF,QAAA;AAAA,IACA,aAAa,EAAC;AAAA,IACd,QAAA,GAAW,EAAA;AAAA,IACX,OAAA,GAAU,mBAAA;AAAA,IACV,UAAA,GAAa,sBAAA;AAAA,IACb,QAAQ;AAAC,GACb,GAAI,OAAA;AAEJ,EAAA,OAAO;AAAA;AAAA;AAAA;AAAA,IAIH,MAAM,KAAA,CAAM,OAAA,EAAkB,GAAA,EAAe,GAAA,EAAkC;AAC3E,MAAA,IAAI;AAEA,QAAA,IAAI,MAAM,YAAA,EAAc;AACpB,UAAA,MAAM,eAAA,GAAkB,MAAM,KAAA,CAAM,YAAA,CAAa,OAAO,CAAA;AACxD,UAAA,IAAI,iBAAiB,OAAA,GAAU,eAAA;AAAA,QACnC;AAEA,QAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAC/B,QAAA,IAAI,WAAW,GAAA,CAAI,QAAA;AAGnB,QAAA,IAAI,QAAA,IAAY,QAAA,CAAS,UAAA,CAAW,QAAQ,CAAA,EAAG;AAC3C,UAAA,QAAA,GAAW,QAAA,CAAS,KAAA,CAAM,QAAA,CAAS,MAAM,CAAA,IAAK,GAAA;AAAA,QAClD;AAGA,QAAA,MAAM,KAAA,GAAQ,eAAA,CAAgB,QAAA,EAAU,UAAU,CAAA;AAGlD,QAAA,IAAI,OAAO,QAAA,EAAU;AACjB,UAAA,OAAO,cAAA,CAAe,MAAM,QAAQ,CAAA;AAAA,QACxC;AAGA,QAAA,IAAI,OAAO,KAAA,EAAO;AACd,UAAA,OAAO,WAAA,CAAY,OAAA,EAAS,KAAA,CAAM,KAAK,CAAA;AAAA,QAC3C;AAGA,QAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,SAAA,IAAa,KAAA,EAAO,IAAA,EAAM;AAC7C,UAAA,OAAO,mBAAA,CAAoB,MAAM,IAAI,CAAA;AAAA,QACzC;AAGA,QAAA,MAAM,KAAA,GAAQ,UAAU,MAAA,EAAQ,IAAA,CAAK,OAAK,SAAA,CAAU,QAAA,EAAU,CAAA,CAAE,IAAI,CAAC,CAAA;AAGrE,QAAA,IAAI,CAAC,KAAA,EAAO;AACR,UAAA,OAAO,MAAM,WAAW,OAAO,CAAA;AAAA,QACnC;AAGA,QAAA,MAAM,OAAA,GAAyB;AAAA,UAC3B,GAAA;AAAA,UACA,MAAA,EAAQ,aAAA,CAAc,QAAA,EAAU,KAAA,CAAM,IAAI,CAAA;AAAA,UAC1C,SAAS,OAAA,CAAQ,OAAA;AAAA,UACjB,QAAQ,OAAA,CAAQ;AAAA,SACpB;AAGA,QAAA,IAAI,MAAM,YAAA,EAAc;AACpB,UAAA,MAAM,KAAA,CAAM,aAAa,OAAO,CAAA;AAAA,QACpC;AAGA,QAAA,IAAI,QAAA;AAEJ,QAAA,QAAQ,MAAM,IAAA;AAAM,UAChB,KAAK,KAAA;AAAA,UACL,KAAK,KAAA;AACD,YAAA,QAAA,GAAW,MAAM,SAAA,CAAU,OAAA,EAAS,OAAO,CAAA;AAC3C,YAAA;AAAA,UACJ,KAAK,KAAA;AACD,YAAA,QAAA,GAAW,MAAM,SAAA,CAAU,OAAA,EAAS,OAAO,CAAA;AAC3C,YAAA;AAAA,UACJ,KAAK,KAAA;AACD,YAAA,QAAA,GAAW,UAAU,OAAO,CAAA;AAC5B,YAAA;AAAA,UACJ;AACI,YAAA,QAAA,GAAW,MAAM,SAAA,CAAU,OAAA,EAAS,OAAO,CAAA;AAAA;AAInD,QAAA,IAAI,OAAO,KAAA,EAAO;AACd,UAAA,QAAA,GAAW,iBAAA,CAAkB,QAAA,EAAU,KAAA,CAAM,KAAK,CAAA;AAAA,QACtD;AAGA,QAAA,IAAI,OAAO,IAAA,EAAM;AACb,UAAA,QAAA,GAAW,gBAAA,CAAiB,QAAA,EAAU,KAAA,CAAM,IAAA,EAAM,OAAO,CAAA;AAAA,QAC7D;AAGA,QAAA,IAAI,OAAO,OAAA,EAAS;AAChB,UAAA,QAAA,GAAW,kBAAA,CAAmB,QAAA,EAAU,KAAA,CAAM,OAAO,CAAA;AAAA,QACzD;AAGA,QAAA,IAAI,MAAM,WAAA,EAAa;AACnB,UAAA,MAAM,gBAAA,GAAmB,MAAM,KAAA,CAAM,WAAA,CAAY,SAAS,QAAQ,CAAA;AAClE,UAAA,IAAI,kBAAkB,QAAA,GAAW,gBAAA;AAAA,QACrC;AAEA,QAAA,OAAO,QAAA;AAAA,MAEX,SAAS,KAAA,EAAO;AACZ,QAAA,OAAO,OAAA,CAAQ,OAAgB,OAAO,CAAA;AAAA,MAC1C;AAAA,IACJ,CAAA;AAAA;AAAA;AAAA;AAAA,IAKA,UAAU,OAAA,EAA2B;AACjC,MAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAC/B,MAAA,IAAI,WAAW,GAAA,CAAI,QAAA;AAEnB,MAAA,IAAI,QAAA,IAAY,QAAA,CAAS,UAAA,CAAW,QAAQ,CAAA,EAAG;AAC3C,QAAA,QAAA,GAAW,QAAA,CAAS,KAAA,CAAM,QAAA,CAAS,MAAM,CAAA,IAAK,GAAA;AAAA,MAClD;AAGA,MAAA,IAAI,UAAU,MAAA,EAAQ;AAClB,QAAA,OAAO,QAAA,CAAS,OAAO,IAAA,CAAK,CAAA,CAAA,KAAK,UAAU,QAAA,EAAU,CAAA,CAAE,IAAI,CAAC,CAAA;AAAA,MAChE;AAGA,MAAA,OAAO,MAAA,CAAO,IAAA,CAAK,UAAU,CAAA,CAAE,IAAA;AAAA,QAAK,CAAA,OAAA,KAChC,SAAA,CAAU,QAAA,EAAU,OAAO;AAAA,OAC/B;AAAA,IACJ;AAAA,GACJ;AACJ;AAMA,SAAS,mBAAA,CAAoB,OAAc,QAAA,EAA6B;AACpE,EAAA,OAAA,CAAQ,KAAA,CAAM,2BAA2B,KAAK,CAAA;AAE9C,EAAA,OAAO,IAAI,QAAA;AAAA,IACP,KAAK,SAAA,CAAU;AAAA,MACX,KAAA,EAAO,uBAAA;AAAA,MACP,SAAS,OAAA,CAAQ,GAAA,CAAI,QAAA,KAAa,aAAA,GAAgB,MAAM,OAAA,GAAU;AAAA,KACrE,CAAA;AAAA,IACD;AAAA,MACI,MAAA,EAAQ,GAAA;AAAA,MACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA;AAAmB;AAClD,GACJ;AACJ;AAEA,SAAS,uBAAuB,QAAA,EAA6B;AACzD,EAAA,OAAO,IAAI,QAAA,CAAS,WAAA,EAAa,EAAE,MAAA,EAAQ,KAAK,CAAA;AACpD;AAEA,SAAS,eAAA,CAAgB,UAAkB,KAAA,EAA2C;AAClF,EAAA,KAAA,MAAW,CAAC,OAAA,EAAS,MAAM,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,EAAG;AACnD,IAAA,IAAI,SAAA,CAAU,QAAA,EAAU,OAAO,CAAA,EAAG;AAC9B,MAAA,OAAO,MAAA;AAAA,IACX;AAAA,EACJ;AACA,EAAA,OAAO,IAAA;AACX;AAEA,SAAS,SAAA,CAAU,UAAkB,OAAA,EAA0B;AAE3D,EAAA,IAAI,OAAA,KAAY,UAAU,OAAO,IAAA;AAGjC,EAAA,IAAI,OAAA,CAAQ,QAAA,CAAS,KAAK,CAAA,EAAG;AACzB,IAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA;AAClC,IAAA,OAAO,QAAA,CAAS,WAAW,MAAM,CAAA;AAAA,EACrC;AAEA,EAAA,IAAI,OAAA,CAAQ,QAAA,CAAS,IAAI,CAAA,EAAG;AACxB,IAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA;AAClC,IAAA,MAAM,IAAA,GAAO,QAAA,CAAS,KAAA,CAAM,MAAA,CAAO,MAAM,CAAA;AACzC,IAAA,OAAO,SAAS,UAAA,CAAW,MAAM,KAAK,CAAC,IAAA,CAAK,SAAS,GAAG,CAAA;AAAA,EAC5D;AAGA,EAAA,MAAM,YAAA,GAAe,OAAA,CAAQ,KAAA,CAAM,GAAG,CAAA;AACtC,EAAA,MAAM,SAAA,GAAY,QAAA,CAAS,KAAA,CAAM,GAAG,CAAA;AAEpC,EAAA,IAAI,YAAA,CAAa,MAAA,KAAW,SAAA,CAAU,MAAA,EAAQ,OAAO,KAAA;AAErD,EAAA,OAAO,YAAA,CAAa,KAAA,CAAM,CAAC,IAAA,EAAM,CAAA,KAAM;AACnC,IAAA,IAAI,IAAA,CAAK,WAAW,GAAG,CAAA,IAAK,KAAK,QAAA,CAAS,GAAG,GAAG,OAAO,IAAA;AACvD,IAAA,IAAI,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,EAAG,OAAO,IAAA;AACjC,IAAA,OAAO,IAAA,KAAS,UAAU,CAAC,CAAA;AAAA,EAC/B,CAAC,CAAA;AACL;AAEA,SAAS,aAAA,CAAc,UAAkB,OAAA,EAAyC;AAC9E,EAAA,MAAM,SAAiC,EAAC;AACxC,EAAA,MAAM,YAAA,GAAe,OAAA,CAAQ,KAAA,CAAM,GAAG,CAAA;AACtC,EAAA,MAAM,SAAA,GAAY,QAAA,CAAS,KAAA,CAAM,GAAG,CAAA;AAEpC,EAAA,YAAA,CAAa,OAAA,CAAQ,CAAC,IAAA,EAAM,CAAA,KAAM;AAC9B,IAAA,IAAI,KAAK,UAAA,CAAW,GAAG,KAAK,IAAA,CAAK,QAAA,CAAS,GAAG,CAAA,EAAG;AAC5C,MAAA,MAAM,GAAA,GAAM,KAAK,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,CAAE,OAAA,CAAQ,OAAO,EAAE,CAAA;AAC/C,MAAA,MAAA,CAAO,GAAG,CAAA,GAAI,SAAA,CAAU,CAAC,CAAA,IAAK,EAAA;AAAA,IAClC,CAAA,MAAA,IAAW,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,EAAG;AAC7B,MAAA,MAAA,CAAO,KAAK,KAAA,CAAM,CAAC,CAAC,CAAA,GAAI,SAAA,CAAU,CAAC,CAAA,IAAK,EAAA;AAAA,IAC5C;AAAA,EACJ,CAAC,CAAA;AAED,EAAA,OAAO,MAAA;AACX;AAEA,SAAS,eAAe,QAAA,EAAkE;AACtF,EAAA,MAAM,EAAA,GAAK,OAAO,QAAA,KAAa,QAAA,GAAW,WAAW,QAAA,CAAS,EAAA;AAC9D,EAAA,MAAM,SAAS,OAAO,QAAA,KAAa,QAAA,GAAW,GAAA,GAAO,SAAS,UAAA,IAAc,GAAA;AAE5E,EAAA,OAAO,IAAI,SAAS,IAAA,EAAM;AAAA,IACtB,MAAA;AAAA,IACA,OAAA,EAAS,EAAE,QAAA,EAAU,EAAA;AAAG,GAC3B,CAAA;AACL;AAEA,eAAe,WAAA,CAAY,SAAkB,MAAA,EAAmC;AAC5E,EAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA;AAC/B,EAAA,MAAM,SAAA,GAAY,IAAI,GAAA,CAAI,MAAM,CAAA;AAChC,EAAA,SAAA,CAAU,WAAW,GAAA,CAAI,QAAA;AACzB,EAAA,SAAA,CAAU,SAAS,GAAA,CAAI,MAAA;AAEvB,EAAA,MAAM,YAAA,GAAe,IAAI,OAAA,CAAQ,SAAA,CAAU,UAAS,EAAG;AAAA,IACnD,QAAQ,OAAA,CAAQ,MAAA;AAAA,IAChB,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,MAAM,OAAA,CAAQ;AAAA,GACjB,CAAA;AAED,EAAA,OAAO,MAAM,YAAY,CAAA;AAC7B;AAEA,SAAS,oBAAoB,IAAA,EAAuC;AAEhE,EAAA,MAAM,UAAuB,IAAA,KAAS,IAAA,GAAO,EAAC,GAAK,QAAQ,EAAC;AAE5D,EAAA,OAAO,IAAI,SAAS,IAAA,EAAM;AAAA,IACtB,MAAA,EAAQ,GAAA;AAAA,IACR,OAAA,EAAS;AAAA,MACL,6BAAA,EAA+B,aAAA,CAAc,OAAA,CAAQ,MAAM,CAAA;AAAA,MAC3D,8BAAA,EAAA,CAAiC,OAAA,CAAQ,OAAA,IAAW,CAAC,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAO,QAAA,EAAU,OAAO,CAAA,EAAG,IAAA,CAAK,IAAI,CAAA;AAAA,MACxG,8BAAA,EAAA,CAAiC,QAAQ,cAAA,IAAkB,CAAC,gBAAgB,eAAe,CAAA,EAAG,KAAK,IAAI,CAAA;AAAA,MACvG,wBAAA,EAA0B,MAAA,CAAO,OAAA,CAAQ,MAAA,IAAU,KAAK,CAAA;AAAA,MACxD,GAAI,OAAA,CAAQ,WAAA,IAAe,EAAE,oCAAoC,MAAA;AAAO;AAC5E,GACH,CAAA;AACL;AAEA,SAAS,cAAc,MAAA,EAA8C;AACjE,EAAA,IAAI,MAAA,KAAW,IAAA,IAAQ,MAAA,KAAW,MAAA,EAAW,OAAO,GAAA;AACpD,EAAA,IAAI,MAAA,KAAW,OAAO,OAAO,GAAA;AAC7B,EAAA,IAAI,MAAM,OAAA,CAAQ,MAAM,GAAG,OAAO,MAAA,CAAO,CAAC,CAAA,IAAK,GAAA;AAC/C,EAAA,OAAO,MAAA;AACX;AAEA,SAAS,iBAAA,CAAkB,UAAoB,KAAA,EAAoD;AAC/F,EAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAQ,QAAA,CAAS,OAAO,CAAA;AAE5C,EAAA,IAAI,YAAA,GAAe,CAAA,gBAAA,EAAmB,KAAA,CAAM,MAAA,IAAU,CAAC,CAAA,CAAA;AACvD,EAAA,IAAI,MAAM,GAAA,EAAK;AACX,IAAA,YAAA,IAAgB,CAAA,yBAAA,EAA4B,MAAM,GAAG,CAAA,CAAA;AAAA,EACzD;AAEA,EAAA,OAAA,CAAQ,GAAA,CAAI,iBAAiB,YAAY,CAAA;AAEzC,EAAA,OAAO,IAAI,QAAA,CAAS,QAAA,CAAS,IAAA,EAAM;AAAA,IAC/B,QAAQ,QAAA,CAAS,MAAA;AAAA,IACjB,YAAY,QAAA,CAAS,UAAA;AAAA,IACrB;AAAA,GACH,CAAA;AACL;AAEA,SAAS,gBAAA,CAAiB,QAAA,EAAoB,IAAA,EAA6B,QAAA,EAA6B;AAEpG,EAAA,MAAM,UAAuB,IAAA,KAAS,IAAA,GAAO,EAAC,GAAK,QAAQ,EAAC;AAC5D,EAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAQ,QAAA,CAAS,OAAO,CAAA;AAE5C,EAAA,OAAA,CAAQ,GAAA,CAAI,6BAAA,EAA+B,aAAA,CAAc,OAAA,CAAQ,MAAM,CAAC,CAAA;AAExE,EAAA,IAAI,QAAQ,WAAA,EAAa;AACrB,IAAA,OAAA,CAAQ,GAAA,CAAI,oCAAoC,MAAM,CAAA;AAAA,EAC1D;AAEA,EAAA,IAAI,QAAQ,cAAA,EAAgB;AACxB,IAAA,OAAA,CAAQ,IAAI,+BAAA,EAAiC,OAAA,CAAQ,cAAA,CAAe,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,EAClF;AAEA,EAAA,OAAO,IAAI,QAAA,CAAS,QAAA,CAAS,IAAA,EAAM;AAAA,IAC/B,QAAQ,QAAA,CAAS,MAAA;AAAA,IACjB,YAAY,QAAA,CAAS,UAAA;AAAA,IACrB;AAAA,GACH,CAAA;AACL;AAEA,SAAS,kBAAA,CAAmB,UAAoB,aAAA,EAAiD;AAC7F,EAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAQ,QAAA,CAAS,OAAO,CAAA;AAE5C,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,aAAa,CAAA,EAAG;AACtD,IAAA,OAAA,CAAQ,GAAA,CAAI,KAAK,KAAK,CAAA;AAAA,EAC1B;AAEA,EAAA,OAAO,IAAI,QAAA,CAAS,QAAA,CAAS,IAAA,EAAM;AAAA,IAC/B,QAAQ,QAAA,CAAS,MAAA;AAAA,IACjB,YAAY,QAAA,CAAS,UAAA;AAAA,IACrB;AAAA,GACH,CAAA;AACL;AAEA,eAAe,SAAA,CAAU,SAAwB,OAAA,EAAqD;AAClG,EAAA,MAAM,EAAE,SAAA,EAAW,KAAA,EAAM,GAAI,OAAA;AAG7B,EAAA,IAAI,CAAC,SAAA,EAAW;AACZ,IAAA,OAAO,IAAI,QAAA,CAAS,qBAAA,CAAsB,OAAA,CAAQ,GAAA,CAAI,QAAQ,CAAA,EAAG;AAAA,MAC7D,OAAA,EAAS,EAAE,cAAA,EAAgB,0BAAA;AAA2B,KACzD,CAAA;AAAA,EACL;AAGA,EAAA,IAAI,SAAS,MAAM,SAAA,CAAU,cAAA,CAAe,IAAa,OAAO,CAAA;AAGhE,EAAA,IAAI,OAAO,WAAA,EAAa;AACpB,IAAA,MAAM,cAAA,GAAiB,MAAM,KAAA,CAAM,WAAA,CAAY,MAAM,CAAA;AACrD,IAAA,IAAI,gBAAgB,MAAA,GAAS,cAAA;AAAA,EACjC;AAEA,EAAA,OAAO,IAAI,QAAA,CAAS,MAAA,CAAO,IAAA,EAAM;AAAA,IAC7B,QAAQ,MAAA,CAAO,MAAA;AAAA,IACf,SAAS,MAAA,CAAO;AAAA,GACnB,CAAA;AACL;AAEA,eAAe,SAAA,CAAU,SAAwB,OAAA,EAAqD;AAGlG,EAAA,OAAO,SAAA,CAAU,SAAS,OAAO,CAAA;AACrC;AAEA,SAAS,UAAU,OAAA,EAA4C;AAC3D,EAAA,MAAM,IAAA,GAAO,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAAA,CAAA;AAab,EAAA,OAAO,IAAI,SAAS,IAAA,EAAM;AAAA,IACtB,OAAA,EAAS,EAAE,cAAA,EAAgB,0BAAA;AAA2B,GACzD,CAAA;AACL;AAEA,SAAS,sBAAsB,QAAA,EAA0B;AACrD,EAAA,OAAO,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAA,EAiBY,QAAQ,CAAA;AAAA;AAAA;AAAA;AAAA,OAAA,CAAA;AAK/B;;;ACxcO,SAAS,sBAAA,CACZ,IAAA,EACA,MAAA,EACA,OAAA,EACoC;AACpC,EAAA,OAAO,CAAC,UAAA,GAAsB,EAAC,KAAqB;AAEhD,IAAA,MAAM,WAAA,GAAc,MAAA,CAAO,SAAA,CAAU,UAAU,CAAA;AAE/C,IAAA,IAAI,CAAC,YAAY,OAAA,EAAS;AACtB,MAAA,MAAM,YAAA,GAAe,qBAAA,CAAsB,IAAA,EAAM,WAAA,CAAY,KAAK,CAAA;AAClE,MAAA,MAAM,IAAI,MAAM,YAAY,CAAA;AAAA,IAChC;AAEA,IAAA,MAAM,mBAAmB,WAAA,CAAY,IAAA;AACrC,IAAA,MAAM,aAAA,GAAgB,QAAQ,gBAAgB,CAAA;AAE9C,IAAA,OAAO;AAAA,MACH,IAAA;AAAA,MACA,OAAO,aAAA,CAAc,KAAA;AAAA,MACrB,SAAS,aAAA,CAAc,OAAA;AAAA,MACvB,UAAU,aAAA,CAAc;AAAA,KAC5B;AAAA,EACJ,CAAA;AACJ;AAUO,SAAS,sBAAA,CACZ,QACA,OAAA,EACmC;AACnC,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,SAAA,CAAU,OAAO,CAAA;AACvC,EAAA,OAAO,MAAA,CAAO,OAAA,GAAW,MAAA,CAAO,IAAA,GAAwC,IAAA;AAC5E;AAWO,SAAS,kBAAA,CACZ,IAAA,EACA,MAAA,EACA,OAAA,EAC4B;AAC5B,EAAA,MAAM,MAAA,GAAS,MAAA,CAAO,SAAA,CAAU,OAAO,CAAA;AAEvC,EAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACjB,IAAA,MAAM,IAAI,KAAA,CAAM,qBAAA,CAAsB,IAAA,EAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,EAC7D;AAEA,EAAA,OAAO,MAAA,CAAO,IAAA;AAClB;AASA,SAAS,qBAAA,CAAsB,aAAqB,KAAA,EAAwB;AACxE,EAAA,MAAM,MAAA,GAAS,IAAI,WAAW,CAAA,yBAAA,CAAA;AAG9B,EAAA,IAAI,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,IAAY,YAAY,KAAA,EAAO;AACzD,IAAA,MAAM,QAAA,GAAW,KAAA;AACjB,IAAA,MAAM,SAAS,QAAA,CAAS,MAAA,CACnB,GAAA,CAAI,CAAA,KAAA,KAAS,OAAO,KAAA,CAAM,IAAA,CAAK,IAAA,CAAK,GAAG,CAAC,CAAA,EAAA,EAAK,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA,CAC5D,KAAK,IAAI,CAAA;AACd,IAAA,OAAO,GAAG,MAAM,CAAA;AAAA,EAAM,MAAM,CAAA,CAAA;AAAA,EAChC;AAGA,EAAA,IAAI,iBAAiB,KAAA,EAAO;AACxB,IAAA,OAAO,CAAA,EAAG,MAAM,CAAA,EAAA,EAAK,KAAA,CAAM,OAAO,CAAA,CAAA;AAAA,EACtC;AAEA,EAAA,OAAO,GAAG,MAAM,CAAA,0BAAA,CAAA;AACpB;;;ACrDO,SAAS,cAAc,OAAA,EAAwC;AAClE,EAAA,OAAO;AAAA,IACH,MAAM,OAAA,CAAQ,IAAA;AAAA,IACd,OAAO,OAAA,CAAQ,KAAA;AAAA,IACf,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,UAAU,OAAA,CAAQ;AAAA,GACtB;AACJ","file":"chunk-77MJZCYD.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 @flight-framework/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 '@flight-framework/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 '@flight-framework/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 '@flight-framework/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 @flight-framework/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 '@flight-framework/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 '@flight-framework/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"]}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// src/plugins/env-plugin.ts
|
|
2
|
+
function flightEnvPlugin(options = {}) {
|
|
3
|
+
const {
|
|
4
|
+
publicPrefix = "FLIGHT_PUBLIC_",
|
|
5
|
+
additionalPublicPrefixes = ["VITE_"],
|
|
6
|
+
privateVarBehavior = "warn",
|
|
7
|
+
allowList = ["NODE_ENV", "MODE"]
|
|
8
|
+
} = options;
|
|
9
|
+
const allPublicPrefixes = [publicPrefix, ...additionalPublicPrefixes];
|
|
10
|
+
return {
|
|
11
|
+
name: "flight:env",
|
|
12
|
+
enforce: "pre",
|
|
13
|
+
configResolved(resolvedConfig) {
|
|
14
|
+
},
|
|
15
|
+
config(_, { isSsrBuild }) {
|
|
16
|
+
if (isSsrBuild) return {};
|
|
17
|
+
const publicEnvVars = {};
|
|
18
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
19
|
+
if (value !== void 0 && isPublicKey(key, allPublicPrefixes, allowList)) {
|
|
20
|
+
publicEnvVars[`process.env.${key}`] = JSON.stringify(value);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
define: publicEnvVars
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
transform(code, id, options2) {
|
|
28
|
+
if (options2?.ssr) return null;
|
|
29
|
+
if (id.includes("node_modules")) return null;
|
|
30
|
+
if (!code.includes("process.env.")) return null;
|
|
31
|
+
const envVarPattern = /process\.env\.([A-Z_][A-Z0-9_]*)/g;
|
|
32
|
+
let hasModifications = false;
|
|
33
|
+
let modifiedCode = code;
|
|
34
|
+
let match;
|
|
35
|
+
const issues = [];
|
|
36
|
+
while ((match = envVarPattern.exec(code)) !== null) {
|
|
37
|
+
const varName = match[1];
|
|
38
|
+
if (isPublicKey(varName, allPublicPrefixes, allowList)) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
hasModifications = true;
|
|
42
|
+
if (privateVarBehavior === "error") {
|
|
43
|
+
issues.push(
|
|
44
|
+
`Private env var "${varName}" accessed in client code.
|
|
45
|
+
File: ${id}
|
|
46
|
+
Use FLIGHT_PUBLIC_${varName} if this should be public.`
|
|
47
|
+
);
|
|
48
|
+
} else if (privateVarBehavior === "warn") {
|
|
49
|
+
console.warn(
|
|
50
|
+
`[Flight] Private env var "process.env.${varName}" in ${id}
|
|
51
|
+
\u2192 Replaced with undefined for security.
|
|
52
|
+
\u2192 Use FLIGHT_PUBLIC_${varName} if this should be public.`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
modifiedCode = modifiedCode.replace(
|
|
56
|
+
new RegExp(`process\\.env\\.${varName}`, "g"),
|
|
57
|
+
"undefined"
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
if (issues.length > 0 && privateVarBehavior === "error") {
|
|
61
|
+
throw new Error(
|
|
62
|
+
`[Flight] Environment variable security violations:
|
|
63
|
+
|
|
64
|
+
${issues.join("\n\n")}`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
if (hasModifications) {
|
|
68
|
+
return {
|
|
69
|
+
code: modifiedCode,
|
|
70
|
+
map: null
|
|
71
|
+
// TODO: source map
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function isPublicKey(key, prefixes, allowList) {
|
|
79
|
+
if (allowList.includes(key)) return true;
|
|
80
|
+
for (const prefix of prefixes) {
|
|
81
|
+
if (key.startsWith(prefix)) return true;
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export { flightEnvPlugin };
|
|
87
|
+
//# sourceMappingURL=chunk-RFTE6JVG.js.map
|
|
88
|
+
//# sourceMappingURL=chunk-RFTE6JVG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/plugins/env-plugin.ts"],"names":["options"],"mappings":"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","file":"chunk-RFTE6JVG.js","sourcesContent":["/**\r\n * @flight-framework/core - Environment Variables Plugin\r\n * \r\n * Vite plugin for environment variable protection.\r\n * OPTIONAL - use if you want build-time protection.\r\n * \r\n * Features:\r\n * - FLIGHT_PUBLIC_* and VITE_* accessible on client\r\n * - Other vars replaced with undefined on client\r\n * - Configurable behavior (warn, error, silent)\r\n * \r\n * @example\r\n * ```typescript\r\n * // vite.config.ts\r\n * import { flightEnvPlugin } from '@flight-framework/core/plugins';\r\n * \r\n * export default {\r\n * plugins: [flightEnvPlugin()],\r\n * };\r\n * ```\r\n */\r\n\r\nimport type { Plugin, ResolvedConfig } from 'vite';\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\nexport interface FlightEnvPluginOptions {\r\n /**\r\n * Prefix for public environment variables.\r\n * Variables with this prefix will be accessible on the client.\r\n * @default 'FLIGHT_PUBLIC_'\r\n */\r\n publicPrefix?: string;\r\n\r\n /**\r\n * Additional prefixes to treat as public.\r\n * @default ['VITE_']\r\n */\r\n additionalPublicPrefixes?: string[];\r\n\r\n /**\r\n * How to handle private vars accessed in client code.\r\n * - 'warn': Log warning, replace with undefined\r\n * - 'error': Throw build error\r\n * - 'silent': Replace with undefined, no warning\r\n * @default 'warn'\r\n */\r\n privateVarBehavior?: 'warn' | 'error' | 'silent';\r\n\r\n /**\r\n * Specific variables to allow (bypass prefix check).\r\n * @default ['NODE_ENV', 'MODE']\r\n */\r\n allowList?: string[];\r\n}\r\n\r\n// ============================================================================\r\n// Plugin\r\n// ============================================================================\r\n\r\n/**\r\n * Vite plugin to protect environment variables.\r\n * \r\n * This plugin is OPTIONAL. Use it if you want build-time\r\n * protection for your environment variables.\r\n * \r\n * By default, it will:\r\n * - Allow FLIGHT_PUBLIC_* and VITE_* on client\r\n * - Allow NODE_ENV and MODE\r\n * - Replace other process.env.* with undefined on client\r\n * - Show warnings in development\r\n */\r\nexport function flightEnvPlugin(options: FlightEnvPluginOptions = {}): Plugin {\r\n const {\r\n publicPrefix = 'FLIGHT_PUBLIC_',\r\n additionalPublicPrefixes = ['VITE_'],\r\n privateVarBehavior = 'warn',\r\n allowList = ['NODE_ENV', 'MODE'],\r\n } = options;\r\n\r\n const allPublicPrefixes = [publicPrefix, ...additionalPublicPrefixes];\r\n let config: ResolvedConfig;\r\n\r\n return {\r\n name: 'flight:env',\r\n enforce: 'pre',\r\n\r\n configResolved(resolvedConfig) {\r\n config = resolvedConfig;\r\n },\r\n\r\n config(_, { isSsrBuild }) {\r\n // For SSR builds, don't modify anything\r\n if (isSsrBuild) return {};\r\n\r\n // Collect public environment variables\r\n const publicEnvVars: Record<string, string> = {};\r\n\r\n for (const [key, value] of Object.entries(process.env)) {\r\n if (value !== undefined && isPublicKey(key, allPublicPrefixes, allowList)) {\r\n publicEnvVars[`process.env.${key}`] = JSON.stringify(value);\r\n }\r\n }\r\n\r\n return {\r\n define: publicEnvVars,\r\n };\r\n },\r\n\r\n transform(code, id, options) {\r\n // Skip SSR builds - server can access everything\r\n if (options?.ssr) return null;\r\n\r\n // Skip node_modules\r\n if (id.includes('node_modules')) return null;\r\n\r\n // Skip if no process.env references\r\n if (!code.includes('process.env.')) return null;\r\n\r\n // Find all process.env.VARIABLE_NAME patterns\r\n const envVarPattern = /process\\.env\\.([A-Z_][A-Z0-9_]*)/g;\r\n let hasModifications = false;\r\n let modifiedCode = code;\r\n\r\n let match;\r\n const issues: string[] = [];\r\n\r\n while ((match = envVarPattern.exec(code)) !== null) {\r\n const varName = match[1];\r\n\r\n // Skip public vars\r\n if (isPublicKey(varName, allPublicPrefixes, allowList)) {\r\n continue;\r\n }\r\n\r\n // Private var accessed in client code\r\n hasModifications = true;\r\n\r\n if (privateVarBehavior === 'error') {\r\n issues.push(\r\n `Private env var \"${varName}\" accessed in client code.\\n` +\r\n ` File: ${id}\\n` +\r\n ` Use FLIGHT_PUBLIC_${varName} if this should be public.`\r\n );\r\n } else if (privateVarBehavior === 'warn') {\r\n console.warn(\r\n `[Flight] Private env var \"process.env.${varName}\" in ${id}\\n` +\r\n ` → Replaced with undefined for security.\\n` +\r\n ` → Use FLIGHT_PUBLIC_${varName} if this should be public.`\r\n );\r\n }\r\n\r\n // Replace with undefined\r\n modifiedCode = modifiedCode.replace(\r\n new RegExp(`process\\\\.env\\\\.${varName}`, 'g'),\r\n 'undefined'\r\n );\r\n }\r\n\r\n // Throw if using error mode\r\n if (issues.length > 0 && privateVarBehavior === 'error') {\r\n throw new Error(\r\n `[Flight] Environment variable security violations:\\n\\n${issues.join('\\n\\n')}`\r\n );\r\n }\r\n\r\n if (hasModifications) {\r\n return {\r\n code: modifiedCode,\r\n map: null, // TODO: source map\r\n };\r\n }\r\n\r\n return null;\r\n },\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// Helpers\r\n// ============================================================================\r\n\r\nfunction isPublicKey(\r\n key: string,\r\n prefixes: string[],\r\n allowList: string[]\r\n): boolean {\r\n // Check allow list\r\n if (allowList.includes(key)) return true;\r\n\r\n // Check prefixes\r\n for (const prefix of prefixes) {\r\n if (key.startsWith(prefix)) return true;\r\n }\r\n\r\n return false;\r\n}\r\n"]}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// src/utils/boundaries.ts
|
|
2
|
+
function assertServer(context) {
|
|
3
|
+
if (typeof window !== "undefined") {
|
|
4
|
+
const name = context || "This function";
|
|
5
|
+
throw new Error(
|
|
6
|
+
`[Flight] ${name} can only be called on the server. If you need this on the client, use an API route or server action.`
|
|
7
|
+
);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function assertClient(context) {
|
|
11
|
+
if (typeof window === "undefined") {
|
|
12
|
+
const name = context || "This function";
|
|
13
|
+
throw new Error(
|
|
14
|
+
`[Flight] ${name} can only be called on the client. This code should not run during SSR.`
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function getEnv(key) {
|
|
19
|
+
if (isPublicEnvVar(key)) {
|
|
20
|
+
return getEnvValue(key);
|
|
21
|
+
}
|
|
22
|
+
if (typeof window !== "undefined") {
|
|
23
|
+
if (process.env.NODE_ENV === "development") {
|
|
24
|
+
console.warn(
|
|
25
|
+
`[Flight] Attempted to access private env var "${key}" on client. This returns undefined for security. If this should be public, rename it to FLIGHT_PUBLIC_${key}`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
return void 0;
|
|
29
|
+
}
|
|
30
|
+
return getEnvValue(key);
|
|
31
|
+
}
|
|
32
|
+
function requireEnv(key) {
|
|
33
|
+
const value = getEnv(key);
|
|
34
|
+
if (value === void 0) {
|
|
35
|
+
if (typeof window !== "undefined" && !isPublicEnvVar(key)) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`[Flight] Cannot access private env var "${key}" on client. Move this code to the server or use FLIGHT_PUBLIC_${key}`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
throw new Error(
|
|
41
|
+
`[Flight] Required environment variable "${key}" is not set. Check your .env file or environment configuration.`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
function isPublicEnvVar(key) {
|
|
47
|
+
return key.startsWith("FLIGHT_PUBLIC_") || key.startsWith("VITE_") || key === "NODE_ENV" || key === "MODE";
|
|
48
|
+
}
|
|
49
|
+
function getEnvValue(key) {
|
|
50
|
+
if (typeof process !== "undefined" && process.env) {
|
|
51
|
+
const value = process.env[key];
|
|
52
|
+
if (value !== void 0) return value;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const meta = import.meta;
|
|
56
|
+
if (meta.env) {
|
|
57
|
+
const value = meta.env[key];
|
|
58
|
+
if (value !== void 0) return value;
|
|
59
|
+
}
|
|
60
|
+
} catch {
|
|
61
|
+
}
|
|
62
|
+
return void 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { assertClient, assertServer, getEnv, requireEnv };
|
|
66
|
+
//# sourceMappingURL=chunk-WF46NESN.js.map
|
|
67
|
+
//# sourceMappingURL=chunk-WF46NESN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/boundaries.ts"],"names":[],"mappings":";AAyCO,SAAS,aAAa,OAAA,EAAwB;AACjD,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAC/B,IAAA,MAAM,OAAO,OAAA,IAAW,eAAA;AACxB,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,YAAY,IAAI,CAAA,qGAAA;AAAA,KAEpB;AAAA,EACJ;AACJ;AAoBO,SAAS,aAAa,OAAA,EAAwB;AACjD,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAC/B,IAAA,MAAM,OAAO,OAAA,IAAW,eAAA;AACxB,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,YAAY,IAAI,CAAA,uEAAA;AAAA,KAEpB;AAAA,EACJ;AACJ;AA2BO,SAAS,OAAO,GAAA,EAAiC;AAEpD,EAAA,IAAI,cAAA,CAAe,GAAG,CAAA,EAAG;AACrB,IAAA,OAAO,YAAY,GAAG,CAAA;AAAA,EAC1B;AAGA,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAE/B,IAAA,IAAI,OAAA,CAAQ,GAAA,CAAI,QAAA,KAAa,aAAA,EAAe;AACxC,MAAA,OAAA,CAAQ,IAAA;AAAA,QACJ,CAAA,8CAAA,EAAiD,GAAG,CAAA,uGAAA,EAEI,GAAG,CAAA;AAAA,OAC/D;AAAA,IACJ;AACA,IAAA,OAAO,MAAA;AAAA,EACX;AAGA,EAAA,OAAO,YAAY,GAAG,CAAA;AAC1B;AAgBO,SAAS,WAAW,GAAA,EAAqB;AAC5C,EAAA,MAAM,KAAA,GAAQ,OAAO,GAAG,CAAA;AAExB,EAAA,IAAI,UAAU,MAAA,EAAW;AAErB,IAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,CAAC,cAAA,CAAe,GAAG,CAAA,EAAG;AACvD,MAAA,MAAM,IAAI,KAAA;AAAA,QACN,CAAA,wCAAA,EAA2C,GAAG,CAAA,+DAAA,EACO,GAAG,CAAA;AAAA,OAC5D;AAAA,IACJ;AACA,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,2CAA2C,GAAG,CAAA,gEAAA;AAAA,KAElD;AAAA,EACJ;AAEA,EAAA,OAAO,KAAA;AACX;AASA,SAAS,eAAe,GAAA,EAAsB;AAC1C,EAAA,OACI,GAAA,CAAI,UAAA,CAAW,gBAAgB,CAAA,IAC/B,GAAA,CAAI,WAAW,OAAO,CAAA,IACtB,GAAA,KAAQ,UAAA,IACR,GAAA,KAAQ,MAAA;AAEhB;AAKA,SAAS,YAAY,GAAA,EAAiC;AAElD,EAAA,IAAI,OAAO,OAAA,KAAY,WAAA,IAAe,OAAA,CAAQ,GAAA,EAAK;AAC/C,IAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAA;AAC7B,IAAA,IAAI,KAAA,KAAU,QAAW,OAAO,KAAA;AAAA,EACpC;AAGA,EAAA,IAAI;AACA,IAAA,MAAM,IAAA,GAAO,MAAA,CAAA,IAAA;AACb,IAAA,IAAI,KAAK,GAAA,EAAK;AACV,MAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA;AAC1B,MAAA,IAAI,KAAA,KAAU,QAAW,OAAO,KAAA;AAAA,IACpC;AAAA,EACJ,CAAA,CAAA,MAAQ;AAAA,EAER;AAEA,EAAA,OAAO,MAAA;AACX","file":"chunk-WF46NESN.js","sourcesContent":["/**\r\n * @flight-framework/core - Server/Client Boundaries\r\n * \r\n * Runtime utilities for server/client code separation.\r\n * These are OPTIONAL - use them if you want safety guards.\r\n * Flight doesn't force you to use them.\r\n * \r\n * @example\r\n * ```typescript\r\n * import { assertServer, getEnv } from '@flight-framework/core';\r\n * \r\n * export async function fetchFromDatabase() {\r\n * assertServer('fetchFromDatabase'); // Throws if called on client\r\n * const dbUrl = getEnv('DATABASE_URL'); // Returns undefined on client\r\n * // ... your code\r\n * }\r\n * ```\r\n */\r\n\r\n// ============================================================================\r\n// Runtime Assertions\r\n// ============================================================================\r\n\r\n/**\r\n * Assert that code is running on the server.\r\n * Throws an error if called from the client (browser).\r\n * \r\n * Use this to protect server-only code from being accidentally\r\n * called on the client.\r\n * \r\n * @param context - Optional context string for the error message\r\n * @throws Error if called on client\r\n * \r\n * @example\r\n * ```typescript\r\n * export async function queryDatabase() {\r\n * assertServer('queryDatabase');\r\n * // Safe to use process.env, database connections, etc.\r\n * }\r\n * ```\r\n */\r\nexport function assertServer(context?: string): void {\r\n if (typeof window !== 'undefined') {\r\n const name = context || 'This function';\r\n throw new Error(\r\n `[Flight] ${name} can only be called on the server. ` +\r\n `If you need this on the client, use an API route or server action.`\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Assert that code is running on the client.\r\n * Throws an error if called from the server.\r\n * \r\n * Use this to protect client-only code (e.g., DOM manipulation,\r\n * browser APIs) from being accidentally called during SSR.\r\n * \r\n * @param context - Optional context string for the error message\r\n * @throws Error if called on server\r\n * \r\n * @example\r\n * ```typescript\r\n * export function scrollToTop() {\r\n * assertClient('scrollToTop');\r\n * window.scrollTo(0, 0);\r\n * }\r\n * ```\r\n */\r\nexport function assertClient(context?: string): void {\r\n if (typeof window === 'undefined') {\r\n const name = context || 'This function';\r\n throw new Error(\r\n `[Flight] ${name} can only be called on the client. ` +\r\n `This code should not run during SSR.`\r\n );\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Environment Variable Helpers\r\n// ============================================================================\r\n\r\n/**\r\n * Get an environment variable safely.\r\n * \r\n * On the server: Returns the variable value.\r\n * On the client: Returns undefined for non-public vars.\r\n * \r\n * Public variables (prefixed with FLIGHT_PUBLIC_) are always accessible.\r\n * \r\n * @param key - Environment variable name\r\n * @returns The value or undefined\r\n * \r\n * @example\r\n * ```typescript\r\n * // On server: returns actual value\r\n * // On client: returns undefined (safe!)\r\n * const dbUrl = getEnv('DATABASE_URL');\r\n * \r\n * // Works on both server and client\r\n * const apiUrl = getEnv('FLIGHT_PUBLIC_API_URL');\r\n * ```\r\n */\r\nexport function getEnv(key: string): string | undefined {\r\n // Public vars are always accessible\r\n if (isPublicEnvVar(key)) {\r\n return getEnvValue(key);\r\n }\r\n\r\n // Private vars only on server\r\n if (typeof window !== 'undefined') {\r\n // On client - return undefined safely\r\n if (process.env.NODE_ENV === 'development') {\r\n console.warn(\r\n `[Flight] Attempted to access private env var \"${key}\" on client. ` +\r\n `This returns undefined for security. ` +\r\n `If this should be public, rename it to FLIGHT_PUBLIC_${key}`\r\n );\r\n }\r\n return undefined;\r\n }\r\n\r\n // On server - return actual value\r\n return getEnvValue(key);\r\n}\r\n\r\n/**\r\n * Get an environment variable, throwing if not found.\r\n * Use this when a variable is required.\r\n * \r\n * @param key - Environment variable name\r\n * @returns The value\r\n * @throws Error if variable is not set or inaccessible\r\n * \r\n * @example\r\n * ```typescript\r\n * // Throws if DATABASE_URL is not set\r\n * const dbUrl = requireEnv('DATABASE_URL');\r\n * ```\r\n */\r\nexport function requireEnv(key: string): string {\r\n const value = getEnv(key);\r\n\r\n if (value === undefined) {\r\n // Check if we're on client accessing private var\r\n if (typeof window !== 'undefined' && !isPublicEnvVar(key)) {\r\n throw new Error(\r\n `[Flight] Cannot access private env var \"${key}\" on client. ` +\r\n `Move this code to the server or use FLIGHT_PUBLIC_${key}`\r\n );\r\n }\r\n throw new Error(\r\n `[Flight] Required environment variable \"${key}\" is not set. ` +\r\n `Check your .env file or environment configuration.`\r\n );\r\n }\r\n\r\n return value;\r\n}\r\n\r\n// ============================================================================\r\n// Helper Functions\r\n// ============================================================================\r\n\r\n/**\r\n * Check if an environment variable is public (safe for client).\r\n */\r\nfunction isPublicEnvVar(key: string): boolean {\r\n return (\r\n key.startsWith('FLIGHT_PUBLIC_') ||\r\n key.startsWith('VITE_') ||\r\n key === 'NODE_ENV' ||\r\n key === 'MODE'\r\n );\r\n}\r\n\r\n/**\r\n * Get environment variable value from available sources.\r\n */\r\nfunction getEnvValue(key: string): string | undefined {\r\n // Try process.env (Node.js / SSR)\r\n if (typeof process !== 'undefined' && process.env) {\r\n const value = process.env[key];\r\n if (value !== undefined) return value;\r\n }\r\n\r\n // Try import.meta.env (Vite) - use type-safe access\r\n try {\r\n const meta = import.meta as { env?: Record<string, string | undefined> };\r\n if (meta.env) {\r\n const value = meta.env[key];\r\n if (value !== undefined) return value;\r\n }\r\n } catch {\r\n // import.meta not available in this environment\r\n }\r\n\r\n return undefined;\r\n}\r\n\r\n// ============================================================================\r\n// Type Guards\r\n// ============================================================================\r\n\r\n/**\r\n * Check if code is running on the server.\r\n * This is a re-export for convenience.\r\n */\r\nexport { isServer } from './env.js';\r\n\r\n/**\r\n * Check if code is running in the browser.\r\n * This is a re-export for convenience.\r\n */\r\nexport { isBrowser } from './env.js';\r\n"]}
|
package/dist/client.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { Middleware, MiddlewareContext, MiddlewareNext, createMiddlewareChain }
|
|
|
5
5
|
export { CustomHydrationTrigger, HydrateOptions, HydrationTrigger, IslandConfig, hydrateIslands, registerFlightIslandElement } from './islands/index.js';
|
|
6
6
|
export { BuiltInCondition, DEFAULT_BOT_PATTERNS, StreamingCondition, isBot, isSlowConnection, prefersNoStream, supportsStreaming } from './streaming/conditional.js';
|
|
7
7
|
export { BadRequestError, ErrorBoundaryOptions, FlightError, FlightErrorOptions, FlightErrorProps, ForbiddenError, InternalError, NotFoundError, ResetDetails, UnauthorizedError, clearError, createError, getError, getErrorStatusCode, isFlightError, isForbiddenError as isForbidden, isNotFoundError as isNotFound, isUnauthorizedError as isUnauthorized, showError } from './errors/index.js';
|
|
8
|
-
export { getEnvironment, isBrowser, isDevelopment, isProduction, isServer, isTest } from './
|
|
8
|
+
export { g as getEnvironment, d as isBrowser, a as isDevelopment, i as isProduction, c as isServer, b as isTest } from './env-9do2c6yn.js';
|
|
9
9
|
import './render/index.js';
|
|
10
10
|
import './adapters/index.js';
|
|
11
11
|
|