@storyshelf/core 0.7.0 → 0.9.0-next-d2cc082
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/dist/adapter/capture-queue.d.mts +0 -2
- package/dist/adapter/capture-runner.d.mts +10 -4
- package/dist/adapter/capture-runner.mjs +7 -2
- package/dist/adapter/database.d.mts +63 -3
- package/dist/adapter/database.mjs +4 -3
- package/dist/adapter/database.mjs.map +1 -1
- package/dist/adapter/design.d.mts +0 -2
- package/dist/adapter/design.mjs +1 -2
- package/dist/adapter/email-sender.d.mts +0 -2
- package/dist/adapter/metadata.d.mts +0 -2
- package/dist/adapter/oauth.d.mts +0 -2
- package/dist/adapter/setup.d.mts +0 -2
- package/dist/adapter/setup.mjs +1 -2
- package/dist/adapter/storage.d.mts +1 -3
- package/dist/adapter/webhook-events.d.mts +0 -2
- package/dist/adapter/webhook-events.mjs +12 -13
- package/dist/adapter/webhook-events.mjs.map +1 -1
- package/dist/adapters/capture-target-offers.d.mts +0 -2
- package/dist/adapters/capture-target-offers.mjs +1 -2
- package/dist/adapters/capture-target.d.mts +0 -2
- package/dist/adapters/capture-target.mjs +1 -2
- package/dist/adapters/notifier/build-comment.d.mts +0 -2
- package/dist/adapters/notifier/build-comment.mjs +1 -2
- package/dist/adapters/notifier/build-state.d.mts +0 -2
- package/dist/adapters/notifier/build-state.mjs +1 -2
- package/dist/adapters/notifier/events.d.mts +0 -2
- package/dist/adapters/notifier/events.mjs +1 -2
- package/dist/adapters/notifier/format.d.mts +0 -2
- package/dist/adapters/notifier/format.mjs +1 -2
- package/dist/adapters/notifier/provider.d.mts +0 -2
- package/dist/adapters/notifier/provider.mjs +1 -2
- package/dist/adapters/notifier/state.d.mts +0 -2
- package/dist/adapters/notifier/state.mjs +1 -2
- package/dist/adapters/notifier/types.d.mts +0 -2
- package/dist/adapters/remote-browser.d.mts +120 -31
- package/dist/adapters/remote-browser.mjs +134 -34
- package/dist/adapters/remote-browser.mjs.map +1 -1
- 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/remote-sizing.d.mts +44 -0
- package/dist/adapters/remote-sizing.mjs +53 -0
- package/dist/adapters/remote-sizing.mjs.map +1 -0
- package/dist/adapters/render-runs.d.mts +0 -2
- package/dist/adapters/render-runs.mjs +1 -2
- package/dist/adapters/setup-runner.d.mts +0 -2
- package/dist/adapters/setup-runner.mjs +1 -2
- package/dist/adapters/sources.d.mts +0 -2
- package/dist/adapters/sources.mjs +1 -2
- package/dist/adapters/validate.d.mts +0 -2
- package/dist/adapters/validate.mjs +2 -2
- package/dist/adapters/validate.mjs.map +1 -1
- package/dist/ai.d.mts +0 -2
- package/dist/ai.mjs +1 -2
- 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 +0 -2
- package/dist/capture/a11y/axe-source.mjs +1 -2
- package/dist/capture/a11y/in-page.d.mts +1 -3
- package/dist/capture/a11y/in-page.mjs +3 -4
- package/dist/capture/a11y/in-page.mjs.map +1 -1
- package/dist/capture/a11y/map.d.mts +0 -2
- package/dist/capture/a11y/map.mjs +1 -2
- package/dist/capture/a11y/options.d.mts +0 -2
- package/dist/capture/a11y/options.mjs +1 -2
- package/dist/capture/adapter.d.mts +5 -2
- package/dist/capture/adapter.mjs +1 -2
- package/dist/capture/adapter.mjs.map +1 -1
- package/dist/capture/affected.d.mts +0 -2
- package/dist/capture/affected.mjs +1 -2
- package/dist/capture/attempt-logger.mjs +1 -2
- package/dist/capture/browsers.d.mts +0 -2
- package/dist/capture/browsers.mjs +1 -2
- package/dist/capture/build-state/deliver.d.mts +0 -2
- package/dist/capture/build-state/deliver.mjs +1 -2
- package/dist/capture/build-state/notify-job.d.mts +0 -2
- package/dist/capture/build-state/notify-job.mjs +1 -2
- package/dist/capture/build-state/publisher.d.mts +0 -2
- package/dist/capture/build-state/publisher.mjs +1 -2
- package/dist/capture/build-state/resume.d.mts +0 -2
- package/dist/capture/build-state/resume.mjs +1 -2
- package/dist/capture/build-state/types.d.mts +1 -3
- package/dist/capture/catalog/catalogue.d.mts +0 -2
- package/dist/capture/catalog/catalogue.mjs +1 -2
- package/dist/capture/catalog/components.d.mts +0 -2
- package/dist/capture/catalog/components.mjs +1 -2
- package/dist/capture/catalog/history.d.mts +0 -2
- package/dist/capture/catalog/history.mjs +1 -2
- package/dist/capture/catalog/hook.d.mts +0 -2
- package/dist/capture/catalog/hook.mjs +1 -2
- package/dist/capture/catalog/index.d.mts +2 -2
- package/dist/capture/catalog/load.d.mts +0 -2
- package/dist/capture/catalog/load.mjs +1 -2
- package/dist/capture/catalog/parse.d.mts +0 -2
- package/dist/capture/catalog/parse.mjs +1 -2
- package/dist/capture/catalog/read.d.mts +6 -3
- package/dist/capture/catalog/read.mjs +14 -8
- package/dist/capture/catalog/read.mjs.map +1 -1
- package/dist/capture/catalog/types.d.mts +0 -2
- 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 +1 -3
- package/dist/capture/consumer/capture-handler.mjs +1 -2
- package/dist/capture/consumer/idle.d.mts +0 -2
- package/dist/capture/consumer/idle.mjs +1 -2
- package/dist/capture/consumer/index.d.mts +0 -2
- package/dist/capture/consumer/index.mjs +1 -2
- package/dist/capture/consumer/loop.mjs +1 -2
- package/dist/capture/consumer/metrics.d.mts +0 -2
- package/dist/capture/consumer/metrics.mjs +1 -2
- package/dist/capture/consumer/retry.d.mts +0 -2
- package/dist/capture/consumer/retry.mjs +1 -2
- package/dist/capture/consumer/run-job.mjs +1 -2
- package/dist/capture/consumer/startup.mjs +1 -2
- package/dist/capture/consumer/types.d.mts +0 -2
- package/dist/capture/crash-failure.d.mts +0 -2
- package/dist/capture/crash-failure.mjs +1 -2
- package/dist/capture/db-queue/cadence.d.mts +0 -2
- package/dist/capture/db-queue/cadence.mjs +1 -2
- package/dist/capture/db-queue/claim.d.mts +0 -2
- package/dist/capture/db-queue/claim.mjs +20 -4
- package/dist/capture/db-queue/claim.mjs.map +1 -1
- package/dist/capture/db-queue/clock.mjs +1 -2
- package/dist/capture/db-queue/codec.mjs +1 -2
- package/dist/capture/db-queue/index.d.mts +0 -2
- package/dist/capture/db-queue/index.mjs +3 -3
- package/dist/capture/db-queue/index.mjs.map +1 -1
- package/dist/capture/db-queue/keeper.d.mts +0 -2
- package/dist/capture/db-queue/keeper.mjs +1 -2
- package/dist/capture/db-queue/lease.mjs +1 -2
- package/dist/capture/db-queue/lifecycle.mjs +1 -2
- package/dist/capture/db-queue/operations.mjs +1 -2
- package/dist/capture/db-queue/poll-loop.d.mts +0 -2
- package/dist/capture/db-queue/poll-loop.mjs +1 -2
- package/dist/capture/db-queue/poll.d.mts +0 -2
- package/dist/capture/db-queue/poll.mjs +1 -2
- package/dist/capture/db-queue/timers.d.mts +0 -2
- package/dist/capture/db-queue/timers.mjs +1 -2
- package/dist/capture/db-queue/types.d.mts +0 -2
- package/dist/capture/db-queue/types.mjs +1 -2
- package/dist/capture/db-queue/types.mjs.map +1 -1
- package/dist/capture/db-queue/wake.mjs +1 -2
- package/dist/capture/design.mjs +1 -2
- package/dist/capture/diff-record.mjs +1 -2
- package/dist/capture/dispatch.d.mts +1 -3
- package/dist/capture/dispatch.mjs +17 -5
- package/dist/capture/dispatch.mjs.map +1 -1
- package/dist/capture/enrich.mjs +1 -2
- package/dist/capture/failure-sets.mjs +1 -2
- package/dist/capture/fingerprint.d.mts +19 -8
- package/dist/capture/fingerprint.mjs +43 -14
- package/dist/capture/fingerprint.mjs.map +1 -1
- package/dist/capture/forked/channel.d.mts +0 -2
- package/dist/capture/forked/child.d.mts +0 -2
- package/dist/capture/forked/child.mjs +1 -2
- package/dist/capture/forked/crash-tracker.mjs +1 -2
- package/dist/capture/forked/decision.d.mts +0 -2
- package/dist/capture/forked/decision.mjs +1 -2
- package/dist/capture/forked/exit.mjs +1 -2
- package/dist/capture/forked/fork.d.mts +0 -2
- package/dist/capture/forked/fork.mjs +1 -2
- package/dist/capture/forked/forked-capture.d.mts +0 -2
- package/dist/capture/forked/forked-capture.mjs +1 -2
- package/dist/capture/forked/lease-book.mjs +1 -2
- package/dist/capture/forked/protocol.d.mts +0 -2
- package/dist/capture/forked/protocol.mjs +1 -2
- package/dist/capture/forked/runs.mjs +1 -2
- 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 +1 -2
- package/dist/capture/job-lease.d.mts +0 -2
- package/dist/capture/job-lease.mjs +1 -2
- package/dist/capture/matrix.mjs +4 -4
- package/dist/capture/matrix.mjs.map +1 -1
- package/dist/capture/modes.d.mts +0 -2
- package/dist/capture/modes.mjs +2 -3
- package/dist/capture/modes.mjs.map +1 -1
- package/dist/capture/orchestrator.d.mts +0 -2
- package/dist/capture/orchestrator.mjs +2 -3
- package/dist/capture/pipeline.d.mts +0 -2
- package/dist/capture/pipeline.mjs +44 -10
- package/dist/capture/pipeline.mjs.map +1 -1
- package/dist/capture/progress.d.mts +0 -2
- package/dist/capture/progress.mjs +1 -2
- package/dist/capture/publish/accept.d.mts +0 -2
- package/dist/capture/publish/accept.mjs +1 -2
- package/dist/capture/publish/complete.d.mts +1 -3
- package/dist/capture/publish/complete.mjs +1 -2
- package/dist/capture/publish/extract.d.mts +0 -2
- package/dist/capture/publish/extract.mjs +6 -5
- package/dist/capture/publish/extract.mjs.map +1 -1
- package/dist/capture/publish/handler.d.mts +1 -3
- package/dist/capture/publish/handler.mjs +1 -2
- package/dist/capture/publish/index.d.mts +2 -1
- 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 +0 -2
- package/dist/capture/publish/limits.mjs +1 -2
- package/dist/capture/publish/published.d.mts +0 -2
- package/dist/capture/publish/published.mjs +1 -2
- package/dist/capture/publish/run.d.mts +1 -3
- package/dist/capture/publish/run.mjs +1 -2
- package/dist/capture/rediff.d.mts +1 -3
- package/dist/capture/rediff.mjs +19 -6
- package/dist/capture/rediff.mjs.map +1 -1
- package/dist/capture/render.mjs +1 -2
- package/dist/capture/render.mjs.map +1 -1
- package/dist/capture/scratch.d.mts +0 -2
- package/dist/capture/scratch.mjs +2 -4
- package/dist/capture/scratch.mjs.map +1 -1
- package/dist/capture/shard-plan.mjs +1 -2
- package/dist/capture/shards/context.mjs +3 -3
- package/dist/capture/shards/context.mjs.map +1 -1
- package/dist/capture/shards/execute.mjs +4 -3
- package/dist/capture/shards/execute.mjs.map +1 -1
- package/dist/capture/shards/fan-out.mjs +1 -2
- package/dist/capture/shards/history.mjs +1 -2
- package/dist/capture/shards/reduce.mjs +1 -2
- package/dist/capture/sizing.d.mts +0 -2
- package/dist/capture/sizing.mjs +1 -2
- package/dist/capture/skip-checks.mjs +1 -2
- package/dist/capture/statics.d.mts +0 -2
- package/dist/capture/statics.mjs +2 -4
- package/dist/capture/statics.mjs.map +1 -1
- package/dist/capture/storybook-urls.d.mts +0 -2
- package/dist/capture/storybook-urls.mjs +3 -3
- package/dist/capture/storybook-urls.mjs.map +1 -1
- package/dist/capture/storybook.d.mts +0 -2
- package/dist/capture/storybook.mjs +8 -4
- package/dist/capture/storybook.mjs.map +1 -1
- package/dist/capture/target.mjs +1 -2
- package/dist/capture/targets.d.mts +0 -2
- package/dist/capture/targets.mjs +1 -2
- package/dist/capture/tests.d.mts +0 -2
- package/dist/capture/tests.mjs +13 -3
- package/dist/capture/tests.mjs.map +1 -1
- package/dist/capture/token-chunks.mjs +24 -7
- package/dist/capture/token-chunks.mjs.map +1 -1
- package/dist/capture.d.mts +5 -2
- package/dist/capture.mjs +5 -2
- package/dist/config.d.mts +16 -3
- package/dist/config.mjs +6 -3
- package/dist/config.mjs.map +1 -1
- package/dist/diff.d.mts +0 -2
- package/dist/diff.mjs +1 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/insights/budget.d.mts +0 -2
- package/dist/insights/budget.mjs +1 -2
- package/dist/insights/envelope.d.mts +0 -2
- package/dist/insights/envelope.mjs +1 -2
- package/dist/insights/evidence-health.d.mts +0 -2
- package/dist/insights/evidence-health.mjs +1 -2
- package/dist/insights/evidence.d.mts +0 -2
- package/dist/insights/evidence.mjs +1 -2
- package/dist/insights/hash.d.mts +0 -2
- package/dist/insights/hash.mjs +1 -2
- package/dist/insights/limits.d.mts +0 -2
- package/dist/insights/limits.mjs +1 -2
- package/dist/insights/prompts/v1.d.mts +0 -2
- package/dist/insights/prompts/v1.mjs +1 -2
- package/dist/insights/redact.d.mts +0 -2
- package/dist/insights/redact.mjs +1 -2
- package/dist/insights/text.mjs +1 -2
- package/dist/logger.d.mts +0 -2
- package/dist/logger.mjs +1 -2
- package/dist/models/ai-budget-alert.d.mts +0 -2
- package/dist/models/ai-budget-alert.mjs +1 -2
- package/dist/models/ai-usage.d.mts +0 -2
- package/dist/models/ai-usage.mjs +1 -2
- 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 +0 -2
- package/dist/models/baseline-archive.mjs +1 -2
- 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 +0 -2
- package/dist/models/baseline-export.mjs +1 -2
- package/dist/models/baseline-import.mjs +1 -2
- 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 +1 -2
- package/dist/models/baseline-transfer.mjs +30 -2
- package/dist/models/baseline-transfer.mjs.map +1 -1
- package/dist/models/baseline.d.mts +5 -2
- package/dist/models/baseline.mjs +91 -12
- package/dist/models/baseline.mjs.map +1 -1
- package/dist/models/build-component.d.mts +0 -2
- package/dist/models/build-component.mjs +1 -2
- package/dist/models/build-review-status.d.mts +0 -2
- package/dist/models/build-review-status.mjs +1 -2
- package/dist/models/build-review.d.mts +0 -2
- package/dist/models/build-review.mjs +1 -2
- package/dist/models/build.d.mts +1 -3
- package/dist/models/build.mjs +14 -15
- package/dist/models/build.mjs.map +1 -1
- package/dist/models/capture-attempt.d.mts +0 -2
- package/dist/models/capture-attempt.mjs +1 -2
- package/dist/models/capture-job.d.mts +0 -2
- package/dist/models/capture-job.mjs +1 -2
- package/dist/models/capture-log.d.mts +0 -2
- package/dist/models/capture-log.mjs +1 -2
- package/dist/models/capture-shard.d.mts +0 -2
- package/dist/models/capture-shard.mjs +1 -2
- package/dist/models/changeset.d.mts +0 -2
- package/dist/models/changeset.mjs +1 -2
- package/dist/models/columns.mjs +1 -2
- package/dist/models/comment.d.mts +0 -2
- package/dist/models/comment.mjs +1 -2
- package/dist/models/component.d.mts +0 -2
- package/dist/models/component.mjs +1 -2
- package/dist/models/connections.d.mts +0 -2
- package/dist/models/connections.mjs +1 -2
- package/dist/models/content-ref.d.mts +0 -2
- package/dist/models/content-ref.mjs +1 -2
- package/dist/models/credentials.d.mts +0 -2
- package/dist/models/credentials.mjs +1 -2
- package/dist/models/design-credentials.d.mts +0 -2
- package/dist/models/design-credentials.mjs +1 -2
- package/dist/models/design-health.d.mts +16 -3
- package/dist/models/design-health.mjs +21 -3
- package/dist/models/design-health.mjs.map +1 -1
- package/dist/models/design-nodes.d.mts +11 -4
- package/dist/models/design-nodes.mjs +18 -3
- package/dist/models/design-nodes.mjs.map +1 -1
- package/dist/models/design-renders.mjs +1 -2
- package/dist/models/design-resolve.d.mts +0 -2
- package/dist/models/design-resolve.mjs +1 -2
- package/dist/models/design.d.mts +0 -2
- package/dist/models/design.mjs +1 -2
- package/dist/models/inherited-ratio.d.mts +0 -2
- package/dist/models/inherited-ratio.mjs +1 -2
- package/dist/models/insight.d.mts +0 -2
- package/dist/models/insight.mjs +1 -2
- package/dist/models/label.d.mts +0 -2
- package/dist/models/label.mjs +1 -2
- package/dist/models/member.d.mts +0 -2
- package/dist/models/member.mjs +1 -2
- package/dist/models/notification-channel.d.mts +0 -2
- package/dist/models/notification-channel.mjs +1 -2
- package/dist/models/notification-subscription.d.mts +0 -2
- package/dist/models/notification-subscription.mjs +1 -2
- package/dist/models/project-group-mapping.d.mts +0 -2
- package/dist/models/project-group-mapping.mjs +1 -2
- package/dist/models/project.d.mts +0 -2
- package/dist/models/project.mjs +1 -2
- package/dist/models/public-origin.d.mts +0 -2
- package/dist/models/public-origin.mjs +1 -2
- package/dist/models/review-switch-off.d.mts +0 -2
- package/dist/models/review-switch-off.mjs +1 -2
- package/dist/models/site-settings.d.mts +0 -2
- package/dist/models/site-settings.mjs +1 -2
- package/dist/models/snapshot.d.mts +3 -2
- package/dist/models/snapshot.mjs +4 -2
- package/dist/models/snapshot.mjs.map +1 -1
- package/dist/models/token.d.mts +0 -2
- package/dist/models/token.mjs +1 -2
- package/dist/models/usage.d.mts +0 -2
- package/dist/models/usage.mjs +1 -2
- package/dist/models/user.d.mts +0 -2
- package/dist/models/user.mjs +1 -2
- package/dist/models/webhook.d.mts +0 -2
- package/dist/models/webhook.mjs +1 -2
- package/dist/models.d.mts +6 -3
- package/dist/models.mjs +6 -3
- package/dist/options.d.mts +0 -2
- package/dist/options.mjs +2 -4
- package/dist/options.mjs.map +1 -1
- package/dist/paths.d.mts +3 -3
- package/dist/paths.mjs +7 -3
- package/dist/paths.mjs.map +1 -1
- package/dist/retention/ai.d.mts +1 -3
- package/dist/retention/ai.mjs +0 -1
- package/dist/retention/pending.mjs +1 -2
- 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 +0 -2
- package/dist/retention/screenshot-sweep.mjs +1 -2
- package/dist/retention.d.mts +0 -2
- package/dist/retention.mjs +5 -2
- package/dist/retention.mjs.map +1 -1
- package/dist/schema/baseline.d.mts +2 -2
- package/dist/schema/build-component.d.mts +0 -2
- package/dist/schema/build-review.d.mts +0 -2
- package/dist/schema/build.d.mts +0 -2
- package/dist/schema/capture-attempt.d.mts +0 -2
- package/dist/schema/capture-job.d.mts +0 -2
- package/dist/schema/capture-shard.d.mts +0 -2
- package/dist/schema/comment.d.mts +0 -2
- package/dist/schema/connection.d.mts +0 -2
- package/dist/schema/content-ref.d.mts +0 -2
- package/dist/schema/design.d.mts +2 -2
- package/dist/schema/insight.d.mts +0 -2
- package/dist/schema/label.d.mts +0 -2
- package/dist/schema/member.d.mts +0 -2
- package/dist/schema/notification.d.mts +0 -2
- package/dist/schema/project-group-mapping.d.mts +0 -2
- package/dist/schema/project.d.mts +0 -2
- package/dist/schema/site-setting.d.mts +0 -2
- package/dist/schema/snapshot.d.mts +5 -2
- package/dist/schema/token.d.mts +0 -2
- package/dist/schema/usage.d.mts +0 -2
- package/dist/schema/user.d.mts +0 -2
- package/dist/schema/webhook.d.mts +0 -2
- package/dist/schema.d.mts +4 -6
- package/dist/serverless.d.mts +0 -2
- package/dist/serverless.mjs +1 -2
- 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 +0 -2
- 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 +0 -2
- package/dist/test-helpers/deferred.mjs +1 -2
- package/dist/test-helpers/design-contract.d.mts +0 -2
- package/dist/test-helpers/design-contract.mjs +1 -2
- 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 +7 -2
- package/dist/test-helpers/fake-database.mjs.map +1 -1
- package/dist/test-helpers/fake-design.d.mts +0 -2
- package/dist/test-helpers/fake-design.mjs +1 -2
- package/dist/test-helpers/fake-notifier.d.mts +0 -2
- package/dist/test-helpers/fake-notifier.mjs +1 -2
- 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 +6 -2
- package/dist/test-helpers/fake-tables.mjs.map +1 -1
- package/dist/test-helpers/manual-clock.d.mts +0 -2
- package/dist/test-helpers/manual-clock.mjs +1 -2
- package/dist/test-helpers/notifier-contract.d.mts +0 -2
- package/dist/test-helpers/notifier-contract.mjs +1 -2
- package/dist/test-helpers/queue-contract.d.mts +0 -2
- package/dist/test-helpers/queue-contract.mjs +1 -2
- package/dist/test-helpers/queue-multi-instance.d.mts +0 -2
- package/dist/test-helpers/queue-multi-instance.mjs +1 -2
- package/dist/test-helpers/runner-contract.d.mts +0 -2
- package/dist/test-helpers/runner-contract.mjs +1 -2
- package/dist/test-helpers/sql-chunks.mjs +2 -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 +1 -2
- package/dist/test-helpers.d.mts +2 -1
- package/dist/test-helpers.mjs +2 -1
- package/dist/tracing.mjs +36 -6
- package/dist/tracing.mjs.map +1 -1
- package/dist/types.d.mts +0 -2
- package/dist/types.mjs +1 -2
- package/dist/urls.d.mts +0 -2
- package/dist/urls.mjs +1 -2
- package/dist/utils/adaptive-concurrency.d.mts +0 -2
- package/dist/utils/adaptive-concurrency.mjs +1 -2
- package/dist/utils/connect-retry.d.mts +2 -2
- package/dist/utils/connect-retry.mjs +2 -3
- package/dist/utils/connect-retry.mjs.map +1 -1
- package/dist/utils/encrypt.d.mts +0 -2
- package/dist/utils/encrypt.mjs +1 -2
- package/dist/utils/environment.d.mts +0 -2
- package/dist/utils/environment.mjs +1 -2
- package/dist/utils/hash.d.mts +0 -2
- package/dist/utils/hash.mjs +1 -2
- package/dist/utils/http.d.mts +13 -3
- package/dist/utils/http.mjs +79 -17
- package/dist/utils/http.mjs.map +1 -1
- package/dist/utils/map-adaptive.d.mts +0 -2
- package/dist/utils/map-adaptive.mjs +1 -2
- package/dist/utils/map-limit.d.mts +0 -2
- package/dist/utils/map-limit.mjs +1 -2
- package/dist/utils/mime.d.mts +0 -2
- package/dist/utils/mime.mjs +1 -2
- package/dist/utils/page-limiter.d.mts +0 -2
- package/dist/utils/page-limiter.mjs +1 -2
- 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 +0 -2
- package/dist/utils/public-origin.mjs +1 -2
- package/dist/utils/redact.d.mts +0 -2
- package/dist/utils/redact.mjs +1 -2
- package/dist/utils/remote-environment.d.mts +40 -3
- package/dist/utils/remote-environment.mjs +52 -3
- package/dist/utils/remote-environment.mjs.map +1 -1
- package/dist/utils/resources.d.mts +0 -2
- package/dist/utils/resources.mjs +1 -2
- package/dist/utils/screenshot-store.d.mts +0 -2
- package/dist/utils/screenshot-store.mjs +1 -2
- package/dist/utils/session-gate.d.mts +7 -6
- package/dist/utils/session-gate.mjs +28 -12
- package/dist/utils/session-gate.mjs.map +1 -1
- package/dist/utils/storybook-token.d.mts +11 -3
- package/dist/utils/storybook-token.mjs +10 -9
- package/dist/utils/storybook-token.mjs.map +1 -1
- package/dist/utils/storybook-url.d.mts +0 -2
- package/dist/utils/storybook-url.mjs +1 -2
- package/dist/utils/timing.d.mts +0 -2
- package/dist/utils/timing.mjs +1 -2
- package/dist/utils/ulid.d.mts +0 -2
- package/dist/utils/ulid.mjs +1 -2
- package/dist/utils/zip-read.mjs +1 -2
- package/dist/utils/zip-write.d.mts +0 -2
- package/dist/utils/zip-write.mjs +1 -2
- package/dist/utils.d.mts +5 -5
- package/dist/utils.mjs +5 -5
- package/package.json +6 -15
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"statics.mjs","names":[],"sources":["../../src/capture/statics.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { mkdir, readdir, readFile, rm } from \"node:fs/promises\";\nimport { join, relative, resolve } from \"node:path\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { StorageAdapter } from \"../adapters/storage.ts\";\nimport { mapLimit } from \"../utils/map-limit.ts\";\nimport { storybookDir, storybookZipPath } from \"../utils/paths.ts\";\nimport { extractZipStream } from \"./publish/extract.ts\";\nimport {\n DEFAULT_MAX_UNZIPPED_BYTES,\n DEFAULT_MAX_ZIP_ENTRIES,\n type ExtractionLimits,\n} from \"./publish/limits.ts\";\nimport { jobScratchDir, shardScratchDir } from \"./scratch.ts\";\n\n/**\n * Shared Storybook statics handling: unzip a stored build into a scratch\n * directory and persist the extracted files back to storage. Used by the\n * capture orchestrator and by the upload path for opt-in inline extraction\n * (`maxInlineUnzipSize`); both write identical keys, so either may run first.\n */\n\nconst DEFAULT_EXTRACTION_LIMITS: ExtractionLimits = {\n maxEntries: DEFAULT_MAX_ZIP_ENTRIES,\n maxBytes: DEFAULT_MAX_UNZIPPED_BYTES,\n};\n\n/**\n * Extract a build's stored zip into a dedicated scratch directory. The caller removes it when\n * done (`removeJobScratch`).\n *\n * @param storage - Storage holding the uploaded zip.\n * @param scratchDir - Root under which per-build scratch dirs are created.\n * @param projectId - Owning project id.\n * @param buildId - Build whose zip to extract.\n * @param shardId - Shard extracting it; gives the shard a directory of its own.\n * @param limits - Entry and uncompressed-byte caps (safe defaults when omitted).\n * @returns The extracted Storybook directory.\n * @throws {ZipLimitError} When the archive breaks a cap or escapes the target directory.\n */\nexport async function extractStorybookToScratch(\n storage: StorageAdapter,\n scratchDir: string,\n projectId: string,\n buildId: string,\n shardId?: string,\n limits: ExtractionLimits = DEFAULT_EXTRACTION_LIMITS,\n): Promise<string> {\n const base = shardId\n ? shardScratchDir(scratchDir, projectId, buildId, shardId)\n : jobScratchDir(scratchDir, projectId, buildId);\n const targetDir = join(base, \"storybook\");\n const root = await prepareTargetDir(targetDir);\n const source = await storage.readStream(storybookZipPath(projectId, buildId));\n await extractZipStream(source, root, limits);\n return targetDir;\n}\n\n/** Reset a scratch target directory, returning its resolved root. */\nasync function prepareTargetDir(targetDir: string): Promise<string> {\n await rm(targetDir, { recursive: true, force: true });\n await mkdir(targetDir, { recursive: true });\n return resolve(targetDir);\n}\n\n/**\n * Persist an extracted Storybook directory to storage for published serving.\n *\n * Uses content-addressed deduplication: each file is hashed and stored once\n * under `content/<hash>`, with a per-build manifest at\n * `${projectId}/builds/${buildId}/storybook/manifest.json`.\n *\n * @param storage - Destination storage adapter.\n * @param sourceDir - Extracted Storybook directory.\n * @param projectId - Owning project id.\n * @param buildId - Build the files belong to.\n * @param db - Optional database for content ref tracking (enables GC).\n */\nexport async function persistStorybookStatics(\n storage: StorageAdapter,\n sourceDir: string,\n projectId: string,\n buildId: string,\n db?: DatabaseAdapter,\n): Promise<void> {\n const root = resolve(sourceDir);\n const destinationPrefix = storybookDir(projectId, buildId);\n const files = await walkFiles(root);\n const manifest: Record<string, string> = {};\n // Bounded: a large Storybook has thousands of files, and buffering them all\n // at once exhausts memory on small hosts. Each file is read fully before any\n // write: the scratch dir is often the storage root, so the per-build path\n // below can be the very file being read (a streamed write would truncate it).\n await mapLimit(files, STATICS_CONCURRENCY, async (file) => {\n const rel = relative(root, file);\n const buffer = await readFile(file);\n const hash = createHash(\"sha256\").update(buffer).digest(\"hex\");\n manifest[rel] = hash;\n // Only write if not already exists (dedup)\n if (!(await storage.exists(`content/${hash}`))) {\n await storage.write(`content/${hash}`, buffer);\n }\n // Also write to legacy per-build path for backward compat (can be removed after migration)\n // and maintain content_refs for GC\n await storage.write(`${destinationPrefix}/${rel}`, buffer);\n if (db) {\n await upsertContentRef(db, hash);\n }\n });\n await storage.write(`${destinationPrefix}/manifest.json`, Buffer.from(JSON.stringify(manifest)));\n}\n\n/** Files persisted at once; bounds how many file buffers are in memory together. */\nconst STATICS_CONCURRENCY = 8;\n\nasync function upsertContentRef(db: DatabaseAdapter, hash: string): Promise<void> {\n try {\n const contentRefs = db.tables.contentRefs;\n const now = new Date().toISOString();\n const existing = (await db.get(contentRefs, hash)) as unknown as { refCount: number } | null;\n if (existing) {\n await db.update(contentRefs, hash, {\n refCount: existing.refCount + 1,\n lastSeenAt: now,\n });\n } else {\n await db.insert(contentRefs, { hash, refCount: 1, lastSeenAt: now, createdAt: now });\n }\n } catch {\n // content_refs table may not exist yet (old DB) — ignore\n }\n}\n\nasync function walkFiles(dir: string): Promise<string[]> {\n const entries = await readdir(dir, { withFileTypes: true });\n const nested = await Promise.all(\n entries.map(async (entry) => {\n const full = join(dir, entry.name);\n if (entry.isDirectory()) {\n return await walkFiles(full);\n }\n return [full];\n }),\n );\n return nested.flat();\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"statics.mjs","names":[],"sources":["../../src/capture/statics.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { mkdir, readdir, readFile, rm } from \"node:fs/promises\";\nimport { join, relative, resolve } from \"node:path\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { StorageAdapter } from \"../adapters/storage.ts\";\nimport { mapLimit } from \"../utils/map-limit.ts\";\nimport { storybookDir, storybookZipPath } from \"../utils/paths.ts\";\nimport { extractZipStream } from \"./publish/extract.ts\";\nimport {\n DEFAULT_MAX_UNZIPPED_BYTES,\n DEFAULT_MAX_ZIP_ENTRIES,\n type ExtractionLimits,\n} from \"./publish/limits.ts\";\nimport { jobScratchDir, shardScratchDir } from \"./scratch.ts\";\n\n/**\n * Shared Storybook statics handling: unzip a stored build into a scratch\n * directory and persist the extracted files back to storage. Used by the\n * capture orchestrator and by the upload path for opt-in inline extraction\n * (`maxInlineUnzipSize`); both write identical keys, so either may run first.\n */\n\nconst DEFAULT_EXTRACTION_LIMITS: ExtractionLimits = {\n maxEntries: DEFAULT_MAX_ZIP_ENTRIES,\n maxBytes: DEFAULT_MAX_UNZIPPED_BYTES,\n};\n\n/**\n * Extract a build's stored zip into a dedicated scratch directory. The caller removes it when\n * done (`removeJobScratch`).\n *\n * @param storage - Storage holding the uploaded zip.\n * @param scratchDir - Root under which per-build scratch dirs are created.\n * @param projectId - Owning project id.\n * @param buildId - Build whose zip to extract.\n * @param shardId - Shard extracting it; gives the shard a directory of its own.\n * @param limits - Entry and uncompressed-byte caps (safe defaults when omitted).\n * @returns The extracted Storybook directory.\n * @throws {ZipLimitError} When the archive breaks a cap or escapes the target directory.\n */\nexport async function extractStorybookToScratch(\n storage: StorageAdapter,\n scratchDir: string,\n projectId: string,\n buildId: string,\n shardId?: string,\n limits: ExtractionLimits = DEFAULT_EXTRACTION_LIMITS,\n): Promise<string> {\n const base = shardId\n ? shardScratchDir(scratchDir, projectId, buildId, shardId)\n : jobScratchDir(scratchDir, projectId, buildId);\n const targetDir = join(base, \"storybook\");\n const root = await prepareTargetDir(targetDir);\n const source = await storage.readStream(storybookZipPath(projectId, buildId));\n await extractZipStream(source, root, limits);\n return targetDir;\n}\n\n/** Reset a scratch target directory, returning its resolved root. */\nasync function prepareTargetDir(targetDir: string): Promise<string> {\n await rm(targetDir, { recursive: true, force: true });\n await mkdir(targetDir, { recursive: true });\n return resolve(targetDir);\n}\n\n/**\n * Persist an extracted Storybook directory to storage for published serving.\n *\n * Uses content-addressed deduplication: each file is hashed and stored once\n * under `content/<hash>`, with a per-build manifest at\n * `${projectId}/builds/${buildId}/storybook/manifest.json`.\n *\n * @param storage - Destination storage adapter.\n * @param sourceDir - Extracted Storybook directory.\n * @param projectId - Owning project id.\n * @param buildId - Build the files belong to.\n * @param db - Optional database for content ref tracking (enables GC).\n */\nexport async function persistStorybookStatics(\n storage: StorageAdapter,\n sourceDir: string,\n projectId: string,\n buildId: string,\n db?: DatabaseAdapter,\n): Promise<void> {\n const root = resolve(sourceDir);\n const destinationPrefix = storybookDir(projectId, buildId);\n const files = await walkFiles(root);\n const manifest: Record<string, string> = {};\n // Bounded: a large Storybook has thousands of files, and buffering them all\n // at once exhausts memory on small hosts. Each file is read fully before any\n // write: the scratch dir is often the storage root, so the per-build path\n // below can be the very file being read (a streamed write would truncate it).\n await mapLimit(files, STATICS_CONCURRENCY, async (file) => {\n const rel = relative(root, file);\n const buffer = await readFile(file);\n const hash = createHash(\"sha256\").update(buffer).digest(\"hex\");\n manifest[rel] = hash;\n // Only write if not already exists (dedup)\n if (!(await storage.exists(`content/${hash}`))) {\n await storage.write(`content/${hash}`, buffer);\n }\n // Also write to legacy per-build path for backward compat (can be removed after migration)\n // and maintain content_refs for GC\n await storage.write(`${destinationPrefix}/${rel}`, buffer);\n if (db) {\n await upsertContentRef(db, hash);\n }\n });\n await storage.write(`${destinationPrefix}/manifest.json`, Buffer.from(JSON.stringify(manifest)));\n}\n\n/** Files persisted at once; bounds how many file buffers are in memory together. */\nconst STATICS_CONCURRENCY = 8;\n\nasync function upsertContentRef(db: DatabaseAdapter, hash: string): Promise<void> {\n try {\n const contentRefs = db.tables.contentRefs;\n const now = new Date().toISOString();\n const existing = (await db.get(contentRefs, hash)) as unknown as { refCount: number } | null;\n if (existing) {\n await db.update(contentRefs, hash, {\n refCount: existing.refCount + 1,\n lastSeenAt: now,\n });\n } else {\n await db.insert(contentRefs, { hash, refCount: 1, lastSeenAt: now, createdAt: now });\n }\n } catch {\n // content_refs table may not exist yet (old DB) — ignore\n }\n}\n\nasync function walkFiles(dir: string): Promise<string[]> {\n const entries = await readdir(dir, { withFileTypes: true });\n const nested = await Promise.all(\n entries.map(async (entry) => {\n const full = join(dir, entry.name);\n if (entry.isDirectory()) {\n return await walkFiles(full);\n }\n return [full];\n }),\n );\n return nested.flat();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAsBA,MAAM,4BAA8C;CAClD,YAAY;CACZ,UAAU;AACZ;;;;;;;;;;;;;;AAeA,eAAsB,0BACpB,SACA,YACA,WACA,SACA,SACA,SAA2B,2BACV;CACjB,MAAM,OAAO,UACT,gBAAgB,YAAY,WAAW,SAAS,OAAO,IACvD,cAAc,YAAY,WAAW,OAAO;CAChD,MAAM,YAAY,KAAK,MAAM,WAAW;CACxC,MAAM,OAAO,MAAM,iBAAiB,SAAS;CAC7C,MAAM,SAAS,MAAM,QAAQ,WAAW,iBAAiB,WAAW,OAAO,CAAC;CAC5E,MAAM,iBAAiB,QAAQ,MAAM,MAAM;CAC3C,OAAO;AACT;;AAGA,eAAe,iBAAiB,WAAoC;CAClE,MAAM,GAAG,WAAW;EAAE,WAAW;EAAM,OAAO;CAAK,CAAC;CACpD,MAAM,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;CAC1C,OAAO,QAAQ,SAAS;AAC1B;;;;;;;;;;;;;;AAeA,eAAsB,wBACpB,SACA,WACA,WACA,SACA,IACe;CACf,MAAM,OAAO,QAAQ,SAAS;CAC9B,MAAM,oBAAoB,aAAa,WAAW,OAAO;CACzD,MAAM,QAAQ,MAAM,UAAU,IAAI;CAClC,MAAM,WAAmC,CAAC;CAK1C,MAAM,SAAS,OAAO,qBAAqB,OAAO,SAAS;EACzD,MAAM,MAAM,SAAS,MAAM,IAAI;EAC/B,MAAM,SAAS,MAAM,SAAS,IAAI;EAClC,MAAM,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,OAAO,KAAK;EAC7D,SAAS,OAAO;EAEhB,IAAI,CAAE,MAAM,QAAQ,OAAO,WAAW,MAAM,GAC1C,MAAM,QAAQ,MAAM,WAAW,QAAQ,MAAM;EAI/C,MAAM,QAAQ,MAAM,GAAG,kBAAkB,GAAG,OAAO,MAAM;EACzD,IAAI,IACF,MAAM,iBAAiB,IAAI,IAAI;CAEnC,CAAC;CACD,MAAM,QAAQ,MAAM,GAAG,kBAAkB,iBAAiB,OAAO,KAAK,KAAK,UAAU,QAAQ,CAAC,CAAC;AACjG;;AAGA,MAAM,sBAAsB;AAE5B,eAAe,iBAAiB,IAAqB,MAA6B;CAChF,IAAI;EACF,MAAM,cAAc,GAAG,OAAO;EAC9B,MAAM,uBAAM,IAAI,KAAK,EAAA,CAAE,YAAY;EACnC,MAAM,WAAY,MAAM,GAAG,IAAI,aAAa,IAAI;EAChD,IAAI,UACF,MAAM,GAAG,OAAO,aAAa,MAAM;GACjC,UAAU,SAAS,WAAW;GAC9B,YAAY;EACd,CAAC;OAED,MAAM,GAAG,OAAO,aAAa;GAAE;GAAM,UAAU;GAAG,YAAY;GAAK,WAAW;EAAI,CAAC;CAEvF,QAAQ,CAER;AACF;AAEA,eAAe,UAAU,KAAgC;CACvD,MAAM,UAAU,MAAM,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;CAU1D,QAAO,MATc,QAAQ,IAC3B,QAAQ,IAAI,OAAO,UAAU;EAC3B,MAAM,OAAO,KAAK,KAAK,MAAM,IAAI;EACjC,IAAI,MAAM,YAAY,GACpB,OAAO,MAAM,UAAU,IAAI;EAE7B,OAAO,CAAC,IAAI;CACd,CAAC,CACH,EAAA,CACc,KAAK;AACrB"}
|
|
@@ -5,7 +5,6 @@ import { CaptureRunner } from "../adapter/capture-runner.mjs";
|
|
|
5
5
|
import { SecretInput } from "../utils/encrypt.mjs";
|
|
6
6
|
import { Build } from "../schema/build.mjs";
|
|
7
7
|
import { Project } from "../schema/project.mjs";
|
|
8
|
-
//#region src/capture/storybook-urls.d.ts
|
|
9
8
|
/** What a pass tells the source: how long it may run, which sizes the token's lifetime. */
|
|
10
9
|
interface StorybookUrlPass {
|
|
11
10
|
/** The render budget of the pass in ms (`renderBudgetMs`); unset when the pass has none. */
|
|
@@ -35,6 +34,5 @@ declare function storybookUrlSource(options: StorybookUrlOptions, subject: {
|
|
|
35
34
|
build: Pick<Build, "id" | "projectId">;
|
|
36
35
|
project: Pick<Project, "storybookUrlTtlSeconds">;
|
|
37
36
|
}): StorybookUrlSource;
|
|
38
|
-
//#endregion
|
|
39
37
|
export { StorybookUrlOptions, StorybookUrlPass, StorybookUrlSource, storybookUrlSource };
|
|
40
38
|
//# sourceMappingURL=storybook-urls.d.mts.map
|
|
@@ -2,7 +2,7 @@ import "node:path";
|
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { signStorybookToken, storybookTokenPath, storybookUrlTtl } from "../utils/storybook-token.mjs";
|
|
5
|
-
|
|
5
|
+
// src/capture/storybook-urls.ts
|
|
6
6
|
/**
|
|
7
7
|
* A source of signed URLs for one build. Every call mints a fresh token, so a shard that waited in
|
|
8
8
|
* the queue or a retried pass is never handed a token minted before it started.
|
|
@@ -29,13 +29,13 @@ function mint(secret, subject, pass) {
|
|
|
29
29
|
secret,
|
|
30
30
|
projectId,
|
|
31
31
|
buildId,
|
|
32
|
-
ttlSeconds
|
|
32
|
+
ttlSeconds,
|
|
33
|
+
purpose: "capture"
|
|
33
34
|
}));
|
|
34
35
|
}
|
|
35
36
|
function needsUrl(runner, target) {
|
|
36
37
|
return runner.metadata.targets?.some((t) => t.target === target && t.reach === "url") ?? false;
|
|
37
38
|
}
|
|
38
|
-
//#endregion
|
|
39
39
|
export { storybookUrlSource };
|
|
40
40
|
|
|
41
41
|
//# sourceMappingURL=storybook-urls.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storybook-urls.mjs","names":[],"sources":["../../src/capture/storybook-urls.ts"],"sourcesContent":["/**\n * The signed Storybook URL of a render (ADR 0007, decision 5): minted at the moment a pass starts,\n * only for a target whose browser cannot reach this host (`reach: \"url\"`). The URL is the build's\n * Storybook root, `<publicOrigin>/_s/<token>/`; it carries the token and is never logged. Its\n * lifetime comes from the pass's render budget unless the project sets one (`storybookUrlTtl`).\n */\nimport type { CaptureRunner, CaptureTarget } from \"../adapters/capture-runner.ts\";\nimport type { Build } from \"../schema/build.ts\";\nimport type { Project } from \"../schema/project.ts\";\nimport type { SecretInput } from \"../utils/encrypt.ts\";\nimport {\n signStorybookToken,\n storybookTokenPath,\n storybookUrlTtl,\n} from \"../utils/storybook-token.ts\";\n\n/** What a pass tells the source: how long it may run, which sizes the token's lifetime. */\nexport interface StorybookUrlPass {\n /** The render budget of the pass in ms (`renderBudgetMs`); unset when the pass has none. */\n budgetMs?: number | undefined;\n}\n\n/** Gives the URL a target's browser opens, or `undefined` when it needs none (or none can be made). */\nexport type StorybookUrlSource = ((\n target: CaptureTarget,\n pass?: StorybookUrlPass,\n) => Promise<string | undefined>) & {\n /**\n * The lifetime in seconds a token minted for a pass of this budget gets (the project's override\n * or the derived, capped one). A pass that outlasts it is rendered in slices, each with a fresh\n * token (`renderInSlices`). `undefined` for a target that needs no signed URL (it is never\n * sliced), and absent on a source that has no such limit.\n */\n ttlSeconds?: (target: CaptureTarget, budgetMs: number) => number | undefined;\n};\n\n/** What minting needs of a capture job. */\nexport interface StorybookUrlOptions {\n runner: Pick<CaptureRunner, \"metadata\">;\n secret?: SecretInput;\n publicOrigin?: (() => Promise<string | undefined>) | undefined;\n}\n\n/**\n * A source of signed URLs for one build. Every call mints a fresh token, so a shard that waited in\n * the queue or a retried pass is never handed a token minted before it started.\n */\nexport function storybookUrlSource(\n options: StorybookUrlOptions,\n subject: {\n build: Pick<Build, \"id\" | \"projectId\">;\n project: Pick<Project, \"storybookUrlTtlSeconds\">;\n },\n): StorybookUrlSource {\n const source: StorybookUrlSource = async (target, pass = {}) => {\n const origin = needsUrl(options.runner, target) ? await options.publicOrigin?.() : undefined;\n return origin !== undefined && options.secret\n ? `${origin}${mint(options.secret, subject, pass)}`\n : undefined;\n };\n source.ttlSeconds = (target, budgetMs): number | undefined =>\n needsUrl(options.runner, target)\n ? storybookUrlTtl({ override: subject.project.storybookUrlTtlSeconds, budgetMs })\n : undefined;\n return source;\n}\n\n/** The path of a fresh token for one pass of the build. */\nfunction mint(\n secret: SecretInput,\n subject: Parameters<typeof storybookUrlSource>[1],\n pass: StorybookUrlPass,\n): string {\n const ttlSeconds = storybookUrlTtl({\n override: subject.project.storybookUrlTtlSeconds,\n budgetMs: pass.budgetMs ?? 0,\n });\n const { id: buildId, projectId } = subject.build;\n return storybookTokenPath(signStorybookToken({ secret, projectId, buildId, ttlSeconds }));\n}\n\nfunction needsUrl(runner: Pick<CaptureRunner, \"metadata\">, target: CaptureTarget): boolean {\n return runner.metadata.targets?.some((t) => t.target === target && t.reach === \"url\") ?? false;\n}\n"],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"storybook-urls.mjs","names":[],"sources":["../../src/capture/storybook-urls.ts"],"sourcesContent":["/**\n * The signed Storybook URL of a render (ADR 0007, decision 5): minted at the moment a pass starts,\n * only for a target whose browser cannot reach this host (`reach: \"url\"`). The URL is the build's\n * Storybook root, `<publicOrigin>/_s/<token>/`; it carries the token and is never logged. Its\n * lifetime comes from the pass's render budget unless the project sets one (`storybookUrlTtl`).\n * Every token minted here carries the signed `capture` purpose, the only claim that lets the\n * server opt a response into browser-private caching.\n */\nimport type { CaptureRunner, CaptureTarget } from \"../adapters/capture-runner.ts\";\nimport type { Build } from \"../schema/build.ts\";\nimport type { Project } from \"../schema/project.ts\";\nimport type { SecretInput } from \"../utils/encrypt.ts\";\nimport {\n signStorybookToken,\n storybookTokenPath,\n storybookUrlTtl,\n} from \"../utils/storybook-token.ts\";\n\n/** What a pass tells the source: how long it may run, which sizes the token's lifetime. */\nexport interface StorybookUrlPass {\n /** The render budget of the pass in ms (`renderBudgetMs`); unset when the pass has none. */\n budgetMs?: number | undefined;\n}\n\n/** Gives the URL a target's browser opens, or `undefined` when it needs none (or none can be made). */\nexport type StorybookUrlSource = ((\n target: CaptureTarget,\n pass?: StorybookUrlPass,\n) => Promise<string | undefined>) & {\n /**\n * The lifetime in seconds a token minted for a pass of this budget gets (the project's override\n * or the derived, capped one). A pass that outlasts it is rendered in slices, each with a fresh\n * token (`renderInSlices`). `undefined` for a target that needs no signed URL (it is never\n * sliced), and absent on a source that has no such limit.\n */\n ttlSeconds?: (target: CaptureTarget, budgetMs: number) => number | undefined;\n};\n\n/** What minting needs of a capture job. */\nexport interface StorybookUrlOptions {\n runner: Pick<CaptureRunner, \"metadata\">;\n secret?: SecretInput;\n publicOrigin?: (() => Promise<string | undefined>) | undefined;\n}\n\n/**\n * A source of signed URLs for one build. Every call mints a fresh token, so a shard that waited in\n * the queue or a retried pass is never handed a token minted before it started.\n */\nexport function storybookUrlSource(\n options: StorybookUrlOptions,\n subject: {\n build: Pick<Build, \"id\" | \"projectId\">;\n project: Pick<Project, \"storybookUrlTtlSeconds\">;\n },\n): StorybookUrlSource {\n const source: StorybookUrlSource = async (target, pass = {}) => {\n const origin = needsUrl(options.runner, target) ? await options.publicOrigin?.() : undefined;\n return origin !== undefined && options.secret\n ? `${origin}${mint(options.secret, subject, pass)}`\n : undefined;\n };\n source.ttlSeconds = (target, budgetMs): number | undefined =>\n needsUrl(options.runner, target)\n ? storybookUrlTtl({ override: subject.project.storybookUrlTtlSeconds, budgetMs })\n : undefined;\n return source;\n}\n\n/** The path of a fresh token for one pass of the build. */\nfunction mint(\n secret: SecretInput,\n subject: Parameters<typeof storybookUrlSource>[1],\n pass: StorybookUrlPass,\n): string {\n const ttlSeconds = storybookUrlTtl({\n override: subject.project.storybookUrlTtlSeconds,\n budgetMs: pass.budgetMs ?? 0,\n });\n const { id: buildId, projectId } = subject.build;\n return storybookTokenPath(\n signStorybookToken({ secret, projectId, buildId, ttlSeconds, purpose: \"capture\" }),\n );\n}\n\nfunction needsUrl(runner: Pick<CaptureRunner, \"metadata\">, target: CaptureTarget): boolean {\n return runner.metadata.targets?.some((t) => t.target === target && t.reach === \"url\") ?? false;\n}\n"],"mappings":";;;;;;;;;AAiDA,SAAgB,mBACd,SACA,SAIoB;CACpB,MAAM,SAA6B,OAAO,QAAQ,OAAO,CAAC,MAAM;EAC9D,MAAM,SAAS,SAAS,QAAQ,QAAQ,MAAM,IAAI,MAAM,QAAQ,eAAe,IAAI,KAAA;EACnF,OAAO,WAAW,KAAA,KAAa,QAAQ,SACnC,GAAG,SAAS,KAAK,QAAQ,QAAQ,SAAS,IAAI,MAC9C,KAAA;CACN;CACA,OAAO,cAAc,QAAQ,aAC3B,SAAS,QAAQ,QAAQ,MAAM,IAC3B,gBAAgB;EAAE,UAAU,QAAQ,QAAQ;EAAwB;CAAS,CAAC,IAC9E,KAAA;CACN,OAAO;AACT;;AAGA,SAAS,KACP,QACA,SACA,MACQ;CACR,MAAM,aAAa,gBAAgB;EACjC,UAAU,QAAQ,QAAQ;EAC1B,UAAU,KAAK,YAAY;CAC7B,CAAC;CACD,MAAM,EAAE,IAAI,SAAS,cAAc,QAAQ;CAC3C,OAAO,mBACL,mBAAmB;EAAE;EAAQ;EAAW;EAAS;EAAY,SAAS;CAAU,CAAC,CACnF;AACF;AAEA,SAAS,SAAS,QAAyC,QAAgC;CACzF,OAAO,OAAO,SAAS,SAAS,MAAM,MAAM,EAAE,WAAW,UAAU,EAAE,UAAU,KAAK,KAAK;AAC3F"}
|
|
@@ -2,7 +2,6 @@ import __tsdown_shims_path from 'node:path';
|
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
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;
|
|
@@ -47,6 +46,5 @@ declare class StorybookAdapter implements StorySourceAdapter {
|
|
|
47
46
|
loadDependencyGraph(source: string): Promise<DepGraph | null>;
|
|
48
47
|
private readIndex;
|
|
49
48
|
}
|
|
50
|
-
//#endregion
|
|
51
49
|
export { STORYBOOK_BUILTIN_VIEWPORTS, StorybookAdapter, mergeParameters, resolveStoryViewports };
|
|
52
50
|
//# sourceMappingURL=storybook.d.mts.map
|
|
@@ -1,12 +1,12 @@
|
|
|
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";
|
|
4
5
|
import { applyModes, buildGlobalsParam } from "./modes.mjs";
|
|
5
6
|
import { extractDesignUrl } from "./design.mjs";
|
|
6
|
-
import { join } from "node:path";
|
|
7
7
|
import { STATS_FILENAME, loadDepGraph } from "@storyshelf/affected";
|
|
8
8
|
import { readFile } from "node:fs/promises";
|
|
9
|
-
|
|
9
|
+
// src/capture/storybook.ts
|
|
10
10
|
/** Merge the `chromatic` and `storyshelf` parameter layers (`storyshelf` wins). */
|
|
11
11
|
function mergeParameters(entry) {
|
|
12
12
|
const { figma: _figma, ...storyshelf } = entry.parameters?.storyshelf ?? {};
|
|
@@ -17,6 +17,11 @@ function mergeParameters(entry) {
|
|
|
17
17
|
const modes = mergeModes(entry.parameters?.chromatic?.modes, entry.parameters?.storyshelf?.modes);
|
|
18
18
|
if (modes) merged.modes = modes;
|
|
19
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
|
+
}
|
|
20
25
|
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
21
26
|
}
|
|
22
27
|
/** Union both parameter keys' modes by name (`storyshelf` wins per mode). */
|
|
@@ -158,7 +163,6 @@ var StorybookAdapter = class {
|
|
|
158
163
|
throw new Error(`No Storybook index found in ${source}`);
|
|
159
164
|
}
|
|
160
165
|
};
|
|
161
|
-
//#endregion
|
|
162
166
|
export { STORYBOOK_BUILTIN_VIEWPORTS, StorybookAdapter, mergeParameters, resolveStoryViewports };
|
|
163
167
|
|
|
164
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 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 { 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 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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"}
|
|
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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"}
|
package/dist/capture/target.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import "node:url";
|
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { BuildModel } from "../models/build.mjs";
|
|
5
5
|
import { ProjectModel } from "../models/project.mjs";
|
|
6
|
-
|
|
6
|
+
// src/capture/target.ts
|
|
7
7
|
/** Load a build and its project, throwing when either is gone. */
|
|
8
8
|
async function loadTarget(options, buildId) {
|
|
9
9
|
const build = await new BuildModel(options.db, options.tables).get(buildId);
|
|
@@ -15,7 +15,6 @@ async function loadTarget(options, buildId) {
|
|
|
15
15
|
project
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
|
-
//#endregion
|
|
19
18
|
export { loadTarget };
|
|
20
19
|
|
|
21
20
|
//# sourceMappingURL=target.mjs.map
|
|
@@ -2,7 +2,6 @@ import __tsdown_shims_path from 'node:path';
|
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
3
|
import { RunnerTarget } from "../adapters/capture-target.mjs";
|
|
4
4
|
import { CaptureRunner } from "../adapter/capture-runner.mjs";
|
|
5
|
-
//#region src/capture/targets.d.ts
|
|
6
5
|
/** Why a `reach: "url"` target is unavailable without a secret (the first words are matched by tests and docs). */
|
|
7
6
|
declare const NO_SECRET_REASON = "no secret: set the server secret, it signs the Storybook URL the remote browser opens";
|
|
8
7
|
/** Why it is unavailable without a confirmed public origin. */
|
|
@@ -20,6 +19,5 @@ interface TargetPrerequisites {
|
|
|
20
19
|
* reason. `undefined` when the runner publishes no list (it offers any target).
|
|
21
20
|
*/
|
|
22
21
|
declare function offeredTargets(runner: Pick<CaptureRunner, "metadata">, prerequisites: TargetPrerequisites): readonly RunnerTarget[] | undefined;
|
|
23
|
-
//#endregion
|
|
24
22
|
export { NO_ORIGIN_REASON, NO_SECRET_REASON, TargetPrerequisites, offeredTargets };
|
|
25
23
|
//# sourceMappingURL=targets.d.mts.map
|
package/dist/capture/targets.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
|
|
4
|
+
// src/capture/targets.ts
|
|
5
5
|
/** Why a `reach: "url"` target is unavailable without a secret (the first words are matched by tests and docs). */
|
|
6
6
|
const NO_SECRET_REASON = "no secret: set the server secret, it signs the Storybook URL the remote browser opens";
|
|
7
7
|
/** Why it is unavailable without a confirmed public origin. */
|
|
@@ -26,7 +26,6 @@ function withPrerequisites(entry, prerequisites) {
|
|
|
26
26
|
};
|
|
27
27
|
return entry;
|
|
28
28
|
}
|
|
29
|
-
//#endregion
|
|
30
29
|
export { NO_ORIGIN_REASON, NO_SECRET_REASON, offeredTargets };
|
|
31
30
|
|
|
32
31
|
//# sourceMappingURL=targets.mjs.map
|
package/dist/capture/tests.d.mts
CHANGED
|
@@ -6,7 +6,6 @@ import "./storybook-urls.mjs";
|
|
|
6
6
|
import "../models/build.mjs";
|
|
7
7
|
import "../models/capture-attempt.mjs";
|
|
8
8
|
import { Logger } from "pino";
|
|
9
|
-
//#region src/capture/tests.d.ts
|
|
10
9
|
/** What a build keeps of one `.test()` entry (JSON in `builds.test_results`). */
|
|
11
10
|
interface TestResult {
|
|
12
11
|
/** The test entry's id (`<story id>:<test name slug>`). */
|
|
@@ -32,6 +31,5 @@ interface TestSummary {
|
|
|
32
31
|
declare function parseTestResults(json: string | null | undefined): TestResult[];
|
|
33
32
|
/** Counts of a build's results. */
|
|
34
33
|
declare function summarizeTests(results: readonly TestResult[]): TestSummary;
|
|
35
|
-
//#endregion
|
|
36
34
|
export { TestResult, TestSummary, parseTestResults, summarizeTests };
|
|
37
35
|
//# sourceMappingURL=tests.d.mts.map
|
package/dist/capture/tests.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import { emitAttemptLog } from "../models/capture-attempt.mjs";
|
|
|
7
7
|
import { classifyFailures } from "./failure-sets.mjs";
|
|
8
8
|
import { renderBudgetMs } from "./render.mjs";
|
|
9
9
|
import { StorybookAdapter } from "./storybook.mjs";
|
|
10
|
-
|
|
10
|
+
// src/capture/tests.ts
|
|
11
11
|
/** Longest failure message kept (rows and logs stay bounded). */
|
|
12
12
|
const ERROR_MAX = 1e3;
|
|
13
13
|
/**
|
|
@@ -20,7 +20,8 @@ const ERROR_MAX = 1e3;
|
|
|
20
20
|
async function runProjectTests(input) {
|
|
21
21
|
const tests = input.project.runTests ? await testsToRun(input) : [];
|
|
22
22
|
const run = input.runner.runTests?.bind(input.runner);
|
|
23
|
-
if (tests.length === 0
|
|
23
|
+
if (tests.length === 0) return await skipTests(input, tests.length);
|
|
24
|
+
if (!run) return await unsupportedTests(input, tests);
|
|
24
25
|
const results = resultsOf(tests, await execute(input, run, tests));
|
|
25
26
|
await keepResults(input, results);
|
|
26
27
|
return await reportFailures(input, results, tests);
|
|
@@ -34,6 +35,16 @@ async function skipTests(input, wanted) {
|
|
|
34
35
|
flaky: /* @__PURE__ */ new Set()
|
|
35
36
|
};
|
|
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
|
+
}
|
|
37
48
|
/** The test entries to run: those of stories that are not skipped. */
|
|
38
49
|
async function testsToRun(input) {
|
|
39
50
|
const skipped = new Set(input.stories.filter((story) => isDisabledStory(story)).map((s) => s.id));
|
|
@@ -120,7 +131,6 @@ function isTestResult(value) {
|
|
|
120
131
|
const record = value;
|
|
121
132
|
return typeof record["id"] === "string" && typeof record["name"] === "string" && (record["status"] === "passed" || record["status"] === "failed");
|
|
122
133
|
}
|
|
123
|
-
//#endregion
|
|
124
134
|
export { parseTestResults, runProjectTests, summarizeTests };
|
|
125
135
|
|
|
126
136
|
//# sourceMappingURL=tests.mjs.map
|
|
@@ -1 +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 || !run) return await skipTests(input, tests.length);\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/** 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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"}
|
|
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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"}
|
|
@@ -3,13 +3,22 @@ import "node:url";
|
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { PER_CAPTURE_BUDGET_MS } from "../utils/storybook-token.mjs";
|
|
5
5
|
import { renderBudgetMs, renderWithTimeout } from "./render.mjs";
|
|
6
|
-
|
|
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
|
+
};
|
|
7
14
|
/** Render the pass, minting a fresh signed URL for each slice; results are concatenated. */
|
|
8
15
|
async function renderInSlices(input) {
|
|
9
16
|
const slices = slicesOf(input);
|
|
10
17
|
const results = [];
|
|
11
18
|
for (const slice of slices) {
|
|
12
|
-
const
|
|
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);
|
|
13
22
|
const storybookUrl = await input.storybookUrl?.(input.browser, { budgetMs });
|
|
14
23
|
results.push(await renderWithTimeout(input.runner, {
|
|
15
24
|
...input.request,
|
|
@@ -30,8 +39,17 @@ function slicesOf(input) {
|
|
|
30
39
|
}];
|
|
31
40
|
const ttl = input.storybookUrl?.ttlSeconds?.(input.browser, renderBudgetMs(expected, input.renderTimeoutMs));
|
|
32
41
|
const budgetSeconds = renderBudgetMs(expected, input.renderTimeoutMs) / 1e3;
|
|
33
|
-
if (ttl === void 0 ||
|
|
34
|
-
return cut(stories, counts,
|
|
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;
|
|
35
53
|
}
|
|
36
54
|
function cut(stories, counts, limit) {
|
|
37
55
|
const slices = [];
|
|
@@ -40,7 +58,7 @@ function cut(stories, counts, limit) {
|
|
|
40
58
|
expected: 0
|
|
41
59
|
};
|
|
42
60
|
for (const [index, story] of stories.entries()) {
|
|
43
|
-
const count = counts[index] ?? 0;
|
|
61
|
+
const count = checkedCount(counts[index] ?? 0, limit);
|
|
44
62
|
if (current.stories.length > 0 && current.expected + count > limit) {
|
|
45
63
|
slices.push(current);
|
|
46
64
|
current = {
|
|
@@ -68,7 +86,6 @@ function tagged(result) {
|
|
|
68
86
|
for (const capture of result.captures) if (result.environment) capture.environment ??= result.environment;
|
|
69
87
|
return result.captures;
|
|
70
88
|
}
|
|
71
|
-
|
|
72
|
-
export { renderInSlices };
|
|
89
|
+
export { SignedUrlLifetimeError, renderInSlices };
|
|
73
90
|
|
|
74
91
|
//# sourceMappingURL=token-chunks.mjs.map
|
|
@@ -1 +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 {
|
|
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"}
|
package/dist/capture.d.mts
CHANGED
|
@@ -5,6 +5,7 @@ import { LeaseRenewal, PollableCaptureQueue, PollableJob } from "./adapter/captu
|
|
|
5
5
|
import { MAX_BROWSERS, assertBrowserCount, assertSupportedBrowsers, listTargets, parseBrowsers, primaryTargetOf, refusalFor, resolveBrowsers } from "./capture/browsers.mjs";
|
|
6
6
|
import { NO_ORIGIN_REASON, NO_SECRET_REASON, TargetPrerequisites, offeredTargets } from "./capture/targets.mjs";
|
|
7
7
|
import { StorybookUrlSource, storybookUrlSource } from "./capture/storybook-urls.mjs";
|
|
8
|
+
import { DEFAULT_IGNORE_SELECTORS, IgnoreSelectorList, MAX_IGNORE_SELECTORS, cleanIgnoreSelectors, declaredIgnoreSelectors, ignoreStylesheet, isSafeIgnoreSelector } from "./capture/ignore-regions.mjs";
|
|
8
9
|
import { MAX_MODES_PER_STORY, buildGlobalsParam, filterModes, modeSlug, parseModesAllowlist } from "./capture/modes.mjs";
|
|
9
10
|
import { TestResult, TestSummary, parseTestResults, summarizeTests } from "./capture/tests.mjs";
|
|
10
11
|
import { JobLease, ProgressLease } from "./capture/job-lease.mjs";
|
|
@@ -50,6 +51,7 @@ import { PublishFacts, publishVerdict } from "./capture/publish/accept.mjs";
|
|
|
50
51
|
import { publishingOf } from "./capture/publish/published.mjs";
|
|
51
52
|
import { ExtractionResult, extractZipStream } from "./capture/publish/extract.mjs";
|
|
52
53
|
import { PUBLISH_SCRATCH_ID, PublishZipOptions, publishStoredZip } from "./capture/publish/run.mjs";
|
|
54
|
+
import { ZipInvalidError } from "./capture/publish/invalid-zip.mjs";
|
|
53
55
|
import "./capture/publish/index.mjs";
|
|
54
56
|
import { Catalog, CatalogComponent, CatalogEntry } from "./capture/catalog/types.mjs";
|
|
55
57
|
import { CatalogDeps, catalogueBuild, readCatalog } from "./capture/catalog/catalogue.mjs";
|
|
@@ -57,11 +59,12 @@ import { CatalogLoader, CatalogLoaderOptions, DEFAULT_CATALOG_CACHE_SIZE, create
|
|
|
57
59
|
import { createCatalogHook } from "./capture/catalog/hook.mjs";
|
|
58
60
|
import { EntryChanges, diffEntries, groupComponents, hashEntries, toBuildComponent } from "./capture/catalog/components.mjs";
|
|
59
61
|
import { parseStorybookIndex, parseStorybookIndexText } from "./capture/catalog/parse.mjs";
|
|
60
|
-
import { readPublishedFile } from "./capture/catalog/read.mjs";
|
|
62
|
+
import { readPublishedFile, readPublishedFileInfo } from "./capture/catalog/read.mjs";
|
|
61
63
|
import { ComponentBuildRow, ComponentChange, catalogPresence, componentBuildHistory } from "./capture/catalog/history.mjs";
|
|
62
64
|
import "./capture/catalog/index.mjs";
|
|
63
65
|
import { ChildProcessHandle, ParentPort } from "./capture/forked/channel.mjs";
|
|
64
66
|
import { ForkedCaptureHandler, ForkedCaptureOptions, createForkedCaptureHandler } from "./capture/forked/forked-capture.mjs";
|
|
65
67
|
import { CAPTURE_RETRIES_ENV, CAPTURE_SHARD_SIZE_ENV, CAPTURE_WORKER_ROLE, ForkOptions, canForkCaptureChild, forkCaptureChild } from "./capture/forked/fork.mjs";
|
|
66
68
|
import { FORK_MIN_CPUS, FORK_MIN_MEMORY_BYTES, ForkDecision, ForkHost, decideCaptureProcess } from "./capture/forked/decision.mjs";
|
|
67
|
-
|
|
69
|
+
import { ComparisonReference, isComparisonImagePath, parseComparisonReference } from "./capture/comparison-reference.mjs";
|
|
70
|
+
export { A11Y_ROOT_SELECTOR, type A11yRunPlan, ACTIVE_POLL_MS, ACTIVE_WINDOW_MS, type AfterPublishContext, type AfterPublishHook, type BoundingBox, type BuildStateDeps, type BuildStatePublisher, type BuildStatePublisherOptions, CANCEL_CHECK_MS, CAPTURE_RETRIES_ENV, CAPTURE_SHARD_SIZE_ENV, CAPTURE_WORKER_ROLE, type CaptureAttemptRef, type CaptureChildOptions, type CaptureConsumer, type CaptureConsumerOptions, type CaptureContext, type CaptureDispatchJob, type CaptureHandlerOptions, type CaptureJobInput, type CaptureJobOptions, type Catalog, type CatalogComponent, type CatalogDeps, type CatalogEntry, type CatalogLoader, type CatalogLoaderOptions, type ChildHost, type ChildMessage, type ChildProcessHandle, type ComparisonReference, type ComponentBuildRow, type ComponentChange, type ConsumerConfig, type ConsumerJob, type ConsumerMetrics, type CrashFailureOutcome, DATABASE_QUEUE_METADATA, DEFAULT_BACKOFF_MS, DEFAULT_CADENCE, DEFAULT_CATALOG_CACHE_SIZE, DEFAULT_IGNORE_SELECTORS, DEFAULT_LEASE_MS, DEFAULT_MAX_INLINE_UNZIP_SIZE, DEFAULT_MAX_RETRIES, DEFAULT_MAX_UNZIPPED_BYTES, DEFAULT_MAX_ZIP_ENTRIES, DEFAULT_RETRIES, DEFAULT_SAMPLE_MS, DEFAULT_VIEWPORTS, DEFAULT_WAIT_TIME_SECONDS, type DatabaseCaptureQueue, type DatabaseCaptureQueueOptions, type DatabaseJob, type DispatchDeps, type EntryChanges, type EnvironmentChange, type ExtractionLimits, type ExtractionResult, FILE_BY_FILE_HINT, FORK_MIN_CPUS, FORK_MIN_MEMORY_BYTES, type Fingerprint, type ForkDecision, type ForkHost, type ForkOptions, type ForkedCaptureHandler, type ForkedCaptureOptions, IDLE_POLL_MS, type IgnoreSelectorList, type JobFinishedEvent, type JobHandlers, type JobLease, type JobLeaseKeeper, type JobOutcome, type JobRunContext, type KindHandler, LEASE_EXHAUSTED_ERROR, type LeaseEnd, type LeaseKeeperOptions, type LeaseRenewal, MAX_BACKOFF_MS, MAX_BROWSERS, MAX_IGNORE_SELECTORS, MAX_MODES_PER_STORY, MAX_NACK_DELAY_MS, MAX_REPORTED_VIOLATIONS, MIN_BLOCKING_POLL_MS, type ModeDefinition, type ModeMap, type ModeViewportValue, NOTIFY_BACKOFF, NOTIFY_MAX_ATTEMPTS, NO_ORIGIN_REASON, NO_SECRET_REASON, type NotifyFailure, type NotifyHandlerOptions, type NotifyJobQueue, PAUSED_MESSAGE, POLL_JITTER, PUBLISH_SCRATCH_ID, type ParentMessage, type ParentPort, type PollCadence, type PollLoop, type PollLoopOptions, type PollableCaptureQueue, type PollableJob, ProgressLease, type PublishCompleter, type PublishCompleterOptions, type PublishFacts, type PublishHandlerOptions, type PublishLimits, type PublishOutcome, type PublishZipOptions, type QueueJobInput, type QueueSample, REDIFFABLE_BUILD_STATUSES, RENEW_THROTTLE_MS, type RediffDeps, type RediffResult, type RetryConfig, SCRATCH_DIRNAME, SCRATCH_MAX_AGE_MS, SIZING_DEFAULTS, STORYBOOK_BUILTIN_VIEWPORTS, type ScratchSweepOptions, type ScreenshotPlan, type SizingOptions, type SkipReason, type SlimViolation, type StateTarget, type StoryEntry, type StoryParameters, type StorySourceAdapter, type StoryViewportConfig, type StoryViewportDefinition, StorybookAdapter, type StorybookUrlSource, type TargetPrerequisites, type TestResult, type TestSummary, type Viewport, ZipInvalidError, ZipLimitError, assertBrowserCount, assertSupportedBrowsers, axeInjectionScript, backoffDelayMs, buildFingerprint, buildGlobalsParam, buildScratchDir, canForkCaptureChild, captureDispatchDeps, catalogPresence, catalogueBuild, changedFields, cleanIgnoreSelectors, componentBuildHistory, createBuildStatePublisher, createCaptureConsumer, createCaptureHandler, createCatalogHook, createCatalogLoader, createDatabaseCaptureQueue, createDispatchJob, createForkedCaptureHandler, createNotifyEnqueuer, createNotifyHandler, createPublishCompleter, createPublishHandler, decideCaptureProcess, declaredIgnoreSelectors, deliverBuildState, diffEntries, executeCaptureJob, extractStorybookToScratch, extractZipStream, extractionLimitsOf, failCrashedCapture, filterModes, fingerprintHash, forkCaptureChild, getScreenshotPlan, groupComponents, hashEntries, idleDelayMs, ignoreStylesheet, inPageReadA11yParameters, inPageRunAxe, isComparisonImagePath, isDisabledStory, isFastEmptyPoll, isFinalAttempt, isFlakyStory, isSafeIgnoreSelector, keepJobLease, listTargets, loadAxeSource, loadBuild, mapAxeViolations, mergeParameters, modeSlug, nextPollDelay, notifyExhausted, offeredTargets, parseBrowsers, parseComparisonReference, parseFingerprint, parseModesAllowlist, parseStorybookIndex, parseStorybookIndexText, parseTestResults, persistCapture, persistStorybookStatics, primaryTargetOf, publishRoute, publishStoredZip, publishVerdict, publishingOf, readCatalog, readPublishedFile, readPublishedFileInfo, rediffBuild, refusalFor, removeBuildScratch, removeJobScratch, requestOpenBuildStates, resolveA11yRun, resolveBrowsers, resolveViewports as resolveCaptureViewports, resolvePublishLimits, resolveStoryViewports, runCaptureChild, sampleQueue, startPollLoop, stateTargets, storybookUrlSource, summarizeTests, sweepOrphanedScratch, toBuildComponent, writeTo };
|
package/dist/capture.mjs
CHANGED
|
@@ -4,14 +4,17 @@ import.meta.url;
|
|
|
4
4
|
import { MAX_BROWSERS, assertBrowserCount, assertSupportedBrowsers, listTargets, parseBrowsers, primaryTargetOf, refusalFor, resolveBrowsers } from "./capture/browsers.mjs";
|
|
5
5
|
import { NO_ORIGIN_REASON, NO_SECRET_REASON, offeredTargets } from "./capture/targets.mjs";
|
|
6
6
|
import { storybookUrlSource } from "./capture/storybook-urls.mjs";
|
|
7
|
+
import { DEFAULT_IGNORE_SELECTORS, MAX_IGNORE_SELECTORS, cleanIgnoreSelectors, declaredIgnoreSelectors, ignoreStylesheet, isSafeIgnoreSelector } from "./capture/ignore-regions.mjs";
|
|
7
8
|
import { MAX_MODES_PER_STORY, buildGlobalsParam, filterModes, modeSlug, parseModesAllowlist } from "./capture/modes.mjs";
|
|
8
9
|
import { DEFAULT_VIEWPORTS, isDisabledStory, isFlakyStory } from "./capture/adapter.mjs";
|
|
9
10
|
import { STORYBOOK_BUILTIN_VIEWPORTS, StorybookAdapter, mergeParameters, resolveStoryViewports } from "./capture/storybook.mjs";
|
|
10
11
|
import { parseTestResults, summarizeTests } from "./capture/tests.mjs";
|
|
12
|
+
import { isComparisonImagePath, parseComparisonReference } from "./capture/comparison-reference.mjs";
|
|
11
13
|
import { SIZING_DEFAULTS, getScreenshotPlan, resolveViewports } from "./capture/sizing.mjs";
|
|
12
14
|
import { buildFingerprint, changedFields, fingerprintHash, parseFingerprint } from "./capture/fingerprint.mjs";
|
|
13
15
|
import { persistCapture } from "./capture/pipeline.mjs";
|
|
14
16
|
import { SCRATCH_DIRNAME, SCRATCH_MAX_AGE_MS, buildScratchDir, removeBuildScratch, removeJobScratch, sweepOrphanedScratch } from "./capture/scratch.mjs";
|
|
17
|
+
import { ZipInvalidError } from "./capture/publish/invalid-zip.mjs";
|
|
15
18
|
import { DEFAULT_MAX_INLINE_UNZIP_SIZE, DEFAULT_MAX_UNZIPPED_BYTES, DEFAULT_MAX_ZIP_ENTRIES, FILE_BY_FILE_HINT, ZipLimitError, extractionLimitsOf, publishRoute, resolvePublishLimits } from "./capture/publish/limits.mjs";
|
|
16
19
|
import { extractZipStream } from "./capture/publish/extract.mjs";
|
|
17
20
|
import { extractStorybookToScratch, persistStorybookStatics } from "./capture/statics.mjs";
|
|
@@ -50,10 +53,10 @@ import { createPublishCompleter } from "./capture/publish/complete.mjs";
|
|
|
50
53
|
import { createCatalogHook } from "./capture/catalog/hook.mjs";
|
|
51
54
|
import { diffEntries, groupComponents, hashEntries, toBuildComponent } from "./capture/catalog/components.mjs";
|
|
52
55
|
import { parseStorybookIndex, parseStorybookIndexText } from "./capture/catalog/parse.mjs";
|
|
53
|
-
import { readPublishedFile } from "./capture/catalog/read.mjs";
|
|
56
|
+
import { readPublishedFile, readPublishedFileInfo } from "./capture/catalog/read.mjs";
|
|
54
57
|
import { catalogueBuild, readCatalog } from "./capture/catalog/catalogue.mjs";
|
|
55
58
|
import { DEFAULT_CATALOG_CACHE_SIZE, createCatalogLoader } from "./capture/catalog/load.mjs";
|
|
56
59
|
import { catalogPresence, componentBuildHistory } from "./capture/catalog/history.mjs";
|
|
57
60
|
import { createForkedCaptureHandler } from "./capture/forked/forked-capture.mjs";
|
|
58
61
|
import { FORK_MIN_CPUS, FORK_MIN_MEMORY_BYTES, decideCaptureProcess } from "./capture/forked/decision.mjs";
|
|
59
|
-
export { A11Y_ROOT_SELECTOR, ACTIVE_POLL_MS, ACTIVE_WINDOW_MS, CANCEL_CHECK_MS, CAPTURE_RETRIES_ENV, CAPTURE_SHARD_SIZE_ENV, CAPTURE_WORKER_ROLE, DATABASE_QUEUE_METADATA, DEFAULT_BACKOFF_MS, DEFAULT_CADENCE, DEFAULT_CATALOG_CACHE_SIZE, DEFAULT_LEASE_MS, DEFAULT_MAX_INLINE_UNZIP_SIZE, DEFAULT_MAX_RETRIES, DEFAULT_MAX_UNZIPPED_BYTES, DEFAULT_MAX_ZIP_ENTRIES, DEFAULT_RETRIES, DEFAULT_SAMPLE_MS, DEFAULT_VIEWPORTS, DEFAULT_WAIT_TIME_SECONDS, FILE_BY_FILE_HINT, FORK_MIN_CPUS, FORK_MIN_MEMORY_BYTES, IDLE_POLL_MS, LEASE_EXHAUSTED_ERROR, MAX_BACKOFF_MS, MAX_BROWSERS, MAX_MODES_PER_STORY, MAX_NACK_DELAY_MS, MAX_REPORTED_VIOLATIONS, MIN_BLOCKING_POLL_MS, NOTIFY_BACKOFF, NOTIFY_MAX_ATTEMPTS, NO_ORIGIN_REASON, NO_SECRET_REASON, PAUSED_MESSAGE, POLL_JITTER, PUBLISH_SCRATCH_ID, ProgressLease, REDIFFABLE_BUILD_STATUSES, RENEW_THROTTLE_MS, SCRATCH_DIRNAME, SCRATCH_MAX_AGE_MS, SIZING_DEFAULTS, STORYBOOK_BUILTIN_VIEWPORTS, StorybookAdapter, ZipLimitError, assertBrowserCount, assertSupportedBrowsers, axeInjectionScript, backoffDelayMs, buildFingerprint, buildGlobalsParam, buildScratchDir, canForkCaptureChild, captureDispatchDeps, catalogPresence, catalogueBuild, changedFields, componentBuildHistory, createBuildStatePublisher, createCaptureConsumer, createCaptureHandler, createCatalogHook, createCatalogLoader, createDatabaseCaptureQueue, createDispatchJob, createForkedCaptureHandler, createNotifyEnqueuer, createNotifyHandler, createPublishCompleter, createPublishHandler, decideCaptureProcess, deliverBuildState, diffEntries, executeCaptureJob, extractStorybookToScratch, extractZipStream, extractionLimitsOf, failCrashedCapture, filterModes, fingerprintHash, forkCaptureChild, getScreenshotPlan, groupComponents, hashEntries, idleDelayMs, inPageReadA11yParameters, inPageRunAxe, isDisabledStory, isFastEmptyPoll, isFinalAttempt, isFlakyStory, keepJobLease, listTargets, loadAxeSource, loadBuild, mapAxeViolations, mergeParameters, modeSlug, nextPollDelay, notifyExhausted, offeredTargets, parseBrowsers, parseFingerprint, parseModesAllowlist, parseStorybookIndex, parseStorybookIndexText, parseTestResults, persistCapture, persistStorybookStatics, primaryTargetOf, publishRoute, publishStoredZip, publishVerdict, publishingOf, readCatalog, readPublishedFile, rediffBuild, refusalFor, removeBuildScratch, removeJobScratch, requestOpenBuildStates, resolveA11yRun, resolveBrowsers, resolveViewports as resolveCaptureViewports, resolvePublishLimits, resolveStoryViewports, runCaptureChild, sampleQueue, startPollLoop, stateTargets, storybookUrlSource, summarizeTests, sweepOrphanedScratch, toBuildComponent, writeTo };
|
|
62
|
+
export { A11Y_ROOT_SELECTOR, ACTIVE_POLL_MS, ACTIVE_WINDOW_MS, CANCEL_CHECK_MS, CAPTURE_RETRIES_ENV, CAPTURE_SHARD_SIZE_ENV, CAPTURE_WORKER_ROLE, DATABASE_QUEUE_METADATA, DEFAULT_BACKOFF_MS, DEFAULT_CADENCE, DEFAULT_CATALOG_CACHE_SIZE, DEFAULT_IGNORE_SELECTORS, DEFAULT_LEASE_MS, DEFAULT_MAX_INLINE_UNZIP_SIZE, DEFAULT_MAX_RETRIES, DEFAULT_MAX_UNZIPPED_BYTES, DEFAULT_MAX_ZIP_ENTRIES, DEFAULT_RETRIES, DEFAULT_SAMPLE_MS, DEFAULT_VIEWPORTS, DEFAULT_WAIT_TIME_SECONDS, FILE_BY_FILE_HINT, FORK_MIN_CPUS, FORK_MIN_MEMORY_BYTES, IDLE_POLL_MS, LEASE_EXHAUSTED_ERROR, MAX_BACKOFF_MS, MAX_BROWSERS, MAX_IGNORE_SELECTORS, MAX_MODES_PER_STORY, MAX_NACK_DELAY_MS, MAX_REPORTED_VIOLATIONS, MIN_BLOCKING_POLL_MS, NOTIFY_BACKOFF, NOTIFY_MAX_ATTEMPTS, NO_ORIGIN_REASON, NO_SECRET_REASON, PAUSED_MESSAGE, POLL_JITTER, PUBLISH_SCRATCH_ID, ProgressLease, REDIFFABLE_BUILD_STATUSES, RENEW_THROTTLE_MS, SCRATCH_DIRNAME, SCRATCH_MAX_AGE_MS, SIZING_DEFAULTS, STORYBOOK_BUILTIN_VIEWPORTS, StorybookAdapter, ZipInvalidError, ZipLimitError, assertBrowserCount, assertSupportedBrowsers, axeInjectionScript, backoffDelayMs, buildFingerprint, buildGlobalsParam, buildScratchDir, canForkCaptureChild, captureDispatchDeps, catalogPresence, catalogueBuild, changedFields, cleanIgnoreSelectors, componentBuildHistory, createBuildStatePublisher, createCaptureConsumer, createCaptureHandler, createCatalogHook, createCatalogLoader, createDatabaseCaptureQueue, createDispatchJob, createForkedCaptureHandler, createNotifyEnqueuer, createNotifyHandler, createPublishCompleter, createPublishHandler, decideCaptureProcess, declaredIgnoreSelectors, deliverBuildState, diffEntries, executeCaptureJob, extractStorybookToScratch, extractZipStream, extractionLimitsOf, failCrashedCapture, filterModes, fingerprintHash, forkCaptureChild, getScreenshotPlan, groupComponents, hashEntries, idleDelayMs, ignoreStylesheet, inPageReadA11yParameters, inPageRunAxe, isComparisonImagePath, isDisabledStory, isFastEmptyPoll, isFinalAttempt, isFlakyStory, isSafeIgnoreSelector, keepJobLease, listTargets, loadAxeSource, loadBuild, mapAxeViolations, mergeParameters, modeSlug, nextPollDelay, notifyExhausted, offeredTargets, parseBrowsers, parseComparisonReference, parseFingerprint, parseModesAllowlist, parseStorybookIndex, parseStorybookIndexText, parseTestResults, persistCapture, persistStorybookStatics, primaryTargetOf, publishRoute, publishStoredZip, publishVerdict, publishingOf, readCatalog, readPublishedFile, readPublishedFileInfo, rediffBuild, refusalFor, removeBuildScratch, removeJobScratch, requestOpenBuildStates, resolveA11yRun, resolveBrowsers, resolveViewports as resolveCaptureViewports, resolvePublishLimits, resolveStoryViewports, runCaptureChild, sampleQueue, startPollLoop, stateTargets, storybookUrlSource, summarizeTests, sweepOrphanedScratch, toBuildComponent, writeTo };
|