@chr33s/solarflare 0.0.2

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 (47) hide show
  1. package/package.json +52 -0
  2. package/readme.md +183 -0
  3. package/src/ast.ts +316 -0
  4. package/src/build.bundle-client.ts +404 -0
  5. package/src/build.bundle-server.ts +131 -0
  6. package/src/build.bundle.ts +48 -0
  7. package/src/build.emit-manifests.ts +25 -0
  8. package/src/build.hmr-entry.ts +88 -0
  9. package/src/build.scan.ts +182 -0
  10. package/src/build.ts +227 -0
  11. package/src/build.validate.ts +63 -0
  12. package/src/client.hmr.ts +78 -0
  13. package/src/client.styles.ts +68 -0
  14. package/src/client.ts +190 -0
  15. package/src/codemod.ts +688 -0
  16. package/src/console-forward.ts +254 -0
  17. package/src/critical-css.ts +103 -0
  18. package/src/devtools-json.ts +52 -0
  19. package/src/diff-dom-streaming.ts +406 -0
  20. package/src/early-flush.ts +125 -0
  21. package/src/early-hints.ts +83 -0
  22. package/src/fetch.ts +44 -0
  23. package/src/fs.ts +11 -0
  24. package/src/head.ts +876 -0
  25. package/src/hmr.ts +647 -0
  26. package/src/hydration.ts +238 -0
  27. package/src/manifest.runtime.ts +25 -0
  28. package/src/manifest.ts +23 -0
  29. package/src/paths.ts +96 -0
  30. package/src/render-priority.ts +69 -0
  31. package/src/route-cache.ts +163 -0
  32. package/src/router-deferred.ts +85 -0
  33. package/src/router-stream.ts +65 -0
  34. package/src/router.ts +535 -0
  35. package/src/runtime.ts +32 -0
  36. package/src/serialize.ts +38 -0
  37. package/src/server.hmr.ts +67 -0
  38. package/src/server.styles.ts +42 -0
  39. package/src/server.ts +480 -0
  40. package/src/solarflare.d.ts +101 -0
  41. package/src/speculation-rules.ts +171 -0
  42. package/src/store.ts +78 -0
  43. package/src/stream-assets.ts +135 -0
  44. package/src/stylesheets.ts +222 -0
  45. package/src/worker.config.ts +243 -0
  46. package/src/worker.ts +542 -0
  47. package/tsconfig.json +21 -0
