@nativescript/vite 8.0.0-alpha.1 → 8.0.0-alpha.3

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 (145) hide show
  1. package/configuration/angular.d.ts +1 -1
  2. package/configuration/angular.js +323 -119
  3. package/configuration/angular.js.map +1 -1
  4. package/configuration/base.js +41 -24
  5. package/configuration/base.js.map +1 -1
  6. package/configuration/javascript.js +3 -3
  7. package/configuration/javascript.js.map +1 -1
  8. package/configuration/solid.js +7 -0
  9. package/configuration/solid.js.map +1 -1
  10. package/configuration/typescript.js +3 -3
  11. package/configuration/typescript.js.map +1 -1
  12. package/helpers/angular/angular-linker.js +39 -34
  13. package/helpers/angular/angular-linker.js.map +1 -1
  14. package/helpers/angular/inline-decorator-component-templates.d.ts +3 -0
  15. package/helpers/angular/inline-decorator-component-templates.js +400 -0
  16. package/helpers/angular/inline-decorator-component-templates.js.map +1 -0
  17. package/helpers/angular/shared-linker.d.ts +7 -0
  18. package/helpers/angular/shared-linker.js +37 -1
  19. package/helpers/angular/shared-linker.js.map +1 -1
  20. package/helpers/angular/synthesize-decorator-ctor-parameters.d.ts +1 -0
  21. package/helpers/angular/synthesize-decorator-ctor-parameters.js +256 -0
  22. package/helpers/angular/synthesize-decorator-ctor-parameters.js.map +1 -0
  23. package/helpers/angular/synthesize-injectable-factories.d.ts +3 -0
  24. package/helpers/angular/synthesize-injectable-factories.js +414 -0
  25. package/helpers/angular/synthesize-injectable-factories.js.map +1 -0
  26. package/helpers/commonjs-plugins.d.ts +5 -2
  27. package/helpers/commonjs-plugins.js +126 -0
  28. package/helpers/commonjs-plugins.js.map +1 -1
  29. package/helpers/esbuild-platform-resolver.js +5 -5
  30. package/helpers/esbuild-platform-resolver.js.map +1 -1
  31. package/helpers/external-configs.d.ts +9 -1
  32. package/helpers/external-configs.js +31 -6
  33. package/helpers/external-configs.js.map +1 -1
  34. package/helpers/import-meta-path.d.ts +4 -0
  35. package/helpers/import-meta-path.js +5 -0
  36. package/helpers/import-meta-path.js.map +1 -0
  37. package/helpers/import-specifier.d.ts +1 -0
  38. package/helpers/import-specifier.js +18 -0
  39. package/helpers/import-specifier.js.map +1 -0
  40. package/helpers/main-entry.d.ts +5 -2
  41. package/helpers/main-entry.js +112 -95
  42. package/helpers/main-entry.js.map +1 -1
  43. package/helpers/nativeclass-transform.js +8 -127
  44. package/helpers/nativeclass-transform.js.map +1 -1
  45. package/helpers/nativeclass-transformer-plugin.d.ts +12 -1
  46. package/helpers/nativeclass-transformer-plugin.js +175 -36
  47. package/helpers/nativeclass-transformer-plugin.js.map +1 -1
  48. package/hmr/client/css-handler.js +60 -20
  49. package/hmr/client/css-handler.js.map +1 -1
  50. package/hmr/client/index.js +524 -23
  51. package/hmr/client/index.js.map +1 -1
  52. package/hmr/client/utils.js +57 -6
  53. package/hmr/client/utils.js.map +1 -1
  54. package/hmr/entry-runtime.d.ts +10 -0
  55. package/hmr/entry-runtime.js +263 -21
  56. package/hmr/entry-runtime.js.map +1 -1
  57. package/hmr/frameworks/angular/client/index.d.ts +2 -1
  58. package/hmr/frameworks/angular/client/index.js +72 -19
  59. package/hmr/frameworks/angular/client/index.js.map +1 -1
  60. package/hmr/frameworks/angular/server/linker.js +36 -2
  61. package/hmr/frameworks/angular/server/linker.js.map +1 -1
  62. package/hmr/frameworks/angular/server/strategy.js +20 -5
  63. package/hmr/frameworks/angular/server/strategy.js.map +1 -1
  64. package/hmr/frameworks/typescript/server/strategy.js +8 -2
  65. package/hmr/frameworks/typescript/server/strategy.js.map +1 -1
  66. package/hmr/helpers/ast-normalizer.js +22 -10
  67. package/hmr/helpers/ast-normalizer.js.map +1 -1
  68. package/hmr/server/constants.d.ts +1 -0
  69. package/hmr/server/constants.js +2 -0
  70. package/hmr/server/constants.js.map +1 -1
  71. package/hmr/server/core-sanitize.d.ts +43 -0
  72. package/hmr/server/core-sanitize.js +219 -13
  73. package/hmr/server/core-sanitize.js.map +1 -1
  74. package/hmr/server/import-map.d.ts +65 -0
  75. package/hmr/server/import-map.js +219 -0
  76. package/hmr/server/import-map.js.map +1 -0
  77. package/hmr/server/index.d.ts +2 -1
  78. package/hmr/server/index.js.map +1 -1
  79. package/hmr/server/runtime-graph-filter.d.ts +5 -0
  80. package/hmr/server/runtime-graph-filter.js +21 -0
  81. package/hmr/server/runtime-graph-filter.js.map +1 -0
  82. package/hmr/server/shared-transform-request.d.ts +12 -0
  83. package/hmr/server/shared-transform-request.js +137 -0
  84. package/hmr/server/shared-transform-request.js.map +1 -0
  85. package/hmr/server/vite-plugin.d.ts +21 -1
  86. package/hmr/server/vite-plugin.js +443 -22
  87. package/hmr/server/vite-plugin.js.map +1 -1
  88. package/hmr/server/websocket-angular-entry.d.ts +2 -0
  89. package/hmr/server/websocket-angular-entry.js +68 -0
  90. package/hmr/server/websocket-angular-entry.js.map +1 -0
  91. package/hmr/server/websocket-angular-hot-update.d.ts +61 -0
  92. package/hmr/server/websocket-angular-hot-update.js +239 -0
  93. package/hmr/server/websocket-angular-hot-update.js.map +1 -0
  94. package/hmr/server/websocket-core-bridge.d.ts +23 -0
  95. package/hmr/server/websocket-core-bridge.js +360 -0
  96. package/hmr/server/websocket-core-bridge.js.map +1 -0
  97. package/hmr/server/websocket-graph-upsert.d.ts +6 -0
  98. package/hmr/server/websocket-graph-upsert.js +13 -0
  99. package/hmr/server/websocket-graph-upsert.js.map +1 -0
  100. package/hmr/server/websocket-module-bindings.d.ts +6 -0
  101. package/hmr/server/websocket-module-bindings.js +471 -0
  102. package/hmr/server/websocket-module-bindings.js.map +1 -0
  103. package/hmr/server/websocket-module-specifiers.d.ts +37 -0
  104. package/hmr/server/websocket-module-specifiers.js +637 -0
  105. package/hmr/server/websocket-module-specifiers.js.map +1 -0
  106. package/hmr/server/websocket.d.ts +26 -3
  107. package/hmr/server/websocket.js +1402 -678
  108. package/hmr/server/websocket.js.map +1 -1
  109. package/hmr/shared/package-classifier.d.ts +9 -0
  110. package/hmr/shared/package-classifier.js +58 -0
  111. package/hmr/shared/package-classifier.js.map +1 -0
  112. package/hmr/shared/runtime/browser-runtime-contract.d.ts +64 -0
  113. package/hmr/shared/runtime/browser-runtime-contract.js +54 -0
  114. package/hmr/shared/runtime/browser-runtime-contract.js.map +1 -0
  115. package/hmr/shared/runtime/dev-overlay.d.ts +38 -0
  116. package/hmr/shared/runtime/dev-overlay.js +675 -0
  117. package/hmr/shared/runtime/dev-overlay.js.map +1 -0
  118. package/hmr/shared/runtime/http-only-boot.d.ts +1 -0
  119. package/hmr/shared/runtime/http-only-boot.js +53 -6
  120. package/hmr/shared/runtime/http-only-boot.js.map +1 -1
  121. package/hmr/shared/runtime/module-provenance.d.ts +1 -0
  122. package/hmr/shared/runtime/module-provenance.js +66 -0
  123. package/hmr/shared/runtime/module-provenance.js.map +1 -0
  124. package/hmr/shared/runtime/platform-polyfills.d.ts +26 -0
  125. package/hmr/shared/runtime/platform-polyfills.js +122 -0
  126. package/hmr/shared/runtime/platform-polyfills.js.map +1 -0
  127. package/hmr/shared/runtime/root-placeholder.d.ts +1 -0
  128. package/hmr/shared/runtime/root-placeholder.js +576 -76
  129. package/hmr/shared/runtime/root-placeholder.js.map +1 -1
  130. package/hmr/shared/runtime/session-bootstrap.d.ts +1 -0
  131. package/hmr/shared/runtime/session-bootstrap.js +146 -0
  132. package/hmr/shared/runtime/session-bootstrap.js.map +1 -0
  133. package/hmr/shared/runtime/vendor-bootstrap.js +51 -6
  134. package/hmr/shared/runtime/vendor-bootstrap.js.map +1 -1
  135. package/hmr/shared/vendor/manifest.d.ts +7 -0
  136. package/hmr/shared/vendor/manifest.js +363 -23
  137. package/hmr/shared/vendor/manifest.js.map +1 -1
  138. package/hmr/shared/vendor/registry.js +104 -7
  139. package/hmr/shared/vendor/registry.js.map +1 -1
  140. package/package.json +12 -2
  141. package/runtime/core-aliases-early.js +83 -32
  142. package/runtime/core-aliases-early.js.map +1 -1
  143. package/shims/solid-jsx-runtime.d.ts +7 -0
  144. package/shims/solid-jsx-runtime.js +17 -0
  145. package/shims/solid-jsx-runtime.js.map +1 -0
