@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
package/src/db/index.ts CHANGED
@@ -2,12 +2,30 @@ import {
2
2
  getHotUpdaterCoreMetadata,
3
3
  type RuntimeHotUpdaterAPI,
4
4
  } from "../createHotUpdaterCore";
5
- import { generateSchemaFromHotUpdaterSchema } from "./schemaGenerators";
6
5
  import { type Migrator, type SchemaGenerator } from "./types";
7
6
 
7
+ export { createDatabaseCoreApi, type CoreApi } from "../core/api";
8
+ export {
9
+ createDatabasePluginApis,
10
+ createMeasuredDatabase,
11
+ type MeasuredDatabase,
12
+ type MeasuredDatabaseOptions,
13
+ } from "../assembly/databasePlugins";
14
+ export { targetBaseCandidateKey } from "../core/baseCandidates";
8
15
  export * from "./createBundleDiff";
9
- export type { Migrator, SchemaGenerator } from "./types";
10
- export { HotUpdaterSchemaMigrationRequiredError } from "./schemaReadiness";
16
+ export {
17
+ generateEngineSql,
18
+ settingsStatements,
19
+ type EngineSqlOptions,
20
+ } from "./engineSql";
21
+ export type {
22
+ DatabaseTooling,
23
+ Migrator,
24
+ SchemaGenerator,
25
+ ToolingDatabase,
26
+ ToolingTarget,
27
+ } from "./types";
28
+ export { HotUpdaterSchemaMigrationRequiredError } from "../database/fence";
11
29
  export { HOT_UPDATER_SERVER_VERSION } from "../version";
12
30
 
