@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,230 @@
1
+ import { DatabaseSchemaError } from "@hot-updater/plugin-core/internal";
2
+ import { describe, expect, it } from "vitest";
3
+
4
+ import { resolveSchema, validateSchema } from "./resolveSchema";
5
+ import { defineAggregate, defineTable, type ModuleSchema } from "./schema";
6
+
7
+ const parents = defineTable(
8
+ {
9
+ id: { type: "string", maxLength: 36 },
10
+ name: { type: "string", unique: true },
11
+ },
12
+ { key: ["id"], indexes: { all: { eq: [], sort: ["id"] } } },
13
+ );
14
+
15
+ const children = defineTable(
16
+ {
17
+ id: { type: "string", maxLength: 36 },
18
+ parent_id: {
19
+ type: "string",
20
+ maxLength: 36,
21
+ references: { model: "parents", onDelete: "cascade" },
22
+ },
23
+ owner_id: {
24
+ type: "string",
25
+ required: false,
26
+ references: { model: "parents", onDelete: "restrict" },
27
+ },
28
+ position: { type: "integer" },
29
+ received_at_ms: { type: "integer" },
30
+ },
31
+ {
32
+ key: ["id"],
33
+ derived: {
34
+ day: {
35
+ type: "integer",
36
+ compute: (row) =>
37
+ row.received_at_ms - (row.received_at_ms % 86_400_000),
38
+ },
39
+ refs: {
40
+ type: "string",
41
+ multi: true,
42
+ compute: (row) => [`parent:${row.parent_id}`],
43
+ },
44
+ },
45
+ indexes: {
46
+ byParent: {
47
+ eq: ["parent_id"],
48
+ sort: ["position"],
49
+ root: { model: "parents" },
50
+ },
51
+ byRef: { eq: ["refs", "day"], sort: ["received_at_ms"] },
52
+ },
53
+ },
54
+ );
55
+
56
+ const totals = defineAggregate(
57
+ { scope: { type: "string", maxLength: 64 }, bucket: { type: "integer" } },
58
+ {
59
+ key: ["scope", "bucket"],
60
+ counters: ["hits"],
61
+ shards: 4,
62
+ indexes: { byScope: { eq: ["scope"], sort: ["bucket"] } },
63
+ },
64
+ );
65
+
66
+ const core: ModuleSchema = { parents, children, totals };
67
+
68
+ describe("resolveSchema", () => {
69
+ it("adds engine columns, unique-field indexes, reference counters, and roots", () => {
70
+ const schema = resolveSchema([{ id: "core", schema: core }]);
71
+ const parent = schema.models.get("parents")!;
72
+
73
+ expect(parent.table.columns.map(({ name }) => name)).toEqual([
74
+ "id",
75
+ "name",
76
+ "_refs_children_parent_id",
77
+ "_refs_children_owner_id",
78
+ "_v",
79
+ ]);
80
+ expect(parent.table.indexes).toEqual([
81
+ { name: "all", eq: [], sort: ["id"] },
82
+ { name: "name", eq: ["name"], sort: [], unique: true },
83
+ ]);
84
+ expect(
85
+ parent.referencedBy.map(({ field, onDelete }) => [field, onDelete]),
86
+ ).toEqual([
87
+ ["parent_id", "cascade"],
88
+ ["owner_id", "restrict"],
89
+ ]);
90
+
91
+ const child = schema.models.get("children")!;
92
+ expect(child.roots).toEqual(new Map([["byParent", "parents"]]));
93
+ expect(child.table.columns.find(({ name }) => name === "refs")).toEqual({
94
+ name: "refs",
95
+ type: "string",
96
+ nullable: true,
97
+ multi: true,
98
+ });
99
+ expect(
100
+ child.table.columns.find(({ name }) => name === "owner_id"),
101
+ ).toMatchObject({
102
+ nullable: true,
103
+ });
104
+ });
105
+
106
+ it("keys aggregates by identity then shard, with metrics as integers", () => {
107
+ const aggregate = resolveSchema([{ id: "core", schema: core }]).models.get(
108
+ "totals",
109
+ )!;
110
+
111
+ expect(aggregate.table.key).toEqual(["scope", "bucket", "_shard"]);
112
+ expect(
113
+ aggregate.table.columns.map(({ name, type }) => [name, type]),
114
+ ).toEqual([
115
+ ["scope", "string"],
116
+ ["bucket", "integer"],
117
+ ["_shard", "integer"],
118
+ ["hits", "integer"],
119
+ ["_v", "integer"],
120
+ ]);
121
+ });
122
+
123
+ it("namespaces a third-party module's tables", () => {
124
+ const schema = resolveSchema([
125
+ { id: "core", schema: core },
126
+ { id: "audit", namespace: "audit", schema: { parents, children } },
127
+ ]);
128
+
129
+ expect([...schema.models.keys()]).toEqual([
130
+ "parents",
131
+ "children",
132
+ "totals",
133
+ "audit_parents",
134
+ "audit_children",
135
+ ]);
136
+ expect(schema.models.get("audit_children")!.references[0]).toMatchObject({
137
+ target: "audit_parents",
138
+ counter: "_refs_audit_children_parent_id",
139
+ });
140
+ });
141
+ });
142
+
143
+ describe("validateSchema", () => {
144
+ const problems = (schema: ModuleSchema) => {
145
+ try {
146
+ validateSchema([{ id: "core", schema }]);
147
+ } catch (error) {
148
+ expect(error).toBeInstanceOf(DatabaseSchemaError);
149
+ return (error as Error).message;
150
+ }
151
+ return "";
152
+ };
153
+
154
+ it("rejects nullable, json, and undeclared key fields", () => {
155
+ const message = problems({
156
+ rows: defineTable(
157
+ { id: { type: "string", required: false }, meta: { type: "json" } },
158
+ { key: ["id", "meta"] },
159
+ ),
160
+ });
161
+
162
+ expect(message).toContain('core.rows: key field "id" is nullable');
163
+ expect(message).toContain('core.rows: key field "meta" is json');
164
+ });
165
+
166
+ it("rejects a misordered aggregate key and mixed sketches", () => {
167
+ const message = problems({
168
+ totals: defineAggregate(
169
+ { scope: { type: "string" }, bucket: { type: "integer" } },
170
+ { key: ["bucket", "scope"], counters: ["hits"], distinct: ["users"] },
171
+ ),
172
+ });
173
+
174
+ expect(message).toContain(
175
+ "core.totals: key must list the identity fields in declaration order: scope, bucket",
176
+ );
177
+ expect(message).toContain(
178
+ "core.totals: distinct sketches need an aggregate of their own",
179
+ );
180
+ });
181
+
182
+ it("rejects bad roots, references, cascades without an index, and multi-valued sorts", () => {
183
+ const message = problems({
184
+ parents,
185
+ children: defineTable(
186
+ {
187
+ id: { type: "string" },
188
+ parent_id: {
189
+ type: "integer",
190
+ references: { model: "parents", onDelete: "cascade" },
191
+ },
192
+ },
193
+ {
194
+ key: ["id"],
195
+ derived: {
196
+ tags: { type: "string", multi: true, compute: () => ["a"] },
197
+ },
198
+ indexes: {
199
+ byTag: { eq: [], sort: ["tags"] },
200
+ rooted: { eq: ["id"], sort: [], root: { model: "missing" } },
201
+ },
202
+ },
203
+ ),
204
+ });
205
+
206
+ expect(message).toContain(
207
+ 'index "byTag" sorts by multi-valued field "tags"',
208
+ );
209
+ expect(message).toContain(
210
+ 'index "rooted" is rooted at unknown table "missing"',
211
+ );
212
+ expect(message).toContain(
213
+ 'field "parent_id" and the key of "parents" differ in type',
214
+ );
215
+ expect(message).toContain(
216
+ 'cascade on "parent_id" needs an index whose eq is exactly [parent_id]',
217
+ );
218
+ });
219
+
220
+ it("rejects duplicate table names across modules", () => {
221
+ expect(() =>
222
+ validateSchema([
223
+ { id: "core", schema: { parents } },
224
+ { id: "plugin", schema: { parents } },
225
+ ]),
226
+ ).toThrow(
227
+ 'plugin.parents: table "parents" is also declared by core.parents',
228
+ );
229
+ });
230
+ });
@@ -0,0 +1,372 @@
1
+ import {
2
+ DATABASE_VERSION_COLUMN,
3
+ DatabaseSchemaError,
4
+ type PhysicalColumn,
5
+ type PhysicalIndex,
6
+ type PhysicalTable,
7
+ } from "@hot-updater/plugin-core/internal";
8
+
9
+ import type {
10
+ AggregateShape,
11
+ ModelShape,
12
+ ReferenceAction,
13
+ SchemaShape,
14
+ TableShape,
15
+ } from "./definitions";
16
+
17
+ export const SHARD_COLUMN = "_shard";
18
+ export const MAX_SHARDS = 64;
19
+
20
+ export interface SchemaModule {
21
+ readonly id: string;
22
+ readonly schema: SchemaShape;
23
+ /** Prefixes this module's table names; third-party plugins use their id. */
24
+ readonly namespace?: string;
25
+ }
26
+
27
+ export interface ResolvedReference {
28
+ /** The child table and the field that holds the parent key. */
29
+ readonly table: string;
30
+ readonly field: string;
31
+ readonly target: string;
32
+ readonly onDelete: ReferenceAction;
33
+ /** The counter column on the parent; absent for `none`. */
34
+ readonly counter?: string;
35
+ }
36
+
37
+ export interface ResolvedModel {
38
+ readonly module: string;
39
+ /** The model name as its module declares it. */
40
+ readonly model: string;
41
+ readonly table: PhysicalTable;
42
+ readonly definition: ModelShape;
43
+ readonly references: readonly ResolvedReference[];
44
+ readonly referencedBy: readonly ResolvedReference[];
45
+ /** Index name to the physical name of its root table. */
46
+ readonly roots: ReadonlyMap<string, string>;
47
+ }
48
+
49
+ export interface ResolvedSchema {
50
+ /** By physical table name. */
51
+ readonly models: ReadonlyMap<string, ResolvedModel>;
52
+ readonly tables: readonly PhysicalTable[];
53
+ }
54
+
55
+ const NAME = /^[a-z][a-z0-9_]*$/u;
56
+ const INDEX_NAME = /^[A-Za-z][A-Za-z0-9_]*$/u;
57
+
58
+ const tableName = (module: SchemaModule, model: string) =>
59
+ module.namespace ? `${module.namespace}_${model}` : model;
60
+
61
+ const integer = (name: string, defaulted = false): PhysicalColumn => ({
62
+ name,
63
+ type: "integer",
64
+ nullable: false,
65
+ ...(defaulted ? { default: 0 } : {}),
66
+ });
67
+
68
+ /** A declared or derived field's type, and whether it holds several values. */
69
+ const fieldType = (model: ModelShape, name: string) => {
70
+ const field =
71
+ model.fields[name] ??
72
+ (model.kind === "table" ? model.derived[name] : undefined);
73
+ return (
74
+ field && { type: field.type, multi: "multi" in field && !!field.multi }
75
+ );
76
+ };
77
+
78
+ const metricsOf = (model: AggregateShape) => [
79
+ ...model.counters,
80
+ ...model.gauges,
81
+ ...model.distinct,
82
+ ];
83
+
84
+ type Add = (message: string) => void;
85
+ /** A table of the model's own module, by model name. */
86
+ type TableOf = (name: string) => TableShape | undefined;
87
+
88
+ /** Field names, the key, and each field's options. */
89
+ const fieldProblems = (definition: ModelShape, add: Add) => {
90
+ const names = [
91
+ ...Object.keys(definition.fields),
92
+ ...(definition.kind === "table"
93
+ ? Object.keys(definition.derived)
94
+ : metricsOf(definition)),
95
+ ];
96
+ for (const name of new Set(names)) {
97
+ if (!NAME.test(name)) add(`field "${name}" must match ${NAME.source}`);
98
+ if (names.indexOf(name) !== names.lastIndexOf(name)) {
99
+ add(`field "${name}" is declared twice`);
100
+ }
101
+ }
102
+ const { key } = definition;
103
+ if (key.length === 0) add("key is empty");
104
+ if (new Set(key).size !== key.length) add("key repeats a field");
105
+ for (const name of key) {
106
+ const field = definition.fields[name];
107
+ if (field === undefined) add(`key field "${name}" is not a declared field`);
108
+ else if (field.required === false) add(`key field "${name}" is nullable`);
109
+ else if (field.type === "json") add(`key field "${name}" is json`);
110
+ }
111
+ for (const [name, { type, maxLength, ascii }] of Object.entries(
112
+ definition.fields,
113
+ )) {
114
+ if (
115
+ maxLength !== undefined &&
116
+ (type !== "string" || !Number.isSafeInteger(maxLength) || maxLength < 1)
117
+ ) {
118
+ add(`field "${name}" has an invalid maxLength`);
119
+ }
120
+ if (ascii && type !== "string") {
121
+ add(`field "${name}" is ascii but not a string`);
122
+ }
123
+ }
124
+ };
125
+
126
+ /** A table's derived fields and references. */
127
+ const tableProblems = (definition: TableShape, add: Add, table: TableOf) => {
128
+ for (const [name, derived] of Object.entries(definition.derived)) {
129
+ if (typeof derived.compute !== "function") {
130
+ add(`derived field "${name}" has no compute function`);
131
+ }
132
+ if (derived.multi && derived.type === "json") {
133
+ add(`derived field "${name}" cannot be multi-valued json`);
134
+ }
135
+ }
136
+ for (const [name, { type, references }] of Object.entries(
137
+ definition.fields,
138
+ )) {
139
+ if (references === undefined) continue;
140
+ const target = table(references.model);
141
+ const targetKey =
142
+ target?.key.length === 1 ? target.fields[target.key[0]!] : undefined;
143
+ if (targetKey === undefined) {
144
+ add(
145
+ `field "${name}" references "${references.model}", which is not a single-key table of this module`,
146
+ );
147
+ } else if (targetKey.type !== type) {
148
+ add(
149
+ `field "${name}" and the key of "${references.model}" differ in type`,
150
+ );
151
+ }
152
+ if (
153
+ references.onDelete === "cascade" &&
154
+ !Object.values(definition.indexes).some(
155
+ ({ eq }) => eq.length === 1 && eq[0] === name,
156
+ )
157
+ ) {
158
+ add(`cascade on "${name}" needs an index whose eq is exactly [${name}]`);
159
+ }
160
+ }
161
+ };
162
+
163
+ /** An aggregate's identity, metrics, and shards. */
164
+ const aggregateProblems = (definition: AggregateShape, add: Add) => {
165
+ const identity = Object.keys(definition.fields);
166
+ if (definition.key.join("\u0000") !== identity.join("\u0000")) {
167
+ add(
168
+ `key must list the identity fields in declaration order: ${identity.join(", ")}`,
169
+ );
170
+ }
171
+ if (metricsOf(definition).length === 0) add("aggregate has no metrics");
172
+ if (
173
+ definition.distinct.length > 0 &&
174
+ definition.counters.length + definition.gauges.length > 0
175
+ ) {
176
+ add("distinct sketches need an aggregate of their own");
177
+ }
178
+ const { shards } = definition;
179
+ if (!Number.isSafeInteger(shards) || shards < 1 || shards > MAX_SHARDS) {
180
+ add(`shards must be 1–${MAX_SHARDS}`);
181
+ }
182
+ };
183
+
184
+ /** Every problem in one model's declaration, its roots and references included. */
185
+ const problemsOf = (
186
+ module: SchemaModule,
187
+ model: string,
188
+ definition: ModelShape,
189
+ ): string[] => {
190
+ const problems: string[] = [];
191
+ const add = (message: string) =>
192
+ problems.push(`${module.id}.${model}: ${message}`);
193
+ const table = (name: string) => {
194
+ const target = module.schema[name];
195
+ return target?.kind === "table" ? target : undefined;
196
+ };
197
+ if (!NAME.test(model)) {
198
+ add("model names use lowercase letters, digits, and _");
199
+ }
200
+ fieldProblems(definition, add);
201
+ for (const [name, index] of Object.entries(definition.indexes)) {
202
+ const columns = [...index.eq, ...index.sort];
203
+ if (!INDEX_NAME.test(name)) {
204
+ add(`index "${name}" must match ${INDEX_NAME.source}`);
205
+ }
206
+ if (columns.length === 0) add(`index "${name}" has no fields`);
207
+ if (index.unique && index.sort.length > 0) {
208
+ add(`unique index "${name}" cannot sort`);
209
+ }
210
+ const multi = columns.filter((column) => {
211
+ const type = fieldType(definition, column);
212
+ if (type === undefined) {
213
+ add(`index "${name}" names undeclared field "${column}"`);
214
+ } else if (type.type === "json") {
215
+ add(`index "${name}" names json field "${column}"`);
216
+ } else if (type.multi && index.sort.includes(column)) {
217
+ add(`index "${name}" sorts by multi-valued field "${column}"`);
218
+ }
219
+ return type?.type !== "json" && type?.multi;
220
+ });
221
+ if (multi.length > 1) {
222
+ add(`index "${name}" has more than one multi-valued field`);
223
+ }
224
+ if (index.root === undefined) continue;
225
+ const root = table(index.root.model);
226
+ if (root === undefined) {
227
+ add(`index "${name}" is rooted at unknown table "${index.root.model}"`);
228
+ } else if (
229
+ root.key.length > index.eq.length ||
230
+ root.key.some(
231
+ (field, position) =>
232
+ fieldType(definition, index.eq[position]!)?.type !==
233
+ root.fields[field]!.type,
234
+ )
235
+ ) {
236
+ add(
237
+ `index "${name}" eq must start with the key of "${index.root.model}"`,
238
+ );
239
+ }
240
+ }
241
+ if (definition.kind === "table") tableProblems(definition, add, table);
242
+ else aggregateProblems(definition, add);
243
+ return problems;
244
+ };
245
+
246
+ /** Rejects invalid module schemas at startup, listing every problem. */
247
+ export const validateSchema = (modules: readonly SchemaModule[]): void => {
248
+ const problems: string[] = [];
249
+ const owners = new Map<string, string>();
250
+ for (const module of modules) {
251
+ for (const [model, definition] of Object.entries(module.schema)) {
252
+ const name = tableName(module, model);
253
+ const owner = owners.get(name);
254
+ if (owner !== undefined) {
255
+ problems.push(
256
+ `${module.id}.${model}: table "${name}" is also declared by ${owner}`,
257
+ );
258
+ }
259
+ owners.set(name, `${module.id}.${model}`);
260
+ problems.push(...problemsOf(module, model, definition));
261
+ }
262
+ }
263
+ if (problems.length > 0) {
264
+ throw new DatabaseSchemaError(
265
+ `Invalid database schema:\n- ${problems.join("\n- ")}`,
266
+ );
267
+ }
268
+ };
269
+
270
+ const indexesOf = (definition: ModelShape): PhysicalIndex[] => [
271
+ ...Object.entries(definition.indexes).map(([name, index]) => ({
272
+ name,
273
+ eq: [...index.eq],
274
+ sort: [...index.sort],
275
+ ...(index.unique ? { unique: true as const } : {}),
276
+ })),
277
+ ...Object.entries(definition.fields).flatMap(([name, field]) =>
278
+ field.unique ? [{ name, eq: [name], sort: [], unique: true as const }] : [],
279
+ ),
280
+ ];
281
+
282
+ /** Validates the modules, then resolves them into physical tables with engine columns. */
283
+ export const resolveSchema = (
284
+ modules: readonly SchemaModule[],
285
+ ): ResolvedSchema => {
286
+ validateSchema(modules);
287
+ const references: ResolvedReference[] = [];
288
+ for (const module of modules) {
289
+ for (const [model, definition] of Object.entries(module.schema)) {
290
+ if (definition.kind !== "table") continue;
291
+ const table = tableName(module, model);
292
+ for (const [field, { references: to }] of Object.entries(
293
+ definition.fields,
294
+ )) {
295
+ if (to === undefined) continue;
296
+ const { onDelete } = to;
297
+ const target = tableName(module, to.model);
298
+ const counter =
299
+ onDelete === "none" ? {} : { counter: `_refs_${table}_${field}` };
300
+ references.push({ table, field, target, onDelete, ...counter });
301
+ }
302
+ }
303
+ }
304
+ const models = new Map<string, ResolvedModel>();
305
+ for (const module of modules) {
306
+ for (const [model, definition] of Object.entries(module.schema)) {
307
+ const name = tableName(module, model);
308
+ const referencedBy = references.filter(({ target }) => target === name);
309
+ const columns: PhysicalColumn[] = Object.entries(definition.fields).map(
310
+ ([field, { type, required, maxLength, ascii }]) => ({
311
+ name: field,
312
+ type,
313
+ nullable: required === false,
314
+ ...(maxLength === undefined ? {} : { maxLength }),
315
+ ...(ascii ? { ascii } : {}),
316
+ }),
317
+ );
318
+ if (definition.kind === "table") {
319
+ for (const [field, { type, multi }] of Object.entries(
320
+ definition.derived,
321
+ )) {
322
+ columns.push({
323
+ name: field,
324
+ type,
325
+ nullable: true,
326
+ ...(multi ? { multi } : {}),
327
+ });
328
+ }
329
+ } else {
330
+ columns.push(
331
+ integer(SHARD_COLUMN),
332
+ ...[...definition.counters, ...definition.gauges].map((metric) =>
333
+ integer(metric),
334
+ ),
335
+ ...definition.distinct.map((metric) => ({
336
+ name: metric,
337
+ type: "string" as const,
338
+ nullable: true,
339
+ })),
340
+ );
341
+ }
342
+ for (const { counter } of referencedBy) {
343
+ if (counter !== undefined) columns.push(integer(counter, true));
344
+ }
345
+ columns.push(integer(DATABASE_VERSION_COLUMN, true));
346
+ models.set(name, {
347
+ module: module.id,
348
+ model,
349
+ definition,
350
+ table: {
351
+ name,
352
+ columns,
353
+ key:
354
+ definition.kind === "aggregate"
355
+ ? [...definition.key, SHARD_COLUMN]
356
+ : [...definition.key],
357
+ indexes: indexesOf(definition),
358
+ },
359
+ references: references.filter(({ table }) => table === name),
360
+ referencedBy,
361
+ roots: new Map(
362
+ Object.entries(definition.indexes).flatMap(([index, { root }]) =>
363
+ root === undefined
364
+ ? []
365
+ : [[index, tableName(module, root.model)] as const],
366
+ ),
367
+ ),
368
+ });
369
+ }
370
+ }
371
+ return { models, tables: [...models.values()].map(({ table }) => table) };
372
+ };