@storyshelf/core 0.6.1 → 0.7.0

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 (631) hide show
  1. package/README.md +49 -32
  2. package/dist/adapter/capture-queue.d.mts +44 -2
  3. package/dist/adapter/capture-runner.d.mts +127 -10
  4. package/dist/adapter/capture-runner.mjs +5 -12
  5. package/dist/adapter/database.d.mts +27 -8
  6. package/dist/adapter/database.mjs.map +1 -1
  7. package/dist/adapter/design.d.mts +85 -0
  8. package/dist/adapter/design.mjs +28 -0
  9. package/dist/adapter/design.mjs.map +1 -0
  10. package/dist/adapter/metadata.d.mts +3 -10
  11. package/dist/adapter/notifier.d.mts +6 -3
  12. package/dist/adapter/notifier.mjs +5 -1
  13. package/dist/adapter/oauth.d.mts +52 -0
  14. package/dist/adapter/setup.d.mts +4 -5
  15. package/dist/adapter/setup.mjs +2 -1
  16. package/dist/adapter/setup.mjs.map +1 -1
  17. package/dist/adapter/webhook-events.mjs +1 -1
  18. package/dist/adapters/capture-target-offers.d.mts +28 -0
  19. package/dist/adapters/capture-target-offers.mjs +58 -0
  20. package/dist/adapters/capture-target-offers.mjs.map +1 -0
  21. package/dist/adapters/capture-target.d.mts +71 -0
  22. package/dist/adapters/capture-target.mjs +76 -0
  23. package/dist/adapters/capture-target.mjs.map +1 -0
  24. package/dist/adapters/notifier/build-comment.d.mts +24 -0
  25. package/dist/adapters/notifier/build-comment.mjs +24 -0
  26. package/dist/adapters/notifier/build-comment.mjs.map +1 -0
  27. package/dist/adapters/notifier/build-state.d.mts +99 -0
  28. package/dist/adapters/notifier/build-state.mjs +218 -0
  29. package/dist/adapters/notifier/build-state.mjs.map +1 -0
  30. package/dist/adapters/notifier/events.d.mts +4 -2
  31. package/dist/adapters/notifier/events.mjs +31 -18
  32. package/dist/adapters/notifier/events.mjs.map +1 -1
  33. package/dist/adapters/notifier/provider.d.mts +53 -7
  34. package/dist/adapters/notifier/provider.mjs +12 -0
  35. package/dist/adapters/notifier/provider.mjs.map +1 -0
  36. package/dist/adapters/notifier/state.d.mts +24 -0
  37. package/dist/adapters/notifier/state.mjs +66 -0
  38. package/dist/adapters/notifier/state.mjs.map +1 -0
  39. package/dist/adapters/notifier/types.d.mts +51 -2
  40. package/dist/adapters/remote-browser.d.mts +87 -0
  41. package/dist/adapters/remote-browser.mjs +103 -0
  42. package/dist/adapters/remote-browser.mjs.map +1 -0
  43. package/dist/adapters/render-runs.d.mts +25 -0
  44. package/dist/adapters/render-runs.mjs +41 -0
  45. package/dist/adapters/render-runs.mjs.map +1 -0
  46. package/dist/adapters/setup-runner.mjs +2 -4
  47. package/dist/adapters/setup-runner.mjs.map +1 -1
  48. package/dist/adapters/sources.d.mts +12 -0
  49. package/dist/adapters/sources.mjs +20 -0
  50. package/dist/adapters/sources.mjs.map +1 -0
  51. package/dist/adapters/validate.mjs +25 -18
  52. package/dist/adapters/validate.mjs.map +1 -1
  53. package/dist/ai.d.mts +105 -0
  54. package/dist/ai.mjs +19 -0
  55. package/dist/ai.mjs.map +1 -0
  56. package/dist/auth.d.mts +8 -1
  57. package/dist/auth.mjs +14 -1
  58. package/dist/auth.mjs.map +1 -0
  59. package/dist/capture/a11y/axe-source.d.mts +18 -0
  60. package/dist/capture/a11y/axe-source.mjs +37 -0
  61. package/dist/capture/a11y/axe-source.mjs.map +1 -0
  62. package/dist/capture/a11y/in-page.d.mts +27 -0
  63. package/dist/capture/a11y/in-page.mjs +60 -0
  64. package/dist/capture/a11y/in-page.mjs.map +1 -0
  65. package/dist/capture/a11y/map.d.mts +24 -0
  66. package/dist/capture/a11y/map.mjs +48 -0
  67. package/dist/capture/a11y/map.mjs.map +1 -0
  68. package/dist/capture/a11y/options.d.mts +23 -0
  69. package/dist/capture/a11y/options.mjs +75 -0
  70. package/dist/capture/a11y/options.mjs.map +1 -0
  71. package/dist/capture/adapter.d.mts +41 -3
  72. package/dist/capture/adapter.mjs.map +1 -1
  73. package/dist/capture/affected.d.mts +2 -0
  74. package/dist/capture/affected.mjs +13 -4
  75. package/dist/capture/affected.mjs.map +1 -1
  76. package/dist/capture/attempt-logger.mjs +84 -0
  77. package/dist/capture/attempt-logger.mjs.map +1 -0
  78. package/dist/capture/browsers.d.mts +35 -0
  79. package/dist/capture/browsers.mjs +67 -0
  80. package/dist/capture/browsers.mjs.map +1 -0
  81. package/dist/capture/build-state/deliver.d.mts +32 -0
  82. package/dist/capture/build-state/deliver.mjs +118 -0
  83. package/dist/capture/build-state/deliver.mjs.map +1 -0
  84. package/dist/capture/build-state/index.d.mts +8 -0
  85. package/dist/capture/build-state/notify-job.d.mts +56 -0
  86. package/dist/capture/build-state/notify-job.mjs +133 -0
  87. package/dist/capture/build-state/notify-job.mjs.map +1 -0
  88. package/dist/capture/build-state/publisher.d.mts +23 -0
  89. package/dist/capture/build-state/publisher.mjs +107 -0
  90. package/dist/capture/build-state/publisher.mjs.map +1 -0
  91. package/dist/capture/build-state/resume.d.mts +9 -0
  92. package/dist/capture/build-state/resume.mjs +47 -0
  93. package/dist/capture/build-state/resume.mjs.map +1 -0
  94. package/dist/capture/build-state/types.d.mts +49 -0
  95. package/dist/capture/catalog/catalogue.d.mts +22 -0
  96. package/dist/capture/catalog/catalogue.mjs +53 -0
  97. package/dist/capture/catalog/catalogue.mjs.map +1 -0
  98. package/dist/capture/catalog/components.d.mts +23 -0
  99. package/dist/capture/catalog/components.mjs +55 -0
  100. package/dist/capture/catalog/components.mjs.map +1 -0
  101. package/dist/capture/catalog/history.d.mts +30 -0
  102. package/dist/capture/catalog/history.mjs +51 -0
  103. package/dist/capture/catalog/history.mjs.map +1 -0
  104. package/dist/capture/catalog/hook.d.mts +14 -0
  105. package/dist/capture/catalog/hook.mjs +19 -0
  106. package/dist/capture/catalog/hook.mjs.map +1 -0
  107. package/dist/capture/catalog/index.d.mts +11 -0
  108. package/dist/capture/catalog/load.d.mts +26 -0
  109. package/dist/capture/catalog/load.mjs +49 -0
  110. package/dist/capture/catalog/load.mjs.map +1 -0
  111. package/dist/capture/catalog/parse.d.mts +15 -0
  112. package/dist/capture/catalog/parse.mjs +56 -0
  113. package/dist/capture/catalog/parse.mjs.map +1 -0
  114. package/dist/capture/catalog/read.d.mts +12 -0
  115. package/dist/capture/catalog/read.mjs +33 -0
  116. package/dist/capture/catalog/read.mjs.map +1 -0
  117. package/dist/capture/catalog/types.d.mts +33 -0
  118. package/dist/capture/consumer/capture-handler.d.mts +58 -0
  119. package/dist/capture/consumer/capture-handler.mjs +118 -0
  120. package/dist/capture/consumer/capture-handler.mjs.map +1 -0
  121. package/dist/capture/consumer/idle.d.mts +17 -0
  122. package/dist/capture/consumer/idle.mjs +25 -0
  123. package/dist/capture/consumer/idle.mjs.map +1 -0
  124. package/dist/capture/consumer/index.d.mts +13 -0
  125. package/dist/capture/consumer/index.mjs +139 -0
  126. package/dist/capture/consumer/index.mjs.map +1 -0
  127. package/dist/capture/consumer/loop.mjs +110 -0
  128. package/dist/capture/consumer/loop.mjs.map +1 -0
  129. package/dist/capture/consumer/metrics.d.mts +41 -0
  130. package/dist/capture/consumer/metrics.mjs +38 -0
  131. package/dist/capture/consumer/metrics.mjs.map +1 -0
  132. package/dist/capture/consumer/retry.d.mts +33 -0
  133. package/dist/capture/consumer/retry.mjs +34 -0
  134. package/dist/capture/consumer/retry.mjs.map +1 -0
  135. package/dist/capture/consumer/run-job.mjs +195 -0
  136. package/dist/capture/consumer/run-job.mjs.map +1 -0
  137. package/dist/capture/consumer/startup.mjs +38 -0
  138. package/dist/capture/consumer/startup.mjs.map +1 -0
  139. package/dist/capture/consumer/types.d.mts +97 -0
  140. package/dist/capture/crash-failure.d.mts +25 -0
  141. package/dist/capture/crash-failure.mjs +70 -0
  142. package/dist/capture/crash-failure.mjs.map +1 -0
  143. package/dist/capture/db-queue/cadence.d.mts +32 -0
  144. package/dist/capture/db-queue/cadence.mjs +38 -0
  145. package/dist/capture/db-queue/cadence.mjs.map +1 -0
  146. package/dist/capture/db-queue/claim.d.mts +9 -0
  147. package/dist/capture/db-queue/claim.mjs +132 -0
  148. package/dist/capture/db-queue/claim.mjs.map +1 -0
  149. package/dist/capture/db-queue/clock.mjs +12 -0
  150. package/dist/capture/db-queue/clock.mjs.map +1 -0
  151. package/dist/capture/db-queue/codec.mjs +76 -0
  152. package/dist/capture/db-queue/codec.mjs.map +1 -0
  153. package/dist/capture/db-queue/index.d.mts +20 -0
  154. package/dist/capture/db-queue/index.mjs +89 -0
  155. package/dist/capture/db-queue/index.mjs.map +1 -0
  156. package/dist/capture/db-queue/keeper.d.mts +36 -0
  157. package/dist/capture/db-queue/keeper.mjs +92 -0
  158. package/dist/capture/db-queue/keeper.mjs.map +1 -0
  159. package/dist/capture/db-queue/lease.mjs +46 -0
  160. package/dist/capture/db-queue/lease.mjs.map +1 -0
  161. package/dist/capture/db-queue/lifecycle.mjs +36 -0
  162. package/dist/capture/db-queue/lifecycle.mjs.map +1 -0
  163. package/dist/capture/db-queue/operations.mjs +94 -0
  164. package/dist/capture/db-queue/operations.mjs.map +1 -0
  165. package/dist/capture/db-queue/poll-loop.d.mts +30 -0
  166. package/dist/capture/db-queue/poll-loop.mjs +89 -0
  167. package/dist/capture/db-queue/poll-loop.mjs.map +1 -0
  168. package/dist/capture/db-queue/poll.d.mts +10 -0
  169. package/dist/capture/db-queue/poll.mjs +69 -0
  170. package/dist/capture/db-queue/poll.mjs.map +1 -0
  171. package/dist/capture/db-queue/timers.d.mts +12 -0
  172. package/dist/capture/db-queue/timers.mjs +37 -0
  173. package/dist/capture/db-queue/timers.mjs.map +1 -0
  174. package/dist/capture/db-queue/types.d.mts +83 -0
  175. package/dist/capture/db-queue/types.mjs +12 -0
  176. package/dist/capture/db-queue/types.mjs.map +1 -0
  177. package/dist/capture/db-queue/wake.mjs +48 -0
  178. package/dist/capture/db-queue/wake.mjs.map +1 -0
  179. package/dist/capture/design.mjs +39 -0
  180. package/dist/capture/design.mjs.map +1 -0
  181. package/dist/capture/diff-record.mjs +23 -3
  182. package/dist/capture/diff-record.mjs.map +1 -1
  183. package/dist/capture/dispatch.d.mts +12 -7
  184. package/dist/capture/dispatch.mjs +86 -107
  185. package/dist/capture/dispatch.mjs.map +1 -1
  186. package/dist/capture/enrich.mjs +60 -0
  187. package/dist/capture/enrich.mjs.map +1 -0
  188. package/dist/capture/failure-sets.mjs +22 -0
  189. package/dist/capture/failure-sets.mjs.map +1 -0
  190. package/dist/capture/fingerprint.d.mts +42 -0
  191. package/dist/capture/fingerprint.mjs +83 -0
  192. package/dist/capture/fingerprint.mjs.map +1 -0
  193. package/dist/capture/forked/channel.d.mts +25 -0
  194. package/dist/capture/forked/child.d.mts +34 -0
  195. package/dist/capture/forked/child.mjs +202 -0
  196. package/dist/capture/forked/child.mjs.map +1 -0
  197. package/dist/capture/forked/crash-tracker.mjs +23 -0
  198. package/dist/capture/forked/crash-tracker.mjs.map +1 -0
  199. package/dist/capture/forked/decision.d.mts +26 -0
  200. package/dist/capture/forked/decision.mjs +27 -0
  201. package/dist/capture/forked/decision.mjs.map +1 -0
  202. package/dist/capture/forked/exit.mjs +12 -0
  203. package/dist/capture/forked/exit.mjs.map +1 -0
  204. package/dist/capture/forked/fork.d.mts +31 -0
  205. package/dist/capture/forked/fork.mjs +93 -0
  206. package/dist/capture/forked/fork.mjs.map +1 -0
  207. package/dist/capture/forked/forked-capture.d.mts +40 -0
  208. package/dist/capture/forked/forked-capture.mjs +240 -0
  209. package/dist/capture/forked/forked-capture.mjs.map +1 -0
  210. package/dist/capture/forked/lease-book.mjs +46 -0
  211. package/dist/capture/forked/lease-book.mjs.map +1 -0
  212. package/dist/capture/forked/protocol.d.mts +38 -0
  213. package/dist/capture/forked/protocol.mjs +25 -0
  214. package/dist/capture/forked/protocol.mjs.map +1 -0
  215. package/dist/capture/forked/runs.mjs +75 -0
  216. package/dist/capture/forked/runs.mjs.map +1 -0
  217. package/dist/capture/isolate.mjs +141 -0
  218. package/dist/capture/isolate.mjs.map +1 -0
  219. package/dist/capture/job-lease.d.mts +28 -0
  220. package/dist/capture/job-lease.mjs +57 -0
  221. package/dist/capture/job-lease.mjs.map +1 -0
  222. package/dist/capture/matrix.mjs +298 -0
  223. package/dist/capture/matrix.mjs.map +1 -0
  224. package/dist/capture/modes.d.mts +25 -0
  225. package/dist/capture/modes.mjs +107 -0
  226. package/dist/capture/modes.mjs.map +1 -0
  227. package/dist/capture/orchestrator.d.mts +37 -5
  228. package/dist/capture/orchestrator.mjs +118 -107
  229. package/dist/capture/orchestrator.mjs.map +1 -1
  230. package/dist/capture/pipeline.d.mts +9 -2
  231. package/dist/capture/pipeline.mjs +250 -45
  232. package/dist/capture/pipeline.mjs.map +1 -1
  233. package/dist/capture/progress.d.mts +11 -0
  234. package/dist/capture/progress.mjs +28 -0
  235. package/dist/capture/progress.mjs.map +1 -0
  236. package/dist/capture/publish/accept.d.mts +39 -0
  237. package/dist/capture/publish/accept.mjs +28 -0
  238. package/dist/capture/publish/accept.mjs.map +1 -0
  239. package/dist/capture/publish/complete.d.mts +46 -0
  240. package/dist/capture/publish/complete.mjs +64 -0
  241. package/dist/capture/publish/complete.mjs.map +1 -0
  242. package/dist/capture/publish/extract.d.mts +20 -0
  243. package/dist/capture/publish/extract.mjs +119 -0
  244. package/dist/capture/publish/extract.mjs.map +1 -0
  245. package/dist/capture/publish/handler.d.mts +28 -0
  246. package/dist/capture/publish/handler.mjs +62 -0
  247. package/dist/capture/publish/handler.mjs.map +1 -0
  248. package/dist/capture/publish/index.d.mts +10 -0
  249. package/dist/capture/publish/limits.d.mts +37 -0
  250. package/dist/capture/publish/limits.mjs +39 -0
  251. package/dist/capture/publish/limits.mjs.map +1 -0
  252. package/dist/capture/publish/published.d.mts +15 -0
  253. package/dist/capture/publish/published.mjs +18 -0
  254. package/dist/capture/publish/published.mjs.map +1 -0
  255. package/dist/capture/publish/run.d.mts +33 -0
  256. package/dist/capture/publish/run.mjs +30 -0
  257. package/dist/capture/publish/run.mjs.map +1 -0
  258. package/dist/capture/rediff.d.mts +1 -1
  259. package/dist/capture/rediff.mjs +4 -3
  260. package/dist/capture/rediff.mjs.map +1 -1
  261. package/dist/capture/render.mjs +53 -0
  262. package/dist/capture/render.mjs.map +1 -0
  263. package/dist/capture/scratch.d.mts +44 -0
  264. package/dist/capture/scratch.mjs +138 -0
  265. package/dist/capture/scratch.mjs.map +1 -0
  266. package/dist/capture/shard-plan.mjs +128 -0
  267. package/dist/capture/shard-plan.mjs.map +1 -0
  268. package/dist/capture/shards/context.mjs +51 -0
  269. package/dist/capture/shards/context.mjs.map +1 -0
  270. package/dist/capture/shards/execute.mjs +174 -0
  271. package/dist/capture/shards/execute.mjs.map +1 -0
  272. package/dist/capture/shards/fan-out.mjs +126 -0
  273. package/dist/capture/shards/fan-out.mjs.map +1 -0
  274. package/dist/capture/shards/history.mjs +35 -0
  275. package/dist/capture/shards/history.mjs.map +1 -0
  276. package/dist/capture/shards/reduce.mjs +97 -0
  277. package/dist/capture/shards/reduce.mjs.map +1 -0
  278. package/dist/capture/sizing.mjs +1 -11
  279. package/dist/capture/sizing.mjs.map +1 -1
  280. package/dist/capture/skip-checks.mjs +2 -65
  281. package/dist/capture/skip-checks.mjs.map +1 -1
  282. package/dist/capture/statics.d.mts +7 -8
  283. package/dist/capture/statics.mjs +25 -71
  284. package/dist/capture/statics.mjs.map +1 -1
  285. package/dist/capture/storybook-urls.d.mts +40 -0
  286. package/dist/capture/storybook-urls.mjs +41 -0
  287. package/dist/capture/storybook-urls.mjs.map +1 -0
  288. package/dist/capture/storybook.d.mts +12 -4
  289. package/dist/capture/storybook.mjs +56 -16
  290. package/dist/capture/storybook.mjs.map +1 -1
  291. package/dist/capture/target.mjs +21 -0
  292. package/dist/capture/target.mjs.map +1 -0
  293. package/dist/capture/targets.d.mts +25 -0
  294. package/dist/capture/targets.mjs +32 -0
  295. package/dist/capture/targets.mjs.map +1 -0
  296. package/dist/capture/tests.d.mts +37 -0
  297. package/dist/capture/tests.mjs +126 -0
  298. package/dist/capture/tests.mjs.map +1 -0
  299. package/dist/capture/token-chunks.mjs +74 -0
  300. package/dist/capture/token-chunks.mjs.map +1 -0
  301. package/dist/capture.d.mts +60 -7
  302. package/dist/capture.mjs +48 -3
  303. package/dist/config.d.mts +121 -12
  304. package/dist/config.mjs +9 -6
  305. package/dist/config.mjs.map +1 -1
  306. package/dist/diff.d.mts +9 -3
  307. package/dist/diff.mjs +16 -3
  308. package/dist/diff.mjs.map +1 -0
  309. package/dist/index.d.mts +1 -1
  310. package/dist/insights/budget.d.mts +20 -0
  311. package/dist/insights/budget.mjs +33 -0
  312. package/dist/insights/budget.mjs.map +1 -0
  313. package/dist/insights/envelope.d.mts +59 -0
  314. package/dist/insights/envelope.mjs +60 -0
  315. package/dist/insights/envelope.mjs.map +1 -0
  316. package/dist/insights/evidence-health.d.mts +32 -0
  317. package/dist/insights/evidence-health.mjs +28 -0
  318. package/dist/insights/evidence-health.mjs.map +1 -0
  319. package/dist/insights/evidence.d.mts +55 -0
  320. package/dist/insights/evidence.mjs +64 -0
  321. package/dist/insights/evidence.mjs.map +1 -0
  322. package/dist/insights/hash.d.mts +18 -0
  323. package/dist/insights/hash.mjs +21 -0
  324. package/dist/insights/hash.mjs.map +1 -0
  325. package/dist/insights/limits.d.mts +24 -0
  326. package/dist/insights/limits.mjs +24 -0
  327. package/dist/insights/limits.mjs.map +1 -0
  328. package/dist/insights/prompts/v1.d.mts +13 -0
  329. package/dist/insights/prompts/v1.mjs +34 -0
  330. package/dist/insights/prompts/v1.mjs.map +1 -0
  331. package/dist/insights/redact.d.mts +9 -0
  332. package/dist/insights/redact.mjs +24 -0
  333. package/dist/insights/redact.mjs.map +1 -0
  334. package/dist/insights/text.mjs +44 -0
  335. package/dist/insights/text.mjs.map +1 -0
  336. package/dist/insights.d.mts +11 -0
  337. package/dist/insights.mjs +12 -0
  338. package/dist/logger.mjs +17 -1
  339. package/dist/logger.mjs.map +1 -1
  340. package/dist/models/ai-budget-alert.d.mts +23 -0
  341. package/dist/models/ai-budget-alert.mjs +38 -0
  342. package/dist/models/ai-budget-alert.mjs.map +1 -0
  343. package/dist/models/ai-usage.d.mts +40 -0
  344. package/dist/models/ai-usage.mjs +61 -0
  345. package/dist/models/ai-usage.mjs.map +1 -0
  346. package/dist/models/baseline-archive.d.mts +84 -0
  347. package/dist/models/baseline-archive.mjs +106 -0
  348. package/dist/models/baseline-archive.mjs.map +1 -0
  349. package/dist/models/baseline-export.d.mts +19 -0
  350. package/dist/models/baseline-export.mjs +136 -0
  351. package/dist/models/baseline-export.mjs.map +1 -0
  352. package/dist/models/baseline-import.mjs +118 -0
  353. package/dist/models/baseline-import.mjs.map +1 -0
  354. package/dist/models/baseline-transfer.d.mts +49 -0
  355. package/dist/models/baseline-transfer.mjs +121 -0
  356. package/dist/models/baseline-transfer.mjs.map +1 -0
  357. package/dist/models/baseline.d.mts +32 -5
  358. package/dist/models/baseline.mjs +65 -20
  359. package/dist/models/baseline.mjs.map +1 -1
  360. package/dist/models/build-component.d.mts +28 -0
  361. package/dist/models/build-component.mjs +55 -0
  362. package/dist/models/build-component.mjs.map +1 -0
  363. package/dist/models/build-review-status.d.mts +29 -0
  364. package/dist/models/build-review-status.mjs +37 -0
  365. package/dist/models/build-review-status.mjs.map +1 -0
  366. package/dist/models/build-review.d.mts +28 -0
  367. package/dist/models/build-review.mjs +54 -0
  368. package/dist/models/build-review.mjs.map +1 -0
  369. package/dist/models/build.d.mts +25 -3
  370. package/dist/models/build.mjs +50 -11
  371. package/dist/models/build.mjs.map +1 -1
  372. package/dist/models/capture-attempt.d.mts +7 -1
  373. package/dist/models/capture-attempt.mjs +29 -4
  374. package/dist/models/capture-attempt.mjs.map +1 -1
  375. package/dist/models/capture-job.d.mts +106 -0
  376. package/dist/models/capture-job.mjs +288 -0
  377. package/dist/models/capture-job.mjs.map +1 -0
  378. package/dist/models/capture-log.d.mts +1 -1
  379. package/dist/models/capture-log.mjs +4 -3
  380. package/dist/models/capture-log.mjs.map +1 -1
  381. package/dist/models/capture-shard.d.mts +62 -0
  382. package/dist/models/capture-shard.mjs +190 -0
  383. package/dist/models/capture-shard.mjs.map +1 -0
  384. package/dist/models/changeset.d.mts +48 -0
  385. package/dist/models/changeset.mjs +81 -0
  386. package/dist/models/changeset.mjs.map +1 -0
  387. package/dist/models/columns.mjs +19 -0
  388. package/dist/models/columns.mjs.map +1 -0
  389. package/dist/models/comment.d.mts +4 -1
  390. package/dist/models/comment.mjs +7 -4
  391. package/dist/models/comment.mjs.map +1 -1
  392. package/dist/models/component.d.mts +43 -0
  393. package/dist/models/component.mjs +60 -0
  394. package/dist/models/component.mjs.map +1 -0
  395. package/dist/models/connections.d.mts +41 -0
  396. package/dist/models/connections.mjs +112 -0
  397. package/dist/models/connections.mjs.map +1 -0
  398. package/dist/models/content-ref.d.mts +1 -1
  399. package/dist/models/content-ref.mjs +1 -1
  400. package/dist/models/content-ref.mjs.map +1 -1
  401. package/dist/models/credentials.mjs +12 -2
  402. package/dist/models/credentials.mjs.map +1 -1
  403. package/dist/models/design-credentials.d.mts +41 -0
  404. package/dist/models/design-credentials.mjs +86 -0
  405. package/dist/models/design-credentials.mjs.map +1 -0
  406. package/dist/models/design-health.d.mts +16 -0
  407. package/dist/models/design-health.mjs +25 -0
  408. package/dist/models/design-health.mjs.map +1 -0
  409. package/dist/models/design-nodes.d.mts +83 -0
  410. package/dist/models/design-nodes.mjs +222 -0
  411. package/dist/models/design-nodes.mjs.map +1 -0
  412. package/dist/models/design-renders.mjs +74 -0
  413. package/dist/models/design-renders.mjs.map +1 -0
  414. package/dist/models/design-resolve.d.mts +29 -0
  415. package/dist/models/design-resolve.mjs +41 -0
  416. package/dist/models/design-resolve.mjs.map +1 -0
  417. package/dist/models/design.d.mts +39 -0
  418. package/dist/models/design.mjs +90 -0
  419. package/dist/models/design.mjs.map +1 -0
  420. package/dist/models/inherited-ratio.d.mts +21 -0
  421. package/dist/models/inherited-ratio.mjs +22 -0
  422. package/dist/models/inherited-ratio.mjs.map +1 -0
  423. package/dist/models/insight.d.mts +70 -0
  424. package/dist/models/insight.mjs +162 -0
  425. package/dist/models/insight.mjs.map +1 -0
  426. package/dist/models/label.d.mts +4 -2
  427. package/dist/models/label.mjs +25 -10
  428. package/dist/models/label.mjs.map +1 -1
  429. package/dist/models/member.d.mts +1 -1
  430. package/dist/models/member.mjs +5 -4
  431. package/dist/models/member.mjs.map +1 -1
  432. package/dist/models/notification-channel.d.mts +37 -3
  433. package/dist/models/notification-channel.mjs +88 -9
  434. package/dist/models/notification-channel.mjs.map +1 -1
  435. package/dist/models/notification-subscription.d.mts +1 -2
  436. package/dist/models/notification-subscription.mjs +7 -9
  437. package/dist/models/notification-subscription.mjs.map +1 -1
  438. package/dist/models/project-group-mapping.d.mts +6 -1
  439. package/dist/models/project-group-mapping.mjs +14 -4
  440. package/dist/models/project-group-mapping.mjs.map +1 -1
  441. package/dist/models/project.d.mts +9 -1
  442. package/dist/models/project.mjs +9 -4
  443. package/dist/models/project.mjs.map +1 -1
  444. package/dist/models/public-origin.d.mts +63 -0
  445. package/dist/models/public-origin.mjs +114 -0
  446. package/dist/models/public-origin.mjs.map +1 -0
  447. package/dist/models/review-switch-off.d.mts +14 -0
  448. package/dist/models/review-switch-off.mjs +26 -0
  449. package/dist/models/review-switch-off.mjs.map +1 -0
  450. package/dist/models/site-settings.d.mts +23 -0
  451. package/dist/models/site-settings.mjs +48 -0
  452. package/dist/models/site-settings.mjs.map +1 -0
  453. package/dist/models/snapshot.d.mts +36 -1
  454. package/dist/models/snapshot.mjs +50 -3
  455. package/dist/models/snapshot.mjs.map +1 -1
  456. package/dist/models/token.d.mts +13 -1
  457. package/dist/models/token.mjs +23 -5
  458. package/dist/models/token.mjs.map +1 -1
  459. package/dist/models/usage.d.mts +76 -0
  460. package/dist/models/usage.mjs +149 -0
  461. package/dist/models/usage.mjs.map +1 -0
  462. package/dist/models/user.d.mts +1 -1
  463. package/dist/models/user.mjs +4 -3
  464. package/dist/models/user.mjs.map +1 -1
  465. package/dist/models/webhook.d.mts +15 -1
  466. package/dist/models/webhook.mjs +33 -4
  467. package/dist/models/webhook.mjs.map +1 -1
  468. package/dist/models.d.mts +26 -3
  469. package/dist/models.mjs +27 -5
  470. package/dist/options.d.mts +18 -0
  471. package/dist/options.mjs +90 -0
  472. package/dist/options.mjs.map +1 -0
  473. package/dist/paths.d.mts +19 -4
  474. package/dist/paths.mjs +51 -10
  475. package/dist/paths.mjs.map +1 -1
  476. package/dist/retention/ai.d.mts +18 -0
  477. package/dist/retention/ai.mjs +23 -0
  478. package/dist/retention/ai.mjs.map +1 -0
  479. package/dist/retention/pending.mjs +47 -0
  480. package/dist/retention/pending.mjs.map +1 -0
  481. package/dist/retention/screenshot-sweep.d.mts +51 -0
  482. package/dist/retention/screenshot-sweep.mjs +128 -0
  483. package/dist/retention/screenshot-sweep.mjs.map +1 -0
  484. package/dist/retention.d.mts +16 -2
  485. package/dist/retention.mjs +71 -14
  486. package/dist/retention.mjs.map +1 -1
  487. package/dist/schema/baseline.d.mts +6 -0
  488. package/dist/schema/build-component.d.mts +25 -0
  489. package/dist/schema/build-review.d.mts +17 -0
  490. package/dist/schema/build.d.mts +7 -0
  491. package/dist/schema/capture-job.d.mts +60 -0
  492. package/dist/schema/capture-shard.d.mts +35 -0
  493. package/dist/schema/comment.d.mts +2 -0
  494. package/dist/schema/connection.d.mts +24 -0
  495. package/dist/schema/design.d.mts +58 -0
  496. package/dist/schema/insight.d.mts +54 -0
  497. package/dist/schema/notification.d.mts +4 -0
  498. package/dist/schema/project.d.mts +21 -0
  499. package/dist/schema/site-setting.d.mts +21 -0
  500. package/dist/schema/snapshot.d.mts +12 -0
  501. package/dist/schema/usage.d.mts +30 -0
  502. package/dist/schema.d.mts +11 -3
  503. package/dist/serverless.d.mts +21 -0
  504. package/dist/serverless.mjs +37 -0
  505. package/dist/serverless.mjs.map +1 -0
  506. package/dist/test-helpers/create-project.d.mts +1 -1
  507. package/dist/test-helpers/deferred.d.mts +39 -0
  508. package/dist/test-helpers/deferred.mjs +61 -0
  509. package/dist/test-helpers/deferred.mjs.map +1 -0
  510. package/dist/test-helpers/design-contract.d.mts +21 -0
  511. package/dist/test-helpers/design-contract.mjs +40 -0
  512. package/dist/test-helpers/design-contract.mjs.map +1 -0
  513. package/dist/test-helpers/fake-database.mjs +14 -2
  514. package/dist/test-helpers/fake-database.mjs.map +1 -1
  515. package/dist/test-helpers/fake-design.d.mts +43 -0
  516. package/dist/test-helpers/fake-design.mjs +114 -0
  517. package/dist/test-helpers/fake-design.mjs.map +1 -0
  518. package/dist/test-helpers/fake-notifier.d.mts +21 -0
  519. package/dist/test-helpers/fake-notifier.mjs +44 -0
  520. package/dist/test-helpers/fake-notifier.mjs.map +1 -0
  521. package/dist/test-helpers/fake-tables.mjs +255 -67
  522. package/dist/test-helpers/fake-tables.mjs.map +1 -1
  523. package/dist/test-helpers/manual-clock.d.mts +27 -0
  524. package/dist/test-helpers/manual-clock.mjs +56 -0
  525. package/dist/test-helpers/manual-clock.mjs.map +1 -0
  526. package/dist/test-helpers/notifier-contract.d.mts +7 -1
  527. package/dist/test-helpers/notifier-contract.mjs +32 -3
  528. package/dist/test-helpers/notifier-contract.mjs.map +1 -1
  529. package/dist/test-helpers/queue-contract.d.mts +16 -2
  530. package/dist/test-helpers/queue-contract.mjs +195 -1
  531. package/dist/test-helpers/queue-contract.mjs.map +1 -1
  532. package/dist/test-helpers/queue-multi-instance.d.mts +20 -0
  533. package/dist/test-helpers/queue-multi-instance.mjs +206 -0
  534. package/dist/test-helpers/queue-multi-instance.mjs.map +1 -0
  535. package/dist/test-helpers/runner-contract.mjs +11 -0
  536. package/dist/test-helpers/runner-contract.mjs.map +1 -1
  537. package/dist/test-helpers/sql-chunks.mjs +23 -0
  538. package/dist/test-helpers/sql-chunks.mjs.map +1 -1
  539. package/dist/test-helpers/storage-contract.mjs +1 -1
  540. package/dist/test-helpers.d.mts +9 -4
  541. package/dist/test-helpers.mjs +8 -3
  542. package/dist/types.d.mts +14 -3
  543. package/dist/types.mjs +21 -4
  544. package/dist/types.mjs.map +1 -1
  545. package/dist/urls.d.mts +4 -0
  546. package/dist/urls.mjs +11 -2
  547. package/dist/urls.mjs.map +1 -1
  548. package/dist/utils/adaptive-concurrency.d.mts +66 -0
  549. package/dist/utils/adaptive-concurrency.mjs +149 -0
  550. package/dist/utils/adaptive-concurrency.mjs.map +1 -0
  551. package/dist/utils/connect-retry.d.mts +26 -0
  552. package/dist/utils/connect-retry.mjs +43 -0
  553. package/dist/utils/connect-retry.mjs.map +1 -0
  554. package/dist/utils/environment.d.mts +19 -0
  555. package/dist/utils/environment.mjs +59 -0
  556. package/dist/utils/environment.mjs.map +1 -0
  557. package/dist/utils/http.d.mts +17 -1
  558. package/dist/utils/http.mjs +22 -3
  559. package/dist/utils/http.mjs.map +1 -1
  560. package/dist/utils/map-adaptive.d.mts +15 -0
  561. package/dist/utils/map-adaptive.mjs +72 -0
  562. package/dist/utils/map-adaptive.mjs.map +1 -0
  563. package/dist/utils/map-limit.d.mts +10 -0
  564. package/dist/utils/map-limit.mjs +32 -0
  565. package/dist/utils/map-limit.mjs.map +1 -0
  566. package/dist/utils/page-limiter.d.mts +37 -0
  567. package/dist/utils/page-limiter.mjs +56 -0
  568. package/dist/utils/page-limiter.mjs.map +1 -0
  569. package/dist/utils/public-origin.d.mts +20 -0
  570. package/dist/utils/public-origin.mjs +75 -0
  571. package/dist/utils/public-origin.mjs.map +1 -0
  572. package/dist/utils/redact.mjs +3 -0
  573. package/dist/utils/redact.mjs.map +1 -1
  574. package/dist/utils/remote-environment.d.mts +39 -0
  575. package/dist/utils/remote-environment.mjs +114 -0
  576. package/dist/utils/remote-environment.mjs.map +1 -0
  577. package/dist/utils/resources.d.mts +64 -0
  578. package/dist/utils/resources.mjs +122 -0
  579. package/dist/utils/resources.mjs.map +1 -0
  580. package/dist/utils/screenshot-store.d.mts +15 -0
  581. package/dist/utils/screenshot-store.mjs +41 -0
  582. package/dist/utils/screenshot-store.mjs.map +1 -0
  583. package/dist/utils/session-gate.d.mts +22 -0
  584. package/dist/utils/session-gate.mjs +53 -0
  585. package/dist/utils/session-gate.mjs.map +1 -0
  586. package/dist/utils/storybook-token.d.mts +81 -0
  587. package/dist/utils/storybook-token.mjs +134 -0
  588. package/dist/utils/storybook-token.mjs.map +1 -0
  589. package/dist/utils/storybook-url.d.mts +15 -0
  590. package/dist/utils/storybook-url.mjs +21 -0
  591. package/dist/utils/storybook-url.mjs.map +1 -0
  592. package/dist/utils/timing.mjs +1 -1
  593. package/dist/utils/timing.mjs.map +1 -1
  594. package/dist/utils/zip-read.mjs +129 -0
  595. package/dist/utils/zip-read.mjs.map +1 -0
  596. package/dist/utils/zip-write.d.mts +18 -0
  597. package/dist/utils/zip-write.mjs +188 -0
  598. package/dist/utils/zip-write.mjs.map +1 -0
  599. package/dist/utils.d.mts +17 -3
  600. package/dist/utils.mjs +18 -4
  601. package/package.json +32 -20
  602. package/dist/adapter/capture-runner.mjs.map +0 -1
  603. package/dist/adapter/git-host/comments.d.mts +0 -41
  604. package/dist/adapter/git-host/comments.mjs +0 -54
  605. package/dist/adapter/git-host/comments.mjs.map +0 -1
  606. package/dist/adapter/git-host/helpers.d.mts +0 -13
  607. package/dist/adapter/git-host/helpers.mjs +0 -30
  608. package/dist/adapter/git-host/helpers.mjs.map +0 -1
  609. package/dist/adapter/git-host.d.mts +0 -42
  610. package/dist/capture/queue.d.mts +0 -38
  611. package/dist/capture/queue.mjs +0 -98
  612. package/dist/capture/queue.mjs.map +0 -1
  613. package/dist/capture/recovery.d.mts +0 -21
  614. package/dist/capture/recovery.mjs +0 -83
  615. package/dist/capture/recovery.mjs.map +0 -1
  616. package/dist/capture/status-fanout.mjs +0 -72
  617. package/dist/capture/status-fanout.mjs.map +0 -1
  618. package/dist/diff/engine.d.mts +0 -16
  619. package/dist/diff/engine.mjs +0 -87
  620. package/dist/diff/engine.mjs.map +0 -1
  621. package/dist/diff/options.d.mts +0 -42
  622. package/dist/diff/options.mjs +0 -15
  623. package/dist/diff/options.mjs.map +0 -1
  624. package/dist/models/status-config.d.mts +0 -38
  625. package/dist/models/status-config.mjs +0 -63
  626. package/dist/models/status-config.mjs.map +0 -1
  627. package/dist/schema/status-config.d.mts +0 -16
  628. package/dist/test-helpers/git-contract.d.mts +0 -11
  629. package/dist/test-helpers/git-contract.mjs +0 -34
  630. package/dist/test-helpers/git-contract.mjs.map +0 -1
  631. /package/dist/adapter/{git-host.mjs → oauth.mjs} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"webhook.mjs","names":[],"sources":["../../src/models/webhook.ts"],"sourcesContent":["/** Webhook subscriptions for project events. */\nimport { eq, getTableColumns } from \"drizzle-orm\";\nimport type { SQLWrapper, Table } from \"drizzle-orm\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { Webhook } from \"../schema/webhook.ts\";\nimport { decryptCredential, encrypt, type SecretInput } from \"../utils/encrypt.ts\";\nimport { ulid } from \"../utils/ulid.ts\";\n\n/** Tables required by {@link WebhookModel}. */\nexport interface WebhookTables {\n webhooks: Table;\n}\n\n/** Data operations for webhook subscriptions. */\nexport class WebhookModel {\n private readonly tables: WebhookTables;\n private readonly secret?: SecretInput;\n /**\n * @param db - Database adapter.\n * @param tables - Table handles.\n * @param secret - Server secret for webhook-secret encryption (throws on write/decrypt when unset).\n */\n constructor(\n private readonly db: DatabaseAdapter,\n tables?: WebhookTables,\n secret?: SecretInput,\n ) {\n this.tables = tables ?? { webhooks: db.tables.webhooks };\n this.secret = secret;\n }\n\n /** List all webhooks for a project. */\n async list(projectId: string): Promise<Webhook[]> {\n return (await this.db.list(this.tables.webhooks, {\n where: eq(\n getTableColumns(this.tables.webhooks)[\"projectId\"] as unknown as SQLWrapper,\n projectId,\n ),\n })) as unknown as Webhook[];\n }\n\n /**\n * Create a webhook subscription for a project.\n *\n * @param projectId - Project ID.\n * @param input - Webhook creation input.\n * @returns The created webhook.\n */\n async create(projectId: string, input: WebhookCreateInput): Promise<Webhook> {\n const now = new Date().toISOString();\n return (await this.db.insert(this.tables.webhooks, {\n id: ulid(),\n projectId,\n url: input.url,\n secretEncrypted: encrypt(this.secret, input.secret),\n events: input.events && input.events.length > 0 ? JSON.stringify(input.events) : null,\n createdAt: now,\n updatedAt: now,\n })) as unknown as Webhook;\n }\n\n /** Decrypt the secret for a stored row (in memory only, at send time). */\n decryptSecret(row: Webhook): string {\n return decryptCredential(this.secret, row.secretEncrypted);\n }\n\n /** Fetch a webhook by id scoped to a project, or null if not found. */\n async get(projectId: string, id: string): Promise<Webhook | null> {\n const rows = (await this.db.list(this.tables.webhooks, {\n where: eq(getTableColumns(this.tables.webhooks)[\"id\"] as unknown as SQLWrapper, id),\n limit: 1,\n })) as unknown as Webhook[];\n const found = rows[0] ?? null;\n return found?.projectId === projectId ? found : null;\n }\n\n /** Remove a webhook if it belongs to the given project. */\n async remove(projectId: string, id: string): Promise<void> {\n const existing = await this.get(projectId, id);\n if (existing) {\n await this.db.remove(this.tables.webhooks, existing.id);\n }\n }\n\n /** Decode the JSON-serialized event list of a webhook. */\n static eventsOf(webhook: Webhook): string[] {\n if (!webhook.events) {\n return [];\n }\n try {\n const parsed: unknown = JSON.parse(webhook.events);\n return Array.isArray(parsed)\n ? parsed.filter((event): event is string => typeof event === \"string\")\n : [];\n } catch {\n return [];\n }\n }\n}\n\n/** Input for creating a webhook subscription. */\nexport interface WebhookCreateInput {\n url: string;\n events?: string[];\n secret: string;\n}\n"],"mappings":";;;;;;;;;AAcA,IAAa,eAAb,MAA0B;CASL;CARnB;CACA;;;;;;CAMA,YACE,IACA,QACA,QACA;EAHiB,KAAA,KAAA;EAIjB,KAAK,SAAS,UAAU,EAAE,UAAU,GAAG,OAAO,SAAS;EACvD,KAAK,SAAS;CAChB;;CAGA,MAAM,KAAK,WAAuC;EAChD,OAAQ,MAAM,KAAK,GAAG,KAAK,KAAK,OAAO,UAAU,EAC/C,OAAO,GACL,gBAAgB,KAAK,OAAO,QAAQ,CAAC,CAAC,cACtC,SACF,EACF,CAAC;CACH;;;;;;;;CASA,MAAM,OAAO,WAAmB,OAA6C;EAC3E,MAAM,uBAAM,IAAI,KAAK,EAAA,CAAE,YAAY;EACnC,OAAQ,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,UAAU;GACjD,IAAI,KAAK;GACT;GACA,KAAK,MAAM;GACX,iBAAiB,QAAQ,KAAK,QAAQ,MAAM,MAAM;GAClD,QAAQ,MAAM,UAAU,MAAM,OAAO,SAAS,IAAI,KAAK,UAAU,MAAM,MAAM,IAAI;GACjF,WAAW;GACX,WAAW;EACb,CAAC;CACH;;CAGA,cAAc,KAAsB;EAClC,OAAO,kBAAkB,KAAK,QAAQ,IAAI,eAAe;CAC3D;;CAGA,MAAM,IAAI,WAAmB,IAAqC;EAKhE,MAAM,SAAQ,MAJM,KAAK,GAAG,KAAK,KAAK,OAAO,UAAU;GACrD,OAAO,GAAG,gBAAgB,KAAK,OAAO,QAAQ,CAAC,CAAC,OAAgC,EAAE;GAClF,OAAO;EACT,CAAC,EAAA,CACkB,MAAM;EACzB,OAAO,OAAO,cAAc,YAAY,QAAQ;CAClD;;CAGA,MAAM,OAAO,WAAmB,IAA2B;EACzD,MAAM,WAAW,MAAM,KAAK,IAAI,WAAW,EAAE;EAC7C,IAAI,UACF,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,UAAU,SAAS,EAAE;CAE1D;;CAGA,OAAO,SAAS,SAA4B;EAC1C,IAAI,CAAC,QAAQ,QACX,OAAO,CAAC;EAEV,IAAI;GACF,MAAM,SAAkB,KAAK,MAAM,QAAQ,MAAM;GACjD,OAAO,MAAM,QAAQ,MAAM,IACvB,OAAO,QAAQ,UAA2B,OAAO,UAAU,QAAQ,IACnE,CAAC;EACP,QAAQ;GACN,OAAO,CAAC;EACV;CACF;AACF"}
