@storyshelf/core 0.3.2 → 0.5.4

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 (393) hide show
  1. package/README.md +38 -31
  2. package/dist/adapter/capture-queue.d.mts +29 -1
  3. package/dist/adapter/capture-runner.d.mts +18 -2
  4. package/dist/adapter/capture-runner.mjs +12 -1
  5. package/dist/adapter/capture-runner.mjs.map +1 -0
  6. package/dist/adapter/database.d.mts +67 -17
  7. package/dist/adapter/database.mjs +22 -55
  8. package/dist/adapter/database.mjs.map +1 -1
  9. package/dist/adapter/email-sender.d.mts +27 -0
  10. package/dist/adapter/email-sender.mjs +4 -0
  11. package/dist/adapter/metadata.d.mts +21 -15
  12. package/dist/adapter/notifier.d.mts +7 -0
  13. package/dist/adapter/notifier.mjs +6 -0
  14. package/dist/adapter/setup.d.mts +61 -0
  15. package/dist/adapter/setup.mjs +29 -0
  16. package/dist/adapter/setup.mjs.map +1 -0
  17. package/dist/adapter/webhook-events.d.mts +29 -0
  18. package/dist/adapter/webhook-events.mjs +83 -0
  19. package/dist/adapter/webhook-events.mjs.map +1 -0
  20. package/dist/adapters/notifier/events.d.mts +26 -0
  21. package/dist/adapters/notifier/events.mjs +66 -0
  22. package/dist/adapters/notifier/events.mjs.map +1 -0
  23. package/dist/adapters/notifier/format.d.mts +14 -0
  24. package/dist/adapters/notifier/format.mjs +96 -0
  25. package/dist/adapters/notifier/format.mjs.map +1 -0
  26. package/dist/adapters/notifier/provider.d.mts +32 -0
  27. package/dist/adapters/notifier/types.d.mts +56 -0
  28. package/dist/adapters/setup-runner.d.mts +23 -0
  29. package/dist/adapters/setup-runner.mjs +149 -0
  30. package/dist/adapters/setup-runner.mjs.map +1 -0
  31. package/dist/adapters/validate.d.mts +30 -0
  32. package/dist/adapters/validate.mjs +313 -0
  33. package/dist/adapters/validate.mjs.map +1 -0
  34. package/dist/auth.d.mts +94 -0
  35. package/dist/auth.mjs +4 -0
  36. package/dist/brand-packs.d.mts +16 -0
  37. package/dist/brand-packs.mjs +130 -0
  38. package/dist/brand-packs.mjs.map +1 -0
  39. package/dist/capture/adapter.d.mts +51 -1
  40. package/dist/capture/adapter.mjs.map +1 -1
  41. package/dist/capture/affected.d.mts +16 -0
  42. package/dist/capture/affected.mjs +64 -0
  43. package/dist/capture/affected.mjs.map +1 -0
  44. package/dist/capture/diff-record.mjs +30 -0
  45. package/dist/capture/diff-record.mjs.map +1 -0
  46. package/dist/capture/dispatch.d.mts +36 -0
  47. package/dist/capture/dispatch.mjs +88 -31
  48. package/dist/capture/dispatch.mjs.map +1 -1
  49. package/dist/capture/guards.mjs +31 -0
  50. package/dist/capture/guards.mjs.map +1 -0
  51. package/dist/capture/orchestrator.d.mts +41 -6
  52. package/dist/capture/orchestrator.mjs +151 -28
  53. package/dist/capture/orchestrator.mjs.map +1 -1
  54. package/dist/capture/pipeline.d.mts +30 -16
  55. package/dist/capture/pipeline.mjs +143 -39
  56. package/dist/capture/pipeline.mjs.map +1 -1
  57. package/dist/capture/recovery.d.mts +21 -0
  58. package/dist/capture/recovery.mjs +83 -0
  59. package/dist/capture/recovery.mjs.map +1 -0
  60. package/dist/capture/rediff.d.mts +35 -0
  61. package/dist/capture/rediff.mjs +77 -0
  62. package/dist/capture/rediff.mjs.map +1 -0
  63. package/dist/capture/sizing.d.mts +58 -0
  64. package/dist/capture/sizing.mjs +119 -0
  65. package/dist/capture/sizing.mjs.map +1 -0
  66. package/dist/capture/skip-checks.mjs +3 -3
  67. package/dist/capture/skip-checks.mjs.map +1 -1
  68. package/dist/capture/statics.d.mts +38 -0
  69. package/dist/capture/statics.mjs +38 -4
  70. package/dist/capture/statics.mjs.map +1 -1
  71. package/dist/capture/status-fanout.mjs +7 -2
  72. package/dist/capture/status-fanout.mjs.map +1 -1
  73. package/dist/capture/storybook.d.mts +31 -2
  74. package/dist/capture/storybook.mjs +77 -2
  75. package/dist/capture/storybook.mjs.map +1 -1
  76. package/dist/capture.d.mts +10 -4
  77. package/dist/capture.mjs +7 -2
  78. package/dist/config.d.mts +121 -4
  79. package/dist/config.mjs +55 -8
  80. package/dist/config.mjs.map +1 -1
  81. package/dist/index.d.mts +5 -56
  82. package/dist/index.mjs +4 -284
  83. package/dist/logger.d.mts +26 -17
  84. package/dist/logger.mjs +10 -5
  85. package/dist/logger.mjs.map +1 -1
  86. package/dist/models/baseline-status.d.mts +29 -0
  87. package/dist/models/baseline-status.mjs +26 -0
  88. package/dist/models/baseline-status.mjs.map +1 -0
  89. package/dist/models/baseline.d.mts +33 -0
  90. package/dist/models/baseline.mjs +35 -13
  91. package/dist/models/baseline.mjs.map +1 -1
  92. package/dist/models/build.d.mts +87 -0
  93. package/dist/models/build.mjs +83 -35
  94. package/dist/models/build.mjs.map +1 -1
  95. package/dist/models/capture-attempt.d.mts +40 -0
  96. package/dist/models/capture-attempt.mjs +85 -0
  97. package/dist/models/capture-attempt.mjs.map +1 -0
  98. package/dist/models/capture-log.d.mts +21 -0
  99. package/dist/models/capture-log.mjs +44 -0
  100. package/dist/models/capture-log.mjs.map +1 -0
  101. package/dist/models/comment.d.mts +29 -0
  102. package/dist/models/comment.mjs +11 -8
  103. package/dist/models/comment.mjs.map +1 -1
  104. package/dist/models/content-ref.d.mts +21 -0
  105. package/dist/models/content-ref.mjs +49 -0
  106. package/dist/models/content-ref.mjs.map +1 -0
  107. package/dist/models/credentials.d.mts +36 -0
  108. package/dist/models/credentials.mjs +103 -0
  109. package/dist/models/credentials.mjs.map +1 -0
  110. package/dist/models/label.d.mts +58 -0
  111. package/dist/models/label.mjs +32 -27
  112. package/dist/models/label.mjs.map +1 -1
  113. package/dist/models/member.d.mts +34 -0
  114. package/dist/models/member.mjs +20 -11
  115. package/dist/models/member.mjs.map +1 -1
  116. package/dist/models/notification-channel.d.mts +52 -0
  117. package/dist/models/notification-channel.mjs +93 -0
  118. package/dist/models/notification-channel.mjs.map +1 -0
  119. package/dist/models/notification-subscription.d.mts +44 -0
  120. package/dist/models/notification-subscription.mjs +89 -0
  121. package/dist/models/notification-subscription.mjs.map +1 -0
  122. package/dist/models/project-group-mapping.d.mts +30 -0
  123. package/dist/models/project-group-mapping.mjs +46 -0
  124. package/dist/models/project-group-mapping.mjs.map +1 -0
  125. package/dist/models/project.d.mts +54 -0
  126. package/dist/models/project.mjs +15 -11
  127. package/dist/models/project.mjs.map +1 -1
  128. package/dist/models/snapshot.d.mts +58 -0
  129. package/dist/models/snapshot.mjs +13 -7
  130. package/dist/models/snapshot.mjs.map +1 -1
  131. package/dist/models/status-config.d.mts +38 -0
  132. package/dist/models/status-config.mjs +12 -11
  133. package/dist/models/status-config.mjs.map +1 -1
  134. package/dist/models/token.d.mts +44 -0
  135. package/dist/models/token.mjs +18 -15
  136. package/dist/models/token.mjs.map +1 -1
  137. package/dist/models/user.d.mts +42 -0
  138. package/dist/models/user.mjs +65 -0
  139. package/dist/models/user.mjs.map +1 -0
  140. package/dist/models/webhook.d.mts +50 -0
  141. package/dist/models/webhook.mjs +12 -10
  142. package/dist/models/webhook.mjs.map +1 -1
  143. package/dist/models.d.mts +22 -0
  144. package/dist/models.mjs +23 -0
  145. package/dist/paths.mjs +29 -0
  146. package/dist/paths.mjs.map +1 -1
  147. package/dist/retention.d.mts +45 -0
  148. package/dist/retention.mjs +145 -0
  149. package/dist/retention.mjs.map +1 -0
  150. package/dist/schema/baseline.d.mts +1 -0
  151. package/dist/schema/build.d.mts +8 -0
  152. package/dist/schema/capture-attempt.d.mts +38 -0
  153. package/dist/schema/comment.d.mts +1 -1
  154. package/dist/schema/content-ref.d.mts +13 -0
  155. package/dist/schema/member.d.mts +2 -0
  156. package/dist/schema/notification.d.mts +35 -0
  157. package/dist/schema/project-group-mapping.d.mts +15 -0
  158. package/dist/schema/project.d.mts +4 -0
  159. package/dist/schema/snapshot.d.mts +7 -0
  160. package/dist/schema/token.d.mts +2 -0
  161. package/dist/schema/user.d.mts +11 -1
  162. package/dist/schema.d.mts +9 -46
  163. package/dist/schema.mjs +1 -55
  164. package/dist/test-helpers/create-project.d.mts +15 -0
  165. package/dist/test-helpers/create-project.mjs +28 -0
  166. package/dist/test-helpers/create-project.mjs.map +1 -0
  167. package/dist/test-helpers/database-contract.d.mts +13 -0
  168. package/dist/test-helpers/database-contract.mjs +58 -0
  169. package/dist/test-helpers/database-contract.mjs.map +1 -0
  170. package/dist/test-helpers/fake-adapters.d.mts +14 -0
  171. package/dist/test-helpers/fake-adapters.mjs +20 -0
  172. package/dist/test-helpers/fake-adapters.mjs.map +1 -0
  173. package/dist/test-helpers/fake-database.d.mts +11 -0
  174. package/dist/test-helpers/fake-database.mjs +104 -0
  175. package/dist/test-helpers/fake-database.mjs.map +1 -0
  176. package/dist/test-helpers/fake-storage.d.mts +14 -0
  177. package/dist/test-helpers/fake-storage.mjs +49 -0
  178. package/dist/test-helpers/fake-storage.mjs.map +1 -0
  179. package/dist/test-helpers/fake-tables.mjs +276 -0
  180. package/dist/test-helpers/fake-tables.mjs.map +1 -0
  181. package/dist/test-helpers/git-contract.d.mts +11 -0
  182. package/dist/test-helpers/git-contract.mjs +34 -0
  183. package/dist/test-helpers/git-contract.mjs.map +1 -0
  184. package/dist/test-helpers/notifier-contract.d.mts +11 -0
  185. package/dist/test-helpers/notifier-contract.mjs +34 -0
  186. package/dist/test-helpers/notifier-contract.mjs.map +1 -0
  187. package/dist/test-helpers/queue-contract.d.mts +11 -0
  188. package/dist/test-helpers/queue-contract.mjs +64 -0
  189. package/dist/test-helpers/queue-contract.mjs.map +1 -0
  190. package/dist/test-helpers/runner-contract.d.mts +11 -0
  191. package/dist/test-helpers/runner-contract.mjs +32 -0
  192. package/dist/test-helpers/runner-contract.mjs.map +1 -0
  193. package/dist/test-helpers/sql-chunks.mjs +138 -0
  194. package/dist/test-helpers/sql-chunks.mjs.map +1 -0
  195. package/dist/test-helpers/storage-contract.d.mts +11 -0
  196. package/dist/test-helpers/storage-contract.mjs +87 -0
  197. package/dist/test-helpers/storage-contract.mjs.map +1 -0
  198. package/dist/test-helpers.d.mts +13 -0
  199. package/dist/test-helpers.mjs +14 -0
  200. package/dist/tracing.mjs +63 -0
  201. package/dist/tracing.mjs.map +1 -0
  202. package/dist/types.d.mts +38 -5
  203. package/dist/types.mjs +18 -5
  204. package/dist/types.mjs.map +1 -1
  205. package/dist/urls.d.mts +12 -1
  206. package/dist/urls.mjs +22 -3
  207. package/dist/urls.mjs.map +1 -1
  208. package/dist/utils/encrypt.d.mts +48 -0
  209. package/dist/utils/encrypt.mjs +67 -13
  210. package/dist/utils/encrypt.mjs.map +1 -1
  211. package/dist/utils/hash.d.mts +17 -0
  212. package/dist/utils/hash.mjs +8 -2
  213. package/dist/utils/hash.mjs.map +1 -1
  214. package/dist/utils/http.d.mts +58 -0
  215. package/dist/utils/http.mjs +147 -0
  216. package/dist/utils/http.mjs.map +1 -0
  217. package/dist/utils/mime.d.mts +9 -0
  218. package/dist/utils/mime.mjs +33 -0
  219. package/dist/utils/mime.mjs.map +1 -0
  220. package/dist/utils/path-security.d.mts +12 -0
  221. package/dist/utils/path-security.mjs +24 -0
  222. package/dist/utils/path-security.mjs.map +1 -0
  223. package/dist/utils/redact.d.mts +17 -0
  224. package/dist/utils/redact.mjs +42 -0
  225. package/dist/utils/redact.mjs.map +1 -0
  226. package/dist/utils/timing.d.mts +19 -0
  227. package/dist/utils/timing.mjs +46 -0
  228. package/dist/utils/timing.mjs.map +1 -0
  229. package/dist/utils/ulid.d.mts +10 -0
  230. package/dist/utils/ulid.mjs +8 -8
  231. package/dist/utils/ulid.mjs.map +1 -1
  232. package/dist/utils.d.mts +12 -0
  233. package/dist/utils.mjs +13 -0
  234. package/package.json +69 -28
  235. package/dist/adapter/auth.d.mts +0 -50
  236. package/dist/adapter/auth.mjs +0 -10
  237. package/dist/adapter/auth.mjs.map +0 -1
  238. package/dist/adapter/init.d.mts +0 -58
  239. package/dist/adapter/init.mjs +0 -93
  240. package/dist/adapter/init.mjs.map +0 -1
  241. package/dist/adapters/webhook-events.mjs +0 -55
  242. package/dist/adapters/webhook-events.mjs.map +0 -1
  243. package/dist/ddl.d.mts +0 -8
  244. package/dist/ddl.mjs +0 -158
  245. package/dist/ddl.mjs.map +0 -1
  246. package/dist/index.mjs.map +0 -1
  247. package/dist/middleware/auth-gate.mjs +0 -36
  248. package/dist/middleware/auth-gate.mjs.map +0 -1
  249. package/dist/middleware/csrf.mjs +0 -40
  250. package/dist/middleware/csrf.mjs.map +0 -1
  251. package/dist/middleware/rate-limit.mjs +0 -39
  252. package/dist/middleware/rate-limit.mjs.map +0 -1
  253. package/dist/middleware/request-log.mjs +0 -28
  254. package/dist/middleware/request-log.mjs.map +0 -1
  255. package/dist/middleware/store-scope.mjs +0 -29
  256. package/dist/middleware/store-scope.mjs.map +0 -1
  257. package/dist/pages/build-detail.mjs +0 -339
  258. package/dist/pages/build-detail.mjs.map +0 -1
  259. package/dist/pages/build-diff-comments.mjs +0 -113
  260. package/dist/pages/build-diff-comments.mjs.map +0 -1
  261. package/dist/pages/build-diff-header.mjs +0 -163
  262. package/dist/pages/build-diff-header.mjs.map +0 -1
  263. package/dist/pages/build-diff-nav.mjs +0 -66
  264. package/dist/pages/build-diff-nav.mjs.map +0 -1
  265. package/dist/pages/build-diff-viewer.mjs +0 -197
  266. package/dist/pages/build-diff-viewer.mjs.map +0 -1
  267. package/dist/pages/build-diff.mjs +0 -90
  268. package/dist/pages/build-diff.mjs.map +0 -1
  269. package/dist/pages/compute-jobs.mjs +0 -198
  270. package/dist/pages/compute-jobs.mjs.map +0 -1
  271. package/dist/pages/label-detail.mjs +0 -228
  272. package/dist/pages/label-detail.mjs.map +0 -1
  273. package/dist/pages/login.mjs +0 -78
  274. package/dist/pages/login.mjs.map +0 -1
  275. package/dist/pages/project-builds.mjs +0 -231
  276. package/dist/pages/project-builds.mjs.map +0 -1
  277. package/dist/pages/project-create.mjs +0 -169
  278. package/dist/pages/project-create.mjs.map +0 -1
  279. package/dist/pages/project-settings.mjs +0 -102
  280. package/dist/pages/project-settings.mjs.map +0 -1
  281. package/dist/pages/projects.mjs +0 -184
  282. package/dist/pages/projects.mjs.map +0 -1
  283. package/dist/pages/root.mjs +0 -94
  284. package/dist/pages/root.mjs.map +0 -1
  285. package/dist/pages/settings-general.mjs +0 -192
  286. package/dist/pages/settings-general.mjs.map +0 -1
  287. package/dist/pages/settings-labels.mjs +0 -150
  288. package/dist/pages/settings-labels.mjs.map +0 -1
  289. package/dist/pages/settings-members.mjs +0 -125
  290. package/dist/pages/settings-members.mjs.map +0 -1
  291. package/dist/pages/settings-status.mjs +0 -152
  292. package/dist/pages/settings-status.mjs.map +0 -1
  293. package/dist/pages/settings-tests.mjs +0 -149
  294. package/dist/pages/settings-tests.mjs.map +0 -1
  295. package/dist/pages/settings-tokens.mjs +0 -96
  296. package/dist/pages/settings-tokens.mjs.map +0 -1
  297. package/dist/pages/settings-webhooks.mjs +0 -150
  298. package/dist/pages/settings-webhooks.mjs.map +0 -1
  299. package/dist/pages/storybook.mjs +0 -96
  300. package/dist/pages/storybook.mjs.map +0 -1
  301. package/dist/retention/purge.mjs +0 -70
  302. package/dist/retention/purge.mjs.map +0 -1
  303. package/dist/routers/admin.mjs +0 -42
  304. package/dist/routers/admin.mjs.map +0 -1
  305. package/dist/routers/assets.mjs +0 -25
  306. package/dist/routers/assets.mjs.map +0 -1
  307. package/dist/routers/auth.mjs +0 -78
  308. package/dist/routers/auth.mjs.map +0 -1
  309. package/dist/routers/builds.handlers.mjs +0 -184
  310. package/dist/routers/builds.handlers.mjs.map +0 -1
  311. package/dist/routers/builds.mjs +0 -185
  312. package/dist/routers/builds.mjs.map +0 -1
  313. package/dist/routers/comments.mjs +0 -90
  314. package/dist/routers/comments.mjs.map +0 -1
  315. package/dist/routers/health.mjs +0 -137
  316. package/dist/routers/health.mjs.map +0 -1
  317. package/dist/routers/helpers.mjs +0 -82
  318. package/dist/routers/helpers.mjs.map +0 -1
  319. package/dist/routers/htmx.mjs +0 -20
  320. package/dist/routers/htmx.mjs.map +0 -1
  321. package/dist/routers/labels.mjs +0 -110
  322. package/dist/routers/labels.mjs.map +0 -1
  323. package/dist/routers/media.mjs +0 -55
  324. package/dist/routers/media.mjs.map +0 -1
  325. package/dist/routers/members.mjs +0 -102
  326. package/dist/routers/members.mjs.map +0 -1
  327. package/dist/routers/projects.mjs +0 -122
  328. package/dist/routers/projects.mjs.map +0 -1
  329. package/dist/routers/schemas.mjs +0 -224
  330. package/dist/routers/schemas.mjs.map +0 -1
  331. package/dist/routers/settings-general.mjs +0 -95
  332. package/dist/routers/settings-general.mjs.map +0 -1
  333. package/dist/routers/settings-labels.mjs +0 -49
  334. package/dist/routers/settings-labels.mjs.map +0 -1
  335. package/dist/routers/settings-members.mjs +0 -30
  336. package/dist/routers/settings-members.mjs.map +0 -1
  337. package/dist/routers/settings-status.mjs +0 -73
  338. package/dist/routers/settings-status.mjs.map +0 -1
  339. package/dist/routers/settings-tokens.mjs +0 -40
  340. package/dist/routers/settings-tokens.mjs.map +0 -1
  341. package/dist/routers/settings-webhooks.mjs +0 -63
  342. package/dist/routers/settings-webhooks.mjs.map +0 -1
  343. package/dist/routers/settings.handlers.mjs +0 -93
  344. package/dist/routers/settings.handlers.mjs.map +0 -1
  345. package/dist/routers/settings.mjs +0 -23
  346. package/dist/routers/settings.mjs.map +0 -1
  347. package/dist/routers/snapshots.mjs +0 -144
  348. package/dist/routers/snapshots.mjs.map +0 -1
  349. package/dist/routers/status-configs.mjs +0 -101
  350. package/dist/routers/status-configs.mjs.map +0 -1
  351. package/dist/routers/storybook.mjs +0 -120
  352. package/dist/routers/storybook.mjs.map +0 -1
  353. package/dist/routers/tokens.mjs +0 -81
  354. package/dist/routers/tokens.mjs.map +0 -1
  355. package/dist/routers/ui.mjs +0 -165
  356. package/dist/routers/ui.mjs.map +0 -1
  357. package/dist/routers/webhooks.mjs +0 -90
  358. package/dist/routers/webhooks.mjs.map +0 -1
  359. package/dist/schema/baseline.mjs +0 -22
  360. package/dist/schema/baseline.mjs.map +0 -1
  361. package/dist/schema/build.mjs +0 -29
  362. package/dist/schema/build.mjs.map +0 -1
  363. package/dist/schema/comment.mjs +0 -26
  364. package/dist/schema/comment.mjs.map +0 -1
  365. package/dist/schema/label.mjs +0 -30
  366. package/dist/schema/label.mjs.map +0 -1
  367. package/dist/schema/member.mjs +0 -19
  368. package/dist/schema/member.mjs.map +0 -1
  369. package/dist/schema/project.mjs +0 -25
  370. package/dist/schema/project.mjs.map +0 -1
  371. package/dist/schema/snapshot.mjs +0 -34
  372. package/dist/schema/snapshot.mjs.map +0 -1
  373. package/dist/schema/status-config.mjs +0 -20
  374. package/dist/schema/status-config.mjs.map +0 -1
  375. package/dist/schema/token.mjs +0 -19
  376. package/dist/schema/token.mjs.map +0 -1
  377. package/dist/schema/user.mjs +0 -19
  378. package/dist/schema/user.mjs.map +0 -1
  379. package/dist/schema/webhook.mjs +0 -20
  380. package/dist/schema/webhook.mjs.map +0 -1
  381. package/dist/schema.mjs.map +0 -1
  382. package/dist/store.mjs +0 -20
  383. package/dist/store.mjs.map +0 -1
  384. package/dist/ui/document.mjs +0 -348
  385. package/dist/ui/document.mjs.map +0 -1
  386. package/dist/ui/feedback.mjs +0 -24
  387. package/dist/ui/feedback.mjs.map +0 -1
  388. package/dist/ui/forms.mjs +0 -117
  389. package/dist/ui/forms.mjs.map +0 -1
  390. package/dist/ui/styles.mjs +0 -174
  391. package/dist/ui/styles.mjs.map +0 -1
  392. package/dist/ui/theme.mjs +0 -44
  393. package/dist/ui/theme.mjs.map +0 -1
