@storyshelf/core 0.7.0 → 0.8.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 (531) hide show
  1. package/dist/adapter/capture-queue.d.mts +0 -2
  2. package/dist/adapter/capture-runner.d.mts +9 -4
  3. package/dist/adapter/capture-runner.mjs +6 -2
  4. package/dist/adapter/database.d.mts +63 -3
  5. package/dist/adapter/database.mjs +4 -3
  6. package/dist/adapter/database.mjs.map +1 -1
  7. package/dist/adapter/design.d.mts +0 -2
  8. package/dist/adapter/design.mjs +1 -2
  9. package/dist/adapter/email-sender.d.mts +0 -2
  10. package/dist/adapter/metadata.d.mts +0 -2
  11. package/dist/adapter/oauth.d.mts +0 -2
  12. package/dist/adapter/setup.d.mts +0 -2
  13. package/dist/adapter/setup.mjs +1 -2
  14. package/dist/adapter/storage.d.mts +1 -3
  15. package/dist/adapter/webhook-events.d.mts +0 -2
  16. package/dist/adapter/webhook-events.mjs +12 -13
  17. package/dist/adapter/webhook-events.mjs.map +1 -1
  18. package/dist/adapters/capture-target-offers.d.mts +0 -2
  19. package/dist/adapters/capture-target-offers.mjs +1 -2
  20. package/dist/adapters/capture-target.d.mts +0 -2
  21. package/dist/adapters/capture-target.mjs +1 -2
  22. package/dist/adapters/notifier/build-comment.d.mts +0 -2
  23. package/dist/adapters/notifier/build-comment.mjs +1 -2
  24. package/dist/adapters/notifier/build-state.d.mts +0 -2
  25. package/dist/adapters/notifier/build-state.mjs +1 -2
  26. package/dist/adapters/notifier/events.d.mts +0 -2
  27. package/dist/adapters/notifier/events.mjs +1 -2
  28. package/dist/adapters/notifier/format.d.mts +0 -2
  29. package/dist/adapters/notifier/format.mjs +1 -2
  30. package/dist/adapters/notifier/provider.d.mts +0 -2
  31. package/dist/adapters/notifier/provider.mjs +1 -2
  32. package/dist/adapters/notifier/state.d.mts +0 -2
  33. package/dist/adapters/notifier/state.mjs +1 -2
  34. package/dist/adapters/notifier/types.d.mts +0 -2
  35. package/dist/adapters/remote-browser.d.mts +112 -31
  36. package/dist/adapters/remote-browser.mjs +132 -34
  37. package/dist/adapters/remote-browser.mjs.map +1 -1
  38. package/dist/adapters/remote-lease-attempt.mjs +39 -0
  39. package/dist/adapters/remote-lease-attempt.mjs.map +1 -0
  40. package/dist/adapters/remote-lease-error.d.mts +25 -0
  41. package/dist/adapters/remote-lease-error.mjs +54 -0
  42. package/dist/adapters/remote-lease-error.mjs.map +1 -0
  43. package/dist/adapters/remote-lease-held.d.mts +15 -0
  44. package/dist/adapters/remote-lease-held.mjs +86 -0
  45. package/dist/adapters/remote-lease-held.mjs.map +1 -0
  46. package/dist/adapters/remote-lease-shutdown.d.mts +12 -0
  47. package/dist/adapters/remote-lease-shutdown.mjs +32 -0
  48. package/dist/adapters/remote-lease-shutdown.mjs.map +1 -0
  49. package/dist/adapters/remote-lease.d.mts +39 -0
  50. package/dist/adapters/remote-lease.mjs +175 -0
  51. package/dist/adapters/remote-lease.mjs.map +1 -0
  52. package/dist/adapters/remote-limiter.mjs +121 -0
  53. package/dist/adapters/remote-limiter.mjs.map +1 -0
  54. package/dist/adapters/remote-pool.d.mts +56 -0
  55. package/dist/adapters/remote-pool.mjs +195 -0
  56. package/dist/adapters/remote-pool.mjs.map +1 -0
  57. package/dist/adapters/render-runs.d.mts +0 -2
  58. package/dist/adapters/render-runs.mjs +1 -2
  59. package/dist/adapters/setup-runner.d.mts +0 -2
  60. package/dist/adapters/setup-runner.mjs +1 -2
  61. package/dist/adapters/sources.d.mts +0 -2
  62. package/dist/adapters/sources.mjs +1 -2
  63. package/dist/adapters/validate.d.mts +0 -2
  64. package/dist/adapters/validate.mjs +2 -2
  65. package/dist/adapters/validate.mjs.map +1 -1
  66. package/dist/ai.d.mts +0 -2
  67. package/dist/ai.mjs +1 -2
  68. package/dist/auth.d.mts +0 -2
  69. package/dist/auth.mjs +1 -2
  70. package/dist/brand-packs.d.mts +0 -2
  71. package/dist/brand-packs.mjs +0 -1
  72. package/dist/capture/a11y/axe-source.d.mts +0 -2
  73. package/dist/capture/a11y/axe-source.mjs +1 -2
  74. package/dist/capture/a11y/in-page.d.mts +1 -3
  75. package/dist/capture/a11y/in-page.mjs +3 -4
  76. package/dist/capture/a11y/in-page.mjs.map +1 -1
  77. package/dist/capture/a11y/map.d.mts +0 -2
  78. package/dist/capture/a11y/map.mjs +1 -2
  79. package/dist/capture/a11y/options.d.mts +0 -2
  80. package/dist/capture/a11y/options.mjs +1 -2
  81. package/dist/capture/adapter.d.mts +5 -2
  82. package/dist/capture/adapter.mjs +1 -2
  83. package/dist/capture/adapter.mjs.map +1 -1
  84. package/dist/capture/affected.d.mts +0 -2
  85. package/dist/capture/affected.mjs +1 -2
  86. package/dist/capture/attempt-logger.mjs +1 -2
  87. package/dist/capture/browsers.d.mts +0 -2
  88. package/dist/capture/browsers.mjs +1 -2
  89. package/dist/capture/build-state/deliver.d.mts +0 -2
  90. package/dist/capture/build-state/deliver.mjs +1 -2
  91. package/dist/capture/build-state/notify-job.d.mts +0 -2
  92. package/dist/capture/build-state/notify-job.mjs +1 -2
  93. package/dist/capture/build-state/publisher.d.mts +0 -2
  94. package/dist/capture/build-state/publisher.mjs +1 -2
  95. package/dist/capture/build-state/resume.d.mts +0 -2
  96. package/dist/capture/build-state/resume.mjs +1 -2
  97. package/dist/capture/build-state/types.d.mts +1 -3
  98. package/dist/capture/catalog/catalogue.d.mts +0 -2
  99. package/dist/capture/catalog/catalogue.mjs +1 -2
  100. package/dist/capture/catalog/components.d.mts +0 -2
  101. package/dist/capture/catalog/components.mjs +1 -2
  102. package/dist/capture/catalog/history.d.mts +0 -2
  103. package/dist/capture/catalog/history.mjs +1 -2
  104. package/dist/capture/catalog/hook.d.mts +0 -2
  105. package/dist/capture/catalog/hook.mjs +1 -2
  106. package/dist/capture/catalog/index.d.mts +2 -2
  107. package/dist/capture/catalog/load.d.mts +0 -2
  108. package/dist/capture/catalog/load.mjs +1 -2
  109. package/dist/capture/catalog/parse.d.mts +0 -2
  110. package/dist/capture/catalog/parse.mjs +1 -2
  111. package/dist/capture/catalog/read.d.mts +6 -3
  112. package/dist/capture/catalog/read.mjs +14 -8
  113. package/dist/capture/catalog/read.mjs.map +1 -1
  114. package/dist/capture/catalog/types.d.mts +0 -2
  115. package/dist/capture/comparison-reference.d.mts +27 -0
  116. package/dist/capture/comparison-reference.mjs +75 -0
  117. package/dist/capture/comparison-reference.mjs.map +1 -0
  118. package/dist/capture/consumer/capture-handler.d.mts +1 -3
  119. package/dist/capture/consumer/capture-handler.mjs +1 -2
  120. package/dist/capture/consumer/idle.d.mts +0 -2
  121. package/dist/capture/consumer/idle.mjs +1 -2
  122. package/dist/capture/consumer/index.d.mts +0 -2
  123. package/dist/capture/consumer/index.mjs +1 -2
  124. package/dist/capture/consumer/loop.mjs +1 -2
  125. package/dist/capture/consumer/metrics.d.mts +0 -2
  126. package/dist/capture/consumer/metrics.mjs +1 -2
  127. package/dist/capture/consumer/retry.d.mts +0 -2
  128. package/dist/capture/consumer/retry.mjs +1 -2
  129. package/dist/capture/consumer/run-job.mjs +1 -2
  130. package/dist/capture/consumer/startup.mjs +1 -2
  131. package/dist/capture/consumer/types.d.mts +0 -2
  132. package/dist/capture/crash-failure.d.mts +0 -2
  133. package/dist/capture/crash-failure.mjs +1 -2
  134. package/dist/capture/db-queue/cadence.d.mts +0 -2
  135. package/dist/capture/db-queue/cadence.mjs +1 -2
  136. package/dist/capture/db-queue/claim.d.mts +0 -2
  137. package/dist/capture/db-queue/claim.mjs +20 -4
  138. package/dist/capture/db-queue/claim.mjs.map +1 -1
  139. package/dist/capture/db-queue/clock.mjs +1 -2
  140. package/dist/capture/db-queue/codec.mjs +1 -2
  141. package/dist/capture/db-queue/index.d.mts +0 -2
  142. package/dist/capture/db-queue/index.mjs +3 -3
  143. package/dist/capture/db-queue/index.mjs.map +1 -1
  144. package/dist/capture/db-queue/keeper.d.mts +0 -2
  145. package/dist/capture/db-queue/keeper.mjs +1 -2
  146. package/dist/capture/db-queue/lease.mjs +1 -2
  147. package/dist/capture/db-queue/lifecycle.mjs +1 -2
  148. package/dist/capture/db-queue/operations.mjs +1 -2
  149. package/dist/capture/db-queue/poll-loop.d.mts +0 -2
  150. package/dist/capture/db-queue/poll-loop.mjs +1 -2
  151. package/dist/capture/db-queue/poll.d.mts +0 -2
  152. package/dist/capture/db-queue/poll.mjs +1 -2
  153. package/dist/capture/db-queue/timers.d.mts +0 -2
  154. package/dist/capture/db-queue/timers.mjs +1 -2
  155. package/dist/capture/db-queue/types.d.mts +0 -2
  156. package/dist/capture/db-queue/types.mjs +1 -2
  157. package/dist/capture/db-queue/types.mjs.map +1 -1
  158. package/dist/capture/db-queue/wake.mjs +1 -2
  159. package/dist/capture/design.mjs +1 -2
  160. package/dist/capture/diff-record.mjs +1 -2
  161. package/dist/capture/dispatch.d.mts +1 -3
  162. package/dist/capture/dispatch.mjs +17 -5
  163. package/dist/capture/dispatch.mjs.map +1 -1
  164. package/dist/capture/enrich.mjs +1 -2
  165. package/dist/capture/failure-sets.mjs +1 -2
  166. package/dist/capture/fingerprint.d.mts +0 -2
  167. package/dist/capture/fingerprint.mjs +1 -2
  168. package/dist/capture/forked/channel.d.mts +0 -2
  169. package/dist/capture/forked/child.d.mts +0 -2
  170. package/dist/capture/forked/child.mjs +1 -2
  171. package/dist/capture/forked/crash-tracker.mjs +1 -2
  172. package/dist/capture/forked/decision.d.mts +0 -2
  173. package/dist/capture/forked/decision.mjs +1 -2
  174. package/dist/capture/forked/exit.mjs +1 -2
  175. package/dist/capture/forked/fork.d.mts +0 -2
  176. package/dist/capture/forked/fork.mjs +1 -2
  177. package/dist/capture/forked/forked-capture.d.mts +0 -2
  178. package/dist/capture/forked/forked-capture.mjs +1 -2
  179. package/dist/capture/forked/lease-book.mjs +1 -2
  180. package/dist/capture/forked/protocol.d.mts +0 -2
  181. package/dist/capture/forked/protocol.mjs +1 -2
  182. package/dist/capture/forked/runs.mjs +1 -2
  183. package/dist/capture/guards.mjs +1 -2
  184. package/dist/capture/ignore-regions.d.mts +36 -0
  185. package/dist/capture/ignore-regions.mjs +65 -0
  186. package/dist/capture/ignore-regions.mjs.map +1 -0
  187. package/dist/capture/isolate.mjs +1 -2
  188. package/dist/capture/job-lease.d.mts +0 -2
  189. package/dist/capture/job-lease.mjs +1 -2
  190. package/dist/capture/matrix.mjs +4 -4
  191. package/dist/capture/matrix.mjs.map +1 -1
  192. package/dist/capture/modes.d.mts +0 -2
  193. package/dist/capture/modes.mjs +2 -3
  194. package/dist/capture/modes.mjs.map +1 -1
  195. package/dist/capture/orchestrator.d.mts +0 -2
  196. package/dist/capture/orchestrator.mjs +2 -3
  197. package/dist/capture/pipeline.d.mts +0 -2
  198. package/dist/capture/pipeline.mjs +37 -6
  199. package/dist/capture/pipeline.mjs.map +1 -1
  200. package/dist/capture/progress.d.mts +0 -2
  201. package/dist/capture/progress.mjs +1 -2
  202. package/dist/capture/publish/accept.d.mts +0 -2
  203. package/dist/capture/publish/accept.mjs +1 -2
  204. package/dist/capture/publish/complete.d.mts +1 -3
  205. package/dist/capture/publish/complete.mjs +1 -2
  206. package/dist/capture/publish/extract.d.mts +0 -2
  207. package/dist/capture/publish/extract.mjs +6 -5
  208. package/dist/capture/publish/extract.mjs.map +1 -1
  209. package/dist/capture/publish/handler.d.mts +1 -3
  210. package/dist/capture/publish/handler.mjs +1 -2
  211. package/dist/capture/publish/index.d.mts +2 -1
  212. package/dist/capture/publish/invalid-zip.d.mts +8 -0
  213. package/dist/capture/publish/invalid-zip.mjs +11 -0
  214. package/dist/capture/publish/invalid-zip.mjs.map +1 -0
  215. package/dist/capture/publish/limits.d.mts +0 -2
  216. package/dist/capture/publish/limits.mjs +1 -2
  217. package/dist/capture/publish/published.d.mts +0 -2
  218. package/dist/capture/publish/published.mjs +1 -2
  219. package/dist/capture/publish/run.d.mts +1 -3
  220. package/dist/capture/publish/run.mjs +1 -2
  221. package/dist/capture/rediff.d.mts +1 -3
  222. package/dist/capture/rediff.mjs +19 -6
  223. package/dist/capture/rediff.mjs.map +1 -1
  224. package/dist/capture/render.mjs +1 -2
  225. package/dist/capture/render.mjs.map +1 -1
  226. package/dist/capture/scratch.d.mts +0 -2
  227. package/dist/capture/scratch.mjs +2 -4
  228. package/dist/capture/scratch.mjs.map +1 -1
  229. package/dist/capture/shard-plan.mjs +1 -2
  230. package/dist/capture/shards/context.mjs +3 -3
  231. package/dist/capture/shards/context.mjs.map +1 -1
  232. package/dist/capture/shards/execute.mjs +4 -3
  233. package/dist/capture/shards/execute.mjs.map +1 -1
  234. package/dist/capture/shards/fan-out.mjs +1 -2
  235. package/dist/capture/shards/history.mjs +1 -2
  236. package/dist/capture/shards/reduce.mjs +1 -2
  237. package/dist/capture/sizing.d.mts +0 -2
  238. package/dist/capture/sizing.mjs +1 -2
  239. package/dist/capture/skip-checks.mjs +1 -2
  240. package/dist/capture/statics.d.mts +0 -2
  241. package/dist/capture/statics.mjs +2 -4
  242. package/dist/capture/statics.mjs.map +1 -1
  243. package/dist/capture/storybook-urls.d.mts +0 -2
  244. package/dist/capture/storybook-urls.mjs +3 -3
  245. package/dist/capture/storybook-urls.mjs.map +1 -1
  246. package/dist/capture/storybook.d.mts +0 -2
  247. package/dist/capture/storybook.mjs +8 -4
  248. package/dist/capture/storybook.mjs.map +1 -1
  249. package/dist/capture/target.mjs +1 -2
  250. package/dist/capture/targets.d.mts +0 -2
  251. package/dist/capture/targets.mjs +1 -2
  252. package/dist/capture/tests.d.mts +0 -2
  253. package/dist/capture/tests.mjs +13 -3
  254. package/dist/capture/tests.mjs.map +1 -1
  255. package/dist/capture/token-chunks.mjs +24 -7
  256. package/dist/capture/token-chunks.mjs.map +1 -1
  257. package/dist/capture.d.mts +5 -2
  258. package/dist/capture.mjs +5 -2
  259. package/dist/config.d.mts +16 -3
  260. package/dist/config.mjs +6 -3
  261. package/dist/config.mjs.map +1 -1
  262. package/dist/diff.d.mts +0 -2
  263. package/dist/diff.mjs +1 -2
  264. package/dist/index.d.mts +2 -2
  265. package/dist/index.mjs +2 -2
  266. package/dist/insights/budget.d.mts +0 -2
  267. package/dist/insights/budget.mjs +1 -2
  268. package/dist/insights/envelope.d.mts +0 -2
  269. package/dist/insights/envelope.mjs +1 -2
  270. package/dist/insights/evidence-health.d.mts +0 -2
  271. package/dist/insights/evidence-health.mjs +1 -2
  272. package/dist/insights/evidence.d.mts +0 -2
  273. package/dist/insights/evidence.mjs +1 -2
  274. package/dist/insights/hash.d.mts +0 -2
  275. package/dist/insights/hash.mjs +1 -2
  276. package/dist/insights/limits.d.mts +0 -2
  277. package/dist/insights/limits.mjs +1 -2
  278. package/dist/insights/prompts/v1.d.mts +0 -2
  279. package/dist/insights/prompts/v1.mjs +1 -2
  280. package/dist/insights/redact.d.mts +0 -2
  281. package/dist/insights/redact.mjs +1 -2
  282. package/dist/insights/text.mjs +1 -2
  283. package/dist/logger.d.mts +0 -2
  284. package/dist/logger.mjs +1 -2
  285. package/dist/models/ai-budget-alert.d.mts +0 -2
  286. package/dist/models/ai-budget-alert.mjs +1 -2
  287. package/dist/models/ai-usage.d.mts +0 -2
  288. package/dist/models/ai-usage.mjs +1 -2
  289. package/dist/models/baseline-approval.d.mts +18 -0
  290. package/dist/models/baseline-approval.mjs +41 -0
  291. package/dist/models/baseline-approval.mjs.map +1 -0
  292. package/dist/models/baseline-archive.d.mts +0 -2
  293. package/dist/models/baseline-archive.mjs +1 -2
  294. package/dist/models/baseline-atomic.mjs +66 -0
  295. package/dist/models/baseline-atomic.mjs.map +1 -0
  296. package/dist/models/baseline-conflict.d.mts +6 -0
  297. package/dist/models/baseline-conflict.mjs +9 -0
  298. package/dist/models/baseline-conflict.mjs.map +1 -0
  299. package/dist/models/baseline-export.d.mts +0 -2
  300. package/dist/models/baseline-export.mjs +1 -2
  301. package/dist/models/baseline-import.mjs +1 -2
  302. package/dist/models/baseline-origin.d.mts +16 -0
  303. package/dist/models/baseline-origin.mjs +35 -0
  304. package/dist/models/baseline-origin.mjs.map +1 -0
  305. package/dist/models/baseline-status.d.mts +0 -2
  306. package/dist/models/baseline-status.mjs +1 -2
  307. package/dist/models/baseline-transfer.d.mts +1 -2
  308. package/dist/models/baseline-transfer.mjs +30 -2
  309. package/dist/models/baseline-transfer.mjs.map +1 -1
  310. package/dist/models/baseline.d.mts +5 -2
  311. package/dist/models/baseline.mjs +91 -12
  312. package/dist/models/baseline.mjs.map +1 -1
  313. package/dist/models/build-component.d.mts +0 -2
  314. package/dist/models/build-component.mjs +1 -2
  315. package/dist/models/build-review-status.d.mts +0 -2
  316. package/dist/models/build-review-status.mjs +1 -2
  317. package/dist/models/build-review.d.mts +0 -2
  318. package/dist/models/build-review.mjs +1 -2
  319. package/dist/models/build.d.mts +1 -3
  320. package/dist/models/build.mjs +14 -15
  321. package/dist/models/build.mjs.map +1 -1
  322. package/dist/models/capture-attempt.d.mts +0 -2
  323. package/dist/models/capture-attempt.mjs +1 -2
  324. package/dist/models/capture-job.d.mts +0 -2
  325. package/dist/models/capture-job.mjs +1 -2
  326. package/dist/models/capture-log.d.mts +0 -2
  327. package/dist/models/capture-log.mjs +1 -2
  328. package/dist/models/capture-shard.d.mts +0 -2
  329. package/dist/models/capture-shard.mjs +1 -2
  330. package/dist/models/changeset.d.mts +0 -2
  331. package/dist/models/changeset.mjs +1 -2
  332. package/dist/models/columns.mjs +1 -2
  333. package/dist/models/comment.d.mts +0 -2
  334. package/dist/models/comment.mjs +1 -2
  335. package/dist/models/component.d.mts +0 -2
  336. package/dist/models/component.mjs +1 -2
  337. package/dist/models/connections.d.mts +0 -2
  338. package/dist/models/connections.mjs +1 -2
  339. package/dist/models/content-ref.d.mts +0 -2
  340. package/dist/models/content-ref.mjs +1 -2
  341. package/dist/models/credentials.d.mts +0 -2
  342. package/dist/models/credentials.mjs +1 -2
  343. package/dist/models/design-credentials.d.mts +0 -2
  344. package/dist/models/design-credentials.mjs +1 -2
  345. package/dist/models/design-health.d.mts +0 -2
  346. package/dist/models/design-health.mjs +1 -2
  347. package/dist/models/design-nodes.d.mts +2 -4
  348. package/dist/models/design-nodes.mjs +1 -2
  349. package/dist/models/design-renders.mjs +1 -2
  350. package/dist/models/design-resolve.d.mts +0 -2
  351. package/dist/models/design-resolve.mjs +1 -2
  352. package/dist/models/design.d.mts +0 -2
  353. package/dist/models/design.mjs +1 -2
  354. package/dist/models/inherited-ratio.d.mts +0 -2
  355. package/dist/models/inherited-ratio.mjs +1 -2
  356. package/dist/models/insight.d.mts +0 -2
  357. package/dist/models/insight.mjs +1 -2
  358. package/dist/models/label.d.mts +0 -2
  359. package/dist/models/label.mjs +1 -2
  360. package/dist/models/member.d.mts +0 -2
  361. package/dist/models/member.mjs +1 -2
  362. package/dist/models/notification-channel.d.mts +0 -2
  363. package/dist/models/notification-channel.mjs +1 -2
  364. package/dist/models/notification-subscription.d.mts +0 -2
  365. package/dist/models/notification-subscription.mjs +1 -2
  366. package/dist/models/project-group-mapping.d.mts +0 -2
  367. package/dist/models/project-group-mapping.mjs +1 -2
  368. package/dist/models/project.d.mts +0 -2
  369. package/dist/models/project.mjs +1 -2
  370. package/dist/models/public-origin.d.mts +0 -2
  371. package/dist/models/public-origin.mjs +1 -2
  372. package/dist/models/review-switch-off.d.mts +0 -2
  373. package/dist/models/review-switch-off.mjs +1 -2
  374. package/dist/models/site-settings.d.mts +0 -2
  375. package/dist/models/site-settings.mjs +1 -2
  376. package/dist/models/snapshot.d.mts +3 -2
  377. package/dist/models/snapshot.mjs +4 -2
  378. package/dist/models/snapshot.mjs.map +1 -1
  379. package/dist/models/token.d.mts +0 -2
  380. package/dist/models/token.mjs +1 -2
  381. package/dist/models/usage.d.mts +0 -2
  382. package/dist/models/usage.mjs +1 -2
  383. package/dist/models/user.d.mts +0 -2
  384. package/dist/models/user.mjs +1 -2
  385. package/dist/models/webhook.d.mts +0 -2
  386. package/dist/models/webhook.mjs +1 -2
  387. package/dist/models.d.mts +5 -2
  388. package/dist/models.mjs +5 -2
  389. package/dist/options.d.mts +0 -2
  390. package/dist/options.mjs +2 -4
  391. package/dist/options.mjs.map +1 -1
  392. package/dist/paths.d.mts +3 -3
  393. package/dist/paths.mjs +7 -3
  394. package/dist/paths.mjs.map +1 -1
  395. package/dist/retention/ai.d.mts +1 -3
  396. package/dist/retention/ai.mjs +0 -1
  397. package/dist/retention/pending.mjs +1 -2
  398. package/dist/retention/published-gzip-sweep.mjs +28 -0
  399. package/dist/retention/published-gzip-sweep.mjs.map +1 -0
  400. package/dist/retention/screenshot-sweep.d.mts +0 -2
  401. package/dist/retention/screenshot-sweep.mjs +1 -2
  402. package/dist/retention.d.mts +0 -2
  403. package/dist/retention.mjs +5 -2
  404. package/dist/retention.mjs.map +1 -1
  405. package/dist/schema/baseline.d.mts +2 -2
  406. package/dist/schema/build-component.d.mts +0 -2
  407. package/dist/schema/build-review.d.mts +0 -2
  408. package/dist/schema/build.d.mts +0 -2
  409. package/dist/schema/capture-attempt.d.mts +0 -2
  410. package/dist/schema/capture-job.d.mts +0 -2
  411. package/dist/schema/capture-shard.d.mts +0 -2
  412. package/dist/schema/comment.d.mts +0 -2
  413. package/dist/schema/connection.d.mts +0 -2
  414. package/dist/schema/content-ref.d.mts +0 -2
  415. package/dist/schema/design.d.mts +0 -2
  416. package/dist/schema/insight.d.mts +0 -2
  417. package/dist/schema/label.d.mts +0 -2
  418. package/dist/schema/member.d.mts +0 -2
  419. package/dist/schema/notification.d.mts +0 -2
  420. package/dist/schema/project-group-mapping.d.mts +0 -2
  421. package/dist/schema/project.d.mts +0 -2
  422. package/dist/schema/site-setting.d.mts +0 -2
  423. package/dist/schema/snapshot.d.mts +5 -2
  424. package/dist/schema/token.d.mts +0 -2
  425. package/dist/schema/usage.d.mts +0 -2
  426. package/dist/schema/user.d.mts +0 -2
  427. package/dist/schema/webhook.d.mts +0 -2
  428. package/dist/schema.d.mts +4 -6
  429. package/dist/serverless.d.mts +0 -2
  430. package/dist/serverless.mjs +1 -2
  431. package/dist/test-helpers/build-reference-contract.d.mts +14 -0
  432. package/dist/test-helpers/build-reference-contract.mjs +66 -0
  433. package/dist/test-helpers/build-reference-contract.mjs.map +1 -0
  434. package/dist/test-helpers/create-project.d.mts +0 -2
  435. package/dist/test-helpers/create-project.mjs +1 -2
  436. package/dist/test-helpers/database-contract.d.mts +0 -2
  437. package/dist/test-helpers/database-contract.mjs +37 -2
  438. package/dist/test-helpers/database-contract.mjs.map +1 -1
  439. package/dist/test-helpers/deferred.d.mts +0 -2
  440. package/dist/test-helpers/deferred.mjs +1 -2
  441. package/dist/test-helpers/design-contract.d.mts +0 -2
  442. package/dist/test-helpers/design-contract.mjs +1 -2
  443. package/dist/test-helpers/fake-adapters.d.mts +0 -2
  444. package/dist/test-helpers/fake-adapters.mjs +1 -2
  445. package/dist/test-helpers/fake-database.d.mts +0 -2
  446. package/dist/test-helpers/fake-database.mjs +7 -2
  447. package/dist/test-helpers/fake-database.mjs.map +1 -1
  448. package/dist/test-helpers/fake-design.d.mts +0 -2
  449. package/dist/test-helpers/fake-design.mjs +1 -2
  450. package/dist/test-helpers/fake-notifier.d.mts +0 -2
  451. package/dist/test-helpers/fake-notifier.mjs +1 -2
  452. package/dist/test-helpers/fake-storage.d.mts +0 -2
  453. package/dist/test-helpers/fake-storage.mjs +1 -2
  454. package/dist/test-helpers/fake-tables.mjs +5 -2
  455. package/dist/test-helpers/fake-tables.mjs.map +1 -1
  456. package/dist/test-helpers/manual-clock.d.mts +0 -2
  457. package/dist/test-helpers/manual-clock.mjs +1 -2
  458. package/dist/test-helpers/notifier-contract.d.mts +0 -2
  459. package/dist/test-helpers/notifier-contract.mjs +1 -2
  460. package/dist/test-helpers/queue-contract.d.mts +0 -2
  461. package/dist/test-helpers/queue-contract.mjs +1 -2
  462. package/dist/test-helpers/queue-multi-instance.d.mts +0 -2
  463. package/dist/test-helpers/queue-multi-instance.mjs +1 -2
  464. package/dist/test-helpers/runner-contract.d.mts +0 -2
  465. package/dist/test-helpers/runner-contract.mjs +1 -2
  466. package/dist/test-helpers/sql-chunks.mjs +2 -2
  467. package/dist/test-helpers/sql-chunks.mjs.map +1 -1
  468. package/dist/test-helpers/storage-contract.d.mts +0 -2
  469. package/dist/test-helpers/storage-contract.mjs +1 -2
  470. package/dist/test-helpers.d.mts +2 -1
  471. package/dist/test-helpers.mjs +2 -1
  472. package/dist/tracing.mjs +36 -6
  473. package/dist/tracing.mjs.map +1 -1
  474. package/dist/types.d.mts +0 -2
  475. package/dist/types.mjs +1 -2
  476. package/dist/urls.d.mts +0 -2
  477. package/dist/urls.mjs +1 -2
  478. package/dist/utils/adaptive-concurrency.d.mts +0 -2
  479. package/dist/utils/adaptive-concurrency.mjs +1 -2
  480. package/dist/utils/connect-retry.d.mts +2 -2
  481. package/dist/utils/connect-retry.mjs +2 -3
  482. package/dist/utils/connect-retry.mjs.map +1 -1
  483. package/dist/utils/encrypt.d.mts +0 -2
  484. package/dist/utils/encrypt.mjs +1 -2
  485. package/dist/utils/environment.d.mts +0 -2
  486. package/dist/utils/environment.mjs +1 -2
  487. package/dist/utils/hash.d.mts +0 -2
  488. package/dist/utils/hash.mjs +1 -2
  489. package/dist/utils/http.d.mts +13 -3
  490. package/dist/utils/http.mjs +79 -17
  491. package/dist/utils/http.mjs.map +1 -1
  492. package/dist/utils/map-adaptive.d.mts +0 -2
  493. package/dist/utils/map-adaptive.mjs +1 -2
  494. package/dist/utils/map-limit.d.mts +0 -2
  495. package/dist/utils/map-limit.mjs +1 -2
  496. package/dist/utils/mime.d.mts +0 -2
  497. package/dist/utils/mime.mjs +1 -2
  498. package/dist/utils/page-limiter.d.mts +0 -2
  499. package/dist/utils/page-limiter.mjs +1 -2
  500. package/dist/utils/path-security.d.mts +0 -2
  501. package/dist/utils/path-security.mjs +2 -4
  502. package/dist/utils/path-security.mjs.map +1 -1
  503. package/dist/utils/public-origin.d.mts +0 -2
  504. package/dist/utils/public-origin.mjs +1 -2
  505. package/dist/utils/redact.d.mts +0 -2
  506. package/dist/utils/redact.mjs +1 -2
  507. package/dist/utils/remote-environment.d.mts +40 -3
  508. package/dist/utils/remote-environment.mjs +52 -3
  509. package/dist/utils/remote-environment.mjs.map +1 -1
  510. package/dist/utils/resources.d.mts +0 -2
  511. package/dist/utils/resources.mjs +1 -2
  512. package/dist/utils/screenshot-store.d.mts +0 -2
  513. package/dist/utils/screenshot-store.mjs +1 -2
  514. package/dist/utils/session-gate.d.mts +7 -6
  515. package/dist/utils/session-gate.mjs +28 -12
  516. package/dist/utils/session-gate.mjs.map +1 -1
  517. package/dist/utils/storybook-token.d.mts +11 -3
  518. package/dist/utils/storybook-token.mjs +10 -9
  519. package/dist/utils/storybook-token.mjs.map +1 -1
  520. package/dist/utils/storybook-url.d.mts +0 -2
  521. package/dist/utils/storybook-url.mjs +1 -2
  522. package/dist/utils/timing.d.mts +0 -2
  523. package/dist/utils/timing.mjs +1 -2
  524. package/dist/utils/ulid.d.mts +0 -2
  525. package/dist/utils/ulid.mjs +1 -2
  526. package/dist/utils/zip-read.mjs +1 -2
  527. package/dist/utils/zip-write.d.mts +0 -2
  528. package/dist/utils/zip-write.mjs +1 -2
  529. package/dist/utils.d.mts +5 -5
  530. package/dist/utils.mjs +5 -5
  531. package/package.json +6 -6
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.mjs","names":[],"sources":["../../src/capture/pipeline.ts"],"sourcesContent":["import type { Logger } from \"pino\";\nimport { DEFAULT_BROWSER, type RenderedSnapshot } from \"../adapters/capture-runner.ts\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { StorageAdapter } from \"../adapters/storage.ts\";\nimport { NO_BASELINE } from \"../models/baseline-status.ts\";\nimport { BaselineModel, type BaselineTables } from \"../models/baseline.ts\";\nimport { BuildModel, type BuildTables } from \"../models/build.ts\";\nimport { emitAttemptLog, type AttemptLogRecorder } from \"../models/capture-attempt.ts\";\nimport { SnapshotModel, type SnapshotTables } from \"../models/snapshot.ts\";\nimport type { Baseline } from \"../schema/baseline.ts\";\nimport type { Build } from \"../schema/build.ts\";\nimport type { Project } from \"../schema/project.ts\";\nimport type { BuildStatus, SnapshotStatus } from \"../types.ts\";\nimport type { SecretInput } from \"../utils/encrypt.ts\";\nimport { mapLimit } from \"../utils/map-limit.ts\";\nimport { diffPath } from \"../utils/paths.ts\";\nimport { storeScreenshot } from \"../utils/screenshot-store.ts\";\nimport type { Viewport } from \"./adapter.ts\";\nimport type { InheritedStory } from \"./affected.ts\";\nimport { diffStoredScreenshots, writeDiffOverlay } from \"./diff-record.ts\";\nimport {\n buildFingerprint,\n changedFields,\n fingerprintHash,\n mergeFingerprint,\n parseFingerprint,\n type EnvironmentChange,\n type Fingerprint,\n} from \"./fingerprint.ts\";\nimport { assertScreenshotBuffer } from \"./guards.ts\";\nimport type { CaptureProgress } from \"./progress.ts\";\n\n/** Snapshots persisted at once: each decodes two PNGs for the diff, so unbounded fan-out spikes memory. */\nexport const PERSIST_CONCURRENCY = 4;\n\n/**\n * Persist a completed capture run: write screenshots, diff against the branch\n * baseline, create snapshots, and finalize the build.\n *\n * Rendering is performed by a pure `CaptureRunner`; this function owns only\n * storage writes and record keeping, so it can run against any renderer.\n *\n * @param ctx - Capture context.\n * @param renderFailedStoryIds - Story ids that the renderer could not capture\n * (blocking failures). They force the build to `failed`.\n * @param flakyFailedStoryIds - Story ids that failed but are marked flaky\n * (non-blocking). They are logged as warnings and do not block the build.\n */\nexport async function persistCapture(\n input: CaptureContext,\n renderFailedStoryIds: ReadonlySet<string> = new Set(),\n flakyFailedStoryIds: ReadonlySet<string> = new Set(),\n a11yFailedStoryIds: ReadonlySet<string> = new Set(),\n): Promise<void> {\n const sets = await persistRendered(\n input,\n renderFailedStoryIds,\n flakyFailedStoryIds,\n a11yFailedStoryIds,\n );\n for (const id of await persistInherited(input)) {\n sets.failed.add(id);\n }\n await finalizeBuild(input, allStoryIds(input), sets.failed, sets.flaky, sets.a11y);\n}\n\n/** Blocking, flaky and a11y story ids a persist run ended with. */\nexport interface PersistedSets {\n failed: Set<string>;\n flaky: Set<string>;\n a11y: Set<string>;\n}\n\n/**\n * Persist the rendered captures only: screenshots, diffs and snapshot rows, without the\n * inherited snapshots or the build finalize. A sharded capture runs this once per shard.\n */\nexport async function persistRendered(\n input: CaptureContext,\n renderFailedStoryIds: ReadonlySet<string> = new Set(),\n flakyFailedStoryIds: ReadonlySet<string> = new Set(),\n a11yFailedStoryIds: ReadonlySet<string> = new Set(),\n): Promise<PersistedSets> {\n const ctx = await withFreshBrowsers(input);\n const failedStoryIds = new Set(renderFailedStoryIds);\n const flakyIds = new Set(flakyFailedStoryIds);\n const a11yIds = new Set(a11yFailedStoryIds);\n // Primary engine first: a newly enabled engine bootstraps from its counterpart.\n for (const group of capturesByPhase(ctx)) {\n // oxlint-disable-next-line no-await-in-loop -- phases must run in order\n await mapLimit(group, PERSIST_CONCURRENCY, async (capture) => {\n try {\n await persistSnapshot(ctx, capture);\n // Each diff is progress: a long, healthy persist phase must not read as a hung job.\n ctx.progress?.();\n } catch (error) {\n // A failure in one story/viewport must not abort the other persists.\n // Flaky and a11y stories remain non-blocking even on persist failure.\n if (flakyIds.has(capture.story.id) || a11yIds.has(capture.story.id)) {\n if (flakyIds.has(capture.story.id)) flakyIds.add(capture.story.id);\n if (a11yIds.has(capture.story.id)) a11yIds.add(capture.story.id);\n ctx.logger?.warn(\n { storyId: capture.story.id, viewport: capture.viewportName, err: error },\n \"capture failed for flaky/a11y story (non-blocking)\",\n );\n await emitAttemptLog(\n ctx.recordLog,\n ctx.logger,\n \"warn\",\n \"capture failed for flaky/a11y story (non-blocking)\",\n {\n storyId: capture.story.id,\n viewport: capture.viewportName,\n },\n );\n } else {\n failedStoryIds.add(capture.story.id);\n }\n ctx.logger?.error(\n { storyId: capture.story.id, viewport: capture.viewportName, err: error },\n \"capture failed for story\",\n );\n await emitAttemptLog(ctx.recordLog, ctx.logger, \"error\", \"capture failed for story\", {\n storyId: capture.story.id,\n viewport: capture.viewportName,\n });\n }\n });\n }\n // Also log flaky and a11y render failures as warnings\n for (const id of flakyIds) {\n if (!failedStoryIds.has(id)) {\n ctx.logger?.warn({ storyId: id }, \"flaky story failed (non-blocking)\");\n await emitAttemptLog(ctx.recordLog, ctx.logger, \"warn\", \"flaky story failed (non-blocking)\", {\n storyId: id,\n });\n }\n }\n for (const id of a11yIds) {\n if (!failedStoryIds.has(id) && !flakyIds.has(id)) {\n ctx.logger?.warn({ storyId: id }, \"a11y violations found (non-blocking)\");\n await emitAttemptLog(\n ctx.recordLog,\n ctx.logger,\n \"warn\",\n \"a11y violations found (non-blocking)\",\n {\n storyId: id,\n },\n );\n }\n }\n return { failed: failedStoryIds, flaky: flakyIds, a11y: a11yIds };\n}\n\n/** Engines of this run, primary (first) first. */\nfunction runBrowsers(ctx: CaptureContext): string[] {\n const listed = ctx.browsers && ctx.browsers.length > 0 ? ctx.browsers : [DEFAULT_BROWSER];\n return [...new Set(listed)];\n}\n\nfunction primaryBrowser(ctx: CaptureContext): string {\n return runBrowsers(ctx)[0] ?? DEFAULT_BROWSER;\n}\n\n/** The capture's axe violation texts as the JSON stored on the snapshot; null when there are none. */\nfunction a11yJsonOf(capture: { a11y?: string[] }): string | null {\n return capture.a11y && capture.a11y.length > 0 ? JSON.stringify(capture.a11y) : null;\n}\n\nfunction browserOf(capture: { browser?: string }): string {\n return capture.browser ?? DEFAULT_BROWSER;\n}\n\n/** Captures split into the primary engine's, then everyone else's. */\nfunction capturesByPhase(ctx: CaptureContext): RenderedSnapshot[][] {\n const primary = primaryBrowser(ctx);\n const first = ctx.captures.filter((capture) => browserOf(capture) === primary);\n const rest = ctx.captures.filter((capture) => browserOf(capture) !== primary);\n return rest.length > 0 ? [first, rest] : [first];\n}\n\n/** Secondary engines with no baseline yet (their first snapshots bootstrap from the primary). */\nexport async function freshBrowsersOf(ctx: CaptureContext): Promise<Set<string>> {\n const primary = primaryBrowser(ctx);\n const baselines = new BaselineModel(ctx.db, ctx.tables, ctx.storage, ctx.secret);\n const secondary = runBrowsers(ctx).filter((browser) => browser !== primary);\n const checks = await Promise.all(\n secondary.map(async (browser) => ({\n browser,\n fresh: !(await baselines.hasAny(ctx.project.id, browser)),\n })),\n );\n return new Set(checks.filter((check) => check.fresh).map((check) => check.browser));\n}\n\n/** Mark secondary engines with no baseline yet: their first snapshots bootstrap. */\nasync function withFreshBrowsers(ctx: CaptureContext): Promise<CaptureContext> {\n return ctx.freshBrowsers ? ctx : { ...ctx, freshBrowsers: await freshBrowsersOf(ctx) };\n}\n\n/**\n * A newly enabled engine accepts a story/mode's first snapshot when the\n * primary engine's snapshot of it is clean (Chromatic's \"auto-accepted where\n * the primary browser was accepted\"); anything else goes to review as `new`.\n */\nasync function bootstrapAccepts(ctx: CaptureContext, capture: RenderedSnapshot): Promise<boolean> {\n const browser = browserOf(capture);\n if (!ctx.freshBrowsers?.has(browser)) return false;\n const primary = primaryBrowser(ctx);\n const siblings = await new SnapshotModel(ctx.db, ctx.tables).listByBuild(ctx.build.id);\n return siblings.some(\n (snapshot) =>\n snapshot.storyId === capture.story.id &&\n snapshot.viewportName === capture.viewportName &&\n (snapshot.browser ?? DEFAULT_BROWSER) === primary &&\n (snapshot.status === \"approved\" || snapshot.status === \"unchanged\"),\n );\n}\n\n/** Persist one inherited snapshot; returns the story id on failure. */\nasync function tryPersistInherited(\n ctx: CaptureContext,\n inherited: InheritedStory,\n): Promise<string | null> {\n try {\n await persistInheritedSnapshot(ctx, inherited);\n return null;\n } catch (error) {\n ctx.logger?.error(\n { storyId: inherited.story.id, viewport: inherited.viewport.name, err: error },\n \"inherit failed for story\",\n );\n await emitAttemptLog(ctx.recordLog, ctx.logger, \"error\", \"inherit failed for story\", {\n storyId: inherited.story.id,\n viewport: inherited.viewport.name,\n });\n return inherited.story.id;\n }\n}\n\n/** Persist inherited snapshots; a failure marks the story failed. */\n/** Persist the inherited snapshots (stories reusing their baseline); returns the story ids that failed. */\nexport async function persistInherited(ctx: CaptureContext): Promise<ReadonlySet<string>> {\n const outcomes = await mapLimit(ctx.inherited ?? [], PERSIST_CONCURRENCY, async (inherited) => {\n const failed = await tryPersistInherited(ctx, inherited);\n ctx.progress?.();\n return failed;\n });\n return new Set(outcomes.filter((id): id is string => id !== null));\n}\n\n/** Create an unchanged snapshot reusing the baseline screenshot. */\nasync function persistInheritedSnapshot(\n ctx: CaptureContext,\n inherited: InheritedStory,\n): Promise<void> {\n const browser = browserOf(inherited);\n // Nothing rendered, so there is no new fingerprint: the snapshot carries the baseline's.\n const infraHash = inherited.baseline.infraHash;\n const snapshots = new SnapshotModel(ctx.db, ctx.tables);\n const snapshot = await snapshots.create(ctx.project.id, ctx.build.id, {\n browser,\n storyId: inherited.story.id,\n storyName: inherited.story.name,\n storyTitle: inherited.story.title,\n storyImportPath: inherited.story.importPath ?? \"\",\n designUrl: inherited.story.designUrl ?? null,\n viewportName: inherited.viewport.name,\n viewportWidth: inherited.viewport.width,\n viewportHeight: inherited.viewport.height,\n screenshotPath: inherited.baseline.screenshotPath,\n infraHash,\n mode: modeJsonOf(inherited.viewport),\n inherited: true,\n baselineId: inherited.baseline.id,\n baselineVersion: inherited.baseline.updatedAt,\n });\n await snapshots.update(snapshot.id, {\n status: \"unchanged\",\n diffPixels: 0,\n diffRatio: 0,\n diffPassed: true,\n diffPath: null,\n infraHash,\n });\n}\n\n/** Every story covered by a capture run, rendered or inherited. */\nfunction allStoryIds(ctx: CaptureContext): ReadonlySet<string> {\n const ids = new Set(ctx.captures.map((capture) => capture.story.id));\n for (const inherited of ctx.inherited ?? []) {\n ids.add(inherited.story.id);\n }\n return ids;\n}\n\n/** Table handles required by the capture pipeline. */\nexport type PipelineTables = BuildTables & BaselineTables & SnapshotTables;\n\n/** Persistence inputs for a completed capture run. */\nexport interface CaptureContext {\n /** Database adapter. */\n db: DatabaseAdapter;\n /** Table handles. */\n tables: PipelineTables;\n /** Storage adapter. */\n storage: StorageAdapter;\n /** The project being captured. */\n project: Project;\n /** The build being captured. */\n build: Build;\n /** Viewports at which stories were captured. */\n viewports: Viewport[];\n /** Engines captured this run, primary first (default: chromium only). */\n browsers?: string[];\n /** Secondary engines with no baselines yet; worked out by `persistCapture`, or fixed up front by a shard plan. */\n freshBrowsers?: ReadonlySet<string>;\n /** Screenshot buffers produced by the capture renderer. */\n captures: RenderedSnapshot[];\n /** Stories inheriting their baseline without rendering (affected capture). */\n inherited?: InheritedStory[];\n /** Optional logger for capture diagnostics. */\n logger?: Logger;\n /** Optional per-attempt log recorder (mirrors logger lines into the DB). */\n recordLog?: AttemptLogRecorder;\n /** Called per snapshot persisted, so the job's lease sees a long diff phase making progress. */\n progress?: CaptureProgress | undefined;\n /** Server secret for decrypting webhook secrets at send time. */\n secret?: SecretInput;\n}\n\n/** JSON for the snapshot `mode` column: the globals a mode applied (null for plain viewports). */\nfunction modeJsonOf(viewport: Viewport | undefined): string | null {\n return viewport?.globals ? JSON.stringify(viewport.globals) : null;\n}\n\nfunction viewportByName(ctx: CaptureContext, name: string): Viewport {\n return ctx.viewports.find((v) => v.name === name) ?? { name, width: 0, height: 0 };\n}\n\n/** Where the capture's screenshot lives: already stored by the orchestrator, or stored now. */\nasync function screenshotLocation(ctx: CaptureContext, capture: RenderedSnapshot): Promise<string> {\n if (capture.storedPath) {\n return capture.storedPath;\n }\n assertScreenshotBuffer(capture.screenshot);\n return await storeScreenshot(ctx.storage, ctx.project.id, capture.screenshot);\n}\n\nasync function persistSnapshot(ctx: CaptureContext, capture: RenderedSnapshot): Promise<void> {\n const viewport = capture.viewport ?? viewportByName(ctx, capture.viewportName);\n const browser = browserOf(capture);\n const screenshot = await screenshotLocation(ctx, capture);\n\n const baseline = await resolveBaseline(ctx, capture.story.id, capture.viewportName, browser);\n if (!baseline) {\n await createWithoutBaseline(ctx, capture, viewport, screenshot);\n return;\n }\n await createWithBaseline(ctx, capture, viewport, screenshot, baseline);\n}\n\nasync function resolveBaseline(\n ctx: CaptureContext,\n storyId: string,\n viewport: string,\n browser: string,\n): Promise<Baseline | null> {\n const baselines = new BaselineModel(ctx.db, ctx.tables, ctx.storage, ctx.secret);\n return await baselines.resolve(\n ctx.project.id,\n storyId,\n viewport,\n ctx.build.gitBranch,\n ctx.project.gitDefaultBranch,\n browser,\n );\n}\n\nasync function createWithoutBaseline(\n ctx: CaptureContext,\n capture: RenderedSnapshot,\n viewport: Viewport,\n screenshot: string,\n): Promise<void> {\n const snapshots = new SnapshotModel(ctx.db, ctx.tables);\n const browser = browserOf(capture);\n const accepted = ctx.build.isDefault || (await bootstrapAccepts(ctx, capture));\n const status = accepted ? \"approved\" : \"new\";\n const now = fingerprintOf(capture, viewport);\n const snapshot = await snapshots.create(ctx.project.id, ctx.build.id, {\n browser,\n durationMs: capture.durationMs ?? null,\n a11yViolations: a11yJsonOf(capture),\n storyId: capture.story.id,\n storyName: capture.story.name,\n storyTitle: capture.story.title,\n storyImportPath: capture.story.importPath ?? \"\",\n designUrl: capture.story.designUrl ?? null,\n viewportName: capture.viewportName,\n viewportWidth: viewport.width,\n viewportHeight: viewport.height,\n screenshotPath: screenshot,\n infraHash: now.infraHash,\n mode: modeJsonOf(viewport),\n baselineVersion: NO_BASELINE,\n });\n await snapshots.setStatus(snapshot.id, status);\n\n if (accepted) {\n const baselines = new BaselineModel(ctx.db, ctx.tables, ctx.storage, ctx.secret);\n await baselines.upsert(\n ctx.project.id,\n capture.story.id,\n capture.viewportName,\n ctx.build.gitBranch,\n snapshot.id,\n screenshot,\n now.infraHash,\n { mode: modeJsonOf(viewport), browser, fingerprint: now.json },\n );\n }\n}\n\n/** A capture's fingerprint: its components, their hash (stored as `infraHash`), and the stored JSON. */\ninterface CaptureFingerprint {\n fingerprint: Fingerprint;\n infraHash: string;\n json: string;\n}\n\nfunction fingerprintOf(capture: RenderedSnapshot, viewport: Viewport): CaptureFingerprint {\n return captureFingerprintOf(buildFingerprint(browserOf(capture), viewport, capture.environment));\n}\n\n/** A fingerprint as stored: its hash (the `infraHash`) and JSON. */\nfunction captureFingerprintOf(fingerprint: Fingerprint): CaptureFingerprint {\n return {\n fingerprint,\n infraHash: fingerprintHash(fingerprint),\n json: JSON.stringify(fingerprint),\n };\n}\n\n/**\n * What changed in the capture environment since the baseline was recorded, or null when nothing\n * did. Compared component by component, only where both sides report a value: a capture with no\n * environment (spooled before a browser crash) is unknown, not a change. A baseline from before\n * fingerprints cannot be compared, so it is never reported as changed; it is backfilled instead.\n */\nfunction environmentChange(baseline: Baseline, now: CaptureFingerprint): EnvironmentChange | null {\n const from = parseFingerprint(baseline.fingerprint);\n if (!from) {\n return null;\n }\n const changed = changedFields(from, now.fingerprint);\n return changed.length > 0 ? { changed, from, to: now.fingerprint } : null;\n}\n\n/**\n * A changed environment migrates the baseline without a diff only on the default branch, which is\n * authoritative and auto-approves its own changes anyway. A feature branch still diffs: a real\n * change is reported as `changed` with the environment change attached, never auto-accepted.\n */\nasync function createWithBaseline(\n ctx: CaptureContext,\n capture: RenderedSnapshot,\n viewport: Viewport,\n screenshot: string,\n baseline: Baseline,\n): Promise<void> {\n const now = fingerprintOf(capture, viewport);\n const change = environmentChange(baseline, now);\n const input = { ctx, capture, viewport, screenshot, baseline, now, change };\n if (change && ctx.build.isDefault) {\n await persistMigration(input, change);\n } else {\n await persistDiff(input);\n }\n}\n\ninterface BaselineCapture {\n ctx: CaptureContext;\n capture: RenderedSnapshot;\n viewport: Viewport;\n screenshot: string;\n baseline: Baseline;\n now: CaptureFingerprint;\n change: EnvironmentChange | null;\n}\n\n/** The fingerprint to write on the baseline: the capture's, keeping components it did not report. */\nfunction baselineFingerprintOf(input: BaselineCapture): CaptureFingerprint {\n const earlier = parseFingerprint(input.baseline.fingerprint);\n return captureFingerprintOf(mergeFingerprint(earlier, input.now.fingerprint));\n}\n\nfunction snapshotInput(input: BaselineCapture): Parameters<SnapshotModel[\"create\"]>[2] {\n const { capture, viewport, screenshot, baseline, now } = input;\n return {\n browser: browserOf(capture),\n durationMs: capture.durationMs ?? null,\n a11yViolations: a11yJsonOf(capture),\n storyId: capture.story.id,\n storyName: capture.story.name,\n storyTitle: capture.story.title,\n storyImportPath: capture.story.importPath ?? \"\",\n designUrl: capture.story.designUrl ?? null,\n viewportName: capture.viewportName,\n viewportWidth: viewport.width,\n viewportHeight: viewport.height,\n screenshotPath: screenshot,\n infraHash: now.infraHash,\n mode: modeJsonOf(viewport),\n baselineId: baseline.id,\n baselineVersion: baseline.updatedAt,\n };\n}\n\n/**\n * The default branch's capture environment changed (browser build, fonts, platform, scale): pixels\n * would differ with no code change, so no diff is computed. The snapshot is accepted, records what\n * changed, and the new screenshot becomes the baseline.\n */\nasync function persistMigration(input: BaselineCapture, change: EnvironmentChange): Promise<void> {\n const { ctx, capture, viewport, screenshot, now } = input;\n const snapshots = new SnapshotModel(ctx.db, ctx.tables);\n const snapshot = await snapshots.create(ctx.project.id, ctx.build.id, {\n ...snapshotInput(input),\n envChange: JSON.stringify(change),\n });\n await snapshots.update(snapshot.id, {\n status: \"unchanged\",\n diffPath: null,\n diffPixels: 0,\n diffRatio: 0,\n diffPassed: true,\n infraHash: now.infraHash,\n });\n await upsertDefaultBaseline(ctx, snapshot.id, capture, screenshot, baselineFingerprintOf(input));\n ctx.logger?.debug(\n { storyId: capture.story.id, viewport: viewport.name, changed: change.changed },\n \"capture environment changed; accepted without a diff\",\n );\n}\n\n/** Diff against the baseline; a feature-branch environment change rides along as `envChange`. */\nasync function persistDiff(input: BaselineCapture): Promise<void> {\n const { ctx, capture, screenshot, baseline, now, change } = input;\n const result = await diffStoredScreenshots(\n ctx.storage,\n ctx.project,\n baseline.screenshotPath,\n screenshot,\n capture.story.parameters?.diffThreshold,\n );\n const snapshots = new SnapshotModel(ctx.db, ctx.tables);\n const snapshot = await snapshots.create(ctx.project.id, ctx.build.id, {\n ...snapshotInput(input),\n ...(change ? { envChange: JSON.stringify(change) } : {}),\n });\n const diff = diffPath(\n ctx.project.id,\n ctx.build.id,\n capture.story.id,\n capture.viewportName,\n browserOf(capture),\n );\n const overlay = await writeDiffOverlay(ctx.storage, diff, result, result.passed);\n await snapshots.update(snapshot.id, {\n status: diffStatus(result.passed, ctx.build.isDefault),\n diffPath: overlay,\n diffPixels: result.diffPixels,\n diffRatio: result.diffRatio,\n diffPassed: result.passed,\n infraHash: now.infraHash,\n });\n if (ctx.build.isDefault) {\n await refreshDefaultBaseline(input, snapshot.id, result.passed);\n }\n}\n\n/**\n * After a default-branch diff: a failed diff moves the baseline to the new screenshot; a passing\n * one backfills the fingerprint when it was missing or had components the capture now reports.\n */\nasync function refreshDefaultBaseline(\n input: BaselineCapture,\n snapshotId: string,\n passed: boolean,\n): Promise<void> {\n const { ctx, capture, screenshot, baseline } = input;\n const target = baselineFingerprintOf(input);\n if (!passed) {\n await upsertDefaultBaseline(ctx, snapshotId, capture, screenshot, target);\n } else if (!baseline.fingerprint || baseline.infraHash !== target.infraHash) {\n await new BaselineModel(ctx.db, ctx.tables, ctx.storage, ctx.secret).backfillFingerprint(\n baseline,\n target.infraHash,\n target.json,\n );\n }\n}\n\n/** Default-branch builds are authoritative: the captured screenshot becomes the baseline. */\nasync function upsertDefaultBaseline(\n ctx: CaptureContext,\n snapshotId: string,\n capture: RenderedSnapshot,\n screenshot: string,\n now: CaptureFingerprint,\n): Promise<void> {\n const baselines = new BaselineModel(ctx.db, ctx.tables, ctx.storage, ctx.secret);\n await baselines.upsert(\n ctx.project.id,\n capture.story.id,\n capture.viewportName,\n ctx.build.gitBranch,\n snapshotId,\n screenshot,\n now.infraHash,\n { mode: modeJsonOf(capture.viewport), browser: browserOf(capture), fingerprint: now.json },\n );\n}\n\n/** Snapshot status after diffing: default-branch changes are auto-approved, not queued for review. */\nexport function diffStatus(passed: boolean, isDefault: boolean): SnapshotStatus {\n if (passed) {\n return \"unchanged\";\n }\n return isDefault ? \"approved\" : \"changed\";\n}\n\n/** A failed capture fails the build; default-branch builds otherwise auto-approve. */\nfunction finalBuildStatus(\n isDefault: boolean,\n failedCount: number,\n hasCaptures: boolean,\n build: { changedCount: number },\n): BuildStatus {\n if (failedCount > 0) {\n return \"failed\";\n }\n if (isDefault || (hasCaptures && build.changedCount === 0)) {\n return \"approved\";\n }\n return \"reviewing\";\n}\n\n/**\n * Close out a capture: roll the snapshot counts up, set the build's final status from the failed\n * story ids, and (on the default branch) drop baselines of stories that no longer exist.\n */\nexport async function finalizeBuild(\n ctx: CaptureContext,\n storyIds: ReadonlySet<string>,\n failedStoryIds: ReadonlySet<string>,\n flakyFailedStoryIds: ReadonlySet<string> = new Set(),\n a11yFailedStoryIds: ReadonlySet<string> = new Set(),\n): Promise<void> {\n const builds = new BuildModel(ctx.db, ctx.tables, ctx.logger);\n if ((await builds.get(ctx.build.id))?.status === \"cancelled\") {\n ctx.logger?.debug({ buildId: ctx.build.id }, \"build is cancelled; finalize skipped\");\n return;\n }\n const build = await builds.updateCounts(ctx.build.id);\n const hasCaptures = storyIds.size > 0;\n const status = finalBuildStatus(ctx.build.isDefault, failedStoryIds.size, hasCaptures, build);\n // Flaky and a11y failures do not block: log warning if any failed non-blocking\n if (flakyFailedStoryIds.size > 0 && failedStoryIds.size === 0) {\n ctx.logger?.warn(\n { flakyStoryIds: [...flakyFailedStoryIds] },\n \"flaky stories failed (non-blocking)\",\n );\n await emitAttemptLog(ctx.recordLog, ctx.logger, \"warn\", \"flaky stories failed (non-blocking)\", {\n flakyStoryIds: [...flakyFailedStoryIds],\n });\n }\n if (a11yFailedStoryIds.size > 0 && failedStoryIds.size === 0) {\n ctx.logger?.warn(\n { a11yStoryIds: [...a11yFailedStoryIds] },\n \"a11y violations found (non-blocking)\",\n );\n await emitAttemptLog(\n ctx.recordLog,\n ctx.logger,\n \"warn\",\n \"a11y violations found (non-blocking)\",\n {\n a11yStoryIds: [...a11yFailedStoryIds],\n },\n );\n }\n await builds.setStatus(ctx.build.id, status);\n\n if (ctx.build.isDefault && hasCaptures) {\n const baselines = new BaselineModel(ctx.db, ctx.tables, ctx.storage, ctx.secret);\n await baselines.removeOrphans(ctx.project.id, new Set(storyIds));\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pipeline.mjs","names":[],"sources":["../../src/capture/pipeline.ts"],"sourcesContent":["import type { Logger } from \"pino\";\nimport { DEFAULT_BROWSER, type RenderedSnapshot } from \"../adapters/capture-runner.ts\";\nimport { ReferenceGenerationConflict, type DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { StorageAdapter } from \"../adapters/storage.ts\";\nimport { BaselineWriteConflict } from \"../models/baseline-conflict.ts\";\nimport { NO_BASELINE } from \"../models/baseline-status.ts\";\nimport { BaselineModel, type BaselineTables } from \"../models/baseline.ts\";\nimport { BuildModel, type BuildTables } from \"../models/build.ts\";\nimport { emitAttemptLog, type AttemptLogRecorder } from \"../models/capture-attempt.ts\";\nimport { SnapshotModel, type SnapshotTables } from \"../models/snapshot.ts\";\nimport type { Baseline } from \"../schema/baseline.ts\";\nimport type { Build } from \"../schema/build.ts\";\nimport type { Project } from \"../schema/project.ts\";\nimport type { BuildStatus, SnapshotStatus } from \"../types.ts\";\nimport type { SecretInput } from \"../utils/encrypt.ts\";\nimport { mapLimit } from \"../utils/map-limit.ts\";\nimport { diffPath } from \"../utils/paths.ts\";\nimport { storeScreenshot } from \"../utils/screenshot-store.ts\";\nimport type { Viewport } from \"./adapter.ts\";\nimport type { InheritedStory } from \"./affected.ts\";\nimport { freezeComparison, sourceSnapshotId } from \"./comparison-reference.ts\";\nimport { diffStoredScreenshots, writeDiffOverlay } from \"./diff-record.ts\";\nimport {\n buildFingerprint,\n changedFields,\n fingerprintHash,\n mergeFingerprint,\n parseFingerprint,\n type EnvironmentChange,\n type Fingerprint,\n} from \"./fingerprint.ts\";\nimport { assertScreenshotBuffer } from \"./guards.ts\";\nimport type { CaptureProgress } from \"./progress.ts\";\n\n/** Snapshots persisted at once: each decodes two PNGs for the diff, so unbounded fan-out spikes memory. */\nexport const PERSIST_CONCURRENCY = 4;\n\n/**\n * Persist a completed capture run: write screenshots, diff against the branch\n * baseline, create snapshots, and finalize the build.\n *\n * Rendering is performed by a pure `CaptureRunner`; this function owns only\n * storage writes and record keeping, so it can run against any renderer.\n *\n * @param ctx - Capture context.\n * @param renderFailedStoryIds - Story ids that the renderer could not capture\n * (blocking failures). They force the build to `failed`.\n * @param flakyFailedStoryIds - Story ids that failed but are marked flaky\n * (non-blocking). They are logged as warnings and do not block the build.\n */\nexport async function persistCapture(\n input: CaptureContext,\n renderFailedStoryIds: ReadonlySet<string> = new Set(),\n flakyFailedStoryIds: ReadonlySet<string> = new Set(),\n a11yFailedStoryIds: ReadonlySet<string> = new Set(),\n): Promise<void> {\n const sets = await persistRendered(\n input,\n renderFailedStoryIds,\n flakyFailedStoryIds,\n a11yFailedStoryIds,\n );\n for (const id of await persistInherited(input)) {\n sets.failed.add(id);\n }\n await finalizeBuild(input, allStoryIds(input), sets.failed, sets.flaky, sets.a11y);\n}\n\n/** Blocking, flaky and a11y story ids a persist run ended with. */\nexport interface PersistedSets {\n failed: Set<string>;\n flaky: Set<string>;\n a11y: Set<string>;\n}\n\n/**\n * Persist the rendered captures only: screenshots, diffs and snapshot rows, without the\n * inherited snapshots or the build finalize. A sharded capture runs this once per shard.\n */\nexport async function persistRendered(\n input: CaptureContext,\n renderFailedStoryIds: ReadonlySet<string> = new Set(),\n flakyFailedStoryIds: ReadonlySet<string> = new Set(),\n a11yFailedStoryIds: ReadonlySet<string> = new Set(),\n): Promise<PersistedSets> {\n const ctx = await withFreshBrowsers(input);\n const failedStoryIds = new Set(renderFailedStoryIds);\n const flakyIds = new Set(flakyFailedStoryIds);\n const a11yIds = new Set(a11yFailedStoryIds);\n // Primary engine first: a newly enabled engine bootstraps from its counterpart.\n for (const group of capturesByPhase(ctx)) {\n // oxlint-disable-next-line no-await-in-loop -- phases must run in order\n await mapLimit(group, PERSIST_CONCURRENCY, async (capture) => {\n try {\n await persistSnapshot(ctx, capture);\n // Each diff is progress: a long, healthy persist phase must not read as a hung job.\n ctx.progress?.();\n } catch (error) {\n // A failure in one story/viewport must not abort the other persists.\n // Flaky and a11y stories remain non-blocking even on persist failure.\n if (flakyIds.has(capture.story.id) || a11yIds.has(capture.story.id)) {\n if (flakyIds.has(capture.story.id)) flakyIds.add(capture.story.id);\n if (a11yIds.has(capture.story.id)) a11yIds.add(capture.story.id);\n ctx.logger?.warn(\n { storyId: capture.story.id, viewport: capture.viewportName, err: error },\n \"capture failed for flaky/a11y story (non-blocking)\",\n );\n await emitAttemptLog(\n ctx.recordLog,\n ctx.logger,\n \"warn\",\n \"capture failed for flaky/a11y story (non-blocking)\",\n {\n storyId: capture.story.id,\n viewport: capture.viewportName,\n },\n );\n } else {\n failedStoryIds.add(capture.story.id);\n }\n ctx.logger?.error(\n { storyId: capture.story.id, viewport: capture.viewportName, err: error },\n \"capture failed for story\",\n );\n await emitAttemptLog(ctx.recordLog, ctx.logger, \"error\", \"capture failed for story\", {\n storyId: capture.story.id,\n viewport: capture.viewportName,\n });\n }\n });\n }\n // Also log flaky and a11y render failures as warnings\n for (const id of flakyIds) {\n if (!failedStoryIds.has(id)) {\n ctx.logger?.warn({ storyId: id }, \"flaky story failed (non-blocking)\");\n await emitAttemptLog(ctx.recordLog, ctx.logger, \"warn\", \"flaky story failed (non-blocking)\", {\n storyId: id,\n });\n }\n }\n for (const id of a11yIds) {\n if (!failedStoryIds.has(id) && !flakyIds.has(id)) {\n ctx.logger?.warn({ storyId: id }, \"a11y violations found (non-blocking)\");\n await emitAttemptLog(\n ctx.recordLog,\n ctx.logger,\n \"warn\",\n \"a11y violations found (non-blocking)\",\n {\n storyId: id,\n },\n );\n }\n }\n return { failed: failedStoryIds, flaky: flakyIds, a11y: a11yIds };\n}\n\n/** Engines of this run, primary (first) first. */\nfunction runBrowsers(ctx: CaptureContext): string[] {\n const listed = ctx.browsers && ctx.browsers.length > 0 ? ctx.browsers : [DEFAULT_BROWSER];\n return [...new Set(listed)];\n}\n\nfunction primaryBrowser(ctx: CaptureContext): string {\n return runBrowsers(ctx)[0] ?? DEFAULT_BROWSER;\n}\n\n/** The capture's axe violation texts as the JSON stored on the snapshot; null when there are none. */\nfunction a11yJsonOf(capture: { a11y?: string[] }): string | null {\n return capture.a11y && capture.a11y.length > 0 ? JSON.stringify(capture.a11y) : null;\n}\n\nfunction browserOf(capture: { browser?: string }): string {\n return capture.browser ?? DEFAULT_BROWSER;\n}\n\n/** Captures split into the primary engine's, then everyone else's. */\nfunction capturesByPhase(ctx: CaptureContext): RenderedSnapshot[][] {\n const primary = primaryBrowser(ctx);\n const first = ctx.captures.filter((capture) => browserOf(capture) === primary);\n const rest = ctx.captures.filter((capture) => browserOf(capture) !== primary);\n return rest.length > 0 ? [first, rest] : [first];\n}\n\n/** Secondary engines with no baseline yet (their first snapshots bootstrap from the primary). */\nexport async function freshBrowsersOf(ctx: CaptureContext): Promise<Set<string>> {\n const primary = primaryBrowser(ctx);\n const baselines = new BaselineModel(ctx.db, ctx.tables, ctx.storage, ctx.secret);\n const secondary = runBrowsers(ctx).filter((browser) => browser !== primary);\n const checks = await Promise.all(\n secondary.map(async (browser) => ({\n browser,\n fresh: !(await baselines.hasAny(ctx.project.id, browser)),\n })),\n );\n return new Set(checks.filter((check) => check.fresh).map((check) => check.browser));\n}\n\n/** Mark secondary engines with no baseline yet: their first snapshots bootstrap. */\nasync function withFreshBrowsers(ctx: CaptureContext): Promise<CaptureContext> {\n return ctx.freshBrowsers ? ctx : { ...ctx, freshBrowsers: await freshBrowsersOf(ctx) };\n}\n\n/**\n * A newly enabled engine accepts a story/mode's first snapshot when the\n * primary engine's snapshot of it is clean (Chromatic's \"auto-accepted where\n * the primary browser was accepted\"); anything else goes to review as `new`.\n */\nasync function bootstrapAccepts(ctx: CaptureContext, capture: RenderedSnapshot): Promise<boolean> {\n const browser = browserOf(capture);\n if (!ctx.freshBrowsers?.has(browser)) return false;\n const primary = primaryBrowser(ctx);\n const siblings = await new SnapshotModel(ctx.db, ctx.tables).listByBuild(ctx.build.id);\n return siblings.some(\n (snapshot) =>\n snapshot.storyId === capture.story.id &&\n snapshot.viewportName === capture.viewportName &&\n (snapshot.browser ?? DEFAULT_BROWSER) === primary &&\n (snapshot.status === \"approved\" || snapshot.status === \"unchanged\"),\n );\n}\n\n/** Persist one inherited snapshot; returns the story id on failure. */\nasync function tryPersistInherited(\n ctx: CaptureContext,\n inherited: InheritedStory,\n): Promise<string | null> {\n try {\n await persistInheritedSnapshot(ctx, inherited);\n return null;\n } catch (error) {\n ctx.logger?.error(\n { storyId: inherited.story.id, viewport: inherited.viewport.name, err: error },\n \"inherit failed for story\",\n );\n await emitAttemptLog(ctx.recordLog, ctx.logger, \"error\", \"inherit failed for story\", {\n storyId: inherited.story.id,\n viewport: inherited.viewport.name,\n });\n return inherited.story.id;\n }\n}\n\n/** Persist inherited snapshots; a failure marks the story failed. */\n/** Persist the inherited snapshots (stories reusing their baseline); returns the story ids that failed. */\nexport async function persistInherited(ctx: CaptureContext): Promise<ReadonlySet<string>> {\n const outcomes = await mapLimit(ctx.inherited ?? [], PERSIST_CONCURRENCY, async (inherited) => {\n const failed = await tryPersistInherited(ctx, inherited);\n ctx.progress?.();\n return failed;\n });\n return new Set(outcomes.filter((id): id is string => id !== null));\n}\n\n/** Create an unchanged snapshot reusing the baseline screenshot. */\nasync function persistInheritedSnapshot(\n ctx: CaptureContext,\n inherited: InheritedStory,\n): Promise<void> {\n const browser = browserOf(inherited);\n // Nothing rendered, so there is no new fingerprint: the snapshot carries the baseline's.\n const infraHash = inherited.baseline.infraHash;\n const snapshots = new SnapshotModel(ctx.db, ctx.tables);\n const comparison = await freezeComparison(\n ctx.storage,\n ctx.project.id,\n ctx.build.id,\n inherited.baseline,\n {\n defaultBranch: ctx.project.gitDefaultBranch,\n mode: modeJsonOf(inherited.viewport),\n target: browser,\n width: inherited.viewport.width,\n height: inherited.viewport.height,\n captureFingerprint: inherited.baseline.fingerprint ?? null,\n sourceSnapshotId: await sourceSnapshotId(ctx.db, inherited.baseline),\n },\n );\n const snapshot = await snapshots.create(ctx.project.id, ctx.build.id, {\n ...comparison,\n browser,\n storyId: inherited.story.id,\n storyName: inherited.story.name,\n storyTitle: inherited.story.title,\n storyImportPath: inherited.story.importPath ?? \"\",\n designUrl: inherited.story.designUrl ?? null,\n viewportName: inherited.viewport.name,\n viewportWidth: inherited.viewport.width,\n viewportHeight: inherited.viewport.height,\n screenshotPath: inherited.baseline.screenshotPath,\n infraHash,\n mode: modeJsonOf(inherited.viewport),\n inherited: true,\n sourceBuildId: inherited.baseline.sourceBuildId ?? null,\n baselineId: inherited.baseline.id,\n baselineVersion: inherited.baseline.updatedAt,\n });\n await snapshots.update(snapshot.id, {\n status: \"unchanged\",\n diffPixels: 0,\n diffRatio: 0,\n diffPassed: true,\n diffPath: null,\n infraHash,\n });\n}\n\n/** Every story covered by a capture run, rendered or inherited. */\nfunction allStoryIds(ctx: CaptureContext): ReadonlySet<string> {\n const ids = new Set(ctx.captures.map((capture) => capture.story.id));\n for (const inherited of ctx.inherited ?? []) {\n ids.add(inherited.story.id);\n }\n return ids;\n}\n\n/** Table handles required by the capture pipeline. */\nexport type PipelineTables = BuildTables & BaselineTables & SnapshotTables;\n\n/** Persistence inputs for a completed capture run. */\nexport interface CaptureContext {\n /** Database adapter. */\n db: DatabaseAdapter;\n /** Table handles. */\n tables: PipelineTables;\n /** Storage adapter. */\n storage: StorageAdapter;\n /** The project being captured. */\n project: Project;\n /** The build being captured. */\n build: Build;\n /** Viewports at which stories were captured. */\n viewports: Viewport[];\n /** Engines captured this run, primary first (default: chromium only). */\n browsers?: string[];\n /** Secondary engines with no baselines yet; worked out by `persistCapture`, or fixed up front by a shard plan. */\n freshBrowsers?: ReadonlySet<string>;\n /** Screenshot buffers produced by the capture renderer. */\n captures: RenderedSnapshot[];\n /** Stories inheriting their baseline without rendering (affected capture). */\n inherited?: InheritedStory[];\n /** Optional logger for capture diagnostics. */\n logger?: Logger;\n /** Optional per-attempt log recorder (mirrors logger lines into the DB). */\n recordLog?: AttemptLogRecorder;\n /** Called per snapshot persisted, so the job's lease sees a long diff phase making progress. */\n progress?: CaptureProgress | undefined;\n /** Server secret for decrypting webhook secrets at send time. */\n secret?: SecretInput;\n}\n\n/** JSON for the snapshot `mode` column: the globals a mode applied (null for plain viewports). */\nfunction modeJsonOf(viewport: Viewport | undefined): string | null {\n return viewport?.globals ? JSON.stringify(viewport.globals) : null;\n}\n\nfunction viewportByName(ctx: CaptureContext, name: string): Viewport {\n return ctx.viewports.find((v) => v.name === name) ?? { name, width: 0, height: 0 };\n}\n\n/** Where the capture's screenshot lives: already stored by the orchestrator, or stored now. */\nasync function screenshotLocation(ctx: CaptureContext, capture: RenderedSnapshot): Promise<string> {\n if (capture.storedPath) {\n return capture.storedPath;\n }\n assertScreenshotBuffer(capture.screenshot);\n return await storeScreenshot(ctx.storage, ctx.project.id, capture.screenshot);\n}\n\nasync function persistSnapshot(ctx: CaptureContext, capture: RenderedSnapshot): Promise<void> {\n const viewport = capture.viewport ?? viewportByName(ctx, capture.viewportName);\n const browser = browserOf(capture);\n const screenshot = await screenshotLocation(ctx, capture);\n\n const baseline = await resolveBaseline(ctx, capture.story.id, capture.viewportName, browser);\n if (!baseline) {\n await createWithoutBaseline(ctx, capture, viewport, screenshot);\n return;\n }\n await createWithBaseline(ctx, capture, viewport, screenshot, baseline);\n}\n\nasync function resolveBaseline(\n ctx: CaptureContext,\n storyId: string,\n viewport: string,\n browser: string,\n): Promise<Baseline | null> {\n const baselines = new BaselineModel(ctx.db, ctx.tables, ctx.storage, ctx.secret);\n return await baselines.resolve(\n ctx.project.id,\n storyId,\n viewport,\n ctx.build.gitBranch,\n ctx.project.gitDefaultBranch,\n browser,\n );\n}\n\nasync function createWithoutBaseline(\n ctx: CaptureContext,\n capture: RenderedSnapshot,\n viewport: Viewport,\n screenshot: string,\n): Promise<void> {\n const snapshots = new SnapshotModel(ctx.db, ctx.tables);\n const browser = browserOf(capture);\n const accepted = ctx.build.isDefault || (await bootstrapAccepts(ctx, capture));\n const status = accepted ? \"approved\" : \"new\";\n const now = fingerprintOf(capture, viewport);\n const snapshot = await snapshots.create(ctx.project.id, ctx.build.id, {\n browser,\n durationMs: capture.durationMs ?? null,\n a11yViolations: a11yJsonOf(capture),\n storyId: capture.story.id,\n storyName: capture.story.name,\n storyTitle: capture.story.title,\n storyImportPath: capture.story.importPath ?? \"\",\n designUrl: capture.story.designUrl ?? null,\n viewportName: capture.viewportName,\n viewportWidth: viewport.width,\n viewportHeight: viewport.height,\n screenshotPath: screenshot,\n infraHash: now.infraHash,\n mode: modeJsonOf(viewport),\n baselineVersion: NO_BASELINE,\n });\n await snapshots.setStatus(snapshot.id, status);\n\n if (accepted) {\n const baselines = new BaselineModel(ctx.db, ctx.tables, ctx.storage, ctx.secret);\n await baselines.upsert(\n ctx.project.id,\n capture.story.id,\n capture.viewportName,\n ctx.build.gitBranch,\n snapshot.id,\n screenshot,\n now.infraHash,\n { mode: modeJsonOf(viewport), browser, fingerprint: now.json },\n );\n }\n}\n\n/** A capture's fingerprint: its components, their hash (stored as `infraHash`), and the stored JSON. */\ninterface CaptureFingerprint {\n fingerprint: Fingerprint;\n infraHash: string;\n json: string;\n}\n\nfunction fingerprintOf(capture: RenderedSnapshot, viewport: Viewport): CaptureFingerprint {\n return captureFingerprintOf(buildFingerprint(browserOf(capture), viewport, capture.environment));\n}\n\n/** A fingerprint as stored: its hash (the `infraHash`) and JSON. */\nfunction captureFingerprintOf(fingerprint: Fingerprint): CaptureFingerprint {\n return {\n fingerprint,\n infraHash: fingerprintHash(fingerprint),\n json: JSON.stringify(fingerprint),\n };\n}\n\n/**\n * What changed in the capture environment since the baseline was recorded, or null when nothing\n * did. Compared component by component, only where both sides report a value: a capture with no\n * environment (spooled before a browser crash) is unknown, not a change. A baseline from before\n * fingerprints cannot be compared, so it is never reported as changed; it is backfilled instead.\n */\nfunction environmentChange(baseline: Baseline, now: CaptureFingerprint): EnvironmentChange | null {\n const from = parseFingerprint(baseline.fingerprint);\n if (!from) {\n return null;\n }\n const changed = changedFields(from, now.fingerprint);\n return changed.length > 0 ? { changed, from, to: now.fingerprint } : null;\n}\n\n/**\n * A changed environment migrates the baseline without a diff only on the default branch, which is\n * authoritative and auto-approves its own changes anyway. A feature branch still diffs: a real\n * change is reported as `changed` with the environment change attached, never auto-accepted.\n */\nasync function createWithBaseline(\n ctx: CaptureContext,\n capture: RenderedSnapshot,\n viewport: Viewport,\n screenshot: string,\n baseline: Baseline,\n): Promise<void> {\n const now = fingerprintOf(capture, viewport);\n const change = environmentChange(baseline, now);\n const comparison = await freezeComparison(ctx.storage, ctx.project.id, ctx.build.id, baseline, {\n defaultBranch: ctx.project.gitDefaultBranch,\n mode: modeJsonOf(viewport),\n target: browserOf(capture),\n width: viewport.width,\n height: viewport.height,\n captureFingerprint: now.json,\n sourceSnapshotId: await sourceSnapshotId(ctx.db, baseline),\n });\n const input = { ctx, capture, viewport, screenshot, baseline, now, change, comparison };\n if (change && ctx.build.isDefault) {\n await persistMigration(input, change);\n } else {\n await persistDiff(input);\n }\n}\n\ninterface BaselineCapture {\n ctx: CaptureContext;\n capture: RenderedSnapshot;\n viewport: Viewport;\n screenshot: string;\n baseline: Baseline;\n now: CaptureFingerprint;\n change: EnvironmentChange | null;\n comparison: { comparisonImagePath: string; comparisonMetadata: string };\n}\n\n/** The fingerprint to write on the baseline: the capture's, keeping components it did not report. */\nfunction baselineFingerprintOf(input: BaselineCapture): CaptureFingerprint {\n const earlier = parseFingerprint(input.baseline.fingerprint);\n return captureFingerprintOf(mergeFingerprint(earlier, input.now.fingerprint));\n}\n\nfunction snapshotInput(input: BaselineCapture): Parameters<SnapshotModel[\"create\"]>[2] {\n const { capture, viewport, screenshot, baseline, now } = input;\n return {\n browser: browserOf(capture),\n durationMs: capture.durationMs ?? null,\n a11yViolations: a11yJsonOf(capture),\n storyId: capture.story.id,\n storyName: capture.story.name,\n storyTitle: capture.story.title,\n storyImportPath: capture.story.importPath ?? \"\",\n designUrl: capture.story.designUrl ?? null,\n viewportName: capture.viewportName,\n viewportWidth: viewport.width,\n viewportHeight: viewport.height,\n screenshotPath: screenshot,\n infraHash: now.infraHash,\n mode: modeJsonOf(viewport),\n baselineId: baseline.id,\n baselineVersion: baseline.updatedAt,\n ...input.comparison,\n };\n}\n\n/**\n * The default branch's capture environment changed (browser build, fonts, platform, scale): pixels\n * would differ with no code change, so no diff is computed. The snapshot is accepted, records what\n * changed, and the new screenshot becomes the baseline.\n */\nasync function persistMigration(input: BaselineCapture, change: EnvironmentChange): Promise<void> {\n const { ctx, capture, viewport, screenshot, now } = input;\n const snapshots = new SnapshotModel(ctx.db, ctx.tables);\n const snapshot = await snapshots.create(ctx.project.id, ctx.build.id, {\n ...snapshotInput(input),\n envChange: JSON.stringify(change),\n });\n await snapshots.update(snapshot.id, {\n status: \"unchanged\",\n diffPath: null,\n diffPixels: 0,\n diffRatio: 0,\n diffPassed: true,\n infraHash: now.infraHash,\n });\n await upsertDefaultBaseline(ctx, snapshot.id, capture, screenshot, baselineFingerprintOf(input));\n ctx.logger?.debug(\n { storyId: capture.story.id, viewport: viewport.name, changed: change.changed },\n \"capture environment changed; accepted without a diff\",\n );\n}\n\n/** Diff against the baseline; a feature-branch environment change rides along as `envChange`. */\nasync function persistDiff(input: BaselineCapture): Promise<void> {\n const { ctx, capture, screenshot, baseline, now, change } = input;\n const result = await diffStoredScreenshots(\n ctx.storage,\n ctx.project,\n baseline.screenshotPath === screenshot ? screenshot : input.comparison.comparisonImagePath,\n screenshot,\n capture.story.parameters?.diffThreshold,\n );\n const snapshots = new SnapshotModel(ctx.db, ctx.tables);\n const snapshot = await snapshots.create(ctx.project.id, ctx.build.id, {\n ...snapshotInput(input),\n ...(change ? { envChange: JSON.stringify(change) } : {}),\n });\n const diff = diffPath(\n ctx.project.id,\n ctx.build.id,\n capture.story.id,\n capture.viewportName,\n browserOf(capture),\n );\n const overlay = await writeDiffOverlay(ctx.storage, diff, result, result.passed);\n await snapshots.update(snapshot.id, {\n status: diffStatus(result.passed, ctx.build.isDefault),\n diffPath: overlay,\n diffPixels: result.diffPixels,\n diffRatio: result.diffRatio,\n diffPassed: result.passed,\n infraHash: now.infraHash,\n });\n if (ctx.build.isDefault) {\n await refreshDefaultBaseline(input, snapshot.id, result.passed);\n }\n}\n\n/**\n * After a default-branch diff: a failed diff moves the baseline to the new screenshot; a passing\n * one backfills the fingerprint when it was missing or had components the capture now reports.\n */\nasync function refreshDefaultBaseline(\n input: BaselineCapture,\n snapshotId: string,\n passed: boolean,\n): Promise<void> {\n const { ctx, capture, screenshot, baseline } = input;\n const target = baselineFingerprintOf(input);\n if (!passed) {\n await upsertDefaultBaseline(ctx, snapshotId, capture, screenshot, target);\n } else if (!baseline.fingerprint || baseline.infraHash !== target.infraHash) {\n try {\n await new BaselineModel(ctx.db, ctx.tables, ctx.storage, ctx.secret).backfillFingerprint(\n baseline,\n target.infraHash,\n target.json,\n );\n } catch (error) {\n if (!(error instanceof BaselineWriteConflict || error instanceof ReferenceGenerationConflict))\n throw error;\n ctx.logger?.debug(\n { err: error, baselineId: baseline.id },\n \"skipped baseline fingerprint backfill after concurrent write\",\n );\n }\n }\n}\n\n/** Default-branch builds are authoritative: the captured screenshot becomes the baseline. */\nasync function upsertDefaultBaseline(\n ctx: CaptureContext,\n snapshotId: string,\n capture: RenderedSnapshot,\n screenshot: string,\n now: CaptureFingerprint,\n): Promise<void> {\n const baselines = new BaselineModel(ctx.db, ctx.tables, ctx.storage, ctx.secret);\n await baselines.upsert(\n ctx.project.id,\n capture.story.id,\n capture.viewportName,\n ctx.build.gitBranch,\n snapshotId,\n screenshot,\n now.infraHash,\n { mode: modeJsonOf(capture.viewport), browser: browserOf(capture), fingerprint: now.json },\n );\n}\n\n/** Snapshot status after diffing: default-branch changes are auto-approved, not queued for review. */\nexport function diffStatus(passed: boolean, isDefault: boolean): SnapshotStatus {\n if (passed) {\n return \"unchanged\";\n }\n return isDefault ? \"approved\" : \"changed\";\n}\n\n/** A failed capture fails the build; default-branch builds otherwise auto-approve. */\nfunction finalBuildStatus(\n isDefault: boolean,\n failedCount: number,\n hasCaptures: boolean,\n build: { changedCount: number },\n): BuildStatus {\n if (failedCount > 0) {\n return \"failed\";\n }\n if (isDefault || (hasCaptures && build.changedCount === 0)) {\n return \"approved\";\n }\n return \"reviewing\";\n}\n\n/**\n * Close out a capture: roll the snapshot counts up, set the build's final status from the failed\n * story ids, and (on the default branch) drop baselines of stories that no longer exist.\n */\nexport async function finalizeBuild(\n ctx: CaptureContext,\n storyIds: ReadonlySet<string>,\n failedStoryIds: ReadonlySet<string>,\n flakyFailedStoryIds: ReadonlySet<string> = new Set(),\n a11yFailedStoryIds: ReadonlySet<string> = new Set(),\n): Promise<void> {\n const builds = new BuildModel(ctx.db, ctx.tables, ctx.logger);\n if ((await builds.get(ctx.build.id))?.status === \"cancelled\") {\n ctx.logger?.debug({ buildId: ctx.build.id }, \"build is cancelled; finalize skipped\");\n return;\n }\n const build = await builds.updateCounts(ctx.build.id);\n const hasCaptures = storyIds.size > 0;\n const status = finalBuildStatus(ctx.build.isDefault, failedStoryIds.size, hasCaptures, build);\n // Flaky and a11y failures do not block: log warning if any failed non-blocking\n if (flakyFailedStoryIds.size > 0 && failedStoryIds.size === 0) {\n ctx.logger?.warn(\n { flakyStoryIds: [...flakyFailedStoryIds] },\n \"flaky stories failed (non-blocking)\",\n );\n await emitAttemptLog(ctx.recordLog, ctx.logger, \"warn\", \"flaky stories failed (non-blocking)\", {\n flakyStoryIds: [...flakyFailedStoryIds],\n });\n }\n if (a11yFailedStoryIds.size > 0 && failedStoryIds.size === 0) {\n ctx.logger?.warn(\n { a11yStoryIds: [...a11yFailedStoryIds] },\n \"a11y violations found (non-blocking)\",\n );\n await emitAttemptLog(\n ctx.recordLog,\n ctx.logger,\n \"warn\",\n \"a11y violations found (non-blocking)\",\n {\n a11yStoryIds: [...a11yFailedStoryIds],\n },\n );\n }\n await builds.setStatus(ctx.build.id, status);\n\n if (ctx.build.isDefault && hasCaptures) {\n const baselines = new BaselineModel(ctx.db, ctx.tables, ctx.storage, ctx.secret);\n await baselines.removeOrphans(ctx.project.id, new Set(storyIds));\n }\n}\n"],"mappings":"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"}
@@ -1,11 +1,9 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
- //#region src/capture/progress.d.ts
4
3
  /**
5
4
  * Called by a capture as it moves: a screenshot taken, a snapshot persisted, a stage done. Cheap
6
5
  * and synchronous, so the hot loops of render and persist can call it on every item.
7
6
  */
