@hot-updater/server 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 (291) hide show
  1. package/dist/adapters/databaseConstraintErrors.cjs +23 -0
  2. package/dist/adapters/databaseConstraintErrors.mjs +21 -0
  3. package/dist/adapters/databasePluginUtils.cjs +117 -0
  4. package/dist/adapters/databasePluginUtils.mjs +106 -0
  5. package/dist/adapters/drizzle.cjs +53 -176
  6. package/dist/adapters/drizzle.d.cts +6 -4
  7. package/dist/adapters/drizzle.d.mts +6 -4
  8. package/dist/adapters/drizzle.mjs +54 -177
  9. package/dist/adapters/drizzleCrud.cjs +250 -0
  10. package/dist/adapters/drizzleCrud.mjs +249 -0
  11. package/dist/adapters/drizzleLazyDB.cjs +84 -36
  12. package/dist/adapters/drizzleLazyDB.mjs +84 -36
  13. package/dist/adapters/drizzleQuery.cjs +68 -0
  14. package/dist/adapters/drizzleQuery.mjs +67 -0
  15. package/dist/adapters/kysely.cjs +18 -138
  16. package/dist/adapters/kysely.d.cts +3 -12
  17. package/dist/adapters/kysely.d.mts +3 -12
  18. package/dist/adapters/kysely.mjs +19 -139
  19. package/dist/adapters/kyselyCrud.cjs +254 -0
  20. package/dist/adapters/kyselyCrud.mjs +253 -0
  21. package/dist/adapters/kyselyQuery.cjs +56 -0
  22. package/dist/adapters/kyselyQuery.mjs +55 -0
  23. package/dist/adapters/mongodb.cjs +21 -174
  24. package/dist/adapters/mongodb.d.cts +3 -2
  25. package/dist/adapters/mongodb.d.mts +3 -2
  26. package/dist/adapters/mongodb.mjs +21 -174
  27. package/dist/adapters/mongodbCollections.cjs +36 -0
  28. package/dist/adapters/mongodbCollections.mjs +30 -0
  29. package/dist/adapters/mongodbQuery.cjs +77 -0
  30. package/dist/adapters/mongodbQuery.mjs +70 -0
  31. package/dist/adapters/mongodbReads.cjs +148 -0
  32. package/dist/adapters/mongodbReads.mjs +147 -0
  33. package/dist/adapters/mongodbWrites.cjs +134 -0
  34. package/dist/adapters/mongodbWrites.mjs +133 -0
  35. package/dist/adapters/prisma.cjs +205 -180
  36. package/dist/adapters/prisma.d.cts +3 -3
  37. package/dist/adapters/prisma.d.mts +3 -3
  38. package/dist/adapters/prisma.mjs +206 -181
  39. package/dist/adapters/prismaQuery.cjs +58 -0
  40. package/dist/adapters/prismaQuery.mjs +56 -0
  41. package/dist/adapters/prismaRows.cjs +198 -0
  42. package/dist/adapters/prismaRows.mjs +188 -0
  43. package/dist/apiKeys.cjs +139 -0
  44. package/dist/apiKeys.d.cts +50 -0
  45. package/dist/apiKeys.d.mts +50 -0
  46. package/dist/apiKeys.mjs +133 -0
  47. package/dist/createHotUpdaterCore.cjs +48 -26
  48. package/dist/createHotUpdaterCore.d.cts +49 -17
  49. package/dist/createHotUpdaterCore.d.mts +49 -17
  50. package/dist/createHotUpdaterCore.mjs +51 -29
  51. package/dist/db/createBundleDiff.cjs +45 -68
  52. package/dist/db/createBundleDiff.d.cts +3 -3
  53. package/dist/db/createBundleDiff.d.mts +3 -3
  54. package/dist/db/createBundleDiff.mjs +47 -68
  55. package/dist/db/databasePluginCore.cjs +138 -0
  56. package/dist/db/databasePluginCore.mjs +137 -0
  57. package/dist/db/fixedMigrator.cjs +36 -93
  58. package/dist/db/fixedMigrator.mjs +36 -93
  59. package/dist/db/fixedMigratorMongo.cjs +319 -0
  60. package/dist/db/fixedMigratorMongo.mjs +319 -0
  61. package/dist/db/fixedMigratorShared.cjs +23 -0
  62. package/dist/db/fixedMigratorShared.mjs +18 -0
  63. package/dist/db/mongoMigrationExecution.cjs +11 -0
  64. package/dist/db/mongoMigrationExecution.mjs +11 -0
  65. package/dist/db/releaseCatalog.cjs +66 -0
  66. package/dist/db/releaseCatalog.d.cts +17 -0
  67. package/dist/db/releaseCatalog.d.mts +17 -0
  68. package/dist/db/releaseCatalog.mjs +64 -0
  69. package/dist/db/schema/mongodb.cjs +14 -6
  70. package/dist/db/schema/mongodb.mjs +14 -6
  71. package/dist/db/schema/registry.cjs +0 -2
  72. package/dist/db/schema/registry.mjs +1 -2
  73. package/dist/db/schema/sql.cjs +31 -14
  74. package/dist/db/schema/sql.mjs +32 -13
  75. package/dist/db/schema/sqlOperations.cjs +4 -4
  76. package/dist/db/schema/sqlOperations.mjs +5 -5
  77. package/dist/db/schemaGenerators.cjs +52 -18
  78. package/dist/db/schemaGenerators.mjs +52 -18
  79. package/dist/db/schemaReadiness.cjs +4 -3
  80. package/dist/db/schemaReadiness.mjs +4 -3
  81. package/dist/db/sqlMigrationExecution.cjs +67 -0
  82. package/dist/db/sqlMigrationExecution.mjs +66 -0
  83. package/dist/db/types.cjs +2 -6
  84. package/dist/db/types.d.cts +60 -16
  85. package/dist/db/types.d.mts +60 -16
  86. package/dist/db/types.mjs +3 -6
  87. package/dist/db/updateArtifacts.cjs +114 -60
  88. package/dist/db/updateArtifacts.mjs +117 -62
  89. package/dist/handler.cjs +108 -282
  90. package/dist/handler.d.cts +3 -46
  91. package/dist/handler.d.mts +3 -46
  92. package/dist/handler.mjs +107 -281
  93. package/dist/handlerBundleRoutes.cjs +155 -0
  94. package/dist/handlerBundleRoutes.mjs +155 -0
  95. package/dist/handlerErrors.cjs +9 -0
  96. package/dist/handlerErrors.mjs +9 -0
  97. package/dist/handlerParameters.cjs +39 -0
  98. package/dist/handlerParameters.mjs +34 -0
  99. package/dist/handlerReleaseCatalogRoutes.cjs +111 -0
  100. package/dist/handlerReleaseCatalogRoutes.mjs +110 -0
  101. package/dist/handlerReleaseManagementRoutes.cjs +125 -0
  102. package/dist/handlerReleaseManagementRoutes.mjs +124 -0
  103. package/dist/handlerTypes.d.cts +63 -0
  104. package/dist/handlerTypes.d.mts +63 -0
  105. package/dist/handlerVersionRoutes.cjs +10 -0
  106. package/dist/handlerVersionRoutes.d.cts +4 -0
  107. package/dist/handlerVersionRoutes.d.mts +4 -0
  108. package/dist/handlerVersionRoutes.mjs +9 -0
  109. package/dist/index.cjs +10 -1
  110. package/dist/index.d.cts +9 -3
  111. package/dist/index.d.mts +9 -3
  112. package/dist/index.mjs +5 -2
  113. package/dist/insights/bounded/activeOverview.cjs +87 -0
  114. package/dist/insights/bounded/activeOverview.mjs +85 -0
  115. package/dist/insights/bounded/installationSearch.cjs +42 -0
  116. package/dist/insights/bounded/installationSearch.mjs +42 -0
  117. package/dist/insights/bounded/persistence.cjs +39 -0
  118. package/dist/insights/bounded/persistence.mjs +38 -0
  119. package/dist/insights/bounded/provider.cjs +170 -0
  120. package/dist/insights/bounded/provider.d.cts +7 -0
  121. package/dist/insights/bounded/provider.d.mts +7 -0
  122. package/dist/insights/bounded/provider.mjs +170 -0
  123. package/dist/insights/bounded/scan.cjs +144 -0
  124. package/dist/insights/bounded/scan.mjs +138 -0
  125. package/dist/insights/domain.d.cts +121 -0
  126. package/dist/insights/domain.d.mts +121 -0
  127. package/dist/insights/errors.cjs +22 -0
  128. package/dist/insights/errors.mjs +20 -0
  129. package/dist/insights/eventInput.cjs +111 -0
  130. package/dist/insights/eventInput.mjs +111 -0
  131. package/dist/insights/persistence.d.cts +6 -0
  132. package/dist/insights/persistence.d.mts +6 -0
  133. package/dist/insights/queryInput.cjs +58 -0
  134. package/dist/insights/queryInput.mjs +55 -0
  135. package/dist/insights/routes.cjs +63 -0
  136. package/dist/insights/routes.mjs +61 -0
  137. package/dist/insights/types.d.cts +17 -0
  138. package/dist/insights/types.d.mts +17 -0
  139. package/dist/node.cjs +19 -12
  140. package/dist/node.d.cts +15 -10
  141. package/dist/node.d.mts +15 -10
  142. package/dist/node.mjs +19 -13
  143. package/dist/package.cjs +1 -1
  144. package/dist/package.mjs +1 -1
  145. package/dist/schema/dsl.cjs +19 -2
  146. package/dist/schema/dsl.mjs +17 -3
  147. package/dist/schema/index.cjs +1 -8
  148. package/dist/schema/index.mjs +2 -8
  149. package/dist/schema/types.cjs +3 -1
  150. package/dist/schema/types.mjs +3 -2
  151. package/dist/schema/v1_0_0.cjs +364 -0
  152. package/dist/schema/v1_0_0.mjs +357 -0
  153. package/dist/storageAccess.cjs +69 -25
  154. package/dist/storageAccess.mjs +68 -25
  155. package/dist/types/index.d.cts +4 -3
  156. package/dist/types/index.d.mts +5 -4
  157. package/package.json +8 -7
  158. package/src/adapters/databaseConstraintErrors.ts +25 -0
  159. package/src/adapters/databasePluginTestDatabase.ts +106 -0
  160. package/src/adapters/databasePluginUtils.spec.ts +29 -0
  161. package/src/adapters/databasePluginUtils.ts +214 -0
  162. package/src/adapters/drizzle.spec.ts +208 -47
  163. package/src/adapters/drizzle.ts +106 -293
  164. package/src/adapters/drizzleCrud.ts +419 -0
  165. package/src/adapters/drizzleLazyDB.ts +206 -100
  166. package/src/adapters/drizzleQuery.ts +164 -0
  167. package/src/adapters/kysely.spec.ts +126 -88
  168. package/src/adapters/kysely.ts +76 -334
  169. package/src/adapters/kyselyCrud.ts +588 -0
  170. package/src/adapters/kyselyQuery.ts +128 -0
  171. package/src/adapters/mongodb.spec.ts +141 -0
  172. package/src/adapters/mongodb.ts +60 -288
  173. package/src/adapters/mongodbCollections.ts +62 -0
  174. package/src/adapters/mongodbQuery.ts +226 -0
  175. package/src/adapters/mongodbReads.ts +311 -0
  176. package/src/adapters/mongodbRows.spec.ts +64 -0
  177. package/src/adapters/mongodbRows.ts +118 -0
  178. package/src/adapters/mongodbTestClient.ts +342 -0
  179. package/src/adapters/mongodbTestFilter.ts +166 -0
  180. package/src/adapters/mongodbWrites.ts +260 -0
  181. package/src/adapters/prisma.spec.ts +196 -0
  182. package/src/adapters/prisma.ts +417 -292
  183. package/src/adapters/prismaQuery.ts +129 -0
  184. package/src/adapters/prismaRows.spec.ts +98 -0
  185. package/src/adapters/prismaRows.ts +301 -0
  186. package/src/adapters/prismaTestClient.ts +396 -0
  187. package/src/apiKeys.integration.spec.ts +192 -0
  188. package/src/apiKeys.spec.ts +162 -0
  189. package/src/apiKeys.ts +220 -0
  190. package/src/createHotUpdaterCore.ts +164 -100
  191. package/src/db/bundleRows.spec.ts +164 -0
  192. package/src/db/bundleRows.ts +10 -140
  193. package/src/db/createBundleDiff.spec.ts +172 -127
  194. package/src/db/createBundleDiff.ts +91 -110
  195. package/src/db/databasePluginCore.spec.ts +62 -0
  196. package/src/db/databasePluginCore.testFixtures.ts +76 -0
  197. package/src/db/databasePluginCore.ts +185 -0
  198. package/src/db/databasePluginUpdateCheck.bench.ts +107 -0
  199. package/src/db/fixedMigrator.spec.ts +184 -42
  200. package/src/db/fixedMigrator.ts +64 -205
  201. package/src/db/fixedMigratorMongo.spec.ts +192 -0
  202. package/src/db/fixedMigratorMongo.ts +425 -0
  203. package/src/db/fixedMigratorShared.ts +36 -0
  204. package/src/db/hotUpdaterSchema.ts +0 -1
  205. package/src/db/index.spec.ts +333 -1025
  206. package/src/db/mongoMigrationExecution.ts +23 -0
  207. package/src/db/node-sqlite.d.ts +25 -0
  208. package/src/db/releaseCatalog.ts +148 -0
  209. package/src/db/schema/mongodb.ts +20 -6
  210. package/src/db/schema/sql.ts +52 -11
  211. package/src/db/schema/sqlOperations.ts +5 -4
  212. package/src/db/schemaGenerators.ts +107 -23
  213. package/src/db/schemaReadiness.ts +2 -1
  214. package/src/db/sqlMigrationExecution.spec.ts +65 -0
  215. package/src/db/sqlMigrationExecution.ts +108 -0
  216. package/src/db/types.spec.ts +55 -0
  217. package/src/db/types.ts +129 -65
  218. package/src/db/updateArtifacts.spec.ts +359 -0
  219. package/src/db/updateArtifacts.ts +290 -126
  220. package/src/handler-standalone.integration.spec.ts +401 -364
  221. package/src/handler.spec.ts +137 -582
  222. package/src/handler.testFixtures.ts +30 -0
  223. package/src/handler.ts +218 -616
  224. package/src/handlerAuth.integration.spec.ts +33 -0
  225. package/src/handlerBundleRoutes.ts +259 -0
  226. package/src/handlerErrors.ts +7 -0
  227. package/src/handlerManagement.spec.ts +262 -0
  228. package/src/handlerParameters.ts +105 -0
  229. package/src/handlerReleaseCatalogRoutes.ts +154 -0
  230. package/src/handlerReleaseManagementRoutes.ts +213 -0
  231. package/src/handlerTypes.ts +96 -0
  232. package/src/handlerVersionRoutes.ts +12 -0
  233. package/src/index.ts +22 -2
  234. package/src/insights/bounded/activeOverview.spec.ts +189 -0
  235. package/src/insights/bounded/activeOverview.ts +148 -0
  236. package/src/insights/bounded/installationSearch.spec.ts +130 -0
  237. package/src/insights/bounded/installationSearch.ts +69 -0
  238. package/src/insights/bounded/persistence.ts +43 -0
  239. package/src/insights/bounded/provider.spec.ts +219 -0
  240. package/src/insights/bounded/provider.ts +229 -0
  241. package/src/insights/bounded/scan.ts +235 -0
  242. package/src/insights/domain.ts +139 -0
  243. package/src/insights/errors.ts +18 -0
  244. package/src/insights/eventInput.ts +168 -0
  245. package/src/insights/insights.integration.spec.ts +144 -0
  246. package/src/insights/persistence.ts +10 -0
  247. package/src/insights/queryInput.ts +102 -0
  248. package/src/insights/routes.ts +123 -0
  249. package/src/insights/types.ts +44 -0
  250. package/src/node.spec.ts +79 -10
  251. package/src/node.ts +20 -17
  252. package/src/releaseCatalogRoutes.integration.spec.ts +238 -0
  253. package/src/runtime.spec.ts +126 -959
  254. package/src/runtime.testFixtures.ts +71 -0
  255. package/src/schema/dsl-all-versions.spec.ts +26 -17
  256. package/src/schema/dsl.ts +41 -2
  257. package/src/schema/index.ts +3 -9
  258. package/src/schema/types.ts +9 -3
  259. package/src/schema/v1_0_0.spec.ts +146 -0
  260. package/src/schema/v1_0_0.ts +465 -0
  261. package/src/storageAccess.spec.ts +127 -32
  262. package/src/storageAccess.ts +106 -55
  263. package/src/types/index.ts +2 -1
  264. package/dist/db/bundleRows.cjs +0 -82
  265. package/dist/db/bundleRows.d.cts +0 -33
  266. package/dist/db/bundleRows.d.mts +0 -33
  267. package/dist/db/bundleRows.mjs +0 -79
  268. package/dist/db/pluginCore.cjs +0 -242
  269. package/dist/db/pluginCore.mjs +0 -241
  270. package/dist/db/schema/sqlMigrations.cjs +0 -74
  271. package/dist/db/schema/sqlMigrations.mjs +0 -73
  272. package/dist/db/schemaEnhancements.cjs +0 -20
  273. package/dist/db/schemaEnhancements.mjs +0 -19
  274. package/dist/route.cjs +0 -7
  275. package/dist/route.mjs +0 -7
  276. package/dist/schema/v0_21_0.cjs +0 -38
  277. package/dist/schema/v0_21_0.mjs +0 -37
  278. package/dist/schema/v0_29_0.cjs +0 -45
  279. package/dist/schema/v0_29_0.mjs +0 -44
  280. package/dist/schema/v0_31_0.cjs +0 -82
  281. package/dist/schema/v0_31_0.mjs +0 -80
  282. package/src/db/pluginCore.spec.ts +0 -1095
  283. package/src/db/pluginCore.ts +0 -475
  284. package/src/db/pluginUpdateCheck.bench.ts +0 -254
  285. package/src/db/schema/sqlMigrations.ts +0 -219
  286. package/src/db/schemaEnhancements.ts +0 -55
  287. package/src/route.ts +0 -7
  288. package/src/schema/v0_21_0.ts +0 -52
  289. package/src/schema/v0_29_0.ts +0 -62
  290. package/src/schema/v0_31_0.spec.ts +0 -73
  291. package/src/schema/v0_31_0.ts +0 -120
