@storyshelf/core 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (628) hide show
  1. package/README.md +49 -32
  2. package/dist/adapter/capture-queue.d.mts +44 -2
  3. package/dist/adapter/capture-runner.d.mts +127 -10
  4. package/dist/adapter/capture-runner.mjs +5 -12
  5. package/dist/adapter/database.d.mts +27 -8
  6. package/dist/adapter/database.mjs.map +1 -1
  7. package/dist/adapter/design.d.mts +85 -0
  8. package/dist/adapter/design.mjs +28 -0
  9. package/dist/adapter/design.mjs.map +1 -0
  10. package/dist/adapter/metadata.d.mts +3 -10
  11. package/dist/adapter/notifier.d.mts +6 -3
  12. package/dist/adapter/notifier.mjs +5 -1
  13. package/dist/adapter/oauth.d.mts +52 -0
  14. package/dist/adapter/setup.d.mts +4 -5
  15. package/dist/adapter/setup.mjs +2 -1
  16. package/dist/adapter/setup.mjs.map +1 -1
  17. package/dist/adapter/webhook-events.mjs +1 -1
  18. package/dist/adapters/capture-target-offers.d.mts +28 -0
  19. package/dist/adapters/capture-target-offers.mjs +58 -0
  20. package/dist/adapters/capture-target-offers.mjs.map +1 -0
  21. package/dist/adapters/capture-target.d.mts +71 -0
  22. package/dist/adapters/capture-target.mjs +76 -0
  23. package/dist/adapters/capture-target.mjs.map +1 -0
  24. package/dist/adapters/notifier/build-comment.d.mts +24 -0
  25. package/dist/adapters/notifier/build-comment.mjs +24 -0
  26. package/dist/adapters/notifier/build-comment.mjs.map +1 -0
  27. package/dist/adapters/notifier/build-state.d.mts +99 -0
  28. package/dist/adapters/notifier/build-state.mjs +218 -0
  29. package/dist/adapters/notifier/build-state.mjs.map +1 -0
  30. package/dist/adapters/notifier/events.d.mts +4 -2
  31. package/dist/adapters/notifier/events.mjs +31 -18
  32. package/dist/adapters/notifier/events.mjs.map +1 -1
  33. package/dist/adapters/notifier/provider.d.mts +53 -7
  34. package/dist/adapters/notifier/provider.mjs +12 -0
  35. package/dist/adapters/notifier/provider.mjs.map +1 -0
  36. package/dist/adapters/notifier/state.d.mts +24 -0
  37. package/dist/adapters/notifier/state.mjs +66 -0
  38. package/dist/adapters/notifier/state.mjs.map +1 -0
  39. package/dist/adapters/notifier/types.d.mts +51 -2
  40. package/dist/adapters/remote-browser.d.mts +87 -0
  41. package/dist/adapters/remote-browser.mjs +103 -0
  42. package/dist/adapters/remote-browser.mjs.map +1 -0
  43. package/dist/adapters/render-runs.d.mts +25 -0
  44. package/dist/adapters/render-runs.mjs +41 -0
  45. package/dist/adapters/render-runs.mjs.map +1 -0
  46. package/dist/adapters/setup-runner.mjs +2 -4
  47. package/dist/adapters/setup-runner.mjs.map +1 -1
  48. package/dist/adapters/sources.d.mts +12 -0
  49. package/dist/adapters/sources.mjs +20 -0
  50. package/dist/adapters/sources.mjs.map +1 -0
  51. package/dist/adapters/validate.mjs +25 -18
  52. package/dist/adapters/validate.mjs.map +1 -1
  53. package/dist/ai.d.mts +105 -0
  54. package/dist/ai.mjs +19 -0
  55. package/dist/ai.mjs.map +1 -0
  56. package/dist/capture/a11y/axe-source.d.mts +18 -0
  57. package/dist/capture/a11y/axe-source.mjs +37 -0
  58. package/dist/capture/a11y/axe-source.mjs.map +1 -0
  59. package/dist/capture/a11y/in-page.d.mts +27 -0
  60. package/dist/capture/a11y/in-page.mjs +60 -0
  61. package/dist/capture/a11y/in-page.mjs.map +1 -0
  62. package/dist/capture/a11y/map.d.mts +24 -0
  63. package/dist/capture/a11y/map.mjs +48 -0
  64. package/dist/capture/a11y/map.mjs.map +1 -0
  65. package/dist/capture/a11y/options.d.mts +23 -0
  66. package/dist/capture/a11y/options.mjs +75 -0
  67. package/dist/capture/a11y/options.mjs.map +1 -0
  68. package/dist/capture/adapter.d.mts +41 -3
  69. package/dist/capture/adapter.mjs.map +1 -1
  70. package/dist/capture/affected.d.mts +2 -0
  71. package/dist/capture/affected.mjs +13 -4
  72. package/dist/capture/affected.mjs.map +1 -1
  73. package/dist/capture/attempt-logger.mjs +84 -0
  74. package/dist/capture/attempt-logger.mjs.map +1 -0
  75. package/dist/capture/browsers.d.mts +35 -0
  76. package/dist/capture/browsers.mjs +67 -0
  77. package/dist/capture/browsers.mjs.map +1 -0
  78. package/dist/capture/build-state/deliver.d.mts +32 -0
  79. package/dist/capture/build-state/deliver.mjs +118 -0
  80. package/dist/capture/build-state/deliver.mjs.map +1 -0
  81. package/dist/capture/build-state/index.d.mts +8 -0
  82. package/dist/capture/build-state/notify-job.d.mts +56 -0
  83. package/dist/capture/build-state/notify-job.mjs +133 -0
  84. package/dist/capture/build-state/notify-job.mjs.map +1 -0
  85. package/dist/capture/build-state/publisher.d.mts +23 -0
  86. package/dist/capture/build-state/publisher.mjs +107 -0
  87. package/dist/capture/build-state/publisher.mjs.map +1 -0
  88. package/dist/capture/build-state/resume.d.mts +9 -0
  89. package/dist/capture/build-state/resume.mjs +47 -0
  90. package/dist/capture/build-state/resume.mjs.map +1 -0
  91. package/dist/capture/build-state/types.d.mts +49 -0
  92. package/dist/capture/catalog/catalogue.d.mts +22 -0
  93. package/dist/capture/catalog/catalogue.mjs +53 -0
  94. package/dist/capture/catalog/catalogue.mjs.map +1 -0
  95. package/dist/capture/catalog/components.d.mts +23 -0
  96. package/dist/capture/catalog/components.mjs +55 -0
  97. package/dist/capture/catalog/components.mjs.map +1 -0
  98. package/dist/capture/catalog/history.d.mts +30 -0
  99. package/dist/capture/catalog/history.mjs +51 -0
  100. package/dist/capture/catalog/history.mjs.map +1 -0
  101. package/dist/capture/catalog/hook.d.mts +14 -0
  102. package/dist/capture/catalog/hook.mjs +19 -0
  103. package/dist/capture/catalog/hook.mjs.map +1 -0
  104. package/dist/capture/catalog/index.d.mts +11 -0
  105. package/dist/capture/catalog/load.d.mts +26 -0
  106. package/dist/capture/catalog/load.mjs +49 -0
  107. package/dist/capture/catalog/load.mjs.map +1 -0
  108. package/dist/capture/catalog/parse.d.mts +15 -0
  109. package/dist/capture/catalog/parse.mjs +56 -0
  110. package/dist/capture/catalog/parse.mjs.map +1 -0
  111. package/dist/capture/catalog/read.d.mts +12 -0
  112. package/dist/capture/catalog/read.mjs +33 -0
  113. package/dist/capture/catalog/read.mjs.map +1 -0
  114. package/dist/capture/catalog/types.d.mts +33 -0
  115. package/dist/capture/consumer/capture-handler.d.mts +58 -0
  116. package/dist/capture/consumer/capture-handler.mjs +118 -0
  117. package/dist/capture/consumer/capture-handler.mjs.map +1 -0
  118. package/dist/capture/consumer/idle.d.mts +17 -0
  119. package/dist/capture/consumer/idle.mjs +25 -0
  120. package/dist/capture/consumer/idle.mjs.map +1 -0
  121. package/dist/capture/consumer/index.d.mts +13 -0
  122. package/dist/capture/consumer/index.mjs +139 -0
  123. package/dist/capture/consumer/index.mjs.map +1 -0
  124. package/dist/capture/consumer/loop.mjs +110 -0
  125. package/dist/capture/consumer/loop.mjs.map +1 -0
  126. package/dist/capture/consumer/metrics.d.mts +41 -0
  127. package/dist/capture/consumer/metrics.mjs +38 -0
  128. package/dist/capture/consumer/metrics.mjs.map +1 -0
  129. package/dist/capture/consumer/retry.d.mts +33 -0
  130. package/dist/capture/consumer/retry.mjs +34 -0
  131. package/dist/capture/consumer/retry.mjs.map +1 -0
  132. package/dist/capture/consumer/run-job.mjs +195 -0
  133. package/dist/capture/consumer/run-job.mjs.map +1 -0
  134. package/dist/capture/consumer/startup.mjs +38 -0
  135. package/dist/capture/consumer/startup.mjs.map +1 -0
  136. package/dist/capture/consumer/types.d.mts +97 -0
  137. package/dist/capture/crash-failure.d.mts +25 -0
  138. package/dist/capture/crash-failure.mjs +70 -0
  139. package/dist/capture/crash-failure.mjs.map +1 -0
  140. package/dist/capture/db-queue/cadence.d.mts +32 -0
  141. package/dist/capture/db-queue/cadence.mjs +38 -0
  142. package/dist/capture/db-queue/cadence.mjs.map +1 -0
  143. package/dist/capture/db-queue/claim.d.mts +9 -0
  144. package/dist/capture/db-queue/claim.mjs +132 -0
  145. package/dist/capture/db-queue/claim.mjs.map +1 -0
  146. package/dist/capture/db-queue/clock.mjs +12 -0
  147. package/dist/capture/db-queue/clock.mjs.map +1 -0
  148. package/dist/capture/db-queue/codec.mjs +76 -0
  149. package/dist/capture/db-queue/codec.mjs.map +1 -0
  150. package/dist/capture/db-queue/index.d.mts +20 -0
  151. package/dist/capture/db-queue/index.mjs +89 -0
  152. package/dist/capture/db-queue/index.mjs.map +1 -0
  153. package/dist/capture/db-queue/keeper.d.mts +36 -0
  154. package/dist/capture/db-queue/keeper.mjs +92 -0
  155. package/dist/capture/db-queue/keeper.mjs.map +1 -0
  156. package/dist/capture/db-queue/lease.mjs +46 -0
  157. package/dist/capture/db-queue/lease.mjs.map +1 -0
  158. package/dist/capture/db-queue/lifecycle.mjs +36 -0
  159. package/dist/capture/db-queue/lifecycle.mjs.map +1 -0
  160. package/dist/capture/db-queue/operations.mjs +94 -0
  161. package/dist/capture/db-queue/operations.mjs.map +1 -0
  162. package/dist/capture/db-queue/poll-loop.d.mts +30 -0
  163. package/dist/capture/db-queue/poll-loop.mjs +89 -0
  164. package/dist/capture/db-queue/poll-loop.mjs.map +1 -0
  165. package/dist/capture/db-queue/poll.d.mts +10 -0
  166. package/dist/capture/db-queue/poll.mjs +69 -0
  167. package/dist/capture/db-queue/poll.mjs.map +1 -0
  168. package/dist/capture/db-queue/timers.d.mts +12 -0
  169. package/dist/capture/db-queue/timers.mjs +37 -0
  170. package/dist/capture/db-queue/timers.mjs.map +1 -0
  171. package/dist/capture/db-queue/types.d.mts +83 -0
  172. package/dist/capture/db-queue/types.mjs +12 -0
  173. package/dist/capture/db-queue/types.mjs.map +1 -0
  174. package/dist/capture/db-queue/wake.mjs +48 -0
  175. package/dist/capture/db-queue/wake.mjs.map +1 -0
  176. package/dist/capture/design.mjs +39 -0
  177. package/dist/capture/design.mjs.map +1 -0
  178. package/dist/capture/diff-record.mjs +23 -3
  179. package/dist/capture/diff-record.mjs.map +1 -1
  180. package/dist/capture/dispatch.d.mts +12 -7
  181. package/dist/capture/dispatch.mjs +86 -107
  182. package/dist/capture/dispatch.mjs.map +1 -1
  183. package/dist/capture/enrich.mjs +60 -0
  184. package/dist/capture/enrich.mjs.map +1 -0
  185. package/dist/capture/failure-sets.mjs +22 -0
  186. package/dist/capture/failure-sets.mjs.map +1 -0
  187. package/dist/capture/fingerprint.d.mts +42 -0
  188. package/dist/capture/fingerprint.mjs +83 -0
  189. package/dist/capture/fingerprint.mjs.map +1 -0
  190. package/dist/capture/forked/channel.d.mts +25 -0
  191. package/dist/capture/forked/child.d.mts +34 -0
  192. package/dist/capture/forked/child.mjs +202 -0
  193. package/dist/capture/forked/child.mjs.map +1 -0
  194. package/dist/capture/forked/crash-tracker.mjs +23 -0
  195. package/dist/capture/forked/crash-tracker.mjs.map +1 -0
  196. package/dist/capture/forked/decision.d.mts +26 -0
  197. package/dist/capture/forked/decision.mjs +27 -0
  198. package/dist/capture/forked/decision.mjs.map +1 -0
  199. package/dist/capture/forked/exit.mjs +12 -0
  200. package/dist/capture/forked/exit.mjs.map +1 -0
  201. package/dist/capture/forked/fork.d.mts +31 -0
  202. package/dist/capture/forked/fork.mjs +93 -0
  203. package/dist/capture/forked/fork.mjs.map +1 -0
  204. package/dist/capture/forked/forked-capture.d.mts +40 -0
  205. package/dist/capture/forked/forked-capture.mjs +240 -0
  206. package/dist/capture/forked/forked-capture.mjs.map +1 -0
  207. package/dist/capture/forked/lease-book.mjs +46 -0
  208. package/dist/capture/forked/lease-book.mjs.map +1 -0
  209. package/dist/capture/forked/protocol.d.mts +38 -0
  210. package/dist/capture/forked/protocol.mjs +25 -0
  211. package/dist/capture/forked/protocol.mjs.map +1 -0
  212. package/dist/capture/forked/runs.mjs +75 -0
  213. package/dist/capture/forked/runs.mjs.map +1 -0
  214. package/dist/capture/isolate.mjs +141 -0
  215. package/dist/capture/isolate.mjs.map +1 -0
  216. package/dist/capture/job-lease.d.mts +28 -0
  217. package/dist/capture/job-lease.mjs +57 -0
  218. package/dist/capture/job-lease.mjs.map +1 -0
  219. package/dist/capture/matrix.mjs +298 -0
  220. package/dist/capture/matrix.mjs.map +1 -0
  221. package/dist/capture/modes.d.mts +25 -0
  222. package/dist/capture/modes.mjs +107 -0
  223. package/dist/capture/modes.mjs.map +1 -0
  224. package/dist/capture/orchestrator.d.mts +37 -5
  225. package/dist/capture/orchestrator.mjs +118 -107
  226. package/dist/capture/orchestrator.mjs.map +1 -1
  227. package/dist/capture/pipeline.d.mts +9 -2
  228. package/dist/capture/pipeline.mjs +250 -45
  229. package/dist/capture/pipeline.mjs.map +1 -1
  230. package/dist/capture/progress.d.mts +11 -0
  231. package/dist/capture/progress.mjs +28 -0
  232. package/dist/capture/progress.mjs.map +1 -0
  233. package/dist/capture/publish/accept.d.mts +39 -0
  234. package/dist/capture/publish/accept.mjs +28 -0
  235. package/dist/capture/publish/accept.mjs.map +1 -0
  236. package/dist/capture/publish/complete.d.mts +46 -0
  237. package/dist/capture/publish/complete.mjs +64 -0
  238. package/dist/capture/publish/complete.mjs.map +1 -0
  239. package/dist/capture/publish/extract.d.mts +20 -0
  240. package/dist/capture/publish/extract.mjs +119 -0
  241. package/dist/capture/publish/extract.mjs.map +1 -0
  242. package/dist/capture/publish/handler.d.mts +28 -0
  243. package/dist/capture/publish/handler.mjs +62 -0
  244. package/dist/capture/publish/handler.mjs.map +1 -0
  245. package/dist/capture/publish/index.d.mts +10 -0
  246. package/dist/capture/publish/limits.d.mts +37 -0
  247. package/dist/capture/publish/limits.mjs +39 -0
  248. package/dist/capture/publish/limits.mjs.map +1 -0
  249. package/dist/capture/publish/published.d.mts +15 -0
  250. package/dist/capture/publish/published.mjs +18 -0
  251. package/dist/capture/publish/published.mjs.map +1 -0
  252. package/dist/capture/publish/run.d.mts +33 -0
  253. package/dist/capture/publish/run.mjs +30 -0
  254. package/dist/capture/publish/run.mjs.map +1 -0
  255. package/dist/capture/rediff.d.mts +1 -1
  256. package/dist/capture/rediff.mjs +4 -3
  257. package/dist/capture/rediff.mjs.map +1 -1
  258. package/dist/capture/render.mjs +53 -0
  259. package/dist/capture/render.mjs.map +1 -0
  260. package/dist/capture/scratch.d.mts +44 -0
  261. package/dist/capture/scratch.mjs +138 -0
  262. package/dist/capture/scratch.mjs.map +1 -0
  263. package/dist/capture/shard-plan.mjs +128 -0
  264. package/dist/capture/shard-plan.mjs.map +1 -0
  265. package/dist/capture/shards/context.mjs +51 -0
  266. package/dist/capture/shards/context.mjs.map +1 -0
  267. package/dist/capture/shards/execute.mjs +174 -0
  268. package/dist/capture/shards/execute.mjs.map +1 -0
  269. package/dist/capture/shards/fan-out.mjs +126 -0
  270. package/dist/capture/shards/fan-out.mjs.map +1 -0
  271. package/dist/capture/shards/history.mjs +35 -0
  272. package/dist/capture/shards/history.mjs.map +1 -0
  273. package/dist/capture/shards/reduce.mjs +97 -0
  274. package/dist/capture/shards/reduce.mjs.map +1 -0
  275. package/dist/capture/sizing.mjs +1 -11
  276. package/dist/capture/sizing.mjs.map +1 -1
  277. package/dist/capture/skip-checks.mjs +2 -65
  278. package/dist/capture/skip-checks.mjs.map +1 -1
  279. package/dist/capture/statics.d.mts +7 -8
  280. package/dist/capture/statics.mjs +25 -71
  281. package/dist/capture/statics.mjs.map +1 -1
  282. package/dist/capture/storybook-urls.d.mts +40 -0
  283. package/dist/capture/storybook-urls.mjs +41 -0
  284. package/dist/capture/storybook-urls.mjs.map +1 -0
  285. package/dist/capture/storybook.d.mts +12 -4
  286. package/dist/capture/storybook.mjs +56 -16
  287. package/dist/capture/storybook.mjs.map +1 -1
  288. package/dist/capture/target.mjs +21 -0
  289. package/dist/capture/target.mjs.map +1 -0
  290. package/dist/capture/targets.d.mts +25 -0
  291. package/dist/capture/targets.mjs +32 -0
  292. package/dist/capture/targets.mjs.map +1 -0
  293. package/dist/capture/tests.d.mts +37 -0
  294. package/dist/capture/tests.mjs +126 -0
  295. package/dist/capture/tests.mjs.map +1 -0
  296. package/dist/capture/token-chunks.mjs +74 -0
  297. package/dist/capture/token-chunks.mjs.map +1 -0
  298. package/dist/capture.d.mts +60 -7
  299. package/dist/capture.mjs +48 -3
  300. package/dist/config.d.mts +121 -12
  301. package/dist/config.mjs +9 -6
  302. package/dist/config.mjs.map +1 -1
  303. package/dist/diff.d.mts +9 -3
  304. package/dist/diff.mjs +16 -3
  305. package/dist/diff.mjs.map +1 -0
  306. package/dist/index.d.mts +1 -1
  307. package/dist/insights/budget.d.mts +20 -0
  308. package/dist/insights/budget.mjs +33 -0
  309. package/dist/insights/budget.mjs.map +1 -0
  310. package/dist/insights/envelope.d.mts +59 -0
  311. package/dist/insights/envelope.mjs +60 -0
  312. package/dist/insights/envelope.mjs.map +1 -0
  313. package/dist/insights/evidence-health.d.mts +32 -0
  314. package/dist/insights/evidence-health.mjs +28 -0
  315. package/dist/insights/evidence-health.mjs.map +1 -0
  316. package/dist/insights/evidence.d.mts +55 -0
  317. package/dist/insights/evidence.mjs +64 -0
  318. package/dist/insights/evidence.mjs.map +1 -0
  319. package/dist/insights/hash.d.mts +18 -0
  320. package/dist/insights/hash.mjs +21 -0
  321. package/dist/insights/hash.mjs.map +1 -0
  322. package/dist/insights/limits.d.mts +24 -0
  323. package/dist/insights/limits.mjs +24 -0
  324. package/dist/insights/limits.mjs.map +1 -0
  325. package/dist/insights/prompts/v1.d.mts +13 -0
  326. package/dist/insights/prompts/v1.mjs +34 -0
  327. package/dist/insights/prompts/v1.mjs.map +1 -0
  328. package/dist/insights/redact.d.mts +9 -0
  329. package/dist/insights/redact.mjs +24 -0
  330. package/dist/insights/redact.mjs.map +1 -0
  331. package/dist/insights/text.mjs +44 -0
  332. package/dist/insights/text.mjs.map +1 -0
  333. package/dist/insights.d.mts +11 -0
  334. package/dist/insights.mjs +12 -0
  335. package/dist/logger.mjs +17 -1
  336. package/dist/logger.mjs.map +1 -1
  337. package/dist/models/ai-budget-alert.d.mts +23 -0
  338. package/dist/models/ai-budget-alert.mjs +38 -0
  339. package/dist/models/ai-budget-alert.mjs.map +1 -0
  340. package/dist/models/ai-usage.d.mts +40 -0
  341. package/dist/models/ai-usage.mjs +61 -0
  342. package/dist/models/ai-usage.mjs.map +1 -0
  343. package/dist/models/baseline-archive.d.mts +84 -0
  344. package/dist/models/baseline-archive.mjs +106 -0
  345. package/dist/models/baseline-archive.mjs.map +1 -0
  346. package/dist/models/baseline-export.d.mts +19 -0
  347. package/dist/models/baseline-export.mjs +136 -0
  348. package/dist/models/baseline-export.mjs.map +1 -0
  349. package/dist/models/baseline-import.mjs +118 -0
  350. package/dist/models/baseline-import.mjs.map +1 -0
  351. package/dist/models/baseline-transfer.d.mts +49 -0
  352. package/dist/models/baseline-transfer.mjs +121 -0
  353. package/dist/models/baseline-transfer.mjs.map +1 -0
  354. package/dist/models/baseline.d.mts +32 -5
  355. package/dist/models/baseline.mjs +65 -20
  356. package/dist/models/baseline.mjs.map +1 -1
  357. package/dist/models/build-component.d.mts +28 -0
  358. package/dist/models/build-component.mjs +55 -0
  359. package/dist/models/build-component.mjs.map +1 -0
  360. package/dist/models/build-review-status.d.mts +29 -0
  361. package/dist/models/build-review-status.mjs +37 -0
  362. package/dist/models/build-review-status.mjs.map +1 -0
  363. package/dist/models/build-review.d.mts +28 -0
  364. package/dist/models/build-review.mjs +54 -0
  365. package/dist/models/build-review.mjs.map +1 -0
  366. package/dist/models/build.d.mts +25 -3
  367. package/dist/models/build.mjs +50 -11
  368. package/dist/models/build.mjs.map +1 -1
  369. package/dist/models/capture-attempt.d.mts +7 -1
  370. package/dist/models/capture-attempt.mjs +29 -4
  371. package/dist/models/capture-attempt.mjs.map +1 -1
  372. package/dist/models/capture-job.d.mts +106 -0
  373. package/dist/models/capture-job.mjs +288 -0
  374. package/dist/models/capture-job.mjs.map +1 -0
  375. package/dist/models/capture-log.d.mts +1 -1
  376. package/dist/models/capture-log.mjs +4 -3
  377. package/dist/models/capture-log.mjs.map +1 -1
  378. package/dist/models/capture-shard.d.mts +62 -0
  379. package/dist/models/capture-shard.mjs +190 -0
  380. package/dist/models/capture-shard.mjs.map +1 -0
  381. package/dist/models/changeset.d.mts +48 -0
  382. package/dist/models/changeset.mjs +81 -0
  383. package/dist/models/changeset.mjs.map +1 -0
  384. package/dist/models/columns.mjs +19 -0
  385. package/dist/models/columns.mjs.map +1 -0
  386. package/dist/models/comment.d.mts +4 -1
  387. package/dist/models/comment.mjs +7 -4
  388. package/dist/models/comment.mjs.map +1 -1
  389. package/dist/models/component.d.mts +43 -0
  390. package/dist/models/component.mjs +60 -0
  391. package/dist/models/component.mjs.map +1 -0
  392. package/dist/models/connections.d.mts +41 -0
  393. package/dist/models/connections.mjs +112 -0
  394. package/dist/models/connections.mjs.map +1 -0
  395. package/dist/models/content-ref.d.mts +1 -1
  396. package/dist/models/content-ref.mjs +1 -1
  397. package/dist/models/content-ref.mjs.map +1 -1
  398. package/dist/models/credentials.mjs +12 -2
  399. package/dist/models/credentials.mjs.map +1 -1
  400. package/dist/models/design-credentials.d.mts +41 -0
  401. package/dist/models/design-credentials.mjs +86 -0
  402. package/dist/models/design-credentials.mjs.map +1 -0
  403. package/dist/models/design-health.d.mts +16 -0
  404. package/dist/models/design-health.mjs +25 -0
  405. package/dist/models/design-health.mjs.map +1 -0
  406. package/dist/models/design-nodes.d.mts +83 -0
  407. package/dist/models/design-nodes.mjs +222 -0
  408. package/dist/models/design-nodes.mjs.map +1 -0
  409. package/dist/models/design-renders.mjs +74 -0
  410. package/dist/models/design-renders.mjs.map +1 -0
  411. package/dist/models/design-resolve.d.mts +29 -0
  412. package/dist/models/design-resolve.mjs +41 -0
  413. package/dist/models/design-resolve.mjs.map +1 -0
  414. package/dist/models/design.d.mts +39 -0
  415. package/dist/models/design.mjs +90 -0
  416. package/dist/models/design.mjs.map +1 -0
  417. package/dist/models/inherited-ratio.d.mts +21 -0
  418. package/dist/models/inherited-ratio.mjs +22 -0
  419. package/dist/models/inherited-ratio.mjs.map +1 -0
  420. package/dist/models/insight.d.mts +70 -0
  421. package/dist/models/insight.mjs +162 -0
  422. package/dist/models/insight.mjs.map +1 -0
  423. package/dist/models/label.d.mts +4 -2
  424. package/dist/models/label.mjs +25 -10
  425. package/dist/models/label.mjs.map +1 -1
  426. package/dist/models/member.d.mts +1 -1
  427. package/dist/models/member.mjs +5 -4
  428. package/dist/models/member.mjs.map +1 -1
  429. package/dist/models/notification-channel.d.mts +37 -3
  430. package/dist/models/notification-channel.mjs +88 -9
  431. package/dist/models/notification-channel.mjs.map +1 -1
  432. package/dist/models/notification-subscription.d.mts +1 -2
  433. package/dist/models/notification-subscription.mjs +7 -9
  434. package/dist/models/notification-subscription.mjs.map +1 -1
  435. package/dist/models/project-group-mapping.d.mts +6 -1
  436. package/dist/models/project-group-mapping.mjs +14 -4
  437. package/dist/models/project-group-mapping.mjs.map +1 -1
  438. package/dist/models/project.d.mts +9 -1
  439. package/dist/models/project.mjs +9 -4
  440. package/dist/models/project.mjs.map +1 -1
  441. package/dist/models/public-origin.d.mts +63 -0
  442. package/dist/models/public-origin.mjs +114 -0
  443. package/dist/models/public-origin.mjs.map +1 -0
  444. package/dist/models/review-switch-off.d.mts +14 -0
  445. package/dist/models/review-switch-off.mjs +26 -0
  446. package/dist/models/review-switch-off.mjs.map +1 -0
  447. package/dist/models/site-settings.d.mts +23 -0
  448. package/dist/models/site-settings.mjs +48 -0
  449. package/dist/models/site-settings.mjs.map +1 -0
  450. package/dist/models/snapshot.d.mts +36 -1
  451. package/dist/models/snapshot.mjs +50 -3
  452. package/dist/models/snapshot.mjs.map +1 -1
  453. package/dist/models/token.d.mts +13 -1
  454. package/dist/models/token.mjs +23 -5
  455. package/dist/models/token.mjs.map +1 -1
  456. package/dist/models/usage.d.mts +76 -0
  457. package/dist/models/usage.mjs +149 -0
  458. package/dist/models/usage.mjs.map +1 -0
  459. package/dist/models/user.d.mts +1 -1
  460. package/dist/models/user.mjs +4 -3
  461. package/dist/models/user.mjs.map +1 -1
  462. package/dist/models/webhook.d.mts +15 -1
  463. package/dist/models/webhook.mjs +33 -4
  464. package/dist/models/webhook.mjs.map +1 -1
  465. package/dist/models.d.mts +26 -3
  466. package/dist/models.mjs +27 -5
  467. package/dist/options.d.mts +18 -0
  468. package/dist/options.mjs +90 -0
  469. package/dist/options.mjs.map +1 -0
  470. package/dist/paths.d.mts +19 -4
  471. package/dist/paths.mjs +51 -10
  472. package/dist/paths.mjs.map +1 -1
  473. package/dist/retention/ai.d.mts +18 -0
  474. package/dist/retention/ai.mjs +23 -0
  475. package/dist/retention/ai.mjs.map +1 -0
  476. package/dist/retention/pending.mjs +47 -0
  477. package/dist/retention/pending.mjs.map +1 -0
  478. package/dist/retention/screenshot-sweep.d.mts +51 -0
  479. package/dist/retention/screenshot-sweep.mjs +128 -0
  480. package/dist/retention/screenshot-sweep.mjs.map +1 -0
  481. package/dist/retention.d.mts +16 -2
  482. package/dist/retention.mjs +71 -14
  483. package/dist/retention.mjs.map +1 -1
  484. package/dist/schema/baseline.d.mts +6 -0
  485. package/dist/schema/build-component.d.mts +25 -0
  486. package/dist/schema/build-review.d.mts +17 -0
  487. package/dist/schema/build.d.mts +7 -0
  488. package/dist/schema/capture-job.d.mts +60 -0
  489. package/dist/schema/capture-shard.d.mts +35 -0
  490. package/dist/schema/comment.d.mts +2 -0
  491. package/dist/schema/connection.d.mts +24 -0
  492. package/dist/schema/design.d.mts +58 -0
  493. package/dist/schema/insight.d.mts +54 -0
  494. package/dist/schema/notification.d.mts +4 -0
  495. package/dist/schema/project.d.mts +21 -0
  496. package/dist/schema/site-setting.d.mts +21 -0
  497. package/dist/schema/snapshot.d.mts +12 -0
  498. package/dist/schema/usage.d.mts +30 -0
  499. package/dist/schema.d.mts +11 -3
  500. package/dist/serverless.d.mts +21 -0
  501. package/dist/serverless.mjs +37 -0
  502. package/dist/serverless.mjs.map +1 -0
  503. package/dist/test-helpers/create-project.d.mts +1 -1
  504. package/dist/test-helpers/deferred.d.mts +39 -0
  505. package/dist/test-helpers/deferred.mjs +61 -0
  506. package/dist/test-helpers/deferred.mjs.map +1 -0
  507. package/dist/test-helpers/design-contract.d.mts +21 -0
  508. package/dist/test-helpers/design-contract.mjs +40 -0
  509. package/dist/test-helpers/design-contract.mjs.map +1 -0
  510. package/dist/test-helpers/fake-database.mjs +14 -2
  511. package/dist/test-helpers/fake-database.mjs.map +1 -1
  512. package/dist/test-helpers/fake-design.d.mts +43 -0
  513. package/dist/test-helpers/fake-design.mjs +114 -0
  514. package/dist/test-helpers/fake-design.mjs.map +1 -0
  515. package/dist/test-helpers/fake-notifier.d.mts +21 -0
  516. package/dist/test-helpers/fake-notifier.mjs +44 -0
  517. package/dist/test-helpers/fake-notifier.mjs.map +1 -0
  518. package/dist/test-helpers/fake-tables.mjs +255 -67
  519. package/dist/test-helpers/fake-tables.mjs.map +1 -1
  520. package/dist/test-helpers/manual-clock.d.mts +27 -0
  521. package/dist/test-helpers/manual-clock.mjs +56 -0
  522. package/dist/test-helpers/manual-clock.mjs.map +1 -0
  523. package/dist/test-helpers/notifier-contract.d.mts +7 -1
  524. package/dist/test-helpers/notifier-contract.mjs +32 -3
  525. package/dist/test-helpers/notifier-contract.mjs.map +1 -1
  526. package/dist/test-helpers/queue-contract.d.mts +16 -2
  527. package/dist/test-helpers/queue-contract.mjs +195 -1
  528. package/dist/test-helpers/queue-contract.mjs.map +1 -1
  529. package/dist/test-helpers/queue-multi-instance.d.mts +20 -0
  530. package/dist/test-helpers/queue-multi-instance.mjs +206 -0
  531. package/dist/test-helpers/queue-multi-instance.mjs.map +1 -0
  532. package/dist/test-helpers/runner-contract.mjs +11 -0
  533. package/dist/test-helpers/runner-contract.mjs.map +1 -1
  534. package/dist/test-helpers/sql-chunks.mjs +23 -0
  535. package/dist/test-helpers/sql-chunks.mjs.map +1 -1
  536. package/dist/test-helpers/storage-contract.mjs +1 -1
  537. package/dist/test-helpers.d.mts +9 -4
  538. package/dist/test-helpers.mjs +8 -3
  539. package/dist/types.d.mts +14 -3
  540. package/dist/types.mjs +21 -4
  541. package/dist/types.mjs.map +1 -1
  542. package/dist/urls.d.mts +4 -0
  543. package/dist/urls.mjs +11 -2
  544. package/dist/urls.mjs.map +1 -1
  545. package/dist/utils/adaptive-concurrency.d.mts +66 -0
  546. package/dist/utils/adaptive-concurrency.mjs +149 -0
  547. package/dist/utils/adaptive-concurrency.mjs.map +1 -0
  548. package/dist/utils/connect-retry.d.mts +26 -0
  549. package/dist/utils/connect-retry.mjs +43 -0
  550. package/dist/utils/connect-retry.mjs.map +1 -0
  551. package/dist/utils/environment.d.mts +19 -0
  552. package/dist/utils/environment.mjs +59 -0
  553. package/dist/utils/environment.mjs.map +1 -0
  554. package/dist/utils/http.d.mts +17 -1
  555. package/dist/utils/http.mjs +22 -3
  556. package/dist/utils/http.mjs.map +1 -1
  557. package/dist/utils/map-adaptive.d.mts +15 -0
  558. package/dist/utils/map-adaptive.mjs +72 -0
  559. package/dist/utils/map-adaptive.mjs.map +1 -0
  560. package/dist/utils/map-limit.d.mts +10 -0
  561. package/dist/utils/map-limit.mjs +32 -0
  562. package/dist/utils/map-limit.mjs.map +1 -0
  563. package/dist/utils/page-limiter.d.mts +37 -0
  564. package/dist/utils/page-limiter.mjs +56 -0
  565. package/dist/utils/page-limiter.mjs.map +1 -0
  566. package/dist/utils/public-origin.d.mts +20 -0
  567. package/dist/utils/public-origin.mjs +75 -0
  568. package/dist/utils/public-origin.mjs.map +1 -0
  569. package/dist/utils/redact.mjs +3 -0
  570. package/dist/utils/redact.mjs.map +1 -1
  571. package/dist/utils/remote-environment.d.mts +39 -0
  572. package/dist/utils/remote-environment.mjs +114 -0
  573. package/dist/utils/remote-environment.mjs.map +1 -0
  574. package/dist/utils/resources.d.mts +64 -0
  575. package/dist/utils/resources.mjs +122 -0
  576. package/dist/utils/resources.mjs.map +1 -0
  577. package/dist/utils/screenshot-store.d.mts +15 -0
  578. package/dist/utils/screenshot-store.mjs +41 -0
  579. package/dist/utils/screenshot-store.mjs.map +1 -0
  580. package/dist/utils/session-gate.d.mts +22 -0
  581. package/dist/utils/session-gate.mjs +53 -0
  582. package/dist/utils/session-gate.mjs.map +1 -0
  583. package/dist/utils/storybook-token.d.mts +81 -0
  584. package/dist/utils/storybook-token.mjs +134 -0
  585. package/dist/utils/storybook-token.mjs.map +1 -0
  586. package/dist/utils/storybook-url.d.mts +15 -0
  587. package/dist/utils/storybook-url.mjs +21 -0
  588. package/dist/utils/storybook-url.mjs.map +1 -0
  589. package/dist/utils/timing.mjs +1 -1
  590. package/dist/utils/timing.mjs.map +1 -1
  591. package/dist/utils/zip-read.mjs +129 -0
  592. package/dist/utils/zip-read.mjs.map +1 -0
  593. package/dist/utils/zip-write.d.mts +18 -0
  594. package/dist/utils/zip-write.mjs +188 -0
  595. package/dist/utils/zip-write.mjs.map +1 -0
  596. package/dist/utils.d.mts +17 -3
  597. package/dist/utils.mjs +18 -4
  598. package/package.json +32 -23
  599. package/dist/adapter/capture-runner.mjs.map +0 -1
  600. package/dist/adapter/git-host/comments.d.mts +0 -41
  601. package/dist/adapter/git-host/comments.mjs +0 -54
  602. package/dist/adapter/git-host/comments.mjs.map +0 -1
  603. package/dist/adapter/git-host/helpers.d.mts +0 -13
  604. package/dist/adapter/git-host/helpers.mjs +0 -30
  605. package/dist/adapter/git-host/helpers.mjs.map +0 -1
  606. package/dist/adapter/git-host.d.mts +0 -42
  607. package/dist/capture/queue.d.mts +0 -38
  608. package/dist/capture/queue.mjs +0 -98
  609. package/dist/capture/queue.mjs.map +0 -1
  610. package/dist/capture/recovery.d.mts +0 -21
  611. package/dist/capture/recovery.mjs +0 -83
  612. package/dist/capture/recovery.mjs.map +0 -1
  613. package/dist/capture/status-fanout.mjs +0 -72
  614. package/dist/capture/status-fanout.mjs.map +0 -1
  615. package/dist/diff/engine.d.mts +0 -16
  616. package/dist/diff/engine.mjs +0 -87
  617. package/dist/diff/engine.mjs.map +0 -1
  618. package/dist/diff/options.d.mts +0 -42
  619. package/dist/diff/options.mjs +0 -15
  620. package/dist/diff/options.mjs.map +0 -1
  621. package/dist/models/status-config.d.mts +0 -38
  622. package/dist/models/status-config.mjs +0 -63
  623. package/dist/models/status-config.mjs.map +0 -1
  624. package/dist/schema/status-config.d.mts +0 -16
  625. package/dist/test-helpers/git-contract.d.mts +0 -11
  626. package/dist/test-helpers/git-contract.mjs +0 -34
  627. package/dist/test-helpers/git-contract.mjs.map +0 -1
  628. /package/dist/adapter/{git-host.mjs → oauth.mjs} +0 -0
