@absolutejs/absolute 0.19.0-beta.706 → 0.19.0-beta.707

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 (80) hide show
  1. package/dist/angular/components/constants.js +78 -0
  2. package/dist/angular/components/core/streamingSlotRegistrar.js +58 -0
  3. package/dist/angular/components/core/streamingSlotRegistry.js +114 -0
  4. package/dist/angular/components/defer-slot-payload.js +6 -0
  5. package/dist/angular/components/defer-slot-templates.directive.js +44 -0
  6. package/dist/angular/components/defer-slot.component.js +149 -0
  7. package/dist/angular/components/image.component.js +202 -0
  8. package/dist/angular/components/index.js +4 -0
  9. package/dist/angular/components/stream-slot.component.js +103 -0
  10. package/dist/angular/index.js +7 -9
  11. package/dist/angular/index.js.map +4 -4
  12. package/dist/angular/server.js +7 -9
  13. package/dist/angular/server.js.map +4 -4
  14. package/dist/build.js +19 -26
  15. package/dist/build.js.map +4 -4
  16. package/dist/cli/index.js +197 -144
  17. package/dist/client/index.js +2 -4
  18. package/dist/client/index.js.map +2 -2
  19. package/dist/dev/client/constants.ts +26 -0
  20. package/dist/dev/client/cssUtils.ts +307 -0
  21. package/dist/dev/client/domDiff.ts +226 -0
  22. package/dist/dev/client/domState.ts +421 -0
  23. package/dist/dev/client/domTracker.ts +61 -0
  24. package/dist/dev/client/errorOverlay.ts +184 -0
  25. package/dist/dev/client/frameworkDetect.ts +63 -0
  26. package/dist/dev/client/handlers/angular.ts +572 -0
  27. package/dist/dev/client/handlers/angularRuntime.ts +226 -0
  28. package/dist/dev/client/handlers/html.ts +364 -0
  29. package/dist/dev/client/handlers/htmx.ts +278 -0
  30. package/dist/dev/client/handlers/react.ts +108 -0
  31. package/dist/dev/client/handlers/rebuild.ts +153 -0
  32. package/dist/dev/client/handlers/svelte.ts +334 -0
  33. package/dist/dev/client/handlers/vue.ts +292 -0
  34. package/dist/dev/client/headPatch.ts +233 -0
  35. package/dist/dev/client/hmrClient.ts +273 -0
  36. package/dist/dev/client/hmrState.ts +14 -0
  37. package/dist/dev/client/moduleVersions.ts +62 -0
  38. package/dist/dev/client/reactRefreshSetup.ts +32 -0
  39. package/dist/index.js +29 -33
  40. package/dist/index.js.map +5 -5
  41. package/dist/islands/index.js +6 -8
  42. package/dist/islands/index.js.map +4 -4
  43. package/dist/react/browser.js +7 -7
  44. package/dist/react/browser.js.map +2 -2
  45. package/dist/react/components/browser/index.js +101 -101
  46. package/dist/react/components/index.js +104 -104
  47. package/dist/react/components/index.js.map +2 -2
  48. package/dist/react/index.js +17 -19
  49. package/dist/react/index.js.map +4 -4
  50. package/dist/react/server.js +3 -3
  51. package/dist/react/server.js.map +3 -3
  52. package/dist/src/angular/components/constants.d.ts +75 -0
  53. package/dist/src/angular/components/defer-slot-templates.directive.d.ts +7 -0
  54. package/dist/src/angular/components/defer-slot.component.d.ts +5 -2
  55. package/dist/src/angular/components/image.component.d.ts +5 -2
  56. package/dist/src/angular/components/index.d.ts +4 -4
  57. package/dist/src/angular/components/stream-slot.component.d.ts +3 -0
  58. package/dist/src/constants.d.ts +1 -0
  59. package/dist/svelte/components/AwaitSlot.svelte +39 -0
  60. package/dist/svelte/components/AwaitSlot.svelte.d.ts +2 -0
  61. package/dist/svelte/components/Head.svelte +144 -0
  62. package/dist/svelte/components/Head.svelte.d.ts +2 -0
  63. package/dist/svelte/components/Image.svelte +164 -0
  64. package/dist/svelte/components/Image.svelte.d.ts +5 -0
  65. package/dist/svelte/components/Island.svelte +71 -0
  66. package/dist/svelte/components/Island.svelte.d.ts +5 -0
  67. package/dist/svelte/components/JsonLd.svelte +21 -0
  68. package/dist/svelte/components/JsonLd.svelte.d.ts +2 -0
  69. package/dist/svelte/components/StreamSlot.svelte +41 -0
  70. package/dist/svelte/components/StreamSlot.svelte.d.ts +2 -0
  71. package/dist/svelte/index.js +7 -9
  72. package/dist/svelte/index.js.map +4 -4
  73. package/dist/svelte/server.js +4 -4
  74. package/dist/svelte/server.js.map +3 -3
  75. package/dist/types/globals.d.ts +122 -0
  76. package/dist/vue/index.js +7 -9
  77. package/dist/vue/index.js.map +4 -4
  78. package/dist/vue/server.js +3 -3
  79. package/dist/vue/server.js.map +3 -3
  80. package/package.json +1 -1
