@hot-updater/console 1.1.4 → 1.2.0-rc.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 (273) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/-native-builds-search-C-TGpFAm.js +1 -0
  3. package/.output/public/assets/{ConsoleFeatureUnavailable-C0KfsKWL.js → ConsoleFeatureUnavailable-CUtcXuY2.js} +1 -1
  4. package/.output/public/assets/EventDetails-Dl2RvPAk.js +1 -0
  5. package/.output/public/assets/HashValueDisplay-6lNRxDJX.js +1 -0
  6. package/.output/public/assets/InsightsPageHeader-DHhqY3DT.js +1 -0
  7. package/.output/public/assets/NotFoundPage-DZ3DxyRA.js +1 -0
  8. package/.output/public/assets/PlatformIcon-BBWQlWm9.js +1 -0
  9. package/.output/public/assets/RolloutPercentageBadge-Cxogpi8F.js +1 -0
  10. package/.output/public/assets/StoreSubmission-JpngSvHS.js +1 -0
  11. package/.output/public/assets/alert-dialog-BqYScr9y.js +1 -0
  12. package/.output/public/assets/api-P2g8dCNg.js +1 -0
  13. package/.output/public/assets/api-keys-DWQC2q6y.js +1 -0
  14. package/.output/public/assets/api-keys-FueRkF9q.js +1 -0
  15. package/.output/public/assets/arrow-left-g9cKqXuC.js +1 -0
  16. package/.output/public/assets/arrow-right-CNII8TYn.js +1 -0
  17. package/.output/public/assets/arrow-up-right-DK8Zdg40.js +1 -0
  18. package/.output/public/assets/badge-DWFcDWho.js +1 -0
  19. package/.output/public/assets/check-CXVR3s3X.js +1 -0
  20. package/.output/public/assets/chevron-right-BoUXo82O.js +1 -0
  21. package/.output/public/assets/circle-off-C7Muo0ss.js +1 -0
  22. package/.output/public/assets/{clipboard-DTkrlIMn.js → clipboard-DQbOcrL7.js} +1 -1
  23. package/.output/public/assets/console-features-api-DEXSwFtc.js +1 -0
  24. package/.output/public/assets/copy-DsuLj3oj.js +1 -0
  25. package/.output/public/assets/dialog-CNUPxNxH.js +1 -0
  26. package/.output/public/assets/{dist-dDj-R6uh.js → dist-CPlO6ko9.js} +1 -1
  27. package/.output/public/assets/download-Cf24LBde.js +1 -0
  28. package/.output/public/assets/{empty-Bf4DanOL.js → empty-B3Uwdd6k.js} +1 -1
  29. package/.output/public/assets/{field-GxF9SyUo.js → field-D-2-mnNh.js} +1 -1
  30. package/.output/public/assets/history-DqHlgSuV.js +1 -0
  31. package/.output/public/assets/index-BWU6owTG.js +10 -0
  32. package/.output/public/assets/{input-group-B4nrqIh0.js → input-group-D3OUBk4n.js} +1 -1
  33. package/.output/public/assets/insights-B-0odqpt.js +42 -0
  34. package/.output/public/assets/insights-BSVGfxSL.js +1 -0
  35. package/.output/public/assets/insights-NK5B6ReS.js +2 -0
  36. package/.output/public/assets/insights-api-BVCXJsoV.js +1 -0
  37. package/.output/public/assets/insights-search-DghCbEnk.js +1 -0
  38. package/.output/public/assets/insights-usage-rpc-CKQH0pUS.js +1 -0
  39. package/.output/public/assets/insights_.distribution-CJvgwcVZ.js +2 -0
  40. package/.output/public/assets/insights_.distribution-DblZM0mO.js +1 -0
  41. package/.output/public/assets/insights_.distribution-dvk6vYDM.js +1 -0
  42. package/.output/public/assets/installations-B674P5Z5.js +2 -0
  43. package/.output/public/assets/installations-BWxczZ2t.js +1 -0
  44. package/.output/public/assets/installations-Dv4kWQYz.js +1 -0
  45. package/.output/public/assets/{key-round-B4fI1psK.js → key-round-DwNKkHhy.js} +1 -1
  46. package/.output/public/assets/link-DTEyW70e.js +2 -0
  47. package/.output/public/assets/{list-Dtj7TtzV.js → list--NIee1FZ.js} +1 -1
  48. package/.output/public/assets/native-builds-BOC71grI.js +1 -0
  49. package/.output/public/assets/native-builds-BswZazmc.js +1 -0
  50. package/.output/public/assets/native-builds-c4BpOLAe.js +2 -0
  51. package/.output/public/assets/native-builds_._buildId-D_Spjl10.js +1 -0
  52. package/.output/public/assets/native-builds_._buildId-DtoXD8rz.js +2 -0
  53. package/.output/public/assets/plus-BReVKct-.js +1 -0
  54. package/.output/public/assets/preload-helper-B44EvKhG.js +1 -0
  55. package/.output/public/assets/{refresh-cw-DjIKjmrS.js → refresh-cw-B9HHjLej.js} +1 -1
  56. package/.output/public/assets/relativeTime-YQt84dYr.js +1 -0
  57. package/.output/public/assets/remote-config-B7ujqhLa.js +6 -0
  58. package/.output/public/assets/remote-config-WjpozBG5.js +1 -0
  59. package/.output/public/assets/remote-config-gXeH6PBW.js +1 -0
  60. package/.output/public/assets/rotate-cw-Cv2aVbYr.js +1 -0
  61. package/.output/public/assets/routes-BDdBIxYS.js +1 -0
  62. package/.output/public/assets/routes-CCD33Azv.js +2 -0
  63. package/.output/public/assets/search-BCel4Oy6.js +1 -0
  64. package/.output/public/assets/select-CBIU0_M1.js +1 -0
  65. package/.output/public/assets/{sidebar-BqX460IW.js → sidebar-Di9SN47G.js} +1 -1
  66. package/.output/public/assets/smartphone-CMaSy_ir.js +1 -0
  67. package/.output/public/assets/styles-Ccxthuz1.css +2 -0
  68. package/.output/public/assets/switch-BpdiHL_t.js +1 -0
  69. package/.output/public/assets/{table-ek1DmRHq.js → table-Bpy3SvSo.js} +1 -1
  70. package/.output/public/assets/{tabs-CgEH_fMT.js → tabs-Xmt7tZeG.js} +1 -1
  71. package/.output/public/assets/{undo-2-DxXx9Zvr.js → undo-2-CUX96_BG.js} +1 -1
  72. package/.output/public/assets/useCompositeListItem-JmyDfo3-.js +1 -0
  73. package/.output/public/assets/useInfiniteQuery-DX4WpBRG.js +1 -0
  74. package/.output/public/assets/useMutation-DbYw7boA.js +1 -0
  75. package/.output/public/assets/uuidv7-zXMtwR9Q.js +1 -0
  76. package/.output/public/assets/{valueToPercent-B44vDssN.js → valueToPercent-BLwGy_xK.js} +1 -1
  77. package/.output/server/{__23tanstack-start-server-fn-resolver-KpQ5Z_24.mjs → __23tanstack-start-server-fn-resolver-CygMPYHZ.mjs} +64 -40
  78. package/.output/server/_chunks/ssr-renderer.mjs +2 -2
  79. package/.output/server/_libs/@tanstack/react-router+[...].mjs +1 -1
  80. package/.output/server/_libs/@tanstack/react-router-ssr-query+[...].mjs +1 -1
  81. package/.output/server/_libs/lucide-react.mjs +88 -1
  82. package/.output/server/_libs/uqr.mjs +858 -0
  83. package/.output/server/_ssr/-native-builds-search-CwNcoXf4.mjs +37 -0
  84. package/.output/server/_ssr/{BundleIdDisplay-CjSnY65J.mjs → BundleIdDisplay-DOPz4pUS.mjs} +2 -2
  85. package/.output/server/_ssr/{ConsoleFeatureUnavailable-CHxn9f-w.mjs → ConsoleFeatureUnavailable-Xb1JKqcc.mjs} +7 -7
  86. package/.output/server/_ssr/{EventDetails-l6dF54zp.mjs → EventDetails-D7MejFCN.mjs} +8 -8
  87. package/.output/server/_ssr/{HashValueDisplay-Zs7ABoOi.mjs → HashValueDisplay-Dd-wu5rY.mjs} +2 -2
  88. package/.output/server/_ssr/{InsightsPageHeader-ZlAlDxQy.mjs → InsightsPageHeader-CWf1n4Vt.mjs} +13 -11
  89. package/.output/server/_ssr/{NotFoundPage-u28ndvp1.mjs → NotFoundPage-C5rFdUpg.mjs} +2 -2
  90. package/.output/server/_ssr/PlatformIcon-CFLiTjh1.mjs +29 -0
  91. package/.output/server/_ssr/RolloutPercentageBadge-g0Bhfl3M.mjs +21 -0
  92. package/.output/server/_ssr/StoreSubmission-JZ2Zltak.mjs +139 -0
  93. package/.output/server/_ssr/{alert-dialog-B8AqlJwo.mjs → alert-dialog-CvNvTvIJ.mjs} +1 -1
  94. package/.output/server/_ssr/{api-C_0B7NXM.mjs → api-0vtwA5vY.mjs} +126 -7
  95. package/.output/server/_ssr/{api-keys-pRNvn2-9.mjs → api-keys-D5dyA4D1.mjs} +10 -10
  96. package/.output/server/_ssr/{api-keys-rpc-wecWs0nA.mjs → api-keys-rpc-BOIUHhDi.mjs} +5 -5
  97. package/.output/server/_ssr/{api-keys-CLH38u9B.mjs → api-keys-yFlQDwQF.mjs} +2 -2
  98. package/.output/server/_ssr/{api-rpc-DWN8eOHS.mjs → api-rpc-COeFOpYi.mjs} +79 -12
  99. package/.output/server/_ssr/{auth-UP0wXs4i.mjs → auth-Z-wLddzh.mjs} +1 -1
  100. package/.output/server/_ssr/{auth-rpc-CLRArbIl.mjs → auth-rpc-D1ss2GUI.mjs} +6 -6
  101. package/.output/server/_ssr/{auth.server-DR9zc0EU.mjs → auth.server-DYS1OJ6P.mjs} +771 -641
  102. package/.output/server/_ssr/{badge-BpT7r-ng.mjs → badge-CEdVRgyl.mjs} +1 -1
  103. package/.output/server/_ssr/{bundleActivity-0qFIcvqt.mjs → bundleActivity-MLrjNJCT.mjs} +1 -1
  104. package/.output/server/_ssr/{config-WVX4IxSR.mjs → config-CjIBFhJX.mjs} +1 -1
  105. package/.output/server/_ssr/config.server-NnMFjMFX.mjs +43 -0
  106. package/.output/server/_ssr/{console-access-aUpQs49I.mjs → console-access-33BNlH97.mjs} +3 -3
  107. package/.output/server/_ssr/{console-features-4xAGfZHw.mjs → console-features-BTV-XuhQ.mjs} +1 -1
  108. package/.output/server/_ssr/{console-features-api-DSSgCV3v.mjs → console-features-api-v1fSycue.mjs} +6 -6
  109. package/.output/server/_ssr/{console-features-rpc-D6kMlR2A.mjs → console-features-rpc-C7XNVqm0.mjs} +5 -5
  110. package/.output/server/_ssr/createCsrfMiddleware-BwHbC8n-.mjs +159 -0
  111. package/.output/server/_ssr/createMiddleware-C3ACsfuG.mjs +24 -0
  112. package/.output/server/_ssr/{createMiddleware-B1iPez7u.mjs → createServerFn-AQvBMBjJ.mjs} +2 -23
  113. package/.output/server/_ssr/{createServerRpc-BaDJLGBx.mjs → createServerRpc-BVvRVOz5.mjs} +2 -2
  114. package/.output/server/_ssr/{databaseConfig-C5SkAyUy.mjs → databaseConfig-B_Bg7qS5.mjs} +1 -1
  115. package/.output/server/_ssr/{databaseJsonValue-Csbuzc9Q.mjs → databaseJsonValue-kS7cvEtz.mjs} +1 -1
  116. package/.output/server/_ssr/{databaseRows-CPphvbag.mjs → databaseRows-B6H8Cmgq.mjs} +3 -3
  117. package/.output/server/_ssr/{dialog-B4ALf7NJ.mjs → dialog-C8CS8RMt.mjs} +1 -1
  118. package/.output/server/_ssr/{dist-CbQbBIw-.mjs → dist-TEpM_UAD.mjs} +125 -125
  119. package/.output/server/_ssr/{downloadBundle-94jPBGSO.mjs → downloadBundle-DCmr7vsj.mjs} +1 -1
  120. package/.output/server/_ssr/{empty-Bh1FEeYF.mjs → empty-CkdEAvCY.mjs} +2 -2
  121. package/.output/server/_ssr/{empty-plugin-adapters-CpCHi1TV.mjs → empty-plugin-adapters-zrZ0xs6n.mjs} +1 -1
  122. package/.output/server/_ssr/{failureReports-Bh22wois.mjs → failureReports-Cn_zU9S8.mjs} +3 -3
  123. package/.output/server/_ssr/{field-HKCaPH3e.mjs → field-CW08sZXd.mjs} +2 -2
  124. package/.output/server/_ssr/{getBundleChildren-DfxB2K8D.mjs → getBundleChildren-rtkEEJw5.mjs} +2 -2
  125. package/.output/server/_ssr/git-CTA8Aqr0.mjs +56 -0
  126. package/.output/server/_ssr/{input-group-xm3LzDuq.mjs → input-group-D-TpFPuG.mjs} +1 -1
  127. package/.output/server/_ssr/{insights-BKZpJ_K4.mjs → insights-CMyzPs8l.mjs} +28 -27
  128. package/.output/server/_ssr/{insights-CCyTACIw.mjs → insights-CYeBl8N9.mjs} +5 -5
  129. package/.output/server/_ssr/{insights-CNqWQLJR.mjs → insights-DAi9KknT.mjs} +2 -2
  130. package/.output/server/_ssr/{insights-api-DtRFz5X3.mjs → insights-api-BFARQ5ct.mjs} +6 -6
  131. package/.output/server/_ssr/{insights-errors-Iy-lBaM7.mjs → insights-errors-7BCwFybo.mjs} +2 -2
  132. package/.output/server/_ssr/{insights-failures-BJN3RfLu.mjs → insights-failures-B9VPkzZE.mjs} +2 -2
  133. package/.output/server/_ssr/{insights-failures-rpc-DLZ91uOG.mjs → insights-failures-rpc-Q4WNNJkU.mjs} +12 -12
  134. package/.output/server/_ssr/{insights-recovery-BUuQdlA3.mjs → insights-recovery-BtXVU3ro.mjs} +1 -1
  135. package/.output/server/_ssr/{insights-recovery-rpc-BQHUhPi7.mjs → insights-recovery-rpc-CY5RYJKa.mjs} +8 -8
  136. package/.output/server/_ssr/{insights-rpc-C6Vukyn7.mjs → insights-rpc-BO7_evvs.mjs} +5 -5
  137. package/.output/server/_ssr/{insights-search-CBYoxT7v.mjs → insights-search-DKfxgHD3.mjs} +5 -5
  138. package/.output/server/_ssr/{insights-usage-CHqvtehH.mjs → insights-usage-DiGnuxW8.mjs} +1 -1
  139. package/.output/server/_ssr/{insights-usage-rpc-BJ7a-xco.mjs → insights-usage-rpc-B4czku50.mjs} +8 -8
  140. package/.output/server/_ssr/{insights-usage-rpc-C-lNUIsD.mjs → insights-usage-rpc-CUCTFska.mjs} +5 -5
  141. package/.output/server/_ssr/{insightsUsage-COUG-_-U.mjs → insightsUsage-ClAmoUz8.mjs} +3 -3
  142. package/.output/server/_ssr/{insights_.distribution-DmLW8I8a.mjs → insights_.distribution-BDeUhZGj.mjs} +2 -2
  143. package/.output/server/_ssr/{insights_.distribution-CTwilMPm.mjs → insights_.distribution-BRrbZN1X.mjs} +5 -5
  144. package/.output/server/_ssr/{insights_.distribution-DztDyrBj.mjs → insights_.distribution-C2MXpzKd.mjs} +15 -14
  145. package/.output/server/_ssr/{installations-Bct7ag5y.mjs → installations-BVk7okiY.mjs} +4 -4
  146. package/.output/server/_ssr/{installations-B6MK5iA8.mjs → installations-BjLMgBoH.mjs} +2 -2
  147. package/.output/server/_ssr/{installations-_N4PtYFl.mjs → installations-DxGZEC5G.mjs} +12 -12
  148. package/.output/server/_ssr/{listReleases-BfUBlCHo.mjs → listReleases-D_GY00SQ.mjs} +1 -1
  149. package/.output/server/_ssr/native-builds-CSCgFB21.mjs +10 -0
  150. package/.output/server/_ssr/native-builds-CdlyAldW.mjs +569 -0
  151. package/.output/server/_ssr/native-builds-DhXL5zf_.mjs +137 -0
  152. package/.output/server/_ssr/native-builds_._buildId-CMp1h8dT.mjs +10 -0
  153. package/.output/server/_ssr/native-builds_._buildId-GHpGaYyE.mjs +813 -0
  154. package/.output/server/_ssr/nativeBuildQr-84cnFQPX.mjs +59 -0
  155. package/.output/server/_ssr/nativeBuilds-B_7BPjt_.mjs +133 -0
  156. package/.output/server/_ssr/{plugins-BaD1slHx.mjs → plugins-RIryUNWj.mjs} +6 -5
  157. package/.output/server/_ssr/qr-code-C7kNRIDX.mjs +22 -0
  158. package/.output/server/_ssr/{release-adoption-DuYJTGuX.mjs → release-adoption-DoAtNPED.mjs} +2 -2
  159. package/.output/server/_ssr/{release-adoption-rpc-CHl7G_Zy.mjs → release-adoption-rpc-BJXZvtoQ.mjs} +10 -10
  160. package/.output/server/_ssr/{releaseAdoption-B39cpvfo.mjs → releaseAdoption-a2byXMfz.mjs} +4 -4
  161. package/.output/server/_ssr/releaseCatalogCompiler-DLcean2k.mjs +364 -0
  162. package/.output/server/_ssr/{releaseManagement-DFMUnDF5.mjs → releaseManagement-DHUux2az.mjs} +1 -1
  163. package/.output/server/_ssr/{releaseReachability-bFsuElSS.mjs → releaseReachability-DZOK_dH-.mjs} +2 -2
  164. package/.output/server/_ssr/{remote-config-Mj05qVYZ.mjs → remote-config-DjsEl270.mjs} +20 -19
  165. package/.output/server/_ssr/{remote-config-10-obSZ0.mjs → remote-config-DlyyohL2.mjs} +2 -2
  166. package/.output/server/_ssr/{remote-config-Dj1eTYi4.mjs → remote-config-XdYuByHz.mjs} +2 -2
  167. package/.output/server/_ssr/{remote-config-rpc-lteqDSLn.mjs → remote-config-rpc-GMLPb-90.mjs} +6 -6
  168. package/.output/server/_ssr/request-guard-CPdRBuGW.mjs +108 -0
  169. package/.output/server/_ssr/{router-Dr4jjjhx.mjs → router-vpxd56RW.mjs} +317 -71
  170. package/.output/server/_ssr/{routes-BZbrY_qw.mjs → routes-DyEM4b3x.mjs} +14 -14
  171. package/.output/server/_ssr/{routes-B5KrCDwk.mjs → routes-dQe3XhiS.mjs} +99 -81
  172. package/.output/server/_ssr/{runtime.server-DsHqVqTr.mjs → runtime.server-BdMY1blX.mjs} +4 -4
  173. package/.output/server/_ssr/schema-ChlRtZIo.mjs +23 -0
  174. package/.output/server/_ssr/schema-migration-rpc-BfGzEK_o.mjs +65 -0
  175. package/.output/server/_ssr/{select-BtdGNSmp.mjs → select-DM1uwtk9.mjs} +3 -27
  176. package/.output/server/_ssr/{server-BxaHSwHh.mjs → server-BUUKNSPI.mjs} +7 -161
  177. package/.output/server/_ssr/{sidebar-CVsgosaD.mjs → sidebar-DB2YQlok.mjs} +4 -4
  178. package/.output/server/_ssr/ssr.mjs +1 -1
  179. package/.output/server/_ssr/start-BvDsQehg.mjs +49 -0
  180. package/.output/server/_ssr/{switch-BmARgkUP.mjs → switch-DLD58zLy.mjs} +2 -2
  181. package/.output/server/_ssr/{table-M5P3YUfY.mjs → table-P4TnAZhl.mjs} +1 -1
  182. package/.output/server/_ssr/{tabs-CrmfOlJj.mjs → tabs-B7ZeLicF.mjs} +2 -2
  183. package/.output/server/_ssr/{updateFailures-z1IQy_4q.mjs → updateFailures-n-tGv90S.mjs} +3 -3
  184. package/.output/server/_ssr/{uuidv7-C88s9jWF.mjs → uuidv7-BIF4nb_q.mjs} +2 -2
  185. package/.output/server/_ssr/{schema-BiAVyEj2.mjs → version-CQ98ZBpL-CWSkTT9J.mjs} +2 -22
  186. package/.output/server/_tanstack-start-manifest_v-C-KC63Zq.mjs +184 -0
  187. package/.output/server/index.mjs +432 -271
  188. package/lib/vite-internal.mjs +32 -1
  189. package/package.json +8 -7
  190. package/src/components/AppSidebar.tsx +12 -1
  191. package/src/components/ConsoleAccessPage.tsx +49 -3
  192. package/src/components/QrCode.tsx +35 -0
  193. package/src/components/SchemaMigrationRequired.tsx +139 -0
  194. package/src/components/features/insights/BuiltinBundle.tsx +5 -3
  195. package/src/components/features/native-builds/BuiltInBundleCard.tsx +163 -0
  196. package/src/components/features/native-builds/CommitLink.tsx +30 -0
  197. package/src/components/features/native-builds/CopyableText.tsx +48 -0
  198. package/src/components/features/native-builds/ImportBuild.tsx +91 -0
  199. package/src/components/features/native-builds/InstallCard.tsx +205 -0
  200. package/src/components/features/native-builds/NativeBuildDetailPage.tsx +175 -0
  201. package/src/components/features/native-builds/NativeBuildsPage.tsx +151 -0
  202. package/src/components/features/native-builds/NativeBuildsTable.tsx +426 -0
  203. package/src/components/features/native-builds/PatchesCard.tsx +222 -0
  204. package/src/components/features/native-builds/ReleaseNativeBuildLink.tsx +101 -0
  205. package/src/components/features/native-builds/ServedNowCard.tsx +214 -0
  206. package/src/components/features/native-builds/ShortHash.tsx +41 -0
  207. package/src/components/features/native-builds/StoreSubmission.tsx +73 -0
  208. package/src/components/features/releases/ReleaseEditorSheet.tsx +8 -0
  209. package/src/lib/api-rpc.ts +98 -0
  210. package/src/lib/api.ts +120 -0
  211. package/src/lib/native-builds.ts +162 -0
  212. package/src/lib/qr-code.ts +16 -0
  213. package/src/lib/schema-migration-api.ts +54 -0
  214. package/src/lib/schema-migration-rpc.ts +26 -0
  215. package/src/lib/schema-migration.ts +101 -0
  216. package/src/lib/server/config.server.ts +23 -14
  217. package/src/lib/server/console-runtime.server.ts +4 -6
  218. package/src/lib/server/local-console.ts +164 -0
  219. package/src/lib/server/nativeBuildQr.ts +73 -0
  220. package/src/lib/server/nativeBuilds.ts +288 -0
  221. package/src/lib/shell.ts +9 -0
  222. package/src/routeTree.gen.ts +88 -1
  223. package/src/router.tsx +11 -1
  224. package/src/routes/-native-builds-search.ts +51 -0
  225. package/src/routes/[_]hot-updater/native-builds/qr.ts +14 -0
  226. package/src/routes/__root.tsx +11 -1
  227. package/src/routes/api/native-builds/$buildId/download.ts +50 -0
  228. package/src/routes/native-builds.tsx +21 -0
  229. package/src/routes/native-builds_.$buildId.tsx +23 -0
  230. package/src/start.ts +33 -0
  231. package/src/virtual-modules.d.ts +18 -0
  232. package/src/vite-internal.ts +57 -1
  233. package/.output/public/assets/EventDetails-gbD_fCqc.js +0 -1
  234. package/.output/public/assets/HashValueDisplay-BGkDPaYi.js +0 -1
  235. package/.output/public/assets/InsightsPageHeader-BRw8XkJW.js +0 -1
  236. package/.output/public/assets/NotFoundPage-Cei6W2lm.js +0 -1
  237. package/.output/public/assets/alert-dialog-BtVg50WQ.js +0 -1
  238. package/.output/public/assets/api-DoicFJfd.js +0 -1
  239. package/.output/public/assets/api-keys-D81VHHNM.js +0 -1
  240. package/.output/public/assets/api-keys-Dia6i8K1.js +0 -1
  241. package/.output/public/assets/arrow-left-DsivSewE.js +0 -1
  242. package/.output/public/assets/arrow-up-right-B5aM0F7f.js +0 -1
  243. package/.output/public/assets/badge-Bqf3UDee.js +0 -1
  244. package/.output/public/assets/circle-off-BsbZQtsF.js +0 -1
  245. package/.output/public/assets/console-features-api-DPE4C06Y.js +0 -2
  246. package/.output/public/assets/dialog-CGKPxz3n.js +0 -1
  247. package/.output/public/assets/index-DrkdiRIn.js +0 -10
  248. package/.output/public/assets/insights-C-5t8wh2.js +0 -2
  249. package/.output/public/assets/insights-D_ngQdiz.js +0 -1
  250. package/.output/public/assets/insights-KigRBDgC.js +0 -42
  251. package/.output/public/assets/insights-api-1ZjVu3Sb.js +0 -1
  252. package/.output/public/assets/insights-search-aT8BVCci.js +0 -1
  253. package/.output/public/assets/insights-usage-rpc-DqTiXI1_.js +0 -1
  254. package/.output/public/assets/insights_.distribution-Bzl6RFBR.js +0 -2
  255. package/.output/public/assets/insights_.distribution-Cthgttqb.js +0 -1
  256. package/.output/public/assets/insights_.distribution-D2c2v1bu.js +0 -1
  257. package/.output/public/assets/installations-2Nx8Htf-.js +0 -1
  258. package/.output/public/assets/installations-BskJ1xQr.js +0 -2
  259. package/.output/public/assets/installations-K2iJ_r0h.js +0 -1
  260. package/.output/public/assets/remote-config-D9X3NiwV.js +0 -6
  261. package/.output/public/assets/remote-config-DzOOiBIO.js +0 -1
  262. package/.output/public/assets/remote-config-ikg7kak0.js +0 -1
  263. package/.output/public/assets/routes-9rb9P5Y7.js +0 -2
  264. package/.output/public/assets/routes-P7683vKQ.js +0 -1
  265. package/.output/public/assets/search-DuI1FBpg.js +0 -1
  266. package/.output/public/assets/select-M3H2gxCc.js +0 -1
  267. package/.output/public/assets/smartphone-CtsHuiIC.js +0 -1
  268. package/.output/public/assets/styles-CpiW_YdW.css +0 -2
  269. package/.output/public/assets/switch-By_vfm8I.js +0 -1
  270. package/.output/public/assets/useCompositeListItem-B-UkrDlu.js +0 -1
  271. package/.output/server/_ssr/config.server-DTdsG47T.mjs +0 -35
  272. package/.output/server/_ssr/start-B6ECIE_7.mjs +0 -4
  273. package/.output/server/_tanstack-start-manifest_v-_BY9hYTm.mjs +0 -136
