@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":"storybook-urls.mjs","names":[],"sources":["../../src/capture/storybook-urls.ts"],"sourcesContent":["/**\n * The signed Storybook URL of a render (ADR 0007, decision 5): minted at the moment a pass starts,\n * only for a target whose browser cannot reach this host (`reach: \"url\"`). The URL is the build's\n * Storybook root, `<publicOrigin>/_s/<token>/`; it carries the token and is never logged. Its\n * lifetime comes from the pass's render budget unless the project sets one (`storybookUrlTtl`).\n */\nimport type { CaptureRunner, CaptureTarget } from \"../adapters/capture-runner.ts\";\nimport type { Build } from \"../schema/build.ts\";\nimport type { Project } from \"../schema/project.ts\";\nimport type { SecretInput } from \"../utils/encrypt.ts\";\nimport {\n signStorybookToken,\n storybookTokenPath,\n storybookUrlTtl,\n} from \"../utils/storybook-token.ts\";\n\n/** What a pass tells the source: how long it may run, which sizes the token's lifetime. */\nexport interface StorybookUrlPass {\n /** The render budget of the pass in ms (`renderBudgetMs`); unset when the pass has none. */\n budgetMs?: number | undefined;\n}\n\n/** Gives the URL a target's browser opens, or `undefined` when it needs none (or none can be made). */\nexport type StorybookUrlSource = ((\n target: CaptureTarget,\n pass?: StorybookUrlPass,\n) => Promise<string | undefined>) & {\n /**\n * The lifetime in seconds a token minted for a pass of this budget gets (the project's override\n * or the derived, capped one). A pass that outlasts it is rendered in slices, each with a fresh\n * token (`renderInSlices`). `undefined` for a target that needs no signed URL (it is never\n * sliced), and absent on a source that has no such limit.\n */\n ttlSeconds?: (target: CaptureTarget, budgetMs: number) => number | undefined;\n};\n\n/** What minting needs of a capture job. */\nexport interface StorybookUrlOptions {\n runner: Pick<CaptureRunner, \"metadata\">;\n secret?: SecretInput;\n publicOrigin?: (() => Promise<string | undefined>) | undefined;\n}\n\n/**\n * A source of signed URLs for one build. Every call mints a fresh token, so a shard that waited in\n * the queue or a retried pass is never handed a token minted before it started.\n */\nexport function storybookUrlSource(\n options: StorybookUrlOptions,\n subject: {\n build: Pick<Build, \"id\" | \"projectId\">;\n project: Pick<Project, \"storybookUrlTtlSeconds\">;\n },\n): StorybookUrlSource {\n const source: StorybookUrlSource = async (target, pass = {}) => {\n const origin = needsUrl(options.runner, target) ? await options.publicOrigin?.() : undefined;\n return origin !== undefined && options.secret\n ? `${origin}${mint(options.secret, subject, pass)}`\n : undefined;\n };\n source.ttlSeconds = (target, budgetMs): number | undefined =>\n needsUrl(options.runner, target)\n ? storybookUrlTtl({ override: subject.project.storybookUrlTtlSeconds, budgetMs })\n : undefined;\n return source;\n}\n\n/** The path of a fresh token for one pass of the build. */\nfunction mint(\n secret: SecretInput,\n subject: Parameters<typeof storybookUrlSource>[1],\n pass: StorybookUrlPass,\n): string {\n const ttlSeconds = storybookUrlTtl({\n override: subject.project.storybookUrlTtlSeconds,\n budgetMs: pass.budgetMs ?? 0,\n });\n const { id: buildId, projectId } = subject.build;\n return storybookTokenPath(signStorybookToken({ secret, projectId, buildId, ttlSeconds }));\n}\n\nfunction needsUrl(runner: Pick<CaptureRunner, \"metadata\">, target: CaptureTarget): boolean {\n return runner.metadata.targets?.some((t) => t.target === target && t.reach === \"url\") ?? false;\n}\n"],"mappings":";;;;;;;;;AA+CA,SAAgB,mBACd,SACA,SAIoB;CACpB,MAAM,SAA6B,OAAO,QAAQ,OAAO,CAAC,MAAM;EAC9D,MAAM,SAAS,SAAS,QAAQ,QAAQ,MAAM,IAAI,MAAM,QAAQ,eAAe,IAAI,KAAA;EACnF,OAAO,WAAW,KAAA,KAAa,QAAQ,SACnC,GAAG,SAAS,KAAK,QAAQ,QAAQ,SAAS,IAAI,MAC9C,KAAA;CACN;CACA,OAAO,cAAc,QAAQ,aAC3B,SAAS,QAAQ,QAAQ,MAAM,IAC3B,gBAAgB;EAAE,UAAU,QAAQ,QAAQ;EAAwB;CAAS,CAAC,IAC9E,KAAA;CACN,OAAO;AACT;;AAGA,SAAS,KACP,QACA,SACA,MACQ;CACR,MAAM,aAAa,gBAAgB;EACjC,UAAU,QAAQ,QAAQ;EAC1B,UAAU,KAAK,YAAY;CAC7B,CAAC;CACD,MAAM,EAAE,IAAI,SAAS,cAAc,QAAQ;CAC3C,OAAO,mBAAmB,mBAAmB;EAAE;EAAQ;EAAW;EAAS;CAAW,CAAC,CAAC;AAC1F;AAEA,SAAS,SAAS,QAAyC,QAAgC;CACzF,OAAO,OAAO,SAAS,SAAS,MAAM,MAAM,EAAE,WAAW,UAAU,EAAE,UAAU,KAAK,KAAK;AAC3F"}
1
+ {"version":3,"file":"storybook-urls.mjs","names":[],"sources":["../../src/capture/storybook-urls.ts"],"sourcesContent":["/**\n * The signed Storybook URL of a render (ADR 0007, decision 5): minted at the moment a pass starts,\n * only for a target whose browser cannot reach this host (`reach: \"url\"`). The URL is the build's\n * Storybook root, `<publicOrigin>/_s/<token>/`; it carries the token and is never logged. Its\n * lifetime comes from the pass's render budget unless the project sets one (`storybookUrlTtl`).\n * Every token minted here carries the signed `capture` purpose, the only claim that lets the\n * server opt a response into browser-private caching.\n */\nimport type { CaptureRunner, CaptureTarget } from \"../adapters/capture-runner.ts\";\nimport type { Build } from \"../schema/build.ts\";\nimport type { Project } from \"../schema/project.ts\";\nimport type { SecretInput } from \"../utils/encrypt.ts\";\nimport {\n signStorybookToken,\n storybookTokenPath,\n storybookUrlTtl,\n} from \"../utils/storybook-token.ts\";\n\n/** What a pass tells the source: how long it may run, which sizes the token's lifetime. */\nexport interface StorybookUrlPass {\n /** The render budget of the pass in ms (`renderBudgetMs`); unset when the pass has none. */\n budgetMs?: number | undefined;\n}\n\n/** Gives the URL a target's browser opens, or `undefined` when it needs none (or none can be made). */\nexport type StorybookUrlSource = ((\n target: CaptureTarget,\n pass?: StorybookUrlPass,\n) => Promise<string | undefined>) & {\n /**\n * The lifetime in seconds a token minted for a pass of this budget gets (the project's override\n * or the derived, capped one). A pass that outlasts it is rendered in slices, each with a fresh\n * token (`renderInSlices`). `undefined` for a target that needs no signed URL (it is never\n * sliced), and absent on a source that has no such limit.\n */\n ttlSeconds?: (target: CaptureTarget, budgetMs: number) => number | undefined;\n};\n\n/** What minting needs of a capture job. */\nexport interface StorybookUrlOptions {\n runner: Pick<CaptureRunner, \"metadata\">;\n secret?: SecretInput;\n publicOrigin?: (() => Promise<string | undefined>) | undefined;\n}\n\n/**\n * A source of signed URLs for one build. Every call mints a fresh token, so a shard that waited in\n * the queue or a retried pass is never handed a token minted before it started.\n */\nexport function storybookUrlSource(\n options: StorybookUrlOptions,\n subject: {\n build: Pick<Build, \"id\" | \"projectId\">;\n project: Pick<Project, \"storybookUrlTtlSeconds\">;\n },\n): StorybookUrlSource {\n const source: StorybookUrlSource = async (target, pass = {}) => {\n const origin = needsUrl(options.runner, target) ? await options.publicOrigin?.() : undefined;\n return origin !== undefined && options.secret\n ? `${origin}${mint(options.secret, subject, pass)}`\n : undefined;\n };\n source.ttlSeconds = (target, budgetMs): number | undefined =>\n needsUrl(options.runner, target)\n ? storybookUrlTtl({ override: subject.project.storybookUrlTtlSeconds, budgetMs })\n : undefined;\n return source;\n}\n\n/** The path of a fresh token for one pass of the build. */\nfunction mint(\n secret: SecretInput,\n subject: Parameters<typeof storybookUrlSource>[1],\n pass: StorybookUrlPass,\n): string {\n const ttlSeconds = storybookUrlTtl({\n override: subject.project.storybookUrlTtlSeconds,\n budgetMs: pass.budgetMs ?? 0,\n });\n const { id: buildId, projectId } = subject.build;\n return storybookTokenPath(\n signStorybookToken({ secret, projectId, buildId, ttlSeconds, purpose: \"capture\" }),\n );\n}\n\nfunction needsUrl(runner: Pick<CaptureRunner, \"metadata\">, target: CaptureTarget): boolean {\n return runner.metadata.targets?.some((t) => t.target === target && t.reach === \"url\") ?? false;\n}\n"],"mappings":";;;;;;;;;AAiDA,SAAgB,mBACd,SACA,SAIoB;CACpB,MAAM,SAA6B,OAAO,QAAQ,OAAO,CAAC,MAAM;EAC9D,MAAM,SAAS,SAAS,QAAQ,QAAQ,MAAM,IAAI,MAAM,QAAQ,eAAe,IAAI,KAAA;EACnF,OAAO,WAAW,KAAA,KAAa,QAAQ,SACnC,GAAG,SAAS,KAAK,QAAQ,QAAQ,SAAS,IAAI,MAC9C,KAAA;CACN;CACA,OAAO,cAAc,QAAQ,aAC3B,SAAS,QAAQ,QAAQ,MAAM,IAC3B,gBAAgB;EAAE,UAAU,QAAQ,QAAQ;EAAwB;CAAS,CAAC,IAC9E,KAAA;CACN,OAAO;AACT;;AAGA,SAAS,KACP,QACA,SACA,MACQ;CACR,MAAM,aAAa,gBAAgB;EACjC,UAAU,QAAQ,QAAQ;EAC1B,UAAU,KAAK,YAAY;CAC7B,CAAC;CACD,MAAM,EAAE,IAAI,SAAS,cAAc,QAAQ;CAC3C,OAAO,mBACL,mBAAmB;EAAE;EAAQ;EAAW;EAAS;EAAY,SAAS;CAAU,CAAC,CACnF;AACF;AAEA,SAAS,SAAS,QAAyC,QAAgC;CACzF,OAAO,OAAO,SAAS,SAAS,MAAM,MAAM,EAAE,WAAW,UAAU,EAAE,UAAU,KAAK,KAAK;AAC3F"}
@@ -2,7 +2,6 @@ import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { DiscoverOptions, StoryEntry, StoryParameters, StorySourceAdapter, StoryViewportConfig, Viewport } from "./adapter.mjs";
4
4
  import { DepGraph } from "@storyshelf/affected";
5
- //#region src/capture/storybook.d.ts
6
5
  /** Story parameters as serialized by the Storybook index. */
