@hot-updater/console 0.36.7 → 1.0.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 (262) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/BundleIdDisplay-D4ErdU5_.js +1 -0
  3. package/.output/public/assets/InsightsCapabilityContext-B42VqvP6.js +1 -0
  4. package/.output/public/assets/api-keys-api-DGVTTvTJ.js +1 -0
  5. package/.output/public/assets/api-keys-z16OZIP4.js +1 -0
  6. package/.output/public/assets/badge-Br_KDO0J.js +1 -0
  7. package/.output/public/assets/card-DzuEXvyJ.js +1 -0
  8. package/.output/public/assets/chart-no-axes-combined-FaYMGmQS.js +1 -0
  9. package/.output/public/assets/createServerFn-gM2-LFBy.js +3 -0
  10. package/.output/public/assets/dialog-BeAENgez.js +1 -0
  11. package/.output/public/assets/dist-Bssh_cv0.js +1 -0
  12. package/.output/public/assets/field-Buq148-A.js +1 -0
  13. package/.output/public/assets/index-uOpQAIC4.js +10 -0
  14. package/.output/public/assets/input-group-B3p5UpyW.js +1 -0
  15. package/.output/public/assets/insights-DJTr7Ozn.js +1 -0
  16. package/.output/public/assets/insights-api-DVMOoRMS.js +1 -0
  17. package/.output/public/assets/installations-BY64iIAK.js +2 -0
  18. package/.output/public/assets/installations-D5v9i3AL.js +1 -0
  19. package/.output/public/assets/lazyRouteComponent-DKYu5cZJ.js +1 -0
  20. package/.output/public/assets/preload-helper-CQ36nxok.js +1 -0
  21. package/.output/public/assets/refresh-cw-BkbfZ62O.js +1 -0
  22. package/.output/public/assets/routes-BuoI5-Cr.js +2 -0
  23. package/.output/public/assets/routes-C8fvgETO.js +1 -0
  24. package/.output/public/assets/search-qsIDvS2I.js +1 -0
  25. package/.output/public/assets/shield-check-DE_hVuHu.js +1 -0
  26. package/.output/public/assets/signing-mJD88Y4C.js +1 -0
  27. package/.output/public/assets/styles-BZIG7t42.css +2 -0
  28. package/.output/public/assets/table-RamFEclP.js +1 -0
  29. package/.output/public/assets/useCompositeListItem-ChzDqdpc.js +42 -0
  30. package/.output/public/assets/useMutation-JsGVjQMY.js +1 -0
  31. package/.output/public/assets/useOpenInteractionType-Dwfy-SpA.js +1 -0
  32. package/.output/server/__23tanstack-start-server-fn-resolver-Dftxlm2E.mjs +146 -0
  33. package/.output/server/_libs/@base-ui/react+[...].mjs +16124 -0
  34. package/.output/server/_libs/@reduxjs/toolkit+[...].mjs +1554 -0
  35. package/.output/server/_libs/@tanstack/react-router+[...].mjs +41 -10
  36. package/.output/server/_libs/@tanstack/react-router-ssr-query+[...].mjs +132 -0
  37. package/.output/server/_libs/d3-array.mjs +231 -0
  38. package/.output/server/_libs/d3-color.mjs +330 -0
  39. package/.output/server/_libs/d3-format.mjs +252 -0
  40. package/.output/server/_libs/d3-interpolate.mjs +207 -0
  41. package/.output/server/_libs/d3-path.mjs +77 -0
  42. package/.output/server/_libs/d3-scale+[...].mjs +1907 -0
  43. package/.output/server/_libs/d3-shape.mjs +818 -0
  44. package/.output/server/_libs/decimal.js-light.mjs +884 -0
  45. package/.output/server/_libs/es-toolkit.mjs +1134 -0
  46. package/.output/server/_libs/eventemitter3.mjs +236 -0
  47. package/.output/server/_libs/immer.mjs +532 -0
  48. package/.output/server/_libs/lucide-react.mjs +141 -13
  49. package/.output/server/_libs/next-themes.mjs +2 -2
  50. package/.output/server/_libs/react-redux+[...].mjs +205 -0
  51. package/.output/server/_libs/recharts+[...].mjs +14117 -0
  52. package/.output/server/_libs/sonner.mjs +2 -2
  53. package/.output/server/_libs/tanstack__query-core.mjs +114 -1
  54. package/.output/server/_libs/tanstack__react-query.mjs +3 -4
  55. package/.output/server/_runtime.mjs +10 -1
  56. package/.output/server/_ssr/BundleActivityChart-CWcdPuUn.mjs +288 -0
  57. package/.output/server/_ssr/BundleIdDisplay-BEvB3MvA.mjs +13 -0
  58. package/.output/server/_ssr/InsightsCapabilityContext-Cx8fjm2s.mjs +15 -0
  59. package/.output/server/_ssr/api-keys-CpBqHpuC.mjs +300 -0
  60. package/.output/server/_ssr/api-keys-api-Czkp57UV.mjs +60 -0
  61. package/.output/server/_ssr/api-keys-rpc-BTtMDSoR.mjs +136 -0
  62. package/.output/server/_ssr/api-rpc-BEX8ISai.mjs +1468 -0
  63. package/.output/server/_ssr/auth-BF8mbAgF.mjs +21 -0
  64. package/.output/server/_ssr/auth-rpc-BFMEC-TT.mjs +24 -0
  65. package/.output/server/_ssr/auth.server-DW71WzsD.mjs +47 -0
  66. package/.output/server/_ssr/badge-CpWXQwyF.mjs +26 -0
  67. package/.output/server/_ssr/bundle-signing-rpc-BbCFUQSu.mjs +16 -0
  68. package/.output/server/_ssr/bundle-signing.server-C4NW5mWx.mjs +12 -0
  69. package/.output/server/_ssr/card-Bsc8mRis.mjs +86 -0
  70. package/.output/server/_ssr/config-Dv8qLDZ0.mjs +13 -0
  71. package/.output/server/_ssr/config.server-DQYEr_5X.mjs +361 -0
  72. package/.output/server/_ssr/{createServerFn-DzU0k62V.mjs → createServerFn-B2hcXoPM.mjs} +1 -1
  73. package/.output/server/_ssr/createServerRpc-DB29C_5p.mjs +12 -0
  74. package/.output/server/_ssr/{sidebar-Cv_H0_Jv.mjs → createSsrRpc-DhfrxEjg.mjs} +68 -50
  75. package/.output/server/_ssr/databaseRows-QlCYLi0H.mjs +166 -0
  76. package/.output/server/_ssr/deleteBundle-Cu_XOMt0.mjs +66 -0
  77. package/.output/server/_ssr/dialog-C0vKUp8R.mjs +170 -0
  78. package/.output/server/_ssr/dist-BVSLTpoP.mjs +250 -0
  79. package/.output/server/_ssr/downloadBundle-CNnYxYO2.mjs +24 -0
  80. package/.output/server/_ssr/{empty-plugin-adapters-B4HCzgmM.mjs → empty-plugin-adapters-B5QvGpEl.mjs} +1 -1
  81. package/.output/server/_ssr/field-DASCh-5d.mjs +74 -0
  82. package/.output/server/_ssr/{getBundleChildren-CVtSiH5t.mjs → getBundleChildren-BcRMFoDF.mjs} +10 -18
  83. package/.output/server/_ssr/input-group-etIsBUqv.mjs +288 -0
  84. package/.output/server/_ssr/insights-api-C1F7UreR.mjs +71 -0
  85. package/.output/server/_ssr/insights-input-9fEJF-ZG.mjs +96 -0
  86. package/.output/server/_ssr/insights-rpc-C-8R6kED.mjs +155 -0
  87. package/.output/server/_ssr/insights-ygT_aIfo.mjs +858 -0
  88. package/.output/server/_ssr/installations-DqRT1QTd.mjs +535 -0
  89. package/.output/server/_ssr/installations-K8TCYTYh.mjs +19 -0
  90. package/.output/server/_ssr/router-BxBEvtF1.mjs +513 -0
  91. package/.output/server/_ssr/routes-BC7rn-tf.mjs +37 -0
  92. package/.output/server/_ssr/routes-CM5KAwpB.mjs +2285 -0
  93. package/.output/server/_ssr/runtime.server-la7TRAH_.mjs +527 -0
  94. package/.output/server/_ssr/server-CpzW--lT.mjs +7468 -0
  95. package/.output/server/_ssr/signing-jk4HANnl.mjs +134 -0
  96. package/.output/server/_ssr/ssr.mjs +4 -5740
  97. package/.output/server/_ssr/start-DpViQGnt.mjs +4 -0
  98. package/.output/server/_ssr/table-CU58HeI7.mjs +53 -0
  99. package/.output/server/_ssr/uuidv7-CaKuKXrV.mjs +31 -0
  100. package/.output/server/_tanstack-start-manifest_v-Mz9MmSlD.mjs +103 -0
  101. package/.output/server/index.mjs +229 -47
  102. package/README.md +29 -8
  103. package/lib/index.d.mts +35 -0
  104. package/lib/index.mjs +4 -0
  105. package/lib/vite-internal.mjs +127 -0
  106. package/lib/vite.d.mts +10 -0
  107. package/lib/vite.mjs +5 -0
  108. package/package.json +51 -29
  109. package/public/apple-touch-icon.png +0 -0
  110. package/public/favicon-16x16.png +0 -0
  111. package/public/favicon-32x32.png +0 -0
  112. package/public/favicon.ico +0 -0
  113. package/public/logo.svg +1 -0
  114. package/public/manifest.json +30 -0
  115. package/public/robots.txt +3 -0
  116. package/src/components/AppSidebar.tsx +159 -0
  117. package/src/components/BundleIdDisplay.tsx +18 -0
  118. package/src/components/ChannelBadge.tsx +31 -0
  119. package/src/components/ConsoleAccessPage.tsx +122 -0
  120. package/src/components/EnabledStatusIcon.tsx +54 -0
  121. package/src/components/HashValueDisplay.tsx +85 -0
  122. package/src/components/HotUpdaterLogo.tsx +30 -0
  123. package/src/components/NotFoundPage.tsx +33 -0
  124. package/src/components/PlatformIcon.tsx +39 -0
  125. package/src/components/RolloutPercentageBadge.tsx +28 -0
  126. package/src/components/ThemeProvider.tsx +116 -0
  127. package/src/components/TimestampDisplay.tsx +41 -0
  128. package/src/components/features/api-keys/ApiKeysPage.tsx +377 -0
  129. package/src/components/features/bundles/BundleActivityChart.tsx +193 -0
  130. package/src/components/features/bundles/BundleChildrenPanel.tsx +201 -0
  131. package/src/components/features/bundles/BundleInsightsSummary.tsx +123 -0
  132. package/src/components/features/bundles/BundleMetadata.tsx +136 -0
  133. package/src/components/features/bundles/RolloutCohortsDialog.tsx +201 -0
  134. package/src/components/features/channels/ChannelManagementDialog.tsx +228 -0
  135. package/src/components/features/insights/ActivityChart.tsx +150 -0
  136. package/src/components/features/insights/BundleDistribution.tsx +133 -0
  137. package/src/components/features/insights/BundleSelector.tsx +114 -0
  138. package/src/components/features/insights/InsightsCapabilityContext.tsx +24 -0
  139. package/src/components/features/insights/InsightsControls.tsx +115 -0
  140. package/src/components/features/insights/InsightsErrorAlert.tsx +21 -0
  141. package/src/components/features/insights/InsightsOverview.tsx +208 -0
  142. package/src/components/features/insights/InstallationHistoryCard.tsx +248 -0
  143. package/src/components/features/insights/InstallationMatchesCard.tsx +132 -0
  144. package/src/components/features/insights/InstallationPageHeader.tsx +138 -0
  145. package/src/components/features/insights/InstallationPagination.tsx +48 -0
  146. package/src/components/features/insights/InstallationSearch.tsx +230 -0
  147. package/src/components/features/insights/UpdateOutcomes.tsx +147 -0
  148. package/src/components/features/releases/ReleaseEditorSheet.tsx +879 -0
  149. package/src/components/features/releases/ReleaseStateBadge.tsx +20 -0
  150. package/src/components/features/signing/BundleSigningPage.tsx +139 -0
  151. package/src/components/ui/alert-dialog.tsx +185 -0
  152. package/src/components/ui/alert.tsx +76 -0
  153. package/src/components/ui/badge.tsx +49 -0
  154. package/src/components/ui/button.tsx +58 -0
  155. package/src/components/ui/card.tsx +89 -0
  156. package/src/components/ui/chart.tsx +371 -0
  157. package/src/components/ui/combobox.tsx +303 -0
  158. package/src/components/ui/dialog.tsx +156 -0
  159. package/src/components/ui/dropdown-menu.tsx +270 -0
  160. package/src/components/ui/field.tsx +235 -0
  161. package/src/components/ui/input-group.tsx +147 -0
  162. package/src/components/ui/input.tsx +19 -0
  163. package/src/components/ui/label.tsx +25 -0
  164. package/src/components/ui/progress.tsx +84 -0
  165. package/src/components/ui/select.tsx +205 -0
  166. package/src/components/ui/separator.tsx +23 -0
  167. package/src/components/ui/sheet.tsx +132 -0
  168. package/src/components/ui/sidebar.tsx +779 -0
  169. package/src/components/ui/skeleton.tsx +13 -0
  170. package/src/components/ui/slider.tsx +56 -0
  171. package/src/components/ui/sonner.tsx +45 -0
  172. package/src/components/ui/switch.tsx +25 -0
  173. package/src/components/ui/table.tsx +114 -0
  174. package/src/components/ui/textarea.tsx +18 -0
  175. package/src/components/ui/toggle-group.tsx +89 -0
  176. package/src/components/ui/toggle.tsx +43 -0
  177. package/src/components/ui/tooltip.tsx +66 -0
  178. package/src/hooks/use-mobile.ts +30 -0
  179. package/src/index.ts +47 -0
  180. package/src/lib/api-keys-api.ts +69 -0
  181. package/src/lib/api-keys-rpc.ts +87 -0
  182. package/src/lib/api-rpc.ts +478 -0
  183. package/src/lib/api.ts +444 -0
  184. package/src/lib/auth-rpc.ts +16 -0
  185. package/src/lib/bundle-signing-api.ts +14 -0
  186. package/src/lib/bundle-signing-rpc.ts +16 -0
  187. package/src/lib/constants.ts +1 -0
  188. package/src/lib/git.ts +51 -0
  189. package/src/lib/insights-api.ts +144 -0
  190. package/src/lib/insights-error.ts +19 -0
  191. package/src/lib/insights-input.ts +187 -0
  192. package/src/lib/insights-overview.ts +170 -0
  193. package/src/lib/insights-rpc.ts +218 -0
  194. package/src/lib/server/auth.server.ts +43 -0
  195. package/src/lib/server/bundle-signing.server.ts +23 -0
  196. package/src/lib/server/config.server.ts +109 -0
  197. package/src/lib/server/console-runtime.server.ts +61 -0
  198. package/src/lib/server/deleteBundle.ts +145 -0
  199. package/src/lib/server/downloadBundle.ts +46 -0
  200. package/src/lib/server/getBundleChildren.ts +130 -0
  201. package/src/lib/server/listReleases.ts +61 -0
  202. package/src/lib/server/releaseActivity.ts +27 -0
  203. package/src/lib/server/releaseReachability.ts +95 -0
  204. package/src/lib/server/runtime.server.ts +172 -0
  205. package/src/lib/utils.ts +9 -0
  206. package/src/logo.svg +1 -0
  207. package/src/routeTree.gen.ts +199 -0
  208. package/src/router.tsx +21 -0
  209. package/src/routes/-installations-search.ts +19 -0
  210. package/src/routes/-releases-search.ts +74 -0
  211. package/src/routes/__root.tsx +174 -0
  212. package/src/routes/api/auth/$.ts +19 -0
  213. package/src/routes/api/bundles/$bundleId/download.ts +17 -0
  214. package/src/routes/api-keys.tsx +37 -0
  215. package/src/routes/index.tsx +982 -0
  216. package/src/routes/insights.tsx +139 -0
  217. package/src/routes/installations.tsx +194 -0
  218. package/src/routes/signing.tsx +37 -0
  219. package/src/styles.css +152 -0
  220. package/src/virtual-modules.d.ts +21 -0
  221. package/src/vite-env.d.ts +1 -0
  222. package/src/vite-internal.ts +195 -0
  223. package/src/vite.ts +12 -0
  224. package/.output/public/assets/dist-DJX53faP.js +0 -3
  225. package/.output/public/assets/index-Iylis5kY.js +0 -10
  226. package/.output/public/assets/routes-HT21hs0C.js +0 -54
  227. package/.output/public/assets/styles-C0Re4quB.css +0 -2
  228. package/.output/server/__23tanstack-start-server-fn-resolver-CgwgZ0-i.mjs +0 -66
  229. package/.output/server/_libs/@floating-ui/core+[...].mjs +0 -698
  230. package/.output/server/_libs/@floating-ui/dom+[...].mjs +0 -644
  231. package/.output/server/_libs/@floating-ui/react-dom+[...].mjs +0 -839
  232. package/.output/server/_libs/@radix-ui/react-alert-dialog+[...].mjs +0 -2138
  233. package/.output/server/_libs/@radix-ui/react-popper+[...].mjs +0 -308
  234. package/.output/server/_libs/@radix-ui/react-select+[...].mjs +0 -1038
  235. package/.output/server/_libs/@tanstack/devtools-event-client+[...].mjs +0 -196
  236. package/.output/server/_libs/@tanstack/form-core+[...].mjs +0 -3228
  237. package/.output/server/_libs/@tanstack/react-form+[...].mjs +0 -430
  238. package/.output/server/_libs/@tanstack/react-table+[...].mjs +0 -2382
  239. package/.output/server/_libs/radix-ui__number.mjs +0 -6
  240. package/.output/server/_libs/radix-ui__primitive.mjs +0 -9
  241. package/.output/server/_libs/radix-ui__react-arrow.mjs +0 -23
  242. package/.output/server/_libs/radix-ui__react-collection.mjs +0 -78
  243. package/.output/server/_libs/radix-ui__react-direction.mjs +0 -13
  244. package/.output/server/_libs/radix-ui__react-label.mjs +0 -22
  245. package/.output/server/_libs/radix-ui__react-separator.mjs +0 -30
  246. package/.output/server/_libs/radix-ui__react-slider.mjs +0 -510
  247. package/.output/server/_libs/radix-ui__react-switch.mjs +0 -157
  248. package/.output/server/_libs/radix-ui__react-tooltip.mjs +0 -493
  249. package/.output/server/_ssr/api-rpc-BIbDUKSz.mjs +0 -292
  250. package/.output/server/_ssr/config.server-BSS366KT.mjs +0 -41
  251. package/.output/server/_ssr/deleteBundle-BgbZTtpW.mjs +0 -141
  252. package/.output/server/_ssr/dist-KboydRVk.mjs +0 -120
  253. package/.output/server/_ssr/router-C3hqEy3k.mjs +0 -307
  254. package/.output/server/_ssr/routes-B0lQvvlC.mjs +0 -3048
  255. package/.output/server/_ssr/start-02sLteYG.mjs +0 -4
  256. package/.output/server/_ssr/storageProfile-FVkjPoGb.mjs +0 -9
  257. package/.output/server/_tanstack-start-manifest_v-Bpx52WOU.mjs +0 -20
  258. package/.output/server/node_modules/tslib/modules/index.js +0 -70
  259. package/.output/server/node_modules/tslib/modules/package.json +0 -3
  260. package/.output/server/node_modules/tslib/package.json +0 -47
  261. package/.output/server/node_modules/tslib/tslib.js +0 -484
  262. package/.output/server/package.json +0 -9
