@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,121 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { stoppedError } from "./remote-lease-error.mjs";
|
|
5
|
+
// src/adapters/remote-limiter.ts
|
|
6
|
+
/** A counting semaphore with a FIFO queue whose waiters can be cancelled. */
|
|
7
|
+
var ConnectionLimiter = class {
|
|
8
|
+
limit;
|
|
9
|
+
#held = 0;
|
|
10
|
+
#queue = [];
|
|
11
|
+
constructor(limit) {
|
|
12
|
+
this.limit = limit;
|
|
13
|
+
}
|
|
14
|
+
/** Slots in use. */
|
|
15
|
+
get held() {
|
|
16
|
+
return this.#held;
|
|
17
|
+
}
|
|
18
|
+
/** Callers waiting for a slot. */
|
|
19
|
+
get waiting() {
|
|
20
|
+
return this.#queue.length;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Take a slot, waiting for one when all are in use. Rejects with the stopped error when `signal`
|
|
24
|
+
* aborts (or the process shuts down) while waiting, without having taken anything.
|
|
25
|
+
* `onLongWait` runs once if the wait outlasts `longWaitMs`.
|
|
26
|
+
*/
|
|
27
|
+
async acquire(signal, onLongWait, longWaitMs = 0) {
|
|
28
|
+
if (signal?.aborted === true) throw stoppedError();
|
|
29
|
+
if (this.#held < this.limit && this.#queue.length === 0) {
|
|
30
|
+
this.#held += 1;
|
|
31
|
+
return this.#slot();
|
|
32
|
+
}
|
|
33
|
+
return await new Promise((resolve, reject) => {
|
|
34
|
+
const timer = onLongWait === void 0 ? void 0 : setTimeout(onLongWait, longWaitMs).unref();
|
|
35
|
+
const settle = () => {
|
|
36
|
+
clearTimeout(timer);
|
|
37
|
+
signal?.removeEventListener("abort", onAbort);
|
|
38
|
+
};
|
|
39
|
+
const waiter = {
|
|
40
|
+
grant: () => {
|
|
41
|
+
settle();
|
|
42
|
+
this.#held += 1;
|
|
43
|
+
resolve(this.#slot());
|
|
44
|
+
},
|
|
45
|
+
stop: () => {
|
|
46
|
+
settle();
|
|
47
|
+
const index = this.#queue.indexOf(waiter);
|
|
48
|
+
if (index >= 0) this.#queue.splice(index, 1);
|
|
49
|
+
reject(stoppedError());
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const onAbort = () => {
|
|
53
|
+
waiter.stop();
|
|
54
|
+
};
|
|
55
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
56
|
+
this.#queue.push(waiter);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/** Reject every waiter (shutdown). Slots already taken are released by their leases. */
|
|
60
|
+
stopWaiting() {
|
|
61
|
+
for (const waiter of this.#queue.splice(0)) waiter.stop();
|
|
62
|
+
}
|
|
63
|
+
#slot() {
|
|
64
|
+
let released = false;
|
|
65
|
+
return { release: () => {
|
|
66
|
+
if (released) return;
|
|
67
|
+
released = true;
|
|
68
|
+
this.#held -= 1;
|
|
69
|
+
this.#queue.shift()?.grant();
|
|
70
|
+
} };
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const limiters = /* @__PURE__ */ new Map();
|
|
74
|
+
let epoch = 0;
|
|
75
|
+
/** The limiter of a service, created on first use; undefined when it has no `maxConnections`. */
|
|
76
|
+
function limiterOf(remote) {
|
|
77
|
+
if (remote.maxConnections === void 0) return;
|
|
78
|
+
let limiter = limiters.get(remote);
|
|
79
|
+
if (limiter === void 0) {
|
|
80
|
+
limiter = new ConnectionLimiter(remote.maxConnections);
|
|
81
|
+
limiters.set(remote, limiter);
|
|
82
|
+
}
|
|
83
|
+
return limiter;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Take the connection slot of `entry`'s service, or undefined when it has no cap. Not `async`: without
|
|
87
|
+
* a cap the caller must not wait even one tick before it connects. A wait longer than
|
|
88
|
+
* `timeouts.acquireMs` is logged once, with the target and the limit and nothing about the endpoint.
|
|
89
|
+
*/
|
|
90
|
+
function takeSlot(entry, signal, logger) {
|
|
91
|
+
const limiter = limiterOf(entry.remote);
|
|
92
|
+
if (limiter === void 0) return;
|
|
93
|
+
return limiter.acquire(signal, () => {
|
|
94
|
+
logger?.warn({
|
|
95
|
+
target: entry.target,
|
|
96
|
+
limit: limiter.limit,
|
|
97
|
+
waiting: limiter.waiting
|
|
98
|
+
}, "waiting for a remote browser connection slot");
|
|
99
|
+
}, entry.remote.timeouts.acquireMs);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Shutdown: reject every caller still waiting for a slot and forget every limiter, so a service used
|
|
103
|
+
* again starts with none held. A lease that is released later gives its slot back to the limiter it
|
|
104
|
+
* took it from, which nothing uses any more.
|
|
105
|
+
*/
|
|
106
|
+
function resetAllLimiters() {
|
|
107
|
+
epoch += 1;
|
|
108
|
+
for (const limiter of limiters.values()) limiter.stopWaiting();
|
|
109
|
+
limiters.clear();
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Counts shutdowns. A caller that took a slot compares it with the value from before it waited: a
|
|
113
|
+
* slot granted just before a shutdown resumes a tick later, after the attempts were stopped, and
|
|
114
|
+
* must not open a connection nobody will release.
|
|
115
|
+
*/
|
|
116
|
+
function shutdownEpoch() {
|
|
117
|
+
return epoch;
|
|
118
|
+
}
|
|
119
|
+
export { ConnectionLimiter, limiterOf, resetAllLimiters, shutdownEpoch, takeSlot };
|
|
120
|
+
|
|
121
|
+
//# sourceMappingURL=remote-limiter.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-limiter.mjs","names":[],"sources":["../../src/adapters/remote-limiter.ts"],"sourcesContent":["/**\n * The per-process cap on a remote service's connections (ADR 0007, decision 4): a separate limiter,\n * never the page gate. A slot is taken before `connect` is called and given back with the lease,\n * exactly once, so a connection counts from the moment it is requested until its render has ended.\n * Waiting for a slot ends on the run's cancel and at shutdown, and frees nothing it did not take.\n */\nimport type { Logger } from \"../logger.ts\";\nimport type { RemoteBrowser, RemoteTarget } from \"./remote-browser.ts\";\nimport { stoppedError } from \"./remote-lease-error.ts\";\n\n/** One connection's place under the cap; `release` is safe to call more than once. */\nexport interface ConnectionSlot {\n release(): void;\n}\n\ninterface Waiter {\n grant(): void;\n stop(): void;\n}\n\n/** A counting semaphore with a FIFO queue whose waiters can be cancelled. */\nexport class ConnectionLimiter {\n readonly limit: number;\n #held = 0;\n readonly #queue: Waiter[] = [];\n\n constructor(limit: number) {\n this.limit = limit;\n }\n\n /** Slots in use. */\n get held(): number {\n return this.#held;\n }\n\n /** Callers waiting for a slot. */\n get waiting(): number {\n return this.#queue.length;\n }\n\n /**\n * Take a slot, waiting for one when all are in use. Rejects with the stopped error when `signal`\n * aborts (or the process shuts down) while waiting, without having taken anything.\n * `onLongWait` runs once if the wait outlasts `longWaitMs`.\n */\n async acquire(\n signal?: AbortSignal,\n onLongWait?: () => void,\n longWaitMs = 0,\n ): Promise<ConnectionSlot> {\n if (signal?.aborted === true) {\n throw stoppedError();\n }\n if (this.#held < this.limit && this.#queue.length === 0) {\n this.#held += 1;\n return this.#slot();\n }\n return await new Promise<ConnectionSlot>((resolve, reject) => {\n const timer =\n onLongWait === undefined ? undefined : setTimeout(onLongWait, longWaitMs).unref();\n const settle = (): void => {\n clearTimeout(timer);\n signal?.removeEventListener(\"abort\", onAbort);\n };\n const waiter: Waiter = {\n grant: () => {\n settle();\n this.#held += 1;\n resolve(this.#slot());\n },\n stop: () => {\n settle();\n const index = this.#queue.indexOf(waiter);\n if (index >= 0) {\n this.#queue.splice(index, 1);\n }\n reject(stoppedError());\n },\n };\n const onAbort = (): void => {\n waiter.stop();\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n this.#queue.push(waiter);\n });\n }\n\n /** Reject every waiter (shutdown). Slots already taken are released by their leases. */\n stopWaiting(): void {\n for (const waiter of this.#queue.splice(0)) {\n waiter.stop();\n }\n }\n\n #slot(): ConnectionSlot {\n let released = false;\n return {\n release: () => {\n if (released) {\n return;\n }\n released = true;\n this.#held -= 1;\n this.#queue.shift()?.grant();\n },\n };\n }\n}\n\nconst limiters = new Map<RemoteBrowser, ConnectionLimiter>();\nlet epoch = 0;\n\n/** The limiter of a service, created on first use; undefined when it has no `maxConnections`. */\nexport function limiterOf(remote: RemoteBrowser): ConnectionLimiter | undefined {\n if (remote.maxConnections === undefined) {\n return undefined;\n }\n let limiter = limiters.get(remote);\n if (limiter === undefined) {\n limiter = new ConnectionLimiter(remote.maxConnections);\n limiters.set(remote, limiter);\n }\n return limiter;\n}\n\n/**\n * Take the connection slot of `entry`'s service, or undefined when it has no cap. Not `async`: without\n * a cap the caller must not wait even one tick before it connects. A wait longer than\n * `timeouts.acquireMs` is logged once, with the target and the limit and nothing about the endpoint.\n */\nexport function takeSlot(\n entry: RemoteTarget,\n signal: AbortSignal | undefined,\n logger: Logger | undefined,\n): Promise<ConnectionSlot> | undefined {\n const limiter = limiterOf(entry.remote);\n if (limiter === undefined) {\n return undefined;\n }\n return limiter.acquire(\n signal,\n () => {\n logger?.warn(\n { target: entry.target, limit: limiter.limit, waiting: limiter.waiting },\n \"waiting for a remote browser connection slot\",\n );\n },\n entry.remote.timeouts.acquireMs,\n );\n}\n\n/**\n * Shutdown: reject every caller still waiting for a slot and forget every limiter, so a service used\n * again starts with none held. A lease that is released later gives its slot back to the limiter it\n * took it from, which nothing uses any more.\n */\nexport function resetAllLimiters(): void {\n epoch += 1;\n for (const limiter of limiters.values()) {\n limiter.stopWaiting();\n }\n limiters.clear();\n}\n\n/**\n * Counts shutdowns. A caller that took a slot compares it with the value from before it waited: a\n * slot granted just before a shutdown resumes a tick later, after the attempts were stopped, and\n * must not open a connection nobody will release.\n */\nexport function shutdownEpoch(): number {\n return epoch;\n}\n"],"mappings":";;;;;;AAqBA,IAAa,oBAAb,MAA+B;CAC7B;CACA,QAAQ;CACR,SAA4B,CAAC;CAE7B,YAAY,OAAe;EACzB,KAAK,QAAQ;CACf;;CAGA,IAAI,OAAe;EACjB,OAAO,KAAK;CACd;;CAGA,IAAI,UAAkB;EACpB,OAAO,KAAK,OAAO;CACrB;;;;;;CAOA,MAAM,QACJ,QACA,YACA,aAAa,GACY;EACzB,IAAI,QAAQ,YAAY,MACtB,MAAM,aAAa;EAErB,IAAI,KAAK,QAAQ,KAAK,SAAS,KAAK,OAAO,WAAW,GAAG;GACvD,KAAK,SAAS;GACd,OAAO,KAAK,MAAM;EACpB;EACA,OAAO,MAAM,IAAI,SAAyB,SAAS,WAAW;GAC5D,MAAM,QACJ,eAAe,KAAA,IAAY,KAAA,IAAY,WAAW,YAAY,UAAU,CAAC,CAAC,MAAM;GAClF,MAAM,eAAqB;IACzB,aAAa,KAAK;IAClB,QAAQ,oBAAoB,SAAS,OAAO;GAC9C;GACA,MAAM,SAAiB;IACrB,aAAa;KACX,OAAO;KACP,KAAK,SAAS;KACd,QAAQ,KAAK,MAAM,CAAC;IACtB;IACA,YAAY;KACV,OAAO;KACP,MAAM,QAAQ,KAAK,OAAO,QAAQ,MAAM;KACxC,IAAI,SAAS,GACX,KAAK,OAAO,OAAO,OAAO,CAAC;KAE7B,OAAO,aAAa,CAAC;IACvB;GACF;GACA,MAAM,gBAAsB;IAC1B,OAAO,KAAK;GACd;GACA,QAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;GACzD,KAAK,OAAO,KAAK,MAAM;EACzB,CAAC;CACH;;CAGA,cAAoB;EAClB,KAAK,MAAM,UAAU,KAAK,OAAO,OAAO,CAAC,GACvC,OAAO,KAAK;CAEhB;CAEA,QAAwB;EACtB,IAAI,WAAW;EACf,OAAO,EACL,eAAe;GACb,IAAI,UACF;GAEF,WAAW;GACX,KAAK,SAAS;GACd,KAAK,OAAO,MAAM,CAAC,EAAE,MAAM;EAC7B,EACF;CACF;AACF;AAEA,MAAM,2BAAW,IAAI,IAAsC;AAC3D,IAAI,QAAQ;;AAGZ,SAAgB,UAAU,QAAsD;CAC9E,IAAI,OAAO,mBAAmB,KAAA,GAC5B;CAEF,IAAI,UAAU,SAAS,IAAI,MAAM;CACjC,IAAI,YAAY,KAAA,GAAW;EACzB,UAAU,IAAI,kBAAkB,OAAO,cAAc;EACrD,SAAS,IAAI,QAAQ,OAAO;CAC9B;CACA,OAAO;AACT;;;;;;AAOA,SAAgB,SACd,OACA,QACA,QACqC;CACrC,MAAM,UAAU,UAAU,MAAM,MAAM;CACtC,IAAI,YAAY,KAAA,GACd;CAEF,OAAO,QAAQ,QACb,cACM;EACJ,QAAQ,KACN;GAAE,QAAQ,MAAM;GAAQ,OAAO,QAAQ;GAAO,SAAS,QAAQ;EAAQ,GACvE,8CACF;CACF,GACA,MAAM,OAAO,SAAS,SACxB;AACF;;;;;;AAOA,SAAgB,mBAAyB;CACvC,SAAS;CACT,KAAK,MAAM,WAAW,SAAS,OAAO,GACpC,QAAQ,YAAY;CAEtB,SAAS,MAAM;AACjB;;;;;;AAOA,SAAgB,gBAAwB;CACtC,OAAO;AACT"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { RemoteConnectContext, RemoteConnection, RemoteLeasedConnection, RemoteReleaseInfo } from "./remote-browser.mjs";
|
|
4
|
+
import { Logger } from "../logger.mjs";
|
|
5
|
+
/** Options of {@link refCounted}. */
|
|
6
|
+
interface RefCountedOptions<TResource> {
|
|
7
|
+
/**
|
|
8
|
+
* Start the resource (wake the computer) and return what connections need of it. Called by the
|
|
9
|
+
* first connection; concurrent first connections share the call. Must be idempotent: it runs
|
|
10
|
+
* again after a failed `close`, and may overlap a `close` that timed out. `signal` aborts when
|
|
11
|
+
* every waiting connection was cancelled, and at shutdown: clean up after yourself when it does,
|
|
12
|
+
* or throw an error with a `resource` property so the pool closes it.
|
|
13
|
+
*/
|
|
14
|
+
open: (context: RemoteConnectContext, signal: AbortSignal) => Promise<TResource>;
|
|
15
|
+
/** Stop the resource (suspend the computer). */
|
|
16
|
+
close: (resource: TResource) => Promise<void> | void;
|
|
17
|
+
/**
|
|
18
|
+
* How long the resource stays open with no connection, in ms. Required: set it longer than the
|
|
19
|
+
* gap between a build's passes (the extract and tests pre-passes and its shards connect close
|
|
20
|
+
* together), for example 120 000.
|
|
21
|
+
*/
|
|
22
|
+
idleMs: number;
|
|
23
|
+
/** How long `close` may take in ms before it counts as failed (default 30 000). */
|
|
24
|
+
closeTimeoutMs?: number | undefined;
|
|
25
|
+
/** Where close failures go (error class only, never the message). */
|
|
26
|
+
logger?: Logger | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Runs `run` after `ms` and returns what cancels it (default: an unref'd `setTimeout`). A seam
|
|
29
|
+
* for tests to drive the idle timer without real time.
|
|
30
|
+
*/
|
|
31
|
+
schedule?: ((run: () => void, ms: number) => () => void) | undefined;
|
|
32
|
+
}
|
|
33
|
+
/** What the pool hands to `release`: an opaque token for one connection. */
|
|
34
|
+
interface PoolLease {
|
|
35
|
+
readonly pool: "refCounted";
|
|
36
|
+
}
|
|
37
|
+
/** A reference-counted resource, wired into a service's `connect` and `release`. */
|
|
38
|
+
interface RefCounted<TResource> {
|
|
39
|
+
/** Build the service's `connect`: `build` gets the shared resource and returns the connection. */
|
|
40
|
+
connect(build: (resource: TResource, context: RemoteConnectContext) => RemoteConnection | Promise<RemoteConnection>): (context: RemoteConnectContext) => Promise<RemoteLeasedConnection<PoolLease>>;
|
|
41
|
+
/** The service's `release`. */
|
|
42
|
+
release(lease: PoolLease, info: RemoteReleaseInfo): Promise<void>;
|
|
43
|
+
/** Close now: abort a pending `open`, then close the resource. Runs at shutdown. */
|
|
44
|
+
flush(): Promise<void>;
|
|
45
|
+
/** Unregister from shutdown and drop the idle timer, without closing (for tests). */
|
|
46
|
+
dispose(): void;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Share one resource between connections: opened by the first, closed `idleMs` after the last
|
|
50
|
+
* was released (or at once on shutdown). A connection arriving while it closes waits, then opens it
|
|
51
|
+
* again. After a failed `close` the resource is `unknown`: the next connection opens it again and
|
|
52
|
+
* the idle timer retries the close.
|
|
53
|
+
*/
|
|
54
|
+
declare function refCounted<TResource>(options: RefCountedOptions<TResource>): RefCounted<TResource>;
|
|
55
|
+
export { PoolLease, RefCounted, RefCountedOptions, refCounted };
|
|
56
|
+
//# sourceMappingURL=remote-pool.d.mts.map
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { onRemoteShutdown } from "./remote-lease-shutdown.mjs";
|
|
5
|
+
// src/adapters/remote-pool.ts
|
|
6
|
+
/**
|
|
7
|
+
* Share one resource between connections: opened by the first, closed `idleMs` after the last
|
|
8
|
+
* was released (or at once on shutdown). A connection arriving while it closes waits, then opens it
|
|
9
|
+
* again. After a failed `close` the resource is `unknown`: the next connection opens it again and
|
|
10
|
+
* the idle timer retries the close.
|
|
11
|
+
*/
|
|
12
|
+
function refCounted(options) {
|
|
13
|
+
const pool = new Pool(options);
|
|
14
|
+
const unregister = onRemoteShutdown(async () => {
|
|
15
|
+
await pool.flush();
|
|
16
|
+
});
|
|
17
|
+
return {
|
|
18
|
+
connect: (build) => async (context) => {
|
|
19
|
+
const slot = await pool.take(context);
|
|
20
|
+
try {
|
|
21
|
+
return {
|
|
22
|
+
...await build(slot.resource, context),
|
|
23
|
+
lease: slot
|
|
24
|
+
};
|
|
25
|
+
} catch (error) {
|
|
26
|
+
pool.give(slot);
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
release: async (lease) => {
|
|
31
|
+
pool.give(lease);
|
|
32
|
+
await Promise.resolve();
|
|
33
|
+
},
|
|
34
|
+
flush: async () => {
|
|
35
|
+
await pool.flush();
|
|
36
|
+
},
|
|
37
|
+
dispose: () => {
|
|
38
|
+
unregister();
|
|
39
|
+
pool.dispose();
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
var Pool = class {
|
|
44
|
+
#state = "idle";
|
|
45
|
+
#slot;
|
|
46
|
+
#stale;
|
|
47
|
+
#hasStale = false;
|
|
48
|
+
#cancelIdle;
|
|
49
|
+
#opening;
|
|
50
|
+
#openController;
|
|
51
|
+
#waiters = /* @__PURE__ */ new Set();
|
|
52
|
+
#closing;
|
|
53
|
+
#options;
|
|
54
|
+
constructor(options) {
|
|
55
|
+
this.#options = options;
|
|
56
|
+
}
|
|
57
|
+
/** A connection's share of the resource, opening it first when nobody has. */
|
|
58
|
+
async take(context) {
|
|
59
|
+
for (;;) if (this.#state === "closing") await this.#closing?.catch(() => {});
|
|
60
|
+
else if (this.#state === "opening") await this.#waitOpen(context.signal);
|
|
61
|
+
else if (this.#state === "open" && this.#slot !== void 0) {
|
|
62
|
+
this.#clearIdle();
|
|
63
|
+
this.#slot.count += 1;
|
|
64
|
+
return this.#slot;
|
|
65
|
+
} else this.#startOpen(context);
|
|
66
|
+
}
|
|
67
|
+
/** A connection is done with its slot; the last one starts the idle timer. */
|
|
68
|
+
give(slot) {
|
|
69
|
+
slot.count = Math.max(0, slot.count - 1);
|
|
70
|
+
if (slot === this.#slot && slot.count === 0 && this.#state === "open") this.#armIdle();
|
|
71
|
+
}
|
|
72
|
+
/** Close now, aborting a pending open first. */
|
|
73
|
+
async flush() {
|
|
74
|
+
this.#clearIdle();
|
|
75
|
+
this.#openController?.abort();
|
|
76
|
+
await this.#opening?.catch(() => {});
|
|
77
|
+
if (this.#state === "closing") await this.#closing?.catch(() => {});
|
|
78
|
+
if (this.#state === "open" || this.#state === "unknown") await this.#close();
|
|
79
|
+
}
|
|
80
|
+
dispose() {
|
|
81
|
+
this.#clearIdle();
|
|
82
|
+
}
|
|
83
|
+
#startOpen(context) {
|
|
84
|
+
this.#clearIdle();
|
|
85
|
+
const controller = new AbortController();
|
|
86
|
+
this.#openController = controller;
|
|
87
|
+
this.#state = "opening";
|
|
88
|
+
this.#opening = this.#options.open(context, controller.signal).then((resource) => {
|
|
89
|
+
this.#slot = {
|
|
90
|
+
resource,
|
|
91
|
+
count: 0
|
|
92
|
+
};
|
|
93
|
+
this.#hasStale = false;
|
|
94
|
+
this.#stale = void 0;
|
|
95
|
+
this.#state = "open";
|
|
96
|
+
}, async (error) => {
|
|
97
|
+
this.#state = this.#hasStale ? "unknown" : "idle";
|
|
98
|
+
if (this.#hasStale) this.#armIdle();
|
|
99
|
+
await this.#closeLeftover(error);
|
|
100
|
+
throw error;
|
|
101
|
+
}).finally(() => {
|
|
102
|
+
this.#openController = void 0;
|
|
103
|
+
this.#waiters.clear();
|
|
104
|
+
});
|
|
105
|
+
this.#opening.catch(() => {});
|
|
106
|
+
this.#waitOpen(context.signal).catch(() => {});
|
|
107
|
+
}
|
|
108
|
+
/** Wait for the open in flight; it is aborted once every waiter has given up. */
|
|
109
|
+
async #waitOpen(signal) {
|
|
110
|
+
this.#waiters.add(signal);
|
|
111
|
+
const onAbort = () => {
|
|
112
|
+
this.#waiters.delete(signal);
|
|
113
|
+
if (this.#waiters.size === 0) this.#openController?.abort();
|
|
114
|
+
};
|
|
115
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
116
|
+
try {
|
|
117
|
+
await this.#opening;
|
|
118
|
+
} finally {
|
|
119
|
+
signal.removeEventListener("abort", onAbort);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
#armIdle() {
|
|
123
|
+
this.#clearIdle();
|
|
124
|
+
const schedule = this.#options.schedule ?? scheduleTimeout;
|
|
125
|
+
this.#cancelIdle = schedule(() => {
|
|
126
|
+
if ((this.#slot?.count ?? 0) === 0) this.#close().catch(() => {});
|
|
127
|
+
}, this.#options.idleMs);
|
|
128
|
+
}
|
|
129
|
+
#clearIdle() {
|
|
130
|
+
this.#cancelIdle?.();
|
|
131
|
+
this.#cancelIdle = void 0;
|
|
132
|
+
}
|
|
133
|
+
async #close() {
|
|
134
|
+
const resource = this.#slot?.resource ?? this.#stale;
|
|
135
|
+
if (resource === void 0 || this.#state === "closing") return;
|
|
136
|
+
this.#state = "closing";
|
|
137
|
+
this.#closing = this.#runClose(resource);
|
|
138
|
+
await this.#closing;
|
|
139
|
+
}
|
|
140
|
+
async #runClose(resource) {
|
|
141
|
+
try {
|
|
142
|
+
await timed(async () => {
|
|
143
|
+
await this.#options.close(resource);
|
|
144
|
+
}, this.#options.closeTimeoutMs ?? 3e4);
|
|
145
|
+
this.#settle("idle");
|
|
146
|
+
} catch (error) {
|
|
147
|
+
this.#settle("unknown", resource);
|
|
148
|
+
this.#options.logger?.warn({ err: error instanceof Error ? error.name : "Error" }, "remote pool close failed, will retry after the idle period");
|
|
149
|
+
this.#armIdle();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/** After a close: `idle` with nothing, or `unknown` holding the resource to close again. */
|
|
153
|
+
#settle(state, stale) {
|
|
154
|
+
this.#slot = void 0;
|
|
155
|
+
this.#stale = stale;
|
|
156
|
+
this.#hasStale = state === "unknown";
|
|
157
|
+
this.#state = state;
|
|
158
|
+
}
|
|
159
|
+
/** An `open` that failed after starting something may name it, so it is not left running. */
|
|
160
|
+
async #closeLeftover(error) {
|
|
161
|
+
const leftover = error?.resource;
|
|
162
|
+
if (leftover === void 0) return;
|
|
163
|
+
try {
|
|
164
|
+
await timed(async () => {
|
|
165
|
+
await this.#options.close(leftover);
|
|
166
|
+
}, this.#options.closeTimeoutMs ?? 3e4);
|
|
167
|
+
} catch (closeError) {
|
|
168
|
+
this.#options.logger?.warn({ err: closeError instanceof Error ? closeError.name : "Error" }, "remote pool close failed after a failed open");
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
/** The default scheduler: an unref'd timer, so an idle pool never keeps the process alive. */
|
|
173
|
+
function scheduleTimeout(run, ms) {
|
|
174
|
+
const timer = setTimeout(run, ms);
|
|
175
|
+
timer.unref();
|
|
176
|
+
return () => {
|
|
177
|
+
clearTimeout(timer);
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
async function timed(run, ms) {
|
|
181
|
+
let timer;
|
|
182
|
+
const deadline = new Promise((_, reject) => {
|
|
183
|
+
timer = setTimeout(() => {
|
|
184
|
+
reject(Object.assign(/* @__PURE__ */ new Error(`close timed out after ${ms} ms`), { name: "CloseTimeout" }));
|
|
185
|
+
}, ms);
|
|
186
|
+
});
|
|
187
|
+
try {
|
|
188
|
+
await Promise.race([run(), deadline]);
|
|
189
|
+
} finally {
|
|
190
|
+
clearTimeout(timer);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
export { refCounted };
|
|
194
|
+
|
|
195
|
+
//# sourceMappingURL=remote-pool.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-pool.mjs","names":[],"sources":["../../src/adapters/remote-pool.ts"],"sourcesContent":["/**\n * A shared resource behind a remote service, opened by the first connection and closed once the\n * last one has been idle for a while: a computer woken for the builds' shards and suspended after\n * (ADR 0007, decision 4). One pool per service; its connections share the resource.\n */\nimport type { Logger } from \"../logger.ts\";\nimport type {\n RemoteConnectContext,\n RemoteConnection,\n RemoteLeasedConnection,\n RemoteReleaseInfo,\n} from \"./remote-browser.ts\";\nimport { onRemoteShutdown } from \"./remote-lease.ts\";\n\n/** Options of {@link refCounted}. */\nexport interface RefCountedOptions<TResource> {\n /**\n * Start the resource (wake the computer) and return what connections need of it. Called by the\n * first connection; concurrent first connections share the call. Must be idempotent: it runs\n * again after a failed `close`, and may overlap a `close` that timed out. `signal` aborts when\n * every waiting connection was cancelled, and at shutdown: clean up after yourself when it does,\n * or throw an error with a `resource` property so the pool closes it.\n */\n open: (context: RemoteConnectContext, signal: AbortSignal) => Promise<TResource>;\n /** Stop the resource (suspend the computer). */\n close: (resource: TResource) => Promise<void> | void;\n /**\n * How long the resource stays open with no connection, in ms. Required: set it longer than the\n * gap between a build's passes (the extract and tests pre-passes and its shards connect close\n * together), for example 120 000.\n */\n idleMs: number;\n /** How long `close` may take in ms before it counts as failed (default 30 000). */\n closeTimeoutMs?: number | undefined;\n /** Where close failures go (error class only, never the message). */\n logger?: Logger | undefined;\n /**\n * Runs `run` after `ms` and returns what cancels it (default: an unref'd `setTimeout`). A seam\n * for tests to drive the idle timer without real time.\n */\n schedule?: ((run: () => void, ms: number) => () => void) | undefined;\n}\n\n/** What the pool hands to `release`: an opaque token for one connection. */\nexport interface PoolLease {\n readonly pool: \"refCounted\";\n}\n\n/** A reference-counted resource, wired into a service's `connect` and `release`. */\nexport interface RefCounted<TResource> {\n /** Build the service's `connect`: `build` gets the shared resource and returns the connection. */\n connect(\n build: (\n resource: TResource,\n context: RemoteConnectContext,\n ) => RemoteConnection | Promise<RemoteConnection>,\n ): (context: RemoteConnectContext) => Promise<RemoteLeasedConnection<PoolLease>>;\n /** The service's `release`. */\n release(lease: PoolLease, info: RemoteReleaseInfo): Promise<void>;\n /** Close now: abort a pending `open`, then close the resource. Runs at shutdown. */\n flush(): Promise<void>;\n /** Unregister from shutdown and drop the idle timer, without closing (for tests). */\n dispose(): void;\n}\n\ntype State = \"idle\" | \"opening\" | \"open\" | \"closing\" | \"unknown\";\n\ninterface Slot<TResource> {\n resource: TResource;\n count: number;\n}\n\n/**\n * Share one resource between connections: opened by the first, closed `idleMs` after the last\n * was released (or at once on shutdown). A connection arriving while it closes waits, then opens it\n * again. After a failed `close` the resource is `unknown`: the next connection opens it again and\n * the idle timer retries the close.\n */\nexport function refCounted<TResource>(\n options: RefCountedOptions<TResource>,\n): RefCounted<TResource> {\n const pool = new Pool(options);\n const unregister = onRemoteShutdown(async () => {\n await pool.flush();\n });\n return {\n connect: (build) => async (context) => {\n const slot = await pool.take(context);\n try {\n const connection = await build(slot.resource, context);\n return { ...connection, lease: slot as unknown as PoolLease };\n } catch (error) {\n pool.give(slot);\n throw error;\n }\n },\n release: async (lease) => {\n pool.give(lease as unknown as Slot<TResource>);\n await Promise.resolve();\n },\n flush: async () => {\n await pool.flush();\n },\n dispose: () => {\n unregister();\n pool.dispose();\n },\n };\n}\n\nclass Pool<TResource> {\n #state: State = \"idle\";\n #slot: Slot<TResource> | undefined;\n #stale: TResource | undefined;\n #hasStale = false;\n #cancelIdle: (() => void) | undefined;\n #opening: Promise<void> | undefined;\n #openController: AbortController | undefined;\n #waiters = new Set<AbortSignal>();\n #closing: Promise<void> | undefined;\n readonly #options: RefCountedOptions<TResource>;\n\n constructor(options: RefCountedOptions<TResource>) {\n this.#options = options;\n }\n\n /** A connection's share of the resource, opening it first when nobody has. */\n async take(context: RemoteConnectContext): Promise<Slot<TResource>> {\n for (;;) {\n if (this.#state === \"closing\") {\n // oxlint-disable-next-line eslint/no-await-in-loop -- a connection waits for the close to end\n await this.#closing?.catch(() => {});\n } else if (this.#state === \"opening\") {\n // oxlint-disable-next-line eslint/no-await-in-loop -- waiters share the one open\n await this.#waitOpen(context.signal);\n } else if (this.#state === \"open\" && this.#slot !== undefined) {\n this.#clearIdle();\n this.#slot.count += 1;\n return this.#slot;\n } else {\n this.#startOpen(context);\n }\n }\n }\n\n /** A connection is done with its slot; the last one starts the idle timer. */\n give(slot: Slot<TResource>): void {\n slot.count = Math.max(0, slot.count - 1);\n if (slot === this.#slot && slot.count === 0 && this.#state === \"open\") {\n this.#armIdle();\n }\n }\n\n /** Close now, aborting a pending open first. */\n async flush(): Promise<void> {\n this.#clearIdle();\n this.#openController?.abort();\n await this.#opening?.catch(() => {});\n if (this.#state === \"closing\") {\n await this.#closing?.catch(() => {});\n }\n if (this.#state === \"open\" || this.#state === \"unknown\") {\n await this.#close();\n }\n }\n\n dispose(): void {\n this.#clearIdle();\n }\n\n #startOpen(context: RemoteConnectContext): void {\n this.#clearIdle();\n const controller = new AbortController();\n this.#openController = controller;\n this.#state = \"opening\";\n this.#opening = this.#options\n .open(context, controller.signal)\n .then(\n (resource) => {\n this.#slot = { resource, count: 0 };\n this.#hasStale = false;\n this.#stale = undefined;\n this.#state = \"open\";\n },\n async (error: unknown) => {\n this.#state = this.#hasStale ? \"unknown\" : \"idle\";\n if (this.#hasStale) {\n // Reopening cancelled the retry of the failed close; it is still owed.\n this.#armIdle();\n }\n await this.#closeLeftover(error);\n throw error;\n },\n )\n .finally(() => {\n this.#openController = undefined;\n this.#waiters.clear();\n });\n // The first waiter is the caller that started it.\n this.#opening.catch(() => {});\n this.#waitOpen(context.signal).catch(() => {});\n }\n\n /** Wait for the open in flight; it is aborted once every waiter has given up. */\n async #waitOpen(signal: AbortSignal): Promise<void> {\n this.#waiters.add(signal);\n const onAbort = (): void => {\n this.#waiters.delete(signal);\n if (this.#waiters.size === 0) {\n this.#openController?.abort();\n }\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n try {\n await this.#opening;\n } finally {\n signal.removeEventListener(\"abort\", onAbort);\n }\n }\n\n #armIdle(): void {\n this.#clearIdle();\n const schedule = this.#options.schedule ?? scheduleTimeout;\n this.#cancelIdle = schedule(() => {\n if ((this.#slot?.count ?? 0) === 0) {\n this.#close().catch(() => {});\n }\n }, this.#options.idleMs);\n }\n\n #clearIdle(): void {\n this.#cancelIdle?.();\n this.#cancelIdle = undefined;\n }\n\n async #close(): Promise<void> {\n const resource = this.#slot?.resource ?? this.#stale;\n if (resource === undefined || this.#state === \"closing\") {\n return;\n }\n this.#state = \"closing\";\n this.#closing = this.#runClose(resource);\n await this.#closing;\n }\n\n async #runClose(resource: TResource): Promise<void> {\n try {\n await timed(async () => {\n await this.#options.close(resource);\n }, this.#options.closeTimeoutMs ?? 30_000);\n this.#settle(\"idle\");\n } catch (error) {\n // The suspend may have worked on the server: keep the reference, but trust nothing about it.\n this.#settle(\"unknown\", resource);\n this.#options.logger?.warn(\n { err: error instanceof Error ? error.name : \"Error\" },\n \"remote pool close failed, will retry after the idle period\",\n );\n this.#armIdle();\n }\n }\n\n /** After a close: `idle` with nothing, or `unknown` holding the resource to close again. */\n #settle(state: \"idle\" | \"unknown\", stale?: TResource): void {\n this.#slot = undefined;\n this.#stale = stale;\n this.#hasStale = state === \"unknown\";\n this.#state = state;\n }\n\n /** An `open` that failed after starting something may name it, so it is not left running. */\n async #closeLeftover(error: unknown): Promise<void> {\n const leftover = (error as { resource?: TResource } | null)?.resource;\n if (leftover === undefined) {\n return;\n }\n try {\n await timed(async () => {\n await this.#options.close(leftover);\n }, this.#options.closeTimeoutMs ?? 30_000);\n } catch (closeError) {\n this.#options.logger?.warn(\n { err: closeError instanceof Error ? closeError.name : \"Error\" },\n \"remote pool close failed after a failed open\",\n );\n }\n }\n}\n\n/** The default scheduler: an unref'd timer, so an idle pool never keeps the process alive. */\nfunction scheduleTimeout(run: () => void, ms: number): () => void {\n const timer = setTimeout(run, ms);\n timer.unref();\n return () => {\n clearTimeout(timer);\n };\n}\n\nasync function timed(run: () => Promise<void>, ms: number): Promise<void> {\n let timer: NodeJS.Timeout | undefined;\n const deadline = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n reject(Object.assign(new Error(`close timed out after ${ms} ms`), { name: \"CloseTimeout\" }));\n }, ms);\n });\n try {\n await Promise.race([run(), deadline]);\n } finally {\n clearTimeout(timer);\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { CaptureTarget } from "./capture-target.mjs";
|
|
4
|
+
import { RemoteBrowser } from "./remote-browser.mjs";
|
|
5
|
+
/** Said of every experimental preset until someone verifies it against the provider. */
|
|
6
|
+
declare const UNVERIFIED_NOTE = "not verified against live infrastructure";
|
|
7
|
+
/** The loggable limits of one remote target. A `null` limit is not capped. */
|
|
8
|
+
interface RemoteSizing {
|
|
9
|
+
target: CaptureTarget;
|
|
10
|
+
/** Pages the plan allows at once: the target's page gate. Per target, per process. */
|
|
11
|
+
pages: number | null;
|
|
12
|
+
/** Pages on one connection, when capped. */
|
|
13
|
+
pagesPerConnection: number | null;
|
|
14
|
+
/** Pages one connection can hold: the lowest of `pages`, `pagesPerConnection` (when the runner enforces it) and the run's `concurrency`. */
|
|
15
|
+
effectivePagesPerConnection: number | null;
|
|
16
|
+
/** Connections this process holds at once, when capped. Per process. */
|
|
17
|
+
maxConnections: number | null;
|
|
18
|
+
scope: {
|
|
19
|
+
pages: "target";
|
|
20
|
+
connections: "process";
|
|
21
|
+
};
|
|
22
|
+
experimental: boolean;
|
|
23
|
+
/** Present only for an experimental preset. */
|
|
24
|
+
verification?: typeof UNVERIFIED_NOTE;
|
|
25
|
+
}
|
|
26
|
+
/** What the runner actually enforces, so the effective limit is not overstated. */
|
|
27
|
+
interface RemoteSizingEnforced {
|
|
28
|
+
/** The shared page gate of the target; its size is the effective page limit. */
|
|
29
|
+
gate?: {
|
|
30
|
+
readonly size: number;
|
|
31
|
+
} | undefined;
|
|
32
|
+
/** The run's configured page concurrency, when set. */
|
|
33
|
+
concurrency?: number | undefined;
|
|
34
|
+
/** Whether the runner applies `maxPagesPerConnection` (default true). */
|
|
35
|
+
pagesPerConnection?: boolean | undefined;
|
|
36
|
+
}
|
|
37
|
+
/** The configured and effective limits of `remote`, as far as the runner enforces them. */
|
|
38
|
+
declare function remoteSizingOf(target: CaptureTarget, remote: RemoteBrowser, enforced?: RemoteSizingEnforced): RemoteSizing;
|
|
39
|
+
/** Log the limits at run start, before any connection is attempted. */
|
|
40
|
+
declare function logRemoteSizing(logger: {
|
|
41
|
+
info(fields: object, message: string): void;
|
|
42
|
+
} | undefined, target: CaptureTarget, remote: RemoteBrowser, enforced?: RemoteSizingEnforced): void;
|
|
43
|
+
export { RemoteSizing, RemoteSizingEnforced, UNVERIFIED_NOTE, logRemoteSizing, remoteSizingOf };
|
|
44
|
+
//# sourceMappingURL=remote-sizing.d.mts.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
// src/adapters/remote-sizing.ts
|
|
5
|
+
/** Said of every experimental preset until someone verifies it against the provider. */
|
|
6
|
+
const UNVERIFIED_NOTE = "not verified against live infrastructure";
|
|
7
|
+
/** The configured and effective limits of `remote`, as far as the runner enforces them. */
|
|
8
|
+
function remoteSizingOf(target, remote, enforced = {}) {
|
|
9
|
+
const pages = enforced.gate === void 0 ? pagesOf(remote) : gatePagesOf(enforced.gate);
|
|
10
|
+
const perConnection = remote.maxPagesPerConnection ?? null;
|
|
11
|
+
const limits = [
|
|
12
|
+
pages,
|
|
13
|
+
enforced.pagesPerConnection === false ? null : perConnection,
|
|
14
|
+
enforced.concurrency ?? null
|
|
15
|
+
].filter((value) => value !== null);
|
|
16
|
+
return {
|
|
17
|
+
target,
|
|
18
|
+
pages,
|
|
19
|
+
pagesPerConnection: perConnection,
|
|
20
|
+
effectivePagesPerConnection: limits.length === 0 ? null : Math.min(...limits),
|
|
21
|
+
maxConnections: remote.maxConnections ?? null,
|
|
22
|
+
scope: {
|
|
23
|
+
pages: "target",
|
|
24
|
+
connections: "process"
|
|
25
|
+
},
|
|
26
|
+
experimental: remote.experimental === true,
|
|
27
|
+
...remote.experimental === true ? { verification: UNVERIFIED_NOTE } : {}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** Log the limits at run start, before any connection is attempted. */
|
|
31
|
+
function logRemoteSizing(logger, target, remote, enforced) {
|
|
32
|
+
logger?.info(remoteSizingOf(target, remote, enforced), "remote sizing");
|
|
33
|
+
}
|
|
34
|
+
/** The shared gate's cap; a `sessions` callback that throws reads as unknown. */
|
|
35
|
+
function gatePagesOf(gate) {
|
|
36
|
+
try {
|
|
37
|
+
return gate.size;
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** The page limit now; a `sessions` function that throws or returns nonsense reads as unknown. */
|
|
43
|
+
function pagesOf(remote) {
|
|
44
|
+
try {
|
|
45
|
+
const value = typeof remote.sessions === "function" ? remote.sessions() : remote.sessions;
|
|
46
|
+
return Number.isInteger(value) && value > 0 ? value : null;
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export { UNVERIFIED_NOTE, logRemoteSizing, remoteSizingOf };
|
|
52
|
+
|
|
53
|
+
//# sourceMappingURL=remote-sizing.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-sizing.mjs","names":[],"sources":["../../src/adapters/remote-sizing.ts"],"sourcesContent":["/** What a remote target may open, for the run's log (ADR 0007, decision 4): limits only, no endpoints. */\nimport type { CaptureTarget } from \"./capture-target.ts\";\nimport type { RemoteBrowser } from \"./remote-browser.ts\";\n\n/** Said of every experimental preset until someone verifies it against the provider. */\nexport const UNVERIFIED_NOTE = \"not verified against live infrastructure\";\n\n/** The loggable limits of one remote target. A `null` limit is not capped. */\nexport interface RemoteSizing {\n target: CaptureTarget;\n /** Pages the plan allows at once: the target's page gate. Per target, per process. */\n pages: number | null;\n /** Pages on one connection, when capped. */\n pagesPerConnection: number | null;\n /** Pages one connection can hold: the lowest of `pages`, `pagesPerConnection` (when the runner enforces it) and the run's `concurrency`. */\n effectivePagesPerConnection: number | null;\n /** Connections this process holds at once, when capped. Per process. */\n maxConnections: number | null;\n scope: { pages: \"target\"; connections: \"process\" };\n experimental: boolean;\n /** Present only for an experimental preset. */\n verification?: typeof UNVERIFIED_NOTE;\n}\n\n/** What the runner actually enforces, so the effective limit is not overstated. */\nexport interface RemoteSizingEnforced {\n /** The shared page gate of the target; its size is the effective page limit. */\n gate?: { readonly size: number } | undefined;\n /** The run's configured page concurrency, when set. */\n concurrency?: number | undefined;\n /** Whether the runner applies `maxPagesPerConnection` (default true). */\n pagesPerConnection?: boolean | undefined;\n}\n\n/** The configured and effective limits of `remote`, as far as the runner enforces them. */\nexport function remoteSizingOf(\n target: CaptureTarget,\n remote: RemoteBrowser,\n enforced: RemoteSizingEnforced = {},\n): RemoteSizing {\n const pages = enforced.gate === undefined ? pagesOf(remote) : gatePagesOf(enforced.gate);\n const perConnection = remote.maxPagesPerConnection ?? null;\n const limits = [\n pages,\n enforced.pagesPerConnection === false ? null : perConnection,\n enforced.concurrency ?? null,\n ].filter((value): value is number => value !== null);\n return {\n target,\n pages,\n pagesPerConnection: perConnection,\n effectivePagesPerConnection: limits.length === 0 ? null : Math.min(...limits),\n maxConnections: remote.maxConnections ?? null,\n scope: { pages: \"target\", connections: \"process\" },\n experimental: remote.experimental === true,\n ...(remote.experimental === true ? { verification: UNVERIFIED_NOTE } : {}),\n };\n}\n\n/** Log the limits at run start, before any connection is attempted. */\nexport function logRemoteSizing(\n logger: { info(fields: object, message: string): void } | undefined,\n target: CaptureTarget,\n remote: RemoteBrowser,\n enforced?: RemoteSizingEnforced,\n): void {\n logger?.info(remoteSizingOf(target, remote, enforced), \"remote sizing\");\n}\n\n/** The shared gate's cap; a `sessions` callback that throws reads as unknown. */\nfunction gatePagesOf(gate: { readonly size: number }): number | null {\n try {\n return gate.size;\n } catch {\n return null;\n }\n}\n\n/** The page limit now; a `sessions` function that throws or returns nonsense reads as unknown. */\nfunction pagesOf(remote: RemoteBrowser): number | null {\n try {\n const value = typeof remote.sessions === \"function\" ? remote.sessions() : remote.sessions;\n return Number.isInteger(value) && value > 0 ? value : null;\n } catch {\n return null;\n }\n}\n"],"mappings":";;;;;AAKA,MAAa,kBAAkB;;AA8B/B,SAAgB,eACd,QACA,QACA,WAAiC,CAAC,GACpB;CACd,MAAM,QAAQ,SAAS,SAAS,KAAA,IAAY,QAAQ,MAAM,IAAI,YAAY,SAAS,IAAI;CACvF,MAAM,gBAAgB,OAAO,yBAAyB;CACtD,MAAM,SAAS;EACb;EACA,SAAS,uBAAuB,QAAQ,OAAO;EAC/C,SAAS,eAAe;CAC1B,CAAC,CAAC,QAAQ,UAA2B,UAAU,IAAI;CACnD,OAAO;EACL;EACA;EACA,oBAAoB;EACpB,6BAA6B,OAAO,WAAW,IAAI,OAAO,KAAK,IAAI,GAAG,MAAM;EAC5E,gBAAgB,OAAO,kBAAkB;EACzC,OAAO;GAAE,OAAO;GAAU,aAAa;EAAU;EACjD,cAAc,OAAO,iBAAiB;EACtC,GAAI,OAAO,iBAAiB,OAAO,EAAE,cAAc,gBAAgB,IAAI,CAAC;CAC1E;AACF;;AAGA,SAAgB,gBACd,QACA,QACA,QACA,UACM;CACN,QAAQ,KAAK,eAAe,QAAQ,QAAQ,QAAQ,GAAG,eAAe;AACxE;;AAGA,SAAS,YAAY,MAAgD;CACnE,IAAI;EACF,OAAO,KAAK;CACd,QAAQ;EACN,OAAO;CACT;AACF;;AAGA,SAAS,QAAQ,QAAsC;CACrD,IAAI;EACF,MAAM,QAAQ,OAAO,OAAO,aAAa,aAAa,OAAO,SAAS,IAAI,OAAO;EACjF,OAAO,OAAO,UAAU,KAAK,KAAK,QAAQ,IAAI,QAAQ;CACxD,QAAQ;EACN,OAAO;CACT;AACF"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
|
-
//#region src/adapters/render-runs.d.ts
|
|
4
3
|
/** In-flight render calls of a capture runner, so `cancel` reaches exactly the runs it names. */
|
|
5
4
|
/**
|
|
6
5
|
* Every render call a runner has in flight, by build and run id.
|
|
@@ -20,6 +19,5 @@ declare class RenderRuns<T> {
|
|
|
20
19
|
/** The runs `cancel(buildId, runId)` should stop: every run of the build without a run id. */
|
|
21
20
|
matching(buildId: string, runId?: string): T[];
|
|
22
21
|
}
|
|
23
|
-
//#endregion
|
|
24
22
|
export { RenderRuns };
|
|
25
23
|
//# sourceMappingURL=render-runs.d.mts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
|
|
4
|
+
// src/adapters/render-runs.ts
|
|
5
5
|
/** In-flight render calls of a capture runner, so `cancel` reaches exactly the runs it names. */
|
|
6
6
|
/**
|
|
7
7
|
* Every render call a runner has in flight, by build and run id.
|
|
@@ -35,7 +35,6 @@ var RenderRuns = class {
|
|
|
35
35
|
return [...this.#byBuild.get(buildId) ?? []].filter((entry) => runId === void 0 || entry.runId === runId).map((e) => e.run);
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
|
-
//#endregion
|
|
39
38
|
export { RenderRuns };
|
|
40
39
|
|
|
41
40
|
//# sourceMappingURL=render-runs.mjs.map
|
|
@@ -3,7 +3,6 @@ import __tsdown_shims_url from 'node:url';
|
|
|
3
3
|
import { AdapterSetupContext } from "../adapter/metadata.mjs";
|
|
4
4
|
import { AdapterSetupResult, AdapterSetupSources, HookEntry } from "../adapter/setup.mjs";
|
|
5
5
|
import { Logger } from "pino";
|
|
6
|
-
//#region src/adapters/setup-runner.d.ts
|
|
7
6
|
/** Collect every available `setup` hook in deterministic order. */
|
|
8
7
|
declare function collectSetups(sources: AdapterSetupSources): HookEntry[];
|
|
9
8
|
/** Collect every available `teardown` hook in deterministic order. */
|
|
@@ -18,6 +17,5 @@ declare function bindAdapterLoggers(sources: AdapterSetupSources, logger: Logger
|
|
|
18
17
|
declare function runAdapterSetups(entries: HookEntry[], ctx: AdapterSetupContext, logger: Logger): Promise<AdapterSetupResult>;
|
|
19
18
|
/** Run teardown hooks concurrently; failures are collected, never thrown. */
|
|
20
19
|
declare function runAdapterTeardowns(entries: HookEntry[], ctx: AdapterSetupContext, logger: Logger): Promise<AdapterSetupResult>;
|
|
21
|
-
//#endregion
|
|
22
20
|
export { bindAdapterLoggers, collectSetups, collectTeardowns, runAdapterSetups, runAdapterTeardowns };
|
|
23
21
|
//# sourceMappingURL=setup-runner.d.mts.map
|
|
@@ -2,7 +2,7 @@ import "node:path";
|
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { sanitizeErrorText } from "../utils/redact.mjs";
|
|
5
|
-
|
|
5
|
+
// src/adapters/setup-runner.ts
|
|
6
6
|
function hookEntry(adapter, hook) {
|
|
7
7
|
const lifecycle = adapter.lifecycle;
|
|
8
8
|
if (!lifecycle) return null;
|
|
@@ -141,7 +141,6 @@ async function runAdapterSetups(entries, ctx, logger) {
|
|
|
141
141
|
async function runAdapterTeardowns(entries, ctx, logger) {
|
|
142
142
|
return await runHooks(entries, ctx, logger, "teardown", SLOW_TEARDOWN_MS);
|
|
143
143
|
}
|
|
144
|
-
//#endregion
|
|
145
144
|
export { bindAdapterLoggers, collectSetups, collectTeardowns, runAdapterSetups, runAdapterTeardowns };
|
|
146
145
|
|
|
147
146
|
//# sourceMappingURL=setup-runner.mjs.map
|
|
@@ -2,11 +2,9 @@ import __tsdown_shims_path from 'node:path';
|
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
3
|
import { ShelfOptions } from "../config.mjs";
|
|
4
4
|
import { AdapterSetupSources } from "../adapter/setup.mjs";
|
|
5
|
-
//#region src/adapters/sources.d.ts
|
|
6
5
|
/** The adapter slots of `options`, with `auth` carried along for boot validation. */
|
|
7
6
|
declare function adapterSourcesOf(options: ShelfOptions): AdapterSetupSources & {
|
|
8
7
|
auth?: unknown;
|
|
9
8
|
};
|
|
10
|
-
//#endregion
|
|
11
9
|
export { adapterSourcesOf };
|
|
12
10
|
//# sourceMappingURL=sources.d.mts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
|
|
4
|
+
// src/adapters/sources.ts
|
|
5
5
|
/** The adapter slots of `options`, with `auth` carried along for boot validation. */
|
|
6
6
|
function adapterSourcesOf(options) {
|
|
7
7
|
return {
|
|
@@ -14,7 +14,6 @@ function adapterSourcesOf(options) {
|
|
|
14
14
|
auth: options.auth
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
|
-
//#endregion
|
|
18
17
|
export { adapterSourcesOf };
|
|
19
18
|
|
|
20
19
|
//# sourceMappingURL=sources.mjs.map
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
3
|
import { AdapterSetupFailure, AdapterSetupSources } from "../adapter/setup.mjs";
|
|
4
|
-
//#region src/adapters/validate.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* Validate one adapter against its slot. Returns human-readable reasons
|
|
7
6
|
* (empty when valid). Never throws — callers decide fail-fast vs collect.
|
|
@@ -25,6 +24,5 @@ declare function validateAdapterSources(sources: AdapterSetupSources): AdapterSe
|
|
|
25
24
|
declare function validateBootAssembly(sources: AdapterSetupSources & {
|
|
26
25
|
auth?: unknown;
|
|
27
26
|
}): AdapterSetupFailure[];
|
|
28
|
-
//#endregion
|
|
29
27
|
export { validateAdapter, validateAdapterSources, validateAuth, validateBootAssembly };
|
|
30
28
|
//# sourceMappingURL=validate.d.mts.map
|