@nativescript/vite 8.0.0-alpha.4 → 8.0.0-alpha.40

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 (410) hide show
  1. package/README.md +51 -11
  2. package/bin/cli.cjs +8 -14
  3. package/configuration/angular.d.ts +34 -1
  4. package/configuration/angular.js +376 -165
  5. package/configuration/angular.js.map +1 -1
  6. package/configuration/base.js +256 -30
  7. package/configuration/base.js.map +1 -1
  8. package/configuration/javascript.js +12 -90
  9. package/configuration/javascript.js.map +1 -1
  10. package/configuration/solid.js +33 -4
  11. package/configuration/solid.js.map +1 -1
  12. package/configuration/typescript.js +10 -90
  13. package/configuration/typescript.js.map +1 -1
  14. package/helpers/app-components.d.ts +2 -1
  15. package/helpers/app-components.js.map +1 -1
  16. package/helpers/app-css-state.d.ts +8 -0
  17. package/helpers/app-css-state.js +8 -0
  18. package/helpers/app-css-state.js.map +1 -0
  19. package/helpers/bundler-context.d.ts +11 -0
  20. package/helpers/bundler-context.js +71 -0
  21. package/helpers/bundler-context.js.map +1 -0
  22. package/helpers/config-as-json.js +10 -0
  23. package/helpers/config-as-json.js.map +1 -1
  24. package/helpers/css-platform-plugin.d.ts +14 -0
  25. package/helpers/css-platform-plugin.js +43 -25
  26. package/helpers/css-platform-plugin.js.map +1 -1
  27. package/helpers/dev-host.d.ts +360 -0
  28. package/helpers/dev-host.js +692 -0
  29. package/helpers/dev-host.js.map +1 -0
  30. package/helpers/dynamic-import-plugin.js +1 -1
  31. package/helpers/dynamic-import-plugin.js.map +1 -1
  32. package/helpers/esbuild-platform-resolver.js +4 -1
  33. package/helpers/esbuild-platform-resolver.js.map +1 -1
  34. package/helpers/external-configs.d.ts +10 -12
  35. package/helpers/external-configs.js +54 -35
  36. package/helpers/external-configs.js.map +1 -1
  37. package/helpers/global-defines.d.ts +128 -0
  38. package/helpers/global-defines.js +174 -11
  39. package/helpers/global-defines.js.map +1 -1
  40. package/helpers/hmr-scope.d.ts +26 -0
  41. package/helpers/hmr-scope.js +67 -0
  42. package/helpers/hmr-scope.js.map +1 -0
  43. package/helpers/init.js +0 -18
  44. package/helpers/init.js.map +1 -1
  45. package/helpers/logging.d.ts +1 -0
  46. package/helpers/logging.js +65 -4
  47. package/helpers/logging.js.map +1 -1
  48. package/helpers/main-entry.d.ts +3 -1
  49. package/helpers/main-entry.js +444 -50
  50. package/helpers/main-entry.js.map +1 -1
  51. package/helpers/nativeclass-esbuild-plugin.d.ts +2 -1
  52. package/helpers/nativeclass-esbuild-plugin.js.map +1 -1
  53. package/helpers/nativeclass-transform.js +5 -6
  54. package/helpers/nativeclass-transform.js.map +1 -1
  55. package/helpers/nativeclass-transformer-plugin.d.ts +9 -2
  56. package/helpers/nativeclass-transformer-plugin.js +157 -14
  57. package/helpers/nativeclass-transformer-plugin.js.map +1 -1
  58. package/helpers/nativescript-package-resolver.js +10 -62
  59. package/helpers/nativescript-package-resolver.js.map +1 -1
  60. package/helpers/normalize-id.d.ts +42 -0
  61. package/helpers/normalize-id.js +60 -0
  62. package/helpers/normalize-id.js.map +1 -0
  63. package/helpers/ns-core-url.d.ts +106 -0
  64. package/helpers/ns-core-url.js +225 -0
  65. package/helpers/ns-core-url.js.map +1 -0
  66. package/helpers/optimize-deps.d.ts +16 -0
  67. package/helpers/optimize-deps.js +17 -0
  68. package/helpers/optimize-deps.js.map +1 -0
  69. package/helpers/package-platform-aliases.js +34 -49
  70. package/helpers/package-platform-aliases.js.map +1 -1
  71. package/helpers/platform-types.d.ts +2 -0
  72. package/helpers/platform-types.js +2 -0
  73. package/helpers/platform-types.js.map +1 -0
  74. package/helpers/postcss-platform-config.d.ts +17 -1
  75. package/helpers/postcss-platform-config.js +20 -37
  76. package/helpers/postcss-platform-config.js.map +1 -1
  77. package/helpers/project.d.ts +35 -0
  78. package/helpers/project.js +120 -2
  79. package/helpers/project.js.map +1 -1
  80. package/helpers/resolve-main-field-platform.d.ts +20 -0
  81. package/helpers/resolve-main-field-platform.js +49 -0
  82. package/helpers/resolve-main-field-platform.js.map +1 -0
  83. package/helpers/resolver.js +17 -2
  84. package/helpers/resolver.js.map +1 -1
  85. package/helpers/theme-core-plugins.js +1 -1
  86. package/helpers/theme-core-plugins.js.map +1 -1
  87. package/helpers/ts-config-paths.d.ts +14 -0
  88. package/helpers/ts-config-paths.js +90 -9
  89. package/helpers/ts-config-paths.js.map +1 -1
  90. package/helpers/typescript-check.d.ts +2 -1
  91. package/helpers/typescript-check.js.map +1 -1
  92. package/helpers/ui-registration.d.ts +21 -0
  93. package/helpers/ui-registration.js +156 -0
  94. package/helpers/ui-registration.js.map +1 -0
  95. package/helpers/utils.js +1 -2
  96. package/helpers/utils.js.map +1 -1
  97. package/helpers/workers.d.ts +20 -19
  98. package/helpers/workers.js +624 -4
  99. package/helpers/workers.js.map +1 -1
  100. package/hmr/client/css-handler.d.ts +2 -1
  101. package/hmr/client/css-handler.js +50 -26
  102. package/hmr/client/css-handler.js.map +1 -1
  103. package/hmr/client/css-update-overlay.d.ts +18 -0
  104. package/hmr/client/css-update-overlay.js +27 -0
  105. package/hmr/client/css-update-overlay.js.map +1 -0
  106. package/hmr/client/framework-client-strategy.d.ts +79 -0
  107. package/hmr/client/framework-client-strategy.js +19 -0
  108. package/hmr/client/framework-client-strategy.js.map +1 -0
  109. package/hmr/client/hmr-pending-overlay.d.ts +13 -0
  110. package/hmr/client/hmr-pending-overlay.js +60 -0
  111. package/hmr/client/hmr-pending-overlay.js.map +1 -0
  112. package/hmr/client/index.js +787 -211
  113. package/hmr/client/index.js.map +1 -1
  114. package/hmr/client/utils.d.ts +7 -1
  115. package/hmr/client/utils.js +207 -29
  116. package/hmr/client/utils.js.map +1 -1
  117. package/hmr/entry-runtime.d.ts +2 -1
  118. package/hmr/entry-runtime.js +256 -69
  119. package/hmr/entry-runtime.js.map +1 -1
  120. package/hmr/frameworks/angular/build/angular-linker.d.ts +12 -0
  121. package/hmr/frameworks/angular/build/angular-linker.js +109 -0
  122. package/hmr/frameworks/angular/build/angular-linker.js.map +1 -0
  123. package/hmr/frameworks/angular/build/inject-component-hmr-registration.d.ts +112 -0
  124. package/hmr/frameworks/angular/build/inject-component-hmr-registration.js +291 -0
  125. package/hmr/frameworks/angular/build/inject-component-hmr-registration.js.map +1 -0
  126. package/hmr/frameworks/angular/build/inject-hmr-vite-ignore.d.ts +75 -0
  127. package/hmr/frameworks/angular/build/inject-hmr-vite-ignore.js +221 -0
  128. package/hmr/frameworks/angular/build/inject-hmr-vite-ignore.js.map +1 -0
  129. package/{helpers/angular → hmr/frameworks/angular/build}/inline-decorator-component-templates.js +1 -170
  130. package/hmr/frameworks/angular/build/inline-decorator-component-templates.js.map +1 -0
  131. package/hmr/frameworks/angular/build/js-lexer.d.ts +4 -0
  132. package/{helpers/angular/synthesize-decorator-ctor-parameters.js → hmr/frameworks/angular/build/js-lexer.js} +22 -96
  133. package/hmr/frameworks/angular/build/js-lexer.js.map +1 -0
  134. package/hmr/frameworks/angular/build/shared-linker.d.ts +39 -0
  135. package/hmr/frameworks/angular/build/shared-linker.js +128 -0
  136. package/hmr/frameworks/angular/build/shared-linker.js.map +1 -0
  137. package/hmr/frameworks/angular/build/synthesize-decorator-ctor-parameters.js +88 -0
  138. package/hmr/frameworks/angular/build/synthesize-decorator-ctor-parameters.js.map +1 -0
  139. package/{helpers/angular → hmr/frameworks/angular/build}/synthesize-injectable-factories.js +1 -174
  140. package/hmr/frameworks/angular/build/synthesize-injectable-factories.js.map +1 -0
  141. package/{helpers/angular → hmr/frameworks/angular/build}/util.d.ts +1 -0
  142. package/hmr/frameworks/angular/build/util.js +155 -0
  143. package/hmr/frameworks/angular/build/util.js.map +1 -0
  144. package/hmr/frameworks/angular/client/index.d.ts +1 -0
  145. package/hmr/frameworks/angular/client/index.js +803 -21
  146. package/hmr/frameworks/angular/client/index.js.map +1 -1
  147. package/hmr/frameworks/angular/client/strategy.d.ts +9 -0
  148. package/hmr/frameworks/angular/client/strategy.js +19 -0
  149. package/hmr/frameworks/angular/client/strategy.js.map +1 -0
  150. package/hmr/frameworks/angular/server/angular-root-component.d.ts +79 -0
  151. package/hmr/frameworks/angular/server/angular-root-component.js +149 -0
  152. package/hmr/frameworks/angular/server/angular-root-component.js.map +1 -0
  153. package/hmr/frameworks/angular/server/linker.js +1 -4
  154. package/hmr/frameworks/angular/server/linker.js.map +1 -1
  155. package/hmr/frameworks/angular/server/strategy.js +460 -12
  156. package/hmr/frameworks/angular/server/strategy.js.map +1 -1
  157. package/hmr/{server → frameworks/angular/server}/websocket-angular-entry.js +2 -2
  158. package/hmr/frameworks/angular/server/websocket-angular-entry.js.map +1 -0
  159. package/hmr/{server → frameworks/angular/server}/websocket-angular-hot-update.d.ts +17 -11
  160. package/hmr/frameworks/angular/server/websocket-angular-hot-update.js +336 -0
  161. package/hmr/frameworks/angular/server/websocket-angular-hot-update.js.map +1 -0
  162. package/hmr/frameworks/solid/build/solid-jsx-deps.d.ts +15 -0
  163. package/hmr/frameworks/solid/build/solid-jsx-deps.js +178 -0
  164. package/hmr/frameworks/solid/build/solid-jsx-deps.js.map +1 -0
  165. package/hmr/frameworks/solid/server/strategy.js +291 -16
  166. package/hmr/frameworks/solid/server/strategy.js.map +1 -1
  167. package/hmr/frameworks/typescript/server/strategy.js +38 -14
  168. package/hmr/frameworks/typescript/server/strategy.js.map +1 -1
  169. package/hmr/frameworks/vue/client/dep-propagation.d.ts +36 -0
  170. package/hmr/frameworks/vue/client/dep-propagation.js +101 -0
  171. package/hmr/frameworks/vue/client/dep-propagation.js.map +1 -0
  172. package/hmr/frameworks/vue/client/index.d.ts +8 -0
  173. package/hmr/frameworks/vue/client/index.js +56 -243
  174. package/hmr/frameworks/vue/client/index.js.map +1 -1
  175. package/hmr/frameworks/vue/client/strategy.d.ts +33 -0
  176. package/hmr/frameworks/vue/client/strategy.js +157 -0
  177. package/hmr/frameworks/vue/client/strategy.js.map +1 -0
  178. package/hmr/frameworks/vue/client/vue-sfc-update-overlay.d.ts +49 -0
  179. package/hmr/frameworks/vue/client/vue-sfc-update-overlay.js +142 -0
  180. package/hmr/frameworks/vue/client/vue-sfc-update-overlay.js.map +1 -0
  181. package/hmr/frameworks/vue/server/sfc-route-assemble.d.ts +7 -0
  182. package/hmr/frameworks/vue/server/sfc-route-assemble.js +747 -0
  183. package/hmr/frameworks/vue/server/sfc-route-assemble.js.map +1 -0
  184. package/hmr/frameworks/vue/server/sfc-route-meta.d.ts +7 -0
  185. package/hmr/frameworks/vue/server/sfc-route-meta.js +80 -0
  186. package/hmr/frameworks/vue/server/sfc-route-meta.js.map +1 -0
  187. package/hmr/frameworks/vue/server/sfc-route-serve.d.ts +8 -0
  188. package/hmr/frameworks/vue/server/sfc-route-serve.js +459 -0
  189. package/hmr/frameworks/vue/server/sfc-route-serve.js.map +1 -0
  190. package/hmr/frameworks/vue/server/sfc-route-shared.d.ts +38 -0
  191. package/hmr/frameworks/vue/server/sfc-route-shared.js +48 -0
  192. package/hmr/frameworks/vue/server/sfc-route-shared.js.map +1 -0
  193. package/hmr/frameworks/vue/server/strategy.d.ts +35 -0
  194. package/hmr/frameworks/vue/server/strategy.js +278 -1
  195. package/hmr/frameworks/vue/server/strategy.js.map +1 -1
  196. package/hmr/frameworks/vue/server/websocket-sfc.d.ts +15 -0
  197. package/hmr/frameworks/vue/server/websocket-sfc.js +20 -0
  198. package/hmr/frameworks/vue/server/websocket-sfc.js.map +1 -0
  199. package/hmr/helpers/ast-normalizer.d.ts +3 -1
  200. package/hmr/helpers/ast-normalizer.js +77 -10
  201. package/hmr/helpers/ast-normalizer.js.map +1 -1
  202. package/hmr/helpers/cjs-named-exports.d.ts +23 -0
  203. package/hmr/helpers/cjs-named-exports.js +152 -0
  204. package/hmr/helpers/cjs-named-exports.js.map +1 -0
  205. package/hmr/helpers/package-exports.d.ts +16 -0
  206. package/hmr/helpers/package-exports.js +396 -0
  207. package/hmr/helpers/package-exports.js.map +1 -0
  208. package/hmr/server/constants.js +20 -5
  209. package/hmr/server/constants.js.map +1 -1
  210. package/hmr/server/core-sanitize.d.ts +90 -7
  211. package/hmr/server/core-sanitize.js +211 -56
  212. package/hmr/server/core-sanitize.js.map +1 -1
  213. package/hmr/server/device-transform-helpers.d.ts +24 -0
  214. package/hmr/server/device-transform-helpers.js +369 -0
  215. package/hmr/server/device-transform-helpers.js.map +1 -0
  216. package/hmr/server/framework-strategy.d.ts +108 -11
  217. package/hmr/server/hmr-module-graph.d.ts +37 -0
  218. package/hmr/server/hmr-module-graph.js +214 -0
  219. package/hmr/server/hmr-module-graph.js.map +1 -0
  220. package/hmr/server/import-map.d.ts +11 -2
  221. package/hmr/server/import-map.js +95 -44
  222. package/hmr/server/import-map.js.map +1 -1
  223. package/hmr/server/index.js +7 -16
  224. package/hmr/server/index.js.map +1 -1
  225. package/hmr/server/ns-core-cjs-shape.d.ts +204 -0
  226. package/hmr/server/ns-core-cjs-shape.js +271 -0
  227. package/hmr/server/ns-core-cjs-shape.js.map +1 -0
  228. package/hmr/server/ns-rt-bridge.d.ts +51 -0
  229. package/hmr/server/ns-rt-bridge.js +131 -0
  230. package/hmr/server/ns-rt-bridge.js.map +1 -0
  231. package/hmr/server/ns-rt-route.d.ts +5 -0
  232. package/hmr/server/ns-rt-route.js +38 -0
  233. package/hmr/server/ns-rt-route.js.map +1 -0
  234. package/hmr/server/perf-instrumentation.d.ts +114 -0
  235. package/hmr/server/perf-instrumentation.js +197 -0
  236. package/hmr/server/perf-instrumentation.js.map +1 -0
  237. package/hmr/server/process-code-for-device.d.ts +14 -0
  238. package/hmr/server/process-code-for-device.js +702 -0
  239. package/hmr/server/process-code-for-device.js.map +1 -0
  240. package/hmr/server/require-guard.d.ts +1 -0
  241. package/hmr/server/require-guard.js +12 -0
  242. package/hmr/server/require-guard.js.map +1 -0
  243. package/hmr/server/rewrite-imports.d.ts +2 -0
  244. package/hmr/server/rewrite-imports.js +600 -0
  245. package/hmr/server/rewrite-imports.js.map +1 -0
  246. package/hmr/server/route-helpers.d.ts +7 -0
  247. package/hmr/server/route-helpers.js +13 -0
  248. package/hmr/server/route-helpers.js.map +1 -0
  249. package/hmr/server/server-origin.d.ts +2 -0
  250. package/hmr/server/server-origin.js +83 -0
  251. package/hmr/server/server-origin.js.map +1 -0
  252. package/hmr/server/shared-transform-request.js +13 -7
  253. package/hmr/server/shared-transform-request.js.map +1 -1
  254. package/hmr/server/transform-cache-invalidation.d.ts +37 -0
  255. package/hmr/server/transform-cache-invalidation.js +156 -0
  256. package/hmr/server/transform-cache-invalidation.js.map +1 -0
  257. package/hmr/server/vendor-bare-module-shims.d.ts +4 -0
  258. package/hmr/server/vendor-bare-module-shims.js +80 -0
  259. package/hmr/server/vendor-bare-module-shims.js.map +1 -0
  260. package/hmr/server/vite-plugin.js +72 -42
  261. package/hmr/server/vite-plugin.js.map +1 -1
  262. package/hmr/server/websocket-core-bridge.d.ts +45 -6
  263. package/hmr/server/websocket-core-bridge.js +81 -77
  264. package/hmr/server/websocket-core-bridge.js.map +1 -1
  265. package/hmr/server/websocket-css-hot-update.d.ts +33 -0
  266. package/hmr/server/websocket-css-hot-update.js +65 -0
  267. package/hmr/server/websocket-css-hot-update.js.map +1 -0
  268. package/hmr/server/websocket-device-transform.d.ts +3 -0
  269. package/hmr/server/websocket-device-transform.js +7 -0
  270. package/hmr/server/websocket-device-transform.js.map +1 -0
  271. package/hmr/server/websocket-graph-upsert.d.ts +15 -0
  272. package/hmr/server/websocket-graph-upsert.js +20 -0
  273. package/hmr/server/websocket-graph-upsert.js.map +1 -1
  274. package/hmr/server/websocket-hmr-pending.d.ts +37 -0
  275. package/hmr/server/websocket-hmr-pending.js +55 -0
  276. package/hmr/server/websocket-hmr-pending.js.map +1 -0
  277. package/hmr/server/websocket-hot-update.d.ts +77 -0
  278. package/hmr/server/websocket-hot-update.js +360 -0
  279. package/hmr/server/websocket-hot-update.js.map +1 -0
  280. package/hmr/server/websocket-import-map-route.d.ts +15 -0
  281. package/hmr/server/websocket-import-map-route.js +50 -0
  282. package/hmr/server/websocket-import-map-route.js.map +1 -0
  283. package/hmr/server/websocket-module-bindings.js +3 -3
  284. package/hmr/server/websocket-module-bindings.js.map +1 -1
  285. package/hmr/server/websocket-module-specifiers.d.ts +66 -2
  286. package/hmr/server/websocket-module-specifiers.js +203 -20
  287. package/hmr/server/websocket-module-specifiers.js.map +1 -1
  288. package/hmr/server/websocket-ns-core.d.ts +21 -0
  289. package/hmr/server/websocket-ns-core.js +311 -0
  290. package/hmr/server/websocket-ns-core.js.map +1 -0
  291. package/hmr/server/websocket-ns-entry.d.ts +21 -0
  292. package/hmr/server/websocket-ns-entry.js +164 -0
  293. package/hmr/server/websocket-ns-entry.js.map +1 -0
  294. package/hmr/server/websocket-ns-m-paths.d.ts +1 -1
  295. package/hmr/server/websocket-ns-m-paths.js +58 -13
  296. package/hmr/server/websocket-ns-m-paths.js.map +1 -1
  297. package/hmr/server/websocket-ns-m-request.d.ts +11 -1
  298. package/hmr/server/websocket-ns-m-request.js +18 -25
  299. package/hmr/server/websocket-ns-m-request.js.map +1 -1
  300. package/hmr/server/websocket-ns-m.d.ts +33 -0
  301. package/hmr/server/websocket-ns-m.js +752 -0
  302. package/hmr/server/websocket-ns-m.js.map +1 -0
  303. package/hmr/server/websocket-served-module-helpers.d.ts +82 -0
  304. package/hmr/server/websocket-served-module-helpers.js +879 -0
  305. package/hmr/server/websocket-served-module-helpers.js.map +1 -0
  306. package/hmr/server/websocket-txn.js +2 -8
  307. package/hmr/server/websocket-txn.js.map +1 -1
  308. package/hmr/server/websocket-vendor-unifier.d.ts +0 -1
  309. package/hmr/server/websocket-vendor-unifier.js +4 -9
  310. package/hmr/server/websocket-vendor-unifier.js.map +1 -1
  311. package/hmr/server/websocket.d.ts +8 -39
  312. package/hmr/server/websocket.js +514 -4030
  313. package/hmr/server/websocket.js.map +1 -1
  314. package/hmr/shared/ns-globals.d.ts +118 -0
  315. package/hmr/shared/ns-globals.js +29 -0
  316. package/hmr/shared/ns-globals.js.map +1 -0
  317. package/hmr/shared/protocol.d.ts +145 -0
  318. package/hmr/shared/protocol.js +28 -0
  319. package/hmr/shared/protocol.js.map +1 -0
  320. package/hmr/shared/runtime/boot-placeholder-ui.d.ts +69 -0
  321. package/hmr/shared/runtime/boot-placeholder-ui.js +101 -0
  322. package/hmr/shared/runtime/boot-placeholder-ui.js.map +1 -0
  323. package/hmr/shared/runtime/boot-progress.d.ts +44 -0
  324. package/hmr/shared/runtime/boot-progress.js +133 -0
  325. package/hmr/shared/runtime/boot-progress.js.map +1 -0
  326. package/hmr/shared/runtime/boot-timeline.d.ts +18 -0
  327. package/hmr/shared/runtime/boot-timeline.js +42 -0
  328. package/hmr/shared/runtime/boot-timeline.js.map +1 -0
  329. package/hmr/shared/runtime/browser-runtime-contract.js.map +1 -1
  330. package/hmr/shared/runtime/dev-overlay-snapshots.d.ts +31 -0
  331. package/hmr/shared/runtime/dev-overlay-snapshots.js +324 -0
  332. package/hmr/shared/runtime/dev-overlay-snapshots.js.map +1 -0
  333. package/hmr/shared/runtime/dev-overlay.d.ts +75 -26
  334. package/hmr/shared/runtime/dev-overlay.js +992 -261
  335. package/hmr/shared/runtime/dev-overlay.js.map +1 -1
  336. package/hmr/shared/runtime/global-scope.d.ts +18 -0
  337. package/hmr/shared/runtime/global-scope.js +21 -0
  338. package/hmr/shared/runtime/global-scope.js.map +1 -0
  339. package/hmr/shared/runtime/hooks.js +2 -1
  340. package/hmr/shared/runtime/hooks.js.map +1 -1
  341. package/hmr/shared/runtime/http-only-boot.js +7 -6
  342. package/hmr/shared/runtime/http-only-boot.js.map +1 -1
  343. package/hmr/shared/runtime/module-provenance.js +4 -6
  344. package/hmr/shared/runtime/module-provenance.js.map +1 -1
  345. package/hmr/shared/runtime/root-placeholder-view.d.ts +19 -0
  346. package/hmr/shared/runtime/root-placeholder-view.js +311 -0
  347. package/hmr/shared/runtime/root-placeholder-view.js.map +1 -0
  348. package/hmr/shared/runtime/root-placeholder.js +371 -197
  349. package/hmr/shared/runtime/root-placeholder.js.map +1 -1
  350. package/hmr/shared/runtime/session-bootstrap.js +168 -4
  351. package/hmr/shared/runtime/session-bootstrap.js.map +1 -1
  352. package/hmr/shared/runtime/vendor-bootstrap.js +3 -10
  353. package/hmr/shared/runtime/vendor-bootstrap.js.map +1 -1
  354. package/hmr/shared/vendor/manifest-collect.d.ts +4 -0
  355. package/hmr/shared/vendor/manifest-collect.js +512 -0
  356. package/hmr/shared/vendor/manifest-collect.js.map +1 -0
  357. package/hmr/shared/vendor/manifest-loader.d.ts +2 -1
  358. package/hmr/shared/vendor/manifest-loader.js +5 -3
  359. package/hmr/shared/vendor/manifest-loader.js.map +1 -1
  360. package/hmr/shared/vendor/manifest.d.ts +1 -7
  361. package/hmr/shared/vendor/manifest.js +102 -741
  362. package/hmr/shared/vendor/manifest.js.map +1 -1
  363. package/hmr/shared/vendor/vendor-device-shim.d.ts +1 -0
  364. package/hmr/shared/vendor/vendor-device-shim.js +208 -0
  365. package/hmr/shared/vendor/vendor-device-shim.js.map +1 -0
  366. package/hmr/shared/vendor/vendor-esbuild-plugins.d.ts +16 -0
  367. package/hmr/shared/vendor/vendor-esbuild-plugins.js +203 -0
  368. package/hmr/shared/vendor/vendor-esbuild-plugins.js.map +1 -0
  369. package/hmr/vendor-bootstrap.d.ts +1 -3
  370. package/hmr/vendor-bootstrap.js +4 -6
  371. package/hmr/vendor-bootstrap.js.map +1 -1
  372. package/index.d.ts +1 -0
  373. package/index.js +5 -0
  374. package/index.js.map +1 -1
  375. package/package.json +55 -11
  376. package/runtime/core-aliases-early.js +25 -55
  377. package/runtime/core-aliases-early.js.map +1 -1
  378. package/helpers/angular/angular-linker.d.ts +0 -13
  379. package/helpers/angular/angular-linker.js +0 -194
  380. package/helpers/angular/angular-linker.js.map +0 -1
  381. package/helpers/angular/inline-decorator-component-templates.js.map +0 -1
  382. package/helpers/angular/shared-linker.d.ts +0 -11
  383. package/helpers/angular/shared-linker.js +0 -75
  384. package/helpers/angular/shared-linker.js.map +0 -1
  385. package/helpers/angular/synthesize-decorator-ctor-parameters.js.map +0 -1
  386. package/helpers/angular/synthesize-injectable-factories.js.map +0 -1
  387. package/helpers/angular/util.js +0 -67
  388. package/helpers/angular/util.js.map +0 -1
  389. package/helpers/prelink-angular.d.ts +0 -2
  390. package/helpers/prelink-angular.js +0 -117
  391. package/helpers/prelink-angular.js.map +0 -1
  392. package/hmr/server/websocket-angular-entry.js.map +0 -1
  393. package/hmr/server/websocket-angular-hot-update.js +0 -239
  394. package/hmr/server/websocket-angular-hot-update.js.map +0 -1
  395. package/hmr/server/websocket-ns-m-finalize.d.ts +0 -32
  396. package/hmr/server/websocket-ns-m-finalize.js +0 -73
  397. package/hmr/server/websocket-ns-m-finalize.js.map +0 -1
  398. package/hmr/server/websocket-runtime-compat.d.ts +0 -19
  399. package/hmr/server/websocket-runtime-compat.js +0 -286
  400. package/hmr/server/websocket-runtime-compat.js.map +0 -1
  401. package/hmr/server/websocket-vue-sfc.d.ts +0 -35
  402. package/hmr/server/websocket-vue-sfc.js +0 -1116
  403. package/hmr/server/websocket-vue-sfc.js.map +0 -1
  404. package/transformers/NativeClass/index.d.ts +0 -2
  405. package/transformers/NativeClass/index.js +0 -222
  406. package/transformers/NativeClass/index.js.map +0 -1
  407. /package/{helpers/angular → hmr/frameworks/angular/build}/inline-decorator-component-templates.d.ts +0 -0
  408. /package/{helpers/angular → hmr/frameworks/angular/build}/synthesize-decorator-ctor-parameters.d.ts +0 -0
  409. /package/{helpers/angular → hmr/frameworks/angular/build}/synthesize-injectable-factories.d.ts +0 -0
  410. /package/hmr/{server → frameworks/angular/server}/websocket-angular-entry.d.ts +0 -0