8
7
  type CaptureProgress = () => void;
9
- //#endregion
10
8
  export { CaptureProgress };
11
9
  //# sourceMappingURL=progress.d.mts.map
@@ -1,7 +1,7 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
- //#region src/capture/progress.ts
4
+ // src/capture/progress.ts
5
5
  /** Shortest gap between two lease touches: a forked child sends one IPC message per touch. */
6
6
  const PROGRESS_THROTTLE_MS = 1e3;
7
7
  /**
@@ -22,7 +22,6 @@ function leaseProgress(lease, everyMs = PROGRESS_THROTTLE_MS, now = Date.now) {
22
22
  }
23
23
  };
24
24
  }
25
- //#endregion
26
25
  export { PROGRESS_THROTTLE_MS, leaseProgress };
27
26
 
28
27
  //# sourceMappingURL=progress.mjs.map
@@ -1,6 +1,5 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
- //#region src/capture/publish/accept.d.ts
4
3
  /**
5
4
  * The one rule for when publishing accepts a build (ADR 0006, decision 2). Pure: callers read the
6
5
  * facts, apply the verdict, and stay free of branching.
@@ -34,6 +33,5 @@ interface PublishFacts {
34
33
  * the upload), so nothing is asked of a reviewer and nothing is reported as one.
35
34
  */
