@storyshelf/core 0.6.2 → 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 (727) hide show
  1. package/README.md +49 -32
  2. package/dist/adapter/capture-queue.d.mts +44 -4
  3. package/dist/adapter/capture-runner.d.mts +134 -12
  4. package/dist/adapter/capture-runner.mjs +9 -12
  5. package/dist/adapter/database.d.mts +89 -10
  6. package/dist/adapter/database.mjs +4 -3
  7. package/dist/adapter/database.mjs.map +1 -1
  8. package/dist/adapter/design.d.mts +83 -0
  9. package/dist/adapter/design.mjs +27 -0
  10. package/dist/adapter/design.mjs.map +1 -0
  11. package/dist/adapter/email-sender.d.mts +0 -2
  12. package/dist/adapter/metadata.d.mts +3 -12
  13. package/dist/adapter/notifier.d.mts +6 -3
  14. package/dist/adapter/notifier.mjs +5 -1
  15. package/dist/adapter/oauth.d.mts +50 -0
  16. package/dist/adapter/setup.d.mts +4 -7
  17. package/dist/adapter/setup.mjs +3 -3
  18. package/dist/adapter/setup.mjs.map +1 -1
  19. package/dist/adapter/storage.d.mts +1 -3
  20. package/dist/adapter/webhook-events.d.mts +0 -2
  21. package/dist/adapter/webhook-events.mjs +13 -14
  22. package/dist/adapter/webhook-events.mjs.map +1 -1
  23. package/dist/adapters/capture-target-offers.d.mts +26 -0
  24. package/dist/adapters/capture-target-offers.mjs +57 -0
  25. package/dist/adapters/capture-target-offers.mjs.map +1 -0
  26. package/dist/adapters/capture-target.d.mts +69 -0
  27. package/dist/adapters/capture-target.mjs +75 -0
  28. package/dist/adapters/capture-target.mjs.map +1 -0
  29. package/dist/adapters/notifier/build-comment.d.mts +22 -0
  30. package/dist/adapters/notifier/build-comment.mjs +23 -0
  31. package/dist/adapters/notifier/build-comment.mjs.map +1 -0
  32. package/dist/adapters/notifier/build-state.d.mts +97 -0
  33. package/dist/adapters/notifier/build-state.mjs +217 -0
  34. package/dist/adapters/notifier/build-state.mjs.map +1 -0
  35. package/dist/adapters/notifier/events.d.mts +4 -4
  36. package/dist/adapters/notifier/events.mjs +32 -20
  37. package/dist/adapters/notifier/events.mjs.map +1 -1
  38. package/dist/adapters/notifier/format.d.mts +0 -2
  39. package/dist/adapters/notifier/format.mjs +1 -2
  40. package/dist/adapters/notifier/provider.d.mts +53 -9
  41. package/dist/adapters/notifier/provider.mjs +11 -0
  42. package/dist/adapters/notifier/provider.mjs.map +1 -0
  43. package/dist/adapters/notifier/state.d.mts +22 -0
  44. package/dist/adapters/notifier/state.mjs +65 -0
  45. package/dist/adapters/notifier/state.mjs.map +1 -0
  46. package/dist/adapters/notifier/types.d.mts +51 -4
  47. package/dist/adapters/remote-browser.d.mts +168 -0
  48. package/dist/adapters/remote-browser.mjs +201 -0
  49. package/dist/adapters/remote-browser.mjs.map +1 -0
  50. package/dist/adapters/remote-lease-attempt.mjs +39 -0
  51. package/dist/adapters/remote-lease-attempt.mjs.map +1 -0
  52. package/dist/adapters/remote-lease-error.d.mts +25 -0
  53. package/dist/adapters/remote-lease-error.mjs +54 -0
  54. package/dist/adapters/remote-lease-error.mjs.map +1 -0
  55. package/dist/adapters/remote-lease-held.d.mts +15 -0
  56. package/dist/adapters/remote-lease-held.mjs +86 -0
  57. package/dist/adapters/remote-lease-held.mjs.map +1 -0
  58. package/dist/adapters/remote-lease-shutdown.d.mts +12 -0
  59. package/dist/adapters/remote-lease-shutdown.mjs +32 -0
  60. package/dist/adapters/remote-lease-shutdown.mjs.map +1 -0
  61. package/dist/adapters/remote-lease.d.mts +39 -0
  62. package/dist/adapters/remote-lease.mjs +175 -0
  63. package/dist/adapters/remote-lease.mjs.map +1 -0
  64. package/dist/adapters/remote-limiter.mjs +121 -0
  65. package/dist/adapters/remote-limiter.mjs.map +1 -0
  66. package/dist/adapters/remote-pool.d.mts +56 -0
  67. package/dist/adapters/remote-pool.mjs +195 -0
  68. package/dist/adapters/remote-pool.mjs.map +1 -0
  69. package/dist/adapters/render-runs.d.mts +23 -0
  70. package/dist/adapters/render-runs.mjs +40 -0
  71. package/dist/adapters/render-runs.mjs.map +1 -0
  72. package/dist/adapters/setup-runner.d.mts +0 -2
  73. package/dist/adapters/setup-runner.mjs +3 -6
  74. package/dist/adapters/setup-runner.mjs.map +1 -1
  75. package/dist/adapters/sources.d.mts +10 -0
  76. package/dist/adapters/sources.mjs +19 -0
  77. package/dist/adapters/sources.mjs.map +1 -0
  78. package/dist/adapters/validate.d.mts +0 -2
  79. package/dist/adapters/validate.mjs +27 -20
  80. package/dist/adapters/validate.mjs.map +1 -1
  81. package/dist/ai.d.mts +103 -0
  82. package/dist/ai.mjs +18 -0
  83. package/dist/ai.mjs.map +1 -0
  84. package/dist/auth.d.mts +0 -2
  85. package/dist/auth.mjs +1 -2
  86. package/dist/brand-packs.d.mts +0 -2
  87. package/dist/brand-packs.mjs +0 -1
  88. package/dist/capture/a11y/axe-source.d.mts +16 -0
  89. package/dist/capture/a11y/axe-source.mjs +36 -0
  90. package/dist/capture/a11y/axe-source.mjs.map +1 -0
  91. package/dist/capture/a11y/in-page.d.mts +25 -0
  92. package/dist/capture/a11y/in-page.mjs +59 -0
  93. package/dist/capture/a11y/in-page.mjs.map +1 -0
  94. package/dist/capture/a11y/map.d.mts +22 -0
  95. package/dist/capture/a11y/map.mjs +47 -0
  96. package/dist/capture/a11y/map.mjs.map +1 -0
  97. package/dist/capture/a11y/options.d.mts +21 -0
  98. package/dist/capture/a11y/options.mjs +74 -0
  99. package/dist/capture/a11y/options.mjs.map +1 -0
  100. package/dist/capture/adapter.d.mts +46 -5
  101. package/dist/capture/adapter.mjs +1 -2
  102. package/dist/capture/adapter.mjs.map +1 -1
  103. package/dist/capture/affected.d.mts +2 -2
  104. package/dist/capture/affected.mjs +14 -6
  105. package/dist/capture/affected.mjs.map +1 -1
  106. package/dist/capture/attempt-logger.mjs +83 -0
  107. package/dist/capture/attempt-logger.mjs.map +1 -0
  108. package/dist/capture/browsers.d.mts +33 -0
  109. package/dist/capture/browsers.mjs +66 -0
  110. package/dist/capture/browsers.mjs.map +1 -0
  111. package/dist/capture/build-state/deliver.d.mts +30 -0
  112. package/dist/capture/build-state/deliver.mjs +117 -0
  113. package/dist/capture/build-state/deliver.mjs.map +1 -0
  114. package/dist/capture/build-state/index.d.mts +8 -0
  115. package/dist/capture/build-state/notify-job.d.mts +54 -0
  116. package/dist/capture/build-state/notify-job.mjs +132 -0
  117. package/dist/capture/build-state/notify-job.mjs.map +1 -0
  118. package/dist/capture/build-state/publisher.d.mts +21 -0
  119. package/dist/capture/build-state/publisher.mjs +106 -0
  120. package/dist/capture/build-state/publisher.mjs.map +1 -0
  121. package/dist/capture/build-state/resume.d.mts +7 -0
  122. package/dist/capture/build-state/resume.mjs +46 -0
  123. package/dist/capture/build-state/resume.mjs.map +1 -0
  124. package/dist/capture/build-state/types.d.mts +47 -0
  125. package/dist/capture/catalog/catalogue.d.mts +20 -0
  126. package/dist/capture/catalog/catalogue.mjs +52 -0
  127. package/dist/capture/catalog/catalogue.mjs.map +1 -0
  128. package/dist/capture/catalog/components.d.mts +21 -0
  129. package/dist/capture/catalog/components.mjs +54 -0
  130. package/dist/capture/catalog/components.mjs.map +1 -0
  131. package/dist/capture/catalog/history.d.mts +28 -0
  132. package/dist/capture/catalog/history.mjs +50 -0
  133. package/dist/capture/catalog/history.mjs.map +1 -0
  134. package/dist/capture/catalog/hook.d.mts +12 -0
  135. package/dist/capture/catalog/hook.mjs +18 -0
  136. package/dist/capture/catalog/hook.mjs.map +1 -0
  137. package/dist/capture/catalog/index.d.mts +11 -0
  138. package/dist/capture/catalog/load.d.mts +24 -0
  139. package/dist/capture/catalog/load.mjs +48 -0
  140. package/dist/capture/catalog/load.mjs.map +1 -0
  141. package/dist/capture/catalog/parse.d.mts +13 -0
  142. package/dist/capture/catalog/parse.mjs +55 -0
  143. package/dist/capture/catalog/parse.mjs.map +1 -0
  144. package/dist/capture/catalog/read.d.mts +15 -0
  145. package/dist/capture/catalog/read.mjs +39 -0
  146. package/dist/capture/catalog/read.mjs.map +1 -0
  147. package/dist/capture/catalog/types.d.mts +31 -0
  148. package/dist/capture/comparison-reference.d.mts +27 -0
  149. package/dist/capture/comparison-reference.mjs +75 -0
  150. package/dist/capture/comparison-reference.mjs.map +1 -0
  151. package/dist/capture/consumer/capture-handler.d.mts +56 -0
  152. package/dist/capture/consumer/capture-handler.mjs +117 -0
  153. package/dist/capture/consumer/capture-handler.mjs.map +1 -0
  154. package/dist/capture/consumer/idle.d.mts +15 -0
  155. package/dist/capture/consumer/idle.mjs +24 -0
  156. package/dist/capture/consumer/idle.mjs.map +1 -0
  157. package/dist/capture/consumer/index.d.mts +11 -0
  158. package/dist/capture/consumer/index.mjs +138 -0
  159. package/dist/capture/consumer/index.mjs.map +1 -0
  160. package/dist/capture/consumer/loop.mjs +109 -0
  161. package/dist/capture/consumer/loop.mjs.map +1 -0
  162. package/dist/capture/consumer/metrics.d.mts +39 -0
  163. package/dist/capture/consumer/metrics.mjs +37 -0
  164. package/dist/capture/consumer/metrics.mjs.map +1 -0
  165. package/dist/capture/consumer/retry.d.mts +31 -0
  166. package/dist/capture/consumer/retry.mjs +33 -0
  167. package/dist/capture/consumer/retry.mjs.map +1 -0
  168. package/dist/capture/consumer/run-job.mjs +194 -0
  169. package/dist/capture/consumer/run-job.mjs.map +1 -0
  170. package/dist/capture/consumer/startup.mjs +37 -0
  171. package/dist/capture/consumer/startup.mjs.map +1 -0
  172. package/dist/capture/consumer/types.d.mts +95 -0
  173. package/dist/capture/crash-failure.d.mts +23 -0
  174. package/dist/capture/crash-failure.mjs +69 -0
  175. package/dist/capture/crash-failure.mjs.map +1 -0
  176. package/dist/capture/db-queue/cadence.d.mts +30 -0
  177. package/dist/capture/db-queue/cadence.mjs +37 -0
  178. package/dist/capture/db-queue/cadence.mjs.map +1 -0
  179. package/dist/capture/db-queue/claim.d.mts +7 -0
  180. package/dist/capture/db-queue/claim.mjs +148 -0
  181. package/dist/capture/db-queue/claim.mjs.map +1 -0
  182. package/dist/capture/db-queue/clock.mjs +11 -0
  183. package/dist/capture/db-queue/clock.mjs.map +1 -0
  184. package/dist/capture/db-queue/codec.mjs +75 -0
  185. package/dist/capture/db-queue/codec.mjs.map +1 -0
  186. package/dist/capture/db-queue/index.d.mts +18 -0
  187. package/dist/capture/db-queue/index.mjs +89 -0
  188. package/dist/capture/db-queue/index.mjs.map +1 -0
  189. package/dist/capture/db-queue/keeper.d.mts +34 -0
  190. package/dist/capture/db-queue/keeper.mjs +91 -0
  191. package/dist/capture/db-queue/keeper.mjs.map +1 -0
  192. package/dist/capture/db-queue/lease.mjs +45 -0
  193. package/dist/capture/db-queue/lease.mjs.map +1 -0
  194. package/dist/capture/db-queue/lifecycle.mjs +35 -0
  195. package/dist/capture/db-queue/lifecycle.mjs.map +1 -0
  196. package/dist/capture/db-queue/operations.mjs +93 -0
  197. package/dist/capture/db-queue/operations.mjs.map +1 -0
  198. package/dist/capture/db-queue/poll-loop.d.mts +28 -0
  199. package/dist/capture/db-queue/poll-loop.mjs +88 -0
  200. package/dist/capture/db-queue/poll-loop.mjs.map +1 -0
  201. package/dist/capture/db-queue/poll.d.mts +8 -0
  202. package/dist/capture/db-queue/poll.mjs +68 -0
  203. package/dist/capture/db-queue/poll.mjs.map +1 -0
  204. package/dist/capture/db-queue/timers.d.mts +10 -0
  205. package/dist/capture/db-queue/timers.mjs +36 -0
  206. package/dist/capture/db-queue/timers.mjs.map +1 -0
  207. package/dist/capture/db-queue/types.d.mts +81 -0
  208. package/dist/capture/db-queue/types.mjs +11 -0
  209. package/dist/capture/db-queue/types.mjs.map +1 -0
  210. package/dist/capture/db-queue/wake.mjs +47 -0
  211. package/dist/capture/db-queue/wake.mjs.map +1 -0
  212. package/dist/capture/design.mjs +38 -0
  213. package/dist/capture/design.mjs.map +1 -0
  214. package/dist/capture/diff-record.mjs +24 -5
  215. package/dist/capture/diff-record.mjs.map +1 -1
  216. package/dist/capture/dispatch.d.mts +13 -10
  217. package/dist/capture/dispatch.mjs +102 -111
  218. package/dist/capture/dispatch.mjs.map +1 -1
  219. package/dist/capture/enrich.mjs +59 -0
  220. package/dist/capture/enrich.mjs.map +1 -0
  221. package/dist/capture/failure-sets.mjs +21 -0
  222. package/dist/capture/failure-sets.mjs.map +1 -0
  223. package/dist/capture/fingerprint.d.mts +40 -0
  224. package/dist/capture/fingerprint.mjs +82 -0
  225. package/dist/capture/fingerprint.mjs.map +1 -0
  226. package/dist/capture/forked/channel.d.mts +23 -0
  227. package/dist/capture/forked/child.d.mts +32 -0
  228. package/dist/capture/forked/child.mjs +201 -0
  229. package/dist/capture/forked/child.mjs.map +1 -0
  230. package/dist/capture/forked/crash-tracker.mjs +22 -0
  231. package/dist/capture/forked/crash-tracker.mjs.map +1 -0
  232. package/dist/capture/forked/decision.d.mts +24 -0
  233. package/dist/capture/forked/decision.mjs +26 -0
  234. package/dist/capture/forked/decision.mjs.map +1 -0
  235. package/dist/capture/forked/exit.mjs +11 -0
  236. package/dist/capture/forked/exit.mjs.map +1 -0
  237. package/dist/capture/forked/fork.d.mts +29 -0
  238. package/dist/capture/forked/fork.mjs +92 -0
  239. package/dist/capture/forked/fork.mjs.map +1 -0
  240. package/dist/capture/forked/forked-capture.d.mts +38 -0
  241. package/dist/capture/forked/forked-capture.mjs +239 -0
  242. package/dist/capture/forked/forked-capture.mjs.map +1 -0
  243. package/dist/capture/forked/lease-book.mjs +45 -0
  244. package/dist/capture/forked/lease-book.mjs.map +1 -0
  245. package/dist/capture/forked/protocol.d.mts +36 -0
  246. package/dist/capture/forked/protocol.mjs +24 -0
  247. package/dist/capture/forked/protocol.mjs.map +1 -0
  248. package/dist/capture/forked/runs.mjs +74 -0
  249. package/dist/capture/forked/runs.mjs.map +1 -0
  250. package/dist/capture/guards.mjs +1 -2
  251. package/dist/capture/ignore-regions.d.mts +36 -0
  252. package/dist/capture/ignore-regions.mjs +65 -0
  253. package/dist/capture/ignore-regions.mjs.map +1 -0
  254. package/dist/capture/isolate.mjs +140 -0
  255. package/dist/capture/isolate.mjs.map +1 -0
  256. package/dist/capture/job-lease.d.mts +26 -0
  257. package/dist/capture/job-lease.mjs +56 -0
  258. package/dist/capture/job-lease.mjs.map +1 -0
  259. package/dist/capture/matrix.mjs +298 -0
  260. package/dist/capture/matrix.mjs.map +1 -0
  261. package/dist/capture/modes.d.mts +23 -0
  262. package/dist/capture/modes.mjs +106 -0
  263. package/dist/capture/modes.mjs.map +1 -0
  264. package/dist/capture/orchestrator.d.mts +37 -7
  265. package/dist/capture/orchestrator.mjs +120 -110
  266. package/dist/capture/orchestrator.mjs.map +1 -1
  267. package/dist/capture/pipeline.d.mts +9 -4
  268. package/dist/capture/pipeline.mjs +284 -48
  269. package/dist/capture/pipeline.mjs.map +1 -1
  270. package/dist/capture/progress.d.mts +9 -0
  271. package/dist/capture/progress.mjs +27 -0
  272. package/dist/capture/progress.mjs.map +1 -0
  273. package/dist/capture/publish/accept.d.mts +37 -0
  274. package/dist/capture/publish/accept.mjs +27 -0
  275. package/dist/capture/publish/accept.mjs.map +1 -0
  276. package/dist/capture/publish/complete.d.mts +44 -0
  277. package/dist/capture/publish/complete.mjs +63 -0
  278. package/dist/capture/publish/complete.mjs.map +1 -0
  279. package/dist/capture/publish/extract.d.mts +18 -0
  280. package/dist/capture/publish/extract.mjs +120 -0
  281. package/dist/capture/publish/extract.mjs.map +1 -0
  282. package/dist/capture/publish/handler.d.mts +26 -0
  283. package/dist/capture/publish/handler.mjs +61 -0
  284. package/dist/capture/publish/handler.mjs.map +1 -0
  285. package/dist/capture/publish/index.d.mts +11 -0
  286. package/dist/capture/publish/invalid-zip.d.mts +8 -0
  287. package/dist/capture/publish/invalid-zip.mjs +11 -0
  288. package/dist/capture/publish/invalid-zip.mjs.map +1 -0
  289. package/dist/capture/publish/limits.d.mts +35 -0
  290. package/dist/capture/publish/limits.mjs +38 -0
  291. package/dist/capture/publish/limits.mjs.map +1 -0
  292. package/dist/capture/publish/published.d.mts +13 -0
  293. package/dist/capture/publish/published.mjs +17 -0
  294. package/dist/capture/publish/published.mjs.map +1 -0
  295. package/dist/capture/publish/run.d.mts +31 -0
  296. package/dist/capture/publish/run.mjs +29 -0
  297. package/dist/capture/publish/run.mjs.map +1 -0
  298. package/dist/capture/rediff.d.mts +1 -3
  299. package/dist/capture/rediff.mjs +23 -9
  300. package/dist/capture/rediff.mjs.map +1 -1
  301. package/dist/capture/render.mjs +52 -0
  302. package/dist/capture/render.mjs.map +1 -0
  303. package/dist/capture/scratch.d.mts +42 -0
  304. package/dist/capture/scratch.mjs +136 -0
  305. package/dist/capture/scratch.mjs.map +1 -0
  306. package/dist/capture/shard-plan.mjs +127 -0
  307. package/dist/capture/shard-plan.mjs.map +1 -0
  308. package/dist/capture/shards/context.mjs +51 -0
  309. package/dist/capture/shards/context.mjs.map +1 -0
  310. package/dist/capture/shards/execute.mjs +175 -0
  311. package/dist/capture/shards/execute.mjs.map +1 -0
  312. package/dist/capture/shards/fan-out.mjs +125 -0
  313. package/dist/capture/shards/fan-out.mjs.map +1 -0
  314. package/dist/capture/shards/history.mjs +34 -0
  315. package/dist/capture/shards/history.mjs.map +1 -0
  316. package/dist/capture/shards/reduce.mjs +96 -0
  317. package/dist/capture/shards/reduce.mjs.map +1 -0
  318. package/dist/capture/sizing.d.mts +0 -2
  319. package/dist/capture/sizing.mjs +2 -13
  320. package/dist/capture/sizing.mjs.map +1 -1
  321. package/dist/capture/skip-checks.mjs +3 -67
  322. package/dist/capture/skip-checks.mjs.map +1 -1
  323. package/dist/capture/statics.d.mts +7 -10
  324. package/dist/capture/statics.mjs +26 -74
  325. package/dist/capture/statics.mjs.map +1 -1
  326. package/dist/capture/storybook-urls.d.mts +38 -0
  327. package/dist/capture/storybook-urls.mjs +41 -0
  328. package/dist/capture/storybook-urls.mjs.map +1 -0
  329. package/dist/capture/storybook.d.mts +12 -6
  330. package/dist/capture/storybook.mjs +63 -19
  331. package/dist/capture/storybook.mjs.map +1 -1
  332. package/dist/capture/target.mjs +20 -0
  333. package/dist/capture/target.mjs.map +1 -0
  334. package/dist/capture/targets.d.mts +23 -0
  335. package/dist/capture/targets.mjs +31 -0
  336. package/dist/capture/targets.mjs.map +1 -0
  337. package/dist/capture/tests.d.mts +35 -0
  338. package/dist/capture/tests.mjs +136 -0
  339. package/dist/capture/tests.mjs.map +1 -0
  340. package/dist/capture/token-chunks.mjs +91 -0
  341. package/dist/capture/token-chunks.mjs.map +1 -0
  342. package/dist/capture.d.mts +63 -7
  343. package/dist/capture.mjs +51 -3
  344. package/dist/config.d.mts +136 -14
  345. package/dist/config.mjs +14 -8
  346. package/dist/config.mjs.map +1 -1
  347. package/dist/diff.d.mts +7 -3
  348. package/dist/diff.mjs +15 -3
  349. package/dist/diff.mjs.map +1 -0
  350. package/dist/index.d.mts +3 -3
  351. package/dist/index.mjs +2 -2
  352. package/dist/insights/budget.d.mts +18 -0
  353. package/dist/insights/budget.mjs +32 -0
  354. package/dist/insights/budget.mjs.map +1 -0
  355. package/dist/insights/envelope.d.mts +57 -0
  356. package/dist/insights/envelope.mjs +59 -0
  357. package/dist/insights/envelope.mjs.map +1 -0
  358. package/dist/insights/evidence-health.d.mts +30 -0
  359. package/dist/insights/evidence-health.mjs +27 -0
  360. package/dist/insights/evidence-health.mjs.map +1 -0
  361. package/dist/insights/evidence.d.mts +53 -0
  362. package/dist/insights/evidence.mjs +63 -0
  363. package/dist/insights/evidence.mjs.map +1 -0
  364. package/dist/insights/hash.d.mts +16 -0
  365. package/dist/insights/hash.mjs +20 -0
  366. package/dist/insights/hash.mjs.map +1 -0
  367. package/dist/insights/limits.d.mts +22 -0
  368. package/dist/insights/limits.mjs +23 -0
  369. package/dist/insights/limits.mjs.map +1 -0
  370. package/dist/insights/prompts/v1.d.mts +11 -0
  371. package/dist/insights/prompts/v1.mjs +33 -0
  372. package/dist/insights/prompts/v1.mjs.map +1 -0
  373. package/dist/insights/redact.d.mts +7 -0
  374. package/dist/insights/redact.mjs +23 -0
  375. package/dist/insights/redact.mjs.map +1 -0
  376. package/dist/insights/text.mjs +43 -0
  377. package/dist/insights/text.mjs.map +1 -0
  378. package/dist/insights.d.mts +11 -0
  379. package/dist/insights.mjs +12 -0
  380. package/dist/logger.d.mts +0 -2
  381. package/dist/logger.mjs +18 -3
  382. package/dist/logger.mjs.map +1 -1
  383. package/dist/models/ai-budget-alert.d.mts +21 -0
  384. package/dist/models/ai-budget-alert.mjs +37 -0
  385. package/dist/models/ai-budget-alert.mjs.map +1 -0
  386. package/dist/models/ai-usage.d.mts +38 -0
  387. package/dist/models/ai-usage.mjs +60 -0
  388. package/dist/models/ai-usage.mjs.map +1 -0
  389. package/dist/models/baseline-approval.d.mts +18 -0
  390. package/dist/models/baseline-approval.mjs +41 -0
  391. package/dist/models/baseline-approval.mjs.map +1 -0
  392. package/dist/models/baseline-archive.d.mts +82 -0
  393. package/dist/models/baseline-archive.mjs +105 -0
  394. package/dist/models/baseline-archive.mjs.map +1 -0
  395. package/dist/models/baseline-atomic.mjs +66 -0
  396. package/dist/models/baseline-atomic.mjs.map +1 -0
  397. package/dist/models/baseline-conflict.d.mts +6 -0
  398. package/dist/models/baseline-conflict.mjs +9 -0
  399. package/dist/models/baseline-conflict.mjs.map +1 -0
  400. package/dist/models/baseline-export.d.mts +17 -0
  401. package/dist/models/baseline-export.mjs +135 -0
  402. package/dist/models/baseline-export.mjs.map +1 -0
  403. package/dist/models/baseline-import.mjs +117 -0
  404. package/dist/models/baseline-import.mjs.map +1 -0
  405. package/dist/models/baseline-origin.d.mts +16 -0
  406. package/dist/models/baseline-origin.mjs +35 -0
  407. package/dist/models/baseline-origin.mjs.map +1 -0
  408. package/dist/models/baseline-status.d.mts +0 -2
  409. package/dist/models/baseline-status.mjs +1 -2
  410. package/dist/models/baseline-transfer.d.mts +48 -0
  411. package/dist/models/baseline-transfer.mjs +149 -0
  412. package/dist/models/baseline-transfer.mjs.map +1 -0
  413. package/dist/models/baseline.d.mts +37 -7
  414. package/dist/models/baseline.mjs +152 -28
  415. package/dist/models/baseline.mjs.map +1 -1
  416. package/dist/models/build-component.d.mts +26 -0
  417. package/dist/models/build-component.mjs +54 -0
  418. package/dist/models/build-component.mjs.map +1 -0
  419. package/dist/models/build-review-status.d.mts +27 -0
  420. package/dist/models/build-review-status.mjs +36 -0
  421. package/dist/models/build-review-status.mjs.map +1 -0
  422. package/dist/models/build-review.d.mts +26 -0
  423. package/dist/models/build-review.mjs +53 -0
  424. package/dist/models/build-review.mjs.map +1 -0
  425. package/dist/models/build.d.mts +25 -5
  426. package/dist/models/build.mjs +53 -15
  427. package/dist/models/build.mjs.map +1 -1
  428. package/dist/models/capture-attempt.d.mts +7 -3
  429. package/dist/models/capture-attempt.mjs +30 -6
  430. package/dist/models/capture-attempt.mjs.map +1 -1
  431. package/dist/models/capture-job.d.mts +104 -0
  432. package/dist/models/capture-job.mjs +287 -0
  433. package/dist/models/capture-job.mjs.map +1 -0
  434. package/dist/models/capture-log.d.mts +1 -3
  435. package/dist/models/capture-log.mjs +5 -5
  436. package/dist/models/capture-log.mjs.map +1 -1
  437. package/dist/models/capture-shard.d.mts +60 -0
  438. package/dist/models/capture-shard.mjs +189 -0
  439. package/dist/models/capture-shard.mjs.map +1 -0
  440. package/dist/models/changeset.d.mts +46 -0
  441. package/dist/models/changeset.mjs +80 -0
  442. package/dist/models/changeset.mjs.map +1 -0
  443. package/dist/models/columns.mjs +18 -0
  444. package/dist/models/columns.mjs.map +1 -0
  445. package/dist/models/comment.d.mts +4 -3
  446. package/dist/models/comment.mjs +8 -6
  447. package/dist/models/comment.mjs.map +1 -1
  448. package/dist/models/component.d.mts +41 -0
  449. package/dist/models/component.mjs +59 -0
  450. package/dist/models/component.mjs.map +1 -0
  451. package/dist/models/connections.d.mts +39 -0
  452. package/dist/models/connections.mjs +111 -0
  453. package/dist/models/connections.mjs.map +1 -0
  454. package/dist/models/content-ref.d.mts +1 -3
  455. package/dist/models/content-ref.mjs +2 -3
  456. package/dist/models/content-ref.mjs.map +1 -1
  457. package/dist/models/credentials.d.mts +0 -2
  458. package/dist/models/credentials.mjs +13 -4
  459. package/dist/models/credentials.mjs.map +1 -1
  460. package/dist/models/design-credentials.d.mts +39 -0
  461. package/dist/models/design-credentials.mjs +85 -0
  462. package/dist/models/design-credentials.mjs.map +1 -0
  463. package/dist/models/design-health.d.mts +14 -0
  464. package/dist/models/design-health.mjs +24 -0
  465. package/dist/models/design-health.mjs.map +1 -0
  466. package/dist/models/design-nodes.d.mts +81 -0
  467. package/dist/models/design-nodes.mjs +221 -0
  468. package/dist/models/design-nodes.mjs.map +1 -0
  469. package/dist/models/design-renders.mjs +73 -0
  470. package/dist/models/design-renders.mjs.map +1 -0
  471. package/dist/models/design-resolve.d.mts +27 -0
  472. package/dist/models/design-resolve.mjs +40 -0
  473. package/dist/models/design-resolve.mjs.map +1 -0
  474. package/dist/models/design.d.mts +37 -0
  475. package/dist/models/design.mjs +89 -0
  476. package/dist/models/design.mjs.map +1 -0
  477. package/dist/models/inherited-ratio.d.mts +19 -0
  478. package/dist/models/inherited-ratio.mjs +21 -0
  479. package/dist/models/inherited-ratio.mjs.map +1 -0
  480. package/dist/models/insight.d.mts +68 -0
  481. package/dist/models/insight.mjs +161 -0
  482. package/dist/models/insight.mjs.map +1 -0
  483. package/dist/models/label.d.mts +4 -4
  484. package/dist/models/label.mjs +26 -12
  485. package/dist/models/label.mjs.map +1 -1
  486. package/dist/models/member.d.mts +1 -3
  487. package/dist/models/member.mjs +6 -6
  488. package/dist/models/member.mjs.map +1 -1
  489. package/dist/models/notification-channel.d.mts +37 -5
  490. package/dist/models/notification-channel.mjs +89 -11
  491. package/dist/models/notification-channel.mjs.map +1 -1
  492. package/dist/models/notification-subscription.d.mts +1 -4
  493. package/dist/models/notification-subscription.mjs +8 -11
  494. package/dist/models/notification-subscription.mjs.map +1 -1
  495. package/dist/models/project-group-mapping.d.mts +6 -3
  496. package/dist/models/project-group-mapping.mjs +15 -6
  497. package/dist/models/project-group-mapping.mjs.map +1 -1
  498. package/dist/models/project.d.mts +9 -3
  499. package/dist/models/project.mjs +10 -6
  500. package/dist/models/project.mjs.map +1 -1
  501. package/dist/models/public-origin.d.mts +61 -0
  502. package/dist/models/public-origin.mjs +113 -0
  503. package/dist/models/public-origin.mjs.map +1 -0
  504. package/dist/models/review-switch-off.d.mts +12 -0
  505. package/dist/models/review-switch-off.mjs +25 -0
  506. package/dist/models/review-switch-off.mjs.map +1 -0
  507. package/dist/models/site-settings.d.mts +21 -0
  508. package/dist/models/site-settings.mjs +47 -0
  509. package/dist/models/site-settings.mjs.map +1 -0
  510. package/dist/models/snapshot.d.mts +39 -3
  511. package/dist/models/snapshot.mjs +54 -5
  512. package/dist/models/snapshot.mjs.map +1 -1
  513. package/dist/models/token.d.mts +13 -3
  514. package/dist/models/token.mjs +24 -7
  515. package/dist/models/token.mjs.map +1 -1
  516. package/dist/models/usage.d.mts +74 -0
  517. package/dist/models/usage.mjs +148 -0
  518. package/dist/models/usage.mjs.map +1 -0
  519. package/dist/models/user.d.mts +1 -3
  520. package/dist/models/user.mjs +5 -5
  521. package/dist/models/user.mjs.map +1 -1
  522. package/dist/models/webhook.d.mts +15 -3
  523. package/dist/models/webhook.mjs +34 -6
  524. package/dist/models/webhook.mjs.map +1 -1
  525. package/dist/models.d.mts +30 -4
  526. package/dist/models.mjs +31 -6
  527. package/dist/options.d.mts +16 -0
  528. package/dist/options.mjs +88 -0
  529. package/dist/options.mjs.map +1 -0
  530. package/dist/paths.d.mts +21 -6
  531. package/dist/paths.mjs +57 -12
  532. package/dist/paths.mjs.map +1 -1
  533. package/dist/retention/ai.d.mts +16 -0
  534. package/dist/retention/ai.mjs +22 -0
  535. package/dist/retention/ai.mjs.map +1 -0
  536. package/dist/retention/pending.mjs +46 -0
  537. package/dist/retention/pending.mjs.map +1 -0
  538. package/dist/retention/published-gzip-sweep.mjs +28 -0
  539. package/dist/retention/published-gzip-sweep.mjs.map +1 -0
  540. package/dist/retention/screenshot-sweep.d.mts +49 -0
  541. package/dist/retention/screenshot-sweep.mjs +127 -0
  542. package/dist/retention/screenshot-sweep.mjs.map +1 -0
  543. package/dist/retention.d.mts +16 -4
  544. package/dist/retention.mjs +76 -16
  545. package/dist/retention.mjs.map +1 -1
  546. package/dist/schema/baseline.d.mts +8 -2
  547. package/dist/schema/build-component.d.mts +23 -0
  548. package/dist/schema/build-review.d.mts +15 -0
  549. package/dist/schema/build.d.mts +7 -2
  550. package/dist/schema/capture-attempt.d.mts +0 -2
  551. package/dist/schema/capture-job.d.mts +58 -0
  552. package/dist/schema/capture-shard.d.mts +33 -0
  553. package/dist/schema/comment.d.mts +2 -2
  554. package/dist/schema/connection.d.mts +22 -0
  555. package/dist/schema/content-ref.d.mts +0 -2
  556. package/dist/schema/design.d.mts +56 -0
  557. package/dist/schema/insight.d.mts +52 -0
  558. package/dist/schema/label.d.mts +0 -2
  559. package/dist/schema/member.d.mts +0 -2
  560. package/dist/schema/notification.d.mts +4 -2
  561. package/dist/schema/project-group-mapping.d.mts +0 -2
  562. package/dist/schema/project.d.mts +21 -2
  563. package/dist/schema/site-setting.d.mts +19 -0
  564. package/dist/schema/snapshot.d.mts +17 -2
  565. package/dist/schema/token.d.mts +0 -2
  566. package/dist/schema/usage.d.mts +28 -0
  567. package/dist/schema/user.d.mts +0 -2
  568. package/dist/schema/webhook.d.mts +0 -2
  569. package/dist/schema.d.mts +12 -6
  570. package/dist/serverless.d.mts +19 -0
  571. package/dist/serverless.mjs +36 -0
  572. package/dist/serverless.mjs.map +1 -0
  573. package/dist/test-helpers/build-reference-contract.d.mts +14 -0
  574. package/dist/test-helpers/build-reference-contract.mjs +66 -0
  575. package/dist/test-helpers/build-reference-contract.mjs.map +1 -0
  576. package/dist/test-helpers/create-project.d.mts +1 -3
  577. package/dist/test-helpers/create-project.mjs +1 -2
  578. package/dist/test-helpers/database-contract.d.mts +0 -2
  579. package/dist/test-helpers/database-contract.mjs +37 -2
  580. package/dist/test-helpers/database-contract.mjs.map +1 -1
  581. package/dist/test-helpers/deferred.d.mts +37 -0
  582. package/dist/test-helpers/deferred.mjs +60 -0
  583. package/dist/test-helpers/deferred.mjs.map +1 -0
  584. package/dist/test-helpers/design-contract.d.mts +19 -0
  585. package/dist/test-helpers/design-contract.mjs +39 -0
  586. package/dist/test-helpers/design-contract.mjs.map +1 -0
  587. package/dist/test-helpers/fake-adapters.d.mts +0 -2
  588. package/dist/test-helpers/fake-adapters.mjs +1 -2
  589. package/dist/test-helpers/fake-database.d.mts +0 -2
  590. package/dist/test-helpers/fake-database.mjs +21 -4
  591. package/dist/test-helpers/fake-database.mjs.map +1 -1
  592. package/dist/test-helpers/fake-design.d.mts +41 -0
  593. package/dist/test-helpers/fake-design.mjs +113 -0
  594. package/dist/test-helpers/fake-design.mjs.map +1 -0
  595. package/dist/test-helpers/fake-notifier.d.mts +19 -0
  596. package/dist/test-helpers/fake-notifier.mjs +43 -0
  597. package/dist/test-helpers/fake-notifier.mjs.map +1 -0
  598. package/dist/test-helpers/fake-storage.d.mts +0 -2
  599. package/dist/test-helpers/fake-storage.mjs +1 -2
  600. package/dist/test-helpers/fake-tables.mjs +260 -69
  601. package/dist/test-helpers/fake-tables.mjs.map +1 -1
  602. package/dist/test-helpers/manual-clock.d.mts +25 -0
  603. package/dist/test-helpers/manual-clock.mjs +55 -0
  604. package/dist/test-helpers/manual-clock.mjs.map +1 -0
  605. package/dist/test-helpers/notifier-contract.d.mts +7 -3
  606. package/dist/test-helpers/notifier-contract.mjs +33 -5
  607. package/dist/test-helpers/notifier-contract.mjs.map +1 -1
  608. package/dist/test-helpers/queue-contract.d.mts +16 -4
  609. package/dist/test-helpers/queue-contract.mjs +196 -3
  610. package/dist/test-helpers/queue-contract.mjs.map +1 -1
  611. package/dist/test-helpers/queue-multi-instance.d.mts +18 -0
  612. package/dist/test-helpers/queue-multi-instance.mjs +205 -0
  613. package/dist/test-helpers/queue-multi-instance.mjs.map +1 -0
  614. package/dist/test-helpers/runner-contract.d.mts +0 -2
  615. package/dist/test-helpers/runner-contract.mjs +12 -2
  616. package/dist/test-helpers/runner-contract.mjs.map +1 -1
  617. package/dist/test-helpers/sql-chunks.mjs +25 -2
  618. package/dist/test-helpers/sql-chunks.mjs.map +1 -1
  619. package/dist/test-helpers/storage-contract.d.mts +0 -2
  620. package/dist/test-helpers/storage-contract.mjs +2 -3
  621. package/dist/test-helpers.d.mts +10 -4
  622. package/dist/test-helpers.mjs +9 -3
  623. package/dist/tracing.mjs +36 -6
  624. package/dist/tracing.mjs.map +1 -1
  625. package/dist/types.d.mts +14 -5
  626. package/dist/types.mjs +22 -6
  627. package/dist/types.mjs.map +1 -1
  628. package/dist/urls.d.mts +4 -2
  629. package/dist/urls.mjs +12 -4
  630. package/dist/urls.mjs.map +1 -1
  631. package/dist/utils/adaptive-concurrency.d.mts +64 -0
  632. package/dist/utils/adaptive-concurrency.mjs +148 -0
  633. package/dist/utils/adaptive-concurrency.mjs.map +1 -0
  634. package/dist/utils/connect-retry.d.mts +26 -0
  635. package/dist/utils/connect-retry.mjs +42 -0
  636. package/dist/utils/connect-retry.mjs.map +1 -0
  637. package/dist/utils/encrypt.d.mts +0 -2
  638. package/dist/utils/encrypt.mjs +1 -2
  639. package/dist/utils/environment.d.mts +17 -0
  640. package/dist/utils/environment.mjs +58 -0
  641. package/dist/utils/environment.mjs.map +1 -0
  642. package/dist/utils/hash.d.mts +0 -2
  643. package/dist/utils/hash.mjs +1 -2
  644. package/dist/utils/http.d.mts +29 -3
  645. package/dist/utils/http.mjs +100 -19
  646. package/dist/utils/http.mjs.map +1 -1
  647. package/dist/utils/map-adaptive.d.mts +13 -0
  648. package/dist/utils/map-adaptive.mjs +71 -0
  649. package/dist/utils/map-adaptive.mjs.map +1 -0
  650. package/dist/utils/map-limit.d.mts +8 -0
  651. package/dist/utils/map-limit.mjs +31 -0
  652. package/dist/utils/map-limit.mjs.map +1 -0
  653. package/dist/utils/mime.d.mts +0 -2
  654. package/dist/utils/mime.mjs +1 -2
  655. package/dist/utils/page-limiter.d.mts +35 -0
  656. package/dist/utils/page-limiter.mjs +55 -0
  657. package/dist/utils/page-limiter.mjs.map +1 -0
  658. package/dist/utils/path-security.d.mts +0 -2
  659. package/dist/utils/path-security.mjs +2 -4
  660. package/dist/utils/path-security.mjs.map +1 -1
  661. package/dist/utils/public-origin.d.mts +18 -0
  662. package/dist/utils/public-origin.mjs +74 -0
  663. package/dist/utils/public-origin.mjs.map +1 -0
  664. package/dist/utils/redact.d.mts +0 -2
  665. package/dist/utils/redact.mjs +4 -2
  666. package/dist/utils/redact.mjs.map +1 -1
  667. package/dist/utils/remote-environment.d.mts +76 -0
  668. package/dist/utils/remote-environment.mjs +163 -0
  669. package/dist/utils/remote-environment.mjs.map +1 -0
  670. package/dist/utils/resources.d.mts +62 -0
  671. package/dist/utils/resources.mjs +121 -0
  672. package/dist/utils/resources.mjs.map +1 -0
  673. package/dist/utils/screenshot-store.d.mts +13 -0
  674. package/dist/utils/screenshot-store.mjs +40 -0
  675. package/dist/utils/screenshot-store.mjs.map +1 -0
  676. package/dist/utils/session-gate.d.mts +23 -0
  677. package/dist/utils/session-gate.mjs +69 -0
  678. package/dist/utils/session-gate.mjs.map +1 -0
  679. package/dist/utils/storybook-token.d.mts +89 -0
  680. package/dist/utils/storybook-token.mjs +135 -0
  681. package/dist/utils/storybook-token.mjs.map +1 -0
  682. package/dist/utils/storybook-url.d.mts +13 -0
  683. package/dist/utils/storybook-url.mjs +20 -0
  684. package/dist/utils/storybook-url.mjs.map +1 -0
  685. package/dist/utils/timing.d.mts +0 -2
  686. package/dist/utils/timing.mjs +2 -3
  687. package/dist/utils/timing.mjs.map +1 -1
  688. package/dist/utils/ulid.d.mts +0 -2
  689. package/dist/utils/ulid.mjs +1 -2
  690. package/dist/utils/zip-read.mjs +128 -0
  691. package/dist/utils/zip-read.mjs.map +1 -0
  692. package/dist/utils/zip-write.d.mts +16 -0
  693. package/dist/utils/zip-write.mjs +187 -0
  694. package/dist/utils/zip-write.mjs.map +1 -0
  695. package/dist/utils.d.mts +17 -3
  696. package/dist/utils.mjs +18 -4
  697. package/package.json +35 -26
  698. package/dist/adapter/capture-runner.mjs.map +0 -1
  699. package/dist/adapter/git-host/comments.d.mts +0 -41
  700. package/dist/adapter/git-host/comments.mjs +0 -54
  701. package/dist/adapter/git-host/comments.mjs.map +0 -1
  702. package/dist/adapter/git-host/helpers.d.mts +0 -13
  703. package/dist/adapter/git-host/helpers.mjs +0 -30
  704. package/dist/adapter/git-host/helpers.mjs.map +0 -1
  705. package/dist/adapter/git-host.d.mts +0 -42
  706. package/dist/capture/queue.d.mts +0 -38
  707. package/dist/capture/queue.mjs +0 -98
  708. package/dist/capture/queue.mjs.map +0 -1
  709. package/dist/capture/recovery.d.mts +0 -21
  710. package/dist/capture/recovery.mjs +0 -83
  711. package/dist/capture/recovery.mjs.map +0 -1
  712. package/dist/capture/status-fanout.mjs +0 -72
  713. package/dist/capture/status-fanout.mjs.map +0 -1
  714. package/dist/diff/engine.d.mts +0 -16
  715. package/dist/diff/engine.mjs +0 -87
  716. package/dist/diff/engine.mjs.map +0 -1
  717. package/dist/diff/options.d.mts +0 -42
  718. package/dist/diff/options.mjs +0 -15
  719. package/dist/diff/options.mjs.map +0 -1
  720. package/dist/models/status-config.d.mts +0 -38
  721. package/dist/models/status-config.mjs +0 -63
  722. package/dist/models/status-config.mjs.map +0 -1
  723. package/dist/schema/status-config.d.mts +0 -16
  724. package/dist/test-helpers/git-contract.d.mts +0 -11
  725. package/dist/test-helpers/git-contract.mjs +0 -34
  726. package/dist/test-helpers/git-contract.mjs.map +0 -1
  727. /package/dist/adapter/{git-host.mjs → oauth.mjs} +0 -0
