@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
|
@@ -5,29 +5,59 @@ import { StorageAdapter } from "../adapter/storage.mjs";
|
|
|
5
5
|
import { SecretInput } from "../utils/encrypt.mjs";
|
|
6
6
|
import { WebhookTables } from "./webhook.mjs";
|
|
7
7
|
import { Baseline } from "../schema/baseline.mjs";
|
|
8
|
+
import { AtomicBaselineApproval } from "./baseline-approval.mjs";
|
|
8
9
|
import { Table } from "drizzle-orm";
|
|
9
|
-
|
|
10
|
+
/** Extra identity/metadata for {@link BaselineModel.upsert}. */
|
|
11
|
+
interface BaselineUpsertOptions {
|
|
12
|
+
approval?: AtomicBaselineApproval;
|
|
13
|
+
/** Engine the baseline belongs to (default chromium). */
|
|
14
|
+
browser?: string;
|
|
15
|
+
/** Resolved mode definition as JSON (display only). */
|
|
16
|
+
mode?: string | null;
|
|
17
|
+
/** JSON `Fingerprint` behind the `infraHash` being written. */
|
|
18
|
+
fingerprint?: string | null;
|
|
19
|
+
}
|
|
10
20
|
/** Tables required by {@link BaselineModel}. */
|
|
11
21
|
interface BaselineTables {
|
|
12
22
|
baselines: Table;
|
|
13
23
|
}
|
|
14
24
|
/** Data operations for baseline screenshots. */
|
|
15
25
|
declare class BaselineModel {
|
|
16
|
-
private
|
|
26
|
+
#private;
|
|
17
27
|
private readonly tables;
|
|
18
28
|
private readonly storage;
|
|
19
29
|
private readonly secret?;
|
|
20
30
|
private readonly webhookTables?;
|
|
31
|
+
private readonly db;
|
|
21
32
|
constructor(db: DatabaseAdapter, tables?: BaselineTables, storage?: StorageAdapter, secret?: SecretInput, webhookTables?: WebhookTables);
|
|
22
|
-
getFor(projectId: string, storyId: string, viewport: string, branch: string): Promise<Baseline | null>;
|
|
23
|
-
resolve(projectId: string, storyId: string, viewport: string, branch: string, defaultBranch: string): Promise<Baseline | null>;
|
|
33
|
+
getFor(projectId: string, storyId: string, viewport: string, branch: string, browser?: string): Promise<Baseline | null>;
|
|
34
|
+
resolve(projectId: string, storyId: string, viewport: string, branch: string, defaultBranch: string, browser?: string): Promise<Baseline | null>;
|
|
35
|
+
/** Whether the project has any baseline captured on this engine. */
|
|
36
|
+
hasAny(projectId: string, browser: string): Promise<boolean>;
|
|
37
|
+
/**
|
|
38
|
+
* Record the fingerprint of a baseline that predates fingerprints, without touching its
|
|
39
|
+
* screenshot. Lets the next environment change be detected instead of diffing as a regression.
|
|
40
|
+
*/
|
|
41
|
+
backfillFingerprint(baseline: Baseline, infraHash: string, fingerprint: string): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* The content-addressed object a baseline should point at. A source that already is one is
|
|
44
|
+
* reused as is (no copy); a legacy per-build path is stored once by content.
|
|
45
|
+
*/
|
|
46
|
+
private contentFor;
|
|
24
47
|
read(baseline: Baseline): Promise<Buffer>;
|
|
25
|
-
upsert(projectId: string, storyId: string, viewport: string, branch: string, snapshotId: string, sourcePath: string, infraHash?: string | null): Promise<Baseline>;
|
|
48
|
+
upsert(projectId: string, storyId: string, viewport: string, branch: string, snapshotId: string, sourcePath: string, infraHash?: string | null, options?: BaselineUpsertOptions): Promise<Baseline>;
|
|
49
|
+
private sourceBuildId;
|
|
26
50
|
list(projectId: string): Promise<Baseline[]>;
|
|
51
|
+
/**
|
|
52
|
+
* Delete a baseline's own file. A content-addressed screenshot is shared with snapshots and other
|
|
53
|
+
* baselines, so it is left for the retention sweep instead.
|
|
54
|
+
*/
|
|
55
|
+
private discardFile;
|
|
56
|
+
/** Drop the reference and row together when the adapter supports lifecycle protection. */
|
|
57
|
+
private removeRecord;
|
|
27
58
|
removeOrphans(projectId: string, validStoryIds: Set<string>): Promise<number>;
|
|
28
59
|
removeForBranch(projectId: string, branch: string): Promise<number>;
|
|
29
60
|
removeStaleBranches(projectId: string, staleBranches: ReadonlySet<string>): Promise<number>;
|
|
30
61
|
}
|
|
31
|
-
|
|
32
|
-
export { BaselineModel, BaselineTables };
|
|
62
|
+
export { BaselineModel, BaselineTables, BaselineUpsertOptions };
|
|
33
63
|
//# sourceMappingURL=baseline.d.mts.map
|
package/dist/models/baseline.mjs
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
+
import { DEFAULT_BROWSER } from "../adapters/capture-target.mjs";
|
|
4
5
|
import { ulid } from "../utils/ulid.mjs";
|
|
6
|
+
import { col } from "./columns.mjs";
|
|
5
7
|
import { emitWebhookEvent } from "../adapter/webhook-events.mjs";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
8
|
+
import { isContentPath } from "../paths.mjs";
|
|
9
|
+
import { storeScreenshot, withContentClaim } from "../utils/screenshot-store.mjs";
|
|
10
|
+
import { BaselineWriteConflict } from "./baseline-conflict.mjs";
|
|
11
|
+
import { approveInTransaction } from "./baseline-approval.mjs";
|
|
12
|
+
import { upsertAtomicBaseline } from "./baseline-atomic.mjs";
|
|
13
|
+
import { and, eq } from "drizzle-orm";
|
|
14
|
+
// src/models/baseline.ts
|
|
9
15
|
/** Per-branch baseline screenshots with default-branch fallback. */
|
|
10
16
|
/** Data operations for baseline screenshots. */
|
|
11
17
|
var BaselineModel = class {
|
|
12
|
-
db;
|
|
13
18
|
tables;
|
|
14
19
|
storage;
|
|
15
20
|
secret;
|
|
16
21
|
webhookTables;
|
|
22
|
+
db;
|
|
17
23
|
constructor(db, tables, storage, secret, webhookTables) {
|
|
18
24
|
this.db = db;
|
|
19
25
|
this.tables = tables ?? { baselines: db.tables.baselines };
|
|
@@ -21,39 +27,130 @@ var BaselineModel = class {
|
|
|
21
27
|
this.secret = secret;
|
|
22
28
|
this.webhookTables = webhookTables;
|
|
23
29
|
}
|
|
24
|
-
async getFor(projectId, storyId, viewport, branch) {
|
|
30
|
+
async getFor(projectId, storyId, viewport, branch, browser = DEFAULT_BROWSER) {
|
|
25
31
|
return (await this.db.list(this.tables.baselines, {
|
|
26
|
-
where: and(eq(
|
|
32
|
+
where: and(eq(col(this.tables.baselines, "projectId"), projectId), eq(col(this.tables.baselines, "storyId"), storyId), eq(col(this.tables.baselines, "viewportName"), viewport), eq(col(this.tables.baselines, "branch"), branch), eq(col(this.tables.baselines, "browser"), browser)),
|
|
27
33
|
limit: 1
|
|
28
34
|
}))[0] ?? null;
|
|
29
35
|
}
|
|
30
|
-
async resolve(projectId, storyId, viewport, branch, defaultBranch) {
|
|
36
|
+
async resolve(projectId, storyId, viewport, branch, defaultBranch, browser = DEFAULT_BROWSER) {
|
|
31
37
|
if (branch !== defaultBranch) {
|
|
32
|
-
const own = await this.getFor(projectId, storyId, viewport, branch);
|
|
38
|
+
const own = await this.getFor(projectId, storyId, viewport, branch, browser);
|
|
33
39
|
if (own) return own;
|
|
34
40
|
}
|
|
35
|
-
return this.getFor(projectId, storyId, viewport, defaultBranch);
|
|
41
|
+
return this.getFor(projectId, storyId, viewport, defaultBranch, browser);
|
|
42
|
+
}
|
|
43
|
+
/** Whether the project has any baseline captured on this engine. */
|
|
44
|
+
async hasAny(projectId, browser) {
|
|
45
|
+
return (await this.db.list(this.tables.baselines, {
|
|
46
|
+
where: and(eq(col(this.tables.baselines, "projectId"), projectId), eq(col(this.tables.baselines, "browser"), browser)),
|
|
47
|
+
limit: 1
|
|
48
|
+
})).length > 0;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Record the fingerprint of a baseline that predates fingerprints, without touching its
|
|
52
|
+
* screenshot. Lets the next environment change be detected instead of diffing as a regression.
|
|
53
|
+
*/
|
|
54
|
+
async backfillFingerprint(baseline, infraHash, fingerprint) {
|
|
55
|
+
const lifecycle = this.db.buildReferenceLifecycle;
|
|
56
|
+
if (lifecycle) {
|
|
57
|
+
const owner = {
|
|
58
|
+
type: "baseline",
|
|
59
|
+
id: baseline.id,
|
|
60
|
+
projectId: baseline.projectId
|
|
61
|
+
};
|
|
62
|
+
const generation = await lifecycle.referenceGeneration(owner);
|
|
63
|
+
await lifecycle.replaceFencedReferences(owner, generation, baseline.sourceBuildId ? [baseline.sourceBuildId] : [], async (tx) => {
|
|
64
|
+
const current = await tx.get(this.tables.baselines, baseline.id);
|
|
65
|
+
if (!current || current.projectId !== baseline.projectId || current.updatedAt !== baseline.updatedAt || current.sourceBuildId !== baseline.sourceBuildId || current.screenshotPath !== baseline.screenshotPath || current.fingerprint !== baseline.fingerprint) throw new BaselineWriteConflict("Baseline changed during fingerprint backfill; retry");
|
|
66
|
+
await tx.update(this.tables.baselines, baseline.id, {
|
|
67
|
+
infraHash,
|
|
68
|
+
fingerprint
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
await this.db.update(this.tables.baselines, baseline.id, {
|
|
74
|
+
infraHash,
|
|
75
|
+
fingerprint
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The content-addressed object a baseline should point at. A source that already is one is
|
|
80
|
+
* reused as is (no copy); a legacy per-build path is stored once by content.
|
|
81
|
+
*/
|
|
82
|
+
async contentFor(projectId, sourcePath) {
|
|
83
|
+
if (isContentPath(projectId, sourcePath)) return sourcePath;
|
|
84
|
+
return await storeScreenshot(this.storage, projectId, await this.storage.read(sourcePath));
|
|
36
85
|
}
|
|
37
86
|
async read(baseline) {
|
|
38
87
|
return await this.storage.read(baseline.screenshotPath);
|
|
39
88
|
}
|
|
40
|
-
async upsert(projectId, storyId, viewport, branch, snapshotId, sourcePath, infraHash) {
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
await this.storage
|
|
44
|
-
|
|
89
|
+
async upsert(projectId, storyId, viewport, branch, snapshotId, sourcePath, infraHash, options = {}) {
|
|
90
|
+
const sourceBuildId = await this.sourceBuildId(projectId, storyId, viewport, snapshotId, sourcePath, options.browser);
|
|
91
|
+
const write = async () => await this.#upsertAt(projectId, storyId, viewport, branch, snapshotId, sourceBuildId, sourcePath, await this.contentFor(projectId, sourcePath), infraHash, options);
|
|
92
|
+
return isContentPath(projectId, sourcePath) ? await write() : await withContentClaim(this.storage, projectId, write);
|
|
93
|
+
}
|
|
94
|
+
async sourceBuildId(projectId, storyId, viewport, snapshotId, sourcePath, browser = DEFAULT_BROWSER) {
|
|
95
|
+
const snapshot = await this.db.get(this.db.tables.snapshots, snapshotId);
|
|
96
|
+
if (!snapshot || snapshot.projectId !== projectId || snapshot.storyId !== storyId || snapshot.viewportName !== viewport || (snapshot.browser ?? "chromium") !== browser || snapshot.screenshotPath !== sourcePath) return null;
|
|
97
|
+
return snapshot.inherited ? snapshot.sourceBuildId ?? null : snapshot.buildId;
|
|
98
|
+
}
|
|
99
|
+
async #upsertAt(projectId, storyId, viewport, branch, snapshotId, sourceBuildId, originalPath, screenshotPath, infraHash, options) {
|
|
100
|
+
const browser = options.browser ?? "chromium";
|
|
101
|
+
const existing = await this.getFor(projectId, storyId, viewport, branch, browser);
|
|
102
|
+
const lifecycle = this.db.buildReferenceLifecycle;
|
|
103
|
+
if (lifecycle) {
|
|
104
|
+
const approval = options.approval;
|
|
105
|
+
const baseline = await upsertAtomicBaseline(lifecycle, {
|
|
106
|
+
baselines: this.tables.baselines,
|
|
107
|
+
snapshots: this.db.tables.snapshots
|
|
108
|
+
}, {
|
|
109
|
+
id: existing?.id ?? ulid(),
|
|
110
|
+
existingId: existing?.id ?? null,
|
|
111
|
+
existingVersion: existing?.updatedAt ?? null,
|
|
112
|
+
projectId,
|
|
113
|
+
storyId,
|
|
114
|
+
viewport,
|
|
115
|
+
branch,
|
|
116
|
+
browser,
|
|
117
|
+
snapshotId,
|
|
118
|
+
sourceBuildId,
|
|
119
|
+
originalPath,
|
|
120
|
+
screenshotPath,
|
|
121
|
+
infraHash,
|
|
122
|
+
fingerprint: options.fingerprint,
|
|
123
|
+
mode: options.mode,
|
|
124
|
+
beforeWrite: approval ? async (tx) => await approveInTransaction(tx, this.db.tables, approval) : void 0
|
|
125
|
+
});
|
|
126
|
+
if (this.webhookTables) await emitWebhookEvent(this.db, this.webhookTables, projectId, existing ? "baseline:updated" : "baseline:created", {
|
|
127
|
+
baselineId: baseline.id,
|
|
128
|
+
storyId,
|
|
129
|
+
viewport,
|
|
130
|
+
browser,
|
|
131
|
+
mode: options.mode ?? null,
|
|
132
|
+
branch,
|
|
133
|
+
snapshotId
|
|
134
|
+
}, this.secret);
|
|
135
|
+
return baseline;
|
|
136
|
+
}
|
|
45
137
|
let baseline;
|
|
46
138
|
if (existing) {
|
|
47
139
|
baseline = await this.db.update(this.tables.baselines, existing.id, {
|
|
48
140
|
snapshotId,
|
|
141
|
+
sourceBuildId,
|
|
49
142
|
screenshotPath,
|
|
50
143
|
infraHash: infraHash ?? existing.infraHash ?? null,
|
|
144
|
+
fingerprint: options.fingerprint ?? existing.fingerprint ?? null,
|
|
145
|
+
mode: options.mode ?? existing.mode ?? null,
|
|
51
146
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
52
147
|
});
|
|
53
148
|
if (this.webhookTables) await emitWebhookEvent(this.db, this.webhookTables, projectId, "baseline:updated", {
|
|
54
149
|
baselineId: baseline.id,
|
|
55
150
|
storyId,
|
|
56
151
|
viewport,
|
|
152
|
+
browser,
|
|
153
|
+
mode: options.mode ?? null,
|
|
57
154
|
branch,
|
|
58
155
|
snapshotId
|
|
59
156
|
}, this.secret);
|
|
@@ -66,8 +163,12 @@ var BaselineModel = class {
|
|
|
66
163
|
viewportName: viewport,
|
|
67
164
|
branch,
|
|
68
165
|
snapshotId,
|
|
166
|
+
sourceBuildId,
|
|
69
167
|
screenshotPath,
|
|
70
168
|
infraHash: infraHash ?? null,
|
|
169
|
+
fingerprint: options.fingerprint ?? null,
|
|
170
|
+
mode: options.mode ?? null,
|
|
171
|
+
browser,
|
|
71
172
|
createdAt: now,
|
|
72
173
|
updatedAt: now
|
|
73
174
|
});
|
|
@@ -75,6 +176,8 @@ var BaselineModel = class {
|
|
|
75
176
|
baselineId: baseline.id,
|
|
76
177
|
storyId,
|
|
77
178
|
viewport,
|
|
179
|
+
browser,
|
|
180
|
+
mode: options.mode ?? null,
|
|
78
181
|
branch,
|
|
79
182
|
snapshotId
|
|
80
183
|
}, this.secret);
|
|
@@ -82,26 +185,48 @@ var BaselineModel = class {
|
|
|
82
185
|
return baseline;
|
|
83
186
|
}
|
|
84
187
|
async list(projectId) {
|
|
85
|
-
return await this.db.list(this.tables.baselines, { where: eq(
|
|
188
|
+
return await this.db.list(this.tables.baselines, { where: eq(col(this.tables.baselines, "projectId"), projectId) });
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Delete a baseline's own file. A content-addressed screenshot is shared with snapshots and other
|
|
192
|
+
* baselines, so it is left for the retention sweep instead.
|
|
193
|
+
*/
|
|
194
|
+
async discardFile(baseline) {
|
|
195
|
+
if (isContentPath(baseline.projectId, baseline.screenshotPath)) return;
|
|
196
|
+
try {
|
|
197
|
+
await this.storage.delete(baseline.screenshotPath);
|
|
198
|
+
} catch {}
|
|
199
|
+
}
|
|
200
|
+
/** Drop the reference and row together when the adapter supports lifecycle protection. */
|
|
201
|
+
async removeRecord(baseline) {
|
|
202
|
+
const lifecycle = this.db.buildReferenceLifecycle;
|
|
203
|
+
if (lifecycle) {
|
|
204
|
+
const owner = {
|
|
205
|
+
type: "baseline",
|
|
206
|
+
projectId: baseline.projectId,
|
|
207
|
+
id: baseline.id
|
|
208
|
+
};
|
|
209
|
+
const generation = await lifecycle.referenceGeneration(owner);
|
|
210
|
+
await lifecycle.replaceFencedReferences(owner, generation, [], async (tx) => {
|
|
211
|
+
const current = await tx.get(this.tables.baselines, baseline.id);
|
|
212
|
+
if (!current) return;
|
|
213
|
+
if (current.updatedAt !== baseline.updatedAt || current.snapshotId !== baseline.snapshotId || current.sourceBuildId !== baseline.sourceBuildId || current.screenshotPath !== baseline.screenshotPath) throw new BaselineWriteConflict("Baseline changed during removal; retry");
|
|
214
|
+
await tx.remove(this.tables.baselines, baseline.id);
|
|
215
|
+
});
|
|
216
|
+
await this.discardFile(baseline);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
await this.discardFile(baseline);
|
|
220
|
+
await this.db.remove(this.tables.baselines, baseline.id);
|
|
86
221
|
}
|
|
87
222
|
async removeOrphans(projectId, validStoryIds) {
|
|
88
223
|
const toRemove = (await this.list(projectId)).filter((baseline) => !validStoryIds.has(baseline.storyId));
|
|
89
|
-
await Promise.all(toRemove.map(async (baseline) =>
|
|
90
|
-
try {
|
|
91
|
-
await this.storage.delete(baseline.screenshotPath);
|
|
92
|
-
} catch {}
|
|
93
|
-
await this.db.remove(this.tables.baselines, baseline.id);
|
|
94
|
-
}));
|
|
224
|
+
await Promise.all(toRemove.map(async (baseline) => await this.removeRecord(baseline)));
|
|
95
225
|
return toRemove.length;
|
|
96
226
|
}
|
|
97
227
|
async removeForBranch(projectId, branch) {
|
|
98
228
|
const toRemove = (await this.list(projectId)).filter((baseline) => baseline.branch === branch);
|
|
99
|
-
await Promise.all(toRemove.map(async (baseline) =>
|
|
100
|
-
try {
|
|
101
|
-
await this.storage.delete(baseline.screenshotPath);
|
|
102
|
-
} catch {}
|
|
103
|
-
await this.db.remove(this.tables.baselines, baseline.id);
|
|
104
|
-
}));
|
|
229
|
+
await Promise.all(toRemove.map(async (baseline) => await this.removeRecord(baseline)));
|
|
105
230
|
return toRemove.length;
|
|
106
231
|
}
|
|
107
232
|
async removeStaleBranches(projectId, staleBranches) {
|
|
@@ -109,7 +234,6 @@ var BaselineModel = class {
|
|
|
109
234
|
return (await Promise.all([...staleBranches].map((branch) => this.removeForBranch(projectId, branch)))).reduce((sum, count) => sum + count, 0);
|
|
110
235
|
}
|
|
111
236
|
};
|
|
112
|
-
//#endregion
|
|
113
237
|
export { BaselineModel };
|
|
114
238
|
|
|
115
239
|
//# sourceMappingURL=baseline.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"baseline.mjs","names":[],"sources":["../../src/models/baseline.ts"],"sourcesContent":["/* oxlint-disable typescript/promise-function-async -- map callbacks return promises from async helpers */\n/** Per-branch baseline screenshots with default-branch fallback. */\nimport { and, eq, getTableColumns } from \"drizzle-orm\";\nimport type { SQLWrapper, Table } from \"drizzle-orm\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { StorageAdapter } from \"../adapters/storage.ts\";\nimport { emitWebhookEvent } from \"../adapters/webhook-events.ts\";\nimport type { Baseline } from \"../schema/baseline.ts\";\nimport type { SecretInput } from \"../utils/encrypt.ts\";\nimport { baselinePath } from \"../utils/paths.ts\";\nimport { ulid } from \"../utils/ulid.ts\";\nimport type { WebhookTables } from \"./webhook.ts\";\n\n/** Tables required by {@link BaselineModel}. */\nexport interface BaselineTables {\n baselines: Table;\n}\n\n/** Data operations for baseline screenshots. */\nexport class BaselineModel {\n private readonly tables: BaselineTables;\n private readonly storage: StorageAdapter;\n private readonly secret?: SecretInput;\n private readonly webhookTables?: WebhookTables;\n constructor(\n private readonly db: DatabaseAdapter,\n tables?: BaselineTables,\n storage?: StorageAdapter,\n secret?: SecretInput,\n webhookTables?: WebhookTables,\n ) {\n this.tables = tables ?? { baselines: db.tables.baselines };\n this.storage = storage as StorageAdapter;\n this.secret = secret;\n this.webhookTables = webhookTables;\n }\n\n async getFor(\n projectId: string,\n storyId: string,\n viewport: string,\n branch: string,\n ): Promise<Baseline | null> {\n const rows = (await this.db.list(this.tables.baselines, {\n where: and(\n eq(getTableColumns(this.tables.baselines)[\"projectId\"] as unknown as SQLWrapper, projectId),\n eq(getTableColumns(this.tables.baselines)[\"storyId\"] as unknown as SQLWrapper, storyId),\n eq(\n getTableColumns(this.tables.baselines)[\"viewportName\"] as unknown as SQLWrapper,\n viewport,\n ),\n eq(getTableColumns(this.tables.baselines)[\"branch\"] as unknown as SQLWrapper, branch),\n ),\n limit: 1,\n })) as unknown as Baseline[];\n return rows[0] ?? null;\n }\n\n async resolve(\n projectId: string,\n storyId: string,\n viewport: string,\n branch: string,\n defaultBranch: string,\n ): Promise<Baseline | null> {\n if (branch !== defaultBranch) {\n const own = await this.getFor(projectId, storyId, viewport, branch);\n if (own) {\n return own;\n }\n }\n return this.getFor(projectId, storyId, viewport, defaultBranch);\n }\n\n async read(baseline: Baseline): Promise<Buffer> {\n return await this.storage.read(baseline.screenshotPath);\n }\n\n async upsert(\n projectId: string,\n storyId: string,\n viewport: string,\n branch: string,\n snapshotId: string,\n sourcePath: string,\n infraHash?: string | null,\n ): Promise<Baseline> {\n const screenshotPath = baselinePath(projectId, branch, storyId, viewport);\n const source = await this.storage.read(sourcePath);\n await this.storage.write(screenshotPath, source);\n\n const existing = await this.getFor(projectId, storyId, viewport, branch);\n let baseline: Baseline;\n if (existing) {\n baseline = (await this.db.update(this.tables.baselines, existing.id, {\n snapshotId,\n screenshotPath,\n infraHash: infraHash ?? existing.infraHash ?? null,\n updatedAt: new Date().toISOString(),\n })) as unknown as Baseline;\n if (this.webhookTables) {\n await emitWebhookEvent(\n this.db,\n this.webhookTables,\n projectId,\n \"baseline:updated\",\n {\n baselineId: baseline.id,\n storyId,\n viewport,\n branch,\n snapshotId,\n },\n this.secret,\n );\n }\n } else {\n const now = new Date().toISOString();\n baseline = (await this.db.insert(this.tables.baselines, {\n id: ulid(),\n projectId,\n storyId,\n viewportName: viewport,\n branch,\n snapshotId,\n screenshotPath,\n infraHash: infraHash ?? null,\n createdAt: now,\n updatedAt: now,\n })) as unknown as Baseline;\n if (this.webhookTables) {\n await emitWebhookEvent(\n this.db,\n this.webhookTables,\n projectId,\n \"baseline:created\",\n {\n baselineId: baseline.id,\n storyId,\n viewport,\n branch,\n snapshotId,\n },\n this.secret,\n );\n }\n }\n return baseline;\n }\n\n async list(projectId: string): Promise<Baseline[]> {\n return (await this.db.list(this.tables.baselines, {\n where: eq(\n getTableColumns(this.tables.baselines)[\"projectId\"] as unknown as SQLWrapper,\n projectId,\n ),\n })) as unknown as Baseline[];\n }\n\n async removeOrphans(projectId: string, validStoryIds: Set<string>): Promise<number> {\n const all = await this.list(projectId);\n const toRemove = all.filter((baseline) => !validStoryIds.has(baseline.storyId));\n await Promise.all(\n toRemove.map(async (baseline) => {\n try {\n await this.storage.delete(baseline.screenshotPath);\n } catch {\n // ignore missing file\n }\n await this.db.remove(this.tables.baselines, baseline.id);\n }),\n );\n return toRemove.length;\n }\n\n async removeForBranch(projectId: string, branch: string): Promise<number> {\n const all = await this.list(projectId);\n const toRemove = all.filter((baseline) => baseline.branch === branch);\n await Promise.all(\n toRemove.map(async (baseline) => {\n try {\n await this.storage.delete(baseline.screenshotPath);\n } catch {\n // ignore missing file\n }\n await this.db.remove(this.tables.baselines, baseline.id);\n }),\n );\n return toRemove.length;\n }\n\n // oxlint-disable-next-line eslint/require-await -- delegates to async removeForBranch via Promise.all\n async removeStaleBranches(\n projectId: string,\n staleBranches: ReadonlySet<string>,\n ): Promise<number> {\n if (staleBranches.size === 0) {\n return 0;\n }\n const results = await Promise.all(\n [...staleBranches].map((branch) => this.removeForBranch(projectId, branch)),\n );\n return results.reduce((sum, count) => sum + count, 0);\n }\n}\n"],"mappings":";;;;;;;;;;AAmBA,IAAa,gBAAb,MAA2B;CAMN;CALnB;CACA;CACA;CACA;CACA,YACE,IACA,QACA,SACA,QACA,eACA;EALiB,KAAA,KAAA;EAMjB,KAAK,SAAS,UAAU,EAAE,WAAW,GAAG,OAAO,UAAU;EACzD,KAAK,UAAU;EACf,KAAK,SAAS;EACd,KAAK,gBAAgB;CACvB;CAEA,MAAM,OACJ,WACA,SACA,UACA,QAC0B;EAa1B,QAAO,MAZa,KAAK,GAAG,KAAK,KAAK,OAAO,WAAW;GACtD,OAAO,IACL,GAAG,gBAAgB,KAAK,OAAO,SAAS,CAAC,CAAC,cAAuC,SAAS,GAC1F,GAAG,gBAAgB,KAAK,OAAO,SAAS,CAAC,CAAC,YAAqC,OAAO,GACtF,GACE,gBAAgB,KAAK,OAAO,SAAS,CAAC,CAAC,iBACvC,QACF,GACA,GAAG,gBAAgB,KAAK,OAAO,SAAS,CAAC,CAAC,WAAoC,MAAM,CACtF;GACA,OAAO;EACT,CAAC,EAAA,CACW,MAAM;CACpB;CAEA,MAAM,QACJ,WACA,SACA,UACA,QACA,eAC0B;EAC1B,IAAI,WAAW,eAAe;GAC5B,MAAM,MAAM,MAAM,KAAK,OAAO,WAAW,SAAS,UAAU,MAAM;GAClE,IAAI,KACF,OAAO;EAEX;EACA,OAAO,KAAK,OAAO,WAAW,SAAS,UAAU,aAAa;CAChE;CAEA,MAAM,KAAK,UAAqC;EAC9C,OAAO,MAAM,KAAK,QAAQ,KAAK,SAAS,cAAc;CACxD;CAEA,MAAM,OACJ,WACA,SACA,UACA,QACA,YACA,YACA,WACmB;EACnB,MAAM,iBAAiB,aAAa,WAAW,QAAQ,SAAS,QAAQ;EACxE,MAAM,SAAS,MAAM,KAAK,QAAQ,KAAK,UAAU;EACjD,MAAM,KAAK,QAAQ,MAAM,gBAAgB,MAAM;EAE/C,MAAM,WAAW,MAAM,KAAK,OAAO,WAAW,SAAS,UAAU,MAAM;EACvE,IAAI;EACJ,IAAI,UAAU;GACZ,WAAY,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,WAAW,SAAS,IAAI;IACnE;IACA;IACA,WAAW,aAAa,SAAS,aAAa;IAC9C,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;GACpC,CAAC;GACD,IAAI,KAAK,eACP,MAAM,iBACJ,KAAK,IACL,KAAK,eACL,WACA,oBACA;IACE,YAAY,SAAS;IACrB;IACA;IACA;IACA;GACF,GACA,KAAK,MACP;EAEJ,OAAO;GACL,MAAM,uBAAM,IAAI,KAAK,EAAA,CAAE,YAAY;GACnC,WAAY,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,WAAW;IACtD,IAAI,KAAK;IACT;IACA;IACA,cAAc;IACd;IACA;IACA;IACA,WAAW,aAAa;IACxB,WAAW;IACX,WAAW;GACb,CAAC;GACD,IAAI,KAAK,eACP,MAAM,iBACJ,KAAK,IACL,KAAK,eACL,WACA,oBACA;IACE,YAAY,SAAS;IACrB;IACA;IACA;IACA;GACF,GACA,KAAK,MACP;EAEJ;EACA,OAAO;CACT;CAEA,MAAM,KAAK,WAAwC;EACjD,OAAQ,MAAM,KAAK,GAAG,KAAK,KAAK,OAAO,WAAW,EAChD,OAAO,GACL,gBAAgB,KAAK,OAAO,SAAS,CAAC,CAAC,cACvC,SACF,EACF,CAAC;CACH;CAEA,MAAM,cAAc,WAAmB,eAA6C;EAElF,MAAM,YAAW,MADC,KAAK,KAAK,SAAS,EAAA,CAChB,QAAQ,aAAa,CAAC,cAAc,IAAI,SAAS,OAAO,CAAC;EAC9E,MAAM,QAAQ,IACZ,SAAS,IAAI,OAAO,aAAa;GAC/B,IAAI;IACF,MAAM,KAAK,QAAQ,OAAO,SAAS,cAAc;GACnD,QAAQ,CAER;GACA,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,WAAW,SAAS,EAAE;EACzD,CAAC,CACH;EACA,OAAO,SAAS;CAClB;CAEA,MAAM,gBAAgB,WAAmB,QAAiC;EAExE,MAAM,YAAW,MADC,KAAK,KAAK,SAAS,EAAA,CAChB,QAAQ,aAAa,SAAS,WAAW,MAAM;EACpE,MAAM,QAAQ,IACZ,SAAS,IAAI,OAAO,aAAa;GAC/B,IAAI;IACF,MAAM,KAAK,QAAQ,OAAO,SAAS,cAAc;GACnD,QAAQ,CAER;GACA,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,WAAW,SAAS,EAAE;EACzD,CAAC,CACH;EACA,OAAO,SAAS;CAClB;CAGA,MAAM,oBACJ,WACA,eACiB;EACjB,IAAI,cAAc,SAAS,GACzB,OAAO;EAKT,QAAO,MAHe,QAAQ,IAC5B,CAAC,GAAG,aAAa,CAAC,CAAC,KAAK,WAAW,KAAK,gBAAgB,WAAW,MAAM,CAAC,CAC5E,EAAA,CACe,QAAQ,KAAK,UAAU,MAAM,OAAO,CAAC;CACtD;AACF"}
|
|
1
|
+
{"version":3,"file":"baseline.mjs","names":[],"sources":["../../src/models/baseline.ts"],"sourcesContent":["/* oxlint-disable typescript/promise-function-async -- map callbacks return promises from async helpers */\n/** Per-branch baseline screenshots with default-branch fallback. */\nimport { and, eq } from \"drizzle-orm\";\nimport type { Table } from \"drizzle-orm\";\nimport { DEFAULT_BROWSER } from \"../adapters/capture-runner.ts\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { StorageAdapter } from \"../adapters/storage.ts\";\nimport { emitWebhookEvent } from \"../adapters/webhook-events.ts\";\nimport type { Baseline } from \"../schema/baseline.ts\";\nimport type { Snapshot } from \"../schema/snapshot.ts\";\nimport type { SecretInput } from \"../utils/encrypt.ts\";\nimport { isContentPath } from \"../utils/paths.ts\";\nimport { storeScreenshot, withContentClaim } from \"../utils/screenshot-store.ts\";\nimport { ulid } from \"../utils/ulid.ts\";\nimport { approveInTransaction, type AtomicBaselineApproval } from \"./baseline-approval.ts\";\nimport { upsertAtomicBaseline } from \"./baseline-atomic.ts\";\nimport { BaselineWriteConflict } from \"./baseline-conflict.ts\";\nimport { col } from \"./columns.ts\";\nimport type { WebhookTables } from \"./webhook.ts\";\n\n/** Extra identity/metadata for {@link BaselineModel.upsert}. */\nexport interface BaselineUpsertOptions {\n approval?: AtomicBaselineApproval;\n /** Engine the baseline belongs to (default chromium). */\n browser?: string;\n /** Resolved mode definition as JSON (display only). */\n mode?: string | null;\n /** JSON `Fingerprint` behind the `infraHash` being written. */\n fingerprint?: string | null;\n}\n\n/** Tables required by {@link BaselineModel}. */\nexport interface BaselineTables {\n baselines: Table;\n}\n\n/** Data operations for baseline screenshots. */\nexport class BaselineModel {\n private readonly tables: BaselineTables;\n private readonly storage: StorageAdapter;\n private readonly secret?: SecretInput;\n private readonly webhookTables?: WebhookTables;\n private readonly db: DatabaseAdapter;\n\n constructor(\n db: DatabaseAdapter,\n tables?: BaselineTables,\n storage?: StorageAdapter,\n secret?: SecretInput,\n webhookTables?: WebhookTables,\n ) {\n this.db = db;\n this.tables = tables ?? { baselines: db.tables.baselines };\n this.storage = storage as StorageAdapter;\n this.secret = secret;\n this.webhookTables = webhookTables;\n }\n\n async getFor(\n projectId: string,\n storyId: string,\n viewport: string,\n branch: string,\n browser: string = DEFAULT_BROWSER,\n ): Promise<Baseline | null> {\n const rows = (await this.db.list(this.tables.baselines, {\n where: and(\n eq(col(this.tables.baselines, \"projectId\"), projectId),\n eq(col(this.tables.baselines, \"storyId\"), storyId),\n eq(col(this.tables.baselines, \"viewportName\"), viewport),\n eq(col(this.tables.baselines, \"branch\"), branch),\n eq(col(this.tables.baselines, \"browser\"), browser),\n ),\n limit: 1,\n })) as unknown as Baseline[];\n return rows[0] ?? null;\n }\n\n async resolve(\n projectId: string,\n storyId: string,\n viewport: string,\n branch: string,\n defaultBranch: string,\n browser: string = DEFAULT_BROWSER,\n ): Promise<Baseline | null> {\n if (branch !== defaultBranch) {\n const own = await this.getFor(projectId, storyId, viewport, branch, browser);\n if (own) {\n return own;\n }\n }\n return this.getFor(projectId, storyId, viewport, defaultBranch, browser);\n }\n\n /** Whether the project has any baseline captured on this engine. */\n async hasAny(projectId: string, browser: string): Promise<boolean> {\n const rows = (await this.db.list(this.tables.baselines, {\n where: and(\n eq(col(this.tables.baselines, \"projectId\"), projectId),\n eq(col(this.tables.baselines, \"browser\"), browser),\n ),\n limit: 1,\n })) as unknown as Baseline[];\n return rows.length > 0;\n }\n\n /**\n * Record the fingerprint of a baseline that predates fingerprints, without touching its\n * screenshot. Lets the next environment change be detected instead of diffing as a regression.\n */\n async backfillFingerprint(\n baseline: Baseline,\n infraHash: string,\n fingerprint: string,\n ): Promise<void> {\n const lifecycle = this.db.buildReferenceLifecycle;\n if (lifecycle) {\n const owner = { type: \"baseline\" as const, id: baseline.id, projectId: baseline.projectId };\n const generation = await lifecycle.referenceGeneration(owner);\n await lifecycle.replaceFencedReferences(\n owner,\n generation,\n baseline.sourceBuildId ? [baseline.sourceBuildId] : [],\n async (tx) => {\n const current = (await tx.get(\n this.tables.baselines,\n baseline.id,\n )) as unknown as Baseline | null;\n if (\n !current ||\n current.projectId !== baseline.projectId ||\n current.updatedAt !== baseline.updatedAt ||\n current.sourceBuildId !== baseline.sourceBuildId ||\n current.screenshotPath !== baseline.screenshotPath ||\n current.fingerprint !== baseline.fingerprint\n )\n throw new BaselineWriteConflict(\"Baseline changed during fingerprint backfill; retry\");\n await tx.update(this.tables.baselines, baseline.id, { infraHash, fingerprint });\n },\n );\n return;\n }\n await this.db.update(this.tables.baselines, baseline.id, { infraHash, fingerprint });\n }\n\n /**\n * The content-addressed object a baseline should point at. A source that already is one is\n * reused as is (no copy); a legacy per-build path is stored once by content.\n */\n private async contentFor(projectId: string, sourcePath: string): Promise<string> {\n if (isContentPath(projectId, sourcePath)) {\n return sourcePath;\n }\n return await storeScreenshot(this.storage, projectId, await this.storage.read(sourcePath));\n }\n\n async read(baseline: Baseline): Promise<Buffer> {\n return await this.storage.read(baseline.screenshotPath);\n }\n\n async upsert(\n projectId: string,\n storyId: string,\n viewport: string,\n branch: string,\n snapshotId: string,\n sourcePath: string,\n infraHash?: string | null,\n options: BaselineUpsertOptions = {},\n ): Promise<Baseline> {\n const sourceBuildId = await this.sourceBuildId(\n projectId,\n storyId,\n viewport,\n snapshotId,\n sourcePath,\n options.browser,\n );\n const write = async (): Promise<Baseline> =>\n await this.#upsertAt(\n projectId,\n storyId,\n viewport,\n branch,\n snapshotId,\n sourceBuildId,\n sourcePath,\n await this.contentFor(projectId, sourcePath),\n infraHash,\n options,\n );\n // Storing a legacy source may reuse an object the sweep is about to delete: claim it until the\n // row points at it. A content source is already referenced by its snapshot row.\n return isContentPath(projectId, sourcePath)\n ? await write()\n : await withContentClaim(this.storage, projectId, write);\n }\n\n private async sourceBuildId(\n projectId: string,\n storyId: string,\n viewport: string,\n snapshotId: string,\n sourcePath: string,\n browser: string = DEFAULT_BROWSER,\n ): Promise<string | null> {\n const snapshot = (await this.db.get(\n this.db.tables.snapshots,\n snapshotId,\n )) as unknown as Snapshot | null;\n if (\n !snapshot ||\n snapshot.projectId !== projectId ||\n snapshot.storyId !== storyId ||\n snapshot.viewportName !== viewport ||\n (snapshot.browser ?? DEFAULT_BROWSER) !== browser ||\n snapshot.screenshotPath !== sourcePath\n )\n return null;\n return snapshot.inherited ? (snapshot.sourceBuildId ?? null) : snapshot.buildId;\n }\n\n async #upsertAt(\n projectId: string,\n storyId: string,\n viewport: string,\n branch: string,\n snapshotId: string,\n sourceBuildId: string | null,\n originalPath: string,\n screenshotPath: string,\n infraHash: string | null | undefined,\n options: BaselineUpsertOptions,\n ): Promise<Baseline> {\n const browser = options.browser ?? DEFAULT_BROWSER;\n\n const existing = await this.getFor(projectId, storyId, viewport, branch, browser);\n const lifecycle = this.db.buildReferenceLifecycle;\n if (lifecycle) {\n const approval = options.approval;\n const baseline = await upsertAtomicBaseline(\n lifecycle,\n { baselines: this.tables.baselines, snapshots: this.db.tables.snapshots },\n {\n id: existing?.id ?? ulid(),\n existingId: existing?.id ?? null,\n existingVersion: existing?.updatedAt ?? null,\n projectId,\n storyId,\n viewport,\n branch,\n browser,\n snapshotId,\n sourceBuildId,\n originalPath,\n screenshotPath,\n infraHash,\n fingerprint: options.fingerprint,\n mode: options.mode,\n beforeWrite: approval\n ? async (tx): Promise<void> => await approveInTransaction(tx, this.db.tables, approval)\n : undefined,\n },\n );\n if (this.webhookTables) {\n await emitWebhookEvent(\n this.db,\n this.webhookTables,\n projectId,\n existing ? \"baseline:updated\" : \"baseline:created\",\n {\n baselineId: baseline.id,\n storyId,\n viewport,\n browser,\n mode: options.mode ?? null,\n branch,\n snapshotId,\n },\n this.secret,\n );\n }\n return baseline;\n }\n let baseline: Baseline;\n if (existing) {\n baseline = (await this.db.update(this.tables.baselines, existing.id, {\n snapshotId,\n sourceBuildId,\n screenshotPath,\n infraHash: infraHash ?? existing.infraHash ?? null,\n fingerprint: options.fingerprint ?? existing.fingerprint ?? null,\n mode: options.mode ?? existing.mode ?? null,\n updatedAt: new Date().toISOString(),\n })) as unknown as Baseline;\n if (this.webhookTables) {\n await emitWebhookEvent(\n this.db,\n this.webhookTables,\n projectId,\n \"baseline:updated\",\n {\n baselineId: baseline.id,\n storyId,\n viewport,\n browser,\n mode: options.mode ?? null,\n branch,\n snapshotId,\n },\n this.secret,\n );\n }\n } else {\n const now = new Date().toISOString();\n baseline = (await this.db.insert(this.tables.baselines, {\n id: ulid(),\n projectId,\n storyId,\n viewportName: viewport,\n branch,\n snapshotId,\n sourceBuildId,\n screenshotPath,\n infraHash: infraHash ?? null,\n fingerprint: options.fingerprint ?? null,\n mode: options.mode ?? null,\n browser,\n createdAt: now,\n updatedAt: now,\n })) as unknown as Baseline;\n if (this.webhookTables) {\n await emitWebhookEvent(\n this.db,\n this.webhookTables,\n projectId,\n \"baseline:created\",\n {\n baselineId: baseline.id,\n storyId,\n viewport,\n browser,\n mode: options.mode ?? null,\n branch,\n snapshotId,\n },\n this.secret,\n );\n }\n }\n return baseline;\n }\n\n async list(projectId: string): Promise<Baseline[]> {\n return (await this.db.list(this.tables.baselines, {\n where: eq(col(this.tables.baselines, \"projectId\"), projectId),\n })) as unknown as Baseline[];\n }\n\n /**\n * Delete a baseline's own file. A content-addressed screenshot is shared with snapshots and other\n * baselines, so it is left for the retention sweep instead.\n */\n private async discardFile(baseline: Baseline): Promise<void> {\n if (isContentPath(baseline.projectId, baseline.screenshotPath)) {\n return;\n }\n try {\n await this.storage.delete(baseline.screenshotPath);\n } catch {\n // ignore missing file\n }\n }\n\n /** Drop the reference and row together when the adapter supports lifecycle protection. */\n private async removeRecord(baseline: Baseline): Promise<void> {\n const lifecycle = this.db.buildReferenceLifecycle;\n if (lifecycle) {\n const owner = { type: \"baseline\" as const, projectId: baseline.projectId, id: baseline.id };\n const generation = await lifecycle.referenceGeneration(owner);\n await lifecycle.replaceFencedReferences(owner, generation, [], async (tx) => {\n const current = (await tx.get(\n this.tables.baselines,\n baseline.id,\n )) as unknown as Baseline | null;\n if (!current) return;\n if (\n current.updatedAt !== baseline.updatedAt ||\n current.snapshotId !== baseline.snapshotId ||\n current.sourceBuildId !== baseline.sourceBuildId ||\n current.screenshotPath !== baseline.screenshotPath\n ) {\n throw new BaselineWriteConflict(\"Baseline changed during removal; retry\");\n }\n await tx.remove(this.tables.baselines, baseline.id);\n });\n await this.discardFile(baseline);\n return;\n }\n await this.discardFile(baseline);\n await this.db.remove(this.tables.baselines, baseline.id);\n }\n\n async removeOrphans(projectId: string, validStoryIds: Set<string>): Promise<number> {\n const all = await this.list(projectId);\n const toRemove = all.filter((baseline) => !validStoryIds.has(baseline.storyId));\n await Promise.all(toRemove.map(async (baseline) => await this.removeRecord(baseline)));\n return toRemove.length;\n }\n\n async removeForBranch(projectId: string, branch: string): Promise<number> {\n const all = await this.list(projectId);\n const toRemove = all.filter((baseline) => baseline.branch === branch);\n await Promise.all(toRemove.map(async (baseline) => await this.removeRecord(baseline)));\n return toRemove.length;\n }\n\n // oxlint-disable-next-line eslint/require-await -- delegates to async removeForBranch via Promise.all\n async removeStaleBranches(\n projectId: string,\n staleBranches: ReadonlySet<string>,\n ): Promise<number> {\n if (staleBranches.size === 0) {\n return 0;\n }\n const results = await Promise.all(\n [...staleBranches].map((branch) => this.removeForBranch(projectId, branch)),\n );\n return results.reduce((sum, count) => sum + count, 0);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAqCA,IAAa,gBAAb,MAA2B;CACzB;CACA;CACA;CACA;CACA;CAEA,YACE,IACA,QACA,SACA,QACA,eACA;EACA,KAAK,KAAK;EACV,KAAK,SAAS,UAAU,EAAE,WAAW,GAAG,OAAO,UAAU;EACzD,KAAK,UAAU;EACf,KAAK,SAAS;EACd,KAAK,gBAAgB;CACvB;CAEA,MAAM,OACJ,WACA,SACA,UACA,QACA,UAAkB,iBACQ;EAW1B,QAAO,MAVa,KAAK,GAAG,KAAK,KAAK,OAAO,WAAW;GACtD,OAAO,IACL,GAAG,IAAI,KAAK,OAAO,WAAW,WAAW,GAAG,SAAS,GACrD,GAAG,IAAI,KAAK,OAAO,WAAW,SAAS,GAAG,OAAO,GACjD,GAAG,IAAI,KAAK,OAAO,WAAW,cAAc,GAAG,QAAQ,GACvD,GAAG,IAAI,KAAK,OAAO,WAAW,QAAQ,GAAG,MAAM,GAC/C,GAAG,IAAI,KAAK,OAAO,WAAW,SAAS,GAAG,OAAO,CACnD;GACA,OAAO;EACT,CAAC,EAAA,CACW,MAAM;CACpB;CAEA,MAAM,QACJ,WACA,SACA,UACA,QACA,eACA,UAAkB,iBACQ;EAC1B,IAAI,WAAW,eAAe;GAC5B,MAAM,MAAM,MAAM,KAAK,OAAO,WAAW,SAAS,UAAU,QAAQ,OAAO;GAC3E,IAAI,KACF,OAAO;EAEX;EACA,OAAO,KAAK,OAAO,WAAW,SAAS,UAAU,eAAe,OAAO;CACzE;;CAGA,MAAM,OAAO,WAAmB,SAAmC;EAQjE,QAAO,MAPa,KAAK,GAAG,KAAK,KAAK,OAAO,WAAW;GACtD,OAAO,IACL,GAAG,IAAI,KAAK,OAAO,WAAW,WAAW,GAAG,SAAS,GACrD,GAAG,IAAI,KAAK,OAAO,WAAW,SAAS,GAAG,OAAO,CACnD;GACA,OAAO;EACT,CAAC,EAAA,CACW,SAAS;CACvB;;;;;CAMA,MAAM,oBACJ,UACA,WACA,aACe;EACf,MAAM,YAAY,KAAK,GAAG;EAC1B,IAAI,WAAW;GACb,MAAM,QAAQ;IAAE,MAAM;IAAqB,IAAI,SAAS;IAAI,WAAW,SAAS;GAAU;GAC1F,MAAM,aAAa,MAAM,UAAU,oBAAoB,KAAK;GAC5D,MAAM,UAAU,wBACd,OACA,YACA,SAAS,gBAAgB,CAAC,SAAS,aAAa,IAAI,CAAC,GACrD,OAAO,OAAO;IACZ,MAAM,UAAW,MAAM,GAAG,IACxB,KAAK,OAAO,WACZ,SAAS,EACX;IACA,IACE,CAAC,WACD,QAAQ,cAAc,SAAS,aAC/B,QAAQ,cAAc,SAAS,aAC/B,QAAQ,kBAAkB,SAAS,iBACnC,QAAQ,mBAAmB,SAAS,kBACpC,QAAQ,gBAAgB,SAAS,aAEjC,MAAM,IAAI,sBAAsB,qDAAqD;IACvF,MAAM,GAAG,OAAO,KAAK,OAAO,WAAW,SAAS,IAAI;KAAE;KAAW;IAAY,CAAC;GAChF,CACF;GACA;EACF;EACA,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,WAAW,SAAS,IAAI;GAAE;GAAW;EAAY,CAAC;CACrF;;;;;CAMA,MAAc,WAAW,WAAmB,YAAqC;EAC/E,IAAI,cAAc,WAAW,UAAU,GACrC,OAAO;EAET,OAAO,MAAM,gBAAgB,KAAK,SAAS,WAAW,MAAM,KAAK,QAAQ,KAAK,UAAU,CAAC;CAC3F;CAEA,MAAM,KAAK,UAAqC;EAC9C,OAAO,MAAM,KAAK,QAAQ,KAAK,SAAS,cAAc;CACxD;CAEA,MAAM,OACJ,WACA,SACA,UACA,QACA,YACA,YACA,WACA,UAAiC,CAAC,GACf;EACnB,MAAM,gBAAgB,MAAM,KAAK,cAC/B,WACA,SACA,UACA,YACA,YACA,QAAQ,OACV;EACA,MAAM,QAAQ,YACZ,MAAM,KAAK,UACT,WACA,SACA,UACA,QACA,YACA,eACA,YACA,MAAM,KAAK,WAAW,WAAW,UAAU,GAC3C,WACA,OACF;EAGF,OAAO,cAAc,WAAW,UAAU,IACtC,MAAM,MAAM,IACZ,MAAM,iBAAiB,KAAK,SAAS,WAAW,KAAK;CAC3D;CAEA,MAAc,cACZ,WACA,SACA,UACA,YACA,YACA,UAAkB,iBACM;EACxB,MAAM,WAAY,MAAM,KAAK,GAAG,IAC9B,KAAK,GAAG,OAAO,WACf,UACF;EACA,IACE,CAAC,YACD,SAAS,cAAc,aACvB,SAAS,YAAY,WACrB,SAAS,iBAAiB,aACzB,SAAS,WAAA,gBAAgC,WAC1C,SAAS,mBAAmB,YAE5B,OAAO;EACT,OAAO,SAAS,YAAa,SAAS,iBAAiB,OAAQ,SAAS;CAC1E;CAEA,MAAM,UACJ,WACA,SACA,UACA,QACA,YACA,eACA,cACA,gBACA,WACA,SACmB;EACnB,MAAM,UAAU,QAAQ,WAAA;EAExB,MAAM,WAAW,MAAM,KAAK,OAAO,WAAW,SAAS,UAAU,QAAQ,OAAO;EAChF,MAAM,YAAY,KAAK,GAAG;EAC1B,IAAI,WAAW;GACb,MAAM,WAAW,QAAQ;GACzB,MAAM,WAAW,MAAM,qBACrB,WACA;IAAE,WAAW,KAAK,OAAO;IAAW,WAAW,KAAK,GAAG,OAAO;GAAU,GACxE;IACE,IAAI,UAAU,MAAM,KAAK;IACzB,YAAY,UAAU,MAAM;IAC5B,iBAAiB,UAAU,aAAa;IACxC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,aAAa,QAAQ;IACrB,MAAM,QAAQ;IACd,aAAa,WACT,OAAO,OAAsB,MAAM,qBAAqB,IAAI,KAAK,GAAG,QAAQ,QAAQ,IACpF,KAAA;GACN,CACF;GACA,IAAI,KAAK,eACP,MAAM,iBACJ,KAAK,IACL,KAAK,eACL,WACA,WAAW,qBAAqB,oBAChC;IACE,YAAY,SAAS;IACrB;IACA;IACA;IACA,MAAM,QAAQ,QAAQ;IACtB;IACA;GACF,GACA,KAAK,MACP;GAEF,OAAO;EACT;EACA,IAAI;EACJ,IAAI,UAAU;GACZ,WAAY,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,WAAW,SAAS,IAAI;IACnE;IACA;IACA;IACA,WAAW,aAAa,SAAS,aAAa;IAC9C,aAAa,QAAQ,eAAe,SAAS,eAAe;IAC5D,MAAM,QAAQ,QAAQ,SAAS,QAAQ;IACvC,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;GACpC,CAAC;GACD,IAAI,KAAK,eACP,MAAM,iBACJ,KAAK,IACL,KAAK,eACL,WACA,oBACA;IACE,YAAY,SAAS;IACrB;IACA;IACA;IACA,MAAM,QAAQ,QAAQ;IACtB;IACA;GACF,GACA,KAAK,MACP;EAEJ,OAAO;GACL,MAAM,uBAAM,IAAI,KAAK,EAAA,CAAE,YAAY;GACnC,WAAY,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,WAAW;IACtD,IAAI,KAAK;IACT;IACA;IACA,cAAc;IACd;IACA;IACA;IACA;IACA,WAAW,aAAa;IACxB,aAAa,QAAQ,eAAe;IACpC,MAAM,QAAQ,QAAQ;IACtB;IACA,WAAW;IACX,WAAW;GACb,CAAC;GACD,IAAI,KAAK,eACP,MAAM,iBACJ,KAAK,IACL,KAAK,eACL,WACA,oBACA;IACE,YAAY,SAAS;IACrB;IACA;IACA;IACA,MAAM,QAAQ,QAAQ;IACtB;IACA;GACF,GACA,KAAK,MACP;EAEJ;EACA,OAAO;CACT;CAEA,MAAM,KAAK,WAAwC;EACjD,OAAQ,MAAM,KAAK,GAAG,KAAK,KAAK,OAAO,WAAW,EAChD,OAAO,GAAG,IAAI,KAAK,OAAO,WAAW,WAAW,GAAG,SAAS,EAC9D,CAAC;CACH;;;;;CAMA,MAAc,YAAY,UAAmC;EAC3D,IAAI,cAAc,SAAS,WAAW,SAAS,cAAc,GAC3D;EAEF,IAAI;GACF,MAAM,KAAK,QAAQ,OAAO,SAAS,cAAc;EACnD,QAAQ,CAER;CACF;;CAGA,MAAc,aAAa,UAAmC;EAC5D,MAAM,YAAY,KAAK,GAAG;EAC1B,IAAI,WAAW;GACb,MAAM,QAAQ;IAAE,MAAM;IAAqB,WAAW,SAAS;IAAW,IAAI,SAAS;GAAG;GAC1F,MAAM,aAAa,MAAM,UAAU,oBAAoB,KAAK;GAC5D,MAAM,UAAU,wBAAwB,OAAO,YAAY,CAAC,GAAG,OAAO,OAAO;IAC3E,MAAM,UAAW,MAAM,GAAG,IACxB,KAAK,OAAO,WACZ,SAAS,EACX;IACA,IAAI,CAAC,SAAS;IACd,IACE,QAAQ,cAAc,SAAS,aAC/B,QAAQ,eAAe,SAAS,cAChC,QAAQ,kBAAkB,SAAS,iBACnC,QAAQ,mBAAmB,SAAS,gBAEpC,MAAM,IAAI,sBAAsB,wCAAwC;IAE1E,MAAM,GAAG,OAAO,KAAK,OAAO,WAAW,SAAS,EAAE;GACpD,CAAC;GACD,MAAM,KAAK,YAAY,QAAQ;GAC/B;EACF;EACA,MAAM,KAAK,YAAY,QAAQ;EAC/B,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,WAAW,SAAS,EAAE;CACzD;CAEA,MAAM,cAAc,WAAmB,eAA6C;EAElF,MAAM,YAAW,MADC,KAAK,KAAK,SAAS,EAAA,CAChB,QAAQ,aAAa,CAAC,cAAc,IAAI,SAAS,OAAO,CAAC;EAC9E,MAAM,QAAQ,IAAI,SAAS,IAAI,OAAO,aAAa,MAAM,KAAK,aAAa,QAAQ,CAAC,CAAC;EACrF,OAAO,SAAS;CAClB;CAEA,MAAM,gBAAgB,WAAmB,QAAiC;EAExE,MAAM,YAAW,MADC,KAAK,KAAK,SAAS,EAAA,CAChB,QAAQ,aAAa,SAAS,WAAW,MAAM;EACpE,MAAM,QAAQ,IAAI,SAAS,IAAI,OAAO,aAAa,MAAM,KAAK,aAAa,QAAQ,CAAC,CAAC;EACrF,OAAO,SAAS;CAClB;CAGA,MAAM,oBACJ,WACA,eACiB;EACjB,IAAI,cAAc,SAAS,GACzB,OAAO;EAKT,QAAO,MAHe,QAAQ,IAC5B,CAAC,GAAG,aAAa,CAAC,CAAC,KAAK,WAAW,KAAK,gBAAgB,WAAW,MAAM,CAAC,CAC5E,EAAA,CACe,QAAQ,KAAK,UAAU,MAAM,OAAO,CAAC;CACtD;AACF"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { DatabaseAdapter } from "../adapter/database.mjs";
|
|
4
|
+
import { BuildComponent } from "../schema/build-component.mjs";
|
|
5
|
+
import { Table } from "drizzle-orm";
|
|
6
|
+
/** Tables required by {@link BuildComponentModel}. */
|
|
7
|
+
interface BuildComponentTables {
|
|
8
|
+
buildComponents: Table;
|
|
9
|
+
}
|
|
10
|
+
/** What a build records of one component. */
|
|
11
|
+
type BuildComponentInput = Pick<BuildComponent, "importPath" | "title" | "storyCount" | "hash">;
|
|
12
|
+
/** Data operations for the components each published build holds. */
|
|
13
|
+
declare class BuildComponentModel {
|
|
14
|
+
#private;
|
|
15
|
+
constructor(db: DatabaseAdapter, tables?: BuildComponentTables);
|
|
16
|
+
/** Components of one build, ordered by title then file. */
|
|
17
|
+
listByBuild(buildId: string): Promise<BuildComponent[]>;
|
|
18
|
+
/** Rows of one story file across the project's builds, newest first: "which builds contain it". */
|
|
19
|
+
listByImportPath(projectId: string, importPath: string): Promise<BuildComponent[]>;
|
|
20
|
+
/** Replace a build's rows with `components`; repeating a publish never duplicates. */
|
|
21
|
+
replaceForBuild(projectId: string, buildId: string, components: readonly BuildComponentInput[]): Promise<void>;
|
|
22
|
+
/** Forget a build's rows (the foreign key cascades too; stub tables have none). */
|
|
23
|
+
removeForBuild(buildId: string): Promise<number>;
|
|
24
|
+
}
|
|
25
|
+
export { BuildComponentInput, BuildComponentModel, BuildComponentTables };
|
|
26
|
+
//# sourceMappingURL=build-component.d.mts.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { ulid } from "../utils/ulid.mjs";
|
|
5
|
+
import { col } from "./columns.mjs";
|
|
6
|
+
import { and, asc, desc, eq } from "drizzle-orm";
|
|
7
|
+
// src/models/build-component.ts
|
|
8
|
+
/** Component rows of published builds (`build_components`, ADR 0006 decision 9). */
|
|
9
|
+
/** Data operations for the components each published build holds. */
|
|
10
|
+
var BuildComponentModel = class {
|
|
11
|
+
#db;
|
|
12
|
+
#table;
|
|
13
|
+
constructor(db, tables) {
|
|
14
|
+
this.#db = db;
|
|
15
|
+
this.#table = (tables ?? { buildComponents: db.tables.buildComponents }).buildComponents;
|
|
16
|
+
}
|
|
17
|
+
/** Components of one build, ordered by title then file. */
|
|
18
|
+
async listByBuild(buildId) {
|
|
19
|
+
return (await this.#db.list(this.#table, {
|
|
20
|
+
where: eq(col(this.#table, "buildId"), buildId),
|
|
21
|
+
orderBy: asc(col(this.#table, "title"))
|
|
22
|
+
})).toSorted((a, b) => a.title.localeCompare(b.title) || a.importPath.localeCompare(b.importPath));
|
|
23
|
+
}
|
|
24
|
+
/** Rows of one story file across the project's builds, newest first: "which builds contain it". */
|
|
25
|
+
async listByImportPath(projectId, importPath) {
|
|
26
|
+
return await this.#db.list(this.#table, {
|
|
27
|
+
where: and(eq(col(this.#table, "projectId"), projectId), eq(col(this.#table, "importPath"), importPath)),
|
|
28
|
+
orderBy: desc(col(this.#table, "createdAt"))
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/** Replace a build's rows with `components`; repeating a publish never duplicates. */
|
|
32
|
+
async replaceForBuild(projectId, buildId, components) {
|
|
33
|
+
await this.removeForBuild(buildId);
|
|
34
|
+
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
35
|
+
for (const component of components) await this.#db.insert(this.#table, {
|
|
36
|
+
id: ulid(),
|
|
37
|
+
projectId,
|
|
38
|
+
buildId,
|
|
39
|
+
createdAt,
|
|
40
|
+
...component
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/** Forget a build's rows (the foreign key cascades too; stub tables have none). */
|
|
44
|
+
async removeForBuild(buildId) {
|
|
45
|
+
const rows = await this.#db.list(this.#table, { where: eq(col(this.#table, "buildId"), buildId) });
|
|
46
|
+
await Promise.all(rows.map(async (row) => {
|
|
47
|
+
await this.#db.remove(this.#table, row.id);
|
|
48
|
+
}));
|
|
49
|
+
return rows.length;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
export { BuildComponentModel };
|
|
53
|
+
|
|
54
|
+
//# sourceMappingURL=build-component.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-component.mjs","names":[],"sources":["../../src/models/build-component.ts"],"sourcesContent":["/** Component rows of published builds (`build_components`, ADR 0006 decision 9). */\nimport { and, asc, desc, eq } from \"drizzle-orm\";\nimport type { Table } from \"drizzle-orm\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { BuildComponent } from \"../schema/build-component.ts\";\nimport { ulid } from \"../utils/ulid.ts\";\nimport { col } from \"./columns.ts\";\n\n/** Tables required by {@link BuildComponentModel}. */\nexport interface BuildComponentTables {\n buildComponents: Table;\n}\n\n/** What a build records of one component. */\nexport type BuildComponentInput = Pick<\n BuildComponent,\n \"importPath\" | \"title\" | \"storyCount\" | \"hash\"\n>;\n\n/** Data operations for the components each published build holds. */\nexport class BuildComponentModel {\n readonly #db: DatabaseAdapter;\n readonly #table: Table;\n\n constructor(db: DatabaseAdapter, tables?: BuildComponentTables) {\n this.#db = db;\n this.#table = (tables ?? { buildComponents: db.tables.buildComponents }).buildComponents;\n }\n\n /** Components of one build, ordered by title then file. */\n async listByBuild(buildId: string): Promise<BuildComponent[]> {\n const rows = (await this.#db.list(this.#table, {\n where: eq(col(this.#table, \"buildId\"), buildId),\n orderBy: asc(col(this.#table, \"title\")),\n })) as unknown as BuildComponent[];\n return rows.toSorted(\n (a, b) => a.title.localeCompare(b.title) || a.importPath.localeCompare(b.importPath),\n );\n }\n\n /** Rows of one story file across the project's builds, newest first: \"which builds contain it\". */\n async listByImportPath(projectId: string, importPath: string): Promise<BuildComponent[]> {\n const rows = (await this.#db.list(this.#table, {\n where: and(\n eq(col(this.#table, \"projectId\"), projectId),\n eq(col(this.#table, \"importPath\"), importPath),\n ),\n orderBy: desc(col(this.#table, \"createdAt\")),\n })) as unknown as BuildComponent[];\n return rows;\n }\n\n /** Replace a build's rows with `components`; repeating a publish never duplicates. */\n async replaceForBuild(\n projectId: string,\n buildId: string,\n components: readonly BuildComponentInput[],\n ): Promise<void> {\n await this.removeForBuild(buildId);\n const createdAt = new Date().toISOString();\n for (const component of components) {\n // eslint-disable-next-line no-await-in-loop -- rows are few; sequential inserts keep ids ordered\n await this.#db.insert(this.#table, {\n id: ulid(),\n projectId,\n buildId,\n createdAt,\n ...component,\n });\n }\n }\n\n /** Forget a build's rows (the foreign key cascades too; stub tables have none). */\n async removeForBuild(buildId: string): Promise<number> {\n const rows = (await this.#db.list(this.#table, {\n where: eq(col(this.#table, \"buildId\"), buildId),\n })) as unknown as BuildComponent[];\n await Promise.all(\n rows.map(async (row) => {\n await this.#db.remove(this.#table, row.id);\n }),\n );\n return rows.length;\n }\n}\n"],"mappings":";;;;;;;;;AAoBA,IAAa,sBAAb,MAAiC;CAC/B;CACA;CAEA,YAAY,IAAqB,QAA+B;EAC9D,KAAK,MAAM;EACX,KAAK,UAAU,UAAU,EAAE,iBAAiB,GAAG,OAAO,gBAAgB,EAAA,CAAG;CAC3E;;CAGA,MAAM,YAAY,SAA4C;EAK5D,QAAO,MAJa,KAAK,IAAI,KAAK,KAAK,QAAQ;GAC7C,OAAO,GAAG,IAAI,KAAK,QAAQ,SAAS,GAAG,OAAO;GAC9C,SAAS,IAAI,IAAI,KAAK,QAAQ,OAAO,CAAC;EACxC,CAAC,EAAA,CACW,UACT,GAAG,MAAM,EAAE,MAAM,cAAc,EAAE,KAAK,KAAK,EAAE,WAAW,cAAc,EAAE,UAAU,CACrF;CACF;;CAGA,MAAM,iBAAiB,WAAmB,YAA+C;EAQvF,OAAO,MAPa,KAAK,IAAI,KAAK,KAAK,QAAQ;GAC7C,OAAO,IACL,GAAG,IAAI,KAAK,QAAQ,WAAW,GAAG,SAAS,GAC3C,GAAG,IAAI,KAAK,QAAQ,YAAY,GAAG,UAAU,CAC/C;GACA,SAAS,KAAK,IAAI,KAAK,QAAQ,WAAW,CAAC;EAC7C,CAAC;CAEH;;CAGA,MAAM,gBACJ,WACA,SACA,YACe;EACf,MAAM,KAAK,eAAe,OAAO;EACjC,MAAM,6BAAY,IAAI,KAAK,EAAA,CAAE,YAAY;EACzC,KAAK,MAAM,aAAa,YAEtB,MAAM,KAAK,IAAI,OAAO,KAAK,QAAQ;GACjC,IAAI,KAAK;GACT;GACA;GACA;GACA,GAAG;EACL,CAAC;CAEL;;CAGA,MAAM,eAAe,SAAkC;EACrD,MAAM,OAAQ,MAAM,KAAK,IAAI,KAAK,KAAK,QAAQ,EAC7C,OAAO,GAAG,IAAI,KAAK,QAAQ,SAAS,GAAG,OAAO,EAChD,CAAC;EACD,MAAM,QAAQ,IACZ,KAAK,IAAI,OAAO,QAAQ;GACtB,MAAM,KAAK,IAAI,OAAO,KAAK,QAAQ,IAAI,EAAE;EAC3C,CAAC,CACH;EACA,OAAO,KAAK;CACd;AACF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { BuildReview } from "../schema/build-review.mjs";
|
|
4
|
+
/** What {@link deriveReviewStatus} depends on. */
|
|
5
|
+
interface ReviewStatusInput {
|
|
6
|
+
/** `testing` is on: the snapshot rollup decides and verdicts never gate. */
|
|
7
|
+
testing: boolean;
|
|
8
|
+
/** The project's review switch. */
|
|
9
|
+
review: boolean;
|
|
10
|
+
/** `projects.review_approvals`: approvals a build needs (at least 1). */
|
|
11
|
+
approvalsRequired: number;
|
|
12
|
+
/** Every verdict given to the build, in any order; see {@link latestVerdicts}. */
|
|
13
|
+
reviews: readonly Pick<BuildReview, "id" | "userId" | "verdict" | "createdAt">[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The status the verdicts give the build, or `null` when they do not decide it (`testing` is on,
|
|
17
|
+
* or review is off and the build was accepted when it was published).
|
|
18
|
+
*
|
|
19
|
+
* - `rejected` when any reviewer's latest verdict is `rejected`;
|
|
20
|
+
* - `approved` once the required approvals are present among the latest verdicts;
|
|
21
|
+
* - `pending` otherwise.
|
|
22
|
+
*/
|
|
23
|
+
declare function deriveReviewStatus(input: ReviewStatusInput): "approved" | "rejected" | "pending" | null;
|
|
24
|
+
/** Each reviewer's latest verdict (newest `createdAt`, then newest id), oldest reviewer first. */
|
|
25
|
+
declare function latestVerdicts<T extends Pick<BuildReview, "id" | "userId" | "createdAt">>(reviews: readonly T[]): T[];
|
|
26
|
+
export { ReviewStatusInput, deriveReviewStatus, latestVerdicts };
|
|
27
|
+
//# sourceMappingURL=build-review-status.d.mts.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
// src/models/build-review-status.ts
|
|
5
|
+
/** Reviewer key standing for verdicts without a user (auth off or an admin token): one reviewer. */
|
|
6
|
+
const ANONYMOUS_REVIEWER = "";
|
|
7
|
+
/**
|
|
8
|
+
* The status the verdicts give the build, or `null` when they do not decide it (`testing` is on,
|
|
9
|
+
* or review is off and the build was accepted when it was published).
|
|
10
|
+
*
|
|
11
|
+
* - `rejected` when any reviewer's latest verdict is `rejected`;
|
|
12
|
+
* - `approved` once the required approvals are present among the latest verdicts;
|
|
13
|
+
* - `pending` otherwise.
|
|
14
|
+
*/
|
|
15
|
+
function deriveReviewStatus(input) {
|
|
16
|
+
if (input.testing || !input.review) return null;
|
|
17
|
+
const latest = latestVerdicts(input.reviews);
|
|
18
|
+
if (latest.some((review) => review.verdict === "rejected")) return "rejected";
|
|
19
|
+
return latest.filter((review) => review.verdict === "approved").length >= Math.max(1, input.approvalsRequired) ? "approved" : "pending";
|
|
20
|
+
}
|
|
21
|
+
/** Each reviewer's latest verdict (newest `createdAt`, then newest id), oldest reviewer first. */
|
|
22
|
+
function latestVerdicts(reviews) {
|
|
23
|
+
const latest = /* @__PURE__ */ new Map();
|
|
24
|
+
for (const review of reviews) {
|
|
25
|
+
const key = review.userId ?? ANONYMOUS_REVIEWER;
|
|
26
|
+
const current = latest.get(key);
|
|
27
|
+
if (current === void 0 || isNewer(review, current)) latest.set(key, review);
|
|
28
|
+
}
|
|
29
|
+
return [...latest.values()].toSorted((a, b) => isNewer(b, a) ? -1 : 1);
|
|
30
|
+
}
|
|
31
|
+
function isNewer(a, b) {
|
|
32
|
+
return a.createdAt === b.createdAt ? a.id > b.id : a.createdAt > b.createdAt;
|
|
33
|
+
}
|
|
34
|
+
export { deriveReviewStatus, latestVerdicts };
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=build-review-status.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-review-status.mjs","names":[],"sources":["../../src/models/build-review-status.ts"],"sourcesContent":["/**\n * What the build-level verdicts make of a build (ADR 0013, decision 3). Pure: callers read the\n * reviews and the project, apply the result, and stay free of branching.\n */\nimport type { BuildReview } from \"../schema/build-review.ts\";\n\n/** What {@link deriveReviewStatus} depends on. */\nexport interface ReviewStatusInput {\n /** `testing` is on: the snapshot rollup decides and verdicts never gate. */\n testing: boolean;\n /** The project's review switch. */\n review: boolean;\n /** `projects.review_approvals`: approvals a build needs (at least 1). */\n approvalsRequired: number;\n /** Every verdict given to the build, in any order; see {@link latestVerdicts}. */\n reviews: readonly Pick<BuildReview, \"id\" | \"userId\" | \"verdict\" | \"createdAt\">[];\n}\n\n/** Reviewer key standing for verdicts without a user (auth off or an admin token): one reviewer. */\nconst ANONYMOUS_REVIEWER = \"\";\n\n/**\n * The status the verdicts give the build, or `null` when they do not decide it (`testing` is on,\n * or review is off and the build was accepted when it was published).\n *\n * - `rejected` when any reviewer's latest verdict is `rejected`;\n * - `approved` once the required approvals are present among the latest verdicts;\n * - `pending` otherwise.\n */\nexport function deriveReviewStatus(\n input: ReviewStatusInput,\n): \"approved\" | \"rejected\" | \"pending\" | null {\n if (input.testing || !input.review) {\n return null;\n }\n const latest = latestVerdicts(input.reviews);\n if (latest.some((review) => review.verdict === \"rejected\")) {\n return \"rejected\";\n }\n const approvals = latest.filter((review) => review.verdict === \"approved\").length;\n return approvals >= Math.max(1, input.approvalsRequired) ? \"approved\" : \"pending\";\n}\n\n/** Each reviewer's latest verdict (newest `createdAt`, then newest id), oldest reviewer first. */\nexport function latestVerdicts<T extends Pick<BuildReview, \"id\" | \"userId\" | \"createdAt\">>(\n reviews: readonly T[],\n): T[] {\n const latest = new Map<string, T>();\n for (const review of reviews) {\n const key = review.userId ?? ANONYMOUS_REVIEWER;\n const current = latest.get(key);\n if (current === undefined || isNewer(review, current)) {\n latest.set(key, review);\n }\n }\n return [...latest.values()].toSorted((a, b) => (isNewer(b, a) ? -1 : 1));\n}\n\nfunction isNewer(\n a: Pick<BuildReview, \"id\" | \"createdAt\">,\n b: Pick<BuildReview, \"id\" | \"createdAt\">,\n): boolean {\n return a.createdAt === b.createdAt ? a.id > b.id : a.createdAt > b.createdAt;\n}\n"],"mappings":";;;;;AAmBA,MAAM,qBAAqB;;;;;;;;;AAU3B,SAAgB,mBACd,OAC4C;CAC5C,IAAI,MAAM,WAAW,CAAC,MAAM,QAC1B,OAAO;CAET,MAAM,SAAS,eAAe,MAAM,OAAO;CAC3C,IAAI,OAAO,MAAM,WAAW,OAAO,YAAY,UAAU,GACvD,OAAO;CAGT,OADkB,OAAO,QAAQ,WAAW,OAAO,YAAY,UAAU,CAAC,CAAC,UACvD,KAAK,IAAI,GAAG,MAAM,iBAAiB,IAAI,aAAa;AAC1E;;AAGA,SAAgB,eACd,SACK;CACL,MAAM,yBAAS,IAAI,IAAe;CAClC,KAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,MAAM,OAAO,UAAU;EAC7B,MAAM,UAAU,OAAO,IAAI,GAAG;EAC9B,IAAI,YAAY,KAAA,KAAa,QAAQ,QAAQ,OAAO,GAClD,OAAO,IAAI,KAAK,MAAM;CAE1B;CACA,OAAO,CAAC,GAAG,OAAO,OAAO,CAAC,CAAC,CAAC,UAAU,GAAG,MAAO,QAAQ,GAAG,CAAC,IAAI,KAAK,CAAE;AACzE;AAEA,SAAS,QACP,GACA,GACS;CACT,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE;AACrE"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { DatabaseAdapter } from "../adapter/database.mjs";
|
|
4
|
+
import { ReviewVerdict } from "../types.mjs";
|
|
5
|
+
import { BuildReview } from "../schema/build-review.mjs";
|
|
6
|
+
import { Table } from "drizzle-orm";
|
|
7
|
+
/** Tables required by {@link BuildReviewModel}. */
|
|
8
|
+
interface BuildReviewTables {
|
|
9
|
+
buildReviews: Table;
|
|
10
|
+
}
|
|
11
|
+
/** Data operations for the verdicts reviewers give a build. */
|
|
12
|
+
declare class BuildReviewModel {
|
|
13
|
+
#private;
|
|
14
|
+
constructor(db: DatabaseAdapter, tables?: BuildReviewTables);
|
|
15
|
+
/**
|
|
16
|
+
* Record a verdict. A reviewer rewrites theirs by adding another row; the newest row counts.
|
|
17
|
+
* `createdAt` is strictly increasing within a build, so "newest" never ties.
|
|
18
|
+
*/
|
|
19
|
+
add(buildId: string, userId: string | null, verdict: ReviewVerdict, body?: string): Promise<BuildReview>;
|
|
20
|
+
/** Every verdict of a build, oldest first. */
|
|
21
|
+
listByBuild(buildId: string): Promise<BuildReview[]>;
|
|
22
|
+
/** Each reviewer's latest verdict on a build. */
|
|
23
|
+
latestByBuild(buildId: string): Promise<BuildReview[]>;
|
|
24
|
+
}
|
|
25
|
+
export { BuildReviewModel, BuildReviewTables };
|
|
26
|
+
//# sourceMappingURL=build-review.d.mts.map
|