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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (542) hide show
  1. package/dist/_virtual/_rolldown/runtime.cjs +1 -1
  2. package/dist/adapters/drizzle.cjs +35 -69
  3. package/dist/adapters/drizzle.d.cts +13 -6
  4. package/dist/adapters/drizzle.d.mts +13 -6
  5. package/dist/adapters/drizzle.mjs +34 -69
  6. package/dist/adapters/drizzleExecutor.cjs +114 -0
  7. package/dist/adapters/drizzleExecutor.d.cts +9 -0
  8. package/dist/adapters/drizzleExecutor.d.mts +9 -0
  9. package/dist/adapters/drizzleExecutor.mjs +112 -0
  10. package/dist/adapters/kysely.cjs +25 -31
  11. package/dist/adapters/kysely.d.cts +10 -7
  12. package/dist/adapters/kysely.d.mts +10 -7
  13. package/dist/adapters/kysely.mjs +25 -31
  14. package/dist/adapters/kyselyExecutor.cjs +32 -0
  15. package/dist/adapters/kyselyExecutor.d.cts +12 -0
  16. package/dist/adapters/kyselyExecutor.d.mts +12 -0
  17. package/dist/adapters/kyselyExecutor.mjs +32 -0
  18. package/dist/adapters/mongodb.cjs +28 -27
  19. package/dist/adapters/mongodb.d.cts +10 -5
  20. package/dist/adapters/mongodb.d.mts +10 -5
  21. package/dist/adapters/mongodb.mjs +28 -27
  22. package/dist/adapters/mongodbAdapter.cjs +174 -0
  23. package/dist/adapters/mongodbAdapter.d.cts +9 -0
  24. package/dist/adapters/mongodbAdapter.d.mts +9 -0
  25. package/dist/adapters/mongodbAdapter.mjs +173 -0
  26. package/dist/adapters/prisma.cjs +38 -220
  27. package/dist/adapters/prisma.d.cts +12 -8
  28. package/dist/adapters/prisma.d.mts +12 -8
  29. package/dist/adapters/prisma.mjs +38 -219
  30. package/dist/adapters/prismaExecutor.cjs +64 -0
  31. package/dist/adapters/prismaExecutor.mjs +64 -0
  32. package/dist/adapters/sqlProviders.cjs +9 -0
  33. package/dist/adapters/sqlProviders.mjs +9 -0
  34. package/dist/apiKeys.cjs +8 -8
  35. package/dist/apiKeys.d.cts +1 -24
  36. package/dist/apiKeys.d.mts +1 -24
  37. package/dist/apiKeys.mjs +5 -9
  38. package/dist/assembly/assemblePlugins.cjs +147 -0
  39. package/dist/assembly/assemblePlugins.d.cts +12 -0
  40. package/dist/assembly/assemblePlugins.d.mts +12 -0
  41. package/dist/assembly/assemblePlugins.mjs +146 -0
  42. package/dist/assembly/databasePlugins.cjs +23 -0
  43. package/dist/assembly/databasePlugins.d.cts +43 -0
  44. package/dist/assembly/databasePlugins.d.mts +43 -0
  45. package/dist/assembly/databasePlugins.mjs +22 -0
  46. package/dist/core/api.cjs +91 -0
  47. package/dist/core/api.d.cts +65 -0
  48. package/dist/core/api.d.mts +65 -0
  49. package/dist/core/api.mjs +87 -0
  50. package/dist/core/baseCandidates.cjs +71 -0
  51. package/dist/core/baseCandidates.d.cts +17 -0
  52. package/dist/core/baseCandidates.d.mts +17 -0
  53. package/dist/core/baseCandidates.mjs +69 -0
  54. package/dist/core/operations.cjs +350 -0
  55. package/dist/core/operations.mjs +350 -0
  56. package/dist/core/reads.cjs +273 -0
  57. package/dist/core/reads.d.cts +48 -0
  58. package/dist/core/reads.d.mts +48 -0
  59. package/dist/core/reads.mjs +267 -0
  60. package/dist/core/releases.cjs +193 -0
  61. package/dist/core/releases.mjs +190 -0
  62. package/dist/core/schema.cjs +279 -0
  63. package/dist/core/schema.d.cts +307 -0
  64. package/dist/core/schema.d.mts +307 -0
  65. package/dist/core/schema.mjs +277 -0
  66. package/dist/core/writes.cjs +96 -0
  67. package/dist/core/writes.mjs +91 -0
  68. package/dist/createHotUpdaterCore.cjs +61 -63
  69. package/dist/createHotUpdaterCore.d.cts +53 -45
  70. package/dist/createHotUpdaterCore.d.mts +53 -45
  71. package/dist/createHotUpdaterCore.mjs +61 -63
  72. package/dist/database/cursor.cjs +44 -0
  73. package/dist/database/cursor.d.cts +7 -0
  74. package/dist/database/cursor.d.mts +7 -0
  75. package/dist/database/cursor.mjs +40 -0
  76. package/dist/database/database.cjs +44 -0
  77. package/dist/database/database.d.cts +123 -0
  78. package/dist/database/database.d.mts +123 -0
  79. package/dist/database/database.mjs +44 -0
  80. package/dist/database/definitions.d.cts +68 -0
  81. package/dist/database/definitions.d.mts +68 -0
  82. package/dist/database/engine.cjs +37 -0
  83. package/dist/database/engine.d.cts +40 -0
  84. package/dist/database/engine.d.mts +40 -0
  85. package/dist/database/engine.mjs +37 -0
  86. package/dist/database/engineAggregates.cjs +115 -0
  87. package/dist/database/engineAggregates.mjs +112 -0
  88. package/dist/database/engineReads.cjs +180 -0
  89. package/dist/database/engineReads.d.cts +35 -0
  90. package/dist/database/engineReads.d.mts +35 -0
  91. package/dist/database/engineReads.mjs +179 -0
  92. package/dist/database/engineTransaction.cjs +403 -0
  93. package/dist/database/engineTransaction.d.cts +26 -0
  94. package/dist/database/engineTransaction.d.mts +26 -0
  95. package/dist/database/engineTransaction.mjs +402 -0
  96. package/dist/database/errors.cjs +27 -0
  97. package/dist/database/errors.d.cts +21 -0
  98. package/dist/database/errors.d.mts +21 -0
  99. package/dist/database/errors.mjs +24 -0
  100. package/dist/database/fence.cjs +124 -0
  101. package/dist/database/fence.d.cts +42 -0
  102. package/dist/database/fence.d.mts +42 -0
  103. package/dist/database/fence.mjs +116 -0
  104. package/dist/database/index.cjs +127 -0
  105. package/dist/database/index.d.cts +17 -0
  106. package/dist/database/index.d.mts +17 -0
  107. package/dist/database/index.mjs +15 -0
  108. package/dist/database/kv/kvAdapter.cjs +273 -0
  109. package/dist/database/kv/kvAdapter.d.cts +101 -0
  110. package/dist/database/kv/kvAdapter.d.mts +101 -0
  111. package/dist/database/kv/kvAdapter.mjs +272 -0
  112. package/dist/database/resolveSchema.cjs +196 -0
  113. package/dist/database/resolveSchema.d.cts +42 -0
  114. package/dist/database/resolveSchema.d.mts +42 -0
  115. package/dist/database/resolveSchema.mjs +193 -0
  116. package/dist/database/schema.cjs +21 -0
  117. package/dist/database/schema.d.cts +61 -0
  118. package/dist/database/schema.d.mts +61 -0
  119. package/dist/database/schema.mjs +20 -0
  120. package/dist/database/sql/sqlAdapter.cjs +282 -0
  121. package/dist/database/sql/sqlAdapter.d.cts +46 -0
  122. package/dist/database/sql/sqlAdapter.d.mts +46 -0
  123. package/dist/database/sql/sqlAdapter.mjs +279 -0
  124. package/dist/database/sql/sqlSchema.cjs +115 -0
  125. package/dist/database/sql/sqlSchema.d.cts +8 -0
  126. package/dist/database/sql/sqlSchema.d.mts +8 -0
  127. package/dist/database/sql/sqlSchema.mjs +111 -0
  128. package/dist/db/builtInDatabase.cjs +104 -0
  129. package/dist/db/builtInDatabase.d.cts +38 -0
  130. package/dist/db/builtInDatabase.d.mts +38 -0
  131. package/dist/db/builtInDatabase.mjs +97 -0
  132. package/dist/db/createBundleDiff.cjs +6 -6
  133. package/dist/db/createBundleDiff.d.cts +4 -7
  134. package/dist/db/createBundleDiff.d.mts +4 -7
  135. package/dist/db/createBundleDiff.mjs +5 -5
  136. package/dist/db/engineDrizzleSchema.cjs +54 -0
  137. package/dist/db/engineDrizzleSchema.mjs +54 -0
  138. package/dist/db/engineMigrator.cjs +53 -0
  139. package/dist/db/engineMigrator.mjs +53 -0
  140. package/dist/db/enginePrismaSchema.cjs +85 -0
  141. package/dist/db/enginePrismaSchema.d.cts +5 -0
  142. package/dist/db/enginePrismaSchema.d.mts +5 -0
  143. package/dist/db/enginePrismaSchema.mjs +84 -0
  144. package/dist/db/engineSql.cjs +26 -0
  145. package/dist/db/engineSql.d.cts +17 -0
  146. package/dist/db/engineSql.d.mts +17 -0
  147. package/dist/db/engineSql.mjs +25 -0
  148. package/dist/db/engineSqlMigrator.cjs +66 -0
  149. package/dist/db/engineSqlMigrator.mjs +66 -0
  150. package/dist/db/fixedMigratorShared.cjs +0 -11
  151. package/dist/db/fixedMigratorShared.mjs +1 -8
  152. package/dist/db/index.cjs +23 -8
  153. package/dist/db/index.d.cts +10 -4
  154. package/dist/db/index.d.mts +10 -4
  155. package/dist/db/index.mjs +16 -8
  156. package/dist/db/releaseCatalog.cjs +20 -44
  157. package/dist/db/releaseCatalog.d.cts +2 -3
  158. package/dist/db/releaseCatalog.d.mts +2 -3
  159. package/dist/db/releaseCatalog.mjs +20 -44
  160. package/dist/db/schemaSettings.cjs +63 -0
  161. package/dist/db/schemaSettings.d.cts +12 -0
  162. package/dist/db/schemaSettings.d.mts +12 -0
  163. package/dist/db/schemaSettings.mjs +62 -0
  164. package/dist/db/settingsMigrator.cjs +92 -0
  165. package/dist/db/settingsMigrator.mjs +89 -0
  166. package/dist/db/types.cjs +3 -12
  167. package/dist/db/types.d.cts +26 -69
  168. package/dist/db/types.d.mts +26 -69
  169. package/dist/db/types.mjs +2 -11
  170. package/dist/db/updateArtifacts.cjs +38 -68
  171. package/dist/db/updateArtifacts.mjs +40 -69
  172. package/dist/handler.cjs +72 -43
  173. package/dist/handler.d.cts +3 -6
  174. package/dist/handler.d.mts +3 -6
  175. package/dist/handler.mjs +73 -43
  176. package/dist/handlerAdminRoutes.cjs +375 -0
  177. package/dist/handlerAdminRoutes.mjs +373 -0
  178. package/dist/handlerParameters.cjs +6 -11
  179. package/dist/handlerParameters.mjs +6 -10
  180. package/dist/handlerReleaseCatalogRoutes.cjs +11 -14
  181. package/dist/handlerReleaseCatalogRoutes.mjs +11 -13
  182. package/dist/handlerTypes.d.cts +2 -56
  183. package/dist/handlerTypes.d.mts +2 -56
  184. package/dist/handlerVersionRoutes.cjs +20 -4
  185. package/dist/handlerVersionRoutes.d.cts +1 -0
  186. package/dist/handlerVersionRoutes.d.mts +1 -0
  187. package/dist/handlerVersionRoutes.mjs +20 -4
  188. package/dist/index.cjs +4 -7
  189. package/dist/index.d.cts +10 -9
  190. package/dist/index.d.mts +10 -9
  191. package/dist/index.mjs +4 -4
  192. package/dist/insights/domain.d.cts +53 -70
  193. package/dist/insights/domain.d.mts +53 -70
  194. package/dist/insights/errors.cjs +35 -8
  195. package/dist/insights/errors.mjs +35 -8
  196. package/dist/insights/eventInput.cjs +64 -24
  197. package/dist/insights/eventInput.mjs +63 -25
  198. package/dist/insights/provider.cjs +356 -0
  199. package/dist/insights/provider.d.cts +6 -0
  200. package/dist/insights/provider.d.mts +6 -0
  201. package/dist/insights/provider.mjs +355 -0
  202. package/dist/insights/queryInput.cjs +78 -43
  203. package/dist/insights/queryInput.mjs +76 -40
  204. package/dist/insights/routes.cjs +94 -34
  205. package/dist/insights/routes.mjs +93 -35
  206. package/dist/insights/types.d.cts +27 -12
  207. package/dist/insights/types.d.mts +27 -12
  208. package/dist/node.d.cts +1 -2
  209. package/dist/node.d.mts +1 -2
  210. package/dist/package.cjs +1 -1
  211. package/dist/package.mjs +1 -1
  212. package/dist/plugins/api-keys/index.cjs +50 -0
  213. package/dist/plugins/api-keys/index.d.cts +85 -0
  214. package/dist/plugins/api-keys/index.d.mts +85 -0
  215. package/dist/plugins/api-keys/index.mjs +47 -0
  216. package/dist/plugins/api-keys/model.cjs +58 -0
  217. package/dist/plugins/api-keys/model.d.cts +8 -0
  218. package/dist/plugins/api-keys/model.d.mts +8 -0
  219. package/dist/plugins/api-keys/model.mjs +58 -0
  220. package/dist/plugins/api-keys/schema.cjs +38 -0
  221. package/dist/plugins/api-keys/schema.d.cts +44 -0
  222. package/dist/plugins/api-keys/schema.d.mts +44 -0
  223. package/dist/plugins/api-keys/schema.mjs +39 -0
  224. package/dist/plugins/builtIn.cjs +7 -0
  225. package/dist/plugins/builtIn.mjs +6 -0
  226. package/dist/plugins/definePlugin.cjs +5 -0
  227. package/dist/plugins/definePlugin.d.cts +73 -0
  228. package/dist/plugins/definePlugin.d.mts +73 -0
  229. package/dist/plugins/definePlugin.mjs +5 -0
  230. package/dist/plugins/index.cjs +17 -0
  231. package/dist/plugins/index.d.cts +9 -0
  232. package/dist/plugins/index.d.mts +9 -0
  233. package/dist/plugins/index.mjs +7 -0
  234. package/dist/plugins/insights/index.cjs +48 -0
  235. package/dist/plugins/insights/index.d.cts +295 -0
  236. package/dist/plugins/insights/index.d.mts +295 -0
  237. package/dist/plugins/insights/index.mjs +44 -0
  238. package/dist/plugins/insights/model.cjs +17 -0
  239. package/dist/plugins/insights/model.d.cts +10 -0
  240. package/dist/plugins/insights/model.d.mts +10 -0
  241. package/dist/plugins/insights/model.mjs +17 -0
  242. package/dist/plugins/insights/reads.cjs +471 -0
  243. package/dist/plugins/insights/reads.mjs +466 -0
  244. package/dist/plugins/insights/recordEvent.cjs +135 -0
  245. package/dist/plugins/insights/recordEvent.d.cts +13 -0
  246. package/dist/plugins/insights/recordEvent.d.mts +13 -0
  247. package/dist/plugins/insights/recordEvent.mjs +132 -0
  248. package/dist/plugins/insights/schema.cjs +347 -0
  249. package/dist/plugins/insights/schema.d.cts +258 -0
  250. package/dist/plugins/insights/schema.d.mts +258 -0
  251. package/dist/plugins/insights/schema.mjs +344 -0
  252. package/dist/storageAccess.cjs +0 -1
  253. package/dist/types/index.d.cts +2 -29
  254. package/dist/types/index.d.mts +2 -29
  255. package/package.json +30 -7
  256. package/src/adapters/drizzle.conformance.integration.spec.ts +34 -0
  257. package/src/adapters/drizzle.readBudgets.integration.spec.ts +41 -0
  258. package/src/adapters/drizzle.spec.ts +277 -208
  259. package/src/adapters/drizzle.ts +48 -128
  260. package/src/adapters/drizzleExecutor.ts +166 -0
  261. package/src/adapters/kysely.conformance.integration.spec.ts +36 -0
  262. package/src/adapters/kysely.readBudgets.integration.spec.ts +42 -0
  263. package/src/adapters/kysely.spec.ts +212 -117
  264. package/src/adapters/kysely.ts +32 -87
  265. package/src/adapters/kyselyExecutor.ts +53 -0
  266. package/src/adapters/mongodb.integration.spec.ts +180 -0
  267. package/src/adapters/mongodb.readBudgets.integration.spec.ts +61 -0
  268. package/src/adapters/mongodb.ts +36 -65
  269. package/src/adapters/mongodbAdapter.ts +293 -0
  270. package/src/adapters/prisma.conformance.integration.spec.ts +36 -0
  271. package/src/adapters/prisma.integration.spec.ts +138 -0
  272. package/src/adapters/prisma.readBudgets.integration.spec.ts +40 -0
  273. package/src/adapters/prisma.spec.ts +226 -173
  274. package/src/adapters/prisma.ts +60 -451
  275. package/src/adapters/prismaExecutor.ts +107 -0
  276. package/src/adapters/prismaTestClients.ts +175 -0
  277. package/src/adapters/sqlProviders.ts +14 -0
  278. package/src/apiKeys.integration.spec.ts +19 -119
  279. package/src/apiKeys.spec.ts +39 -35
  280. package/src/apiKeys.ts +2 -8
  281. package/src/assembly/assemblePlugins.ts +269 -0
  282. package/src/assembly/assembly.spec.ts +325 -0
  283. package/src/assembly/assembly.types.spec.ts +155 -0
  284. package/src/assembly/builtInRoutes.spec.ts +213 -0
  285. package/src/assembly/databasePlugins.spec.ts +71 -0
  286. package/src/assembly/databasePlugins.ts +96 -0
  287. package/src/core/api.ts +139 -0
  288. package/src/core/baseCandidates.spec.ts +378 -0
  289. package/src/core/baseCandidates.ts +136 -0
  290. package/src/core/index.ts +44 -0
  291. package/src/core/operations.spec.ts +415 -0
  292. package/src/core/operations.ts +551 -0
  293. package/src/core/reads.spec.ts +300 -0
  294. package/src/core/reads.ts +417 -0
  295. package/src/core/releases.spec.ts +308 -0
  296. package/src/core/releases.ts +345 -0
  297. package/src/core/schema.ts +173 -0
  298. package/src/core/writes.ts +160 -0
  299. package/src/createHotUpdaterCore.ts +191 -167
  300. package/src/database/cursor.ts +65 -0
  301. package/src/database/database.ts +335 -0
  302. package/src/database/database.types.spec.ts +135 -0
  303. package/src/database/definitions.ts +72 -0
  304. package/src/database/engine.ts +62 -0
  305. package/src/database/engineAggregates.spec.ts +539 -0
  306. package/src/database/engineAggregates.ts +193 -0
  307. package/src/database/engineReads.spec.ts +219 -0
  308. package/src/database/engineReads.ts +302 -0
  309. package/src/database/engineTransaction.spec.ts +710 -0
  310. package/src/database/engineTransaction.ts +588 -0
  311. package/src/database/errors.ts +30 -0
  312. package/src/database/fence.spec.ts +223 -0
  313. package/src/database/fence.ts +171 -0
  314. package/src/database/index.ts +154 -0
  315. package/src/database/kv/kvAdapter.conformance.integration.spec.ts +17 -0
  316. package/src/database/kv/kvAdapter.readBudgets.integration.spec.ts +17 -0
  317. package/src/database/kv/kvAdapter.spec.ts +468 -0
  318. package/src/database/kv/kvAdapter.ts +450 -0
  319. package/src/database/kv/kvTestStore.ts +91 -0
  320. package/src/database/memory.readBudgets.integration.spec.ts +13 -0
  321. package/src/database/resolveSchema.spec.ts +230 -0
  322. package/src/database/resolveSchema.ts +372 -0
  323. package/src/database/schema.ts +169 -0
  324. package/src/database/schema.types.spec.ts +94 -0
  325. package/src/database/sql/docker-compose.yml +36 -0
  326. package/src/database/sql/sqlAdapter.integration.spec.ts +148 -0
  327. package/src/database/sql/sqlAdapter.readBudgets.integration.spec.ts +109 -0
  328. package/src/database/sql/sqlAdapter.spec.ts +255 -0
  329. package/src/database/sql/sqlAdapter.ts +532 -0
  330. package/src/database/sql/sqlSchema.ts +205 -0
  331. package/src/database/sql/sqlTestExecutors.ts +183 -0
  332. package/src/databaseTestSuite.spec.ts +39 -0
  333. package/src/db/builtInDatabase.ts +149 -0
  334. package/src/db/bundleRows.spec.ts +3 -3
  335. package/src/db/createBundleDiff.spec.ts +43 -29
  336. package/src/db/createBundleDiff.ts +13 -7
  337. package/src/db/engineDrizzleSchema.ts +72 -0
  338. package/src/db/engineMigrator.ts +105 -0
  339. package/src/db/enginePrismaSchema.ts +147 -0
  340. package/src/db/engineSql.spec.ts +129 -0
  341. package/src/db/engineSql.ts +48 -0
  342. package/src/db/engineSqlMigrator.ts +116 -0
  343. package/src/db/fixedMigratorShared.ts +1 -20
  344. package/src/db/index.spec.ts +116 -824
  345. package/src/db/index.ts +43 -14
  346. package/src/db/pluginTables.spec.ts +218 -0
  347. package/src/db/releaseCatalog.ts +48 -90
  348. package/src/db/schemaSettings.ts +104 -0
  349. package/src/db/settingsMigrator.spec.ts +65 -0
  350. package/src/db/settingsMigrator.ts +152 -0
  351. package/src/db/types.ts +25 -165
  352. package/src/db/updateArtifacts.spec.ts +170 -294
  353. package/src/db/updateArtifacts.ts +74 -142
  354. package/src/db/updateCheck.bench.ts +47 -0
  355. package/src/handler-standalone.integration.spec.ts +204 -412
  356. package/src/handler.spec.ts +134 -51
  357. package/src/handler.testFixtures.ts +14 -19
  358. package/src/handler.ts +163 -75
  359. package/src/handlerAdminRoutes.spec.ts +183 -0
  360. package/src/handlerAdminRoutes.ts +536 -0
  361. package/src/handlerAuth.integration.spec.ts +3 -3
  362. package/src/handlerManagement.spec.ts +136 -196
  363. package/src/handlerReleaseCatalogRoutes.ts +15 -17
  364. package/src/handlerTypes.ts +3 -80
  365. package/src/handlerVersionRoutes.ts +20 -7
  366. package/src/index.ts +7 -14
  367. package/src/insights/domain.ts +56 -77
  368. package/src/insights/errors.spec.ts +48 -0
  369. package/src/insights/errors.ts +50 -6
  370. package/src/insights/eventInput.ts +80 -27
  371. package/src/insights/insights.integration.spec.ts +525 -44
  372. package/src/insights/provider.spec.ts +574 -0
  373. package/src/insights/provider.ts +667 -0
  374. package/src/insights/queryInput.ts +113 -73
  375. package/src/insights/routes.ts +125 -69
  376. package/src/insights/types.ts +47 -36
  377. package/src/lintRules.spec.ts +76 -0
  378. package/src/plugins/api-keys/apiKeysPlugin.spec.ts +172 -0
  379. package/src/plugins/api-keys/index.ts +66 -0
  380. package/src/plugins/api-keys/model.ts +69 -0
  381. package/src/plugins/api-keys/schema.ts +22 -0
  382. package/src/plugins/builtIn.ts +5 -0
  383. package/src/plugins/definePlugin.ts +100 -0
  384. package/src/plugins/index.ts +55 -0
  385. package/src/plugins/insights/index.ts +86 -0
  386. package/src/plugins/insights/insightsRollout.integration.spec.ts +187 -0
  387. package/src/plugins/insights/model.spec.ts +66 -0
  388. package/src/plugins/insights/model.ts +19 -0
  389. package/src/plugins/insights/reads.spec.ts +725 -0
  390. package/src/plugins/insights/reads.ts +707 -0
  391. package/src/plugins/insights/recordEvent.spec.ts +443 -0
  392. package/src/plugins/insights/recordEvent.ts +238 -0
  393. package/src/plugins/insights/schema.ts +229 -0
  394. package/src/plugins/pluginTestHarness.spec.ts +56 -0
  395. package/src/readBudgets.testFixtures.ts +15 -0
  396. package/src/releaseCatalogRoutes.integration.spec.ts +47 -92
  397. package/src/runtime.spec.ts +91 -77
  398. package/src/runtime.testFixtures.ts +28 -44
  399. package/src/runtimeBundles.integration.spec.ts +112 -0
  400. package/src/testUtilsMutation.integration.spec.ts +215 -0
  401. package/src/testUtilsPackage.integration.spec.ts +176 -0
  402. package/src/types/index.ts +0 -32
  403. package/dist/adapters/databaseConstraintErrors.cjs +0 -23
  404. package/dist/adapters/databaseConstraintErrors.mjs +0 -21
  405. package/dist/adapters/databasePluginUtils.cjs +0 -117
  406. package/dist/adapters/databasePluginUtils.mjs +0 -106
  407. package/dist/adapters/drizzleCrud.cjs +0 -250
  408. package/dist/adapters/drizzleCrud.mjs +0 -249
  409. package/dist/adapters/drizzleLazyDB.cjs +0 -101
  410. package/dist/adapters/drizzleLazyDB.mjs +0 -101
  411. package/dist/adapters/drizzleQuery.cjs +0 -68
  412. package/dist/adapters/drizzleQuery.mjs +0 -67
  413. package/dist/adapters/kyselyCrud.cjs +0 -254
  414. package/dist/adapters/kyselyCrud.mjs +0 -253
  415. package/dist/adapters/kyselyQuery.cjs +0 -56
  416. package/dist/adapters/kyselyQuery.mjs +0 -55
  417. package/dist/adapters/mongodbCollections.cjs +0 -36
  418. package/dist/adapters/mongodbCollections.mjs +0 -30
  419. package/dist/adapters/mongodbQuery.cjs +0 -77
  420. package/dist/adapters/mongodbQuery.mjs +0 -70
  421. package/dist/adapters/mongodbReads.cjs +0 -148
  422. package/dist/adapters/mongodbReads.mjs +0 -147
  423. package/dist/adapters/mongodbWrites.cjs +0 -134
  424. package/dist/adapters/mongodbWrites.mjs +0 -133
  425. package/dist/adapters/prismaQuery.cjs +0 -58
  426. package/dist/adapters/prismaQuery.mjs +0 -56
  427. package/dist/adapters/prismaRows.cjs +0 -198
  428. package/dist/adapters/prismaRows.mjs +0 -188
  429. package/dist/db/databasePluginCore.cjs +0 -138
  430. package/dist/db/databasePluginCore.mjs +0 -137
  431. package/dist/db/fixedMigrator.cjs +0 -79
  432. package/dist/db/fixedMigrator.mjs +0 -78
  433. package/dist/db/fixedMigratorMongo.cjs +0 -319
  434. package/dist/db/fixedMigratorMongo.mjs +0 -319
  435. package/dist/db/mongoMigrationExecution.cjs +0 -11
  436. package/dist/db/mongoMigrationExecution.mjs +0 -11
  437. package/dist/db/schema/mongodb.cjs +0 -25
  438. package/dist/db/schema/mongodb.mjs +0 -25
  439. package/dist/db/schema/registry.cjs +0 -21
  440. package/dist/db/schema/registry.mjs +0 -17
  441. package/dist/db/schema/sql.cjs +0 -101
  442. package/dist/db/schema/sql.mjs +0 -97
  443. package/dist/db/schema/sqlOperations.cjs +0 -27
  444. package/dist/db/schema/sqlOperations.mjs +0 -26
  445. package/dist/db/schemaGenerators.cjs +0 -256
  446. package/dist/db/schemaGenerators.mjs +0 -254
  447. package/dist/db/schemaReadiness.cjs +0 -24
  448. package/dist/db/schemaReadiness.d.cts +0 -8
  449. package/dist/db/schemaReadiness.d.mts +0 -8
  450. package/dist/db/schemaReadiness.mjs +0 -23
  451. package/dist/db/sqlMigrationExecution.cjs +0 -67
  452. package/dist/db/sqlMigrationExecution.mjs +0 -66
  453. package/dist/handlerBundleRoutes.cjs +0 -155
  454. package/dist/handlerBundleRoutes.mjs +0 -155
  455. package/dist/handlerReleaseManagementRoutes.cjs +0 -125
  456. package/dist/handlerReleaseManagementRoutes.mjs +0 -124
  457. package/dist/insights/bounded/activeOverview.cjs +0 -87
  458. package/dist/insights/bounded/activeOverview.mjs +0 -85
  459. package/dist/insights/bounded/installationSearch.cjs +0 -42
  460. package/dist/insights/bounded/installationSearch.mjs +0 -42
  461. package/dist/insights/bounded/persistence.cjs +0 -39
  462. package/dist/insights/bounded/persistence.mjs +0 -38
  463. package/dist/insights/bounded/provider.cjs +0 -170
  464. package/dist/insights/bounded/provider.d.cts +0 -7
  465. package/dist/insights/bounded/provider.d.mts +0 -7
  466. package/dist/insights/bounded/provider.mjs +0 -170
  467. package/dist/insights/bounded/scan.cjs +0 -144
  468. package/dist/insights/bounded/scan.mjs +0 -138
  469. package/dist/insights/persistence.d.cts +0 -6
  470. package/dist/insights/persistence.d.mts +0 -6
  471. package/dist/schema/dsl.cjs +0 -103
  472. package/dist/schema/dsl.mjs +0 -87
  473. package/dist/schema/index.cjs +0 -6
  474. package/dist/schema/index.mjs +0 -7
  475. package/dist/schema/settings.cjs +0 -9
  476. package/dist/schema/settings.mjs +0 -9
  477. package/dist/schema/types.cjs +0 -8
  478. package/dist/schema/types.mjs +0 -6
  479. package/dist/schema/v1_0_0.cjs +0 -364
  480. package/dist/schema/v1_0_0.mjs +0 -357
  481. package/src/adapters/databaseConstraintErrors.ts +0 -25
  482. package/src/adapters/databasePluginTestDatabase.ts +0 -106
  483. package/src/adapters/databasePluginUtils.spec.ts +0 -29
  484. package/src/adapters/databasePluginUtils.ts +0 -214
  485. package/src/adapters/drizzleCrud.ts +0 -419
  486. package/src/adapters/drizzleLazyDB.ts +0 -257
  487. package/src/adapters/drizzleQuery.ts +0 -164
  488. package/src/adapters/kyselyCrud.ts +0 -588
  489. package/src/adapters/kyselyQuery.ts +0 -128
  490. package/src/adapters/mongodb.spec.ts +0 -141
  491. package/src/adapters/mongodbCollections.ts +0 -62
  492. package/src/adapters/mongodbQuery.ts +0 -226
  493. package/src/adapters/mongodbReads.ts +0 -311
  494. package/src/adapters/mongodbRows.spec.ts +0 -64
  495. package/src/adapters/mongodbRows.ts +0 -118
  496. package/src/adapters/mongodbTestClient.ts +0 -342
  497. package/src/adapters/mongodbTestFilter.ts +0 -166
  498. package/src/adapters/mongodbWrites.ts +0 -260
  499. package/src/adapters/prismaQuery.ts +0 -129
  500. package/src/adapters/prismaRows.spec.ts +0 -98
  501. package/src/adapters/prismaRows.ts +0 -301
  502. package/src/adapters/prismaTestClient.ts +0 -396
  503. package/src/calculatePagination.ts +0 -34
  504. package/src/db/databasePluginCore.spec.ts +0 -62
  505. package/src/db/databasePluginCore.testFixtures.ts +0 -76
  506. package/src/db/databasePluginCore.ts +0 -185
  507. package/src/db/databasePluginUpdateCheck.bench.ts +0 -107
  508. package/src/db/fixedMigrator.spec.ts +0 -231
  509. package/src/db/fixedMigrator.ts +0 -147
  510. package/src/db/fixedMigratorMongo.spec.ts +0 -192
  511. package/src/db/fixedMigratorMongo.ts +0 -425
  512. package/src/db/hotUpdaterSchema.ts +0 -5
  513. package/src/db/mongoMigrationExecution.ts +0 -23
  514. package/src/db/schema/definitions.ts +0 -1
  515. package/src/db/schema/mongodb.ts +0 -40
  516. package/src/db/schema/registry.ts +0 -55
  517. package/src/db/schema/sql.ts +0 -241
  518. package/src/db/schema/sqlOperations.ts +0 -63
  519. package/src/db/schema/types.ts +0 -1
  520. package/src/db/schemaGenerators.ts +0 -466
  521. package/src/db/schemaReadiness.ts +0 -34
  522. package/src/db/sqlMigrationExecution.spec.ts +0 -65
  523. package/src/db/sqlMigrationExecution.ts +0 -108
  524. package/src/db/types.spec.ts +0 -55
  525. package/src/handlerBundleRoutes.ts +0 -259
  526. package/src/handlerReleaseManagementRoutes.ts +0 -213
  527. package/src/insights/bounded/activeOverview.spec.ts +0 -189
  528. package/src/insights/bounded/activeOverview.ts +0 -148
  529. package/src/insights/bounded/installationSearch.spec.ts +0 -130
  530. package/src/insights/bounded/installationSearch.ts +0 -69
  531. package/src/insights/bounded/persistence.ts +0 -43
  532. package/src/insights/bounded/provider.spec.ts +0 -219
  533. package/src/insights/bounded/provider.ts +0 -229
  534. package/src/insights/bounded/scan.ts +0 -235
  535. package/src/insights/persistence.ts +0 -10
  536. package/src/schema/dsl-all-versions.spec.ts +0 -35
  537. package/src/schema/dsl.ts +0 -187
  538. package/src/schema/index.ts +0 -10
  539. package/src/schema/settings.ts +0 -15
  540. package/src/schema/types.ts +0 -79
  541. package/src/schema/v1_0_0.spec.ts +0 -146
  542. package/src/schema/v1_0_0.ts +0 -465