1
+ {"version":3,"file":"webhook.mjs","names":[],"sources":["../../src/models/webhook.ts"],"sourcesContent":["/** Webhook subscriptions for project events. */\nimport { eq } from \"drizzle-orm\";\nimport type { Table } from \"drizzle-orm\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { Webhook } from \"../schema/webhook.ts\";\nimport { decryptCredential, encrypt, type SecretInput } from \"../utils/encrypt.ts\";\nimport { randomToken } from \"../utils/hash.ts\";\nimport { ulid } from \"../utils/ulid.ts\";\nimport { col } from \"./columns.ts\";\n\n/** Tables required by {@link WebhookModel}. */\nexport interface WebhookTables {\n webhooks: Table;\n}\n\n/** Data operations for webhook subscriptions. */\nexport class WebhookModel {\n private readonly tables: WebhookTables;\n private readonly secret?: SecretInput;\n private readonly db: DatabaseAdapter;\n\n /**\n * @param db - Database adapter.\n * @param tables - Table handles.\n * @param secret - Server secret for webhook-secret encryption (throws on write/decrypt when unset).\n */\n constructor(db: DatabaseAdapter, tables?: WebhookTables, secret?: SecretInput) {\n this.db = db;\n this.tables = tables ?? { webhooks: db.tables.webhooks };\n this.secret = secret;\n }\n\n /** List all webhooks for a project. */\n async list(projectId: string): Promise<Webhook[]> {\n return (await this.db.list(this.tables.webhooks, {\n where: eq(col(this.tables.webhooks, \"projectId\"), projectId),\n })) as unknown as Webhook[];\n }\n\n /** Validate a webhook target URL; returns an error message, or null when acceptable. */\n static urlError(url: string | undefined): string | null {\n if (!url) {\n return \"A valid URL is required\";\n }\n let protocol: string;\n try {\n ({ protocol } = new URL(url));\n } catch {\n return \"Enter a valid URL\";\n }\n return protocol === \"https:\" || protocol === \"http:\" ? null : \"URL must use http or https\";\n }\n\n /**\n * Create a webhook with a freshly generated `whsec_` signing secret.\n *\n * @returns The stored webhook and the plaintext secret (shown to the user once).\n */\n async issue(\n projectId: string,\n input: { url: string; events?: string[] },\n ): Promise<{ webhook: Webhook; secret: string }> {\n const secret = randomToken(\"whsec_\").value;\n const webhook = await this.create(projectId, { url: input.url, events: input.events, secret });\n return { webhook, secret };\n }\n\n /**\n * Create a webhook subscription for a project.\n *\n * @param projectId - Project ID.\n * @param input - Webhook creation input.\n * @returns The created webhook.\n */\n async create(projectId: string, input: WebhookCreateInput): Promise<Webhook> {\n const now = new Date().toISOString();\n return (await this.db.insert(this.tables.webhooks, {\n id: ulid(),\n projectId,\n url: input.url,\n secretEncrypted: encrypt(this.secret, input.secret),\n events: input.events && input.events.length > 0 ? JSON.stringify(input.events) : null,\n createdAt: now,\n updatedAt: now,\n })) as unknown as Webhook;\n }\n\n /** Decrypt the secret for a stored row (in memory only, at send time). */\n decryptSecret(row: Webhook): string {\n return decryptCredential(this.secret, row.secretEncrypted);\n }\n\n /** Fetch a webhook by id scoped to a project, or null if not found. */\n async get(projectId: string, id: string): Promise<Webhook | null> {\n const rows = (await this.db.list(this.tables.webhooks, {\n where: eq(col(this.tables.webhooks, \"id\"), id),\n limit: 1,\n })) as unknown as Webhook[];\n const found = rows[0] ?? null;\n return found?.projectId === projectId ? found : null;\n }\n\n /** Remove a webhook if it belongs to the given project. */\n async remove(projectId: string, id: string): Promise<void> {\n const existing = await this.get(projectId, id);\n if (existing) {\n await this.db.remove(this.tables.webhooks, existing.id);\n }\n }\n\n /** Decode the JSON-serialized event list of a webhook. */\n static eventsOf(webhook: Webhook): string[] {\n if (!webhook.events) {\n return [];\n }\n try {\n const parsed: unknown = JSON.parse(webhook.events);\n return Array.isArray(parsed)\n ? parsed.filter((event): event is string => typeof event === \"string\")\n : [];\n } catch {\n return [];\n }\n }\n}\n\n/** Input for creating a webhook subscription. */\nexport interface WebhookCreateInput {\n url: string;\n events?: string[];\n secret: string;\n}\n"],"mappings":";;;;;;;;;;;AAgBA,IAAa,eAAb,MAA0B;CACxB;CACA;CACA;;;;;;CAOA,YAAY,IAAqB,QAAwB,QAAsB;EAC7E,KAAK,KAAK;EACV,KAAK,SAAS,UAAU,EAAE,UAAU,GAAG,OAAO,SAAS;EACvD,KAAK,SAAS;CAChB;;CAGA,MAAM,KAAK,WAAuC;EAChD,OAAQ,MAAM,KAAK,GAAG,KAAK,KAAK,OAAO,UAAU,EAC/C,OAAO,GAAG,IAAI,KAAK,OAAO,UAAU,WAAW,GAAG,SAAS,EAC7D,CAAC;CACH;;CAGA,OAAO,SAAS,KAAwC;EACtD,IAAI,CAAC,KACH,OAAO;EAET,IAAI;EACJ,IAAI;GACF,CAAC,CAAE,YAAa,IAAI,IAAI,GAAG;EAC7B,QAAQ;GACN,OAAO;EACT;EACA,OAAO,aAAa,YAAY,aAAa,UAAU,OAAO;CAChE;;;;;;CAOA,MAAM,MACJ,WACA,OAC+C;EAC/C,MAAM,SAAS,YAAY,QAAQ,CAAC,CAAC;EAErC,OAAO;GAAE,SAAA,MADa,KAAK,OAAO,WAAW;IAAE,KAAK,MAAM;IAAK,QAAQ,MAAM;IAAQ;GAAO,CAAC;GAC3E;EAAO;CAC3B;;;;;;;;CASA,MAAM,OAAO,WAAmB,OAA6C;EAC3E,MAAM,uBAAM,IAAI,KAAK,EAAA,CAAE,YAAY;EACnC,OAAQ,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,UAAU;GACjD,IAAI,KAAK;GACT;GACA,KAAK,MAAM;GACX,iBAAiB,QAAQ,KAAK,QAAQ,MAAM,MAAM;GAClD,QAAQ,MAAM,UAAU,MAAM,OAAO,SAAS,IAAI,KAAK,UAAU,MAAM,MAAM,IAAI;GACjF,WAAW;GACX,WAAW;EACb,CAAC;CACH;;CAGA,cAAc,KAAsB;EAClC,OAAO,kBAAkB,KAAK,QAAQ,IAAI,eAAe;CAC3D;;CAGA,MAAM,IAAI,WAAmB,IAAqC;EAKhE,MAAM,SAAQ,MAJM,KAAK,GAAG,KAAK,KAAK,OAAO,UAAU;GACrD,OAAO,GAAG,IAAI,KAAK,OAAO,UAAU,IAAI,GAAG,EAAE;GAC7C,OAAO;EACT,CAAC,EAAA,CACkB,MAAM;EACzB,OAAO,OAAO,cAAc,YAAY,QAAQ;CAClD;;CAGA,MAAM,OAAO,WAAmB,IAA2B;EACzD,MAAM,WAAW,MAAM,KAAK,IAAI,WAAW,EAAE;EAC7C,IAAI,UACF,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,UAAU,SAAS,EAAE;CAE1D;;CAGA,OAAO,SAAS,SAA4B;EAC1C,IAAI,CAAC,QAAQ,QACX,OAAO,CAAC;EAEV,IAAI;GACF,MAAM,SAAkB,KAAK,MAAM,QAAQ,MAAM;GACjD,OAAO,MAAM,QAAQ,MAAM,IACvB,OAAO,QAAQ,UAA2B,OAAO,UAAU,QAAQ,IACnE,CAAC;EACP,QAAQ;GACN,OAAO,CAAC;EACV;CACF;AACF"}
package/dist/models.d.mts CHANGED
@@ -7,16 +7,39 @@ import { CaptureLogModel, CaptureLogTables } from "./models/capture-log.mjs";
7
7
  import { ProjectCreateInput, ProjectModel } from "./models/project.mjs";