13
31
  export type HotUpdaterDBTarget = {
@@ -26,22 +44,33 @@ const getDBMetadata = (hotUpdater: HotUpdaterDBTarget) => {
26
44
  return metadata;
27
45
  };
28
46
 
47
+ /** Whether `hot-updater db generate` writes schema files for a server's database. */
48
+ export const generatesSchema = (hotUpdater: HotUpdaterDBTarget): boolean =>
49
+ getDBMetadata(hotUpdater).database.generateSchema !== undefined;
50
+
51
+ /** The migrator for a server's database: its built-in tables and its plugins' tables. */
29
52
  export function createMigrator(hotUpdater: HotUpdaterDBTarget): Migrator {
30
- const { adapterCapabilities, core } = getDBMetadata(hotUpdater);
31
- return (adapterCapabilities.createMigrator ?? core.createMigrator)();
53
+ const { database, target } = getDBMetadata(hotUpdater);
54
+ if (database.createMigrator === undefined) {
55
+ throw new Error(
56
+ database.generateSchema === undefined
57
+ ? `The ${database.name} database has no migrator; its provider applies the schema.`
58
+ : `The ${database.name} database applies its schema from migration files: run \`hot-updater db generate\`, then apply the file with the provider's tooling.`,
59
+ );
60
+ }
61
+ return database.createMigrator(target);
32
62
  }
33
63
 
34
64
  export function generateSchema(
35
65
  hotUpdater: HotUpdaterDBTarget,
36
66
  ...args: Parameters<SchemaGenerator>
37
67
  ): ReturnType<SchemaGenerator> {
38
- const { adapterCapabilities, core } = getDBMetadata(hotUpdater);
39
- const schemaGenerator =
40
- adapterCapabilities.generateSchema ?? core.generateSchema;
41
- return generateSchemaFromHotUpdaterSchema(
42
- hotUpdater.adapterName,
43
- adapterCapabilities.provider,
44
- args[0],
45
- schemaGenerator(...args),
46
- );
68
+ const { database, target } = getDBMetadata(hotUpdater);
69
+ if (database.generateSchema === undefined) {
70
+ throw new Error(
71
+ `The ${database.name} database has no schema generator; run \`hot-updater db migrate\` instead.`,
72
+ );
73
+ }
74
+ const [version, name] = args;
75
+ return database.generateSchema(version, name, target);
47
76
  }
@@ -0,0 +1,218 @@
1
+ import { PGlite } from "@electric-sql/pglite";
2
+ import { createMemoryAdapter } from "@hot-updater/plugin-core/internal";
3
+ import { Kysely } from "kysely";
4
+ import { PGliteDialect } from "kysely-pglite-dialect";
5
+ import { describe, expect, it, vi } from "vitest";
6
+
7
+ import { drizzleAdapter } from "../adapters/drizzle";
8
+ import { kyselyAdapter } from "../adapters/kysely";
9
+ import { prismaAdapter } from "../adapters/prisma";
10
+ import { createHotUpdater } from "../createHotUpdaterCore";
11
+ import { HotUpdaterSchemaMigrationRequiredError } from "../database/fence";
12
+ import { defineTable } from "../database/schema";
13
+ import { definePlugin } from "../plugins/definePlugin";
14
+ import { insights } from "../plugins/insights";
15
+ import {
16
+ builtInTarget,
17
+ createEngineDatabase,
18
+ toolingTargetOf,
19
+ } from "./builtInDatabase";
20
+ import { createMigrator, generateSchema } from "./index";
21
+
22
+ const notes = definePlugin({
23
+ id: "notes",
24
+ schemaVersion: "2",
25
+ schema: {
26
+ notes: defineTable(
27
+ {
28
+ id: { type: "string" },
29
+ author: { type: "string" },
30
+ text: { type: "string" },
31
+ },
32
+ { key: ["id"], indexes: { byAuthor: { eq: ["author"], sort: ["id"] } } },
33
+ ),
34
+ },
35
+ init: ({ db }) => ({
36
+ api: {
37
+ add: (id: string, author: string, text: string) =>
38
+ db.transaction(async (tx) => {
39
+ tx.create("notes", { id, author, text });
40
+ }),
41
+ read: (id: string) => db.findOne("notes", { id }),
42
+ },
43
+ }),
44
+ });
45
+
46
+ /** `hot-updater db migrate`: the migrator for the server's tables, run. */
47
+ const migrate = async (hotUpdater: { readonly adapterName: string }) => {
48
+ const result = await createMigrator(hotUpdater).migrateToLatest({
49
+ mode: "from-schema",
50
+ updateSettings: true,
51
+ });
52
+ await result.execute();
53
+ return result;
54
+ };
55
+
56
+ const memoryDatabase = () =>
57
+ createEngineDatabase({ name: "memory", adapter: createMemoryAdapter() });
58
+
59
+ describe("third-party plugin tables in db tooling", () => {
60
+ it("adds each third-party plugin's namespaced tables and settings row to the built-in ones", () => {
61
+ expect(toolingTargetOf([insights()])).toBe(builtInTarget);
62
+ const target = toolingTargetOf([insights(), notes]);
63
+ expect(target.schema.tables.map(({ name }) => name)).toEqual([
64
+ ...builtInTarget.schema.tables.map(({ name }) => name),
65
+ "notes_notes",
66
+ ]);
67
+ expect(target.settings).toEqual({
68
+ ...builtInTarget.settings,
69
+ "schema.notes": "2",
70
+ });
71
+ });
72
+
73
+ it("migrates an engine database's built-in and plugin tables with its adapter", async () => {
74
+ const hotUpdater = createHotUpdater({
75
+ database: memoryDatabase(),
76
+ plugins: [notes],
77
+ clientAccess: "public",
78
+ });
79
+ await expect(hotUpdater.api.notes.read("n1")).rejects.toBeInstanceOf(
80
+ HotUpdaterSchemaMigrationRequiredError,
81
+ );
82
+
83
+ const result = await migrate(hotUpdater);
84
+ expect(result.operations).toContainEqual(
85
+ expect.objectContaining({
86
+ type: "create-table",
87
+ value: expect.objectContaining({ ormName: "notes_notes" }),
88
+ }),
89
+ );
90
+ await hotUpdater.api.notes.add("n1", "ada", "hello");
91
+ await expect(hotUpdater.api.notes.read("n1")).resolves.toEqual({
92
+ id: "n1",
93
+ author: "ada",
94
+ text: "hello",
95
+ });
96
+ await expect(hotUpdater.core.listChannels()).resolves.toEqual([]);
97
+ });
98
+
99
+ it("answers 503 until the plugin's settings row exists, then serves it", async () => {
100
+ const database = memoryDatabase();
101
+ await migrate(createHotUpdater({ database, clientAccess: "public" }));
102
+ const hotUpdater = createHotUpdater({
103
+ database,
104
+ plugins: [notes],
105
+ clientAccess: "public",
106
+ });
107
+ await expect(hotUpdater.api.notes.read("n1")).rejects.toThrow(
108
+ 'Hot Updater schema setting "schema.notes" for memory is missing; expected "2". Run `hot-updater db migrate`.',
109
+ );
110
+ await expect(hotUpdater.core.listChannels()).rejects.toBeInstanceOf(
111
+ HotUpdaterSchemaMigrationRequiredError,
112
+ );
113
+ const channels = () =>
114
+ hotUpdater.handlers.admin(new Request("http://localhost/channels"));
115
+ const error = vi.spyOn(console, "error").mockImplementation(() => {});
116
+ expect((await channels()).status).toBe(503);
117
+ error.mockRestore();
118
+
119
+ await migrate(hotUpdater);
120
+ expect((await channels()).status).toBe(200);
121
+ await expect(hotUpdater.api.notes.read("n1")).resolves.toBeNull();
122
+ // The server without the plugin was already served.
123
+ await expect(
124
+ createHotUpdater({
125
+ database,
126
+ clientAccess: "public",
127
+ }).core.listChannels(),
128
+ ).resolves.toEqual([]);
129
+ });
130
+
131
+ it("writes a Kysely migration of the plugin's tables and settings row", async () => {
132
+ const db = new PGlite();
133
+ const kysely = new Kysely<object>({ dialect: new PGliteDialect(db) });
134
+ try {
135
+ const hotUpdater = createHotUpdater({
136
+ database: kyselyAdapter({ db: kysely, provider: "postgresql" }),
137
+ plugins: [notes],
138
+ clientAccess: "public",
139
+ });
140
+ const result = await createMigrator(hotUpdater).migrateToLatest({
141
+ mode: "from-schema",
142
+ updateSettings: true,
143
+ });
144
+ const sql = result.getSQL?.() ?? "";
145
+ expect(sql).toContain('CREATE TABLE IF NOT EXISTS "notes_notes"');
146
+ expect(sql).toContain(
147
+ 'CREATE INDEX IF NOT EXISTS "notes_notes_byAuthor" ON "notes_notes" ("author", "id")',
148
+ );
149
+ expect(sql).toContain("VALUES ('schema.notes', '2', 0)");
150
+
151
+ await result.execute();
152
+ await hotUpdater.api.notes.add("n1", "ada", "hello");
153
+ await expect(hotUpdater.api.notes.read("n1")).resolves.toMatchObject({
154
+ text: "hello",
155
+ });
156
+ } finally {
157
+ await kysely.destroy();
158
+ await db.close();
159
+ }
160
+ });
161
+
162
+ it("generates Drizzle and Prisma schemas with the plugin's tables", () => {
163
+ const generated = (
164
+ database: Parameters<typeof createHotUpdater>[0]["database"],
165
+ ) =>
166
+ generateSchema(
167
+ createHotUpdater({
168
+ database,
169
+ plugins: [notes],
170
+ clientAccess: "public",
171
+ }),
172
+ "latest",
173
+ ).code;
174
+
175
+ expect(
176
+ generated(drizzleAdapter({ db: {}, provider: "postgresql" })),
177
+ ).toContain('pgTable("notes_notes"');
178
+ expect(
179
+ generated(prismaAdapter({ prisma: {}, provider: "postgresql" })),
180
+ ).toContain("model notes_notes {");
181
+ });
182
+
183
+ it("rejects a third-party plugin that takes a built-in plugin's id or table", () => {
184
+ const named = definePlugin({
185
+ id: "insights",
186
+ schemaVersion: "1",
187
+ schema: {
188
+ notes: defineTable({ id: { type: "string" } }, { key: ["id"] }),
189
+ },
190
+ init: () => ({ api: {} }),
191
+ });
192
+ const tabled = definePlugin({
193
+ id: "api",
194
+ schemaVersion: "1",
195
+ schema: {
196
+ keys: defineTable({ id: { type: "string" } }, { key: ["id"] }),
197
+ },
198
+ init: () => ({ api: {} }),
199
+ });
200
+
201
+ expect(() =>
202
+ createHotUpdater({
203
+ database: memoryDatabase(),
204
+ plugins: [named],
205
+ clientAccess: "public",
206
+ }),
207
+ ).toThrow(`Plugin "insights" uses a built-in plugin's id.`);
208
+ expect(() =>
209
+ createHotUpdater({
210
+ database: memoryDatabase(),
211
+ plugins: [tabled],
212
+ clientAccess: "public",
213
+ }),
214
+ ).toThrow(
215
+ 'api.keys: table "api_keys" is also declared by apiKeys.api_keys',
216
+ );
217
+ });
218
+ });
@@ -1,24 +1,16 @@
1
1
  import {
2
2
  createReleaseCatalogScopeKey,
3
- NIL_UUID,
4
3
  RELEASE_CATALOG_FALLBACK_POLICY,
5
4
  RELEASE_CATALOG_SCHEMA_VERSION,
6
- type ArtifactInfo,
7
5
  type ReleaseCatalog,
8
6
  } from "@hot-updater/core";
9
7
  import {
10
- createDatabaseClient,
11
8
  projectCompiledCatalog,
12
9
  projectCompiledRollbackCatalog,
13
10
  type CompiledReleaseCatalog,
14
- type DatabasePlugin,
11
+ type ReleaseCatalogRow,
15
12
  } from "@hot-updater/plugin-core";
16
13
 
17
- import { resolveManifestArtifacts } from "./updateArtifacts";
18
-
19
- type ResolveFileUrl = (storageUri: string | null) => Promise<string | null>;
20
- type ReadStorageText = (storageUri: string) => Promise<string | null>;
21
-
22
14
  export type ReleaseCatalogRequest =
23
15
  | {
24
16
  readonly strategy: "APP_VERSION";
@@ -33,7 +25,8 @@ export type ReleaseCatalogRequest =
33
25
  readonly fingerprintHash: string;
34
26
  };
35
27
 
36
- const parseCompiledCatalog = (
28
+ /** A stored catalog payload, checked against its strategy's shape. */
29
+ export const parseCompiledCatalog = (
37
30
  payload: string,
38
31
  strategy: ReleaseCatalogRequest["strategy"],
39
32
  ): CompiledReleaseCatalog => {
@@ -63,86 +56,51 @@ const parseCompiledCatalog = (
63
56
  return parsed as CompiledReleaseCatalog;
64
57
  };
65
58
 
66
- export const createReleaseCatalogReader =
67
- (database: DatabasePlugin) =>
68
- async (input: ReleaseCatalogRequest): Promise<ReleaseCatalog | null> => {
69
- const scopeKey =
70
- input.strategy === "APP_VERSION"
71
- ? createReleaseCatalogScopeKey({
72
- channelKey: input.channelKey,
73
- platform: input.platform,
74
- strategy: "APP_VERSION",
75
- })
76
- : createReleaseCatalogScopeKey({
77
- channelKey: input.channelKey,
78
- fingerprintHash: input.fingerprintHash,
79
- platform: input.platform,
80
- strategy: "FINGERPRINT",
81
- });
82
- const row = await database.models.releaseCatalogs.findByScopeKey(scopeKey);
83
- if (
84
- row === null ||
85
- row.platform !== input.platform ||
86
- row.strategy !== input.strategy ||
87
- row.channel_key !== input.channelKey ||
88
- row.scope_key !== scopeKey ||
89
- row.fingerprint_hash !==
90
- (input.strategy === "FINGERPRINT" ? input.fingerprintHash : null)
91
- ) {
92
- return null;
93
- }
94
- const compiled = parseCompiledCatalog(row.payload, input.strategy);
95
- const releases = projectCompiledCatalog(
96
- compiled,
97
- input.strategy === "APP_VERSION" ? input.appVersion : undefined,
98
- );
99
- const rollbackReleases = projectCompiledRollbackCatalog(
100
- compiled,
101
- input.strategy === "APP_VERSION" ? input.appVersion : undefined,
102
- );
103
- return {
104
- catalogId: row.catalog_id,
105
- catalogHash: row.catalog_hash,
106
- fallbackPolicy: RELEASE_CATALOG_FALLBACK_POLICY,
107
- generation: row.generation,
108
- releases,
109
- rollbackReleases,
110
- schemaVersion: RELEASE_CATALOG_SCHEMA_VERSION,
111
- scopeKey,
112
- };
113
- };
114
-
115
- export const createArtifactResolver = (input: {
116
- readonly database: DatabasePlugin;
117
- readonly readStorageText?: ReadStorageText;
118
- readonly resolveFileUrl: ResolveFileUrl;
119
- }) => {
120
- const databaseClient = createDatabaseClient(input.database);
59
+ /** The scope key an update check reads. */
60
+ export const releaseCatalogScopeKeyOf = (
61
+ input: ReleaseCatalogRequest,
62
+ ): string =>
63
+ input.strategy === "APP_VERSION"
64
+ ? createReleaseCatalogScopeKey({
65
+ channelKey: input.channelKey,
66
+ platform: input.platform,
67
+ strategy: "APP_VERSION",
68
+ })
69
+ : createReleaseCatalogScopeKey({
70
+ channelKey: input.channelKey,
71
+ fingerprintHash: input.fingerprintHash,
72
+ platform: input.platform,
73
+ strategy: "FINGERPRINT",
74
+ });
121
75
 
122
- return async (
123
- targetBundleId: string,
124
- currentBundleId: string,
125
- ): Promise<ArtifactInfo | null> => {
126
- const [targetBundle, currentBundle] = await Promise.all([
127
- databaseClient.getBundleById(targetBundleId),
128
- currentBundleId === NIL_UUID
129
- ? null
130
- : databaseClient.getBundleById(currentBundleId),
131
- ]);
132
- if (targetBundle === null) return null;
133
- const fileUrl = await input.resolveFileUrl(targetBundle.storageUri);
134
- const base: ArtifactInfo = {
135
- fileHash: targetBundle.fileHash,
136
- fileUrl,
137
- };
138
- if (input.readStorageText === undefined) return base;
139
- const manifest = await resolveManifestArtifacts({
140
- archiveUrlUsable: fileUrl !== null,
141
- currentBundle,
142
- readStorageText: input.readStorageText,
143
- resolveFileUrl: input.resolveFileUrl,
144
- targetBundle,
145
- });
146
- return manifest === null ? base : { ...base, ...manifest };
76
+ /** Projects a stored catalog row for one update check; null when the row is not this scope's. */
77
+ export const projectReleaseCatalogRow = (
78
+ row: ReleaseCatalogRow | null,
79
+ input: ReleaseCatalogRequest,
80
+ scopeKey = releaseCatalogScopeKeyOf(input),
81
+ ): ReleaseCatalog | null => {
82
+ if (
83
+ row === null ||
84
+ row.platform !== input.platform ||
85
+ row.strategy !== input.strategy ||
86
+ row.channel_key !== input.channelKey ||
87
+ row.scope_key !== scopeKey ||
88
+ row.fingerprint_hash !==
89
+ (input.strategy === "FINGERPRINT" ? input.fingerprintHash : null)
90
+ ) {
91
+ return null;
92
+ }
93
+ const compiled = parseCompiledCatalog(row.payload, input.strategy);
94
+ const appVersion =
95
+ input.strategy === "APP_VERSION" ? input.appVersion : undefined;
96
+ return {
97
+ catalogId: row.catalog_id,
98
+ catalogHash: row.catalog_hash,
99
+ fallbackPolicy: RELEASE_CATALOG_FALLBACK_POLICY,
100
+ generation: row.generation,
101
+ releases: projectCompiledCatalog(compiled, appVersion),
102
+ rollbackReleases: projectCompiledRollbackCatalog(compiled, appVersion),
103
+ schemaVersion: RELEASE_CATALOG_SCHEMA_VERSION,
104
+ scopeKey,
147
105
  };
148
106
  };
@@ -0,0 +1,104 @@
1
+ import type {
2
+ DatabaseAdapter,
3
+ PhysicalTable,
4
+ StoredRow,
5
+ WriteOp,
6
+ } from "@hot-updater/plugin-core/internal";
7
+
8
+ import {
9
+ ENGINE_SCHEMA_KEY,
10
+ ENGINE_SCHEMA_VERSION,
11
+ HotUpdaterSchemaMigrationRequiredError,
12
+ isMissingSchemaError,
13
+ readSettings,
14
+ SETTINGS_TABLE,
15
+ type SchemaSettings,
16
+ } from "../database/fence";
17
+
18
+ /** Refuses a database with `schema.core` but no `schema.engine`: it predates the engine. */
19
+ const assertEngineDatabase = (
20
+ adapterName: string,
21
+ stored: ReadonlyMap<string, StoredRow | null>,
22
+ ) => {
23
+ if (stored.get("schema.core") && !stored.get(ENGINE_SCHEMA_KEY)) {
24
+ throw new HotUpdaterSchemaMigrationRequiredError(
25
+ adapterName,
26
+ String(stored.get("schema.core")!.value),
27
+ { key: ENGINE_SCHEMA_KEY, expected: ENGINE_SCHEMA_VERSION, found: null },
28
+ );
29
+ }
30
+ };
31
+
32
+ const storedSettings = async (
33
+ adapter: DatabaseAdapter,
34
+ keys: readonly string[],
35
+ ) => {
36
+ const rows = await readSettings(adapter, keys);
37
+ return new Map(keys.map((key, position) => [key, rows[position] ?? null]));
38
+ };
39
+
40
+ /** Writes the settings rows after every table exists, refusing a pre-engine database. */
41
+ export const writeSchemaSettings = async (
42
+ adapter: DatabaseAdapter,
43
+ adapterName: string,
44
+ settings: SchemaSettings,
45
+ ): Promise<void> => {
46
+ const stored = await storedSettings(adapter, [
47
+ ...new Set(["schema.core", ENGINE_SCHEMA_KEY, ...Object.keys(settings)]),
48
+ ]);
49
+ assertEngineDatabase(adapterName, stored);
50
+ const ops = Object.entries(settings).flatMap(([key, value]): WriteOp[] => {
51
+ const row = stored.get(key);
52
+ if (row === null || row === undefined) {
53
+ return [
54
+ { type: "insert", table: SETTINGS_TABLE, row: { key, value, _v: 0 } },
55
+ ];
56
+ }
57
+ if (row.value === value) return [];
58
+ return [
59
+ {
60
+ type: "patch",
61
+ table: SETTINGS_TABLE,
62
+ key: [key],
63
+ set: { value },
64
+ guard: { v: Number(row._v) },
65
+ previous: row,
66
+ },
67
+ ];
68
+ });
69
+ if (ops.length === 0) return;
70
+ const result = await adapter.write(ops);
71
+ if (!result.ok) {
72
+ throw new Error(
73
+ "The schema settings changed during the migration; run it again.",
74
+ );
75
+ }
76
+ };
77
+
78
+ /**
79
+ * Creates or updates every table, then writes the settings rows last. A
80
+ * pre-engine database is refused before any table changes.
81
+ */
82
+ export const migrateSchema = async (
83
+ adapter: DatabaseAdapter,
84
+ adapterName: string,
85
+ tables: readonly PhysicalTable[],
86
+ settings: SchemaSettings,
87
+ ): Promise<void> => {
88
+ if (adapter.migrations === undefined) {
89
+ throw new Error(
90
+ `${adapterName} creates its tables with its own tooling; write only the settings rows.`,
91
+ );
92
+ }
93
+ // A database without the settings table is empty, so nothing is refused.
94
+ const stored = await storedSettings(adapter, [
95
+ "schema.core",
96
+ ENGINE_SCHEMA_KEY,
97
+ ]).catch((error: unknown) => {
98
+ if (!isMissingSchemaError(error)) throw error;
99
+ return new Map<string, StoredRow | null>();
100
+ });
101
+ assertEngineDatabase(adapterName, stored);
102
+ await adapter.migrations.apply([...tables, SETTINGS_TABLE]);
103
+ await writeSchemaSettings(adapter, adapterName, settings);
104
+ };
@@ -0,0 +1,65 @@
1
+ import { DatabaseSync } from "node:sqlite";
2
+
3
+ import { describe, expect, it } from "vitest";
4
+
5
+ import { checkSchemaFence, SETTINGS_TABLE } from "../database/fence";
6
+ import { createSqlAdapter } from "../database/sql/sqlAdapter";
7
+ import { createTableStatements } from "../database/sql/sqlSchema";
8
+ import { sqliteExecutor } from "../database/sql/sqlTestExecutors";
9
+ import { createSettingsMigrator } from "./settingsMigrator";
10
+
11
+ const settings = { "schema.engine": "1", "schema.core": "1.0.0" };
12
+
13
+ const migratorOver = (database: DatabaseSync) =>
14
+ createSettingsMigrator({
15
+ adapterName: "orm",
16
+ executor: sqliteExecutor(database),
17
+ settings,
18
+ applyTables: "the ORM's push",
19
+ });
20
+
21
+ describe("the settings-only migrator", () => {
22
+ it("writes only the settings rows once the ORM applied the tables, then has nothing left", async () => {
23
+ const database = new DatabaseSync(":memory:");
24
+ database.exec(createTableStatements("sqlite", [SETTINGS_TABLE]).join(";"));
25
+ const migrator = migratorOver(database);
26
+
27
+ const pending = await migrator.migrateToLatest();
28
+ expect(pending.operations).toEqual([
29
+ expect.objectContaining({ type: "custom" }),
30
+ ]);
31
+ expect(pending.getSQL?.()).toContain("ON CONFLICT");
32
+ await pending.execute();
33
+ await expect(migrator.migrateToLatest()).resolves.toMatchObject({
34
+ operations: [],
35
+ });
36
+ await expect(migrator.getVersion()).resolves.toBe("1.0.0");
37
+ await expect(
38
+ checkSchemaFence(
39
+ createSqlAdapter({ executor: sqliteExecutor(database) }),
40
+ "orm",
41
+ settings,
42
+ ),
43
+ ).resolves.toBeUndefined();
44
+ });
45
+
46
+ it("asks for the ORM's tables first, refuses a database from before the engine, and rethrows other failures", async () => {
47
+ await expect(
48
+ migratorOver(new DatabaseSync(":memory:")).migrateToLatest(),
49
+ ).rejects.toThrow("the ORM's push");
50
+
51
+ const legacy = new DatabaseSync(":memory:");
52
+ legacy.exec(
53
+ "CREATE TABLE private_hot_updater_settings (key TEXT PRIMARY KEY, value TEXT NOT NULL); INSERT INTO private_hot_updater_settings VALUES ('schema.core', '1.0.0');",
54
+ );
55
+ await expect(migratorOver(legacy).migrateToLatest()).rejects.toMatchObject({
56
+ setting: { key: "schema.engine", expected: "1", found: null },
57
+ });
58
+
59
+ const closed = new DatabaseSync(":memory:");
60
+ closed.close();
61
+ await expect(migratorOver(closed).migrateToLatest()).rejects.toThrow(
62
+ "database is not open",
63
+ );
64
+ });
65
+ });