@absolutejs/absolute 0.19.0-beta.999 → 0.20.0-beta.1

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 (341) hide show
  1. package/LICENSE +21 -13
  2. package/README.md +48 -2
  3. package/dist/angular/browser.js +82 -77
  4. package/dist/angular/browser.js.map +11 -11
  5. package/dist/angular/components/constants.js +43 -0
  6. package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
  7. package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
  8. package/dist/angular/hmrPreserveCore.ts +132 -134
  9. package/dist/angular/index.js +1719 -844
  10. package/dist/angular/index.js.map +34 -26
  11. package/dist/angular/server.js +1620 -768
  12. package/dist/angular/server.js.map +28 -20
  13. package/dist/build.js +4935 -2593
  14. package/dist/build.js.map +72 -57
  15. package/dist/cli/config/client.js +578 -0
  16. package/dist/cli/config/server.js +39587 -0
  17. package/dist/cli/htmx/htmx.min.js +1 -0
  18. package/dist/cli/index.js +16828 -3511
  19. package/dist/client/index.js +49 -46
  20. package/dist/client/index.js.map +12 -12
  21. package/dist/core/streamingSlotRegistrar.js +1 -19
  22. package/dist/core/streamingSlotRegistrar.js.map +2 -2
  23. package/dist/core/streamingSlotRegistry.js +1 -19
  24. package/dist/core/streamingSlotRegistry.js.map +2 -2
  25. package/dist/dev/client/cssUtils.ts +98 -41
  26. package/dist/dev/client/domState.ts +2 -0
  27. package/dist/dev/client/errorOverlay.ts +14 -13
  28. package/dist/dev/client/handlers/angularHmrShim.ts +20 -20
  29. package/dist/dev/client/handlers/angularRemount.ts +74 -55
  30. package/dist/dev/client/handlers/angularRemountWiring.ts +1 -2
  31. package/dist/dev/client/handlers/html.ts +144 -41
  32. package/dist/dev/client/handlers/htmx.ts +43 -8
  33. package/dist/dev/client/handlers/react.ts +114 -32
  34. package/dist/dev/client/handlers/rebuild.ts +28 -8
  35. package/dist/dev/client/handlers/svelte.ts +92 -51
  36. package/dist/dev/client/handlers/vue.ts +36 -45
  37. package/dist/dev/client/hmrClient.ts +101 -26
  38. package/dist/dev/client/hmrTiming.ts +141 -0
  39. package/dist/dev/client/hmrToast.ts +22 -24
  40. package/dist/dev/client/reactRefreshSetup.ts +4 -0
  41. package/dist/dev/client/vendor/reactRefreshRuntime.d.ts +12 -0
  42. package/dist/dev/client/vendor/reactRefreshRuntime.js +313 -0
  43. package/dist/dev/serverBootstrap.js +45 -0
  44. package/dist/index.js +7153 -3224
  45. package/dist/index.js.map +118 -86
  46. package/dist/islands/browser.js +1 -19
  47. package/dist/islands/browser.js.map +3 -3
  48. package/dist/islands/index.js +709 -120
  49. package/dist/islands/index.js.map +18 -13
  50. package/dist/mobile/browser.js +428 -0
  51. package/dist/mobile/browser.js.map +12 -0
  52. package/dist/mobile/index.js +3058 -0
  53. package/dist/mobile/index.js.map +35 -0
  54. package/dist/react/browser.js +13 -62
  55. package/dist/react/browser.js.map +5 -6
  56. package/dist/react/components/browser/index.js +23 -13
  57. package/dist/react/components/index.js +30 -34
  58. package/dist/react/components/index.js.map +8 -7
  59. package/dist/react/hooks/index.js +1 -19
  60. package/dist/react/hooks/index.js.map +3 -3
  61. package/dist/react/index.js +1643 -679
  62. package/dist/react/index.js.map +29 -20
  63. package/dist/react/jsxDevRuntimeCompat.js +22 -20
  64. package/dist/react/jsxDevRuntimeCompat.js.map +5 -4
  65. package/dist/react/jsxRuntimeCompat.js +90 -0
  66. package/dist/react/jsxRuntimeCompat.js.map +10 -0
  67. package/dist/react/router/browser.js +77 -0
  68. package/dist/react/router/browser.js.map +10 -0
  69. package/dist/react/router/index.js +77 -0
  70. package/dist/react/router/index.js.map +10 -0
  71. package/dist/react/server.js +950 -565
  72. package/dist/react/server.js.map +16 -11
  73. package/dist/src/angular/components/constants.d.ts +21 -0
  74. package/dist/src/angular/hmrPreserveCore.d.ts +7 -20
  75. package/dist/src/angular/islands.d.ts +1 -1
  76. package/dist/src/angular/pageHandler.d.ts +2 -0
  77. package/dist/src/angular/ssrRender.d.ts +4 -1
  78. package/dist/src/build/bunStringRawUnicodePlugin.d.ts +12 -0
  79. package/dist/src/build/chainInlineSourcemaps.d.ts +1 -0
  80. package/dist/src/build/compileAngular.d.ts +5 -7
  81. package/dist/src/build/compileSvelte.d.ts +3 -0
  82. package/dist/src/build/compileVue.d.ts +3 -1
  83. package/dist/src/build/generateReactIndexes.d.ts +1 -0
  84. package/dist/src/build/islandRegistryTransform.d.ts +17 -0
  85. package/dist/src/build/maskLiterals.d.ts +28 -0
  86. package/dist/src/build/parseVueSpaRoutes.d.ts +5 -0
  87. package/dist/src/build/resolveBuildMode.d.ts +7 -0
  88. package/dist/src/build/serverExternals.d.ts +14 -0
  89. package/dist/src/build/spaSideManifests.d.ts +16 -0
  90. package/dist/src/build/tailwindCompiler.d.ts +0 -1
  91. package/dist/src/cli/add/dependencies.d.ts +13 -0
  92. package/dist/src/cli/config/absolute/AbsoluteConfigPanel.d.ts +6 -0
  93. package/dist/src/cli/config/absolute/editAbsoluteConfig.d.ts +5 -0
  94. package/dist/src/cli/config/absolute/resolveAbsoluteConfig.d.ts +19 -0
  95. package/dist/src/cli/config/auth/AuthPanel.d.ts +6 -0
  96. package/dist/src/cli/config/auth/authCatalog.d.ts +10 -0
  97. package/dist/src/cli/config/auth/authScaffolds.d.ts +16 -0
  98. package/dist/src/cli/config/auth/editAuthConfig.d.ts +5 -0
  99. package/dist/src/cli/config/auth/resolveAuthSettings.d.ts +14 -0
  100. package/dist/src/cli/config/auth/resolveAuthState.d.ts +2 -0
  101. package/dist/src/cli/config/auth/scaffoldAuthFeature.d.ts +6 -0
  102. package/dist/src/cli/config/client.d.ts +1 -0
  103. package/dist/src/cli/config/configCert.d.ts +12 -0
  104. package/dist/src/cli/config/eslint/EslintPanel.d.ts +6 -0
  105. package/dist/src/cli/config/eslint/configAst.d.ts +18 -0
  106. package/dist/src/cli/config/eslint/editConfigRule.d.ts +6 -0
  107. package/dist/src/cli/config/eslint/eslintStyles.d.ts +1 -0
  108. package/dist/src/cli/config/eslint/resolveConfig.d.ts +3 -0
  109. package/dist/src/cli/config/eslint/serializeValue.d.ts +9 -0
  110. package/dist/src/cli/config/guards.d.ts +4 -0
  111. package/dist/src/cli/config/integrations/IntegrationsPanel.d.ts +6 -0
  112. package/dist/src/cli/config/packageJson/PackageJsonPanel.d.ts +6 -0
  113. package/dist/src/cli/config/packageJson/editPackageJson.d.ts +9 -0
  114. package/dist/src/cli/config/packageJson/resolvePackageJson.d.ts +8 -0
  115. package/dist/src/cli/config/page/ConfigShell.d.ts +6 -0
  116. package/dist/src/cli/config/page/FieldEditor.d.ts +9 -0
  117. package/dist/src/cli/config/page/PanelHost.d.ts +6 -0
  118. package/dist/src/cli/config/page/configStyles.d.ts +1 -0
  119. package/dist/src/cli/config/panels.d.ts +7 -0
  120. package/dist/src/cli/config/prettier/PrettierPanel.d.ts +6 -0
  121. package/dist/src/cli/config/prettier/editPrettier.d.ts +5 -0
  122. package/dist/src/cli/config/prettier/resolvePrettier.d.ts +11 -0
  123. package/dist/src/cli/config/schema/fromJsonSchema.d.ts +3 -0
  124. package/dist/src/cli/config/schema/fromType.d.ts +1 -0
  125. package/dist/src/cli/config/schema/serialize.d.ts +1 -0
  126. package/dist/src/cli/config/server.d.ts +527 -0
  127. package/dist/src/cli/config/tsconfig/TsconfigPanel.d.ts +6 -0
  128. package/dist/src/cli/config/tsconfig/editTsconfig.d.ts +5 -0
  129. package/dist/src/cli/config/tsconfig/resolveTsconfig.d.ts +8 -0
  130. package/dist/src/cli/config/tsconfig/tsconfigStyles.d.ts +1 -0
  131. package/dist/src/cli/discoverInstances.d.ts +5 -0
  132. package/dist/src/cli/elysiaOpenApiTypeboxPlugin.d.ts +8 -0
  133. package/dist/src/cli/generate/componentTemplates.d.ts +7 -0
  134. package/dist/src/cli/generate/context.d.ts +30 -0
  135. package/dist/src/cli/generate/cssStrategy.d.ts +20 -0
  136. package/dist/src/cli/generate/frameworkKey.d.ts +3 -0
  137. package/dist/src/cli/generate/frameworks.d.ts +40 -0
  138. package/dist/src/cli/generate/generateApi.d.ts +3 -0
  139. package/dist/src/cli/generate/generateComponent.d.ts +4 -0
  140. package/dist/src/cli/generate/generatePage.d.ts +4 -0
  141. package/dist/src/cli/generate/naming.d.ts +5 -0
  142. package/dist/src/cli/generate/navData.d.ts +10 -0
  143. package/dist/src/cli/generate/outcome.d.ts +17 -0
  144. package/dist/src/cli/generate/pageTemplates.d.ts +11 -0
  145. package/dist/src/cli/generate/routeWiring.d.ts +40 -0
  146. package/dist/src/cli/generate/staticNav.d.ts +5 -0
  147. package/dist/src/cli/heapDiff.d.ts +1 -0
  148. package/dist/src/cli/htmx/install.d.ts +6 -0
  149. package/dist/src/cli/inspectData.d.ts +12 -0
  150. package/dist/src/cli/inspectTui.d.ts +1 -0
  151. package/dist/src/cli/instanceStatus.d.ts +22 -0
  152. package/dist/src/cli/integrations/addPlugin.d.ts +8 -0
  153. package/dist/src/cli/integrations/catalog.d.ts +20 -0
  154. package/dist/src/cli/psTui.d.ts +1 -0
  155. package/dist/src/cli/scripts/add.d.ts +1 -0
  156. package/dist/src/cli/scripts/analyze.d.ts +1 -0
  157. package/dist/src/cli/scripts/api.d.ts +1 -0
  158. package/dist/src/cli/scripts/build.d.ts +1 -1
  159. package/dist/src/cli/scripts/db.d.ts +20 -0
  160. package/dist/src/cli/scripts/dev.d.ts +5 -1
  161. package/dist/src/cli/scripts/doctor.d.ts +1 -0
  162. package/dist/src/cli/scripts/env.d.ts +7 -0
  163. package/dist/src/cli/scripts/eslint.d.ts +37 -2
  164. package/dist/src/cli/scripts/eslintChunked.d.ts +37 -0
  165. package/dist/src/cli/scripts/generate.d.ts +1 -0
  166. package/dist/src/cli/scripts/htmx.d.ts +1 -0
  167. package/dist/src/cli/scripts/inspect.d.ts +1 -0
  168. package/dist/src/cli/scripts/islands.d.ts +1 -0
  169. package/dist/src/cli/scripts/lintProof.d.ts +42 -0
  170. package/dist/src/cli/scripts/logs.d.ts +1 -0
  171. package/dist/src/cli/scripts/ls.d.ts +1 -0
  172. package/dist/src/cli/scripts/mem.d.ts +1 -0
  173. package/dist/src/cli/scripts/mobile.d.ts +1 -0
  174. package/dist/src/cli/scripts/prettier.d.ts +2 -0
  175. package/dist/src/cli/scripts/ps.d.ts +1 -0
  176. package/dist/src/cli/scripts/remove.d.ts +1 -0
  177. package/dist/src/cli/scripts/routes.d.ts +1 -0
  178. package/dist/src/cli/scripts/start.d.ts +6 -1
  179. package/dist/src/cli/scripts/tunnelRelay.d.ts +9 -0
  180. package/dist/src/cli/scripts/workspace.d.ts +3 -1
  181. package/dist/src/cli/serverBundleExternals.d.ts +2 -0
  182. package/dist/src/cli/tuiPrimitives.d.ts +14 -0
  183. package/dist/src/cli/typeGraphCoherence.d.ts +32 -0
  184. package/dist/src/cli/utils.d.ts +3 -1
  185. package/dist/src/cli/workspaceTui.d.ts +2 -1
  186. package/dist/src/client/hydrators/react.d.ts +1 -1
  187. package/dist/src/client/hydrators/svelte.d.ts +1 -1
  188. package/dist/src/client/hydrators/vue.d.ts +1 -1
  189. package/dist/src/client/islandStore.d.ts +3 -3
  190. package/dist/src/client/preserveIslandMarkup.d.ts +4 -1
  191. package/dist/src/constants.d.ts +21 -0
  192. package/dist/src/core/angularServerModule.d.ts +1 -0
  193. package/dist/src/core/build.d.ts +11 -3
  194. package/dist/src/core/devRouteRegistrationCallsite.d.ts +1 -5
  195. package/dist/src/core/index.d.ts +1 -0
  196. package/dist/src/core/pageResponseCache.d.ts +23 -0
  197. package/dist/src/core/prepare.d.ts +192 -154
  198. package/dist/src/core/requestContext.d.ts +18 -0
  199. package/dist/src/dev/angular/fastHmrCompiler.d.ts +5 -3
  200. package/dist/src/dev/angular/hmrCompiler.d.ts +1 -1
  201. package/dist/src/dev/angular/resolveOwningComponents.d.ts +1 -1
  202. package/dist/src/dev/clientManager.d.ts +10 -1
  203. package/dist/src/dev/configResolver.d.ts +2 -0
  204. package/dist/src/dev/moduleMapper.d.ts +1 -1
  205. package/dist/src/dev/moduleServer.d.ts +2 -1
  206. package/dist/src/dev/pathUtils.d.ts +1 -1
  207. package/dist/src/dev/reactComponentClassifier.d.ts +1 -1
  208. package/dist/src/dev/rebuildTrigger.d.ts +4 -0
  209. package/dist/src/dev/requestInspector.d.ts +31 -0
  210. package/dist/src/dev/serverBootstrap.d.ts +1 -0
  211. package/dist/src/dev/serverEntryWatcher.d.ts +3 -3
  212. package/dist/src/dev/tunnel/client.d.ts +20 -0
  213. package/dist/src/dev/tunnel/protocol.d.ts +85 -0
  214. package/dist/src/dev/tunnel/relay.d.ts +28 -0
  215. package/dist/src/dev/webSocket.d.ts +1 -1
  216. package/dist/src/mobile/androidConformance.d.ts +42 -0
  217. package/dist/src/mobile/androidEmulatorController.d.ts +99 -0
  218. package/dist/src/mobile/androidNativeWatcher.d.ts +19 -0
  219. package/dist/src/mobile/androidRelease.d.ts +47 -0
  220. package/dist/src/mobile/androidWebView.d.ts +46 -0
  221. package/dist/src/mobile/artifactStore.d.ts +47 -0
  222. package/dist/src/mobile/associationFiles.d.ts +49 -0
  223. package/dist/src/mobile/browser.d.ts +2 -0
  224. package/dist/src/mobile/buildMetadata.d.ts +10 -0
  225. package/dist/src/mobile/buildPipeline.d.ts +8 -0
  226. package/dist/src/mobile/buildRelease.d.ts +24 -0
  227. package/dist/src/mobile/capacitorBundle.d.ts +28 -0
  228. package/dist/src/mobile/capacitorProject.d.ts +9 -0
  229. package/dist/src/mobile/client.d.ts +35 -0
  230. package/dist/src/mobile/compatibilityDispatcher.d.ts +26 -0
  231. package/dist/src/mobile/config.d.ts +16 -0
  232. package/dist/src/mobile/emulatorDoctor.d.ts +25 -0
  233. package/dist/src/mobile/emulatorInstaller.d.ts +41 -0
  234. package/dist/src/mobile/index.d.ts +19 -0
  235. package/dist/src/mobile/materializedBundle.d.ts +36 -0
  236. package/dist/src/mobile/nativeDeepLinks.d.ts +5 -0
  237. package/dist/src/mobile/pageProtocol.d.ts +89 -0
  238. package/dist/src/mobile/producerContext.d.ts +4 -0
  239. package/dist/src/mobile/producerContextState.d.ts +10 -0
  240. package/dist/src/mobile/releaseArtifact.d.ts +75 -0
  241. package/dist/src/mobile/releaseDoctor.d.ts +13 -0
  242. package/dist/src/mobile/routeMatcher.d.ts +3 -0
  243. package/dist/src/mobile/routeMetadataTransform.d.ts +13 -0
  244. package/dist/src/mobile/shellBootstrap.d.ts +1 -0
  245. package/dist/src/mobile/transport.d.ts +31 -0
  246. package/dist/src/plugins/devtoolsJson.d.ts +6 -47
  247. package/dist/src/plugins/hmr.d.ts +22 -96
  248. package/dist/src/plugins/imageOptimizer.d.ts +150 -62
  249. package/dist/src/plugins/networking.d.ts +2 -2
  250. package/dist/src/plugins/openApiPlugin.d.ts +4 -0
  251. package/dist/src/plugins/telemetryPlugin.d.ts +36 -0
  252. package/dist/src/react/browser.d.ts +0 -2
  253. package/dist/src/react/hooks/useMediaQuery.d.ts +1 -1
  254. package/dist/src/react/index.d.ts +0 -2
  255. package/dist/src/react/jsxDevRuntimeCompat.d.ts +1 -1
  256. package/dist/src/react/jsxRuntimeCompat.d.ts +7 -0
  257. package/dist/src/react/pageHandler.d.ts +8 -0
  258. package/dist/src/react/router/UniversalRouter.d.ts +43 -0
  259. package/dist/src/react/router/browser.d.ts +2 -0
  260. package/dist/src/react/router/index.d.ts +2 -0
  261. package/dist/src/svelte/pageHandler.d.ts +5 -0
  262. package/dist/src/svelte/router/hashMode.d.ts +1 -6
  263. package/dist/src/svelte/router/matchPath.d.ts +4 -24
  264. package/dist/src/svelte/router/page.svelte.d.ts +1 -10
  265. package/dist/src/svelte/router/prefetchCache.d.ts +2 -6
  266. package/dist/src/utils/cleanup.d.ts +1 -0
  267. package/dist/src/utils/defineConvention.d.ts +2 -2
  268. package/dist/src/utils/defineEnv.d.ts +4 -4
  269. package/dist/src/utils/formatBytes.d.ts +2 -0
  270. package/dist/src/utils/generatedDir.d.ts +1 -1
  271. package/dist/src/utils/iconVersion.d.ts +5 -0
  272. package/dist/src/utils/imageProcessing.d.ts +1 -1
  273. package/dist/src/utils/index.d.ts +2 -0
  274. package/dist/src/utils/inlinePageCss.d.ts +9 -0
  275. package/dist/src/utils/instanceRegistry.d.ts +13 -0
  276. package/dist/src/utils/isTestSourcePath.d.ts +1 -0
  277. package/dist/src/utils/jsonLd.d.ts +1 -1
  278. package/dist/src/utils/loadConfig.d.ts +11 -3
  279. package/dist/src/utils/logger.d.ts +2 -0
  280. package/dist/src/utils/portScan.d.ts +22 -0
  281. package/dist/src/utils/projectRoot.d.ts +17 -0
  282. package/dist/src/utils/resolveConvention.d.ts +1 -1
  283. package/dist/src/utils/resolveDevPort.d.ts +4 -1
  284. package/dist/src/utils/runtimeMode.d.ts +1 -1
  285. package/dist/src/utils/spaRouteCss.d.ts +10 -0
  286. package/dist/src/utils/spaRouteManifest.d.ts +8 -0
  287. package/dist/src/utils/stripStringsAndComments.d.ts +18 -0
  288. package/dist/src/utils/userAgentFunctions.d.ts +1 -1
  289. package/dist/src/utils/vueCompiler.d.ts +1 -0
  290. package/dist/src/vue/components/Image.d.ts +2 -2
  291. package/dist/src/vue/defineVuePage.d.ts +1 -11
  292. package/dist/src/vue/index.d.ts +3 -0
  293. package/dist/src/vue/pageHandler.d.ts +5 -0
  294. package/dist/src/vue/resolveGeneratedVueModulePath.d.ts +1 -0
  295. package/dist/src/vue/teleports.d.ts +2 -0
  296. package/dist/src/vue/useResource.d.ts +65 -0
  297. package/dist/svelte/browser.js +1 -19
  298. package/dist/svelte/browser.js.map +3 -3
  299. package/dist/svelte/components/Head.svelte +7 -1
  300. package/dist/svelte/index.js +1281 -543
  301. package/dist/svelte/index.js.map +27 -19
  302. package/dist/svelte/router/Link.svelte.d.ts +3 -3
  303. package/dist/svelte/router/Route.svelte.d.ts +5 -5
  304. package/dist/svelte/router/Router.svelte.d.ts +3 -3
  305. package/dist/svelte/router/hashMode.ts +6 -12
  306. package/dist/svelte/router/matchPath.ts +44 -61
  307. package/dist/svelte/router/page.d.ts +1 -10
  308. package/dist/svelte/router/page.js +6 -6
  309. package/dist/svelte/router/prefetchCache.ts +21 -23
  310. package/dist/svelte/router/viewTransitions.ts +22 -12
  311. package/dist/svelte/server.js +697 -565
  312. package/dist/svelte/server.js.map +14 -11
  313. package/dist/types/absoluteConfig.d.ts +22 -0
  314. package/dist/types/angular.d.ts +4 -1
  315. package/dist/types/authPanel.d.ts +62 -0
  316. package/dist/types/build.d.ts +116 -0
  317. package/dist/types/cli.d.ts +57 -0
  318. package/dist/types/config.d.ts +52 -0
  319. package/dist/types/eslintConfig.d.ts +68 -0
  320. package/dist/types/globals.d.ts +31 -10
  321. package/dist/types/image.d.ts +2 -0
  322. package/dist/types/index.d.ts +1 -0
  323. package/dist/types/integrationsPanel.d.ts +29 -0
  324. package/dist/types/messages.d.ts +12 -0
  325. package/dist/types/packageJson.d.ts +74 -0
  326. package/dist/types/packageJsonPanel.d.ts +29 -0
  327. package/dist/types/prettier.d.ts +26 -0
  328. package/dist/types/tsconfig.d.ts +31 -0
  329. package/dist/types/vue.d.ts +13 -15
  330. package/dist/vue/browser.js +14 -36
  331. package/dist/vue/browser.js.map +5 -5
  332. package/dist/vue/components/Image.js +3 -20
  333. package/dist/vue/components/Image.js.map +3 -3
  334. package/dist/vue/components/index.js +3 -20
  335. package/dist/vue/components/index.js.map +3 -3
  336. package/dist/vue/index.js +1573 -620
  337. package/dist/vue/index.js.map +34 -22
  338. package/dist/vue/server.js +800 -520
  339. package/dist/vue/server.js.map +17 -11
  340. package/package.json +145 -68
  341. package/dist/src/react/UniversalRouter.d.ts +0 -12
