@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
package/dist/paths.mjs CHANGED
@@ -1,25 +1,54 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
+ import { isSafeSegment } from "./utils/path-security.mjs";
4
5
  //#region src/utils/paths.ts
6
+ /** Storage path builders. Every segment is validated — a third-party
7
+ * capture runner echoing `story.id = "../../x"` must fail here, not in an
8
+ * adapter with its own key-jailing quirks. */
9
+ /** Throw when any key segment could escape its storage prefix. */
10
+ function assertSegments(segments) {
11
+ const bad = segments.filter((segment) => !isSafeSegment(segment) || segment.length === 0);
12
+ if (bad.length > 0) throw new Error(`Unsafe storage path segment: ${bad.join(",")}`);
13
+ }
5
14
  /** Storage path for a captured snapshot screenshot. */
6
15
  function screenshotPath(projectId, buildId, storyId, viewport) {
16
+ assertSegments([
17
+ projectId,
18
+ buildId,
19
+ storyId,
20
+ viewport
21
+ ]);
7
22
  return `${projectId}/builds/${buildId}/screenshots/${storyId}/${viewport}.png`;
8
23
  }
9
24
  /** Storage path for a snapshot diff overlay image. */
10
25
  function diffPath(projectId, buildId, storyId, viewport) {
26
+ assertSegments([
27
+ projectId,
28
+ buildId,
29
+ storyId,
30
+ viewport
31
+ ]);
11
32
  return `${projectId}/builds/${buildId}/diffs/${storyId}/${viewport}.png`;
12
33
  }
13
34
  /** Storage path for a branch baseline screenshot. */
14
35
  function baselinePath(projectId, branch, storyId, viewport) {
36
+ assertSegments([
37
+ projectId,
38
+ branch,
39
+ storyId,
40
+ viewport
41
+ ]);
15
42
  return `${projectId}/baselines/${branch}/${storyId}/${viewport}.png`;
16
43
  }
17
44
  /** Storage prefix for an extracted published Storybook. */
18
45
  function storybookDir(projectId, buildId) {
46
+ assertSegments([projectId, buildId]);
19
47
  return `${projectId}/builds/${buildId}/storybook`;
20
48
  }
21
49
  /** Storage path for an uploaded Storybook zip. */
22
50
  function storybookZipPath(projectId, buildId) {
51
+ assertSegments([projectId, buildId]);
23
52
  return `${projectId}/builds/${buildId}/storybook.zip`;
24
53
  }