8
8
  import { BaselineModel } from "./models/baseline.mjs";
9
9
  import { SnapshotCreateInput, SnapshotModel } from "./models/snapshot.mjs";
10
- import { StatusConfigCreateInput, StatusConfigModel } from "./models/status-config.mjs";
10
+ import { AttemptClaim, CaptureJobModel, CaptureJobTables, NewCaptureJob, TERMINAL_JOB_STATUSES, attemptRowId, isTerminalJob, parseAttemptRowId } from "./models/capture-job.mjs";
11
+ import { BuildComponentInput, BuildComponentModel, BuildComponentTables } from "./models/build-component.mjs";
12
+ import { ComponentHistoryRow, ComponentPresence, componentHistory, componentPresence, worstStatus } from "./models/component.mjs";
13
+ import { BASELINE_ARCHIVE_FORMAT, BASELINE_ARCHIVE_LIMITS, BASELINE_ARCHIVE_VERSION, BaselineArchiveError, BaselineManifest, BaselineManifestEntry } from "./models/baseline-archive.mjs";
14
+ import { BaselineExportOptions } from "./models/baseline-export.mjs";
15
+ import { BaselineImportResult, BaselineTransferModel } from "./models/baseline-transfer.mjs";
11
16
  import { BaselineStatus, NO_BASELINE, baselineStatus, isBaselineDrifted } from "./models/baseline-status.mjs";