@@ -0,0 +1,101 @@
1
+ import type { Bundle, ReleaseRow } from "@hot-updater/plugin-core";
2
+ import { Link } from "@tanstack/react-router";
3
+ import { ArrowUpRight } from "lucide-react";
4
+
5
+ import { Badge } from "@/components/ui/badge";
6
+ import { Skeleton } from "@/components/ui/skeleton";
7
+ import { useReleaseNativeBuildQuery } from "@/lib/api";
8
+ import {
9
+ nativeBuildArtifacts,
10
+ platformLabel,
11
+ releaseNativeBuildScope,
12
+ } from "@/lib/native-builds";
13
+
14
+ /**
15
+ * The native build a release's bundle reaches, and whether that bundle has
16
+ * a patch from its built-in bundle: the newest build of the release's
17
+ * scope, one read. A release of an app version range targets many builds,
18
+ * so it shows none and reads nothing.
19
+ */
20
+ export function ReleaseNativeBuildLink({
21
+ release,
22
+ channel,
23
+ bundle,
24
+ }: {
25
+ readonly release: Pick<
26
+ ReleaseRow,
27
+ "platform" | "fingerprint_hash" | "target_app_version" | "bundle_id"
28
+ >;
29
+ readonly channel: string;
30
+ /** The release's bundle with its patches, once loaded. */
31
+ readonly bundle: Pick<Bundle, "patches"> | null | undefined;
32
+ }) {
33
+ const input =
34
+ release.bundle_id === null
35
+ ? null
36
+ : {
37
+ platform: release.platform,
38
+ channel,
39
+ fingerprintHash: release.fingerprint_hash,
40
+ targetAppVersion: release.target_app_version,
41
+ };
42
+ const scoped = input !== null && releaseNativeBuildScope(input) !== null;
43
+ const query = useReleaseNativeBuildQuery(scoped ? input : null);
44
+ if (!scoped) return null;
45
+
46
+ const build = query.data;
47
+ return (
48
+ <div className="flex flex-wrap items-center gap-2">
49
+ <span className="font-medium text-muted-foreground">Native build</span>
50
+ {query.isPending ? (
51
+ <Skeleton className="h-4 w-40" />
52
+ ) : build ? (
53
+ <>
54
+ <Link
55
+ className="inline-flex flex-wrap items-center gap-x-1 gap-y-0.5 text-xs text-foreground underline-offset-4 hover:underline"
56
+ params={{ buildId: build.id }}
57
+ to="/native-builds/$buildId"
58
+ >
59
+ {platformLabel(build.platform)}{" "}
60
+ {nativeBuildArtifacts[build.artifact_kind].label} ·{" "}
61
+ <span className="font-mono" translate="no">
62
+ {build.min_bundle_id}
63
+ </span>
64
+ <ArrowUpRight aria-hidden="true" className="size-3" />
65
+ </Link>
66
+ {bundle ? (
67
+ <Badge variant="secondary">
68
+ {(bundle.patches ?? []).some(
69
+ ({ baseBundleId }) => baseBundleId === build.min_bundle_id,
70
+ )
71
+ ? "Patches from it"
72
+ : "No patch from it"}
73
+ </Badge>
74
+ ) : null}
75
+ </>
76
+ ) : query.isError ? (
77
+ <span className="text-xs text-muted-foreground">
78
+ Couldn't load the scope's native build.{" "}
79
+ <button
80
+ className="underline underline-offset-4"
81
+ onClick={() => void query.refetch()}
82
+ type="button"
83
+ >
84
+ Retry
85
+ </button>
86
+ </span>
87
+ ) : (
88
+ <span className="text-xs text-muted-foreground">
89
+ None tracked for this scope ·{" "}
90
+ <Link
91
+ className="underline-offset-4 hover:text-foreground hover:underline"
92
+ search={{ channel }}
93
+ to="/native-builds"
94
+ >
95
+ This channel's builds
96
+ </Link>
97
+ </span>
98
+ )}
99
+ </div>
100
+ );
101
+ }
@@ -0,0 +1,214 @@
1
+ import { NUMERIC_COHORT_SIZE } from "@hot-updater/protocol";
2
+ import { Link } from "@tanstack/react-router";
3
+ import { ArrowUpRight, PackageOpen } from "lucide-react";
4
+
5
+ import { RolloutPercentageBadge } from "@/components/RolloutPercentageBadge";
6
+ import { TimestampDisplay } from "@/components/TimestampDisplay";
7
+ import { Badge } from "@/components/ui/badge";
8
+ import { buttonVariants } from "@/components/ui/button";
9
+ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
10
+ import {
11
+ Empty,
12
+ EmptyContent,
13
+ EmptyDescription,
14
+ EmptyHeader,
15
+ EmptyMedia,
16
+ EmptyTitle,
17
+ } from "@/components/ui/empty";
18
+ import {
19
+ Table,
20
+ TableBody,
21
+ TableCell,
22
+ TableHead,
23
+ TableHeader,
24
+ TableRow,
25
+ } from "@/components/ui/table";
26
+ import { formatByteSize } from "@/lib/native-builds";
27
+ import type { NativeBuildDetail } from "@/lib/server/nativeBuilds";
28
+
29
+ /** How the first OTA to a bundle downloads on this build. */
30
+ export type FirstDownload =
31
+ | { readonly kind: "patch"; readonly byteSize: number }
32
+ | { readonly kind: "full" }
33
+ /** The patches page on screen doesn't reach back to the bundle. */
34
+ | { readonly kind: "unknown" };
35
+
36
+ /**
37
+ * How an install of the build downloads `bundleId` as its first OTA: as the
38
+ * patch from the built-in bundle when one exists, else the full bundle. The
39
+ * newest page of patches decides it for every bundle down to the page's
40
+ * oldest; an older bundle is unknown only when the page is full.
41
+ */
42
+ export const firstDownloadOf = (
43
+ patches: NativeBuildDetail["patches"],
44
+ bundleId: string,
45
+ ): FirstDownload => {
46
+ const patch = patches.data.find((row) => row.bundleId === bundleId);
47
+ if (patch !== undefined) return { kind: "patch", byteSize: patch.byteSize };
48
+ const oldest = patches.data.at(-1)?.bundleId;
49
+ return patches.next !== undefined && oldest !== undefined && bundleId < oldest
50
+ ? { kind: "unknown" }
51
+ : { kind: "full" };
52
+ };
53
+
54
+ function FirstDownloadCell({ download }: { readonly download: FirstDownload }) {
55
+ switch (download.kind) {
56
+ case "patch":
57
+ return (
58
+ <span className="text-xs">
59
+ Patch{" "}
60
+ <span className="text-muted-foreground tabular-nums">
61
+ ({formatByteSize(download.byteSize)})
62
+ </span>
63
+ </span>
64
+ );
65
+ case "full":
66
+ return <span className="text-xs text-muted-foreground">Full bundle</span>;
67
+ case "unknown":
68
+ return (
69
+ <a
70
+ className="text-xs underline-offset-4 hover:underline"
71
+ href="#patches"
72
+ >
73
+ See patches
74
+ </a>
75
+ );
76
+ }
77
+ }
78
+
79
+ /**
80
+ * What the update check offers an install of the build now: its scope's
81
+ * catalog at the build's app version, down to the built-in bundle, newest
82
+ * first. It is the catalog of now, not the history.
83
+ */
84
+ export function ServedNowCard({
85
+ detail,
86
+ }: {
87
+ readonly detail: NativeBuildDetail;
88
+ }) {
89
+ const { servedNow, patches, catalogScopeKey } = detail;
90
+ const scopeLink = (
91
+ <Link
92
+ className="inline-flex items-center gap-0.5 underline-offset-4 hover:underline"
93
+ search={{ scopeKey: catalogScopeKey }}
94
+ to="/"
95
+ >
96
+ All releases in this scope
97
+ <ArrowUpRight aria-hidden="true" className="size-3" />
98
+ </Link>
99
+ );
100
+
101
+ return (
102
+ <Card className="overflow-hidden shadow-sm">
103
+ <CardHeader className="border-b px-4 py-3">
104
+ <CardTitle className="text-sm">
105
+ <h2>Served now</h2>
106
+ </CardTitle>
107
+ </CardHeader>
108
+ <CardContent className="p-0">
109
+ {servedNow.length === 0 ? (
110
+ <Empty className="py-10">
111
+ <EmptyHeader>
112
+ <EmptyMedia variant="icon">
113
+ <PackageOpen />
114
+ </EmptyMedia>
115
+ <EmptyTitle>No OTA for this build yet</EmptyTitle>
116
+ <EmptyDescription>
117
+ No release in this build's scope is newer than its built-in
118
+ bundle. Deploy one with{" "}
119
+ <code className="font-mono">hot-updater deploy</code>, and it
120
+ shows here.
121
+ </EmptyDescription>
122
+ </EmptyHeader>
123
+ <EmptyContent>
124
+ <Link
125
+ className={buttonVariants({
126
+ className: "min-h-11 sm:min-h-8",
127
+ variant: "outline",
128
+ })}
129
+ search={{ scopeKey: catalogScopeKey }}
130
+ to="/"
131
+ >
132
+ All releases in this scope
133
+ </Link>
134
+ </EmptyContent>
135
+ </Empty>
136
+ ) : (
137
+ <>
138
+ <Table aria-label="Served now" className="table-fixed">
139
+ <TableHeader>
140
+ <TableRow>
141
+ <TableHead>Bundle</TableHead>
142
+ <TableHead className="hidden w-40 md:table-cell">
143
+ Rollout
144
+ </TableHead>
145
+ <TableHead className="hidden w-32 sm:table-cell">
146
+ Created
147
+ </TableHead>
148
+ <TableHead className="w-32">First OTA download</TableHead>
149
+ </TableRow>
150
+ </TableHeader>
151
+ <TableBody>
152
+ {servedNow.map((release) => {
153
+ const bundleId = release.bundleId!;
154
+ const percentage =
155
+ (release.rolloutCohortCount / NUMERIC_COHORT_SIZE) * 100;
156
+ return (
157
+ <TableRow key={release.releaseId}>
158
+ <TableCell className="py-3 whitespace-normal">
159
+ <div className="flex min-w-0 flex-col gap-1">
160
+ <Link
161
+ className="font-mono text-xs [overflow-wrap:anywhere] underline-offset-4 hover:underline"
162
+ search={{ releaseId: release.releaseId }}
163
+ title={bundleId}
164
+ to="/"
165
+ translate="no"
166
+ >
167
+ {bundleId}
168
+ </Link>
169
+ {release.message ? (
170
+ <span className="text-xs text-muted-foreground">
171
+ {release.message}
172
+ </span>
173
+ ) : null}
174
+ </div>
175
+ </TableCell>
176
+ <TableCell className="hidden md:table-cell">
177
+ <div className="flex flex-wrap gap-1">
178
+ {release.targetCohorts.length > 0 ? (
179
+ <Badge variant="secondary">
180
+ {release.targetCohorts.length === 1
181
+ ? "1 cohort"
182
+ : `${release.targetCohorts.length} cohorts`}
183
+ </Badge>
184
+ ) : (
185
+ <RolloutPercentageBadge percentage={percentage} />
186
+ )}
187
+ {release.shouldForceUpdate ? (
188
+ <Badge variant="outline">Forced</Badge>
189
+ ) : null}
190
+ </div>
191
+ </TableCell>
192
+ <TableCell className="hidden sm:table-cell">
193
+ <TimestampDisplay uuid={bundleId} />
194
+ </TableCell>
195
+ <TableCell>
196
+ <FirstDownloadCell
197
+ download={firstDownloadOf(patches, bundleId)}
198
+ />
199
+ </TableCell>
200
+ </TableRow>
201
+ );
202
+ })}
203
+ </TableBody>
204
+ </Table>
205
+ <p className="border-t px-4 py-3 text-xs text-muted-foreground">
206
+ What the update check serves an install of this build now, newest
207
+ first: not the history. {scopeLink}
208
+ </p>
209
+ </>
210
+ )}
211
+ </CardContent>
212
+ </Card>
213
+ );
214
+ }
@@ -0,0 +1,41 @@
1
+ import { toast } from "sonner";
2
+
3
+ /**
4
+ * A hash shortened to its prefix, as git shows a commit: never cut in the
5
+ * middle, the full value on hover, and copied on click.
6
+ */
7
+ export function ShortHash({
8
+ value,
9
+ length,
10
+ label,
11
+ }: {
12
+ readonly value: string;
13
+ /** How many leading characters show, such as 7 for a commit. */
14
+ readonly length: number;
15
+ /** What the value is, for the copy button, such as "commit". */
16
+ readonly label: string;
17
+ }) {
18
+ const copy = async () => {
19
+ try {
20
+ await navigator.clipboard.writeText(value);
21
+ toast.success("Copied to clipboard", { description: value });
22
+ } catch {
23
+ toast.error(`Could not copy the ${label}`);
24
+ }
25
+ };
26
+ return (
27
+ <button
28
+ aria-label={`Copy the ${label} ${value}`}
29
+ className="cursor-copy rounded-sm font-mono text-xs whitespace-nowrap tabular-nums underline-offset-4 hover:underline focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none"
30
+ onClick={(event) => {
31
+ event.stopPropagation();
32
+ void copy();
33
+ }}
34
+ title={value}
35
+ translate="no"
36
+ type="button"
37
+ >
38
+ {value.slice(0, length)}
39
+ </button>
40
+ );
41
+ }
@@ -0,0 +1,73 @@
1
+ import type { NativeBuildRow } from "@hot-updater/plugin-core";
2
+ import dayjs from "dayjs";
3
+ import relativeTime from "dayjs/plugin/relativeTime";
4
+ import { Send } from "lucide-react";
5
+
6
+ import { Badge } from "@/components/ui/badge";
7
+
8
+ dayjs.extend(relativeTime);
9
+
10
+ /** Each store track's name as the stores call it. */
11
+ export const storeTrackLabels = {
12
+ testflight: "TestFlight",
13
+ "play-internal": "Play internal",
14
+ } as const satisfies Record<
15
+ NonNullable<NativeBuildRow["submitted_to"]>,
16
+ string
17
+ >;
18
+
19
+ type Submitted = NativeBuildRow & {
20
+ readonly submitted_to: NonNullable<NativeBuildRow["submitted_to"]>;
21
+ };
22
+
23
+ /** Whether a store took the build, as its row records. */
24
+ export const isSubmitted = (build: NativeBuildRow): build is Submitted =>
25
+ build.submitted_to !== null && build.submitted_to !== undefined;
26
+
27
+ /** When the submission was recorded: relative, with the exact time on hover. */
28
+ function SubmittedAt({ ms }: { readonly ms: number | null }) {
29
+ if (ms === null) return null;
30
+ const date = dayjs(ms);
31
+ return (
32
+ <time
33
+ className="text-xs text-muted-foreground"
34
+ dateTime={date.toISOString()}
35
+ title={date.format("YYYY-MM-DD HH:mm:ss")}
36
+ >
37
+ {date.fromNow()}
38
+ </time>
39
+ );
40
+ }
41
+
42
+ /**
43
+ * The store track a build went to, with the store's label of it, such as
44
+ * "TestFlight 1.7.0 (66)": none for a build no store took.
45
+ */
46
+ export function StoreSubmissionBadge({
47
+ build,
48
+ withTime = false,
49
+ }: {
50
+ readonly build: NativeBuildRow;
51
+ /** Whether to show when it was submitted beside the badge. */
52
+ readonly withTime?: boolean;
53
+ }) {
54
+ if (!isSubmitted(build)) return null;
55
+ return (
56
+ <span className="inline-flex flex-wrap items-center gap-1.5">
57
+ <Badge
58
+ className="gap-1"
59
+ title={`Submitted to ${storeTrackLabels[build.submitted_to]}`}
60
+ variant="secondary"
61
+ >
62
+ <Send aria-hidden="true" className="size-3" />
63
+ {storeTrackLabels[build.submitted_to]}
64
+ {build.submitted_version ? (
65
+ <span className="font-mono tabular-nums" translate="no">
66
+ {build.submitted_version}
67
+ </span>
68
+ ) : null}
69
+ </Badge>
70
+ {withTime ? <SubmittedAt ms={build.submitted_at_ms} /> : null}
71
+ </span>
72
+ );
73
+ }
@@ -18,6 +18,7 @@ import { BundleIdDisplay } from "@/components/BundleIdDisplay";
18
18
  import { BundleMetadata } from "@/components/features/bundles/BundleMetadata";