36
35
  declare function publishVerdict(facts: PublishFacts): "approved" | "failed" | "unchanged";
37
- //#endregion
38
36
  export { PublishFacts, publishVerdict };
39
37
  //# sourceMappingURL=accept.d.mts.map
@@ -1,7 +1,7 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
- //#region src/capture/publish/accept.ts
4
+ // src/capture/publish/accept.ts
5
5
  /**
6
6
  * What publishing does to the build's status.
7
7
  *
@@ -22,7 +22,6 @@ function publishVerdict(facts) {
22
22
  if (!facts.published) return "failed";
23
23
  return facts.defaultBranch || !facts.reviewEnabled ? "approved" : "unchanged";
24
24
  }
25
- //#endregion
26
25
  export { publishVerdict };
27
26
 
28
27
  //# sourceMappingURL=accept.mjs.map
@@ -1,10 +1,9 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
- import { DatabaseAdapter } from "../../adapter/database.mjs";
4
3
  import { Logger } from "../../logger.mjs";
4
+ import { DatabaseAdapter } from "../../adapter/database.mjs";
5
5
  import { Build } from "../../schema/build.mjs";
6
6
  import { Project } from "../../schema/project.mjs";
7
- //#region src/capture/publish/complete.d.ts
8
7
  /** How publishing ended. */
