@hot-updater/console 1.0.0-rc.20 → 1.0.0-rc.21

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 (180) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{ConsoleFeatureUnavailable-B9VYGA3N.js → ConsoleFeatureUnavailable-D0LPL1Gy.js} +1 -1
  3. package/.output/public/assets/{HashValueDisplay-D880zntR.js → HashValueDisplay-BmuB4FW-.js} +1 -1
  4. package/.output/public/assets/InsightsInfo--v4SC07c.js +1 -0
  5. package/.output/public/assets/InsightsPageHeader-dU83CAQY.js +1 -0
  6. package/.output/public/assets/{NotFoundPage-Dys8C0cW.js → NotFoundPage-B5mhGG2a.js} +1 -1
  7. package/.output/public/assets/api-D-vOVlQ4.js +1 -0
  8. package/.output/public/assets/api-keys-Csh8ARKU.js +1 -0
  9. package/.output/public/assets/api-keys-OMPUoA_A.js +1 -0
  10. package/.output/public/assets/arrow-left-Bwq9X1eS.js +1 -0
  11. package/.output/public/assets/arrow-up-right-BkOVn4if.js +1 -0
  12. package/.output/public/assets/{card-DzCPSRsh.js → card-C_7d6O9a.js} +1 -1
  13. package/.output/public/assets/console-features-api-C1jRZTGw.js +1 -0
  14. package/.output/public/assets/dialog-DE8wFN_P.js +1 -0
  15. package/.output/public/assets/{dist-Clb6cQ9l.js → dist-C7ENGzpS.js} +1 -1
  16. package/.output/public/assets/empty-CVd5NlQD.js +1 -0
  17. package/.output/public/assets/{field-Bou7shoO.js → field-C06q1iC9.js} +1 -1
  18. package/.output/public/assets/index-BeWlQtO6.js +11 -0
  19. package/.output/public/assets/{input-group-DVtWFUUF.js → input-group-Bi61biIA.js} +1 -1
  20. package/.output/public/assets/insights-BjnAtC0f.js +2 -0
  21. package/.output/public/assets/insights-D86m11yH.js +42 -0
  22. package/.output/public/assets/insights-DpYstMoY.js +1 -0
  23. package/.output/public/assets/{insights-recovery-rpc-wvdyM4LD.js → insights-recovery-rpc-BIm5_ALc.js} +1 -1
  24. package/.output/public/assets/{insights-usage-rpc-qUkNtrWr.js → insights-usage-rpc-VHj8vyBt.js} +1 -1
  25. package/.output/public/assets/insights_.distribution-ADcrX48c.js +2 -0
  26. package/.output/public/assets/insights_.distribution-CuuJUN6Y.js +1 -0
  27. package/.output/public/assets/{insights_.distribution-CItZw3-8.js → insights_.distribution-DJRjuXsO.js} +1 -1
  28. package/.output/public/assets/installations-BSBBzqWs.js +2 -0
  29. package/.output/public/assets/installations-DqIE6WcO.js +1 -0
  30. package/.output/public/assets/installations-UsELbDvw.js +1 -0
  31. package/.output/public/assets/{loader-circle-Ca7C9YY1.js → loader-circle-CnCBxzHp.js} +1 -1
  32. package/.output/public/assets/mutation-Csp7xUNh.js +1 -0
  33. package/.output/public/assets/preload-helper-B-cOmpLH.js +1 -0
  34. package/.output/public/assets/{refresh-cw-CVGqvy8Z.js → refresh-cw-FF1NtzlQ.js} +1 -1
  35. package/.output/public/assets/{routes-veKtDW39.js → routes-CoN9XHP_.js} +2 -2
  36. package/.output/public/assets/routes-DT1jB0Zi.js +1 -0
  37. package/.output/public/assets/sidebar-D8mJRF15.js +3 -0
  38. package/.output/public/assets/styles-Bwt7v6U5.css +2 -0
  39. package/.output/public/assets/table-WBgfvgXg.js +1 -0
  40. package/.output/public/assets/{useCompositeListItem-CF7vsfcA.js → useCompositeListItem-CloBmDwn.js} +1 -1
  41. package/.output/public/assets/useMutation-DO8cvsTx.js +1 -0
  42. package/.output/public/assets/{useOpenInteractionType-AnAMWYIY.js → useOpenInteractionType-ReGr9GzJ.js} +1 -1
  43. package/.output/public/assets/valueToPercent-BvPAi4bP.js +1 -0
  44. package/.output/server/{__23tanstack-start-server-fn-resolver-CUQLz7hs.mjs → __23tanstack-start-server-fn-resolver-DMRD4e4f.mjs} +34 -42
  45. package/.output/server/_libs/@tanstack/react-router+[...].mjs +3756 -4022
  46. package/.output/server/_libs/@tanstack/react-router-ssr-query+[...].mjs +138 -95
  47. package/.output/server/_libs/tanstack__history.mjs +68 -41
  48. package/.output/server/_libs/tanstack__query-core.mjs +1618 -309
  49. package/.output/server/_libs/tanstack__react-query.mjs +255 -20
  50. package/.output/server/_ssr/{ConsoleFeatureUnavailable-DoxET2AT.mjs → ConsoleFeatureUnavailable-Du7jNPNS.mjs} +7 -7
  51. package/.output/server/_ssr/{HashValueDisplay-BplboHEb.mjs → HashValueDisplay-GQvFkXoi.mjs} +2 -2
  52. package/.output/server/_ssr/{InsightsInfo-o2YPd5Qc.mjs → InsightsInfo-Do7QSaFR.mjs} +2 -2
  53. package/.output/server/_ssr/{InsightsPageHeader-NExCJL1I.mjs → InsightsPageHeader-DSUWUWRc.mjs} +6 -6
  54. package/.output/server/_ssr/{NotFoundPage-Cd-A8ock.mjs → NotFoundPage-DsF4Vrhg.mjs} +2 -2
  55. package/.output/server/_ssr/{api-B1Ro0uys.mjs → api-BEiVs0H7.mjs} +9 -8
  56. package/.output/server/_ssr/{api-keys-Dvvy1sem.mjs → api-keys-BrvBWGRf.mjs} +2 -2
  57. package/.output/server/_ssr/{api-keys-CT3HG7KT.mjs → api-keys-K5v32nf7.mjs} +8 -8
  58. package/.output/server/_ssr/{api-keys-rpc-b1qTTFfG.mjs → api-keys-rpc-B96bBllm.mjs} +5 -5
  59. package/.output/server/_ssr/{api-rpc-Cbns2MGx.mjs → api-rpc-DcUe280B.mjs} +13 -55
  60. package/.output/server/_ssr/{auth-rpc-DChS7m9y.mjs → auth-rpc-C68W56DP.mjs} +6 -6
  61. package/.output/server/_ssr/{api-C0G9MTMI.mjs → auth.server-DM1J6NwR.mjs} +2815 -1308
  62. package/.output/server/_ssr/{bundleActivity-D17Pb_h5.mjs → bundleActivity-CpuY9CTP.mjs} +1 -1
  63. package/.output/server/_ssr/{card-DMbcvlPz.mjs → card-CPAEKx0w.mjs} +1 -1
  64. package/.output/server/_ssr/config-nsIcsHh8.mjs +15 -0
  65. package/.output/server/_ssr/config.server-DDCZk3Sq.mjs +36 -0
  66. package/.output/server/_ssr/{console-access-KpPv5lin.mjs → console-access-Da-2Qd7q.mjs} +3 -3
  67. package/.output/server/_ssr/{console-features-CsbyHYfK.mjs → console-features-Dzu2iUGD.mjs} +6 -6
  68. package/.output/server/_ssr/{console-features-api-B-KVuo4p.mjs → console-features-api-xAsSgFa_.mjs} +6 -6
  69. package/.output/server/_ssr/{console-features-rpc-Djyo5740.mjs → console-features-rpc-qARGpB24.mjs} +5 -5
  70. package/.output/server/_ssr/{createMiddleware-B_I3Ll4M.mjs → createMiddleware-BdBf_ezN.mjs} +41 -56
  71. package/.output/server/_ssr/{createServerRpc-DuNm5mmo.mjs → createServerRpc-B_y2lrwA.mjs} +2 -2
  72. package/.output/server/_ssr/databaseJsonValue-Bhxciy7Z.mjs +68 -0
  73. package/.output/server/_ssr/databaseRows-DwMAjaF8.mjs +74 -0
  74. package/.output/server/_ssr/{dialog-cOmLTUV2.mjs → dialog-q__BJ7mQ.mjs} +1 -1
  75. package/.output/server/_ssr/{dist-B-r4u3Lg.mjs → dist-Cxo9ghYv.mjs} +114 -3
  76. package/.output/server/_ssr/{downloadBundle-CexOFoZ4.mjs → downloadBundle-BW9I-pMQ.mjs} +6 -5
  77. package/.output/server/_ssr/{empty-BHoY_5jt.mjs → empty-DcBTAooP.mjs} +2 -2
  78. package/.output/server/_ssr/{empty-plugin-adapters-B5QvGpEl.mjs → empty-plugin-adapters-CYdinE-i.mjs} +1 -1
  79. package/.output/server/_ssr/{field-mjhcNEXD.mjs → field-QCDBVpXA.mjs} +2 -2
  80. package/.output/server/_ssr/{getBundleChildren-DbDXe9wB.mjs → getBundleChildren-DIAGCF4a.mjs} +2 -2
  81. package/.output/server/_ssr/{input-group-96H30HWC.mjs → input-group-hCk2nUoU.mjs} +1 -1
  82. package/.output/server/_ssr/{insights-DgFv1NsO.mjs → insights-BfBO0RFr.mjs} +18 -18
  83. package/.output/server/_ssr/{insights-D359GJFL.mjs → insights-DsAU3-TB.mjs} +5 -5
  84. package/.output/server/_ssr/{insights-D25VDVaz.mjs → insights-RIfg447W.mjs} +2 -2
  85. package/.output/server/_ssr/{insights-api-ChAKrbhG.mjs → insights-api-CDn-QQHU.mjs} +5 -5
  86. package/.output/server/_ssr/{insights-failures-WQ-ttjOx.mjs → insights-failures-BBGMhuUr.mjs} +2 -2
  87. package/.output/server/_ssr/{insights-failures-rpc-BFsqlltN.mjs → insights-failures-rpc-rA2dctK2.mjs} +8 -8
  88. package/.output/server/_ssr/{insights-recovery-DkL6bxMW.mjs → insights-recovery-Dpk6Mfb9.mjs} +1 -1
  89. package/.output/server/_ssr/{insights-recovery-rpc-2P5m8F62.mjs → insights-recovery-rpc-BCaF-xSd.mjs} +11 -11
  90. package/.output/server/_ssr/{insights-recovery-rpc-Tj8vNC19.mjs → insights-recovery-rpc-CcgYyUHk.mjs} +5 -5
  91. package/.output/server/_ssr/{insights-rpc-DrkVmV9o.mjs → insights-rpc-keycmwzw.mjs} +5 -5
  92. package/.output/server/_ssr/{insights-search-BsalnNw-.mjs → insights-search-BRg5VsNe.mjs} +1 -1
  93. package/.output/server/_ssr/{insights-usage-1nVTQrLy.mjs → insights-usage-cbw9n2Fq.mjs} +1 -1
  94. package/.output/server/_ssr/{insights-usage-rpc-Ct_4xHhZ.mjs → insights-usage-rpc-BKgV1QDf.mjs} +8 -8
  95. package/.output/server/_ssr/{insights-usage-rpc-DaYIRtFK.mjs → insights-usage-rpc-D-JYachy.mjs} +5 -5
  96. package/.output/server/_ssr/{insightsRecovery-CpGucWN1.mjs → insightsRecovery-B7oMc8d8.mjs} +2 -2
  97. package/.output/server/_ssr/{insightsUsage-JYpKZAF7.mjs → insightsUsage-Oa7kk5ek.mjs} +3 -3
  98. package/.output/server/_ssr/{insights_.distribution-jQZEF07J.mjs → insights_.distribution-BIqOqRW0.mjs} +13 -13
  99. package/.output/server/_ssr/{insights_.distribution-Bcu6uYs_.mjs → insights_.distribution-CuUrDa7P.mjs} +2 -2
  100. package/.output/server/_ssr/{insights_.distribution-D3Csuyjw.mjs → insights_.distribution-RT_6x_ud.mjs} +5 -5
  101. package/.output/server/_ssr/{installations-Cxikllmx.mjs → installations-BO5CjX2z.mjs} +11 -11
  102. package/.output/server/_ssr/{installations-BI5kelHv.mjs → installations-BjaHiIfh.mjs} +2 -2
  103. package/.output/server/_ssr/{installations-B8Vh54lH.mjs → installations-Dt5luA-N.mjs} +4 -4
  104. package/.output/server/_ssr/{listReleases-DqU9t1l3.mjs → listReleases-BitHzOYB.mjs} +1 -1
  105. package/.output/server/_ssr/{router-CRkm-wHD.mjs → router-DfVAixxe.mjs} +42 -39
  106. package/.output/server/_ssr/{routes-CNAi0Mmv.mjs → routes-C6LstNp8.mjs} +14 -14
  107. package/.output/server/_ssr/{routes-B3IJo_jL.mjs → routes-Q3Uhmwwz.mjs} +11 -11
  108. package/.output/server/_ssr/{runtime.server-NucbYjc7.mjs → runtime.server-fL0o6I2b.mjs} +500 -413
  109. package/.output/server/_ssr/schema-BKY9SqaY.mjs +25 -0
  110. package/.output/server/_ssr/{select-BkPAJFL7.mjs → select-CUlInmjk.mjs} +1 -1
  111. package/.output/server/_ssr/server-C43wq9ft.mjs +8330 -0
  112. package/.output/server/_ssr/{sidebar-Cw1VuC7d.mjs → sidebar-DKbx2ach.mjs} +3 -3
  113. package/.output/server/_ssr/ssr.mjs +1 -1
  114. package/.output/server/_ssr/start-BXUOv_n8.mjs +4 -0
  115. package/.output/server/_ssr/{table-q0P9wz7x.mjs → table-NGWV8B-S.mjs} +1 -1
  116. package/.output/server/_ssr/{updateFailures-DMU9f0pK.mjs → updateFailures-Jwe2Baab.mjs} +3 -3
  117. package/.output/server/_ssr/{uuidv7-BzSO-fr-.mjs → uuidv7-C8bwiZr8.mjs} +2 -2
  118. package/.output/server/_tanstack-start-manifest_v-WQ5qGn8n.mjs +124 -0
  119. package/.output/server/index.mjs +218 -218
  120. package/LICENSE +13 -14
  121. package/README.md +16 -12
  122. package/lib/index.d.mts +18 -8
  123. package/lib/vite-internal.mjs +15 -8
  124. package/lib/vite.d.mts +2 -0
  125. package/package.json +14 -14
  126. package/src/components/AppSidebar.tsx +9 -0
  127. package/src/components/ConsoleFeatureUnavailable.tsx +1 -3
  128. package/src/components/features/bundles/BundleMetadata.tsx +1 -1
  129. package/src/components/features/bundles/RolloutCohortsDialog.tsx +1 -1
  130. package/src/components/features/releases/ReleaseEditorSheet.tsx +6 -6
  131. package/src/index.ts +27 -15
  132. package/src/lib/api-rpc.ts +1 -54
  133. package/src/lib/api.ts +8 -89
  134. package/src/lib/console-features.ts +5 -5
  135. package/src/lib/scroll-restoration.ts +1 -1
  136. package/src/lib/server/config.server.ts +15 -52
  137. package/src/lib/server/console-runtime.server.ts +84 -6
  138. package/src/lib/server/downloadBundle.ts +10 -6
  139. package/src/lib/server/releaseReachability.ts +4 -4
  140. package/src/lib/server/runtime.server.ts +34 -46
  141. package/src/routeTree.gen.ts +31 -31
  142. package/src/routes/api/bundles/$bundleId/download.ts +2 -2
  143. package/src/vite-internal.ts +17 -8
  144. package/src/vite.ts +2 -0
  145. package/.output/public/assets/InsightsInfo-By_sPjTE.js +0 -1
  146. package/.output/public/assets/InsightsPageHeader-9_kx_LL7.js +0 -1
  147. package/.output/public/assets/api-CtarlV0l.js +0 -1
  148. package/.output/public/assets/api-keys-DFF_QqtN.js +0 -1
  149. package/.output/public/assets/api-keys-evfsQ4sN.js +0 -1
  150. package/.output/public/assets/arrow-left-BP9D-ta_.js +0 -1
  151. package/.output/public/assets/arrow-up-right-CDP9Tevw.js +0 -1
  152. package/.output/public/assets/console-features-api-yv7rm3DX.js +0 -1
  153. package/.output/public/assets/dialog-BDvCy_QO.js +0 -1
  154. package/.output/public/assets/empty-C5x9mn_s.js +0 -1
  155. package/.output/public/assets/index-0eg3l8aC.js +0 -10
  156. package/.output/public/assets/insights-2MbdiSNv.js +0 -1
  157. package/.output/public/assets/insights-C6tmCZHk.js +0 -2
  158. package/.output/public/assets/insights-CadS_aIA.js +0 -42
  159. package/.output/public/assets/insights_.distribution-BhBs6PNA.js +0 -1
  160. package/.output/public/assets/insights_.distribution-CJUH_wAS.js +0 -2
  161. package/.output/public/assets/installations-CH2H7JQt.js +0 -2
  162. package/.output/public/assets/installations-DMeMuwsW.js +0 -1
  163. package/.output/public/assets/installations-DnvxI9Dt.js +0 -1
  164. package/.output/public/assets/mutation-x9L28_CD.js +0 -1
  165. package/.output/public/assets/preload-helper-BN-UkjOE.js +0 -1
  166. package/.output/public/assets/routes-BhK8DuZA.js +0 -1
  167. package/.output/public/assets/sidebar-B45aMqRm.js +0 -3
  168. package/.output/public/assets/styles-Dn0pK1EX.css +0 -2
  169. package/.output/public/assets/table-CwLnN6KX.js +0 -1
  170. package/.output/public/assets/useMutation-CN1ygYEr.js +0 -1
  171. package/.output/public/assets/valueToPercent-C-t8FT0y.js +0 -1
  172. package/.output/server/_ssr/auth.server-Ccqz2iVv.mjs +0 -37
  173. package/.output/server/_ssr/config-DMEJdhoW.mjs +0 -13
  174. package/.output/server/_ssr/config.server-DyypN6cQ.mjs +0 -60
  175. package/.output/server/_ssr/databaseRows-CXxf_VIB.mjs +0 -132
  176. package/.output/server/_ssr/deleteBundle-Dcc21Lbd.mjs +0 -56
  177. package/.output/server/_ssr/server-DKJYsaCP.mjs +0 -7442
  178. package/.output/server/_ssr/start-DpViQGnt.mjs +0 -4
  179. package/.output/server/_tanstack-start-manifest_v-Bb0gKDbb.mjs +0 -124
  180. package/src/lib/server/deleteBundle.ts +0 -136
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
- MIT License
1
+ MIT License with Hosted Service Attribution
2
2
 
