@useavalon/avalon 0.1.11 → 0.1.13

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.
Files changed (141) hide show
  1. package/README.md +54 -54
  2. package/mod.ts +302 -302
  3. package/package.json +49 -26
  4. package/src/build/integration-bundler-plugin.ts +116 -116
  5. package/src/build/integration-config.ts +168 -168
  6. package/src/build/integration-detection-plugin.ts +117 -117
  7. package/src/build/integration-resolver-plugin.ts +90 -90
  8. package/src/build/island-manifest.ts +269 -269
  9. package/src/build/island-types-generator.ts +476 -476
  10. package/src/build/mdx-island-transform.ts +464 -464
  11. package/src/build/mdx-plugin.ts +98 -98
  12. package/src/build/page-island-transform.ts +598 -598
  13. package/src/build/prop-extractors/index.ts +21 -21
  14. package/src/build/prop-extractors/lit.ts +140 -140
  15. package/src/build/prop-extractors/qwik.ts +16 -16
  16. package/src/build/prop-extractors/solid.ts +125 -125
  17. package/src/build/prop-extractors/svelte.ts +194 -194
  18. package/src/build/prop-extractors/vue.ts +111 -111
  19. package/src/build/sidecar-file-manager.ts +104 -104
  20. package/src/build/sidecar-renderer.ts +30 -30
  21. package/src/client/adapters/index.ts +21 -13
  22. package/src/client/components.ts +35 -35
  23. package/src/client/css-hmr-handler.ts +344 -344
  24. package/src/client/framework-adapter.ts +462 -462
  25. package/src/client/hmr-coordinator.ts +396 -396
  26. package/src/client/hmr-error-overlay.js +533 -533
  27. package/src/client/main.js +824 -816
  28. package/src/client/types/framework-runtime.d.ts +68 -68
  29. package/src/client/types/vite-hmr.d.ts +46 -46
  30. package/src/client/types/vite-virtual-modules.d.ts +70 -60
  31. package/src/components/Image.tsx +123 -123
  32. package/src/components/IslandErrorBoundary.tsx +145 -145
  33. package/src/components/LayoutDataErrorBoundary.tsx +141 -141
  34. package/src/components/LayoutErrorBoundary.tsx +127 -127
  35. package/src/components/PersistentIsland.tsx +52 -52
  36. package/src/components/StreamingErrorBoundary.tsx +233 -233
  37. package/src/components/StreamingLayout.tsx +538 -538
  38. package/src/core/components/component-analyzer.ts +192 -192
  39. package/src/core/components/component-detection.ts +508 -508
  40. package/src/core/components/enhanced-framework-detector.ts +500 -500
  41. package/src/core/components/framework-registry.ts +563 -563
  42. package/src/core/content/mdx-processor.ts +46 -46
  43. package/src/core/integrations/index.ts +19 -19
  44. package/src/core/integrations/loader.ts +125 -125
  45. package/src/core/integrations/registry.ts +175 -175
  46. package/src/core/islands/island-persistence.ts +325 -325
  47. package/src/core/islands/island-state-serializer.ts +258 -258
  48. package/src/core/islands/persistent-island-context.tsx +80 -80
  49. package/src/core/islands/use-persistent-state.ts +68 -68
  50. package/src/core/layout/enhanced-layout-resolver.ts +322 -322
  51. package/src/core/layout/layout-cache-manager.ts +485 -485
  52. package/src/core/layout/layout-composer.ts +357 -357
  53. package/src/core/layout/layout-data-loader.ts +516 -516
  54. package/src/core/layout/layout-discovery.ts +243 -243
  55. package/src/core/layout/layout-matcher.ts +299 -299
  56. package/src/core/layout/layout-types.ts +110 -110
  57. package/src/core/modules/framework-module-resolver.ts +273 -273
  58. package/src/islands/component-analysis.ts +213 -213
  59. package/src/islands/css-utils.ts +565 -565
  60. package/src/islands/discovery/index.ts +80 -80
  61. package/src/islands/discovery/registry.ts +340 -340
  62. package/src/islands/discovery/resolver.ts +477 -477
  63. package/src/islands/discovery/scanner.ts +386 -386
  64. package/src/islands/discovery/types.ts +117 -117
  65. package/src/islands/discovery/validator.ts +544 -544
  66. package/src/islands/discovery/watcher.ts +368 -368
  67. package/src/islands/framework-detection.ts +428 -428
  68. package/src/islands/integration-loader.ts +490 -490
  69. package/src/islands/island.tsx +565 -565
  70. package/src/islands/render-cache.ts +550 -550
  71. package/src/islands/types.ts +80 -80
  72. package/src/islands/universal-css-collector.ts +157 -157
  73. package/src/islands/universal-head-collector.ts +137 -137
  74. package/src/layout-system.d.ts +592 -592
  75. package/src/layout-system.ts +218 -218
  76. package/src/middleware/discovery.ts +268 -268
  77. package/src/middleware/executor.ts +315 -315
  78. package/src/middleware/index.ts +76 -76
  79. package/src/middleware/types.ts +99 -99
  80. package/src/nitro/build-config.ts +575 -575
  81. package/src/nitro/config.ts +483 -483
  82. package/src/nitro/error-handler.ts +636 -636
  83. package/src/nitro/index.ts +173 -173
  84. package/src/nitro/island-manifest.ts +584 -584
  85. package/src/nitro/middleware-adapter.ts +260 -260
  86. package/src/nitro/renderer.ts +1471 -1471
  87. package/src/nitro/route-discovery.ts +439 -439
  88. package/src/nitro/types.ts +321 -321
  89. package/src/render/collect-css.ts +198 -198
  90. package/src/render/error-pages.ts +79 -79
  91. package/src/render/isolated-ssr-renderer.ts +654 -654
  92. package/src/render/ssr.ts +1030 -1030
  93. package/src/schemas/api.ts +30 -30
  94. package/src/schemas/core.ts +64 -64
  95. package/src/schemas/index.ts +212 -212
  96. package/src/schemas/layout.ts +279 -279
  97. package/src/schemas/routing/index.ts +38 -38
  98. package/src/schemas/routing.ts +376 -376
  99. package/src/types/as-island.ts +20 -20
  100. package/src/types/image.d.ts +106 -106
  101. package/src/types/index.d.ts +22 -22
  102. package/src/types/island-jsx.d.ts +33 -33
  103. package/src/types/island-prop.d.ts +20 -20
  104. package/src/types/layout.ts +285 -285
  105. package/src/types/mdx.d.ts +6 -6
  106. package/src/types/routing.ts +555 -555
  107. package/src/types/types.ts +5 -5
  108. package/src/types/urlpattern.d.ts +49 -49
  109. package/src/types/vite-env.d.ts +11 -11
  110. package/src/utils/dev-logger.ts +299 -299
  111. package/src/utils/fs.ts +151 -151
  112. package/src/vite-plugin/auto-discover.ts +551 -551
  113. package/src/vite-plugin/config.ts +266 -266
  114. package/src/vite-plugin/errors.ts +127 -127
  115. package/src/vite-plugin/image-optimization.ts +156 -156
  116. package/src/vite-plugin/integration-activator.ts +126 -126
  117. package/src/vite-plugin/island-sidecar-plugin.ts +176 -176
  118. package/src/vite-plugin/module-discovery.ts +189 -189
  119. package/src/vite-plugin/nitro-integration.ts +1354 -1354
  120. package/src/vite-plugin/plugin.ts +403 -409
  121. package/src/vite-plugin/types.ts +327 -327
  122. package/src/vite-plugin/validation.ts +228 -228
  123. package/src/client/adapters/index.js +0 -12
  124. package/src/client/adapters/lit-adapter.js +0 -467
  125. package/src/client/adapters/lit-adapter.ts +0 -654
  126. package/src/client/adapters/preact-adapter.js +0 -223
  127. package/src/client/adapters/preact-adapter.ts +0 -331
  128. package/src/client/adapters/qwik-adapter.js +0 -259
  129. package/src/client/adapters/qwik-adapter.ts +0 -345
  130. package/src/client/adapters/react-adapter.js +0 -220
  131. package/src/client/adapters/react-adapter.ts +0 -353
  132. package/src/client/adapters/solid-adapter.js +0 -295
  133. package/src/client/adapters/solid-adapter.ts +0 -451
  134. package/src/client/adapters/svelte-adapter.js +0 -368
  135. package/src/client/adapters/svelte-adapter.ts +0 -524
  136. package/src/client/adapters/vue-adapter.js +0 -278
  137. package/src/client/adapters/vue-adapter.ts +0 -467
  138. package/src/client/components.js +0 -23
  139. package/src/client/css-hmr-handler.js +0 -263
  140. package/src/client/framework-adapter.js +0 -283
  141. package/src/client/hmr-coordinator.js +0 -274
