@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,7 +1,6 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
3
|
import { ChannelToggles, FormattedNotification, NotificationBrand, NotificationEvent, SystemNotification } from "./types.mjs";
|
|
4
|
-
//#region src/adapters/notifier/format.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* Format a project event into subject/text/markdown/html using brand +
|
|
7
6
|
* channel toggles. Pure (no I/O) so settings preview and senders share it.
|
|
@@ -9,6 +8,5 @@ import { ChannelToggles, FormattedNotification, NotificationBrand, NotificationE
|
|
|
9
8
|
declare function formatNotification(event: NotificationEvent, brand?: NotificationBrand, toggles?: ChannelToggles): FormattedNotification;
|
|
10
9
|
/** Format a site-wide admin alert (no project scope, always verbose). */
|
|
11
10
|
declare function formatSystemNotification(event: SystemNotification, brand?: NotificationBrand): FormattedNotification;
|
|
12
|
-
//#endregion
|
|
13
11
|
export { formatNotification, formatSystemNotification };
|
|
14
12
|
//# sourceMappingURL=format.d.mts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
|
|
4
|
+
// src/adapters/notifier/format.ts
|
|
5
5
|
/** Escape HTML special chars in free-form values (commit messages, names). */
|
|
6
6
|
function esc(value) {
|
|
7
7
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
@@ -90,7 +90,6 @@ function formatSystemNotification(event, brand) {
|
|
|
90
90
|
subjectPrefix: "[Shelf sys]"
|
|
91
91
|
});
|
|
92
92
|
}
|
|
93
|
-
//#endregion
|
|
94
93
|
export { formatNotification, formatSystemNotification };
|
|
95
94
|
|
|
96
95
|
//# sourceMappingURL=format.mjs.map
|
|
@@ -4,7 +4,6 @@ import { FormattedNotification, NotificationChannel, NotifierState, NotifierStat
|
|
|
4
4
|
import { Adapter, AdapterMetadata } from "../../adapter/metadata.mjs";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { Logger } from "pino";
|
|
7
|
-
//#region src/adapters/notifier/provider.d.ts
|
|
8
7
|
/** How a provider delivers: `message` sends once, `state` makes a host show a named state. */
|
|
9
8
|
type NotifierDelivery = "message" | "state";
|
|
10
9
|
/** Notifier-specific metadata (adds logo + config validation schema). */
|
|
@@ -73,6 +72,5 @@ interface StateNotifierAdapter extends Adapter<StateNotifierMetadata> {
|
|
|
73
72
|
type NotifierAdapter = MessageNotifierAdapter | StateNotifierAdapter;
|
|
74
73
|
/** True when the provider delivers state (git hosts) rather than messages. */
|
|
75
74
|
declare function isStateProvider(provider: NotifierProvider): provider is StateNotifierProvider;
|
|
76
|
-
//#endregion
|
|
77
75
|
export { MessageNotifierAdapter, MessageNotifierMetadata, MessageNotifierProvider, NotifierAdapter, NotifierDelivery, NotifierMetadata, NotifierProvider, SetStateInput, StateNotifierAdapter, StateNotifierMetadata, StateNotifierProvider, isStateProvider };
|
|
78
76
|
//# sourceMappingURL=provider.d.mts.map
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
|
|
4
|
+
// src/adapters/notifier/provider.ts
|
|
5
5
|
/** True when the provider delivers state (git hosts) rather than messages. */
|
|
6
6
|
function isStateProvider(provider) {
|
|
7
7
|
return provider.metadata.delivery === "state";
|
|
8
8
|
}
|
|
9
|
-
//#endregion
|
|
10
9
|
export { isStateProvider };
|
|
11
10
|
|
|
12
11
|
//# sourceMappingURL=provider.mjs.map
|
|
@@ -4,7 +4,6 @@ import { Logger } from "../../logger.mjs";
|
|
|
4
4
|
import { NotificationChannel } from "./types.mjs";
|
|
5
5
|
import { StateNotifierProvider } from "./provider.mjs";
|
|
6
6
|
import { BuildStateSet } from "./build-state.mjs";
|
|
7
|
-
//#region src/adapters/notifier/state.d.ts
|
|
8
7
|
/** Default status-name prefix of a git channel (`<prefix>/testing`). */
|
|
9
8
|
declare const DEFAULT_STATUS_PREFIX = "storyshelf";
|
|
10
9
|
/** The channel's status-name prefix: its `prefix` config, else {@link DEFAULT_STATUS_PREFIX}. */
|
|
@@ -19,6 +18,5 @@ declare function writeBuildState(provider: StateNotifierProvider, channel: Notif
|
|
|
19
18
|
states: BuildStateSet;
|
|
20
19
|
logger?: Logger | undefined;
|
|
21
20
|
}): Promise<void>;
|
|
22
|
-
//#endregion
|
|
23
21
|
export { DEFAULT_STATUS_PREFIX, channelPrefix, writeBuildState };
|
|
24
22
|
//# sourceMappingURL=state.d.mts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
|
|
4
|
+
// src/adapters/notifier/state.ts
|
|
5
5
|
/** Default status-name prefix of a git channel (`<prefix>/testing`). */
|
|
6
6
|
const DEFAULT_STATUS_PREFIX = "storyshelf";
|
|
7
7
|
/** The channel's status-name prefix: its `prefix` config, else {@link DEFAULT_STATUS_PREFIX}. */
|
|
@@ -60,7 +60,6 @@ async function writeBuildState(provider, channel, write) {
|
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
|
-
//#endregion
|
|
64
63
|
export { DEFAULT_STATUS_PREFIX, channelPrefix, writeBuildState };
|
|
65
64
|
|
|
66
65
|
//# sourceMappingURL=state.mjs.map
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
|
-
//#region src/adapters/notifier/types.d.ts
|
|
4
3
|
/** Shared event envelope for human-facing notifications. */
|
|
5
4
|
interface NotificationEvent {
|
|
6
5
|
event: string;
|
|
@@ -100,6 +99,5 @@ interface SystemNotification {
|
|
|
100
99
|
}
|
|
101
100
|
/** Site-wide events for admins (new users, failures, purge). */
|
|
102
101
|
type SystemEventName = "sys:user-created" | "sys:invite-issued" | "sys:auth-failed" | "sys:capture-failed" | "sys:purge-completed" | "sys:notify-failed" | "sys:ai-budget";
|
|
103
|
-
//#endregion
|
|
104
102
|
export { ChannelOutcome, ChannelToggles, FormattedNotification, NotificationBrand, NotificationChannel, NotificationEvent, NotifierState, NotifierStateTarget, StateAnnotation, SystemEventName, SystemNotification };
|
|
105
103
|
//# sourceMappingURL=types.d.mts.map
|
|
@@ -1,51 +1,138 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
3
|
import { BrowserName, CaptureTarget, TargetReach } from "./capture-target.mjs";
|
|
4
|
-
//#region src/adapters/remote-browser.d.ts
|
|
5
4
|
/** How a runner talks to the service: Playwright's own protocol, or the Chrome DevTools Protocol. */
|
|
6
5
|
type RemoteProtocol = "playwright" | "cdp";
|
|
7
|
-
/** Where and how to connect
|
|
6
|
+
/** Where and how to connect. A function `connect` is called per connection, so a token can be fresh. */
|
|
8
7
|
interface RemoteConnection {
|
|
9
|
-
protocol: RemoteProtocol;
|
|
10
8
|
/** `ws(s)://…` (CDP also accepts `http(s)://…`). May carry credentials: it is never logged. */
|
|
11
9
|
endpoint: string;
|
|
12
10
|
/** Request headers for the handshake, such as `authorization`. Never logged. */
|
|
13
11
|
headers?: Record<string, string>;
|
|
12
|
+
}
|
|
13
|
+
/** A connection a function `connect` returns, with what `release` needs back. */
|
|
14
|
+
interface RemoteLeasedConnection<TLease = unknown> extends RemoteConnection {
|
|
15
|
+
/** Opaque to the runner, handed to `release` once and never logged (it may carry credentials). */
|
|
16
|
+
lease?: TLease | undefined;
|
|
17
|
+
}
|
|
18
|
+
/** What a run is doing when it connects. */
|
|
19
|
+
type RemotePurpose = "render" | "extract" | "tests";
|
|
20
|
+
/** How a leased connection ended, handed to `release`. */
|
|
21
|
+
type RemoteLeaseOutcome = "finished" | "failed" | "cancelled";
|
|
22
|
+
/** What a function `connect` is told about the connection it is asked for. */
|
|
23
|
+
interface RemoteConnectContext {
|
|
24
|
+
engine: BrowserName;
|
|
25
|
+
/** The StoryShelf build; the same for every shard of it. */
|
|
26
|
+
buildId: string;
|
|
27
|
+
/** The shard's own id; the build id when the render is not sharded. */
|
|
28
|
+
runId: string;
|
|
29
|
+
/** The shard's 1-based position in the build's plan; `undefined` (never 0) when not sharded. */
|
|
30
|
+
shardIndex?: number | undefined;
|
|
31
|
+
/** `render` for a build or shard, `extract` for the parameter pre-pass, `tests` for `.test()` entries. */
|
|
32
|
+
purpose: RemotePurpose;
|
|
33
|
+
/** This connection's attempt, from 1; each retry connects anew. */
|
|
34
|
+
attempt: number;
|
|
14
35
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
36
|
+
* Aborts while `connect` is pending: on `timeouts.acquireMs`, a cancel (user, build, render
|
|
37
|
+
* budget) and shutdown. Spent once `connect` resolves.
|
|
17
38
|
*/
|
|
18
|
-
|
|
39
|
+
signal: AbortSignal;
|
|
40
|
+
}
|
|
41
|
+
/** What `release` is told about how the connection's render went. */
|
|
42
|
+
interface RemoteReleaseInfo {
|
|
43
|
+
outcome: RemoteLeaseOutcome;
|
|
44
|
+
/** Why it failed, for `failed`. Carries no credentials, but may carry provider text. */
|
|
45
|
+
error?: unknown;
|
|
19
46
|
}
|
|
20
47
|
/** What {@link defineRemote} takes: what `remote()` exposes to operators. */
|
|
21
|
-
interface RemoteOptions {
|
|
48
|
+
interface RemoteOptions<TLease = unknown> {
|
|
22
49
|
/** The target name half (`chromium@<name>`): lowercase letters, digits and hyphens, at most 32. */
|
|
23
50
|
name: string;
|
|
24
51
|
/** The engines to offer (a subset of what the service has). */
|
|
25
52
|
engines: readonly BrowserName[];
|
|
26
|
-
/**
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
|
|
53
|
+
/** Parallel sessions the plan allows: the page cap of the target, per process (decision 4). A function is read live. */
|
|
54
|
+
sessions: number | (() => number);
|
|
55
|
+
/** Maximum concurrent pages on each browser connection; independent of the shared session gate. */
|
|
56
|
+
maxPagesPerConnection?: number | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Maximum connections this process holds to the service at once (default: no cap). A separate
|
|
59
|
+
* limit from `sessions` (pages) and `maxPagesPerConnection`: a connection counts from the moment
|
|
60
|
+
* it is requested until its render has ended, a shared one once. Renders over the cap wait, and a
|
|
61
|
+
* cancel or shutdown ends the wait. Per process; the deployment-wide cap is another issue.
|
|
62
|
+
*/
|
|
63
|
+
maxConnections?: number | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* Marks a vendor preset as experimental: written against the provider's documentation and not
|
|
66
|
+
* verified against live infrastructure. Reported in the run's sizing log and the docs; it changes
|
|
67
|
+
* no behavior.
|
|
68
|
+
*/
|
|
69
|
+
experimental?: boolean | undefined;
|
|
70
|
+
/** Declared, never inferred: how the runner talks to the service. */
|
|
71
|
+
protocol: RemoteProtocol;
|
|
72
|
+
/**
|
|
73
|
+
* Declared, never inferred: `loopback` when the remote browser can route back to this host
|
|
74
|
+
* (Playwright's `exposeNetwork: "<loopback>"`), else `url` (it needs a signed Storybook URL).
|
|
75
|
+
*/
|
|
76
|
+
reach: TargetReach;
|
|
77
|
+
/**
|
|
78
|
+
* The connection: an object for a fixed service (validated at creation), or a function called
|
|
79
|
+
* for every connection (sync or async) to wake or start something, pick a server per shard, or
|
|
80
|
+
* mint a token. A throw is retried and ends as a `TargetLaunchError`.
|
|
81
|
+
*/
|
|
82
|
+
connect: RemoteConnection | ((context: RemoteConnectContext) => RemoteLeasedConnection<TLease> | Promise<RemoteLeasedConnection<TLease>>);
|
|
83
|
+
/**
|
|
84
|
+
* Called once for every connection `connect` returned a `lease` for, after the render that used
|
|
85
|
+
* it finished, failed or was cancelled (and for leases still held at shutdown, `cancelled`).
|
|
86
|
+
* Only valid with a function `connect`. A throw or a timeout (`timeouts.releaseMs`) is logged and
|
|
87
|
+
* never fails the render.
|
|
88
|
+
*/
|
|
89
|
+
release?: ((lease: TLease, info: RemoteReleaseInfo) => Promise<void> | void) | undefined;
|
|
30
90
|
/** OS the service renders on, reported when the page cannot tell (`linux-x64`, `windows`, …). */
|
|
31
91
|
platform?: string | undefined;
|
|
32
|
-
/** Connect retries
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
92
|
+
/** Connect retries: `attempts` after the first (default 3), `backoffMs` doubling (default 500). */
|
|
93
|
+
retry?: {
|
|
94
|
+
attempts?: number | undefined;
|
|
95
|
+
backoffMs?: number | undefined;
|
|
96
|
+
} | undefined;
|
|
36
97
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
98
|
+
* `acquireMs` bounds the `connect` function, `connectMs` the handshake after it (a farm with a
|
|
99
|
+
* client cap queues extra connections, so a busy farm shows up as this timeout), `releaseMs`
|
|
100
|
+
* the `release` hook. All default to 30 000. `probeMs` bounds measuring the browser's
|
|
101
|
+
* environment once connected (default 15 000): a probe that overruns leaves the environment
|
|
102
|
+
* unknown with a warning, and a cancel stops it at once.
|
|
39
103
|
*/
|
|
40
|
-
|
|
104
|
+
timeouts?: {
|
|
105
|
+
acquireMs?: number | undefined;
|
|
106
|
+
connectMs?: number | undefined;
|
|
107
|
+
releaseMs?: number | undefined;
|
|
108
|
+
probeMs?: number | undefined;
|
|
109
|
+
} | undefined;
|
|
41
110
|
}
|
|
42
111
|
/** One remote service, validated: an entry of a runner's `browsers`. */
|
|
43
|
-
interface RemoteBrowser
|
|
112
|
+
interface RemoteBrowser {
|
|
44
113
|
readonly kind: "remote";
|
|
114
|
+
readonly name: string;
|
|
115
|
+
readonly engines: readonly BrowserName[];
|
|
116
|
+
readonly sessions: number | (() => number);
|
|
117
|
+
readonly maxPagesPerConnection?: number | undefined;
|
|
118
|
+
readonly maxConnections?: number | undefined;
|
|
119
|
+
/** A vendor preset not verified against live infrastructure (see {@link RemoteOptions.experimental}). */
|
|
120
|
+
readonly experimental?: boolean | undefined;
|
|
121
|
+
readonly protocol: RemoteProtocol;
|
|
122
|
+
readonly reach: TargetReach;
|
|
123
|
+
readonly connect: RemoteOptions["connect"];
|
|
124
|
+
readonly release: ((lease: never, info: RemoteReleaseInfo) => Promise<void> | void) | undefined;
|
|
45
125
|
readonly platform: string | undefined;
|
|
46
|
-
readonly
|
|
47
|
-
|
|
48
|
-
|
|
126
|
+
readonly retry: {
|
|
127
|
+
readonly attempts: number;
|
|
128
|
+
readonly backoffMs: number;
|
|
129
|
+
};
|
|
130
|
+
readonly timeouts: {
|
|
131
|
+
readonly acquireMs: number;
|
|
132
|
+
readonly connectMs: number;
|
|
133
|
+
readonly releaseMs: number;
|
|
134
|
+
readonly probeMs: number;
|
|
135
|
+
};
|
|
49
136
|
}
|
|
50
137
|
/** What a runner supports, which {@link defineRemote} checks the service against. */
|
|
51
138
|
interface RemoteSupport {
|
|
@@ -67,21 +154,23 @@ interface RemoteTarget {
|
|
|
67
154
|
}
|
|
68
155
|
/**
|
|
69
156
|
* Validate a remote service eagerly, so a mistake fails when the runner is created and not when the
|
|
70
|
-
* first build runs: the name, the sessions, the engines against what the
|
|
71
|
-
*
|
|
157
|
+
* first build runs: unknown or moved options, the name, the sessions, the engines against what the
|
|
158
|
+
* runner drives, the protocol, the reach, and a fixed connection (endpoint scheme).
|
|
72
159
|
*/
|
|
73
|
-
declare function defineRemote(options: RemoteOptions
|
|
160
|
+
declare function defineRemote<TLease = unknown>(options: RemoteOptions<TLease>, support: RemoteSupport): RemoteBrowser;
|
|
74
161
|
/** The targets a service offers, in the order of its `engines`. */
|
|
75
162
|
declare function remoteTargetsOf(remote: RemoteBrowser): RemoteTarget[];
|
|
76
|
-
/** `loopback` when the remote browser can route back to this host, else it needs a URL (decision 5). */
|
|
77
|
-
declare function reachOf(connection: RemoteConnection): TargetReach;
|
|
78
163
|
/** Whether a value is a remote service entry. */
|
|
79
164
|
declare function isRemoteBrowser(value: unknown): value is RemoteBrowser;
|
|
165
|
+
/** Whether `endpoint` has a scheme `protocol` can open. */
|
|
166
|
+
declare function hasSchemeFor(protocol: RemoteProtocol, endpoint: string): boolean;
|
|
80
167
|
/**
|
|
81
168
|
* Remove what identifies the connection from text surfaced to operators: the endpoint (it can carry
|
|
82
|
-
* a key in its path or query), its host and path, its query values
|
|
169
|
+
* a key in its path or query), its host and path, its query values, every header value and every
|
|
170
|
+
* value inside the lease (non-strings are stringified).
|
|
83
171
|
*/
|
|
84
|
-
declare function scrubConnection(text: string, connection: RemoteConnection
|
|
85
|
-
|
|
86
|
-
|
|
172
|
+
declare function scrubConnection(text: string, connection: RemoteConnection & {
|
|
173
|
+
lease?: unknown;
|
|
174
|
+
}): string;
|
|
175
|
+
export { RemoteBrowser, RemoteConnectContext, RemoteConnection, RemoteLeaseOutcome, RemoteLeasedConnection, RemoteOptions, RemoteProtocol, RemotePurpose, RemoteReleaseInfo, RemoteSupport, RemoteTarget, defineRemote, hasSchemeFor, isRemoteBrowser, remoteTargetsOf, scrubConnection };
|
|
87
176
|
//# sourceMappingURL=remote-browser.d.mts.map
|
|
@@ -2,12 +2,14 @@ import "node:path";
|
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { TARGET_NAME_PATTERN, isBrowserName } from "./capture-target.mjs";
|
|
5
|
-
|
|
5
|
+
// src/adapters/remote-browser.ts
|
|
6
6
|
/**
|
|
7
7
|
* Remote browsers (ADR 0007, decisions 1 and 3): one service a runner connects to instead of
|
|
8
8
|
* launching a browser, expanding to one `<engine>@<name>` target per engine. Presets are plain
|
|
9
9
|
* configuration over {@link defineRemote}; the runners add only what they can drive.
|
|
10
10
|
*/
|
|
11
|
+
/** Default bound on measuring a connected remote browser's environment (`timeouts.probeMs`). */
|
|
12
|
+
const DEFAULT_PROBE_MS = 15e3;
|
|
11
13
|
const CDP_ENGINES = /* @__PURE__ */ new Set(["chromium", "chrome"]);
|
|
12
14
|
const SCHEMES = {
|
|
13
15
|
playwright: ["ws:", "wss:"],
|
|
@@ -18,27 +20,63 @@ const SCHEMES = {
|
|
|
18
20
|
"https:"
|
|
19
21
|
]
|
|
20
22
|
};
|
|
23
|
+
/** Options 0.7 took that 0.8 renamed or removed, with what replaces them (a loud migration error). */
|
|
24
|
+
const MOVED = {
|
|
25
|
+
acquire: "merge it into `connect` (a function that receives a context)",
|
|
26
|
+
exposeNetwork: "declare `reach: \"loopback\"` on the service instead",
|
|
27
|
+
connectRetries: "use `retry: { attempts }`",
|
|
28
|
+
connectBackoffMs: "use `retry: { backoffMs }`",
|
|
29
|
+
connectTimeoutMs: "use `timeouts: { connectMs }`",
|
|
30
|
+
acquireTimeoutMs: "use `timeouts: { acquireMs }`"
|
|
31
|
+
};
|
|
32
|
+
const KNOWN = /* @__PURE__ */ new Set([
|
|
33
|
+
"name",
|
|
34
|
+
"engines",
|
|
35
|
+
"sessions",
|
|
36
|
+
"maxPagesPerConnection",
|
|
37
|
+
"maxConnections",
|
|
38
|
+
"experimental",
|
|
39
|
+
"protocol",
|
|
40
|
+
"reach",
|
|
41
|
+
"connect",
|
|
42
|
+
"release",
|
|
43
|
+
"platform",
|
|
44
|
+
"retry",
|
|
45
|
+
"timeouts"
|
|
46
|
+
]);
|
|
21
47
|
/**
|
|
22
48
|
* Validate a remote service eagerly, so a mistake fails when the runner is created and not when the
|
|
23
|
-
* first build runs: the name, the sessions, the engines against what the
|
|
24
|
-
*
|
|
49
|
+
* first build runs: unknown or moved options, the name, the sessions, the engines against what the
|
|
50
|
+
* runner drives, the protocol, the reach, and a fixed connection (endpoint scheme).
|
|
25
51
|
*/
|
|
26
52
|
function defineRemote(options, support) {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
for (const engine of options.engines) checkConnection(label, options, engine, support);
|
|
53
|
+
const plain = options;
|
|
54
|
+
checkOptions(plain, support);
|
|
55
|
+
checkEngines(plain, support);
|
|
56
|
+
checkProtocol(plain, support);
|
|
32
57
|
return {
|
|
33
58
|
kind: "remote",
|
|
34
59
|
name: options.name,
|
|
35
60
|
engines: [...options.engines],
|
|
36
|
-
connect: options.connect,
|
|
37
61
|
sessions: options.sessions,
|
|
62
|
+
maxPagesPerConnection: options.maxPagesPerConnection,
|
|
63
|
+
maxConnections: options.maxConnections,
|
|
64
|
+
experimental: options.experimental === true,
|
|
65
|
+
protocol: options.protocol,
|
|
66
|
+
reach: options.reach,
|
|
67
|
+
connect: options.connect,
|
|
68
|
+
release: options.release,
|
|
38
69
|
platform: options.platform,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
70
|
+
retry: {
|
|
71
|
+
attempts: options.retry?.attempts ?? 3,
|
|
72
|
+
backoffMs: options.retry?.backoffMs ?? 500
|
|
73
|
+
},
|
|
74
|
+
timeouts: {
|
|
75
|
+
acquireMs: options.timeouts?.acquireMs ?? 3e4,
|
|
76
|
+
connectMs: options.timeouts?.connectMs ?? 3e4,
|
|
77
|
+
releaseMs: options.timeouts?.releaseMs ?? 3e4,
|
|
78
|
+
probeMs: options.timeouts?.probeMs ?? 15e3
|
|
79
|
+
}
|
|
42
80
|
};
|
|
43
81
|
}
|
|
44
82
|
/** The targets a service offers, in the order of its `engines`. */
|
|
@@ -48,23 +86,36 @@ function remoteTargetsOf(remote) {
|
|
|
48
86
|
target: `${engine}@${remote.name}`,
|
|
49
87
|
engine,
|
|
50
88
|
remote,
|
|
51
|
-
reach:
|
|
89
|
+
reach: remote.reach
|
|
52
90
|
}));
|
|
53
91
|
}
|
|
54
|
-
/** `loopback` when the remote browser can route back to this host, else it needs a URL (decision 5). */
|
|
55
|
-
function reachOf(connection) {
|
|
56
|
-
return connection.protocol === "playwright" && connection.exposeNetwork !== void 0 ? "loopback" : "url";
|
|
57
|
-
}
|
|
58
92
|
/** Whether a value is a remote service entry. */
|
|
59
93
|
function isRemoteBrowser(value) {
|
|
60
94
|
return typeof value === "object" && value !== null && value.kind === "remote";
|
|
61
95
|
}
|
|
96
|
+
/** Whether `endpoint` has a scheme `protocol` can open. */
|
|
97
|
+
function hasSchemeFor(protocol, endpoint) {
|
|
98
|
+
try {
|
|
99
|
+
return SCHEMES[protocol].includes(new URL(endpoint).protocol);
|
|
100
|
+
} catch {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** The schemes `protocol` accepts, for messages. */
|
|
105
|
+
function schemesOf(protocol) {
|
|
106
|
+
return SCHEMES[protocol].join(" or ");
|
|
107
|
+
}
|
|
62
108
|
/**
|
|
63
109
|
* Remove what identifies the connection from text surfaced to operators: the endpoint (it can carry
|
|
64
|
-
* a key in its path or query), its host and path, its query values
|
|
110
|
+
* a key in its path or query), its host and path, its query values, every header value and every
|
|
111
|
+
* value inside the lease (non-strings are stringified).
|
|
65
112
|
*/
|
|
66
113
|
function scrubConnection(text, connection) {
|
|
67
|
-
const secrets = [
|
|
114
|
+
const secrets = [
|
|
115
|
+
connection.endpoint,
|
|
116
|
+
...Object.values(connection.headers ?? {}),
|
|
117
|
+
...leaseSecrets(connection.lease)
|
|
118
|
+
];
|
|
68
119
|
try {
|
|
69
120
|
const url = new URL(connection.endpoint);
|
|
70
121
|
secrets.push(url.password, ...url.searchParams.values(), decodeURIComponent(url.search), `${url.protocol}//${url.host}${url.pathname}`, url.host, url.pathname);
|
|
@@ -73,6 +124,50 @@ function scrubConnection(text, connection) {
|
|
|
73
124
|
for (const secret of secrets.filter((value) => value.length >= 4).toSorted((a, b) => b.length - a.length)) scrubbed = scrubbed.replaceAll(secret, "[redacted]");
|
|
74
125
|
return scrubbed;
|
|
75
126
|
}
|
|
127
|
+
/** Every value of a lease, as text: a string whole, anything else stringified, objects walked. */
|
|
128
|
+
function leaseSecrets(lease, depth = 0) {
|
|
129
|
+
if (lease === void 0 || lease === null) return [];
|
|
130
|
+
if (typeof lease === "object") return depth > 3 ? [] : Object.values(lease).flatMap((value) => leaseSecrets(value, depth + 1));
|
|
131
|
+
return typeof lease === "string" || typeof lease === "number" || typeof lease === "boolean" ? [String(lease)] : [];
|
|
132
|
+
}
|
|
133
|
+
function checkKnownKeys(options, where) {
|
|
134
|
+
for (const key of Object.keys(options)) if (!KNOWN.has(key)) {
|
|
135
|
+
const moved = MOVED[key];
|
|
136
|
+
throw new Error(moved === void 0 ? `${where}: unknown option "${key}"` : `${where}: option "${key}" is gone in 0.8: ${moved}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function checkOptions(options, { label }) {
|
|
140
|
+
const where = `${label} "${options.name}"`;
|
|
141
|
+
checkKnownKeys(options, where);
|
|
142
|
+
if (!TARGET_NAME_PATTERN.test(options.name)) throw new Error(`${label}: name "${options.name}" must be lowercase letters, digits and hyphens, start with a letter or digit and be at most 32 characters`);
|
|
143
|
+
checkSessions(options.sessions, where);
|
|
144
|
+
checkPositiveInteger("maxPagesPerConnection", options.maxPagesPerConnection, where);
|
|
145
|
+
checkPositiveInteger("maxConnections", options.maxConnections, where);
|
|
146
|
+
checkNumbers(options, where);
|
|
147
|
+
if (options.release !== void 0 && typeof options.connect !== "function") throw new Error(`${where}: release needs a function connect (a fixed connection has no lease)`);
|
|
148
|
+
}
|
|
149
|
+
/** An optional limit: absent, or a whole number of at least 1. */
|
|
150
|
+
function checkPositiveInteger(name, value, where) {
|
|
151
|
+
if (value !== void 0 && (!Number.isInteger(value) || value < 1)) throw new Error(`${where}: ${name} must be a positive integer`);
|
|
152
|
+
}
|
|
153
|
+
function checkSessions(sessions, where) {
|
|
154
|
+
const value = typeof sessions === "function" ? sessions() : sessions;
|
|
155
|
+
if (!Number.isInteger(value) || value < 1) throw new Error(`${where}: sessions must be a positive integer`);
|
|
156
|
+
}
|
|
157
|
+
function checkNumbers(options, where) {
|
|
158
|
+
const entries = [
|
|
159
|
+
["retry.attempts", options.retry?.attempts],
|
|
160
|
+
["timeouts.acquireMs", options.timeouts?.acquireMs],
|
|
161
|
+
["timeouts.connectMs", options.timeouts?.connectMs],
|
|
162
|
+
["timeouts.releaseMs", options.timeouts?.releaseMs],
|
|
163
|
+
["timeouts.probeMs", options.timeouts?.probeMs],
|
|
164
|
+
["retry.backoffMs", options.retry?.backoffMs]
|
|
165
|
+
];
|
|
166
|
+
for (const [name, value] of entries) {
|
|
167
|
+
const allowed = name === "retry.attempts" || name === "retry.backoffMs" ? 0 : 1;
|
|
168
|
+
if (value !== void 0 && (!Number.isInteger(value) || value < allowed)) throw new Error(`${where}: ${name} must be an integer of at least ${allowed}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
76
171
|
function checkEngines(options, support) {
|
|
77
172
|
const where = `${support.label} "${options.name}"`;
|
|
78
173
|
if (options.engines.length === 0) throw new Error(`${where}: engines must list at least one engine`);
|
|
@@ -82,22 +177,27 @@ function checkEngines(options, support) {
|
|
|
82
177
|
if (options.engines.indexOf(engine) !== index) throw new Error(`${where}: engine "${engine}" is listed twice`);
|
|
83
178
|
}
|
|
84
179
|
}
|
|
85
|
-
function
|
|
86
|
-
const where = `${label} "${options.name}"
|
|
87
|
-
|
|
88
|
-
if (
|
|
89
|
-
if (
|
|
90
|
-
if (connection.protocol === "cdp" && connection.exposeNetwork !== void 0) throw new Error(`${where}: exposeNetwork needs the "playwright" protocol`);
|
|
91
|
-
if (!SCHEMES[connection.protocol].some((scheme) => isScheme(connection.endpoint, scheme))) throw new Error(`${where}: the endpoint must be a ${SCHEMES[connection.protocol].join(" or ")} URL`);
|
|
180
|
+
function checkProtocol(options, support) {
|
|
181
|
+
const where = `${support.label} "${options.name}"`;
|
|
182
|
+
checkDeclared(options, support, where);
|
|
183
|
+
if (options.protocol === "cdp") checkCdp(options, where);
|
|
184
|
+
if (typeof options.connect !== "function") checkFixedConnection(options.connect, options.protocol, where);
|
|
92
185
|
}
|
|
93
|
-
function
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
186
|
+
function checkDeclared(options, support, where) {
|
|
187
|
+
if (options.protocol === void 0 || options.reach === void 0) throw new Error(`${where}: "protocol" and "reach" are required in 0.8 (declare how the runner talks to the service and whether its browser can reach this host)`);
|
|
188
|
+
if (!support.protocols.includes(options.protocol)) throw new Error(`${where}: protocol "${options.protocol}" is not supported by this runner (it speaks ${support.protocols.join(", ")})`);
|
|
189
|
+
if (options.reach !== "loopback" && options.reach !== "url") throw new Error(`${where}: reach must be "loopback" or "url"`);
|
|
190
|
+
}
|
|
191
|
+
function checkCdp(options, where) {
|
|
192
|
+
const bad = options.engines.find((engine) => !CDP_ENGINES.has(engine));
|
|
193
|
+
if (bad !== void 0) throw new Error(`${where}: the CDP protocol drives Chromium only, not "${bad}"`);
|
|
194
|
+
if (options.reach === "loopback") throw new Error(`${where}: reach "loopback" needs the "playwright" protocol`);
|
|
195
|
+
}
|
|
196
|
+
function checkFixedConnection(connection, protocol, where) {
|
|
197
|
+
const extra = Object.keys(connection).filter((key) => key !== "endpoint" && key !== "headers");
|
|
198
|
+
if (extra.length > 0) throw new Error(`${where}: connect has unknown field(s) ${extra.join(", ")}`);
|
|
199
|
+
if (!hasSchemeFor(protocol, connection.endpoint)) throw new Error(`${where}: the endpoint must be a ${schemesOf(protocol)} URL`);
|
|
99
200
|
}
|
|
100
|
-
|
|
101
|
-
export { defineRemote, isRemoteBrowser, reachOf, remoteTargetsOf, scrubConnection };
|
|
201
|
+
export { DEFAULT_PROBE_MS, defineRemote, hasSchemeFor, isRemoteBrowser, remoteTargetsOf, schemesOf, scrubConnection };
|
|
102
202
|
|
|
103
203
|
//# sourceMappingURL=remote-browser.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-browser.mjs","names":[],"sources":["../../src/adapters/remote-browser.ts"],"sourcesContent":["/**\n * Remote browsers (ADR 0007, decisions 1 and 3): one service a runner connects to instead of\n * launching a browser, expanding to one `<engine>@<name>` target per engine. Presets are plain\n * configuration over {@link defineRemote}; the runners add only what they can drive.\n */\nimport {\n TARGET_NAME_PATTERN,\n isBrowserName,\n type BrowserName,\n type CaptureTarget,\n type TargetReach,\n} from \"./capture-target.ts\";\n\n/** How a runner talks to the service: Playwright's own protocol, or the Chrome DevTools Protocol. */\nexport type RemoteProtocol = \"playwright\" | \"cdp\";\n\n/** Where and how to connect for one engine. Re-evaluated on every connect, so a token can be fresh. */\nexport interface RemoteConnection {\n protocol: RemoteProtocol;\n /** `ws(s)://…` (CDP also accepts `http(s)://…`). May carry credentials: it is never logged. */\n endpoint: string;\n /** Request headers for the handshake, such as `authorization`. Never logged. */\n headers?: Record<string, string>;\n /**\n * Playwright `connect` only: `\"<loopback>\"` routes the remote browser's requests for `127.0.0.1`\n * back through the connection, so the loopback Storybook server works unchanged (decision 5).\n */\n exposeNetwork?: string;\n}\n\n/** What {@link defineRemote} takes: what `remote()` exposes to operators. */\nexport interface RemoteOptions {\n /** The target name half (`chromium@<name>`): lowercase letters, digits and hyphens, at most 32. */\n name: string;\n /** The engines to offer (a subset of what the service has). */\n engines: readonly BrowserName[];\n /** The connection for an engine. Pure: it is called once at creation to validate it. */\n connect: (engine: BrowserName) => RemoteConnection;\n /** Parallel sessions the plan allows: the page cap of the target, per process (decision 4). */\n sessions: number;\n /** OS the service renders on, reported when the page cannot tell (`linux-x64`, `windows`, …). */\n platform?: string | undefined;\n /** Connect retries before the render fails (default 3). */\n connectRetries?: number | undefined;\n /** First backoff between connect attempts in ms, doubling each time (default 500). */\n connectBackoffMs?: number | undefined;\n /**\n * How long one connect attempt waits in ms (default 30 000). A farm with a client cap queues\n * extra connections instead of refusing them, so a busy farm shows up as this timeout.\n */\n connectTimeoutMs?: number | undefined;\n}\n\n/** One remote service, validated: an entry of a runner's `browsers`. */\nexport interface RemoteBrowser extends Required<\n Pick<RemoteOptions, \"name\" | \"engines\" | \"connect\" | \"sessions\">\n> {\n readonly kind: \"remote\";\n readonly platform: string | undefined;\n readonly connectRetries: number;\n readonly connectBackoffMs: number;\n readonly connectTimeoutMs: number;\n}\n\n/** What a runner supports, which {@link defineRemote} checks the service against. */\nexport interface RemoteSupport {\n /** Engines the runner can drive. */\n engines: readonly BrowserName[];\n /** Protocols the runner can speak. */\n protocols: readonly RemoteProtocol[];\n /** Shown in errors, for example `remote()`. */\n label: string;\n}\n\n/** One target a remote service offers. */\nexport interface RemoteTarget {\n kind: \"remote\";\n target: CaptureTarget;\n engine: BrowserName;\n remote: RemoteBrowser;\n /** How the browser reaches the Storybook. */\n reach: TargetReach;\n}\n\nconst CDP_ENGINES = new Set<BrowserName>([\"chromium\", \"chrome\"]);\nconst SCHEMES: Record<RemoteProtocol, readonly string[]> = {\n playwright: [\"ws:\", \"wss:\"],\n cdp: [\"ws:\", \"wss:\", \"http:\", \"https:\"],\n};\n\n/**\n * Validate a remote service eagerly, so a mistake fails when the runner is created and not when the\n * first build runs: the name, the sessions, the engines against what the runner drives, and the\n * connection of every engine (protocol, endpoint scheme, CDP being Chromium only).\n */\nexport function defineRemote(options: RemoteOptions, support: RemoteSupport): RemoteBrowser {\n const { label } = support;\n if (!TARGET_NAME_PATTERN.test(options.name)) {\n throw new Error(\n `${label}: name \"${options.name}\" must be lowercase letters, digits and hyphens, start with a letter or digit and be at most 32 characters`,\n );\n }\n if (!Number.isInteger(options.sessions) || options.sessions < 1) {\n throw new Error(`${label} \"${options.name}\": sessions must be a positive integer`);\n }\n checkEngines(options, support);\n for (const engine of options.engines) {\n checkConnection(label, options, engine, support);\n }\n return {\n kind: \"remote\",\n name: options.name,\n engines: [...options.engines],\n connect: options.connect,\n sessions: options.sessions,\n platform: options.platform,\n connectRetries: options.connectRetries ?? 3,\n connectBackoffMs: options.connectBackoffMs ?? 500,\n connectTimeoutMs: options.connectTimeoutMs ?? 30_000,\n };\n}\n\n/** The targets a service offers, in the order of its `engines`. */\nexport function remoteTargetsOf(remote: RemoteBrowser): RemoteTarget[] {\n return remote.engines.map((engine) => ({\n kind: \"remote\",\n target: `${engine}@${remote.name}`,\n engine,\n remote,\n reach: reachOf(remote.connect(engine)),\n }));\n}\n\n/** `loopback` when the remote browser can route back to this host, else it needs a URL (decision 5). */\nexport function reachOf(connection: RemoteConnection): TargetReach {\n return connection.protocol === \"playwright\" && connection.exposeNetwork !== undefined\n ? \"loopback\"\n : \"url\";\n}\n\n/** Whether a value is a remote service entry. */\nexport function isRemoteBrowser(value: unknown): value is RemoteBrowser {\n return typeof value === \"object\" && value !== null && (value as RemoteBrowser).kind === \"remote\";\n}\n\n/**\n * Remove what identifies the connection from text surfaced to operators: the endpoint (it can carry\n * a key in its path or query), its host and path, its query values and every header value.\n */\nexport function scrubConnection(text: string, connection: RemoteConnection): string {\n const secrets = [connection.endpoint, ...Object.values(connection.headers ?? {})];\n try {\n const url = new URL(connection.endpoint);\n // Drivers echo the endpoint without its query; the host and a path that acts as a key stay hidden.\n secrets.push(\n url.password,\n ...url.searchParams.values(),\n decodeURIComponent(url.search),\n `${url.protocol}//${url.host}${url.pathname}`,\n url.host,\n url.pathname,\n );\n } catch {\n // An unparsable endpoint is scrubbed whole above.\n }\n let scrubbed = text;\n // Longest first: the endpoint goes before the fragments it contains.\n for (const secret of secrets\n .filter((value) => value.length >= 4)\n .toSorted((a, b) => b.length - a.length)) {\n scrubbed = scrubbed.replaceAll(secret, \"[redacted]\");\n }\n return scrubbed;\n}\n\nfunction checkEngines(options: RemoteOptions, support: RemoteSupport): void {\n const where = `${support.label} \"${options.name}\"`;\n if (options.engines.length === 0) {\n throw new Error(`${where}: engines must list at least one engine`);\n }\n for (const [index, engine] of options.engines.entries()) {\n if (!isBrowserName(engine)) {\n throw new Error(`${where}: unknown engine \"${String(engine)}\"`);\n }\n if (!support.engines.includes(engine)) {\n throw new Error(\n `${where}: this runner cannot drive \"${engine}\" (it drives ${support.engines.join(\", \")})`,\n );\n }\n if (options.engines.indexOf(engine) !== index) {\n throw new Error(`${where}: engine \"${engine}\" is listed twice`);\n }\n }\n}\n\nfunction checkConnection(\n label: string,\n options: RemoteOptions,\n engine: BrowserName,\n support: RemoteSupport,\n): void {\n const where = `${label} \"${options.name}\" (${engine})`;\n const connection = options.connect(engine);\n if (!support.protocols.includes(connection.protocol)) {\n throw new Error(\n `${where}: protocol \"${connection.protocol}\" is not supported by this runner (it speaks ${support.protocols.join(\", \")})`,\n );\n }\n if (connection.protocol === \"cdp\" && !CDP_ENGINES.has(engine)) {\n throw new Error(`${where}: the CDP protocol drives Chromium only, not \"${engine}\"`);\n }\n if (connection.protocol === \"cdp\" && connection.exposeNetwork !== undefined) {\n throw new Error(`${where}: exposeNetwork needs the \"playwright\" protocol`);\n }\n if (!SCHEMES[connection.protocol].some((scheme) => isScheme(connection.endpoint, scheme))) {\n throw new Error(\n `${where}: the endpoint must be a ${SCHEMES[connection.protocol].join(\" or \")} URL`,\n );\n }\n}\n\nfunction isScheme(endpoint: string, scheme: string): boolean {\n try {\n return new URL(endpoint).protocol === scheme;\n } catch {\n return false;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"remote-browser.mjs","names":[],"sources":["../../src/adapters/remote-browser.ts"],"sourcesContent":["/**\n * Remote browsers (ADR 0007, decisions 1 and 3): one service a runner connects to instead of\n * launching a browser, expanding to one `<engine>@<name>` target per engine. Presets are plain\n * configuration over {@link defineRemote}; the runners add only what they can drive.\n */\nimport {\n TARGET_NAME_PATTERN,\n isBrowserName,\n type BrowserName,\n type CaptureTarget,\n type TargetReach,\n} from \"./capture-target.ts\";\n\n/** Default bound on measuring a connected remote browser's environment (`timeouts.probeMs`). */\nexport const DEFAULT_PROBE_MS = 15_000;\n\n/** How a runner talks to the service: Playwright's own protocol, or the Chrome DevTools Protocol. */\nexport type RemoteProtocol = \"playwright\" | \"cdp\";\n\n/** Where and how to connect. A function `connect` is called per connection, so a token can be fresh. */\nexport interface RemoteConnection {\n /** `ws(s)://…` (CDP also accepts `http(s)://…`). May carry credentials: it is never logged. */\n endpoint: string;\n /** Request headers for the handshake, such as `authorization`. Never logged. */\n headers?: Record<string, string>;\n}\n\n/** A connection a function `connect` returns, with what `release` needs back. */\nexport interface RemoteLeasedConnection<TLease = unknown> extends RemoteConnection {\n /** Opaque to the runner, handed to `release` once and never logged (it may carry credentials). */\n lease?: TLease | undefined;\n}\n\n/** What a run is doing when it connects. */\nexport type RemotePurpose = \"render\" | \"extract\" | \"tests\";\n\n/** How a leased connection ended, handed to `release`. */\nexport type RemoteLeaseOutcome = \"finished\" | \"failed\" | \"cancelled\";\n\n/** What a function `connect` is told about the connection it is asked for. */\nexport interface RemoteConnectContext {\n engine: BrowserName;\n /** The StoryShelf build; the same for every shard of it. */\n buildId: string;\n /** The shard's own id; the build id when the render is not sharded. */\n runId: string;\n /** The shard's 1-based position in the build's plan; `undefined` (never 0) when not sharded. */\n shardIndex?: number | undefined;\n /** `render` for a build or shard, `extract` for the parameter pre-pass, `tests` for `.test()` entries. */\n purpose: RemotePurpose;\n /** This connection's attempt, from 1; each retry connects anew. */\n attempt: number;\n /**\n * Aborts while `connect` is pending: on `timeouts.acquireMs`, a cancel (user, build, render\n * budget) and shutdown. Spent once `connect` resolves.\n */\n signal: AbortSignal;\n}\n\n/** What `release` is told about how the connection's render went. */\nexport interface RemoteReleaseInfo {\n outcome: RemoteLeaseOutcome;\n /** Why it failed, for `failed`. Carries no credentials, but may carry provider text. */\n error?: unknown;\n}\n\n/** What {@link defineRemote} takes: what `remote()` exposes to operators. */\nexport interface RemoteOptions<TLease = unknown> {\n /** The target name half (`chromium@<name>`): lowercase letters, digits and hyphens, at most 32. */\n name: string;\n /** The engines to offer (a subset of what the service has). */\n engines: readonly BrowserName[];\n /** Parallel sessions the plan allows: the page cap of the target, per process (decision 4). A function is read live. */\n sessions: number | (() => number);\n /** Maximum concurrent pages on each browser connection; independent of the shared session gate. */\n maxPagesPerConnection?: number | undefined;\n /**\n * Maximum connections this process holds to the service at once (default: no cap). A separate\n * limit from `sessions` (pages) and `maxPagesPerConnection`: a connection counts from the moment\n * it is requested until its render has ended, a shared one once. Renders over the cap wait, and a\n * cancel or shutdown ends the wait. Per process; the deployment-wide cap is another issue.\n */\n maxConnections?: number | undefined;\n /**\n * Marks a vendor preset as experimental: written against the provider's documentation and not\n * verified against live infrastructure. Reported in the run's sizing log and the docs; it changes\n * no behavior.\n */\n experimental?: boolean | undefined;\n /** Declared, never inferred: how the runner talks to the service. */\n protocol: RemoteProtocol;\n /**\n * Declared, never inferred: `loopback` when the remote browser can route back to this host\n * (Playwright's `exposeNetwork: \"<loopback>\"`), else `url` (it needs a signed Storybook URL).\n */\n reach: TargetReach;\n /**\n * The connection: an object for a fixed service (validated at creation), or a function called\n * for every connection (sync or async) to wake or start something, pick a server per shard, or\n * mint a token. A throw is retried and ends as a `TargetLaunchError`.\n */\n connect:\n | RemoteConnection\n | ((\n context: RemoteConnectContext,\n ) => RemoteLeasedConnection<TLease> | Promise<RemoteLeasedConnection<TLease>>);\n /**\n * Called once for every connection `connect` returned a `lease` for, after the render that used\n * it finished, failed or was cancelled (and for leases still held at shutdown, `cancelled`).\n * Only valid with a function `connect`. A throw or a timeout (`timeouts.releaseMs`) is logged and\n * never fails the render.\n */\n release?: ((lease: TLease, info: RemoteReleaseInfo) => Promise<void> | void) | undefined;\n /** OS the service renders on, reported when the page cannot tell (`linux-x64`, `windows`, …). */\n platform?: string | undefined;\n /** Connect retries: `attempts` after the first (default 3), `backoffMs` doubling (default 500). */\n retry?: { attempts?: number | undefined; backoffMs?: number | undefined } | undefined;\n /**\n * `acquireMs` bounds the `connect` function, `connectMs` the handshake after it (a farm with a\n * client cap queues extra connections, so a busy farm shows up as this timeout), `releaseMs`\n * the `release` hook. All default to 30 000. `probeMs` bounds measuring the browser's\n * environment once connected (default 15 000): a probe that overruns leaves the environment\n * unknown with a warning, and a cancel stops it at once.\n */\n timeouts?:\n | {\n acquireMs?: number | undefined;\n connectMs?: number | undefined;\n releaseMs?: number | undefined;\n probeMs?: number | undefined;\n }\n | undefined;\n}\n\n/** One remote service, validated: an entry of a runner's `browsers`. */\nexport interface RemoteBrowser {\n readonly kind: \"remote\";\n readonly name: string;\n readonly engines: readonly BrowserName[];\n readonly sessions: number | (() => number);\n readonly maxPagesPerConnection?: number | undefined;\n readonly maxConnections?: number | undefined;\n /** A vendor preset not verified against live infrastructure (see {@link RemoteOptions.experimental}). */\n readonly experimental?: boolean | undefined;\n readonly protocol: RemoteProtocol;\n readonly reach: TargetReach;\n readonly connect: RemoteOptions[\"connect\"];\n readonly release: ((lease: never, info: RemoteReleaseInfo) => Promise<void> | void) | undefined;\n readonly platform: string | undefined;\n readonly retry: { readonly attempts: number; readonly backoffMs: number };\n readonly timeouts: {\n readonly acquireMs: number;\n readonly connectMs: number;\n readonly releaseMs: number;\n readonly probeMs: number;\n };\n}\n\n/** What a runner supports, which {@link defineRemote} checks the service against. */\nexport interface RemoteSupport {\n /** Engines the runner can drive. */\n engines: readonly BrowserName[];\n /** Protocols the runner can speak. */\n protocols: readonly RemoteProtocol[];\n /** Shown in errors, for example `remote()`. */\n label: string;\n}\n\n/** One target a remote service offers. */\nexport interface RemoteTarget {\n kind: \"remote\";\n target: CaptureTarget;\n engine: BrowserName;\n remote: RemoteBrowser;\n /** How the browser reaches the Storybook. */\n reach: TargetReach;\n}\n\nconst CDP_ENGINES = new Set<BrowserName>([\"chromium\", \"chrome\"]);\nconst SCHEMES: Record<RemoteProtocol, readonly string[]> = {\n playwright: [\"ws:\", \"wss:\"],\n cdp: [\"ws:\", \"wss:\", \"http:\", \"https:\"],\n};\n\n/** Options 0.7 took that 0.8 renamed or removed, with what replaces them (a loud migration error). */\nconst MOVED: Record<string, string> = {\n acquire: \"merge it into `connect` (a function that receives a context)\",\n exposeNetwork: 'declare `reach: \"loopback\"` on the service instead',\n connectRetries: \"use `retry: { attempts }`\",\n connectBackoffMs: \"use `retry: { backoffMs }`\",\n connectTimeoutMs: \"use `timeouts: { connectMs }`\",\n acquireTimeoutMs: \"use `timeouts: { acquireMs }`\",\n};\n\nconst KNOWN = new Set([\n \"name\",\n \"engines\",\n \"sessions\",\n \"maxPagesPerConnection\",\n \"maxConnections\",\n \"experimental\",\n \"protocol\",\n \"reach\",\n \"connect\",\n \"release\",\n \"platform\",\n \"retry\",\n \"timeouts\",\n]);\n\n/**\n * Validate a remote service eagerly, so a mistake fails when the runner is created and not when the\n * first build runs: unknown or moved options, the name, the sessions, the engines against what the\n * runner drives, the protocol, the reach, and a fixed connection (endpoint scheme).\n */\nexport function defineRemote<TLease = unknown>(\n options: RemoteOptions<TLease>,\n support: RemoteSupport,\n): RemoteBrowser {\n const plain = options as unknown as RemoteOptions;\n checkOptions(plain, support);\n checkEngines(plain, support);\n checkProtocol(plain, support);\n return {\n kind: \"remote\",\n name: options.name,\n engines: [...options.engines],\n sessions: options.sessions,\n maxPagesPerConnection: options.maxPagesPerConnection,\n maxConnections: options.maxConnections,\n experimental: options.experimental === true,\n protocol: options.protocol,\n reach: options.reach,\n connect: options.connect,\n release: options.release,\n platform: options.platform,\n retry: { attempts: options.retry?.attempts ?? 3, backoffMs: options.retry?.backoffMs ?? 500 },\n timeouts: {\n acquireMs: options.timeouts?.acquireMs ?? 30_000,\n connectMs: options.timeouts?.connectMs ?? 30_000,\n releaseMs: options.timeouts?.releaseMs ?? 30_000,\n probeMs: options.timeouts?.probeMs ?? DEFAULT_PROBE_MS,\n },\n };\n}\n\n/** The targets a service offers, in the order of its `engines`. */\nexport function remoteTargetsOf(remote: RemoteBrowser): RemoteTarget[] {\n return remote.engines.map((engine) => ({\n kind: \"remote\",\n target: `${engine}@${remote.name}`,\n engine,\n remote,\n reach: remote.reach,\n }));\n}\n\n/** Whether a value is a remote service entry. */\nexport function isRemoteBrowser(value: unknown): value is RemoteBrowser {\n return typeof value === \"object\" && value !== null && (value as RemoteBrowser).kind === \"remote\";\n}\n\n/** Whether `endpoint` has a scheme `protocol` can open. */\nexport function hasSchemeFor(protocol: RemoteProtocol, endpoint: string): boolean {\n try {\n return SCHEMES[protocol].includes(new URL(endpoint).protocol);\n } catch {\n return false;\n }\n}\n\n/** The schemes `protocol` accepts, for messages. */\nexport function schemesOf(protocol: RemoteProtocol): string {\n return SCHEMES[protocol].join(\" or \");\n}\n\n/**\n * Remove what identifies the connection from text surfaced to operators: the endpoint (it can carry\n * a key in its path or query), its host and path, its query values, every header value and every\n * value inside the lease (non-strings are stringified).\n */\nexport function scrubConnection(\n text: string,\n connection: RemoteConnection & { lease?: unknown },\n): string {\n const secrets = [\n connection.endpoint,\n ...Object.values(connection.headers ?? {}),\n ...leaseSecrets(connection.lease),\n ];\n try {\n const url = new URL(connection.endpoint);\n // Drivers echo the endpoint without its query; the host and a path that acts as a key stay hidden.\n secrets.push(\n url.password,\n ...url.searchParams.values(),\n decodeURIComponent(url.search),\n `${url.protocol}//${url.host}${url.pathname}`,\n url.host,\n url.pathname,\n );\n } catch {\n // An unparsable endpoint is scrubbed whole above.\n }\n let scrubbed = text;\n // Longest first: the endpoint goes before the fragments it contains.\n for (const secret of secrets\n .filter((value) => value.length >= 4)\n .toSorted((a, b) => b.length - a.length)) {\n scrubbed = scrubbed.replaceAll(secret, \"[redacted]\");\n }\n return scrubbed;\n}\n\n/** Every value of a lease, as text: a string whole, anything else stringified, objects walked. */\nfunction leaseSecrets(lease: unknown, depth = 0): string[] {\n if (lease === undefined || lease === null) {\n return [];\n }\n if (typeof lease === \"object\") {\n return depth > 3 ? [] : Object.values(lease).flatMap((value) => leaseSecrets(value, depth + 1));\n }\n return typeof lease === \"string\" || typeof lease === \"number\" || typeof lease === \"boolean\"\n ? [String(lease)]\n : [];\n}\n\nfunction checkKnownKeys(options: RemoteOptions, where: string): void {\n for (const key of Object.keys(options)) {\n if (!KNOWN.has(key)) {\n const moved = MOVED[key];\n throw new Error(\n moved === undefined\n ? `${where}: unknown option \"${key}\"`\n : `${where}: option \"${key}\" is gone in 0.8: ${moved}`,\n );\n }\n }\n}\n\nfunction checkOptions(options: RemoteOptions, { label }: RemoteSupport): void {\n const where = `${label} \"${options.name}\"`;\n checkKnownKeys(options, where);\n if (!TARGET_NAME_PATTERN.test(options.name)) {\n throw new Error(\n `${label}: name \"${options.name}\" must be lowercase letters, digits and hyphens, start with a letter or digit and be at most 32 characters`,\n );\n }\n checkSessions(options.sessions, where);\n checkPositiveInteger(\"maxPagesPerConnection\", options.maxPagesPerConnection, where);\n checkPositiveInteger(\"maxConnections\", options.maxConnections, where);\n checkNumbers(options, where);\n if (options.release !== undefined && typeof options.connect !== \"function\") {\n throw new Error(`${where}: release needs a function connect (a fixed connection has no lease)`);\n }\n}\n\n/** An optional limit: absent, or a whole number of at least 1. */\nfunction checkPositiveInteger(name: string, value: number | undefined, where: string): void {\n if (value !== undefined && (!Number.isInteger(value) || value < 1)) {\n throw new Error(`${where}: ${name} must be a positive integer`);\n }\n}\n\nfunction checkSessions(sessions: RemoteOptions[\"sessions\"], where: string): void {\n const value = typeof sessions === \"function\" ? sessions() : sessions;\n if (!Number.isInteger(value) || value < 1) {\n throw new Error(`${where}: sessions must be a positive integer`);\n }\n}\n\nfunction checkNumbers(options: RemoteOptions, where: string): void {\n const entries: [string, number | undefined][] = [\n [\"retry.attempts\", options.retry?.attempts],\n [\"timeouts.acquireMs\", options.timeouts?.acquireMs],\n [\"timeouts.connectMs\", options.timeouts?.connectMs],\n [\"timeouts.releaseMs\", options.timeouts?.releaseMs],\n [\"timeouts.probeMs\", options.timeouts?.probeMs],\n [\"retry.backoffMs\", options.retry?.backoffMs],\n ];\n for (const [name, value] of entries) {\n const allowed = name === \"retry.attempts\" || name === \"retry.backoffMs\" ? 0 : 1;\n if (value !== undefined && (!Number.isInteger(value) || value < allowed)) {\n throw new Error(`${where}: ${name} must be an integer of at least ${allowed}`);\n }\n }\n}\n\nfunction checkEngines(options: RemoteOptions, support: RemoteSupport): void {\n const where = `${support.label} \"${options.name}\"`;\n if (options.engines.length === 0) {\n throw new Error(`${where}: engines must list at least one engine`);\n }\n for (const [index, engine] of options.engines.entries()) {\n if (!isBrowserName(engine)) {\n throw new Error(`${where}: unknown engine \"${String(engine)}\"`);\n }\n if (!support.engines.includes(engine)) {\n throw new Error(\n `${where}: this runner cannot drive \"${engine}\" (it drives ${support.engines.join(\", \")})`,\n );\n }\n if (options.engines.indexOf(engine) !== index) {\n throw new Error(`${where}: engine \"${engine}\" is listed twice`);\n }\n }\n}\n\nfunction checkProtocol(options: RemoteOptions, support: RemoteSupport): void {\n const where = `${support.label} \"${options.name}\"`;\n checkDeclared(options, support, where);\n if (options.protocol === \"cdp\") {\n checkCdp(options, where);\n }\n if (typeof options.connect !== \"function\") {\n checkFixedConnection(options.connect, options.protocol, where);\n }\n}\n\nfunction checkDeclared(options: RemoteOptions, support: RemoteSupport, where: string): void {\n if (options.protocol === undefined || options.reach === undefined) {\n throw new Error(\n `${where}: \"protocol\" and \"reach\" are required in 0.8 (declare how the runner talks to the service and whether its browser can reach this host)`,\n );\n }\n if (!support.protocols.includes(options.protocol)) {\n throw new Error(\n `${where}: protocol \"${options.protocol}\" is not supported by this runner (it speaks ${support.protocols.join(\", \")})`,\n );\n }\n if (options.reach !== \"loopback\" && options.reach !== \"url\") {\n throw new Error(`${where}: reach must be \"loopback\" or \"url\"`);\n }\n}\n\nfunction checkCdp(options: RemoteOptions, where: string): void {\n const bad = options.engines.find((engine) => !CDP_ENGINES.has(engine));\n if (bad !== undefined) {\n throw new Error(`${where}: the CDP protocol drives Chromium only, not \"${bad}\"`);\n }\n if (options.reach === \"loopback\") {\n throw new Error(`${where}: reach \"loopback\" needs the \"playwright\" protocol`);\n }\n}\n\nfunction checkFixedConnection(\n connection: RemoteConnection,\n protocol: RemoteProtocol,\n where: string,\n): void {\n const extra = Object.keys(connection).filter((key) => key !== \"endpoint\" && key !== \"headers\");\n if (extra.length > 0) {\n throw new Error(`${where}: connect has unknown field(s) ${extra.join(\", \")}`);\n }\n if (!hasSchemeFor(protocol, connection.endpoint)) {\n throw new Error(`${where}: the endpoint must be a ${schemesOf(protocol)} URL`);\n }\n}\n"],"mappings":"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"}
|