@storyshelf/core 0.6.2 → 0.7.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 -2
- package/dist/adapter/capture-runner.d.mts +127 -10
- package/dist/adapter/capture-runner.mjs +5 -12
- package/dist/adapter/database.d.mts +27 -8
- package/dist/adapter/database.mjs.map +1 -1
- package/dist/adapter/design.d.mts +85 -0
- package/dist/adapter/design.mjs +28 -0
- package/dist/adapter/design.mjs.map +1 -0
- package/dist/adapter/metadata.d.mts +3 -10
- package/dist/adapter/notifier.d.mts +6 -3
- package/dist/adapter/notifier.mjs +5 -1
- package/dist/adapter/oauth.d.mts +52 -0
- package/dist/adapter/setup.d.mts +4 -5
- package/dist/adapter/setup.mjs +2 -1
- package/dist/adapter/setup.mjs.map +1 -1
- package/dist/adapter/webhook-events.mjs +1 -1
- package/dist/adapters/capture-target-offers.d.mts +28 -0
- package/dist/adapters/capture-target-offers.mjs +58 -0
- package/dist/adapters/capture-target-offers.mjs.map +1 -0
- package/dist/adapters/capture-target.d.mts +71 -0
- package/dist/adapters/capture-target.mjs +76 -0
- package/dist/adapters/capture-target.mjs.map +1 -0
- package/dist/adapters/notifier/build-comment.d.mts +24 -0
- package/dist/adapters/notifier/build-comment.mjs +24 -0
- package/dist/adapters/notifier/build-comment.mjs.map +1 -0
- package/dist/adapters/notifier/build-state.d.mts +99 -0
- package/dist/adapters/notifier/build-state.mjs +218 -0
- package/dist/adapters/notifier/build-state.mjs.map +1 -0
- package/dist/adapters/notifier/events.d.mts +4 -2
- package/dist/adapters/notifier/events.mjs +31 -18
- package/dist/adapters/notifier/events.mjs.map +1 -1
- package/dist/adapters/notifier/provider.d.mts +53 -7
- package/dist/adapters/notifier/provider.mjs +12 -0
- package/dist/adapters/notifier/provider.mjs.map +1 -0
- package/dist/adapters/notifier/state.d.mts +24 -0
- package/dist/adapters/notifier/state.mjs +66 -0
- package/dist/adapters/notifier/state.mjs.map +1 -0
- package/dist/adapters/notifier/types.d.mts +51 -2
- package/dist/adapters/remote-browser.d.mts +87 -0
- package/dist/adapters/remote-browser.mjs +103 -0
- package/dist/adapters/remote-browser.mjs.map +1 -0
- package/dist/adapters/render-runs.d.mts +25 -0
- package/dist/adapters/render-runs.mjs +41 -0
- package/dist/adapters/render-runs.mjs.map +1 -0
- package/dist/adapters/setup-runner.mjs +2 -4
- package/dist/adapters/setup-runner.mjs.map +1 -1
- package/dist/adapters/sources.d.mts +12 -0
- package/dist/adapters/sources.mjs +20 -0
- package/dist/adapters/sources.mjs.map +1 -0
- package/dist/adapters/validate.mjs +25 -18
- package/dist/adapters/validate.mjs.map +1 -1
- package/dist/ai.d.mts +105 -0
- package/dist/ai.mjs +19 -0
- package/dist/ai.mjs.map +1 -0
- package/dist/capture/a11y/axe-source.d.mts +18 -0
- package/dist/capture/a11y/axe-source.mjs +37 -0
- package/dist/capture/a11y/axe-source.mjs.map +1 -0
- package/dist/capture/a11y/in-page.d.mts +27 -0
- package/dist/capture/a11y/in-page.mjs +60 -0
- package/dist/capture/a11y/in-page.mjs.map +1 -0
- package/dist/capture/a11y/map.d.mts +24 -0
- package/dist/capture/a11y/map.mjs +48 -0
- package/dist/capture/a11y/map.mjs.map +1 -0
- package/dist/capture/a11y/options.d.mts +23 -0
- package/dist/capture/a11y/options.mjs +75 -0
- package/dist/capture/a11y/options.mjs.map +1 -0
- package/dist/capture/adapter.d.mts +41 -3
- package/dist/capture/adapter.mjs.map +1 -1
- package/dist/capture/affected.d.mts +2 -0
- package/dist/capture/affected.mjs +13 -4
- package/dist/capture/affected.mjs.map +1 -1
- package/dist/capture/attempt-logger.mjs +84 -0
- package/dist/capture/attempt-logger.mjs.map +1 -0
- package/dist/capture/browsers.d.mts +35 -0
- package/dist/capture/browsers.mjs +67 -0
- package/dist/capture/browsers.mjs.map +1 -0
- package/dist/capture/build-state/deliver.d.mts +32 -0
- package/dist/capture/build-state/deliver.mjs +118 -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 +56 -0
- package/dist/capture/build-state/notify-job.mjs +133 -0
- package/dist/capture/build-state/notify-job.mjs.map +1 -0
- package/dist/capture/build-state/publisher.d.mts +23 -0
- package/dist/capture/build-state/publisher.mjs +107 -0
- package/dist/capture/build-state/publisher.mjs.map +1 -0
- package/dist/capture/build-state/resume.d.mts +9 -0
- package/dist/capture/build-state/resume.mjs +47 -0
- package/dist/capture/build-state/resume.mjs.map +1 -0
- package/dist/capture/build-state/types.d.mts +49 -0
- package/dist/capture/catalog/catalogue.d.mts +22 -0
- package/dist/capture/catalog/catalogue.mjs +53 -0
- package/dist/capture/catalog/catalogue.mjs.map +1 -0
- package/dist/capture/catalog/components.d.mts +23 -0
- package/dist/capture/catalog/components.mjs +55 -0
- package/dist/capture/catalog/components.mjs.map +1 -0
- package/dist/capture/catalog/history.d.mts +30 -0
- package/dist/capture/catalog/history.mjs +51 -0
- package/dist/capture/catalog/history.mjs.map +1 -0
- package/dist/capture/catalog/hook.d.mts +14 -0
- package/dist/capture/catalog/hook.mjs +19 -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 +26 -0
- package/dist/capture/catalog/load.mjs +49 -0
- package/dist/capture/catalog/load.mjs.map +1 -0
- package/dist/capture/catalog/parse.d.mts +15 -0
- package/dist/capture/catalog/parse.mjs +56 -0
- package/dist/capture/catalog/parse.mjs.map +1 -0
- package/dist/capture/catalog/read.d.mts +12 -0
- package/dist/capture/catalog/read.mjs +33 -0
- package/dist/capture/catalog/read.mjs.map +1 -0
- package/dist/capture/catalog/types.d.mts +33 -0
- package/dist/capture/consumer/capture-handler.d.mts +58 -0
- package/dist/capture/consumer/capture-handler.mjs +118 -0
- package/dist/capture/consumer/capture-handler.mjs.map +1 -0
- package/dist/capture/consumer/idle.d.mts +17 -0
- package/dist/capture/consumer/idle.mjs +25 -0
- package/dist/capture/consumer/idle.mjs.map +1 -0
- package/dist/capture/consumer/index.d.mts +13 -0
- package/dist/capture/consumer/index.mjs +139 -0
- package/dist/capture/consumer/index.mjs.map +1 -0
- package/dist/capture/consumer/loop.mjs +110 -0
- package/dist/capture/consumer/loop.mjs.map +1 -0
- package/dist/capture/consumer/metrics.d.mts +41 -0
- package/dist/capture/consumer/metrics.mjs +38 -0
- package/dist/capture/consumer/metrics.mjs.map +1 -0
- package/dist/capture/consumer/retry.d.mts +33 -0
- package/dist/capture/consumer/retry.mjs +34 -0
- package/dist/capture/consumer/retry.mjs.map +1 -0
- package/dist/capture/consumer/run-job.mjs +195 -0
- package/dist/capture/consumer/run-job.mjs.map +1 -0
- package/dist/capture/consumer/startup.mjs +38 -0
- package/dist/capture/consumer/startup.mjs.map +1 -0
- package/dist/capture/consumer/types.d.mts +97 -0
- package/dist/capture/crash-failure.d.mts +25 -0
- package/dist/capture/crash-failure.mjs +70 -0
- package/dist/capture/crash-failure.mjs.map +1 -0
- package/dist/capture/db-queue/cadence.d.mts +32 -0
- package/dist/capture/db-queue/cadence.mjs +38 -0
- package/dist/capture/db-queue/cadence.mjs.map +1 -0
- package/dist/capture/db-queue/claim.d.mts +9 -0
- package/dist/capture/db-queue/claim.mjs +132 -0
- package/dist/capture/db-queue/claim.mjs.map +1 -0
- package/dist/capture/db-queue/clock.mjs +12 -0
- package/dist/capture/db-queue/clock.mjs.map +1 -0
- package/dist/capture/db-queue/codec.mjs +76 -0
- package/dist/capture/db-queue/codec.mjs.map +1 -0
- package/dist/capture/db-queue/index.d.mts +20 -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 +36 -0
- package/dist/capture/db-queue/keeper.mjs +92 -0
- package/dist/capture/db-queue/keeper.mjs.map +1 -0
- package/dist/capture/db-queue/lease.mjs +46 -0
- package/dist/capture/db-queue/lease.mjs.map +1 -0
- package/dist/capture/db-queue/lifecycle.mjs +36 -0
- package/dist/capture/db-queue/lifecycle.mjs.map +1 -0
- package/dist/capture/db-queue/operations.mjs +94 -0
- package/dist/capture/db-queue/operations.mjs.map +1 -0
- package/dist/capture/db-queue/poll-loop.d.mts +30 -0
- package/dist/capture/db-queue/poll-loop.mjs +89 -0
- package/dist/capture/db-queue/poll-loop.mjs.map +1 -0
- package/dist/capture/db-queue/poll.d.mts +10 -0
- package/dist/capture/db-queue/poll.mjs +69 -0
- package/dist/capture/db-queue/poll.mjs.map +1 -0
- package/dist/capture/db-queue/timers.d.mts +12 -0
- package/dist/capture/db-queue/timers.mjs +37 -0
- package/dist/capture/db-queue/timers.mjs.map +1 -0
- package/dist/capture/db-queue/types.d.mts +83 -0
- package/dist/capture/db-queue/types.mjs +12 -0
- package/dist/capture/db-queue/types.mjs.map +1 -0
- package/dist/capture/db-queue/wake.mjs +48 -0
- package/dist/capture/db-queue/wake.mjs.map +1 -0
- package/dist/capture/design.mjs +39 -0
- package/dist/capture/design.mjs.map +1 -0
- package/dist/capture/diff-record.mjs +23 -3
- package/dist/capture/diff-record.mjs.map +1 -1
- package/dist/capture/dispatch.d.mts +12 -7
- package/dist/capture/dispatch.mjs +86 -107
- package/dist/capture/dispatch.mjs.map +1 -1
- package/dist/capture/enrich.mjs +60 -0
- package/dist/capture/enrich.mjs.map +1 -0
- package/dist/capture/failure-sets.mjs +22 -0
- package/dist/capture/failure-sets.mjs.map +1 -0
- package/dist/capture/fingerprint.d.mts +42 -0
- package/dist/capture/fingerprint.mjs +83 -0
- package/dist/capture/fingerprint.mjs.map +1 -0
- package/dist/capture/forked/channel.d.mts +25 -0
- package/dist/capture/forked/child.d.mts +34 -0
- package/dist/capture/forked/child.mjs +202 -0
- package/dist/capture/forked/child.mjs.map +1 -0
- package/dist/capture/forked/crash-tracker.mjs +23 -0
- package/dist/capture/forked/crash-tracker.mjs.map +1 -0
- package/dist/capture/forked/decision.d.mts +26 -0
- package/dist/capture/forked/decision.mjs +27 -0
- package/dist/capture/forked/decision.mjs.map +1 -0
- package/dist/capture/forked/exit.mjs +12 -0
- package/dist/capture/forked/exit.mjs.map +1 -0
- package/dist/capture/forked/fork.d.mts +31 -0
- package/dist/capture/forked/fork.mjs +93 -0
- package/dist/capture/forked/fork.mjs.map +1 -0
- package/dist/capture/forked/forked-capture.d.mts +40 -0
- package/dist/capture/forked/forked-capture.mjs +240 -0
- package/dist/capture/forked/forked-capture.mjs.map +1 -0
- package/dist/capture/forked/lease-book.mjs +46 -0
- package/dist/capture/forked/lease-book.mjs.map +1 -0
- package/dist/capture/forked/protocol.d.mts +38 -0
- package/dist/capture/forked/protocol.mjs +25 -0
- package/dist/capture/forked/protocol.mjs.map +1 -0
- package/dist/capture/forked/runs.mjs +75 -0
- package/dist/capture/forked/runs.mjs.map +1 -0
- package/dist/capture/isolate.mjs +141 -0
- package/dist/capture/isolate.mjs.map +1 -0
- package/dist/capture/job-lease.d.mts +28 -0
- package/dist/capture/job-lease.mjs +57 -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 +25 -0
- package/dist/capture/modes.mjs +107 -0
- package/dist/capture/modes.mjs.map +1 -0
- package/dist/capture/orchestrator.d.mts +37 -5
- package/dist/capture/orchestrator.mjs +118 -107
- package/dist/capture/orchestrator.mjs.map +1 -1
- package/dist/capture/pipeline.d.mts +9 -2
- package/dist/capture/pipeline.mjs +250 -45
- package/dist/capture/pipeline.mjs.map +1 -1
- package/dist/capture/progress.d.mts +11 -0
- package/dist/capture/progress.mjs +28 -0
- package/dist/capture/progress.mjs.map +1 -0
- package/dist/capture/publish/accept.d.mts +39 -0
- package/dist/capture/publish/accept.mjs +28 -0
- package/dist/capture/publish/accept.mjs.map +1 -0
- package/dist/capture/publish/complete.d.mts +46 -0
- package/dist/capture/publish/complete.mjs +64 -0
- package/dist/capture/publish/complete.mjs.map +1 -0
- package/dist/capture/publish/extract.d.mts +20 -0
- package/dist/capture/publish/extract.mjs +119 -0
- package/dist/capture/publish/extract.mjs.map +1 -0
- package/dist/capture/publish/handler.d.mts +28 -0
- package/dist/capture/publish/handler.mjs +62 -0
- package/dist/capture/publish/handler.mjs.map +1 -0
- package/dist/capture/publish/index.d.mts +10 -0
- package/dist/capture/publish/limits.d.mts +37 -0
- package/dist/capture/publish/limits.mjs +39 -0
- package/dist/capture/publish/limits.mjs.map +1 -0
- package/dist/capture/publish/published.d.mts +15 -0
- package/dist/capture/publish/published.mjs +18 -0
- package/dist/capture/publish/published.mjs.map +1 -0
- package/dist/capture/publish/run.d.mts +33 -0
- package/dist/capture/publish/run.mjs +30 -0
- package/dist/capture/publish/run.mjs.map +1 -0
- package/dist/capture/rediff.d.mts +1 -1
- package/dist/capture/rediff.mjs +4 -3
- package/dist/capture/rediff.mjs.map +1 -1
- package/dist/capture/render.mjs +53 -0
- package/dist/capture/render.mjs.map +1 -0
- package/dist/capture/scratch.d.mts +44 -0
- package/dist/capture/scratch.mjs +138 -0
- package/dist/capture/scratch.mjs.map +1 -0
- package/dist/capture/shard-plan.mjs +128 -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 +174 -0
- package/dist/capture/shards/execute.mjs.map +1 -0
- package/dist/capture/shards/fan-out.mjs +126 -0
- package/dist/capture/shards/fan-out.mjs.map +1 -0
- package/dist/capture/shards/history.mjs +35 -0
- package/dist/capture/shards/history.mjs.map +1 -0
- package/dist/capture/shards/reduce.mjs +97 -0
- package/dist/capture/shards/reduce.mjs.map +1 -0
- package/dist/capture/sizing.mjs +1 -11
- package/dist/capture/sizing.mjs.map +1 -1
- package/dist/capture/skip-checks.mjs +2 -65
- package/dist/capture/skip-checks.mjs.map +1 -1
- package/dist/capture/statics.d.mts +7 -8
- package/dist/capture/statics.mjs +25 -71
- package/dist/capture/statics.mjs.map +1 -1
- package/dist/capture/storybook-urls.d.mts +40 -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 -4
- package/dist/capture/storybook.mjs +56 -16
- package/dist/capture/storybook.mjs.map +1 -1
- package/dist/capture/target.mjs +21 -0
- package/dist/capture/target.mjs.map +1 -0
- package/dist/capture/targets.d.mts +25 -0
- package/dist/capture/targets.mjs +32 -0
- package/dist/capture/targets.mjs.map +1 -0
- package/dist/capture/tests.d.mts +37 -0
- package/dist/capture/tests.mjs +126 -0
- package/dist/capture/tests.mjs.map +1 -0
- package/dist/capture/token-chunks.mjs +74 -0
- package/dist/capture/token-chunks.mjs.map +1 -0
- package/dist/capture.d.mts +60 -7
- package/dist/capture.mjs +48 -3
- package/dist/config.d.mts +121 -12
- package/dist/config.mjs +9 -6
- package/dist/config.mjs.map +1 -1
- package/dist/diff.d.mts +9 -3
- package/dist/diff.mjs +16 -3
- package/dist/diff.mjs.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/insights/budget.d.mts +20 -0
- package/dist/insights/budget.mjs +33 -0
- package/dist/insights/budget.mjs.map +1 -0
- package/dist/insights/envelope.d.mts +59 -0
- package/dist/insights/envelope.mjs +60 -0
- package/dist/insights/envelope.mjs.map +1 -0
- package/dist/insights/evidence-health.d.mts +32 -0
- package/dist/insights/evidence-health.mjs +28 -0
- package/dist/insights/evidence-health.mjs.map +1 -0
- package/dist/insights/evidence.d.mts +55 -0
- package/dist/insights/evidence.mjs +64 -0
- package/dist/insights/evidence.mjs.map +1 -0
- package/dist/insights/hash.d.mts +18 -0
- package/dist/insights/hash.mjs +21 -0
- package/dist/insights/hash.mjs.map +1 -0
- package/dist/insights/limits.d.mts +24 -0
- package/dist/insights/limits.mjs +24 -0
- package/dist/insights/limits.mjs.map +1 -0
- package/dist/insights/prompts/v1.d.mts +13 -0
- package/dist/insights/prompts/v1.mjs +34 -0
- package/dist/insights/prompts/v1.mjs.map +1 -0
- package/dist/insights/redact.d.mts +9 -0
- package/dist/insights/redact.mjs +24 -0
- package/dist/insights/redact.mjs.map +1 -0
- package/dist/insights/text.mjs +44 -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.mjs +17 -1
- package/dist/logger.mjs.map +1 -1
- package/dist/models/ai-budget-alert.d.mts +23 -0
- package/dist/models/ai-budget-alert.mjs +38 -0
- package/dist/models/ai-budget-alert.mjs.map +1 -0
- package/dist/models/ai-usage.d.mts +40 -0
- package/dist/models/ai-usage.mjs +61 -0
- package/dist/models/ai-usage.mjs.map +1 -0
- package/dist/models/baseline-archive.d.mts +84 -0
- package/dist/models/baseline-archive.mjs +106 -0
- package/dist/models/baseline-archive.mjs.map +1 -0
- package/dist/models/baseline-export.d.mts +19 -0
- package/dist/models/baseline-export.mjs +136 -0
- package/dist/models/baseline-export.mjs.map +1 -0
- package/dist/models/baseline-import.mjs +118 -0
- package/dist/models/baseline-import.mjs.map +1 -0
- package/dist/models/baseline-transfer.d.mts +49 -0
- package/dist/models/baseline-transfer.mjs +121 -0
- package/dist/models/baseline-transfer.mjs.map +1 -0
- package/dist/models/baseline.d.mts +32 -5
- package/dist/models/baseline.mjs +65 -20
- package/dist/models/baseline.mjs.map +1 -1
- package/dist/models/build-component.d.mts +28 -0
- package/dist/models/build-component.mjs +55 -0
- package/dist/models/build-component.mjs.map +1 -0
- package/dist/models/build-review-status.d.mts +29 -0
- package/dist/models/build-review-status.mjs +37 -0
- package/dist/models/build-review-status.mjs.map +1 -0
- package/dist/models/build-review.d.mts +28 -0
- package/dist/models/build-review.mjs +54 -0
- package/dist/models/build-review.mjs.map +1 -0
- package/dist/models/build.d.mts +25 -3
- package/dist/models/build.mjs +50 -11
- package/dist/models/build.mjs.map +1 -1
- package/dist/models/capture-attempt.d.mts +7 -1
- package/dist/models/capture-attempt.mjs +29 -4
- package/dist/models/capture-attempt.mjs.map +1 -1
- package/dist/models/capture-job.d.mts +106 -0
- package/dist/models/capture-job.mjs +288 -0
- package/dist/models/capture-job.mjs.map +1 -0
- package/dist/models/capture-log.d.mts +1 -1
- package/dist/models/capture-log.mjs +4 -3
- package/dist/models/capture-log.mjs.map +1 -1
- package/dist/models/capture-shard.d.mts +62 -0
- package/dist/models/capture-shard.mjs +190 -0
- package/dist/models/capture-shard.mjs.map +1 -0
- package/dist/models/changeset.d.mts +48 -0
- package/dist/models/changeset.mjs +81 -0
- package/dist/models/changeset.mjs.map +1 -0
- package/dist/models/columns.mjs +19 -0
- package/dist/models/columns.mjs.map +1 -0
- package/dist/models/comment.d.mts +4 -1
- package/dist/models/comment.mjs +7 -4
- package/dist/models/comment.mjs.map +1 -1
- package/dist/models/component.d.mts +43 -0
- package/dist/models/component.mjs +60 -0
- package/dist/models/component.mjs.map +1 -0
- package/dist/models/connections.d.mts +41 -0
- package/dist/models/connections.mjs +112 -0
- package/dist/models/connections.mjs.map +1 -0
- package/dist/models/content-ref.d.mts +1 -1
- package/dist/models/content-ref.mjs +1 -1
- package/dist/models/content-ref.mjs.map +1 -1
- package/dist/models/credentials.mjs +12 -2
- package/dist/models/credentials.mjs.map +1 -1
- package/dist/models/design-credentials.d.mts +41 -0
- package/dist/models/design-credentials.mjs +86 -0
- package/dist/models/design-credentials.mjs.map +1 -0
- package/dist/models/design-health.d.mts +16 -0
- package/dist/models/design-health.mjs +25 -0
- package/dist/models/design-health.mjs.map +1 -0
- package/dist/models/design-nodes.d.mts +83 -0
- package/dist/models/design-nodes.mjs +222 -0
- package/dist/models/design-nodes.mjs.map +1 -0
- package/dist/models/design-renders.mjs +74 -0
- package/dist/models/design-renders.mjs.map +1 -0
- package/dist/models/design-resolve.d.mts +29 -0
- package/dist/models/design-resolve.mjs +41 -0
- package/dist/models/design-resolve.mjs.map +1 -0
- package/dist/models/design.d.mts +39 -0
- package/dist/models/design.mjs +90 -0
- package/dist/models/design.mjs.map +1 -0
- package/dist/models/inherited-ratio.d.mts +21 -0
- package/dist/models/inherited-ratio.mjs +22 -0
- package/dist/models/inherited-ratio.mjs.map +1 -0
- package/dist/models/insight.d.mts +70 -0
- package/dist/models/insight.mjs +162 -0
- package/dist/models/insight.mjs.map +1 -0
- package/dist/models/label.d.mts +4 -2
- package/dist/models/label.mjs +25 -10
- package/dist/models/label.mjs.map +1 -1
- package/dist/models/member.d.mts +1 -1
- package/dist/models/member.mjs +5 -4
- package/dist/models/member.mjs.map +1 -1
- package/dist/models/notification-channel.d.mts +37 -3
- package/dist/models/notification-channel.mjs +88 -9
- package/dist/models/notification-channel.mjs.map +1 -1
- package/dist/models/notification-subscription.d.mts +1 -2
- package/dist/models/notification-subscription.mjs +7 -9
- package/dist/models/notification-subscription.mjs.map +1 -1
- package/dist/models/project-group-mapping.d.mts +6 -1
- package/dist/models/project-group-mapping.mjs +14 -4
- package/dist/models/project-group-mapping.mjs.map +1 -1
- package/dist/models/project.d.mts +9 -1
- package/dist/models/project.mjs +9 -4
- package/dist/models/project.mjs.map +1 -1
- package/dist/models/public-origin.d.mts +63 -0
- package/dist/models/public-origin.mjs +114 -0
- package/dist/models/public-origin.mjs.map +1 -0
- package/dist/models/review-switch-off.d.mts +14 -0
- package/dist/models/review-switch-off.mjs +26 -0
- package/dist/models/review-switch-off.mjs.map +1 -0
- package/dist/models/site-settings.d.mts +23 -0
- package/dist/models/site-settings.mjs +48 -0
- package/dist/models/site-settings.mjs.map +1 -0
- package/dist/models/snapshot.d.mts +36 -1
- package/dist/models/snapshot.mjs +50 -3
- package/dist/models/snapshot.mjs.map +1 -1
- package/dist/models/token.d.mts +13 -1
- package/dist/models/token.mjs +23 -5
- package/dist/models/token.mjs.map +1 -1
- package/dist/models/usage.d.mts +76 -0
- package/dist/models/usage.mjs +149 -0
- package/dist/models/usage.mjs.map +1 -0
- package/dist/models/user.d.mts +1 -1
- package/dist/models/user.mjs +4 -3
- package/dist/models/user.mjs.map +1 -1
- package/dist/models/webhook.d.mts +15 -1
- package/dist/models/webhook.mjs +33 -4
- package/dist/models/webhook.mjs.map +1 -1
- package/dist/models.d.mts +26 -3
- package/dist/models.mjs +27 -5
- package/dist/options.d.mts +18 -0
- package/dist/options.mjs +90 -0
- package/dist/options.mjs.map +1 -0
- package/dist/paths.d.mts +19 -4
- package/dist/paths.mjs +51 -10
- package/dist/paths.mjs.map +1 -1
- package/dist/retention/ai.d.mts +18 -0
- package/dist/retention/ai.mjs +23 -0
- package/dist/retention/ai.mjs.map +1 -0
- package/dist/retention/pending.mjs +47 -0
- package/dist/retention/pending.mjs.map +1 -0
- package/dist/retention/screenshot-sweep.d.mts +51 -0
- package/dist/retention/screenshot-sweep.mjs +128 -0
- package/dist/retention/screenshot-sweep.mjs.map +1 -0
- package/dist/retention.d.mts +16 -2
- package/dist/retention.mjs +71 -14
- package/dist/retention.mjs.map +1 -1
- package/dist/schema/baseline.d.mts +6 -0
- package/dist/schema/build-component.d.mts +25 -0
- package/dist/schema/build-review.d.mts +17 -0
- package/dist/schema/build.d.mts +7 -0
- package/dist/schema/capture-job.d.mts +60 -0
- package/dist/schema/capture-shard.d.mts +35 -0
- package/dist/schema/comment.d.mts +2 -0
- package/dist/schema/connection.d.mts +24 -0
- package/dist/schema/design.d.mts +58 -0
- package/dist/schema/insight.d.mts +54 -0
- package/dist/schema/notification.d.mts +4 -0
- package/dist/schema/project.d.mts +21 -0
- package/dist/schema/site-setting.d.mts +21 -0
- package/dist/schema/snapshot.d.mts +12 -0
- package/dist/schema/usage.d.mts +30 -0
- package/dist/schema.d.mts +11 -3
- package/dist/serverless.d.mts +21 -0
- package/dist/serverless.mjs +37 -0
- package/dist/serverless.mjs.map +1 -0
- package/dist/test-helpers/create-project.d.mts +1 -1
- package/dist/test-helpers/deferred.d.mts +39 -0
- package/dist/test-helpers/deferred.mjs +61 -0
- package/dist/test-helpers/deferred.mjs.map +1 -0
- package/dist/test-helpers/design-contract.d.mts +21 -0
- package/dist/test-helpers/design-contract.mjs +40 -0
- package/dist/test-helpers/design-contract.mjs.map +1 -0
- package/dist/test-helpers/fake-database.mjs +14 -2
- package/dist/test-helpers/fake-database.mjs.map +1 -1
- package/dist/test-helpers/fake-design.d.mts +43 -0
- package/dist/test-helpers/fake-design.mjs +114 -0
- package/dist/test-helpers/fake-design.mjs.map +1 -0
- package/dist/test-helpers/fake-notifier.d.mts +21 -0
- package/dist/test-helpers/fake-notifier.mjs +44 -0
- package/dist/test-helpers/fake-notifier.mjs.map +1 -0
- package/dist/test-helpers/fake-tables.mjs +255 -67
- package/dist/test-helpers/fake-tables.mjs.map +1 -1
- package/dist/test-helpers/manual-clock.d.mts +27 -0
- package/dist/test-helpers/manual-clock.mjs +56 -0
- package/dist/test-helpers/manual-clock.mjs.map +1 -0
- package/dist/test-helpers/notifier-contract.d.mts +7 -1
- package/dist/test-helpers/notifier-contract.mjs +32 -3
- package/dist/test-helpers/notifier-contract.mjs.map +1 -1
- package/dist/test-helpers/queue-contract.d.mts +16 -2
- package/dist/test-helpers/queue-contract.mjs +195 -1
- package/dist/test-helpers/queue-contract.mjs.map +1 -1
- package/dist/test-helpers/queue-multi-instance.d.mts +20 -0
- package/dist/test-helpers/queue-multi-instance.mjs +206 -0
- package/dist/test-helpers/queue-multi-instance.mjs.map +1 -0
- package/dist/test-helpers/runner-contract.mjs +11 -0
- package/dist/test-helpers/runner-contract.mjs.map +1 -1
- package/dist/test-helpers/sql-chunks.mjs +23 -0
- package/dist/test-helpers/sql-chunks.mjs.map +1 -1
- package/dist/test-helpers/storage-contract.mjs +1 -1
- package/dist/test-helpers.d.mts +9 -4
- package/dist/test-helpers.mjs +8 -3
- package/dist/types.d.mts +14 -3
- package/dist/types.mjs +21 -4
- package/dist/types.mjs.map +1 -1
- package/dist/urls.d.mts +4 -0
- package/dist/urls.mjs +11 -2
- package/dist/urls.mjs.map +1 -1
- package/dist/utils/adaptive-concurrency.d.mts +66 -0
- package/dist/utils/adaptive-concurrency.mjs +149 -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 +43 -0
- package/dist/utils/connect-retry.mjs.map +1 -0
- package/dist/utils/environment.d.mts +19 -0
- package/dist/utils/environment.mjs +59 -0
- package/dist/utils/environment.mjs.map +1 -0
- package/dist/utils/http.d.mts +17 -1
- package/dist/utils/http.mjs +22 -3
- package/dist/utils/http.mjs.map +1 -1
- package/dist/utils/map-adaptive.d.mts +15 -0
- package/dist/utils/map-adaptive.mjs +72 -0
- package/dist/utils/map-adaptive.mjs.map +1 -0
- package/dist/utils/map-limit.d.mts +10 -0
- package/dist/utils/map-limit.mjs +32 -0
- package/dist/utils/map-limit.mjs.map +1 -0
- package/dist/utils/page-limiter.d.mts +37 -0
- package/dist/utils/page-limiter.mjs +56 -0
- package/dist/utils/page-limiter.mjs.map +1 -0
- package/dist/utils/public-origin.d.mts +20 -0
- package/dist/utils/public-origin.mjs +75 -0
- package/dist/utils/public-origin.mjs.map +1 -0
- package/dist/utils/redact.mjs +3 -0
- package/dist/utils/redact.mjs.map +1 -1
- package/dist/utils/remote-environment.d.mts +39 -0
- package/dist/utils/remote-environment.mjs +114 -0
- package/dist/utils/remote-environment.mjs.map +1 -0
- package/dist/utils/resources.d.mts +64 -0
- package/dist/utils/resources.mjs +122 -0
- package/dist/utils/resources.mjs.map +1 -0
- package/dist/utils/screenshot-store.d.mts +15 -0
- package/dist/utils/screenshot-store.mjs +41 -0
- package/dist/utils/screenshot-store.mjs.map +1 -0
- package/dist/utils/session-gate.d.mts +22 -0
- package/dist/utils/session-gate.mjs +53 -0
- package/dist/utils/session-gate.mjs.map +1 -0
- package/dist/utils/storybook-token.d.mts +81 -0
- package/dist/utils/storybook-token.mjs +134 -0
- package/dist/utils/storybook-token.mjs.map +1 -0
- package/dist/utils/storybook-url.d.mts +15 -0
- package/dist/utils/storybook-url.mjs +21 -0
- package/dist/utils/storybook-url.mjs.map +1 -0
- package/dist/utils/timing.mjs +1 -1
- package/dist/utils/timing.mjs.map +1 -1
- package/dist/utils/zip-read.mjs +129 -0
- package/dist/utils/zip-read.mjs.map +1 -0
- package/dist/utils/zip-write.d.mts +18 -0
- package/dist/utils/zip-write.mjs +188 -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 +32 -23
- 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
|
@@ -1,18 +1,38 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
import {
|
|
5
|
-
import { DEFAULT_DIFF_OPTIONS } from "../diff/options.mjs";
|
|
4
|
+
import { DEFAULT_DIFF_OPTIONS, diffImagesAsync } from "@storyshelf/image";
|
|
6
5
|
//#region src/capture/diff-record.ts
|
|
6
|
+
/** Diff a stored screenshot against a baseline and persist the overlay; shared by capture and re-diff. */
|
|
7
7
|
/** Per-story `diffThreshold` when it is a finite number in 0..1; otherwise the project threshold. */
|
|
8
8
|
function resolvePixelThreshold(projectThreshold, override) {
|
|
9
9
|
return typeof override === "number" && Number.isFinite(override) && override >= 0 && override <= 1 ? override : projectThreshold;
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Content-addressed screenshots share a path exactly when their bytes are equal, so the diff is
|
|
13
|
+
* empty without reading either image. Dimensions are not known (nothing was read) and report 0.
|
|
14
|
+
*/
|
|
15
|
+
function samePathResult() {
|
|
16
|
+
const unread = {
|
|
17
|
+
width: 0,
|
|
18
|
+
height: 0
|
|
19
|
+
};
|
|
20
|
+
return {
|
|
21
|
+
passed: true,
|
|
22
|
+
diffPixels: 0,
|
|
23
|
+
diffRatio: 0,
|
|
24
|
+
diffImage: null,
|
|
25
|
+
baselineDimensions: unread,
|
|
26
|
+
currentDimensions: unread,
|
|
27
|
+
sizeChanged: false
|
|
28
|
+
};
|
|
29
|
+
}
|
|
11
30
|
/** Read two stored PNGs and diff them with the project's thresholds (optionally a per-story pixel threshold). */
|
|
12
31
|
async function diffStoredScreenshots(storage, project, baselinePath, currentPath, diffThreshold) {
|
|
32
|
+
if (baselinePath === currentPath) return samePathResult();
|
|
13
33
|
const current = await storage.read(currentPath);
|
|
14
34
|
const previous = await storage.read(baselinePath);
|
|
15
|
-
return
|
|
35
|
+
return await diffImagesAsync(previous, current, {
|
|
16
36
|
...DEFAULT_DIFF_OPTIONS,
|
|
17
37
|
pixelThreshold: resolvePixelThreshold(project.pixelThreshold, diffThreshold),
|
|
18
38
|
maxDiffRatio: project.maxDiffRatio
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff-record.mjs","names":[],"sources":["../../src/capture/diff-record.ts"],"sourcesContent":["/** Diff a stored screenshot against a baseline and persist the overlay; shared by capture and re-diff. */\nimport
|
|
1
|
+
{"version":3,"file":"diff-record.mjs","names":[],"sources":["../../src/capture/diff-record.ts"],"sourcesContent":["/** Diff a stored screenshot against a baseline and persist the overlay; shared by capture and re-diff. */\nimport { DEFAULT_DIFF_OPTIONS, diffImagesAsync, type DiffResult } from \"@storyshelf/image\";\nimport type { StorageAdapter } from \"../adapters/storage.ts\";\n\n/** Project thresholds that drive a diff. */\nexport interface DiffThresholds {\n pixelThreshold: number;\n maxDiffRatio: number;\n}\n\n/** Per-story `diffThreshold` when it is a finite number in 0..1; otherwise the project threshold. */\nexport function resolvePixelThreshold(projectThreshold: number, override?: number): number {\n const valid = typeof override === \"number\" && Number.isFinite(override);\n return valid && override >= 0 && override <= 1 ? override : projectThreshold;\n}\n\n/**\n * Content-addressed screenshots share a path exactly when their bytes are equal, so the diff is\n * empty without reading either image. Dimensions are not known (nothing was read) and report 0.\n */\nfunction samePathResult(): DiffResult {\n const unread = { width: 0, height: 0 };\n return {\n passed: true,\n diffPixels: 0,\n diffRatio: 0,\n diffImage: null,\n baselineDimensions: unread,\n currentDimensions: unread,\n sizeChanged: false,\n };\n}\n\n/** Read two stored PNGs and diff them with the project's thresholds (optionally a per-story pixel threshold). */\nexport async function diffStoredScreenshots(\n storage: StorageAdapter,\n project: DiffThresholds,\n baselinePath: string,\n currentPath: string,\n diffThreshold?: number,\n): Promise<DiffResult> {\n if (baselinePath === currentPath) {\n return samePathResult();\n }\n const current = await storage.read(currentPath);\n const previous = await storage.read(baselinePath);\n return await diffImagesAsync(previous, current, {\n ...DEFAULT_DIFF_OPTIONS,\n pixelThreshold: resolvePixelThreshold(project.pixelThreshold, diffThreshold),\n maxDiffRatio: project.maxDiffRatio,\n });\n}\n\n/** Write the diff overlay for a failing diff; returns the stored path, or null when none applies. */\nexport async function writeDiffOverlay(\n storage: StorageAdapter,\n path: string,\n result: Pick<DiffResult, \"diffImage\">,\n passed: boolean,\n): Promise<string | null> {\n if (passed || !result.diffImage) {\n return null;\n }\n await storage.write(path, result.diffImage);\n return path;\n}\n"],"mappings":";;;;;;;AAWA,SAAgB,sBAAsB,kBAA0B,UAA2B;CAEzF,OADc,OAAO,aAAa,YAAY,OAAO,SAAS,QAAQ,KACtD,YAAY,KAAK,YAAY,IAAI,WAAW;AAC9D;;;;;AAMA,SAAS,iBAA6B;CACpC,MAAM,SAAS;EAAE,OAAO;EAAG,QAAQ;CAAE;CACrC,OAAO;EACL,QAAQ;EACR,YAAY;EACZ,WAAW;EACX,WAAW;EACX,oBAAoB;EACpB,mBAAmB;EACnB,aAAa;CACf;AACF;;AAGA,eAAsB,sBACpB,SACA,SACA,cACA,aACA,eACqB;CACrB,IAAI,iBAAiB,aACnB,OAAO,eAAe;CAExB,MAAM,UAAU,MAAM,QAAQ,KAAK,WAAW;CAC9C,MAAM,WAAW,MAAM,QAAQ,KAAK,YAAY;CAChD,OAAO,MAAM,gBAAgB,UAAU,SAAS;EAC9C,GAAG;EACH,gBAAgB,sBAAsB,QAAQ,gBAAgB,aAAa;EAC3E,cAAc,QAAQ;CACxB,CAAC;AACH;;AAGA,eAAsB,iBACpB,SACA,MACA,QACA,QACwB;CACxB,IAAI,UAAU,CAAC,OAAO,WACpB,OAAO;CAET,MAAM,QAAQ,MAAM,MAAM,OAAO,SAAS;CAC1C,OAAO;AACT"}
|
|
@@ -1,32 +1,37 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
3
|
import { DatabaseAdapter } from "../adapter/database.mjs";
|
|
4
|
-
import { GitHostProvider } from "../adapter/git-host.mjs";
|
|
5
4
|
import { Logger } from "../logger.mjs";
|
|
6
|
-
import { SecretInput } from "../utils/encrypt.mjs";
|
|
7
5
|
import { BuildTables } from "../models/build.mjs";
|
|
8
6
|
import { CaptureAttemptTables } from "../models/capture-attempt.mjs";
|
|
9
7
|
import { CaptureLogTables } from "../models/capture-log.mjs";
|
|
10
8
|
import { ProjectTables } from "../models/project.mjs";
|
|
9
|
+
import { JobLease } from "./job-lease.mjs";
|
|
11
10
|
import { CaptureJobOptions } from "./orchestrator.mjs";
|
|
12
|
-
import {
|
|
11
|
+
import { BuildStatePublisher } from "./build-state/types.mjs";
|
|
13
12
|
//#region src/capture/dispatch.d.ts
|
|
14
13
|
/** Build the queue's job runner: post pending, apply skip rules, capture, report. */
|
|
15
|
-
declare function createDispatchJob(deps: DispatchDeps): (job: CaptureDispatchJob) => Promise<void>;
|
|
14
|
+
declare function createDispatchJob(deps: DispatchDeps): (job: CaptureDispatchJob, lease?: JobLease) => Promise<void>;
|
|
16
15
|
/** Table handles required by dispatch. */
|
|
17
|
-
type DispatchTables = BuildTables & ProjectTables &
|
|
16
|
+
type DispatchTables = BuildTables & ProjectTables & CaptureAttemptTables & CaptureLogTables;
|
|
18
17
|
/** Dependencies for building the capture queue's job runner. */
|
|
19
18
|
interface DispatchDeps {
|
|
20
19
|
db: DatabaseAdapter;
|
|
21
20
|
tables: DispatchTables;
|
|
22
21
|
jobOptions: CaptureJobOptions;
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
/** Shows a build's current state on its git channels; called at every transition. */
|
|
23
|
+
buildState: BuildStatePublisher;
|
|
25
24
|
logger: Logger;
|
|
25
|
+
/** Redeliveries the queue allows a failing job; a shard's last try is its final word. */
|
|
26
|
+
maxRetries?: number | undefined;
|
|
26
27
|
}
|
|
27
28
|
/** A queued capture job. */
|
|
28
29
|
interface CaptureDispatchJob {
|
|
29
30
|
buildId: string;
|
|
31
|
+
/** A `capture_shards` row to render; absent for the build's own (plan or whole-build) job. */
|
|
32
|
+
shard?: string | undefined;
|
|
33
|
+
/** Times the queue has already delivered this job and seen it fail. */
|
|
34
|
+
attempts?: number | undefined;
|
|
30
35
|
reqId?: string;
|
|
31
36
|
/** W3C `traceparent` continuing the enqueueing request's trace. */
|
|
32
37
|
traceparent?: string;
|
|
@@ -1,40 +1,50 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
import {
|
|
4
|
+
import { redactSecrets } from "../utils/redact.mjs";
|
|
5
5
|
import { CaptureAttemptModel, emitAttemptLog } from "../models/capture-attempt.mjs";
|
|
6
|
+
import { BuildModel } from "../models/build.mjs";
|
|
7
|
+
import { CaptureShardModel } from "../models/capture-shard.mjs";
|
|
6
8
|
import { ProjectModel } from "../models/project.mjs";
|
|
7
9
|
import { executeCaptureJob } from "./orchestrator.mjs";
|
|
8
|
-
import { postStatusesForBuild } from "./status-fanout.mjs";
|
|
9
10
|
import { CaptureLogModel } from "../models/capture-log.mjs";
|
|
10
|
-
import {
|
|
11
|
+
import { leaseProgress } from "./progress.mjs";
|
|
12
|
+
import { executeCaptureShard } from "./shards/execute.mjs";
|
|
13
|
+
import { hasApprovedBuildForSha } from "./skip-checks.mjs";
|
|
11
14
|
//#region src/capture/dispatch.ts
|
|
12
15
|
/** Build the queue's job runner: post pending, apply skip rules, capture, report. */
|
|
13
16
|
function createDispatchJob(deps) {
|
|
14
|
-
return async (job) => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
17
|
+
return async (job, lease) => {
|
|
18
|
+
await (job.shard ? runShardJob(deps, {
|
|
19
|
+
...job,
|
|
20
|
+
shard: job.shard
|
|
21
|
+
}, lease) : runBuildJob(deps, job, lease));
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** The build's own job: post pending, apply skip rules, capture (or plan), report. */
|
|
25
|
+
async function runBuildJob(deps, job, lease) {
|
|
26
|
+
const target = await loadJobTarget(deps, job);
|
|
27
|
+
if (!target) return;
|
|
28
|
+
await deps.buildState.request(job.buildId);
|
|
29
|
+
const { attempt, recordLog } = await beginAttempt(deps, target.project, target.build, job.reqId, lease);
|
|
30
|
+
const ctx = {
|
|
31
|
+
deps,
|
|
32
|
+
build: target.build,
|
|
33
|
+
project: target.project,
|
|
34
|
+
attempt,
|
|
35
|
+
recordLog,
|
|
36
|
+
progress: leaseProgress(lease)
|
|
30
37
|
};
|
|
38
|
+
if (await maybeSkipDuplicate(ctx)) return;
|
|
39
|
+
await runAndReport(ctx, job);
|
|
31
40
|
}
|
|
32
41
|
/** Open a new attempt row for this run and return its log recorder. */
|
|
33
|
-
async function beginAttempt(deps, project, build, reqId) {
|
|
42
|
+
async function beginAttempt(deps, project, build, reqId, lease) {
|
|
34
43
|
const attempts = new CaptureAttemptModel(deps.db, deps.tables);
|
|
35
44
|
const logs = new CaptureLogModel(deps.db, deps.tables);
|
|
36
45
|
const attempt = await attempts.startAttempt(project.id, build.id, reqId);
|
|
37
46
|
const recordLog = async (level, message, fields) => {
|
|
47
|
+
lease?.touch();
|
|
38
48
|
await logs.append(project.id, build.id, attempt.id, level, message, fields);
|
|
39
49
|
};
|
|
40
50
|
await emitAttemptLog(recordLog, deps.logger, "info", "capture queued", {
|
|
@@ -54,41 +64,10 @@ async function finishAttempt(ctx, status, error, counts) {
|
|
|
54
64
|
}
|
|
55
65
|
/** Full error text for an attempt row (message plus stack when available). */
|
|
56
66
|
function errorText(reason) {
|
|
57
|
-
|
|
58
|
-
return String(reason);
|
|
59
|
-
}
|
|
60
|
-
async function postPending(deps, project, url, sha) {
|
|
61
|
-
await postStatusesForBuild({
|
|
62
|
-
db: deps.db,
|
|
63
|
-
tables: deps.tables,
|
|
64
|
-
project,
|
|
65
|
-
sha,
|
|
66
|
-
status: "pending",
|
|
67
|
-
url,
|
|
68
|
-
providers: deps.gitHosts,
|
|
69
|
-
secret: deps.secret,
|
|
70
|
-
logger: deps.logger
|
|
71
|
-
}).catch((error) => {
|
|
72
|
-
deps.logger.error({ err: error }, "failed to post pending status");
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
async function postTerminal(deps, project, url, sha, status) {
|
|
76
|
-
await postStatusesForBuild({
|
|
77
|
-
db: deps.db,
|
|
78
|
-
tables: deps.tables,
|
|
79
|
-
project,
|
|
80
|
-
sha,
|
|
81
|
-
status,
|
|
82
|
-
url,
|
|
83
|
-
providers: deps.gitHosts,
|
|
84
|
-
secret: deps.secret,
|
|
85
|
-
logger: deps.logger
|
|
86
|
-
}).catch((error) => {
|
|
87
|
-
deps.logger.error({ err: error }, "failed to post terminal status");
|
|
88
|
-
});
|
|
67
|
+
return redactSecrets(reason instanceof Error ? reason.stack ?? reason.message : String(reason));
|
|
89
68
|
}
|
|
90
69
|
async function approveWithoutCapture(ctx, reason) {
|
|
91
|
-
const { deps, build
|
|
70
|
+
const { deps, build } = ctx;
|
|
92
71
|
deps.logger.info({
|
|
93
72
|
buildId: build.id,
|
|
94
73
|
sha: build.gitSha,
|
|
@@ -100,25 +79,7 @@ async function approveWithoutCapture(ctx, reason) {
|
|
|
100
79
|
});
|
|
101
80
|
await finishAttempt(ctx, "completed");
|
|
102
81
|
await new BuildModel(deps.db, deps.tables).setStatus(build.id, "approved").catch(() => {});
|
|
103
|
-
await
|
|
104
|
-
}
|
|
105
|
-
async function maybeSkipMerged(ctx) {
|
|
106
|
-
const { deps, build, project } = ctx;
|
|
107
|
-
if (build.isDefault) return false;
|
|
108
|
-
if (await isAlreadyMerged({
|
|
109
|
-
providers: deps.gitHosts,
|
|
110
|
-
sha: build.gitSha,
|
|
111
|
-
branch: build.gitBranch,
|
|
112
|
-
secret: deps.secret,
|
|
113
|
-
db: deps.db,
|
|
114
|
-
tables: deps.tables,
|
|
115
|
-
projectId: project.id,
|
|
116
|
-
logger: deps.logger
|
|
117
|
-
}).catch(() => false)) {
|
|
118
|
-
await approveWithoutCapture(ctx, "skipping capture — already merged");
|
|
119
|
-
return true;
|
|
120
|
-
}
|
|
121
|
-
return false;
|
|
82
|
+
await deps.buildState.request(build.id);
|
|
122
83
|
}
|
|
123
84
|
async function maybeSkipDuplicate(ctx) {
|
|
124
85
|
const { deps, build, project } = ctx;
|
|
@@ -128,17 +89,14 @@ async function maybeSkipDuplicate(ctx) {
|
|
|
128
89
|
}
|
|
129
90
|
return false;
|
|
130
91
|
}
|
|
131
|
-
/**
|
|
132
|
-
function
|
|
133
|
-
if (
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const { deps, build, project, pendingUrl } = ctx;
|
|
140
|
-
const terminal = terminalState((await new BuildModel(deps.db, deps.tables).get(buildId))?.status);
|
|
141
|
-
if (terminal) await postTerminal(deps, project, pendingUrl, build.gitSha, terminal);
|
|
92
|
+
/** A sharded build's attempt stays open: the worker that finishes the last shard closes it. */
|
|
93
|
+
async function finishUnlessSharded(ctx, summary) {
|
|
94
|
+
if (summary.shards) {
|
|
95
|
+
await emitAttemptLog(ctx.recordLog, ctx.deps.logger, "info", "capture sharded", { shards: summary.shards });
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
await finishAttempt(ctx, "completed", void 0, summary);
|
|
99
|
+
await ctx.deps.buildState.request(ctx.build.id);
|
|
142
100
|
}
|
|
143
101
|
async function runAndReport(ctx, job) {
|
|
144
102
|
const { deps, build } = ctx;
|
|
@@ -148,7 +106,7 @@ async function runAndReport(ctx, job) {
|
|
|
148
106
|
attemptNo: ctx.attempt.attemptNo
|
|
149
107
|
});
|
|
150
108
|
try {
|
|
151
|
-
await
|
|
109
|
+
await finishUnlessSharded(ctx, await executeCaptureJob({
|
|
152
110
|
buildId: job.buildId,
|
|
153
111
|
reqId: job.reqId,
|
|
154
112
|
traceparent: job.traceparent,
|
|
@@ -157,38 +115,20 @@ async function runAndReport(ctx, job) {
|
|
|
157
115
|
attemptNo: ctx.attempt.attemptNo,
|
|
158
116
|
projectId: ctx.project.id
|
|
159
117
|
},
|
|
160
|
-
recordLog: ctx.recordLog
|
|
118
|
+
recordLog: ctx.recordLog,
|
|
119
|
+
progress: ctx.progress
|
|
161
120
|
}, deps.jobOptions));
|
|
162
|
-
await reportTerminalStatus(ctx, job.buildId);
|
|
163
121
|
} catch (error) {
|
|
164
122
|
await finishAttempt(ctx, "failed", errorText(error));
|
|
165
|
-
await
|
|
166
|
-
db: deps.db,
|
|
167
|
-
tables: deps.tables,
|
|
168
|
-
project: ctx.project,
|
|
169
|
-
sha: build.gitSha,
|
|
170
|
-
status: "failure",
|
|
171
|
-
url: ctx.pendingUrl,
|
|
172
|
-
providers: deps.gitHosts,
|
|
173
|
-
secret: deps.secret,
|
|
174
|
-
logger: deps.logger
|
|
175
|
-
}).catch(() => {});
|
|
123
|
+
await deps.buildState.request(build.id);
|
|
176
124
|
throw error;
|
|
177
125
|
}
|
|
178
126
|
}
|
|
179
127
|
/** Load the build + project for a job, running orphan jobs directly. */
|
|
180
128
|
async function loadJobTarget(deps, job) {
|
|
181
129
|
const build = await new BuildModel(deps.db, deps.tables).get(job.buildId);
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
buildId: job.buildId,
|
|
185
|
-
reqId: job.reqId,
|
|
186
|
-
traceparent: job.traceparent
|
|
187
|
-
}, deps.jobOptions);
|
|
188
|
-
return null;
|
|
189
|
-
}
|
|
190
|
-
const project = await new ProjectModel(deps.db, deps.tables).get(build.projectId);
|
|
191
|
-
if (!project) {
|
|
130
|
+
const project = build ? await new ProjectModel(deps.db, deps.tables).get(build.projectId) : null;
|
|
131
|
+
if (!build || !project) {
|
|
192
132
|
await executeCaptureJob({
|
|
193
133
|
buildId: job.buildId,
|
|
194
134
|
reqId: job.reqId,
|
|
@@ -201,6 +141,45 @@ async function loadJobTarget(deps, job) {
|
|
|
201
141
|
project
|
|
202
142
|
};
|
|
203
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* Run one render shard. Only the run that finalized the build closes the plan's attempt and posts
|
|
146
|
+
* the terminal status, whether its shard succeeded or failed; every other run leaves them alone.
|
|
147
|
+
*/
|
|
148
|
+
async function runShardJob(deps, job, lease) {
|
|
149
|
+
const final = (job.attempts ?? 0) >= (deps.maxRetries ?? 0);
|
|
150
|
+
await executeCaptureShard({
|
|
151
|
+
buildId: job.buildId,
|
|
152
|
+
shard: job.shard,
|
|
153
|
+
reqId: job.reqId,
|
|
154
|
+
final,
|
|
155
|
+
progress: leaseProgress(lease),
|
|
156
|
+
onFinalized: async (summary, error) => {
|
|
157
|
+
await closeShardedBuild(deps, job.buildId, summary, error);
|
|
158
|
+
}
|
|
159
|
+
}, deps.jobOptions);
|
|
160
|
+
}
|
|
161
|
+
/** End the plan's attempt with the finalizer's counts and report the build's terminal status. */
|
|
162
|
+
async function closeShardedBuild(deps, buildId, summary, error) {
|
|
163
|
+
if (!await loadClosing(deps, buildId)) return;
|
|
164
|
+
const failure = error === void 0 ? summary.error : errorText(error);
|
|
165
|
+
const failed = failure !== void 0 || summary.buildStatus === "failed";
|
|
166
|
+
const attemptId = summary.attemptId ?? await planAttemptId(deps, buildId);
|
|
167
|
+
if (attemptId) await new CaptureAttemptModel(deps.db, deps.tables).markFinished(attemptId, failed ? "failed" : "completed", failure, summary).catch((closeError) => {
|
|
168
|
+
deps.logger.error({ err: closeError }, "failed to close attempt");
|
|
169
|
+
});
|
|
170
|
+
await deps.buildState.request(buildId);
|
|
171
|
+
}
|
|
172
|
+
async function planAttemptId(deps, buildId) {
|
|
173
|
+
return (await new CaptureShardModel(deps.db).plan(buildId))?.attemptId ?? null;
|
|
174
|
+
}
|
|
175
|
+
async function loadClosing(deps, buildId) {
|
|
176
|
+
const build = await new BuildModel(deps.db, deps.tables).get(buildId);
|
|
177
|
+
const project = build ? await new ProjectModel(deps.db, deps.tables).get(build.projectId) : null;
|
|
178
|
+
return build && project ? {
|
|
179
|
+
build,
|
|
180
|
+
project
|
|
181
|
+
} : null;
|
|
182
|
+
}
|
|
204
183
|
//#endregion
|
|
205
184
|
export { createDispatchJob };
|
|
206
185
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch.mjs","names":[],"sources":["../../src/capture/dispatch.ts"],"sourcesContent":["import type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { GitHostProvider } from \"../adapters/git-host/index.ts\";\nimport type { Logger } from \"../logger.ts\";\nimport { BuildModel, type BuildTables } from \"../models/build.ts\";\nimport {\n CaptureAttemptModel,\n emitAttemptLog,\n type AttemptLogRecorder,\n type CaptureAttemptTables,\n} from \"../models/capture-attempt.ts\";\nimport { CaptureLogModel, type CaptureLogTables } from \"../models/capture-log.ts\";\nimport { ProjectModel, type ProjectTables } from \"../models/project.ts\";\nimport type { StatusConfigTables } from \"../models/status-config.ts\";\nimport type { Build } from \"../schema/build.ts\";\nimport type { CaptureAttempt } from \"../schema/capture-attempt.ts\";\nimport type { Project } from \"../schema/project.ts\";\nimport type { SecretInput } from \"../utils/encrypt.ts\";\nimport { executeCaptureJob, type CaptureJobOptions } from \"./orchestrator.ts\";\nimport { hasApprovedBuildForSha, isAlreadyMerged } from \"./skip-checks.ts\";\nimport { postStatusesForBuild } from \"./status-fanout.ts\";\n\n/** Build the queue's job runner: post pending, apply skip rules, capture, report. */\nexport function createDispatchJob(deps: DispatchDeps): (job: CaptureDispatchJob) => Promise<void> {\n return async (job: CaptureDispatchJob): Promise<void> => {\n const target = await loadJobTarget(deps, job);\n if (!target) {\n return;\n }\n const pendingUrl = `/projects/${target.project.slug}/builds/${job.buildId}`;\n await postPending(deps, target.project, pendingUrl, target.build.gitSha);\n const { attempt, recordLog } = await beginAttempt(\n deps,\n target.project,\n target.build,\n job.reqId,\n );\n const ctx: JobContext = {\n deps,\n build: target.build,\n project: target.project,\n pendingUrl,\n attempt,\n recordLog,\n };\n if ((await maybeSkipMerged(ctx)) || (await maybeSkipDuplicate(ctx))) {\n return;\n }\n await runAndReport(ctx, job);\n };\n}\n\n/** Table handles required by dispatch. */\nexport type DispatchTables = BuildTables &\n ProjectTables &\n StatusConfigTables &\n CaptureAttemptTables &\n CaptureLogTables;\n\n/** Dependencies for building the capture queue's job runner. */\nexport interface DispatchDeps {\n db: DatabaseAdapter;\n tables: DispatchTables;\n jobOptions: CaptureJobOptions;\n gitHosts: GitHostProvider[];\n secret: SecretInput;\n logger: Logger;\n}\n\n/** A queued capture job. */\nexport interface CaptureDispatchJob {\n buildId: string;\n reqId?: string;\n /** W3C `traceparent` continuing the enqueueing request's trace. */\n traceparent?: string;\n}\n\ninterface JobContext {\n deps: DispatchDeps;\n build: Build;\n project: Project;\n pendingUrl: string;\n attempt: CaptureAttempt;\n recordLog: AttemptLogRecorder;\n}\n\n/** Open a new attempt row for this run and return its log recorder. */\nasync function beginAttempt(\n deps: DispatchDeps,\n project: Project,\n build: Build,\n reqId: string | undefined,\n): Promise<{ attempt: CaptureAttempt; recordLog: AttemptLogRecorder }> {\n const attempts = new CaptureAttemptModel(deps.db, deps.tables);\n const logs = new CaptureLogModel(deps.db, deps.tables);\n const attempt = await attempts.startAttempt(project.id, build.id, reqId);\n const recordLog: AttemptLogRecorder = async (level, message, fields) => {\n await logs.append(project.id, build.id, attempt.id, level, message, fields);\n };\n await emitAttemptLog(recordLog, deps.logger, \"info\", \"capture queued\", {\n buildId: build.id,\n attemptNo: attempt.attemptNo,\n });\n return { attempt, recordLog };\n}\n\n/** Close the attempt row with its terminal status, counts, and error. */\nasync function finishAttempt(\n ctx: JobContext,\n status: \"completed\" | \"failed\" | \"cancelled\",\n error?: string,\n counts?: { storyCount: number; failedCount: number },\n): Promise<void> {\n const { deps, attempt } = ctx;\n await new CaptureAttemptModel(deps.db, deps.tables).markFinished(\n attempt.id,\n status,\n error,\n counts,\n );\n await emitAttemptLog(\n ctx.recordLog,\n deps.logger,\n status === \"failed\" ? \"error\" : \"info\",\n `capture ${status}`,\n { attemptNo: attempt.attemptNo },\n );\n}\n\n/** Full error text for an attempt row (message plus stack when available). */\nfunction errorText(reason: unknown): string {\n if (reason instanceof Error) {\n return reason.stack ?? reason.message;\n }\n return String(reason);\n}\n\nasync function postPending(\n deps: DispatchDeps,\n project: Project,\n url: string,\n sha: string,\n): Promise<void> {\n await postStatusesForBuild({\n db: deps.db,\n tables: deps.tables,\n project,\n sha,\n status: \"pending\",\n url,\n providers: deps.gitHosts,\n secret: deps.secret,\n logger: deps.logger,\n }).catch((error: unknown) => {\n deps.logger.error({ err: error }, \"failed to post pending status\");\n });\n}\n\nasync function postTerminal(\n deps: DispatchDeps,\n project: Project,\n url: string,\n sha: string,\n status: \"success\" | \"failure\",\n): Promise<void> {\n await postStatusesForBuild({\n db: deps.db,\n tables: deps.tables,\n project,\n sha,\n status,\n url,\n providers: deps.gitHosts,\n secret: deps.secret,\n logger: deps.logger,\n }).catch((error: unknown) => {\n deps.logger.error({ err: error }, \"failed to post terminal status\");\n });\n}\n\nasync function approveWithoutCapture(ctx: JobContext, reason: string): Promise<void> {\n const { deps, build, project, pendingUrl } = ctx;\n deps.logger.info({ buildId: build.id, sha: build.gitSha, branch: build.gitBranch }, reason);\n await emitAttemptLog(ctx.recordLog, deps.logger, \"info\", reason, {\n sha: build.gitSha,\n branch: build.gitBranch,\n });\n await finishAttempt(ctx, \"completed\");\n await new BuildModel(deps.db, deps.tables).setStatus(build.id, \"approved\").catch(() => {}); // Intentionally empty — fire-and-forget\n await postTerminal(deps, project, pendingUrl, build.gitSha, \"success\").catch(() => {}); // Intentionally empty — fire-and-forget\n}\n\nasync function maybeSkipMerged(ctx: JobContext): Promise<boolean> {\n const { deps, build, project } = ctx;\n if (build.isDefault) {\n return false;\n }\n const merged = await isAlreadyMerged({\n providers: deps.gitHosts,\n sha: build.gitSha,\n branch: build.gitBranch,\n secret: deps.secret,\n db: deps.db,\n tables: deps.tables,\n projectId: project.id,\n logger: deps.logger,\n }).catch(() => false);\n if (merged) {\n await approveWithoutCapture(ctx, \"skipping capture — already merged\");\n return true;\n }\n return false;\n}\n\nasync function maybeSkipDuplicate(ctx: JobContext): Promise<boolean> {\n const { deps, build, project } = ctx;\n const dup = await hasApprovedBuildForSha(\n deps.db,\n deps.tables,\n project.id,\n build.gitSha,\n build.id,\n );\n if (dup) {\n await approveWithoutCapture(ctx, \"skipping capture — duplicate sha already approved\");\n return true;\n }\n return false;\n}\n\n/** Map a terminal build status to its check-run state. */\nfunction terminalState(status: string | undefined): \"success\" | \"failure\" | null {\n if (status === \"approved\") {\n return \"success\";\n }\n if (status === \"rejected\" || status === \"failed\") {\n return \"failure\";\n }\n return null;\n}\n\n/** Post the terminal status for a finished capture. */\nasync function reportTerminalStatus(ctx: JobContext, buildId: string): Promise<void> {\n const { deps, build, project, pendingUrl } = ctx;\n const updated = await new BuildModel(deps.db, deps.tables).get(buildId);\n const terminal = terminalState(updated?.status);\n if (terminal) {\n await postTerminal(deps, project, pendingUrl, build.gitSha, terminal);\n }\n}\n\nasync function runAndReport(ctx: JobContext, job: CaptureDispatchJob): Promise<void> {\n const { deps, build } = ctx;\n await new CaptureAttemptModel(deps.db, deps.tables).markRunning(ctx.attempt.id);\n await emitAttemptLog(ctx.recordLog, deps.logger, \"info\", \"capture started\", {\n buildId: build.id,\n attemptNo: ctx.attempt.attemptNo,\n });\n try {\n const summary = await executeCaptureJob(\n {\n buildId: job.buildId,\n reqId: job.reqId,\n traceparent: job.traceparent,\n attempt: {\n id: ctx.attempt.id,\n attemptNo: ctx.attempt.attemptNo,\n projectId: ctx.project.id,\n },\n recordLog: ctx.recordLog,\n },\n deps.jobOptions,\n );\n await finishAttempt(ctx, \"completed\", undefined, summary);\n await reportTerminalStatus(ctx, job.buildId);\n } catch (error: unknown) {\n await finishAttempt(ctx, \"failed\", errorText(error));\n await postStatusesForBuild({\n db: deps.db,\n tables: deps.tables,\n project: ctx.project,\n sha: build.gitSha,\n status: \"failure\",\n url: ctx.pendingUrl,\n providers: deps.gitHosts,\n secret: deps.secret,\n logger: deps.logger,\n }).catch(() => {\n // Ignore: status post failure already logged\n });\n throw error;\n }\n}\n\n/** Load the build + project for a job, running orphan jobs directly. */\nasync function loadJobTarget(\n deps: DispatchDeps,\n job: CaptureDispatchJob,\n): Promise<{ build: Build; project: Project } | null> {\n const builds = new BuildModel(deps.db, deps.tables);\n const build = await builds.get(job.buildId);\n if (!build) {\n await executeCaptureJob(\n { buildId: job.buildId, reqId: job.reqId, traceparent: job.traceparent },\n deps.jobOptions,\n );\n return null;\n }\n const project = await new ProjectModel(deps.db, deps.tables).get(build.projectId);\n if (!project) {\n await executeCaptureJob(\n { buildId: job.buildId, reqId: job.reqId, traceparent: job.traceparent },\n deps.jobOptions,\n );\n return null;\n }\n return { build, project };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dispatch.mjs","names":[],"sources":["../../src/capture/dispatch.ts"],"sourcesContent":["import type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { Logger } from \"../logger.ts\";\nimport { BuildModel, type BuildTables } from \"../models/build.ts\";\nimport {\n CaptureAttemptModel,\n emitAttemptLog,\n type AttemptLogRecorder,\n type CaptureAttemptTables,\n} from \"../models/capture-attempt.ts\";\nimport { CaptureLogModel, type CaptureLogTables } from \"../models/capture-log.ts\";\nimport { CaptureShardModel } from \"../models/capture-shard.ts\";\nimport { ProjectModel, type ProjectTables } from \"../models/project.ts\";\nimport type { Build } from \"../schema/build.ts\";\nimport type { CaptureAttempt } from \"../schema/capture-attempt.ts\";\nimport type { Project } from \"../schema/project.ts\";\nimport { redactSecrets } from \"../utils/redact.ts\";\nimport type { BuildStatePublisher } from \"./build-state/types.ts\";\nimport type { JobLease } from \"./job-lease.ts\";\nimport { executeCaptureJob, type CaptureJobOptions, type CaptureSummary } from \"./orchestrator.ts\";\nimport { leaseProgress, type CaptureProgress } from \"./progress.ts\";\nimport { executeCaptureShard } from \"./shards/execute.ts\";\nimport type { FinalSummary } from \"./shards/reduce.ts\";\nimport { hasApprovedBuildForSha } from \"./skip-checks.ts\";\n\n/** Build the queue's job runner: post pending, apply skip rules, capture, report. */\nexport function createDispatchJob(\n deps: DispatchDeps,\n): (job: CaptureDispatchJob, lease?: JobLease) => Promise<void> {\n return async (job: CaptureDispatchJob, lease?: JobLease): Promise<void> => {\n await (job.shard\n ? runShardJob(deps, { ...job, shard: job.shard }, lease)\n : runBuildJob(deps, job, lease));\n };\n}\n\n/** The build's own job: post pending, apply skip rules, capture (or plan), report. */\nasync function runBuildJob(\n deps: DispatchDeps,\n job: CaptureDispatchJob,\n lease?: JobLease,\n): Promise<void> {\n const target = await loadJobTarget(deps, job);\n if (!target) {\n return;\n }\n await deps.buildState.request(job.buildId);\n const { attempt, recordLog } = await beginAttempt(\n deps,\n target.project,\n target.build,\n job.reqId,\n lease,\n );\n const ctx: JobContext = {\n deps,\n build: target.build,\n project: target.project,\n attempt,\n recordLog,\n progress: leaseProgress(lease),\n };\n if (await maybeSkipDuplicate(ctx)) {\n return;\n }\n await runAndReport(ctx, job);\n}\n\n/** Table handles required by dispatch. */\nexport type DispatchTables = BuildTables & ProjectTables & CaptureAttemptTables & CaptureLogTables;\n\n/** Dependencies for building the capture queue's job runner. */\nexport interface DispatchDeps {\n db: DatabaseAdapter;\n tables: DispatchTables;\n jobOptions: CaptureJobOptions;\n /** Shows a build's current state on its git channels; called at every transition. */\n buildState: BuildStatePublisher;\n logger: Logger;\n /** Redeliveries the queue allows a failing job; a shard's last try is its final word. */\n maxRetries?: number | undefined;\n}\n\n/** A queued capture job. */\nexport interface CaptureDispatchJob {\n buildId: string;\n /** A `capture_shards` row to render; absent for the build's own (plan or whole-build) job. */\n shard?: string | undefined;\n /** Times the queue has already delivered this job and seen it fail. */\n attempts?: number | undefined;\n reqId?: string;\n /** W3C `traceparent` continuing the enqueueing request's trace. */\n traceparent?: string;\n}\n\ninterface JobContext {\n deps: DispatchDeps;\n build: Build;\n project: Project;\n attempt: CaptureAttempt;\n recordLog: AttemptLogRecorder;\n progress: CaptureProgress | undefined;\n}\n\n/** Open a new attempt row for this run and return its log recorder. */\nasync function beginAttempt(\n deps: DispatchDeps,\n project: Project,\n build: Build,\n reqId: string | undefined,\n lease?: JobLease,\n): Promise<{ attempt: CaptureAttempt; recordLog: AttemptLogRecorder }> {\n const attempts = new CaptureAttemptModel(deps.db, deps.tables);\n const logs = new CaptureLogModel(deps.db, deps.tables);\n const attempt = await attempts.startAttempt(project.id, build.id, reqId);\n // Every attempt-log line (stages, render progress, runner output) counts as progress.\n const recordLog: AttemptLogRecorder = async (level, message, fields) => {\n lease?.touch();\n await logs.append(project.id, build.id, attempt.id, level, message, fields);\n };\n await emitAttemptLog(recordLog, deps.logger, \"info\", \"capture queued\", {\n buildId: build.id,\n attemptNo: attempt.attemptNo,\n });\n return { attempt, recordLog };\n}\n\n/** Close the attempt row with its terminal status, counts, and error. */\nasync function finishAttempt(\n ctx: JobContext,\n status: \"completed\" | \"failed\" | \"cancelled\",\n error?: string,\n counts?: { storyCount: number; failedCount: number },\n): Promise<void> {\n const { deps, attempt } = ctx;\n await new CaptureAttemptModel(deps.db, deps.tables).markFinished(\n attempt.id,\n status,\n error,\n counts,\n );\n await emitAttemptLog(\n ctx.recordLog,\n deps.logger,\n status === \"failed\" ? \"error\" : \"info\",\n `capture ${status}`,\n { attemptNo: attempt.attemptNo },\n );\n}\n\n/** Full error text for an attempt row (message plus stack when available). */\nfunction errorText(reason: unknown): string {\n // A runner's error can quote the signed Storybook URL it failed to open.\n return redactSecrets(reason instanceof Error ? (reason.stack ?? reason.message) : String(reason));\n}\n\nasync function approveWithoutCapture(ctx: JobContext, reason: string): Promise<void> {\n const { deps, build } = ctx;\n deps.logger.info({ buildId: build.id, sha: build.gitSha, branch: build.gitBranch }, reason);\n await emitAttemptLog(ctx.recordLog, deps.logger, \"info\", reason, {\n sha: build.gitSha,\n branch: build.gitBranch,\n });\n await finishAttempt(ctx, \"completed\");\n await new BuildModel(deps.db, deps.tables).setStatus(build.id, \"approved\").catch(() => {}); // Intentionally empty — fire-and-forget\n await deps.buildState.request(build.id);\n}\n\nasync function maybeSkipDuplicate(ctx: JobContext): Promise<boolean> {\n const { deps, build, project } = ctx;\n const dup = await hasApprovedBuildForSha(\n deps.db,\n deps.tables,\n project.id,\n build.gitSha,\n build.id,\n );\n if (dup) {\n await approveWithoutCapture(ctx, \"skipping capture — duplicate sha already approved\");\n return true;\n }\n return false;\n}\n\n/** A sharded build's attempt stays open: the worker that finishes the last shard closes it. */\nasync function finishUnlessSharded(ctx: JobContext, summary: CaptureSummary): Promise<void> {\n if (summary.shards) {\n await emitAttemptLog(ctx.recordLog, ctx.deps.logger, \"info\", \"capture sharded\", {\n shards: summary.shards,\n });\n return;\n }\n await finishAttempt(ctx, \"completed\", undefined, summary);\n await ctx.deps.buildState.request(ctx.build.id);\n}\n\nasync function runAndReport(ctx: JobContext, job: CaptureDispatchJob): Promise<void> {\n const { deps, build } = ctx;\n await new CaptureAttemptModel(deps.db, deps.tables).markRunning(ctx.attempt.id);\n await emitAttemptLog(ctx.recordLog, deps.logger, \"info\", \"capture started\", {\n buildId: build.id,\n attemptNo: ctx.attempt.attemptNo,\n });\n try {\n const summary = await executeCaptureJob(\n {\n buildId: job.buildId,\n reqId: job.reqId,\n traceparent: job.traceparent,\n attempt: {\n id: ctx.attempt.id,\n attemptNo: ctx.attempt.attemptNo,\n projectId: ctx.project.id,\n },\n recordLog: ctx.recordLog,\n progress: ctx.progress,\n },\n deps.jobOptions,\n );\n await finishUnlessSharded(ctx, summary);\n } catch (error: unknown) {\n await finishAttempt(ctx, \"failed\", errorText(error));\n await deps.buildState.request(build.id);\n throw error;\n }\n}\n\n/** Load the build + project for a job, running orphan jobs directly. */\nasync function loadJobTarget(\n deps: DispatchDeps,\n job: CaptureDispatchJob,\n): Promise<{ build: Build; project: Project } | null> {\n const build = await new BuildModel(deps.db, deps.tables).get(job.buildId);\n const project = build ? await new ProjectModel(deps.db, deps.tables).get(build.projectId) : null;\n if (!build || !project) {\n await executeCaptureJob(\n { buildId: job.buildId, reqId: job.reqId, traceparent: job.traceparent },\n deps.jobOptions,\n );\n return null;\n }\n return { build, project };\n}\n\n/**\n * Run one render shard. Only the run that finalized the build closes the plan's attempt and posts\n * the terminal status, whether its shard succeeded or failed; every other run leaves them alone.\n */\nasync function runShardJob(\n deps: DispatchDeps,\n job: CaptureDispatchJob & { shard: string },\n lease?: JobLease,\n): Promise<void> {\n const final = (job.attempts ?? 0) >= (deps.maxRetries ?? 0);\n await executeCaptureShard(\n {\n buildId: job.buildId,\n shard: job.shard,\n reqId: job.reqId,\n final,\n progress: leaseProgress(lease),\n onFinalized: async (summary, error) => {\n await closeShardedBuild(deps, job.buildId, summary, error);\n },\n },\n deps.jobOptions,\n );\n}\n\n/** End the plan's attempt with the finalizer's counts and report the build's terminal status. */\nasync function closeShardedBuild(\n deps: DispatchDeps,\n buildId: string,\n summary: FinalSummary,\n error?: unknown,\n): Promise<void> {\n const loaded = await loadClosing(deps, buildId);\n if (!loaded) {\n return;\n }\n const failure = error === undefined ? summary.error : errorText(error);\n const failed = failure !== undefined || summary.buildStatus === \"failed\";\n const attemptId = summary.attemptId ?? (await planAttemptId(deps, buildId));\n if (attemptId) {\n await new CaptureAttemptModel(deps.db, deps.tables)\n .markFinished(attemptId, failed ? \"failed\" : \"completed\", failure, summary)\n .catch((closeError: unknown) => {\n deps.logger.error({ err: closeError }, \"failed to close attempt\");\n });\n }\n await deps.buildState.request(buildId);\n}\n\nasync function planAttemptId(deps: DispatchDeps, buildId: string): Promise<string | null> {\n const plan = await new CaptureShardModel(deps.db).plan(buildId);\n return plan?.attemptId ?? null;\n}\n\nasync function loadClosing(\n deps: DispatchDeps,\n buildId: string,\n): Promise<{ build: Build; project: Project } | null> {\n const build = await new BuildModel(deps.db, deps.tables).get(buildId);\n const project = build ? await new ProjectModel(deps.db, deps.tables).get(build.projectId) : null;\n return build && project ? { build, project } : null;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { filterModes } from "./modes.mjs";
|
|
5
|
+
import { renderBudgetMs } from "./render.mjs";
|
|
6
|
+
import { resolveStoryViewports } from "./storybook.mjs";
|
|
7
|
+
//#region src/capture/enrich.ts
|
|
8
|
+
/**
|
|
9
|
+
* Merge runtime-resolved parameters (project/component/story levels stacked by
|
|
10
|
+
* Storybook) over the index's, then apply the project's mode allowlist. A
|
|
11
|
+
* runner without `extractParameters`, or one that fails, leaves index
|
|
12
|
+
* parameters untouched — the build still captures plain viewports.
|
|
13
|
+
*/
|
|
14
|
+
async function enrichStories(input) {
|
|
15
|
+
const extracted = await extractSafely(input);
|
|
16
|
+
return input.stories.map((story) => {
|
|
17
|
+
const parameters = {
|
|
18
|
+
...story.parameters,
|
|
19
|
+
...extracted[story.id]
|
|
20
|
+
};
|
|
21
|
+
const modes = filterModes(parameters.modes, input.allowlist);
|
|
22
|
+
if (modes) parameters.modes = modes;
|
|
23
|
+
return Object.keys(parameters).length > 0 ? {
|
|
24
|
+
...story,
|
|
25
|
+
parameters
|
|
26
|
+
} : story;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Fail the build before any rendering when a story expands beyond the cap
|
|
31
|
+
* (global viewports + default viewport + modes).
|
|
32
|
+
*/
|
|
33
|
+
function assertModeCaps(stories, viewports) {
|
|
34
|
+
for (const story of stories) {
|
|
35
|
+
const count = resolveStoryViewports(story, viewports).length;
|
|
36
|
+
if (count > 8) throw new Error(`Story "${story.id}" expands to ${count} captures (max 8); reduce its modes or the project viewports`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function extractSafely(input) {
|
|
40
|
+
const { runner } = input;
|
|
41
|
+
if (!runner.extractParameters) return {};
|
|
42
|
+
try {
|
|
43
|
+
const storybookUrl = await input.storybookUrl?.(input.target, { budgetMs: renderBudgetMs(input.stories.length) });
|
|
44
|
+
return await runner.extractParameters({
|
|
45
|
+
buildId: input.buildId,
|
|
46
|
+
storybookDir: input.storybookDir,
|
|
47
|
+
stories: input.stories,
|
|
48
|
+
target: input.target,
|
|
49
|
+
...storybookUrl === void 0 ? {} : { storybookUrl },
|
|
50
|
+
...input.logger ? { logger: input.logger } : {}
|
|
51
|
+
});
|
|
52
|
+
} catch (error) {
|
|
53
|
+
input.logger?.warn({ err: error }, "parameter extraction failed; modes from index only");
|
|
54
|
+
return {};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
export { assertModeCaps, enrichStories };
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=enrich.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enrich.mjs","names":[],"sources":["../../src/capture/enrich.ts"],"sourcesContent":["/** Orchestrator pre-pass: learn each story's modes before building the capture matrix. */\nimport type { Logger } from \"pino\";\nimport type { CaptureTarget, CaptureRunner } from \"../adapters/capture-runner.ts\";\nimport type { StoryEntry, Viewport } from \"./adapter.ts\";\nimport { filterModes, MAX_MODES_PER_STORY } from \"./modes.ts\";\nimport { renderBudgetMs } from \"./render.ts\";\nimport type { StorybookUrlSource } from \"./storybook-urls.ts\";\nimport { resolveStoryViewports } from \"./storybook.ts\";\n\n/** Inputs for {@link enrichStories}. */\nexport interface EnrichInput {\n runner: CaptureRunner;\n buildId: string;\n storybookDir: string;\n stories: StoryEntry[];\n target: CaptureTarget;\n allowlist: readonly string[] | null;\n logger: Logger | undefined;\n /** Mints the signed URL for a `reach: \"url\"` target. */\n storybookUrl?: StorybookUrlSource | undefined;\n}\n\n/**\n * Merge runtime-resolved parameters (project/component/story levels stacked by\n * Storybook) over the index's, then apply the project's mode allowlist. A\n * runner without `extractParameters`, or one that fails, leaves index\n * parameters untouched — the build still captures plain viewports.\n */\nexport async function enrichStories(input: EnrichInput): Promise<StoryEntry[]> {\n const extracted = await extractSafely(input);\n return input.stories.map((story) => {\n const parameters = { ...story.parameters, ...extracted[story.id] };\n const modes = filterModes(parameters.modes, input.allowlist);\n if (modes) parameters.modes = modes;\n return Object.keys(parameters).length > 0 ? { ...story, parameters } : story;\n });\n}\n\n/**\n * Fail the build before any rendering when a story expands beyond the cap\n * (global viewports + default viewport + modes).\n */\nexport function assertModeCaps(stories: StoryEntry[], viewports: Viewport[]): void {\n for (const story of stories) {\n const count = resolveStoryViewports(story, viewports).length;\n if (count > MAX_MODES_PER_STORY) {\n throw new Error(\n `Story \"${story.id}\" expands to ${count} captures (max ${MAX_MODES_PER_STORY}); reduce its modes or the project viewports`,\n );\n }\n }\n}\n\nasync function extractSafely(\n input: EnrichInput,\n): ReturnType<NonNullable<CaptureRunner[\"extractParameters\"]>> {\n const { runner } = input;\n if (!runner.extractParameters) return {};\n try {\n const storybookUrl = await input.storybookUrl?.(input.target, {\n budgetMs: renderBudgetMs(input.stories.length),\n });\n return await runner.extractParameters({\n buildId: input.buildId,\n storybookDir: input.storybookDir,\n stories: input.stories,\n target: input.target,\n ...(storybookUrl === undefined ? {} : { storybookUrl }),\n ...(input.logger ? { logger: input.logger } : {}),\n });\n } catch (error) {\n input.logger?.warn({ err: error }, \"parameter extraction failed; modes from index only\");\n return {};\n }\n}\n"],"mappings":";;;;;;;;;;;;;AA4BA,eAAsB,cAAc,OAA2C;CAC7E,MAAM,YAAY,MAAM,cAAc,KAAK;CAC3C,OAAO,MAAM,QAAQ,KAAK,UAAU;EAClC,MAAM,aAAa;GAAE,GAAG,MAAM;GAAY,GAAG,UAAU,MAAM;EAAI;EACjE,MAAM,QAAQ,YAAY,WAAW,OAAO,MAAM,SAAS;EAC3D,IAAI,OAAO,WAAW,QAAQ;EAC9B,OAAO,OAAO,KAAK,UAAU,CAAC,CAAC,SAAS,IAAI;GAAE,GAAG;GAAO;EAAW,IAAI;CACzE,CAAC;AACH;;;;;AAMA,SAAgB,eAAe,SAAuB,WAA6B;CACjF,KAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,QAAQ,sBAAsB,OAAO,SAAS,CAAC,CAAC;EACtD,IAAI,QAAA,GACF,MAAM,IAAI,MACR,UAAU,MAAM,GAAG,eAAe,MAAM,6DAC1C;CAEJ;AACF;AAEA,eAAe,cACb,OAC6D;CAC7D,MAAM,EAAE,WAAW;CACnB,IAAI,CAAC,OAAO,mBAAmB,OAAO,CAAC;CACvC,IAAI;EACF,MAAM,eAAe,MAAM,MAAM,eAAe,MAAM,QAAQ,EAC5D,UAAU,eAAe,MAAM,QAAQ,MAAM,EAC/C,CAAC;EACD,OAAO,MAAM,OAAO,kBAAkB;GACpC,SAAS,MAAM;GACf,cAAc,MAAM;GACpB,SAAS,MAAM;GACf,QAAQ,MAAM;GACd,GAAI,iBAAiB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa;GACrD,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;EACjD,CAAC;CACH,SAAS,OAAO;EACd,MAAM,QAAQ,KAAK,EAAE,KAAK,MAAM,GAAG,oDAAoD;EACvF,OAAO,CAAC;CACV;AACF"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { isFlakyStory } from "./adapter.mjs";
|
|
5
|
+
//#region src/capture/failure-sets.ts
|
|
6
|
+
/** Classify render failures: `a11y:` errors, then flaky stories, then everything else is blocking. */
|
|
7
|
+
function classifyFailures(stories, failures) {
|
|
8
|
+
const flakyIds = new Set(stories.filter((story) => isFlakyStory(story)).map((story) => story.id));
|
|
9
|
+
const sets = {
|
|
10
|
+
blocking: /* @__PURE__ */ new Set(),
|
|
11
|
+
flaky: /* @__PURE__ */ new Set(),
|
|
12
|
+
a11y: /* @__PURE__ */ new Set()
|
|
13
|
+
};
|
|
14
|
+
for (const failure of failures) if (failure.error.startsWith("a11y:")) sets.a11y.add(failure.storyId);
|
|
15
|
+
else if (flakyIds.has(failure.storyId)) sets.flaky.add(failure.storyId);
|
|
16
|
+
else sets.blocking.add(failure.storyId);
|
|
17
|
+
return sets;
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { classifyFailures };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=failure-sets.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failure-sets.mjs","names":[],"sources":["../../src/capture/failure-sets.ts"],"sourcesContent":["/** Sorting a render's failures into the three kinds that decide a build's status. */\nimport type { RenderFailure } from \"../adapters/capture-runner.ts\";\nimport { isFlakyStory, type StoryEntry } from \"./adapter.ts\";\n\n/** Story ids by how their failure counts. */\nexport interface FailureSets {\n /** Fail the build. */\n blocking: Set<string>;\n /** Flaky stories: logged, never blocking. */\n flaky: Set<string>;\n /** Accessibility violations: logged, never blocking. */\n a11y: Set<string>;\n}\n\n/** Classify render failures: `a11y:` errors, then flaky stories, then everything else is blocking. */\nexport function classifyFailures(\n stories: readonly StoryEntry[],\n failures: readonly RenderFailure[],\n): FailureSets {\n const flakyIds = new Set(stories.filter((story) => isFlakyStory(story)).map((story) => story.id));\n const sets: FailureSets = { blocking: new Set(), flaky: new Set(), a11y: new Set() };\n for (const failure of failures) {\n if (failure.error.startsWith(\"a11y:\")) {\n sets.a11y.add(failure.storyId);\n } else if (flakyIds.has(failure.storyId)) {\n sets.flaky.add(failure.storyId);\n } else {\n sets.blocking.add(failure.storyId);\n }\n }\n return sets;\n}\n"],"mappings":";;;;;;AAeA,SAAgB,iBACd,SACA,UACa;CACb,MAAM,WAAW,IAAI,IAAI,QAAQ,QAAQ,UAAU,aAAa,KAAK,CAAC,CAAC,CAAC,KAAK,UAAU,MAAM,EAAE,CAAC;CAChG,MAAM,OAAoB;EAAE,0BAAU,IAAI,IAAI;EAAG,uBAAO,IAAI,IAAI;EAAG,sBAAM,IAAI,IAAI;CAAE;CACnF,KAAK,MAAM,WAAW,UACpB,IAAI,QAAQ,MAAM,WAAW,OAAO,GAClC,KAAK,KAAK,IAAI,QAAQ,OAAO;MACxB,IAAI,SAAS,IAAI,QAAQ,OAAO,GACrC,KAAK,MAAM,IAAI,QAAQ,OAAO;MAE9B,KAAK,SAAS,IAAI,QAAQ,OAAO;CAGrC,OAAO;AACT"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { Viewport } from "./adapter.mjs";
|
|
4
|
+
import { CaptureEnvironment } from "../adapter/capture-runner.mjs";
|
|
5
|
+
import { SIZING_DEFAULTS } from "./sizing.mjs";
|
|
6
|
+
//#region src/capture/fingerprint.d.ts
|
|
7
|
+
/** The components behind a snapshot's `infraHash`, kept so a mismatch can say what changed. */
|
|
8
|
+
interface Fingerprint {
|
|
9
|
+
engine: string;
|
|
10
|
+
browserVersion: string | null;
|
|
11
|
+
platform: string | null;
|
|
12
|
+
deviceScaleFactor: number | null;
|
|
13
|
+
fontsHash: string | null;
|
|
14
|
+
viewport: {
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
};
|
|
18
|
+
sizing: typeof SIZING_DEFAULTS;
|
|
19
|
+
}
|
|
20
|
+
/** Build the fingerprint of one capture. Missing environment fields stay `null` (runner did not report them). */
|
|
21
|
+
declare function buildFingerprint(engine: string, viewport: Viewport, environment: CaptureEnvironment | undefined): Fingerprint;
|
|
22
|
+
/** Stable hash of a fingerprint: the value stored as `infraHash`. */
|
|
23
|
+
declare function fingerprintHash(fingerprint: Fingerprint): string;
|
|
24
|
+
/**
|
|
25
|
+
* Names of the environment components that differ; `["unknown"]` when the earlier fingerprint was
|
|
26
|
+
* not recorded. A component one side did not report (`null`: a runner that does not report it, a
|
|
27
|
+
* capture spooled before a browser crash, a failed `fc-list`) is unknown, not changed. Viewport is
|
|
28
|
+
* never a change here: its size comes from story parameters (the PR's own code), so a new size must
|
|
29
|
+
* be reviewed, and it diffs on its own as a dimension change.
|
|
30
|
+
*/
|
|
31
|
+
declare function changedFields(from: Fingerprint | null, to: Fingerprint): string[];
|
|
32
|
+
/** Parse a stored fingerprint; null for a missing or malformed value. */
|
|
33
|
+
declare function parseFingerprint(raw: string | null | undefined): Fingerprint | null;
|
|
34
|
+
/** What a migrated snapshot records: which components changed, and the before and after. */
|
|
35
|
+
interface EnvironmentChange {
|
|
36
|
+
changed: string[];
|
|
37
|
+
from: Fingerprint | null;
|
|
38
|
+
to: Fingerprint;
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { EnvironmentChange, Fingerprint, buildFingerprint, changedFields, fingerprintHash, parseFingerprint };
|
|
42
|
+
//# sourceMappingURL=fingerprint.d.mts.map
|