19
19
  import { RolloutCohortsDialog } from "@/components/features/bundles/RolloutCohortsDialog";
20
20
  import { ReleaseSections } from "@/components/features/FeatureSlots";
21
+ import { ReleaseNativeBuildLink } from "@/components/features/native-builds/ReleaseNativeBuildLink";
21
22
  import { InfoTooltip } from "@/components/InfoTooltip";
22
23
  import { PlatformIcon } from "@/components/PlatformIcon";
23
24
  import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
@@ -386,6 +387,13 @@ export function ReleaseEditorSheet({
386
387
  {channelName}
387
388
  </span>
388
389
  </div>
390
+ {channelName ? (
391
+ <ReleaseNativeBuildLink
392
+ bundle={bundle}
393
+ channel={channelName}
394
+ release={release}
395
+ />
396
+ ) : null}
389
397
  <div className="flex flex-wrap items-center gap-2">
390
398
  <span className="font-medium text-muted-foreground">
391
399
  Platform
@@ -5,10 +5,12 @@ import {
5
5
  type ReleasePolicyPatch,
6
6
  rowToBundle,
7
7
  } from "@hot-updater/plugin-core";
8
+ import { assertCanonicalChannelName } from "@hot-updater/protocol";
8
9
  import { createServerFn } from "@tanstack/react-start";
9
10
 
10
11
  import { consoleAccess } from "./console-access";
11
12
  import { DEFAULT_PAGE_LIMIT } from "./constants";
13
+ import { channelIdOf, releaseNativeBuildScope } from "./native-builds";
12
14
  import { withPublicBundleMutationErrors } from "./public-bundle-error";
13
15
  import { listReleases, readReleaseFilter } from "./server/listReleases";
14
16
  import { addReleaseReachability } from "./server/releaseReachability";
@@ -242,3 +244,99 @@ export const getBundleChildCounts = createServerFn({ method: "GET" })
242
244
  throw error;
243
245
  }
244
246
  });