9
8
  type PublishOutcome = {
10
9
  ok: true;
@@ -41,6 +40,5 @@ interface PublishCompleterOptions {
41
40
  }
42
41
  /** A completer over `options.db`. */
43
42
  declare function createPublishCompleter(options: PublishCompleterOptions): PublishCompleter;
44
- //#endregion
45
43
  export { AfterPublishContext, AfterPublishHook, PublishCompleter, PublishCompleterOptions, PublishOutcome, createPublishCompleter };
46
44
  //# sourceMappingURL=complete.d.mts.map
@@ -4,7 +4,7 @@ import.meta.url;
4
4
  import { BuildModel } from "../../models/build.mjs";
5
5
  import { ProjectModel } from "../../models/project.mjs";
6
6
  import { publishVerdict } from "./accept.mjs";
7
- //#region src/capture/publish/complete.ts
7
+ // src/capture/publish/complete.ts
8
8
  /** A completer over `options.db`. */
9
9
  function createPublishCompleter(options) {
10
10
  return { complete: async (buildId, outcome) => {
@@ -58,7 +58,6 @@ async function runHooks(options, context) {
58
58
  logger.error({ err: error }, "afterPublish hook failed");
59
59
  }
60
60
  }
61
- //#endregion
62
61
  export { createPublishCompleter };
63
62
 
64
63
  //# sourceMappingURL=complete.mjs.map
@@ -2,7 +2,6 @@ import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { ExtractionLimits } from "./limits.mjs";
4
4
  import { Readable } from "node:stream";
5
- //#region src/capture/publish/extract.d.ts
6
5
  /** What an extraction produced. */
7
6
  interface ExtractionResult {
8
7
  entries: number;
@@ -15,6 +14,5 @@ interface ExtractionResult {
15
14
  * destroyed at once, and the files written so far are left for the caller's scratch cleanup.
16
15
  */
17
16
  declare function extractZipStream(source: Readable, root: string, limits: ExtractionLimits): Promise<ExtractionResult>;
18
- //#endregion
19
17
  export { ExtractionResult, extractZipStream };
20
18
  //# sourceMappingURL=extract.d.mts.map
@@ -1,14 +1,14 @@
1
- import "node:path";
1
+ import { dirname, isAbsolute, join, resolve, sep } from "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
+ import { ZipInvalidError } from "./invalid-zip.mjs";
4
5
  import { ZipLimitError } from "./limits.mjs";
5
- import { dirname, isAbsolute, join, resolve, sep } from "node:path";
6
6
  import { mkdir } from "node:fs/promises";
7
7
  import { createWriteStream } from "node:fs";
8
8
  import { Transform } from "node:stream";
9
9
  import { pipeline } from "node:stream/promises";
10
10
  import { Parse } from "unzipper";
11
- //#region src/capture/publish/extract.ts
11
+ // src/capture/publish/extract.ts
12
12
  /**
13
13
  * Streaming, capped extraction of an uploaded Storybook zip into a directory. Entry count and
14
14
  * uncompressed bytes are counted while the archive streams (never from a header or from
@@ -61,7 +61,9 @@ var Extraction = class {
61
61
  parser.on("entry", (entry) => {
62
62
  this.#entry(entry);
63
63
  });
64
- parser.on("error", this.#fail);
64
+ parser.on("error", () => {
65
+ this.#fail(new ZipInvalidError("Invalid Storybook zip"));
66
+ });
65
67
  parser.on("close", () => {
66
68
  settle(null);
67
69
  });
@@ -113,7 +115,6 @@ function unsafePath(root, entryPath) {
113
115
  const candidate = resolve(join(root, entryPath));
114
116
  return candidate !== root && !candidate.startsWith(root + sep);
115
117
  }
116
- //#endregion
117
118
  export { extractZipStream };
118
119
 
119
120
  //# sourceMappingURL=extract.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"extract.mjs","names":[],"sources":["../../../src/capture/publish/extract.ts"],"sourcesContent":["/**\n * Streaming, capped extraction of an uploaded Storybook zip into a directory. Entry count and\n * uncompressed bytes are counted while the archive streams (never from a header or from\n * `Content-Length`), and a path that leaves the target is refused.\n */\nimport { createWriteStream } from \"node:fs\";\nimport { mkdir } from \"node:fs/promises\";\nimport { dirname, isAbsolute, join, resolve, sep } from \"node:path\";\nimport { Transform, type Readable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport { Parse, type Entry } from \"unzipper\";\nimport { ZipLimitError, type ExtractionLimits } from \"./limits.ts\";\n\n/** What an extraction produced. */\nexport interface ExtractionResult {\n entries: number;\n bytes: number;\n}\n\n/**\n * Extract `source` (a zip stream) under `root`, which must exist.\n *\n * @throws {ZipLimitError} When a cap is exceeded or an entry path escapes `root`; the source is\n * destroyed at once, and the files written so far are left for the caller's scratch cleanup.\n */\nexport async function extractZipStream(\n source: Readable,\n root: string,\n limits: ExtractionLimits,\n): Promise<ExtractionResult> {\n const run = new Extraction(resolve(root), limits);\n const failure = await run.parse(source);\n // Writes may still be in flight after a failure; settle them before anyone removes the files.\n const outcomes = await Promise.allSettled(run.writes);\n if (failure) {\n throw failure;\n }\n const writeError = outcomes.find(\n (outcome): outcome is PromiseRejectedResult => outcome.status === \"rejected\",\n );\n if (writeError) {\n throw writeError.reason;\n }\n return { entries: run.entries, bytes: run.bytes };\n}\n\nfunction ignore(): void {}\n\n/** State of one extraction. */\nclass Extraction {\n entries = 0;\n bytes = 0;\n readonly writes: Promise<void>[] = [];\n readonly root: string;\n readonly limits: ExtractionLimits;\n #failed = false;\n #fail: (error: unknown) => void = ignore;\n\n constructor(root: string, limits: ExtractionLimits) {\n this.root = root;\n this.limits = limits;\n }\n\n /** Resolves with the first failure, or null once the archive was read to its end. */\n // oxlint-disable-next-line typescript/promise-function-async -- executor-style wrapper around stream events\n parse(source: Readable): Promise<unknown> {\n return new Promise((settle) => {\n const parser = Parse();\n this.#fail = (error: unknown): void => {\n if (!this.#failed) {\n this.#failed = true;\n source.destroy();\n parser.destroy();\n settle(error);\n }\n };\n parser.on(\"entry\", (entry: Entry) => {\n this.#entry(entry);\n });\n parser.on(\"error\", this.#fail);\n parser.on(\"close\", () => {\n settle(null);\n });\n source.on(\"error\", this.#fail);\n source.pipe(parser);\n });\n }\n\n #entry(entry: Entry): void {\n this.entries += 1;\n const problem = this.#problemWith(entry);\n if (problem) {\n this.#fail(problem);\n }\n if (this.#failed || entry.type === \"Directory\") {\n entry.autodrain();\n return;\n }\n this.#write(entry);\n }\n\n #problemWith(entry: Entry): ZipLimitError | null {\n if (this.entries > this.limits.maxEntries) {\n return new ZipLimitError(`Zip holds more than ${this.limits.maxEntries} entries`);\n }\n if (unsafePath(this.root, entry.path)) {\n return new ZipLimitError(`Blocked path traversal in uploaded Storybook: ${entry.path}`);\n }\n return null;\n }\n\n #write(entry: Entry): void {\n const target = join(this.root, entry.path);\n const done = (async (): Promise<void> => {\n await mkdir(dirname(target), { recursive: true });\n await pipeline(entry, this.#counter(), createWriteStream(target));\n })();\n this.writes.push(done);\n done.catch(ignore); // Aggregated through allSettled by the caller\n }\n\n /** Counts the bytes that pass; past the cap the whole extraction fails at once. */\n #counter(): Transform {\n return new Transform({\n transform: (chunk: Buffer, _encoding, callback) => {\n this.bytes += chunk.length;\n if (this.bytes > this.limits.maxBytes) {\n const error = new ZipLimitError(`Zip expands beyond ${this.limits.maxBytes} bytes`);\n this.#fail(error);\n callback(error);\n return;\n }\n callback(null, chunk);\n },\n });\n }\n}\n\n/** Absolute paths, drive letters and anything resolving outside `root`. */\nfunction unsafePath(root: string, entryPath: string): boolean {\n if (isAbsolute(entryPath) || /^[a-zA-Z]:/u.test(entryPath)) {\n return true;\n }\n const candidate = resolve(join(root, entryPath));\n return candidate !== root && !candidate.startsWith(root + sep);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAyBA,eAAsB,iBACpB,QACA,MACA,QAC2B;CAC3B,MAAM,MAAM,IAAI,WAAW,QAAQ,IAAI,GAAG,MAAM;CAChD,MAAM,UAAU,MAAM,IAAI,MAAM,MAAM;CAEtC,MAAM,WAAW,MAAM,QAAQ,WAAW,IAAI,MAAM;CACpD,IAAI,SACF,MAAM;CAER,MAAM,aAAa,SAAS,MACzB,YAA8C,QAAQ,WAAW,UACpE;CACA,IAAI,YACF,MAAM,WAAW;CAEnB,OAAO;EAAE,SAAS,IAAI;EAAS,OAAO,IAAI;CAAM;AAClD;AAEA,SAAS,SAAe,CAAC;;AAGzB,IAAM,aAAN,MAAiB;CACf,UAAU;CACV,QAAQ;CACR,SAAmC,CAAC;CACpC;CACA;CACA,UAAU;CACV,QAAkC;CAElC,YAAY,MAAc,QAA0B;EAClD,KAAK,OAAO;EACZ,KAAK,SAAS;CAChB;;CAIA,MAAM,QAAoC;EACxC,OAAO,IAAI,SAAS,WAAW;GAC7B,MAAM,SAAS,MAAM;GACrB,KAAK,SAAS,UAAyB;IACrC,IAAI,CAAC,KAAK,SAAS;KACjB,KAAK,UAAU;KACf,OAAO,QAAQ;KACf,OAAO,QAAQ;KACf,OAAO,KAAK;IACd;GACF;GACA,OAAO,GAAG,UAAU,UAAiB;IACnC,KAAK,OAAO,KAAK;GACnB,CAAC;GACD,OAAO,GAAG,SAAS,KAAK,KAAK;GAC7B,OAAO,GAAG,eAAe;IACvB,OAAO,IAAI;GACb,CAAC;GACD,OAAO,GAAG,SAAS,KAAK,KAAK;GAC7B,OAAO,KAAK,MAAM;EACpB,CAAC;CACH;CAEA,OAAO,OAAoB;EACzB,KAAK,WAAW;EAChB,MAAM,UAAU,KAAK,aAAa,KAAK;EACvC,IAAI,SACF,KAAK,MAAM,OAAO;EAEpB,IAAI,KAAK,WAAW,MAAM,SAAS,aAAa;GAC9C,MAAM,UAAU;GAChB;EACF;EACA,KAAK,OAAO,KAAK;CACnB;CAEA,aAAa,OAAoC;EAC/C,IAAI,KAAK,UAAU,KAAK,OAAO,YAC7B,OAAO,IAAI,cAAc,uBAAuB,KAAK,OAAO,WAAW,SAAS;EAElF,IAAI,WAAW,KAAK,MAAM,MAAM,IAAI,GAClC,OAAO,IAAI,cAAc,iDAAiD,MAAM,MAAM;EAExF,OAAO;CACT;CAEA,OAAO,OAAoB;EACzB,MAAM,SAAS,KAAK,KAAK,MAAM,MAAM,IAAI;EACzC,MAAM,QAAQ,YAA2B;GACvC,MAAM,MAAM,QAAQ,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;GAChD,MAAM,SAAS,OAAO,KAAK,SAAS,GAAG,kBAAkB,MAAM,CAAC;EAClE,EAAA,CAAG;EACH,KAAK,OAAO,KAAK,IAAI;EACrB,KAAK,MAAM,MAAM;CACnB;;CAGA,WAAsB;EACpB,OAAO,IAAI,UAAU,EACnB,YAAY,OAAe,WAAW,aAAa;GACjD,KAAK,SAAS,MAAM;GACpB,IAAI,KAAK,QAAQ,KAAK,OAAO,UAAU;IACrC,MAAM,QAAQ,IAAI,cAAc,sBAAsB,KAAK,OAAO,SAAS,OAAO;IAClF,KAAK,MAAM,KAAK;IAChB,SAAS,KAAK;IACd;GACF;GACA,SAAS,MAAM,KAAK;EACtB,EACF,CAAC;CACH;AACF;;AAGA,SAAS,WAAW,MAAc,WAA4B;CAC5D,IAAI,WAAW,SAAS,KAAK,cAAc,KAAK,SAAS,GACvD,OAAO;CAET,MAAM,YAAY,QAAQ,KAAK,MAAM,SAAS,CAAC;CAC/C,OAAO,cAAc,QAAQ,CAAC,UAAU,WAAW,OAAO,GAAG;AAC/D"}
1
+ {"version":3,"file":"extract.mjs","names":[],"sources":["../../../src/capture/publish/extract.ts"],"sourcesContent":["/**\n * Streaming, capped extraction of an uploaded Storybook zip into a directory. Entry count and\n * uncompressed bytes are counted while the archive streams (never from a header or from\n * `Content-Length`), and a path that leaves the target is refused.\n */\nimport { createWriteStream } from \"node:fs\";\nimport { mkdir } from \"node:fs/promises\";\nimport { dirname, isAbsolute, join, resolve, sep } from \"node:path\";\nimport { Transform, type Readable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport { Parse, type Entry } from \"unzipper\";\nimport { ZipInvalidError } from \"./invalid-zip.ts\";\nimport { ZipLimitError, type ExtractionLimits } from \"./limits.ts\";\n\n/** What an extraction produced. */\nexport interface ExtractionResult {\n entries: number;\n bytes: number;\n}\n\n/**\n * Extract `source` (a zip stream) under `root`, which must exist.\n *\n * @throws {ZipLimitError} When a cap is exceeded or an entry path escapes `root`; the source is\n * destroyed at once, and the files written so far are left for the caller's scratch cleanup.\n */\nexport async function extractZipStream(\n source: Readable,\n root: string,\n limits: ExtractionLimits,\n): Promise<ExtractionResult> {\n const run = new Extraction(resolve(root), limits);\n const failure = await run.parse(source);\n // Writes may still be in flight after a failure; settle them before anyone removes the files.\n const outcomes = await Promise.allSettled(run.writes);\n if (failure) {\n throw failure;\n }\n const writeError = outcomes.find(\n (outcome): outcome is PromiseRejectedResult => outcome.status === \"rejected\",\n );\n if (writeError) {\n throw writeError.reason;\n }\n return { entries: run.entries, bytes: run.bytes };\n}\n\nfunction ignore(): void {}\n\n/** State of one extraction. */\nclass Extraction {\n entries = 0;\n bytes = 0;\n readonly writes: Promise<void>[] = [];\n readonly root: string;\n readonly limits: ExtractionLimits;\n #failed = false;\n #fail: (error: unknown) => void = ignore;\n\n constructor(root: string, limits: ExtractionLimits) {\n this.root = root;\n this.limits = limits;\n }\n\n /** Resolves with the first failure, or null once the archive was read to its end. */\n // oxlint-disable-next-line typescript/promise-function-async -- executor-style wrapper around stream events\n parse(source: Readable): Promise<unknown> {\n return new Promise((settle) => {\n const parser = Parse();\n this.#fail = (error: unknown): void => {\n if (!this.#failed) {\n this.#failed = true;\n source.destroy();\n parser.destroy();\n settle(error);\n }\n };\n parser.on(\"entry\", (entry: Entry) => {\n this.#entry(entry);\n });\n parser.on(\"error\", () => {\n this.#fail(new ZipInvalidError(\"Invalid Storybook zip\"));\n });\n parser.on(\"close\", () => {\n settle(null);\n });\n source.on(\"error\", this.#fail);\n source.pipe(parser);\n });\n }\n\n #entry(entry: Entry): void {\n this.entries += 1;\n const problem = this.#problemWith(entry);\n if (problem) {\n this.#fail(problem);\n }\n if (this.#failed || entry.type === \"Directory\") {\n entry.autodrain();\n return;\n }\n this.#write(entry);\n }\n\n #problemWith(entry: Entry): ZipLimitError | null {\n if (this.entries > this.limits.maxEntries) {\n return new ZipLimitError(`Zip holds more than ${this.limits.maxEntries} entries`);\n }\n if (unsafePath(this.root, entry.path)) {\n return new ZipLimitError(`Blocked path traversal in uploaded Storybook: ${entry.path}`);\n }\n return null;\n }\n\n #write(entry: Entry): void {\n const target = join(this.root, entry.path);\n const done = (async (): Promise<void> => {\n await mkdir(dirname(target), { recursive: true });\n await pipeline(entry, this.#counter(), createWriteStream(target));\n })();\n this.writes.push(done);\n done.catch(ignore); // Aggregated through allSettled by the caller\n }\n\n /** Counts the bytes that pass; past the cap the whole extraction fails at once. */\n #counter(): Transform {\n return new Transform({\n transform: (chunk: Buffer, _encoding, callback) => {\n this.bytes += chunk.length;\n if (this.bytes > this.limits.maxBytes) {\n const error = new ZipLimitError(`Zip expands beyond ${this.limits.maxBytes} bytes`);\n this.#fail(error);\n callback(error);\n return;\n }\n callback(null, chunk);\n },\n });\n }\n}\n\n/** Absolute paths, drive letters and anything resolving outside `root`. */\nfunction unsafePath(root: string, entryPath: string): boolean {\n if (isAbsolute(entryPath) || /^[a-zA-Z]:/u.test(entryPath)) {\n return true;\n }\n const candidate = resolve(join(root, entryPath));\n return candidate !== root && !candidate.startsWith(root + sep);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA0BA,eAAsB,iBACpB,QACA,MACA,QAC2B;CAC3B,MAAM,MAAM,IAAI,WAAW,QAAQ,IAAI,GAAG,MAAM;CAChD,MAAM,UAAU,MAAM,IAAI,MAAM,MAAM;CAEtC,MAAM,WAAW,MAAM,QAAQ,WAAW,IAAI,MAAM;CACpD,IAAI,SACF,MAAM;CAER,MAAM,aAAa,SAAS,MACzB,YAA8C,QAAQ,WAAW,UACpE;CACA,IAAI,YACF,MAAM,WAAW;CAEnB,OAAO;EAAE,SAAS,IAAI;EAAS,OAAO,IAAI;CAAM;AAClD;AAEA,SAAS,SAAe,CAAC;;AAGzB,IAAM,aAAN,MAAiB;CACf,UAAU;CACV,QAAQ;CACR,SAAmC,CAAC;CACpC;CACA;CACA,UAAU;CACV,QAAkC;CAElC,YAAY,MAAc,QAA0B;EAClD,KAAK,OAAO;EACZ,KAAK,SAAS;CAChB;;CAIA,MAAM,QAAoC;EACxC,OAAO,IAAI,SAAS,WAAW;GAC7B,MAAM,SAAS,MAAM;GACrB,KAAK,SAAS,UAAyB;IACrC,IAAI,CAAC,KAAK,SAAS;KACjB,KAAK,UAAU;KACf,OAAO,QAAQ;KACf,OAAO,QAAQ;KACf,OAAO,KAAK;IACd;GACF;GACA,OAAO,GAAG,UAAU,UAAiB;IACnC,KAAK,OAAO,KAAK;GACnB,CAAC;GACD,OAAO,GAAG,eAAe;IACvB,KAAK,MAAM,IAAI,gBAAgB,uBAAuB,CAAC;GACzD,CAAC;GACD,OAAO,GAAG,eAAe;IACvB,OAAO,IAAI;GACb,CAAC;GACD,OAAO,GAAG,SAAS,KAAK,KAAK;GAC7B,OAAO,KAAK,MAAM;EACpB,CAAC;CACH;CAEA,OAAO,OAAoB;EACzB,KAAK,WAAW;EAChB,MAAM,UAAU,KAAK,aAAa,KAAK;EACvC,IAAI,SACF,KAAK,MAAM,OAAO;EAEpB,IAAI,KAAK,WAAW,MAAM,SAAS,aAAa;GAC9C,MAAM,UAAU;GAChB;EACF;EACA,KAAK,OAAO,KAAK;CACnB;CAEA,aAAa,OAAoC;EAC/C,IAAI,KAAK,UAAU,KAAK,OAAO,YAC7B,OAAO,IAAI,cAAc,uBAAuB,KAAK,OAAO,WAAW,SAAS;EAElF,IAAI,WAAW,KAAK,MAAM,MAAM,IAAI,GAClC,OAAO,IAAI,cAAc,iDAAiD,MAAM,MAAM;EAExF,OAAO;CACT;CAEA,OAAO,OAAoB;EACzB,MAAM,SAAS,KAAK,KAAK,MAAM,MAAM,IAAI;EACzC,MAAM,QAAQ,YAA2B;GACvC,MAAM,MAAM,QAAQ,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;GAChD,MAAM,SAAS,OAAO,KAAK,SAAS,GAAG,kBAAkB,MAAM,CAAC;EAClE,EAAA,CAAG;EACH,KAAK,OAAO,KAAK,IAAI;EACrB,KAAK,MAAM,MAAM;CACnB;;CAGA,WAAsB;EACpB,OAAO,IAAI,UAAU,EACnB,YAAY,OAAe,WAAW,aAAa;GACjD,KAAK,SAAS,MAAM;GACpB,IAAI,KAAK,QAAQ,KAAK,OAAO,UAAU;IACrC,MAAM,QAAQ,IAAI,cAAc,sBAAsB,KAAK,OAAO,SAAS,OAAO;IAClF,KAAK,MAAM,KAAK;IAChB,SAAS,KAAK;IACd;GACF;GACA,SAAS,MAAM,KAAK;EACtB,EACF,CAAC;CACH;AACF;;AAGA,SAAS,WAAW,MAAc,WAA4B;CAC5D,IAAI,WAAW,SAAS,KAAK,cAAc,KAAK,SAAS,GACvD,OAAO;CAET,MAAM,YAAY,QAAQ,KAAK,MAAM,SAAS,CAAC;CAC/C,OAAO,cAAc,QAAQ,CAAC,UAAU,WAAW,OAAO,GAAG;AAC/D"}
@@ -1,12 +1,11 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
- import { DatabaseAdapter } from "../../adapter/database.mjs";
4
3
  import { Logger } from "../../logger.mjs";
4
+ import { DatabaseAdapter } from "../../adapter/database.mjs";
5
5
  import { StorageAdapter } from "../../adapter/storage.mjs";
6
6
  import { ExtractionLimits } from "./limits.mjs";
7
7
  import { KindHandler } from "../consumer/types.mjs";
8
8
  import { PublishCompleter } from "./complete.mjs";
9
- //#region src/capture/publish/handler.d.ts
10
9
  /** What {@link createPublishHandler} needs. */
11
10
  interface PublishHandlerOptions {
12
11
  db: DatabaseAdapter;
@@ -23,6 +22,5 @@ interface PublishHandlerOptions {
23
22
  * (same keys) and leaves an already accepted build alone.
24
23
  */
25
24
  declare function createPublishHandler(options: PublishHandlerOptions): KindHandler;
26
- //#endregion
27
25
  export { PublishHandlerOptions, createPublishHandler };
28
26
  //# sourceMappingURL=handler.d.mts.map
@@ -4,7 +4,7 @@ import.meta.url;
4
4
  import { BuildModel } from "../../models/build.mjs";
5
5
  import { ZipLimitError } from "./limits.mjs";
6
6
  import { publishStoredZip } from "./run.mjs";
7
- //#region src/capture/publish/handler.ts
7
+ // src/capture/publish/handler.ts
8
8
  /** A `publish` job needs the build to publish. */
9
9
  function hasBuild(job) {
10
10
  return typeof job.buildId === "string" && job.buildId.length > 0;
@@ -56,7 +56,6 @@ async function publishOnce(options, job, context, projectId) {
56
56
  function messageOf(error) {
57
57
  return error instanceof Error ? error.message : String(error);
58
58
  }
59
- //#endregion
60
59
  export { createPublishHandler };
61
60
 
62
61
  //# sourceMappingURL=handler.mjs.map
@@ -7,4 +7,5 @@ import { PublishFacts, publishVerdict } from "./accept.mjs";
7
7
  import { publishingOf } from "./published.mjs";
8
8
  import { ExtractionResult, extractZipStream } from "./extract.mjs";
9
9
  import { PUBLISH_SCRATCH_ID, PublishZipOptions, publishStoredZip } from "./run.mjs";
10
- export { type AfterPublishContext, type AfterPublishHook, DEFAULT_MAX_INLINE_UNZIP_SIZE, DEFAULT_MAX_UNZIPPED_BYTES, DEFAULT_MAX_ZIP_ENTRIES, type ExtractionLimits, type ExtractionResult, FILE_BY_FILE_HINT, PUBLISH_SCRATCH_ID, type PublishCompleter, type PublishCompleterOptions, type PublishFacts, type PublishHandlerOptions, type PublishLimits, type PublishOutcome, type PublishZipOptions, ZipLimitError, createPublishCompleter, createPublishHandler, extractZipStream, extractionLimitsOf, publishRoute, publishStoredZip, publishVerdict, publishingOf, resolvePublishLimits };
10
+ import { ZipInvalidError } from "./invalid-zip.mjs";
11
+ export { type AfterPublishContext, type AfterPublishHook, DEFAULT_MAX_INLINE_UNZIP_SIZE, DEFAULT_MAX_UNZIPPED_BYTES, DEFAULT_MAX_ZIP_ENTRIES, type ExtractionLimits, type ExtractionResult, FILE_BY_FILE_HINT, PUBLISH_SCRATCH_ID, type PublishCompleter, type PublishCompleterOptions, type PublishFacts, type PublishHandlerOptions, type PublishLimits, type PublishOutcome, type PublishZipOptions, ZipInvalidError, ZipLimitError, createPublishCompleter, createPublishHandler, extractZipStream, extractionLimitsOf, publishRoute, publishStoredZip, publishVerdict, publishingOf, resolvePublishLimits };
@@ -0,0 +1,8 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ /** An archive that cannot be parsed; retrying the same bytes cannot help. */
4
+ declare class ZipInvalidError extends Error {
5
+ readonly name = "ZipInvalidError";
6
+ }
7
+ export { ZipInvalidError };
8
+ //# sourceMappingURL=invalid-zip.d.mts.map
@@ -0,0 +1,11 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ // src/capture/publish/invalid-zip.ts
5
+ /** An archive that cannot be parsed; retrying the same bytes cannot help. */
6
+ var ZipInvalidError = class extends Error {
7
+ name = "ZipInvalidError";
8
+ };
9
+ export { ZipInvalidError };
10
+
11
+ //# sourceMappingURL=invalid-zip.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invalid-zip.mjs","names":[],"sources":["../../../src/capture/publish/invalid-zip.ts"],"sourcesContent":["/** An archive that cannot be parsed; retrying the same bytes cannot help. */\nexport class ZipInvalidError extends Error {\n override readonly name = \"ZipInvalidError\";\n}\n"],"mappings":";;;;;AACA,IAAa,kBAAb,cAAqC,MAAM;CACzC,OAAyB;AAC3B"}
@@ -1,7 +1,6 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { ShelfConfig } from "../../config.mjs";
4
- //#region src/capture/publish/limits.d.ts
5
4
  /** Zip size (bytes) up to which a zip is extracted inline in the upload request: 50 MB. */
6
5
  declare const DEFAULT_MAX_INLINE_UNZIP_SIZE: number;
7
6
  /** Entries an uploaded Storybook zip may hold. A large Storybook has some thousands of files. */
@@ -32,6 +31,5 @@ declare class ZipLimitError extends Error {
32
31
  }
33
32
  /** Message of the 413 an over-limit zip gets on a host with no `publish` consumer. */
34
33
  declare const FILE_BY_FILE_HINT: string;
35
- //#endregion
36
34
  export { DEFAULT_MAX_INLINE_UNZIP_SIZE, DEFAULT_MAX_UNZIPPED_BYTES, DEFAULT_MAX_ZIP_ENTRIES, ExtractionLimits, FILE_BY_FILE_HINT, PublishLimits, ZipLimitError, extractionLimitsOf, publishRoute, resolvePublishLimits };
37
35
  //# sourceMappingURL=limits.d.mts.map
@@ -1,7 +1,7 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
- //#region src/capture/publish/limits.ts
4
+ // src/capture/publish/limits.ts
5
5
  /** Zip size (bytes) up to which a zip is extracted inline in the upload request: 50 MB. */
6
6
  const DEFAULT_MAX_INLINE_UNZIP_SIZE = 52428800;
7
7
  /** Entries an uploaded Storybook zip may hold. A large Storybook has some thousands of files. */
@@ -33,7 +33,6 @@ var ZipLimitError = class extends Error {
33
33
  };
34
34
  /** Message of the 413 an over-limit zip gets on a host with no `publish` consumer. */
35
35
  const FILE_BY_FILE_HINT = "Zip is larger than the inline publish limit and this host runs no publish consumer. Upload the Storybook file by file instead: POST /dedup, then PUT /content, then POST /manifest (the storyshelf CLI does this when it is given a directory).";
36
- //#endregion
37
36
  export { DEFAULT_MAX_INLINE_UNZIP_SIZE, DEFAULT_MAX_UNZIPPED_BYTES, DEFAULT_MAX_ZIP_ENTRIES, FILE_BY_FILE_HINT, ZipLimitError, extractionLimitsOf, publishRoute, resolvePublishLimits };
38
37
 
39
38
  //# sourceMappingURL=limits.mjs.map
@@ -3,13 +3,11 @@ import __tsdown_shims_url from 'node:url';
3
3
  import { StorageAdapter } from "../../adapter/storage.mjs";
4
4
  import { PublishingState } from "../../adapters/notifier/build-state.mjs";
5
5
  import { Build } from "../../schema/build.mjs";
6
- //#region src/capture/publish/published.d.ts
7
6
  /**
8
7
  * How publishing the build stands. The manifest is written last, so its presence means done. A
9
8
  * `failed` or `cancelled` build without one did not publish (or never uploaded); any other build waits. Without
10
9
  * a storage adapter nothing can be read, so publishing reads as pending.
11
10
  */
12
11
  declare function publishingOf(storage: StorageAdapter | undefined, build: Pick<Build, "id" | "projectId" | "status">): Promise<PublishingState>;
13
- //#endregion
14
12
  export { publishingOf };
15
13
  //# sourceMappingURL=published.d.mts.map
@@ -2,7 +2,7 @@ import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
4
  import { storybookDir } from "../../paths.mjs";
5
- //#region src/capture/publish/published.ts
5
+ // src/capture/publish/published.ts
6
6
  /**
7
7
  * How publishing the build stands. The manifest is written last, so its presence means done. A
8
8
  * `failed` or `cancelled` build without one did not publish (or never uploaded); any other build waits. Without
@@ -12,7 +12,6 @@ async function publishingOf(storage, build) {
12
12
  if (storage !== void 0 && await storage.exists(`${storybookDir(build.projectId, build.id)}/manifest.json`)) return "success";
13
13
  return build.status === "failed" || build.status === "cancelled" ? "failure" : "pending";
14
14
  }
15
- //#endregion
16
15
  export { publishingOf };
17
16
 
18
17
  //# sourceMappingURL=published.mjs.map
@@ -1,10 +1,9 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
- import { DatabaseAdapter } from "../../adapter/database.mjs";
4
3
  import { Logger } from "../../logger.mjs";
4
+ import { DatabaseAdapter } from "../../adapter/database.mjs";
5
5
  import { StorageAdapter } from "../../adapter/storage.mjs";
6
6
  import { ExtractionLimits } from "./limits.mjs";
7
- //#region src/capture/publish/run.d.ts
8
7
  /** Scratch id publishing uses, so it never shares a directory with the build's capture. */
9
8
  declare const PUBLISH_SCRATCH_ID = "publish";
10
9
  /** What {@link publishStoredZip} works with. */
@@ -28,6 +27,5 @@ declare function publishStoredZip(options: PublishZipOptions, target: {
28
27
  projectId: string;
29
28
  buildId: string;
30
29
  }): Promise<void>;
31
- //#endregion
32
30
  export { PUBLISH_SCRATCH_ID, PublishZipOptions, publishStoredZip };
33
31
  //# sourceMappingURL=run.d.mts.map
@@ -3,7 +3,7 @@ import "node:url";
3
3
  import.meta.url;
4
4
  import { removeShardScratch } from "../scratch.mjs";
5
5
  import { extractStorybookToScratch, persistStorybookStatics } from "../statics.mjs";
6
- //#region src/capture/publish/run.ts
6
+ // src/capture/publish/run.ts
7
7
  /** Scratch id publishing uses, so it never shares a directory with the build's capture. */
8
8
  const PUBLISH_SCRATCH_ID = "publish";
9
9
  /**
@@ -24,7 +24,6 @@ async function publishStoredZip(options, target) {
24
24
  await removeShardScratch(scratchDir, projectId, buildId, PUBLISH_SCRATCH_ID, logger);
25
25
  }
26
26
  }
27
- //#endregion
28
27
  export { PUBLISH_SCRATCH_ID, publishStoredZip };
29
28
 
30
29
  //# sourceMappingURL=run.mjs.map
@@ -1,11 +1,10 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
- import { DatabaseAdapter } from "../adapter/database.mjs";
4
3
  import { Logger } from "../logger.mjs";
4
+ import { DatabaseAdapter } from "../adapter/database.mjs";
5
5
  import { StorageAdapter } from "../adapter/storage.mjs";
6
6
  import { Build } from "../schema/build.mjs";
7
7
  import { Project } from "../schema/project.mjs";
8
- //#region src/capture/rediff.d.ts
9
8
  /** Dependencies for {@link rediffBuild}. */
10
9
  interface RediffDeps {
11
10
  db: DatabaseAdapter;
@@ -30,6 +29,5 @@ declare const REDIFFABLE_BUILD_STATUSES: ReadonlySet<string>;
30
29
  * rendered. The caller refreshes the build rollup afterwards.
31
30
  */
32
31
  declare function rediffBuild(deps: RediffDeps, project: Project, build: Build): Promise<RediffResult>;
33
- //#endregion
34
32
  export { REDIFFABLE_BUILD_STATUSES, RediffDeps, RediffResult, rediffBuild };
35
33
  //# sourceMappingURL=rediff.d.mts.map