@@ -0,0 +1,124 @@
1
+ import { HandlerBadRequestError } from "./handlerErrors.mjs";
2
+ import { decodeMaybe, requireRouteParam } from "./handlerParameters.mjs";
3
+ import { ReleaseCatalogMutationError, ReleaseManagementError } from "@hot-updater/plugin-core";
4
+ //#region src/handlerReleaseManagementRoutes.ts
5
+ const unavailable = () => Response.json({ error: "Not found" }, { status: 404 });
6
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
7
+ const parseRevision = (value) => {
8
+ if (value === void 0 || value === null || value === "") return void 0;
9
+ const revision = typeof value === "number" ? value : Number(value);
10
+ if (!Number.isSafeInteger(revision) || revision < 1) throw new HandlerBadRequestError("Invalid expected Release revision");
11
+ return revision;
12
+ };
13
+ const parsePolicyInput = async (request) => {
14
+ const body = await request.json();
15
+ if (!isRecord(body) || !isRecord(body.patch)) throw new HandlerBadRequestError("Invalid Release policy mutation");
16
+ return {
17
+ expectedRevision: parseRevision(body.expectedRevision),
18
+ patch: body.patch
19
+ };
20
+ };
21
+ const mutationResponse = async (operation) => {
22
+ try {
23
+ return Response.json({ data: await operation() });
24
+ } catch (error) {
25
+ if (error instanceof ReleaseManagementError || error instanceof ReleaseCatalogMutationError) {
26
+ const status = error.code === "RELEASE_NOT_FOUND" ? 404 : error.code === "VERSION_CONFLICT" ? 409 : 400;
27
+ return Response.json({
28
+ code: error.code,
29
+ error: error.message
30
+ }, { status });
31
+ }
32
+ throw error;
33
+ }
34
+ };
35
+ const createReleaseManagementRouteHandlers = () => ({
36
+ getRelease: async (params, _request, api) => {
37
+ if (api.getReleaseById === void 0) return unavailable();
38
+ const row = await api.getReleaseById(requireRouteParam(params, "id"));
39
+ return row === null ? unavailable() : Response.json({ data: row }, { status: 200 });
40
+ },
41
+ getReleases: async (_params, request, api) => {
42
+ const url = new URL(request.url);
43
+ const scopeKey = url.searchParams.get("scopeKey");
44
+ const limit = Number(url.searchParams.get("limit") ?? "100");
45
+ if (!Number.isSafeInteger(limit) || limit <= 0 || limit > 1e3) throw new HandlerBadRequestError("Invalid Release scope query");
46
+ if (scopeKey !== null) {
47
+ if (scopeKey.length === 0 || api.getReleasesByScope === void 0) throw new HandlerBadRequestError("Invalid Release scope query");
48
+ const afterReleaseId = url.searchParams.get("afterReleaseId") ?? void 0;
49
+ return Response.json({ data: await api.getReleasesByScope({
50
+ ...afterReleaseId === void 0 ? {} : { afterReleaseId },
51
+ limit,
52
+ scopeKey
53
+ }) });
54
+ }
55
+ if (api.getReleases === void 0) return unavailable();
56
+ const enabledValue = url.searchParams.get("enabled");
57
+ const enabled = enabledValue === null ? void 0 : enabledValue === "true" ? true : enabledValue === "false" ? false : null;
58
+ const platformValue = url.searchParams.get("platform");
59
+ const platform = platformValue === null ? void 0 : platformValue === "ios" || platformValue === "android" ? platformValue : null;
60
+ if (enabled === null || platform === null) throw new HandlerBadRequestError("Invalid Release list filter");
61
+ return Response.json({ data: await api.getReleases({
62
+ afterReleaseId: url.searchParams.get("afterReleaseId") ?? void 0,
63
+ beforeReleaseId: url.searchParams.get("beforeReleaseId") ?? void 0,
64
+ bundleId: url.searchParams.get("bundleId") ?? void 0,
65
+ channelId: url.searchParams.get("channelId") ?? void 0,
66
+ enabled,
67
+ limit,
68
+ platform,
69
+ targetAppVersion: url.searchParams.get("targetAppVersion") ?? void 0
70
+ }) });
71
+ },
72
+ getReleaseCatalogRow: async (params, _request, api) => {
73
+ if (api.getReleaseCatalogByScopeKey === void 0) return unavailable();
74
+ const row = await api.getReleaseCatalogByScopeKey(decodeMaybe(requireRouteParam(params, "scopeKey")) ?? "");
75
+ return row === null ? unavailable() : Response.json({ data: row }, { status: 200 });
76
+ },
77
+ getReleaseCatalogs: async (_params, request, api) => {
78
+ if (api.getReleaseCatalogs === void 0) return unavailable();
79
+ const url = new URL(request.url);
80
+ const limit = Number(url.searchParams.get("limit") ?? "100");
81
+ if (!Number.isSafeInteger(limit) || limit <= 0 || limit > 1e3) throw new HandlerBadRequestError("Invalid Release catalog list query");
82
+ return Response.json({ data: await api.getReleaseCatalogs({
83
+ afterScopeKey: url.searchParams.get("afterScopeKey") ?? void 0,
84
+ limit
85
+ }) });
86
+ },
87
+ updateRelease: async (params, request, api) => {
88
+ if (api.updateReleasePolicy === void 0) return unavailable();
89
+ const input = await parsePolicyInput(request);
90
+ return mutationResponse(() => api.updateReleasePolicy({
91
+ ...input,
92
+ releaseId: requireRouteParam(params, "id")
93
+ }));
94
+ },
95
+ preflightRelease: async (params, request, api) => {
96
+ if (api.preflightReleasePolicy === void 0) return unavailable();
97
+ const input = await parsePolicyInput(request);
98
+ return mutationResponse(() => api.preflightReleasePolicy({
99
+ ...input,
100
+ releaseId: requireRouteParam(params, "id")
101
+ }));
102
+ },
103
+ deleteRelease: async (params, request, api) => {
104
+ if (api.deleteRelease === void 0) return unavailable();
105
+ const releaseId = requireRouteParam(params, "id");
106
+ const url = new URL(request.url);
107
+ if (url.searchParams.get("confirm") !== releaseId) throw new HandlerBadRequestError("Release hard deletion requires confirm=<release-id>");
108
+ return mutationResponse(() => api.deleteRelease({
109
+ expectedRevision: parseRevision(url.searchParams.get("expectedRevision")),
110
+ releaseId
111
+ }));
112
+ },
113
+ rebuildReleaseCatalog: async (params, _request, api) => {
114
+ if (api.rebuildReleaseCatalog === void 0) return unavailable();
115
+ return mutationResponse(() => api.rebuildReleaseCatalog(decodeMaybe(requireRouteParam(params, "scopeKey")) ?? ""));
116
+ },
117
+ commitDatabase: async (_params, request, api) => {
118
+ if (api.commitDatabase === void 0) return unavailable();
119
+ const input = await request.json();
120
+ return Response.json({ data: await api.commitDatabase(input) });
121
+ }
122
+ });
123
+ //#endregion
124
+ export { createReleaseManagementRouteHandlers };
@@ -0,0 +1,63 @@
1
+ import { ReleaseCatalogRequest } from "./db/releaseCatalog.cjs";
2
+ import { PaginatedResult } from "./types/index.cjs";
3
+ import { ArtifactInfo, Bundle, ReleaseCatalog } from "@hot-updater/core";
4
+ import { ChannelDeleteInput, ChannelDeleteResult, ChannelInsertInput, ChannelInsertResult, ChannelRow, DatabaseBundleQueryOptions, DatabaseCommit, DatabaseCommitResult, ReleaseCatalogMutationPreflight, ReleaseCatalogMutationResult, ReleaseCatalogRebuildResult, ReleaseCatalogRow, ReleasePolicyPatch, ReleaseRow } from "@hot-updater/plugin-core";
5
+
6
+ //#region src/handlerTypes.d.ts
7
+ interface HandlerAPI {
8
+ getReleaseCatalog?: (input: ReleaseCatalogRequest) => Promise<ReleaseCatalog | null>;
9
+ getArtifactInfo?: (targetBundleId: string, currentBundleId: string) => Promise<ArtifactInfo | null>;
10
+ getReleaseById?: (id: string) => Promise<ReleaseRow | null>;
11
+ getReleasesByScope?: (input: {
12
+ readonly scopeKey: string;
13
+ readonly afterReleaseId?: string;
14
+ readonly limit: number;
15
+ }) => Promise<readonly ReleaseRow[]>;
16
+ getReleases?: (input: {
17
+ readonly afterReleaseId?: string;
18
+ readonly beforeReleaseId?: string;
19
+ readonly bundleId?: string;
20
+ readonly channelId?: string;
21
+ readonly enabled?: boolean;
22
+ readonly platform?: "ios" | "android";
23
+ readonly targetAppVersion?: string;
24
+ readonly limit: number;
25
+ }) => Promise<readonly ReleaseRow[]>;
26
+ getReleaseCatalogByScopeKey?: (scopeKey: string) => Promise<ReleaseCatalogRow | null>;
27
+ getReleaseCatalogs?: (input: {
28
+ readonly afterScopeKey?: string;
29
+ readonly limit: number;
30
+ }) => Promise<readonly ReleaseCatalogRow[]>;
31
+ updateReleasePolicy?: (input: {
32
+ readonly expectedRevision?: number;
33
+ readonly patch: ReleasePolicyPatch;
34
+ readonly releaseId: string;
35
+ }) => Promise<ReleaseCatalogMutationResult>;
36
+ preflightReleasePolicy?: (input: {
37
+ readonly expectedRevision?: number;
38
+ readonly patch: ReleasePolicyPatch;
39
+ readonly releaseId: string;
40
+ }) => Promise<ReleaseCatalogMutationPreflight>;
41
+ deleteRelease?: (input: {
42
+ readonly expectedRevision?: number;
43
+ readonly releaseId: string;
44
+ }) => Promise<ReleaseCatalogMutationResult>;
45
+ rebuildReleaseCatalog?: (scopeKey: string) => Promise<ReleaseCatalogRebuildResult>;
46
+ commitDatabase?: (input: DatabaseCommit) => Promise<DatabaseCommitResult>;
47
+ getBundleById: (id: string) => Promise<Bundle | null>;
48
+ getBundles: (options: DatabaseBundleQueryOptions) => Promise<PaginatedResult>;
49
+ insertBundle: (bundle: Bundle) => Promise<void>;
50
+ insertBundles?: (bundles: readonly Bundle[]) => Promise<void>;
51
+ updateBundleById: (bundleId: string, bundle: Partial<Bundle>) => Promise<void>;
52
+ deleteBundleById: (bundleId: string) => Promise<void>;
53
+ getChannels: () => Promise<readonly ChannelRow[]>;
54
+ insertChannel: (input: ChannelInsertInput) => Promise<ChannelInsertResult>;
55
+ deleteChannel: (input: ChannelDeleteInput) => Promise<ChannelDeleteResult>;
56
+ }
57
+ type HotUpdaterHandler = (request: Request) => Promise<Response>;
58
+ interface HotUpdaterHandlers {
59
+ readonly client: HotUpdaterHandler;
60
+ readonly admin: HotUpdaterHandler;
61
+ }
62
+ //#endregion
63
+ export { HandlerAPI, HotUpdaterHandler, HotUpdaterHandlers };
@@ -0,0 +1,63 @@
1
+ import { ReleaseCatalogRequest } from "./db/releaseCatalog.mjs";
2
+ import { PaginatedResult } from "./types/index.mjs";
3
+ import { ChannelDeleteInput, ChannelDeleteResult, ChannelInsertInput, ChannelInsertResult, ChannelRow, DatabaseBundleQueryOptions, DatabaseCommit, DatabaseCommitResult, ReleaseCatalogMutationPreflight, ReleaseCatalogMutationResult, ReleaseCatalogRebuildResult, ReleaseCatalogRow, ReleasePolicyPatch, ReleaseRow } from "@hot-updater/plugin-core";
4
+ import { ArtifactInfo, Bundle as Bundle$1, ReleaseCatalog } from "@hot-updater/core";
5
+
6
+ //#region src/handlerTypes.d.ts
7
+ interface HandlerAPI {
8
+ getReleaseCatalog?: (input: ReleaseCatalogRequest) => Promise<ReleaseCatalog | null>;
9
+ getArtifactInfo?: (targetBundleId: string, currentBundleId: string) => Promise<ArtifactInfo | null>;
10
+ getReleaseById?: (id: string) => Promise<ReleaseRow | null>;
11
+ getReleasesByScope?: (input: {
12
+ readonly scopeKey: string;
13
+ readonly afterReleaseId?: string;
14
+ readonly limit: number;
15
+ }) => Promise<readonly ReleaseRow[]>;
16
+ getReleases?: (input: {
17
+ readonly afterReleaseId?: string;
18
+ readonly beforeReleaseId?: string;
19
+ readonly bundleId?: string;
20
+ readonly channelId?: string;
21
+ readonly enabled?: boolean;
22
+ readonly platform?: "ios" | "android";
23
+ readonly targetAppVersion?: string;
24
+ readonly limit: number;
25
+ }) => Promise<readonly ReleaseRow[]>;
26
+ getReleaseCatalogByScopeKey?: (scopeKey: string) => Promise<ReleaseCatalogRow | null>;
27
+ getReleaseCatalogs?: (input: {
28
+ readonly afterScopeKey?: string;
29
+ readonly limit: number;
30
+ }) => Promise<readonly ReleaseCatalogRow[]>;
31
+ updateReleasePolicy?: (input: {
32
+ readonly expectedRevision?: number;
33
+ readonly patch: ReleasePolicyPatch;
34
+ readonly releaseId: string;
35
+ }) => Promise<ReleaseCatalogMutationResult>;
36
+ preflightReleasePolicy?: (input: {
37
+ readonly expectedRevision?: number;
38
+ readonly patch: ReleasePolicyPatch;
39
+ readonly releaseId: string;
40
+ }) => Promise<ReleaseCatalogMutationPreflight>;
41
+ deleteRelease?: (input: {
42
+ readonly expectedRevision?: number;
43
+ readonly releaseId: string;
44
+ }) => Promise<ReleaseCatalogMutationResult>;
45
+ rebuildReleaseCatalog?: (scopeKey: string) => Promise<ReleaseCatalogRebuildResult>;
46
+ commitDatabase?: (input: DatabaseCommit) => Promise<DatabaseCommitResult>;
47
+ getBundleById: (id: string) => Promise<Bundle$1 | null>;
48
+ getBundles: (options: DatabaseBundleQueryOptions) => Promise<PaginatedResult>;
49
+ insertBundle: (bundle: Bundle$1) => Promise<void>;
50
+ insertBundles?: (bundles: readonly Bundle$1[]) => Promise<void>;
51
+ updateBundleById: (bundleId: string, bundle: Partial<Bundle$1>) => Promise<void>;
52
+ deleteBundleById: (bundleId: string) => Promise<void>;
53
+ getChannels: () => Promise<readonly ChannelRow[]>;
54
+ insertChannel: (input: ChannelInsertInput) => Promise<ChannelInsertResult>;
55
+ deleteChannel: (input: ChannelDeleteInput) => Promise<ChannelDeleteResult>;
56
+ }
57
+ type HotUpdaterHandler = (request: Request) => Promise<Response>;
58
+ interface HotUpdaterHandlers {
59
+ readonly client: HotUpdaterHandler;
60
+ readonly admin: HotUpdaterHandler;
61
+ }
62
+ //#endregion
63
+ export { HandlerAPI, HotUpdaterHandler, HotUpdaterHandlers };
@@ -0,0 +1,10 @@
1
+ const require_version = require("./version.cjs");
2
+ //#region src/handlerVersionRoutes.ts
3
+ const HOT_UPDATER_INFRASTRUCTURE_GENERATION = 1;
4
+ const createVersionRouteHandlers = () => ({ version: async () => Response.json({
5
+ infrastructureGeneration: 1,
6
+ version: require_version.HOT_UPDATER_SERVER_VERSION
7
+ }) });
8
+ //#endregion
9
+ exports.HOT_UPDATER_INFRASTRUCTURE_GENERATION = HOT_UPDATER_INFRASTRUCTURE_GENERATION;
10
+ exports.createVersionRouteHandlers = createVersionRouteHandlers;
@@ -0,0 +1,4 @@
1
+ //#region src/handlerVersionRoutes.d.ts
2
+ declare const HOT_UPDATER_INFRASTRUCTURE_GENERATION = 1;
3
+ //#endregion
4
+ export { HOT_UPDATER_INFRASTRUCTURE_GENERATION };
@@ -0,0 +1,4 @@
1
+ //#region src/handlerVersionRoutes.d.ts
2
+ declare const HOT_UPDATER_INFRASTRUCTURE_GENERATION = 1;
3
+ //#endregion
4
+ export { HOT_UPDATER_INFRASTRUCTURE_GENERATION };
@@ -0,0 +1,9 @@
1
+ import { HOT_UPDATER_SERVER_VERSION } from "./version.mjs";
2
+ //#region src/handlerVersionRoutes.ts
3
+ const HOT_UPDATER_INFRASTRUCTURE_GENERATION = 1;
4
+ const createVersionRouteHandlers = () => ({ version: async () => Response.json({
5
+ infrastructureGeneration: 1,
6
+ version: HOT_UPDATER_SERVER_VERSION
7
+ }) });
8
+ //#endregion
9
+ export { HOT_UPDATER_INFRASTRUCTURE_GENERATION, createVersionRouteHandlers };
package/dist/index.cjs CHANGED
@@ -1,7 +1,16 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_version = require("./version.cjs");
3
+ const require_handlerVersionRoutes = require("./handlerVersionRoutes.cjs");
3
4
  const require_handler = require("./handler.cjs");
