@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,107 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
//#region src/capture/modes.ts
|
|
5
|
+
/** Upper bound on captures (global viewports + modes) per story. */
|
|
6
|
+
const MAX_MODES_PER_STORY = 8;
|
|
7
|
+
const SLUG_STRIP = /[^a-z0-9]+/gu;
|
|
8
|
+
const SLUG_EDGES = /^-+|-+$/gu;
|
|
9
|
+
const HEX_COLOR = /^#(?<hex>[0-9a-fA-F]{3,8})$/u;
|
|
10
|
+
/**
|
|
11
|
+
* Baseline identity for a mode name: lower-case, URL/path-safe, stable under
|
|
12
|
+
* whitespace/case edits. Throws for names with no usable characters.
|
|
13
|
+
*/
|
|
14
|
+
function modeSlug(name) {
|
|
15
|
+
const slug = name.toLowerCase().replace(SLUG_STRIP, "-").replace(SLUG_EDGES, "");
|
|
16
|
+
if (slug === "") throw new Error(`Invalid mode name: ${JSON.stringify(name)}`);
|
|
17
|
+
return slug;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Apply `modes` on top of a story's base viewports. A mode named like an
|
|
21
|
+
* existing viewport replaces it; `disable: true` drops that entry (inherited
|
|
22
|
+
* mode or viewport). Output order: base viewports first, then new modes.
|
|
23
|
+
*/
|
|
24
|
+
function applyModes(modes, base, lookup) {
|
|
25
|
+
if (!modes) return [...base];
|
|
26
|
+
const bySlug = new Map(base.map((viewport) => [modeSlug(viewport.name), viewport]));
|
|
27
|
+
const fallback = base[0] ?? {
|
|
28
|
+
name: "desktop",
|
|
29
|
+
width: 1280,
|
|
30
|
+
height: 720
|
|
31
|
+
};
|
|
32
|
+
for (const [name, definition] of Object.entries(modes)) {
|
|
33
|
+
const slug = modeSlug(name);
|
|
34
|
+
if (definition.disable === true) bySlug.delete(slug);
|
|
35
|
+
else bySlug.set(slug, modeViewport(slug, definition, fallback, lookup));
|
|
36
|
+
}
|
|
37
|
+
return [...bySlug.values()];
|
|
38
|
+
}
|
|
39
|
+
/** Keep only allow-listed modes; `null`/empty allowlist keeps all. */
|
|
40
|
+
function filterModes(modes, allowlist) {
|
|
41
|
+
if (!modes || !allowlist || allowlist.length === 0) return modes;
|
|
42
|
+
const allowed = new Set(allowlist.map((name) => modeSlug(name)));
|
|
43
|
+
return Object.fromEntries(Object.entries(modes).filter(([name, definition]) => definition.disable === true || allowed.has(modeSlug(name))));
|
|
44
|
+
}
|
|
45
|
+
/** Parse the project's JSON `modesAllowlist` column; `null` when unset/invalid. */
|
|
46
|
+
function parseModesAllowlist(raw) {
|
|
47
|
+
if (!raw) return null;
|
|
48
|
+
try {
|
|
49
|
+
const parsed = JSON.parse(raw);
|
|
50
|
+
return isStringArray(parsed) ? parsed : null;
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Encode globals for Storybook's `globals` URL arg (`a:b;c.d:e`). Nested
|
|
57
|
+
* objects flatten to dot paths; booleans/null/undefined/hex colors use the
|
|
58
|
+
* `!`-prefixed forms Storybook parses. Plain strings pass through (Storybook
|
|
59
|
+
* only accepts `[a-zA-Z0-9 _-]` and silently drops anything else).
|
|
60
|
+
*/
|
|
61
|
+
function buildGlobalsParam(globals) {
|
|
62
|
+
return flatten(globals, "").map(([key, value]) => `${key}:${encodeValue(value)}`).join(";");
|
|
63
|
+
}
|
|
64
|
+
function isStringArray(value) {
|
|
65
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
66
|
+
}
|
|
67
|
+
function modeViewport(slug, definition, fallback, lookup) {
|
|
68
|
+
const { viewport, disable: _disable, ...globals } = definition;
|
|
69
|
+
const size = resolveSize(viewport, fallback, lookup);
|
|
70
|
+
return {
|
|
71
|
+
name: slug,
|
|
72
|
+
width: size.width,
|
|
73
|
+
height: size.height,
|
|
74
|
+
...Object.keys(globals).length > 0 ? { globals } : {}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function resolveSize(value, fallback, lookup) {
|
|
78
|
+
if (value === void 0) return fallback;
|
|
79
|
+
if (typeof value === "number") return {
|
|
80
|
+
width: Math.round(value),
|
|
81
|
+
height: fallback.height
|
|
82
|
+
};
|
|
83
|
+
if (typeof value === "string") return lookup(value) ?? fallback;
|
|
84
|
+
return {
|
|
85
|
+
width: Math.round(value.width),
|
|
86
|
+
height: Math.round(value.height)
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function flatten(value, prefix) {
|
|
90
|
+
return Object.entries(value).flatMap(([key, entry]) => isPlainObject(entry) ? flatten(entry, `${prefix}${key}.`) : [[`${prefix}${key}`, entry]]);
|
|
91
|
+
}
|
|
92
|
+
function isPlainObject(value) {
|
|
93
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
94
|
+
}
|
|
95
|
+
function encodeValue(value) {
|
|
96
|
+
if (value === true) return "!true";
|
|
97
|
+
if (value === false) return "!false";
|
|
98
|
+
if (value === null) return "!null";
|
|
99
|
+
if (value === void 0) return "!undefined";
|
|
100
|
+
const text = typeof value === "string" || typeof value === "number" ? String(value) : JSON.stringify(value) ?? "";
|
|
101
|
+
const hex = HEX_COLOR.exec(text)?.groups?.["hex"];
|
|
102
|
+
return hex ? `!hex(${hex})` : text;
|
|
103
|
+
}
|
|
104
|
+
//#endregion
|
|
105
|
+
export { MAX_MODES_PER_STORY, applyModes, buildGlobalsParam, filterModes, modeSlug, parseModesAllowlist };
|
|
106
|
+
|
|
107
|
+
//# sourceMappingURL=modes.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modes.mjs","names":[],"sources":["../../src/capture/modes.ts"],"sourcesContent":["/** Named capture modes: parameters → viewports carrying Storybook globals. */\nimport type { ModeDefinition, ModeMap, ModeViewportValue, Viewport } from \"./adapter.ts\";\n\n/** Upper bound on captures (global viewports + modes) per story. */\nexport const MAX_MODES_PER_STORY = 8;\n\n/** Resolves a Storybook viewport name to dimensions (story config, then builtins). */\nexport type ViewportLookup = (name: string) => Viewport | undefined;\n\nconst SLUG_STRIP = /[^a-z0-9]+/gu;\nconst SLUG_EDGES = /^-+|-+$/gu;\nconst HEX_COLOR = /^#(?<hex>[0-9a-fA-F]{3,8})$/u;\n\n/**\n * Baseline identity for a mode name: lower-case, URL/path-safe, stable under\n * whitespace/case edits. Throws for names with no usable characters.\n */\nexport function modeSlug(name: string): string {\n const slug = name.toLowerCase().replace(SLUG_STRIP, \"-\").replace(SLUG_EDGES, \"\");\n if (slug === \"\") {\n throw new Error(`Invalid mode name: ${JSON.stringify(name)}`);\n }\n return slug;\n}\n\n/**\n * Apply `modes` on top of a story's base viewports. A mode named like an\n * existing viewport replaces it; `disable: true` drops that entry (inherited\n * mode or viewport). Output order: base viewports first, then new modes.\n */\nexport function applyModes(\n modes: ModeMap | undefined,\n base: Viewport[],\n lookup: ViewportLookup,\n): Viewport[] {\n if (!modes) return [...base];\n const bySlug = new Map<string, Viewport>(\n base.map((viewport) => [modeSlug(viewport.name), viewport]),\n );\n const fallback = base[0] ?? { name: \"desktop\", width: 1280, height: 720 };\n for (const [name, definition] of Object.entries(modes)) {\n const slug = modeSlug(name);\n if (definition.disable === true) {\n bySlug.delete(slug);\n } else {\n bySlug.set(slug, modeViewport(slug, definition, fallback, lookup));\n }\n }\n return [...bySlug.values()];\n}\n\n/** Keep only allow-listed modes; `null`/empty allowlist keeps all. */\nexport function filterModes(\n modes: ModeMap | undefined,\n allowlist: readonly string[] | null,\n): ModeMap | undefined {\n if (!modes || !allowlist || allowlist.length === 0) return modes;\n const allowed = new Set(allowlist.map((name) => modeSlug(name)));\n return Object.fromEntries(\n Object.entries(modes).filter(\n ([name, definition]) => definition.disable === true || allowed.has(modeSlug(name)),\n ),\n );\n}\n\n/** Parse the project's JSON `modesAllowlist` column; `null` when unset/invalid. */\nexport function parseModesAllowlist(raw: string | null | undefined): string[] | null {\n if (!raw) return null;\n try {\n const parsed: unknown = JSON.parse(raw);\n return isStringArray(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Encode globals for Storybook's `globals` URL arg (`a:b;c.d:e`). Nested\n * objects flatten to dot paths; booleans/null/undefined/hex colors use the\n * `!`-prefixed forms Storybook parses. Plain strings pass through (Storybook\n * only accepts `[a-zA-Z0-9 _-]` and silently drops anything else).\n */\nexport function buildGlobalsParam(globals: Record<string, unknown>): string {\n return flatten(globals, \"\")\n .map(([key, value]) => `${key}:${encodeValue(value)}`)\n .join(\";\");\n}\n\nfunction isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((item) => typeof item === \"string\");\n}\n\nfunction modeViewport(\n slug: string,\n definition: ModeDefinition,\n fallback: Viewport,\n lookup: ViewportLookup,\n): Viewport {\n const { viewport, disable: _disable, ...globals } = definition;\n const size = resolveSize(viewport, fallback, lookup);\n return {\n name: slug,\n width: size.width,\n height: size.height,\n ...(Object.keys(globals).length > 0 ? { globals } : {}),\n };\n}\n\nfunction resolveSize(\n value: ModeViewportValue | undefined,\n fallback: Viewport,\n lookup: ViewportLookup,\n): { width: number; height: number } {\n if (value === undefined) return fallback;\n if (typeof value === \"number\") return { width: Math.round(value), height: fallback.height };\n if (typeof value === \"string\") return lookup(value) ?? fallback;\n return { width: Math.round(value.width), height: Math.round(value.height) };\n}\n\nfunction flatten(value: Record<string, unknown>, prefix: string): [string, unknown][] {\n return Object.entries(value).flatMap(([key, entry]): [string, unknown][] =>\n isPlainObject(entry) ? flatten(entry, `${prefix}${key}.`) : [[`${prefix}${key}`, entry]],\n );\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction encodeValue(value: unknown): string {\n if (value === true) return \"!true\";\n if (value === false) return \"!false\";\n if (value === null) return \"!null\";\n if (value === undefined) return \"!undefined\";\n const text =\n typeof value === \"string\" || typeof value === \"number\"\n ? String(value)\n : (JSON.stringify(value) ?? \"\");\n const hex = HEX_COLOR.exec(text)?.groups?.[\"hex\"];\n return hex ? `!hex(${hex})` : text;\n}\n"],"mappings":"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"}
|
|
@@ -3,13 +3,16 @@ import __tsdown_shims_url from 'node:url';
|
|
|
3
3
|
import { Viewport } from "./adapter.mjs";
|
|
4
4
|
import { CaptureRunner } from "../adapter/capture-runner.mjs";
|
|
5
5
|
import { DatabaseAdapter } from "../adapter/database.mjs";
|
|
6
|
+
import { DesignProvider } from "../adapter/design.mjs";
|
|
6
7
|
import { StorageAdapter } from "../adapter/storage.mjs";
|
|
7
8
|
import { SecretInput } from "../utils/encrypt.mjs";
|
|
8
9
|
import { BuildTables } from "../models/build.mjs";
|
|
9
10
|
import { AttemptLogRecorder, CaptureAttemptTables } from "../models/capture-attempt.mjs";
|
|
10
11
|
import { CaptureLogTables } from "../models/capture-log.mjs";
|
|
11
12
|
import { ProjectTables } from "../models/project.mjs";
|
|
13
|
+
import { CaptureProgress } from "./progress.mjs";
|
|
12
14
|
import { PipelineTables } from "./pipeline.mjs";
|
|
15
|
+
import { ExtractionLimits } from "./publish/limits.mjs";
|
|
13
16
|
import { Logger } from "pino";
|
|
14
17
|
//#region src/capture/orchestrator.d.ts
|
|
15
18
|
/** Table handles required by the orchestrator. */
|
|
@@ -21,12 +24,42 @@ interface CaptureJobOptions {
|
|
|
21
24
|
storage: StorageAdapter;
|
|
22
25
|
runner: CaptureRunner;
|
|
23
26
|
scratchDir: string;
|
|
27
|
+
/** Entry and size caps of the zip extraction (`maxZipEntries`, `maxUnzippedBytes`); built-in defaults when unset. */
|
|
28
|
+
limits?: ExtractionLimits | undefined;
|
|
24
29
|
viewports?: Viewport[];
|
|
25
30
|
logger?: Logger;
|
|
26
31
|
/** Server secret for decrypting webhook secrets at send time. */
|
|
27
32
|
secret?: SecretInput;
|
|
33
|
+
/** Design providers validating declared design links; none ⇒ links are not extracted. */
|
|
34
|
+
design?: DesignProvider[];
|
|
35
|
+
/** The resolved public origin (ADR 0007, decision 6); targets that need a URL wait for it. */
|
|
36
|
+
publicOrigin?: (() => Promise<string | undefined>) | undefined;
|
|
28
37
|
/** Render budget override (tests use a short fuse). */
|
|
29
38
|
renderTimeoutMs?: number | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Target captures (story x viewport x engine) per shard. Unset turns sharding off; a build of up
|
|
41
|
+
* to twice this size still runs as one shard, in this process, exactly as without sharding.
|
|
42
|
+
*/
|
|
43
|
+
shardSize?: number | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Puts a job on the capture queue. Sharding needs it to hand the shards out; without it every
|
|
46
|
+
* build runs in one piece.
|
|
47
|
+
*/
|
|
48
|
+
enqueue?: ((job: ShardJob) => Promise<void>) | undefined;
|
|
49
|
+
}
|
|
50
|
+
/** A shard job: the build and the `capture_shards` row to render. */
|
|
51
|
+
interface ShardJob {
|
|
52
|
+
buildId: string;
|
|
53
|
+
shard: string;
|
|
54
|
+
reqId?: string | undefined;
|
|
55
|
+
traceparent?: string | undefined;
|
|
56
|
+
}
|
|
57
|
+
/** What a capture job reports: counts for the attempt row, and how many shards it handed out. */
|
|
58
|
+
interface CaptureSummary {
|
|
59
|
+
storyCount: number;
|
|
60
|
+
failedCount: number;
|
|
61
|
+
/** Set when the build was split: the shards are still to run and this job did not finish it. */
|
|
62
|
+
shards?: number;
|
|
30
63
|
}
|
|
31
64
|
/** Pointer to the attempt row owning this run's log history. */
|
|
32
65
|
interface CaptureAttemptRef {
|
|
@@ -47,6 +80,8 @@ interface CaptureJobInput {
|
|
|
47
80
|
attempt?: CaptureAttemptRef;
|
|
48
81
|
/** Recorder mirroring phase logs into the attempt's log history. */
|
|
49
82
|
recordLog?: AttemptLogRecorder;
|
|
83
|
+
/** Reports render and persist progress to the job's lease, apart from logging. */
|
|
84
|
+
progress?: CaptureProgress | undefined;
|
|
50
85
|
}
|
|
51
86
|
/**
|
|
52
87
|
* Run the full capture for a build: extract, render, persist, and finalize.
|
|
@@ -58,10 +93,7 @@ interface CaptureJobInput {
|
|
|
58
93
|
* @param options - Adapters, scratch dir, viewports, and logger.
|
|
59
94
|
* @returns Story and blocking-failure counts for the attempt row.
|
|
60
95
|
*/
|
|
61
|
-
declare function executeCaptureJob(input: CaptureJobInput, options: CaptureJobOptions): Promise<
|
|
62
|
-
storyCount: number;
|
|
63
|
-
failedCount: number;
|
|
64
|
-
}>;
|
|
96
|
+
declare function executeCaptureJob(input: CaptureJobInput, options: CaptureJobOptions): Promise<CaptureSummary>;
|
|
65
97
|
//#endregion
|
|
66
|
-
export { CaptureAttemptRef, CaptureJobInput, CaptureJobOptions, OrchestratorTables, executeCaptureJob };
|
|
98
|
+
export { CaptureAttemptRef, CaptureJobInput, CaptureJobOptions, CaptureSummary, OrchestratorTables, ShardJob, executeCaptureJob };
|
|
67
99
|
//# sourceMappingURL=orchestrator.d.mts.map
|
|
@@ -1,51 +1,34 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { parseDesignUrl } from "../adapter/design.mjs";
|
|
5
|
+
import { assertSupportedBrowsers, resolveBrowsers } from "./browsers.mjs";
|
|
6
|
+
import { offeredTargets } from "./targets.mjs";
|
|
7
|
+
import { storybookUrlSource } from "./storybook-urls.mjs";
|
|
8
|
+
import { parseModesAllowlist } from "./modes.mjs";
|
|
9
|
+
import { DEFAULT_VIEWPORTS, isDisabledStory } from "./adapter.mjs";
|
|
6
10
|
import { emitAttemptLog } from "../models/capture-attempt.mjs";
|
|
7
|
-
import {
|
|
11
|
+
import { classifyFailures } from "./failure-sets.mjs";
|
|
12
|
+
import { logInvalidDesigns } from "./design.mjs";
|
|
13
|
+
import { StorybookAdapter } from "./storybook.mjs";
|
|
14
|
+
import { runProjectTests } from "./tests.mjs";
|
|
15
|
+
import { BuildModel } from "../models/build.mjs";
|
|
8
16
|
import { parentContext, withSpan } from "../tracing.mjs";
|
|
9
17
|
import { roundedTimings, runWithTimings, timed } from "../utils/timing.mjs";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
18
|
+
import { affectedPathsFor } from "./affected.mjs";
|
|
19
|
+
import { assertModeCaps, enrichStories } from "./enrich.mjs";
|
|
20
|
+
import { captureAcrossBrowsers } from "./matrix.mjs";
|
|
12
21
|
import { resolveViewports } from "./sizing.mjs";
|
|
13
22
|
import { persistCapture } from "./pipeline.mjs";
|
|
23
|
+
import { removeJobScratch } from "./scratch.mjs";
|
|
24
|
+
import { fanOutCapture } from "./shards/fan-out.mjs";
|
|
14
25
|
import { extractStorybookToScratch, persistStorybookStatics } from "./statics.mjs";
|
|
26
|
+
import { loadTarget } from "./target.mjs";
|
|
15
27
|
//#region src/capture/orchestrator.ts
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
/** Cap failure error text so one chatty runner cannot flood rows and logs. */
|
|
21
|
-
function capFailures(result) {
|
|
22
|
-
return {
|
|
23
|
-
captures: result.captures,
|
|
24
|
-
failures: result.failures.map((failure) => ({
|
|
25
|
-
...failure,
|
|
26
|
-
error: failure.error.length > FAILURE_ERROR_MAX ? failure.error.slice(0, FAILURE_ERROR_MAX) : failure.error
|
|
27
|
-
}))
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
/** Render with a timeout; cancels the runner when the budget expires. */
|
|
31
|
-
async function renderWithTimeout(runner, request, timeoutMs) {
|
|
32
|
-
let timedOut = false;
|
|
33
|
-
let timer;
|
|
34
|
-
const timeout = new Promise((_, reject) => {
|
|
35
|
-
timer = setTimeout(() => {
|
|
36
|
-
timedOut = true;
|
|
37
|
-
reject(/* @__PURE__ */ new Error(`Render timed out after ${timeoutMs}ms`));
|
|
38
|
-
}, timeoutMs);
|
|
39
|
-
});
|
|
40
|
-
timer?.unref?.();
|
|
41
|
-
try {
|
|
42
|
-
return capFailures(await Promise.race([runner.render(request), timeout]));
|
|
43
|
-
} catch (error) {
|
|
44
|
-
if (timedOut) await runner.cancel(request.buildId).catch(() => {});
|
|
45
|
-
throw error;
|
|
46
|
-
} finally {
|
|
47
|
-
clearTimeout(timer);
|
|
48
|
-
}
|
|
28
|
+
/** Parser over the configured design providers, or undefined when there are none. */
|
|
29
|
+
function designParser(providers) {
|
|
30
|
+
if (!providers || providers.length === 0) return void 0;
|
|
31
|
+
return (raw) => parseDesignUrl(providers, raw)?.ref ?? null;
|
|
49
32
|
}
|
|
50
33
|
/**
|
|
51
34
|
* Run the full capture for a build: extract, render, persist, and finalize.
|
|
@@ -58,7 +41,7 @@ async function renderWithTimeout(runner, request, timeoutMs) {
|
|
|
58
41
|
* @returns Story and blocking-failure counts for the attempt row.
|
|
59
42
|
*/
|
|
60
43
|
async function executeCaptureJob(input, options) {
|
|
61
|
-
const builds = new BuildModel(options.db, options.tables);
|
|
44
|
+
const builds = new BuildModel(options.db, options.tables, options.logger);
|
|
62
45
|
const { build, project } = await loadTarget(options, input.buildId);
|
|
63
46
|
return await withSpan("capture.job", async () => {
|
|
64
47
|
const { result, timings } = await runWithTimings(async () => await runCapturePhases(input, options, builds, build, project));
|
|
@@ -80,13 +63,27 @@ async function runCapturePhases(input, options, builds, build, project) {
|
|
|
80
63
|
reqId: input.reqId,
|
|
81
64
|
...input.attempt ? { attemptNo: input.attempt.attemptNo } : {}
|
|
82
65
|
});
|
|
66
|
+
if (build.status === "cancelled") {
|
|
67
|
+
logger?.info("build is cancelled; capture skipped");
|
|
68
|
+
return {
|
|
69
|
+
storyCount: 0,
|
|
70
|
+
failedCount: 0
|
|
71
|
+
};
|
|
72
|
+
}
|
|
83
73
|
await builds.setStatus(build.id, "capturing");
|
|
84
74
|
const startTime = performance.now();
|
|
85
75
|
let extractedDir;
|
|
76
|
+
let scratchPending = true;
|
|
77
|
+
const cleanScratch = async () => {
|
|
78
|
+
if (scratchPending) {
|
|
79
|
+
scratchPending = false;
|
|
80
|
+
await removeJobScratch(options.scratchDir, project.id, build.id, logger);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
86
83
|
try {
|
|
87
84
|
const extractStart = performance.now();
|
|
88
85
|
extractedDir = await timed("capture.extract", async () => await withSpan("capture.extract", async () => {
|
|
89
|
-
return await extractStorybookToScratch(options.storage, options.scratchDir, project.id, build.id);
|
|
86
|
+
return await extractStorybookToScratch(options.storage, options.scratchDir, project.id, build.id, void 0, options.limits);
|
|
90
87
|
}));
|
|
91
88
|
const extractDuration = performance.now() - extractStart;
|
|
92
89
|
logger?.info({ durationMs: Math.round(extractDuration) }, "storybook extracted");
|
|
@@ -94,69 +91,98 @@ async function runCapturePhases(input, options, builds, build, project) {
|
|
|
94
91
|
const storybookDir = extractedDir;
|
|
95
92
|
const staticsStart = performance.now();
|
|
96
93
|
await timed("capture.persist-statics", async () => await withSpan("capture.persist-statics", async () => {
|
|
97
|
-
await persistStorybookStatics(options.storage, storybookDir, project.id, build.id);
|
|
94
|
+
await persistStorybookStatics(options.storage, storybookDir, project.id, build.id, options.db);
|
|
98
95
|
}));
|
|
99
96
|
logger?.info({ durationMs: Math.round(performance.now() - staticsStart) }, "storybook statics persisted");
|
|
100
97
|
await emitAttemptLog(input.recordLog, logger, "info", "storybook statics persisted", { durationMs: Math.round(performance.now() - staticsStart) });
|
|
101
|
-
const
|
|
98
|
+
const discovered = await new StorybookAdapter().discover(extractedDir, { parseDesignUrl: designParser(options.design) });
|
|
99
|
+
logInvalidDesigns(logger, discovered);
|
|
102
100
|
const rawViewports = project.viewports;
|
|
103
101
|
const viewports = resolveViewports(rawViewports, DEFAULT_VIEWPORTS, options.viewports);
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
102
|
+
const browsers = resolveBrowsers(project);
|
|
103
|
+
assertSupportedBrowsers(browsers, offeredTargets(options.runner, {
|
|
104
|
+
publicOrigin: await options.publicOrigin?.(),
|
|
105
|
+
hasSecret: options.secret !== void 0
|
|
106
|
+
}));
|
|
107
|
+
const storybookUrl = storybookUrlSource(options, {
|
|
108
|
+
build,
|
|
109
|
+
project
|
|
110
|
+
});
|
|
111
|
+
const stories = (await enrichStories({
|
|
112
|
+
runner: options.runner,
|
|
113
|
+
buildId: build.id,
|
|
114
|
+
storybookDir,
|
|
115
|
+
stories: discovered.filter((s) => !isDisabledStory(s)),
|
|
116
|
+
target: browsers[0] ?? "chromium",
|
|
117
|
+
allowlist: parseModesAllowlist(project.modesAllowlist),
|
|
118
|
+
logger,
|
|
119
|
+
storybookUrl
|
|
120
|
+
})).filter((s) => !isDisabledStory(s));
|
|
121
|
+
assertModeCaps(stories, viewports);
|
|
122
|
+
const testRun = await runProjectTests({
|
|
123
|
+
runner: options.runner,
|
|
124
|
+
builds,
|
|
125
|
+
build,
|
|
126
|
+
project,
|
|
127
|
+
storybookDir,
|
|
128
|
+
stories: discovered,
|
|
129
|
+
target: browsers[0] ?? "chromium",
|
|
130
|
+
storybookUrl,
|
|
131
|
+
logger,
|
|
132
|
+
recordLog: input.recordLog
|
|
133
|
+
});
|
|
134
|
+
const fanned = await fanOutCapture({
|
|
135
|
+
options,
|
|
136
|
+
input,
|
|
137
|
+
build,
|
|
138
|
+
project,
|
|
111
139
|
stories,
|
|
112
140
|
viewports,
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
rendered: partition.render.length,
|
|
117
|
-
inherited: partition.inherited.length,
|
|
118
|
-
total: stories.length
|
|
119
|
-
}, "affected capture partitioned");
|
|
120
|
-
await emitAttemptLog(input.recordLog, logger, "info", "affected capture partitioned", {
|
|
121
|
-
rendered: partition.render.length,
|
|
122
|
-
inherited: partition.inherited.length,
|
|
123
|
-
total: stories.length
|
|
141
|
+
browsers,
|
|
142
|
+
logger,
|
|
143
|
+
testFailed: testRun.blocking
|
|
124
144
|
});
|
|
145
|
+
if (fanned) {
|
|
146
|
+
await cleanScratch();
|
|
147
|
+
return fanned;
|
|
148
|
+
}
|
|
125
149
|
const renderStart = performance.now();
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}, { "storyshelf.
|
|
150
|
+
const matrix = await timed("capture.render", async () => await withSpan("capture.render", async () => await captureAcrossBrowsers({
|
|
151
|
+
db: options.db,
|
|
152
|
+
storage: options.storage,
|
|
153
|
+
tables: options.tables,
|
|
154
|
+
runner: options.runner,
|
|
155
|
+
recordLog: input.recordLog,
|
|
156
|
+
progress: input.progress,
|
|
157
|
+
build,
|
|
158
|
+
project,
|
|
159
|
+
stories,
|
|
160
|
+
viewports,
|
|
161
|
+
storybookDir,
|
|
162
|
+
browsers,
|
|
163
|
+
affectedPaths: affectedPathsFor(build),
|
|
164
|
+
renderTimeoutMs: options.renderTimeoutMs,
|
|
165
|
+
storybookUrl,
|
|
166
|
+
logger
|
|
167
|
+
}), { "storyshelf.browsers": browsers.join(",") }));
|
|
168
|
+
await cleanScratch();
|
|
169
|
+
const result = {
|
|
170
|
+
captures: matrix.captures,
|
|
171
|
+
failures: matrix.failures
|
|
172
|
+
};
|
|
144
173
|
const renderDuration = performance.now() - renderStart;
|
|
145
174
|
logger?.info({
|
|
146
175
|
durationMs: Math.round(renderDuration),
|
|
147
|
-
storyCount: stories.length
|
|
176
|
+
storyCount: stories.length,
|
|
177
|
+
browsers
|
|
148
178
|
}, "stories rendered");
|
|
149
179
|
await emitAttemptLog(input.recordLog, logger, "info", "stories rendered", {
|
|
150
180
|
durationMs: Math.round(renderDuration),
|
|
151
|
-
storyCount: stories.length
|
|
181
|
+
storyCount: stories.length,
|
|
182
|
+
rendered: matrix.renderCount
|
|
152
183
|
});
|
|
153
|
-
const
|
|
154
|
-
const
|
|
155
|
-
const flakyFailed = /* @__PURE__ */ new Set();
|
|
156
|
-
const a11yFailed = /* @__PURE__ */ new Set();
|
|
157
|
-
for (const f of result.failures) if (f.error.startsWith("a11y:")) a11yFailed.add(f.storyId);
|
|
158
|
-
else if (flakyStoryIds.has(f.storyId)) flakyFailed.add(f.storyId);
|
|
159
|
-
else blockingFailed.add(f.storyId);
|
|
184
|
+
const { blocking: blockingFailed, flaky: flakyFailed, a11y: a11yFailed } = classifyFailures(stories, result.failures);
|
|
185
|
+
for (const id of testRun.blocking) blockingFailed.add(id);
|
|
160
186
|
const persistStart = performance.now();
|
|
161
187
|
await timed("capture.persist", async () => await withSpan("capture.persist", async () => {
|
|
162
188
|
await persistCapture({
|
|
@@ -167,9 +193,11 @@ async function runCapturePhases(input, options, builds, build, project) {
|
|
|
167
193
|
build,
|
|
168
194
|
viewports,
|
|
169
195
|
captures: result.captures,
|
|
170
|
-
inherited:
|
|
196
|
+
inherited: matrix.inherited,
|
|
197
|
+
browsers,
|
|
171
198
|
logger,
|
|
172
199
|
recordLog: input.recordLog,
|
|
200
|
+
progress: input.progress,
|
|
173
201
|
secret: options.secret
|
|
174
202
|
}, blockingFailed, flakyFailed, a11yFailed);
|
|
175
203
|
}));
|
|
@@ -184,6 +212,7 @@ async function runCapturePhases(input, options, builds, build, project) {
|
|
|
184
212
|
failedCount: blockingFailed.size
|
|
185
213
|
};
|
|
186
214
|
} catch (error) {
|
|
215
|
+
await cleanScratch();
|
|
187
216
|
const totalDuration = performance.now() - startTime;
|
|
188
217
|
logger?.error({
|
|
189
218
|
durationMs: Math.round(totalDuration),
|
|
@@ -196,25 +225,7 @@ async function runCapturePhases(input, options, builds, build, project) {
|
|
|
196
225
|
throw error;
|
|
197
226
|
}
|
|
198
227
|
}
|
|
199
|
-
/**
|
|
200
|
-
* Resolve the affected import paths for a build, or `null` for full capture
|
|
201
|
-
* (affected capture disabled, or no computation was posted).
|
|
202
|
-
*/
|
|
203
|
-
function affectedPathsFor(build) {
|
|
204
|
-
if (!build.affectedOnly) return null;
|
|
205
|
-
return BuildModel.affectedPaths(build);
|
|
206
|
-
}
|
|
207
|
-
async function loadTarget(options, buildId) {
|
|
208
|
-
const build = await new BuildModel(options.db, options.tables).get(buildId);
|
|
209
|
-
if (!build) throw new Error(`Build not found: ${buildId}`);
|
|
210
|
-
const project = await new ProjectModel(options.db, options.tables).get(build.projectId);
|
|
211
|
-
if (!project) throw new Error(`Project not found: ${build.projectId}`);
|
|
212
|
-
return {
|
|
213
|
-
build,
|
|
214
|
-
project
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
228
|
//#endregion
|
|
218
|
-
export {
|
|
229
|
+
export { executeCaptureJob };
|
|
219
230
|
|
|
220
231
|
//# sourceMappingURL=orchestrator.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator.mjs","names":[],"sources":["../../src/capture/orchestrator.ts"],"sourcesContent":["import type { Logger } from \"pino\";\nimport type { CaptureRunner, RenderResult } from \"../adapters/capture-runner.ts\";\nimport type { BrowserName } from \"../adapters/capture-runner.ts\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { StorageAdapter } from \"../adapters/storage.ts\";\nimport { BuildModel, type BuildTables } from \"../models/build.ts\";\nimport {\n emitAttemptLog,\n type AttemptLogRecorder,\n type CaptureAttemptTables,\n} from \"../models/capture-attempt.ts\";\nimport type { CaptureLogTables } from \"../models/capture-log.ts\";\nimport { ProjectModel, type ProjectTables } from \"../models/project.ts\";\nimport type { Build } from \"../schema/build.ts\";\nimport type { Project } from \"../schema/project.ts\";\nimport { parentContext, withSpan } from \"../tracing.ts\";\nimport type { SecretInput } from \"../utils/encrypt.ts\";\nimport { roundedTimings, runWithTimings, timed } from \"../utils/timing.ts\";\nimport { DEFAULT_VIEWPORTS, isDisabledStory, isFlakyStory } from \"./adapter.ts\";\nimport type { Viewport } from \"./adapter.ts\";\nimport { partitionAffectedStories } from \"./affected.ts\";\nimport { persistCapture, type PipelineTables } from \"./pipeline.ts\";\nimport { resolveViewports } from \"./sizing.ts\";\nimport { extractStorybookToScratch, persistStorybookStatics } from \"./statics.ts\";\nimport { StorybookAdapter } from \"./storybook.ts\";\n\n/** Table handles required by the orchestrator. */\nexport type OrchestratorTables = BuildTables &\n ProjectTables &\n PipelineTables &\n CaptureAttemptTables &\n CaptureLogTables;\n\n/** Inputs for running a capture job against a Storybook build. */\nexport interface CaptureJobOptions {\n db: DatabaseAdapter;\n tables: OrchestratorTables;\n storage: StorageAdapter;\n runner: CaptureRunner;\n scratchDir: string;\n viewports?: Viewport[];\n logger?: Logger;\n /** Server secret for decrypting webhook secrets at send time. */\n secret?: SecretInput;\n /** Render budget override (tests use a short fuse). */\n renderTimeoutMs?: number | undefined;\n}\n\n/** Default budget for one render call before the orchestrator cancels it. */\nexport const RENDER_TIMEOUT_MS = 15 * 60_000;\n\n/** Failure error text cap (persisted rows and logs stay bounded). */\nconst FAILURE_ERROR_MAX = 500;\n\n/** Render input assembled by the orchestrator for one build. */\ninterface RenderRequest {\n buildId: string;\n storybookDir: string;\n stories: Parameters<CaptureRunner[\"render\"]>[0][\"stories\"];\n viewports: Parameters<CaptureRunner[\"render\"]>[0][\"viewports\"];\n logger: Logger | undefined;\n executePlay: boolean;\n playTimeoutMs: number;\n runA11y: boolean;\n browser: BrowserName;\n}\n\n/** Cap failure error text so one chatty runner cannot flood rows and logs. */\nfunction capFailures(result: RenderResult): RenderResult {\n return {\n captures: result.captures,\n failures: result.failures.map((failure) => ({\n ...failure,\n error:\n failure.error.length > FAILURE_ERROR_MAX\n ? failure.error.slice(0, FAILURE_ERROR_MAX)\n : failure.error,\n })),\n };\n}\n\n/** Render with a timeout; cancels the runner when the budget expires. */\nasync function renderWithTimeout(\n runner: CaptureRunner,\n request: RenderRequest,\n timeoutMs: number,\n): Promise<RenderResult> {\n let timedOut = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n timedOut = true;\n reject(new Error(`Render timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n });\n timer?.unref?.();\n try {\n return capFailures(await Promise.race([runner.render(request), timeout]));\n } catch (error) {\n if (timedOut) {\n await runner.cancel(request.buildId).catch(() => {});\n }\n throw error;\n } finally {\n clearTimeout(timer);\n }\n}\n\n/** Pointer to the attempt row owning this run's log history. */\nexport interface CaptureAttemptRef {\n id: string;\n attemptNo: number;\n projectId: string;\n}\n\n/** Inputs for running a capture job against a Storybook build. */\nexport interface CaptureJobInput {\n buildId: string;\n reqId?: string;\n /**\n * W3C `traceparent` from the enqueueing request or queue payload.\n * Continues the inbound trace instead of starting a root span.\n */\n traceparent?: string;\n /** Attempt row owning this run; when omitted no per-attempt logs are stored. */\n attempt?: CaptureAttemptRef;\n /** Recorder mirroring phase logs into the attempt's log history. */\n recordLog?: AttemptLogRecorder;\n}\n/**\n * Run the full capture for a build: extract, render, persist, and finalize.\n *\n * The run executes inside a `capture.job` span (continuing `input.traceparent`\n * when present) with one child span per pipeline phase.\n *\n * @param input - Build id plus the originating request id.\n * @param options - Adapters, scratch dir, viewports, and logger.\n * @returns Story and blocking-failure counts for the attempt row.\n */\nexport async function executeCaptureJob(\n input: CaptureJobInput,\n options: CaptureJobOptions,\n): Promise<{ storyCount: number; failedCount: number }> {\n const builds = new BuildModel(options.db, options.tables);\n const { build, project } = await loadTarget(options, input.buildId);\n return await withSpan(\n \"capture.job\",\n async () => {\n const { result, timings } = await runWithTimings(\n async () => await runCapturePhases(input, options, builds, build, project),\n );\n options.logger\n ?.child({ buildId: build.id, ...(input.reqId ? { reqId: input.reqId } : {}) })\n ?.info({ timings: roundedTimings(timings) }, \"capture timings\");\n return result;\n },\n {\n \"storyshelf.build_id\": build.id,\n ...(input.reqId ? { \"storyshelf.req_id\": input.reqId } : {}),\n ...(input.attempt ? { \"storyshelf.attempt_no\": input.attempt.attemptNo } : {}),\n },\n parentContext(input.traceparent),\n );\n}\n\n/** Execute the phase pipeline inside the `capture.job` span. */\nasync function runCapturePhases(\n input: CaptureJobInput,\n options: CaptureJobOptions,\n builds: BuildModel,\n build: Build,\n project: Project,\n): Promise<{ storyCount: number; failedCount: number }> {\n const logger = options.logger?.child({\n buildId: input.buildId,\n reqId: input.reqId,\n ...(input.attempt ? { attemptNo: input.attempt.attemptNo } : {}),\n });\n await builds.setStatus(build.id, \"capturing\");\n\n const startTime = performance.now();\n let extractedDir: string | undefined;\n try {\n const extractStart = performance.now();\n extractedDir = await timed(\n \"capture.extract\",\n async () =>\n await withSpan(\"capture.extract\", async () => {\n return await extractStorybookToScratch(\n options.storage,\n options.scratchDir,\n project.id,\n build.id,\n );\n }),\n );\n const extractDuration = performance.now() - extractStart;\n logger?.info({ durationMs: Math.round(extractDuration) }, \"storybook extracted\");\n await emitAttemptLog(input.recordLog, logger, \"info\", \"storybook extracted\", {\n durationMs: Math.round(extractDuration),\n });\n\n // Persist the extracted statics to storage so the published Storybook\n // (`storybookDir`) can be served after the scratch dir is cleaned up.\n // Copy to a const: closures below need the narrowed string type.\n const storybookDir: string = extractedDir;\n const staticsStart = performance.now();\n await timed(\n \"capture.persist-statics\",\n async () =>\n await withSpan(\"capture.persist-statics\", async () => {\n await persistStorybookStatics(options.storage, storybookDir, project.id, build.id);\n }),\n );\n logger?.info(\n { durationMs: Math.round(performance.now() - staticsStart) },\n \"storybook statics persisted\",\n );\n await emitAttemptLog(input.recordLog, logger, \"info\", \"storybook statics persisted\", {\n durationMs: Math.round(performance.now() - staticsStart),\n });\n\n const adapter = new StorybookAdapter();\n const discovered = await adapter.discover(extractedDir);\n const stories = discovered.filter((s) => !isDisabledStory(s));\n // Resolve viewports via single helper (project JSON → server override → default)\n const rawViewports = (project as unknown as { viewports?: string | null }).viewports;\n const viewports = resolveViewports(rawViewports, DEFAULT_VIEWPORTS, options.viewports);\n const browser = (project as unknown as { browser?: string }).browser as BrowserName | undefined;\n\n const partition = await partitionAffectedStories({\n db: options.db,\n tables: options.tables,\n projectId: project.id,\n branch: build.gitBranch,\n defaultBranch: project.gitDefaultBranch,\n stories,\n viewports,\n affectedPaths: affectedPathsFor(build),\n });\n logger?.info(\n {\n rendered: partition.render.length,\n inherited: partition.inherited.length,\n total: stories.length,\n },\n \"affected capture partitioned\",\n );\n await emitAttemptLog(input.recordLog, logger, \"info\", \"affected capture partitioned\", {\n rendered: partition.render.length,\n inherited: partition.inherited.length,\n total: stories.length,\n });\n\n const renderStart = performance.now();\n const renderTimeoutMs = options.renderTimeoutMs ?? RENDER_TIMEOUT_MS;\n const result = await timed(\n \"capture.render\",\n async () =>\n await withSpan(\n \"capture.render\",\n async () => {\n if (partition.render.length === 0) {\n return { captures: [], failures: [] };\n }\n return await renderWithTimeout(\n options.runner,\n {\n buildId: build.id,\n storybookDir,\n stories: partition.render,\n viewports,\n logger,\n executePlay: project.executePlay ?? false,\n playTimeoutMs: project.playTimeoutMs ?? 10_000,\n runA11y: project.runA11y ?? false,\n browser: browser ?? \"chromium\",\n },\n renderTimeoutMs,\n );\n },\n { \"storyshelf.render_count\": partition.render.length },\n ),\n );\n const renderDuration = performance.now() - renderStart;\n logger?.info(\n { durationMs: Math.round(renderDuration), storyCount: stories.length },\n \"stories rendered\",\n );\n await emitAttemptLog(input.recordLog, logger, \"info\", \"stories rendered\", {\n durationMs: Math.round(renderDuration),\n storyCount: stories.length,\n });\n\n const flakyStoryIds = new Set(stories.filter((s) => isFlakyStory(s)).map((s) => s.id));\n const blockingFailed = new Set<string>();\n const flakyFailed = new Set<string>();\n const a11yFailed = new Set<string>();\n for (const f of result.failures) {\n if (f.error.startsWith(\"a11y:\")) a11yFailed.add(f.storyId);\n else if (flakyStoryIds.has(f.storyId)) flakyFailed.add(f.storyId);\n else blockingFailed.add(f.storyId);\n }\n\n const persistStart = performance.now();\n await timed(\n \"capture.persist\",\n async () =>\n await withSpan(\"capture.persist\", async () => {\n await persistCapture(\n {\n db: options.db,\n tables: options.tables,\n storage: options.storage,\n project,\n build,\n viewports,\n captures: result.captures,\n inherited: partition.inherited,\n logger,\n recordLog: input.recordLog,\n secret: options.secret,\n },\n blockingFailed,\n flakyFailed,\n a11yFailed,\n );\n }),\n );\n const persistDuration = performance.now() - persistStart;\n logger?.info({ durationMs: Math.round(persistDuration) }, \"capture persisted\");\n await emitAttemptLog(input.recordLog, logger, \"info\", \"capture persisted\", {\n durationMs: Math.round(persistDuration),\n });\n\n const totalDuration = performance.now() - startTime;\n logger?.info({ durationMs: Math.round(totalDuration) }, \"capture completed\");\n await emitAttemptLog(input.recordLog, logger, \"info\", \"capture completed\", {\n durationMs: Math.round(totalDuration),\n });\n return { storyCount: stories.length, failedCount: blockingFailed.size };\n } catch (error) {\n const totalDuration = performance.now() - startTime;\n logger?.error({ durationMs: Math.round(totalDuration), err: error }, \"capture failed\");\n await emitAttemptLog(input.recordLog, logger, \"error\", \"capture failed\", {\n durationMs: Math.round(totalDuration),\n });\n await builds.setStatus(build.id, \"failed\").catch((markError: unknown) => {\n logger?.error({ err: markError }, \"failed to mark build failed after capture error\");\n });\n throw error;\n }\n}\n\n/**\n * Resolve the affected import paths for a build, or `null` for full capture\n * (affected capture disabled, or no computation was posted).\n */\nfunction affectedPathsFor(build: Build): string[] | null {\n if (!build.affectedOnly) {\n return null;\n }\n return BuildModel.affectedPaths(build);\n}\n\nasync function loadTarget(\n options: CaptureJobOptions,\n buildId: string,\n): Promise<{ build: Build; project: Project }> {\n const build = await new BuildModel(options.db, options.tables).get(buildId);\n if (!build) {\n throw new Error(`Build not found: ${buildId}`);\n }\n const project = await new ProjectModel(options.db, options.tables).get(build.projectId);\n if (!project) {\n throw new Error(`Project not found: ${build.projectId}`);\n }\n return { build, project };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAiDA,MAAa,oBAAoB;;AAGjC,MAAM,oBAAoB;;AAgB1B,SAAS,YAAY,QAAoC;CACvD,OAAO;EACL,UAAU,OAAO;EACjB,UAAU,OAAO,SAAS,KAAK,aAAa;GAC1C,GAAG;GACH,OACE,QAAQ,MAAM,SAAS,oBACnB,QAAQ,MAAM,MAAM,GAAG,iBAAiB,IACxC,QAAQ;EAChB,EAAE;CACJ;AACF;;AAGA,eAAe,kBACb,QACA,SACA,WACuB;CACvB,IAAI,WAAW;CACf,IAAI;CACJ,MAAM,UAAU,IAAI,SAAgB,GAAG,WAAW;EAChD,QAAQ,iBAAiB;GACvB,WAAW;GACX,uBAAO,IAAI,MAAM,0BAA0B,UAAU,GAAG,CAAC;EAC3D,GAAG,SAAS;CACd,CAAC;CACD,OAAO,QAAQ;CACf,IAAI;EACF,OAAO,YAAY,MAAM,QAAQ,KAAK,CAAC,OAAO,OAAO,OAAO,GAAG,OAAO,CAAC,CAAC;CAC1E,SAAS,OAAO;EACd,IAAI,UACF,MAAM,OAAO,OAAO,QAAQ,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC;EAErD,MAAM;CACR,UAAU;EACR,aAAa,KAAK;CACpB;AACF;;;;;;;;;;;AAiCA,eAAsB,kBACpB,OACA,SACsD;CACtD,MAAM,SAAS,IAAI,WAAW,QAAQ,IAAI,QAAQ,MAAM;CACxD,MAAM,EAAE,OAAO,YAAY,MAAM,WAAW,SAAS,MAAM,OAAO;CAClE,OAAO,MAAM,SACX,eACA,YAAY;EACV,MAAM,EAAE,QAAQ,YAAY,MAAM,eAChC,YAAY,MAAM,iBAAiB,OAAO,SAAS,QAAQ,OAAO,OAAO,CAC3E;EACA,QAAQ,QACJ,MAAM;GAAE,SAAS,MAAM;GAAI,GAAI,MAAM,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;EAAG,CAAC,CAAC,EAC5E,KAAK,EAAE,SAAS,eAAe,OAAO,EAAE,GAAG,iBAAiB;EAChE,OAAO;CACT,GACA;EACE,uBAAuB,MAAM;EAC7B,GAAI,MAAM,QAAQ,EAAE,qBAAqB,MAAM,MAAM,IAAI,CAAC;EAC1D,GAAI,MAAM,UAAU,EAAE,yBAAyB,MAAM,QAAQ,UAAU,IAAI,CAAC;CAC9E,GACA,cAAc,MAAM,WAAW,CACjC;AACF;;AAGA,eAAe,iBACb,OACA,SACA,QACA,OACA,SACsD;CACtD,MAAM,SAAS,QAAQ,QAAQ,MAAM;EACnC,SAAS,MAAM;EACf,OAAO,MAAM;EACb,GAAI,MAAM,UAAU,EAAE,WAAW,MAAM,QAAQ,UAAU,IAAI,CAAC;CAChE,CAAC;CACD,MAAM,OAAO,UAAU,MAAM,IAAI,WAAW;CAE5C,MAAM,YAAY,YAAY,IAAI;CAClC,IAAI;CACJ,IAAI;EACF,MAAM,eAAe,YAAY,IAAI;EACrC,eAAe,MAAM,MACnB,mBACA,YACE,MAAM,SAAS,mBAAmB,YAAY;GAC5C,OAAO,MAAM,0BACX,QAAQ,SACR,QAAQ,YACR,QAAQ,IACR,MAAM,EACR;EACF,CAAC,CACL;EACA,MAAM,kBAAkB,YAAY,IAAI,IAAI;EAC5C,QAAQ,KAAK,EAAE,YAAY,KAAK,MAAM,eAAe,EAAE,GAAG,qBAAqB;EAC/E,MAAM,eAAe,MAAM,WAAW,QAAQ,QAAQ,uBAAuB,EAC3E,YAAY,KAAK,MAAM,eAAe,EACxC,CAAC;EAKD,MAAM,eAAuB;EAC7B,MAAM,eAAe,YAAY,IAAI;EACrC,MAAM,MACJ,2BACA,YACE,MAAM,SAAS,2BAA2B,YAAY;GACpD,MAAM,wBAAwB,QAAQ,SAAS,cAAc,QAAQ,IAAI,MAAM,EAAE;EACnF,CAAC,CACL;EACA,QAAQ,KACN,EAAE,YAAY,KAAK,MAAM,YAAY,IAAI,IAAI,YAAY,EAAE,GAC3D,6BACF;EACA,MAAM,eAAe,MAAM,WAAW,QAAQ,QAAQ,+BAA+B,EACnF,YAAY,KAAK,MAAM,YAAY,IAAI,IAAI,YAAY,EACzD,CAAC;EAID,MAAM,WAAU,MADS,IADL,iBACW,CAAC,CAAC,SAAS,YAAY,EAAA,CAC3B,QAAQ,MAAM,CAAC,gBAAgB,CAAC,CAAC;EAE5D,MAAM,eAAgB,QAAqD;EAC3E,MAAM,YAAY,iBAAiB,cAAc,mBAAmB,QAAQ,SAAS;EACrF,MAAM,UAAW,QAA4C;EAE7D,MAAM,YAAY,MAAM,yBAAyB;GAC/C,IAAI,QAAQ;GACZ,QAAQ,QAAQ;GAChB,WAAW,QAAQ;GACnB,QAAQ,MAAM;GACd,eAAe,QAAQ;GACvB;GACA;GACA,eAAe,iBAAiB,KAAK;EACvC,CAAC;EACD,QAAQ,KACN;GACE,UAAU,UAAU,OAAO;GAC3B,WAAW,UAAU,UAAU;GAC/B,OAAO,QAAQ;EACjB,GACA,8BACF;EACA,MAAM,eAAe,MAAM,WAAW,QAAQ,QAAQ,gCAAgC;GACpF,UAAU,UAAU,OAAO;GAC3B,WAAW,UAAU,UAAU;GAC/B,OAAO,QAAQ;EACjB,CAAC;EAED,MAAM,cAAc,YAAY,IAAI;EACpC,MAAM,kBAAkB,QAAQ,mBAAA;EAChC,MAAM,SAAS,MAAM,MACnB,kBACA,YACE,MAAM,SACJ,kBACA,YAAY;GACV,IAAI,UAAU,OAAO,WAAW,GAC9B,OAAO;IAAE,UAAU,CAAC;IAAG,UAAU,CAAC;GAAE;GAEtC,OAAO,MAAM,kBACX,QAAQ,QACR;IACE,SAAS,MAAM;IACf;IACA,SAAS,UAAU;IACnB;IACA;IACA,aAAa,QAAQ,eAAe;IACpC,eAAe,QAAQ,iBAAiB;IACxC,SAAS,QAAQ,WAAW;IAC5B,SAAS,WAAW;GACtB,GACA,eACF;EACF,GACA,EAAE,2BAA2B,UAAU,OAAO,OAAO,CACvD,CACJ;EACA,MAAM,iBAAiB,YAAY,IAAI,IAAI;EAC3C,QAAQ,KACN;GAAE,YAAY,KAAK,MAAM,cAAc;GAAG,YAAY,QAAQ;EAAO,GACrE,kBACF;EACA,MAAM,eAAe,MAAM,WAAW,QAAQ,QAAQ,oBAAoB;GACxE,YAAY,KAAK,MAAM,cAAc;GACrC,YAAY,QAAQ;EACtB,CAAC;EAED,MAAM,gBAAgB,IAAI,IAAI,QAAQ,QAAQ,MAAM,aAAa,CAAC,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,EAAE,CAAC;EACrF,MAAM,iCAAiB,IAAI,IAAY;EACvC,MAAM,8BAAc,IAAI,IAAY;EACpC,MAAM,6BAAa,IAAI,IAAY;EACnC,KAAK,MAAM,KAAK,OAAO,UACrB,IAAI,EAAE,MAAM,WAAW,OAAO,GAAG,WAAW,IAAI,EAAE,OAAO;OACpD,IAAI,cAAc,IAAI,EAAE,OAAO,GAAG,YAAY,IAAI,EAAE,OAAO;OAC3D,eAAe,IAAI,EAAE,OAAO;EAGnC,MAAM,eAAe,YAAY,IAAI;EACrC,MAAM,MACJ,mBACA,YACE,MAAM,SAAS,mBAAmB,YAAY;GAC5C,MAAM,eACJ;IACE,IAAI,QAAQ;IACZ,QAAQ,QAAQ;IAChB,SAAS,QAAQ;IACjB;IACA;IACA;IACA,UAAU,OAAO;IACjB,WAAW,UAAU;IACrB;IACA,WAAW,MAAM;IACjB,QAAQ,QAAQ;GAClB,GACA,gBACA,aACA,UACF;EACF,CAAC,CACL;EACA,MAAM,kBAAkB,YAAY,IAAI,IAAI;EAC5C,QAAQ,KAAK,EAAE,YAAY,KAAK,MAAM,eAAe,EAAE,GAAG,mBAAmB;EAC7E,MAAM,eAAe,MAAM,WAAW,QAAQ,QAAQ,qBAAqB,EACzE,YAAY,KAAK,MAAM,eAAe,EACxC,CAAC;EAED,MAAM,gBAAgB,YAAY,IAAI,IAAI;EAC1C,QAAQ,KAAK,EAAE,YAAY,KAAK,MAAM,aAAa,EAAE,GAAG,mBAAmB;EAC3E,MAAM,eAAe,MAAM,WAAW,QAAQ,QAAQ,qBAAqB,EACzE,YAAY,KAAK,MAAM,aAAa,EACtC,CAAC;EACD,OAAO;GAAE,YAAY,QAAQ;GAAQ,aAAa,eAAe;EAAK;CACxE,SAAS,OAAO;EACd,MAAM,gBAAgB,YAAY,IAAI,IAAI;EAC1C,QAAQ,MAAM;GAAE,YAAY,KAAK,MAAM,aAAa;GAAG,KAAK;EAAM,GAAG,gBAAgB;EACrF,MAAM,eAAe,MAAM,WAAW,QAAQ,SAAS,kBAAkB,EACvE,YAAY,KAAK,MAAM,aAAa,EACtC,CAAC;EACD,MAAM,OAAO,UAAU,MAAM,IAAI,QAAQ,CAAC,CAAC,OAAO,cAAuB;GACvE,QAAQ,MAAM,EAAE,KAAK,UAAU,GAAG,iDAAiD;EACrF,CAAC;EACD,MAAM;CACR;AACF;;;;;AAMA,SAAS,iBAAiB,OAA+B;CACvD,IAAI,CAAC,MAAM,cACT,OAAO;CAET,OAAO,WAAW,cAAc,KAAK;AACvC;AAEA,eAAe,WACb,SACA,SAC6C;CAC7C,MAAM,QAAQ,MAAM,IAAI,WAAW,QAAQ,IAAI,QAAQ,MAAM,CAAC,CAAC,IAAI,OAAO;CAC1E,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,oBAAoB,SAAS;CAE/C,MAAM,UAAU,MAAM,IAAI,aAAa,QAAQ,IAAI,QAAQ,MAAM,CAAC,CAAC,IAAI,MAAM,SAAS;CACtF,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,sBAAsB,MAAM,WAAW;CAEzD,OAAO;EAAE;EAAO;CAAQ;AAC1B"}
|
|
1
|
+
{"version":3,"file":"orchestrator.mjs","names":[],"sources":["../../src/capture/orchestrator.ts"],"sourcesContent":["import type { Logger } from \"pino\";\nimport type { CaptureRunner } from \"../adapters/capture-runner.ts\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport { parseDesignUrl, type DesignProvider } from \"../adapters/design.ts\";\nimport type { StorageAdapter } from \"../adapters/storage.ts\";\nimport { BuildModel, type BuildTables } from \"../models/build.ts\";\nimport {\n emitAttemptLog,\n type AttemptLogRecorder,\n type CaptureAttemptTables,\n} from \"../models/capture-attempt.ts\";\nimport type { CaptureLogTables } from \"../models/capture-log.ts\";\nimport type { ProjectTables } from \"../models/project.ts\";\nimport type { Build } from \"../schema/build.ts\";\nimport type { Project } from \"../schema/project.ts\";\nimport { parentContext, withSpan } from \"../tracing.ts\";\nimport type { SecretInput } from \"../utils/encrypt.ts\";\nimport { roundedTimings, runWithTimings, timed } from \"../utils/timing.ts\";\nimport { DEFAULT_VIEWPORTS, isDisabledStory } from \"./adapter.ts\";\nimport type { DiscoverOptions, Viewport } from \"./adapter.ts\";\nimport { affectedPathsFor } from \"./affected.ts\";\nimport { assertSupportedBrowsers, resolveBrowsers } from \"./browsers.ts\";\nimport { logInvalidDesigns } from \"./design.ts\";\nimport { assertModeCaps, enrichStories } from \"./enrich.ts\";\nimport { classifyFailures } from \"./failure-sets.ts\";\nimport { captureAcrossBrowsers } from \"./matrix.ts\";\nimport { parseModesAllowlist } from \"./modes.ts\";\nimport { persistCapture, type PipelineTables } from \"./pipeline.ts\";\nimport type { CaptureProgress } from \"./progress.ts\";\nimport type { ExtractionLimits } from \"./publish/limits.ts\";\nimport { removeJobScratch } from \"./scratch.ts\";\nimport { fanOutCapture } from \"./shards/fan-out.ts\";\nimport { resolveViewports } from \"./sizing.ts\";\nimport { extractStorybookToScratch, persistStorybookStatics } from \"./statics.ts\";\nimport { storybookUrlSource } from \"./storybook-urls.ts\";\nimport { StorybookAdapter } from \"./storybook.ts\";\nimport { loadTarget } from \"./target.ts\";\nimport { offeredTargets } from \"./targets.ts\";\nimport { runProjectTests } from \"./tests.ts\";\n\n/** Table handles required by the orchestrator. */\nexport type OrchestratorTables = BuildTables &\n ProjectTables &\n PipelineTables &\n CaptureAttemptTables &\n CaptureLogTables;\n\n/** Inputs for running a capture job against a Storybook build. */\nexport interface CaptureJobOptions {\n db: DatabaseAdapter;\n tables: OrchestratorTables;\n storage: StorageAdapter;\n runner: CaptureRunner;\n scratchDir: string;\n /** Entry and size caps of the zip extraction (`maxZipEntries`, `maxUnzippedBytes`); built-in defaults when unset. */\n limits?: ExtractionLimits | undefined;\n viewports?: Viewport[];\n logger?: Logger;\n /** Server secret for decrypting webhook secrets at send time. */\n secret?: SecretInput;\n /** Design providers validating declared design links; none ⇒ links are not extracted. */\n design?: DesignProvider[];\n /** The resolved public origin (ADR 0007, decision 6); targets that need a URL wait for it. */\n publicOrigin?: (() => Promise<string | undefined>) | undefined;\n /** Render budget override (tests use a short fuse). */\n renderTimeoutMs?: number | undefined;\n /**\n * Target captures (story x viewport x engine) per shard. Unset turns sharding off; a build of up\n * to twice this size still runs as one shard, in this process, exactly as without sharding.\n */\n shardSize?: number | undefined;\n /**\n * Puts a job on the capture queue. Sharding needs it to hand the shards out; without it every\n * build runs in one piece.\n */\n enqueue?: ((job: ShardJob) => Promise<void>) | undefined;\n}\n\n/** A shard job: the build and the `capture_shards` row to render. */\nexport interface ShardJob {\n buildId: string;\n shard: string;\n reqId?: string | undefined;\n traceparent?: string | undefined;\n}\n\n/** Parser over the configured design providers, or undefined when there are none. */\nfunction designParser(providers: DesignProvider[] | undefined): DiscoverOptions[\"parseDesignUrl\"] {\n if (!providers || providers.length === 0) return undefined;\n return (raw) => parseDesignUrl(providers, raw)?.ref ?? null;\n}\n\n/** What a capture job reports: counts for the attempt row, and how many shards it handed out. */\nexport interface CaptureSummary {\n storyCount: number;\n failedCount: number;\n /** Set when the build was split: the shards are still to run and this job did not finish it. */\n shards?: number;\n}\n\n/** Pointer to the attempt row owning this run's log history. */\nexport interface CaptureAttemptRef {\n id: string;\n attemptNo: number;\n projectId: string;\n}\n\n/** Inputs for running a capture job against a Storybook build. */\nexport interface CaptureJobInput {\n buildId: string;\n reqId?: string;\n /**\n * W3C `traceparent` from the enqueueing request or queue payload.\n * Continues the inbound trace instead of starting a root span.\n */\n traceparent?: string;\n /** Attempt row owning this run; when omitted no per-attempt logs are stored. */\n attempt?: CaptureAttemptRef;\n /** Recorder mirroring phase logs into the attempt's log history. */\n recordLog?: AttemptLogRecorder;\n /** Reports render and persist progress to the job's lease, apart from logging. */\n progress?: CaptureProgress | undefined;\n}\n/**\n * Run the full capture for a build: extract, render, persist, and finalize.\n *\n * The run executes inside a `capture.job` span (continuing `input.traceparent`\n * when present) with one child span per pipeline phase.\n *\n * @param input - Build id plus the originating request id.\n * @param options - Adapters, scratch dir, viewports, and logger.\n * @returns Story and blocking-failure counts for the attempt row.\n */\nexport async function executeCaptureJob(\n input: CaptureJobInput,\n options: CaptureJobOptions,\n): Promise<CaptureSummary> {\n const builds = new BuildModel(options.db, options.tables, options.logger);\n const { build, project } = await loadTarget(options, input.buildId);\n return await withSpan(\n \"capture.job\",\n async () => {\n const { result, timings } = await runWithTimings(\n async () => await runCapturePhases(input, options, builds, build, project),\n );\n options.logger\n ?.child({ buildId: build.id, ...(input.reqId ? { reqId: input.reqId } : {}) })\n ?.info({ timings: roundedTimings(timings) }, \"capture timings\");\n return result;\n },\n {\n \"storyshelf.build_id\": build.id,\n ...(input.reqId ? { \"storyshelf.req_id\": input.reqId } : {}),\n ...(input.attempt ? { \"storyshelf.attempt_no\": input.attempt.attemptNo } : {}),\n },\n parentContext(input.traceparent),\n );\n}\n\n/** Execute the phase pipeline inside the `capture.job` span. */\nasync function runCapturePhases(\n input: CaptureJobInput,\n options: CaptureJobOptions,\n builds: BuildModel,\n build: Build,\n project: Project,\n): Promise<CaptureSummary> {\n const logger = options.logger?.child({\n buildId: input.buildId,\n reqId: input.reqId,\n ...(input.attempt ? { attemptNo: input.attempt.attemptNo } : {}),\n });\n if (build.status === \"cancelled\") {\n logger?.info(\"build is cancelled; capture skipped\");\n return { storyCount: 0, failedCount: 0 };\n }\n await builds.setStatus(build.id, \"capturing\");\n\n const startTime = performance.now();\n let extractedDir: string | undefined;\n // Statics are persisted to storage once extracted; the copy on disk is only scratch. Removed as\n // soon as rendering ends (before persist, so no I/O sits between the build's final status and the\n // status fan-out) or on any failure, whichever comes first.\n let scratchPending = true;\n const cleanScratch = async (): Promise<void> => {\n if (scratchPending) {\n scratchPending = false;\n await removeJobScratch(options.scratchDir, project.id, build.id, logger);\n }\n };\n try {\n const extractStart = performance.now();\n extractedDir = await timed(\n \"capture.extract\",\n async () =>\n await withSpan(\"capture.extract\", async () => {\n return await extractStorybookToScratch(\n options.storage,\n options.scratchDir,\n project.id,\n build.id,\n undefined,\n options.limits,\n );\n }),\n );\n const extractDuration = performance.now() - extractStart;\n logger?.info({ durationMs: Math.round(extractDuration) }, \"storybook extracted\");\n await emitAttemptLog(input.recordLog, logger, \"info\", \"storybook extracted\", {\n durationMs: Math.round(extractDuration),\n });\n\n // Persist the extracted statics to storage so the published Storybook\n // (`storybookDir`) can be served after the scratch dir is cleaned up.\n // Copy to a const: closures below need the narrowed string type.\n const storybookDir: string = extractedDir;\n const staticsStart = performance.now();\n await timed(\n \"capture.persist-statics\",\n async () =>\n await withSpan(\"capture.persist-statics\", async () => {\n await persistStorybookStatics(\n options.storage,\n storybookDir,\n project.id,\n build.id,\n options.db,\n );\n }),\n );\n logger?.info(\n { durationMs: Math.round(performance.now() - staticsStart) },\n \"storybook statics persisted\",\n );\n await emitAttemptLog(input.recordLog, logger, \"info\", \"storybook statics persisted\", {\n durationMs: Math.round(performance.now() - staticsStart),\n });\n\n const adapter = new StorybookAdapter();\n const discovered = await adapter.discover(extractedDir, {\n parseDesignUrl: designParser(options.design),\n });\n logInvalidDesigns(logger, discovered);\n // Resolve viewports via single helper (project JSON → server override → default)\n const rawViewports = (project as unknown as { viewports?: string | null }).viewports;\n const viewports = resolveViewports(rawViewports, DEFAULT_VIEWPORTS, options.viewports);\n const browsers = resolveBrowsers(project);\n assertSupportedBrowsers(\n browsers,\n offeredTargets(options.runner, {\n publicOrigin: await options.publicOrigin?.(),\n hasSecret: options.secret !== undefined,\n }),\n );\n const storybookUrl = storybookUrlSource(options, { build, project });\n const enriched = await enrichStories({\n runner: options.runner,\n buildId: build.id,\n storybookDir,\n stories: discovered.filter((s) => !isDisabledStory(s)),\n target: browsers[0] ?? \"chromium\",\n allowlist: parseModesAllowlist(project.modesAllowlist),\n logger,\n storybookUrl,\n });\n const stories = enriched.filter((s) => !isDisabledStory(s));\n assertModeCaps(stories, viewports);\n // Opt-in `.test()` runs: a failing test fails the build like a failing play function.\n const testRun = await runProjectTests({\n runner: options.runner,\n builds,\n build,\n project,\n storybookDir,\n stories: discovered,\n target: browsers[0] ?? \"chromium\",\n storybookUrl,\n logger,\n recordLog: input.recordLog,\n });\n\n // A big build is planned into shards that other workers render; this job's part ends there.\n const fanned = await fanOutCapture({\n options,\n input,\n build,\n project,\n stories,\n viewports,\n browsers,\n logger,\n testFailed: testRun.blocking,\n });\n if (fanned) {\n await cleanScratch();\n return fanned;\n }\n\n const renderStart = performance.now();\n const matrix = await timed(\n \"capture.render\",\n async () =>\n await withSpan(\n \"capture.render\",\n async () =>\n await captureAcrossBrowsers({\n db: options.db,\n storage: options.storage,\n tables: options.tables,\n runner: options.runner,\n recordLog: input.recordLog,\n progress: input.progress,\n build,\n project,\n stories,\n viewports,\n storybookDir,\n browsers,\n affectedPaths: affectedPathsFor(build),\n renderTimeoutMs: options.renderTimeoutMs,\n storybookUrl,\n logger,\n }),\n { \"storyshelf.browsers\": browsers.join(\",\") },\n ),\n );\n await cleanScratch();\n const result = { captures: matrix.captures, failures: matrix.failures };\n const renderDuration = performance.now() - renderStart;\n logger?.info(\n { durationMs: Math.round(renderDuration), storyCount: stories.length, browsers },\n \"stories rendered\",\n );\n await emitAttemptLog(input.recordLog, logger, \"info\", \"stories rendered\", {\n durationMs: Math.round(renderDuration),\n storyCount: stories.length,\n rendered: matrix.renderCount,\n });\n\n const {\n blocking: blockingFailed,\n flaky: flakyFailed,\n a11y: a11yFailed,\n } = classifyFailures(stories, result.failures);\n for (const id of testRun.blocking) blockingFailed.add(id);\n\n const persistStart = performance.now();\n await timed(\n \"capture.persist\",\n async () =>\n await withSpan(\"capture.persist\", async () => {\n await persistCapture(\n {\n db: options.db,\n tables: options.tables,\n storage: options.storage,\n project,\n build,\n viewports,\n captures: result.captures,\n inherited: matrix.inherited,\n browsers,\n logger,\n recordLog: input.recordLog,\n progress: input.progress,\n secret: options.secret,\n },\n blockingFailed,\n flakyFailed,\n a11yFailed,\n );\n }),\n );\n const persistDuration = performance.now() - persistStart;\n logger?.info({ durationMs: Math.round(persistDuration) }, \"capture persisted\");\n await emitAttemptLog(input.recordLog, logger, \"info\", \"capture persisted\", {\n durationMs: Math.round(persistDuration),\n });\n\n const totalDuration = performance.now() - startTime;\n logger?.info({ durationMs: Math.round(totalDuration) }, \"capture completed\");\n await emitAttemptLog(input.recordLog, logger, \"info\", \"capture completed\", {\n durationMs: Math.round(totalDuration),\n });\n return { storyCount: stories.length, failedCount: blockingFailed.size };\n } catch (error) {\n await cleanScratch();\n const totalDuration = performance.now() - startTime;\n logger?.error({ durationMs: Math.round(totalDuration), err: error }, \"capture failed\");\n await emitAttemptLog(input.recordLog, logger, \"error\", \"capture failed\", {\n durationMs: Math.round(totalDuration),\n });\n await builds.setStatus(build.id, \"failed\").catch((markError: unknown) => {\n logger?.error({ err: markError }, \"failed to mark build failed after capture error\");\n });\n throw error;\n }\n}\n"],"mappings":"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"}
|
|
@@ -6,12 +6,13 @@ import { DatabaseAdapter } from "../adapter/database.mjs";
|
|
|
6
6
|
import { StorageAdapter } from "../adapter/storage.mjs";
|
|
7
7
|
import { SecretInput } from "../utils/encrypt.mjs";
|
|
8
8
|
import { Build } from "../schema/build.mjs";
|
|
9
|
+
import { Project } from "../schema/project.mjs";
|
|
9
10
|
import { BuildTables } from "../models/build.mjs";
|
|
10
11
|
import { AttemptLogRecorder } from "../models/capture-attempt.mjs";
|
|
11
|
-
import { Project } from "../schema/project.mjs";
|
|
12
12
|
import { BaselineTables } from "../models/baseline.mjs";
|
|
13
13
|
import { SnapshotTables } from "../models/snapshot.mjs";
|
|
14
14
|
import { InheritedStory } from "./affected.mjs";
|
|
15
|
+
import { CaptureProgress } from "./progress.mjs";
|
|
15
16
|
import { Logger } from "pino";
|
|
16
17
|
//#region src/capture/pipeline.d.ts
|
|
17
18
|
/**
|
|
@@ -27,7 +28,7 @@ import { Logger } from "pino";
|
|
|
27
28
|
* @param flakyFailedStoryIds - Story ids that failed but are marked flaky
|
|
28
29
|
* (non-blocking). They are logged as warnings and do not block the build.
|
|
29
30
|
*/
|
|
30
|
-
declare function persistCapture(
|
|
31
|
+
declare function persistCapture(input: CaptureContext, renderFailedStoryIds?: ReadonlySet<string>, flakyFailedStoryIds?: ReadonlySet<string>, a11yFailedStoryIds?: ReadonlySet<string>): Promise<void>;
|
|
31
32
|
/** Table handles required by the capture pipeline. */
|
|
32
33
|
type PipelineTables = BuildTables & BaselineTables & SnapshotTables;
|
|
33
34
|
/** Persistence inputs for a completed capture run. */
|
|
@@ -44,6 +45,10 @@ interface CaptureContext {
|
|
|
44
45
|
build: Build;
|
|
45
46
|
/** Viewports at which stories were captured. */
|
|
46
47
|
viewports: Viewport[];
|
|
48
|
+
/** Engines captured this run, primary first (default: chromium only). */
|
|
49
|
+
browsers?: string[];
|
|
50
|
+
/** Secondary engines with no baselines yet; worked out by `persistCapture`, or fixed up front by a shard plan. */
|
|
51
|
+
freshBrowsers?: ReadonlySet<string>;
|
|
47
52
|
/** Screenshot buffers produced by the capture renderer. */
|
|
48
53
|
captures: RenderedSnapshot[];
|
|
49
54
|
/** Stories inheriting their baseline without rendering (affected capture). */
|
|
@@ -52,6 +57,8 @@ interface CaptureContext {
|
|
|
52
57
|
logger?: Logger;
|
|
53
58
|
/** Optional per-attempt log recorder (mirrors logger lines into the DB). */
|
|
54
59
|
recordLog?: AttemptLogRecorder;
|
|
60
|
+
/** Called per snapshot persisted, so the job's lease sees a long diff phase making progress. */
|
|
61
|
+
progress?: CaptureProgress | undefined;
|
|
55
62
|
/** Server secret for decrypting webhook secrets at send time. */
|
|
56
63
|
secret?: SecretInput;
|
|
57
64
|
}
|