package/src/hmr.ts ADDED
@@ -0,0 +1,647 @@
1
+ import { type FunctionComponent, type VNode, h, Component as PreactComponent } from "preact";
2
+ import { signal, type Signal, useSignal, useSignalEffect } from "@preact/signals";
3
+ import { useMemo } from "preact/hooks";
4
+ import register from "preact-custom-element";
5
+ import { initRouter, getRouter } from "./router.ts";
6
+ import { extractDataIsland, initHydrationCoordinator } from "./hydration.ts";
7
+ import { installHeadHoisting, createHeadContext, setHeadContext } from "./head.ts";
8
+ import { getRuntime } from "./runtime.ts";
9
+ import type { RoutesManifest } from "./manifest.ts";
10
+ import type { HmrApi } from "./client.hmr.ts";
11
+ import { stylesheets } from "./stylesheets.ts";
12
+
13
+ /** Global storage for component hook state across HMR updates. */
14
+ const hookStateMap = new Map<string, unknown[]>();
15
+
16
+ /** Saves hook state for a component. */
17
+ function saveHookState(componentId: string, hookState: unknown[]) {
18
+ hookStateMap.set(componentId, [...hookState]);
19
+ }
20
+
21
+ /** Restores hook state for a component. */
22
+ function restoreHookState(componentId: string) {
23
+ return hookStateMap.get(componentId);
24
+ }
25
+
26
+ /** Stored scroll positions keyed by component tag. */
27
+ const scrollPositions = new Map<string, { x: number; y: number }>();
28
+
29
+ /** Saves current scroll position. */
30
+ function saveScrollPosition(tag?: string) {
31
+ const key = tag ?? "__global__";
32
+ scrollPositions.set(key, {
33
+ x: globalThis.scrollX ?? 0,
34
+ y: globalThis.scrollY ?? 0,
35
+ });
36
+ }
37
+
38
+ /** Restores scroll position. */
39
+ function restoreScrollPosition(tag?: string) {
40
+ const key = tag ?? "__global__";
41
+ const pos = scrollPositions.get(key);
42
+ if (pos) {
43
+ requestAnimationFrame(() => {
44
+ globalThis.scrollTo(pos.x, pos.y);
45
+ });
46
+ }
47
+ }
48
+
49
+ /** Props for HMR error boundary. */
50
+ interface HMRErrorBoundaryProps {
51
+ /** Child components to render. */
52
+ children?: VNode;
53
+ /** Component tag for identification. */
54
+ tag: string;
55
+ /** HMR version signal to trigger re-render on updates. */
56
+ hmrVersion: Signal<number>;
57
+ /** Fallback UI when error occurs. */
58
+ fallback?: (error: Error, retry: () => void) => VNode;
59
+ }
60
+
61
+ /** State for HMR error boundary. */
62
+ interface HMRErrorBoundaryState {
63
+ error: Error | null;
64
+ errorInfo: { componentStack?: string } | null;
65
+ }
66
+
67
+ /** Error boundary that recovers on HMR updates. */
68
+ export class HMRErrorBoundary extends PreactComponent<
69
+ HMRErrorBoundaryProps,
70
+ HMRErrorBoundaryState
71
+ > {
72
+ state: HMRErrorBoundaryState = { error: null, errorInfo: null };
73
+
74
+ private lastHmrVersion = -1;
75
+ private unsubscribe?: () => void;
76
+
77
+ componentDidMount() {
78
+ this.lastHmrVersion = this.props.hmrVersion.value;
79
+ this.unsubscribe = this.props.hmrVersion.subscribe((version) => {
80
+ if (version !== this.lastHmrVersion && this.state.error) {
81
+ console.log(`[HMR] Attempting recovery for <${this.props.tag}>`);
82
+ this.setState({ error: null, errorInfo: null });
83
+ }
84
+ this.lastHmrVersion = version;
85
+ });
86
+ }
87
+
88
+ componentWillUnmount() {
89
+ this.unsubscribe?.();
90
+ }
91
+
92
+ static getDerivedStateFromError(error: Error): Partial<HMRErrorBoundaryState> {
93
+ return { error };
94
+ }
95
+
96
+ componentDidCatch(error: Error, errorInfo: { componentStack?: string }) {
97
+ this.setState({ errorInfo });
98
+ console.error(`[HMR] Error in <${this.props.tag}>:`, error);
99
+ if (errorInfo.componentStack) {
100
+ console.error("Component stack:", errorInfo.componentStack);
101
+ }
102
+ }
103
+
104
+ retry = () => {
105
+ this.setState({ error: null, errorInfo: null });
106
+ };
107
+
108
+ render() {
109
+ const { error, errorInfo } = this.state;
110
+ const { children, tag, fallback } = this.props;
111
+
112
+ if (error) {
113
+ if (fallback) {
114
+ return fallback(error, this.retry);
115
+ }
116
+
117
+ return h(
118
+ "div",
119
+ {
120
+ style: {
121
+ padding: "16px",
122
+ margin: "8px",
123
+ backgroundColor: "#fee2e2",
124
+ border: "1px solid #ef4444",
125
+ borderRadius: "8px",
126
+ fontFamily: "system-ui, sans-serif",
127
+ },
128
+ },
129
+ h(
130
+ "div",
131
+ {
132
+ style: {
133
+ display: "flex",
134
+ alignItems: "center",
135
+ gap: "8px",
136
+ marginBottom: "8px",
137
+ },
138
+ },
139
+ h("span", { style: { fontSize: "20px" } }, "⚠️"),
140
+ h("strong", { style: { color: "#991b1b" } }, `Error in <${tag}>`),
141
+ ),
142
+ h(
143
+ "pre",
144
+ {
145
+ style: {
146
+ margin: "8px 0",
147
+ padding: "8px",
148
+ backgroundColor: "#fef2f2",
149
+ borderRadius: "4px",
150
+ overflow: "auto",
151
+ fontSize: "12px",
152
+ color: "#7f1d1d",
153
+ },
154
+ },
155
+ error.message,
156
+ ),
157
+ errorInfo?.componentStack &&
158
+ h(
159
+ "details",
160
+ { style: { marginTop: "8px" } },
161
+ h("summary", { style: { cursor: "pointer", color: "#991b1b" } }, "Component Stack"),
162
+ h(
163
+ "pre",
164
+ {
165
+ style: {
166
+ fontSize: "10px",
167
+ color: "#7f1d1d",
168
+ whiteSpace: "pre-wrap",
169
+ },
170
+ },
171
+ errorInfo.componentStack,
172
+ ),
173
+ ),
174
+ h(
175
+ "button",
176
+ {
177
+ onClick: this.retry,
178
+ style: {
179
+ marginTop: "12px",
180
+ padding: "8px 16px",
181
+ backgroundColor: "#ef4444",
182
+ color: "white",
183
+ border: "none",
184
+ borderRadius: "4px",
185
+ cursor: "pointer",
186
+ fontSize: "14px",
187
+ },
188
+ },
189
+ "Retry",
190
+ ),
191
+ );
192
+ }
193
+
194
+ return children ?? h("span", null);
195
+ }
196
+ }
197
+
198
+ /** Tracks loaded CSS files for HMR. */
199
+ const loadedStylesheets = new Map<string, HTMLLinkElement>();
200
+
201
+ /** CSS HMR update payload. */
202
+ export interface CssHmrUpdate {
203
+ id: string;
204
+ css: string;
205
+ /** Specific rules that changed (for incremental updates) */
206
+ changedRules?: Array<{
207
+ selector: string;
208
+ properties: string;
209
+ action: "add" | "update" | "delete";
210
+ }>;
211
+ }
212
+
213
+ /** Handles CSS HMR updates. */
214
+ export function handleCssHmrUpdate(update: CssHmrUpdate) {
215
+ const { id, css, changedRules } = update;
216
+
217
+ // Try granular update first
218
+ if (changedRules && changedRules.length < 10) {
219
+ const success = applyGranularUpdates(id, changedRules);
220
+ if (success) {
221
+ console.log(`[HMR] Incrementally updated ${changedRules.length} rules in ${id}`);
222
+ return;
223
+ }
224
+ }
225
+
226
+ // Fall back to full replacement
227
+ const updated = stylesheets.update(id, css);
228
+ if (updated) {
229
+ console.log(`[HMR] Replaced stylesheet: ${id}`);
230
+ }
231
+ }
232
+
233
+ /** Applies granular rule updates using insertRule/deleteRule. */
234
+ function applyGranularUpdates(id: string, changes: CssHmrUpdate["changedRules"]) {
235
+ if (!changes) return false;
236
+
237
+ const sheet = stylesheets.get(id);
238
+ if (!sheet) return false;
239
+
240
+ try {
241
+ // Build a map of existing rules by selector
242
+ const ruleMap = new Map<string, number>();
243
+ for (let i = 0; i < sheet.cssRules.length; i++) {
244
+ const rule = sheet.cssRules[i];
245
+ if (rule instanceof CSSStyleRule) {
246
+ ruleMap.set(rule.selectorText, i);
247
+ }
248
+ }
249
+
250
+ // Process changes in reverse order to maintain indices
251
+ const sortedChanges = [...changes].sort((a, b) => {
252
+ const idxA = ruleMap.get(a.selector) ?? -1;
253
+ const idxB = ruleMap.get(b.selector) ?? -1;
254
+ return idxB - idxA; // Reverse order
255
+ });
256
+
257
+ for (const change of sortedChanges) {
258
+ const existingIndex = ruleMap.get(change.selector);
259
+
260
+ switch (change.action) {
261
+ case "delete":
262
+ if (existingIndex !== undefined) {
263
+ sheet.deleteRule(existingIndex);
264
+ }
265
+ break;
266
+
267
+ case "update":
268
+ if (existingIndex !== undefined) {
269
+ // Delete and re-insert at same position
270
+ sheet.deleteRule(existingIndex);
271
+ sheet.insertRule(`${change.selector} { ${change.properties} }`, existingIndex);
272
+ }
273
+ break;
274
+
275
+ case "add":
276
+ sheet.insertRule(`${change.selector} { ${change.properties} }`, sheet.cssRules.length);
277
+ break;
278
+ }
279
+ }
280
+
281
+ return true;
282
+ } catch (e) {
283
+ console.warn("[HMR] Granular update failed, falling back to full replace", e);
284
+ return false;
285
+ }
286
+ }
287
+
288
+ /** Registers HMR handlers for CSS files. */
289
+ export function setupCssHmr(hmr: { on: (event: string, cb: (data: unknown) => void) => void }) {
290
+ hmr.on("sf:css-update", (data) => {
291
+ handleCssHmrUpdate(data as CssHmrUpdate);
292
+ });
293
+
294
+ // Handle full CSS file replacement
295
+ hmr.on("sf:css-replace", (data) => {
296
+ const { id, css } = data as { id: string; css: string };
297
+ stylesheets.update(id, css);
298
+ console.log(`[HMR] Full CSS replacement: ${id}`);
299
+ });
300
+ }
301
+
302
+ /** Reloads a CSS file by updating its href with a cache-busting query. */
303
+ export function reloadStylesheet(href: string) {
304
+ const existing =
305
+ loadedStylesheets.get(href) ?? document.querySelector<HTMLLinkElement>(`link[href^="${href}"]`);
306
+
307
+ if (existing) {
308
+ const url = new URL(existing.href, window.location.origin);
309
+ url.searchParams.set("t", Date.now().toString());
310
+ existing.href = url.toString();
311
+ console.log(`[HMR] Reloaded stylesheet: ${href}`);
312
+ } else {
313
+ const link = document.createElement("link");
314
+ link.rel = "stylesheet";
315
+ link.href = `${href}?t=${Date.now()}`;
316
+ document.head.appendChild(link);
317
+ loadedStylesheets.set(href, link);
318
+ console.log(`[HMR] Injected stylesheet: ${href}`);
319
+ }
320
+ }
321
+
322
+ /** Reloads all linked stylesheets on the page. */
323
+ export function reloadAllStylesheets() {
324
+ if (typeof document === "undefined") return;
325
+ const links = document.querySelectorAll<HTMLLinkElement>('link[rel="stylesheet"]');
326
+
327
+ links.forEach((link) => {
328
+ const href = link.getAttribute("href");
329
+ if (!href) return;
330
+ if (!href.includes("?")) {
331
+ link.setAttribute("href", `${href}?t=${Date.now()}`);
332
+ } else {
333
+ link.setAttribute("href", href.replace(/\?t=\d+/, `?t=${Date.now()}`));
334
+ }
335
+ });
336
+
337
+ console.log("[HMR] Reloaded stylesheets");
338
+ }
339
+
340
+ /** Removes a stylesheet from the document. */
341
+ export function removeStylesheet(href: string) {
342
+ const existing =
343
+ loadedStylesheets.get(href) ?? document.querySelector<HTMLLinkElement>(`link[href^="${href}"]`);
344
+
345
+ if (existing) {
346
+ existing.remove();
347
+ loadedStylesheets.delete(href);
348
+ console.log(`[HMR] Removed stylesheet: ${href}`);
349
+ }
350
+ }
351
+
352
+ /** Accepts CSS HMR updates. */
353
+ export function acceptCssHMR(cssFiles: string[]) {
354
+ for (const file of cssFiles) {
355
+ reloadStylesheet(file);
356
+ }
357
+
358
+ return () => {
359
+ for (const file of cssFiles) {
360
+ removeStylesheet(file);
361
+ }
362
+ };
363
+ }
364
+
365
+ /** Options for creating an HMR wrapper. */
366
+ export interface HMRWrapperOptions {
367
+ /** Component tag name. */
368
+ tag: string;
369
+ /** Preserve scroll position. */
370
+ preserveScroll?: boolean;
371
+ /** Preserve hook state. */
372
+ preserveHookState?: boolean;
373
+ /** Custom error fallback. */
374
+ errorFallback?: (error: Error, retry: () => void) => VNode;
375
+ }
376
+
377
+ /** Creates an HMR-enabled component wrapper. */
378
+ export function createHMRWrapper<P extends Record<string, unknown>>(
379
+ hmrVersion: Signal<number>,
380
+ getComponent: () => FunctionComponent<P>,
381
+ options: HMRWrapperOptions,
382
+ ): FunctionComponent<P> {
383
+ const { tag, preserveScroll = true, errorFallback } = options;
384
+
385
+ return function HMRWrapper(props: P) {
386
+ void hmrVersion.value;
387
+
388
+ if (preserveScroll && typeof window !== "undefined") {
389
+ saveScrollPosition(tag);
390
+ }
391
+
392
+ const CurrentComponent = getComponent();
393
+ const inner = h(CurrentComponent, props);
394
+
395
+ return h(
396
+ HMRErrorBoundary,
397
+ {
398
+ tag,
399
+ hmrVersion,
400
+ fallback: errorFallback,
401
+ },
402
+ inner,
403
+ );
404
+ };
405
+ }
406
+
407
+ /** Dispatches an HMR event for external listeners. */
408
+ export function dispatchHMREvent(
409
+ type: "update" | "error" | "recover",
410
+ detail: { tag: string; error?: Error },
411
+ ) {
412
+ if (typeof document === "undefined") return;
413
+
414
+ document.dispatchEvent(
415
+ new CustomEvent(`sf:hmr:${type}`, {
416
+ detail,
417
+ bubbles: true,
418
+ }),
419
+ );
420
+ }
421
+
422
+ /** Registers an HMR event listener. */
423
+ export function onHMREvent(
424
+ type: "update" | "error" | "recover",
425
+ handler: (detail: { tag: string; error?: Error }) => void,
426
+ ) {
427
+ if (typeof document === "undefined") return () => {};
428
+
429
+ const listener = (e: Event) => {
430
+ handler((e as CustomEvent).detail);
431
+ };
432
+
433
+ document.addEventListener(`sf:hmr:${type}`, listener);
434
+ return () => document.removeEventListener(`sf:hmr:${type}`, listener);
435
+ }
436
+
437
+ interface HmrEntryOptions {
438
+ tag: string;
439
+ props: string[];
440
+ routesManifest: RoutesManifest;
441
+ BaseComponent: FunctionComponent<any>;
442
+ hmr: HmrApi;
443
+ cssFiles?: string[];
444
+ onCssUpdate?: () => void;
445
+ }
446
+
447
+ function initClientRuntime() {
448
+ if (typeof document !== "undefined") {
449
+ const runtime = getRuntime();
450
+ runtime.headContext ??= createHeadContext();
451
+ setHeadContext(runtime.headContext);
452
+ installHeadHoisting();
453
+ }
454
+ initHydrationCoordinator();
455
+ }
456
+
457
+ function createHmrEntryComponent(options: HmrEntryOptions) {
458
+ const { tag, routesManifest, BaseComponent, hmr, cssFiles = [], onCssUpdate } = options;
459
+
460
+ initClientRuntime();
461
+
462
+ let CurrentComponent = BaseComponent;
463
+ const hmrVersion = signal(0);
464
+ const navVersion = signal(0);
465
+
466
+ if (onCssUpdate) {
467
+ hmr.on("sf:css-update", onCssUpdate);
468
+ }
469
+
470
+ for (const file of cssFiles) {
471
+ hmr.on<string>(`sf:css:${file}`, (newCss) => {
472
+ if (!newCss) return;
473
+ stylesheets.update(file, newCss);
474
+ console.log(`[HMR] Updated stylesheet: ${file}`);
475
+ });
476
+ }
477
+
478
+ const captureHookState = (el: any) => {
479
+ if (!el?._vdom?.__hooks?.list) return;
480
+ const list = el._vdom.__hooks.list as Array<{ _value?: unknown; current?: unknown } | null>;
481
+ saveHookState(
482
+ tag,
483
+ list.map((hook) => (hook?._value !== undefined ? hook._value : hook?.current)),
484
+ );
485
+ };
486
+
487
+ const applyHookState = (el: any) => {
488
+ const saved = restoreHookState(tag);
489
+ if (!saved || !el?._vdom?.__hooks?.list) return;
490
+ const list = el._vdom.__hooks.list as Array<{ _value?: unknown; current?: unknown } | null>;
491
+ list.forEach((hook, i) => {
492
+ if (saved[i] !== undefined) {
493
+ if (hook?._value !== undefined) hook._value = saved[i];
494
+ else if (hook?.current !== undefined) hook.current = saved[i];
495
+ }
496
+ });
497
+ };
498
+
499
+ hmr.on<{ default?: FunctionComponent<any> }>(`sf:module:${tag}`, (newModule) => {
500
+ if (newModule?.default) {
501
+ saveScrollPosition(tag);
502
+
503
+ const el = document.querySelector(tag) as any;
504
+ captureHookState(el);
505
+
506
+ CurrentComponent = newModule.default;
507
+ console.log(`[HMR] Updated <${tag}>`);
508
+ hmrVersion.value++;
509
+
510
+ requestAnimationFrame(() => {
511
+ restoreScrollPosition(tag);
512
+ const nextEl = document.querySelector(tag) as any;
513
+ applyHookState(nextEl);
514
+ });
515
+
516
+ dispatchHMREvent("update", { tag });
517
+ }
518
+ });
519
+
520
+ hmr.dispose(() => {
521
+ console.log(`[HMR] Disposing <${tag}>`);
522
+ saveScrollPosition(tag);
523
+ const el = document.querySelector(tag) as any;
524
+ captureHookState(el);
525
+ });
526
+
527
+ let routerInitialized = false;
528
+ function ensureRouter() {
529
+ if (typeof document === "undefined") return null;
530
+ if (routerInitialized) {
531
+ try {
532
+ return getRouter();
533
+ } catch {
534
+ return null;
535
+ }
536
+ }
537
+ routerInitialized = true;
538
+ return initRouter(routesManifest).start();
539
+ }
540
+
541
+ return function Component(props: Record<string, unknown>) {
542
+ const deferredSignals = useMemo(() => new Map<string, Signal<unknown>>(), []);
543
+ const deferredVersion = useSignal(0);
544
+ void hmrVersion.value;
545
+ const navVer = navVersion.value;
546
+
547
+ const getOrCreateSignal = (key: string, value: unknown) => {
548
+ if (!deferredSignals.has(key)) {
549
+ deferredSignals.set(key, signal(value));
550
+ deferredVersion.value++;
551
+ } else {
552
+ deferredSignals.get(key)!.value = value;
553
+ }
554
+ };
555
+
556
+ useSignalEffect(() => {
557
+ const el = document.querySelector(tag) as any;
558
+ if (!el) return;
559
+
560
+ const extractDeferred = () => {
561
+ if (!el.isConnected) return;
562
+
563
+ if (el._sfDeferred) {
564
+ for (const [key, value] of Object.entries(el._sfDeferred)) {
565
+ getOrCreateSignal(key, value);
566
+ }
567
+ delete el._sfDeferred;
568
+ return;
569
+ }
570
+
571
+ const scripts = document.querySelectorAll(
572
+ `script[type="application/json"][data-island^="${tag}-deferred"]`,
573
+ );
574
+ if (!scripts.length) return;
575
+
576
+ void (async () => {
577
+ for (const script of scripts) {
578
+ if (!el.isConnected) return;
579
+ const id = script.getAttribute("data-island");
580
+ if (!id) continue;
581
+ const data = await extractDataIsland<Record<string, unknown>>(id);
582
+ if (data && typeof data === "object") {
583
+ for (const [key, value] of Object.entries(data)) {
584
+ getOrCreateSignal(key, value);
585
+ }
586
+ }
587
+ await new Promise((r) => setTimeout(r, 0));
588
+ }
589
+ })();
590
+ };
591
+
592
+ extractDeferred();
593
+
594
+ const hydrateHandler = (e: Event) => {
595
+ const detail = (e as CustomEvent).detail as Record<string, unknown>;
596
+ for (const [key, value] of Object.entries(detail)) {
597
+ getOrCreateSignal(key, value);
598
+ }
599
+ delete el._sfDeferred;
600
+ };
601
+
602
+ el.addEventListener("sf:hydrate", hydrateHandler);
603
+
604
+ const navHandler = () => setTimeout(extractDeferred, 0);
605
+ const rerenderHandler = () => {
606
+ navVersion.value++;
607
+ };
608
+ window.addEventListener("sf:navigate", navHandler);
609
+ el.addEventListener("sf:rerender", rerenderHandler);
610
+
611
+ ensureRouter();
612
+
613
+ return () => {
614
+ el.removeEventListener("sf:hydrate", hydrateHandler);
615
+ window.removeEventListener("sf:navigate", navHandler);
616
+ el.removeEventListener("sf:rerender", rerenderHandler);
617
+ };
618
+ });
619
+
620
+ const cleanProps: Record<string, unknown> = {};
621
+ for (const key in props) {
622
+ if (props[key] !== "undefined" && props[key] !== undefined) {
623
+ cleanProps[key] = props[key];
624
+ }
625
+ }
626
+
627
+ const _ver = deferredVersion.value;
628
+ void _ver;
629
+ void navVer;
630
+
631
+ const deferredProps: Record<string, unknown> = {};
632
+ for (const [key, sig] of deferredSignals) {
633
+ deferredProps[key] = sig.value;
634
+ }
635
+
636
+ const finalProps = { ...cleanProps, ...deferredProps };
637
+
638
+ return h(HMRErrorBoundary, { tag, hmrVersion }, h(CurrentComponent, finalProps));
639
+ };
640
+ }
641
+
642
+ export function initHmrEntry(options: HmrEntryOptions) {
643
+ const Component = createHmrEntryComponent(options);
644
+ if (!customElements.get(options.tag)) {
645
+ register(Component, options.tag, options.props, { shadow: false });
646
+ }
647
+ }