5
+ const require_provider = require("./insights/bounded/provider.cjs");
6
+ const require_apiKeys = require("./apiKeys.cjs");
4
7
  const require_createHotUpdaterCore = require("./createHotUpdaterCore.cjs");
8
+ exports.API_KEY_HEADER_NAME = require_apiKeys.API_KEY_HEADER_NAME;
9
+ exports.HOT_UPDATER_INFRASTRUCTURE_GENERATION = require_handlerVersionRoutes.HOT_UPDATER_INFRASTRUCTURE_GENERATION;
5
10
  exports.HOT_UPDATER_SERVER_VERSION = require_version.HOT_UPDATER_SERVER_VERSION;
6
- exports.createHandler = require_handler.createHandler;
11
+ exports.createApiKey = require_apiKeys.createApiKey;
12
+ exports.createHandlers = require_handler.createHandlers;
7
13
  exports.createHotUpdater = require_createHotUpdaterCore.createHotUpdater;
14
+ exports.createInsightsProvider = require_provider.createInsightsProvider;
15
+ exports.provisionApiKey = require_apiKeys.provisionApiKey;
16
+ exports.registerApiKey = require_apiKeys.registerApiKey;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,11 @@
1
- import { HandlerAPI, HandlerOptions, HandlerRoutes, createHandler } from "./handler.cjs";
2
- import { CreateHotUpdaterOptions, HotUpdaterAPI, RuntimeHotUpdaterAPI, createHotUpdater } from "./createHotUpdaterCore.cjs";
1
+ import { API_KEY_HEADER_NAME, ApiKeyManagementAPI, ApiKeyMetadata, CreatedApiKey, createApiKey, provisionApiKey, registerApiKey } from "./apiKeys.cjs";
2
+ import { HandlerAPI, HotUpdaterHandler, HotUpdaterHandlers } from "./handlerTypes.cjs";
3
+ import { ActiveInstallationInput, ActiveInstallationOverview, ActiveInstallationWindow, BundleEventInsightsResult, BundleEventInsightsWindow, BundleEventOverview, BundleEventSummary, BundleEventSummaryByBundle, CreateBundleEventRequest, CreateBundleEventRequestBase, InsightsCohortPoint, InsightsSeriesPoint, InstallationHistoryRow, InstallationSearchRow, OffsetPaginationResult } from "./insights/domain.cjs";
4
+ import { InsightsProvider } from "./insights/types.cjs";
5
+ import { createHandlers } from "./handler.cjs";
6
+ import { ClientAccessPolicy, CreateHotUpdaterOptions, HotUpdaterAPI, RuntimeHotUpdaterAPI, createHotUpdater } from "./createHotUpdaterCore.cjs";
3
7
  import { Bundle, ChannelsResponse, DataResponse, Paginated, PaginatedResult, PaginationInfo, PaginationOptions } from "./types/index.cjs";
