@storyshelf/core 0.6.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -32
- package/dist/adapter/capture-queue.d.mts +44 -4
- package/dist/adapter/capture-runner.d.mts +134 -12
- package/dist/adapter/capture-runner.mjs +9 -12
- package/dist/adapter/database.d.mts +89 -10
- package/dist/adapter/database.mjs +4 -3
- package/dist/adapter/database.mjs.map +1 -1
- package/dist/adapter/design.d.mts +83 -0
- package/dist/adapter/design.mjs +27 -0
- package/dist/adapter/design.mjs.map +1 -0
- package/dist/adapter/email-sender.d.mts +0 -2
- package/dist/adapter/metadata.d.mts +3 -12
- package/dist/adapter/notifier.d.mts +6 -3
- package/dist/adapter/notifier.mjs +5 -1
- package/dist/adapter/oauth.d.mts +50 -0
- package/dist/adapter/setup.d.mts +4 -7
- package/dist/adapter/setup.mjs +3 -3
- package/dist/adapter/setup.mjs.map +1 -1
- package/dist/adapter/storage.d.mts +1 -3
- package/dist/adapter/webhook-events.d.mts +0 -2
- package/dist/adapter/webhook-events.mjs +13 -14
- package/dist/adapter/webhook-events.mjs.map +1 -1
- package/dist/adapters/capture-target-offers.d.mts +26 -0
- package/dist/adapters/capture-target-offers.mjs +57 -0
- package/dist/adapters/capture-target-offers.mjs.map +1 -0
- package/dist/adapters/capture-target.d.mts +69 -0
- package/dist/adapters/capture-target.mjs +75 -0
- package/dist/adapters/capture-target.mjs.map +1 -0
- package/dist/adapters/notifier/build-comment.d.mts +22 -0
- package/dist/adapters/notifier/build-comment.mjs +23 -0
- package/dist/adapters/notifier/build-comment.mjs.map +1 -0
- package/dist/adapters/notifier/build-state.d.mts +97 -0
- package/dist/adapters/notifier/build-state.mjs +217 -0
- package/dist/adapters/notifier/build-state.mjs.map +1 -0
- package/dist/adapters/notifier/events.d.mts +4 -4
- package/dist/adapters/notifier/events.mjs +32 -20
- package/dist/adapters/notifier/events.mjs.map +1 -1
- package/dist/adapters/notifier/format.d.mts +0 -2
- package/dist/adapters/notifier/format.mjs +1 -2
- package/dist/adapters/notifier/provider.d.mts +53 -9
- package/dist/adapters/notifier/provider.mjs +11 -0
- package/dist/adapters/notifier/provider.mjs.map +1 -0
- package/dist/adapters/notifier/state.d.mts +22 -0
- package/dist/adapters/notifier/state.mjs +65 -0
- package/dist/adapters/notifier/state.mjs.map +1 -0
- package/dist/adapters/notifier/types.d.mts +51 -4
- package/dist/adapters/remote-browser.d.mts +168 -0
- package/dist/adapters/remote-browser.mjs +201 -0
- package/dist/adapters/remote-browser.mjs.map +1 -0
- package/dist/adapters/remote-lease-attempt.mjs +39 -0
- package/dist/adapters/remote-lease-attempt.mjs.map +1 -0
- package/dist/adapters/remote-lease-error.d.mts +25 -0
- package/dist/adapters/remote-lease-error.mjs +54 -0
- package/dist/adapters/remote-lease-error.mjs.map +1 -0
- package/dist/adapters/remote-lease-held.d.mts +15 -0
- package/dist/adapters/remote-lease-held.mjs +86 -0
- package/dist/adapters/remote-lease-held.mjs.map +1 -0
- package/dist/adapters/remote-lease-shutdown.d.mts +12 -0
- package/dist/adapters/remote-lease-shutdown.mjs +32 -0
- package/dist/adapters/remote-lease-shutdown.mjs.map +1 -0
- package/dist/adapters/remote-lease.d.mts +39 -0
- package/dist/adapters/remote-lease.mjs +175 -0
- package/dist/adapters/remote-lease.mjs.map +1 -0
- package/dist/adapters/remote-limiter.mjs +121 -0
- package/dist/adapters/remote-limiter.mjs.map +1 -0
- package/dist/adapters/remote-pool.d.mts +56 -0
- package/dist/adapters/remote-pool.mjs +195 -0
- package/dist/adapters/remote-pool.mjs.map +1 -0
- package/dist/adapters/render-runs.d.mts +23 -0
- package/dist/adapters/render-runs.mjs +40 -0
- package/dist/adapters/render-runs.mjs.map +1 -0
- package/dist/adapters/setup-runner.d.mts +0 -2
- package/dist/adapters/setup-runner.mjs +3 -6
- package/dist/adapters/setup-runner.mjs.map +1 -1
- package/dist/adapters/sources.d.mts +10 -0
- package/dist/adapters/sources.mjs +19 -0
- package/dist/adapters/sources.mjs.map +1 -0
- package/dist/adapters/validate.d.mts +0 -2
- package/dist/adapters/validate.mjs +27 -20
- package/dist/adapters/validate.mjs.map +1 -1
- package/dist/ai.d.mts +103 -0
- package/dist/ai.mjs +18 -0
- package/dist/ai.mjs.map +1 -0
- package/dist/auth.d.mts +0 -2
- package/dist/auth.mjs +1 -2
- package/dist/brand-packs.d.mts +0 -2
- package/dist/brand-packs.mjs +0 -1
- package/dist/capture/a11y/axe-source.d.mts +16 -0
- package/dist/capture/a11y/axe-source.mjs +36 -0
- package/dist/capture/a11y/axe-source.mjs.map +1 -0
- package/dist/capture/a11y/in-page.d.mts +25 -0
- package/dist/capture/a11y/in-page.mjs +59 -0
- package/dist/capture/a11y/in-page.mjs.map +1 -0
- package/dist/capture/a11y/map.d.mts +22 -0
- package/dist/capture/a11y/map.mjs +47 -0
- package/dist/capture/a11y/map.mjs.map +1 -0
- package/dist/capture/a11y/options.d.mts +21 -0
- package/dist/capture/a11y/options.mjs +74 -0
- package/dist/capture/a11y/options.mjs.map +1 -0
- package/dist/capture/adapter.d.mts +46 -5
- package/dist/capture/adapter.mjs +1 -2
- package/dist/capture/adapter.mjs.map +1 -1
- package/dist/capture/affected.d.mts +2 -2
- package/dist/capture/affected.mjs +14 -6
- package/dist/capture/affected.mjs.map +1 -1
- package/dist/capture/attempt-logger.mjs +83 -0
- package/dist/capture/attempt-logger.mjs.map +1 -0
- package/dist/capture/browsers.d.mts +33 -0
- package/dist/capture/browsers.mjs +66 -0
- package/dist/capture/browsers.mjs.map +1 -0
- package/dist/capture/build-state/deliver.d.mts +30 -0
- package/dist/capture/build-state/deliver.mjs +117 -0
- package/dist/capture/build-state/deliver.mjs.map +1 -0
- package/dist/capture/build-state/index.d.mts +8 -0
- package/dist/capture/build-state/notify-job.d.mts +54 -0
- package/dist/capture/build-state/notify-job.mjs +132 -0
- package/dist/capture/build-state/notify-job.mjs.map +1 -0
- package/dist/capture/build-state/publisher.d.mts +21 -0
- package/dist/capture/build-state/publisher.mjs +106 -0
- package/dist/capture/build-state/publisher.mjs.map +1 -0
- package/dist/capture/build-state/resume.d.mts +7 -0
- package/dist/capture/build-state/resume.mjs +46 -0
- package/dist/capture/build-state/resume.mjs.map +1 -0
- package/dist/capture/build-state/types.d.mts +47 -0
- package/dist/capture/catalog/catalogue.d.mts +20 -0
- package/dist/capture/catalog/catalogue.mjs +52 -0
- package/dist/capture/catalog/catalogue.mjs.map +1 -0
- package/dist/capture/catalog/components.d.mts +21 -0
- package/dist/capture/catalog/components.mjs +54 -0
- package/dist/capture/catalog/components.mjs.map +1 -0
- package/dist/capture/catalog/history.d.mts +28 -0
- package/dist/capture/catalog/history.mjs +50 -0
- package/dist/capture/catalog/history.mjs.map +1 -0
- package/dist/capture/catalog/hook.d.mts +12 -0
- package/dist/capture/catalog/hook.mjs +18 -0
- package/dist/capture/catalog/hook.mjs.map +1 -0
- package/dist/capture/catalog/index.d.mts +11 -0
- package/dist/capture/catalog/load.d.mts +24 -0
- package/dist/capture/catalog/load.mjs +48 -0
- package/dist/capture/catalog/load.mjs.map +1 -0
- package/dist/capture/catalog/parse.d.mts +13 -0
- package/dist/capture/catalog/parse.mjs +55 -0
- package/dist/capture/catalog/parse.mjs.map +1 -0
- package/dist/capture/catalog/read.d.mts +15 -0
- package/dist/capture/catalog/read.mjs +39 -0
- package/dist/capture/catalog/read.mjs.map +1 -0
- package/dist/capture/catalog/types.d.mts +31 -0
- package/dist/capture/comparison-reference.d.mts +27 -0
- package/dist/capture/comparison-reference.mjs +75 -0
- package/dist/capture/comparison-reference.mjs.map +1 -0
- package/dist/capture/consumer/capture-handler.d.mts +56 -0
- package/dist/capture/consumer/capture-handler.mjs +117 -0
- package/dist/capture/consumer/capture-handler.mjs.map +1 -0
- package/dist/capture/consumer/idle.d.mts +15 -0
- package/dist/capture/consumer/idle.mjs +24 -0
- package/dist/capture/consumer/idle.mjs.map +1 -0
- package/dist/capture/consumer/index.d.mts +11 -0
- package/dist/capture/consumer/index.mjs +138 -0
- package/dist/capture/consumer/index.mjs.map +1 -0
- package/dist/capture/consumer/loop.mjs +109 -0
- package/dist/capture/consumer/loop.mjs.map +1 -0
- package/dist/capture/consumer/metrics.d.mts +39 -0
- package/dist/capture/consumer/metrics.mjs +37 -0
- package/dist/capture/consumer/metrics.mjs.map +1 -0
- package/dist/capture/consumer/retry.d.mts +31 -0
- package/dist/capture/consumer/retry.mjs +33 -0
- package/dist/capture/consumer/retry.mjs.map +1 -0
- package/dist/capture/consumer/run-job.mjs +194 -0
- package/dist/capture/consumer/run-job.mjs.map +1 -0
- package/dist/capture/consumer/startup.mjs +37 -0
- package/dist/capture/consumer/startup.mjs.map +1 -0
- package/dist/capture/consumer/types.d.mts +95 -0
- package/dist/capture/crash-failure.d.mts +23 -0
- package/dist/capture/crash-failure.mjs +69 -0
- package/dist/capture/crash-failure.mjs.map +1 -0
- package/dist/capture/db-queue/cadence.d.mts +30 -0
- package/dist/capture/db-queue/cadence.mjs +37 -0
- package/dist/capture/db-queue/cadence.mjs.map +1 -0
- package/dist/capture/db-queue/claim.d.mts +7 -0
- package/dist/capture/db-queue/claim.mjs +148 -0
- package/dist/capture/db-queue/claim.mjs.map +1 -0
- package/dist/capture/db-queue/clock.mjs +11 -0
- package/dist/capture/db-queue/clock.mjs.map +1 -0
- package/dist/capture/db-queue/codec.mjs +75 -0
- package/dist/capture/db-queue/codec.mjs.map +1 -0
- package/dist/capture/db-queue/index.d.mts +18 -0
- package/dist/capture/db-queue/index.mjs +89 -0
- package/dist/capture/db-queue/index.mjs.map +1 -0
- package/dist/capture/db-queue/keeper.d.mts +34 -0
- package/dist/capture/db-queue/keeper.mjs +91 -0
- package/dist/capture/db-queue/keeper.mjs.map +1 -0
- package/dist/capture/db-queue/lease.mjs +45 -0
- package/dist/capture/db-queue/lease.mjs.map +1 -0
- package/dist/capture/db-queue/lifecycle.mjs +35 -0
- package/dist/capture/db-queue/lifecycle.mjs.map +1 -0
- package/dist/capture/db-queue/operations.mjs +93 -0
- package/dist/capture/db-queue/operations.mjs.map +1 -0
- package/dist/capture/db-queue/poll-loop.d.mts +28 -0
- package/dist/capture/db-queue/poll-loop.mjs +88 -0
- package/dist/capture/db-queue/poll-loop.mjs.map +1 -0
- package/dist/capture/db-queue/poll.d.mts +8 -0
- package/dist/capture/db-queue/poll.mjs +68 -0
- package/dist/capture/db-queue/poll.mjs.map +1 -0
- package/dist/capture/db-queue/timers.d.mts +10 -0
- package/dist/capture/db-queue/timers.mjs +36 -0
- package/dist/capture/db-queue/timers.mjs.map +1 -0
- package/dist/capture/db-queue/types.d.mts +81 -0
- package/dist/capture/db-queue/types.mjs +11 -0
- package/dist/capture/db-queue/types.mjs.map +1 -0
- package/dist/capture/db-queue/wake.mjs +47 -0
- package/dist/capture/db-queue/wake.mjs.map +1 -0
- package/dist/capture/design.mjs +38 -0
- package/dist/capture/design.mjs.map +1 -0
- package/dist/capture/diff-record.mjs +24 -5
- package/dist/capture/diff-record.mjs.map +1 -1
- package/dist/capture/dispatch.d.mts +13 -10
- package/dist/capture/dispatch.mjs +102 -111
- package/dist/capture/dispatch.mjs.map +1 -1
- package/dist/capture/enrich.mjs +59 -0
- package/dist/capture/enrich.mjs.map +1 -0
- package/dist/capture/failure-sets.mjs +21 -0
- package/dist/capture/failure-sets.mjs.map +1 -0
- package/dist/capture/fingerprint.d.mts +40 -0
- package/dist/capture/fingerprint.mjs +82 -0
- package/dist/capture/fingerprint.mjs.map +1 -0
- package/dist/capture/forked/channel.d.mts +23 -0
- package/dist/capture/forked/child.d.mts +32 -0
- package/dist/capture/forked/child.mjs +201 -0
- package/dist/capture/forked/child.mjs.map +1 -0
- package/dist/capture/forked/crash-tracker.mjs +22 -0
- package/dist/capture/forked/crash-tracker.mjs.map +1 -0
- package/dist/capture/forked/decision.d.mts +24 -0
- package/dist/capture/forked/decision.mjs +26 -0
- package/dist/capture/forked/decision.mjs.map +1 -0
- package/dist/capture/forked/exit.mjs +11 -0
- package/dist/capture/forked/exit.mjs.map +1 -0
- package/dist/capture/forked/fork.d.mts +29 -0
- package/dist/capture/forked/fork.mjs +92 -0
- package/dist/capture/forked/fork.mjs.map +1 -0
- package/dist/capture/forked/forked-capture.d.mts +38 -0
- package/dist/capture/forked/forked-capture.mjs +239 -0
- package/dist/capture/forked/forked-capture.mjs.map +1 -0
- package/dist/capture/forked/lease-book.mjs +45 -0
- package/dist/capture/forked/lease-book.mjs.map +1 -0
- package/dist/capture/forked/protocol.d.mts +36 -0
- package/dist/capture/forked/protocol.mjs +24 -0
- package/dist/capture/forked/protocol.mjs.map +1 -0
- package/dist/capture/forked/runs.mjs +74 -0
- package/dist/capture/forked/runs.mjs.map +1 -0
- package/dist/capture/guards.mjs +1 -2
- package/dist/capture/ignore-regions.d.mts +36 -0
- package/dist/capture/ignore-regions.mjs +65 -0
- package/dist/capture/ignore-regions.mjs.map +1 -0
- package/dist/capture/isolate.mjs +140 -0
- package/dist/capture/isolate.mjs.map +1 -0
- package/dist/capture/job-lease.d.mts +26 -0
- package/dist/capture/job-lease.mjs +56 -0
- package/dist/capture/job-lease.mjs.map +1 -0
- package/dist/capture/matrix.mjs +298 -0
- package/dist/capture/matrix.mjs.map +1 -0
- package/dist/capture/modes.d.mts +23 -0
- package/dist/capture/modes.mjs +106 -0
- package/dist/capture/modes.mjs.map +1 -0
- package/dist/capture/orchestrator.d.mts +37 -7
- package/dist/capture/orchestrator.mjs +120 -110
- package/dist/capture/orchestrator.mjs.map +1 -1
- package/dist/capture/pipeline.d.mts +9 -4
- package/dist/capture/pipeline.mjs +284 -48
- package/dist/capture/pipeline.mjs.map +1 -1
- package/dist/capture/progress.d.mts +9 -0
- package/dist/capture/progress.mjs +27 -0
- package/dist/capture/progress.mjs.map +1 -0
- package/dist/capture/publish/accept.d.mts +37 -0
- package/dist/capture/publish/accept.mjs +27 -0
- package/dist/capture/publish/accept.mjs.map +1 -0
- package/dist/capture/publish/complete.d.mts +44 -0
- package/dist/capture/publish/complete.mjs +63 -0
- package/dist/capture/publish/complete.mjs.map +1 -0
- package/dist/capture/publish/extract.d.mts +18 -0
- package/dist/capture/publish/extract.mjs +120 -0
- package/dist/capture/publish/extract.mjs.map +1 -0
- package/dist/capture/publish/handler.d.mts +26 -0
- package/dist/capture/publish/handler.mjs +61 -0
- package/dist/capture/publish/handler.mjs.map +1 -0
- package/dist/capture/publish/index.d.mts +11 -0
- package/dist/capture/publish/invalid-zip.d.mts +8 -0
- package/dist/capture/publish/invalid-zip.mjs +11 -0
- package/dist/capture/publish/invalid-zip.mjs.map +1 -0
- package/dist/capture/publish/limits.d.mts +35 -0
- package/dist/capture/publish/limits.mjs +38 -0
- package/dist/capture/publish/limits.mjs.map +1 -0
- package/dist/capture/publish/published.d.mts +13 -0
- package/dist/capture/publish/published.mjs +17 -0
- package/dist/capture/publish/published.mjs.map +1 -0
- package/dist/capture/publish/run.d.mts +31 -0
- package/dist/capture/publish/run.mjs +29 -0
- package/dist/capture/publish/run.mjs.map +1 -0
- package/dist/capture/rediff.d.mts +1 -3
- package/dist/capture/rediff.mjs +23 -9
- package/dist/capture/rediff.mjs.map +1 -1
- package/dist/capture/render.mjs +52 -0
- package/dist/capture/render.mjs.map +1 -0
- package/dist/capture/scratch.d.mts +42 -0
- package/dist/capture/scratch.mjs +136 -0
- package/dist/capture/scratch.mjs.map +1 -0
- package/dist/capture/shard-plan.mjs +127 -0
- package/dist/capture/shard-plan.mjs.map +1 -0
- package/dist/capture/shards/context.mjs +51 -0
- package/dist/capture/shards/context.mjs.map +1 -0
- package/dist/capture/shards/execute.mjs +175 -0
- package/dist/capture/shards/execute.mjs.map +1 -0
- package/dist/capture/shards/fan-out.mjs +125 -0
- package/dist/capture/shards/fan-out.mjs.map +1 -0
- package/dist/capture/shards/history.mjs +34 -0
- package/dist/capture/shards/history.mjs.map +1 -0
- package/dist/capture/shards/reduce.mjs +96 -0
- package/dist/capture/shards/reduce.mjs.map +1 -0
- package/dist/capture/sizing.d.mts +0 -2
- package/dist/capture/sizing.mjs +2 -13
- package/dist/capture/sizing.mjs.map +1 -1
- package/dist/capture/skip-checks.mjs +3 -67
- package/dist/capture/skip-checks.mjs.map +1 -1
- package/dist/capture/statics.d.mts +7 -10
- package/dist/capture/statics.mjs +26 -74
- package/dist/capture/statics.mjs.map +1 -1
- package/dist/capture/storybook-urls.d.mts +38 -0
- package/dist/capture/storybook-urls.mjs +41 -0
- package/dist/capture/storybook-urls.mjs.map +1 -0
- package/dist/capture/storybook.d.mts +12 -6
- package/dist/capture/storybook.mjs +63 -19
- package/dist/capture/storybook.mjs.map +1 -1
- package/dist/capture/target.mjs +20 -0
- package/dist/capture/target.mjs.map +1 -0
- package/dist/capture/targets.d.mts +23 -0
- package/dist/capture/targets.mjs +31 -0
- package/dist/capture/targets.mjs.map +1 -0
- package/dist/capture/tests.d.mts +35 -0
- package/dist/capture/tests.mjs +136 -0
- package/dist/capture/tests.mjs.map +1 -0
- package/dist/capture/token-chunks.mjs +91 -0
- package/dist/capture/token-chunks.mjs.map +1 -0
- package/dist/capture.d.mts +63 -7
- package/dist/capture.mjs +51 -3
- package/dist/config.d.mts +136 -14
- package/dist/config.mjs +14 -8
- package/dist/config.mjs.map +1 -1
- package/dist/diff.d.mts +7 -3
- package/dist/diff.mjs +15 -3
- package/dist/diff.mjs.map +1 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +2 -2
- package/dist/insights/budget.d.mts +18 -0
- package/dist/insights/budget.mjs +32 -0
- package/dist/insights/budget.mjs.map +1 -0
- package/dist/insights/envelope.d.mts +57 -0
- package/dist/insights/envelope.mjs +59 -0
- package/dist/insights/envelope.mjs.map +1 -0
- package/dist/insights/evidence-health.d.mts +30 -0
- package/dist/insights/evidence-health.mjs +27 -0
- package/dist/insights/evidence-health.mjs.map +1 -0
- package/dist/insights/evidence.d.mts +53 -0
- package/dist/insights/evidence.mjs +63 -0
- package/dist/insights/evidence.mjs.map +1 -0
- package/dist/insights/hash.d.mts +16 -0
- package/dist/insights/hash.mjs +20 -0
- package/dist/insights/hash.mjs.map +1 -0
- package/dist/insights/limits.d.mts +22 -0
- package/dist/insights/limits.mjs +23 -0
- package/dist/insights/limits.mjs.map +1 -0
- package/dist/insights/prompts/v1.d.mts +11 -0
- package/dist/insights/prompts/v1.mjs +33 -0
- package/dist/insights/prompts/v1.mjs.map +1 -0
- package/dist/insights/redact.d.mts +7 -0
- package/dist/insights/redact.mjs +23 -0
- package/dist/insights/redact.mjs.map +1 -0
- package/dist/insights/text.mjs +43 -0
- package/dist/insights/text.mjs.map +1 -0
- package/dist/insights.d.mts +11 -0
- package/dist/insights.mjs +12 -0
- package/dist/logger.d.mts +0 -2
- package/dist/logger.mjs +18 -3
- package/dist/logger.mjs.map +1 -1
- package/dist/models/ai-budget-alert.d.mts +21 -0
- package/dist/models/ai-budget-alert.mjs +37 -0
- package/dist/models/ai-budget-alert.mjs.map +1 -0
- package/dist/models/ai-usage.d.mts +38 -0
- package/dist/models/ai-usage.mjs +60 -0
- package/dist/models/ai-usage.mjs.map +1 -0
- package/dist/models/baseline-approval.d.mts +18 -0
- package/dist/models/baseline-approval.mjs +41 -0
- package/dist/models/baseline-approval.mjs.map +1 -0
- package/dist/models/baseline-archive.d.mts +82 -0
- package/dist/models/baseline-archive.mjs +105 -0
- package/dist/models/baseline-archive.mjs.map +1 -0
- package/dist/models/baseline-atomic.mjs +66 -0
- package/dist/models/baseline-atomic.mjs.map +1 -0
- package/dist/models/baseline-conflict.d.mts +6 -0
- package/dist/models/baseline-conflict.mjs +9 -0
- package/dist/models/baseline-conflict.mjs.map +1 -0
- package/dist/models/baseline-export.d.mts +17 -0
- package/dist/models/baseline-export.mjs +135 -0
- package/dist/models/baseline-export.mjs.map +1 -0
- package/dist/models/baseline-import.mjs +117 -0
- package/dist/models/baseline-import.mjs.map +1 -0
- package/dist/models/baseline-origin.d.mts +16 -0
- package/dist/models/baseline-origin.mjs +35 -0
- package/dist/models/baseline-origin.mjs.map +1 -0
- package/dist/models/baseline-status.d.mts +0 -2
- package/dist/models/baseline-status.mjs +1 -2
- package/dist/models/baseline-transfer.d.mts +48 -0
- package/dist/models/baseline-transfer.mjs +149 -0
- package/dist/models/baseline-transfer.mjs.map +1 -0
- package/dist/models/baseline.d.mts +37 -7
- package/dist/models/baseline.mjs +152 -28
- package/dist/models/baseline.mjs.map +1 -1
- package/dist/models/build-component.d.mts +26 -0
- package/dist/models/build-component.mjs +54 -0
- package/dist/models/build-component.mjs.map +1 -0
- package/dist/models/build-review-status.d.mts +27 -0
- package/dist/models/build-review-status.mjs +36 -0
- package/dist/models/build-review-status.mjs.map +1 -0
- package/dist/models/build-review.d.mts +26 -0
- package/dist/models/build-review.mjs +53 -0
- package/dist/models/build-review.mjs.map +1 -0
- package/dist/models/build.d.mts +25 -5
- package/dist/models/build.mjs +53 -15
- package/dist/models/build.mjs.map +1 -1
- package/dist/models/capture-attempt.d.mts +7 -3
- package/dist/models/capture-attempt.mjs +30 -6
- package/dist/models/capture-attempt.mjs.map +1 -1
- package/dist/models/capture-job.d.mts +104 -0
- package/dist/models/capture-job.mjs +287 -0
- package/dist/models/capture-job.mjs.map +1 -0
- package/dist/models/capture-log.d.mts +1 -3
- package/dist/models/capture-log.mjs +5 -5
- package/dist/models/capture-log.mjs.map +1 -1
- package/dist/models/capture-shard.d.mts +60 -0
- package/dist/models/capture-shard.mjs +189 -0
- package/dist/models/capture-shard.mjs.map +1 -0
- package/dist/models/changeset.d.mts +46 -0
- package/dist/models/changeset.mjs +80 -0
- package/dist/models/changeset.mjs.map +1 -0
- package/dist/models/columns.mjs +18 -0
- package/dist/models/columns.mjs.map +1 -0
- package/dist/models/comment.d.mts +4 -3
- package/dist/models/comment.mjs +8 -6
- package/dist/models/comment.mjs.map +1 -1
- package/dist/models/component.d.mts +41 -0
- package/dist/models/component.mjs +59 -0
- package/dist/models/component.mjs.map +1 -0
- package/dist/models/connections.d.mts +39 -0
- package/dist/models/connections.mjs +111 -0
- package/dist/models/connections.mjs.map +1 -0
- package/dist/models/content-ref.d.mts +1 -3
- package/dist/models/content-ref.mjs +2 -3
- package/dist/models/content-ref.mjs.map +1 -1
- package/dist/models/credentials.d.mts +0 -2
- package/dist/models/credentials.mjs +13 -4
- package/dist/models/credentials.mjs.map +1 -1
- package/dist/models/design-credentials.d.mts +39 -0
- package/dist/models/design-credentials.mjs +85 -0
- package/dist/models/design-credentials.mjs.map +1 -0
- package/dist/models/design-health.d.mts +14 -0
- package/dist/models/design-health.mjs +24 -0
- package/dist/models/design-health.mjs.map +1 -0
- package/dist/models/design-nodes.d.mts +81 -0
- package/dist/models/design-nodes.mjs +221 -0
- package/dist/models/design-nodes.mjs.map +1 -0
- package/dist/models/design-renders.mjs +73 -0
- package/dist/models/design-renders.mjs.map +1 -0
- package/dist/models/design-resolve.d.mts +27 -0
- package/dist/models/design-resolve.mjs +40 -0
- package/dist/models/design-resolve.mjs.map +1 -0
- package/dist/models/design.d.mts +37 -0
- package/dist/models/design.mjs +89 -0
- package/dist/models/design.mjs.map +1 -0
- package/dist/models/inherited-ratio.d.mts +19 -0
- package/dist/models/inherited-ratio.mjs +21 -0
- package/dist/models/inherited-ratio.mjs.map +1 -0
- package/dist/models/insight.d.mts +68 -0
- package/dist/models/insight.mjs +161 -0
- package/dist/models/insight.mjs.map +1 -0
- package/dist/models/label.d.mts +4 -4
- package/dist/models/label.mjs +26 -12
- package/dist/models/label.mjs.map +1 -1
- package/dist/models/member.d.mts +1 -3
- package/dist/models/member.mjs +6 -6
- package/dist/models/member.mjs.map +1 -1
- package/dist/models/notification-channel.d.mts +37 -5
- package/dist/models/notification-channel.mjs +89 -11
- package/dist/models/notification-channel.mjs.map +1 -1
- package/dist/models/notification-subscription.d.mts +1 -4
- package/dist/models/notification-subscription.mjs +8 -11
- package/dist/models/notification-subscription.mjs.map +1 -1
- package/dist/models/project-group-mapping.d.mts +6 -3
- package/dist/models/project-group-mapping.mjs +15 -6
- package/dist/models/project-group-mapping.mjs.map +1 -1
- package/dist/models/project.d.mts +9 -3
- package/dist/models/project.mjs +10 -6
- package/dist/models/project.mjs.map +1 -1
- package/dist/models/public-origin.d.mts +61 -0
- package/dist/models/public-origin.mjs +113 -0
- package/dist/models/public-origin.mjs.map +1 -0
- package/dist/models/review-switch-off.d.mts +12 -0
- package/dist/models/review-switch-off.mjs +25 -0
- package/dist/models/review-switch-off.mjs.map +1 -0
- package/dist/models/site-settings.d.mts +21 -0
- package/dist/models/site-settings.mjs +47 -0
- package/dist/models/site-settings.mjs.map +1 -0
- package/dist/models/snapshot.d.mts +39 -3
- package/dist/models/snapshot.mjs +54 -5
- package/dist/models/snapshot.mjs.map +1 -1
- package/dist/models/token.d.mts +13 -3
- package/dist/models/token.mjs +24 -7
- package/dist/models/token.mjs.map +1 -1
- package/dist/models/usage.d.mts +74 -0
- package/dist/models/usage.mjs +148 -0
- package/dist/models/usage.mjs.map +1 -0
- package/dist/models/user.d.mts +1 -3
- package/dist/models/user.mjs +5 -5
- package/dist/models/user.mjs.map +1 -1
- package/dist/models/webhook.d.mts +15 -3
- package/dist/models/webhook.mjs +34 -6
- package/dist/models/webhook.mjs.map +1 -1
- package/dist/models.d.mts +30 -4
- package/dist/models.mjs +31 -6
- package/dist/options.d.mts +16 -0
- package/dist/options.mjs +88 -0
- package/dist/options.mjs.map +1 -0
- package/dist/paths.d.mts +21 -6
- package/dist/paths.mjs +57 -12
- package/dist/paths.mjs.map +1 -1
- package/dist/retention/ai.d.mts +16 -0
- package/dist/retention/ai.mjs +22 -0
- package/dist/retention/ai.mjs.map +1 -0
- package/dist/retention/pending.mjs +46 -0
- package/dist/retention/pending.mjs.map +1 -0
- package/dist/retention/published-gzip-sweep.mjs +28 -0
- package/dist/retention/published-gzip-sweep.mjs.map +1 -0
- package/dist/retention/screenshot-sweep.d.mts +49 -0
- package/dist/retention/screenshot-sweep.mjs +127 -0
- package/dist/retention/screenshot-sweep.mjs.map +1 -0
- package/dist/retention.d.mts +16 -4
- package/dist/retention.mjs +76 -16
- package/dist/retention.mjs.map +1 -1
- package/dist/schema/baseline.d.mts +8 -2
- package/dist/schema/build-component.d.mts +23 -0
- package/dist/schema/build-review.d.mts +15 -0
- package/dist/schema/build.d.mts +7 -2
- package/dist/schema/capture-attempt.d.mts +0 -2
- package/dist/schema/capture-job.d.mts +58 -0
- package/dist/schema/capture-shard.d.mts +33 -0
- package/dist/schema/comment.d.mts +2 -2
- package/dist/schema/connection.d.mts +22 -0
- package/dist/schema/content-ref.d.mts +0 -2
- package/dist/schema/design.d.mts +56 -0
- package/dist/schema/insight.d.mts +52 -0
- package/dist/schema/label.d.mts +0 -2
- package/dist/schema/member.d.mts +0 -2
- package/dist/schema/notification.d.mts +4 -2
- package/dist/schema/project-group-mapping.d.mts +0 -2
- package/dist/schema/project.d.mts +21 -2
- package/dist/schema/site-setting.d.mts +19 -0
- package/dist/schema/snapshot.d.mts +17 -2
- package/dist/schema/token.d.mts +0 -2
- package/dist/schema/usage.d.mts +28 -0
- package/dist/schema/user.d.mts +0 -2
- package/dist/schema/webhook.d.mts +0 -2
- package/dist/schema.d.mts +12 -6
- package/dist/serverless.d.mts +19 -0
- package/dist/serverless.mjs +36 -0
- package/dist/serverless.mjs.map +1 -0
- package/dist/test-helpers/build-reference-contract.d.mts +14 -0
- package/dist/test-helpers/build-reference-contract.mjs +66 -0
- package/dist/test-helpers/build-reference-contract.mjs.map +1 -0
- package/dist/test-helpers/create-project.d.mts +1 -3
- package/dist/test-helpers/create-project.mjs +1 -2
- package/dist/test-helpers/database-contract.d.mts +0 -2
- package/dist/test-helpers/database-contract.mjs +37 -2
- package/dist/test-helpers/database-contract.mjs.map +1 -1
- package/dist/test-helpers/deferred.d.mts +37 -0
- package/dist/test-helpers/deferred.mjs +60 -0
- package/dist/test-helpers/deferred.mjs.map +1 -0
- package/dist/test-helpers/design-contract.d.mts +19 -0
- package/dist/test-helpers/design-contract.mjs +39 -0
- package/dist/test-helpers/design-contract.mjs.map +1 -0
- package/dist/test-helpers/fake-adapters.d.mts +0 -2
- package/dist/test-helpers/fake-adapters.mjs +1 -2
- package/dist/test-helpers/fake-database.d.mts +0 -2
- package/dist/test-helpers/fake-database.mjs +21 -4
- package/dist/test-helpers/fake-database.mjs.map +1 -1
- package/dist/test-helpers/fake-design.d.mts +41 -0
- package/dist/test-helpers/fake-design.mjs +113 -0
- package/dist/test-helpers/fake-design.mjs.map +1 -0
- package/dist/test-helpers/fake-notifier.d.mts +19 -0
- package/dist/test-helpers/fake-notifier.mjs +43 -0
- package/dist/test-helpers/fake-notifier.mjs.map +1 -0
- package/dist/test-helpers/fake-storage.d.mts +0 -2
- package/dist/test-helpers/fake-storage.mjs +1 -2
- package/dist/test-helpers/fake-tables.mjs +260 -69
- package/dist/test-helpers/fake-tables.mjs.map +1 -1
- package/dist/test-helpers/manual-clock.d.mts +25 -0
- package/dist/test-helpers/manual-clock.mjs +55 -0
- package/dist/test-helpers/manual-clock.mjs.map +1 -0
- package/dist/test-helpers/notifier-contract.d.mts +7 -3
- package/dist/test-helpers/notifier-contract.mjs +33 -5
- package/dist/test-helpers/notifier-contract.mjs.map +1 -1
- package/dist/test-helpers/queue-contract.d.mts +16 -4
- package/dist/test-helpers/queue-contract.mjs +196 -3
- package/dist/test-helpers/queue-contract.mjs.map +1 -1
- package/dist/test-helpers/queue-multi-instance.d.mts +18 -0
- package/dist/test-helpers/queue-multi-instance.mjs +205 -0
- package/dist/test-helpers/queue-multi-instance.mjs.map +1 -0
- package/dist/test-helpers/runner-contract.d.mts +0 -2
- package/dist/test-helpers/runner-contract.mjs +12 -2
- package/dist/test-helpers/runner-contract.mjs.map +1 -1
- package/dist/test-helpers/sql-chunks.mjs +25 -2
- package/dist/test-helpers/sql-chunks.mjs.map +1 -1
- package/dist/test-helpers/storage-contract.d.mts +0 -2
- package/dist/test-helpers/storage-contract.mjs +2 -3
- package/dist/test-helpers.d.mts +10 -4
- package/dist/test-helpers.mjs +9 -3
- package/dist/tracing.mjs +36 -6
- package/dist/tracing.mjs.map +1 -1
- package/dist/types.d.mts +14 -5
- package/dist/types.mjs +22 -6
- package/dist/types.mjs.map +1 -1
- package/dist/urls.d.mts +4 -2
- package/dist/urls.mjs +12 -4
- package/dist/urls.mjs.map +1 -1
- package/dist/utils/adaptive-concurrency.d.mts +64 -0
- package/dist/utils/adaptive-concurrency.mjs +148 -0
- package/dist/utils/adaptive-concurrency.mjs.map +1 -0
- package/dist/utils/connect-retry.d.mts +26 -0
- package/dist/utils/connect-retry.mjs +42 -0
- package/dist/utils/connect-retry.mjs.map +1 -0
- package/dist/utils/encrypt.d.mts +0 -2
- package/dist/utils/encrypt.mjs +1 -2
- package/dist/utils/environment.d.mts +17 -0
- package/dist/utils/environment.mjs +58 -0
- package/dist/utils/environment.mjs.map +1 -0
- package/dist/utils/hash.d.mts +0 -2
- package/dist/utils/hash.mjs +1 -2
- package/dist/utils/http.d.mts +29 -3
- package/dist/utils/http.mjs +100 -19
- package/dist/utils/http.mjs.map +1 -1
- package/dist/utils/map-adaptive.d.mts +13 -0
- package/dist/utils/map-adaptive.mjs +71 -0
- package/dist/utils/map-adaptive.mjs.map +1 -0
- package/dist/utils/map-limit.d.mts +8 -0
- package/dist/utils/map-limit.mjs +31 -0
- package/dist/utils/map-limit.mjs.map +1 -0
- package/dist/utils/mime.d.mts +0 -2
- package/dist/utils/mime.mjs +1 -2
- package/dist/utils/page-limiter.d.mts +35 -0
- package/dist/utils/page-limiter.mjs +55 -0
- package/dist/utils/page-limiter.mjs.map +1 -0
- package/dist/utils/path-security.d.mts +0 -2
- package/dist/utils/path-security.mjs +2 -4
- package/dist/utils/path-security.mjs.map +1 -1
- package/dist/utils/public-origin.d.mts +18 -0
- package/dist/utils/public-origin.mjs +74 -0
- package/dist/utils/public-origin.mjs.map +1 -0
- package/dist/utils/redact.d.mts +0 -2
- package/dist/utils/redact.mjs +4 -2
- package/dist/utils/redact.mjs.map +1 -1
- package/dist/utils/remote-environment.d.mts +76 -0
- package/dist/utils/remote-environment.mjs +163 -0
- package/dist/utils/remote-environment.mjs.map +1 -0
- package/dist/utils/resources.d.mts +62 -0
- package/dist/utils/resources.mjs +121 -0
- package/dist/utils/resources.mjs.map +1 -0
- package/dist/utils/screenshot-store.d.mts +13 -0
- package/dist/utils/screenshot-store.mjs +40 -0
- package/dist/utils/screenshot-store.mjs.map +1 -0
- package/dist/utils/session-gate.d.mts +23 -0
- package/dist/utils/session-gate.mjs +69 -0
- package/dist/utils/session-gate.mjs.map +1 -0
- package/dist/utils/storybook-token.d.mts +89 -0
- package/dist/utils/storybook-token.mjs +135 -0
- package/dist/utils/storybook-token.mjs.map +1 -0
- package/dist/utils/storybook-url.d.mts +13 -0
- package/dist/utils/storybook-url.mjs +20 -0
- package/dist/utils/storybook-url.mjs.map +1 -0
- package/dist/utils/timing.d.mts +0 -2
- package/dist/utils/timing.mjs +2 -3
- package/dist/utils/timing.mjs.map +1 -1
- package/dist/utils/ulid.d.mts +0 -2
- package/dist/utils/ulid.mjs +1 -2
- package/dist/utils/zip-read.mjs +128 -0
- package/dist/utils/zip-read.mjs.map +1 -0
- package/dist/utils/zip-write.d.mts +16 -0
- package/dist/utils/zip-write.mjs +187 -0
- package/dist/utils/zip-write.mjs.map +1 -0
- package/dist/utils.d.mts +17 -3
- package/dist/utils.mjs +18 -4
- package/package.json +35 -26
- package/dist/adapter/capture-runner.mjs.map +0 -1
- package/dist/adapter/git-host/comments.d.mts +0 -41
- package/dist/adapter/git-host/comments.mjs +0 -54
- package/dist/adapter/git-host/comments.mjs.map +0 -1
- package/dist/adapter/git-host/helpers.d.mts +0 -13
- package/dist/adapter/git-host/helpers.mjs +0 -30
- package/dist/adapter/git-host/helpers.mjs.map +0 -1
- package/dist/adapter/git-host.d.mts +0 -42
- package/dist/capture/queue.d.mts +0 -38
- package/dist/capture/queue.mjs +0 -98
- package/dist/capture/queue.mjs.map +0 -1
- package/dist/capture/recovery.d.mts +0 -21
- package/dist/capture/recovery.mjs +0 -83
- package/dist/capture/recovery.mjs.map +0 -1
- package/dist/capture/status-fanout.mjs +0 -72
- package/dist/capture/status-fanout.mjs.map +0 -1
- package/dist/diff/engine.d.mts +0 -16
- package/dist/diff/engine.mjs +0 -87
- package/dist/diff/engine.mjs.map +0 -1
- package/dist/diff/options.d.mts +0 -42
- package/dist/diff/options.mjs +0 -15
- package/dist/diff/options.mjs.map +0 -1
- package/dist/models/status-config.d.mts +0 -38
- package/dist/models/status-config.mjs +0 -63
- package/dist/models/status-config.mjs.map +0 -1
- package/dist/schema/status-config.d.mts +0 -16
- package/dist/test-helpers/git-contract.d.mts +0 -11
- package/dist/test-helpers/git-contract.mjs +0 -34
- package/dist/test-helpers/git-contract.mjs.map +0 -1
- /package/dist/adapter/{git-host.mjs → oauth.mjs} +0 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { TARGET_NAME_PATTERN, isBrowserName } from "./capture-target.mjs";
|
|
5
|
+
// src/adapters/remote-browser.ts
|
|
6
|
+
/**
|
|
7
|
+
* Remote browsers (ADR 0007, decisions 1 and 3): one service a runner connects to instead of
|
|
8
|
+
* launching a browser, expanding to one `<engine>@<name>` target per engine. Presets are plain
|
|
9
|
+
* configuration over {@link defineRemote}; the runners add only what they can drive.
|
|
10
|
+
*/
|
|
11
|
+
/** Default bound on measuring a connected remote browser's environment (`timeouts.probeMs`). */
|
|
12
|
+
const DEFAULT_PROBE_MS = 15e3;
|
|
13
|
+
const CDP_ENGINES = /* @__PURE__ */ new Set(["chromium", "chrome"]);
|
|
14
|
+
const SCHEMES = {
|
|
15
|
+
playwright: ["ws:", "wss:"],
|
|
16
|
+
cdp: [
|
|
17
|
+
"ws:",
|
|
18
|
+
"wss:",
|
|
19
|
+
"http:",
|
|
20
|
+
"https:"
|
|
21
|
+
]
|
|
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
|
+
"protocol",
|
|
39
|
+
"reach",
|
|
40
|
+
"connect",
|
|
41
|
+
"release",
|
|
42
|
+
"platform",
|
|
43
|
+
"retry",
|
|
44
|
+
"timeouts"
|
|
45
|
+
]);
|
|
46
|
+
/**
|
|
47
|
+
* Validate a remote service eagerly, so a mistake fails when the runner is created and not when the
|
|
48
|
+
* first build runs: unknown or moved options, the name, the sessions, the engines against what the
|
|
49
|
+
* runner drives, the protocol, the reach, and a fixed connection (endpoint scheme).
|
|
50
|
+
*/
|
|
51
|
+
function defineRemote(options, support) {
|
|
52
|
+
const plain = options;
|
|
53
|
+
checkOptions(plain, support);
|
|
54
|
+
checkEngines(plain, support);
|
|
55
|
+
checkProtocol(plain, support);
|
|
56
|
+
return {
|
|
57
|
+
kind: "remote",
|
|
58
|
+
name: options.name,
|
|
59
|
+
engines: [...options.engines],
|
|
60
|
+
sessions: options.sessions,
|
|
61
|
+
maxPagesPerConnection: options.maxPagesPerConnection,
|
|
62
|
+
maxConnections: options.maxConnections,
|
|
63
|
+
protocol: options.protocol,
|
|
64
|
+
reach: options.reach,
|
|
65
|
+
connect: options.connect,
|
|
66
|
+
release: options.release,
|
|
67
|
+
platform: options.platform,
|
|
68
|
+
retry: {
|
|
69
|
+
attempts: options.retry?.attempts ?? 3,
|
|
70
|
+
backoffMs: options.retry?.backoffMs ?? 500
|
|
71
|
+
},
|
|
72
|
+
timeouts: {
|
|
73
|
+
acquireMs: options.timeouts?.acquireMs ?? 3e4,
|
|
74
|
+
connectMs: options.timeouts?.connectMs ?? 3e4,
|
|
75
|
+
releaseMs: options.timeouts?.releaseMs ?? 3e4,
|
|
76
|
+
probeMs: options.timeouts?.probeMs ?? 15e3
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/** The targets a service offers, in the order of its `engines`. */
|
|
81
|
+
function remoteTargetsOf(remote) {
|
|
82
|
+
return remote.engines.map((engine) => ({
|
|
83
|
+
kind: "remote",
|
|
84
|
+
target: `${engine}@${remote.name}`,
|
|
85
|
+
engine,
|
|
86
|
+
remote,
|
|
87
|
+
reach: remote.reach
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
/** Whether a value is a remote service entry. */
|
|
91
|
+
function isRemoteBrowser(value) {
|
|
92
|
+
return typeof value === "object" && value !== null && value.kind === "remote";
|
|
93
|
+
}
|
|
94
|
+
/** Whether `endpoint` has a scheme `protocol` can open. */
|
|
95
|
+
function hasSchemeFor(protocol, endpoint) {
|
|
96
|
+
try {
|
|
97
|
+
return SCHEMES[protocol].includes(new URL(endpoint).protocol);
|
|
98
|
+
} catch {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/** The schemes `protocol` accepts, for messages. */
|
|
103
|
+
function schemesOf(protocol) {
|
|
104
|
+
return SCHEMES[protocol].join(" or ");
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Remove what identifies the connection from text surfaced to operators: the endpoint (it can carry
|
|
108
|
+
* a key in its path or query), its host and path, its query values, every header value and every
|
|
109
|
+
* value inside the lease (non-strings are stringified).
|
|
110
|
+
*/
|
|
111
|
+
function scrubConnection(text, connection) {
|
|
112
|
+
const secrets = [
|
|
113
|
+
connection.endpoint,
|
|
114
|
+
...Object.values(connection.headers ?? {}),
|
|
115
|
+
...leaseSecrets(connection.lease)
|
|
116
|
+
];
|
|
117
|
+
try {
|
|
118
|
+
const url = new URL(connection.endpoint);
|
|
119
|
+
secrets.push(url.password, ...url.searchParams.values(), decodeURIComponent(url.search), `${url.protocol}//${url.host}${url.pathname}`, url.host, url.pathname);
|
|
120
|
+
} catch {}
|
|
121
|
+
let scrubbed = text;
|
|
122
|
+
for (const secret of secrets.filter((value) => value.length >= 4).toSorted((a, b) => b.length - a.length)) scrubbed = scrubbed.replaceAll(secret, "[redacted]");
|
|
123
|
+
return scrubbed;
|
|
124
|
+
}
|
|
125
|
+
/** Every value of a lease, as text: a string whole, anything else stringified, objects walked. */
|
|
126
|
+
function leaseSecrets(lease, depth = 0) {
|
|
127
|
+
if (lease === void 0 || lease === null) return [];
|
|
128
|
+
if (typeof lease === "object") return depth > 3 ? [] : Object.values(lease).flatMap((value) => leaseSecrets(value, depth + 1));
|
|
129
|
+
return typeof lease === "string" || typeof lease === "number" || typeof lease === "boolean" ? [String(lease)] : [];
|
|
130
|
+
}
|
|
131
|
+
function checkKnownKeys(options, where) {
|
|
132
|
+
for (const key of Object.keys(options)) if (!KNOWN.has(key)) {
|
|
133
|
+
const moved = MOVED[key];
|
|
134
|
+
throw new Error(moved === void 0 ? `${where}: unknown option "${key}"` : `${where}: option "${key}" is gone in 0.8: ${moved}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function checkOptions(options, { label }) {
|
|
138
|
+
const where = `${label} "${options.name}"`;
|
|
139
|
+
checkKnownKeys(options, where);
|
|
140
|
+
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`);
|
|
141
|
+
checkSessions(options.sessions, where);
|
|
142
|
+
checkPositiveInteger("maxPagesPerConnection", options.maxPagesPerConnection, where);
|
|
143
|
+
checkPositiveInteger("maxConnections", options.maxConnections, where);
|
|
144
|
+
checkNumbers(options, where);
|
|
145
|
+
if (options.release !== void 0 && typeof options.connect !== "function") throw new Error(`${where}: release needs a function connect (a fixed connection has no lease)`);
|
|
146
|
+
}
|
|
147
|
+
/** An optional limit: absent, or a whole number of at least 1. */
|
|
148
|
+
function checkPositiveInteger(name, value, where) {
|
|
149
|
+
if (value !== void 0 && (!Number.isInteger(value) || value < 1)) throw new Error(`${where}: ${name} must be a positive integer`);
|
|
150
|
+
}
|
|
151
|
+
function checkSessions(sessions, where) {
|
|
152
|
+
const value = typeof sessions === "function" ? sessions() : sessions;
|
|
153
|
+
if (!Number.isInteger(value) || value < 1) throw new Error(`${where}: sessions must be a positive integer`);
|
|
154
|
+
}
|
|
155
|
+
function checkNumbers(options, where) {
|
|
156
|
+
const entries = [
|
|
157
|
+
["retry.attempts", options.retry?.attempts],
|
|
158
|
+
["timeouts.acquireMs", options.timeouts?.acquireMs],
|
|
159
|
+
["timeouts.connectMs", options.timeouts?.connectMs],
|
|
160
|
+
["timeouts.releaseMs", options.timeouts?.releaseMs],
|
|
161
|
+
["timeouts.probeMs", options.timeouts?.probeMs],
|
|
162
|
+
["retry.backoffMs", options.retry?.backoffMs]
|
|
163
|
+
];
|
|
164
|
+
for (const [name, value] of entries) {
|
|
165
|
+
const allowed = name === "retry.attempts" || name === "retry.backoffMs" ? 0 : 1;
|
|
166
|
+
if (value !== void 0 && (!Number.isInteger(value) || value < allowed)) throw new Error(`${where}: ${name} must be an integer of at least ${allowed}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function checkEngines(options, support) {
|
|
170
|
+
const where = `${support.label} "${options.name}"`;
|
|
171
|
+
if (options.engines.length === 0) throw new Error(`${where}: engines must list at least one engine`);
|
|
172
|
+
for (const [index, engine] of options.engines.entries()) {
|
|
173
|
+
if (!isBrowserName(engine)) throw new Error(`${where}: unknown engine "${String(engine)}"`);
|
|
174
|
+
if (!support.engines.includes(engine)) throw new Error(`${where}: this runner cannot drive "${engine}" (it drives ${support.engines.join(", ")})`);
|
|
175
|
+
if (options.engines.indexOf(engine) !== index) throw new Error(`${where}: engine "${engine}" is listed twice`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function checkProtocol(options, support) {
|
|
179
|
+
const where = `${support.label} "${options.name}"`;
|
|
180
|
+
checkDeclared(options, support, where);
|
|
181
|
+
if (options.protocol === "cdp") checkCdp(options, where);
|
|
182
|
+
if (typeof options.connect !== "function") checkFixedConnection(options.connect, options.protocol, where);
|
|
183
|
+
}
|
|
184
|
+
function checkDeclared(options, support, where) {
|
|
185
|
+
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)`);
|
|
186
|
+
if (!support.protocols.includes(options.protocol)) throw new Error(`${where}: protocol "${options.protocol}" is not supported by this runner (it speaks ${support.protocols.join(", ")})`);
|
|
187
|
+
if (options.reach !== "loopback" && options.reach !== "url") throw new Error(`${where}: reach must be "loopback" or "url"`);
|
|
188
|
+
}
|
|
189
|
+
function checkCdp(options, where) {
|
|
190
|
+
const bad = options.engines.find((engine) => !CDP_ENGINES.has(engine));
|
|
191
|
+
if (bad !== void 0) throw new Error(`${where}: the CDP protocol drives Chromium only, not "${bad}"`);
|
|
192
|
+
if (options.reach === "loopback") throw new Error(`${where}: reach "loopback" needs the "playwright" protocol`);
|
|
193
|
+
}
|
|
194
|
+
function checkFixedConnection(connection, protocol, where) {
|
|
195
|
+
const extra = Object.keys(connection).filter((key) => key !== "endpoint" && key !== "headers");
|
|
196
|
+
if (extra.length > 0) throw new Error(`${where}: connect has unknown field(s) ${extra.join(", ")}`);
|
|
197
|
+
if (!hasSchemeFor(protocol, connection.endpoint)) throw new Error(`${where}: the endpoint must be a ${schemesOf(protocol)} URL`);
|
|
198
|
+
}
|
|
199
|
+
export { DEFAULT_PROBE_MS, defineRemote, hasSchemeFor, isRemoteBrowser, remoteTargetsOf, schemesOf, scrubConnection };
|
|
200
|
+
|
|
201
|
+
//# sourceMappingURL=remote-browser.mjs.map
|
|
@@ -0,0 +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/** 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 /** 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 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 \"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 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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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
// src/adapters/remote-lease-attempt.ts
|
|
5
|
+
const attempts = /* @__PURE__ */ new Set();
|
|
6
|
+
/** Start an attempt tied to the run's signal; a signal already aborted stops it at once. */
|
|
7
|
+
function startAttempt(parent) {
|
|
8
|
+
const controller = new AbortController();
|
|
9
|
+
const onAbort = () => {
|
|
10
|
+
attempt.stop();
|
|
11
|
+
};
|
|
12
|
+
const attempt = {
|
|
13
|
+
controller,
|
|
14
|
+
stopped: false,
|
|
15
|
+
stop: () => {
|
|
16
|
+
attempt.stopped = true;
|
|
17
|
+
controller.abort();
|
|
18
|
+
},
|
|
19
|
+
detach: () => {
|
|
20
|
+
parent?.removeEventListener("abort", onAbort);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
attempts.add(attempt);
|
|
24
|
+
if (parent?.aborted === true) attempt.stop();
|
|
25
|
+
else parent?.addEventListener("abort", onAbort, { once: true });
|
|
26
|
+
return attempt;
|
|
27
|
+
}
|
|
28
|
+
/** The attempt is over: forget it and stop listening to the run's signal. */
|
|
29
|
+
function endAttempt(attempt) {
|
|
30
|
+
attempts.delete(attempt);
|
|
31
|
+
attempt.detach();
|
|
32
|
+
}
|
|
33
|
+
/** Stop every attempt still pending (shutdown). */
|
|
34
|
+
function stopAllAttempts() {
|
|
35
|
+
for (const attempt of attempts) attempt.stop();
|
|
36
|
+
}
|
|
37
|
+
export { endAttempt, startAttempt, stopAllAttempts };
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=remote-lease-attempt.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-lease-attempt.mjs","names":[],"sources":["../../src/adapters/remote-lease-attempt.ts"],"sourcesContent":["/** A `connect` attempt in flight, so a cancel or shutdown can reach it (ADR 0007, decision 4). */\n\n/** A `connect` attempt in flight: aborted by its deadline, a cancel or shutdown. */\nexport interface Attempt {\n readonly controller: AbortController;\n stopped: boolean;\n stop(): void;\n /** Stop listening to the run's signal, so retries do not pile up listeners on it. */\n detach(): void;\n}\n\nconst attempts = new Set<Attempt>();\n\n/** Start an attempt tied to the run's signal; a signal already aborted stops it at once. */\nexport function startAttempt(parent?: AbortSignal): Attempt {\n const controller = new AbortController();\n const onAbort = (): void => {\n attempt.stop();\n };\n const attempt: Attempt = {\n controller,\n stopped: false,\n stop: () => {\n attempt.stopped = true;\n controller.abort();\n },\n detach: () => {\n parent?.removeEventListener(\"abort\", onAbort);\n },\n };\n attempts.add(attempt);\n if (parent?.aborted === true) {\n attempt.stop();\n } else {\n parent?.addEventListener(\"abort\", onAbort, { once: true });\n }\n return attempt;\n}\n\n/** The attempt is over: forget it and stop listening to the run's signal. */\nexport function endAttempt(attempt: Attempt): void {\n attempts.delete(attempt);\n attempt.detach();\n}\n\n/** Stop every attempt still pending (shutdown). */\nexport function stopAllAttempts(): void {\n for (const attempt of attempts) {\n attempt.stop();\n }\n}\n"],"mappings":";;;;AAWA,MAAM,2BAAW,IAAI,IAAa;;AAGlC,SAAgB,aAAa,QAA+B;CAC1D,MAAM,aAAa,IAAI,gBAAgB;CACvC,MAAM,gBAAsB;EAC1B,QAAQ,KAAK;CACf;CACA,MAAM,UAAmB;EACvB;EACA,SAAS;EACT,YAAY;GACV,QAAQ,UAAU;GAClB,WAAW,MAAM;EACnB;EACA,cAAc;GACZ,QAAQ,oBAAoB,SAAS,OAAO;EAC9C;CACF;CACA,SAAS,IAAI,OAAO;CACpB,IAAI,QAAQ,YAAY,MACtB,QAAQ,KAAK;MAEb,QAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;CAE3D,OAAO;AACT;;AAGA,SAAgB,WAAW,SAAwB;CACjD,SAAS,OAAO,OAAO;CACvB,QAAQ,OAAO;AACjB;;AAGA,SAAgB,kBAAwB;CACtC,KAAK,MAAM,WAAW,UACpB,QAAQ,KAAK;AAEjB"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
/** What a connect failure means for the retry loop and for the target. */
|
|
4
|
+
interface RemoteConnectErrorOptions {
|
|
5
|
+
/** `false` stops the retries and fails the job (default `true`: retried). */
|
|
6
|
+
transient?: boolean | undefined;
|
|
7
|
+
/**
|
|
8
|
+
* For `transient: false`: `target` (default) also marks the target unavailable until restart,
|
|
9
|
+
* `job` fails only this job and keeps the target offered (a per-shard server that is gone).
|
|
10
|
+
*/
|
|
11
|
+
scope?: "target" | "job" | undefined;
|
|
12
|
+
/** A lease the failed `connect` already took, released as `failed`. */
|
|
13
|
+
lease?: unknown;
|
|
14
|
+
cause?: unknown;
|
|
15
|
+
}
|
|
16
|
+
/** An error a function `connect` throws to say how the runner should treat the failure. */
|
|
17
|
+
declare class RemoteConnectError extends Error {
|
|
18
|
+
readonly name = "RemoteConnectError";
|
|
19
|
+
readonly transient: boolean;
|
|
20
|
+
readonly scope: "target" | "job";
|
|
21
|
+
readonly lease: unknown;
|
|
22
|
+
constructor(message: string, options?: RemoteConnectErrorOptions);
|
|
23
|
+
}
|
|
24
|
+
export { RemoteConnectError, RemoteConnectErrorOptions };
|
|
25
|
+
//# sourceMappingURL=remote-lease-error.d.mts.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { isRetryableConnectError } from "../utils/connect-retry.mjs";
|
|
5
|
+
// src/adapters/remote-lease-error.ts
|
|
6
|
+
/** How a connect failure is classified (ADR 0007, decision 4): retried or not, and what it does to the target. */
|
|
7
|
+
/** An error a function `connect` throws to say how the runner should treat the failure. */
|
|
8
|
+
var RemoteConnectError = class extends Error {
|
|
9
|
+
name = "RemoteConnectError";
|
|
10
|
+
transient;
|
|
11
|
+
scope;
|
|
12
|
+
lease;
|
|
13
|
+
constructor(message, options = {}) {
|
|
14
|
+
super(message, options.cause === void 0 ? void 0 : { cause: options.cause });
|
|
15
|
+
this.transient = options.transient ?? true;
|
|
16
|
+
this.scope = options.scope ?? "target";
|
|
17
|
+
this.lease = options.lease;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
/** Marks the error of a `connect` abandoned because the run was cancelled or the process is shutting down. */
|
|
21
|
+
const STOPPED = Symbol("remote connect stopped");
|
|
22
|
+
/** The error of a `connect` that was cancelled or cut short by shutdown: never retried. */
|
|
23
|
+
function stoppedError() {
|
|
24
|
+
return Object.assign(/* @__PURE__ */ new Error("cancelled while connecting"), { [STOPPED]: true });
|
|
25
|
+
}
|
|
26
|
+
/** What a connect error means: whether to retry it, and whether it marks the target unavailable. */
|
|
27
|
+
function classify(error) {
|
|
28
|
+
if (typeof error === "object" && error !== null && STOPPED in error) return {
|
|
29
|
+
retry: false,
|
|
30
|
+
unavailable: false
|
|
31
|
+
};
|
|
32
|
+
if (error instanceof RemoteConnectError) return {
|
|
33
|
+
retry: error.transient,
|
|
34
|
+
unavailable: !error.transient && error.scope !== "job"
|
|
35
|
+
};
|
|
36
|
+
const retry = isRetryableConnectError(error);
|
|
37
|
+
return {
|
|
38
|
+
retry,
|
|
39
|
+
unavailable: !retry
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** A provider's thrown value as a {@link RemoteConnectError}: transient unless it says `transient: false`. */
|
|
43
|
+
function connectErrorOf(error) {
|
|
44
|
+
const flags = typeof error === "object" && error !== null ? error : {};
|
|
45
|
+
return new RemoteConnectError(`acquire failed: ${error instanceof Error ? error.message : String(error)}`, {
|
|
46
|
+
cause: error,
|
|
47
|
+
transient: flags.transient !== false,
|
|
48
|
+
scope: flags.scope === "job" ? "job" : "target",
|
|
49
|
+
lease: flags.lease
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
export { RemoteConnectError, classify, connectErrorOf, stoppedError };
|
|
53
|
+
|
|
54
|
+
//# sourceMappingURL=remote-lease-error.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-lease-error.mjs","names":[],"sources":["../../src/adapters/remote-lease-error.ts"],"sourcesContent":["/** How a connect failure is classified (ADR 0007, decision 4): retried or not, and what it does to the target. */\nimport { isRetryableConnectError } from \"../utils/connect-retry.ts\";\n\n/** What a connect failure means for the retry loop and for the target. */\nexport interface RemoteConnectErrorOptions {\n /** `false` stops the retries and fails the job (default `true`: retried). */\n transient?: boolean | undefined;\n /**\n * For `transient: false`: `target` (default) also marks the target unavailable until restart,\n * `job` fails only this job and keeps the target offered (a per-shard server that is gone).\n */\n scope?: \"target\" | \"job\" | undefined;\n /** A lease the failed `connect` already took, released as `failed`. */\n lease?: unknown;\n cause?: unknown;\n}\n\n/** An error a function `connect` throws to say how the runner should treat the failure. */\nexport class RemoteConnectError extends Error {\n override readonly name = \"RemoteConnectError\";\n readonly transient: boolean;\n readonly scope: \"target\" | \"job\";\n readonly lease: unknown;\n\n constructor(message: string, options: RemoteConnectErrorOptions = {}) {\n super(message, options.cause === undefined ? undefined : { cause: options.cause });\n this.transient = options.transient ?? true;\n this.scope = options.scope ?? \"target\";\n this.lease = options.lease;\n }\n}\n\n/** Marks the error of a `connect` abandoned because the run was cancelled or the process is shutting down. */\nconst STOPPED = Symbol(\"remote connect stopped\");\n\n/** The error of a `connect` that was cancelled or cut short by shutdown: never retried. */\nexport function stoppedError(): Error {\n return Object.assign(new Error(\"cancelled while connecting\"), { [STOPPED]: true });\n}\n\n/** What a connect error means: whether to retry it, and whether it marks the target unavailable. */\nexport function classify(error: unknown): { retry: boolean; unavailable: boolean } {\n if (typeof error === \"object\" && error !== null && STOPPED in error) {\n return { retry: false, unavailable: false };\n }\n if (error instanceof RemoteConnectError) {\n return { retry: error.transient, unavailable: !error.transient && error.scope !== \"job\" };\n }\n const retry = isRetryableConnectError(error);\n return { retry, unavailable: !retry };\n}\n\n/** A provider's thrown value as a {@link RemoteConnectError}: transient unless it says `transient: false`. */\nexport function connectErrorOf(error: unknown): RemoteConnectError {\n const flags = (typeof error === \"object\" && error !== null ? error : {}) as {\n transient?: unknown;\n scope?: unknown;\n lease?: unknown;\n };\n return new RemoteConnectError(\n `acquire failed: ${error instanceof Error ? error.message : String(error)}`,\n {\n cause: error,\n transient: flags.transient !== false,\n scope: flags.scope === \"job\" ? \"job\" : \"target\",\n lease: flags.lease,\n },\n );\n}\n"],"mappings":";;;;;;;AAkBA,IAAa,qBAAb,cAAwC,MAAM;CAC5C,OAAyB;CACzB;CACA;CACA;CAEA,YAAY,SAAiB,UAAqC,CAAC,GAAG;EACpE,MAAM,SAAS,QAAQ,UAAU,KAAA,IAAY,KAAA,IAAY,EAAE,OAAO,QAAQ,MAAM,CAAC;EACjF,KAAK,YAAY,QAAQ,aAAa;EACtC,KAAK,QAAQ,QAAQ,SAAS;EAC9B,KAAK,QAAQ,QAAQ;CACvB;AACF;;AAGA,MAAM,UAAU,OAAO,wBAAwB;;AAG/C,SAAgB,eAAsB;CACpC,OAAO,OAAO,uBAAO,IAAI,MAAM,4BAA4B,GAAG,GAAG,UAAU,KAAK,CAAC;AACnF;;AAGA,SAAgB,SAAS,OAA0D;CACjF,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,WAAW,OAC5D,OAAO;EAAE,OAAO;EAAO,aAAa;CAAM;CAE5C,IAAI,iBAAiB,oBACnB,OAAO;EAAE,OAAO,MAAM;EAAW,aAAa,CAAC,MAAM,aAAa,MAAM,UAAU;CAAM;CAE1F,MAAM,QAAQ,wBAAwB,KAAK;CAC3C,OAAO;EAAE;EAAO,aAAa,CAAC;CAAM;AACtC;;AAGA,SAAgB,eAAe,OAAoC;CACjE,MAAM,QAAS,OAAO,UAAU,YAAY,UAAU,OAAO,QAAQ,CAAC;CAKtE,OAAO,IAAI,mBACT,mBAAmB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KACxE;EACE,OAAO;EACP,WAAW,MAAM,cAAc;EAC/B,OAAO,MAAM,UAAU,QAAQ,QAAQ;EACvC,OAAO,MAAM;CACf,CACF;AACF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { RemoteConnection, RemoteLeaseOutcome } from "./remote-browser.mjs";
|
|
4
|
+
import "../logger.mjs";
|
|
5
|
+
/** A connection held until its render ends. */
|
|
6
|
+
interface HeldConnection {
|
|
7
|
+
/** The connection to open (with its lease, for scrubbing): the function's result, or the fixed object. */
|
|
8
|
+
readonly connection: RemoteConnection & {
|
|
9
|
+
lease?: unknown;
|
|
10
|
+
};
|
|
11
|
+
/** Release the lease with how the render ended; later calls do nothing (the first outcome wins). */
|
|
12
|
+
end(outcome: RemoteLeaseOutcome, error?: unknown): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export { HeldConnection };
|
|
15
|
+
//# sourceMappingURL=remote-lease-held.d.mts.map
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
// src/adapters/remote-lease-held.ts
|
|
5
|
+
const held = /* @__PURE__ */ new Set();
|
|
6
|
+
const warnedNoLease = /* @__PURE__ */ new Set();
|
|
7
|
+
/** Release every lease still held, as `cancelled`. */
|
|
8
|
+
async function releaseAllHeld() {
|
|
9
|
+
await Promise.all([...held].map(async (lease) => {
|
|
10
|
+
await lease.end("cancelled");
|
|
11
|
+
}));
|
|
12
|
+
}
|
|
13
|
+
/** `pending`, or the error `onTimeout` makes after `ms`. */
|
|
14
|
+
async function withDeadline(pending, ms, onTimeout) {
|
|
15
|
+
let timer;
|
|
16
|
+
const deadline = new Promise((_, reject) => {
|
|
17
|
+
timer = setTimeout(() => {
|
|
18
|
+
reject(onTimeout());
|
|
19
|
+
}, ms);
|
|
20
|
+
});
|
|
21
|
+
try {
|
|
22
|
+
return await Promise.race([pending, deadline]);
|
|
23
|
+
} finally {
|
|
24
|
+
clearTimeout(timer);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function noLease(connection) {
|
|
28
|
+
return {
|
|
29
|
+
connection,
|
|
30
|
+
end: async () => {
|
|
31
|
+
await Promise.resolve();
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Hold `lease` for `connection`: `end` calls the service's `release` once, bounded by
|
|
37
|
+
* `timeouts.releaseMs`, and a failing or hanging release is logged (class only) and never thrown.
|
|
38
|
+
* No lease means nothing to release: a warning says so once per target when `release` is set.
|
|
39
|
+
*/
|
|
40
|
+
function holdLease(entry, lease, connection, logger) {
|
|
41
|
+
const { release, timeouts } = entry.remote;
|
|
42
|
+
if (lease === void 0) {
|
|
43
|
+
if (release !== void 0 && !warnedNoLease.has(entry.target)) {
|
|
44
|
+
warnedNoLease.add(entry.target);
|
|
45
|
+
logger?.warn({ target: entry.target }, "remote release is set but connect returned no lease, so release is skipped");
|
|
46
|
+
}
|
|
47
|
+
return noLease(connection);
|
|
48
|
+
}
|
|
49
|
+
let ended = false;
|
|
50
|
+
const connectionHeld = {
|
|
51
|
+
connection: {
|
|
52
|
+
...connection,
|
|
53
|
+
lease
|
|
54
|
+
},
|
|
55
|
+
end: async (outcome, error) => {
|
|
56
|
+
if (ended) return;
|
|
57
|
+
ended = true;
|
|
58
|
+
held.delete(connectionHeld);
|
|
59
|
+
await runRelease(entry, async () => {
|
|
60
|
+
await release?.(lease, {
|
|
61
|
+
outcome,
|
|
62
|
+
error
|
|
63
|
+
});
|
|
64
|
+
}, timeouts.releaseMs, {
|
|
65
|
+
logger,
|
|
66
|
+
outcome
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
held.add(connectionHeld);
|
|
71
|
+
return connectionHeld;
|
|
72
|
+
}
|
|
73
|
+
async function runRelease(entry, call, timeoutMs, log) {
|
|
74
|
+
try {
|
|
75
|
+
await withDeadline(call(), timeoutMs, () => Object.assign(/* @__PURE__ */ new Error(`release timed out after ${timeoutMs} ms`), { name: "ReleaseTimeout" }));
|
|
76
|
+
} catch (error) {
|
|
77
|
+
log.logger?.warn({
|
|
78
|
+
target: entry.target,
|
|
79
|
+
outcome: log.outcome,
|
|
80
|
+
err: error instanceof Error ? error.name : "Error"
|
|
81
|
+
}, "remote browser release failed");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export { holdLease, noLease, releaseAllHeld, withDeadline };
|
|
85
|
+
|
|
86
|
+
//# sourceMappingURL=remote-lease-held.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-lease-held.mjs","names":[],"sources":["../../src/adapters/remote-lease-held.ts"],"sourcesContent":["/** A connection held until its render ends, and the one release it owes (ADR 0007, decision 4). */\nimport type { Logger } from \"../logger.ts\";\nimport type { RemoteConnection, RemoteLeaseOutcome, RemoteTarget } from \"./remote-browser.ts\";\n\n/** A connection held until its render ends. */\nexport interface HeldConnection {\n /** The connection to open (with its lease, for scrubbing): the function's result, or the fixed object. */\n readonly connection: RemoteConnection & { lease?: unknown };\n /** Release the lease with how the render ended; later calls do nothing (the first outcome wins). */\n end(outcome: RemoteLeaseOutcome, error?: unknown): Promise<void>;\n}\n\nconst held = new Set<HeldConnection>();\nconst warnedNoLease = new Set<string>();\n\n/** Release every lease still held, as `cancelled`. */\nexport async function releaseAllHeld(): Promise<void> {\n await Promise.all(\n [...held].map(async (lease) => {\n await lease.end(\"cancelled\");\n }),\n );\n}\n\n/** `pending`, or the error `onTimeout` makes after `ms`. */\nexport async function withDeadline<T>(\n pending: Promise<T>,\n ms: number,\n onTimeout: () => Error,\n): Promise<T> {\n let timer: NodeJS.Timeout | undefined;\n const deadline = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n reject(onTimeout());\n }, ms);\n });\n try {\n return await Promise.race([pending, deadline]);\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport function noLease(connection: RemoteConnection): HeldConnection {\n return {\n connection,\n end: async () => {\n await Promise.resolve();\n },\n };\n}\n\n/**\n * Hold `lease` for `connection`: `end` calls the service's `release` once, bounded by\n * `timeouts.releaseMs`, and a failing or hanging release is logged (class only) and never thrown.\n * No lease means nothing to release: a warning says so once per target when `release` is set.\n */\nexport function holdLease(\n entry: RemoteTarget,\n lease: unknown,\n connection: RemoteConnection,\n logger?: Logger,\n): HeldConnection {\n const { release, timeouts } = entry.remote;\n if (lease === undefined) {\n if (release !== undefined && !warnedNoLease.has(entry.target)) {\n warnedNoLease.add(entry.target);\n logger?.warn(\n { target: entry.target },\n \"remote release is set but connect returned no lease, so release is skipped\",\n );\n }\n return noLease(connection);\n }\n let ended = false;\n const connectionHeld: HeldConnection = {\n connection: { ...connection, lease },\n end: async (outcome, error) => {\n if (ended) {\n return;\n }\n ended = true;\n held.delete(connectionHeld);\n await runRelease(\n entry,\n async () => {\n await release?.(lease as never, { outcome, error });\n },\n timeouts.releaseMs,\n { logger, outcome },\n );\n },\n };\n held.add(connectionHeld);\n return connectionHeld;\n}\n\nasync function runRelease(\n entry: RemoteTarget,\n call: () => Promise<void>,\n timeoutMs: number,\n log: { logger: Logger | undefined; outcome: RemoteLeaseOutcome },\n): Promise<void> {\n try {\n await withDeadline(call(), timeoutMs, () =>\n Object.assign(new Error(`release timed out after ${timeoutMs} ms`), {\n name: \"ReleaseTimeout\",\n }),\n );\n } catch (error) {\n // Never the lease or the error text: either can carry a credential.\n log.logger?.warn(\n {\n target: entry.target,\n outcome: log.outcome,\n err: error instanceof Error ? error.name : \"Error\",\n },\n \"remote browser release failed\",\n );\n }\n}\n"],"mappings":";;;;AAYA,MAAM,uBAAO,IAAI,IAAoB;AACrC,MAAM,gCAAgB,IAAI,IAAY;;AAGtC,eAAsB,iBAAgC;CACpD,MAAM,QAAQ,IACZ,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,OAAO,UAAU;EAC7B,MAAM,MAAM,IAAI,WAAW;CAC7B,CAAC,CACH;AACF;;AAGA,eAAsB,aACpB,SACA,IACA,WACY;CACZ,IAAI;CACJ,MAAM,WAAW,IAAI,SAAgB,GAAG,WAAW;EACjD,QAAQ,iBAAiB;GACvB,OAAO,UAAU,CAAC;EACpB,GAAG,EAAE;CACP,CAAC;CACD,IAAI;EACF,OAAO,MAAM,QAAQ,KAAK,CAAC,SAAS,QAAQ,CAAC;CAC/C,UAAU;EACR,aAAa,KAAK;CACpB;AACF;AAEA,SAAgB,QAAQ,YAA8C;CACpE,OAAO;EACL;EACA,KAAK,YAAY;GACf,MAAM,QAAQ,QAAQ;EACxB;CACF;AACF;;;;;;AAOA,SAAgB,UACd,OACA,OACA,YACA,QACgB;CAChB,MAAM,EAAE,SAAS,aAAa,MAAM;CACpC,IAAI,UAAU,KAAA,GAAW;EACvB,IAAI,YAAY,KAAA,KAAa,CAAC,cAAc,IAAI,MAAM,MAAM,GAAG;GAC7D,cAAc,IAAI,MAAM,MAAM;GAC9B,QAAQ,KACN,EAAE,QAAQ,MAAM,OAAO,GACvB,4EACF;EACF;EACA,OAAO,QAAQ,UAAU;CAC3B;CACA,IAAI,QAAQ;CACZ,MAAM,iBAAiC;EACrC,YAAY;GAAE,GAAG;GAAY;EAAM;EACnC,KAAK,OAAO,SAAS,UAAU;GAC7B,IAAI,OACF;GAEF,QAAQ;GACR,KAAK,OAAO,cAAc;GAC1B,MAAM,WACJ,OACA,YAAY;IACV,MAAM,UAAU,OAAgB;KAAE;KAAS;IAAM,CAAC;GACpD,GACA,SAAS,WACT;IAAE;IAAQ;GAAQ,CACpB;EACF;CACF;CACA,KAAK,IAAI,cAAc;CACvB,OAAO;AACT;AAEA,eAAe,WACb,OACA,MACA,WACA,KACe;CACf,IAAI;EACF,MAAM,aAAa,KAAK,GAAG,iBACzB,OAAO,uBAAO,IAAI,MAAM,2BAA2B,UAAU,IAAI,GAAG,EAClE,MAAM,iBACR,CAAC,CACH;CACF,SAAS,OAAO;EAEd,IAAI,QAAQ,KACV;GACE,QAAQ,MAAM;GACd,SAAS,IAAI;GACb,KAAK,iBAAiB,QAAQ,MAAM,OAAO;EAC7C,GACA,+BACF;CACF;AACF"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
/**
|
|
4
|
+
* Stop everything remote this process holds, for a runner shutting down: abort pending `connect`
|
|
5
|
+
* calls, release every lease still held as `cancelled`, then run the shutdown hooks (pools close
|
|
6
|
+
* their resources). A server is not left running by a process that is gone.
|
|
7
|
+
*/
|
|
8
|
+
declare function shutdownRemote(): Promise<void>;
|
|
9
|
+
/** Run `hook` on shutdown; returns the function that unregisters it. */
|
|
10
|
+
declare function onRemoteShutdown(hook: () => Promise<void>): () => void;
|
|
11
|
+
export { onRemoteShutdown, shutdownRemote };
|
|
12
|
+
//# sourceMappingURL=remote-lease-shutdown.d.mts.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { stopAllAttempts } from "./remote-lease-attempt.mjs";
|
|
5
|
+
import { releaseAllHeld } from "./remote-lease-held.mjs";
|
|
6
|
+
import { resetAllLimiters } from "./remote-limiter.mjs";
|
|
7
|
+
// src/adapters/remote-lease-shutdown.ts
|
|
8
|
+
/** Stopping everything remote the process holds, for a runner shutting down (ADR 0007, decision 4). */
|
|
9
|
+
const shutdownHooks = /* @__PURE__ */ new Set();
|
|
10
|
+
/**
|
|
11
|
+
* Stop everything remote this process holds, for a runner shutting down: abort pending `connect`
|
|
12
|
+
* calls, release every lease still held as `cancelled`, then run the shutdown hooks (pools close
|
|
13
|
+
* their resources). A server is not left running by a process that is gone.
|
|
14
|
+
*/
|
|
15
|
+
async function shutdownRemote() {
|
|
16
|
+
stopAllAttempts();
|
|
17
|
+
resetAllLimiters();
|
|
18
|
+
await releaseAllHeld();
|
|
19
|
+
await Promise.all([...shutdownHooks].map(async (hook) => {
|
|
20
|
+
await hook();
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
/** Run `hook` on shutdown; returns the function that unregisters it. */
|
|
24
|
+
function onRemoteShutdown(hook) {
|
|
25
|
+
shutdownHooks.add(hook);
|
|
26
|
+
return () => {
|
|
27
|
+
shutdownHooks.delete(hook);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export { onRemoteShutdown, shutdownRemote };
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=remote-lease-shutdown.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-lease-shutdown.mjs","names":[],"sources":["../../src/adapters/remote-lease-shutdown.ts"],"sourcesContent":["import { stopAllAttempts } from \"./remote-lease-attempt.ts\";\n/** Stopping everything remote the process holds, for a runner shutting down (ADR 0007, decision 4). */\nimport { releaseAllHeld } from \"./remote-lease-held.ts\";\nimport { resetAllLimiters } from \"./remote-limiter.ts\";\n\nconst shutdownHooks = new Set<() => Promise<void>>();\n\n/**\n * Stop everything remote this process holds, for a runner shutting down: abort pending `connect`\n * calls, release every lease still held as `cancelled`, then run the shutdown hooks (pools close\n * their resources). A server is not left running by a process that is gone.\n */\nexport async function shutdownRemote(): Promise<void> {\n stopAllAttempts();\n resetAllLimiters();\n await releaseAllHeld();\n await Promise.all(\n [...shutdownHooks].map(async (hook) => {\n await hook();\n }),\n );\n}\n\n/** Run `hook` on shutdown; returns the function that unregisters it. */\nexport function onRemoteShutdown(hook: () => Promise<void>): () => void {\n shutdownHooks.add(hook);\n return () => {\n shutdownHooks.delete(hook);\n };\n}\n"],"mappings":";;;;;;;;AAKA,MAAM,gCAAgB,IAAI,IAAyB;;;;;;AAOnD,eAAsB,iBAAgC;CACpD,gBAAgB;CAChB,iBAAiB;CACjB,MAAM,eAAe;CACrB,MAAM,QAAQ,IACZ,CAAC,GAAG,aAAa,CAAC,CAAC,IAAI,OAAO,SAAS;EACrC,MAAM,KAAK;CACb,CAAC,CACH;AACF;;AAGA,SAAgB,iBAAiB,MAAuC;CACtE,cAAc,IAAI,IAAI;CACtB,aAAa;EACX,cAAc,OAAO,IAAI;CAC3B;AACF"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { RemoteConnectContext, RemoteConnection, RemoteTarget } from "./remote-browser.mjs";
|
|
4
|
+
import { Logger } from "../logger.mjs";
|
|
5
|
+
import { HeldConnection } from "./remote-lease-held.mjs";
|
|
6
|
+
import { RemoteConnectError, RemoteConnectErrorOptions } from "./remote-lease-error.mjs";
|
|
7
|
+
import { onRemoteShutdown, shutdownRemote } from "./remote-lease-shutdown.mjs";
|
|
8
|
+
/** Which run a connection is for. */
|
|
9
|
+
interface ConnectLeasedContext {
|
|
10
|
+
buildId: string;
|
|
11
|
+
runId: string;
|
|
12
|
+
shardIndex?: number | undefined;
|
|
13
|
+
purpose: RemoteConnectContext["purpose"];
|
|
14
|
+
/** Aborts a pending `connect` when the run is cancelled. */
|
|
15
|
+
signal?: AbortSignal | undefined;
|
|
16
|
+
}
|
|
17
|
+
/** Options of {@link connectLeased}. */
|
|
18
|
+
interface ConnectLeasedOptions<T> {
|
|
19
|
+
logger?: Logger | undefined;
|
|
20
|
+
/** The handshake over one connection; its failures are retried when connection-level. */
|
|
21
|
+
open: (connection: RemoteConnection) => Promise<T>;
|
|
22
|
+
/** One line saying what failed, with the connection's credentials removed. */
|
|
23
|
+
reasonOf: (error: unknown, connection: RemoteConnection & {
|
|
24
|
+
lease?: unknown;
|
|
25
|
+
}) => string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Connect (calling `connect` when it is a function) and open the connection, as one retried
|
|
29
|
+
* attempt: a failed handshake releases its lease as `failed` before the next attempt connects
|
|
30
|
+
* again, so at most one lease is held per connection. On success the caller owns the returned
|
|
31
|
+
* lease and must call {@link HeldConnection.end}; on failure nothing is held and a
|
|
32
|
+
* {@link TargetLaunchError} is thrown.
|
|
33
|
+
*/
|
|
34
|
+
declare function connectLeased<T>(entry: RemoteTarget, context: ConnectLeasedContext, options: ConnectLeasedOptions<T>): Promise<{
|
|
35
|
+
value: T;
|
|
36
|
+
lease: HeldConnection;
|
|
37
|
+
}>;
|
|
38
|
+
export { ConnectLeasedContext, ConnectLeasedOptions, type HeldConnection, RemoteConnectError, type RemoteConnectErrorOptions, connectLeased, onRemoteShutdown, shutdownRemote };
|
|
39
|
+
//# sourceMappingURL=remote-lease.d.mts.map
|