@@ -0,0 +1,87 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { DatabaseAdapter } from "../adapter/database.mjs";
4
+ import { BuildStatus } from "../types.mjs";
5
+ import { Build } from "../schema/build.mjs";
6
+ import { Table } from "drizzle-orm";
7
+ //#region src/models/build.d.ts
8
+ /** Tables required by {@link BuildModel}. */
9
+ interface BuildTables {
10
+ builds: Table;
11
+ buildLabels: Table;
12
+ snapshots: Table;
13
+ }
14
+ /** Data operations for build records. */
15
+ declare class BuildModel {
16
+ private readonly db;
17
+ private readonly tables;
18
+ constructor(db: DatabaseAdapter, tables?: BuildTables);
19
+ create(projectId: string, input: BuildCreateInput): Promise<Build>;
20
+ get(id: string): Promise<Build | null>;
21
+ list(projectId: string, filter?: BuildListFilter): Promise<Build[]>;
22
+ /**
23
+ * Return the build a project-slug Storybook link resolves to: the most recent
24
+ * `approved` build on the project's default branch. Approved builds on other
25
+ * branches never qualify (their PR can still be dismissed), and neither do
26
+ * pending, failed, or unreviewed default-branch builds. Visibility (public or
27
+ * authenticated) is checked separately by the caller.
28
+ */
29
+ latestApprovedDefault(project: {
30
+ id: string;
31
+ gitDefaultBranch: string;
32
+ }): Promise<Build | null>;
33
+ update(id: string, patch: Partial<Pick<Build, "status" | "public" | "message" | "authorEmail" | "authorName">>): Promise<Build>;
34
+ setStatus(id: string, status: BuildStatus): Promise<Build>;
35
+ /**
36
+ * Record the affected-capture computation for a build. Idempotent: the CLI
37
+ * posts this once after creating the build and before uploading the bundle.
38
+ */
39
+ setAffected(id: string, input: AffectedComputation): Promise<Build>;
40
+ /**
41
+ * Parse a build's affected import paths. Returns `null` for full capture
42
+ * (no computation, or an unreadable payload) — callers render everything.
43
+ */
44
+ static affectedPaths(build: Pick<Build, "affectedImportPaths">): string[] | null;
45
+ updateCounts(id: string): Promise<Build>;
46
+ remove(id: string): Promise<void>;
47
+ }
48
+ /**
49
+ * Return whether a build is publicly viewable without a session.
50
+ *
51
+ * A build is public iff `builds.public` is set or its branch matches the
52
+ * project's `public_branch_regex` (ADR 0011). Supplying an empty/nonexistent
53
+ * regex makes every build require auth.
54
+ */
55
+ declare function isPublicBuild(project: {
56
+ publicBranchRegex: string | null;
57
+ }, build: Pick<Build, "public" | "gitBranch">): boolean;
58
+ /** Input for creating a build. */
59
+ interface BuildCreateInput {
60
+ gitSha: string;
61
+ gitBranch: string;
62
+ isDefault?: boolean;
63
+ authorEmail?: string;
64
+ authorName?: string;
65
+ message?: string;
66
+ public?: boolean;
67
+ affectedOnly?: boolean;
68
+ baselineSha?: string | null;
69
+ changedFiles?: string[] | null;
70
+ affectedImportPaths?: string[] | null;
71
+ }
72
+ /** Affected-capture computation posted after build creation. */
73
+ interface AffectedComputation {
74
+ baselineSha: string | null;
75
+ changedFiles: string[];
76
+ affectedImportPaths: string[] | null;
77
+ }
78
+ /** Filter options for listing builds. */
79
+ interface BuildListFilter {
80
+ status?: BuildStatus;
81
+ branch?: string;
82
+ labelKey?: string;
83
+ labelValue?: string;
84
+ }
85
+ //#endregion
86
+ export { AffectedComputation, BuildCreateInput, BuildListFilter, BuildModel, BuildTables, isPublicBuild };
87
+ //# sourceMappingURL=build.d.mts.map
@@ -1,34 +1,25 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
- import { builds } from "../schema/build.mjs";
5
- import { buildLabels } from "../schema/label.mjs";
6
- import { snapshots } from "../schema/snapshot.mjs";
7
4
  import { ulid } from "../utils/ulid.mjs";