25
54
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"paths.mjs","names":[],"sources":["../src/utils/paths.ts"],"sourcesContent":["/** Storage path for a captured snapshot screenshot. */\nexport function screenshotPath(\n projectId: string,\n buildId: string,\n storyId: string,\n viewport: string,\n): string {\n return `${projectId}/builds/${buildId}/screenshots/${storyId}/${viewport}.png`;\n}\n\n/** Storage path for a snapshot diff overlay image. */\nexport function diffPath(\n projectId: string,\n buildId: string,\n storyId: string,\n viewport: string,\n): string {\n return `${projectId}/builds/${buildId}/diffs/${storyId}/${viewport}.png`;\n}\n\n/** Storage path for a branch baseline screenshot. */\nexport function baselinePath(\n projectId: string,\n branch: string,\n storyId: string,\n viewport: string,\n): string {\n return `${projectId}/baselines/${branch}/${storyId}/${viewport}.png`;\n}\n\n/** Storage prefix for an extracted published Storybook. */\nexport function storybookDir(projectId: string, buildId: string): string {\n return `${projectId}/builds/${buildId}/storybook`;\n}\n\n/** Storage path for an uploaded Storybook zip. */\nexport function storybookZipPath(projectId: string, buildId: string): string {\n return `${projectId}/builds/${buildId}/storybook.zip`;\n}\n"],"mappings":";;;;;AACA,SAAgB,eACd,WACA,SACA,SACA,UACQ;CACR,OAAO,GAAG,UAAU,UAAU,QAAQ,eAAe,QAAQ,GAAG,SAAS;AAC3E;;AAGA,SAAgB,SACd,WACA,SACA,SACA,UACQ;CACR,OAAO,GAAG,UAAU,UAAU,QAAQ,SAAS,QAAQ,GAAG,SAAS;AACrE;;AAGA,SAAgB,aACd,WACA,QACA,SACA,UACQ;CACR,OAAO,GAAG,UAAU,aAAa,OAAO,GAAG,QAAQ,GAAG,SAAS;AACjE;;AAGA,SAAgB,aAAa,WAAmB,SAAyB;CACvE,OAAO,GAAG,UAAU,UAAU,QAAQ;AACxC;;AAGA,SAAgB,iBAAiB,WAAmB,SAAyB;CAC3E,OAAO,GAAG,UAAU,UAAU,QAAQ;AACxC"}
1
+ {"version":3,"file":"paths.mjs","names":[],"sources":["../src/utils/paths.ts"],"sourcesContent":["/** Storage path builders. Every segment is validated — a third-party\n * capture runner echoing `story.id = \"../../x\"` must fail here, not in an\n * adapter with its own key-jailing quirks. */\nimport { isSafeSegment } from \"./path-security.ts\";\n\n/** Throw when any key segment could escape its storage prefix. */\nfunction assertSegments(segments: string[]): void {\n const bad = segments.filter((segment) => !isSafeSegment(segment) || segment.length === 0);\n if (bad.length > 0) {\n throw new Error(`Unsafe storage path segment: ${bad.join(\",\")}`);\n }\n}\n/** Storage path for a captured snapshot screenshot. */\nexport function screenshotPath(\n projectId: string,\n buildId: string,\n storyId: string,\n viewport: string,\n): string {\n assertSegments([projectId, buildId, storyId, viewport]);\n return `${projectId}/builds/${buildId}/screenshots/${storyId}/${viewport}.png`;\n}\n\n/** Storage path for a snapshot diff overlay image. */\nexport function diffPath(\n projectId: string,\n buildId: string,\n storyId: string,\n viewport: string,\n): string {\n assertSegments([projectId, buildId, storyId, viewport]);\n return `${projectId}/builds/${buildId}/diffs/${storyId}/${viewport}.png`;\n}\n\n/** Storage path for a branch baseline screenshot. */\nexport function baselinePath(\n projectId: string,\n branch: string,\n storyId: string,\n viewport: string,\n): string {\n assertSegments([projectId, branch, storyId, viewport]);\n return `${projectId}/baselines/${branch}/${storyId}/${viewport}.png`;\n}\n\n/** Storage prefix for an extracted published Storybook. */\nexport function storybookDir(projectId: string, buildId: string): string {\n assertSegments([projectId, buildId]);\n return `${projectId}/builds/${buildId}/storybook`;\n}\n\n/** Storage path for an uploaded Storybook zip. */\nexport function storybookZipPath(projectId: string, buildId: string): string {\n assertSegments([projectId, buildId]);\n return `${projectId}/builds/${buildId}/storybook.zip`;\n}\n"],"mappings":";;;;;;;;;AAMA,SAAS,eAAe,UAA0B;CAChD,MAAM,MAAM,SAAS,QAAQ,YAAY,CAAC,cAAc,OAAO,KAAK,QAAQ,WAAW,CAAC;CACxF,IAAI,IAAI,SAAS,GACf,MAAM,IAAI,MAAM,gCAAgC,IAAI,KAAK,GAAG,GAAG;AAEnE;;AAEA,SAAgB,eACd,WACA,SACA,SACA,UACQ;CACR,eAAe;EAAC;EAAW;EAAS;EAAS;CAAQ,CAAC;CACtD,OAAO,GAAG,UAAU,UAAU,QAAQ,eAAe,QAAQ,GAAG,SAAS;AAC3E;;AAGA,SAAgB,SACd,WACA,SACA,SACA,UACQ;CACR,eAAe;EAAC;EAAW;EAAS;EAAS;CAAQ,CAAC;CACtD,OAAO,GAAG,UAAU,UAAU,QAAQ,SAAS,QAAQ,GAAG,SAAS;AACrE;;AAGA,SAAgB,aACd,WACA,QACA,SACA,UACQ;CACR,eAAe;EAAC;EAAW;EAAQ;EAAS;CAAQ,CAAC;CACrD,OAAO,GAAG,UAAU,aAAa,OAAO,GAAG,QAAQ,GAAG,SAAS;AACjE;;AAGA,SAAgB,aAAa,WAAmB,SAAyB;CACvE,eAAe,CAAC,WAAW,OAAO,CAAC;CACnC,OAAO,GAAG,UAAU,UAAU,QAAQ;AACxC;;AAGA,SAAgB,iBAAiB,WAAmB,SAAyB;CAC3E,eAAe,CAAC,WAAW,OAAO,CAAC;CACnC,OAAO,GAAG,UAAU,UAAU,QAAQ;AACxC"}
@@ -0,0 +1,45 @@
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 { StorageAdapter } from "./adapter/storage.mjs";
5
+ import { Project } from "./schema/project.mjs";
6
+ import { Logger } from "pino";
7
+ import { Table } from "drizzle-orm";
8
+ //#region src/retention/purge.d.ts
9
+ /** Table handles for retention queries. */
10
+ interface RetentionTables {
11
+ builds: Table;
12
+ buildLabels: Table;
13
+ baselines: Table;
14
+ }
15
+ /** Removes expired transient builds while keeping baselines and persistent builds. */
16
+ declare class Retention {
17
+ private readonly db;
18
+ private readonly storage;
19
+ private readonly tables;
20
+ private readonly logger?;
21
+ constructor(db: DatabaseAdapter, storage: StorageAdapter, tables: RetentionTables, logger?: Logger | undefined);
22
+ purge(project: Project, options: PurgeOptions): Promise<PurgeResult>;
23
+ private removeBuild;
24
+ private latestPerBranch;
25
+ private deleteBuildFiles;
26
+ purgeStaleBranches(project: Project, ttlDays: number): Promise<BranchGcResult>;
27
+ }
28
+ /** Options controlling which builds a retention purge removes. */
29
+ interface PurgeOptions {
30
+ ttlDays: number;
31
+ keepLatestPerBranch: boolean;
32
+ }
33
+ /** Counts of builds and files removed by a retention purge. */
34
+ interface PurgeResult {
35
+ removedBuilds: number;
36
+ removedFiles: number;
37
+ }
38
+ /** Counts of stale branches and baselines removed. */
39
+ interface BranchGcResult {
40
+ removedBranches: number;
41
+ removedBaselines: number;
42
+ }
43
+ //#endregion
44
+ export { BranchGcResult, PurgeOptions, PurgeResult, Retention, RetentionTables };
45
+ //# sourceMappingURL=retention.d.mts.map
@@ -0,0 +1,145 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { TERMINAL_BUILD_STATUSES } from "./types.mjs";
5
+ import { BuildModel } from "./models/build.mjs";
6
+ import { BaselineModel } from "./models/baseline.mjs";
7
+ import { LabelModel } from "./models/label.mjs";
8
+ import { and, desc, eq, getTableColumns, inArray, lt } from "drizzle-orm";
9
+ //#region src/retention/purge.ts
10
+ /** Removes expired transient builds while keeping baselines and persistent builds. */
11
+ var Retention = class {
12
+ db;
13
+ storage;
14
+ tables;
15
+ logger;
16
+ constructor(db, storage, tables, logger) {
17
+ this.db = db;
18
+ this.storage = storage;
19
+ this.tables = tables;
20
+ this.logger = logger;
21
+ }
22
+ async purge(project, options) {
23
+ const cutoff = (/* @__PURE__ */ new Date(Date.now() - options.ttlDays * 864e5)).toISOString();
24
+ const candidates = await this.db.list(this.tables.builds, { where: and(eq(getTableColumns(this.tables.builds)["projectId"], project.id), inArray(getTableColumns(this.tables.builds)["status"], [...TERMINAL_BUILD_STATUSES]), lt(getTableColumns(this.tables.builds)["updatedAt"], cutoff)) });
25
+ const keep = options.keepLatestPerBranch ? await this.latestPerBranch(project.id) : /* @__PURE__ */ new Set();
26
+ const labelModel = new LabelModel(this.db, this.tables);
27
+ const buildIds = (await Promise.all(candidates.map(async (build) => {
28
+ const buildId = build.id;
29
+ if (keep.has(buildId) || await labelModel.hasPersistent(project.id, buildId)) return null;
30
+ return buildId;
31
+ }))).filter((id) => id !== null);
32
+ const results = await Promise.all(buildIds.map(async (buildId) => ({
33
+ files: await this.deleteBuildFiles(project.id, buildId),
34
+ removed: await this.removeBuild(buildId)
35
+ })));
36
+ const removedBuilds = results.filter((r) => r.removed).length;
37
+ const removedFiles = results.reduce((sum, r) => sum + r.files, 0);
38
+ this.logger?.info({
39
+ projectId: project.id,
40
+ removedBuilds,
41
+ removedFiles
42
+ }, "build retention purge complete");
43
+ return {
44
+ removedBuilds,
45
+ removedFiles
46
+ };
47
+ }
48
+ async removeBuild(buildId) {
49
+ await new BuildModel(this.db, this.tables).remove(buildId);
50
+ return true;
51
+ }
52
+ async latestPerBranch(projectId) {
53
+ const rows = await this.db.list(this.tables.builds, {
54
+ where: eq(getTableColumns(this.tables.builds)["projectId"], projectId),
55
+ orderBy: desc(getTableColumns(this.tables.builds)["updatedAt"])
56
+ });
57
+ const latest = /* @__PURE__ */ new Map();
58
+ for (const row of rows) {
59
+ const branch = row.gitBranch;
60
+ if (!latest.get(branch)) latest.set(branch, row.id);
61
+ }
62
+ return new Set(latest.values());
63
+ }
64
+ async deleteBuildFiles(projectId, buildId) {
65
+ const prefix = `${projectId}/builds/${buildId}/`;
66
+ const manifestPath = `${prefix}storybook/manifest.json`;
67
+ let manifestFiles = [];
68
+ try {
69
+ if (await this.storage.exists(manifestPath)) {
70
+ const raw = await this.storage.read(manifestPath);
71
+ const manifest = JSON.parse(raw.toString("utf8"));
72
+ manifestFiles = Object.values(manifest);
73
+ }
74
+ } catch {}
75
+ const files = await this.storage.list(prefix);
76
+ await Promise.all(files.map(async (file) => {
77
+ await this.storage.delete(file);
78
+ }));
79
+ for (const hash of manifestFiles) try {
80
+ const contentRefs = this.db.tables.contentRefs;
81
+ const existing = await this.db.get(contentRefs, hash);
82
+ if (existing) {
83
+ const refCount = existing.refCount;
84
+ if (refCount <= 1) await this.db.update(contentRefs, hash, {
85
+ refCount: 0,
86
+ lastSeenAt: (/* @__PURE__ */ new Date()).toISOString()
87
+ });
88
+ else await this.db.update(contentRefs, hash, {
89
+ refCount: refCount - 1,
90
+ lastSeenAt: (/* @__PURE__ */ new Date()).toISOString()
91
+ });
92
+ }
93
+ } catch {}
94
+ try {
95
+ const contentRefs = this.db.tables.contentRefs;
96
+ const cutoff = (/* @__PURE__ */ new Date(Date.now() - 6048e5)).toISOString();
97
+ const all = await this.db.list(contentRefs);
98
+ for (const row of all) if (row.refCount === 0 && row.lastSeenAt < cutoff) {
99
+ await this.storage.delete(`content/${row.hash}`).catch(() => {});
100
+ await this.db.remove(contentRefs, row.hash);
101
+ }
102
+ } catch {}
103
+ return files.length;
104
+ }
105
+ async purgeStaleBranches(project, ttlDays) {
106
+ if (ttlDays <= 0) return {
107
+ removedBranches: 0,
108
+ removedBaselines: 0
109
+ };
110
+ const cutoff = (/* @__PURE__ */ new Date(Date.now() - ttlDays * 864e5)).toISOString();
111
+ const stale = collectStaleBranches(await this.db.list(this.tables.builds, { where: eq(getTableColumns(this.tables.builds)["projectId"], project.id) }), project.gitDefaultBranch, cutoff);
112
+ if (stale.size === 0) return {
113
+ removedBranches: 0,
114
+ removedBaselines: 0
115
+ };
116
+ const removedBaselines = await new BaselineModel(this.db, this.tables, this.storage).removeStaleBranches(project.id, stale);
117
+ this.logger?.info({
118
+ projectId: project.id,
119
+ removedBranches: stale.size,
120
+ removedBaselines
121
+ }, "branch GC complete");
122
+ return {
123
+ removedBranches: stale.size,
124
+ removedBaselines
125
+ };
126
+ }
127
+ };
128
+ function collectStaleBranches(rows, defaultBranch, cutoff) {
129
+ const latestAt = branchLatestAt(rows);
130
+ const stale = /* @__PURE__ */ new Set();
131
+ for (const [branch, updatedAt] of latestAt) if (branch !== defaultBranch && updatedAt < cutoff) stale.add(branch);
132
+ return stale;
133
+ }
134
+ function branchLatestAt(rows) {
135
+ const latestAt = /* @__PURE__ */ new Map();
136
+ for (const row of rows) {
137
+ const current = latestAt.get(row.gitBranch);
138
+ if (!current || row.updatedAt > current) latestAt.set(row.gitBranch, row.updatedAt);
139
+ }
140
+ return latestAt;
141
+ }
142
+ //#endregion
143
+ export { Retention };
144
+
145
+ //# sourceMappingURL=retention.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention.mjs","names":[],"sources":["../src/retention/purge.ts"],"sourcesContent":["import { and, desc, eq, getTableColumns, inArray, lt } from \"drizzle-orm\";\nimport type { SQLWrapper, Table } from \"drizzle-orm\";\nimport type { Logger } from \"pino\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { StorageAdapter } from \"../adapters/storage.ts\";\nimport { BaselineModel } from \"../models/baseline.ts\";\nimport { BuildModel, type BuildTables } from \"../models/build.ts\";\nimport { LabelModel, type LabelTables } from \"../models/label.ts\";\nimport type { Project } from \"../schema/project.ts\";\nimport { TERMINAL_BUILD_STATUSES } from \"../types.ts\";\n\n/** Table handles for retention queries. */\nexport interface RetentionTables {\n builds: Table;\n buildLabels: Table;\n baselines: Table;\n}\n\n/** Removes expired transient builds while keeping baselines and persistent builds. */\nexport class Retention {\n constructor(\n private readonly db: DatabaseAdapter,\n private readonly storage: StorageAdapter,\n private readonly tables: RetentionTables,\n private readonly logger?: Logger,\n ) {}\n\n async purge(project: Project, options: PurgeOptions): Promise<PurgeResult> {\n const cutoff = new Date(Date.now() - options.ttlDays * 86_400_000).toISOString();\n const candidates = await this.db.list(this.tables.builds, {\n where: and(\n eq(getTableColumns(this.tables.builds)[\"projectId\"] as unknown as SQLWrapper, project.id),\n inArray(getTableColumns(this.tables.builds)[\"status\"] as unknown as SQLWrapper, [\n ...TERMINAL_BUILD_STATUSES,\n ]),\n lt(getTableColumns(this.tables.builds)[\"updatedAt\"] as unknown as SQLWrapper, cutoff),\n ),\n });\n\n const keep = options.keepLatestPerBranch\n ? await this.latestPerBranch(project.id)\n : new Set<string>();\n const labelModel = new LabelModel(this.db, this.tables as unknown as LabelTables);\n const target = await Promise.all(\n candidates.map(async (build) => {\n const buildId = (build as { id: string }).id;\n if (keep.has(buildId) || (await labelModel.hasPersistent(project.id, buildId))) {\n return null;\n }\n return buildId;\n }),\n );\n\n const buildIds = target.filter((id): id is string => id !== null);\n const results = await Promise.all(\n buildIds.map(async (buildId) => ({\n files: await this.deleteBuildFiles(project.id, buildId),\n removed: await this.removeBuild(buildId),\n })),\n );\n const removedBuilds = results.filter((r: { removed: boolean }) => r.removed).length;\n const removedFiles = results.reduce((sum: number, r: { files: number }) => sum + r.files, 0);\n this.logger?.info(\n { projectId: project.id, removedBuilds, removedFiles },\n \"build retention purge complete\",\n );\n return { removedBuilds, removedFiles };\n }\n\n private async removeBuild(buildId: string): Promise<boolean> {\n await new BuildModel(this.db, this.tables as unknown as BuildTables).remove(buildId);\n return true;\n }\n\n private async latestPerBranch(projectId: string): Promise<Set<string>> {\n const rows = (await this.db.list(this.tables.builds, {\n where: eq(\n getTableColumns(this.tables.builds)[\"projectId\"] as unknown as SQLWrapper,\n projectId,\n ),\n orderBy: desc(getTableColumns(this.tables.builds)[\"updatedAt\"] as unknown as SQLWrapper),\n })) as unknown as { gitBranch: string; id: string }[];\n const latest = new Map<string, string>();\n for (const row of rows) {\n const branch = row.gitBranch;\n const current = latest.get(branch);\n if (!current) {\n latest.set(branch, row.id);\n }\n }\n return new Set(latest.values());\n }\n\n private async deleteBuildFiles(projectId: string, buildId: string): Promise<number> {\n const prefix = `${projectId}/builds/${buildId}/`;\n const manifestPath = `${prefix}storybook/manifest.json`;\n let manifestFiles: string[] = [];\n try {\n if (await this.storage.exists(manifestPath)) {\n const raw = await this.storage.read(manifestPath);\n const manifest = JSON.parse(raw.toString(\"utf8\")) as Record<string, string>;\n manifestFiles = Object.values(manifest);\n }\n } catch {\n // ignore — manifest may not exist for old builds\n }\n const files = await this.storage.list(prefix);\n await Promise.all(\n files.map(async (file) => {\n await this.storage.delete(file);\n }),\n );\n // Decrement content refs for this build's manifest\n for (const hash of manifestFiles) {\n try {\n const contentRefs = this.db.tables.contentRefs;\n const existing = (await this.db.get(contentRefs, hash)) as unknown as {\n refCount: number;\n } | null;\n // oxlint-disable-next-line unicorn/prefer-ternary -- refCount check is clearer as if/else\n if (existing) {\n const refCount = existing.refCount;\n // oxlint-disable-next-line unicorn/prefer-ternary -- grace logic is clearer as if/else\n if (refCount <= 1) {\n // Keep for 7-day grace: set refCount 0 and update lastSeenAt, don't delete yet\n await this.db.update(contentRefs, hash, {\n refCount: 0,\n lastSeenAt: new Date().toISOString(),\n });\n } else {\n await this.db.update(contentRefs, hash, {\n refCount: refCount - 1,\n lastSeenAt: new Date().toISOString(),\n });\n }\n }\n } catch {\n // ignore — content_refs may not exist\n }\n }\n // GC stale content_refs with 7-day grace\n try {\n const contentRefs = this.db.tables.contentRefs;\n const cutoff = new Date(Date.now() - 7 * 86_400_000).toISOString();\n const all = (await this.db.list(contentRefs)) as unknown as {\n hash: string;\n refCount: number;\n lastSeenAt: string;\n }[];\n for (const row of all) {\n if (row.refCount === 0 && row.lastSeenAt < cutoff) {\n await this.storage.delete(`content/${row.hash}`).catch(() => {\n // ignore storage delete failure\n });\n await this.db.remove(contentRefs, row.hash);\n }\n }\n } catch {\n // ignore — content_refs may not exist\n }\n return files.length;\n }\n\n async purgeStaleBranches(project: Project, ttlDays: number): Promise<BranchGcResult> {\n if (ttlDays <= 0) {\n return { removedBranches: 0, removedBaselines: 0 };\n }\n const cutoff = new Date(Date.now() - ttlDays * 86_400_000).toISOString();\n const rows = (await this.db.list(this.tables.builds, {\n where: eq(\n getTableColumns(this.tables.builds)[\"projectId\"] as unknown as SQLWrapper,\n project.id,\n ),\n })) as unknown as { gitBranch: string; updatedAt: string }[];\n const stale = collectStaleBranches(rows, project.gitDefaultBranch, cutoff);\n if (stale.size === 0) {\n return { removedBranches: 0, removedBaselines: 0 };\n }\n const baselines = new BaselineModel(this.db, this.tables, this.storage);\n const removedBaselines = await baselines.removeStaleBranches(project.id, stale);\n this.logger?.info(\n { projectId: project.id, removedBranches: stale.size, removedBaselines },\n \"branch GC complete\",\n );\n return { removedBranches: stale.size, removedBaselines };\n }\n}\n\n/** Options controlling which builds a retention purge removes. */\nexport interface PurgeOptions {\n ttlDays: number;\n keepLatestPerBranch: boolean;\n}\n\n/** Counts of builds and files removed by a retention purge. */\nexport interface PurgeResult {\n removedBuilds: number;\n removedFiles: number;\n}\n\n/** Counts of stale branches and baselines removed. */\nexport interface BranchGcResult {\n removedBranches: number;\n removedBaselines: number;\n}\n\nfunction collectStaleBranches(\n rows: { gitBranch: string; updatedAt: string }[],\n defaultBranch: string,\n cutoff: string,\n): Set<string> {\n const latestAt = branchLatestAt(rows);\n const stale = new Set<string>();\n for (const [branch, updatedAt] of latestAt) {\n if (branch !== defaultBranch && updatedAt < cutoff) {\n stale.add(branch);\n }\n }\n return stale;\n}\n\nfunction branchLatestAt(rows: { gitBranch: string; updatedAt: string }[]): Map<string, string> {\n const latestAt = new Map<string, string>();\n for (const row of rows) {\n const current = latestAt.get(row.gitBranch);\n if (!current || row.updatedAt > current) {\n latestAt.set(row.gitBranch, row.updatedAt);\n }\n }\n return latestAt;\n}\n"],"mappings":";;;;;;;;;;AAmBA,IAAa,YAAb,MAAuB;CAEF;CACA;CACA;CACA;CAJnB,YACE,IACA,SACA,QACA,QACA;EAJiB,KAAA,KAAA;EACA,KAAA,UAAA;EACA,KAAA,SAAA;EACA,KAAA,SAAA;CAChB;CAEH,MAAM,MAAM,SAAkB,SAA6C;EACzE,MAAM,0BAAS,IAAI,KAAK,KAAK,IAAI,IAAI,QAAQ,UAAU,KAAU,EAAA,CAAE,YAAY;EAC/E,MAAM,aAAa,MAAM,KAAK,GAAG,KAAK,KAAK,OAAO,QAAQ,EACxD,OAAO,IACL,GAAG,gBAAgB,KAAK,OAAO,MAAM,CAAC,CAAC,cAAuC,QAAQ,EAAE,GACxF,QAAQ,gBAAgB,KAAK,OAAO,MAAM,CAAC,CAAC,WAAoC,CAC9E,GAAG,uBACL,CAAC,GACD,GAAG,gBAAgB,KAAK,OAAO,MAAM,CAAC,CAAC,cAAuC,MAAM,CACtF,EACF,CAAC;EAED,MAAM,OAAO,QAAQ,sBACjB,MAAM,KAAK,gBAAgB,QAAQ,EAAE,oBACrC,IAAI,IAAY;EACpB,MAAM,aAAa,IAAI,WAAW,KAAK,IAAI,KAAK,MAAgC;EAWhF,MAAM,YAAW,MAVI,QAAQ,IAC3B,WAAW,IAAI,OAAO,UAAU;GAC9B,MAAM,UAAW,MAAyB;GAC1C,IAAI,KAAK,IAAI,OAAO,KAAM,MAAM,WAAW,cAAc,QAAQ,IAAI,OAAO,GAC1E,OAAO;GAET,OAAO;EACT,CAAC,CACH,EAAA,CAEwB,QAAQ,OAAqB,OAAO,IAAI;EAChE,MAAM,UAAU,MAAM,QAAQ,IAC5B,SAAS,IAAI,OAAO,aAAa;GAC/B,OAAO,MAAM,KAAK,iBAAiB,QAAQ,IAAI,OAAO;GACtD,SAAS,MAAM,KAAK,YAAY,OAAO;EACzC,EAAE,CACJ;EACA,MAAM,gBAAgB,QAAQ,QAAQ,MAA4B,EAAE,OAAO,CAAC,CAAC;EAC7E,MAAM,eAAe,QAAQ,QAAQ,KAAa,MAAyB,MAAM,EAAE,OAAO,CAAC;EAC3F,KAAK,QAAQ,KACX;GAAE,WAAW,QAAQ;GAAI;GAAe;EAAa,GACrD,gCACF;EACA,OAAO;GAAE;GAAe;EAAa;CACvC;CAEA,MAAc,YAAY,SAAmC;EAC3D,MAAM,IAAI,WAAW,KAAK,IAAI,KAAK,MAAgC,CAAC,CAAC,OAAO,OAAO;EACnF,OAAO;CACT;CAEA,MAAc,gBAAgB,WAAyC;EACrE,MAAM,OAAQ,MAAM,KAAK,GAAG,KAAK,KAAK,OAAO,QAAQ;GACnD,OAAO,GACL,gBAAgB,KAAK,OAAO,MAAM,CAAC,CAAC,cACpC,SACF;GACA,SAAS,KAAK,gBAAgB,KAAK,OAAO,MAAM,CAAC,CAAC,YAAqC;EACzF,CAAC;EACD,MAAM,yBAAS,IAAI,IAAoB;EACvC,KAAK,MAAM,OAAO,MAAM;GACtB,MAAM,SAAS,IAAI;GAEnB,IAAI,CADY,OAAO,IAAI,MAChB,GACT,OAAO,IAAI,QAAQ,IAAI,EAAE;EAE7B;EACA,OAAO,IAAI,IAAI,OAAO,OAAO,CAAC;CAChC;CAEA,MAAc,iBAAiB,WAAmB,SAAkC;EAClF,MAAM,SAAS,GAAG,UAAU,UAAU,QAAQ;EAC9C,MAAM,eAAe,GAAG,OAAO;EAC/B,IAAI,gBAA0B,CAAC;EAC/B,IAAI;GACF,IAAI,MAAM,KAAK,QAAQ,OAAO,YAAY,GAAG;IAC3C,MAAM,MAAM,MAAM,KAAK,QAAQ,KAAK,YAAY;IAChD,MAAM,WAAW,KAAK,MAAM,IAAI,SAAS,MAAM,CAAC;IAChD,gBAAgB,OAAO,OAAO,QAAQ;GACxC;EACF,QAAQ,CAER;EACA,MAAM,QAAQ,MAAM,KAAK,QAAQ,KAAK,MAAM;EAC5C,MAAM,QAAQ,IACZ,MAAM,IAAI,OAAO,SAAS;GACxB,MAAM,KAAK,QAAQ,OAAO,IAAI;EAChC,CAAC,CACH;EAEA,KAAK,MAAM,QAAQ,eACjB,IAAI;GACF,MAAM,cAAc,KAAK,GAAG,OAAO;GACnC,MAAM,WAAY,MAAM,KAAK,GAAG,IAAI,aAAa,IAAI;GAIrD,IAAI,UAAU;IACZ,MAAM,WAAW,SAAS;IAE1B,IAAI,YAAY,GAEd,MAAM,KAAK,GAAG,OAAO,aAAa,MAAM;KACtC,UAAU;KACV,6BAAY,IAAI,KAAK,EAAA,CAAE,YAAY;IACrC,CAAC;SAED,MAAM,KAAK,GAAG,OAAO,aAAa,MAAM;KACtC,UAAU,WAAW;KACrB,6BAAY,IAAI,KAAK,EAAA,CAAE,YAAY;IACrC,CAAC;GAEL;EACF,QAAQ,CAER;EAGF,IAAI;GACF,MAAM,cAAc,KAAK,GAAG,OAAO;GACnC,MAAM,0BAAS,IAAI,KAAK,KAAK,IAAI,IAAI,MAAc,EAAA,CAAE,YAAY;GACjE,MAAM,MAAO,MAAM,KAAK,GAAG,KAAK,WAAW;GAK3C,KAAK,MAAM,OAAO,KAChB,IAAI,IAAI,aAAa,KAAK,IAAI,aAAa,QAAQ;IACjD,MAAM,KAAK,QAAQ,OAAO,WAAW,IAAI,MAAM,CAAC,CAAC,YAAY,CAE7D,CAAC;IACD,MAAM,KAAK,GAAG,OAAO,aAAa,IAAI,IAAI;GAC5C;EAEJ,QAAQ,CAER;EACA,OAAO,MAAM;CACf;CAEA,MAAM,mBAAmB,SAAkB,SAA0C;EACnF,IAAI,WAAW,GACb,OAAO;GAAE,iBAAiB;GAAG,kBAAkB;EAAE;EAEnD,MAAM,0BAAS,IAAI,KAAK,KAAK,IAAI,IAAI,UAAU,KAAU,EAAA,CAAE,YAAY;EAOvE,MAAM,QAAQ,qBAAqB,MANf,KAAK,GAAG,KAAK,KAAK,OAAO,QAAQ,EACnD,OAAO,GACL,gBAAgB,KAAK,OAAO,MAAM,CAAC,CAAC,cACpC,QAAQ,EACV,EACF,CAAC,GACwC,QAAQ,kBAAkB,MAAM;EACzE,IAAI,MAAM,SAAS,GACjB,OAAO;GAAE,iBAAiB;GAAG,kBAAkB;EAAE;EAGnD,MAAM,mBAAmB,MAAM,IADT,cAAc,KAAK,IAAI,KAAK,QAAQ,KAAK,OACxB,CAAC,CAAC,oBAAoB,QAAQ,IAAI,KAAK;EAC9E,KAAK,QAAQ,KACX;GAAE,WAAW,QAAQ;GAAI,iBAAiB,MAAM;GAAM;EAAiB,GACvE,oBACF;EACA,OAAO;GAAE,iBAAiB,MAAM;GAAM;EAAiB;CACzD;AACF;AAoBA,SAAS,qBACP,MACA,eACA,QACa;CACb,MAAM,WAAW,eAAe,IAAI;CACpC,MAAM,wBAAQ,IAAI,IAAY;CAC9B,KAAK,MAAM,CAAC,QAAQ,cAAc,UAChC,IAAI,WAAW,iBAAiB,YAAY,QAC1C,MAAM,IAAI,MAAM;CAGpB,OAAO;AACT;AAEA,SAAS,eAAe,MAAuE;CAC7F,MAAM,2BAAW,IAAI,IAAoB;CACzC,KAAK,MAAM,OAAO,MAAM;EACtB,MAAM,UAAU,SAAS,IAAI,IAAI,SAAS;EAC1C,IAAI,CAAC,WAAW,IAAI,YAAY,SAC9B,SAAS,IAAI,IAAI,WAAW,IAAI,SAAS;CAE7C;CACA,OAAO;AACT"}
@@ -10,6 +10,7 @@ interface Baseline {
10
10
  branch: string;
11
11
  snapshotId: string | null;
12
12
  screenshotPath: string;
13
+ infraHash: string | null;
13
14
  createdAt: string;
14
15
  updatedAt: string;
15
16
  }