@@ -0,0 +1,233 @@
1
+ import type {} from '../../types/globals';
2
+ /* Head element patching for HMR updates (title, meta, favicon, etc.) */
3
+
4
+ const getLinkElementKey = (elem: Element) => {
5
+ const rel = (elem.getAttribute('rel') || '').toLowerCase();
6
+ if (rel === 'icon' || rel === 'shortcut icon' || rel === 'apple-touch-icon')
7
+ return `link:icon:${rel}`;
8
+ if (rel === 'stylesheet') return null;
9
+ if (rel === 'preconnect')
10
+ return `link:preconnect:${elem.getAttribute('href') || ''}`;
11
+ if (rel === 'preload')
12
+ return `link:preload:${elem.getAttribute('href') || ''}`;
13
+ if (rel === 'canonical') return 'link:canonical';
14
+ if (rel === 'dns-prefetch')
15
+ return `link:dns-prefetch:${elem.getAttribute('href') || ''}`;
16
+
17
+ return null;
18
+ };
19
+
20
+ const getHeadElementKey = (elem: Element) => {
21
+ const tag = elem.tagName.toLowerCase();
22
+
23
+ if (tag === 'title') return 'title';
24
+ if (tag === 'meta' && elem.hasAttribute('charset')) return 'meta:charset';
25
+ if (tag === 'meta' && elem.hasAttribute('name'))
26
+ return `meta:name:${elem.getAttribute('name')}`;
27
+ if (tag === 'meta' && elem.hasAttribute('property'))
28
+ return `meta:property:${elem.getAttribute('property')}`;
29
+ if (tag === 'meta' && elem.hasAttribute('http-equiv'))
30
+ return `meta:http-equiv:${elem.getAttribute('http-equiv')}`;
31
+
32
+ if (tag === 'link') return getLinkElementKey(elem);
33
+
34
+ if (tag === 'script' && elem.hasAttribute('data-hmr-id'))
35
+ return `script:hmr:${elem.getAttribute('data-hmr-id')}`;
36
+ if (tag === 'script') return null;
37
+ if (tag === 'base') return 'base';
38
+
39
+ return null;
40
+ };
41
+
42
+ const shouldPreserveElement = (elem: Element) => {
43
+ if (elem.hasAttribute('data-hmr-import-map')) return true;
44
+ if (elem.hasAttribute('data-hmr-client')) return true;
45
+ if (elem.hasAttribute('data-react-refresh-setup')) return true;
46
+
47
+ const attrs = Array.from(elem.attributes);
48
+ for (let idx = 0; idx < attrs.length; idx++) {
49
+ if (attrs[idx]?.name.startsWith('data-hmr-')) return true;
50
+ }
51
+
52
+ if (elem.tagName === 'SCRIPT') {
53
+ const src = elem.getAttribute('src') || '';
54
+ if (src.includes('htmx.min.js') || src.includes('htmx.js')) return true;
55
+ }
56
+
57
+ return false;
58
+ };
59
+
60
+ const updateTitleElement = (oldEl: Element, newEl: Element) => {
61
+ const newTitle = newEl.textContent || '';
62
+ if (oldEl.textContent === newTitle) return;
63
+ oldEl.textContent = newTitle;
64
+ document.title = newTitle;
65
+ };
66
+
67
+ const updateMetaElement = (oldEl: Element, newEl: Element) => {
68
+ const newContent = newEl.getAttribute('content');
69
+ const oldContent = oldEl.getAttribute('content');
70
+ if (oldContent !== newContent && newContent !== null) {
71
+ oldEl.setAttribute('content', newContent);
72
+ }
73
+ if (!newEl.hasAttribute('charset')) return;
74
+ const newCharset = newEl.getAttribute('charset');
75
+ if (oldEl.getAttribute('charset') !== newCharset && newCharset !== null) {
76
+ oldEl.setAttribute('charset', newCharset);
77
+ }
78
+ };
79
+
80
+ const updateFaviconHref = (
81
+ oldEl: Element,
82
+ newHref: string,
83
+ oldHref: string
84
+ ) => {
85
+ const [oldBase] = oldHref.split('?');
86
+ const [newBase] = newHref.split('?');
87
+ if (oldBase === newBase) return;
88
+ const cacheBustedHref = `${
89
+ newHref + (newHref.includes('?') ? '&' : '?')
90
+ }t=${Date.now()}`;
91
+ oldEl.setAttribute('href', cacheBustedHref);
92
+ };
93
+
94
+ const updateLinkElement = (oldEl: Element, newEl: Element) => {
95
+ const rel = (oldEl.getAttribute('rel') || '').toLowerCase();
96
+ const newHref = newEl.getAttribute('href');
97
+ const oldHref = oldEl.getAttribute('href');
98
+
99
+ const isIcon =
100
+ rel === 'icon' || rel === 'shortcut icon' || rel === 'apple-touch-icon';
101
+
102
+ if (isIcon && newHref && oldHref) {
103
+ updateFaviconHref(oldEl, newHref, oldHref);
104
+ } else if (!isIcon && newHref && oldHref !== newHref) {
105
+ oldEl.setAttribute('href', newHref);
106
+ }
107
+
108
+ const attrsToCheck = ['type', 'sizes', 'crossorigin', 'as', 'media'];
109
+ attrsToCheck.forEach((attr) => {
110
+ const newVal = newEl.getAttribute(attr);
111
+ const oldVal = oldEl.getAttribute(attr);
112
+ if (newVal !== null && oldVal !== newVal) {
113
+ oldEl.setAttribute(attr, newVal);
114
+ } else if (newVal === null && oldVal !== null) {
115
+ oldEl.removeAttribute(attr);
116
+ }
117
+ });
118
+ };
119
+
120
+ const updateBaseElement = (oldEl: Element, newEl: Element) => {
121
+ const newHref = newEl.getAttribute('href');
122
+ const newTarget = newEl.getAttribute('target');
123
+ if (newHref && oldEl.getAttribute('href') !== newHref) {
124
+ oldEl.setAttribute('href', newHref);
125
+ }
126
+ if (newTarget && oldEl.getAttribute('target') !== newTarget) {
127
+ oldEl.setAttribute('target', newTarget);
128
+ }
129
+ };
130
+
131
+ const updateHeadElement = (oldEl: Element, newEl: Element) => {
132
+ const tag = oldEl.tagName.toLowerCase();
133
+
134
+ if (tag === 'title') {
135
+ updateTitleElement(oldEl, newEl);
136
+
137
+ return;
138
+ }
139
+
140
+ if (tag === 'meta') {
141
+ updateMetaElement(oldEl, newEl);
142
+
143
+ return;
144
+ }
145
+
146
+ if (tag === 'link') {
147
+ updateLinkElement(oldEl, newEl);
148
+
149
+ return;
150
+ }
151
+
152
+ if (tag === 'base') {
153
+ updateBaseElement(oldEl, newEl);
154
+ }
155
+ };
156
+
157
+ const addHeadElement = (newEl: Element) => {
158
+ const clone = document.createElement(newEl.tagName.toLowerCase());
159
+ for (const attr of Array.from(newEl.attributes)) {
160
+ clone.setAttribute(attr.name, attr.value);
161
+ }
162
+ clone.textContent = newEl.textContent;
163
+ clone.setAttribute('data-hmr-source', 'patched');
164
+
165
+ const tag = newEl.tagName.toLowerCase();
166
+ const { head } = document;
167
+ let insertBefore: Node | null = null;
168
+
169
+ if (tag === 'title') {
170
+ insertBefore = head.firstChild;
171
+ } else if (tag === 'meta') {
172
+ const firstLink = head.querySelector('link');
173
+ const firstScript = head.querySelector('script');
174
+ insertBefore = firstLink || firstScript;
175
+ } else if (tag === 'link') {
176
+ const firstScript = head.querySelector('script');
177
+ insertBefore = firstScript;
178
+ }
179
+
180
+ if (insertBefore) {
181
+ head.insertBefore(clone, insertBefore);
182
+ } else {
183
+ head.appendChild(clone);
184
+ }
185
+ };
186
+
187
+ const removeStaleElement = (existingEl: Element) => {
188
+ if (shouldPreserveElement(existingEl)) return;
189
+ const tag = existingEl.tagName.toLowerCase();
190
+ const rel = existingEl.getAttribute('rel') || '';
191
+ if (tag === 'link' && rel === 'stylesheet') return;
192
+ existingEl.remove();
193
+ };
194
+
195
+ export const patchHeadInPlace = (newHeadHTML: string) => {
196
+ if (!newHeadHTML) return;
197
+
198
+ const tempDiv = document.createElement('div');
199
+ tempDiv.innerHTML = newHeadHTML;
200
+
201
+ const existingMap = new Map<string, Element>();
202
+ const newMap = new Map<string, Element>();
203
+
204
+ Array.from(document.head.children).forEach((elem) => {
205
+ if (shouldPreserveElement(elem)) return;
206
+ const key = getHeadElementKey(elem);
207
+ if (key) {
208
+ existingMap.set(key, elem);
209
+ }
210
+ });
211
+
212
+ Array.from(tempDiv.children).forEach((elem) => {
213
+ const key = getHeadElementKey(elem);
214
+ if (key) {
215
+ newMap.set(key, elem);
216
+ }
217
+ });
218
+
219
+ newMap.forEach((newEl, key) => {
220
+ const existingEl = existingMap.get(key);
221
+ if (existingEl) {
222
+ updateHeadElement(existingEl, newEl);
223
+ } else {
224
+ addHeadElement(newEl);
225
+ }
226
+ });
227
+
228
+ existingMap.forEach((existingEl, key) => {
229
+ if (!newMap.has(key)) {
230
+ removeStaleElement(existingEl);
231
+ }
232
+ });
233
+ };
@@ -0,0 +1,273 @@
1
+ import type {} from '../../types/globals';
2
+ /* AbsoluteJS HMR Client - Entry point
3
+ Initializes WebSocket connection, dispatches messages to framework handlers */
4
+
5
+ import { hmrState } from './hmrState';
6
+ import {
7
+ HMR_UPDATE_TIMEOUT_MS,
8
+ MAX_RECONNECT_ATTEMPTS,
9
+ PING_INTERVAL_MS,
10
+ RECONNECT_INITIAL_DELAY_MS,
11
+ RECONNECT_POLL_INTERVAL_MS,
12
+ WEBSOCKET_NORMAL_CLOSURE
13
+ } from './constants';
14
+ import { detectCurrentFramework } from './frameworkDetect';
15
+ import { hideErrorOverlay, showErrorOverlay } from './errorOverlay';
16
+ import { handleAngularUpdate } from './handlers/angular';
17
+ import { handleReactUpdate } from './handlers/react';
18
+ import { handleHTMLUpdate, handleScriptUpdate } from './handlers/html';
19
+ import { handleHTMXUpdate } from './handlers/htmx';
20
+ import { handleSvelteUpdate } from './handlers/svelte';
21
+ import { handleVueUpdate } from './handlers/vue';
22
+ import { reloadCSSStylesheets } from './cssUtils';
23
+ import {
24
+ handleFullReload,
25
+ handleManifest,
26
+ handleModuleUpdate,
27
+ handleRebuildComplete,
28
+ handleRebuildError
29
+ } from './handlers/rebuild';
30
+
31
+ // Initialize HMR globals
32
+ if (typeof window !== 'undefined') {
33
+ if (!window.__HMR_MANIFEST__) {
34
+ window.__HMR_MANIFEST__ = {};
35
+ }
36
+ if (!window.__HMR_MODULE_UPDATES__) {
37
+ window.__HMR_MODULE_UPDATES__ = [];
38
+ }
39
+ if (!window.__HMR_MODULE_VERSIONS__) {
40
+ window.__HMR_MODULE_VERSIONS__ = {};
41
+ }
42
+ if (!window.__HMR_SERVER_VERSIONS__) {
43
+ window.__HMR_SERVER_VERSIONS__ = {};
44
+ }
45
+ }
46
+
47
+ // Catch uncaught runtime errors and show the error overlay
48
+ window.addEventListener('error', (evt) => {
49
+ if (!evt.error) return;
50
+ showErrorOverlay({
51
+ framework: detectCurrentFramework() || undefined,
52
+ kind: 'runtime',
53
+ message:
54
+ evt.error instanceof Error
55
+ ? evt.error.stack || evt.error.message
56
+ : String(evt.error)
57
+ });
58
+ });
59
+
60
+ window.addEventListener('unhandledrejection', (evt) => {
61
+ if (!evt.reason) return;
62
+ showErrorOverlay({
63
+ framework: detectCurrentFramework() || undefined,
64
+ kind: 'runtime',
65
+ message:
66
+ evt.reason instanceof Error
67
+ ? evt.reason.stack || evt.reason.message
68
+ : String(evt.reason)
69
+ });
70
+ });
71
+
72
+ const hmrUpdateTypes = new Set([
73
+ 'angular-update',
74
+ 'react-update',
75
+ 'html-update',
76
+ 'htmx-update',
77
+ 'vue-update',
78
+ 'svelte-update',
79
+ 'style-update',
80
+ 'module-update',
81
+ 'rebuild-start'
82
+ ]);
83
+
84
+ type HMRMessage = {
85
+ data: {
86
+ affectedFrameworks?: string[];
87
+ column?: number;
88
+ error?: string;
89
+ file?: string;
90
+ framework?: string;
91
+ hasCSSChanges?: boolean;
92
+ hasComponentChanges?: boolean;
93
+ html?: string | { body?: string; head?: string; html?: string };
94
+ line?: number;
95
+ lineText?: string;
96
+ manifest?: Record<string, string>;
97
+ moduleVersions?: Record<string, number>;
98
+ pageModuleUrl?: string;
99
+ primarySource?: string;
100
+ scriptUrl?: string;
101
+ serverDuration?: number;
102
+ serverVersions?: Record<string, number>;
103
+ };
104
+ type: string;
105
+ };
106
+
107
+ const handleHMRMessage = (message: HMRMessage) => {
108
+ if (hmrUpdateTypes.has(message.type)) {
109
+ hmrState.isHMRUpdating = true;
110
+ setTimeout(() => {
111
+ hmrState.isHMRUpdating = false;
112
+ }, HMR_UPDATE_TIMEOUT_MS);
113
+ }
114
+
115
+ switch (message.type) {
116
+ case 'manifest':
117
+ handleManifest(message);
118
+ break;
119
+ case 'rebuild-start':
120
+ break;
121
+ case 'rebuild-complete':
122
+ handleRebuildComplete(message);
123
+ break;
124
+ case 'framework-update':
125
+ break;
126
+ case 'module-update':
127
+ hideErrorOverlay();
128
+ handleModuleUpdate(message);
129
+ break;
130
+ case 'react-update':
131
+ handleReactUpdate(message);
132
+ break;
133
+ case 'script-update':
134
+ hideErrorOverlay();
135
+ handleScriptUpdate(message);
136
+ break;
137
+ case 'html-update':
138
+ hideErrorOverlay();
139
+ handleHTMLUpdate(message);
140
+ break;
141
+ case 'htmx-update':
142
+ hideErrorOverlay();
143
+ handleHTMXUpdate(message);
144
+ break;
145
+ case 'svelte-update':
146
+ hideErrorOverlay();
147
+ handleSvelteUpdate(message);
148
+ break;
149
+ case 'vue-update':
150
+ hideErrorOverlay();
151
+ handleVueUpdate(message);
152
+ break;
153
+ case 'angular-update':
154
+ hideErrorOverlay();
155
+ handleAngularUpdate(message);
156
+ break;
157
+ case 'rebuild-error':
158
+ handleRebuildError(message);
159
+ break;
160
+ case 'full-reload':
161
+ handleFullReload();
162
+ break;
163
+ case 'pong':
164
+ break;
165
+ case 'style-update':
166
+ reloadCSSStylesheets(message.data.manifest ?? {});
167
+ break;
168
+ default:
169
+ break;
170
+ }
171
+ };
172
+
173
+ // Prevent multiple WebSocket connections
174
+ if (!(window.__HMR_WS__ && window.__HMR_WS__.readyState === WebSocket.OPEN)) {
175
+ // Determine WebSocket URL
176
+ const wsHost = location.hostname;
177
+ const wsPort =
178
+ location.port || (location.protocol === 'https:' ? '443' : '80');
179
+ const wsProtocol = location.protocol === 'https:' ? 'wss' : 'ws';
180
+ const wsUrl = `${wsProtocol}://${wsHost}:${wsPort}/hmr`;
181
+
182
+ const wsc = new WebSocket(wsUrl);
183
+ window.__HMR_WS__ = wsc;
184
+
185
+ wsc.onopen = function () {
186
+ hmrState.isConnected = true;
187
+ sessionStorage.setItem('__HMR_CONNECTED__', 'true');
188
+
189
+ const currentFramework = detectCurrentFramework();
190
+ wsc.send(
191
+ JSON.stringify({
192
+ framework: currentFramework,
193
+ type: 'ready'
194
+ })
195
+ );
196
+
197
+ if (hmrState.reconnectTimeout) {
198
+ clearTimeout(hmrState.reconnectTimeout);
199
+ hmrState.reconnectTimeout = null;
200
+ }
201
+
202
+ hmrState.pingInterval = setInterval(() => {
203
+ if (wsc.readyState === WebSocket.OPEN && hmrState.isConnected) {
204
+ wsc.send(JSON.stringify({ type: 'ping' }));
205
+ }
206
+ }, PING_INTERVAL_MS);
207
+ };
208
+
209
+ wsc.onmessage = function (event: MessageEvent) {
210
+ let message;
211
+ try {
212
+ message = JSON.parse(event.data);
213
+ } catch {
214
+ return;
215
+ }
216
+
217
+ handleHMRMessage(message);
218
+ };
219
+
220
+ wsc.onclose = function (event: CloseEvent) {
221
+ hmrState.isConnected = false;
222
+
223
+ if (hmrState.pingInterval) {
224
+ clearInterval(hmrState.pingInterval);
225
+ hmrState.pingInterval = null;
226
+ }
227
+
228
+ if (event.code !== WEBSOCKET_NORMAL_CLOSURE) {
229
+ let attempts = 0;
230
+ hmrState.reconnectTimeout = setTimeout(function pollServer() {
231
+ attempts++;
232
+ if (attempts > MAX_RECONNECT_ATTEMPTS) return;
233
+
234
+ fetch('/hmr-status', { cache: 'no-store' })
235
+ .then((res) => {
236
+ if (res.ok) {
237
+ window.location.reload();
238
+ } else {
239
+ hmrState.reconnectTimeout = setTimeout(
240
+ pollServer,
241
+ RECONNECT_POLL_INTERVAL_MS
242
+ );
243
+ }
244
+
245
+ return undefined;
246
+ })
247
+ .catch(() => {
248
+ hmrState.reconnectTimeout = setTimeout(
249
+ pollServer,
250
+ RECONNECT_POLL_INTERVAL_MS
251
+ );
252
+ });
253
+ }, RECONNECT_INITIAL_DELAY_MS);
254
+ }
255
+ };
256
+
257
+ wsc.onerror = function () {
258
+ hmrState.isConnected = false;
259
+ };
260
+
261
+ window.addEventListener('beforeunload', () => {
262
+ if (hmrState.isHMRUpdating) {
263
+ if (hmrState.pingInterval) clearInterval(hmrState.pingInterval);
264
+ if (hmrState.reconnectTimeout)
265
+ clearTimeout(hmrState.reconnectTimeout);
266
+
267
+ return;
268
+ }
269
+
270
+ if (hmrState.pingInterval) clearInterval(hmrState.pingInterval);
271
+ if (hmrState.reconnectTimeout) clearTimeout(hmrState.reconnectTimeout);
272
+ });
273
+ }
@@ -0,0 +1,14 @@
1
+ import type {} from '../../types/globals';
2
+ export const hmrState: {
3
+ isConnected: boolean;
4
+ isFirstHMRUpdate: boolean;
5
+ isHMRUpdating: boolean;
6
+ pingInterval: ReturnType<typeof setInterval> | null;
7
+ reconnectTimeout: ReturnType<typeof setTimeout> | null;
8
+ } = {
9
+ isConnected: false,
10
+ isFirstHMRUpdate: true,
11
+ isHMRUpdating: false,
12
+ pingInterval: null,
13
+ reconnectTimeout: null
14
+ };
@@ -0,0 +1,62 @@
1
+ import type {} from '../../types/globals';
2
+ /* Module version validation and sync */
3
+
4
+ export const checkModuleVersions = (
5
+ serverVersions: Record<string, number> | undefined,
6
+ clientVersions: Record<string, number> | undefined
7
+ ) => {
8
+ if (!serverVersions || !clientVersions) {
9
+ return { needsSync: false, stale: [] };
10
+ }
11
+
12
+ const stale = Object.entries(serverVersions)
13
+ .filter(([modulePath, serverVersion]) => {
14
+ const clientVersion = clientVersions[modulePath];
15
+
16
+ return clientVersion === undefined || clientVersion < serverVersion;
17
+ })
18
+ .map(([modulePath]) => modulePath);
19
+
20
+ return { needsSync: stale.length > 0, stale };
21
+ };
22
+
23
+ const resolveManifestPath = (
24
+ modulePath: string,
25
+ manifest: Record<string, string> | undefined
26
+ ) => {
27
+ if (!manifest) {
28
+ return modulePath;
29
+ }
30
+ for (const key of Object.keys(manifest)) {
31
+ const path = manifest[key] ?? modulePath;
32
+ if (path === modulePath || path.includes(modulePath)) {
33
+ return path;
34
+ }
35
+ }
36
+
37
+ return modulePath;
38
+ };
39
+
40
+ export const prefetchModules = (
41
+ modulePaths: string[],
42
+ manifest: Record<string, string> | undefined
43
+ ) => {
44
+ const prefetchPromises: Promise<unknown>[] = [];
45
+
46
+ for (const modulePath of modulePaths) {
47
+ const manifestPath = resolveManifestPath(modulePath, manifest);
48
+
49
+ const cacheBuster = `?t=${Date.now()}`;
50
+ const fullPath = manifestPath.startsWith('/')
51
+ ? manifestPath + cacheBuster
52
+ : `/${manifestPath}${cacheBuster}`;
53
+
54
+ prefetchPromises.push(
55
+ import(fullPath).catch(() => {
56
+ /* ignore */
57
+ })
58
+ );
59
+ }
60
+
61
+ return Promise.all(prefetchPromises);
62
+ };
@@ -0,0 +1,32 @@
1
+ import type {} from '../../types/globals';
2
+ /* React Refresh runtime setup — must be imported before any component modules.
3
+ Bun's reactFastRefresh flag injects $RefreshSig$/$RefreshReg$ calls into
4
+ component code. This module ensures those globals exist before components
5
+ initialize.
6
+
7
+ IMPORTANT: This module is idempotent. On HMR re-import the existing runtime
8
+ is preserved so new component registrations feed into the SAME RefreshRuntime
9
+ instance that owns the current React tree. */
10
+
11
+
12
+ import * as RefreshRuntime from 'react-refresh/runtime';
13
+
14
+ if (!window.$RefreshRuntime$) {
15
+ RefreshRuntime.injectIntoGlobalHook(window);
16
+ window.$RefreshRuntime$ = RefreshRuntime;
17
+ window.$RefreshReg$ = (type: unknown, id: string) =>
18
+ RefreshRuntime.register(type, id);
19
+ window.$RefreshSig$ = () =>
20
+ RefreshRuntime.createSignatureFunctionForTransform();
21
+
22
+ // Replay buffered registrations from the bootstrap script.
23
+ // The SSR HTML injects a buffering $RefreshReg$ that captures
24
+ // registrations before the runtime is ready.
25
+ const buffer = window.__REFRESH_BUFFER__;
26
+ if (buffer) {
27
+ for (const [type, id] of buffer) {
28
+ RefreshRuntime.register(type, id);
29
+ }
30
+ window.__REFRESH_BUFFER__ = undefined;
31
+ }
32
+ }