247
+
248
+ /** A channel name the list filters by, as its channel ID. */
249
+ const channelIdFilter = (channel: unknown) => {
250
+ if (channel === undefined) return {};
251
+ if (typeof channel !== "string") throw new Error("channel must be a name.");
252
+ assertCanonicalChannelName(channel);
253
+ return { channelId: channelIdOf(channel) };
254
+ };
255
+
256
+ export const getNativeBuilds = createServerFn({ method: "GET" })
257
+ .middleware([consoleAccess])
258
+ .validator(
259
+ (
260
+ input:
261
+ | { before?: string; after?: string; channel?: string; limit?: number }
262
+ | undefined,
263
+ ) => ({
264
+ ...(input?.before === undefined ? {} : { before: input.before }),
265
+ ...(input?.after === undefined ? {} : { after: input.after }),
266
+ ...channelIdFilter(input?.channel),
267
+ limit: pageLimit(input?.limit),
268
+ }),
269
+ )
270
+ .handler(async ({ data }) => {
271
+ const [{ core }, { listNativeBuilds }] = await Promise.all([
272
+ prepare(),
273
+ import("./server/nativeBuilds"),
274
+ ]);
275
+ return listNativeBuilds(core, data);
276
+ });
277
+
278
+ /** A native build's page: the build, its built-in bundle, what it is served, and its patches. */
279
+ export const getNativeBuildDetail = createServerFn({ method: "GET" })
280
+ .middleware([consoleAccess])
281
+ .validator((input: { id: string }) => input)
282
+ .handler(async ({ data }) => {
283
+ const [{ core }, { getNativeBuildDetail: readNativeBuildDetail }] =
284
+ await Promise.all([prepare(), import("./server/nativeBuilds")]);
285
+ return readNativeBuildDetail(core, data.id);
286
+ });
287
+
288
+ /** One page of the patches from a native build's built-in bundle. */
289
+ export const getNativeBuildPatches = createServerFn({ method: "GET" })
290
+ .middleware([consoleAccess])
291
+ .validator(
292
+ (input: { minBundleId: string; before?: string; after?: string }) => ({
293
+ minBundleId: input.minBundleId,
294
+ ...(input.before === undefined ? {} : { before: input.before }),
295
+ ...(input.after === undefined ? {} : { after: input.after }),
296
+ }),
297
+ )
298
+ .handler(async ({ data: { minBundleId, ...page } }) => {
299
+ const [{ core }, { listNativeBuildPatches, NATIVE_BUILD_PATCH_PAGE_SIZE }] =
300
+ await Promise.all([prepare(), import("./server/nativeBuilds")]);
301
+ return listNativeBuildPatches(core, minBundleId, {
302
+ ...page,
303
+ limit: NATIVE_BUILD_PATCH_PAGE_SIZE,
304
+ });
305
+ });
306
+
307
+ /**
308
+ * The newest native build of a release's scope, which its patches start
309
+ * from: one read. A release of an app version range has no one build, and
310
+ * reads nothing.
311
+ */
312
+ export const getReleaseNativeBuild = createServerFn({ method: "GET" })
313
+ .middleware([consoleAccess])
314
+ .validator(
315
+ (input: {
316
+ platform: "ios" | "android";
317
+ channel: string;
318
+ fingerprintHash: string | null;
319
+ targetAppVersion: string | null;
320
+ }) => input,
321
+ )
322
+ .handler(async ({ data }) => {
323
+ const scope = releaseNativeBuildScope(data);
324
+ if (scope === null) return null;
325
+ return (await prepare()).core.findNativeBuild(scope);
326
+ });
327
+
328
+ /**
329
+ * The URL a phone downloads a build from without the console's sign-in, or
330
+ * null when the storage gives none: the storage's download URL, resolved
331
+ * again each time it is read, since it may expire.
332
+ */
333
+ export const getNativeBuildDownloadUrl = createServerFn({ method: "GET" })
334
+ .middleware([consoleAccess])
335
+ .validator((input: { id: string }) => input)
336
+ .handler(async ({ data }) => {
337
+ const [deps, { resolveNativeBuildDownloadUrl }] = await Promise.all([
338
+ prepare(),
339
+ import("./server/nativeBuilds"),
340
+ ]);
341
+ return { url: await resolveNativeBuildDownloadUrl(data.id, deps) };
342
+ });