@nativescript/vite 8.0.0-alpha.6 → 8.0.0-alpha.8

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 (85) hide show
  1. package/configuration/angular.js +155 -22
  2. package/configuration/angular.js.map +1 -1
  3. package/configuration/base.js +9 -12
  4. package/configuration/base.js.map +1 -1
  5. package/configuration/typescript.js +1 -1
  6. package/configuration/typescript.js.map +1 -1
  7. package/helpers/angular/angular-linker.js +3 -12
  8. package/helpers/angular/angular-linker.js.map +1 -1
  9. package/helpers/angular/inject-component-hmr-registration.d.ts +112 -0
  10. package/helpers/angular/inject-component-hmr-registration.js +359 -0
  11. package/helpers/angular/inject-component-hmr-registration.js.map +1 -0
  12. package/helpers/global-defines.d.ts +7 -13
  13. package/helpers/global-defines.js +10 -16
  14. package/helpers/global-defines.js.map +1 -1
  15. package/helpers/main-entry.js +6 -7
  16. package/helpers/main-entry.js.map +1 -1
  17. package/helpers/ns-core-url.d.ts +5 -6
  18. package/helpers/ns-core-url.js +5 -6
  19. package/helpers/ns-core-url.js.map +1 -1
  20. package/helpers/prelink-angular.js +1 -4
  21. package/helpers/prelink-angular.js.map +1 -1
  22. package/hmr/client/css-handler.js +2 -1
  23. package/hmr/client/css-handler.js.map +1 -1
  24. package/hmr/client/hmr-pending-overlay.d.ts +1 -1
  25. package/hmr/client/hmr-pending-overlay.js +1 -1
  26. package/hmr/client/index.js +27 -26
  27. package/hmr/client/index.js.map +1 -1
  28. package/hmr/client/utils.js +27 -25
  29. package/hmr/client/utils.js.map +1 -1
  30. package/hmr/entry-runtime.js +11 -29
  31. package/hmr/entry-runtime.js.map +1 -1
  32. package/hmr/frameworks/angular/client/index.js +118 -136
  33. package/hmr/frameworks/angular/client/index.js.map +1 -1
  34. package/hmr/frameworks/angular/server/linker.js +1 -4
  35. package/hmr/frameworks/angular/server/linker.js.map +1 -1
  36. package/hmr/frameworks/vue/client/index.js +18 -42
  37. package/hmr/frameworks/vue/client/index.js.map +1 -1
  38. package/hmr/server/core-sanitize.js +8 -8
  39. package/hmr/server/core-sanitize.js.map +1 -1
  40. package/hmr/server/import-map.js +3 -4
  41. package/hmr/server/import-map.js.map +1 -1
  42. package/hmr/server/ns-core-cjs-shape.d.ts +2 -4
  43. package/hmr/server/ns-core-cjs-shape.js +4 -6
  44. package/hmr/server/ns-core-cjs-shape.js.map +1 -1
  45. package/hmr/server/perf-instrumentation.d.ts +5 -9
  46. package/hmr/server/perf-instrumentation.js +2 -5
  47. package/hmr/server/perf-instrumentation.js.map +1 -1
  48. package/hmr/server/vite-plugin.js +3 -1
  49. package/hmr/server/vite-plugin.js.map +1 -1
  50. package/hmr/server/websocket-angular-hot-update.d.ts +1 -0
  51. package/hmr/server/websocket-angular-hot-update.js +42 -30
  52. package/hmr/server/websocket-angular-hot-update.js.map +1 -1
  53. package/hmr/server/websocket-core-bridge.js +12 -17
  54. package/hmr/server/websocket-core-bridge.js.map +1 -1
  55. package/hmr/server/websocket-hmr-pending.d.ts +1 -1
  56. package/hmr/server/websocket-hmr-pending.js +1 -1
  57. package/hmr/server/websocket-module-bindings.js +2 -2
  58. package/hmr/server/websocket-module-bindings.js.map +1 -1
  59. package/hmr/server/websocket-ns-m-paths.js +14 -15
  60. package/hmr/server/websocket-ns-m-paths.js.map +1 -1
  61. package/hmr/server/websocket-runtime-compat.js +4 -3
  62. package/hmr/server/websocket-runtime-compat.js.map +1 -1
  63. package/hmr/server/websocket-served-module-helpers.js +13 -12
  64. package/hmr/server/websocket-served-module-helpers.js.map +1 -1
  65. package/hmr/server/websocket-vue-sfc.js +15 -60
  66. package/hmr/server/websocket-vue-sfc.js.map +1 -1
  67. package/hmr/server/websocket.js +286 -338
  68. package/hmr/server/websocket.js.map +1 -1
  69. package/hmr/shared/runtime/boot-timeline.js +0 -3
  70. package/hmr/shared/runtime/boot-timeline.js.map +1 -1
  71. package/hmr/shared/runtime/dev-overlay.js +36 -50
  72. package/hmr/shared/runtime/dev-overlay.js.map +1 -1
  73. package/hmr/shared/runtime/module-provenance.js +1 -4
  74. package/hmr/shared/runtime/module-provenance.js.map +1 -1
  75. package/hmr/shared/runtime/root-placeholder.js +38 -68
  76. package/hmr/shared/runtime/root-placeholder.js.map +1 -1
  77. package/hmr/shared/runtime/session-bootstrap.js +8 -8
  78. package/hmr/shared/runtime/session-bootstrap.js.map +1 -1
  79. package/hmr/shared/runtime/vendor-bootstrap.js +1 -9
  80. package/hmr/shared/runtime/vendor-bootstrap.js.map +1 -1
  81. package/hmr/shared/vendor/manifest.js +4 -16
  82. package/hmr/shared/vendor/manifest.js.map +1 -1
  83. package/package.json +1 -1
  84. package/runtime/core-aliases-early.js +17 -41
  85. package/runtime/core-aliases-early.js.map +1 -1
@@ -80,14 +80,13 @@ const APP_VIRTUAL_PREFIX = getProjectAppVirtualPath();
80
80
  const APP_VIRTUAL_WITH_SLASH = `${APP_VIRTUAL_PREFIX}/`;
81
81
  const DEFAULT_MAIN_ENTRY = getProjectAppRelativePath('app.ts');
82
82
  const DEFAULT_MAIN_ENTRY_VIRTUAL = getProjectAppVirtualPath('app.ts');
83
- // alpha.59 — Stable URL + Explicit Invalidation:
84
83
  // Memoized resolver for the project bootstrap entry as a posix
85
- // project-relative path (e.g. `/src/main.ts`). This mirrors the resolution
86
- // the cold-boot wrapper performs (`getPackageJson().main` →
87
- // project-relative under `/<APP_ROOT_DIR>/`) so the eviction set for HMR
88
- // always lines up with the URL the runtime actually re-imports. Resolved
89
- // at first call and cached: `package.json` is read at startup and never
90
- // changes during a dev session, so it's safe to memoize.
84
+ // project-relative path (e.g. `/src/main.ts`). This mirrors the
85
+ // resolution the cold-boot wrapper performs (`getPackageJson().main` →
86
+ // project-relative under `/<APP_ROOT_DIR>/`) so the eviction set for
87
+ // HMR always lines up with the URL the runtime actually re-imports.
88
+ // Resolved at first call and cached: `package.json` is read at startup
89
+ // and never changes during a dev session, so it's safe to memoize.
91
90
  let __ns_bootstrap_entry_rel_cached = null;
92
91
  function getBootstrapEntryRelPath() {
93
92
  if (__ns_bootstrap_entry_rel_cached)
@@ -354,11 +353,9 @@ function ensureGuardPlainDynamicImports(code, origin) {
354
353
  return code;
355
354
  }
356
355
  }
357
- // alpha.59 — `ensureDynamicHmrImportHelper` was previously duplicated
358
- // here. Single source of truth now lives in
356
+ // `ensureDynamicHmrImportHelper` lives in
359
357
  // `./websocket-served-module-helpers.js`. See that file for the
360
- // architectural rationale and the current (much smaller) helper
361
- // implementation.
358
+ // architectural rationale and the current helper implementation.
362
359
  async function expandStarExports(code, server, projectRoot, verbose, sharedTransformer) {
363
360
  const STAR_RE = /^[ \t]*(export\s+\*\s+from\s+["'])([^"']+)(["'];?)[ \t]*$/gm;
364
361
  let match;
@@ -390,10 +387,7 @@ async function expandStarExports(code, server, projectRoot, verbose, sharedTrans
390
387
  if (!names.length)
391
388
  return null;
392
389
  if (verbose) {
393
- try {
394
- console.log(`[ns/m] expanded export* -> ${names.length} names from ${vitePath}`);
395
- }
396
- catch { }
390
+ console.log(`[ns/m] expanded export* -> ${names.length} names from ${vitePath}`);
397
391
  }
398
392
  return { rep, names };
399
393
  }
@@ -456,7 +450,7 @@ function stripViteDynamicImportVirtual(code) {
456
450
  }
457
451
  return code;
458
452
  }
459
- const REQUIRE_GUARD_SNIPPET = `// [guard] install require('http(s)://') detector\n(()=>{try{var g=globalThis;if(g.__NS_REQUIRE_GUARD_INSTALLED__){}else{var mk=function(o,l){return function(){try{var s=arguments[0];if(typeof s==='string'&&/^(?:https?:)\\/\\//.test(s)){var e=new Error('[ns-hmr][require-guard] require of URL: '+s+' via '+l);try{console.error(e.message+'\\n'+(e.stack||''));}catch(e2){}try{g.__NS_REQUIRE_GUARD_LAST__={spec:s,stack:e.stack,label:l,ts:Date.now()};}catch(e3){}}}catch(e1){}return o.apply(this, arguments);};};if(typeof g.require==='function'&&!g.require.__NS_REQ_GUARDED__){var o1=g.require;g.require=mk(o1,'require');g.require.__NS_REQ_GUARDED__=true;}if(typeof g.__nsRequire==='function'&&!g.__nsRequire.__NS_REQ_GUARDED__){var o2=g.__nsRequire;g.__nsRequire=mk(o2,'__nsRequire');g.__nsRequire.__NS_REQ_GUARDED__=true;}g.__NS_REQUIRE_GUARD_INSTALLED__=true;}}catch(e){}})();\n`;
453
+ const REQUIRE_GUARD_SNIPPET = `// [guard] install require('http(s)://') detector\n(()=>{try{var g=globalThis;if(g.__NS_REQUIRE_GUARD_INSTALLED__){}else{var mk=function(o,l){return function(){try{var s=arguments[0];if(typeof s==='string'&&/^(?:https?:)\\/\\//.test(s)){var e=new Error('[ns-hmr][require-guard] require of URL: '+s+' via '+l);console.error(e.message+'\\n'+(e.stack||''));try{g.__NS_REQUIRE_GUARD_LAST__={spec:s,stack:e.stack,label:l,ts:Date.now()};}catch(e3){}}}catch(e1){}return o.apply(this, arguments);};};if(typeof g.require==='function'&&!g.require.__NS_REQ_GUARDED__){var o1=g.require;g.require=mk(o1,'require');g.require.__NS_REQ_GUARDED__=true;}if(typeof g.__nsRequire==='function'&&!g.__nsRequire.__NS_REQ_GUARDED__){var o2=g.__nsRequire;g.__nsRequire=mk(o2,'__nsRequire');g.__nsRequire.__NS_REQ_GUARDED__=true;}g.__NS_REQUIRE_GUARD_INSTALLED__=true;}}catch(e){}})();\n`;
460
454
  function shouldRemapImport(spec) {
461
455
  if (!spec || typeof spec !== 'string')
462
456
  return false;
@@ -914,14 +908,13 @@ function toNodeModulesHttpModuleId(importPath) {
914
908
  }
915
909
  return `/ns/m/node_modules/${nodeModulesSpecifier}`;
916
910
  }
