@useavalon/avalon 0.1.12 → 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 (230) hide show
  1. package/mod.ts +302 -0
  2. package/package.json +9 -17
  3. package/src/build/integration-bundler-plugin.ts +116 -0
  4. package/src/build/integration-config.ts +168 -0
  5. package/src/build/integration-detection-plugin.ts +117 -0
  6. package/src/build/integration-resolver-plugin.ts +90 -0
  7. package/src/build/island-manifest.ts +269 -0
  8. package/src/build/island-types-generator.ts +476 -0
  9. package/src/build/mdx-island-transform.ts +464 -0
  10. package/src/build/mdx-plugin.ts +98 -0
  11. package/src/build/page-island-transform.ts +598 -0
  12. package/src/build/prop-extractors/index.ts +21 -0
  13. package/src/build/prop-extractors/lit.ts +140 -0
  14. package/src/build/prop-extractors/qwik.ts +16 -0
  15. package/src/build/prop-extractors/solid.ts +125 -0
  16. package/src/build/prop-extractors/svelte.ts +194 -0
  17. package/src/build/prop-extractors/vue.ts +111 -0
  18. package/src/build/sidecar-file-manager.ts +104 -0
  19. package/src/build/sidecar-renderer.ts +30 -0
  20. package/src/client/adapters/index.ts +21 -0
  21. package/src/client/components.ts +35 -0
  22. package/src/client/css-hmr-handler.ts +344 -0
  23. package/src/client/framework-adapter.ts +462 -0
  24. package/src/client/hmr-coordinator.ts +396 -0
  25. package/src/client/hmr-error-overlay.js +533 -0
  26. package/src/client/main.js +824 -0
  27. package/src/components/Image.tsx +123 -0
  28. package/src/components/IslandErrorBoundary.tsx +145 -0
  29. package/src/components/LayoutDataErrorBoundary.tsx +141 -0
  30. package/src/components/LayoutErrorBoundary.tsx +127 -0
  31. package/src/components/PersistentIsland.tsx +52 -0
  32. package/src/components/StreamingErrorBoundary.tsx +233 -0
  33. package/src/components/StreamingLayout.tsx +538 -0
  34. package/src/core/components/component-analyzer.ts +192 -0
  35. package/src/core/components/component-detection.ts +508 -0
  36. package/src/core/components/enhanced-framework-detector.ts +500 -0
  37. package/src/core/components/framework-registry.ts +563 -0
  38. package/src/core/content/mdx-processor.ts +46 -0
  39. package/src/core/integrations/index.ts +19 -0
  40. package/src/core/integrations/loader.ts +125 -0
  41. package/src/core/integrations/registry.ts +175 -0
  42. package/src/core/islands/island-persistence.ts +325 -0
  43. package/src/core/islands/island-state-serializer.ts +258 -0
  44. package/src/core/islands/persistent-island-context.tsx +80 -0
  45. package/src/core/islands/use-persistent-state.ts +68 -0
  46. package/src/core/layout/enhanced-layout-resolver.ts +322 -0
  47. package/src/core/layout/layout-cache-manager.ts +485 -0
  48. package/src/core/layout/layout-composer.ts +357 -0
  49. package/src/core/layout/layout-data-loader.ts +516 -0
  50. package/src/core/layout/layout-discovery.ts +243 -0
  51. package/src/core/layout/layout-matcher.ts +299 -0
  52. package/src/core/layout/layout-types.ts +110 -0
  53. package/src/core/modules/framework-module-resolver.ts +273 -0
  54. package/src/islands/component-analysis.ts +213 -0
  55. package/src/islands/css-utils.ts +565 -0
  56. package/src/islands/discovery/index.ts +80 -0
  57. package/src/islands/discovery/registry.ts +340 -0
  58. package/src/islands/discovery/resolver.ts +477 -0
  59. package/src/islands/discovery/scanner.ts +386 -0
  60. package/src/islands/discovery/types.ts +117 -0
  61. package/src/islands/discovery/validator.ts +544 -0
  62. package/src/islands/discovery/watcher.ts +368 -0
  63. package/src/islands/framework-detection.ts +428 -0
  64. package/src/islands/integration-loader.ts +490 -0
  65. package/src/islands/island.tsx +565 -0
  66. package/src/islands/render-cache.ts +550 -0
  67. package/src/islands/types.ts +80 -0
  68. package/src/islands/universal-css-collector.ts +157 -0
  69. package/src/islands/universal-head-collector.ts +137 -0
  70. package/src/layout-system.ts +218 -0
  71. package/src/middleware/discovery.ts +268 -0
  72. package/src/middleware/executor.ts +315 -0
  73. package/src/middleware/index.ts +76 -0
  74. package/src/middleware/types.ts +99 -0
  75. package/src/nitro/build-config.ts +576 -0
  76. package/src/nitro/config.ts +483 -0
  77. package/src/nitro/error-handler.ts +636 -0
  78. package/src/nitro/index.ts +173 -0
  79. package/src/nitro/island-manifest.ts +584 -0
  80. package/src/nitro/middleware-adapter.ts +260 -0
  81. package/src/nitro/renderer.ts +1471 -0
  82. package/src/nitro/route-discovery.ts +439 -0
  83. package/src/nitro/types.ts +321 -0
  84. package/src/render/collect-css.ts +198 -0
  85. package/src/render/error-pages.ts +79 -0
  86. package/src/render/isolated-ssr-renderer.ts +654 -0
  87. package/src/render/ssr.ts +1030 -0
  88. package/src/schemas/api.ts +30 -0
  89. package/src/schemas/core.ts +64 -0
  90. package/src/schemas/index.ts +212 -0
  91. package/src/schemas/layout.ts +279 -0
  92. package/src/schemas/routing/index.ts +38 -0
  93. package/src/schemas/routing.ts +376 -0
  94. package/src/types/as-island.ts +20 -0
  95. package/src/types/layout.ts +285 -0
  96. package/src/types/routing.ts +555 -0
  97. package/src/types/types.ts +5 -0
  98. package/src/utils/dev-logger.ts +299 -0
  99. package/src/utils/fs.ts +151 -0
  100. package/src/vite-plugin/auto-discover.ts +551 -0
  101. package/src/vite-plugin/config.ts +266 -0
  102. package/src/vite-plugin/errors.ts +127 -0
  103. package/src/vite-plugin/image-optimization.ts +156 -0
  104. package/src/vite-plugin/integration-activator.ts +126 -0
  105. package/src/vite-plugin/island-sidecar-plugin.ts +176 -0
  106. package/src/vite-plugin/module-discovery.ts +189 -0
  107. package/src/vite-plugin/nitro-integration.ts +1354 -0
  108. package/src/vite-plugin/plugin.ts +403 -0
  109. package/src/vite-plugin/types.ts +327 -0
  110. package/src/vite-plugin/validation.ts +228 -0
  111. package/dist/mod.js +0 -1
  112. package/dist/src/build/integration-bundler-plugin.js +0 -1
  113. package/dist/src/build/integration-config.js +0 -1
  114. package/dist/src/build/integration-detection-plugin.js +0 -1
  115. package/dist/src/build/integration-resolver-plugin.js +0 -1
  116. package/dist/src/build/island-manifest.js +0 -1
  117. package/dist/src/build/island-types-generator.js +0 -5
  118. package/dist/src/build/mdx-island-transform.js +0 -2
  119. package/dist/src/build/mdx-plugin.js +0 -1
  120. package/dist/src/build/page-island-transform.js +0 -3
  121. package/dist/src/build/prop-extractors/index.js +0 -1
  122. package/dist/src/build/prop-extractors/lit.js +0 -1
  123. package/dist/src/build/prop-extractors/qwik.js +0 -1
  124. package/dist/src/build/prop-extractors/solid.js +0 -1
  125. package/dist/src/build/prop-extractors/svelte.js +0 -1
  126. package/dist/src/build/prop-extractors/vue.js +0 -1
  127. package/dist/src/build/sidecar-file-manager.js +0 -1
  128. package/dist/src/build/sidecar-renderer.js +0 -6
  129. package/dist/src/client/adapters/index.js +0 -1
  130. package/dist/src/client/components.js +0 -1
  131. package/dist/src/client/css-hmr-handler.js +0 -1
  132. package/dist/src/client/framework-adapter.js +0 -13
  133. package/dist/src/client/hmr-coordinator.js +0 -1
  134. package/dist/src/client/hmr-error-overlay.js +0 -214
  135. package/dist/src/client/main.js +0 -39
  136. package/dist/src/components/Image.js +0 -1
  137. package/dist/src/components/IslandErrorBoundary.js +0 -1
  138. package/dist/src/components/LayoutDataErrorBoundary.js +0 -1
  139. package/dist/src/components/LayoutErrorBoundary.js +0 -1
  140. package/dist/src/components/PersistentIsland.js +0 -1
  141. package/dist/src/components/StreamingErrorBoundary.js +0 -1
  142. package/dist/src/components/StreamingLayout.js +0 -29
  143. package/dist/src/core/components/component-analyzer.js +0 -1
  144. package/dist/src/core/components/component-detection.js +0 -5
  145. package/dist/src/core/components/enhanced-framework-detector.js +0 -1
  146. package/dist/src/core/components/framework-registry.js +0 -1
  147. package/dist/src/core/content/mdx-processor.js +0 -1
  148. package/dist/src/core/integrations/index.js +0 -1
  149. package/dist/src/core/integrations/loader.js +0 -1
  150. package/dist/src/core/integrations/registry.js +0 -1
  151. package/dist/src/core/islands/island-persistence.js +0 -1
  152. package/dist/src/core/islands/island-state-serializer.js +0 -1
  153. package/dist/src/core/islands/persistent-island-context.js +0 -1
  154. package/dist/src/core/islands/use-persistent-state.js +0 -1
  155. package/dist/src/core/layout/enhanced-layout-resolver.js +0 -1
  156. package/dist/src/core/layout/layout-cache-manager.js +0 -1
  157. package/dist/src/core/layout/layout-composer.js +0 -1
  158. package/dist/src/core/layout/layout-data-loader.js +0 -1
  159. package/dist/src/core/layout/layout-discovery.js +0 -1
  160. package/dist/src/core/layout/layout-matcher.js +0 -1
  161. package/dist/src/core/layout/layout-types.js +0 -1
  162. package/dist/src/core/modules/framework-module-resolver.js +0 -1
  163. package/dist/src/islands/component-analysis.js +0 -1
  164. package/dist/src/islands/css-utils.js +0 -17
  165. package/dist/src/islands/discovery/index.js +0 -1
  166. package/dist/src/islands/discovery/registry.js +0 -1
  167. package/dist/src/islands/discovery/resolver.js +0 -2
  168. package/dist/src/islands/discovery/scanner.js +0 -1
  169. package/dist/src/islands/discovery/types.js +0 -1
  170. package/dist/src/islands/discovery/validator.js +0 -18
  171. package/dist/src/islands/discovery/watcher.js +0 -1
  172. package/dist/src/islands/framework-detection.js +0 -1
  173. package/dist/src/islands/integration-loader.js +0 -1
  174. package/dist/src/islands/island.js +0 -1
  175. package/dist/src/islands/render-cache.js +0 -1
  176. package/dist/src/islands/types.js +0 -1
  177. package/dist/src/islands/universal-css-collector.js +0 -5
  178. package/dist/src/islands/universal-head-collector.js +0 -2
  179. package/dist/src/layout-system.js +0 -1
  180. package/dist/src/middleware/discovery.js +0 -1
  181. package/dist/src/middleware/executor.js +0 -1
  182. package/dist/src/middleware/index.js +0 -1
  183. package/dist/src/middleware/types.js +0 -1
  184. package/dist/src/nitro/build-config.js +0 -1
  185. package/dist/src/nitro/config.js +0 -1
  186. package/dist/src/nitro/error-handler.js +0 -198
  187. package/dist/src/nitro/index.js +0 -1
  188. package/dist/src/nitro/island-manifest.js +0 -2
  189. package/dist/src/nitro/middleware-adapter.js +0 -1
  190. package/dist/src/nitro/renderer.js +0 -183
  191. package/dist/src/nitro/route-discovery.js +0 -1
  192. package/dist/src/nitro/types.js +0 -1
  193. package/dist/src/render/collect-css.js +0 -3
  194. package/dist/src/render/error-pages.js +0 -48
  195. package/dist/src/render/isolated-ssr-renderer.js +0 -1
  196. package/dist/src/render/ssr.js +0 -90
  197. package/dist/src/schemas/api.js +0 -1
  198. package/dist/src/schemas/core.js +0 -1
  199. package/dist/src/schemas/index.js +0 -1
  200. package/dist/src/schemas/layout.js +0 -1
  201. package/dist/src/schemas/routing/index.js +0 -1
  202. package/dist/src/schemas/routing.js +0 -1
  203. package/dist/src/types/as-island.js +0 -1
  204. package/dist/src/types/layout.js +0 -1
  205. package/dist/src/types/routing.js +0 -1
  206. package/dist/src/types/types.js +0 -1
  207. package/dist/src/utils/dev-logger.js +0 -12
  208. package/dist/src/utils/fs.js +0 -1
  209. package/dist/src/vite-plugin/auto-discover.js +0 -1
  210. package/dist/src/vite-plugin/config.js +0 -1
  211. package/dist/src/vite-plugin/errors.js +0 -1
  212. package/dist/src/vite-plugin/image-optimization.js +0 -45
  213. package/dist/src/vite-plugin/integration-activator.js +0 -1
  214. package/dist/src/vite-plugin/island-sidecar-plugin.js +0 -1
  215. package/dist/src/vite-plugin/module-discovery.js +0 -1
  216. package/dist/src/vite-plugin/nitro-integration.js +0 -42
  217. package/dist/src/vite-plugin/plugin.js +0 -1
  218. package/dist/src/vite-plugin/types.js +0 -1
  219. package/dist/src/vite-plugin/validation.js +0 -2
  220. /package/{dist/src → src}/client/types/framework-runtime.d.ts +0 -0
  221. /package/{dist/src → src}/client/types/vite-hmr.d.ts +0 -0
  222. /package/{dist/src → src}/client/types/vite-virtual-modules.d.ts +0 -0
  223. /package/{dist/src → src}/layout-system.d.ts +0 -0
  224. /package/{dist/src → src}/types/image.d.ts +0 -0
  225. /package/{dist/src → src}/types/index.d.ts +0 -0
  226. /package/{dist/src → src}/types/island-jsx.d.ts +0 -0
  227. /package/{dist/src → src}/types/island-prop.d.ts +0 -0
  228. /package/{dist/src → src}/types/mdx.d.ts +0 -0
  229. /package/{dist/src → src}/types/urlpattern.d.ts +0 -0
  230. /package/{dist/src → src}/types/vite-env.d.ts +0 -0