@@ -0,0 +1,527 @@
1
+ import { t as createUUIDv7 } from "./uuidv7-CaKuKXrV.mjs";
2
+ import { a as parseSearchInstallationsInput, i as parseInstallationHistoryInput, n as parseBundleEventInsightsInput, r as parseBundleEventSummaryInput, t as parseActiveInstallationInput } from "./insights-input-9fEJF-ZG.mjs";
3
+ //#region node_modules/.nitro/vite/services/ssr/assets/runtime.server-la7TRAH_.js
4
+ var InsightsScanLimitExceededError = class extends Error {
5
+ constructor(limit) {
6
+ super(`Insights event scan exceeded ${limit} rows.`);
7
+ this.limit = limit;
8
+ this.name = "InsightsScanLimitExceededError";
9
+ }
10
+ };
11
+ var HOUR_MS = 3600 * 1e3;
12
+ var DAY_MS = 24 * HOUR_MS;
13
+ var ACTIVE_BUNDLE_EVENT_TYPES = [
14
+ "UPDATE_APPLIED",
15
+ "RECOVERED",
16
+ "RELEASE_ADOPTED",
17
+ "UNCHANGED"
18
+ ];
19
+ var activeWindowDefinitions = {
20
+ "24h": {
21
+ bucketCount: 24,
22
+ bucketSizeMs: HOUR_MS
23
+ },
24
+ "7d": {
25
+ bucketCount: 7,
26
+ bucketSizeMs: DAY_MS
27
+ },
28
+ "30d": {
29
+ bucketCount: 30,
30
+ bucketSizeMs: DAY_MS
31
+ }
32
+ };
33
+ function getActiveWindowDefinition(window) {
34
+ return activeWindowDefinitions[window];
35
+ }
36
+ function compareCodePoints$1(left, right) {
37
+ if (left < right) return -1;
38
+ if (left > right) return 1;
39
+ return 0;
40
+ }
41
+ function isNewer(candidate, current) {
42
+ return candidate.received_at_ms > current.received_at_ms || candidate.received_at_ms === current.received_at_ms && compareCodePoints$1(candidate.id, current.id) > 0;
43
+ }
44
+ function collectActiveInstallationOverview(request) {
45
+ const definition = getActiveWindowDefinition(request.window);
46
+ const windowStartMs = request.asOfMs - definition.bucketCount * definition.bucketSizeMs;
47
+ const rows = request.rows.filter((row) => row.received_at_ms >= windowStartMs && row.received_at_ms < request.asOfMs);
48
+ const latestByInstall = /* @__PURE__ */ new Map();
49
+ for (const row of rows) {
50
+ const current = latestByInstall.get(row.install_id);
51
+ if (current === void 0 || isNewer(row, current)) latestByInstall.set(row.install_id, row);
52
+ }
53
+ const selectedRows = [...latestByInstall.values()].filter((row) => request.userId === void 0 || row.user_id === request.userId);
54
+ const selectedInstallIds = new Set(selectedRows.map((row) => row.install_id));
55
+ const bundleCounts = /* @__PURE__ */ new Map();
56
+ for (const row of selectedRows) bundleCounts.set(row.to_bundle_id, (bundleCounts.get(row.to_bundle_id) ?? 0) + 1);
57
+ const latestByBucket = Array.from({ length: definition.bucketCount }, () => /* @__PURE__ */ new Map());
58
+ for (const row of rows) {
59
+ if (!selectedInstallIds.has(row.install_id)) continue;
60
+ const bucket = latestByBucket[Math.floor((row.received_at_ms - windowStartMs) / definition.bucketSizeMs)];
61
+ if (bucket === void 0) continue;
62
+ const current = bucket.get(row.install_id);
63
+ if (current === void 0 || isNewer(row, current)) bucket.set(row.install_id, row);
64
+ }
65
+ const bundleCountsByBucket = latestByBucket.map((bucket) => {
66
+ const counts = /* @__PURE__ */ new Map();
67
+ for (const row of bucket.values()) counts.set(row.to_bundle_id, (counts.get(row.to_bundle_id) ?? 0) + 1);
68
+ return counts;
69
+ });
70
+ const bundleObservationTotals = /* @__PURE__ */ new Map();
71
+ for (const counts of bundleCountsByBucket) for (const [bundleId, count] of counts) bundleObservationTotals.set(bundleId, (bundleObservationTotals.get(bundleId) ?? 0) + count);
72
+ return {
73
+ asOfMs: request.asOfMs,
74
+ window: request.window,
75
+ activeInstallations: selectedInstallIds.size,
76
+ series: latestByBucket.map((bucket, index) => ({
77
+ bucketStartMs: windowStartMs + index * definition.bucketSizeMs,
78
+ value: bucket.size
79
+ })),
80
+ bundleSeries: [...bundleObservationTotals].sort(([leftId, leftTotal], [rightId, rightTotal]) => rightTotal - leftTotal || compareCodePoints$1(leftId, rightId)).map(([bundleId]) => ({
81
+ bundleId,
82
+ series: bundleCountsByBucket.map((counts, index) => ({
83
+ bucketStartMs: windowStartMs + index * definition.bucketSizeMs,
84
+ value: counts.get(bundleId) ?? 0
85
+ }))
86
+ })),
87
+ bundles: [...bundleCounts].map(([bundleId, installations]) => ({
88
+ bundleId,
89
+ installations
90
+ })).sort((left, right) => right.installations - left.installations || compareCodePoints$1(left.bundleId, right.bundleId))
91
+ };
92
+ }
93
+ var INSIGHTS_SCAN_MAX_ROWS = 5e4;
94
+ var INSIGHTS_MATERIALIZATION_LIMIT = INSIGHTS_SCAN_MAX_ROWS + 1;
95
+ var INSIGHTS_SCAN_PAGE_SIZE = 1e3;
96
+ var INSIGHTS_LOWER_BOUND_ID = "00000000-0000-0000-0000-000000000000";
97
+ var compareCodePoints = (left, right) => {
98
+ if (left < right) return -1;
99
+ if (left > right) return 1;
100
+ return 0;
101
+ };
102
+ var compareEventNewest = (left, right) => right.received_at_ms - left.received_at_ms || compareCodePoints(right.id, left.id);
103
+ var compareEventOldest = (left, right) => left.received_at_ms - right.received_at_ms || compareCodePoints(left.id, right.id);
104
+ var materializeEventRows = async (scope) => {
105
+ const rows = [];
106
+ const seenIds = /* @__PURE__ */ new Set();
107
+ let after = scope.lowerBoundMs === void 0 ? void 0 : {
108
+ receivedAtMs: scope.lowerBoundMs,
109
+ id: INSIGHTS_LOWER_BOUND_ID
110
+ };
111
+ while (rows.length < INSIGHTS_MATERIALIZATION_LIMIT) {
112
+ const limit = Math.min(INSIGHTS_SCAN_PAGE_SIZE, INSIGHTS_MATERIALIZATION_LIMIT - rows.length);
113
+ const page = await scope.persistence.scan({
114
+ beforeReceivedAtMs: scope.cutoffMs,
115
+ ...after === void 0 ? {} : { after },
116
+ limit
117
+ });
118
+ if (page.length === 0) break;
119
+ if (page.length > limit) throw new InsightsPersistenceOrderError();
120
+ let previous = after === void 0 ? void 0 : {
121
+ received_at_ms: after.receivedAtMs,
122
+ id: after.id
123
+ };
124
+ for (const row of page) {
125
+ if (row.received_at_ms >= scope.cutoffMs || previous !== void 0 && compareEventOldest(previous, row) >= 0 || seenIds.has(row.id)) throw new InsightsPersistenceOrderError();
126
+ seenIds.add(row.id);
127
+ rows.push(row);
128
+ previous = row;
129
+ }
130
+ const last = page.at(-1);
131
+ if (last !== void 0) after = {
132
+ receivedAtMs: last.received_at_ms,
133
+ id: last.id
134
+ };
135
+ if (rows.length > 5e4) throw new InsightsScanLimitExceededError(INSIGHTS_SCAN_MAX_ROWS);
136
+ }
137
+ return rows;
138
+ };
139
+ var InsightsPersistenceOrderError = class extends Error {
140
+ name = "InsightsPersistenceOrderError";
141
+ constructor() {
142
+ super("Insights persistence did not advance its scan cursor.");
143
+ }
144
+ };
145
+ var materializeActiveRows = async (scope, window) => {
146
+ const definition = getActiveWindowDefinition(window);
147
+ const durationMs = definition.bucketCount * definition.bucketSizeMs;
148
+ return (await materializeEventRows({
149
+ ...scope,
150
+ lowerBoundMs: scope.cutoffMs - durationMs
151
+ })).filter((row) => row.received_at_ms >= scope.cutoffMs - durationMs && ACTIVE_BUNDLE_EVENT_TYPES.includes(row.type));
152
+ };
153
+ var startOfUtcHour = (value) => {
154
+ const date = new Date(value);
155
+ return Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours());
156
+ };
157
+ var startOfUtcDay = (value) => {
158
+ const date = new Date(value);
159
+ return Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
160
+ };
161
+ var getWindowRange = (window, now) => {
162
+ if (window === "24h") return {
163
+ sizeMs: 3600 * 1e3,
164
+ rangeStart: startOfUtcHour(now) - 1380 * 60 * 1e3
165
+ };
166
+ const days = window === "7d" ? 7 : 30;
167
+ return {
168
+ sizeMs: 1440 * 60 * 1e3,
169
+ rangeStart: startOfUtcDay(now) - (days - 1) * 24 * 60 * 60 * 1e3
170
+ };
171
+ };
172
+ var materializeRowsForWindow = async (scope, window) => {
173
+ if (window === "all") return materializeEventRows(scope);
174
+ const range = getWindowRange(window, scope.cutoffMs);
175
+ return (await materializeEventRows({
176
+ ...scope,
177
+ lowerBoundMs: range.rangeStart
178
+ })).filter(({ received_at_ms }) => received_at_ms >= range.rangeStart);
179
+ };
180
+ var bucketStart = (receivedAtMs, sizeMs) => sizeMs === 3600 * 1e3 ? startOfUtcHour(receivedAtMs) : startOfUtcDay(receivedAtMs);
181
+ var createSeries = (request) => {
182
+ const range = request.window === "all" ? void 0 : getWindowRange(request.window, request.cutoffMs);
183
+ const sizeMs = range?.sizeMs ?? 1440 * 60 * 1e3;
184
+ const installIdsByBucket = /* @__PURE__ */ new Map();
185
+ let oldestMs = request.cutoffMs;
186
+ for (const row of request.rows) {
187
+ oldestMs = Math.min(oldestMs, row.received_at_ms);
188
+ const start = bucketStart(row.received_at_ms, sizeMs);
189
+ const installIds = installIdsByBucket.get(start) ?? /* @__PURE__ */ new Set();
190
+ installIds.add(row.install_id);
191
+ installIdsByBucket.set(start, installIds);
192
+ }
193
+ const first = range?.rangeStart ?? startOfUtcDay(oldestMs);
194
+ const last = bucketStart(request.cutoffMs, sizeMs);
195
+ return Array.from({ length: Math.floor((last - first) / sizeMs) + 1 }, (_, index) => {
196
+ const start = first + index * sizeMs;
197
+ return {
198
+ bucketStartMs: start,
199
+ value: installIdsByBucket.get(start)?.size ?? 0
200
+ };
201
+ });
202
+ };
203
+ var collectEventActivity = (request) => {
204
+ const range = request.window === "all" ? void 0 : getWindowRange(request.window, request.cutoffMs);
205
+ const rows = range === void 0 ? request.rows : request.rows.filter(({ received_at_ms }) => received_at_ms >= range.rangeStart && received_at_ms < request.cutoffMs);
206
+ const installs = /* @__PURE__ */ new Set();
207
+ const installsByCohort = /* @__PURE__ */ new Map();
208
+ for (const row of rows) {
209
+ installs.add(row.install_id);
210
+ const cohort = installsByCohort.get(row.cohort) ?? /* @__PURE__ */ new Set();
211
+ cohort.add(row.install_id);
212
+ installsByCohort.set(row.cohort, cohort);
213
+ }
214
+ return {
215
+ summary: installs.size,
216
+ cohorts: [...installsByCohort].sort(([left], [right]) => compareCodePoints(left, right)).map(([cohort, installIds]) => ({
217
+ cohort,
218
+ value: installIds.size
219
+ })),
220
+ series: createSeries({
221
+ ...request,
222
+ rows
223
+ })
224
+ };
225
+ };
226
+ function toSearchRow(row) {
227
+ return {
228
+ installId: row.install_id,
229
+ username: row.username,
230
+ userId: row.user_id,
231
+ lastKnownBundleId: row.to_bundle_id,
232
+ latestStatus: row.type,
233
+ platform: row.platform,
234
+ appVersion: row.app_version,
235
+ channel: row.channel,
236
+ cohort: row.cohort,
237
+ receivedAtMs: row.received_at_ms
238
+ };
239
+ }
240
+ function matchesIdentity(row, query) {
241
+ return row.install_id.toLowerCase().includes(query) || row.user_id?.toLowerCase().includes(query) === true || row.username?.toLowerCase().includes(query) === true;
242
+ }
243
+ function searchEventInstallations(request) {
244
+ const query = request.query.toLowerCase();
245
+ const matchingInstallIds = /* @__PURE__ */ new Set();
246
+ for (const row of request.rows) if (query.length === 0 || matchesIdentity(row, query)) matchingInstallIds.add(row.install_id);
247
+ const latestByInstall = /* @__PURE__ */ new Map();
248
+ for (const row of request.rows) {
249
+ if (!matchingInstallIds.has(row.install_id)) continue;
250
+ const current = latestByInstall.get(row.install_id);
251
+ if (current === void 0 || compareEventNewest(row, current) < 0) latestByInstall.set(row.install_id, row);
252
+ }
253
+ const matchingRows = [...latestByInstall.values()].sort((left, right) => compareCodePoints(left.install_id, right.install_id));
254
+ const pageSize = Math.min(Math.max(request.limit, 0), 100);
255
+ return {
256
+ data: matchingRows.slice(request.offset, request.offset + pageSize).map(toSearchRow),
257
+ pagination: {
258
+ total: matchingRows.length,
259
+ limit: request.limit,
260
+ offset: request.offset
261
+ }
262
+ };
263
+ }
264
+ function createBundleEventRow(input) {
265
+ const base = {
266
+ id: createUUIDv7(),
267
+ install_id: input.installId,
268
+ user_id: input.userId ?? null,
269
+ username: input.username ?? null,
270
+ from_release_id: input.fromReleaseId,
271
+ to_release_id: input.toReleaseId,
272
+ to_bundle_id: input.toBundleId,
273
+ platform: input.platform,
274
+ app_version: input.appVersion,
275
+ channel: input.channel,
276
+ cohort: input.cohort,
277
+ fingerprint_hash: input.fingerprintHash,
278
+ sdk_version: input.sdkVersion ?? null,
279
+ received_at_ms: Date.now()
280
+ };
281
+ switch (input.type) {
282
+ case "UPDATE_APPLIED":
283
+ case "RECOVERED":
284
+ case "RELEASE_ADOPTED": return {
285
+ ...base,
286
+ type: input.type,
287
+ from_bundle_id: input.fromBundleId,
288
+ update_strategy: input.updateStrategy
289
+ };
290
+ case "UNCHANGED": return {
291
+ ...base,
292
+ type: input.type,
293
+ from_bundle_id: null,
294
+ update_strategy: null
295
+ };
296
+ }
297
+ }
298
+ var isTransitionEventRow = (row) => row.type === "UPDATE_APPLIED" || row.type === "RECOVERED";
299
+ var toHistoryRow = (row) => ({
300
+ id: row.id,
301
+ type: row.type,
302
+ fromBundleId: row.from_bundle_id,
303
+ toBundleId: row.to_bundle_id,
304
+ username: row.username,
305
+ userId: row.user_id,
306
+ platform: row.platform,
307
+ appVersion: row.app_version,
308
+ channel: row.channel,
309
+ cohort: row.cohort,
310
+ receivedAtMs: row.received_at_ms
311
+ });
312
+ var isInstalledForBundle = (row, bundleId) => row.type === "UPDATE_APPLIED" && row.to_bundle_id === bundleId;
313
+ var isRecoveredFromBundle = (row, bundleId) => row.type === "RECOVERED" && row.from_bundle_id === bundleId;
314
+ var countDistinctInstallations = (rows) => new Set(rows.map(({ install_id }) => install_id)).size;
315
+ var getInsightsResult = async (persistence, bundleId, window, limit, offset) => {
316
+ const scope = {
317
+ persistence,
318
+ cutoffMs: Date.now()
319
+ };
320
+ const rows = (await materializeRowsForWindow(scope, window)).filter(isTransitionEventRow);
321
+ const installedRows = rows.filter((row) => isInstalledForBundle(row, bundleId));
322
+ const recoveredRows = rows.filter((row) => isRecoveredFromBundle(row, bundleId));
323
+ const installed = collectEventActivity({
324
+ rows: installedRows,
325
+ window,
326
+ cutoffMs: scope.cutoffMs
327
+ });
328
+ const recovered = collectEventActivity({
329
+ rows: recoveredRows,
330
+ window,
331
+ cutoffMs: scope.cutoffMs
332
+ });
333
+ const recentRows = [...installedRows, ...recoveredRows].sort(compareEventNewest);
334
+ return {
335
+ summary: {
336
+ installed: installed.summary,
337
+ recovered: recovered.summary
338
+ },
339
+ series: {
340
+ installed: installed.series,
341
+ recovered: recovered.series
342
+ },
343
+ cohorts: {
344
+ installed: installed.cohorts,
345
+ recovered: recovered.cohorts
346
+ },
347
+ recentEvents: {
348
+ data: recentRows.slice(offset, offset + limit).map(toHistoryRow),
349
+ pagination: {
350
+ total: recentRows.length,
351
+ limit,
352
+ offset
353
+ }
354
+ }
355
+ };
356
+ };
357
+ var getInsightsSummaries = async (persistence, bundleIds, window) => {
358
+ const normalizedBundleIds = [...new Set(bundleIds)];
359
+ if (normalizedBundleIds.length === 0) return [];
360
+ const requestedBundleIds = new Set(normalizedBundleIds);
361
+ const installedByBundleId = /* @__PURE__ */ new Map();
362
+ const recoveredByBundleId = /* @__PURE__ */ new Map();
363
+ const rows = (await materializeRowsForWindow({
364
+ persistence,
365
+ cutoffMs: Date.now()
366
+ }, window)).filter(isTransitionEventRow);
367
+ for (const row of rows) {
368
+ const bundleId = row.type === "UPDATE_APPLIED" ? row.to_bundle_id : row.from_bundle_id;
369
+ if (!requestedBundleIds.has(bundleId)) continue;
370
+ const counts = row.type === "UPDATE_APPLIED" ? installedByBundleId : recoveredByBundleId;
371
+ const installIds = counts.get(bundleId) ?? /* @__PURE__ */ new Set();
372
+ installIds.add(row.install_id);
373
+ counts.set(bundleId, installIds);
374
+ }
375
+ return normalizedBundleIds.map((bundleId) => ({
376
+ bundleId,
377
+ installed: installedByBundleId.get(bundleId)?.size ?? 0,
378
+ recovered: recoveredByBundleId.get(bundleId)?.size ?? 0
379
+ }));
380
+ };
381
+ var createInsightsProvider = (persistence) => Object.freeze({
382
+ mode: "bounded",
383
+ maxMatchingRows: INSIGHTS_SCAN_MAX_ROWS,
384
+ async appendBundleEvent(input) {
385
+ await persistence.append(createBundleEventRow(input));
386
+ },
387
+ async getBundleEventSummary(bundleId) {
388
+ const rows = (await materializeEventRows({
389
+ persistence,
390
+ cutoffMs: Date.now()
391
+ })).filter(isTransitionEventRow);
392
+ return {
393
+ installed: countDistinctInstallations(rows.filter((row) => isInstalledForBundle(row, bundleId))),
394
+ recovered: countDistinctInstallations(rows.filter((row) => isRecoveredFromBundle(row, bundleId)))
395
+ };
396
+ },
397
+ getBundleEventSummaries(bundleIds, window) {
398
+ return getInsightsSummaries(persistence, bundleIds, window);
399
+ },
400
+ getBundleEventInsights(bundleId, window, limit, offset) {
401
+ return getInsightsResult(persistence, bundleId, window, limit, offset);
402
+ },
403
+ async getBundleEventOverview() {
404
+ const rows = await materializeEventRows({
405
+ persistence,
406
+ cutoffMs: Date.now()
407
+ });
408
+ const latestByInstall = /* @__PURE__ */ new Map();
409
+ for (const row of rows) {
410
+ const current = latestByInstall.get(row.install_id);
411
+ if (current === void 0 || compareEventNewest(row, current) < 0) latestByInstall.set(row.install_id, row);
412
+ }
413
+ const counts = /* @__PURE__ */ new Map();
414
+ for (const row of latestByInstall.values()) counts.set(row.to_bundle_id, (counts.get(row.to_bundle_id) ?? 0) + 1);
415
+ return {
416
+ trackedInstallations: latestByInstall.size,
417
+ bundles: [...counts].map(([bundleId, installations]) => ({
418
+ bundleId,
419
+ installations
420
+ })).sort((left, right) => right.installations - left.installations || compareCodePoints(left.bundleId, right.bundleId))
421
+ };
422
+ },
423
+ async getActiveInstallationOverview(input) {
424
+ const asOfMs = Date.now();
425
+ return collectActiveInstallationOverview({
426
+ rows: await materializeActiveRows({
427
+ persistence,
428
+ cutoffMs: asOfMs
429
+ }, input.window),
430
+ asOfMs,
431
+ window: input.window,
432
+ ...input.userId === void 0 ? {} : { userId: input.userId }
433
+ });
434
+ },
435
+ async searchInstallations(query, limit, offset) {
436
+ return searchEventInstallations({
437
+ rows: await materializeEventRows({
438
+ persistence,
439
+ cutoffMs: Date.now()
440
+ }),
441
+ query,
442
+ limit,
443
+ offset
444
+ });
445
+ },
446
+ async getInstallationHistory(installId, limit, offset) {
447
+ const ordered = (await materializeEventRows({
448
+ persistence,
449
+ cutoffMs: Date.now()
450
+ })).filter((row) => row.install_id === installId && isTransitionEventRow(row)).toSorted(compareEventNewest);
451
+ return {
452
+ data: ordered.slice(offset, offset + limit).map(toHistoryRow),
453
+ pagination: {
454
+ total: ordered.length,
455
+ limit,
456
+ offset
457
+ }
458
+ };
459
+ }
460
+ });
461
+ function createRuntimeHotUpdater(config) {
462
+ const models = Reflect.get(config.database, "models");
463
+ const insights = typeof models === "object" && models !== null ? Reflect.get(models, "insights") : void 0;
464
+ if (typeof insights !== "object" || insights === null || typeof Reflect.get(insights, "append") !== "function" || typeof Reflect.get(insights, "scan") !== "function") return null;
465
+ return createInsightsProvider(insights);
466
+ }
467
+ function createApiKeyStore(config) {
468
+ const models = Reflect.get(config.database, "models");
469
+ const apiKeys = typeof models === "object" && models !== null ? Reflect.get(models, "apiKeys") : void 0;
470
+ if (typeof apiKeys !== "object" || apiKeys === null || typeof Reflect.get(apiKeys, "create") !== "function" || typeof Reflect.get(apiKeys, "findByHash") !== "function" || typeof Reflect.get(apiKeys, "list") !== "function" || typeof Reflect.get(apiKeys, "revoke") !== "function") return null;
471
+ return apiKeys;
472
+ }
473
+ var providerMethods = [
474
+ "appendBundleEvent",
475
+ "getBundleEventSummary",
476
+ "getBundleEventSummaries",
477
+ "getBundleEventInsights",
478
+ "getBundleEventOverview",
479
+ "getActiveInstallationOverview",
480
+ "searchInstallations",
481
+ "getInstallationHistory"
482
+ ];
483
+ var parseInsightsProvider = (value) => {
484
+ if (typeof value !== "object" || value === null || Reflect.get(value, "mode") !== "bounded" || typeof Reflect.get(value, "maxMatchingRows") !== "number" || !providerMethods.every((method) => typeof Reflect.get(value, method) === "function")) return null;
485
+ return value;
486
+ };
487
+ var getInsightsCapability = async (provider) => {
488
+ const parsed = parseInsightsProvider(provider);
489
+ return parsed === null ? {
490
+ insights: false,
491
+ insightsQueries: false,
492
+ eventIngestion: false
493
+ } : {
494
+ insights: true,
495
+ insightsQueries: true,
496
+ eventIngestion: true,
497
+ maxMatchingRows: parsed.maxMatchingRows,
498
+ mode: "bounded"
499
+ };
500
+ };
501
+ var requireInsightsSupport = async (provider) => {
502
+ const parsedProvider = parseInsightsProvider(provider);
503
+ if (parsedProvider === null) throw new Error("Insights are not supported by the configured database plugin.");
504
+ return parsedProvider;
505
+ };
506
+ async function getBundleEventSummary(provider, input) {
507
+ const { bundleId } = parseBundleEventSummaryInput(input);
508
+ return (await requireInsightsSupport(provider)).getBundleEventSummary(bundleId);
509
+ }
510
+ async function getActiveInstallationOverview(provider, input) {
511
+ const parsed = parseActiveInstallationInput(input);
512
+ return (await requireInsightsSupport(provider)).getActiveInstallationOverview(parsed);
513
+ }
514
+ async function getBundleEventInsights(provider, input) {
515
+ const parsed = parseBundleEventInsightsInput(input);
516
+ return (await requireInsightsSupport(provider)).getBundleEventInsights(parsed.bundleId, parsed.window, parsed.limit ?? 50, parsed.offset ?? 0);
517
+ }
518
+ async function searchInstallations(provider, input) {
519
+ const parsed = parseSearchInstallationsInput(input);
520
+ return (await requireInsightsSupport(provider)).searchInstallations(parsed.query, parsed.limit ?? 50, parsed.offset ?? 0);
521
+ }
522
+ async function getInstallationHistory(provider, input) {
523
+ const parsed = parseInstallationHistoryInput(input);
524
+ return (await requireInsightsSupport(provider)).getInstallationHistory(parsed.installId, parsed.limit ?? 50, parsed.offset ?? 0);
525
+ }
526
+ //#endregion
527
+ export { createApiKeyStore, createRuntimeHotUpdater, getActiveInstallationOverview, getBundleEventInsights, getBundleEventSummary, getInsightsCapability, getInstallationHistory, searchInstallations };