17
+ import { CaptureShardModel, CaptureShardTables } from "./models/capture-shard.mjs";
18
+ import { AiBudgetAlertModel, AiBudgetAlertTables } from "./models/ai-budget-alert.mjs";
19
+ import { AiUsageInput, AiUsageModel, AiUsageTables } from "./models/ai-usage.mjs";
20
+ import { InsightModel, InsightResultInput, InsightStartInput, InsightStartResult, InsightTables } from "./models/insight.mjs";
21
+ import { InheritedRatio, RECENT_BUILDS_FOR_INHERITED_RATIO, averageInheritedRatio } from "./models/inherited-ratio.mjs";
22
+ import { BuildReviewModel, BuildReviewTables } from "./models/build-review.mjs";
23
+ import { ReviewStatusInput, deriveReviewStatus, latestVerdicts } from "./models/build-review-status.mjs";
24
+ import { BuildChangeset, ChangedComponent, Changeset, ChangesetComponent, changesetOf, computeChangeset, findChangesetBaseline } from "./models/changeset.mjs";
12
25
  import { CommentCreateInput, CommentModel } from "./models/comment.mjs";
26
+ import { acceptPendingWithoutWork } from "./models/review-switch-off.mjs";
27
+ import { ConnectionModel, ConnectionTables } from "./models/connections.mjs";
28
+ import { DesignModel, DesignTables } from "./models/design.mjs";
29
+ import { DesignCredentialStatus, DesignResolution, DesignSource, normalizeImportPath, resolveDesigns } from "./models/design-resolve.mjs";
30
+ import { DesignCredentialModel, DesignCredentialTables, PROJECT_SCOPE } from "./models/design-credentials.mjs";
31
+ import { DesignNodeModel, DesignNodeTables, RefreshOptions } from "./models/design-nodes.mjs";
32
+ import { DESIGN_TTL_MS, credentialDaysLeft, isDesignStale } from "./models/design-health.mjs";
13
33
  import { LabelModel } from "./models/label.mjs";