3
- Copyright (c) 2023 Sungyu Kang
3
+ Copyright (c) 2023 Sungyu Kang
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -12,6 +12,17 @@ furnished to do so, subject to the following conditions:
12
12
  The above copyright notice and this permission notice shall be included in all
13
13
  copies or substantial portions of the Software.
14
14
 
15
+ Hosted service attribution. If you make the Software, a modified version of
16
+ it, or a service built on it available to third parties as a hosted or managed
17
+ service, that is, a service that third parties use to deliver updates to their
18
+ own apps, you must display the notice "Powered by hot-updater" with a link to
19
+ https://hot-updater.dev or https://github.com/gronxb/hot-updater where the
20
+ users of that service can reasonably see it, such as the service's dashboard,
21
+ the footer of its website, or its about page. The Software's sidebar displays
22
+ this notice, and keeping it in place satisfies this condition. This condition
23
+ does not apply when you run the Software only for your own apps, or for apps
24
+ that you build or operate for your clients.
25
+
15
26
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
27
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
28
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -19,15 +30,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
30
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
31
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
32
  SOFTWARE.
22
-
23
- ---
24
-
25
- ADDITIONAL DISCLAIMER
26
-
27
- The authors of this software shall not be held responsible for any legal issues,
28
- regulatory violations, policy violations, or disputes arising from the use,
29
- distribution, or integration of this software.
30
-
31
- Users are solely responsible for ensuring compliance with all applicable laws,
32
- regulations, and third-party policies (including but not limited to app store
33
- policies).
package/README.md CHANGED
@@ -33,7 +33,7 @@ Modern web-based management console for Hot Updater built with **TanStack Start*
33
33
  ### Backend
