@useavalon/avalon 0.1.10 → 0.1.12

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 (250) hide show
  1. package/README.md +54 -54
  2. package/dist/mod.js +1 -0
  3. package/dist/src/build/integration-bundler-plugin.js +1 -0
  4. package/dist/src/build/integration-config.js +1 -0
  5. package/dist/src/build/integration-detection-plugin.js +1 -0
  6. package/dist/src/build/integration-resolver-plugin.js +1 -0
  7. package/dist/src/build/island-manifest.js +1 -0
  8. package/dist/src/build/island-types-generator.js +5 -0
  9. package/dist/src/build/mdx-island-transform.js +2 -0
  10. package/dist/src/build/mdx-plugin.js +1 -0
  11. package/dist/src/build/page-island-transform.js +3 -0
  12. package/dist/src/build/prop-extractors/index.js +1 -0
  13. package/dist/src/build/prop-extractors/lit.js +1 -0
  14. package/dist/src/build/prop-extractors/qwik.js +1 -0
  15. package/dist/src/build/prop-extractors/solid.js +1 -0
  16. package/dist/src/build/prop-extractors/svelte.js +1 -0
  17. package/dist/src/build/prop-extractors/vue.js +1 -0
  18. package/dist/src/build/sidecar-file-manager.js +1 -0
  19. package/dist/src/build/sidecar-renderer.js +6 -0
  20. package/dist/src/client/adapters/index.js +1 -0
  21. package/dist/src/client/components.js +1 -0
  22. package/dist/src/client/css-hmr-handler.js +1 -0
  23. package/dist/src/client/framework-adapter.js +13 -0
  24. package/dist/src/client/hmr-coordinator.js +1 -0
  25. package/dist/src/client/hmr-error-overlay.js +214 -0
  26. package/dist/src/client/main.js +39 -0
  27. package/{src → dist/src}/client/types/framework-runtime.d.ts +68 -68
  28. package/{src → dist/src}/client/types/vite-hmr.d.ts +46 -46
  29. package/dist/src/client/types/vite-virtual-modules.d.ts +70 -0
  30. package/dist/src/components/Image.js +1 -0
  31. package/dist/src/components/IslandErrorBoundary.js +1 -0
  32. package/dist/src/components/LayoutDataErrorBoundary.js +1 -0
  33. package/dist/src/components/LayoutErrorBoundary.js +1 -0
  34. package/dist/src/components/PersistentIsland.js +1 -0
  35. package/dist/src/components/StreamingErrorBoundary.js +1 -0
  36. package/dist/src/components/StreamingLayout.js +29 -0
  37. package/dist/src/core/components/component-analyzer.js +1 -0
  38. package/dist/src/core/components/component-detection.js +5 -0
  39. package/dist/src/core/components/enhanced-framework-detector.js +1 -0
  40. package/dist/src/core/components/framework-registry.js +1 -0
  41. package/dist/src/core/content/mdx-processor.js +1 -0
  42. package/dist/src/core/integrations/index.js +1 -0
  43. package/dist/src/core/integrations/loader.js +1 -0
  44. package/dist/src/core/integrations/registry.js +1 -0
  45. package/dist/src/core/islands/island-persistence.js +1 -0
  46. package/dist/src/core/islands/island-state-serializer.js +1 -0
  47. package/dist/src/core/islands/persistent-island-context.js +1 -0
  48. package/dist/src/core/islands/use-persistent-state.js +1 -0
  49. package/dist/src/core/layout/enhanced-layout-resolver.js +1 -0
  50. package/dist/src/core/layout/layout-cache-manager.js +1 -0
  51. package/dist/src/core/layout/layout-composer.js +1 -0
  52. package/dist/src/core/layout/layout-data-loader.js +1 -0
  53. package/dist/src/core/layout/layout-discovery.js +1 -0
  54. package/dist/src/core/layout/layout-matcher.js +1 -0
  55. package/dist/src/core/layout/layout-types.js +1 -0
  56. package/dist/src/core/modules/framework-module-resolver.js +1 -0
  57. package/dist/src/islands/component-analysis.js +1 -0
  58. package/dist/src/islands/css-utils.js +17 -0
  59. package/dist/src/islands/discovery/index.js +1 -0
  60. package/dist/src/islands/discovery/registry.js +1 -0
  61. package/dist/src/islands/discovery/resolver.js +2 -0
  62. package/dist/src/islands/discovery/scanner.js +1 -0
  63. package/dist/src/islands/discovery/types.js +1 -0
  64. package/dist/src/islands/discovery/validator.js +18 -0
  65. package/dist/src/islands/discovery/watcher.js +1 -0
  66. package/dist/src/islands/framework-detection.js +1 -0
  67. package/dist/src/islands/integration-loader.js +1 -0
  68. package/dist/src/islands/island.js +1 -0
  69. package/dist/src/islands/render-cache.js +1 -0
  70. package/dist/src/islands/types.js +1 -0
  71. package/dist/src/islands/universal-css-collector.js +5 -0
  72. package/dist/src/islands/universal-head-collector.js +2 -0
  73. package/{src → dist/src}/layout-system.d.ts +592 -592
  74. package/dist/src/layout-system.js +1 -0
  75. package/dist/src/middleware/discovery.js +1 -0
  76. package/dist/src/middleware/executor.js +1 -0
  77. package/dist/src/middleware/index.js +1 -0
  78. package/dist/src/middleware/types.js +1 -0
  79. package/dist/src/nitro/build-config.js +1 -0
  80. package/dist/src/nitro/config.js +1 -0
  81. package/dist/src/nitro/error-handler.js +198 -0
  82. package/dist/src/nitro/index.js +1 -0
  83. package/dist/src/nitro/island-manifest.js +2 -0
  84. package/dist/src/nitro/middleware-adapter.js +1 -0
  85. package/dist/src/nitro/renderer.js +183 -0
  86. package/dist/src/nitro/route-discovery.js +1 -0
  87. package/dist/src/nitro/types.js +1 -0
  88. package/dist/src/render/collect-css.js +3 -0
  89. package/{src/render/error-pages.ts → dist/src/render/error-pages.js} +7 -38
  90. package/dist/src/render/isolated-ssr-renderer.js +1 -0
  91. package/dist/src/render/ssr.js +90 -0
  92. package/dist/src/schemas/api.js +1 -0
  93. package/dist/src/schemas/core.js +1 -0
  94. package/dist/src/schemas/index.js +1 -0
  95. package/dist/src/schemas/layout.js +1 -0
  96. package/dist/src/schemas/routing/index.js +1 -0
  97. package/dist/src/schemas/routing.js +1 -0
  98. package/dist/src/types/as-island.js +1 -0
  99. package/{src → dist/src}/types/image.d.ts +106 -106
  100. package/{src → dist/src}/types/index.d.ts +22 -22
  101. package/{src → dist/src}/types/island-jsx.d.ts +33 -33
  102. package/{src → dist/src}/types/island-prop.d.ts +20 -20
  103. package/dist/src/types/layout.js +1 -0
  104. package/{src → dist/src}/types/mdx.d.ts +6 -6
  105. package/dist/src/types/routing.js +1 -0
  106. package/dist/src/types/types.js +1 -0
  107. package/{src → dist/src}/types/urlpattern.d.ts +49 -49
  108. package/{src → dist/src}/types/vite-env.d.ts +11 -11
  109. package/dist/src/utils/dev-logger.js +12 -0
  110. package/dist/src/utils/fs.js +1 -0
  111. package/dist/src/vite-plugin/auto-discover.js +1 -0
  112. package/dist/src/vite-plugin/config.js +1 -0
  113. package/dist/src/vite-plugin/errors.js +1 -0
  114. package/dist/src/vite-plugin/image-optimization.js +45 -0
  115. package/dist/src/vite-plugin/integration-activator.js +1 -0
  116. package/dist/src/vite-plugin/island-sidecar-plugin.js +1 -0
  117. package/dist/src/vite-plugin/module-discovery.js +1 -0
  118. package/dist/src/vite-plugin/nitro-integration.js +42 -0
  119. package/dist/src/vite-plugin/plugin.js +1 -0
  120. package/dist/src/vite-plugin/types.js +1 -0
  121. package/dist/src/vite-plugin/validation.js +2 -0
  122. package/package.json +57 -26
  123. package/mod.ts +0 -302
  124. package/src/build/integration-bundler-plugin.ts +0 -116
  125. package/src/build/integration-config.ts +0 -168
  126. package/src/build/integration-detection-plugin.ts +0 -117
  127. package/src/build/integration-resolver-plugin.ts +0 -90
  128. package/src/build/island-manifest.ts +0 -269
  129. package/src/build/island-types-generator.ts +0 -476
  130. package/src/build/mdx-island-transform.ts +0 -464
  131. package/src/build/mdx-plugin.ts +0 -98
  132. package/src/build/page-island-transform.ts +0 -598
  133. package/src/build/prop-extractors/index.ts +0 -21
  134. package/src/build/prop-extractors/lit.ts +0 -140
  135. package/src/build/prop-extractors/qwik.ts +0 -16
  136. package/src/build/prop-extractors/solid.ts +0 -125
  137. package/src/build/prop-extractors/svelte.ts +0 -194
  138. package/src/build/prop-extractors/vue.ts +0 -111
  139. package/src/build/sidecar-file-manager.ts +0 -104
  140. package/src/build/sidecar-renderer.ts +0 -30
  141. package/src/client/adapters/index.js +0 -12
  142. package/src/client/adapters/index.ts +0 -13
  143. package/src/client/adapters/lit-adapter.js +0 -467
  144. package/src/client/adapters/lit-adapter.ts +0 -654
  145. package/src/client/adapters/preact-adapter.js +0 -223
  146. package/src/client/adapters/preact-adapter.ts +0 -331
  147. package/src/client/adapters/qwik-adapter.js +0 -259
  148. package/src/client/adapters/qwik-adapter.ts +0 -345
  149. package/src/client/adapters/react-adapter.js +0 -220
  150. package/src/client/adapters/react-adapter.ts +0 -353
  151. package/src/client/adapters/solid-adapter.js +0 -295
  152. package/src/client/adapters/solid-adapter.ts +0 -451
  153. package/src/client/adapters/svelte-adapter.js +0 -368
  154. package/src/client/adapters/svelte-adapter.ts +0 -524
  155. package/src/client/adapters/vue-adapter.js +0 -278
  156. package/src/client/adapters/vue-adapter.ts +0 -467
  157. package/src/client/components.js +0 -23
  158. package/src/client/components.ts +0 -35
  159. package/src/client/css-hmr-handler.js +0 -263
  160. package/src/client/css-hmr-handler.ts +0 -344
  161. package/src/client/framework-adapter.js +0 -283
  162. package/src/client/framework-adapter.ts +0 -462
  163. package/src/client/hmr-coordinator.js +0 -274
  164. package/src/client/hmr-coordinator.ts +0 -396
  165. package/src/client/hmr-error-overlay.js +0 -533
  166. package/src/client/main.js +0 -816
  167. package/src/client/types/vite-virtual-modules.d.ts +0 -60
  168. package/src/components/Image.tsx +0 -123
  169. package/src/components/IslandErrorBoundary.tsx +0 -145
  170. package/src/components/LayoutDataErrorBoundary.tsx +0 -141
  171. package/src/components/LayoutErrorBoundary.tsx +0 -127
  172. package/src/components/PersistentIsland.tsx +0 -52
  173. package/src/components/StreamingErrorBoundary.tsx +0 -233
  174. package/src/components/StreamingLayout.tsx +0 -538
  175. package/src/core/components/component-analyzer.ts +0 -192
  176. package/src/core/components/component-detection.ts +0 -508
  177. package/src/core/components/enhanced-framework-detector.ts +0 -500
  178. package/src/core/components/framework-registry.ts +0 -563
  179. package/src/core/content/mdx-processor.ts +0 -46
  180. package/src/core/integrations/index.ts +0 -19
  181. package/src/core/integrations/loader.ts +0 -125
  182. package/src/core/integrations/registry.ts +0 -175
  183. package/src/core/islands/island-persistence.ts +0 -325
  184. package/src/core/islands/island-state-serializer.ts +0 -258
  185. package/src/core/islands/persistent-island-context.tsx +0 -80
  186. package/src/core/islands/use-persistent-state.ts +0 -68
  187. package/src/core/layout/enhanced-layout-resolver.ts +0 -322
  188. package/src/core/layout/layout-cache-manager.ts +0 -485
  189. package/src/core/layout/layout-composer.ts +0 -357
  190. package/src/core/layout/layout-data-loader.ts +0 -516
  191. package/src/core/layout/layout-discovery.ts +0 -243
  192. package/src/core/layout/layout-matcher.ts +0 -299
  193. package/src/core/layout/layout-types.ts +0 -110
  194. package/src/core/modules/framework-module-resolver.ts +0 -273
  195. package/src/islands/component-analysis.ts +0 -213
  196. package/src/islands/css-utils.ts +0 -565
  197. package/src/islands/discovery/index.ts +0 -80
  198. package/src/islands/discovery/registry.ts +0 -340
  199. package/src/islands/discovery/resolver.ts +0 -477
  200. package/src/islands/discovery/scanner.ts +0 -386
  201. package/src/islands/discovery/types.ts +0 -117
  202. package/src/islands/discovery/validator.ts +0 -544
  203. package/src/islands/discovery/watcher.ts +0 -368
  204. package/src/islands/framework-detection.ts +0 -428
  205. package/src/islands/integration-loader.ts +0 -490
  206. package/src/islands/island.tsx +0 -565
  207. package/src/islands/render-cache.ts +0 -550
  208. package/src/islands/types.ts +0 -80
  209. package/src/islands/universal-css-collector.ts +0 -157
  210. package/src/islands/universal-head-collector.ts +0 -137
  211. package/src/layout-system.ts +0 -218
  212. package/src/middleware/discovery.ts +0 -268
  213. package/src/middleware/executor.ts +0 -315
  214. package/src/middleware/index.ts +0 -76
  215. package/src/middleware/types.ts +0 -99
  216. package/src/nitro/build-config.ts +0 -576
  217. package/src/nitro/config.ts +0 -483
  218. package/src/nitro/error-handler.ts +0 -636
  219. package/src/nitro/index.ts +0 -173
  220. package/src/nitro/island-manifest.ts +0 -584
  221. package/src/nitro/middleware-adapter.ts +0 -260
  222. package/src/nitro/renderer.ts +0 -1471
  223. package/src/nitro/route-discovery.ts +0 -439
  224. package/src/nitro/types.ts +0 -321
  225. package/src/render/collect-css.ts +0 -198
  226. package/src/render/isolated-ssr-renderer.ts +0 -654
  227. package/src/render/ssr.ts +0 -1030
  228. package/src/schemas/api.ts +0 -30
  229. package/src/schemas/core.ts +0 -64
  230. package/src/schemas/index.ts +0 -212
  231. package/src/schemas/layout.ts +0 -279
  232. package/src/schemas/routing/index.ts +0 -38
  233. package/src/schemas/routing.ts +0 -376
  234. package/src/types/as-island.ts +0 -20
  235. package/src/types/layout.ts +0 -285
  236. package/src/types/routing.ts +0 -555
  237. package/src/types/types.ts +0 -5
  238. package/src/utils/dev-logger.ts +0 -299
  239. package/src/utils/fs.ts +0 -151
  240. package/src/vite-plugin/auto-discover.ts +0 -551
  241. package/src/vite-plugin/config.ts +0 -266
  242. package/src/vite-plugin/errors.ts +0 -127
  243. package/src/vite-plugin/image-optimization.ts +0 -156
  244. package/src/vite-plugin/integration-activator.ts +0 -126
  245. package/src/vite-plugin/island-sidecar-plugin.ts +0 -176
  246. package/src/vite-plugin/module-discovery.ts +0 -189
  247. package/src/vite-plugin/nitro-integration.ts +0 -1354
  248. package/src/vite-plugin/plugin.ts +0 -401
  249. package/src/vite-plugin/types.ts +0 -327
  250. package/src/vite-plugin/validation.ts +0 -228
