@storyshelf/core 0.6.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -32
- package/dist/adapter/capture-queue.d.mts +44 -4
- package/dist/adapter/capture-runner.d.mts +134 -12
- package/dist/adapter/capture-runner.mjs +9 -12
- package/dist/adapter/database.d.mts +89 -10
- package/dist/adapter/database.mjs +4 -3
- package/dist/adapter/database.mjs.map +1 -1
- package/dist/adapter/design.d.mts +83 -0
- package/dist/adapter/design.mjs +27 -0
- package/dist/adapter/design.mjs.map +1 -0
- package/dist/adapter/email-sender.d.mts +0 -2
- package/dist/adapter/metadata.d.mts +3 -12
- package/dist/adapter/notifier.d.mts +6 -3
- package/dist/adapter/notifier.mjs +5 -1
- package/dist/adapter/oauth.d.mts +50 -0
- package/dist/adapter/setup.d.mts +4 -7
- package/dist/adapter/setup.mjs +3 -3
- package/dist/adapter/setup.mjs.map +1 -1
- package/dist/adapter/storage.d.mts +1 -3
- package/dist/adapter/webhook-events.d.mts +0 -2
- package/dist/adapter/webhook-events.mjs +13 -14
- package/dist/adapter/webhook-events.mjs.map +1 -1
- package/dist/adapters/capture-target-offers.d.mts +26 -0
- package/dist/adapters/capture-target-offers.mjs +57 -0
- package/dist/adapters/capture-target-offers.mjs.map +1 -0
- package/dist/adapters/capture-target.d.mts +69 -0
- package/dist/adapters/capture-target.mjs +75 -0
- package/dist/adapters/capture-target.mjs.map +1 -0
- package/dist/adapters/notifier/build-comment.d.mts +22 -0
- package/dist/adapters/notifier/build-comment.mjs +23 -0
- package/dist/adapters/notifier/build-comment.mjs.map +1 -0
- package/dist/adapters/notifier/build-state.d.mts +97 -0
- package/dist/adapters/notifier/build-state.mjs +217 -0
- package/dist/adapters/notifier/build-state.mjs.map +1 -0
- package/dist/adapters/notifier/events.d.mts +4 -4
- package/dist/adapters/notifier/events.mjs +32 -20
- package/dist/adapters/notifier/events.mjs.map +1 -1
- package/dist/adapters/notifier/format.d.mts +0 -2
- package/dist/adapters/notifier/format.mjs +1 -2
- package/dist/adapters/notifier/provider.d.mts +53 -9
- package/dist/adapters/notifier/provider.mjs +11 -0
- package/dist/adapters/notifier/provider.mjs.map +1 -0
- package/dist/adapters/notifier/state.d.mts +22 -0
- package/dist/adapters/notifier/state.mjs +65 -0
- package/dist/adapters/notifier/state.mjs.map +1 -0
- package/dist/adapters/notifier/types.d.mts +51 -4
- package/dist/adapters/remote-browser.d.mts +168 -0
- package/dist/adapters/remote-browser.mjs +201 -0
- package/dist/adapters/remote-browser.mjs.map +1 -0
- package/dist/adapters/remote-lease-attempt.mjs +39 -0
- package/dist/adapters/remote-lease-attempt.mjs.map +1 -0
- package/dist/adapters/remote-lease-error.d.mts +25 -0
- package/dist/adapters/remote-lease-error.mjs +54 -0
- package/dist/adapters/remote-lease-error.mjs.map +1 -0
- package/dist/adapters/remote-lease-held.d.mts +15 -0
- package/dist/adapters/remote-lease-held.mjs +86 -0
- package/dist/adapters/remote-lease-held.mjs.map +1 -0
- package/dist/adapters/remote-lease-shutdown.d.mts +12 -0
- package/dist/adapters/remote-lease-shutdown.mjs +32 -0
- package/dist/adapters/remote-lease-shutdown.mjs.map +1 -0
- package/dist/adapters/remote-lease.d.mts +39 -0
- package/dist/adapters/remote-lease.mjs +175 -0
- package/dist/adapters/remote-lease.mjs.map +1 -0
- package/dist/adapters/remote-limiter.mjs +121 -0
- package/dist/adapters/remote-limiter.mjs.map +1 -0
- package/dist/adapters/remote-pool.d.mts +56 -0
- package/dist/adapters/remote-pool.mjs +195 -0
- package/dist/adapters/remote-pool.mjs.map +1 -0
- package/dist/adapters/render-runs.d.mts +23 -0
- package/dist/adapters/render-runs.mjs +40 -0
- package/dist/adapters/render-runs.mjs.map +1 -0
- package/dist/adapters/setup-runner.d.mts +0 -2
- package/dist/adapters/setup-runner.mjs +3 -6
- package/dist/adapters/setup-runner.mjs.map +1 -1
- package/dist/adapters/sources.d.mts +10 -0
- package/dist/adapters/sources.mjs +19 -0
- package/dist/adapters/sources.mjs.map +1 -0
- package/dist/adapters/validate.d.mts +0 -2
- package/dist/adapters/validate.mjs +27 -20
- package/dist/adapters/validate.mjs.map +1 -1
- package/dist/ai.d.mts +103 -0
- package/dist/ai.mjs +18 -0
- package/dist/ai.mjs.map +1 -0
- package/dist/auth.d.mts +0 -2
- package/dist/auth.mjs +1 -2
- package/dist/brand-packs.d.mts +0 -2
- package/dist/brand-packs.mjs +0 -1
- package/dist/capture/a11y/axe-source.d.mts +16 -0
- package/dist/capture/a11y/axe-source.mjs +36 -0
- package/dist/capture/a11y/axe-source.mjs.map +1 -0
- package/dist/capture/a11y/in-page.d.mts +25 -0
- package/dist/capture/a11y/in-page.mjs +59 -0
- package/dist/capture/a11y/in-page.mjs.map +1 -0
- package/dist/capture/a11y/map.d.mts +22 -0
- package/dist/capture/a11y/map.mjs +47 -0
- package/dist/capture/a11y/map.mjs.map +1 -0
- package/dist/capture/a11y/options.d.mts +21 -0
- package/dist/capture/a11y/options.mjs +74 -0
- package/dist/capture/a11y/options.mjs.map +1 -0
- package/dist/capture/adapter.d.mts +46 -5
- package/dist/capture/adapter.mjs +1 -2
- package/dist/capture/adapter.mjs.map +1 -1
- package/dist/capture/affected.d.mts +2 -2
- package/dist/capture/affected.mjs +14 -6
- package/dist/capture/affected.mjs.map +1 -1
- package/dist/capture/attempt-logger.mjs +83 -0
- package/dist/capture/attempt-logger.mjs.map +1 -0
- package/dist/capture/browsers.d.mts +33 -0
- package/dist/capture/browsers.mjs +66 -0
- package/dist/capture/browsers.mjs.map +1 -0
- package/dist/capture/build-state/deliver.d.mts +30 -0
- package/dist/capture/build-state/deliver.mjs +117 -0
- package/dist/capture/build-state/deliver.mjs.map +1 -0
- package/dist/capture/build-state/index.d.mts +8 -0
- package/dist/capture/build-state/notify-job.d.mts +54 -0
- package/dist/capture/build-state/notify-job.mjs +132 -0
- package/dist/capture/build-state/notify-job.mjs.map +1 -0
- package/dist/capture/build-state/publisher.d.mts +21 -0
- package/dist/capture/build-state/publisher.mjs +106 -0
- package/dist/capture/build-state/publisher.mjs.map +1 -0
- package/dist/capture/build-state/resume.d.mts +7 -0
- package/dist/capture/build-state/resume.mjs +46 -0
- package/dist/capture/build-state/resume.mjs.map +1 -0
- package/dist/capture/build-state/types.d.mts +47 -0
- package/dist/capture/catalog/catalogue.d.mts +20 -0
- package/dist/capture/catalog/catalogue.mjs +52 -0
- package/dist/capture/catalog/catalogue.mjs.map +1 -0
- package/dist/capture/catalog/components.d.mts +21 -0
- package/dist/capture/catalog/components.mjs +54 -0
- package/dist/capture/catalog/components.mjs.map +1 -0
- package/dist/capture/catalog/history.d.mts +28 -0
- package/dist/capture/catalog/history.mjs +50 -0
- package/dist/capture/catalog/history.mjs.map +1 -0
- package/dist/capture/catalog/hook.d.mts +12 -0
- package/dist/capture/catalog/hook.mjs +18 -0
- package/dist/capture/catalog/hook.mjs.map +1 -0
- package/dist/capture/catalog/index.d.mts +11 -0
- package/dist/capture/catalog/load.d.mts +24 -0
- package/dist/capture/catalog/load.mjs +48 -0
- package/dist/capture/catalog/load.mjs.map +1 -0
- package/dist/capture/catalog/parse.d.mts +13 -0
- package/dist/capture/catalog/parse.mjs +55 -0
- package/dist/capture/catalog/parse.mjs.map +1 -0
- package/dist/capture/catalog/read.d.mts +15 -0
- package/dist/capture/catalog/read.mjs +39 -0
- package/dist/capture/catalog/read.mjs.map +1 -0
- package/dist/capture/catalog/types.d.mts +31 -0
- package/dist/capture/comparison-reference.d.mts +27 -0
- package/dist/capture/comparison-reference.mjs +75 -0
- package/dist/capture/comparison-reference.mjs.map +1 -0
- package/dist/capture/consumer/capture-handler.d.mts +56 -0
- package/dist/capture/consumer/capture-handler.mjs +117 -0
- package/dist/capture/consumer/capture-handler.mjs.map +1 -0
- package/dist/capture/consumer/idle.d.mts +15 -0
- package/dist/capture/consumer/idle.mjs +24 -0
- package/dist/capture/consumer/idle.mjs.map +1 -0
- package/dist/capture/consumer/index.d.mts +11 -0
- package/dist/capture/consumer/index.mjs +138 -0
- package/dist/capture/consumer/index.mjs.map +1 -0
- package/dist/capture/consumer/loop.mjs +109 -0
- package/dist/capture/consumer/loop.mjs.map +1 -0
- package/dist/capture/consumer/metrics.d.mts +39 -0
- package/dist/capture/consumer/metrics.mjs +37 -0
- package/dist/capture/consumer/metrics.mjs.map +1 -0
- package/dist/capture/consumer/retry.d.mts +31 -0
- package/dist/capture/consumer/retry.mjs +33 -0
- package/dist/capture/consumer/retry.mjs.map +1 -0
- package/dist/capture/consumer/run-job.mjs +194 -0
- package/dist/capture/consumer/run-job.mjs.map +1 -0
- package/dist/capture/consumer/startup.mjs +37 -0
- package/dist/capture/consumer/startup.mjs.map +1 -0
- package/dist/capture/consumer/types.d.mts +95 -0
- package/dist/capture/crash-failure.d.mts +23 -0
- package/dist/capture/crash-failure.mjs +69 -0
- package/dist/capture/crash-failure.mjs.map +1 -0
- package/dist/capture/db-queue/cadence.d.mts +30 -0
- package/dist/capture/db-queue/cadence.mjs +37 -0
- package/dist/capture/db-queue/cadence.mjs.map +1 -0
- package/dist/capture/db-queue/claim.d.mts +7 -0
- package/dist/capture/db-queue/claim.mjs +148 -0
- package/dist/capture/db-queue/claim.mjs.map +1 -0
- package/dist/capture/db-queue/clock.mjs +11 -0
- package/dist/capture/db-queue/clock.mjs.map +1 -0
- package/dist/capture/db-queue/codec.mjs +75 -0
- package/dist/capture/db-queue/codec.mjs.map +1 -0
- package/dist/capture/db-queue/index.d.mts +18 -0
- package/dist/capture/db-queue/index.mjs +89 -0
- package/dist/capture/db-queue/index.mjs.map +1 -0
- package/dist/capture/db-queue/keeper.d.mts +34 -0
- package/dist/capture/db-queue/keeper.mjs +91 -0
- package/dist/capture/db-queue/keeper.mjs.map +1 -0
- package/dist/capture/db-queue/lease.mjs +45 -0
- package/dist/capture/db-queue/lease.mjs.map +1 -0
- package/dist/capture/db-queue/lifecycle.mjs +35 -0
- package/dist/capture/db-queue/lifecycle.mjs.map +1 -0
- package/dist/capture/db-queue/operations.mjs +93 -0
- package/dist/capture/db-queue/operations.mjs.map +1 -0
- package/dist/capture/db-queue/poll-loop.d.mts +28 -0
- package/dist/capture/db-queue/poll-loop.mjs +88 -0
- package/dist/capture/db-queue/poll-loop.mjs.map +1 -0
- package/dist/capture/db-queue/poll.d.mts +8 -0
- package/dist/capture/db-queue/poll.mjs +68 -0
- package/dist/capture/db-queue/poll.mjs.map +1 -0
- package/dist/capture/db-queue/timers.d.mts +10 -0
- package/dist/capture/db-queue/timers.mjs +36 -0
- package/dist/capture/db-queue/timers.mjs.map +1 -0
- package/dist/capture/db-queue/types.d.mts +81 -0
- package/dist/capture/db-queue/types.mjs +11 -0
- package/dist/capture/db-queue/types.mjs.map +1 -0
- package/dist/capture/db-queue/wake.mjs +47 -0
- package/dist/capture/db-queue/wake.mjs.map +1 -0
- package/dist/capture/design.mjs +38 -0
- package/dist/capture/design.mjs.map +1 -0
- package/dist/capture/diff-record.mjs +24 -5
- package/dist/capture/diff-record.mjs.map +1 -1
- package/dist/capture/dispatch.d.mts +13 -10
- package/dist/capture/dispatch.mjs +102 -111
- package/dist/capture/dispatch.mjs.map +1 -1
- package/dist/capture/enrich.mjs +59 -0
- package/dist/capture/enrich.mjs.map +1 -0
- package/dist/capture/failure-sets.mjs +21 -0
- package/dist/capture/failure-sets.mjs.map +1 -0
- package/dist/capture/fingerprint.d.mts +40 -0
- package/dist/capture/fingerprint.mjs +82 -0
- package/dist/capture/fingerprint.mjs.map +1 -0
- package/dist/capture/forked/channel.d.mts +23 -0
- package/dist/capture/forked/child.d.mts +32 -0
- package/dist/capture/forked/child.mjs +201 -0
- package/dist/capture/forked/child.mjs.map +1 -0
- package/dist/capture/forked/crash-tracker.mjs +22 -0
- package/dist/capture/forked/crash-tracker.mjs.map +1 -0
- package/dist/capture/forked/decision.d.mts +24 -0
- package/dist/capture/forked/decision.mjs +26 -0
- package/dist/capture/forked/decision.mjs.map +1 -0
- package/dist/capture/forked/exit.mjs +11 -0
- package/dist/capture/forked/exit.mjs.map +1 -0
- package/dist/capture/forked/fork.d.mts +29 -0
- package/dist/capture/forked/fork.mjs +92 -0
- package/dist/capture/forked/fork.mjs.map +1 -0
- package/dist/capture/forked/forked-capture.d.mts +38 -0
- package/dist/capture/forked/forked-capture.mjs +239 -0
- package/dist/capture/forked/forked-capture.mjs.map +1 -0
- package/dist/capture/forked/lease-book.mjs +45 -0
- package/dist/capture/forked/lease-book.mjs.map +1 -0
- package/dist/capture/forked/protocol.d.mts +36 -0
- package/dist/capture/forked/protocol.mjs +24 -0
- package/dist/capture/forked/protocol.mjs.map +1 -0
- package/dist/capture/forked/runs.mjs +74 -0
- package/dist/capture/forked/runs.mjs.map +1 -0
- package/dist/capture/guards.mjs +1 -2
- package/dist/capture/ignore-regions.d.mts +36 -0
- package/dist/capture/ignore-regions.mjs +65 -0
- package/dist/capture/ignore-regions.mjs.map +1 -0
- package/dist/capture/isolate.mjs +140 -0
- package/dist/capture/isolate.mjs.map +1 -0
- package/dist/capture/job-lease.d.mts +26 -0
- package/dist/capture/job-lease.mjs +56 -0
- package/dist/capture/job-lease.mjs.map +1 -0
- package/dist/capture/matrix.mjs +298 -0
- package/dist/capture/matrix.mjs.map +1 -0
- package/dist/capture/modes.d.mts +23 -0
- package/dist/capture/modes.mjs +106 -0
- package/dist/capture/modes.mjs.map +1 -0
- package/dist/capture/orchestrator.d.mts +37 -7
- package/dist/capture/orchestrator.mjs +120 -110
- package/dist/capture/orchestrator.mjs.map +1 -1
- package/dist/capture/pipeline.d.mts +9 -4
- package/dist/capture/pipeline.mjs +284 -48
- package/dist/capture/pipeline.mjs.map +1 -1
- package/dist/capture/progress.d.mts +9 -0
- package/dist/capture/progress.mjs +27 -0
- package/dist/capture/progress.mjs.map +1 -0
- package/dist/capture/publish/accept.d.mts +37 -0
- package/dist/capture/publish/accept.mjs +27 -0
- package/dist/capture/publish/accept.mjs.map +1 -0
- package/dist/capture/publish/complete.d.mts +44 -0
- package/dist/capture/publish/complete.mjs +63 -0
- package/dist/capture/publish/complete.mjs.map +1 -0
- package/dist/capture/publish/extract.d.mts +18 -0
- package/dist/capture/publish/extract.mjs +120 -0
- package/dist/capture/publish/extract.mjs.map +1 -0
- package/dist/capture/publish/handler.d.mts +26 -0
- package/dist/capture/publish/handler.mjs +61 -0
- package/dist/capture/publish/handler.mjs.map +1 -0
- package/dist/capture/publish/index.d.mts +11 -0
- package/dist/capture/publish/invalid-zip.d.mts +8 -0
- package/dist/capture/publish/invalid-zip.mjs +11 -0
- package/dist/capture/publish/invalid-zip.mjs.map +1 -0
- package/dist/capture/publish/limits.d.mts +35 -0
- package/dist/capture/publish/limits.mjs +38 -0
- package/dist/capture/publish/limits.mjs.map +1 -0
- package/dist/capture/publish/published.d.mts +13 -0
- package/dist/capture/publish/published.mjs +17 -0
- package/dist/capture/publish/published.mjs.map +1 -0
- package/dist/capture/publish/run.d.mts +31 -0
- package/dist/capture/publish/run.mjs +29 -0
- package/dist/capture/publish/run.mjs.map +1 -0
- package/dist/capture/rediff.d.mts +1 -3
- package/dist/capture/rediff.mjs +23 -9
- package/dist/capture/rediff.mjs.map +1 -1
- package/dist/capture/render.mjs +52 -0
- package/dist/capture/render.mjs.map +1 -0
- package/dist/capture/scratch.d.mts +42 -0
- package/dist/capture/scratch.mjs +136 -0
- package/dist/capture/scratch.mjs.map +1 -0
- package/dist/capture/shard-plan.mjs +127 -0
- package/dist/capture/shard-plan.mjs.map +1 -0
- package/dist/capture/shards/context.mjs +51 -0
- package/dist/capture/shards/context.mjs.map +1 -0
- package/dist/capture/shards/execute.mjs +175 -0
- package/dist/capture/shards/execute.mjs.map +1 -0
- package/dist/capture/shards/fan-out.mjs +125 -0
- package/dist/capture/shards/fan-out.mjs.map +1 -0
- package/dist/capture/shards/history.mjs +34 -0
- package/dist/capture/shards/history.mjs.map +1 -0
- package/dist/capture/shards/reduce.mjs +96 -0
- package/dist/capture/shards/reduce.mjs.map +1 -0
- package/dist/capture/sizing.d.mts +0 -2
- package/dist/capture/sizing.mjs +2 -13
- package/dist/capture/sizing.mjs.map +1 -1
- package/dist/capture/skip-checks.mjs +3 -67
- package/dist/capture/skip-checks.mjs.map +1 -1
- package/dist/capture/statics.d.mts +7 -10
- package/dist/capture/statics.mjs +26 -74
- package/dist/capture/statics.mjs.map +1 -1
- package/dist/capture/storybook-urls.d.mts +38 -0
- package/dist/capture/storybook-urls.mjs +41 -0
- package/dist/capture/storybook-urls.mjs.map +1 -0
- package/dist/capture/storybook.d.mts +12 -6
- package/dist/capture/storybook.mjs +63 -19
- package/dist/capture/storybook.mjs.map +1 -1
- package/dist/capture/target.mjs +20 -0
- package/dist/capture/target.mjs.map +1 -0
- package/dist/capture/targets.d.mts +23 -0
- package/dist/capture/targets.mjs +31 -0
- package/dist/capture/targets.mjs.map +1 -0
- package/dist/capture/tests.d.mts +35 -0
- package/dist/capture/tests.mjs +136 -0
- package/dist/capture/tests.mjs.map +1 -0
- package/dist/capture/token-chunks.mjs +91 -0
- package/dist/capture/token-chunks.mjs.map +1 -0
- package/dist/capture.d.mts +63 -7
- package/dist/capture.mjs +51 -3
- package/dist/config.d.mts +136 -14
- package/dist/config.mjs +14 -8
- package/dist/config.mjs.map +1 -1
- package/dist/diff.d.mts +7 -3
- package/dist/diff.mjs +15 -3
- package/dist/diff.mjs.map +1 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +2 -2
- package/dist/insights/budget.d.mts +18 -0
- package/dist/insights/budget.mjs +32 -0
- package/dist/insights/budget.mjs.map +1 -0
- package/dist/insights/envelope.d.mts +57 -0
- package/dist/insights/envelope.mjs +59 -0
- package/dist/insights/envelope.mjs.map +1 -0
- package/dist/insights/evidence-health.d.mts +30 -0
- package/dist/insights/evidence-health.mjs +27 -0
- package/dist/insights/evidence-health.mjs.map +1 -0
- package/dist/insights/evidence.d.mts +53 -0
- package/dist/insights/evidence.mjs +63 -0
- package/dist/insights/evidence.mjs.map +1 -0
- package/dist/insights/hash.d.mts +16 -0
- package/dist/insights/hash.mjs +20 -0
- package/dist/insights/hash.mjs.map +1 -0
- package/dist/insights/limits.d.mts +22 -0
- package/dist/insights/limits.mjs +23 -0
- package/dist/insights/limits.mjs.map +1 -0
- package/dist/insights/prompts/v1.d.mts +11 -0
- package/dist/insights/prompts/v1.mjs +33 -0
- package/dist/insights/prompts/v1.mjs.map +1 -0
- package/dist/insights/redact.d.mts +7 -0
- package/dist/insights/redact.mjs +23 -0
- package/dist/insights/redact.mjs.map +1 -0
- package/dist/insights/text.mjs +43 -0
- package/dist/insights/text.mjs.map +1 -0
- package/dist/insights.d.mts +11 -0
- package/dist/insights.mjs +12 -0
- package/dist/logger.d.mts +0 -2
- package/dist/logger.mjs +18 -3
- package/dist/logger.mjs.map +1 -1
- package/dist/models/ai-budget-alert.d.mts +21 -0
- package/dist/models/ai-budget-alert.mjs +37 -0
- package/dist/models/ai-budget-alert.mjs.map +1 -0
- package/dist/models/ai-usage.d.mts +38 -0
- package/dist/models/ai-usage.mjs +60 -0
- package/dist/models/ai-usage.mjs.map +1 -0
- package/dist/models/baseline-approval.d.mts +18 -0
- package/dist/models/baseline-approval.mjs +41 -0
- package/dist/models/baseline-approval.mjs.map +1 -0
- package/dist/models/baseline-archive.d.mts +82 -0
- package/dist/models/baseline-archive.mjs +105 -0
- package/dist/models/baseline-archive.mjs.map +1 -0
- package/dist/models/baseline-atomic.mjs +66 -0
- package/dist/models/baseline-atomic.mjs.map +1 -0
- package/dist/models/baseline-conflict.d.mts +6 -0
- package/dist/models/baseline-conflict.mjs +9 -0
- package/dist/models/baseline-conflict.mjs.map +1 -0
- package/dist/models/baseline-export.d.mts +17 -0
- package/dist/models/baseline-export.mjs +135 -0
- package/dist/models/baseline-export.mjs.map +1 -0
- package/dist/models/baseline-import.mjs +117 -0
- package/dist/models/baseline-import.mjs.map +1 -0
- package/dist/models/baseline-origin.d.mts +16 -0
- package/dist/models/baseline-origin.mjs +35 -0
- package/dist/models/baseline-origin.mjs.map +1 -0
- package/dist/models/baseline-status.d.mts +0 -2
- package/dist/models/baseline-status.mjs +1 -2
- package/dist/models/baseline-transfer.d.mts +48 -0
- package/dist/models/baseline-transfer.mjs +149 -0
- package/dist/models/baseline-transfer.mjs.map +1 -0
- package/dist/models/baseline.d.mts +37 -7
- package/dist/models/baseline.mjs +152 -28
- package/dist/models/baseline.mjs.map +1 -1
- package/dist/models/build-component.d.mts +26 -0
- package/dist/models/build-component.mjs +54 -0
- package/dist/models/build-component.mjs.map +1 -0
- package/dist/models/build-review-status.d.mts +27 -0
- package/dist/models/build-review-status.mjs +36 -0
- package/dist/models/build-review-status.mjs.map +1 -0
- package/dist/models/build-review.d.mts +26 -0
- package/dist/models/build-review.mjs +53 -0
- package/dist/models/build-review.mjs.map +1 -0
- package/dist/models/build.d.mts +25 -5
- package/dist/models/build.mjs +53 -15
- package/dist/models/build.mjs.map +1 -1
- package/dist/models/capture-attempt.d.mts +7 -3
- package/dist/models/capture-attempt.mjs +30 -6
- package/dist/models/capture-attempt.mjs.map +1 -1
- package/dist/models/capture-job.d.mts +104 -0
- package/dist/models/capture-job.mjs +287 -0
- package/dist/models/capture-job.mjs.map +1 -0
- package/dist/models/capture-log.d.mts +1 -3
- package/dist/models/capture-log.mjs +5 -5
- package/dist/models/capture-log.mjs.map +1 -1
- package/dist/models/capture-shard.d.mts +60 -0
- package/dist/models/capture-shard.mjs +189 -0
- package/dist/models/capture-shard.mjs.map +1 -0
- package/dist/models/changeset.d.mts +46 -0
- package/dist/models/changeset.mjs +80 -0
- package/dist/models/changeset.mjs.map +1 -0
- package/dist/models/columns.mjs +18 -0
- package/dist/models/columns.mjs.map +1 -0
- package/dist/models/comment.d.mts +4 -3
- package/dist/models/comment.mjs +8 -6
- package/dist/models/comment.mjs.map +1 -1
- package/dist/models/component.d.mts +41 -0
- package/dist/models/component.mjs +59 -0
- package/dist/models/component.mjs.map +1 -0
- package/dist/models/connections.d.mts +39 -0
- package/dist/models/connections.mjs +111 -0
- package/dist/models/connections.mjs.map +1 -0
- package/dist/models/content-ref.d.mts +1 -3
- package/dist/models/content-ref.mjs +2 -3
- package/dist/models/content-ref.mjs.map +1 -1
- package/dist/models/credentials.d.mts +0 -2
- package/dist/models/credentials.mjs +13 -4
- package/dist/models/credentials.mjs.map +1 -1
- package/dist/models/design-credentials.d.mts +39 -0
- package/dist/models/design-credentials.mjs +85 -0
- package/dist/models/design-credentials.mjs.map +1 -0
- package/dist/models/design-health.d.mts +14 -0
- package/dist/models/design-health.mjs +24 -0
- package/dist/models/design-health.mjs.map +1 -0
- package/dist/models/design-nodes.d.mts +81 -0
- package/dist/models/design-nodes.mjs +221 -0
- package/dist/models/design-nodes.mjs.map +1 -0
- package/dist/models/design-renders.mjs +73 -0
- package/dist/models/design-renders.mjs.map +1 -0
- package/dist/models/design-resolve.d.mts +27 -0
- package/dist/models/design-resolve.mjs +40 -0
- package/dist/models/design-resolve.mjs.map +1 -0
- package/dist/models/design.d.mts +37 -0
- package/dist/models/design.mjs +89 -0
- package/dist/models/design.mjs.map +1 -0
- package/dist/models/inherited-ratio.d.mts +19 -0
- package/dist/models/inherited-ratio.mjs +21 -0
- package/dist/models/inherited-ratio.mjs.map +1 -0
- package/dist/models/insight.d.mts +68 -0
- package/dist/models/insight.mjs +161 -0
- package/dist/models/insight.mjs.map +1 -0
- package/dist/models/label.d.mts +4 -4
- package/dist/models/label.mjs +26 -12
- package/dist/models/label.mjs.map +1 -1
- package/dist/models/member.d.mts +1 -3
- package/dist/models/member.mjs +6 -6
- package/dist/models/member.mjs.map +1 -1
- package/dist/models/notification-channel.d.mts +37 -5
- package/dist/models/notification-channel.mjs +89 -11
- package/dist/models/notification-channel.mjs.map +1 -1
- package/dist/models/notification-subscription.d.mts +1 -4
- package/dist/models/notification-subscription.mjs +8 -11
- package/dist/models/notification-subscription.mjs.map +1 -1
- package/dist/models/project-group-mapping.d.mts +6 -3
- package/dist/models/project-group-mapping.mjs +15 -6
- package/dist/models/project-group-mapping.mjs.map +1 -1
- package/dist/models/project.d.mts +9 -3
- package/dist/models/project.mjs +10 -6
- package/dist/models/project.mjs.map +1 -1
- package/dist/models/public-origin.d.mts +61 -0
- package/dist/models/public-origin.mjs +113 -0
- package/dist/models/public-origin.mjs.map +1 -0
- package/dist/models/review-switch-off.d.mts +12 -0
- package/dist/models/review-switch-off.mjs +25 -0
- package/dist/models/review-switch-off.mjs.map +1 -0
- package/dist/models/site-settings.d.mts +21 -0
- package/dist/models/site-settings.mjs +47 -0
- package/dist/models/site-settings.mjs.map +1 -0
- package/dist/models/snapshot.d.mts +39 -3
- package/dist/models/snapshot.mjs +54 -5
- package/dist/models/snapshot.mjs.map +1 -1
- package/dist/models/token.d.mts +13 -3
- package/dist/models/token.mjs +24 -7
- package/dist/models/token.mjs.map +1 -1
- package/dist/models/usage.d.mts +74 -0
- package/dist/models/usage.mjs +148 -0
- package/dist/models/usage.mjs.map +1 -0
- package/dist/models/user.d.mts +1 -3
- package/dist/models/user.mjs +5 -5
- package/dist/models/user.mjs.map +1 -1
- package/dist/models/webhook.d.mts +15 -3
- package/dist/models/webhook.mjs +34 -6
- package/dist/models/webhook.mjs.map +1 -1
- package/dist/models.d.mts +30 -4
- package/dist/models.mjs +31 -6
- package/dist/options.d.mts +16 -0
- package/dist/options.mjs +88 -0
- package/dist/options.mjs.map +1 -0
- package/dist/paths.d.mts +21 -6
- package/dist/paths.mjs +57 -12
- package/dist/paths.mjs.map +1 -1
- package/dist/retention/ai.d.mts +16 -0
- package/dist/retention/ai.mjs +22 -0
- package/dist/retention/ai.mjs.map +1 -0
- package/dist/retention/pending.mjs +46 -0
- package/dist/retention/pending.mjs.map +1 -0
- package/dist/retention/published-gzip-sweep.mjs +28 -0
- package/dist/retention/published-gzip-sweep.mjs.map +1 -0
- package/dist/retention/screenshot-sweep.d.mts +49 -0
- package/dist/retention/screenshot-sweep.mjs +127 -0
- package/dist/retention/screenshot-sweep.mjs.map +1 -0
- package/dist/retention.d.mts +16 -4
- package/dist/retention.mjs +76 -16
- package/dist/retention.mjs.map +1 -1
- package/dist/schema/baseline.d.mts +8 -2
- package/dist/schema/build-component.d.mts +23 -0
- package/dist/schema/build-review.d.mts +15 -0
- package/dist/schema/build.d.mts +7 -2
- package/dist/schema/capture-attempt.d.mts +0 -2
- package/dist/schema/capture-job.d.mts +58 -0
- package/dist/schema/capture-shard.d.mts +33 -0
- package/dist/schema/comment.d.mts +2 -2
- package/dist/schema/connection.d.mts +22 -0
- package/dist/schema/content-ref.d.mts +0 -2
- package/dist/schema/design.d.mts +56 -0
- package/dist/schema/insight.d.mts +52 -0
- package/dist/schema/label.d.mts +0 -2
- package/dist/schema/member.d.mts +0 -2
- package/dist/schema/notification.d.mts +4 -2
- package/dist/schema/project-group-mapping.d.mts +0 -2
- package/dist/schema/project.d.mts +21 -2
- package/dist/schema/site-setting.d.mts +19 -0
- package/dist/schema/snapshot.d.mts +17 -2
- package/dist/schema/token.d.mts +0 -2
- package/dist/schema/usage.d.mts +28 -0
- package/dist/schema/user.d.mts +0 -2
- package/dist/schema/webhook.d.mts +0 -2
- package/dist/schema.d.mts +12 -6
- package/dist/serverless.d.mts +19 -0
- package/dist/serverless.mjs +36 -0
- package/dist/serverless.mjs.map +1 -0
- package/dist/test-helpers/build-reference-contract.d.mts +14 -0
- package/dist/test-helpers/build-reference-contract.mjs +66 -0
- package/dist/test-helpers/build-reference-contract.mjs.map +1 -0
- package/dist/test-helpers/create-project.d.mts +1 -3
- package/dist/test-helpers/create-project.mjs +1 -2
- package/dist/test-helpers/database-contract.d.mts +0 -2
- package/dist/test-helpers/database-contract.mjs +37 -2
- package/dist/test-helpers/database-contract.mjs.map +1 -1
- package/dist/test-helpers/deferred.d.mts +37 -0
- package/dist/test-helpers/deferred.mjs +60 -0
- package/dist/test-helpers/deferred.mjs.map +1 -0
- package/dist/test-helpers/design-contract.d.mts +19 -0
- package/dist/test-helpers/design-contract.mjs +39 -0
- package/dist/test-helpers/design-contract.mjs.map +1 -0
- package/dist/test-helpers/fake-adapters.d.mts +0 -2
- package/dist/test-helpers/fake-adapters.mjs +1 -2
- package/dist/test-helpers/fake-database.d.mts +0 -2
- package/dist/test-helpers/fake-database.mjs +21 -4
- package/dist/test-helpers/fake-database.mjs.map +1 -1
- package/dist/test-helpers/fake-design.d.mts +41 -0
- package/dist/test-helpers/fake-design.mjs +113 -0
- package/dist/test-helpers/fake-design.mjs.map +1 -0
- package/dist/test-helpers/fake-notifier.d.mts +19 -0
- package/dist/test-helpers/fake-notifier.mjs +43 -0
- package/dist/test-helpers/fake-notifier.mjs.map +1 -0
- package/dist/test-helpers/fake-storage.d.mts +0 -2
- package/dist/test-helpers/fake-storage.mjs +1 -2
- package/dist/test-helpers/fake-tables.mjs +260 -69
- package/dist/test-helpers/fake-tables.mjs.map +1 -1
- package/dist/test-helpers/manual-clock.d.mts +25 -0
- package/dist/test-helpers/manual-clock.mjs +55 -0
- package/dist/test-helpers/manual-clock.mjs.map +1 -0
- package/dist/test-helpers/notifier-contract.d.mts +7 -3
- package/dist/test-helpers/notifier-contract.mjs +33 -5
- package/dist/test-helpers/notifier-contract.mjs.map +1 -1
- package/dist/test-helpers/queue-contract.d.mts +16 -4
- package/dist/test-helpers/queue-contract.mjs +196 -3
- package/dist/test-helpers/queue-contract.mjs.map +1 -1
- package/dist/test-helpers/queue-multi-instance.d.mts +18 -0
- package/dist/test-helpers/queue-multi-instance.mjs +205 -0
- package/dist/test-helpers/queue-multi-instance.mjs.map +1 -0
- package/dist/test-helpers/runner-contract.d.mts +0 -2
- package/dist/test-helpers/runner-contract.mjs +12 -2
- package/dist/test-helpers/runner-contract.mjs.map +1 -1
- package/dist/test-helpers/sql-chunks.mjs +25 -2
- package/dist/test-helpers/sql-chunks.mjs.map +1 -1
- package/dist/test-helpers/storage-contract.d.mts +0 -2
- package/dist/test-helpers/storage-contract.mjs +2 -3
- package/dist/test-helpers.d.mts +10 -4
- package/dist/test-helpers.mjs +9 -3
- package/dist/tracing.mjs +36 -6
- package/dist/tracing.mjs.map +1 -1
- package/dist/types.d.mts +14 -5
- package/dist/types.mjs +22 -6
- package/dist/types.mjs.map +1 -1
- package/dist/urls.d.mts +4 -2
- package/dist/urls.mjs +12 -4
- package/dist/urls.mjs.map +1 -1
- package/dist/utils/adaptive-concurrency.d.mts +64 -0
- package/dist/utils/adaptive-concurrency.mjs +148 -0
- package/dist/utils/adaptive-concurrency.mjs.map +1 -0
- package/dist/utils/connect-retry.d.mts +26 -0
- package/dist/utils/connect-retry.mjs +42 -0
- package/dist/utils/connect-retry.mjs.map +1 -0
- package/dist/utils/encrypt.d.mts +0 -2
- package/dist/utils/encrypt.mjs +1 -2
- package/dist/utils/environment.d.mts +17 -0
- package/dist/utils/environment.mjs +58 -0
- package/dist/utils/environment.mjs.map +1 -0
- package/dist/utils/hash.d.mts +0 -2
- package/dist/utils/hash.mjs +1 -2
- package/dist/utils/http.d.mts +29 -3
- package/dist/utils/http.mjs +100 -19
- package/dist/utils/http.mjs.map +1 -1
- package/dist/utils/map-adaptive.d.mts +13 -0
- package/dist/utils/map-adaptive.mjs +71 -0
- package/dist/utils/map-adaptive.mjs.map +1 -0
- package/dist/utils/map-limit.d.mts +8 -0
- package/dist/utils/map-limit.mjs +31 -0
- package/dist/utils/map-limit.mjs.map +1 -0
- package/dist/utils/mime.d.mts +0 -2
- package/dist/utils/mime.mjs +1 -2
- package/dist/utils/page-limiter.d.mts +35 -0
- package/dist/utils/page-limiter.mjs +55 -0
- package/dist/utils/page-limiter.mjs.map +1 -0
- package/dist/utils/path-security.d.mts +0 -2
- package/dist/utils/path-security.mjs +2 -4
- package/dist/utils/path-security.mjs.map +1 -1
- package/dist/utils/public-origin.d.mts +18 -0
- package/dist/utils/public-origin.mjs +74 -0
- package/dist/utils/public-origin.mjs.map +1 -0
- package/dist/utils/redact.d.mts +0 -2
- package/dist/utils/redact.mjs +4 -2
- package/dist/utils/redact.mjs.map +1 -1
- package/dist/utils/remote-environment.d.mts +76 -0
- package/dist/utils/remote-environment.mjs +163 -0
- package/dist/utils/remote-environment.mjs.map +1 -0
- package/dist/utils/resources.d.mts +62 -0
- package/dist/utils/resources.mjs +121 -0
- package/dist/utils/resources.mjs.map +1 -0
- package/dist/utils/screenshot-store.d.mts +13 -0
- package/dist/utils/screenshot-store.mjs +40 -0
- package/dist/utils/screenshot-store.mjs.map +1 -0
- package/dist/utils/session-gate.d.mts +23 -0
- package/dist/utils/session-gate.mjs +69 -0
- package/dist/utils/session-gate.mjs.map +1 -0
- package/dist/utils/storybook-token.d.mts +89 -0
- package/dist/utils/storybook-token.mjs +135 -0
- package/dist/utils/storybook-token.mjs.map +1 -0
- package/dist/utils/storybook-url.d.mts +13 -0
- package/dist/utils/storybook-url.mjs +20 -0
- package/dist/utils/storybook-url.mjs.map +1 -0
- package/dist/utils/timing.d.mts +0 -2
- package/dist/utils/timing.mjs +2 -3
- package/dist/utils/timing.mjs.map +1 -1
- package/dist/utils/ulid.d.mts +0 -2
- package/dist/utils/ulid.mjs +1 -2
- package/dist/utils/zip-read.mjs +128 -0
- package/dist/utils/zip-read.mjs.map +1 -0
- package/dist/utils/zip-write.d.mts +16 -0
- package/dist/utils/zip-write.mjs +187 -0
- package/dist/utils/zip-write.mjs.map +1 -0
- package/dist/utils.d.mts +17 -3
- package/dist/utils.mjs +18 -4
- package/package.json +35 -26
- package/dist/adapter/capture-runner.mjs.map +0 -1
- package/dist/adapter/git-host/comments.d.mts +0 -41
- package/dist/adapter/git-host/comments.mjs +0 -54
- package/dist/adapter/git-host/comments.mjs.map +0 -1
- package/dist/adapter/git-host/helpers.d.mts +0 -13
- package/dist/adapter/git-host/helpers.mjs +0 -30
- package/dist/adapter/git-host/helpers.mjs.map +0 -1
- package/dist/adapter/git-host.d.mts +0 -42
- package/dist/capture/queue.d.mts +0 -38
- package/dist/capture/queue.mjs +0 -98
- package/dist/capture/queue.mjs.map +0 -1
- package/dist/capture/recovery.d.mts +0 -21
- package/dist/capture/recovery.mjs +0 -83
- package/dist/capture/recovery.mjs.map +0 -1
- package/dist/capture/status-fanout.mjs +0 -72
- package/dist/capture/status-fanout.mjs.map +0 -1
- package/dist/diff/engine.d.mts +0 -16
- package/dist/diff/engine.mjs +0 -87
- package/dist/diff/engine.mjs.map +0 -1
- package/dist/diff/options.d.mts +0 -42
- package/dist/diff/options.mjs +0 -15
- package/dist/diff/options.mjs.map +0 -1
- package/dist/models/status-config.d.mts +0 -38
- package/dist/models/status-config.mjs +0 -63
- package/dist/models/status-config.mjs.map +0 -1
- package/dist/schema/status-config.d.mts +0 -16
- package/dist/test-helpers/git-contract.d.mts +0 -11
- package/dist/test-helpers/git-contract.mjs +0 -34
- package/dist/test-helpers/git-contract.mjs.map +0 -1
- /package/dist/adapter/{git-host.mjs → oauth.mjs} +0 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { sha256 } from "./hash.mjs";
|
|
5
|
+
// src/utils/environment.ts
|
|
6
|
+
let fonts;
|
|
7
|
+
/**
|
|
8
|
+
* Digest of the installed system font families (`fc-list`), or undefined where fontconfig is not
|
|
9
|
+
* available. Cached for the process (fonts do not change under a running server), except a failed
|
|
10
|
+
* run such as a timeout under load: that is retried next time rather than reported as "no fonts"
|
|
11
|
+
* for the life of the process.
|
|
12
|
+
*/
|
|
13
|
+
async function systemFontsHash() {
|
|
14
|
+
fonts ??= listFonts().then((result) => {
|
|
15
|
+
if (!result.settled) fonts = void 0;
|
|
16
|
+
return result.hash;
|
|
17
|
+
});
|
|
18
|
+
return await fonts;
|
|
19
|
+
}
|
|
20
|
+
async function listFonts() {
|
|
21
|
+
const childProcess = process.getBuiltinModule?.call(process, "node:child_process");
|
|
22
|
+
if (!childProcess) return {
|
|
23
|
+
hash: void 0,
|
|
24
|
+
settled: true
|
|
25
|
+
};
|
|
26
|
+
return await new Promise((resolve) => {
|
|
27
|
+
childProcess.execFile("fc-list", [":", "family"], { timeout: 3e3 }, (error, stdout) => {
|
|
28
|
+
resolve(error ? {
|
|
29
|
+
hash: void 0,
|
|
30
|
+
settled: error.code === "ENOENT"
|
|
31
|
+
} : {
|
|
32
|
+
hash: digestOf(stdout),
|
|
33
|
+
settled: true
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The environment a runner captured in: browser build, host platform, device scale factor and a
|
|
40
|
+
* font digest. Runners call this once per render with the version their driver reports.
|
|
41
|
+
*/
|
|
42
|
+
async function detectCaptureEnvironment(browserVersion, deviceScaleFactor = 1) {
|
|
43
|
+
const fontsHash = await systemFontsHash();
|
|
44
|
+
return {
|
|
45
|
+
...browserVersion ? { browserVersion } : {},
|
|
46
|
+
platform: `${process.platform}-${process.arch}`,
|
|
47
|
+
deviceScaleFactor,
|
|
48
|
+
...fontsHash ? { fontsHash } : {}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** Digest of the distinct family names in `fc-list` output; undefined when there are none. */
|
|
52
|
+
function digestOf(stdout) {
|
|
53
|
+
const families = [...new Set(stdout.split("\n").map((line) => line.trim()))].filter(Boolean);
|
|
54
|
+
return families.length > 0 ? sha256(families.toSorted().join("\n")).slice(0, 16) : void 0;
|
|
55
|
+
}
|
|
56
|
+
export { detectCaptureEnvironment, systemFontsHash };
|
|
57
|
+
|
|
58
|
+
//# sourceMappingURL=environment.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environment.mjs","names":[],"sources":["../../src/utils/environment.ts"],"sourcesContent":["/** Detects the rendering environment a capture runner reports (see `CaptureEnvironment`). */\nimport type { CaptureEnvironment } from \"../adapters/capture-runner.ts\";\nimport { sha256 } from \"./hash.ts\";\n\ntype ChildProcessModule = typeof import(\"node:child_process\");\n\nlet fonts: Promise<string | undefined> | undefined;\n\n/**\n * Digest of the installed system font families (`fc-list`), or undefined where fontconfig is not\n * available. Cached for the process (fonts do not change under a running server), except a failed\n * run such as a timeout under load: that is retried next time rather than reported as \"no fonts\"\n * for the life of the process.\n */\nexport async function systemFontsHash(): Promise<string | undefined> {\n fonts ??= listFonts().then((result) => {\n if (!result.settled) {\n fonts = undefined;\n }\n return result.hash;\n });\n return await fonts;\n}\n\n/** An `fc-list` outcome; `settled` is false for a failure worth retrying. */\ninterface FontsResult {\n hash: string | undefined;\n settled: boolean;\n}\n\nasync function listFonts(): Promise<FontsResult> {\n const getBuiltin = (process as { getBuiltinModule?: (id: string) => unknown }).getBuiltinModule;\n const childProcess = getBuiltin?.call(process, \"node:child_process\") as\n | ChildProcessModule\n | undefined;\n if (!childProcess) {\n return { hash: undefined, settled: true };\n }\n return await new Promise<FontsResult>((resolve) => {\n childProcess.execFile(\"fc-list\", [\":\", \"family\"], { timeout: 3000 }, (error, stdout) => {\n resolve(\n error\n ? { hash: undefined, settled: (error as { code?: unknown }).code === \"ENOENT\" }\n : { hash: digestOf(stdout), settled: true },\n );\n });\n });\n}\n\n/**\n * The environment a runner captured in: browser build, host platform, device scale factor and a\n * font digest. Runners call this once per render with the version their driver reports.\n */\nexport async function detectCaptureEnvironment(\n browserVersion?: string,\n deviceScaleFactor = 1,\n): Promise<CaptureEnvironment> {\n const fontsHash = await systemFontsHash();\n return {\n ...(browserVersion ? { browserVersion } : {}),\n platform: `${process.platform}-${process.arch}`,\n deviceScaleFactor,\n ...(fontsHash ? { fontsHash } : {}),\n };\n}\n\n/** Digest of the distinct family names in `fc-list` output; undefined when there are none. */\nfunction digestOf(stdout: string): string | undefined {\n const families = [...new Set(stdout.split(\"\\n\").map((line) => line.trim()))].filter(Boolean);\n return families.length > 0 ? sha256(families.toSorted().join(\"\\n\")).slice(0, 16) : undefined;\n}\n"],"mappings":";;;;;AAMA,IAAI;;;;;;;AAQJ,eAAsB,kBAA+C;CACnE,UAAU,UAAU,CAAC,CAAC,MAAM,WAAW;EACrC,IAAI,CAAC,OAAO,SACV,QAAQ,KAAA;EAEV,OAAO,OAAO;CAChB,CAAC;CACD,OAAO,MAAM;AACf;AAQA,eAAe,YAAkC;CAE/C,MAAM,eADc,QAA2D,kBAC9C,KAAK,SAAS,oBAAoB;CAGnE,IAAI,CAAC,cACH,OAAO;EAAE,MAAM,KAAA;EAAW,SAAS;CAAK;CAE1C,OAAO,MAAM,IAAI,SAAsB,YAAY;EACjD,aAAa,SAAS,WAAW,CAAC,KAAK,QAAQ,GAAG,EAAE,SAAS,IAAK,IAAI,OAAO,WAAW;GACtF,QACE,QACI;IAAE,MAAM,KAAA;IAAW,SAAU,MAA6B,SAAS;GAAS,IAC5E;IAAE,MAAM,SAAS,MAAM;IAAG,SAAS;GAAK,CAC9C;EACF,CAAC;CACH,CAAC;AACH;;;;;AAMA,eAAsB,yBACpB,gBACA,oBAAoB,GACS;CAC7B,MAAM,YAAY,MAAM,gBAAgB;CACxC,OAAO;EACL,GAAI,iBAAiB,EAAE,eAAe,IAAI,CAAC;EAC3C,UAAU,GAAG,QAAQ,SAAS,GAAG,QAAQ;EACzC;EACA,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;CACnC;AACF;;AAGA,SAAS,SAAS,QAAoC;CACpD,MAAM,WAAW,CAAC,GAAG,IAAI,IAAI,OAAO,MAAM,IAAI,CAAC,CAAC,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,OAAO;CAC3F,OAAO,SAAS,SAAS,IAAI,OAAO,SAAS,SAAS,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,GAAG,EAAE,IAAI,KAAA;AACrF"}
|
package/dist/utils/hash.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/hash.d.ts
|
|
4
3
|
/** Hash a value with SHA-256, returning lowercase hex. */
|
|
5
4
|
declare function sha256(value: string): string;
|
|
6
5
|
/** Sign a value with HMAC-SHA-256, returning lowercase hex. */
|
|
@@ -12,6 +11,5 @@ declare function randomToken(prefix: string): {
|
|
|
12
11
|
};
|
|
13
12
|
/** Compare two strings in constant time to avoid timing leaks. */
|
|
14
13
|
declare function timingSafeEqualString(a: string, b: string): boolean;
|
|
15
|
-
//#endregion
|
|
16
14
|
export { hmacSha256, randomToken, sha256, timingSafeEqualString };
|
|
17
15
|
//# sourceMappingURL=hash.d.mts.map
|
package/dist/utils/hash.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import "node:path";
|
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { createHash, createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
5
|
-
|
|
5
|
+
// src/utils/hash.ts
|
|
6
6
|
/** Hash a value with SHA-256, returning lowercase hex. */
|
|
7
7
|
function sha256(value) {
|
|
8
8
|
return createHash("sha256").update(value).digest("hex");
|
|
@@ -25,7 +25,6 @@ function timingSafeEqualString(a, b) {
|
|
|
25
25
|
const bb = Buffer.from(b);
|
|
26
26
|
return ab.length === bb.length && timingSafeEqual(ab, bb);
|
|
27
27
|
}
|
|
28
|
-
//#endregion
|
|
29
28
|
export { hmacSha256, randomToken, sha256, timingSafeEqualString };
|
|
30
29
|
|
|
31
30
|
//# sourceMappingURL=hash.mjs.map
|
package/dist/utils/http.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
3
|
import { Logger } from "../logger.mjs";
|
|
4
|
-
|
|
4
|
+
import { Buffer } from "node:buffer";
|
|
5
5
|
/**
|
|
6
6
|
* Fetch JSON with timeout, retries, and structured errors.
|
|
7
7
|
*
|
|
@@ -13,6 +13,15 @@ import { Logger } from "../logger.mjs";
|
|
|
13
13
|
* @returns The parsed JSON response body.
|
|
14
14
|
*/
|
|
15
15
|
declare function httpJson<T>(url: string, options?: HttpRequestOptions): Promise<T>;
|
|
16
|
+
/**
|
|
17
|
+
* Download a binary response (images) with the same timeout, retry, and
|
|
18
|
+
* `Retry-After` semantics as {@link httpJson}.
|
|
19
|
+
*
|
|
20
|
+
* @param url - Absolute request URL.
|
|
21
|
+
* @param options - Method, headers, timeout, retries, logger.
|
|
22
|
+
* @returns The raw response bytes.
|
|
23
|
+
*/
|
|
24
|
+
declare function httpBytes(url: string, options?: HttpRequestOptions): Promise<Buffer>;
|
|
16
25
|
/**
|
|
17
26
|
* POST/PUT plain payloads (e.g. Slack/Teams webhook cards) with the same
|
|
18
27
|
* timeout, retry, and `Retry-After` semantics as {@link httpJson}.
|
|
@@ -22,6 +31,18 @@ declare function httpJson<T>(url: string, options?: HttpRequestOptions): Promise
|
|
|
22
31
|
* @returns The raw response body text.
|
|
23
32
|
*/
|
|
24
33
|
declare function httpText(url: string, options?: HttpTextOptions): Promise<string>;
|
|
34
|
+
/**
|
|
35
|
+
* Send a request and resolve with the response status, without ever reading the response body (it
|
|
36
|
+
* is cancelled on every response). Use it when the answer is only "did it succeed", such as webhook
|
|
37
|
+
* delivery, so a receiver cannot make the process buffer an arbitrarily large body. Retries,
|
|
38
|
+
* timeouts, redaction and tracing are those of {@link httpText}. A non-2xx response throws
|
|
39
|
+
* {@link HttpError} with an empty `body`.
|
|
40
|
+
*
|
|
41
|
+
* @param url - Absolute request URL.
|
|
42
|
+
* @param options - Method, headers, JSON body, timeout, retries, logger.
|
|
43
|
+
* @returns The 2xx response status.
|
|
44
|
+
*/
|
|
45
|
+
declare function httpStatus(url: string, options?: HttpTextOptions): Promise<number>;
|
|
25
46
|
/** Failed HTTP response: status code plus a snippet of the response body. */
|
|
26
47
|
declare class HttpError extends Error {
|
|
27
48
|
readonly status: number;
|
|
@@ -35,10 +56,14 @@ interface HttpRequestOptions {
|
|
|
35
56
|
headers?: Record<string, string>;
|
|
36
57
|
/** Body serialized as JSON with a `content-type` header. */
|
|
37
58
|
json?: unknown;
|
|
59
|
+
/** Body serialized as `application/x-www-form-urlencoded` (ignored when `json` is set). */
|
|
60
|
+
form?: Record<string, string>;
|
|
38
61
|
/** Per-attempt timeout in milliseconds (default 30s). */
|
|
39
62
|
timeoutMs?: number;
|
|
40
63
|
/** Total attempts including the first (default 3). */
|
|
41
64
|
retries?: number;
|
|
65
|
+
/** Aborts the request (and any further retry) when it fires. */
|
|
66
|
+
signal?: AbortSignal;
|
|
42
67
|
logger?: Logger;
|
|
43
68
|
}
|
|
44
69
|
/** Options for {@link httpText}. */
|
|
@@ -51,8 +76,9 @@ interface HttpTextOptions {
|
|
|
51
76
|
timeoutMs?: number;
|
|
52
77
|
/** Total attempts including the first (default 3). */
|
|
53
78
|
retries?: number;
|
|
79
|
+
/** Aborts the request (and any further retry) when it fires. */
|
|
80
|
+
signal?: AbortSignal;
|
|
54
81
|
logger?: Logger;
|
|
55
82
|
}
|
|
56
|
-
|
|
57
|
-
export { HttpError, HttpRequestOptions, HttpTextOptions, httpJson, httpText };
|
|
83
|
+
export { HttpError, HttpRequestOptions, HttpTextOptions, httpBytes, httpJson, httpStatus, httpText };
|
|
58
84
|
//# sourceMappingURL=http.d.mts.map
|
package/dist/utils/http.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
import { injectTraceContext, withSpan } from "../tracing.mjs";
|
|
5
|
-
|
|
4
|
+
import { injectTraceContext, setSpanMessage, spanException, withSpan } from "../tracing.mjs";
|
|
5
|
+
import { Buffer } from "node:buffer";
|
|
6
|
+
// src/utils/http.ts
|
|
6
7
|
/**
|
|
7
8
|
* Fetch JSON with timeout, retries, and structured errors.
|
|
8
9
|
*
|
|
@@ -17,6 +18,17 @@ async function httpJson(url, options = {}) {
|
|
|
17
18
|
return await requestWithRetry(url, options, options.retries ?? DEFAULT_RETRIES, options.timeoutMs ?? DEFAULT_TIMEOUT_MS, doFetchJson);
|
|
18
19
|
}
|
|
19
20
|
/**
|
|
21
|
+
* Download a binary response (images) with the same timeout, retry, and
|
|
22
|
+
* `Retry-After` semantics as {@link httpJson}.
|
|
23
|
+
*
|
|
24
|
+
* @param url - Absolute request URL.
|
|
25
|
+
* @param options - Method, headers, timeout, retries, logger.
|
|
26
|
+
* @returns The raw response bytes.
|
|
27
|
+
*/
|
|
28
|
+
async function httpBytes(url, options = {}) {
|
|
29
|
+
return await requestWithRetry(url, options, options.retries ?? DEFAULT_RETRIES, options.timeoutMs ?? DEFAULT_TIMEOUT_MS, doFetchBytes);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
20
32
|
* POST/PUT plain payloads (e.g. Slack/Teams webhook cards) with the same
|
|
21
33
|
* timeout, retry, and `Retry-After` semantics as {@link httpJson}.
|
|
22
34
|
*
|
|
@@ -27,6 +39,20 @@ async function httpJson(url, options = {}) {
|
|
|
27
39
|
async function httpText(url, options = {}) {
|
|
28
40
|
return await requestWithRetry(url, options, options.retries ?? DEFAULT_RETRIES, options.timeoutMs ?? DEFAULT_TIMEOUT_MS, doFetchText);
|
|
29
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Send a request and resolve with the response status, without ever reading the response body (it
|
|
44
|
+
* is cancelled on every response). Use it when the answer is only "did it succeed", such as webhook
|
|
45
|
+
* delivery, so a receiver cannot make the process buffer an arbitrarily large body. Retries,
|
|
46
|
+
* timeouts, redaction and tracing are those of {@link httpText}. A non-2xx response throws
|
|
47
|
+
* {@link HttpError} with an empty `body`.
|
|
48
|
+
*
|
|
49
|
+
* @param url - Absolute request URL.
|
|
50
|
+
* @param options - Method, headers, JSON body, timeout, retries, logger.
|
|
51
|
+
* @returns The 2xx response status.
|
|
52
|
+
*/
|
|
53
|
+
async function httpStatus(url, options = {}) {
|
|
54
|
+
return await requestWithRetry(url, options, options.retries ?? DEFAULT_RETRIES, options.timeoutMs ?? DEFAULT_TIMEOUT_MS, doFetchStatus);
|
|
55
|
+
}
|
|
30
56
|
/** Failed HTTP response: status code plus a snippet of the response body. */
|
|
31
57
|
var HttpError = class extends Error {
|
|
32
58
|
status;
|
|
@@ -79,25 +105,54 @@ async function requestWithRetry(url, options, maxAttempts, timeoutMs, perform) {
|
|
|
79
105
|
for (let attempt = 1;; attempt += 1) try {
|
|
80
106
|
return await perform(url, options, timeoutMs);
|
|
81
107
|
} catch (error) {
|
|
82
|
-
span
|
|
83
|
-
if (!shouldRetry(error, attempt, maxAttempts)) throw error;
|
|
108
|
+
describeFailure(span, error, url);
|
|
109
|
+
if (options.signal?.aborted || !shouldRetry(error, attempt, maxAttempts)) throw error;
|
|
110
|
+
span.recordException(spanException(error));
|
|
84
111
|
span.setAttribute("http.retry.count", attempt);
|
|
85
112
|
await backoff(error, attempt, url, options.logger);
|
|
86
113
|
}
|
|
87
114
|
}, {
|
|
88
115
|
"http.request.method": options.method ?? "GET",
|
|
89
|
-
"url.full":
|
|
116
|
+
"url.full": redactUrlForLog(url)
|
|
90
117
|
});
|
|
91
118
|
}
|
|
92
|
-
/**
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
119
|
+
/**
|
|
120
|
+
* The form of a request URL that may appear in logs and span attributes: its origin
|
|
121
|
+
* (`protocol//host`) and nothing else. The path of an incoming webhook (Slack, Teams) is the secret,
|
|
122
|
+
* a bot token can be the first segment (Telegram), and other URLs carry credentials in the userinfo
|
|
123
|
+
* or the query, so the path, query, fragment and userinfo are dropped by construction. There is no
|
|
124
|
+
* opt-out.
|
|
125
|
+
*/
|
|
126
|
+
function redactUrlForLog(url) {
|
|
127
|
+
try {
|
|
128
|
+
const { protocol, host } = new URL(url);
|
|
129
|
+
return `${protocol}//${host}`;
|
|
130
|
+
} catch {
|
|
131
|
+
return "[invalid url]";
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* What a span may say about a failed request. An HTTP failure carries its status and never the
|
|
136
|
+
* response body, which can echo the request URL in any encoding. Any other failure keeps its
|
|
137
|
+
* message with the request URL replaced by its origin, because native `fetch` repeats the whole
|
|
138
|
+
* URL, userinfo included, when it refuses one. The error a caller catches is not changed.
|
|
139
|
+
*/
|
|
140
|
+
function describeFailure(span, error, url) {
|
|
141
|
+
if (error instanceof HttpError) {
|
|
142
|
+
span.setAttribute("http.response.status_code", error.status);
|
|
143
|
+
setSpanMessage(error, `HTTP ${String(error.status)}`);
|
|
144
|
+
} else if (error instanceof Error) setSpanMessage(error, scrubUrl(error.message, url));
|
|
145
|
+
}
|
|
146
|
+
/** `text` with the request URL, as given and as normalized, replaced by its origin. */
|
|
147
|
+
function scrubUrl(text, url) {
|
|
148
|
+
const origin = redactUrlForLog(url);
|
|
149
|
+
const normalized = URL.canParse(url) ? new URL(url).href : url;
|
|
150
|
+
return text.replaceAll(url, origin).replaceAll(normalized, origin);
|
|
96
151
|
}
|
|
97
152
|
async function backoff(error, attempt, url, logger) {
|
|
98
153
|
const delay = retryDelayMs(error, attempt);
|
|
99
154
|
logger?.warn({
|
|
100
|
-
url,
|
|
155
|
+
url: redactUrlForLog(url),
|
|
101
156
|
attempt,
|
|
102
157
|
delayMs: delay
|
|
103
158
|
}, "http request failed, retrying");
|
|
@@ -109,6 +164,9 @@ function buildRequest(options) {
|
|
|
109
164
|
if (options.json !== void 0) {
|
|
110
165
|
headers["content-type"] = "application/json";
|
|
111
166
|
body = JSON.stringify(options.json);
|
|
167
|
+
} else if (options.form) {
|
|
168
|
+
headers["content-type"] = "application/x-www-form-urlencoded";
|
|
169
|
+
body = new URLSearchParams(options.form).toString();
|
|
112
170
|
}
|
|
113
171
|
return {
|
|
114
172
|
headers,
|
|
@@ -122,26 +180,49 @@ async function doFetchJson(url, options, timeoutMs) {
|
|
|
122
180
|
async function doFetchText(url, options, timeoutMs) {
|
|
123
181
|
return await (await doFetchResponse(url, options, timeoutMs)).text();
|
|
124
182
|
}
|
|
125
|
-
async function
|
|
183
|
+
async function doFetchStatus(url, options, timeoutMs) {
|
|
184
|
+
const response = await doFetchResponse(url, options, timeoutMs, false);
|
|
185
|
+
await discardBody(response);
|
|
186
|
+
return response.status;
|
|
187
|
+
}
|
|
188
|
+
async function doFetchBytes(url, options, timeoutMs) {
|
|
189
|
+
const response = await doFetchResponse(url, options, timeoutMs);
|
|
190
|
+
return Buffer.from(await response.arrayBuffer());
|
|
191
|
+
}
|
|
192
|
+
async function doFetchResponse(url, options, timeoutMs, readErrorBody = true) {
|
|
126
193
|
const request = buildRequest(options);
|
|
127
194
|
injectTraceContext(request.headers);
|
|
128
195
|
options.logger?.debug({
|
|
129
|
-
url,
|
|
196
|
+
url: redactUrlForLog(url),
|
|
130
197
|
method: request.method
|
|
131
198
|
}, "http request");
|
|
132
199
|
const response = await fetch(url, {
|
|
133
200
|
method: request.method,
|
|
134
201
|
headers: request.headers,
|
|
135
202
|
body: request.body,
|
|
136
|
-
signal: AbortSignal.timeout(timeoutMs)
|
|
203
|
+
signal: options.signal ? AbortSignal.any([options.signal, AbortSignal.timeout(timeoutMs)]) : AbortSignal.timeout(timeoutMs)
|
|
137
204
|
});
|
|
138
|
-
if (!response.ok)
|
|
139
|
-
const text = await response.text();
|
|
140
|
-
throw new HttpError(response.status, text.slice(0, MAX_BODY_SNIPPET), response.headers);
|
|
141
|
-
}
|
|
205
|
+
if (!response.ok) throw await failureOf(response, readErrorBody);
|
|
142
206
|
return response;
|
|
143
207
|
}
|
|
144
|
-
|
|
145
|
-
|
|
208
|
+
/**
|
|
209
|
+
* Cancel an unread body. Cleanup only: a rejected cancel must never replace the request's outcome,
|
|
210
|
+
* or a delivered request would look failed and be retried.
|
|
211
|
+
*/
|
|
212
|
+
async function discardBody(response) {
|
|
213
|
+
try {
|
|
214
|
+
await response.body?.cancel();
|
|
215
|
+
} catch {}
|
|
216
|
+
}
|
|
217
|
+
/** The {@link HttpError} for a non-2xx response; the body is read (and capped) only on request. */
|
|
218
|
+
async function failureOf(response, readBody) {
|
|
219
|
+
if (!readBody) {
|
|
220
|
+
await discardBody(response);
|
|
221
|
+
return new HttpError(response.status, "", response.headers);
|
|
222
|
+
}
|
|
223
|
+
const text = await response.text();
|
|
224
|
+
return new HttpError(response.status, text.slice(0, MAX_BODY_SNIPPET), response.headers);
|
|
225
|
+
}
|
|
226
|
+
export { HttpError, httpBytes, httpJson, httpStatus, httpText, redactUrlForLog };
|
|
146
227
|
|
|
147
228
|
//# sourceMappingURL=http.mjs.map
|
package/dist/utils/http.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.mjs","names":[],"sources":["../../src/utils/http.ts"],"sourcesContent":["import type { Logger } from \"../logger.ts\";\nimport { injectTraceContext, withSpan } from \"../tracing.ts\";\n\n/**\n * Fetch JSON with timeout, retries, and structured errors.\n *\n * Retries network failures, 429, and 502/503/504 (honoring `Retry-After`);\n * every other status throws {@link HttpError} immediately.\n *\n * @param url - Absolute request URL.\n * @param options - Method, headers, JSON body, timeout, retries, logger.\n * @returns The parsed JSON response body.\n */\nexport async function httpJson<T>(url: string, options: HttpRequestOptions = {}): Promise<T> {\n return await requestWithRetry<T>(\n url,\n options,\n options.retries ?? DEFAULT_RETRIES,\n options.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n doFetchJson,\n );\n}\n\n/**\n * POST/PUT plain payloads (e.g. Slack/Teams webhook cards) with the same\n * timeout, retry, and `Retry-After` semantics as {@link httpJson}.\n *\n * @param url - Absolute request URL.\n * @param options - Method, headers, text body, timeout, retries, logger.\n * @returns The raw response body text.\n */\nexport async function httpText(url: string, options: HttpTextOptions = {}): Promise<string> {\n return await requestWithRetry<string>(\n url,\n options,\n options.retries ?? DEFAULT_RETRIES,\n options.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n doFetchText,\n );\n}\n\n/** Failed HTTP response: status code plus a snippet of the response body. */\nexport class HttpError extends Error {\n readonly status: number;\n readonly body: string;\n readonly headers: Headers;\n\n constructor(status: number, body: string, headers?: Headers) {\n super(`HTTP ${status}: ${body}`);\n this.name = \"HttpError\";\n this.status = status;\n this.body = body;\n this.headers = headers ?? new Headers();\n }\n}\n\n/** Options for {@link httpJson}. */\nexport interface HttpRequestOptions {\n method?: string;\n headers?: Record<string, string>;\n /** Body serialized as JSON with a `content-type` header. */\n json?: unknown;\n /** Per-attempt timeout in milliseconds (default 30s). */\n timeoutMs?: number;\n /** Total attempts including the first (default 3). */\n retries?: number;\n logger?: Logger;\n}\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n/** Options for {@link httpText}. */\nexport interface HttpTextOptions {\n method?: string;\n headers?: Record<string, string>;\n /** Body serialized as JSON with a `content-type` header. */\n json?: unknown;\n /** Per-attempt timeout in milliseconds (default 30s). */\n timeoutMs?: number;\n /** Total attempts including the first (default 3). */\n retries?: number;\n logger?: Logger;\n}\nconst DEFAULT_RETRIES = 3;\nconst MAX_RETRY_AFTER_MS = 30_000;\nconst BACKOFF_BASE_MS = 1000;\nconst MAX_BODY_SNIPPET = 500;\n\n/** Whether a failure is worth retrying: network errors, 429, and 502/503/504. */\nfunction shouldRetry(error: unknown, attempt: number, maxAttempts: number): boolean {\n if (attempt >= maxAttempts) {\n return false;\n }\n if (!(error instanceof HttpError)) {\n return true;\n }\n return error.status === 429 || (error.status >= 502 && error.status <= 504);\n}\n\n/** Parse a Retry-After value (seconds or HTTP-date) into milliseconds. */\nfunction parseRetryAfter(value: string | null): number | null {\n if (!value) {\n return null;\n }\n const seconds = Number(value);\n if (Number.isFinite(seconds) && seconds >= 0) {\n return Math.min(seconds * 1000, MAX_RETRY_AFTER_MS);\n }\n const date = Date.parse(value);\n if (!Number.isNaN(date)) {\n return Math.min(Math.max(date - Date.now(), 0), MAX_RETRY_AFTER_MS);\n }\n return null;\n}\n\n/** Delay before the next attempt: Retry-After when present, else 1s/2s/4s. */\nfunction retryDelayMs(error: unknown, attempt: number): number {\n if (error instanceof HttpError) {\n const retryAfter = parseRetryAfter(error.headers.get(\"retry-after\"));\n if (retryAfter !== null) {\n return retryAfter;\n }\n }\n return Math.min(BACKOFF_BASE_MS * 2 ** (attempt - 1), MAX_RETRY_AFTER_MS);\n}\n\n/** Sleep helper for retry backoff. */\nasync function sleep(ms: number): Promise<void> {\n await new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\n/* oxlint-disable eslint/no-await-in-loop -- retry attempts run sequentially by design */\nasync function requestWithRetry<T>(\n url: string,\n options: HttpRequestOptions,\n maxAttempts: number,\n timeoutMs: number,\n perform: (url: string, options: HttpRequestOptions, timeoutMs: number) => Promise<T>,\n): Promise<T> {\n return await withSpan(\n \"http.client\",\n async (span) => {\n for (let attempt = 1; ; attempt += 1) {\n try {\n return await perform(url, options, timeoutMs);\n } catch (error) {\n span.recordException(error instanceof Error ? error : String(error));\n if (!shouldRetry(error, attempt, maxAttempts)) {\n throw error;\n }\n span.setAttribute(\"http.retry.count\", attempt);\n await backoff(error, attempt, url, options.logger);\n }\n }\n },\n { \"http.request.method\": options.method ?? \"GET\", \"url.full\": redactUrl(url) },\n );\n}\n/* oxlint-enable eslint/no-await-in-loop */\n\n/** Strip the query string (tokens, signatures) from a URL for span attributes. */\nfunction redactUrl(url: string): string {\n const query = url.indexOf(\"?\");\n return query === -1 ? url : url.slice(0, query);\n}\n\nasync function backoff(\n error: unknown,\n attempt: number,\n url: string,\n logger?: Logger,\n): Promise<void> {\n const delay = retryDelayMs(error, attempt);\n logger?.warn({ url, attempt, delayMs: delay }, \"http request failed, retrying\");\n await sleep(delay);\n}\n\nfunction buildRequest(options: HttpRequestOptions): {\n headers: Record<string, string>;\n body: string | undefined;\n method: string;\n} {\n const headers: Record<string, string> = { ...options.headers };\n let body: string | undefined;\n if (options.json !== undefined) {\n headers[\"content-type\"] = \"application/json\";\n body = JSON.stringify(options.json);\n }\n return { headers, body, method: options.method ?? \"GET\" };\n}\n\nasync function doFetchJson<T>(\n url: string,\n options: HttpRequestOptions,\n timeoutMs: number,\n): Promise<T> {\n const response = await doFetchResponse(url, options, timeoutMs);\n return (await response.json()) as T;\n}\n\nasync function doFetchText(\n url: string,\n options: HttpRequestOptions,\n timeoutMs: number,\n): Promise<string> {\n const response = await doFetchResponse(url, options, timeoutMs);\n return await response.text();\n}\n\nasync function doFetchResponse(\n url: string,\n options: HttpRequestOptions,\n timeoutMs: number,\n): Promise<Response> {\n const request = buildRequest(options);\n injectTraceContext(request.headers);\n options.logger?.debug({ url, method: request.method }, \"http request\");\n const response = await fetch(url, {\n method: request.method,\n headers: request.headers,\n body: request.body,\n signal: AbortSignal.timeout(timeoutMs),\n });\n if (!response.ok) {\n const text = await response.text();\n throw new HttpError(response.status, text.slice(0, MAX_BODY_SNIPPET), response.headers);\n }\n return response;\n}\n"],"mappings":";;;;;;;;;;;;;;;AAaA,eAAsB,SAAY,KAAa,UAA8B,CAAC,GAAe;CAC3F,OAAO,MAAM,iBACX,KACA,SACA,QAAQ,WAAW,iBACnB,QAAQ,aAAa,oBACrB,WACF;AACF;;;;;;;;;AAUA,eAAsB,SAAS,KAAa,UAA2B,CAAC,GAAoB;CAC1F,OAAO,MAAM,iBACX,KACA,SACA,QAAQ,WAAW,iBACnB,QAAQ,aAAa,oBACrB,WACF;AACF;;AAGA,IAAa,YAAb,cAA+B,MAAM;CACnC;CACA;CACA;CAEA,YAAY,QAAgB,MAAc,SAAmB;EAC3D,MAAM,QAAQ,OAAO,IAAI,MAAM;EAC/B,KAAK,OAAO;EACZ,KAAK,SAAS;EACd,KAAK,OAAO;EACZ,KAAK,UAAU,WAAW,IAAI,QAAQ;CACxC;AACF;AAeA,MAAM,qBAAqB;AAc3B,MAAM,kBAAkB;AACxB,MAAM,qBAAqB;AAC3B,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;;AAGzB,SAAS,YAAY,OAAgB,SAAiB,aAA8B;CAClF,IAAI,WAAW,aACb,OAAO;CAET,IAAI,EAAE,iBAAiB,YACrB,OAAO;CAET,OAAO,MAAM,WAAW,OAAQ,MAAM,UAAU,OAAO,MAAM,UAAU;AACzE;;AAGA,SAAS,gBAAgB,OAAqC;CAC5D,IAAI,CAAC,OACH,OAAO;CAET,MAAM,UAAU,OAAO,KAAK;CAC5B,IAAI,OAAO,SAAS,OAAO,KAAK,WAAW,GACzC,OAAO,KAAK,IAAI,UAAU,KAAM,kBAAkB;CAEpD,MAAM,OAAO,KAAK,MAAM,KAAK;CAC7B,IAAI,CAAC,OAAO,MAAM,IAAI,GACpB,OAAO,KAAK,IAAI,KAAK,IAAI,OAAO,KAAK,IAAI,GAAG,CAAC,GAAG,kBAAkB;CAEpE,OAAO;AACT;;AAGA,SAAS,aAAa,OAAgB,SAAyB;CAC7D,IAAI,iBAAiB,WAAW;EAC9B,MAAM,aAAa,gBAAgB,MAAM,QAAQ,IAAI,aAAa,CAAC;EACnE,IAAI,eAAe,MACjB,OAAO;CAEX;CACA,OAAO,KAAK,IAAI,kBAAkB,MAAM,UAAU,IAAI,kBAAkB;AAC1E;;AAGA,eAAe,MAAM,IAA2B;CAC9C,MAAM,IAAI,SAAS,YAAY;EAC7B,WAAW,SAAS,EAAE;CACxB,CAAC;AACH;AAGA,eAAe,iBACb,KACA,SACA,aACA,WACA,SACY;CACZ,OAAO,MAAM,SACX,eACA,OAAO,SAAS;EACd,KAAK,IAAI,UAAU,IAAK,WAAW,GACjC,IAAI;GACF,OAAO,MAAM,QAAQ,KAAK,SAAS,SAAS;EAC9C,SAAS,OAAO;GACd,KAAK,gBAAgB,iBAAiB,QAAQ,QAAQ,OAAO,KAAK,CAAC;GACnE,IAAI,CAAC,YAAY,OAAO,SAAS,WAAW,GAC1C,MAAM;GAER,KAAK,aAAa,oBAAoB,OAAO;GAC7C,MAAM,QAAQ,OAAO,SAAS,KAAK,QAAQ,MAAM;EACnD;CAEJ,GACA;EAAE,uBAAuB,QAAQ,UAAU;EAAO,YAAY,UAAU,GAAG;CAAE,CAC/E;AACF;;AAIA,SAAS,UAAU,KAAqB;CACtC,MAAM,QAAQ,IAAI,QAAQ,GAAG;CAC7B,OAAO,UAAU,KAAK,MAAM,IAAI,MAAM,GAAG,KAAK;AAChD;AAEA,eAAe,QACb,OACA,SACA,KACA,QACe;CACf,MAAM,QAAQ,aAAa,OAAO,OAAO;CACzC,QAAQ,KAAK;EAAE;EAAK;EAAS,SAAS;CAAM,GAAG,+BAA+B;CAC9E,MAAM,MAAM,KAAK;AACnB;AAEA,SAAS,aAAa,SAIpB;CACA,MAAM,UAAkC,EAAE,GAAG,QAAQ,QAAQ;CAC7D,IAAI;CACJ,IAAI,QAAQ,SAAS,KAAA,GAAW;EAC9B,QAAQ,kBAAkB;EAC1B,OAAO,KAAK,UAAU,QAAQ,IAAI;CACpC;CACA,OAAO;EAAE;EAAS;EAAM,QAAQ,QAAQ,UAAU;CAAM;AAC1D;AAEA,eAAe,YACb,KACA,SACA,WACY;CAEZ,OAAQ,OAAM,MADS,gBAAgB,KAAK,SAAS,SAAS,EAAA,CACvC,KAAK;AAC9B;AAEA,eAAe,YACb,KACA,SACA,WACiB;CAEjB,OAAO,OAAM,MADU,gBAAgB,KAAK,SAAS,SAAS,EAAA,CACxC,KAAK;AAC7B;AAEA,eAAe,gBACb,KACA,SACA,WACmB;CACnB,MAAM,UAAU,aAAa,OAAO;CACpC,mBAAmB,QAAQ,OAAO;CAClC,QAAQ,QAAQ,MAAM;EAAE;EAAK,QAAQ,QAAQ;CAAO,GAAG,cAAc;CACrE,MAAM,WAAW,MAAM,MAAM,KAAK;EAChC,QAAQ,QAAQ;EAChB,SAAS,QAAQ;EACjB,MAAM,QAAQ;EACd,QAAQ,YAAY,QAAQ,SAAS;CACvC,CAAC;CACD,IAAI,CAAC,SAAS,IAAI;EAChB,MAAM,OAAO,MAAM,SAAS,KAAK;EACjC,MAAM,IAAI,UAAU,SAAS,QAAQ,KAAK,MAAM,GAAG,gBAAgB,GAAG,SAAS,OAAO;CACxF;CACA,OAAO;AACT"}
|
|
1
|
+
{"version":3,"file":"http.mjs","names":[],"sources":["../../src/utils/http.ts"],"sourcesContent":["import type { Span } from \"@opentelemetry/api\";\nimport { Buffer } from \"node:buffer\";\nimport type { Logger } from \"../logger.ts\";\nimport { injectTraceContext, setSpanMessage, spanException, withSpan } from \"../tracing.ts\";\n\n/**\n * Fetch JSON with timeout, retries, and structured errors.\n *\n * Retries network failures, 429, and 502/503/504 (honoring `Retry-After`);\n * every other status throws {@link HttpError} immediately.\n *\n * @param url - Absolute request URL.\n * @param options - Method, headers, JSON body, timeout, retries, logger.\n * @returns The parsed JSON response body.\n */\nexport async function httpJson<T>(url: string, options: HttpRequestOptions = {}): Promise<T> {\n return await requestWithRetry<T>(\n url,\n options,\n options.retries ?? DEFAULT_RETRIES,\n options.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n doFetchJson,\n );\n}\n\n/**\n * Download a binary response (images) with the same timeout, retry, and\n * `Retry-After` semantics as {@link httpJson}.\n *\n * @param url - Absolute request URL.\n * @param options - Method, headers, timeout, retries, logger.\n * @returns The raw response bytes.\n */\nexport async function httpBytes(url: string, options: HttpRequestOptions = {}): Promise<Buffer> {\n return await requestWithRetry<Buffer>(\n url,\n options,\n options.retries ?? DEFAULT_RETRIES,\n options.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n doFetchBytes,\n );\n}\n\n/**\n * POST/PUT plain payloads (e.g. Slack/Teams webhook cards) with the same\n * timeout, retry, and `Retry-After` semantics as {@link httpJson}.\n *\n * @param url - Absolute request URL.\n * @param options - Method, headers, text body, timeout, retries, logger.\n * @returns The raw response body text.\n */\nexport async function httpText(url: string, options: HttpTextOptions = {}): Promise<string> {\n return await requestWithRetry<string>(\n url,\n options,\n options.retries ?? DEFAULT_RETRIES,\n options.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n doFetchText,\n );\n}\n\n/**\n * Send a request and resolve with the response status, without ever reading the response body (it\n * is cancelled on every response). Use it when the answer is only \"did it succeed\", such as webhook\n * delivery, so a receiver cannot make the process buffer an arbitrarily large body. Retries,\n * timeouts, redaction and tracing are those of {@link httpText}. A non-2xx response throws\n * {@link HttpError} with an empty `body`.\n *\n * @param url - Absolute request URL.\n * @param options - Method, headers, JSON body, timeout, retries, logger.\n * @returns The 2xx response status.\n */\nexport async function httpStatus(url: string, options: HttpTextOptions = {}): Promise<number> {\n return await requestWithRetry<number>(\n url,\n options,\n options.retries ?? DEFAULT_RETRIES,\n options.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n doFetchStatus,\n );\n}\n\n/** Failed HTTP response: status code plus a snippet of the response body. */\nexport class HttpError extends Error {\n readonly status: number;\n readonly body: string;\n readonly headers: Headers;\n\n constructor(status: number, body: string, headers?: Headers) {\n super(`HTTP ${status}: ${body}`);\n this.name = \"HttpError\";\n this.status = status;\n this.body = body;\n this.headers = headers ?? new Headers();\n }\n}\n\n/** Options for {@link httpJson}. */\nexport interface HttpRequestOptions {\n method?: string;\n headers?: Record<string, string>;\n /** Body serialized as JSON with a `content-type` header. */\n json?: unknown;\n /** Body serialized as `application/x-www-form-urlencoded` (ignored when `json` is set). */\n form?: Record<string, string>;\n /** Per-attempt timeout in milliseconds (default 30s). */\n timeoutMs?: number;\n /** Total attempts including the first (default 3). */\n retries?: number;\n /** Aborts the request (and any further retry) when it fires. */\n signal?: AbortSignal;\n logger?: Logger;\n}\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n/** Options for {@link httpText}. */\nexport interface HttpTextOptions {\n method?: string;\n headers?: Record<string, string>;\n /** Body serialized as JSON with a `content-type` header. */\n json?: unknown;\n /** Per-attempt timeout in milliseconds (default 30s). */\n timeoutMs?: number;\n /** Total attempts including the first (default 3). */\n retries?: number;\n /** Aborts the request (and any further retry) when it fires. */\n signal?: AbortSignal;\n logger?: Logger;\n}\nconst DEFAULT_RETRIES = 3;\nconst MAX_RETRY_AFTER_MS = 30_000;\nconst BACKOFF_BASE_MS = 1000;\nconst MAX_BODY_SNIPPET = 500;\n\n/** Whether a failure is worth retrying: network errors, 429, and 502/503/504. */\nfunction shouldRetry(error: unknown, attempt: number, maxAttempts: number): boolean {\n if (attempt >= maxAttempts) {\n return false;\n }\n if (!(error instanceof HttpError)) {\n return true;\n }\n return error.status === 429 || (error.status >= 502 && error.status <= 504);\n}\n\n/** Parse a Retry-After value (seconds or HTTP-date) into milliseconds. */\nfunction parseRetryAfter(value: string | null): number | null {\n if (!value) {\n return null;\n }\n const seconds = Number(value);\n if (Number.isFinite(seconds) && seconds >= 0) {\n return Math.min(seconds * 1000, MAX_RETRY_AFTER_MS);\n }\n const date = Date.parse(value);\n if (!Number.isNaN(date)) {\n return Math.min(Math.max(date - Date.now(), 0), MAX_RETRY_AFTER_MS);\n }\n return null;\n}\n\n/** Delay before the next attempt: Retry-After when present, else 1s/2s/4s. */\nfunction retryDelayMs(error: unknown, attempt: number): number {\n if (error instanceof HttpError) {\n const retryAfter = parseRetryAfter(error.headers.get(\"retry-after\"));\n if (retryAfter !== null) {\n return retryAfter;\n }\n }\n return Math.min(BACKOFF_BASE_MS * 2 ** (attempt - 1), MAX_RETRY_AFTER_MS);\n}\n\n/** Sleep helper for retry backoff. */\nasync function sleep(ms: number): Promise<void> {\n await new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\n/* oxlint-disable eslint/no-await-in-loop -- retry attempts run sequentially by design */\nasync function requestWithRetry<T>(\n url: string,\n options: HttpRequestOptions,\n maxAttempts: number,\n timeoutMs: number,\n perform: (url: string, options: HttpRequestOptions, timeoutMs: number) => Promise<T>,\n): Promise<T> {\n return await withSpan(\n \"http.client\",\n async (span) => {\n for (let attempt = 1; ; attempt += 1) {\n try {\n return await perform(url, options, timeoutMs);\n } catch (error) {\n describeFailure(span, error, url);\n if (options.signal?.aborted || !shouldRetry(error, attempt, maxAttempts)) {\n // The final failure is recorded once, by `withSpan`, in the same redacted form.\n throw error;\n }\n span.recordException(spanException(error));\n span.setAttribute(\"http.retry.count\", attempt);\n await backoff(error, attempt, url, options.logger);\n }\n }\n },\n { \"http.request.method\": options.method ?? \"GET\", \"url.full\": redactUrlForLog(url) },\n );\n}\n/* oxlint-enable eslint/no-await-in-loop */\n\n/**\n * The form of a request URL that may appear in logs and span attributes: its origin\n * (`protocol//host`) and nothing else. The path of an incoming webhook (Slack, Teams) is the secret,\n * a bot token can be the first segment (Telegram), and other URLs carry credentials in the userinfo\n * or the query, so the path, query, fragment and userinfo are dropped by construction. There is no\n * opt-out.\n */\nexport function redactUrlForLog(url: string): string {\n try {\n const { protocol, host } = new URL(url);\n return `${protocol}//${host}`;\n } catch {\n return \"[invalid url]\";\n }\n}\n\n/**\n * What a span may say about a failed request. An HTTP failure carries its status and never the\n * response body, which can echo the request URL in any encoding. Any other failure keeps its\n * message with the request URL replaced by its origin, because native `fetch` repeats the whole\n * URL, userinfo included, when it refuses one. The error a caller catches is not changed.\n */\nfunction describeFailure(span: Span, error: unknown, url: string): void {\n if (error instanceof HttpError) {\n span.setAttribute(\"http.response.status_code\", error.status);\n setSpanMessage(error, `HTTP ${String(error.status)}`);\n } else if (error instanceof Error) {\n setSpanMessage(error, scrubUrl(error.message, url));\n }\n}\n\n/** `text` with the request URL, as given and as normalized, replaced by its origin. */\nfunction scrubUrl(text: string, url: string): string {\n const origin = redactUrlForLog(url);\n const normalized = URL.canParse(url) ? new URL(url).href : url;\n return text.replaceAll(url, origin).replaceAll(normalized, origin);\n}\n\nasync function backoff(\n error: unknown,\n attempt: number,\n url: string,\n logger?: Logger,\n): Promise<void> {\n const delay = retryDelayMs(error, attempt);\n logger?.warn(\n { url: redactUrlForLog(url), attempt, delayMs: delay },\n \"http request failed, retrying\",\n );\n await sleep(delay);\n}\n\nfunction buildRequest(options: HttpRequestOptions): {\n headers: Record<string, string>;\n body: string | undefined;\n method: string;\n} {\n const headers: Record<string, string> = { ...options.headers };\n let body: string | undefined;\n if (options.json !== undefined) {\n headers[\"content-type\"] = \"application/json\";\n body = JSON.stringify(options.json);\n } else if (options.form) {\n headers[\"content-type\"] = \"application/x-www-form-urlencoded\";\n body = new URLSearchParams(options.form).toString();\n }\n return { headers, body, method: options.method ?? \"GET\" };\n}\n\nasync function doFetchJson<T>(\n url: string,\n options: HttpRequestOptions,\n timeoutMs: number,\n): Promise<T> {\n const response = await doFetchResponse(url, options, timeoutMs);\n return (await response.json()) as T;\n}\n\nasync function doFetchText(\n url: string,\n options: HttpRequestOptions,\n timeoutMs: number,\n): Promise<string> {\n const response = await doFetchResponse(url, options, timeoutMs);\n return await response.text();\n}\n\nasync function doFetchStatus(\n url: string,\n options: HttpRequestOptions,\n timeoutMs: number,\n): Promise<number> {\n const response = await doFetchResponse(url, options, timeoutMs, false);\n await discardBody(response);\n return response.status;\n}\n\nasync function doFetchBytes(\n url: string,\n options: HttpRequestOptions,\n timeoutMs: number,\n): Promise<Buffer> {\n const response = await doFetchResponse(url, options, timeoutMs);\n return Buffer.from(await response.arrayBuffer());\n}\n\nasync function doFetchResponse(\n url: string,\n options: HttpRequestOptions,\n timeoutMs: number,\n readErrorBody = true,\n): Promise<Response> {\n const request = buildRequest(options);\n injectTraceContext(request.headers);\n options.logger?.debug({ url: redactUrlForLog(url), method: request.method }, \"http request\");\n const response = await fetch(url, {\n method: request.method,\n headers: request.headers,\n body: request.body,\n signal: options.signal\n ? AbortSignal.any([options.signal, AbortSignal.timeout(timeoutMs)])\n : AbortSignal.timeout(timeoutMs),\n });\n if (!response.ok) {\n throw await failureOf(response, readErrorBody);\n }\n return response;\n}\n\n/**\n * Cancel an unread body. Cleanup only: a rejected cancel must never replace the request's outcome,\n * or a delivered request would look failed and be retried.\n */\nasync function discardBody(response: Response): Promise<void> {\n try {\n await response.body?.cancel();\n } catch {\n // The status is already known; there is nothing left to do with the body.\n }\n}\n\n/** The {@link HttpError} for a non-2xx response; the body is read (and capped) only on request. */\nasync function failureOf(response: Response, readBody: boolean): Promise<HttpError> {\n if (!readBody) {\n await discardBody(response);\n return new HttpError(response.status, \"\", response.headers);\n }\n const text = await response.text();\n return new HttpError(response.status, text.slice(0, MAX_BODY_SNIPPET), response.headers);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { AdaptiveConcurrency } from "./adaptive-concurrency.mjs";
|
|
4
|
+
/**
|
|
5
|
+
* Like `mapLimit`, but the limit is read live from `controller` and every finished item is fed
|
|
6
|
+
* back to it. `fn` reports a timeout it handled itself with `timedOut: true`.
|
|
7
|
+
*/
|
|
8
|
+
declare function mapAdaptive<T, R>(items: readonly T[], controller: AdaptiveConcurrency, fn: (item: T, index: number) => Promise<{
|
|
9
|
+
value: R;
|
|
10
|
+
timedOut?: boolean;
|
|
11
|
+
}>): Promise<R[]>;
|
|
12
|
+
export { mapAdaptive };
|
|
13
|
+
//# sourceMappingURL=map-adaptive.d.mts.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
// src/utils/map-adaptive.ts
|
|
5
|
+
/**
|
|
6
|
+
* Like `mapLimit`, but the limit is read live from `controller` and every finished item is fed
|
|
7
|
+
* back to it. `fn` reports a timeout it handled itself with `timedOut: true`.
|
|
8
|
+
*/
|
|
9
|
+
async function mapAdaptive(items, controller, fn) {
|
|
10
|
+
return await new AdaptiveRun(items, controller, fn).run();
|
|
11
|
+
}
|
|
12
|
+
var AdaptiveRun = class {
|
|
13
|
+
results;
|
|
14
|
+
next = 0;
|
|
15
|
+
active = 0;
|
|
16
|
+
failure = null;
|
|
17
|
+
finish = null;
|
|
18
|
+
items;
|
|
19
|
+
controller;
|
|
20
|
+
fn;
|
|
21
|
+
constructor(items, controller, fn) {
|
|
22
|
+
this.items = items;
|
|
23
|
+
this.controller = controller;
|
|
24
|
+
this.fn = fn;
|
|
25
|
+
this.results = Array.from({ length: items.length });
|
|
26
|
+
}
|
|
27
|
+
async run() {
|
|
28
|
+
return await new Promise((resolve, reject) => {
|
|
29
|
+
this.finish = {
|
|
30
|
+
resolve,
|
|
31
|
+
reject
|
|
32
|
+
};
|
|
33
|
+
this.pump();
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
pump() {
|
|
37
|
+
while (this.canLaunch()) {
|
|
38
|
+
this.launch(this.next);
|
|
39
|
+
this.next += 1;
|
|
40
|
+
}
|
|
41
|
+
if (this.active === 0 && (this.failure || this.next >= this.items.length)) {
|
|
42
|
+
if (this.failure) this.finish?.reject(this.failure.error);
|
|
43
|
+
else this.finish?.resolve(this.results);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
canLaunch() {
|
|
47
|
+
return !this.failure && this.active < this.controller.limit && this.next < this.items.length;
|
|
48
|
+
}
|
|
49
|
+
launch(index) {
|
|
50
|
+
this.active += 1;
|
|
51
|
+
const started = performance.now();
|
|
52
|
+
this.fn(this.items[index], index).then((outcome) => {
|
|
53
|
+
this.results[index] = outcome.value;
|
|
54
|
+
this.controller.observe({
|
|
55
|
+
durationMs: performance.now() - started,
|
|
56
|
+
timedOut: outcome.timedOut
|
|
57
|
+
});
|
|
58
|
+
this.settle();
|
|
59
|
+
}, (error) => {
|
|
60
|
+
this.failure ??= { error: error instanceof Error ? error : new Error(String(error)) };
|
|
61
|
+
this.settle();
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
settle() {
|
|
65
|
+
this.active -= 1;
|
|
66
|
+
this.pump();
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
export { mapAdaptive };
|
|
70
|
+
|
|
71
|
+
//# sourceMappingURL=map-adaptive.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"map-adaptive.mjs","names":[],"sources":["../../src/utils/map-adaptive.ts"],"sourcesContent":["/** `mapLimit` with a live, self-adjusting limit. */\nimport type { AdaptiveConcurrency } from \"./adaptive-concurrency.ts\";\n\n/**\n * Like `mapLimit`, but the limit is read live from `controller` and every finished item is fed\n * back to it. `fn` reports a timeout it handled itself with `timedOut: true`.\n */\nexport async function mapAdaptive<T, R>(\n items: readonly T[],\n controller: AdaptiveConcurrency,\n fn: (item: T, index: number) => Promise<{ value: R; timedOut?: boolean }>,\n): Promise<R[]> {\n return await new AdaptiveRun(items, controller, fn).run();\n}\n\nclass AdaptiveRun<T, R> {\n private readonly results: R[];\n private next = 0;\n private active = 0;\n private failure: { error: Error } | null = null;\n private finish: { resolve: (value: R[]) => void; reject: (error: Error) => void } | null = null;\n\n private readonly items: readonly T[];\n private readonly controller: AdaptiveConcurrency;\n private readonly fn: (item: T, index: number) => Promise<{ value: R; timedOut?: boolean }>;\n\n constructor(\n items: readonly T[],\n controller: AdaptiveConcurrency,\n fn: (item: T, index: number) => Promise<{ value: R; timedOut?: boolean }>,\n ) {\n this.items = items;\n this.controller = controller;\n this.fn = fn;\n this.results = Array.from<R>({ length: items.length });\n }\n\n async run(): Promise<R[]> {\n return await new Promise<R[]>((resolve, reject) => {\n this.finish = { resolve, reject };\n this.pump();\n });\n }\n\n private pump(): void {\n while (this.canLaunch()) {\n this.launch(this.next);\n this.next += 1;\n }\n if (this.active === 0 && (this.failure || this.next >= this.items.length)) {\n if (this.failure) {\n this.finish?.reject(this.failure.error);\n } else {\n this.finish?.resolve(this.results);\n }\n }\n }\n\n private canLaunch(): boolean {\n return !this.failure && this.active < this.controller.limit && this.next < this.items.length;\n }\n\n private launch(index: number): void {\n this.active += 1;\n const started = performance.now();\n this.fn(this.items[index] as T, index).then(\n (outcome) => {\n this.results[index] = outcome.value;\n this.controller.observe({\n durationMs: performance.now() - started,\n timedOut: outcome.timedOut,\n });\n this.settle();\n },\n (error: unknown) => {\n this.failure ??= { error: error instanceof Error ? error : new Error(String(error)) };\n this.settle();\n },\n );\n }\n\n private settle(): void {\n this.active -= 1;\n this.pump();\n }\n}\n"],"mappings":";;;;;;;;AAOA,eAAsB,YACpB,OACA,YACA,IACc;CACd,OAAO,MAAM,IAAI,YAAY,OAAO,YAAY,EAAE,CAAC,CAAC,IAAI;AAC1D;AAEA,IAAM,cAAN,MAAwB;CACtB;CACA,OAAe;CACf,SAAiB;CACjB,UAA2C;CAC3C,SAA2F;CAE3F;CACA;CACA;CAEA,YACE,OACA,YACA,IACA;EACA,KAAK,QAAQ;EACb,KAAK,aAAa;EAClB,KAAK,KAAK;EACV,KAAK,UAAU,MAAM,KAAQ,EAAE,QAAQ,MAAM,OAAO,CAAC;CACvD;CAEA,MAAM,MAAoB;EACxB,OAAO,MAAM,IAAI,SAAc,SAAS,WAAW;GACjD,KAAK,SAAS;IAAE;IAAS;GAAO;GAChC,KAAK,KAAK;EACZ,CAAC;CACH;CAEA,OAAqB;EACnB,OAAO,KAAK,UAAU,GAAG;GACvB,KAAK,OAAO,KAAK,IAAI;GACrB,KAAK,QAAQ;EACf;EACA,IAAI,KAAK,WAAW,MAAM,KAAK,WAAW,KAAK,QAAQ,KAAK,MAAM,SAAS;GACzE,IAAI,KAAK,SACP,KAAK,QAAQ,OAAO,KAAK,QAAQ,KAAK;QAEtC,KAAK,QAAQ,QAAQ,KAAK,OAAO;EAErC;CACF;CAEA,YAA6B;EAC3B,OAAO,CAAC,KAAK,WAAW,KAAK,SAAS,KAAK,WAAW,SAAS,KAAK,OAAO,KAAK,MAAM;CACxF;CAEA,OAAe,OAAqB;EAClC,KAAK,UAAU;EACf,MAAM,UAAU,YAAY,IAAI;EAChC,KAAK,GAAG,KAAK,MAAM,QAAa,KAAK,CAAC,CAAC,MACpC,YAAY;GACX,KAAK,QAAQ,SAAS,QAAQ;GAC9B,KAAK,WAAW,QAAQ;IACtB,YAAY,YAAY,IAAI,IAAI;IAChC,UAAU,QAAQ;GACpB,CAAC;GACD,KAAK,OAAO;EACd,IACC,UAAmB;GAClB,KAAK,YAAY,EAAE,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,EAAE;GACpF,KAAK,OAAO;EACd,CACF;CACF;CAEA,SAAuB;EACrB,KAAK,UAAU;EACf,KAAK,KAAK;CACZ;AACF"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
/** Split `items` into consecutive batches of at most `size` (a non-positive size means one batch). */
|
|
4
|
+
declare function chunk<T>(items: readonly T[], size: number): T[][];
|
|
5
|
+
/** Run `fn` over `items` with at most `limit` in flight; results keep input order. */
|
|
6
|
+
declare function mapLimit<T, R>(items: readonly T[], limit: number, fn: (item: T, index: number) => Promise<R>): Promise<R[]>;
|
|
7
|
+
export { chunk, mapLimit };
|
|
8
|
+
//# sourceMappingURL=map-limit.d.mts.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
// src/utils/map-limit.ts
|
|
5
|
+
/** Split `items` into consecutive batches of at most `size` (a non-positive size means one batch). */
|
|
6
|
+
function chunk(items, size) {
|
|
7
|
+
const step = size > 0 ? Math.max(1, Math.floor(size)) : items.length || 1;
|
|
8
|
+
const batches = [];
|
|
9
|
+
for (let start = 0; start < items.length; start += step) batches.push(items.slice(start, start + step));
|
|
10
|
+
return batches;
|
|
11
|
+
}
|
|
12
|
+
/** Run `fn` over `items` with at most `limit` in flight; results keep input order. */
|
|
13
|
+
async function mapLimit(items, limit, fn) {
|
|
14
|
+
const results = Array.from({ length: items.length });
|
|
15
|
+
let next = 0;
|
|
16
|
+
const worker = async () => {
|
|
17
|
+
while (next < items.length) {
|
|
18
|
+
const index = next;
|
|
19
|
+
next += 1;
|
|
20
|
+
results[index] = await fn(items[index], index);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const workers = Math.max(1, Math.min(Math.floor(limit) || 1, items.length));
|
|
24
|
+
await Promise.all(Array.from({ length: workers }, async () => {
|
|
25
|
+
await worker();
|
|
26
|
+
}));
|
|
27
|
+
return results;
|
|
28
|
+
}
|
|
29
|
+
export { chunk, mapLimit };
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=map-limit.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"map-limit.mjs","names":[],"sources":["../../src/utils/map-limit.ts"],"sourcesContent":["/** Split `items` into consecutive batches of at most `size` (a non-positive size means one batch). */\nexport function chunk<T>(items: readonly T[], size: number): T[][] {\n const step = size > 0 ? Math.max(1, Math.floor(size)) : items.length || 1;\n const batches: T[][] = [];\n for (let start = 0; start < items.length; start += step) {\n batches.push(items.slice(start, start + step));\n }\n return batches;\n}\n\n/** Run `fn` over `items` with at most `limit` in flight; results keep input order. */\nexport async function mapLimit<T, R>(\n items: readonly T[],\n limit: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results = Array.from<R>({ length: items.length });\n let next = 0;\n const worker = async (): Promise<void> => {\n while (next < items.length) {\n const index = next;\n next += 1;\n // oxlint-disable-next-line eslint/no-await-in-loop -- bounded worker pulls sequentially\n results[index] = await fn(items[index] as T, index);\n }\n };\n const workers = Math.max(1, Math.min(Math.floor(limit) || 1, items.length));\n await Promise.all(\n Array.from({ length: workers }, async () => {\n await worker();\n }),\n );\n return results;\n}\n"],"mappings":";;;;;AACA,SAAgB,MAAS,OAAqB,MAAqB;CACjE,MAAM,OAAO,OAAO,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,IAAI,CAAC,IAAI,MAAM,UAAU;CACxE,MAAM,UAAiB,CAAC;CACxB,KAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SAAS,MACjD,QAAQ,KAAK,MAAM,MAAM,OAAO,QAAQ,IAAI,CAAC;CAE/C,OAAO;AACT;;AAGA,eAAsB,SACpB,OACA,OACA,IACc;CACd,MAAM,UAAU,MAAM,KAAQ,EAAE,QAAQ,MAAM,OAAO,CAAC;CACtD,IAAI,OAAO;CACX,MAAM,SAAS,YAA2B;EACxC,OAAO,OAAO,MAAM,QAAQ;GAC1B,MAAM,QAAQ;GACd,QAAQ;GAER,QAAQ,SAAS,MAAM,GAAG,MAAM,QAAa,KAAK;EACpD;CACF;CACA,MAAM,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,MAAM,KAAK,KAAK,GAAG,MAAM,MAAM,CAAC;CAC1E,MAAM,QAAQ,IACZ,MAAM,KAAK,EAAE,QAAQ,QAAQ,GAAG,YAAY;EAC1C,MAAM,OAAO;CACf,CAAC,CACH;CACA,OAAO;AACT"}
|
package/dist/utils/mime.d.mts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
|
-
//#region src/utils/mime.d.ts
|
|
4
3
|
/** Shared MIME types for storybook static serving. */
|
|
5
4
|
declare const MIME_TYPES: Record<string, string>;
|
|
6
5
|
declare function mimeFor(filePath: string): string;
|
|
7
|
-
//#endregion
|
|
8
6
|
export { MIME_TYPES, mimeFor };
|
|
9
7
|
//# sourceMappingURL=mime.d.mts.map
|
package/dist/utils/mime.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
|
|
4
|
+
// src/utils/mime.ts
|
|
5
5
|
/** Shared MIME types for storybook static serving. */
|
|
6
6
|
const MIME_TYPES = {
|
|
7
7
|
".css": "text/css; charset=utf-8",
|
|
@@ -27,7 +27,6 @@ function mimeFor(filePath) {
|
|
|
27
27
|
const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
28
28
|
return MIME_TYPES[ext] ?? "application/octet-stream";
|
|
29
29
|
}
|
|
30
|
-
//#endregion
|
|
31
30
|
export { MIME_TYPES, mimeFor };
|
|
32
31
|
|
|
33
32
|
//# sourceMappingURL=mime.mjs.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { Logger } from "../logger.mjs";
|
|
4
|
+
import { SessionGate } from "./session-gate.mjs";
|
|
5
|
+
/** Options for {@link createPageLimiter}. */
|
|
6
|
+
interface PageLimiterOptions {
|
|
7
|
+
/** Explicit page count (config or run input). Fixed: the adaptive controller stays off. */
|
|
8
|
+
concurrency?: number;
|
|
9
|
+
/**
|
|
10
|
+
* A remote target's session cap, shared by every render of that target in the process. It
|
|
11
|
+
* replaces CPU and memory sizing: the plan's parallel-session limit is the budget, not this host.
|
|
12
|
+
* An explicit `concurrency` can lower a run's share of it, never raise it.
|
|
13
|
+
*/
|
|
14
|
+
gate?: SessionGate;
|
|
15
|
+
/** Engine being rendered; heavier engines get a smaller budget. */
|
|
16
|
+
browser?: string;
|
|
17
|
+
logger?: Logger;
|
|
18
|
+
}
|
|
19
|
+
/** Runs capture jobs under a page limit. One limiter spans a whole run so what it learns carries across batches. */
|
|
20
|
+
interface PageLimiter {
|
|
21
|
+
/**
|
|
22
|
+
* Run `fn` over `items`. `fn` resolves to whether the capture timed out (it handles its own
|
|
23
|
+
* failures, so the limiter cannot see them otherwise).
|
|
24
|
+
*/
|
|
25
|
+
each<T>(items: readonly T[], fn: (item: T) => Promise<boolean>): Promise<void>;
|
|
26
|
+
/** Release the event-loop monitor. */
|
|
27
|
+
dispose(): void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The session gate's size for a remote target; a fixed limit when `concurrency` is given; otherwise start at the CPU/memory/engine-sized
|
|
31
|
+
* recommendation and let latency, event-loop lag and timeouts move it up to the adaptive ceiling.
|
|
32
|
+
*/
|
|
33
|
+
declare function createPageLimiter(options: PageLimiterOptions): PageLimiter;
|
|
34
|
+
export { PageLimiter, PageLimiterOptions, createPageLimiter };
|
|
35
|
+
//# sourceMappingURL=page-limiter.d.mts.map
|