@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
|
@@ -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
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { TargetLaunchError } from "./capture-target.mjs";
|
|
5
|
+
import { hasSchemeFor, schemesOf } from "./remote-browser.mjs";
|
|
6
|
+
import { retryConnect } from "../utils/connect-retry.mjs";
|
|
7
|
+
import { endAttempt, startAttempt } from "./remote-lease-attempt.mjs";
|
|
8
|
+
import { RemoteConnectError, classify, connectErrorOf, stoppedError } from "./remote-lease-error.mjs";
|
|
9
|
+
import { holdLease, noLease, withDeadline } from "./remote-lease-held.mjs";
|
|
10
|
+
import { shutdownEpoch, takeSlot } from "./remote-limiter.mjs";
|
|
11
|
+
import { onRemoteShutdown, shutdownRemote } from "./remote-lease-shutdown.mjs";
|
|
12
|
+
// src/adapters/remote-lease.ts
|
|
13
|
+
/**
|
|
14
|
+
* Connect (calling `connect` when it is a function) and open the connection, as one retried
|
|
15
|
+
* attempt: a failed handshake releases its lease as `failed` before the next attempt connects
|
|
16
|
+
* again, so at most one lease is held per connection. On success the caller owns the returned
|
|
17
|
+
* lease and must call {@link HeldConnection.end}; on failure nothing is held and a
|
|
18
|
+
* {@link TargetLaunchError} is thrown.
|
|
19
|
+
*/
|
|
20
|
+
async function connectLeased(entry, context, options) {
|
|
21
|
+
const { remote } = entry;
|
|
22
|
+
let current = typeof remote.connect === "function" ? { endpoint: "" } : remote.connect;
|
|
23
|
+
let attemptNo = 0;
|
|
24
|
+
try {
|
|
25
|
+
return await retryConnect(async () => {
|
|
26
|
+
attemptNo += 1;
|
|
27
|
+
const lease = await acquire(entry, context, attemptNo, options.logger);
|
|
28
|
+
current = lease.connection;
|
|
29
|
+
try {
|
|
30
|
+
return {
|
|
31
|
+
value: await options.open(lease.connection),
|
|
32
|
+
lease
|
|
33
|
+
};
|
|
34
|
+
} catch (error) {
|
|
35
|
+
await lease.end("failed", error);
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
}, {
|
|
39
|
+
retries: remote.retry.attempts,
|
|
40
|
+
backoffMs: remote.retry.backoffMs,
|
|
41
|
+
retryable: (error) => classify(error).retry,
|
|
42
|
+
onRetry: (error, retry, delayMs) => {
|
|
43
|
+
options.logger?.warn({
|
|
44
|
+
target: entry.target,
|
|
45
|
+
retry,
|
|
46
|
+
delayMs,
|
|
47
|
+
reason: options.reasonOf(error, current)
|
|
48
|
+
}, "remote browser connect failed, retrying");
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
} catch (error) {
|
|
52
|
+
const reason = options.reasonOf(error, current);
|
|
53
|
+
throw new TargetLaunchError(entry.target, reason, {
|
|
54
|
+
cause: Object.assign(new Error(reason), { name: error instanceof Error ? error.name : "Error" }),
|
|
55
|
+
transient: !classify(error).unavailable
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* One connection, counted under the service's `maxConnections` when it has one: the slot is taken
|
|
61
|
+
* (waiting, cancellably) before `connect` runs and given back when the lease ends, or at once when
|
|
62
|
+
* the connection never materialises. Without a cap this is just {@link open}.
|
|
63
|
+
*/
|
|
64
|
+
async function acquire(entry, context, attemptNo, logger) {
|
|
65
|
+
const epoch = shutdownEpoch();
|
|
66
|
+
const taking = takeSlot(entry, context.signal, logger);
|
|
67
|
+
const slot = taking === void 0 ? void 0 : await taking;
|
|
68
|
+
stopIfShutDown(slot, epoch);
|
|
69
|
+
try {
|
|
70
|
+
const held = await open(entry, context, attemptNo, logger);
|
|
71
|
+
return slot === void 0 ? held : withSlot(held, slot);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
slot?.release();
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* A slot granted across a shutdown must not open a connection: the shutdown has already stopped the
|
|
79
|
+
* attempts and collected the leases, so nothing would release it. The slot is given back and the
|
|
80
|
+
* connection stops. Called right before `open`, with nothing awaited in between, so a shutdown cannot
|
|
81
|
+
* slip into the gap (`open` registers its attempt synchronously).
|
|
82
|
+
*/
|
|
83
|
+
function stopIfShutDown(slot, epoch) {
|
|
84
|
+
if (slot !== void 0 && shutdownEpoch() !== epoch) {
|
|
85
|
+
slot.release();
|
|
86
|
+
throw stoppedError();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** `held` that gives its slot back, once, after the lease is released. */
|
|
90
|
+
function withSlot(held, slot) {
|
|
91
|
+
let ending;
|
|
92
|
+
return {
|
|
93
|
+
connection: held.connection,
|
|
94
|
+
end: async (outcome, error) => {
|
|
95
|
+
ending ??= endAndFree(held, slot, outcome, error);
|
|
96
|
+
await ending;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
async function endAndFree(held, slot, outcome, error) {
|
|
101
|
+
try {
|
|
102
|
+
await held.end(outcome, error);
|
|
103
|
+
} finally {
|
|
104
|
+
slot.release();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async function open(entry, context, attemptNo, logger) {
|
|
108
|
+
const { remote } = entry;
|
|
109
|
+
if (typeof remote.connect !== "function") return noLease(remote.connect);
|
|
110
|
+
const attempt = startAttempt(context.signal);
|
|
111
|
+
try {
|
|
112
|
+
return await adopt(entry, await called(entry, context, attemptNo, attempt, logger), logger);
|
|
113
|
+
} finally {
|
|
114
|
+
endAttempt(attempt);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** The function `connect` under its deadline; a lease that arrives after it is released at once. */
|
|
118
|
+
async function called(entry, context, attemptNo, attempt, logger) {
|
|
119
|
+
if (attempt.stopped) throw stoppedError();
|
|
120
|
+
const pending = invoke(entry.remote.connect, {
|
|
121
|
+
engine: entry.engine,
|
|
122
|
+
buildId: context.buildId,
|
|
123
|
+
runId: context.runId,
|
|
124
|
+
shardIndex: context.shardIndex,
|
|
125
|
+
purpose: context.purpose,
|
|
126
|
+
attempt: attemptNo,
|
|
127
|
+
signal: attempt.controller.signal
|
|
128
|
+
});
|
|
129
|
+
const { acquireMs } = entry.remote.timeouts;
|
|
130
|
+
try {
|
|
131
|
+
return await withDeadline(pending, acquireMs, () => {
|
|
132
|
+
attempt.controller.abort();
|
|
133
|
+
releaseLate(entry, pending, logger).catch(() => {});
|
|
134
|
+
return new RemoteConnectError(`acquire timed out after ${acquireMs} ms`);
|
|
135
|
+
});
|
|
136
|
+
} catch (error) {
|
|
137
|
+
throw await failedConnect(entry, error, attempt, logger);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/** Call a sync or async `connect`, so a sync throw is a rejection like any other. */
|
|
141
|
+
async function invoke(connect, context) {
|
|
142
|
+
return await connect(context);
|
|
143
|
+
}
|
|
144
|
+
/** A function `connect`'s failure as the error to throw, releasing a lease it carried. */
|
|
145
|
+
async function failedConnect(entry, error, attempt, logger) {
|
|
146
|
+
const converted = error instanceof RemoteConnectError ? error : connectErrorOf(error);
|
|
147
|
+
if (converted.lease !== void 0) await holdLease(entry, converted.lease, { endpoint: "" }, logger).end("failed", converted);
|
|
148
|
+
return attempt.stopped ? stoppedError() : converted;
|
|
149
|
+
}
|
|
150
|
+
/** Check what a function `connect` returned against what the service declared, then hold it. */
|
|
151
|
+
async function adopt(entry, result, logger) {
|
|
152
|
+
const problem = problemWith(entry, result);
|
|
153
|
+
if (problem === null) return holdLease(entry, result.lease, result, logger);
|
|
154
|
+
const lease = result?.lease;
|
|
155
|
+
if (lease !== void 0) await holdLease(entry, lease, { endpoint: "" }, logger).end("failed");
|
|
156
|
+
throw new RemoteConnectError(problem, {
|
|
157
|
+
transient: false,
|
|
158
|
+
scope: "job"
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
function problemWith(entry, result) {
|
|
162
|
+
if (typeof result !== "object" || result === null || typeof result.endpoint !== "string") return "connect must return an object with an endpoint";
|
|
163
|
+
const extra = Object.keys(result).filter((key) => key !== "endpoint" && key !== "headers" && key !== "lease");
|
|
164
|
+
if (extra.length > 0) return `connect returned ${extra.join(", ")}: protocol and reach are declared on remote(), not returned`;
|
|
165
|
+
const { protocol } = entry.remote;
|
|
166
|
+
return hasSchemeFor(protocol, result.endpoint) ? null : `connect returned an endpoint that is not a ${schemesOf(protocol)} URL`;
|
|
167
|
+
}
|
|
168
|
+
/** A connection that arrives after its deadline is released at once. */
|
|
169
|
+
async function releaseLate(entry, pending, logger) {
|
|
170
|
+
const late = await pending.catch(() => null);
|
|
171
|
+
if (late?.lease !== void 0) await holdLease(entry, late.lease, { endpoint: "" }, logger).end("failed");
|
|
172
|
+
}
|
|
173
|
+
export { RemoteConnectError, connectLeased, onRemoteShutdown, shutdownRemote };
|
|
174
|
+
|
|
175
|
+
//# sourceMappingURL=remote-lease.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-lease.mjs","names":[],"sources":["../../src/adapters/remote-lease.ts"],"sourcesContent":["/**\n * Connecting to a remote browser over a lease (ADR 0007, decision 4): a function `connect` is\n * called per connection, and `release` once the render that used it has ended. The runners own the\n * handshake itself; this owns the pairing, so a lease is released exactly once on every path\n * (finish, failure, cancel, shutdown) and never logged.\n */\nimport type { Logger } from \"../logger.ts\";\nimport { retryConnect } from \"../utils/connect-retry.ts\";\nimport { TargetLaunchError } from \"./capture-target.ts\";\nimport {\n hasSchemeFor,\n schemesOf,\n type RemoteConnectContext,\n type RemoteConnection,\n type RemoteLeaseOutcome,\n type RemoteLeasedConnection,\n type RemoteTarget,\n} from \"./remote-browser.ts\";\nimport { endAttempt, startAttempt, type Attempt } from \"./remote-lease-attempt.ts\";\nimport {\n RemoteConnectError,\n classify,\n connectErrorOf,\n stoppedError,\n} from \"./remote-lease-error.ts\";\nimport { holdLease, noLease, withDeadline, type HeldConnection } from \"./remote-lease-held.ts\";\nimport { shutdownEpoch, takeSlot, type ConnectionSlot } from \"./remote-limiter.ts\";\n\nexport { RemoteConnectError, type RemoteConnectErrorOptions } from \"./remote-lease-error.ts\";\nexport type { HeldConnection } from \"./remote-lease-held.ts\";\nexport { onRemoteShutdown, shutdownRemote } from \"./remote-lease-shutdown.ts\";\n\n/** Which run a connection is for. */\nexport interface ConnectLeasedContext {\n buildId: string;\n runId: string;\n shardIndex?: number | undefined;\n purpose: RemoteConnectContext[\"purpose\"];\n /** Aborts a pending `connect` when the run is cancelled. */\n signal?: AbortSignal | undefined;\n}\n\n/** Options of {@link connectLeased}. */\nexport interface ConnectLeasedOptions<T> {\n logger?: Logger | undefined;\n /** The handshake over one connection; its failures are retried when connection-level. */\n open: (connection: RemoteConnection) => Promise<T>;\n /** One line saying what failed, with the connection's credentials removed. */\n reasonOf: (error: unknown, connection: RemoteConnection & { lease?: unknown }) => string;\n}\n\ntype ConnectFn = (\n context: RemoteConnectContext,\n) => RemoteLeasedConnection | Promise<RemoteLeasedConnection>;\n\n/**\n * Connect (calling `connect` when it is a function) and open the connection, as one retried\n * attempt: a failed handshake releases its lease as `failed` before the next attempt connects\n * again, so at most one lease is held per connection. On success the caller owns the returned\n * lease and must call {@link HeldConnection.end}; on failure nothing is held and a\n * {@link TargetLaunchError} is thrown.\n */\nexport async function connectLeased<T>(\n entry: RemoteTarget,\n context: ConnectLeasedContext,\n options: ConnectLeasedOptions<T>,\n): Promise<{ value: T; lease: HeldConnection }> {\n const { remote } = entry;\n let current: RemoteConnection & { lease?: unknown } =\n typeof remote.connect === \"function\" ? { endpoint: \"\" } : remote.connect;\n let attemptNo = 0;\n try {\n return await retryConnect(\n async () => {\n attemptNo += 1;\n const lease = await acquire(entry, context, attemptNo, options.logger);\n current = lease.connection;\n try {\n return { value: await options.open(lease.connection), lease };\n } catch (error) {\n await lease.end(\"failed\", error);\n throw error;\n }\n },\n {\n retries: remote.retry.attempts,\n backoffMs: remote.retry.backoffMs,\n retryable: (error) => classify(error).retry,\n onRetry: (error, retry, delayMs) => {\n options.logger?.warn(\n { target: entry.target, retry, delayMs, reason: options.reasonOf(error, current) },\n \"remote browser connect failed, retrying\",\n );\n },\n },\n );\n } catch (error) {\n const reason = options.reasonOf(error, current);\n throw new TargetLaunchError(entry.target, reason, {\n cause: Object.assign(new Error(reason), {\n name: error instanceof Error ? error.name : \"Error\",\n }),\n transient: !classify(error).unavailable,\n });\n }\n}\n\n/**\n * One connection, counted under the service's `maxConnections` when it has one: the slot is taken\n * (waiting, cancellably) before `connect` runs and given back when the lease ends, or at once when\n * the connection never materialises. Without a cap this is just {@link open}.\n */\nasync function acquire(\n entry: RemoteTarget,\n context: ConnectLeasedContext,\n attemptNo: number,\n logger: Logger | undefined,\n): Promise<HeldConnection> {\n // No `await` without a cap: an uncapped connection starts exactly as it did before.\n const epoch = shutdownEpoch();\n const taking = takeSlot(entry, context.signal, logger);\n const slot = taking === undefined ? undefined : await taking;\n stopIfShutDown(slot, epoch);\n try {\n const held = await open(entry, context, attemptNo, logger);\n return slot === undefined ? held : withSlot(held, slot);\n } catch (error) {\n slot?.release();\n throw error;\n }\n}\n\n/**\n * A slot granted across a shutdown must not open a connection: the shutdown has already stopped the\n * attempts and collected the leases, so nothing would release it. The slot is given back and the\n * connection stops. Called right before `open`, with nothing awaited in between, so a shutdown cannot\n * slip into the gap (`open` registers its attempt synchronously).\n */\nfunction stopIfShutDown(slot: ConnectionSlot | undefined, epoch: number): void {\n if (slot !== undefined && shutdownEpoch() !== epoch) {\n slot.release();\n throw stoppedError();\n }\n}\n\n/** `held` that gives its slot back, once, after the lease is released. */\nfunction withSlot(held: HeldConnection, slot: ConnectionSlot): HeldConnection {\n let ending: Promise<void> | undefined;\n return {\n connection: held.connection,\n // Every caller gets the first call's promise: the inner `end` returns at once for a second call,\n // which would free the slot while the first release is still running.\n end: async (outcome, error) => {\n ending ??= endAndFree(held, slot, outcome, error);\n await ending;\n },\n };\n}\n\nasync function endAndFree(\n held: HeldConnection,\n slot: ConnectionSlot,\n outcome: RemoteLeaseOutcome,\n error: unknown,\n): Promise<void> {\n try {\n await held.end(outcome, error);\n } finally {\n slot.release();\n }\n}\n\nasync function open(\n entry: RemoteTarget,\n context: ConnectLeasedContext,\n attemptNo: number,\n logger: Logger | undefined,\n): Promise<HeldConnection> {\n const { remote } = entry;\n if (typeof remote.connect !== \"function\") {\n return noLease(remote.connect);\n }\n const attempt = startAttempt(context.signal);\n try {\n const result = await called(entry, context, attemptNo, attempt, logger);\n return await adopt(entry, result, logger);\n } finally {\n endAttempt(attempt);\n }\n}\n\n/** The function `connect` under its deadline; a lease that arrives after it is released at once. */\nasync function called(\n entry: RemoteTarget,\n context: ConnectLeasedContext,\n attemptNo: number,\n attempt: Attempt,\n logger: Logger | undefined,\n): Promise<RemoteLeasedConnection> {\n if (attempt.stopped) {\n // Cancelled during a backoff: never call the provider with a signal that is already aborted.\n throw stoppedError();\n }\n const pending = invoke(entry.remote.connect as ConnectFn, {\n engine: entry.engine,\n buildId: context.buildId,\n runId: context.runId,\n shardIndex: context.shardIndex,\n purpose: context.purpose,\n attempt: attemptNo,\n signal: attempt.controller.signal,\n });\n const { acquireMs } = entry.remote.timeouts;\n try {\n return await withDeadline(pending, acquireMs, () => {\n attempt.controller.abort();\n releaseLate(entry, pending, logger).catch(() => {});\n return new RemoteConnectError(`acquire timed out after ${acquireMs} ms`);\n });\n } catch (error) {\n throw await failedConnect(entry, error, attempt, logger);\n }\n}\n\n/** Call a sync or async `connect`, so a sync throw is a rejection like any other. */\nasync function invoke(\n connect: ConnectFn,\n context: RemoteConnectContext,\n): Promise<RemoteLeasedConnection> {\n return await connect(context);\n}\n\n/** A function `connect`'s failure as the error to throw, releasing a lease it carried. */\nasync function failedConnect(\n entry: RemoteTarget,\n error: unknown,\n attempt: Attempt,\n logger: Logger | undefined,\n): Promise<Error> {\n const converted = error instanceof RemoteConnectError ? error : connectErrorOf(error);\n if (converted.lease !== undefined) {\n await holdLease(entry, converted.lease, { endpoint: \"\" }, logger).end(\"failed\", converted);\n }\n return attempt.stopped ? stoppedError() : converted;\n}\n\n/** Check what a function `connect` returned against what the service declared, then hold it. */\nasync function adopt(\n entry: RemoteTarget,\n result: RemoteLeasedConnection,\n logger: Logger | undefined,\n): Promise<HeldConnection> {\n const problem = problemWith(entry, result);\n if (problem === null) {\n return holdLease(entry, result.lease, result, logger);\n }\n const lease = (result as { lease?: unknown } | null)?.lease;\n if (lease !== undefined) {\n await holdLease(entry, lease, { endpoint: \"\" }, logger).end(\"failed\");\n }\n throw new RemoteConnectError(problem, { transient: false, scope: \"job\" });\n}\n\nfunction problemWith(entry: RemoteTarget, result: RemoteLeasedConnection): string | null {\n if (typeof result !== \"object\" || result === null || typeof result.endpoint !== \"string\") {\n return \"connect must return an object with an endpoint\";\n }\n const extra = Object.keys(result).filter(\n (key) => key !== \"endpoint\" && key !== \"headers\" && key !== \"lease\",\n );\n if (extra.length > 0) {\n return `connect returned ${extra.join(\", \")}: protocol and reach are declared on remote(), not returned`;\n }\n const { protocol } = entry.remote;\n return hasSchemeFor(protocol, result.endpoint)\n ? null\n : `connect returned an endpoint that is not a ${schemesOf(protocol)} URL`;\n}\n\n/** A connection that arrives after its deadline is released at once. */\nasync function releaseLate(\n entry: RemoteTarget,\n pending: Promise<RemoteLeasedConnection>,\n logger: Logger | undefined,\n): Promise<void> {\n const late = await pending.catch(() => null);\n if (late?.lease !== undefined) {\n await holdLease(entry, late.lease, { endpoint: \"\" }, logger).end(\"failed\");\n }\n}\n"],"mappings":"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"}
|