4
8
  import { HOT_UPDATER_SERVER_VERSION } from "./version.cjs";
5
- export { Bundle, ChannelsResponse, type CreateHotUpdaterOptions, DataResponse, HOT_UPDATER_SERVER_VERSION, type HandlerAPI, type HandlerOptions, type HandlerRoutes, type HotUpdaterAPI, Paginated, PaginatedResult, PaginationInfo, PaginationOptions, type RuntimeHotUpdaterAPI, createHandler, createHotUpdater };
9
+ import { createInsightsProvider } from "./insights/bounded/provider.cjs";
10
+ import { HOT_UPDATER_INFRASTRUCTURE_GENERATION } from "./handlerVersionRoutes.cjs";
11
+ export { API_KEY_HEADER_NAME, ActiveInstallationInput, ActiveInstallationOverview, ActiveInstallationWindow, type ApiKeyManagementAPI, type ApiKeyMetadata, Bundle, BundleEventInsightsResult, BundleEventInsightsWindow, BundleEventOverview, BundleEventSummary, BundleEventSummaryByBundle, ChannelsResponse, type ClientAccessPolicy, CreateBundleEventRequest, CreateBundleEventRequestBase, type CreateHotUpdaterOptions, type CreatedApiKey, DataResponse, HOT_UPDATER_INFRASTRUCTURE_GENERATION, HOT_UPDATER_SERVER_VERSION, type HandlerAPI, type HotUpdaterAPI, type HotUpdaterHandler, type HotUpdaterHandlers, InsightsCohortPoint, type InsightsProvider, InsightsSeriesPoint, InstallationHistoryRow, InstallationSearchRow, OffsetPaginationResult, Paginated, PaginatedResult, PaginationInfo, PaginationOptions, type RuntimeHotUpdaterAPI, createApiKey, createHandlers, createHotUpdater, createInsightsProvider, provisionApiKey, registerApiKey };
package/dist/index.d.mts CHANGED
@@ -1,5 +1,11 @@
1
- import { HandlerAPI, HandlerOptions, HandlerRoutes, createHandler } from "./handler.mjs";
2
- import { CreateHotUpdaterOptions, HotUpdaterAPI, RuntimeHotUpdaterAPI, createHotUpdater } from "./createHotUpdaterCore.mjs";
1
+ import { API_KEY_HEADER_NAME, ApiKeyManagementAPI, ApiKeyMetadata, CreatedApiKey, createApiKey, provisionApiKey, registerApiKey } from "./apiKeys.mjs";
2
+ import { HandlerAPI, HotUpdaterHandler, HotUpdaterHandlers } from "./handlerTypes.mjs";
3
+ import { ActiveInstallationInput, ActiveInstallationOverview, ActiveInstallationWindow, BundleEventInsightsResult, BundleEventInsightsWindow, BundleEventOverview, BundleEventSummary, BundleEventSummaryByBundle, CreateBundleEventRequest, CreateBundleEventRequestBase, InsightsCohortPoint, InsightsSeriesPoint, InstallationHistoryRow, InstallationSearchRow, OffsetPaginationResult } from "./insights/domain.mjs";
4
+ import { InsightsProvider } from "./insights/types.mjs";
5
+ import { createHandlers } from "./handler.mjs";
6
+ import { ClientAccessPolicy, CreateHotUpdaterOptions, HotUpdaterAPI, RuntimeHotUpdaterAPI, createHotUpdater } from "./createHotUpdaterCore.mjs";
3
7
  import { Bundle, ChannelsResponse, DataResponse, Paginated, PaginatedResult, PaginationInfo, PaginationOptions } from "./types/index.mjs";
