@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,15 +1,6 @@
1
1
  import { PGlite } from "@electric-sql/pglite";
2
2
  import type { Bundle } from "@hot-updater/core";
3
- import {
4
- createReleaseCatalogScopeKey,
5
- encodeChannelKey,
6
- NIL_UUID,
7
- } from "@hot-updater/core";
8
- import {
9
- bundleToRow,
10
- commitReleaseCatalogMutation,
11
- createDatabaseClient,
12
- } from "@hot-updater/plugin-core";
3
+ import { NIL_UUID } from "@hot-updater/core";
13
4
  import { Kysely } from "kysely";
14
5
  import { PGliteDialect } from "kysely-pglite-dialect";
15
6
  import { HttpResponse, http } from "msw";
@@ -18,17 +9,18 @@ import { uuidv7 } from "uuidv7";
18
9
  import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest";
19
10
 
20
11
  import { standaloneRepository } from "../../../plugins/standalone/src";
21
- import { createInMemoryDatabasePlugin } from "../../test-utils/test/inMemoryDatabasePlugin";
22
12
  import { kyselyAdapter } from "./adapters/kysely";
23
13
  import { createMigrator } from "./db";
24
14
  import { createHotUpdater, type RuntimeHotUpdaterAPI } from "./index";
15
+ import { insights } from "./plugins/insights";
25
16
 
26
17
  const db = new PGlite();
27
18
  const kysely = new Kysely<object>({ dialect: new PGliteDialect(db) });
28
19
  const clientMountPath = "/hot-updater";
29
20
  const api = createHotUpdater({
30
21
  database: kyselyAdapter({ db: kysely, provider: "postgresql" }),
31
- clientAccess: { type: "public" },
22
+ plugins: [insights()],
23
+ clientAccess: "public",
32
24
  });
33
25
  const baseUrl = "http://localhost:3000";
34
26
  const server = setupServer();
@@ -46,10 +38,11 @@ const invokeHandler = async (
46
38
  ? hotUpdater.handlers.admin
47
39
  : hotUpdater.handlers.client;
48
40
  const response = await handler(new Request(url, request));
49
- return new HttpResponse(await response.text(), {
50
- status: response.status,
51
- headers: response.headers,
52
- });
41
+ // A 204 carries no body, and a Response refuses one.
42
+ return new HttpResponse(
43
+ response.status === 204 ? null : await response.text(),
44
+ { status: response.status, headers: response.headers },
45
+ );
53
46
  };
54
47
 