@@ -8,6 +8,38 @@
8
8
  import { setHMRWsUrl, getHMRWsUrl, pendingModuleFetches, deriveHttpOrigin, setHttpOriginForVite, moduleFetchCache, requestModuleFromServer, getHttpOriginForVite, normalizeSpec, hmrMetrics, graph, setGraphVersion, getGraphVersion, getCurrentApp, getRootFrame, setCurrentApp, setRootFrame, getCore } from './utils.js';
9
9
  import { handleCssUpdates } from './css-handler.js';
10
10
  const VERBOSE = typeof __NS_ENV_VERBOSE__ !== 'undefined' && __NS_ENV_VERBOSE__;
11
+ function resolveTargetFlavor() {
12
+ try {
13
+ if (typeof __NS_TARGET_FLAVOR__ !== 'undefined' && __NS_TARGET_FLAVOR__) {
14
+ return __NS_TARGET_FLAVOR__;
15
+ }
16
+ }
17
+ catch { }
18
+ try {
19
+ const g = globalThis;
20
+ if (typeof g.__NS_TARGET_FLAVOR__ === 'string' && g.__NS_TARGET_FLAVOR__) {
21
+ return g.__NS_TARGET_FLAVOR__;
22
+ }
23
+ if (typeof g.__NS_HMR_BROWSER_RUNTIME_TARGET_FLAVOR__ === 'string' && g.__NS_HMR_BROWSER_RUNTIME_TARGET_FLAVOR__) {
24
+ return g.__NS_HMR_BROWSER_RUNTIME_TARGET_FLAVOR__;
25
+ }
26
+ if (typeof g.__reboot_ng_modules__ === 'function') {
27
+ return 'angular';
28
+ }
29
+ if (g.__VUE_HMR_RUNTIME__ || g.__NS_HMR_VUE_SFC_REGISTRY__) {
30
+ return 'vue';
31
+ }
32
+ }
33
+ catch { }
34
+ return undefined;
35
+ }
36
+ const TARGET_FLAVOR = resolveTargetFlavor();
37
+ try {
38
+ if (TARGET_FLAVOR && !globalThis.__NS_TARGET_FLAVOR__) {
39
+ globalThis.__NS_TARGET_FLAVOR__ = TARGET_FLAVOR;
40
+ }
41
+ }
42
+ catch { }
11
43
  const APP_ROOT_VIRTUAL = typeof __NS_APP_ROOT_VIRTUAL__ === 'string' && __NS_APP_ROOT_VIRTUAL__ ? __NS_APP_ROOT_VIRTUAL__ : '/src';
12
44
  const APP_VIRTUAL_WITH_SLASH = APP_ROOT_VIRTUAL.endsWith('/') ? APP_ROOT_VIRTUAL : `${APP_ROOT_VIRTUAL}/`;
