@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
@@ -1,12 +1,14 @@
1
- import { n as DatabaseBundleNotFoundError, r as DatabaseRowReferencedError } from "./databaseJsonValue-Csbuzc9Q.mjs";
2
- import { C as isUUIDv7, D as parseReleaseCatalogScopeKey, E as normalizeRolloutCohortCount, T as normalizeCohortValue, a as RELEASE_CATALOG_FALLBACK_POLICY, b as getRolledOutNumericCohorts, c as decodeChannelKey, d as getBundlePatch, g as getNumericCohortValue, l as encodeChannelKey, m as getManifestStorageUri, n as MAX_COMPILED_CATALOG_BYTES, o as canonicalizeAppVersion, p as getManifestFileHash, r as NIL_UUID, s as createReleaseCatalogScopeKey, u as getAssetBaseStorageUri, x as isCustomCohort } from "./dist-CbQbBIw-.mjs";
3
- import { i as rowToBundle, n as bundleToRow, r as releaseRowToRelease, t as bundleToPatchRows } from "./databaseRows-CPphvbag.mjs";
4
- import { t as ReleaseManagementError } from "./releaseManagement-DFMUnDF5.mjs";
5
- import { n as createUUIDv7After, t as createUUIDv7 } from "./uuidv7-C88s9jWF.mjs";
6
- import { a as normalize, i as defineTable, n as compare, o as normalizeRange, r as defineAggregate, s as parseRange } from "./schema-BiAVyEj2.mjs";
7
- import { t as isRemoteDatabase } from "./databaseConfig-C5SkAyUy.mjs";
1
+ import { n as DatabaseBundleNotFoundError, r as DatabaseRowReferencedError } from "./databaseJsonValue-kS7cvEtz.mjs";
2
+ import { O as parseReleaseCatalogScopeKey, a as RELEASE_CATALOG_FALLBACK_POLICY, c as createReleaseCatalogScopeKey, d as getAssetBaseStorageUri, f as getBundlePatch, h as getManifestStorageUri, l as decodeChannelKey, m as getManifestFileHash, r as NIL_UUID, s as canonicalizeAppVersion, u as encodeChannelKey, w as isUUIDv7 } from "./dist-TEpM_UAD.mjs";
3
+ import { i as rowToBundle, n as bundleToRow, r as releaseRowToRelease, t as bundleToPatchRows } from "./databaseRows-B6H8Cmgq.mjs";
4
+ import { t as ReleaseManagementError } from "./releaseManagement-DHUux2az.mjs";
5
+ import { n as createUUIDv7After, t as createUUIDv7 } from "./uuidv7-BIF4nb_q.mjs";
6
+ import { i as normalizeRange } from "./version-CQ98ZBpL-CWSkTT9J.mjs";
7
+ import { n as projectCompiledCatalog, r as projectCompiledRollbackCatalog, t as compileReleaseCatalog } from "./releaseCatalogCompiler-DLcean2k.mjs";
8
+ import { t as isRemoteDatabase } from "./databaseConfig-B_Bg7qS5.mjs";
9
+ import { n as defineTable, t as defineAggregate } from "./schema-ChlRtZIo.mjs";
8
10
  import { r as rangesIntersect } from "../_libs/verkit.mjs";
9
- //#region node_modules/.nitro/vite/services/ssr/assets/auth.server-DR9zc0EU.js
11
+ //#region node_modules/.nitro/vite/services/ssr/assets/auth.server-DYS1OJ6P.js
10
12
  var SHA256_FILE_HASH_RE = /^[0-9a-f]{64}$/;
