@hot-updater/server 1.0.0-rc.1 → 1.0.0-rc.16

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 (542) hide show
  1. package/dist/_virtual/_rolldown/runtime.cjs +1 -1
  2. package/dist/adapters/drizzle.cjs +35 -69
  3. package/dist/adapters/drizzle.d.cts +13 -6
  4. package/dist/adapters/drizzle.d.mts +13 -6
  5. package/dist/adapters/drizzle.mjs +34 -69
  6. package/dist/adapters/drizzleExecutor.cjs +114 -0
  7. package/dist/adapters/drizzleExecutor.d.cts +9 -0
  8. package/dist/adapters/drizzleExecutor.d.mts +9 -0
  9. package/dist/adapters/drizzleExecutor.mjs +112 -0
  10. package/dist/adapters/kysely.cjs +25 -31
  11. package/dist/adapters/kysely.d.cts +10 -7
  12. package/dist/adapters/kysely.d.mts +10 -7
  13. package/dist/adapters/kysely.mjs +25 -31
  14. package/dist/adapters/kyselyExecutor.cjs +32 -0
  15. package/dist/adapters/kyselyExecutor.d.cts +12 -0
  16. package/dist/adapters/kyselyExecutor.d.mts +12 -0
  17. package/dist/adapters/kyselyExecutor.mjs +32 -0
  18. package/dist/adapters/mongodb.cjs +28 -27
  19. package/dist/adapters/mongodb.d.cts +10 -5
  20. package/dist/adapters/mongodb.d.mts +10 -5
  21. package/dist/adapters/mongodb.mjs +28 -27
  22. package/dist/adapters/mongodbAdapter.cjs +174 -0
  23. package/dist/adapters/mongodbAdapter.d.cts +9 -0
  24. package/dist/adapters/mongodbAdapter.d.mts +9 -0
  25. package/dist/adapters/mongodbAdapter.mjs +173 -0
  26. package/dist/adapters/prisma.cjs +38 -220
  27. package/dist/adapters/prisma.d.cts +12 -8
  28. package/dist/adapters/prisma.d.mts +12 -8
  29. package/dist/adapters/prisma.mjs +38 -219
  30. package/dist/adapters/prismaExecutor.cjs +64 -0
  31. package/dist/adapters/prismaExecutor.mjs +64 -0
  32. package/dist/adapters/sqlProviders.cjs +9 -0
  33. package/dist/adapters/sqlProviders.mjs +9 -0
  34. package/dist/apiKeys.cjs +8 -8
  35. package/dist/apiKeys.d.cts +1 -24
  36. package/dist/apiKeys.d.mts +1 -24
  37. package/dist/apiKeys.mjs +5 -9
  38. package/dist/assembly/assemblePlugins.cjs +147 -0
  39. package/dist/assembly/assemblePlugins.d.cts +12 -0
  40. package/dist/assembly/assemblePlugins.d.mts +12 -0
  41. package/dist/assembly/assemblePlugins.mjs +146 -0
  42. package/dist/assembly/databasePlugins.cjs +23 -0
  43. package/dist/assembly/databasePlugins.d.cts +43 -0
  44. package/dist/assembly/databasePlugins.d.mts +43 -0
  45. package/dist/assembly/databasePlugins.mjs +22 -0
  46. package/dist/core/api.cjs +91 -0
  47. package/dist/core/api.d.cts +65 -0
  48. package/dist/core/api.d.mts +65 -0
  49. package/dist/core/api.mjs +87 -0
  50. package/dist/core/baseCandidates.cjs +71 -0
  51. package/dist/core/baseCandidates.d.cts +17 -0
  52. package/dist/core/baseCandidates.d.mts +17 -0
  53. package/dist/core/baseCandidates.mjs +69 -0
  54. package/dist/core/operations.cjs +350 -0
  55. package/dist/core/operations.mjs +350 -0
  56. package/dist/core/reads.cjs +273 -0
  57. package/dist/core/reads.d.cts +48 -0
  58. package/dist/core/reads.d.mts +48 -0
  59. package/dist/core/reads.mjs +267 -0
  60. package/dist/core/releases.cjs +193 -0
  61. package/dist/core/releases.mjs +190 -0
  62. package/dist/core/schema.cjs +279 -0
  63. package/dist/core/schema.d.cts +307 -0
  64. package/dist/core/schema.d.mts +307 -0
  65. package/dist/core/schema.mjs +277 -0
  66. package/dist/core/writes.cjs +96 -0
  67. package/dist/core/writes.mjs +91 -0
  68. package/dist/createHotUpdaterCore.cjs +61 -63
  69. package/dist/createHotUpdaterCore.d.cts +53 -45
  70. package/dist/createHotUpdaterCore.d.mts +53 -45
  71. package/dist/createHotUpdaterCore.mjs +61 -63
  72. package/dist/database/cursor.cjs +44 -0
  73. package/dist/database/cursor.d.cts +7 -0
  74. package/dist/database/cursor.d.mts +7 -0
  75. package/dist/database/cursor.mjs +40 -0
  76. package/dist/database/database.cjs +44 -0
  77. package/dist/database/database.d.cts +123 -0
  78. package/dist/database/database.d.mts +123 -0
  79. package/dist/database/database.mjs +44 -0
  80. package/dist/database/definitions.d.cts +68 -0
  81. package/dist/database/definitions.d.mts +68 -0
  82. package/dist/database/engine.cjs +37 -0
  83. package/dist/database/engine.d.cts +40 -0
  84. package/dist/database/engine.d.mts +40 -0
  85. package/dist/database/engine.mjs +37 -0
  86. package/dist/database/engineAggregates.cjs +115 -0
  87. package/dist/database/engineAggregates.mjs +112 -0
  88. package/dist/database/engineReads.cjs +180 -0
  89. package/dist/database/engineReads.d.cts +35 -0
  90. package/dist/database/engineReads.d.mts +35 -0
  91. package/dist/database/engineReads.mjs +179 -0
  92. package/dist/database/engineTransaction.cjs +403 -0
  93. package/dist/database/engineTransaction.d.cts +26 -0
  94. package/dist/database/engineTransaction.d.mts +26 -0
  95. package/dist/database/engineTransaction.mjs +402 -0
  96. package/dist/database/errors.cjs +27 -0
  97. package/dist/database/errors.d.cts +21 -0
  98. package/dist/database/errors.d.mts +21 -0
  99. package/dist/database/errors.mjs +24 -0
  100. package/dist/database/fence.cjs +124 -0
  101. package/dist/database/fence.d.cts +42 -0
  102. package/dist/database/fence.d.mts +42 -0
  103. package/dist/database/fence.mjs +116 -0
  104. package/dist/database/index.cjs +127 -0
  105. package/dist/database/index.d.cts +17 -0
  106. package/dist/database/index.d.mts +17 -0
  107. package/dist/database/index.mjs +15 -0
  108. package/dist/database/kv/kvAdapter.cjs +273 -0
  109. package/dist/database/kv/kvAdapter.d.cts +101 -0
  110. package/dist/database/kv/kvAdapter.d.mts +101 -0
  111. package/dist/database/kv/kvAdapter.mjs +272 -0
  112. package/dist/database/resolveSchema.cjs +196 -0
  113. package/dist/database/resolveSchema.d.cts +42 -0
  114. package/dist/database/resolveSchema.d.mts +42 -0
  115. package/dist/database/resolveSchema.mjs +193 -0
  116. package/dist/database/schema.cjs +21 -0
  117. package/dist/database/schema.d.cts +61 -0
  118. package/dist/database/schema.d.mts +61 -0
  119. package/dist/database/schema.mjs +20 -0
  120. package/dist/database/sql/sqlAdapter.cjs +282 -0
  121. package/dist/database/sql/sqlAdapter.d.cts +46 -0
  122. package/dist/database/sql/sqlAdapter.d.mts +46 -0
  123. package/dist/database/sql/sqlAdapter.mjs +279 -0
  124. package/dist/database/sql/sqlSchema.cjs +115 -0
  125. package/dist/database/sql/sqlSchema.d.cts +8 -0
  126. package/dist/database/sql/sqlSchema.d.mts +8 -0
  127. package/dist/database/sql/sqlSchema.mjs +111 -0
  128. package/dist/db/builtInDatabase.cjs +104 -0
  129. package/dist/db/builtInDatabase.d.cts +38 -0
  130. package/dist/db/builtInDatabase.d.mts +38 -0
  131. package/dist/db/builtInDatabase.mjs +97 -0
  132. package/dist/db/createBundleDiff.cjs +6 -6
  133. package/dist/db/createBundleDiff.d.cts +4 -7
  134. package/dist/db/createBundleDiff.d.mts +4 -7
  135. package/dist/db/createBundleDiff.mjs +5 -5
  136. package/dist/db/engineDrizzleSchema.cjs +54 -0
  137. package/dist/db/engineDrizzleSchema.mjs +54 -0
  138. package/dist/db/engineMigrator.cjs +53 -0
  139. package/dist/db/engineMigrator.mjs +53 -0
  140. package/dist/db/enginePrismaSchema.cjs +85 -0
  141. package/dist/db/enginePrismaSchema.d.cts +5 -0
  142. package/dist/db/enginePrismaSchema.d.mts +5 -0
  143. package/dist/db/enginePrismaSchema.mjs +84 -0
  144. package/dist/db/engineSql.cjs +26 -0
  145. package/dist/db/engineSql.d.cts +17 -0
  146. package/dist/db/engineSql.d.mts +17 -0
  147. package/dist/db/engineSql.mjs +25 -0
  148. package/dist/db/engineSqlMigrator.cjs +66 -0
  149. package/dist/db/engineSqlMigrator.mjs +66 -0
  150. package/dist/db/fixedMigratorShared.cjs +0 -11
  151. package/dist/db/fixedMigratorShared.mjs +1 -8
  152. package/dist/db/index.cjs +23 -8
  153. package/dist/db/index.d.cts +10 -4
  154. package/dist/db/index.d.mts +10 -4
  155. package/dist/db/index.mjs +16 -8
  156. package/dist/db/releaseCatalog.cjs +20 -44
  157. package/dist/db/releaseCatalog.d.cts +2 -3
  158. package/dist/db/releaseCatalog.d.mts +2 -3
  159. package/dist/db/releaseCatalog.mjs +20 -44
  160. package/dist/db/schemaSettings.cjs +63 -0
  161. package/dist/db/schemaSettings.d.cts +12 -0
  162. package/dist/db/schemaSettings.d.mts +12 -0
  163. package/dist/db/schemaSettings.mjs +62 -0
  164. package/dist/db/settingsMigrator.cjs +92 -0
  165. package/dist/db/settingsMigrator.mjs +89 -0
  166. package/dist/db/types.cjs +3 -12
  167. package/dist/db/types.d.cts +26 -69
  168. package/dist/db/types.d.mts +26 -69
  169. package/dist/db/types.mjs +2 -11
  170. package/dist/db/updateArtifacts.cjs +38 -68
  171. package/dist/db/updateArtifacts.mjs +40 -69
  172. package/dist/handler.cjs +72 -43
  173. package/dist/handler.d.cts +3 -6
  174. package/dist/handler.d.mts +3 -6
  175. package/dist/handler.mjs +73 -43
  176. package/dist/handlerAdminRoutes.cjs +375 -0
  177. package/dist/handlerAdminRoutes.mjs +373 -0
  178. package/dist/handlerParameters.cjs +6 -11
  179. package/dist/handlerParameters.mjs +6 -10
  180. package/dist/handlerReleaseCatalogRoutes.cjs +11 -14
  181. package/dist/handlerReleaseCatalogRoutes.mjs +11 -13
  182. package/dist/handlerTypes.d.cts +2 -56
  183. package/dist/handlerTypes.d.mts +2 -56
  184. package/dist/handlerVersionRoutes.cjs +20 -4
  185. package/dist/handlerVersionRoutes.d.cts +1 -0
  186. package/dist/handlerVersionRoutes.d.mts +1 -0
  187. package/dist/handlerVersionRoutes.mjs +20 -4
  188. package/dist/index.cjs +4 -7
  189. package/dist/index.d.cts +10 -9
  190. package/dist/index.d.mts +10 -9
  191. package/dist/index.mjs +4 -4
  192. package/dist/insights/domain.d.cts +53 -70
  193. package/dist/insights/domain.d.mts +53 -70
  194. package/dist/insights/errors.cjs +35 -8
  195. package/dist/insights/errors.mjs +35 -8
  196. package/dist/insights/eventInput.cjs +64 -24
  197. package/dist/insights/eventInput.mjs +63 -25
  198. package/dist/insights/provider.cjs +356 -0
  199. package/dist/insights/provider.d.cts +6 -0
  200. package/dist/insights/provider.d.mts +6 -0
  201. package/dist/insights/provider.mjs +355 -0
  202. package/dist/insights/queryInput.cjs +78 -43
  203. package/dist/insights/queryInput.mjs +76 -40
  204. package/dist/insights/routes.cjs +94 -34
  205. package/dist/insights/routes.mjs +93 -35
  206. package/dist/insights/types.d.cts +27 -12
  207. package/dist/insights/types.d.mts +27 -12
  208. package/dist/node.d.cts +1 -2
  209. package/dist/node.d.mts +1 -2
  210. package/dist/package.cjs +1 -1
  211. package/dist/package.mjs +1 -1
  212. package/dist/plugins/api-keys/index.cjs +50 -0
  213. package/dist/plugins/api-keys/index.d.cts +85 -0
  214. package/dist/plugins/api-keys/index.d.mts +85 -0
  215. package/dist/plugins/api-keys/index.mjs +47 -0
  216. package/dist/plugins/api-keys/model.cjs +58 -0
  217. package/dist/plugins/api-keys/model.d.cts +8 -0
  218. package/dist/plugins/api-keys/model.d.mts +8 -0
  219. package/dist/plugins/api-keys/model.mjs +58 -0
  220. package/dist/plugins/api-keys/schema.cjs +38 -0
  221. package/dist/plugins/api-keys/schema.d.cts +44 -0
  222. package/dist/plugins/api-keys/schema.d.mts +44 -0
  223. package/dist/plugins/api-keys/schema.mjs +39 -0
  224. package/dist/plugins/builtIn.cjs +7 -0
  225. package/dist/plugins/builtIn.mjs +6 -0
  226. package/dist/plugins/definePlugin.cjs +5 -0
  227. package/dist/plugins/definePlugin.d.cts +73 -0
  228. package/dist/plugins/definePlugin.d.mts +73 -0
  229. package/dist/plugins/definePlugin.mjs +5 -0
  230. package/dist/plugins/index.cjs +17 -0
  231. package/dist/plugins/index.d.cts +9 -0
  232. package/dist/plugins/index.d.mts +9 -0
  233. package/dist/plugins/index.mjs +7 -0
  234. package/dist/plugins/insights/index.cjs +48 -0
  235. package/dist/plugins/insights/index.d.cts +295 -0
  236. package/dist/plugins/insights/index.d.mts +295 -0
  237. package/dist/plugins/insights/index.mjs +44 -0
  238. package/dist/plugins/insights/model.cjs +17 -0
  239. package/dist/plugins/insights/model.d.cts +10 -0
  240. package/dist/plugins/insights/model.d.mts +10 -0
  241. package/dist/plugins/insights/model.mjs +17 -0
  242. package/dist/plugins/insights/reads.cjs +471 -0
  243. package/dist/plugins/insights/reads.mjs +466 -0
  244. package/dist/plugins/insights/recordEvent.cjs +135 -0
  245. package/dist/plugins/insights/recordEvent.d.cts +13 -0
  246. package/dist/plugins/insights/recordEvent.d.mts +13 -0
  247. package/dist/plugins/insights/recordEvent.mjs +132 -0
  248. package/dist/plugins/insights/schema.cjs +347 -0
  249. package/dist/plugins/insights/schema.d.cts +258 -0
  250. package/dist/plugins/insights/schema.d.mts +258 -0
  251. package/dist/plugins/insights/schema.mjs +344 -0
  252. package/dist/storageAccess.cjs +0 -1
  253. package/dist/types/index.d.cts +2 -29
  254. package/dist/types/index.d.mts +2 -29
  255. package/package.json +30 -7
  256. package/src/adapters/drizzle.conformance.integration.spec.ts +34 -0
  257. package/src/adapters/drizzle.readBudgets.integration.spec.ts +41 -0
  258. package/src/adapters/drizzle.spec.ts +277 -208
  259. package/src/adapters/drizzle.ts +48 -128
  260. package/src/adapters/drizzleExecutor.ts +166 -0
  261. package/src/adapters/kysely.conformance.integration.spec.ts +36 -0
  262. package/src/adapters/kysely.readBudgets.integration.spec.ts +42 -0
  263. package/src/adapters/kysely.spec.ts +212 -117
  264. package/src/adapters/kysely.ts +32 -87
  265. package/src/adapters/kyselyExecutor.ts +53 -0
  266. package/src/adapters/mongodb.integration.spec.ts +180 -0
  267. package/src/adapters/mongodb.readBudgets.integration.spec.ts +61 -0
  268. package/src/adapters/mongodb.ts +36 -65
  269. package/src/adapters/mongodbAdapter.ts +293 -0
  270. package/src/adapters/prisma.conformance.integration.spec.ts +36 -0
  271. package/src/adapters/prisma.integration.spec.ts +138 -0
  272. package/src/adapters/prisma.readBudgets.integration.spec.ts +40 -0
  273. package/src/adapters/prisma.spec.ts +226 -173
  274. package/src/adapters/prisma.ts +60 -451
  275. package/src/adapters/prismaExecutor.ts +107 -0
  276. package/src/adapters/prismaTestClients.ts +175 -0
  277. package/src/adapters/sqlProviders.ts +14 -0
  278. package/src/apiKeys.integration.spec.ts +19 -119
  279. package/src/apiKeys.spec.ts +39 -35
  280. package/src/apiKeys.ts +2 -8
  281. package/src/assembly/assemblePlugins.ts +269 -0
  282. package/src/assembly/assembly.spec.ts +325 -0
  283. package/src/assembly/assembly.types.spec.ts +155 -0
  284. package/src/assembly/builtInRoutes.spec.ts +213 -0
  285. package/src/assembly/databasePlugins.spec.ts +71 -0
  286. package/src/assembly/databasePlugins.ts +96 -0
  287. package/src/core/api.ts +139 -0
  288. package/src/core/baseCandidates.spec.ts +378 -0
  289. package/src/core/baseCandidates.ts +136 -0
  290. package/src/core/index.ts +44 -0
  291. package/src/core/operations.spec.ts +415 -0
  292. package/src/core/operations.ts +551 -0
  293. package/src/core/reads.spec.ts +300 -0
  294. package/src/core/reads.ts +417 -0
  295. package/src/core/releases.spec.ts +308 -0
  296. package/src/core/releases.ts +345 -0
  297. package/src/core/schema.ts +173 -0
  298. package/src/core/writes.ts +160 -0
  299. package/src/createHotUpdaterCore.ts +191 -167
  300. package/src/database/cursor.ts +65 -0
  301. package/src/database/database.ts +335 -0
  302. package/src/database/database.types.spec.ts +135 -0
  303. package/src/database/definitions.ts +72 -0
  304. package/src/database/engine.ts +62 -0
  305. package/src/database/engineAggregates.spec.ts +539 -0
  306. package/src/database/engineAggregates.ts +193 -0
  307. package/src/database/engineReads.spec.ts +219 -0
  308. package/src/database/engineReads.ts +302 -0
  309. package/src/database/engineTransaction.spec.ts +710 -0
  310. package/src/database/engineTransaction.ts +588 -0
  311. package/src/database/errors.ts +30 -0
  312. package/src/database/fence.spec.ts +223 -0
  313. package/src/database/fence.ts +171 -0
  314. package/src/database/index.ts +154 -0
  315. package/src/database/kv/kvAdapter.conformance.integration.spec.ts +17 -0
  316. package/src/database/kv/kvAdapter.readBudgets.integration.spec.ts +17 -0
  317. package/src/database/kv/kvAdapter.spec.ts +468 -0
  318. package/src/database/kv/kvAdapter.ts +450 -0
  319. package/src/database/kv/kvTestStore.ts +91 -0
  320. package/src/database/memory.readBudgets.integration.spec.ts +13 -0
  321. package/src/database/resolveSchema.spec.ts +230 -0
  322. package/src/database/resolveSchema.ts +372 -0
  323. package/src/database/schema.ts +169 -0
  324. package/src/database/schema.types.spec.ts +94 -0
  325. package/src/database/sql/docker-compose.yml +36 -0
  326. package/src/database/sql/sqlAdapter.integration.spec.ts +148 -0
  327. package/src/database/sql/sqlAdapter.readBudgets.integration.spec.ts +109 -0
  328. package/src/database/sql/sqlAdapter.spec.ts +255 -0
  329. package/src/database/sql/sqlAdapter.ts +532 -0
  330. package/src/database/sql/sqlSchema.ts +205 -0
  331. package/src/database/sql/sqlTestExecutors.ts +183 -0
  332. package/src/databaseTestSuite.spec.ts +39 -0
  333. package/src/db/builtInDatabase.ts +149 -0
  334. package/src/db/bundleRows.spec.ts +3 -3
  335. package/src/db/createBundleDiff.spec.ts +43 -29
  336. package/src/db/createBundleDiff.ts +13 -7
  337. package/src/db/engineDrizzleSchema.ts +72 -0
  338. package/src/db/engineMigrator.ts +105 -0
  339. package/src/db/enginePrismaSchema.ts +147 -0
  340. package/src/db/engineSql.spec.ts +129 -0
  341. package/src/db/engineSql.ts +48 -0
  342. package/src/db/engineSqlMigrator.ts +116 -0
  343. package/src/db/fixedMigratorShared.ts +1 -20
  344. package/src/db/index.spec.ts +116 -824
  345. package/src/db/index.ts +43 -14
  346. package/src/db/pluginTables.spec.ts +218 -0
  347. package/src/db/releaseCatalog.ts +48 -90
  348. package/src/db/schemaSettings.ts +104 -0
  349. package/src/db/settingsMigrator.spec.ts +65 -0
  350. package/src/db/settingsMigrator.ts +152 -0
  351. package/src/db/types.ts +25 -165
  352. package/src/db/updateArtifacts.spec.ts +170 -294
  353. package/src/db/updateArtifacts.ts +74 -142
  354. package/src/db/updateCheck.bench.ts +47 -0
  355. package/src/handler-standalone.integration.spec.ts +204 -412
  356. package/src/handler.spec.ts +134 -51
  357. package/src/handler.testFixtures.ts +14 -19
  358. package/src/handler.ts +163 -75
  359. package/src/handlerAdminRoutes.spec.ts +183 -0
  360. package/src/handlerAdminRoutes.ts +536 -0
  361. package/src/handlerAuth.integration.spec.ts +3 -3
  362. package/src/handlerManagement.spec.ts +136 -196
  363. package/src/handlerReleaseCatalogRoutes.ts +15 -17
  364. package/src/handlerTypes.ts +3 -80
  365. package/src/handlerVersionRoutes.ts +20 -7
  366. package/src/index.ts +7 -14
  367. package/src/insights/domain.ts +56 -77
  368. package/src/insights/errors.spec.ts +48 -0
  369. package/src/insights/errors.ts +50 -6
  370. package/src/insights/eventInput.ts +80 -27
  371. package/src/insights/insights.integration.spec.ts +525 -44
  372. package/src/insights/provider.spec.ts +574 -0
  373. package/src/insights/provider.ts +667 -0
  374. package/src/insights/queryInput.ts +113 -73
  375. package/src/insights/routes.ts +125 -69
  376. package/src/insights/types.ts +47 -36
  377. package/src/lintRules.spec.ts +76 -0
  378. package/src/plugins/api-keys/apiKeysPlugin.spec.ts +172 -0
  379. package/src/plugins/api-keys/index.ts +66 -0
  380. package/src/plugins/api-keys/model.ts +69 -0
  381. package/src/plugins/api-keys/schema.ts +22 -0
  382. package/src/plugins/builtIn.ts +5 -0
  383. package/src/plugins/definePlugin.ts +100 -0
  384. package/src/plugins/index.ts +55 -0
  385. package/src/plugins/insights/index.ts +86 -0
  386. package/src/plugins/insights/insightsRollout.integration.spec.ts +187 -0
  387. package/src/plugins/insights/model.spec.ts +66 -0
  388. package/src/plugins/insights/model.ts +19 -0
  389. package/src/plugins/insights/reads.spec.ts +725 -0
  390. package/src/plugins/insights/reads.ts +707 -0
  391. package/src/plugins/insights/recordEvent.spec.ts +443 -0
  392. package/src/plugins/insights/recordEvent.ts +238 -0
  393. package/src/plugins/insights/schema.ts +229 -0
  394. package/src/plugins/pluginTestHarness.spec.ts +56 -0
  395. package/src/readBudgets.testFixtures.ts +15 -0
  396. package/src/releaseCatalogRoutes.integration.spec.ts +47 -92
  397. package/src/runtime.spec.ts +91 -77
  398. package/src/runtime.testFixtures.ts +28 -44
  399. package/src/runtimeBundles.integration.spec.ts +112 -0
  400. package/src/testUtilsMutation.integration.spec.ts +215 -0
  401. package/src/testUtilsPackage.integration.spec.ts +176 -0
  402. package/src/types/index.ts +0 -32
  403. package/dist/adapters/databaseConstraintErrors.cjs +0 -23
  404. package/dist/adapters/databaseConstraintErrors.mjs +0 -21
  405. package/dist/adapters/databasePluginUtils.cjs +0 -117
  406. package/dist/adapters/databasePluginUtils.mjs +0 -106
  407. package/dist/adapters/drizzleCrud.cjs +0 -250
  408. package/dist/adapters/drizzleCrud.mjs +0 -249
  409. package/dist/adapters/drizzleLazyDB.cjs +0 -101
  410. package/dist/adapters/drizzleLazyDB.mjs +0 -101
  411. package/dist/adapters/drizzleQuery.cjs +0 -68
  412. package/dist/adapters/drizzleQuery.mjs +0 -67
  413. package/dist/adapters/kyselyCrud.cjs +0 -254
  414. package/dist/adapters/kyselyCrud.mjs +0 -253
  415. package/dist/adapters/kyselyQuery.cjs +0 -56
  416. package/dist/adapters/kyselyQuery.mjs +0 -55
  417. package/dist/adapters/mongodbCollections.cjs +0 -36
  418. package/dist/adapters/mongodbCollections.mjs +0 -30
  419. package/dist/adapters/mongodbQuery.cjs +0 -77
  420. package/dist/adapters/mongodbQuery.mjs +0 -70
  421. package/dist/adapters/mongodbReads.cjs +0 -148
  422. package/dist/adapters/mongodbReads.mjs +0 -147
  423. package/dist/adapters/mongodbWrites.cjs +0 -134
  424. package/dist/adapters/mongodbWrites.mjs +0 -133
  425. package/dist/adapters/prismaQuery.cjs +0 -58
  426. package/dist/adapters/prismaQuery.mjs +0 -56
  427. package/dist/adapters/prismaRows.cjs +0 -198
  428. package/dist/adapters/prismaRows.mjs +0 -188
  429. package/dist/db/databasePluginCore.cjs +0 -138
  430. package/dist/db/databasePluginCore.mjs +0 -137
  431. package/dist/db/fixedMigrator.cjs +0 -79
  432. package/dist/db/fixedMigrator.mjs +0 -78
  433. package/dist/db/fixedMigratorMongo.cjs +0 -319
  434. package/dist/db/fixedMigratorMongo.mjs +0 -319
  435. package/dist/db/mongoMigrationExecution.cjs +0 -11
  436. package/dist/db/mongoMigrationExecution.mjs +0 -11
  437. package/dist/db/schema/mongodb.cjs +0 -25
  438. package/dist/db/schema/mongodb.mjs +0 -25
  439. package/dist/db/schema/registry.cjs +0 -21
  440. package/dist/db/schema/registry.mjs +0 -17
  441. package/dist/db/schema/sql.cjs +0 -101
  442. package/dist/db/schema/sql.mjs +0 -97
  443. package/dist/db/schema/sqlOperations.cjs +0 -27
  444. package/dist/db/schema/sqlOperations.mjs +0 -26
  445. package/dist/db/schemaGenerators.cjs +0 -256
  446. package/dist/db/schemaGenerators.mjs +0 -254
  447. package/dist/db/schemaReadiness.cjs +0 -24
  448. package/dist/db/schemaReadiness.d.cts +0 -8
  449. package/dist/db/schemaReadiness.d.mts +0 -8
  450. package/dist/db/schemaReadiness.mjs +0 -23
  451. package/dist/db/sqlMigrationExecution.cjs +0 -67
  452. package/dist/db/sqlMigrationExecution.mjs +0 -66
  453. package/dist/handlerBundleRoutes.cjs +0 -155
  454. package/dist/handlerBundleRoutes.mjs +0 -155
  455. package/dist/handlerReleaseManagementRoutes.cjs +0 -125
  456. package/dist/handlerReleaseManagementRoutes.mjs +0 -124
  457. package/dist/insights/bounded/activeOverview.cjs +0 -87
  458. package/dist/insights/bounded/activeOverview.mjs +0 -85
  459. package/dist/insights/bounded/installationSearch.cjs +0 -42
  460. package/dist/insights/bounded/installationSearch.mjs +0 -42
  461. package/dist/insights/bounded/persistence.cjs +0 -39
  462. package/dist/insights/bounded/persistence.mjs +0 -38
  463. package/dist/insights/bounded/provider.cjs +0 -170
  464. package/dist/insights/bounded/provider.d.cts +0 -7
  465. package/dist/insights/bounded/provider.d.mts +0 -7
  466. package/dist/insights/bounded/provider.mjs +0 -170
  467. package/dist/insights/bounded/scan.cjs +0 -144
  468. package/dist/insights/bounded/scan.mjs +0 -138
  469. package/dist/insights/persistence.d.cts +0 -6
  470. package/dist/insights/persistence.d.mts +0 -6
  471. package/dist/schema/dsl.cjs +0 -103
  472. package/dist/schema/dsl.mjs +0 -87
  473. package/dist/schema/index.cjs +0 -6
  474. package/dist/schema/index.mjs +0 -7
  475. package/dist/schema/settings.cjs +0 -9
  476. package/dist/schema/settings.mjs +0 -9
  477. package/dist/schema/types.cjs +0 -8
  478. package/dist/schema/types.mjs +0 -6
  479. package/dist/schema/v1_0_0.cjs +0 -364
  480. package/dist/schema/v1_0_0.mjs +0 -357
  481. package/src/adapters/databaseConstraintErrors.ts +0 -25
  482. package/src/adapters/databasePluginTestDatabase.ts +0 -106
  483. package/src/adapters/databasePluginUtils.spec.ts +0 -29
  484. package/src/adapters/databasePluginUtils.ts +0 -214
  485. package/src/adapters/drizzleCrud.ts +0 -419
  486. package/src/adapters/drizzleLazyDB.ts +0 -257
  487. package/src/adapters/drizzleQuery.ts +0 -164
  488. package/src/adapters/kyselyCrud.ts +0 -588
  489. package/src/adapters/kyselyQuery.ts +0 -128
  490. package/src/adapters/mongodb.spec.ts +0 -141
  491. package/src/adapters/mongodbCollections.ts +0 -62
  492. package/src/adapters/mongodbQuery.ts +0 -226
  493. package/src/adapters/mongodbReads.ts +0 -311
  494. package/src/adapters/mongodbRows.spec.ts +0 -64
  495. package/src/adapters/mongodbRows.ts +0 -118
  496. package/src/adapters/mongodbTestClient.ts +0 -342
  497. package/src/adapters/mongodbTestFilter.ts +0 -166
  498. package/src/adapters/mongodbWrites.ts +0 -260
  499. package/src/adapters/prismaQuery.ts +0 -129
  500. package/src/adapters/prismaRows.spec.ts +0 -98
  501. package/src/adapters/prismaRows.ts +0 -301
  502. package/src/adapters/prismaTestClient.ts +0 -396
  503. package/src/calculatePagination.ts +0 -34
  504. package/src/db/databasePluginCore.spec.ts +0 -62
  505. package/src/db/databasePluginCore.testFixtures.ts +0 -76
  506. package/src/db/databasePluginCore.ts +0 -185
  507. package/src/db/databasePluginUpdateCheck.bench.ts +0 -107
  508. package/src/db/fixedMigrator.spec.ts +0 -231
  509. package/src/db/fixedMigrator.ts +0 -147
  510. package/src/db/fixedMigratorMongo.spec.ts +0 -192
  511. package/src/db/fixedMigratorMongo.ts +0 -425
  512. package/src/db/hotUpdaterSchema.ts +0 -5
  513. package/src/db/mongoMigrationExecution.ts +0 -23
  514. package/src/db/schema/definitions.ts +0 -1
  515. package/src/db/schema/mongodb.ts +0 -40
  516. package/src/db/schema/registry.ts +0 -55
  517. package/src/db/schema/sql.ts +0 -241
  518. package/src/db/schema/sqlOperations.ts +0 -63
  519. package/src/db/schema/types.ts +0 -1
  520. package/src/db/schemaGenerators.ts +0 -466
  521. package/src/db/schemaReadiness.ts +0 -34
  522. package/src/db/sqlMigrationExecution.spec.ts +0 -65
  523. package/src/db/sqlMigrationExecution.ts +0 -108
  524. package/src/db/types.spec.ts +0 -55
  525. package/src/handlerBundleRoutes.ts +0 -259
  526. package/src/handlerReleaseManagementRoutes.ts +0 -213
  527. package/src/insights/bounded/activeOverview.spec.ts +0 -189
  528. package/src/insights/bounded/activeOverview.ts +0 -148
  529. package/src/insights/bounded/installationSearch.spec.ts +0 -130
  530. package/src/insights/bounded/installationSearch.ts +0 -69
  531. package/src/insights/bounded/persistence.ts +0 -43
  532. package/src/insights/bounded/provider.spec.ts +0 -219
  533. package/src/insights/bounded/provider.ts +0 -229
  534. package/src/insights/bounded/scan.ts +0 -235
  535. package/src/insights/persistence.ts +0 -10
  536. package/src/schema/dsl-all-versions.spec.ts +0 -35
  537. package/src/schema/dsl.ts +0 -187
  538. package/src/schema/index.ts +0 -10
  539. package/src/schema/settings.ts +0 -15
  540. package/src/schema/types.ts +0 -79
  541. package/src/schema/v1_0_0.spec.ts +0 -146
  542. package/src/schema/v1_0_0.ts +0 -465