34
+ import { PackageUsageInput, PackageUsageModel, PackageUsageTables, UpstreamStory, matchUpstreamStory } from "./models/usage.mjs";
14
35
  import { MemberModel } from "./models/member.mjs";
15
- import { NotificationChannelCreateInput, NotificationChannelModel, NotificationChannelTables } from "./models/notification-channel.mjs";
36
+ import { ChannelValidation, NotificationChannelCreateInput, NotificationChannelModel, NotificationChannelTables } from "./models/notification-channel.mjs";
16
37
  import { NotificationSubscriptionInput, NotificationSubscriptionModel, NotificationSubscriptionTables } from "./models/notification-subscription.mjs";
17
38
  import { ProjectGroupMappingModel } from "./models/project-group-mapping.mjs";
18
39
  import { TokenModel } from "./models/token.mjs";
19
40
  import { UserModel } from "./models/user.mjs";
20
41
  import { ContentRefModel } from "./models/content-ref.mjs";
21
42
  import { CredentialProbe, ReencryptResult, UnreadableCredential, probeCredentials, reencryptCredentials } from "./models/credentials.mjs";
22
- export { type AttemptLogRecorder, BaselineModel, type BaselineStatus, type BuildCreateInput, type BuildListFilter, BuildModel, CaptureAttemptModel, type CaptureAttemptTables, CaptureLogModel, type CaptureLogTables, type CommentCreateInput, CommentModel, ContentRefModel, type CredentialProbe, LabelModel, MemberModel, NO_BASELINE, type NotificationChannelCreateInput, NotificationChannelModel, type NotificationChannelTables, type NotificationSubscriptionInput, NotificationSubscriptionModel, type NotificationSubscriptionTables, type ProjectCreateInput, ProjectGroupMappingModel, ProjectModel, type ReencryptResult, type SnapshotCreateInput, SnapshotModel, type StatusConfigCreateInput, StatusConfigModel, TokenModel, type UnreadableCredential, UserModel, type WebhookCreateInput, WebhookModel, baselineStatus, emitAttemptLog, isBaselineDrifted, isPublicBuild, probeCredentials, reencryptCredentials };
43
+ import { SiteSettingsModel, SiteSettingsTables } from "./models/site-settings.mjs";
44
+ import { PUBLIC_ORIGIN_KEY, PublicOriginOptions, PublicOriginResolver, PublicOriginSource, ResolvedPublicOrigin, StoredPublicOrigin, createPublicOriginResolver } from "./models/public-origin.mjs";
45
+ export { AiBudgetAlertModel, type AiBudgetAlertTables, type AiUsageInput, AiUsageModel, type AiUsageTables, type AttemptClaim, type AttemptLogRecorder, BASELINE_ARCHIVE_FORMAT, BASELINE_ARCHIVE_LIMITS, BASELINE_ARCHIVE_VERSION, BaselineArchiveError, type BaselineExportOptions, type BaselineImportResult, type BaselineManifest, type BaselineManifestEntry, BaselineModel, type BaselineStatus, BaselineTransferModel, type BuildChangeset, type BuildComponentInput, BuildComponentModel, type BuildComponentTables, type BuildCreateInput, type BuildListFilter, BuildModel, BuildReviewModel, type BuildReviewTables, CaptureAttemptModel, type CaptureAttemptTables, CaptureJobModel, type CaptureJobTables, CaptureLogModel, type CaptureLogTables, CaptureShardModel, type CaptureShardTables, type ChangedComponent, type Changeset, type ChangesetComponent, type ChannelValidation, type CommentCreateInput, CommentModel, type ComponentHistoryRow, type ComponentPresence, ConnectionModel, type ConnectionTables, ContentRefModel, type CredentialProbe, DESIGN_TTL_MS, DesignCredentialModel, type DesignCredentialStatus, type DesignCredentialTables, DesignModel, DesignNodeModel, type DesignNodeTables, type DesignResolution, type DesignSource, type DesignTables, type InheritedRatio, InsightModel, type InsightResultInput, type InsightStartInput, type InsightStartResult, type InsightTables, LabelModel, MemberModel, NO_BASELINE, type NewCaptureJob, type NotificationChannelCreateInput, NotificationChannelModel, type NotificationChannelTables, type NotificationSubscriptionInput, NotificationSubscriptionModel, type NotificationSubscriptionTables, PROJECT_SCOPE, PUBLIC_ORIGIN_KEY, type PackageUsageInput, PackageUsageModel, type PackageUsageTables, type ProjectCreateInput, ProjectGroupMappingModel, ProjectModel, type PublicOriginOptions, type PublicOriginResolver, type PublicOriginSource, RECENT_BUILDS_FOR_INHERITED_RATIO, type ReencryptResult, type RefreshOptions, type ResolvedPublicOrigin, type ReviewStatusInput, SiteSettingsModel, type SiteSettingsTables, type SnapshotCreateInput, SnapshotModel, type StoredPublicOrigin, TERMINAL_JOB_STATUSES, TokenModel, type UnreadableCredential, type UpstreamStory, UserModel, type WebhookCreateInput, WebhookModel, acceptPendingWithoutWork, attemptRowId, averageInheritedRatio, baselineStatus, changesetOf, componentHistory, componentPresence, computeChangeset, createPublicOriginResolver, credentialDaysLeft, deriveReviewStatus, emitAttemptLog, findChangesetBaseline, isBaselineDrifted, isDesignStale, isPublicBuild, isTerminalJob, latestVerdicts, matchUpstreamStory, normalizeImportPath, parseAttemptRowId, probeCredentials, reencryptCredentials, resolveDesigns, worstStatus };
package/dist/models.mjs CHANGED
@@ -2,22 +2,44 @@ import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
4
  import { WebhookModel } from "./models/webhook.mjs";
5
- import { BuildModel, isPublicBuild } from "./models/build.mjs";
6
5
  import { CaptureAttemptModel, emitAttemptLog } from "./models/capture-attempt.mjs";
7
- import { ProjectModel } from "./models/project.mjs";
6
+ import { BuildModel, isPublicBuild } from "./models/build.mjs";
8
7
  import { BaselineModel } from "./models/baseline.mjs";
9
8
  import { NO_BASELINE, baselineStatus, isBaselineDrifted } from "./models/baseline-status.mjs";
10
9
  import { SnapshotModel } from "./models/snapshot.mjs";
11
- import { StatusConfigModel } from "./models/status-config.mjs";
10
+ import { CaptureShardModel } from "./models/capture-shard.mjs";
11
+ import { ProjectModel } from "./models/project.mjs";
12
+ import { deriveReviewStatus, latestVerdicts } from "./models/build-review-status.mjs";
13
+ import { BuildReviewModel } from "./models/build-review.mjs";
14
+ import { NotificationChannelModel } from "./models/notification-channel.mjs";
15
+ import { CaptureJobModel, TERMINAL_JOB_STATUSES, attemptRowId, isTerminalJob, parseAttemptRowId } from "./models/capture-job.mjs";
16
+ import { SiteSettingsModel } from "./models/site-settings.mjs";
17
+ import { PUBLIC_ORIGIN_KEY, createPublicOriginResolver } from "./models/public-origin.mjs";
12
18
  import { CaptureLogModel } from "./models/capture-log.mjs";
19
+ import { BuildComponentModel } from "./models/build-component.mjs";
20
+ import { normalizeImportPath, resolveDesigns } from "./models/design-resolve.mjs";
21
+ import { BASELINE_ARCHIVE_FORMAT, BASELINE_ARCHIVE_LIMITS, BASELINE_ARCHIVE_VERSION, BaselineArchiveError } from "./models/baseline-archive.mjs";
22
+ import { BaselineTransferModel } from "./models/baseline-transfer.mjs";
23
+ import { AiBudgetAlertModel } from "./models/ai-budget-alert.mjs";
24
+ import { AiUsageModel } from "./models/ai-usage.mjs";
25
+ import { InsightModel } from "./models/insight.mjs";
26
+ import { RECENT_BUILDS_FOR_INHERITED_RATIO, averageInheritedRatio } from "./models/inherited-ratio.mjs";
27
+ import { changesetOf, computeChangeset, findChangesetBaseline } from "./models/changeset.mjs";
13
28
  import { CommentModel } from "./models/comment.mjs";
29
+ import { acceptPendingWithoutWork } from "./models/review-switch-off.mjs";
30
+ import { componentHistory, componentPresence, worstStatus } from "./models/component.mjs";
31
+ import { ConnectionModel } from "./models/connections.mjs";
32
+ import { DesignModel } from "./models/design.mjs";
33
+ import { DesignCredentialModel, PROJECT_SCOPE } from "./models/design-credentials.mjs";
34
+ import { DESIGN_TTL_MS, credentialDaysLeft, isDesignStale } from "./models/design-health.mjs";
35
+ import { DesignNodeModel } from "./models/design-nodes.mjs";
14
36
  import { LabelModel } from "./models/label.mjs";
37
+ import { PackageUsageModel, matchUpstreamStory } from "./models/usage.mjs";
15
38
  import { MemberModel } from "./models/member.mjs";
16
- import { NotificationChannelModel } from "./models/notification-channel.mjs";
17
39
  import { NotificationSubscriptionModel } from "./models/notification-subscription.mjs";
18
40
  import { ProjectGroupMappingModel } from "./models/project-group-mapping.mjs";
19
41
  import { TokenModel } from "./models/token.mjs";
20
42
  import { UserModel } from "./models/user.mjs";
21
43
  import { ContentRefModel } from "./models/content-ref.mjs";
22
44
  import { probeCredentials, reencryptCredentials } from "./models/credentials.mjs";