34
34
 
35
35
  - **TanStack Start Server Functions** - Type-safe server endpoints
36
- - **Hot Updater Integration** - Storage and database plugin integration
36
+ - **Hot Updater Integration** - Storage and database adapter integration
37
37
 
38
38
  ## 📦 Installation
39
39
 
@@ -123,7 +123,7 @@ src/
123
123
  ## 🔌 API Integration
124
124
 
125
125
  The console integrates with the configured Hot Updater storage and database
126
- plugins through TanStack Start server functions in `src/lib/api-rpc.ts`.
126
+ adapters through TanStack Start server functions in `src/lib/api-rpc.ts`.
127
127
  Functions with input take a `{ data: input }` argument:
128
128
 
129
129
  - `getConfig()` and `getChannels()` load configuration and channels.
@@ -143,22 +143,25 @@ Use one pagination cursor at a time.
143
143
 
144
144
  ## 🎯 Configuration
145
145
 
146
- The checked-in [demo configuration](hot-updater.config.ts) seeds sample bundles
147
- and releases through `createDatabaseCoreApi(database.withoutLatency()).deploy`.
148
- For an empty local mock console, use this `hot-updater.config.ts`:
146
+ The console reads `database`, `storage`, and `plugins` from
147
+ [hot-updater.config.ts](hot-updater.config.ts), as the CLI does, and runs the
148
+ plugins over the database as the server does. The checked-in
149
+ [demo database](demoDatabase.ts) seeds sample bundles, releases, and Insights
150
+ events through `assembleServer` from `@hot-updater/cli-tools`, over
151
+ `database.withoutLatency()`. For an empty local mock console, use this
152
+ `hot-updater.config.ts`:
149
153
 