11
13
  var isContentAddressedAssetFileHash = (value) => {
12
14
  return typeof value === "string" && SHA256_FILE_HASH_RE.test(value);
@@ -136,365 +138,6 @@ var getBundleArchiveStorageUri = ({ manifestStorageUri, bundleId }) => replaceSt
136
138
  function assertStorageOperations(adapter, operations) {
137
139
  for (const operation of operations) if (typeof adapter[operation] !== "function") throw new Error(`Storage adapter "${adapter.name}" does not implement ${operation}.`);
138
140
  }
139
- var ReleaseCatalogCompilationError = class extends Error {
140
- code;
141
- constructor(code, message) {
142
- super(message);
143
- this.name = "ReleaseCatalogCompilationError";
144
- this.code = code;
145
- }
146
- };
147
- function compareBounds(left, right) {
148
- return compare(left, right);
149
- }
150
- function strongerLower(current, candidate) {
151
- if (current === null) return candidate;
152
- const compared = compareBounds(candidate.version, current.version);
153
- if (compared > 0) return candidate;
154
- if (compared < 0) return current;
155
- return {
156
- version: current.version,
157
- inclusive: current.inclusive && candidate.inclusive
158
- };
159
- }
160
- function strongerUpper(current, candidate) {
161
- if (current === null) return candidate;
162
- const compared = compareBounds(candidate.version, current.version);
163
- if (compared < 0) return candidate;
164
- if (compared > 0) return current;
165
- return {
166
- version: current.version,
167
- inclusive: current.inclusive && candidate.inclusive
168
- };
169
- }
170
- function comparatorVersion(comparator) {
171
- const version = comparator.version && normalize(comparator.version);
172
- if (version === null) throw new ReleaseCatalogCompilationError("INVALID_TARGET_RANGE", `Invalid comparator: ${comparator.value}`);
173
- return version;
174
- }
175
- function comparatorSetToInterval(comparators) {
176
- let lower = null;
177
- let upper = null;
178
- for (const comparator of comparators) {
179
- if (comparator.version === null) continue;
180
- const version = comparatorVersion(comparator);
181
- if (comparator.operator === "") {
182
- lower = strongerLower(lower, {
183
- inclusive: true,
184
- version
185
- });
186
- upper = strongerUpper(upper, {
187
- inclusive: true,
188
- version
189
- });
190
- } else if (comparator.operator === ">") lower = strongerLower(lower, {
191
- inclusive: false,
192
- version
193
- });
194
- else if (comparator.operator === ">=") lower = strongerLower(lower, {
195
- inclusive: true,
196
- version
197
- });
198
- else if (comparator.operator === "<") upper = strongerUpper(upper, {
199
- inclusive: false,
200
- version
201
- });
202
- else if (comparator.operator === "<=") upper = strongerUpper(upper, {
203
- inclusive: true,
204
- version
205
- });
206
- }
207
- if (lower !== null && upper !== null) {
208
- const compared = compareBounds(lower.version, upper.version);
209
- if (compared > 0 || compared === 0 && (!lower.inclusive || !upper.inclusive)) return null;
210
- }
211
- return {
212
- lower,
213
- upper
214
- };
215
- }
216
- function rangeToIntervals(range) {
217
- try {
218
- return parseRange(range).sets.flatMap((comparators) => {
219
- const interval = comparatorSetToInterval(comparators);
220
- return interval === null ? [] : [interval];
221
- });
222
- } catch {
223
- throw new ReleaseCatalogCompilationError("INVALID_TARGET_RANGE", `Invalid target app-version range: ${range}`);
224
- }
225
- }
226
- function releaseDescriptor(release) {
227
- return {
228
- releaseId: release.id,
229
- kind: release.kind,
230
- bundleId: release.bundleId,
231
- rolloutCohortCount: release.rolloutCohortCount,
232
- targetCohorts: [...new Set(release.targetCohorts)].sort(),
233
- shouldForceUpdate: release.shouldForceUpdate,
234
- message: release.message
235
- };
236
- }
237
- function validateReleases(releases, strategy) {
238
- const enabled = releases.filter((release) => release.enabled).sort((left, right) => right.id.localeCompare(left.id));
239
- const seenIds = /* @__PURE__ */ new Set();
240
- for (const release of enabled) {
241
- if (!isUUIDv7(release.id)) throw new ReleaseCatalogCompilationError("INVALID_RELEASE", `Release ${release.id} must use a canonical lowercase UUIDv7 ID`);
242
- if (seenIds.has(release.id)) throw new ReleaseCatalogCompilationError("INVALID_RELEASE", `Duplicate Release ID: ${release.id}`);
243
- seenIds.add(release.id);
244
- if (release.strategy !== strategy) throw new ReleaseCatalogCompilationError("INVALID_RELEASE", `Release ${release.id} has the wrong strategy`);
245
- if (release.bundleId === null) throw new ReleaseCatalogCompilationError("INVALID_RELEASE", `Release ${release.id} has no Bundle`);
246
- if (release.targetCohorts.length > 100) throw new ReleaseCatalogCompilationError("TARGET_COHORT_LIMIT", `Release ${release.id} exceeds 100 target cohorts`);
247
- if (strategy === "APP_VERSION" && (release.targetAppVersion === null || release.fingerprintHash !== null)) throw new ReleaseCatalogCompilationError("INVALID_RELEASE", `Release ${release.id} has an invalid app-version target`);
248
- if (strategy === "FINGERPRINT" && (release.fingerprintHash === null || release.targetAppVersion !== null)) throw new ReleaseCatalogCompilationError("INVALID_RELEASE", `Release ${release.id} has an invalid fingerprint target`);
249
- }
250
- return enabled;
251
- }
252
- function collectFrontierReleaseIds(releases) {
253
- if (releases.every((release) => normalizeRolloutCohortCount(release.rolloutCohortCount) >= 1e3)) {
254
- const retained = /* @__PURE__ */ new Set();
255
- const bundleIds = /* @__PURE__ */ new Set();
256
- for (const release of releases) {
257
- if (release.bundleId !== null && !bundleIds.has(release.bundleId)) {
258
- bundleIds.add(release.bundleId);
259
- retained.add(release.id);
260
- }
261
- if (bundleIds.size >= 11) break;
262
- }
263
- return retained;
264
- }
265
- const explicitTargetCohorts = new Set(releases.flatMap((release) => release.targetCohorts));
266
- const cohortStates = [
267
- void 0,
268
- ...Array.from({ length: 1e3 }, (_, index) => String(index + 1)),
269
- ...[...explicitTargetCohorts].sort(),
270
- "release-catalog-non-targeted-cohort"
271
- ].map((cohort) => {
272
- const normalized = cohort === void 0 ? void 0 : normalizeCohortValue(cohort);
273
- return {
274
- isCustom: normalized !== void 0 && isCustomCohort(normalized),
275
- normalized,
276
- numeric: normalized === void 0 ? null : getNumericCohortValue(normalized),
277
- bundleIds: /* @__PURE__ */ new Set(),
278
- complete: false
279
- };
280
- });
281
- const stateIndexesByCohort = /* @__PURE__ */ new Map();
282
- const fullRolloutStateIndexes = [];
283
- for (const [index, state] of cohortStates.entries()) {
284
- if (state.normalized !== void 0) {
285
- const indexes = stateIndexesByCohort.get(state.normalized) ?? [];
286
- indexes.push(index);
287
- stateIndexesByCohort.set(state.normalized, indexes);
288
- }
289
- if (state.normalized === void 0 || state.numeric !== null || state.isCustom) fullRolloutStateIndexes.push(index);
290
- }
291
- const retained = /* @__PURE__ */ new Set();
292
- let incompleteStateCount = cohortStates.length;
293
- for (const release of releases) {
294
- const rolloutCohortCount = normalizeRolloutCohortCount(release.rolloutCohortCount);
295
- const eligibleStateIndexes = /* @__PURE__ */ new Set();
296
- if (rolloutCohortCount >= 1e3) for (const index of fullRolloutStateIndexes) eligibleStateIndexes.add(index);
297
- else if (rolloutCohortCount > 0) for (const numericCohort of getRolledOutNumericCohorts(release.id, rolloutCohortCount)) for (const index of stateIndexesByCohort.get(String(numericCohort)) ?? []) eligibleStateIndexes.add(index);
298
- for (const targetCohort of new Set(release.targetCohorts.map(normalizeCohortValue))) for (const index of stateIndexesByCohort.get(targetCohort) ?? []) eligibleStateIndexes.add(index);
299
- for (const index of eligibleStateIndexes) {
300
- const state = cohortStates[index];
301
- if (state.complete) continue;
302
- if (release.bundleId !== null && !state.bundleIds.has(release.bundleId)) {
303
- state.bundleIds.add(release.bundleId);
304
- retained.add(release.id);
305
- }
306
- if (state.bundleIds.size >= 11) {
307
- state.complete = true;
308
- incompleteStateCount -= 1;
309
- }
310
- }
311
- if (incompleteStateCount === 0) break;
312
- }
313
- return retained;
314
- }
315
- function intervalContainsSegment(interval, segment) {
316
- if (interval.lower !== null) {
317
- if (segment.lower === null) return false;
318
- const compared = compareBounds(segment.lower.version, interval.lower.version);
319
- if (compared < 0 || compared === 0 && segment.lower.inclusive && !interval.lower.inclusive) return false;
320
- }
321
- if (interval.upper !== null) {
322
- if (segment.upper === null) return false;
323
- const compared = compareBounds(segment.upper.version, interval.upper.version);
324
- if (compared > 0 || compared === 0 && segment.upper.inclusive && !interval.upper.inclusive) return false;
325
- }
326
- return true;
327
- }
328
- function atomicVersionSegments(intervals) {
329
- const endpoints = [...new Set(intervals.flatMap((interval) => [interval.lower?.version, interval.upper?.version].filter((version) => version !== void 0)))].sort(compareBounds);
330
- if (endpoints.length === 0) return [{
331
- lower: null,
332
- upper: null
333
- }];
334
- const segments = [{
335
- lower: null,
336
- upper: {
337
- inclusive: false,
338
- version: endpoints[0]
339
- }
340
- }];
341
- for (let index = 0; index < endpoints.length; index += 1) {
342
- const endpoint = endpoints[index];
343
- segments.push({
344
- lower: {
345
- inclusive: true,
346
- version: endpoint
347
- },
348
- upper: {
349
- inclusive: true,
350
- version: endpoint
351
- }
352
- });
353
- const next = endpoints[index + 1];
354
- if (next !== void 0) segments.push({
355
- lower: {
356
- inclusive: false,
357
- version: endpoint
358
- },
359
- upper: {
360
- inclusive: false,
361
- version: next
362
- }
363
- });
364
- }
365
- segments.push({
366
- lower: {
367
- inclusive: false,
368
- version: endpoints.at(-1)
369
- },
370
- upper: null
371
- });
372
- return segments;
373
- }
374
- function sameIndexes(left, right) {
375
- return left.length === right.length && left.every((value, index) => value === right[index]);
376
- }
377
- function mergeSegments(segments) {
378
- const merged = [];
379
- for (const segment of segments) {
380
- const previous = merged.at(-1);
381
- if (previous && sameIndexes(previous.releaseIndexes, segment.releaseIndexes) && sameIndexes(previous.rollbackReleaseIndexes, segment.rollbackReleaseIndexes)) merged[merged.length - 1] = {
382
- ...previous,
383
- upper: segment.upper
384
- };
385
- else merged.push(segment);
386
- }
387
- return merged;
388
- }
389
- function compileAppVersion(releases) {
390
- const ranges = /* @__PURE__ */ new Map();
391
- for (const release of releases) ranges.set(release.id, rangeToIntervals(release.targetAppVersion));
392
- const segmentReleases = atomicVersionSegments([...ranges.values()].flat()).map((segment) => {
393
- const applicable = releases.filter((release) => ranges.get(release.id).some((range) => intervalContainsSegment(range, segment)));
394
- return {
395
- rollbackIds: new Set(applicable.map(({ id }) => id)),
396
- segment,
397
- retainedIds: collectFrontierReleaseIds(applicable)
398
- };
399
- });
400
- const retainedIds = new Set(segmentReleases.flatMap(({ retainedIds, rollbackIds }) => [...retainedIds, ...rollbackIds]));
401
- const retainedReleases = releases.filter((release) => retainedIds.has(release.id));
402
- const descriptorIndex = new Map(retainedReleases.map((release, index) => [release.id, index]));
403
- return {
404
- envelope: {
405
- fallbackPolicy: RELEASE_CATALOG_FALLBACK_POLICY,
406
- schemaVersion: 1,
407
- segments: mergeSegments(segmentReleases.map(({ segment, retainedIds, rollbackIds }) => ({
408
- ...segment,
409
- releaseIndexes: releases.filter((release) => retainedIds.has(release.id)).map((release) => descriptorIndex.get(release.id)).filter((index) => index !== void 0),
410
- rollbackReleaseIndexes: releases.filter((release) => rollbackIds.has(release.id)).map((release) => descriptorIndex.get(release.id)).filter((index) => index !== void 0)
411
- }))).filter((segment) => segment.releaseIndexes.length > 0 || segment.rollbackReleaseIndexes.length > 0),
412
- strategy: "APP_VERSION"
413
- },
414
- retainedReleases
415
- };
416
- }
417
- function compileFingerprint(releases) {
418
- const retainedIds = collectFrontierReleaseIds(releases);
419
- const retainedReleases = releases;
420
- return {
421
- envelope: {
422
- fallbackPolicy: RELEASE_CATALOG_FALLBACK_POLICY,
423
- releaseIndexes: retainedReleases.map((release, index) => retainedIds.has(release.id) ? index : -1).filter((index) => index >= 0),
424
- rollbackReleaseIndexes: retainedReleases.map((_, index) => index),
425
- schemaVersion: 1,
426
- strategy: "FINGERPRINT"
427
- },
428
- retainedReleases
429
- };
430
- }
431
- async function sha256(value) {
432
- const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
433
- return `sha256:${[...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("")}`;
434
- }
435
- function canonicalStringify(value) {
436
- if (value === null || typeof value !== "object") return JSON.stringify(value);
437
- if (Array.isArray(value)) return `[${value.map(canonicalStringify).join(",")}]`;
438
- const record = value;
439
- return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${canonicalStringify(record[key])}`).join(",")}}`;
440
- }
441
- async function compileReleaseCatalog(input) {
442
- const releases = validateReleases(input.releases, input.strategy);
443
- const targetCohorts = new Set(releases.flatMap((release) => release.targetCohorts));
444
- if (targetCohorts.size > 512) throw new ReleaseCatalogCompilationError("DISTINCT_TARGET_COHORT_LIMIT", `Scope exceeds 512 distinct target cohorts`);
445
- const { envelope, retainedReleases } = input.strategy === "APP_VERSION" ? compileAppVersion(releases) : compileFingerprint(releases);
446
- const payload = {
447
- ...envelope,
448
- releaseDescriptors: retainedReleases.map(releaseDescriptor)
449
- };
450
- const canonicalPayload = canonicalStringify(payload);
451
- const byteSize = new TextEncoder().encode(canonicalPayload).byteLength;
452
- if (byteSize > 262144) throw new ReleaseCatalogCompilationError("CATALOG_OVERSIZE", `Compiled Release catalog is ${byteSize} bytes; maximum is ${MAX_COMPILED_CATALOG_BYTES}`);
453
- return {
454
- byteSize,
455
- canonicalPayload,
456
- catalogHash: await sha256(canonicalPayload),
457
- diagnostics: {
458
- byteSize,
459
- descriptorCount: payload.releaseDescriptors.length,
460
- distinctTargetCohortCount: targetCohorts.size,
461
- releaseCount: releases.length,
462
- segmentCount: payload.strategy === "APP_VERSION" ? payload.segments.length : 1
463
- },
464
- payload
465
- };
466
- }
467
- function versionInSegment(version, segment) {
468
- if (segment.lower !== null) {
469
- const compared = compareBounds(version, segment.lower.version);
470
- if (compared < 0 || compared === 0 && !segment.lower.inclusive) return false;
471
- }
472
- if (segment.upper !== null) {
473
- const compared = compareBounds(version, segment.upper.version);
474
- if (compared > 0 || compared === 0 && !segment.upper.inclusive) return false;
475
- }
476
- return true;
477
- }
478
- function projectCompiledCatalog(catalog, appVersion) {
479
- let indexes;
480
- if (catalog.strategy === "FINGERPRINT") indexes = catalog.releaseIndexes;
481
- else {
482
- const canonicalVersion = appVersion && canonicalizeAppVersion(appVersion);
483
- if (!canonicalVersion) return [];
484
- indexes = catalog.segments.find((segment) => versionInSegment(canonicalVersion, segment))?.releaseIndexes ?? [];
485
- }
486
- return indexes.map((index) => catalog.releaseDescriptors[index]);
487
- }
488
- function projectCompiledRollbackCatalog(catalog, appVersion) {
489
- let indexes;
490
- if (catalog.strategy === "FINGERPRINT") indexes = catalog.rollbackReleaseIndexes;
491
- else {
492
- const canonicalVersion = appVersion && canonicalizeAppVersion(appVersion);
493
- if (!canonicalVersion) return [];
494
- indexes = catalog.segments.find((segment) => versionInSegment(canonicalVersion, segment))?.rollbackReleaseIndexes ?? [];
495
- }
496
- return indexes.map((index) => catalog.releaseDescriptors[index]);
497
- }
498
141
  var ReleaseCatalogMutationError = class extends Error {
499
142
  code;
500
143
  name = "ReleaseCatalogMutationError";
@@ -957,12 +600,17 @@ var SETTINGS_TABLE = {
957
600
  var ENGINE_SCHEMA_KEY = "schema.engine";
958
601
  var CORE_SCHEMA_KEY = "schema.core";
959
602
  var stored = ({ key, expected, found }) => `"${key}" is ${found === null ? "missing" : `"${found}"`}; expected "${expected}"`;
960
- var settingMessage = (adapterName, { key, expected, found }) => `Hot Updater schema setting "${key}" for ${adapterName} is ${found === null ? "missing" : `"${found}"`}; expected "${expected}". Run \`hot-updater db migrate\`.`;
603
+ /**
604
+ * How to migrate a database the fence refused: doctor names the command for
605
+ * it, init again for a deployment init manages, or the database's own
606
+ * tooling in the server's project.
607
+ */
608
+ var MIGRATE_SCHEMA_FIX = "Run `npx hot-updater doctor` in the project whose hot-updater.config.ts names this database: it names the command that migrates it. In a server project, `hot-updater db migrate` migrates the database its server file names.";
609
+ var settingMessage = (adapterName, { key, expected, found }) => `Hot Updater schema setting "${key}" for ${adapterName} is ${found === null ? "missing" : `"${found}"`}; expected "${expected}". ${MIGRATE_SCHEMA_FIX}`;
961
610
  /** The plugin a `schema.<id>` setting belongs to; undefined for core's and the engine's. */
962
611
  var pluginOf = ({ key }) => key.startsWith("schema.") && key !== "schema.engine" && key !== CORE_SCHEMA_KEY ? key.slice(7) : void 0;
963
612
  var listed = (items) => items.length < 2 ? items.join("") : `${items.slice(0, -1).join(", ")} and ${items.at(-1)}`;
964
- /** How to create plugins' tables when the fence was given no fix. */
965
- var PLUGIN_TABLES_FIX = "Run `hot-updater db migrate`. A managed server runs only its provider's plugins, and rerunning `hot-updater init --provider <provider>` applies the provider's migrations. Run `hot-updater db` commands in the server's project: they load the server file named on the command line, or src/hotUpdater.* or src/db.*, and don't read hot-updater.config.ts.";
613
+ var settingsMessage = (adapterName, settings) => `Hot Updater schema settings for ${adapterName} are not migrated: ${settings.map(stored).join(", and ")}. ${MIGRATE_SCHEMA_FIX}`;
966
614
  var pluginsMessage = (adapterName, plugins, settings, fix) => `The tables of ${plugins.length === 1 ? "plugin" : "plugins"} ${listed(plugins.map((id) => `"${id}"`))} are not migrated on ${adapterName}: schema ${settings.length === 1 ? "setting" : "settings"} ${settings.map(stored).join(", and ")}. ${fix}`;
967
615
  /** The database needs `hot-updater db migrate`, or a new database; the handler answers 503. */
968
616
  var HotUpdaterSchemaMigrationRequiredError = class extends Error {
@@ -979,7 +627,7 @@ var HotUpdaterSchemaMigrationRequiredError = class extends Error {
979
627
  constructor(adapterName, currentVersion, setting, options) {
980
628
  const settings = options?.settings ?? (setting === void 0 ? [] : [setting]);
981
629
  const plugins = settings.flatMap((mismatch) => pluginOf(mismatch) ?? []);
982
- super(setting === void 0 ? currentVersion === void 0 ? `Hot Updater database schema is not initialized for ${adapterName}. Run \`hot-updater db migrate\` before using this adapter.` : `Hot Updater v1 cannot migrate schema ${currentVersion} in place. Create a new empty database and run \`hot-updater db migrate\`.` : plugins.length > 0 && plugins.length === settings.length ? pluginsMessage(adapterName, plugins, settings, options?.fix ?? PLUGIN_TABLES_FIX) : settingMessage(adapterName, setting), options?.cause === void 0 ? void 0 : { cause: options.cause });
630
+ super(setting === void 0 ? currentVersion === void 0 ? `Hot Updater database schema is not initialized for ${adapterName}. ${MIGRATE_SCHEMA_FIX}` : `Hot Updater v1 cannot migrate schema ${currentVersion} in place. Create a new empty database and run \`hot-updater db migrate\`.` : plugins.length > 0 && plugins.length === settings.length ? pluginsMessage(adapterName, plugins, settings, options?.fix ?? MIGRATE_SCHEMA_FIX) : settings.length > 1 && !(setting.key === "schema.engine" && setting.found === null) ? settingsMessage(adapterName, settings) : settingMessage(adapterName, setting), options?.cause === void 0 ? void 0 : { cause: options.cause });
983
631
  this.adapterName = adapterName;
984
632
  this.currentVersion = currentVersion;
985
633
  this.setting = setting;
@@ -1043,17 +691,37 @@ var checkSchemaFence = async (adapter, adapterName, expected, fix) => {
1043
691
  };
1044
692
  /** The database name a fenced adapter carries, so a server can fence its plugins' rows too. */
1045
693
  var FENCED = Symbol.for("@hot-updater/server/schema-fence");
694
+ /**
695
+ * The adapter a fence reads its settings through and the settings it
696
+ * expects, so a fence over it checks them with its own in one read. Another
697
+ * key than `FENCED`, whose value another copy of plugin-core reads as the
698
+ * name.
699
+ */
700
+ var FENCED_SETTINGS = Symbol.for("@hot-updater/server/schema-fence-settings");
1046
701
  /** The database name an adapter behind the schema fence reports under. */
1047
702
  var fencedName = (adapter) => adapter[FENCED];
1048
703
  /**
1049
704
  * The adapter behind the schema fence: a process's first read or write
1050
705
  * checks the settings first. Success is kept; a failure is checked again on
1051
- * the next call.
706
+ * the next call. A fence over another one, as a server's plugins' over
707
+ * core's, checks the inner fence's settings with its own in that one read,
708
+ * so its refusal lists every setting missing or different.
1052
709
  */
1053
710
  var withSchemaFence = (adapter, adapterName, expected, fix) => {
711
+ const inner = adapter[FENCED_SETTINGS];
712
+ const settings = inner === void 0 ? {
713
+ adapter,
714
+ expected
715
+ } : {
716
+ adapter: inner.adapter,
717
+ expected: {
718
+ ...inner.expected,
719
+ ...expected
720
+ }
721
+ };
1054
722
  let ready;
1055
723
  const fence = () => {
1056
- ready ??= checkSchemaFence(adapter, adapterName, expected, fix).catch((error) => {
724
+ ready ??= checkSchemaFence(settings.adapter, adapterName, settings.expected, fix).catch((error) => {
1057
725
  ready = void 0;
1058
726
  throw error;
1059
727
  });
@@ -1062,6 +730,7 @@ var withSchemaFence = (adapter, adapterName, expected, fix) => {
1062
730
  return {
1063
731
  ...adapter,
1064
732
  [FENCED]: adapterName,
733
+ [FENCED_SETTINGS]: settings,
1065
734
  get: (table, keys) => fence().then(() => adapter.get(table, keys)),
1066
735
  query: (table, request) => fence().then(() => adapter.query(table, request)),
1067
736
  write: (ops) => fence().then(() => adapter.write(ops)),
@@ -1077,257 +746,375 @@ var CHANNEL_ID = {
1077
746
  maxLength: 1408,
1078
747
  ascii: true
1079
748
  };
749
+ var bundles = defineTable({
750
+ id: {
751
+ type: "string",
752
+ maxLength: 36
753
+ },
754
+ platform: {
755
+ type: "string",
756
+ maxLength: 16
757
+ },
758
+ git_commit_hash: {
759
+ type: "string",
760
+ maxLength: 255,
761
+ required: false
762
+ },
763
+ metadata: { type: "json" },
764
+ manifest_storage_uri: { type: "string" },
765
+ manifest_file_hash: { type: "string" },
766
+ asset_base_storage_uri: { type: "string" }
767
+ }, {
768
+ key: ["id"],
769
+ indexes: {
770
+ byPlatform: {
771
+ eq: ["platform"],
772
+ sort: ["id"]
773
+ },
774
+ all: {
775
+ eq: [],
776
+ sort: ["id"]
777
+ }
778
+ }
779
+ });
780
+ var bundlePatches = defineTable({
781
+ id: {
782
+ type: "string",
783
+ maxLength: 255
784
+ },
785
+ bundle_id: {
786
+ type: "string",
787
+ maxLength: 36,
788
+ references: {
789
+ model: "bundles",
790
+ onDelete: "cascade"
791
+ }
792
+ },
793
+ base_bundle_id: {
794
+ type: "string",
795
+ maxLength: 36,
796
+ references: {
797
+ model: "bundles",
798
+ onDelete: "cascade"
799
+ }
800
+ },
801
+ base_file_hash: { type: "string" },
802
+ patch_file_hash: { type: "string" },
803
+ patch_storage_uri: { type: "string" },
804
+ byte_size: { type: "integer" },
805
+ order_index: { type: "integer" }
806
+ }, {
807
+ key: ["id"],
808
+ indexes: {
809
+ pair: {
810
+ eq: ["bundle_id", "base_bundle_id"],
811
+ sort: [],
812
+ unique: true
813
+ },
814
+ byBundle: {
815
+ eq: ["bundle_id"],
816
+ sort: ["order_index"],
817
+ root: { model: "bundles" }
818
+ },
819
+ byBase: {
820
+ eq: ["base_bundle_id"],
821
+ sort: ["bundle_id"]
822
+ }
823
+ }
824
+ });
825
+ var releases = defineTable({
826
+ id: {
827
+ type: "string",
828
+ maxLength: 36
829
+ },
830
+ revision: { type: "integer" },
831
+ scope_key: {
832
+ type: "string",
833
+ maxLength: 2048,
834
+ ascii: true
835
+ },
836
+ channel_id: {
837
+ ...CHANNEL_ID,
838
+ references: {
839
+ model: "channels",
840
+ onDelete: "restrict"
841
+ }
842
+ },
843
+ platform: {
844
+ type: "string",
845
+ maxLength: 16
846
+ },
847
+ kind: {
848
+ type: "string",
849
+ maxLength: 16
850
+ },
851
+ bundle_id: {
852
+ type: "string",
853
+ maxLength: 36,
854
+ required: false,
855
+ references: {
856
+ model: "bundles",
857
+ onDelete: "restrict"
858
+ }
859
+ },
860
+ strategy: {
861
+ type: "string",
862
+ maxLength: 16
863
+ },
864
+ target_app_version: {
865
+ type: "string",
866
+ required: false
867
+ },
868
+ fingerprint_hash: {
869
+ type: "string",
870
+ maxLength: 255,
871
+ required: false
872
+ },
873
+ enabled: { type: "boolean" },
874
+ should_force_update: { type: "boolean" },
875
+ message: {
876
+ type: "string",
877
+ required: false
878
+ },
879
+ rollout_cohort_count: { type: "integer" },
880
+ target_cohorts: { type: "json" },
881
+ operation: {
882
+ type: "string",
883
+ maxLength: 16
884
+ },
885
+ source_release_id: {
886
+ type: "string",
887
+ maxLength: 36,
888
+ required: false,
889
+ references: {
890
+ model: "releases",
891
+ onDelete: "none"
892
+ }
893
+ },
894
+ created_at_ms: { type: "integer" },
895
+ updated_at_ms: { type: "integer" }
896
+ }, {
897
+ key: ["id"],
898
+ indexes: {
899
+ byScope: {
900
+ eq: ["scope_key"],
901
+ sort: ["id"],
902
+ root: { model: "release_catalogs" }
903
+ },
904
+ byScopeEnabled: {
905
+ eq: ["scope_key", "enabled"],
906
+ sort: ["id"],
907
+ root: { model: "release_catalogs" }
908
+ },
909
+ byChannelPlatform: {
910
+ eq: ["channel_id", "platform"],
911
+ sort: ["id"]
912
+ },
913
+ byChannelPlatformEnabled: {
914
+ eq: [
915
+ "channel_id",
916
+ "platform",
917
+ "enabled"
918
+ ],
919
+ sort: ["id"]
920
+ },
921
+ byBundle: {
922
+ eq: ["bundle_id"],
923
+ sort: ["id"]
924
+ },
925
+ all: {
926
+ eq: [],
927
+ sort: ["id"]
928
+ }
929
+ }
930
+ });
931
+ /** One compiled catalog per scope; the update check reads exactly this row. */
932
+ var releaseCatalogs = defineTable({
933
+ scope_key: {
934
+ type: "string",
935
+ maxLength: 2048,
936
+ ascii: true
937
+ },
938
+ catalog_id: {
939
+ type: "string",
940
+ maxLength: 255
941
+ },
942
+ strategy: {
943
+ type: "string",
944
+ maxLength: 16
945
+ },
946
+ channel_id: {
947
+ ...CHANNEL_ID,
948
+ references: {
949
+ model: "channels",
950
+ onDelete: "none"
951
+ }
952
+ },
953
+ channel_key: {
954
+ type: "string",
955
+ maxLength: 1400,
956
+ ascii: true
957
+ },
958
+ platform: {
959
+ type: "string",
960
+ maxLength: 16
961
+ },
962
+ fingerprint_hash: {
963
+ type: "string",
964
+ maxLength: 255,
965
+ required: false
966
+ },
967
+ generation: { type: "integer" },
968
+ payload: { type: "string" },
969
+ catalog_hash: {
970
+ type: "string",
971
+ maxLength: 71
972
+ },
973
+ byte_size: { type: "integer" },
974
+ is_tombstone: { type: "boolean" },
975
+ updated_at_ms: { type: "integer" }
976
+ }, {
977
+ key: ["scope_key"],
978
+ indexes: { all: {
979
+ eq: [],
980
+ sort: ["scope_key"]
981
+ } }
982
+ });
983
+ var channels = defineTable({
984
+ id: CHANNEL_ID,
985
+ name: {
986
+ type: "string",
987
+ maxLength: 255,
988
+ unique: true
989
+ }
990
+ }, {
991
+ key: ["id"],
992
+ indexes: { all: {
993
+ eq: [],
994
+ sort: ["name"]
995
+ } }
996
+ });
997
+ /**
998
+ * The native builds `native-build android` and `native-build ios` track, newest first
999
+ * in each scope: a platform, a channel, and the fingerprint or app version
1000
+ * the build compiled in. `min_bundle_id` names the build's built-in bundle,
1001
+ * a `bundles` row no release publishes. Its references keep no counters, so
1002
+ * adding this table changes no existing one; a channel with builds is
1003
+ * refused deletion through `byChannel`. `build_key` is the native build key
1004
+ * of every input the artifact was built from (`nbk1:` and a SHA-256), which
1005
+ * a build with the same inputs reuses it by; a build registered without
1006
+ * one is never reused. `build_number` is the artifact's `CFBundleVersion`
1007
+ * or `versionCode`. `submitted_to`, `submitted_at_ms` and
1008
+ * `submitted_version` record the store track a build went to, when, and
1009
+ * the store's label of the build; they stay null for a build no store took.
1010
+ */
1011
+ var nativeBuilds = defineTable({
1012
+ id: {
1013
+ type: "string",
1014
+ maxLength: 36
1015
+ },
1016
+ scope_key: {
1017
+ type: "string",
1018
+ maxLength: 2048,
1019
+ ascii: true
1020
+ },
1021
+ platform: {
1022
+ type: "string",
1023
+ maxLength: 16
1024
+ },
1025
+ channel_id: {
1026
+ ...CHANNEL_ID,
1027
+ references: {
1028
+ model: "channels",
1029
+ onDelete: "none"
1030
+ }
1031
+ },
1032
+ fingerprint_hash: {
1033
+ type: "string",
1034
+ maxLength: 255,
1035
+ required: false
1036
+ },
1037
+ app_version: {
1038
+ type: "string",
1039
+ required: false
1040
+ },
1041
+ min_bundle_id: {
1042
+ type: "string",
1043
+ maxLength: 36,
1044
+ unique: true,
1045
+ references: {
1046
+ model: "bundles",
1047
+ onDelete: "none"
1048
+ }
1049
+ },
1050
+ artifact_kind: {
1051
+ type: "string",
1052
+ maxLength: 16
1053
+ },
1054
+ artifact_storage_uri: { type: "string" },
1055
+ byte_size: { type: "integer" },
1056
+ git_commit_hash: {
1057
+ type: "string",
1058
+ maxLength: 255,
1059
+ required: false
1060
+ },
1061
+ created_at_ms: { type: "integer" },
1062
+ build_key: {
1063
+ type: "string",
1064
+ maxLength: 69,
1065
+ ascii: true,
1066
+ required: false
1067
+ },
1068
+ build_number: {
1069
+ type: "string",
1070
+ maxLength: 64,
1071
+ required: false
1072
+ },
1073
+ submitted_to: {
1074
+ type: "string",
1075
+ maxLength: 16,
1076
+ required: false
1077
+ },
1078
+ submitted_at_ms: {
1079
+ type: "integer",
1080
+ required: false
1081
+ },
1082
+ submitted_version: {
1083
+ type: "string",
1084
+ maxLength: 255,
1085
+ required: false
1086
+ }
1087
+ }, {
1088
+ key: ["id"],
1089
+ indexes: {
1090
+ byScope: {
1091
+ eq: ["scope_key"],
1092
+ sort: ["id"]
1093
+ },
1094
+ byBuildKey: {
1095
+ eq: ["scope_key", "build_key"],
1096
+ sort: ["id"]
1097
+ },
1098
+ byChannel: {
1099
+ eq: ["channel_id"],
1100
+ sort: ["id"],
1101
+ root: { model: "channels" }
1102
+ },
1103
+ all: {
1104
+ eq: [],
1105
+ sort: ["id"]
1106
+ }
1107
+ }
1108
+ });
1080
1109
  /** Core's module: always installed, never a plugin. */
1081
1110
  var coreModule = {
1082
1111
  id: "core",
1083
1112
  schema: {
1084
- bundles: defineTable({
1085
- id: {
1086
- type: "string",
1087
- maxLength: 36
1088
- },
1089
- platform: {
1090
- type: "string",
1091
- maxLength: 16
1092
- },
1093
- git_commit_hash: {
1094
- type: "string",
1095
- maxLength: 255,
1096
- required: false
1097
- },
1098
- metadata: { type: "json" },
1099
- manifest_storage_uri: { type: "string" },
1100
- manifest_file_hash: { type: "string" },
1101
- asset_base_storage_uri: { type: "string" }
1102
- }, {
1103
- key: ["id"],
1104
- indexes: {
1105
- byPlatform: {
1106
- eq: ["platform"],
1107
- sort: ["id"]
1108
- },
1109
- all: {
1110
- eq: [],
1111
- sort: ["id"]
1112
- }
1113
- }
1114
- }),
1115
- bundle_patches: defineTable({
1116
- id: {
1117
- type: "string",
1118
- maxLength: 255
1119
- },
1120
- bundle_id: {
1121
- type: "string",
1122
- maxLength: 36,
1123
- references: {
1124
- model: "bundles",
1125
- onDelete: "cascade"
1126
- }
1127
- },
1128
- base_bundle_id: {
1129
- type: "string",
1130
- maxLength: 36,
1131
- references: {
1132
- model: "bundles",
1133
- onDelete: "cascade"
1134
- }
1135
- },
1136
- base_file_hash: { type: "string" },
1137
- patch_file_hash: { type: "string" },
1138
- patch_storage_uri: { type: "string" },
1139
- byte_size: { type: "integer" },
1140
- order_index: { type: "integer" }
1141
- }, {
1142
- key: ["id"],
1143
- indexes: {
1144
- pair: {
1145
- eq: ["bundle_id", "base_bundle_id"],
1146
- sort: [],
1147
- unique: true
1148
- },
1149
- byBundle: {
1150
- eq: ["bundle_id"],
1151
- sort: ["order_index"],
1152
- root: { model: "bundles" }
1153
- },
1154
- byBase: {
1155
- eq: ["base_bundle_id"],
1156
- sort: ["bundle_id"]
1157
- }
1158
- }
1159
- }),
1160
- releases: defineTable({
1161
- id: {
1162
- type: "string",
1163
- maxLength: 36
1164
- },
1165
- revision: { type: "integer" },
1166
- scope_key: {
1167
- type: "string",
1168
- maxLength: 2048,
1169
- ascii: true
1170
- },
1171
- channel_id: {
1172
- ...CHANNEL_ID,
1173
- references: {
1174
- model: "channels",
1175
- onDelete: "restrict"
1176
- }
1177
- },
1178
- platform: {
1179
- type: "string",
1180
- maxLength: 16
1181
- },
1182
- kind: {
1183
- type: "string",
1184
- maxLength: 16
1185
- },
1186
- bundle_id: {
1187
- type: "string",
1188
- maxLength: 36,
1189
- required: false,
1190
- references: {
1191
- model: "bundles",
1192
- onDelete: "restrict"
1193
- }
1194
- },
1195
- strategy: {
1196
- type: "string",
1197
- maxLength: 16
1198
- },
1199
- target_app_version: {
1200
- type: "string",
1201
- required: false
1202
- },
1203
- fingerprint_hash: {
1204
- type: "string",
1205
- maxLength: 255,
1206
- required: false
1207
- },
1208
- enabled: { type: "boolean" },
1209
- should_force_update: { type: "boolean" },
1210
- message: {
1211
- type: "string",
1212
- required: false
1213
- },
1214
- rollout_cohort_count: { type: "integer" },
1215
- target_cohorts: { type: "json" },
1216
- operation: {
1217
- type: "string",
1218
- maxLength: 16
1219
- },
1220
- source_release_id: {
1221
- type: "string",
1222
- maxLength: 36,
1223
- required: false,
1224
- references: {
1225
- model: "releases",
1226
- onDelete: "none"
1227
- }
1228
- },
1229
- created_at_ms: { type: "integer" },
1230
- updated_at_ms: { type: "integer" }
1231
- }, {
1232
- key: ["id"],
1233
- indexes: {
1234
- byScope: {
1235
- eq: ["scope_key"],
1236
- sort: ["id"],
1237
- root: { model: "release_catalogs" }
1238
- },
1239
- byScopeEnabled: {
1240
- eq: ["scope_key", "enabled"],
1241
- sort: ["id"],
1242
- root: { model: "release_catalogs" }
1243
- },
1244
- byChannelPlatform: {
1245
- eq: ["channel_id", "platform"],
1246
- sort: ["id"]
1247
- },
1248
- byChannelPlatformEnabled: {
1249
- eq: [
1250
- "channel_id",
1251
- "platform",
1252
- "enabled"
1253
- ],
1254
- sort: ["id"]
1255
- },
1256
- byBundle: {
1257
- eq: ["bundle_id"],
1258
- sort: ["id"]
1259
- },
1260
- all: {
1261
- eq: [],
1262
- sort: ["id"]
1263
- }
1264
- }
1265
- }),
1266
- release_catalogs: defineTable({
1267
- scope_key: {
1268
- type: "string",
1269
- maxLength: 2048,
1270
- ascii: true
1271
- },
1272
- catalog_id: {
1273
- type: "string",
1274
- maxLength: 255
1275
- },
1276
- strategy: {
1277
- type: "string",
1278
- maxLength: 16
1279
- },
1280
- channel_id: {
1281
- ...CHANNEL_ID,
1282
- references: {
1283
- model: "channels",
1284
- onDelete: "none"
1285
- }
1286
- },
1287
- channel_key: {
1288
- type: "string",
1289
- maxLength: 1400,
1290
- ascii: true
1291
- },
1292
- platform: {
1293
- type: "string",
1294
- maxLength: 16
1295
- },
1296
- fingerprint_hash: {
1297
- type: "string",
1298
- maxLength: 255,
1299
- required: false
1300
- },
1301
- generation: { type: "integer" },
1302
- payload: { type: "string" },
1303
- catalog_hash: {
1304
- type: "string",
1305
- maxLength: 71
1306
- },
1307
- byte_size: { type: "integer" },
1308
- is_tombstone: { type: "boolean" },
1309
- updated_at_ms: { type: "integer" }
1310
- }, {
1311
- key: ["scope_key"],
1312
- indexes: { all: {
1313
- eq: [],
1314
- sort: ["scope_key"]
1315
- } }
1316
- }),
1317
- channels: defineTable({
1318
- id: CHANNEL_ID,
1319
- name: {
1320
- type: "string",
1321
- maxLength: 255,
1322
- unique: true
1323
- }
1324
- }, {
1325
- key: ["id"],
1326
- indexes: { all: {
1327
- eq: [],
1328
- sort: ["name"]
1329
- } }
1330
- }),
1113
+ bundles,
1114
+ bundle_patches: bundlePatches,
1115
+ releases,
1116
+ release_catalogs: releaseCatalogs,
1117
+ channels,
1331
1118
  bundle_totals: defineAggregate({ platform_key: {
1332
1119
  type: "string",
1333
1120
  maxLength: 16
@@ -1338,7 +1125,8 @@ var coreModule = {
1338
1125
  eq: ["platform_key"],
1339
1126
  sort: []
1340
1127
  } }
1341
- })
1128
+ }),
1129
+ native_builds: nativeBuilds
1342
1130
  }
1343
1131
  };
1344
1132
  var encoder = new TextEncoder();
@@ -3044,9 +2832,9 @@ var createDatabaseEngine = ({ batching, now = Date.now, meter, ...options }) =>
3044
2832
  */
3045
2833
  var pluginTablesFix = (database) => {
3046
2834
  const { createMigrator, generateSchema } = database;
3047
- const managed = "A managed server runs only its provider's plugins, and rerunning `hot-updater init --provider <provider>` applies the provider's migrations.";
2835
+ const doctor = "Run `npx hot-updater doctor` in the project whose hot-updater.config.ts names this database: it names the command that migrates it.";
3048
2836
  const serverProject = "Run `hot-updater db` commands in the server's project: they load the server file named on the command line, or src/hotUpdater.* or src/db.*, and don't read hot-updater.config.ts.";
3049
- return createMigrator !== void 0 ? `Run \`hot-updater db migrate\`. ${managed} ${serverProject}` : generateSchema !== void 0 ? `Run \`hot-updater db generate\` and apply the file it writes. ${managed} ${serverProject}` : managed;
2837
+ return createMigrator !== void 0 ? `${doctor} In a server project, run \`hot-updater db migrate\`. ${serverProject}` : generateSchema !== void 0 ? `${doctor} On a database \`hot-updater init\` created, that is init again with its saved inputs; for your own server, run \`hot-updater db generate\` and apply the file it writes. ${serverProject}` : `${doctor} On a database \`hot-updater init\` created, that is init again with its saved inputs.`;
3050
2838
  };
3051
2839
  /**
3052
2840
  * The storage engine over a database, as a server runs it: core's tables
@@ -3098,7 +2886,7 @@ var createEngine = (database, { plugins = [], now = Date.now } = {}) => {
3098
2886
  dispose: () => engine.dispose()
3099
2887
  };
3100
2888
  };
3101
- var HOT_UPDATER_SERVER_VERSION = "1.1.4";
2889
+ var HOT_UPDATER_SERVER_VERSION = "1.2.0-rc.1";
3102
2890
  /**
3103
2891
  * `/version` on both mounts. The admin mount also lists the ids of the
3104
2892
  * plugins the server runs, sorted; apps never learn which plugins a server
@@ -3533,8 +3321,8 @@ var baseBundleIdsOf = (row, range, bundleId, limit) => {
3533
3321
  return ids;
3534
3322
  };
3535
3323
  var PAGE$2 = 500;
3536
- var pick = (fields) => (row) => Object.fromEntries(fields.map((field) => [field, row[field]]));
3537
- var toBundleRow = pick([
3324
+ var pick$1 = (fields) => (row) => Object.fromEntries(fields.map((field) => [field, row[field]]));
3325
+ var toBundleRow = pick$1([
3538
3326
  "id",
3539
3327
  "platform",
3540
3328
  "git_commit_hash",
@@ -3543,7 +3331,7 @@ var toBundleRow = pick([
3543
3331
  "manifest_file_hash",
3544
3332
  "asset_base_storage_uri"
3545
3333
  ]);
3546
- var toPatchRow = pick([
3334
+ var toPatchRow = pick$1([
3547
3335
  "id",
3548
3336
  "bundle_id",
3549
3337
  "base_bundle_id",
@@ -3553,7 +3341,7 @@ var toPatchRow = pick([
3553
3341
  "byte_size",
3554
3342
  "order_index"
3555
3343
  ]);
3556
- var toReleaseRow = pick([
3344
+ var toReleaseRow = pick$1([
3557
3345
  "id",
3558
3346
  "revision",
3559
3347
  "scope_key",
@@ -3574,7 +3362,7 @@ var toReleaseRow = pick([
3574
3362
  "created_at_ms",
3575
3363
  "updated_at_ms"
3576
3364
  ]);
3577
- var toCatalogRow = pick([
3365
+ var toCatalogRow = pick$1([
3578
3366
  "scope_key",
3579
3367
  "catalog_id",
3580
3368
  "strategy",
@@ -3589,7 +3377,7 @@ var toCatalogRow = pick([
3589
3377
  "is_tombstone",
3590
3378
  "updated_at_ms"
3591
3379
  ]);
3592
- var toChannelRow = pick(["id", "name"]);
3380
+ var toChannelRow = pick$1(["id", "name"]);
3593
3381
  /** Every page of one read, in order. */
3594
3382
  var drain = async (read) => {
3595
3383
  const rows = [];
@@ -3601,6 +3389,8 @@ var drain = async (read) => {
3601
3389
  } while (cursor !== void 0);
3602
3390
  return rows;
3603
3391
  };
3392
+ /** A keyset page's range: the keys strictly after `after`, in `order`. */
3393
+ var after = (input) => input.after === void 0 ? {} : { range: (input.order ?? "asc") === "asc" ? { gt: input.after } : { lt: input.after } };
3604
3394
  /** Core's reads, each through one declared index or key. */
3605
3395
  var createCoreReads = (db, storage) => {
3606
3396
  /** A bundle row's patches, exactly as many as its reference counter holds. */
@@ -3625,7 +3415,6 @@ var createCoreReads = (db, storage) => {
3625
3415
  childCount: row._refs_bundle_patches_base_bundle_id ?? 0
3626
3416
  };
3627
3417
  };
3628
- const after = (input) => input.after === void 0 ? {} : { range: (input.order ?? "asc") === "asc" ? { gt: input.after } : { lt: input.after } };
3629
3418
  return {
3630
3419
  async getReleaseCatalog(input) {
3631
3420
  const scopeKey = releaseCatalogScopeKeyOf(input);
@@ -3802,9 +3591,13 @@ var insertBundle = (tx, row) => {
3802
3591
  tx.create("bundles", row);
3803
3592
  countBundle(tx, row.platform, 1);
3804
3593
  };
3805
- var updateBundle = (tx, current, set) => {
3594
+ /**
3595
+ * Updates a bundle; a platform change moves it between platform totals,
3596
+ * except for a native build's built-in bundle, which they never count.
3597
+ */
3598
+ var updateBundle = (tx, current, set, { counted = true } = {}) => {
3806
3599
  tx.update("bundles", current, set);
3807
- if (set.platform !== void 0 && set.platform !== current.platform) {
3600
+ if (counted && set.platform !== void 0 && set.platform !== current.platform) {
3808
3601
  countBundle(tx, current.platform, -1);
3809
3602
  countBundle(tx, set.platform, 1);
3810
3603
  }
@@ -3865,7 +3658,7 @@ var insertChannel = async (db, row) => {
3865
3658
  throw error;
3866
3659
  }
3867
3660
  };
3868
- /** Deletes a channel no release uses: 1 read + 1 write. */
3661
+ /** Deletes a channel no release or native build uses: 1 read, 1 query, 1 write. */
3869
3662
  var deleteChannel = (db, id) => db.transaction(async (tx) => {
3870
3663
  const row = await tx.findOne("channels", { id });
3871
3664
  if (row === null) return {
@@ -3876,9 +3669,240 @@ var deleteChannel = (db, id) => db.transaction(async (tx) => {
3876
3669
  deleted: false,
3877
3670
  reason: "not_empty"
3878
3671
  };
3672
+ const { rows: builds } = await tx.findMany("native_builds", {
3673
+ index: "byChannel",
3674
+ where: { channel_id: id },
3675
+ limit: 1
3676
+ });
3677
+ if (builds.length > 0) return {
3678
+ deleted: false,
3679
+ reason: "not_empty"
3680
+ };
3879
3681
  await tx.delete("channels", row);
3880
3682
  return { deleted: true };
3881
3683
  });
3684
+ /** The artifacts each platform's build produces. */
3685
+ var ARTIFACT_KINDS = {
3686
+ android: ["apk", "aab"],
3687
+ ios: ["ipa", "app"]
3688
+ };
3689
+ /** `native_builds.scope_key`'s bound in core's schema. */
3690
+ var SCOPE_KEY_MAX_LENGTH = 2048;
3691
+ /** The `git_commit_hash` bound of `bundles` and `native_builds`. */
3692
+ var GIT_COMMIT_HASH_MAX_LENGTH = 255;
3693
+ /**
3694
+ * A native build key: `nbk1:` and a SHA-256, as the CLI computes it. The
3695
+ * format is the CLI's convention, implemented here so the registry stores
3696
+ * and looks up nothing else.
3697
+ */
3698
+ var BUILD_KEY = /^nbk1:[0-9a-f]{64}$/;
3699
+ /** A native build or scope core cannot store or look up: the caller's input. */
3700
+ var NativeBuildInputError = class extends TypeError {
3701
+ name = "NativeBuildInputError";
3702
+ };
3703
+ var pick = (row) => ({
3704
+ id: row.id,
3705
+ scope_key: row.scope_key,
3706
+ platform: row.platform,
3707
+ channel_id: row.channel_id,
3708
+ fingerprint_hash: row.fingerprint_hash ?? null,
3709
+ app_version: row.app_version ?? null,
3710
+ min_bundle_id: row.min_bundle_id,
3711
+ artifact_kind: row.artifact_kind,
3712
+ artifact_storage_uri: row.artifact_storage_uri,
3713
+ byte_size: row.byte_size,
3714
+ git_commit_hash: row.git_commit_hash ?? null,
3715
+ created_at_ms: row.created_at_ms,
3716
+ build_key: row.build_key ?? null,
3717
+ build_number: row.build_number ?? null,
3718
+ submitted_to: row.submitted_to ?? null,
3719
+ submitted_at_ms: row.submitted_at_ms ?? null,
3720
+ submitted_version: row.submitted_version ?? null
3721
+ });
3722
+ /** The store track each platform's builds are submitted to. */
3723
+ var SUBMISSION_DESTINATIONS = {
3724
+ android: "play-internal",
3725
+ ios: "testflight"
3726
+ };
3727
+ /** `native_builds.build_number`'s bound in core's schema. */
3728
+ var BUILD_NUMBER_MAX_LENGTH = 64;
3729
+ /** `native_builds.submitted_version`'s bound in core's schema. */
3730
+ var SUBMITTED_VERSION_MAX_LENGTH = 255;
3731
+ /**
3732
+ * A native build scope's key: the Release Catalog scope key of a
3733
+ * fingerprint, or of the app-version strategy followed by the exact app
3734
+ * version the build compiled in. One key serves both strategies, so one
3735
+ * index finds the newest build of either.
3736
+ */
3737
+ var nativeBuildScopeKey = (scope) => {
3738
+ let scopeKey;
3739
+ try {
3740
+ const channelKey = encodeChannelKey(scope.channel);
3741
+ scopeKey = scope.fingerprintHash === null ? `${createReleaseCatalogScopeKey({
3742
+ channelKey,
3743
+ platform: scope.platform,
3744
+ strategy: "APP_VERSION"
3745
+ })}:${encodeURIComponent(scope.appVersion)}` : createReleaseCatalogScopeKey({
3746
+ channelKey,
3747
+ fingerprintHash: scope.fingerprintHash,
3748
+ platform: scope.platform,
3749
+ strategy: "FINGERPRINT"
3750
+ });
3751
+ } catch (error) {
3752
+ throw new NativeBuildInputError(`Invalid native build scope: ${error instanceof Error ? error.message : String(error)}.`);
3753
+ }
3754
+ if (scopeKey.length > SCOPE_KEY_MAX_LENGTH) throw new NativeBuildInputError(`Invalid native build scope: its key is longer than ${SCOPE_KEY_MAX_LENGTH} characters; shorten the channel or app version.`);
3755
+ return scopeKey;
3756
+ };
3757
+ /** Whether `bundleId` is a native build's built-in bundle: one unique read. */
3758
+ var isBuiltInBundle = async (db, bundleId) => await db.findOne("native_builds", { min_bundle_id: bundleId }) !== null;
3759
+ var problemOf = ({ id, minBundleId, platform, channel, fingerprintHash, appVersion, gitCommitHash, buildKey, buildNumber, artifact }) => {
3760
+ if (!isUUIDv7(id)) return "id must be a UUIDv7";
3761
+ if (!isUUIDv7(minBundleId)) return "minBundleId must be the UUIDv7 a release build compiles in";
3762
+ if (platform !== "ios" && platform !== "android") return "platform must be ios or android";
3763
+ if (channel.trim().length === 0) return "channel is required";
3764
+ if (gitCommitHash !== null && gitCommitHash.length > GIT_COMMIT_HASH_MAX_LENGTH) return `gitCommitHash must be at most ${GIT_COMMIT_HASH_MAX_LENGTH} characters`;
3765
+ if (fingerprintHash === null === (appVersion === null)) return "exactly one of fingerprintHash and appVersion is required";
3766
+ if (buildKey !== null && !BUILD_KEY.test(buildKey)) return "buildKey must be nbk1: and a SHA-256, or null";
3767
+ if (buildNumber !== void 0 && buildNumber !== null && (buildNumber.trim().length === 0 || buildNumber.length > BUILD_NUMBER_MAX_LENGTH)) return `buildNumber must be the artifact's build number, up to ${BUILD_NUMBER_MAX_LENGTH} characters, or null`;
3768
+ const kinds = ARTIFACT_KINDS[platform];
3769
+ if (!kinds.includes(artifact.kind)) return `artifact.kind of an ${platform} build must be ${kinds.join(" or ")}`;
3770
+ if (!Number.isSafeInteger(artifact.byteSize) || artifact.byteSize < 0) return "artifact.byteSize must be a byte count";
3771
+ return null;
3772
+ };
3773
+ /** The native build registry: core's `native_builds` and the built-in bundle rows. */
3774
+ var createNativeBuildApi = (db, { now = Date.now } = {}) => ({
3775
+ async registerNativeBuild(registration) {
3776
+ const problem = problemOf(registration);
3777
+ if (problem !== null) throw new NativeBuildInputError(`Invalid native build: ${problem}.`);
3778
+ const scopeKey = nativeBuildScopeKey(registration);
3779
+ const channelId = `channel:${encodeChannelKey(registration.channel)}`;
3780
+ const { embeddedBundle } = registration;
3781
+ const row = {
3782
+ id: registration.id,
3783
+ scope_key: scopeKey,
3784
+ platform: registration.platform,
3785
+ channel_id: channelId,
3786
+ fingerprint_hash: registration.fingerprintHash,
3787
+ app_version: registration.appVersion,
3788
+ min_bundle_id: registration.minBundleId,
3789
+ artifact_kind: registration.artifact.kind,
3790
+ artifact_storage_uri: registration.artifact.storageUri,
3791
+ byte_size: registration.artifact.byteSize,
3792
+ git_commit_hash: registration.gitCommitHash,
3793
+ created_at_ms: now(),
3794
+ build_key: registration.buildKey,
3795
+ build_number: registration.buildNumber ?? null,
3796
+ submitted_to: null,
3797
+ submitted_at_ms: null,
3798
+ submitted_version: null
3799
+ };
3800
+ for (let attempt = 1;; attempt += 1) {
3801
+ await insertChannel(db, {
3802
+ id: channelId,
3803
+ name: registration.channel
3804
+ });
3805
+ try {
3806
+ await db.transaction(async (tx) => {
3807
+ tx.create("bundles", {
3808
+ id: registration.minBundleId,
3809
+ platform: registration.platform,
3810
+ git_commit_hash: registration.gitCommitHash,
3811
+ metadata: embeddedBundle.metadata ?? {},
3812
+ manifest_storage_uri: embeddedBundle.manifestStorageUri,
3813
+ manifest_file_hash: embeddedBundle.manifestFileHash,
3814
+ asset_base_storage_uri: embeddedBundle.assetBaseStorageUri
3815
+ });
3816
+ tx.create("native_builds", row);
3817
+ });
3818
+ break;
3819
+ } catch (error) {
3820
+ if (!(error instanceof DatabaseConstraintError)) throw error;
3821
+ if (error.reason === "exists" || error.reason === "unique") throw new DatabaseConstraintError(error.reason, "native_builds");
3822
+ if (attempt < 3 && error.reason === "not_found" && error.model === "channels") continue;
3823
+ throw error;
3824
+ }
3825
+ }
3826
+ return row;
3827
+ },
3828
+ async getNativeBuild(id) {
3829
+ const row = await db.findOne("native_builds", { id });
3830
+ return row === null ? null : pick(row);
3831
+ },
3832
+ async findNativeBuild(scope, options) {
3833
+ const scopeKey = nativeBuildScopeKey(scope);
3834
+ const buildKey = options?.buildKey;
3835
+ if (buildKey !== void 0 && !BUILD_KEY.test(buildKey)) throw new NativeBuildInputError("Invalid native build key: it must be nbk1: and a SHA-256.");
3836
+ const { rows } = buildKey === void 0 ? await db.findMany("native_builds", {
3837
+ index: "byScope",
3838
+ where: { scope_key: scopeKey },
3839
+ order: "desc",
3840
+ limit: 1
3841
+ }) : await db.findMany("native_builds", {
3842
+ index: "byBuildKey",
3843
+ where: {
3844
+ scope_key: scopeKey,
3845
+ build_key: buildKey
3846
+ },
3847
+ order: "desc",
3848
+ limit: 1
3849
+ });
3850
+ return rows[0] === void 0 ? null : pick(rows[0]);
3851
+ },
3852
+ async recordNativeBuildSubmission(id, { destination, version }) {
3853
+ if (version.trim().length === 0 || version.length > SUBMITTED_VERSION_MAX_LENGTH) throw new NativeBuildInputError(`Invalid native build submission: version must be the store's label of the build, up to ${SUBMITTED_VERSION_MAX_LENGTH} characters.`);
3854
+ return db.transaction(async (tx) => {
3855
+ const row = await tx.findOne("native_builds", { id });
3856
+ if (row === null) return null;
3857
+ const build = pick(row);
3858
+ if (SUBMISSION_DESTINATIONS[build.platform] !== destination) throw new NativeBuildInputError(`Invalid native build submission: an ${build.platform} build is submitted to ${SUBMISSION_DESTINATIONS[build.platform]}, not ${destination}.`);
3859
+ const set = {
3860
+ submitted_to: destination,
3861
+ submitted_at_ms: now(),
3862
+ submitted_version: version
3863
+ };
3864
+ tx.update("native_builds", row, set);
3865
+ return {
3866
+ ...build,
3867
+ ...set
3868
+ };
3869
+ });
3870
+ },
3871
+ async deleteNativeBuild(id) {
3872
+ return db.transaction(async (tx) => {
3873
+ const row = await tx.findOne("native_builds", { id });
3874
+ if (row === null) return {
3875
+ deleted: false,
3876
+ reason: "not_found"
3877
+ };
3878
+ await tx.delete("native_builds", row);
3879
+ const bundle = await tx.findOne("bundles", { id: row.min_bundle_id });
3880
+ if (bundle !== null) await tx.delete("bundles", bundle);
3881
+ return {
3882
+ deleted: true,
3883
+ nativeBuild: pick(row)
3884
+ };
3885
+ });
3886
+ },
3887
+ async listNativeBuilds(input) {
3888
+ const page = {
3889
+ order: input.order ?? "asc",
3890
+ limit: input.limit
3891
+ };
3892
+ const { rows } = input.channelId === void 0 ? await db.findMany("native_builds", {
3893
+ index: "all",
3894
+ where: {},
3895
+ ...page,
3896
+ ...after(input)
3897
+ }) : await db.findMany("native_builds", {
3898
+ index: "byChannel",
3899
+ where: { channel_id: input.channelId },
3900
+ ...page,
3901
+ ...after(input)
3902
+ });
3903
+ return rows.map(pick);
3904
+ }
3905
+ });
3882
3906
  var PAGE = 500;
3883
3907
  var fail$1 = (code, message) => {
3884
3908
  throw new ReleaseCatalogMutationError(code, message);
@@ -4188,6 +4212,7 @@ var createCoreOperations = (db, { now = Date.now } = {}) => {
4188
4212
  if (!("bundleId" in deployment) || platforms.has(deployment.bundleId)) continue;
4189
4213
  const row = await db.findOne("bundles", { id: deployment.bundleId });
4190
4214
  if (row === null) throw new DatabaseBundleNotFoundError(deployment.bundleId);
4215
+ if (await isBuiltInBundle(db, deployment.bundleId)) throw new ReleaseManagementError("TARGET_RELEASE_INVALID", `Bundle "${deployment.bundleId}" is a native build's built-in bundle, which no release publishes.`);
4191
4216
  platforms.set(deployment.bundleId, toBundleRow(row).platform);
4192
4217
  }
4193
4218
  const updatedAtMs = now();
@@ -4383,7 +4408,7 @@ var createCoreOperations = (db, { now = Date.now } = {}) => {
4383
4408
  id
4384
4409
  };
4385
4410
  const { id: _id, ...set } = bundleToRow(next);
4386
- updateBundle(tx, current, set);
4411
+ updateBundle(tx, current, set, { counted: set.platform === current.platform || !await isBuiltInBundle(tx, id) });
4387
4412
  if (patches !== void 0) await replaceBundlePatches(tx, current, bundleToPatchRows({
4388
4413
  ...next,
4389
4414
  patches
@@ -4394,7 +4419,9 @@ var createCoreOperations = (db, { now = Date.now } = {}) => {
4394
4419
  await db.transaction(async (tx) => {
4395
4420
  for (const id of new Set(ids)) {
4396
4421
  const current = await tx.findOne("bundles", { id });
4397
- if (current !== null) await deleteBundle(tx, current);
4422
+ if (current === null) continue;
4423
+ if (await isBuiltInBundle(tx, id)) throw new DatabaseRowReferencedError();
4424
+ await deleteBundle(tx, current);
4398
4425
  }
4399
4426
  });
4400
4427
  } catch (error) {
@@ -4444,6 +4471,7 @@ var createCoreApi = (db, storage, options = {}) => {
4444
4471
  return {
4445
4472
  ...reads,
4446
4473
  ...createCoreOperations(purgingCachedRoutes(db, options.onCachedRoutesChange), options),
4474
+ ...createNativeBuildApi(db, options),
4447
4475
  ready: async () => {
4448
4476
  await reads.listReleaseCatalogs({ limit: 1 });
4449
4477
  }
@@ -4635,6 +4663,10 @@ var answer = async (operation) => {
4635
4663
  code: "REFERENCED",
4636
4664
  error: error.message
4637
4665
  }, 409);
4666
+ if (error instanceof DatabaseConstraintError && error.model === "native_builds") return json({
4667
+ code: "NATIVE_BUILD_EXISTS",
4668
+ error: "A native build with this ID or built-in bundle ID is already registered."
4669
+ }, 409);
4638
4670
  throw error;
4639
4671
  }
4640
4672
  };
@@ -4737,6 +4769,42 @@ var bundleUpdateOf = (value, bundleId) => {
4737
4769
  if (id !== void 0 && id !== bundleId) throw new HandlerBadRequestError("Bundle id mismatch");
4738
4770
  return update;
4739
4771
  };
4772
+ var NATIVE_BUILD_ARTIFACT_KINDS = /* @__PURE__ */ new Set([
4773
+ "apk",
4774
+ "aab",
4775
+ "ipa",
4776
+ "app"
4777
+ ]);
4778
+ /** A native build's scope: `fingerprintHash` or `appVersion`, never both. */
4779
+ var nativeBuildScopeOf = (url) => {
4780
+ const params = url.searchParams;
4781
+ const platform = platformOf(url);
4782
+ const channel = params.get("channel");
4783
+ const fingerprintHash = params.get("fingerprintHash");
4784
+ const appVersion = params.get("appVersion");
4785
+ if (platform === void 0 || !isText(channel) || fingerprintHash === null === (appVersion === null) || fingerprintHash === "" || appVersion === "") throw new HandlerBadRequestError("A native build lookup needs platform, channel, and fingerprintHash or appVersion.");
4786
+ return fingerprintHash !== null ? {
4787
+ platform,
4788
+ channel,
4789
+ fingerprintHash,
4790
+ appVersion: null
4791
+ } : {
4792
+ platform,
4793
+ channel,
4794
+ fingerprintHash: null,
4795
+ appVersion
4796
+ };
4797
+ };
4798
+ /** A native build or scope core refuses: a bad request. */
4799
+ var refusingInvalid = async (operation) => {
4800
+ try {
4801
+ return await operation();
4802
+ } catch (error) {
4803
+ if (error instanceof NativeBuildInputError) throw new HandlerBadRequestError(error.message);
4804
+ throw error;
4805
+ }
4806
+ };
4807
+ var isNativeBuildRegistration = (value) => isRecord$1(value) && isText(value.id) && isText(value.minBundleId) && typeof value.platform === "string" && isPlatform(value.platform) && isText(value.channel) && isTextOrNull(value.fingerprintHash) && isTextOrNull(value.appVersion) && value.fingerprintHash === null !== (value.appVersion === null) && isTextOrNull(value.gitCommitHash) && isTextOrNull(value.buildKey) && (value.buildNumber === void 0 || isTextOrNull(value.buildNumber)) && isRecord$1(value.artifact) && typeof value.artifact.kind === "string" && NATIVE_BUILD_ARTIFACT_KINDS.has(value.artifact.kind) && isText(value.artifact.storageUri) && Number.isSafeInteger(value.artifact.byteSize) && value.artifact.byteSize >= 0 && isRecord$1(value.embeddedBundle) && isText(value.embeddedBundle.manifestStorageUri) && isText(value.embeddedBundle.manifestFileHash) && isText(value.embeddedBundle.assetBaseStorageUri) && (value.embeddedBundle.metadata === void 0 || isRecord$1(value.embeddedBundle.metadata));
4740
4808
  var scopeKeyOf = (params) => decodeMaybe(requireRouteParam(params, "scopeKey")) ?? "";
4741
4809
  /** Every admin route's handler, by the name `ADMIN_ROUTES` mounts it under. */
4742
4810
  var createAdminRouteHandlers = () => ({
@@ -4849,6 +4917,37 @@ var createAdminRouteHandlers = () => ({
4849
4917
  }) });
4850
4918
  }),
4851
4919
  preflightReleaseCatalogRebuild: onCore(async (core, _request, params) => json({ data: await core.preflightReleaseCatalogRebuild(scopeKeyOf(params)) })),
4920
+ registerNativeBuild: onCore(async (core, request) => {
4921
+ const input = await body(request);
4922
+ if (!isNativeBuildRegistration(input)) throw new HandlerBadRequestError("Invalid native build");
4923
+ return json({ data: await refusingInvalid(() => core.registerNativeBuild(input)) }, 201);
4924
+ }),
4925
+ findNativeBuild: onCore(async (core, request) => {
4926
+ const url = new URL(request.url);
4927
+ const scope = nativeBuildScopeOf(url);
4928
+ const buildKey = url.searchParams.get("buildKey");
4929
+ return json({ data: await refusingInvalid(() => buildKey === null ? core.findNativeBuild(scope) : core.findNativeBuild(scope, { buildKey })) });
4930
+ }),
4931
+ getNativeBuild: onCore(async (core, _request, params) => json({ data: await core.getNativeBuild(requireRouteParam(params, "id")) })),
4932
+ listNativeBuilds: onCore(async (core, request) => {
4933
+ const url = new URL(request.url);
4934
+ const input = keyset(url);
4935
+ const channelId = url.searchParams.get("channelId");
4936
+ return page(await core.listNativeBuilds({
4937
+ ...input,
4938
+ ...channelId === null ? {} : { channelId }
4939
+ }), input, ({ id }) => id);
4940
+ }),
4941
+ recordNativeBuildSubmission: onCore(async (core, request, params) => {
4942
+ const input = await body(request);
4943
+ if (!isRecord$1(input) || input.destination !== "testflight" && input.destination !== "play-internal" || !isText(input.version)) throw new HandlerBadRequestError("A native build submission needs destination (testflight or play-internal) and version.");
4944
+ const submission = {
4945
+ destination: input.destination,
4946
+ version: input.version
4947
+ };
4948
+ return json({ data: await refusingInvalid(() => core.recordNativeBuildSubmission(requireRouteParam(params, "id"), submission)) });
4949
+ }),
4950
+ deleteNativeBuild: onCore(async (core, _request, params) => json({ data: await core.deleteNativeBuild(requireRouteParam(params, "id")) })),
4852
4951
  deleteBundles: onCore(async (core, request) => {
4853
4952
  const { ids } = await body(request);
4854
4953
  if (!Array.isArray(ids) || !ids.every((id) => typeof id === "string" && id.length > 0)) throw new HandlerBadRequestError("Bundle deletion needs ids");
@@ -4967,6 +5066,36 @@ var ADMIN_ROUTES = [
4967
5066
  method: "GET",
4968
5067
  path: "/base-candidates/:candidateKey",
4969
5068
  handler: "findBaseCandidates"
5069
+ },
5070
+ {
5071
+ method: "POST",
5072
+ path: "/native-builds",
5073
+ handler: "registerNativeBuild"
5074
+ },
5075
+ {
5076
+ method: "GET",
5077
+ path: "/native-builds/latest",
5078
+ handler: "findNativeBuild"
5079
+ },
5080
+ {
5081
+ method: "GET",
5082
+ path: "/native-builds/:id",
5083
+ handler: "getNativeBuild"
5084
+ },
5085
+ {
5086
+ method: "GET",
5087
+ path: "/native-builds",
5088
+ handler: "listNativeBuilds"
5089
+ },
5090
+ {
5091
+ method: "POST",
5092
+ path: "/native-builds/:id/submission",
5093
+ handler: "recordNativeBuildSubmission"
5094
+ },
5095
+ {
5096
+ method: "DELETE",
5097
+ path: "/native-builds/:id",
5098
+ handler: "deleteNativeBuild"
4970
5099
  }
4971
5100
  ];
4972
5101
  var CATALOG_CONTENT_TYPE = "application/vnd.hot-updater.release-catalog+json; version=1";
@@ -5468,20 +5597,21 @@ var assemble = (database, storage, plugins) => createHotUpdater({
5468
5597
  ...plugins.some((plugin) => plugin.provides?.clientAuth) ? {} : { clientAccess: "public" }
5469
5598
  });
5470
5599
  var getConsoleAuthAdapter = async () => {
5471
- return (await import("./auth-UP0wXs4i.mjs")).default;
5600
+ return (await import("./auth-Z-wLddzh.mjs")).default;
5472
5601
  };
5473
5602
  var resolveConsoleConfig = async (request) => {
5474
- const { default: source } = await import("./config-WVX4IxSR.mjs");
5603
+ const { default: source } = await import("./config-CjIBFhJX.mjs");
5475
5604
  const config = typeof source === "function" ? await source(request) : source;
5476
5605
  const { database, storage, plugins = [] } = config;
5477
- const gitUrl = config.console?.gitUrl === void 0 ? {} : { gitUrl: config.console.gitUrl };
5606
+ const { gitUrl } = config.console ?? {};
5607
+ const consoleOptions = gitUrl === void 0 ? {} : { gitUrl };
5478
5608
  if (isRemoteDatabase(database)) {
5479
5609
  const checked = assemble({
5480
5610
  name: "memory",
5481
5611
  adapter: createMemoryAdapter()
5482
5612
  }, storage, plugins);
5483
5613
  return {
5484
- ...gitUrl,
5614
+ ...consoleOptions,
5485
5615
  database,
5486
5616
  core: database.core,
5487
5617
  storage,
@@ -5490,7 +5620,7 @@ var resolveConsoleConfig = async (request) => {
5490
5620
  }
5491
5621
  const server = assemble(database, storage, plugins);
5492
5622
  return {
5493
- ...gitUrl,
5623
+ ...consoleOptions,
5494
5624
  database,
5495
5625
  core: server.core,
5496
5626
  storage,