@storyshelf/core 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/capture-queue.d.mts +0 -2
- package/dist/adapter/capture-runner.d.mts +9 -4
- package/dist/adapter/capture-runner.mjs +6 -2
- package/dist/adapter/database.d.mts +63 -3
- package/dist/adapter/database.mjs +4 -3
- package/dist/adapter/database.mjs.map +1 -1
- package/dist/adapter/design.d.mts +0 -2
- package/dist/adapter/design.mjs +1 -2
- package/dist/adapter/email-sender.d.mts +0 -2
- package/dist/adapter/metadata.d.mts +0 -2
- package/dist/adapter/oauth.d.mts +0 -2
- package/dist/adapter/setup.d.mts +0 -2
- package/dist/adapter/setup.mjs +1 -2
- package/dist/adapter/storage.d.mts +1 -3
- package/dist/adapter/webhook-events.d.mts +0 -2
- package/dist/adapter/webhook-events.mjs +12 -13
- package/dist/adapter/webhook-events.mjs.map +1 -1
- package/dist/adapters/capture-target-offers.d.mts +0 -2
- package/dist/adapters/capture-target-offers.mjs +1 -2
- package/dist/adapters/capture-target.d.mts +0 -2
- package/dist/adapters/capture-target.mjs +1 -2
- package/dist/adapters/notifier/build-comment.d.mts +0 -2
- package/dist/adapters/notifier/build-comment.mjs +1 -2
- package/dist/adapters/notifier/build-state.d.mts +0 -2
- package/dist/adapters/notifier/build-state.mjs +1 -2
- package/dist/adapters/notifier/events.d.mts +0 -2
- package/dist/adapters/notifier/events.mjs +1 -2
- package/dist/adapters/notifier/format.d.mts +0 -2
- package/dist/adapters/notifier/format.mjs +1 -2
- package/dist/adapters/notifier/provider.d.mts +0 -2
- package/dist/adapters/notifier/provider.mjs +1 -2
- package/dist/adapters/notifier/state.d.mts +0 -2
- package/dist/adapters/notifier/state.mjs +1 -2
- package/dist/adapters/notifier/types.d.mts +0 -2
- package/dist/adapters/remote-browser.d.mts +112 -31
- package/dist/adapters/remote-browser.mjs +132 -34
- package/dist/adapters/remote-browser.mjs.map +1 -1
- package/dist/adapters/remote-lease-attempt.mjs +39 -0
- package/dist/adapters/remote-lease-attempt.mjs.map +1 -0
- package/dist/adapters/remote-lease-error.d.mts +25 -0
- package/dist/adapters/remote-lease-error.mjs +54 -0
- package/dist/adapters/remote-lease-error.mjs.map +1 -0
- package/dist/adapters/remote-lease-held.d.mts +15 -0
- package/dist/adapters/remote-lease-held.mjs +86 -0
- package/dist/adapters/remote-lease-held.mjs.map +1 -0
- package/dist/adapters/remote-lease-shutdown.d.mts +12 -0
- package/dist/adapters/remote-lease-shutdown.mjs +32 -0
- package/dist/adapters/remote-lease-shutdown.mjs.map +1 -0
- package/dist/adapters/remote-lease.d.mts +39 -0
- package/dist/adapters/remote-lease.mjs +175 -0
- package/dist/adapters/remote-lease.mjs.map +1 -0
- package/dist/adapters/remote-limiter.mjs +121 -0
- package/dist/adapters/remote-limiter.mjs.map +1 -0
- package/dist/adapters/remote-pool.d.mts +56 -0
- package/dist/adapters/remote-pool.mjs +195 -0
- package/dist/adapters/remote-pool.mjs.map +1 -0
- package/dist/adapters/render-runs.d.mts +0 -2
- package/dist/adapters/render-runs.mjs +1 -2
- package/dist/adapters/setup-runner.d.mts +0 -2
- package/dist/adapters/setup-runner.mjs +1 -2
- package/dist/adapters/sources.d.mts +0 -2
- package/dist/adapters/sources.mjs +1 -2
- package/dist/adapters/validate.d.mts +0 -2
- package/dist/adapters/validate.mjs +2 -2
- package/dist/adapters/validate.mjs.map +1 -1
- package/dist/ai.d.mts +0 -2
- package/dist/ai.mjs +1 -2
- package/dist/auth.d.mts +0 -2
- package/dist/auth.mjs +1 -2
- package/dist/brand-packs.d.mts +0 -2
- package/dist/brand-packs.mjs +0 -1
- package/dist/capture/a11y/axe-source.d.mts +0 -2
- package/dist/capture/a11y/axe-source.mjs +1 -2
- package/dist/capture/a11y/in-page.d.mts +1 -3
- package/dist/capture/a11y/in-page.mjs +3 -4
- package/dist/capture/a11y/in-page.mjs.map +1 -1
- package/dist/capture/a11y/map.d.mts +0 -2
- package/dist/capture/a11y/map.mjs +1 -2
- package/dist/capture/a11y/options.d.mts +0 -2
- package/dist/capture/a11y/options.mjs +1 -2
- package/dist/capture/adapter.d.mts +5 -2
- package/dist/capture/adapter.mjs +1 -2
- package/dist/capture/adapter.mjs.map +1 -1
- package/dist/capture/affected.d.mts +0 -2
- package/dist/capture/affected.mjs +1 -2
- package/dist/capture/attempt-logger.mjs +1 -2
- package/dist/capture/browsers.d.mts +0 -2
- package/dist/capture/browsers.mjs +1 -2
- package/dist/capture/build-state/deliver.d.mts +0 -2
- package/dist/capture/build-state/deliver.mjs +1 -2
- package/dist/capture/build-state/notify-job.d.mts +0 -2
- package/dist/capture/build-state/notify-job.mjs +1 -2
- package/dist/capture/build-state/publisher.d.mts +0 -2
- package/dist/capture/build-state/publisher.mjs +1 -2
- package/dist/capture/build-state/resume.d.mts +0 -2
- package/dist/capture/build-state/resume.mjs +1 -2
- package/dist/capture/build-state/types.d.mts +1 -3
- package/dist/capture/catalog/catalogue.d.mts +0 -2
- package/dist/capture/catalog/catalogue.mjs +1 -2
- package/dist/capture/catalog/components.d.mts +0 -2
- package/dist/capture/catalog/components.mjs +1 -2
- package/dist/capture/catalog/history.d.mts +0 -2
- package/dist/capture/catalog/history.mjs +1 -2
- package/dist/capture/catalog/hook.d.mts +0 -2
- package/dist/capture/catalog/hook.mjs +1 -2
- package/dist/capture/catalog/index.d.mts +2 -2
- package/dist/capture/catalog/load.d.mts +0 -2
- package/dist/capture/catalog/load.mjs +1 -2
- package/dist/capture/catalog/parse.d.mts +0 -2
- package/dist/capture/catalog/parse.mjs +1 -2
- package/dist/capture/catalog/read.d.mts +6 -3
- package/dist/capture/catalog/read.mjs +14 -8
- package/dist/capture/catalog/read.mjs.map +1 -1
- package/dist/capture/catalog/types.d.mts +0 -2
- package/dist/capture/comparison-reference.d.mts +27 -0
- package/dist/capture/comparison-reference.mjs +75 -0
- package/dist/capture/comparison-reference.mjs.map +1 -0
- package/dist/capture/consumer/capture-handler.d.mts +1 -3
- package/dist/capture/consumer/capture-handler.mjs +1 -2
- package/dist/capture/consumer/idle.d.mts +0 -2
- package/dist/capture/consumer/idle.mjs +1 -2
- package/dist/capture/consumer/index.d.mts +0 -2
- package/dist/capture/consumer/index.mjs +1 -2
- package/dist/capture/consumer/loop.mjs +1 -2
- package/dist/capture/consumer/metrics.d.mts +0 -2
- package/dist/capture/consumer/metrics.mjs +1 -2
- package/dist/capture/consumer/retry.d.mts +0 -2
- package/dist/capture/consumer/retry.mjs +1 -2
- package/dist/capture/consumer/run-job.mjs +1 -2
- package/dist/capture/consumer/startup.mjs +1 -2
- package/dist/capture/consumer/types.d.mts +0 -2
- package/dist/capture/crash-failure.d.mts +0 -2
- package/dist/capture/crash-failure.mjs +1 -2
- package/dist/capture/db-queue/cadence.d.mts +0 -2
- package/dist/capture/db-queue/cadence.mjs +1 -2
- package/dist/capture/db-queue/claim.d.mts +0 -2
- package/dist/capture/db-queue/claim.mjs +20 -4
- package/dist/capture/db-queue/claim.mjs.map +1 -1
- package/dist/capture/db-queue/clock.mjs +1 -2
- package/dist/capture/db-queue/codec.mjs +1 -2
- package/dist/capture/db-queue/index.d.mts +0 -2
- package/dist/capture/db-queue/index.mjs +3 -3
- package/dist/capture/db-queue/index.mjs.map +1 -1
- package/dist/capture/db-queue/keeper.d.mts +0 -2
- package/dist/capture/db-queue/keeper.mjs +1 -2
- package/dist/capture/db-queue/lease.mjs +1 -2
- package/dist/capture/db-queue/lifecycle.mjs +1 -2
- package/dist/capture/db-queue/operations.mjs +1 -2
- package/dist/capture/db-queue/poll-loop.d.mts +0 -2
- package/dist/capture/db-queue/poll-loop.mjs +1 -2
- package/dist/capture/db-queue/poll.d.mts +0 -2
- package/dist/capture/db-queue/poll.mjs +1 -2
- package/dist/capture/db-queue/timers.d.mts +0 -2
- package/dist/capture/db-queue/timers.mjs +1 -2
- package/dist/capture/db-queue/types.d.mts +0 -2
- package/dist/capture/db-queue/types.mjs +1 -2
- package/dist/capture/db-queue/types.mjs.map +1 -1
- package/dist/capture/db-queue/wake.mjs +1 -2
- package/dist/capture/design.mjs +1 -2
- package/dist/capture/diff-record.mjs +1 -2
- package/dist/capture/dispatch.d.mts +1 -3
- package/dist/capture/dispatch.mjs +17 -5
- package/dist/capture/dispatch.mjs.map +1 -1
- package/dist/capture/enrich.mjs +1 -2
- package/dist/capture/failure-sets.mjs +1 -2
- package/dist/capture/fingerprint.d.mts +0 -2
- package/dist/capture/fingerprint.mjs +1 -2
- package/dist/capture/forked/channel.d.mts +0 -2
- package/dist/capture/forked/child.d.mts +0 -2
- package/dist/capture/forked/child.mjs +1 -2
- package/dist/capture/forked/crash-tracker.mjs +1 -2
- package/dist/capture/forked/decision.d.mts +0 -2
- package/dist/capture/forked/decision.mjs +1 -2
- package/dist/capture/forked/exit.mjs +1 -2
- package/dist/capture/forked/fork.d.mts +0 -2
- package/dist/capture/forked/fork.mjs +1 -2
- package/dist/capture/forked/forked-capture.d.mts +0 -2
- package/dist/capture/forked/forked-capture.mjs +1 -2
- package/dist/capture/forked/lease-book.mjs +1 -2
- package/dist/capture/forked/protocol.d.mts +0 -2
- package/dist/capture/forked/protocol.mjs +1 -2
- package/dist/capture/forked/runs.mjs +1 -2
- package/dist/capture/guards.mjs +1 -2
- package/dist/capture/ignore-regions.d.mts +36 -0
- package/dist/capture/ignore-regions.mjs +65 -0
- package/dist/capture/ignore-regions.mjs.map +1 -0
- package/dist/capture/isolate.mjs +1 -2
- package/dist/capture/job-lease.d.mts +0 -2
- package/dist/capture/job-lease.mjs +1 -2
- package/dist/capture/matrix.mjs +4 -4
- package/dist/capture/matrix.mjs.map +1 -1
- package/dist/capture/modes.d.mts +0 -2
- package/dist/capture/modes.mjs +2 -3
- package/dist/capture/modes.mjs.map +1 -1
- package/dist/capture/orchestrator.d.mts +0 -2
- package/dist/capture/orchestrator.mjs +2 -3
- package/dist/capture/pipeline.d.mts +0 -2
- package/dist/capture/pipeline.mjs +37 -6
- package/dist/capture/pipeline.mjs.map +1 -1
- package/dist/capture/progress.d.mts +0 -2
- package/dist/capture/progress.mjs +1 -2
- package/dist/capture/publish/accept.d.mts +0 -2
- package/dist/capture/publish/accept.mjs +1 -2
- package/dist/capture/publish/complete.d.mts +1 -3
- package/dist/capture/publish/complete.mjs +1 -2
- package/dist/capture/publish/extract.d.mts +0 -2
- package/dist/capture/publish/extract.mjs +6 -5
- package/dist/capture/publish/extract.mjs.map +1 -1
- package/dist/capture/publish/handler.d.mts +1 -3
- package/dist/capture/publish/handler.mjs +1 -2
- package/dist/capture/publish/index.d.mts +2 -1
- package/dist/capture/publish/invalid-zip.d.mts +8 -0
- package/dist/capture/publish/invalid-zip.mjs +11 -0
- package/dist/capture/publish/invalid-zip.mjs.map +1 -0
- package/dist/capture/publish/limits.d.mts +0 -2
- package/dist/capture/publish/limits.mjs +1 -2
- package/dist/capture/publish/published.d.mts +0 -2
- package/dist/capture/publish/published.mjs +1 -2
- package/dist/capture/publish/run.d.mts +1 -3
- package/dist/capture/publish/run.mjs +1 -2
- package/dist/capture/rediff.d.mts +1 -3
- package/dist/capture/rediff.mjs +19 -6
- package/dist/capture/rediff.mjs.map +1 -1
- package/dist/capture/render.mjs +1 -2
- package/dist/capture/render.mjs.map +1 -1
- package/dist/capture/scratch.d.mts +0 -2
- package/dist/capture/scratch.mjs +2 -4
- package/dist/capture/scratch.mjs.map +1 -1
- package/dist/capture/shard-plan.mjs +1 -2
- package/dist/capture/shards/context.mjs +3 -3
- package/dist/capture/shards/context.mjs.map +1 -1
- package/dist/capture/shards/execute.mjs +4 -3
- package/dist/capture/shards/execute.mjs.map +1 -1
- package/dist/capture/shards/fan-out.mjs +1 -2
- package/dist/capture/shards/history.mjs +1 -2
- package/dist/capture/shards/reduce.mjs +1 -2
- package/dist/capture/sizing.d.mts +0 -2
- package/dist/capture/sizing.mjs +1 -2
- package/dist/capture/skip-checks.mjs +1 -2
- package/dist/capture/statics.d.mts +0 -2
- package/dist/capture/statics.mjs +2 -4
- package/dist/capture/statics.mjs.map +1 -1
- package/dist/capture/storybook-urls.d.mts +0 -2
- package/dist/capture/storybook-urls.mjs +3 -3
- package/dist/capture/storybook-urls.mjs.map +1 -1
- package/dist/capture/storybook.d.mts +0 -2
- package/dist/capture/storybook.mjs +8 -4
- package/dist/capture/storybook.mjs.map +1 -1
- package/dist/capture/target.mjs +1 -2
- package/dist/capture/targets.d.mts +0 -2
- package/dist/capture/targets.mjs +1 -2
- package/dist/capture/tests.d.mts +0 -2
- package/dist/capture/tests.mjs +13 -3
- package/dist/capture/tests.mjs.map +1 -1
- package/dist/capture/token-chunks.mjs +24 -7
- package/dist/capture/token-chunks.mjs.map +1 -1
- package/dist/capture.d.mts +5 -2
- package/dist/capture.mjs +5 -2
- package/dist/config.d.mts +16 -3
- package/dist/config.mjs +6 -3
- package/dist/config.mjs.map +1 -1
- package/dist/diff.d.mts +0 -2
- package/dist/diff.mjs +1 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/insights/budget.d.mts +0 -2
- package/dist/insights/budget.mjs +1 -2
- package/dist/insights/envelope.d.mts +0 -2
- package/dist/insights/envelope.mjs +1 -2
- package/dist/insights/evidence-health.d.mts +0 -2
- package/dist/insights/evidence-health.mjs +1 -2
- package/dist/insights/evidence.d.mts +0 -2
- package/dist/insights/evidence.mjs +1 -2
- package/dist/insights/hash.d.mts +0 -2
- package/dist/insights/hash.mjs +1 -2
- package/dist/insights/limits.d.mts +0 -2
- package/dist/insights/limits.mjs +1 -2
- package/dist/insights/prompts/v1.d.mts +0 -2
- package/dist/insights/prompts/v1.mjs +1 -2
- package/dist/insights/redact.d.mts +0 -2
- package/dist/insights/redact.mjs +1 -2
- package/dist/insights/text.mjs +1 -2
- package/dist/logger.d.mts +0 -2
- package/dist/logger.mjs +1 -2
- package/dist/models/ai-budget-alert.d.mts +0 -2
- package/dist/models/ai-budget-alert.mjs +1 -2
- package/dist/models/ai-usage.d.mts +0 -2
- package/dist/models/ai-usage.mjs +1 -2
- package/dist/models/baseline-approval.d.mts +18 -0
- package/dist/models/baseline-approval.mjs +41 -0
- package/dist/models/baseline-approval.mjs.map +1 -0
- package/dist/models/baseline-archive.d.mts +0 -2
- package/dist/models/baseline-archive.mjs +1 -2
- package/dist/models/baseline-atomic.mjs +66 -0
- package/dist/models/baseline-atomic.mjs.map +1 -0
- package/dist/models/baseline-conflict.d.mts +6 -0
- package/dist/models/baseline-conflict.mjs +9 -0
- package/dist/models/baseline-conflict.mjs.map +1 -0
- package/dist/models/baseline-export.d.mts +0 -2
- package/dist/models/baseline-export.mjs +1 -2
- package/dist/models/baseline-import.mjs +1 -2
- package/dist/models/baseline-origin.d.mts +16 -0
- package/dist/models/baseline-origin.mjs +35 -0
- package/dist/models/baseline-origin.mjs.map +1 -0
- package/dist/models/baseline-status.d.mts +0 -2
- package/dist/models/baseline-status.mjs +1 -2
- package/dist/models/baseline-transfer.d.mts +1 -2
- package/dist/models/baseline-transfer.mjs +30 -2
- package/dist/models/baseline-transfer.mjs.map +1 -1
- package/dist/models/baseline.d.mts +5 -2
- package/dist/models/baseline.mjs +91 -12
- package/dist/models/baseline.mjs.map +1 -1
- package/dist/models/build-component.d.mts +0 -2
- package/dist/models/build-component.mjs +1 -2
- package/dist/models/build-review-status.d.mts +0 -2
- package/dist/models/build-review-status.mjs +1 -2
- package/dist/models/build-review.d.mts +0 -2
- package/dist/models/build-review.mjs +1 -2
- package/dist/models/build.d.mts +1 -3
- package/dist/models/build.mjs +14 -15
- package/dist/models/build.mjs.map +1 -1
- package/dist/models/capture-attempt.d.mts +0 -2
- package/dist/models/capture-attempt.mjs +1 -2
- package/dist/models/capture-job.d.mts +0 -2
- package/dist/models/capture-job.mjs +1 -2
- package/dist/models/capture-log.d.mts +0 -2
- package/dist/models/capture-log.mjs +1 -2
- package/dist/models/capture-shard.d.mts +0 -2
- package/dist/models/capture-shard.mjs +1 -2
- package/dist/models/changeset.d.mts +0 -2
- package/dist/models/changeset.mjs +1 -2
- package/dist/models/columns.mjs +1 -2
- package/dist/models/comment.d.mts +0 -2
- package/dist/models/comment.mjs +1 -2
- package/dist/models/component.d.mts +0 -2
- package/dist/models/component.mjs +1 -2
- package/dist/models/connections.d.mts +0 -2
- package/dist/models/connections.mjs +1 -2
- package/dist/models/content-ref.d.mts +0 -2
- package/dist/models/content-ref.mjs +1 -2
- package/dist/models/credentials.d.mts +0 -2
- package/dist/models/credentials.mjs +1 -2
- package/dist/models/design-credentials.d.mts +0 -2
- package/dist/models/design-credentials.mjs +1 -2
- package/dist/models/design-health.d.mts +0 -2
- package/dist/models/design-health.mjs +1 -2
- package/dist/models/design-nodes.d.mts +2 -4
- package/dist/models/design-nodes.mjs +1 -2
- package/dist/models/design-renders.mjs +1 -2
- package/dist/models/design-resolve.d.mts +0 -2
- package/dist/models/design-resolve.mjs +1 -2
- package/dist/models/design.d.mts +0 -2
- package/dist/models/design.mjs +1 -2
- package/dist/models/inherited-ratio.d.mts +0 -2
- package/dist/models/inherited-ratio.mjs +1 -2
- package/dist/models/insight.d.mts +0 -2
- package/dist/models/insight.mjs +1 -2
- package/dist/models/label.d.mts +0 -2
- package/dist/models/label.mjs +1 -2
- package/dist/models/member.d.mts +0 -2
- package/dist/models/member.mjs +1 -2
- package/dist/models/notification-channel.d.mts +0 -2
- package/dist/models/notification-channel.mjs +1 -2
- package/dist/models/notification-subscription.d.mts +0 -2
- package/dist/models/notification-subscription.mjs +1 -2
- package/dist/models/project-group-mapping.d.mts +0 -2
- package/dist/models/project-group-mapping.mjs +1 -2
- package/dist/models/project.d.mts +0 -2
- package/dist/models/project.mjs +1 -2
- package/dist/models/public-origin.d.mts +0 -2
- package/dist/models/public-origin.mjs +1 -2
- package/dist/models/review-switch-off.d.mts +0 -2
- package/dist/models/review-switch-off.mjs +1 -2
- package/dist/models/site-settings.d.mts +0 -2
- package/dist/models/site-settings.mjs +1 -2
- package/dist/models/snapshot.d.mts +3 -2
- package/dist/models/snapshot.mjs +4 -2
- package/dist/models/snapshot.mjs.map +1 -1
- package/dist/models/token.d.mts +0 -2
- package/dist/models/token.mjs +1 -2
- package/dist/models/usage.d.mts +0 -2
- package/dist/models/usage.mjs +1 -2
- package/dist/models/user.d.mts +0 -2
- package/dist/models/user.mjs +1 -2
- package/dist/models/webhook.d.mts +0 -2
- package/dist/models/webhook.mjs +1 -2
- package/dist/models.d.mts +5 -2
- package/dist/models.mjs +5 -2
- package/dist/options.d.mts +0 -2
- package/dist/options.mjs +2 -4
- package/dist/options.mjs.map +1 -1
- package/dist/paths.d.mts +3 -3
- package/dist/paths.mjs +7 -3
- package/dist/paths.mjs.map +1 -1
- package/dist/retention/ai.d.mts +1 -3
- package/dist/retention/ai.mjs +0 -1
- package/dist/retention/pending.mjs +1 -2
- package/dist/retention/published-gzip-sweep.mjs +28 -0
- package/dist/retention/published-gzip-sweep.mjs.map +1 -0
- package/dist/retention/screenshot-sweep.d.mts +0 -2
- package/dist/retention/screenshot-sweep.mjs +1 -2
- package/dist/retention.d.mts +0 -2
- package/dist/retention.mjs +5 -2
- package/dist/retention.mjs.map +1 -1
- package/dist/schema/baseline.d.mts +2 -2
- package/dist/schema/build-component.d.mts +0 -2
- package/dist/schema/build-review.d.mts +0 -2
- package/dist/schema/build.d.mts +0 -2
- package/dist/schema/capture-attempt.d.mts +0 -2
- package/dist/schema/capture-job.d.mts +0 -2
- package/dist/schema/capture-shard.d.mts +0 -2
- package/dist/schema/comment.d.mts +0 -2
- package/dist/schema/connection.d.mts +0 -2
- package/dist/schema/content-ref.d.mts +0 -2
- package/dist/schema/design.d.mts +0 -2
- package/dist/schema/insight.d.mts +0 -2
- package/dist/schema/label.d.mts +0 -2
- package/dist/schema/member.d.mts +0 -2
- package/dist/schema/notification.d.mts +0 -2
- package/dist/schema/project-group-mapping.d.mts +0 -2
- package/dist/schema/project.d.mts +0 -2
- package/dist/schema/site-setting.d.mts +0 -2
- package/dist/schema/snapshot.d.mts +5 -2
- package/dist/schema/token.d.mts +0 -2
- package/dist/schema/usage.d.mts +0 -2
- package/dist/schema/user.d.mts +0 -2
- package/dist/schema/webhook.d.mts +0 -2
- package/dist/schema.d.mts +4 -6
- package/dist/serverless.d.mts +0 -2
- package/dist/serverless.mjs +1 -2
- package/dist/test-helpers/build-reference-contract.d.mts +14 -0
- package/dist/test-helpers/build-reference-contract.mjs +66 -0
- package/dist/test-helpers/build-reference-contract.mjs.map +1 -0
- package/dist/test-helpers/create-project.d.mts +0 -2
- package/dist/test-helpers/create-project.mjs +1 -2
- package/dist/test-helpers/database-contract.d.mts +0 -2
- package/dist/test-helpers/database-contract.mjs +37 -2
- package/dist/test-helpers/database-contract.mjs.map +1 -1
- package/dist/test-helpers/deferred.d.mts +0 -2
- package/dist/test-helpers/deferred.mjs +1 -2
- package/dist/test-helpers/design-contract.d.mts +0 -2
- package/dist/test-helpers/design-contract.mjs +1 -2
- package/dist/test-helpers/fake-adapters.d.mts +0 -2
- package/dist/test-helpers/fake-adapters.mjs +1 -2
- package/dist/test-helpers/fake-database.d.mts +0 -2
- package/dist/test-helpers/fake-database.mjs +7 -2
- package/dist/test-helpers/fake-database.mjs.map +1 -1
- package/dist/test-helpers/fake-design.d.mts +0 -2
- package/dist/test-helpers/fake-design.mjs +1 -2
- package/dist/test-helpers/fake-notifier.d.mts +0 -2
- package/dist/test-helpers/fake-notifier.mjs +1 -2
- package/dist/test-helpers/fake-storage.d.mts +0 -2
- package/dist/test-helpers/fake-storage.mjs +1 -2
- package/dist/test-helpers/fake-tables.mjs +5 -2
- package/dist/test-helpers/fake-tables.mjs.map +1 -1
- package/dist/test-helpers/manual-clock.d.mts +0 -2
- package/dist/test-helpers/manual-clock.mjs +1 -2
- package/dist/test-helpers/notifier-contract.d.mts +0 -2
- package/dist/test-helpers/notifier-contract.mjs +1 -2
- package/dist/test-helpers/queue-contract.d.mts +0 -2
- package/dist/test-helpers/queue-contract.mjs +1 -2
- package/dist/test-helpers/queue-multi-instance.d.mts +0 -2
- package/dist/test-helpers/queue-multi-instance.mjs +1 -2
- package/dist/test-helpers/runner-contract.d.mts +0 -2
- package/dist/test-helpers/runner-contract.mjs +1 -2
- package/dist/test-helpers/sql-chunks.mjs +2 -2
- package/dist/test-helpers/sql-chunks.mjs.map +1 -1
- package/dist/test-helpers/storage-contract.d.mts +0 -2
- package/dist/test-helpers/storage-contract.mjs +1 -2
- package/dist/test-helpers.d.mts +2 -1
- package/dist/test-helpers.mjs +2 -1
- package/dist/tracing.mjs +36 -6
- package/dist/tracing.mjs.map +1 -1
- package/dist/types.d.mts +0 -2
- package/dist/types.mjs +1 -2
- package/dist/urls.d.mts +0 -2
- package/dist/urls.mjs +1 -2
- package/dist/utils/adaptive-concurrency.d.mts +0 -2
- package/dist/utils/adaptive-concurrency.mjs +1 -2
- package/dist/utils/connect-retry.d.mts +2 -2
- package/dist/utils/connect-retry.mjs +2 -3
- package/dist/utils/connect-retry.mjs.map +1 -1
- package/dist/utils/encrypt.d.mts +0 -2
- package/dist/utils/encrypt.mjs +1 -2
- package/dist/utils/environment.d.mts +0 -2
- package/dist/utils/environment.mjs +1 -2
- package/dist/utils/hash.d.mts +0 -2
- package/dist/utils/hash.mjs +1 -2
- package/dist/utils/http.d.mts +13 -3
- package/dist/utils/http.mjs +79 -17
- package/dist/utils/http.mjs.map +1 -1
- package/dist/utils/map-adaptive.d.mts +0 -2
- package/dist/utils/map-adaptive.mjs +1 -2
- package/dist/utils/map-limit.d.mts +0 -2
- package/dist/utils/map-limit.mjs +1 -2
- package/dist/utils/mime.d.mts +0 -2
- package/dist/utils/mime.mjs +1 -2
- package/dist/utils/page-limiter.d.mts +0 -2
- package/dist/utils/page-limiter.mjs +1 -2
- package/dist/utils/path-security.d.mts +0 -2
- package/dist/utils/path-security.mjs +2 -4
- package/dist/utils/path-security.mjs.map +1 -1
- package/dist/utils/public-origin.d.mts +0 -2
- package/dist/utils/public-origin.mjs +1 -2
- package/dist/utils/redact.d.mts +0 -2
- package/dist/utils/redact.mjs +1 -2
- package/dist/utils/remote-environment.d.mts +40 -3
- package/dist/utils/remote-environment.mjs +52 -3
- package/dist/utils/remote-environment.mjs.map +1 -1
- package/dist/utils/resources.d.mts +0 -2
- package/dist/utils/resources.mjs +1 -2
- package/dist/utils/screenshot-store.d.mts +0 -2
- package/dist/utils/screenshot-store.mjs +1 -2
- package/dist/utils/session-gate.d.mts +7 -6
- package/dist/utils/session-gate.mjs +28 -12
- package/dist/utils/session-gate.mjs.map +1 -1
- package/dist/utils/storybook-token.d.mts +11 -3
- package/dist/utils/storybook-token.mjs +10 -9
- package/dist/utils/storybook-token.mjs.map +1 -1
- package/dist/utils/storybook-url.d.mts +0 -2
- package/dist/utils/storybook-url.mjs +1 -2
- package/dist/utils/timing.d.mts +0 -2
- package/dist/utils/timing.mjs +1 -2
- package/dist/utils/ulid.d.mts +0 -2
- package/dist/utils/ulid.mjs +1 -2
- package/dist/utils/zip-read.mjs +1 -2
- package/dist/utils/zip-write.d.mts +0 -2
- package/dist/utils/zip-write.mjs +1 -2
- package/dist/utils.d.mts +5 -5
- package/dist/utils.mjs +5 -5
- package/package.json +6 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"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 { 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 { col } from \"./columns.ts\";\nimport type { WebhookTables } from \"./webhook.ts\";\n\n/** Extra identity/metadata for {@link BaselineModel.upsert}. */\nexport interface BaselineUpsertOptions {\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 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 write = async (): Promise<Baseline> =>\n await this.#upsertAt(\n projectId,\n storyId,\n viewport,\n branch,\n snapshotId,\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 async #upsertAt(\n projectId: string,\n storyId: string,\n viewport: string,\n branch: string,\n snapshotId: 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 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 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 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 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 await this.discardFile(baseline);\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 await this.discardFile(baseline);\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":"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"}
|
|
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":"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"}
|
|
@@ -3,7 +3,6 @@ import __tsdown_shims_url from 'node:url';
|
|
|
3
3
|
import { DatabaseAdapter } from "../adapter/database.mjs";
|
|
4
4
|
import { BuildComponent } from "../schema/build-component.mjs";
|
|
5
5
|
import { Table } from "drizzle-orm";
|
|
6
|
-
//#region src/models/build-component.d.ts
|
|
7
6
|
/** Tables required by {@link BuildComponentModel}. */
|
|
8
7
|
interface BuildComponentTables {
|
|
9
8
|
buildComponents: Table;
|
|
@@ -23,6 +22,5 @@ declare class BuildComponentModel {
|
|
|
23
22
|
/** Forget a build's rows (the foreign key cascades too; stub tables have none). */
|
|
24
23
|
removeForBuild(buildId: string): Promise<number>;
|
|
25
24
|
}
|
|
26
|
-
//#endregion
|
|
27
25
|
export { BuildComponentInput, BuildComponentModel, BuildComponentTables };
|
|
28
26
|
//# sourceMappingURL=build-component.d.mts.map
|
|
@@ -4,7 +4,7 @@ import.meta.url;
|
|
|
4
4
|
import { ulid } from "../utils/ulid.mjs";
|
|
5
5
|
import { col } from "./columns.mjs";
|
|
6
6
|
import { and, asc, desc, eq } from "drizzle-orm";
|
|
7
|
-
|
|
7
|
+
// src/models/build-component.ts
|
|
8
8
|
/** Component rows of published builds (`build_components`, ADR 0006 decision 9). */
|
|
9
9
|
/** Data operations for the components each published build holds. */
|
|
10
10
|
var BuildComponentModel = class {
|
|
@@ -49,7 +49,6 @@ var BuildComponentModel = class {
|
|
|
49
49
|
return rows.length;
|
|
50
50
|
}
|
|
51
51
|
};
|
|
52
|
-
//#endregion
|
|
53
52
|
export { BuildComponentModel };
|
|
54
53
|
|
|
55
54
|
//# sourceMappingURL=build-component.mjs.map
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
3
|
import { BuildReview } from "../schema/build-review.mjs";
|
|
4
|
-
//#region src/models/build-review-status.d.ts
|
|
5
4
|
/** What {@link deriveReviewStatus} depends on. */
|
|
6
5
|
interface ReviewStatusInput {
|
|
7
6
|
/** `testing` is on: the snapshot rollup decides and verdicts never gate. */
|
|
@@ -24,6 +23,5 @@ interface ReviewStatusInput {
|
|
|
24
23
|
declare function deriveReviewStatus(input: ReviewStatusInput): "approved" | "rejected" | "pending" | null;
|
|
25
24
|
/** Each reviewer's latest verdict (newest `createdAt`, then newest id), oldest reviewer first. */
|
|
26
25
|
declare function latestVerdicts<T extends Pick<BuildReview, "id" | "userId" | "createdAt">>(reviews: readonly T[]): T[];
|
|
27
|
-
//#endregion
|
|
28
26
|
export { ReviewStatusInput, deriveReviewStatus, latestVerdicts };
|
|
29
27
|
//# sourceMappingURL=build-review-status.d.mts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
|
|
4
|
+
// src/models/build-review-status.ts
|
|
5
5
|
/** Reviewer key standing for verdicts without a user (auth off or an admin token): one reviewer. */
|
|
6
6
|
const ANONYMOUS_REVIEWER = "";
|
|
7
7
|
/**
|
|
@@ -31,7 +31,6 @@ function latestVerdicts(reviews) {
|
|
|
31
31
|
function isNewer(a, b) {
|
|
32
32
|
return a.createdAt === b.createdAt ? a.id > b.id : a.createdAt > b.createdAt;
|
|
33
33
|
}
|
|
34
|
-
//#endregion
|
|
35
34
|
export { deriveReviewStatus, latestVerdicts };
|
|
36
35
|
|
|
37
36
|
//# sourceMappingURL=build-review-status.mjs.map
|
|
@@ -4,7 +4,6 @@ import { DatabaseAdapter } from "../adapter/database.mjs";
|
|
|
4
4
|
import { ReviewVerdict } from "../types.mjs";
|
|
5
5
|
import { BuildReview } from "../schema/build-review.mjs";
|
|
6
6
|
import { Table } from "drizzle-orm";
|
|
7
|
-
//#region src/models/build-review.d.ts
|
|
8
7
|
/** Tables required by {@link BuildReviewModel}. */
|
|
9
8
|
interface BuildReviewTables {
|
|
10
9
|
buildReviews: Table;
|
|
@@ -23,6 +22,5 @@ declare class BuildReviewModel {
|
|
|
23
22
|
/** Each reviewer's latest verdict on a build. */
|
|
24
23
|
latestByBuild(buildId: string): Promise<BuildReview[]>;
|
|
25
24
|
}
|
|
26
|
-
//#endregion
|
|
27
25
|
export { BuildReviewModel, BuildReviewTables };
|
|
28
26
|
//# sourceMappingURL=build-review.d.mts.map
|
|
@@ -5,7 +5,7 @@ import { ulid } from "../utils/ulid.mjs";
|
|
|
5
5
|
import { col } from "./columns.mjs";
|
|
6
6
|
import { latestVerdicts } from "./build-review-status.mjs";
|
|
7
7
|
import { asc, eq } from "drizzle-orm";
|
|
8
|
-
|
|
8
|
+
// src/models/build-review.ts
|
|
9
9
|
/** Build-level review verdicts (ADR 0013, decision 3). */
|
|
10
10
|
/** Data operations for the verdicts reviewers give a build. */
|
|
11
11
|
var BuildReviewModel = class {
|
|
@@ -48,7 +48,6 @@ function after(newest) {
|
|
|
48
48
|
const floor = newest === void 0 ? Number.NEGATIVE_INFINITY : Date.parse(newest) + 1;
|
|
49
49
|
return new Date(Math.max(now, floor)).toISOString();
|
|
50
50
|
}
|
|
51
|
-
//#endregion
|
|
52
51
|
export { BuildReviewModel };
|
|
53
52
|
|
|
54
53
|
//# sourceMappingURL=build-review.mjs.map
|
package/dist/models/build.d.mts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
|
-
import { DatabaseAdapter } from "../adapter/database.mjs";
|
|
4
3
|
import { Logger } from "../logger.mjs";
|
|
4
|
+
import { DatabaseAdapter } from "../adapter/database.mjs";
|
|
5
5
|
import { BuildStatus } from "../types.mjs";
|
|
6
6
|
import { Build } from "../schema/build.mjs";
|
|
7
7
|
import { Table } from "drizzle-orm";
|
|
8
|
-
//#region src/models/build.d.ts
|
|
9
8
|
/** Tables required by {@link BuildModel}. */
|
|
10
9
|
interface BuildTables {
|
|
11
10
|
builds: Table;
|
|
@@ -104,6 +103,5 @@ interface BuildListFilter {
|
|
|
104
103
|
/** Newest builds to return at most. */
|
|
105
104
|
limit?: number;
|
|
106
105
|
}
|
|
107
|
-
//#endregion
|
|
108
106
|
export { AffectedComputation, BuildCreateInput, BuildListFilter, BuildModel, BuildTables, isPublicBuild };
|
|
109
107
|
//# sourceMappingURL=build.d.mts.map
|
package/dist/models/build.mjs
CHANGED
|
@@ -3,8 +3,8 @@ import "node:url";
|
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { ulid } from "../utils/ulid.mjs";
|
|
5
5
|
import { col } from "./columns.mjs";
|
|
6
|
-
import { and, desc, eq, inArray } from "drizzle-orm";
|
|
7
|
-
|
|
6
|
+
import { and, desc, eq, inArray, ne } from "drizzle-orm";
|
|
7
|
+
// src/models/build.ts
|
|
8
8
|
/** Build records, status transitions, and publication helpers. */
|
|
9
9
|
/** Data operations for build records. */
|
|
10
10
|
var BuildModel = class {
|
|
@@ -79,20 +79,20 @@ var BuildModel = class {
|
|
|
79
79
|
* the build. Only {@link BuildModel.resetStatus} leaves `cancelled`.
|
|
80
80
|
*/
|
|
81
81
|
async update(id, patch, options = {}) {
|
|
82
|
-
|
|
83
|
-
const current = await this.get(id);
|
|
84
|
-
if (current?.status === "cancelled") {
|
|
85
|
-
this.logger?.debug({
|
|
86
|
-
buildId: id,
|
|
87
|
-
refused: patch.status
|
|
88
|
-
}, "build is cancelled; status write refused");
|
|
89
|
-
return current;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return await this.db.update(this.tables.builds, id, {
|
|
82
|
+
const values = {
|
|
93
83
|
...patch,
|
|
94
84
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
95
|
-
}
|
|
85
|
+
};
|
|
86
|
+
if (patch.status !== void 0 && patch.status !== "cancelled" && options.reset !== true) {
|
|
87
|
+
const current = await this.db.updateWhere(this.tables.builds, id, values, ne(col(this.tables.builds, "status"), "cancelled"));
|
|
88
|
+
if (!current) throw new Error("Build not found");
|
|
89
|
+
if (current.status === "cancelled") this.logger?.debug({
|
|
90
|
+
buildId: id,
|
|
91
|
+
refused: patch.status
|
|
92
|
+
}, "build is cancelled; status write refused");
|
|
93
|
+
return current;
|
|
94
|
+
}
|
|
95
|
+
return await this.db.update(this.tables.builds, id, values);
|
|
96
96
|
}
|
|
97
97
|
async setStatus(id, status) {
|
|
98
98
|
return await this.update(id, { status });
|
|
@@ -180,7 +180,6 @@ function parsePaths(raw) {
|
|
|
180
180
|
return null;
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
|
-
//#endregion
|
|
184
183
|
export { BuildModel, isPublicBuild };
|
|
185
184
|
|
|
186
185
|
//# sourceMappingURL=build.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.mjs","names":[],"sources":["../../src/models/build.ts"],"sourcesContent":["/** Build records, status transitions, and publication helpers. */\nimport { and, desc, eq, inArray } from \"drizzle-orm\";\nimport type { Table } from \"drizzle-orm\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { Logger } from \"../logger.ts\";\nimport type { Build } from \"../schema/build.ts\";\nimport type { BuildStatus } from \"../types.ts\";\nimport { ulid } from \"../utils/ulid.ts\";\nimport { col } from \"./columns.ts\";\n\n/** Tables required by {@link BuildModel}. */\nexport interface BuildTables {\n builds: Table;\n buildLabels: Table;\n snapshots: Table;\n}\n\n/** Data operations for build records. */\nexport class BuildModel {\n private readonly tables: BuildTables;\n private readonly db: DatabaseAdapter;\n private readonly logger: Logger | undefined;\n\n /** `logger` only reports a status write refused by a `cancelled` build, at debug. */\n constructor(db: DatabaseAdapter, tables?: BuildTables, logger?: Logger) {\n this.db = db;\n this.logger = logger;\n this.tables = tables ?? {\n builds: db.tables.builds,\n buildLabels: db.tables.buildLabels,\n snapshots: db.tables.snapshots,\n };\n }\n\n async create(projectId: string, input: BuildCreateInput): Promise<Build> {\n const now = new Date().toISOString();\n return (await this.db.insert(this.tables.builds, {\n id: ulid(),\n projectId,\n gitSha: input.gitSha,\n gitBranch: input.gitBranch,\n isDefault: input.isDefault ?? false,\n authorEmail: input.authorEmail,\n authorName: input.authorName,\n message: input.message,\n public: input.public ?? false,\n affectedOnly: input.affectedOnly ?? true,\n baselineSha: input.baselineSha ?? null,\n changedFiles: serializePaths(input.changedFiles),\n affectedImportPaths: serializePaths(input.affectedImportPaths),\n status: \"pending\",\n createdAt: now,\n updatedAt: now,\n })) as unknown as Build;\n }\n\n async get(id: string): Promise<Build | null> {\n return (await this.db.get(this.tables.builds, id)) as unknown as Build | null;\n }\n\n async list(projectId: string, filter: BuildListFilter = {}): Promise<Build[]> {\n const conditions = [eq(col(this.tables.builds, \"projectId\"), projectId)];\n if (filter.status) {\n conditions.push(eq(col(this.tables.builds, \"status\"), filter.status));\n }\n if (filter.branch) {\n conditions.push(eq(col(this.tables.builds, \"gitBranch\"), filter.branch));\n }\n if (filter.labelKey && filter.labelValue) {\n const labels = (await this.db.list(this.tables.buildLabels, {\n where: and(\n eq(col(this.tables.buildLabels, \"projectId\"), projectId),\n eq(col(this.tables.buildLabels, \"typeKey\"), filter.labelKey),\n eq(col(this.tables.buildLabels, \"value\"), filter.labelValue),\n ),\n })) as unknown as { buildId: string }[];\n const ids = labels.map((l) => l.buildId);\n if (ids.length === 0) {\n return [];\n }\n conditions.push(inArray(col(this.tables.builds, \"id\"), ids));\n }\n return (await this.db.list(this.tables.builds, {\n where: and(...conditions),\n orderBy: desc(col(this.tables.builds, \"createdAt\")),\n ...(filter.limit === undefined ? {} : { limit: filter.limit }),\n })) as unknown as Build[];\n }\n\n /**\n * Return the build a project-slug Storybook link resolves to: the most recent\n * `approved` build on the project's default branch. Approved builds on other\n * branches never qualify (their PR can still be dismissed), and neither do\n * pending, failed, or unreviewed default-branch builds. Visibility (public or\n * authenticated) is checked separately by the caller.\n */\n async latestApprovedDefault(project: {\n id: string;\n gitDefaultBranch: string;\n }): Promise<Build | null> {\n const rows = await this.list(project.id, {\n branch: project.gitDefaultBranch,\n status: \"approved\",\n });\n return rows[0] ?? null;\n }\n\n /**\n * Patch a build. A `cancelled` build is terminal (ADR 0006): a patch that carries another status\n * is refused, the row is returned as it is, so a capture that ends after a cancel cannot move\n * the build. Only {@link BuildModel.resetStatus} leaves `cancelled`.\n */\n async update(\n id: string,\n patch: Partial<Pick<Build, \"status\" | \"public\" | \"message\" | \"authorEmail\" | \"authorName\">>,\n options: { reset?: boolean } = {},\n ): Promise<Build> {\n if (patch.status !== undefined && patch.status !== \"cancelled\" && options.reset !== true) {\n const current = await this.get(id);\n if (current?.status === \"cancelled\") {\n this.logger?.debug(\n { buildId: id, refused: patch.status },\n \"build is cancelled; status write refused\",\n );\n return current;\n }\n }\n return (await this.db.update(this.tables.builds, id, {\n ...patch,\n updatedAt: new Date().toISOString(),\n })) as unknown as Build;\n }\n\n async setStatus(id: string, status: BuildStatus): Promise<Build> {\n return await this.update(id, { status });\n }\n\n /**\n * Put a finished build back to `status` for a retry. The one write that leaves `cancelled`\n * (a user chose it), so it skips the terminal guard of {@link BuildModel.update}.\n */\n async resetStatus(id: string, status: BuildStatus): Promise<Build> {\n return await this.update(id, { status }, { reset: true });\n }\n\n /**\n * Record the affected-capture computation for a build. Idempotent: the CLI\n * posts this once after creating the build and before uploading the bundle.\n */\n async setAffected(id: string, input: AffectedComputation): Promise<Build> {\n return (await this.db.update(this.tables.builds, id, {\n baselineSha: input.baselineSha,\n changedFiles: serializePaths(input.changedFiles),\n affectedImportPaths: serializePaths(input.affectedImportPaths),\n updatedAt: new Date().toISOString(),\n })) as unknown as Build;\n }\n\n /**\n * Store the results of the build's `.test()` entries (`TestResult[]`, as JSON), or clear them\n * with null. Written once per capture run, by the run that ran the tests.\n */\n async setTestResults(id: string, results: readonly unknown[] | null): Promise<Build> {\n return (await this.db.update(this.tables.builds, id, {\n testResults: results === null ? null : JSON.stringify(results),\n updatedAt: new Date().toISOString(),\n })) as unknown as Build;\n }\n\n /**\n * Parse a build's affected import paths. Returns `null` for full capture\n * (no computation, or an unreadable payload) — callers render everything.\n */\n static affectedPaths(build: Pick<Build, \"affectedImportPaths\">): string[] | null {\n return parsePaths(build.affectedImportPaths);\n }\n\n async updateCounts(id: string): Promise<Build> {\n const rows = (await this.db.list(this.tables.snapshots, {\n where: eq(col(this.tables.snapshots, \"buildId\"), id),\n })) as unknown as { status: string; inherited: boolean }[];\n const snapshotCount = rows.length;\n const changedCount = rows.filter((s) => s.status === \"changed\" || s.status === \"new\").length;\n const approvedCount = rows.filter(\n (s) => s.status === \"approved\" || s.status === \"unchanged\",\n ).length;\n const rejectedCount = rows.filter((s) => s.status === \"rejected\").length;\n const inheritedCount = rows.filter((s) => s.inherited).length;\n return (await this.db.update(this.tables.builds, id, {\n snapshotCount,\n changedCount,\n approvedCount,\n rejectedCount,\n inheritedCount,\n updatedAt: new Date().toISOString(),\n })) as unknown as Build;\n }\n\n async remove(id: string): Promise<void> {\n await this.db.remove(this.tables.builds, id);\n }\n}\n\n/**\n * Return whether a build is publicly viewable without a session.\n *\n * A build is public iff `builds.public` is set or its branch matches the\n * project's `public_branch_regex` (ADR 0011). Supplying an empty/nonexistent\n * regex makes every build require auth.\n */\nexport function isPublicBuild(\n project: { publicBranchRegex: string | null },\n build: Pick<Build, \"public\" | \"gitBranch\">,\n): boolean {\n if (build.public) {\n return true;\n }\n if (!project.publicBranchRegex) {\n return false;\n }\n return new RegExp(project.publicBranchRegex, \"u\").test(build.gitBranch);\n}\n\n/** Input for creating a build. */\nexport interface BuildCreateInput {\n gitSha: string;\n gitBranch: string;\n isDefault?: boolean;\n authorEmail?: string;\n authorName?: string;\n message?: string;\n public?: boolean;\n affectedOnly?: boolean;\n baselineSha?: string | null;\n changedFiles?: string[] | null;\n affectedImportPaths?: string[] | null;\n}\n\n/** Affected-capture computation posted after build creation. */\nexport interface AffectedComputation {\n baselineSha: string | null;\n changedFiles: string[];\n affectedImportPaths: string[] | null;\n}\n\n/** Serialize an optional path list to its JSON text form (or null). */\nfunction serializePaths(paths: string[] | null | undefined): string | null {\n if (!paths) {\n return null;\n }\n return JSON.stringify(paths);\n}\n\n/** Parse a JSON path list; null (full capture) on missing/invalid input. */\nfunction parsePaths(raw: string | null): string[] | null {\n if (!raw) {\n return null;\n }\n try {\n const parsed: unknown = JSON.parse(raw);\n return Array.isArray(parsed) && parsed.every((entry) => typeof entry === \"string\")\n ? (parsed as string[])\n : null;\n } catch {\n return null;\n }\n}\n\n/** Filter options for listing builds. */\nexport interface BuildListFilter {\n status?: BuildStatus;\n branch?: string;\n labelKey?: string;\n labelValue?: string;\n /** Newest builds to return at most. */\n limit?: number;\n}\n"],"mappings":";;;;;;;;;AAkBA,IAAa,aAAb,MAAwB;CACtB;CACA;CACA;;CAGA,YAAY,IAAqB,QAAsB,QAAiB;EACtE,KAAK,KAAK;EACV,KAAK,SAAS;EACd,KAAK,SAAS,UAAU;GACtB,QAAQ,GAAG,OAAO;GAClB,aAAa,GAAG,OAAO;GACvB,WAAW,GAAG,OAAO;EACvB;CACF;CAEA,MAAM,OAAO,WAAmB,OAAyC;EACvE,MAAM,uBAAM,IAAI,KAAK,EAAA,CAAE,YAAY;EACnC,OAAQ,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,QAAQ;GAC/C,IAAI,KAAK;GACT;GACA,QAAQ,MAAM;GACd,WAAW,MAAM;GACjB,WAAW,MAAM,aAAa;GAC9B,aAAa,MAAM;GACnB,YAAY,MAAM;GAClB,SAAS,MAAM;GACf,QAAQ,MAAM,UAAU;GACxB,cAAc,MAAM,gBAAgB;GACpC,aAAa,MAAM,eAAe;GAClC,cAAc,eAAe,MAAM,YAAY;GAC/C,qBAAqB,eAAe,MAAM,mBAAmB;GAC7D,QAAQ;GACR,WAAW;GACX,WAAW;EACb,CAAC;CACH;CAEA,MAAM,IAAI,IAAmC;EAC3C,OAAQ,MAAM,KAAK,GAAG,IAAI,KAAK,OAAO,QAAQ,EAAE;CAClD;CAEA,MAAM,KAAK,WAAmB,SAA0B,CAAC,GAAqB;EAC5E,MAAM,aAAa,CAAC,GAAG,IAAI,KAAK,OAAO,QAAQ,WAAW,GAAG,SAAS,CAAC;EACvE,IAAI,OAAO,QACT,WAAW,KAAK,GAAG,IAAI,KAAK,OAAO,QAAQ,QAAQ,GAAG,OAAO,MAAM,CAAC;EAEtE,IAAI,OAAO,QACT,WAAW,KAAK,GAAG,IAAI,KAAK,OAAO,QAAQ,WAAW,GAAG,OAAO,MAAM,CAAC;EAEzE,IAAI,OAAO,YAAY,OAAO,YAAY;GAQxC,MAAM,OAAM,MAPU,KAAK,GAAG,KAAK,KAAK,OAAO,aAAa,EAC1D,OAAO,IACL,GAAG,IAAI,KAAK,OAAO,aAAa,WAAW,GAAG,SAAS,GACvD,GAAG,IAAI,KAAK,OAAO,aAAa,SAAS,GAAG,OAAO,QAAQ,GAC3D,GAAG,IAAI,KAAK,OAAO,aAAa,OAAO,GAAG,OAAO,UAAU,CAC7D,EACF,CAAC,EAAA,CACkB,KAAK,MAAM,EAAE,OAAO;GACvC,IAAI,IAAI,WAAW,GACjB,OAAO,CAAC;GAEV,WAAW,KAAK,QAAQ,IAAI,KAAK,OAAO,QAAQ,IAAI,GAAG,GAAG,CAAC;EAC7D;EACA,OAAQ,MAAM,KAAK,GAAG,KAAK,KAAK,OAAO,QAAQ;GAC7C,OAAO,IAAI,GAAG,UAAU;GACxB,SAAS,KAAK,IAAI,KAAK,OAAO,QAAQ,WAAW,CAAC;GAClD,GAAI,OAAO,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO,OAAO,MAAM;EAC9D,CAAC;CACH;;;;;;;;CASA,MAAM,sBAAsB,SAGF;EAKxB,QAAO,MAJY,KAAK,KAAK,QAAQ,IAAI;GACvC,QAAQ,QAAQ;GAChB,QAAQ;EACV,CAAC,EAAA,CACW,MAAM;CACpB;;;;;;CAOA,MAAM,OACJ,IACA,OACA,UAA+B,CAAC,GAChB;EAChB,IAAI,MAAM,WAAW,KAAA,KAAa,MAAM,WAAW,eAAe,QAAQ,UAAU,MAAM;GACxF,MAAM,UAAU,MAAM,KAAK,IAAI,EAAE;GACjC,IAAI,SAAS,WAAW,aAAa;IACnC,KAAK,QAAQ,MACX;KAAE,SAAS;KAAI,SAAS,MAAM;IAAO,GACrC,0CACF;IACA,OAAO;GACT;EACF;EACA,OAAQ,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,QAAQ,IAAI;GACnD,GAAG;GACH,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;EACpC,CAAC;CACH;CAEA,MAAM,UAAU,IAAY,QAAqC;EAC/D,OAAO,MAAM,KAAK,OAAO,IAAI,EAAE,OAAO,CAAC;CACzC;;;;;CAMA,MAAM,YAAY,IAAY,QAAqC;EACjE,OAAO,MAAM,KAAK,OAAO,IAAI,EAAE,OAAO,GAAG,EAAE,OAAO,KAAK,CAAC;CAC1D;;;;;CAMA,MAAM,YAAY,IAAY,OAA4C;EACxE,OAAQ,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,QAAQ,IAAI;GACnD,aAAa,MAAM;GACnB,cAAc,eAAe,MAAM,YAAY;GAC/C,qBAAqB,eAAe,MAAM,mBAAmB;GAC7D,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;EACpC,CAAC;CACH;;;;;CAMA,MAAM,eAAe,IAAY,SAAoD;EACnF,OAAQ,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,QAAQ,IAAI;GACnD,aAAa,YAAY,OAAO,OAAO,KAAK,UAAU,OAAO;GAC7D,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;EACpC,CAAC;CACH;;;;;CAMA,OAAO,cAAc,OAA4D;EAC/E,OAAO,WAAW,MAAM,mBAAmB;CAC7C;CAEA,MAAM,aAAa,IAA4B;EAC7C,MAAM,OAAQ,MAAM,KAAK,GAAG,KAAK,KAAK,OAAO,WAAW,EACtD,OAAO,GAAG,IAAI,KAAK,OAAO,WAAW,SAAS,GAAG,EAAE,EACrD,CAAC;EACD,MAAM,gBAAgB,KAAK;EAC3B,MAAM,eAAe,KAAK,QAAQ,MAAM,EAAE,WAAW,aAAa,EAAE,WAAW,KAAK,CAAC,CAAC;EACtF,MAAM,gBAAgB,KAAK,QACxB,MAAM,EAAE,WAAW,cAAc,EAAE,WAAW,WACjD,CAAC,CAAC;EACF,MAAM,gBAAgB,KAAK,QAAQ,MAAM,EAAE,WAAW,UAAU,CAAC,CAAC;EAClE,MAAM,iBAAiB,KAAK,QAAQ,MAAM,EAAE,SAAS,CAAC,CAAC;EACvD,OAAQ,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,QAAQ,IAAI;GACnD;GACA;GACA;GACA;GACA;GACA,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;EACpC,CAAC;CACH;CAEA,MAAM,OAAO,IAA2B;EACtC,MAAM,KAAK,GAAG,OAAO,KAAK,OAAO,QAAQ,EAAE;CAC7C;AACF;;;;;;;;AASA,SAAgB,cACd,SACA,OACS;CACT,IAAI,MAAM,QACR,OAAO;CAET,IAAI,CAAC,QAAQ,mBACX,OAAO;CAET,OAAO,IAAI,OAAO,QAAQ,mBAAmB,GAAG,CAAC,CAAC,KAAK,MAAM,SAAS;AACxE;;AAyBA,SAAS,eAAe,OAAmD;CACzE,IAAI,CAAC,OACH,OAAO;CAET,OAAO,KAAK,UAAU,KAAK;AAC7B;;AAGA,SAAS,WAAW,KAAqC;CACvD,IAAI,CAAC,KACH,OAAO;CAET,IAAI;EACF,MAAM,SAAkB,KAAK,MAAM,GAAG;EACtC,OAAO,MAAM,QAAQ,MAAM,KAAK,OAAO,OAAO,UAAU,OAAO,UAAU,QAAQ,IAC5E,SACD;CACN,QAAQ;EACN,OAAO;CACT;AACF"}
|
|
1
|
+
{"version":3,"file":"build.mjs","names":[],"sources":["../../src/models/build.ts"],"sourcesContent":["/** Build records, status transitions, and publication helpers. */\nimport { and, desc, eq, inArray, ne } from \"drizzle-orm\";\nimport type { Table } from \"drizzle-orm\";\nimport type { DatabaseAdapter } from \"../adapters/database.ts\";\nimport type { Logger } from \"../logger.ts\";\nimport type { Build } from \"../schema/build.ts\";\nimport type { BuildStatus } from \"../types.ts\";\nimport { ulid } from \"../utils/ulid.ts\";\nimport { col } from \"./columns.ts\";\n\n/** Tables required by {@link BuildModel}. */\nexport interface BuildTables {\n builds: Table;\n buildLabels: Table;\n snapshots: Table;\n}\n\n/** Data operations for build records. */\nexport class BuildModel {\n private readonly tables: BuildTables;\n private readonly db: DatabaseAdapter;\n private readonly logger: Logger | undefined;\n\n /** `logger` only reports a status write refused by a `cancelled` build, at debug. */\n constructor(db: DatabaseAdapter, tables?: BuildTables, logger?: Logger) {\n this.db = db;\n this.logger = logger;\n this.tables = tables ?? {\n builds: db.tables.builds,\n buildLabels: db.tables.buildLabels,\n snapshots: db.tables.snapshots,\n };\n }\n\n async create(projectId: string, input: BuildCreateInput): Promise<Build> {\n const now = new Date().toISOString();\n return (await this.db.insert(this.tables.builds, {\n id: ulid(),\n projectId,\n gitSha: input.gitSha,\n gitBranch: input.gitBranch,\n isDefault: input.isDefault ?? false,\n authorEmail: input.authorEmail,\n authorName: input.authorName,\n message: input.message,\n public: input.public ?? false,\n affectedOnly: input.affectedOnly ?? true,\n baselineSha: input.baselineSha ?? null,\n changedFiles: serializePaths(input.changedFiles),\n affectedImportPaths: serializePaths(input.affectedImportPaths),\n status: \"pending\",\n createdAt: now,\n updatedAt: now,\n })) as unknown as Build;\n }\n\n async get(id: string): Promise<Build | null> {\n return (await this.db.get(this.tables.builds, id)) as unknown as Build | null;\n }\n\n async list(projectId: string, filter: BuildListFilter = {}): Promise<Build[]> {\n const conditions = [eq(col(this.tables.builds, \"projectId\"), projectId)];\n if (filter.status) {\n conditions.push(eq(col(this.tables.builds, \"status\"), filter.status));\n }\n if (filter.branch) {\n conditions.push(eq(col(this.tables.builds, \"gitBranch\"), filter.branch));\n }\n if (filter.labelKey && filter.labelValue) {\n const labels = (await this.db.list(this.tables.buildLabels, {\n where: and(\n eq(col(this.tables.buildLabels, \"projectId\"), projectId),\n eq(col(this.tables.buildLabels, \"typeKey\"), filter.labelKey),\n eq(col(this.tables.buildLabels, \"value\"), filter.labelValue),\n ),\n })) as unknown as { buildId: string }[];\n const ids = labels.map((l) => l.buildId);\n if (ids.length === 0) {\n return [];\n }\n conditions.push(inArray(col(this.tables.builds, \"id\"), ids));\n }\n return (await this.db.list(this.tables.builds, {\n where: and(...conditions),\n orderBy: desc(col(this.tables.builds, \"createdAt\")),\n ...(filter.limit === undefined ? {} : { limit: filter.limit }),\n })) as unknown as Build[];\n }\n\n /**\n * Return the build a project-slug Storybook link resolves to: the most recent\n * `approved` build on the project's default branch. Approved builds on other\n * branches never qualify (their PR can still be dismissed), and neither do\n * pending, failed, or unreviewed default-branch builds. Visibility (public or\n * authenticated) is checked separately by the caller.\n */\n async latestApprovedDefault(project: {\n id: string;\n gitDefaultBranch: string;\n }): Promise<Build | null> {\n const rows = await this.list(project.id, {\n branch: project.gitDefaultBranch,\n status: \"approved\",\n });\n return rows[0] ?? null;\n }\n\n /**\n * Patch a build. A `cancelled` build is terminal (ADR 0006): a patch that carries another status\n * is refused, the row is returned as it is, so a capture that ends after a cancel cannot move\n * the build. Only {@link BuildModel.resetStatus} leaves `cancelled`.\n */\n async update(\n id: string,\n patch: Partial<Pick<Build, \"status\" | \"public\" | \"message\" | \"authorEmail\" | \"authorName\">>,\n options: { reset?: boolean } = {},\n ): Promise<Build> {\n const values = { ...patch, updatedAt: new Date().toISOString() };\n if (patch.status !== undefined && patch.status !== \"cancelled\" && options.reset !== true) {\n const current = (await this.db.updateWhere(\n this.tables.builds,\n id,\n values,\n ne(col(this.tables.builds, \"status\"), \"cancelled\"),\n )) as unknown as Build | null;\n if (!current) throw new Error(\"Build not found\");\n if (current.status === \"cancelled\") {\n this.logger?.debug(\n { buildId: id, refused: patch.status },\n \"build is cancelled; status write refused\",\n );\n }\n return current;\n }\n return (await this.db.update(this.tables.builds, id, values)) as unknown as Build;\n }\n\n async setStatus(id: string, status: BuildStatus): Promise<Build> {\n return await this.update(id, { status });\n }\n\n /**\n * Put a finished build back to `status` for a retry. The one write that leaves `cancelled`\n * (a user chose it), so it skips the terminal guard of {@link BuildModel.update}.\n */\n async resetStatus(id: string, status: BuildStatus): Promise<Build> {\n return await this.update(id, { status }, { reset: true });\n }\n\n /**\n * Record the affected-capture computation for a build. Idempotent: the CLI\n * posts this once after creating the build and before uploading the bundle.\n */\n async setAffected(id: string, input: AffectedComputation): Promise<Build> {\n return (await this.db.update(this.tables.builds, id, {\n baselineSha: input.baselineSha,\n changedFiles: serializePaths(input.changedFiles),\n affectedImportPaths: serializePaths(input.affectedImportPaths),\n updatedAt: new Date().toISOString(),\n })) as unknown as Build;\n }\n\n /**\n * Store the results of the build's `.test()` entries (`TestResult[]`, as JSON), or clear them\n * with null. Written once per capture run, by the run that ran the tests.\n */\n async setTestResults(id: string, results: readonly unknown[] | null): Promise<Build> {\n return (await this.db.update(this.tables.builds, id, {\n testResults: results === null ? null : JSON.stringify(results),\n updatedAt: new Date().toISOString(),\n })) as unknown as Build;\n }\n\n /**\n * Parse a build's affected import paths. Returns `null` for full capture\n * (no computation, or an unreadable payload) — callers render everything.\n */\n static affectedPaths(build: Pick<Build, \"affectedImportPaths\">): string[] | null {\n return parsePaths(build.affectedImportPaths);\n }\n\n async updateCounts(id: string): Promise<Build> {\n const rows = (await this.db.list(this.tables.snapshots, {\n where: eq(col(this.tables.snapshots, \"buildId\"), id),\n })) as unknown as { status: string; inherited: boolean }[];\n const snapshotCount = rows.length;\n const changedCount = rows.filter((s) => s.status === \"changed\" || s.status === \"new\").length;\n const approvedCount = rows.filter(\n (s) => s.status === \"approved\" || s.status === \"unchanged\",\n ).length;\n const rejectedCount = rows.filter((s) => s.status === \"rejected\").length;\n const inheritedCount = rows.filter((s) => s.inherited).length;\n return (await this.db.update(this.tables.builds, id, {\n snapshotCount,\n changedCount,\n approvedCount,\n rejectedCount,\n inheritedCount,\n updatedAt: new Date().toISOString(),\n })) as unknown as Build;\n }\n\n async remove(id: string): Promise<void> {\n await this.db.remove(this.tables.builds, id);\n }\n}\n\n/**\n * Return whether a build is publicly viewable without a session.\n *\n * A build is public iff `builds.public` is set or its branch matches the\n * project's `public_branch_regex` (ADR 0011). Supplying an empty/nonexistent\n * regex makes every build require auth.\n */\nexport function isPublicBuild(\n project: { publicBranchRegex: string | null },\n build: Pick<Build, \"public\" | \"gitBranch\">,\n): boolean {\n if (build.public) {\n return true;\n }\n if (!project.publicBranchRegex) {\n return false;\n }\n return new RegExp(project.publicBranchRegex, \"u\").test(build.gitBranch);\n}\n\n/** Input for creating a build. */\nexport interface BuildCreateInput {\n gitSha: string;\n gitBranch: string;\n isDefault?: boolean;\n authorEmail?: string;\n authorName?: string;\n message?: string;\n public?: boolean;\n affectedOnly?: boolean;\n baselineSha?: string | null;\n changedFiles?: string[] | null;\n affectedImportPaths?: string[] | null;\n}\n\n/** Affected-capture computation posted after build creation. */\nexport interface AffectedComputation {\n baselineSha: string | null;\n changedFiles: string[];\n affectedImportPaths: string[] | null;\n}\n\n/** Serialize an optional path list to its JSON text form (or null). */\nfunction serializePaths(paths: string[] | null | undefined): string | null {\n if (!paths) {\n return null;\n }\n return JSON.stringify(paths);\n}\n\n/** Parse a JSON path list; null (full capture) on missing/invalid input. */\nfunction parsePaths(raw: string | null): string[] | null {\n if (!raw) {\n return null;\n }\n try {\n const parsed: unknown = JSON.parse(raw);\n return Array.isArray(parsed) && parsed.every((entry) => typeof entry === \"string\")\n ? (parsed as string[])\n : null;\n } catch {\n return null;\n }\n}\n\n/** Filter options for listing builds. */\nexport interface BuildListFilter {\n status?: BuildStatus;\n branch?: string;\n labelKey?: string;\n labelValue?: string;\n /** Newest builds to return at most. */\n limit?: number;\n}\n"],"mappings":"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"}
|
|
@@ -5,7 +5,6 @@ import { JobStatus } from "../adapter/capture-queue.mjs";
|
|
|
5
5
|
import { CaptureAttempt, CaptureLogLevel } from "../schema/capture-attempt.mjs";
|
|
6
6
|
import { Logger } from "pino";
|
|
7
7
|
import { Table } from "drizzle-orm";
|
|
8
|
-
//#region src/models/capture-attempt.d.ts
|
|
9
8
|
/** Tables required by {@link CaptureAttemptModel}. */
|
|
10
9
|
interface CaptureAttemptTables {
|
|
11
10
|
captureAttempts: Table;
|
|
@@ -41,6 +40,5 @@ type AttemptLogRecorder = (level: CaptureLogLevel, message: string, fields?: unk
|
|
|
41
40
|
* Log-write failures are reported through pino and never thrown.
|
|
42
41
|
*/
|
|
43
42
|
declare function emitAttemptLog(record: AttemptLogRecorder | undefined, logger: Logger | undefined, level: CaptureLogLevel, message: string, fields?: unknown): Promise<void>;
|
|
44
|
-
//#endregion
|
|
45
43
|
export { AttemptLogRecorder, CaptureAttemptModel, CaptureAttemptTables, emitAttemptLog };
|
|
46
44
|
//# sourceMappingURL=capture-attempt.d.mts.map
|
|
@@ -5,7 +5,7 @@ import { redactSecrets } from "../utils/redact.mjs";
|
|
|
5
5
|
import { ulid } from "../utils/ulid.mjs";
|
|
6
6
|
import { col } from "./columns.mjs";
|
|
7
7
|
import { and, asc, eq } from "drizzle-orm";
|
|
8
|
-
|
|
8
|
+
// src/models/capture-attempt.ts
|
|
9
9
|
/** Capture attempts and their log lines: per-run history for a build. */
|
|
10
10
|
/** Data operations for capture attempts (one row per build run). */
|
|
11
11
|
var CaptureAttemptModel = class {
|
|
@@ -104,7 +104,6 @@ async function emitAttemptLog(record, logger, level, message, fields) {
|
|
|
104
104
|
logger?.warn({ err: error }, "attempt log write failed");
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
|
-
//#endregion
|
|
108
107
|
export { CaptureAttemptModel, emitAttemptLog };
|
|
109
108
|
|
|
110
109
|
//# sourceMappingURL=capture-attempt.mjs.map
|
|
@@ -3,7 +3,6 @@ import __tsdown_shims_url from 'node:url';
|
|
|
3
3
|
import { DatabaseAdapter } from "../adapter/database.mjs";
|
|
4
4
|
import { CaptureAttemptStatus, CaptureJobKind, CaptureJobRow, CaptureJobStatus } from "../schema/capture-job.mjs";
|
|
5
5
|
import { Table } from "drizzle-orm";
|
|
6
|
-
//#region src/models/capture-job.d.ts
|
|
7
6
|
/** Tables required by {@link CaptureJobModel}. */
|
|
8
7
|
interface CaptureJobTables {
|
|
9
8
|
captureJobs: Table;
|
|
@@ -101,6 +100,5 @@ declare function parseAttemptRowId(id: string): {
|
|
|
101
100
|
} | null;
|
|
102
101
|
/** Whether a job row has ended. */
|
|
103
102
|
declare function isTerminalJob(row: CaptureJobRow): boolean;
|
|
104
|
-
//#endregion
|
|
105
103
|
export { AttemptClaim, CaptureJobModel, CaptureJobTables, NewCaptureJob, TERMINAL_JOB_STATUSES, attemptRowId, isTerminalJob, parseAttemptRowId };
|
|
106
104
|
//# sourceMappingURL=capture-job.d.mts.map
|
|
@@ -3,7 +3,7 @@ import "node:url";
|
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { col } from "./columns.mjs";
|
|
5
5
|
import { and, asc, desc, eq, inArray, lt, lte } from "drizzle-orm";
|
|
6
|
-
|
|
6
|
+
// src/models/capture-job.ts
|
|
7
7
|
/** Rows of the database queue (`capture_jobs`): job rows, per-attempt claim rows, and their views. */
|
|
8
8
|
/** Job statuses that end a job: nothing claims it again. */
|
|
9
9
|
const TERMINAL_JOB_STATUSES = [
|
|
@@ -282,7 +282,6 @@ function blankRow(now) {
|
|
|
282
282
|
updatedAt: now
|
|
283
283
|
};
|
|
284
284
|
}
|
|
285
|
-
//#endregion
|
|
286
285
|
export { CaptureJobModel, TERMINAL_JOB_STATUSES, attemptRowId, isTerminalJob, parseAttemptRowId };
|
|
287
286
|
|
|
288
287
|
//# sourceMappingURL=capture-job.mjs.map
|
|
@@ -3,7 +3,6 @@ import __tsdown_shims_url from 'node:url';
|
|
|
3
3
|
import { DatabaseAdapter } from "../adapter/database.mjs";
|
|
4
4
|
import { CaptureLog, CaptureLogLevel } from "../schema/capture-attempt.mjs";
|
|
5
5
|
import { Table } from "drizzle-orm";
|
|
6
|
-
//#region src/models/capture-log.d.ts
|
|
7
6
|
/** Tables required by {@link CaptureLogModel}. */
|
|
8
7
|
interface CaptureLogTables {
|
|
9
8
|
captureLogs: Table;
|
|
@@ -16,6 +15,5 @@ declare class CaptureLogModel {
|
|
|
16
15
|
listByAttempt(attemptId: string, limit?: number): Promise<CaptureLog[]>;
|
|
17
16
|
append(projectId: string, buildId: string, attemptId: string, level: CaptureLogLevel, message: string, fields?: unknown): Promise<CaptureLog>;
|
|
18
17
|
}
|
|
19
|
-
//#endregion
|
|
20
18
|
export { CaptureLogModel, CaptureLogTables };
|
|
21
19
|
//# sourceMappingURL=capture-log.d.mts.map
|
|
@@ -4,7 +4,7 @@ import.meta.url;
|
|
|
4
4
|
import { ulid } from "../utils/ulid.mjs";
|
|
5
5
|
import { col } from "./columns.mjs";
|
|
6
6
|
import { asc, eq } from "drizzle-orm";
|
|
7
|
-
|
|
7
|
+
// src/models/capture-log.ts
|
|
8
8
|
/** Capture log lines: every stored line of a capture attempt. */
|
|
9
9
|
/** Data operations for capture log lines (every line of an attempt). */
|
|
10
10
|
var CaptureLogModel = class {
|
|
@@ -39,7 +39,6 @@ var CaptureLogModel = class {
|
|
|
39
39
|
function columnsOf(table, column) {
|
|
40
40
|
return col(table, column);
|
|
41
41
|
}
|
|
42
|
-
//#endregion
|
|
43
42
|
export { CaptureLogModel };
|
|
44
43
|
|
|
45
44
|
//# sourceMappingURL=capture-log.mjs.map
|
|
@@ -3,7 +3,6 @@ import __tsdown_shims_url from 'node:url';
|
|
|
3
3
|
import { DatabaseAdapter } from "../adapter/database.mjs";
|
|
4
4
|
import { CaptureShard } from "../schema/capture-shard.mjs";
|
|
5
5
|
import { Table } from "drizzle-orm";
|
|
6
|
-
//#region src/models/capture-shard.d.ts
|
|
7
6
|
/** Tables required by {@link CaptureShardModel}. */
|
|
8
7
|
interface CaptureShardTables {
|
|
9
8
|
captureShards: Table;
|
|
@@ -57,6 +56,5 @@ declare class CaptureShardModel {
|
|
|
57
56
|
/** The finalize failed: drop its claim, so the next run to reduce the build can finalize it. */
|
|
58
57
|
releaseFinalize(buildId: string): Promise<void>;
|
|
59
58
|
}
|
|
60
|
-
//#endregion
|
|
61
59
|
export { CaptureShardModel, CaptureShardTables, NewRenderShard };
|
|
62
60
|
//# sourceMappingURL=capture-shard.d.mts.map
|
|
@@ -4,7 +4,7 @@ import.meta.url;
|
|
|
4
4
|
import { ulid } from "../utils/ulid.mjs";
|
|
5
5
|
import { col } from "./columns.mjs";
|
|
6
6
|
import { asc, eq } from "drizzle-orm";
|
|
7
|
-
|
|
7
|
+
// src/models/capture-shard.ts
|
|
8
8
|
/** Shard rows of a sharded capture: the plan, the render slices, and the finalize claim. */
|
|
9
9
|
/** Data operations for the stages of a sharded capture. */
|
|
10
10
|
var CaptureShardModel = class {
|
|
@@ -184,7 +184,6 @@ function base(projectId, buildId, kind, ordinal, now) {
|
|
|
184
184
|
function kindRank(kind) {
|
|
185
185
|
return kind === "plan" ? 0 : kind === "render" ? 1 : 2;
|
|
186
186
|
}
|
|
187
|
-
//#endregion
|
|
188
187
|
export { CaptureShardModel, FINALIZE_STALE_MS };
|
|
189
188
|
|
|
190
189
|
//# sourceMappingURL=capture-shard.mjs.map
|
|
@@ -3,7 +3,6 @@ import __tsdown_shims_url from 'node:url';
|
|
|
3
3
|
import { DatabaseAdapter } from "../adapter/database.mjs";
|
|
4
4
|
import { Build } from "../schema/build.mjs";
|
|
5
5
|
import { BuildComponent } from "../schema/build-component.mjs";
|
|
6
|
-
//#region src/models/changeset.d.ts
|
|
7
6
|
/** What the changeset shows of a component. */
|
|
8
7
|
type ChangesetComponent = Pick<BuildComponent, "importPath" | "title" | "storyCount">;
|
|
9
8
|
/** A component present in both builds whose hash differs. */
|
|
@@ -43,6 +42,5 @@ declare function changesetOf(db: DatabaseAdapter, project: {
|
|
|
43
42
|
id: string;
|
|
44
43
|
gitDefaultBranch: string;
|
|
45
44
|
}, build: Build): Promise<BuildChangeset>;
|
|
46
|
-
//#endregion
|
|
47
45
|
export { BuildChangeset, ChangedComponent, Changeset, ChangesetComponent, changesetOf, computeChangeset, findChangesetBaseline };
|
|
48
46
|
//# sourceMappingURL=changeset.d.mts.map
|
|
@@ -3,7 +3,7 @@ import "node:url";
|
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { BuildModel } from "./build.mjs";
|
|
5
5
|
import { BuildComponentModel } from "./build-component.mjs";
|
|
6
|
-
|
|
6
|
+
// src/models/changeset.ts
|
|
7
7
|
/**
|
|
8
8
|
* Compare a build's components with the baseline's. Components match by story file, or by title
|
|
9
9
|
* when the index carries no file. With no baseline (`null`) every component is `added`.
|
|
@@ -75,7 +75,6 @@ function summary(row) {
|
|
|
75
75
|
function byTitle(a, b) {
|
|
76
76
|
return a.title.localeCompare(b.title) || a.importPath.localeCompare(b.importPath);
|
|
77
77
|
}
|
|
78
|
-
//#endregion
|
|
79
78
|
export { changesetOf, computeChangeset, findChangesetBaseline };
|
|
80
79
|
|
|
81
80
|
//# sourceMappingURL=changeset.mjs.map
|
package/dist/models/columns.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import "node:path";
|
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { getTableColumns } from "drizzle-orm";
|
|
5
|
-
|
|
5
|
+
// src/models/columns.ts
|
|
6
6
|
/**
|
|
7
7
|
* Column handle for query builders (`eq`, `inArray`, `desc`, …) from a table
|
|
8
8
|
* handle and a property name. Throws on an unknown name instead of letting
|
|
@@ -13,7 +13,6 @@ function col(table, name) {
|
|
|
13
13
|
if (!column) throw new Error(`Unknown column "${name}"`);
|
|
14
14
|
return column;
|
|
15
15
|
}
|
|
16
|
-
//#endregion
|
|
17
16
|
export { col };
|
|
18
17
|
|
|
19
18
|
//# sourceMappingURL=columns.mjs.map
|
|
@@ -3,7 +3,6 @@ import __tsdown_shims_url from 'node:url';
|
|
|
3
3
|
import { DatabaseAdapter } from "../adapter/database.mjs";
|
|
4
4
|
import { Comment } from "../schema/comment.mjs";
|
|
5
5
|
import { Table } from "drizzle-orm";
|
|
6
|
-
//#region src/models/comment.d.ts
|
|
7
6
|
/** Tables required by {@link CommentModel}. */
|
|
8
7
|
interface CommentTables {
|
|
9
8
|
comments: Table;
|
|
@@ -27,6 +26,5 @@ interface CommentCreateInput {
|
|
|
27
26
|
storyId?: string;
|
|
28
27
|
parentId?: string;
|
|
29
28
|
}
|
|
30
|
-
//#endregion
|
|
31
29
|
export { CommentCreateInput, CommentModel, CommentTables };
|
|
32
30
|
//# sourceMappingURL=comment.d.mts.map
|
package/dist/models/comment.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import.meta.url;
|
|
|
4
4
|
import { ulid } from "../utils/ulid.mjs";
|
|
5
5
|
import { col } from "./columns.mjs";
|
|
6
6
|
import { eq } from "drizzle-orm";
|
|
7
|
-
|
|
7
|
+
// src/models/comment.ts
|
|
8
8
|
/** Build and snapshot review comments. */
|
|
9
9
|
/** Data operations for review comments. */
|
|
10
10
|
var CommentModel = class {
|
|
@@ -45,7 +45,6 @@ var CommentModel = class {
|
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
};
|
|
48
|
-
//#endregion
|
|
49
48
|
export { CommentModel };
|
|
50
49
|
|
|
51
50
|
//# sourceMappingURL=comment.mjs.map
|
|
@@ -3,7 +3,6 @@ import __tsdown_shims_url from 'node:url';
|
|
|
3
3
|
import { SnapshotStatus } from "../types.mjs";
|
|
4
4
|
import { Build } from "../schema/build.mjs";
|
|
5
5
|
import { Snapshot } from "../schema/snapshot.mjs";
|
|
6
|
-
//#region src/models/component.d.ts
|
|
7
6
|
/** The most attention-worthy status of a set (unchanged when empty). */
|
|
8
7
|
declare function worstStatus(statuses: readonly SnapshotStatus[]): SnapshotStatus;
|
|
9
8
|
/** One build's view of a component. */
|
|
@@ -38,6 +37,5 @@ type ComponentPresence = {
|
|
|
38
37
|
* every file, used to find a moved file by its last-seen title.
|
|
39
38
|
*/
|
|
40
39
|
declare function componentPresence(history: readonly ComponentHistoryRow[], latestBuildId: string | null, latest: readonly Pick<Snapshot, "storyTitle" | "storyImportPath">[], lastTitle: string | null): ComponentPresence;
|
|
41
|
-
//#endregion
|
|
42
40
|
export { ComponentHistoryRow, ComponentPresence, componentHistory, componentPresence, worstStatus };
|
|
43
41
|
//# sourceMappingURL=component.d.mts.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
|
|
4
|
+
// src/models/component.ts
|
|
5
5
|
/** Worst-first order used for status roll-ups. */
|
|
6
6
|
const STATUS_PRIORITY = [
|
|
7
7
|
"rejected",
|
|
@@ -54,7 +54,6 @@ function componentPresence(history, latestBuildId, latest, lastTitle) {
|
|
|
54
54
|
lastSeen: newest.build
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
|
-
//#endregion
|
|
58
57
|
export { componentHistory, componentPresence, worstStatus };
|
|
59
58
|
|
|
60
59
|
//# sourceMappingURL=component.mjs.map
|
|
@@ -5,7 +5,6 @@ import { OAuthClient, OAuthTokens } from "../adapter/oauth.mjs";
|
|
|
5
5
|
import { SecretInput } from "../utils/encrypt.mjs";
|
|
6
6
|
import { UserConnection } from "../schema/connection.mjs";
|
|
7
7
|
import { Table } from "drizzle-orm";
|
|
8
|
-
//#region src/models/connections.d.ts
|
|
9
8
|
/** Tables required by {@link ConnectionModel}. */
|
|
10
9
|
interface ConnectionTables {
|
|
11
10
|
userConnections: Table;
|
|
@@ -36,6 +35,5 @@ declare class ConnectionModel {
|
|
|
36
35
|
accessToken(userId: string, service: string, oauth?: Pick<OAuthClient, "refresh">, now?: Date): Promise<string | null>;
|
|
37
36
|
private refreshOnce;
|
|
38
37
|
}
|
|
39
|
-
//#endregion
|
|
40
38
|
export { ConnectionModel, ConnectionTables };
|
|
41
39
|
//# sourceMappingURL=connections.d.mts.map
|
|
@@ -5,7 +5,7 @@ import { decryptCredential, encrypt } from "../utils/encrypt.mjs";
|
|
|
5
5
|
import { ulid } from "../utils/ulid.mjs";
|
|
6
6
|
import { col } from "./columns.mjs";
|
|
7
7
|
import { and, eq } from "drizzle-orm";
|
|
8
|
-
|
|
8
|
+
// src/models/connections.ts
|
|
9
9
|
/** A user's own OAuth connections to external services: encrypted tokens, lazy refresh on use. */
|
|
10
10
|
/** Refresh this long before expiry so a call never starts with a token about to lapse. */
|
|
11
11
|
const REFRESH_MARGIN_MS = 6e4;
|
|
@@ -106,7 +106,6 @@ var ConnectionModel = class {
|
|
|
106
106
|
function isExpiring(expiresAt, now) {
|
|
107
107
|
return expiresAt !== null && Date.parse(expiresAt) - now.getTime() < REFRESH_MARGIN_MS;
|
|
108
108
|
}
|
|
109
|
-
//#endregion
|
|
110
109
|
export { ConnectionModel };
|
|
111
110
|
|
|
112
111
|
//# sourceMappingURL=connections.mjs.map
|
|
@@ -3,7 +3,6 @@ import __tsdown_shims_url from 'node:url';
|
|
|
3
3
|
import { DatabaseAdapter } from "../adapter/database.mjs";
|
|
4
4
|
import { ContentRef } from "../schema/content-ref.mjs";
|
|
5
5
|
import { Table } from "drizzle-orm";
|
|
6
|
-
//#region src/models/content-ref.d.ts
|
|
7
6
|
/** Content refs for deduplicated Storybook assets. */
|
|
8
7
|
declare class ContentRefModel {
|
|
9
8
|
private readonly tables;
|
|
@@ -16,6 +15,5 @@ declare class ContentRefModel {
|
|
|
16
15
|
decrement(hash: string): Promise<ContentRef | null>;
|
|
17
16
|
listStale(cutoff: string): Promise<ContentRef[]>;
|
|
18
17
|
}
|
|
19
|
-
//#endregion
|
|
20
18
|
export { ContentRefModel };
|
|
21
19
|
//# sourceMappingURL=content-ref.d.mts.map
|