@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
@@ -0,0 +1,450 @@
1
+ import {
2
+ canonicalJson,
3
+ compareUtf8,
4
+ type DatabaseAdapter,
5
+ DatabaseAdapterContractError,
6
+ type DatabaseKey,
7
+ type DatabaseKeyValue,
8
+ DATABASE_VERSION_COLUMN as V,
9
+ findPhysicalIndex,
10
+ indexEntries,
11
+ indexOrderColumns,
12
+ indexOrderTuple,
13
+ matchesQuery,
14
+ type PhysicalIndex,
15
+ type PhysicalTable,
16
+ type QueryBound,
17
+ type QueryRequest,
18
+ rowKey,
19
+ type StoredRow,
20
+ type WriteOp,
21
+ type WriteResult,
22
+ } from "@hot-updater/plugin-core/internal";
23
+
24
+ /** An item's address: a partition, and a sort key within it. */
25
+ export interface KvKey {
26
+ readonly pk: string;
27
+ readonly sk: string;
28
+ }
29
+
30
+ /** What an op requires of its item before the write: that it exists or not, or that its `_v` is `v`. */
31
+ export type KvCondition = { readonly exists: boolean } | { readonly v: number };
32
+
33
+ export type KvOp = {
34
+ readonly key: KvKey;
35
+ readonly condition?: KvCondition;
36
+ } & (
37
+ | { readonly type: "put"; readonly value: StoredRow }
38
+ | { readonly type: "delete" }
39
+ | { readonly type: "check" }
40
+ /** Adds to numeric attributes, creating the item from `init` when it is missing. */
41
+ | {
42
+ readonly type: "add";
43
+ readonly by: Readonly<Record<string, number>>;
44
+ readonly init?: StoredRow;
45
+ }
46
+ );
47
+
48
+ /** Sort keys from `gte` up to `lt` in one partition, resuming past `after`. */
49
+ export interface KvRange {
50
+ readonly pk: string;
51
+ readonly gte?: string;
52
+ readonly lt?: string;
53
+ /**
54
+ * `lt` as an inclusive bound, with `lt`: every key the helper writes is
55
+ * below `lt` exactly when it is at most `lte`. A store whose two-sided
56
+ * range is inclusive (DynamoDB's `BETWEEN`) uses it to read nothing past
57
+ * the range.
58
+ */
59
+ readonly lte?: string;
60
+ readonly order: "asc" | "desc";
61
+ readonly limit: number;
62
+ readonly after?: string;
63
+ }
64
+
65
+ export interface KvItem {
66
+ readonly sk: string;
67
+ readonly value: StoredRow;
68
+ }
69
+
70
+ /**
71
+ * A key-value backend (DynamoDB, Firestore): strongly consistent point
72
+ * reads, one partition's sort-key range per page, and atomic writes whose
73
+ * conditions all see the state before the write. Sort keys compare as UTF-8.
74
+ */
75
+ export interface KeyValueStore {
76
+ readonly id: string;
77
+ /**
78
+ * The most items, and bytes, one atomic write takes; `itemBytes` caps each
79
+ * item, and `keyBytes` the partition and sort keys the store indexes whole.
80
+ */
81
+ readonly limits: {
82
+ readonly items: number;
83
+ readonly bytes: number;
84
+ readonly itemBytes?: number;
85
+ readonly keyBytes?: { readonly pk: number; readonly sk: number };
86
+ };
87
+ get(keys: readonly KvKey[]): Promise<readonly (StoredRow | null)[]>;
88
+ /** One native page; `more` when the range may go on past it. An empty page ends the range. */
89
+ query(
90
+ range: KvRange,
91
+ ): Promise<{ readonly items: readonly KvItem[]; readonly more: boolean }>;
92
+ /** Applies every op or none; `failedOp` names an op whose condition failed. */
93
+ write(ops: readonly KvOp[]): Promise<WriteResult>;
94
+ /** Prepares the store, such as its one table, for `db migrate`. */
95
+ readonly migrations?: { apply(): Promise<void> };
96
+ dispose?(): Promise<void>;
97
+ }
98
+
99
+ const SEPARATOR = "\u0001";
100
+
101
+ /** Order-preserving text for key values; each value ends in the lowest code point any encoding uses. */
102
+ export const encodeKvKey = (values: readonly DatabaseKeyValue[]): string =>
103
+ values
104
+ .map((value) => {
105
+ if (typeof value === "boolean") return `${Number(value)}${SEPARATOR}`;
106
+ if (typeof value === "number") {
107
+ // IEEE 754 bits sort as hex once positives flip the sign bit and negatives every bit.
108
+ const view = new DataView(new ArrayBuffer(8));
109
+ view.setFloat64(0, value === 0 ? 0 : value);
110
+ const mask = view.getUint32(0) >>> 31 ? [~0, ~0] : [1 << 31, 0];
111
+ const words = [0, 4].map((at, word) =>
112
+ ((view.getUint32(at) ^ mask[word]!) >>> 0)
113
+ .toString(16)
114
+ .padStart(8, "0"),
115
+ );
116
+ return `${words.join("")}${SEPARATOR}`;
117
+ }
118
+ let text = "";
119
+ // U+0000–U+0002 become two code units above the separator, in order.
120
+ for (const char of value) {
121
+ text +=
122
+ char < "\u0003"
123
+ ? `\u0002${String.fromCharCode(char.charCodeAt(0) + 1)}`
124
+ : char;
125
+ }
126
+ return `${text}${SEPARATOR}`;
127
+ })
128
+ .join("");
129
+
130
+ /** The first sort key after every key that starts with `prefix`. */
131
+ const pastPrefix = (prefix: string) => `${prefix.slice(0, -1)}\u0002`;
132
+
133
+ /** A unique item's sort key, and an index item's when its order is empty. */
134
+ const ONLY = "#";
135
+
136
+ interface Layout {
137
+ readonly table: PhysicalTable;
138
+ readonly rows: string;
139
+ /** The columns index copies hold: all but `_v` and counters (columns with a default). */
140
+ readonly copied: readonly string[];
141
+ readonly counters: readonly string[];
142
+ /** Indexes with items of their own: unique ones, and those not in key order. */
143
+ readonly indexed: readonly PhysicalIndex[];
144
+ }
145
+
146
+ /** An index whose eq and order columns are the key, in order, reads the row items. */
147
+ const inKeyOrder = (table: PhysicalTable, index: PhysicalIndex) => {
148
+ const columns = [...index.eq, ...indexOrderColumns(table, index)];
149
+ return (
150
+ !index.unique &&
151
+ columns.length === table.key.length &&
152
+ columns.every((column, position) => column === table.key[position])
153
+ );
154
+ };
155
+
156
+ const same = (left: StoredRow, right: StoredRow, columns: readonly string[]) =>
157
+ columns.every(
158
+ (name) => canonicalJson(left[name]) === canonicalJson(right[name]),
159
+ );
160
+
161
+ type Planned = { readonly op: KvOp; readonly origin: number };
162
+
163
+ export interface KvAdapterOptions {
164
+ readonly store: KeyValueStore;
165
+ readonly tablePrefix?: string;
166
+ }
167
+
168
+ /**
169
+ * The storage adapter over a key-value store. A row is one item at
170
+ * `pk = <table>`, `sk = enc(key)`; each index not in key order adds an item
171
+ * per entry at `pk = <table>#<index>#enc(eq)`, `sk = enc(order)`, and each
172
+ * unique entry one item at `sk = "#"`, written only where no other row holds
173
+ * it. Index items hold a copy of the row without `_v` or counters, so an
174
+ * increment, which changes only those, never makes them stale.
175
+ */
176
+ export const createKvAdapter = ({
177
+ store,
178
+ tablePrefix = "",
179
+ }: KvAdapterOptions): DatabaseAdapter => {
180
+ const layouts = new WeakMap<PhysicalTable, Layout>();
181
+ const layoutOf = (table: PhysicalTable): Layout => {
182
+ const known = layouts.get(table);
183
+ if (known) return known;
184
+ const counters = table.columns
185
+ .filter((column) => column.default !== undefined && column.name !== V)
186
+ .map((column) => column.name);
187
+ const layout = {
188
+ table,
189
+ rows: tablePrefix + table.name,
190
+ copied: table.columns
191
+ .map((column) => column.name)
192
+ .filter((name) => name !== V && !counters.includes(name)),
193
+ counters,
194
+ indexed: table.indexes.filter((index) => !inKeyOrder(table, index)),
195
+ };
196
+ layouts.set(table, layout);
197
+ return layout;
198
+ };
199
+ const rowItem = (layout: Layout, key: DatabaseKey): KvKey => ({
200
+ pk: layout.rows,
201
+ sk: encodeKvKey(key),
202
+ });
203
+ const partition = (layout: Layout, index: PhysicalIndex, eq: DatabaseKey) =>
204
+ `${layout.rows}#${index.name}#${encodeKvKey(eq)}`;
205
+ const copyOf = (layout: Layout, row: StoredRow): StoredRow =>
206
+ Object.fromEntries(layout.copied.map((name) => [name, row[name] ?? null]));
207
+
208
+ /** Every index and unique item a row has, by address. */
209
+ const itemsOf = (layout: Layout, row: StoredRow | null) => {
210
+ const items = new Map<string, { key: KvKey; unique: boolean }>();
211
+ for (const index of row === null ? [] : layout.indexed) {
212
+ for (const eq of indexEntries(layout.table, index, row!)) {
213
+ const pk = partition(layout, index, eq);
214
+ const sk = index.unique
215
+ ? ONLY
216
+ : encodeKvKey(indexOrderTuple(layout.table, index, row!)) || ONLY;
217
+ items.set(JSON.stringify([pk, sk]), {
218
+ key: { pk, sk },
219
+ unique: index.unique === true,
220
+ });
221
+ }
222
+ }
223
+ return items;
224
+ };
225
+
226
+ /** Moves a row's index items from `before` to `after`; a unique entry is taken only where no row holds it. */
227
+ const indexOps = (
228
+ layout: Layout,
229
+ before: StoredRow | null,
230
+ after: StoredRow | null,
231
+ ): KvOp[] => {
232
+ const old = itemsOf(layout, before);
233
+ const next = itemsOf(layout, after);
234
+ const ops: KvOp[] = [...old]
235
+ .filter(([id]) => !next.has(id))
236
+ .map(([, item]) => ({ type: "delete", key: item.key }));
237
+ const copy = after && copyOf(layout, after);
238
+ const unchanged =
239
+ before !== null && copy !== null && same(before, copy, layout.copied);
240
+ for (const [id, item] of next) {
241
+ if (old.has(id) && unchanged) continue;
242
+ const taken = item.unique && !old.has(id);
243
+ ops.push({
244
+ type: "put",
245
+ key: item.key,
246
+ value: copy!,
247
+ condition: taken ? { exists: false } : undefined,
248
+ });
249
+ }
250
+ return ops;
251
+ };
252
+
253
+ /** The item ops of one adapter op: its row item first, then its index items. */
254
+ const plan = (op: WriteOp): KvOp[] => {
255
+ const layout = layoutOf(op.table);
256
+ const key = rowItem(
257
+ layout,
258
+ op.type === "insert" ? rowKey(op.table, op.row) : op.key,
259
+ );
260
+ const guard = op.type === "insert" ? { exists: false } : op.guard;
261
+ const condition = guard && ("v" in guard ? { v: guard.v } : guard);
262
+ switch (op.type) {
263
+ case "check":
264
+ return [{ type: "check", key, condition }];
265
+ case "increment":
266
+ if (
267
+ layout.indexed.length > 0 &&
268
+ (op.init !== undefined ||
269
+ Object.keys(op.by).some((name) => layout.copied.includes(name)))
270
+ ) {
271
+ throw new DatabaseAdapterContractError(
272
+ `${op.table.name} has index copies: an increment may change only counters of an existing row.`,
273
+ );
274
+ }
275
+ return [
276
+ {
277
+ type: "add",
278
+ key,
279
+ by: { ...op.by, [V]: 1 },
280
+ init: op.init,
281
+ condition: condition ?? (op.init ? undefined : { exists: true }),
282
+ },
283
+ ];
284
+ case "delete":
285
+ return [
286
+ { type: "delete", key, condition },
287
+ ...indexOps(layout, op.previous, null),
288
+ ];
289
+ default: {
290
+ const before = op.type === "insert" ? null : op.previous;
291
+ const row =
292
+ op.type === "insert"
293
+ ? op.row
294
+ : { ...op.previous, ...op.set, [V]: op.guard.v + 1 };
295
+ return [
296
+ { type: "put", key, value: row, condition },
297
+ ...indexOps(layout, before, row),
298
+ ];
299
+ }
300
+ }
301
+ };
302
+
303
+ /**
304
+ * One op per item. Only unique items of two rows can meet: one op frees
305
+ * the value and another takes it. That becomes one unconditional put, as
306
+ * the freeing op's guard already holds the value's owner. Two rows taking
307
+ * one value fail at the later op.
308
+ */
309
+ const compile = (ops: readonly WriteOp[]) => {
310
+ const items = new Map<string, Planned>();
311
+ for (const [origin, op] of ops.entries()) {
312
+ for (const kv of plan(op)) {
313
+ const id = JSON.stringify([kv.key.pk, kv.key.sk]);
314
+ const earlier = items.get(id);
315
+ const put = [earlier, { op: kv, origin }].find(
316
+ (planned) => planned?.op.type === "put",
317
+ );
318
+ if (earlier === undefined) {
319
+ items.set(id, { op: kv, origin });
320
+ } else if (
321
+ put?.op.type === "put" &&
322
+ [earlier.op, kv].some(({ type }) => type === "delete")
323
+ ) {
324
+ items.set(id, { op: { ...put.op, condition: undefined }, origin });
325
+ } else {
326
+ return { failedOp: origin };
327
+ }
328
+ }
329
+ }
330
+ return { items: [...items.values()] };
331
+ };
332
+
333
+ const encoder = new TextEncoder();
334
+ const sizeOf = ({ op }: Planned) =>
335
+ encoder.encode(
336
+ op.key.pk +
337
+ op.key.sk +
338
+ JSON.stringify(
339
+ op.type === "put" ? op.value : op.type === "add" ? op.init : null,
340
+ ),
341
+ ).length;
342
+
343
+ /** Fills counters from the rows; a row that still equals its copy is returned whole, `_v` included. */
344
+ const complete = async (layout: Layout, copies: readonly StoredRow[]) => {
345
+ if (layout.counters.length === 0 || copies.length === 0) return copies;
346
+ const rows = await store.get(
347
+ copies.map((copy) => rowItem(layout, rowKey(layout.table, copy))),
348
+ );
349
+ return copies.map((copy, at) => {
350
+ const row = rows[at];
351
+ if (row && same(row, copy, layout.copied)) return row;
352
+ const counters = layout.counters.map((name) => [name, row?.[name] ?? 0]);
353
+ return { ...copy, ...Object.fromEntries(counters) };
354
+ });
355
+ };
356
+
357
+ /**
358
+ * A request's sort keys within its partition, from `gte` up to `lt`; null
359
+ * when none can match. Every encoding ends in the separator, so the keys
360
+ * that start with a prefix end right before `pastPrefix` of it.
361
+ */
362
+ const rangeOf = (prefix: string, request: QueryRequest) => {
363
+ const edge = (bound: QueryBound | undefined, lower: boolean) => {
364
+ if (bound === undefined) {
365
+ return prefix === "" ? undefined : lower ? prefix : pastPrefix(prefix);
366
+ }
367
+ const text = prefix + encodeKvKey(bound.values);
368
+ if (text === "") return bound.inclusive ? undefined : null;
369
+ return lower === bound.inclusive ? text : pastPrefix(text);
370
+ };
371
+ const gte = edge(request.lower, true);
372
+ const lt = edge(request.upper, false);
373
+ if (gte === null || lt === null) return null;
374
+ // Bounds that cross leave nothing, and DynamoDB refuses them.
375
+ if (gte !== undefined && lt !== undefined && compareUtf8(gte, lt) >= 0) {
376
+ return null;
377
+ }
378
+ if (lt === undefined) return { gte, lt };
379
+ // No key holds U+0000, so none sorts between an `lt` without its final
380
+ // separator and `lt`; and no key ends in U+0002, so an `lt` from
381
+ // `pastPrefix` is never a key itself.
382
+ return { gte, lt, lte: lt.endsWith(SEPARATOR) ? lt.slice(0, -1) : lt };
383
+ };
384
+
385
+ return {
386
+ id: store.id,
387
+ fits(ops) {
388
+ const { items } = compile(ops);
389
+ // A write in which two rows take one unique value fails at its op, not here.
390
+ if (items === undefined) return true;
391
+ const { items: most, bytes, itemBytes = bytes, keyBytes } = store.limits;
392
+ const sizes = items.map(sizeOf);
393
+ const length = (text: string) => encoder.encode(text).length;
394
+ return (
395
+ items.length <= most &&
396
+ sizes.reduce((sum, size) => sum + size, 0) <= bytes &&
397
+ sizes.every((size) => size <= itemBytes) &&
398
+ (keyBytes === undefined ||
399
+ items.every(
400
+ ({ op }) =>
401
+ length(op.key.pk) <= keyBytes.pk &&
402
+ length(op.key.sk) <= keyBytes.sk,
403
+ ))
404
+ );
405
+ },
406
+ get: (table, keys) =>
407
+ store.get(keys.map((key) => rowItem(layoutOf(table), key))),
408
+ async query(table, request) {
409
+ const layout = layoutOf(table);
410
+ const index = findPhysicalIndex(table, request.index);
411
+ if (index.unique) {
412
+ const key = { pk: partition(layout, index, request.eq), sk: ONLY };
413
+ const [copy] = await store.get([key]);
414
+ const found = copy && matchesQuery(table, index, request, copy);
415
+ return complete(layout, found ? [copy] : []);
416
+ }
417
+ const own = inKeyOrder(table, index);
418
+ const range = rangeOf(own ? encodeKvKey(request.eq) : "", request);
419
+ if (range === null) return [];
420
+ const pk = own ? layout.rows : partition(layout, index, request.eq);
421
+ const rows: StoredRow[] = [];
422
+ let after: string | undefined;
423
+ // Native pages are capped (DynamoDB: 1 MB), so read on until the limit or the range's end.
424
+ while (rows.length < request.limit) {
425
+ const { items, more } = await store.query({
426
+ pk,
427
+ ...range,
428
+ order: request.order,
429
+ limit: request.limit - rows.length,
430
+ after,
431
+ });
432
+ rows.push(...items.map((item) => item.value));
433
+ if (!more || items.length === 0) break;
434
+ after = items.at(-1)!.sk;
435
+ }
436
+ return own ? rows : complete(layout, rows);
437
+ },
438
+ async write(ops) {
439
+ const { items, failedOp } = compile(ops);
440
+ if (items === undefined) return { ok: false, failedOp };
441
+ const result = await store.write(items.map(({ op }) => op));
442
+ return !result.ok && "failedOp" in result
443
+ ? { ok: false, failedOp: items[result.failedOp]!.origin }
444
+ : result;
445
+ },
446
+ // Tables need no items of their own; the store prepares itself, if at all.
447
+ migrations: { apply: async () => store.migrations?.apply() },
448
+ ...(store.dispose === undefined ? {} : { dispose: () => store.dispose!() }),
449
+ };
450
+ };
@@ -0,0 +1,91 @@
1
+ import {
2
+ compareUtf8,
3
+ DATABASE_VERSION_COLUMN,
4
+ type StoredRow,
5
+ } from "@hot-updater/plugin-core/internal";
6
+
7
+ import type { KeyValueStore, KvCondition, KvKey } from "./kvAdapter";
8
+
9
+ export interface MemoryKeyValueStoreOptions {
10
+ /** Test-only: the most items one `query` page returns. */
11
+ readonly nativePageSize?: number;
12
+ readonly limits?: KeyValueStore["limits"];
13
+ }
14
+
15
+ /**
16
+ * An in-memory key-value store that behaves like DynamoDB where the adapter
17
+ * can tell: a write checks every condition against the state before it,
18
+ * refuses two ops on one item, and applies all ops or none.
19
+ */
20
+ export const createMemoryKeyValueStore = (
21
+ options: MemoryKeyValueStoreOptions = {},
22
+ ): KeyValueStore & { readonly items: () => number } => {
23
+ let partitions = new Map<string, Map<string, StoredRow>>();
24
+ const read = (key: KvKey) => partitions.get(key.pk)?.get(key.sk) ?? null;
25
+ const holds = (key: KvKey, condition: KvCondition | undefined) => {
26
+ if (condition === undefined) return true;
27
+ const item = read(key);
28
+ return "v" in condition
29
+ ? item !== null && item[DATABASE_VERSION_COLUMN] === condition.v
30
+ : (item !== null) === condition.exists;
31
+ };
32
+ return {
33
+ id: "memory-kv",
34
+ limits: options.limits ?? { items: 100, bytes: 4_000_000 },
35
+ items: () =>
36
+ [...partitions.values()].reduce((sum, items) => sum + items.size, 0),
37
+ async get(keys) {
38
+ return keys.map((key) => structuredClone(read(key)));
39
+ },
40
+ async query({ pk, gte, lt, order, limit, after }) {
41
+ const direction = order === "asc" ? 1 : -1;
42
+ const matching = [...(partitions.get(pk) ?? new Map<string, StoredRow>())]
43
+ .filter(
44
+ ([sk]) =>
45
+ (gte === undefined || compareUtf8(sk, gte) >= 0) &&
46
+ (lt === undefined || compareUtf8(sk, lt) < 0) &&
47
+ (after === undefined || direction * compareUtf8(sk, after) > 0),
48
+ )
49
+ .sort(([left], [right]) => direction * compareUtf8(left, right));
50
+ const size = Math.min(limit, options.nativePageSize ?? limit);
51
+ return {
52
+ items: matching
53
+ .slice(0, size)
54
+ .map(([sk, value]) => ({ sk, value: structuredClone(value) })),
55
+ more: matching.length > size,
56
+ };
57
+ },
58
+ async write(ops) {
59
+ const ids = new Set(ops.map(({ key }) => JSON.stringify(key)));
60
+ if (ids.size !== ops.length) {
61
+ throw new Error("A write may touch each item once.");
62
+ }
63
+ const failed = ops.findIndex((op) => !holds(op.key, op.condition));
64
+ if (failed !== -1) return { ok: false, failedOp: failed };
65
+ const next = new Map(partitions);
66
+ for (const op of ops) {
67
+ if (op.type === "check") continue;
68
+ const items = new Map(next.get(op.key.pk));
69
+ next.set(op.key.pk, items);
70
+ if (op.type === "delete") {
71
+ items.delete(op.key.sk);
72
+ } else if (op.type === "put") {
73
+ items.set(op.key.sk, structuredClone(op.value));
74
+ } else {
75
+ const item: Record<string, unknown> = structuredClone(
76
+ items.get(op.key.sk) ?? op.init ?? {},
77
+ );
78
+ for (const [name, delta] of Object.entries(op.by)) {
79
+ item[name] = Number(item[name] ?? 0) + delta;
80
+ }
81
+ items.set(op.key.sk, item as StoredRow);
82
+ }
83
+ }
84
+ partitions = next;
85
+ return { ok: true };
86
+ },
87
+ async dispose() {
88
+ partitions = new Map();
89
+ },
90
+ };
91
+ };
@@ -0,0 +1,13 @@
1
+ import { createMemoryAdapter } from "@hot-updater/plugin-core/internal";
2
+ import { setupReadBudgetTestSuite } from "@hot-updater/test-utils";
3
+
4
+ import { readBudgetServer } from "../readBudgets.testFixtures";
5
+
6
+ /** The reference adapter, with the native page size the suite sets. */
7
+ setupReadBudgetTestSuite({
8
+ name: "memory",
9
+ server: readBudgetServer,
10
+ createAdapter: async ({ nativePageSize }) => ({
11
+ adapter: createMemoryAdapter({ nativePageSize }),
12
+ }),
13
+ });