@@ -0,0 +1,39 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ //#region src/capture/publish/accept.d.ts
4
+ /**
5
+ * The one rule for when publishing accepts a build (ADR 0006, decision 2). Pure: callers read the
6
+ * facts, apply the verdict, and stay free of branching.
7
+ */
8
+ /** Facts the verdict depends on. */
9
+ interface PublishFacts {
10
+ /** Whether publishing succeeded. */
11
+ published: boolean;
12
+ /** Whether a capture will run for this build (`testing` is on); capture then owns the status. */
13
+ captureWillRun: boolean;
14
+ /** Whether the build is on the project's default branch. */
15
+ defaultBranch: boolean;
16
+ /** The project's review switch (`projects.review_enabled`, ADR 0013, decision 1). */
17
+ reviewEnabled: boolean;
18
+ /** The build's status right now. */
19
+ status: string;
20
+ }
21
+ /**
22
+ * What publishing does to the build's status.
23
+ *
24
+ * - A capture will run: `unchanged`, the capture sets the status.
25
+ * - Not `pending` any more (already accepted, failed, cancelled or reviewed): `unchanged`, so a repeated
26
+ * publish (job redelivery after a lost lease) changes nothing.
27
+ * - Publish failed: `failed`.
28
+ * - Published on the default branch: `approved`, whatever the review switch says.
29
+ * - Published on another branch with review off: `approved`, nobody will sign it off.
30
+ * - Published on another branch with review on: `unchanged`, the build waits as `pending` for a
31
+ * build-level verdict (ADR 0013, decision 3).
32
+ *
33
+ * Accepting here is not an approval: no event, webhook or notification (`build:created` announced
34
+ * the upload), so nothing is asked of a reviewer and nothing is reported as one.
35
+ */
36
+ declare function publishVerdict(facts: PublishFacts): "approved" | "failed" | "unchanged";
37
+ //#endregion
38
+ export { PublishFacts, publishVerdict };
39
+ //# sourceMappingURL=accept.d.mts.map
@@ -0,0 +1,28 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ //#region src/capture/publish/accept.ts
5
+ /**
6
+ * What publishing does to the build's status.
7
+ *
8
+ * - A capture will run: `unchanged`, the capture sets the status.
9
+ * - Not `pending` any more (already accepted, failed, cancelled or reviewed): `unchanged`, so a repeated
10
+ * publish (job redelivery after a lost lease) changes nothing.
11
+ * - Publish failed: `failed`.
12
+ * - Published on the default branch: `approved`, whatever the review switch says.
13
+ * - Published on another branch with review off: `approved`, nobody will sign it off.
14
+ * - Published on another branch with review on: `unchanged`, the build waits as `pending` for a
15
+ * build-level verdict (ADR 0013, decision 3).
16
+ *
17
+ * Accepting here is not an approval: no event, webhook or notification (`build:created` announced
18
+ * the upload), so nothing is asked of a reviewer and nothing is reported as one.
19
+ */
20
+ function publishVerdict(facts) {
21
+ if (facts.captureWillRun || facts.status !== "pending") return "unchanged";
22
+ if (!facts.published) return "failed";
23
+ return facts.defaultBranch || !facts.reviewEnabled ? "approved" : "unchanged";
24
+ }
25
+ //#endregion
26
+ export { publishVerdict };
27
+
28
+ //# sourceMappingURL=accept.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accept.mjs","names":[],"sources":["../../../src/capture/publish/accept.ts"],"sourcesContent":["/**\n * The one rule for when publishing accepts a build (ADR 0006, decision 2). Pure: callers read the\n * facts, apply the verdict, and stay free of branching.\n */\n\n/** Facts the verdict depends on. */\nexport interface PublishFacts {\n /** Whether publishing succeeded. */\n published: boolean;\n /** Whether a capture will run for this build (`testing` is on); capture then owns the status. */\n captureWillRun: boolean;\n /** Whether the build is on the project's default branch. */\n defaultBranch: boolean;\n /** The project's review switch (`projects.review_enabled`, ADR 0013, decision 1). */\n reviewEnabled: boolean;\n /** The build's status right now. */\n status: string;\n}\n\n/**\n * What publishing does to the build's status.\n *\n * - A capture will run: `unchanged`, the capture sets the status.\n * - Not `pending` any more (already accepted, failed, cancelled or reviewed): `unchanged`, so a repeated\n * publish (job redelivery after a lost lease) changes nothing.\n * - Publish failed: `failed`.\n * - Published on the default branch: `approved`, whatever the review switch says.\n * - Published on another branch with review off: `approved`, nobody will sign it off.\n * - Published on another branch with review on: `unchanged`, the build waits as `pending` for a\n * build-level verdict (ADR 0013, decision 3).\n *\n * Accepting here is not an approval: no event, webhook or notification (`build:created` announced\n * the upload), so nothing is asked of a reviewer and nothing is reported as one.\n */\nexport function publishVerdict(facts: PublishFacts): \"approved\" | \"failed\" | \"unchanged\" {\n if (facts.captureWillRun || facts.status !== \"pending\") {\n return \"unchanged\";\n }\n if (!facts.published) {\n return \"failed\";\n }\n return facts.defaultBranch || !facts.reviewEnabled ? \"approved\" : \"unchanged\";\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAkCA,SAAgB,eAAe,OAA0D;CACvF,IAAI,MAAM,kBAAkB,MAAM,WAAW,WAC3C,OAAO;CAET,IAAI,CAAC,MAAM,WACT,OAAO;CAET,OAAO,MAAM,iBAAiB,CAAC,MAAM,gBAAgB,aAAa;AACpE"}
@@ -0,0 +1,46 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { DatabaseAdapter } from "../../adapter/database.mjs";
4
+ import { Logger } from "../../logger.mjs";
5
+ import { Build } from "../../schema/build.mjs";
6
+ import { Project } from "../../schema/project.mjs";
7
+ //#region src/capture/publish/complete.d.ts
8
+ /** How publishing ended. */
9
+ type PublishOutcome = {
10
+ ok: true;
11
+ } | {
12
+ ok: false;
13
+ error: string;
14
+ };
15
+ /** What an `afterPublish` hook is given. */
16
+ interface AfterPublishContext {
17
+ /** The build as it is after the verdict was applied. */
18
+ build: Build;
19
+ project: Project;
20
+ outcome: PublishOutcome;
21
+ logger: Logger;
22
+ }
23
+ /** Runs once per finished publish (extension point of the catalog). Failures are logged, never thrown on. */
24
+ type AfterPublishHook = (context: AfterPublishContext) => Promise<void>;
25
+ /** Finishes a build's publishing. */
26
+ interface PublishCompleter {
27
+ /**
28
+ * Record the end of a build's publishing. Resolves to the build, or null when it is gone.
29
+ * Safe to repeat: a build that left `pending` keeps its status.
30
+ */
31
+ complete(buildId: string, outcome: PublishOutcome): Promise<Build | null>;
32
+ }
33
+ /** What {@link createPublishCompleter} needs. */
34
+ interface PublishCompleterOptions {
35
+ db: DatabaseAdapter;
36
+ /** Whether a capture runs for uploads (`testing`); capture then owns the build status. */
37
+ captureWillRun: boolean;
38
+ /** Hooks, read on every call so later registrations count. */
39
+ afterPublish: readonly AfterPublishHook[];
40
+ logger: Logger;
41
+ }
42
+ /** A completer over `options.db`. */
43
+ declare function createPublishCompleter(options: PublishCompleterOptions): PublishCompleter;
44
+ //#endregion
45
+ export { AfterPublishContext, AfterPublishHook, PublishCompleter, PublishCompleterOptions, PublishOutcome, createPublishCompleter };
46
+ //# sourceMappingURL=complete.d.mts.map
@@ -0,0 +1,64 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { BuildModel } from "../../models/build.mjs";
5
+ import { ProjectModel } from "../../models/project.mjs";
6
+ import { publishVerdict } from "./accept.mjs";
7
+ //#region src/capture/publish/complete.ts
8
+ /** A completer over `options.db`. */
9
+ function createPublishCompleter(options) {
10
+ return { complete: async (buildId, outcome) => {
11
+ const target = await load(options.db, buildId);
12
+ if (!target) {
13
+ options.logger.warn({ buildId }, "build is gone; nothing to complete");
14
+ return null;
15
+ }
16
+ const build = await applyVerdict(options, target.build, target.project, outcome);
17
+ await runHooks(options, {
18
+ build,
19
+ project: target.project,
20
+ outcome
21
+ });
22
+ return build;
23
+ } };
24
+ }
25
+ async function load(db, buildId) {
26
+ const build = await new BuildModel(db).get(buildId);
27
+ const project = build ? await new ProjectModel(db).get(build.projectId) : null;
28
+ return build && project ? {
29
+ build,
30
+ project
31
+ } : null;
32
+ }
33
+ /** Move the build per the rule. No approval event or webhook: `build:created` announced the upload. */
34
+ async function applyVerdict(options, build, project, outcome) {
35
+ const verdict = publishVerdict({
36
+ published: outcome.ok,
37
+ captureWillRun: options.captureWillRun,
38
+ defaultBranch: build.gitBranch === project.gitDefaultBranch,
39
+ reviewEnabled: project.reviewEnabled !== false,
40
+ status: build.status
41
+ });
42
+ if (!outcome.ok) options.logger.error({
43
+ buildId: build.id,
44
+ error: outcome.error,
45
+ verdict
46
+ }, "publish failed");
47
+ if (verdict === "unchanged") return build;
48
+ return await new BuildModel(options.db).setStatus(build.id, verdict);
49
+ }
50
+ async function runHooks(options, context) {
51
+ const logger = options.logger.child({ buildId: context.build.id });
52
+ for (const hook of options.afterPublish) try {
53
+ await hook({
54
+ ...context,
55
+ logger
56
+ });
57
+ } catch (error) {
58
+ logger.error({ err: error }, "afterPublish hook failed");
59
+ }
60
+ }
61
+ //#endregion
62
+ export { createPublishCompleter };
63
+
64
+ //# sourceMappingURL=complete.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"complete.mjs","names":[],"sources":["../../../src/capture/publish/complete.ts"],"sourcesContent":["/**\n * Publishing's end: apply {@link publishVerdict} to the build and run the `afterPublish` hooks.\n * Every publish route (inline zip, `publish` job, file-by-file manifest) ends here, so this is the\n * single owner of the acceptance rule.\n */\nimport type { DatabaseAdapter } from \"../../adapters/database.ts\";\nimport type { Logger } from \"../../logger.ts\";\nimport { BuildModel } from \"../../models/build.ts\";\nimport { ProjectModel } from \"../../models/project.ts\";\nimport type { Build } from \"../../schema/build.ts\";\nimport type { Project } from \"../../schema/project.ts\";\nimport { publishVerdict } from \"./accept.ts\";\n\n/** How publishing ended. */\nexport type PublishOutcome = { ok: true } | { ok: false; error: string };\n\n/** What an `afterPublish` hook is given. */\nexport interface AfterPublishContext {\n /** The build as it is after the verdict was applied. */\n build: Build;\n project: Project;\n outcome: PublishOutcome;\n logger: Logger;\n}\n\n/** Runs once per finished publish (extension point of the catalog). Failures are logged, never thrown on. */\nexport type AfterPublishHook = (context: AfterPublishContext) => Promise<void>;\n\n/** Finishes a build's publishing. */\nexport interface PublishCompleter {\n /**\n * Record the end of a build's publishing. Resolves to the build, or null when it is gone.\n * Safe to repeat: a build that left `pending` keeps its status.\n */\n complete(buildId: string, outcome: PublishOutcome): Promise<Build | null>;\n}\n\n/** What {@link createPublishCompleter} needs. */\nexport interface PublishCompleterOptions {\n db: DatabaseAdapter;\n /** Whether a capture runs for uploads (`testing`); capture then owns the build status. */\n captureWillRun: boolean;\n /** Hooks, read on every call so later registrations count. */\n afterPublish: readonly AfterPublishHook[];\n logger: Logger;\n}\n\n/** A completer over `options.db`. */\nexport function createPublishCompleter(options: PublishCompleterOptions): PublishCompleter {\n return {\n complete: async (buildId, outcome) => {\n const target = await load(options.db, buildId);\n if (!target) {\n options.logger.warn({ buildId }, \"build is gone; nothing to complete\");\n return null;\n }\n const build = await applyVerdict(options, target.build, target.project, outcome);\n await runHooks(options, { build, project: target.project, outcome });\n return build;\n },\n };\n}\n\nasync function load(\n db: DatabaseAdapter,\n buildId: string,\n): Promise<{ build: Build; project: Project } | null> {\n const build = await new BuildModel(db).get(buildId);\n const project = build ? await new ProjectModel(db).get(build.projectId) : null;\n return build && project ? { build, project } : null;\n}\n\n/** Move the build per the rule. No approval event or webhook: `build:created` announced the upload. */\nasync function applyVerdict(\n options: PublishCompleterOptions,\n build: Build,\n project: Project,\n outcome: PublishOutcome,\n): Promise<Build> {\n const verdict = publishVerdict({\n published: outcome.ok,\n captureWillRun: options.captureWillRun,\n defaultBranch: build.gitBranch === project.gitDefaultBranch,\n reviewEnabled: project.reviewEnabled !== false,\n status: build.status,\n });\n if (!outcome.ok) {\n options.logger.error({ buildId: build.id, error: outcome.error, verdict }, \"publish failed\");\n }\n if (verdict === \"unchanged\") {\n return build;\n }\n return await new BuildModel(options.db).setStatus(build.id, verdict);\n}\n\nasync function runHooks(\n options: PublishCompleterOptions,\n context: Omit<AfterPublishContext, \"logger\">,\n): Promise<void> {\n const logger = options.logger.child({ buildId: context.build.id });\n for (const hook of options.afterPublish) {\n try {\n // eslint-disable-next-line no-await-in-loop -- hooks run in registration order\n await hook({ ...context, logger });\n } catch (error) {\n logger.error({ err: error }, \"afterPublish hook failed\");\n }\n }\n}\n"],"mappings":";;;;;;;;AAgDA,SAAgB,uBAAuB,SAAoD;CACzF,OAAO,EACL,UAAU,OAAO,SAAS,YAAY;EACpC,MAAM,SAAS,MAAM,KAAK,QAAQ,IAAI,OAAO;EAC7C,IAAI,CAAC,QAAQ;GACX,QAAQ,OAAO,KAAK,EAAE,QAAQ,GAAG,oCAAoC;GACrE,OAAO;EACT;EACA,MAAM,QAAQ,MAAM,aAAa,SAAS,OAAO,OAAO,OAAO,SAAS,OAAO;EAC/E,MAAM,SAAS,SAAS;GAAE;GAAO,SAAS,OAAO;GAAS;EAAQ,CAAC;EACnE,OAAO;CACT,EACF;AACF;AAEA,eAAe,KACb,IACA,SACoD;CACpD,MAAM,QAAQ,MAAM,IAAI,WAAW,EAAE,CAAC,CAAC,IAAI,OAAO;CAClD,MAAM,UAAU,QAAQ,MAAM,IAAI,aAAa,EAAE,CAAC,CAAC,IAAI,MAAM,SAAS,IAAI;CAC1E,OAAO,SAAS,UAAU;EAAE;EAAO;CAAQ,IAAI;AACjD;;AAGA,eAAe,aACb,SACA,OACA,SACA,SACgB;CAChB,MAAM,UAAU,eAAe;EAC7B,WAAW,QAAQ;EACnB,gBAAgB,QAAQ;EACxB,eAAe,MAAM,cAAc,QAAQ;EAC3C,eAAe,QAAQ,kBAAkB;EACzC,QAAQ,MAAM;CAChB,CAAC;CACD,IAAI,CAAC,QAAQ,IACX,QAAQ,OAAO,MAAM;EAAE,SAAS,MAAM;EAAI,OAAO,QAAQ;EAAO;CAAQ,GAAG,gBAAgB;CAE7F,IAAI,YAAY,aACd,OAAO;CAET,OAAO,MAAM,IAAI,WAAW,QAAQ,EAAE,CAAC,CAAC,UAAU,MAAM,IAAI,OAAO;AACrE;AAEA,eAAe,SACb,SACA,SACe;CACf,MAAM,SAAS,QAAQ,OAAO,MAAM,EAAE,SAAS,QAAQ,MAAM,GAAG,CAAC;CACjE,KAAK,MAAM,QAAQ,QAAQ,cACzB,IAAI;EAEF,MAAM,KAAK;GAAE,GAAG;GAAS;EAAO,CAAC;CACnC,SAAS,OAAO;EACd,OAAO,MAAM,EAAE,KAAK,MAAM,GAAG,0BAA0B;CACzD;AAEJ"}
@@ -0,0 +1,20 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { ExtractionLimits } from "./limits.mjs";
4
+ import { Readable } from "node:stream";
5
+ //#region src/capture/publish/extract.d.ts
6
+ /** What an extraction produced. */
7
+ interface ExtractionResult {
8
+ entries: number;
9
+ bytes: number;
10
+ }
11
+ /**
12
+ * Extract `source` (a zip stream) under `root`, which must exist.
13
+ *
14
+ * @throws {ZipLimitError} When a cap is exceeded or an entry path escapes `root`; the source is
15
+ * destroyed at once, and the files written so far are left for the caller's scratch cleanup.
16
+ */
17
+ declare function extractZipStream(source: Readable, root: string, limits: ExtractionLimits): Promise<ExtractionResult>;
18
+ //#endregion
19
+ export { ExtractionResult, extractZipStream };
20
+ //# sourceMappingURL=extract.d.mts.map
@@ -0,0 +1,119 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { ZipLimitError } from "./limits.mjs";
5
+ import { dirname, isAbsolute, join, resolve, sep } from "node:path";
6
+ import { mkdir } from "node:fs/promises";
7
+ import { createWriteStream } from "node:fs";
8
+ import { Transform } from "node:stream";
9
+ import { pipeline } from "node:stream/promises";
10
+ import { Parse } from "unzipper";
11
+ //#region src/capture/publish/extract.ts
12
+ /**
13
+ * Streaming, capped extraction of an uploaded Storybook zip into a directory. Entry count and
14
+ * uncompressed bytes are counted while the archive streams (never from a header or from
15
+ * `Content-Length`), and a path that leaves the target is refused.
16
+ */
17
+ /**
18
+ * Extract `source` (a zip stream) under `root`, which must exist.
19
+ *
20
+ * @throws {ZipLimitError} When a cap is exceeded or an entry path escapes `root`; the source is
21
+ * destroyed at once, and the files written so far are left for the caller's scratch cleanup.
22
+ */
23
+ async function extractZipStream(source, root, limits) {
24
+ const run = new Extraction(resolve(root), limits);
25
+ const failure = await run.parse(source);
26
+ const outcomes = await Promise.allSettled(run.writes);
27
+ if (failure) throw failure;
28
+ const writeError = outcomes.find((outcome) => outcome.status === "rejected");
29
+ if (writeError) throw writeError.reason;
30
+ return {
31
+ entries: run.entries,
32
+ bytes: run.bytes
33
+ };
34
+ }
35
+ function ignore() {}
36
+ /** State of one extraction. */
37
+ var Extraction = class {
38
+ entries = 0;
39
+ bytes = 0;
40
+ writes = [];
41
+ root;
42
+ limits;
43
+ #failed = false;
44
+ #fail = ignore;
45
+ constructor(root, limits) {
46
+ this.root = root;
47
+ this.limits = limits;
48
+ }
49
+ /** Resolves with the first failure, or null once the archive was read to its end. */
50
+ parse(source) {
51
+ return new Promise((settle) => {
52
+ const parser = Parse();
53
+ this.#fail = (error) => {
54
+ if (!this.#failed) {
55
+ this.#failed = true;
56
+ source.destroy();
57
+ parser.destroy();
58
+ settle(error);
59
+ }
60
+ };
61
+ parser.on("entry", (entry) => {
62
+ this.#entry(entry);
63
+ });
64
+ parser.on("error", this.#fail);
65
+ parser.on("close", () => {
66
+ settle(null);
67
+ });
68
+ source.on("error", this.#fail);
69
+ source.pipe(parser);
70
+ });
71
+ }
72
+ #entry(entry) {
73
+ this.entries += 1;
74
+ const problem = this.#problemWith(entry);
75
+ if (problem) this.#fail(problem);
76
+ if (this.#failed || entry.type === "Directory") {
77
+ entry.autodrain();
78
+ return;
79
+ }
80
+ this.#write(entry);
81
+ }
82
+ #problemWith(entry) {
83
+ if (this.entries > this.limits.maxEntries) return new ZipLimitError(`Zip holds more than ${this.limits.maxEntries} entries`);
84
+ if (unsafePath(this.root, entry.path)) return new ZipLimitError(`Blocked path traversal in uploaded Storybook: ${entry.path}`);
85
+ return null;
86
+ }
87
+ #write(entry) {
88
+ const target = join(this.root, entry.path);
89
+ const done = (async () => {
90
+ await mkdir(dirname(target), { recursive: true });
91
+ await pipeline(entry, this.#counter(), createWriteStream(target));
92
+ })();
93
+ this.writes.push(done);
94
+ done.catch(ignore);
95
+ }
96
+ /** Counts the bytes that pass; past the cap the whole extraction fails at once. */
97
+ #counter() {
98
+ return new Transform({ transform: (chunk, _encoding, callback) => {
99
+ this.bytes += chunk.length;
100
+ if (this.bytes > this.limits.maxBytes) {
101
+ const error = new ZipLimitError(`Zip expands beyond ${this.limits.maxBytes} bytes`);
102
+ this.#fail(error);
103
+ callback(error);
104
+ return;
105
+ }
106
+ callback(null, chunk);
107
+ } });
108
+ }
109
+ };
110
+ /** Absolute paths, drive letters and anything resolving outside `root`. */
111
+ function unsafePath(root, entryPath) {
112
+ if (isAbsolute(entryPath) || /^[a-zA-Z]:/u.test(entryPath)) return true;
113
+ const candidate = resolve(join(root, entryPath));
114
+ return candidate !== root && !candidate.startsWith(root + sep);
115
+ }
116
+ //#endregion
117
+ export { extractZipStream };
118
+
119
+ //# sourceMappingURL=extract.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.mjs","names":[],"sources":["../../../src/capture/publish/extract.ts"],"sourcesContent":["/**\n * Streaming, capped extraction of an uploaded Storybook zip into a directory. Entry count and\n * uncompressed bytes are counted while the archive streams (never from a header or from\n * `Content-Length`), and a path that leaves the target is refused.\n */\nimport { createWriteStream } from \"node:fs\";\nimport { mkdir } from \"node:fs/promises\";\nimport { dirname, isAbsolute, join, resolve, sep } from \"node:path\";\nimport { Transform, type Readable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport { Parse, type Entry } from \"unzipper\";\nimport { ZipLimitError, type ExtractionLimits } from \"./limits.ts\";\n\n/** What an extraction produced. */\nexport interface ExtractionResult {\n entries: number;\n bytes: number;\n}\n\n/**\n * Extract `source` (a zip stream) under `root`, which must exist.\n *\n * @throws {ZipLimitError} When a cap is exceeded or an entry path escapes `root`; the source is\n * destroyed at once, and the files written so far are left for the caller's scratch cleanup.\n */\nexport async function extractZipStream(\n source: Readable,\n root: string,\n limits: ExtractionLimits,\n): Promise<ExtractionResult> {\n const run = new Extraction(resolve(root), limits);\n const failure = await run.parse(source);\n // Writes may still be in flight after a failure; settle them before anyone removes the files.\n const outcomes = await Promise.allSettled(run.writes);\n if (failure) {\n throw failure;\n }\n const writeError = outcomes.find(\n (outcome): outcome is PromiseRejectedResult => outcome.status === \"rejected\",\n );\n if (writeError) {\n throw writeError.reason;\n }\n return { entries: run.entries, bytes: run.bytes };\n}\n\nfunction ignore(): void {}\n\n/** State of one extraction. */\nclass Extraction {\n entries = 0;\n bytes = 0;\n readonly writes: Promise<void>[] = [];\n readonly root: string;\n readonly limits: ExtractionLimits;\n #failed = false;\n #fail: (error: unknown) => void = ignore;\n\n constructor(root: string, limits: ExtractionLimits) {\n this.root = root;\n this.limits = limits;\n }\n\n /** Resolves with the first failure, or null once the archive was read to its end. */\n // oxlint-disable-next-line typescript/promise-function-async -- executor-style wrapper around stream events\n parse(source: Readable): Promise<unknown> {\n return new Promise((settle) => {\n const parser = Parse();\n this.#fail = (error: unknown): void => {\n if (!this.#failed) {\n this.#failed = true;\n source.destroy();\n parser.destroy();\n settle(error);\n }\n };\n parser.on(\"entry\", (entry: Entry) => {\n this.#entry(entry);\n });\n parser.on(\"error\", this.#fail);\n parser.on(\"close\", () => {\n settle(null);\n });\n source.on(\"error\", this.#fail);\n source.pipe(parser);\n });\n }\n\n #entry(entry: Entry): void {\n this.entries += 1;\n const problem = this.#problemWith(entry);\n if (problem) {\n this.#fail(problem);\n }\n if (this.#failed || entry.type === \"Directory\") {\n entry.autodrain();\n return;\n }\n this.#write(entry);\n }\n\n #problemWith(entry: Entry): ZipLimitError | null {\n if (this.entries > this.limits.maxEntries) {\n return new ZipLimitError(`Zip holds more than ${this.limits.maxEntries} entries`);\n }\n if (unsafePath(this.root, entry.path)) {\n return new ZipLimitError(`Blocked path traversal in uploaded Storybook: ${entry.path}`);\n }\n return null;\n }\n\n #write(entry: Entry): void {\n const target = join(this.root, entry.path);\n const done = (async (): Promise<void> => {\n await mkdir(dirname(target), { recursive: true });\n await pipeline(entry, this.#counter(), createWriteStream(target));\n })();\n this.writes.push(done);\n done.catch(ignore); // Aggregated through allSettled by the caller\n }\n\n /** Counts the bytes that pass; past the cap the whole extraction fails at once. */\n #counter(): Transform {\n return new Transform({\n transform: (chunk: Buffer, _encoding, callback) => {\n this.bytes += chunk.length;\n if (this.bytes > this.limits.maxBytes) {\n const error = new ZipLimitError(`Zip expands beyond ${this.limits.maxBytes} bytes`);\n this.#fail(error);\n callback(error);\n return;\n }\n callback(null, chunk);\n },\n });\n }\n}\n\n/** Absolute paths, drive letters and anything resolving outside `root`. */\nfunction unsafePath(root: string, entryPath: string): boolean {\n if (isAbsolute(entryPath) || /^[a-zA-Z]:/u.test(entryPath)) {\n return true;\n }\n const candidate = resolve(join(root, entryPath));\n return candidate !== root && !candidate.startsWith(root + sep);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAyBA,eAAsB,iBACpB,QACA,MACA,QAC2B;CAC3B,MAAM,MAAM,IAAI,WAAW,QAAQ,IAAI,GAAG,MAAM;CAChD,MAAM,UAAU,MAAM,IAAI,MAAM,MAAM;CAEtC,MAAM,WAAW,MAAM,QAAQ,WAAW,IAAI,MAAM;CACpD,IAAI,SACF,MAAM;CAER,MAAM,aAAa,SAAS,MACzB,YAA8C,QAAQ,WAAW,UACpE;CACA,IAAI,YACF,MAAM,WAAW;CAEnB,OAAO;EAAE,SAAS,IAAI;EAAS,OAAO,IAAI;CAAM;AAClD;AAEA,SAAS,SAAe,CAAC;;AAGzB,IAAM,aAAN,MAAiB;CACf,UAAU;CACV,QAAQ;CACR,SAAmC,CAAC;CACpC;CACA;CACA,UAAU;CACV,QAAkC;CAElC,YAAY,MAAc,QAA0B;EAClD,KAAK,OAAO;EACZ,KAAK,SAAS;CAChB;;CAIA,MAAM,QAAoC;EACxC,OAAO,IAAI,SAAS,WAAW;GAC7B,MAAM,SAAS,MAAM;GACrB,KAAK,SAAS,UAAyB;IACrC,IAAI,CAAC,KAAK,SAAS;KACjB,KAAK,UAAU;KACf,OAAO,QAAQ;KACf,OAAO,QAAQ;KACf,OAAO,KAAK;IACd;GACF;GACA,OAAO,GAAG,UAAU,UAAiB;IACnC,KAAK,OAAO,KAAK;GACnB,CAAC;GACD,OAAO,GAAG,SAAS,KAAK,KAAK;GAC7B,OAAO,GAAG,eAAe;IACvB,OAAO,IAAI;GACb,CAAC;GACD,OAAO,GAAG,SAAS,KAAK,KAAK;GAC7B,OAAO,KAAK,MAAM;EACpB,CAAC;CACH;CAEA,OAAO,OAAoB;EACzB,KAAK,WAAW;EAChB,MAAM,UAAU,KAAK,aAAa,KAAK;EACvC,IAAI,SACF,KAAK,MAAM,OAAO;EAEpB,IAAI,KAAK,WAAW,MAAM,SAAS,aAAa;GAC9C,MAAM,UAAU;GAChB;EACF;EACA,KAAK,OAAO,KAAK;CACnB;CAEA,aAAa,OAAoC;EAC/C,IAAI,KAAK,UAAU,KAAK,OAAO,YAC7B,OAAO,IAAI,cAAc,uBAAuB,KAAK,OAAO,WAAW,SAAS;EAElF,IAAI,WAAW,KAAK,MAAM,MAAM,IAAI,GAClC,OAAO,IAAI,cAAc,iDAAiD,MAAM,MAAM;EAExF,OAAO;CACT;CAEA,OAAO,OAAoB;EACzB,MAAM,SAAS,KAAK,KAAK,MAAM,MAAM,IAAI;EACzC,MAAM,QAAQ,YAA2B;GACvC,MAAM,MAAM,QAAQ,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;GAChD,MAAM,SAAS,OAAO,KAAK,SAAS,GAAG,kBAAkB,MAAM,CAAC;EAClE,EAAA,CAAG;EACH,KAAK,OAAO,KAAK,IAAI;EACrB,KAAK,MAAM,MAAM;CACnB;;CAGA,WAAsB;EACpB,OAAO,IAAI,UAAU,EACnB,YAAY,OAAe,WAAW,aAAa;GACjD,KAAK,SAAS,MAAM;GACpB,IAAI,KAAK,QAAQ,KAAK,OAAO,UAAU;IACrC,MAAM,QAAQ,IAAI,cAAc,sBAAsB,KAAK,OAAO,SAAS,OAAO;IAClF,KAAK,MAAM,KAAK;IAChB,SAAS,KAAK;IACd;GACF;GACA,SAAS,MAAM,KAAK;EACtB,EACF,CAAC;CACH;AACF;;AAGA,SAAS,WAAW,MAAc,WAA4B;CAC5D,IAAI,WAAW,SAAS,KAAK,cAAc,KAAK,SAAS,GACvD,OAAO;CAET,MAAM,YAAY,QAAQ,KAAK,MAAM,SAAS,CAAC;CAC/C,OAAO,cAAc,QAAQ,CAAC,UAAU,WAAW,OAAO,GAAG;AAC/D"}
@@ -0,0 +1,28 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { DatabaseAdapter } from "../../adapter/database.mjs";
4
+ import { Logger } from "../../logger.mjs";
5
+ import { StorageAdapter } from "../../adapter/storage.mjs";
6
+ import { ExtractionLimits } from "./limits.mjs";
7
+ import { KindHandler } from "../consumer/types.mjs";
8
+ import { PublishCompleter } from "./complete.mjs";
9
+ //#region src/capture/publish/handler.d.ts
10
+ /** What {@link createPublishHandler} needs. */
11
+ interface PublishHandlerOptions {
12
+ db: DatabaseAdapter;
13
+ storage: StorageAdapter;
14
+ scratchDir: string;
15
+ limits: ExtractionLimits;
16
+ completer: PublishCompleter;
17
+ logger: Logger;
18
+ }
19
+ /**
20
+ * The handler. Resolving acks the job. A zip that breaks a cap is a final answer (retrying cannot
21
+ * help): the build is failed and the job acked. Any other error is retried by the consumer, and
22
+ * the build is failed on the last attempt. A redelivery after a lost lease repeats the extraction
23
+ * (same keys) and leaves an already accepted build alone.
24
+ */
25
+ declare function createPublishHandler(options: PublishHandlerOptions): KindHandler;
26
+ //#endregion
27
+ export { PublishHandlerOptions, createPublishHandler };
28
+ //# sourceMappingURL=handler.d.mts.map
@@ -0,0 +1,62 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { BuildModel } from "../../models/build.mjs";
5
+ import { ZipLimitError } from "./limits.mjs";
6
+ import { publishStoredZip } from "./run.mjs";
7
+ //#region src/capture/publish/handler.ts
8
+ /** A `publish` job needs the build to publish. */
9
+ function hasBuild(job) {
10
+ return typeof job.buildId === "string" && job.buildId.length > 0;
11
+ }
12
+ /**
13
+ * The handler. Resolving acks the job. A zip that breaks a cap is a final answer (retrying cannot
14
+ * help): the build is failed and the job acked. Any other error is retried by the consumer, and
15
+ * the build is failed on the last attempt. A redelivery after a lost lease repeats the extraction
16
+ * (same keys) and leaves an already accepted build alone.
17
+ */
18
+ function createPublishHandler(options) {
19
+ return {
20
+ accepts: hasBuild,
21
+ run: async (job, context) => {
22
+ const build = await new BuildModel(options.db).get(job.buildId);
23
+ if (!build) {
24
+ context.logger.warn("build is gone; dropping publish job");
25
+ return;
26
+ }
27
+ await publishOnce(options, job, context, build.projectId);
28
+ }
29
+ };
30
+ }
31
+ async function publishOnce(options, job, context, projectId) {
32
+ const { completer, storage, scratchDir, limits, db } = options;
33
+ try {
34
+ await publishStoredZip({
35
+ storage,
36
+ scratchDir,
37
+ limits,
38
+ db,
39
+ logger: context.logger,
40
+ touch: context.touch
41
+ }, {
42
+ projectId,
43
+ buildId: job.buildId
44
+ });
45
+ } catch (error) {
46
+ const permanent = error instanceof ZipLimitError;
47
+ if (permanent || (job.attempt ?? 1) >= (job.maxAttempts ?? 1)) await completer.complete(job.buildId, {
48
+ ok: false,
49
+ error: messageOf(error)
50
+ });
51
+ if (permanent) return;
52
+ throw error;
53
+ }
54
+ await completer.complete(job.buildId, { ok: true });
55
+ }
56
+ function messageOf(error) {
57
+ return error instanceof Error ? error.message : String(error);
58
+ }
59
+ //#endregion
60
+ export { createPublishHandler };
61
+
62
+ //# sourceMappingURL=handler.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.mjs","names":[],"sources":["../../../src/capture/publish/handler.ts"],"sourcesContent":["import type { DatabaseAdapter } from \"../../adapters/database.ts\";\n/** The `publish` job handler of the consumer (ADR 0006, decision 10): publish a stored zip, then complete the build. */\nimport type { StorageAdapter } from \"../../adapters/storage.ts\";\nimport type { Logger } from \"../../logger.ts\";\nimport { BuildModel } from \"../../models/build.ts\";\nimport type { ConsumerJob, KindHandler } from \"../consumer/types.ts\";\nimport type { PublishCompleter } from \"./complete.ts\";\nimport { ZipLimitError, type ExtractionLimits } from \"./limits.ts\";\nimport { publishStoredZip } from \"./run.ts\";\n\n/** What {@link createPublishHandler} needs. */\nexport interface PublishHandlerOptions {\n db: DatabaseAdapter;\n storage: StorageAdapter;\n scratchDir: string;\n limits: ExtractionLimits;\n completer: PublishCompleter;\n logger: Logger;\n}\n\n/** A `publish` job needs the build to publish. */\nfunction hasBuild(job: ConsumerJob): boolean {\n return typeof job.buildId === \"string\" && job.buildId.length > 0;\n}\n\n/**\n * The handler. Resolving acks the job. A zip that breaks a cap is a final answer (retrying cannot\n * help): the build is failed and the job acked. Any other error is retried by the consumer, and\n * the build is failed on the last attempt. A redelivery after a lost lease repeats the extraction\n * (same keys) and leaves an already accepted build alone.\n */\nexport function createPublishHandler(options: PublishHandlerOptions): KindHandler {\n return {\n accepts: hasBuild,\n run: async (job, context) => {\n const build = await new BuildModel(options.db).get(job.buildId);\n if (!build) {\n context.logger.warn(\"build is gone; dropping publish job\");\n return;\n }\n await publishOnce(options, job, context, build.projectId);\n },\n };\n}\n\nasync function publishOnce(\n options: PublishHandlerOptions,\n job: ConsumerJob,\n context: { logger: Logger; touch: () => void },\n projectId: string,\n): Promise<void> {\n const { completer, storage, scratchDir, limits, db } = options;\n try {\n await publishStoredZip(\n { storage, scratchDir, limits, db, logger: context.logger, touch: context.touch },\n { projectId, buildId: job.buildId },\n );\n } catch (error) {\n const permanent = error instanceof ZipLimitError;\n if (permanent || (job.attempt ?? 1) >= (job.maxAttempts ?? 1)) {\n await completer.complete(job.buildId, { ok: false, error: messageOf(error) });\n }\n if (permanent) {\n return;\n }\n throw error;\n }\n await completer.complete(job.buildId, { ok: true });\n}\n\nfunction messageOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"],"mappings":";;;;;;;;AAqBA,SAAS,SAAS,KAA2B;CAC3C,OAAO,OAAO,IAAI,YAAY,YAAY,IAAI,QAAQ,SAAS;AACjE;;;;;;;AAQA,SAAgB,qBAAqB,SAA6C;CAChF,OAAO;EACL,SAAS;EACT,KAAK,OAAO,KAAK,YAAY;GAC3B,MAAM,QAAQ,MAAM,IAAI,WAAW,QAAQ,EAAE,CAAC,CAAC,IAAI,IAAI,OAAO;GAC9D,IAAI,CAAC,OAAO;IACV,QAAQ,OAAO,KAAK,qCAAqC;IACzD;GACF;GACA,MAAM,YAAY,SAAS,KAAK,SAAS,MAAM,SAAS;EAC1D;CACF;AACF;AAEA,eAAe,YACb,SACA,KACA,SACA,WACe;CACf,MAAM,EAAE,WAAW,SAAS,YAAY,QAAQ,OAAO;CACvD,IAAI;EACF,MAAM,iBACJ;GAAE;GAAS;GAAY;GAAQ;GAAI,QAAQ,QAAQ;GAAQ,OAAO,QAAQ;EAAM,GAChF;GAAE;GAAW,SAAS,IAAI;EAAQ,CACpC;CACF,SAAS,OAAO;EACd,MAAM,YAAY,iBAAiB;EACnC,IAAI,cAAc,IAAI,WAAW,OAAO,IAAI,eAAe,IACzD,MAAM,UAAU,SAAS,IAAI,SAAS;GAAE,IAAI;GAAO,OAAO,UAAU,KAAK;EAAE,CAAC;EAE9E,IAAI,WACF;EAEF,MAAM;CACR;CACA,MAAM,UAAU,SAAS,IAAI,SAAS,EAAE,IAAI,KAAK,CAAC;AACpD;AAEA,SAAS,UAAU,OAAwB;CACzC,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D"}
@@ -0,0 +1,10 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { DEFAULT_MAX_INLINE_UNZIP_SIZE, DEFAULT_MAX_UNZIPPED_BYTES, DEFAULT_MAX_ZIP_ENTRIES, ExtractionLimits, FILE_BY_FILE_HINT, PublishLimits, ZipLimitError, extractionLimitsOf, publishRoute, resolvePublishLimits } from "./limits.mjs";
4
+ import { AfterPublishContext, AfterPublishHook, PublishCompleter, PublishCompleterOptions, PublishOutcome, createPublishCompleter } from "./complete.mjs";
5
+ import { PublishHandlerOptions, createPublishHandler } from "./handler.mjs";
6
+ import { PublishFacts, publishVerdict } from "./accept.mjs";
7
+ import { publishingOf } from "./published.mjs";
8
+ import { ExtractionResult, extractZipStream } from "./extract.mjs";
9
+ import { PUBLISH_SCRATCH_ID, PublishZipOptions, publishStoredZip } from "./run.mjs";
10
+ export { type AfterPublishContext, type AfterPublishHook, DEFAULT_MAX_INLINE_UNZIP_SIZE, DEFAULT_MAX_UNZIPPED_BYTES, DEFAULT_MAX_ZIP_ENTRIES, type ExtractionLimits, type ExtractionResult, FILE_BY_FILE_HINT, PUBLISH_SCRATCH_ID, type PublishCompleter, type PublishCompleterOptions, type PublishFacts, type PublishHandlerOptions, type PublishLimits, type PublishOutcome, type PublishZipOptions, ZipLimitError, createPublishCompleter, createPublishHandler, extractZipStream, extractionLimitsOf, publishRoute, publishStoredZip, publishVerdict, publishingOf, resolvePublishLimits };
@@ -0,0 +1,37 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { ShelfConfig } from "../../config.mjs";
4
+ //#region src/capture/publish/limits.d.ts
5
+ /** Zip size (bytes) up to which a zip is extracted inline in the upload request: 50 MB. */
6
+ declare const DEFAULT_MAX_INLINE_UNZIP_SIZE: number;
7
+ /** Entries an uploaded Storybook zip may hold. A large Storybook has some thousands of files. */
8
+ declare const DEFAULT_MAX_ZIP_ENTRIES = 50000;
9
+ /** Total uncompressed bytes an uploaded Storybook zip may expand to: 2 GiB. */
10
+ declare const DEFAULT_MAX_UNZIPPED_BYTES: number;
11
+ /** Caps enforced while a zip is extracted; the compressed size alone is no bound. */
12
+ interface ExtractionLimits {
13
+ /** Most entries (files and directories) the archive may hold. */
14
+ maxEntries: number;
15
+ /** Most uncompressed bytes all files together may expand to, counted while streaming. */
16
+ maxBytes: number;
17
+ }
18
+ /** Everything the publish pipeline reads from `ShelfConfig`, defaults applied. */
19
+ interface PublishLimits extends ExtractionLimits {
20
+ /** Largest zip extracted inline in the upload request; `0` sends every zip to a `publish` job. */
21
+ inlineBytes: number;
22
+ }
23
+ /** The effective publish limits of a config. */
24
+ declare function resolvePublishLimits(config?: Pick<ShelfConfig, "maxInlineUnzipSize" | "maxZipEntries" | "maxUnzippedBytes">): PublishLimits;
25
+ /** The extraction caps alone. */
26
+ declare function extractionLimitsOf(limits: PublishLimits | ExtractionLimits): ExtractionLimits;
27
+ /** How an upload of `zipBytes` is published: inline in the request, or by a `publish` job. */
28
+ declare function publishRoute(zipBytes: number, limits: Pick<PublishLimits, "inlineBytes">): "inline" | "job";
29
+ /** An archive that breaks a cap or tries to write outside the target; retrying cannot help. */
30
+ declare class ZipLimitError extends Error {
31
+ readonly name = "ZipLimitError";
32
+ }
33
+ /** Message of the 413 an over-limit zip gets on a host with no `publish` consumer. */
34
+ declare const FILE_BY_FILE_HINT: string;
35
+ //#endregion
36
+ export { DEFAULT_MAX_INLINE_UNZIP_SIZE, DEFAULT_MAX_UNZIPPED_BYTES, DEFAULT_MAX_ZIP_ENTRIES, ExtractionLimits, FILE_BY_FILE_HINT, PublishLimits, ZipLimitError, extractionLimitsOf, publishRoute, resolvePublishLimits };
37
+ //# sourceMappingURL=limits.d.mts.map
@@ -0,0 +1,39 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ //#region src/capture/publish/limits.ts
5
+ /** Zip size (bytes) up to which a zip is extracted inline in the upload request: 50 MB. */
6
+ const DEFAULT_MAX_INLINE_UNZIP_SIZE = 52428800;
7
+ /** Entries an uploaded Storybook zip may hold. A large Storybook has some thousands of files. */
8
+ const DEFAULT_MAX_ZIP_ENTRIES = 5e4;
9
+ /** Total uncompressed bytes an uploaded Storybook zip may expand to: 2 GiB. */
10
+ const DEFAULT_MAX_UNZIPPED_BYTES = 2147483648;
11
+ /** The effective publish limits of a config. */
12
+ function resolvePublishLimits(config) {
13
+ return {
14
+ inlineBytes: config?.maxInlineUnzipSize ?? 52428800,
15
+ maxEntries: config?.maxZipEntries ?? 5e4,
16
+ maxBytes: config?.maxUnzippedBytes ?? 2147483648
17
+ };
18
+ }
19
+ /** The extraction caps alone. */
20
+ function extractionLimitsOf(limits) {
21
+ return {
22
+ maxEntries: limits.maxEntries,
23
+ maxBytes: limits.maxBytes
24
+ };
25
+ }
26
+ /** How an upload of `zipBytes` is published: inline in the request, or by a `publish` job. */
27
+ function publishRoute(zipBytes, limits) {
28
+ return zipBytes <= limits.inlineBytes ? "inline" : "job";
29
+ }
30
+ /** An archive that breaks a cap or tries to write outside the target; retrying cannot help. */
31
+ var ZipLimitError = class extends Error {
32
+ name = "ZipLimitError";
33
+ };
34
+ /** Message of the 413 an over-limit zip gets on a host with no `publish` consumer. */
35
+ const FILE_BY_FILE_HINT = "Zip is larger than the inline publish limit and this host runs no publish consumer. Upload the Storybook file by file instead: POST /dedup, then PUT /content, then POST /manifest (the storyshelf CLI does this when it is given a directory).";
36
+ //#endregion
37
+ export { DEFAULT_MAX_INLINE_UNZIP_SIZE, DEFAULT_MAX_UNZIPPED_BYTES, DEFAULT_MAX_ZIP_ENTRIES, FILE_BY_FILE_HINT, ZipLimitError, extractionLimitsOf, publishRoute, resolvePublishLimits };
38
+
39
+ //# sourceMappingURL=limits.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limits.mjs","names":[],"sources":["../../../src/capture/publish/limits.ts"],"sourcesContent":["/** Publish limits (ADR 0006, decision 10): the inline threshold and the extraction caps, with defaults. */\nimport type { ShelfConfig } from \"../../config.ts\";\n\n/** Zip size (bytes) up to which a zip is extracted inline in the upload request: 50 MB. */\nexport const DEFAULT_MAX_INLINE_UNZIP_SIZE = 50 * 1024 * 1024;\n\n/** Entries an uploaded Storybook zip may hold. A large Storybook has some thousands of files. */\nexport const DEFAULT_MAX_ZIP_ENTRIES = 50_000;\n\n/** Total uncompressed bytes an uploaded Storybook zip may expand to: 2 GiB. */\nexport const DEFAULT_MAX_UNZIPPED_BYTES = 2 * 1024 * 1024 * 1024;\n\n/** Caps enforced while a zip is extracted; the compressed size alone is no bound. */\nexport interface ExtractionLimits {\n /** Most entries (files and directories) the archive may hold. */\n maxEntries: number;\n /** Most uncompressed bytes all files together may expand to, counted while streaming. */\n maxBytes: number;\n}\n\n/** Everything the publish pipeline reads from `ShelfConfig`, defaults applied. */\nexport interface PublishLimits extends ExtractionLimits {\n /** Largest zip extracted inline in the upload request; `0` sends every zip to a `publish` job. */\n inlineBytes: number;\n}\n\n/** The effective publish limits of a config. */\nexport function resolvePublishLimits(\n config?: Pick<ShelfConfig, \"maxInlineUnzipSize\" | \"maxZipEntries\" | \"maxUnzippedBytes\">,\n): PublishLimits {\n return {\n inlineBytes: config?.maxInlineUnzipSize ?? DEFAULT_MAX_INLINE_UNZIP_SIZE,\n maxEntries: config?.maxZipEntries ?? DEFAULT_MAX_ZIP_ENTRIES,\n maxBytes: config?.maxUnzippedBytes ?? DEFAULT_MAX_UNZIPPED_BYTES,\n };\n}\n\n/** The extraction caps alone. */\nexport function extractionLimitsOf(limits: PublishLimits | ExtractionLimits): ExtractionLimits {\n return { maxEntries: limits.maxEntries, maxBytes: limits.maxBytes };\n}\n\n/** How an upload of `zipBytes` is published: inline in the request, or by a `publish` job. */\nexport function publishRoute(\n zipBytes: number,\n limits: Pick<PublishLimits, \"inlineBytes\">,\n): \"inline\" | \"job\" {\n return zipBytes <= limits.inlineBytes ? \"inline\" : \"job\";\n}\n\n/** An archive that breaks a cap or tries to write outside the target; retrying cannot help. */\nexport class ZipLimitError extends Error {\n override readonly name = \"ZipLimitError\";\n}\n\n/** Message of the 413 an over-limit zip gets on a host with no `publish` consumer. */\nexport const FILE_BY_FILE_HINT =\n \"Zip is larger than the inline publish limit and this host runs no publish consumer. \" +\n \"Upload the Storybook file by file instead: POST /dedup, then PUT /content, then POST /manifest \" +\n \"(the storyshelf CLI does this when it is given a directory).\";\n"],"mappings":";;;;;AAIA,MAAa,gCAAgC;;AAG7C,MAAa,0BAA0B;;AAGvC,MAAa,6BAA6B;;AAiB1C,SAAgB,qBACd,QACe;CACf,OAAO;EACL,aAAa,QAAQ,sBAAA;EACrB,YAAY,QAAQ,iBAAA;EACpB,UAAU,QAAQ,oBAAA;CACpB;AACF;;AAGA,SAAgB,mBAAmB,QAA4D;CAC7F,OAAO;EAAE,YAAY,OAAO;EAAY,UAAU,OAAO;CAAS;AACpE;;AAGA,SAAgB,aACd,UACA,QACkB;CAClB,OAAO,YAAY,OAAO,cAAc,WAAW;AACrD;;AAGA,IAAa,gBAAb,cAAmC,MAAM;CACvC,OAAyB;AAC3B;;AAGA,MAAa,oBACX"}
@@ -0,0 +1,15 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { StorageAdapter } from "../../adapter/storage.mjs";
4
+ import { PublishingState } from "../../adapters/notifier/build-state.mjs";
5
+ import { Build } from "../../schema/build.mjs";
6
+ //#region src/capture/publish/published.d.ts
7
+ /**
8
+ * How publishing the build stands. The manifest is written last, so its presence means done. A
9
+ * `failed` or `cancelled` build without one did not publish (or never uploaded); any other build waits. Without
10
+ * a storage adapter nothing can be read, so publishing reads as pending.
11
+ */
12
+ declare function publishingOf(storage: StorageAdapter | undefined, build: Pick<Build, "id" | "projectId" | "status">): Promise<PublishingState>;
13
+ //#endregion
14
+ export { publishingOf };
15
+ //# sourceMappingURL=published.d.mts.map
@@ -0,0 +1,18 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { storybookDir } from "../../paths.mjs";
5
+ //#region src/capture/publish/published.ts
6
+ /**
7
+ * How publishing the build stands. The manifest is written last, so its presence means done. A
8
+ * `failed` or `cancelled` build without one did not publish (or never uploaded); any other build waits. Without
9
+ * a storage adapter nothing can be read, so publishing reads as pending.
10
+ */
11
+ async function publishingOf(storage, build) {
12
+ if (storage !== void 0 && await storage.exists(`${storybookDir(build.projectId, build.id)}/manifest.json`)) return "success";
13
+ return build.status === "failed" || build.status === "cancelled" ? "failure" : "pending";
14
+ }
15
+ //#endregion
16
+ export { publishingOf };
17
+
18
+ //# sourceMappingURL=published.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"published.mjs","names":[],"sources":["../../../src/capture/publish/published.ts"],"sourcesContent":["/**\n * Whether a build's Storybook is published: the one probe behind the `<prefix>/storybook` status\n * (ADR 0014) and the rule that a verdict waits for publishing to end (ADR 0013, decision 3).\n */\nimport type { PublishingState } from \"../../adapters/notifier/build-state.ts\";\nimport type { StorageAdapter } from \"../../adapters/storage.ts\";\nimport type { Build } from \"../../schema/build.ts\";\nimport { storybookDir } from \"../../utils/paths.ts\";\n\n/**\n * How publishing the build stands. The manifest is written last, so its presence means done. A\n * `failed` or `cancelled` build without one did not publish (or never uploaded); any other build waits. Without\n * a storage adapter nothing can be read, so publishing reads as pending.\n */\nexport async function publishingOf(\n storage: StorageAdapter | undefined,\n build: Pick<Build, \"id\" | \"projectId\" | \"status\">,\n): Promise<PublishingState> {\n const published =\n storage !== undefined &&\n (await storage.exists(`${storybookDir(build.projectId, build.id)}/manifest.json`));\n if (published) {\n return \"success\";\n }\n return build.status === \"failed\" || build.status === \"cancelled\" ? \"failure\" : \"pending\";\n}\n"],"mappings":";;;;;;;;;;AAcA,eAAsB,aACpB,SACA,OAC0B;CAI1B,IAFE,YAAY,KAAA,KACX,MAAM,QAAQ,OAAO,GAAG,aAAa,MAAM,WAAW,MAAM,EAAE,EAAE,eAAe,GAEhF,OAAO;CAET,OAAO,MAAM,WAAW,YAAY,MAAM,WAAW,cAAc,YAAY;AACjF"}
@@ -0,0 +1,33 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { DatabaseAdapter } from "../../adapter/database.mjs";
4
+ import { Logger } from "../../logger.mjs";
5
+ import { StorageAdapter } from "../../adapter/storage.mjs";
6
+ import { ExtractionLimits } from "./limits.mjs";
7
+ //#region src/capture/publish/run.d.ts
8
+ /** Scratch id publishing uses, so it never shares a directory with the build's capture. */
9
+ declare const PUBLISH_SCRATCH_ID = "publish";
10
+ /** What {@link publishStoredZip} works with. */
11
+ interface PublishZipOptions {
12
+ storage: StorageAdapter;
13
+ scratchDir: string;
14
+ limits: ExtractionLimits;
15
+ /** Records the persisted content in `content_refs`, so retention knows who still uses it. */
16
+ db?: DatabaseAdapter | undefined;
17
+ logger: Logger;
18
+ /** Called between stages (renews a job's lease). */
19
+ touch?: () => void;
20
+ }
21
+ /**
22
+ * Extract `storybook.zip` of a build and persist its statics. The manifest is written last, so a
23
+ * build serves only once complete and a repeat run rewrites the same keys.
24
+ *
25
+ * @throws {ZipLimitError} When the zip breaks a cap or tries to escape the target.
26
+ */
27
+ declare function publishStoredZip(options: PublishZipOptions, target: {
28
+ projectId: string;
29
+ buildId: string;
30
+ }): Promise<void>;
31
+ //#endregion
32
+ export { PUBLISH_SCRATCH_ID, PublishZipOptions, publishStoredZip };
33
+ //# sourceMappingURL=run.d.mts.map