13
45
  const APP_MAIN_ENTRY_SPEC = `${APP_VIRTUAL_WITH_SLASH}app.ts`;
@@ -29,8 +61,9 @@ function ensureCoreAliasesOnGlobalThis() {
29
61
  }
30
62
  catch { }
31
63
  try {
32
- if (A && !g.Application)
64
+ if (A && (!g.Application || typeof g.Application.run !== 'function' || typeof g.Application.on !== 'function' || typeof g.Application.resetRootView !== 'function')) {
33
65
  g.Application = A;
66
+ }
34
67
  }
35
68
  catch { }
36
69
  try {
@@ -43,12 +76,78 @@ function ensureCoreAliasesOnGlobalThis() {
43
76
  }
44
77
  // Apply once on module evaluation
45
78
  ensureCoreAliasesOnGlobalThis();
79
+ function getHmrOverlayApi() {
80
+ try {
81
+ return globalThis.__NS_HMR_DEV_OVERLAY__ || null;
82
+ }
83
+ catch { }
84
+ return null;
85
+ }
86
+ function setConnectionOverlayStage(stage, detail) {
87
+ try {
88
+ const api = getHmrOverlayApi();
89
+ if (api && typeof api.setConnectionStage === 'function') {
90
+ api.setConnectionStage(stage, { detail });
91
+ }
92
+ }
93
+ catch { }
94
+ }
95
+ function hideConnectionOverlay() {
96
+ try {
97
+ const api = getHmrOverlayApi();
98
+ if (api && typeof api.hide === 'function') {
99
+ api.hide('healthy');
100
+ }
101
+ }
102
+ catch { }
103
+ }
104
+ let connectionOverlayTimer = null;
105
+ let connectionOverlayVisible = false;
106
+ let hasOpenedHmrSocket = false;
107
+ let awaitingHealthyHmrMessage = false;
108
+ let pendingConnectionOverlayStage = 'connecting';
109
+ let pendingConnectionOverlayDetail = '';
110
+ function clearConnectionOverlayTimer() {
111
+ if (connectionOverlayTimer) {
112
+ clearTimeout(connectionOverlayTimer);
113
+ connectionOverlayTimer = null;
114
+ }
115
+ }
116
+ function showConnectionOverlayNow(stage, detail) {
117
+ pendingConnectionOverlayStage = stage;
118
+ pendingConnectionOverlayDetail = detail || '';
119
+ connectionOverlayVisible = true;
120
+ setConnectionOverlayStage(stage, detail);
121
+ }
122
+ function scheduleConnectionOverlay(stage, detail, delayMs = 1200) {
123
+ pendingConnectionOverlayStage = stage;
124
+ pendingConnectionOverlayDetail = detail || '';
125
+ clearConnectionOverlayTimer();
126
+ connectionOverlayTimer = setTimeout(() => {
127
+ showConnectionOverlayNow(pendingConnectionOverlayStage, pendingConnectionOverlayDetail);
128
+ }, delayMs);
129
+ }
130
+ function updateConnectionOverlay(stage, detail) {
131
+ pendingConnectionOverlayStage = stage;
132
+ pendingConnectionOverlayDetail = detail || '';
133
+ if (connectionOverlayVisible) {
134
+ showConnectionOverlayNow(stage, detail);
135
+ }
136
+ }
137
+ function markHmrConnectionHealthy() {
138
+ awaitingHealthyHmrMessage = false;
139
+ clearConnectionOverlayTimer();
140
+ if (connectionOverlayVisible) {
141
+ connectionOverlayVisible = false;
142
+ hideConnectionOverlay();
143
+ }
144
+ }
46
145
  /**
47
146
  * Flavor hooks
48
147
  */
49
- import { installNsVueDevShims, ensureBackWrapperInstalled, getRootForVue, loadSfcComponent, ensureVueGlobals, ensurePiniaOnApp, addSfcMapping, recordVuePayloadChanges, handleVueSfcRegistry, handleVueSfcRegistryUpdate } from '../frameworks/vue/client/index.js';
148
+ import { installNsVueDevShims, ensureBackWrapperInstalled, getRootForVue, loadSfcComponent, ensureVueGlobals, ensurePiniaOnApp, addSfcMapping, recordVuePayloadChanges, handleVueSfcRegistry, handleVueSfcRegistryUpdate, sfcArtifactMap } from '../frameworks/vue/client/index.js';
50
149
  import { handleAngularHotUpdateMessage, installAngularHmrClientHooks } from '../frameworks/angular/client/index.js';
51
- switch (__NS_TARGET_FLAVOR__) {
150
+ switch (TARGET_FLAVOR) {
52
151
  case 'vue':
53
152
  installNsVueDevShims();
54
153
  break;
@@ -60,6 +159,10 @@ switch (__NS_TARGET_FLAVOR__) {
60
159
  let initialMounted = !!globalThis.__NS_HMR_BOOT_COMPLETE__;
61
160
  // Prevent duplicate initial-mount scheduling across rapid full-graph broadcasts and re-evaluations
62
161
  let initialMounting = !!globalThis.__NS_HMR_INITIAL_MOUNT_IN_PROGRESS__;
162
+ // Track whether the first full-graph has been received. Before the full-graph,
163
+ // delta messages are just the server discovering modules during initial boot —
164
+ // NOT actual code changes. Re-imports must be gated behind this flag.
165
+ let hasReceivedFullGraph = false;
63
166
  // TypeScript flavor: track registry modules and inferred main id
64
167
  let tsModuleSet = null;
65
168
  let tsMainId = null;
@@ -94,10 +197,14 @@ function applyFullGraph(payload) {
94
197
  console.log('[hmr][graph] full graph applied version', getGraphVersion(), 'modules=', graph.size);
95
198
  // Guarded initial mount rescue: if app hasn't replaced the placeholder shortly after graph arrives,
96
199
  // perform a one-time mount. This waits briefly to let the app's main entry start() run first to avoid double mounts.
200
+ // TypeScript flavor: skip the rescue entirely. The HTTP boot will load main.ts which
201
+ // calls Application.run() (patched to resetRootView) — the rescue is redundant and
202
+ // causes a double-mount race (rescue fires at 450ms, then main.ts fires ~1s later,
203
+ // causing a visual flash and leaving the app in an inconsistent state).
97
204
  try {
98
205
  const g = globalThis;
99
206
  const bootDone = !!g.__NS_HMR_BOOT_COMPLETE__;
100
- if (!bootDone && !initialMounted && !initialMounting && !g.__NS_HMR_RESCUE_SCHEDULED__) {
207
+ if (!bootDone && !initialMounted && !initialMounting && !g.__NS_HMR_RESCUE_SCHEDULED__ && TARGET_FLAVOR !== 'typescript') {
101
208
  // simple snapshot helpers
102
209
  const getTopmost = () => {
103
210
  try {
@@ -170,7 +277,7 @@ function applyFullGraph(payload) {
170
277
  if (VERBOSE)
171
278
  console.log('[hmr][init] placeholder persists after delay; evaluating rescue policy');
172
279
  // Flavor-specific rescue handling
173
- if (__NS_TARGET_FLAVOR__ === 'typescript') {
280
+ if (TARGET_FLAVOR === 'typescript') {
174
281
  // For TS apps, perform a one-time resetRootView to the conventional
175
282
  // app root module. This mimics what Application.run would do and
176
283
  // replaces the placeholder with the real UI without trying to
@@ -211,7 +318,7 @@ function applyFullGraph(payload) {
211
318
  return;
212
319
  }
213
320
  let candidate = null;
214
- switch (__NS_TARGET_FLAVOR__) {
321
+ switch (TARGET_FLAVOR) {
215
322
  case 'vue': {
216
323
  const appEntry = graph.get(APP_MAIN_ENTRY_SPEC);
217
324
  if (appEntry && Array.isArray(appEntry.deps)) {
@@ -227,6 +334,19 @@ function applyFullGraph(payload) {
227
334
  }
228
335
  }
229
336
  }
337
+ // Fallback: when the module graph is empty (Vite 7+ may not populate it
338
+ // before the first full-graph broadcast), check the SFC artifact registry
339
+ // which is populated from the ns:vue-sfc-registry message.
340
+ if (!candidate && sfcArtifactMap.size > 0) {
341
+ for (const id of sfcArtifactMap.keys()) {
342
+ if (/\.vue$/i.test(id)) {
343
+ candidate = id;
344
+ if (VERBOSE)
345
+ console.log('[hmr][init] rescue candidate from SFC registry:', id);
346
+ break;
347
+ }
348
+ }
349
+ }
230
350
  break;
231
351
  }
232
352
  }
@@ -240,7 +360,7 @@ function applyFullGraph(payload) {
240
360
  (async () => {
241
361
  try {
242
362
  let comp = null;
243
- switch (__NS_TARGET_FLAVOR__) {
363
+ switch (TARGET_FLAVOR) {
244
364
  case 'vue':
245
365
  comp = await loadSfcComponent(candidate, 'initial_mount_rescue');
246
366
  break;
@@ -285,7 +405,7 @@ function applyFullGraph(payload) {
285
405
  // to avoid double-mount races that can cause duplicate navigation logs.
286
406
  if (ALLOW_INITIAL_MOUNT && !initialMounted && !bootDone && !bootInProgress && !getCurrentApp() && !getRootFrame()) {
287
407
  let candidate = null;
288
- switch (__NS_TARGET_FLAVOR__) {
408
+ switch (TARGET_FLAVOR) {
289
409
  case 'vue': {
290
410
  const appEntry = graph.get(APP_MAIN_ENTRY_SPEC);
291
411
  if (appEntry && Array.isArray(appEntry.deps)) {
@@ -301,6 +421,17 @@ function applyFullGraph(payload) {
301
421
  }
302
422
  }
303
423
  }
424
+ // Fallback: SFC registry (same as rescue mount above)
425
+ if (!candidate && sfcArtifactMap.size > 0) {
426
+ for (const id of sfcArtifactMap.keys()) {
427
+ if (/\.vue$/i.test(id)) {
428
+ candidate = id;
429
+ if (VERBOSE)
430
+ console.log('[hmr][init] initial mount candidate from SFC registry:', id);
431
+ break;
432
+ }
433
+ }
434
+ }
304
435
  break;
305
436
  }
306
437
  case 'typescript': {
@@ -318,7 +449,7 @@ function applyFullGraph(payload) {
318
449
  (async () => {
319
450
  try {
320
451
  if (VERBOSE)
321
- console.log('[hmr][init] mounting initial root from', candidate, 'flavor=', __NS_TARGET_FLAVOR__);
452
+ console.log('[hmr][init] mounting initial root from', candidate, 'flavor=', TARGET_FLAVOR);
322
453
  // Android-only: avoid racing entry-runtime reset and Activity bring-up
323
454
  try {
324
455
  const g = globalThis;
@@ -352,7 +483,7 @@ function applyFullGraph(payload) {
352
483
  }
353
484
  catch { }
354
485
  let comp = null;
355
- switch (__NS_TARGET_FLAVOR__) {
486
+ switch (TARGET_FLAVOR) {
356
487
  case 'vue':
357
488
  comp = await loadSfcComponent(candidate, 'initial_mount');
358
489
  break;
@@ -401,7 +532,7 @@ function applyFullGraph(payload) {
401
532
  })();
402
533
  }
403
534
  else if (VERBOSE) {
404
- console.warn('[hmr][init] no component found in graph to mount initially for flavor', __NS_TARGET_FLAVOR__);
535
+ console.warn('[hmr][init] no component found in graph to mount initially for flavor', TARGET_FLAVOR);
405
536
  }
406
537
  }
407
538
  }
@@ -427,7 +558,7 @@ function applyDelta(payload) {
427
558
  setGraphVersion(payload.newVersion);
428
559
  }
429
560
  const changed = payload.changed || [];
430
- switch (__NS_TARGET_FLAVOR__) {
561
+ switch (TARGET_FLAVOR) {
431
562
  case 'vue':
432
563
  recordVuePayloadChanges(changed, getGraphVersion());
433
564
  break;
@@ -445,6 +576,16 @@ function applyDelta(payload) {
445
576
  console.log('[hmr][graph] delta applied newVersion', getGraphVersion(), 'changed=', (payload.changed || []).length, 'removed=', (payload.removed || []).length, 'baseVersion=', payload.baseVersion);
446
577
  // Queue evaluation of changed modules (placeholder pipeline)
447
578
  if (payload.changed?.length) {
579
+ // Gate: Before the first full-graph is received, delta messages are the server
580
+ // discovering modules during initial boot — NOT actual code changes. The entry-runtime
581
+ // already loaded all modules; re-importing them would cause duplicate Application.run(),
582
+ // router initialization, and modal conflicts. Only queue re-imports after the first
583
+ // full-graph confirms the graph is synced and subsequent deltas are real changes.
584
+ if (!hasReceivedFullGraph) {
585
+ if (VERBOSE)
586
+ console.log('[hmr][delta] skipping re-import queue (initial graph build, no full-graph yet)');
587
+ return;
588
+ }
448
589
  // HARD SUPPRESS: the very first delta (baseVersion 0) commonly includes the app main entry which is already evaluated during bootstrap.
449
590
  // Importing it again with a cache-bust often produces a spurious module-not-found (timestamp param treated as distinct file).
450
591
  const isInitial = payload.baseVersion === 0;
@@ -507,7 +648,7 @@ function applyDelta(payload) {
507
648
  // Deterministic navigation using the current Vue app instance rather than vendor-held rootApp
508
649
  function __nsNavigateUsingApp(comp, opts = {}) {
509
650
  const g = globalThis;
510
- switch (__NS_TARGET_FLAVOR__) {
651
+ switch (TARGET_FLAVOR) {
511
652
  case 'vue':
512
653
  ensureVueGlobals();
513
654
  break;
@@ -535,7 +676,7 @@ function __nsNavigateUsingApp(comp, opts = {}) {
535
676
  const existingApp = getCurrentApp();
536
677
  const baseProvides = (existingApp && existingApp._context && existingApp._context.provides) || {};
537
678
  const app = AppFactory(normalizeComponent(comp, comp && (comp.__name || comp.name)));
538
- switch (__NS_TARGET_FLAVOR__) {
679
+ switch (TARGET_FLAVOR) {
539
680
  case 'vue':
540
681
  ensurePiniaOnApp(app);
541
682
  break;
@@ -686,10 +827,167 @@ async function processQueue() {
686
827
  }
687
828
  }
688
829
  // After evaluating the batch, perform flavor-specific UI refresh.
689
- switch (__NS_TARGET_FLAVOR__) {
830
+ switch (TARGET_FLAVOR) {
690
831
  case 'vue':
691
832
  // Vue SFCs are handled via the registry update path; nothing to do here.
692
833
  break;
834
+ case 'solid': {
835
+ // Solid .tsx components are self-accepting via solid-refresh's inline
836
+ // patchRegistry — re-importing them is sufficient. For non-component
837
+ // .ts utility modules, we must propagate up the import graph to find
838
+ // the .tsx/.jsx component boundaries and re-import those so their
839
+ // solid-refresh proxies pick up the new dependency values.
840
+ try {
841
+ // Build reverse index: dep id → list of importer ids
842
+ const reverseIndex = new Map();
843
+ for (const [id, mod] of graph) {
844
+ for (const dep of mod.deps) {
845
+ let arr = reverseIndex.get(dep);
846
+ if (!arr) {
847
+ arr = [];
848
+ reverseIndex.set(dep, arr);
849
+ }
850
+ arr.push(id);
851
+ }
852
+ }
853
+ // BFS from each non-tsx changed module up to tsx/jsx boundaries
854
+ const boundaries = new Set();
855
+ for (const id of drained) {
856
+ if (/\.(tsx|jsx)$/i.test(id))
857
+ continue; // already self-accepting
858
+ const visited = new Set();
859
+ const queue = [id];
860
+ while (queue.length) {
861
+ const cur = queue.shift();
862
+ if (visited.has(cur))
863
+ continue;
864
+ visited.add(cur);
865
+ const importers = reverseIndex.get(cur);
866
+ if (!importers)
867
+ continue;
868
+ for (const imp of importers) {
869
+ if (/\.(tsx|jsx)$/i.test(imp)) {
870
+ boundaries.add(imp);
871
+ }
872
+ else {
873
+ queue.push(imp);
874
+ }
875
+ }
876
+ }
877
+ }
878
+ // Re-import each boundary so solid-refresh patchRegistry fires.
879
+ // For route files (TanStack Router), capture the new Route export
880
+ // and patch the router's existing route with the fresh loader.
881
+ let routesPatchCount = 0;
882
+ let discoveredRouter = null;
883
+ // Discover router: try __ns_router global (set by createNativeScriptRouter),
884
+ // then scan globalThis for any router-shaped object with routesById.
885
+ const findRouter = () => {
886
+ if (discoveredRouter)
887
+ return discoveredRouter;
888
+ const g = globalThis;
889
+ if (g.__ns_router?.routesById)
890
+ return (discoveredRouter = g.__ns_router);
891
+ // Fallback: scan common global keys for router
892
+ for (const key of ['__ns_router', 'router', '__router']) {
893
+ if (g[key]?.routesById && g[key]?.invalidate)
894
+ return (discoveredRouter = g[key]);
895
+ }
896
+ return null;
897
+ };
898
+ // Convert boundary file path to TanStack Router fullPath.
899
+ // e.g. /src/routes/posts.$postId.tsx → /posts/$postId
900
+ const boundaryToFullPath = (bid) => {
901
+ const m = bid.match(/\/src\/routes\/(.+)\.(tsx|jsx|ts|js)$/i);
902
+ if (!m)
903
+ return null;
904
+ let p = m[1];
905
+ // Replace dots between segments with slashes (posts.$postId → posts/$postId)
906
+ p = p.replace(/\./g, '/');
907
+ // Handle index files
908
+ if (p === 'index')
909
+ return '/';
910
+ if (p.endsWith('/index'))
911
+ p = p.slice(0, -6);
912
+ // Strip leading - (TanStack pathless layout convention)
913
+ p = p.replace(/(^|\/)-([\w])/g, '$1$2');
914
+ return '/' + p;
915
+ };
916
+ // Find existing route by fullPath (since new Route has no id yet)
917
+ const findRouteByFullPath = (router, fp) => {
918
+ if (!router?.routesById)
919
+ return null;
920
+ for (const rid of Object.keys(router.routesById)) {
921
+ const r = router.routesById[rid];
922
+ if (r?.fullPath === fp)
923
+ return r;
924
+ }
925
+ return null;
926
+ };
927
+ for (const id of boundaries) {
928
+ if (seen.has(id))
929
+ continue;
930
+ try {
931
+ const spec = normalizeSpec(id);
932
+ const url = await requestModuleFromServer(spec);
933
+ if (!url)
934
+ continue;
935
+ if (VERBOSE)
936
+ console.log('[hmr][solid] propagated to boundary', { id, url });
937
+ const mod = await import(/* @vite-ignore */ url);
938
+ // Patch TanStack Router route loaders
939
+ try {
940
+ const newRoute = mod?.Route;
941
+ if (newRoute?.options?.loader) {
942
+ const router = findRouter();
943
+ const fullPath = boundaryToFullPath(id);
944
+ if (VERBOSE)
945
+ console.log('[hmr][solid][diag] route patch attempt', { id, fullPath, hasRouter: !!router, routesByIdKeys: router?.routesById ? Object.keys(router.routesById) : 'none' });
946
+ const existingRoute = fullPath && router ? findRouteByFullPath(router, fullPath) : null;
947
+ if (existingRoute?.options) {
948
+ existingRoute.options.loader = newRoute.options.loader;
949
+ if (newRoute.options.component)
950
+ existingRoute.options.component = newRoute.options.component;
951
+ routesPatchCount++;
952
+ if (VERBOSE)
953
+ console.log('[hmr][solid] patched route loader', existingRoute.id, 'fullPath=', fullPath);
954
+ }
955
+ else if (VERBOSE) {
956
+ console.log('[hmr][solid] no matching route for fullPath', fullPath);
957
+ }
958
+ }
959
+ }
960
+ catch (e) {
961
+ if (VERBOSE)
962
+ console.warn('[hmr][solid] route patch error', id, e);
963
+ }
964
+ }
965
+ catch (e) {
966
+ if (VERBOSE)
967
+ console.warn('[hmr][solid] boundary re-import failed', id, e);
968
+ }
969
+ }
970
+ // Route loaders were patched with fresh closures. The data is
971
+ // correct in router.state.matches[].loaderData (confirmed via
972
+ // diagnostics), but the currently visible page doesn't re-render
973
+ // because the NativeScriptRouterProvider's Solid store flush only
974
+ // fires through history.subscribe. TODO: find the right mechanism
975
+ // to trigger a Solid reactive update for the active match stores.
976
+ if (routesPatchCount > 0 && VERBOSE) {
977
+ console.log('[hmr][solid] patched', routesPatchCount, 'route loaders (data correct in match state, pending UI refresh mechanism)');
978
+ }
979
+ if (VERBOSE) {
980
+ if (boundaries.size)
981
+ console.log('[hmr][solid] propagated non-component change to', boundaries.size, 'boundaries', Array.from(boundaries));
982
+ console.log('[hmr][queue] Solid: modules re-imported, solid-refresh handles reactive update', drained);
983
+ }
984
+ }
985
+ catch (e) {
986
+ if (VERBOSE)
987
+ console.warn('[hmr][solid] propagation failed', e);
988
+ }
989
+ break;
990
+ }
693
991
  case 'typescript': {
694
992
  // For TS apps, always reset back to the conventional app root.
695
993
  // This preserves the shell (Frame, ActionBar, etc.) that the app's
@@ -702,9 +1000,124 @@ async function processQueue() {
702
1000
  console.warn('[hmr][queue] TS flavor: Application.resetRootView unavailable; skipping UI refresh');
703
1001
  break;
704
1002
  }
1003
+ // Re-fetch changed XML/CSS files and update the bundled module registry
1004
+ // so Builder.createViewFromEntry picks up fresh content.
1005
+ const rawAssetIds = drained.filter((id) => /\.(xml|css|scss|sass|less)$/i.test(id));
1006
+ if (rawAssetIds.length && typeof g.registerModule === 'function') {
1007
+ const origin = getHttpOriginForVite() || deriveHttpOrigin(getHMRWsUrl());
1008
+ if (origin) {
1009
+ for (const id of rawAssetIds) {
1010
+ try {
1011
+ const spec = normalizeSpec(id);
1012
+ // Fetch the raw file content directly from Vite's dev server.
1013
+ // Use the project-relative path which Vite serves as static files.
1014
+ const fetchUrl = origin + (spec.startsWith('/') ? spec : '/' + spec);
1015
+ if (VERBOSE)
1016
+ console.log('[hmr][queue] fetching raw asset', { id, fetchUrl });
1017
+ const resp = await fetch(fetchUrl);
1018
+ if (resp.ok) {
1019
+ const rawContent = await resp.text();
1020
+ // Register under all nickname variants the module registry uses.
1021
+ // The bundler context registers XML as e.g., './main-page.xml' and 'main-page.xml'
1022
+ const appVirtual = APP_VIRTUAL_WITH_SLASH.replace(/^\//, '');
1023
+ let relPath = spec.startsWith('/') ? spec.slice(1) : spec;
1024
+ if (relPath.startsWith(appVirtual))
1025
+ relPath = relPath.slice(appVirtual.length);
1026
+ const nicknames = ['./' + relPath, relPath];
1027
+ // Also add without extension for CSS
1028
+ const extIdx = relPath.lastIndexOf('.');
1029
+ if (extIdx > 0) {
1030
+ const baseName = relPath.slice(0, extIdx);
1031
+ if (!relPath.endsWith('.xml'))
1032
+ nicknames.push(baseName, './' + baseName);
1033
+ }
1034
+ for (const name of nicknames) {
1035
+ if (VERBOSE)
1036
+ console.log('[hmr][queue] re-registering module', name);
1037
+ g.registerModule(name, () => rawContent);
1038
+ }
1039
+ }
1040
+ else if (VERBOSE) {
1041
+ console.warn('[hmr][queue] raw asset fetch failed', id, resp.status);
1042
+ }
1043
+ }
1044
+ catch (e) {
1045
+ if (VERBOSE)
1046
+ console.warn('[hmr][queue] raw asset refresh failed for', id, e);
1047
+ }
1048
+ }
1049
+ }
1050
+ }
1051
+ // Determine if we can navigate in-place to a changed page
1052
+ // instead of resetting all the way back to app-root.
1053
+ // This keeps the user on the page they're editing for faster iteration.
1054
+ const changedXmlPages = drained
1055
+ .filter((id) => /\.xml$/i.test(id))
1056
+ .map((id) => {
1057
+ const spec = normalizeSpec(id);
1058
+ const appVirtual = APP_VIRTUAL_WITH_SLASH.replace(/^\//, '');
1059
+ let relPath = spec.startsWith('/') ? spec.slice(1) : spec;
1060
+ if (relPath.startsWith(appVirtual))
1061
+ relPath = relPath.slice(appVirtual.length);
1062
+ // Strip .xml extension to get the moduleName (e.g., 'pages/status-bar')
1063
+ return relPath.replace(/\.xml$/i, '');
1064
+ })
1065
+ .filter((m) => m && m !== 'app-root');
1066
+ // Resolve the topmost Frame from the bundled realm.
1067
+ // Frame.topmost() relies on an internal frameStack array, so we must
1068
+ // call it on the bundled-realm class. Multiple strategies to find it:
1069
+ const FrameClass = getCore('Frame') || g.Frame;
1070
+ let topFrame = null;
1071
+ // 1) Try the vendor-realm static topmost()
1072
+ try {
1073
+ topFrame = FrameClass?.topmost?.();
1074
+ }
1075
+ catch { }
1076
+ // 2) Try getting the root view from Application — if it's a Frame, use it
1077
+ if (!topFrame) {
1078
+ try {
1079
+ const rootView = App.getRootView?.() || App._rootView;
1080
+ if (rootView) {
1081
+ // rootView could be a Frame itself, or contain a Frame
1082
+ const isFrame = rootView.constructor?.name === 'Frame' || rootView.navigate;
1083
+ if (isFrame) {
1084
+ topFrame = rootView;
1085
+ }
1086
+ else if (rootView.getChildAt) {
1087
+ // Walk direct children looking for a Frame
1088
+ for (let i = 0; i < (rootView.getChildrenCount?.() || 0); i++) {
1089
+ const child = rootView.getChildAt(i);
1090
+ if (child?.constructor?.name === 'Frame' || child?.navigate) {
1091
+ topFrame = child;
1092
+ break;
1093
+ }
1094
+ }
1095
+ }
1096
+ }
1097
+ }
1098
+ catch { }
1099
+ }
705
1100
  if (VERBOSE)
706
- console.log('[hmr][queue] TS flavor: resetRootView(app-root) after changes');
707
- App.resetRootView({ moduleName: 'app-root' });
1101
+ console.log('[hmr][queue] TS: changedXmlPages=', changedXmlPages, 'topFrame=', !!topFrame);
1102
+ if (changedXmlPages.length > 0 && topFrame) {
1103
+ // Navigate the current frame to the changed page directly.
1104
+ // Use the last changed XML page (most specific).
1105
+ const moduleName = changedXmlPages[changedXmlPages.length - 1];
1106
+ if (VERBOSE)
1107
+ console.log('[hmr][queue] TS: navigating in-place to', moduleName);
1108
+ try {
1109
+ topFrame.navigate({ moduleName, clearHistory: false, animated: false });
1110
+ }
1111
+ catch (navErr) {
1112
+ console.warn('[hmr][queue] TS flavor: in-place navigate failed, falling back to resetRootView', navErr);
1113
+ App.resetRootView({ moduleName: 'app-root' });
1114
+ }
1115
+ }
1116
+ else {
1117
+ if (VERBOSE)
1118
+ console.log('[hmr][queue] TS flavor: resetRootView(app-root) after changes');
1119
+ App.resetRootView({ moduleName: 'app-root' });
1120
+ }
708
1121
  }
709
1122
  catch (e) {
710
1123
  console.warn('[hmr][queue] TS flavor: resetRootView(app-root) failed', e);
@@ -752,6 +1165,11 @@ function connectHmr() {
752
1165
  console.log('[hmr-client] Already connecting to HMR WebSocket, skipping');
753
1166
  return;
754
1167
  }
1168
+ try {
1169
+ globalThis.__NS_HMR_CLIENT_SOCKET_READY__ = false;
1170
+ }
1171
+ catch { }
1172
+ const overlayStage = hasOpenedHmrSocket ? 'reconnecting' : 'connecting';
755
1173
  const baseUrl = getHMRWsUrl() || 'ws://localhost:5173/ns-hmr';
756
1174
  const buildCandidates = (url) => {
757
1175
  let candidates = [];
@@ -795,7 +1213,7 @@ function connectHmr() {
795
1213
  if (seen.has(key))
796
1214
  continue;
797
1215
  seen.add(key);
798
- const cand = `${p}://${host}:${port}${u.pathname || '/ns-hmr'}`;
1216
+ const cand = `${p}://${host}:${port}${u.pathname || '/ns-hmr'}${u.search || ''}`;
799
1217
  candidates.push(cand);
800
1218
  }
801
1219
  }
@@ -811,10 +1229,19 @@ function connectHmr() {
811
1229
  let idx = 0;
812
1230
  const tryNext = () => {
813
1231
  if (idx >= candidates.length) {
1232
+ showConnectionOverlayNow('offline', 'Waiting for the Vite websocket to come back.');
814
1233
  console.warn('[hmr-client] All WS candidates failed:', candidates.join(', '));
1234
+ setTimeout(connectHmr, 1500);
815
1235
  return;
816
1236
  }
817
1237
  const url = candidates[idx++];
1238
+ const connectionDetail = `${overlayStage === 'reconnecting' ? 'Retrying' : 'Opening'} ${url}`;
1239
+ if (connectionOverlayVisible) {
1240
+ updateConnectionOverlay(overlayStage, connectionDetail);
1241
+ }
1242
+ else {
1243
+ scheduleConnectionOverlay(overlayStage, connectionDetail);
1244
+ }
818
1245
  try {
819
1246
  if (__NS_ENV_VERBOSE__)
820
1247
  console.log('[hmr-client] Connecting to HMR WebSocket:', url);
@@ -836,6 +1263,16 @@ function connectHmr() {
836
1263
  sock.onopen = () => {
837
1264
  opened = true;
838
1265
  clearTimeout(timeout);
1266
+ clearConnectionOverlayTimer();
1267
+ hasOpenedHmrSocket = true;
1268
+ awaitingHealthyHmrMessage = true;
1269
+ try {
1270
+ globalThis.__NS_HMR_CLIENT_SOCKET_READY__ = true;
1271
+ }
1272
+ catch { }
1273
+ if (connectionOverlayVisible) {
1274
+ showConnectionOverlayNow('synchronizing', 'Connected. Synchronizing the HMR graph.');
1275
+ }
839
1276
  VERBOSE && console.log('[hmr-client] Connected to HMR WebSocket');
840
1277
  };
841
1278
  sock.onmessage = handleHmrMessage;
@@ -845,6 +1282,10 @@ function connectHmr() {
845
1282
  };
846
1283
  sock.onclose = (ev) => {
847
1284
  clearTimeout(timeout);
1285
+ try {
1286
+ globalThis.__NS_HMR_CLIENT_SOCKET_READY__ = false;
1287
+ }
1288
+ catch { }
848
1289
  if (!opened) {
849
1290
  // immediate failure during connect → try another candidate
850
1291
  if (VERBOSE)
@@ -854,6 +1295,7 @@ function connectHmr() {
854
1295
  else {
855
1296
  if (VERBOSE)
856
1297
  console.log('[hmr-client] WebSocket closed (code', ev?.code, '), will reconnect…');
1298
+ scheduleConnectionOverlay('reconnecting', 'The websocket closed. Waiting to reconnect.', 700);
857
1299
  // try to reconnect with full candidate list again
858
1300
  setTimeout(connectHmr, 1000);
859
1301
  }
@@ -874,6 +1316,12 @@ async function handleHmrMessage(ev) {
874
1316
  catch {
875
1317
  return;
876
1318
  }
1319
+ if (awaitingHealthyHmrMessage && msg) {
1320
+ markHmrConnectionHealthy();
1321
+ }
1322
+ if (VERBOSE && msg?.type) {
1323
+ console.log('[hmr-client] received message', msg.type);
1324
+ }
877
1325
  // Notify optional app-level hook after an HMR batch is applied.
878
1326
  function notifyAppHmrUpdate(kind, changedIds) {
879
1327
  try {
@@ -896,6 +1344,34 @@ async function handleHmrMessage(ev) {
896
1344
  const prevGraph = new Map(graph);
897
1345
  setGraphVersion(Number(msg.version || getGraphVersion() || 0));
898
1346
  applyFullGraph(msg);
1347
+ hasReceivedFullGraph = true;
1348
+ // Gate: On first boot, the entry-runtime handles all initial module loading
1349
+ // (with the import map already configured). Don't re-import here — the graph
1350
+ // is stored above for future HMR delta comparisons, but modules are already
1351
+ // loaded correctly via the entry-runtime boot sequence.
1352
+ //
1353
+ // Two cases to catch:
1354
+ // 1. Boot still in progress (__NS_HMR_BOOT_COMPLETE__ is false)
1355
+ // 2. Boot already finished but this is the FIRST full-graph (prevGraph was
1356
+ // empty). The WebSocket often connects after entry-runtime finishes, so
1357
+ // boot is "complete" but we still shouldn't re-import — all modules were
1358
+ // just loaded fresh. Only re-import on subsequent full-graphs (reconnect
1359
+ // scenarios) where prevGraph already has entries.
1360
+ if (!globalThis.__NS_HMR_BOOT_COMPLETE__) {
1361
+ if (VERBOSE)
1362
+ console.info('[hmr][full-graph] skipping initial re-import (boot in progress)');
1363
+ const fullIds = Array.isArray(msg.modules) ? msg.modules.map((m) => m?.id).filter(Boolean) : [];
1364
+ notifyAppHmrUpdate('full-graph', fullIds);
1365
+ return;
1366
+ }
1367
+ if (prevGraph.size === 0) {
1368
+ if (VERBOSE)
1369
+ console.info('[hmr][full-graph] skipping re-import on first graph after boot (modules already fresh)');
1370
+ const fullIds = Array.isArray(msg.modules) ? msg.modules.map((m) => m?.id).filter(Boolean) : [];
1371
+ notifyAppHmrUpdate('full-graph', fullIds);
1372
+ return;
1373
+ }
1374
+ // Reconnect / resync case — re-import changed modules as normal.
899
1375
  // In some cases (e.g. server chooses full-graph resync / page reload), we won't
900
1376
  // receive a delta queue to re-import changed TS modules. Without re-import,
901
1377
  // HTTP ESM caching means module bodies (and side effects) won't re-run.
@@ -998,8 +1474,13 @@ async function handleHmrMessage(ev) {
998
1474
  notifyAppHmrUpdate('delta', deltaIds);
999
1475
  return;
1000
1476
  }
1001
- else if (handleAngularHotUpdateMessage(msg, { getCore, verbose: VERBOSE })) {
1002
- return;
1477
+ else {
1478
+ if (msg.type === 'ns:angular-update' && typeof msg.version === 'number') {
1479
+ setGraphVersion(Number(msg.version || getGraphVersion() || 0));
1480
+ }
1481
+ if (await handleAngularHotUpdateMessage(msg, { getCore, verbose: VERBOSE })) {
1482
+ return;
1483
+ }
1003
1484
  }
1004
1485
  }
1005
1486
  // On-demand module fetch response (Option A)
@@ -1140,7 +1621,7 @@ async function performResetRoot(newComponent) {
1140
1621
  if (cachedRoot)
1141
1622
  return cachedRoot;
1142
1623
  try {
1143
- switch (__NS_TARGET_FLAVOR__) {
1624
+ switch (TARGET_FLAVOR) {
1144
1625
  case 'vue':
1145
1626
  cachedRoot = getRootForVue(newComponent, state);
1146
1627
  break;
@@ -1455,9 +1936,29 @@ export function initHmrClient(opts) {
1455
1936
  }
1456
1937
  g.__NS_HMR_CLIENT_ACTIVE__ = true;
1457
1938
  ensureCoreAliasesOnGlobalThis();
1458
- connectHmr();
1939
+ // Defer WebSocket connection until boot completes to avoid native V8 crashes
1940
+ // caused by concurrent WebSocket message handling + HTTP fetch during early startup.
1941
+ // The WebSocket is only needed for HMR updates, not the initial boot sequence.
1942
+ if (g.__NS_HMR_BOOT_COMPLETE__) {
1943
+ connectHmr();
1944
+ }
1945
+ else {
1946
+ const waitForBoot = () => {
1947
+ if (globalThis.__NS_HMR_BOOT_COMPLETE__) {
1948
+ if (VERBOSE)
1949
+ console.log('[hmr-client] boot complete, connecting HMR WebSocket');
1950
+ connectHmr();
1951
+ }
1952
+ else {
1953
+ setTimeout(waitForBoot, 100);
1954
+ }
1955
+ };
1956
+ if (VERBOSE)
1957
+ console.log('[hmr-client] deferring WebSocket connection until boot completes');
1958
+ setTimeout(waitForBoot, 100);
1959
+ }
1459
1960
  // Best-effort: install back wrapper even before first remount; original root may be captured later
1460
- switch (__NS_TARGET_FLAVOR__) {
1961
+ switch (TARGET_FLAVOR) {
1461
1962
  case 'vue':
1462
1963
  ensureBackWrapperInstalled(performResetRoot, getCore);
1463
1964
  break;