@@ -1,102 +1,142 @@
1
1
  import type {
2
- ActiveInstallationInput,
3
- BundleEventInsightsWindow,
2
+ ActiveInstallationWindow,
3
+ InsightsBundleSelection,
4
+ InsightsScope,
4
5
  } from "./domain";
5
6
  import { InsightsBadRequestError } from "./errors";
7
+ import type {
8
+ InsightsEventPageInput,
9
+ InsightsUserInstallationPageInput,
10
+ } from "./types";
6
11
 
7
- type IntegerBounds = {
8
- readonly defaultValue: number;
9
- readonly minimum: number;
10
- readonly maximum?: number;
11
- };
12
+ const MAX_PAGE_LIMIT = 100;
13
+ const MAX_IDENTITY_LENGTH = 255;
14
+ const MAX_CURSOR_LENGTH = 8 * 1_024;
12
15
 
13
- const EVENT_LIST_BOUNDS = {
14
- defaultValue: 50,
15
- maximum: 100,
16
- minimum: 1,
17
- } as const satisfies IntegerBounds;
18
- const EVENT_LIST_OFFSET_BOUNDS = {
19
- defaultValue: 0,
20
- minimum: 0,
21
- } as const satisfies IntegerBounds;
22
- const MAX_USER_ID_LENGTH = 1_024;
16
+ const readSingle = (url: URL, key: string): string | undefined => {
17
+ const values = url.searchParams.getAll(key);
18
+ if (values.length > 1) {
19
+ throw new InsightsBadRequestError(`Duplicate '${key}' query parameter.`);
20
+ }
21
+ return values[0];
22
+ };
23
23
 