4
8
  import { HOT_UPDATER_SERVER_VERSION } from "./version.mjs";
5
- export { Bundle, ChannelsResponse, type CreateHotUpdaterOptions, DataResponse, HOT_UPDATER_SERVER_VERSION, type HandlerAPI, type HandlerOptions, type HandlerRoutes, type HotUpdaterAPI, Paginated, PaginatedResult, PaginationInfo, PaginationOptions, type RuntimeHotUpdaterAPI, createHandler, createHotUpdater };
9
+ import { createInsightsProvider } from "./insights/bounded/provider.mjs";
10
+ import { HOT_UPDATER_INFRASTRUCTURE_GENERATION } from "./handlerVersionRoutes.mjs";
11
+ export { API_KEY_HEADER_NAME, ActiveInstallationInput, ActiveInstallationOverview, ActiveInstallationWindow, type ApiKeyManagementAPI, type ApiKeyMetadata, Bundle, BundleEventInsightsResult, BundleEventInsightsWindow, BundleEventOverview, BundleEventSummary, BundleEventSummaryByBundle, ChannelsResponse, type ClientAccessPolicy, CreateBundleEventRequest, CreateBundleEventRequestBase, type CreateHotUpdaterOptions, type CreatedApiKey, DataResponse, HOT_UPDATER_INFRASTRUCTURE_GENERATION, HOT_UPDATER_SERVER_VERSION, type HandlerAPI, type HotUpdaterAPI, type HotUpdaterHandler, type HotUpdaterHandlers, InsightsCohortPoint, type InsightsProvider, InsightsSeriesPoint, InstallationHistoryRow, InstallationSearchRow, OffsetPaginationResult, Paginated, PaginatedResult, PaginationInfo, PaginationOptions, type RuntimeHotUpdaterAPI, createApiKey, createHandlers, createHotUpdater, createInsightsProvider, provisionApiKey, registerApiKey };
package/dist/index.mjs CHANGED
@@ -1,4 +1,7 @@
1
1
  import { HOT_UPDATER_SERVER_VERSION } from "./version.mjs";