@@ -1,268 +0,0 @@
1
- /**
2
- * Simplified Middleware Discovery
3
- *
4
- * This module provides a streamlined middleware discovery system that scans
5
- * for _middleware.ts files in src/pages/ directories.
6
- *
7
- * Key features:
8
- * - Scans src/pages/ for page middleware
9
- * - Calculates priority based on directory depth (parent before child)
10
- * - Creates URL patterns for route matching
11
- *
12
- * Note: API middleware is handled natively by Nitro and is not scanned here.
13
- *
14
- * Requirements: 3.1, 3.2, 3.3, 3.4
15
- */
16
-
17
- import { join, relative, resolve } from 'node:path';
18
- import { stat as fsStat, readdir } from 'node:fs/promises';
19
- import type { MiddlewareRoute, MiddlewareDiscoveryOptions } from './types.ts';
20
-
21
- /**
22
- * Default options for middleware discovery
23
- */
24
- const DEFAULT_OPTIONS: Required<Omit<MiddlewareDiscoveryOptions, 'baseDir'>> = {
25
- filePattern: '_middleware.ts',
26
- excludeDirs: ['node_modules', '.git', 'dist', '.output', '.vite'],
27
- devMode: false,
28
- };
29
-
30
- /**
31
- * Base priority values for different middleware types
32
- * Lower values execute first
33
- */
34
- const PRIORITY_BASE = {
35
- /** Page middleware base priority */
36
- pages: 50,
37
- } as const;
38
-
39
- /**
40
- * Discovers route-scoped middleware files in the project
41
- *
42
- * Scans for _middleware.ts files in:
43
- * - src/pages/ directory (and subdirectories) for page routes
44
- *
45
- * API middleware is handled natively by Nitro and is not scanned here.
46
- *
47
- * Middleware files are sorted by priority (directory depth), ensuring
48
- * parent middleware executes before child middleware.
49
- *
50
- * @param options - Discovery options
51
- * @returns Array of discovered middleware routes, sorted by priority
52
- *
53
- * @example
54
- * ```ts
55
- * const routes = await discoverScopedMiddleware({
56
- * baseDir: 'src',
57
- * devMode: true,
58
- * });
59
- *
60
- * // Returns routes like:
61
- * // [
62
- * // { pattern: URLPattern('/blog/*'), filePath: 'src/pages/blog/_middleware.ts', priority: 51, type: 'pages' },
63
- * // ]
64
- * ```
65
- */
66
- export async function discoverScopedMiddleware(
67
- options: MiddlewareDiscoveryOptions
68
- ): Promise<MiddlewareRoute[]> {
69
- const {
70
- baseDir,
71
- filePattern = DEFAULT_OPTIONS.filePattern,
72
- excludeDirs = DEFAULT_OPTIONS.excludeDirs,
73
- devMode = DEFAULT_OPTIONS.devMode,
74
- } = options;
75
-
76
- const resolvedBaseDir = resolve(baseDir);
77
- const routes: MiddlewareRoute[] = [];
78
-
79
- // Scan pages directory for page middleware
80
- const pagesDir = join(resolvedBaseDir, 'pages');
81
- await scanDirectory(pagesDir, 'pages', filePattern, excludeDirs, routes, devMode);
82
-
83
- // Sort by priority (lower numbers execute first)
84
- routes.sort((a, b) => a.priority - b.priority);
85
-
86
- if (devMode && routes.length > 0) {
87
- console.log(`[middleware] Discovered ${routes.length} route-scoped middleware:`);
88
- for (const route of routes) {
89
- console.log(` - ${route.type}: ${route.filePath} (priority: ${route.priority})`);
90
- }
91
- }
92
-
93
- return routes;
94
- }
95
-
96
- /**
97
- * Scans a directory recursively for middleware files
98
- *
99
- * @param dir - Directory to scan
100
- * @param type - Middleware type ('pages')
101
- * @param filePattern - File pattern to match
102
- * @param excludeDirs - Directories to exclude
103
- * @param routes - Array to collect discovered routes
104
- * @param devMode - Whether to log debug information
105
- */
106
- async function scanDirectory(
107
- dir: string,
108
- type: 'pages',
109
- filePattern: string,
110
- excludeDirs: string[],
111
- routes: MiddlewareRoute[],
112
- devMode: boolean
113
- ): Promise<void> {
114
- try {
115
- // Check if directory exists
116
- const dirInfo = await fsStat(dir).catch(() => null);
117
- if (!dirInfo?.isDirectory()) {
118
- return;
119
- }
120
-
121
- // Recursively scan directory
122
- await scanDirectoryRecursive(dir, dir, type, filePattern, excludeDirs, routes, devMode);
123
- } catch (error) {
124
- // Directory doesn't exist or can't be read - skip silently
125
- if (devMode && (!(error instanceof Error) || (error as NodeJS.ErrnoException).code !== 'ENOENT')) {
126
- console.warn(`[middleware] Error scanning ${dir}: ${error instanceof Error ? error.message : String(error)}`);
127
- }
128
- }
129
- }
130
-
131
- /**
132
- * Recursively scans a directory for middleware files
133
- *
134
- * @param rootDir - Root directory being scanned
135
- * @param currentDir - Current directory being scanned
136
- * @param type - Middleware type
137
- * @param filePattern - File pattern to match
138
- * @param excludeDirs - Directories to exclude
139
- * @param routes - Array to collect discovered routes
140
- * @param devMode - Whether to log debug information
141
- */
142
- async function scanDirectoryRecursive(
143
- rootDir: string,
144
- currentDir: string,
145
- type: 'pages',
146
- filePattern: string,
147
- excludeDirs: string[],
148
- routes: MiddlewareRoute[],
149
- devMode: boolean
150
- ): Promise<void> {
151
- try {
152
- const entries = await readdir(currentDir, { withFileTypes: true });
153
- for (const entry of entries) {
154
- const fullPath = join(currentDir, entry.name);
155
-
156
- // Skip excluded directories
157
- if (entry.isDirectory() && excludeDirs.includes(entry.name)) {
158
- continue;
159
- }
160
-
161
- if (entry.isDirectory()) {
162
- // Recursively scan subdirectories
163
- await scanDirectoryRecursive(rootDir, fullPath, type, filePattern, excludeDirs, routes, devMode);
164
- } else if (entry.name === filePattern) {
165
- // Found a middleware file
166
- const relativePath = relative(rootDir, currentDir);
167
- const route = createMiddlewareRoute(fullPath, relativePath, type);
168
- routes.push(route);
169
- }
170
- }
171
- } catch (error) {
172
- if (devMode && (!(error instanceof Error) || (error as NodeJS.ErrnoException).code !== 'ENOENT')) {
173
- console.warn(`[middleware] Error reading ${currentDir}: ${error instanceof Error ? error.message : String(error)}`);
174
- }
175
- }
176
- }
177
-
178
- /**
179
- * Creates a middleware route configuration from a file path
180
- *
181
- * @param filePath - Absolute path to the middleware file
182
- * @param relativePath - Path relative to the pages directory
183
- * @param type - Middleware type
184
- * @returns Middleware route configuration
185
- *
186
- * @example
187
- * ```ts
188
- * // For src/pages/blog/_middleware.ts
189
- * createMiddlewareRoute('/abs/path/src/pages/blog/_middleware.ts', 'blog', 'pages')
190
- * // Returns: { pattern: URLPattern('/blog/*'), filePath: '...', priority: 51, type: 'pages' }
191
- * ```
192
- */
193
- function createMiddlewareRoute(
194
- filePath: string,
195
- relativePath: string,
196
- type: 'pages'
197
- ): MiddlewareRoute {
198
- // Calculate URL pattern from relative path
199
- // relativePath is the directory path relative to pages/
200
- // e.g., 'blog' for src/pages/blog/_middleware.ts
201
- // e.g., '' for src/pages/_middleware.ts (root middleware)
202
-
203
- // Page middleware: /* or /blog{/*}?
204
- // Use {/*}? to match both /blog and /blog/anything
205
- const urlPattern = relativePath ? `/${relativePath}{/*}?` : '/*';
206
-
207
- // Calculate priority based on directory depth
208
- // Shallower directories have lower priority (execute first)
209
- const depth = relativePath ? relativePath.split('/').filter(Boolean).length : 0;
210
- const priority = PRIORITY_BASE[type] + depth;
211
-
212
- return {
213
- pattern: new URLPattern({ pathname: urlPattern }),
214
- filePath,
215
- priority,
216
- type,
217
- };
218
- }
219
-
220
- /**
221
- * Gets middleware routes that match a given URL
222
- *
223
- * Filters discovered routes to only include those that match the URL,
224
- * respecting type isolation (page middleware doesn't run for API routes
225
- * and vice versa).
226
- *
227
- * @param routes - All discovered middleware routes
228
- * @param url - URL to match against
229
- * @returns Matching routes, sorted by priority
230
- *
231
- * @example
232
- * ```ts
233
- * const allRoutes = await discoverScopedMiddleware({ baseDir: 'src' });
234
- * const matchingRoutes = getMatchingMiddleware(allRoutes, new URL('http://localhost/blog/post-1'));
235
- * ```
236
- */
237
- export function getMatchingMiddleware(
238
- routes: MiddlewareRoute[],
239
- url: URL
240
- ): MiddlewareRoute[] {
241
- const isApiRoute = url.pathname.startsWith('/api');
242
-
243
- return routes.filter(route => {
244
- // Check if pattern matches the URL
245
- if (!route.pattern.test(url)) {
246
- return false;
247
- }
248
-
249
- // Type isolation: page middleware doesn't run for API routes
250
- if (route.type === 'pages' && isApiRoute) {
251
- return false;
252
- }
253
-
254
- return true;
255
- });
256
- }
257
-
258
- /**
259
- * Clears the middleware discovery cache
260
- *
261
- * This is a no-op in the simplified discovery module since we don't
262
- * maintain a persistent cache. The function is provided for API
263
- * compatibility with the executor module.
264
- */
265
- export function clearDiscoveryCache(): void {
266
- // No-op: simplified discovery doesn't maintain a persistent cache
267
- // Each call to discoverScopedMiddleware scans the file system
268
- }
@@ -1,315 +0,0 @@
1
- /**
2
- * Simplified Middleware Executor
3
- *
4
- * This module provides a streamlined middleware execution system that aligns
5
- * with Nitro's conventions while supporting Avalon's route-scoped middleware.
6
- *
7
- * Key features:
8
- * - Nitro-style handler signature: return void to continue, return Response to terminate
9
- * - Middleware caching for performance
10
-
11
- * - Error propagation to Nitro's error handling
12
- * - Context preservation: event.context modifications persist through the chain
13
- * - Development logging for context changes
14
- *
15
- * Requirements: 1.2, 1.3, 1.4, 2.1, 2.2
16
- */
17
-
18
- import type { H3Event } from 'h3';
19
- import type { MiddlewareHandler, MiddlewareRoute, MiddlewareFileExport, MiddlewareExecutorOptions } from './types.ts';
20
- import { getMatchingMiddleware } from './discovery.ts';
21
-
22
- /**
23
- * Captures a snapshot of the event context for comparison
24
- * Used in development mode to log context changes
25
- */
26
- function captureContextSnapshot(context: Record<string, unknown>): Map<string, unknown> {
27
- const snapshot = new Map<string, unknown>();
28
- for (const key of Object.keys(context)) {
29
- snapshot.set(key, context[key]);
30
- }
31
- return snapshot;
32
- }
33
-
34
- /**
35
- * Compares two context snapshots and returns the changes
36
- * Used in development mode to log context modifications
37
- */
38
- function getContextChanges(
39
- before: Map<string, unknown>,
40
- after: Record<string, unknown>,
41
- ): { added: string[]; modified: string[]; removed: string[] } {
42
- const added: string[] = [];
43
- const modified: string[] = [];
44
- const removed: string[] = [];
45
-
46
- for (const key of Object.keys(after)) {
47
- if (!before.has(key)) {
48
- added.push(key);
49
- } else if (before.get(key) !== after[key]) {
50
- modified.push(key);
51
- }
52
- }
53
-
54
- for (const key of before.keys()) {
55
- if (!(key in after)) {
56
- removed.push(key);
57
- }
58
- }
59
-
60
- return { added, modified, removed };
61
- }
62
-
63
- /**
64
- * Logs context changes in development mode
65
- * Requirements: 2.1, 2.2
66
- *
67
- * Note: Intentionally minimal to reduce log noise.
68
- * Uncomment the body for debugging middleware context issues.
69
- */
70
- function logContextChanges(
71
- _filePath: string,
72
- _changes: { added: string[]; modified: string[]; removed: string[] },
73
- ): void {
74
- // Uncomment for debugging middleware context issues:
75
- // const hasChanges = _changes.added.length > 0 || _changes.modified.length > 0 || _changes.removed.length > 0;
76
- // if (!hasChanges) return;
77
- // console.log(`[middleware] Context changes in ${_filePath}: ...`);
78
- }
79
-
80
- /** Default executor options */
81
- const DEFAULT_OPTIONS: Required<MiddlewareExecutorOptions> = {
82
- devMode: false,
83
- timeout: 30000,
84
- };
85
-
86
- /** Middleware cache: maps file paths to loaded handlers */
87
- const middlewareCache = new Map<string, MiddlewareHandler>();
88
-
89
- /**
90
- * Executes a single middleware route handler with context tracking.
91
- * Returns the Response if the chain should terminate, undefined to continue.
92
- */
93
- async function executeMiddlewareRoute(
94
- event: H3Event,
95
- route: MiddlewareRoute,
96
- devMode: boolean,
97
- timeout: number,
98
- ): Promise<Response | undefined> {
99
- const handler = await loadMiddleware(route.filePath, devMode);
100
- if (!handler) return undefined;
101
-
102
- const contextBefore = devMode ? captureContextSnapshot(event.context) : null;
103
-
104
- const result = await executeWithTimeout(() => handler(event), timeout, route.filePath);
105
-
106
- if (devMode && contextBefore) {
107
- const changes = getContextChanges(contextBefore, event.context);
108
- logContextChanges(route.filePath, changes);
109
- }
110
-
111
- const response = handleMiddlewareResult(result, route.filePath, devMode);
112
- if (response && devMode) {
113
- console.log(`[middleware] Chain terminated by ${route.filePath}`);
114
- }
115
- return response;
116
- }
117
-
118
- /**
119
- * Executes route-scoped middleware chain for a request
120
- *
121
- * 1. Finds middleware that matches the request URL
122
- * 2. Executes them in priority order (parent before child)
123
- * 3. Handles void return (continue) and Response return (terminate)
124
- * 4. Propagates errors to Nitro's error handling
125
- * 5. Preserves event.context modifications across the chain
126
- * 6. Logs context changes in development mode
127
- *
128
- * @param event - H3 event object from Nitro
129
- * @param routes - Discovered middleware routes from discoverScopedMiddleware
130
- * @param options - Execution options
131
- * @returns Response if middleware terminated the chain, undefined otherwise
132
- */
133
- export async function executeScopedMiddleware(
134
- event: H3Event,
135
- routes: MiddlewareRoute[],
136
- options: MiddlewareExecutorOptions = {},
137
- ): Promise<Response | undefined> {
138
- const { devMode, timeout } = { ...DEFAULT_OPTIONS, ...options };
139
-
140
- const url = buildUrlFromEvent(event);
141
- const matchingRoutes = getMatchingMiddleware(routes, url);
142
-
143
- if (matchingRoutes.length === 0) {
144
- return undefined;
145
- }
146
-
147
- for (const route of matchingRoutes) {
148
- try {
149
- const response = await executeMiddlewareRoute(event, route, devMode, timeout);
150
- if (response) return response;
151
- } catch (error) {
152
- if (devMode) {
153
- console.error(`[middleware] Error in ${route.filePath}:`, error);
154
- }
155
- throw error;
156
- }
157
- }
158
-
159
- return undefined;
160
- }
161
-
162
- /**
163
- * Builds a URL object from an H3 event.
164
- * Supports both h3 v2 (event.url / event.req.url) and the dev-mode mock shape (event.path).
165
- */
166
- function buildUrlFromEvent(event: H3Event): URL {
167
- const base = 'http://localhost';
168
-
169
- // h3 v2: event.url is a string
170
- if (typeof (event as any).url === 'string') {
171
- return new URL((event as any).url, base);
172
- }
173
-
174
- // h3 v2: event.req is a Web Request
175
- if ((event as any).req?.url) {
176
- return new URL((event as any).req.url, base);
177
- }
178
-
179
- // Dev-mode mock: event.path
180
- if ((event as any).path) {
181
- return new URL((event as any).path, base);
182
- }
183
-
184
- // Legacy h3 v1: event.node.req.url
185
- if ((event as any).node?.req?.url) {
186
- return new URL((event as any).node.req.url, base);
187
- }
188
-
189
- return new URL('/', base);
190
- }
191
-
192
- /**
193
- * Converts an absolute file path to a valid ESM import specifier.
194
- * Windows absolute paths (C:\...) are converted to file:// URLs.
195
- * On Unix, the path is returned as-is (no-op).
196
- */
197
- export function toImportSpecifier(filePath: string): string {
198
- if (/^[A-Za-z]:[\\/]/.test(filePath)) {
199
- return `file:///${filePath.replaceAll('\\', '/')}`;
200
- }
201
- return filePath;
202
- }
203
-
204
- /**
205
- * Loads a middleware handler via Vite's ssrLoadModule (dev) or dynamic import (prod/worker).
206
- */
207
- async function loadMiddleware(filePath: string, devMode: boolean): Promise<MiddlewareHandler | null> {
208
- if (!devMode && middlewareCache.has(filePath)) {
209
- return middlewareCache.get(filePath)!;
210
- }
211
-
212
- try {
213
- let module: MiddlewareFileExport;
214
-
215
- const viteServer = globalThis.__viteDevServer;
216
- if (devMode && viteServer) {
217
- const viteRoot = viteServer.config.root || '';
218
- const vitePath = filePath.startsWith(viteRoot) ? '/' + filePath.slice(viteRoot.length + 1) : filePath;
219
- module = (await viteServer.ssrLoadModule(vitePath)) as MiddlewareFileExport;
220
- } else {
221
- const importPath = toImportSpecifier(filePath);
222
- module = (await import(/* @vite-ignore */ importPath)) as MiddlewareFileExport;
223
- }
224
-
225
- if (!module.default || typeof module.default !== 'function') {
226
- if (devMode) {
227
- console.warn(`[middleware] ${filePath} does not export a default function`);
228
- }
229
- return null;
230
- }
231
-
232
- if (!devMode) {
233
- middlewareCache.set(filePath, module.default);
234
- }
235
-
236
- return module.default;
237
- } catch (error) {
238
- if (devMode) {
239
- console.error(`[middleware] Failed to load ${filePath}:`, error);
240
- }
241
- return null;
242
- }
243
- }
244
-
245
- /**
246
- * Executes a middleware handler with a timeout
247
- */
248
- async function executeWithTimeout<T>(fn: () => T | Promise<T>, timeout: number, filePath: string): Promise<T> {
249
- return Promise.race([
250
- Promise.resolve(fn()),
251
- new Promise<never>((_, reject) => {
252
- setTimeout(() => {
253
- reject(new Error(`Middleware timeout after ${timeout}ms: ${filePath}`));
254
- }, timeout);
255
- }),
256
- ]);
257
- }
258
-
259
- /**
260
- * Handles the result of a middleware execution.
261
- * Supports Nitro-style (void/Response) format.
262
- */
263
- function handleMiddlewareResult(result: unknown, filePath: string, devMode: boolean): Response | undefined {
264
- if (result === undefined || result === null) {
265
- return undefined;
266
- }
267
-
268
- if (result instanceof Response) {
269
- return result;
270
- }
271
-
272
- if (devMode) {
273
- console.warn(
274
- `[middleware] ${filePath} returned unexpected value: ${typeof result}. ` +
275
- `Expected void or Response.`,
276
- );
277
- }
278
-
279
- return undefined;
280
- }
281
-
282
- /** Clears the middleware cache (e.g. during hot reload) */
283
- export function clearMiddlewareCache(): void {
284
- middlewareCache.clear();
285
- }
286
-
287
- /** Removes a specific middleware from the cache */
288
- export function invalidateMiddleware(filePath: string): boolean {
289
- return middlewareCache.delete(filePath);
290
- }
291
-
292
- /** Gets the current size of the middleware cache */
293
- export function getMiddlewareCacheSize(): number {
294
- return middlewareCache.size;
295
- }
296
-
297
- /** Checks if a key exists in event.context */
298
- export function hasContextValue(event: H3Event, key: string): boolean {
299
- return key in event.context;
300
- }
301
-
302
- /** Gets a typed value from event.context */
303
- export function getContextValue<T>(event: H3Event, key: string): T | undefined {
304
- return event.context[key] as T | undefined;
305
- }
306
-
307
- /** Sets a value in event.context with optional development logging */
308
- export function setContextValue<T>(event: H3Event, key: string, value: T, devMode: boolean = false): void {
309
- const isNew = !(key in event.context);
310
- event.context[key] = value;
311
-
312
- if (devMode) {
313
- console.log(`[middleware] Context ${isNew ? 'set' : 'updated'}: ${key}`);
314
- }
315
- }
@@ -1,76 +0,0 @@
1
- /**
2
- * Avalon Middleware Module
3
- *
4
- * This module provides a Nitro-aligned middleware system for Avalon that supports
5
- * both global middleware (via Nitro's middleware/ directory) and route-scoped
6
- * middleware (via _middleware.ts files in page/API directories).
7
- *
8
- * Key features:
9
- * - Nitro-compatible handler signature: return void to continue, return Response to terminate
10
- * - Route-scoped middleware discovery in src/pages/ and src/api/
11
- * - Priority-based execution order (parent before child)
12
- * - Type-safe context augmentation for H3 events
13
- *
14
- * @example
15
- * ```ts
16
- * // Define middleware with proper typing
17
- * import { defineMiddleware } from 'avalon/middleware';
18
- * import { getHeader, createError } from 'h3';
19
- *
20
- * export default defineMiddleware(async (event) => {
21
- * const token = getHeader(event, 'Authorization');
22
- * if (!token) {
23
- * throw createError({ statusCode: 401, message: 'Unauthorized' });
24
- * }
25
- * event.context.user = await validateToken(token);
26
- * });
27
- * ```
28
- *
29
- * @example
30
- * ```ts
31
- * // Discover and execute middleware in your server
32
- * import { discoverScopedMiddleware, executeScopedMiddleware } from 'avalon/middleware';
33
- *
34
- * const routes = await discoverScopedMiddleware({ baseDir: 'src', devMode: true });
35
- * const response = await executeScopedMiddleware(event, routes);
36
- * if (response) return response;
37
- * ```
38
- *
39
- * Requirements: 4.3
40
- */
41
-
42
- // =============================================================================
43
- // Type Exports
44
- // =============================================================================
45
-
46
- export type {
47
- MiddlewareHandler,
48
- MiddlewareFileExport,
49
- MiddlewareRoute,
50
- MiddlewareDiscoveryOptions,
51
- MiddlewareExecutorOptions,
52
- } from './types.ts';
53
-
54
- // =============================================================================
55
- // Helper Functions (defineMiddleware removed — use defineHandler from 'nitro/h3')
56
- // =============================================================================
57
-
58
- // =============================================================================
59
- // Discovery Functions
60
- // =============================================================================
61
-
62
- export { discoverScopedMiddleware, getMatchingMiddleware, clearDiscoveryCache } from './discovery.ts';
63
-
64
- // =============================================================================
65
- // Executor Functions
66
- // =============================================================================
67
-
68
- export {
69
- executeScopedMiddleware,
70
- clearMiddlewareCache,
71
- invalidateMiddleware,
72
- getMiddlewareCacheSize,
73
- hasContextValue,
74
- getContextValue,
75
- setContextValue,
76
- } from './executor.ts';