24
- export type PaginationInput = {
25
- readonly limit: number;
26
- readonly offset: number;
24
+ const readPageLimit = (url: URL): number | undefined => {
25
+ const value = readSingle(url, "limit");
26
+ if (value === undefined) return undefined;
27
+ const limit = Number(value);
28
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > MAX_PAGE_LIMIT) {
29
+ throw new InsightsBadRequestError("Invalid 'limit' query parameter.");
30
+ }
31
+ return limit;
27
32
  };
28
33
 
29
- export type InsightsQueryInput = PaginationInput & {
30
- readonly window: BundleEventInsightsWindow;
34
+ const readCursor = (url: URL): string | undefined => {
35
+ const cursor = readSingle(url, "cursor");
36
+ if (
37
+ cursor !== undefined &&
38
+ (cursor.length === 0 || cursor.length > MAX_CURSOR_LENGTH)
39
+ ) {
40
+ throw new InsightsBadRequestError("Invalid 'cursor' query parameter.");
41
+ }
42
+ return cursor;
31
43
  };
32
44
 
33
- function parseInteger(url: URL, key: string, bounds: IntegerBounds): number {
34
- const value = url.searchParams.get(key);
35
- if (value === null) return bounds.defaultValue;
36
- const parsed = Number(value);
45
+ const readId = (
46
+ url: URL,
47
+ key: string,
48
+ maximumLength = MAX_IDENTITY_LENGTH,
49
+ ): string => {
50
+ const value = readSingle(url, key);
37
51
  if (
38
- !Number.isSafeInteger(parsed) ||
39
- parsed < bounds.minimum ||
40
- (bounds.maximum !== undefined && parsed > bounds.maximum)
52
+ value === undefined ||
53
+ value.length === 0 ||
54
+ value.length > maximumLength
41
55
  ) {
42
56
  throw new InsightsBadRequestError(`Invalid '${key}' query parameter.`);
43
57
  }
44
- return parsed;
45
- }
58
+ return value;
59
+ };
60
+
61
+ const readTimestamp = (url: URL, key: string): number | undefined => {
62
+ const raw = readSingle(url, key);
63
+ if (raw === undefined) return undefined;
64
+ const value = Number(raw);
65
+ if (!raw.length || !Number.isSafeInteger(value) || value < 0) {
66
+ throw new InsightsBadRequestError(`Invalid '${key}' query parameter.`);
67
+ }
68
+ return value;
69
+ };
46
70
 
47
- export const parsePagination = (request: Request): PaginationInput => {
71
+ const readScope = (url: URL): InsightsScope => {
72
+ const platform = readSingle(url, "platform");
73
+ if (platform !== "ios" && platform !== "android") {
74
+ throw new InsightsBadRequestError("Invalid 'platform' query parameter.");
75
+ }
76
+ return { platform, channel: readId(url, "channel", 1_024) };
77
+ };
78
+
79
+ export const parseEventPageInput = (
80
+ request: Request,
81
+ ): InsightsEventPageInput => {
48
82
  const url = new URL(request.url);
83
+ const bundleFields = ["bundleId", "outcome", "platform", "channel"];
84
+ let bundle: InsightsBundleSelection | undefined;
85
+ if (bundleFields.some((key) => url.searchParams.has(key))) {
86
+ const outcome = readSingle(url, "outcome");
87
+ if (
88
+ outcome !== "downloaded" &&
89
+ outcome !== "applied" &&
90
+ outcome !== "recovered" &&
91
+ outcome !== "unchanged"
92
+ ) {
93
+ throw new InsightsBadRequestError("Invalid 'outcome' query parameter.");
94
+ }
95
+ bundle = {
96
+ ...readScope(url),
97
+ bundleId: readId(url, "bundleId", 1_024),
98
+ outcome,
99
+ };
100
+ }
49
101
  return {
50
- limit: parseInteger(url, "limit", EVENT_LIST_BOUNDS),
51
- offset: parseInteger(url, "offset", EVENT_LIST_OFFSET_BOUNDS),
102
+ beforeReceivedAtMs: readTimestamp(url, "beforeReceivedAtMs"),
103
+ sinceMs: readTimestamp(url, "sinceMs"),
104
+ cursor: readCursor(url),
105
+ limit: readPageLimit(url),
106
+ ...(bundle === undefined ? {} : { bundle }),
52
107
  };
53
108
  };
54
109
 
55
- export const parseInsightsQuery = (request: Request): InsightsQueryInput => {
110
+ export const parseUserInstallationPageInput = (
111
+ request: Request,
112
+ ): InsightsUserInstallationPageInput => {
56
113
  const url = new URL(request.url);
57
- const window = url.searchParams.get("window") ?? "24h";
58
- if (
59
- window !== "24h" &&
60
- window !== "7d" &&
61
- window !== "30d" &&
62
- window !== "all"
63
- ) {
64
- throw new InsightsBadRequestError("Invalid 'window' query parameter.");
65
- }
66
- return { ...parsePagination(request), window };
114
+ const cursor = readCursor(url);
115
+ const limit = readPageLimit(url);
116
+ return {
117
+ userId: readId(url, "userId"),
118
+ ...(cursor === undefined ? {} : { cursor }),
119
+ ...(limit === undefined ? {} : { limit }),
120
+ };
67
121
  };
68
122
 
69
- export const parseActiveInstallationInput = (
123
+ export const parseReportingOverviewInput = (
70
124
  request: Request,
71
- ): ActiveInstallationInput => {
125
+ ): InsightsScope & {
126
+ readonly window: ActiveInstallationWindow;
127
+ readonly bundleId?: string;
128
+ } => {
72
129
  const url = new URL(request.url);
73
- const windows = url.searchParams.getAll("window");
74
- if (windows.length > 1) {
75
- throw new InsightsBadRequestError("Duplicate 'window' query parameter.");
76
- }
77
- const window = windows[0] ?? "30d";
130
+ const window = readSingle(url, "window") ?? "30d";
78
131
  if (window !== "24h" && window !== "7d" && window !== "30d") {
79
132
  throw new InsightsBadRequestError("Invalid 'window' query parameter.");
80
133
  }
81
- const userIds = url.searchParams.getAll("userId");
82
- if (userIds.length > 1) {
83
- throw new InsightsBadRequestError("Duplicate 'userId' query parameter.");
84
- }
85
- const userId = userIds[0];
86
- if (
87
- userId !== undefined &&
88
- (userId.length === 0 || userId.length > MAX_USER_ID_LENGTH)
89
- ) {
90
- throw new InsightsBadRequestError("Invalid 'userId' query parameter.");
91
- }
92
- return userId === undefined ? { window } : { window, userId };
134
+ const bundleId = readSingle(url, "bundleId");
135
+ return {
136
+ ...readScope(url),
137
+ window,
138
+ ...(bundleId === undefined
139
+ ? {}
140
+ : { bundleId: readId(url, "bundleId", 1_024) }),
141
+ };
93
142
  };
94
-
95
- export const parseSearchInput = (
96
- request: Request,
97
- ): PaginationInput & { readonly query: string } => ({
98
- ...parsePagination(request),
99
- query: new URL(request.url).searchParams.get("query")?.trim() ?? "",
100
- });
101
-
102
- export const parseEmptyInput = (): undefined => undefined;
@@ -1,21 +1,26 @@
1
- import type { RouteHandler } from "../handlerTypes";
2
1
  import {
3
2
  InsightsBadRequestError,
4
3
  InsightsPayloadTooLargeError,
5
- InsightsScanLimitExceededError,
4
+ isDatabaseBusyError,
6
5
  } from "./errors";
7
6
  import { parseBundleEventRequest } from "./eventInput";
8
7
  import {
9
- parseActiveInstallationInput,
10
- parseInsightsQuery,
11
- parsePagination,
12
- parseSearchInput,
8
+ parseReportingOverviewInput,
9
+ parseEventPageInput,
10
+ parseUserInstallationPageInput,
13
11
  } from "./queryInput";
14
12
  import type { InsightsProvider } from "./types";
15
13
 
16
- const json = (body: unknown, status: number): Response =>
14
+ /** Seconds a client waits before it resends a request the busy database refused. */
15
+ const RETRY_AFTER_SECONDS = 5;
16
+
17
+ const json = (
18
+ body: unknown,
19
+ status: number,
20
+ headers: Readonly<Record<string, string>> = {},
21
+ ): Response =>
17
22
  Response.json(body, {
18
- headers: { "cache-control": "private, no-store" },
23
+ headers: { "cache-control": "private, no-store", ...headers },
19
24
  status,
20
25
  });
21
26
 
@@ -27,7 +32,11 @@ const requireParam = (
27
32
  if (value === undefined || value.length === 0) {
28
33
  throw new InsightsBadRequestError(`Missing route parameter: ${key}`);
29
34
  }
30
- return value;
35
+ try {
36
+ return decodeURIComponent(value);
37
+ } catch {
38
+ throw new InsightsBadRequestError(`Invalid route parameter: ${key}`);
39
+ }
31
40
  };
32
41
 
33
42
  const run = async (operation: () => Promise<Response>): Promise<Response> => {
@@ -40,16 +49,17 @@ const run = async (operation: () => Promise<Response>): Promise<Response> => {
40
49
  if (error instanceof InsightsPayloadTooLargeError) {
41
50
  return json({ error: error.message }, 413);
42
51
  }
43
- if (error instanceof InsightsScanLimitExceededError) {
44
- return json(
45
- {
46
- error: {
47
- code: "INSIGHTS_SCAN_LIMIT_EXCEEDED",
48
- limit: error.limit,
49
- },
50
- },
51
- 503,
52
+ // Back-pressure: a busy database has not failed the request, so the
53
+ // client may send it again after Retry-After. A 500 would read as a fault.
54
+ // The warning keeps the overload visible to whoever runs the server.
55
+ if (isDatabaseBusyError(error)) {
56
+ console.warn(
57
+ "[hot-updater] Insights answered 503: the database is busy.",
58
+ error,
52
59
  );
60
+ return json({ error: "Service unavailable" }, 503, {
61
+ "retry-after": String(RETRY_AFTER_SECONDS),
62
+ });
53
63
  }
54
64
  throw error;
55
65
  }
@@ -58,66 +68,112 @@ const run = async (operation: () => Promise<Response>): Promise<Response> => {
58
68
  const query = (operation: () => Promise<unknown>): Promise<Response> =>
59
69
  run(async () => json(await operation(), 200));
60
70
 
71
+ /** Every Insights route: where it mounts, and the handler it runs. */
72
+ export const INSIGHTS_ROUTES = [
73
+ {
74
+ access: "client",
75
+ method: "POST",
76
+ path: "/events",
77
+ handler: "appendBundleEvent",
78
+ },
79
+ { access: "admin", method: "GET", path: "/events", handler: "listEvents" },
80
+ {
81
+ access: "admin",
82
+ method: "GET",
83
+ path: "/overview",
84
+ handler: "getReportingOverview",
85
+ },
86
+ {
87
+ access: "admin",
88
+ method: "GET",
89
+ path: "/installations",
90
+ handler: "pageInstallationsByCurrentUserId",
91
+ },
92
+ {
93
+ access: "admin",
94
+ method: "GET",
95
+ path: "/installations/:installId/events",
96
+ handler: "listInstallationEvents",
97
+ },
98
+ {
99
+ access: "admin",
100
+ method: "GET",
101
+ path: "/installations/:installId",
102
+ handler: "getInstallation",
103
+ },
104
+ ] as const;
105
+
106
+ export type InsightsRouteName = (typeof INSIGHTS_ROUTES)[number]["handler"];
107
+
108
+ export type InsightsRouteHandler = (
109
+ params: Readonly<Record<string, string>>,
110
+ request: Request,
111
+ ) => Promise<Response>;
112
+
113
+ /** Without the insights plugin, its routes answer 204 and say Insights is off. */
114
+ export const insightsDisabled: InsightsRouteHandler = async () =>
115
+ new Response(null, {
116
+ headers: {
117
+ "cache-control": "private, no-store",
118
+ "x-hot-updater-insights": "disabled",
119
+ },
120
+ status: 204,
121
+ });
122
+
123
+ /** What a server without the insights plugin logs on the first event it drops. */
124
+ export const INSIGHTS_OFF_WARNING =
125
+ "[hot-updater] Insights is off, so POST /events answered 204 and stored nothing. Add insights() from @hot-updater/server/plugins/insights to plugins to record app events, or set insights: false in the app's HotUpdater.init to stop sending them. This warning prints once per server.";
126
+
127
+ /**
128
+ * The client's `POST /events` without the insights plugin. It answers as
129
+ * {@link insightsDisabled} does, so apps need no change, and warns on the
130
+ * first event it drops, so a server that left out `insights()` says so.
131
+ */
132
+ export const createDroppedEventHandler = (): InsightsRouteHandler => {
133
+ let warned = false;
134
+ return (params, request) => {
135
+ if (!warned) {
136
+ warned = true;
137
+ console.warn(INSIGHTS_OFF_WARNING);
138
+ }
139
+ return insightsDisabled(params, request);
140
+ };
141
+ };
142
+
61
143
  export const createInsightsRouteHandlers = (
62
144
  provider: InsightsProvider,
63
- ): Record<string, RouteHandler> => ({
145
+ ): Record<InsightsRouteName, InsightsRouteHandler> => ({
64
146
  appendBundleEvent: async (_params, request) =>
65
147
  run(async () => {
66
148
  await provider.appendBundleEvent(await parseBundleEventRequest(request));
67
149
  return new Response(null, { status: 204 });
68
150
  }),
69
- getBundleEventSummary: (params) =>
70
- query(() => provider.getBundleEventSummary(requireParam(params, "id"))),
71
- getBundleEventInsights: (params, request) =>
72
- query(() => {
73
- const input = parseInsightsQuery(request);
74
- return provider.getBundleEventInsights(
75
- requireParam(params, "id"),
76
- input.window,
77
- input.limit,
78
- input.offset,
79
- );
151
+ getReportingOverview: (_params, request) =>
152
+ query(() =>
153
+ provider.getReportingOverview(parseReportingOverviewInput(request)),
154
+ ),
155
+ getInstallation: (params) =>
156
+ run(async () => {
157
+ const installation = await provider.getInstallation({
158
+ installId: requireParam(params, "installId"),
159
+ });
160
+ return installation === null
161
+ ? json({ error: "Installation not found" }, 404)
162
+ : json(installation, 200);
80
163
  }),
81
- getBundleEventOverview: () => query(() => provider.getBundleEventOverview()),
82
- getActiveInstallationOverview: (_params, request) =>
164
+ listEvents: (_params, request) =>
165
+ query(() => provider.listEvents(parseEventPageInput(request))),
166
+ listInstallationEvents: (params, request) =>
83
167
  query(() =>
84
- provider.getActiveInstallationOverview(
85
- parseActiveInstallationInput(request),
168
+ provider.listInstallationEvents({
169
+ ...parseEventPageInput(request),
170
+ installId: requireParam(params, "installId"),
171
+ }),
172
+ ),
173
+ pageInstallationsByCurrentUserId: (_params, request) =>
174
+ query(() =>
175
+ provider.pageInstallationsByCurrentUserId(
176
+ parseUserInstallationPageInput(request),
86
177
  ),
87
178
  ),
88
- searchInstallations: (_params, request) =>
89
- query(() => {
90
- const input = parseSearchInput(request);
91
- return provider.searchInstallations(
92
- input.query,
93
- input.limit,
94
- input.offset,
95
- );
96
- }),
97
- getInstallationHistory: (params, request) =>
98
- query(() => {
99
- const input = parsePagination(request);
100
- return provider.getInstallationHistory(
101
- requireParam(params, "installId"),
102
- input.limit,
103
- input.offset,
104
- );
105
- }),
106
179
  });
107
-
108
- export const registerInsightsClientRoutes = (
109
- add: (method: string, path: string, handler: string) => void,
110
- ): void => {
111
- add("POST", "/events", "appendBundleEvent");
112
- };
113
-
114
- export const registerInsightsAdminRoutes = (
115
- add: (method: string, path: string, handler: string) => void,
116
- ): void => {
117
- add("GET", "/bundles/:id/events/summary", "getBundleEventSummary");
118
- add("GET", "/bundles/:id/events/insights", "getBundleEventInsights");
119
- add("GET", "/installations/overview", "getBundleEventOverview");
120
- add("GET", "/installations/active", "getActiveInstallationOverview");
121
- add("GET", "/installations", "searchInstallations");
122
- add("GET", "/installations/:installId/events", "getInstallationHistory");
123
- };
@@ -1,44 +1,55 @@
1
1
  import type {
2
- ActiveInstallationInput,
3
- ActiveInstallationOverview,
4
- BundleEventInsightsResult,
5
- BundleEventInsightsWindow,
6
- BundleEventOverview,
7
- BundleEventSummary,
8
- BundleEventSummaryByBundle,
2
+ ReportingOverview,
3
+ InsightsBundleSelection,
4
+ InsightsScope,
5
+ ActiveInstallationWindow,
9
6
  CreateBundleEventRequest,
7
+ CursorPage,
8
+ EventCursorPage,
9
+ EventHistoryRow,
10
10
  InstallationHistoryRow,
11
- InstallationSearchRow,
12
- OffsetPaginationResult,
11
+ InstallationRow,
13
12
  } from "./domain";
14
13
 
14
+ export type InsightsEventPageInput = {
15
+ readonly beforeReceivedAtMs?: number;
16
+ readonly sinceMs?: number;
17
+ readonly bundle?: InsightsBundleSelection;
18
+ readonly cursor?: string;
19
+ readonly limit?: number;
20
+ };
21
+
22
+ export type InsightsInstallationEventPageInput = Omit<
23
+ InsightsEventPageInput,
24
+ "bundle"
25
+ > & {
26
+ readonly installId: string;
27
+ };
28
+
29
+ export type InsightsUserInstallationPageInput = {
30
+ readonly userId: string;
31
+ readonly cursor?: string;
32
+ readonly limit?: number;
33
+ };
34
+
15
35
  export type InsightsProvider = {
16
- readonly mode: "bounded";
17
- readonly maxMatchingRows: number;
18
36
  appendBundleEvent(input: CreateBundleEventRequest): Promise<void>;
19
- getBundleEventSummary(bundleId: string): Promise<BundleEventSummary>;
20
- getBundleEventSummaries(
21
- bundleIds: readonly string[],
22
- window: BundleEventInsightsWindow,
23
- ): Promise<readonly BundleEventSummaryByBundle[]>;
24
- getBundleEventInsights(
25
- bundleId: string,
26
- window: BundleEventInsightsWindow,
27
- limit: number,
28
- offset: number,
29
- ): Promise<BundleEventInsightsResult>;
30
- getBundleEventOverview(): Promise<BundleEventOverview>;
31
- getActiveInstallationOverview(
32
- input: ActiveInstallationInput,
33
- ): Promise<ActiveInstallationOverview>;
34
- searchInstallations(
35
- query: string,
36
- limit: number,
37
- offset: number,
38
- ): Promise<OffsetPaginationResult<InstallationSearchRow>>;
39
- getInstallationHistory(
40
- installId: string,
41
- limit: number,
42
- offset: number,
43
- ): Promise<OffsetPaginationResult<InstallationHistoryRow>>;
37
+ listEvents(
38
+ input: InsightsEventPageInput,
39
+ ): Promise<EventCursorPage<EventHistoryRow>>;
40
+ listInstallationEvents(
41
+ input: InsightsInstallationEventPageInput,
42
+ ): Promise<EventCursorPage<InstallationHistoryRow>>;
43
+ getInstallation(input: {
44
+ readonly installId: string;
45
+ }): Promise<InstallationRow | null>;
46
+ pageInstallationsByCurrentUserId(
47
+ input: InsightsUserInstallationPageInput,
48
+ ): Promise<CursorPage<InstallationRow>>;
49
+ getReportingOverview(
50
+ input: InsightsScope & {
51
+ readonly window: ActiveInstallationWindow;
52
+ readonly bundleId?: string;
53
+ },
54
+ ): Promise<ReportingOverview>;
44
55
  };
@@ -0,0 +1,76 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+
6
+ import { afterAll, describe, expect, it } from "vitest";
7
+
8
+ const root = path.resolve(import.meta.dirname, "../../..");
9
+ const oxlint = path.join(root, "node_modules/.bin/oxlint");
10
+ const dir = mkdtempSync(path.join(os.tmpdir(), "hot-updater-lint-"));
11
+
12
+ afterAll(() => {
13
+ rmSync(dir, { recursive: true, force: true });
14
+ });
15
+
16
+ /**
17
+ * The lines `hot-updater/no-filtered-find-many` reports on `source` under the
18
+ * repo's oxlint config. oxlint lints only paths under its working directory,
19
+ * so it runs from the scratch directory.
20
+ */
21
+ const reported = (file: string, source: string): number[] => {
22
+ writeFileSync(path.join(dir, file), source);
23
+ let output: string;
24
+ try {
25
+ output = execFileSync(
26
+ oxlint,
27
+ ["-c", path.join(root, ".oxlintrc.json"), "--format", "json", file],
28
+ { cwd: dir, encoding: "utf8" },
29
+ );
30
+ } catch (error) {
31
+ // oxlint exits 1 when it reports an error.
32
+ output = (error as { stdout: string }).stdout;
33
+ }
34
+ const { diagnostics } = JSON.parse(output) as {
35
+ diagnostics: {
36
+ code: string;
37
+ labels: { span: { line: number } }[];
38
+ }[];
39
+ };
40
+ return diagnostics
41
+ .filter(({ code }) => code === "hot-updater(no-filtered-find-many)")
42
+ .map(({ labels }) => labels[0]!.span.line);
43
+ };
44
+
45
+ const filtered = [
46
+ "export const a = async (db: any) =>",
47
+ ' (await db.findMany("t", {})).rows.filter((row: any) => row.ok);',
48
+ "export const b = async (db: any) => {",
49
+ ' const page = await db.findMany("t", {});',
50
+ " return page.rows.find((row: any) => row.ok);",
51
+ "};",
52
+ "export const c = async (db: any) => {",
53
+ ' const { rows } = await db.findMany("t", {});',
54
+ " return rows.filter(Boolean);",
55
+ "};",
56
+ ].join("\n");
57
+
58
+ describe("hot-updater/no-filtered-find-many", () => {
59
+ it("reports findMany rows filtered in code", () => {
60
+ expect(reported("reads.ts", filtered)).toEqual([2, 5, 9]);
61
+ });
62
+
63
+ it("allows rows used whole, other arrays, and tests", () => {
64
+ expect(
65
+ reported(
66
+ "whole.ts",
67
+ [
68
+ "export const d = async (db: any) =>",
69
+ ' (await db.findMany("t", {})).rows.map((row: any) => row.id);',
70
+ "export const e = (items: number[]) => items.filter(Boolean);",
71
+ ].join("\n"),
72
+ ),
73
+ ).toEqual([]);
74
+ expect(reported("reads.spec.ts", filtered)).toEqual([]);
75
+ });
76
+ });