@@ -1,4 +1,16 @@
1
+ /**
2
+ * The body of `POST /events`. Fields the server does not know are ignored, so
3
+ * a newer client's report still records on an older server.
4
+ */
1
5
  export type CreateBundleEventRequestBase = {
6
+ /**
7
+ * The report's idempotency key: a canonical lowercase UUIDv7 that the
8
+ * client creates once per report and repeats on every retry. The server
9
+ * stores the report under it, so a retried report counts once, and a
10
+ * report under an ID already stored changes nothing. Without it, the
11
+ * server creates the ID, and a retry counts as another report.
12
+ */
13
+ readonly eventId?: string;
2
14
  readonly installId: string;
3
15
  readonly toBundleId: string;
4
16
  readonly userId?: string;
@@ -15,17 +27,7 @@ export type CreateBundleEventRequestBase = {
15
27
 
16
28
  export type CreateBundleEventRequest =
17
29
  | (CreateBundleEventRequestBase & {
18
- readonly type: "UPDATE_APPLIED";
19
- readonly fromBundleId: string;
20
- readonly updateStrategy: "fingerprint" | "appVersion";
21
- })
22
- | (CreateBundleEventRequestBase & {
23
- readonly type: "RECOVERED";
24
- readonly fromBundleId: string;
25
- readonly updateStrategy: "fingerprint" | "appVersion";
26
- })
27
- | (CreateBundleEventRequestBase & {
28
- readonly type: "RELEASE_ADOPTED";
30
+ readonly type: "UPDATE_DOWNLOADED" | "UPDATE_APPLIED" | "RECOVERED";
29
31
  readonly fromBundleId: string;
30
32
  readonly updateStrategy: "fingerprint" | "appVersion";
31
33
  })
@@ -35,28 +37,20 @@ export type CreateBundleEventRequest =
35
37
  readonly updateStrategy: null;
36
38
  });
