@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
@@ -0,0 +1,3058 @@
1
+ // @bun
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
12
+ var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
20
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
21
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
22
+ for (let key of __getOwnPropNames(mod))
23
+ if (!__hasOwnProp.call(to, key))
24
+ __defProp(to, key, {
25
+ get: __accessProp.bind(mod, key),
26
+ enumerable: true
27
+ });
28
+ if (canCache)
29
+ cache.set(mod, to);
30
+ return to;
31
+ };
32
+ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
33
+ var __returnValue = (v) => v;
34
+ function __exportSetter(name, newValue) {
35
+ this[name] = __returnValue.bind(null, newValue);
36
+ }
37
+ var __export = (target, all) => {
38
+ for (var name in all)
39
+ __defProp(target, name, {
40
+ get: all[name],
41
+ enumerable: true,
42
+ configurable: true,
43
+ set: __exportSetter.bind(all, name)
44
+ });
45
+ };
46
+ var __legacyDecorateClassTS = function(decorators, target, key, desc) {
47
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
48
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
49
+ r = Reflect.decorate(decorators, target, key, desc);
50
+ else
51
+ for (var i = decorators.length - 1;i >= 0; i--)
52
+ if (d = decorators[i])
53
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
54
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
55
+ };
56
+ var __legacyMetadataTS = (k, v) => {
57
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function")
58
+ return Reflect.metadata(k, v);
59
+ };
60
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
61
+ var __require = import.meta.require;
62
+
63
+ // src/constants.ts
64
+ var ANGULAR_INIT_TIMEOUT_MS = 500, ANSI_ESCAPE_CODE = 27, ANSI_ESCAPE_LENGTH = 3, ASCII_SPACE = 32, BASE_36_RADIX = 36, BUN_BUILD_WARNING_SUPPRESSION = "wildcard sideEffects are not supported yet", TAILWIND_BUN_CSS_WARNING_PATTERN, BODY_SLICE_LENGTH = 2000, BYTES_PER_KILOBYTE = 1024, CLI_ARGS_OFFSET = 3, CSS_ERROR_RESOLVE_DELAY_MS = 50, CSS_MAX_CHECK_ATTEMPTS = 10, CSS_MAX_PARSE_TIMEOUT_MS = 500, CSS_SHEET_READY_TIMEOUT_MS = 100, DEFAULT_CHUNK_SIZE = 16384, DEFAULT_DEBOUNCE_MS = 15, DEFAULT_PORT = 3000, DEV_SERVER_RESTART_DEBOUNCE_MS = 100, DOM_UPDATE_DELAY_MS = 50, FILE_PROTOCOL_PREFIX_LENGTH = 7, FOCUS_ID_PREFIX_LENGTH = 3, FOCUS_IDX_PREFIX_LENGTH = 4, FOCUS_NAME_PREFIX_LENGTH = 5, ESLINT_STUDIO_DEFAULT_HOST = "eslint.absolute.localhost", ESLINT_STUDIO_DEFAULT_PORT = 4099, CONFIG_DEFAULT_HOST = "config.absolute.localhost", CONFIG_DEFAULT_PORT = 4099, HMR_UPDATE_TIMEOUT_MS = 2000, HOOK_SIGNATURE_LENGTH = 12, EXCLUDE_LAST_OFFSET = -1, HTTP_STATUS_OK = 200, HTTP_STATUS_BAD_REQUEST = 400, HTTP_STATUS_NOT_FOUND = 404, HOURS_IN_DAY = 24, HOURS_IN_HALF_DAY = 12, IMAGE_DEFAULT_DEVICE_SIZES, IMAGE_DEFAULT_IMAGE_SIZES, IMAGE_DEFAULT_QUALITY = 75, IMAGE_GLOB_SUFFIX_LENGTH = 2, INSTANCE_PROBE_TIMEOUT_MS = 250, MAX_ERROR_LENGTH = 200, MAX_RECONNECT_ATTEMPTS = 60, MILLISECONDS_IN_A_SECOND = 1000, MINUTES_IN_AN_HOUR = 60, SECONDS_IN_A_MINUTE = 60, MILLISECONDS_IN_A_MINUTE, MILLISECONDS_IN_A_DAY, OVERLAY_FADE_DURATION_MS = 150, PING_INTERVAL_MS = 30000, LIST_LOG_TAIL_MAX_BYTES = 65536, LIST_TUI_COLUMN_GAP = 2, LIST_TUI_DEFAULT_HEIGHT = 28, LIST_TUI_DEFAULT_WIDTH = 100, LIST_TUI_ESCAPE_SEQUENCE_TIMEOUT_MS = 30, LIST_TUI_FOOTER_LINE_COUNT = 2, LIST_TUI_MARKER_WIDTH = 2, LIST_TUI_MIN_LOG_HEIGHT = 3, LIST_TUI_MIN_URL_WIDTH = 16, LIST_TUI_RENDER_DEBOUNCE_MS = 16, LIST_TUI_STATUS_MESSAGE_TIMEOUT_MS = 4000, LIST_WATCH_REFRESH_MS = 1000, RAF_BATCH_COUNT = 3, RANDOM_ID_END_INDEX = 11, REBUILD_BATCH_DELAY_MS = 10, REBUILD_RELOAD_DELAY_MS = 200, RECONNECT_INITIAL_DELAY_MS = 500, RECONNECT_POLL_INTERVAL_MS = 300, REACT_STREAM_SLOT_FAST_DELAY_MS = 5, REACT_STREAM_SLOT_SLOW_DELAY_MS = 20, SIGINT_EXIT_CODE = 130, SIGTERM_EXIT_CODE = 143, SVELTE_CSS_LOAD_TIMEOUT_MS = 500, TIME_PRECISION = 2, TWO_THIRDS, UNFOUND_INDEX = -1, WEBSOCKET_NORMAL_CLOSURE = 1000, DEFAULT_WEBSOCKET_IDLE_TIMEOUT_SECONDS = 240, DEFAULT_HTTP_IDLE_TIMEOUT_SECONDS = 240, MAX_HTTP_IDLE_TIMEOUT_SECONDS = 255, WORKSPACE_COMMAND_ARGS_OFFSET = 3, WORKSPACE_FAILURE_LOG_PRINT_LIMIT = 30, WORKSPACE_FAILURE_RECENT_LOG_LIMIT = 60, WORKSPACE_READY_ATTEMPT_TIMEOUT_MS = 5000, WORKSPACE_READY_PROBE_INTERVAL_MS = 250, WORKSPACE_READY_TIMEOUT_MS = 30000, WORKSPACE_SHUTDOWN_TIMEOUT_MS = 1e4, WORKSPACE_TUI_DEFAULT_HEIGHT = 28, WORKSPACE_TUI_DEFAULT_WIDTH = 100, WORKSPACE_TUI_ESCAPE_SEQUENCE_TIMEOUT_MS = 30, WORKSPACE_TUI_FOOTER_LINE_COUNT = 3, WORKSPACE_TUI_MIN_LOG_HEIGHT = 3, WORKSPACE_TUI_MIN_SERVICE_NAME_WIDTH = 7, WORKSPACE_TUI_MIN_TARGET_WIDTH = 8, WORKSPACE_TUI_MIN_WRAP_WIDTH = 12, WORKSPACE_TUI_PROMPT_CURSOR_OFFSET = 3, WORKSPACE_TUI_RECENT_LOG_LIMIT = 40, WORKSPACE_TUI_RENDER_DEBOUNCE_MS = 16, WORKSPACE_TUI_STATUS_WIDTH = 10, WORKSPACE_TUI_TARGET_PADDING_WIDTH = 6, WORKSPACE_TUI_VISIBILITY_WIDTH = 8;
65
+ var init_constants = __esm(() => {
66
+ TAILWIND_BUN_CSS_WARNING_PATTERN = /invalid @ rule encountered: '@(theme|tailwind|source|utility|variant|custom-variant|apply|reference|plugin|config)'/;
67
+ IMAGE_DEFAULT_DEVICE_SIZES = [
68
+ 640,
69
+ 750,
70
+ 828,
71
+ 1080,
72
+ 1200,
73
+ 1920,
74
+ 2048,
75
+ 3840
76
+ ];
77
+ IMAGE_DEFAULT_IMAGE_SIZES = [16, 32, 48, 64, 96, 128, 256, 384];
78
+ MILLISECONDS_IN_A_MINUTE = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE;
79
+ MILLISECONDS_IN_A_DAY = MILLISECONDS_IN_A_SECOND * SECONDS_IN_A_MINUTE * MINUTES_IN_AN_HOUR * HOURS_IN_DAY;
80
+ TWO_THIRDS = 2 / 3;
81
+ });
82
+
83
+ // src/utils/getDurationString.ts
84
+ var getDurationString = (duration) => {
85
+ let durationString;
86
+ if (duration < MILLISECONDS_IN_A_SECOND) {
87
+ durationString = `${duration.toFixed(TIME_PRECISION)}ms`;
88
+ } else if (duration < MILLISECONDS_IN_A_MINUTE) {
89
+ durationString = `${(duration / MILLISECONDS_IN_A_SECOND).toFixed(TIME_PRECISION)}s`;
90
+ } else {
91
+ const totalSeconds = Math.round(duration / MILLISECONDS_IN_A_SECOND);
92
+ const minutes = Math.floor(totalSeconds / 60);
93
+ const seconds = totalSeconds % 60;
94
+ durationString = seconds === 0 ? `${minutes}m` : `${minutes}m ${seconds}s`;
95
+ }
96
+ return durationString;
97
+ };
98
+ var init_getDurationString = __esm(() => {
99
+ init_constants();
100
+ });
101
+
102
+ // src/utils/startupBanner.ts
103
+ var colors, MONTHS, formatTimestamp = () => {
104
+ const now = new Date;
105
+ const month = MONTHS[now.getMonth()];
106
+ const day = now.getDate().toString().padStart(2, "0");
107
+ let hours = now.getHours();
108
+ const minutes = now.getMinutes().toString().padStart(2, "0");
109
+ const seconds = now.getSeconds().toString().padStart(2, "0");
110
+ const ampm = hours >= HOURS_IN_HALF_DAY ? "PM" : "AM";
111
+ hours = hours % HOURS_IN_HALF_DAY || HOURS_IN_HALF_DAY;
112
+ return `${month} ${day} ${hours}:${minutes}:${seconds} ${ampm}`;
113
+ }, startupBanner = (options) => {
114
+ const {
115
+ version,
116
+ readyDuration,
117
+ buildDuration,
118
+ port,
119
+ host,
120
+ networkUrl,
121
+ protocol = "http"
122
+ } = options;
123
+ const name = `${colors.cyan}${colors.bold}ABSOLUTEJS${colors.reset}`;
124
+ const ver = `${colors.dim}v${version}${colors.reset}`;
125
+ const time = `${colors.dim}ready in${colors.reset} ${colors.bold}${getDurationString(readyDuration)}${colors.reset}`;
126
+ const build = typeof buildDuration === "number" && Number.isFinite(buildDuration) ? ` ${colors.dim}(build ${getDurationString(buildDuration)})${colors.reset}` : "";
127
+ console.log("");
128
+ console.log(` ${name} ${ver} ${time}${build}`);
129
+ console.log("");
130
+ console.log(` ${colors.green}\u279C${colors.reset} ${colors.bold}Local:${colors.reset} ${protocol}://${host === "0.0.0.0" ? "localhost" : host}:${port}/`);
131
+ if (networkUrl) {
132
+ console.log(` ${colors.green}\u279C${colors.reset} ${colors.bold}Network:${colors.reset} ${networkUrl}`);
133
+ }
134
+ console.log("");
135
+ };
136
+ var init_startupBanner = __esm(() => {
137
+ init_constants();
138
+ init_getDurationString();
139
+ colors = {
140
+ bold: "\x1B[1m",
141
+ cyan: "\x1B[36m",
142
+ dim: "\x1B[2m",
143
+ green: "\x1B[32m",
144
+ reset: "\x1B[0m"
145
+ };
146
+ MONTHS = [
147
+ "Jan",
148
+ "Feb",
149
+ "Mar",
150
+ "Apr",
151
+ "May",
152
+ "Jun",
153
+ "Jul",
154
+ "Aug",
155
+ "Sep",
156
+ "Oct",
157
+ "Nov",
158
+ "Dec"
159
+ ];
160
+ });
161
+
162
+ // src/mobile/artifactStore.ts
163
+ import { createHash as createHash2 } from "crypto";
164
+ import {
165
+ mkdir,
166
+ mkdtemp,
167
+ readFile,
168
+ readdir,
169
+ rename,
170
+ rm,
171
+ writeFile
172
+ } from "fs/promises";
173
+ import { join, resolve as resolvePath } from "path";
174
+
175
+ // src/mobile/releaseArtifact.ts
176
+ import { createHash } from "crypto";
177
+ var ABSOLUTE_MOBILE_COMPATIBILITY_FORMAT = 1;
178
+ var ABSOLUTE_MOBILE_RETAINED_GENERATIONS = 3;
179
+ var SHA_256 = "sha256";
180
+ var RELEASE_ID_PREFIX = "amc_";
181
+ var MODULE_SEGMENT_PARENT = "..";
182
+ var EXPORT_NAME_PATTERN = /^[A-Z_a-z]\w*$/;
183
+ var frameworks = new Set([
184
+ "angular",
185
+ "ember",
186
+ "html",
187
+ "htmx",
188
+ "react",
189
+ "svelte",
190
+ "vue"
191
+ ]);
192
+ var isCanonicalRecord = (value) => {
193
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
194
+ return false;
195
+ }
196
+ const prototype = Object.getPrototypeOf(value);
197
+ return prototype === Object.prototype || prototype === null;
198
+ };
199
+ var enterCanonicalValue = (value, ancestors) => {
200
+ if (ancestors.has(value)) {
201
+ throw new TypeError("Compatibility metadata cannot contain cycles.");
202
+ }
203
+ ancestors.add(value);
204
+ };
205
+ var normalizeCanonicalArray = (value, ancestors) => {
206
+ enterCanonicalValue(value, ancestors);
207
+ const normalized = value.map((item) => normalizeCanonicalValue(item, ancestors));
208
+ ancestors.delete(value);
209
+ return normalized;
210
+ };
211
+ var normalizeCanonicalRecord = (value, ancestors) => {
212
+ enterCanonicalValue(value, ancestors);
213
+ const normalized = Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [
214
+ key,
215
+ normalizeCanonicalValue(item, ancestors)
216
+ ]));
217
+ ancestors.delete(value);
218
+ return normalized;
219
+ };
220
+ var normalizeCanonicalValue = (value, ancestors) => {
221
+ if (value === null || typeof value === "string" || typeof value === "boolean") {
222
+ return value;
223
+ }
224
+ if (typeof value === "number" && Number.isFinite(value))
225
+ return value;
226
+ if (Array.isArray(value))
227
+ return normalizeCanonicalArray(value, ancestors);
228
+ if (isCanonicalRecord(value)) {
229
+ return normalizeCanonicalRecord(value, ancestors);
230
+ }
231
+ throw new TypeError("Compatibility metadata must contain only finite JSON values.");
232
+ };
233
+ var canonicalJson = (value) => JSON.stringify(normalizeCanonicalValue(value, new Set));
234
+ var hashCanonicalValue = (value) => createHash(SHA_256).update(canonicalJson(value)).digest("hex");
235
+ var requireNonEmpty = (value, field) => {
236
+ if (!value.trim()) {
237
+ throw new TypeError(`${field} must not be empty.`);
238
+ }
239
+ return value;
240
+ };
241
+ var isPageFramework = (value) => typeof value === "string" && frameworks.has(value);
242
+ var readString = (value, field) => {
243
+ if (typeof value !== "string") {
244
+ throw new TypeError(`${field} must be a string.`);
245
+ }
246
+ return value;
247
+ };
248
+ var parseCompatibilityPage = (value) => {
249
+ if (!isCanonicalRecord(value) || !isPageFramework(value.framework)) {
250
+ throw new TypeError("Compatibility artifact contains an invalid page.");
251
+ }
252
+ return {
253
+ bundleHash: readString(value.bundleHash, "page.bundleHash"),
254
+ bundlePath: readString(value.bundlePath, "page.bundlePath"),
255
+ contract: readString(value.contract, "page.contract"),
256
+ framework: value.framework,
257
+ pageId: readString(value.pageId, "page.pageId"),
258
+ propsSchemaHash: readString(value.propsSchemaHash, "page.propsSchemaHash")
259
+ };
260
+ };
261
+ var parseCompatibilityRoute = (value) => {
262
+ if (!isCanonicalRecord(value) || value.method !== "GET" && value.method !== "HEAD") {
263
+ throw new TypeError("Compatibility artifact contains an invalid route.");
264
+ }
265
+ return {
266
+ method: value.method,
267
+ pageId: readString(value.pageId, "route.pageId"),
268
+ pattern: readString(value.pattern, "route.pattern")
269
+ };
270
+ };
271
+ var parseCompatibilityProducer = (value) => {
272
+ if (!isCanonicalRecord(value) || typeof value.bytes !== "number") {
273
+ throw new TypeError("Compatibility artifact contains an invalid producer.");
274
+ }
275
+ return {
276
+ bundleHash: readString(value.bundleHash, "producer.bundleHash"),
277
+ bytes: value.bytes,
278
+ exportName: readString(value.exportName, "producer.exportName"),
279
+ module: readString(value.module, "producer.module")
280
+ };
281
+ };
282
+ var validateProducerModule = (module) => {
283
+ requireNonEmpty(module, "producer.module");
284
+ const segments = module.split("/");
285
+ if (module.startsWith("/") || module.includes("\\") || segments.includes(MODULE_SEGMENT_PARENT)) {
286
+ throw new TypeError("producer.module must be a safe relative path.");
287
+ }
288
+ return module;
289
+ };
290
+ var normalizePage = (page) => ({
291
+ bundleHash: requireNonEmpty(page.bundleHash, "page.bundleHash"),
292
+ bundlePath: requireNonEmpty(page.bundlePath, "page.bundlePath"),
293
+ contract: requireNonEmpty(page.contract, "page.contract"),
294
+ framework: page.framework,
295
+ pageId: requireNonEmpty(page.pageId, "page.pageId"),
296
+ propsSchemaHash: requireNonEmpty(page.propsSchemaHash, "page.propsSchemaHash")
297
+ });
298
+ var normalizeRoute = (route) => {
299
+ if (!route.pattern.startsWith("/")) {
300
+ throw new TypeError("route.pattern must start with /.");
301
+ }
302
+ return {
303
+ method: route.method,
304
+ pageId: requireNonEmpty(route.pageId, "route.pageId"),
305
+ pattern: route.pattern
306
+ };
307
+ };
308
+ var assertUnique = (values, field) => {
309
+ if (new Set(values).size !== values.length) {
310
+ throw new TypeError(`${field} values must be unique.`);
311
+ }
312
+ };
313
+ var normalizeArtifactContent = (input) => {
314
+ if (!Number.isSafeInteger(input.generation) || input.generation < 1) {
315
+ throw new TypeError("generation must be a positive safe integer.");
316
+ }
317
+ if (!Number.isSafeInteger(input.producer.bytes) || input.producer.bytes < 1) {
318
+ throw new TypeError("producer.bytes must be a positive safe integer.");
319
+ }
320
+ const pages = input.pages.map(normalizePage).sort((left, right) => left.pageId.localeCompare(right.pageId));
321
+ const routes = input.routes.map(normalizeRoute).sort((left, right) => `${left.pattern}:${left.method}`.localeCompare(`${right.pattern}:${right.method}`));
322
+ assertUnique(pages.map(({ pageId }) => pageId), "page.pageId");
323
+ assertUnique(routes.map(({ method, pattern }) => `${method}:${pattern}`), "route method and pattern");
324
+ const pageIds = new Set(pages.map(({ pageId }) => pageId));
325
+ const missingPage = routes.find(({ pageId }) => !pageIds.has(pageId));
326
+ if (missingPage) {
327
+ throw new TypeError(`Route ${missingPage.pattern} references unknown page ${missingPage.pageId}.`);
328
+ }
329
+ if (!EXPORT_NAME_PATTERN.test(input.producer.exportName)) {
330
+ throw new TypeError("producer.exportName must be a JavaScript identifier.");
331
+ }
332
+ return {
333
+ appBuild: requireNonEmpty(input.appBuild, "appBuild"),
334
+ appId: requireNonEmpty(input.appId, "appId"),
335
+ format: ABSOLUTE_MOBILE_COMPATIBILITY_FORMAT,
336
+ generation: input.generation,
337
+ pages,
338
+ producer: {
339
+ bundleHash: requireNonEmpty(input.producer.bundleHash, "producer.bundleHash"),
340
+ bytes: input.producer.bytes,
341
+ exportName: input.producer.exportName,
342
+ module: validateProducerModule(input.producer.module)
343
+ },
344
+ routes,
345
+ runtime: requireNonEmpty(input.runtime, "runtime")
346
+ };
347
+ };
348
+ var createAbsoluteMobileCompatibilityArtifact = (input) => {
349
+ const content = normalizeArtifactContent(input);
350
+ return {
351
+ ...content,
352
+ releaseId: `${RELEASE_ID_PREFIX}${hashCanonicalValue(content)}`
353
+ };
354
+ };
355
+ var hashAbsoluteMobilePropsSchema = (schema) => hashCanonicalValue(schema);
356
+ var parseAbsoluteMobileCompatibilityArtifact = (value) => {
357
+ if (!isCanonicalRecord(value) || value.format !== ABSOLUTE_MOBILE_COMPATIBILITY_FORMAT || typeof value.generation !== "number" || !Array.isArray(value.pages) || !Array.isArray(value.routes)) {
358
+ throw new TypeError("Invalid mobile compatibility artifact.");
359
+ }
360
+ const releaseId = readString(value.releaseId, "releaseId");
361
+ const artifact = createAbsoluteMobileCompatibilityArtifact({
362
+ appBuild: readString(value.appBuild, "appBuild"),
363
+ appId: readString(value.appId, "appId"),
364
+ generation: value.generation,
365
+ pages: value.pages.map(parseCompatibilityPage),
366
+ producer: parseCompatibilityProducer(value.producer),
367
+ routes: value.routes.map(parseCompatibilityRoute),
368
+ runtime: readString(value.runtime, "runtime")
369
+ });
370
+ if (artifact.releaseId !== releaseId) {
371
+ throw new TypeError("Mobile compatibility artifact integrity failed.");
372
+ }
373
+ return artifact;
374
+ };
375
+ var resolveAbsoluteMobileCompatibilityRelease = (client, artifacts) => {
376
+ const artifact = artifacts.find((candidate) => candidate.appBuild === client.appBuild && candidate.runtime === client.runtime);
377
+ if (!artifact) {
378
+ return {
379
+ kind: "upgrade-required",
380
+ result: {
381
+ kind: "upgrade-required",
382
+ pageId: client.pageId,
383
+ reason: "app-release"
384
+ }
385
+ };
386
+ }
387
+ const page = artifact.pages.find((candidate) => candidate.pageId === client.pageId && candidate.bundleHash === client.pageBundle && client.pageContracts.includes(candidate.contract));
388
+ if (!page) {
389
+ return {
390
+ kind: "upgrade-required",
391
+ result: {
392
+ kind: "upgrade-required",
393
+ pageId: client.pageId,
394
+ reason: "page-contract",
395
+ supportedContracts: artifact.pages.filter(({ pageId }) => pageId === client.pageId).map(({ contract }) => contract)
396
+ }
397
+ };
398
+ }
399
+ return { artifact, kind: "retained", page };
400
+ };
401
+ var retainAbsoluteMobileCompatibilityArtifacts = (artifacts) => {
402
+ const appIds = new Set(artifacts.map(({ appId }) => appId));
403
+ if (appIds.size > 1) {
404
+ throw new TypeError("Cannot retain compatibility artifacts for multiple apps.");
405
+ }
406
+ assertUnique(artifacts.map(({ appBuild }) => appBuild), "appBuild");
407
+ assertUnique(artifacts.map(({ generation }) => String(generation)), "generation");
408
+ return [...artifacts].sort((left, right) => right.generation - left.generation).slice(0, ABSOLUTE_MOBILE_RETAINED_GENERATIONS);
409
+ };
410
+
411
+ // src/mobile/artifactStore.ts
412
+ var DEFAULT_MAX_PRODUCER_BYTES = 134217728;
413
+ var SHA_2562 = "sha256";
414
+ var ARTIFACT_FILE = "artifact.json";
415
+ var RELEASE_ID_PATTERN = /^amc_[a-f0-9]{64}$/;
416
+ var DEFAULT_BLOB_PREFIX = "absolutejs/mobile-compatibility";
417
+ var hashBlob = async (blob) => {
418
+ const bytes = new Uint8Array(await blob.arrayBuffer());
419
+ return createHash2(SHA_2562).update(bytes).digest("hex");
420
+ };
421
+ var blobFromBytes = (bytes) => {
422
+ const buffer = new ArrayBuffer(bytes.byteLength);
423
+ new Uint8Array(buffer).set(bytes);
424
+ return new Blob([buffer]);
425
+ };
426
+ var errorHasCode = (error, code) => typeof error === "object" && error !== null && Reflect.get(error, "code") === code;
427
+ var appDirectoryName = (appId) => createHash2(SHA_2562).update(appId).digest("hex");
428
+ var requireReleaseId = (releaseId) => {
429
+ if (!RELEASE_ID_PATTERN.test(releaseId)) {
430
+ throw new TypeError("Invalid mobile compatibility release id.");
431
+ }
432
+ return releaseId;
433
+ };
434
+ var readStoredArtifact = async (releaseDirectory) => {
435
+ const serialized = await readFile(join(releaseDirectory, ARTIFACT_FILE), "utf8");
436
+ const parsed = JSON.parse(serialized);
437
+ return parseAbsoluteMobileCompatibilityArtifact(parsed);
438
+ };
439
+ var validateStoredIdentity = (artifact, appId, releaseId) => {
440
+ if (artifact.appId !== appId || artifact.releaseId !== releaseId) {
441
+ throw new TypeError("Stored mobile compatibility identity does not match.");
442
+ }
443
+ return artifact;
444
+ };
445
+ var listBlobKeys = async (store, prefix, cursor, keys = []) => {
446
+ const result = await store.list({ cursor, prefix });
447
+ const collected = [...keys, ...result.objects.map(({ key }) => key)];
448
+ if (!result.truncated)
449
+ return collected;
450
+ if (!result.cursor) {
451
+ throw new TypeError("Truncated blob listing did not provide a cursor.");
452
+ }
453
+ return listBlobKeys(store, prefix, result.cursor, collected);
454
+ };
455
+ var normalizeBlobPrefix = (prefix) => {
456
+ const normalized = prefix.replace(/^\/+|\/+$/g, "");
457
+ if (!normalized || normalized.split("/").includes("..")) {
458
+ throw new TypeError("Mobile compatibility blob prefix is invalid.");
459
+ }
460
+ return normalized;
461
+ };
462
+ var carryForwardAbsoluteMobileCompatibilityReleases = async (input) => {
463
+ await verifyAbsoluteMobileCompatibilityProducer(input.current);
464
+ await input.store.write(input.current);
465
+ const available = await input.store.list(input.current.artifact.appId);
466
+ const retained = retainAbsoluteMobileCompatibilityArtifacts(available);
467
+ if (retained[0]?.releaseId !== input.current.artifact.releaseId) {
468
+ throw new TypeError("Current mobile release generation must be newer than stored history.");
469
+ }
470
+ const releases = await Promise.all(retained.map(({ releaseId }) => input.store.read(input.current.artifact.appId, releaseId)));
471
+ return Promise.all(releases.map((release) => {
472
+ if (!release) {
473
+ throw new TypeError("Compatibility store listed a release it could not read.");
474
+ }
475
+ return verifyAbsoluteMobileCompatibilityProducer(release);
476
+ }));
477
+ };
478
+ var createAbsoluteMobileBlobArtifactStore = (options) => {
479
+ const prefix = normalizeBlobPrefix(options.prefix ?? DEFAULT_BLOB_PREFIX);
480
+ const maxProducerBytes = options.maxProducerBytes ?? DEFAULT_MAX_PRODUCER_BYTES;
481
+ const appPrefix = (appId) => `${prefix}/${appDirectoryName(appId)}/releases`;
482
+ const releasePrefix = (appId, releaseId) => `${appPrefix(appId)}/${requireReleaseId(releaseId)}`;
483
+ const artifactKey = (appId, releaseId) => `${releasePrefix(appId, releaseId)}/${ARTIFACT_FILE}`;
484
+ const producerKey = (artifact) => `${releasePrefix(artifact.appId, artifact.releaseId)}/${artifact.producer.module}`;
485
+ const readArtifact = async (appId, releaseId) => {
486
+ const bytes = await options.store.get(artifactKey(appId, releaseId));
487
+ if (!bytes)
488
+ return null;
489
+ const parsed = JSON.parse(new TextDecoder().decode(bytes));
490
+ return validateStoredIdentity(parseAbsoluteMobileCompatibilityArtifact(parsed), appId, releaseId);
491
+ };
492
+ return {
493
+ delete: async (appId, releaseId) => {
494
+ const artifact = await readArtifact(appId, releaseId);
495
+ if (artifact)
496
+ await options.store.delete(producerKey(artifact));
497
+ await options.store.delete(artifactKey(appId, releaseId));
498
+ },
499
+ list: async (appId) => {
500
+ const releaseRoot = `${appPrefix(appId)}/`;
501
+ const keys = await listBlobKeys(options.store, releaseRoot);
502
+ const releaseIds = keys.filter((key) => key.endsWith(`/${ARTIFACT_FILE}`)).map((key) => key.slice(releaseRoot.length).split("/")[0]).filter((releaseId) => typeof releaseId === "string" && RELEASE_ID_PATTERN.test(releaseId));
503
+ const artifacts = await Promise.all([...new Set(releaseIds)].map((releaseId) => readArtifact(appId, releaseId)));
504
+ return artifacts.filter((artifact) => artifact !== null).sort((left, right) => right.generation - left.generation);
505
+ },
506
+ read: async (appId, releaseId) => {
507
+ const artifact = await readArtifact(appId, releaseId);
508
+ if (!artifact)
509
+ return null;
510
+ const bytes = await options.store.get(producerKey(artifact));
511
+ if (!bytes) {
512
+ throw new TypeError("Mobile compatibility artifact is missing its producer.");
513
+ }
514
+ return { artifact, producer: blobFromBytes(bytes) };
515
+ },
516
+ write: async (release) => {
517
+ const artifact = parseAbsoluteMobileCompatibilityArtifact(release.artifact);
518
+ const validated = await verifyAbsoluteMobileCompatibilityProducer({ artifact, producer: release.producer }, maxProducerBytes);
519
+ await options.store.put(producerKey(validated.artifact), validated.producer.stream(), {
520
+ contentType: "text/javascript; charset=utf-8",
521
+ maxBytes: maxProducerBytes,
522
+ metadata: {
523
+ appId: validated.artifact.appId,
524
+ releaseId: validated.artifact.releaseId,
525
+ sha256: validated.artifact.producer.bundleHash
526
+ }
527
+ });
528
+ await options.store.put(artifactKey(validated.artifact.appId, validated.artifact.releaseId), `${JSON.stringify(validated.artifact, null, "\t")}
529
+ `, { contentType: "application/json" });
530
+ }
531
+ };
532
+ };
533
+ var createAbsoluteMobileFileArtifactStore = (options) => {
534
+ const root = resolvePath(options.root);
535
+ const maxProducerBytes = options.maxProducerBytes ?? DEFAULT_MAX_PRODUCER_BYTES;
536
+ const appDirectory = (appId) => join(root, appDirectoryName(appId));
537
+ const releaseDirectory = (appId, releaseId) => join(appDirectory(appId), requireReleaseId(releaseId));
538
+ const acceptExistingRelease = async (error, artifact) => {
539
+ if (!errorHasCode(error, "EEXIST") && !errorHasCode(error, "ENOTEMPTY")) {
540
+ return false;
541
+ }
542
+ const existing = await readStoredArtifact(releaseDirectory(artifact.appId, artifact.releaseId));
543
+ return existing.releaseId === artifact.releaseId;
544
+ };
545
+ return {
546
+ delete: async (appId, releaseId) => {
547
+ await rm(releaseDirectory(appId, releaseId), {
548
+ force: true,
549
+ recursive: true
550
+ });
551
+ },
552
+ list: async (appId) => {
553
+ let entries;
554
+ try {
555
+ entries = await readdir(appDirectory(appId), {
556
+ withFileTypes: true
557
+ });
558
+ } catch (error) {
559
+ if (errorHasCode(error, "ENOENT"))
560
+ return [];
561
+ throw error;
562
+ }
563
+ const artifacts = await Promise.all(entries.filter((entry) => entry.isDirectory() && RELEASE_ID_PATTERN.test(entry.name)).map(async ({ name }) => validateStoredIdentity(await readStoredArtifact(releaseDirectory(appId, name)), appId, name)));
564
+ return artifacts.sort((left, right) => right.generation - left.generation);
565
+ },
566
+ read: async (appId, releaseId) => {
567
+ const directory = releaseDirectory(appId, releaseId);
568
+ let artifact;
569
+ try {
570
+ artifact = validateStoredIdentity(await readStoredArtifact(directory), appId, releaseId);
571
+ } catch (error) {
572
+ if (errorHasCode(error, "ENOENT"))
573
+ return null;
574
+ throw error;
575
+ }
576
+ return {
577
+ artifact,
578
+ producer: Bun.file(join(directory, artifact.producer.module))
579
+ };
580
+ },
581
+ write: async (release) => {
582
+ const validatedArtifact = parseAbsoluteMobileCompatibilityArtifact(release.artifact);
583
+ const validated = await verifyAbsoluteMobileCompatibilityProducer({ artifact: validatedArtifact, producer: release.producer }, maxProducerBytes);
584
+ const parent = appDirectory(validated.artifact.appId);
585
+ await mkdir(parent, { recursive: true });
586
+ const staging = await mkdtemp(join(parent, ".stage-"));
587
+ const producerPath = join(staging, validated.artifact.producer.module);
588
+ try {
589
+ await mkdir(resolvePath(producerPath, ".."), {
590
+ recursive: true
591
+ });
592
+ await Promise.all([
593
+ writeFile(join(staging, ARTIFACT_FILE), `${JSON.stringify(validated.artifact, null, "\t")}
594
+ `),
595
+ writeFile(producerPath, new Uint8Array(await validated.producer.arrayBuffer()))
596
+ ]);
597
+ await rename(staging, releaseDirectory(validated.artifact.appId, validated.artifact.releaseId));
598
+ } catch (error) {
599
+ await rm(staging, { force: true, recursive: true });
600
+ if (await acceptExistingRelease(error, validated.artifact))
601
+ return;
602
+ throw error;
603
+ }
604
+ }
605
+ };
606
+ };
607
+ var verifyAbsoluteMobileCompatibilityProducer = async (release, maxProducerBytes = DEFAULT_MAX_PRODUCER_BYTES) => {
608
+ const { artifact, producer } = release;
609
+ if (producer.size !== artifact.producer.bytes || producer.size > maxProducerBytes) {
610
+ throw new TypeError("Mobile compatibility producer size does not match its artifact.");
611
+ }
612
+ if (await hashBlob(producer) !== artifact.producer.bundleHash) {
613
+ throw new TypeError("Mobile compatibility producer hash does not match its artifact.");
614
+ }
615
+ return release;
616
+ };
617
+ // src/mobile/androidRelease.ts
618
+ import { createHash as createHash3 } from "crypto";
619
+ import {
620
+ access as access3,
621
+ copyFile as copyFile2,
622
+ mkdir as mkdir3,
623
+ mkdtemp as mkdtemp2,
624
+ readFile as readFile4,
625
+ rename as rename4,
626
+ rm as rm3,
627
+ stat,
628
+ writeFile as writeFile4
629
+ } from "fs/promises";
630
+ import { dirname as dirname2, isAbsolute as isAbsolute2, join as join4, relative as relative3, resolve as resolve3, sep as sep2 } from "path";
631
+
632
+ // src/mobile/emulatorDoctor.ts
633
+ import { homedir } from "os";
634
+ import { join as join2 } from "path";
635
+
636
+ // src/cli/utils.ts
637
+ import { existsSync, readFileSync } from "fs";
638
+ init_constants();
639
+ init_startupBanner();
640
+ var isWSLEnvironment = () => {
641
+ try {
642
+ const release = readFileSync("/proc/version", "utf-8");
643
+ return /microsoft|wsl/i.test(release);
644
+ } catch {
645
+ return false;
646
+ }
647
+ };
648
+
649
+ // src/mobile/emulatorDoctor.ts
650
+ var windowsPathToWsl = (path) => {
651
+ const match = /^([a-z]):[\\/](.*)$/i.exec(path.trim());
652
+ if (!match)
653
+ return path.trim().replaceAll("\\", "/");
654
+ return `/mnt/${match[1]?.toLowerCase()}/${match[2]?.replaceAll("\\", "/")}`;
655
+ };
656
+ var windowsLocalAppDataFromWsl = () => {
657
+ try {
658
+ const result = Bun.spawnSync(["cmd.exe", "/d", "/c", "echo", "%LOCALAPPDATA%"], { stderr: "ignore", stdout: "pipe" });
659
+ if (result.exitCode !== 0)
660
+ return;
661
+ const output = result.stdout.toString().trim();
662
+ if (!output || output === "%LOCALAPPDATA%")
663
+ return;
664
+ return windowsPathToWsl(output);
665
+ } catch {
666
+ return;
667
+ }
668
+ };
669
+ var absoluteManagedAndroidSdkRoot = (host, env = process.env) => {
670
+ if (host === "windows") {
671
+ return join2(env.LOCALAPPDATA ?? join2(homedir(), "AppData", "Local"), "AbsoluteJS", "Android", "Sdk");
672
+ }
673
+ if (host === "wsl") {
674
+ const localAppData = windowsLocalAppDataFromWsl();
675
+ if (localAppData) {
676
+ return join2(localAppData, "AbsoluteJS", "Android", "Sdk");
677
+ }
678
+ }
679
+ return join2(homedir(), ".absolutejs", "android-sdk");
680
+ };
681
+ var detectAbsoluteMobileHost = (platform = process.platform, wsl = isWSLEnvironment()) => {
682
+ if (platform === "darwin")
683
+ return "macos";
684
+ if (platform === "win32")
685
+ return "windows";
686
+ if (platform === "linux" && wsl)
687
+ return "wsl";
688
+ return "linux";
689
+ };
690
+
691
+ // src/mobile/androidEmulatorController.ts
692
+ import {
693
+ access as access2,
694
+ copyFile,
695
+ lstat,
696
+ mkdir as mkdir2,
697
+ readFile as readFile3,
698
+ readdir as readdir2,
699
+ readlink,
700
+ realpath,
701
+ rename as rename3,
702
+ rm as rm2,
703
+ writeFile as writeFile3
704
+ } from "fs/promises";
705
+ import {
706
+ dirname,
707
+ isAbsolute,
708
+ join as join3,
709
+ relative as relative2,
710
+ resolve as resolve2,
711
+ sep,
712
+ win32
713
+ } from "path";
714
+
715
+ // src/mobile/capacitorProject.ts
716
+ import { access, readFile as readFile2, rename as rename2, writeFile as writeFile2 } from "fs/promises";
717
+ import { relative, resolve } from "path";
718
+ var CONFIG_FILE = "capacitor.config.ts";
719
+ var portableRelative = (root, path) => relative(root, path).replaceAll("\\", "/");
720
+ var capacitorConfigSource = (config, projectRoot) => `import type { CapacitorConfig } from '@capacitor/cli';
721
+
722
+ const config: CapacitorConfig = {
723
+ appId: ${JSON.stringify(config.appId)},
724
+ appName: ${JSON.stringify(config.appName)},
725
+ webDir: ${JSON.stringify(portableRelative(projectRoot, config.bundleDirectory))},
726
+ android: {
727
+ path: ${JSON.stringify(portableRelative(projectRoot, `${config.nativeProjectDirectory}/android`))}
728
+ },
729
+ ios: {
730
+ path: ${JSON.stringify(portableRelative(projectRoot, `${config.nativeProjectDirectory}/ios`))}
731
+ }
732
+ };
733
+
734
+ export default config;
735
+ `;
736
+ var exists = async (path) => {
737
+ try {
738
+ await access(path);
739
+ return true;
740
+ } catch {
741
+ return false;
742
+ }
743
+ };
744
+ var writeAbsoluteCapacitorConfig = async (config, options) => {
745
+ const projectRoot = resolve(options.projectRoot);
746
+ const destination = resolve(projectRoot, CONFIG_FILE);
747
+ const source = capacitorConfigSource(config, projectRoot);
748
+ if (await exists(destination)) {
749
+ const current = await readFile2(destination, "utf8");
750
+ if (current === source)
751
+ return { changed: false, path: destination };
752
+ if (!options.force) {
753
+ throw new TypeError(`${CONFIG_FILE} already exists and differs; rerun with --force only after reviewing it.`);
754
+ }
755
+ }
756
+ const temporary = `${destination}.${crypto.randomUUID()}.tmp`;
757
+ await writeFile2(temporary, source, { flag: "wx" });
758
+ await rename2(temporary, destination);
759
+ return { changed: true, path: destination };
760
+ };
761
+
762
+ // src/mobile/androidEmulatorController.ts
763
+ init_getDurationString();
764
+ var HASH_RADIX = 16;
765
+ var CAPACITOR_PROJECT_DIRECTORY_PATTERN = /project\(['"](:[^'"]+)['"]\)\.projectDir\s*=\s*new File\(['"]([^'"]+)['"]\)/gu;
766
+ var pathExists = async (path) => {
767
+ try {
768
+ await access2(path);
769
+ return true;
770
+ } catch {
771
+ return false;
772
+ }
773
+ };
774
+ var forwardCommandOutput = (stream, destination) => stream.pipeTo(new WritableStream({
775
+ write: (chunk) => {
776
+ destination.write(chunk);
777
+ }
778
+ }));
779
+ var runCommand = async (command, options = {}) => {
780
+ const subprocess = Bun.spawn(command, {
781
+ cwd: options.cwd,
782
+ env: options.env,
783
+ signal: options.signal,
784
+ stderr: "pipe",
785
+ stdin: "ignore",
786
+ stdout: "pipe"
787
+ });
788
+ const [exitCode] = await Promise.all([
789
+ subprocess.exited,
790
+ forwardCommandOutput(subprocess.stdout, process.stdout),
791
+ forwardCommandOutput(subprocess.stderr, process.stderr)
792
+ ]);
793
+ return exitCode;
794
+ };
795
+ var captureCommand = (command, options = {}) => {
796
+ try {
797
+ const result = Bun.spawnSync(command, {
798
+ cwd: options.cwd,
799
+ env: options.env,
800
+ stderr: "pipe",
801
+ stdout: "pipe"
802
+ });
803
+ return {
804
+ exitCode: result.exitCode,
805
+ stderr: result.stderr.toString(),
806
+ stdout: result.stdout.toString()
807
+ };
808
+ } catch (error) {
809
+ return {
810
+ exitCode: 1,
811
+ stderr: error instanceof Error ? error.message : String(error),
812
+ stdout: ""
813
+ };
814
+ }
815
+ };
816
+ var ASCII_ESCAPE = 27;
817
+ var ANSI_SEQUENCE_PATTERN = new RegExp(`${String.fromCharCode(ASCII_ESCAPE)}(?:\\[[0-?]*[ -/]*[@-~]|[@-_])`, "gu");
818
+ var requireSuccess = async (command, label, run, options) => {
819
+ const exitCode = await run(command, options);
820
+ throwIfAborted(options?.signal);
821
+ if (exitCode !== 0)
822
+ throw new Error(`${label} failed (exit ${exitCode}).`);
823
+ };
824
+ var throwIfAborted = (signal) => {
825
+ if (!signal?.aborted)
826
+ return;
827
+ throw new DOMException("Android development startup was cancelled.", "AbortError");
828
+ };
829
+ var windowsPathFromWsl = (path, capture) => {
830
+ const result = capture(["wslpath", "-w", path]);
831
+ if (result.exitCode !== 0 || !result.stdout.trim()) {
832
+ throw new Error(`Could not translate WSL path for Windows: ${path}`);
833
+ }
834
+ return result.stdout.trim();
835
+ };
836
+ var mirroredCapacitorDependencies = async (project, capture) => {
837
+ const settingsPath = join3(project.nativeDirectory, "capacitor.settings.gradle");
838
+ const settings = await readFile3(settingsPath, "utf8");
839
+ const dependencies = [];
840
+ const rewrittenSettings = settings.replace(CAPACITOR_PROJECT_DIRECTORY_PATTERN, (_statement, projectName, sourcePath) => {
841
+ const name = projectName.slice(1).replaceAll(/[^a-zA-Z0-9_.-]/gu, "_");
842
+ const source = resolve2(project.nativeDirectory, sourcePath);
843
+ dependencies.push({
844
+ name,
845
+ windowsSource: windowsPathFromWsl(source, capture)
846
+ });
847
+ return `project('${projectName}').projectDir = new File('./.absolutejs-dependencies/${name}')`;
848
+ });
849
+ if (dependencies.length === 0) {
850
+ throw new Error("Capacitor Android settings did not declare any native dependencies.");
851
+ }
852
+ return { dependencies, rewrittenSettings };
853
+ };
854
+ var encodedWindowsGradleCommand = (windowsSource, windowsDirectory, windowsAndroidRoot, dependencies, rewrittenSettings, task) => {
855
+ const sourceDirectory = Buffer.from(windowsSource, "utf8").toString("base64");
856
+ const buildDirectory = Buffer.from(windowsDirectory, "utf8").toString("base64");
857
+ const androidRoot = Buffer.from(windowsAndroidRoot, "utf8").toString("base64");
858
+ const dependencyData = Buffer.from(JSON.stringify(dependencies), "utf8").toString("base64");
859
+ const settingsData = Buffer.from(rewrittenSettings, "utf8").toString("base64");
860
+ const source = [
861
+ "$ErrorActionPreference = 'Stop'",
862
+ `$source = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${sourceDirectory}'))`,
863
+ `$directory = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${buildDirectory}'))`,
864
+ `$androidHome = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${androidRoot}'))`,
865
+ `$dependencies = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${dependencyData}')) | ConvertFrom-Json`,
866
+ `$settings = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${settingsData}'))`,
867
+ "$env:ANDROID_HOME = $androidHome",
868
+ "$env:ANDROID_SDK_ROOT = $androidHome",
869
+ "New-Item -ItemType Directory -Force -Path $directory | Out-Null",
870
+ "& robocopy.exe $source $directory /MIR /XD .gradle build .absolutejs-dependencies /NFL /NDL /NJH /NJS /NP",
871
+ "$copyExit = $LASTEXITCODE",
872
+ "if ($copyExit -ge 8) { exit $copyExit }",
873
+ "foreach ($dependency in @($dependencies)) { $target = Join-Path $directory ('.absolutejs-dependencies\\' + $dependency.name); New-Item -ItemType Directory -Force -Path $target | Out-Null; & robocopy.exe $dependency.windowsSource $target /MIR /XD .gradle build /NFL /NDL /NJH /NJS /NP; if ($LASTEXITCODE -ge 8) { exit $LASTEXITCODE } }",
874
+ "[IO.File]::WriteAllText((Join-Path $directory 'capacitor.settings.gradle'), $settings)",
875
+ "$wrapper = Join-Path $directory 'gradlew.bat'",
876
+ `& $wrapper --no-daemon --console=plain -p $directory ${task}`,
877
+ "if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }",
878
+ "exit 0"
879
+ ].join("; ");
880
+ return Buffer.from(source, "utf16le").toString("base64");
881
+ };
882
+ var gradleArtifactPath = (nativeDirectory, task, windows = false) => {
883
+ const pathJoin = windows ? win32.join : join3;
884
+ if (task === "assembleDebug") {
885
+ return pathJoin(nativeDirectory, "app", "build", "outputs", "apk", "debug", "app-debug.apk");
886
+ }
887
+ if (task === "assembleRelease") {
888
+ return pathJoin(nativeDirectory, "app", "build", "outputs", "apk", "release", "app-release.apk");
889
+ }
890
+ return pathJoin(nativeDirectory, "app", "build", "outputs", "bundle", "release", "app-release.aab");
891
+ };
892
+ var resolveGradleArtifactPath = async (nativeDirectory, task) => {
893
+ const primary = gradleArtifactPath(nativeDirectory, task);
894
+ if (task !== "assembleRelease" || await pathExists(primary)) {
895
+ return primary;
896
+ }
897
+ return join3(nativeDirectory, "app", "build", "outputs", "apk", "release", "app-release-unsigned.apk");
898
+ };
899
+ var buildAbsoluteAndroidGradleArtifact = async (options) => {
900
+ const { project, task } = options;
901
+ const capture = options.capture ?? captureCommand;
902
+ const run = options.run ?? runCommand;
903
+ const env = options.env ?? process.env;
904
+ if (project.host === "wsl") {
905
+ const windowsSource = windowsPathFromWsl(project.nativeDirectory, capture);
906
+ const buildId = Bun.hash(project.projectRoot).toString(HASH_RADIX);
907
+ const managedBuildDirectory = resolve2(project.androidRoot, "..", "..", "Builds", `${project.config.appId}-${buildId}`);
908
+ const windowsDirectory = windowsPathFromWsl(managedBuildDirectory, capture);
909
+ const windowsAndroidRoot = windowsPathFromWsl(project.androidRoot, capture);
910
+ const { dependencies, rewrittenSettings } = await mirroredCapacitorDependencies(project, capture);
911
+ await requireSuccess([
912
+ "powershell.exe",
913
+ "-NoProfile",
914
+ "-EncodedCommand",
915
+ encodedWindowsGradleCommand(windowsSource, windowsDirectory, windowsAndroidRoot, dependencies, rewrittenSettings, task)
916
+ ], "Android Gradle build", run, { env, signal: options.signal });
917
+ const artifactPath2 = await resolveGradleArtifactPath(managedBuildDirectory, task);
918
+ const unsigned = artifactPath2.endsWith("-unsigned.apk");
919
+ return {
920
+ artifactPath: artifactPath2,
921
+ installPath: unsigned ? win32.join(windowsDirectory, "app", "build", "outputs", "apk", "release", "app-release-unsigned.apk") : gradleArtifactPath(windowsDirectory, task, true)
922
+ };
923
+ }
924
+ const wrapper = project.host === "windows" ? "gradlew.bat" : "./gradlew";
925
+ await requireSuccess([wrapper, "--no-daemon", "--console=plain", task], "Android Gradle build", run, { cwd: project.nativeDirectory, env, signal: options.signal });
926
+ const artifactPath = await resolveGradleArtifactPath(project.nativeDirectory, task);
927
+ return { artifactPath, installPath: artifactPath };
928
+ };
929
+
930
+ // src/mobile/androidRelease.ts
931
+ var ABSOLUTE_ANDROID_RELEASE_FORMAT = 1;
932
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
933
+ var requireManifest = (value) => {
934
+ if (!isRecord(value) || typeof value.appBuild !== "string" || typeof value.appId !== "string" || typeof value.runtime !== "string") {
935
+ throw new TypeError("Invalid embedded AbsoluteJS mobile manifest.");
936
+ }
937
+ return {
938
+ appBuild: value.appBuild,
939
+ appId: value.appId,
940
+ runtime: value.runtime
941
+ };
942
+ };
943
+ var pathExists2 = async (path) => {
944
+ try {
945
+ await access3(path);
946
+ return true;
947
+ } catch {
948
+ return false;
949
+ }
950
+ };
951
+ var defaultCapture = (command, options = {}) => {
952
+ try {
953
+ const result = Bun.spawnSync(command, {
954
+ cwd: options.cwd,
955
+ env: options.env,
956
+ stderr: "pipe",
957
+ stdin: "ignore",
958
+ stdout: "pipe"
959
+ });
960
+ return {
961
+ exitCode: result.exitCode,
962
+ stderr: result.stderr.toString(),
963
+ stdout: result.stdout.toString()
964
+ };
965
+ } catch (error) {
966
+ return {
967
+ exitCode: 1,
968
+ stderr: error instanceof Error ? error.message : String(error),
969
+ stdout: ""
970
+ };
971
+ }
972
+ };
973
+ var verifyAabSignature = (artifactPath, capture, jarsigner) => {
974
+ const executable = jarsigner === undefined ? Bun.which("jarsigner") : jarsigner;
975
+ if (!executable)
976
+ return null;
977
+ const result = capture([
978
+ executable,
979
+ "-J-Duser.language=en",
980
+ "-J-Duser.country=US",
981
+ "-verify",
982
+ artifactPath
983
+ ]);
984
+ return result.exitCode === 0 && /jar verified/iu.test(result.stdout);
985
+ };
986
+ var sha256File = async (path) => createHash3("sha256").update(await readFile4(path)).digest("hex");
987
+ var safeOutputDirectory = (projectRoot, requested) => {
988
+ const root = resolve3(projectRoot);
989
+ const output = resolve3(root, requested ?? ".absolutejs/mobile/releases/android");
990
+ const projectRelative = relative3(root, output);
991
+ if (projectRelative === ".." || projectRelative.startsWith(`..${sep2}`) || isAbsolute2(projectRelative)) {
992
+ throw new TypeError("mobile build --outdir must remain inside the project.");
993
+ }
994
+ return output;
995
+ };
996
+ var installRelease = async (artifactPath, metadata, outputRoot) => {
997
+ const releaseRoot = join4(outputRoot, metadata.releaseId);
998
+ const artifactName = "app-release.aab";
999
+ const destination = join4(releaseRoot, artifactName);
1000
+ if (await pathExists2(releaseRoot)) {
1001
+ const existing = requireManifestIdentity(JSON.parse(await readFile4(join4(releaseRoot, "release.json"), "utf8")), metadata);
1002
+ const [installedBytes, installedSha256] = await Promise.all([
1003
+ stat(destination).then(({ size }) => size),
1004
+ sha256File(destination)
1005
+ ]);
1006
+ if (installedBytes !== metadata.bytes || installedSha256 !== metadata.sha256) {
1007
+ throw new TypeError(`Immutable Android release ${metadata.releaseId} artifact is missing or modified.`);
1008
+ }
1009
+ return { artifactPath: destination, metadata: existing, releaseRoot };
1010
+ }
1011
+ await mkdir3(dirname2(releaseRoot), { recursive: true });
1012
+ const staging = await mkdtemp2(join4(dirname2(releaseRoot), ".android-stage-"));
1013
+ try {
1014
+ await copyFile2(artifactPath, join4(staging, artifactName));
1015
+ const complete = {
1016
+ ...metadata,
1017
+ artifact: artifactName
1018
+ };
1019
+ await writeFile4(join4(staging, "release.json"), `${JSON.stringify(complete, null, "\t")}
1020
+ `, { flag: "wx" });
1021
+ await rename4(staging, releaseRoot);
1022
+ return { artifactPath: destination, metadata: complete, releaseRoot };
1023
+ } finally {
1024
+ await rm3(staging, { force: true, recursive: true }).catch(() => {
1025
+ return;
1026
+ });
1027
+ }
1028
+ };
1029
+ var requireManifestIdentity = (value, expected) => {
1030
+ if (!isRecord(value)) {
1031
+ throw new TypeError("Existing Android release metadata is invalid.");
1032
+ }
1033
+ const { artifact } = value;
1034
+ if (artifact !== "app-release.aab" || Object.entries(expected).some(([key, expectedValue]) => Reflect.get(value, key) !== expectedValue)) {
1035
+ throw new TypeError(`Immutable Android release ${expected.releaseId} does not match its content.`);
1036
+ }
1037
+ return { ...expected, artifact };
1038
+ };
1039
+ var buildAbsoluteAndroidRelease = async (options) => {
1040
+ const projectRoot = resolve3(options.projectRoot);
1041
+ const host = options.host ?? detectAbsoluteMobileHost();
1042
+ const androidRoot = options.androidRoot ?? process.env.ANDROID_HOME ?? process.env.ANDROID_SDK_ROOT ?? absoluteManagedAndroidSdkRoot(host);
1043
+ const nativeDirectory = join4(options.config.nativeProjectDirectory, "android");
1044
+ const { artifactPath } = await buildAbsoluteAndroidGradleArtifact({
1045
+ capture: options.capture,
1046
+ project: {
1047
+ androidRoot,
1048
+ config: options.config,
1049
+ host,
1050
+ nativeDirectory,
1051
+ projectRoot
1052
+ },
1053
+ run: options.run,
1054
+ task: "bundleRelease"
1055
+ });
1056
+ if (!await pathExists2(artifactPath)) {
1057
+ throw new TypeError(`Android Gradle did not produce the expected App Bundle: ${artifactPath}`);
1058
+ }
1059
+ const capture = options.capture ?? defaultCapture;
1060
+ const signed = verifyAabSignature(artifactPath, capture, options.jarsigner);
1061
+ if (signed === null && !options.allowUnsigned) {
1062
+ throw new TypeError("Could not verify the Android App Bundle signature because jarsigner is unavailable. Install a JDK, or use --unsigned only for a non-publishable build.");
1063
+ }
1064
+ if (!signed && !options.allowUnsigned) {
1065
+ throw new TypeError("Android Gradle produced an unsigned App Bundle. Configure the release signingConfig in the source-owned Android project (prefer external Gradle properties), or pass --unsigned only for a non-publishable build.");
1066
+ }
1067
+ const manifest = requireManifest(JSON.parse(await readFile4(join4(options.config.bundleDirectory, "absolute-mobile-manifest.json"), "utf8")));
1068
+ if (manifest.appId !== options.config.appId) {
1069
+ throw new TypeError("Embedded mobile manifest appId does not match mobile.appId.");
1070
+ }
1071
+ const [bytes, sha256] = await Promise.all([
1072
+ stat(artifactPath).then(({ size }) => size),
1073
+ sha256File(artifactPath)
1074
+ ]);
1075
+ const releaseId = `amobile_android_${sha256}`;
1076
+ const metadata = {
1077
+ appBuild: manifest.appBuild,
1078
+ appId: manifest.appId,
1079
+ bytes,
1080
+ engine: "capacitor",
1081
+ format: ABSOLUTE_ANDROID_RELEASE_FORMAT,
1082
+ platform: "android",
1083
+ releaseId,
1084
+ runtime: manifest.runtime,
1085
+ sha256,
1086
+ signed: signed === true,
1087
+ type: "aab"
1088
+ };
1089
+ return installRelease(artifactPath, metadata, safeOutputDirectory(projectRoot, options.outputDirectory));
1090
+ };
1091
+ // src/mobile/associationFiles.ts
1092
+ import {
1093
+ access as access4,
1094
+ mkdir as mkdir4,
1095
+ readFile as readFile5,
1096
+ rename as rename5,
1097
+ rm as rm4,
1098
+ writeFile as writeFile5
1099
+ } from "fs/promises";
1100
+ import { resolve as resolve5 } from "path";
1101
+ import { Elysia } from "elysia";
1102
+
1103
+ // src/mobile/config.ts
1104
+ import { resolve as resolve4 } from "path";
1105
+ var APP_ID_PATTERN = /^[A-Za-z][\w]*(?:\.[A-Za-z][\w]*)+$/;
1106
+ var SCHEME_PATTERN = /^[a-z][a-z0-9+.-]*$/;
1107
+ var APPLE_APP_ID_PREFIX_PATTERN = /^[A-Z0-9]{10}$/;
1108
+ var CERTIFICATE_FINGERPRINT_PATTERN = /^[0-9A-F]{64}$/;
1109
+ var HOSTNAME_PATTERN = /^(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)(?:\.(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?))*$/;
1110
+ var resolveProjectPath = (projectRoot, value, field) => {
1111
+ const root = resolve4(projectRoot);
1112
+ const path = resolve4(root, value);
1113
+ if (path !== root && !path.startsWith(`${root}/`)) {
1114
+ throw new TypeError(`${field} must remain inside the project root.`);
1115
+ }
1116
+ return path;
1117
+ };
1118
+ var requireText = (value, field) => {
1119
+ const normalized = value.trim();
1120
+ if (!normalized)
1121
+ throw new TypeError(`${field} must not be empty.`);
1122
+ return normalized;
1123
+ };
1124
+ var normalizeEntry = (entry) => {
1125
+ const normalized = requireText(entry ?? "/", "mobile.entry");
1126
+ if (!normalized.startsWith("/") || normalized.startsWith("//")) {
1127
+ throw new TypeError("mobile.entry must be an absolute application path.");
1128
+ }
1129
+ return normalized;
1130
+ };
1131
+ var normalizeProductionOrigin = (value) => {
1132
+ const parsed = new URL(requireText(value, "mobile.server.productionOrigin"));
1133
+ if (parsed.protocol !== "https:") {
1134
+ throw new TypeError("mobile.server.productionOrigin must use HTTPS in production.");
1135
+ }
1136
+ if (parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash) {
1137
+ throw new TypeError("mobile.server.productionOrigin must be an origin without credentials, path, query, or hash.");
1138
+ }
1139
+ return parsed.origin;
1140
+ };
1141
+ var normalizePlatforms = (platforms) => {
1142
+ const normalized = [
1143
+ ...new Set(platforms ?? ["ios", "android"])
1144
+ ];
1145
+ if (normalized.length === 0) {
1146
+ throw new TypeError("mobile.platforms must contain at least one platform.");
1147
+ }
1148
+ return normalized;
1149
+ };
1150
+ var normalizeHosts = (hosts, productionOrigin) => {
1151
+ const normalizeHostname = (host) => {
1152
+ const value = requireText(host, "mobile.deepLinks.hosts").toLowerCase();
1153
+ if (!HOSTNAME_PATTERN.test(value)) {
1154
+ throw new TypeError("mobile.deepLinks.hosts entries must be valid hostnames without ports, wildcards, or paths.");
1155
+ }
1156
+ return value;
1157
+ };
1158
+ const normalized = new Set([
1159
+ normalizeHostname(new URL(productionOrigin).hostname)
1160
+ ]);
1161
+ for (const host of hosts ?? []) {
1162
+ normalized.add(normalizeHostname(host));
1163
+ }
1164
+ return [...normalized].sort();
1165
+ };
1166
+ var normalizeAppleAppIdPrefix = (value) => {
1167
+ if (value === undefined)
1168
+ return;
1169
+ const normalized = requireText(value, "mobile.deepLinks.apple.appIdPrefix").toUpperCase();
1170
+ if (!APPLE_APP_ID_PREFIX_PATTERN.test(normalized)) {
1171
+ throw new TypeError("mobile.deepLinks.apple.appIdPrefix must contain ten letters or digits.");
1172
+ }
1173
+ return normalized;
1174
+ };
1175
+ var normalizeCertificateFingerprints = (values) => [
1176
+ ...new Set((values ?? []).map((value) => requireText(value, "mobile.deepLinks.android.sha256CertificateFingerprints").replaceAll(":", "").toUpperCase()).map((value) => {
1177
+ if (!CERTIFICATE_FINGERPRINT_PATTERN.test(value)) {
1178
+ throw new TypeError("mobile.deepLinks.android.sha256CertificateFingerprints entries must be SHA-256 certificate fingerprints.");
1179
+ }
1180
+ return value.match(/.{2}/g)?.join(":") ?? value;
1181
+ }))
1182
+ ].sort();
1183
+ var normalizeAbsoluteMobileConfig = (config, projectRoot) => {
1184
+ const appId = requireText(config.appId, "mobile.appId");
1185
+ if (!APP_ID_PATTERN.test(appId)) {
1186
+ throw new TypeError("mobile.appId must use reverse-domain notation, for example com.example.app.");
1187
+ }
1188
+ const productionOrigin = normalizeProductionOrigin(config.server.productionOrigin);
1189
+ const deepLinkScheme = config.deepLinks?.scheme?.trim().toLowerCase();
1190
+ if (deepLinkScheme && !SCHEME_PATTERN.test(deepLinkScheme)) {
1191
+ throw new TypeError("mobile.deepLinks.scheme is not a valid URL scheme.");
1192
+ }
1193
+ return {
1194
+ androidCertificateFingerprints: normalizeCertificateFingerprints(config.deepLinks?.android?.sha256CertificateFingerprints),
1195
+ appId,
1196
+ appleAppIdPrefix: normalizeAppleAppIdPrefix(config.deepLinks?.apple?.appIdPrefix),
1197
+ appName: requireText(config.appName, "mobile.appName"),
1198
+ bundleDirectory: resolveProjectPath(projectRoot, config.bundleDirectory ?? ".absolutejs/mobile/web", "mobile.bundleDirectory"),
1199
+ deepLinkHosts: normalizeHosts(config.deepLinks?.hosts, productionOrigin),
1200
+ deepLinkScheme,
1201
+ engine: "capacitor",
1202
+ entry: normalizeEntry(config.entry),
1203
+ nativeProjectDirectory: resolveProjectPath(projectRoot, config.nativeProject?.directory ?? "mobile", "mobile.nativeProject.directory"),
1204
+ platforms: normalizePlatforms(config.platforms),
1205
+ productionOrigin
1206
+ };
1207
+ };
1208
+
1209
+ // src/mobile/associationFiles.ts
1210
+ var JSON_HEADERS = {
1211
+ "cache-control": "public, max-age=300, must-revalidate",
1212
+ "content-type": "application/json; charset=utf-8"
1213
+ };
1214
+ var OWNERSHIP_FILE = ".absolutejs-mobile-associations.json";
1215
+ var HTTP_OK = 200;
1216
+ var VERIFY_TIMEOUT_MS = 1e4;
1217
+ var ANDROID_ASSOCIATION_PATH = "/.well-known/assetlinks.json";
1218
+ var APPLE_ASSOCIATION_PATH = "/.well-known/apple-app-site-association";
1219
+ var missingIdentity = (field, platform) => new TypeError(`${field} is required to publish ${platform} deep-link association files.`);
1220
+ var createAppleDocument = (config, requireAll) => {
1221
+ if (!config.platforms.includes("ios"))
1222
+ return;
1223
+ if (!config.appleAppIdPrefix && requireAll) {
1224
+ throw missingIdentity("mobile.deepLinks.apple.appIdPrefix", "iOS");
1225
+ }
1226
+ if (!config.appleAppIdPrefix)
1227
+ return;
1228
+ return {
1229
+ applinks: {
1230
+ details: [
1231
+ {
1232
+ appIDs: [
1233
+ `${config.appleAppIdPrefix}.${config.appId}`
1234
+ ],
1235
+ components: [{ "/": "/*" }]
1236
+ }
1237
+ ]
1238
+ }
1239
+ };
1240
+ };
1241
+ var createAndroidDocument = (config, requireAll) => {
1242
+ if (!config.platforms.includes("android"))
1243
+ return;
1244
+ if (config.androidCertificateFingerprints.length === 0 && requireAll) {
1245
+ throw missingIdentity("mobile.deepLinks.android.sha256CertificateFingerprints", "Android");
1246
+ }
1247
+ if (config.androidCertificateFingerprints.length === 0)
1248
+ return;
1249
+ return [
1250
+ {
1251
+ relation: ["delegate_permission/common.handle_all_urls"],
1252
+ target: {
1253
+ namespace: "android_app",
1254
+ package_name: config.appId,
1255
+ sha256_cert_fingerprints: config.androidCertificateFingerprints
1256
+ }
1257
+ }
1258
+ ];
1259
+ };
1260
+ var createAbsoluteMobileAssociationDocuments = (config, options = {}) => {
1261
+ const documents = {};
1262
+ const requireAll = options.requireAll === true;
1263
+ const android = createAndroidDocument(config, requireAll);
1264
+ const apple = createAppleDocument(config, requireAll);
1265
+ if (android)
1266
+ documents.android = android;
1267
+ if (apple)
1268
+ documents.apple = apple;
1269
+ return documents;
1270
+ };
1271
+ var jsonResponse = (value) => new Response(`${JSON.stringify(value)}
1272
+ `, { headers: JSON_HEADERS });
1273
+ var createAbsoluteMobileAssociationPlugin = (mobile, projectRoot, options = {}) => {
1274
+ const plugin = new Elysia({ name: "absolutejs-mobile-associations" });
1275
+ if (!mobile)
1276
+ return plugin;
1277
+ const config = normalizeAbsoluteMobileConfig(mobile, projectRoot);
1278
+ const documents = createAbsoluteMobileAssociationDocuments(config, options);
1279
+ if (documents.apple) {
1280
+ plugin.get(APPLE_ASSOCIATION_PATH, () => jsonResponse(documents.apple));
1281
+ }
1282
+ if (documents.android) {
1283
+ plugin.get(ANDROID_ASSOCIATION_PATH, () => jsonResponse(documents.android));
1284
+ }
1285
+ return plugin;
1286
+ };
1287
+ var writeAtomic = async (path, source) => {
1288
+ let current;
1289
+ try {
1290
+ current = await readFile5(path, "utf8");
1291
+ } catch (error) {
1292
+ if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
1293
+ throw error;
1294
+ }
1295
+ }
1296
+ if (current === source)
1297
+ return false;
1298
+ const temporary = `${path}.${crypto.randomUUID()}.tmp`;
1299
+ await writeFile5(temporary, source, { flag: "wx" });
1300
+ await rename5(temporary, path);
1301
+ return true;
1302
+ };
1303
+ var exists2 = async (path) => {
1304
+ try {
1305
+ await access4(path);
1306
+ return true;
1307
+ } catch {
1308
+ return false;
1309
+ }
1310
+ };
1311
+ var assertOwnedOutput = async (root) => {
1312
+ const path = resolve5(root, OWNERSHIP_FILE);
1313
+ let ownership;
1314
+ try {
1315
+ ownership = JSON.parse(await readFile5(path, "utf8"));
1316
+ } catch {
1317
+ throw new TypeError(`Association output ${root} already exists and is not owned by AbsoluteJS.`);
1318
+ }
1319
+ if (typeof ownership !== "object" || ownership === null || Reflect.get(ownership, "format") !== 1) {
1320
+ throw new TypeError(`Association output ${root} has an unsupported ownership manifest.`);
1321
+ }
1322
+ };
1323
+ var publishGeneratedDirectory = async (temporary, root) => {
1324
+ const hasCurrent = await exists2(root);
1325
+ if (hasCurrent)
1326
+ await assertOwnedOutput(root);
1327
+ const backup = `${root}.${crypto.randomUUID()}.previous`;
1328
+ if (hasCurrent)
1329
+ await rename5(root, backup);
1330
+ try {
1331
+ await rename5(temporary, root);
1332
+ } catch (error) {
1333
+ if (hasCurrent)
1334
+ await rename5(backup, root);
1335
+ throw error;
1336
+ }
1337
+ if (hasCurrent)
1338
+ await rm4(backup, { force: true, recursive: true });
1339
+ };
1340
+ var materializeHost = async (root, host, files) => {
1341
+ const directory = resolve5(root, host, ".well-known");
1342
+ await mkdir4(directory, { recursive: true });
1343
+ return Promise.all(files.map(async ([name, document]) => {
1344
+ const path = resolve5(directory, name);
1345
+ await writeAtomic(path, `${JSON.stringify(document, null, 2)}
1346
+ `);
1347
+ return path;
1348
+ }));
1349
+ };
1350
+ var associationEndpoints = (config, documents) => config.deepLinkHosts.flatMap((host) => {
1351
+ const endpoints = [];
1352
+ if (documents.android)
1353
+ endpoints.push({
1354
+ document: documents.android,
1355
+ host,
1356
+ path: ANDROID_ASSOCIATION_PATH,
1357
+ platform: "Android"
1358
+ });
1359
+ if (documents.apple)
1360
+ endpoints.push({
1361
+ document: documents.apple,
1362
+ host,
1363
+ path: APPLE_ASSOCIATION_PATH,
1364
+ platform: "Apple"
1365
+ });
1366
+ return endpoints;
1367
+ });
1368
+ var materializeAbsoluteMobileAssociationFiles = async (config, outputDirectory) => {
1369
+ const root = resolve5(outputDirectory);
1370
+ const temporary = `${root}.${crypto.randomUUID()}.tmp`;
1371
+ const documents = createAbsoluteMobileAssociationDocuments(config, {
1372
+ requireAll: true
1373
+ });
1374
+ const files = [];
1375
+ if (documents.android)
1376
+ files.push(["assetlinks.json", documents.android]);
1377
+ if (documents.apple) {
1378
+ files.push(["apple-app-site-association", documents.apple]);
1379
+ }
1380
+ await mkdir4(temporary, { recursive: true });
1381
+ try {
1382
+ const temporaryPaths = (await Promise.all(config.deepLinkHosts.map((host) => materializeHost(temporary, host, files)))).flat();
1383
+ await writeAtomic(resolve5(temporary, OWNERSHIP_FILE), `${JSON.stringify({ format: 1, hosts: config.deepLinkHosts }, null, 2)}
1384
+ `);
1385
+ await publishGeneratedDirectory(temporary, root);
1386
+ const written = temporaryPaths.map((path) => resolve5(root, path.slice(temporary.length + 1)));
1387
+ return { root, written };
1388
+ } catch (error) {
1389
+ await rm4(temporary, { force: true, recursive: true });
1390
+ throw error;
1391
+ }
1392
+ };
1393
+ var verifyAbsoluteMobileAssociationFiles = async (config, request = globalThis.fetch) => {
1394
+ const documents = createAbsoluteMobileAssociationDocuments(config, {
1395
+ requireAll: true
1396
+ });
1397
+ const results = await Promise.all(associationEndpoints(config, documents).map(async (endpoint) => {
1398
+ const url = `https://${endpoint.host}${endpoint.path}`;
1399
+ const response = await request(url, {
1400
+ redirect: "manual",
1401
+ signal: AbortSignal.timeout(VERIFY_TIMEOUT_MS)
1402
+ });
1403
+ if (response.status !== HTTP_OK) {
1404
+ throw new TypeError(`${endpoint.platform} association verification failed for ${url}: HTTP ${response.status}; redirects are not allowed.`);
1405
+ }
1406
+ const contentType = response.headers.get("content-type") ?? "";
1407
+ if (!contentType.toLowerCase().startsWith("application/json")) {
1408
+ throw new TypeError(`${endpoint.platform} association verification failed for ${url}: expected application/json.`);
1409
+ }
1410
+ let document;
1411
+ try {
1412
+ document = await response.json();
1413
+ } catch {
1414
+ throw new TypeError(`${endpoint.platform} association verification failed for ${url}: invalid JSON.`);
1415
+ }
1416
+ if (JSON.stringify(document) !== JSON.stringify(endpoint.document)) {
1417
+ throw new TypeError(`${endpoint.platform} association verification failed for ${url}: the hosted identity does not match mobile config.`);
1418
+ }
1419
+ return { platform: endpoint.platform, url };
1420
+ }));
1421
+ return { results };
1422
+ };
1423
+ // src/mobile/buildMetadata.ts
1424
+ var ABSOLUTE_MOBILE_ROUTE_DETAIL = "x-absolute-mobile";
1425
+ var frameworks2 = new Set([
1426
+ "angular",
1427
+ "ember",
1428
+ "html",
1429
+ "htmx",
1430
+ "react",
1431
+ "svelte",
1432
+ "vue"
1433
+ ]);
1434
+ var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1435
+ var isPageFramework2 = (value) => typeof value === "string" && frameworks2.has(value);
1436
+ var parseAbsoluteMobileBuildPageMetadata = (value) => {
1437
+ if (!isRecord2(value))
1438
+ return;
1439
+ if (typeof value.bundleKey !== "string" || typeof value.contract !== "string" || !isPageFramework2(value.framework) || typeof value.pageId !== "string" || typeof value.propsSchemaHash !== "string") {
1440
+ return;
1441
+ }
1442
+ return {
1443
+ bundleKey: value.bundleKey,
1444
+ contract: value.contract,
1445
+ framework: value.framework,
1446
+ pageId: value.pageId,
1447
+ propsSchemaHash: value.propsSchemaHash
1448
+ };
1449
+ };
1450
+ // src/mobile/buildPipeline.ts
1451
+ import { readFile as readFile9 } from "fs/promises";
1452
+ import { join as join8, resolve as resolve8 } from "path";
1453
+ import { pathToFileURL as pathToFileURL2 } from "url";
1454
+
1455
+ // src/mobile/buildRelease.ts
1456
+ import { createHash as createHash4 } from "crypto";
1457
+ import { readFile as readFile6 } from "fs/promises";
1458
+ import { join as join5, relative as relative4, resolve as resolve6 } from "path";
1459
+ var sha256 = (bytes) => createHash4("sha256").update(bytes).digest("hex");
1460
+ var readPageMetadata = (route) => parseAbsoluteMobileBuildPageMetadata(route.hooks?.detail?.[ABSOLUTE_MOBILE_ROUTE_DETAIL]);
1461
+ var resolveAssetPath = (buildDirectory, assetPath) => {
1462
+ const resolvedBuildDirectory = resolve6(buildDirectory);
1463
+ const resolvedAsset = resolve6(assetPath);
1464
+ if (resolvedAsset.startsWith(`${resolvedBuildDirectory}/`)) {
1465
+ return resolvedAsset;
1466
+ }
1467
+ return join5(buildDirectory, assetPath.replace(/^\/+/, ""));
1468
+ };
1469
+ var pageFor = async (metadata, manifest, buildDirectory) => {
1470
+ const assetPath = manifest[metadata.bundleKey];
1471
+ if (!assetPath) {
1472
+ throw new TypeError(`Mobile page ${metadata.pageId} references missing manifest asset ${metadata.bundleKey}.`);
1473
+ }
1474
+ const resolvedAssetPath = resolveAssetPath(buildDirectory, assetPath);
1475
+ const bytes = await readFile6(resolvedAssetPath);
1476
+ const bundlePath = `/${relative4(resolve6(buildDirectory), resolvedAssetPath).replaceAll("\\", "/")}`;
1477
+ return {
1478
+ bundleHash: sha256(bytes),
1479
+ bundlePath,
1480
+ contract: metadata.contract,
1481
+ framework: metadata.framework,
1482
+ pageId: metadata.pageId,
1483
+ propsSchemaHash: metadata.propsSchemaHash
1484
+ };
1485
+ };
1486
+ var buildAbsoluteMobileCompatibilityRelease = async (options) => {
1487
+ const [captured, producerBytes] = await Promise.all([
1488
+ captureAbsoluteMobileRouteGraph(options.app),
1489
+ readFile6(options.producerPath)
1490
+ ]);
1491
+ if (captured.length === 0) {
1492
+ throw new TypeError("No instrumented AbsoluteJS mobile page routes were found in the finalized Elysia route graph.");
1493
+ }
1494
+ const metadataByPage = new Map;
1495
+ for (const { metadata } of captured) {
1496
+ const prior = metadataByPage.get(metadata.pageId);
1497
+ if (prior && JSON.stringify(prior) !== JSON.stringify(metadata)) {
1498
+ throw new TypeError(`Mobile page ${metadata.pageId} has inconsistent generated contracts.`);
1499
+ }
1500
+ metadataByPage.set(metadata.pageId, metadata);
1501
+ }
1502
+ const pages = await Promise.all([...metadataByPage.values()].map((metadata) => pageFor(metadata, options.manifest, options.buildDirectory)));
1503
+ const producerHash = sha256(producerBytes);
1504
+ const appBuild = `ambuild_${sha256(new TextEncoder().encode(JSON.stringify({
1505
+ pages: pages.map(({ bundleHash, bundlePath, contract, pageId }) => ({
1506
+ bundleHash,
1507
+ bundlePath,
1508
+ contract,
1509
+ pageId
1510
+ })),
1511
+ producerHash,
1512
+ runtime: options.runtime
1513
+ })))}`;
1514
+ const priorAppIds = new Set((options.previousArtifacts ?? []).map(({ appId }) => appId));
1515
+ if (priorAppIds.size > 1 || priorAppIds.size === 1 && !priorAppIds.has(options.appId)) {
1516
+ throw new TypeError("Previous mobile compatibility artifacts belong to another app.");
1517
+ }
1518
+ const unchanged = options.previousArtifacts?.find((artifact2) => artifact2.appBuild === appBuild && artifact2.runtime === options.runtime);
1519
+ const generation = unchanged?.generation ?? Math.max(0, ...(options.previousArtifacts ?? []).map((artifact2) => artifact2.generation)) + 1;
1520
+ const artifact = createAbsoluteMobileCompatibilityArtifact({
1521
+ appBuild,
1522
+ appId: options.appId,
1523
+ generation,
1524
+ pages,
1525
+ producer: {
1526
+ bundleHash: producerHash,
1527
+ bytes: producerBytes.byteLength,
1528
+ exportName: options.producerExport ?? "server",
1529
+ module: options.producerModule ?? "producer.js"
1530
+ },
1531
+ routes: captured.map(({ route }) => route),
1532
+ runtime: options.runtime
1533
+ });
1534
+ return { artifact, producer: new Blob([producerBytes]) };
1535
+ };
1536
+ var captureAbsoluteMobileRouteGraph = async (app) => {
1537
+ if (app.modules)
1538
+ await app.modules;
1539
+ app.compile();
1540
+ const captured = app.routes.flatMap((route) => {
1541
+ const metadata = readPageMetadata(route);
1542
+ if (!metadata || route.method !== "GET" && route.method !== "HEAD") {
1543
+ return [];
1544
+ }
1545
+ return [
1546
+ {
1547
+ metadata,
1548
+ route: {
1549
+ method: route.method === "GET" ? "GET" : "HEAD",
1550
+ pageId: metadata.pageId,
1551
+ pattern: route.path
1552
+ }
1553
+ }
1554
+ ];
1555
+ });
1556
+ const identities = new Map;
1557
+ for (const { metadata, route } of captured) {
1558
+ const prior = identities.get(`${route.method}:${route.pattern}`);
1559
+ if (prior && prior !== metadata.pageId) {
1560
+ throw new TypeError(`Mobile route ${route.method} ${route.pattern} resolves to multiple pages.`);
1561
+ }
1562
+ identities.set(`${route.method}:${route.pattern}`, metadata.pageId);
1563
+ }
1564
+ return captured;
1565
+ };
1566
+
1567
+ // src/mobile/capacitorBundle.ts
1568
+ import {
1569
+ copyFile as copyFile3,
1570
+ mkdir as mkdir5,
1571
+ mkdtemp as mkdtemp3,
1572
+ readFile as readFile7,
1573
+ rename as rename6,
1574
+ rm as rm5,
1575
+ writeFile as writeFile6
1576
+ } from "fs/promises";
1577
+ import { existsSync as existsSync2 } from "fs";
1578
+ import { basename, dirname as dirname3, extname, join as join6, resolve as resolve7 } from "path";
1579
+
1580
+ // src/mobile/routeMatcher.ts
1581
+ var REGEXP_SPECIAL_CHARACTERS = /[.*+?^${}()|[\]\\]/g;
1582
+ var routeSegmentPattern = (segment) => {
1583
+ if (segment === "*")
1584
+ return ".*";
1585
+ if (segment.startsWith(":") && segment.endsWith("?"))
1586
+ return "[^/]*";
1587
+ if (segment.startsWith(":"))
1588
+ return "[^/]+";
1589
+ return segment.replace(REGEXP_SPECIAL_CHARACTERS, "\\$&");
1590
+ };
1591
+ var matchesAbsoluteMobileRoutePattern = (pattern, pathname) => {
1592
+ const expression = pattern.split("/").map(routeSegmentPattern).join("/");
1593
+ return new RegExp(`^${expression}/?$`).test(pathname);
1594
+ };
1595
+ var resolveAbsoluteMobileRoute = (routes, pathname, method = "GET") => routes.find((route) => route.method === method && matchesAbsoluteMobileRoutePattern(route.pattern, pathname));
1596
+
1597
+ // src/mobile/producerContextState.ts
1598
+ var ABSOLUTE_MOBILE_PRODUCER_STORAGE_KEY = Symbol.for("absolutejs.mobileProducerAsyncLocalStorage");
1599
+ var frameworks3 = new Set([
1600
+ "angular",
1601
+ "ember",
1602
+ "html",
1603
+ "htmx",
1604
+ "react",
1605
+ "svelte",
1606
+ "vue"
1607
+ ]);
1608
+ var isCompatibilityPage = (value) => typeof value === "object" && value !== null && ("bundleHash" in value) && typeof value.bundleHash === "string" && ("contract" in value) && typeof value.contract === "string" && ("framework" in value) && typeof value.framework === "string" && frameworks3.has(value.framework) && ("pageId" in value) && typeof value.pageId === "string" && ("propsSchemaHash" in value) && typeof value.propsSchemaHash === "string";
1609
+ var getCurrentAbsoluteMobileProducerContext = () => {
1610
+ const value = Reflect.get(globalThis, ABSOLUTE_MOBILE_PRODUCER_STORAGE_KEY);
1611
+ if (typeof value !== "object" || value === null || !("getStore" in value) || typeof value.getStore !== "function") {
1612
+ return;
1613
+ }
1614
+ const context = value.getStore();
1615
+ if (typeof context !== "object" || context === null || !("page" in context) || !isCompatibilityPage(context.page) || !("releaseId" in context) || typeof context.releaseId !== "string") {
1616
+ return;
1617
+ }
1618
+ return { page: context.page, releaseId: context.releaseId };
1619
+ };
1620
+
1621
+ // src/mobile/pageProtocol.ts
1622
+ var ABSOLUTE_MOBILE_PAGE_MEDIA_TYPE = "application/vnd.absolute.page+json";
1623
+ var ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION = 1;
1624
+ var BAD_REQUEST_STATUS = 400;
1625
+ var OK_STATUS = 200;
1626
+ var SERVER_ERROR_STATUS = 500;
1627
+ var UPGRADE_REQUIRED_STATUS = 426;
1628
+ var MOBILE_PAGE_REQUEST_HEADERS = {
1629
+ appBuild: "x-absolute-mobile-app-build",
1630
+ pageBundle: "x-absolute-mobile-page-bundle",
1631
+ pageContracts: "x-absolute-mobile-page-contracts",
1632
+ pageId: "x-absolute-mobile-page-id",
1633
+ protocol: "x-absolute-mobile-protocol",
1634
+ runtime: "x-absolute-mobile-runtime"
1635
+ };
1636
+ var parseMediaType = (value) => value.split(";", 1)[0]?.trim().toLowerCase();
1637
+ var acceptsAbsoluteMobilePage = (request) => {
1638
+ if (!request)
1639
+ return false;
1640
+ return (request.headers.get("accept") ?? "").split(",").some((value) => parseMediaType(value) === ABSOLUTE_MOBILE_PAGE_MEDIA_TYPE);
1641
+ };
1642
+ var readRequiredHeader = (request, name) => {
1643
+ const value = request.headers.get(name)?.trim();
1644
+ return value ? value : undefined;
1645
+ };
1646
+ var parseAbsoluteMobilePageRequest = (request) => {
1647
+ if (!request || !acceptsAbsoluteMobilePage(request)) {
1648
+ return { kind: "not-mobile" };
1649
+ }
1650
+ const protocolValue = readRequiredHeader(request, MOBILE_PAGE_REQUEST_HEADERS.protocol);
1651
+ const runtime = readRequiredHeader(request, MOBILE_PAGE_REQUEST_HEADERS.runtime);
1652
+ const appBuild = readRequiredHeader(request, MOBILE_PAGE_REQUEST_HEADERS.appBuild);
1653
+ const pageBundle = readRequiredHeader(request, MOBILE_PAGE_REQUEST_HEADERS.pageBundle);
1654
+ const pageContractsValue = readRequiredHeader(request, MOBILE_PAGE_REQUEST_HEADERS.pageContracts);
1655
+ const pageId = readRequiredHeader(request, MOBILE_PAGE_REQUEST_HEADERS.pageId);
1656
+ if (!protocolValue || !/^\d+$/.test(protocolValue)) {
1657
+ return {
1658
+ kind: "invalid",
1659
+ message: `Missing or invalid ${MOBILE_PAGE_REQUEST_HEADERS.protocol} header.`
1660
+ };
1661
+ }
1662
+ if (!runtime) {
1663
+ return {
1664
+ kind: "invalid",
1665
+ message: `Missing ${MOBILE_PAGE_REQUEST_HEADERS.runtime} header.`
1666
+ };
1667
+ }
1668
+ if (!appBuild) {
1669
+ return {
1670
+ kind: "invalid",
1671
+ message: `Missing ${MOBILE_PAGE_REQUEST_HEADERS.appBuild} header.`
1672
+ };
1673
+ }
1674
+ if (!pageBundle) {
1675
+ return {
1676
+ kind: "invalid",
1677
+ message: `Missing ${MOBILE_PAGE_REQUEST_HEADERS.pageBundle} header.`
1678
+ };
1679
+ }
1680
+ if (!pageContractsValue) {
1681
+ return {
1682
+ kind: "invalid",
1683
+ message: `Missing ${MOBILE_PAGE_REQUEST_HEADERS.pageContracts} header.`
1684
+ };
1685
+ }
1686
+ if (!pageId) {
1687
+ return {
1688
+ kind: "invalid",
1689
+ message: `Missing ${MOBILE_PAGE_REQUEST_HEADERS.pageId} header.`
1690
+ };
1691
+ }
1692
+ const pageContracts = [
1693
+ ...new Set(pageContractsValue.split(",").map((value) => value.trim()).filter(Boolean))
1694
+ ];
1695
+ if (pageContracts.length === 0) {
1696
+ return {
1697
+ kind: "invalid",
1698
+ message: `${MOBILE_PAGE_REQUEST_HEADERS.pageContracts} must contain at least one contract.`
1699
+ };
1700
+ }
1701
+ return {
1702
+ client: {
1703
+ appBuild,
1704
+ pageBundle,
1705
+ pageContracts,
1706
+ pageId,
1707
+ protocol: Number(protocolValue),
1708
+ runtime
1709
+ },
1710
+ kind: "mobile"
1711
+ };
1712
+ };
1713
+ var responseHeaders = () => {
1714
+ const headers = new Headers({
1715
+ "cache-control": "no-store",
1716
+ "content-type": `${ABSOLUTE_MOBILE_PAGE_MEDIA_TYPE}; version=${ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION}`
1717
+ });
1718
+ headerVaryValues.forEach((value) => headers.append("vary", value));
1719
+ return headers;
1720
+ };
1721
+ var headerVaryValues = [
1722
+ "Accept",
1723
+ MOBILE_PAGE_REQUEST_HEADERS.protocol,
1724
+ MOBILE_PAGE_REQUEST_HEADERS.runtime,
1725
+ MOBILE_PAGE_REQUEST_HEADERS.appBuild,
1726
+ MOBILE_PAGE_REQUEST_HEADERS.pageBundle,
1727
+ MOBILE_PAGE_REQUEST_HEADERS.pageContracts,
1728
+ MOBILE_PAGE_REQUEST_HEADERS.pageId
1729
+ ];
1730
+ var envelopeResponse = (response, status) => new Response(JSON.stringify({
1731
+ protocol: ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION,
1732
+ response
1733
+ }), { headers: responseHeaders(), status });
1734
+ var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1735
+ var normalizeJsonValue = (value) => {
1736
+ const serialized = JSON.stringify(value);
1737
+ if (serialized === undefined) {
1738
+ throw new TypeError("Mobile page props must be JSON-serializable.");
1739
+ }
1740
+ const parsed = JSON.parse(serialized);
1741
+ if (!isRecord3(parsed)) {
1742
+ throw new TypeError("Mobile page props must serialize to an object.");
1743
+ }
1744
+ return parsed;
1745
+ };
1746
+ var finalizeArchivedMobilePage = (context, input) => {
1747
+ const { page } = context;
1748
+ if (page.pageId !== input.compatibility.pageId || page.framework !== input.compatibility.framework) {
1749
+ return envelopeResponse({
1750
+ kind: "invalid-request",
1751
+ message: "Archived producer page identity does not match its release artifact."
1752
+ }, BAD_REQUEST_STATUS);
1753
+ }
1754
+ try {
1755
+ const [currentRepresentation] = input.compatibility.representations;
1756
+ if (!currentRepresentation) {
1757
+ throw new TypeError("Mobile page has no current representation.");
1758
+ }
1759
+ return envelopeResponse({
1760
+ contract: page.contract,
1761
+ framework: page.framework,
1762
+ kind: "page",
1763
+ pageId: page.pageId,
1764
+ props: normalizeJsonValue(currentRepresentation.mapProps(input.props)),
1765
+ status: input.status ?? OK_STATUS
1766
+ }, input.status ?? OK_STATUS);
1767
+ } catch (error) {
1768
+ console.error(`[Mobile] Archived producer ${context.releaseId} failed for ${page.pageId}:`, error);
1769
+ return createAbsoluteMobilePageErrorResponse(page.pageId);
1770
+ }
1771
+ };
1772
+ var createAbsoluteMobileInvalidRequestResponse = (message) => envelopeResponse({ kind: "invalid-request", message }, BAD_REQUEST_STATUS);
1773
+ var createAbsoluteMobilePageErrorResponse = (pageId) => envelopeResponse({
1774
+ code: "representation-failed",
1775
+ kind: "error",
1776
+ pageId,
1777
+ status: SERVER_ERROR_STATUS
1778
+ }, SERVER_ERROR_STATUS);
1779
+ var createAbsoluteMobileUpgradeResponse = (result) => envelopeResponse(result, UPGRADE_REQUIRED_STATUS);
1780
+ var finalizeAbsoluteMobilePage = (input) => {
1781
+ const parsed = parseAbsoluteMobilePageRequest(input.request);
1782
+ if (parsed.kind === "not-mobile")
1783
+ return;
1784
+ if (parsed.kind === "invalid") {
1785
+ return envelopeResponse({ kind: "invalid-request", message: parsed.message }, BAD_REQUEST_STATUS);
1786
+ }
1787
+ const { client } = parsed;
1788
+ if (client.pageId !== input.compatibility.pageId) {
1789
+ return envelopeResponse({
1790
+ kind: "invalid-request",
1791
+ message: `Requested mobile page ${client.pageId} does not match ${input.compatibility.pageId}.`
1792
+ }, BAD_REQUEST_STATUS);
1793
+ }
1794
+ const producerContext = getCurrentAbsoluteMobileProducerContext();
1795
+ if (producerContext)
1796
+ return finalizeArchivedMobilePage(producerContext, input);
1797
+ if (client.protocol !== ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION) {
1798
+ return envelopeResponse({
1799
+ kind: "upgrade-required",
1800
+ pageId: input.compatibility.pageId,
1801
+ reason: "protocol"
1802
+ }, UPGRADE_REQUIRED_STATUS);
1803
+ }
1804
+ if (!input.compatibility.runtimes.includes(client.runtime)) {
1805
+ return envelopeResponse({
1806
+ kind: "upgrade-required",
1807
+ pageId: input.compatibility.pageId,
1808
+ reason: "runtime",
1809
+ supportedRuntimes: [...input.compatibility.runtimes]
1810
+ }, UPGRADE_REQUIRED_STATUS);
1811
+ }
1812
+ const clientContracts = new Set(client.pageContracts);
1813
+ const representation = input.compatibility.representations.find(({ contract }) => clientContracts.has(contract));
1814
+ if (!representation) {
1815
+ return envelopeResponse({
1816
+ kind: "upgrade-required",
1817
+ pageId: input.compatibility.pageId,
1818
+ reason: "page-contract",
1819
+ supportedContracts: input.compatibility.representations.map(({ contract }) => contract)
1820
+ }, UPGRADE_REQUIRED_STATUS);
1821
+ }
1822
+ try {
1823
+ return envelopeResponse({
1824
+ contract: representation.contract,
1825
+ framework: input.compatibility.framework,
1826
+ kind: "page",
1827
+ pageId: input.compatibility.pageId,
1828
+ props: normalizeJsonValue(representation.mapProps(input.props)),
1829
+ status: input.status ?? OK_STATUS
1830
+ }, input.status ?? OK_STATUS);
1831
+ } catch (error) {
1832
+ console.error(`[Mobile] Failed to produce ${input.compatibility.pageId} representation ${representation.contract}:`, error);
1833
+ return envelopeResponse({
1834
+ code: "representation-failed",
1835
+ kind: "error",
1836
+ pageId: input.compatibility.pageId,
1837
+ status: SERVER_ERROR_STATUS
1838
+ }, SERVER_ERROR_STATUS);
1839
+ }
1840
+ };
1841
+
1842
+ // src/mobile/client.ts
1843
+ class AbsoluteMobilePageProtocolError extends Error {
1844
+ code;
1845
+ constructor(code, message) {
1846
+ super(message);
1847
+ this.name = "AbsoluteMobilePageProtocolError";
1848
+ this.code = code;
1849
+ }
1850
+ }
1851
+ var frameworks4 = new Set([
1852
+ "angular",
1853
+ "ember",
1854
+ "html",
1855
+ "htmx",
1856
+ "react",
1857
+ "svelte",
1858
+ "vue"
1859
+ ]);
1860
+ var upgradeReasons = new Set([
1861
+ "app-release",
1862
+ "page-contract",
1863
+ "protocol",
1864
+ "runtime"
1865
+ ]);
1866
+ var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1867
+ var isFramework = (value) => typeof value === "string" && frameworks4.has(value);
1868
+ var isUpgradeReason = (value) => typeof value === "string" && upgradeReasons.has(value);
1869
+ var parsePageResult = (value) => {
1870
+ if (typeof value.contract !== "string" || !isFramework(value.framework) || typeof value.pageId !== "string" || typeof value.status !== "number") {
1871
+ throw new AbsoluteMobilePageProtocolError("invalid-envelope", "The mobile page response is missing required page metadata.");
1872
+ }
1873
+ if (!isRecord4(value.props)) {
1874
+ throw new AbsoluteMobilePageProtocolError("invalid-props", "The mobile page response must contain an object props value.");
1875
+ }
1876
+ return {
1877
+ contract: value.contract,
1878
+ framework: value.framework,
1879
+ kind: "page",
1880
+ pageId: value.pageId,
1881
+ props: value.props,
1882
+ status: value.status
1883
+ };
1884
+ };
1885
+ var parseUpgradeResult = (value) => {
1886
+ if (typeof value.pageId !== "string" || !isUpgradeReason(value.reason)) {
1887
+ throw new AbsoluteMobilePageProtocolError("invalid-envelope", "The update response is missing its page or reason.");
1888
+ }
1889
+ return {
1890
+ kind: "upgrade-required",
1891
+ pageId: value.pageId,
1892
+ reason: value.reason,
1893
+ supportedContracts: Array.isArray(value.supportedContracts) ? value.supportedContracts.filter((contract) => typeof contract === "string") : undefined,
1894
+ supportedRuntimes: Array.isArray(value.supportedRuntimes) ? value.supportedRuntimes.filter((runtime) => typeof runtime === "string") : undefined
1895
+ };
1896
+ };
1897
+ var activateAbsoluteMobilePage = async (value, options) => {
1898
+ const envelope = parseAbsoluteMobilePageEnvelope(value);
1899
+ if (envelope.response.kind === "upgrade-required") {
1900
+ return envelope.response;
1901
+ }
1902
+ if (envelope.response.kind !== "page") {
1903
+ throw new AbsoluteMobilePageProtocolError("invalid-envelope", "Expected a renderable mobile page response.");
1904
+ }
1905
+ const target = options.target ?? window;
1906
+ target.__INITIAL_PROPS__ = envelope.response.props;
1907
+ target.__ABSOLUTE_PAGE_RENDER_MODE__ = "client";
1908
+ await options.loadPage({
1909
+ contract: envelope.response.contract,
1910
+ pageId: envelope.response.pageId
1911
+ });
1912
+ return {
1913
+ contract: envelope.response.contract,
1914
+ kind: "rendered",
1915
+ pageId: envelope.response.pageId
1916
+ };
1917
+ };
1918
+ var parseAbsoluteMobilePageEnvelope = (value) => {
1919
+ if (!isRecord4(value) || !isRecord4(value.response)) {
1920
+ throw new AbsoluteMobilePageProtocolError("invalid-envelope", "The server did not return an AbsoluteJS mobile page envelope.");
1921
+ }
1922
+ if (value.protocol !== ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION) {
1923
+ throw new AbsoluteMobilePageProtocolError("invalid-envelope", `Unsupported mobile page protocol ${String(value.protocol)}.`);
1924
+ }
1925
+ const { kind } = value.response;
1926
+ if (kind === "page") {
1927
+ return {
1928
+ protocol: ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION,
1929
+ response: parsePageResult(value.response)
1930
+ };
1931
+ }
1932
+ if (kind === "upgrade-required") {
1933
+ return {
1934
+ protocol: ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION,
1935
+ response: parseUpgradeResult(value.response)
1936
+ };
1937
+ }
1938
+ if (kind === "invalid-request") {
1939
+ throw new AbsoluteMobilePageProtocolError("server-rejected-request", typeof value.response.message === "string" ? value.response.message : "The server rejected the mobile page request.");
1940
+ }
1941
+ if (kind === "error") {
1942
+ throw new AbsoluteMobilePageProtocolError("server-error", "The server could not create a compatible mobile page representation.");
1943
+ }
1944
+ throw new AbsoluteMobilePageProtocolError("invalid-envelope", `Unknown mobile page response kind ${String(kind)}.`);
1945
+ };
1946
+
1947
+ // src/mobile/transport.ts
1948
+ var ABSOLUTE_MOBILE_CLIENT_MANIFEST_FORMAT = 1;
1949
+ var pageForPathname = (manifest, pathname) => {
1950
+ const route = resolveAbsoluteMobileRoute(manifest.routes, pathname);
1951
+ if (!route) {
1952
+ throw new TypeError(`No embedded mobile page owns ${pathname}.`);
1953
+ }
1954
+ const page = manifest.pages.find((candidate) => candidate.pageId === route.pageId);
1955
+ if (!page) {
1956
+ throw new TypeError(`Mobile route ${route.pattern} references missing page ${route.pageId}.`);
1957
+ }
1958
+ return page;
1959
+ };
1960
+ var canonicalBackendUrl = (manifest, path) => {
1961
+ const origin = new URL(manifest.productionOrigin);
1962
+ const url = new URL(path, `${origin.origin}/`);
1963
+ if (url.origin !== origin.origin) {
1964
+ throw new TypeError("Mobile transport cannot leave productionOrigin.");
1965
+ }
1966
+ url.hash = "";
1967
+ return url;
1968
+ };
1969
+ var createAbsoluteMobilePageRequest = (manifest, path, options = {}) => {
1970
+ const url = canonicalBackendUrl(manifest, path);
1971
+ const page = pageForPathname(manifest, url.pathname);
1972
+ const headers = new Headers(options.headers);
1973
+ headers.set("accept", ABSOLUTE_MOBILE_PAGE_MEDIA_TYPE);
1974
+ headers.set(MOBILE_PAGE_REQUEST_HEADERS.appBuild, manifest.appBuild);
1975
+ headers.set(MOBILE_PAGE_REQUEST_HEADERS.pageBundle, page.bundleHash);
1976
+ headers.set(MOBILE_PAGE_REQUEST_HEADERS.pageContracts, page.contract);
1977
+ headers.set(MOBILE_PAGE_REQUEST_HEADERS.pageId, page.pageId);
1978
+ headers.set(MOBILE_PAGE_REQUEST_HEADERS.protocol, String(ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION));
1979
+ headers.set(MOBILE_PAGE_REQUEST_HEADERS.runtime, manifest.runtime);
1980
+ return new Request(url, { headers, method: "GET" });
1981
+ };
1982
+ var fetchAbsoluteMobilePage = async (manifest, path, options = {}) => {
1983
+ const request = createAbsoluteMobilePageRequest(manifest, path, options);
1984
+ const response = await (options.fetch ?? globalThis.fetch)(request);
1985
+ const value = await response.json();
1986
+ return value;
1987
+ };
1988
+ var navigateAbsoluteMobilePage = async (manifest, path, options) => {
1989
+ const envelope = await fetchAbsoluteMobilePage(manifest, path, options);
1990
+ return activateAbsoluteMobilePage(envelope, options);
1991
+ };
1992
+ var resolveAbsoluteMobileDeepLink = (manifest, value) => {
1993
+ const url = new URL(value);
1994
+ if (url.username || url.password) {
1995
+ throw new TypeError("Mobile deep links cannot contain credentials.");
1996
+ }
1997
+ const isHttpsHost = url.protocol === "https:" && manifest.deepLinkHosts.includes(url.hostname.toLowerCase());
1998
+ const isCustomScheme = manifest.deepLinkScheme !== undefined && url.protocol === `${manifest.deepLinkScheme}:`;
1999
+ if (!isHttpsHost && !isCustomScheme) {
2000
+ throw new TypeError("Mobile deep link is outside the configured app URLs.");
2001
+ }
2002
+ return `${url.pathname || "/"}${url.search}${url.hash}`;
2003
+ };
2004
+
2005
+ // src/mobile/capacitorBundle.ts
2006
+ var MANIFEST_FILE = "absolute-mobile-manifest.json";
2007
+ var BOOTSTRAP_FILE = "absolute-mobile-bootstrap.js";
2008
+ var INDEX_FILE = "index.html";
2009
+ var CLIENT_IMPORT_PATTERN = /(?:\bfrom\s*|\bimport\s*\(\s*|\bimport\s*)["'](\/[^"']+)["']/gu;
2010
+ var errorHasCode2 = (error, code) => typeof error === "object" && error !== null && Reflect.get(error, "code") === code;
2011
+ var shellBootstrapModule = () => {
2012
+ const candidate = ["js", "ts"].map((extension) => join6(import.meta.dir, `shellBootstrap.${extension}`)).find(existsSync2);
2013
+ if (candidate)
2014
+ return candidate;
2015
+ throw new TypeError("AbsoluteJS mobile shell bootstrap module is missing.");
2016
+ };
2017
+ var escapeHtml = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
2018
+ var indexHtml = (appName) => `<!doctype html>
2019
+ <html>
2020
+ <head>
2021
+ <meta charset="utf-8">
2022
+ <meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
2023
+ <meta name="color-scheme" content="light dark">
2024
+ <title>${escapeHtml(appName)}</title>
2025
+ </head>
2026
+ <body>
2027
+ <main id="absolute-mobile-status" role="status">Starting\u2026</main>
2028
+ <script type="module" src="./${BOOTSTRAP_FILE}"></script>
2029
+ </body>
2030
+ </html>
2031
+ `;
2032
+ var sourceAssetPath = (buildDirectory, bundlePath) => {
2033
+ const root = resolve7(buildDirectory);
2034
+ const asset = resolve7(root, bundlePath.replace(/^\/+/, ""));
2035
+ if (!asset.startsWith(`${root}/`)) {
2036
+ throw new TypeError("Mobile page bundle escaped the build directory.");
2037
+ }
2038
+ return asset;
2039
+ };
2040
+ var buildShellBootstrap = async (staging) => {
2041
+ const modulePath = shellBootstrapModule();
2042
+ const entryPath = join6(staging, ".absolute-mobile-entry.ts");
2043
+ await writeFile6(entryPath, `import { startAbsoluteMobileShell } from ${JSON.stringify(modulePath)};
2044
+ void startAbsoluteMobileShell();
2045
+ `);
2046
+ const build = await Bun.build({
2047
+ entrypoints: [entryPath],
2048
+ minify: true,
2049
+ outdir: staging,
2050
+ target: "browser"
2051
+ });
2052
+ if (!build.success || build.outputs.length !== 1) {
2053
+ throw new AggregateError(build.logs, "Failed to build the AbsoluteJS Capacitor shell.");
2054
+ }
2055
+ await rename6(build.outputs[0]?.path ?? "", join6(staging, BOOTSTRAP_FILE));
2056
+ await rm5(entryPath, { force: true });
2057
+ };
2058
+ var removePreviousBundle = async (backup, moved) => {
2059
+ if (!moved)
2060
+ return;
2061
+ await rm5(backup, { force: true, recursive: true });
2062
+ };
2063
+ var restorePreviousBundle = async (backup, destination, moved) => {
2064
+ if (!moved)
2065
+ return;
2066
+ await rename6(backup, destination);
2067
+ };
2068
+ var installBundle = async (staging, destination) => {
2069
+ const backup = `${destination}.previous-${crypto.randomUUID()}`;
2070
+ let movedPrevious = false;
2071
+ try {
2072
+ await rename6(destination, backup);
2073
+ movedPrevious = true;
2074
+ } catch (error) {
2075
+ if (!errorHasCode2(error, "ENOENT"))
2076
+ throw error;
2077
+ }
2078
+ try {
2079
+ await rename6(staging, destination);
2080
+ await removePreviousBundle(backup, movedPrevious);
2081
+ } catch (error) {
2082
+ await restorePreviousBundle(backup, destination, movedPrevious);
2083
+ throw error;
2084
+ }
2085
+ };
2086
+ var copyClientPage = async (page, buildDirectory, staging, copiedDependencies) => {
2087
+ if (page.framework !== "react") {
2088
+ throw new TypeError(`Capacitor spike currently supports React pages; ${page.pageId} is ${page.framework}.`);
2089
+ }
2090
+ const extension = extname(page.bundlePath) || ".js";
2091
+ const localBundlePath = `./pages/${page.bundleHash}${extension}`;
2092
+ const source = sourceAssetPath(buildDirectory, page.bundlePath);
2093
+ await copyFile3(source, join6(staging, localBundlePath));
2094
+ await copyAbsoluteClientDependencies(source, buildDirectory, staging, copiedDependencies);
2095
+ return { ...page, localBundlePath };
2096
+ };
2097
+ var absoluteClientImports = async (sourcePath) => {
2098
+ const source = await readFile7(sourcePath, "utf8");
2099
+ return [...source.matchAll(CLIENT_IMPORT_PATTERN)].flatMap((match) => {
2100
+ const [specifier] = match.slice(1);
2101
+ return specifier ? [specifier.split(/[?#]/u, 1)[0] ?? specifier] : [];
2102
+ });
2103
+ };
2104
+ var copyAbsoluteClientDependency = async (specifier, buildDirectory, staging, copied) => {
2105
+ if (copied.has(specifier))
2106
+ return;
2107
+ copied.add(specifier);
2108
+ const source = sourceAssetPath(buildDirectory, specifier);
2109
+ const destination = join6(staging, specifier.replace(/^\/+/, ""));
2110
+ await mkdir5(dirname3(destination), { recursive: true });
2111
+ await copyFile3(source, destination);
2112
+ await copyAbsoluteClientDependencies(source, buildDirectory, staging, copied);
2113
+ };
2114
+ var copyAbsoluteClientDependencies = async (sourcePath, buildDirectory, staging, copied) => {
2115
+ const dependencies = await absoluteClientImports(sourcePath);
2116
+ await Promise.all(dependencies.map((specifier) => copyAbsoluteClientDependency(specifier, buildDirectory, staging, copied)));
2117
+ };
2118
+ var materializeAbsoluteCapacitorWebBundle = async (options) => {
2119
+ if (!resolveAbsoluteMobileRoute(options.artifact.routes, new URL(options.config.entry, "https://absolute.invalid").pathname)) {
2120
+ throw new TypeError(`mobile.entry ${options.config.entry} is not a captured mobile page route.`);
2121
+ }
2122
+ const destination = options.config.bundleDirectory;
2123
+ await mkdir5(dirname3(destination), { recursive: true });
2124
+ const staging = await mkdtemp3(join6(dirname3(destination), `.${basename(destination)}.stage-`));
2125
+ try {
2126
+ const pageDirectory = join6(staging, "pages");
2127
+ await mkdir5(pageDirectory, { recursive: true });
2128
+ const copiedDependencies = new Set;
2129
+ const pages = await Promise.all(options.artifact.pages.map((page) => copyClientPage(page, options.buildDirectory, staging, copiedDependencies)));
2130
+ const manifest = {
2131
+ appBuild: options.artifact.appBuild,
2132
+ appId: options.config.appId,
2133
+ appName: options.config.appName,
2134
+ deepLinkHosts: options.config.deepLinkHosts,
2135
+ deepLinkScheme: options.config.deepLinkScheme,
2136
+ entry: options.config.entry,
2137
+ format: ABSOLUTE_MOBILE_CLIENT_MANIFEST_FORMAT,
2138
+ pages,
2139
+ productionOrigin: options.config.productionOrigin,
2140
+ routes: options.artifact.routes,
2141
+ runtime: options.artifact.runtime
2142
+ };
2143
+ await Promise.all([
2144
+ writeFile6(join6(staging, MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
2145
+ `),
2146
+ writeFile6(join6(staging, INDEX_FILE), indexHtml(options.config.appName)),
2147
+ buildShellBootstrap(staging)
2148
+ ]);
2149
+ await installBundle(staging, destination);
2150
+ return manifest;
2151
+ } catch (error) {
2152
+ await rm5(staging, { force: true, recursive: true });
2153
+ throw error;
2154
+ }
2155
+ };
2156
+
2157
+ // src/mobile/materializedBundle.ts
2158
+ import { createHash as createHash5 } from "crypto";
2159
+ import {
2160
+ access as access5,
2161
+ mkdir as mkdir6,
2162
+ mkdtemp as mkdtemp4,
2163
+ readFile as readFile8,
2164
+ rename as rename7,
2165
+ rm as rm6,
2166
+ writeFile as writeFile7
2167
+ } from "fs/promises";
2168
+ import { dirname as dirname4, join as join7, resolve as resolvePath2 } from "path";
2169
+ import { pathToFileURL } from "url";
2170
+ var ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT = 1;
2171
+ var CURRENT_BUNDLE_FILE = "current.json";
2172
+ var BUNDLES_DIRECTORY = "bundles";
2173
+ var ARTIFACT_FILE2 = "artifact.json";
2174
+ var BUNDLE_ID_PATTERN = /^amb_[a-f0-9]{64}$/;
2175
+ var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2176
+ var errorHasCode3 = (error, code) => typeof error === "object" && error !== null && Reflect.get(error, "code") === code;
2177
+ var bundleIdFor = (currentReleaseId, releases) => {
2178
+ const identity = JSON.stringify({
2179
+ currentReleaseId,
2180
+ releases: releases.map(({ releaseId }) => releaseId)
2181
+ });
2182
+ return `amb_${createHash5("sha256").update(identity).digest("hex")}`;
2183
+ };
2184
+ var parseBundleIndex = (value) => {
2185
+ if (!isRecord5(value) || value.format !== ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT || typeof value.bundleId !== "string" || !BUNDLE_ID_PATTERN.test(value.bundleId) || typeof value.currentReleaseId !== "string" || !Array.isArray(value.releases)) {
2186
+ throw new TypeError("Invalid materialized mobile compatibility bundle.");
2187
+ }
2188
+ const releases = value.releases.map(parseAbsoluteMobileCompatibilityArtifact);
2189
+ const retained = retainAbsoluteMobileCompatibilityArtifacts(releases);
2190
+ if (retained.length !== releases.length || retained.some(({ releaseId }, index) => releaseId !== releases[index]?.releaseId)) {
2191
+ throw new TypeError("Materialized mobile bundle releases are not a retained generation window.");
2192
+ }
2193
+ const expectedBundleId = bundleIdFor(value.currentReleaseId, releases);
2194
+ if (value.bundleId !== expectedBundleId) {
2195
+ throw new TypeError("Materialized mobile bundle integrity failed.");
2196
+ }
2197
+ if (releases[0]?.releaseId !== value.currentReleaseId) {
2198
+ throw new TypeError("Materialized mobile bundle current release is not its newest generation.");
2199
+ }
2200
+ return {
2201
+ bundleId: value.bundleId,
2202
+ currentReleaseId: value.currentReleaseId,
2203
+ format: ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT,
2204
+ releases
2205
+ };
2206
+ };
2207
+ var writeRelease = async (root, release) => {
2208
+ const directory = join7(root, release.artifact.releaseId);
2209
+ const producerPath = join7(directory, release.artifact.producer.module);
2210
+ await mkdir6(dirname4(producerPath), { recursive: true });
2211
+ await Promise.all([
2212
+ writeFile7(join7(directory, ARTIFACT_FILE2), `${JSON.stringify(release.artifact, null, "\t")}
2213
+ `),
2214
+ writeFile7(producerPath, new Uint8Array(await release.producer.arrayBuffer()))
2215
+ ]);
2216
+ };
2217
+ var installImmutableBundle = async (bundlesRoot, bundleId, releases) => {
2218
+ const destination = join7(bundlesRoot, bundleId);
2219
+ try {
2220
+ await access5(destination);
2221
+ return destination;
2222
+ } catch (error) {
2223
+ if (!errorHasCode3(error, "ENOENT"))
2224
+ throw error;
2225
+ }
2226
+ const staging = await mkdtemp4(join7(bundlesRoot, ".stage-"));
2227
+ try {
2228
+ await Promise.all(releases.map((release) => writeRelease(staging, release)));
2229
+ await rename7(staging, destination);
2230
+ } catch (error) {
2231
+ await rm6(staging, { force: true, recursive: true });
2232
+ if (errorHasCode3(error, "EEXIST") || errorHasCode3(error, "ENOTEMPTY")) {
2233
+ return destination;
2234
+ }
2235
+ throw error;
2236
+ }
2237
+ return destination;
2238
+ };
2239
+ var readCompatibilityModule = (modulePath) => import(pathToFileURL(modulePath).href);
2240
+ var resolveProducerHandler = (loaded, exportName) => {
2241
+ const value = loaded[exportName];
2242
+ if (!isRecord5(value) || typeof value.handle !== "function") {
2243
+ throw new TypeError(`Compatibility producer export ${exportName} must expose handle(request).`);
2244
+ }
2245
+ const { handle } = value;
2246
+ return {
2247
+ handle: async (request) => {
2248
+ const response = await Reflect.apply(handle, value, [
2249
+ request
2250
+ ]);
2251
+ if (!(response instanceof Response)) {
2252
+ throw new TypeError(`Compatibility producer ${exportName} returned a non-Response value.`);
2253
+ }
2254
+ return response;
2255
+ }
2256
+ };
2257
+ };
2258
+ var loadAbsoluteMobileMaterializedBundle = async (root) => {
2259
+ const resolvedRoot = resolvePath2(root);
2260
+ const serialized = await readFile8(join7(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
2261
+ const parsed = JSON.parse(serialized);
2262
+ const index = parseBundleIndex(parsed);
2263
+ const bundleRoot = join7(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
2264
+ return {
2265
+ artifacts: index.releases,
2266
+ currentReleaseId: index.currentReleaseId,
2267
+ loadProducer: async (artifact) => {
2268
+ const modulePath = join7(bundleRoot, artifact.releaseId, artifact.producer.module);
2269
+ await verifyAbsoluteMobileCompatibilityProducer({
2270
+ artifact,
2271
+ producer: Bun.file(modulePath)
2272
+ });
2273
+ const loaded = await readCompatibilityModule(modulePath);
2274
+ return resolveProducerHandler(loaded, artifact.producer.exportName);
2275
+ }
2276
+ };
2277
+ };
2278
+ var materializeAbsoluteMobileCompatibilityBundle = async (input) => {
2279
+ const releases = await Promise.all(input.releases.map((release) => verifyAbsoluteMobileCompatibilityProducer(release)));
2280
+ const artifacts = retainAbsoluteMobileCompatibilityArtifacts(releases.map(({ artifact }) => artifact));
2281
+ if (artifacts.length !== releases.length) {
2282
+ throw new TypeError("Materialization input must already contain only retained releases.");
2283
+ }
2284
+ if (artifacts[0]?.releaseId !== input.currentReleaseId) {
2285
+ throw new TypeError("Materialization current release must be the newest generation.");
2286
+ }
2287
+ const orderedReleases = artifacts.map((artifact) => {
2288
+ const release = releases.find((candidate) => candidate.artifact.releaseId === artifact.releaseId);
2289
+ if (!release) {
2290
+ throw new TypeError("Materialization input is missing a producer.");
2291
+ }
2292
+ return release;
2293
+ });
2294
+ const root = resolvePath2(input.root);
2295
+ const bundlesRoot = join7(root, BUNDLES_DIRECTORY);
2296
+ await mkdir6(bundlesRoot, { recursive: true });
2297
+ const bundleId = bundleIdFor(input.currentReleaseId, artifacts);
2298
+ await installImmutableBundle(bundlesRoot, bundleId, orderedReleases);
2299
+ const index = {
2300
+ bundleId,
2301
+ currentReleaseId: input.currentReleaseId,
2302
+ format: ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT,
2303
+ releases: artifacts
2304
+ };
2305
+ const pointerPath = join7(root, CURRENT_BUNDLE_FILE);
2306
+ const temporaryPointerPath = join7(root, `.current-${crypto.randomUUID()}.json`);
2307
+ await writeFile7(temporaryPointerPath, `${JSON.stringify(index, null, "\t")}
2308
+ `, { flag: "wx" });
2309
+ await rename7(temporaryPointerPath, pointerPath);
2310
+ return index;
2311
+ };
2312
+ var readAbsoluteMobileMaterializedReleases = async (root) => {
2313
+ const resolvedRoot = resolvePath2(root);
2314
+ try {
2315
+ const serialized = await readFile8(join7(resolvedRoot, CURRENT_BUNDLE_FILE), "utf8");
2316
+ const parsed = JSON.parse(serialized);
2317
+ const index = parseBundleIndex(parsed);
2318
+ const bundleRoot = join7(resolvedRoot, BUNDLES_DIRECTORY, index.bundleId);
2319
+ return Promise.all(index.releases.map(async (artifact) => {
2320
+ const producer = Bun.file(join7(bundleRoot, artifact.releaseId, artifact.producer.module));
2321
+ await verifyAbsoluteMobileCompatibilityProducer({
2322
+ artifact,
2323
+ producer
2324
+ });
2325
+ return { artifact, producer };
2326
+ }));
2327
+ } catch (error) {
2328
+ if (errorHasCode3(error, "ENOENT"))
2329
+ return [];
2330
+ throw error;
2331
+ }
2332
+ };
2333
+
2334
+ // src/mobile/buildPipeline.ts
2335
+ var isElysiaApp = (value) => typeof value === "object" && value !== null && typeof Reflect.get(value, "compile") === "function" && Array.isArray(Reflect.get(value, "routes"));
2336
+ var isStringRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && Object.values(value).every((entry) => typeof entry === "string");
2337
+ var serverExportName = (loaded, app) => {
2338
+ if (loaded.server === app)
2339
+ return "server";
2340
+ if (loaded.app === app)
2341
+ return "app";
2342
+ return "default";
2343
+ };
2344
+ var restoreBuildDirectory = (previous) => {
2345
+ if (previous !== undefined) {
2346
+ process.env.ABSOLUTE_BUILD_DIR = previous;
2347
+ return;
2348
+ }
2349
+ delete process.env.ABSOLUTE_BUILD_DIR;
2350
+ };
2351
+ var requireRelease = (releases, releaseId) => {
2352
+ const release = releases.get(releaseId);
2353
+ if (!release) {
2354
+ throw new TypeError(`Missing retained mobile release ${releaseId}.`);
2355
+ }
2356
+ return release;
2357
+ };
2358
+ var loadServerApp = async (producerPath) => {
2359
+ const loaded = await import(`${pathToFileURL2(producerPath).href}?absolute-mobile-capture=${crypto.randomUUID()}`);
2360
+ const candidates = [loaded.server, loaded.app, loaded.default];
2361
+ const app = candidates.find(isElysiaApp);
2362
+ if (!app) {
2363
+ throw new TypeError("Mobile builds require the server entry to export its Elysia app as server, app, or default.");
2364
+ }
2365
+ const exportName = serverExportName(loaded, app);
2366
+ return { app, exportName };
2367
+ };
2368
+ var finalizeAbsoluteMobileCompatibilityBuild = async (options) => {
2369
+ const buildDirectory = resolve8(options.buildDirectory);
2370
+ const mobile = normalizeAbsoluteMobileConfig(options.mobile, options.projectRoot);
2371
+ const root = join8(buildDirectory, ".absolutejs", "mobile-compatibility");
2372
+ const [manifestSource, previous] = await Promise.all([
2373
+ readFile9(join8(buildDirectory, "manifest.json"), "utf8"),
2374
+ readAbsoluteMobileMaterializedReleases(root)
2375
+ ]);
2376
+ const manifest = JSON.parse(manifestSource);
2377
+ if (!isStringRecord(manifest)) {
2378
+ throw new TypeError("Invalid AbsoluteJS build manifest for mobile capture.");
2379
+ }
2380
+ const previousBuildDirectory = process.env.ABSOLUTE_BUILD_DIR;
2381
+ process.env.ABSOLUTE_BUILD_DIR = buildDirectory;
2382
+ let loaded;
2383
+ try {
2384
+ loaded = await loadServerApp(resolve8(options.producerPath));
2385
+ } finally {
2386
+ restoreBuildDirectory(previousBuildDirectory);
2387
+ }
2388
+ const current = await buildAbsoluteMobileCompatibilityRelease({
2389
+ app: loaded.app,
2390
+ appId: mobile.appId,
2391
+ buildDirectory,
2392
+ manifest,
2393
+ previousArtifacts: previous.map(({ artifact }) => artifact),
2394
+ producerExport: loaded.exportName,
2395
+ producerPath: resolve8(options.producerPath),
2396
+ runtime: String(ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION)
2397
+ });
2398
+ const releasesById = new Map([current, ...previous].map((release) => [
2399
+ release.artifact.releaseId,
2400
+ release
2401
+ ]));
2402
+ const retained = retainAbsoluteMobileCompatibilityArtifacts([...releasesById.values()].map(({ artifact }) => artifact));
2403
+ await materializeAbsoluteMobileCompatibilityBundle({
2404
+ currentReleaseId: current.artifact.releaseId,
2405
+ releases: retained.map((artifact) => requireRelease(releasesById, artifact.releaseId)),
2406
+ root
2407
+ });
2408
+ await materializeAbsoluteCapacitorWebBundle({
2409
+ artifact: current.artifact,
2410
+ buildDirectory,
2411
+ config: mobile
2412
+ });
2413
+ return current.artifact;
2414
+ };
2415
+ // src/mobile/compatibilityDispatcher.ts
2416
+ import { Elysia as Elysia2 } from "elysia";
2417
+
2418
+ // src/mobile/producerContext.ts
2419
+ import { AsyncLocalStorage } from "async_hooks";
2420
+ var isProducerStorage = (value) => typeof value === "object" && value !== null && ("getStore" in value) && typeof value.getStore === "function" && ("run" in value) && typeof value.run === "function";
2421
+ var getProducerStorage = () => {
2422
+ const value = Reflect.get(globalThis, ABSOLUTE_MOBILE_PRODUCER_STORAGE_KEY);
2423
+ return isProducerStorage(value) ? value : undefined;
2424
+ };
2425
+ var ensureProducerStorage = () => {
2426
+ const existing = getProducerStorage();
2427
+ if (existing)
2428
+ return existing;
2429
+ const storage = new AsyncLocalStorage;
2430
+ Reflect.set(globalThis, ABSOLUTE_MOBILE_PRODUCER_STORAGE_KEY, storage);
2431
+ return storage;
2432
+ };
2433
+ var runWithAbsoluteMobileProducer = (context, callback) => ensureProducerStorage().run(context, callback);
2434
+
2435
+ // src/mobile/compatibilityDispatcher.ts
2436
+ var artifactOwnsRequest = (artifact, pageId, request) => {
2437
+ const { pathname } = new URL(request.url);
2438
+ return artifact.routes.some((route) => route.pageId === pageId && route.method === request.method && matchesAbsoluteMobileRoutePattern(route.pattern, pathname));
2439
+ };
2440
+ var createProducerResolver = (loadProducer) => {
2441
+ const producers = new Map;
2442
+ return (artifact) => {
2443
+ const cached = producers.get(artifact.releaseId);
2444
+ if (cached)
2445
+ return cached;
2446
+ const loading = loadProducer(artifact).catch((error) => {
2447
+ producers.delete(artifact.releaseId);
2448
+ throw error;
2449
+ });
2450
+ producers.set(artifact.releaseId, loading);
2451
+ return loading;
2452
+ };
2453
+ };
2454
+ var createAbsoluteMobileCompatibilityDispatcher = (options) => {
2455
+ const artifacts = retainAbsoluteMobileCompatibilityArtifacts(options.artifacts);
2456
+ if (!artifacts.some(({ releaseId }) => releaseId === options.currentReleaseId)) {
2457
+ throw new TypeError("currentReleaseId must identify a retained compatibility artifact.");
2458
+ }
2459
+ const resolveProducer = createProducerResolver(options.loadProducer);
2460
+ return new Elysia2({ name: "absolutejs-mobile-compatibility-dispatcher" }).request(async ({ request }) => {
2461
+ if (getCurrentAbsoluteMobileProducerContext())
2462
+ return;
2463
+ const parsed = parseAbsoluteMobilePageRequest(request);
2464
+ if (parsed.kind !== "mobile")
2465
+ return;
2466
+ const resolved = resolveAbsoluteMobileCompatibilityRelease(parsed.client, artifacts);
2467
+ if (resolved.kind === "upgrade-required") {
2468
+ return createAbsoluteMobileUpgradeResponse(resolved.result);
2469
+ }
2470
+ if (!artifactOwnsRequest(resolved.artifact, parsed.client.pageId, request)) {
2471
+ return createAbsoluteMobileInvalidRequestResponse("The requested URL is not assigned to this mobile page.");
2472
+ }
2473
+ if (resolved.artifact.releaseId === options.currentReleaseId) {
2474
+ return;
2475
+ }
2476
+ try {
2477
+ const producer = await resolveProducer(resolved.artifact);
2478
+ return runWithAbsoluteMobileProducer({
2479
+ page: resolved.page,
2480
+ releaseId: resolved.artifact.releaseId
2481
+ }, () => producer.handle(request));
2482
+ } catch (error) {
2483
+ console.error(`[Mobile] Failed to load retained producer ${resolved.artifact.releaseId}:`, error);
2484
+ return createAbsoluteMobilePageErrorResponse(parsed.client.pageId);
2485
+ }
2486
+ }).as("global");
2487
+ };
2488
+ // src/mobile/nativeDeepLinks.ts
2489
+ import { readFile as readFile10, rename as rename8, writeFile as writeFile8 } from "fs/promises";
2490
+ import { join as join9 } from "path";
2491
+ var START_MARKER = "<!-- absolutejs:deep-links:start -->";
2492
+ var END_MARKER = "<!-- absolutejs:deep-links:end -->";
2493
+ var IOS_ENTITLEMENTS = "App/AbsoluteJS.entitlements";
2494
+ var NOT_FOUND = -1;
2495
+ var escapeXml = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("'", "&apos;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
2496
+ var writeChangedFile = async (path, source) => {
2497
+ const current = await readFile10(path, "utf8");
2498
+ if (current === source)
2499
+ return false;
2500
+ const temporary = `${path}.${crypto.randomUUID()}.tmp`;
2501
+ await writeFile8(temporary, source, { flag: "wx" });
2502
+ await rename8(temporary, path);
2503
+ return true;
2504
+ };
2505
+ var replaceManagedRegion = (source, region, insertAt) => {
2506
+ const start = source.indexOf(START_MARKER);
2507
+ const end = source.indexOf(END_MARKER);
2508
+ if (start === NOT_FOUND !== (end === NOT_FOUND) || start !== NOT_FOUND && end < start) {
2509
+ throw new TypeError("AbsoluteJS deep-link ownership markers are malformed.");
2510
+ }
2511
+ if (start !== NOT_FOUND) {
2512
+ const lineStart = source.lastIndexOf(`
2513
+ `, start) + 1;
2514
+ const nextLine = source.indexOf(`
2515
+ `, end + END_MARKER.length);
2516
+ const lineEnd = nextLine === NOT_FOUND ? source.length : nextLine + 1;
2517
+ return `${source.slice(0, lineStart)}${region}${source.slice(lineEnd)}`;
2518
+ }
2519
+ const index = insertAt();
2520
+ if (index === NOT_FOUND) {
2521
+ throw new TypeError("Could not find a safe native project location for deep-link configuration.");
2522
+ }
2523
+ return `${source.slice(0, index)}${region}${source.slice(index)}`;
2524
+ };
2525
+ var androidRegion = (config) => {
2526
+ const hosts = config.deepLinkHosts.map((host) => ` <data android:scheme="https" android:host="${escapeXml(host)}" />`).join(`
2527
+ `);
2528
+ const customScheme = config.deepLinkScheme ? `
2529
+
2530
+ <intent-filter>
2531
+ <action android:name="android.intent.action.VIEW" />
2532
+ <category android:name="android.intent.category.DEFAULT" />
2533
+ <category android:name="android.intent.category.BROWSABLE" />
2534
+ <data android:scheme="${escapeXml(config.deepLinkScheme)}" />
2535
+ </intent-filter>` : "";
2536
+ return ` ${START_MARKER}
2537
+ <intent-filter android:autoVerify="true">
2538
+ <action android:name="android.intent.action.VIEW" />
2539
+ <category android:name="android.intent.category.DEFAULT" />
2540
+ <category android:name="android.intent.category.BROWSABLE" />
2541
+ ${hosts}
2542
+ </intent-filter>${customScheme}
2543
+ ${END_MARKER}
2544
+ `;
2545
+ };
2546
+ var configureAndroid = async (config) => {
2547
+ const path = join9(config.nativeProjectDirectory, "android/app/src/main/AndroidManifest.xml");
2548
+ const source = await readFile10(path, "utf8");
2549
+ const mainActivity = source.indexOf('android:name=".MainActivity"');
2550
+ if (mainActivity === NOT_FOUND) {
2551
+ throw new TypeError("Android MainActivity was not found.");
2552
+ }
2553
+ const activityEnd = source.indexOf("</activity>", mainActivity);
2554
+ const updated = replaceManagedRegion(source, androidRegion(config), () => activityEnd === NOT_FOUND ? NOT_FOUND : source.lastIndexOf(`
2555
+ `, activityEnd) + 1);
2556
+ return writeChangedFile(path, updated);
2557
+ };
2558
+ var iosSchemeRegion = (scheme) => ` ${START_MARKER}
2559
+ <key>CFBundleURLTypes</key>
2560
+ <array>
2561
+ <dict>
2562
+ <key>CFBundleURLName</key>
2563
+ <string>absolutejs</string>
2564
+ <key>CFBundleURLSchemes</key>
2565
+ <array>
2566
+ <string>${escapeXml(scheme)}</string>
2567
+ </array>
2568
+ </dict>
2569
+ </array>
2570
+ ${END_MARKER}
2571
+ `;
2572
+ var configureIosInfo = async (config) => {
2573
+ const path = join9(config.nativeProjectDirectory, "ios/App/App/Info.plist");
2574
+ const source = await readFile10(path, "utf8");
2575
+ const region = config.deepLinkScheme ? iosSchemeRegion(config.deepLinkScheme) : ` ${START_MARKER}
2576
+ ${END_MARKER}
2577
+ `;
2578
+ const updated = replaceManagedRegion(source, region, () => source.lastIndexOf("</dict>"));
2579
+ return writeChangedFile(path, updated);
2580
+ };
2581
+ var iosEntitlementsSource = (config) => {
2582
+ const domains = config.deepLinkHosts.map((host) => ` <string>applinks:${escapeXml(host)}</string>`).join(`
2583
+ `);
2584
+ return `<?xml version="1.0" encoding="UTF-8"?>
2585
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
2586
+ <plist version="1.0">
2587
+ <dict>
2588
+ <key>com.apple.developer.associated-domains</key>
2589
+ <array>
2590
+ ${domains}
2591
+ </array>
2592
+ </dict>
2593
+ </plist>
2594
+ `;
2595
+ };
2596
+ var configureIosEntitlements = async (config) => {
2597
+ const path = join9(config.nativeProjectDirectory, "ios/App/AbsoluteJS.entitlements");
2598
+ let current = "";
2599
+ try {
2600
+ current = await readFile10(path, "utf8");
2601
+ } catch (error) {
2602
+ if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") {
2603
+ throw error;
2604
+ }
2605
+ }
2606
+ const source = iosEntitlementsSource(config);
2607
+ if (current === source)
2608
+ return false;
2609
+ const temporary = `${path}.${crypto.randomUUID()}.tmp`;
2610
+ await writeFile8(temporary, source, { flag: "wx" });
2611
+ await rename8(temporary, path);
2612
+ return true;
2613
+ };
2614
+ var configureIosProject = async (config) => {
2615
+ const path = join9(config.nativeProjectDirectory, "ios/App/App.xcodeproj/project.pbxproj");
2616
+ const source = await readFile10(path, "utf8");
2617
+ const declarations = [
2618
+ ...source.matchAll(/CODE_SIGN_ENTITLEMENTS = ([^;]+);/g)
2619
+ ].map((match) => match[1]);
2620
+ if (declarations.some((value) => value !== IOS_ENTITLEMENTS)) {
2621
+ throw new TypeError("The iOS target already uses a different entitlements file; merge associated domains there before continuing.");
2622
+ }
2623
+ if (declarations.length > 0)
2624
+ return false;
2625
+ let count = 0;
2626
+ const updated = source.replaceAll("INFOPLIST_FILE = App/Info.plist;", () => {
2627
+ count += 1;
2628
+ return `CODE_SIGN_ENTITLEMENTS = ${IOS_ENTITLEMENTS};
2629
+ INFOPLIST_FILE = App/Info.plist;`;
2630
+ });
2631
+ if (count === 0) {
2632
+ throw new TypeError("The iOS application target build settings were not found.");
2633
+ }
2634
+ return writeChangedFile(path, updated);
2635
+ };
2636
+ var configureIos = async (config) => {
2637
+ const changed = await Promise.all([
2638
+ configureIosInfo(config),
2639
+ configureIosEntitlements(config),
2640
+ configureIosProject(config)
2641
+ ]);
2642
+ return changed.some(Boolean);
2643
+ };
2644
+ var applyAbsoluteNativeDeepLinks = async (config, platforms = config.platforms) => {
2645
+ const results = await Promise.all(platforms.map(async (platform) => {
2646
+ const didChange = platform === "android" ? await configureAndroid(config) : await configureIos(config);
2647
+ return { didChange, platform };
2648
+ }));
2649
+ return {
2650
+ changed: results.filter(({ didChange }) => didChange).map(({ platform }) => platform)
2651
+ };
2652
+ };
2653
+ // src/mobile/routeMetadataTransform.ts
2654
+ import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
2655
+ import { dirname as dirname5, extname as extname2, relative as relative5, resolve as resolve9 } from "path";
2656
+ import ts from "typescript";
2657
+ var ROUTE_METHODS = new Set(["get", "head"]);
2658
+ var SOURCE_FILTER = /\.[cm]?[jt]sx?$/;
2659
+ var PAGE_HANDLER = "handleReactPageRequest";
2660
+ var posixPath = (value) => value.replace(/\\/g, "/");
2661
+ var findTsconfig = (entry, projectRoot) => ts.findConfigFile(dirname5(entry), existsSync3, "tsconfig.json") ?? ts.findConfigFile(projectRoot, existsSync3, "tsconfig.json");
2662
+ var createProgram = (entry, projectRoot) => {
2663
+ const configPath = findTsconfig(entry, projectRoot);
2664
+ if (!configPath) {
2665
+ return ts.createProgram([entry], {
2666
+ allowJs: true,
2667
+ jsx: ts.JsxEmit.ReactJSX,
2668
+ module: ts.ModuleKind.ESNext,
2669
+ moduleResolution: ts.ModuleResolutionKind.Bundler,
2670
+ target: ts.ScriptTarget.ESNext
2671
+ });
2672
+ }
2673
+ const parsed = ts.parseJsonConfigFileContent(ts.readConfigFile(configPath, (path) => readFileSync2(path, "utf8")).config, ts.sys, dirname5(configPath));
2674
+ if (!parsed.fileNames.includes(entry))
2675
+ parsed.fileNames.push(entry);
2676
+ return ts.createProgram(parsed.fileNames, parsed.options);
2677
+ };
2678
+ var propertyName = (property) => {
2679
+ if (!("name" in property) || !property.name)
2680
+ return;
2681
+ if (ts.isIdentifier(property.name))
2682
+ return property.name.text;
2683
+ if (ts.isStringLiteralLike(property.name))
2684
+ return property.name.text;
2685
+ return;
2686
+ };
2687
+ var objectPropertyExpression = (object, name) => {
2688
+ const property = object.properties.find((candidate) => propertyName(candidate) === name);
2689
+ if (property && ts.isPropertyAssignment(property)) {
2690
+ return property.initializer;
2691
+ }
2692
+ if (property && ts.isShorthandPropertyAssignment(property)) {
2693
+ return property.name;
2694
+ }
2695
+ return;
2696
+ };
2697
+ var serializeType = (type, checker, ancestors = new Set) => {
2698
+ if (type.flags & ts.TypeFlags.Any)
2699
+ return { type: "any" };
2700
+ if (type.flags & ts.TypeFlags.Unknown)
2701
+ return { type: "unknown" };
2702
+ if (type.flags & ts.TypeFlags.Never)
2703
+ return { type: "never" };
2704
+ if (type.flags & ts.TypeFlags.StringLike)
2705
+ return { type: "string" };
2706
+ if (type.flags & ts.TypeFlags.NumberLike)
2707
+ return { type: "number" };
2708
+ if (type.flags & ts.TypeFlags.BooleanLike)
2709
+ return { type: "boolean" };
2710
+ if (type.flags & ts.TypeFlags.BigIntLike)
2711
+ return { type: "bigint" };
2712
+ if (type.flags & ts.TypeFlags.Null)
2713
+ return { type: "null" };
2714
+ if (type.flags & ts.TypeFlags.Undefined)
2715
+ return { type: "undefined" };
2716
+ if (type.isUnion()) {
2717
+ return {
2718
+ anyOf: type.types.map((member) => serializeType(member, checker, ancestors)).sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)))
2719
+ };
2720
+ }
2721
+ if (type.isIntersection()) {
2722
+ return {
2723
+ allOf: type.types.map((member) => serializeType(member, checker, ancestors)).sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)))
2724
+ };
2725
+ }
2726
+ if (ancestors.has(type)) {
2727
+ return {
2728
+ ref: checker.typeToString(type, undefined, ts.TypeFormatFlags.NoTruncation)
2729
+ };
2730
+ }
2731
+ ancestors.add(type);
2732
+ const arrayElement = checker.getIndexTypeOfType(type, ts.IndexKind.Number);
2733
+ const properties = checker.getPropertiesOfType(type);
2734
+ let schema;
2735
+ if (arrayElement && properties.some(({ name }) => name === "length")) {
2736
+ schema = {
2737
+ items: serializeType(arrayElement, checker, ancestors),
2738
+ type: "array"
2739
+ };
2740
+ } else if (properties.length > 0) {
2741
+ const entries = properties.filter(({ name }) => !name.startsWith("__")).map((property) => {
2742
+ const declaration = property.valueDeclaration ?? property.declarations?.[0];
2743
+ const propertyType = declaration ? checker.getTypeOfSymbolAtLocation(property, declaration) : checker.getDeclaredTypeOfSymbol(property);
2744
+ return [
2745
+ property.name,
2746
+ {
2747
+ optional: Boolean(property.flags & ts.SymbolFlags.Optional),
2748
+ schema: serializeType(propertyType, checker, ancestors)
2749
+ }
2750
+ ];
2751
+ }).sort(([left], [right]) => left.localeCompare(right));
2752
+ schema = { properties: Object.fromEntries(entries), type: "object" };
2753
+ } else {
2754
+ schema = {
2755
+ type: checker.typeToString(type, undefined, ts.TypeFormatFlags.NoTruncation)
2756
+ };
2757
+ }
2758
+ ancestors.delete(type);
2759
+ return schema;
2760
+ };
2761
+ var pagePropsType = (pageExpression, propsExpression, checker) => {
2762
+ const pageType = checker.getTypeAtLocation(pageExpression);
2763
+ const [signature] = pageType.getCallSignatures();
2764
+ const [parameter] = signature?.parameters ?? [];
2765
+ const declaration = parameter?.valueDeclaration ?? parameter?.declarations?.[0];
2766
+ if (parameter && declaration) {
2767
+ return checker.getTypeOfSymbolAtLocation(parameter, declaration);
2768
+ }
2769
+ return propsExpression ? checker.getTypeAtLocation(propsExpression) : checker.getTypeAtLocation(pageExpression);
2770
+ };
2771
+ var resolvePageIdentity = (expression, sourceFile, checker, projectRoot) => {
2772
+ let symbol = checker.getSymbolAtLocation(expression);
2773
+ if (symbol?.flags && symbol.flags & ts.SymbolFlags.Alias) {
2774
+ symbol = checker.getAliasedSymbol(symbol);
2775
+ }
2776
+ const declaration = symbol?.declarations?.[0];
2777
+ const file = declaration?.getSourceFile().fileName ?? sourceFile.fileName;
2778
+ const exportedName = symbol?.name ?? expression.getText(sourceFile);
2779
+ const source = posixPath(relative5(projectRoot, file));
2780
+ return `${source}#${exportedName}`;
2781
+ };
2782
+ var resolveAlias = (symbol, checker) => {
2783
+ if (!(symbol.flags & ts.SymbolFlags.Alias))
2784
+ return symbol;
2785
+ return checker.getAliasedSymbol(symbol);
2786
+ };
2787
+ var assetKey = (expression, checker, seen = new Set) => {
2788
+ if (!expression)
2789
+ return;
2790
+ if (ts.isIdentifier(expression)) {
2791
+ const unresolved = ts.isShorthandPropertyAssignment(expression.parent) ? checker.getShorthandAssignmentValueSymbol(expression.parent) : checker.getSymbolAtLocation(expression);
2792
+ if (!unresolved)
2793
+ return;
2794
+ const symbol = resolveAlias(unresolved, checker);
2795
+ if (seen.has(symbol))
2796
+ return;
2797
+ seen.add(symbol);
2798
+ const declaration = symbol.valueDeclaration ?? symbol.declarations?.[0];
2799
+ if (!declaration || !ts.isVariableDeclaration(declaration))
2800
+ return;
2801
+ return assetKey(declaration.initializer, checker, seen);
2802
+ }
2803
+ if (!ts.isCallExpression(expression))
2804
+ return;
2805
+ if (!ts.isIdentifier(expression.expression) || expression.expression.text !== "asset") {
2806
+ return;
2807
+ }
2808
+ const [, key] = expression.arguments;
2809
+ return key && ts.isStringLiteralLike(key) ? key.text : undefined;
2810
+ };
2811
+ var findPageCall = (nodes) => {
2812
+ let found;
2813
+ const visit = (candidate) => {
2814
+ if (found)
2815
+ return;
2816
+ if (ts.isCallExpression(candidate) && ts.isIdentifier(candidate.expression) && candidate.expression.text === PAGE_HANDLER) {
2817
+ found = candidate;
2818
+ return;
2819
+ }
2820
+ ts.forEachChild(candidate, visit);
2821
+ };
2822
+ for (const node of nodes)
2823
+ visit(node);
2824
+ return found;
2825
+ };
2826
+ var isProjectSource = (sourceFile, resolvedFile, projectRoot) => !sourceFile.isDeclarationFile && !resolvedFile.includes("/node_modules/") && resolvedFile.startsWith(`${projectRoot}/`);
2827
+ var analyzeRouteCall = (node, sourceFile, checker, projectRoot) => {
2828
+ const callee = node.expression;
2829
+ if (!ts.isPropertyAccessExpression(callee))
2830
+ return;
2831
+ if (!ROUTE_METHODS.has(callee.name.text))
2832
+ return;
2833
+ const [routePath] = node.arguments;
2834
+ if (!routePath || !ts.isStringLiteralLike(routePath))
2835
+ return;
2836
+ const pageCall = findPageCall(node.arguments.slice(1));
2837
+ const [input] = pageCall?.arguments ?? [];
2838
+ if (!pageCall || !input || !ts.isObjectLiteralExpression(input)) {
2839
+ return;
2840
+ }
2841
+ const page = objectPropertyExpression(input, "Page");
2842
+ if (!page)
2843
+ return;
2844
+ const props = objectPropertyExpression(input, "props");
2845
+ const index = objectPropertyExpression(input, "index");
2846
+ const bundleKey = assetKey(index, checker);
2847
+ if (!bundleKey)
2848
+ return;
2849
+ const pageId = resolvePageIdentity(page, sourceFile, checker, projectRoot);
2850
+ const schema = serializeType(pagePropsType(page, props, checker), checker);
2851
+ const propsSchemaHash = hashAbsoluteMobilePropsSchema(schema);
2852
+ const metadata = {
2853
+ bundleKey,
2854
+ contract: `react:${pageId}:${propsSchemaHash}`,
2855
+ framework: "react",
2856
+ pageId,
2857
+ propsSchemaHash
2858
+ };
2859
+ const result = {
2860
+ metadata,
2861
+ pageCallStart: pageCall.getStart(sourceFile),
2862
+ routeCallSpan: `${node.getStart(sourceFile)}:${node.end}`
2863
+ };
2864
+ return result;
2865
+ };
2866
+ var analyzeSourceFile = (sourceFile, checker, projectRoot) => {
2867
+ const analysis = {
2868
+ byPageCall: new Map,
2869
+ byRouteCall: new Map
2870
+ };
2871
+ const visit = (node) => {
2872
+ const result = ts.isCallExpression(node) ? analyzeRouteCall(node, sourceFile, checker, projectRoot) : undefined;
2873
+ if (result) {
2874
+ analysis.byPageCall.set(result.pageCallStart, result);
2875
+ analysis.byRouteCall.set(result.routeCallSpan, result);
2876
+ }
2877
+ ts.forEachChild(node, visit);
2878
+ };
2879
+ ts.forEachChild(sourceFile, visit);
2880
+ return analysis;
2881
+ };
2882
+ var analyzeProgram = (program, projectRoot) => {
2883
+ const checker = program.getTypeChecker();
2884
+ const analyzed = new Map;
2885
+ for (const sourceFile of program.getSourceFiles()) {
2886
+ const resolvedFile = resolve9(sourceFile.fileName);
2887
+ if (!isProjectSource(sourceFile, resolvedFile, projectRoot))
2888
+ continue;
2889
+ const analysis = analyzeSourceFile(sourceFile, checker, projectRoot);
2890
+ if (analysis.byPageCall.size > 0)
2891
+ analyzed.set(resolvedFile, analysis);
2892
+ }
2893
+ return analyzed;
2894
+ };
2895
+ var metadataExpression = (metadata) => ts.factory.createObjectLiteralExpression(Object.entries(metadata).map(([key, item]) => ts.factory.createPropertyAssignment(ts.factory.createStringLiteral(key), ts.factory.createStringLiteral(item))), false);
2896
+ var routeOptions = (existing, metadata) => {
2897
+ const detail = ts.factory.createObjectLiteralExpression([
2898
+ ts.factory.createPropertyAssignment(ts.factory.createStringLiteral(ABSOLUTE_MOBILE_ROUTE_DETAIL), metadataExpression(metadata))
2899
+ ]);
2900
+ if (!existing) {
2901
+ return ts.factory.createObjectLiteralExpression([
2902
+ ts.factory.createPropertyAssignment("detail", detail)
2903
+ ]);
2904
+ }
2905
+ return ts.factory.createObjectLiteralExpression([
2906
+ ts.factory.createSpreadAssignment(existing),
2907
+ ts.factory.createPropertyAssignment("detail", ts.factory.createObjectLiteralExpression([
2908
+ ts.factory.createSpreadAssignment(ts.factory.createPropertyAccessExpression(existing, "detail")),
2909
+ ts.factory.createPropertyAssignment(ts.factory.createStringLiteral(ABSOLUTE_MOBILE_ROUTE_DETAIL), metadataExpression(metadata))
2910
+ ]))
2911
+ ]);
2912
+ };
2913
+ var transformPageCall = (node, page) => {
2914
+ if (!page)
2915
+ return;
2916
+ const [input] = node.arguments;
2917
+ if (!input || !ts.isObjectLiteralExpression(input))
2918
+ return;
2919
+ return ts.factory.updateCallExpression(node, node.expression, node.typeArguments, [
2920
+ ts.factory.updateObjectLiteralExpression(input, [
2921
+ ...input.properties,
2922
+ ts.factory.createPropertyAssignment("__absoluteMobile", metadataExpression(page.metadata))
2923
+ ]),
2924
+ ...node.arguments.slice(1)
2925
+ ]);
2926
+ };
2927
+ var transformRouteCall = (node, route) => {
2928
+ if (!route)
2929
+ return;
2930
+ const [path, maybeOptions, maybeHandler, ...rest] = node.arguments;
2931
+ if (!path || !maybeOptions)
2932
+ return;
2933
+ const options = maybeHandler ? maybeOptions : undefined;
2934
+ const handler = maybeHandler ?? maybeOptions;
2935
+ return ts.factory.updateCallExpression(node, node.expression, node.typeArguments, [path, routeOptions(options, route.metadata), handler, ...rest]);
2936
+ };
2937
+ var transformFile = (source, fileName, analysis) => {
2938
+ const sourceFile = ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, true, fileName.endsWith("x") ? ts.ScriptKind.TSX : ts.ScriptKind.TS);
2939
+ const transformer = (context) => {
2940
+ const visit = (node) => {
2941
+ if (!ts.isCallExpression(node)) {
2942
+ return ts.visitEachChild(node, visit, context);
2943
+ }
2944
+ const transformedChildren = ts.visitEachChild(node, visit, context);
2945
+ const page = analysis.byPageCall.get(node.getStart(sourceFile));
2946
+ const transformedPage = transformPageCall(transformedChildren, page);
2947
+ if (transformedPage)
2948
+ return transformedPage;
2949
+ const route = analysis.byRouteCall.get(`${node.getStart(sourceFile)}:${node.end}`);
2950
+ const transformedRoute = transformRouteCall(transformedChildren, route);
2951
+ if (transformedRoute)
2952
+ return transformedRoute;
2953
+ return transformedChildren;
2954
+ };
2955
+ return (node) => ts.visitNode(node, visit, ts.isSourceFile) ?? node;
2956
+ };
2957
+ const result = ts.transform(sourceFile, [transformer]);
2958
+ try {
2959
+ const [transformed] = result.transformed;
2960
+ if (!transformed)
2961
+ throw new TypeError("Mobile route transform failed.");
2962
+ return ts.createPrinter().printFile(transformed);
2963
+ } finally {
2964
+ result.dispose();
2965
+ }
2966
+ };
2967
+ var ABSOLUTE_MOBILE_TRANSFORM_PROTOCOL = ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION;
2968
+ var createAbsoluteMobileRouteMetadataPlugin = (options) => {
2969
+ const projectRoot = resolve9(options.projectRoot ?? process.cwd());
2970
+ const entry = resolve9(options.entry);
2971
+ const analyzed = analyzeProgram(createProgram(entry, projectRoot), projectRoot);
2972
+ return {
2973
+ name: "absolute-mobile-route-metadata",
2974
+ setup(build) {
2975
+ build.onLoad({ filter: SOURCE_FILTER }, async ({ path }) => {
2976
+ const analysis = analyzed.get(resolve9(path));
2977
+ if (!analysis)
2978
+ return;
2979
+ const source = await Bun.file(path).text();
2980
+ return {
2981
+ contents: transformFile(source, path, analysis),
2982
+ loader: extname2(path).endsWith("x") ? "tsx" : "ts"
2983
+ };
2984
+ });
2985
+ }
2986
+ };
2987
+ };
2988
+ var inspectAbsoluteMobileRouteMetadata = (options) => {
2989
+ const projectRoot = resolve9(options.projectRoot ?? process.cwd());
2990
+ const entry = resolve9(options.entry);
2991
+ const analyzed = analyzeProgram(createProgram(entry, projectRoot), projectRoot);
2992
+ return [...analyzed.entries()].flatMap(([file, analysis]) => [...analysis.byRouteCall.values()].map(({ metadata }) => ({
2993
+ file: posixPath(relative5(projectRoot, file)),
2994
+ metadata
2995
+ })));
2996
+ };
2997
+ export {
2998
+ writeAbsoluteCapacitorConfig,
2999
+ verifyAbsoluteMobileCompatibilityProducer,
3000
+ verifyAbsoluteMobileAssociationFiles,
3001
+ runWithAbsoluteMobileProducer,
3002
+ retainAbsoluteMobileCompatibilityArtifacts,
3003
+ resolveAbsoluteMobileRoute,
3004
+ resolveAbsoluteMobileDeepLink,
3005
+ resolveAbsoluteMobileCompatibilityRelease,
3006
+ readAbsoluteMobileMaterializedReleases,
3007
+ parseAbsoluteMobilePageRequest,
3008
+ parseAbsoluteMobilePageEnvelope,
3009
+ parseAbsoluteMobileCompatibilityArtifact,
3010
+ parseAbsoluteMobileBuildPageMetadata,
3011
+ normalizeAbsoluteMobileConfig,
3012
+ navigateAbsoluteMobilePage,
3013
+ materializeAbsoluteMobileCompatibilityBundle,
3014
+ materializeAbsoluteMobileAssociationFiles,
3015
+ materializeAbsoluteCapacitorWebBundle,
3016
+ matchesAbsoluteMobileRoutePattern,
3017
+ loadAbsoluteMobileMaterializedBundle,
3018
+ inspectAbsoluteMobileRouteMetadata,
3019
+ hashAbsoluteMobilePropsSchema,
3020
+ getCurrentAbsoluteMobileProducerContext,
3021
+ finalizeAbsoluteMobilePage,
3022
+ finalizeAbsoluteMobileCompatibilityBuild,
3023
+ fetchAbsoluteMobilePage,
3024
+ createAbsoluteMobileUpgradeResponse,
3025
+ createAbsoluteMobileRouteMetadataPlugin,
3026
+ createAbsoluteMobilePageRequest,
3027
+ createAbsoluteMobilePageErrorResponse,
3028
+ createAbsoluteMobileInvalidRequestResponse,
3029
+ createAbsoluteMobileFileArtifactStore,
3030
+ createAbsoluteMobileCompatibilityDispatcher,
3031
+ createAbsoluteMobileCompatibilityArtifact,
3032
+ createAbsoluteMobileBlobArtifactStore,
3033
+ createAbsoluteMobileAssociationPlugin,
3034
+ createAbsoluteMobileAssociationDocuments,
3035
+ carryForwardAbsoluteMobileCompatibilityReleases,
3036
+ captureAbsoluteMobileRouteGraph,
3037
+ buildAbsoluteMobileCompatibilityRelease,
3038
+ buildAbsoluteAndroidRelease,
3039
+ applyAbsoluteNativeDeepLinks,
3040
+ activateAbsoluteMobilePage,
3041
+ acceptsAbsoluteMobilePage,
3042
+ MOBILE_PAGE_REQUEST_HEADERS,
3043
+ AbsoluteMobilePageProtocolError,
3044
+ APPLE_ASSOCIATION_PATH,
3045
+ ANDROID_ASSOCIATION_PATH,
3046
+ ABSOLUTE_MOBILE_TRANSFORM_PROTOCOL,
3047
+ ABSOLUTE_MOBILE_ROUTE_DETAIL,
3048
+ ABSOLUTE_MOBILE_RETAINED_GENERATIONS,
3049
+ ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION,
3050
+ ABSOLUTE_MOBILE_PAGE_MEDIA_TYPE,
3051
+ ABSOLUTE_MOBILE_MATERIALIZED_BUNDLE_FORMAT,
3052
+ ABSOLUTE_MOBILE_COMPATIBILITY_FORMAT,
3053
+ ABSOLUTE_MOBILE_CLIENT_MANIFEST_FORMAT,
3054
+ ABSOLUTE_ANDROID_RELEASE_FORMAT
3055
+ };
3056
+
3057
+ //# debugId=EC6A042BA6D550B864756E2164756E21
3058
+ //# sourceMappingURL=index.js.map