917
- // alpha.59 — `rewriteNsMImportPathForHmr` and `getNumericServeVersionTag`
918
- // previously lived here as duplicates of the implementations in
911
+ // `rewriteNsMImportPathForHmr` and `getNumericServeVersionTag` live in
919
912
  // `./websocket-ns-m-paths.js`. The path rewriter is part of the
920
- // "Stable URL + Explicit Invalidation" architecture (see
921
- // HMR_STABLE_URL_INVALIDATION_PLAN.md) and must be a single source of
922
- // truth so the canonicalization rules can't drift between the two files.
923
- // They are imported above and re-exported below for tests / external
924
- // callers that historically reached them through this module.
913
+ // "Stable URL + Explicit Invalidation" architecture and must be a
914
+ // single source of truth so the canonicalization rules can't drift
915
+ // between modules. They are imported above and re-exported below for
916
+ // tests / external callers that historically reached them through this
917
+ // module.
925
918
  function normalizeAbsoluteFilesystemImport(spec, importerPath, projectRoot) {
926
919
  if (!spec || typeof spec !== 'string') {
927
920
  return null;
@@ -1966,10 +1959,7 @@ export function rewriteImports(code, importerPath, sfcFileMap, depFileMap, proje
1966
1959
  if (spec === '@') {
1967
1960
  const stub = `/ns/m/__invalid_at__.mjs`;
1968
1961
  if (verbose) {
1969
- try {
1970
- console.warn(`[rewrite] mapped bare '@' spec to stub: ${stub}`);
1971
- }
1972
- catch { }
1962
+ console.warn(`[rewrite] mapped bare '@' spec to stub: ${stub}`);
1973
1963
  }
1974
1964
  return `${prefix}${stub}${suffix}`;
1975
1965
  }
@@ -2190,10 +2180,7 @@ export function rewriteImports(code, importerPath, sfcFileMap, depFileMap, proje
2190
2180
  result = result.replace(/(import\(\s*['"])@(['"]\s*\))/g, (_m) => {
2191
2181
  const stubExpr = `import(new URL('/ns/m/__invalid_at__.mjs', import.meta.url).href)`;
2192
2182
  if (verbose) {
2193
- try {
2194
- console.warn(`[rewrite] mapped dynamic import('@') to /ns/m/__invalid_at__.mjs via import.meta.url`);
2195
- }
2196
- catch { }
2183
+ console.warn(`[rewrite] mapped dynamic import('@') to /ns/m/__invalid_at__.mjs via import.meta.url`);
2197
2184
  }
2198
2185
  return stubExpr;
2199
2186
  });
@@ -2202,10 +2189,7 @@ export function rewriteImports(code, importerPath, sfcFileMap, depFileMap, proje
2202
2189
  result = result.replace(/(from\s*['"])@(['"])/g, (_m, p1, p2) => {
2203
2190
  const stub = `/ns/m/__invalid_at__.mjs`;
2204
2191
  if (verbose) {
2205
- try {
2206
- console.warn(`[rewrite] mapped static from '@' to ${stub}`);
2207
- }
2208
- catch { }
2192
+ console.warn(`[rewrite] mapped static from '@' to ${stub}`);
2209
2193
  }
2210
2194
  return `${p1}${stub}${p2}`;
2211
2195
  });
@@ -2213,10 +2197,7 @@ export function rewriteImports(code, importerPath, sfcFileMap, depFileMap, proje
2213
2197
  result = result.replace(/(import\s*(?!\()\s*['"])@(['"])/g, (_m, p1, p2) => {
2214
2198
  const stub = `/ns/m/__invalid_at__.mjs`;
2215
2199
  if (verbose) {
2216
- try {
2217
- console.warn(`[rewrite] mapped side-effect import '@' to ${stub}`);
2218
- }
2219
- catch { }
2200
+ console.warn(`[rewrite] mapped side-effect import '@' to ${stub}`);
2220
2201
  }
2221
2202
  return `${p1}${stub}${p2}`;
2222
2203
  });
@@ -2508,11 +2489,8 @@ function createHmrWebSocketPlugin(opts) {
2508
2489
  const gm = { id, deps: normDeps, hash };
2509
2490
  graph.set(id, gm);
2510
2491
  if (verbose) {
2511
- try {
2512
- console.log('[hmr-ws][graph] upsert', { id, deps: normDeps, hash, graphVersion, classification, bumpVersion });
2513
- console.log('[hmr-ws][graph] size', graph.size);
2514
- }
2515
- catch { }
2492
+ console.log('[hmr-ws][graph] upsert', { id, deps: normDeps, hash, graphVersion, classification, bumpVersion });
2493
+ console.log('[hmr-ws][graph] size', graph.size);
2516
2494
  }
2517
2495
  if (shouldBroadcastGraphUpsertDelta(classification, options?.emitDeltaOnInsert === true, options?.broadcastDelta !== false)) {
2518
2496
  emitDelta([gm], []);
@@ -2593,7 +2571,7 @@ function createHmrWebSocketPlugin(opts) {
2593
2571
  }
2594
2572
  // bumpVersion: false — the initial walk is a bulk load, not a live
2595
2573
  // edit. Keeping graphVersion stable during cold boot avoids double
2596
- // cache-key drift described in Track 1.3 of the HMR plan.
2574
+ // cache-key drift.
2597
2575
  upsertGraphModule(rel, code, deps, { bumpVersion: false });
2598
2576
  }
2599
2577
  catch { }
@@ -2607,10 +2585,12 @@ function createHmrWebSocketPlugin(opts) {
2607
2585
  await walk(pathMod.join(root, 'src'));
2608
2586
  }
2609
2587
  catch { }
2610
- // Diagnostic summary: Always-on so slow
2611
- // cold-boot walks surface even when verbose is off. A `bumpedVersion=no`
2612
- // result is the happy path (Track 1.3); `yes` indicates a regression.
2613
- try {
2588
+ // Diagnostic summary. Gated behind the verbose flag so the
2589
+ // dev console stays quiet on a normal save. Flip
2590
+ // NS_VITE_VERBOSE=1 to surface slow cold-boot walks; a
2591
+ // `bumpedVersion=no` result is the happy path, `yes`
2592
+ // indicates a regression.
2593
+ if (verbose) {
2614
2594
  console.info(formatPopulateInitialGraphSummary({
2615
2595
  moduleCount: graph.size,
2616
2596
  durationMs: Date.now() - tStart,
@@ -2618,7 +2598,6 @@ function createHmrWebSocketPlugin(opts) {
2618
2598
  bumpedVersion: graphVersion !== versionAtStart,
2619
2599
  }));
2620
2600
  }
2621
- catch { }
2622
2601
  }
2623
2602
  // Kick off `populateInitialGraph` in the background (non-awaited) so /ns/m
2624
2603
  // responses are never blocked on a full tree walk. Returns the shared
@@ -2669,32 +2648,28 @@ function createHmrWebSocketPlugin(opts) {
2669
2648
  // race conditions during HTTP HMR startup, but the shared runner
2670
2649
  // already has per-URL coalescing and an async-cached result map,
2671
2650
  // so higher fan-out is safe and dramatically reduces cold-boot
2672
- // time (Track 1.4 in HMR_CORE_REALM_DETERMINISTIC_PLAN.md). We cap
2673
- // at 8 by default to match typical dev machines and respect Vite's
2674
- // internal worker pool limits. Override via the env var when needed.
2651
+ // time. We cap at 8 by default to match typical dev machines and
2652
+ // respect Vite's internal worker pool limits. Override via the
2653
+ // `NS_VITE_HMR_TRANSFORM_CONCURRENCY` env var when needed.
2675
2654
  const configuredTransformConcurrency = Number.parseInt(process.env.NS_VITE_HMR_TRANSFORM_CONCURRENCY || '', 10);
2676
2655
  const transformConcurrency = Number.isFinite(configuredTransformConcurrency) && configuredTransformConcurrency > 0 ? configuredTransformConcurrency : 8;
2677
- // Keep transformed code cached for longer across HMR updates so that
2678
- // unchanged neighbours of an edited file don't re-run through the
2679
- // Angular/TypeScript/Vite transform pipeline. The HMR flow
2680
- // explicitly invalidates affected URLs, so a longer TTL is safe.
2681
- // See Track 2.2 in HMR_CORE_REALM_DETERMINISTIC_PLAN.md.
2656
+ // Keep transformed code cached for longer across HMR updates so
2657
+ // that unchanged neighbours of an edited file don't re-run
2658
+ // through the Angular/TypeScript/Vite transform pipeline. The
2659
+ // HMR flow explicitly invalidates affected URLs, so a longer TTL
2660
+ // is safe. Override with `NS_VITE_HMR_TRANSFORM_CACHE_MS`.
2682
2661
  const configuredTransformCacheMs = Number.parseInt(process.env.NS_VITE_HMR_TRANSFORM_CACHE_MS || '', 10);
2683
2662
  const transformCacheMs = Number.isFinite(configuredTransformCacheMs) && configuredTransformCacheMs >= 0 ? configuredTransformCacheMs : 60000;
2684
2663
  sharedTransformRequest = createSharedTransformRequestRunner((url) => server.transformRequest(url), (url, timeoutMs) => {
2685
- try {
2686
- console.warn('[ns:m] slow transformRequest for', url, '(>' + timeoutMs + 'ms)');
2687
- }
2688
- catch { }
2664
+ console.warn('[ns:m] slow transformRequest for', url, '(>' + timeoutMs + 'ms)');
2689
2665
  }, {
2690
2666
  maxConcurrent: transformConcurrency,
2691
2667
  resultCacheTtlMs: transformCacheMs,
2692
2668
  getResultCacheKey: (url) => canonicalizeTransformRequestCacheKey(url, pluginRoot || process.cwd()),
2693
2669
  });
2694
- // Always-on startup banner — prints once per dev server process so
2695
- // anyone investigating perf can immediately see which build is live
2696
- // and what knobs are active. See HMR_CORE_REALM_DETERMINISTIC_PLAN.md
2697
- // ("Track 1 + 2 — diagnostic instrumentation").
2670
+ // Always-on startup banner — prints once per dev server process
2671
+ // so anyone investigating perf can immediately see which build
2672
+ // is live and what knobs are active.
2698
2673
  try {
2699
2674
  let pkgVersion = 'unknown';
2700
2675
  try {
@@ -2717,26 +2692,27 @@ function createHmrWebSocketPlugin(opts) {
2717
2692
  }
2718
2693
  catch { }
2719
2694
  }
2720
- console.info(formatServerStartupBanner({
2721
- version: pkgVersion,
2722
- transformConcurrency,
2723
- transformCacheMs,
2724
- lazyInitialGraph: true,
2725
- graphVersion,
2726
- }));
2695
+ if (verbose) {
2696
+ console.info(formatServerStartupBanner({
2697
+ version: pkgVersion,
2698
+ transformConcurrency,
2699
+ transformCacheMs,
2700
+ lazyInitialGraph: true,
2701
+ graphVersion,
2702
+ }));
2703
+ }
2727
2704
  }
2728
2705
  catch { }
2729
- // Always-on cold-boot request trace. Runs in front of every other
2730
- // middleware so it catches all NS dev routes (/ns/m/*, /ns/rt/*,
2731
- // /ns/core/*, /__ns_boot__/*, etc.) with a single hook. Closes
2732
- // itself after an idle window so HMR edits don't get rolled into
2733
- // the cold-boot numbers. The idle window is generous by default
2734
- // (5s) because V8's HTTP ESM resolver pauses between dep levels
2735
- // while parsing — a too-tight window was closing after the first
2736
- // wave and under-reporting boot by 100x. Override via
2737
- // NS_VITE_HMR_BOOT_TRACE_IDLE_MS when profiling something tricky.
2738
- // See HMR_CORE_REALM_DETERMINISTIC_PLAN.md ("Track 1 + 2 — round
2739
- // three, 2026-04").
2706
+ // Always-on cold-boot request trace. Runs in front of every
2707
+ // other middleware so it catches all NS dev routes (/ns/m/*,
2708
+ // /ns/rt/*, /ns/core/*, /__ns_boot__/*, etc.) with a single
2709
+ // hook. Closes itself after an idle window so HMR edits don't
2710
+ // get rolled into the cold-boot numbers. The idle window is
2711
+ // generous by default (5s) because V8's HTTP ESM resolver
2712
+ // pauses between dep levels while parsing — a too-tight window
2713
+ // was closing after the first wave and under-reporting boot by
2714
+ // 100x. Override via `NS_VITE_HMR_BOOT_TRACE_IDLE_MS` when
2715
+ // profiling something tricky.
2740
2716
  try {
2741
2717
  const configuredIdleMs = Number.parseInt(process.env.NS_VITE_HMR_BOOT_TRACE_IDLE_MS || '', 10);
2742
2718
  const idleWindowMs = Number.isFinite(configuredIdleMs) && configuredIdleMs > 0 ? configuredIdleMs : 5000;
@@ -2746,11 +2722,13 @@ function createHmrWebSocketPlugin(opts) {
2746
2722
  coldBootCounter = createColdBootRequestCounter({
2747
2723
  summaryEvery,
2748
2724
  idleWindowMs,
2725
+ // Gated on the verbose flag so cold-boot progress and
2726
+ // the final window-closed summary stay quiet by
2727
+ // default. Flip NS_VITE_VERBOSE=1 to surface them.
2749
2728
  log: (line) => {
2750
- try {
2751
- console.info(line);
2752
- }
2753
- catch { }
2729
+ if (!verbose)
2730
+ return;
2731
+ console.info(line);
2754
2732
  },
2755
2733
  });
2756
2734
  }
@@ -2784,13 +2762,12 @@ function createHmrWebSocketPlugin(opts) {
2784
2762
  // kicked off on the first /ns/m hit, which meant populate was
2785
2763
  // competing with the device for the same 8 transform slots
2786
2764
  // throughout the first 4-5 seconds of cold boot. Starting at
2787
- // `configureServer` time gives populate the full app build/launch
2788
- // window (typically 2-3s on simulator) as a head start, so more
2789
- // of its work lands before the device even connects. Disable via
2790
- // NS_VITE_HMR_DISABLE_POPULATE=1 when profiling whether populate
2791
- // is helping or hurting a specific app. See
2792
- // HMR_CORE_REALM_DETERMINISTIC_PLAN.md ("Track 1 + 2 — round
2793
- // three").
2765
+ // `configureServer` time gives populate the full app
2766
+ // build/launch window (typically 2-3s on simulator) as a head
2767
+ // start, so more of its work lands before the device even
2768
+ // connects. Disable via `NS_VITE_HMR_DISABLE_POPULATE=1` when
2769
+ // profiling whether populate is helping or hurting a specific
2770
+ // app.
2794
2771
  try {
2795
2772
  const disablePopulate = process.env.NS_VITE_HMR_DISABLE_POPULATE === '1' || process.env.NS_VITE_HMR_DISABLE_POPULATE === 'true';
2796
2773
  if (disablePopulate) {
@@ -2870,10 +2847,7 @@ function createHmrWebSocketPlugin(opts) {
2870
2847
  });
2871
2848
  });
2872
2849
  wss.on('error', (err) => {
2873
- try {
2874
- console.warn('[hmr-ws] server error:', err?.message || String(err));
2875
- }
2876
- catch { }
2850
+ console.warn('[hmr-ws] server error:', err?.message || String(err));
2877
2851
  });
2878
2852
  // Import map endpoint: GET /ns/import-map.json
2879
2853
  // Returns the import map + runtime config for __nsConfigureRuntime()
@@ -3084,12 +3058,12 @@ function createHmrWebSocketPlugin(opts) {
3084
3058
  // That gave deterministic URL tags but blocked the cold boot on a
3085
3059
  // full src/ tree walk (hundreds of transformRequest calls, 3-6s).
3086
3060
  //
3087
- // Track 1.3: graphVersion now starts at 1 and stays stable during
3088
- // cold boot (see `upsertGraphModule`'s bumpVersion option and the
3089
- // inline comment at the graphVersion declaration). We kick off the
3090
- // initial population in the background so it doesn't block the first
3091
- // response. `handleHotUpdate` awaits the same promise so the first
3092
- // HMR event still sees a fully populated graph.
3061
+ // graphVersion now starts at 1 and stays stable during cold boot
3062
+ // (see `upsertGraphModule`'s bumpVersion option and the inline
3063
+ // comment at the graphVersion declaration). We kick off the
3064
+ // initial population in the background so it doesn't block the
3065
+ // first response. `handleHotUpdate` awaits the same promise so
3066
+ // the first HMR event still sees a fully populated graph.
3093
3067
  ensureInitialGraphPopulationStarted(server);
3094
3068
  // Cold-boot counter is now hooked via the leading boot-trace
3095
3069
  // middleware (see `configureServer` — it records the request
@@ -3307,7 +3281,9 @@ function createHmrWebSocketPlugin(opts) {
3307
3281
  if (transformed?.code && ACTIVE_STRATEGY?.flavor === 'solid' && (resolvedCandidate || spec || '').includes('@solid-refresh')) {
3308
3282
  const PATCH_SENTINEL = '/* __ns_solid_refresh_patched__ */';
3309
3283
  const alreadyPatched = transformed.code.includes(PATCH_SENTINEL);
3310
- console.log('[hmr-ws][solid] @solid-refresh patch check:', { spec: resolvedCandidate || spec, alreadyPatched, codeLen: transformed.code.length });
3284
+ if (verbose) {
3285
+ console.log('[hmr-ws][solid] @solid-refresh patch check:', { spec: resolvedCandidate || spec, alreadyPatched, codeLen: transformed.code.length });
3286
+ }
3311
3287
  if (!alreadyPatched) {
3312
3288
  let patchedCode = transformed.code;
3313
3289
  // Patch 1: Bypass shouldWarnAndDecline() — the vendor-bundled solid-js
@@ -3316,7 +3292,9 @@ function createHmrWebSocketPlugin(opts) {
3316
3292
  const declineCheck = 'function shouldWarnAndDecline() {';
3317
3293
  if (patchedCode.includes(declineCheck)) {
3318
3294
  patchedCode = patchedCode.replace(declineCheck, `${PATCH_SENTINEL}\nfunction shouldWarnAndDecline() { return false; /* NS HMR: always allow refresh */ }\nfunction __original_shouldWarnAndDecline() {`);
3319
- console.log('[hmr-ws][solid] bypassed shouldWarnAndDecline() for NativeScript HMR');
3295
+ if (verbose) {
3296
+ console.log('[hmr-ws][solid] bypassed shouldWarnAndDecline() for NativeScript HMR');
3297
+ }
3320
3298
  }
3321
3299
  // Patch 2: Force createMemo path in createProxy.
3322
3300
  // Without the 'development' condition, $DEVCOMP is not set on components,
@@ -3327,10 +3305,17 @@ function createHmrWebSocketPlugin(opts) {
3327
3305
  const proxyCondition = 'if (!s || $DEVCOMP in s) {';
3328
3306
  if (patchedCode.includes(proxyCondition)) {
3329
3307
  patchedCode = patchedCode.replace(proxyCondition, 'if (true) { /* NS HMR: always use createMemo for reactive HMR updates */');
3330
- console.log('[hmr-ws][solid] forced createMemo path in createProxy for NativeScript HMR');
3308
+ if (verbose) {
3309
+ console.log('[hmr-ws][solid] forced createMemo path in createProxy for NativeScript HMR');
3310
+ }
3331
3311
  }
3332
3312
  // Patch 3: Inline patchRegistry call so updates apply immediately
3333
3313
  // on module re-evaluation (accept callbacks are not invoked by the HMR client).
3314
+ // The injected `console.log` helpers run inside the user's runtime
3315
+ // when @solid-refresh re-evaluates a module, so they are a runtime
3316
+ // concern (stripped if the user disables the patch). Keeping them
3317
+ // behind the patch sentinel rather than the dev-server `verbose`
3318
+ // flag is intentional — the patch only runs when Solid HMR fires.
3334
3319
  const marker = 'hot.data[SOLID_REFRESH] = hot.data[SOLID_REFRESH] || registry;';
3335
3320
  if (patchedCode.includes(marker)) {
3336
3321
  const patchCode = [
@@ -3344,7 +3329,9 @@ function createHmrWebSocketPlugin(opts) {
3344
3329
  `}`,
3345
3330
  ].join('\n ');
3346
3331
  patchedCode = patchedCode.replace(marker, `${patchCode}\n ${marker}`);
3347
- console.log('[hmr-ws][solid] added inline patchRegistry for NativeScript HMR');
3332
+ if (verbose) {
3333
+ console.log('[hmr-ws][solid] added inline patchRegistry for NativeScript HMR');
3334
+ }
3348
3335
  }
3349
3336
  // Work on a copy to avoid mutating Vite's cached TransformResult
3350
3337
  transformed = { ...transformed, code: patchedCode };
@@ -3369,13 +3356,10 @@ function createHmrWebSocketPlugin(opts) {
3369
3356
  if (isApp && /\.(ts|tsx|js|jsx|mjs|mts|cts)$/i.test(id) && !isRuntimeGraphExcludedPath(id)) {
3370
3357
  const deps = Array.from(collectImportDependencies(code, id));
3371
3358
  if (verbose) {
3372
- try {
3373
- console.log('[hmr-ws][ts-graph] candidate', { id, depsCount: deps.length });
3374
- }
3375
- catch { }
3359
+ console.log('[hmr-ws][ts-graph] candidate', { id, depsCount: deps.length });
3376
3360
  }
3377
3361
  // Serve-time warm-up: no live edit happened, so don't bump
3378
- // graphVersion. See Track 1.3 in HMR_CORE_REALM_DETERMINISTIC_PLAN.md.
3362
+ // graphVersion.
3379
3363
  upsertGraphModule(id, code, deps, { bumpVersion: false });
3380
3364
  }
3381
3365
  }
@@ -3599,19 +3583,20 @@ export const piniaSymbol = p.piniaSymbol;
3599
3583
  }
3600
3584
  }
3601
3585
  catch { }
3602
- // alpha.59 — `/ns/rt` and `/ns/core` URL versioning.
3586
+ // `/ns/rt` and `/ns/core` URL versioning.
3603
3587
  //
3604
- // Pre-alpha.59 these URLs were emitted as `/ns/rt/<ver>`
3605
- // and `/ns/core/<ver>` so V8's HTTP module cache would see
3606
- // a fresh URL on every save. The runtime canonicalizer
3607
- // (HMRSupport.mm `CanonicalizeHttpUrlKey`) collapses these
3608
- // version segments to the bare `/ns/rt` and `/ns/core`
3609
- // keys before lookup, so V8 actually saw a single cache
3610
- // entry — but the server was doing extra work to inject a
3611
- // version segment that the runtime then immediately
3612
- // stripped. Now that alpha.59 has explicit eviction (and
3613
- // these bridge endpoints don't change at HMR time
3614
- // anyway), the version segment is purely vestigial.
3588
+ // Older versions of the server emitted `/ns/rt/<ver>` and
3589
+ // `/ns/core/<ver>` so V8's HTTP module cache would see a
3590
+ // fresh URL on every save. The runtime canonicalizer
3591
+ // (`CanonicalizeHttpUrlKey` in HMRSupport.mm) collapses
3592
+ // these version segments to the bare `/ns/rt` and
3593
+ // `/ns/core` keys before lookup, so V8 actually saw a
3594
+ // single cache entry — but the server was doing extra
3595
+ // work to inject a version segment that the runtime then
3596
+ // immediately stripped. Now that the runtime supports
3597
+ // explicit eviction (and these bridge endpoints don't
3598
+ // change at HMR time anyway), the version segment is
3599
+ // purely vestigial.
3615
3600
  //
3616
3601
  // Rather than rip the helpers out (which would touch
3617
3602
  // every ensureVersionedImports caller and risk bumping
@@ -3627,25 +3612,25 @@ export const piniaSymbol = p.piniaSymbol;
3627
3612
  code = ensureVersionedCoreImports(code, getServerOrigin(server), verNum);
3628
3613
  }
3629
3614
  catch { }
3630
- // alpha.59 — `/ns/m` URL finalize step.
3615
+ // `/ns/m` URL finalize step.
3631
3616
  //
3632
- // `rewriteNsMImportPathForHmr` (Phase 3a) is now a
3633
- // canonicalizer: it strips legacy `__ns_hmr__/<tag>/`
3634
- // segments and adds `__ns_boot__/b1/` only for boot-tagged
3635
- // requests. The `ver` parameter is preserved on the
3636
- // signature for API compatibility but is ignored for app
3637
- // modules (cache busting is driven by
3638
- // `__nsInvalidateModules`, not URL versioning). We pass
3639
- // `'v0'` as a stable placeholder the canonicalizer
3640
- // emits the same URL regardless of this value, but a
3641
- // constant placeholder makes the contract explicit.
3617
+ // `rewriteNsMImportPathForHmr` is a canonicalizer: it
3618
+ // strips legacy `__ns_hmr__/<tag>/` segments and adds
3619
+ // `__ns_boot__/b1/` only for boot-tagged requests. The
3620
+ // `ver` parameter is preserved on the signature for API
3621
+ // compatibility but is ignored for app modules (cache
3622
+ // busting is driven by `__nsInvalidateModules`, not URL
3623
+ // versioning). We pass `'v0'` as a stable placeholder —
3624
+ // the canonicalizer emits the same URL regardless of
3625
+ // this value, but a constant placeholder makes the
3626
+ // contract explicit.
3642
3627
  //
3643
3628
  // SFC URLs (line below, `/ns/sfc/${verTag}/...`) still
3644
3629
  // embed a version because the Vue SFC pathway does not
3645
- // yet have an eviction protocol. The runtime canonicalizer
3646
- // does NOT strip `/ns/sfc/<ver>/`, so Vue users still see
3647
- // per-save SFC re-fetches — that's a known follow-up
3648
- // (HMR_STABLE_URL_INVALIDATION_PLAN.md "Vue Follow-up").
3630
+ // yet have an eviction protocol. The runtime
3631
+ // canonicalizer does NOT strip `/ns/sfc/<ver>/`, so Vue
3632
+ // users still see per-save SFC re-fetches — that's a
3633
+ // known follow-up.
3649
3634
  try {
3650
3635
  const verTag = (() => {
3651
3636
  const numeric = getNumericServeVersionTag(forcedVer, Number(graphVersion || 0));
@@ -3789,10 +3774,9 @@ export const piniaSymbol = p.piniaSymbol;
3789
3774
  // by the serving pipeline. Only warn, don't fatally block the importer.
3790
3775
  const hasCjsPattern = /\bmodule\s*\.\s*exports\b/.test(targetCode) || /\bexports\s*\.\s*\w/.test(targetCode);
3791
3776
  if (hasCjsPattern) {
3792
- try {
3777
+ if (verbose) {
3793
3778
  console.warn(`[ns:m][link-check] CJS module without export default: ${u.pathname} (will be CJS-wrapped at serve time)`);
3794
3779
  }
3795
- catch { }
3796
3780
  continue;
3797
3781
  }
3798
3782
  const msg = `[link-check] Missing default export in ${u.pathname}${u.search} (imported by ${resolvedCandidate || spec})`;
@@ -3806,19 +3790,15 @@ export const piniaSymbol = p.piniaSymbol;
3806
3790
  }
3807
3791
  }
3808
3792
  catch (eLC) {
3809
- try {
3793
+ if (verbose) {
3810
3794
  console.warn('[ns:m][link-check] failed', eLC?.message || eLC);
3811
3795
  }
3812
- catch { }
3813
3796
  }
3814
3797
  res.statusCode = 200;
3815
3798
  res.end(code);
3816
3799
  }
3817
3800
  catch (e) {
3818
- try {
3819
- console.warn('[sfc-asm] error serving', req.url, e && e.message ? e.message : e);
3820
- }
3821
- catch { }
3801
+ console.warn('[sfc-asm] error serving', req.url, e && e.message ? e.message : e);
3822
3802
  res.statusCode = 500;
3823
3803
  res.end('export {}\n');
3824
3804
  }
@@ -3851,8 +3831,10 @@ export const piniaSymbol = p.piniaSymbol;
3851
3831
  `let __cached_rt = null;\n` +
3852
3832
  `let __cached_vm = null;\n` +
3853
3833
  `const __RT_REALM_TAG = (globalThis.__NS_RT_REALM__ ||= Math.random().toString(36).slice(2));\n` +
3854
- // Unconditional one-shot evaluation marker to confirm bridge is executed on device
3855
- `try { if (!(globalThis.__NS_RT_ONCE__ && globalThis.__NS_RT_ONCE__.eval)) { (globalThis.__NS_RT_ONCE__ ||= {}).eval = true; console.log('[ns-rt] evaluated', { rtRealm: __RT_REALM_TAG }); } } catch {}\n` +
3834
+ // One-shot evaluation marker to confirm the bridge is executed on
3835
+ // device. Gated on __NS_ENV_VERBOSE__ so it stays silent unless
3836
+ // the developer opts in via NS_VITE_VERBOSE / VITE_DEBUG_LOGS.
3837
+ `try { if (!(globalThis.__NS_RT_ONCE__ && globalThis.__NS_RT_ONCE__.eval)) { (globalThis.__NS_RT_ONCE__ ||= {}).eval = true; if (globalThis.__NS_ENV_VERBOSE__) console.log('[ns-rt] evaluated', { rtRealm: __RT_REALM_TAG }); } } catch {}\n` +
3856
3838
  `function __ensure(){\n` +
3857
3839
  ` if (__cached_rt) return __cached_rt;\n` +
3858
3840
  ` let vm = null;\n` +
@@ -3944,7 +3926,7 @@ export const piniaSymbol = p.piniaSymbol;
3944
3926
  `export const vShow = (__ensure().vShow);\n` +
3945
3927
  `export const createApp = (...a) => (__ensure().createApp)(...a);\n` +
3946
3928
  `export const registerElement = (...a) => (__ensure().registerElement)(...a);\n` +
3947
- `export const $navigateTo = (...a) => { const vm = (__cached_vm || (void __ensure(), __cached_vm)); const rt = __ensure(); try { if (!(g && g.Frame)) { const ns = (__ns_core_bridge && (__ns_core_bridge.__esModule && __ns_core_bridge.default ? __ns_core_bridge.default : (__ns_core_bridge.default || __ns_core_bridge))) || __ns_core_bridge || {}; if (ns) { if (!g.Frame && ns.Frame) g.Frame = ns.Frame; if (!g.Page && ns.Page) g.Page = ns.Page; if (!g.Application && (ns.Application||ns.app||ns.application)) g.Application = (ns.Application||ns.app||ns.application); } } } catch {} try { const hmrRealm = (g && g.__NS_HMR_REALM__) || 'unknown'; const hasTop = !!(g && g.Frame && g.Frame.topmost && g.Frame.topmost()); const top = hasTop ? g.Frame.topmost() : null; const ctor = top && top.constructor && top.constructor.name; } catch {} if (g && typeof g.__nsNavigateUsingApp === 'function') { try { return g.__nsNavigateUsingApp(...a); } catch (e) { try { console.error('[ns-rt] $navigateTo app navigator error', e); } catch {} throw e; } } try { console.error('[ns-rt] $navigateTo unavailable: app navigator missing'); } catch {} throw new Error('$navigateTo unavailable: app navigator missing'); } ;\n` +
3929
+ `export const $navigateTo = (...a) => { const vm = (__cached_vm || (void __ensure(), __cached_vm)); const rt = __ensure(); try { if (!(g && g.Frame)) { const ns = (__ns_core_bridge && (__ns_core_bridge.__esModule && __ns_core_bridge.default ? __ns_core_bridge.default : (__ns_core_bridge.default || __ns_core_bridge))) || __ns_core_bridge || {}; if (ns) { if (!g.Frame && ns.Frame) g.Frame = ns.Frame; if (!g.Page && ns.Page) g.Page = ns.Page; if (!g.Application && (ns.Application||ns.app||ns.application)) g.Application = (ns.Application||ns.app||ns.application); } } } catch {} try { const hmrRealm = (g && g.__NS_HMR_REALM__) || 'unknown'; const hasTop = !!(g && g.Frame && g.Frame.topmost && g.Frame.topmost()); const top = hasTop ? g.Frame.topmost() : null; const ctor = top && top.constructor && top.constructor.name; } catch {} if (g && typeof g.__nsNavigateUsingApp === 'function') { try { return g.__nsNavigateUsingApp(...a); } catch (e) { console.error('[ns-rt] $navigateTo app navigator error', e); throw e; } } console.error('[ns-rt] $navigateTo unavailable: app navigator missing'); throw new Error('$navigateTo unavailable: app navigator missing'); } ;\n` +
3948
3930
  `export const $navigateBack = (...a) => { const vm = (__cached_vm || (void __ensure(), __cached_vm)); const rt = __ensure(); const impl = (vm && (vm.$navigateBack || (vm.default && vm.default.$navigateBack))) || (rt && (rt.$navigateBack || (rt.runtimeHelpers && rt.runtimeHelpers.navigateBack))); let res; try { const via = (impl && (impl === (vm && vm.$navigateBack) || impl === (vm && vm.default && vm.default.$navigateBack))) ? 'vm' : (impl ? 'rt' : 'none'); } catch {} try { if (typeof impl === 'function') res = impl(...a); } catch {} try { const top = (g && g.Frame && g.Frame.topmost && g.Frame.topmost()); if (!res && top && top.canGoBack && top.canGoBack()) { res = top.goBack(); } } catch {} try { const hook = g && (g.__NS_HMR_ON_NAVIGATE_BACK || g.__NS_HMR_ON_BACK || g.__nsAttemptBackRemount); if (typeof hook === 'function') hook(); } catch {} return res; }\n` +
3949
3931
  `export const $showModal = (...a) => { const vm = (__cached_vm || (void __ensure(), __cached_vm)); const rt = __ensure(); const impl = (vm && (vm.$showModal || (vm.default && vm.default.$showModal))) || (rt && (rt.$showModal || (rt.runtimeHelpers && rt.runtimeHelpers.showModal))); try { if (typeof impl === 'function') return impl(...a); } catch (e) { } return undefined; }\n` +
3950
3932
  `export default {\n` +
@@ -4032,15 +4014,15 @@ export const piniaSymbol = p.piniaSymbol;
4032
4014
  });
4033
4015
  // 2.6) ESM bridge for @nativescript/core: GET /ns/core[/<ver>][?p=sub/path]
4034
4016
  //
4035
- // Since bundle.mjs no longer bundles @nativescript/core (see
4036
- // HMR_CORE_REALM_DETERMINISTIC_PLAN.md external in the rolldown
4037
- // config under HMR), this endpoint is the ONE place core is
4038
- // evaluated. Every consumer — bundle.mjs's own `@nativescript/core*`
4039
- // imports (resolved to full HTTP URLs in the entry virtual module),
4040
- // externalized vendor packages, HTTP-served app modules — all end
4041
- // up here. No more proxy bridge, no enumeration, no namespace
4042
- // detection, no prototype-polluted maps. We just serve Vite's
4043
- // authoritative transformed module content.
4017
+ // Since bundle.mjs no longer bundles @nativescript/core (it is
4018
+ // declared external in the rolldown config under HMR), this
4019
+ // endpoint is the ONE place core is evaluated. Every consumer —
4020
+ // bundle.mjs's own `@nativescript/core*` imports (resolved to
4021
+ // full HTTP URLs in the entry virtual module), externalized
4022
+ // vendor packages, HTTP-served app modules — all end up here.
4023
+ // No more proxy bridge, no enumeration, no namespace detection,
4024
+ // no prototype-polluted maps. We just serve Vite's authoritative
4025
+ // transformed module content.
4044
4026
  //
4045
4027
  // iOS caches by URL path, so each unique URL is evaluated exactly
4046
4028
  // once per app lifetime. Every class identity is shared, every
@@ -4147,16 +4129,15 @@ export const piniaSymbol = p.piniaSymbol;
4147
4129
  `const __TEST__ = false;`,
4148
4130
  ].join('\n');
4149
4131
  // Boot-time instrumentation + module self-registration.
4150
- // See HMR_CORE_REALM_DETERMINISTIC_PLAN.md:
4151
- // - Invariant A (URL canonicalization): the same
4152
- // logical module must always resolve to byte-
4153
- // identical URLs across every emitter. The /ns/core
4154
- // handler records the first URL seen for each
4155
- // canonical sub (or '' for main) in
4156
- // `globalThis.__NS_CORE_FIRST_URL__` and fails hard
4157
- // on mismatch so drift in any emitter surfaces
4132
+ //
4133
+ // - URL canonicalization: the same logical module must
4134
+ // always resolve to byte-identical URLs across every
4135
+ // emitter. The /ns/core handler records the first URL
4136
+ // seen for each canonical sub (or '' for main) in
4137
+ // `globalThis.__NS_CORE_FIRST_URL__` and fails hard on
4138
+ // mismatch so drift in any emitter surfaces
4158
4139
  // immediately, before the realm splits.
4159
- // - Invariant C (boot-order): CommonJS
4140
+ // - CJS/ESM boot order: CommonJS
4160
4141
  // `require('@nativescript/core/...')` calls from
4161
4142
  // vendor install() hooks must resolve to the SAME
4162
4143
  // ESM namespace that ran this side-effect preamble.
@@ -4185,13 +4166,13 @@ export const piniaSymbol = p.piniaSymbol;
4185
4166
  ` const __nsUrl = ${JSON.stringify(canonicalUrl)};`,
4186
4167
  ` __nsSeen.push(__nsUrl);`,
4187
4168
  ` if (typeof __nsFirst[__nsKey] === 'string' && __nsFirst[__nsKey] !== __nsUrl) {`,
4188
- ` throw new Error('[ns-core] URL drift for sub=' + __nsKey + ': first=' + __nsFirst[__nsKey] + ' now=' + __nsUrl + ' (see HMR_CORE_REALM_DETERMINISTIC_PLAN.md Invariant A)');`,
4169
+ ` throw new Error('[ns-core] URL drift for sub=' + __nsKey + ': first=' + __nsFirst[__nsKey] + ' now=' + __nsUrl);`,
4189
4170
  ` }`,
4190
4171
  ` if (!__nsFirst[__nsKey]) __nsFirst[__nsKey] = __nsUrl;`,
4191
4172
  ` globalThis.__NS_CORE_EVAL_COUNT__ = (globalThis.__NS_CORE_EVAL_COUNT__ || 0) + 1;`,
4192
- `} } catch (e) { try { console.warn('[ns-core] instrumentation failed:', (e && e.message) || e); } catch {} }`,
4173
+ `} } catch (e) { console.warn('[ns-core] instrumentation failed:', (e && e.message) || e); }`,
4193
4174
  ].join('\n');
4194
- // Invariant D (CJS/ESM interop shape) — REGISTRATION side.
4175
+ // CJS/ESM interop shape — REGISTRATION side.
4195
4176
  //
4196
4177
  // The actual shape installer runs earlier in the module
4197
4178
  // body (between preamble and selfImport; see
@@ -4207,9 +4188,6 @@ export const piniaSymbol = p.piniaSymbol;
4207
4188
  // registration — the shape function is identity-preserving
4208
4189
  // via WeakMap — gives a stable, shared, CJS-compatible
4209
4190
  // view without copying on every require.
4210
- //
4211
- // See HMR_CORE_REALM_DETERMINISTIC_PLAN.md § "Invariant D"
4212
- // for the full rationale.
4213
4191
  const registrationFooter = [
4214
4192
  `try { if (typeof globalThis !== 'undefined') {`,
4215
4193
  ` const __nsReg = globalThis.__NS_CORE_MODULES__ || (globalThis.__NS_CORE_MODULES__ = Object.create(null));`,
@@ -4217,7 +4195,7 @@ export const piniaSymbol = p.piniaSymbol;
4217
4195
  ` const __nsSelfRaw = (typeof __ns_core_self_ns__ !== 'undefined') ? __ns_core_self_ns__ : { default: undefined };`,
4218
4196
  ` const __nsSelf = __nsShapeFn(__nsSelfRaw);`,
4219
4197
  ...registrationKeys.map((k) => ` __nsReg[${k}] = __nsSelf;`),
4220
- `} } catch (e) { try { console.warn('[ns-core] self-register failed:', (e && e.message) || e); } catch {} }`,
4198
+ `} } catch (e) { console.warn('[ns-core] self-register failed:', (e && e.message) || e); }`,
4221
4199
  ].join('\n');
4222
4200
  // Bind `import * as __ns_core_self_ns__` to the module's
4223
4201
  // own export namespace so the footer can stash it into
@@ -4265,10 +4243,7 @@ export const piniaSymbol = p.piniaSymbol;
4265
4243
  res.end(moduleCode);
4266
4244
  }
4267
4245
  catch (e) {
4268
- try {
4269
- console.warn('[ns-core-bridge] serve failed:', e?.message);
4270
- }
4271
- catch { }
4246
+ console.warn('[ns-core-bridge] serve failed:', e?.message);
4272
4247
  next();
4273
4248
  }
4274
4249
  });
@@ -4278,14 +4253,11 @@ export const piniaSymbol = p.piniaSymbol;
4278
4253
  const urlObj = new URL(req.url || '', 'http://localhost');
4279
4254
  if (!(urlObj.pathname === '/ns/entry-rt'))
4280
4255
  return next();
4281
- try {
4282
- if (verbose) {
4283
- const ra = req.socket?.remoteAddress;
4284
- const rp = req.socket?.remotePort;
4285
- console.log('[hmr-http] GET /ns/entry-rt from', ra + (rp ? ':' + rp : ''));
4286
- }
4256
+ if (verbose) {
4257
+ const ra = req.socket?.remoteAddress;
4258
+ const rp = req.socket?.remotePort;
4259
+ console.log('[hmr-http] GET /ns/entry-rt from', ra + (rp ? ':' + rp : ''));
4287
4260
  }
4288
- catch { }
4289
4261
  res.setHeader('Access-Control-Allow-Origin', '*');
4290
4262
  res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
4291
4263
  res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
@@ -4321,7 +4293,8 @@ export const piniaSymbol = p.piniaSymbol;
4321
4293
  content = 'export default async function start(){ console.error("[/ns/entry-rt] not found"); }\n';
4322
4294
  }
4323
4295
  }
4324
- console.log('[hmr-http] /ns/entry-rt serving', content.length, 'bytes');
4296
+ if (verbose)
4297
+ console.log('[hmr-http] /ns/entry-rt serving', content.length, 'bytes');
4325
4298
  res.statusCode = 200;
4326
4299
  res.end(content);
4327
4300
  }
@@ -4529,10 +4502,7 @@ export const piniaSymbol = p.piniaSymbol;
4529
4502
  }
4530
4503
  if (!transformed?.code) {
4531
4504
  if (verbose) {
4532
- try {
4533
- console.warn(`[sfc][serve] transform miss for`, fullSpec);
4534
- }
4535
- catch { }
4505
+ console.warn(`[sfc][serve] transform miss for`, fullSpec);
4536
4506
  }
4537
4507
  // Emit an erroring module to surface the failure at import site with helpful hints
4538
4508
  try {
@@ -4648,10 +4618,7 @@ export const piniaSymbol = p.piniaSymbol;
4648
4618
  }
4649
4619
  catch (eTplSelf) {
4650
4620
  if (verbose) {
4651
- try {
4652
- console.warn('[sfc][template][self-compile][fail]', fullSpec, eTplSelf?.message);
4653
- }
4654
- catch { }
4621
+ console.warn('[sfc][template][self-compile][fail]', fullSpec, eTplSelf?.message);
4655
4622
  }
4656
4623
  code = transformed.code || 'export {}\n';
4657
4624
  code = processTemplateVariantMinimal(code);
@@ -4818,10 +4785,7 @@ export const piniaSymbol = p.piniaSymbol;
4818
4785
  }
4819
4786
  catch (eTsVar) {
4820
4787
  if (verbose) {
4821
- try {
4822
- console.warn('[sfc][variant:script][babel-ts][fail]', fullSpec, eTsVar?.message);
4823
- }
4824
- catch { }
4788
+ console.warn('[sfc][variant:script][babel-ts][fail]', fullSpec, eTsVar?.message);
4825
4789
  }
4826
4790
  }
4827
4791
  }
@@ -4885,10 +4849,7 @@ export const piniaSymbol = p.piniaSymbol;
4885
4849
  const kind = isVariant ? `variant:${variantType || 'unknown'}` : 'full';
4886
4850
  const sig = `// [sfc] kind=${kind} path=${importerPath} len=${code.length} default=${hasDefault} wrapped=${false}\n`;
4887
4851
  if (verbose) {
4888
- try {
4889
- console.log(`[sfc][serve] ${fullSpec} kind=${kind} default=${hasDefault} bytes=${code.length}`);
4890
- }
4891
- catch { }
4852
+ console.log(`[sfc][serve] ${fullSpec} kind=${kind} default=${hasDefault} bytes=${code.length}`);
4892
4853
  }
4893
4854
  // Ensure script variants always provide a default export if they declare a component
4894
4855
  if (!hasDefault) {
@@ -4957,16 +4918,10 @@ export const piniaSymbol = p.piniaSymbol;
4957
4918
  // 6) Object property forms (rare in template output) render: (...) => {}
4958
4919
  const hasRender = /export\s+function\s+render\s*\(/.test(templateCode) || /(?:^|\n)\s*function\s+render\s*\(/.test(templateCode) || /export\s+(?:const|let|var)\s+render\s*=/.test(templateCode) || /(?:^|\n)\s*(?:const|let|var)\s+render\s*=/.test(templateCode) || /\brender\s*[:=]\s*/.test(templateCode) || /export\s*\{\s*render\s*(?:as\s*render)?\s*\}/.test(templateCode);
4959
4920
  if (hasRender && verbose) {
4960
- try {
4961
- console.log('[sfc-meta] detected render for', base);
4962
- }
4963
- catch { }
4921
+ console.log('[sfc-meta] detected render for', base);
4964
4922
  }
4965
4923
  else if (!hasRender && verbose) {
4966
- try {
4967
- console.warn('[sfc-meta] render NOT detected for', base);
4968
- }
4969
- catch { }
4924
+ console.warn('[sfc-meta] render NOT detected for', base);
4970
4925
  }
4971
4926
  const hash = createHash('md5').update(base).digest('hex').slice(0, 8);
4972
4927
  const payload = {
@@ -5100,10 +5055,7 @@ export const piniaSymbol = p.piniaSymbol;
5100
5055
  }
5101
5056
  catch (eScript) {
5102
5057
  if (verbose) {
5103
- try {
5104
- console.warn('[sfc-asm][compileScript] failed', base, eScript?.message);
5105
- }
5106
- catch { }
5058
+ console.warn('[sfc-asm][compileScript] failed', base, eScript?.message);
5107
5059
  }
5108
5060
  // Retry without inlineTemplate
5109
5061
  try {
@@ -5119,10 +5071,7 @@ export const piniaSymbol = p.piniaSymbol;
5119
5071
  }
5120
5072
  catch (eNoInline) {
5121
5073
  if (verbose) {
5122
- try {
5123
- console.warn('[sfc-asm][compileScript][no-inline-fallback] failed', base, eNoInline?.message);
5124
- }
5125
- catch { }
5074
+ console.warn('[sfc-asm][compileScript][no-inline-fallback] failed', base, eNoInline?.message);
5126
5075
  }
5127
5076
  }
5128
5077
  }
@@ -5153,10 +5102,7 @@ export const piniaSymbol = p.piniaSymbol;
5153
5102
  }
5154
5103
  catch (eNoInline) {
5155
5104
  if (verbose) {
5156
- try {
5157
- console.warn('[sfc-asm][compileScript][no-inline-fallback] failed', base, eNoInline?.message);
5158
- }
5159
- catch { }
5105
+ console.warn('[sfc-asm][compileScript][no-inline-fallback] failed', base, eNoInline?.message);
5160
5106
  }
5161
5107
  }
5162
5108
  }
@@ -5179,20 +5125,14 @@ export const piniaSymbol = p.piniaSymbol;
5179
5125
  });
5180
5126
  compiledTplCode = (ct && (ct.code || '')) || '';
5181
5127
  if (ct?.errors?.length && verbose) {
5182
- try {
5183
- console.warn('[sfc-asm][compileTemplate][errors]', base, ct.errors);
5184
- }
5185
- catch { }
5128
+ console.warn('[sfc-asm][compileTemplate][errors]', base, ct.errors);
5186
5129
  }
5187
5130
  }
5188
5131
  }
5189
5132
  catch (eTpl) {
5190
5133
  templateErr = eTpl;
5191
5134
  if (verbose) {
5192
- try {
5193
- console.warn('[sfc-asm][compileTemplate] failed', base, eTpl?.message);
5194
- }
5195
- catch { }
5135
+ console.warn('[sfc-asm][compileTemplate] failed', base, eTpl?.message);
5196
5136
  }
5197
5137
  // Fallback: use the variant-transformed template code if available
5198
5138
  try {
@@ -5255,10 +5195,7 @@ export const piniaSymbol = p.piniaSymbol;
5255
5195
  }
5256
5196
  catch (eExtract) {
5257
5197
  if (verbose) {
5258
- try {
5259
- console.warn('[sfc-asm][extractTemplateRender] failed', base, eExtract?.message);
5260
- }
5261
- catch { }
5198
+ console.warn('[sfc-asm][extractTemplateRender] failed', base, eExtract?.message);
5262
5199
  }
5263
5200
  }
5264
5201
  }
@@ -5336,10 +5273,7 @@ export const piniaSymbol = p.piniaSymbol;
5336
5273
  }
5337
5274
  catch (eTs) {
5338
5275
  if (verbose) {
5339
- try {
5340
- console.warn('[sfc-asm][babel-ts][fail]', base, eTs?.message);
5341
- }
5342
- catch { }
5276
+ console.warn('[sfc-asm][babel-ts][fail]', base, eTs?.message);
5343
5277
  }
5344
5278
  }
5345
5279
  // Hoist imports + strip residual TS via AST
@@ -5349,18 +5283,12 @@ export const piniaSymbol = p.piniaSymbol;
5349
5283
  importLines = astRes.imports;
5350
5284
  scriptTransformed = astRes.body;
5351
5285
  if (astRes.diagnostics.length && verbose) {
5352
- try {
5353
- console.warn('[sfc-asm][ast]', base, astRes.diagnostics.join('; '));
5354
- }
5355
- catch { }
5286
+ console.warn('[sfc-asm][ast]', base, astRes.diagnostics.join('; '));
5356
5287
  }
5357
5288
  }
5358
5289
  catch (eAst) {
5359
5290
  if (verbose) {
5360
- try {
5361
- console.warn('[sfc-asm][ast][fail]', base, eAst?.message);
5362
- }
5363
- catch { }
5291
+ console.warn('[sfc-asm][ast][fail]', base, eAst?.message);
5364
5292
  }
5365
5293
  }
5366
5294
  // Ensure renderDecl ends with closing brace ONLY for function declaration forms
@@ -6054,13 +5982,12 @@ export const piniaSymbol = p.piniaSymbol;
6054
5982
  if (isRuntimeGraphExcludedPath(file)) {
6055
5983
  return;
6056
5984
  }
6057
- // Always-on update timing see HMR_CORE_REALM_DETERMINISTIC_PLAN.md
6058
- // ("Track 2 — round one, 2026-04: HMR update metrics"). Captures
6059
- // the four phases (await, framework, broadcast, total) plus
6060
- // invalidated module count and recipient count. Emitted at the
6061
- // end of this function via `emitHmrUpdateSummary()`. Single line,
6062
- // always-on so a 6-second `.ts` save is immediately visible
6063
- // without flipping verbose.
5985
+ // Always-on update timing. Captures the four phases (await,
5986
+ // framework, broadcast, total) plus invalidated module count
5987
+ // and recipient count. Emitted at the end of this function via
5988
+ // `emitHmrUpdateSummary()`. Single line, always-on so a
5989
+ // 6-second `.ts` save is immediately visible without flipping
5990
+ // verbose.
6064
5991
  const updateRoot = server.config.root || process.cwd();
6065
5992
  const updateRel = (() => {
6066
5993
  try {
@@ -6079,21 +6006,19 @@ export const piniaSymbol = p.piniaSymbol;
6079
6006
  tEnd: 0,
6080
6007
  invalidated: 0,
6081
6008
  recipients: 0,
6082
- // Round-eight diagnostic — populated by the angular branch when
6009
+ // Narrowing diagnostic — populated by the angular branch when
6083
6010
  // the changed file is `.ts`, otherwise remains undefined and is
6084
- // omitted from the summary line entirely. See
6085
- // HMR_CORE_REALM_DETERMINISTIC_PLAN.md ("Round-eight — surface
6086
- // narrowing decision").
6011
+ // omitted from the summary line entirely.
6087
6012
  narrowed: undefined,
6088
6013
  emitted: false,
6089
6014
  };
6090
- // alpha.62 follow-up — broadcast a "pending" notification at
6091
- // the very start of handleHotUpdate so the client can show
6092
- // the HMR-applying overlay BEFORE we spend time on graph
6093
- // updates / transforms / dependency analysis (typically
6094
- // 7–200ms on a warm cache). Without this, the overlay only
6095
- // appears at `ns:angular-update` broadcast time and the
6096
- // user perceives a "delayed" reaction to their save.
6015
+ // Broadcast a "pending" notification at the very start of
6016
+ // handleHotUpdate so the client can show the HMR-applying
6017
+ // overlay BEFORE we spend time on graph updates / transforms /
6018
+ // dependency analysis (typically 7–200ms on a warm cache).
6019
+ // Without this, the overlay only appears at `ns:angular-update`
6020
+ // broadcast time and the user perceives a "delayed" reaction
6021
+ // to their save.
6097
6022
  //
6098
6023
  // Fire-and-forget: a failed pending broadcast must never
6099
6024
  // hold up the actual update. The client treats receipt of
@@ -6140,10 +6065,11 @@ export const piniaSymbol = p.piniaSymbol;
6140
6065
  }
6141
6066
  catch { }
6142
6067
  };
6143
- // Track 1.3: the first /ns/m request kicks off populateInitialGraph
6144
- // in the background. If an HMR update races in before that walk
6145
- // completes, we'd lose transitive-importer data. Await completion
6146
- // here so the delta computation below always sees a populated graph.
6068
+ // The first /ns/m request kicks off populateInitialGraph in the
6069
+ // background. If an HMR update races in before that walk
6070
+ // completes, we'd lose transitive-importer data. Await
6071
+ // completion here so the delta computation below always sees a
6072
+ // populated graph.
6147
6073
  if (graphInitialPopulationPromise) {
6148
6074
  try {
6149
6075
  await graphInitialPopulationPromise;
@@ -6160,6 +6086,7 @@ export const piniaSymbol = p.piniaSymbol;
6160
6086
  flavor: ACTIVE_STRATEGY.flavor,
6161
6087
  modules: ctx.modules,
6162
6088
  getModuleById: (id) => server.moduleGraph.getModuleById(id),
6089
+ verbose,
6163
6090
  });
6164
6091
  for (const mod of graphTargets) {
6165
6092
  if (!mod?.id)
@@ -6240,6 +6167,12 @@ export const piniaSymbol = p.piniaSymbol;
6240
6167
  getModuleById: (id) => server.moduleGraph.getModuleById(id),
6241
6168
  getModulesByFile: (targetFile) => server.moduleGraph.getModulesByFile?.(targetFile),
6242
6169
  });
6170
+ if (verbose) {
6171
+ console.info(`[ns-hmr-diag][server] hot-update file=${file} isHtml=${isHtml} isTs=${isTs} ctxModules=${Array.from(ctx.modules || []).length} hotUpdateRoots=${angularHotUpdateRoots.length} (${angularHotUpdateRoots
6172
+ .map((m) => m?.id ?? '(none)')
6173
+ .slice(0, 8)
6174
+ .join(', ')}${angularHotUpdateRoots.length > 8 ? ', …' : ''})`);
6175
+ }
6243
6176
  if (!(isHtml || isTs))
6244
6177
  return;
6245
6178
  updateMetrics.invalidated += angularHotUpdateRoots.length;
@@ -6259,13 +6192,13 @@ export const piniaSymbol = p.piniaSymbol;
6259
6192
  }
6260
6193
  }
6261
6194
  const angularTransitiveInvalidationRoots = (angularHotUpdateRoots.length ? angularHotUpdateRoots : ctx.modules);
6262
- // Round-six narrowing: read the source for `.ts/.tsx/.js/.jsx`
6263
- // edits so `shouldInvalidateAngularTransitiveImporters` can
6195
+ // Read the source for `.ts/.tsx/.js/.jsx` edits so
6196
+ // `shouldInvalidateAngularTransitiveImporters` can
6264
6197
  // distinguish leaf modules (constants/utils) from real
6265
6198
  // Angular files. If `ctx.read()` throws (file deleted, race
6266
6199
  // against the watcher), `angularChangedSource` stays
6267
- // undefined and we fall back to the conservative
6268
- // pre-round-six "always invalidate transitively" behavior.
6200
+ // undefined and we fall back to the conservative "always
6201
+ // invalidate transitively" behavior.
6269
6202
  let angularChangedSource;
6270
6203
  if (isTs) {
6271
6204
  try {
@@ -6280,37 +6213,36 @@ export const piniaSymbol = p.piniaSymbol;
6280
6213
  file,
6281
6214
  source: angularChangedSource,
6282
6215
  });
6283
- // Round-eight diagnostic. We surface the narrowing decision on
6284
- // every `.ts` Angular hot update (HTML routes always invalidate
6285
- // transitively today and aren't subject to Round-Seven, so we
6286
- // leave them as `undefined` — the field is omitted from the
6287
- // summary line). The boolean is the inverse of
6288
- // `angularNeedsTransitive` because "needs transitive" is the
6289
- // pre-Round-Seven (broad) behavior.
6216
+ // Surface the narrowing decision on every `.ts` Angular hot
6217
+ // update (HTML routes always invalidate transitively and
6218
+ // aren't subject to narrowing, so we leave them as
6219
+ // `undefined` — the field is omitted from the summary line).
6220
+ // The boolean is the inverse of `angularNeedsTransitive`
6221
+ // because "needs transitive" is the broad (un-narrowed)
6222
+ // behavior.
6290
6223
  if (isTs) {
6291
6224
  updateMetrics.narrowed = !angularNeedsTransitive;
6292
6225
  }
6293
- // alpha.59 — Stable URL + Explicit Invalidation:
6226
+ // Stable URL + Explicit Invalidation:
6294
6227
  //
6295
- // Compute the transitive importer closure ONCE here and reuse it
6296
- // for (a) `server.moduleGraph.invalidateModule` (so Vite's
6228
+ // Compute the transitive importer closure ONCE here and reuse
6229
+ // it for (a) `server.moduleGraph.invalidateModule` (so Vite's
6297
6230
  // transform pipeline re-runs on next request), (b) the shared
6298
- // transform-request cache, and (c) the runtime eviction set we
6299
- // broadcast in `ns:angular-update`. Pre-alpha.59 code computed
6300
- // this list twice in adjacent try blocks; consolidating it
6231
+ // transform-request cache, and (c) the runtime eviction set
6232
+ // we broadcast in `ns:angular-update`. Consolidating this
6301
6233
  // removes a redundant graph walk and guarantees the three
6302
6234
  // consumers see the exact same set of importers (otherwise a
6303
6235
  // late module-graph mutation between calls could leave an
6304
6236
  // asymmetric narrowed/broad mix).
6305
6237
  //
6306
- // alpha.59.1 separate Vite-transform narrowing from runtime
6307
- // eviction. `angularNeedsTransitive` answers the question "does
6308
- // the changed file's symbol shape change such that importers
6309
- // must be re-transformed by Vite?". The runtime, however, has
6310
- // a stricter requirement: ESM live bindings only refresh if
6311
- // the importing module re-evaluates inside V8. A constants
6312
- // file with no Angular decorator does NOT need a Vite
6313
- // re-transform of its importers (their compiled JS is
6238
+ // We separate Vite-transform narrowing from runtime eviction:
6239
+ // `angularNeedsTransitive` answers the question "does the
6240
+ // changed file's symbol shape change such that importers
6241
+ // must be re-transformed by Vite?". The runtime, however,
6242
+ // has a stricter requirement: ESM live bindings only refresh
6243
+ // if the importing module re-evaluates inside V8. A
6244
+ // constants file with no Angular decorator does NOT need a
6245
+ // Vite re-transform of its importers (their compiled JS is
6314
6246
  // identical), but its importers still hold stale bindings to
6315
6247
  // the OLD constants Module record. After eviction + re-import
6316
6248
  // of `main.ts`, V8 sees the cached importers, returns them
@@ -6318,12 +6250,16 @@ export const piniaSymbol = p.piniaSymbol;
6318
6250
  // user-visible symptom: HMR completes successfully, logs are
6319
6251
  // clean, but the simulator does not reflect the change.
6320
6252
  //
6321
- // The fix: ALWAYS compute the transitive importer closure for
6322
- // runtime eviction. Only skip Vite's `moduleGraph.invalidate`
6323
- // + transform-cache purge when `angularNeedsTransitive` is
6324
- // false — those are the genuine narrowing wins (saves
6325
- // re-transform work on the server). The eviction set always
6326
- // includes importers so V8 re-fetches and re-binds them.
6253
+ // The fix: ALWAYS compute the transitive importer closure
6254
+ // for runtime eviction. Only skip Vite's
6255
+ // `moduleGraph.invalidate` + transform-cache purge when
6256
+ // `angularNeedsTransitive` is false — those are the genuine
6257
+ // narrowing wins (saves re-transform work on the server).
6258
+ // The eviction set always includes importers so V8 re-fetches
6259
+ // and re-binds them.
6260
+ if (verbose) {
6261
+ console.info(`[ns-hmr-diag][server] angularNeedsTransitive=${angularNeedsTransitive} (file=${path.basename(file)})`);
6262
+ }
6327
6263
  let transitiveImporters = [];
6328
6264
  try {
6329
6265
  transitiveImporters = collectAngularTransitiveImportersForInvalidation({
@@ -6331,6 +6267,9 @@ export const piniaSymbol = p.piniaSymbol;
6331
6267
  isExcluded: (id) => id.includes('/node_modules/'),
6332
6268
  maxDepth: 16,
6333
6269
  });
6270
+ if (verbose) {
6271
+ console.info(`[ns-hmr-diag][server] transitiveImporters count=${transitiveImporters.length} firstN=`, transitiveImporters.slice(0, 16).map((m) => m?.id ?? '(none)'));
6272
+ }
6334
6273
  if (angularNeedsTransitive) {
6335
6274
  updateMetrics.invalidated += transitiveImporters.length;
6336
6275
  for (const mod of transitiveImporters) {
@@ -6348,19 +6287,18 @@ export const piniaSymbol = p.piniaSymbol;
6348
6287
  }
6349
6288
  }
6350
6289
  else if (isTs && typeof angularChangedSource === 'string') {
6351
- // Round-eight: was previously gated on `verbose`. Surfacing this
6352
- // log unconditionally lets the user immediately confirm whether
6353
- // Round-Seven's narrowing fired for a given `.ts` edit (the
6354
- // summary line below still emits `narrowed=yes`/`no`, but
6355
- // having both makes the decision easier to spot in noisy logs
6356
- // and lets the user diff scenarios without flipping
6290
+ // Surfacing this log unconditionally lets the user
6291
+ // immediately confirm whether narrowing fired for a
6292
+ // given `.ts` edit (the summary line below still
6293
+ // emits `narrowed=yes`/`no`, but having both makes
6294
+ // the decision easier to spot in noisy logs and lets
6295
+ // the user diff scenarios without flipping
6357
6296
  // `NS_HMR_VERBOSE=true`).
6358
6297
  //
6359
- // alpha.59.1 — narrowing now means "skip Vite re-transform"
6360
- // (the importers still get evicted from the V8 module
6361
- // registry so live bindings refresh). The log call-out is
6362
- // preserved but extended with the importer count to make the
6363
- // distinction visible.
6298
+ // Narrowing means "skip Vite re-transform" (the
6299
+ // importers still get evicted from the V8 module
6300
+ // registry so live bindings refresh). The importer
6301
+ // count is appended so the distinction is visible.
6364
6302
  console.log(`[hmr-ws][angular] narrowed transitive invalidation (no @Component/@Directive/@Pipe/@Injectable/@NgModule): ${updateRel} — Vite transform skipped, runtime eviction includes ${transitiveImporters.length} importer(s)`);
6365
6303
  }
6366
6304
  }
@@ -6369,13 +6307,13 @@ export const piniaSymbol = p.piniaSymbol;
6369
6307
  console.warn('[hmr-ws][angular] transitive importer collection failed', error);
6370
6308
  }
6371
6309
  try {
6372
- // alpha.59.1 — purge shared transform cache for the changed
6373
- // file + hot-update roots unconditionally (their transform
6374
- // output IS different now). Transitive importers are only
6375
- // purged when narrowing decides their output may have
6376
- // changed; otherwise their cached transforms are still
6377
- // valid (compiled JS is identical even though the runtime
6378
- // must re-evaluate them to refresh ESM bindings).
6310
+ // Purge shared transform cache for the changed file +
6311
+ // hot-update roots unconditionally (their transform
6312
+ // output IS different now). Transitive importers are
6313
+ // only purged when narrowing decides their output may
6314
+ // have changed; otherwise their cached transforms are
6315
+ // still valid (compiled JS is identical even though the
6316
+ // runtime must re-evaluate them to refresh ESM bindings).
6379
6317
  const transformCacheInvalidationUrls = new Set(collectAngularTransformCacheInvalidationUrls({
6380
6318
  file,
6381
6319
  isTs,
@@ -6401,19 +6339,19 @@ export const piniaSymbol = p.piniaSymbol;
6401
6339
  rememberAngularReloadSuppression(root, file);
6402
6340
  const origin = getServerOrigin(server);
6403
6341
  const bootstrapEntryRel = getBootstrapEntryRelPath();
6404
- // alpha.59 — Stable URL + Explicit Invalidation:
6342
+ // Stable URL + Explicit Invalidation:
6405
6343
  //
6406
6344
  // `evictPaths` is the canonical list of `/ns/m/<rel>` URLs
6407
6345
  // the runtime must drop from `g_moduleRegistry` before
6408
- // re-importing `importerEntry`. Pre-alpha.59 the server
6409
- // signaled invalidation by bumping a global `graphVersion`
6410
- // counter and embedding it in every URL — but V8 keys the
6411
- // module registry by full URL, so a v1 → v2 bump
6412
- // effectively flushed the entire dependency graph from
6413
- // the cache and forced the runtime to re-fetch + re-eval
6414
- // every transitively-imported module on each save (~3s
6415
- // HMR cycles, dominated by Vite's single-threaded transform
6416
- // pipeline). The new model:
6346
+ // re-importing `importerEntry`. Older versions of the
6347
+ // server signaled invalidation by bumping a global
6348
+ // `graphVersion` counter and embedding it in every URL —
6349
+ // but V8 keys the module registry by full URL, so a v1 →
6350
+ // v2 bump effectively flushed the entire dependency
6351
+ // graph from the cache and forced the runtime to
6352
+ // re-fetch + re-eval every transitively-imported module
6353
+ // on each save (~3s HMR cycles, dominated by Vite's
6354
+ // single-threaded transform pipeline). The new model:
6417
6355
  //
6418
6356
  // 1. URLs are stable: `/ns/m/<rel>` everywhere, no `vN`.
6419
6357
  // 2. The server walks the inverse-dependency closure and
@@ -6445,14 +6383,23 @@ export const piniaSymbol = p.piniaSymbol;
6445
6383
  });
6446
6384
  }
6447
6385
  catch (error) {
6448
- if (verbose)
6449
- console.warn('[hmr-ws][angular] eviction set computation failed', error);
6386
+ if (verbose) {
6387
+ console.warn('[ns-hmr-diag][server] eviction set computation failed', error);
6388
+ }
6450
6389
  }
6451
6390
  if (verbose) {
6452
- console.log('[hmr-ws][angular] eviction set', {
6453
- count: evictPaths.length,
6454
- importerEntry: bootstrapEntryRel,
6455
- });
6391
+ try {
6392
+ const tsRel = rel.replace(/\.(html|htm)$/i, '.ts');
6393
+ const jsRel = rel.replace(/\.(html|htm)$/i, '.js');
6394
+ const containsRelatedTs = evictPaths.some((u) => u.endsWith(tsRel));
6395
+ const containsRelatedJs = evictPaths.some((u) => u.endsWith(jsRel));
6396
+ const sample = evictPaths.slice(0, 32);
6397
+ console.info(`[ns-hmr-diag][server] evict-set count=${evictPaths.length} importerEntry=${bootstrapEntryRel ?? '(none)'} containsRelatedTs=${containsRelatedTs} containsRelatedJs=${containsRelatedJs} firstN=`, sample);
6398
+ if (evictPaths.length > sample.length) {
6399
+ console.info(`[ns-hmr-diag][server] evict-set hidden=${evictPaths.length - sample.length} (showed first ${sample.length})`);
6400
+ }
6401
+ }
6402
+ catch { }
6456
6403
  }
6457
6404
  const msg = {
6458
6405
  type: 'ns:angular-update',
@@ -6550,7 +6497,8 @@ export const piniaSymbol = p.piniaSymbol;
6550
6497
  console.log('[hmr-ws] Not a .vue file, skipping');
6551
6498
  return;
6552
6499
  }
6553
- console.log('[hmr-ws] Processing .vue file update...');
6500
+ if (verbose)
6501
+ console.log('[hmr-ws] Processing .vue file update...');
6554
6502
  try {
6555
6503
  const root = server.config.root || process.cwd();
6556
6504
  let rel = '/' + path.posix.normalize(path.relative(root, file)).split(path.sep).join('/');
@@ -6694,7 +6642,7 @@ if (typeof __VUE_HMR_RUNTIME__ === 'undefined') {
6694
6642
  if (typeof spec === 'string' && /^(?:https?:)\/\//.test(spec)) {
6695
6643
  const err = new Error('[ns-hmr][require-guard] require of URL: ' + spec + ' via ' + label);
6696
6644
  const stack = err.stack || '';
6697
- try { console.error(err.message + '\n' + stack); } catch {}
6645
+ console.error(err.message + '\n' + stack);
6698
6646
  try { g.__NS_REQUIRE_GUARD_LAST__ = { spec, stack, label, ts: Date.now() }; } catch {}
6699
6647
  }
6700
6648
  } catch {}