@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,40 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { Logger } from "../../logger.mjs";
4
+ import { ConsumerJob, KindHandler } from "../consumer/types.mjs";
5
+ import { ChildProcessHandle } from "./channel.mjs";
6
+ //#region src/capture/forked/forked-capture.d.ts
7
+ /** Options of {@link createForkedCaptureHandler}. */
8
+ interface ForkedCaptureOptions {
9
+ /** Start the capture child. Throwing counts as "fork failed" and trips the fallback. */
10
+ spawn: () => ChildProcessHandle;
11
+ /** Runs jobs in this process when forking stops being viable. */
12
+ fallback: KindHandler;
13
+ /** Longest a pushed job may go without progress before the child is declared hung and killed (0 disables). */
14
+ leaseMs?: number;
15
+ /** Non-zero exits within `crashWindowMs` that trip the fallback (default 3). */
16
+ crashLimit?: number;
17
+ crashWindowMs?: number;
18
+ /** Delay before pushing again after a crash (default 250 ms). */
19
+ restartDelayMs?: number;
20
+ /** Retries assumed for jobs that do not state `maxAttempts` (the consumer's `maxRetries`). */
21
+ maxRetries?: number;
22
+ /**
23
+ * The child kept dying on this job's last attempt, so it never recorded the failure: write it
24
+ * (the build, or the shard, fails). Runs before the job is failed on the queue.
25
+ */
26
+ onCrashed?: (job: ConsumerJob, error: string) => Promise<void>;
27
+ logger?: Logger;
28
+ }
29
+ /** A `capture` handler that runs jobs in a forked child. */
30
+ interface ForkedCaptureHandler extends KindHandler {
31
+ /** Whether forking was given up and jobs run in-process. */
32
+ readonly tripped: boolean;
33
+ /** Stop the child (SIGTERM, escalating to SIGKILL). Call after the consumer has drained. */
34
+ close(): Promise<void>;
35
+ }
36
+ /** Create the handler; the child is forked when the first job arrives. */
37
+ declare function createForkedCaptureHandler(options: ForkedCaptureOptions): ForkedCaptureHandler;
38
+ //#endregion
39
+ export { ForkedCaptureHandler, ForkedCaptureOptions, createForkedCaptureHandler };
40
+ //# sourceMappingURL=forked-capture.d.mts.map
@@ -0,0 +1,240 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { isFinalAttempt } from "../consumer/retry.mjs";
5
+ import "../db-queue/types.mjs";
6
+ import { isChildMessage } from "./protocol.mjs";
7
+ import { CrashTracker } from "./crash-tracker.mjs";
8
+ import { describeExit } from "./exit.mjs";
9
+ import { LeaseBook } from "./lease-book.mjs";
10
+ import { RunBook, jobKey } from "./runs.mjs";
11
+ //#region src/capture/forked/forked-capture.ts
12
+ /** Create the handler; the child is forked when the first job arrives. */
13
+ function createForkedCaptureHandler(options) {
14
+ const forked = new ForkedCapture(options);
15
+ return {
16
+ get tripped() {
17
+ return forked.tripped;
18
+ },
19
+ accepts: (job) => forked.accepts(job),
20
+ run: async (job, context) => {
21
+ await forked.run(job, context);
22
+ },
23
+ cancel: async (job) => {
24
+ await forked.cancel(job);
25
+ },
26
+ close: async () => {
27
+ await forked.close();
28
+ }
29
+ };
30
+ }
31
+ var ForkedCapture = class {
32
+ #options;
33
+ #runs = new RunBook();
34
+ #crashes;
35
+ #leases;
36
+ /** Jobs the consumer gave up on (lease lost, cancelled) that may still wait to be pushed. */
37
+ #abandoned = /* @__PURE__ */ new Set();
38
+ #child = null;
39
+ /** The run whose lease ran out, while its child is being killed; that run alone is at fault. */
40
+ #hung = null;
41
+ #tripped = false;
42
+ #closed = false;
43
+ constructor(options) {
44
+ this.#options = options;
45
+ this.#crashes = new CrashTracker(options.crashLimit ?? 3, options.crashWindowMs ?? 3e5);
46
+ this.#leases = new LeaseBook(options.leaseMs ?? 3e5, (runId) => {
47
+ this.#onHung(runId);
48
+ });
49
+ }
50
+ get tripped() {
51
+ return this.#tripped;
52
+ }
53
+ accepts(job) {
54
+ return this.#options.fallback.accepts?.(job) !== false;
55
+ }
56
+ async run(job, context) {
57
+ const key = jobKey(job);
58
+ try {
59
+ await this.#runUntilSettled(job, context);
60
+ } finally {
61
+ this.#abandoned.delete(key);
62
+ }
63
+ }
64
+ /** Push the job until it settles: a crash that was not this run's fault pushes it again, alone. */
65
+ async #runUntilSettled(job, context, solo = false) {
66
+ if (this.#tripped || this.#closed) {
67
+ await this.#options.fallback.run(job, context);
68
+ return;
69
+ }
70
+ const verdict = await this.#pushWhenAllowed(job, context, solo);
71
+ if (verdict !== null && await this.#conclude(job, verdict)) return;
72
+ if (verdict !== null) await delay(this.#options.restartDelayMs ?? 250);
73
+ await this.#runUntilSettled(job, context, solo || verdict !== null);
74
+ }
75
+ /** Whether the push is over: true for a done run; a failed or charged one throws; false = push again. */
76
+ async #conclude(job, verdict) {
77
+ if (verdict.kind === "done") return true;
78
+ if (verdict.kind === "failed") throw new Error(verdict.error);
79
+ if (!verdict.charged) return false;
80
+ await this.#failCrashed(job, verdict.error);
81
+ throw new Error(verdict.error);
82
+ }
83
+ /** Wait for a place, push, and wait for the verdict; null when the situation changed (tripped). */
84
+ async #pushWhenAllowed(job, context, solo) {
85
+ const run = await this.#waitForPlace(job, context, solo);
86
+ return run === null ? null : await run.verdict;
87
+ }
88
+ async #waitForPlace(job, context, solo) {
89
+ while (!this.#tripped && !this.#closed) {
90
+ if (this.#abandoned.has(jobKey(job))) throw new Error("capture job was cancelled before it ran");
91
+ if (this.#runs.canStart(solo)) return this.#push(job, context, solo);
92
+ await this.#runs.changed();
93
+ }
94
+ return null;
95
+ }
96
+ /** The consumer stopped waiting for this job: stop the child's render, or keep it from starting. */
97
+ async cancel(job) {
98
+ const run = this.#runs.runOf(job);
99
+ if (run) {
100
+ this.#child?.send({
101
+ type: "cancel",
102
+ runId: run.runId
103
+ });
104
+ return;
105
+ }
106
+ this.#abandoned.add(jobKey(job));
107
+ this.#runs.wake();
108
+ if (this.#tripped) await this.#options.fallback.cancel?.(job);
109
+ }
110
+ async close() {
111
+ this.#closed = true;
112
+ this.#shutDown();
113
+ for (const run of this.#runs.takeAll()) run.settle({
114
+ kind: "failed",
115
+ error: "capture child stopped"
116
+ });
117
+ await Promise.resolve();
118
+ }
119
+ #push(job, context, solo) {
120
+ const child = this.#ensureChild();
121
+ if (child === null) return null;
122
+ const run = this.#runs.add(job, context, solo);
123
+ this.#leases.start(run.runId);
124
+ child.send({
125
+ type: "run",
126
+ runId: run.runId,
127
+ job
128
+ });
129
+ return run;
130
+ }
131
+ #ensureChild() {
132
+ if (this.#child) return this.#child;
133
+ try {
134
+ this.#child = this.#attach(this.#options.spawn());
135
+ return this.#child;
136
+ } catch (error) {
137
+ this.#options.logger?.error({ err: error }, "failed to start capture child");
138
+ this.#trip("spawn failed");
139
+ return null;
140
+ }
141
+ }
142
+ #attach(child) {
143
+ child.onMessage((message) => {
144
+ this.#onMessage(message);
145
+ });
146
+ child.onExit((info) => {
147
+ this.#onExit(child, info);
148
+ });
149
+ return child;
150
+ }
151
+ #onMessage(message) {
152
+ if (!isChildMessage(message)) return;
153
+ const run = this.#runs.get(message.runId);
154
+ if (!run) return;
155
+ this.#onRunMessage(run, message);
156
+ }
157
+ #onRunMessage(run, message) {
158
+ if (message.type === "progress") {
159
+ this.#leases.touch(run.runId);
160
+ run.context.touch();
161
+ return;
162
+ }
163
+ this.#leases.stop(run.runId);
164
+ this.#runs.finish(run.runId, message.type === "done" ? { kind: "done" } : {
165
+ kind: "failed",
166
+ error: message.error
167
+ });
168
+ }
169
+ #onExit(child, info) {
170
+ if (this.#child !== child) return;
171
+ this.#child = null;
172
+ const crashed = info.code !== 0 || this.#runs.size > 0;
173
+ this.#settleCrashedRuns(info);
174
+ if (crashed && !this.#closed && this.#crashes.record()) this.#trip(`capture child crashed repeatedly (last: ${describeExit(info)})`);
175
+ this.#runs.wake();
176
+ }
177
+ /** Charge the crash to the hung run, or to the only run out; the others go back uncharged. */
178
+ #settleCrashedRuns(info) {
179
+ const runs = this.#runs.takeAll();
180
+ const hung = this.#hung;
181
+ this.#hung = null;
182
+ for (const run of runs) {
183
+ this.#leases.stop(run.runId);
184
+ run.settle({
185
+ kind: "crashed",
186
+ charged: hung === null ? runs.length === 1 : run.runId === hung,
187
+ error: crashMessage(info, hung === run.runId)
188
+ });
189
+ }
190
+ }
191
+ /** On its last attempt a job the child kept killing is failed here: the child never recorded it. */
192
+ async #failCrashed(job, error) {
193
+ if (!isFinalAttempt(job, this.#options.maxRetries)) return;
194
+ try {
195
+ await this.#options.onCrashed?.(job, error);
196
+ } catch (hookError) {
197
+ this.#options.logger?.error({
198
+ err: hookError,
199
+ buildId: job.buildId
200
+ }, "crash hook failed");
201
+ }
202
+ }
203
+ /** A pushed job went a full lease without progress: the child is hung, so kill it like a crash. */
204
+ #onHung(runId) {
205
+ if (!this.#runs.get(runId) || !this.#child || this.#hung !== null) return;
206
+ this.#options.logger?.error({
207
+ runId,
208
+ leaseMs: this.#options.leaseMs
209
+ }, "capture child made no progress; killing it");
210
+ this.#hung = runId;
211
+ this.#child.kill(true);
212
+ }
213
+ /** Forking is no longer viable: stop the child; waiting and crashed runs go to the fallback. */
214
+ #trip(reason) {
215
+ if (this.#tripped) return;
216
+ this.#tripped = true;
217
+ this.#options.logger?.error({ reason }, "capture child unavailable; falling back to in-process capture");
218
+ this.#shutDown();
219
+ }
220
+ #shutDown() {
221
+ this.#hung = null;
222
+ this.#leases.stopAll();
223
+ const child = this.#child;
224
+ this.#child = null;
225
+ child?.kill();
226
+ this.#runs.wake();
227
+ }
228
+ };
229
+ function crashMessage(info, hung) {
230
+ return hung ? `capture worker made no progress and was killed (${describeExit(info)})` : `capture worker exited (${describeExit(info)})`;
231
+ }
232
+ async function delay(ms) {
233
+ await new Promise((resolve) => {
234
+ setTimeout(resolve, ms).unref();
235
+ });
236
+ }
237
+ //#endregion
238
+ export { createForkedCaptureHandler };
239
+
240
+ //# sourceMappingURL=forked-capture.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forked-capture.mjs","names":[],"sources":["../../../src/capture/forked/forked-capture.ts"],"sourcesContent":["/**\n * The parent side of forked capture (ADR 0006, decision 5). The app's consumer polls the queue and\n * holds the slots; its `capture` handler is this one, which pushes each leased job to a child over\n * IPC and waits for the child's `done` or `failed`. The child's `progress` messages become the\n * job's progress, which renews the lease the consumer keeps.\n *\n * One limit covers failed attempts, lost leases and child crashes, so a crash simply fails the\n * run and the consumer retries it. A crash is charged only to the run that caused it (the hung one,\n * or the only one out); runs that shared a crashed child are pushed again, uncharged, one at a time,\n * so a repeat crash names its culprit. A crash loop, or a child that cannot start, hands everything\n * to the in-process handler.\n */\nimport type { Logger } from \"../../logger.ts\";\nimport { isFinalAttempt } from \"../consumer/retry.ts\";\nimport type { ConsumerJob, JobRunContext, KindHandler } from \"../consumer/types.ts\";\nimport { DEFAULT_LEASE_MS } from \"../db-queue/types.ts\";\nimport type { ChildProcessHandle } from \"./channel.ts\";\nimport { CrashTracker } from \"./crash-tracker.ts\";\nimport { describeExit, type ChildExit } from \"./exit.ts\";\nimport { LeaseBook } from \"./lease-book.ts\";\nimport { isChildMessage, type ChildMessage } from \"./protocol.ts\";\nimport { jobKey, RunBook, type Run, type RunVerdict } from \"./runs.ts\";\n\n/** Options of {@link createForkedCaptureHandler}. */\nexport interface ForkedCaptureOptions {\n /** Start the capture child. Throwing counts as \"fork failed\" and trips the fallback. */\n spawn: () => ChildProcessHandle;\n /** Runs jobs in this process when forking stops being viable. */\n fallback: KindHandler;\n /** Longest a pushed job may go without progress before the child is declared hung and killed (0 disables). */\n leaseMs?: number;\n /** Non-zero exits within `crashWindowMs` that trip the fallback (default 3). */\n crashLimit?: number;\n crashWindowMs?: number;\n /** Delay before pushing again after a crash (default 250 ms). */\n restartDelayMs?: number;\n /** Retries assumed for jobs that do not state `maxAttempts` (the consumer's `maxRetries`). */\n maxRetries?: number;\n /**\n * The child kept dying on this job's last attempt, so it never recorded the failure: write it\n * (the build, or the shard, fails). Runs before the job is failed on the queue.\n */\n onCrashed?: (job: ConsumerJob, error: string) => Promise<void>;\n logger?: Logger;\n}\n\n/** A `capture` handler that runs jobs in a forked child. */\nexport interface ForkedCaptureHandler extends KindHandler {\n /** Whether forking was given up and jobs run in-process. */\n readonly tripped: boolean;\n /** Stop the child (SIGTERM, escalating to SIGKILL). Call after the consumer has drained. */\n close(): Promise<void>;\n}\n\n/** Create the handler; the child is forked when the first job arrives. */\nexport function createForkedCaptureHandler(options: ForkedCaptureOptions): ForkedCaptureHandler {\n const forked = new ForkedCapture(options);\n // Plain closures, so the pieces can be passed around detached from the instance.\n return {\n get tripped() {\n return forked.tripped;\n },\n accepts: (job) => forked.accepts(job),\n run: async (job, context) => {\n await forked.run(job, context);\n },\n cancel: async (job) => {\n await forked.cancel(job);\n },\n close: async () => {\n await forked.close();\n },\n };\n}\n\nclass ForkedCapture {\n readonly #options: ForkedCaptureOptions;\n readonly #runs = new RunBook();\n readonly #crashes: CrashTracker;\n readonly #leases: LeaseBook;\n /** Jobs the consumer gave up on (lease lost, cancelled) that may still wait to be pushed. */\n readonly #abandoned = new Set<string>();\n #child: ChildProcessHandle | null = null;\n /** The run whose lease ran out, while its child is being killed; that run alone is at fault. */\n #hung: string | null = null;\n #tripped = false;\n #closed = false;\n\n constructor(options: ForkedCaptureOptions) {\n this.#options = options;\n this.#crashes = new CrashTracker(options.crashLimit ?? 3, options.crashWindowMs ?? 300_000);\n this.#leases = new LeaseBook(options.leaseMs ?? DEFAULT_LEASE_MS, (runId) => {\n this.#onHung(runId);\n });\n }\n\n get tripped(): boolean {\n return this.#tripped;\n }\n\n accepts(job: ConsumerJob): boolean {\n return this.#options.fallback.accepts?.(job) !== false;\n }\n\n async run(job: ConsumerJob, context: JobRunContext): Promise<void> {\n const key = jobKey(job);\n try {\n await this.#runUntilSettled(job, context);\n } finally {\n this.#abandoned.delete(key);\n }\n }\n\n /** Push the job until it settles: a crash that was not this run's fault pushes it again, alone. */\n async #runUntilSettled(job: ConsumerJob, context: JobRunContext, solo = false): Promise<void> {\n if (this.#tripped || this.#closed) {\n await this.#options.fallback.run(job, context);\n return;\n }\n const verdict = await this.#pushWhenAllowed(job, context, solo);\n if (verdict !== null && (await this.#conclude(job, verdict))) {\n return;\n }\n if (verdict !== null) {\n await delay(this.#options.restartDelayMs ?? 250);\n }\n await this.#runUntilSettled(job, context, solo || verdict !== null);\n }\n\n /** Whether the push is over: true for a done run; a failed or charged one throws; false = push again. */\n async #conclude(job: ConsumerJob, verdict: RunVerdict): Promise<boolean> {\n if (verdict.kind === \"done\") {\n return true;\n }\n if (verdict.kind === \"failed\") {\n throw new Error(verdict.error);\n }\n if (!verdict.charged) {\n return false;\n }\n await this.#failCrashed(job, verdict.error);\n throw new Error(verdict.error);\n }\n\n /** Wait for a place, push, and wait for the verdict; null when the situation changed (tripped). */\n async #pushWhenAllowed(\n job: ConsumerJob,\n context: JobRunContext,\n solo: boolean,\n ): Promise<RunVerdict | null> {\n const run = await this.#waitForPlace(job, context, solo);\n return run === null ? null : await run.verdict;\n }\n\n async #waitForPlace(\n job: ConsumerJob,\n context: JobRunContext,\n solo: boolean,\n ): Promise<Run | null> {\n while (!this.#tripped && !this.#closed) {\n if (this.#abandoned.has(jobKey(job))) {\n throw new Error(\"capture job was cancelled before it ran\");\n }\n if (this.#runs.canStart(solo)) {\n // Check and registration share one synchronous block: two waiters never both start solo.\n return this.#push(job, context, solo);\n }\n // oxlint-disable-next-line no-await-in-loop -- wait for a place\n await this.#runs.changed();\n }\n return null;\n }\n\n /** The consumer stopped waiting for this job: stop the child's render, or keep it from starting. */\n async cancel(job: ConsumerJob): Promise<void> {\n const run = this.#runs.runOf(job);\n if (run) {\n this.#child?.send({ type: \"cancel\", runId: run.runId });\n return;\n }\n this.#abandoned.add(jobKey(job));\n this.#runs.wake();\n if (this.#tripped) {\n await this.#options.fallback.cancel?.(job);\n }\n }\n\n async close(): Promise<void> {\n this.#closed = true;\n this.#shutDown();\n for (const run of this.#runs.takeAll()) {\n run.settle({ kind: \"failed\", error: \"capture child stopped\" });\n }\n await Promise.resolve();\n }\n\n #push(job: ConsumerJob, context: JobRunContext, solo: boolean): Run | null {\n const child = this.#ensureChild();\n if (child === null) {\n return null;\n }\n const run = this.#runs.add(job, context, solo);\n this.#leases.start(run.runId);\n child.send({ type: \"run\", runId: run.runId, job });\n return run;\n }\n\n #ensureChild(): ChildProcessHandle | null {\n if (this.#child) {\n return this.#child;\n }\n try {\n this.#child = this.#attach(this.#options.spawn());\n return this.#child;\n } catch (error) {\n this.#options.logger?.error({ err: error }, \"failed to start capture child\");\n this.#trip(\"spawn failed\");\n return null;\n }\n }\n\n #attach(child: ChildProcessHandle): ChildProcessHandle {\n child.onMessage((message) => {\n this.#onMessage(message);\n });\n child.onExit((info) => {\n this.#onExit(child, info);\n });\n return child;\n }\n\n #onMessage(message: unknown): void {\n if (!isChildMessage(message)) {\n return;\n }\n const run = this.#runs.get(message.runId);\n if (!run) {\n return;\n }\n this.#onRunMessage(run, message);\n }\n\n #onRunMessage(run: Run, message: ChildMessage): void {\n if (message.type === \"progress\") {\n this.#leases.touch(run.runId);\n run.context.touch();\n return;\n }\n this.#leases.stop(run.runId);\n this.#runs.finish(\n run.runId,\n message.type === \"done\" ? { kind: \"done\" } : { kind: \"failed\", error: message.error },\n );\n }\n\n #onExit(child: ChildProcessHandle, info: ChildExit): void {\n if (this.#child !== child) {\n return;\n }\n this.#child = null;\n // An exit the parent did not ask for while work was out is a crash whatever its code: a child\n // that returns cleanly before answering (a broken entry module) would otherwise loop forever.\n const crashed = info.code !== 0 || this.#runs.size > 0;\n this.#settleCrashedRuns(info);\n if (crashed && !this.#closed && this.#crashes.record()) {\n this.#trip(`capture child crashed repeatedly (last: ${describeExit(info)})`);\n }\n this.#runs.wake();\n }\n\n /** Charge the crash to the hung run, or to the only run out; the others go back uncharged. */\n #settleCrashedRuns(info: ChildExit): void {\n const runs = this.#runs.takeAll();\n const hung = this.#hung;\n this.#hung = null;\n for (const run of runs) {\n this.#leases.stop(run.runId);\n run.settle({\n kind: \"crashed\",\n charged: hung === null ? runs.length === 1 : run.runId === hung,\n error: crashMessage(info, hung === run.runId),\n });\n }\n }\n\n /** On its last attempt a job the child kept killing is failed here: the child never recorded it. */\n async #failCrashed(job: ConsumerJob, error: string): Promise<void> {\n if (!isFinalAttempt(job, this.#options.maxRetries)) {\n return;\n }\n try {\n await this.#options.onCrashed?.(job, error);\n } catch (hookError) {\n this.#options.logger?.error({ err: hookError, buildId: job.buildId }, \"crash hook failed\");\n }\n }\n\n /** A pushed job went a full lease without progress: the child is hung, so kill it like a crash. */\n #onHung(runId: string): void {\n if (!this.#runs.get(runId) || !this.#child || this.#hung !== null) {\n return;\n }\n this.#options.logger?.error(\n { runId, leaseMs: this.#options.leaseMs },\n \"capture child made no progress; killing it\",\n );\n this.#hung = runId;\n this.#child.kill(true);\n }\n\n /** Forking is no longer viable: stop the child; waiting and crashed runs go to the fallback. */\n #trip(reason: string): void {\n if (this.#tripped) {\n return;\n }\n this.#tripped = true;\n this.#options.logger?.error(\n { reason },\n \"capture child unavailable; falling back to in-process capture\",\n );\n this.#shutDown();\n }\n\n #shutDown(): void {\n this.#hung = null;\n this.#leases.stopAll();\n const child = this.#child;\n this.#child = null;\n child?.kill();\n this.#runs.wake();\n }\n}\n\nfunction crashMessage(info: ChildExit, hung: boolean): string {\n return hung\n ? `capture worker made no progress and was killed (${describeExit(info)})`\n : `capture worker exited (${describeExit(info)})`;\n}\n\nasync function delay(ms: number): Promise<void> {\n await new Promise<void>((resolve) => {\n setTimeout(resolve, ms).unref();\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { ProgressLease } from "../job-lease.mjs";
5
+ //#region src/capture/forked/lease-book.ts
6
+ /** Progress leases for jobs out with the capture child: no progress within the lease means the child is hung. */
7
+ /** One {@link ProgressLease} per in-flight receipt. A `leaseMs` of 0 disables the whole book. */
8
+ var LeaseBook = class {
9
+ #leaseMs;
10
+ #onExpired;
11
+ #leases = /* @__PURE__ */ new Map();
12
+ constructor(leaseMs, onExpired) {
13
+ this.#leaseMs = leaseMs;
14
+ this.#onExpired = onExpired;
15
+ }
16
+ /** Start watching a delivered job. */
17
+ start(receipt) {
18
+ if (this.#leaseMs <= 0) return;
19
+ this.stop(receipt);
20
+ const lease = new ProgressLease(this.#leaseMs);
21
+ this.#leases.set(receipt, lease);
22
+ lease.expired.then(() => {
23
+ if (this.#leases.get(receipt) === lease) {
24
+ this.#leases.delete(receipt);
25
+ this.#onExpired(receipt);
26
+ }
27
+ }, () => {});
28
+ }
29
+ /** The job under `receipt` made progress. */
30
+ touch(receipt) {
31
+ this.#leases.get(receipt)?.touch();
32
+ }
33
+ /** The job ended (or was taken back); stop watching it. */
34
+ stop(receipt) {
35
+ this.#leases.get(receipt)?.stop();
36
+ this.#leases.delete(receipt);
37
+ }
38
+ stopAll() {
39
+ for (const lease of this.#leases.values()) lease.stop();
40
+ this.#leases.clear();
41
+ }
42
+ };
43
+ //#endregion
44
+ export { LeaseBook };
45
+
46
+ //# sourceMappingURL=lease-book.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lease-book.mjs","names":[],"sources":["../../../src/capture/forked/lease-book.ts"],"sourcesContent":["/** Progress leases for jobs out with the capture child: no progress within the lease means the child is hung. */\nimport { ProgressLease } from \"../job-lease.ts\";\n\n/** One {@link ProgressLease} per in-flight receipt. A `leaseMs` of 0 disables the whole book. */\nexport class LeaseBook {\n readonly #leaseMs: number;\n readonly #onExpired: (receipt: string) => void;\n readonly #leases = new Map<string, ProgressLease>();\n\n constructor(leaseMs: number, onExpired: (receipt: string) => void) {\n this.#leaseMs = leaseMs;\n this.#onExpired = onExpired;\n }\n\n /** Start watching a delivered job. */\n start(receipt: string): void {\n if (this.#leaseMs <= 0) {\n return;\n }\n this.stop(receipt);\n const lease = new ProgressLease(this.#leaseMs);\n this.#leases.set(receipt, lease);\n lease.expired.then(\n () => {\n if (this.#leases.get(receipt) === lease) {\n this.#leases.delete(receipt);\n this.#onExpired(receipt);\n }\n },\n () => {},\n );\n }\n\n /** The job under `receipt` made progress. */\n touch(receipt: string): void {\n this.#leases.get(receipt)?.touch();\n }\n\n /** The job ended (or was taken back); stop watching it. */\n stop(receipt: string): void {\n this.#leases.get(receipt)?.stop();\n this.#leases.delete(receipt);\n }\n\n stopAll(): void {\n for (const lease of this.#leases.values()) {\n lease.stop();\n }\n this.#leases.clear();\n }\n}\n"],"mappings":";;;;;;;AAIA,IAAa,YAAb,MAAuB;CACrB;CACA;CACA,0BAAmB,IAAI,IAA2B;CAElD,YAAY,SAAiB,WAAsC;EACjE,KAAK,WAAW;EAChB,KAAK,aAAa;CACpB;;CAGA,MAAM,SAAuB;EAC3B,IAAI,KAAK,YAAY,GACnB;EAEF,KAAK,KAAK,OAAO;EACjB,MAAM,QAAQ,IAAI,cAAc,KAAK,QAAQ;EAC7C,KAAK,QAAQ,IAAI,SAAS,KAAK;EAC/B,MAAM,QAAQ,WACN;GACJ,IAAI,KAAK,QAAQ,IAAI,OAAO,MAAM,OAAO;IACvC,KAAK,QAAQ,OAAO,OAAO;IAC3B,KAAK,WAAW,OAAO;GACzB;EACF,SACM,CAAC,CACT;CACF;;CAGA,MAAM,SAAuB;EAC3B,KAAK,QAAQ,IAAI,OAAO,CAAC,EAAE,MAAM;CACnC;;CAGA,KAAK,SAAuB;EAC1B,KAAK,QAAQ,IAAI,OAAO,CAAC,EAAE,KAAK;EAChC,KAAK,QAAQ,OAAO,OAAO;CAC7B;CAEA,UAAgB;EACd,KAAK,MAAM,SAAS,KAAK,QAAQ,OAAO,GACtC,MAAM,KAAK;EAEb,KAAK,QAAQ,MAAM;CACrB;AACF"}
@@ -0,0 +1,38 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { ConsumerJob } from "../consumer/types.mjs";
4
+ //#region src/capture/forked/protocol.d.ts
5
+ /** Parent to child. `runId` names one push of a job; a re-push after a crash gets a new one. */
6
+ type ParentMessage =
7
+ /** Run this leased job. The child holds the same database, storage and runner configuration. */
8
+ {
9
+ type: "run";
10
+ runId: string;
11
+ job: ConsumerJob;
12
+ } |
13
+ /** The lease is lost or the job was cancelled: stop the render (`runner.cancel`). */
14
+ {
15
+ type: "cancel";
16
+ runId: string;
17
+ };
18
+ /** Child to parent. */
19
+ type ChildMessage =
20
+ /** The run made progress; the parent renews the job's lease. */
21
+ {
22
+ type: "progress";
23
+ runId: string;
24
+ } |
25
+ /** The run finished; the parent acknowledges the job. */
26
+ {
27
+ type: "done";
28
+ runId: string;
29
+ } |
30
+ /** The run threw; the parent retries or fails the job by its attempt count. */
31
+ {
32
+ type: "failed";
33
+ runId: string;
34
+ error: string;
35
+ };
36
+ //#endregion
37
+ export { ChildMessage, ParentMessage };
38
+ //# sourceMappingURL=protocol.d.mts.map
@@ -0,0 +1,25 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ //#region src/capture/forked/protocol.ts
5
+ function isRecord(value) {
6
+ return typeof value === "object" && value !== null;
7
+ }
8
+ function isJob(value) {
9
+ return isRecord(value) && typeof value["buildId"] === "string" && value["buildId"].length > 0;
10
+ }
11
+ /** Runtime guard for a message arriving from the child (never trust an IPC payload's shape). */
12
+ function isChildMessage(value) {
13
+ if (!isRecord(value) || typeof value["runId"] !== "string") return false;
14
+ const type = value["type"];
15
+ return type === "progress" || type === "done" || type === "failed" && typeof value["error"] === "string";
16
+ }
17
+ /** Runtime guard for a message arriving from the parent. */
18
+ function isParentMessage(value) {
19
+ if (!isRecord(value) || typeof value["runId"] !== "string") return false;
20
+ return value["type"] === "cancel" || value["type"] === "run" && isJob(value["job"]);
21
+ }
22
+ //#endregion
23
+ export { isChildMessage, isParentMessage };
24
+
25
+ //# sourceMappingURL=protocol.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.mjs","names":[],"sources":["../../../src/capture/forked/protocol.ts"],"sourcesContent":["/**\n * Wire protocol between the app's forked capture handler (parent) and `runCaptureChild` (child).\n * Plain JSON only. The parent owns the queue: it pushes each leased job to the child, and the\n * child only reports on it (ADR 0006, decision 5), so there is no `poll`, `enqueue`, `ack` or `nack`.\n */\nimport type { ConsumerJob } from \"../consumer/types.ts\";\n\n/** Parent to child. `runId` names one push of a job; a re-push after a crash gets a new one. */\nexport type ParentMessage =\n /** Run this leased job. The child holds the same database, storage and runner configuration. */\n | { type: \"run\"; runId: string; job: ConsumerJob }\n /** The lease is lost or the job was cancelled: stop the render (`runner.cancel`). */\n | { type: \"cancel\"; runId: string };\n\n/** Child to parent. */\nexport type ChildMessage =\n /** The run made progress; the parent renews the job's lease. */\n | { type: \"progress\"; runId: string }\n /** The run finished; the parent acknowledges the job. */\n | { type: \"done\"; runId: string }\n /** The run threw; the parent retries or fails the job by its attempt count. */\n | { type: \"failed\"; runId: string; error: string };\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction isJob(value: unknown): value is ConsumerJob {\n return isRecord(value) && typeof value[\"buildId\"] === \"string\" && value[\"buildId\"].length > 0;\n}\n\n/** Runtime guard for a message arriving from the child (never trust an IPC payload's shape). */\nexport function isChildMessage(value: unknown): value is ChildMessage {\n if (!isRecord(value) || typeof value[\"runId\"] !== \"string\") {\n return false;\n }\n const type = value[\"type\"];\n return (\n type === \"progress\" ||\n type === \"done\" ||\n (type === \"failed\" && typeof value[\"error\"] === \"string\")\n );\n}\n\n/** Runtime guard for a message arriving from the parent. */\nexport function isParentMessage(value: unknown): value is ParentMessage {\n if (!isRecord(value) || typeof value[\"runId\"] !== \"string\") {\n return false;\n }\n return value[\"type\"] === \"cancel\" || (value[\"type\"] === \"run\" && isJob(value[\"job\"]));\n}\n"],"mappings":";;;;AAuBA,SAAS,SAAS,OAAkD;CAClE,OAAO,OAAO,UAAU,YAAY,UAAU;AAChD;AAEA,SAAS,MAAM,OAAsC;CACnD,OAAO,SAAS,KAAK,KAAK,OAAO,MAAM,eAAe,YAAY,MAAM,UAAU,CAAC,SAAS;AAC9F;;AAGA,SAAgB,eAAe,OAAuC;CACpE,IAAI,CAAC,SAAS,KAAK,KAAK,OAAO,MAAM,aAAa,UAChD,OAAO;CAET,MAAM,OAAO,MAAM;CACnB,OACE,SAAS,cACT,SAAS,UACR,SAAS,YAAY,OAAO,MAAM,aAAa;AAEpD;;AAGA,SAAgB,gBAAgB,OAAwC;CACtE,IAAI,CAAC,SAAS,KAAK,KAAK,OAAO,MAAM,aAAa,UAChD,OAAO;CAET,OAAO,MAAM,YAAY,YAAa,MAAM,YAAY,SAAS,MAAM,MAAM,MAAM;AACrF"}
@@ -0,0 +1,75 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ //#region src/capture/forked/runs.ts
5
+ /** In-flight runs by id, plus the admission rule for solo runs and a signal for waiters. */
6
+ var RunBook = class {
7
+ #runs = /* @__PURE__ */ new Map();
8
+ #waiters = [];
9
+ #ids = 0;
10
+ /** Register a run (call right after {@link canStart}, with no await between). */
11
+ add(job, context, solo) {
12
+ this.#ids += 1;
13
+ const { promise, resolve } = Promise.withResolvers();
14
+ const run = {
15
+ runId: `run-${this.#ids}`,
16
+ job,
17
+ context,
18
+ solo,
19
+ settle: resolve,
20
+ verdict: promise
21
+ };
22
+ this.#runs.set(run.runId, run);
23
+ return run;
24
+ }
25
+ get(runId) {
26
+ return this.#runs.get(runId);
27
+ }
28
+ /** The run of `job` (by lease receipt, else build and shard), when it is out with the child. */
29
+ runOf(job) {
30
+ return [...this.#runs.values()].find((run) => jobKey(run.job) === jobKey(job));
31
+ }
32
+ get size() {
33
+ return this.#runs.size;
34
+ }
35
+ /** A solo run waits for an empty child and then runs alone; nothing starts beside it. */
36
+ canStart(solo) {
37
+ const runs = [...this.#runs.values()];
38
+ if (runs.some((run) => run.solo)) return false;
39
+ return solo ? runs.length === 0 : true;
40
+ }
41
+ /** Settle and remove one run, then wake whoever waits for a place. */
42
+ finish(runId, verdict) {
43
+ const run = this.#runs.get(runId);
44
+ if (run) {
45
+ this.#runs.delete(runId);
46
+ run.settle(verdict);
47
+ }
48
+ this.wake();
49
+ }
50
+ /** Remove every run (the child that held them is gone), returning them. */
51
+ takeAll() {
52
+ const runs = [...this.#runs.values()];
53
+ this.#runs.clear();
54
+ return runs;
55
+ }
56
+ /** Resolves at the next {@link wake}. */
57
+ async changed() {
58
+ await new Promise((resolve) => {
59
+ this.#waiters.push(resolve);
60
+ });
61
+ }
62
+ wake() {
63
+ const waiters = this.#waiters;
64
+ this.#waiters = [];
65
+ for (const resolve of waiters) resolve();
66
+ }
67
+ };
68
+ /** Identity of a job across redeliveries of the same push loop: its lease receipt, else build and shard. */
69
+ function jobKey(job) {
70
+ return job.receipt ?? `${job.buildId}:${job.shard ?? ""}`;
71
+ }
72
+ //#endregion
73
+ export { RunBook, jobKey };
74
+
75
+ //# sourceMappingURL=runs.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runs.mjs","names":[],"sources":["../../../src/capture/forked/runs.ts"],"sourcesContent":["/** The runs pushed to the capture child and not yet settled, and who may start next. */\nimport type { ConsumerJob, JobRunContext } from \"../consumer/types.ts\";\n\n/** How a push ended. */\nexport type RunVerdict =\n | { kind: \"done\" }\n | { kind: \"failed\"; error: string }\n /** The child died under it; `charged` when this run is the one that took it down. */\n | { kind: \"crashed\"; charged: boolean; error: string };\n\n/** One push of a job to the child. */\nexport interface Run {\n runId: string;\n job: ConsumerJob;\n context: JobRunContext;\n /** Runs alone: it shared a crashed child, so a repeat crash will name its culprit. */\n solo: boolean;\n settle: (verdict: RunVerdict) => void;\n verdict: Promise<RunVerdict>;\n}\n\n/** In-flight runs by id, plus the admission rule for solo runs and a signal for waiters. */\nexport class RunBook {\n readonly #runs = new Map<string, Run>();\n #waiters: (() => void)[] = [];\n #ids = 0;\n\n /** Register a run (call right after {@link canStart}, with no await between). */\n add(job: ConsumerJob, context: JobRunContext, solo: boolean): Run {\n this.#ids += 1;\n const { promise, resolve } = Promise.withResolvers<RunVerdict>();\n const run: Run = {\n runId: `run-${this.#ids}`,\n job,\n context,\n solo,\n settle: resolve,\n verdict: promise,\n };\n this.#runs.set(run.runId, run);\n return run;\n }\n\n get(runId: string): Run | undefined {\n return this.#runs.get(runId);\n }\n\n /** The run of `job` (by lease receipt, else build and shard), when it is out with the child. */\n runOf(job: ConsumerJob): Run | undefined {\n return [...this.#runs.values()].find((run) => jobKey(run.job) === jobKey(job));\n }\n\n get size(): number {\n return this.#runs.size;\n }\n\n /** A solo run waits for an empty child and then runs alone; nothing starts beside it. */\n canStart(solo: boolean): boolean {\n const runs = [...this.#runs.values()];\n if (runs.some((run) => run.solo)) {\n return false;\n }\n return solo ? runs.length === 0 : true;\n }\n\n /** Settle and remove one run, then wake whoever waits for a place. */\n finish(runId: string, verdict: RunVerdict): void {\n const run = this.#runs.get(runId);\n if (run) {\n this.#runs.delete(runId);\n run.settle(verdict);\n }\n this.wake();\n }\n\n /** Remove every run (the child that held them is gone), returning them. */\n takeAll(): Run[] {\n const runs = [...this.#runs.values()];\n this.#runs.clear();\n return runs;\n }\n\n /** Resolves at the next {@link wake}. */\n async changed(): Promise<void> {\n await new Promise<void>((resolve) => {\n this.#waiters.push(resolve);\n });\n }\n\n wake(): void {\n const waiters = this.#waiters;\n this.#waiters = [];\n for (const resolve of waiters) {\n resolve();\n }\n }\n}\n\n/** Identity of a job across redeliveries of the same push loop: its lease receipt, else build and shard. */\nexport function jobKey(job: ConsumerJob): string {\n return job.receipt ?? `${job.buildId}:${job.shard ?? \"\"}`;\n}\n"],"mappings":";;;;;AAsBA,IAAa,UAAb,MAAqB;CACnB,wBAAiB,IAAI,IAAiB;CACtC,WAA2B,CAAC;CAC5B,OAAO;;CAGP,IAAI,KAAkB,SAAwB,MAAoB;EAChE,KAAK,QAAQ;EACb,MAAM,EAAE,SAAS,YAAY,QAAQ,cAA0B;EAC/D,MAAM,MAAW;GACf,OAAO,OAAO,KAAK;GACnB;GACA;GACA;GACA,QAAQ;GACR,SAAS;EACX;EACA,KAAK,MAAM,IAAI,IAAI,OAAO,GAAG;EAC7B,OAAO;CACT;CAEA,IAAI,OAAgC;EAClC,OAAO,KAAK,MAAM,IAAI,KAAK;CAC7B;;CAGA,MAAM,KAAmC;EACvC,OAAO,CAAC,GAAG,KAAK,MAAM,OAAO,CAAC,CAAC,CAAC,MAAM,QAAQ,OAAO,IAAI,GAAG,MAAM,OAAO,GAAG,CAAC;CAC/E;CAEA,IAAI,OAAe;EACjB,OAAO,KAAK,MAAM;CACpB;;CAGA,SAAS,MAAwB;EAC/B,MAAM,OAAO,CAAC,GAAG,KAAK,MAAM,OAAO,CAAC;EACpC,IAAI,KAAK,MAAM,QAAQ,IAAI,IAAI,GAC7B,OAAO;EAET,OAAO,OAAO,KAAK,WAAW,IAAI;CACpC;;CAGA,OAAO,OAAe,SAA2B;EAC/C,MAAM,MAAM,KAAK,MAAM,IAAI,KAAK;EAChC,IAAI,KAAK;GACP,KAAK,MAAM,OAAO,KAAK;GACvB,IAAI,OAAO,OAAO;EACpB;EACA,KAAK,KAAK;CACZ;;CAGA,UAAiB;EACf,MAAM,OAAO,CAAC,GAAG,KAAK,MAAM,OAAO,CAAC;EACpC,KAAK,MAAM,MAAM;EACjB,OAAO;CACT;;CAGA,MAAM,UAAyB;EAC7B,MAAM,IAAI,SAAe,YAAY;GACnC,KAAK,SAAS,KAAK,OAAO;EAC5B,CAAC;CACH;CAEA,OAAa;EACX,MAAM,UAAU,KAAK;EACrB,KAAK,WAAW,CAAC;EACjB,KAAK,MAAM,WAAW,SACpB,QAAQ;CAEZ;AACF;;AAGA,SAAgB,OAAO,KAA0B;CAC/C,OAAO,IAAI,WAAW,GAAG,IAAI,QAAQ,GAAG,IAAI,SAAS;AACvD"}