@storyshelf/core 0.6.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -32
- package/dist/adapter/capture-queue.d.mts +44 -4
- package/dist/adapter/capture-runner.d.mts +134 -12
- package/dist/adapter/capture-runner.mjs +9 -12
- package/dist/adapter/database.d.mts +89 -10
- package/dist/adapter/database.mjs +4 -3
- package/dist/adapter/database.mjs.map +1 -1
- package/dist/adapter/design.d.mts +83 -0
- package/dist/adapter/design.mjs +27 -0
- package/dist/adapter/design.mjs.map +1 -0
- package/dist/adapter/email-sender.d.mts +0 -2
- package/dist/adapter/metadata.d.mts +3 -12
- package/dist/adapter/notifier.d.mts +6 -3
- package/dist/adapter/notifier.mjs +5 -1
- package/dist/adapter/oauth.d.mts +50 -0
- package/dist/adapter/setup.d.mts +4 -7
- package/dist/adapter/setup.mjs +3 -3
- package/dist/adapter/setup.mjs.map +1 -1
- package/dist/adapter/storage.d.mts +1 -3
- package/dist/adapter/webhook-events.d.mts +0 -2
- package/dist/adapter/webhook-events.mjs +13 -14
- package/dist/adapter/webhook-events.mjs.map +1 -1
- package/dist/adapters/capture-target-offers.d.mts +26 -0
- package/dist/adapters/capture-target-offers.mjs +57 -0
- package/dist/adapters/capture-target-offers.mjs.map +1 -0
- package/dist/adapters/capture-target.d.mts +69 -0
- package/dist/adapters/capture-target.mjs +75 -0
- package/dist/adapters/capture-target.mjs.map +1 -0
- package/dist/adapters/notifier/build-comment.d.mts +22 -0
- package/dist/adapters/notifier/build-comment.mjs +23 -0
- package/dist/adapters/notifier/build-comment.mjs.map +1 -0
- package/dist/adapters/notifier/build-state.d.mts +97 -0
- package/dist/adapters/notifier/build-state.mjs +217 -0
- package/dist/adapters/notifier/build-state.mjs.map +1 -0
- package/dist/adapters/notifier/events.d.mts +4 -4
- package/dist/adapters/notifier/events.mjs +32 -20
- package/dist/adapters/notifier/events.mjs.map +1 -1
- package/dist/adapters/notifier/format.d.mts +0 -2
- package/dist/adapters/notifier/format.mjs +1 -2
- package/dist/adapters/notifier/provider.d.mts +53 -9
- package/dist/adapters/notifier/provider.mjs +11 -0
- package/dist/adapters/notifier/provider.mjs.map +1 -0
- package/dist/adapters/notifier/state.d.mts +22 -0
- package/dist/adapters/notifier/state.mjs +65 -0
- package/dist/adapters/notifier/state.mjs.map +1 -0
- package/dist/adapters/notifier/types.d.mts +51 -4
- package/dist/adapters/remote-browser.d.mts +168 -0
- package/dist/adapters/remote-browser.mjs +201 -0
- package/dist/adapters/remote-browser.mjs.map +1 -0
- package/dist/adapters/remote-lease-attempt.mjs +39 -0
- package/dist/adapters/remote-lease-attempt.mjs.map +1 -0
- package/dist/adapters/remote-lease-error.d.mts +25 -0
- package/dist/adapters/remote-lease-error.mjs +54 -0
- package/dist/adapters/remote-lease-error.mjs.map +1 -0
- package/dist/adapters/remote-lease-held.d.mts +15 -0
- package/dist/adapters/remote-lease-held.mjs +86 -0
- package/dist/adapters/remote-lease-held.mjs.map +1 -0
- package/dist/adapters/remote-lease-shutdown.d.mts +12 -0
- package/dist/adapters/remote-lease-shutdown.mjs +32 -0
- package/dist/adapters/remote-lease-shutdown.mjs.map +1 -0
- package/dist/adapters/remote-lease.d.mts +39 -0
- package/dist/adapters/remote-lease.mjs +175 -0
- package/dist/adapters/remote-lease.mjs.map +1 -0
- package/dist/adapters/remote-limiter.mjs +121 -0
- package/dist/adapters/remote-limiter.mjs.map +1 -0
- package/dist/adapters/remote-pool.d.mts +56 -0
- package/dist/adapters/remote-pool.mjs +195 -0
- package/dist/adapters/remote-pool.mjs.map +1 -0
- package/dist/adapters/render-runs.d.mts +23 -0
- package/dist/adapters/render-runs.mjs +40 -0
- package/dist/adapters/render-runs.mjs.map +1 -0
- package/dist/adapters/setup-runner.d.mts +0 -2
- package/dist/adapters/setup-runner.mjs +3 -6
- package/dist/adapters/setup-runner.mjs.map +1 -1
- package/dist/adapters/sources.d.mts +10 -0
- package/dist/adapters/sources.mjs +19 -0
- package/dist/adapters/sources.mjs.map +1 -0
- package/dist/adapters/validate.d.mts +0 -2
- package/dist/adapters/validate.mjs +27 -20
- package/dist/adapters/validate.mjs.map +1 -1
- package/dist/ai.d.mts +103 -0
- package/dist/ai.mjs +18 -0
- package/dist/ai.mjs.map +1 -0
- package/dist/auth.d.mts +0 -2
- package/dist/auth.mjs +1 -2
- package/dist/brand-packs.d.mts +0 -2
- package/dist/brand-packs.mjs +0 -1
- package/dist/capture/a11y/axe-source.d.mts +16 -0
- package/dist/capture/a11y/axe-source.mjs +36 -0
- package/dist/capture/a11y/axe-source.mjs.map +1 -0
- package/dist/capture/a11y/in-page.d.mts +25 -0
- package/dist/capture/a11y/in-page.mjs +59 -0
- package/dist/capture/a11y/in-page.mjs.map +1 -0
- package/dist/capture/a11y/map.d.mts +22 -0
- package/dist/capture/a11y/map.mjs +47 -0
- package/dist/capture/a11y/map.mjs.map +1 -0
- package/dist/capture/a11y/options.d.mts +21 -0
- package/dist/capture/a11y/options.mjs +74 -0
- package/dist/capture/a11y/options.mjs.map +1 -0
- package/dist/capture/adapter.d.mts +46 -5
- package/dist/capture/adapter.mjs +1 -2
- package/dist/capture/adapter.mjs.map +1 -1
- package/dist/capture/affected.d.mts +2 -2
- package/dist/capture/affected.mjs +14 -6
- package/dist/capture/affected.mjs.map +1 -1
- package/dist/capture/attempt-logger.mjs +83 -0
- package/dist/capture/attempt-logger.mjs.map +1 -0
- package/dist/capture/browsers.d.mts +33 -0
- package/dist/capture/browsers.mjs +66 -0
- package/dist/capture/browsers.mjs.map +1 -0
- package/dist/capture/build-state/deliver.d.mts +30 -0
- package/dist/capture/build-state/deliver.mjs +117 -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 +54 -0
- package/dist/capture/build-state/notify-job.mjs +132 -0
- package/dist/capture/build-state/notify-job.mjs.map +1 -0
- package/dist/capture/build-state/publisher.d.mts +21 -0
- package/dist/capture/build-state/publisher.mjs +106 -0
- package/dist/capture/build-state/publisher.mjs.map +1 -0
- package/dist/capture/build-state/resume.d.mts +7 -0
- package/dist/capture/build-state/resume.mjs +46 -0
- package/dist/capture/build-state/resume.mjs.map +1 -0
- package/dist/capture/build-state/types.d.mts +47 -0
- package/dist/capture/catalog/catalogue.d.mts +20 -0
- package/dist/capture/catalog/catalogue.mjs +52 -0
- package/dist/capture/catalog/catalogue.mjs.map +1 -0
- package/dist/capture/catalog/components.d.mts +21 -0
- package/dist/capture/catalog/components.mjs +54 -0
- package/dist/capture/catalog/components.mjs.map +1 -0
- package/dist/capture/catalog/history.d.mts +28 -0
- package/dist/capture/catalog/history.mjs +50 -0
- package/dist/capture/catalog/history.mjs.map +1 -0
- package/dist/capture/catalog/hook.d.mts +12 -0
- package/dist/capture/catalog/hook.mjs +18 -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 +24 -0
- package/dist/capture/catalog/load.mjs +48 -0
- package/dist/capture/catalog/load.mjs.map +1 -0
- package/dist/capture/catalog/parse.d.mts +13 -0
- package/dist/capture/catalog/parse.mjs +55 -0
- package/dist/capture/catalog/parse.mjs.map +1 -0
- package/dist/capture/catalog/read.d.mts +15 -0
- package/dist/capture/catalog/read.mjs +39 -0
- package/dist/capture/catalog/read.mjs.map +1 -0
- package/dist/capture/catalog/types.d.mts +31 -0
- package/dist/capture/comparison-reference.d.mts +27 -0
- package/dist/capture/comparison-reference.mjs +75 -0
- package/dist/capture/comparison-reference.mjs.map +1 -0
- package/dist/capture/consumer/capture-handler.d.mts +56 -0
- package/dist/capture/consumer/capture-handler.mjs +117 -0
- package/dist/capture/consumer/capture-handler.mjs.map +1 -0
- package/dist/capture/consumer/idle.d.mts +15 -0
- package/dist/capture/consumer/idle.mjs +24 -0
- package/dist/capture/consumer/idle.mjs.map +1 -0
- package/dist/capture/consumer/index.d.mts +11 -0
- package/dist/capture/consumer/index.mjs +138 -0
- package/dist/capture/consumer/index.mjs.map +1 -0
- package/dist/capture/consumer/loop.mjs +109 -0
- package/dist/capture/consumer/loop.mjs.map +1 -0
- package/dist/capture/consumer/metrics.d.mts +39 -0
- package/dist/capture/consumer/metrics.mjs +37 -0
- package/dist/capture/consumer/metrics.mjs.map +1 -0
- package/dist/capture/consumer/retry.d.mts +31 -0
- package/dist/capture/consumer/retry.mjs +33 -0
- package/dist/capture/consumer/retry.mjs.map +1 -0
- package/dist/capture/consumer/run-job.mjs +194 -0
- package/dist/capture/consumer/run-job.mjs.map +1 -0
- package/dist/capture/consumer/startup.mjs +37 -0
- package/dist/capture/consumer/startup.mjs.map +1 -0
- package/dist/capture/consumer/types.d.mts +95 -0
- package/dist/capture/crash-failure.d.mts +23 -0
- package/dist/capture/crash-failure.mjs +69 -0
- package/dist/capture/crash-failure.mjs.map +1 -0
- package/dist/capture/db-queue/cadence.d.mts +30 -0
- package/dist/capture/db-queue/cadence.mjs +37 -0
- package/dist/capture/db-queue/cadence.mjs.map +1 -0
- package/dist/capture/db-queue/claim.d.mts +7 -0
- package/dist/capture/db-queue/claim.mjs +148 -0
- package/dist/capture/db-queue/claim.mjs.map +1 -0
- package/dist/capture/db-queue/clock.mjs +11 -0
- package/dist/capture/db-queue/clock.mjs.map +1 -0
- package/dist/capture/db-queue/codec.mjs +75 -0
- package/dist/capture/db-queue/codec.mjs.map +1 -0
- package/dist/capture/db-queue/index.d.mts +18 -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 +34 -0
- package/dist/capture/db-queue/keeper.mjs +91 -0
- package/dist/capture/db-queue/keeper.mjs.map +1 -0
- package/dist/capture/db-queue/lease.mjs +45 -0
- package/dist/capture/db-queue/lease.mjs.map +1 -0
- package/dist/capture/db-queue/lifecycle.mjs +35 -0
- package/dist/capture/db-queue/lifecycle.mjs.map +1 -0
- package/dist/capture/db-queue/operations.mjs +93 -0
- package/dist/capture/db-queue/operations.mjs.map +1 -0
- package/dist/capture/db-queue/poll-loop.d.mts +28 -0
- package/dist/capture/db-queue/poll-loop.mjs +88 -0
- package/dist/capture/db-queue/poll-loop.mjs.map +1 -0
- package/dist/capture/db-queue/poll.d.mts +8 -0
- package/dist/capture/db-queue/poll.mjs +68 -0
- package/dist/capture/db-queue/poll.mjs.map +1 -0
- package/dist/capture/db-queue/timers.d.mts +10 -0
- package/dist/capture/db-queue/timers.mjs +36 -0
- package/dist/capture/db-queue/timers.mjs.map +1 -0
- package/dist/capture/db-queue/types.d.mts +81 -0
- package/dist/capture/db-queue/types.mjs +11 -0
- package/dist/capture/db-queue/types.mjs.map +1 -0
- package/dist/capture/db-queue/wake.mjs +47 -0
- package/dist/capture/db-queue/wake.mjs.map +1 -0
- package/dist/capture/design.mjs +38 -0
- package/dist/capture/design.mjs.map +1 -0
- package/dist/capture/diff-record.mjs +24 -5
- package/dist/capture/diff-record.mjs.map +1 -1
- package/dist/capture/dispatch.d.mts +13 -10
- package/dist/capture/dispatch.mjs +102 -111
- package/dist/capture/dispatch.mjs.map +1 -1
- package/dist/capture/enrich.mjs +59 -0
- package/dist/capture/enrich.mjs.map +1 -0
- package/dist/capture/failure-sets.mjs +21 -0
- package/dist/capture/failure-sets.mjs.map +1 -0
- package/dist/capture/fingerprint.d.mts +40 -0
- package/dist/capture/fingerprint.mjs +82 -0
- package/dist/capture/fingerprint.mjs.map +1 -0
- package/dist/capture/forked/channel.d.mts +23 -0
- package/dist/capture/forked/child.d.mts +32 -0
- package/dist/capture/forked/child.mjs +201 -0
- package/dist/capture/forked/child.mjs.map +1 -0
- package/dist/capture/forked/crash-tracker.mjs +22 -0
- package/dist/capture/forked/crash-tracker.mjs.map +1 -0
- package/dist/capture/forked/decision.d.mts +24 -0
- package/dist/capture/forked/decision.mjs +26 -0
- package/dist/capture/forked/decision.mjs.map +1 -0
- package/dist/capture/forked/exit.mjs +11 -0
- package/dist/capture/forked/exit.mjs.map +1 -0
- package/dist/capture/forked/fork.d.mts +29 -0
- package/dist/capture/forked/fork.mjs +92 -0
- package/dist/capture/forked/fork.mjs.map +1 -0
- package/dist/capture/forked/forked-capture.d.mts +38 -0
- package/dist/capture/forked/forked-capture.mjs +239 -0
- package/dist/capture/forked/forked-capture.mjs.map +1 -0
- package/dist/capture/forked/lease-book.mjs +45 -0
- package/dist/capture/forked/lease-book.mjs.map +1 -0
- package/dist/capture/forked/protocol.d.mts +36 -0
- package/dist/capture/forked/protocol.mjs +24 -0
- package/dist/capture/forked/protocol.mjs.map +1 -0
- package/dist/capture/forked/runs.mjs +74 -0
- package/dist/capture/forked/runs.mjs.map +1 -0
- package/dist/capture/guards.mjs +1 -2
- package/dist/capture/ignore-regions.d.mts +36 -0
- package/dist/capture/ignore-regions.mjs +65 -0
- package/dist/capture/ignore-regions.mjs.map +1 -0
- package/dist/capture/isolate.mjs +140 -0
- package/dist/capture/isolate.mjs.map +1 -0
- package/dist/capture/job-lease.d.mts +26 -0
- package/dist/capture/job-lease.mjs +56 -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 +23 -0
- package/dist/capture/modes.mjs +106 -0
- package/dist/capture/modes.mjs.map +1 -0
- package/dist/capture/orchestrator.d.mts +37 -7
- package/dist/capture/orchestrator.mjs +120 -110
- package/dist/capture/orchestrator.mjs.map +1 -1
- package/dist/capture/pipeline.d.mts +9 -4
- package/dist/capture/pipeline.mjs +284 -48
- package/dist/capture/pipeline.mjs.map +1 -1
- package/dist/capture/progress.d.mts +9 -0
- package/dist/capture/progress.mjs +27 -0
- package/dist/capture/progress.mjs.map +1 -0
- package/dist/capture/publish/accept.d.mts +37 -0
- package/dist/capture/publish/accept.mjs +27 -0
- package/dist/capture/publish/accept.mjs.map +1 -0
- package/dist/capture/publish/complete.d.mts +44 -0
- package/dist/capture/publish/complete.mjs +63 -0
- package/dist/capture/publish/complete.mjs.map +1 -0
- package/dist/capture/publish/extract.d.mts +18 -0
- package/dist/capture/publish/extract.mjs +120 -0
- package/dist/capture/publish/extract.mjs.map +1 -0
- package/dist/capture/publish/handler.d.mts +26 -0
- package/dist/capture/publish/handler.mjs +61 -0
- package/dist/capture/publish/handler.mjs.map +1 -0
- package/dist/capture/publish/index.d.mts +11 -0
- package/dist/capture/publish/invalid-zip.d.mts +8 -0
- package/dist/capture/publish/invalid-zip.mjs +11 -0
- package/dist/capture/publish/invalid-zip.mjs.map +1 -0
- package/dist/capture/publish/limits.d.mts +35 -0
- package/dist/capture/publish/limits.mjs +38 -0
- package/dist/capture/publish/limits.mjs.map +1 -0
- package/dist/capture/publish/published.d.mts +13 -0
- package/dist/capture/publish/published.mjs +17 -0
- package/dist/capture/publish/published.mjs.map +1 -0
- package/dist/capture/publish/run.d.mts +31 -0
- package/dist/capture/publish/run.mjs +29 -0
- package/dist/capture/publish/run.mjs.map +1 -0
- package/dist/capture/rediff.d.mts +1 -3
- package/dist/capture/rediff.mjs +23 -9
- package/dist/capture/rediff.mjs.map +1 -1
- package/dist/capture/render.mjs +52 -0
- package/dist/capture/render.mjs.map +1 -0
- package/dist/capture/scratch.d.mts +42 -0
- package/dist/capture/scratch.mjs +136 -0
- package/dist/capture/scratch.mjs.map +1 -0
- package/dist/capture/shard-plan.mjs +127 -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 +175 -0
- package/dist/capture/shards/execute.mjs.map +1 -0
- package/dist/capture/shards/fan-out.mjs +125 -0
- package/dist/capture/shards/fan-out.mjs.map +1 -0
- package/dist/capture/shards/history.mjs +34 -0
- package/dist/capture/shards/history.mjs.map +1 -0
- package/dist/capture/shards/reduce.mjs +96 -0
- package/dist/capture/shards/reduce.mjs.map +1 -0
- package/dist/capture/sizing.d.mts +0 -2
- package/dist/capture/sizing.mjs +2 -13
- package/dist/capture/sizing.mjs.map +1 -1
- package/dist/capture/skip-checks.mjs +3 -67
- package/dist/capture/skip-checks.mjs.map +1 -1
- package/dist/capture/statics.d.mts +7 -10
- package/dist/capture/statics.mjs +26 -74
- package/dist/capture/statics.mjs.map +1 -1
- package/dist/capture/storybook-urls.d.mts +38 -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 -6
- package/dist/capture/storybook.mjs +63 -19
- package/dist/capture/storybook.mjs.map +1 -1
- package/dist/capture/target.mjs +20 -0
- package/dist/capture/target.mjs.map +1 -0
- package/dist/capture/targets.d.mts +23 -0
- package/dist/capture/targets.mjs +31 -0
- package/dist/capture/targets.mjs.map +1 -0
- package/dist/capture/tests.d.mts +35 -0
- package/dist/capture/tests.mjs +136 -0
- package/dist/capture/tests.mjs.map +1 -0
- package/dist/capture/token-chunks.mjs +91 -0
- package/dist/capture/token-chunks.mjs.map +1 -0
- package/dist/capture.d.mts +63 -7
- package/dist/capture.mjs +51 -3
- package/dist/config.d.mts +136 -14
- package/dist/config.mjs +14 -8
- package/dist/config.mjs.map +1 -1
- package/dist/diff.d.mts +7 -3
- package/dist/diff.mjs +15 -3
- package/dist/diff.mjs.map +1 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +2 -2
- package/dist/insights/budget.d.mts +18 -0
- package/dist/insights/budget.mjs +32 -0
- package/dist/insights/budget.mjs.map +1 -0
- package/dist/insights/envelope.d.mts +57 -0
- package/dist/insights/envelope.mjs +59 -0
- package/dist/insights/envelope.mjs.map +1 -0
- package/dist/insights/evidence-health.d.mts +30 -0
- package/dist/insights/evidence-health.mjs +27 -0
- package/dist/insights/evidence-health.mjs.map +1 -0
- package/dist/insights/evidence.d.mts +53 -0
- package/dist/insights/evidence.mjs +63 -0
- package/dist/insights/evidence.mjs.map +1 -0
- package/dist/insights/hash.d.mts +16 -0
- package/dist/insights/hash.mjs +20 -0
- package/dist/insights/hash.mjs.map +1 -0
- package/dist/insights/limits.d.mts +22 -0
- package/dist/insights/limits.mjs +23 -0
- package/dist/insights/limits.mjs.map +1 -0
- package/dist/insights/prompts/v1.d.mts +11 -0
- package/dist/insights/prompts/v1.mjs +33 -0
- package/dist/insights/prompts/v1.mjs.map +1 -0
- package/dist/insights/redact.d.mts +7 -0
- package/dist/insights/redact.mjs +23 -0
- package/dist/insights/redact.mjs.map +1 -0
- package/dist/insights/text.mjs +43 -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.d.mts +0 -2
- package/dist/logger.mjs +18 -3
- package/dist/logger.mjs.map +1 -1
- package/dist/models/ai-budget-alert.d.mts +21 -0
- package/dist/models/ai-budget-alert.mjs +37 -0
- package/dist/models/ai-budget-alert.mjs.map +1 -0
- package/dist/models/ai-usage.d.mts +38 -0
- package/dist/models/ai-usage.mjs +60 -0
- package/dist/models/ai-usage.mjs.map +1 -0
- package/dist/models/baseline-approval.d.mts +18 -0
- package/dist/models/baseline-approval.mjs +41 -0
- package/dist/models/baseline-approval.mjs.map +1 -0
- package/dist/models/baseline-archive.d.mts +82 -0
- package/dist/models/baseline-archive.mjs +105 -0
- package/dist/models/baseline-archive.mjs.map +1 -0
- package/dist/models/baseline-atomic.mjs +66 -0
- package/dist/models/baseline-atomic.mjs.map +1 -0
- package/dist/models/baseline-conflict.d.mts +6 -0
- package/dist/models/baseline-conflict.mjs +9 -0
- package/dist/models/baseline-conflict.mjs.map +1 -0
- package/dist/models/baseline-export.d.mts +17 -0
- package/dist/models/baseline-export.mjs +135 -0
- package/dist/models/baseline-export.mjs.map +1 -0
- package/dist/models/baseline-import.mjs +117 -0
- package/dist/models/baseline-import.mjs.map +1 -0
- package/dist/models/baseline-origin.d.mts +16 -0
- package/dist/models/baseline-origin.mjs +35 -0
- package/dist/models/baseline-origin.mjs.map +1 -0
- package/dist/models/baseline-status.d.mts +0 -2
- package/dist/models/baseline-status.mjs +1 -2
- package/dist/models/baseline-transfer.d.mts +48 -0
- package/dist/models/baseline-transfer.mjs +149 -0
- package/dist/models/baseline-transfer.mjs.map +1 -0
- package/dist/models/baseline.d.mts +37 -7
- package/dist/models/baseline.mjs +152 -28
- package/dist/models/baseline.mjs.map +1 -1
- package/dist/models/build-component.d.mts +26 -0
- package/dist/models/build-component.mjs +54 -0
- package/dist/models/build-component.mjs.map +1 -0
- package/dist/models/build-review-status.d.mts +27 -0
- package/dist/models/build-review-status.mjs +36 -0
- package/dist/models/build-review-status.mjs.map +1 -0
- package/dist/models/build-review.d.mts +26 -0
- package/dist/models/build-review.mjs +53 -0
- package/dist/models/build-review.mjs.map +1 -0
- package/dist/models/build.d.mts +25 -5
- package/dist/models/build.mjs +53 -15
- package/dist/models/build.mjs.map +1 -1
- package/dist/models/capture-attempt.d.mts +7 -3
- package/dist/models/capture-attempt.mjs +30 -6
- package/dist/models/capture-attempt.mjs.map +1 -1
- package/dist/models/capture-job.d.mts +104 -0
- package/dist/models/capture-job.mjs +287 -0
- package/dist/models/capture-job.mjs.map +1 -0
- package/dist/models/capture-log.d.mts +1 -3
- package/dist/models/capture-log.mjs +5 -5
- package/dist/models/capture-log.mjs.map +1 -1
- package/dist/models/capture-shard.d.mts +60 -0
- package/dist/models/capture-shard.mjs +189 -0
- package/dist/models/capture-shard.mjs.map +1 -0
- package/dist/models/changeset.d.mts +46 -0
- package/dist/models/changeset.mjs +80 -0
- package/dist/models/changeset.mjs.map +1 -0
- package/dist/models/columns.mjs +18 -0
- package/dist/models/columns.mjs.map +1 -0
- package/dist/models/comment.d.mts +4 -3
- package/dist/models/comment.mjs +8 -6
- package/dist/models/comment.mjs.map +1 -1
- package/dist/models/component.d.mts +41 -0
- package/dist/models/component.mjs +59 -0
- package/dist/models/component.mjs.map +1 -0
- package/dist/models/connections.d.mts +39 -0
- package/dist/models/connections.mjs +111 -0
- package/dist/models/connections.mjs.map +1 -0
- package/dist/models/content-ref.d.mts +1 -3
- package/dist/models/content-ref.mjs +2 -3
- package/dist/models/content-ref.mjs.map +1 -1
- package/dist/models/credentials.d.mts +0 -2
- package/dist/models/credentials.mjs +13 -4
- package/dist/models/credentials.mjs.map +1 -1
- package/dist/models/design-credentials.d.mts +39 -0
- package/dist/models/design-credentials.mjs +85 -0
- package/dist/models/design-credentials.mjs.map +1 -0
- package/dist/models/design-health.d.mts +14 -0
- package/dist/models/design-health.mjs +24 -0
- package/dist/models/design-health.mjs.map +1 -0
- package/dist/models/design-nodes.d.mts +81 -0
- package/dist/models/design-nodes.mjs +221 -0
- package/dist/models/design-nodes.mjs.map +1 -0
- package/dist/models/design-renders.mjs +73 -0
- package/dist/models/design-renders.mjs.map +1 -0
- package/dist/models/design-resolve.d.mts +27 -0
- package/dist/models/design-resolve.mjs +40 -0
- package/dist/models/design-resolve.mjs.map +1 -0
- package/dist/models/design.d.mts +37 -0
- package/dist/models/design.mjs +89 -0
- package/dist/models/design.mjs.map +1 -0
- package/dist/models/inherited-ratio.d.mts +19 -0
- package/dist/models/inherited-ratio.mjs +21 -0
- package/dist/models/inherited-ratio.mjs.map +1 -0
- package/dist/models/insight.d.mts +68 -0
- package/dist/models/insight.mjs +161 -0
- package/dist/models/insight.mjs.map +1 -0
- package/dist/models/label.d.mts +4 -4
- package/dist/models/label.mjs +26 -12
- package/dist/models/label.mjs.map +1 -1
- package/dist/models/member.d.mts +1 -3
- package/dist/models/member.mjs +6 -6
- package/dist/models/member.mjs.map +1 -1
- package/dist/models/notification-channel.d.mts +37 -5
- package/dist/models/notification-channel.mjs +89 -11
- package/dist/models/notification-channel.mjs.map +1 -1
- package/dist/models/notification-subscription.d.mts +1 -4
- package/dist/models/notification-subscription.mjs +8 -11
- package/dist/models/notification-subscription.mjs.map +1 -1
- package/dist/models/project-group-mapping.d.mts +6 -3
- package/dist/models/project-group-mapping.mjs +15 -6
- package/dist/models/project-group-mapping.mjs.map +1 -1
- package/dist/models/project.d.mts +9 -3
- package/dist/models/project.mjs +10 -6
- package/dist/models/project.mjs.map +1 -1
- package/dist/models/public-origin.d.mts +61 -0
- package/dist/models/public-origin.mjs +113 -0
- package/dist/models/public-origin.mjs.map +1 -0
- package/dist/models/review-switch-off.d.mts +12 -0
- package/dist/models/review-switch-off.mjs +25 -0
- package/dist/models/review-switch-off.mjs.map +1 -0
- package/dist/models/site-settings.d.mts +21 -0
- package/dist/models/site-settings.mjs +47 -0
- package/dist/models/site-settings.mjs.map +1 -0
- package/dist/models/snapshot.d.mts +39 -3
- package/dist/models/snapshot.mjs +54 -5
- package/dist/models/snapshot.mjs.map +1 -1
- package/dist/models/token.d.mts +13 -3
- package/dist/models/token.mjs +24 -7
- package/dist/models/token.mjs.map +1 -1
- package/dist/models/usage.d.mts +74 -0
- package/dist/models/usage.mjs +148 -0
- package/dist/models/usage.mjs.map +1 -0
- package/dist/models/user.d.mts +1 -3
- package/dist/models/user.mjs +5 -5
- package/dist/models/user.mjs.map +1 -1
- package/dist/models/webhook.d.mts +15 -3
- package/dist/models/webhook.mjs +34 -6
- package/dist/models/webhook.mjs.map +1 -1
- package/dist/models.d.mts +30 -4
- package/dist/models.mjs +31 -6
- package/dist/options.d.mts +16 -0
- package/dist/options.mjs +88 -0
- package/dist/options.mjs.map +1 -0
- package/dist/paths.d.mts +21 -6
- package/dist/paths.mjs +57 -12
- package/dist/paths.mjs.map +1 -1
- package/dist/retention/ai.d.mts +16 -0
- package/dist/retention/ai.mjs +22 -0
- package/dist/retention/ai.mjs.map +1 -0
- package/dist/retention/pending.mjs +46 -0
- package/dist/retention/pending.mjs.map +1 -0
- package/dist/retention/published-gzip-sweep.mjs +28 -0
- package/dist/retention/published-gzip-sweep.mjs.map +1 -0
- package/dist/retention/screenshot-sweep.d.mts +49 -0
- package/dist/retention/screenshot-sweep.mjs +127 -0
- package/dist/retention/screenshot-sweep.mjs.map +1 -0
- package/dist/retention.d.mts +16 -4
- package/dist/retention.mjs +76 -16
- package/dist/retention.mjs.map +1 -1
- package/dist/schema/baseline.d.mts +8 -2
- package/dist/schema/build-component.d.mts +23 -0
- package/dist/schema/build-review.d.mts +15 -0
- package/dist/schema/build.d.mts +7 -2
- package/dist/schema/capture-attempt.d.mts +0 -2
- package/dist/schema/capture-job.d.mts +58 -0
- package/dist/schema/capture-shard.d.mts +33 -0
- package/dist/schema/comment.d.mts +2 -2
- package/dist/schema/connection.d.mts +22 -0
- package/dist/schema/content-ref.d.mts +0 -2
- package/dist/schema/design.d.mts +56 -0
- package/dist/schema/insight.d.mts +52 -0
- package/dist/schema/label.d.mts +0 -2
- package/dist/schema/member.d.mts +0 -2
- package/dist/schema/notification.d.mts +4 -2
- package/dist/schema/project-group-mapping.d.mts +0 -2
- package/dist/schema/project.d.mts +21 -2
- package/dist/schema/site-setting.d.mts +19 -0
- package/dist/schema/snapshot.d.mts +17 -2
- package/dist/schema/token.d.mts +0 -2
- package/dist/schema/usage.d.mts +28 -0
- package/dist/schema/user.d.mts +0 -2
- package/dist/schema/webhook.d.mts +0 -2
- package/dist/schema.d.mts +12 -6
- package/dist/serverless.d.mts +19 -0
- package/dist/serverless.mjs +36 -0
- package/dist/serverless.mjs.map +1 -0
- package/dist/test-helpers/build-reference-contract.d.mts +14 -0
- package/dist/test-helpers/build-reference-contract.mjs +66 -0
- package/dist/test-helpers/build-reference-contract.mjs.map +1 -0
- package/dist/test-helpers/create-project.d.mts +1 -3
- package/dist/test-helpers/create-project.mjs +1 -2
- package/dist/test-helpers/database-contract.d.mts +0 -2
- package/dist/test-helpers/database-contract.mjs +37 -2
- package/dist/test-helpers/database-contract.mjs.map +1 -1
- package/dist/test-helpers/deferred.d.mts +37 -0
- package/dist/test-helpers/deferred.mjs +60 -0
- package/dist/test-helpers/deferred.mjs.map +1 -0
- package/dist/test-helpers/design-contract.d.mts +19 -0
- package/dist/test-helpers/design-contract.mjs +39 -0
- package/dist/test-helpers/design-contract.mjs.map +1 -0
- package/dist/test-helpers/fake-adapters.d.mts +0 -2
- package/dist/test-helpers/fake-adapters.mjs +1 -2
- package/dist/test-helpers/fake-database.d.mts +0 -2
- package/dist/test-helpers/fake-database.mjs +21 -4
- package/dist/test-helpers/fake-database.mjs.map +1 -1
- package/dist/test-helpers/fake-design.d.mts +41 -0
- package/dist/test-helpers/fake-design.mjs +113 -0
- package/dist/test-helpers/fake-design.mjs.map +1 -0
- package/dist/test-helpers/fake-notifier.d.mts +19 -0
- package/dist/test-helpers/fake-notifier.mjs +43 -0
- package/dist/test-helpers/fake-notifier.mjs.map +1 -0
- package/dist/test-helpers/fake-storage.d.mts +0 -2
- package/dist/test-helpers/fake-storage.mjs +1 -2
- package/dist/test-helpers/fake-tables.mjs +260 -69
- package/dist/test-helpers/fake-tables.mjs.map +1 -1
- package/dist/test-helpers/manual-clock.d.mts +25 -0
- package/dist/test-helpers/manual-clock.mjs +55 -0
- package/dist/test-helpers/manual-clock.mjs.map +1 -0
- package/dist/test-helpers/notifier-contract.d.mts +7 -3
- package/dist/test-helpers/notifier-contract.mjs +33 -5
- package/dist/test-helpers/notifier-contract.mjs.map +1 -1
- package/dist/test-helpers/queue-contract.d.mts +16 -4
- package/dist/test-helpers/queue-contract.mjs +196 -3
- package/dist/test-helpers/queue-contract.mjs.map +1 -1
- package/dist/test-helpers/queue-multi-instance.d.mts +18 -0
- package/dist/test-helpers/queue-multi-instance.mjs +205 -0
- package/dist/test-helpers/queue-multi-instance.mjs.map +1 -0
- package/dist/test-helpers/runner-contract.d.mts +0 -2
- package/dist/test-helpers/runner-contract.mjs +12 -2
- package/dist/test-helpers/runner-contract.mjs.map +1 -1
- package/dist/test-helpers/sql-chunks.mjs +25 -2
- package/dist/test-helpers/sql-chunks.mjs.map +1 -1
- package/dist/test-helpers/storage-contract.d.mts +0 -2
- package/dist/test-helpers/storage-contract.mjs +2 -3
- package/dist/test-helpers.d.mts +10 -4
- package/dist/test-helpers.mjs +9 -3
- package/dist/tracing.mjs +36 -6
- package/dist/tracing.mjs.map +1 -1
- package/dist/types.d.mts +14 -5
- package/dist/types.mjs +22 -6
- package/dist/types.mjs.map +1 -1
- package/dist/urls.d.mts +4 -2
- package/dist/urls.mjs +12 -4
- package/dist/urls.mjs.map +1 -1
- package/dist/utils/adaptive-concurrency.d.mts +64 -0
- package/dist/utils/adaptive-concurrency.mjs +148 -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 +42 -0
- package/dist/utils/connect-retry.mjs.map +1 -0
- package/dist/utils/encrypt.d.mts +0 -2
- package/dist/utils/encrypt.mjs +1 -2
- package/dist/utils/environment.d.mts +17 -0
- package/dist/utils/environment.mjs +58 -0
- package/dist/utils/environment.mjs.map +1 -0
- package/dist/utils/hash.d.mts +0 -2
- package/dist/utils/hash.mjs +1 -2
- package/dist/utils/http.d.mts +29 -3
- package/dist/utils/http.mjs +100 -19
- package/dist/utils/http.mjs.map +1 -1
- package/dist/utils/map-adaptive.d.mts +13 -0
- package/dist/utils/map-adaptive.mjs +71 -0
- package/dist/utils/map-adaptive.mjs.map +1 -0
- package/dist/utils/map-limit.d.mts +8 -0
- package/dist/utils/map-limit.mjs +31 -0
- package/dist/utils/map-limit.mjs.map +1 -0
- package/dist/utils/mime.d.mts +0 -2
- package/dist/utils/mime.mjs +1 -2
- package/dist/utils/page-limiter.d.mts +35 -0
- package/dist/utils/page-limiter.mjs +55 -0
- package/dist/utils/page-limiter.mjs.map +1 -0
- package/dist/utils/path-security.d.mts +0 -2
- package/dist/utils/path-security.mjs +2 -4
- package/dist/utils/path-security.mjs.map +1 -1
- package/dist/utils/public-origin.d.mts +18 -0
- package/dist/utils/public-origin.mjs +74 -0
- package/dist/utils/public-origin.mjs.map +1 -0
- package/dist/utils/redact.d.mts +0 -2
- package/dist/utils/redact.mjs +4 -2
- package/dist/utils/redact.mjs.map +1 -1
- package/dist/utils/remote-environment.d.mts +76 -0
- package/dist/utils/remote-environment.mjs +163 -0
- package/dist/utils/remote-environment.mjs.map +1 -0
- package/dist/utils/resources.d.mts +62 -0
- package/dist/utils/resources.mjs +121 -0
- package/dist/utils/resources.mjs.map +1 -0
- package/dist/utils/screenshot-store.d.mts +13 -0
- package/dist/utils/screenshot-store.mjs +40 -0
- package/dist/utils/screenshot-store.mjs.map +1 -0
- package/dist/utils/session-gate.d.mts +23 -0
- package/dist/utils/session-gate.mjs +69 -0
- package/dist/utils/session-gate.mjs.map +1 -0
- package/dist/utils/storybook-token.d.mts +89 -0
- package/dist/utils/storybook-token.mjs +135 -0
- package/dist/utils/storybook-token.mjs.map +1 -0
- package/dist/utils/storybook-url.d.mts +13 -0
- package/dist/utils/storybook-url.mjs +20 -0
- package/dist/utils/storybook-url.mjs.map +1 -0
- package/dist/utils/timing.d.mts +0 -2
- package/dist/utils/timing.mjs +2 -3
- package/dist/utils/timing.mjs.map +1 -1
- package/dist/utils/ulid.d.mts +0 -2
- package/dist/utils/ulid.mjs +1 -2
- package/dist/utils/zip-read.mjs +128 -0
- package/dist/utils/zip-read.mjs.map +1 -0
- package/dist/utils/zip-write.d.mts +16 -0
- package/dist/utils/zip-write.mjs +187 -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 +35 -26
- 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
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
|
-
import { StoryEntry, StoryParameters, StorySourceAdapter, StoryViewportConfig, Viewport } from "./adapter.mjs";
|
|
3
|
+
import { DiscoverOptions, StoryEntry, StoryParameters, StorySourceAdapter, StoryViewportConfig, Viewport } from "./adapter.mjs";
|
|
4
4
|
import { DepGraph } from "@storyshelf/affected";
|
|
5
|
-
//#region src/capture/storybook.d.ts
|
|
6
5
|
/** Story parameters as serialized by the Storybook index. */
|
|
7
6
|
interface StorybookParameters {
|
|
8
7
|
chromatic?: StoryParameters;
|
|
9
|
-
storyshelf?: StoryParameters
|
|
8
|
+
storyshelf?: StoryParameters & {
|
|
9
|
+
figma?: unknown;
|
|
10
|
+
};
|
|
10
11
|
viewport?: StoryViewportConfig;
|
|
12
|
+
design?: unknown;
|
|
11
13
|
}
|
|
12
14
|
/** Merge the `chromatic` and `storyshelf` parameter layers (`storyshelf` wins). */
|
|
13
15
|
declare function mergeParameters(entry: {
|
|
@@ -29,8 +31,13 @@ declare function resolveStoryViewports(entry: Pick<StoryEntry, "parameters">, gl
|
|
|
29
31
|
declare class StorybookAdapter implements StorySourceAdapter {
|
|
30
32
|
readonly name = "storybook";
|
|
31
33
|
readonly screenshotSelector = "#storybook-root";
|
|
32
|
-
discover(source: string): Promise<StoryEntry[]>;
|
|
33
|
-
|
|
34
|
+
discover(source: string, options?: DiscoverOptions): Promise<StoryEntry[]>;
|
|
35
|
+
/**
|
|
36
|
+
* The `.test()` entries of a Storybook 10 build (index `subtype: "test"`), which {@link discover}
|
|
37
|
+
* leaves out. Empty for older Storybooks, whose index has no such entries.
|
|
38
|
+
*/
|
|
39
|
+
discoverTests(source: string): Promise<StoryEntry[]>;
|
|
40
|
+
buildUrl(baseUrl: string, storyId: string, globals?: Record<string, unknown>): string;
|
|
34
41
|
/**
|
|
35
42
|
* Load the Vite stats dependency graph of an extracted build for affected
|
|
36
43
|
* capture. Resolves `null` when the build ships no usable stats, in which
|
|
@@ -39,6 +46,5 @@ declare class StorybookAdapter implements StorySourceAdapter {
|
|
|
39
46
|
loadDependencyGraph(source: string): Promise<DepGraph | null>;
|
|
40
47
|
private readIndex;
|
|
41
48
|
}
|
|
42
|
-
//#endregion
|
|
43
49
|
export { STORYBOOK_BUILTIN_VIEWPORTS, StorybookAdapter, mergeParameters, resolveStoryViewports };
|
|
44
50
|
//# sourceMappingURL=storybook.d.mts.map
|
|
@@ -1,17 +1,35 @@
|
|
|
1
|
-
import "node:path";
|
|
1
|
+
import { join } from "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
+
import { declaredIgnoreSelectors } from "./ignore-regions.mjs";
|
|
5
|
+
import { applyModes, buildGlobalsParam } from "./modes.mjs";
|
|
6
|
+
import { extractDesignUrl } from "./design.mjs";
|
|
4
7
|
import { STATS_FILENAME, loadDepGraph } from "@storyshelf/affected";
|
|
5
|
-
import { join } from "node:path";
|
|
6
8
|
import { readFile } from "node:fs/promises";
|
|
7
|
-
|
|
9
|
+
// src/capture/storybook.ts
|
|
8
10
|
/** Merge the `chromatic` and `storyshelf` parameter layers (`storyshelf` wins). */
|
|
9
11
|
function mergeParameters(entry) {
|
|
12
|
+
const { figma: _figma, ...storyshelf } = entry.parameters?.storyshelf ?? {};
|
|
10
13
|
const merged = {
|
|
11
14
|
...entry.parameters?.chromatic,
|
|
12
|
-
...
|
|
15
|
+
...storyshelf
|
|
13
16
|
};
|
|
17
|
+
const modes = mergeModes(entry.parameters?.chromatic?.modes, entry.parameters?.storyshelf?.modes);
|
|
18
|
+
if (modes) merged.modes = modes;
|
|
14
19
|
if (entry.parameters?.viewport) merged.viewport = entry.parameters.viewport;
|
|
20
|
+
if ("ignoreSelectors" in merged) {
|
|
21
|
+
const declared = declaredIgnoreSelectors(merged.ignoreSelectors);
|
|
22
|
+
if (declared) merged.ignoreSelectors = declared;
|
|
23
|
+
else delete merged.ignoreSelectors;
|
|
24
|
+
}
|
|
25
|
+
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
26
|
+
}
|
|
27
|
+
/** Union both parameter keys' modes by name (`storyshelf` wins per mode). */
|
|
28
|
+
function mergeModes(chromatic, storyshelf) {
|
|
29
|
+
const merged = {
|
|
30
|
+
...chromatic,
|
|
31
|
+
...storyshelf
|
|
32
|
+
};
|
|
15
33
|
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
16
34
|
}
|
|
17
35
|
/**
|
|
@@ -48,8 +66,13 @@ const STORYBOOK_BUILTIN_VIEWPORTS = {
|
|
|
48
66
|
*/
|
|
49
67
|
function resolveStoryViewports(entry, globalViewports) {
|
|
50
68
|
const resolved = storyViewport(entry);
|
|
51
|
-
|
|
52
|
-
return
|
|
69
|
+
const withDefault = resolved && !globalViewports.some((v) => v.name === resolved.name) ? [...globalViewports, resolved] : [...globalViewports];
|
|
70
|
+
return applyModes(entry.parameters?.modes, withDefault, (name) => namedViewport(entry, name));
|
|
71
|
+
}
|
|
72
|
+
/** Resolve a Storybook viewport name via the story's inline config, then builtins. */
|
|
73
|
+
function namedViewport(entry, name) {
|
|
74
|
+
const inline = entry.parameters?.viewport?.viewports?.[name];
|
|
75
|
+
return (inline ? viewportFromInline(name, inline) : void 0) ?? STORYBOOK_BUILTIN_VIEWPORTS[name];
|
|
53
76
|
}
|
|
54
77
|
function storyViewport(entry) {
|
|
55
78
|
const config = entry.parameters?.viewport;
|
|
@@ -78,24 +101,46 @@ function pxOf(value) {
|
|
|
78
101
|
const match = /^(\d+(?:\.\d+)?)px$/u.exec(bare);
|
|
79
102
|
return match ? Number(match[1]) : void 0;
|
|
80
103
|
}
|
|
104
|
+
function toTestEntry(entry) {
|
|
105
|
+
const test = toStoryEntry(entry);
|
|
106
|
+
if (entry.parent !== void 0) test.parentId = entry.parent;
|
|
107
|
+
return test;
|
|
108
|
+
}
|
|
109
|
+
function toStoryEntry(entry, parseDesignUrl) {
|
|
110
|
+
const design = parseDesignUrl ? extractDesignUrl(entry.parameters, parseDesignUrl) : {};
|
|
111
|
+
const story = {
|
|
112
|
+
id: entry.id,
|
|
113
|
+
title: entry.title,
|
|
114
|
+
name: entry.name,
|
|
115
|
+
importPath: entry.importPath,
|
|
116
|
+
tags: entry.tags,
|
|
117
|
+
type: entry.type === "docs" ? "docs" : "story",
|
|
118
|
+
parameters: mergeParameters(entry)
|
|
119
|
+
};
|
|
120
|
+
if (design.url) story.designUrl = design.url;
|
|
121
|
+
if (design.invalid) story.designInvalid = design.invalid;
|
|
122
|
+
return story;
|
|
123
|
+
}
|
|
81
124
|
/** Discovers stories from a built Storybook via its index file. */
|
|
82
125
|
var StorybookAdapter = class {
|
|
83
126
|
name = "storybook";
|
|
84
127
|
screenshotSelector = "#storybook-root";
|
|
85
|
-
async discover(source) {
|
|
128
|
+
async discover(source, options = {}) {
|
|
129
|
+
const index = await this.readIndex(source);
|
|
130
|
+
return Object.values(index.entries).filter((entry) => entry.type !== "docs" && entry.subtype !== "test").map((entry) => toStoryEntry(entry, options.parseDesignUrl));
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* The `.test()` entries of a Storybook 10 build (index `subtype: "test"`), which {@link discover}
|
|
134
|
+
* leaves out. Empty for older Storybooks, whose index has no such entries.
|
|
135
|
+
*/
|
|
136
|
+
async discoverTests(source) {
|
|
86
137
|
const index = await this.readIndex(source);
|
|
87
|
-
return Object.values(index.entries).filter((entry) => entry.
|
|
88
|
-
id: entry.id,
|
|
89
|
-
title: entry.title,
|
|
90
|
-
name: entry.name,
|
|
91
|
-
importPath: entry.importPath,
|
|
92
|
-
tags: entry.tags,
|
|
93
|
-
type: entry.type === "docs" ? "docs" : "story",
|
|
94
|
-
parameters: mergeParameters(entry)
|
|
95
|
-
}));
|
|
138
|
+
return Object.values(index.entries).filter((entry) => entry.subtype === "test" && entry.type !== "docs").map((entry) => toTestEntry(entry));
|
|
96
139
|
}
|
|
97
|
-
buildUrl(baseUrl, storyId) {
|
|
98
|
-
|
|
140
|
+
buildUrl(baseUrl, storyId, globals) {
|
|
141
|
+
const url = `${baseUrl}/iframe.html?id=${encodeURIComponent(storyId)}&viewMode=story`;
|
|
142
|
+
const param = globals ? buildGlobalsParam(globals) : "";
|
|
143
|
+
return param === "" ? url : `${url}&globals=${encodeURIComponent(param)}`;
|
|
99
144
|
}
|
|
100
145
|
/**
|
|
101
146
|
* Load the Vite stats dependency graph of an extracted build for affected
|
|
@@ -118,7 +163,6 @@ var StorybookAdapter = class {
|
|
|
118
163
|
throw new Error(`No Storybook index found in ${source}`);
|
|
119
164
|
}
|
|
120
165
|
};
|
|
121
|
-
//#endregion
|
|
122
166
|
export { STORYBOOK_BUILTIN_VIEWPORTS, StorybookAdapter, mergeParameters, resolveStoryViewports };
|
|
123
167
|
|
|
124
168
|
//# sourceMappingURL=storybook.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storybook.mjs","names":[],"sources":["../../src/capture/storybook.ts"],"sourcesContent":["import { loadDepGraph, STATS_FILENAME, type DepGraph } from \"@storyshelf/affected\";\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type {\n StoryEntry,\n StoryParameters,\n StorySourceAdapter,\n StoryViewportConfig,\n StoryViewportDefinition,\n Viewport,\n} from \"./adapter.ts\";\n\ninterface StorybookIndex {\n v: number;\n entries: Record<\n string,\n {\n id: string;\n name: string;\n title: string;\n importPath?: string;\n tags?: string[];\n type: string;\n subtype?: string;\n parameters?: StorybookParameters;\n }\n >;\n}\n\n/** Story parameters as serialized by the Storybook index. */\ninterface StorybookParameters {\n chromatic?: StoryParameters;\n storyshelf?: StoryParameters;\n viewport?: StoryViewportConfig;\n}\n\n/** Merge the `chromatic` and `storyshelf` parameter layers (`storyshelf` wins). */\nexport function mergeParameters(entry: {\n parameters?: StorybookParameters;\n}): StoryParameters | undefined {\n const merged: StoryParameters = {\n ...entry.parameters?.chromatic,\n ...entry.parameters?.storyshelf,\n };\n if (entry.parameters?.viewport) merged.viewport = entry.parameters.viewport;\n return Object.keys(merged).length > 0 ? merged : undefined;\n}\n\n/**\n * The Storybook `MINIMAL_VIEWPORTS` snapshot used to resolve a story's\n * `defaultViewport` name at capture time. Dimensions are the documented\n * stable set; inline `parameters.viewport.viewports` always win over these.\n */\nexport const STORYBOOK_BUILTIN_VIEWPORTS: Readonly<Record<string, Viewport>> = {\n mobile1: { name: \"mobile1\", width: 320, height: 568 },\n mobile2: { name: \"mobile2\", width: 414, height: 896 },\n tablet: { name: \"tablet\", width: 834, height: 1112 },\n desktop: { name: \"desktop\", width: 1024, height: 1280 },\n};\n\n/**\n * Resolve the viewports a story should be captured at: the project's global\n * list plus the story's Storybook default viewport (union), deduplicated by\n * name. An unresolvable or duplicate default contributes nothing extra.\n */\nexport function resolveStoryViewports(\n entry: Pick<StoryEntry, \"parameters\">,\n globalViewports: Viewport[],\n): Viewport[] {\n const resolved = storyViewport(entry);\n if (resolved && !globalViewports.some((v) => v.name === resolved.name)) {\n return [...globalViewports, resolved];\n }\n return [...globalViewports];\n}\n\nfunction storyViewport(entry: Pick<StoryEntry, \"parameters\">): Viewport | undefined {\n const config = entry.parameters?.viewport;\n const name = config?.defaultViewport;\n const inline = name ? config?.viewports?.[name] : undefined;\n const resolved =\n name && inline\n ? viewportFromInline(name, inline)\n : name\n ? STORYBOOK_BUILTIN_VIEWPORTS[name]\n : undefined;\n return resolved;\n}\n\nfunction viewportFromInline(\n defaultName: string,\n inline: StoryViewportDefinition,\n): Viewport | undefined {\n const dims = viewportDims(inline.styles);\n return dims ? { name: inline.name ?? defaultName, ...dims } : undefined;\n}\n\nfunction viewportDims(\n styles: StoryViewportDefinition[\"styles\"] | undefined,\n): { width: number; height: number } | undefined {\n const width = pxOf(styles?.width);\n const height = pxOf(styles?.height);\n return width !== undefined && height !== undefined ? { width, height } : undefined;\n}\n\nfunction pxOf(value: string | number | undefined): number | undefined {\n if (typeof value === \"number\") return Math.round(value);\n const bare = typeof value === \"string\" ? value.trim() : \"\";\n const match = /^(\\d+(?:\\.\\d+)?)px$/u.exec(bare);\n return match ? Number(match[1]) : undefined;\n}\n\n/** Discovers stories from a built Storybook via its index file. */\nexport class StorybookAdapter implements StorySourceAdapter {\n readonly name = \"storybook\";\n readonly screenshotSelector = \"#storybook-root\";\n\n async discover(source: string): Promise<StoryEntry[]> {\n const index = await this.readIndex(source);\n return Object.values(index.entries)\n .filter((entry) => entry.type !== \"docs\" && entry.subtype !== \"test\")\n .map((entry) => ({\n id: entry.id,\n title: entry.title,\n name: entry.name,\n importPath: entry.importPath,\n tags: entry.tags,\n type: entry.type === \"docs\" ? \"docs\" : \"story\",\n parameters: mergeParameters(entry),\n }));\n }\n\n // eslint-disable-next-line class-methods-use-this\n buildUrl(baseUrl: string, storyId: string): string {\n return `${baseUrl}/iframe.html?id=${encodeURIComponent(storyId)}&viewMode=story`;\n }\n\n /**\n * Load the Vite stats dependency graph of an extracted build for affected\n * capture. Resolves `null` when the build ships no usable stats, in which\n * case the orchestrator renders every story.\n */\n // eslint-disable-next-line class-methods-use-this\n async loadDependencyGraph(source: string): Promise<DepGraph | null> {\n return await loadDepGraph(join(source, STATS_FILENAME));\n }\n\n // eslint-disable-next-line class-methods-use-this\n private async readIndex(source: string): Promise<StorybookIndex> {\n const candidates: string[] = [\"stories.json\", \"index.json\"];\n const results = await Promise.all(\n candidates.map(async (name) => {\n try {\n const raw = await readFile(join(source, name), \"utf8\");\n return JSON.parse(raw) as StorybookIndex;\n } catch {\n return null;\n }\n }),\n );\n const found = results.find((index) => index !== null);\n if (found) {\n return found;\n }\n throw new Error(`No Storybook index found in ${source}`);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"storybook.mjs","names":[],"sources":["../../src/capture/storybook.ts"],"sourcesContent":["import { loadDepGraph, STATS_FILENAME, type DepGraph } from \"@storyshelf/affected\";\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type {\n ModeMap,\n StoryEntry,\n StoryParameters,\n StorySourceAdapter,\n StoryViewportConfig,\n StoryViewportDefinition,\n Viewport,\n} from \"./adapter.ts\";\nimport type { DiscoverOptions } from \"./adapter.ts\";\nimport { extractDesignUrl, type DesignUrlParser } from \"./design.ts\";\nimport { declaredIgnoreSelectors } from \"./ignore-regions.ts\";\nimport { applyModes, buildGlobalsParam } from \"./modes.ts\";\n\ninterface StorybookIndex {\n v: number;\n entries: Record<\n string,\n {\n id: string;\n name: string;\n title: string;\n importPath?: string;\n tags?: string[];\n type: string;\n subtype?: string;\n /** Test entries only: the id of the story the `.test()` is written under. */\n parent?: string;\n parameters?: StorybookParameters;\n }\n >;\n}\n\n/** Story parameters as serialized by the Storybook index. */\ninterface StorybookParameters {\n chromatic?: StoryParameters;\n storyshelf?: StoryParameters & { figma?: unknown };\n viewport?: StoryViewportConfig;\n design?: unknown;\n}\n\n/** Merge the `chromatic` and `storyshelf` parameter layers (`storyshelf` wins). */\nexport function mergeParameters(entry: {\n parameters?: StorybookParameters;\n}): StoryParameters | undefined {\n const { figma: _figma, ...storyshelf } = entry.parameters?.storyshelf ?? {};\n const merged: StoryParameters = { ...entry.parameters?.chromatic, ...storyshelf };\n const modes = mergeModes(entry.parameters?.chromatic?.modes, entry.parameters?.storyshelf?.modes);\n if (modes) merged.modes = modes;\n if (entry.parameters?.viewport) merged.viewport = entry.parameters.viewport;\n if (\"ignoreSelectors\" in merged) {\n const declared = declaredIgnoreSelectors(merged.ignoreSelectors);\n if (declared) merged.ignoreSelectors = declared;\n else delete merged.ignoreSelectors;\n }\n return Object.keys(merged).length > 0 ? merged : undefined;\n}\n\n/** Union both parameter keys' modes by name (`storyshelf` wins per mode). */\nfunction mergeModes(chromatic?: ModeMap, storyshelf?: ModeMap): ModeMap | undefined {\n const merged = { ...chromatic, ...storyshelf };\n return Object.keys(merged).length > 0 ? merged : undefined;\n}\n\n/**\n * The Storybook `MINIMAL_VIEWPORTS` snapshot used to resolve a story's\n * `defaultViewport` name at capture time. Dimensions are the documented\n * stable set; inline `parameters.viewport.viewports` always win over these.\n */\nexport const STORYBOOK_BUILTIN_VIEWPORTS: Readonly<Record<string, Viewport>> = {\n mobile1: { name: \"mobile1\", width: 320, height: 568 },\n mobile2: { name: \"mobile2\", width: 414, height: 896 },\n tablet: { name: \"tablet\", width: 834, height: 1112 },\n desktop: { name: \"desktop\", width: 1024, height: 1280 },\n};\n\n/**\n * Resolve the viewports a story should be captured at: the project's global\n * list plus the story's Storybook default viewport (union), deduplicated by\n * name. An unresolvable or duplicate default contributes nothing extra.\n */\nexport function resolveStoryViewports(\n entry: Pick<StoryEntry, \"parameters\">,\n globalViewports: Viewport[],\n): Viewport[] {\n const resolved = storyViewport(entry);\n const withDefault =\n resolved && !globalViewports.some((v) => v.name === resolved.name)\n ? [...globalViewports, resolved]\n : [...globalViewports];\n return applyModes(entry.parameters?.modes, withDefault, (name) => namedViewport(entry, name));\n}\n\n/** Resolve a Storybook viewport name via the story's inline config, then builtins. */\nfunction namedViewport(entry: Pick<StoryEntry, \"parameters\">, name: string): Viewport | undefined {\n const inline = entry.parameters?.viewport?.viewports?.[name];\n return (\n (inline ? viewportFromInline(name, inline) : undefined) ?? STORYBOOK_BUILTIN_VIEWPORTS[name]\n );\n}\n\nfunction storyViewport(entry: Pick<StoryEntry, \"parameters\">): Viewport | undefined {\n const config = entry.parameters?.viewport;\n const name = config?.defaultViewport;\n const inline = name ? config?.viewports?.[name] : undefined;\n const resolved =\n name && inline\n ? viewportFromInline(name, inline)\n : name\n ? STORYBOOK_BUILTIN_VIEWPORTS[name]\n : undefined;\n return resolved;\n}\n\nfunction viewportFromInline(\n defaultName: string,\n inline: StoryViewportDefinition,\n): Viewport | undefined {\n const dims = viewportDims(inline.styles);\n return dims ? { name: inline.name ?? defaultName, ...dims } : undefined;\n}\n\nfunction viewportDims(\n styles: StoryViewportDefinition[\"styles\"] | undefined,\n): { width: number; height: number } | undefined {\n const width = pxOf(styles?.width);\n const height = pxOf(styles?.height);\n return width !== undefined && height !== undefined ? { width, height } : undefined;\n}\n\nfunction pxOf(value: string | number | undefined): number | undefined {\n if (typeof value === \"number\") return Math.round(value);\n const bare = typeof value === \"string\" ? value.trim() : \"\";\n const match = /^(\\d+(?:\\.\\d+)?)px$/u.exec(bare);\n return match ? Number(match[1]) : undefined;\n}\n\nfunction toTestEntry(entry: StorybookIndex[\"entries\"][string]): StoryEntry {\n const test = toStoryEntry(entry);\n if (entry.parent !== undefined) test.parentId = entry.parent;\n return test;\n}\n\nfunction toStoryEntry(\n entry: StorybookIndex[\"entries\"][string],\n parseDesignUrl?: DesignUrlParser,\n): StoryEntry {\n const design = parseDesignUrl ? extractDesignUrl(entry.parameters, parseDesignUrl) : {};\n const story: StoryEntry = {\n id: entry.id,\n title: entry.title,\n name: entry.name,\n importPath: entry.importPath,\n tags: entry.tags,\n type: entry.type === \"docs\" ? \"docs\" : \"story\",\n parameters: mergeParameters(entry),\n };\n if (design.url) story.designUrl = design.url;\n if (design.invalid) story.designInvalid = design.invalid;\n return story;\n}\n\n/** Discovers stories from a built Storybook via its index file. */\nexport class StorybookAdapter implements StorySourceAdapter {\n readonly name = \"storybook\";\n readonly screenshotSelector = \"#storybook-root\";\n\n async discover(source: string, options: DiscoverOptions = {}): Promise<StoryEntry[]> {\n const index = await this.readIndex(source);\n return Object.values(index.entries)\n .filter((entry) => entry.type !== \"docs\" && entry.subtype !== \"test\")\n .map((entry) => toStoryEntry(entry, options.parseDesignUrl));\n }\n\n /**\n * The `.test()` entries of a Storybook 10 build (index `subtype: \"test\"`), which {@link discover}\n * leaves out. Empty for older Storybooks, whose index has no such entries.\n */\n async discoverTests(source: string): Promise<StoryEntry[]> {\n const index = await this.readIndex(source);\n return Object.values(index.entries)\n .filter((entry) => entry.subtype === \"test\" && entry.type !== \"docs\")\n .map((entry) => toTestEntry(entry));\n }\n\n // eslint-disable-next-line class-methods-use-this\n buildUrl(baseUrl: string, storyId: string, globals?: Record<string, unknown>): string {\n const url = `${baseUrl}/iframe.html?id=${encodeURIComponent(storyId)}&viewMode=story`;\n const param = globals ? buildGlobalsParam(globals) : \"\";\n return param === \"\" ? url : `${url}&globals=${encodeURIComponent(param)}`;\n }\n\n /**\n * Load the Vite stats dependency graph of an extracted build for affected\n * capture. Resolves `null` when the build ships no usable stats, in which\n * case the orchestrator renders every story.\n */\n // eslint-disable-next-line class-methods-use-this\n async loadDependencyGraph(source: string): Promise<DepGraph | null> {\n return await loadDepGraph(join(source, STATS_FILENAME));\n }\n\n // eslint-disable-next-line class-methods-use-this\n private async readIndex(source: string): Promise<StorybookIndex> {\n const candidates: string[] = [\"stories.json\", \"index.json\"];\n const results = await Promise.all(\n candidates.map(async (name) => {\n try {\n const raw = await readFile(join(source, name), \"utf8\");\n return JSON.parse(raw) as StorybookIndex;\n } catch {\n return null;\n }\n }),\n );\n const found = results.find((index) => index !== null);\n if (found) {\n return found;\n }\n throw new Error(`No Storybook index found in ${source}`);\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { BuildModel } from "../models/build.mjs";
|
|
5
|
+
import { ProjectModel } from "../models/project.mjs";
|
|
6
|
+
// src/capture/target.ts
|
|
7
|
+
/** Load a build and its project, throwing when either is gone. */
|
|
8
|
+
async function loadTarget(options, buildId) {
|
|
9
|
+
const build = await new BuildModel(options.db, options.tables).get(buildId);
|
|
10
|
+
if (!build) throw new Error(`Build not found: ${buildId}`);
|
|
11
|
+
const project = await new ProjectModel(options.db, options.tables).get(build.projectId);
|
|
12
|
+
if (!project) throw new Error(`Project not found: ${build.projectId}`);
|
|
13
|
+
return {
|
|
14
|
+
build,
|
|
15
|
+
project
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export { loadTarget };
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=target.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"target.mjs","names":[],"sources":["../../src/capture/target.ts"],"sourcesContent":["import type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport { BuildModel, type BuildTables } from \"../models/build.ts\";\nimport { ProjectModel, type ProjectTables } from \"../models/project.ts\";\nimport type { Build } from \"../schema/build.ts\";\nimport type { Project } from \"../schema/project.ts\";\n\n/** Load a build and its project, throwing when either is gone. */\nexport async function loadTarget(\n options: { db: DatabaseAdapter; tables: BuildTables & ProjectTables },\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":";;;;;;;AAOA,eAAsB,WACpB,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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { RunnerTarget } from "../adapters/capture-target.mjs";
|
|
4
|
+
import { CaptureRunner } from "../adapter/capture-runner.mjs";
|
|
5
|
+
/** Why a `reach: "url"` target is unavailable without a secret (the first words are matched by tests and docs). */
|
|
6
|
+
declare const NO_SECRET_REASON = "no secret: set the server secret, it signs the Storybook URL the remote browser opens";
|
|
7
|
+
/** Why it is unavailable without a confirmed public origin. */
|
|
8
|
+
declare const NO_ORIGIN_REASON = "no public origin: set PUBLIC_BASE_URL, or confirm the detected origin on the System page";
|
|
9
|
+
/** What a target that needs a URL depends on. */
|
|
10
|
+
interface TargetPrerequisites {
|
|
11
|
+
/** The confirmed public origin (ADR 0007, decision 6); absent while none is confirmed. */
|
|
12
|
+
publicOrigin: string | undefined;
|
|
13
|
+
/** Whether the server has a `secret` to sign Storybook URLs with. */
|
|
14
|
+
hasSecret: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The runner's targets with `reach: "url"` ones marked `unavailable` while they lack a `secret` or a
|
|
18
|
+
* public origin (ADR 0007), so a settings save refuses them and a build fails at once with the
|
|
19
|
+
* reason. `undefined` when the runner publishes no list (it offers any target).
|
|
20
|
+
*/
|
|
21
|
+
declare function offeredTargets(runner: Pick<CaptureRunner, "metadata">, prerequisites: TargetPrerequisites): readonly RunnerTarget[] | undefined;
|
|
22
|
+
export { NO_ORIGIN_REASON, NO_SECRET_REASON, TargetPrerequisites, offeredTargets };
|
|
23
|
+
//# sourceMappingURL=targets.d.mts.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
// src/capture/targets.ts
|
|
5
|
+
/** Why a `reach: "url"` target is unavailable without a secret (the first words are matched by tests and docs). */
|
|
6
|
+
const NO_SECRET_REASON = "no secret: set the server secret, it signs the Storybook URL the remote browser opens";
|
|
7
|
+
/** Why it is unavailable without a confirmed public origin. */
|
|
8
|
+
const NO_ORIGIN_REASON = "no public origin: set PUBLIC_BASE_URL, or confirm the detected origin on the System page";
|
|
9
|
+
/**
|
|
10
|
+
* The runner's targets with `reach: "url"` ones marked `unavailable` while they lack a `secret` or a
|
|
11
|
+
* public origin (ADR 0007), so a settings save refuses them and a build fails at once with the
|
|
12
|
+
* reason. `undefined` when the runner publishes no list (it offers any target).
|
|
13
|
+
*/
|
|
14
|
+
function offeredTargets(runner, prerequisites) {
|
|
15
|
+
return runner.metadata.targets?.map((entry) => withPrerequisites(entry, prerequisites));
|
|
16
|
+
}
|
|
17
|
+
function withPrerequisites(entry, prerequisites) {
|
|
18
|
+
if (entry.reach !== "url" || entry.status !== "ready") return entry;
|
|
19
|
+
if (!prerequisites.hasSecret) return {
|
|
20
|
+
...entry,
|
|
21
|
+
status: { unavailable: NO_SECRET_REASON }
|
|
22
|
+
};
|
|
23
|
+
if (prerequisites.publicOrigin === void 0) return {
|
|
24
|
+
...entry,
|
|
25
|
+
status: { unavailable: NO_ORIGIN_REASON }
|
|
26
|
+
};
|
|
27
|
+
return entry;
|
|
28
|
+
}
|
|
29
|
+
export { NO_ORIGIN_REASON, NO_SECRET_REASON, offeredTargets };
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=targets.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"targets.mjs","names":[],"sources":["../../src/capture/targets.ts"],"sourcesContent":["/** What a runner offers right now: its declared targets, minus the ones this deployment cannot reach. */\nimport type { CaptureRunner, RunnerTarget } from \"../adapters/capture-runner.ts\";\n\n/** Why a `reach: \"url\"` target is unavailable without a secret (the first words are matched by tests and docs). */\nexport const NO_SECRET_REASON =\n \"no secret: set the server secret, it signs the Storybook URL the remote browser opens\";\n\n/** Why it is unavailable without a confirmed public origin. */\nexport const NO_ORIGIN_REASON =\n \"no public origin: set PUBLIC_BASE_URL, or confirm the detected origin on the System page\";\n\n/** What a target that needs a URL depends on. */\nexport interface TargetPrerequisites {\n /** The confirmed public origin (ADR 0007, decision 6); absent while none is confirmed. */\n publicOrigin: string | undefined;\n /** Whether the server has a `secret` to sign Storybook URLs with. */\n hasSecret: boolean;\n}\n\n/**\n * The runner's targets with `reach: \"url\"` ones marked `unavailable` while they lack a `secret` or a\n * public origin (ADR 0007), so a settings save refuses them and a build fails at once with the\n * reason. `undefined` when the runner publishes no list (it offers any target).\n */\nexport function offeredTargets(\n runner: Pick<CaptureRunner, \"metadata\">,\n prerequisites: TargetPrerequisites,\n): readonly RunnerTarget[] | undefined {\n return runner.metadata.targets?.map((entry) => withPrerequisites(entry, prerequisites));\n}\n\nfunction withPrerequisites(entry: RunnerTarget, prerequisites: TargetPrerequisites): RunnerTarget {\n if (entry.reach !== \"url\" || entry.status !== \"ready\") {\n return entry;\n }\n if (!prerequisites.hasSecret) {\n return { ...entry, status: { unavailable: NO_SECRET_REASON } };\n }\n if (prerequisites.publicOrigin === undefined) {\n return { ...entry, status: { unavailable: NO_ORIGIN_REASON } };\n }\n return entry;\n}\n"],"mappings":";;;;;AAIA,MAAa,mBACX;;AAGF,MAAa,mBACX;;;;;;AAeF,SAAgB,eACd,QACA,eACqC;CACrC,OAAO,OAAO,SAAS,SAAS,KAAK,UAAU,kBAAkB,OAAO,aAAa,CAAC;AACxF;AAEA,SAAS,kBAAkB,OAAqB,eAAkD;CAChG,IAAI,MAAM,UAAU,SAAS,MAAM,WAAW,SAC5C,OAAO;CAET,IAAI,CAAC,cAAc,WACjB,OAAO;EAAE,GAAG;EAAO,QAAQ,EAAE,aAAa,iBAAiB;CAAE;CAE/D,IAAI,cAAc,iBAAiB,KAAA,GACjC,OAAO;EAAE,GAAG;EAAO,QAAQ,EAAE,aAAa,iBAAiB;CAAE;CAE/D,OAAO;AACT"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import "./adapter.mjs";
|
|
4
|
+
import "../adapter/capture-runner.mjs";
|
|
5
|
+
import "./storybook-urls.mjs";
|
|
6
|
+
import "../models/build.mjs";
|
|
7
|
+
import "../models/capture-attempt.mjs";
|
|
8
|
+
import { Logger } from "pino";
|
|
9
|
+
/** What a build keeps of one `.test()` entry (JSON in `builds.test_results`). */
|
|
10
|
+
interface TestResult {
|
|
11
|
+
/** The test entry's id (`<story id>:<test name slug>`). */
|
|
12
|
+
id: string;
|
|
13
|
+
/** Story file title (the component path), as on its index entry. */
|
|
14
|
+
title: string;
|
|
15
|
+
/** The test's own name, as written in `story.test("...")`. */
|
|
16
|
+
name: string;
|
|
17
|
+
/** The story the test is written under. */
|
|
18
|
+
parentId: string | null;
|
|
19
|
+
status: "passed" | "failed";
|
|
20
|
+
/** The failure message of a failed test. */
|
|
21
|
+
error?: string;
|
|
22
|
+
durationMs: number;
|
|
23
|
+
}
|
|
24
|
+
/** Pass and fail counts of a build's tests. */
|
|
25
|
+
interface TestSummary {
|
|
26
|
+
total: number;
|
|
27
|
+
passed: number;
|
|
28
|
+
failed: number;
|
|
29
|
+
}
|
|
30
|
+
/** The results a build holds; empty for none or an unreadable value. */
|
|
31
|
+
declare function parseTestResults(json: string | null | undefined): TestResult[];
|
|
32
|
+
/** Counts of a build's results. */
|
|
33
|
+
declare function summarizeTests(results: readonly TestResult[]): TestSummary;
|
|
34
|
+
export { TestResult, TestSummary, parseTestResults, summarizeTests };
|
|
35
|
+
//# sourceMappingURL=tests.d.mts.map
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { redactSecrets } from "../utils/redact.mjs";
|
|
5
|
+
import { isDisabledStory } from "./adapter.mjs";
|
|
6
|
+
import { emitAttemptLog } from "../models/capture-attempt.mjs";
|
|
7
|
+
import { classifyFailures } from "./failure-sets.mjs";
|
|
8
|
+
import { renderBudgetMs } from "./render.mjs";
|
|
9
|
+
import { StorybookAdapter } from "./storybook.mjs";
|
|
10
|
+
// src/capture/tests.ts
|
|
11
|
+
/** Longest failure message kept (rows and logs stay bounded). */
|
|
12
|
+
const ERROR_MAX = 1e3;
|
|
13
|
+
/**
|
|
14
|
+
* Run the build's `.test()` entries when the project turned tests on, and store the results on the
|
|
15
|
+
* build. A failing test is a blocking failure of its own id, exactly like a `play` that throws; it
|
|
16
|
+
* takes no screenshot, writes no snapshot and sets no status of its own. Without the setting (or
|
|
17
|
+
* without test entries) nothing runs and nothing is written, except that results left by an
|
|
18
|
+
* earlier run of the same build are cleared.
|
|
19
|
+
*/
|
|
20
|
+
async function runProjectTests(input) {
|
|
21
|
+
const tests = input.project.runTests ? await testsToRun(input) : [];
|
|
22
|
+
const run = input.runner.runTests?.bind(input.runner);
|
|
23
|
+
if (tests.length === 0) return await skipTests(input, tests.length);
|
|
24
|
+
if (!run) return await unsupportedTests(input, tests);
|
|
25
|
+
const results = resultsOf(tests, await execute(input, run, tests));
|
|
26
|
+
await keepResults(input, results);
|
|
27
|
+
return await reportFailures(input, results, tests);
|
|
28
|
+
}
|
|
29
|
+
/** Nothing to run: warn when tests were wanted but the runner cannot, and clear stale results. */
|
|
30
|
+
async function skipTests(input, wanted) {
|
|
31
|
+
if (wanted > 0) input.logger?.warn({ tests: wanted }, "project runs tests but the capture runner cannot run them; skipped");
|
|
32
|
+
await keepResults(input, null);
|
|
33
|
+
return {
|
|
34
|
+
blocking: /* @__PURE__ */ new Set(),
|
|
35
|
+
flaky: /* @__PURE__ */ new Set()
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** Tests were wanted and exist, but the runner cannot run them: fail loudly rather than silently. */
|
|
39
|
+
async function unsupportedTests(input, tests) {
|
|
40
|
+
input.logger?.error({ tests: tests.length }, "project runs tests but the capture runner cannot run them; unsupported capability");
|
|
41
|
+
await emitAttemptLog(input.recordLog, input.logger, "error", "storybook tests unsupported", { tests: tests.length });
|
|
42
|
+
await keepResults(input, null);
|
|
43
|
+
return {
|
|
44
|
+
blocking: new Set(tests.map((test) => test.id)),
|
|
45
|
+
flaky: /* @__PURE__ */ new Set()
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** The test entries to run: those of stories that are not skipped. */
|
|
49
|
+
async function testsToRun(input) {
|
|
50
|
+
const skipped = new Set(input.stories.filter((story) => isDisabledStory(story)).map((s) => s.id));
|
|
51
|
+
return (await new StorybookAdapter().discoverTests(input.storybookDir)).filter((entry) => !isDisabledStory(entry) && !(entry.parentId !== void 0 && skipped.has(entry.parentId)));
|
|
52
|
+
}
|
|
53
|
+
async function execute(input, run, tests) {
|
|
54
|
+
const storybookUrl = await input.storybookUrl?.(input.target, { budgetMs: renderBudgetMs(tests.length) });
|
|
55
|
+
await emitAttemptLog(input.recordLog, input.logger, "info", "running storybook tests", { tests: tests.length });
|
|
56
|
+
return await run({
|
|
57
|
+
buildId: input.build.id,
|
|
58
|
+
storybookDir: input.storybookDir,
|
|
59
|
+
tests,
|
|
60
|
+
playTimeoutMs: input.project.playTimeoutMs,
|
|
61
|
+
target: input.target,
|
|
62
|
+
...storybookUrl === void 0 ? {} : { storybookUrl },
|
|
63
|
+
...input.logger ? { logger: input.logger } : {}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/** One result per test entry; a test the runner reported nothing for counts as failed. */
|
|
67
|
+
function resultsOf(tests, outcomes) {
|
|
68
|
+
const byId = new Map(outcomes.map((outcome) => [outcome.storyId, outcome]));
|
|
69
|
+
return tests.map((test) => {
|
|
70
|
+
const outcome = byId.get(test.id);
|
|
71
|
+
const error = outcome === void 0 ? "the runner reported no result for this test" : outcome.error;
|
|
72
|
+
const failed = outcome === void 0 || outcome.status === "failed";
|
|
73
|
+
return {
|
|
74
|
+
id: test.id,
|
|
75
|
+
title: test.title,
|
|
76
|
+
name: test.name,
|
|
77
|
+
parentId: test.parentId ?? null,
|
|
78
|
+
status: failed ? "failed" : "passed",
|
|
79
|
+
...failed ? { error: redactSecrets(error ?? "test failed").slice(0, ERROR_MAX) } : {},
|
|
80
|
+
durationMs: outcome?.durationMs ?? 0
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/** Write the results; a build that has none and never had any is left untouched. */
|
|
85
|
+
async function keepResults(input, results) {
|
|
86
|
+
if (results === null && !input.build.testResults) return;
|
|
87
|
+
await input.builds.setTestResults(input.build.id, results);
|
|
88
|
+
}
|
|
89
|
+
async function reportFailures(input, results, tests) {
|
|
90
|
+
const failed = results.filter((result) => result.status === "failed");
|
|
91
|
+
const classified = classifyFailures(tests, failed.map((result) => ({
|
|
92
|
+
storyId: result.id,
|
|
93
|
+
viewportName: "test",
|
|
94
|
+
error: result.error ?? ""
|
|
95
|
+
})));
|
|
96
|
+
const sets = {
|
|
97
|
+
blocking: classified.blocking,
|
|
98
|
+
flaky: classified.flaky
|
|
99
|
+
};
|
|
100
|
+
const fields = {
|
|
101
|
+
tests: results.length,
|
|
102
|
+
failed: failed.length,
|
|
103
|
+
flaky: sets.flaky.size
|
|
104
|
+
};
|
|
105
|
+
const level = sets.blocking.size > 0 ? "error" : "info";
|
|
106
|
+
input.logger?.[level](fields, "storybook tests finished");
|
|
107
|
+
await emitAttemptLog(input.recordLog, input.logger, level, "storybook tests finished", fields);
|
|
108
|
+
return sets;
|
|
109
|
+
}
|
|
110
|
+
/** The results a build holds; empty for none or an unreadable value. */
|
|
111
|
+
function parseTestResults(json) {
|
|
112
|
+
if (!json) return [];
|
|
113
|
+
try {
|
|
114
|
+
const parsed = JSON.parse(json);
|
|
115
|
+
return Array.isArray(parsed) ? parsed.filter((item) => isTestResult(item)) : [];
|
|
116
|
+
} catch {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** Counts of a build's results. */
|
|
121
|
+
function summarizeTests(results) {
|
|
122
|
+
const failed = results.filter((result) => result.status === "failed").length;
|
|
123
|
+
return {
|
|
124
|
+
total: results.length,
|
|
125
|
+
passed: results.length - failed,
|
|
126
|
+
failed
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function isTestResult(value) {
|
|
130
|
+
if (typeof value !== "object" || value === null) return false;
|
|
131
|
+
const record = value;
|
|
132
|
+
return typeof record["id"] === "string" && typeof record["name"] === "string" && (record["status"] === "passed" || record["status"] === "failed");
|
|
133
|
+
}
|
|
134
|
+
export { parseTestResults, runProjectTests, summarizeTests };
|
|
135
|
+
|
|
136
|
+
//# sourceMappingURL=tests.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tests.mjs","names":[],"sources":["../../src/capture/tests.ts"],"sourcesContent":["/** Running a project's Storybook 10 `.test()` entries and keeping what they reported (ADR 0004, decision 10). */\nimport type { Logger } from \"pino\";\nimport type { CaptureRunner, CaptureTarget, TestOutcome } from \"../adapters/capture-runner.ts\";\nimport type { BuildModel } from \"../models/build.ts\";\nimport { emitAttemptLog, type AttemptLogRecorder } from \"../models/capture-attempt.ts\";\nimport type { Build } from \"../schema/build.ts\";\nimport type { Project } from \"../schema/project.ts\";\nimport { redactSecrets } from \"../utils/redact.ts\";\nimport { isDisabledStory, type StoryEntry } from \"./adapter.ts\";\nimport { classifyFailures } from \"./failure-sets.ts\";\nimport { renderBudgetMs } from \"./render.ts\";\nimport type { StorybookUrlSource } from \"./storybook-urls.ts\";\nimport { StorybookAdapter } from \"./storybook.ts\";\n\n/** What a build keeps of one `.test()` entry (JSON in `builds.test_results`). */\nexport interface TestResult {\n /** The test entry's id (`<story id>:<test name slug>`). */\n id: string;\n /** Story file title (the component path), as on its index entry. */\n title: string;\n /** The test's own name, as written in `story.test(\"...\")`. */\n name: string;\n /** The story the test is written under. */\n parentId: string | null;\n status: \"passed\" | \"failed\";\n /** The failure message of a failed test. */\n error?: string;\n durationMs: number;\n}\n\n/** Pass and fail counts of a build's tests. */\nexport interface TestSummary {\n total: number;\n passed: number;\n failed: number;\n}\n\n/** Longest failure message kept (rows and logs stay bounded). */\nconst ERROR_MAX = 1000;\n\n/** Test ids by how their failure counts, as {@link runProjectTests} classifies them. */\nexport interface TestRunSets {\n /** Fail the build, like a failing `play` function. */\n blocking: Set<string>;\n /** Tests of a story marked flaky: logged, never blocking. */\n flaky: Set<string>;\n}\n\n/** Inputs for {@link runProjectTests}. */\nexport interface RunTestsInput {\n runner: CaptureRunner;\n builds: BuildModel;\n build: Build;\n project: Project;\n storybookDir: string;\n /** Every discovered story, so the tests of a skipped story are skipped too. */\n stories: readonly StoryEntry[];\n target: CaptureTarget;\n storybookUrl?: StorybookUrlSource | undefined;\n logger: Logger | undefined;\n recordLog?: AttemptLogRecorder | undefined;\n}\n\n/**\n * Run the build's `.test()` entries when the project turned tests on, and store the results on the\n * build. A failing test is a blocking failure of its own id, exactly like a `play` that throws; it\n * takes no screenshot, writes no snapshot and sets no status of its own. Without the setting (or\n * without test entries) nothing runs and nothing is written, except that results left by an\n * earlier run of the same build are cleared.\n */\nexport async function runProjectTests(input: RunTestsInput): Promise<TestRunSets> {\n const tests = input.project.runTests ? await testsToRun(input) : [];\n const run = input.runner.runTests?.bind(input.runner);\n if (tests.length === 0) return await skipTests(input, tests.length);\n if (!run) return await unsupportedTests(input, tests);\n const results = resultsOf(tests, await execute(input, run, tests));\n await keepResults(input, results);\n return await reportFailures(input, results, tests);\n}\n\n/** Nothing to run: warn when tests were wanted but the runner cannot, and clear stale results. */\nasync function skipTests(input: RunTestsInput, wanted: number): Promise<TestRunSets> {\n if (wanted > 0) {\n input.logger?.warn(\n { tests: wanted },\n \"project runs tests but the capture runner cannot run them; skipped\",\n );\n }\n await keepResults(input, null);\n return { blocking: new Set(), flaky: new Set() };\n}\n\n/** Tests were wanted and exist, but the runner cannot run them: fail loudly rather than silently. */\nasync function unsupportedTests(\n input: RunTestsInput,\n tests: readonly StoryEntry[],\n): Promise<TestRunSets> {\n input.logger?.error(\n { tests: tests.length },\n \"project runs tests but the capture runner cannot run them; unsupported capability\",\n );\n await emitAttemptLog(input.recordLog, input.logger, \"error\", \"storybook tests unsupported\", {\n tests: tests.length,\n });\n await keepResults(input, null);\n return { blocking: new Set(tests.map((test) => test.id)), flaky: new Set() };\n}\n\n/** The test entries to run: those of stories that are not skipped. */\nasync function testsToRun(input: RunTestsInput): Promise<StoryEntry[]> {\n const skipped = new Set(input.stories.filter((story) => isDisabledStory(story)).map((s) => s.id));\n const found = await new StorybookAdapter().discoverTests(input.storybookDir);\n return found.filter(\n (entry) =>\n !isDisabledStory(entry) && !(entry.parentId !== undefined && skipped.has(entry.parentId)),\n );\n}\n\nasync function execute(\n input: RunTestsInput,\n run: NonNullable<CaptureRunner[\"runTests\"]>,\n tests: StoryEntry[],\n): Promise<TestOutcome[]> {\n const storybookUrl = await input.storybookUrl?.(input.target, {\n budgetMs: renderBudgetMs(tests.length),\n });\n await emitAttemptLog(input.recordLog, input.logger, \"info\", \"running storybook tests\", {\n tests: tests.length,\n });\n return await run({\n buildId: input.build.id,\n storybookDir: input.storybookDir,\n tests,\n playTimeoutMs: input.project.playTimeoutMs,\n target: input.target,\n ...(storybookUrl === undefined ? {} : { storybookUrl }),\n ...(input.logger ? { logger: input.logger } : {}),\n });\n}\n\n/** One result per test entry; a test the runner reported nothing for counts as failed. */\nfunction resultsOf(tests: readonly StoryEntry[], outcomes: readonly TestOutcome[]): TestResult[] {\n const byId = new Map(outcomes.map((outcome) => [outcome.storyId, outcome]));\n return tests.map((test) => {\n const outcome = byId.get(test.id);\n const error =\n outcome === undefined ? \"the runner reported no result for this test\" : outcome.error;\n const failed = outcome === undefined || outcome.status === \"failed\";\n return {\n id: test.id,\n title: test.title,\n name: test.name,\n parentId: test.parentId ?? null,\n status: failed ? \"failed\" : \"passed\",\n ...(failed ? { error: redactSecrets(error ?? \"test failed\").slice(0, ERROR_MAX) } : {}),\n durationMs: outcome?.durationMs ?? 0,\n };\n });\n}\n\n/** Write the results; a build that has none and never had any is left untouched. */\nasync function keepResults(input: RunTestsInput, results: TestResult[] | null): Promise<void> {\n if (results === null && !input.build.testResults) return;\n await input.builds.setTestResults(input.build.id, results);\n}\n\nasync function reportFailures(\n input: RunTestsInput,\n results: readonly TestResult[],\n tests: readonly StoryEntry[],\n): Promise<TestRunSets> {\n const failed = results.filter((result) => result.status === \"failed\");\n const classified = classifyFailures(\n tests,\n failed.map((result) => ({\n storyId: result.id,\n viewportName: \"test\",\n error: result.error ?? \"\",\n })),\n );\n const sets = { blocking: classified.blocking, flaky: classified.flaky };\n const fields = { tests: results.length, failed: failed.length, flaky: sets.flaky.size };\n const level = sets.blocking.size > 0 ? \"error\" : \"info\";\n input.logger?.[level](fields, \"storybook tests finished\");\n await emitAttemptLog(input.recordLog, input.logger, level, \"storybook tests finished\", fields);\n return sets;\n}\n\n/** The results a build holds; empty for none or an unreadable value. */\nexport function parseTestResults(json: string | null | undefined): TestResult[] {\n if (!json) return [];\n try {\n const parsed: unknown = JSON.parse(json);\n return Array.isArray(parsed) ? parsed.filter((item) => isTestResult(item)) : [];\n } catch {\n return [];\n }\n}\n\n/** Counts of a build's results. */\nexport function summarizeTests(results: readonly TestResult[]): TestSummary {\n const failed = results.filter((result) => result.status === \"failed\").length;\n return { total: results.length, passed: results.length - failed, failed };\n}\n\nfunction isTestResult(value: unknown): value is TestResult {\n if (typeof value !== \"object\" || value === null) return false;\n const record = value as Record<string, unknown>;\n return (\n typeof record[\"id\"] === \"string\" &&\n typeof record[\"name\"] === \"string\" &&\n (record[\"status\"] === \"passed\" || record[\"status\"] === \"failed\")\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { PER_CAPTURE_BUDGET_MS } from "../utils/storybook-token.mjs";
|
|
5
|
+
import { renderBudgetMs, renderWithTimeout } from "./render.mjs";
|
|
6
|
+
// src/capture/token-chunks.ts
|
|
7
|
+
/**
|
|
8
|
+
* The project's signed URL lifetime cannot hold what the pass must capture (a configuration
|
|
9
|
+
* problem): retrying or isolating stories cannot help, so the job fails once with this message.
|
|
10
|
+
*/
|
|
11
|
+
var SignedUrlLifetimeError = class extends Error {
|
|
12
|
+
name = "SignedUrlLifetimeError";
|
|
13
|
+
};
|
|
14
|
+
/** Render the pass, minting a fresh signed URL for each slice; results are concatenated. */
|
|
15
|
+
async function renderInSlices(input) {
|
|
16
|
+
const slices = slicesOf(input);
|
|
17
|
+
const results = [];
|
|
18
|
+
for (const slice of slices) {
|
|
19
|
+
const requestedBudgetMs = renderBudgetMs(slice.expected, input.renderTimeoutMs);
|
|
20
|
+
const ttl = input.storybookUrl?.ttlSeconds?.(input.browser, requestedBudgetMs);
|
|
21
|
+
const budgetMs = ttl === void 0 ? requestedBudgetMs : Math.min(requestedBudgetMs, (ttl - 300) * 1e3);
|
|
22
|
+
const storybookUrl = await input.storybookUrl?.(input.browser, { budgetMs });
|
|
23
|
+
results.push(await renderWithTimeout(input.runner, {
|
|
24
|
+
...input.request,
|
|
25
|
+
stories: slice.stories,
|
|
26
|
+
...storybookUrl === void 0 ? {} : { storybookUrl }
|
|
27
|
+
}, budgetMs));
|
|
28
|
+
}
|
|
29
|
+
return mergeResults(results);
|
|
30
|
+
}
|
|
31
|
+
/** One slice with every story, unless the pass outlasts the lifetime a token can have. */
|
|
32
|
+
function slicesOf(input) {
|
|
33
|
+
const { stories } = input.request;
|
|
34
|
+
const counts = stories.map((story) => input.captureCount(story));
|
|
35
|
+
const expected = counts.reduce((sum, count) => sum + count, 0);
|
|
36
|
+
const whole = [{
|
|
37
|
+
stories,
|
|
38
|
+
expected
|
|
39
|
+
}];
|
|
40
|
+
const ttl = input.storybookUrl?.ttlSeconds?.(input.browser, renderBudgetMs(expected, input.renderTimeoutMs));
|
|
41
|
+
const budgetSeconds = renderBudgetMs(expected, input.renderTimeoutMs) / 1e3;
|
|
42
|
+
if (ttl === void 0 || budgetSeconds + 300 <= ttl) return whole;
|
|
43
|
+
return cut(stories, counts, sliceLimit(ttl));
|
|
44
|
+
}
|
|
45
|
+
function sliceLimit(ttl) {
|
|
46
|
+
const limit = Math.floor((ttl - 300) * 1e3 / PER_CAPTURE_BUDGET_MS);
|
|
47
|
+
if (limit < 1) throw new SignedUrlLifetimeError("Signed Storybook URL lifetime is too short for one capture and its grace period; increase or clear the project lifetime override.");
|
|
48
|
+
return limit;
|
|
49
|
+
}
|
|
50
|
+
function checkedCount(count, limit) {
|
|
51
|
+
if (count > limit) throw new SignedUrlLifetimeError(`A story expands to ${count} captures, exceeding the signed URL slice limit of ${limit}; increase or clear the project lifetime override.`);
|
|
52
|
+
return count;
|
|
53
|
+
}
|
|
54
|
+
function cut(stories, counts, limit) {
|
|
55
|
+
const slices = [];
|
|
56
|
+
let current = {
|
|
57
|
+
stories: [],
|
|
58
|
+
expected: 0
|
|
59
|
+
};
|
|
60
|
+
for (const [index, story] of stories.entries()) {
|
|
61
|
+
const count = checkedCount(counts[index] ?? 0, limit);
|
|
62
|
+
if (current.stories.length > 0 && current.expected + count > limit) {
|
|
63
|
+
slices.push(current);
|
|
64
|
+
current = {
|
|
65
|
+
stories: [],
|
|
66
|
+
expected: 0
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
current.stories.push(story);
|
|
70
|
+
current.expected += count;
|
|
71
|
+
}
|
|
72
|
+
return [...slices, current];
|
|
73
|
+
}
|
|
74
|
+
function mergeResults(results) {
|
|
75
|
+
const [only] = results;
|
|
76
|
+
if (results.length === 1 && only) return only;
|
|
77
|
+
const environment = results.find((result) => result.environment)?.environment;
|
|
78
|
+
return {
|
|
79
|
+
captures: results.flatMap((result) => tagged(result)),
|
|
80
|
+
failures: results.flatMap((result) => result.failures),
|
|
81
|
+
...environment ? { environment } : {}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/** A slice's captures with the environment that slice reported (a reconnect may differ). */
|
|
85
|
+
function tagged(result) {
|
|
86
|
+
for (const capture of result.captures) if (result.environment) capture.environment ??= result.environment;
|
|
87
|
+
return result.captures;
|
|
88
|
+
}
|
|
89
|
+
export { SignedUrlLifetimeError, renderInSlices };
|
|
90
|
+
|
|
91
|
+
//# sourceMappingURL=token-chunks.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-chunks.mjs","names":[],"sources":["../../src/capture/token-chunks.ts"],"sourcesContent":["/**\n * Renders a pass in slices that each fit one signed Storybook URL (ADR 0007, decision 5). A token\n * never lives longer than `MAX_STORYBOOK_URL_TTL` (and a project may set it lower), so a pass whose\n * budget outlasts that is cut into slices of stories and every slice asks the source for a fresh\n * token just before it starts: the render never dies when its token does. A pass that fits one\n * token (the usual case, and every target without a signed URL) is one render call, as before.\n */\nimport type { CaptureTarget, CaptureRunner, RenderResult } from \"../adapters/capture-runner.ts\";\nimport { PER_CAPTURE_BUDGET_MS, STORYBOOK_URL_TTL_GRACE } from \"../utils/storybook-token.ts\";\nimport type { StoryEntry } from \"./adapter.ts\";\nimport { renderBudgetMs, renderWithTimeout } from \"./render.ts\";\nimport type { RenderRequest } from \"./render.ts\";\nimport type { StorybookUrlSource } from \"./storybook-urls.ts\";\n\n/** What cutting a pass into slices needs of its caller. */\nexport interface SliceInput {\n runner: CaptureRunner;\n /** The render call without its `storybookUrl`; its `stories` are the whole pass. */\n request: RenderRequest;\n browser: CaptureTarget;\n storybookUrl: StorybookUrlSource | undefined;\n /** Captures one story expands to (its viewports and modes). */\n captureCount: (story: StoryEntry) => number;\n /** An explicit budget limits each call; a signed URL may require a shorter timeout. */\n renderTimeoutMs: number | undefined;\n}\n\n/**\n * The project's signed URL lifetime cannot hold what the pass must capture (a configuration\n * problem): retrying or isolating stories cannot help, so the job fails once with this message.\n */\nexport class SignedUrlLifetimeError extends Error {\n override readonly name = \"SignedUrlLifetimeError\";\n}\n\n/** Render the pass, minting a fresh signed URL for each slice; results are concatenated. */\nexport async function renderInSlices(input: SliceInput): Promise<RenderResult> {\n const slices = slicesOf(input);\n const results: RenderResult[] = [];\n for (const slice of slices) {\n const requestedBudgetMs = renderBudgetMs(slice.expected, input.renderTimeoutMs);\n const ttl = input.storybookUrl?.ttlSeconds?.(input.browser, requestedBudgetMs);\n const budgetMs =\n ttl === undefined\n ? requestedBudgetMs\n : Math.min(requestedBudgetMs, (ttl - STORYBOOK_URL_TTL_GRACE) * 1000);\n // oxlint-disable-next-line no-await-in-loop -- each slice needs a token minted just before it\n const storybookUrl = await input.storybookUrl?.(input.browser, { budgetMs });\n results.push(\n // oxlint-disable-next-line no-await-in-loop -- slices render one after another\n await renderWithTimeout(\n input.runner,\n {\n ...input.request,\n stories: slice.stories,\n ...(storybookUrl === undefined ? {} : { storybookUrl }),\n },\n budgetMs,\n ),\n );\n }\n return mergeResults(results);\n}\n\ninterface Slice {\n stories: StoryEntry[];\n expected: number;\n}\n\n/** One slice with every story, unless the pass outlasts the lifetime a token can have. */\nfunction slicesOf(input: SliceInput): Slice[] {\n const { stories } = input.request;\n const counts = stories.map((story) => input.captureCount(story));\n const expected = counts.reduce((sum, count) => sum + count, 0);\n const whole: Slice[] = [{ stories, expected }];\n const ttl = input.storybookUrl?.ttlSeconds?.(\n input.browser,\n renderBudgetMs(expected, input.renderTimeoutMs),\n );\n const budgetSeconds = renderBudgetMs(expected, input.renderTimeoutMs) / 1000;\n if (ttl === undefined || budgetSeconds + STORYBOOK_URL_TTL_GRACE <= ttl) {\n return whole;\n }\n return cut(stories, counts, sliceLimit(ttl));\n}\n\nfunction sliceLimit(ttl: number): number {\n const limit = Math.floor(((ttl - STORYBOOK_URL_TTL_GRACE) * 1000) / PER_CAPTURE_BUDGET_MS);\n if (limit < 1) {\n throw new SignedUrlLifetimeError(\n \"Signed Storybook URL lifetime is too short for one capture and its grace period; increase or clear the project lifetime override.\",\n );\n }\n return limit;\n}\n\nfunction checkedCount(count: number, limit: number): number {\n if (count > limit) {\n throw new SignedUrlLifetimeError(\n `A story expands to ${count} captures, exceeding the signed URL slice limit of ${limit}; increase or clear the project lifetime override.`,\n );\n }\n return count;\n}\n\nfunction cut(stories: StoryEntry[], counts: number[], limit: number): Slice[] {\n const slices: Slice[] = [];\n let current: Slice = { stories: [], expected: 0 };\n for (const [index, story] of stories.entries()) {\n const count = checkedCount(counts[index] ?? 0, limit);\n if (current.stories.length > 0 && current.expected + count > limit) {\n slices.push(current);\n current = { stories: [], expected: 0 };\n }\n current.stories.push(story);\n current.expected += count;\n }\n return [...slices, current];\n}\n\nfunction mergeResults(results: RenderResult[]): RenderResult {\n const [only] = results;\n if (results.length === 1 && only) {\n return only;\n }\n const environment = results.find((result) => result.environment)?.environment;\n return {\n captures: results.flatMap((result) => tagged(result)),\n failures: results.flatMap((result) => result.failures),\n ...(environment ? { environment } : {}),\n };\n}\n\n/** A slice's captures with the environment that slice reported (a reconnect may differ). */\nfunction tagged(result: RenderResult): RenderResult[\"captures\"] {\n for (const capture of result.captures) {\n if (result.environment) {\n capture.environment ??= result.environment;\n }\n }\n return result.captures;\n}\n"],"mappings":";;;;;;;;;;AA+BA,IAAa,yBAAb,cAA4C,MAAM;CAChD,OAAyB;AAC3B;;AAGA,eAAsB,eAAe,OAA0C;CAC7E,MAAM,SAAS,SAAS,KAAK;CAC7B,MAAM,UAA0B,CAAC;CACjC,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,oBAAoB,eAAe,MAAM,UAAU,MAAM,eAAe;EAC9E,MAAM,MAAM,MAAM,cAAc,aAAa,MAAM,SAAS,iBAAiB;EAC7E,MAAM,WACJ,QAAQ,KAAA,IACJ,oBACA,KAAK,IAAI,oBAAoB,MAAA,OAAiC,GAAI;EAExE,MAAM,eAAe,MAAM,MAAM,eAAe,MAAM,SAAS,EAAE,SAAS,CAAC;EAC3E,QAAQ,KAEN,MAAM,kBACJ,MAAM,QACN;GACE,GAAG,MAAM;GACT,SAAS,MAAM;GACf,GAAI,iBAAiB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa;EACvD,GACA,QACF,CACF;CACF;CACA,OAAO,aAAa,OAAO;AAC7B;;AAQA,SAAS,SAAS,OAA4B;CAC5C,MAAM,EAAE,YAAY,MAAM;CAC1B,MAAM,SAAS,QAAQ,KAAK,UAAU,MAAM,aAAa,KAAK,CAAC;CAC/D,MAAM,WAAW,OAAO,QAAQ,KAAK,UAAU,MAAM,OAAO,CAAC;CAC7D,MAAM,QAAiB,CAAC;EAAE;EAAS;CAAS,CAAC;CAC7C,MAAM,MAAM,MAAM,cAAc,aAC9B,MAAM,SACN,eAAe,UAAU,MAAM,eAAe,CAChD;CACA,MAAM,gBAAgB,eAAe,UAAU,MAAM,eAAe,IAAI;CACxE,IAAI,QAAQ,KAAA,KAAa,gBAAA,OAA2C,KAClE,OAAO;CAET,OAAO,IAAI,SAAS,QAAQ,WAAW,GAAG,CAAC;AAC7C;AAEA,SAAS,WAAW,KAAqB;CACvC,MAAM,QAAQ,KAAK,OAAQ,MAAA,OAAiC,MAAQ,qBAAqB;CACzF,IAAI,QAAQ,GACV,MAAM,IAAI,uBACR,mIACF;CAEF,OAAO;AACT;AAEA,SAAS,aAAa,OAAe,OAAuB;CAC1D,IAAI,QAAQ,OACV,MAAM,IAAI,uBACR,sBAAsB,MAAM,qDAAqD,MAAM,mDACzF;CAEF,OAAO;AACT;AAEA,SAAS,IAAI,SAAuB,QAAkB,OAAwB;CAC5E,MAAM,SAAkB,CAAC;CACzB,IAAI,UAAiB;EAAE,SAAS,CAAC;EAAG,UAAU;CAAE;CAChD,KAAK,MAAM,CAAC,OAAO,UAAU,QAAQ,QAAQ,GAAG;EAC9C,MAAM,QAAQ,aAAa,OAAO,UAAU,GAAG,KAAK;EACpD,IAAI,QAAQ,QAAQ,SAAS,KAAK,QAAQ,WAAW,QAAQ,OAAO;GAClE,OAAO,KAAK,OAAO;GACnB,UAAU;IAAE,SAAS,CAAC;IAAG,UAAU;GAAE;EACvC;EACA,QAAQ,QAAQ,KAAK,KAAK;EAC1B,QAAQ,YAAY;CACtB;CACA,OAAO,CAAC,GAAG,QAAQ,OAAO;AAC5B;AAEA,SAAS,aAAa,SAAuC;CAC3D,MAAM,CAAC,QAAQ;CACf,IAAI,QAAQ,WAAW,KAAK,MAC1B,OAAO;CAET,MAAM,cAAc,QAAQ,MAAM,WAAW,OAAO,WAAW,CAAC,EAAE;CAClE,OAAO;EACL,UAAU,QAAQ,SAAS,WAAW,OAAO,MAAM,CAAC;EACpD,UAAU,QAAQ,SAAS,WAAW,OAAO,QAAQ;EACrD,GAAI,cAAc,EAAE,YAAY,IAAI,CAAC;CACvC;AACF;;AAGA,SAAS,OAAO,QAAgD;CAC9D,KAAK,MAAM,WAAW,OAAO,UAC3B,IAAI,OAAO,aACT,QAAQ,gBAAgB,OAAO;CAGnC,OAAO,OAAO;AAChB"}
|