7
6
  interface StorybookParameters {
8
7
  chromatic?: StoryParameters;
@@ -47,6 +46,5 @@ declare class StorybookAdapter implements StorySourceAdapter {
47
46
  loadDependencyGraph(source: string): Promise<DepGraph | null>;
48
47
  private readIndex;
49
48
  }
50
- //#endregion
51
49
  export { STORYBOOK_BUILTIN_VIEWPORTS, StorybookAdapter, mergeParameters, resolveStoryViewports };
52
50
  //# sourceMappingURL=storybook.d.mts.map
@@ -1,12 +1,12 @@
1
- import "node:path";
1
+ import { join } from "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
+ import { declaredIgnoreSelectors } from "./ignore-regions.mjs";
4
5
  import { applyModes, buildGlobalsParam } from "./modes.mjs";
5
6
  import { extractDesignUrl } from "./design.mjs";
6
- import { join } from "node:path";
7
7
  import { STATS_FILENAME, loadDepGraph } from "@storyshelf/affected";
8
8
  import { readFile } from "node:fs/promises";
9
- //#region src/capture/storybook.ts
9
+ // src/capture/storybook.ts
10
10
  /** Merge the `chromatic` and `storyshelf` parameter layers (`storyshelf` wins). */
11
11
  function mergeParameters(entry) {
12
12
  const { figma: _figma, ...storyshelf } = entry.parameters?.storyshelf ?? {};
@@ -17,6 +17,11 @@ function mergeParameters(entry) {
17
17
  const modes = mergeModes(entry.parameters?.chromatic?.modes, entry.parameters?.storyshelf?.modes);
18
18
  if (modes) merged.modes = modes;
19
19
  if (entry.parameters?.viewport) merged.viewport = entry.parameters.viewport;
20
+ if ("ignoreSelectors" in merged) {
21
+ const declared = declaredIgnoreSelectors(merged.ignoreSelectors);
22
+ if (declared) merged.ignoreSelectors = declared;
23
+ else delete merged.ignoreSelectors;
24
+ }
20
25
  return Object.keys(merged).length > 0 ? merged : void 0;
21
26
  }
22
27
  /** Union both parameter keys' modes by name (`storyshelf` wins per mode). */
@@ -158,7 +163,6 @@ var StorybookAdapter = class {
158
163
  throw new Error(`No Storybook index found in ${source}`);
159
164
  }
160
165
  };
161
- //#endregion
162
166
  export { STORYBOOK_BUILTIN_VIEWPORTS, StorybookAdapter, mergeParameters, resolveStoryViewports };
163
167
 
164
168
  //# sourceMappingURL=storybook.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"storybook.mjs","names":[],"sources":["../../src/capture/storybook.ts"],"sourcesContent":["import { loadDepGraph, STATS_FILENAME, type DepGraph } from \"@storyshelf/affected\";\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type {\n ModeMap,\n StoryEntry,\n StoryParameters,\n StorySourceAdapter,\n StoryViewportConfig,\n StoryViewportDefinition,\n Viewport,\n} from \"./adapter.ts\";\nimport type { DiscoverOptions } from \"./adapter.ts\";\nimport { extractDesignUrl, type DesignUrlParser } from \"./design.ts\";\nimport { applyModes, buildGlobalsParam } from \"./modes.ts\";\n\ninterface StorybookIndex {\n v: number;\n entries: Record<\n string,\n {\n id: string;\n name: string;\n title: string;\n importPath?: string;\n tags?: string[];\n type: string;\n subtype?: string;\n /** Test entries only: the id of the story the `.test()` is written under. */\n parent?: string;\n parameters?: StorybookParameters;\n }\n >;\n}\n\n/** Story parameters as serialized by the Storybook index. */\ninterface StorybookParameters {\n chromatic?: StoryParameters;\n storyshelf?: StoryParameters & { figma?: unknown };\n viewport?: StoryViewportConfig;\n design?: unknown;\n}\n\n/** Merge the `chromatic` and `storyshelf` parameter layers (`storyshelf` wins). */\nexport function mergeParameters(entry: {\n parameters?: StorybookParameters;\n}): StoryParameters | undefined {\n const { figma: _figma, ...storyshelf } = entry.parameters?.storyshelf ?? {};\n const merged: StoryParameters = { ...entry.parameters?.chromatic, ...storyshelf };\n const modes = mergeModes(entry.parameters?.chromatic?.modes, entry.parameters?.storyshelf?.modes);\n if (modes) merged.modes = modes;\n if (entry.parameters?.viewport) merged.viewport = entry.parameters.viewport;\n return Object.keys(merged).length > 0 ? merged : undefined;\n}\n\n/** Union both parameter keys' modes by name (`storyshelf` wins per mode). */\nfunction mergeModes(chromatic?: ModeMap, storyshelf?: ModeMap): ModeMap | undefined {\n const merged = { ...chromatic, ...storyshelf };\n return Object.keys(merged).length > 0 ? merged : undefined;\n}\n\n/**\n * The Storybook `MINIMAL_VIEWPORTS` snapshot used to resolve a story's\n * `defaultViewport` name at capture time. Dimensions are the documented\n * stable set; inline `parameters.viewport.viewports` always win over these.\n */\nexport const STORYBOOK_BUILTIN_VIEWPORTS: Readonly<Record<string, Viewport>> = {\n mobile1: { name: \"mobile1\", width: 320, height: 568 },\n mobile2: { name: \"mobile2\", width: 414, height: 896 },\n tablet: { name: \"tablet\", width: 834, height: 1112 },\n desktop: { name: \"desktop\", width: 1024, height: 1280 },\n};\n\n/**\n * Resolve the viewports a story should be captured at: the project's global\n * list plus the story's Storybook default viewport (union), deduplicated by\n * name. An unresolvable or duplicate default contributes nothing extra.\n */\nexport function resolveStoryViewports(\n entry: Pick<StoryEntry, \"parameters\">,\n globalViewports: Viewport[],\n): Viewport[] {\n const resolved = storyViewport(entry);\n const withDefault =\n resolved && !globalViewports.some((v) => v.name === resolved.name)\n ? [...globalViewports, resolved]\n : [...globalViewports];\n return applyModes(entry.parameters?.modes, withDefault, (name) => namedViewport(entry, name));\n}\n\n/** Resolve a Storybook viewport name via the story's inline config, then builtins. */\nfunction namedViewport(entry: Pick<StoryEntry, \"parameters\">, name: string): Viewport | undefined {\n const inline = entry.parameters?.viewport?.viewports?.[name];\n return (\n (inline ? viewportFromInline(name, inline) : undefined) ?? STORYBOOK_BUILTIN_VIEWPORTS[name]\n );\n}\n\nfunction storyViewport(entry: Pick<StoryEntry, \"parameters\">): Viewport | undefined {\n const config = entry.parameters?.viewport;\n const name = config?.defaultViewport;\n const inline = name ? config?.viewports?.[name] : undefined;\n const resolved =\n name && inline\n ? viewportFromInline(name, inline)\n : name\n ? STORYBOOK_BUILTIN_VIEWPORTS[name]\n : undefined;\n return resolved;\n}\n\nfunction viewportFromInline(\n defaultName: string,\n inline: StoryViewportDefinition,\n): Viewport | undefined {\n const dims = viewportDims(inline.styles);\n return dims ? { name: inline.name ?? defaultName, ...dims } : undefined;\n}\n\nfunction viewportDims(\n styles: StoryViewportDefinition[\"styles\"] | undefined,\n): { width: number; height: number } | undefined {\n const width = pxOf(styles?.width);\n const height = pxOf(styles?.height);\n return width !== undefined && height !== undefined ? { width, height } : undefined;\n}\n\nfunction pxOf(value: string | number | undefined): number | undefined {\n if (typeof value === \"number\") return Math.round(value);\n const bare = typeof value === \"string\" ? value.trim() : \"\";\n const match = /^(\\d+(?:\\.\\d+)?)px$/u.exec(bare);\n return match ? Number(match[1]) : undefined;\n}\n\nfunction toTestEntry(entry: StorybookIndex[\"entries\"][string]): StoryEntry {\n const test = toStoryEntry(entry);\n if (entry.parent !== undefined) test.parentId = entry.parent;\n return test;\n}\n\nfunction toStoryEntry(\n entry: StorybookIndex[\"entries\"][string],\n parseDesignUrl?: DesignUrlParser,\n): StoryEntry {\n const design = parseDesignUrl ? extractDesignUrl(entry.parameters, parseDesignUrl) : {};\n const story: StoryEntry = {\n id: entry.id,\n title: entry.title,\n name: entry.name,\n importPath: entry.importPath,\n tags: entry.tags,\n type: entry.type === \"docs\" ? \"docs\" : \"story\",\n parameters: mergeParameters(entry),\n };\n if (design.url) story.designUrl = design.url;\n if (design.invalid) story.designInvalid = design.invalid;\n return story;\n}\n\n/** Discovers stories from a built Storybook via its index file. */\nexport class StorybookAdapter implements StorySourceAdapter {\n readonly name = \"storybook\";\n readonly screenshotSelector = \"#storybook-root\";\n\n async discover(source: string, options: DiscoverOptions = {}): Promise<StoryEntry[]> {\n const index = await this.readIndex(source);\n return Object.values(index.entries)\n .filter((entry) => entry.type !== \"docs\" && entry.subtype !== \"test\")\n .map((entry) => toStoryEntry(entry, options.parseDesignUrl));\n }\n\n /**\n * The `.test()` entries of a Storybook 10 build (index `subtype: \"test\"`), which {@link discover}\n * leaves out. Empty for older Storybooks, whose index has no such entries.\n */\n async discoverTests(source: string): Promise<StoryEntry[]> {\n const index = await this.readIndex(source);\n return Object.values(index.entries)\n .filter((entry) => entry.subtype === \"test\" && entry.type !== \"docs\")\n .map((entry) => toTestEntry(entry));\n }\n\n // eslint-disable-next-line class-methods-use-this\n buildUrl(baseUrl: string, storyId: string, globals?: Record<string, unknown>): string {\n const url = `${baseUrl}/iframe.html?id=${encodeURIComponent(storyId)}&viewMode=story`;\n const param = globals ? buildGlobalsParam(globals) : \"\";\n return param === \"\" ? url : `${url}&globals=${encodeURIComponent(param)}`;\n }\n\n /**\n * Load the Vite stats dependency graph of an extracted build for affected\n * capture. Resolves `null` when the build ships no usable stats, in which\n * case the orchestrator renders every story.\n */\n // eslint-disable-next-line class-methods-use-this\n async loadDependencyGraph(source: string): Promise<DepGraph | null> {\n return await loadDepGraph(join(source, STATS_FILENAME));\n }\n\n // eslint-disable-next-line class-methods-use-this\n private async readIndex(source: string): Promise<StorybookIndex> {\n const candidates: string[] = [\"stories.json\", \"index.json\"];\n const results = await Promise.all(\n candidates.map(async (name) => {\n try {\n const raw = await readFile(join(source, name), \"utf8\");\n return JSON.parse(raw) as StorybookIndex;\n } catch {\n return null;\n }\n }),\n );\n const found = results.find((index) => index !== null);\n if (found) {\n return found;\n }\n throw new Error(`No Storybook index found in ${source}`);\n }\n}\n"],"mappings":";;;;;;;;;;AA4CA,SAAgB,gBAAgB,OAEA;CAC9B,MAAM,EAAE,OAAO,QAAQ,GAAG,eAAe,MAAM,YAAY,cAAc,CAAC;CAC1E,MAAM,SAA0B;EAAE,GAAG,MAAM,YAAY;EAAW,GAAG;CAAW;CAChF,MAAM,QAAQ,WAAW,MAAM,YAAY,WAAW,OAAO,MAAM,YAAY,YAAY,KAAK;CAChG,IAAI,OAAO,OAAO,QAAQ;CAC1B,IAAI,MAAM,YAAY,UAAU,OAAO,WAAW,MAAM,WAAW;CACnE,OAAO,OAAO,KAAK,MAAM,CAAC,CAAC,SAAS,IAAI,SAAS,KAAA;AACnD;;AAGA,SAAS,WAAW,WAAqB,YAA2C;CAClF,MAAM,SAAS;EAAE,GAAG;EAAW,GAAG;CAAW;CAC7C,OAAO,OAAO,KAAK,MAAM,CAAC,CAAC,SAAS,IAAI,SAAS,KAAA;AACnD;;;;;;AAOA,MAAa,8BAAkE;CAC7E,SAAS;EAAE,MAAM;EAAW,OAAO;EAAK,QAAQ;CAAI;CACpD,SAAS;EAAE,MAAM;EAAW,OAAO;EAAK,QAAQ;CAAI;CACpD,QAAQ;EAAE,MAAM;EAAU,OAAO;EAAK,QAAQ;CAAK;CACnD,SAAS;EAAE,MAAM;EAAW,OAAO;EAAM,QAAQ;CAAK;AACxD;;;;;;AAOA,SAAgB,sBACd,OACA,iBACY;CACZ,MAAM,WAAW,cAAc,KAAK;CACpC,MAAM,cACJ,YAAY,CAAC,gBAAgB,MAAM,MAAM,EAAE,SAAS,SAAS,IAAI,IAC7D,CAAC,GAAG,iBAAiB,QAAQ,IAC7B,CAAC,GAAG,eAAe;CACzB,OAAO,WAAW,MAAM,YAAY,OAAO,cAAc,SAAS,cAAc,OAAO,IAAI,CAAC;AAC9F;;AAGA,SAAS,cAAc,OAAuC,MAAoC;CAChG,MAAM,SAAS,MAAM,YAAY,UAAU,YAAY;CACvD,QACG,SAAS,mBAAmB,MAAM,MAAM,IAAI,KAAA,MAAc,4BAA4B;AAE3F;AAEA,SAAS,cAAc,OAA6D;CAClF,MAAM,SAAS,MAAM,YAAY;CACjC,MAAM,OAAO,QAAQ;CACrB,MAAM,SAAS,OAAO,QAAQ,YAAY,QAAQ,KAAA;CAOlD,OALE,QAAQ,SACJ,mBAAmB,MAAM,MAAM,IAC/B,OACE,4BAA4B,QAC5B,KAAA;AAEV;AAEA,SAAS,mBACP,aACA,QACsB;CACtB,MAAM,OAAO,aAAa,OAAO,MAAM;CACvC,OAAO,OAAO;EAAE,MAAM,OAAO,QAAQ;EAAa,GAAG;CAAK,IAAI,KAAA;AAChE;AAEA,SAAS,aACP,QAC+C;CAC/C,MAAM,QAAQ,KAAK,QAAQ,KAAK;CAChC,MAAM,SAAS,KAAK,QAAQ,MAAM;CAClC,OAAO,UAAU,KAAA,KAAa,WAAW,KAAA,IAAY;EAAE;EAAO;CAAO,IAAI,KAAA;AAC3E;AAEA,SAAS,KAAK,OAAwD;CACpE,IAAI,OAAO,UAAU,UAAU,OAAO,KAAK,MAAM,KAAK;CACtD,MAAM,OAAO,OAAO,UAAU,WAAW,MAAM,KAAK,IAAI;CACxD,MAAM,QAAQ,uBAAuB,KAAK,IAAI;CAC9C,OAAO,QAAQ,OAAO,MAAM,EAAE,IAAI,KAAA;AACpC;AAEA,SAAS,YAAY,OAAsD;CACzE,MAAM,OAAO,aAAa,KAAK;CAC/B,IAAI,MAAM,WAAW,KAAA,GAAW,KAAK,WAAW,MAAM;CACtD,OAAO;AACT;AAEA,SAAS,aACP,OACA,gBACY;CACZ,MAAM,SAAS,iBAAiB,iBAAiB,MAAM,YAAY,cAAc,IAAI,CAAC;CACtF,MAAM,QAAoB;EACxB,IAAI,MAAM;EACV,OAAO,MAAM;EACb,MAAM,MAAM;EACZ,YAAY,MAAM;EAClB,MAAM,MAAM;EACZ,MAAM,MAAM,SAAS,SAAS,SAAS;EACvC,YAAY,gBAAgB,KAAK;CACnC;CACA,IAAI,OAAO,KAAK,MAAM,YAAY,OAAO;CACzC,IAAI,OAAO,SAAS,MAAM,gBAAgB,OAAO;CACjD,OAAO;AACT;;AAGA,IAAa,mBAAb,MAA4D;CAC1D,OAAgB;CAChB,qBAA8B;CAE9B,MAAM,SAAS,QAAgB,UAA2B,CAAC,GAA0B;EACnF,MAAM,QAAQ,MAAM,KAAK,UAAU,MAAM;EACzC,OAAO,OAAO,OAAO,MAAM,OAAO,CAAC,CAChC,QAAQ,UAAU,MAAM,SAAS,UAAU,MAAM,YAAY,MAAM,CAAC,CACpE,KAAK,UAAU,aAAa,OAAO,QAAQ,cAAc,CAAC;CAC/D;;;;;CAMA,MAAM,cAAc,QAAuC;EACzD,MAAM,QAAQ,MAAM,KAAK,UAAU,MAAM;EACzC,OAAO,OAAO,OAAO,MAAM,OAAO,CAAC,CAChC,QAAQ,UAAU,MAAM,YAAY,UAAU,MAAM,SAAS,MAAM,CAAC,CACpE,KAAK,UAAU,YAAY,KAAK,CAAC;CACtC;CAGA,SAAS,SAAiB,SAAiB,SAA2C;EACpF,MAAM,MAAM,GAAG,QAAQ,kBAAkB,mBAAmB,OAAO,EAAE;EACrE,MAAM,QAAQ,UAAU,kBAAkB,OAAO,IAAI;EACrD,OAAO,UAAU,KAAK,MAAM,GAAG,IAAI,WAAW,mBAAmB,KAAK;CACxE;;;;;;CAQA,MAAM,oBAAoB,QAA0C;EAClE,OAAO,MAAM,aAAa,KAAK,QAAQ,cAAc,CAAC;CACxD;CAGA,MAAc,UAAU,QAAyC;EAY/D,MAAM,SAAQ,MAVQ,QAAQ,IAC5B,CAF4B,gBAAgB,YAEnC,CAAC,CAAC,IAAI,OAAO,SAAS;GAC7B,IAAI;IACF,MAAM,MAAM,MAAM,SAAS,KAAK,QAAQ,IAAI,GAAG,MAAM;IACrD,OAAO,KAAK,MAAM,GAAG;GACvB,QAAQ;IACN,OAAO;GACT;EACF,CAAC,CACH,EAAA,CACsB,MAAM,UAAU,UAAU,IAAI;EACpD,IAAI,OACF,OAAO;EAET,MAAM,IAAI,MAAM,+BAA+B,QAAQ;CACzD;AACF"}
1
+ {"version":3,"file":"storybook.mjs","names":[],"sources":["../../src/capture/storybook.ts"],"sourcesContent":["import { loadDepGraph, STATS_FILENAME, type DepGraph } from \"@storyshelf/affected\";\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type {\n ModeMap,\n StoryEntry,\n StoryParameters,\n StorySourceAdapter,\n StoryViewportConfig,\n StoryViewportDefinition,\n Viewport,\n} from \"./adapter.ts\";\nimport type { DiscoverOptions } from \"./adapter.ts\";\nimport { extractDesignUrl, type DesignUrlParser } from \"./design.ts\";\nimport { declaredIgnoreSelectors } from \"./ignore-regions.ts\";\nimport { applyModes, buildGlobalsParam } from \"./modes.ts\";\n\ninterface StorybookIndex {\n v: number;\n entries: Record<\n string,\n {\n id: string;\n name: string;\n title: string;\n importPath?: string;\n tags?: string[];\n type: string;\n subtype?: string;\n /** Test entries only: the id of the story the `.test()` is written under. */\n parent?: string;\n parameters?: StorybookParameters;\n }\n >;\n}\n\n/** Story parameters as serialized by the Storybook index. */\ninterface StorybookParameters {\n chromatic?: StoryParameters;\n storyshelf?: StoryParameters & { figma?: unknown };\n viewport?: StoryViewportConfig;\n design?: unknown;\n}\n\n/** Merge the `chromatic` and `storyshelf` parameter layers (`storyshelf` wins). */\nexport function mergeParameters(entry: {\n parameters?: StorybookParameters;\n}): StoryParameters | undefined {\n const { figma: _figma, ...storyshelf } = entry.parameters?.storyshelf ?? {};\n const merged: StoryParameters = { ...entry.parameters?.chromatic, ...storyshelf };\n const modes = mergeModes(entry.parameters?.chromatic?.modes, entry.parameters?.storyshelf?.modes);\n if (modes) merged.modes = modes;\n if (entry.parameters?.viewport) merged.viewport = entry.parameters.viewport;\n if (\"ignoreSelectors\" in merged) {\n const declared = declaredIgnoreSelectors(merged.ignoreSelectors);\n if (declared) merged.ignoreSelectors = declared;\n else delete merged.ignoreSelectors;\n }\n return Object.keys(merged).length > 0 ? merged : undefined;\n}\n\n/** Union both parameter keys' modes by name (`storyshelf` wins per mode). */\nfunction mergeModes(chromatic?: ModeMap, storyshelf?: ModeMap): ModeMap | undefined {\n const merged = { ...chromatic, ...storyshelf };\n return Object.keys(merged).length > 0 ? merged : undefined;\n}\n\n/**\n * The Storybook `MINIMAL_VIEWPORTS` snapshot used to resolve a story's\n * `defaultViewport` name at capture time. Dimensions are the documented\n * stable set; inline `parameters.viewport.viewports` always win over these.\n */\nexport const STORYBOOK_BUILTIN_VIEWPORTS: Readonly<Record<string, Viewport>> = {\n mobile1: { name: \"mobile1\", width: 320, height: 568 },\n mobile2: { name: \"mobile2\", width: 414, height: 896 },\n tablet: { name: \"tablet\", width: 834, height: 1112 },\n desktop: { name: \"desktop\", width: 1024, height: 1280 },\n};\n\n/**\n * Resolve the viewports a story should be captured at: the project's global\n * list plus the story's Storybook default viewport (union), deduplicated by\n * name. An unresolvable or duplicate default contributes nothing extra.\n */\nexport function resolveStoryViewports(\n entry: Pick<StoryEntry, \"parameters\">,\n globalViewports: Viewport[],\n): Viewport[] {\n const resolved = storyViewport(entry);\n const withDefault =\n resolved && !globalViewports.some((v) => v.name === resolved.name)\n ? [...globalViewports, resolved]\n : [...globalViewports];\n return applyModes(entry.parameters?.modes, withDefault, (name) => namedViewport(entry, name));\n}\n\n/** Resolve a Storybook viewport name via the story's inline config, then builtins. */\nfunction namedViewport(entry: Pick<StoryEntry, \"parameters\">, name: string): Viewport | undefined {\n const inline = entry.parameters?.viewport?.viewports?.[name];\n return (\n (inline ? viewportFromInline(name, inline) : undefined) ?? STORYBOOK_BUILTIN_VIEWPORTS[name]\n );\n}\n\nfunction storyViewport(entry: Pick<StoryEntry, \"parameters\">): Viewport | undefined {\n const config = entry.parameters?.viewport;\n const name = config?.defaultViewport;\n const inline = name ? config?.viewports?.[name] : undefined;\n const resolved =\n name && inline\n ? viewportFromInline(name, inline)\n : name\n ? STORYBOOK_BUILTIN_VIEWPORTS[name]\n : undefined;\n return resolved;\n}\n\nfunction viewportFromInline(\n defaultName: string,\n inline: StoryViewportDefinition,\n): Viewport | undefined {\n const dims = viewportDims(inline.styles);\n return dims ? { name: inline.name ?? defaultName, ...dims } : undefined;\n}\n\nfunction viewportDims(\n styles: StoryViewportDefinition[\"styles\"] | undefined,\n): { width: number; height: number } | undefined {\n const width = pxOf(styles?.width);\n const height = pxOf(styles?.height);\n return width !== undefined && height !== undefined ? { width, height } : undefined;\n}\n\nfunction pxOf(value: string | number | undefined): number | undefined {\n if (typeof value === \"number\") return Math.round(value);\n const bare = typeof value === \"string\" ? value.trim() : \"\";\n const match = /^(\\d+(?:\\.\\d+)?)px$/u.exec(bare);\n return match ? Number(match[1]) : undefined;\n}\n\nfunction toTestEntry(entry: StorybookIndex[\"entries\"][string]): StoryEntry {\n const test = toStoryEntry(entry);\n if (entry.parent !== undefined) test.parentId = entry.parent;\n return test;\n}\n\nfunction toStoryEntry(\n entry: StorybookIndex[\"entries\"][string],\n parseDesignUrl?: DesignUrlParser,\n): StoryEntry {\n const design = parseDesignUrl ? extractDesignUrl(entry.parameters, parseDesignUrl) : {};\n const story: StoryEntry = {\n id: entry.id,\n title: entry.title,\n name: entry.name,\n importPath: entry.importPath,\n tags: entry.tags,\n type: entry.type === \"docs\" ? \"docs\" : \"story\",\n parameters: mergeParameters(entry),\n };\n if (design.url) story.designUrl = design.url;\n if (design.invalid) story.designInvalid = design.invalid;\n return story;\n}\n\n/** Discovers stories from a built Storybook via its index file. */\nexport class StorybookAdapter implements StorySourceAdapter {\n readonly name = \"storybook\";\n readonly screenshotSelector = \"#storybook-root\";\n\n async discover(source: string, options: DiscoverOptions = {}): Promise<StoryEntry[]> {\n const index = await this.readIndex(source);\n return Object.values(index.entries)\n .filter((entry) => entry.type !== \"docs\" && entry.subtype !== \"test\")\n .map((entry) => toStoryEntry(entry, options.parseDesignUrl));\n }\n\n /**\n * The `.test()` entries of a Storybook 10 build (index `subtype: \"test\"`), which {@link discover}\n * leaves out. Empty for older Storybooks, whose index has no such entries.\n */\n async discoverTests(source: string): Promise<StoryEntry[]> {\n const index = await this.readIndex(source);\n return Object.values(index.entries)\n .filter((entry) => entry.subtype === \"test\" && entry.type !== \"docs\")\n .map((entry) => toTestEntry(entry));\n }\n\n // eslint-disable-next-line class-methods-use-this\n buildUrl(baseUrl: string, storyId: string, globals?: Record<string, unknown>): string {\n const url = `${baseUrl}/iframe.html?id=${encodeURIComponent(storyId)}&viewMode=story`;\n const param = globals ? buildGlobalsParam(globals) : \"\";\n return param === \"\" ? url : `${url}&globals=${encodeURIComponent(param)}`;\n }\n\n /**\n * Load the Vite stats dependency graph of an extracted build for affected\n * capture. Resolves `null` when the build ships no usable stats, in which\n * case the orchestrator renders every story.\n */\n // eslint-disable-next-line class-methods-use-this\n async loadDependencyGraph(source: string): Promise<DepGraph | null> {\n return await loadDepGraph(join(source, STATS_FILENAME));\n }\n\n // eslint-disable-next-line class-methods-use-this\n private async readIndex(source: string): Promise<StorybookIndex> {\n const candidates: string[] = [\"stories.json\", \"index.json\"];\n const results = await Promise.all(\n candidates.map(async (name) => {\n try {\n const raw = await readFile(join(source, name), \"utf8\");\n return JSON.parse(raw) as StorybookIndex;\n } catch {\n return null;\n }\n }),\n );\n const found = results.find((index) => index !== null);\n if (found) {\n return found;\n }\n throw new Error(`No Storybook index found in ${source}`);\n }\n}\n"],"mappings":"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"}
@@ -3,7 +3,7 @@ import "node:url";
3
3
  import.meta.url;
4
4
  import { BuildModel } from "../models/build.mjs";
5
5
  import { ProjectModel } from "../models/project.mjs";
6
- //#region src/capture/target.ts
6
+ // src/capture/target.ts
7
7
  /** Load a build and its project, throwing when either is gone. */
8
8
  async function loadTarget(options, buildId) {
9
9
  const build = await new BuildModel(options.db, options.tables).get(buildId);
@@ -15,7 +15,6 @@ async function loadTarget(options, buildId) {
15
15
  project
16
16
  };
17
17
  }
18
- //#endregion
19
18
  export { loadTarget };
20
19
 
21
20
  //# sourceMappingURL=target.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 { RunnerTarget } from "../adapters/capture-target.mjs";
4
4
  import { CaptureRunner } from "../adapter/capture-runner.mjs";
5
- //#region src/capture/targets.d.ts
6
5
  /** Why a `reach: "url"` target is unavailable without a secret (the first words are matched by tests and docs). */
7
6
  declare const NO_SECRET_REASON = "no secret: set the server secret, it signs the Storybook URL the remote browser opens";
8
7
  /** Why it is unavailable without a confirmed public origin. */
@@ -20,6 +19,5 @@ interface TargetPrerequisites {
20
19
  * reason. `undefined` when the runner publishes no list (it offers any target).
21
20
  */
22
21
  declare function offeredTargets(runner: Pick<CaptureRunner, "metadata">, prerequisites: TargetPrerequisites): readonly RunnerTarget[] | undefined;
23
- //#endregion
24
22
  export { NO_ORIGIN_REASON, NO_SECRET_REASON, TargetPrerequisites, offeredTargets };
25
23
  //# sourceMappingURL=targets.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/targets.ts
4
+ // src/capture/targets.ts
5
5
  /** Why a `reach: "url"` target is unavailable without a secret (the first words are matched by tests and docs). */
6
6
  const NO_SECRET_REASON = "no secret: set the server secret, it signs the Storybook URL the remote browser opens";
7
7
  /** Why it is unavailable without a confirmed public origin. */
@@ -26,7 +26,6 @@ function withPrerequisites(entry, prerequisites) {
26
26
  };
27
27
  return entry;
28
28
  }
29
- //#endregion
30
29
  export { NO_ORIGIN_REASON, NO_SECRET_REASON, offeredTargets };
31
30
 
32
31
  //# sourceMappingURL=targets.mjs.map
@@ -6,7 +6,6 @@ import "./storybook-urls.mjs";
6
6
  import "../models/build.mjs";
7
7
  import "../models/capture-attempt.mjs";
8
8
  import { Logger } from "pino";
9
- //#region src/capture/tests.d.ts
10
9
  /** What a build keeps of one `.test()` entry (JSON in `builds.test_results`). */
11
10
  interface TestResult {
12
11
  /** The test entry's id (`<story id>:<test name slug>`). */
@@ -32,6 +31,5 @@ interface TestSummary {
32
31
  declare function parseTestResults(json: string | null | undefined): TestResult[];
33
32
  /** Counts of a build's results. */
34
33
  declare function summarizeTests(results: readonly TestResult[]): TestSummary;
35
- //#endregion
36
34
  export { TestResult, TestSummary, parseTestResults, summarizeTests };
37
35
  //# sourceMappingURL=tests.d.mts.map
@@ -7,7 +7,7 @@ import { emitAttemptLog } from "../models/capture-attempt.mjs";
7
7
  import { classifyFailures } from "./failure-sets.mjs";
8
8
  import { renderBudgetMs } from "./render.mjs";
9
9
  import { StorybookAdapter } from "./storybook.mjs";
10
- //#region src/capture/tests.ts
10
+ // src/capture/tests.ts
11
11
  /** Longest failure message kept (rows and logs stay bounded). */
12
12
  const ERROR_MAX = 1e3;
13
13
  /**
@@ -20,7 +20,8 @@ const ERROR_MAX = 1e3;
20
20
  async function runProjectTests(input) {
21
21
  const tests = input.project.runTests ? await testsToRun(input) : [];
22
22
  const run = input.runner.runTests?.bind(input.runner);
23
- if (tests.length === 0 || !run) return await skipTests(input, tests.length);
23
+ if (tests.length === 0) return await skipTests(input, tests.length);
24
+ if (!run) return await unsupportedTests(input, tests);
24
25
  const results = resultsOf(tests, await execute(input, run, tests));
25
26
  await keepResults(input, results);
26
27
  return await reportFailures(input, results, tests);
@@ -34,6 +35,16 @@ async function skipTests(input, wanted) {
34
35
  flaky: /* @__PURE__ */ new Set()
35
36
  };
36
37
  }
38
+ /** Tests were wanted and exist, but the runner cannot run them: fail loudly rather than silently. */
39
+ async function unsupportedTests(input, tests) {
40
+ input.logger?.error({ tests: tests.length }, "project runs tests but the capture runner cannot run them; unsupported capability");
41
+ await emitAttemptLog(input.recordLog, input.logger, "error", "storybook tests unsupported", { tests: tests.length });
42
+ await keepResults(input, null);
43
+ return {
44
+ blocking: new Set(tests.map((test) => test.id)),
45
+ flaky: /* @__PURE__ */ new Set()
46
+ };
47
+ }
37
48
  /** The test entries to run: those of stories that are not skipped. */
38
49
  async function testsToRun(input) {
39
50
  const skipped = new Set(input.stories.filter((story) => isDisabledStory(story)).map((s) => s.id));
@@ -120,7 +131,6 @@ function isTestResult(value) {
120
131
  const record = value;
121
132
  return typeof record["id"] === "string" && typeof record["name"] === "string" && (record["status"] === "passed" || record["status"] === "failed");
122
133
  }
123
- //#endregion
124
134
  export { parseTestResults, runProjectTests, summarizeTests };
125
135
 
126
136
  //# sourceMappingURL=tests.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"tests.mjs","names":[],"sources":["../../src/capture/tests.ts"],"sourcesContent":["/** Running a project's Storybook 10 `.test()` entries and keeping what they reported (ADR 0004, decision 10). */\nimport type { Logger } from \"pino\";\nimport type { CaptureRunner, CaptureTarget, TestOutcome } from \"../adapters/capture-runner.ts\";\nimport type { BuildModel } from \"../models/build.ts\";\nimport { emitAttemptLog, type AttemptLogRecorder } from \"../models/capture-attempt.ts\";\nimport type { Build } from \"../schema/build.ts\";\nimport type { Project } from \"../schema/project.ts\";\nimport { redactSecrets } from \"../utils/redact.ts\";\nimport { isDisabledStory, type StoryEntry } from \"./adapter.ts\";\nimport { classifyFailures } from \"./failure-sets.ts\";\nimport { renderBudgetMs } from \"./render.ts\";\nimport type { StorybookUrlSource } from \"./storybook-urls.ts\";\nimport { StorybookAdapter } from \"./storybook.ts\";\n\n/** What a build keeps of one `.test()` entry (JSON in `builds.test_results`). */\nexport interface TestResult {\n /** The test entry's id (`<story id>:<test name slug>`). */\n id: string;\n /** Story file title (the component path), as on its index entry. */\n title: string;\n /** The test's own name, as written in `story.test(\"...\")`. */\n name: string;\n /** The story the test is written under. */\n parentId: string | null;\n status: \"passed\" | \"failed\";\n /** The failure message of a failed test. */\n error?: string;\n durationMs: number;\n}\n\n/** Pass and fail counts of a build's tests. */\nexport interface TestSummary {\n total: number;\n passed: number;\n failed: number;\n}\n\n/** Longest failure message kept (rows and logs stay bounded). */\nconst ERROR_MAX = 1000;\n\n/** Test ids by how their failure counts, as {@link runProjectTests} classifies them. */\nexport interface TestRunSets {\n /** Fail the build, like a failing `play` function. */\n blocking: Set<string>;\n /** Tests of a story marked flaky: logged, never blocking. */\n flaky: Set<string>;\n}\n\n/** Inputs for {@link runProjectTests}. */\nexport interface RunTestsInput {\n runner: CaptureRunner;\n builds: BuildModel;\n build: Build;\n project: Project;\n storybookDir: string;\n /** Every discovered story, so the tests of a skipped story are skipped too. */\n stories: readonly StoryEntry[];\n target: CaptureTarget;\n storybookUrl?: StorybookUrlSource | undefined;\n logger: Logger | undefined;\n recordLog?: AttemptLogRecorder | undefined;\n}\n\n/**\n * Run the build's `.test()` entries when the project turned tests on, and store the results on the\n * build. A failing test is a blocking failure of its own id, exactly like a `play` that throws; it\n * takes no screenshot, writes no snapshot and sets no status of its own. Without the setting (or\n * without test entries) nothing runs and nothing is written, except that results left by an\n * earlier run of the same build are cleared.\n */\nexport async function runProjectTests(input: RunTestsInput): Promise<TestRunSets> {\n const tests = input.project.runTests ? await testsToRun(input) : [];\n const run = input.runner.runTests?.bind(input.runner);\n if (tests.length === 0 || !run) return await skipTests(input, tests.length);\n const results = resultsOf(tests, await execute(input, run, tests));\n await keepResults(input, results);\n return await reportFailures(input, results, tests);\n}\n\n/** Nothing to run: warn when tests were wanted but the runner cannot, and clear stale results. */\nasync function skipTests(input: RunTestsInput, wanted: number): Promise<TestRunSets> {\n if (wanted > 0) {\n input.logger?.warn(\n { tests: wanted },\n \"project runs tests but the capture runner cannot run them; skipped\",\n );\n }\n await keepResults(input, null);\n return { blocking: new Set(), flaky: new Set() };\n}\n\n/** The test entries to run: those of stories that are not skipped. */\nasync function testsToRun(input: RunTestsInput): Promise<StoryEntry[]> {\n const skipped = new Set(input.stories.filter((story) => isDisabledStory(story)).map((s) => s.id));\n const found = await new StorybookAdapter().discoverTests(input.storybookDir);\n return found.filter(\n (entry) =>\n !isDisabledStory(entry) && !(entry.parentId !== undefined && skipped.has(entry.parentId)),\n );\n}\n\nasync function execute(\n input: RunTestsInput,\n run: NonNullable<CaptureRunner[\"runTests\"]>,\n tests: StoryEntry[],\n): Promise<TestOutcome[]> {\n const storybookUrl = await input.storybookUrl?.(input.target, {\n budgetMs: renderBudgetMs(tests.length),\n });\n await emitAttemptLog(input.recordLog, input.logger, \"info\", \"running storybook tests\", {\n tests: tests.length,\n });\n return await run({\n buildId: input.build.id,\n storybookDir: input.storybookDir,\n tests,\n playTimeoutMs: input.project.playTimeoutMs,\n target: input.target,\n ...(storybookUrl === undefined ? {} : { storybookUrl }),\n ...(input.logger ? { logger: input.logger } : {}),\n });\n}\n\n/** One result per test entry; a test the runner reported nothing for counts as failed. */\nfunction resultsOf(tests: readonly StoryEntry[], outcomes: readonly TestOutcome[]): TestResult[] {\n const byId = new Map(outcomes.map((outcome) => [outcome.storyId, outcome]));\n return tests.map((test) => {\n const outcome = byId.get(test.id);\n const error =\n outcome === undefined ? \"the runner reported no result for this test\" : outcome.error;\n const failed = outcome === undefined || outcome.status === \"failed\";\n return {\n id: test.id,\n title: test.title,\n name: test.name,\n parentId: test.parentId ?? null,\n status: failed ? \"failed\" : \"passed\",\n ...(failed ? { error: redactSecrets(error ?? \"test failed\").slice(0, ERROR_MAX) } : {}),\n durationMs: outcome?.durationMs ?? 0,\n };\n });\n}\n\n/** Write the results; a build that has none and never had any is left untouched. */\nasync function keepResults(input: RunTestsInput, results: TestResult[] | null): Promise<void> {\n if (results === null && !input.build.testResults) return;\n await input.builds.setTestResults(input.build.id, results);\n}\n\nasync function reportFailures(\n input: RunTestsInput,\n results: readonly TestResult[],\n tests: readonly StoryEntry[],\n): Promise<TestRunSets> {\n const failed = results.filter((result) => result.status === \"failed\");\n const classified = classifyFailures(\n tests,\n failed.map((result) => ({\n storyId: result.id,\n viewportName: \"test\",\n error: result.error ?? \"\",\n })),\n );\n const sets = { blocking: classified.blocking, flaky: classified.flaky };\n const fields = { tests: results.length, failed: failed.length, flaky: sets.flaky.size };\n const level = sets.blocking.size > 0 ? \"error\" : \"info\";\n input.logger?.[level](fields, \"storybook tests finished\");\n await emitAttemptLog(input.recordLog, input.logger, level, \"storybook tests finished\", fields);\n return sets;\n}\n\n/** The results a build holds; empty for none or an unreadable value. */\nexport function parseTestResults(json: string | null | undefined): TestResult[] {\n if (!json) return [];\n try {\n const parsed: unknown = JSON.parse(json);\n return Array.isArray(parsed) ? parsed.filter((item) => isTestResult(item)) : [];\n } catch {\n return [];\n }\n}\n\n/** Counts of a build's results. */\nexport function summarizeTests(results: readonly TestResult[]): TestSummary {\n const failed = results.filter((result) => result.status === \"failed\").length;\n return { total: results.length, passed: results.length - failed, failed };\n}\n\nfunction isTestResult(value: unknown): value is TestResult {\n if (typeof value !== \"object\" || value === null) return false;\n const record = value as Record<string, unknown>;\n return (\n typeof record[\"id\"] === \"string\" &&\n typeof record[\"name\"] === \"string\" &&\n (record[\"status\"] === \"passed\" || record[\"status\"] === \"failed\")\n );\n}\n"],"mappings":";;;;;;;;;;;AAsCA,MAAM,YAAY;;;;;;;;AAgClB,eAAsB,gBAAgB,OAA4C;CAChF,MAAM,QAAQ,MAAM,QAAQ,WAAW,MAAM,WAAW,KAAK,IAAI,CAAC;CAClE,MAAM,MAAM,MAAM,OAAO,UAAU,KAAK,MAAM,MAAM;CACpD,IAAI,MAAM,WAAW,KAAK,CAAC,KAAK,OAAO,MAAM,UAAU,OAAO,MAAM,MAAM;CAC1E,MAAM,UAAU,UAAU,OAAO,MAAM,QAAQ,OAAO,KAAK,KAAK,CAAC;CACjE,MAAM,YAAY,OAAO,OAAO;CAChC,OAAO,MAAM,eAAe,OAAO,SAAS,KAAK;AACnD;;AAGA,eAAe,UAAU,OAAsB,QAAsC;CACnF,IAAI,SAAS,GACX,MAAM,QAAQ,KACZ,EAAE,OAAO,OAAO,GAChB,oEACF;CAEF,MAAM,YAAY,OAAO,IAAI;CAC7B,OAAO;EAAE,0BAAU,IAAI,IAAI;EAAG,uBAAO,IAAI,IAAI;CAAE;AACjD;;AAGA,eAAe,WAAW,OAA6C;CACrE,MAAM,UAAU,IAAI,IAAI,MAAM,QAAQ,QAAQ,UAAU,gBAAgB,KAAK,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,EAAE,CAAC;CAEhG,QAAO,MADa,IAAI,iBAAiB,CAAC,CAAC,cAAc,MAAM,YAAY,EAAA,CAC9D,QACV,UACC,CAAC,gBAAgB,KAAK,KAAK,EAAE,MAAM,aAAa,KAAA,KAAa,QAAQ,IAAI,MAAM,QAAQ,EAC3F;AACF;AAEA,eAAe,QACb,OACA,KACA,OACwB;CACxB,MAAM,eAAe,MAAM,MAAM,eAAe,MAAM,QAAQ,EAC5D,UAAU,eAAe,MAAM,MAAM,EACvC,CAAC;CACD,MAAM,eAAe,MAAM,WAAW,MAAM,QAAQ,QAAQ,2BAA2B,EACrF,OAAO,MAAM,OACf,CAAC;CACD,OAAO,MAAM,IAAI;EACf,SAAS,MAAM,MAAM;EACrB,cAAc,MAAM;EACpB;EACA,eAAe,MAAM,QAAQ;EAC7B,QAAQ,MAAM;EACd,GAAI,iBAAiB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa;EACrD,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;CACjD,CAAC;AACH;;AAGA,SAAS,UAAU,OAA8B,UAAgD;CAC/F,MAAM,OAAO,IAAI,IAAI,SAAS,KAAK,YAAY,CAAC,QAAQ,SAAS,OAAO,CAAC,CAAC;CAC1E,OAAO,MAAM,KAAK,SAAS;EACzB,MAAM,UAAU,KAAK,IAAI,KAAK,EAAE;EAChC,MAAM,QACJ,YAAY,KAAA,IAAY,gDAAgD,QAAQ;EAClF,MAAM,SAAS,YAAY,KAAA,KAAa,QAAQ,WAAW;EAC3D,OAAO;GACL,IAAI,KAAK;GACT,OAAO,KAAK;GACZ,MAAM,KAAK;GACX,UAAU,KAAK,YAAY;GAC3B,QAAQ,SAAS,WAAW;GAC5B,GAAI,SAAS,EAAE,OAAO,cAAc,SAAS,aAAa,CAAC,CAAC,MAAM,GAAG,SAAS,EAAE,IAAI,CAAC;GACrF,YAAY,SAAS,cAAc;EACrC;CACF,CAAC;AACH;;AAGA,eAAe,YAAY,OAAsB,SAA6C;CAC5F,IAAI,YAAY,QAAQ,CAAC,MAAM,MAAM,aAAa;CAClD,MAAM,MAAM,OAAO,eAAe,MAAM,MAAM,IAAI,OAAO;AAC3D;AAEA,eAAe,eACb,OACA,SACA,OACsB;CACtB,MAAM,SAAS,QAAQ,QAAQ,WAAW,OAAO,WAAW,QAAQ;CACpE,MAAM,aAAa,iBACjB,OACA,OAAO,KAAK,YAAY;EACtB,SAAS,OAAO;EAChB,cAAc;EACd,OAAO,OAAO,SAAS;CACzB,EAAE,CACJ;CACA,MAAM,OAAO;EAAE,UAAU,WAAW;EAAU,OAAO,WAAW;CAAM;CACtE,MAAM,SAAS;EAAE,OAAO,QAAQ;EAAQ,QAAQ,OAAO;EAAQ,OAAO,KAAK,MAAM;CAAK;CACtF,MAAM,QAAQ,KAAK,SAAS,OAAO,IAAI,UAAU;CACjD,MAAM,SAAS,MAAM,CAAC,QAAQ,0BAA0B;CACxD,MAAM,eAAe,MAAM,WAAW,MAAM,QAAQ,OAAO,4BAA4B,MAAM;CAC7F,OAAO;AACT;;AAGA,SAAgB,iBAAiB,MAA+C;CAC9E,IAAI,CAAC,MAAM,OAAO,CAAC;CACnB,IAAI;EACF,MAAM,SAAkB,KAAK,MAAM,IAAI;EACvC,OAAO,MAAM,QAAQ,MAAM,IAAI,OAAO,QAAQ,SAAS,aAAa,IAAI,CAAC,IAAI,CAAC;CAChF,QAAQ;EACN,OAAO,CAAC;CACV;AACF;;AAGA,SAAgB,eAAe,SAA6C;CAC1E,MAAM,SAAS,QAAQ,QAAQ,WAAW,OAAO,WAAW,QAAQ,CAAC,CAAC;CACtE,OAAO;EAAE,OAAO,QAAQ;EAAQ,QAAQ,QAAQ,SAAS;EAAQ;CAAO;AAC1E;AAEA,SAAS,aAAa,OAAqC;CACzD,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM,OAAO;CACxD,MAAM,SAAS;CACf,OACE,OAAO,OAAO,UAAU,YACxB,OAAO,OAAO,YAAY,aACzB,OAAO,cAAc,YAAY,OAAO,cAAc;AAE3D"}
1
+ {"version":3,"file":"tests.mjs","names":[],"sources":["../../src/capture/tests.ts"],"sourcesContent":["/** Running a project's Storybook 10 `.test()` entries and keeping what they reported (ADR 0004, decision 10). */\nimport type { Logger } from \"pino\";\nimport type { CaptureRunner, CaptureTarget, TestOutcome } from \"../adapters/capture-runner.ts\";\nimport type { BuildModel } from \"../models/build.ts\";\nimport { emitAttemptLog, type AttemptLogRecorder } from \"../models/capture-attempt.ts\";\nimport type { Build } from \"../schema/build.ts\";\nimport type { Project } from \"../schema/project.ts\";\nimport { redactSecrets } from \"../utils/redact.ts\";\nimport { isDisabledStory, type StoryEntry } from \"./adapter.ts\";\nimport { classifyFailures } from \"./failure-sets.ts\";\nimport { renderBudgetMs } from \"./render.ts\";\nimport type { StorybookUrlSource } from \"./storybook-urls.ts\";\nimport { StorybookAdapter } from \"./storybook.ts\";\n\n/** What a build keeps of one `.test()` entry (JSON in `builds.test_results`). */\nexport interface TestResult {\n /** The test entry's id (`<story id>:<test name slug>`). */\n id: string;\n /** Story file title (the component path), as on its index entry. */\n title: string;\n /** The test's own name, as written in `story.test(\"...\")`. */\n name: string;\n /** The story the test is written under. */\n parentId: string | null;\n status: \"passed\" | \"failed\";\n /** The failure message of a failed test. */\n error?: string;\n durationMs: number;\n}\n\n/** Pass and fail counts of a build's tests. */\nexport interface TestSummary {\n total: number;\n passed: number;\n failed: number;\n}\n\n/** Longest failure message kept (rows and logs stay bounded). */\nconst ERROR_MAX = 1000;\n\n/** Test ids by how their failure counts, as {@link runProjectTests} classifies them. */\nexport interface TestRunSets {\n /** Fail the build, like a failing `play` function. */\n blocking: Set<string>;\n /** Tests of a story marked flaky: logged, never blocking. */\n flaky: Set<string>;\n}\n\n/** Inputs for {@link runProjectTests}. */\nexport interface RunTestsInput {\n runner: CaptureRunner;\n builds: BuildModel;\n build: Build;\n project: Project;\n storybookDir: string;\n /** Every discovered story, so the tests of a skipped story are skipped too. */\n stories: readonly StoryEntry[];\n target: CaptureTarget;\n storybookUrl?: StorybookUrlSource | undefined;\n logger: Logger | undefined;\n recordLog?: AttemptLogRecorder | undefined;\n}\n\n/**\n * Run the build's `.test()` entries when the project turned tests on, and store the results on the\n * build. A failing test is a blocking failure of its own id, exactly like a `play` that throws; it\n * takes no screenshot, writes no snapshot and sets no status of its own. Without the setting (or\n * without test entries) nothing runs and nothing is written, except that results left by an\n * earlier run of the same build are cleared.\n */\nexport async function runProjectTests(input: RunTestsInput): Promise<TestRunSets> {\n const tests = input.project.runTests ? await testsToRun(input) : [];\n const run = input.runner.runTests?.bind(input.runner);\n if (tests.length === 0) return await skipTests(input, tests.length);\n if (!run) return await unsupportedTests(input, tests);\n const results = resultsOf(tests, await execute(input, run, tests));\n await keepResults(input, results);\n return await reportFailures(input, results, tests);\n}\n\n/** Nothing to run: warn when tests were wanted but the runner cannot, and clear stale results. */\nasync function skipTests(input: RunTestsInput, wanted: number): Promise<TestRunSets> {\n if (wanted > 0) {\n input.logger?.warn(\n { tests: wanted },\n \"project runs tests but the capture runner cannot run them; skipped\",\n );\n }\n await keepResults(input, null);\n return { blocking: new Set(), flaky: new Set() };\n}\n\n/** Tests were wanted and exist, but the runner cannot run them: fail loudly rather than silently. */\nasync function unsupportedTests(\n input: RunTestsInput,\n tests: readonly StoryEntry[],\n): Promise<TestRunSets> {\n input.logger?.error(\n { tests: tests.length },\n \"project runs tests but the capture runner cannot run them; unsupported capability\",\n );\n await emitAttemptLog(input.recordLog, input.logger, \"error\", \"storybook tests unsupported\", {\n tests: tests.length,\n });\n await keepResults(input, null);\n return { blocking: new Set(tests.map((test) => test.id)), flaky: new Set() };\n}\n\n/** The test entries to run: those of stories that are not skipped. */\nasync function testsToRun(input: RunTestsInput): Promise<StoryEntry[]> {\n const skipped = new Set(input.stories.filter((story) => isDisabledStory(story)).map((s) => s.id));\n const found = await new StorybookAdapter().discoverTests(input.storybookDir);\n return found.filter(\n (entry) =>\n !isDisabledStory(entry) && !(entry.parentId !== undefined && skipped.has(entry.parentId)),\n );\n}\n\nasync function execute(\n input: RunTestsInput,\n run: NonNullable<CaptureRunner[\"runTests\"]>,\n tests: StoryEntry[],\n): Promise<TestOutcome[]> {\n const storybookUrl = await input.storybookUrl?.(input.target, {\n budgetMs: renderBudgetMs(tests.length),\n });\n await emitAttemptLog(input.recordLog, input.logger, \"info\", \"running storybook tests\", {\n tests: tests.length,\n });\n return await run({\n buildId: input.build.id,\n storybookDir: input.storybookDir,\n tests,\n playTimeoutMs: input.project.playTimeoutMs,\n target: input.target,\n ...(storybookUrl === undefined ? {} : { storybookUrl }),\n ...(input.logger ? { logger: input.logger } : {}),\n });\n}\n\n/** One result per test entry; a test the runner reported nothing for counts as failed. */\nfunction resultsOf(tests: readonly StoryEntry[], outcomes: readonly TestOutcome[]): TestResult[] {\n const byId = new Map(outcomes.map((outcome) => [outcome.storyId, outcome]));\n return tests.map((test) => {\n const outcome = byId.get(test.id);\n const error =\n outcome === undefined ? \"the runner reported no result for this test\" : outcome.error;\n const failed = outcome === undefined || outcome.status === \"failed\";\n return {\n id: test.id,\n title: test.title,\n name: test.name,\n parentId: test.parentId ?? null,\n status: failed ? \"failed\" : \"passed\",\n ...(failed ? { error: redactSecrets(error ?? \"test failed\").slice(0, ERROR_MAX) } : {}),\n durationMs: outcome?.durationMs ?? 0,\n };\n });\n}\n\n/** Write the results; a build that has none and never had any is left untouched. */\nasync function keepResults(input: RunTestsInput, results: TestResult[] | null): Promise<void> {\n if (results === null && !input.build.testResults) return;\n await input.builds.setTestResults(input.build.id, results);\n}\n\nasync function reportFailures(\n input: RunTestsInput,\n results: readonly TestResult[],\n tests: readonly StoryEntry[],\n): Promise<TestRunSets> {\n const failed = results.filter((result) => result.status === \"failed\");\n const classified = classifyFailures(\n tests,\n failed.map((result) => ({\n storyId: result.id,\n viewportName: \"test\",\n error: result.error ?? \"\",\n })),\n );\n const sets = { blocking: classified.blocking, flaky: classified.flaky };\n const fields = { tests: results.length, failed: failed.length, flaky: sets.flaky.size };\n const level = sets.blocking.size > 0 ? \"error\" : \"info\";\n input.logger?.[level](fields, \"storybook tests finished\");\n await emitAttemptLog(input.recordLog, input.logger, level, \"storybook tests finished\", fields);\n return sets;\n}\n\n/** The results a build holds; empty for none or an unreadable value. */\nexport function parseTestResults(json: string | null | undefined): TestResult[] {\n if (!json) return [];\n try {\n const parsed: unknown = JSON.parse(json);\n return Array.isArray(parsed) ? parsed.filter((item) => isTestResult(item)) : [];\n } catch {\n return [];\n }\n}\n\n/** Counts of a build's results. */\nexport function summarizeTests(results: readonly TestResult[]): TestSummary {\n const failed = results.filter((result) => result.status === \"failed\").length;\n return { total: results.length, passed: results.length - failed, failed };\n}\n\nfunction isTestResult(value: unknown): value is TestResult {\n if (typeof value !== \"object\" || value === null) return false;\n const record = value as Record<string, unknown>;\n return (\n typeof record[\"id\"] === \"string\" &&\n typeof record[\"name\"] === \"string\" &&\n (record[\"status\"] === \"passed\" || record[\"status\"] === \"failed\")\n );\n}\n"],"mappings":"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"}
@@ -3,13 +3,22 @@ import "node:url";
3
3
  import.meta.url;
4
4
  import { PER_CAPTURE_BUDGET_MS } from "../utils/storybook-token.mjs";
5
5
  import { renderBudgetMs, renderWithTimeout } from "./render.mjs";
6
- //#region src/capture/token-chunks.ts
6
+ // src/capture/token-chunks.ts
7
+ /**
8
+ * The project's signed URL lifetime cannot hold what the pass must capture (a configuration
9
+ * problem): retrying or isolating stories cannot help, so the job fails once with this message.
10
+ */
11
+ var SignedUrlLifetimeError = class extends Error {
12
+ name = "SignedUrlLifetimeError";
13
+ };
7
14
  /** Render the pass, minting a fresh signed URL for each slice; results are concatenated. */
8
15
  async function renderInSlices(input) {
9
16
  const slices = slicesOf(input);
10
17
  const results = [];
11
18
  for (const slice of slices) {
12
- const budgetMs = renderBudgetMs(slice.expected, input.renderTimeoutMs);
19
+ const requestedBudgetMs = renderBudgetMs(slice.expected, input.renderTimeoutMs);
20
+ const ttl = input.storybookUrl?.ttlSeconds?.(input.browser, requestedBudgetMs);
21
+ const budgetMs = ttl === void 0 ? requestedBudgetMs : Math.min(requestedBudgetMs, (ttl - 300) * 1e3);
13
22
  const storybookUrl = await input.storybookUrl?.(input.browser, { budgetMs });
14
23
  results.push(await renderWithTimeout(input.runner, {
15
24
  ...input.request,
@@ -30,8 +39,17 @@ function slicesOf(input) {
30
39
  }];
31
40
  const ttl = input.storybookUrl?.ttlSeconds?.(input.browser, renderBudgetMs(expected, input.renderTimeoutMs));
32
41
  const budgetSeconds = renderBudgetMs(expected, input.renderTimeoutMs) / 1e3;
33
- if (ttl === void 0 || input.renderTimeoutMs !== void 0 || budgetSeconds + 300 <= ttl) return whole;
34
- return cut(stories, counts, Math.max(50, Math.floor((ttl - 300) * 1e3 / PER_CAPTURE_BUDGET_MS)));
42
+ if (ttl === void 0 || budgetSeconds + 300 <= ttl) return whole;
43
+ return cut(stories, counts, sliceLimit(ttl));
44
+ }
45
+ function sliceLimit(ttl) {
46
+ const limit = Math.floor((ttl - 300) * 1e3 / PER_CAPTURE_BUDGET_MS);
47
+ if (limit < 1) throw new SignedUrlLifetimeError("Signed Storybook URL lifetime is too short for one capture and its grace period; increase or clear the project lifetime override.");
48
+ return limit;
49
+ }
50
+ function checkedCount(count, limit) {
51
+ if (count > limit) throw new SignedUrlLifetimeError(`A story expands to ${count} captures, exceeding the signed URL slice limit of ${limit}; increase or clear the project lifetime override.`);
52
+ return count;
35
53
  }
36
54
  function cut(stories, counts, limit) {
37
55
  const slices = [];
@@ -40,7 +58,7 @@ function cut(stories, counts, limit) {
40
58
  expected: 0
41
59
  };
42
60
  for (const [index, story] of stories.entries()) {
43
- const count = counts[index] ?? 0;
61
+ const count = checkedCount(counts[index] ?? 0, limit);
44
62
  if (current.stories.length > 0 && current.expected + count > limit) {
45
63
  slices.push(current);
46
64
  current = {
@@ -68,7 +86,6 @@ function tagged(result) {
68
86
  for (const capture of result.captures) if (result.environment) capture.environment ??= result.environment;
69
87
  return result.captures;
70
88
  }
71
- //#endregion
72
- export { renderInSlices };
89
+ export { SignedUrlLifetimeError, renderInSlices };
73
90
 
74
91
  //# sourceMappingURL=token-chunks.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"token-chunks.mjs","names":[],"sources":["../../src/capture/token-chunks.ts"],"sourcesContent":["/**\n * Renders a pass in slices that each fit one signed Storybook URL (ADR 0007, decision 5). A token\n * never lives longer than `MAX_STORYBOOK_URL_TTL` (and a project may set it lower), so a pass whose\n * budget outlasts that is cut into slices of stories and every slice asks the source for a fresh\n * token just before it starts: the render never dies when its token does. A pass that fits one\n * token (the usual case, and every target without a signed URL) is one render call, as before.\n */\nimport type { CaptureTarget, CaptureRunner, RenderResult } from \"../adapters/capture-runner.ts\";\nimport {\n MIN_CAPTURES_PER_SLICE,\n PER_CAPTURE_BUDGET_MS,\n STORYBOOK_URL_TTL_GRACE,\n} from \"../utils/storybook-token.ts\";\nimport type { StoryEntry } from \"./adapter.ts\";\nimport { renderBudgetMs, renderWithTimeout } from \"./render.ts\";\nimport type { RenderRequest } from \"./render.ts\";\nimport type { StorybookUrlSource } from \"./storybook-urls.ts\";\n\n/** What cutting a pass into slices needs of its caller. */\nexport interface SliceInput {\n runner: CaptureRunner;\n /** The render call without its `storybookUrl`; its `stories` are the whole pass. */\n request: RenderRequest;\n browser: CaptureTarget;\n storybookUrl: StorybookUrlSource | undefined;\n /** Captures one story expands to (its viewports and modes). */\n captureCount: (story: StoryEntry) => number;\n /** An explicit budget fixes one call's timeout; slices then are not budgeted separately. */\n renderTimeoutMs: number | undefined;\n}\n\n/** Render the pass, minting a fresh signed URL for each slice; results are concatenated. */\nexport async function renderInSlices(input: SliceInput): Promise<RenderResult> {\n const slices = slicesOf(input);\n const results: RenderResult[] = [];\n for (const slice of slices) {\n const budgetMs = renderBudgetMs(slice.expected, input.renderTimeoutMs);\n // oxlint-disable-next-line no-await-in-loop -- each slice needs a token minted just before it\n const storybookUrl = await input.storybookUrl?.(input.browser, { budgetMs });\n results.push(\n // oxlint-disable-next-line no-await-in-loop -- slices render one after another\n await renderWithTimeout(\n input.runner,\n {\n ...input.request,\n stories: slice.stories,\n ...(storybookUrl === undefined ? {} : { storybookUrl }),\n },\n budgetMs,\n ),\n );\n }\n return mergeResults(results);\n}\n\ninterface Slice {\n stories: StoryEntry[];\n expected: number;\n}\n\n/** One slice with every story, unless the pass outlasts the lifetime a token can have. */\nfunction slicesOf(input: SliceInput): Slice[] {\n const { stories } = input.request;\n const counts = stories.map((story) => input.captureCount(story));\n const expected = counts.reduce((sum, count) => sum + count, 0);\n const whole: Slice[] = [{ stories, expected }];\n const ttl = input.storybookUrl?.ttlSeconds?.(\n input.browser,\n renderBudgetMs(expected, input.renderTimeoutMs),\n );\n const budgetSeconds = renderBudgetMs(expected, input.renderTimeoutMs) / 1000;\n if (\n ttl === undefined ||\n input.renderTimeoutMs !== undefined ||\n budgetSeconds + STORYBOOK_URL_TTL_GRACE <= ttl\n ) {\n return whole;\n }\n const limit = Math.max(\n MIN_CAPTURES_PER_SLICE,\n Math.floor(((ttl - STORYBOOK_URL_TTL_GRACE) * 1000) / PER_CAPTURE_BUDGET_MS),\n );\n return cut(stories, counts, limit);\n}\n\nfunction cut(stories: StoryEntry[], counts: number[], limit: number): Slice[] {\n const slices: Slice[] = [];\n let current: Slice = { stories: [], expected: 0 };\n for (const [index, story] of stories.entries()) {\n const count = counts[index] ?? 0;\n if (current.stories.length > 0 && current.expected + count > limit) {\n slices.push(current);\n current = { stories: [], expected: 0 };\n }\n current.stories.push(story);\n current.expected += count;\n }\n return [...slices, current];\n}\n\nfunction mergeResults(results: RenderResult[]): RenderResult {\n const [only] = results;\n if (results.length === 1 && only) {\n return only;\n }\n const environment = results.find((result) => result.environment)?.environment;\n return {\n captures: results.flatMap((result) => tagged(result)),\n failures: results.flatMap((result) => result.failures),\n ...(environment ? { environment } : {}),\n };\n}\n\n/** A slice's captures with the environment that slice reported (a reconnect may differ). */\nfunction tagged(result: RenderResult): RenderResult[\"captures\"] {\n for (const capture of result.captures) {\n if (result.environment) {\n capture.environment ??= result.environment;\n }\n }\n return result.captures;\n}\n"],"mappings":";;;;;;;AAgCA,eAAsB,eAAe,OAA0C;CAC7E,MAAM,SAAS,SAAS,KAAK;CAC7B,MAAM,UAA0B,CAAC;CACjC,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,WAAW,eAAe,MAAM,UAAU,MAAM,eAAe;EAErE,MAAM,eAAe,MAAM,MAAM,eAAe,MAAM,SAAS,EAAE,SAAS,CAAC;EAC3E,QAAQ,KAEN,MAAM,kBACJ,MAAM,QACN;GACE,GAAG,MAAM;GACT,SAAS,MAAM;GACf,GAAI,iBAAiB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa;EACvD,GACA,QACF,CACF;CACF;CACA,OAAO,aAAa,OAAO;AAC7B;;AAQA,SAAS,SAAS,OAA4B;CAC5C,MAAM,EAAE,YAAY,MAAM;CAC1B,MAAM,SAAS,QAAQ,KAAK,UAAU,MAAM,aAAa,KAAK,CAAC;CAC/D,MAAM,WAAW,OAAO,QAAQ,KAAK,UAAU,MAAM,OAAO,CAAC;CAC7D,MAAM,QAAiB,CAAC;EAAE;EAAS;CAAS,CAAC;CAC7C,MAAM,MAAM,MAAM,cAAc,aAC9B,MAAM,SACN,eAAe,UAAU,MAAM,eAAe,CAChD;CACA,MAAM,gBAAgB,eAAe,UAAU,MAAM,eAAe,IAAI;CACxE,IACE,QAAQ,KAAA,KACR,MAAM,oBAAoB,KAAA,KAC1B,gBAAA,OAA2C,KAE3C,OAAO;CAMT,OAAO,IAAI,SAAS,QAJN,KAAK,IAAA,IAEjB,KAAK,OAAQ,MAAA,OAAiC,MAAQ,qBAAqB,CAE7C,CAAC;AACnC;AAEA,SAAS,IAAI,SAAuB,QAAkB,OAAwB;CAC5E,MAAM,SAAkB,CAAC;CACzB,IAAI,UAAiB;EAAE,SAAS,CAAC;EAAG,UAAU;CAAE;CAChD,KAAK,MAAM,CAAC,OAAO,UAAU,QAAQ,QAAQ,GAAG;EAC9C,MAAM,QAAQ,OAAO,UAAU;EAC/B,IAAI,QAAQ,QAAQ,SAAS,KAAK,QAAQ,WAAW,QAAQ,OAAO;GAClE,OAAO,KAAK,OAAO;GACnB,UAAU;IAAE,SAAS,CAAC;IAAG,UAAU;GAAE;EACvC;EACA,QAAQ,QAAQ,KAAK,KAAK;EAC1B,QAAQ,YAAY;CACtB;CACA,OAAO,CAAC,GAAG,QAAQ,OAAO;AAC5B;AAEA,SAAS,aAAa,SAAuC;CAC3D,MAAM,CAAC,QAAQ;CACf,IAAI,QAAQ,WAAW,KAAK,MAC1B,OAAO;CAET,MAAM,cAAc,QAAQ,MAAM,WAAW,OAAO,WAAW,CAAC,EAAE;CAClE,OAAO;EACL,UAAU,QAAQ,SAAS,WAAW,OAAO,MAAM,CAAC;EACpD,UAAU,QAAQ,SAAS,WAAW,OAAO,QAAQ;EACrD,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;CACvC;AACF;;AAGA,SAAS,OAAO,QAAgD;CAC9D,KAAK,MAAM,WAAW,OAAO,UAC3B,IAAI,OAAO,aACT,QAAQ,gBAAgB,OAAO;CAGnC,OAAO,OAAO;AAChB"}
1
+ {"version":3,"file":"token-chunks.mjs","names":[],"sources":["../../src/capture/token-chunks.ts"],"sourcesContent":["/**\n * Renders a pass in slices that each fit one signed Storybook URL (ADR 0007, decision 5). A token\n * never lives longer than `MAX_STORYBOOK_URL_TTL` (and a project may set it lower), so a pass whose\n * budget outlasts that is cut into slices of stories and every slice asks the source for a fresh\n * token just before it starts: the render never dies when its token does. A pass that fits one\n * token (the usual case, and every target without a signed URL) is one render call, as before.\n */\nimport type { CaptureTarget, CaptureRunner, RenderResult } from \"../adapters/capture-runner.ts\";\nimport { PER_CAPTURE_BUDGET_MS, STORYBOOK_URL_TTL_GRACE } from \"../utils/storybook-token.ts\";\nimport type { StoryEntry } from \"./adapter.ts\";\nimport { renderBudgetMs, renderWithTimeout } from \"./render.ts\";\nimport type { RenderRequest } from \"./render.ts\";\nimport type { StorybookUrlSource } from \"./storybook-urls.ts\";\n\n/** What cutting a pass into slices needs of its caller. */\nexport interface SliceInput {\n runner: CaptureRunner;\n /** The render call without its `storybookUrl`; its `stories` are the whole pass. */\n request: RenderRequest;\n browser: CaptureTarget;\n storybookUrl: StorybookUrlSource | undefined;\n /** Captures one story expands to (its viewports and modes). */\n captureCount: (story: StoryEntry) => number;\n /** An explicit budget limits each call; a signed URL may require a shorter timeout. */\n renderTimeoutMs: number | undefined;\n}\n\n/**\n * The project's signed URL lifetime cannot hold what the pass must capture (a configuration\n * problem): retrying or isolating stories cannot help, so the job fails once with this message.\n */\nexport class SignedUrlLifetimeError extends Error {\n override readonly name = \"SignedUrlLifetimeError\";\n}\n\n/** Render the pass, minting a fresh signed URL for each slice; results are concatenated. */\nexport async function renderInSlices(input: SliceInput): Promise<RenderResult> {\n const slices = slicesOf(input);\n const results: RenderResult[] = [];\n for (const slice of slices) {\n const requestedBudgetMs = renderBudgetMs(slice.expected, input.renderTimeoutMs);\n const ttl = input.storybookUrl?.ttlSeconds?.(input.browser, requestedBudgetMs);\n const budgetMs =\n ttl === undefined\n ? requestedBudgetMs\n : Math.min(requestedBudgetMs, (ttl - STORYBOOK_URL_TTL_GRACE) * 1000);\n // oxlint-disable-next-line no-await-in-loop -- each slice needs a token minted just before it\n const storybookUrl = await input.storybookUrl?.(input.browser, { budgetMs });\n results.push(\n // oxlint-disable-next-line no-await-in-loop -- slices render one after another\n await renderWithTimeout(\n input.runner,\n {\n ...input.request,\n stories: slice.stories,\n ...(storybookUrl === undefined ? {} : { storybookUrl }),\n },\n budgetMs,\n ),\n );\n }\n return mergeResults(results);\n}\n\ninterface Slice {\n stories: StoryEntry[];\n expected: number;\n}\n\n/** One slice with every story, unless the pass outlasts the lifetime a token can have. */\nfunction slicesOf(input: SliceInput): Slice[] {\n const { stories } = input.request;\n const counts = stories.map((story) => input.captureCount(story));\n const expected = counts.reduce((sum, count) => sum + count, 0);\n const whole: Slice[] = [{ stories, expected }];\n const ttl = input.storybookUrl?.ttlSeconds?.(\n input.browser,\n renderBudgetMs(expected, input.renderTimeoutMs),\n );\n const budgetSeconds = renderBudgetMs(expected, input.renderTimeoutMs) / 1000;\n if (ttl === undefined || budgetSeconds + STORYBOOK_URL_TTL_GRACE <= ttl) {\n return whole;\n }\n return cut(stories, counts, sliceLimit(ttl));\n}\n\nfunction sliceLimit(ttl: number): number {\n const limit = Math.floor(((ttl - STORYBOOK_URL_TTL_GRACE) * 1000) / PER_CAPTURE_BUDGET_MS);\n if (limit < 1) {\n throw new SignedUrlLifetimeError(\n \"Signed Storybook URL lifetime is too short for one capture and its grace period; increase or clear the project lifetime override.\",\n );\n }\n return limit;\n}\n\nfunction checkedCount(count: number, limit: number): number {\n if (count > limit) {\n throw new SignedUrlLifetimeError(\n `A story expands to ${count} captures, exceeding the signed URL slice limit of ${limit}; increase or clear the project lifetime override.`,\n );\n }\n return count;\n}\n\nfunction cut(stories: StoryEntry[], counts: number[], limit: number): Slice[] {\n const slices: Slice[] = [];\n let current: Slice = { stories: [], expected: 0 };\n for (const [index, story] of stories.entries()) {\n const count = checkedCount(counts[index] ?? 0, limit);\n if (current.stories.length > 0 && current.expected + count > limit) {\n slices.push(current);\n current = { stories: [], expected: 0 };\n }\n current.stories.push(story);\n current.expected += count;\n }\n return [...slices, current];\n}\n\nfunction mergeResults(results: RenderResult[]): RenderResult {\n const [only] = results;\n if (results.length === 1 && only) {\n return only;\n }\n const environment = results.find((result) => result.environment)?.environment;\n return {\n captures: results.flatMap((result) => tagged(result)),\n failures: results.flatMap((result) => result.failures),\n ...(environment ? { environment } : {}),\n };\n}\n\n/** A slice's captures with the environment that slice reported (a reconnect may differ). */\nfunction tagged(result: RenderResult): RenderResult[\"captures\"] {\n for (const capture of result.captures) {\n if (result.environment) {\n capture.environment ??= result.environment;\n }\n }\n return result.captures;\n}\n"],"mappings":";;;;;;;;;;AA+BA,IAAa,yBAAb,cAA4C,MAAM;CAChD,OAAyB;AAC3B;;AAGA,eAAsB,eAAe,OAA0C;CAC7E,MAAM,SAAS,SAAS,KAAK;CAC7B,MAAM,UAA0B,CAAC;CACjC,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,oBAAoB,eAAe,MAAM,UAAU,MAAM,eAAe;EAC9E,MAAM,MAAM,MAAM,cAAc,aAAa,MAAM,SAAS,iBAAiB;EAC7E,MAAM,WACJ,QAAQ,KAAA,IACJ,oBACA,KAAK,IAAI,oBAAoB,MAAA,OAAiC,GAAI;EAExE,MAAM,eAAe,MAAM,MAAM,eAAe,MAAM,SAAS,EAAE,SAAS,CAAC;EAC3E,QAAQ,KAEN,MAAM,kBACJ,MAAM,QACN;GACE,GAAG,MAAM;GACT,SAAS,MAAM;GACf,GAAI,iBAAiB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa;EACvD,GACA,QACF,CACF;CACF;CACA,OAAO,aAAa,OAAO;AAC7B;;AAQA,SAAS,SAAS,OAA4B;CAC5C,MAAM,EAAE,YAAY,MAAM;CAC1B,MAAM,SAAS,QAAQ,KAAK,UAAU,MAAM,aAAa,KAAK,CAAC;CAC/D,MAAM,WAAW,OAAO,QAAQ,KAAK,UAAU,MAAM,OAAO,CAAC;CAC7D,MAAM,QAAiB,CAAC;EAAE;EAAS;CAAS,CAAC;CAC7C,MAAM,MAAM,MAAM,cAAc,aAC9B,MAAM,SACN,eAAe,UAAU,MAAM,eAAe,CAChD;CACA,MAAM,gBAAgB,eAAe,UAAU,MAAM,eAAe,IAAI;CACxE,IAAI,QAAQ,KAAA,KAAa,gBAAA,OAA2C,KAClE,OAAO;CAET,OAAO,IAAI,SAAS,QAAQ,WAAW,GAAG,CAAC;AAC7C;AAEA,SAAS,WAAW,KAAqB;CACvC,MAAM,QAAQ,KAAK,OAAQ,MAAA,OAAiC,MAAQ,qBAAqB;CACzF,IAAI,QAAQ,GACV,MAAM,IAAI,uBACR,mIACF;CAEF,OAAO;AACT;AAEA,SAAS,aAAa,OAAe,OAAuB;CAC1D,IAAI,QAAQ,OACV,MAAM,IAAI,uBACR,sBAAsB,MAAM,qDAAqD,MAAM,mDACzF;CAEF,OAAO;AACT;AAEA,SAAS,IAAI,SAAuB,QAAkB,OAAwB;CAC5E,MAAM,SAAkB,CAAC;CACzB,IAAI,UAAiB;EAAE,SAAS,CAAC;EAAG,UAAU;CAAE;CAChD,KAAK,MAAM,CAAC,OAAO,UAAU,QAAQ,QAAQ,GAAG;EAC9C,MAAM,QAAQ,aAAa,OAAO,UAAU,GAAG,KAAK;EACpD,IAAI,QAAQ,QAAQ,SAAS,KAAK,QAAQ,WAAW,QAAQ,OAAO;GAClE,OAAO,KAAK,OAAO;GACnB,UAAU;IAAE,SAAS,CAAC;IAAG,UAAU;GAAE;EACvC;EACA,QAAQ,QAAQ,KAAK,KAAK;EAC1B,QAAQ,YAAY;CACtB;CACA,OAAO,CAAC,GAAG,QAAQ,OAAO;AAC5B;AAEA,SAAS,aAAa,SAAuC;CAC3D,MAAM,CAAC,QAAQ;CACf,IAAI,QAAQ,WAAW,KAAK,MAC1B,OAAO;CAET,MAAM,cAAc,QAAQ,MAAM,WAAW,OAAO,WAAW,CAAC,EAAE;CAClE,OAAO;EACL,UAAU,QAAQ,SAAS,WAAW,OAAO,MAAM,CAAC;EACpD,UAAU,QAAQ,SAAS,WAAW,OAAO,QAAQ;EACrD,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;CACvC;AACF;;AAGA,SAAS,OAAO,QAAgD;CAC9D,KAAK,MAAM,WAAW,OAAO,UAC3B,IAAI,OAAO,aACT,QAAQ,gBAAgB,OAAO;CAGnC,OAAO,OAAO;AAChB"}
@@ -5,6 +5,7 @@ import { LeaseRenewal, PollableCaptureQueue, PollableJob } from "./adapter/captu
5
5
  import { MAX_BROWSERS, assertBrowserCount, assertSupportedBrowsers, listTargets, parseBrowsers, primaryTargetOf, refusalFor, resolveBrowsers } from "./capture/browsers.mjs";
6
6
  import { NO_ORIGIN_REASON, NO_SECRET_REASON, TargetPrerequisites, offeredTargets } from "./capture/targets.mjs";
7
7
  import { StorybookUrlSource, storybookUrlSource } from "./capture/storybook-urls.mjs";
8
+ import { DEFAULT_IGNORE_SELECTORS, IgnoreSelectorList, MAX_IGNORE_SELECTORS, cleanIgnoreSelectors, declaredIgnoreSelectors, ignoreStylesheet, isSafeIgnoreSelector } from "./capture/ignore-regions.mjs";
8
9
  import { MAX_MODES_PER_STORY, buildGlobalsParam, filterModes, modeSlug, parseModesAllowlist } from "./capture/modes.mjs";
9
10
  import { TestResult, TestSummary, parseTestResults, summarizeTests } from "./capture/tests.mjs";
10
11
  import { JobLease, ProgressLease } from "./capture/job-lease.mjs";
@@ -50,6 +51,7 @@ import { PublishFacts, publishVerdict } from "./capture/publish/accept.mjs";
50
51
  import { publishingOf } from "./capture/publish/published.mjs";
51
52
  import { ExtractionResult, extractZipStream } from "./capture/publish/extract.mjs";
52
53
  import { PUBLISH_SCRATCH_ID, PublishZipOptions, publishStoredZip } from "./capture/publish/run.mjs";
54
+ import { ZipInvalidError } from "./capture/publish/invalid-zip.mjs";
53
55
  import "./capture/publish/index.mjs";
54
56
  import { Catalog, CatalogComponent, CatalogEntry } from "./capture/catalog/types.mjs";
55
57
  import { CatalogDeps, catalogueBuild, readCatalog } from "./capture/catalog/catalogue.mjs";
@@ -57,11 +59,12 @@ import { CatalogLoader, CatalogLoaderOptions, DEFAULT_CATALOG_CACHE_SIZE, create
57
59
  import { createCatalogHook } from "./capture/catalog/hook.mjs";
58
60
  import { EntryChanges, diffEntries, groupComponents, hashEntries, toBuildComponent } from "./capture/catalog/components.mjs";
59
61
  import { parseStorybookIndex, parseStorybookIndexText } from "./capture/catalog/parse.mjs";
60
- import { readPublishedFile } from "./capture/catalog/read.mjs";
62
+ import { readPublishedFile, readPublishedFileInfo } from "./capture/catalog/read.mjs";
61
63
  import { ComponentBuildRow, ComponentChange, catalogPresence, componentBuildHistory } from "./capture/catalog/history.mjs";
62
64
  import "./capture/catalog/index.mjs";
63
65
  import { ChildProcessHandle, ParentPort } from "./capture/forked/channel.mjs";
64
66
  import { ForkedCaptureHandler, ForkedCaptureOptions, createForkedCaptureHandler } from "./capture/forked/forked-capture.mjs";
65
67
  import { CAPTURE_RETRIES_ENV, CAPTURE_SHARD_SIZE_ENV, CAPTURE_WORKER_ROLE, ForkOptions, canForkCaptureChild, forkCaptureChild } from "./capture/forked/fork.mjs";
66
68
  import { FORK_MIN_CPUS, FORK_MIN_MEMORY_BYTES, ForkDecision, ForkHost, decideCaptureProcess } from "./capture/forked/decision.mjs";
67
- export { A11Y_ROOT_SELECTOR, type A11yRunPlan, ACTIVE_POLL_MS, ACTIVE_WINDOW_MS, type AfterPublishContext, type AfterPublishHook, type BoundingBox, type BuildStateDeps, type BuildStatePublisher, type BuildStatePublisherOptions, CANCEL_CHECK_MS, CAPTURE_RETRIES_ENV, CAPTURE_SHARD_SIZE_ENV, CAPTURE_WORKER_ROLE, type CaptureAttemptRef, type CaptureChildOptions, type CaptureConsumer, type CaptureConsumerOptions, type CaptureContext, type CaptureDispatchJob, type CaptureHandlerOptions, type CaptureJobInput, type CaptureJobOptions, type Catalog, type CatalogComponent, type CatalogDeps, type CatalogEntry, type CatalogLoader, type CatalogLoaderOptions, type ChildHost, type ChildMessage, type ChildProcessHandle, type ComponentBuildRow, type ComponentChange, type ConsumerConfig, type ConsumerJob, type ConsumerMetrics, type CrashFailureOutcome, DATABASE_QUEUE_METADATA, DEFAULT_BACKOFF_MS, DEFAULT_CADENCE, DEFAULT_CATALOG_CACHE_SIZE, DEFAULT_LEASE_MS, DEFAULT_MAX_INLINE_UNZIP_SIZE, DEFAULT_MAX_RETRIES, DEFAULT_MAX_UNZIPPED_BYTES, DEFAULT_MAX_ZIP_ENTRIES, DEFAULT_RETRIES, DEFAULT_SAMPLE_MS, DEFAULT_VIEWPORTS, DEFAULT_WAIT_TIME_SECONDS, type DatabaseCaptureQueue, type DatabaseCaptureQueueOptions, type DatabaseJob, type DispatchDeps, type EntryChanges, type EnvironmentChange, type ExtractionLimits, type ExtractionResult, FILE_BY_FILE_HINT, FORK_MIN_CPUS, FORK_MIN_MEMORY_BYTES, type Fingerprint, type ForkDecision, type ForkHost, type ForkOptions, type ForkedCaptureHandler, type ForkedCaptureOptions, IDLE_POLL_MS, type JobFinishedEvent, type JobHandlers, type JobLease, type JobLeaseKeeper, type JobOutcome, type JobRunContext, type KindHandler, LEASE_EXHAUSTED_ERROR, type LeaseEnd, type LeaseKeeperOptions, type LeaseRenewal, MAX_BACKOFF_MS, MAX_BROWSERS, MAX_MODES_PER_STORY, MAX_NACK_DELAY_MS, MAX_REPORTED_VIOLATIONS, MIN_BLOCKING_POLL_MS, type ModeDefinition, type ModeMap, type ModeViewportValue, NOTIFY_BACKOFF, NOTIFY_MAX_ATTEMPTS, NO_ORIGIN_REASON, NO_SECRET_REASON, type NotifyFailure, type NotifyHandlerOptions, type NotifyJobQueue, PAUSED_MESSAGE, POLL_JITTER, PUBLISH_SCRATCH_ID, type ParentMessage, type ParentPort, type PollCadence, type PollLoop, type PollLoopOptions, type PollableCaptureQueue, type PollableJob, ProgressLease, type PublishCompleter, type PublishCompleterOptions, type PublishFacts, type PublishHandlerOptions, type PublishLimits, type PublishOutcome, type PublishZipOptions, type QueueJobInput, type QueueSample, REDIFFABLE_BUILD_STATUSES, RENEW_THROTTLE_MS, type RediffDeps, type RediffResult, type RetryConfig, SCRATCH_DIRNAME, SCRATCH_MAX_AGE_MS, SIZING_DEFAULTS, STORYBOOK_BUILTIN_VIEWPORTS, type ScratchSweepOptions, type ScreenshotPlan, type SizingOptions, type SkipReason, type SlimViolation, type StateTarget, type StoryEntry, type StoryParameters, type StorySourceAdapter, type StoryViewportConfig, type StoryViewportDefinition, StorybookAdapter, type StorybookUrlSource, type TargetPrerequisites, type TestResult, type TestSummary, type Viewport, ZipLimitError, assertBrowserCount, assertSupportedBrowsers, axeInjectionScript, backoffDelayMs, buildFingerprint, buildGlobalsParam, buildScratchDir, canForkCaptureChild, captureDispatchDeps, catalogPresence, catalogueBuild, changedFields, componentBuildHistory, createBuildStatePublisher, createCaptureConsumer, createCaptureHandler, createCatalogHook, createCatalogLoader, createDatabaseCaptureQueue, createDispatchJob, createForkedCaptureHandler, createNotifyEnqueuer, createNotifyHandler, createPublishCompleter, createPublishHandler, decideCaptureProcess, deliverBuildState, diffEntries, executeCaptureJob, extractStorybookToScratch, extractZipStream, extractionLimitsOf, failCrashedCapture, filterModes, fingerprintHash, forkCaptureChild, getScreenshotPlan, groupComponents, hashEntries, idleDelayMs, inPageReadA11yParameters, inPageRunAxe, isDisabledStory, isFastEmptyPoll, isFinalAttempt, isFlakyStory, keepJobLease, listTargets, loadAxeSource, loadBuild, mapAxeViolations, mergeParameters, modeSlug, nextPollDelay, notifyExhausted, offeredTargets, parseBrowsers, parseFingerprint, parseModesAllowlist, parseStorybookIndex, parseStorybookIndexText, parseTestResults, persistCapture, persistStorybookStatics, primaryTargetOf, publishRoute, publishStoredZip, publishVerdict, publishingOf, readCatalog, readPublishedFile, rediffBuild, refusalFor, removeBuildScratch, removeJobScratch, requestOpenBuildStates, resolveA11yRun, resolveBrowsers, resolveViewports as resolveCaptureViewports, resolvePublishLimits, resolveStoryViewports, runCaptureChild, sampleQueue, startPollLoop, stateTargets, storybookUrlSource, summarizeTests, sweepOrphanedScratch, toBuildComponent, writeTo };
69
+ import { ComparisonReference, isComparisonImagePath, parseComparisonReference } from "./capture/comparison-reference.mjs";
70
+ export { A11Y_ROOT_SELECTOR, type A11yRunPlan, ACTIVE_POLL_MS, ACTIVE_WINDOW_MS, type AfterPublishContext, type AfterPublishHook, type BoundingBox, type BuildStateDeps, type BuildStatePublisher, type BuildStatePublisherOptions, CANCEL_CHECK_MS, CAPTURE_RETRIES_ENV, CAPTURE_SHARD_SIZE_ENV, CAPTURE_WORKER_ROLE, type CaptureAttemptRef, type CaptureChildOptions, type CaptureConsumer, type CaptureConsumerOptions, type CaptureContext, type CaptureDispatchJob, type CaptureHandlerOptions, type CaptureJobInput, type CaptureJobOptions, type Catalog, type CatalogComponent, type CatalogDeps, type CatalogEntry, type CatalogLoader, type CatalogLoaderOptions, type ChildHost, type ChildMessage, type ChildProcessHandle, type ComparisonReference, type ComponentBuildRow, type ComponentChange, type ConsumerConfig, type ConsumerJob, type ConsumerMetrics, type CrashFailureOutcome, DATABASE_QUEUE_METADATA, DEFAULT_BACKOFF_MS, DEFAULT_CADENCE, DEFAULT_CATALOG_CACHE_SIZE, DEFAULT_IGNORE_SELECTORS, DEFAULT_LEASE_MS, DEFAULT_MAX_INLINE_UNZIP_SIZE, DEFAULT_MAX_RETRIES, DEFAULT_MAX_UNZIPPED_BYTES, DEFAULT_MAX_ZIP_ENTRIES, DEFAULT_RETRIES, DEFAULT_SAMPLE_MS, DEFAULT_VIEWPORTS, DEFAULT_WAIT_TIME_SECONDS, type DatabaseCaptureQueue, type DatabaseCaptureQueueOptions, type DatabaseJob, type DispatchDeps, type EntryChanges, type EnvironmentChange, type ExtractionLimits, type ExtractionResult, FILE_BY_FILE_HINT, FORK_MIN_CPUS, FORK_MIN_MEMORY_BYTES, type Fingerprint, type ForkDecision, type ForkHost, type ForkOptions, type ForkedCaptureHandler, type ForkedCaptureOptions, IDLE_POLL_MS, type IgnoreSelectorList, type JobFinishedEvent, type JobHandlers, type JobLease, type JobLeaseKeeper, type JobOutcome, type JobRunContext, type KindHandler, LEASE_EXHAUSTED_ERROR, type LeaseEnd, type LeaseKeeperOptions, type LeaseRenewal, MAX_BACKOFF_MS, MAX_BROWSERS, MAX_IGNORE_SELECTORS, MAX_MODES_PER_STORY, MAX_NACK_DELAY_MS, MAX_REPORTED_VIOLATIONS, MIN_BLOCKING_POLL_MS, type ModeDefinition, type ModeMap, type ModeViewportValue, NOTIFY_BACKOFF, NOTIFY_MAX_ATTEMPTS, NO_ORIGIN_REASON, NO_SECRET_REASON, type NotifyFailure, type NotifyHandlerOptions, type NotifyJobQueue, PAUSED_MESSAGE, POLL_JITTER, PUBLISH_SCRATCH_ID, type ParentMessage, type ParentPort, type PollCadence, type PollLoop, type PollLoopOptions, type PollableCaptureQueue, type PollableJob, ProgressLease, type PublishCompleter, type PublishCompleterOptions, type PublishFacts, type PublishHandlerOptions, type PublishLimits, type PublishOutcome, type PublishZipOptions, type QueueJobInput, type QueueSample, REDIFFABLE_BUILD_STATUSES, RENEW_THROTTLE_MS, type RediffDeps, type RediffResult, type RetryConfig, SCRATCH_DIRNAME, SCRATCH_MAX_AGE_MS, SIZING_DEFAULTS, STORYBOOK_BUILTIN_VIEWPORTS, type ScratchSweepOptions, type ScreenshotPlan, type SizingOptions, type SkipReason, type SlimViolation, type StateTarget, type StoryEntry, type StoryParameters, type StorySourceAdapter, type StoryViewportConfig, type StoryViewportDefinition, StorybookAdapter, type StorybookUrlSource, type TargetPrerequisites, type TestResult, type TestSummary, type Viewport, ZipInvalidError, ZipLimitError, assertBrowserCount, assertSupportedBrowsers, axeInjectionScript, backoffDelayMs, buildFingerprint, buildGlobalsParam, buildScratchDir, canForkCaptureChild, captureDispatchDeps, catalogPresence, catalogueBuild, changedFields, cleanIgnoreSelectors, componentBuildHistory, createBuildStatePublisher, createCaptureConsumer, createCaptureHandler, createCatalogHook, createCatalogLoader, createDatabaseCaptureQueue, createDispatchJob, createForkedCaptureHandler, createNotifyEnqueuer, createNotifyHandler, createPublishCompleter, createPublishHandler, decideCaptureProcess, declaredIgnoreSelectors, deliverBuildState, diffEntries, executeCaptureJob, extractStorybookToScratch, extractZipStream, extractionLimitsOf, failCrashedCapture, filterModes, fingerprintHash, forkCaptureChild, getScreenshotPlan, groupComponents, hashEntries, idleDelayMs, ignoreStylesheet, inPageReadA11yParameters, inPageRunAxe, isComparisonImagePath, isDisabledStory, isFastEmptyPoll, isFinalAttempt, isFlakyStory, isSafeIgnoreSelector, keepJobLease, listTargets, loadAxeSource, loadBuild, mapAxeViolations, mergeParameters, modeSlug, nextPollDelay, notifyExhausted, offeredTargets, parseBrowsers, parseComparisonReference, parseFingerprint, parseModesAllowlist, parseStorybookIndex, parseStorybookIndexText, parseTestResults, persistCapture, persistStorybookStatics, primaryTargetOf, publishRoute, publishStoredZip, publishVerdict, publishingOf, readCatalog, readPublishedFile, readPublishedFileInfo, rediffBuild, refusalFor, removeBuildScratch, removeJobScratch, requestOpenBuildStates, resolveA11yRun, resolveBrowsers, resolveViewports as resolveCaptureViewports, resolvePublishLimits, resolveStoryViewports, runCaptureChild, sampleQueue, startPollLoop, stateTargets, storybookUrlSource, summarizeTests, sweepOrphanedScratch, toBuildComponent, writeTo };
package/dist/capture.mjs CHANGED
@@ -4,14 +4,17 @@ import.meta.url;
4
4
  import { MAX_BROWSERS, assertBrowserCount, assertSupportedBrowsers, listTargets, parseBrowsers, primaryTargetOf, refusalFor, resolveBrowsers } from "./capture/browsers.mjs";
5
5
  import { NO_ORIGIN_REASON, NO_SECRET_REASON, offeredTargets } from "./capture/targets.mjs";
6
6
  import { storybookUrlSource } from "./capture/storybook-urls.mjs";
7
+ import { DEFAULT_IGNORE_SELECTORS, MAX_IGNORE_SELECTORS, cleanIgnoreSelectors, declaredIgnoreSelectors, ignoreStylesheet, isSafeIgnoreSelector } from "./capture/ignore-regions.mjs";
7
8
  import { MAX_MODES_PER_STORY, buildGlobalsParam, filterModes, modeSlug, parseModesAllowlist } from "./capture/modes.mjs";
8
9
  import { DEFAULT_VIEWPORTS, isDisabledStory, isFlakyStory } from "./capture/adapter.mjs";
9
10
  import { STORYBOOK_BUILTIN_VIEWPORTS, StorybookAdapter, mergeParameters, resolveStoryViewports } from "./capture/storybook.mjs";
10
11
  import { parseTestResults, summarizeTests } from "./capture/tests.mjs";
12
+ import { isComparisonImagePath, parseComparisonReference } from "./capture/comparison-reference.mjs";
11
13
  import { SIZING_DEFAULTS, getScreenshotPlan, resolveViewports } from "./capture/sizing.mjs";
12
14
  import { buildFingerprint, changedFields, fingerprintHash, parseFingerprint } from "./capture/fingerprint.mjs";
13
15
  import { persistCapture } from "./capture/pipeline.mjs";
14
16
  import { SCRATCH_DIRNAME, SCRATCH_MAX_AGE_MS, buildScratchDir, removeBuildScratch, removeJobScratch, sweepOrphanedScratch } from "./capture/scratch.mjs";
17
+ import { ZipInvalidError } from "./capture/publish/invalid-zip.mjs";
15
18
  import { DEFAULT_MAX_INLINE_UNZIP_SIZE, DEFAULT_MAX_UNZIPPED_BYTES, DEFAULT_MAX_ZIP_ENTRIES, FILE_BY_FILE_HINT, ZipLimitError, extractionLimitsOf, publishRoute, resolvePublishLimits } from "./capture/publish/limits.mjs";
16
19
  import { extractZipStream } from "./capture/publish/extract.mjs";
17
20
  import { extractStorybookToScratch, persistStorybookStatics } from "./capture/statics.mjs";
@@ -50,10 +53,10 @@ import { createPublishCompleter } from "./capture/publish/complete.mjs";
50
53
  import { createCatalogHook } from "./capture/catalog/hook.mjs";
51
54
  import { diffEntries, groupComponents, hashEntries, toBuildComponent } from "./capture/catalog/components.mjs";
52
55
  import { parseStorybookIndex, parseStorybookIndexText } from "./capture/catalog/parse.mjs";
53
- import { readPublishedFile } from "./capture/catalog/read.mjs";
56
+ import { readPublishedFile, readPublishedFileInfo } from "./capture/catalog/read.mjs";
54
57
  import { catalogueBuild, readCatalog } from "./capture/catalog/catalogue.mjs";
55
58
  import { DEFAULT_CATALOG_CACHE_SIZE, createCatalogLoader } from "./capture/catalog/load.mjs";
56
59
  import { catalogPresence, componentBuildHistory } from "./capture/catalog/history.mjs";
57
60
  import { createForkedCaptureHandler } from "./capture/forked/forked-capture.mjs";
58
61
  import { FORK_MIN_CPUS, FORK_MIN_MEMORY_BYTES, decideCaptureProcess } from "./capture/forked/decision.mjs";
59
- export { A11Y_ROOT_SELECTOR, ACTIVE_POLL_MS, ACTIVE_WINDOW_MS, CANCEL_CHECK_MS, CAPTURE_RETRIES_ENV, CAPTURE_SHARD_SIZE_ENV, CAPTURE_WORKER_ROLE, DATABASE_QUEUE_METADATA, DEFAULT_BACKOFF_MS, DEFAULT_CADENCE, DEFAULT_CATALOG_CACHE_SIZE, DEFAULT_LEASE_MS, DEFAULT_MAX_INLINE_UNZIP_SIZE, DEFAULT_MAX_RETRIES, DEFAULT_MAX_UNZIPPED_BYTES, DEFAULT_MAX_ZIP_ENTRIES, DEFAULT_RETRIES, DEFAULT_SAMPLE_MS, DEFAULT_VIEWPORTS, DEFAULT_WAIT_TIME_SECONDS, FILE_BY_FILE_HINT, FORK_MIN_CPUS, FORK_MIN_MEMORY_BYTES, IDLE_POLL_MS, LEASE_EXHAUSTED_ERROR, MAX_BACKOFF_MS, MAX_BROWSERS, MAX_MODES_PER_STORY, MAX_NACK_DELAY_MS, MAX_REPORTED_VIOLATIONS, MIN_BLOCKING_POLL_MS, NOTIFY_BACKOFF, NOTIFY_MAX_ATTEMPTS, NO_ORIGIN_REASON, NO_SECRET_REASON, PAUSED_MESSAGE, POLL_JITTER, PUBLISH_SCRATCH_ID, ProgressLease, REDIFFABLE_BUILD_STATUSES, RENEW_THROTTLE_MS, SCRATCH_DIRNAME, SCRATCH_MAX_AGE_MS, SIZING_DEFAULTS, STORYBOOK_BUILTIN_VIEWPORTS, StorybookAdapter, ZipLimitError, assertBrowserCount, assertSupportedBrowsers, axeInjectionScript, backoffDelayMs, buildFingerprint, buildGlobalsParam, buildScratchDir, canForkCaptureChild, captureDispatchDeps, catalogPresence, catalogueBuild, changedFields, componentBuildHistory, createBuildStatePublisher, createCaptureConsumer, createCaptureHandler, createCatalogHook, createCatalogLoader, createDatabaseCaptureQueue, createDispatchJob, createForkedCaptureHandler, createNotifyEnqueuer, createNotifyHandler, createPublishCompleter, createPublishHandler, decideCaptureProcess, deliverBuildState, diffEntries, executeCaptureJob, extractStorybookToScratch, extractZipStream, extractionLimitsOf, failCrashedCapture, filterModes, fingerprintHash, forkCaptureChild, getScreenshotPlan, groupComponents, hashEntries, idleDelayMs, inPageReadA11yParameters, inPageRunAxe, isDisabledStory, isFastEmptyPoll, isFinalAttempt, isFlakyStory, keepJobLease, listTargets, loadAxeSource, loadBuild, mapAxeViolations, mergeParameters, modeSlug, nextPollDelay, notifyExhausted, offeredTargets, parseBrowsers, parseFingerprint, parseModesAllowlist, parseStorybookIndex, parseStorybookIndexText, parseTestResults, persistCapture, persistStorybookStatics, primaryTargetOf, publishRoute, publishStoredZip, publishVerdict, publishingOf, readCatalog, readPublishedFile, rediffBuild, refusalFor, removeBuildScratch, removeJobScratch, requestOpenBuildStates, resolveA11yRun, resolveBrowsers, resolveViewports as resolveCaptureViewports, resolvePublishLimits, resolveStoryViewports, runCaptureChild, sampleQueue, startPollLoop, stateTargets, storybookUrlSource, summarizeTests, sweepOrphanedScratch, toBuildComponent, writeTo };
62
+ export { A11Y_ROOT_SELECTOR, ACTIVE_POLL_MS, ACTIVE_WINDOW_MS, CANCEL_CHECK_MS, CAPTURE_RETRIES_ENV, CAPTURE_SHARD_SIZE_ENV, CAPTURE_WORKER_ROLE, DATABASE_QUEUE_METADATA, DEFAULT_BACKOFF_MS, DEFAULT_CADENCE, DEFAULT_CATALOG_CACHE_SIZE, DEFAULT_IGNORE_SELECTORS, DEFAULT_LEASE_MS, DEFAULT_MAX_INLINE_UNZIP_SIZE, DEFAULT_MAX_RETRIES, DEFAULT_MAX_UNZIPPED_BYTES, DEFAULT_MAX_ZIP_ENTRIES, DEFAULT_RETRIES, DEFAULT_SAMPLE_MS, DEFAULT_VIEWPORTS, DEFAULT_WAIT_TIME_SECONDS, FILE_BY_FILE_HINT, FORK_MIN_CPUS, FORK_MIN_MEMORY_BYTES, IDLE_POLL_MS, LEASE_EXHAUSTED_ERROR, MAX_BACKOFF_MS, MAX_BROWSERS, MAX_IGNORE_SELECTORS, MAX_MODES_PER_STORY, MAX_NACK_DELAY_MS, MAX_REPORTED_VIOLATIONS, MIN_BLOCKING_POLL_MS, NOTIFY_BACKOFF, NOTIFY_MAX_ATTEMPTS, NO_ORIGIN_REASON, NO_SECRET_REASON, PAUSED_MESSAGE, POLL_JITTER, PUBLISH_SCRATCH_ID, ProgressLease, REDIFFABLE_BUILD_STATUSES, RENEW_THROTTLE_MS, SCRATCH_DIRNAME, SCRATCH_MAX_AGE_MS, SIZING_DEFAULTS, STORYBOOK_BUILTIN_VIEWPORTS, StorybookAdapter, ZipInvalidError, ZipLimitError, assertBrowserCount, assertSupportedBrowsers, axeInjectionScript, backoffDelayMs, buildFingerprint, buildGlobalsParam, buildScratchDir, canForkCaptureChild, captureDispatchDeps, catalogPresence, catalogueBuild, changedFields, cleanIgnoreSelectors, componentBuildHistory, createBuildStatePublisher, createCaptureConsumer, createCaptureHandler, createCatalogHook, createCatalogLoader, createDatabaseCaptureQueue, createDispatchJob, createForkedCaptureHandler, createNotifyEnqueuer, createNotifyHandler, createPublishCompleter, createPublishHandler, decideCaptureProcess, declaredIgnoreSelectors, deliverBuildState, diffEntries, executeCaptureJob, extractStorybookToScratch, extractZipStream, extractionLimitsOf, failCrashedCapture, filterModes, fingerprintHash, forkCaptureChild, getScreenshotPlan, groupComponents, hashEntries, idleDelayMs, ignoreStylesheet, inPageReadA11yParameters, inPageRunAxe, isComparisonImagePath, isDisabledStory, isFastEmptyPoll, isFinalAttempt, isFlakyStory, isSafeIgnoreSelector, keepJobLease, listTargets, loadAxeSource, loadBuild, mapAxeViolations, mergeParameters, modeSlug, nextPollDelay, notifyExhausted, offeredTargets, parseBrowsers, parseComparisonReference, parseFingerprint, parseModesAllowlist, parseStorybookIndex, parseStorybookIndexText, parseTestResults, persistCapture, persistStorybookStatics, primaryTargetOf, publishRoute, publishStoredZip, publishVerdict, publishingOf, readCatalog, readPublishedFile, readPublishedFileInfo, rediffBuild, refusalFor, removeBuildScratch, removeJobScratch, requestOpenBuildStates, resolveA11yRun, resolveBrowsers, resolveViewports as resolveCaptureViewports, resolvePublishLimits, resolveStoryViewports, runCaptureChild, sampleQueue, startPollLoop, stateTargets, storybookUrlSource, summarizeTests, sweepOrphanedScratch, toBuildComponent, writeTo };
package/dist/config.d.mts CHANGED
@@ -13,7 +13,6 @@ import { ALLOW_SERVERLESS_ENV, assertServerless, isServerlessHost } from "./serv
13
13
  import { AdapterCategory } from "./adapter/metadata.mjs";
14
14
  import { z } from "zod";
15
15
  import { Logger } from "pino";
16
- //#region src/config.d.ts
17
16
  /** Brand color theme for the server-rendered UI. */
18
17
  interface BrandTheme {
19
18
  accent: string;
@@ -101,6 +100,17 @@ interface NotificationsConfig {
101
100
  footerText?: string;
102
101
  slackUsername?: string;
103
102
  }
103
+ /** Longest browser-private freshness a signed capture asset may be given, in seconds (1 hour). */
104
+ declare const MAX_CAPTURE_ASSET_CACHE_SECONDS = 3600;
105
+ /** Signed Storybook asset serving for remote capture browsers (ADR 0007, decision 5). */
106
+ interface CaptureAssetsConfig {
107
+ /**
108
+ * Opt-in browser-private freshness of capture-scoped signed assets, in whole seconds from 0 to
109
+ * {@link MAX_CAPTURE_ASSET_CACHE_SECONDS}. 0 (default) keeps every signed answer `no-store`. A
110
+ * positive value is a cap: the answer's `max-age` never exceeds the grant's remaining lifetime.
111
+ */
112
+ cacheMaxAgeSeconds?: number;
113
+ }
104
114
  /** Shelf-level configuration (validated by {@link shelfConfigSchema}). */
105
115
  interface ShelfConfig {
106
116
  secret?: string;
@@ -145,6 +155,10 @@ interface ShelfConfig {
145
155
  * Opt-in, off by default — some deployments prefer no timing headers.
146
156
  */
147
157
  serverTiming?: boolean;
158
+ /** Persist gzip variants for published Storybook files (default off). */
159
+ publishedCompression?: boolean;
160
+ /** Signed Storybook asset serving for remote capture browsers; see {@link CaptureAssetsConfig}. */
161
+ captureAssets?: CaptureAssetsConfig;
148
162
  /**
149
163
  * Largest zip (bytes) extracted inline in the upload request; a larger zip is published by a
150
164
  * `publish` job (or refused with 413 on a host that runs no consumer). The count is taken
@@ -304,6 +318,5 @@ interface ObservabilityHandle {
304
318
  /** Flush exporters and release SDK resources. Must be idempotent. */
305
319
  shutdown(): Promise<void>;
306
320
  }
307
- //#endregion
308
- export { ALLOW_SERVERLESS_ENV, AdapterSnapshot, AuthUiConfig, BrandTheme, DEFAULT_MAX_UPLOAD_BYTES, DEFAULT_SCRATCH_DIR, NotificationsConfig, ObservabilityHandle, ShelfCompareOptions, ShelfConfig, ShelfImagesOptions, ShelfNotificationsOptions, ShelfOptions, ShelfTestingOptions, ShelfViewport, UIConfig, assertServerless, isServerlessHost, notificationsConfigSchema, resolveShelfOptions, shelfConfigSchema, uiConfigSchema, validateConfig, validateUiConfig };
321
+ export { ALLOW_SERVERLESS_ENV, AdapterSnapshot, AuthUiConfig, BrandTheme, CaptureAssetsConfig, DEFAULT_MAX_UPLOAD_BYTES, DEFAULT_SCRATCH_DIR, MAX_CAPTURE_ASSET_CACHE_SECONDS, NotificationsConfig, ObservabilityHandle, ShelfCompareOptions, ShelfConfig, ShelfImagesOptions, ShelfNotificationsOptions, ShelfOptions, ShelfTestingOptions, ShelfViewport, UIConfig, assertServerless, isServerlessHost, notificationsConfigSchema, resolveShelfOptions, shelfConfigSchema, uiConfigSchema, validateConfig, validateUiConfig };
309
322
  //# sourceMappingURL=config.d.mts.map
package/dist/config.mjs CHANGED
@@ -4,9 +4,11 @@ import.meta.url;
4
4
  import { DEFAULT_SCRATCH_DIR, resolveShelfOptions } from "./options.mjs";
5
5
  import { ALLOW_SERVERLESS_ENV, assertServerless, isServerlessHost } from "./serverless.mjs";
6
6
  import { z } from "zod";
7
- //#region src/config.ts
7
+ // src/config.ts
8
8
  /** Default cap for a single Storybook zip upload (1 GiB). */
9
9
  const DEFAULT_MAX_UPLOAD_BYTES = 1073741824;
10
+ /** Longest browser-private freshness a signed capture asset may be given, in seconds (1 hour). */
11
+ const MAX_CAPTURE_ASSET_CACHE_SECONDS = 3600;
10
12
  const viewportSchema = z.object({
11
13
  name: z.string().min(1),
12
14
  width: z.number().int().positive(),
@@ -85,6 +87,8 @@ const shelfConfigSchema = z.object({
85
87
  branchGcIntervalMs: z.number().int().positive().optional(),
86
88
  maxUploadBytes: z.number().int().positive().optional(),
87
89
  serverTiming: z.boolean().optional(),
90
+ publishedCompression: z.boolean().optional(),
91
+ captureAssets: z.object({ cacheMaxAgeSeconds: z.number().int().min(0).max(MAX_CAPTURE_ASSET_CACHE_SECONDS).optional() }).strict().optional(),
88
92
  maxInlineUnzipSize: z.number().int().min(0).optional(),
89
93
  maxZipEntries: z.number().int().positive().optional(),
90
94
  maxUnzippedBytes: z.number().int().positive().optional(),
@@ -139,7 +143,6 @@ function validateUiConfig(config) {
139
143
  }
140
144
  return result.data;
141
145
  }
142
- //#endregion
143
- export { ALLOW_SERVERLESS_ENV, DEFAULT_MAX_UPLOAD_BYTES, DEFAULT_SCRATCH_DIR, assertServerless, isServerlessHost, notificationsConfigSchema, resolveShelfOptions, shelfConfigSchema, uiConfigSchema, validateConfig, validateUiConfig };
146
+ export { ALLOW_SERVERLESS_ENV, DEFAULT_MAX_UPLOAD_BYTES, DEFAULT_SCRATCH_DIR, MAX_CAPTURE_ASSET_CACHE_SECONDS, assertServerless, isServerlessHost, notificationsConfigSchema, resolveShelfOptions, shelfConfigSchema, uiConfigSchema, validateConfig, validateUiConfig };
144
147
 
145
148
  //# sourceMappingURL=config.mjs.map