37
39
 
38
- export type BundleEventSummary = {
39
- readonly installed: number;
40
- readonly recovered: number;
41
- };
42
-
43
- export type BundleEventSummaryByBundle = BundleEventSummary & {
44
- readonly bundleId: string;
45
- };
46
-
47
- export type BundleEventInsightsWindow = "24h" | "7d" | "30d" | "all";
48
40
  export type ActiveInstallationWindow = "24h" | "7d" | "30d";
49
41
 
50
- export type InstallationSearchRow = {
42
+ export type EventHistoryRow = {
43
+ readonly id: string;
51
44
  readonly installId: string;
52
- readonly username: string | null;
53
- readonly userId: string | null;
54
- readonly lastKnownBundleId: string;
55
- readonly latestStatus:
45
+ readonly type:
46
+ | "UPDATE_DOWNLOADED"
56
47
  | "UPDATE_APPLIED"
57
48
  | "RECOVERED"
58
- | "RELEASE_ADOPTED"
59
49
  | "UNCHANGED";
50
+ readonly fromBundleId: string | null;
51
+ readonly toBundleId: string;
52
+ readonly username: string | null;
53
+ readonly userId: string | null;
60
54
  readonly platform: "ios" | "android";
61
55
  readonly appVersion: string;
62
56
  readonly channel: string;
@@ -64,13 +58,19 @@ export type InstallationSearchRow = {
64
58
  readonly receivedAtMs: number;
65
59
  };
66
60
 
67
- export type InstallationHistoryRow = {
68
- readonly id: string;
69
- readonly type: "UPDATE_APPLIED" | "RECOVERED";
61
+ export type InstallationHistoryRow = EventHistoryRow & {
62
+ readonly type: "UPDATE_DOWNLOADED" | "UPDATE_APPLIED" | "RECOVERED";
70
63
  readonly fromBundleId: string;
71
- readonly toBundleId: string;
64
+ };
65
+
66
+ export type InstallationRow = {
67
+ readonly installId: string;
72
68
  readonly username: string | null;
73
69
  readonly userId: string | null;
70
+ readonly lastKnownBundleId: string;
71
+ readonly pendingBundleId: string | null;
72
+ readonly pendingReleaseId: string | null;
73
+ readonly latestStatus: EventHistoryRow["type"];
74
74
  readonly platform: "ios" | "android";
75
75
  readonly appVersion: string;
76
76
  readonly channel: string;
@@ -78,62 +78,41 @@ export type InstallationHistoryRow = {
78
78
  readonly receivedAtMs: number;
79
79
  };
80
80
 
81
- export type OffsetPaginationResult<TData> = {
82
- readonly data: readonly TData[];
83
- readonly pagination: {
84
- readonly total: number;
85
- readonly limit: number;
86
- readonly offset: number;
87
- };
81
+ export type CursorPage<T> = {
82
+ readonly data: readonly T[];
83
+ readonly nextCursor: string | null;
88
84
  };
89
85
 
90
- export type InsightsSeriesPoint = {
91
- readonly bucketStartMs: number;
92
- readonly value: number;
86
+ export type EventCursorPage<T extends EventHistoryRow> = CursorPage<T> & {
87
+ readonly beforeReceivedAtMs: number;
93
88
  };
94
89
 
95
- export type InsightsCohortPoint = {
96
- readonly cohort: string;
97
- readonly value: number;
90
+ export type InsightsScope = {
91
+ readonly platform: "ios" | "android";
92
+ readonly channel: string;
98
93
  };
99
94
 
100
- export type BundleEventInsightsResult = {
101
- readonly summary: BundleEventSummary;
102
- readonly series: {
103
- readonly installed: readonly InsightsSeriesPoint[];
104
- readonly recovered: readonly InsightsSeriesPoint[];
105
- };
106
- readonly cohorts: {
107
- readonly installed: readonly InsightsCohortPoint[];
108
- readonly recovered: readonly InsightsCohortPoint[];
109
- };
110
- readonly recentEvents: OffsetPaginationResult<InstallationHistoryRow>;
95
+ export type InsightsBundleSelection = InsightsScope & {
96
+ readonly bundleId: string;
97
+ readonly outcome: "downloaded" | "applied" | "recovered" | "unchanged";
111
98
  };
112
99
 
113
- export type BundleEventOverview = {
114
- readonly trackedInstallations: number;
115
- readonly bundles: readonly {
116
- readonly bundleId: string;
117
- readonly installations: number;
118
- }[];
100
+ export type InsightsCountMeasurement = {
101
+ readonly count: number;
102
+ readonly measuredAtMs: number;
119
103
  };
120
104
 
121
- export type ActiveInstallationOverview = {
122
- readonly asOfMs: number;
105
+ export type ReportingOverview = InsightsScope & {
123
106
  readonly window: ActiveInstallationWindow;
124
- readonly activeInstallations: number;
125
- readonly series: readonly InsightsSeriesPoint[];
126
- readonly bundleSeries: readonly {
127
- readonly bundleId: string;
128
- readonly series: readonly InsightsSeriesPoint[];
129
- }[];
130
- readonly bundles: readonly {
107
+ readonly sinceMs: number;
108
+ readonly beforeReceivedAtMs: number;
109
+ readonly reportingInstallations: InsightsCountMeasurement;
110
+ readonly bundle?: {
131
111
  readonly bundleId: string;
132
- readonly installations: number;
133
- }[];
134
- };
135
-
136
- export type ActiveInstallationInput = {
137
- readonly window: ActiveInstallationWindow;
138
- readonly userId?: string;
112
+ readonly reportingInstallations: InsightsCountMeasurement;
113
+ readonly downloadedReports: InsightsCountMeasurement;
114
+ readonly appliedReports: InsightsCountMeasurement;
115
+ readonly recoveredReports: InsightsCountMeasurement;
116
+ readonly unchangedReports: InsightsCountMeasurement;
117
+ };
139
118
  };
@@ -0,0 +1,48 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ import {
4
+ DatabaseConflictError,
5
+ DatabaseTransactionError,
6
+ } from "../database/errors";
7
+ import { isDatabaseBusyError } from "./errors";
8
+
9
+ const failure = (message: string, fields: object) =>
10
+ Object.assign(new Error(message), fields);
11
+
12
+ describe("isDatabaseBusyError", () => {
13
+ it.each([
14
+ [
15
+ "a transaction out of retries",
16
+ new DatabaseConflictError("The transaction conflicted 8 times."),
17
+ ],
18
+ [
19
+ "a throttled DynamoDB read",
20
+ failure("Throughput exceeds the current capacity of your table.", {
21
+ name: "ProvisionedThroughputExceededException",
22
+ }),
23
+ ],
24
+ ["an HTTP 429", failure("Too Many Requests", { status: 429 })],
25
+ [
26
+ "an AWS SDK 429",
27
+ failure("Too Many Requests", { $metadata: { httpStatusCode: 429 } }),
28
+ ],
29
+ [
30
+ "Firestore's RESOURCE_EXHAUSTED",
31
+ failure("8 RESOURCE_EXHAUSTED: Quota exceeded.", { code: 8 }),
32
+ ],
33
+ ])("holds for %s", (_case, error) => {
34
+ expect(isDatabaseBusyError(error)).toBe(true);
35
+ });
36
+
37
+ it.each([
38
+ ["a gauge below zero", new DatabaseTransactionError("gauge below zero")],
39
+ ["MongoDB's unknown error, also code 8", failure("unknown", { code: 8 })],
40
+ [
41
+ "a refused connection",
42
+ failure("connect ECONNREFUSED", { code: "ECONNREFUSED" }),
43
+ ],
44
+ ["a thrown string", "busy"],
45
+ ])("does not hold for %s", (_case, error) => {
46
+ expect(isDatabaseBusyError(error)).toBe(false);
47
+ });
48
+ });
@@ -1,9 +1,4 @@
1
- export class InsightsScanLimitExceededError extends Error {
2
- constructor(readonly limit: number) {
3
- super(`Insights event scan exceeded ${limit} rows.`);
4
- this.name = "InsightsScanLimitExceededError";
5
- }
6
- }
1
+ import { DatabaseConflictError } from "../database/errors";
7
2
 
8
3
  export class InsightsBadRequestError extends Error {
9
4
  readonly name = "InsightsBadRequestError";
@@ -16,3 +11,52 @@ export class InsightsPayloadTooLargeError extends Error {
16
11
  super(`Event payload exceeds ${maximumBytes} bytes`);
17
12
  }
18
13
  }
14
+
15
+ /** Error names the AWS SDKs give a throttled request, as their retry strategy classifies them. */
16
+ const THROTTLING_NAMES = new Set([
17
+ "ProvisionedThroughputExceededException",
18
+ "RequestLimitExceeded",
19
+ "RequestThrottled",
20
+ "RequestThrottledException",
21
+ "SlowDown",
22
+ "ThrottledException",
23
+ "Throttling",
24
+ "ThrottlingException",
25
+ "TooManyRequestsException",
26
+ ]);
27
+
28
+ /** gRPC's RESOURCE_EXHAUSTED, the status Firestore throttles a request with. */
29
+ const RESOURCE_EXHAUSTED = 8;
30
+
31
+ interface ErrorFields {
32
+ readonly name?: unknown;
33
+ readonly code?: unknown;
34
+ readonly message?: unknown;
35
+ readonly status?: unknown;
36
+ readonly statusCode?: unknown;
37
+ readonly $metadata?: { readonly httpStatusCode?: unknown };
38
+ }
39
+
40
+ /**
41
+ * Whether the database is busy rather than broken, so the same request can
42
+ * succeed later: a transaction ran out of retries, or the backend throttled
43
+ * a request. Writes already retry transient failures, throttled ones
44
+ * included, until the budget runs out. A throttled read throws the backend's
45
+ * own error, so it is recognized by what the backends call it: a name the
46
+ * AWS SDKs retry as throttling, HTTP 429, or gRPC's RESOURCE_EXHAUSTED.
47
+ */
48
+ export const isDatabaseBusyError = (error: unknown): boolean => {
49
+ if (error instanceof DatabaseConflictError) return true;
50
+ if (typeof error !== "object" || error === null) return false;
51
+ const { name, code, message, status, statusCode, $metadata } =
52
+ error as ErrorFields;
53
+ return (
54
+ (typeof name === "string" && THROTTLING_NAMES.has(name)) ||
55
+ [status, statusCode, $metadata?.httpStatusCode].includes(429) ||
56
+ // A gRPC error's message starts with its status name; other drivers use
57
+ // numeric codes too, such as MongoDB's 8 for an unknown error.
58
+ (code === RESOURCE_EXHAUSTED &&
59
+ typeof message === "string" &&
60
+ message.includes("RESOURCE_EXHAUSTED"))
61
+ );
62
+ };
@@ -1,3 +1,9 @@
1
+ import {
2
+ createUUIDv7,
3
+ isUUIDv7,
4
+ type BundleEventRow,
5
+ } from "@hot-updater/plugin-core";
6
+
1
7
  import type {
2
8
  CreateBundleEventRequest,
3
9
  CreateBundleEventRequestBase,
@@ -8,26 +14,9 @@ import {
8
14
  } from "./errors";
9
15
 
10
16
  const MAX_EVENT_STRING_LENGTH = 1_024;
17
+ const MAX_IDENTITY_LENGTH = 255;
11
18
  export const EVENT_BODY_MAX_BYTES = 16 * 1_024;
12
19
 
13
- const eventKeys = new Set([
14
- "type",
15
- "installId",
16
- "toBundleId",
17
- "userId",
18
- "username",
19
- "platform",
20
- "appVersion",
21
- "channel",
22
- "cohort",
23
- "fingerprintHash",
24
- "fromBundleId",
25
- "fromReleaseId",
26
- "toReleaseId",
27
- "updateStrategy",
28
- "sdkVersion",
29
- ]);
30
-
31
20
  function isRecord(value: unknown): value is Readonly<Record<string, unknown>> {
32
21
  return typeof value === "object" && value !== null && !Array.isArray(value);
33
22
  }
@@ -40,7 +29,10 @@ function requireStringField(
40
29
  if (
41
30
  typeof value !== "string" ||
42
31
  value.length === 0 ||
43
- value.length > MAX_EVENT_STRING_LENGTH
32
+ value.length > MAX_EVENT_STRING_LENGTH ||
33
+ new TextDecoder("utf-8", { ignoreBOM: true }).decode(
34
+ new TextEncoder().encode(value),
35
+ ) !== value
44
36
  ) {
45
37
  throw new InsightsBadRequestError(`Invalid event field: ${key}`);
46
38
  }
@@ -55,6 +47,32 @@ function requireNullableStringField(
55
47
  return requireStringField(payload, key);
56
48
  }
57
49
 
50
+ function requireIdentityField(
51
+ payload: Readonly<Record<string, unknown>>,
52
+ key: "installId" | "userId",
53
+ ): string {
54
+ const value = requireStringField(payload, key);
55
+ if (value.length > MAX_IDENTITY_LENGTH) {
56
+ throw new InsightsBadRequestError(`Invalid event field: ${key}`);
57
+ }
58
+ return value;
59
+ }
60
+
61
+ /**
62
+ * The report's idempotency key, when the client sends one: it creates the
63
+ * UUIDv7 once and repeats it on every retry of that report.
64
+ */
65
+ function readEventId(
66
+ payload: Readonly<Record<string, unknown>>,
67
+ ): string | undefined {
68
+ const value = payload.eventId;
69
+ if (value === undefined) return undefined;
70
+ if (!isUUIDv7(value)) {
71
+ throw new InsightsBadRequestError("Invalid event field: eventId");
72
+ }
73
+ return value;
74
+ }
75
+
58
76
  async function readBoundedText(request: Request): Promise<string> {
59
77
  const contentLength = request.headers.get("content-length");
60
78
  const declaredByteLength = Number(contentLength);
@@ -96,22 +114,23 @@ async function parseJson(request: Request): Promise<unknown> {
96
114
  }
97
115
 
98
116
  function requireEvent(payload: unknown): CreateBundleEventRequest {
99
- if (
100
- !isRecord(payload) ||
101
- Object.keys(payload).some((key) => !eventKeys.has(key))
102
- ) {
117
+ // Fields this server does not know are ignored, not refused, so a report
118
+ // from a newer SDK that adds one still records on an older server.
119
+ if (!isRecord(payload)) {
103
120
  throw new InsightsBadRequestError("Invalid event payload");
104
121
  }
105
122
  const platform = requireStringField(payload, "platform");
106
123
  if (platform !== "ios" && platform !== "android") {
107
124
  throw new InsightsBadRequestError("Invalid event field: platform");
108
125
  }
126
+ const eventId = readEventId(payload);
109
127
  const base: CreateBundleEventRequestBase = {
110
- installId: requireStringField(payload, "installId"),
128
+ ...(eventId === undefined ? {} : { eventId }),
129
+ installId: requireIdentityField(payload, "installId"),
111
130
  toBundleId: requireStringField(payload, "toBundleId"),
112
131
  ...(payload.userId === undefined
113
132
  ? {}
114
- : { userId: requireStringField(payload, "userId") }),
133
+ : { userId: requireIdentityField(payload, "userId") }),
115
134
  ...(payload.username === undefined
116
135
  ? {}
117
136
  : { username: requireStringField(payload, "username") }),
@@ -129,9 +148,9 @@ function requireEvent(payload: unknown): CreateBundleEventRequest {
129
148
  };
130
149
  const type = requireStringField(payload, "type");
131
150
  switch (type) {
151
+ case "UPDATE_DOWNLOADED":
132
152
  case "UPDATE_APPLIED":
133
- case "RECOVERED":
134
- case "RELEASE_ADOPTED": {
153
+ case "RECOVERED": {
135
154
  const updateStrategy = requireStringField(payload, "updateStrategy");
136
155
  if (updateStrategy !== "fingerprint" && updateStrategy !== "appVersion") {
137
156
  throw new InsightsBadRequestError(
@@ -166,3 +185,37 @@ export async function parseBundleEventRequest(
166
185
  const payload = await parseJson(request);
167
186
  return requireEvent(payload);
168
187
  }
188
+
189
+ export function createBundleEventRow(
190
+ input: CreateBundleEventRequest,
191
+ ): BundleEventRow {
192
+ input = requireEvent(input);
193
+ const base = {
194
+ app_version: input.appVersion,
195
+ channel: input.channel,
196
+ from_release_id: input.fromReleaseId,
197
+ // The client's ID makes a retried report the same row, which records once.
198
+ id: input.eventId ?? createUUIDv7(),
199
+ install_id: input.installId,
200
+ platform: input.platform,
201
+ received_at_ms: Date.now(),
202
+ to_bundle_id: input.toBundleId,
203
+ to_release_id: input.toReleaseId,
204
+ user_id: input.userId ?? null,
205
+ metadata: {
206
+ cohort: input.cohort,
207
+ fingerprint_hash: input.fingerprintHash,
208
+ sdk_version: input.sdkVersion ?? null,
209
+ username: input.username ?? null,
210
+ update_strategy: input.updateStrategy,
211
+ },
212
+ };
213
+ switch (input.type) {
214
+ case "UPDATE_DOWNLOADED":
215
+ case "UPDATE_APPLIED":
216
+ case "RECOVERED":
217
+ return { ...base, from_bundle_id: input.fromBundleId, type: input.type };
218
+ case "UNCHANGED":
219
+ return { ...base, from_bundle_id: null, type: input.type };
220
+ }
221
+ }