150
154
  ```typescript
151
155
  import { mockDatabase, mockStorage } from "@hot-updater/mock";
152
156
 
153
157
  export default {
158
+ database: mockDatabase({ latency: { min: 500, max: 700 } }),
154
159
  storage: mockStorage({}),
155
- database: mockDatabase({
156
- latency: { min: 500, max: 700 },
157
- }),
158
160
  };
159
161
  ```
160
162
 
161
- Mock data is held in memory and resets when the configuration is reloaded.
163
+ Add `plugins`, such as `[insights(), apiKeys()]`, to show their pages. Mock
164
+ data is held in memory and resets when the configuration is reloaded.
162
165
 
163
166
  ## 🌈 Theming
164
167
 
@@ -178,7 +181,7 @@ The console uses Tailwind CSS v4 with oklch color space for accessible colors. T
178
181
  ## 📊 Data Flow
179
182
 
180
183
  1. **URL State** → `Route.useSearch()` and `-releases-search.ts` manage release filters
181
- 2. **Server Functions** → TanStack Start server functions call the configured storage and database plugins
184
+ 2. **Server Functions** → TanStack Start server functions call the configured storage and database adapters
182
185
  3. **React Query** → `useReleasesQuery()` and `useBundleQuery()` fetch and cache data
183
186
  4. **UI Components** → Display data with shadcn components
184
187
  5. **Mutations** → `usePreflightReleaseMutation()` validates changes before `useUpdateReleaseMutation()` saves them
@@ -206,8 +209,9 @@ The console uses Tailwind CSS v4 with oklch color space for accessible colors. T
206
209
 
207
210
  ### Hot Updater Config
208
211
 
209
- - Ensure `hot-updater.config.ts` is at package root
210
- - Verify the storage and database plugins are correctly initialized
212
+ - Ensure `hot-updater.config.ts` is at the package root and sets `database`
213
+ and `storage`
214
+ - Verify its storage and database adapters are correctly initialized
211
215
 