@@ -1,3 +1,81 @@
1
+ import { getGlobalScope } from '../../../shared/runtime/global-scope.js';
2
+ // Opt-out flag for the apply-progress overlay (default: enabled).
3
+ // Driven by `NS_VITE_PROGRESS_OVERLAY=0` (or `false`/`off`/`no`) on the
4
+ // dev server; baked into the bundle via
5
+ // `__NS_HMR_PROGRESS_OVERLAY_ENABLED__` at build time. We collapse the
6
+ // build-time constant into a runtime boolean once so each call-site is
7
+ // a single property check rather than a try/typeof. Tests that re-run
8
+ // the angular client (via vitest) see `undefined` and default to
9
+ // enabled — matching the production dev-server experience.
10
+ const overlayEnabled = (() => {
11
+ // Define substitution does not reach this raw-served file — fall back to
12
+ // the globalThis seed planted by the entry's defines-seed module (which
13
+ // evaluates before this client is loaded) before defaulting to enabled.
14
+ try {
15
+ if (typeof __NS_HMR_PROGRESS_OVERLAY_ENABLED__ === 'boolean')
16
+ return __NS_HMR_PROGRESS_OVERLAY_ENABLED__;
17
+ }
18
+ catch { }
19
+ try {
20
+ const seeded = getGlobalScope().__NS_HMR_PROGRESS_OVERLAY_ENABLED__;
21
+ if (typeof seeded === 'boolean')
22
+ return seeded;
23
+ }
24
+ catch { }
25
+ return true;
26
+ })();
27
+ function getHmrOverlayApi() {
28
+ if (!overlayEnabled)
29
+ return null;
30
+ try {
31
+ return globalThis.__NS_HMR_DEV_OVERLAY__ || null;
32
+ }
33
+ catch { }
34
+ return null;
35
+ }
36
+ function setUpdateOverlayStage(stage, info) {
37
+ if (!overlayEnabled)
38
+ return;
39
+ try {
40
+ const api = getHmrOverlayApi();
41
+ if (api && typeof api.setUpdateStage === 'function') {
42
+ api.setUpdateStage(stage, info);
43
+ }
44
+ }
45
+ catch { }
46
+ }
47
+ function hideUpdateOverlay() {
48
+ if (!overlayEnabled)
49
+ return;
50
+ try {
51
+ const api = getHmrOverlayApi();
52
+ if (api && typeof api.hide === 'function') {
53
+ api.hide('hmr-applied');
54
+ }
55
+ }
56
+ catch { }
57
+ }
58
+ // Safe accessor for the build-time `__NS_ENV_VERBOSE__` constant.
59
+ //
60
+ // Vite replaces `__NS_ENV_VERBOSE__` at build time via `define`. In
61
+ // unit tests (vitest, plain ts-node, etc.) the identifier is
62
+ // undeclared at runtime, so a bare reference throws ReferenceError —
63
+ // `typeof` is the only safe way to probe it. We collapse the
64
+ // build-time constant into a runtime boolean once at module load and
65
+ // then short-circuit on `verbose` arguments at every call-site.
66
+ //
67
+ // Always order `verbose && envVerbose` so the runtime flag
68
+ // short-circuits the build-time constant; that prevents reference
69
+ // errors in test environments where the `define` substitution does
70
+ // not run.
71
+ const envVerbose = (() => {
72
+ try {
73
+ return typeof __NS_ENV_VERBOSE__ === 'boolean' ? __NS_ENV_VERBOSE__ : false;
74
+ }
75
+ catch {
76
+ return false;
77
+ }
78
+ })();
1
79
  export function installAngularHmrClientHooks() {
2
80
  const g = globalThis;
3
81
  if (g.__NS_ANGULAR_HMR_CLIENT_INSTALLED__) {
@@ -5,16 +83,455 @@ export function installAngularHmrClientHooks() {
5
83
  }
6
84
  try {
7
85
  g.__NS_ANGULAR_HMR_CLIENT_INSTALLED__ = true;
8
- if (__NS_ENV_VERBOSE__) {
86
+ if (envVerbose) {
9
87
  console.log('[hmr-angular] client hooks installed');
10
88
  }
11
89
  }
12
90
  catch { }
13
91
  }
92
+ // HMR cycle serialization mutex.
93
+ //
94
+ // A back-to-back save (e.g., the user holds Cmd+S, or `runOnSave`
95
+ // saves a file twice during one tick) could overlap two HMR cycles:
96
+ // the first cycle's `import(entry)` would still be resolving when the
97
+ // second cycle's `__nsInvalidateModules` ran, leaving the registry in
98
+ // a half-evicted state and producing flaky "module already evaluated"
99
+ // errors.
100
+ //
101
+ // Each `handleAngularHotUpdateMessage` call publishes a promise to
102
+ // `inFlightHmrCycle` and awaits the previous publication before
103
+ // starting its own evict + import. The previous-cycle promise resolves
104
+ // regardless of success or failure (we always run `resolveCycle()` in
105
+ // `finally`), so a stuck cycle can't deadlock the queue. The mutex is
106
+ // intentionally process-wide and resets naturally on cold-boot or
107
+ // websocket reconnect (both blow away the JS realm).
108
+ let inFlightHmrCycle = null;
109
+ // Explicit module eviction.
110
+ //
111
+ // `__nsInvalidateModules` is a runtime-installed global that takes an
112
+ // array of canonical /ns/m/<rel> URLs and removes each one from V8's
113
+ // module registry (`g_moduleRegistry`). The runtime canonicalizer
114
+ // strips legacy `__ns_hmr__/<tag>/` and `__ns_boot__/b1/` segments
115
+ // before lookup, so the same URL evicts every cache entry historically
116
+ // created for that module — even if a stale tagged URL is still around.
117
+ //
118
+ // Soft-fails on older runtimes that don't expose the function. In that
119
+ // case we fall through to the legacy URL-versioning path and emit a
120
+ // one-time warning so the user knows the eviction protocol is
121
+ // unavailable.
122
+ // Dispatch one of Vite's standard HMR lifecycle events through the iOS
123
+ // runtime's `__NS_DISPATCH_HOT_EVENT__` global. The runtime owns the
124
+ // listener registry that `import.meta.hot.on(event, cb)` populates; this
125
+ // function is the producer side that lets the Angular HMR client emit the
126
+ // canonical Vite events (`vite:beforeUpdate`, `vite:afterUpdate`,
127
+ // `vite:beforeFullReload`, `vite:invalidate`, `vite:error`) at the right
128
+ // moments. User code that does
129
+ // `import.meta.hot.on('vite:beforeUpdate', cb)` only fires when this is
130
+ // called.
131
+ //
132
+ // Soft-fails on older runtimes that don't expose the dispatcher (the
133
+ // global is undefined; we no-op). Per-listener failures are swallowed
134
+ // inside the runtime, so a single bad listener can't break the HMR cycle.
135
+ function dispatchHotEvent(g, event, payload) {
136
+ try {
137
+ const fn = g && g.__NS_DISPATCH_HOT_EVENT__;
138
+ if (typeof fn === 'function') {
139
+ fn.call(g, event, payload);
140
+ }
141
+ }
142
+ catch (err) {
143
+ // Defensive: if the dispatcher itself throws, we'd already have
144
+ // no listeners (per-callback failures swallowed inside the
145
+ // runtime), so this is purely a safety net for runtime
146
+ // regressions. Log only under verbose to avoid noisy dev logs.
147
+ if (envVerbose) {
148
+ console.warn(`[ns-hmr][client] __NS_DISPATCH_HOT_EVENT__('${event}') threw:`, err?.message ?? err);
149
+ }
150
+ }
151
+ }
152
+ // Trigger a full app reload via the runtime's `__nsReloadDevApp` global,
153
+ // dispatching `vite:beforeFullReload` first so user-code listeners can
154
+ // react. Returns `true` if the reload was triggered, `false` if the
155
+ // runtime doesn't expose the API. Used both by `import.meta.hot.invalidate()`
156
+ // (handled directly inside the runtime) AND by the declined-module check
157
+ // below (where the JS layer makes the decision and asks for the reload).
158
+ function triggerFullReload(g, message) {
159
+ const fn = g && g.__nsReloadDevApp;
160
+ if (typeof fn !== 'function') {
161
+ return false;
162
+ }
163
+ dispatchHotEvent(g, 'vite:beforeFullReload', { message });
164
+ try {
165
+ // Fire-and-forget — `__nsReloadDevApp` returns a Promise, but we
166
+ // don't await it here because the JS realm is about to be torn
167
+ // down. The HMR cycle's caller treats this as terminal and
168
+ // short-circuits the rest of the cycle.
169
+ fn.call(g);
170
+ }
171
+ catch (err) {
172
+ if (envVerbose) {
173
+ console.warn('[ns-hmr][client] __nsReloadDevApp threw:', err?.message ?? err);
174
+ }
175
+ return false;
176
+ }
177
+ return true;
178
+ }
179
+ // Check if any module being touched by this HMR update has called
180
+ // `import.meta.hot.decline()`. If so, per Vite spec we MUST do a full
181
+ // reload instead of a hot-update — declining means "I refuse to be
182
+ // hot-updated". Returns `true` if a full reload was triggered (caller
183
+ // should short-circuit the rest of the cycle).
184
+ //
185
+ // Older runtimes without `__nsHasDeclinedModule` skip the check entirely
186
+ // (legacy behaviour: HMR proceeds with the reboot, which is the same
187
+ // behaviour they had before this fix).
188
+ function checkDeclinedAndReload(g, msg, options) {
189
+ const checker = g && g.__nsHasDeclinedModule;
190
+ if (typeof checker !== 'function')
191
+ return false;
192
+ // `evictPaths` is the eviction set the dev server computed for this
193
+ // HMR cycle — every module that needs to be re-evaluated. If ANY of
194
+ // those modules declined HMR, the whole cycle has to convert to a
195
+ // full reload. Pass them through verbatim; the runtime canonicalizes
196
+ // internally when matching against `g_hotDeclined`.
197
+ const evictPaths = Array.isArray(msg?.evictPaths) ? msg.evictPaths : [];
198
+ let declined = false;
199
+ try {
200
+ declined = !!checker.call(g, evictPaths);
201
+ }
202
+ catch (err) {
203
+ if (options.verbose && envVerbose) {
204
+ console.warn('[ns-hmr][client] __nsHasDeclinedModule threw:', err?.message ?? err);
205
+ }
206
+ return false;
207
+ }
208
+ if (!declined)
209
+ return false;
210
+ const filePath = typeof msg?.path === 'string' ? msg.path : '<unknown>';
211
+ const message = `module declined HMR (file=${filePath}, evictPaths=${evictPaths.length})`;
212
+ console.info(`[ns-hmr][decline] ${message} — falling back to full reload`);
213
+ const reloaded = triggerFullReload(g, message);
214
+ if (!reloaded && options.verbose && envVerbose) {
215
+ console.warn('[ns-hmr][client] declined module detected but __nsReloadDevApp unavailable; HMR will proceed (older runtime)');
216
+ }
217
+ return reloaded;
218
+ }
219
+ // Drain `import.meta.hot.dispose(cb)` callbacks via the iOS runtime's
220
+ // `globalThis.__nsRunHmrDispose()` global before Angular's reboot.
221
+ //
222
+ // The runtime owns the dispose registry (`g_hotDispose` in
223
+ // `HMRSupport.mm`) — every call to `import.meta.hot.dispose(cb)` from
224
+ // user code lands there. This client just asks the runtime to drain.
225
+ // We pass NO key argument so the runtime drains every registered
226
+ // callback across every module, which matches the wholesale-reboot
227
+ // semantics of `__reboot_ng_modules__`: when Angular tears down its
228
+ // component tree and re-bootstraps, every module's accumulated side
229
+ // effects are conceptually being thrown away. (A future per-module
230
+ // HMR client could pass an explicit key list to limit the drain.)
231
+ //
232
+ // Older runtimes (before this hook shipped) silently no-op via the
233
+ // optional chain; in that case, dispose callbacks register but never
234
+ // fire, and the worker-specific safety net in `terminateTrackedWorkers`
235
+ // below still catches the worker pile-up case.
236
+ function runHmrDisposeCallbacks(g, options) {
237
+ let runner;
238
+ try {
239
+ const candidate = g && g.__nsRunHmrDispose;
240
+ if (typeof candidate === 'function') {
241
+ runner = candidate;
242
+ }
243
+ }
244
+ catch {
245
+ // `globalThis` access defensiveness — Proxy-wrapped globals can
246
+ // throw on property reads.
247
+ }
248
+ if (!runner) {
249
+ // Older runtime: the dispose callback registry exists in the
250
+ // native runtime but no JS-callable drain entrypoint is
251
+ // available. Silently no-op (worker terminator below still
252
+ // runs). The diagnostic message is gated to verbose so we
253
+ // don't nag users on every cycle of every project that hasn't
254
+ // adopted the new runtime yet.
255
+ if (options.verbose && envVerbose) {
256
+ console.info('[ns-hmr][client] runtime missing __nsRunHmrDispose; skipping import.meta.hot.dispose drain (upgrade @nativescript/ios for support)');
257
+ }
258
+ return;
259
+ }
260
+ let executed = null;
261
+ try {
262
+ const result = runner.call(g);
263
+ executed = typeof result === 'number' ? result : 0;
264
+ }
265
+ catch (err) {
266
+ // One bad disposer should already be swallowed inside the
267
+ // runtime per-callback try/catch. If the runtime itself throws
268
+ // (out-of-memory, isolate teardown race, etc.) we MUST NOT
269
+ // take down the HMR cycle.
270
+ if (options.verbose && envVerbose) {
271
+ console.warn('[ns-hmr][client] __nsRunHmrDispose threw:', err?.message ?? err);
272
+ }
273
+ return;
274
+ }
275
+ // Surface ONE concise log per HMR cycle that actually had disposers
276
+ // to run. Quiet on cycles where no module had registered any.
277
+ if (executed && executed > 0) {
278
+ console.info(`[ns-hmr][dispose] ran ${executed} import.meta.hot.dispose callback${executed === 1 ? '' : 's'} before reboot`);
279
+ }
280
+ }
281
+ // Terminate every live worker before Angular's reboot. Two-tier strategy:
282
+ //
283
+ // 1. PREFERRED — `globalThis.__nsTerminateAllWorkers()` (NS iOS runtime
284
+ // ≥ the version that ships `Worker::TerminateAllWorkersCallback`).
285
+ // Native code iterates `Caches::Workers` (the runtime's authoritative
286
+ // worker registry) and calls `WorkerWrapper::Terminate()` on each
287
+ // live entry. Universal: catches every worker regardless of how it
288
+ // was created — `new Worker(new URL(...))`, raw `Worker('~/x.js')`,
289
+ // dynamic-import-spawned workers, plugin-spawned workers, etc.
290
+ //
291
+ // 2. FALLBACK — drain `globalThis.__NS_HMR_WORKERS__` (a Set populated
292
+ // by `__nsHmrTrackWorker`, the helper that `workerHmrUrlPlugin`
293
+ // injects at the top of every dev module containing a
294
+ // `new Worker(new URL(...))` call). This keeps HMR worker cleanup
295
+ // working on older runtimes that don't yet expose
296
+ // `__nsTerminateAllWorkers`. It only catches workers spawned via
297
+ // the Vite-rewritten path, so the runtime API is strictly better
298
+ // coverage.
299
+ //
300
+ // Either way: producer-side wraps are still useful as diagnostic
301
+ // metadata (which user code spawned which worker), so we always clear
302
+ // the JS Set after termination — irrespective of which tier ran — so
303
+ // it doesn't grow unbounded across HMR cycles.
304
+ //
305
+ // Tolerant of:
306
+ // * Missing globals — non-worker apps have neither global; we no-op.
307
+ // * `terminate()` throwing — the runtime can throw on already-dead
308
+ // workers; we catch per-entry so subsequent terminations and the
309
+ // reboot itself still proceed.
310
+ // * Native API throwing — wrapped in try/catch so a runtime regression
311
+ // can't take down the HMR cycle; we degrade to the JS-Set fallback.
312
+ function terminateTrackedWorkers(g, options) {
313
+ let nativeApi;
314
+ try {
315
+ const candidate = g && g.__nsTerminateAllWorkers;
316
+ if (typeof candidate === 'function') {
317
+ nativeApi = candidate;
318
+ }
319
+ }
320
+ catch {
321
+ // Reading the global threw (extremely defensive — `globalThis`
322
+ // access shouldn't, but Proxy-wrapped globals can).
323
+ }
324
+ let nativeTerminated = null;
325
+ if (nativeApi) {
326
+ try {
327
+ const result = nativeApi.call(g);
328
+ // Native API returns the count of workers terminated as a
329
+ // number. Older betas may return undefined; treat any
330
+ // non-number as "ran successfully, count unknown".
331
+ nativeTerminated = typeof result === 'number' ? result : 0;
332
+ }
333
+ catch (err) {
334
+ if (options.verbose && envVerbose) {
335
+ console.warn('[ns-hmr][client] __nsTerminateAllWorkers threw; falling back to JS-tracked Set:', err?.message ?? err);
336
+ }
337
+ nativeApi = undefined; // force fallback below
338
+ }
339
+ }
340
+ // Always touch the JS-tracked Set so producer-side bookkeeping
341
+ // doesn't outlive its workers across HMR cycles. If the native API
342
+ // already terminated everything, this is just a `.clear()`. If the
343
+ // native API isn't available (older runtime), this is the actual
344
+ // cleanup path.
345
+ let workers;
346
+ try {
347
+ workers = g && g.__NS_HMR_WORKERS__;
348
+ }
349
+ catch {
350
+ // fall through with `workers === undefined`
351
+ }
352
+ let fallbackTerminated = 0;
353
+ let fallbackFailed = 0;
354
+ const fallbackTotal = workers && typeof workers.size === 'number' ? workers.size : 0;
355
+ if (!nativeApi && workers && fallbackTotal > 0) {
356
+ // No native API — drain the JS-tracked Set as the primary path.
357
+ for (const worker of workers) {
358
+ try {
359
+ if (worker && typeof worker.terminate === 'function') {
360
+ worker.terminate();
361
+ fallbackTerminated++;
362
+ }
363
+ }
364
+ catch {
365
+ fallbackFailed++;
366
+ }
367
+ }
368
+ }
369
+ if (workers) {
370
+ try {
371
+ workers.clear();
372
+ }
373
+ catch { }
374
+ }
375
+ // Surface ONE concise log per HMR cycle when there was actually work
376
+ // to do. This isn't gated on `verbose` so the user can see in the
377
+ // dev terminal which cleanup tier ran without flipping a flag —
378
+ // it's a single line per HMR-cycle-with-workers, never per cycle
379
+ // when no workers exist. Filter on "actually did something" so we
380
+ // stay quiet on no-worker apps and on cycles where neither tier
381
+ // found anything.
382
+ if (nativeApi && (nativeTerminated ?? 0) > 0) {
383
+ console.info(`[ns-hmr][workers] terminated ${nativeTerminated} via runtime __nsTerminateAllWorkers before reboot`);
384
+ }
385
+ else if (!nativeApi && fallbackTerminated > 0) {
386
+ console.info(`[ns-hmr][workers] terminated ${fallbackTerminated}/${fallbackTotal} via JS-tracked Set fallback before reboot (older runtime; consider upgrading @nativescript/ios for native API)`);
387
+ }
388
+ }
389
+ function invalidateModules(urls, verbose) {
390
+ if (!urls || !urls.length)
391
+ return false;
392
+ const g = globalThis;
393
+ const fn = g.__nsInvalidateModules;
394
+ if (typeof fn !== 'function') {
395
+ console.warn(`[ns-hmr][client] runtime missing __nsInvalidateModules; falling back to legacy URL versioning. evict=${urls.length}`);
396
+ return false;
397
+ }
398
+ try {
399
+ if (verbose && envVerbose) {
400
+ console.info(`[ns-hmr][client] invalidateModules calling __nsInvalidateModules urls=${urls.length}`);
401
+ }
402
+ fn.call(null, urls);
403
+ if (verbose && envVerbose) {
404
+ console.info(`[ns-hmr][client] invalidateModules OK urls=${urls.length}`);
405
+ }
406
+ return true;
407
+ }
408
+ catch (error) {
409
+ // Real exception path — the runtime hook itself threw. Always
410
+ // surfaced for the same reason as the missing-hook warn above.
411
+ console.warn('[ns-hmr][client] __nsInvalidateModules threw', error?.message || error);
412
+ return false;
413
+ }
414
+ }
415
+ // Eviction-set size cap for the kickstart.
416
+ //
417
+ // `__NS_HMR_KICKSTART_MAX_URLS__` is a build-time literal injected
418
+ // by `helpers/global-defines.ts` (default 32). It is a pure
419
+ // performance gate: the kickstart never affects correctness, so
420
+ // skipping it for large fan-out edits simply reverts the runtime to
421
+ // sequential `HttpFetchText`. See the doc comment on
422
+ // `resolveHmrKickstartMaxUrls` (in global-defines.ts) for the
423
+ // rationale and the empirical numbers behind the default.
424
+ //
425
+ // The constant is read once at module load (the same pattern used
426
+ // for `overlayEnabled`) so each call site is a single inequality
427
+ // check rather than a try/typeof. Tests that re-import this module
428
+ // see `undefined` and fall back to the default.
429
+ const KICKSTART_DEFAULT_MAX_URLS = 32;
430
+ const kickstartMaxUrls = (() => {
431
+ // Define substitution does not reach this raw-served file — fall back to
432
+ // the globalThis seed planted by the entry's defines-seed module.
433
+ const normalize = (raw) => {
434
+ if (typeof raw !== 'number')
435
+ return null;
436
+ if (!Number.isFinite(raw))
437
+ return Number.POSITIVE_INFINITY;
438
+ if (raw < 0)
439
+ return null;
440
+ return Math.floor(raw);
441
+ };
442
+ try {
443
+ const substituted = normalize(__NS_HMR_KICKSTART_MAX_URLS__);
444
+ if (substituted !== null)
445
+ return substituted;
446
+ }
447
+ catch { }
448
+ try {
449
+ const seeded = normalize(getGlobalScope().__NS_HMR_KICKSTART_MAX_URLS__);
450
+ if (seeded !== null)
451
+ return seeded;
452
+ }
453
+ catch { }
454
+ return KICKSTART_DEFAULT_MAX_URLS;
455
+ })();
456
+ // Decide whether to run the kickstart for a given eviction set.
457
+ // Exported for unit testing — callers in this module use the inline
458
+ // `evictPaths.length` comparison below for a slightly cheaper
459
+ // hot-path.
460
+ //
461
+ // Semantics:
462
+ // - `0` urls → no work to do; skip (returns false).
463
+ // - `urls.length <= cap` → kickstart eligible (returns true).
464
+ // - `urls.length > cap` → skip kickstart (returns false). V8 falls
465
+ // back to per-module synchronous fetches inside its module walk.
466
+ // - `cap === 0` → kickstart disabled regardless of size.
467
+ // - `cap === Infinity` → kickstart always eligible (no cap).
468
+ export function shouldRunKickstart(urlCount, maxUrls = kickstartMaxUrls) {
469
+ if (!Number.isFinite(urlCount) || urlCount <= 0)
470
+ return false;
471
+ if (!Number.isFinite(maxUrls))
472
+ return maxUrls > 0;
473
+ if (maxUrls <= 0)
474
+ return false;
475
+ return urlCount <= maxUrls;
476
+ }
477
+ function kickstartHmrPrefetch(urls, verbose) {
478
+ if (!urls || !urls.length)
479
+ return null;
480
+ const g = globalThis;
481
+ const fn = g.__nsKickstartHmrPrefetch;
482
+ if (typeof fn !== 'function') {
483
+ if (verbose && envVerbose) {
484
+ console.info(`[hmr-angular] runtime missing __nsKickstartHmrPrefetch; serial fetches will be used. urls=${urls.length}`);
485
+ }
486
+ return null;
487
+ }
488
+ try {
489
+ // Concurrency cap of 16 matches the runtime's documented
490
+ // default for the kickstart BFS. Timeout at 10s tracks the
491
+ // runtime's `HttpFetchText` retry envelope so we don't
492
+ // hold the JS thread forever on a stalled dev server.
493
+ const result = fn.call(null, urls.slice(), { maxConcurrent: 16, timeoutMs: 10000 });
494
+ if (result && typeof result === 'object') {
495
+ const ok = !!result.ok;
496
+ const fetched = typeof result.fetched === 'number' ? result.fetched : 0;
497
+ const ms = typeof result.ms === 'number' ? result.ms : 0;
498
+ return { ok, fetched, ms };
499
+ }
500
+ return null;
501
+ }
502
+ catch (error) {
503
+ if (verbose && envVerbose) {
504
+ console.warn('[hmr-angular] __nsKickstartHmrPrefetch threw', error?.message || error);
505
+ }
506
+ return null;
507
+ }
508
+ }
14
509
  function getAngularBootstrapEntryCandidates(msg) {
15
- const root = typeof __NS_APP_ROOT_VIRTUAL__ === 'string' && __NS_APP_ROOT_VIRTUAL__ ? __NS_APP_ROOT_VIRTUAL__ : '/src';
16
- const rawCandidates = Array.isArray(msg?.entryCandidates) && msg.entryCandidates.length ? msg.entryCandidates : [`${root}/main.ts`, `${root}/app.ts`];
17
- return rawCandidates.filter((entry) => typeof entry === 'string' && entry.startsWith('/'));
510
+ // Define substitution does not reach this raw-served file; prefer the
511
+ // globalThis seed from the entry's defines-seed module ('app/'-rooted
512
+ // projects would otherwise get the wrong '/src' default).
513
+ const root = (typeof __NS_APP_ROOT_VIRTUAL__ === 'string' && __NS_APP_ROOT_VIRTUAL__) || (typeof getGlobalScope().__NS_APP_ROOT_VIRTUAL__ === 'string' && getGlobalScope().__NS_APP_ROOT_VIRTUAL__) || '/src';
514
+ // Server announces the canonical bootstrap entry as
515
+ // `importerEntry`, computed from `package.json#main`. Fall back to
516
+ // the legacy `entryCandidates` array (for older servers) and finally
517
+ // to a hard-coded list. All candidates must be project-relative
518
+ // posix paths (e.g. `/src/main.ts`) so they slot directly behind
519
+ // `${origin}/ns/m`.
520
+ const explicit = typeof msg?.importerEntry === 'string' && msg.importerEntry.startsWith('/') ? [msg.importerEntry] : [];
521
+ const legacy = Array.isArray(msg?.entryCandidates) && msg.entryCandidates.length ? msg.entryCandidates : [];
522
+ const fallback = [`${root}/main.ts`, `${root}/app.ts`];
523
+ const merged = [...explicit, ...legacy, ...fallback];
524
+ const seen = new Set();
525
+ const result = [];
526
+ for (const candidate of merged) {
527
+ if (typeof candidate !== 'string' || !candidate.startsWith('/'))
528
+ continue;
529
+ if (seen.has(candidate))
530
+ continue;
531
+ seen.add(candidate);
532
+ result.push(candidate);
533
+ }
534
+ return result;
18
535
  }
19
536
  async function importAngularBootstrapEntry(url) {
20
537
  const g = globalThis;
@@ -28,18 +545,113 @@ export async function refreshAngularBootstrapOptions(msg, options) {
28
545
  if (typeof g.__NS_UPDATE_ANGULAR_APP_OPTIONS__ !== 'function') {
29
546
  return;
30
547
  }
31
- const version = typeof msg?.version === 'number' ? msg.version : 0;
32
- if (!version) {
33
- return;
34
- }
35
548
  const originSource = typeof msg?.origin === 'string' && /^https?:\/\//.test(msg.origin) ? msg.origin : g.__NS_HTTP_ORIGIN__;
36
549
  if (typeof originSource !== 'string' || !/^https?:\/\//.test(originSource)) {
37
550
  return;
38
551
  }
39
552
  const origin = originSource.replace(/\/$/, '');
553
+ // Explicit eviction set takes precedence over URL versioning. The
554
+ // server walks the inverse-dep closure of the changed file
555
+ // (collectAngularEvictionUrls) and emits canonical /ns/m/<rel> URLs
556
+ // in `evictPaths`. We hand the list to the runtime before
557
+ // re-importing the entry; the runtime drops those entries from
558
+ // `g_moduleRegistry` so V8's subsequent `import(entry)` walks the
559
+ // dep graph and re-fetches ONLY those modules. Everything else stays
560
+ // hot in the cache.
561
+ const evictPaths = Array.isArray(msg?.evictPaths) ? msg.evictPaths.filter((u) => typeof u === 'string' && /^https?:\/\//.test(u)) : [];
562
+ // Diagnostic: log eviction set client-side so we can verify what
563
+ // the runtime is being asked to drop. Up to 32 entries are sampled
564
+ // so large constants edits don't swamp the console. Gated behind
565
+ // the standard `__NS_ENV_VERBOSE__` build define (`NS_VITE_VERBOSE`)
566
+ // so the lines stay silent on normal saves.
567
+ if (options.verbose && envVerbose) {
568
+ const _path = typeof msg?.path === 'string' ? msg.path : '(unknown)';
569
+ console.info(`[ns-hmr][client] received ns:angular-update path=${_path} evictPaths.length=${evictPaths.length} importerEntry=${msg?.importerEntry ?? '(none)'}`);
570
+ if (evictPaths.length) {
571
+ const sample = evictPaths.slice(0, 32);
572
+ console.info(`[ns-hmr][client] evictPaths firstN=`, sample);
573
+ if (evictPaths.length > sample.length) {
574
+ console.info(`[ns-hmr][client] evictPaths hidden=${evictPaths.length - sample.length}`);
575
+ }
576
+ }
577
+ }
578
+ // Drive the apply-progress overlay through the 'evicting' frame
579
+ // *before* the runtime invalidate call so the user sees the count
580
+ // even on the cheapest stage. Eviction count also doubles as a
581
+ // useful debug breadcrumb in the overlay's detail line — large
582
+ // counts (e.g. constants edits → 100+ importers) explain why a
583
+ // cycle takes longer than an HTML edit.
584
+ setUpdateOverlayStage('evicting', {
585
+ detail: evictPaths.length ? `Invalidating ${evictPaths.length} module${evictPaths.length === 1 ? '' : 's'}` : 'Invalidating module cache',
586
+ });
587
+ const evicted = invalidateModules(evictPaths, options.verbose);
588
+ if (options.verbose && envVerbose) {
589
+ console.info(`[ns-hmr][client] evict count=${evictPaths.length} ok=${evicted ? 'yes' : 'no'}`);
590
+ }
591
+ // Parallel HTTP prefetch for the freshly-evicted modules.
592
+ //
593
+ // Order matters here: the kickstart MUST run after the eviction so
594
+ // that it actually re-populates `g_prefetchCache` for the modules
595
+ // V8 will ask for. If we kickstarted before the eviction, every URL
596
+ // would skip on the "already cached" check (the walk consumes
597
+ // destructively but the prior non-evicted modules sit in V8's
598
+ // `g_moduleRegistry`, not the prefetch cache).
599
+ //
600
+ // We only kickstart when the eviction succeeded, because the
601
+ // fallback path (legacy URL-versioning, see `useStableUrls = evicted`
602
+ // below) doesn't go through canonical /ns/m URLs and the runtime
603
+ // would not match its prefetch cache lookups against the version-
604
+ // prefixed forms V8 will end up requesting. Better to skip the
605
+ // optimization than risk a confusing partial cache hit.
606
+ //
607
+ // `shouldRunKickstart` gates large eviction sets out of the
608
+ // parallel wave. A `.ts` file with deep inverse-dep fan-in
609
+ // (constants files, design-system enums) can produce a
610
+ // 200–300-URL closure that overwhelms Vite's single-threaded
611
+ // transform pipeline. The kickstart fan-out then makes the cycle
612
+ // 5–8× slower than letting V8 fetch sequentially as it walks the
613
+ // real forward path. The cap (default 32, override with
614
+ // `NS_VITE_KICKSTART_MAX_URLS`) keeps component-shaped closures
615
+ // (typically 5–30 URLs) on the fast path. Correctness is
616
+ // unaffected — V8's per-module synchronous fetch is the same fall-
617
+ // back code path the runtime has always used.
618
+ if (evicted && evictPaths.length > 0) {
619
+ if (shouldRunKickstart(evictPaths.length)) {
620
+ const result = kickstartHmrPrefetch(evictPaths, options.verbose);
621
+ if (options.verbose && envVerbose && result) {
622
+ console.info(`[ns-hmr][angular] kickstart urls=${evictPaths.length} fetched=${result.fetched} ok=${result.ok ? 'yes' : 'no'} ms=${result.ms}`);
623
+ }
624
+ }
625
+ else if (options.verbose && envVerbose) {
626
+ console.info(`[ns-hmr][angular] kickstart skipped urls=${evictPaths.length} cap=${Number.isFinite(kickstartMaxUrls) ? kickstartMaxUrls : 'Infinity'} (eviction set too large; falling back to sequential fetch)`);
627
+ }
628
+ }
629
+ // URL strategy:
630
+ //
631
+ // * `evicted=true` → modern runtime accepted the eviction; we
632
+ // re-import the entry under its STABLE canonical URL. V8's
633
+ // registry no longer holds the evicted modules so the import
634
+ // triggers fresh fetches for them; the rest of the graph is a
635
+ // cache hit.
636
+ // * `evicted=false` → either we have no eviction set (older server)
637
+ // or the runtime lacks `__nsInvalidateModules` (older runtime).
638
+ // Fall back to the legacy `__ns_hmr__/v<version>/` URL pattern so
639
+ // V8 sees a fresh URL and re-fetches the entry. The runtime
640
+ // canonicalizer collapses the path back to the stable key, which
641
+ // keeps cache identity consistent across saves.
642
+ const versionRaw = typeof msg?.version === 'number' ? msg.version : 0;
643
+ const useStableUrls = evicted;
40
644
  let lastError;
645
+ // 'reimporting' is the entry point for the long tail of an HMR
646
+ // cycle: V8 walks the freshly-evicted graph and the iOS runtime
647
+ // re-fetches each node from the dev server. We announce the stage
648
+ // once, before the loop, so the user sees a progress jump even when
649
+ // the import resolves on the first candidate (the common case).
650
+ setUpdateOverlayStage('reimporting', {
651
+ detail: 'Re-importing Angular bootstrap entry',
652
+ });
41
653
  for (const entry of getAngularBootstrapEntryCandidates(msg)) {
42
- const url = `${origin}/ns/m/__ns_hmr__/v${version}${entry}`;
654
+ const url = useStableUrls ? `${origin}/ns/m${entry}` : versionRaw ? `${origin}/ns/m/__ns_hmr__/v${versionRaw}${entry}` : `${origin}/ns/m${entry}`;
43
655
  try {
44
656
  g.__NS_ANGULAR_HMR_REGISTER_ONLY__ = true;
45
657
  await importAngularBootstrapEntry(url);
@@ -52,19 +664,83 @@ export async function refreshAngularBootstrapOptions(msg, options) {
52
664
  g.__NS_ANGULAR_HMR_REGISTER_ONLY__ = false;
53
665
  }
54
666
  }
55
- if (options.verbose && __NS_ENV_VERBOSE__ && lastError) {
56
- try {
57
- console.warn('[hmr-angular] failed to refresh Angular bootstrap entry', lastError?.message || lastError);
58
- }
59
- catch { }
667
+ if (options.verbose && envVerbose && lastError) {
668
+ console.warn('[hmr-angular] failed to refresh Angular bootstrap entry', lastError?.message || lastError);
60
669
  }
61
670
  }
62
671
  export async function handleAngularHotUpdateMessage(msg, options) {
63
672
  if (!msg || msg.type !== 'ns:angular-update') {
64
673
  return false;
65
674
  }
675
+ // Cycle mutex. See `inFlightHmrCycle` for why this is necessary. We
676
+ // publish `thisCycle` *before* awaiting the previous one so a third
677
+ // concurrent message sees the latest in-flight, not the stale
678
+ // previous. This serializes N concurrent updates into a single FIFO
679
+ // chain.
680
+ const previousCycle = inFlightHmrCycle;
681
+ let resolveCycle;
682
+ const thisCycle = new Promise((resolve) => {
683
+ resolveCycle = resolve;
684
+ });
685
+ inFlightHmrCycle = thisCycle;
686
+ if (previousCycle) {
687
+ try {
688
+ await previousCycle;
689
+ }
690
+ catch { }
691
+ }
692
+ // Single-line log lands in iOS device console as `CONSOLE INFO` so the user can
693
+ // correlate with the server's `[hmr-ws][update] ...` line and see
694
+ // where the save's wall time actually goes (refresh vs. reboot).
695
+ const t0 = Date.now();
696
+ let tAfterRefresh = t0;
697
+ let tEnd = t0;
698
+ const filePath = typeof msg?.path === 'string' ? msg.path : '<unknown>';
699
+ const emitTiming = (ok, errorMessage) => {
700
+ const refreshMs = Math.max(0, tAfterRefresh - t0);
701
+ const rebootMs = Math.max(0, tEnd - tAfterRefresh);
702
+ const totalMs = Math.max(0, tEnd - t0);
703
+ const status = ok ? 'ok' : 'FAILED';
704
+ const suffix = errorMessage ? ` error=${errorMessage}` : '';
705
+ console.info(`[ns-hmr][angular] ${status} file=${filePath} refresh=${refreshMs}ms reboot=${rebootMs}ms total=${totalMs}ms${suffix}`);
706
+ };
707
+ // Show the apply-progress overlay as soon as the
708
+ // mutex unblocks us. Posting this *after* the awaited
709
+ // previousCycle keeps each cycle's stages visually grouped in the
710
+ // overlay (cycle-1 finishes its 'complete' before cycle-2's
711
+ // 'received' overrides it).
712
+ //
713
+ // The detail line surfaces the changed file path so the user can
714
+ // confirm at a glance which save the overlay is reflecting.
715
+ const overlayDetail = filePath && filePath !== '<unknown>' ? `Updating ${filePath}` : 'Preparing update';
716
+ setUpdateOverlayStage('received', { detail: overlayDetail });
66
717
  try {
67
718
  const g = globalThis;
719
+ // Vite-spec entry: `vite:beforeUpdate` fires as soon as the
720
+ // client receives an update message, BEFORE any work begins
721
+ // (refresh, dispose, reboot). User listeners can use this to
722
+ // pause animations, cancel in-flight requests, etc. Payload
723
+ // matches Vite's `Update[]` shape loosely — `path` is the
724
+ // canonical changed file, `evictPaths` is the eviction
725
+ // closure the server already computed.
726
+ const updatePayload = {
727
+ type: 'js-update',
728
+ path: typeof msg?.path === 'string' ? msg.path : null,
729
+ evictPaths: Array.isArray(msg?.evictPaths) ? msg.evictPaths : [],
730
+ timestamp: t0,
731
+ };
732
+ dispatchHotEvent(g, 'vite:beforeUpdate', { updates: [updatePayload] });
733
+ // Declined-module short-circuit. If any updated module called
734
+ // `import.meta.hot.decline()`, we MUST do a full reload per
735
+ // Vite spec rather than the hot-update path. `triggerFullReload`
736
+ // also dispatches `vite:beforeFullReload`. The reload tears the
737
+ // JS realm down so we don't need to do any further bookkeeping
738
+ // here — return early.
739
+ if (checkDeclinedAndReload(g, msg, options)) {
740
+ tEnd = Date.now();
741
+ emitTiming(true);
742
+ return true;
743
+ }
68
744
  // Prefer __reboot_ng_modules__ — it properly disposes existing modules,
69
745
  // re-bootstraps Angular inside NgZone, and sets the root view internally.
70
746
  // __NS_ANGULAR_BOOTSTRAP__ is exposed as a secondary signal that the
@@ -72,10 +748,26 @@ export async function handleAngularHotUpdateMessage(msg, options) {
72
748
  // canonical reboot entry point.
73
749
  const reboot = g.__reboot_ng_modules__;
74
750
  if (typeof reboot === 'function') {
75
- if (options.verbose && __NS_ENV_VERBOSE__) {
76
- console.log('[hmr-angular] rebooting Angular modules via __reboot_ng_modules__');
751
+ if (options.verbose && envVerbose) {
752
+ console.info('[ns-hmr][client] calling __reboot_ng_modules__');
753
+ }
754
+ // Pre-import reset: clear Angular's `GENERATED_COMP_IDS` map BEFORE
755
+ // the changed component modules are re-imported. If we don't, every
756
+ // touched component's `ɵɵdefineComponent` call hashes to the same
757
+ // id as its predecessor (selectors + className haven't changed),
758
+ // hits a "previousCompDefType !== componentDef.type" collision, and
759
+ // surfaces NG0912 "Component ID generation collision detected".
760
+ // The map is cleared again post-reboot as a safety net, but doing
761
+ // it once here suppresses the warning at the source.
762
+ try {
763
+ const preResetCompiled = g.__reset_ng_compiled_components__;
764
+ if (typeof preResetCompiled === 'function') {
765
+ preResetCompiled();
766
+ }
77
767
  }
768
+ catch { }
78
769
  await refreshAngularBootstrapOptions(msg, options);
770
+ tAfterRefresh = Date.now();
79
771
  try {
80
772
  g.__NS_HMR_IMPORT_NONCE__ = (typeof g.__NS_HMR_IMPORT_NONCE__ === 'number' ? g.__NS_HMR_IMPORT_NONCE__ : 0) + 1;
81
773
  }
@@ -84,6 +776,51 @@ export async function handleAngularHotUpdateMessage(msg, options) {
84
776
  g.__NS_DEV_RESET_IN_PROGRESS__ = true;
85
777
  }
86
778
  catch { }
779
+ // Two-step pre-reboot cleanup. Order matters: dispose runs
780
+ // FIRST so user-code disposers see a still-live runtime
781
+ // (sockets, workers, etc.) and can issue graceful "I'm
782
+ // going away" messages; then we hard-terminate any worker
783
+ // the user didn't manually clean up.
784
+ //
785
+ // Step 1 — `import.meta.hot.dispose(cb)` callbacks
786
+ // ────────────────────────────────────────────────
787
+ // The NS iOS runtime exposes
788
+ // `globalThis.__nsRunHmrDispose()` (registered in
789
+ // `Worker.mm`'s sibling `HMRSupport.mm`). It walks
790
+ // `g_hotDispose` — the per-module callback registry that
791
+ // `import.meta.hot.dispose(cb)` populates — and invokes
792
+ // every callback with that module's `hot.data` object,
793
+ // matching the Vite spec. Soft-fails on older runtimes
794
+ // without the API (call is optional-chained).
795
+ //
796
+ // This is the standards-compliant cleanup hook every Vite
797
+ // user already knows. Apps register their intervals,
798
+ // listeners, sockets, store subscriptions, etc. via
799
+ // `import.meta.hot.dispose(...)` and they're guaranteed to
800
+ // fire before the next Angular reboot — no NS-specific
801
+ // knowledge required.
802
+ runHmrDisposeCallbacks(g, options);
803
+ // Step 2 — worker auto-terminate (defence in depth)
804
+ // ───────────────────────────────────────────────────
805
+ // Even with perfect dispose() coverage, workers spawned
806
+ // from constructors that re-run on Angular reboot (e.g.
807
+ // `new Worker(...)` inside `AppComponent`'s constructor)
808
+ // don't fire dispose for `app.component.ts` when only
809
+ // `login.component.html` changes — `app.component.ts`
810
+ // isn't being replaced, so its disposers don't run per
811
+ // Vite spec. The runtime API
812
+ // `__nsTerminateAllWorkers()` doesn't care about module
813
+ // boundaries; it just kills every live worker in
814
+ // `Caches::Workers`. Falls back to the
815
+ // `__NS_HMR_WORKERS__` JS Set on older runtimes.
816
+ terminateTrackedWorkers(g, options);
817
+ // 'rebooting' marks the long tail of the cycle: NgZone
818
+ // teardown, module re-instantiation, and resetRootView.
819
+ // The detail line shows refresh-so-far ms so a user can
820
+ // already see if the slow phase is re-import or reboot.
821
+ setUpdateOverlayStage('rebooting', {
822
+ detail: `Re-bootstrapping Angular (refresh ${Math.max(0, tAfterRefresh - t0)}ms)`,
823
+ });
87
824
  try {
88
825
  reboot(true);
89
826
  }
@@ -93,18 +830,63 @@ export async function handleAngularHotUpdateMessage(msg, options) {
93
830
  }
94
831
  catch { }
95
832
  }
833
+ tEnd = Date.now();
834
+ emitTiming(true);
835
+ // Vite-spec exit: `vite:afterUpdate` fires after the update
836
+ // has been successfully applied. User code can re-attach
837
+ // any state torn down by `vite:beforeUpdate` (resume
838
+ // animations, re-fetch, etc.). Same payload shape as
839
+ // `vite:beforeUpdate` so listeners can correlate.
840
+ dispatchHotEvent(g, 'vite:afterUpdate', { updates: [updatePayload] });
841
+ // 'complete' surfaces the wall-clock total so a user gets a
842
+ // single-glance confirmation matching the [ns-hmr][angular]
843
+ // log line in the terminal. The overlay auto-hides shortly
844
+ // after.
845
+ setUpdateOverlayStage('complete', {
846
+ detail: `Total ${Math.max(0, tEnd - t0)}ms`,
847
+ });
96
848
  return true;
97
849
  }
98
- if (options.verbose && __NS_ENV_VERBOSE__) {
850
+ if (options.verbose && envVerbose) {
99
851
  console.warn('[hmr-angular] No Angular HMR handler found. Ensure runNativeScriptAngularApp() has been called.');
100
852
  }
853
+ tEnd = Date.now();
854
+ emitTiming(false, 'no-reboot-handler');
855
+ // No reboot handler → we can't apply the update; hide the
856
+ // overlay rather than leaving stale progress on screen.
857
+ hideUpdateOverlay();
101
858
  }
102
859
  catch (error) {
103
860
  if (options.verbose) {
104
- try {
105
- console.warn('[hmr-angular] failed to handle update', (error && error.message) || error);
106
- }
107
- catch { }
861
+ console.warn('[hmr-angular] failed to handle update', (error && error.message) || error);
862
+ }
863
+ tEnd = Date.now();
864
+ const errorMessage = (error && error.message) || String(error);
865
+ emitTiming(false, errorMessage);
866
+ // Vite-spec error event — fires on any HMR cycle that throws.
867
+ // Payload shape mirrors Vite's `ErrorPayload`: `err: { message,
868
+ // stack? }` plus optional `path`. Soft-fails if no listeners.
869
+ dispatchHotEvent(globalThis, 'vite:error', {
870
+ type: 'error',
871
+ err: {
872
+ message: errorMessage,
873
+ stack: (error && error.stack) || undefined,
874
+ },
875
+ path: typeof msg?.path === 'string' ? msg.path : undefined,
876
+ });
877
+ // Errors → drop the overlay so the user isn't left looking
878
+ // at indefinite progress; the underlying error is already
879
+ // logged above (and surfaced via Vite's standard error
880
+ // pipeline through the websocket).
881
+ hideUpdateOverlay();
882
+ }
883
+ finally {
884
+ try {
885
+ resolveCycle();
886
+ }
887
+ catch { }
888
+ if (inFlightHmrCycle === thisCycle) {
889
+ inFlightHmrCycle = null;
108
890
  }
109
891
  }
110
892
  return true;