@@ -18,6 +18,14 @@ interface Build {
18
18
  changedCount: number;
19
19
  approvedCount: number;
20
20
  rejectedCount: number;
21
+ /** Affected capture enabled for this build (default true). */
22
+ affectedOnly: boolean;
23
+ /** Ancestor commit the affected set was computed against, if any. */
24
+ baselineSha: string | null;
25
+ /** JSON array of repo-relative changed files, if computed. */
26
+ changedFiles: string | null;
27
+ /** JSON array of affected story import paths; null means full capture. */
28
+ affectedImportPaths: string | null;
21
29
  createdAt: string;
22
30
  updatedAt: string;
23
31
  }
@@ -0,0 +1,38 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { JobStatus } from "../adapter/capture-queue.mjs";
4
+ //#region src/schema/capture-attempt.d.ts
5
+ /** A capture attempt row: one queued run of a build's capture pipeline. */
6
+ interface CaptureAttempt {
7
+ id: string;
8
+ projectId: string;
9
+ buildId: string;
10
+ attemptNo: number;
11
+ status: JobStatus;
12
+ error: string | null;
13
+ reqId: string | null;
14
+ storyCount: number;
15
+ failedCount: number;
16
+ queuedAt: string;
17
+ startedAt: string | null;
18
+ finishedAt: string | null;
19
+ createdAt: string;
20
+ updatedAt: string;
21
+ }
22
+ /** Log levels stored per capture attempt. */
23
+ type CaptureLogLevel = "debug" | "info" | "warn" | "error";
24
+ /** A single log line captured for a build attempt. */
25
+ interface CaptureLog {
26
+ id: string;
27
+ projectId: string;
28
+ buildId: string;
29
+ attemptId: string;
30
+ seq: number;
31
+ level: CaptureLogLevel;
32
+ message: string;
33
+ fields: string | null;
34
+ createdAt: string;
35
+ }
36
+ //#endregion
37
+ export { CaptureAttempt, CaptureLog, CaptureLogLevel };
38
+ //# sourceMappingURL=capture-attempt.d.mts.map
@@ -7,7 +7,7 @@ interface Comment {
7
7
  projectId: string;
8
8
  buildId: string;
9
9
  snapshotId: string | null;
10
- userId: string;
10
+ userId: string | null;
11
11
  body: string;
12
12
  parentId: string | null;
13
13
  resolved: boolean;
@@ -0,0 +1,13 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ //#region src/schema/content-ref.d.ts
4
+ /** Content ref row for deduplicated assets. */
5
+ interface ContentRef {
6
+ hash: string;
7
+ refCount: number;
8
+ lastSeenAt: string;
9
+ createdAt: string;
10
+ }
11
+ //#endregion
12
+ export { ContentRef };
13
+ //# sourceMappingURL=content-ref.d.mts.map
@@ -8,6 +8,8 @@ interface ProjectMember {
8
8
  projectId: string;
9
9
  userId: string;
10
10
  role: ProjectRole;
11
+ /** Provenance: `manual` grants vs `oidc:<group>` synced grants. */
12
+ source: string;
11
13
  createdAt: string;
12
14
  }
13
15
  //#endregion
@@ -0,0 +1,35 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ //#region src/schema/notification.d.ts
4
+ /** A project (or site-wide) notification channel row. */
5
+ interface NotificationChannelRow {
6
+ id: string;
7
+ /** Owning project, or null for site-wide admin (`sys:*`) channels. */
8
+ projectId: string | null;
9
+ /** Provider kind (e.g. "slack-webhook", "teams-workflow", "email"). */
10
+ provider: string;
11
+ /** JSON-serialized provider config (toggles, addresses; never secrets). */
12
+ config: string;
13
+ /** AES-256-GCM secret (webhook URL / SMTP pass), null when none. */
14
+ secretEncrypted: string | null;
15
+ /** JSON array of subscribed events, or null for all. */
16
+ events: string | null;
17
+ enabled: boolean;
18
+ createdAt: string;
19
+ updatedAt: string;
20
+ }
21
+ /** A per-user opt-in row for project notifications. */
22
+ interface NotificationSubscriptionRow {
23
+ id: string;
24
+ projectId: string;
25
+ userId: string;
26
+ /** JSON array of subscribed events, or null for all. */
27
+ events: string | null;
28
+ /** JSON array of channels (e.g. ["email"]); v1 supports email only. */
29
+ via: string | null;
30
+ enabled: boolean;
31
+ createdAt: string;
32
+ }
33
+ //#endregion
34
+ export { NotificationChannelRow, NotificationSubscriptionRow };
35
+ //# sourceMappingURL=notification.d.mts.map
@@ -0,0 +1,15 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { ProjectRole } from "../types.mjs";
4
+ //#region src/schema/project-group-mapping.d.ts
5
+ /** An identity-provider group to project-role mapping row. */
6
+ interface ProjectGroupMapping {
7
+ id: string;
8
+ projectId: string;
9
+ groupName: string;
10
+ role: ProjectRole;
11
+ createdAt: string;
12
+ }
13
+ //#endregion
14
+ export { ProjectGroupMapping };
15
+ //# sourceMappingURL=project-group-mapping.d.mts.map
@@ -14,6 +14,10 @@ interface Project {
14
14
  storybookMeta: string | null | undefined;
15
15
  executePlay: boolean;
16
16
  playTimeoutMs: number;
17
+ runA11y?: boolean;
18
+ browser?: string;
19
+ viewports?: string | null;
20
+ automigrate?: boolean;
17
21
  createdAt: string;
18
22
  updatedAt: string;
19
23
  }
@@ -22,6 +22,13 @@ interface Snapshot {
22
22
  status: SnapshotStatus;
23
23
  reviewedBy: string | null;
24
24
  reviewedAt: string | null;
25
+ infraHash: string | null;
26
+ /** True when inherited unchanged from the baseline without rendering. */
27
+ inherited: boolean;
28
+ /** Baseline row the diff was computed against (null: no baseline, or legacy row). */
29
+ baselineId: string | null;
30
+ /** That baseline's `updatedAt` at diff time; compared on approval to detect drift. */
31
+ baselineVersion: string | null;
25
32
  createdAt: string;
26
33
  updatedAt: string;
27
34
  }
@@ -7,6 +7,8 @@ interface Token {
7
7
  projectId: string;
8
8
  name: string;
9
9
  hash: string;
10
+ /** Owning user, or null for legacy pre-binding tokens (resolved as viewer). */
11
+ userId: string | null;
10
12
  lastUsedAt: string | null;
11
13
  createdAt: string;
12
14
  }
@@ -2,6 +2,8 @@ import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { SiteRole } from "../types.mjs";
4
4
  //#region src/schema/user.d.ts
5
+ /** How a user authenticates. */
6
+ type AuthProvider = "local" | "oidc" | "shared";
5
7
  /** A user row. */
6
8
  interface User {
7
9
  id: string;
@@ -11,7 +13,15 @@ interface User {
11
13
  role: SiteRole;
12
14
  lastLoginAt: string | null;
13
15
  createdAt: string;
16
+ /** Scrypt password hash for local accounts, null for SSO/shared users. */
17
+ passwordHash?: string | null;
18
+ /** User-edited display name that survives OIDC refresh, null if never edited. */
19
+ displayNameOverride?: string | null;
20
+ /** Which provider minted this identity. */
21
+ authProvider?: AuthProvider;
22
+ /** Whether the account is disabled (login rejected). */
23
+ disabled?: boolean;
14
24
  }
15
25
  //#endregion
16
- export { User };
26
+ export { AuthProvider, User };
17
27
  //# sourceMappingURL=user.d.mts.map
package/dist/schema.d.mts CHANGED
@@ -1,59 +1,22 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
+ import { Webhook } from "./schema/webhook.mjs";
3
4
  import { Build } from "./schema/build.mjs";
5
+ import { CaptureAttempt, CaptureLog, CaptureLogLevel } from "./schema/capture-attempt.mjs";
4
6
  import { Project, StorybookMeta } from "./schema/project.mjs";
5
7
  import { Baseline } from "./schema/baseline.mjs";
8
+ import { Snapshot } from "./schema/snapshot.mjs";
9
+ import { ProjectStatusConfig } from "./schema/status-config.mjs";
6
10
  import { Comment } from "./schema/comment.mjs";
7
11
  import { BuildLabel, LabelType } from "./schema/label.mjs";
8
12
  import { ProjectMember } from "./schema/member.mjs";
9
- import { ProjectStatusConfig } from "./schema/status-config.mjs";
10
- import { Snapshot } from "./schema/snapshot.mjs";
13
+ import { NotificationChannelRow, NotificationSubscriptionRow } from "./schema/notification.mjs";
14
+ import { ProjectGroupMapping } from "./schema/project-group-mapping.mjs";
11
15
  import { Token } from "./schema/token.mjs";
12
16
  import { User } from "./schema/user.mjs";
13
- import { Webhook } from "./schema/webhook.mjs";
14
- import { AnySQLiteTable } from "drizzle-orm/sqlite-core";
15
17
  //#region src/schema/index.d.ts
16
- /** Projects table (one row per Storybook project). */
17
- declare const projects: AnySQLiteTable;
18
- /** Project status-configs table (merge-gate configurations). */
19
- declare const projectStatusConfigs: AnySQLiteTable;
20
- /** Builds table (one row per uploaded Storybook build). */
21
- declare const builds: AnySQLiteTable;
22
- /** Snapshots table (one row per captured story screenshot). */
23
- declare const snapshots: AnySQLiteTable;
24
- /** Baselines table (accepted reference snapshots per branch). */
25
- declare const baselines: AnySQLiteTable;
26
- /** Comments table (review comments on snapshots). */
27
- declare const comments: AnySQLiteTable;
28
- /** Label-types table (project-defined build label vocabularies). */
29
- declare const labelTypes: AnySQLiteTable;
30
- /** Build-labels table (labels attached to builds). */
31
- declare const buildLabels: AnySQLiteTable;
32
- /** Tokens table (CI token hashes per project). */
33
- declare const tokens: AnySQLiteTable;
34
- /** Webhooks table (outgoing build-event subscriptions). */
35
- declare const webhooks: AnySQLiteTable;
36
- /** Users table (dashboard members). */
37
- declare const users: AnySQLiteTable;
38
- /** Project-members table (per-project memberships and roles). */
39
- declare const projectMembers: AnySQLiteTable;
40
- /** Full Drizzle schema object passed to the database client. */
41
- declare const schema: {
42
- projects: AnySQLiteTable;
43
- projectStatusConfigs: AnySQLiteTable;
44
- builds: AnySQLiteTable;
45
- snapshots: AnySQLiteTable;
46
- baselines: AnySQLiteTable;
47
- comments: AnySQLiteTable;
48
- labelTypes: AnySQLiteTable;
49
- buildLabels: AnySQLiteTable;
50
- tokens: AnySQLiteTable;
51
- webhooks: AnySQLiteTable;
52
- users: AnySQLiteTable;
53
- projectMembers: AnySQLiteTable;
54
- };
55
- /** The full database schema type. */
56
- type Schema = typeof schema;
18
+ /** Placeholder schema type — Drizzle schema lives in the db adapters. */
19
+ type Schema = Record<string, unknown>;
57
20
  //#endregion
58
- export { type Baseline, type Build, type BuildLabel, type Comment, type LabelType, type Project, type ProjectMember, type ProjectStatusConfig, Schema, type Snapshot, type StorybookMeta, type Token, type User, type Webhook, baselines, buildLabels, builds, comments, labelTypes, projectMembers, projectStatusConfigs, projects, schema, snapshots, tokens, users, webhooks };
21
+ export { type Baseline, type Build, type BuildLabel, type CaptureAttempt, type CaptureLog, type CaptureLogLevel, type Comment, type LabelType, type NotificationChannelRow, type NotificationSubscriptionRow, type Project, type ProjectGroupMapping, type ProjectMember, type ProjectStatusConfig, Schema, type Snapshot, type StorybookMeta, type Token, type User, type Webhook };
59
22
  //# sourceMappingURL=schema.d.mts.map
package/dist/schema.mjs CHANGED
@@ -1,58 +1,4 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
- import { projects as projects$1 } from "./schema/project.mjs";
5
- import { builds as builds$1 } from "./schema/build.mjs";
6
- import { buildLabels as buildLabels$1, labelTypes as labelTypes$1 } from "./schema/label.mjs";
7
- import { snapshots as snapshots$1 } from "./schema/snapshot.mjs";
8
- import { webhooks as webhooks$1 } from "./schema/webhook.mjs";
9
- import { baselines as baselines$1 } from "./schema/baseline.mjs";
10
- import { projectStatusConfigs as projectStatusConfigs$1 } from "./schema/status-config.mjs";
11
- import { users as users$1 } from "./schema/user.mjs";
12
- import { projectMembers as projectMembers$1 } from "./schema/member.mjs";
13
- import { tokens as tokens$1 } from "./schema/token.mjs";
14
- import { comments as comments$1 } from "./schema/comment.mjs";
15
- //#region src/schema/index.ts
16
- /** Projects table (one row per Storybook project). */
17
- const projects = projects$1;
18
- /** Project status-configs table (merge-gate configurations). */
19
- const projectStatusConfigs = projectStatusConfigs$1;
20
- /** Builds table (one row per uploaded Storybook build). */
21
- const builds = builds$1;
22
- /** Snapshots table (one row per captured story screenshot). */
23
- const snapshots = snapshots$1;
24
- /** Baselines table (accepted reference snapshots per branch). */
25
- const baselines = baselines$1;
26
- /** Comments table (review comments on snapshots). */
27
- const comments = comments$1;
28
- /** Label-types table (project-defined build label vocabularies). */
29
- const labelTypes = labelTypes$1;
30
- /** Build-labels table (labels attached to builds). */
31
- const buildLabels = buildLabels$1;
32
- /** Tokens table (CI token hashes per project). */
33
- const tokens = tokens$1;
34
- /** Webhooks table (outgoing build-event subscriptions). */
35
- const webhooks = webhooks$1;
36
- /** Users table (dashboard members). */
37
- const users = users$1;
38
- /** Project-members table (per-project memberships and roles). */
39
- const projectMembers = projectMembers$1;
40
- /** Full Drizzle schema object passed to the database client. */
41
- const schema = {
42
- projects,
43
- projectStatusConfigs,
44
- builds,
45
- snapshots,
46
- baselines,
47
- comments,
48
- labelTypes,
49
- buildLabels,
50
- tokens,
51
- webhooks,
52
- users,
53
- projectMembers
54
- };
55
- //#endregion
56
- export { baselines, buildLabels, builds, comments, labelTypes, projectMembers, projectStatusConfigs, projects, schema, snapshots, tokens, users, webhooks };
57
-
58
- //# sourceMappingURL=schema.mjs.map
4
+ export {};
@@ -0,0 +1,15 @@
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 { Build } from "../schema/build.mjs";
5
+ import { BuildTables } from "../models/build.mjs";
6
+ import { Project } from "../schema/project.mjs";
7
+ import { ProjectTables } from "../models/project.mjs";
8
+ //#region src/test-helpers/create-project.d.ts
9
+ /** Create a test project with owner/repo defaults. */
10
+ declare function createTestProject(db: DatabaseAdapter, tables: ProjectTables, overrides?: Partial<Project>): Promise<Project>;
11
+ /** Create a test build on main with sha-1 defaults. */
12
+ declare function createTestBuild(db: DatabaseAdapter, tables: BuildTables, projectId: string, overrides?: Partial<Build>): Promise<Build>;
13
+ //#endregion
14
+ export { createTestBuild, createTestProject };
15
+ //# sourceMappingURL=create-project.d.mts.map