@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,195 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { roundedTimings, runWithTimings } from "../../utils/timing.mjs";
5
+ import { backoffDelayMs, isFinalAttempt } from "./retry.mjs";
6
+ import { keepJobLease } from "../db-queue/keeper.mjs";
7
+ import { reportFinished } from "./metrics.mjs";
8
+ //#region src/capture/consumer/run-job.ts
9
+ /** Run `job` in the slot the caller holds; never rejects. */
10
+ async function runJob(rt, job) {
11
+ const kind = job.kind ?? "capture";
12
+ const startedAt = rt.now();
13
+ const logger = rt.logger.child({
14
+ buildId: job.buildId,
15
+ jobId: job.jobId ?? job.receipt,
16
+ kind,
17
+ reqId: job.reqId
18
+ });
19
+ const finish = (outcome) => {
20
+ reportFinished(rt.metrics, {
21
+ kind,
22
+ outcome,
23
+ durationMs: rt.now() - startedAt,
24
+ attempt: job.attempt ?? (job.attempts ?? 0) + 1
25
+ });
26
+ };
27
+ try {
28
+ finish(await execute(rt, job, logger));
29
+ } catch (error) {
30
+ logger.error({ err: error }, "failed to settle job");
31
+ finish("failed");
32
+ }
33
+ }
34
+ async function execute(rt, job, logger) {
35
+ const handler = rt.handlers.get(job.kind ?? "capture");
36
+ const refused = await refuse(rt, job, {
37
+ handler,
38
+ logger
39
+ });
40
+ if (refused !== null || handler === void 0) return refused ?? "unhandled";
41
+ return await runHandler(rt, {
42
+ job,
43
+ handler,
44
+ logger
45
+ });
46
+ }
47
+ /** Fail a job nobody can run (no handler for its kind) or that is malformed; null when runnable. */
48
+ async function refuse(rt, job, held) {
49
+ const { handler, logger } = held;
50
+ if (handler === void 0) {
51
+ logger.error("no handler registered for job kind; failing the job");
52
+ await failJob(rt, job, `no handler registered for job kind "${job.kind ?? "capture"}"`, logger);
53
+ return "unhandled";
54
+ }
55
+ if (handler.accepts?.(job) === false) {
56
+ logger.warn({ attempts: job.attempts }, "dropping malformed queue job");
57
+ await failJob(rt, job, "malformed job", logger);
58
+ return "dropped";
59
+ }
60
+ return null;
61
+ }
62
+ async function runHandler(rt, held) {
63
+ const { job, handler, logger } = held;
64
+ const keeper = keepLease(rt, job, logger);
65
+ const touch = () => {
66
+ keeper.touch();
67
+ rt.onProgress?.(job);
68
+ };
69
+ logger.info("consumer picked up job");
70
+ const run = invoke(handler, job, {
71
+ logger,
72
+ touch
73
+ });
74
+ try {
75
+ const raced = await Promise.race([run, endOf(keeper)]);
76
+ return raced.kind === "ended" ? await abandon(rt, held, raced.end) : await settle(rt, held, raced);
77
+ } finally {
78
+ keeper.stop();
79
+ run.catch(() => {});
80
+ }
81
+ }
82
+ function keepLease(rt, job, logger) {
83
+ return keepJobLease(rt.queue, job, {
84
+ ...rt.lease,
85
+ now: rt.now,
86
+ ...rt.timers === void 0 ? {} : { timers: rt.timers },
87
+ logger
88
+ });
89
+ }
90
+ /** Run the handler, timing it; its failure is a value so the race can carry it. */
91
+ async function invoke(handler, job, context) {
92
+ try {
93
+ const { timings } = await runWithTimings(async () => {
94
+ await handler.run(job, context);
95
+ });
96
+ context.logger.info({ timings: roundedTimings(timings) }, "consumer job timings");
97
+ return { kind: "settled" };
98
+ } catch (error) {
99
+ return {
100
+ kind: "settled",
101
+ error
102
+ };
103
+ }
104
+ }
105
+ async function endOf(keeper) {
106
+ return {
107
+ kind: "ended",
108
+ end: await keeper.ended
109
+ };
110
+ }
111
+ /** The handler returned: ack, or retry / fail on its error. */
112
+ async function settle(rt, held, raced) {
113
+ if (!("error" in raced)) {
114
+ await rt.queue.ack(held.job);
115
+ held.logger.info("consumer completed job");
116
+ return "completed";
117
+ }
118
+ return await retryOrFail(rt, held, raced.error);
119
+ }
120
+ /** The handler threw: requeue with backoff, or fail the job on its last attempt. */
121
+ async function retryOrFail(rt, held, error) {
122
+ const { job, logger } = held;
123
+ const message = error instanceof Error ? error.message : String(error);
124
+ const attempts = job.attempts ?? 0;
125
+ if (isFinalAttempt(job, rt.maxRetries)) {
126
+ await failJob(rt, job, message, logger);
127
+ logger.error({
128
+ err: error,
129
+ attempts
130
+ }, "capture permanently failed");
131
+ return "failed";
132
+ }
133
+ await requeue(rt, held, {
134
+ error,
135
+ message,
136
+ attempts
137
+ });
138
+ return "retried";
139
+ }
140
+ /** Put the job back with the next backoff delay. */
141
+ async function requeue(rt, held, failure) {
142
+ const { backoff } = held.handler;
143
+ const delayMs = backoffDelayMs(failure.attempts, backoff?.baseMs ?? rt.backoffMs ?? 1e3, backoff?.maxMs);
144
+ await rt.queue.nack(held.job, {
145
+ requeue: true,
146
+ delayMs,
147
+ error: failure.message
148
+ });
149
+ held.logger.warn({
150
+ err: failure.error,
151
+ attempts: failure.attempts,
152
+ delayMs
153
+ }, "job failed, requeued");
154
+ }
155
+ /**
156
+ * The lease is gone or the job was cancelled while the handler ran: stop the work. A lost lease
157
+ * means another consumer owns the job (it settles it); a cancel is settled here as `cancelled`.
158
+ */
159
+ async function abandon(rt, held, end) {
160
+ const { job, handler, logger } = held;
161
+ await handler.cancel?.(job).catch((error) => {
162
+ logger.warn({ err: error }, "failed to cancel the running job");
163
+ });
164
+ if (end === "lost") {
165
+ logger.warn("queue lease lost, aborting job");
166
+ return "lost";
167
+ }
168
+ logger.info("job cancelled, aborting");
169
+ await rt.queue.nack(job, {
170
+ requeue: false,
171
+ error: "cancelled"
172
+ });
173
+ return "cancelled";
174
+ }
175
+ /**
176
+ * Final failure: nack without requeue so the queue records it failed. A queue that does not count
177
+ * attempts itself falls back to ack when the nack fails, so a poison job never loops; the database
178
+ * queue (it states `maxAttempts`) ends the job by itself, so there a failed nack is left to it.
179
+ */
180
+ async function failJob(rt, job, message, logger) {
181
+ try {
182
+ await rt.queue.nack(job, {
183
+ requeue: false,
184
+ error: message
185
+ });
186
+ } catch (nackError) {
187
+ if (typeof job.maxAttempts === "number") throw nackError;
188
+ logger.warn({ err: nackError }, "final nack failed, acknowledging the job instead");
189
+ await rt.queue.ack(job);
190
+ }
191
+ }
192
+ //#endregion
193
+ export { runJob };
194
+
195
+ //# sourceMappingURL=run-job.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-job.mjs","names":[],"sources":["../../../src/capture/consumer/run-job.ts"],"sourcesContent":["/**\n * Run one claimed job to its end: pick the handler by kind, keep the lease (heartbeat and progress), then settle\n * the job on the queue (ack, retry with backoff, fail, or leave it to whoever owns it now).\n */\nimport type { PollableCaptureQueue } from \"../../adapters/capture-queue.ts\";\nimport type { Logger } from \"../../logger.ts\";\nimport { roundedTimings, runWithTimings } from \"../../utils/timing.ts\";\nimport { keepJobLease, type JobLeaseKeeper, type LeaseEnd } from \"../db-queue/keeper.ts\";\nimport type { QueueTimers } from \"../db-queue/timers.ts\";\nimport { reportFinished, type ConsumerMetrics, type JobOutcome } from \"./metrics.ts\";\nimport { backoffDelayMs, DEFAULT_BACKOFF_MS, isFinalAttempt } from \"./retry.ts\";\nimport type { ConsumerJob, KindHandler } from \"./types.ts\";\n\n/** What {@link runJob} needs from the consumer. */\nexport interface JobRuntime {\n queue: PollableCaptureQueue;\n /** Normalized registry. */\n handlers: ReadonlyMap<string, KindHandler>;\n maxRetries: number;\n backoffMs?: number;\n logger: Logger;\n metrics?: ConsumerMetrics | undefined;\n onProgress?: ((job: ConsumerJob) => void) | undefined;\n now: () => number;\n timers?: QueueTimers | undefined;\n lease?: { renewEveryMs?: number; checkMs?: number } | undefined;\n}\n\n/** Why a job's handler stopped: it finished (or threw), or the queue took the job away. */\ntype Race = { kind: \"settled\"; error?: unknown } | { kind: \"ended\"; end: LeaseEnd };\n\n/** Run `job` in the slot the caller holds; never rejects. */\nexport async function runJob(rt: JobRuntime, job: ConsumerJob): Promise<void> {\n const kind = job.kind ?? \"capture\";\n const startedAt = rt.now();\n const logger = rt.logger.child({\n buildId: job.buildId,\n jobId: job.jobId ?? job.receipt,\n kind,\n reqId: job.reqId,\n });\n const finish = (outcome: JobOutcome): void => {\n reportFinished(rt.metrics, {\n kind,\n outcome,\n durationMs: rt.now() - startedAt,\n attempt: job.attempt ?? (job.attempts ?? 0) + 1,\n });\n };\n try {\n finish(await execute(rt, job, logger));\n } catch (error) {\n // Settling failed (the database is unreachable): the lease runs out and the queue redelivers.\n logger.error({ err: error }, \"failed to settle job\");\n finish(\"failed\");\n }\n}\n\nasync function execute(rt: JobRuntime, job: ConsumerJob, logger: Logger): Promise<JobOutcome> {\n const handler = rt.handlers.get(job.kind ?? \"capture\");\n const refused = await refuse(rt, job, { handler, logger });\n if (refused !== null || handler === undefined) {\n return refused ?? \"unhandled\";\n }\n return await runHandler(rt, { job, handler, logger });\n}\n\n/** Fail a job nobody can run (no handler for its kind) or that is malformed; null when runnable. */\nasync function refuse(\n rt: JobRuntime,\n job: ConsumerJob,\n held: { handler: KindHandler | undefined; logger: Logger },\n): Promise<JobOutcome | null> {\n const { handler, logger } = held;\n if (handler === undefined) {\n logger.error(\"no handler registered for job kind; failing the job\");\n await failJob(rt, job, `no handler registered for job kind \"${job.kind ?? \"capture\"}\"`, logger);\n return \"unhandled\";\n }\n if (handler.accepts?.(job) === false) {\n logger.warn({ attempts: job.attempts }, \"dropping malformed queue job\");\n await failJob(rt, job, \"malformed job\", logger);\n return \"dropped\";\n }\n return null;\n}\n\nasync function runHandler(\n rt: JobRuntime,\n held: { job: ConsumerJob; handler: KindHandler; logger: Logger },\n): Promise<JobOutcome> {\n const { job, handler, logger } = held;\n const keeper = keepLease(rt, job, logger);\n const touch = (): void => {\n keeper.touch();\n rt.onProgress?.(job);\n };\n logger.info(\"consumer picked up job\");\n const run = invoke(handler, job, { logger, touch });\n try {\n const raced = await Promise.race([run, endOf(keeper)]);\n return raced.kind === \"ended\"\n ? await abandon(rt, held, raced.end)\n : await settle(rt, held, raced);\n } finally {\n keeper.stop();\n run.catch(() => {});\n }\n}\n\nfunction keepLease(rt: JobRuntime, job: ConsumerJob, logger: Logger): JobLeaseKeeper {\n return keepJobLease(rt.queue, job, {\n ...rt.lease,\n now: rt.now,\n ...(rt.timers === undefined ? {} : { timers: rt.timers }),\n logger,\n });\n}\n\n/** Run the handler, timing it; its failure is a value so the race can carry it. */\nasync function invoke(\n handler: KindHandler,\n job: ConsumerJob,\n context: { logger: Logger; touch: () => void },\n): Promise<Race> {\n try {\n const { timings } = await runWithTimings(async () => {\n await handler.run(job, context);\n });\n context.logger.info({ timings: roundedTimings(timings) }, \"consumer job timings\");\n return { kind: \"settled\" };\n } catch (error) {\n return { kind: \"settled\", error };\n }\n}\n\nasync function endOf(keeper: JobLeaseKeeper): Promise<Race> {\n return { kind: \"ended\", end: await keeper.ended };\n}\n\n/** The handler returned: ack, or retry / fail on its error. */\nasync function settle(\n rt: JobRuntime,\n held: { job: ConsumerJob; handler: KindHandler; logger: Logger },\n raced: Race & { kind: \"settled\" },\n): Promise<JobOutcome> {\n if (!(\"error\" in raced)) {\n await rt.queue.ack(held.job);\n held.logger.info(\"consumer completed job\");\n return \"completed\";\n }\n return await retryOrFail(rt, held, raced.error);\n}\n\n/** The handler threw: requeue with backoff, or fail the job on its last attempt. */\nasync function retryOrFail(\n rt: JobRuntime,\n held: { job: ConsumerJob; handler: KindHandler; logger: Logger },\n error: unknown,\n): Promise<JobOutcome> {\n const { job, logger } = held;\n const message = error instanceof Error ? error.message : String(error);\n const attempts = job.attempts ?? 0;\n if (isFinalAttempt(job, rt.maxRetries)) {\n await failJob(rt, job, message, logger);\n logger.error({ err: error, attempts }, \"capture permanently failed\");\n return \"failed\";\n }\n await requeue(rt, held, { error, message, attempts });\n return \"retried\";\n}\n\n/** Put the job back with the next backoff delay. */\nasync function requeue(\n rt: JobRuntime,\n held: { job: ConsumerJob; handler: KindHandler; logger: Logger },\n failure: { error: unknown; message: string; attempts: number },\n): Promise<void> {\n const { backoff } = held.handler;\n const delayMs = backoffDelayMs(\n failure.attempts,\n backoff?.baseMs ?? rt.backoffMs ?? DEFAULT_BACKOFF_MS,\n backoff?.maxMs,\n );\n await rt.queue.nack(held.job, { requeue: true, delayMs, error: failure.message });\n held.logger.warn(\n { err: failure.error, attempts: failure.attempts, delayMs },\n \"job failed, requeued\",\n );\n}\n\n/**\n * The lease is gone or the job was cancelled while the handler ran: stop the work. A lost lease\n * means another consumer owns the job (it settles it); a cancel is settled here as `cancelled`.\n */\nasync function abandon(\n rt: JobRuntime,\n held: { job: ConsumerJob; handler: KindHandler; logger: Logger },\n end: LeaseEnd,\n): Promise<JobOutcome> {\n const { job, handler, logger } = held;\n await handler.cancel?.(job).catch((error: unknown) => {\n logger.warn({ err: error }, \"failed to cancel the running job\");\n });\n if (end === \"lost\") {\n logger.warn(\"queue lease lost, aborting job\");\n return \"lost\";\n }\n logger.info(\"job cancelled, aborting\");\n await rt.queue.nack(job, { requeue: false, error: \"cancelled\" });\n return \"cancelled\";\n}\n\n/**\n * Final failure: nack without requeue so the queue records it failed. A queue that does not count\n * attempts itself falls back to ack when the nack fails, so a poison job never loops; the database\n * queue (it states `maxAttempts`) ends the job by itself, so there a failed nack is left to it.\n */\nasync function failJob(\n rt: JobRuntime,\n job: ConsumerJob,\n message: string,\n logger: Logger,\n): Promise<void> {\n try {\n await rt.queue.nack(job, { requeue: false, error: message });\n } catch (nackError) {\n if (typeof job.maxAttempts === \"number\") {\n throw nackError;\n }\n logger.warn({ err: nackError }, \"final nack failed, acknowledging the job instead\");\n await rt.queue.ack(job);\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { sweepOrphanedScratch } from "../scratch.mjs";
5
+ import { configureImages, verifyImageEngine } from "../../diff.mjs";
6
+ //#region src/capture/consumer/startup.ts
7
+ /**
8
+ * Only touch the process-wide image settings when asked, so a consumer created next to an app in
9
+ * one process does not undo the app's own `images`/`compare` options.
10
+ */
11
+ function applyImageSettings(images, compare) {
12
+ if (images || compare) configureImages({
13
+ engine: images?.engine,
14
+ threads: compare?.threads
15
+ });
16
+ }
17
+ /** Check that the image engine loads; a consumer that cannot diff must not claim jobs. */
18
+ async function ensureImageEngine(logger) {
19
+ try {
20
+ await verifyImageEngine();
21
+ } catch (error) {
22
+ logger.error({ err: error }, "image engine cannot load; capture consumer stopping");
23
+ throw error;
24
+ }
25
+ }
26
+ /** Sweep scratch left by a killed run; failures are logged, never fatal. */
27
+ async function sweepScratch(scratchDir, logger) {
28
+ if (scratchDir === void 0) return;
29
+ try {
30
+ await sweepOrphanedScratch(scratchDir, { logger });
31
+ } catch (error) {
32
+ logger.warn({ err: error }, "scratch sweep failed");
33
+ }
34
+ }
35
+ //#endregion
36
+ export { applyImageSettings, ensureImageEngine, sweepScratch };
37
+
38
+ //# sourceMappingURL=startup.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"startup.mjs","names":[],"sources":["../../../src/capture/consumer/startup.ts"],"sourcesContent":["/** What the consumer does before it polls: process-wide image settings, the engine check, scratch sweep. */\nimport type { ShelfCompareOptions, ShelfImagesOptions } from \"../../config.ts\";\nimport { configureImages, verifyImageEngine } from \"../../diff/index.ts\";\nimport type { Logger } from \"../../logger.ts\";\nimport { sweepOrphanedScratch } from \"../scratch.ts\";\n\n/**\n * Only touch the process-wide image settings when asked, so a consumer created next to an app in\n * one process does not undo the app's own `images`/`compare` options.\n */\nexport function applyImageSettings(\n images?: ShelfImagesOptions,\n compare?: ShelfCompareOptions,\n): void {\n if (images || compare) {\n configureImages({ engine: images?.engine, threads: compare?.threads });\n }\n}\n\n/** Check that the image engine loads; a consumer that cannot diff must not claim jobs. */\nexport async function ensureImageEngine(logger: Logger): Promise<void> {\n try {\n await verifyImageEngine();\n } catch (error) {\n logger.error({ err: error }, \"image engine cannot load; capture consumer stopping\");\n throw error;\n }\n}\n\n/** Sweep scratch left by a killed run; failures are logged, never fatal. */\nexport async function sweepScratch(scratchDir: string | undefined, logger: Logger): Promise<void> {\n if (scratchDir === undefined) {\n return;\n }\n try {\n await sweepOrphanedScratch(scratchDir, { logger });\n } catch (error) {\n logger.warn({ err: error }, \"scratch sweep failed\");\n }\n}\n"],"mappings":";;;;;;;;;;AAUA,SAAgB,mBACd,QACA,SACM;CACN,IAAI,UAAU,SACZ,gBAAgB;EAAE,QAAQ,QAAQ;EAAQ,SAAS,SAAS;CAAQ,CAAC;AAEzE;;AAGA,eAAsB,kBAAkB,QAA+B;CACrE,IAAI;EACF,MAAM,kBAAkB;CAC1B,SAAS,OAAO;EACd,OAAO,MAAM,EAAE,KAAK,MAAM,GAAG,qDAAqD;EAClF,MAAM;CACR;AACF;;AAGA,eAAsB,aAAa,YAAgC,QAA+B;CAChG,IAAI,eAAe,KAAA,GACjB;CAEF,IAAI;EACF,MAAM,qBAAqB,YAAY,EAAE,OAAO,CAAC;CACnD,SAAS,OAAO;EACd,OAAO,KAAK,EAAE,KAAK,MAAM,GAAG,sBAAsB;CACpD;AACF"}
@@ -0,0 +1,97 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { Logger } from "../../logger.mjs";
4
+ import { ShelfCompareOptions, ShelfImagesOptions } from "../../config.mjs";
5
+ import { PollableCaptureQueue, PollableJob } from "../../adapter/capture-queue.mjs";
6
+ import { CaptureJobKind } from "../../schema/capture-job.mjs";
7
+ import { PollCadence } from "../db-queue/cadence.mjs";
8
+ import { QueueTimers } from "../db-queue/timers.mjs";
9
+ import { CaptureHandlerOptions } from "./capture-handler.mjs";
10
+ import { ConsumerMetrics } from "./metrics.mjs";
11
+ import { RetryConfig } from "./retry.mjs";
12
+ //#region src/capture/consumer/types.d.ts
13
+ /** A polled job as the consumer sees it: database jobs carry the extra fields, others omit them. */
14
+ type ConsumerJob = PollableJob & {
15
+ /** Job kind; absent on queues that only carry capture jobs (remote, IPC). */
16
+ kind?: CaptureJobKind;
17
+ /** Id of the job row (database queue). */
18
+ jobId?: string;
19
+ /** Attempt number from 1 (database queue). */
20
+ attempt?: number;
21
+ maxAttempts?: number;
22
+ /** Kind-specific fields given at enqueue. */
23
+ data?: Record<string, unknown>;
24
+ };
25
+ /** What a handler gets besides the job. */
26
+ interface JobRunContext {
27
+ /** Logger scoped to the job (`buildId`, `jobId`, `kind`). */
28
+ logger: Logger;
29
+ /** Report progress: renews the job's lease (throttled) and tells the host (`onProgress`). */
30
+ touch(): void;
31
+ }
32
+ /** The code that runs jobs of one kind. */
33
+ interface KindHandler {
34
+ /** Run the job; resolve to ack it, reject to retry (or fail it on its last attempt). */
35
+ run(job: ConsumerJob, context: JobRunContext): Promise<void>;
36
+ /** Whether the job is well-formed; a job that is not is failed without retries. */
37
+ accepts?(job: ConsumerJob): boolean;
38
+ /** Retry delay of this kind: `baseMs * 2^n` capped at `maxMs`; defaults to the consumer's. */
39
+ backoff?: {
40
+ baseMs: number;
41
+ maxMs: number;
42
+ };
43
+ /** Stop a running job (lease lost or cancelled); must tolerate a job that already ended. */
44
+ cancel?(job: ConsumerJob): Promise<void>;
45
+ }
46
+ /** Handler registry by job kind; a plain function is shorthand for `{ run }`. */
47
+ type JobHandlers = Partial<Record<CaptureJobKind, KindHandler | KindHandler["run"] | undefined>>;
48
+ /** Consumer tuning. */
49
+ interface ConsumerConfig extends RetryConfig {
50
+ /** Jobs run at once; default sized from the CPU and memory the process can use. */
51
+ concurrency?: number;
52
+ /** Long-poll wait for queues that block inside `poll` (default 20, max 20). */
53
+ waitTimeSeconds?: number;
54
+ }
55
+ /** Options of {@link createCaptureConsumer}. */
56
+ interface CaptureConsumerOptions {
57
+ /** Queue to consume: the database queue, or the IPC queue of a forked child. */
58
+ queue: PollableCaptureQueue;
59
+ /** Capture jobs: wires the `capture` kind to the core dispatch (shorthand for `handlers.capture`). */
60
+ capture?: Omit<CaptureHandlerOptions, "enqueue" | "logger"> & Partial<Pick<CaptureHandlerOptions, "enqueue">>;
61
+ /** Handlers by kind; `publish` and `notify` register here. Overrides `capture`'s. */
62
+ handlers?: JobHandlers;
63
+ config?: ConsumerConfig;
64
+ /** Scratch directory swept at start; defaults to `capture.scratchDir`. */
65
+ scratchDir?: string;
66
+ /** Image engine, as the server's `images` option; `start()` fails when it cannot load. */
67
+ images?: ShelfImagesOptions;
68
+ /** Diff pool size, as the server's `compare` option. */
69
+ compare?: ShelfCompareOptions;
70
+ /** Called whenever a running job makes progress (the forked child reports it to its parent). */
71
+ onProgress?: (job: ConsumerJob) => void;
72
+ /** Optional observation hooks; core does not depend on any metrics library. */
73
+ metrics?: ConsumerMetrics;
74
+ logger?: Logger;
75
+ /** Clock and timers (tests inject a manual clock). */
76
+ now?: () => number;
77
+ timers?: QueueTimers;
78
+ /** Poll cadence of the database queue (default ADR 0006's). */
79
+ cadence?: PollCadence;
80
+ random?: () => number;
81
+ /** Lease renewal throttle and state-check interval overrides (tests). */
82
+ lease?: {
83
+ renewEveryMs?: number;
84
+ checkMs?: number;
85
+ };
86
+ }
87
+ /** Handle returned by {@link createCaptureConsumer}. */
88
+ interface CaptureConsumer {
89
+ /** Start consuming. Resolves once stopped and drained; rejects when the image engine cannot load. */
90
+ start(): Promise<void>;
91
+ /** Stop polling and wait for the jobs in flight to finish (SIGTERM drain). */
92
+ stop(): Promise<void>;
93
+ isRunning(): boolean;
94
+ }
95
+ //#endregion
96
+ export { CaptureConsumer, CaptureConsumerOptions, ConsumerConfig, ConsumerJob, JobHandlers, JobRunContext, KindHandler };
97
+ //# sourceMappingURL=types.d.mts.map
@@ -0,0 +1,25 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { DispatchDeps } from "./dispatch.mjs";
4
+ //#region src/capture/crash-failure.d.ts
5
+ /** What {@link failCrashedCapture} did. */
6
+ type CrashFailureOutcome =
7
+ /** The build was failed. */
8
+ "failed" |
9
+ /** The shard was failed; other shards are still open, and the last of them finishes the build. */
10
+ "shard-failed" |
11
+ /** The shard was the last one open: the build was finalized (failed, with this shard's stories). */
12
+ "finalized" |
13
+ /** The build had already ended (or is gone): nothing to do. */
14
+ "skipped";
15
+ /**
16
+ * Fail a capture job that crashed its worker past its retries. Safe to call for a build that has
17
+ * since ended: it is left alone.
18
+ */
19
+ declare function failCrashedCapture(deps: DispatchDeps, job: {
20
+ buildId: string;
21
+ shard?: string | undefined;
22
+ }, error: string): Promise<CrashFailureOutcome>;
23
+ //#endregion
24
+ export { CrashFailureOutcome, failCrashedCapture };
25
+ //# sourceMappingURL=crash-failure.d.mts.map
@@ -0,0 +1,70 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { CaptureAttemptModel } from "../models/capture-attempt.mjs";
5
+ import { BuildModel } from "../models/build.mjs";
6
+ import { CaptureShardModel } from "../models/capture-shard.mjs";
7
+ import { ProjectModel } from "../models/project.mjs";
8
+ import { reduceBuild } from "./shards/reduce.mjs";
9
+ //#region src/capture/crash-failure.ts
10
+ /**
11
+ * The terminal path for a capture job the forked parent gave up on because the child kept dying on
12
+ * it (ADR 0005 phase 3). The child never got to record the failure, so the parent writes it: the
13
+ * build fails, or, for a shard, the shard row fails and the build is reduced like any other end.
14
+ */
15
+ /**
16
+ * Fail a capture job that crashed its worker past its retries. Safe to call for a build that has
17
+ * since ended: it is left alone.
18
+ */
19
+ async function failCrashedCapture(deps, job, error) {
20
+ const outcome = job.shard ? await failShard(deps, job.buildId, job.shard, error) : await failBuild(deps, job.buildId, error);
21
+ if (outcome !== "skipped") deps.logger.error({
22
+ buildId: job.buildId,
23
+ shard: job.shard,
24
+ outcome,
25
+ error
26
+ }, "capture kept crashing its worker; failed it");
27
+ return outcome;
28
+ }
29
+ async function failBuild(deps, buildId, error) {
30
+ const loaded = await load(deps, buildId);
31
+ if (!loaded || loaded.build.status !== "capturing" && loaded.build.status !== "pending") return "skipped";
32
+ await closeOpenAttempts(deps, buildId, error);
33
+ await new BuildModel(deps.db, deps.tables).setStatus(buildId, "failed");
34
+ await deps.buildState.request(buildId);
35
+ return "failed";
36
+ }
37
+ async function failShard(deps, buildId, shardId, error) {
38
+ await new CaptureShardModel(deps.db).fail(shardId, error);
39
+ const loaded = await load(deps, buildId);
40
+ if (loaded?.build.status !== "capturing") return "skipped";
41
+ const summary = await reduceBuild({
42
+ options: deps.jobOptions,
43
+ ...loaded
44
+ });
45
+ if (!summary) return "shard-failed";
46
+ await closeFinalized(deps, loaded, summary, error);
47
+ return "finalized";
48
+ }
49
+ /** The crashed shard was the last one: close the plan's attempt and report the build's end. */
50
+ async function closeFinalized(deps, loaded, summary, error) {
51
+ if (summary.attemptId) await new CaptureAttemptModel(deps.db, deps.tables).markFinished(summary.attemptId, "failed", error, summary);
52
+ await deps.buildState.request(loaded.build.id);
53
+ }
54
+ async function load(deps, buildId) {
55
+ const build = await new BuildModel(deps.db, deps.tables).get(buildId);
56
+ const project = build ? await new ProjectModel(deps.db, deps.tables).get(build.projectId) : null;
57
+ return build && project ? {
58
+ build,
59
+ project
60
+ } : null;
61
+ }
62
+ async function closeOpenAttempts(deps, buildId, error) {
63
+ const attempts = new CaptureAttemptModel(deps.db, deps.tables);
64
+ const open = (await attempts.listByBuild(buildId)).filter((attempt) => attempt.status === "queued" || attempt.status === "running");
65
+ await Promise.all(open.map(async (attempt) => await attempts.markFinished(attempt.id, "failed", error)));
66
+ }
67
+ //#endregion
68
+ export { failCrashedCapture };
69
+
70
+ //# sourceMappingURL=crash-failure.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crash-failure.mjs","names":[],"sources":["../../src/capture/crash-failure.ts"],"sourcesContent":["/**\n * The terminal path for a capture job the forked parent gave up on because the child kept dying on\n * it (ADR 0005 phase 3). The child never got to record the failure, so the parent writes it: the\n * build fails, or, for a shard, the shard row fails and the build is reduced like any other end.\n */\nimport { BuildModel } from \"../models/build.ts\";\nimport { CaptureAttemptModel } from \"../models/capture-attempt.ts\";\nimport { CaptureShardModel } from \"../models/capture-shard.ts\";\nimport { ProjectModel } from \"../models/project.ts\";\nimport type { Build } from \"../schema/build.ts\";\nimport type { Project } from \"../schema/project.ts\";\nimport type { DispatchDeps } from \"./dispatch.ts\";\nimport { reduceBuild, type FinalSummary } from \"./shards/reduce.ts\";\n\n/** What {@link failCrashedCapture} did. */\nexport type CrashFailureOutcome =\n /** The build was failed. */\n | \"failed\"\n /** The shard was failed; other shards are still open, and the last of them finishes the build. */\n | \"shard-failed\"\n /** The shard was the last one open: the build was finalized (failed, with this shard's stories). */\n | \"finalized\"\n /** The build had already ended (or is gone): nothing to do. */\n | \"skipped\";\n\n/**\n * Fail a capture job that crashed its worker past its retries. Safe to call for a build that has\n * since ended: it is left alone.\n */\nexport async function failCrashedCapture(\n deps: DispatchDeps,\n job: { buildId: string; shard?: string | undefined },\n error: string,\n): Promise<CrashFailureOutcome> {\n const outcome = job.shard\n ? await failShard(deps, job.buildId, job.shard, error)\n : await failBuild(deps, job.buildId, error);\n if (outcome !== \"skipped\") {\n deps.logger.error(\n { buildId: job.buildId, shard: job.shard, outcome, error },\n \"capture kept crashing its worker; failed it\",\n );\n }\n return outcome;\n}\n\nasync function failBuild(\n deps: DispatchDeps,\n buildId: string,\n error: string,\n): Promise<CrashFailureOutcome> {\n const loaded = await load(deps, buildId);\n if (!loaded || (loaded.build.status !== \"capturing\" && loaded.build.status !== \"pending\")) {\n return \"skipped\";\n }\n await closeOpenAttempts(deps, buildId, error);\n await new BuildModel(deps.db, deps.tables).setStatus(buildId, \"failed\");\n await deps.buildState.request(buildId);\n return \"failed\";\n}\n\nasync function failShard(\n deps: DispatchDeps,\n buildId: string,\n shardId: string,\n error: string,\n): Promise<CrashFailureOutcome> {\n await new CaptureShardModel(deps.db).fail(shardId, error);\n const loaded = await load(deps, buildId);\n if (loaded?.build.status !== \"capturing\") {\n return \"skipped\";\n }\n const summary = await reduceBuild({ options: deps.jobOptions, ...loaded });\n if (!summary) {\n return \"shard-failed\";\n }\n await closeFinalized(deps, loaded, summary, error);\n return \"finalized\";\n}\n\n/** The crashed shard was the last one: close the plan's attempt and report the build's end. */\nasync function closeFinalized(\n deps: DispatchDeps,\n loaded: { build: Build; project: Project },\n summary: FinalSummary,\n error: string,\n): Promise<void> {\n if (summary.attemptId) {\n await new CaptureAttemptModel(deps.db, deps.tables).markFinished(\n summary.attemptId,\n \"failed\",\n error,\n summary,\n );\n }\n await deps.buildState.request(loaded.build.id);\n}\n\nasync function load(\n deps: DispatchDeps,\n buildId: string,\n): Promise<{ build: Build; project: Project } | null> {\n const build = await new BuildModel(deps.db, deps.tables).get(buildId);\n const project = build ? await new ProjectModel(deps.db, deps.tables).get(build.projectId) : null;\n return build && project ? { build, project } : null;\n}\n\nasync function closeOpenAttempts(\n deps: DispatchDeps,\n buildId: string,\n error: string,\n): Promise<void> {\n const attempts = new CaptureAttemptModel(deps.db, deps.tables);\n const open = (await attempts.listByBuild(buildId)).filter(\n (attempt) => attempt.status === \"queued\" || attempt.status === \"running\",\n );\n await Promise.all(\n open.map(async (attempt) => await attempts.markFinished(attempt.id, \"failed\", error)),\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA6BA,eAAsB,mBACpB,MACA,KACA,OAC8B;CAC9B,MAAM,UAAU,IAAI,QAChB,MAAM,UAAU,MAAM,IAAI,SAAS,IAAI,OAAO,KAAK,IACnD,MAAM,UAAU,MAAM,IAAI,SAAS,KAAK;CAC5C,IAAI,YAAY,WACd,KAAK,OAAO,MACV;EAAE,SAAS,IAAI;EAAS,OAAO,IAAI;EAAO;EAAS;CAAM,GACzD,6CACF;CAEF,OAAO;AACT;AAEA,eAAe,UACb,MACA,SACA,OAC8B;CAC9B,MAAM,SAAS,MAAM,KAAK,MAAM,OAAO;CACvC,IAAI,CAAC,UAAW,OAAO,MAAM,WAAW,eAAe,OAAO,MAAM,WAAW,WAC7E,OAAO;CAET,MAAM,kBAAkB,MAAM,SAAS,KAAK;CAC5C,MAAM,IAAI,WAAW,KAAK,IAAI,KAAK,MAAM,CAAC,CAAC,UAAU,SAAS,QAAQ;CACtE,MAAM,KAAK,WAAW,QAAQ,OAAO;CACrC,OAAO;AACT;AAEA,eAAe,UACb,MACA,SACA,SACA,OAC8B;CAC9B,MAAM,IAAI,kBAAkB,KAAK,EAAE,CAAC,CAAC,KAAK,SAAS,KAAK;CACxD,MAAM,SAAS,MAAM,KAAK,MAAM,OAAO;CACvC,IAAI,QAAQ,MAAM,WAAW,aAC3B,OAAO;CAET,MAAM,UAAU,MAAM,YAAY;EAAE,SAAS,KAAK;EAAY,GAAG;CAAO,CAAC;CACzE,IAAI,CAAC,SACH,OAAO;CAET,MAAM,eAAe,MAAM,QAAQ,SAAS,KAAK;CACjD,OAAO;AACT;;AAGA,eAAe,eACb,MACA,QACA,SACA,OACe;CACf,IAAI,QAAQ,WACV,MAAM,IAAI,oBAAoB,KAAK,IAAI,KAAK,MAAM,CAAC,CAAC,aAClD,QAAQ,WACR,UACA,OACA,OACF;CAEF,MAAM,KAAK,WAAW,QAAQ,OAAO,MAAM,EAAE;AAC/C;AAEA,eAAe,KACb,MACA,SACoD;CACpD,MAAM,QAAQ,MAAM,IAAI,WAAW,KAAK,IAAI,KAAK,MAAM,CAAC,CAAC,IAAI,OAAO;CACpE,MAAM,UAAU,QAAQ,MAAM,IAAI,aAAa,KAAK,IAAI,KAAK,MAAM,CAAC,CAAC,IAAI,MAAM,SAAS,IAAI;CAC5F,OAAO,SAAS,UAAU;EAAE;EAAO;CAAQ,IAAI;AACjD;AAEA,eAAe,kBACb,MACA,SACA,OACe;CACf,MAAM,WAAW,IAAI,oBAAoB,KAAK,IAAI,KAAK,MAAM;CAC7D,MAAM,QAAQ,MAAM,SAAS,YAAY,OAAO,EAAA,CAAG,QAChD,YAAY,QAAQ,WAAW,YAAY,QAAQ,WAAW,SACjE;CACA,MAAM,QAAQ,IACZ,KAAK,IAAI,OAAO,YAAY,MAAM,SAAS,aAAa,QAAQ,IAAI,UAAU,KAAK,CAAC,CACtF;AACF"}
@@ -0,0 +1,32 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ //#region src/capture/db-queue/cadence.d.ts
4
+ /** Poll and cancel-check cadence of the database queue (ADR 0006, decision 11). */
5
+ /** Poll interval of an idle instance. */
6
+ declare const IDLE_POLL_MS = 15000;
7
+ /** Poll interval while jobs were recently queued or found. */
8
+ declare const ACTIVE_POLL_MS = 2000;
9
+ /** How long after the last job seen an instance keeps polling at the active rate. */
10
+ declare const ACTIVE_WINDOW_MS = 60000;
11
+ /** Each delay is stretched by up to this fraction (never shortened, so idle stays ≥ 15 s). */
12
+ declare const POLL_JITTER = 0.2;
13
+ /** How often a running job reads its own row for cancel. */
14
+ declare const CANCEL_CHECK_MS = 5000;
15
+ /** Tunable cadence; tests shrink it, production uses the defaults above. */
16
+ interface PollCadence {
17
+ idleMs: number;
18
+ activeMs: number;
19
+ activeWindowMs: number;
20
+ jitter: number;
21
+ }
22
+ /** The cadence ADR 0006 fixes. */
23
+ declare const DEFAULT_CADENCE: PollCadence;
24
+ /**
25
+ * Delay before the next poll: the active interval while the last job was seen (found by a poll or
26
+ * queued locally) within the active window, otherwise the idle interval; plus jitter in
27
+ * `[0, jitter)` of it, so instances started together drift apart instead of probing in lockstep.
28
+ */
29
+ declare function nextPollDelay(lastWorkAt: number | null, now: number, cadence?: PollCadence, random?: () => number): number;
30
+ //#endregion
31
+ export { ACTIVE_POLL_MS, ACTIVE_WINDOW_MS, CANCEL_CHECK_MS, DEFAULT_CADENCE, IDLE_POLL_MS, POLL_JITTER, PollCadence, nextPollDelay };
32
+ //# sourceMappingURL=cadence.d.mts.map
@@ -0,0 +1,38 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ //#region src/capture/db-queue/cadence.ts
5
+ /** Poll and cancel-check cadence of the database queue (ADR 0006, decision 11). */
6
+ /** Poll interval of an idle instance. */
7
+ const IDLE_POLL_MS = 15e3;
8
+ /** Poll interval while jobs were recently queued or found. */
9
+ const ACTIVE_POLL_MS = 2e3;
10
+ /** How long after the last job seen an instance keeps polling at the active rate. */
11
+ const ACTIVE_WINDOW_MS = 6e4;
12
+ /** Each delay is stretched by up to this fraction (never shortened, so idle stays ≥ 15 s). */
13
+ const POLL_JITTER = .2;
14
+ /** How often a running job reads its own row for cancel. */
15
+ const CANCEL_CHECK_MS = 5e3;
16
+ /** The cadence ADR 0006 fixes. */
17
+ const DEFAULT_CADENCE = {
18
+ idleMs: IDLE_POLL_MS,
19
+ activeMs: ACTIVE_POLL_MS,
20
+ activeWindowMs: ACTIVE_WINDOW_MS,
21
+ jitter: POLL_JITTER
22
+ };
23
+ /**
24
+ * Delay before the next poll: the active interval while the last job was seen (found by a poll or
25
+ * queued locally) within the active window, otherwise the idle interval; plus jitter in
26
+ * `[0, jitter)` of it, so instances started together drift apart instead of probing in lockstep.
27
+ */
28
+ function nextPollDelay(lastWorkAt, now, cadence = DEFAULT_CADENCE, random = Math.random) {
29
+ const base = lastWorkAt !== null && now - lastWorkAt < cadence.activeWindowMs ? cadence.activeMs : cadence.idleMs;
30
+ return Math.round(base * (1 + cadence.jitter * clampUnit(random())));
31
+ }
32
+ function clampUnit(value) {
33
+ return Math.min(Math.max(value, 0), .999999);
34
+ }
35
+ //#endregion
36
+ export { ACTIVE_POLL_MS, ACTIVE_WINDOW_MS, CANCEL_CHECK_MS, DEFAULT_CADENCE, IDLE_POLL_MS, POLL_JITTER, nextPollDelay };
37
+
38
+ //# sourceMappingURL=cadence.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cadence.mjs","names":[],"sources":["../../../src/capture/db-queue/cadence.ts"],"sourcesContent":["/** Poll and cancel-check cadence of the database queue (ADR 0006, decision 11). */\n\n/** Poll interval of an idle instance. */\nexport const IDLE_POLL_MS = 15_000;\n/** Poll interval while jobs were recently queued or found. */\nexport const ACTIVE_POLL_MS = 2000;\n/** How long after the last job seen an instance keeps polling at the active rate. */\nexport const ACTIVE_WINDOW_MS = 60_000;\n/** Each delay is stretched by up to this fraction (never shortened, so idle stays ≥ 15 s). */\nexport const POLL_JITTER = 0.2;\n/** How often a running job reads its own row for cancel. */\nexport const CANCEL_CHECK_MS = 5000;\n\n/** Tunable cadence; tests shrink it, production uses the defaults above. */\nexport interface PollCadence {\n idleMs: number;\n activeMs: number;\n activeWindowMs: number;\n jitter: number;\n}\n\n/** The cadence ADR 0006 fixes. */\nexport const DEFAULT_CADENCE: PollCadence = {\n idleMs: IDLE_POLL_MS,\n activeMs: ACTIVE_POLL_MS,\n activeWindowMs: ACTIVE_WINDOW_MS,\n jitter: POLL_JITTER,\n};\n\n/**\n * Delay before the next poll: the active interval while the last job was seen (found by a poll or\n * queued locally) within the active window, otherwise the idle interval; plus jitter in\n * `[0, jitter)` of it, so instances started together drift apart instead of probing in lockstep.\n */\nexport function nextPollDelay(\n lastWorkAt: number | null,\n now: number,\n cadence: PollCadence = DEFAULT_CADENCE,\n random: () => number = Math.random,\n): number {\n const active = lastWorkAt !== null && now - lastWorkAt < cadence.activeWindowMs;\n const base = active ? cadence.activeMs : cadence.idleMs;\n return Math.round(base * (1 + cadence.jitter * clampUnit(random())));\n}\n\nfunction clampUnit(value: number): number {\n return Math.min(Math.max(value, 0), 0.999999);\n}\n"],"mappings":";;;;;;AAGA,MAAa,eAAe;;AAE5B,MAAa,iBAAiB;;AAE9B,MAAa,mBAAmB;;AAEhC,MAAa,cAAc;;AAE3B,MAAa,kBAAkB;;AAW/B,MAAa,kBAA+B;CAC1C,QAAQ;CACR,UAAU;CACV,gBAAgB;CAChB,QAAQ;AACV;;;;;;AAOA,SAAgB,cACd,YACA,KACA,UAAuB,iBACvB,SAAuB,KAAK,QACpB;CAER,MAAM,OADS,eAAe,QAAQ,MAAM,aAAa,QAAQ,iBAC3C,QAAQ,WAAW,QAAQ;CACjD,OAAO,KAAK,MAAM,QAAQ,IAAI,QAAQ,SAAS,UAAU,OAAO,CAAC,EAAE;AACrE;AAEA,SAAS,UAAU,OAAuB;CACxC,OAAO,KAAK,IAAI,KAAK,IAAI,OAAO,CAAC,GAAG,OAAQ;AAC9C"}
@@ -0,0 +1,9 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import "./types.mjs";
4
+ //#region src/capture/db-queue/claim.d.ts
5
+ /** Error recorded on a job whose last allowed attempt lost its lease. */
6
+ declare const LEASE_EXHAUSTED_ERROR = "lease expired on the final attempt";
7
+ //#endregion
8
+ export { LEASE_EXHAUSTED_ERROR };
9
+ //# sourceMappingURL=claim.d.mts.map