23
- export { BaselineModel, BuildModel, CaptureAttemptModel, CaptureLogModel, CommentModel, ContentRefModel, LabelModel, MemberModel, NO_BASELINE, NotificationChannelModel, NotificationSubscriptionModel, ProjectGroupMappingModel, ProjectModel, SnapshotModel, StatusConfigModel, TokenModel, UserModel, WebhookModel, baselineStatus, emitAttemptLog, isBaselineDrifted, isPublicBuild, probeCredentials, reencryptCredentials };
45
+ export { AiBudgetAlertModel, AiUsageModel, BASELINE_ARCHIVE_FORMAT, BASELINE_ARCHIVE_LIMITS, BASELINE_ARCHIVE_VERSION, BaselineArchiveError, BaselineModel, BaselineTransferModel, BuildComponentModel, BuildModel, BuildReviewModel, CaptureAttemptModel, CaptureJobModel, CaptureLogModel, CaptureShardModel, CommentModel, ConnectionModel, ContentRefModel, DESIGN_TTL_MS, DesignCredentialModel, DesignModel, DesignNodeModel, InsightModel, LabelModel, MemberModel, NO_BASELINE, NotificationChannelModel, NotificationSubscriptionModel, PROJECT_SCOPE, PUBLIC_ORIGIN_KEY, PackageUsageModel, ProjectGroupMappingModel, ProjectModel, RECENT_BUILDS_FOR_INHERITED_RATIO, SiteSettingsModel, SnapshotModel, TERMINAL_JOB_STATUSES, TokenModel, UserModel, WebhookModel, acceptPendingWithoutWork, attemptRowId, averageInheritedRatio, baselineStatus, changesetOf, componentHistory, componentPresence, computeChangeset, createPublicOriginResolver, credentialDaysLeft, deriveReviewStatus, emitAttemptLog, findChangesetBaseline, isBaselineDrifted, isDesignStale, isPublicBuild, isTerminalJob, latestVerdicts, matchUpstreamStory, normalizeImportPath, parseAttemptRowId, probeCredentials, reencryptCredentials, resolveDesigns, worstStatus };
@@ -0,0 +1,18 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { ShelfOptions } from "./config.mjs";
4
+ //#region src/options.d.ts
5
+ /** Where capture scratch lives when `config.scratchDir` is not set. */
6
+ declare const DEFAULT_SCRATCH_DIR: string;
7
+ /**
8
+ * Validate and complete the options.
9
+ *
10
+ * - `testing.compare.threads` must be a non-negative integer.
11
+ * - `notifications.config` must match its schema (it used to live in `ShelfConfig`, which validated it).
12
+ * - `config.scratchDir` defaults to {@link DEFAULT_SCRATCH_DIR}: publishing extracts zips there,
13
+ * with or without `testing`.
14
+ */
15
+ declare function resolveShelfOptions(options: ShelfOptions): ShelfOptions;
16
+ //#endregion
17
+ export { DEFAULT_SCRATCH_DIR, resolveShelfOptions };
18
+ //# sourceMappingURL=options.d.mts.map
@@ -0,0 +1,90 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { notificationsConfigSchema } from "./config.mjs";
5
+ import { tmpdir } from "node:os";
6
+ import { join } from "node:path";
7
+ //#region src/options.ts
8
+ /** Resolves `ShelfOptions` once at boot: validates `testing.compare` and fills the scratch default. */
9
+ /** Where capture scratch lives when `config.scratchDir` is not set. */
10
+ const DEFAULT_SCRATCH_DIR = join(tmpdir(), "storyshelf");
11
+ /**
12
+ * Validate and complete the options.
13
+ *
14
+ * - `testing.compare.threads` must be a non-negative integer.
15
+ * - `notifications.config` must match its schema (it used to live in `ShelfConfig`, which validated it).
16
+ * - `config.scratchDir` defaults to {@link DEFAULT_SCRATCH_DIR}: publishing extracts zips there,
17
+ * with or without `testing`.
18
+ */
19
+ function resolveShelfOptions(options) {
20
+ assertThreads(options.testing?.compare?.threads);
21
+ assertTestingProcess(options.testing);
22
+ assertTestingBackable(options);
23
+ assertNotificationsConfig(options.notifications?.config);
24
+ if (options.config?.scratchDir !== void 0) return options;
25
+ return {
26
+ ...options,
27
+ config: {
28
+ ...options.config,
29
+ scratchDir: DEFAULT_SCRATCH_DIR
30
+ }
31
+ };
32
+ }
33
+ /**
34
+ * A `testing` instance renders here, so it needs a database a long-running server can keep.
35
+ */
36
+ function assertTestingBackable(options) {
37
+ if (options.testing === void 0) return;
38
+ if (options.database.metadata.supportsTesting === false) throw new Error(`The ${options.database.metadata.name} database cannot back testing: it is reachable only from a platform that ends the process after each request, so nothing would run capture jobs. Remove the testing option to host Storybooks only, or use a network database (Turso, Postgres, MySQL, SQLite on a volume).`);
39
+ }
40
+ function assertTestingProcess(testing) {
41
+ if (testing !== void 0 && testing.runner === void 0) throw new Error("testing requires testing.runner");
42
+ if (testing?.workerEntry !== void 0 && testing.runner === void 0) throw new Error("testing.workerEntry requires testing.runner (it is the in-process fallback)");
43
+ if (testing?.mode === "fork" && testing.workerEntry === void 0) throw new Error("testing.mode \"fork\" requires testing.workerEntry");
44
+ assertShardSize(testing);
45
+ assertTestingNumbers(testing);
46
+ }
47
+ /** Numeric `testing` options: concurrency, retries and the Storybook URL rate limit. */
48
+ function assertTestingNumbers(testing) {
49
+ assertConcurrency(testing?.concurrency);
50
+ assertRetries(testing?.retries);
51
+ assertLeaseSeconds(testing?.leaseSeconds);
52
+ assertStorybookUrlRateLimit(testing?.storybookUrlRateLimit);
53
+ }
54
+ /** A whole number of requests per minute; `0` would refuse every render. */
55
+ function assertStorybookUrlRateLimit(limit) {
56
+ if (limit !== void 0 && (!Number.isInteger(limit) || limit < 1)) throw new Error(`testing.storybookUrlRateLimit must be a positive whole number of requests per minute, got ${String(limit)}`);
57
+ }
58
+ /** Retries are counted after the first attempt: `0` runs a job once. */
59
+ function assertRetries(retries) {
60
+ if (retries !== void 0 && (!Number.isInteger(retries) || retries < 0)) throw new Error(`testing.retries must be a non-negative integer, got ${String(retries)}`);
61
+ }
62
+ /** A lease must be a positive length: `0` would expire every job at once. */
63
+ function assertLeaseSeconds(seconds) {
64
+ if (seconds !== void 0 && (!Number.isFinite(seconds) || seconds <= 0)) throw new Error(`testing.leaseSeconds must be a positive number of seconds, got ${String(seconds)}`);
65
+ }
66
+ /** `0` would deadlock the consumer. */
67
+ function assertConcurrency(concurrency) {
68
+ if (concurrency !== void 0 && (!Number.isInteger(concurrency) || concurrency < 1)) throw new Error(`testing.concurrency must be a positive integer, got ${String(concurrency)}`);
69
+ }
70
+ function assertShardSize(testing) {
71
+ const size = testing?.shardSize;
72
+ if (size === void 0) return;
73
+ if (!Number.isInteger(size) || size < 1) throw new Error(`testing.shardSize must be a positive integer, got ${String(size)}`);
74
+ if (testing?.runner === void 0) throw new Error("testing.shardSize requires testing.runner (workers set their own shardSize)");
75
+ }
76
+ function assertThreads(threads) {
77
+ if (threads !== void 0 && (!Number.isInteger(threads) || threads < 0)) throw new Error(`testing.compare.threads must be a non-negative integer, got ${String(threads)}`);
78
+ }
79
+ function assertNotificationsConfig(config) {
80
+ if (config === void 0) return;
81
+ const result = notificationsConfigSchema.safeParse(config);
82
+ if (!result.success) {
83
+ const issues = result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
84
+ throw new Error(`Invalid notifications.config: ${issues}`);
85
+ }
86
+ }
87
+ //#endregion
88
+ export { DEFAULT_SCRATCH_DIR, resolveShelfOptions };
89
+
90
+ //# sourceMappingURL=options.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.mjs","names":[],"sources":["../src/options.ts"],"sourcesContent":["/** Resolves `ShelfOptions` once at boot: validates `testing.compare` and fills the scratch default. */\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { notificationsConfigSchema, type ShelfOptions } from \"./config.ts\";\n\n/** Where capture scratch lives when `config.scratchDir` is not set. */\nexport const DEFAULT_SCRATCH_DIR: string = join(tmpdir(), \"storyshelf\");\n\n/**\n * Validate and complete the options.\n *\n * - `testing.compare.threads` must be a non-negative integer.\n * - `notifications.config` must match its schema (it used to live in `ShelfConfig`, which validated it).\n * - `config.scratchDir` defaults to {@link DEFAULT_SCRATCH_DIR}: publishing extracts zips there,\n * with or without `testing`.\n */\nexport function resolveShelfOptions(options: ShelfOptions): ShelfOptions {\n assertThreads(options.testing?.compare?.threads);\n assertTestingProcess(options.testing);\n assertTestingBackable(options);\n assertNotificationsConfig(options.notifications?.config);\n if (options.config?.scratchDir !== undefined) {\n return options;\n }\n return { ...options, config: { ...options.config, scratchDir: DEFAULT_SCRATCH_DIR } };\n}\n\n/**\n * A `testing` instance renders here, so it needs a database a long-running server can keep.\n */\nfunction assertTestingBackable(options: ShelfOptions): void {\n if (options.testing === undefined) {\n return;\n }\n if (options.database.metadata.supportsTesting === false) {\n throw new Error(\n `The ${options.database.metadata.name} database cannot back testing: it is reachable only ` +\n `from a platform that ends the process after each request, so nothing would run capture ` +\n `jobs. Remove the testing option to host Storybooks only, or use a network database ` +\n `(Turso, Postgres, MySQL, SQLite on a volume).`,\n );\n }\n}\n\nfunction assertTestingProcess(testing: ShelfOptions[\"testing\"]): void {\n if (testing !== undefined && testing.runner === undefined) {\n throw new Error(\"testing requires testing.runner\");\n }\n if (testing?.workerEntry !== undefined && testing.runner === undefined) {\n throw new Error(\"testing.workerEntry requires testing.runner (it is the in-process fallback)\");\n }\n if (testing?.mode === \"fork\" && testing.workerEntry === undefined) {\n throw new Error('testing.mode \"fork\" requires testing.workerEntry');\n }\n assertShardSize(testing);\n assertTestingNumbers(testing);\n}\n\n/** Numeric `testing` options: concurrency, retries and the Storybook URL rate limit. */\nfunction assertTestingNumbers(testing: ShelfOptions[\"testing\"]): void {\n assertConcurrency(testing?.concurrency);\n assertRetries(testing?.retries);\n assertLeaseSeconds(testing?.leaseSeconds);\n assertStorybookUrlRateLimit(testing?.storybookUrlRateLimit);\n}\n\n/** A whole number of requests per minute; `0` would refuse every render. */\nfunction assertStorybookUrlRateLimit(limit: number | undefined): void {\n if (limit !== undefined && (!Number.isInteger(limit) || limit < 1)) {\n throw new Error(\n `testing.storybookUrlRateLimit must be a positive whole number of requests per minute, got ${String(limit)}`,\n );\n }\n}\n\n/** Retries are counted after the first attempt: `0` runs a job once. */\nfunction assertRetries(retries: number | undefined): void {\n if (retries !== undefined && (!Number.isInteger(retries) || retries < 0)) {\n throw new Error(`testing.retries must be a non-negative integer, got ${String(retries)}`);\n }\n}\n\n/** A lease must be a positive length: `0` would expire every job at once. */\nfunction assertLeaseSeconds(seconds: number | undefined): void {\n if (seconds !== undefined && (!Number.isFinite(seconds) || seconds <= 0)) {\n throw new Error(\n `testing.leaseSeconds must be a positive number of seconds, got ${String(seconds)}`,\n );\n }\n}\n\n/** `0` would deadlock the consumer. */\nfunction assertConcurrency(concurrency: number | undefined): void {\n if (concurrency !== undefined && (!Number.isInteger(concurrency) || concurrency < 1)) {\n throw new Error(`testing.concurrency must be a positive integer, got ${String(concurrency)}`);\n }\n}\n\nfunction assertShardSize(testing: ShelfOptions[\"testing\"]): void {\n const size = testing?.shardSize;\n if (size === undefined) {\n return;\n }\n if (!Number.isInteger(size) || size < 1) {\n throw new Error(`testing.shardSize must be a positive integer, got ${String(size)}`);\n }\n if (testing?.runner === undefined) {\n throw new Error(\"testing.shardSize requires testing.runner (workers set their own shardSize)\");\n }\n}\n\nfunction assertThreads(threads: number | undefined): void {\n if (threads !== undefined && (!Number.isInteger(threads) || threads < 0)) {\n throw new Error(\n `testing.compare.threads must be a non-negative integer, got ${String(threads)}`,\n );\n }\n}\n\nfunction assertNotificationsConfig(config: unknown): void {\n if (config === undefined) {\n return;\n }\n const result = notificationsConfigSchema.safeParse(config);\n if (!result.success) {\n const issues = result.error.issues.map((i) => `${i.path.join(\".\")}: ${i.message}`).join(\"; \");\n throw new Error(`Invalid notifications.config: ${issues}`);\n }\n}\n"],"mappings":";;;;;;;;;AAMA,MAAa,sBAA8B,KAAK,OAAO,GAAG,YAAY;;;;;;;;;AAUtE,SAAgB,oBAAoB,SAAqC;CACvE,cAAc,QAAQ,SAAS,SAAS,OAAO;CAC/C,qBAAqB,QAAQ,OAAO;CACpC,sBAAsB,OAAO;CAC7B,0BAA0B,QAAQ,eAAe,MAAM;CACvD,IAAI,QAAQ,QAAQ,eAAe,KAAA,GACjC,OAAO;CAET,OAAO;EAAE,GAAG;EAAS,QAAQ;GAAE,GAAG,QAAQ;GAAQ,YAAY;EAAoB;CAAE;AACtF;;;;AAKA,SAAS,sBAAsB,SAA6B;CAC1D,IAAI,QAAQ,YAAY,KAAA,GACtB;CAEF,IAAI,QAAQ,SAAS,SAAS,oBAAoB,OAChD,MAAM,IAAI,MACR,OAAO,QAAQ,SAAS,SAAS,KAAK,4QAIxC;AAEJ;AAEA,SAAS,qBAAqB,SAAwC;CACpE,IAAI,YAAY,KAAA,KAAa,QAAQ,WAAW,KAAA,GAC9C,MAAM,IAAI,MAAM,iCAAiC;CAEnD,IAAI,SAAS,gBAAgB,KAAA,KAAa,QAAQ,WAAW,KAAA,GAC3D,MAAM,IAAI,MAAM,6EAA6E;CAE/F,IAAI,SAAS,SAAS,UAAU,QAAQ,gBAAgB,KAAA,GACtD,MAAM,IAAI,MAAM,oDAAkD;CAEpE,gBAAgB,OAAO;CACvB,qBAAqB,OAAO;AAC9B;;AAGA,SAAS,qBAAqB,SAAwC;CACpE,kBAAkB,SAAS,WAAW;CACtC,cAAc,SAAS,OAAO;CAC9B,mBAAmB,SAAS,YAAY;CACxC,4BAA4B,SAAS,qBAAqB;AAC5D;;AAGA,SAAS,4BAA4B,OAAiC;CACpE,IAAI,UAAU,KAAA,MAAc,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,IAC9D,MAAM,IAAI,MACR,6FAA6F,OAAO,KAAK,GAC3G;AAEJ;;AAGA,SAAS,cAAc,SAAmC;CACxD,IAAI,YAAY,KAAA,MAAc,CAAC,OAAO,UAAU,OAAO,KAAK,UAAU,IACpE,MAAM,IAAI,MAAM,uDAAuD,OAAO,OAAO,GAAG;AAE5F;;AAGA,SAAS,mBAAmB,SAAmC;CAC7D,IAAI,YAAY,KAAA,MAAc,CAAC,OAAO,SAAS,OAAO,KAAK,WAAW,IACpE,MAAM,IAAI,MACR,kEAAkE,OAAO,OAAO,GAClF;AAEJ;;AAGA,SAAS,kBAAkB,aAAuC;CAChE,IAAI,gBAAgB,KAAA,MAAc,CAAC,OAAO,UAAU,WAAW,KAAK,cAAc,IAChF,MAAM,IAAI,MAAM,uDAAuD,OAAO,WAAW,GAAG;AAEhG;AAEA,SAAS,gBAAgB,SAAwC;CAC/D,MAAM,OAAO,SAAS;CACtB,IAAI,SAAS,KAAA,GACX;CAEF,IAAI,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,GACpC,MAAM,IAAI,MAAM,qDAAqD,OAAO,IAAI,GAAG;CAErF,IAAI,SAAS,WAAW,KAAA,GACtB,MAAM,IAAI,MAAM,6EAA6E;AAEjG;AAEA,SAAS,cAAc,SAAmC;CACxD,IAAI,YAAY,KAAA,MAAc,CAAC,OAAO,UAAU,OAAO,KAAK,UAAU,IACpE,MAAM,IAAI,MACR,+DAA+D,OAAO,OAAO,GAC/E;AAEJ;AAEA,SAAS,0BAA0B,QAAuB;CACxD,IAAI,WAAW,KAAA,GACb;CAEF,MAAM,SAAS,0BAA0B,UAAU,MAAM;CACzD,IAAI,CAAC,OAAO,SAAS;EACnB,MAAM,SAAS,OAAO,MAAM,OAAO,KAAK,MAAM,GAAG,EAAE,KAAK,KAAK,GAAG,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,KAAK,IAAI;EAC5F,MAAM,IAAI,MAAM,iCAAiC,QAAQ;CAC3D;AACF"}
package/dist/paths.d.mts CHANGED
@@ -1,16 +1,31 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  //#region src/utils/paths.d.ts
4
+ /**
5
+ * Path segment for a mode on a given engine. Chromium keeps the bare mode key
6
+ * so pre-multi-browser objects stay addressable; other engines get `~browser`.
7
+ */
8
+ declare function modeSegment(viewport: string, browser?: string): string;
4
9
  /** Storage path for a captured snapshot screenshot. */