212
216
  ## 📝 License
213
217
 
package/lib/index.d.mts CHANGED
@@ -1,5 +1,4 @@
1
- import { ConfigInput, ConfiguredDatabase } from "@hot-updater/plugin-core";
2
- import { AnyHotUpdaterPlugin } from "@hot-updater/server/plugins";
1
+ import { AnyHotUpdaterPlugin, ConfiguredDatabase, StorageAdapter } from "@hot-updater/plugin-core";
3
2
  //#region src/index.d.ts
4
3
  type ConsoleAuthProvider = "google" | "github";
5
4
  type ConsolePrincipal = Readonly<{
@@ -21,16 +20,27 @@ type ConsoleAuthAdapter = Readonly<{
21
20
  getAccess(request: Request): Promise<ConsoleAccess>;
22
21
  getProviders(request: Request): Promise<readonly ConsoleAuthProvider[]>;
23
22
  }>;
24
- type HotUpdaterConsoleConfig = Readonly<Omit<Pick<ConfigInput, "console" | "database" | "storage">, "database"> & {
23
+ type HotUpdaterConsoleConfig = Readonly<{
24
+ /**
25
+ * The database your server runs on, such as `dynamoDB(...)`, or
26
+ * `standaloneRepository(...)`, which reaches a self-hosted server through
27
+ * its admin API.
28
+ */
25
29
  database: ConfiguredDatabase;
30
+ /** The storage your server lists; the console reads bundle files with it. */
31
+ storage: StorageAdapter;
26
32
  /**
27
- * The plugins your server runs, such as `[insights(), apiKeys()]`. The
28
- * console runs them over `database` to read Insights and manage API
29
- * keys, and shows only the features of the plugins listed. A
30
- * `standaloneRepository` database needs none: the console reads its
31
- * server's plugins from the admin `/version`.
33
+ * The plugins your server runs, such as the `plugins` a managed provider
34
+ * package exports or `[insights(), apiKeys()]`. The console runs them over
35
+ * `database` as the server does, and shows only the built-in features of
36
+ * the plugins listed. With `standaloneRepository`, they run on the server,
37
+ * and the console shows the features its admin API serves.
32
38
  */
33
39
  plugins?: readonly AnyHotUpdaterPlugin[];
40
+ console?: {
41
+ /** The Git repository whose commits the console links bundles to. */
42
+ gitUrl?: string;
43
+ };
34
44
  }>;
35
45
  type HotUpdaterConsoleConfigSource = HotUpdaterConsoleConfig | ((request: Request) => HotUpdaterConsoleConfig | Promise<HotUpdaterConsoleConfig>);
36
46
  declare const defineConsoleConfig: <const TConfig extends HotUpdaterConsoleConfigSource>(config: TConfig) => TConfig;
@@ -38,7 +38,7 @@ const createConsoleModulesPlugin = (mode) => {
38
38
  const cacheDirectory = path.join(root, ".hot-updater/console");
39
39
  generatedRouteTreeFile = path.join(cacheDirectory, "routeTree.gen.ts");
40
40
  authFile = resolveFromRoot(root, mode.options.auth ?? "console.auth.ts");
41
- configFile = resolveFromRoot(root, mode.options.config ?? "hot-updater.config.ts");
41
+ configFile = resolveFromRoot(root, mode.options.config ?? "console.config.ts");
42
42
  writeFileIfChanged(path.join(cacheDirectory, "router.ts"), [`export { getRouter } from ${JSON.stringify(virtualPackageRouterModuleId)};`, ""].join("\n"));
43
43
  }
44
44
  return {
@@ -69,17 +69,24 @@ const createConsoleModulesPlugin = (mode) => {
69
69
  if (id === resolvedVirtualPackageRouterModuleId) return `export { getRouter } from ${JSON.stringify(toImportPath(packageRouterFile))};`;
70
70
  if (id === resolvedVirtualConfigModuleId) {
71
71
  if (mode.type === "local") return [
72
- "import { loadConfig, loadHotUpdaterPlugins } from \"@hot-updater/cli-tools\";",
72
+ "import { loadConfig } from \"@hot-updater/cli-tools\";",
73
73
  "export default async () => {",
74
- " const [config, plugins] = await Promise.all([",
75
- " loadConfig(null),",
76
- " loadHotUpdaterPlugins(),",
77
- " ]);",
74
+ " const config = await loadConfig(null);",
75
+ " if (config.database === undefined) {",
76
+ " throw new Error(",
77
+ " \"Set database in hot-updater.config.ts: the database your server runs on, such as d1Database(...), or standaloneRepository({ baseUrl }) to reach a self-hosted server through its admin API.\",",
78
+ " );",
79
+ " }",
80
+ " if (config.storage === undefined) {",
81
+ " throw new Error(",
82
+ " \"Set storage in hot-updater.config.ts: where your server stores bundles, which the console reads, such as r2Storage(...).\",",
83
+ " );",
84
+ " }",
78
85
  " return {",
79
- " console: { gitUrl: config.console.gitUrl },",
80
86
  " database: config.database,",
81
87
  " storage: config.storage,",
82
- " ...(plugins === undefined ? {} : { plugins }),",
88
+ " plugins: config.plugins,",
89
+ " console: { gitUrl: config.console.gitUrl },",
83
90
  " };",
84
91
  "};"
85
92
  ].join("\n");
package/lib/vite.d.mts CHANGED
@@ -1,7 +1,9 @@
1
1
  import { PluginOption } from "vite";
2
2
  //#region src/vite.d.ts
3
3
  interface HotUpdaterConsolePluginOptions {
4
+ /** The console's auth module, `console.auth.ts` by default. */
4
5
  readonly auth?: string;
6
+ /** The module whose default export is `defineConsoleConfig(...)`, `console.config.ts` by default. */
5
7
  readonly config?: string;
6
8
  }
7
9
  declare const hotUpdaterConsole: (options?: HotUpdaterConsolePluginOptions) => PluginOption[];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hot-updater/console",
3
3
  "type": "module",
4
- "version": "1.0.0-rc.20",
4
+ "version": "1.0.0-rc.21",
5
5
  "files": [
6
6
  ".output",
7
7
  "lib",
@@ -38,11 +38,11 @@
38
38
  "@fontsource-variable/inter": "^5.2.8",
39
39
  "@tailwindcss/vite": "^4.2.1",
40
40
  "@tanstack/react-devtools": "^0.10.0",
41
- "@tanstack/react-query": "^5.90.21",
42
- "@tanstack/react-router": "^1.167.0",
43
- "@tanstack/react-router-devtools": "^1.166.7",
44
- "@tanstack/react-router-ssr-query": "^1.166.7",
45
- "@tanstack/react-start": "^1.168.25",
41
+ "@tanstack/react-query": "^5.104.0",
42
+ "@tanstack/react-router": "^1.170.41",
43
+ "@tanstack/react-router-devtools": "^1.167.2",
44
+ "@tanstack/react-router-ssr-query": "^1.167.3",
45
+ "@tanstack/react-start": "^1.168.60",
46
46
  "@vitejs/plugin-react": "^6.0.1",
47
47
  "class-variance-authority": "^0.7.1",
48
48
  "clsx": "^2.1.1",
@@ -58,16 +58,16 @@
58
58
  "tailwindcss": "^4.2.1",
59
59
  "tw-animate-css": "^1.4.0",
60
60
  "verkit": "0.4.0",
61
- "@hot-updater/plugin-core": "1.0.0-rc.17",
62
- "@hot-updater/server": "1.0.0-rc.18",
63
- "@hot-updater/core": "1.0.0-rc.15",
64
- "@hot-updater/bsdiff": "1.0.0-rc.14"
61
+ "@hot-updater/bsdiff": "1.0.0-rc.21",
62
+ "@hot-updater/protocol": "1.0.0-rc.21",
63
+ "@hot-updater/server": "1.0.0-rc.21",
64
+ "@hot-updater/plugin-core": "1.0.0-rc.21"
65
65
  },
66
66
  "devDependencies": {
67
67
  "@tanstack/devtools-vite": "^0.6.0",
68
68
  "@tanstack/react-form": "^1.28.5",
69
69
  "@tanstack/react-table": "^8.21.3",
70
- "@tanstack/router-plugin": "^1.166.9",
70
+ "@tanstack/router-plugin": "^1.168.42",
71
71
  "@testing-library/dom": "^10.4.0",
72
72
  "@testing-library/react": "^16.3.2",
73
73
  "@types/node": "^25.5.0",
@@ -86,11 +86,11 @@
86
86
  "vite-tsconfig-paths": "^6.1.1",
87
87
  "vitest": "4.1.4",
88
88
  "web-vitals": "^5.1.0",
89
- "@hot-updater/cli-tools": "1.0.0-rc.17",
90
- "@hot-updater/mock": "1.0.0-rc.17"
89
+ "@hot-updater/cli-tools": "1.0.0-rc.21",
90
+ "@hot-updater/mock": "1.0.0-rc.21"
91
91
  },
92
92
  "description": "React Native OTA solution for self-hosted",
93
- "license": "MIT",
93
+ "license": "SEE LICENSE IN LICENSE",
94
94
  "repository": {
95
95
  "type": "git",
96
96
  "url": "https://github.com/gronxb/hot-updater"
@@ -169,6 +169,15 @@ export function AppSidebar({ canSignOut = false }: { canSignOut?: boolean }) {
169
169
  </SidebarMenuButton>
170
170
  </SidebarMenuItem>
171
171
  </SidebarMenu>
172
+ {/* The Console's license asks a hosted service to keep this notice. */}
173
+ <a
174
+ href="https://hot-updater.dev"
175
+ target="_blank"
176
+ rel="noreferrer"
177
+ className="px-2 pb-1 text-[11px] text-sidebar-foreground/60 hover:text-sidebar-foreground group-data-[collapsible=icon]:hidden"
178
+ >
179
+ Powered by hot-updater
180
+ </a>
172
181
  </SidebarFooter>
173
182
  </Sidebar>
174
183
  );
@@ -57,9 +57,7 @@ export function ConsoleFeatureUnavailable({
57
57
  <EmptyTitle>{label} not installed</EmptyTitle>
58
58
  <EmptyDescription>
59
59
  Add {pluginCall} to <code className="font-mono">plugins</code>{" "}
60
- {remote
61
- ? "where you create the server."
62
- : "where you create the server and in the console config."}
60
+ where you create the server and in the console config.
63
61
  </EmptyDescription>
64
62
  </>
65
63
  )}
@@ -1,5 +1,5 @@
1
- import { getPatchBaseFileHash, getPatchFileHash } from "@hot-updater/core";
2
1
  import type { Bundle, ReleaseRow } from "@hot-updater/plugin-core";
2
+ import { getPatchBaseFileHash, getPatchFileHash } from "@hot-updater/protocol";
3
3
  import { ExternalLink } from "lucide-react";
4
4
 
5
5
  import { HashValueDisplay } from "@/components/HashValueDisplay";
@@ -2,7 +2,7 @@ import {
2
2
  getNumericCohortRolloutPosition,
3
3
  NUMERIC_COHORT_SIZE,
4
4
  normalizeRolloutCohortCount,
5
- } from "@hot-updater/core";
5
+ } from "@hot-updater/protocol";
6
6
  import { List } from "lucide-react";
7
7
 
8
8
  import { Badge } from "@/components/ui/badge";
@@ -1,14 +1,14 @@
1
- import {
2
- getPatchBaseBundleId,
3
- INVALID_COHORT_ERROR_MESSAGE,
4
- isValidCohort,
5
- normalizeCohortValue,
6
- } from "@hot-updater/core";
7
1
  import type {
8
2
  ChannelRow,
9
3
  ReleasePolicyPatch,
10
4
  ReleaseRow,
11
5
  } from "@hot-updater/plugin-core";
6
+ import {
7
+ getPatchBaseBundleId,
8
+ INVALID_COHORT_ERROR_MESSAGE,
9
+ isValidCohort,
10
+ normalizeCohortValue,
11
+ } from "@hot-updater/protocol";
12
12
  import { AlertTriangle, Download, Plus, X } from "lucide-react";
13
13
  import { useEffect, useState } from "react";
14
14
  import { toast } from "sonner";
package/src/index.ts CHANGED
@@ -1,5 +1,8 @@
1
- import type { ConfigInput, ConfiguredDatabase } from "@hot-updater/plugin-core";
2
- import type { AnyHotUpdaterPlugin } from "@hot-updater/server/plugins";
1
+ import type {
2
+ AnyHotUpdaterPlugin,
3
+ ConfiguredDatabase,
4
+ StorageAdapter,
5
+ } from "@hot-updater/plugin-core";
3
6
 
4
7
  export type ConsoleAuthProvider = "google" | "github";
5
8
 
@@ -20,19 +23,28 @@ export type ConsoleAuthAdapter = Readonly<{
20
23
  getProviders(request: Request): Promise<readonly ConsoleAuthProvider[]>;
21
24
  }>;
22
25
 
23
- export type HotUpdaterConsoleConfig = Readonly<
24
- Omit<Pick<ConfigInput, "console" | "database" | "storage">, "database"> & {
25
- database: ConfiguredDatabase;
26
- /**
27
- * The plugins your server runs, such as `[insights(), apiKeys()]`. The
28
- * console runs them over `database` to read Insights and manage API
29
- * keys, and shows only the features of the plugins listed. A
30
- * `standaloneRepository` database needs none: the console reads its
31
- * server's plugins from the admin `/version`.
32
- */
33
- plugins?: readonly AnyHotUpdaterPlugin[];
34
- }
35
- >;
26
+ export type HotUpdaterConsoleConfig = Readonly<{
27
+ /**
28
+ * The database your server runs on, such as `dynamoDB(...)`, or
29
+ * `standaloneRepository(...)`, which reaches a self-hosted server through
30
+ * its admin API.
31
+ */
32
+ database: ConfiguredDatabase;
33
+ /** The storage your server lists; the console reads bundle files with it. */
34
+ storage: StorageAdapter;
35
+ /**
36
+ * The plugins your server runs, such as the `plugins` a managed provider
37
+ * package exports or `[insights(), apiKeys()]`. The console runs them over
38
+ * `database` as the server does, and shows only the built-in features of
39
+ * the plugins listed. With `standaloneRepository`, they run on the server,
40
+ * and the console shows the features its admin API serves.
41
+ */
42
+ plugins?: readonly AnyHotUpdaterPlugin[];
43
+ console?: {
44
+ /** The Git repository whose commits the console links bundles to. */
45
+ gitUrl?: string;
46
+ };
47
+ }>;
36
48
 
37
49
  export type HotUpdaterConsoleConfigSource =
38
50
  | HotUpdaterConsoleConfig
@@ -34,14 +34,6 @@ type GetBundleChildCountsInput = {
34
34
  bundleIds: string[];
35
35
  };
36
36
 
37
- type DeleteBundleInput = {
38
- bundleId: string;
39
- };
40
-
41
- type DeleteBundlesInput = {
42
- bundleIds: string[];
43
- };
44
-
45
37
  type GetReleasesInput = {
46
38
  /** One of the filter sets the release indexes serve. */
47
39
  filter?: ReleaseFilter;
@@ -160,7 +152,7 @@ export const getConfig = createServerFn()
160
152
  .handler(async () => {
161
153
  try {
162
154
  const { config } = await prepare();
163
- return { console: config.console };
155
+ return { console: { gitUrl: config.gitUrl } };
164
156
  } catch (error) {
165
157
  console.error("Error during config retrieval:", error);
166
158
  throw error;
@@ -336,48 +328,3 @@ export const getBundleChildCounts = createServerFn({ method: "GET" })
336
328
  throw error;
337
329
  }
338
330
  });
339
-
340
- // DELETE /api/bundles/:bundleId
341
- export const deleteBundle = createServerFn({ method: "POST" })
342
- .middleware([consoleAccess])
343
- .validator((input: DeleteBundleInput) => input)
344
- .handler(async ({ data }) => {
345
- try {
346
- const { deleteBundle: deleteBundleWithStorage } =
347
- await import("./server/deleteBundle");
348
- const { core, storagePlugin } = await prepare();
349
-
350
- await deleteBundleWithStorage(data, {
351
- core,
352
- storagePlugin,
353
- waitForStorageCleanup: false,
354
- });
355
-
356
- return { success: true };
357
- } catch (error) {
358
- console.error("Error during bundle deletion:", error);
359
- throw error;
360
- }
361
- });
362
-
363
- export const deleteBundles = createServerFn({ method: "POST" })
364
- .middleware([consoleAccess])
365
- .validator((input: DeleteBundlesInput) => input)
366
- .handler(async ({ data }) => {
367
- try {
368
- const { deleteBundles: deleteBundlesWithStorage } =
369
- await import("./server/deleteBundle");
370
- const { core, storagePlugin } = await prepare();
371
-
372
- const result = await deleteBundlesWithStorage(data, {
373
- core,
374
- storagePlugin,
375
- waitForStorageCleanup: false,
376
- });
377
-
378
- return { success: true, ...result };
379
- } catch (error) {
380
- console.error("Error during bundle deletion:", error);
381
- throw error;
382
- }
383
- });
package/src/lib/api.ts CHANGED
@@ -2,19 +2,11 @@ import type {
2
2
  ReleaseFilter,
3
3
  ReleasePolicyPatch,
4
4
  } from "@hot-updater/plugin-core";
5
- import {
6
- type QueryClient,
7
- type QueryKey,
8
- useMutation,
9
- useQuery,
10
- useQueryClient,
11
- } from "@tanstack/react-query";
5
+ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
12
6
 
13
7
  import {
14
8
  createChannel as createChannelApi,
15
9
  deleteChannel as deleteChannelApi,
16
- deleteBundle as deleteBundleApi,
17
- deleteBundles as deleteBundlesApi,
18
10
  deleteRelease as deleteReleaseApi,
19
11
  getBundle,
20
12
  getBundleChildCounts,
@@ -40,8 +32,6 @@ type BundleFilters = {
40
32
  before?: string;
41
33
  };
42
34
 
43
- type BundlesQueryData = Awaited<ReturnType<typeof getBundles>>;
44
-
45
35
  const bundleListQueryKey = ["bundles"] as const;
46
36
  const releaseListQueryKey = ["releases"] as const;
47
37
 
@@ -80,42 +70,6 @@ export type ReleaseFilters = {
80
70
  limit?: number;
81
71
  };
82
72
 
83
- function removeBundleFromQueryData(
84
- data: BundlesQueryData | undefined,
85
- bundleId: string,
86
- ) {
87
- if (!data) {
88
- return data;
89
- }
90
-
91
- return {
92
- ...data,
93
- data: data.data.filter((bundle) => bundle.id !== bundleId),
94
- };
95
- }
96
-
97
- function removeBundlesFromQueryData(
98
- data: BundlesQueryData | undefined,
99
- bundleIds: readonly string[],
100
- ) {
101
- if (!data) {
102
- return data;
103
- }
104
-
105
- const bundleIdSet = new Set(bundleIds);
106
- return {
107
- ...data,
108
- data: data.data.filter((bundle) => !bundleIdSet.has(bundle.id)),
109
- };
110
- }
111
-
112
- const invalidateInBackground = (
113
- queryClient: QueryClient,
114
- queryKey: QueryKey,
115
- ) => {
116
- void queryClient.invalidateQueries({ queryKey }).catch(() => undefined);
117
- };
118
-
119
73
  // Query Hooks
120
74
  export function useConfigQuery() {
121
75
  return useQuery({
@@ -231,48 +185,6 @@ export function useDeleteChannelMutation() {
231
185
  });
232
186
  }
233
187
 
234
- export function useDeleteBundleMutation() {
235
- const queryClient = useQueryClient();
236
-
237
- return useMutation({
238
- mutationFn: (params: { bundleId: string }) =>
239
- deleteBundleApi({ data: params }),
240
- onSuccess: (_, vars) => {
241
- queryClient.removeQueries({ queryKey: queryKeys.bundle(vars.bundleId) });
242
- queryClient.setQueriesData(
243
- { queryKey: queryKeys.bundles.all },
244
- (data: BundlesQueryData | undefined) =>
245
- removeBundleFromQueryData(data, vars.bundleId),
246
- );
247
-
248
- invalidateInBackground(queryClient, queryKeys.bundles.all);
249
- invalidateInBackground(queryClient, queryKeys.bundleChildren.all);
250
- },
251
- });
252
- }
253
-
254
- export function useDeleteBundlesMutation() {
255
- const queryClient = useQueryClient();
256
-
257
- return useMutation({
258
- mutationFn: (params: { bundleIds: string[] }) =>
259
- deleteBundlesApi({ data: params }),
260
- onSuccess: (_, vars) => {
261
- for (const bundleId of vars.bundleIds) {
262
- queryClient.removeQueries({ queryKey: queryKeys.bundle(bundleId) });
263
- }
264
- queryClient.setQueriesData(
265
- { queryKey: queryKeys.bundles.all },
266
- (data: BundlesQueryData | undefined) =>
267
- removeBundlesFromQueryData(data, vars.bundleIds),
268
- );
269
-
270
- invalidateInBackground(queryClient, queryKeys.bundles.all);
271
- invalidateInBackground(queryClient, queryKeys.bundleChildren.all);
272
- },
273
- });
274
- }
275
-
276
188
  export function useUpdateReleaseMutation() {
277
189
  const queryClient = useQueryClient();
278
190
  return useMutation({
@@ -313,10 +225,17 @@ export function useDeleteReleaseMutation() {
313
225
  queryClient.removeQueries({
314
226
  queryKey: queryKeys.release(input.releaseId),
315
227
  });
228
+ // Core deletes the artifact with its last release, and the patches
229
+ // built on it, so the bundle reads can change too.
316
230
  await Promise.all([
317
231
  queryClient.invalidateQueries({ queryKey: queryKeys.releases.all }),
318
232
  queryClient.invalidateQueries({ queryKey: ["release-catalog"] }),
319
233
  queryClient.invalidateQueries({ queryKey: queryKeys.channels }),
234
+ queryClient.invalidateQueries({ queryKey: queryKeys.bundles.all }),
235
+ queryClient.invalidateQueries({ queryKey: ["bundle"] }),
236
+ queryClient.invalidateQueries({
237
+ queryKey: queryKeys.bundleChildren.all,
238
+ }),
320
239
  ]);
321
240
  },
322
241
  });
@@ -1,8 +1,8 @@
1
1
  /**
2
- * The console's features and the server plugin each one needs. The server is
3
- * the source of truth: the console serves a feature only when the server runs
4
- * its plugin, and against a self-hosted server (`standaloneRepository`) only
5
- * the features its admin API serves (`remote`); the rest read the database.
2
+ * The console's features and the server plugin each one needs. The console
3
+ * serves a feature only when its config lists the plugin, as the server runs
4
+ * it, and against a self-hosted server (`standaloneRepository`) only the
5
+ * features its admin API serves (`remote`); the rest read the database.
6
6
  * Navigation, route guards, and server functions all read this registry. Its
7
7
  * other halves, keyed by the same features, hold what serves each feature on
8
8
  * the server (`server/console-feature-apis.ts`) and what each adds to pages
@@ -41,7 +41,7 @@ export type ConsoleFeatureSet = {
41
41
 
42
42
  const featureIds = Object.keys(consoleFeatures) as ConsoleFeature[];
43
43
 
44
- /** The features on for the plugins a server runs, from where the console reads them. */
44
+ /** The features on for the plugins the console's config lists, from where the console reads them. */
45
45
  export const resolveConsoleFeatures = (
46
46
  plugins: readonly string[],
47
47
  { remote }: { readonly remote: boolean },
@@ -15,7 +15,7 @@ export const routeScrollRestorationKey = (
15
15
  router: Pick<AnyRouter, "getMatchedRoutes">,
16
16
  location: ParsedLocation,
17
17
  ): string => {
18
- const { foundRoute } = router.getMatchedRoutes(location.pathname);
18
+ const [, , foundRoute] = router.getMatchedRoutes(location.pathname);
19
19
  const routeKey = foundRoute?.options.staticData?.scrollRestorationKey;
20
20
  return routeKey?.(location) ?? location.state.__TSR_key!;
21
21
  };