55
48
  beforeAll(async () => {
@@ -67,11 +60,13 @@ beforeAll(async () => {
67
60
  });
68
61
 
69
62
  afterEach(async () => {
70
- await db.exec("DELETE FROM bundle_patches");
71
- await db.exec("DELETE FROM release_catalogs");
72
- await db.exec("DELETE FROM releases");
73
- await db.exec("DELETE FROM bundles");
74
- await db.exec("DELETE FROM channels");
63
+ // Every table but the settings rows, aggregates included.
64
+ const { rows } = await db.query<{ tablename: string }>(
65
+ "SELECT tablename FROM pg_tables WHERE schemaname = 'public' AND tablename <> 'private_hot_updater_settings'",
66
+ );
67
+ await db.exec(
68
+ `TRUNCATE ${rows.map(({ tablename }) => `"${tablename}"`).join(", ")} CASCADE`,
69
+ );
75
70
  });
76
71
 
77
72
  afterAll(async () => {
@@ -83,448 +78,245 @@ afterAll(async () => {
83
78
  const createTestBundle = (overrides?: Partial<Bundle>): Bundle => ({
84
79
  id: NIL_UUID,
85
80
  platform: "ios",
86
- fileHash: "test-hash",
87
81
  gitCommitHash: null,
88
- storageUri: "test://storage",
89
- archiveByteSize: 3_000_000_001,
82
+ manifestStorageUri: "test://storage/manifest.json",
83
+ manifestFileHash: "test-manifest-hash",
84
+ assetBaseStorageUri: "test://assets",
90
85
  ...overrides,
91
86
  });
92
87
 
93
- const createStandaloneClient = (base = `${baseUrl}/hot-updater/admin`) =>
94
- createDatabaseClient(standaloneRepository({ baseUrl: base }));
95
-
96
- const commitTestRelease = async (
97
- bundle: Bundle,
98
- channelName = "production",
99
- ) => {
100
- const repository = standaloneRepository({
101
- baseUrl: `${baseUrl}/hot-updater/admin`,
102
- });
103
- const channel = await repository.models.channels.insert({
104
- onConflict: "returnExisting",
105
- row: { id: uuidv7(), name: channelName },
106
- });
107
- const scopeKey = createReleaseCatalogScopeKey({
108
- channelKey: encodeChannelKey(channelName),
109
- platform: bundle.platform,
110
- strategy: "APP_VERSION",
111
- });
112
- const releaseId = uuidv7();
113
- const committed = await commitReleaseCatalogMutation({
114
- companionChanges: [
115
- {
116
- model: "bundles",
117
- operation: "insert",
118
- row: bundleToRow(bundle),
119
- },
120
- ],
121
- database: repository,
122
- mutation: {
123
- operation: "insert",
124
- row: {
125
- bundle_id: bundle.id,
126
- channel_id: channel.row.id,
127
- created_at_ms: 1,
128
- enabled: true,
129
- fingerprint_hash: null,
130
- id: releaseId,
131
- kind: "BUNDLE",
132
- message: null,
133
- operation: "DEPLOY",
134
- platform: bundle.platform,
135
- revision: 1,
136
- rollout_cohort_count: 1_000,
137
- scope_key: scopeKey,
138
- should_force_update: false,
139
- source_release_id: null,
140
- strategy: "APP_VERSION",
141
- target_app_version: "*",
142
- target_cohorts: [],
143
- updated_at_ms: 1,
144
- },
145
- },
146
- scope: {
147
- channelId: channel.row.id,
148
- channelName,
88
+ describe("Standalone core API over admin API protocol 2", () => {
89
+ const core = () =>
90
+ standaloneRepository({ baseUrl: `${baseUrl}/hot-updater/admin` }).core;
91
+ const deployment = (bundle: Bundle, channel = "production") => ({
92
+ bundle,
93
+ release: {
94
+ channel,
95
+ enabled: true,
149
96
  fingerprintHash: null,
150
- platform: bundle.platform,
151
- scopeKey,
152
- strategy: "APP_VERSION",
97
+ message: null,
98
+ shouldForceUpdate: false,
99
+ targetAppVersion: "1.0.0",
153
100
  },
154
101
  });
155
- return { channel: channel.row, committed, releaseId, repository, scopeKey };
156
- };
157
102
 
158
- describe("Handler <-> Standalone Repository Integration", () => {
159
- it("uses Standalone for bundle management and the server database for Insights", async () => {
160
- const bundleId = uuidv7();
161
- const installId = "standalone-install";
162
- await createStandaloneClient().insertBundle(
163
- createTestBundle({ id: bundleId }),
103
+ it("deploys, lists by key, and reads bundles, releases, catalogs, and channels", async () => {
104
+ const remote = core();
105
+ const bundles = [uuidv7(), uuidv7(), uuidv7()].map((id) =>
106
+ createTestBundle({ id }),
164
107
  );
165
-
166
- const ingestion = await api.handlers.client(
167
- new Request(`${baseUrl}/events`, {
168
- method: "POST",
169
- headers: {
170
- "content-type": "application/json",
171
- },
172
- body: JSON.stringify({
173
- appVersion: "1.0.0",
174
- channel: "production",
175
- cohort: "default",
176
- fingerprintHash: null,
177
- fromBundleId: NIL_UUID,
178
- fromReleaseId: null,
179
- installId,
108
+ const deployed = [];
109
+ for (const bundle of bundles) {
110
+ deployed.push(...(await remote.deploy([deployment(bundle)])));
111
+ }
112
+ const scopeKey = deployed[0]!.catalog.scope_key;
113
+ const channel = await remote.findChannelByName("production");
114
+
115
+ expect(deployed.map(({ catalog }) => catalog.generation)).toEqual([
116
+ 1, 2, 3,
117
+ ]);
118
+ expect(channel).toMatchObject({ name: "production" });
119
+ await expect(remote.listChannels()).resolves.toEqual([channel]);
120
+ const first = await remote.listBundles({ limit: 2, order: "desc" });
121
+ const second = await remote.listBundles({
122
+ limit: 2,
123
+ order: "desc",
124
+ after: first.at(-1)!.bundle.id,
125
+ });
126
+ expect([...first, ...second].map(({ bundle }) => bundle.id)).toEqual(
127
+ bundles.map(({ id }) => id).reverse(),
128
+ );
129
+ await expect(remote.countBundles()).resolves.toBe(3);
130
+ await expect(remote.countBundles("android")).resolves.toBe(0);
131
+ await expect(remote.getBundle(bundles[0]!.id)).resolves.toMatchObject({
132
+ bundle: { id: bundles[0]!.id },
133
+ patches: [],
134
+ childCount: 0,
135
+ });
136
+ await expect(remote.getBundle(uuidv7())).resolves.toBeNull();
137
+ await expect(
138
+ remote.listReleases({
139
+ limit: 10,
140
+ order: "asc",
141
+ filter: { kind: "scope", scopeKey, enabled: true },
142
+ }),
143
+ ).resolves.toHaveLength(3);
144
+ await expect(
145
+ remote.listReleases({
146
+ limit: 10,
147
+ filter: {
148
+ kind: "channelPlatform",
149
+ channelId: channel!.id,
180
150
  platform: "ios",
181
- sdkVersion: "2.0.0",
182
- toBundleId: bundleId,
183
- toReleaseId: null,
184
- type: "UPDATE_APPLIED",
185
- updateStrategy: "appVersion",
186
- }),
151
+ enabled: false,
152
+ },
187
153
  }),
188
- );
189
- const overview = await api.handlers.admin(
190
- new Request(`${baseUrl}/installations/overview`),
191
- );
192
-
193
- expect(ingestion.status).toBe(204);
194
- expect(overview.status).toBe(200);
195
- await expect(overview.json()).resolves.toEqual({
196
- bundles: [{ bundleId, installations: 1 }],
197
- trackedInstallations: 1,
154
+ ).resolves.toEqual([]);
155
+ await expect(remote.getReleaseCatalogRow(scopeKey)).resolves.toMatchObject({
156
+ generation: 3,
198
157
  });
158
+ await expect(remote.listReleaseCatalogs({ limit: 10 })).resolves.toEqual([
159
+ expect.objectContaining({ scope_key: scopeKey }),
160
+ ]);
199
161
  });
200
162
 
201
- it("atomically commits and reads Release catalogs through standalone management routes", async () => {
202
- const bundle = createTestBundle({ id: uuidv7() });
203
- const { committed, releaseId, repository, scopeKey } =
204
- await commitTestRelease(bundle);
163
+ it("changes, previews, promotes, and deletes releases, and rebuilds catalogs", async () => {
164
+ const remote = core();
165
+ const [deployed] = await remote.deploy([
166
+ deployment(createTestBundle({ id: uuidv7() })),
167
+ ]);
168
+ const releaseId = deployed!.release!.id;
169
+ const scopeKey = deployed!.catalog.scope_key;
205
170
 
206
171
  await expect(
207
- repository.models.releases.findById(releaseId),
172
+ remote.preflightReleasePolicy({
173
+ releaseId,
174
+ patch: { rolloutCohortCount: 100 },
175
+ }),
208
176
  ).resolves.toMatchObject({
209
- bundle_id: bundle.id,
210
- revision: 1,
177
+ expectedReleaseRevision: 1,
178
+ release: { revision: 2 },
211
179
  });
212
180
  await expect(
213
- repository.models.releaseCatalogs.findByScopeKey(scopeKey),
214
- ).resolves.toMatchObject({ generation: committed.catalog.generation });
215
- const response = await api.handlers.client(
216
- new Request(
217
- `${baseUrl}/release-catalogs/app-version/ios/${encodeChannelKey("production")}/1.0.0`,
218
- ),
219
- );
220
- expect(response.status).toBe(200);
221
- await expect(response.json()).resolves.toMatchObject({
222
- releases: [{ bundleId: bundle.id, releaseId }],
223
- });
224
- });
225
-
226
- it("keeps Standalone management routes separate from API key protection", async () => {
227
- const protectedApi = createHotUpdater({
228
- database: createInMemoryDatabasePlugin(),
229
- clientAccess: { type: "api-key" },
230
- });
231
- server.use(
232
- http.all(`${baseUrl}/protected-hot-updater/*`, async ({ request }) => {
233
- return invokeHandler(protectedApi, request, "/protected-hot-updater");
181
+ remote.updateReleasePolicy({
182
+ releaseId,
183
+ expectedRevision: 1,
184
+ patch: { rolloutCohortCount: 100 },
234
185
  }),
235
- );
236
- const bundleId = uuidv7();
237
- const client = createStandaloneClient(
238
- `${baseUrl}/protected-hot-updater/admin`,
239
- );
240
-
241
- await client.insertBundle(createTestBundle({ id: bundleId }));
242
- const updateCheck = await protectedApi.handlers.client(
243
- new Request(
244
- `${baseUrl}/release-catalogs/app-version/ios/${encodeChannelKey("production")}/1.0.0`,
245
- ),
246
- );
247
-
248
- await expect(client.getBundleById(bundleId)).resolves.toMatchObject({
249
- id: bundleId,
250
- });
251
- expect(updateCheck.status).toBe(401);
252
- });
253
-
254
- it("creates a bundle through handler POST /bundles", async () => {
255
- const bundleId = uuidv7();
256
- const response = await api.handlers.admin(
257
- new Request(`${baseUrl}/bundles`, {
258
- body: JSON.stringify(
259
- createTestBundle({
260
- id: bundleId,
261
- fileHash: "integration-hash-1",
262
- }),
263
- ),
264
- headers: { "Content-Type": "application/json" },
265
- method: "POST",
186
+ ).resolves.toMatchObject({ release: { revision: 2 } });
187
+ await expect(
188
+ remote.updateReleasePolicy({
189
+ releaseId,
190
+ expectedRevision: 1,
191
+ patch: { enabled: false },
266
192
  }),
267
- );
268
-
269
- const retrieved = await api.handlers.admin(
270
- new Request(`${baseUrl}/bundles/${bundleId}`),
271
- );
272
- expect(response.status).toBe(201);
273
- expect(retrieved.status).toBe(200);
274
- await expect(retrieved.json()).resolves.toMatchObject({
275
- id: bundleId,
276
- fileHash: "integration-hash-1",
277
- });
278
- await expect(api.getReleases({ bundleId, limit: 10 })).resolves.toEqual([]);
279
- });
280
-
281
- it("retrieves a bundle through handler GET /bundles/:id", async () => {
282
- const bundleId = uuidv7();
283
- await api.insertBundle(
284
- createTestBundle({ id: bundleId, fileHash: "get-hash-1" }),
285
- );
286
-
287
- const retrieved = await createStandaloneClient().getBundleById(bundleId);
288
-
289
- expect(retrieved).toMatchObject({ id: bundleId, fileHash: "get-hash-1" });
290
- });
291
-
292
- it("refuses to delete an artifact referenced by a Release", async () => {
293
- const bundleId = uuidv7();
294
- const bundle = createTestBundle({ id: bundleId });
295
- await commitTestRelease(bundle);
296
- const client = createStandaloneClient();
297
-
298
- await expect(client.deleteBundleById(bundleId)).rejects.toThrow(
299
- "still referenced",
300
- );
301
-
302
- await expect(api.getBundleById(bundleId)).resolves.toMatchObject({
303
- id: bundleId,
193
+ ).rejects.toMatchObject({
194
+ name: "ReleaseManagementError",
195
+ code: "VERSION_CONFLICT",
304
196
  });
305
- });
306
-
307
- it("lists and filters artifacts through handler GET /bundles", async () => {
308
- await api.insertBundle(createTestBundle({ id: uuidv7() }));
309
- await api.insertBundle(createTestBundle({ id: uuidv7() }));
310
- await api.insertBundle(createTestBundle({ id: uuidv7() }));
311
- const client = createStandaloneClient();
312
-
313
- const all = await client.getBundles({ limit: 50 });
314
- const ios = await client.getBundles({
315
- where: { platform: "ios" },
316
- limit: 50,
317
- });
318
-
319
- expect(all.data).toHaveLength(3);
320
- expect(all.pagination.total).toBe(3);
321
- expect(ios.data).toHaveLength(3);
322
- });
323
-
324
- it("lists Channel rows through handler GET /channels", async () => {
325
- await api.insertChannel({
326
- onConflict: "returnExisting",
327
- row: { id: uuidv7(), name: "production" },
197
+ const promoted = await remote.promoteRelease({
198
+ releaseId,
199
+ targetChannel: "beta",
200
+ action: "move",
328
201
  });
329
- await api.insertChannel({
330
- onConflict: "returnExisting",
331
- row: { id: uuidv7(), name: "beta" },
202
+ expect(promoted.source!.release).toMatchObject({ enabled: false });
203
+ expect(promoted.target.release).toMatchObject({
204
+ operation: "PROMOTE",
205
+ source_release_id: releaseId,
332
206
  });
333
-
334
- const channels = await createStandaloneClient().getChannels();
335
-
336
- expect(channels.map(({ name }) => name)).toEqual(
337
- expect.arrayContaining(["production", "beta"]),
338
- );
339
- expect(channels.every(({ id }) => id.length > 0)).toBe(true);
340
- expect(new Set(channels.map(({ name }) => name)).size).toBe(
341
- channels.length,
207
+ await expect(
208
+ remote.preflightReleaseCatalogRebuild(scopeKey),
209
+ ).resolves.toMatchObject({ changed: false });
210
+ await expect(remote.rebuildReleaseCatalog(scopeKey)).resolves.toMatchObject(
211
+ { changed: false },
342
212
  );
213
+ await expect(remote.deleteRelease({ releaseId })).resolves.toMatchObject({
214
+ release: null,
215
+ });
216
+ await expect(remote.getRelease(releaseId)).resolves.toBeNull();
343
217
  });
344
218
 
345
- it("creates and deletes an empty Channel through the canonical routes", async () => {
346
- const client = createStandaloneClient();
347
- const id = uuidv7();
219
+ it("creates and deletes channels, replaces patches, and refuses to delete a bundle a release uses", async () => {
220
+ const remote = core();
221
+ const base = createTestBundle({ id: uuidv7() });
222
+ const target = createTestBundle({ id: uuidv7() });
223
+ await remote.deploy([deployment(base)]);
224
+ await remote.deploy([deployment(target)]);
225
+ const qa = await remote.ensureChannel("qa");
348
226
 
349
- await expect(
350
- client.insertChannel({
351
- row: { id, name: "preview" },
352
- onConflict: "returnExisting",
353
- }),
354
- ).resolves.toEqual({
355
- row: { id, name: "preview" },
356
- inserted: true,
357
- });
358
- await expect(client.deleteChannel({ id })).resolves.toEqual({
227
+ await expect(remote.ensureChannel("qa")).resolves.toEqual(qa);
228
+ await expect(remote.deleteChannel(qa.id)).resolves.toEqual({
359
229
  deleted: true,
360
230
  });
361
- await expect(client.deleteChannel({ id })).resolves.toEqual({
362
- deleted: false,
363
- reason: "not_found",
364
- });
365
- });
366
-
367
- it("refuses to delete a Channel referenced by a Release", async () => {
368
- const client = createStandaloneClient();
369
- const bundleId = uuidv7();
370
- await commitTestRelease(createTestBundle({ id: bundleId }), "preview");
371
- const channel = (await client.getChannels()).find(
372
- ({ name }) => name === "preview",
373
- );
374
-
375
- expect(channel).toBeDefined();
376
- if (!channel) throw new Error("preview Channel was not created");
377
- await expect(client.deleteChannel({ id: channel.id })).resolves.toEqual({
231
+ const production = await remote.findChannelByName("production");
232
+ await expect(remote.deleteChannel(production!.id)).resolves.toEqual({
378
233
  deleted: false,
379
234
  reason: "not_empty",
380
235
  });
381
- await expect(client.getBundleById(bundleId)).resolves.toMatchObject({
382
- id: bundleId,
383
- });
384
- });
385
-
386
- it("updates an artifact without changing its Release or catalog", async () => {
387
- const bundle = createTestBundle({ id: uuidv7() });
388
- const { releaseId, repository, scopeKey } = await commitTestRelease(bundle);
389
- const releaseBefore = await repository.models.releases.findById(releaseId);
390
- const catalogBefore =
391
- await repository.models.releaseCatalogs.findByScopeKey(scopeKey);
392
-
393
- await createStandaloneClient().updateBundleById(bundle.id, {
394
- storageUri: "test://updated-artifact",
395
- });
396
-
397
- await expect(api.getBundleById(bundle.id)).resolves.toMatchObject({
398
- storageUri: "test://updated-artifact",
236
+ await remote.updateBundle(target.id, {
237
+ patches: [
238
+ {
239
+ baseBundleId: base.id,
240
+ baseFileHash: "base-hash",
241
+ byteSize: 10,
242
+ patchFileHash: "patch-hash",
243
+ patchStorageUri: "test://patches/1.patch",
244
+ },
245
+ ],
399
246
  });
400
247
  await expect(
401
- repository.models.releases.findById(releaseId),
402
- ).resolves.toEqual(releaseBefore);
248
+ remote.listPatchesFromBase(base.id, { limit: 10 }),
249
+ ).resolves.toEqual([expect.objectContaining({ bundle_id: target.id })]);
403
250
  await expect(
404
- repository.models.releaseCatalogs.findByScopeKey(scopeKey),
405
- ).resolves.toEqual(catalogBefore);
406
- });
407
-
408
- it("creates, retrieves, updates, and deletes through existing routes", async () => {
409
- const client = createStandaloneClient();
410
- const bundleId = uuidv7();
411
- await client.insertBundle(
412
- createTestBundle({ id: bundleId, fileHash: "e2e-hash" }),
413
- );
414
-
415
- await expect(client.getBundleById(bundleId)).resolves.toMatchObject({
416
- fileHash: "e2e-hash",
251
+ remote.countBundleChildren([base.id, target.id]),
252
+ ).resolves.toEqual({ [base.id]: 1, [target.id]: 0 });
253
+ await expect(remote.deleteBundles([base.id])).rejects.toMatchObject({
254
+ status: 409,
417
255
  });
418
- await client.updateBundleById(bundleId, { storageUri: "test://updated" });
419
- await expect(client.getBundleById(bundleId)).resolves.toMatchObject({
420
- storageUri: "test://updated",
421
- });
422
- await client.deleteBundleById(bundleId);
423
- await expect(client.getBundleById(bundleId)).resolves.toBeNull();
424
256
  });
425
257
 
426
- it("creates multiple artifacts atomically through POST /bundles", async () => {
427
- const ids = [uuidv7(), uuidv7(), uuidv7()];
428
-
429
- const response = await api.handlers.admin(
430
- new Request(`${baseUrl}/bundles`, {
431
- body: JSON.stringify(ids.map((id) => createTestBundle({ id }))),
432
- headers: { "Content-Type": "application/json" },
433
- method: "POST",
434
- }),
435
- );
436
-
437
- expect(response.status).toBe(201);
438
- for (const id of ids) {
439
- await expect(api.getBundleById(id)).resolves.toMatchObject({ id });
440
- }
441
- });
442
-
443
- it("rolls back the entire create batch when one bundle cannot be inserted", async () => {
444
- const existingId = uuidv7();
445
- const newId = uuidv7();
446
- await api.insertBundle(createTestBundle({ id: existingId }));
447
-
448
- const response = await api.handlers.admin(
449
- new Request(`${baseUrl}/bundles`, {
450
- method: "POST",
451
- headers: { "Content-Type": "application/json" },
452
- body: JSON.stringify([
453
- createTestBundle({ id: newId }),
454
- createTestBundle({ id: existingId }),
455
- ]),
456
- }),
457
- );
458
-
459
- expect(response.status).toBe(500);
460
- await expect(api.getBundleById(newId)).resolves.toBeNull();
461
- });
462
-
463
- it("works when handlers are mounted under a custom client path", async () => {
464
- const customApi = createHotUpdater({
465
- database: kyselyAdapter({ db: kysely, provider: "postgresql" }),
466
- clientAccess: { type: "public" },
467
- });
258
+ it("fails fast on a server that predates protocol 2, and refuses release filters no index serves", async () => {
468
259
  server.use(
469
- http.all(`${baseUrl}/api/v2/*`, async ({ request }) => {
470
- return invokeHandler(customApi, request, "/api/v2");
471
- }),
260
+ http.get(`${baseUrl}/old-server/version`, () =>
261
+ HttpResponse.json({ error: "Not found" }, { status: 404 }),
262
+ ),
472
263
  );
473
- const bundleId = uuidv7();
474
- const client = createStandaloneClient(`${baseUrl}/api/v2/admin`);
264
+ await expect(
265
+ standaloneRepository({
266
+ baseUrl: `${baseUrl}/old-server`,
267
+ }).core.listChannels(),
268
+ ).rejects.toThrow("speaks admin API protocol 1, and this CLI needs 2");
475
269
 
476
- await client.insertBundle(
477
- createTestBundle({ id: bundleId, fileHash: "custom-hash" }),
270
+ const response = await fetch(
271
+ `${baseUrl}/hot-updater/admin/releases?v=2&channelId=x`,
478
272
  );
479
-
480
- await expect(client.getBundleById(bundleId)).resolves.toMatchObject({
481
- fileHash: "custom-hash",
482
- });
273
+ expect(response.status).toBe(400);
483
274
  });
275
+ });
484
276
 
485
- it("returns null when the bundle endpoint returns 404", async () => {
486
- await expect(
487
- createStandaloneClient().getBundleById(uuidv7()),
488
- ).resolves.toBeNull();
277
+ describe("Insights through a self-hosted server's admin API", () => {
278
+ const offPath = "/no-insights";
279
+ const withoutInsights = createHotUpdater({
280
+ database: kyselyAdapter({ db: kysely, provider: "postgresql" }),
281
+ plugins: [],
282
+ clientAccess: "public",
489
283
  });
490
284
 
491
- it("updates a bundle without creating a duplicate row", async () => {
492
- const memoryApi = createHotUpdater({
493
- database: createInMemoryDatabasePlugin(),
494
- clientAccess: { type: "public" },
495
- });
285
+ beforeAll(() => {
496
286
  server.use(
497
- http.all(`${baseUrl}/memory-hot-updater/*`, async ({ request }) => {
498
- return invokeHandler(memoryApi, request, "/memory-hot-updater");
499
- }),
500
- );
501
- const client = createStandaloneClient(
502
- `${baseUrl}/memory-hot-updater/admin`,
503
- );
504
- const bundleId = uuidv7();
505
- await client.insertBundle(
506
- createTestBundle({
507
- id: bundleId,
508
- storageUri: "s3://test-bucket/original.zip",
509
- }),
287
+ http.all(`${baseUrl}${offPath}/*`, ({ request }) =>
288
+ invokeHandler(withoutInsights, request, offPath),
289
+ ),
510
290
  );
291
+ });
511
292
 
512
- await client.updateBundleById(bundleId, {
513
- storageUri: "s3://test-bucket/updated.zip",
293
+ it("reads events with the repository's headers, and a server without insights() says it is off", async () => {
294
+ const on = standaloneRepository({
295
+ baseUrl: `${baseUrl}/hot-updater/admin`,
514
296
  });
297
+ const onResponse = await on.fetchAdmin("/events?limit=1");
298
+ expect(onResponse.status).toBe(200);
299
+ await expect(onResponse.json()).resolves.toMatchObject({ data: [] });
300
+
301
+ const off = standaloneRepository({ baseUrl: `${baseUrl}${offPath}/admin` });
302
+ const offResponse = await off.fetchAdmin("/events?limit=1");
303
+ expect(offResponse.status).toBe(204);
304
+ expect(offResponse.headers.get("x-hot-updater-insights")).toBe("disabled");
305
+ });
515
306
 
516
- await expect(client.getBundleById(bundleId)).resolves.toMatchObject({
517
- id: bundleId,
518
- storageUri: "s3://test-bucket/updated.zip",
519
- });
520
- await expect(client.getBundles({ limit: 50 })).resolves.toMatchObject({
521
- data: [
522
- expect.objectContaining({
523
- id: bundleId,
524
- storageUri: "s3://test-bucket/updated.zip",
525
- }),
526
- ],
527
- pagination: { total: 1 },
528
- });
307
+ it("lists the plugins each server runs on its admin /version, as the console reads them", async () => {
308
+ const plugins = async (adminUrl: string) => {
309
+ const response = await standaloneRepository({
310
+ baseUrl: adminUrl,
311
+ }).fetchAdmin("/version");
312
+ return ((await response.json()) as { plugins: unknown }).plugins;
313
+ };
314
+
315
+ await expect(plugins(`${baseUrl}/hot-updater/admin`)).resolves.toEqual([
316
+ "insights",
317
+ ]);
318
+ await expect(plugins(`${baseUrl}${offPath}/admin`)).resolves.toEqual([]);
319
+ const client = await fetch(`${baseUrl}/hot-updater/version`);
320
+ expect(await client.json()).not.toHaveProperty("plugins");
529
321
  });
530
322
  });