@@ -0,0 +1,58 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { sha256 } from "./hash.mjs";
5
+ // src/utils/environment.ts
6
+ let fonts;
7
+ /**
8
+ * Digest of the installed system font families (`fc-list`), or undefined where fontconfig is not
9
+ * available. Cached for the process (fonts do not change under a running server), except a failed
10
+ * run such as a timeout under load: that is retried next time rather than reported as "no fonts"
11
+ * for the life of the process.
12
+ */
13
+ async function systemFontsHash() {
14
+ fonts ??= listFonts().then((result) => {
15
+ if (!result.settled) fonts = void 0;
16
+ return result.hash;
17
+ });
18
+ return await fonts;
19
+ }
20
+ async function listFonts() {
21
+ const childProcess = process.getBuiltinModule?.call(process, "node:child_process");
22
+ if (!childProcess) return {
23
+ hash: void 0,
24
+ settled: true
25
+ };
26
+ return await new Promise((resolve) => {
27
+ childProcess.execFile("fc-list", [":", "family"], { timeout: 3e3 }, (error, stdout) => {
28
+ resolve(error ? {
29
+ hash: void 0,
30
+ settled: error.code === "ENOENT"
31
+ } : {
32
+ hash: digestOf(stdout),
33
+ settled: true
34
+ });
35
+ });
36
+ });
37
+ }
38
+ /**
39
+ * The environment a runner captured in: browser build, host platform, device scale factor and a
40
+ * font digest. Runners call this once per render with the version their driver reports.
41
+ */
42
+ async function detectCaptureEnvironment(browserVersion, deviceScaleFactor = 1) {
43
+ const fontsHash = await systemFontsHash();
44
+ return {
45
+ ...browserVersion ? { browserVersion } : {},
46
+ platform: `${process.platform}-${process.arch}`,
47
+ deviceScaleFactor,
48
+ ...fontsHash ? { fontsHash } : {}
49
+ };
50
+ }
51
+ /** Digest of the distinct family names in `fc-list` output; undefined when there are none. */
52
+ function digestOf(stdout) {
53
+ const families = [...new Set(stdout.split("\n").map((line) => line.trim()))].filter(Boolean);
54
+ return families.length > 0 ? sha256(families.toSorted().join("\n")).slice(0, 16) : void 0;
55
+ }
56
+ export { detectCaptureEnvironment, systemFontsHash };
57
+
58
+ //# sourceMappingURL=environment.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment.mjs","names":[],"sources":["../../src/utils/environment.ts"],"sourcesContent":["/** Detects the rendering environment a capture runner reports (see `CaptureEnvironment`). */\nimport type { CaptureEnvironment } from \"../adapters/capture-runner.ts\";\nimport { sha256 } from \"./hash.ts\";\n\ntype ChildProcessModule = typeof import(\"node:child_process\");\n\nlet fonts: Promise<string | undefined> | undefined;\n\n/**\n * Digest of the installed system font families (`fc-list`), or undefined where fontconfig is not\n * available. Cached for the process (fonts do not change under a running server), except a failed\n * run such as a timeout under load: that is retried next time rather than reported as \"no fonts\"\n * for the life of the process.\n */\nexport async function systemFontsHash(): Promise<string | undefined> {\n fonts ??= listFonts().then((result) => {\n if (!result.settled) {\n fonts = undefined;\n }\n return result.hash;\n });\n return await fonts;\n}\n\n/** An `fc-list` outcome; `settled` is false for a failure worth retrying. */\ninterface FontsResult {\n hash: string | undefined;\n settled: boolean;\n}\n\nasync function listFonts(): Promise<FontsResult> {\n const getBuiltin = (process as { getBuiltinModule?: (id: string) => unknown }).getBuiltinModule;\n const childProcess = getBuiltin?.call(process, \"node:child_process\") as\n | ChildProcessModule\n | undefined;\n if (!childProcess) {\n return { hash: undefined, settled: true };\n }\n return await new Promise<FontsResult>((resolve) => {\n childProcess.execFile(\"fc-list\", [\":\", \"family\"], { timeout: 3000 }, (error, stdout) => {\n resolve(\n error\n ? { hash: undefined, settled: (error as { code?: unknown }).code === \"ENOENT\" }\n : { hash: digestOf(stdout), settled: true },\n );\n });\n });\n}\n\n/**\n * The environment a runner captured in: browser build, host platform, device scale factor and a\n * font digest. Runners call this once per render with the version their driver reports.\n */\nexport async function detectCaptureEnvironment(\n browserVersion?: string,\n deviceScaleFactor = 1,\n): Promise<CaptureEnvironment> {\n const fontsHash = await systemFontsHash();\n return {\n ...(browserVersion ? { browserVersion } : {}),\n platform: `${process.platform}-${process.arch}`,\n deviceScaleFactor,\n ...(fontsHash ? { fontsHash } : {}),\n };\n}\n\n/** Digest of the distinct family names in `fc-list` output; undefined when there are none. */\nfunction digestOf(stdout: string): string | undefined {\n const families = [...new Set(stdout.split(\"\\n\").map((line) => line.trim()))].filter(Boolean);\n return families.length > 0 ? sha256(families.toSorted().join(\"\\n\")).slice(0, 16) : undefined;\n}\n"],"mappings":";;;;;AAMA,IAAI;;;;;;;AAQJ,eAAsB,kBAA+C;CACnE,UAAU,UAAU,CAAC,CAAC,MAAM,WAAW;EACrC,IAAI,CAAC,OAAO,SACV,QAAQ,KAAA;EAEV,OAAO,OAAO;CAChB,CAAC;CACD,OAAO,MAAM;AACf;AAQA,eAAe,YAAkC;CAE/C,MAAM,eADc,QAA2D,kBAC9C,KAAK,SAAS,oBAAoB;CAGnE,IAAI,CAAC,cACH,OAAO;EAAE,MAAM,KAAA;EAAW,SAAS;CAAK;CAE1C,OAAO,MAAM,IAAI,SAAsB,YAAY;EACjD,aAAa,SAAS,WAAW,CAAC,KAAK,QAAQ,GAAG,EAAE,SAAS,IAAK,IAAI,OAAO,WAAW;GACtF,QACE,QACI;IAAE,MAAM,KAAA;IAAW,SAAU,MAA6B,SAAS;GAAS,IAC5E;IAAE,MAAM,SAAS,MAAM;IAAG,SAAS;GAAK,CAC9C;EACF,CAAC;CACH,CAAC;AACH;;;;;AAMA,eAAsB,yBACpB,gBACA,oBAAoB,GACS;CAC7B,MAAM,YAAY,MAAM,gBAAgB;CACxC,OAAO;EACL,GAAI,iBAAiB,EAAE,eAAe,IAAI,CAAC;EAC3C,UAAU,GAAG,QAAQ,SAAS,GAAG,QAAQ;EACzC;EACA,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;CACnC;AACF;;AAGA,SAAS,SAAS,QAAoC;CACpD,MAAM,WAAW,CAAC,GAAG,IAAI,IAAI,OAAO,MAAM,IAAI,CAAC,CAAC,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,OAAO;CAC3F,OAAO,SAAS,SAAS,IAAI,OAAO,SAAS,SAAS,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,GAAG,EAAE,IAAI,KAAA;AACrF"}
@@ -1,6 +1,5 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
- //#region src/utils/hash.d.ts
4
3
  /** Hash a value with SHA-256, returning lowercase hex. */