5
- declare function screenshotPath(projectId: string, buildId: string, storyId: string, viewport: string): string;
10
+ declare function screenshotPath(projectId: string, buildId: string, storyId: string, viewport: string, browser?: string): string;
11
+ /** Storage prefix of a project's content-addressed screenshots. */
12
+ declare function contentDir(projectId: string): string;
13
+ /** Storage path of a screenshot stored by the SHA-256 of its bytes (shared by builds and baselines). */
14
+ declare function contentPath(projectId: string, hash: string): string;
15
+ /** Whether `path` is one of `projectId`'s content-addressed screenshots. */
16
+ declare function isContentPath(projectId: string, path: string): boolean;
6
17
  /** Storage path for a snapshot diff overlay image. */
7
- declare function diffPath(projectId: string, buildId: string, storyId: string, viewport: string): string;
18
+ declare function diffPath(projectId: string, buildId: string, storyId: string, viewport: string, browser?: string): string;
19
+ /** Storage path for a cached design node render (overwritten in place when the file version moves). */
20
+ declare function designRenderPath(projectId: string, provider: string, fileKey: string, nodeId: string, viewerId?: string): string;
8
21
  /** Storage path for a branch baseline screenshot. */
9
- declare function baselinePath(projectId: string, branch: string, storyId: string, viewport: string): string;
22
+ declare function baselinePath(projectId: string, branch: string, storyId: string, viewport: string, browser?: string): string;
10
23
  /** Storage prefix for an extracted published Storybook. */
11
24
  declare function storybookDir(projectId: string, buildId: string): string;
12
25
  /** Storage path for an uploaded Storybook zip. */
13
26
  declare function storybookZipPath(projectId: string, buildId: string): string;
27
+ /** Storage path of a build's persisted catalog (ADR 0006, decision 9), next to its `storybook/` statics. */
28
+ declare function catalogPath(projectId: string, buildId: string): string;
14
29
  //#endregion
15
- export { baselinePath, diffPath, screenshotPath, storybookDir, storybookZipPath };
30
+ export { baselinePath, catalogPath, contentDir, contentPath, designRenderPath, diffPath, isContentPath, modeSegment, screenshotPath, storybookDir, storybookZipPath };
16
31
  //# sourceMappingURL=paths.d.mts.map
package/dist/paths.mjs CHANGED
@@ -11,35 +11,71 @@ function assertSegments(segments) {
11
11
  const bad = segments.filter((segment) => !isSafeSegment(segment) || segment.length === 0);
12
12
  if (bad.length > 0) throw new Error(`Unsafe storage path segment: ${bad.join(",")}`);
13
13
  }
14
+ /**
15
+ * Path segment for a mode on a given engine. Chromium keeps the bare mode key
16
+ * so pre-multi-browser objects stay addressable; other engines get `~browser`.
17
+ */
18
+ function modeSegment(viewport, browser) {
19
+ return browser && browser !== "chromium" ? `${viewport}~${browser}` : viewport;
20
+ }
14
21
  /** Storage path for a captured snapshot screenshot. */
