@storyshelf/core 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/capture-queue.d.mts +0 -2
- package/dist/adapter/capture-runner.d.mts +9 -4
- package/dist/adapter/capture-runner.mjs +6 -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 +112 -31
- package/dist/adapters/remote-browser.mjs +132 -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/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 +0 -2
- package/dist/capture/fingerprint.mjs +1 -2
- 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 +37 -6
- 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 +0 -2
- package/dist/models/design-health.mjs +1 -2
- package/dist/models/design-nodes.d.mts +2 -4
- package/dist/models/design-nodes.mjs +1 -2
- 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 +5 -2
- package/dist/models.mjs +5 -2
- 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 +0 -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 +5 -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 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-environment.mjs","names":[],"sources":["../../src/utils/remote-environment.ts"],"sourcesContent":["/**\n * The environment of a remote browser, measured inside it (ADR 0007, decision 2). `fc-list` and\n * `process.platform` describe the machine running the runner, not the one that rendered.\n */\nimport type { CaptureEnvironment } from \"../adapters/capture-runner.ts\";\nimport { sha256 } from \"./hash.ts\";\n\n/** What the probe page reports. */\nexport interface BrowserProbe {\n /** OS as the page sees it (`navigator.userAgentData.platform`, else `navigator.platform`). */\n platform?: string;\n deviceScaleFactor?: number;\n /** Which of {@link FONT_PROBE_FAMILIES} are installed. */\n fonts?: string[];\n}\n\n/**\n * Families probed for the fonts digest: system fonts that differ between operating systems and\n * image builds, so a provider-side change moves the hash. A fixed list; changing it re-baselines\n * every remote target's fingerprint.\n */\nexport const FONT_PROBE_FAMILIES: readonly string[] = [\n \"Arial\",\n \"Helvetica\",\n \"Helvetica Neue\",\n \"Times New Roman\",\n \"Georgia\",\n \"Verdana\",\n \"Tahoma\",\n \"Trebuchet MS\",\n \"Courier New\",\n \"Consolas\",\n \"Menlo\",\n \"Monaco\",\n \"Segoe UI\",\n \"Calibri\",\n \"Cambria\",\n \"San Francisco\",\n \"SF Pro Text\",\n \"Lucida Grande\",\n \"Roboto\",\n \"Open Sans\",\n \"Noto Sans\",\n \"Noto Serif\",\n \"Noto Sans Mono\",\n \"Noto Color Emoji\",\n \"Apple Color Emoji\",\n \"Segoe UI Emoji\",\n \"DejaVu Sans\",\n \"DejaVu Serif\",\n \"DejaVu Sans Mono\",\n \"Liberation Sans\",\n \"Liberation Serif\",\n \"Liberation Mono\",\n \"Ubuntu\",\n \"Cantarell\",\n \"Droid Sans\",\n \"Source Sans Pro\",\n \"Fira Sans\",\n \"Inter\",\n \"Arial Unicode MS\",\n \"MS Gothic\",\n \"Hiragino Sans\",\n \"PingFang SC\",\n \"Microsoft YaHei\",\n \"Malgun Gothic\",\n];\n\n/**\n * Read the OS, the device pixel ratio and the installed probe fonts. Runs inside the page, so it\n * must stay one self-contained function (no imports, no closures): the runners pass it to\n * `page.evaluate`, which sends its source. A font counts as installed when text in it measures\n * differently from the generic family it would fall back to; `document.fonts.check` answers `true`\n * for a family that does not exist, so it cannot tell.\n */\n// oxlint-disable-next-line eslint/max-statements, eslint/max-lines-per-function -- must stay one self-contained function (see above)\nexport function inPageProbe(families: string[]): BrowserProbe {\n const win = globalThis as unknown as {\n devicePixelRatio?: number;\n navigator: { userAgentData?: { platform?: string }; platform?: string };\n document: {\n createElement: (tag: string) => {\n getContext: (kind: string) => {\n font: string;\n measureText: (text: string) => { width: number };\n } | null;\n };\n };\n };\n const probe: BrowserProbe = {};\n const reported = win.navigator.userAgentData?.platform;\n const platform = reported === undefined || reported === \"\" ? win.navigator.platform : reported;\n if (platform) probe.platform = platform;\n if (typeof win.devicePixelRatio === \"number\") probe.deviceScaleFactor = win.devicePixelRatio;\n const context = win.document.createElement(\"canvas\").getContext(\"2d\");\n if (!context) return probe;\n const text = \"mmmmmmmmmmlli1WwQqÅgy 漢字\";\n const generics = [\"monospace\", \"sans-serif\", \"serif\"];\n const widthOf = (font: string): number => {\n context.font = `72px ${font}`;\n return context.measureText(text).width;\n };\n const baseline = generics.map((generic) => widthOf(generic));\n probe.fonts = families.filter((family) =>\n generics.some((generic, index) => widthOf(`\"${family}\", ${generic}`) !== baseline[index]),\n );\n return probe;\n}\n\n/**\n * The environment of a remote render: the driver's browser version and what the page reported. A\n * field the page could not give is left out, except the OS, which falls back to what the service\n * was configured to render on; an unknown fingerprint component is never a change (ADR 0005).\n */\nexport function remoteEnvironment(options: {\n browserVersion?: string | undefined;\n probe?: BrowserProbe | undefined;\n declaredPlatform?: string | undefined;\n}): CaptureEnvironment {\n const { probe, browserVersion } = options;\n const platform = normalizePlatform(probe?.platform) ?? options.declaredPlatform;\n const fonts = probe?.fonts;\n return {\n ...(browserVersion ? { browserVersion } : {}),\n ...(platform ? { platform } : {}),\n ...(probe?.deviceScaleFactor ? { deviceScaleFactor: probe.deviceScaleFactor } : {}),\n ...(fonts && fonts.length > 0\n ? { fontsHash: sha256(fonts.toSorted().join(\"\\n\")).slice(0, 16) }\n : {}),\n };\n}\n\n/** `Win32`, `Windows` and `Linux x86_64` read as the OS, in one spelling per OS. */\nfunction normalizePlatform(value: string | undefined): string | undefined {\n const text = value?.trim().toLowerCase();\n if (!text) {\n return undefined;\n }\n if (text.startsWith(\"win\")) return \"windows\";\n if (text.startsWith(\"mac\") || text === \"iphone\" || text === \"ipad\") return \"macos\";\n return text.startsWith(\"linux\") ? \"linux\" : text;\n}\n"],"mappings":";;;;;;;;;;AAqBA,MAAa,sBAAyC;CACpD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;;;;;AAUA,SAAgB,YAAY,UAAkC;CAC5D,MAAM,MAAM;CAYZ,MAAM,QAAsB,CAAC;CAC7B,MAAM,WAAW,IAAI,UAAU,eAAe;CAC9C,MAAM,WAAW,aAAa,KAAA,KAAa,aAAa,KAAK,IAAI,UAAU,WAAW;CACtF,IAAI,UAAU,MAAM,WAAW;CAC/B,IAAI,OAAO,IAAI,qBAAqB,UAAU,MAAM,oBAAoB,IAAI;CAC5E,MAAM,UAAU,IAAI,SAAS,cAAc,QAAQ,CAAC,CAAC,WAAW,IAAI;CACpE,IAAI,CAAC,SAAS,OAAO;CACrB,MAAM,OAAO;CACb,MAAM,WAAW;EAAC;EAAa;EAAc;CAAO;CACpD,MAAM,WAAW,SAAyB;EACxC,QAAQ,OAAO,QAAQ;EACvB,OAAO,QAAQ,YAAY,IAAI,CAAC,CAAC;CACnC;CACA,MAAM,WAAW,SAAS,KAAK,YAAY,QAAQ,OAAO,CAAC;CAC3D,MAAM,QAAQ,SAAS,QAAQ,WAC7B,SAAS,MAAM,SAAS,UAAU,QAAQ,IAAI,OAAO,KAAK,SAAS,MAAM,SAAS,MAAM,CAC1F;CACA,OAAO;AACT;;;;;;AAOA,SAAgB,kBAAkB,SAIX;CACrB,MAAM,EAAE,OAAO,mBAAmB;CAClC,MAAM,WAAW,kBAAkB,OAAO,QAAQ,KAAK,QAAQ;CAC/D,MAAM,QAAQ,OAAO;CACrB,OAAO;EACL,GAAI,iBAAiB,EAAE,eAAe,IAAI,CAAC;EAC3C,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;EAC/B,GAAI,OAAO,oBAAoB,EAAE,mBAAmB,MAAM,kBAAkB,IAAI,CAAC;EACjF,GAAI,SAAS,MAAM,SAAS,IACxB,EAAE,WAAW,OAAO,MAAM,SAAS,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,GAAG,EAAE,EAAE,IAC9D,CAAC;CACP;AACF;;AAGA,SAAS,kBAAkB,OAA+C;CACxE,MAAM,OAAO,OAAO,KAAK,CAAC,CAAC,YAAY;CACvC,IAAI,CAAC,MACH;CAEF,IAAI,KAAK,WAAW,KAAK,GAAG,OAAO;CACnC,IAAI,KAAK,WAAW,KAAK,KAAK,SAAS,YAAY,SAAS,QAAQ,OAAO;CAC3E,OAAO,KAAK,WAAW,OAAO,IAAI,UAAU;AAC9C"}
|
|
1
|
+
{"version":3,"file":"remote-environment.mjs","names":[],"sources":["../../src/utils/remote-environment.ts"],"sourcesContent":["/**\n * The environment of a remote browser, measured inside it (ADR 0007, decision 2). `fc-list` and\n * `process.platform` describe the machine running the runner, not the one that rendered.\n */\nimport type { CaptureEnvironment } from \"../adapters/capture-runner.ts\";\nimport { sha256 } from \"./hash.ts\";\n\n/** What the probe page reports. */\nexport interface BrowserProbe {\n /** OS as the page sees it (`navigator.userAgentData.platform`, else `navigator.platform`). */\n platform?: string;\n deviceScaleFactor?: number;\n /** Which of {@link FONT_PROBE_FAMILIES} are installed. */\n fonts?: string[];\n}\n\n/**\n * Families probed for the fonts digest: system fonts that differ between operating systems and\n * image builds, so a provider-side change moves the hash. A fixed list; changing it re-baselines\n * every remote target's fingerprint.\n */\nexport const FONT_PROBE_FAMILIES: readonly string[] = [\n \"Arial\",\n \"Helvetica\",\n \"Helvetica Neue\",\n \"Times New Roman\",\n \"Georgia\",\n \"Verdana\",\n \"Tahoma\",\n \"Trebuchet MS\",\n \"Courier New\",\n \"Consolas\",\n \"Menlo\",\n \"Monaco\",\n \"Segoe UI\",\n \"Calibri\",\n \"Cambria\",\n \"San Francisco\",\n \"SF Pro Text\",\n \"Lucida Grande\",\n \"Roboto\",\n \"Open Sans\",\n \"Noto Sans\",\n \"Noto Serif\",\n \"Noto Sans Mono\",\n \"Noto Color Emoji\",\n \"Apple Color Emoji\",\n \"Segoe UI Emoji\",\n \"DejaVu Sans\",\n \"DejaVu Serif\",\n \"DejaVu Sans Mono\",\n \"Liberation Sans\",\n \"Liberation Serif\",\n \"Liberation Mono\",\n \"Ubuntu\",\n \"Cantarell\",\n \"Droid Sans\",\n \"Source Sans Pro\",\n \"Fira Sans\",\n \"Inter\",\n \"Arial Unicode MS\",\n \"MS Gothic\",\n \"Hiragino Sans\",\n \"PingFang SC\",\n \"Microsoft YaHei\",\n \"Malgun Gothic\",\n];\n\n/**\n * Read the OS, the device pixel ratio and the installed probe fonts. Runs inside the page, so it\n * must stay one self-contained function (no imports, no closures): the runners pass it to\n * `page.evaluate`, which sends its source. A font counts as installed when text in it measures\n * differently from the generic family it would fall back to; `document.fonts.check` answers `true`\n * for a family that does not exist, so it cannot tell.\n */\n// oxlint-disable-next-line eslint/max-statements, eslint/max-lines-per-function -- must stay one self-contained function (see above)\nexport function inPageProbe(families: string[]): BrowserProbe {\n const win = globalThis as unknown as {\n devicePixelRatio?: number;\n navigator: { userAgentData?: { platform?: string }; platform?: string };\n document: {\n createElement: (tag: string) => {\n getContext: (kind: string) => {\n font: string;\n measureText: (text: string) => { width: number };\n } | null;\n };\n };\n };\n const probe: BrowserProbe = {};\n const reported = win.navigator.userAgentData?.platform;\n const platform = reported === undefined || reported === \"\" ? win.navigator.platform : reported;\n if (platform) probe.platform = platform;\n if (typeof win.devicePixelRatio === \"number\") probe.deviceScaleFactor = win.devicePixelRatio;\n const context = win.document.createElement(\"canvas\").getContext(\"2d\");\n if (!context) return probe;\n const text = \"mmmmmmmmmmlli1WwQqÅgy 漢字\";\n const generics = [\"monospace\", \"sans-serif\", \"serif\"];\n const widthOf = (font: string): number => {\n context.font = `72px ${font}`;\n return context.measureText(text).width;\n };\n const baseline = generics.map((generic) => widthOf(generic));\n probe.fonts = families.filter((family) =>\n generics.some((generic, index) => widthOf(`\"${family}\", ${generic}`) !== baseline[index]),\n );\n return probe;\n}\n\n/** How a {@link boundedProbe} ended. */\nexport type ProbeOutcome<T> =\n | { status: \"done\"; value: T }\n /** Still running after `timeoutMs`: the environment is unknown, the run goes on. */\n | { status: \"timeout\" }\n /** The run was cancelled: the run stops, it is never read as \"unknown environment\". */\n | { status: \"cancelled\" }\n /** The probe threw (a service outage, a page that would not open): unknown, with a warning. */\n | { status: \"failed\"; error: unknown };\n\n/** What a probe's work can ask about its own fate. */\nexport interface ProbeControl {\n /**\n * True once the probe timed out or was cancelled. Work that opens a page must check it after\n * every await and release what it opened, because nothing waits for it any more.\n */\n stopped: () => boolean;\n}\n\n/**\n * Run probe work under a time limit and a cancel signal (ADR 0007, decision 2). A probe that hangs\n * on an unresponsive service must not hold a render, and a cancel must interrupt it instead of\n * waiting for the service. The work is never awaited past the limit and never leaves an unhandled\n * rejection; releasing the page it opened is the caller's job (`ProbeControl.stopped`, or closing\n * what it holds once this returns). A failure after the signal fired reads as cancelled, since\n * closing the browser is how a cancel interrupts it.\n */\nexport async function boundedProbe<T>(options: {\n run: (control: ProbeControl) => Promise<T>;\n timeoutMs: number;\n signal?: AbortSignal | undefined;\n}): Promise<ProbeOutcome<T>> {\n const { signal, timeoutMs } = options;\n if (signal?.aborted) {\n return { status: \"cancelled\" };\n }\n const halt = { halted: false };\n const work = options.run({ stopped: () => halt.halted }).then(\n (value): ProbeOutcome<T> => ({ status: \"done\", value }),\n (error: unknown): ProbeOutcome<T> => ({ status: \"failed\", error }),\n );\n return await settle(work, limitOf<T>(timeoutMs, signal), signal, halt);\n}\n\n/** Wait for the work or the limit, mark the work stopped if it lost, and drop the timer and listener. */\nasync function settle<T>(\n work: Promise<ProbeOutcome<T>>,\n limit: { reached: Promise<ProbeOutcome<T>>; clear: () => void },\n signal: AbortSignal | undefined,\n halt: { halted: boolean },\n): Promise<ProbeOutcome<T>> {\n try {\n const outcome = await Promise.race([work, limit.reached]);\n halt.halted = outcome.status === \"timeout\" || outcome.status === \"cancelled\";\n return outcome.status === \"failed\" && signal?.aborted ? { status: \"cancelled\" } : outcome;\n } finally {\n limit.clear();\n }\n}\n\n/** A promise that settles on the timeout or the abort, and a way to drop both. */\nfunction limitOf<T>(\n timeoutMs: number,\n signal: AbortSignal | undefined,\n): { reached: Promise<ProbeOutcome<T>>; clear: () => void } {\n let timer: ReturnType<typeof setTimeout> | undefined;\n let onAbort: (() => void) | undefined;\n const reached = new Promise<ProbeOutcome<T>>((resolve) => {\n timer = setTimeout(() => {\n resolve({ status: \"timeout\" });\n }, timeoutMs);\n onAbort = (): void => {\n resolve({ status: \"cancelled\" });\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n return {\n reached,\n clear: (): void => {\n clearTimeout(timer);\n if (onAbort) signal?.removeEventListener(\"abort\", onAbort);\n },\n };\n}\n\n/**\n * The environment of a remote render: the driver's browser version and what the page reported. A\n * field the page could not give is left out, except the OS, which falls back to what the service\n * was configured to render on; an unknown fingerprint component is never a change (ADR 0005).\n */\nexport function remoteEnvironment(options: {\n browserVersion?: string | undefined;\n probe?: BrowserProbe | undefined;\n declaredPlatform?: string | undefined;\n}): CaptureEnvironment {\n const { probe, browserVersion } = options;\n const platform = normalizePlatform(probe?.platform) ?? options.declaredPlatform;\n const fonts = probe?.fonts;\n return {\n ...(browserVersion ? { browserVersion } : {}),\n ...(platform ? { platform } : {}),\n ...(probe?.deviceScaleFactor ? { deviceScaleFactor: probe.deviceScaleFactor } : {}),\n ...(fonts && fonts.length > 0\n ? { fontsHash: sha256(fonts.toSorted().join(\"\\n\")).slice(0, 16) }\n : {}),\n };\n}\n\n/** `Win32`, `Windows` and `Linux x86_64` read as the OS, in one spelling per OS. */\nfunction normalizePlatform(value: string | undefined): string | undefined {\n const text = value?.trim().toLowerCase();\n if (!text) {\n return undefined;\n }\n if (text.startsWith(\"win\")) return \"windows\";\n if (text.startsWith(\"mac\") || text === \"iphone\" || text === \"ipad\") return \"macos\";\n return text.startsWith(\"linux\") ? \"linux\" : text;\n}\n"],"mappings":"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"}
|
|
@@ -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/utils/resources.d.ts
|
|
4
3
|
/** CPU and memory actually available to this process (container limits included). */
|
|
5
4
|
interface HostResources {
|
|
6
5
|
/** Fractional vCPUs: a cgroup quota of 0.5 CPU reports `0.5`. */
|
|
@@ -59,6 +58,5 @@ interface CaptureHostSummary {
|
|
|
59
58
|
}
|
|
60
59
|
/** Summarise the host budget and effective capture concurrency, for the System page. */
|
|
61
60
|
declare function describeCaptureHost(configuredBuilds?: number, resources?: HostResources): CaptureHostSummary;
|
|
62
|
-
//#endregion
|
|
63
61
|
export { CaptureHostSummary, DEFAULT_RECYCLE_BROWSER_EVERY, HostResources, describeCaptureHost, describeSizing, detectResources, engineWeight, maxPageConcurrency, recommendedBuildConcurrency, recommendedNavigationTimeoutMs, recommendedPageConcurrency };
|
|
64
62
|
//# sourceMappingURL=resources.d.mts.map
|
package/dist/utils/resources.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import "node:url";
|
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { availableParallelism, totalmem } from "node:os";
|
|
5
5
|
import { readFileSync } from "node:fs";
|
|
6
|
-
|
|
6
|
+
// src/utils/resources.ts
|
|
7
7
|
const MB = 1048576;
|
|
8
8
|
/** Headroom for Node, the server and Chromium's browser process. */
|
|
9
9
|
const BASE_MEMORY_MB = 512;
|
|
@@ -116,7 +116,6 @@ function readText(path) {
|
|
|
116
116
|
return null;
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
|
-
//#endregion
|
|
120
119
|
export { DEFAULT_RECYCLE_BROWSER_EVERY, describeCaptureHost, describeSizing, detectResources, engineWeight, maxPageConcurrency, recommendedBuildConcurrency, recommendedNavigationTimeoutMs, recommendedPageConcurrency };
|
|
121
120
|
|
|
122
121
|
//# sourceMappingURL=resources.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 { StorageAdapter } from "../adapter/storage.mjs";
|
|
4
|
-
//#region src/utils/screenshot-store.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* Store a screenshot under the SHA-256 of its bytes and return its path.
|
|
7
6
|
*
|
|
@@ -10,6 +9,5 @@ import { StorageAdapter } from "../adapter/storage.mjs";
|
|
|
10
9
|
* identical paths mean identical pixels (the diff can be skipped without reading either image).
|
|
11
10
|
*/
|
|
12
11
|
declare function storeScreenshot(storage: StorageAdapter, projectId: string, data: Buffer): Promise<string>;
|
|
13
|
-
//#endregion
|
|
14
12
|
export { storeScreenshot };
|
|
15
13
|
//# sourceMappingURL=screenshot-store.d.mts.map
|
|
@@ -4,7 +4,7 @@ import.meta.url;
|
|
|
4
4
|
import { ulid } from "./ulid.mjs";
|
|
5
5
|
import { contentDir, contentPath } from "../paths.mjs";
|
|
6
6
|
import { createHash } from "node:crypto";
|
|
7
|
-
|
|
7
|
+
// src/utils/screenshot-store.ts
|
|
8
8
|
/** Content-addressed screenshot storage: identical bytes are stored once per project. */
|
|
9
9
|
/**
|
|
10
10
|
* Store a screenshot under the SHA-256 of its bytes and return its path.
|
|
@@ -35,7 +35,6 @@ async function withContentClaim(storage, projectId, work) {
|
|
|
35
35
|
await storage.delete(path).catch(() => false);
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
-
//#endregion
|
|
39
38
|
export { CLAIMS_DIR, storeScreenshot, withContentClaim };
|
|
40
39
|
|
|
41
40
|
//# sourceMappingURL=screenshot-store.mjs.map
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
|
-
//#region src/utils/session-gate.d.ts
|
|
4
3
|
/** Per-target session caps for remote browsers, shared by every render in the process (ADR 0007, decision 4). */
|
|
5
4
|
/** Runs work under a fixed number of sessions. */
|
|
6
5
|
interface SessionGate {
|
|
7
|
-
/** Sessions the plan allows. */
|
|
6
|
+
/** Sessions the plan allows now (a scaling provider's cap can change between reads). */
|
|
8
7
|
readonly size: number;
|
|
9
8
|
/** Run `fn` once a session is free. */
|
|
10
9
|
run<T>(fn: () => Promise<T>): Promise<T>;
|
|
@@ -14,9 +13,11 @@ interface SessionGate {
|
|
|
14
13
|
* concurrent shards share the cap instead of multiplying it. A later call with another `sessions`
|
|
15
14
|
* keeps the first gate: the size is the runner's configuration and does not change under it.
|
|
16
15
|
*/
|
|
17
|
-
declare function sessionGateFor(target: string, sessions: number): SessionGate;
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
declare function sessionGateFor(target: string, sessions: number | (() => number), onInvalid?: (value: unknown) => void): SessionGate;
|
|
17
|
+
/**
|
|
18
|
+
* A gate of its own (not shared through {@link sessionGateFor}); `size` below 1 counts as 1. A
|
|
19
|
+
* function is read on every use, so a cap that grows lets waiting work in at the next release.
|
|
20
|
+
*/
|
|
21
|
+
declare function createSessionGate(size: number | (() => number), onInvalid?: (value: unknown) => void): SessionGate;
|
|
21
22
|
export { SessionGate, createSessionGate, sessionGateFor };
|
|
22
23
|
//# sourceMappingURL=session-gate.d.mts.map
|
|
@@ -1,33 +1,48 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
|
|
4
|
+
// src/utils/session-gate.ts
|
|
5
5
|
const gates = /* @__PURE__ */ new Map();
|
|
6
6
|
/**
|
|
7
7
|
* The gate of `target`, created on first use. One gate per target for the whole process, so
|
|
8
8
|
* concurrent shards share the cap instead of multiplying it. A later call with another `sessions`
|
|
9
9
|
* keeps the first gate: the size is the runner's configuration and does not change under it.
|
|
10
10
|
*/
|
|
11
|
-
function sessionGateFor(target, sessions) {
|
|
11
|
+
function sessionGateFor(target, sessions, onInvalid) {
|
|
12
12
|
let gate = gates.get(target);
|
|
13
13
|
if (gate === void 0) {
|
|
14
|
-
gate = createSessionGate(sessions);
|
|
14
|
+
gate = createSessionGate(sessions, onInvalid);
|
|
15
15
|
gates.set(target, gate);
|
|
16
16
|
}
|
|
17
17
|
return gate;
|
|
18
18
|
}
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
/**
|
|
20
|
+
* A gate of its own (not shared through {@link sessionGateFor}); `size` below 1 counts as 1. A
|
|
21
|
+
* function is read on every use, so a cap that grows lets waiting work in at the next release.
|
|
22
|
+
*/
|
|
23
|
+
function createSessionGate(size, onInvalid) {
|
|
24
|
+
let last = 1;
|
|
25
|
+
let warned = false;
|
|
26
|
+
const limitNow = () => {
|
|
27
|
+
const raw = typeof size === "function" ? size() : size;
|
|
28
|
+
if (typeof raw === "number" && Number.isFinite(raw)) last = Math.max(1, Math.floor(raw));
|
|
29
|
+
else if (!warned) {
|
|
30
|
+
warned = true;
|
|
31
|
+
onInvalid?.(raw);
|
|
32
|
+
}
|
|
33
|
+
return last;
|
|
34
|
+
};
|
|
22
35
|
let active = 0;
|
|
23
36
|
const waiting = [];
|
|
24
37
|
const release = () => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
38
|
+
active -= 1;
|
|
39
|
+
while (active < limitNow() && waiting.length > 0) {
|
|
40
|
+
active += 1;
|
|
41
|
+
waiting.shift()?.();
|
|
42
|
+
}
|
|
28
43
|
};
|
|
29
44
|
const acquire = async () => {
|
|
30
|
-
if (active <
|
|
45
|
+
if (active < limitNow()) {
|
|
31
46
|
active += 1;
|
|
32
47
|
return;
|
|
33
48
|
}
|
|
@@ -36,7 +51,9 @@ function createSessionGate(size) {
|
|
|
36
51
|
});
|
|
37
52
|
};
|
|
38
53
|
return {
|
|
39
|
-
size
|
|
54
|
+
get size() {
|
|
55
|
+
return limitNow();
|
|
56
|
+
},
|
|
40
57
|
run: async (fn) => {
|
|
41
58
|
await acquire();
|
|
42
59
|
try {
|
|
@@ -47,7 +64,6 @@ function createSessionGate(size) {
|
|
|
47
64
|
}
|
|
48
65
|
};
|
|
49
66
|
}
|
|
50
|
-
//#endregion
|
|
51
67
|
export { createSessionGate, sessionGateFor };
|
|
52
68
|
|
|
53
69
|
//# sourceMappingURL=session-gate.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-gate.mjs","names":[],"sources":["../../src/utils/session-gate.ts"],"sourcesContent":["/** Per-target session caps for remote browsers, shared by every render in the process (ADR 0007, decision 4). */\n\n/** Runs work under a fixed number of sessions. */\nexport interface SessionGate {\n /** Sessions the plan allows. */\n readonly size: number;\n /** Run `fn` once a session is free. */\n run<T>(fn: () => Promise<T>): Promise<T>;\n}\n\nconst gates = new Map<string, SessionGate>();\n\n/**\n * The gate of `target`, created on first use. One gate per target for the whole process, so\n * concurrent shards share the cap instead of multiplying it. A later call with another `sessions`\n * keeps the first gate: the size is the runner's configuration and does not change under it.\n */\nexport function sessionGateFor(target: string
|
|
1
|
+
{"version":3,"file":"session-gate.mjs","names":[],"sources":["../../src/utils/session-gate.ts"],"sourcesContent":["/** Per-target session caps for remote browsers, shared by every render in the process (ADR 0007, decision 4). */\n\n/** Runs work under a fixed number of sessions. */\nexport interface SessionGate {\n /** Sessions the plan allows now (a scaling provider's cap can change between reads). */\n readonly size: number;\n /** Run `fn` once a session is free. */\n run<T>(fn: () => Promise<T>): Promise<T>;\n}\n\nconst gates = new Map<string, SessionGate>();\n\n/**\n * The gate of `target`, created on first use. One gate per target for the whole process, so\n * concurrent shards share the cap instead of multiplying it. A later call with another `sessions`\n * keeps the first gate: the size is the runner's configuration and does not change under it.\n */\nexport function sessionGateFor(\n target: string,\n sessions: number | (() => number),\n onInvalid?: (value: unknown) => void,\n): SessionGate {\n let gate = gates.get(target);\n if (gate === undefined) {\n gate = createSessionGate(sessions, onInvalid);\n gates.set(target, gate);\n }\n return gate;\n}\n\n/**\n * A gate of its own (not shared through {@link sessionGateFor}); `size` below 1 counts as 1. A\n * function is read on every use, so a cap that grows lets waiting work in at the next release.\n */\nexport function createSessionGate(\n size: number | (() => number),\n onInvalid?: (value: unknown) => void,\n): SessionGate {\n let last = 1;\n let warned = false;\n // A function returning garbage (NaN, a string) keeps the last good cap instead of blocking every page.\n const limitNow = (): number => {\n const raw: unknown = typeof size === \"function\" ? size() : size;\n if (typeof raw === \"number\" && Number.isFinite(raw)) {\n last = Math.max(1, Math.floor(raw));\n } else if (!warned) {\n warned = true;\n onInvalid?.(raw);\n }\n return last;\n };\n let active = 0;\n const waiting: (() => void)[] = [];\n // A cap that grew lets in as many waiters as it now has room for, not one per release.\n const release = (): void => {\n active -= 1;\n while (active < limitNow() && waiting.length > 0) {\n active += 1;\n waiting.shift()?.();\n }\n };\n const acquire = async (): Promise<void> => {\n if (active < limitNow()) {\n active += 1;\n return;\n }\n await new Promise<void>((resolve) => {\n waiting.push(resolve);\n });\n };\n return {\n get size() {\n return limitNow();\n },\n run: async (fn) => {\n await acquire();\n try {\n return await fn();\n } finally {\n release();\n }\n },\n };\n}\n"],"mappings":";;;;AAUA,MAAM,wBAAQ,IAAI,IAAyB;;;;;;AAO3C,SAAgB,eACd,QACA,UACA,WACa;CACb,IAAI,OAAO,MAAM,IAAI,MAAM;CAC3B,IAAI,SAAS,KAAA,GAAW;EACtB,OAAO,kBAAkB,UAAU,SAAS;EAC5C,MAAM,IAAI,QAAQ,IAAI;CACxB;CACA,OAAO;AACT;;;;;AAMA,SAAgB,kBACd,MACA,WACa;CACb,IAAI,OAAO;CACX,IAAI,SAAS;CAEb,MAAM,iBAAyB;EAC7B,MAAM,MAAe,OAAO,SAAS,aAAa,KAAK,IAAI;EAC3D,IAAI,OAAO,QAAQ,YAAY,OAAO,SAAS,GAAG,GAChD,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,GAAG,CAAC;OAC7B,IAAI,CAAC,QAAQ;GAClB,SAAS;GACT,YAAY,GAAG;EACjB;EACA,OAAO;CACT;CACA,IAAI,SAAS;CACb,MAAM,UAA0B,CAAC;CAEjC,MAAM,gBAAsB;EAC1B,UAAU;EACV,OAAO,SAAS,SAAS,KAAK,QAAQ,SAAS,GAAG;GAChD,UAAU;GACV,QAAQ,MAAM,CAAC,GAAG;EACpB;CACF;CACA,MAAM,UAAU,YAA2B;EACzC,IAAI,SAAS,SAAS,GAAG;GACvB,UAAU;GACV;EACF;EACA,MAAM,IAAI,SAAe,YAAY;GACnC,QAAQ,KAAK,OAAO;EACtB,CAAC;CACH;CACA,OAAO;EACL,IAAI,OAAO;GACT,OAAO,SAAS;EAClB;EACA,KAAK,OAAO,OAAO;GACjB,MAAM,QAAQ;GACd,IAAI;IACF,OAAO,MAAM,GAAG;GAClB,UAAU;IACR,QAAQ;GACV;EACF;CACF;AACF"}
|
|
@@ -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 { SecretInput } from "./encrypt.mjs";
|
|
4
|
-
//#region src/utils/storybook-token.d.ts
|
|
5
4
|
/** The path prefix every signed Storybook URL starts with; `/_/` is the unrelated short link. */
|
|
6
5
|
declare const STORYBOOK_TOKEN_PREFIX = "/_s/";
|
|
7
6
|
/** Regex source of a token, shared by the shape check and the log redaction. */
|
|
@@ -24,12 +23,20 @@ declare const MIN_CAPTURES_PER_SLICE = 50;
|
|
|
24
23
|
* override (project API, settings form, page hint) reads it through {@link storybookUrlTtlOverrideError}.
|
|
25
24
|
*/
|
|
26
25
|
declare const MIN_STORYBOOK_URL_TTL_OVERRIDE: number;
|
|
26
|
+
/**
|
|
27
|
+
* What a token was minted for. `capture` marks the tokens the server mints for a render pass or
|
|
28
|
+
* slice (`storybookUrlSource`); it is the only claim that may opt into browser-private caching of
|
|
29
|
+
* the build's assets. Interactive preview grants and legacy tokens carry none.
|
|
30
|
+
*/
|
|
31
|
+
type StorybookTokenPurpose = "capture";
|
|
27
32
|
/** What a valid token grants: read-only access to one build's static files until `expiresAt`. */
|
|
28
33
|
interface StorybookTokenClaims {
|
|
29
34
|
projectId: string;
|
|
30
35
|
buildId: string;
|
|
31
36
|
/** Unix seconds. */
|
|
32
37
|
expiresAt: number;
|
|
38
|
+
/** Set only on tokens minted for a render pass; absent on previews and legacy tokens. */
|
|
39
|
+
purpose?: StorybookTokenPurpose;
|
|
33
40
|
}
|
|
34
41
|
/** Inputs for {@link signStorybookToken}. */
|
|
35
42
|
interface SignStorybookTokenInput {
|
|
@@ -39,6 +46,8 @@ interface SignStorybookTokenInput {
|
|
|
39
46
|
buildId: string;
|
|
40
47
|
/** Lifetime in seconds; defaults to {@link DEFAULT_STORYBOOK_URL_TTL}. See {@link storybookUrlTtl}. */
|
|
41
48
|
ttlSeconds?: number | undefined;
|
|
49
|
+
/** What the token is for; omit for an interactive preview. Signed into the token. */
|
|
50
|
+
purpose?: StorybookTokenPurpose | undefined;
|
|
42
51
|
/** Clock override for tests (ms). */
|
|
43
52
|
now?: number | undefined;
|
|
44
53
|
}
|
|
@@ -76,6 +85,5 @@ declare function storybookUrlTtl(input: {
|
|
|
76
85
|
declare function storybookUrlTtlOverrideError(seconds: number): string | undefined;
|
|
77
86
|
/** Throws unless `seconds` is an integer within the allowed lifetime range. */
|
|
78
87
|
declare function assertStorybookUrlTtl(seconds: number): void;
|
|
79
|
-
|
|
80
|
-
export { DEFAULT_STORYBOOK_URL_TTL, MAX_STORYBOOK_URL_TTL, MIN_CAPTURES_PER_SLICE, MIN_STORYBOOK_URL_TTL, MIN_STORYBOOK_URL_TTL_OVERRIDE, PER_CAPTURE_BUDGET_MS, STORYBOOK_TOKEN_PREFIX, STORYBOOK_TOKEN_SOURCE, STORYBOOK_URL_TTL_GRACE, SignStorybookTokenInput, StorybookTokenClaims, StorybookTokenFailure, StorybookTokenResult, assertStorybookUrlTtl, signStorybookToken, storybookTokenPath, storybookUrlTtl, storybookUrlTtlOverrideError, verifyStorybookToken };
|
|
88
|
+
export { DEFAULT_STORYBOOK_URL_TTL, MAX_STORYBOOK_URL_TTL, MIN_CAPTURES_PER_SLICE, MIN_STORYBOOK_URL_TTL, MIN_STORYBOOK_URL_TTL_OVERRIDE, PER_CAPTURE_BUDGET_MS, STORYBOOK_TOKEN_PREFIX, STORYBOOK_TOKEN_SOURCE, STORYBOOK_URL_TTL_GRACE, SignStorybookTokenInput, StorybookTokenClaims, StorybookTokenFailure, StorybookTokenPurpose, StorybookTokenResult, assertStorybookUrlTtl, signStorybookToken, storybookTokenPath, storybookUrlTtl, storybookUrlTtlOverrideError, verifyStorybookToken };
|
|
81
89
|
//# sourceMappingURL=storybook-token.d.mts.map
|
|
@@ -3,13 +3,14 @@ import "node:url";
|
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { timingSafeEqualString } from "./hash.mjs";
|
|
5
5
|
import { createHmac } from "node:crypto";
|
|
6
|
-
|
|
6
|
+
// src/utils/storybook-token.ts
|
|
7
7
|
/**
|
|
8
8
|
* Signed Storybook tokens (ADR 0007, decision 5): the single owner of everything about the path
|
|
9
9
|
* prefix of the URL a remote browser opens to read one build's published Storybook, `/_s/<token>/`:
|
|
10
10
|
* the prefix, the token shape, the derived signing key, signing, verification and the lifetime
|
|
11
|
-
* rules. Stateless: `base64url("<projectId>:<buildId>:<exp>")`, a dot, and the base64url
|
|
12
|
-
* HMAC-SHA-256 of that first part.
|
|
11
|
+
* rules. Stateless: `base64url("<projectId>:<buildId>:<exp>[:<purpose>]")`, a dot, and the base64url
|
|
12
|
+
* HMAC-SHA-256 of that first part. The optional purpose is covered by the signature; a token
|
|
13
|
+
* without one is a legacy or interactive-preview grant. Imported through `@storyshelf/core/utils`.
|
|
13
14
|
*/
|
|
14
15
|
/** The path prefix every signed Storybook URL starts with; `/_/` is the unrelated short link. */
|
|
15
16
|
const STORYBOOK_TOKEN_PREFIX = "/_s/";
|
|
@@ -36,14 +37,14 @@ const MIN_STORYBOOK_URL_TTL_OVERRIDE = Math.ceil(50 * PER_CAPTURE_BUDGET_MS / 1e
|
|
|
36
37
|
/** Domain separation: the signing key is derived from the secret with this context, nothing else uses it. */
|
|
37
38
|
const KEY_CONTEXT = "storyshelf/storybook-url/v1";
|
|
38
39
|
const TOKEN_SHAPE = new RegExp(`^${STORYBOOK_TOKEN_SOURCE}$`, "u");
|
|
39
|
-
const CLAIMS_SHAPE = /^([A-Za-z0-9_-]{1,64}):([A-Za-z0-9_-]{1,64}):(\d{1,12})
|
|
40
|
+
const CLAIMS_SHAPE = /^([A-Za-z0-9_-]{1,64}):([A-Za-z0-9_-]{1,64}):(\d{1,12})(?::(capture))?$/u;
|
|
40
41
|
/** Sign a token for one build. Throws on an empty secret, bad ids or a lifetime out of range. */
|
|
41
42
|
function signStorybookToken(input) {
|
|
42
43
|
const ttl = input.ttlSeconds ?? 7200;
|
|
43
44
|
assertStorybookUrlTtl(ttl);
|
|
44
45
|
const secret = currentSecret(input.secret);
|
|
45
46
|
const expiresAt = Math.floor((input.now ?? Date.now()) / 1e3) + ttl;
|
|
46
|
-
const claims = `${input.projectId}:${input.buildId}:${expiresAt}`;
|
|
47
|
+
const claims = `${input.projectId}:${input.buildId}:${expiresAt}${input.purpose === void 0 ? "" : `:${input.purpose}`}`;
|
|
47
48
|
if (!CLAIMS_SHAPE.test(claims)) throw new Error("Cannot sign a Storybook URL: unexpected project or build id");
|
|
48
49
|
const payload = Buffer.from(claims, "utf8").toString("base64url");
|
|
49
50
|
return `${payload}.${signature(secret, payload)}`;
|
|
@@ -82,7 +83,7 @@ function storybookUrlTtl(input) {
|
|
|
82
83
|
}
|
|
83
84
|
/** The reason a project's lifetime override is refused, or `undefined` when it is acceptable. */
|
|
84
85
|
function storybookUrlTtlOverrideError(seconds) {
|
|
85
|
-
if (Number.isInteger(seconds) && seconds >=
|
|
86
|
+
if (Number.isInteger(seconds) && seconds >= 1050 && seconds <= 86400) return;
|
|
86
87
|
return `The signed URL lifetime is a whole number of seconds from ${MIN_STORYBOOK_URL_TTL_OVERRIDE} (one slice of 50 captures plus the 300 s grace) to ${MAX_STORYBOOK_URL_TTL}`;
|
|
87
88
|
}
|
|
88
89
|
/** Throws unless `seconds` is an integer within the allowed lifetime range. */
|
|
@@ -95,7 +96,7 @@ function claimsOf(payload, nowSeconds) {
|
|
|
95
96
|
ok: false,
|
|
96
97
|
reason: "malformed"
|
|
97
98
|
};
|
|
98
|
-
const [, projectId = "", buildId = "", exp = ""] = match;
|
|
99
|
+
const [, projectId = "", buildId = "", exp = "", purpose] = match;
|
|
99
100
|
const expiresAt = Number(exp);
|
|
100
101
|
if (expiresAt <= nowSeconds) return {
|
|
101
102
|
ok: false,
|
|
@@ -110,7 +111,8 @@ function claimsOf(payload, nowSeconds) {
|
|
|
110
111
|
claims: {
|
|
111
112
|
projectId,
|
|
112
113
|
buildId,
|
|
113
|
-
expiresAt
|
|
114
|
+
expiresAt,
|
|
115
|
+
...purpose === "capture" ? { purpose } : {}
|
|
114
116
|
}
|
|
115
117
|
};
|
|
116
118
|
}
|
|
@@ -128,7 +130,6 @@ function signature(secret, payload) {
|
|
|
128
130
|
const key = createHmac("sha256", secret).update(KEY_CONTEXT).digest();
|
|
129
131
|
return createHmac("sha256", key).update(payload).digest("base64url");
|
|
130
132
|
}
|
|
131
|
-
//#endregion
|
|
132
133
|
export { DEFAULT_STORYBOOK_URL_TTL, MAX_STORYBOOK_URL_TTL, MIN_CAPTURES_PER_SLICE, MIN_STORYBOOK_URL_TTL, MIN_STORYBOOK_URL_TTL_OVERRIDE, PER_CAPTURE_BUDGET_MS, STORYBOOK_TOKEN_PREFIX, STORYBOOK_TOKEN_SOURCE, STORYBOOK_URL_TTL_GRACE, assertStorybookUrlTtl, signStorybookToken, storybookTokenPath, storybookUrlTtl, storybookUrlTtlOverrideError, verifyStorybookToken };
|
|
133
134
|
|
|
134
135
|
//# sourceMappingURL=storybook-token.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storybook-token.mjs","names":[],"sources":["../../src/utils/storybook-token.ts"],"sourcesContent":["/**\n * Signed Storybook tokens (ADR 0007, decision 5): the single owner of everything about the path\n * prefix of the URL a remote browser opens to read one build's published Storybook, `/_s/<token>/`:\n * the prefix, the token shape, the derived signing key, signing, verification and the lifetime\n * rules. Stateless: `base64url(\"<projectId>:<buildId>:<exp>\")`, a dot, and the base64url\n * HMAC-SHA-256 of that first part. Imported through `@storyshelf/core/utils`.\n */\nimport { createHmac } from \"node:crypto\";\nimport type { SecretInput } from \"./encrypt.ts\";\nimport { timingSafeEqualString } from \"./hash.ts\";\n\n/** The path prefix every signed Storybook URL starts with; `/_/` is the unrelated short link. */\nexport const STORYBOOK_TOKEN_PREFIX = \"/_s/\";\n/** Regex source of a token, shared by the shape check and the log redaction. */\nexport const STORYBOOK_TOKEN_SOURCE = \"[A-Za-z0-9_-]{8,200}\\\\.[A-Za-z0-9_-]{43}\";\n\n/** Floor of a derived lifetime, in seconds (2 hours). */\nexport const DEFAULT_STORYBOOK_URL_TTL = 7200;\n/** Shortest lifetime a token may be minted with (a render needs at least this). */\nexport const MIN_STORYBOOK_URL_TTL = 60;\n/** Ceiling of a derived lifetime and the longest accepted: a token claiming more is refused, whoever signed it. */\nexport const MAX_STORYBOOK_URL_TTL = 86_400;\n/** Slack added to a render budget when deriving a lifetime (queue wait, retries, clock skew). */\nexport const STORYBOOK_URL_TTL_GRACE = 300;\n\n/** Extra time granted per capture of a render pass, in ms; sizes a pass's budget and its slices. */\nexport const PER_CAPTURE_BUDGET_MS = 15_000;\n/** Smallest slice (captures), so a very short project override never renders a page per call. */\nexport const MIN_CAPTURES_PER_SLICE = 50;\n/**\n * Shortest lifetime a project may set as its override (seconds): one minimum slice's budget plus\n * the grace, so a slice never outlives its token. Single owner of the rule; every parser of the\n * override (project API, settings form, page hint) reads it through {@link storybookUrlTtlOverrideError}.\n */\nexport const MIN_STORYBOOK_URL_TTL_OVERRIDE =\n Math.ceil((MIN_CAPTURES_PER_SLICE * PER_CAPTURE_BUDGET_MS) / 1000) + STORYBOOK_URL_TTL_GRACE;\n\n/** Domain separation: the signing key is derived from the secret with this context, nothing else uses it. */\nconst KEY_CONTEXT = \"storyshelf/storybook-url/v1\";\nconst TOKEN_SHAPE = new RegExp(`^${STORYBOOK_TOKEN_SOURCE}$`, \"u\");\nconst CLAIMS_SHAPE = /^([A-Za-z0-9_-]{1,64}):([A-Za-z0-9_-]{1,64}):(\\d{1,12})$/u;\n\n/** What a valid token grants: read-only access to one build's static files until `expiresAt`. */\nexport interface StorybookTokenClaims {\n projectId: string;\n buildId: string;\n /** Unix seconds. */\n expiresAt: number;\n}\n\n/** Inputs for {@link signStorybookToken}. */\nexport interface SignStorybookTokenInput {\n /** The server secret (a rotation pair signs with its current key); throws when empty. */\n secret: SecretInput;\n projectId: string;\n buildId: string;\n /** Lifetime in seconds; defaults to {@link DEFAULT_STORYBOOK_URL_TTL}. See {@link storybookUrlTtl}. */\n ttlSeconds?: number | undefined;\n /** Clock override for tests (ms). */\n now?: number | undefined;\n}\n\n/** Why a token was refused (kept apart from the response, which is the same for all). */\nexport type StorybookTokenFailure = \"malformed\" | \"signature\" | \"expired\" | \"scope\";\n\n/** The outcome of {@link verifyStorybookToken}. */\nexport type StorybookTokenResult =\n | { ok: true; claims: StorybookTokenClaims }\n | { ok: false; reason: StorybookTokenFailure };\n\n/** Sign a token for one build. Throws on an empty secret, bad ids or a lifetime out of range. */\nexport function signStorybookToken(input: SignStorybookTokenInput): string {\n const ttl = input.ttlSeconds ?? DEFAULT_STORYBOOK_URL_TTL;\n assertStorybookUrlTtl(ttl);\n const secret = currentSecret(input.secret);\n const expiresAt = Math.floor((input.now ?? Date.now()) / 1000) + ttl;\n const claims = `${input.projectId}:${input.buildId}:${expiresAt}`;\n if (!CLAIMS_SHAPE.test(claims)) {\n throw new Error(\"Cannot sign a Storybook URL: unexpected project or build id\");\n }\n const payload = Buffer.from(claims, \"utf8\").toString(\"base64url\");\n return `${payload}.${signature(secret, payload)}`;\n}\n\n/**\n * Check a token against the current secret and, during a rotation, the previous one. The signature\n * is checked (in constant time) before the payload is read, and every failure looks the same to\n * the caller except for `reason`, which is for the server's own log.\n */\nexport function verifyStorybookToken(\n token: unknown,\n secret: SecretInput,\n now: number = Date.now(),\n): StorybookTokenResult {\n if (typeof token !== \"string\" || !TOKEN_SHAPE.test(token)) {\n return { ok: false, reason: \"malformed\" };\n }\n const [payload = \"\", given = \"\"] = token.split(\".\");\n const signed = secretsOf(secret).some((key) =>\n timingSafeEqualString(signature(key, payload), given),\n );\n if (!signed) {\n return { ok: false, reason: \"signature\" };\n }\n return claimsOf(payload, Math.floor(now / 1000));\n}\n\n/** The path of a token's root: `/_s/<token>/`. */\nexport function storybookTokenPath(token: string): string {\n return `${STORYBOOK_TOKEN_PREFIX}${token}/`;\n}\n\n/**\n * The lifetime of a render's tokens: the project's override when it has one, otherwise the render\n * budget (seconds) plus {@link STORYBOOK_URL_TTL_GRACE}, never below {@link DEFAULT_STORYBOOK_URL_TTL}\n * and never above {@link MAX_STORYBOOK_URL_TTL}. A pass cannot outlive its budget, so a token\n * derived from it lasts as long as the pass that carries it.\n */\nexport function storybookUrlTtl(input: {\n override?: number | null | undefined;\n budgetMs: number;\n}): number {\n if (input.override !== undefined && input.override !== null) {\n return input.override;\n }\n const derived = Math.ceil(input.budgetMs / 1000) + STORYBOOK_URL_TTL_GRACE;\n return Math.min(MAX_STORYBOOK_URL_TTL, Math.max(DEFAULT_STORYBOOK_URL_TTL, derived));\n}\n\n/** The reason a project's lifetime override is refused, or `undefined` when it is acceptable. */\nexport function storybookUrlTtlOverrideError(seconds: number): string | undefined {\n if (\n Number.isInteger(seconds) &&\n seconds >= MIN_STORYBOOK_URL_TTL_OVERRIDE &&\n seconds <= MAX_STORYBOOK_URL_TTL\n ) {\n return undefined;\n }\n return `The signed URL lifetime is a whole number of seconds from ${MIN_STORYBOOK_URL_TTL_OVERRIDE} (one slice of ${MIN_CAPTURES_PER_SLICE} captures plus the ${STORYBOOK_URL_TTL_GRACE} s grace) to ${MAX_STORYBOOK_URL_TTL}`;\n}\n\n/** Throws unless `seconds` is an integer within the allowed lifetime range. */\nexport function assertStorybookUrlTtl(seconds: number): void {\n if (\n !Number.isInteger(seconds) ||\n seconds < MIN_STORYBOOK_URL_TTL ||\n seconds > MAX_STORYBOOK_URL_TTL\n ) {\n throw new Error(\n `A Storybook URL lifetime must be a whole number of seconds from ${MIN_STORYBOOK_URL_TTL} to ${MAX_STORYBOOK_URL_TTL}, got ${String(seconds)}`,\n );\n }\n}\n\nfunction claimsOf(payload: string, nowSeconds: number): StorybookTokenResult {\n const match = CLAIMS_SHAPE.exec(Buffer.from(payload, \"base64url\").toString(\"utf8\"));\n if (!match) {\n return { ok: false, reason: \"malformed\" };\n }\n const [, projectId = \"\", buildId = \"\", exp = \"\"] = match;\n const expiresAt = Number(exp);\n if (expiresAt <= nowSeconds) {\n return { ok: false, reason: \"expired\" };\n }\n if (expiresAt > nowSeconds + MAX_STORYBOOK_URL_TTL) {\n return { ok: false, reason: \"scope\" };\n }\n return { ok: true, claims: { projectId, buildId, expiresAt } };\n}\n\n/** The key tokens are signed with; throws when none is configured. */\nfunction currentSecret(secret: SecretInput): string {\n const current = typeof secret === \"object\" ? secret.current : secret;\n if (!current) {\n throw new Error(\"Cannot sign a Storybook URL: ShelfConfig.secret is not configured\");\n }\n return current;\n}\n\n/** Every key a token may verify with: the current secret and, during a rotation, the previous one. */\nfunction secretsOf(secret: SecretInput): string[] {\n const keys = typeof secret === \"object\" ? [secret.current, secret.previous] : [secret];\n return keys.filter((key): key is string => key !== undefined && key !== \"\");\n}\n\nfunction signature(secret: string, payload: string): string {\n const key = createHmac(\"sha256\", secret).update(KEY_CONTEXT).digest();\n return createHmac(\"sha256\", key).update(payload).digest(\"base64url\");\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"storybook-token.mjs","names":[],"sources":["../../src/utils/storybook-token.ts"],"sourcesContent":["/**\n * Signed Storybook tokens (ADR 0007, decision 5): the single owner of everything about the path\n * prefix of the URL a remote browser opens to read one build's published Storybook, `/_s/<token>/`:\n * the prefix, the token shape, the derived signing key, signing, verification and the lifetime\n * rules. Stateless: `base64url(\"<projectId>:<buildId>:<exp>[:<purpose>]\")`, a dot, and the base64url\n * HMAC-SHA-256 of that first part. The optional purpose is covered by the signature; a token\n * without one is a legacy or interactive-preview grant. Imported through `@storyshelf/core/utils`.\n */\nimport { createHmac } from \"node:crypto\";\nimport type { SecretInput } from \"./encrypt.ts\";\nimport { timingSafeEqualString } from \"./hash.ts\";\n\n/** The path prefix every signed Storybook URL starts with; `/_/` is the unrelated short link. */\nexport const STORYBOOK_TOKEN_PREFIX = \"/_s/\";\n/** Regex source of a token, shared by the shape check and the log redaction. */\nexport const STORYBOOK_TOKEN_SOURCE = \"[A-Za-z0-9_-]{8,200}\\\\.[A-Za-z0-9_-]{43}\";\n\n/** Floor of a derived lifetime, in seconds (2 hours). */\nexport const DEFAULT_STORYBOOK_URL_TTL = 7200;\n/** Shortest lifetime a token may be minted with (a render needs at least this). */\nexport const MIN_STORYBOOK_URL_TTL = 60;\n/** Ceiling of a derived lifetime and the longest accepted: a token claiming more is refused, whoever signed it. */\nexport const MAX_STORYBOOK_URL_TTL = 86_400;\n/** Slack added to a render budget when deriving a lifetime (queue wait, retries, clock skew). */\nexport const STORYBOOK_URL_TTL_GRACE = 300;\n\n/** Extra time granted per capture of a render pass, in ms; sizes a pass's budget and its slices. */\nexport const PER_CAPTURE_BUDGET_MS = 15_000;\n/** Smallest slice (captures), so a very short project override never renders a page per call. */\nexport const MIN_CAPTURES_PER_SLICE = 50;\n/**\n * Shortest lifetime a project may set as its override (seconds): one minimum slice's budget plus\n * the grace, so a slice never outlives its token. Single owner of the rule; every parser of the\n * override (project API, settings form, page hint) reads it through {@link storybookUrlTtlOverrideError}.\n */\nexport const MIN_STORYBOOK_URL_TTL_OVERRIDE =\n Math.ceil((MIN_CAPTURES_PER_SLICE * PER_CAPTURE_BUDGET_MS) / 1000) + STORYBOOK_URL_TTL_GRACE;\n\n/** Domain separation: the signing key is derived from the secret with this context, nothing else uses it. */\nconst KEY_CONTEXT = \"storyshelf/storybook-url/v1\";\nconst TOKEN_SHAPE = new RegExp(`^${STORYBOOK_TOKEN_SOURCE}$`, \"u\");\nconst CLAIMS_SHAPE = /^([A-Za-z0-9_-]{1,64}):([A-Za-z0-9_-]{1,64}):(\\d{1,12})(?::(capture))?$/u;\n\n/**\n * What a token was minted for. `capture` marks the tokens the server mints for a render pass or\n * slice (`storybookUrlSource`); it is the only claim that may opt into browser-private caching of\n * the build's assets. Interactive preview grants and legacy tokens carry none.\n */\nexport type StorybookTokenPurpose = \"capture\";\n\n/** What a valid token grants: read-only access to one build's static files until `expiresAt`. */\nexport interface StorybookTokenClaims {\n projectId: string;\n buildId: string;\n /** Unix seconds. */\n expiresAt: number;\n /** Set only on tokens minted for a render pass; absent on previews and legacy tokens. */\n purpose?: StorybookTokenPurpose;\n}\n\n/** Inputs for {@link signStorybookToken}. */\nexport interface SignStorybookTokenInput {\n /** The server secret (a rotation pair signs with its current key); throws when empty. */\n secret: SecretInput;\n projectId: string;\n buildId: string;\n /** Lifetime in seconds; defaults to {@link DEFAULT_STORYBOOK_URL_TTL}. See {@link storybookUrlTtl}. */\n ttlSeconds?: number | undefined;\n /** What the token is for; omit for an interactive preview. Signed into the token. */\n purpose?: StorybookTokenPurpose | undefined;\n /** Clock override for tests (ms). */\n now?: number | undefined;\n}\n\n/** Why a token was refused (kept apart from the response, which is the same for all). */\nexport type StorybookTokenFailure = \"malformed\" | \"signature\" | \"expired\" | \"scope\";\n\n/** The outcome of {@link verifyStorybookToken}. */\nexport type StorybookTokenResult =\n | { ok: true; claims: StorybookTokenClaims }\n | { ok: false; reason: StorybookTokenFailure };\n\n/** Sign a token for one build. Throws on an empty secret, bad ids or a lifetime out of range. */\nexport function signStorybookToken(input: SignStorybookTokenInput): string {\n const ttl = input.ttlSeconds ?? DEFAULT_STORYBOOK_URL_TTL;\n assertStorybookUrlTtl(ttl);\n const secret = currentSecret(input.secret);\n const expiresAt = Math.floor((input.now ?? Date.now()) / 1000) + ttl;\n const claims = `${input.projectId}:${input.buildId}:${expiresAt}${\n input.purpose === undefined ? \"\" : `:${input.purpose}`\n }`;\n if (!CLAIMS_SHAPE.test(claims)) {\n throw new Error(\"Cannot sign a Storybook URL: unexpected project or build id\");\n }\n const payload = Buffer.from(claims, \"utf8\").toString(\"base64url\");\n return `${payload}.${signature(secret, payload)}`;\n}\n\n/**\n * Check a token against the current secret and, during a rotation, the previous one. The signature\n * is checked (in constant time) before the payload is read, and every failure looks the same to\n * the caller except for `reason`, which is for the server's own log.\n */\nexport function verifyStorybookToken(\n token: unknown,\n secret: SecretInput,\n now: number = Date.now(),\n): StorybookTokenResult {\n if (typeof token !== \"string\" || !TOKEN_SHAPE.test(token)) {\n return { ok: false, reason: \"malformed\" };\n }\n const [payload = \"\", given = \"\"] = token.split(\".\");\n const signed = secretsOf(secret).some((key) =>\n timingSafeEqualString(signature(key, payload), given),\n );\n if (!signed) {\n return { ok: false, reason: \"signature\" };\n }\n return claimsOf(payload, Math.floor(now / 1000));\n}\n\n/** The path of a token's root: `/_s/<token>/`. */\nexport function storybookTokenPath(token: string): string {\n return `${STORYBOOK_TOKEN_PREFIX}${token}/`;\n}\n\n/**\n * The lifetime of a render's tokens: the project's override when it has one, otherwise the render\n * budget (seconds) plus {@link STORYBOOK_URL_TTL_GRACE}, never below {@link DEFAULT_STORYBOOK_URL_TTL}\n * and never above {@link MAX_STORYBOOK_URL_TTL}. A pass cannot outlive its budget, so a token\n * derived from it lasts as long as the pass that carries it.\n */\nexport function storybookUrlTtl(input: {\n override?: number | null | undefined;\n budgetMs: number;\n}): number {\n if (input.override !== undefined && input.override !== null) {\n return input.override;\n }\n const derived = Math.ceil(input.budgetMs / 1000) + STORYBOOK_URL_TTL_GRACE;\n return Math.min(MAX_STORYBOOK_URL_TTL, Math.max(DEFAULT_STORYBOOK_URL_TTL, derived));\n}\n\n/** The reason a project's lifetime override is refused, or `undefined` when it is acceptable. */\nexport function storybookUrlTtlOverrideError(seconds: number): string | undefined {\n if (\n Number.isInteger(seconds) &&\n seconds >= MIN_STORYBOOK_URL_TTL_OVERRIDE &&\n seconds <= MAX_STORYBOOK_URL_TTL\n ) {\n return undefined;\n }\n return `The signed URL lifetime is a whole number of seconds from ${MIN_STORYBOOK_URL_TTL_OVERRIDE} (one slice of ${MIN_CAPTURES_PER_SLICE} captures plus the ${STORYBOOK_URL_TTL_GRACE} s grace) to ${MAX_STORYBOOK_URL_TTL}`;\n}\n\n/** Throws unless `seconds` is an integer within the allowed lifetime range. */\nexport function assertStorybookUrlTtl(seconds: number): void {\n if (\n !Number.isInteger(seconds) ||\n seconds < MIN_STORYBOOK_URL_TTL ||\n seconds > MAX_STORYBOOK_URL_TTL\n ) {\n throw new Error(\n `A Storybook URL lifetime must be a whole number of seconds from ${MIN_STORYBOOK_URL_TTL} to ${MAX_STORYBOOK_URL_TTL}, got ${String(seconds)}`,\n );\n }\n}\n\nfunction claimsOf(payload: string, nowSeconds: number): StorybookTokenResult {\n const match = CLAIMS_SHAPE.exec(Buffer.from(payload, \"base64url\").toString(\"utf8\"));\n if (!match) {\n return { ok: false, reason: \"malformed\" };\n }\n const [, projectId = \"\", buildId = \"\", exp = \"\", purpose] = match;\n const expiresAt = Number(exp);\n if (expiresAt <= nowSeconds) {\n return { ok: false, reason: \"expired\" };\n }\n if (expiresAt > nowSeconds + MAX_STORYBOOK_URL_TTL) {\n return { ok: false, reason: \"scope\" };\n }\n return {\n ok: true,\n claims: { projectId, buildId, expiresAt, ...(purpose === \"capture\" ? { purpose } : {}) },\n };\n}\n\n/** The key tokens are signed with; throws when none is configured. */\nfunction currentSecret(secret: SecretInput): string {\n const current = typeof secret === \"object\" ? secret.current : secret;\n if (!current) {\n throw new Error(\"Cannot sign a Storybook URL: ShelfConfig.secret is not configured\");\n }\n return current;\n}\n\n/** Every key a token may verify with: the current secret and, during a rotation, the previous one. */\nfunction secretsOf(secret: SecretInput): string[] {\n const keys = typeof secret === \"object\" ? [secret.current, secret.previous] : [secret];\n return keys.filter((key): key is string => key !== undefined && key !== \"\");\n}\n\nfunction signature(secret: string, payload: string): string {\n const key = createHmac(\"sha256\", secret).update(KEY_CONTEXT).digest();\n return createHmac(\"sha256\", key).update(payload).digest(\"base64url\");\n}\n"],"mappings":"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"}
|
|
@@ -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/utils/storybook-url.d.ts
|
|
4
3
|
/** The Storybook URL a runner opens for a `reach: "url"` target (ADR 0007, decision 5). */
|
|
5
4
|
/**
|
|
6
5
|
* The root of a build's Storybook from the URL core hands a runner, without its trailing slash. The
|
|
@@ -10,6 +9,5 @@ import __tsdown_shims_url from 'node:url';
|
|
|
10
9
|
* quoted in the error.
|
|
11
10
|
*/
|
|
12
11
|
declare function storybookBase(storybookUrl: string): string;
|
|
13
|
-
//#endregion
|
|
14
12
|
export { storybookBase };
|
|
15
13
|
//# sourceMappingURL=storybook-url.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/utils/storybook-url.ts
|
|
5
5
|
/** The Storybook URL a runner opens for a `reach: "url"` target (ADR 0007, decision 5). */
|
|
6
6
|
/**
|
|
7
7
|
* The root of a build's Storybook from the URL core hands a runner, without its trailing slash. The
|
|
@@ -15,7 +15,6 @@ function storybookBase(storybookUrl) {
|
|
|
15
15
|
if (url.search !== "" || url.hash !== "" || !url.pathname.endsWith("/")) throw new Error("The Storybook URL must be the build's directory root (ending in a slash, no query or fragment)");
|
|
16
16
|
return `${url.origin}${url.pathname.replace(/\/+$/u, "")}`;
|
|
17
17
|
}
|
|
18
|
-
//#endregion
|
|
19
18
|
export { storybookBase };
|
|
20
19
|
|
|
21
20
|
//# sourceMappingURL=storybook-url.mjs.map
|
package/dist/utils/timing.d.mts
CHANGED
|
@@ -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/utils/timing.d.ts
|
|
4
3
|
/** Aggregated durations for one scope (family/phase name → total ms). */
|
|
5
4
|
type TimingMap = Record<string, number>;
|
|
6
5
|
/** Run `fn` with a fresh timing scope, returning its result and aggregates. */
|
|
@@ -14,6 +13,5 @@ declare function addTiming(name: string, durationMs: number): void;
|
|
|
14
13
|
declare function timed<T>(name: string, fn: () => T | Promise<T>): Promise<T>;
|
|
15
14
|
/** Copy of aggregates with whole-millisecond values for logs and headers. */
|
|
16
15
|
declare function roundedTimings(timings: TimingMap): TimingMap;
|
|
17
|
-
//#endregion
|
|
18
16
|
export { TimingMap, addTiming, roundedTimings, runWithTimings, timed };
|
|
19
17
|
//# sourceMappingURL=timing.d.mts.map
|
package/dist/utils/timing.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import "node:path";
|
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
|
-
|
|
5
|
+
// src/utils/timing.ts
|
|
6
6
|
/**
|
|
7
7
|
* Per-scope timing recorder: aggregate millisecond sums under shared names.
|
|
8
8
|
*
|
|
@@ -40,7 +40,6 @@ async function timed(name, fn) {
|
|
|
40
40
|
function roundedTimings(timings) {
|
|
41
41
|
return Object.fromEntries(Object.entries(timings).map(([name, ms]) => [name, Math.round(ms)]));
|
|
42
42
|
}
|
|
43
|
-
//#endregion
|
|
44
43
|
export { addTiming, roundedTimings, runWithTimings, timed };
|
|
45
44
|
|
|
46
45
|
//# sourceMappingURL=timing.mjs.map
|
package/dist/utils/ulid.d.mts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
|
-
//#region src/utils/ulid.d.ts
|
|
4
3
|
/** Generate a sortable, collision-resistant ULID. */
|
|
5
4
|
declare function ulid(): string;
|
|
6
5
|
/** Convert a name to a URL-safe slug of at most 63 characters. */
|
|
7
6
|
declare function slugify(value: string): string;
|
|
8
|
-
//#endregion
|
|
9
7
|
export { slugify, ulid };
|
|
10
8
|
//# sourceMappingURL=ulid.d.mts.map
|
package/dist/utils/ulid.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import "node:path";
|
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { randomBytes } from "node:crypto";
|
|
5
|
-
|
|
5
|
+
// src/utils/ulid.ts
|
|
6
6
|
/** Generate a sortable, collision-resistant ULID. */
|
|
7
7
|
function ulid() {
|
|
8
8
|
return encodeTime(Date.now()) + encodeRandom(randomBytes(10));
|
|
@@ -37,7 +37,6 @@ function encodeRandom(bytes) {
|
|
|
37
37
|
if (bits > 0) out += CROCKFORD.charAt(buffer << 5 - bits & 31);
|
|
38
38
|
return out;
|
|
39
39
|
}
|
|
40
|
-
//#endregion
|
|
41
40
|
export { slugify, ulid };
|
|
42
41
|
|
|
43
42
|
//# sourceMappingURL=ulid.mjs.map
|
package/dist/utils/zip-read.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import "node:url";
|
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import "node:buffer";
|
|
5
5
|
import { crc32, inflateRawSync } from "node:zlib";
|
|
6
|
-
|
|
6
|
+
// src/utils/zip-read.ts
|
|
7
7
|
/** A malformed, unsafe, or over-limit archive. */
|
|
8
8
|
var ZipFormatError = class extends Error {
|
|
9
9
|
name = "ZipFormatError";
|
|
@@ -123,7 +123,6 @@ function inflate(raw, record) {
|
|
|
123
123
|
return fail(`Zip entry failed to inflate: ${record.name}`);
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
|
-
//#endregion
|
|
127
126
|
export { ZipFormatError, isUnsafeEntryName, readZip };
|
|
128
127
|
|
|
129
128
|
//# sourceMappingURL=zip-read.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 { Buffer } from "node:buffer";
|
|
4
|
-
//#region src/utils/zip-write.d.ts
|
|
5
4
|
/** One file to add to an archive; a `data` function is called lazily when the entry is written. */
|
|
6
5
|
interface ZipSource {
|
|
7
6
|
name: string;
|
|
@@ -13,6 +12,5 @@ interface ZipSource {
|
|
|
13
12
|
* @throws When the archive would need zip64 (more than 65535 entries or 4 GiB).
|
|
14
13
|
*/
|
|
15
14
|
declare function zipStream(sources: Iterable<ZipSource> | AsyncIterable<ZipSource>): AsyncGenerator<Buffer>;
|
|
16
|
-
//#endregion
|
|
17
15
|
export { ZipSource, zipStream };
|
|
18
16
|
//# sourceMappingURL=zip-write.d.mts.map
|
package/dist/utils/zip-write.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import "node:url";
|
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { Buffer } from "node:buffer";
|
|
5
5
|
import { crc32 } from "node:zlib";
|
|
6
|
-
|
|
6
|
+
// src/utils/zip-write.ts
|
|
7
7
|
/**
|
|
8
8
|
* Minimal streaming zip writer (STORE method, no zip64).
|
|
9
9
|
*
|
|
@@ -182,7 +182,6 @@ function* trailer(central, offset) {
|
|
|
182
182
|
]
|
|
183
183
|
]);
|
|
184
184
|
}
|
|
185
|
-
//#endregion
|
|
186
185
|
export { zipStream };
|
|
187
186
|
|
|
188
187
|
//# sourceMappingURL=zip-write.mjs.map
|
package/dist/utils.d.mts
CHANGED
|
@@ -3,14 +3,14 @@ import __tsdown_shims_url from 'node:url';
|
|
|
3
3
|
import { KeyUsed, SecretInput, SecretKeys, UndecryptableCredentialError, decrypt, decryptCredential, decryptWithKey, encrypt } from "./utils/encrypt.mjs";
|
|
4
4
|
import { Env, detectPublicBaseUrl, normalizeOrigin } from "./utils/public-origin.mjs";
|
|
5
5
|
import { hmacSha256, randomToken, sha256, timingSafeEqualString } from "./utils/hash.mjs";
|
|
6
|
-
import { HttpError, HttpRequestOptions, HttpTextOptions, httpBytes, httpJson, httpText } from "./utils/http.mjs";
|
|
7
|
-
import { baselinePath, catalogPath, contentDir, contentPath, designRenderPath, diffPath, isContentPath, modeSegment, screenshotPath, storybookDir, storybookZipPath } from "./paths.mjs";
|
|
6
|
+
import { HttpError, HttpRequestOptions, HttpTextOptions, httpBytes, httpJson, httpStatus, httpText } from "./utils/http.mjs";
|
|
7
|
+
import { baselinePath, catalogPath, contentDir, contentPath, designRenderPath, diffPath, isContentPath, modeSegment, publishedGzipPath, screenshotPath, storybookDir, storybookZipPath } from "./paths.mjs";
|
|
8
8
|
import { AdaptiveConcurrency, AdaptiveOptions, Observation, isTimeoutError } from "./utils/adaptive-concurrency.mjs";
|
|
9
9
|
import { detectCaptureEnvironment, systemFontsHash } from "./utils/environment.mjs";
|
|
10
10
|
import { RetryConnectOptions, isRetryableConnectError, retryConnect } from "./utils/connect-retry.mjs";
|
|
11
|
-
import { BrowserProbe, FONT_PROBE_FAMILIES, inPageProbe, remoteEnvironment } from "./utils/remote-environment.mjs";
|
|
11
|
+
import { BrowserProbe, FONT_PROBE_FAMILIES, ProbeControl, ProbeOutcome, boundedProbe, inPageProbe, remoteEnvironment } from "./utils/remote-environment.mjs";
|
|
12
12
|
import { storybookBase } from "./utils/storybook-url.mjs";
|
|
13
|
-
import { DEFAULT_STORYBOOK_URL_TTL, MAX_STORYBOOK_URL_TTL, MIN_CAPTURES_PER_SLICE, MIN_STORYBOOK_URL_TTL, MIN_STORYBOOK_URL_TTL_OVERRIDE, PER_CAPTURE_BUDGET_MS, STORYBOOK_TOKEN_PREFIX, STORYBOOK_TOKEN_SOURCE, STORYBOOK_URL_TTL_GRACE, SignStorybookTokenInput, StorybookTokenClaims, StorybookTokenFailure, StorybookTokenResult, assertStorybookUrlTtl, signStorybookToken, storybookTokenPath, storybookUrlTtl, storybookUrlTtlOverrideError, verifyStorybookToken } from "./utils/storybook-token.mjs";
|
|
13
|
+
import { DEFAULT_STORYBOOK_URL_TTL, MAX_STORYBOOK_URL_TTL, MIN_CAPTURES_PER_SLICE, MIN_STORYBOOK_URL_TTL, MIN_STORYBOOK_URL_TTL_OVERRIDE, PER_CAPTURE_BUDGET_MS, STORYBOOK_TOKEN_PREFIX, STORYBOOK_TOKEN_SOURCE, STORYBOOK_URL_TTL_GRACE, SignStorybookTokenInput, StorybookTokenClaims, StorybookTokenFailure, StorybookTokenPurpose, StorybookTokenResult, assertStorybookUrlTtl, signStorybookToken, storybookTokenPath, storybookUrlTtl, storybookUrlTtlOverrideError, verifyStorybookToken } from "./utils/storybook-token.mjs";
|
|
14
14
|
import { SessionGate, createSessionGate, sessionGateFor } from "./utils/session-gate.mjs";
|
|
15
15
|
import { storeScreenshot } from "./utils/screenshot-store.mjs";
|
|
16
16
|
import { PageLimiter, PageLimiterOptions, createPageLimiter } from "./utils/page-limiter.mjs";
|
|
@@ -23,4 +23,4 @@ import { redactSecrets, sanitizeErrorText } from "./utils/redact.mjs";
|
|
|
23
23
|
import { TimingMap, addTiming, roundedTimings, runWithTimings, timed } from "./utils/timing.mjs";
|
|
24
24
|
import { slugify, ulid } from "./utils/ulid.mjs";
|
|
25
25
|
import { ZipSource, zipStream } from "./utils/zip-write.mjs";
|
|
26
|
-
export { AdaptiveConcurrency, type AdaptiveOptions, type BrowserProbe, type CaptureHostSummary, DEFAULT_RECYCLE_BROWSER_EVERY, DEFAULT_STORYBOOK_URL_TTL, type Env, FONT_PROBE_FAMILIES, type HostResources, HttpError, type HttpRequestOptions, type HttpTextOptions, type KeyUsed, MAX_STORYBOOK_URL_TTL, MIME_TYPES, MIN_CAPTURES_PER_SLICE, MIN_STORYBOOK_URL_TTL, MIN_STORYBOOK_URL_TTL_OVERRIDE, type Observation, PER_CAPTURE_BUDGET_MS, type PageLimiter, type PageLimiterOptions, type RetryConnectOptions, STORYBOOK_TOKEN_PREFIX, STORYBOOK_TOKEN_SOURCE, STORYBOOK_URL_TTL_GRACE, type SecretInput, type SecretKeys, type SessionGate, type SignStorybookTokenInput, type StorybookTokenClaims, type StorybookTokenFailure, type StorybookTokenResult, type TimingMap, UndecryptableCredentialError, type ZipSource, addTiming, assertStorybookUrlTtl, baselinePath, catalogPath, chunk, contentDir, contentPath, createPageLimiter, createSessionGate, decrypt, decryptCredential, decryptWithKey, describeCaptureHost, describeSizing, designRenderPath, detectCaptureEnvironment, detectPublicBaseUrl, detectResources, diffPath, encrypt, engineWeight, hmacSha256, httpBytes, httpJson, httpText, inPageProbe, isBlockedTarget, isContentPath, isPathSafe, isRetryableConnectError, isSafeSegment, isTimeoutError, mapAdaptive, mapLimit, maxPageConcurrency, mimeFor, modeSegment, normalizeOrigin, randomToken, recommendedBuildConcurrency, recommendedNavigationTimeoutMs, recommendedPageConcurrency, redactSecrets, remoteEnvironment, retryConnect, roundedTimings, runWithTimings, sanitizeErrorText, screenshotPath, sessionGateFor, sha256, signStorybookToken, slugify, storeScreenshot, storybookBase, storybookDir, storybookTokenPath, storybookUrlTtl, storybookUrlTtlOverrideError, storybookZipPath, systemFontsHash, timed, timingSafeEqualString, ulid, verifyStorybookToken, zipStream };
|
|
26
|
+
export { AdaptiveConcurrency, type AdaptiveOptions, type BrowserProbe, type CaptureHostSummary, DEFAULT_RECYCLE_BROWSER_EVERY, DEFAULT_STORYBOOK_URL_TTL, type Env, FONT_PROBE_FAMILIES, type HostResources, HttpError, type HttpRequestOptions, type HttpTextOptions, type KeyUsed, MAX_STORYBOOK_URL_TTL, MIME_TYPES, MIN_CAPTURES_PER_SLICE, MIN_STORYBOOK_URL_TTL, MIN_STORYBOOK_URL_TTL_OVERRIDE, type Observation, PER_CAPTURE_BUDGET_MS, type PageLimiter, type PageLimiterOptions, type ProbeControl, type ProbeOutcome, type RetryConnectOptions, STORYBOOK_TOKEN_PREFIX, STORYBOOK_TOKEN_SOURCE, STORYBOOK_URL_TTL_GRACE, type SecretInput, type SecretKeys, type SessionGate, type SignStorybookTokenInput, type StorybookTokenClaims, type StorybookTokenFailure, type StorybookTokenPurpose, type StorybookTokenResult, type TimingMap, UndecryptableCredentialError, type ZipSource, addTiming, assertStorybookUrlTtl, baselinePath, boundedProbe, catalogPath, chunk, contentDir, contentPath, createPageLimiter, createSessionGate, decrypt, decryptCredential, decryptWithKey, describeCaptureHost, describeSizing, designRenderPath, detectCaptureEnvironment, detectPublicBaseUrl, detectResources, diffPath, encrypt, engineWeight, hmacSha256, httpBytes, httpJson, httpStatus, httpText, inPageProbe, isBlockedTarget, isContentPath, isPathSafe, isRetryableConnectError, isSafeSegment, isTimeoutError, mapAdaptive, mapLimit, maxPageConcurrency, mimeFor, modeSegment, normalizeOrigin, publishedGzipPath, randomToken, recommendedBuildConcurrency, recommendedNavigationTimeoutMs, recommendedPageConcurrency, redactSecrets, remoteEnvironment, retryConnect, roundedTimings, runWithTimings, sanitizeErrorText, screenshotPath, sessionGateFor, sha256, signStorybookToken, slugify, storeScreenshot, storybookBase, storybookDir, storybookTokenPath, storybookUrlTtl, storybookUrlTtlOverrideError, storybookZipPath, systemFontsHash, timed, timingSafeEqualString, ulid, verifyStorybookToken, zipStream };
|