2
- import { createHandler } from "./handler.mjs";
2
+ import { HOT_UPDATER_INFRASTRUCTURE_GENERATION } from "./handlerVersionRoutes.mjs";
3
+ import { createHandlers } from "./handler.mjs";
4
+ import { createInsightsProvider } from "./insights/bounded/provider.mjs";
5
+ import { API_KEY_HEADER_NAME, createApiKey, provisionApiKey, registerApiKey } from "./apiKeys.mjs";
3
6
  import { createHotUpdater } from "./createHotUpdaterCore.mjs";
4
- export { HOT_UPDATER_SERVER_VERSION, createHandler, createHotUpdater };
7
+ export { API_KEY_HEADER_NAME, HOT_UPDATER_INFRASTRUCTURE_GENERATION, HOT_UPDATER_SERVER_VERSION, createApiKey, createHandlers, createHotUpdater, createInsightsProvider, provisionApiKey, registerApiKey };
@@ -0,0 +1,87 @@
1
+ //#region src/insights/bounded/activeOverview.ts
2
+ const HOUR_MS = 3600 * 1e3;
3
+ const DAY_MS = 24 * HOUR_MS;
4
+ const ACTIVE_BUNDLE_EVENT_TYPES = [
5
+ "UPDATE_APPLIED",
6
+ "RECOVERED",
7
+ "RELEASE_ADOPTED",
8
+ "UNCHANGED"
9
+ ];
10
+ const activeWindowDefinitions = {
11
+ "24h": {
12
+ bucketCount: 24,
13
+ bucketSizeMs: HOUR_MS
14
+ },
15
+ "7d": {
16
+ bucketCount: 7,
17
+ bucketSizeMs: DAY_MS
18
+ },
19
+ "30d": {
20
+ bucketCount: 30,
21
+ bucketSizeMs: DAY_MS
22
+ }
23
+ };
24
+ function getActiveWindowDefinition(window) {
25
+ return activeWindowDefinitions[window];
26
+ }
27
+ function compareCodePoints(left, right) {
28
+ if (left < right) return -1;
29
+ if (left > right) return 1;
30
+ return 0;
31
+ }
32
+ function isNewer(candidate, current) {
33
+ return candidate.received_at_ms > current.received_at_ms || candidate.received_at_ms === current.received_at_ms && compareCodePoints(candidate.id, current.id) > 0;
34
+ }
35
+ function collectActiveInstallationOverview(request) {
36
+ const definition = getActiveWindowDefinition(request.window);
37
+ const windowStartMs = request.asOfMs - definition.bucketCount * definition.bucketSizeMs;
38
+ const rows = request.rows.filter((row) => row.received_at_ms >= windowStartMs && row.received_at_ms < request.asOfMs);
39
+ const latestByInstall = /* @__PURE__ */ new Map();
40
+ for (const row of rows) {
41
+ const current = latestByInstall.get(row.install_id);
42
+ if (current === void 0 || isNewer(row, current)) latestByInstall.set(row.install_id, row);
43
+ }
44
+ const selectedRows = [...latestByInstall.values()].filter((row) => request.userId === void 0 || row.user_id === request.userId);
45
+ const selectedInstallIds = new Set(selectedRows.map((row) => row.install_id));
46
+ const bundleCounts = /* @__PURE__ */ new Map();
47
+ for (const row of selectedRows) bundleCounts.set(row.to_bundle_id, (bundleCounts.get(row.to_bundle_id) ?? 0) + 1);
48
+ const latestByBucket = Array.from({ length: definition.bucketCount }, () => /* @__PURE__ */ new Map());
49
+ for (const row of rows) {
50
+ if (!selectedInstallIds.has(row.install_id)) continue;
51
+ const bucket = latestByBucket[Math.floor((row.received_at_ms - windowStartMs) / definition.bucketSizeMs)];
52
+ if (bucket === void 0) continue;
53
+ const current = bucket.get(row.install_id);
54
+ if (current === void 0 || isNewer(row, current)) bucket.set(row.install_id, row);
55
+ }
56
+ const bundleCountsByBucket = latestByBucket.map((bucket) => {
57
+ const counts = /* @__PURE__ */ new Map();
58
+ for (const row of bucket.values()) counts.set(row.to_bundle_id, (counts.get(row.to_bundle_id) ?? 0) + 1);
59
+ return counts;
60
+ });
61
+ const bundleObservationTotals = /* @__PURE__ */ new Map();
62
+ for (const counts of bundleCountsByBucket) for (const [bundleId, count] of counts) bundleObservationTotals.set(bundleId, (bundleObservationTotals.get(bundleId) ?? 0) + count);
63
+ return {
64
+ asOfMs: request.asOfMs,
65
+ window: request.window,
66
+ activeInstallations: selectedInstallIds.size,
67
+ series: latestByBucket.map((bucket, index) => ({
68
+ bucketStartMs: windowStartMs + index * definition.bucketSizeMs,
69
+ value: bucket.size
70
+ })),
71
+ bundleSeries: [...bundleObservationTotals].sort(([leftId, leftTotal], [rightId, rightTotal]) => rightTotal - leftTotal || compareCodePoints(leftId, rightId)).map(([bundleId]) => ({
72
+ bundleId,
73
+ series: bundleCountsByBucket.map((counts, index) => ({
74
+ bucketStartMs: windowStartMs + index * definition.bucketSizeMs,
75
+ value: counts.get(bundleId) ?? 0
76
+ }))
77
+ })),
78
+ bundles: [...bundleCounts].map(([bundleId, installations]) => ({
79
+ bundleId,
80
+ installations
81
+ })).sort((left, right) => right.installations - left.installations || compareCodePoints(left.bundleId, right.bundleId))
82
+ };
83
+ }
84
+ //#endregion
85
+ exports.ACTIVE_BUNDLE_EVENT_TYPES = ACTIVE_BUNDLE_EVENT_TYPES;
86
+ exports.collectActiveInstallationOverview = collectActiveInstallationOverview;
87
+ exports.getActiveWindowDefinition = getActiveWindowDefinition;
@@ -0,0 +1,85 @@
1
+ //#region src/insights/bounded/activeOverview.ts
2
+ const HOUR_MS = 3600 * 1e3;
3
+ const DAY_MS = 24 * HOUR_MS;
4
+ const ACTIVE_BUNDLE_EVENT_TYPES = [
5
+ "UPDATE_APPLIED",
6
+ "RECOVERED",
7
+ "RELEASE_ADOPTED",
8
+ "UNCHANGED"
9
+ ];
10
+ const activeWindowDefinitions = {
11
+ "24h": {
12
+ bucketCount: 24,
13
+ bucketSizeMs: HOUR_MS
14
+ },
15
+ "7d": {
16
+ bucketCount: 7,
17
+ bucketSizeMs: DAY_MS
18
+ },
19
+ "30d": {
20
+ bucketCount: 30,
21
+ bucketSizeMs: DAY_MS
22
+ }
23
+ };
24
+ function getActiveWindowDefinition(window) {
25
+ return activeWindowDefinitions[window];
26
+ }
27
+ function compareCodePoints(left, right) {
28
+ if (left < right) return -1;
29
+ if (left > right) return 1;
30
+ return 0;
31
+ }
32
+ function isNewer(candidate, current) {
33
+ return candidate.received_at_ms > current.received_at_ms || candidate.received_at_ms === current.received_at_ms && compareCodePoints(candidate.id, current.id) > 0;
34
+ }
35
+ function collectActiveInstallationOverview(request) {
36
+ const definition = getActiveWindowDefinition(request.window);
37
+ const windowStartMs = request.asOfMs - definition.bucketCount * definition.bucketSizeMs;
38
+ const rows = request.rows.filter((row) => row.received_at_ms >= windowStartMs && row.received_at_ms < request.asOfMs);
39
+ const latestByInstall = /* @__PURE__ */ new Map();
40
+ for (const row of rows) {
41
+ const current = latestByInstall.get(row.install_id);
42
+ if (current === void 0 || isNewer(row, current)) latestByInstall.set(row.install_id, row);
43
+ }
44
+ const selectedRows = [...latestByInstall.values()].filter((row) => request.userId === void 0 || row.user_id === request.userId);
45
+ const selectedInstallIds = new Set(selectedRows.map((row) => row.install_id));
46
+ const bundleCounts = /* @__PURE__ */ new Map();
47
+ for (const row of selectedRows) bundleCounts.set(row.to_bundle_id, (bundleCounts.get(row.to_bundle_id) ?? 0) + 1);
48
+ const latestByBucket = Array.from({ length: definition.bucketCount }, () => /* @__PURE__ */ new Map());
49
+ for (const row of rows) {
50
+ if (!selectedInstallIds.has(row.install_id)) continue;
51
+ const bucket = latestByBucket[Math.floor((row.received_at_ms - windowStartMs) / definition.bucketSizeMs)];
52
+ if (bucket === void 0) continue;
53
+ const current = bucket.get(row.install_id);
54
+ if (current === void 0 || isNewer(row, current)) bucket.set(row.install_id, row);
55
+ }
56
+ const bundleCountsByBucket = latestByBucket.map((bucket) => {
57
+ const counts = /* @__PURE__ */ new Map();
58
+ for (const row of bucket.values()) counts.set(row.to_bundle_id, (counts.get(row.to_bundle_id) ?? 0) + 1);
59
+ return counts;
60
+ });
61
+ const bundleObservationTotals = /* @__PURE__ */ new Map();
62
+ for (const counts of bundleCountsByBucket) for (const [bundleId, count] of counts) bundleObservationTotals.set(bundleId, (bundleObservationTotals.get(bundleId) ?? 0) + count);
63
+ return {
64
+ asOfMs: request.asOfMs,
65
+ window: request.window,
66
+ activeInstallations: selectedInstallIds.size,
67
+ series: latestByBucket.map((bucket, index) => ({
68
+ bucketStartMs: windowStartMs + index * definition.bucketSizeMs,
69
+ value: bucket.size
70
+ })),
71
+ bundleSeries: [...bundleObservationTotals].sort(([leftId, leftTotal], [rightId, rightTotal]) => rightTotal - leftTotal || compareCodePoints(leftId, rightId)).map(([bundleId]) => ({
72
+ bundleId,
73
+ series: bundleCountsByBucket.map((counts, index) => ({
74
+ bucketStartMs: windowStartMs + index * definition.bucketSizeMs,
75
+ value: counts.get(bundleId) ?? 0
76
+ }))
77
+ })),
78
+ bundles: [...bundleCounts].map(([bundleId, installations]) => ({
79
+ bundleId,
80
+ installations
81
+ })).sort((left, right) => right.installations - left.installations || compareCodePoints(left.bundleId, right.bundleId))
82
+ };
83
+ }
84
+ //#endregion
85
+ export { ACTIVE_BUNDLE_EVENT_TYPES, collectActiveInstallationOverview, getActiveWindowDefinition };
@@ -0,0 +1,42 @@
1
+ const require_scan = require("./scan.cjs");
2
+ //#region src/insights/bounded/installationSearch.ts
3
+ function toSearchRow(row) {
4
+ return {
5
+ installId: row.install_id,
6
+ username: row.username,
7
+ userId: row.user_id,
8
+ lastKnownBundleId: row.to_bundle_id,
9
+ latestStatus: row.type,
10
+ platform: row.platform,
11
+ appVersion: row.app_version,
12
+ channel: row.channel,
13
+ cohort: row.cohort,
14
+ receivedAtMs: row.received_at_ms
15
+ };
16
+ }
17
+ function matchesIdentity(row, query) {
18
+ return row.install_id.toLowerCase().includes(query) || row.user_id?.toLowerCase().includes(query) === true || row.username?.toLowerCase().includes(query) === true;
19
+ }
20
+ function searchEventInstallations(request) {
21
+ const query = request.query.toLowerCase();
22
+ const matchingInstallIds = /* @__PURE__ */ new Set();
23
+ for (const row of request.rows) if (query.length === 0 || matchesIdentity(row, query)) matchingInstallIds.add(row.install_id);
24
+ const latestByInstall = /* @__PURE__ */ new Map();
25
+ for (const row of request.rows) {
26
+ if (!matchingInstallIds.has(row.install_id)) continue;
27
+ const current = latestByInstall.get(row.install_id);
28
+ if (current === void 0 || require_scan.compareEventNewest(row, current) < 0) latestByInstall.set(row.install_id, row);
29
+ }
30
+ const matchingRows = [...latestByInstall.values()].sort((left, right) => require_scan.compareCodePoints(left.install_id, right.install_id));
31
+ const pageSize = Math.min(Math.max(request.limit, 0), 100);
32
+ return {
33
+ data: matchingRows.slice(request.offset, request.offset + pageSize).map(toSearchRow),
34
+ pagination: {
35
+ total: matchingRows.length,
36
+ limit: request.limit,
37
+ offset: request.offset
38
+ }
39
+ };
40
+ }
41
+ //#endregion
42
+ exports.searchEventInstallations = searchEventInstallations;
@@ -0,0 +1,42 @@
1
+ import { compareCodePoints, compareEventNewest } from "./scan.mjs";
2
+ //#region src/insights/bounded/installationSearch.ts
3
+ function toSearchRow(row) {
4
+ return {
5
+ installId: row.install_id,
6
+ username: row.username,
7
+ userId: row.user_id,
8
+ lastKnownBundleId: row.to_bundle_id,
9
+ latestStatus: row.type,
10
+ platform: row.platform,
11
+ appVersion: row.app_version,
12
+ channel: row.channel,
13
+ cohort: row.cohort,
14
+ receivedAtMs: row.received_at_ms
15
+ };
16
+ }
17
+ function matchesIdentity(row, query) {
18
+ return row.install_id.toLowerCase().includes(query) || row.user_id?.toLowerCase().includes(query) === true || row.username?.toLowerCase().includes(query) === true;
19
+ }
20
+ function searchEventInstallations(request) {
21
+ const query = request.query.toLowerCase();
22
+ const matchingInstallIds = /* @__PURE__ */ new Set();
23
+ for (const row of request.rows) if (query.length === 0 || matchesIdentity(row, query)) matchingInstallIds.add(row.install_id);
24
+ const latestByInstall = /* @__PURE__ */ new Map();
25
+ for (const row of request.rows) {
26
+ if (!matchingInstallIds.has(row.install_id)) continue;
27
+ const current = latestByInstall.get(row.install_id);
28
+ if (current === void 0 || compareEventNewest(row, current) < 0) latestByInstall.set(row.install_id, row);
29
+ }
30
+ const matchingRows = [...latestByInstall.values()].sort((left, right) => compareCodePoints(left.install_id, right.install_id));
31
+ const pageSize = Math.min(Math.max(request.limit, 0), 100);
32
+ return {
33
+ data: matchingRows.slice(request.offset, request.offset + pageSize).map(toSearchRow),
34
+ pagination: {
35
+ total: matchingRows.length,
36
+ limit: request.limit,
37
+ offset: request.offset
38
+ }
39
+ };
40
+ }
41
+ //#endregion
42
+ export { searchEventInstallations };
@@ -0,0 +1,39 @@
1
+ require("../../_virtual/_rolldown/runtime.cjs");
2
+ let _hot_updater_plugin_core = require("@hot-updater/plugin-core");
3
+ //#region src/insights/bounded/persistence.ts
4
+ function createBundleEventRow(input) {
5
+ const base = {
6
+ id: (0, _hot_updater_plugin_core.createUUIDv7)(),
7
+ install_id: input.installId,
8
+ user_id: input.userId ?? null,
9
+ username: input.username ?? null,
10
+ from_release_id: input.fromReleaseId,
11
+ to_release_id: input.toReleaseId,
12
+ to_bundle_id: input.toBundleId,
13
+ platform: input.platform,
14
+ app_version: input.appVersion,
15
+ channel: input.channel,
16
+ cohort: input.cohort,
17
+ fingerprint_hash: input.fingerprintHash,
18
+ sdk_version: input.sdkVersion ?? null,
19
+ received_at_ms: Date.now()
20
+ };
21
+ switch (input.type) {
22
+ case "UPDATE_APPLIED":
23
+ case "RECOVERED":
24
+ case "RELEASE_ADOPTED": return {
25
+ ...base,
26
+ type: input.type,
27
+ from_bundle_id: input.fromBundleId,
28
+ update_strategy: input.updateStrategy
29
+ };
30
+ case "UNCHANGED": return {
31
+ ...base,
32
+ type: input.type,
33
+ from_bundle_id: null,
34
+ update_strategy: null
35
+ };
36
+ }
37
+ }
38
+ //#endregion
39
+ exports.createBundleEventRow = createBundleEventRow;