15
- function screenshotPath(projectId, buildId, storyId, viewport) {
22
+ function screenshotPath(projectId, buildId, storyId, viewport, browser) {
23
+ const segment = modeSegment(viewport, browser);
16
24
  assertSegments([
17
25
  projectId,
18
26
  buildId,
19
27
  storyId,
20
- viewport
28
+ segment
21
29
  ]);
22
- return `${projectId}/builds/${buildId}/screenshots/${storyId}/${viewport}.png`;
30
+ return `${projectId}/builds/${buildId}/screenshots/${storyId}/${segment}.png`;
31
+ }
32
+ /** Storage prefix of a project's content-addressed screenshots. */
33
+ function contentDir(projectId) {
34
+ assertSegments([projectId]);
35
+ return `${projectId}/content`;
36
+ }
37
+ /** Storage path of a screenshot stored by the SHA-256 of its bytes (shared by builds and baselines). */
38
+ function contentPath(projectId, hash) {
39
+ assertSegments([projectId, hash]);
40
+ return `${contentDir(projectId)}/${hash}.png`;
41
+ }
42
+ /** Whether `path` is one of `projectId`'s content-addressed screenshots. */
43
+ function isContentPath(projectId, path) {
44
+ return path.startsWith(`${contentDir(projectId)}/`) && path.endsWith(".png");
23
45
  }
24
46
  /** Storage path for a snapshot diff overlay image. */
25
- function diffPath(projectId, buildId, storyId, viewport) {
47
+ function diffPath(projectId, buildId, storyId, viewport, browser) {
48
+ const segment = modeSegment(viewport, browser);
26
49
  assertSegments([
27
50
  projectId,
28
51
  buildId,
29
52
  storyId,
30
- viewport
53
+ segment
54
+ ]);
55
+ return `${projectId}/builds/${buildId}/diffs/${storyId}/${segment}.png`;
56
+ }
57
+ /** Storage path for a cached design node render (overwritten in place when the file version moves). */
58
+ function designRenderPath(projectId, provider, fileKey, nodeId, viewerId = "") {
59
+ const node = nodeId.replaceAll(":", "-");
60
+ assertSegments([
61
+ projectId,
62
+ provider,
63
+ fileKey,
64
+ node,
65
+ ...viewerId ? [viewerId] : []
31
66
  ]);
32
- return `${projectId}/builds/${buildId}/diffs/${storyId}/${viewport}.png`;
67
+ return `${projectId}/design/${provider}/${viewerId ? `user-${viewerId}/` : ""}${fileKey}/${node}.png`;
33
68
  }
34
69
  /** Storage path for a branch baseline screenshot. */
35
- function baselinePath(projectId, branch, storyId, viewport) {
70
+ function baselinePath(projectId, branch, storyId, viewport, browser) {
71
+ const segment = modeSegment(viewport, browser);
36
72
  assertSegments([
37
73
  projectId,
38
74
  branch,
39
75
  storyId,
40
- viewport
76
+ segment
41
77
  ]);
42
- return `${projectId}/baselines/${branch}/${storyId}/${viewport}.png`;
78
+ return `${projectId}/baselines/${branch}/${storyId}/${segment}.png`;
43
79
  }
44
80
  /** Storage prefix for an extracted published Storybook. */
45
81
  function storybookDir(projectId, buildId) {
@@ -51,7 +87,12 @@ function storybookZipPath(projectId, buildId) {
51
87
  assertSegments([projectId, buildId]);
52
88
  return `${projectId}/builds/${buildId}/storybook.zip`;
53
89
  }
90
+ /** Storage path of a build's persisted catalog (ADR 0006, decision 9), next to its `storybook/` statics. */
91
+ function catalogPath(projectId, buildId) {
92
+ assertSegments([projectId, buildId]);
93
+ return `${projectId}/builds/${buildId}/catalog.json`;
94
+ }
54
95
  //#endregion
55
- export { baselinePath, diffPath, screenshotPath, storybookDir, storybookZipPath };
96
+ export { baselinePath, catalogPath, contentDir, contentPath, designRenderPath, diffPath, isContentPath, modeSegment, screenshotPath, storybookDir, storybookZipPath };
56
97
 
57
98
  //# sourceMappingURL=paths.mjs.map
@@ -1 +1 @@
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"}
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/**\n * Path segment for a mode on a given engine. Chromium keeps the bare mode key\n * so pre-multi-browser objects stay addressable; other engines get `~browser`.\n */\nexport function modeSegment(viewport: string, browser?: string): string {\n return browser && browser !== \"chromium\" ? `${viewport}~${browser}` : viewport;\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 browser?: string,\n): string {\n const segment = modeSegment(viewport, browser);\n assertSegments([projectId, buildId, storyId, segment]);\n return `${projectId}/builds/${buildId}/screenshots/${storyId}/${segment}.png`;\n}\n\n/** Storage prefix of a project's content-addressed screenshots. */\nexport function contentDir(projectId: string): string {\n assertSegments([projectId]);\n return `${projectId}/content`;\n}\n\n/** Storage path of a screenshot stored by the SHA-256 of its bytes (shared by builds and baselines). */\nexport function contentPath(projectId: string, hash: string): string {\n assertSegments([projectId, hash]);\n return `${contentDir(projectId)}/${hash}.png`;\n}\n\n/** Whether `path` is one of `projectId`'s content-addressed screenshots. */\nexport function isContentPath(projectId: string, path: string): boolean {\n return path.startsWith(`${contentDir(projectId)}/`) && path.endsWith(\".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 browser?: string,\n): string {\n const segment = modeSegment(viewport, browser);\n assertSegments([projectId, buildId, storyId, segment]);\n return `${projectId}/builds/${buildId}/diffs/${storyId}/${segment}.png`;\n}\n\n/** Storage path for a cached design node render (overwritten in place when the file version moves). */\nexport function designRenderPath(\n projectId: string,\n provider: string,\n fileKey: string,\n nodeId: string,\n viewerId = \"\",\n): string {\n const node = nodeId.replaceAll(\":\", \"-\");\n assertSegments([projectId, provider, fileKey, node, ...(viewerId ? [viewerId] : [])]);\n const owner = viewerId ? `user-${viewerId}/` : \"\";\n return `${projectId}/design/${provider}/${owner}${fileKey}/${node}.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 browser?: string,\n): string {\n const segment = modeSegment(viewport, browser);\n assertSegments([projectId, branch, storyId, segment]);\n return `${projectId}/baselines/${branch}/${storyId}/${segment}.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\n/** Storage path of a build's persisted catalog (ADR 0006, decision 9), next to its `storybook/` statics. */\nexport function catalogPath(projectId: string, buildId: string): string {\n assertSegments([projectId, buildId]);\n return `${projectId}/builds/${buildId}/catalog.json`;\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;;;;;AAKA,SAAgB,YAAY,UAAkB,SAA0B;CACtE,OAAO,WAAW,YAAY,aAAa,GAAG,SAAS,GAAG,YAAY;AACxE;;AAGA,SAAgB,eACd,WACA,SACA,SACA,UACA,SACQ;CACR,MAAM,UAAU,YAAY,UAAU,OAAO;CAC7C,eAAe;EAAC;EAAW;EAAS;EAAS;CAAO,CAAC;CACrD,OAAO,GAAG,UAAU,UAAU,QAAQ,eAAe,QAAQ,GAAG,QAAQ;AAC1E;;AAGA,SAAgB,WAAW,WAA2B;CACpD,eAAe,CAAC,SAAS,CAAC;CAC1B,OAAO,GAAG,UAAU;AACtB;;AAGA,SAAgB,YAAY,WAAmB,MAAsB;CACnE,eAAe,CAAC,WAAW,IAAI,CAAC;CAChC,OAAO,GAAG,WAAW,SAAS,EAAE,GAAG,KAAK;AAC1C;;AAGA,SAAgB,cAAc,WAAmB,MAAuB;CACtE,OAAO,KAAK,WAAW,GAAG,WAAW,SAAS,EAAE,EAAE,KAAK,KAAK,SAAS,MAAM;AAC7E;;AAGA,SAAgB,SACd,WACA,SACA,SACA,UACA,SACQ;CACR,MAAM,UAAU,YAAY,UAAU,OAAO;CAC7C,eAAe;EAAC;EAAW;EAAS;EAAS;CAAO,CAAC;CACrD,OAAO,GAAG,UAAU,UAAU,QAAQ,SAAS,QAAQ,GAAG,QAAQ;AACpE;;AAGA,SAAgB,iBACd,WACA,UACA,SACA,QACA,WAAW,IACH;CACR,MAAM,OAAO,OAAO,WAAW,KAAK,GAAG;CACvC,eAAe;EAAC;EAAW;EAAU;EAAS;EAAM,GAAI,WAAW,CAAC,QAAQ,IAAI,CAAC;CAAE,CAAC;CAEpF,OAAO,GAAG,UAAU,UAAU,SAAS,GADzB,WAAW,QAAQ,SAAS,KAAK,KACG,QAAQ,GAAG,KAAK;AACpE;;AAGA,SAAgB,aACd,WACA,QACA,SACA,UACA,SACQ;CACR,MAAM,UAAU,YAAY,UAAU,OAAO;CAC7C,eAAe;EAAC;EAAW;EAAQ;EAAS;CAAO,CAAC;CACpD,OAAO,GAAG,UAAU,aAAa,OAAO,GAAG,QAAQ,GAAG,QAAQ;AAChE;;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;;AAGA,SAAgB,YAAY,WAAmB,SAAyB;CACtE,eAAe,CAAC,WAAW,OAAO,CAAC;CACnC,OAAO,GAAG,UAAU,UAAU,QAAQ;AACxC"}
@@ -0,0 +1,18 @@
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 { Logger } from "../logger.mjs";
5
+ //#region src/retention/ai.d.ts
6
+ /** Pending/running rows older than this are marked `interrupted`. */
7
+ declare const STALE_RUN_MS: number;
8
+ /** What one AI retention pass removed. */
9
+ interface AiPurgeResult {
10
+ health: number;
11
+ usage: number;
12
+ interrupted: number;
13
+ }
14
+ /** Run one AI retention pass (safe to call from any instance, any time). */
15
+ declare function purgeAiData(db: DatabaseAdapter, now?: Date, logger?: Logger): Promise<AiPurgeResult>;
16
+ //#endregion
17
+ export { AiPurgeResult, STALE_RUN_MS, purgeAiData };
18
+ //# sourceMappingURL=ai.d.mts.map
@@ -0,0 +1,23 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { AiUsageModel } from "../models/ai-usage.mjs";
5
+ import { InsightModel } from "../models/insight.mjs";
6
+ /** Pending/running rows older than this are marked `interrupted`. */
7
+ const STALE_RUN_MS = 18e5;
8
+ /** Run one AI retention pass (safe to call from any instance, any time). */
9
+ async function purgeAiData(db, now = /* @__PURE__ */ new Date(), logger) {
10
+ const insights = new InsightModel(db);
11
+ const day = 864e5;
12
+ const result = {
13
+ health: await insights.purgeHealthBefore((/* @__PURE__ */ new Date(now.getTime() - 90 * day)).toISOString()),
14
+ usage: await new AiUsageModel(db).purgeBefore((/* @__PURE__ */ new Date(now.getTime() - 90 * day)).toISOString()),
15
+ interrupted: await insights.sweepStale((/* @__PURE__ */ new Date(now.getTime() - STALE_RUN_MS)).toISOString())
16
+ };
17
+ if (result.health > 0 || result.usage > 0 || result.interrupted > 0) logger?.info(result, "ai data retention pass complete");
18
+ return result;
19
+ }
20
+ //#endregion
21
+ export { STALE_RUN_MS, purgeAiData };
22
+
23
+ //# sourceMappingURL=ai.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai.mjs","names":[],"sources":["../../src/retention/ai.ts"],"sourcesContent":["/** Retention for AI data: old health digests, old usage rows and interrupted runs. */\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { Logger } from \"../logger.ts\";\nimport { AiUsageModel } from \"../models/ai-usage.ts\";\nimport { InsightModel } from \"../models/insight.ts\";\n\n/** Health digests older than this are deleted (ADR 0016 §5). */\nexport const HEALTH_RETENTION_DAYS = 90;\n\n/** Usage rows older than this are deleted (budgets only look at days/hours). */\nexport const USAGE_RETENTION_DAYS = 90;\n\n/** Pending/running rows older than this are marked `interrupted`. */\nexport const STALE_RUN_MS = 30 * 60_000;\n\n/** What one AI retention pass removed. */\nexport interface AiPurgeResult {\n health: number;\n usage: number;\n interrupted: number;\n}\n\n/** Run one AI retention pass (safe to call from any instance, any time). */\nexport async function purgeAiData(\n db: DatabaseAdapter,\n now: Date = new Date(),\n logger?: Logger,\n): Promise<AiPurgeResult> {\n const insights = new InsightModel(db);\n const day = 86_400_000;\n const result: AiPurgeResult = {\n health: await insights.purgeHealthBefore(\n new Date(now.getTime() - HEALTH_RETENTION_DAYS * day).toISOString(),\n ),\n usage: await new AiUsageModel(db).purgeBefore(\n new Date(now.getTime() - USAGE_RETENTION_DAYS * day).toISOString(),\n ),\n interrupted: await insights.sweepStale(new Date(now.getTime() - STALE_RUN_MS).toISOString()),\n };\n if (result.health > 0 || result.usage > 0 || result.interrupted > 0) {\n logger?.info(result, \"ai data retention pass complete\");\n }\n return result;\n}\n"],"mappings":";;;;;;AAaA,MAAa,eAAe;;AAU5B,eAAsB,YACpB,IACA,sBAAY,IAAI,KAAK,GACrB,QACwB;CACxB,MAAM,WAAW,IAAI,aAAa,EAAE;CACpC,MAAM,MAAM;CACZ,MAAM,SAAwB;EAC5B,QAAQ,MAAM,SAAS,mCACrB,IAAI,KAAK,IAAI,QAAQ,IAAA,KAA4B,GAAG,EAAA,CAAE,YAAY,CACpE;EACA,OAAO,MAAM,IAAI,aAAa,EAAE,CAAC,CAAC,6BAChC,IAAI,KAAK,IAAI,QAAQ,IAAA,KAA2B,GAAG,EAAA,CAAE,YAAY,CACnE;EACA,aAAa,MAAM,SAAS,4BAAW,IAAI,KAAK,IAAI,QAAQ,IAAI,YAAY,EAAA,CAAE,YAAY,CAAC;CAC7F;CACA,IAAI,OAAO,SAAS,KAAK,OAAO,QAAQ,KAAK,OAAO,cAAc,GAChE,QAAQ,KAAK,QAAQ,iCAAiC;CAExD,OAAO;AACT"}
@@ -0,0 +1,47 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { BuildModel } from "../models/build.mjs";
5
+ import { BuildReviewModel } from "../models/build-review.mjs";
6
+ import { CommentModel } from "../models/comment.mjs";
7
+ //#region src/retention/pending.ts
8
+ /**
9
+ * The project's pending builds whose last activity (upload, comment or verdict) is before
10
+ * `cutoff`. `capture_attempts` need no check: a build capture worked on leaves `pending`.
11
+ */
12
+ async function pendingCandidates(db, projectId, cutoff) {
13
+ const builds = new BuildModel(db);
14
+ const pending = await builds.list(projectId, { status: "pending" });
15
+ if (pending.length === 0) return [];
16
+ const all = await builds.list(projectId);
17
+ return (await Promise.all(pending.map(async (build) => {
18
+ const activity = await activityOf(db, build);
19
+ if (activity.last >= cutoff) return null;
20
+ const superseded = !activity.touched && hasNewerOnBranch(all, build);
21
+ return {
22
+ buildId: build.id,
23
+ reason: superseded ? "superseded" : "idle"
24
+ };
25
+ }))).filter((candidate) => candidate !== null);
26
+ }
27
+ async function activityOf(db, build) {
28
+ const [comments, reviews] = await Promise.all([new CommentModel(db).listByBuild(build.id), new BuildReviewModel(db).listByBuild(build.id)]);
29
+ return {
30
+ last: [
31
+ build.updatedAt,
32
+ ...comments.map((item) => stampOf(item)),
33
+ ...reviews.map((item) => stampOf(item))
34
+ ].toSorted().at(-1) ?? build.updatedAt,
35
+ touched: comments.length > 0 || reviews.length > 0
36
+ };
37
+ }
38
+ function stampOf(item) {
39
+ return item.createdAt;
40
+ }
41
+ function hasNewerOnBranch(all, build) {
42
+ return all.some((other) => other.id !== build.id && other.gitBranch === build.gitBranch && other.createdAt > build.createdAt);
43
+ }
44
+ //#endregion
45
+ export { pendingCandidates };
46
+
47
+ //# sourceMappingURL=pending.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pending.mjs","names":[],"sources":["../../src/retention/pending.ts"],"sourcesContent":["/**\n * Pending builds nobody finishes (ADR 0013, decision 9; ADR 0009, decision 4). A `pending` build\n * waits for a review verdict, so the terminal-status rule never reaches it; this rule gives it an\n * exit. A pending build is purgeable once its last activity is older than the retention window:\n * the later of its own last update (upload) and its newest comment or verdict. A build superseded\n * by a newer one on its branch, with no attempts, verdicts or comments, has no activity beyond\n * its upload, so it leaves at the same window; the build under review is never cut short.\n */\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport { BuildReviewModel } from \"../models/build-review.ts\";\nimport { BuildModel } from \"../models/build.ts\";\nimport { CommentModel } from \"../models/comment.ts\";\nimport type { Build } from \"../schema/build.ts\";\n\n/** Why a pending build is purgeable. */\nexport type PendingReason = \"superseded\" | \"idle\";\n\n/** A pending build the retention pass may remove. */\nexport interface PendingCandidate {\n buildId: string;\n reason: PendingReason;\n}\n\n/**\n * The project's pending builds whose last activity (upload, comment or verdict) is before\n * `cutoff`. `capture_attempts` need no check: a build capture worked on leaves `pending`.\n */\nexport async function pendingCandidates(\n db: DatabaseAdapter,\n projectId: string,\n cutoff: string,\n): Promise<PendingCandidate[]> {\n const builds = new BuildModel(db);\n const pending = await builds.list(projectId, { status: \"pending\" });\n if (pending.length === 0) {\n return [];\n }\n const all = await builds.list(projectId);\n const found = await Promise.all(\n pending.map(async (build): Promise<PendingCandidate | null> => {\n const activity = await activityOf(db, build);\n if (activity.last >= cutoff) {\n return null;\n }\n const superseded = !activity.touched && hasNewerOnBranch(all, build);\n return { buildId: build.id, reason: superseded ? \"superseded\" : \"idle\" };\n }),\n );\n return found.filter((candidate) => candidate !== null);\n}\n\ninterface Activity {\n /** Newest of the build's own update, its comments and its verdicts. */\n last: string;\n /** A person commented on or gave a verdict to the build. */\n touched: boolean;\n}\n\nasync function activityOf(db: DatabaseAdapter, build: Build): Promise<Activity> {\n const [comments, reviews] = await Promise.all([\n new CommentModel(db).listByBuild(build.id),\n new BuildReviewModel(db).listByBuild(build.id),\n ]);\n const stamps = [\n build.updatedAt,\n ...comments.map((item) => stampOf(item)),\n ...reviews.map((item) => stampOf(item)),\n ];\n return {\n last: stamps.toSorted().at(-1) ?? build.updatedAt,\n touched: comments.length > 0 || reviews.length > 0,\n };\n}\n\nfunction stampOf(item: { createdAt: string }): string {\n return item.createdAt;\n}\n\nfunction hasNewerOnBranch(all: readonly Build[], build: Build): boolean {\n return all.some(\n (other) =>\n other.id !== build.id &&\n other.gitBranch === build.gitBranch &&\n other.createdAt > build.createdAt,\n );\n}\n"],"mappings":";;;;;;;;;;;AA2BA,eAAsB,kBACpB,IACA,WACA,QAC6B;CAC7B,MAAM,SAAS,IAAI,WAAW,EAAE;CAChC,MAAM,UAAU,MAAM,OAAO,KAAK,WAAW,EAAE,QAAQ,UAAU,CAAC;CAClE,IAAI,QAAQ,WAAW,GACrB,OAAO,CAAC;CAEV,MAAM,MAAM,MAAM,OAAO,KAAK,SAAS;CAWvC,QAAO,MAVa,QAAQ,IAC1B,QAAQ,IAAI,OAAO,UAA4C;EAC7D,MAAM,WAAW,MAAM,WAAW,IAAI,KAAK;EAC3C,IAAI,SAAS,QAAQ,QACnB,OAAO;EAET,MAAM,aAAa,CAAC,SAAS,WAAW,iBAAiB,KAAK,KAAK;EACnE,OAAO;GAAE,SAAS,MAAM;GAAI,QAAQ,aAAa,eAAe;EAAO;CACzE,CAAC,CACH,EAAA,CACa,QAAQ,cAAc,cAAc,IAAI;AACvD;AASA,eAAe,WAAW,IAAqB,OAAiC;CAC9E,MAAM,CAAC,UAAU,WAAW,MAAM,QAAQ,IAAI,CAC5C,IAAI,aAAa,EAAE,CAAC,CAAC,YAAY,MAAM,EAAE,GACzC,IAAI,iBAAiB,EAAE,CAAC,CAAC,YAAY,MAAM,EAAE,CAC/C,CAAC;CAMD,OAAO;EACL,MAAM;GALN,MAAM;GACN,GAAG,SAAS,KAAK,SAAS,QAAQ,IAAI,CAAC;GACvC,GAAG,QAAQ,KAAK,SAAS,QAAQ,IAAI,CAAC;EAG3B,CAAC,CAAC,SAAS,CAAC,CAAC,GAAG,EAAE,KAAK,MAAM;EACxC,SAAS,SAAS,SAAS,KAAK,QAAQ,SAAS;CACnD;AACF;AAEA,SAAS,QAAQ,MAAqC;CACpD,OAAO,KAAK;AACd;AAEA,SAAS,iBAAiB,KAAuB,OAAuB;CACtE,OAAO,IAAI,MACR,UACC,MAAM,OAAO,MAAM,MACnB,MAAM,cAAc,MAAM,aAC1B,MAAM,YAAY,MAAM,SAC5B;AACF"}
@@ -0,0 +1,51 @@
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 { Logger } from "pino";
6
+ import { Table } from "drizzle-orm";
7
+ //#region src/retention/screenshot-sweep.d.ts
8
+ /** Longest an unreferenced screenshot is kept after the sweep first sees it (default 24 h). */
9
+ declare const SWEEP_GRACE_MS: number;
10
+ /** Tables the sweep reads references and in-flight builds from. */
11
+ interface SweepTables {
12
+ snapshots: Table;
13
+ baselines: Table;
14
+ builds: Table;
15
+ }
16
+ /** Inputs for {@link sweepScreenshotContent}. */
17
+ interface SweepDeps {
18
+ db: DatabaseAdapter;
19
+ storage: StorageAdapter;
20
+ tables: SweepTables;
21
+ logger?: Logger;
22
+ }
23
+ /** What one sweep did. */
24
+ interface SweepResult {
25
+ /** Objects deleted. */
26
+ removed: number;
27
+ /** Unreferenced objects still inside their grace period. */
28
+ pending: number;
29
+ }
30
+ /**
31
+ * Delete content-addressed screenshots that no snapshot or baseline points at any more.
32
+ *
33
+ * An object is deleted only after it has been unreferenced across two sweeps at least `graceMs`
34
+ * apart (first-seen times live in a marker object, so no schema is needed and the storage
35
+ * adapter needs no modification times). A capture writes its screenshot before the rows that
36
+ * reference it, so a single look is never enough; each candidate is also re-checked against the
37
+ * database immediately before it is deleted.
38
+ *
39
+ * Reusing an object that already exists writes nothing, so the sweep cannot see it by looking at
40
+ * storage. Deletion is therefore deferred (objects stay pending, first-seen times kept) while the
41
+ * project has reuse in flight: a build still capturing, or a reuse claim from a baseline import or
42
+ * approval ({@link withContentClaim}). Both checks are bounded by the grace period, so a build
43
+ * stuck in `capturing` or a claim left by a crash delays reclaiming storage, never forever.
44
+ */
45
+ declare function sweepScreenshotContent(deps: SweepDeps, projectId: string, options?: {
46
+ now?: number;
47
+ graceMs?: number;
48
+ }): Promise<SweepResult>;
49
+ //#endregion
50
+ export { SWEEP_GRACE_MS, SweepDeps, SweepResult, SweepTables, sweepScreenshotContent };
51
+ //# sourceMappingURL=screenshot-sweep.d.mts.map