5
4
  declare function sha256(value: string): string;
6
5
  /** Sign a value with HMAC-SHA-256, returning lowercase hex. */
@@ -12,6 +11,5 @@ declare function randomToken(prefix: string): {
12
11
  };
13
12
  /** Compare two strings in constant time to avoid timing leaks. */
14
13
  declare function timingSafeEqualString(a: string, b: string): boolean;
15
- //#endregion
16
14
  export { hmacSha256, randomToken, sha256, timingSafeEqualString };
17
15
  //# sourceMappingURL=hash.d.mts.map
@@ -2,7 +2,7 @@ import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
4
  import { createHash, createHmac, randomBytes, timingSafeEqual } from "node:crypto";
5
- //#region src/utils/hash.ts
5
+ // src/utils/hash.ts
6
6
  /** Hash a value with SHA-256, returning lowercase hex. */
7
7
  function sha256(value) {
8
8
  return createHash("sha256").update(value).digest("hex");
@@ -25,7 +25,6 @@ function timingSafeEqualString(a, b) {
25
25
  const bb = Buffer.from(b);
26
26
  return ab.length === bb.length && timingSafeEqual(ab, bb);
27
27
  }
28
- //#endregion
29
28
  export { hmacSha256, randomToken, sha256, timingSafeEqualString };
30
29
 
31
30
  //# sourceMappingURL=hash.mjs.map
@@ -1,7 +1,7 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { Logger } from "../logger.mjs";
4
- //#region src/utils/http.d.ts
4
+ import { Buffer } from "node:buffer";
5
5
  /**
6
6
  * Fetch JSON with timeout, retries, and structured errors.
7
7
  *
@@ -13,6 +13,15 @@ import { Logger } from "../logger.mjs";
13
13
  * @returns The parsed JSON response body.
14
14
  */
15
15
  declare function httpJson<T>(url: string, options?: HttpRequestOptions): Promise<T>;
16
+ /**
17
+ * Download a binary response (images) with the same timeout, retry, and
18
+ * `Retry-After` semantics as {@link httpJson}.
19
+ *
20
+ * @param url - Absolute request URL.
21
+ * @param options - Method, headers, timeout, retries, logger.
22
+ * @returns The raw response bytes.
23
+ */
24
+ declare function httpBytes(url: string, options?: HttpRequestOptions): Promise<Buffer>;
16
25
  /**
17
26
  * POST/PUT plain payloads (e.g. Slack/Teams webhook cards) with the same
18
27
  * timeout, retry, and `Retry-After` semantics as {@link httpJson}.
@@ -22,6 +31,18 @@ declare function httpJson<T>(url: string, options?: HttpRequestOptions): Promise
22
31
  * @returns The raw response body text.
23
32
  */
24
33
  declare function httpText(url: string, options?: HttpTextOptions): Promise<string>;
34
+ /**
35
+ * Send a request and resolve with the response status, without ever reading the response body (it
36
+ * is cancelled on every response). Use it when the answer is only "did it succeed", such as webhook
37
+ * delivery, so a receiver cannot make the process buffer an arbitrarily large body. Retries,
38
+ * timeouts, redaction and tracing are those of {@link httpText}. A non-2xx response throws
39
+ * {@link HttpError} with an empty `body`.
40
+ *
41
+ * @param url - Absolute request URL.
42
+ * @param options - Method, headers, JSON body, timeout, retries, logger.
43
+ * @returns The 2xx response status.
44
+ */
45
+ declare function httpStatus(url: string, options?: HttpTextOptions): Promise<number>;
25
46
  /** Failed HTTP response: status code plus a snippet of the response body. */
26
47
  declare class HttpError extends Error {
27
48
  readonly status: number;
@@ -35,10 +56,14 @@ interface HttpRequestOptions {
35
56
  headers?: Record<string, string>;
36
57
  /** Body serialized as JSON with a `content-type` header. */
37
58
  json?: unknown;
59
+ /** Body serialized as `application/x-www-form-urlencoded` (ignored when `json` is set). */
60
+ form?: Record<string, string>;
38
61
  /** Per-attempt timeout in milliseconds (default 30s). */
39
62
  timeoutMs?: number;
40
63
  /** Total attempts including the first (default 3). */
41
64
  retries?: number;
65
+ /** Aborts the request (and any further retry) when it fires. */
66
+ signal?: AbortSignal;
42
67
  logger?: Logger;
43
68
  }
44
69
  /** Options for {@link httpText}. */
@@ -51,8 +76,9 @@ interface HttpTextOptions {
51
76
  timeoutMs?: number;
52
77
  /** Total attempts including the first (default 3). */
53
78
  retries?: number;
79
+ /** Aborts the request (and any further retry) when it fires. */
80
+ signal?: AbortSignal;
54
81
  logger?: Logger;
55
82
  }
56
- //#endregion
57
- export { HttpError, HttpRequestOptions, HttpTextOptions, httpJson, httpText };
83
+ export { HttpError, HttpRequestOptions, HttpTextOptions, httpBytes, httpJson, httpStatus, httpText };
58
84
  //# sourceMappingURL=http.d.mts.map
@@ -1,8 +1,9 @@
1
1
  import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
- import { injectTraceContext, withSpan } from "../tracing.mjs";
5
- //#region src/utils/http.ts
4
+ import { injectTraceContext, setSpanMessage, spanException, withSpan } from "../tracing.mjs";
5
+ import { Buffer } from "node:buffer";
6
+ // src/utils/http.ts
6
7
  /**
7
8
  * Fetch JSON with timeout, retries, and structured errors.
8
9
  *
@@ -17,6 +18,17 @@ async function httpJson(url, options = {}) {
17
18
  return await requestWithRetry(url, options, options.retries ?? DEFAULT_RETRIES, options.timeoutMs ?? DEFAULT_TIMEOUT_MS, doFetchJson);
18
19
  }
19
20
  /**
21
+ * Download a binary response (images) with the same timeout, retry, and
22
+ * `Retry-After` semantics as {@link httpJson}.
23
+ *
24
+ * @param url - Absolute request URL.
25
+ * @param options - Method, headers, timeout, retries, logger.
26
+ * @returns The raw response bytes.
27
+ */
28
+ async function httpBytes(url, options = {}) {
29
+ return await requestWithRetry(url, options, options.retries ?? DEFAULT_RETRIES, options.timeoutMs ?? DEFAULT_TIMEOUT_MS, doFetchBytes);
30
+ }
31
+ /**
20
32
  * POST/PUT plain payloads (e.g. Slack/Teams webhook cards) with the same
21
33
  * timeout, retry, and `Retry-After` semantics as {@link httpJson}.
22
34
  *
@@ -27,6 +39,20 @@ async function httpJson(url, options = {}) {
27
39
  async function httpText(url, options = {}) {
28
40
  return await requestWithRetry(url, options, options.retries ?? DEFAULT_RETRIES, options.timeoutMs ?? DEFAULT_TIMEOUT_MS, doFetchText);
29
41
  }
42
+ /**
43
+ * Send a request and resolve with the response status, without ever reading the response body (it
44
+ * is cancelled on every response). Use it when the answer is only "did it succeed", such as webhook
45
+ * delivery, so a receiver cannot make the process buffer an arbitrarily large body. Retries,
46
+ * timeouts, redaction and tracing are those of {@link httpText}. A non-2xx response throws
47
+ * {@link HttpError} with an empty `body`.
48
+ *
49
+ * @param url - Absolute request URL.
50
+ * @param options - Method, headers, JSON body, timeout, retries, logger.
51
+ * @returns The 2xx response status.
52
+ */
53
+ async function httpStatus(url, options = {}) {
54
+ return await requestWithRetry(url, options, options.retries ?? DEFAULT_RETRIES, options.timeoutMs ?? DEFAULT_TIMEOUT_MS, doFetchStatus);
55
+ }
30
56
  /** Failed HTTP response: status code plus a snippet of the response body. */
31
57
  var HttpError = class extends Error {
32
58
  status;
@@ -79,25 +105,54 @@ async function requestWithRetry(url, options, maxAttempts, timeoutMs, perform) {
79
105
  for (let attempt = 1;; attempt += 1) try {
80
106
  return await perform(url, options, timeoutMs);
81
107
  } catch (error) {
82
- span.recordException(error instanceof Error ? error : String(error));
83
- if (!shouldRetry(error, attempt, maxAttempts)) throw error;
108
+ describeFailure(span, error, url);
109
+ if (options.signal?.aborted || !shouldRetry(error, attempt, maxAttempts)) throw error;
110
+ span.recordException(spanException(error));
84
111
  span.setAttribute("http.retry.count", attempt);
85
112
  await backoff(error, attempt, url, options.logger);
86
113
  }
87
114
  }, {
88
115
  "http.request.method": options.method ?? "GET",
89
- "url.full": redactUrl(url)
116
+ "url.full": redactUrlForLog(url)
90
117
  });
91
118
  }
92
- /** Strip the query string (tokens, signatures) from a URL for span attributes. */
93
- function redactUrl(url) {
94
- const query = url.indexOf("?");
95
- return query === -1 ? url : url.slice(0, query);
119
+ /**
120
+ * The form of a request URL that may appear in logs and span attributes: its origin
121
+ * (`protocol//host`) and nothing else. The path of an incoming webhook (Slack, Teams) is the secret,
122
+ * a bot token can be the first segment (Telegram), and other URLs carry credentials in the userinfo
123
+ * or the query, so the path, query, fragment and userinfo are dropped by construction. There is no
124
+ * opt-out.
125
+ */
126
+ function redactUrlForLog(url) {
127
+ try {
128
+ const { protocol, host } = new URL(url);
129
+ return `${protocol}//${host}`;
130
+ } catch {
131
+ return "[invalid url]";
132
+ }
133
+ }
134
+ /**
135
+ * What a span may say about a failed request. An HTTP failure carries its status and never the
136
+ * response body, which can echo the request URL in any encoding. Any other failure keeps its
137
+ * message with the request URL replaced by its origin, because native `fetch` repeats the whole
138
+ * URL, userinfo included, when it refuses one. The error a caller catches is not changed.
139
+ */
140
+ function describeFailure(span, error, url) {
141
+ if (error instanceof HttpError) {
142
+ span.setAttribute("http.response.status_code", error.status);
143
+ setSpanMessage(error, `HTTP ${String(error.status)}`);
144
+ } else if (error instanceof Error) setSpanMessage(error, scrubUrl(error.message, url));
145
+ }
146
+ /** `text` with the request URL, as given and as normalized, replaced by its origin. */
147
+ function scrubUrl(text, url) {
148
+ const origin = redactUrlForLog(url);
149
+ const normalized = URL.canParse(url) ? new URL(url).href : url;
150
+ return text.replaceAll(url, origin).replaceAll(normalized, origin);
96
151
  }
97
152
  async function backoff(error, attempt, url, logger) {
98
153
  const delay = retryDelayMs(error, attempt);
99
154
  logger?.warn({
100
- url,
155
+ url: redactUrlForLog(url),
101
156
  attempt,
102
157
  delayMs: delay
103
158
  }, "http request failed, retrying");
@@ -109,6 +164,9 @@ function buildRequest(options) {
109
164
  if (options.json !== void 0) {
110
165
  headers["content-type"] = "application/json";
111
166
  body = JSON.stringify(options.json);
167
+ } else if (options.form) {
168
+ headers["content-type"] = "application/x-www-form-urlencoded";
169
+ body = new URLSearchParams(options.form).toString();
112
170
  }
113
171
  return {
114
172
  headers,
@@ -122,26 +180,49 @@ async function doFetchJson(url, options, timeoutMs) {
122
180
  async function doFetchText(url, options, timeoutMs) {
123
181
  return await (await doFetchResponse(url, options, timeoutMs)).text();
124
182
  }
125
- async function doFetchResponse(url, options, timeoutMs) {
183
+ async function doFetchStatus(url, options, timeoutMs) {
184
+ const response = await doFetchResponse(url, options, timeoutMs, false);
185
+ await discardBody(response);
186
+ return response.status;
187
+ }
188
+ async function doFetchBytes(url, options, timeoutMs) {
189
+ const response = await doFetchResponse(url, options, timeoutMs);
190
+ return Buffer.from(await response.arrayBuffer());
191
+ }
192
+ async function doFetchResponse(url, options, timeoutMs, readErrorBody = true) {
126
193
  const request = buildRequest(options);
127
194
  injectTraceContext(request.headers);
128
195
  options.logger?.debug({
129
- url,
196
+ url: redactUrlForLog(url),
130
197
  method: request.method
131
198
  }, "http request");
132
199
  const response = await fetch(url, {
133
200
  method: request.method,
134
201
  headers: request.headers,
135
202
  body: request.body,
136
- signal: AbortSignal.timeout(timeoutMs)
203
+ signal: options.signal ? AbortSignal.any([options.signal, AbortSignal.timeout(timeoutMs)]) : AbortSignal.timeout(timeoutMs)
137
204
  });
138
- if (!response.ok) {
139
- const text = await response.text();
140
- throw new HttpError(response.status, text.slice(0, MAX_BODY_SNIPPET), response.headers);
141
- }
205
+ if (!response.ok) throw await failureOf(response, readErrorBody);
142
206
  return response;
143
207
  }
144
- //#endregion
145
- export { HttpError, httpJson, httpText };
208
+ /**
209
+ * Cancel an unread body. Cleanup only: a rejected cancel must never replace the request's outcome,
210
+ * or a delivered request would look failed and be retried.
211
+ */
212
+ async function discardBody(response) {
213
+ try {
214
+ await response.body?.cancel();
215
+ } catch {}
216
+ }
217
+ /** The {@link HttpError} for a non-2xx response; the body is read (and capped) only on request. */
218
+ async function failureOf(response, readBody) {
219
+ if (!readBody) {
220
+ await discardBody(response);
221
+ return new HttpError(response.status, "", response.headers);
222
+ }
223
+ const text = await response.text();
224
+ return new HttpError(response.status, text.slice(0, MAX_BODY_SNIPPET), response.headers);
225
+ }
226
+ export { HttpError, httpBytes, httpJson, httpStatus, httpText, redactUrlForLog };
146
227
 
147
228
  //# sourceMappingURL=http.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"http.mjs","names":[],"sources":["../../src/utils/http.ts"],"sourcesContent":["import type { Logger } from \"../logger.ts\";\nimport { injectTraceContext, withSpan } from \"../tracing.ts\";\n\n/**\n * Fetch JSON with timeout, retries, and structured errors.\n *\n * Retries network failures, 429, and 502/503/504 (honoring `Retry-After`);\n * every other status throws {@link HttpError} immediately.\n *\n * @param url - Absolute request URL.\n * @param options - Method, headers, JSON body, timeout, retries, logger.\n * @returns The parsed JSON response body.\n */\nexport async function httpJson<T>(url: string, options: HttpRequestOptions = {}): Promise<T> {\n return await requestWithRetry<T>(\n url,\n options,\n options.retries ?? DEFAULT_RETRIES,\n options.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n doFetchJson,\n );\n}\n\n/**\n * POST/PUT plain payloads (e.g. Slack/Teams webhook cards) with the same\n * timeout, retry, and `Retry-After` semantics as {@link httpJson}.\n *\n * @param url - Absolute request URL.\n * @param options - Method, headers, text body, timeout, retries, logger.\n * @returns The raw response body text.\n */\nexport async function httpText(url: string, options: HttpTextOptions = {}): Promise<string> {\n return await requestWithRetry<string>(\n url,\n options,\n options.retries ?? DEFAULT_RETRIES,\n options.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n doFetchText,\n );\n}\n\n/** Failed HTTP response: status code plus a snippet of the response body. */\nexport class HttpError extends Error {\n readonly status: number;\n readonly body: string;\n readonly headers: Headers;\n\n constructor(status: number, body: string, headers?: Headers) {\n super(`HTTP ${status}: ${body}`);\n this.name = \"HttpError\";\n this.status = status;\n this.body = body;\n this.headers = headers ?? new Headers();\n }\n}\n\n/** Options for {@link httpJson}. */\nexport interface HttpRequestOptions {\n method?: string;\n headers?: Record<string, string>;\n /** Body serialized as JSON with a `content-type` header. */\n json?: unknown;\n /** Per-attempt timeout in milliseconds (default 30s). */\n timeoutMs?: number;\n /** Total attempts including the first (default 3). */\n retries?: number;\n logger?: Logger;\n}\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n/** Options for {@link httpText}. */\nexport interface HttpTextOptions {\n method?: string;\n headers?: Record<string, string>;\n /** Body serialized as JSON with a `content-type` header. */\n json?: unknown;\n /** Per-attempt timeout in milliseconds (default 30s). */\n timeoutMs?: number;\n /** Total attempts including the first (default 3). */\n retries?: number;\n logger?: Logger;\n}\nconst DEFAULT_RETRIES = 3;\nconst MAX_RETRY_AFTER_MS = 30_000;\nconst BACKOFF_BASE_MS = 1000;\nconst MAX_BODY_SNIPPET = 500;\n\n/** Whether a failure is worth retrying: network errors, 429, and 502/503/504. */\nfunction shouldRetry(error: unknown, attempt: number, maxAttempts: number): boolean {\n if (attempt >= maxAttempts) {\n return false;\n }\n if (!(error instanceof HttpError)) {\n return true;\n }\n return error.status === 429 || (error.status >= 502 && error.status <= 504);\n}\n\n/** Parse a Retry-After value (seconds or HTTP-date) into milliseconds. */\nfunction parseRetryAfter(value: string | null): number | null {\n if (!value) {\n return null;\n }\n const seconds = Number(value);\n if (Number.isFinite(seconds) && seconds >= 0) {\n return Math.min(seconds * 1000, MAX_RETRY_AFTER_MS);\n }\n const date = Date.parse(value);\n if (!Number.isNaN(date)) {\n return Math.min(Math.max(date - Date.now(), 0), MAX_RETRY_AFTER_MS);\n }\n return null;\n}\n\n/** Delay before the next attempt: Retry-After when present, else 1s/2s/4s. */\nfunction retryDelayMs(error: unknown, attempt: number): number {\n if (error instanceof HttpError) {\n const retryAfter = parseRetryAfter(error.headers.get(\"retry-after\"));\n if (retryAfter !== null) {\n return retryAfter;\n }\n }\n return Math.min(BACKOFF_BASE_MS * 2 ** (attempt - 1), MAX_RETRY_AFTER_MS);\n}\n\n/** Sleep helper for retry backoff. */\nasync function sleep(ms: number): Promise<void> {\n await new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\n/* oxlint-disable eslint/no-await-in-loop -- retry attempts run sequentially by design */\nasync function requestWithRetry<T>(\n url: string,\n options: HttpRequestOptions,\n maxAttempts: number,\n timeoutMs: number,\n perform: (url: string, options: HttpRequestOptions, timeoutMs: number) => Promise<T>,\n): Promise<T> {\n return await withSpan(\n \"http.client\",\n async (span) => {\n for (let attempt = 1; ; attempt += 1) {\n try {\n return await perform(url, options, timeoutMs);\n } catch (error) {\n span.recordException(error instanceof Error ? error : String(error));\n if (!shouldRetry(error, attempt, maxAttempts)) {\n throw error;\n }\n span.setAttribute(\"http.retry.count\", attempt);\n await backoff(error, attempt, url, options.logger);\n }\n }\n },\n { \"http.request.method\": options.method ?? \"GET\", \"url.full\": redactUrl(url) },\n );\n}\n/* oxlint-enable eslint/no-await-in-loop */\n\n/** Strip the query string (tokens, signatures) from a URL for span attributes. */\nfunction redactUrl(url: string): string {\n const query = url.indexOf(\"?\");\n return query === -1 ? url : url.slice(0, query);\n}\n\nasync function backoff(\n error: unknown,\n attempt: number,\n url: string,\n logger?: Logger,\n): Promise<void> {\n const delay = retryDelayMs(error, attempt);\n logger?.warn({ url, attempt, delayMs: delay }, \"http request failed, retrying\");\n await sleep(delay);\n}\n\nfunction buildRequest(options: HttpRequestOptions): {\n headers: Record<string, string>;\n body: string | undefined;\n method: string;\n} {\n const headers: Record<string, string> = { ...options.headers };\n let body: string | undefined;\n if (options.json !== undefined) {\n headers[\"content-type\"] = \"application/json\";\n body = JSON.stringify(options.json);\n }\n return { headers, body, method: options.method ?? \"GET\" };\n}\n\nasync function doFetchJson<T>(\n url: string,\n options: HttpRequestOptions,\n timeoutMs: number,\n): Promise<T> {\n const response = await doFetchResponse(url, options, timeoutMs);\n return (await response.json()) as T;\n}\n\nasync function doFetchText(\n url: string,\n options: HttpRequestOptions,\n timeoutMs: number,\n): Promise<string> {\n const response = await doFetchResponse(url, options, timeoutMs);\n return await response.text();\n}\n\nasync function doFetchResponse(\n url: string,\n options: HttpRequestOptions,\n timeoutMs: number,\n): Promise<Response> {\n const request = buildRequest(options);\n injectTraceContext(request.headers);\n options.logger?.debug({ url, method: request.method }, \"http request\");\n const response = await fetch(url, {\n method: request.method,\n headers: request.headers,\n body: request.body,\n signal: AbortSignal.timeout(timeoutMs),\n });\n if (!response.ok) {\n const text = await response.text();\n throw new HttpError(response.status, text.slice(0, MAX_BODY_SNIPPET), response.headers);\n }\n return response;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"http.mjs","names":[],"sources":["../../src/utils/http.ts"],"sourcesContent":["import type { Span } from \"@opentelemetry/api\";\nimport { Buffer } from \"node:buffer\";\nimport type { Logger } from \"../logger.ts\";\nimport { injectTraceContext, setSpanMessage, spanException, withSpan } from \"../tracing.ts\";\n\n/**\n * Fetch JSON with timeout, retries, and structured errors.\n *\n * Retries network failures, 429, and 502/503/504 (honoring `Retry-After`);\n * every other status throws {@link HttpError} immediately.\n *\n * @param url - Absolute request URL.\n * @param options - Method, headers, JSON body, timeout, retries, logger.\n * @returns The parsed JSON response body.\n */\nexport async function httpJson<T>(url: string, options: HttpRequestOptions = {}): Promise<T> {\n return await requestWithRetry<T>(\n url,\n options,\n options.retries ?? DEFAULT_RETRIES,\n options.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n doFetchJson,\n );\n}\n\n/**\n * Download a binary response (images) with the same timeout, retry, and\n * `Retry-After` semantics as {@link httpJson}.\n *\n * @param url - Absolute request URL.\n * @param options - Method, headers, timeout, retries, logger.\n * @returns The raw response bytes.\n */\nexport async function httpBytes(url: string, options: HttpRequestOptions = {}): Promise<Buffer> {\n return await requestWithRetry<Buffer>(\n url,\n options,\n options.retries ?? DEFAULT_RETRIES,\n options.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n doFetchBytes,\n );\n}\n\n/**\n * POST/PUT plain payloads (e.g. Slack/Teams webhook cards) with the same\n * timeout, retry, and `Retry-After` semantics as {@link httpJson}.\n *\n * @param url - Absolute request URL.\n * @param options - Method, headers, text body, timeout, retries, logger.\n * @returns The raw response body text.\n */\nexport async function httpText(url: string, options: HttpTextOptions = {}): Promise<string> {\n return await requestWithRetry<string>(\n url,\n options,\n options.retries ?? DEFAULT_RETRIES,\n options.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n doFetchText,\n );\n}\n\n/**\n * Send a request and resolve with the response status, without ever reading the response body (it\n * is cancelled on every response). Use it when the answer is only \"did it succeed\", such as webhook\n * delivery, so a receiver cannot make the process buffer an arbitrarily large body. Retries,\n * timeouts, redaction and tracing are those of {@link httpText}. A non-2xx response throws\n * {@link HttpError} with an empty `body`.\n *\n * @param url - Absolute request URL.\n * @param options - Method, headers, JSON body, timeout, retries, logger.\n * @returns The 2xx response status.\n */\nexport async function httpStatus(url: string, options: HttpTextOptions = {}): Promise<number> {\n return await requestWithRetry<number>(\n url,\n options,\n options.retries ?? DEFAULT_RETRIES,\n options.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n doFetchStatus,\n );\n}\n\n/** Failed HTTP response: status code plus a snippet of the response body. */\nexport class HttpError extends Error {\n readonly status: number;\n readonly body: string;\n readonly headers: Headers;\n\n constructor(status: number, body: string, headers?: Headers) {\n super(`HTTP ${status}: ${body}`);\n this.name = \"HttpError\";\n this.status = status;\n this.body = body;\n this.headers = headers ?? new Headers();\n }\n}\n\n/** Options for {@link httpJson}. */\nexport interface HttpRequestOptions {\n method?: string;\n headers?: Record<string, string>;\n /** Body serialized as JSON with a `content-type` header. */\n json?: unknown;\n /** Body serialized as `application/x-www-form-urlencoded` (ignored when `json` is set). */\n form?: Record<string, string>;\n /** Per-attempt timeout in milliseconds (default 30s). */\n timeoutMs?: number;\n /** Total attempts including the first (default 3). */\n retries?: number;\n /** Aborts the request (and any further retry) when it fires. */\n signal?: AbortSignal;\n logger?: Logger;\n}\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n/** Options for {@link httpText}. */\nexport interface HttpTextOptions {\n method?: string;\n headers?: Record<string, string>;\n /** Body serialized as JSON with a `content-type` header. */\n json?: unknown;\n /** Per-attempt timeout in milliseconds (default 30s). */\n timeoutMs?: number;\n /** Total attempts including the first (default 3). */\n retries?: number;\n /** Aborts the request (and any further retry) when it fires. */\n signal?: AbortSignal;\n logger?: Logger;\n}\nconst DEFAULT_RETRIES = 3;\nconst MAX_RETRY_AFTER_MS = 30_000;\nconst BACKOFF_BASE_MS = 1000;\nconst MAX_BODY_SNIPPET = 500;\n\n/** Whether a failure is worth retrying: network errors, 429, and 502/503/504. */\nfunction shouldRetry(error: unknown, attempt: number, maxAttempts: number): boolean {\n if (attempt >= maxAttempts) {\n return false;\n }\n if (!(error instanceof HttpError)) {\n return true;\n }\n return error.status === 429 || (error.status >= 502 && error.status <= 504);\n}\n\n/** Parse a Retry-After value (seconds or HTTP-date) into milliseconds. */\nfunction parseRetryAfter(value: string | null): number | null {\n if (!value) {\n return null;\n }\n const seconds = Number(value);\n if (Number.isFinite(seconds) && seconds >= 0) {\n return Math.min(seconds * 1000, MAX_RETRY_AFTER_MS);\n }\n const date = Date.parse(value);\n if (!Number.isNaN(date)) {\n return Math.min(Math.max(date - Date.now(), 0), MAX_RETRY_AFTER_MS);\n }\n return null;\n}\n\n/** Delay before the next attempt: Retry-After when present, else 1s/2s/4s. */\nfunction retryDelayMs(error: unknown, attempt: number): number {\n if (error instanceof HttpError) {\n const retryAfter = parseRetryAfter(error.headers.get(\"retry-after\"));\n if (retryAfter !== null) {\n return retryAfter;\n }\n }\n return Math.min(BACKOFF_BASE_MS * 2 ** (attempt - 1), MAX_RETRY_AFTER_MS);\n}\n\n/** Sleep helper for retry backoff. */\nasync function sleep(ms: number): Promise<void> {\n await new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\n/* oxlint-disable eslint/no-await-in-loop -- retry attempts run sequentially by design */\nasync function requestWithRetry<T>(\n url: string,\n options: HttpRequestOptions,\n maxAttempts: number,\n timeoutMs: number,\n perform: (url: string, options: HttpRequestOptions, timeoutMs: number) => Promise<T>,\n): Promise<T> {\n return await withSpan(\n \"http.client\",\n async (span) => {\n for (let attempt = 1; ; attempt += 1) {\n try {\n return await perform(url, options, timeoutMs);\n } catch (error) {\n describeFailure(span, error, url);\n if (options.signal?.aborted || !shouldRetry(error, attempt, maxAttempts)) {\n // The final failure is recorded once, by `withSpan`, in the same redacted form.\n throw error;\n }\n span.recordException(spanException(error));\n span.setAttribute(\"http.retry.count\", attempt);\n await backoff(error, attempt, url, options.logger);\n }\n }\n },\n { \"http.request.method\": options.method ?? \"GET\", \"url.full\": redactUrlForLog(url) },\n );\n}\n/* oxlint-enable eslint/no-await-in-loop */\n\n/**\n * The form of a request URL that may appear in logs and span attributes: its origin\n * (`protocol//host`) and nothing else. The path of an incoming webhook (Slack, Teams) is the secret,\n * a bot token can be the first segment (Telegram), and other URLs carry credentials in the userinfo\n * or the query, so the path, query, fragment and userinfo are dropped by construction. There is no\n * opt-out.\n */\nexport function redactUrlForLog(url: string): string {\n try {\n const { protocol, host } = new URL(url);\n return `${protocol}//${host}`;\n } catch {\n return \"[invalid url]\";\n }\n}\n\n/**\n * What a span may say about a failed request. An HTTP failure carries its status and never the\n * response body, which can echo the request URL in any encoding. Any other failure keeps its\n * message with the request URL replaced by its origin, because native `fetch` repeats the whole\n * URL, userinfo included, when it refuses one. The error a caller catches is not changed.\n */\nfunction describeFailure(span: Span, error: unknown, url: string): void {\n if (error instanceof HttpError) {\n span.setAttribute(\"http.response.status_code\", error.status);\n setSpanMessage(error, `HTTP ${String(error.status)}`);\n } else if (error instanceof Error) {\n setSpanMessage(error, scrubUrl(error.message, url));\n }\n}\n\n/** `text` with the request URL, as given and as normalized, replaced by its origin. */\nfunction scrubUrl(text: string, url: string): string {\n const origin = redactUrlForLog(url);\n const normalized = URL.canParse(url) ? new URL(url).href : url;\n return text.replaceAll(url, origin).replaceAll(normalized, origin);\n}\n\nasync function backoff(\n error: unknown,\n attempt: number,\n url: string,\n logger?: Logger,\n): Promise<void> {\n const delay = retryDelayMs(error, attempt);\n logger?.warn(\n { url: redactUrlForLog(url), attempt, delayMs: delay },\n \"http request failed, retrying\",\n );\n await sleep(delay);\n}\n\nfunction buildRequest(options: HttpRequestOptions): {\n headers: Record<string, string>;\n body: string | undefined;\n method: string;\n} {\n const headers: Record<string, string> = { ...options.headers };\n let body: string | undefined;\n if (options.json !== undefined) {\n headers[\"content-type\"] = \"application/json\";\n body = JSON.stringify(options.json);\n } else if (options.form) {\n headers[\"content-type\"] = \"application/x-www-form-urlencoded\";\n body = new URLSearchParams(options.form).toString();\n }\n return { headers, body, method: options.method ?? \"GET\" };\n}\n\nasync function doFetchJson<T>(\n url: string,\n options: HttpRequestOptions,\n timeoutMs: number,\n): Promise<T> {\n const response = await doFetchResponse(url, options, timeoutMs);\n return (await response.json()) as T;\n}\n\nasync function doFetchText(\n url: string,\n options: HttpRequestOptions,\n timeoutMs: number,\n): Promise<string> {\n const response = await doFetchResponse(url, options, timeoutMs);\n return await response.text();\n}\n\nasync function doFetchStatus(\n url: string,\n options: HttpRequestOptions,\n timeoutMs: number,\n): Promise<number> {\n const response = await doFetchResponse(url, options, timeoutMs, false);\n await discardBody(response);\n return response.status;\n}\n\nasync function doFetchBytes(\n url: string,\n options: HttpRequestOptions,\n timeoutMs: number,\n): Promise<Buffer> {\n const response = await doFetchResponse(url, options, timeoutMs);\n return Buffer.from(await response.arrayBuffer());\n}\n\nasync function doFetchResponse(\n url: string,\n options: HttpRequestOptions,\n timeoutMs: number,\n readErrorBody = true,\n): Promise<Response> {\n const request = buildRequest(options);\n injectTraceContext(request.headers);\n options.logger?.debug({ url: redactUrlForLog(url), method: request.method }, \"http request\");\n const response = await fetch(url, {\n method: request.method,\n headers: request.headers,\n body: request.body,\n signal: options.signal\n ? AbortSignal.any([options.signal, AbortSignal.timeout(timeoutMs)])\n : AbortSignal.timeout(timeoutMs),\n });\n if (!response.ok) {\n throw await failureOf(response, readErrorBody);\n }\n return response;\n}\n\n/**\n * Cancel an unread body. Cleanup only: a rejected cancel must never replace the request's outcome,\n * or a delivered request would look failed and be retried.\n */\nasync function discardBody(response: Response): Promise<void> {\n try {\n await response.body?.cancel();\n } catch {\n // The status is already known; there is nothing left to do with the body.\n }\n}\n\n/** The {@link HttpError} for a non-2xx response; the body is read (and capped) only on request. */\nasync function failureOf(response: Response, readBody: boolean): Promise<HttpError> {\n if (!readBody) {\n await discardBody(response);\n return new HttpError(response.status, \"\", response.headers);\n }\n const text = await response.text();\n return new HttpError(response.status, text.slice(0, MAX_BODY_SNIPPET), response.headers);\n}\n"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { AdaptiveConcurrency } from "./adaptive-concurrency.mjs";
4
+ /**
5
+ * Like `mapLimit`, but the limit is read live from `controller` and every finished item is fed
6
+ * back to it. `fn` reports a timeout it handled itself with `timedOut: true`.
7
+ */
8
+ declare function mapAdaptive<T, R>(items: readonly T[], controller: AdaptiveConcurrency, fn: (item: T, index: number) => Promise<{
9
+ value: R;
10
+ timedOut?: boolean;
11
+ }>): Promise<R[]>;
12
+ export { mapAdaptive };
13
+ //# sourceMappingURL=map-adaptive.d.mts.map
@@ -0,0 +1,71 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ // src/utils/map-adaptive.ts
5
+ /**
6
+ * Like `mapLimit`, but the limit is read live from `controller` and every finished item is fed
7
+ * back to it. `fn` reports a timeout it handled itself with `timedOut: true`.
8
+ */
9
+ async function mapAdaptive(items, controller, fn) {
10
+ return await new AdaptiveRun(items, controller, fn).run();
11
+ }
12
+ var AdaptiveRun = class {
13
+ results;
14
+ next = 0;
15
+ active = 0;
16
+ failure = null;
17
+ finish = null;
18
+ items;
19
+ controller;
20
+ fn;
21
+ constructor(items, controller, fn) {
22
+ this.items = items;
23
+ this.controller = controller;
24
+ this.fn = fn;
25
+ this.results = Array.from({ length: items.length });
26
+ }
27
+ async run() {
28
+ return await new Promise((resolve, reject) => {
29
+ this.finish = {
30
+ resolve,
31
+ reject
32
+ };
33
+ this.pump();
34
+ });
35
+ }
36
+ pump() {
37
+ while (this.canLaunch()) {
38
+ this.launch(this.next);
39
+ this.next += 1;
40
+ }
41
+ if (this.active === 0 && (this.failure || this.next >= this.items.length)) {
42
+ if (this.failure) this.finish?.reject(this.failure.error);
43
+ else this.finish?.resolve(this.results);
44
+ }
45
+ }
46
+ canLaunch() {
47
+ return !this.failure && this.active < this.controller.limit && this.next < this.items.length;
48
+ }
49
+ launch(index) {
50
+ this.active += 1;
51
+ const started = performance.now();
52
+ this.fn(this.items[index], index).then((outcome) => {
53
+ this.results[index] = outcome.value;
54
+ this.controller.observe({
55
+ durationMs: performance.now() - started,
56
+ timedOut: outcome.timedOut
57
+ });
58
+ this.settle();
59
+ }, (error) => {
60
+ this.failure ??= { error: error instanceof Error ? error : new Error(String(error)) };
61
+ this.settle();
62
+ });
63
+ }
64
+ settle() {
65
+ this.active -= 1;
66
+ this.pump();
67
+ }
68
+ };
69
+ export { mapAdaptive };
70
+
71
+ //# sourceMappingURL=map-adaptive.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map-adaptive.mjs","names":[],"sources":["../../src/utils/map-adaptive.ts"],"sourcesContent":["/** `mapLimit` with a live, self-adjusting limit. */\nimport type { AdaptiveConcurrency } from \"./adaptive-concurrency.ts\";\n\n/**\n * Like `mapLimit`, but the limit is read live from `controller` and every finished item is fed\n * back to it. `fn` reports a timeout it handled itself with `timedOut: true`.\n */\nexport async function mapAdaptive<T, R>(\n items: readonly T[],\n controller: AdaptiveConcurrency,\n fn: (item: T, index: number) => Promise<{ value: R; timedOut?: boolean }>,\n): Promise<R[]> {\n return await new AdaptiveRun(items, controller, fn).run();\n}\n\nclass AdaptiveRun<T, R> {\n private readonly results: R[];\n private next = 0;\n private active = 0;\n private failure: { error: Error } | null = null;\n private finish: { resolve: (value: R[]) => void; reject: (error: Error) => void } | null = null;\n\n private readonly items: readonly T[];\n private readonly controller: AdaptiveConcurrency;\n private readonly fn: (item: T, index: number) => Promise<{ value: R; timedOut?: boolean }>;\n\n constructor(\n items: readonly T[],\n controller: AdaptiveConcurrency,\n fn: (item: T, index: number) => Promise<{ value: R; timedOut?: boolean }>,\n ) {\n this.items = items;\n this.controller = controller;\n this.fn = fn;\n this.results = Array.from<R>({ length: items.length });\n }\n\n async run(): Promise<R[]> {\n return await new Promise<R[]>((resolve, reject) => {\n this.finish = { resolve, reject };\n this.pump();\n });\n }\n\n private pump(): void {\n while (this.canLaunch()) {\n this.launch(this.next);\n this.next += 1;\n }\n if (this.active === 0 && (this.failure || this.next >= this.items.length)) {\n if (this.failure) {\n this.finish?.reject(this.failure.error);\n } else {\n this.finish?.resolve(this.results);\n }\n }\n }\n\n private canLaunch(): boolean {\n return !this.failure && this.active < this.controller.limit && this.next < this.items.length;\n }\n\n private launch(index: number): void {\n this.active += 1;\n const started = performance.now();\n this.fn(this.items[index] as T, index).then(\n (outcome) => {\n this.results[index] = outcome.value;\n this.controller.observe({\n durationMs: performance.now() - started,\n timedOut: outcome.timedOut,\n });\n this.settle();\n },\n (error: unknown) => {\n this.failure ??= { error: error instanceof Error ? error : new Error(String(error)) };\n this.settle();\n },\n );\n }\n\n private settle(): void {\n this.active -= 1;\n this.pump();\n }\n}\n"],"mappings":";;;;;;;;AAOA,eAAsB,YACpB,OACA,YACA,IACc;CACd,OAAO,MAAM,IAAI,YAAY,OAAO,YAAY,EAAE,CAAC,CAAC,IAAI;AAC1D;AAEA,IAAM,cAAN,MAAwB;CACtB;CACA,OAAe;CACf,SAAiB;CACjB,UAA2C;CAC3C,SAA2F;CAE3F;CACA;CACA;CAEA,YACE,OACA,YACA,IACA;EACA,KAAK,QAAQ;EACb,KAAK,aAAa;EAClB,KAAK,KAAK;EACV,KAAK,UAAU,MAAM,KAAQ,EAAE,QAAQ,MAAM,OAAO,CAAC;CACvD;CAEA,MAAM,MAAoB;EACxB,OAAO,MAAM,IAAI,SAAc,SAAS,WAAW;GACjD,KAAK,SAAS;IAAE;IAAS;GAAO;GAChC,KAAK,KAAK;EACZ,CAAC;CACH;CAEA,OAAqB;EACnB,OAAO,KAAK,UAAU,GAAG;GACvB,KAAK,OAAO,KAAK,IAAI;GACrB,KAAK,QAAQ;EACf;EACA,IAAI,KAAK,WAAW,MAAM,KAAK,WAAW,KAAK,QAAQ,KAAK,MAAM,SAAS;GACzE,IAAI,KAAK,SACP,KAAK,QAAQ,OAAO,KAAK,QAAQ,KAAK;QAEtC,KAAK,QAAQ,QAAQ,KAAK,OAAO;EAErC;CACF;CAEA,YAA6B;EAC3B,OAAO,CAAC,KAAK,WAAW,KAAK,SAAS,KAAK,WAAW,SAAS,KAAK,OAAO,KAAK,MAAM;CACxF;CAEA,OAAe,OAAqB;EAClC,KAAK,UAAU;EACf,MAAM,UAAU,YAAY,IAAI;EAChC,KAAK,GAAG,KAAK,MAAM,QAAa,KAAK,CAAC,CAAC,MACpC,YAAY;GACX,KAAK,QAAQ,SAAS,QAAQ;GAC9B,KAAK,WAAW,QAAQ;IACtB,YAAY,YAAY,IAAI,IAAI;IAChC,UAAU,QAAQ;GACpB,CAAC;GACD,KAAK,OAAO;EACd,IACC,UAAmB;GAClB,KAAK,YAAY,EAAE,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,EAAE;GACpF,KAAK,OAAO;EACd,CACF;CACF;CAEA,SAAuB;EACrB,KAAK,UAAU;EACf,KAAK,KAAK;CACZ;AACF"}
@@ -0,0 +1,8 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ /** Split `items` into consecutive batches of at most `size` (a non-positive size means one batch). */
4
+ declare function chunk<T>(items: readonly T[], size: number): T[][];
5
+ /** Run `fn` over `items` with at most `limit` in flight; results keep input order. */
6
+ declare function mapLimit<T, R>(items: readonly T[], limit: number, fn: (item: T, index: number) => Promise<R>): Promise<R[]>;
7
+ export { chunk, mapLimit };
8
+ //# sourceMappingURL=map-limit.d.mts.map
@@ -0,0 +1,31 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ // src/utils/map-limit.ts
5
+ /** Split `items` into consecutive batches of at most `size` (a non-positive size means one batch). */
6
+ function chunk(items, size) {
7
+ const step = size > 0 ? Math.max(1, Math.floor(size)) : items.length || 1;
8
+ const batches = [];
9
+ for (let start = 0; start < items.length; start += step) batches.push(items.slice(start, start + step));
10
+ return batches;
11
+ }
12
+ /** Run `fn` over `items` with at most `limit` in flight; results keep input order. */
13
+ async function mapLimit(items, limit, fn) {
14
+ const results = Array.from({ length: items.length });
15
+ let next = 0;
16
+ const worker = async () => {
17
+ while (next < items.length) {
18
+ const index = next;
19
+ next += 1;
20
+ results[index] = await fn(items[index], index);
21
+ }
22
+ };
23
+ const workers = Math.max(1, Math.min(Math.floor(limit) || 1, items.length));
24
+ await Promise.all(Array.from({ length: workers }, async () => {
25
+ await worker();
26
+ }));
27
+ return results;
28
+ }
29
+ export { chunk, mapLimit };
30
+
31
+ //# sourceMappingURL=map-limit.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map-limit.mjs","names":[],"sources":["../../src/utils/map-limit.ts"],"sourcesContent":["/** Split `items` into consecutive batches of at most `size` (a non-positive size means one batch). */\nexport function chunk<T>(items: readonly T[], size: number): T[][] {\n const step = size > 0 ? Math.max(1, Math.floor(size)) : items.length || 1;\n const batches: T[][] = [];\n for (let start = 0; start < items.length; start += step) {\n batches.push(items.slice(start, start + step));\n }\n return batches;\n}\n\n/** Run `fn` over `items` with at most `limit` in flight; results keep input order. */\nexport async function mapLimit<T, R>(\n items: readonly T[],\n limit: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results = Array.from<R>({ length: items.length });\n let next = 0;\n const worker = async (): Promise<void> => {\n while (next < items.length) {\n const index = next;\n next += 1;\n // oxlint-disable-next-line eslint/no-await-in-loop -- bounded worker pulls sequentially\n results[index] = await fn(items[index] as T, index);\n }\n };\n const workers = Math.max(1, Math.min(Math.floor(limit) || 1, items.length));\n await Promise.all(\n Array.from({ length: workers }, async () => {\n await worker();\n }),\n );\n return results;\n}\n"],"mappings":";;;;;AACA,SAAgB,MAAS,OAAqB,MAAqB;CACjE,MAAM,OAAO,OAAO,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,IAAI,CAAC,IAAI,MAAM,UAAU;CACxE,MAAM,UAAiB,CAAC;CACxB,KAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SAAS,MACjD,QAAQ,KAAK,MAAM,MAAM,OAAO,QAAQ,IAAI,CAAC;CAE/C,OAAO;AACT;;AAGA,eAAsB,SACpB,OACA,OACA,IACc;CACd,MAAM,UAAU,MAAM,KAAQ,EAAE,QAAQ,MAAM,OAAO,CAAC;CACtD,IAAI,OAAO;CACX,MAAM,SAAS,YAA2B;EACxC,OAAO,OAAO,MAAM,QAAQ;GAC1B,MAAM,QAAQ;GACd,QAAQ;GAER,QAAQ,SAAS,MAAM,GAAG,MAAM,QAAa,KAAK;EACpD;CACF;CACA,MAAM,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,MAAM,KAAK,KAAK,GAAG,MAAM,MAAM,CAAC;CAC1E,MAAM,QAAQ,IACZ,MAAM,KAAK,EAAE,QAAQ,QAAQ,GAAG,YAAY;EAC1C,MAAM,OAAO;CACf,CAAC,CACH;CACA,OAAO;AACT"}
@@ -1,9 +1,7 @@
1
1
  import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
- //#region src/utils/mime.d.ts
4
3
  /** Shared MIME types for storybook static serving. */
5
4
  declare const MIME_TYPES: Record<string, string>;
6
5
  declare function mimeFor(filePath: string): string;
7
- //#endregion
8
6
  export { MIME_TYPES, mimeFor };
9
7
  //# sourceMappingURL=mime.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/utils/mime.ts
4
+ // src/utils/mime.ts
5
5
  /** Shared MIME types for storybook static serving. */
6
6
  const MIME_TYPES = {
7
7
  ".css": "text/css; charset=utf-8",
@@ -27,7 +27,6 @@ function mimeFor(filePath) {
27
27
  const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
28
28
  return MIME_TYPES[ext] ?? "application/octet-stream";
29
29
  }
30
- //#endregion
31
30
  export { MIME_TYPES, mimeFor };
32
31
 
33
32
  //# sourceMappingURL=mime.mjs.map
@@ -0,0 +1,35 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { Logger } from "../logger.mjs";
4
+ import { SessionGate } from "./session-gate.mjs";
5
+ /** Options for {@link createPageLimiter}. */
6
+ interface PageLimiterOptions {
7
+ /** Explicit page count (config or run input). Fixed: the adaptive controller stays off. */
8
+ concurrency?: number;
9
+ /**
10
+ * A remote target's session cap, shared by every render of that target in the process. It
11
+ * replaces CPU and memory sizing: the plan's parallel-session limit is the budget, not this host.
12
+ * An explicit `concurrency` can lower a run's share of it, never raise it.
13
+ */
14
+ gate?: SessionGate;
15
+ /** Engine being rendered; heavier engines get a smaller budget. */
16
+ browser?: string;
17
+ logger?: Logger;
18
+ }
19
+ /** Runs capture jobs under a page limit. One limiter spans a whole run so what it learns carries across batches. */
20
+ interface PageLimiter {
21
+ /**
22
+ * Run `fn` over `items`. `fn` resolves to whether the capture timed out (it handles its own
23
+ * failures, so the limiter cannot see them otherwise).
24
+ */
25
+ each<T>(items: readonly T[], fn: (item: T) => Promise<boolean>): Promise<void>;
26
+ /** Release the event-loop monitor. */
27
+ dispose(): void;
28
+ }
29
+ /**
30
+ * The session gate's size for a remote target; a fixed limit when `concurrency` is given; otherwise start at the CPU/memory/engine-sized
31
+ * recommendation and let latency, event-loop lag and timeouts move it up to the adaptive ceiling.
32
+ */
33
+ declare function createPageLimiter(options: PageLimiterOptions): PageLimiter;
34
+ export { PageLimiter, PageLimiterOptions, createPageLimiter };
35
+ //# sourceMappingURL=page-limiter.d.mts.map