@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.
- package/README.md +49 -32
- package/dist/adapter/capture-queue.d.mts +44 -2
- package/dist/adapter/capture-runner.d.mts +127 -10
- package/dist/adapter/capture-runner.mjs +5 -12
- package/dist/adapter/database.d.mts +27 -8
- package/dist/adapter/database.mjs.map +1 -1
- package/dist/adapter/design.d.mts +85 -0
- package/dist/adapter/design.mjs +28 -0
- package/dist/adapter/design.mjs.map +1 -0
- package/dist/adapter/metadata.d.mts +3 -10
- package/dist/adapter/notifier.d.mts +6 -3
- package/dist/adapter/notifier.mjs +5 -1
- package/dist/adapter/oauth.d.mts +52 -0
- package/dist/adapter/setup.d.mts +4 -5
- package/dist/adapter/setup.mjs +2 -1
- package/dist/adapter/setup.mjs.map +1 -1
- package/dist/adapter/webhook-events.mjs +1 -1
- package/dist/adapters/capture-target-offers.d.mts +28 -0
- package/dist/adapters/capture-target-offers.mjs +58 -0
- package/dist/adapters/capture-target-offers.mjs.map +1 -0
- package/dist/adapters/capture-target.d.mts +71 -0
- package/dist/adapters/capture-target.mjs +76 -0
- package/dist/adapters/capture-target.mjs.map +1 -0
- package/dist/adapters/notifier/build-comment.d.mts +24 -0
- package/dist/adapters/notifier/build-comment.mjs +24 -0
- package/dist/adapters/notifier/build-comment.mjs.map +1 -0
- package/dist/adapters/notifier/build-state.d.mts +99 -0
- package/dist/adapters/notifier/build-state.mjs +218 -0
- package/dist/adapters/notifier/build-state.mjs.map +1 -0
- package/dist/adapters/notifier/events.d.mts +4 -2
- package/dist/adapters/notifier/events.mjs +31 -18
- package/dist/adapters/notifier/events.mjs.map +1 -1
- package/dist/adapters/notifier/provider.d.mts +53 -7
- package/dist/adapters/notifier/provider.mjs +12 -0
- package/dist/adapters/notifier/provider.mjs.map +1 -0
- package/dist/adapters/notifier/state.d.mts +24 -0
- package/dist/adapters/notifier/state.mjs +66 -0
- package/dist/adapters/notifier/state.mjs.map +1 -0
- package/dist/adapters/notifier/types.d.mts +51 -2
- package/dist/adapters/remote-browser.d.mts +87 -0
- package/dist/adapters/remote-browser.mjs +103 -0
- package/dist/adapters/remote-browser.mjs.map +1 -0
- package/dist/adapters/render-runs.d.mts +25 -0
- package/dist/adapters/render-runs.mjs +41 -0
- package/dist/adapters/render-runs.mjs.map +1 -0
- package/dist/adapters/setup-runner.mjs +2 -4
- package/dist/adapters/setup-runner.mjs.map +1 -1
- package/dist/adapters/sources.d.mts +12 -0
- package/dist/adapters/sources.mjs +20 -0
- package/dist/adapters/sources.mjs.map +1 -0
- package/dist/adapters/validate.mjs +25 -18
- package/dist/adapters/validate.mjs.map +1 -1
- package/dist/ai.d.mts +105 -0
- package/dist/ai.mjs +19 -0
- package/dist/ai.mjs.map +1 -0
- package/dist/capture/a11y/axe-source.d.mts +18 -0
- package/dist/capture/a11y/axe-source.mjs +37 -0
- package/dist/capture/a11y/axe-source.mjs.map +1 -0
- package/dist/capture/a11y/in-page.d.mts +27 -0
- package/dist/capture/a11y/in-page.mjs +60 -0
- package/dist/capture/a11y/in-page.mjs.map +1 -0
- package/dist/capture/a11y/map.d.mts +24 -0
- package/dist/capture/a11y/map.mjs +48 -0
- package/dist/capture/a11y/map.mjs.map +1 -0
- package/dist/capture/a11y/options.d.mts +23 -0
- package/dist/capture/a11y/options.mjs +75 -0
- package/dist/capture/a11y/options.mjs.map +1 -0
- package/dist/capture/adapter.d.mts +41 -3
- package/dist/capture/adapter.mjs.map +1 -1
- package/dist/capture/affected.d.mts +2 -0
- package/dist/capture/affected.mjs +13 -4
- package/dist/capture/affected.mjs.map +1 -1
- package/dist/capture/attempt-logger.mjs +84 -0
- package/dist/capture/attempt-logger.mjs.map +1 -0
- package/dist/capture/browsers.d.mts +35 -0
- package/dist/capture/browsers.mjs +67 -0
- package/dist/capture/browsers.mjs.map +1 -0
- package/dist/capture/build-state/deliver.d.mts +32 -0
- package/dist/capture/build-state/deliver.mjs +118 -0
- package/dist/capture/build-state/deliver.mjs.map +1 -0
- package/dist/capture/build-state/index.d.mts +8 -0
- package/dist/capture/build-state/notify-job.d.mts +56 -0
- package/dist/capture/build-state/notify-job.mjs +133 -0
- package/dist/capture/build-state/notify-job.mjs.map +1 -0
- package/dist/capture/build-state/publisher.d.mts +23 -0
- package/dist/capture/build-state/publisher.mjs +107 -0
- package/dist/capture/build-state/publisher.mjs.map +1 -0
- package/dist/capture/build-state/resume.d.mts +9 -0
- package/dist/capture/build-state/resume.mjs +47 -0
- package/dist/capture/build-state/resume.mjs.map +1 -0
- package/dist/capture/build-state/types.d.mts +49 -0
- package/dist/capture/catalog/catalogue.d.mts +22 -0
- package/dist/capture/catalog/catalogue.mjs +53 -0
- package/dist/capture/catalog/catalogue.mjs.map +1 -0
- package/dist/capture/catalog/components.d.mts +23 -0
- package/dist/capture/catalog/components.mjs +55 -0
- package/dist/capture/catalog/components.mjs.map +1 -0
- package/dist/capture/catalog/history.d.mts +30 -0
- package/dist/capture/catalog/history.mjs +51 -0
- package/dist/capture/catalog/history.mjs.map +1 -0
- package/dist/capture/catalog/hook.d.mts +14 -0
- package/dist/capture/catalog/hook.mjs +19 -0
- package/dist/capture/catalog/hook.mjs.map +1 -0
- package/dist/capture/catalog/index.d.mts +11 -0
- package/dist/capture/catalog/load.d.mts +26 -0
- package/dist/capture/catalog/load.mjs +49 -0
- package/dist/capture/catalog/load.mjs.map +1 -0
- package/dist/capture/catalog/parse.d.mts +15 -0
- package/dist/capture/catalog/parse.mjs +56 -0
- package/dist/capture/catalog/parse.mjs.map +1 -0
- package/dist/capture/catalog/read.d.mts +12 -0
- package/dist/capture/catalog/read.mjs +33 -0
- package/dist/capture/catalog/read.mjs.map +1 -0
- package/dist/capture/catalog/types.d.mts +33 -0
- package/dist/capture/consumer/capture-handler.d.mts +58 -0
- package/dist/capture/consumer/capture-handler.mjs +118 -0
- package/dist/capture/consumer/capture-handler.mjs.map +1 -0
- package/dist/capture/consumer/idle.d.mts +17 -0
- package/dist/capture/consumer/idle.mjs +25 -0
- package/dist/capture/consumer/idle.mjs.map +1 -0
- package/dist/capture/consumer/index.d.mts +13 -0
- package/dist/capture/consumer/index.mjs +139 -0
- package/dist/capture/consumer/index.mjs.map +1 -0
- package/dist/capture/consumer/loop.mjs +110 -0
- package/dist/capture/consumer/loop.mjs.map +1 -0
- package/dist/capture/consumer/metrics.d.mts +41 -0
- package/dist/capture/consumer/metrics.mjs +38 -0
- package/dist/capture/consumer/metrics.mjs.map +1 -0
- package/dist/capture/consumer/retry.d.mts +33 -0
- package/dist/capture/consumer/retry.mjs +34 -0
- package/dist/capture/consumer/retry.mjs.map +1 -0
- package/dist/capture/consumer/run-job.mjs +195 -0
- package/dist/capture/consumer/run-job.mjs.map +1 -0
- package/dist/capture/consumer/startup.mjs +38 -0
- package/dist/capture/consumer/startup.mjs.map +1 -0
- package/dist/capture/consumer/types.d.mts +97 -0
- package/dist/capture/crash-failure.d.mts +25 -0
- package/dist/capture/crash-failure.mjs +70 -0
- package/dist/capture/crash-failure.mjs.map +1 -0
- package/dist/capture/db-queue/cadence.d.mts +32 -0
- package/dist/capture/db-queue/cadence.mjs +38 -0
- package/dist/capture/db-queue/cadence.mjs.map +1 -0
- package/dist/capture/db-queue/claim.d.mts +9 -0
- package/dist/capture/db-queue/claim.mjs +132 -0
- package/dist/capture/db-queue/claim.mjs.map +1 -0
- package/dist/capture/db-queue/clock.mjs +12 -0
- package/dist/capture/db-queue/clock.mjs.map +1 -0
- package/dist/capture/db-queue/codec.mjs +76 -0
- package/dist/capture/db-queue/codec.mjs.map +1 -0
- package/dist/capture/db-queue/index.d.mts +20 -0
- package/dist/capture/db-queue/index.mjs +89 -0
- package/dist/capture/db-queue/index.mjs.map +1 -0
- package/dist/capture/db-queue/keeper.d.mts +36 -0
- package/dist/capture/db-queue/keeper.mjs +92 -0
- package/dist/capture/db-queue/keeper.mjs.map +1 -0
- package/dist/capture/db-queue/lease.mjs +46 -0
- package/dist/capture/db-queue/lease.mjs.map +1 -0
- package/dist/capture/db-queue/lifecycle.mjs +36 -0
- package/dist/capture/db-queue/lifecycle.mjs.map +1 -0
- package/dist/capture/db-queue/operations.mjs +94 -0
- package/dist/capture/db-queue/operations.mjs.map +1 -0
- package/dist/capture/db-queue/poll-loop.d.mts +30 -0
- package/dist/capture/db-queue/poll-loop.mjs +89 -0
- package/dist/capture/db-queue/poll-loop.mjs.map +1 -0
- package/dist/capture/db-queue/poll.d.mts +10 -0
- package/dist/capture/db-queue/poll.mjs +69 -0
- package/dist/capture/db-queue/poll.mjs.map +1 -0
- package/dist/capture/db-queue/timers.d.mts +12 -0
- package/dist/capture/db-queue/timers.mjs +37 -0
- package/dist/capture/db-queue/timers.mjs.map +1 -0
- package/dist/capture/db-queue/types.d.mts +83 -0
- package/dist/capture/db-queue/types.mjs +12 -0
- package/dist/capture/db-queue/types.mjs.map +1 -0
- package/dist/capture/db-queue/wake.mjs +48 -0
- package/dist/capture/db-queue/wake.mjs.map +1 -0
- package/dist/capture/design.mjs +39 -0
- package/dist/capture/design.mjs.map +1 -0
- package/dist/capture/diff-record.mjs +23 -3
- package/dist/capture/diff-record.mjs.map +1 -1
- package/dist/capture/dispatch.d.mts +12 -7
- package/dist/capture/dispatch.mjs +86 -107
- package/dist/capture/dispatch.mjs.map +1 -1
- package/dist/capture/enrich.mjs +60 -0
- package/dist/capture/enrich.mjs.map +1 -0
- package/dist/capture/failure-sets.mjs +22 -0
- package/dist/capture/failure-sets.mjs.map +1 -0
- package/dist/capture/fingerprint.d.mts +42 -0
- package/dist/capture/fingerprint.mjs +83 -0
- package/dist/capture/fingerprint.mjs.map +1 -0
- package/dist/capture/forked/channel.d.mts +25 -0
- package/dist/capture/forked/child.d.mts +34 -0
- package/dist/capture/forked/child.mjs +202 -0
- package/dist/capture/forked/child.mjs.map +1 -0
- package/dist/capture/forked/crash-tracker.mjs +23 -0
- package/dist/capture/forked/crash-tracker.mjs.map +1 -0
- package/dist/capture/forked/decision.d.mts +26 -0
- package/dist/capture/forked/decision.mjs +27 -0
- package/dist/capture/forked/decision.mjs.map +1 -0
- package/dist/capture/forked/exit.mjs +12 -0
- package/dist/capture/forked/exit.mjs.map +1 -0
- package/dist/capture/forked/fork.d.mts +31 -0
- package/dist/capture/forked/fork.mjs +93 -0
- package/dist/capture/forked/fork.mjs.map +1 -0
- package/dist/capture/forked/forked-capture.d.mts +40 -0
- package/dist/capture/forked/forked-capture.mjs +240 -0
- package/dist/capture/forked/forked-capture.mjs.map +1 -0
- package/dist/capture/forked/lease-book.mjs +46 -0
- package/dist/capture/forked/lease-book.mjs.map +1 -0
- package/dist/capture/forked/protocol.d.mts +38 -0
- package/dist/capture/forked/protocol.mjs +25 -0
- package/dist/capture/forked/protocol.mjs.map +1 -0
- package/dist/capture/forked/runs.mjs +75 -0
- package/dist/capture/forked/runs.mjs.map +1 -0
- package/dist/capture/isolate.mjs +141 -0
- package/dist/capture/isolate.mjs.map +1 -0
- package/dist/capture/job-lease.d.mts +28 -0
- package/dist/capture/job-lease.mjs +57 -0
- package/dist/capture/job-lease.mjs.map +1 -0
- package/dist/capture/matrix.mjs +298 -0
- package/dist/capture/matrix.mjs.map +1 -0
- package/dist/capture/modes.d.mts +25 -0
- package/dist/capture/modes.mjs +107 -0
- package/dist/capture/modes.mjs.map +1 -0
- package/dist/capture/orchestrator.d.mts +37 -5
- package/dist/capture/orchestrator.mjs +118 -107
- package/dist/capture/orchestrator.mjs.map +1 -1
- package/dist/capture/pipeline.d.mts +9 -2
- package/dist/capture/pipeline.mjs +250 -45
- package/dist/capture/pipeline.mjs.map +1 -1
- package/dist/capture/progress.d.mts +11 -0
- package/dist/capture/progress.mjs +28 -0
- package/dist/capture/progress.mjs.map +1 -0
- package/dist/capture/publish/accept.d.mts +39 -0
- package/dist/capture/publish/accept.mjs +28 -0
- package/dist/capture/publish/accept.mjs.map +1 -0
- package/dist/capture/publish/complete.d.mts +46 -0
- package/dist/capture/publish/complete.mjs +64 -0
- package/dist/capture/publish/complete.mjs.map +1 -0
- package/dist/capture/publish/extract.d.mts +20 -0
- package/dist/capture/publish/extract.mjs +119 -0
- package/dist/capture/publish/extract.mjs.map +1 -0
- package/dist/capture/publish/handler.d.mts +28 -0
- package/dist/capture/publish/handler.mjs +62 -0
- package/dist/capture/publish/handler.mjs.map +1 -0
- package/dist/capture/publish/index.d.mts +10 -0
- package/dist/capture/publish/limits.d.mts +37 -0
- package/dist/capture/publish/limits.mjs +39 -0
- package/dist/capture/publish/limits.mjs.map +1 -0
- package/dist/capture/publish/published.d.mts +15 -0
- package/dist/capture/publish/published.mjs +18 -0
- package/dist/capture/publish/published.mjs.map +1 -0
- package/dist/capture/publish/run.d.mts +33 -0
- package/dist/capture/publish/run.mjs +30 -0
- package/dist/capture/publish/run.mjs.map +1 -0
- package/dist/capture/rediff.d.mts +1 -1
- package/dist/capture/rediff.mjs +4 -3
- package/dist/capture/rediff.mjs.map +1 -1
- package/dist/capture/render.mjs +53 -0
- package/dist/capture/render.mjs.map +1 -0
- package/dist/capture/scratch.d.mts +44 -0
- package/dist/capture/scratch.mjs +138 -0
- package/dist/capture/scratch.mjs.map +1 -0
- package/dist/capture/shard-plan.mjs +128 -0
- package/dist/capture/shard-plan.mjs.map +1 -0
- package/dist/capture/shards/context.mjs +51 -0
- package/dist/capture/shards/context.mjs.map +1 -0
- package/dist/capture/shards/execute.mjs +174 -0
- package/dist/capture/shards/execute.mjs.map +1 -0
- package/dist/capture/shards/fan-out.mjs +126 -0
- package/dist/capture/shards/fan-out.mjs.map +1 -0
- package/dist/capture/shards/history.mjs +35 -0
- package/dist/capture/shards/history.mjs.map +1 -0
- package/dist/capture/shards/reduce.mjs +97 -0
- package/dist/capture/shards/reduce.mjs.map +1 -0
- package/dist/capture/sizing.mjs +1 -11
- package/dist/capture/sizing.mjs.map +1 -1
- package/dist/capture/skip-checks.mjs +2 -65
- package/dist/capture/skip-checks.mjs.map +1 -1
- package/dist/capture/statics.d.mts +7 -8
- package/dist/capture/statics.mjs +25 -71
- package/dist/capture/statics.mjs.map +1 -1
- package/dist/capture/storybook-urls.d.mts +40 -0
- package/dist/capture/storybook-urls.mjs +41 -0
- package/dist/capture/storybook-urls.mjs.map +1 -0
- package/dist/capture/storybook.d.mts +12 -4
- package/dist/capture/storybook.mjs +56 -16
- package/dist/capture/storybook.mjs.map +1 -1
- package/dist/capture/target.mjs +21 -0
- package/dist/capture/target.mjs.map +1 -0
- package/dist/capture/targets.d.mts +25 -0
- package/dist/capture/targets.mjs +32 -0
- package/dist/capture/targets.mjs.map +1 -0
- package/dist/capture/tests.d.mts +37 -0
- package/dist/capture/tests.mjs +126 -0
- package/dist/capture/tests.mjs.map +1 -0
- package/dist/capture/token-chunks.mjs +74 -0
- package/dist/capture/token-chunks.mjs.map +1 -0
- package/dist/capture.d.mts +60 -7
- package/dist/capture.mjs +48 -3
- package/dist/config.d.mts +121 -12
- package/dist/config.mjs +9 -6
- package/dist/config.mjs.map +1 -1
- package/dist/diff.d.mts +9 -3
- package/dist/diff.mjs +16 -3
- package/dist/diff.mjs.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/insights/budget.d.mts +20 -0
- package/dist/insights/budget.mjs +33 -0
- package/dist/insights/budget.mjs.map +1 -0
- package/dist/insights/envelope.d.mts +59 -0
- package/dist/insights/envelope.mjs +60 -0
- package/dist/insights/envelope.mjs.map +1 -0
- package/dist/insights/evidence-health.d.mts +32 -0
- package/dist/insights/evidence-health.mjs +28 -0
- package/dist/insights/evidence-health.mjs.map +1 -0
- package/dist/insights/evidence.d.mts +55 -0
- package/dist/insights/evidence.mjs +64 -0
- package/dist/insights/evidence.mjs.map +1 -0
- package/dist/insights/hash.d.mts +18 -0
- package/dist/insights/hash.mjs +21 -0
- package/dist/insights/hash.mjs.map +1 -0
- package/dist/insights/limits.d.mts +24 -0
- package/dist/insights/limits.mjs +24 -0
- package/dist/insights/limits.mjs.map +1 -0
- package/dist/insights/prompts/v1.d.mts +13 -0
- package/dist/insights/prompts/v1.mjs +34 -0
- package/dist/insights/prompts/v1.mjs.map +1 -0
- package/dist/insights/redact.d.mts +9 -0
- package/dist/insights/redact.mjs +24 -0
- package/dist/insights/redact.mjs.map +1 -0
- package/dist/insights/text.mjs +44 -0
- package/dist/insights/text.mjs.map +1 -0
- package/dist/insights.d.mts +11 -0
- package/dist/insights.mjs +12 -0
- package/dist/logger.mjs +17 -1
- package/dist/logger.mjs.map +1 -1
- package/dist/models/ai-budget-alert.d.mts +23 -0
- package/dist/models/ai-budget-alert.mjs +38 -0
- package/dist/models/ai-budget-alert.mjs.map +1 -0
- package/dist/models/ai-usage.d.mts +40 -0
- package/dist/models/ai-usage.mjs +61 -0
- package/dist/models/ai-usage.mjs.map +1 -0
- package/dist/models/baseline-archive.d.mts +84 -0
- package/dist/models/baseline-archive.mjs +106 -0
- package/dist/models/baseline-archive.mjs.map +1 -0
- package/dist/models/baseline-export.d.mts +19 -0
- package/dist/models/baseline-export.mjs +136 -0
- package/dist/models/baseline-export.mjs.map +1 -0
- package/dist/models/baseline-import.mjs +118 -0
- package/dist/models/baseline-import.mjs.map +1 -0
- package/dist/models/baseline-transfer.d.mts +49 -0
- package/dist/models/baseline-transfer.mjs +121 -0
- package/dist/models/baseline-transfer.mjs.map +1 -0
- package/dist/models/baseline.d.mts +32 -5
- package/dist/models/baseline.mjs +65 -20
- package/dist/models/baseline.mjs.map +1 -1
- package/dist/models/build-component.d.mts +28 -0
- package/dist/models/build-component.mjs +55 -0
- package/dist/models/build-component.mjs.map +1 -0
- package/dist/models/build-review-status.d.mts +29 -0
- package/dist/models/build-review-status.mjs +37 -0
- package/dist/models/build-review-status.mjs.map +1 -0
- package/dist/models/build-review.d.mts +28 -0
- package/dist/models/build-review.mjs +54 -0
- package/dist/models/build-review.mjs.map +1 -0
- package/dist/models/build.d.mts +25 -3
- package/dist/models/build.mjs +50 -11
- package/dist/models/build.mjs.map +1 -1
- package/dist/models/capture-attempt.d.mts +7 -1
- package/dist/models/capture-attempt.mjs +29 -4
- package/dist/models/capture-attempt.mjs.map +1 -1
- package/dist/models/capture-job.d.mts +106 -0
- package/dist/models/capture-job.mjs +288 -0
- package/dist/models/capture-job.mjs.map +1 -0
- package/dist/models/capture-log.d.mts +1 -1
- package/dist/models/capture-log.mjs +4 -3
- package/dist/models/capture-log.mjs.map +1 -1
- package/dist/models/capture-shard.d.mts +62 -0
- package/dist/models/capture-shard.mjs +190 -0
- package/dist/models/capture-shard.mjs.map +1 -0
- package/dist/models/changeset.d.mts +48 -0
- package/dist/models/changeset.mjs +81 -0
- package/dist/models/changeset.mjs.map +1 -0
- package/dist/models/columns.mjs +19 -0
- package/dist/models/columns.mjs.map +1 -0
- package/dist/models/comment.d.mts +4 -1
- package/dist/models/comment.mjs +7 -4
- package/dist/models/comment.mjs.map +1 -1
- package/dist/models/component.d.mts +43 -0
- package/dist/models/component.mjs +60 -0
- package/dist/models/component.mjs.map +1 -0
- package/dist/models/connections.d.mts +41 -0
- package/dist/models/connections.mjs +112 -0
- package/dist/models/connections.mjs.map +1 -0
- package/dist/models/content-ref.d.mts +1 -1
- package/dist/models/content-ref.mjs +1 -1
- package/dist/models/content-ref.mjs.map +1 -1
- package/dist/models/credentials.mjs +12 -2
- package/dist/models/credentials.mjs.map +1 -1
- package/dist/models/design-credentials.d.mts +41 -0
- package/dist/models/design-credentials.mjs +86 -0
- package/dist/models/design-credentials.mjs.map +1 -0
- package/dist/models/design-health.d.mts +16 -0
- package/dist/models/design-health.mjs +25 -0
- package/dist/models/design-health.mjs.map +1 -0
- package/dist/models/design-nodes.d.mts +83 -0
- package/dist/models/design-nodes.mjs +222 -0
- package/dist/models/design-nodes.mjs.map +1 -0
- package/dist/models/design-renders.mjs +74 -0
- package/dist/models/design-renders.mjs.map +1 -0
- package/dist/models/design-resolve.d.mts +29 -0
- package/dist/models/design-resolve.mjs +41 -0
- package/dist/models/design-resolve.mjs.map +1 -0
- package/dist/models/design.d.mts +39 -0
- package/dist/models/design.mjs +90 -0
- package/dist/models/design.mjs.map +1 -0
- package/dist/models/inherited-ratio.d.mts +21 -0
- package/dist/models/inherited-ratio.mjs +22 -0
- package/dist/models/inherited-ratio.mjs.map +1 -0
- package/dist/models/insight.d.mts +70 -0
- package/dist/models/insight.mjs +162 -0
- package/dist/models/insight.mjs.map +1 -0
- package/dist/models/label.d.mts +4 -2
- package/dist/models/label.mjs +25 -10
- package/dist/models/label.mjs.map +1 -1
- package/dist/models/member.d.mts +1 -1
- package/dist/models/member.mjs +5 -4
- package/dist/models/member.mjs.map +1 -1
- package/dist/models/notification-channel.d.mts +37 -3
- package/dist/models/notification-channel.mjs +88 -9
- package/dist/models/notification-channel.mjs.map +1 -1
- package/dist/models/notification-subscription.d.mts +1 -2
- package/dist/models/notification-subscription.mjs +7 -9
- package/dist/models/notification-subscription.mjs.map +1 -1
- package/dist/models/project-group-mapping.d.mts +6 -1
- package/dist/models/project-group-mapping.mjs +14 -4
- package/dist/models/project-group-mapping.mjs.map +1 -1
- package/dist/models/project.d.mts +9 -1
- package/dist/models/project.mjs +9 -4
- package/dist/models/project.mjs.map +1 -1
- package/dist/models/public-origin.d.mts +63 -0
- package/dist/models/public-origin.mjs +114 -0
- package/dist/models/public-origin.mjs.map +1 -0
- package/dist/models/review-switch-off.d.mts +14 -0
- package/dist/models/review-switch-off.mjs +26 -0
- package/dist/models/review-switch-off.mjs.map +1 -0
- package/dist/models/site-settings.d.mts +23 -0
- package/dist/models/site-settings.mjs +48 -0
- package/dist/models/site-settings.mjs.map +1 -0
- package/dist/models/snapshot.d.mts +36 -1
- package/dist/models/snapshot.mjs +50 -3
- package/dist/models/snapshot.mjs.map +1 -1
- package/dist/models/token.d.mts +13 -1
- package/dist/models/token.mjs +23 -5
- package/dist/models/token.mjs.map +1 -1
- package/dist/models/usage.d.mts +76 -0
- package/dist/models/usage.mjs +149 -0
- package/dist/models/usage.mjs.map +1 -0
- package/dist/models/user.d.mts +1 -1
- package/dist/models/user.mjs +4 -3
- package/dist/models/user.mjs.map +1 -1
- package/dist/models/webhook.d.mts +15 -1
- package/dist/models/webhook.mjs +33 -4
- package/dist/models/webhook.mjs.map +1 -1
- package/dist/models.d.mts +26 -3
- package/dist/models.mjs +27 -5
- package/dist/options.d.mts +18 -0
- package/dist/options.mjs +90 -0
- package/dist/options.mjs.map +1 -0
- package/dist/paths.d.mts +19 -4
- package/dist/paths.mjs +51 -10
- package/dist/paths.mjs.map +1 -1
- package/dist/retention/ai.d.mts +18 -0
- package/dist/retention/ai.mjs +23 -0
- package/dist/retention/ai.mjs.map +1 -0
- package/dist/retention/pending.mjs +47 -0
- package/dist/retention/pending.mjs.map +1 -0
- package/dist/retention/screenshot-sweep.d.mts +51 -0
- package/dist/retention/screenshot-sweep.mjs +128 -0
- package/dist/retention/screenshot-sweep.mjs.map +1 -0
- package/dist/retention.d.mts +16 -2
- package/dist/retention.mjs +71 -14
- package/dist/retention.mjs.map +1 -1
- package/dist/schema/baseline.d.mts +6 -0
- package/dist/schema/build-component.d.mts +25 -0
- package/dist/schema/build-review.d.mts +17 -0
- package/dist/schema/build.d.mts +7 -0
- package/dist/schema/capture-job.d.mts +60 -0
- package/dist/schema/capture-shard.d.mts +35 -0
- package/dist/schema/comment.d.mts +2 -0
- package/dist/schema/connection.d.mts +24 -0
- package/dist/schema/design.d.mts +58 -0
- package/dist/schema/insight.d.mts +54 -0
- package/dist/schema/notification.d.mts +4 -0
- package/dist/schema/project.d.mts +21 -0
- package/dist/schema/site-setting.d.mts +21 -0
- package/dist/schema/snapshot.d.mts +12 -0
- package/dist/schema/usage.d.mts +30 -0
- package/dist/schema.d.mts +11 -3
- package/dist/serverless.d.mts +21 -0
- package/dist/serverless.mjs +37 -0
- package/dist/serverless.mjs.map +1 -0
- package/dist/test-helpers/create-project.d.mts +1 -1
- package/dist/test-helpers/deferred.d.mts +39 -0
- package/dist/test-helpers/deferred.mjs +61 -0
- package/dist/test-helpers/deferred.mjs.map +1 -0
- package/dist/test-helpers/design-contract.d.mts +21 -0
- package/dist/test-helpers/design-contract.mjs +40 -0
- package/dist/test-helpers/design-contract.mjs.map +1 -0
- package/dist/test-helpers/fake-database.mjs +14 -2
- package/dist/test-helpers/fake-database.mjs.map +1 -1
- package/dist/test-helpers/fake-design.d.mts +43 -0
- package/dist/test-helpers/fake-design.mjs +114 -0
- package/dist/test-helpers/fake-design.mjs.map +1 -0
- package/dist/test-helpers/fake-notifier.d.mts +21 -0
- package/dist/test-helpers/fake-notifier.mjs +44 -0
- package/dist/test-helpers/fake-notifier.mjs.map +1 -0
- package/dist/test-helpers/fake-tables.mjs +255 -67
- package/dist/test-helpers/fake-tables.mjs.map +1 -1
- package/dist/test-helpers/manual-clock.d.mts +27 -0
- package/dist/test-helpers/manual-clock.mjs +56 -0
- package/dist/test-helpers/manual-clock.mjs.map +1 -0
- package/dist/test-helpers/notifier-contract.d.mts +7 -1
- package/dist/test-helpers/notifier-contract.mjs +32 -3
- package/dist/test-helpers/notifier-contract.mjs.map +1 -1
- package/dist/test-helpers/queue-contract.d.mts +16 -2
- package/dist/test-helpers/queue-contract.mjs +195 -1
- package/dist/test-helpers/queue-contract.mjs.map +1 -1
- package/dist/test-helpers/queue-multi-instance.d.mts +20 -0
- package/dist/test-helpers/queue-multi-instance.mjs +206 -0
- package/dist/test-helpers/queue-multi-instance.mjs.map +1 -0
- package/dist/test-helpers/runner-contract.mjs +11 -0
- package/dist/test-helpers/runner-contract.mjs.map +1 -1
- package/dist/test-helpers/sql-chunks.mjs +23 -0
- package/dist/test-helpers/sql-chunks.mjs.map +1 -1
- package/dist/test-helpers/storage-contract.mjs +1 -1
- package/dist/test-helpers.d.mts +9 -4
- package/dist/test-helpers.mjs +8 -3
- package/dist/types.d.mts +14 -3
- package/dist/types.mjs +21 -4
- package/dist/types.mjs.map +1 -1
- package/dist/urls.d.mts +4 -0
- package/dist/urls.mjs +11 -2
- package/dist/urls.mjs.map +1 -1
- package/dist/utils/adaptive-concurrency.d.mts +66 -0
- package/dist/utils/adaptive-concurrency.mjs +149 -0
- package/dist/utils/adaptive-concurrency.mjs.map +1 -0
- package/dist/utils/connect-retry.d.mts +26 -0
- package/dist/utils/connect-retry.mjs +43 -0
- package/dist/utils/connect-retry.mjs.map +1 -0
- package/dist/utils/environment.d.mts +19 -0
- package/dist/utils/environment.mjs +59 -0
- package/dist/utils/environment.mjs.map +1 -0
- package/dist/utils/http.d.mts +17 -1
- package/dist/utils/http.mjs +22 -3
- package/dist/utils/http.mjs.map +1 -1
- package/dist/utils/map-adaptive.d.mts +15 -0
- package/dist/utils/map-adaptive.mjs +72 -0
- package/dist/utils/map-adaptive.mjs.map +1 -0
- package/dist/utils/map-limit.d.mts +10 -0
- package/dist/utils/map-limit.mjs +32 -0
- package/dist/utils/map-limit.mjs.map +1 -0
- package/dist/utils/page-limiter.d.mts +37 -0
- package/dist/utils/page-limiter.mjs +56 -0
- package/dist/utils/page-limiter.mjs.map +1 -0
- package/dist/utils/public-origin.d.mts +20 -0
- package/dist/utils/public-origin.mjs +75 -0
- package/dist/utils/public-origin.mjs.map +1 -0
- package/dist/utils/redact.mjs +3 -0
- package/dist/utils/redact.mjs.map +1 -1
- package/dist/utils/remote-environment.d.mts +39 -0
- package/dist/utils/remote-environment.mjs +114 -0
- package/dist/utils/remote-environment.mjs.map +1 -0
- package/dist/utils/resources.d.mts +64 -0
- package/dist/utils/resources.mjs +122 -0
- package/dist/utils/resources.mjs.map +1 -0
- package/dist/utils/screenshot-store.d.mts +15 -0
- package/dist/utils/screenshot-store.mjs +41 -0
- package/dist/utils/screenshot-store.mjs.map +1 -0
- package/dist/utils/session-gate.d.mts +22 -0
- package/dist/utils/session-gate.mjs +53 -0
- package/dist/utils/session-gate.mjs.map +1 -0
- package/dist/utils/storybook-token.d.mts +81 -0
- package/dist/utils/storybook-token.mjs +134 -0
- package/dist/utils/storybook-token.mjs.map +1 -0
- package/dist/utils/storybook-url.d.mts +15 -0
- package/dist/utils/storybook-url.mjs +21 -0
- package/dist/utils/storybook-url.mjs.map +1 -0
- package/dist/utils/timing.mjs +1 -1
- package/dist/utils/timing.mjs.map +1 -1
- package/dist/utils/zip-read.mjs +129 -0
- package/dist/utils/zip-read.mjs.map +1 -0
- package/dist/utils/zip-write.d.mts +18 -0
- package/dist/utils/zip-write.mjs +188 -0
- package/dist/utils/zip-write.mjs.map +1 -0
- package/dist/utils.d.mts +17 -3
- package/dist/utils.mjs +18 -4
- package/package.json +32 -23
- package/dist/adapter/capture-runner.mjs.map +0 -1
- package/dist/adapter/git-host/comments.d.mts +0 -41
- package/dist/adapter/git-host/comments.mjs +0 -54
- package/dist/adapter/git-host/comments.mjs.map +0 -1
- package/dist/adapter/git-host/helpers.d.mts +0 -13
- package/dist/adapter/git-host/helpers.mjs +0 -30
- package/dist/adapter/git-host/helpers.mjs.map +0 -1
- package/dist/adapter/git-host.d.mts +0 -42
- package/dist/capture/queue.d.mts +0 -38
- package/dist/capture/queue.mjs +0 -98
- package/dist/capture/queue.mjs.map +0 -1
- package/dist/capture/recovery.d.mts +0 -21
- package/dist/capture/recovery.mjs +0 -83
- package/dist/capture/recovery.mjs.map +0 -1
- package/dist/capture/status-fanout.mjs +0 -72
- package/dist/capture/status-fanout.mjs.map +0 -1
- package/dist/diff/engine.d.mts +0 -16
- package/dist/diff/engine.mjs +0 -87
- package/dist/diff/engine.mjs.map +0 -1
- package/dist/diff/options.d.mts +0 -42
- package/dist/diff/options.mjs +0 -15
- package/dist/diff/options.mjs.map +0 -1
- package/dist/models/status-config.d.mts +0 -38
- package/dist/models/status-config.mjs +0 -63
- package/dist/models/status-config.mjs.map +0 -1
- package/dist/schema/status-config.d.mts +0 -16
- package/dist/test-helpers/git-contract.d.mts +0 -11
- package/dist/test-helpers/git-contract.mjs +0 -34
- package/dist/test-helpers/git-contract.mjs.map +0 -1
- /package/dist/adapter/{git-host.mjs → oauth.mjs} +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { sha256 } from "../utils/hash.mjs";
|
|
5
|
+
import { SIZING_DEFAULTS } from "./sizing.mjs";
|
|
6
|
+
//#region src/capture/fingerprint.ts
|
|
7
|
+
/** Build the fingerprint of one capture. Missing environment fields stay `null` (runner did not report them). */
|
|
8
|
+
function buildFingerprint(engine, viewport, environment) {
|
|
9
|
+
return {
|
|
10
|
+
engine,
|
|
11
|
+
browserVersion: environment?.browserVersion ?? null,
|
|
12
|
+
platform: environment?.platform ?? null,
|
|
13
|
+
deviceScaleFactor: environment?.deviceScaleFactor ?? null,
|
|
14
|
+
fontsHash: environment?.fontsHash ?? null,
|
|
15
|
+
viewport: {
|
|
16
|
+
width: viewport.width,
|
|
17
|
+
height: viewport.height
|
|
18
|
+
},
|
|
19
|
+
sizing: SIZING_DEFAULTS
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/** Stable hash of a fingerprint: the value stored as `infraHash`. */
|
|
23
|
+
function fingerprintHash(fingerprint) {
|
|
24
|
+
return sha256(JSON.stringify([
|
|
25
|
+
fingerprint.engine,
|
|
26
|
+
fingerprint.browserVersion,
|
|
27
|
+
fingerprint.platform,
|
|
28
|
+
fingerprint.deviceScaleFactor,
|
|
29
|
+
fingerprint.fontsHash,
|
|
30
|
+
fingerprint.viewport.width,
|
|
31
|
+
fingerprint.viewport.height,
|
|
32
|
+
fingerprint.sizing
|
|
33
|
+
]));
|
|
34
|
+
}
|
|
35
|
+
/** The components whose change is an environment migration. Viewport is not one (see below). */
|
|
36
|
+
const ENVIRONMENT_FIELDS = [
|
|
37
|
+
"engine",
|
|
38
|
+
"browserVersion",
|
|
39
|
+
"platform",
|
|
40
|
+
"deviceScaleFactor",
|
|
41
|
+
"fontsHash"
|
|
42
|
+
];
|
|
43
|
+
/**
|
|
44
|
+
* Names of the environment components that differ; `["unknown"]` when the earlier fingerprint was
|
|
45
|
+
* not recorded. A component one side did not report (`null`: a runner that does not report it, a
|
|
46
|
+
* capture spooled before a browser crash, a failed `fc-list`) is unknown, not changed. Viewport is
|
|
47
|
+
* never a change here: its size comes from story parameters (the PR's own code), so a new size must
|
|
48
|
+
* be reviewed, and it diffs on its own as a dimension change.
|
|
49
|
+
*/
|
|
50
|
+
function changedFields(from, to) {
|
|
51
|
+
if (!from) return ["unknown"];
|
|
52
|
+
return [...ENVIRONMENT_FIELDS.filter((key) => known(from[key]) && known(to[key]) && from[key] !== to[key]), ...JSON.stringify(from.sizing) === JSON.stringify(to.sizing) ? [] : ["sizing"]];
|
|
53
|
+
}
|
|
54
|
+
function known(value) {
|
|
55
|
+
return value !== null && value !== void 0;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The fingerprint to store on a baseline: the capture's, with any component it did not report kept
|
|
59
|
+
* from the baseline's earlier fingerprint, so an unknown component never overwrites a known one.
|
|
60
|
+
*/
|
|
61
|
+
function mergeFingerprint(earlier, now) {
|
|
62
|
+
return {
|
|
63
|
+
...now,
|
|
64
|
+
browserVersion: now.browserVersion ?? earlier?.browserVersion ?? null,
|
|
65
|
+
platform: now.platform ?? earlier?.platform ?? null,
|
|
66
|
+
deviceScaleFactor: now.deviceScaleFactor ?? earlier?.deviceScaleFactor ?? null,
|
|
67
|
+
fontsHash: now.fontsHash ?? earlier?.fontsHash ?? null
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/** Parse a stored fingerprint; null for a missing or malformed value. */
|
|
71
|
+
function parseFingerprint(raw) {
|
|
72
|
+
if (!raw) return null;
|
|
73
|
+
try {
|
|
74
|
+
const value = JSON.parse(raw);
|
|
75
|
+
return typeof value.engine === "string" && value.viewport ? value : null;
|
|
76
|
+
} catch {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
export { buildFingerprint, changedFields, fingerprintHash, mergeFingerprint, parseFingerprint };
|
|
82
|
+
|
|
83
|
+
//# sourceMappingURL=fingerprint.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fingerprint.mjs","names":[],"sources":["../../src/capture/fingerprint.ts"],"sourcesContent":["/** Capture fingerprint: everything that changes pixels without a code change (ADR 0005 phase 4). */\nimport type { CaptureEnvironment } from \"../adapters/capture-runner.ts\";\nimport { sha256 } from \"../utils/hash.ts\";\nimport type { Viewport } from \"./adapter.ts\";\nimport { SIZING_DEFAULTS } from \"./sizing.ts\";\n\n/** The components behind a snapshot's `infraHash`, kept so a mismatch can say what changed. */\nexport interface Fingerprint {\n engine: string;\n browserVersion: string | null;\n platform: string | null;\n deviceScaleFactor: number | null;\n fontsHash: string | null;\n viewport: { width: number; height: number };\n sizing: typeof SIZING_DEFAULTS;\n}\n\n/** Build the fingerprint of one capture. Missing environment fields stay `null` (runner did not report them). */\nexport function buildFingerprint(\n engine: string,\n viewport: Viewport,\n environment: CaptureEnvironment | undefined,\n): Fingerprint {\n return {\n engine,\n browserVersion: environment?.browserVersion ?? null,\n platform: environment?.platform ?? null,\n deviceScaleFactor: environment?.deviceScaleFactor ?? null,\n fontsHash: environment?.fontsHash ?? null,\n viewport: { width: viewport.width, height: viewport.height },\n sizing: SIZING_DEFAULTS,\n };\n}\n\n/** Stable hash of a fingerprint: the value stored as `infraHash`. */\nexport function fingerprintHash(fingerprint: Fingerprint): string {\n return sha256(\n JSON.stringify([\n fingerprint.engine,\n fingerprint.browserVersion,\n fingerprint.platform,\n fingerprint.deviceScaleFactor,\n fingerprint.fontsHash,\n fingerprint.viewport.width,\n fingerprint.viewport.height,\n fingerprint.sizing,\n ]),\n );\n}\n\n/** The components whose change is an environment migration. Viewport is not one (see below). */\nconst ENVIRONMENT_FIELDS = [\n \"engine\",\n \"browserVersion\",\n \"platform\",\n \"deviceScaleFactor\",\n \"fontsHash\",\n] as const;\n\n/**\n * Names of the environment components that differ; `[\"unknown\"]` when the earlier fingerprint was\n * not recorded. A component one side did not report (`null`: a runner that does not report it, a\n * capture spooled before a browser crash, a failed `fc-list`) is unknown, not changed. Viewport is\n * never a change here: its size comes from story parameters (the PR's own code), so a new size must\n * be reviewed, and it diffs on its own as a dimension change.\n */\nexport function changedFields(from: Fingerprint | null, to: Fingerprint): string[] {\n if (!from) {\n return [\"unknown\"];\n }\n return [\n ...ENVIRONMENT_FIELDS.filter(\n (key) => known(from[key]) && known(to[key]) && from[key] !== to[key],\n ),\n ...(JSON.stringify(from.sizing) === JSON.stringify(to.sizing) ? [] : [\"sizing\"]),\n ];\n}\n\nfunction known(value: unknown): boolean {\n return value !== null && value !== undefined;\n}\n\n/**\n * The fingerprint to store on a baseline: the capture's, with any component it did not report kept\n * from the baseline's earlier fingerprint, so an unknown component never overwrites a known one.\n */\nexport function mergeFingerprint(earlier: Fingerprint | null, now: Fingerprint): Fingerprint {\n return {\n ...now,\n browserVersion: now.browserVersion ?? earlier?.browserVersion ?? null,\n platform: now.platform ?? earlier?.platform ?? null,\n deviceScaleFactor: now.deviceScaleFactor ?? earlier?.deviceScaleFactor ?? null,\n fontsHash: now.fontsHash ?? earlier?.fontsHash ?? null,\n };\n}\n\n/** Parse a stored fingerprint; null for a missing or malformed value. */\nexport function parseFingerprint(raw: string | null | undefined): Fingerprint | null {\n if (!raw) {\n return null;\n }\n try {\n const value = JSON.parse(raw) as Partial<Fingerprint>;\n return typeof value.engine === \"string\" && value.viewport ? (value as Fingerprint) : null;\n } catch {\n return null;\n }\n}\n\n/** What a migrated snapshot records: which components changed, and the before and after. */\nexport interface EnvironmentChange {\n changed: string[];\n from: Fingerprint | null;\n to: Fingerprint;\n}\n"],"mappings":";;;;;;;AAkBA,SAAgB,iBACd,QACA,UACA,aACa;CACb,OAAO;EACL;EACA,gBAAgB,aAAa,kBAAkB;EAC/C,UAAU,aAAa,YAAY;EACnC,mBAAmB,aAAa,qBAAqB;EACrD,WAAW,aAAa,aAAa;EACrC,UAAU;GAAE,OAAO,SAAS;GAAO,QAAQ,SAAS;EAAO;EAC3D,QAAQ;CACV;AACF;;AAGA,SAAgB,gBAAgB,aAAkC;CAChE,OAAO,OACL,KAAK,UAAU;EACb,YAAY;EACZ,YAAY;EACZ,YAAY;EACZ,YAAY;EACZ,YAAY;EACZ,YAAY,SAAS;EACrB,YAAY,SAAS;EACrB,YAAY;CACd,CAAC,CACH;AACF;;AAGA,MAAM,qBAAqB;CACzB;CACA;CACA;CACA;CACA;AACF;;;;;;;;AASA,SAAgB,cAAc,MAA0B,IAA2B;CACjF,IAAI,CAAC,MACH,OAAO,CAAC,SAAS;CAEnB,OAAO,CACL,GAAG,mBAAmB,QACnB,QAAQ,MAAM,KAAK,IAAI,KAAK,MAAM,GAAG,IAAI,KAAK,KAAK,SAAS,GAAG,IAClE,GACA,GAAI,KAAK,UAAU,KAAK,MAAM,MAAM,KAAK,UAAU,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAChF;AACF;AAEA,SAAS,MAAM,OAAyB;CACtC,OAAO,UAAU,QAAQ,UAAU,KAAA;AACrC;;;;;AAMA,SAAgB,iBAAiB,SAA6B,KAA+B;CAC3F,OAAO;EACL,GAAG;EACH,gBAAgB,IAAI,kBAAkB,SAAS,kBAAkB;EACjE,UAAU,IAAI,YAAY,SAAS,YAAY;EAC/C,mBAAmB,IAAI,qBAAqB,SAAS,qBAAqB;EAC1E,WAAW,IAAI,aAAa,SAAS,aAAa;CACpD;AACF;;AAGA,SAAgB,iBAAiB,KAAoD;CACnF,IAAI,CAAC,KACH,OAAO;CAET,IAAI;EACF,MAAM,QAAQ,KAAK,MAAM,GAAG;EAC5B,OAAO,OAAO,MAAM,WAAW,YAAY,MAAM,WAAY,QAAwB;CACvF,QAAQ;EACN,OAAO;CACT;AACF"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { ChildMessage, ParentMessage } from "./protocol.mjs";
|
|
4
|
+
//#region src/capture/forked/channel.d.ts
|
|
5
|
+
/** How the parent sees the capture child (`child_process.fork` adapts to this). */
|
|
6
|
+
interface ChildProcessHandle {
|
|
7
|
+
send(message: ParentMessage): void;
|
|
8
|
+
onMessage(listener: (message: unknown) => void): void;
|
|
9
|
+
/** Fires once when the child ends; `code` is null when it was killed by a signal. */
|
|
10
|
+
onExit(listener: (info: {
|
|
11
|
+
code: number | null;
|
|
12
|
+
signal: string | null;
|
|
13
|
+
}) => void): void;
|
|
14
|
+
/** Stop the child: SIGTERM, escalating to SIGKILL; `force` goes straight to SIGKILL (a hung child ignores SIGTERM). */
|
|
15
|
+
kill(force?: boolean): void;
|
|
16
|
+
}
|
|
17
|
+
/** How the child sees its parent (`process.send` / `process.on("message")` adapt to this). */
|
|
18
|
+
interface ParentPort {
|
|
19
|
+
send(message: ChildMessage): void;
|
|
20
|
+
onMessage(listener: (message: unknown) => void): void;
|
|
21
|
+
onDisconnect(listener: () => void): void;
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { ChildProcessHandle, ParentPort };
|
|
25
|
+
//# sourceMappingURL=channel.d.mts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { Logger } from "../../logger.mjs";
|
|
4
|
+
import { CaptureHandlerOptions } from "../consumer/capture-handler.mjs";
|
|
5
|
+
import { CaptureConsumerOptions } from "../consumer/types.mjs";
|
|
6
|
+
import { ChildMessage } from "./protocol.mjs";
|
|
7
|
+
//#region src/capture/forked/child.d.ts
|
|
8
|
+
/** Handler options minus the shard hand-out, which the child wires to the database queue. */
|
|
9
|
+
type CaptureChildOptions = Omit<CaptureHandlerOptions, "enqueue" | "logger"> & Pick<CaptureConsumerOptions, "images" | "compare"> & {
|
|
10
|
+
logger?: Logger;
|
|
11
|
+
/** Retries stamped on shard jobs (default: the parent's `testing.retries`, handed down in the environment). */
|
|
12
|
+
retries?: number;
|
|
13
|
+
};
|
|
14
|
+
/** The slice of `process` the child uses; injectable so the lifecycle is testable. */
|
|
15
|
+
interface ChildHost {
|
|
16
|
+
/** Present only in a forked process with an IPC channel. */
|
|
17
|
+
send?: (message: ChildMessage) => unknown;
|
|
18
|
+
on(event: "message" | "disconnect" | "SIGTERM" | "SIGINT", listener: (message?: unknown) => void): unknown;
|
|
19
|
+
env: Record<string, string | undefined>;
|
|
20
|
+
exit(code: number): void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Run pushed jobs until the parent stops the child.
|
|
24
|
+
*
|
|
25
|
+
* - `SIGTERM` / `SIGINT`: let the runs in flight finish, then exit 0.
|
|
26
|
+
* - Parent gone (`disconnect`): nobody can receive results, so exit at once. Whatever was running
|
|
27
|
+
* loses its lease with the parent and is redelivered to another consumer.
|
|
28
|
+
*
|
|
29
|
+
* @throws When not running as a forked child (no IPC channel), or when the image engine cannot load.
|
|
30
|
+
*/
|
|
31
|
+
declare function runCaptureChild(options: CaptureChildOptions, host?: ChildHost): Promise<void>;
|
|
32
|
+
//#endregion
|
|
33
|
+
export { CaptureChildOptions, ChildHost, runCaptureChild };
|
|
34
|
+
//# sourceMappingURL=child.d.mts.map
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { createShelfLogger } from "../../logger.mjs";
|
|
5
|
+
import { createNotifyEnqueuer } from "../build-state/notify-job.mjs";
|
|
6
|
+
import { createBuildStatePublisher } from "../build-state/publisher.mjs";
|
|
7
|
+
import "../db-queue/types.mjs";
|
|
8
|
+
import { createDatabaseCaptureQueue } from "../db-queue/index.mjs";
|
|
9
|
+
import { createCaptureHandler, originOf } from "../consumer/capture-handler.mjs";
|
|
10
|
+
import { applyImageSettings, ensureImageEngine } from "../consumer/startup.mjs";
|
|
11
|
+
import "./fork.mjs";
|
|
12
|
+
import { isParentMessage } from "./protocol.mjs";
|
|
13
|
+
//#region src/capture/forked/child.ts
|
|
14
|
+
/**
|
|
15
|
+
* Entry helper for a forked capture child (`testing.workerEntry`). The app is the consumer: it
|
|
16
|
+
* polls the database queue, holds the slots and the lease, and pushes each leased job here over
|
|
17
|
+
* process IPC. The child runs the whole capture pipeline for the job it is given and reports
|
|
18
|
+
* `progress`, then `done` or `failed`. It never reads the queue (ADR 0006, decision 5); the only
|
|
19
|
+
* thing it writes there is the shard jobs a big build fans out into, which it inserts itself.
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* // capture-worker.ts — the module named by `testing.workerEntry`
|
|
23
|
+
* import { runCaptureChild } from "@storyshelf/core/capture";
|
|
24
|
+
* await runCaptureChild({ db, storage, runner, scratchDir });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* Run pushed jobs until the parent stops the child.
|
|
29
|
+
*
|
|
30
|
+
* - `SIGTERM` / `SIGINT`: let the runs in flight finish, then exit 0.
|
|
31
|
+
* - Parent gone (`disconnect`): nobody can receive results, so exit at once. Whatever was running
|
|
32
|
+
* loses its lease with the parent and is redelivered to another consumer.
|
|
33
|
+
*
|
|
34
|
+
* @throws When not running as a forked child (no IPC channel), or when the image engine cannot load.
|
|
35
|
+
*/
|
|
36
|
+
async function runCaptureChild(options, host = processHost()) {
|
|
37
|
+
const { send } = host;
|
|
38
|
+
if (!send) throw new Error("runCaptureChild must run in a forked process; set testing.workerEntry");
|
|
39
|
+
const logger = options.logger ?? createShelfLogger();
|
|
40
|
+
applyImageSettings(options.images, options.compare);
|
|
41
|
+
await ensureImageEngine(logger);
|
|
42
|
+
await wireHost(host, new CaptureChild(buildHandler(options, host.env, logger), logger, (message) => {
|
|
43
|
+
send.call(host, message);
|
|
44
|
+
}));
|
|
45
|
+
host.exit(0);
|
|
46
|
+
}
|
|
47
|
+
/** Feed the parent's messages to the child; resolves once a signal asked it to stop and it drained. */
|
|
48
|
+
async function wireHost(host, child) {
|
|
49
|
+
const stopped = Promise.withResolvers();
|
|
50
|
+
const settle = () => {
|
|
51
|
+
stopped.resolve(null);
|
|
52
|
+
};
|
|
53
|
+
const drain = () => {
|
|
54
|
+
child.drain().then(settle, settle);
|
|
55
|
+
};
|
|
56
|
+
host.on("message", (message) => {
|
|
57
|
+
child.onMessage(message);
|
|
58
|
+
});
|
|
59
|
+
host.on("SIGTERM", drain);
|
|
60
|
+
host.on("SIGINT", drain);
|
|
61
|
+
host.on("disconnect", () => {
|
|
62
|
+
host.exit(0);
|
|
63
|
+
});
|
|
64
|
+
await stopped.promise;
|
|
65
|
+
}
|
|
66
|
+
/** The capture handler over the app's own adapters; shard jobs go straight onto the database queue. */
|
|
67
|
+
function buildHandler(options, env, logger) {
|
|
68
|
+
const maxRetries = options.retries ?? positiveInt(env["STORYSHELF_CAPTURE_RETRIES"], 0) ?? 2;
|
|
69
|
+
const queue = createDatabaseCaptureQueue(options.db, {
|
|
70
|
+
retries: maxRetries,
|
|
71
|
+
logger: logger.child({ component: "child-queue" })
|
|
72
|
+
});
|
|
73
|
+
return createCaptureHandler({
|
|
74
|
+
db: options.db,
|
|
75
|
+
storage: options.storage,
|
|
76
|
+
runner: options.runner,
|
|
77
|
+
scratchDir: options.scratchDir,
|
|
78
|
+
notifiers: options.notifiers,
|
|
79
|
+
config: options.config,
|
|
80
|
+
buildState: options.buildState ?? notifyJobsPublisher(options, queue, logger),
|
|
81
|
+
design: options.design,
|
|
82
|
+
secret: options.secret,
|
|
83
|
+
viewports: options.viewports,
|
|
84
|
+
shardSize: options.shardSize ?? positiveInt(env["STORYSHELF_CAPTURE_SHARD_SIZE"]),
|
|
85
|
+
maxRetries,
|
|
86
|
+
enqueue: async (job) => {
|
|
87
|
+
await queue.enqueue(job);
|
|
88
|
+
},
|
|
89
|
+
logger
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/** Publisher that queues one `notify` job per git channel on the database queue. */
|
|
93
|
+
function notifyJobsPublisher(options, queue, logger) {
|
|
94
|
+
return createBuildStatePublisher({
|
|
95
|
+
deps: {
|
|
96
|
+
db: options.db,
|
|
97
|
+
storage: options.storage,
|
|
98
|
+
notifiers: options.notifiers ?? [],
|
|
99
|
+
secret: options.secret,
|
|
100
|
+
config: options.config ?? {},
|
|
101
|
+
publicOrigin: originOf(options),
|
|
102
|
+
flags: () => ({
|
|
103
|
+
testing: true,
|
|
104
|
+
review: false
|
|
105
|
+
}),
|
|
106
|
+
logger
|
|
107
|
+
},
|
|
108
|
+
enqueue: createNotifyEnqueuer({ db: options.db }, queue)
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/** One run per pushed job; the parent decides what runs, how many at once, and when it ends. */
|
|
112
|
+
var CaptureChild = class {
|
|
113
|
+
#handler;
|
|
114
|
+
#logger;
|
|
115
|
+
#send;
|
|
116
|
+
#running = /* @__PURE__ */ new Map();
|
|
117
|
+
constructor(handler, logger, send) {
|
|
118
|
+
this.#handler = handler;
|
|
119
|
+
this.#logger = logger;
|
|
120
|
+
this.#send = send;
|
|
121
|
+
}
|
|
122
|
+
onMessage(message) {
|
|
123
|
+
if (!isParentMessage(message)) return;
|
|
124
|
+
if (message.type === "run") {
|
|
125
|
+
this.#start(message.runId, message.job);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const run = this.#running.get(message.runId);
|
|
129
|
+
if (run) this.#handler.cancel?.(run.job).catch(ignore);
|
|
130
|
+
}
|
|
131
|
+
/** Resolves once every run in flight has finished. */
|
|
132
|
+
async drain() {
|
|
133
|
+
while (this.#running.size > 0) await Promise.allSettled([...this.#running.values()].map(async (run) => {
|
|
134
|
+
await run.done;
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
#start(runId, job) {
|
|
138
|
+
const done = this.#execute(runId, job).finally(() => {
|
|
139
|
+
this.#running.delete(runId);
|
|
140
|
+
});
|
|
141
|
+
this.#running.set(runId, {
|
|
142
|
+
job,
|
|
143
|
+
done
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
async #execute(runId, job) {
|
|
147
|
+
try {
|
|
148
|
+
await this.#handler.run(job, {
|
|
149
|
+
logger: this.#logger.child({
|
|
150
|
+
buildId: job.buildId,
|
|
151
|
+
jobId: job.jobId,
|
|
152
|
+
runId
|
|
153
|
+
}),
|
|
154
|
+
touch: throttled(() => {
|
|
155
|
+
this.#send({
|
|
156
|
+
type: "progress",
|
|
157
|
+
runId
|
|
158
|
+
});
|
|
159
|
+
})
|
|
160
|
+
});
|
|
161
|
+
this.#send({
|
|
162
|
+
type: "done",
|
|
163
|
+
runId
|
|
164
|
+
});
|
|
165
|
+
} catch (error) {
|
|
166
|
+
this.#send({
|
|
167
|
+
type: "failed",
|
|
168
|
+
runId,
|
|
169
|
+
error: error instanceof Error ? error.message : String(error)
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
/** At most one progress message per second per run: a capture logs far more often than a lease needs. */
|
|
175
|
+
const PROGRESS_INTERVAL_MS = 1e3;
|
|
176
|
+
function throttled(report) {
|
|
177
|
+
let last = 0;
|
|
178
|
+
return () => {
|
|
179
|
+
const now = Date.now();
|
|
180
|
+
if (now - last >= PROGRESS_INTERVAL_MS) {
|
|
181
|
+
last = now;
|
|
182
|
+
report();
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function ignore() {}
|
|
187
|
+
function positiveInt(value, min = 1) {
|
|
188
|
+
const parsed = Number(value);
|
|
189
|
+
return value !== void 0 && Number.isInteger(parsed) && parsed >= min ? parsed : void 0;
|
|
190
|
+
}
|
|
191
|
+
function processHost() {
|
|
192
|
+
return {
|
|
193
|
+
send: process.send ? (message) => process.send?.(message) : void 0,
|
|
194
|
+
on: (event, listener) => process.on(event, listener),
|
|
195
|
+
env: process.env,
|
|
196
|
+
exit: (code) => process.exit(code)
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
//#endregion
|
|
200
|
+
export { runCaptureChild };
|
|
201
|
+
|
|
202
|
+
//# sourceMappingURL=child.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"child.mjs","names":[],"sources":["../../../src/capture/forked/child.ts"],"sourcesContent":["/**\n * Entry helper for a forked capture child (`testing.workerEntry`). The app is the consumer: it\n * polls the database queue, holds the slots and the lease, and pushes each leased job here over\n * process IPC. The child runs the whole capture pipeline for the job it is given and reports\n * `progress`, then `done` or `failed`. It never reads the queue (ADR 0006, decision 5); the only\n * thing it writes there is the shard jobs a big build fans out into, which it inserts itself.\n *\n * ```ts\n * // capture-worker.ts — the module named by `testing.workerEntry`\n * import { runCaptureChild } from \"@storyshelf/core/capture\";\n * await runCaptureChild({ db, storage, runner, scratchDir });\n * ```\n */\nimport { createShelfLogger, type Logger } from \"../../logger.ts\";\nimport { createNotifyEnqueuer } from \"../build-state/notify-job.ts\";\nimport { createBuildStatePublisher } from \"../build-state/publisher.ts\";\nimport type { BuildStatePublisher } from \"../build-state/types.ts\";\nimport {\n createCaptureHandler,\n originOf,\n type CaptureHandlerOptions,\n} from \"../consumer/capture-handler.ts\";\nimport { applyImageSettings, ensureImageEngine } from \"../consumer/startup.ts\";\nimport type { CaptureConsumerOptions, ConsumerJob, KindHandler } from \"../consumer/types.ts\";\nimport { createDatabaseCaptureQueue, DEFAULT_RETRIES } from \"../db-queue/index.ts\";\nimport { CAPTURE_RETRIES_ENV, CAPTURE_SHARD_SIZE_ENV } from \"./fork.ts\";\nimport { isParentMessage, type ChildMessage } from \"./protocol.ts\";\n\n/** Handler options minus the shard hand-out, which the child wires to the database queue. */\nexport type CaptureChildOptions = Omit<CaptureHandlerOptions, \"enqueue\" | \"logger\"> &\n Pick<CaptureConsumerOptions, \"images\" | \"compare\"> & {\n logger?: Logger;\n /** Retries stamped on shard jobs (default: the parent's `testing.retries`, handed down in the environment). */\n retries?: number;\n };\n\n/** The slice of `process` the child uses; injectable so the lifecycle is testable. */\nexport interface ChildHost {\n /** Present only in a forked process with an IPC channel. */\n send?: (message: ChildMessage) => unknown;\n on(\n event: \"message\" | \"disconnect\" | \"SIGTERM\" | \"SIGINT\",\n listener: (message?: unknown) => void,\n ): unknown;\n env: Record<string, string | undefined>;\n exit(code: number): void;\n}\n\n/**\n * Run pushed jobs until the parent stops the child.\n *\n * - `SIGTERM` / `SIGINT`: let the runs in flight finish, then exit 0.\n * - Parent gone (`disconnect`): nobody can receive results, so exit at once. Whatever was running\n * loses its lease with the parent and is redelivered to another consumer.\n *\n * @throws When not running as a forked child (no IPC channel), or when the image engine cannot load.\n */\nexport async function runCaptureChild(\n options: CaptureChildOptions,\n host: ChildHost = processHost(),\n): Promise<void> {\n const { send } = host;\n if (!send) {\n throw new Error(\"runCaptureChild must run in a forked process; set testing.workerEntry\");\n }\n const logger = options.logger ?? createShelfLogger();\n applyImageSettings(options.images, options.compare);\n await ensureImageEngine(logger);\n const child = new CaptureChild(buildHandler(options, host.env, logger), logger, (message) => {\n send.call(host, message);\n });\n const stopped = wireHost(host, child);\n await stopped;\n host.exit(0);\n}\n\n/** Feed the parent's messages to the child; resolves once a signal asked it to stop and it drained. */\nasync function wireHost(host: ChildHost, child: CaptureChild): Promise<void> {\n const stopped = Promise.withResolvers<null>();\n const settle = (): void => {\n stopped.resolve(null);\n };\n const drain = (): void => {\n child.drain().then(settle, settle);\n };\n host.on(\"message\", (message) => {\n child.onMessage(message);\n });\n host.on(\"SIGTERM\", drain);\n host.on(\"SIGINT\", drain);\n host.on(\"disconnect\", () => {\n host.exit(0);\n });\n await stopped.promise;\n}\n\n/** The capture handler over the app's own adapters; shard jobs go straight onto the database queue. */\nfunction buildHandler(\n options: CaptureChildOptions,\n env: Record<string, string | undefined>,\n logger: Logger,\n): KindHandler {\n const maxRetries = options.retries ?? positiveInt(env[CAPTURE_RETRIES_ENV], 0) ?? DEFAULT_RETRIES;\n const queue = createDatabaseCaptureQueue(options.db, {\n retries: maxRetries,\n logger: logger.child({ component: \"child-queue\" }),\n });\n return createCaptureHandler({\n db: options.db,\n storage: options.storage,\n runner: options.runner,\n scratchDir: options.scratchDir,\n notifiers: options.notifiers,\n config: options.config,\n // The parent's consumer runs `notify` jobs, so the child hands state deliveries to the queue.\n buildState: options.buildState ?? notifyJobsPublisher(options, queue, logger),\n design: options.design,\n secret: options.secret,\n viewports: options.viewports,\n shardSize: options.shardSize ?? positiveInt(env[CAPTURE_SHARD_SIZE_ENV]),\n maxRetries,\n enqueue: async (job) => {\n await queue.enqueue(job);\n },\n logger,\n });\n}\n\n/** Publisher that queues one `notify` job per git channel on the database queue. */\nfunction notifyJobsPublisher(\n options: CaptureChildOptions,\n queue: ReturnType<typeof createDatabaseCaptureQueue>,\n logger: Logger,\n): BuildStatePublisher {\n return createBuildStatePublisher({\n deps: {\n db: options.db,\n storage: options.storage,\n notifiers: options.notifiers ?? [],\n secret: options.secret,\n config: options.config ?? {},\n publicOrigin: originOf(options),\n flags: () => ({ testing: true, review: false }),\n logger,\n },\n enqueue: createNotifyEnqueuer({ db: options.db }, queue),\n });\n}\n\n/** One run per pushed job; the parent decides what runs, how many at once, and when it ends. */\nclass CaptureChild {\n readonly #handler: KindHandler;\n readonly #logger: Logger;\n readonly #send: (message: ChildMessage) => void;\n readonly #running = new Map<string, { job: ConsumerJob; done: Promise<void> }>();\n\n constructor(handler: KindHandler, logger: Logger, send: (message: ChildMessage) => void) {\n this.#handler = handler;\n this.#logger = logger;\n this.#send = send;\n }\n\n onMessage(message: unknown): void {\n if (!isParentMessage(message)) {\n return;\n }\n if (message.type === \"run\") {\n this.#start(message.runId, message.job);\n return;\n }\n const run = this.#running.get(message.runId);\n if (run) {\n this.#handler.cancel?.(run.job).catch(ignore);\n }\n }\n\n /** Resolves once every run in flight has finished. */\n async drain(): Promise<void> {\n while (this.#running.size > 0) {\n // oxlint-disable-next-line no-await-in-loop -- runs started while draining join the drain\n await Promise.allSettled(\n [...this.#running.values()].map(async (run) => {\n await run.done;\n }),\n );\n }\n }\n\n #start(runId: string, job: ConsumerJob): void {\n const done = this.#execute(runId, job).finally(() => {\n this.#running.delete(runId);\n });\n this.#running.set(runId, { job, done });\n }\n\n async #execute(runId: string, job: ConsumerJob): Promise<void> {\n try {\n await this.#handler.run(job, {\n logger: this.#logger.child({ buildId: job.buildId, jobId: job.jobId, runId }),\n touch: throttled(() => {\n this.#send({ type: \"progress\", runId });\n }),\n });\n this.#send({ type: \"done\", runId });\n } catch (error) {\n this.#send({\n type: \"failed\",\n runId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n}\n\n/** At most one progress message per second per run: a capture logs far more often than a lease needs. */\nconst PROGRESS_INTERVAL_MS = 1000;\n\nfunction throttled(report: () => void): () => void {\n let last = 0;\n return () => {\n const now = Date.now();\n if (now - last >= PROGRESS_INTERVAL_MS) {\n last = now;\n report();\n }\n };\n}\n\nfunction ignore(): void {}\n\nfunction positiveInt(value: string | undefined, min = 1): number | undefined {\n const parsed = Number(value);\n return value !== undefined && Number.isInteger(parsed) && parsed >= min ? parsed : undefined;\n}\n\nfunction processHost(): ChildHost {\n return {\n send: process.send ? (message) => process.send?.(message) : undefined,\n on: (event, listener) => process.on(event as never, listener as never),\n env: process.env,\n // A forked child has to end its own process once the runs have drained.\n // oxlint-disable-next-line unicorn/no-process-exit\n exit: (code) => process.exit(code),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyDA,eAAsB,gBACpB,SACA,OAAkB,YAAY,GACf;CACf,MAAM,EAAE,SAAS;CACjB,IAAI,CAAC,MACH,MAAM,IAAI,MAAM,uEAAuE;CAEzF,MAAM,SAAS,QAAQ,UAAU,kBAAkB;CACnD,mBAAmB,QAAQ,QAAQ,QAAQ,OAAO;CAClD,MAAM,kBAAkB,MAAM;CAK9B,MADgB,SAAS,MAAM,IAHb,aAAa,aAAa,SAAS,KAAK,KAAK,MAAM,GAAG,SAAS,YAAY;EAC3F,KAAK,KAAK,MAAM,OAAO;CACzB,CACmC,CACvB;CACZ,KAAK,KAAK,CAAC;AACb;;AAGA,eAAe,SAAS,MAAiB,OAAoC;CAC3E,MAAM,UAAU,QAAQ,cAAoB;CAC5C,MAAM,eAAqB;EACzB,QAAQ,QAAQ,IAAI;CACtB;CACA,MAAM,cAAoB;EACxB,MAAM,MAAM,CAAC,CAAC,KAAK,QAAQ,MAAM;CACnC;CACA,KAAK,GAAG,YAAY,YAAY;EAC9B,MAAM,UAAU,OAAO;CACzB,CAAC;CACD,KAAK,GAAG,WAAW,KAAK;CACxB,KAAK,GAAG,UAAU,KAAK;CACvB,KAAK,GAAG,oBAAoB;EAC1B,KAAK,KAAK,CAAC;CACb,CAAC;CACD,MAAM,QAAQ;AAChB;;AAGA,SAAS,aACP,SACA,KACA,QACa;CACb,MAAM,aAAa,QAAQ,WAAW,YAAY,IAAA,+BAA0B,CAAC,KAAA;CAC7E,MAAM,QAAQ,2BAA2B,QAAQ,IAAI;EACnD,SAAS;EACT,QAAQ,OAAO,MAAM,EAAE,WAAW,cAAc,CAAC;CACnD,CAAC;CACD,OAAO,qBAAqB;EAC1B,IAAI,QAAQ;EACZ,SAAS,QAAQ;EACjB,QAAQ,QAAQ;EAChB,YAAY,QAAQ;EACpB,WAAW,QAAQ;EACnB,QAAQ,QAAQ;EAEhB,YAAY,QAAQ,cAAc,oBAAoB,SAAS,OAAO,MAAM;EAC5E,QAAQ,QAAQ;EAChB,QAAQ,QAAQ;EAChB,WAAW,QAAQ;EACnB,WAAW,QAAQ,aAAa,YAAY,IAAA,gCAA2B;EACvE;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,MAAM,QAAQ,GAAG;EACzB;EACA;CACF,CAAC;AACH;;AAGA,SAAS,oBACP,SACA,OACA,QACqB;CACrB,OAAO,0BAA0B;EAC/B,MAAM;GACJ,IAAI,QAAQ;GACZ,SAAS,QAAQ;GACjB,WAAW,QAAQ,aAAa,CAAC;GACjC,QAAQ,QAAQ;GAChB,QAAQ,QAAQ,UAAU,CAAC;GAC3B,cAAc,SAAS,OAAO;GAC9B,cAAc;IAAE,SAAS;IAAM,QAAQ;GAAM;GAC7C;EACF;EACA,SAAS,qBAAqB,EAAE,IAAI,QAAQ,GAAG,GAAG,KAAK;CACzD,CAAC;AACH;;AAGA,IAAM,eAAN,MAAmB;CACjB;CACA;CACA;CACA,2BAAoB,IAAI,IAAuD;CAE/E,YAAY,SAAsB,QAAgB,MAAuC;EACvF,KAAK,WAAW;EAChB,KAAK,UAAU;EACf,KAAK,QAAQ;CACf;CAEA,UAAU,SAAwB;EAChC,IAAI,CAAC,gBAAgB,OAAO,GAC1B;EAEF,IAAI,QAAQ,SAAS,OAAO;GAC1B,KAAK,OAAO,QAAQ,OAAO,QAAQ,GAAG;GACtC;EACF;EACA,MAAM,MAAM,KAAK,SAAS,IAAI,QAAQ,KAAK;EAC3C,IAAI,KACF,KAAK,SAAS,SAAS,IAAI,GAAG,CAAC,CAAC,MAAM,MAAM;CAEhD;;CAGA,MAAM,QAAuB;EAC3B,OAAO,KAAK,SAAS,OAAO,GAE1B,MAAM,QAAQ,WACZ,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,QAAQ;GAC7C,MAAM,IAAI;EACZ,CAAC,CACH;CAEJ;CAEA,OAAO,OAAe,KAAwB;EAC5C,MAAM,OAAO,KAAK,SAAS,OAAO,GAAG,CAAC,CAAC,cAAc;GACnD,KAAK,SAAS,OAAO,KAAK;EAC5B,CAAC;EACD,KAAK,SAAS,IAAI,OAAO;GAAE;GAAK;EAAK,CAAC;CACxC;CAEA,MAAM,SAAS,OAAe,KAAiC;EAC7D,IAAI;GACF,MAAM,KAAK,SAAS,IAAI,KAAK;IAC3B,QAAQ,KAAK,QAAQ,MAAM;KAAE,SAAS,IAAI;KAAS,OAAO,IAAI;KAAO;IAAM,CAAC;IAC5E,OAAO,gBAAgB;KACrB,KAAK,MAAM;MAAE,MAAM;MAAY;KAAM,CAAC;IACxC,CAAC;GACH,CAAC;GACD,KAAK,MAAM;IAAE,MAAM;IAAQ;GAAM,CAAC;EACpC,SAAS,OAAO;GACd,KAAK,MAAM;IACT,MAAM;IACN;IACA,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAC9D,CAAC;EACH;CACF;AACF;;AAGA,MAAM,uBAAuB;AAE7B,SAAS,UAAU,QAAgC;CACjD,IAAI,OAAO;CACX,aAAa;EACX,MAAM,MAAM,KAAK,IAAI;EACrB,IAAI,MAAM,QAAQ,sBAAsB;GACtC,OAAO;GACP,OAAO;EACT;CACF;AACF;AAEA,SAAS,SAAe,CAAC;AAEzB,SAAS,YAAY,OAA2B,MAAM,GAAuB;CAC3E,MAAM,SAAS,OAAO,KAAK;CAC3B,OAAO,UAAU,KAAA,KAAa,OAAO,UAAU,MAAM,KAAK,UAAU,MAAM,SAAS,KAAA;AACrF;AAEA,SAAS,cAAyB;CAChC,OAAO;EACL,MAAM,QAAQ,QAAQ,YAAY,QAAQ,OAAO,OAAO,IAAI,KAAA;EAC5D,KAAK,OAAO,aAAa,QAAQ,GAAG,OAAgB,QAAiB;EACrE,KAAK,QAAQ;EAGb,OAAO,SAAS,QAAQ,KAAK,IAAI;CACnC;AACF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
//#region src/capture/forked/crash-tracker.ts
|
|
5
|
+
/** Counts child crashes inside a sliding window, to tell a flaky child from a crash loop. */
|
|
6
|
+
var CrashTracker = class {
|
|
7
|
+
#limit;
|
|
8
|
+
#windowMs;
|
|
9
|
+
#times = [];
|
|
10
|
+
constructor(limit, windowMs) {
|
|
11
|
+
this.#limit = limit;
|
|
12
|
+
this.#windowMs = windowMs;
|
|
13
|
+
}
|
|
14
|
+
/** Record a crash; true when this one reaches the limit within the window. */
|
|
15
|
+
record(now = Date.now()) {
|
|
16
|
+
this.#times = [...this.#times.filter((time) => now - time < this.#windowMs), now];
|
|
17
|
+
return this.#times.length >= this.#limit;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
export { CrashTracker };
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=crash-tracker.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crash-tracker.mjs","names":[],"sources":["../../../src/capture/forked/crash-tracker.ts"],"sourcesContent":["/** Counts child crashes inside a sliding window, to tell a flaky child from a crash loop. */\nexport class CrashTracker {\n readonly #limit: number;\n readonly #windowMs: number;\n #times: number[] = [];\n\n constructor(limit: number, windowMs: number) {\n this.#limit = limit;\n this.#windowMs = windowMs;\n }\n\n /** Record a crash; true when this one reaches the limit within the window. */\n record(now: number = Date.now()): boolean {\n this.#times = [...this.#times.filter((time) => now - time < this.#windowMs), now];\n return this.#times.length >= this.#limit;\n }\n}\n"],"mappings":";;;;;AACA,IAAa,eAAb,MAA0B;CACxB;CACA;CACA,SAAmB,CAAC;CAEpB,YAAY,OAAe,UAAkB;EAC3C,KAAK,SAAS;EACd,KAAK,YAAY;CACnB;;CAGA,OAAO,MAAc,KAAK,IAAI,GAAY;EACxC,KAAK,SAAS,CAAC,GAAG,KAAK,OAAO,QAAQ,SAAS,MAAM,OAAO,KAAK,SAAS,GAAG,GAAG;EAChF,OAAO,KAAK,OAAO,UAAU,KAAK;CACpC;AACF"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
//#region src/capture/forked/decision.d.ts
|
|
4
|
+
/** Whether a capture child is worth forking on this host (ADR 0005 phase 3 fallback rules). */
|
|
5
|
+
/** What the decision looks at. */
|
|
6
|
+
interface ForkHost {
|
|
7
|
+
cpus: number;
|
|
8
|
+
memoryBytes: number;
|
|
9
|
+
/** `child_process.fork` exists and works in this runtime. */
|
|
10
|
+
canFork: boolean;
|
|
11
|
+
}
|
|
12
|
+
/** Outcome: fork a child, or stay in-process and say why. */
|
|
13
|
+
type ForkDecision = {
|
|
14
|
+
mode: "fork";
|
|
15
|
+
} | {
|
|
16
|
+
mode: "in-process";
|
|
17
|
+
reason: string;
|
|
18
|
+
};
|
|
19
|
+
/** Smallest host that still gains from a second process: below this the child only costs memory. */
|
|
20
|
+
declare const FORK_MIN_CPUS = 2;
|
|
21
|
+
declare const FORK_MIN_MEMORY_BYTES: number;
|
|
22
|
+
/** Pick the capture process model for a host. */
|
|
23
|
+
declare function decideCaptureProcess(host: ForkHost): ForkDecision;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { FORK_MIN_CPUS, FORK_MIN_MEMORY_BYTES, ForkDecision, ForkHost, decideCaptureProcess };
|
|
26
|
+
//# sourceMappingURL=decision.d.mts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
//#region src/capture/forked/decision.ts
|
|
5
|
+
/** Smallest host that still gains from a second process: below this the child only costs memory. */
|
|
6
|
+
const FORK_MIN_CPUS = 2;
|
|
7
|
+
const FORK_MIN_MEMORY_BYTES = 2 * 1024 ** 3;
|
|
8
|
+
/** Pick the capture process model for a host. */
|
|
9
|
+
function decideCaptureProcess(host) {
|
|
10
|
+
if (!host.canFork) return {
|
|
11
|
+
mode: "in-process",
|
|
12
|
+
reason: "child processes are unavailable in this runtime"
|
|
13
|
+
};
|
|
14
|
+
if (host.cpus < 2) return {
|
|
15
|
+
mode: "in-process",
|
|
16
|
+
reason: `only ${host.cpus} CPU available`
|
|
17
|
+
};
|
|
18
|
+
if (host.memoryBytes < FORK_MIN_MEMORY_BYTES) return {
|
|
19
|
+
mode: "in-process",
|
|
20
|
+
reason: "not enough memory for a second process"
|
|
21
|
+
};
|
|
22
|
+
return { mode: "fork" };
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { FORK_MIN_CPUS, FORK_MIN_MEMORY_BYTES, decideCaptureProcess };
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=decision.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision.mjs","names":[],"sources":["../../../src/capture/forked/decision.ts"],"sourcesContent":["/** Whether a capture child is worth forking on this host (ADR 0005 phase 3 fallback rules). */\n\n/** What the decision looks at. */\nexport interface ForkHost {\n cpus: number;\n memoryBytes: number;\n /** `child_process.fork` exists and works in this runtime. */\n canFork: boolean;\n}\n\n/** Outcome: fork a child, or stay in-process and say why. */\nexport type ForkDecision = { mode: \"fork\" } | { mode: \"in-process\"; reason: string };\n\n/** Smallest host that still gains from a second process: below this the child only costs memory. */\nexport const FORK_MIN_CPUS = 2;\nexport const FORK_MIN_MEMORY_BYTES = 2 * 1024 ** 3;\n\n/** Pick the capture process model for a host. */\nexport function decideCaptureProcess(host: ForkHost): ForkDecision {\n if (!host.canFork) {\n return { mode: \"in-process\", reason: \"child processes are unavailable in this runtime\" };\n }\n if (host.cpus < FORK_MIN_CPUS) {\n return { mode: \"in-process\", reason: `only ${host.cpus} CPU available` };\n }\n if (host.memoryBytes < FORK_MIN_MEMORY_BYTES) {\n return { mode: \"in-process\", reason: \"not enough memory for a second process\" };\n }\n return { mode: \"fork\" };\n}\n"],"mappings":";;;;;AAcA,MAAa,gBAAgB;AAC7B,MAAa,wBAAwB,IAAI,QAAQ;;AAGjD,SAAgB,qBAAqB,MAA8B;CACjE,IAAI,CAAC,KAAK,SACR,OAAO;EAAE,MAAM;EAAc,QAAQ;CAAkD;CAEzF,IAAI,KAAK,OAAA,GACP,OAAO;EAAE,MAAM;EAAc,QAAQ,QAAQ,KAAK,KAAK;CAAgB;CAEzE,IAAI,KAAK,cAAc,uBACrB,OAAO;EAAE,MAAM;EAAc,QAAQ;CAAyC;CAEhF,OAAO,EAAE,MAAM,OAAO;AACxB"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
//#region src/capture/forked/exit.ts
|
|
5
|
+
/** `signal SIGKILL` or `code 1`. */
|
|
6
|
+
function describeExit(info) {
|
|
7
|
+
return info.signal ? `signal ${info.signal}` : `code ${info.code}`;
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
export { describeExit };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=exit.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exit.mjs","names":[],"sources":["../../../src/capture/forked/exit.ts"],"sourcesContent":["/** How a capture child ended, as a phrase for logs and failure messages. */\n\n/** An exit as `child_process` reports it: a code, or the signal that killed the child. */\nexport interface ChildExit {\n code: number | null;\n signal: string | null;\n}\n\n/** `signal SIGKILL` or `code 1`. */\nexport function describeExit(info: ChildExit): string {\n return info.signal ? `signal ${info.signal}` : `code ${info.code}`;\n}\n"],"mappings":";;;;;AASA,SAAgB,aAAa,MAAyB;CACpD,OAAO,KAAK,SAAS,UAAU,KAAK,WAAW,QAAQ,KAAK;AAC9D"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { ChildProcessHandle } from "./channel.mjs";
|
|
4
|
+
//#region src/capture/forked/fork.d.ts
|
|
5
|
+
/** Environment variable the forked child sees; set to this value so a module can tell it is the child. */
|
|
6
|
+
declare const CAPTURE_WORKER_ROLE = "capture-worker";
|
|
7
|
+
/** Environment variable carrying the parent's `testing.retries` to the child, which stamps it on the shard jobs it queues. */
|
|
8
|
+
declare const CAPTURE_RETRIES_ENV = "STORYSHELF_CAPTURE_RETRIES";
|
|
9
|
+
/** Environment variable carrying the parent's `testing.shardSize` to the child, which plans big builds with it. */
|
|
10
|
+
declare const CAPTURE_SHARD_SIZE_ENV = "STORYSHELF_CAPTURE_SHARD_SIZE";
|
|
11
|
+
/** Whether this runtime can fork a capture child. */
|
|
12
|
+
declare function canForkCaptureChild(): boolean;
|
|
13
|
+
/** Options for {@link forkCaptureChild}. */
|
|
14
|
+
interface ForkOptions {
|
|
15
|
+
/** Module to run in the child (`testing.workerEntry`); absolute path or `file:` URL. */
|
|
16
|
+
entry: string;
|
|
17
|
+
/** Extra environment for the child, on top of this process's. */
|
|
18
|
+
env?: Record<string, string>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Fork the capture child with an IPC channel and wrap it as a {@link ChildProcessHandle}.
|
|
22
|
+
*
|
|
23
|
+
* The channel is also the lifeline: if this process dies the child sees `disconnect` and exits.
|
|
24
|
+
* An `exit` hook kills the child too, for parents that stop without a graceful teardown.
|
|
25
|
+
*
|
|
26
|
+
* @throws When the runtime cannot fork, or the fork itself throws.
|
|
27
|
+
*/
|
|
28
|
+
declare function forkCaptureChild(options: ForkOptions): ChildProcessHandle;
|
|
29
|
+
//#endregion
|
|
30
|
+
export { CAPTURE_RETRIES_ENV, CAPTURE_SHARD_SIZE_ENV, CAPTURE_WORKER_ROLE, ForkOptions, canForkCaptureChild, forkCaptureChild };
|
|
31
|
+
//# sourceMappingURL=fork.d.mts.map
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
//#region src/capture/forked/fork.ts
|
|
5
|
+
/** Environment variable the forked child sees; set to this value so a module can tell it is the child. */
|
|
6
|
+
const CAPTURE_WORKER_ROLE = "capture-worker";
|
|
7
|
+
/** Environment variable carrying the parent's `testing.retries` to the child, which stamps it on the shard jobs it queues. */
|
|
8
|
+
const CAPTURE_RETRIES_ENV = "STORYSHELF_CAPTURE_RETRIES";
|
|
9
|
+
/** Environment variable carrying the parent's `testing.shardSize` to the child, which plans big builds with it. */
|
|
10
|
+
const CAPTURE_SHARD_SIZE_ENV = "STORYSHELF_CAPTURE_SHARD_SIZE";
|
|
11
|
+
/** Grace between SIGTERM and SIGKILL when the parent stops a child. */
|
|
12
|
+
const KILL_ESCALATION_MS = 3e4;
|
|
13
|
+
/** `node:child_process`, or undefined where the runtime has none (edge, browsers). Loaded synchronously. */
|
|
14
|
+
function childProcessModule() {
|
|
15
|
+
const getBuiltin = process.getBuiltinModule;
|
|
16
|
+
return typeof getBuiltin === "function" ? getBuiltin.call(process, "node:child_process") : void 0;
|
|
17
|
+
}
|
|
18
|
+
/** Whether this runtime can fork a capture child. */
|
|
19
|
+
function canForkCaptureChild() {
|
|
20
|
+
return typeof childProcessModule()?.fork === "function";
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Fork the capture child with an IPC channel and wrap it as a {@link ChildProcessHandle}.
|
|
24
|
+
*
|
|
25
|
+
* The channel is also the lifeline: if this process dies the child sees `disconnect` and exits.
|
|
26
|
+
* An `exit` hook kills the child too, for parents that stop without a graceful teardown.
|
|
27
|
+
*
|
|
28
|
+
* @throws When the runtime cannot fork, or the fork itself throws.
|
|
29
|
+
*/
|
|
30
|
+
function forkCaptureChild(options) {
|
|
31
|
+
const childProcess = childProcessModule();
|
|
32
|
+
if (!childProcess) throw new Error("child processes are not available in this runtime");
|
|
33
|
+
const child = childProcess.fork(options.entry, [], {
|
|
34
|
+
env: {
|
|
35
|
+
...process.env,
|
|
36
|
+
...options.env,
|
|
37
|
+
STORYSHELF_ROLE: CAPTURE_WORKER_ROLE
|
|
38
|
+
},
|
|
39
|
+
serialization: "json",
|
|
40
|
+
stdio: [
|
|
41
|
+
"inherit",
|
|
42
|
+
"inherit",
|
|
43
|
+
"inherit",
|
|
44
|
+
"ipc"
|
|
45
|
+
]
|
|
46
|
+
});
|
|
47
|
+
const stopOnExit = () => {
|
|
48
|
+
child.kill("SIGKILL");
|
|
49
|
+
};
|
|
50
|
+
process.once("exit", stopOnExit);
|
|
51
|
+
return wrap(child, stopOnExit);
|
|
52
|
+
}
|
|
53
|
+
function wrap(child, stopOnExit) {
|
|
54
|
+
return {
|
|
55
|
+
send: (message) => {
|
|
56
|
+
if (child.connected) child.send(message);
|
|
57
|
+
},
|
|
58
|
+
onMessage: (listener) => {
|
|
59
|
+
child.on("message", listener);
|
|
60
|
+
},
|
|
61
|
+
onExit: (listener) => {
|
|
62
|
+
let reported = false;
|
|
63
|
+
const report = (code, signal) => {
|
|
64
|
+
if (!reported) {
|
|
65
|
+
reported = true;
|
|
66
|
+
process.off("exit", stopOnExit);
|
|
67
|
+
listener({
|
|
68
|
+
code,
|
|
69
|
+
signal
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
child.once("exit", report);
|
|
74
|
+
child.once("error", () => {
|
|
75
|
+
report(1, null);
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
kill: (force) => {
|
|
79
|
+
if (force) {
|
|
80
|
+
child.kill("SIGKILL");
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
child.kill("SIGTERM");
|
|
84
|
+
setTimeout(() => {
|
|
85
|
+
if (child.exitCode === null && child.signalCode === null) child.kill("SIGKILL");
|
|
86
|
+
}, KILL_ESCALATION_MS).unref();
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
91
|
+
export { CAPTURE_RETRIES_ENV, CAPTURE_SHARD_SIZE_ENV, CAPTURE_WORKER_ROLE, canForkCaptureChild, forkCaptureChild };
|
|
92
|
+
|
|
93
|
+
//# sourceMappingURL=fork.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fork.mjs","names":[],"sources":["../../../src/capture/forked/fork.ts"],"sourcesContent":["/** The real `child_process.fork` behind {@link ChildProcessHandle}. */\nimport type { ChildProcess } from \"node:child_process\";\nimport type { ChildProcessHandle } from \"./channel.ts\";\nimport type { ParentMessage } from \"./protocol.ts\";\n\n/** Environment variable the forked child sees; set to this value so a module can tell it is the child. */\nexport const CAPTURE_WORKER_ROLE = \"capture-worker\";\n\n/** Environment variable carrying the parent's `testing.retries` to the child, which stamps it on the shard jobs it queues. */\nexport const CAPTURE_RETRIES_ENV = \"STORYSHELF_CAPTURE_RETRIES\";\n\n/** Environment variable carrying the parent's `testing.shardSize` to the child, which plans big builds with it. */\nexport const CAPTURE_SHARD_SIZE_ENV = \"STORYSHELF_CAPTURE_SHARD_SIZE\";\n\n/** Grace between SIGTERM and SIGKILL when the parent stops a child. */\nconst KILL_ESCALATION_MS = 30_000;\n\ntype ChildProcessModule = typeof import(\"node:child_process\");\n\n/** `node:child_process`, or undefined where the runtime has none (edge, browsers). Loaded synchronously. */\nfunction childProcessModule(): ChildProcessModule | undefined {\n const getBuiltin = (process as { getBuiltinModule?: (id: string) => unknown }).getBuiltinModule;\n return typeof getBuiltin === \"function\"\n ? (getBuiltin.call(process, \"node:child_process\") as ChildProcessModule | undefined)\n : undefined;\n}\n\n/** Whether this runtime can fork a capture child. */\nexport function canForkCaptureChild(): boolean {\n return typeof childProcessModule()?.fork === \"function\";\n}\n\n/** Options for {@link forkCaptureChild}. */\nexport interface ForkOptions {\n /** Module to run in the child (`testing.workerEntry`); absolute path or `file:` URL. */\n entry: string;\n /** Extra environment for the child, on top of this process's. */\n env?: Record<string, string>;\n}\n\n/**\n * Fork the capture child with an IPC channel and wrap it as a {@link ChildProcessHandle}.\n *\n * The channel is also the lifeline: if this process dies the child sees `disconnect` and exits.\n * An `exit` hook kills the child too, for parents that stop without a graceful teardown.\n *\n * @throws When the runtime cannot fork, or the fork itself throws.\n */\nexport function forkCaptureChild(options: ForkOptions): ChildProcessHandle {\n const childProcess = childProcessModule();\n if (!childProcess) {\n throw new Error(\"child processes are not available in this runtime\");\n }\n const child = childProcess.fork(options.entry, [], {\n env: { ...process.env, ...options.env, STORYSHELF_ROLE: CAPTURE_WORKER_ROLE },\n serialization: \"json\",\n stdio: [\"inherit\", \"inherit\", \"inherit\", \"ipc\"],\n });\n const stopOnExit = (): void => {\n child.kill(\"SIGKILL\");\n };\n process.once(\"exit\", stopOnExit);\n return wrap(child, stopOnExit);\n}\n\nfunction wrap(child: ChildProcess, stopOnExit: () => void): ChildProcessHandle {\n return {\n send: (message: ParentMessage): void => {\n if (child.connected) {\n child.send(message);\n }\n },\n onMessage: (listener) => {\n child.on(\"message\", listener);\n },\n onExit: (listener) => {\n let reported = false;\n const report = (code: number | null, signal: string | null): void => {\n if (!reported) {\n reported = true;\n process.off(\"exit\", stopOnExit);\n listener({ code, signal });\n }\n };\n child.once(\"exit\", report);\n // A child that never started emits `error` and no `exit`.\n child.once(\"error\", () => {\n report(1, null);\n });\n },\n kill: (force) => {\n if (force) {\n child.kill(\"SIGKILL\");\n return;\n }\n child.kill(\"SIGTERM\");\n setTimeout(() => {\n if (child.exitCode === null && child.signalCode === null) {\n child.kill(\"SIGKILL\");\n }\n }, KILL_ESCALATION_MS).unref();\n },\n };\n}\n"],"mappings":";;;;;AAMA,MAAa,sBAAsB;;AAGnC,MAAa,sBAAsB;;AAGnC,MAAa,yBAAyB;;AAGtC,MAAM,qBAAqB;;AAK3B,SAAS,qBAAqD;CAC5D,MAAM,aAAc,QAA2D;CAC/E,OAAO,OAAO,eAAe,aACxB,WAAW,KAAK,SAAS,oBAAoB,IAC9C,KAAA;AACN;;AAGA,SAAgB,sBAA+B;CAC7C,OAAO,OAAO,mBAAmB,CAAC,EAAE,SAAS;AAC/C;;;;;;;;;AAkBA,SAAgB,iBAAiB,SAA0C;CACzE,MAAM,eAAe,mBAAmB;CACxC,IAAI,CAAC,cACH,MAAM,IAAI,MAAM,mDAAmD;CAErE,MAAM,QAAQ,aAAa,KAAK,QAAQ,OAAO,CAAC,GAAG;EACjD,KAAK;GAAE,GAAG,QAAQ;GAAK,GAAG,QAAQ;GAAK,iBAAiB;EAAoB;EAC5E,eAAe;EACf,OAAO;GAAC;GAAW;GAAW;GAAW;EAAK;CAChD,CAAC;CACD,MAAM,mBAAyB;EAC7B,MAAM,KAAK,SAAS;CACtB;CACA,QAAQ,KAAK,QAAQ,UAAU;CAC/B,OAAO,KAAK,OAAO,UAAU;AAC/B;AAEA,SAAS,KAAK,OAAqB,YAA4C;CAC7E,OAAO;EACL,OAAO,YAAiC;GACtC,IAAI,MAAM,WACR,MAAM,KAAK,OAAO;EAEtB;EACA,YAAY,aAAa;GACvB,MAAM,GAAG,WAAW,QAAQ;EAC9B;EACA,SAAS,aAAa;GACpB,IAAI,WAAW;GACf,MAAM,UAAU,MAAqB,WAAgC;IACnE,IAAI,CAAC,UAAU;KACb,WAAW;KACX,QAAQ,IAAI,QAAQ,UAAU;KAC9B,SAAS;MAAE;MAAM;KAAO,CAAC;IAC3B;GACF;GACA,MAAM,KAAK,QAAQ,MAAM;GAEzB,MAAM,KAAK,eAAe;IACxB,OAAO,GAAG,IAAI;GAChB,CAAC;EACH;EACA,OAAO,UAAU;GACf,IAAI,OAAO;IACT,MAAM,KAAK,SAAS;IACpB;GACF;GACA,MAAM,KAAK,SAAS;GACpB,iBAAiB;IACf,IAAI,MAAM,aAAa,QAAQ,MAAM,eAAe,MAClD,MAAM,KAAK,SAAS;GAExB,GAAG,kBAAkB,CAAC,CAAC,MAAM;EAC/B;CACF;AACF"}
|