@@ -9,43 +9,13 @@ import {
9
9
  restoreScrollState
10
10
  } from '../domState';
11
11
  import { detectCurrentFramework, findIndexPath } from '../frameworkDetect';
12
+ import { sendAbsoluteHmrTiming } from '../hmrTiming';
13
+ import { swapCSSStylesheet } from '../cssUtils';
12
14
 
13
15
  type SvelteHmrWindow = Window & {
14
16
  __SVELTE_HMR_ACCEPT__?: Record<string, (mod: unknown) => void>;
15
17
  };
16
18
 
17
- /* Swap a stylesheet link by matching cssBaseName or framework name */
18
- const swapStylesheet = (
19
- cssUrl: string,
20
- cssBaseName: string,
21
- framework: string
22
- ) => {
23
- let existingLink: HTMLLinkElement | null = null;
24
- document
25
- .querySelectorAll<HTMLLinkElement>('link[rel="stylesheet"]')
26
- .forEach((link) => {
27
- const href = link.getAttribute('href') ?? '';
28
- if (href.includes(cssBaseName) || href.includes(framework)) {
29
- existingLink = link;
30
- }
31
- });
32
-
33
- if (!existingLink) {
34
- return;
35
- }
36
-
37
- const capturedExisting: HTMLLinkElement = existingLink;
38
- const newLink = document.createElement('link');
39
- newLink.rel = 'stylesheet';
40
- newLink.href = `${cssUrl}?t=${Date.now()}`;
41
- newLink.onload = () => {
42
- if (capturedExisting && capturedExisting.parentNode) {
43
- capturedExisting.remove();
44
- }
45
- };
46
- document.head.appendChild(newLink);
47
- };
48
-
49
19
  const extractCountFromDOM = () => {
50
20
  const countButton = document.querySelector('button');
51
21
  if (!countButton || !countButton.textContent) {
@@ -222,17 +192,27 @@ export const handleSvelteUpdate = (message: {
222
192
  sourceFile?: string;
223
193
  updateType?: string;
224
194
  };
195
+ timestamp?: number;
225
196
  }) => {
226
197
  const svelteFrameworkCheck = detectCurrentFramework();
227
198
  if (svelteFrameworkCheck !== 'svelte') return;
228
199
 
229
200
  /* CSS-only update: hot-swap stylesheet, no remount needed */
230
201
  if (message.data.updateType === 'css-only' && message.data.cssUrl) {
231
- swapStylesheet(
202
+ const clientStart = performance.now();
203
+ const cssBaseName = message.data.cssBaseName || '';
204
+ void swapCSSStylesheet(
232
205
  message.data.cssUrl,
233
- message.data.cssBaseName || '',
234
- 'svelte'
235
- );
206
+ (href) => href.includes(cssBaseName) || href.includes('svelte')
207
+ ).then((applied) => {
208
+ sendAbsoluteHmrTiming({
209
+ clientStart,
210
+ kind: 'css',
211
+ outcome: applied ? 'applied' : 'failed',
212
+ serverMs: message.data.serverDuration,
213
+ updateId: message.timestamp
214
+ });
215
+ });
236
216
 
237
217
  return;
238
218
  }
@@ -255,10 +235,10 @@ export const handleSvelteUpdate = (message: {
255
235
 
256
236
  /* CSS pre-update: swap stylesheet BEFORE importing to prevent FOUC */
257
237
  if (message.data.cssUrl) {
258
- swapStylesheet(
238
+ const cssBaseName = message.data.cssBaseName || '';
239
+ void swapCSSStylesheet(
259
240
  message.data.cssUrl,
260
- message.data.cssBaseName || '',
261
- 'svelte'
241
+ (href) => href.includes(cssBaseName) || href.includes('svelte')
262
242
  );
263
243
  }
264
244
 
@@ -273,32 +253,93 @@ export const handleSvelteUpdate = (message: {
273
253
  const svelteWindow: SvelteHmrWindow = window;
274
254
  const acceptRegistry = svelteWindow.__SVELTE_HMR_ACCEPT__;
275
255
 
276
- // Save the OLD module's accept callback BEFORE importing.
277
- const acceptFn = acceptRegistry?.[pageModuleUrl];
256
+ // Save the OLD module's accept callback BEFORE importing. The exact
257
+ // key is the broadcast `pageModuleUrl`; on the happy path it matches
258
+ // the key the module server registered. If it doesn't (the registry
259
+ // was repopulated under a slightly different URL key, or `bun --hot`
260
+ // re-evaluated the module and reset `__SVELTE_HMR_ACCEPT__`), fall back
261
+ // to a suffix/basename match so we still find the callback. Exact match
262
+ // always wins, so the happy path is unchanged.
263
+ let acceptFn = acceptRegistry?.[pageModuleUrl];
264
+ if (!acceptFn && acceptRegistry) {
265
+ const wantBase = pageModuleUrl.split('?')[0] ?? pageModuleUrl;
266
+ const hit = Object.keys(acceptRegistry).find(
267
+ (key) =>
268
+ key === wantBase ||
269
+ key.endsWith(wantBase) ||
270
+ wantBase.endsWith(key)
271
+ );
272
+ if (hit) acceptFn = acceptRegistry[hit];
273
+ }
278
274
 
279
275
  import(modulePath)
280
276
  .then((newModule) => {
277
+ let applied = false;
281
278
  if (acceptFn) {
282
279
  acceptFn(newModule);
280
+ applied = true;
283
281
  }
284
282
 
285
- if (
286
- window.__HMR_WS__ &&
287
- message.data.serverDuration !== undefined
288
- ) {
289
- const clientMs = Math.round(
290
- performance.now() - clientStart
291
- );
292
- const total = (message.data.serverDuration ?? 0) + clientMs;
293
- window.__HMR_WS__.send(
294
- JSON.stringify({ duration: total, type: 'hmr-timing' })
283
+ /* $.hmr_accept swaps component code in place but re-runs
284
+ * the <script> body with the original mount props, so any
285
+ * state seeded from a prop (e.g. a composable doing
286
+ * $state(initialCount)) resets. Remount with the preserved
287
+ * state merged into props — mirroring the bundled-fallback
288
+ * bootstrap — so that state carries across (issue #41). */
289
+ const preserved = window.__HMR_PRESERVED_STATE__;
290
+ const remount = window.__SVELTE_REMOUNT__;
291
+ const hasPreserved =
292
+ preserved && Object.keys(preserved).length > 0;
293
+
294
+ if (applied) {
295
+ if (typeof remount === 'function' && hasPreserved) {
296
+ remount({
297
+ ...(window.__INITIAL_PROPS__ ?? {}),
298
+ ...preserved
299
+ });
300
+ }
301
+ } else if (typeof remount === 'function') {
302
+ /* Resilience fallback: no accept callback was found, so
303
+ * `$.hmr` never wired the freshly imported module in. The
304
+ * import warmed the module cache; remount the page with
305
+ * merged props to actually apply the new code instead of
306
+ * silently doing nothing. */
307
+ remount({
308
+ ...(window.__INITIAL_PROPS__ ?? {}),
309
+ ...(preserved ?? {})
310
+ });
311
+ } else {
312
+ /* Last resort: nothing can apply the update in place. */
313
+ console.warn(
314
+ '[HMR] Svelte accept callback missing and no remount available; reloading'
295
315
  );
316
+ window.__HMR_PRESERVED_STATE__ = undefined;
317
+ window.location.reload();
318
+
319
+ return undefined;
320
+ }
321
+ window.__HMR_PRESERVED_STATE__ = undefined;
322
+
323
+ if (message.data.serverDuration !== undefined) {
324
+ sendAbsoluteHmrTiming({
325
+ clientStart,
326
+ kind: 'component',
327
+ serverMs: message.data.serverDuration,
328
+ updateId: message.timestamp
329
+ });
296
330
  }
297
331
 
298
332
  return undefined;
299
333
  })
300
334
  .catch((err: unknown) => {
301
335
  console.warn('[HMR] Svelte HMR failed, reloading:', err);
336
+ sendAbsoluteHmrTiming({
337
+ clientStart,
338
+ kind: 'component',
339
+ outcome: 'reloaded',
340
+ serverMs: message.data.serverDuration,
341
+ updateId: message.timestamp
342
+ });
302
343
  window.location.reload();
303
344
  });
304
345
 
@@ -4,6 +4,8 @@ import type {} from '../../../types/globals';
4
4
  import type { VueComponentInstance, VueVNode } from '../../../types/vue';
5
5
  import { saveDOMState, restoreDOMState } from '../domState';
6
6
  import { detectCurrentFramework, findIndexPath } from '../frameworkDetect';
7
+ import { sendAbsoluteHmrTiming } from '../hmrTiming';
8
+ import { swapCSSStylesheet } from '../cssUtils';
7
9
 
8
10
  /* Collect reactive value from a setup state entry into the target record */
9
11
  const collectSetupValue = (
@@ -67,38 +69,6 @@ const extractChildComponentState = (
67
69
  walkVNode(instance.subTree, state);
68
70
  };
69
71
 
70
- /* Find an existing stylesheet link matching the given base name */
71
- const findMatchingStylesheetLink = (cssBaseName: string) => {
72
- let found: HTMLLinkElement | null = null;
73
- document
74
- .querySelectorAll<HTMLLinkElement>('link[rel="stylesheet"]')
75
- .forEach((link) => {
76
- const href = link.getAttribute('href') ?? '';
77
- if (cssBaseName && href.includes(cssBaseName)) {
78
- found = link;
79
- }
80
- });
81
-
82
- return found;
83
- };
84
-
85
- /* Swap a stylesheet link with a new one, removing the old on load */
86
- const swapStylesheet = (cssUrl: string, cssBaseName: string) => {
87
- const existingLink = findMatchingStylesheetLink(cssBaseName);
88
- if (!existingLink) return;
89
-
90
- const capturedExisting: HTMLLinkElement = existingLink;
91
- const newLink = document.createElement('link');
92
- newLink.rel = 'stylesheet';
93
- newLink.href = `${cssUrl}?t=${Date.now()}`;
94
- newLink.onload = function () {
95
- if (capturedExisting && capturedExisting.parentNode) {
96
- capturedExisting.remove();
97
- }
98
- };
99
- document.head.appendChild(newLink);
100
- };
101
-
102
72
  /* Extract Vue reactive state from app instance */
103
73
  const extractVueAppState = (vuePreservedState: Record<string, unknown>) => {
104
74
  if (!window.__VUE_APP__ || !window.__VUE_APP__._instance) return;
@@ -163,12 +133,26 @@ export const handleVueUpdate = (message: {
163
133
  sourceFile?: string;
164
134
  updateType?: string;
165
135
  };
136
+ timestamp?: number;
166
137
  }) => {
167
138
  const vueFrameworkCheck = detectCurrentFramework();
168
139
  if (vueFrameworkCheck !== 'vue') return;
169
140
 
170
141
  if (message.data.updateType === 'css-only' && message.data.cssUrl) {
171
- swapStylesheet(message.data.cssUrl, message.data.cssBaseName || '');
142
+ const clientStart = performance.now();
143
+ const cssBaseName = message.data.cssBaseName || '';
144
+ void swapCSSStylesheet(
145
+ message.data.cssUrl,
146
+ (href) => Boolean(cssBaseName) && href.includes(cssBaseName)
147
+ ).then((applied) => {
148
+ sendAbsoluteHmrTiming({
149
+ clientStart,
150
+ kind: 'css',
151
+ outcome: applied ? 'applied' : 'failed',
152
+ serverMs: message.data.serverDuration,
153
+ updateId: message.timestamp
154
+ });
155
+ });
172
156
 
173
157
  return;
174
158
  }
@@ -224,17 +208,13 @@ export const handleVueUpdate = (message: {
224
208
  }
225
209
  sessionStorage.removeItem('__HMR_ACTIVE__');
226
210
 
227
- if (
228
- window.__HMR_WS__ &&
229
- message.data.serverDuration !== undefined
230
- ) {
231
- const clientMs = Math.round(
232
- performance.now() - clientStart
233
- );
234
- const total = (message.data.serverDuration ?? 0) + clientMs;
235
- window.__HMR_WS__.send(
236
- JSON.stringify({ duration: total, type: 'hmr-timing' })
237
- );
211
+ if (message.data.serverDuration !== undefined) {
212
+ sendAbsoluteHmrTiming({
213
+ clientStart,
214
+ kind: 'component',
215
+ serverMs: message.data.serverDuration,
216
+ updateId: message.timestamp
217
+ });
238
218
  }
239
219
 
240
220
  return undefined;
@@ -242,6 +222,13 @@ export const handleVueUpdate = (message: {
242
222
  .catch((err: unknown) => {
243
223
  console.warn('[HMR] Vue HMR failed, reloading:', err);
244
224
  sessionStorage.removeItem('__HMR_ACTIVE__');
225
+ sendAbsoluteHmrTiming({
226
+ clientStart,
227
+ kind: 'component',
228
+ outcome: 'reloaded',
229
+ serverMs: message.data.serverDuration,
230
+ updateId: message.timestamp
231
+ });
245
232
  window.location.reload();
246
233
  });
247
234
 
@@ -250,7 +237,11 @@ export const handleVueUpdate = (message: {
250
237
 
251
238
  /* CSS pre-update: swap stylesheet BEFORE unmounting to prevent FOUC */
252
239
  if (message.data.cssUrl) {
253
- swapStylesheet(message.data.cssUrl, message.data.cssBaseName || '');
240
+ const cssBaseName = message.data.cssBaseName || '';
241
+ void swapCSSStylesheet(
242
+ message.data.cssUrl,
243
+ (href) => Boolean(cssBaseName) && href.includes(cssBaseName)
244
+ );
254
245
  }
255
246
 
256
247
  /* Unmount old Vue app but keep DOM visually intact during async import.
@@ -12,6 +12,11 @@ import {
12
12
  WEBSOCKET_NORMAL_CLOSURE
13
13
  } from './constants';
14
14
  import { detectCurrentFramework } from './frameworkDetect';
15
+ import {
16
+ absoluteHmrClientTarget,
17
+ restoreAbsoluteHmrApply,
18
+ sendAbsoluteHmrTiming
19
+ } from './hmrTiming';
15
20
  import { hideErrorOverlay, showErrorOverlay } from './errorOverlay';
16
21
  import {
17
22
  dispatchAngularComponentRemount,
@@ -32,8 +37,54 @@ import {
32
37
  handleRebuildError
33
38
  } from './handlers/rebuild';
34
39
 
40
+ const isRecord = (value: unknown): value is Record<string, unknown> =>
41
+ typeof value === 'object' && value !== null;
42
+
43
+ const isStringRecord = (value: unknown): value is Record<string, string> =>
44
+ isRecord(value) &&
45
+ Object.values(value).every((entry) => typeof entry === 'string');
46
+
47
+ restoreAbsoluteHmrApply();
48
+
49
+ /* Lightweight "server disconnected" banner. When the dev server is
50
+ * genuinely down (process restarting or crashed) the browser would
51
+ * otherwise show its own blank "site can't be reached" page — this keeps
52
+ * a branded, self-explanatory notice on screen while the client polls
53
+ * `/hmr-status` and reloads on recovery. Kept separate from the compile
54
+ * error overlay so it never clobbers a build-error message. The
55
+ * `data-hmr-overlay` attribute exempts it from HMR DOM diffing. */
56
+ const CONNECTION_LOST_BANNER_ID = 'absolutejs-connection-lost';
57
+ const showConnectionLostBanner = () => {
58
+ if (typeof document === 'undefined' || !document.body) return;
59
+ if (document.getElementById(CONNECTION_LOST_BANNER_ID)) return;
60
+ const banner = document.createElement('div');
61
+ banner.id = CONNECTION_LOST_BANNER_ID;
62
+ banner.setAttribute('data-hmr-overlay', 'true');
63
+ banner.style.cssText = [
64
+ 'position:fixed',
65
+ 'left:0',
66
+ 'right:0',
67
+ 'bottom:0',
68
+ 'z-index:2147483646',
69
+ 'padding:10px 16px',
70
+ 'font:600 13px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace',
71
+ 'color:#fff',
72
+ 'background:#b91c1c',
73
+ 'text-align:center',
74
+ 'box-shadow:0 -2px 12px rgba(0,0,0,0.3)'
75
+ ].join(';');
76
+ banner.textContent = 'AbsoluteJS · dev server disconnected — reconnecting…';
77
+ document.body.appendChild(banner);
78
+ };
79
+ const hideConnectionLostBanner = () => {
80
+ if (typeof document === 'undefined') return;
81
+ const banner = document.getElementById(CONNECTION_LOST_BANNER_ID);
82
+ if (banner && banner.parentNode) banner.parentNode.removeChild(banner);
83
+ };
84
+
35
85
  // Initialize HMR globals
36
86
  if (typeof window !== 'undefined') {
87
+ window.__ABS_HMR_TARGET__ = absoluteHmrClientTarget();
37
88
  installAngularRemountGlobal();
38
89
  if (!window.__HMR_MANIFEST__) {
39
90
  window.__HMR_MANIFEST__ = {};
@@ -76,6 +127,7 @@ const hmrUpdateTypes = new Set([
76
127
  'angular:component-update',
77
128
  'angular:component-remount',
78
129
  'angular:rebootstrap',
130
+ 'angular-update',
79
131
  'react-update',
80
132
  'html-update',
81
133
  'htmx-update',
@@ -91,6 +143,7 @@ type HMRMessage = {
91
143
  affectedFrameworks?: string[];
92
144
  column?: number;
93
145
  error?: string;
146
+ fastRefreshSupported?: boolean;
94
147
  file?: string;
95
148
  framework?: string;
96
149
  hasCSSChanges?: boolean;
@@ -106,9 +159,23 @@ type HMRMessage = {
106
159
  serverDuration?: number;
107
160
  serverVersions?: Record<string, number>;
108
161
  };
162
+ timestamp?: number;
109
163
  type: string;
110
164
  };
111
165
 
166
+ const handleStylesheetUpdate = (message: HMRMessage) => {
167
+ const clientStart = performance.now();
168
+ void reloadCSSStylesheets(message.data.manifest ?? {}).then((applied) => {
169
+ sendAbsoluteHmrTiming({
170
+ clientStart,
171
+ kind: 'css',
172
+ outcome: applied ? 'applied' : 'failed',
173
+ serverMs: message.data.serverDuration,
174
+ updateId: message.timestamp
175
+ });
176
+ });
177
+ };
178
+
112
179
  const handleHMRMessage = (message: HMRMessage) => {
113
180
  if (hmrUpdateTypes.has(message.type)) {
114
181
  hmrState.isHMRUpdating = true;
@@ -162,15 +229,15 @@ const handleHMRMessage = (message: HMRMessage) => {
162
229
  // `__ng_hmr_load` blocks (see hmrInjectionPlugin.ts)
163
230
  // listen here and re-fetch the applyMetadata module.
164
231
  hideErrorOverlay();
165
- const data = message.data as
166
- | { id?: string; timestamp?: number }
167
- | undefined;
168
- if (data && typeof data.id === 'string') {
232
+ const { data } = message;
233
+ if (isRecord(data)) {
234
+ const id = Reflect.get(data, 'id');
235
+ if (typeof id !== 'string') break;
169
236
  dispatchAngularComponentUpdate({
170
- id: data.id,
237
+ id,
171
238
  timestamp:
172
- typeof data.timestamp === 'number'
173
- ? data.timestamp
239
+ typeof message.timestamp === 'number'
240
+ ? message.timestamp
174
241
  : Date.now()
175
242
  });
176
243
  }
@@ -185,15 +252,15 @@ const handleHMRMessage = (message: HMRMessage) => {
185
252
  // `__absAngularRemount` global wired in
186
253
  // `installAngularRemountGlobal`.
187
254
  hideErrorOverlay();
188
- const data = message.data as
189
- | { id?: string; timestamp?: number }
190
- | undefined;
191
- if (data && typeof data.id === 'string') {
255
+ const { data } = message;
256
+ if (isRecord(data)) {
257
+ const id = Reflect.get(data, 'id');
258
+ if (typeof id !== 'string') break;
192
259
  dispatchAngularComponentRemount({
193
- id: data.id,
260
+ id,
194
261
  timestamp:
195
- typeof data.timestamp === 'number'
196
- ? data.timestamp
262
+ typeof message.timestamp === 'number'
263
+ ? message.timestamp
197
264
  : Date.now()
198
265
  });
199
266
  }
@@ -209,17 +276,17 @@ const handleHMRMessage = (message: HMRMessage) => {
209
276
  // to dynamic-import the fresh bundle URL — re-importing
210
277
  // re-runs the destroy + bootstrapApplication block.
211
278
  hideErrorOverlay();
212
- const data = message.data as
213
- | { manifest?: Record<string, string>; reason?: string }
214
- | undefined;
215
- if (data?.manifest) {
216
- window.__HMR_MANIFEST__ = data.manifest;
279
+ const { data } = message;
280
+ if (isRecord(data) && isStringRecord(data.manifest)) {
281
+ const { manifest } = data;
282
+ window.__HMR_MANIFEST__ = manifest;
217
283
  }
218
- const w = window as Window & {
219
- __ABS_ANGULAR_REBOOTSTRAP__?: () => Promise<void>;
220
- };
221
- if (typeof w.__ABS_ANGULAR_REBOOTSTRAP__ === 'function') {
222
- w.__ABS_ANGULAR_REBOOTSTRAP__().catch((err) => {
284
+ const rebootstrap = Reflect.get(
285
+ window,
286
+ '__ABS_ANGULAR_REBOOTSTRAP__'
287
+ );
288
+ if (typeof rebootstrap === 'function') {
289
+ Promise.resolve(rebootstrap()).catch((err) => {
223
290
  console.error(
224
291
  '[absolutejs] angular:rebootstrap failed',
225
292
  err
@@ -237,12 +304,17 @@ const handleHMRMessage = (message: HMRMessage) => {
237
304
  handleRebuildError(message);
238
305
  break;
239
306
  case 'full-reload':
240
- handleFullReload();
307
+ handleFullReload(message);
241
308
  break;
242
309
  case 'pong':
243
310
  break;
244
311
  case 'style-update':
245
- reloadCSSStylesheets(message.data.manifest ?? {});
312
+ handleStylesheetUpdate(message);
313
+ break;
314
+ case 'angular-update':
315
+ if (detectCurrentFramework() === 'angular') {
316
+ handleStylesheetUpdate(message);
317
+ }
246
318
  break;
247
319
  default:
248
320
  break;
@@ -263,12 +335,14 @@ if (!(window.__HMR_WS__ && window.__HMR_WS__.readyState === WebSocket.OPEN)) {
263
335
 
264
336
  wsc.onopen = function () {
265
337
  hmrState.isConnected = true;
338
+ hideConnectionLostBanner();
266
339
  sessionStorage.setItem('__HMR_CONNECTED__', 'true');
267
340
 
268
341
  const currentFramework = detectCurrentFramework();
269
342
  wsc.send(
270
343
  JSON.stringify({
271
344
  framework: currentFramework,
345
+ target: absoluteHmrClientTarget(),
272
346
  type: 'ready'
273
347
  })
274
348
  );
@@ -305,6 +379,7 @@ if (!(window.__HMR_WS__ && window.__HMR_WS__.readyState === WebSocket.OPEN)) {
305
379
  }
306
380
 
307
381
  if (event.code !== WEBSOCKET_NORMAL_CLOSURE) {
382
+ showConnectionLostBanner();
308
383
  let attempts = 0;
309
384
  hmrState.reconnectTimeout = setTimeout(function pollServer() {
310
385
  attempts++;
@@ -0,0 +1,141 @@
1
+ import type {} from '../../types/globals';
2
+ import type {
3
+ HMRApplyKind,
4
+ HMRApplyOutcome,
5
+ HMRClientTarget
6
+ } from '../../../types/messages';
7
+
8
+ const MAX_RETAINED_HMR_APPLIES = 50;
9
+ const HMR_APPLY_STORAGE_KEY = '__absolute_hmr_last_apply__';
10
+
11
+ const isRecord = (value: unknown): value is Record<string, unknown> =>
12
+ typeof value === 'object' && value !== null;
13
+
14
+ const isHmrTarget = (value: unknown): value is HMRClientTarget =>
15
+ value === 'capacitor-android' ||
16
+ value === 'capacitor-ios' ||
17
+ value === 'capacitor-native' ||
18
+ value === 'web';
19
+
20
+ const isHmrKind = (value: unknown): value is HMRApplyKind =>
21
+ value === 'component' ||
22
+ value === 'css' ||
23
+ value === 'full-reload' ||
24
+ value === 'html' ||
25
+ value === 'htmx' ||
26
+ value === 'script';
27
+
28
+ const isHmrOutcome = (value: unknown): value is HMRApplyOutcome =>
29
+ value === 'applied' || value === 'failed' || value === 'reloaded';
30
+
31
+ type HmrApply = NonNullable<Window['__ABS_HMR_LAST_APPLY__']>;
32
+
33
+ const isHmrApply = (value: unknown): value is HmrApply =>
34
+ isRecord(value) &&
35
+ typeof value.clientMs === 'number' &&
36
+ typeof value.duration === 'number' &&
37
+ (value.kind === undefined || isHmrKind(value.kind)) &&
38
+ isHmrOutcome(value.outcome) &&
39
+ typeof value.serverMs === 'number' &&
40
+ isHmrTarget(value.target) &&
41
+ (value.updateId === undefined || typeof value.updateId === 'number');
42
+
43
+ const getSessionStorage = () => {
44
+ const storage = Reflect.get(window, 'sessionStorage');
45
+ if (!isRecord(storage)) return undefined;
46
+
47
+ return storage;
48
+ };
49
+
50
+ const persistHmrApply = (timing: HmrApply) => {
51
+ try {
52
+ const storage = getSessionStorage();
53
+ const setItem = storage && Reflect.get(storage, 'setItem');
54
+ if (typeof setItem !== 'function') return;
55
+ Reflect.apply(setItem, storage, [
56
+ HMR_APPLY_STORAGE_KEY,
57
+ JSON.stringify(timing)
58
+ ]);
59
+ } catch {
60
+ // Storage may be unavailable in sandboxed or privacy-restricted documents.
61
+ }
62
+ };
63
+
64
+ export const absoluteHmrClientTarget = () => {
65
+ const currentLocation = Reflect.get(globalThis, 'location');
66
+ if (typeof currentLocation === 'object' && currentLocation !== null) {
67
+ const search = Reflect.get(currentLocation, 'search');
68
+ if (typeof search === 'string') {
69
+ const marker = new URLSearchParams(search).get('__absolute_target');
70
+ if (marker === 'capacitor-android') return marker;
71
+ if (marker === 'capacitor-ios') return marker;
72
+ }
73
+ }
74
+ const capacitor = Reflect.get(globalThis, 'Capacitor');
75
+ if (!isRecord(capacitor)) return 'web';
76
+ const getPlatform = Reflect.get(capacitor, 'getPlatform');
77
+ const isNativePlatform = Reflect.get(capacitor, 'isNativePlatform');
78
+ const platform =
79
+ typeof getPlatform === 'function'
80
+ ? Reflect.apply(getPlatform, capacitor, [])
81
+ : undefined;
82
+ if (platform === 'android') return 'capacitor-android';
83
+ if (platform === 'ios') return 'capacitor-ios';
84
+ if (
85
+ typeof isNativePlatform === 'function' &&
86
+ Reflect.apply(isNativePlatform, capacitor, []) === true
87
+ ) {
88
+ return 'capacitor-native';
89
+ }
90
+
91
+ return 'web';
92
+ };
93
+
94
+ export const restoreAbsoluteHmrApply = () => {
95
+ try {
96
+ const storage = getSessionStorage();
97
+ const getItem = storage && Reflect.get(storage, 'getItem');
98
+ if (typeof getItem !== 'function') return;
99
+ const serialized = Reflect.apply(getItem, storage, [
100
+ HMR_APPLY_STORAGE_KEY
101
+ ]);
102
+ if (typeof serialized !== 'string') return;
103
+ const timing: unknown = JSON.parse(serialized);
104
+ if (!isHmrApply(timing)) return;
105
+ window.__ABS_HMR_LAST_APPLY__ = timing;
106
+ window.__ABS_HMR_APPLIES__ = [timing];
107
+ } catch {
108
+ // A malformed or inaccessible entry should never affect HMR startup.
109
+ }
110
+ };
111
+
112
+ type SendHmrTimingOptions = {
113
+ clientStart: number;
114
+ kind?: HMRApplyKind;
115
+ outcome?: HMRApplyOutcome;
116
+ serverMs?: number;
117
+ updateId?: number;
118
+ };
119
+
120
+ export const sendAbsoluteHmrTiming = (options: SendHmrTimingOptions) => {
121
+ const clientMs = Math.round(performance.now() - options.clientStart);
122
+ const serverMs = options.serverMs ?? 0;
123
+ const timing = {
124
+ clientMs,
125
+ duration: serverMs + clientMs,
126
+ kind: options.kind,
127
+ outcome: options.outcome ?? 'applied',
128
+ serverMs,
129
+ target: absoluteHmrClientTarget(),
130
+ updateId: options.updateId
131
+ } satisfies NonNullable<Window['__ABS_HMR_LAST_APPLY__']>;
132
+ window.__ABS_HMR_LAST_APPLY__ = timing;
133
+ const applies = (window.__ABS_HMR_APPLIES__ ??= []);
134
+ applies.push(timing);
135
+ if (applies.length > MAX_RETAINED_HMR_APPLIES) {
136
+ applies.splice(0, applies.length - MAX_RETAINED_HMR_APPLIES);
137
+ }
138
+ persistHmrApply(timing);
139
+ if (!window.__HMR_WS__) return;
140
+ window.__HMR_WS__.send(JSON.stringify({ ...timing, type: 'hmr-timing' }));
141
+ };