@@ -0,0 +1,824 @@
1
+ // Main client entry point for Vite
2
+ // Integration-based island hydration system
3
+ //
4
+ // NOTE: This file contains imports to Vite virtual modules (/@useavalon/*/client)
5
+ // that will show as errors in the IDE. These are resolved by Vite at runtime
6
+ // and work correctly in the browser. The errors can be safely ignored.
7
+
8
+ if (document.readyState === 'loading') {
9
+ document.addEventListener('DOMContentLoaded', initializeHydration);
10
+ } else {
11
+ initializeHydration();
12
+ }
13
+
14
+ /**
15
+ * Initialize hydration for all islands on the page
16
+ * Discovers islands by data-framework attribute and routes to appropriate integration
17
+ */
18
+ function initializeHydration() {
19
+ const islands = document.querySelectorAll('[data-framework]');
20
+
21
+ if (islands.length === 0) {
22
+ return;
23
+ }
24
+
25
+ islands.forEach(island => {
26
+ try {
27
+ const framework = island.dataset.framework;
28
+ const condition = island.dataset.condition || 'on:client';
29
+ const renderStrategy = island.dataset.renderStrategy;
30
+
31
+ if (renderStrategy === 'ssr-only') {
32
+ return;
33
+ }
34
+
35
+ if (!shouldHydrate(island, condition)) {
36
+ return;
37
+ }
38
+
39
+ if (condition === 'on:client') {
40
+ hydrateIsland(island, framework);
41
+ } else if (condition === 'on:visible') {
42
+ setupIntersectionObserver(island, framework);
43
+ } else if (condition === 'on:interaction') {
44
+ setupInteractionObserver(island, framework);
45
+ } else if (condition === 'on:idle') {
46
+ setupIdleCallback(island, framework);
47
+ } else if (condition.startsWith('media:')) {
48
+ const mediaQuery = condition.slice(6);
49
+ setupMediaQuery(island, framework, mediaQuery);
50
+ } else {
51
+ hydrateIsland(island, framework);
52
+ }
53
+ } catch (error) {
54
+ console.error('Error processing island:', error);
55
+ handleHydrationError(island, island.dataset.framework || 'unknown', island.dataset.src || 'unknown', error);
56
+ }
57
+ });
58
+ }
59
+
60
+ /**
61
+ * Determine if an island should hydrate based on its condition
62
+ *
63
+ * @param {HTMLElement} island - The island element
64
+ * @param {string} condition - The hydration condition
65
+ * @returns {boolean} Whether the island should hydrate
66
+ */
67
+ function shouldHydrate(island, condition) {
68
+ if (!condition || condition === 'on:client') {
69
+ return true;
70
+ }
71
+
72
+ if (condition.startsWith('media:')) {
73
+ const mediaQuery = condition.slice(6);
74
+ try {
75
+ return globalThis.matchMedia(mediaQuery).matches;
76
+ } catch (error) {
77
+ console.error('Invalid media query:', mediaQuery, error);
78
+ return true;
79
+ }
80
+ }
81
+
82
+ if (condition === 'on:visible' || condition === 'on:interaction' || condition === 'on:idle') {
83
+ return true;
84
+ }
85
+
86
+ console.warn('Unknown hydration condition:', condition);
87
+ return true;
88
+ }
89
+
90
+ /**
91
+ * Setup Intersection Observer for "on:visible" hydration
92
+ *
93
+ * @param {HTMLElement} island - The island element
94
+ * @param {string} framework - The framework name
95
+ */
96
+ function setupIntersectionObserver(island, framework) {
97
+ try {
98
+ const observer = new IntersectionObserver(
99
+ entries => {
100
+ const entry = entries[0];
101
+ if (entry.isIntersecting) {
102
+ hydrateIsland(island, framework);
103
+ observer.disconnect();
104
+ }
105
+ },
106
+ {
107
+ rootMargin: '50px',
108
+ threshold: 0,
109
+ },
110
+ );
111
+
112
+ observer.observe(island);
113
+ } catch (error) {
114
+ console.error('Failed to setup intersection observer:', error);
115
+ hydrateIsland(island, framework);
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Setup interaction observer for "on:interaction" hydration
121
+ *
122
+ * @param {HTMLElement} island - The island element
123
+ * @param {string} framework - The framework name
124
+ */
125
+ function setupInteractionObserver(island, framework) {
126
+ const events = ['click', 'touchstart', 'mouseenter', 'focusin'];
127
+ let hydrated = false;
128
+
129
+ const handleInteraction = () => {
130
+ if (hydrated) return;
131
+ hydrated = true;
132
+
133
+ events.forEach(eventType => {
134
+ island.removeEventListener(eventType, handleInteraction);
135
+ });
136
+
137
+ hydrateIsland(island, framework);
138
+ };
139
+
140
+ try {
141
+ events.forEach(eventType => {
142
+ island.addEventListener(eventType, handleInteraction, { once: true, passive: true });
143
+ });
144
+ } catch (error) {
145
+ console.error('Failed to setup interaction observer:', error);
146
+ hydrateIsland(island, framework);
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Setup idle callback for "on:idle" hydration
152
+ *
153
+ * @param {HTMLElement} island - The island element
154
+ * @param {string} framework - The framework name
155
+ */
156
+ function setupIdleCallback(island, framework) {
157
+ try {
158
+ if ('requestIdleCallback' in globalThis) {
159
+ globalThis.requestIdleCallback(
160
+ () => {
161
+ hydrateIsland(island, framework);
162
+ },
163
+ { timeout: 5000 },
164
+ );
165
+ } else if (document.readyState === 'complete') {
166
+ setTimeout(() => {
167
+ hydrateIsland(island, framework);
168
+ }, 200);
169
+ } else {
170
+ globalThis.addEventListener(
171
+ 'load',
172
+ () => {
173
+ setTimeout(() => {
174
+ hydrateIsland(island, framework);
175
+ }, 200);
176
+ },
177
+ { once: true },
178
+ );
179
+ }
180
+ } catch (error) {
181
+ console.error('Failed to setup idle callback:', error);
182
+ hydrateIsland(island, framework);
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Setup media query listener for "media:" hydration
188
+ *
189
+ * @param {HTMLElement} island - The island element
190
+ * @param {string} framework - The framework name
191
+ * @param {string} mediaQuery - The media query string
192
+ */
193
+ function setupMediaQuery(island, framework, mediaQuery) {
194
+ try {
195
+ const mql = globalThis.matchMedia(mediaQuery);
196
+
197
+ if (mql.matches) {
198
+ hydrateIsland(island, framework);
199
+ return;
200
+ }
201
+
202
+ const handleChange = event => {
203
+ if (event.matches) {
204
+ hydrateIsland(island, framework);
205
+ mql.removeEventListener('change', handleChange);
206
+ }
207
+ };
208
+
209
+ mql.addEventListener('change', handleChange);
210
+ } catch (error) {
211
+ console.error('Failed to setup media query:', mediaQuery, error);
212
+ hydrateIsland(island, framework);
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Load the integration module for a given framework
218
+ *
219
+ * @param {string} framework - The framework name
220
+ * @returns {Promise<object>} The integration module
221
+ */
222
+ async function loadIntegrationModule(framework) {
223
+ switch (framework) {
224
+ case 'preact':
225
+ // @ts-ignore - Vite resolves this at runtime
226
+ return import('/@useavalon/preact/client');
227
+ case 'react':
228
+ // @ts-ignore - Vite resolves this at runtime
229
+ return import('/@useavalon/react/client');
230
+ case 'vue':
231
+ // @ts-ignore - Vite resolves this at runtime
232
+ return import('/@useavalon/vue/client');
233
+ case 'svelte':
234
+ // @ts-ignore - Vite resolves this at runtime
235
+ return import('/@useavalon/svelte/client');
236
+ case 'solid':
237
+ // @ts-ignore - Vite resolves this at runtime
238
+ return import('/@useavalon/solid/client');
239
+ case 'lit':
240
+ // @ts-ignore - Vite resolves this at runtime
241
+ return import('/@useavalon/lit/client');
242
+ case 'qwik':
243
+ // @ts-ignore - Vite resolves this at runtime
244
+ return import('/@useavalon/qwik/client');
245
+ default:
246
+ throw new Error(`Unknown framework: ${framework}`);
247
+ }
248
+ }
249
+
250
+ /**
251
+ * Resolve the component from a module, trying default export then named exports
252
+ *
253
+ * @param {object} componentModule - The imported module
254
+ * @param {string} src - The component source path (for error messages)
255
+ * @returns {object} The resolved component
256
+ */
257
+ function resolveComponent(componentModule, src) {
258
+ let Component = componentModule.default;
259
+
260
+ if (!Component) {
261
+ const exports = Object.keys(componentModule).filter(key => key !== 'default');
262
+ for (const exportName of exports) {
263
+ const exportValue = componentModule[exportName];
264
+ if (typeof exportValue === 'function' && exportValue.prototype) {
265
+ Component = exportValue;
266
+ break;
267
+ }
268
+ }
269
+
270
+ if (!Component) {
271
+ Component = componentModule;
272
+ }
273
+ }
274
+
275
+ if (!Component) {
276
+ throw new Error(`Component ${src} has no default export`);
277
+ }
278
+
279
+ return Component;
280
+ }
281
+
282
+ /**
283
+ * Hydrate an island using the integration system
284
+ *
285
+ * @param {HTMLElement} island - The island element
286
+ * @param {string} framework - The framework name
287
+ */
288
+ async function hydrateIsland(island, framework) {
289
+ if (island.dataset.hydrated) {
290
+ return;
291
+ }
292
+
293
+ const src = island.dataset.src;
294
+ const propsAttr = island.dataset.props;
295
+
296
+ if (!src) {
297
+ console.warn('Island missing data-src attribute');
298
+ return;
299
+ }
300
+
301
+ try {
302
+ const props = propsAttr ? JSON.parse(propsAttr) : {};
303
+
304
+ // CRITICAL: For Lit components, load hydration support BEFORE importing the component
305
+ // This ensures our patch is applied before @customElement decorator runs
306
+ if (framework === 'lit') {
307
+ // @ts-ignore - Vite resolves this virtual module at runtime
308
+ await import('/@useavalon/lit/client');
309
+ }
310
+
311
+ const componentModule = await import(/* @vite-ignore */ src);
312
+ const Component = resolveComponent(componentModule, src);
313
+
314
+ try {
315
+ const integrationModule = await loadIntegrationModule(framework);
316
+
317
+ if (!integrationModule.hydrate || typeof integrationModule.hydrate !== 'function') {
318
+ throw new Error(`Integration ${framework} does not export a hydrate function`);
319
+ }
320
+
321
+ integrationModule.hydrate(island, Component, props);
322
+ island.dataset.hydrated = 'true';
323
+ } catch (integrationError) {
324
+ if (import.meta.env?.DEV) {
325
+ console.error(`Integration hydration failed for ${framework}: ${src}`, integrationError);
326
+ }
327
+
328
+ island.dataset.hydrationStatus = 'failed';
329
+ island.dataset.hydrationError = integrationError.message;
330
+
331
+ island.dispatchEvent(
332
+ new CustomEvent('hydration-error', {
333
+ detail: {
334
+ framework,
335
+ src,
336
+ error: integrationError.message,
337
+ timestamp: Date.now(),
338
+ hydrationType: 'integration-level',
339
+ },
340
+ bubbles: true,
341
+ }),
342
+ );
343
+ }
344
+ } catch (error) {
345
+ console.error(`❌ Critical error hydrating ${framework} island ${src}:`, error);
346
+ handleHydrationError(island, framework, src, error);
347
+ }
348
+ }
349
+
350
+ /**
351
+ * Handle hydration errors with graceful degradation
352
+ *
353
+ * @param {HTMLElement} island - The island element
354
+ * @param {string} framework - The framework name
355
+ * @param {string} src - The component source path
356
+ * @param {Error} error - The error that occurred
357
+ */
358
+ function handleHydrationError(island, framework, src, error) {
359
+ console.error(`Hydration error for ${framework} island:`, {
360
+ src,
361
+ error: error.message,
362
+ stack: error.stack,
363
+ });
364
+
365
+ island.dataset.hydrationStatus = 'failed';
366
+ island.dataset.renderStrategy = 'ssr-only';
367
+ island.classList.add('hydration-failed');
368
+
369
+ island.dispatchEvent(
370
+ new CustomEvent('hydration-error', {
371
+ detail: {
372
+ framework,
373
+ src,
374
+ error: error.message,
375
+ timestamp: Date.now(),
376
+ },
377
+ bubbles: true,
378
+ }),
379
+ );
380
+
381
+ if (isDevelopment()) {
382
+ addErrorIndicator(island, framework, src, error);
383
+ }
384
+ }
385
+
386
+ /**
387
+ * Add visual error indicator in development mode
388
+ *
389
+ * @param {HTMLElement} island - The island element
390
+ * @param {string} framework - The framework name
391
+ * @param {string} src - The component source path
392
+ * @param {Error} error - The error that occurred
393
+ */
394
+ function addErrorIndicator(island, framework, src, error) {
395
+ const indicator = document.createElement('div');
396
+ indicator.className = 'hydration-error-indicator';
397
+ indicator.style.cssText = `
398
+ position: absolute;
399
+ top: 0;
400
+ right: 0;
401
+ background: #ff4444;
402
+ color: white;
403
+ padding: 4px 8px;
404
+ font-size: 11px;
405
+ font-family: monospace;
406
+ border-radius: 0 0 0 4px;
407
+ z-index: 9999;
408
+ cursor: pointer;
409
+ box-shadow: 0 2px 4px rgba(0,0,0,0.2);
410
+ `;
411
+ indicator.textContent = `❌ ${framework}`;
412
+ indicator.title = `Hydration failed: ${src}\n${error.message}\nClick for details`;
413
+
414
+ indicator.addEventListener('click', () => {
415
+ alert(
416
+ `Hydration Error\n\nFramework: ${framework}\nComponent: ${src}\n\nError: ${error.message}\n\nStack:\n${error.stack}`,
417
+ );
418
+ });
419
+
420
+ const computedStyle = globalThis.getComputedStyle(island);
421
+ if (computedStyle.position === 'static') {
422
+ island.style.position = 'relative';
423
+ }
424
+
425
+ island.appendChild(indicator);
426
+ }
427
+
428
+ /**
429
+ * Check if running in development mode
430
+ *
431
+ * @returns {boolean} True if in development
432
+ */
433
+ function isDevelopment() {
434
+ return (
435
+ import.meta.env?.DEV ||
436
+ import.meta.env?.MODE === 'development' ||
437
+ globalThis.location?.hostname === 'localhost' ||
438
+ globalThis.location?.hostname === '127.0.0.1'
439
+ );
440
+ }
441
+
442
+ /**
443
+ * Store island state before HMR update
444
+ * @param {HTMLElement} island - The island element
445
+ * @returns {object|null} The preserved state
446
+ */
447
+ function preserveIslandState(island) {
448
+ const framework = island.dataset.framework;
449
+ const src = island.dataset.src;
450
+
451
+ if (!src) return null;
452
+
453
+ const state = {
454
+ framework,
455
+ src,
456
+ props: island.dataset.props,
457
+ scrollPosition: {
458
+ x: globalThis.scrollX,
459
+ y: globalThis.scrollY,
460
+ },
461
+ focusedElement: document.activeElement?.id || null,
462
+ };
463
+
464
+ try {
465
+ if (framework === 'vue' && island.__vue__) {
466
+ state.vueData = structuredClone(island.__vue__.$data || {});
467
+ } else if (framework === 'svelte' && island.__svelte__) {
468
+ state.svelteState = island.__svelte__;
469
+ } else if (framework === 'lit' && island.tagName?.includes('-')) {
470
+ const litElement = island.querySelector('[data-lit-element]') || island;
471
+ if (litElement._$litElement$) {
472
+ state.litProperties = {};
473
+ }
474
+ }
475
+ } catch (error) {
476
+ console.warn('Failed to preserve island state:', error);
477
+ }
478
+
479
+ return state;
480
+ }
481
+
482
+ /**
483
+ * Restore island state after HMR update
484
+ * @param {HTMLElement} island - The island element
485
+ * @param {object} state - The preserved state
486
+ */
487
+ function restoreIslandState(island, state) {
488
+ if (!state) return;
489
+
490
+ try {
491
+ if (state.scrollPosition) {
492
+ globalThis.scrollTo(state.scrollPosition.x, state.scrollPosition.y);
493
+ }
494
+
495
+ if (state.focusedElement) {
496
+ const element = document.getElementById(state.focusedElement);
497
+ if (element) {
498
+ element.focus();
499
+ }
500
+ }
501
+
502
+ if (island.dataset.framework === 'vue' && state.vueData && island.__vue__) {
503
+ Object.assign(island.__vue__.$data, state.vueData);
504
+ }
505
+ } catch (error) {
506
+ console.warn('Failed to restore island state:', error);
507
+ }
508
+ }
509
+
510
+ /**
511
+ * Hydrate an island with a fresh module (cache-busted)
512
+ * @param {HTMLElement} island - The island element
513
+ * @param {string} framework - The framework name
514
+ * @param {string} freshSrc - The cache-busted source path
515
+ * @param {string} originalSrc - The original source path
516
+ */
517
+ async function hydrateIslandWithFreshModule(island, framework, freshSrc, originalSrc) {
518
+ const propsAttr = island.dataset.props;
519
+ const props = propsAttr ? JSON.parse(propsAttr) : {};
520
+
521
+ if (framework === 'lit') {
522
+ // @ts-ignore - Vite resolves this at runtime
523
+ await import('/@useavalon/lit/client');
524
+ }
525
+
526
+ const componentModule = await import(/* @vite-ignore */ freshSrc);
527
+ const Component = resolveComponent(componentModule, originalSrc);
528
+
529
+ const integrationModule = await loadIntegrationModule(framework);
530
+
531
+ if (!integrationModule.hydrate || typeof integrationModule.hydrate !== 'function') {
532
+ throw new Error(`Integration ${framework} does not export a hydrate function`);
533
+ }
534
+
535
+ integrationModule.hydrate(island, Component, props);
536
+ island.dataset.hydrated = 'true';
537
+ }
538
+
539
+ /**
540
+ * Show inline HMR error indicator
541
+ * @param {HTMLElement} island - The island element
542
+ * @param {string} framework - The framework name
543
+ * @param {string} src - The component source path
544
+ * @param {Error} error - The error that occurred
545
+ */
546
+ function showInlineHMRError(island, framework, src, error) {
547
+ const existing = island.querySelector('.hmr-error-indicator');
548
+ if (existing) {
549
+ existing.remove();
550
+ }
551
+
552
+ const indicator = document.createElement('div');
553
+ indicator.className = 'hmr-error-indicator';
554
+ indicator.style.cssText = `
555
+ position: absolute;
556
+ top: 0;
557
+ left: 0;
558
+ right: 0;
559
+ background: linear-gradient(135deg, #ff6b6b, #ee5a5a);
560
+ color: white;
561
+ padding: 8px 12px;
562
+ font-size: 12px;
563
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, monospace;
564
+ z-index: 10000;
565
+ box-shadow: 0 2px 8px rgba(0,0,0,0.3);
566
+ display: flex;
567
+ align-items: center;
568
+ gap: 8px;
569
+ `;
570
+
571
+ const icon = document.createElement('span');
572
+ icon.textContent = '⚠️';
573
+ icon.style.fontSize = '14px';
574
+
575
+ const message = document.createElement('span');
576
+ message.style.flex = '1';
577
+ message.innerHTML = `<strong>HMR Failed:</strong> ${error.message.slice(0, 100)}${error.message.length > 100 ? '...' : ''}`;
578
+
579
+ const dismissBtn = document.createElement('button');
580
+ dismissBtn.textContent = '×';
581
+ dismissBtn.style.cssText = `
582
+ background: rgba(255,255,255,0.2);
583
+ border: none;
584
+ color: white;
585
+ width: 20px;
586
+ height: 20px;
587
+ border-radius: 50%;
588
+ cursor: pointer;
589
+ font-size: 14px;
590
+ line-height: 1;
591
+ `;
592
+ dismissBtn.onclick = () => indicator.remove();
593
+
594
+ indicator.appendChild(icon);
595
+ indicator.appendChild(message);
596
+ indicator.appendChild(dismissBtn);
597
+
598
+ const computedStyle = globalThis.getComputedStyle(island);
599
+ if (computedStyle.position === 'static') {
600
+ island.style.position = 'relative';
601
+ }
602
+
603
+ island.insertBefore(indicator, island.firstChild);
604
+ }
605
+
606
+ // HMR support for development
607
+ if (import.meta.hot) {
608
+ import.meta.hot.accept();
609
+
610
+ // Lazy HMR adapter registration - only load adapters for frameworks used on the page
611
+ import('./hmr-coordinator.js')
612
+ .then(async ({ initializeHMR, getHMRCoordinator }) => {
613
+ initializeHMR();
614
+
615
+ const coordinator = getHMRCoordinator();
616
+
617
+ // Discover which frameworks are actually used on this page
618
+ const usedFrameworks = new Set();
619
+ document.querySelectorAll('[data-framework]').forEach(island => {
620
+ const framework = island.dataset.framework;
621
+ if (framework) usedFrameworks.add(framework);
622
+ });
623
+
624
+ // Only register adapters for frameworks that are used
625
+ // Adapters are loaded from their respective integration packages
626
+ const adapterLoaders = {
627
+ // @ts-ignore - Vite resolves these virtual modules at runtime
628
+ react: () => import('/@useavalon/react/client/hmr').then(m => m.reactAdapter),
629
+ // @ts-ignore - Vite resolves these virtual modules at runtime
630
+ preact: () => import('/@useavalon/preact/client/hmr').then(m => m.preactAdapter),
631
+ // @ts-ignore - Vite resolves these virtual modules at runtime
632
+ vue: () => import('/@useavalon/vue/client/hmr').then(m => m.vueAdapter),
633
+ // @ts-ignore - Vite resolves these virtual modules at runtime
634
+ svelte: () => import('/@useavalon/svelte/client/hmr').then(m => m.svelteAdapter),
635
+ // @ts-ignore - Vite resolves these virtual modules at runtime
636
+ solid: () => import('/@useavalon/solid/client/hmr').then(m => m.solidAdapter),
637
+ // @ts-ignore - Vite resolves these virtual modules at runtime
638
+ lit: () => import('/@useavalon/lit/client/hmr').then(m => m.litAdapter),
639
+ // @ts-ignore - Vite resolves these virtual modules at runtime
640
+ qwik: () => import('/@useavalon/qwik/client/hmr').then(m => m.qwikAdapter),
641
+ };
642
+
643
+ for (const framework of usedFrameworks) {
644
+ const loader = adapterLoaders[framework];
645
+ if (loader) {
646
+ try {
647
+ const adapter = await loader();
648
+ coordinator.registerAdapter(framework, adapter);
649
+ } catch (error) {
650
+ console.warn(`[HMR] Failed to load adapter for ${framework}:`, error);
651
+ }
652
+ }
653
+ }
654
+ })
655
+ .catch(error => {
656
+ console.error('[HMR] Failed to initialize:', error);
657
+ });
658
+
659
+ // Enhanced HMR support for nested islands
660
+ setupNestedIslandHMR();
661
+ }
662
+
663
+ /**
664
+ * Show HMR error feedback on the island
665
+ * @param {HTMLElement} island - The island element
666
+ * @param {string} hmrFramework - The framework name
667
+ * @param {string} hmrSrc - The component source path
668
+ * @param {Error} error - The error that occurred
669
+ */
670
+ async function showHMRError(island, hmrFramework, hmrSrc, error) {
671
+ try {
672
+ const { showHMRErrorOverlay } = await import('./hmr-error-overlay.js');
673
+ showHMRErrorOverlay({
674
+ framework: hmrFramework,
675
+ src: hmrSrc,
676
+ error,
677
+ filePath: hmrSrc,
678
+ });
679
+ } catch {
680
+ showInlineHMRError(island, hmrFramework, hmrSrc, error);
681
+ }
682
+ }
683
+
684
+ /**
685
+ * Setup HMR support for nested island directories.
686
+ * Handles hot module replacement for islands in any discovered directory,
687
+ * including nested paths like /src/modules/[module]/islands/.
688
+ */
689
+ function setupNestedIslandHMR() {
690
+ if (!import.meta.hot) return;
691
+
692
+ const hydratedIslands = new Map();
693
+
694
+ async function handleIslandHMR(modulePath) {
695
+ const normalizedPath = modulePath.replaceAll('\\', '/');
696
+
697
+ const islands = document.querySelectorAll(`[data-src*="${normalizedPath}"], [data-src$="${normalizedPath}"]`);
698
+
699
+ if (islands.length === 0) {
700
+ const allIslands = document.querySelectorAll('[data-src]');
701
+ for (const island of allIslands) {
702
+ const src = island.dataset.src;
703
+ if (src && (src.includes(normalizedPath) || normalizedPath.includes(src.replace(/^\//, '')))) {
704
+ await rehydrateIsland(island);
705
+ }
706
+ }
707
+ return;
708
+ }
709
+
710
+ for (const island of islands) {
711
+ await rehydrateIsland(island);
712
+ }
713
+ }
714
+
715
+ async function rehydrateIsland(island) {
716
+ const framework = island.dataset.framework;
717
+ const src = island.dataset.src;
718
+
719
+ if (!src || !framework) return;
720
+
721
+ try {
722
+ const state = preserveIslandState(island);
723
+ hydratedIslands.set(src, state);
724
+
725
+ delete island.dataset.hydrated;
726
+ delete island.dataset.hydrationStatus;
727
+
728
+ const errorIndicator = island.querySelector('.hydration-error-indicator');
729
+ if (errorIndicator) {
730
+ errorIndicator.remove();
731
+ }
732
+
733
+ const timestamp = Date.now();
734
+ const freshSrc = src.includes('?') ? `${src}&t=${timestamp}` : `${src}?t=${timestamp}`;
735
+
736
+ await hydrateIslandWithFreshModule(island, framework, freshSrc, src);
737
+
738
+ const preservedState = hydratedIslands.get(src);
739
+ if (preservedState) {
740
+ restoreIslandState(island, preservedState);
741
+ hydratedIslands.delete(src);
742
+ }
743
+
744
+ island.dispatchEvent(
745
+ new CustomEvent('hmr-update', {
746
+ detail: {
747
+ framework,
748
+ src,
749
+ timestamp: Date.now(),
750
+ success: true,
751
+ },
752
+ bubbles: true,
753
+ }),
754
+ );
755
+ } catch (error) {
756
+ console.error(`[HMR] Failed for ${framework} island ${src}:`, error);
757
+
758
+ island.dispatchEvent(
759
+ new CustomEvent('hmr-error', {
760
+ detail: {
761
+ framework,
762
+ src,
763
+ error: error.message,
764
+ timestamp: Date.now(),
765
+ },
766
+ bubbles: true,
767
+ }),
768
+ );
769
+
770
+ if (isDevelopment()) {
771
+ showHMRError(island, framework, src, error);
772
+ }
773
+ }
774
+ }
775
+
776
+ // Listen for Vite HMR events
777
+ import.meta.hot.on('vite:beforeUpdate', payload => {
778
+ for (const update of payload.updates || []) {
779
+ const path = update.path || update.acceptedPath;
780
+ if (path && (path.includes('/islands/') || path.includes('\\islands\\'))) {
781
+ handleIslandHMR(path);
782
+ }
783
+ }
784
+ });
785
+
786
+ // Handle full page reloads for islands
787
+ import.meta.hot.on('vite:beforeFullReload', () => {
788
+ const islands = document.querySelectorAll('[data-hydrated="true"]');
789
+ const states = {};
790
+
791
+ for (const island of islands) {
792
+ const src = island.dataset.src;
793
+ if (src) {
794
+ states[src] = preserveIslandState(island);
795
+ }
796
+ }
797
+
798
+ try {
799
+ sessionStorage.setItem('__avalon_hmr_states__', JSON.stringify(states));
800
+ } catch {
801
+ // sessionStorage might not be available
802
+ }
803
+ });
804
+
805
+ // Restore states after page load (for full reloads)
806
+ try {
807
+ const savedStates = sessionStorage.getItem('__avalon_hmr_states__');
808
+ if (savedStates) {
809
+ const states = JSON.parse(savedStates);
810
+ sessionStorage.removeItem('__avalon_hmr_states__');
811
+
812
+ setTimeout(() => {
813
+ for (const [src, state] of Object.entries(states)) {
814
+ const island = document.querySelector(`[data-src="${src}"]`);
815
+ if (island && state) {
816
+ restoreIslandState(island, state);
817
+ }
818
+ }
819
+ }, 100);
820
+ }
821
+ } catch {
822
+ // Ignore errors - state restoration is best-effort
823
+ }
824
+ }