@@ -1,483 +1,483 @@
1
- /**
2
- * Nitro Configuration Module for Avalon
3
- *
4
- * This module provides configuration interfaces and utilities for integrating
5
- * Nitro as Avalon's server runtime. It handles preset configuration, route rules,
6
- * and merging Avalon-specific options with Nitro defaults.
7
- *
8
- * ## Static Asset Handling
9
- *
10
- * Nitro automatically serves static assets from the `public/` directory at the root path.
11
- * The configuration supports:
12
- * - `publicAssets`: Configures directories for static file serving
13
- * - `routeRules`: Configures cache headers for different asset types
14
- * - `staticAssets`: Avalon-specific static asset configuration
15
- *
16
- * Default route rules for static assets are generated by `createDefaultStaticAssetRouteRules()`:
17
- * - `/assets/**`, `/islands/**`, `/chunks/**`: Long cache (1 year, immutable)
18
- * - `/*.woff`, `/*.woff2`: Long cache for fonts
19
- * - `/*.html`, `/*.css`: Short cache with revalidation
20
- * - `/favicon.ico`: Medium cache (1 day)
21
- */
22
-
23
- import type { ResolvedAvalonConfig } from "../vite-plugin/types.ts";
24
-
25
- /**
26
- * Cache configuration options for route rules
27
- */
28
- export interface CacheOptions {
29
- /** Maximum age in seconds for cache validity */
30
- maxAge?: number;
31
- /** Maximum age in seconds for stale content */
32
- staleMaxAge?: number;
33
- /** Enable stale-while-revalidate behavior */
34
- swr?: boolean;
35
- }
36
-
37
- /**
38
- * Route rule configuration for caching, redirects, proxying, and headers
39
- */
40
- export interface RouteRule {
41
- /** Cache configuration or boolean to enable/disable */
42
- cache?: CacheOptions | boolean;
43
- /** Redirect destination URL */
44
- redirect?: string;
45
- /** Proxy destination URL */
46
- proxy?: string;
47
- /** Custom headers to set */
48
- headers?: Record<string, string>;
49
- /** Enable CORS for this route */
50
- cors?: boolean;
51
- }
52
-
53
- /**
54
- * Static asset serving configuration
55
- */
56
- export interface StaticAssetsConfig {
57
- /**
58
- * Directory containing public static assets
59
- * @default "public"
60
- */
61
- publicDir?: string;
62
-
63
- /**
64
- * Directory containing compiled build output
65
- * @default "dist"
66
- */
67
- buildDir?: string;
68
-
69
- /**
70
- * Enable serving pre-compressed assets (gzip, brotli)
71
- * @default true
72
- */
73
- compression?: boolean;
74
-
75
- /**
76
- * Default cache control header for immutable static assets
77
- * @default "public, max-age=31536000, immutable"
78
- */
79
- cacheControl?: string;
80
-
81
- /**
82
- * Cache control header for mutable assets (HTML, etc.)
83
- * @default "public, max-age=0, must-revalidate"
84
- */
85
- mutableCacheControl?: string;
86
-
87
- /**
88
- * Custom headers to add to all static asset responses
89
- */
90
- headers?: Record<string, string>;
91
- }
92
-
93
- /**
94
- * Avalon-specific Nitro configuration options
95
- */
96
- export interface AvalonNitroConfig {
97
- /**
98
- * Deployment preset (vercel, cloudflare, deno-deploy, node-server, netlify, etc.)
99
- * @default "node-server"
100
- */
101
- preset?: string;
102
-
103
- /**
104
- * Directory containing server routes
105
- * @default "server"
106
- */
107
- serverDir?: string;
108
-
109
- /**
110
- * Directory containing pages (overrides Avalon config if set)
111
- */
112
- pagesDir?: string;
113
-
114
- /**
115
- * Enable streaming SSR responses
116
- * @default true
117
- */
118
- streaming?: boolean;
119
-
120
- /**
121
- * Route rules for caching, redirects, proxying, and headers
122
- * Keys are route patterns (e.g., "/api/**", "/static/**")
123
- */
124
- routeRules?: Record<string, RouteRule>;
125
-
126
- /**
127
- * Runtime configuration accessible via useRuntimeConfig()
128
- * Can be overridden by NITRO_ prefixed environment variables
129
- */
130
- runtimeConfig?: Record<string, unknown>;
131
-
132
- /**
133
- * Public runtime configuration (exposed to client)
134
- */
135
- publicRuntimeConfig?: Record<string, unknown>;
136
-
137
- /**
138
- * Static asset serving configuration
139
- */
140
- staticAssets?: StaticAssetsConfig;
141
-
142
- /** Nitro v3: Compatibility date for preset features (YYYY-MM-DD) */
143
- compatibilityDate?: string;
144
- /** Nitro v3: Dependencies to trace instead of bundle */
145
- traceDeps?: string[];
146
- /** Nitro v3: Rolldown-specific configuration */
147
- rolldownConfig?: Record<string, unknown>;
148
- /** Nitro v3: Custom server entry point */
149
- serverEntry?: string;
150
- /** Nitro v3: Renderer configuration, or false to disable */
151
- renderer?: { handler: string } | false;
152
- /** Nitro v3: Pre-compress public assets (gzip, brotli, zstd) */
153
- compressPublicAssets?: boolean | { gzip?: boolean; brotli?: boolean; zstd?: boolean };
154
- }
155
-
156
- /**
157
- * Nitro configuration output structure
158
- * This matches the expected NitroConfig interface from nitro/types
159
- */
160
- export interface NitroConfigOutput {
161
- /** Deployment preset */
162
- preset: string;
163
- /** Server directory */
164
- serverDir: string;
165
- /** Route rules */
166
- routeRules: Record<string, RouteRule>;
167
- /** Runtime configuration */
168
- runtimeConfig: {
169
- avalon: AvalonRuntimeConfig;
170
- [key: string]: unknown;
171
- };
172
- /** Public runtime configuration */
173
- publicRuntimeConfig?: Record<string, unknown>;
174
- /** Renderer configuration, or false to disable */
175
- renderer?: {
176
- handler: string;
177
- } | false;
178
- /** Nitro v3: Compatibility date for preset features (YYYY-MM-DD) */
179
- compatibilityDate?: string;
180
- /** Nitro v3: Dependencies to trace instead of bundle */
181
- traceDeps?: string[];
182
- /** Nitro v3: Rolldown-specific configuration */
183
- rolldownConfig?: Record<string, unknown>;
184
- /** Nitro v3: Custom server entry point */
185
- serverEntry?: string;
186
- /** Nitro v3: Pre-compress public assets (gzip, brotli, zstd) */
187
- compressPublicAssets?: boolean | { gzip?: boolean; brotli?: boolean; zstd?: boolean };
188
- /** Public assets directory */
189
- publicAssets?: Array<{
190
- dir: string;
191
- baseURL?: string;
192
- maxAge?: number;
193
- }>;
194
- /** Server assets configuration */
195
- serverAssets?: Array<{
196
- baseName: string;
197
- dir: string;
198
- }>;
199
- /** Static assets configuration */
200
- staticAssets?: StaticAssetsConfig;
201
- }
202
-
203
- /**
204
- * Avalon-specific runtime configuration stored in Nitro's runtimeConfig
205
- */
206
- export interface AvalonRuntimeConfig {
207
- /** Enable streaming SSR */
208
- streaming: boolean;
209
- /** Pages directory path */
210
- pagesDir: string;
211
- /** Layouts directory path */
212
- layoutsDir: string;
213
- }
214
-
215
- /**
216
- * Default static assets configuration
217
- */
218
- export const DEFAULT_STATIC_ASSETS_CONFIG: Required<StaticAssetsConfig> = {
219
- publicDir: "public",
220
- buildDir: "dist",
221
- compression: true,
222
- cacheControl: "public, max-age=31536000, immutable",
223
- mutableCacheControl: "public, max-age=0, must-revalidate",
224
- headers: {},
225
- };
226
-
227
- /**
228
- * All valid Nitro v3 preset names (underscore convention).
229
- */
230
- export const VALID_V3_PRESETS: string[] = [
231
- "node_server",
232
- "node_middleware",
233
- "vercel",
234
- "cloudflare_module",
235
- "cloudflare_pages",
236
- "deno_deploy",
237
- "deno_server",
238
- "netlify_functions",
239
- "netlify_edge",
240
- "aws_lambda",
241
- "azure_swa",
242
- "firebase_functions",
243
- "render_com",
244
- "static",
245
- "browser",
246
- ];
247
-
248
- /**
249
- * Validates a preset name against the v3 preset list.
250
- * Returns the preset name if valid, throws if unrecognized.
251
- *
252
- * @param preset - The preset name to validate (must be a v3 name)
253
- * @returns The validated v3 preset name
254
- * @throws Error if the preset is not recognized
255
- */
256
- export function resolvePresetName(preset: string): string {
257
- if (VALID_V3_PRESETS.includes(preset)) return preset;
258
- throw new Error(
259
- `Unknown Nitro preset: "${preset}". Valid presets: ${VALID_V3_PRESETS.join(", ")}`
260
- );
261
- }
262
-
263
- /**
264
- * Default Nitro configuration values
265
- */
266
- export const DEFAULT_NITRO_CONFIG: Required<
267
- Pick<AvalonNitroConfig, "preset" | "serverDir" | "streaming">
268
- > = {
269
- preset: "node_server",
270
- serverDir: "server",
271
- streaming: true,
272
- };
273
-
274
- /**
275
- * Creates a Nitro configuration from Avalon plugin config and Nitro-specific options
276
- *
277
- * This function merges Avalon's resolved configuration with Nitro-specific options
278
- * to produce a complete Nitro configuration object. Avalon-specific settings are
279
- * stored in `runtimeConfig.avalon` for access by handlers.
280
- *
281
- * @param avalonNitroConfig - Nitro-specific configuration options
282
- * @param resolvedAvalonConfig - Resolved Avalon plugin configuration
283
- * @returns Complete Nitro configuration object
284
- *
285
- * @example
286
- * ```ts
287
- * const nitroConfig = createNitroConfig(
288
- * { preset: 'vercel', streaming: true },
289
- * resolvedAvalonConfig
290
- * );
291
- * ```
292
- */
293
- export function createNitroConfig(
294
- avalonNitroConfig: AvalonNitroConfig,
295
- resolvedAvalonConfig: ResolvedAvalonConfig
296
- ): NitroConfigOutput {
297
- // Determine final directory paths (Nitro config overrides Avalon config)
298
- const pagesDir = avalonNitroConfig.pagesDir ?? resolvedAvalonConfig.pagesDir;
299
- const layoutsDir = resolvedAvalonConfig.layoutsDir;
300
-
301
- // Build Avalon runtime config
302
- const avalonRuntimeConfig: AvalonRuntimeConfig = {
303
- streaming: avalonNitroConfig.streaming ?? DEFAULT_NITRO_CONFIG.streaming,
304
- pagesDir,
305
- layoutsDir,
306
- };
307
-
308
- // Validate that runtimeConfig does not contain a 'nitro' key (reserved in v3)
309
- if (avalonNitroConfig.runtimeConfig && "nitro" in avalonNitroConfig.runtimeConfig) {
310
- throw new Error(
311
- 'The "nitro" key in runtimeConfig is reserved by Nitro v3 and cannot be used.'
312
- );
313
- }
314
-
315
- // Merge user runtime config with Avalon runtime config
316
- const runtimeConfig: NitroConfigOutput["runtimeConfig"] = {
317
- avalon: avalonRuntimeConfig,
318
- ...avalonNitroConfig.runtimeConfig,
319
- };
320
-
321
- // Merge static assets config with defaults
322
- const staticAssetsConfig: StaticAssetsConfig = {
323
- ...DEFAULT_STATIC_ASSETS_CONFIG,
324
- ...avalonNitroConfig.staticAssets,
325
- };
326
-
327
- // Create default route rules for static assets
328
- const defaultStaticRouteRules = createDefaultStaticAssetRouteRules(staticAssetsConfig);
329
-
330
- // Merge user route rules with default static asset rules
331
- const routeRules = mergeRouteRules(
332
- defaultStaticRouteRules,
333
- avalonNitroConfig.routeRules ?? {}
334
- );
335
-
336
- // Configure public assets directories
337
- const publicAssets: NitroConfigOutput["publicAssets"] = [
338
- {
339
- dir: staticAssetsConfig.publicDir ?? DEFAULT_STATIC_ASSETS_CONFIG.publicDir,
340
- baseURL: "/",
341
- maxAge: 0, // Let route rules handle caching
342
- },
343
- ];
344
-
345
- // Resolve renderer: support explicit false to disable, otherwise use default handler
346
- const renderer: NitroConfigOutput["renderer"] =
347
- avalonNitroConfig.renderer === false
348
- ? false
349
- : (avalonNitroConfig.renderer ?? { handler: "./server/renderer.ts" });
350
-
351
- return {
352
- preset: resolvePresetName(avalonNitroConfig.preset ?? DEFAULT_NITRO_CONFIG.preset),
353
- serverDir: avalonNitroConfig.serverDir ?? DEFAULT_NITRO_CONFIG.serverDir,
354
- routeRules,
355
- runtimeConfig,
356
- publicRuntimeConfig: avalonNitroConfig.publicRuntimeConfig,
357
- renderer,
358
- compatibilityDate: avalonNitroConfig.compatibilityDate,
359
- traceDeps: avalonNitroConfig.traceDeps,
360
- rolldownConfig: avalonNitroConfig.rolldownConfig,
361
- serverEntry: avalonNitroConfig.serverEntry,
362
- compressPublicAssets: avalonNitroConfig.compressPublicAssets,
363
- publicAssets,
364
- staticAssets: staticAssetsConfig,
365
- };
366
- }
367
-
368
- /**
369
- * Creates default route rules for static asset caching
370
- *
371
- * @param config - Static assets configuration
372
- * @returns Route rules for static assets
373
- */
374
- export function createDefaultStaticAssetRouteRules(
375
- config: StaticAssetsConfig
376
- ): Record<string, RouteRule> {
377
- const cacheControl = config.cacheControl ?? DEFAULT_STATIC_ASSETS_CONFIG.cacheControl;
378
- const mutableCacheControl = config.mutableCacheControl ?? DEFAULT_STATIC_ASSETS_CONFIG.mutableCacheControl;
379
-
380
- return {
381
- // Immutable assets with hashed filenames (long cache)
382
- "/assets/**": {
383
- headers: {
384
- "Cache-Control": cacheControl,
385
- ...config.headers,
386
- },
387
- },
388
- "/islands/**": {
389
- headers: {
390
- "Cache-Control": cacheControl,
391
- ...config.headers,
392
- },
393
- },
394
- "/chunks/**": {
395
- headers: {
396
- "Cache-Control": cacheControl,
397
- ...config.headers,
398
- },
399
- },
400
- "/_nuxt/**": {
401
- headers: {
402
- "Cache-Control": cacheControl,
403
- ...config.headers,
404
- },
405
- },
406
- // Font files (long cache)
407
- "/**/*.woff": {
408
- headers: {
409
- "Cache-Control": cacheControl,
410
- ...config.headers,
411
- },
412
- },
413
- "/**/*.woff2": {
414
- headers: {
415
- "Cache-Control": cacheControl,
416
- ...config.headers,
417
- },
418
- },
419
- // Mutable assets (short cache with revalidation)
420
- "/**/*.html": {
421
- headers: {
422
- "Cache-Control": mutableCacheControl,
423
- ...config.headers,
424
- },
425
- },
426
- // Favicon (medium cache)
427
- "/favicon.ico": {
428
- headers: {
429
- "Cache-Control": "public, max-age=86400",
430
- ...config.headers,
431
- },
432
- },
433
- // CSS files in public directory (may be mutable)
434
- "/**/*.css": {
435
- headers: {
436
- "Cache-Control": mutableCacheControl,
437
- ...config.headers,
438
- },
439
- },
440
- };
441
- }
442
-
443
- /**
444
- * Validates a Nitro preset name against the v3 preset list
445
- *
446
- * @param preset - The preset name to validate
447
- * @returns True if the preset is a known valid v3 preset
448
- */
449
- export function isValidPreset(preset: string): boolean {
450
- return VALID_V3_PRESETS.includes(preset);
451
- }
452
-
453
- /**
454
- * Merges route rules, with later rules taking precedence
455
- *
456
- * @param baseRules - Base route rules
457
- * @param overrideRules - Override route rules
458
- * @returns Merged route rules
459
- */
460
- export function mergeRouteRules(
461
- baseRules: Record<string, RouteRule>,
462
- overrideRules: Record<string, RouteRule>
463
- ): Record<string, RouteRule> {
464
- const merged: Record<string, RouteRule> = { ...baseRules };
465
-
466
- for (const [pattern, rule] of Object.entries(overrideRules)) {
467
- if (merged[pattern]) {
468
- // Deep merge the rule
469
- merged[pattern] = {
470
- ...merged[pattern],
471
- ...rule,
472
- headers: {
473
- ...merged[pattern].headers,
474
- ...rule.headers,
475
- },
476
- };
477
- } else {
478
- merged[pattern] = rule;
479
- }
480
- }
481
-
482
- return merged;
483
- }
1
+ /**
2
+ * Nitro Configuration Module for Avalon
3
+ *
4
+ * This module provides configuration interfaces and utilities for integrating
5
+ * Nitro as Avalon's server runtime. It handles preset configuration, route rules,
6
+ * and merging Avalon-specific options with Nitro defaults.
7
+ *
8
+ * ## Static Asset Handling
9
+ *
10
+ * Nitro automatically serves static assets from the `public/` directory at the root path.
11
+ * The configuration supports:
12
+ * - `publicAssets`: Configures directories for static file serving
13
+ * - `routeRules`: Configures cache headers for different asset types
14
+ * - `staticAssets`: Avalon-specific static asset configuration
15
+ *
16
+ * Default route rules for static assets are generated by `createDefaultStaticAssetRouteRules()`:
17
+ * - `/assets/**`, `/islands/**`, `/chunks/**`: Long cache (1 year, immutable)
18
+ * - `/*.woff`, `/*.woff2`: Long cache for fonts
19
+ * - `/*.html`, `/*.css`: Short cache with revalidation
20
+ * - `/favicon.ico`: Medium cache (1 day)
21
+ */
22
+
23
+ import type { ResolvedAvalonConfig } from "../vite-plugin/types.ts";
24
+
25
+ /**
26
+ * Cache configuration options for route rules
27
+ */
28
+ export interface CacheOptions {
29
+ /** Maximum age in seconds for cache validity */
30
+ maxAge?: number;
31
+ /** Maximum age in seconds for stale content */
32
+ staleMaxAge?: number;
33
+ /** Enable stale-while-revalidate behavior */
34
+ swr?: boolean;
35
+ }
36
+
37
+ /**
38
+ * Route rule configuration for caching, redirects, proxying, and headers
39
+ */
40
+ export interface RouteRule {
41
+ /** Cache configuration or boolean to enable/disable */
42
+ cache?: CacheOptions | boolean;
43
+ /** Redirect destination URL */
44
+ redirect?: string;
45
+ /** Proxy destination URL */
46
+ proxy?: string;
47
+ /** Custom headers to set */
48
+ headers?: Record<string, string>;
49
+ /** Enable CORS for this route */
50
+ cors?: boolean;
51
+ }
52
+
53
+ /**
54
+ * Static asset serving configuration
55
+ */
56
+ export interface StaticAssetsConfig {
57
+ /**
58
+ * Directory containing public static assets
59
+ * @default "public"
60
+ */
61
+ publicDir?: string;
62
+
63
+ /**
64
+ * Directory containing compiled build output
65
+ * @default "dist"
66
+ */
67
+ buildDir?: string;
68
+
69
+ /**
70
+ * Enable serving pre-compressed assets (gzip, brotli)
71
+ * @default true
72
+ */
73
+ compression?: boolean;
74
+
75
+ /**
76
+ * Default cache control header for immutable static assets
77
+ * @default "public, max-age=31536000, immutable"
78
+ */
79
+ cacheControl?: string;
80
+
81
+ /**
82
+ * Cache control header for mutable assets (HTML, etc.)
83
+ * @default "public, max-age=0, must-revalidate"
84
+ */
85
+ mutableCacheControl?: string;
86
+
87
+ /**
88
+ * Custom headers to add to all static asset responses
89
+ */
90
+ headers?: Record<string, string>;
91
+ }
92
+
93
+ /**
94
+ * Avalon-specific Nitro configuration options
95
+ */
96
+ export interface AvalonNitroConfig {
97
+ /**
98
+ * Deployment preset (vercel, cloudflare, deno-deploy, node-server, netlify, etc.)
99
+ * @default "node-server"
100
+ */
101
+ preset?: string;
102
+
103
+ /**
104
+ * Directory containing server routes
105
+ * @default "server"
106
+ */
107
+ serverDir?: string;
108
+
109
+ /**
110
+ * Directory containing pages (overrides Avalon config if set)
111
+ */
112
+ pagesDir?: string;
113
+
114
+ /**
115
+ * Enable streaming SSR responses
116
+ * @default true
117
+ */
118
+ streaming?: boolean;
119
+
120
+ /**
121
+ * Route rules for caching, redirects, proxying, and headers
122
+ * Keys are route patterns (e.g., "/api/**", "/static/**")
123
+ */
124
+ routeRules?: Record<string, RouteRule>;
125
+
126
+ /**
127
+ * Runtime configuration accessible via useRuntimeConfig()
128
+ * Can be overridden by NITRO_ prefixed environment variables
129
+ */
130
+ runtimeConfig?: Record<string, unknown>;
131
+
132
+ /**
133
+ * Public runtime configuration (exposed to client)
134
+ */
135
+ publicRuntimeConfig?: Record<string, unknown>;
136
+
137
+ /**
138
+ * Static asset serving configuration
139
+ */
140
+ staticAssets?: StaticAssetsConfig;
141
+
142
+ /** Nitro v3: Compatibility date for preset features (YYYY-MM-DD) */
143
+ compatibilityDate?: string;
144
+ /** Nitro v3: Dependencies to trace instead of bundle */
145
+ traceDeps?: string[];
146
+ /** Nitro v3: Rolldown-specific configuration */
147
+ rolldownConfig?: Record<string, unknown>;
148
+ /** Nitro v3: Custom server entry point */
149
+ serverEntry?: string;
150
+ /** Nitro v3: Renderer configuration, or false to disable */
151
+ renderer?: { handler: string } | false;
152
+ /** Nitro v3: Pre-compress public assets (gzip, brotli, zstd) */
153
+ compressPublicAssets?: boolean | { gzip?: boolean; brotli?: boolean; zstd?: boolean };
154
+ }
155
+
156
+ /**
157
+ * Nitro configuration output structure
158
+ * This matches the expected NitroConfig interface from nitro/types
159
+ */
160
+ export interface NitroConfigOutput {
161
+ /** Deployment preset */
162
+ preset: string;
163
+ /** Server directory */
164
+ serverDir: string;
165
+ /** Route rules */
166
+ routeRules: Record<string, RouteRule>;
167
+ /** Runtime configuration */
168
+ runtimeConfig: {
169
+ avalon: AvalonRuntimeConfig;
170
+ [key: string]: unknown;
171
+ };
172
+ /** Public runtime configuration */
173
+ publicRuntimeConfig?: Record<string, unknown>;
174
+ /** Renderer configuration, or false to disable */
175
+ renderer?: {
176
+ handler: string;
177
+ } | false;
178
+ /** Nitro v3: Compatibility date for preset features (YYYY-MM-DD) */
179
+ compatibilityDate?: string;
180
+ /** Nitro v3: Dependencies to trace instead of bundle */
181
+ traceDeps?: string[];
182
+ /** Nitro v3: Rolldown-specific configuration */
183
+ rolldownConfig?: Record<string, unknown>;
184
+ /** Nitro v3: Custom server entry point */
185
+ serverEntry?: string;
186
+ /** Nitro v3: Pre-compress public assets (gzip, brotli, zstd) */
187
+ compressPublicAssets?: boolean | { gzip?: boolean; brotli?: boolean; zstd?: boolean };
188
+ /** Public assets directory */
189
+ publicAssets?: Array<{
190
+ dir: string;
191
+ baseURL?: string;
192
+ maxAge?: number;
193
+ }>;
194
+ /** Server assets configuration */
195
+ serverAssets?: Array<{
196
+ baseName: string;
197
+ dir: string;
198
+ }>;
199
+ /** Static assets configuration */
200
+ staticAssets?: StaticAssetsConfig;
201
+ }
202
+
203
+ /**
204
+ * Avalon-specific runtime configuration stored in Nitro's runtimeConfig
205
+ */
206
+ export interface AvalonRuntimeConfig {
207
+ /** Enable streaming SSR */
208
+ streaming: boolean;
209
+ /** Pages directory path */
210
+ pagesDir: string;
211
+ /** Layouts directory path */
212
+ layoutsDir: string;
213
+ }
214
+
215
+ /**
216
+ * Default static assets configuration
217
+ */
218
+ export const DEFAULT_STATIC_ASSETS_CONFIG: Required<StaticAssetsConfig> = {
219
+ publicDir: "public",
220
+ buildDir: "dist",
221
+ compression: true,
222
+ cacheControl: "public, max-age=31536000, immutable",
223
+ mutableCacheControl: "public, max-age=0, must-revalidate",
224
+ headers: {},
225
+ };
226
+
227
+ /**
228
+ * All valid Nitro v3 preset names (underscore convention).
229
+ */
230
+ export const VALID_V3_PRESETS: string[] = [
231
+ "node_server",
232
+ "node_middleware",
233
+ "vercel",
234
+ "cloudflare_module",
235
+ "cloudflare_pages",
236
+ "deno_deploy",
237
+ "deno_server",
238
+ "netlify_functions",
239
+ "netlify_edge",
240
+ "aws_lambda",
241
+ "azure_swa",
242
+ "firebase_functions",
243
+ "render_com",
244
+ "static",
245
+ "browser",
246
+ ];
247
+
248
+ /**
249
+ * Validates a preset name against the v3 preset list.
250
+ * Returns the preset name if valid, throws if unrecognized.
251
+ *
252
+ * @param preset - The preset name to validate (must be a v3 name)
253
+ * @returns The validated v3 preset name
254
+ * @throws Error if the preset is not recognized
255
+ */
256
+ export function resolvePresetName(preset: string): string {
257
+ if (VALID_V3_PRESETS.includes(preset)) return preset;
258
+ throw new Error(
259
+ `Unknown Nitro preset: "${preset}". Valid presets: ${VALID_V3_PRESETS.join(", ")}`
260
+ );
261
+ }
262
+
263
+ /**
264
+ * Default Nitro configuration values
265
+ */
266
+ export const DEFAULT_NITRO_CONFIG: Required<
267
+ Pick<AvalonNitroConfig, "preset" | "serverDir" | "streaming">
268
+ > = {
269
+ preset: "node_server",
270
+ serverDir: "server",
271
+ streaming: true,
272
+ };
273
+
274
+ /**
275
+ * Creates a Nitro configuration from Avalon plugin config and Nitro-specific options
276
+ *
277
+ * This function merges Avalon's resolved configuration with Nitro-specific options
278
+ * to produce a complete Nitro configuration object. Avalon-specific settings are
279
+ * stored in `runtimeConfig.avalon` for access by handlers.
280
+ *
281
+ * @param avalonNitroConfig - Nitro-specific configuration options
282
+ * @param resolvedAvalonConfig - Resolved Avalon plugin configuration
283
+ * @returns Complete Nitro configuration object
284
+ *
285
+ * @example
286
+ * ```ts
287
+ * const nitroConfig = createNitroConfig(
288
+ * { preset: 'vercel', streaming: true },
289
+ * resolvedAvalonConfig
290
+ * );
291
+ * ```
292
+ */
293
+ export function createNitroConfig(
294
+ avalonNitroConfig: AvalonNitroConfig,
295
+ resolvedAvalonConfig: ResolvedAvalonConfig
296
+ ): NitroConfigOutput {
297
+ // Determine final directory paths (Nitro config overrides Avalon config)
298
+ const pagesDir = avalonNitroConfig.pagesDir ?? resolvedAvalonConfig.pagesDir;
299
+ const layoutsDir = resolvedAvalonConfig.layoutsDir;
300
+
301
+ // Build Avalon runtime config
302
+ const avalonRuntimeConfig: AvalonRuntimeConfig = {
303
+ streaming: avalonNitroConfig.streaming ?? DEFAULT_NITRO_CONFIG.streaming,
304
+ pagesDir,
305
+ layoutsDir,
306
+ };
307
+
308
+ // Validate that runtimeConfig does not contain a 'nitro' key (reserved in v3)
309
+ if (avalonNitroConfig.runtimeConfig && "nitro" in avalonNitroConfig.runtimeConfig) {
310
+ throw new Error(
311
+ 'The "nitro" key in runtimeConfig is reserved by Nitro v3 and cannot be used.'
312
+ );
313
+ }
314
+
315
+ // Merge user runtime config with Avalon runtime config
316
+ const runtimeConfig: NitroConfigOutput["runtimeConfig"] = {
317
+ avalon: avalonRuntimeConfig,
318
+ ...avalonNitroConfig.runtimeConfig,
319
+ };
320
+
321
+ // Merge static assets config with defaults
322
+ const staticAssetsConfig: StaticAssetsConfig = {
323
+ ...DEFAULT_STATIC_ASSETS_CONFIG,
324
+ ...avalonNitroConfig.staticAssets,
325
+ };
326
+
327
+ // Create default route rules for static assets
328
+ const defaultStaticRouteRules = createDefaultStaticAssetRouteRules(staticAssetsConfig);
329
+
330
+ // Merge user route rules with default static asset rules
331
+ const routeRules = mergeRouteRules(
332
+ defaultStaticRouteRules,
333
+ avalonNitroConfig.routeRules ?? {}
334
+ );
335
+
336
+ // Configure public assets directories
337
+ const publicAssets: NitroConfigOutput["publicAssets"] = [
338
+ {
339
+ dir: staticAssetsConfig.publicDir ?? DEFAULT_STATIC_ASSETS_CONFIG.publicDir,
340
+ baseURL: "/",
341
+ maxAge: 0, // Let route rules handle caching
342
+ },
343
+ ];
344
+
345
+ // Resolve renderer: support explicit false to disable, otherwise use default handler
346
+ const renderer: NitroConfigOutput["renderer"] =
347
+ avalonNitroConfig.renderer === false
348
+ ? false
349
+ : (avalonNitroConfig.renderer ?? { handler: "./server/renderer.ts" });
350
+
351
+ return {
352
+ preset: resolvePresetName(avalonNitroConfig.preset ?? DEFAULT_NITRO_CONFIG.preset),
353
+ serverDir: avalonNitroConfig.serverDir ?? DEFAULT_NITRO_CONFIG.serverDir,
354
+ routeRules,
355
+ runtimeConfig,
356
+ publicRuntimeConfig: avalonNitroConfig.publicRuntimeConfig,
357
+ renderer,
358
+ compatibilityDate: avalonNitroConfig.compatibilityDate,
359
+ traceDeps: avalonNitroConfig.traceDeps,
360
+ rolldownConfig: avalonNitroConfig.rolldownConfig,
361
+ serverEntry: avalonNitroConfig.serverEntry,
362
+ compressPublicAssets: avalonNitroConfig.compressPublicAssets,
363
+ publicAssets,
364
+ staticAssets: staticAssetsConfig,
365
+ };
366
+ }
367
+
368
+ /**
369
+ * Creates default route rules for static asset caching
370
+ *
371
+ * @param config - Static assets configuration
372
+ * @returns Route rules for static assets
373
+ */
374
+ export function createDefaultStaticAssetRouteRules(
375
+ config: StaticAssetsConfig
376
+ ): Record<string, RouteRule> {
377
+ const cacheControl = config.cacheControl ?? DEFAULT_STATIC_ASSETS_CONFIG.cacheControl;
378
+ const mutableCacheControl = config.mutableCacheControl ?? DEFAULT_STATIC_ASSETS_CONFIG.mutableCacheControl;
379
+
380
+ return {
381
+ // Immutable assets with hashed filenames (long cache)
382
+ "/assets/**": {
383
+ headers: {
384
+ "Cache-Control": cacheControl,
385
+ ...config.headers,
386
+ },
387
+ },
388
+ "/islands/**": {
389
+ headers: {
390
+ "Cache-Control": cacheControl,
391
+ ...config.headers,
392
+ },
393
+ },
394
+ "/chunks/**": {
395
+ headers: {
396
+ "Cache-Control": cacheControl,
397
+ ...config.headers,
398
+ },
399
+ },
400
+ "/_nuxt/**": {
401
+ headers: {
402
+ "Cache-Control": cacheControl,
403
+ ...config.headers,
404
+ },
405
+ },
406
+ // Font files (long cache)
407
+ "/**/*.woff": {
408
+ headers: {
409
+ "Cache-Control": cacheControl,
410
+ ...config.headers,
411
+ },
412
+ },
413
+ "/**/*.woff2": {
414
+ headers: {
415
+ "Cache-Control": cacheControl,
416
+ ...config.headers,
417
+ },
418
+ },
419
+ // Mutable assets (short cache with revalidation)
420
+ "/**/*.html": {
421
+ headers: {
422
+ "Cache-Control": mutableCacheControl,
423
+ ...config.headers,
424
+ },
425
+ },
426
+ // Favicon (medium cache)
427
+ "/favicon.ico": {
428
+ headers: {
429
+ "Cache-Control": "public, max-age=86400",
430
+ ...config.headers,
431
+ },
432
+ },
433
+ // CSS files in public directory (may be mutable)
434
+ "/**/*.css": {
435
+ headers: {
436
+ "Cache-Control": mutableCacheControl,
437
+ ...config.headers,
438
+ },
439
+ },
440
+ };
441
+ }
442
+
443
+ /**
444
+ * Validates a Nitro preset name against the v3 preset list
445
+ *
446
+ * @param preset - The preset name to validate
447
+ * @returns True if the preset is a known valid v3 preset
448
+ */
449
+ export function isValidPreset(preset: string): boolean {
450
+ return VALID_V3_PRESETS.includes(preset);
451
+ }
452
+
453
+ /**
454
+ * Merges route rules, with later rules taking precedence
455
+ *
456
+ * @param baseRules - Base route rules
457
+ * @param overrideRules - Override route rules
458
+ * @returns Merged route rules
459
+ */
460
+ export function mergeRouteRules(
461
+ baseRules: Record<string, RouteRule>,
462
+ overrideRules: Record<string, RouteRule>
463
+ ): Record<string, RouteRule> {
464
+ const merged: Record<string, RouteRule> = { ...baseRules };
465
+
466
+ for (const [pattern, rule] of Object.entries(overrideRules)) {
467
+ if (merged[pattern]) {
468
+ // Deep merge the rule
469
+ merged[pattern] = {
470
+ ...merged[pattern],
471
+ ...rule,
472
+ headers: {
473
+ ...merged[pattern].headers,
474
+ ...rule.headers,
475
+ },
476
+ };
477
+ } else {
478
+ merged[pattern] = rule;
479
+ }
480
+ }
481
+
482
+ return merged;
483
+ }