8
- import { and, desc, eq, inArray } from "drizzle-orm";
5
+ import { and, desc, eq, getTableColumns, inArray } from "drizzle-orm";
9
6
  //#region src/models/build.ts
10
7
  /** Build records, status transitions, and publication helpers. */
11
- /**
12
- * Return whether a build is publicly viewable without a session.
13
- *
14
- * A build is public iff `builds.public` is set or its branch matches the
15
- * project's `public_branch_regex` (ADR 0011). Supplying an empty/nonexistent
16
- * regex makes every build require auth.
17
- */
18
- function isPublicBuild(project, build) {
19
- if (build.public) return true;
20
- if (!project.publicBranchRegex) return false;
21
- return new RegExp(project.publicBranchRegex, "u").test(build.gitBranch);
22
- }
23
8
  /** Data operations for build records. */
24
9
  var BuildModel = class {
25
10
  db;
26
- constructor(db) {
11
+ tables;
12
+ constructor(db, tables) {
27
13
  this.db = db;
14
+ this.tables = tables ?? {
15
+ builds: db.tables.builds,
16
+ buildLabels: db.tables.buildLabels,
17
+ snapshots: db.tables.snapshots
18
+ };
28
19
  }
29
20
  async create(projectId, input) {
30
21
  const now = (/* @__PURE__ */ new Date()).toISOString();
31
- return await this.db.insert(builds, {
22
+ return await this.db.insert(this.tables.builds, {
32
23
  id: ulid(),
33
24
  projectId,
34
25
  gitSha: input.gitSha,
@@ -38,36 +29,47 @@ var BuildModel = class {
38
29
  authorName: input.authorName,
39
30
  message: input.message,
40
31
  public: input.public ?? false,
32
+ affectedOnly: input.affectedOnly ?? true,
33
+ baselineSha: input.baselineSha ?? null,
34
+ changedFiles: serializePaths(input.changedFiles),
35
+ affectedImportPaths: serializePaths(input.affectedImportPaths),
41
36
  status: "pending",
42
37
  createdAt: now,
43
38
  updatedAt: now
44
39
  });
45
40
  }
46
41
  async get(id) {
47
- return await this.db.get(builds, id);
42
+ return await this.db.get(this.tables.builds, id);
48
43
  }
49
44
  async list(projectId, filter = {}) {
50
- const conditions = [eq(builds.projectId, projectId)];
51
- if (filter.status) conditions.push(eq(builds.status, filter.status));
52
- if (filter.branch) conditions.push(eq(builds.gitBranch, filter.branch));
45
+ const conditions = [eq(getTableColumns(this.tables.builds)["projectId"], projectId)];
46
+ if (filter.status) conditions.push(eq(getTableColumns(this.tables.builds)["status"], filter.status));
47
+ if (filter.branch) conditions.push(eq(getTableColumns(this.tables.builds)["gitBranch"], filter.branch));
53
48
  if (filter.labelKey && filter.labelValue) {
54
- const ids = (await this.db.list(buildLabels, { where: and(eq(buildLabels.projectId, projectId), eq(buildLabels.typeKey, filter.labelKey), eq(buildLabels.value, filter.labelValue)) })).map((l) => l.buildId);
49
+ const ids = (await this.db.list(this.tables.buildLabels, { where: and(eq(getTableColumns(this.tables.buildLabels)["projectId"], projectId), eq(getTableColumns(this.tables.buildLabels)["typeKey"], filter.labelKey), eq(getTableColumns(this.tables.buildLabels)["value"], filter.labelValue)) })).map((l) => l.buildId);
55
50
  if (ids.length === 0) return [];
56
- conditions.push(inArray(builds.id, ids));
51
+ conditions.push(inArray(getTableColumns(this.tables.builds)["id"], ids));
57
52
  }
58
- return this.db.list(builds, {
53
+ return await this.db.list(this.tables.builds, {
59
54
  where: and(...conditions),
60
- orderBy: desc(builds.createdAt)
55
+ orderBy: desc(getTableColumns(this.tables.builds)["createdAt"])
61
56
  });
62
57
  }
63
- /** Return the most recent build intended for publishing, if any. */
64
- async latestPublished(project) {
65
- const rows = await this.list(project.id);
66
- for (const build of rows) if (isPublicBuild(project, build)) return build;
67
- return null;
58
+ /**
59
+ * Return the build a project-slug Storybook link resolves to: the most recent
60
+ * `approved` build on the project's default branch. Approved builds on other
61
+ * branches never qualify (their PR can still be dismissed), and neither do
62
+ * pending, failed, or unreviewed default-branch builds. Visibility (public or
63
+ * authenticated) is checked separately by the caller.
64
+ */
65
+ async latestApprovedDefault(project) {
66
+ return (await this.list(project.id, {
67
+ branch: project.gitDefaultBranch,
68
+ status: "approved"
69
+ }))[0] ?? null;
68
70
  }
69
71
  async update(id, patch) {
70
- return await this.db.update(builds, id, {
72
+ return await this.db.update(this.tables.builds, id, {
71
73
  ...patch,
72
74
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
73
75
  });
@@ -75,13 +77,32 @@ var BuildModel = class {
75
77
  async setStatus(id, status) {
76
78
  return await this.update(id, { status });
77
79
  }
80
+ /**
81
+ * Record the affected-capture computation for a build. Idempotent: the CLI
82
+ * posts this once after creating the build and before uploading the bundle.
83
+ */
84
+ async setAffected(id, input) {
85
+ return await this.db.update(this.tables.builds, id, {
86
+ baselineSha: input.baselineSha,
87
+ changedFiles: serializePaths(input.changedFiles),
88
+ affectedImportPaths: serializePaths(input.affectedImportPaths),
89
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
90
+ });
91
+ }
92
+ /**
93
+ * Parse a build's affected import paths. Returns `null` for full capture
94
+ * (no computation, or an unreadable payload) — callers render everything.
95
+ */
96
+ static affectedPaths(build) {
97
+ return parsePaths(build.affectedImportPaths);
98
+ }
78
99
  async updateCounts(id) {
79
- const rows = await this.db.list(snapshots, { where: eq(snapshots.buildId, id) });
100
+ const rows = await this.db.list(this.tables.snapshots, { where: eq(getTableColumns(this.tables.snapshots)["buildId"], id) });
80
101
  const snapshotCount = rows.length;
81
102
  const changedCount = rows.filter((s) => s.status === "changed" || s.status === "new").length;
82
103
  const approvedCount = rows.filter((s) => s.status === "approved" || s.status === "unchanged").length;
83
104
  const rejectedCount = rows.filter((s) => s.status === "rejected").length;
84
- return this.db.update(builds, id, {
105
+ return await this.db.update(this.tables.builds, id, {
85
106
  snapshotCount,
86
107
  changedCount,
87
108
  approvedCount,
@@ -90,9 +111,36 @@ var BuildModel = class {
90
111
  });
91
112
  }
92
113
  async remove(id) {
93
- await this.db.remove(builds, id);
114
+ await this.db.remove(this.tables.builds, id);
94
115
  }
95
116
  };
117
+ /**
118
+ * Return whether a build is publicly viewable without a session.
119
+ *
120
+ * A build is public iff `builds.public` is set or its branch matches the
121
+ * project's `public_branch_regex` (ADR 0011). Supplying an empty/nonexistent
122
+ * regex makes every build require auth.
123
+ */
124
+ function isPublicBuild(project, build) {
125
+ if (build.public) return true;
126
+ if (!project.publicBranchRegex) return false;
127
+ return new RegExp(project.publicBranchRegex, "u").test(build.gitBranch);
128
+ }
129
+ /** Serialize an optional path list to its JSON text form (or null). */
130
+ function serializePaths(paths) {
131
+ if (!paths) return null;
132
+ return JSON.stringify(paths);
133
+ }
134
+ /** Parse a JSON path list; null (full capture) on missing/invalid input. */
135
+ function parsePaths(raw) {
136
+ if (!raw) return null;
137
+ try {
138
+ const parsed = JSON.parse(raw);
139
+ return Array.isArray(parsed) && parsed.every((entry) => typeof entry === "string") ? parsed : null;
140
+ } catch {
141
+ return null;
142
+ }
143
+ }
96
144
  //#endregion
97
145
  export { BuildModel, isPublicBuild };
98
146
 
@@ -1 +1 @@
1
- {"version":3,"file":"build.mjs","names":[],"sources":["../../src/models/build.ts"],"sourcesContent":["/** Build records, status transitions, and publication helpers. */\nimport { and, desc, eq, inArray } from \"drizzle-orm\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport { builds } from \"../schema/build.ts\";\nimport type { Build } from \"../schema/build.ts\";\nimport { buildLabels } from \"../schema/label.ts\";\nimport { snapshots } from \"../schema/snapshot.ts\";\nimport type { BuildStatus } from \"../types.ts\";\nimport { ulid } from \"../utils/ulid.ts\";\n\n/**\n * Return whether a build is publicly viewable without a session.\n *\n * A build is public iff `builds.public` is set or its branch matches the\n * project's `public_branch_regex` (ADR 0011). Supplying an empty/nonexistent\n * regex makes every build require auth.\n */\nexport function isPublicBuild(\n project: { publicBranchRegex: string | null },\n build: Pick<Build, \"public\" | \"gitBranch\">,\n): boolean {\n if (build.public) {\n return true;\n }\n if (!project.publicBranchRegex) {\n return false;\n }\n return new RegExp(project.publicBranchRegex, \"u\").test(build.gitBranch);\n}\n\n/** Input for creating a build. */\nexport interface BuildCreateInput {\n gitSha: string;\n gitBranch: string;\n isDefault?: boolean;\n authorEmail?: string;\n authorName?: string;\n message?: string;\n public?: boolean;\n}\n\n/** Filter options for listing builds. */\nexport interface BuildListFilter {\n status?: BuildStatus;\n branch?: string;\n labelKey?: string;\n labelValue?: string;\n}\n\n/** Data operations for build records. */\nexport class BuildModel {\n constructor(private readonly db: DatabaseAdapter) {}\n\n async create(projectId: string, input: BuildCreateInput): Promise<Build> {\n const now = new Date().toISOString();\n return await this.db.insert(builds, {\n id: ulid(),\n projectId,\n gitSha: input.gitSha,\n gitBranch: input.gitBranch,\n isDefault: input.isDefault ?? false,\n authorEmail: input.authorEmail,\n authorName: input.authorName,\n message: input.message,\n public: input.public ?? false,\n status: \"pending\",\n createdAt: now,\n updatedAt: now,\n });\n }\n\n async get(id: string): Promise<Build | null> {\n return await this.db.get(builds, id);\n }\n\n async list(projectId: string, filter: BuildListFilter = {}): Promise<Build[]> {\n const conditions = [eq(builds.projectId, projectId)];\n if (filter.status) {\n conditions.push(eq(builds.status, filter.status));\n }\n if (filter.branch) {\n conditions.push(eq(builds.gitBranch, filter.branch));\n }\n if (filter.labelKey && filter.labelValue) {\n const labels = await this.db.list(buildLabels, {\n where: and(\n eq(buildLabels.projectId, projectId),\n eq(buildLabels.typeKey, filter.labelKey),\n eq(buildLabels.value, filter.labelValue),\n ),\n });\n const ids = labels.map((l) => l.buildId);\n if (ids.length === 0) {\n return [];\n }\n conditions.push(inArray(builds.id, ids));\n }\n return this.db.list(builds, { where: and(...conditions), orderBy: desc(builds.createdAt) });\n }\n\n /** Return the most recent build intended for publishing, if any. */\n async latestPublished(project: {\n id: string;\n publicBranchRegex: string | null;\n }): Promise<Build | null> {\n const rows = await this.list(project.id);\n for (const build of rows) {\n if (isPublicBuild(project, build)) {\n return build;\n }\n }\n return null;\n }\n\n async update(\n id: string,\n patch: Partial<Pick<Build, \"status\" | \"public\" | \"message\" | \"authorEmail\" | \"authorName\">>,\n ): Promise<Build> {\n return await this.db.update(builds, id, { ...patch, updatedAt: new Date().toISOString() });\n }\n\n async setStatus(id: string, status: BuildStatus): Promise<Build> {\n return await this.update(id, { status });\n }\n\n async updateCounts(id: string): Promise<Build> {\n const rows = await this.db.list(snapshots, { where: eq(snapshots.buildId, id) });\n const snapshotCount = rows.length;\n const changedCount = rows.filter((s) => s.status === \"changed\" || s.status === \"new\").length;\n const approvedCount = rows.filter(\n (s) => s.status === \"approved\" || s.status === \"unchanged\",\n ).length;\n const rejectedCount = rows.filter((s) => s.status === \"rejected\").length;\n return this.db.update(builds, id, {\n snapshotCount,\n changedCount,\n approvedCount,\n rejectedCount,\n updatedAt: new Date().toISOString(),\n });\n }\n\n async remove(id: string): Promise<void> {\n await this.db.remove(builds, id);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"build.mjs","names":[],"sources":["../../src/models/build.ts"],"sourcesContent":["/** Build records, status transitions, and publication helpers. */\nimport { and, desc, eq, getTableColumns, inArray } from \"drizzle-orm\";\nimport type { SQLWrapper, Table } from \"drizzle-orm\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { Build } from \"../schema/build.ts\";\nimport type { BuildStatus } from \"../types.ts\";\nimport { ulid } from \"../utils/ulid.ts\";\n\n/** Tables required by {@link BuildModel}. */\nexport interface BuildTables {\n builds: Table;\n buildLabels: Table;\n snapshots: Table;\n}\n\n/** Data operations for build records. */\nexport class BuildModel {\n private readonly tables: BuildTables;\n constructor(\n private readonly db: DatabaseAdapter,\n tables?: BuildTables,\n ) {\n this.tables = tables ?? {\n builds: db.tables.builds,\n buildLabels: db.tables.buildLabels,\n snapshots: db.tables.snapshots,\n };\n }\n\n async create(projectId: string, input: BuildCreateInput): Promise<Build> {\n const now = new Date().toISOString();\n return (await this.db.insert(this.tables.builds, {\n id: ulid(),\n projectId,\n gitSha: input.gitSha,\n gitBranch: input.gitBranch,\n isDefault: input.isDefault ?? false,\n authorEmail: input.authorEmail,\n authorName: input.authorName,\n message: input.message,\n public: input.public ?? false,\n affectedOnly: input.affectedOnly ?? true,\n baselineSha: input.baselineSha ?? null,\n changedFiles: serializePaths(input.changedFiles),\n affectedImportPaths: serializePaths(input.affectedImportPaths),\n status: \"pending\",\n createdAt: now,\n updatedAt: now,\n })) as unknown as Build;\n }\n\n async get(id: string): Promise<Build | null> {\n return (await this.db.get(this.tables.builds, id)) as unknown as Build | null;\n }\n\n async list(projectId: string, filter: BuildListFilter = {}): Promise<Build[]> {\n const conditions = [\n eq(getTableColumns(this.tables.builds)[\"projectId\"] as unknown as SQLWrapper, projectId),\n ];\n if (filter.status) {\n conditions.push(\n eq(getTableColumns(this.tables.builds)[\"status\"] as unknown as SQLWrapper, filter.status),\n );\n }\n if (filter.branch) {\n conditions.push(\n eq(\n getTableColumns(this.tables.builds)[\"gitBranch\"] as unknown as SQLWrapper,\n filter.branch,\n ),\n );\n }\n if (filter.labelKey && filter.labelValue) {\n const labels = (await this.db.list(this.tables.buildLabels, {\n where: and(\n eq(\n getTableColumns(this.tables.buildLabels)[\"projectId\"] as unknown as SQLWrapper,\n projectId,\n ),\n eq(\n getTableColumns(this.tables.buildLabels)[\"typeKey\"] as unknown as SQLWrapper,\n filter.labelKey,\n ),\n eq(\n getTableColumns(this.tables.buildLabels)[\"value\"] as unknown as SQLWrapper,\n filter.labelValue,\n ),\n ),\n })) as unknown as { buildId: string }[];\n const ids = labels.map((l) => l.buildId);\n if (ids.length === 0) {\n return [];\n }\n conditions.push(\n inArray(getTableColumns(this.tables.builds)[\"id\"] as unknown as SQLWrapper, ids),\n );\n }\n return (await this.db.list(this.tables.builds, {\n where: and(...conditions),\n orderBy: desc(getTableColumns(this.tables.builds)[\"createdAt\"] as unknown as SQLWrapper),\n })) as unknown as Build[];\n }\n\n /**\n * Return the build a project-slug Storybook link resolves to: the most recent\n * `approved` build on the project's default branch. Approved builds on other\n * branches never qualify (their PR can still be dismissed), and neither do\n * pending, failed, or unreviewed default-branch builds. Visibility (public or\n * authenticated) is checked separately by the caller.\n */\n async latestApprovedDefault(project: {\n id: string;\n gitDefaultBranch: string;\n }): Promise<Build | null> {\n const rows = await this.list(project.id, {\n branch: project.gitDefaultBranch,\n status: \"approved\",\n });\n return rows[0] ?? null;\n }\n\n async update(\n id: string,\n patch: Partial<Pick<Build, \"status\" | \"public\" | \"message\" | \"authorEmail\" | \"authorName\">>,\n ): Promise<Build> {\n return (await this.db.update(this.tables.builds, id, {\n ...patch,\n updatedAt: new Date().toISOString(),\n })) as unknown as Build;\n }\n\n async setStatus(id: string, status: BuildStatus): Promise<Build> {\n return await this.update(id, { status });\n }\n\n /**\n * Record the affected-capture computation for a build. Idempotent: the CLI\n * posts this once after creating the build and before uploading the bundle.\n */\n async setAffected(id: string, input: AffectedComputation): Promise<Build> {\n return (await this.db.update(this.tables.builds, id, {\n baselineSha: input.baselineSha,\n changedFiles: serializePaths(input.changedFiles),\n affectedImportPaths: serializePaths(input.affectedImportPaths),\n updatedAt: new Date().toISOString(),\n })) as unknown as Build;\n }\n\n /**\n * Parse a build's affected import paths. Returns `null` for full capture\n * (no computation, or an unreadable payload) — callers render everything.\n */\n static affectedPaths(build: Pick<Build, \"affectedImportPaths\">): string[] | null {\n return parsePaths(build.affectedImportPaths);\n }\n\n async updateCounts(id: string): Promise<Build> {\n const rows = (await this.db.list(this.tables.snapshots, {\n where: eq(getTableColumns(this.tables.snapshots)[\"buildId\"] as unknown as SQLWrapper, id),\n })) as unknown as { status: string }[];\n const snapshotCount = rows.length;\n const changedCount = rows.filter((s) => s.status === \"changed\" || s.status === \"new\").length;\n const approvedCount = rows.filter(\n (s) => s.status === \"approved\" || s.status === \"unchanged\",\n ).length;\n const rejectedCount = rows.filter((s) => s.status === \"rejected\").length;\n return (await this.db.update(this.tables.builds, id, {\n snapshotCount,\n changedCount,\n approvedCount,\n rejectedCount,\n updatedAt: new Date().toISOString(),\n })) as unknown as Build;\n }\n\n async remove(id: string): Promise<void> {\n await this.db.remove(this.tables.builds, id);\n }\n}\n\n/**\n * Return whether a build is publicly viewable without a session.\n *\n * A build is public iff `builds.public` is set or its branch matches the\n * project's `public_branch_regex` (ADR 0011). Supplying an empty/nonexistent\n * regex makes every build require auth.\n */\nexport function isPublicBuild(\n project: { publicBranchRegex: string | null },\n build: Pick<Build, \"public\" | \"gitBranch\">,\n): boolean {\n if (build.public) {\n return true;\n }\n if (!project.publicBranchRegex) {\n return false;\n }\n return new RegExp(project.publicBranchRegex, \"u\").test(build.gitBranch);\n}\n\n/** Input for creating a build. */\nexport interface BuildCreateInput {\n gitSha: string;\n gitBranch: string;\n isDefault?: boolean;\n authorEmail?: string;\n authorName?: string;\n message?: string;\n public?: boolean;\n affectedOnly?: boolean;\n baselineSha?: string | null;\n changedFiles?: string[] | null;\n affectedImportPaths?: string[] | null;\n}\n\n/** Affected-capture computation posted after build creation. */\nexport interface AffectedComputation {\n baselineSha: string | null;\n changedFiles: string[];\n affectedImportPaths: string[] | null;\n}\n\n/** Serialize an optional path list to its JSON text form (or null). */\nfunction serializePaths(paths: string[] | null | undefined): string | null {\n if (!paths) {\n return null;\n }\n return JSON.stringify(paths);\n}\n\n/** Parse a JSON path list; null (full capture) on missing/invalid input. */\nfunction parsePaths(raw: string | null): string[] | null {\n if (!raw) {\n return null;\n }\n try {\n const parsed: unknown = JSON.parse(raw);\n return Array.isArray(parsed) && parsed.every((entry) => typeof entry === \"string\")\n ? (parsed as string[])\n : null;\n } catch {\n return null;\n }\n}\n\n/** Filter options for listing builds. */\nexport interface BuildListFilter {\n status?: BuildStatus;\n branch?: string;\n labelKey?: string;\n labelValue?: string;\n}\n"],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { DatabaseAdapter } from "../adapter/database.mjs";
4
+ import { JobStatus } from "../adapter/capture-queue.mjs";
5
+ import { CaptureAttempt, CaptureLogLevel } from "../schema/capture-attempt.mjs";
6
+ import { Logger } from "pino";
7
+ import { Table } from "drizzle-orm";
8
+ //#region src/models/capture-attempt.d.ts
9
+ /** Tables required by {@link CaptureAttemptModel}. */
10
+ interface CaptureAttemptTables {
11
+ captureAttempts: Table;
12
+ }
13
+ /** Data operations for capture attempts (one row per build run). */
14
+ declare class CaptureAttemptModel {
15
+ private readonly db;
16
+ private readonly tables;
17
+ constructor(db: DatabaseAdapter, tables?: CaptureAttemptTables);
18
+ listByBuild(buildId: string): Promise<CaptureAttempt[]>;
19
+ getByNo(buildId: string, attemptNo: number): Promise<CaptureAttempt | null>;
20
+ startAttempt(projectId: string, buildId: string, reqId?: string): Promise<CaptureAttempt>;
21
+ markRunning(id: string): Promise<CaptureAttempt>;
22
+ markFinished(id: string, status: Extract<JobStatus, "completed" | "failed" | "cancelled">, error?: string, counts?: {
23
+ storyCount: number;
24
+ failedCount: number;
25
+ }): Promise<CaptureAttempt>;
26
+ }
27
+ /**
28
+ * Recorder writing one attempt log line to the `capture_logs` table.
29
+ * Dispatch builds it per attempt; the orchestrator and pipeline call it
30
+ * alongside every pino log so the UI can replay the run later.
31
+ */
32
+ type AttemptLogRecorder = (level: CaptureLogLevel, message: string, fields?: unknown) => Promise<void>;
33
+ /**
34
+ * Record one attempt log line without breaking the capture on write failure.
35
+ * Log-write failures are reported through pino and never thrown.
36
+ */
37
+ declare function emitAttemptLog(record: AttemptLogRecorder | undefined, logger: Logger | undefined, level: CaptureLogLevel, message: string, fields?: unknown): Promise<void>;
38
+ //#endregion
39
+ export { AttemptLogRecorder, CaptureAttemptModel, CaptureAttemptTables, emitAttemptLog };
40
+ //# sourceMappingURL=capture-attempt.d.mts.map
@@ -0,0 +1,85 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { ulid } from "../utils/ulid.mjs";
5
+ import { and, asc, eq, getTableColumns } from "drizzle-orm";
6
+ //#region src/models/capture-attempt.ts
7
+ /** Capture attempts and their log lines: per-run history for a build. */
8
+ /** Data operations for capture attempts (one row per build run). */
9
+ var CaptureAttemptModel = class {
10
+ db;
11
+ tables;
12
+ constructor(db, tables) {
13
+ this.db = db;
14
+ this.tables = tables ?? { captureAttempts: db.tables.captureAttempts };
15
+ }
16
+ async listByBuild(buildId) {
17
+ return await this.db.list(this.tables.captureAttempts, {
18
+ where: eq(columnsOf(this.tables.captureAttempts, "buildId"), buildId),
19
+ orderBy: asc(columnsOf(this.tables.captureAttempts, "attemptNo"))
20
+ });
21
+ }
22
+ async getByNo(buildId, attemptNo) {
23
+ return (await this.db.list(this.tables.captureAttempts, { where: and(eq(columnsOf(this.tables.captureAttempts, "buildId"), buildId), eq(columnsOf(this.tables.captureAttempts, "attemptNo"), attemptNo)) }))[0] ?? null;
24
+ }
25
+ async startAttempt(projectId, buildId, reqId) {
26
+ const existing = await this.listByBuild(buildId);
27
+ let attemptNo = 1;
28
+ for (const row of existing) if (row.attemptNo >= attemptNo) attemptNo = row.attemptNo + 1;
29
+ const now = (/* @__PURE__ */ new Date()).toISOString();
30
+ return await this.db.insert(this.tables.captureAttempts, {
31
+ id: ulid(),
32
+ projectId,
33
+ buildId,
34
+ attemptNo,
35
+ status: "queued",
36
+ error: null,
37
+ reqId: reqId ?? null,
38
+ storyCount: 0,
39
+ failedCount: 0,
40
+ queuedAt: now,
41
+ startedAt: null,
42
+ finishedAt: null,
43
+ createdAt: now,
44
+ updatedAt: now
45
+ });
46
+ }
47
+ async markRunning(id) {
48
+ const now = (/* @__PURE__ */ new Date()).toISOString();
49
+ return await this.db.update(this.tables.captureAttempts, id, {
50
+ status: "running",
51
+ startedAt: now,
52
+ updatedAt: now
53
+ });
54
+ }
55
+ async markFinished(id, status, error, counts) {
56
+ const now = (/* @__PURE__ */ new Date()).toISOString();
57
+ return await this.db.update(this.tables.captureAttempts, id, {
58
+ status,
59
+ error: error ?? null,
60
+ storyCount: counts?.storyCount ?? 0,
61
+ failedCount: counts?.failedCount ?? 0,
62
+ finishedAt: now,
63
+ updatedAt: now
64
+ });
65
+ }
66
+ };
67
+ function columnsOf(table, column) {
68
+ return getTableColumns(table)[column];
69
+ }
70
+ /**
71
+ * Record one attempt log line without breaking the capture on write failure.
72
+ * Log-write failures are reported through pino and never thrown.
73
+ */
74
+ async function emitAttemptLog(record, logger, level, message, fields) {
75
+ if (!record) return;
76
+ try {
77
+ await record(level, message, fields);
78
+ } catch (error) {
79
+ logger?.warn({ err: error }, "attempt log write failed");
80
+ }
81
+ }
82
+ //#endregion
83
+ export { CaptureAttemptModel, emitAttemptLog };
84
+
85
+ //# sourceMappingURL=capture-attempt.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture-attempt.mjs","names":[],"sources":["../../src/models/capture-attempt.ts"],"sourcesContent":["/** Capture attempts and their log lines: per-run history for a build. */\nimport { and, asc, eq, getTableColumns } from \"drizzle-orm\";\nimport type { SQLWrapper, Table } from \"drizzle-orm\";\nimport type { Logger } from \"pino\";\nimport type { JobStatus } from \"../adapters/capture-queue.ts\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { CaptureAttempt, CaptureLogLevel } from \"../schema/capture-attempt.ts\";\nimport { ulid } from \"../utils/ulid.ts\";\n\n/** Tables required by {@link CaptureAttemptModel}. */\nexport interface CaptureAttemptTables {\n captureAttempts: Table;\n}\n\n/** Data operations for capture attempts (one row per build run). */\nexport class CaptureAttemptModel {\n private readonly tables: CaptureAttemptTables;\n constructor(\n private readonly db: DatabaseAdapter,\n tables?: CaptureAttemptTables,\n ) {\n this.tables = tables ?? { captureAttempts: db.tables.captureAttempts };\n }\n\n async listByBuild(buildId: string): Promise<CaptureAttempt[]> {\n return (await this.db.list(this.tables.captureAttempts, {\n where: eq(columnsOf(this.tables.captureAttempts, \"buildId\"), buildId),\n orderBy: asc(columnsOf(this.tables.captureAttempts, \"attemptNo\")),\n })) as unknown as CaptureAttempt[];\n }\n\n async getByNo(buildId: string, attemptNo: number): Promise<CaptureAttempt | null> {\n const rows = (await this.db.list(this.tables.captureAttempts, {\n where: and(\n eq(columnsOf(this.tables.captureAttempts, \"buildId\"), buildId),\n eq(columnsOf(this.tables.captureAttempts, \"attemptNo\"), attemptNo),\n ),\n })) as unknown as CaptureAttempt[];\n return rows[0] ?? null;\n }\n\n async startAttempt(projectId: string, buildId: string, reqId?: string): Promise<CaptureAttempt> {\n const existing = await this.listByBuild(buildId);\n let attemptNo = 1;\n for (const row of existing) {\n if (row.attemptNo >= attemptNo) {\n attemptNo = row.attemptNo + 1;\n }\n }\n const now = new Date().toISOString();\n return (await this.db.insert(this.tables.captureAttempts, {\n id: ulid(),\n projectId,\n buildId,\n attemptNo,\n status: \"queued\",\n error: null,\n reqId: reqId ?? null,\n storyCount: 0,\n failedCount: 0,\n queuedAt: now,\n startedAt: null,\n finishedAt: null,\n createdAt: now,\n updatedAt: now,\n })) as unknown as CaptureAttempt;\n }\n\n async markRunning(id: string): Promise<CaptureAttempt> {\n const now = new Date().toISOString();\n return (await this.db.update(this.tables.captureAttempts, id, {\n status: \"running\",\n startedAt: now,\n updatedAt: now,\n })) as unknown as CaptureAttempt;\n }\n\n async markFinished(\n id: string,\n status: Extract<JobStatus, \"completed\" | \"failed\" | \"cancelled\">,\n error?: string,\n counts?: { storyCount: number; failedCount: number },\n ): Promise<CaptureAttempt> {\n const now = new Date().toISOString();\n return (await this.db.update(this.tables.captureAttempts, id, {\n status,\n error: error ?? null,\n storyCount: counts?.storyCount ?? 0,\n failedCount: counts?.failedCount ?? 0,\n finishedAt: now,\n updatedAt: now,\n })) as unknown as CaptureAttempt;\n }\n}\n\nfunction columnsOf(table: Table, column: string): SQLWrapper {\n return getTableColumns(table)[column] as unknown as SQLWrapper;\n}\n\n/**\n * Recorder writing one attempt log line to the `capture_logs` table.\n * Dispatch builds it per attempt; the orchestrator and pipeline call it\n * alongside every pino log so the UI can replay the run later.\n */\nexport type AttemptLogRecorder = (\n level: CaptureLogLevel,\n message: string,\n fields?: unknown,\n) => Promise<void>;\n\n/**\n * Record one attempt log line without breaking the capture on write failure.\n * Log-write failures are reported through pino and never thrown.\n */\nexport async function emitAttemptLog(\n record: AttemptLogRecorder | undefined,\n logger: Logger | undefined,\n level: CaptureLogLevel,\n message: string,\n fields?: unknown,\n): Promise<void> {\n if (!record) {\n return;\n }\n try {\n await record(level, message, fields);\n } catch (error) {\n logger?.warn({ err: error }, \"attempt log write failed\");\n }\n}\n"],"mappings":";;;;;;;;AAeA,IAAa,sBAAb,MAAiC;CAGZ;CAFnB;CACA,YACE,IACA,QACA;EAFiB,KAAA,KAAA;EAGjB,KAAK,SAAS,UAAU,EAAE,iBAAiB,GAAG,OAAO,gBAAgB;CACvE;CAEA,MAAM,YAAY,SAA4C;EAC5D,OAAQ,MAAM,KAAK,GAAG,KAAK,KAAK,OAAO,iBAAiB;GACtD,OAAO,GAAG,UAAU,KAAK,OAAO,iBAAiB,SAAS,GAAG,OAAO;GACpE,SAAS,IAAI,UAAU,KAAK,OAAO,iBAAiB,WAAW,CAAC;EAClE,CAAC;CACH;CAEA,MAAM,QAAQ,SAAiB,WAAmD;EAOhF,QAAO,MANa,KAAK,GAAG,KAAK,KAAK,OAAO,iBAAiB,EAC5D,OAAO,IACL,GAAG,UAAU,KAAK,OAAO,iBAAiB,SAAS,GAAG,OAAO,GAC7D,GAAG,UAAU,KAAK,OAAO,iBAAiB,WAAW,GAAG,SAAS,CACnE,EACF,CAAC,EAAA,CACW,MAAM;CACpB;CAEA,MAAM,aAAa,WAAmB,SAAiB,OAAyC;EAC9F,MAAM,WAAW,MAAM,KAAK,YAAY,OAAO;EAC/C,IAAI,YAAY;EAChB,KAAK,MAAM,OAAO,UAChB,IAAI,IAAI,aAAa,WACnB,YAAY,IAAI,YAAY;EAGhC,MAAM,uBAAM,IAAI,KAAK,EAAA,CAAE,YAAY;EACnC,OAAQ,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,iBAAiB;GACxD,IAAI,KAAK;GACT;GACA;GACA;GACA,QAAQ;GACR,OAAO;GACP,OAAO,SAAS;GAChB,YAAY;GACZ,aAAa;GACb,UAAU;GACV,WAAW;GACX,YAAY;GACZ,WAAW;GACX,WAAW;EACb,CAAC;CACH;CAEA,MAAM,YAAY,IAAqC;EACrD,MAAM,uBAAM,IAAI,KAAK,EAAA,CAAE,YAAY;EACnC,OAAQ,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,iBAAiB,IAAI;GAC5D,QAAQ;GACR,WAAW;GACX,WAAW;EACb,CAAC;CACH;CAEA,MAAM,aACJ,IACA,QACA,OACA,QACyB;EACzB,MAAM,uBAAM,IAAI,KAAK,EAAA,CAAE,YAAY;EACnC,OAAQ,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,iBAAiB,IAAI;GAC5D;GACA,OAAO,SAAS;GAChB,YAAY,QAAQ,cAAc;GAClC,aAAa,QAAQ,eAAe;GACpC,YAAY;GACZ,WAAW;EACb,CAAC;CACH;AACF;AAEA,SAAS,UAAU,OAAc,QAA4B;CAC3D,OAAO,gBAAgB,KAAK,CAAC,CAAC;AAChC;;;;;AAiBA,eAAsB,eACpB,QACA,QACA,OACA,SACA,QACe;CACf,IAAI,CAAC,QACH;CAEF,IAAI;EACF,MAAM,OAAO,OAAO,SAAS,MAAM;CACrC,SAAS,OAAO;EACd,QAAQ,KAAK,EAAE,KAAK,MAAM,GAAG,0BAA0B;CACzD;AACF"}
@@ -0,0 +1,21 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { DatabaseAdapter } from "../adapter/database.mjs";
4
+ import { CaptureLog, CaptureLogLevel } from "../schema/capture-attempt.mjs";
5
+ import { Table } from "drizzle-orm";
6
+ //#region src/models/capture-log.d.ts
7
+ /** Tables required by {@link CaptureLogModel}. */
8
+ interface CaptureLogTables {
9
+ captureLogs: Table;
10
+ }
11
+ /** Data operations for capture log lines (every line of an attempt). */
12
+ declare class CaptureLogModel {
13
+ private readonly db;
14
+ private readonly tables;
15
+ constructor(db: DatabaseAdapter, tables?: CaptureLogTables);
16
+ listByAttempt(attemptId: string, limit?: number): Promise<CaptureLog[]>;
17
+ append(projectId: string, buildId: string, attemptId: string, level: CaptureLogLevel, message: string, fields?: unknown): Promise<CaptureLog>;
18
+ }
19
+ //#endregion
20
+ export { CaptureLogModel, CaptureLogTables };
21
+ //# sourceMappingURL=capture-log.d.mts.map
@@ -0,0 +1,44 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { ulid } from "../utils/ulid.mjs";
5
+ import { asc, eq, getTableColumns } from "drizzle-orm";
6
+ //#region src/models/capture-log.ts
7
+ /** Capture log lines: every stored line of a capture attempt. */
8
+ /** Data operations for capture log lines (every line of an attempt). */
9
+ var CaptureLogModel = class {
10
+ db;
11
+ tables;
12
+ constructor(db, tables) {
13
+ this.db = db;
14
+ this.tables = tables ?? { captureLogs: db.tables.captureLogs };
15
+ }
16
+ async listByAttempt(attemptId, limit) {
17
+ return await this.db.list(this.tables.captureLogs, {
18
+ where: eq(columnsOf(this.tables.captureLogs, "attemptId"), attemptId),
19
+ orderBy: asc(columnsOf(this.tables.captureLogs, "seq")),
20
+ ...limit === void 0 ? {} : { limit }
21
+ });
22
+ }
23
+ async append(projectId, buildId, attemptId, level, message, fields) {
24
+ const seq = await this.db.count(this.tables.captureLogs, eq(columnsOf(this.tables.captureLogs, "attemptId"), attemptId)) + 1;
25
+ return await this.db.insert(this.tables.captureLogs, {
26
+ id: ulid(),
27
+ projectId,
28
+ buildId,
29
+ attemptId,
30
+ seq,
31
+ level,
32
+ message,
33
+ fields: fields === void 0 ? null : JSON.stringify(fields),
34
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
35
+ });
36
+ }
37
+ };
38
+ function columnsOf(table, column) {
39
+ return getTableColumns(table)[column];
40
+ }
41
+ //#endregion
42
+ export { CaptureLogModel };
43
+
44
+ //# sourceMappingURL=capture-log.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture-log.mjs","names":[],"sources":["../../src/models/capture-log.ts"],"sourcesContent":["/** Capture log lines: every stored line of a capture attempt. */\nimport { asc, eq, getTableColumns } from \"drizzle-orm\";\nimport type { SQLWrapper, Table } from \"drizzle-orm\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { CaptureLog, CaptureLogLevel } from \"../schema/capture-attempt.ts\";\nimport { ulid } from \"../utils/ulid.ts\";\n\n/** Tables required by {@link CaptureLogModel}. */\nexport interface CaptureLogTables {\n captureLogs: Table;\n}\n\n/** Data operations for capture log lines (every line of an attempt). */\nexport class CaptureLogModel {\n private readonly tables: CaptureLogTables;\n constructor(\n private readonly db: DatabaseAdapter,\n tables?: CaptureLogTables,\n ) {\n this.tables = tables ?? { captureLogs: db.tables.captureLogs };\n }\n\n async listByAttempt(attemptId: string, limit?: number): Promise<CaptureLog[]> {\n return (await this.db.list(this.tables.captureLogs, {\n where: eq(columnsOf(this.tables.captureLogs, \"attemptId\"), attemptId),\n orderBy: asc(columnsOf(this.tables.captureLogs, \"seq\")),\n ...(limit === undefined ? {} : { limit }),\n })) as unknown as CaptureLog[];\n }\n\n async append(\n projectId: string,\n buildId: string,\n attemptId: string,\n level: CaptureLogLevel,\n message: string,\n fields?: unknown,\n ): Promise<CaptureLog> {\n const seq =\n (await this.db.count(\n this.tables.captureLogs,\n eq(columnsOf(this.tables.captureLogs, \"attemptId\"), attemptId),\n )) + 1;\n return (await this.db.insert(this.tables.captureLogs, {\n id: ulid(),\n projectId,\n buildId,\n attemptId,\n seq,\n level,\n message,\n fields: fields === undefined ? null : JSON.stringify(fields),\n createdAt: new Date().toISOString(),\n })) as unknown as CaptureLog;\n }\n}\n\nfunction columnsOf(table: Table, column: string): SQLWrapper {\n return getTableColumns(table)[column] as unknown as SQLWrapper;\n}\n"],"mappings":";;;;;;;;AAaA,IAAa,kBAAb,MAA6B;CAGR;CAFnB;CACA,YACE,IACA,QACA;EAFiB,KAAA,KAAA;EAGjB,KAAK,SAAS,UAAU,EAAE,aAAa,GAAG,OAAO,YAAY;CAC/D;CAEA,MAAM,cAAc,WAAmB,OAAuC;EAC5E,OAAQ,MAAM,KAAK,GAAG,KAAK,KAAK,OAAO,aAAa;GAClD,OAAO,GAAG,UAAU,KAAK,OAAO,aAAa,WAAW,GAAG,SAAS;GACpE,SAAS,IAAI,UAAU,KAAK,OAAO,aAAa,KAAK,CAAC;GACtD,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;EACzC,CAAC;CACH;CAEA,MAAM,OACJ,WACA,SACA,WACA,OACA,SACA,QACqB;EACrB,MAAM,MACH,MAAM,KAAK,GAAG,MACb,KAAK,OAAO,aACZ,GAAG,UAAU,KAAK,OAAO,aAAa,WAAW,GAAG,SAAS,CAC/D,IAAK;EACP,OAAQ,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,aAAa;GACpD,IAAI,KAAK;GACT;GACA;GACA;GACA;GACA;GACA;GACA,QAAQ,WAAW,KAAA,IAAY,OAAO,KAAK,UAAU,MAAM;GAC3D,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;EACpC,CAAC;CACH;AACF;AAEA,SAAS,UAAU,OAAc,QAA4B;CAC3D,OAAO,gBAAgB,KAAK,CAAC,CAAC;AAChC"}
@@ -0,0 +1,29 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { DatabaseAdapter } from "../adapter/database.mjs";
4
+ import { Comment } from "../schema/comment.mjs";
5
+ import { Table } from "drizzle-orm";
6
+ //#region src/models/comment.d.ts
7
+ /** Tables required by {@link CommentModel}. */
8
+ interface CommentTables {
9
+ comments: Table;
10
+ projects: Table;
11
+ }
12
+ /** Data operations for review comments. */
13
+ declare class CommentModel {
14
+ private readonly db;
15
+ private readonly tables;
16
+ constructor(db: DatabaseAdapter, tables?: CommentTables);
17
+ listByBuild(buildId: string): Promise<Comment[]>;
18
+ create(projectId: string, buildId: string, userId: string | null, input: CommentCreateInput): Promise<Comment>;
19
+ resolve(id: string): Promise<Comment>;
20
+ }
21
+ /** Input for creating a review comment. */
22
+ interface CommentCreateInput {
23
+ body: string;
24
+ snapshotId?: string;
25
+ parentId?: string;
26
+ }
27
+ //#endregion
28
+ export { CommentCreateInput, CommentModel, CommentTables };
29
+ //# sourceMappingURL=comment.d.mts.map
@@ -1,25 +1,28 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
- import { projects } from "../schema/project.mjs";
5
4
  import { ulid } from "../utils/ulid.mjs";
6
- import { comments } from "../schema/comment.mjs";
7
- import { eq } from "drizzle-orm";
5
+ import { eq, getTableColumns } from "drizzle-orm";
8
6
  //#region src/models/comment.ts
9
7
  /** Build and snapshot review comments. */
10
8
  /** Data operations for review comments. */
11
9
  var CommentModel = class {
12
10
  db;
13
- constructor(db) {
11
+ tables;
12
+ constructor(db, tables) {
14
13
  this.db = db;
14
+ this.tables = tables ?? {
15
+ comments: db.tables.comments,
16
+ projects: db.tables.projects
17
+ };
15
18
  }
16
19
  async listByBuild(buildId) {
17
- return await this.db.list(comments, { where: eq(comments.buildId, buildId) });
20
+ return await this.db.list(this.tables.comments, { where: eq(getTableColumns(this.tables.comments)["buildId"], buildId) });
18
21
  }
19
22
  async create(projectId, buildId, userId, input) {
20
23
  const now = (/* @__PURE__ */ new Date()).toISOString();
21
- if (!await this.db.get(projects, projectId)) throw new Error(`Project not found: ${projectId}`);
22
- return await this.db.insert(comments, {
24
+ if (!await this.db.get(this.tables.projects, projectId)) throw new Error(`Project not found: ${projectId}`);
25
+ return await this.db.insert(this.tables.comments, {
23
26
  id: ulid(),
24
27
  projectId,
25
28
  buildId,
@@ -33,7 +36,7 @@ var CommentModel = class {
33
36
  });
34
37
  }
35
38
  async resolve(id) {
36
- return await this.db.update(comments, id, {
39
+ return await this.db.update(this.tables.comments, id, {
37
40
  resolved: true,
38
41
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
39
42
  });
@@ -1 +1 @@
1
- {"version":3,"file":"comment.mjs","names":[],"sources":["../../src/models/comment.ts"],"sourcesContent":["/** Build and snapshot review comments. */\nimport { eq } from \"drizzle-orm\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport { comments } from \"../schema/comment.ts\";\nimport type { Comment } from \"../schema/comment.ts\";\nimport { projects } from \"../schema/project.ts\";\nimport { ulid } from \"../utils/ulid.ts\";\n\n/** Input for creating a review comment. */\nexport interface CommentCreateInput {\n body: string;\n snapshotId?: string;\n parentId?: string;\n}\n\n/** Data operations for review comments. */\nexport class CommentModel {\n constructor(private readonly db: DatabaseAdapter) {}\n\n async listByBuild(buildId: string): Promise<Comment[]> {\n return await this.db.list(comments, { where: eq(comments.buildId, buildId) });\n }\n\n async create(\n projectId: string,\n buildId: string,\n userId: string,\n input: CommentCreateInput,\n ): Promise<Comment> {\n const now = new Date().toISOString();\n const project = await this.db.get(projects, projectId);\n if (!project) {\n throw new Error(`Project not found: ${projectId}`);\n }\n return await this.db.insert(comments, {\n id: ulid(),\n projectId,\n buildId,\n snapshotId: input.snapshotId,\n userId,\n body: input.body,\n parentId: input.parentId,\n resolved: false,\n createdAt: now,\n updatedAt: now,\n });\n }\n\n async resolve(id: string): Promise<Comment> {\n return await this.db.update(comments, id, {\n resolved: true,\n updatedAt: new Date().toISOString(),\n });\n }\n}\n"],"mappings":";;;;;;;;;;AAgBA,IAAa,eAAb,MAA0B;CACK;CAA7B,YAAY,IAAsC;EAArB,KAAA,KAAA;CAAsB;CAEnD,MAAM,YAAY,SAAqC;EACrD,OAAO,MAAM,KAAK,GAAG,KAAK,UAAU,EAAE,OAAO,GAAG,SAAS,SAAS,OAAO,EAAE,CAAC;CAC9E;CAEA,MAAM,OACJ,WACA,SACA,QACA,OACkB;EAClB,MAAM,uBAAM,IAAI,KAAK,EAAA,CAAE,YAAY;EAEnC,IAAI,CAAC,MADiB,KAAK,GAAG,IAAI,UAAU,SAAS,GAEnD,MAAM,IAAI,MAAM,sBAAsB,WAAW;EAEnD,OAAO,MAAM,KAAK,GAAG,OAAO,UAAU;GACpC,IAAI,KAAK;GACT;GACA;GACA,YAAY,MAAM;GAClB;GACA,MAAM,MAAM;GACZ,UAAU,MAAM;GAChB,UAAU;GACV,WAAW;GACX,WAAW;EACb,CAAC;CACH;CAEA,MAAM,QAAQ,IAA8B;EAC1C,OAAO,MAAM,KAAK,GAAG,OAAO,UAAU,IAAI;GACxC,UAAU;GACV,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;EACpC,CAAC;CACH;AACF"}
1
+ {"version":3,"file":"comment.mjs","names":[],"sources":["../../src/models/comment.ts"],"sourcesContent":["/** Build and snapshot review comments. */\nimport { eq, getTableColumns } from \"drizzle-orm\";\nimport type { SQLWrapper, Table } from \"drizzle-orm\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { Comment } from \"../schema/comment.ts\";\nimport { ulid } from \"../utils/ulid.ts\";\n\n/** Tables required by {@link CommentModel}. */\nexport interface CommentTables {\n comments: Table;\n projects: Table;\n}\n\n/** Data operations for review comments. */\nexport class CommentModel {\n private readonly tables: CommentTables;\n constructor(\n private readonly db: DatabaseAdapter,\n tables?: CommentTables,\n ) {\n this.tables = tables ?? {\n comments: db.tables.comments,\n projects: db.tables.projects,\n };\n }\n\n async listByBuild(buildId: string): Promise<Comment[]> {\n return (await this.db.list(this.tables.comments, {\n where: eq(getTableColumns(this.tables.comments)[\"buildId\"] as unknown as SQLWrapper, buildId),\n })) as unknown as Comment[];\n }\n\n async create(\n projectId: string,\n buildId: string,\n userId: string | null,\n input: CommentCreateInput,\n ): Promise<Comment> {\n const now = new Date().toISOString();\n const project = (await this.db.get(this.tables.projects, projectId)) as unknown as {\n id: string;\n } | null;\n if (!project) {\n throw new Error(`Project not found: ${projectId}`);\n }\n return (await this.db.insert(this.tables.comments, {\n id: ulid(),\n projectId,\n buildId,\n snapshotId: input.snapshotId,\n userId,\n body: input.body,\n parentId: input.parentId,\n resolved: false,\n createdAt: now,\n updatedAt: now,\n })) as unknown as Comment;\n }\n\n async resolve(id: string): Promise<Comment> {\n return (await this.db.update(this.tables.comments, id, {\n resolved: true,\n updatedAt: new Date().toISOString(),\n })) as unknown as Comment;\n }\n}\n\n/** Input for creating a review comment. */\nexport interface CommentCreateInput {\n body: string;\n snapshotId?: string;\n parentId?: string;\n}\n"],"mappings":";;;;;;;;AAcA,IAAa,eAAb,MAA0B;CAGL;CAFnB;CACA,YACE,IACA,QACA;EAFiB,KAAA,KAAA;EAGjB,KAAK,SAAS,UAAU;GACtB,UAAU,GAAG,OAAO;GACpB,UAAU,GAAG,OAAO;EACtB;CACF;CAEA,MAAM,YAAY,SAAqC;EACrD,OAAQ,MAAM,KAAK,GAAG,KAAK,KAAK,OAAO,UAAU,EAC/C,OAAO,GAAG,gBAAgB,KAAK,OAAO,QAAQ,CAAC,CAAC,YAAqC,OAAO,EAC9F,CAAC;CACH;CAEA,MAAM,OACJ,WACA,SACA,QACA,OACkB;EAClB,MAAM,uBAAM,IAAI,KAAK,EAAA,CAAE,YAAY;EAInC,IAAI,CAAC,MAHkB,KAAK,GAAG,IAAI,KAAK,OAAO,UAAU,SAAS,GAIhE,MAAM,IAAI,MAAM,sBAAsB,WAAW;EAEnD,OAAQ,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,UAAU;GACjD,IAAI,KAAK;GACT;GACA;GACA,YAAY,MAAM;GAClB;GACA,MAAM,MAAM;GACZ,UAAU,MAAM;GAChB,UAAU;GACV,WAAW;GACX,WAAW;EACb,CAAC;CACH;CAEA,MAAM,QAAQ,IAA8B;EAC1C,OAAQ,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,UAAU,IAAI;GACrD,UAAU;GACV,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;EACpC,CAAC;CACH;AACF"}
@@ -0,0 +1,21 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { DatabaseAdapter } from "../adapter/database.mjs";
4
+ import { ContentRef } from "../schema/content-ref.mjs";
5
+ import { Table } from "drizzle-orm";
6
+ //#region src/models/content-ref.d.ts
7
+ /** Content refs for deduplicated Storybook assets. */
8
+ declare class ContentRefModel {
9
+ private readonly db;
10
+ private readonly tables;
11
+ constructor(db: DatabaseAdapter, tables?: {
12
+ contentRefs: Table;
13
+ });
14
+ get(hash: string): Promise<ContentRef | null>;
15
+ upsert(hash: string): Promise<ContentRef>;
16
+ decrement(hash: string): Promise<ContentRef | null>;
17
+ listStale(cutoff: string): Promise<ContentRef[]>;
18
+ }
19
+ //#endregion
20
+ export { ContentRefModel };
21
+ //# sourceMappingURL=content-ref.d.mts.map