@storyshelf/core 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -32
- package/dist/adapter/capture-queue.d.mts +44 -2
- package/dist/adapter/capture-runner.d.mts +127 -10
- package/dist/adapter/capture-runner.mjs +5 -12
- package/dist/adapter/database.d.mts +27 -8
- package/dist/adapter/database.mjs.map +1 -1
- package/dist/adapter/design.d.mts +85 -0
- package/dist/adapter/design.mjs +28 -0
- package/dist/adapter/design.mjs.map +1 -0
- package/dist/adapter/metadata.d.mts +3 -10
- package/dist/adapter/notifier.d.mts +6 -3
- package/dist/adapter/notifier.mjs +5 -1
- package/dist/adapter/oauth.d.mts +52 -0
- package/dist/adapter/setup.d.mts +4 -5
- package/dist/adapter/setup.mjs +2 -1
- package/dist/adapter/setup.mjs.map +1 -1
- package/dist/adapter/webhook-events.mjs +1 -1
- package/dist/adapters/capture-target-offers.d.mts +28 -0
- package/dist/adapters/capture-target-offers.mjs +58 -0
- package/dist/adapters/capture-target-offers.mjs.map +1 -0
- package/dist/adapters/capture-target.d.mts +71 -0
- package/dist/adapters/capture-target.mjs +76 -0
- package/dist/adapters/capture-target.mjs.map +1 -0
- package/dist/adapters/notifier/build-comment.d.mts +24 -0
- package/dist/adapters/notifier/build-comment.mjs +24 -0
- package/dist/adapters/notifier/build-comment.mjs.map +1 -0
- package/dist/adapters/notifier/build-state.d.mts +99 -0
- package/dist/adapters/notifier/build-state.mjs +218 -0
- package/dist/adapters/notifier/build-state.mjs.map +1 -0
- package/dist/adapters/notifier/events.d.mts +4 -2
- package/dist/adapters/notifier/events.mjs +31 -18
- package/dist/adapters/notifier/events.mjs.map +1 -1
- package/dist/adapters/notifier/provider.d.mts +53 -7
- package/dist/adapters/notifier/provider.mjs +12 -0
- package/dist/adapters/notifier/provider.mjs.map +1 -0
- package/dist/adapters/notifier/state.d.mts +24 -0
- package/dist/adapters/notifier/state.mjs +66 -0
- package/dist/adapters/notifier/state.mjs.map +1 -0
- package/dist/adapters/notifier/types.d.mts +51 -2
- package/dist/adapters/remote-browser.d.mts +87 -0
- package/dist/adapters/remote-browser.mjs +103 -0
- package/dist/adapters/remote-browser.mjs.map +1 -0
- package/dist/adapters/render-runs.d.mts +25 -0
- package/dist/adapters/render-runs.mjs +41 -0
- package/dist/adapters/render-runs.mjs.map +1 -0
- package/dist/adapters/setup-runner.mjs +2 -4
- package/dist/adapters/setup-runner.mjs.map +1 -1
- package/dist/adapters/sources.d.mts +12 -0
- package/dist/adapters/sources.mjs +20 -0
- package/dist/adapters/sources.mjs.map +1 -0
- package/dist/adapters/validate.mjs +25 -18
- package/dist/adapters/validate.mjs.map +1 -1
- package/dist/ai.d.mts +105 -0
- package/dist/ai.mjs +19 -0
- package/dist/ai.mjs.map +1 -0
- package/dist/auth.d.mts +8 -1
- package/dist/auth.mjs +14 -1
- package/dist/auth.mjs.map +1 -0
- package/dist/capture/a11y/axe-source.d.mts +18 -0
- package/dist/capture/a11y/axe-source.mjs +37 -0
- package/dist/capture/a11y/axe-source.mjs.map +1 -0
- package/dist/capture/a11y/in-page.d.mts +27 -0
- package/dist/capture/a11y/in-page.mjs +60 -0
- package/dist/capture/a11y/in-page.mjs.map +1 -0
- package/dist/capture/a11y/map.d.mts +24 -0
- package/dist/capture/a11y/map.mjs +48 -0
- package/dist/capture/a11y/map.mjs.map +1 -0
- package/dist/capture/a11y/options.d.mts +23 -0
- package/dist/capture/a11y/options.mjs +75 -0
- package/dist/capture/a11y/options.mjs.map +1 -0
- package/dist/capture/adapter.d.mts +41 -3
- package/dist/capture/adapter.mjs.map +1 -1
- package/dist/capture/affected.d.mts +2 -0
- package/dist/capture/affected.mjs +13 -4
- package/dist/capture/affected.mjs.map +1 -1
- package/dist/capture/attempt-logger.mjs +84 -0
- package/dist/capture/attempt-logger.mjs.map +1 -0
- package/dist/capture/browsers.d.mts +35 -0
- package/dist/capture/browsers.mjs +67 -0
- package/dist/capture/browsers.mjs.map +1 -0
- package/dist/capture/build-state/deliver.d.mts +32 -0
- package/dist/capture/build-state/deliver.mjs +118 -0
- package/dist/capture/build-state/deliver.mjs.map +1 -0
- package/dist/capture/build-state/index.d.mts +8 -0
- package/dist/capture/build-state/notify-job.d.mts +56 -0
- package/dist/capture/build-state/notify-job.mjs +133 -0
- package/dist/capture/build-state/notify-job.mjs.map +1 -0
- package/dist/capture/build-state/publisher.d.mts +23 -0
- package/dist/capture/build-state/publisher.mjs +107 -0
- package/dist/capture/build-state/publisher.mjs.map +1 -0
- package/dist/capture/build-state/resume.d.mts +9 -0
- package/dist/capture/build-state/resume.mjs +47 -0
- package/dist/capture/build-state/resume.mjs.map +1 -0
- package/dist/capture/build-state/types.d.mts +49 -0
- package/dist/capture/catalog/catalogue.d.mts +22 -0
- package/dist/capture/catalog/catalogue.mjs +53 -0
- package/dist/capture/catalog/catalogue.mjs.map +1 -0
- package/dist/capture/catalog/components.d.mts +23 -0
- package/dist/capture/catalog/components.mjs +55 -0
- package/dist/capture/catalog/components.mjs.map +1 -0
- package/dist/capture/catalog/history.d.mts +30 -0
- package/dist/capture/catalog/history.mjs +51 -0
- package/dist/capture/catalog/history.mjs.map +1 -0
- package/dist/capture/catalog/hook.d.mts +14 -0
- package/dist/capture/catalog/hook.mjs +19 -0
- package/dist/capture/catalog/hook.mjs.map +1 -0
- package/dist/capture/catalog/index.d.mts +11 -0
- package/dist/capture/catalog/load.d.mts +26 -0
- package/dist/capture/catalog/load.mjs +49 -0
- package/dist/capture/catalog/load.mjs.map +1 -0
- package/dist/capture/catalog/parse.d.mts +15 -0
- package/dist/capture/catalog/parse.mjs +56 -0
- package/dist/capture/catalog/parse.mjs.map +1 -0
- package/dist/capture/catalog/read.d.mts +12 -0
- package/dist/capture/catalog/read.mjs +33 -0
- package/dist/capture/catalog/read.mjs.map +1 -0
- package/dist/capture/catalog/types.d.mts +33 -0
- package/dist/capture/consumer/capture-handler.d.mts +58 -0
- package/dist/capture/consumer/capture-handler.mjs +118 -0
- package/dist/capture/consumer/capture-handler.mjs.map +1 -0
- package/dist/capture/consumer/idle.d.mts +17 -0
- package/dist/capture/consumer/idle.mjs +25 -0
- package/dist/capture/consumer/idle.mjs.map +1 -0
- package/dist/capture/consumer/index.d.mts +13 -0
- package/dist/capture/consumer/index.mjs +139 -0
- package/dist/capture/consumer/index.mjs.map +1 -0
- package/dist/capture/consumer/loop.mjs +110 -0
- package/dist/capture/consumer/loop.mjs.map +1 -0
- package/dist/capture/consumer/metrics.d.mts +41 -0
- package/dist/capture/consumer/metrics.mjs +38 -0
- package/dist/capture/consumer/metrics.mjs.map +1 -0
- package/dist/capture/consumer/retry.d.mts +33 -0
- package/dist/capture/consumer/retry.mjs +34 -0
- package/dist/capture/consumer/retry.mjs.map +1 -0
- package/dist/capture/consumer/run-job.mjs +195 -0
- package/dist/capture/consumer/run-job.mjs.map +1 -0
- package/dist/capture/consumer/startup.mjs +38 -0
- package/dist/capture/consumer/startup.mjs.map +1 -0
- package/dist/capture/consumer/types.d.mts +97 -0
- package/dist/capture/crash-failure.d.mts +25 -0
- package/dist/capture/crash-failure.mjs +70 -0
- package/dist/capture/crash-failure.mjs.map +1 -0
- package/dist/capture/db-queue/cadence.d.mts +32 -0
- package/dist/capture/db-queue/cadence.mjs +38 -0
- package/dist/capture/db-queue/cadence.mjs.map +1 -0
- package/dist/capture/db-queue/claim.d.mts +9 -0
- package/dist/capture/db-queue/claim.mjs +132 -0
- package/dist/capture/db-queue/claim.mjs.map +1 -0
- package/dist/capture/db-queue/clock.mjs +12 -0
- package/dist/capture/db-queue/clock.mjs.map +1 -0
- package/dist/capture/db-queue/codec.mjs +76 -0
- package/dist/capture/db-queue/codec.mjs.map +1 -0
- package/dist/capture/db-queue/index.d.mts +20 -0
- package/dist/capture/db-queue/index.mjs +89 -0
- package/dist/capture/db-queue/index.mjs.map +1 -0
- package/dist/capture/db-queue/keeper.d.mts +36 -0
- package/dist/capture/db-queue/keeper.mjs +92 -0
- package/dist/capture/db-queue/keeper.mjs.map +1 -0
- package/dist/capture/db-queue/lease.mjs +46 -0
- package/dist/capture/db-queue/lease.mjs.map +1 -0
- package/dist/capture/db-queue/lifecycle.mjs +36 -0
- package/dist/capture/db-queue/lifecycle.mjs.map +1 -0
- package/dist/capture/db-queue/operations.mjs +94 -0
- package/dist/capture/db-queue/operations.mjs.map +1 -0
- package/dist/capture/db-queue/poll-loop.d.mts +30 -0
- package/dist/capture/db-queue/poll-loop.mjs +89 -0
- package/dist/capture/db-queue/poll-loop.mjs.map +1 -0
- package/dist/capture/db-queue/poll.d.mts +10 -0
- package/dist/capture/db-queue/poll.mjs +69 -0
- package/dist/capture/db-queue/poll.mjs.map +1 -0
- package/dist/capture/db-queue/timers.d.mts +12 -0
- package/dist/capture/db-queue/timers.mjs +37 -0
- package/dist/capture/db-queue/timers.mjs.map +1 -0
- package/dist/capture/db-queue/types.d.mts +83 -0
- package/dist/capture/db-queue/types.mjs +12 -0
- package/dist/capture/db-queue/types.mjs.map +1 -0
- package/dist/capture/db-queue/wake.mjs +48 -0
- package/dist/capture/db-queue/wake.mjs.map +1 -0
- package/dist/capture/design.mjs +39 -0
- package/dist/capture/design.mjs.map +1 -0
- package/dist/capture/diff-record.mjs +23 -3
- package/dist/capture/diff-record.mjs.map +1 -1
- package/dist/capture/dispatch.d.mts +12 -7
- package/dist/capture/dispatch.mjs +86 -107
- package/dist/capture/dispatch.mjs.map +1 -1
- package/dist/capture/enrich.mjs +60 -0
- package/dist/capture/enrich.mjs.map +1 -0
- package/dist/capture/failure-sets.mjs +22 -0
- package/dist/capture/failure-sets.mjs.map +1 -0
- package/dist/capture/fingerprint.d.mts +42 -0
- package/dist/capture/fingerprint.mjs +83 -0
- package/dist/capture/fingerprint.mjs.map +1 -0
- package/dist/capture/forked/channel.d.mts +25 -0
- package/dist/capture/forked/child.d.mts +34 -0
- package/dist/capture/forked/child.mjs +202 -0
- package/dist/capture/forked/child.mjs.map +1 -0
- package/dist/capture/forked/crash-tracker.mjs +23 -0
- package/dist/capture/forked/crash-tracker.mjs.map +1 -0
- package/dist/capture/forked/decision.d.mts +26 -0
- package/dist/capture/forked/decision.mjs +27 -0
- package/dist/capture/forked/decision.mjs.map +1 -0
- package/dist/capture/forked/exit.mjs +12 -0
- package/dist/capture/forked/exit.mjs.map +1 -0
- package/dist/capture/forked/fork.d.mts +31 -0
- package/dist/capture/forked/fork.mjs +93 -0
- package/dist/capture/forked/fork.mjs.map +1 -0
- package/dist/capture/forked/forked-capture.d.mts +40 -0
- package/dist/capture/forked/forked-capture.mjs +240 -0
- package/dist/capture/forked/forked-capture.mjs.map +1 -0
- package/dist/capture/forked/lease-book.mjs +46 -0
- package/dist/capture/forked/lease-book.mjs.map +1 -0
- package/dist/capture/forked/protocol.d.mts +38 -0
- package/dist/capture/forked/protocol.mjs +25 -0
- package/dist/capture/forked/protocol.mjs.map +1 -0
- package/dist/capture/forked/runs.mjs +75 -0
- package/dist/capture/forked/runs.mjs.map +1 -0
- package/dist/capture/isolate.mjs +141 -0
- package/dist/capture/isolate.mjs.map +1 -0
- package/dist/capture/job-lease.d.mts +28 -0
- package/dist/capture/job-lease.mjs +57 -0
- package/dist/capture/job-lease.mjs.map +1 -0
- package/dist/capture/matrix.mjs +298 -0
- package/dist/capture/matrix.mjs.map +1 -0
- package/dist/capture/modes.d.mts +25 -0
- package/dist/capture/modes.mjs +107 -0
- package/dist/capture/modes.mjs.map +1 -0
- package/dist/capture/orchestrator.d.mts +37 -5
- package/dist/capture/orchestrator.mjs +118 -107
- package/dist/capture/orchestrator.mjs.map +1 -1
- package/dist/capture/pipeline.d.mts +9 -2
- package/dist/capture/pipeline.mjs +250 -45
- package/dist/capture/pipeline.mjs.map +1 -1
- package/dist/capture/progress.d.mts +11 -0
- package/dist/capture/progress.mjs +28 -0
- package/dist/capture/progress.mjs.map +1 -0
- package/dist/capture/publish/accept.d.mts +39 -0
- package/dist/capture/publish/accept.mjs +28 -0
- package/dist/capture/publish/accept.mjs.map +1 -0
- package/dist/capture/publish/complete.d.mts +46 -0
- package/dist/capture/publish/complete.mjs +64 -0
- package/dist/capture/publish/complete.mjs.map +1 -0
- package/dist/capture/publish/extract.d.mts +20 -0
- package/dist/capture/publish/extract.mjs +119 -0
- package/dist/capture/publish/extract.mjs.map +1 -0
- package/dist/capture/publish/handler.d.mts +28 -0
- package/dist/capture/publish/handler.mjs +62 -0
- package/dist/capture/publish/handler.mjs.map +1 -0
- package/dist/capture/publish/index.d.mts +10 -0
- package/dist/capture/publish/limits.d.mts +37 -0
- package/dist/capture/publish/limits.mjs +39 -0
- package/dist/capture/publish/limits.mjs.map +1 -0
- package/dist/capture/publish/published.d.mts +15 -0
- package/dist/capture/publish/published.mjs +18 -0
- package/dist/capture/publish/published.mjs.map +1 -0
- package/dist/capture/publish/run.d.mts +33 -0
- package/dist/capture/publish/run.mjs +30 -0
- package/dist/capture/publish/run.mjs.map +1 -0
- package/dist/capture/rediff.d.mts +1 -1
- package/dist/capture/rediff.mjs +4 -3
- package/dist/capture/rediff.mjs.map +1 -1
- package/dist/capture/render.mjs +53 -0
- package/dist/capture/render.mjs.map +1 -0
- package/dist/capture/scratch.d.mts +44 -0
- package/dist/capture/scratch.mjs +138 -0
- package/dist/capture/scratch.mjs.map +1 -0
- package/dist/capture/shard-plan.mjs +128 -0
- package/dist/capture/shard-plan.mjs.map +1 -0
- package/dist/capture/shards/context.mjs +51 -0
- package/dist/capture/shards/context.mjs.map +1 -0
- package/dist/capture/shards/execute.mjs +174 -0
- package/dist/capture/shards/execute.mjs.map +1 -0
- package/dist/capture/shards/fan-out.mjs +126 -0
- package/dist/capture/shards/fan-out.mjs.map +1 -0
- package/dist/capture/shards/history.mjs +35 -0
- package/dist/capture/shards/history.mjs.map +1 -0
- package/dist/capture/shards/reduce.mjs +97 -0
- package/dist/capture/shards/reduce.mjs.map +1 -0
- package/dist/capture/sizing.mjs +1 -11
- package/dist/capture/sizing.mjs.map +1 -1
- package/dist/capture/skip-checks.mjs +2 -65
- package/dist/capture/skip-checks.mjs.map +1 -1
- package/dist/capture/statics.d.mts +7 -8
- package/dist/capture/statics.mjs +25 -71
- package/dist/capture/statics.mjs.map +1 -1
- package/dist/capture/storybook-urls.d.mts +40 -0
- package/dist/capture/storybook-urls.mjs +41 -0
- package/dist/capture/storybook-urls.mjs.map +1 -0
- package/dist/capture/storybook.d.mts +12 -4
- package/dist/capture/storybook.mjs +56 -16
- package/dist/capture/storybook.mjs.map +1 -1
- package/dist/capture/target.mjs +21 -0
- package/dist/capture/target.mjs.map +1 -0
- package/dist/capture/targets.d.mts +25 -0
- package/dist/capture/targets.mjs +32 -0
- package/dist/capture/targets.mjs.map +1 -0
- package/dist/capture/tests.d.mts +37 -0
- package/dist/capture/tests.mjs +126 -0
- package/dist/capture/tests.mjs.map +1 -0
- package/dist/capture/token-chunks.mjs +74 -0
- package/dist/capture/token-chunks.mjs.map +1 -0
- package/dist/capture.d.mts +60 -7
- package/dist/capture.mjs +48 -3
- package/dist/config.d.mts +121 -12
- package/dist/config.mjs +9 -6
- package/dist/config.mjs.map +1 -1
- package/dist/diff.d.mts +9 -3
- package/dist/diff.mjs +16 -3
- package/dist/diff.mjs.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/insights/budget.d.mts +20 -0
- package/dist/insights/budget.mjs +33 -0
- package/dist/insights/budget.mjs.map +1 -0
- package/dist/insights/envelope.d.mts +59 -0
- package/dist/insights/envelope.mjs +60 -0
- package/dist/insights/envelope.mjs.map +1 -0
- package/dist/insights/evidence-health.d.mts +32 -0
- package/dist/insights/evidence-health.mjs +28 -0
- package/dist/insights/evidence-health.mjs.map +1 -0
- package/dist/insights/evidence.d.mts +55 -0
- package/dist/insights/evidence.mjs +64 -0
- package/dist/insights/evidence.mjs.map +1 -0
- package/dist/insights/hash.d.mts +18 -0
- package/dist/insights/hash.mjs +21 -0
- package/dist/insights/hash.mjs.map +1 -0
- package/dist/insights/limits.d.mts +24 -0
- package/dist/insights/limits.mjs +24 -0
- package/dist/insights/limits.mjs.map +1 -0
- package/dist/insights/prompts/v1.d.mts +13 -0
- package/dist/insights/prompts/v1.mjs +34 -0
- package/dist/insights/prompts/v1.mjs.map +1 -0
- package/dist/insights/redact.d.mts +9 -0
- package/dist/insights/redact.mjs +24 -0
- package/dist/insights/redact.mjs.map +1 -0
- package/dist/insights/text.mjs +44 -0
- package/dist/insights/text.mjs.map +1 -0
- package/dist/insights.d.mts +11 -0
- package/dist/insights.mjs +12 -0
- package/dist/logger.mjs +17 -1
- package/dist/logger.mjs.map +1 -1
- package/dist/models/ai-budget-alert.d.mts +23 -0
- package/dist/models/ai-budget-alert.mjs +38 -0
- package/dist/models/ai-budget-alert.mjs.map +1 -0
- package/dist/models/ai-usage.d.mts +40 -0
- package/dist/models/ai-usage.mjs +61 -0
- package/dist/models/ai-usage.mjs.map +1 -0
- package/dist/models/baseline-archive.d.mts +84 -0
- package/dist/models/baseline-archive.mjs +106 -0
- package/dist/models/baseline-archive.mjs.map +1 -0
- package/dist/models/baseline-export.d.mts +19 -0
- package/dist/models/baseline-export.mjs +136 -0
- package/dist/models/baseline-export.mjs.map +1 -0
- package/dist/models/baseline-import.mjs +118 -0
- package/dist/models/baseline-import.mjs.map +1 -0
- package/dist/models/baseline-transfer.d.mts +49 -0
- package/dist/models/baseline-transfer.mjs +121 -0
- package/dist/models/baseline-transfer.mjs.map +1 -0
- package/dist/models/baseline.d.mts +32 -5
- package/dist/models/baseline.mjs +65 -20
- package/dist/models/baseline.mjs.map +1 -1
- package/dist/models/build-component.d.mts +28 -0
- package/dist/models/build-component.mjs +55 -0
- package/dist/models/build-component.mjs.map +1 -0
- package/dist/models/build-review-status.d.mts +29 -0
- package/dist/models/build-review-status.mjs +37 -0
- package/dist/models/build-review-status.mjs.map +1 -0
- package/dist/models/build-review.d.mts +28 -0
- package/dist/models/build-review.mjs +54 -0
- package/dist/models/build-review.mjs.map +1 -0
- package/dist/models/build.d.mts +25 -3
- package/dist/models/build.mjs +50 -11
- package/dist/models/build.mjs.map +1 -1
- package/dist/models/capture-attempt.d.mts +7 -1
- package/dist/models/capture-attempt.mjs +29 -4
- package/dist/models/capture-attempt.mjs.map +1 -1
- package/dist/models/capture-job.d.mts +106 -0
- package/dist/models/capture-job.mjs +288 -0
- package/dist/models/capture-job.mjs.map +1 -0
- package/dist/models/capture-log.d.mts +1 -1
- package/dist/models/capture-log.mjs +4 -3
- package/dist/models/capture-log.mjs.map +1 -1
- package/dist/models/capture-shard.d.mts +62 -0
- package/dist/models/capture-shard.mjs +190 -0
- package/dist/models/capture-shard.mjs.map +1 -0
- package/dist/models/changeset.d.mts +48 -0
- package/dist/models/changeset.mjs +81 -0
- package/dist/models/changeset.mjs.map +1 -0
- package/dist/models/columns.mjs +19 -0
- package/dist/models/columns.mjs.map +1 -0
- package/dist/models/comment.d.mts +4 -1
- package/dist/models/comment.mjs +7 -4
- package/dist/models/comment.mjs.map +1 -1
- package/dist/models/component.d.mts +43 -0
- package/dist/models/component.mjs +60 -0
- package/dist/models/component.mjs.map +1 -0
- package/dist/models/connections.d.mts +41 -0
- package/dist/models/connections.mjs +112 -0
- package/dist/models/connections.mjs.map +1 -0
- package/dist/models/content-ref.d.mts +1 -1
- package/dist/models/content-ref.mjs +1 -1
- package/dist/models/content-ref.mjs.map +1 -1
- package/dist/models/credentials.mjs +12 -2
- package/dist/models/credentials.mjs.map +1 -1
- package/dist/models/design-credentials.d.mts +41 -0
- package/dist/models/design-credentials.mjs +86 -0
- package/dist/models/design-credentials.mjs.map +1 -0
- package/dist/models/design-health.d.mts +16 -0
- package/dist/models/design-health.mjs +25 -0
- package/dist/models/design-health.mjs.map +1 -0
- package/dist/models/design-nodes.d.mts +83 -0
- package/dist/models/design-nodes.mjs +222 -0
- package/dist/models/design-nodes.mjs.map +1 -0
- package/dist/models/design-renders.mjs +74 -0
- package/dist/models/design-renders.mjs.map +1 -0
- package/dist/models/design-resolve.d.mts +29 -0
- package/dist/models/design-resolve.mjs +41 -0
- package/dist/models/design-resolve.mjs.map +1 -0
- package/dist/models/design.d.mts +39 -0
- package/dist/models/design.mjs +90 -0
- package/dist/models/design.mjs.map +1 -0
- package/dist/models/inherited-ratio.d.mts +21 -0
- package/dist/models/inherited-ratio.mjs +22 -0
- package/dist/models/inherited-ratio.mjs.map +1 -0
- package/dist/models/insight.d.mts +70 -0
- package/dist/models/insight.mjs +162 -0
- package/dist/models/insight.mjs.map +1 -0
- package/dist/models/label.d.mts +4 -2
- package/dist/models/label.mjs +25 -10
- package/dist/models/label.mjs.map +1 -1
- package/dist/models/member.d.mts +1 -1
- package/dist/models/member.mjs +5 -4
- package/dist/models/member.mjs.map +1 -1
- package/dist/models/notification-channel.d.mts +37 -3
- package/dist/models/notification-channel.mjs +88 -9
- package/dist/models/notification-channel.mjs.map +1 -1
- package/dist/models/notification-subscription.d.mts +1 -2
- package/dist/models/notification-subscription.mjs +7 -9
- package/dist/models/notification-subscription.mjs.map +1 -1
- package/dist/models/project-group-mapping.d.mts +6 -1
- package/dist/models/project-group-mapping.mjs +14 -4
- package/dist/models/project-group-mapping.mjs.map +1 -1
- package/dist/models/project.d.mts +9 -1
- package/dist/models/project.mjs +9 -4
- package/dist/models/project.mjs.map +1 -1
- package/dist/models/public-origin.d.mts +63 -0
- package/dist/models/public-origin.mjs +114 -0
- package/dist/models/public-origin.mjs.map +1 -0
- package/dist/models/review-switch-off.d.mts +14 -0
- package/dist/models/review-switch-off.mjs +26 -0
- package/dist/models/review-switch-off.mjs.map +1 -0
- package/dist/models/site-settings.d.mts +23 -0
- package/dist/models/site-settings.mjs +48 -0
- package/dist/models/site-settings.mjs.map +1 -0
- package/dist/models/snapshot.d.mts +36 -1
- package/dist/models/snapshot.mjs +50 -3
- package/dist/models/snapshot.mjs.map +1 -1
- package/dist/models/token.d.mts +13 -1
- package/dist/models/token.mjs +23 -5
- package/dist/models/token.mjs.map +1 -1
- package/dist/models/usage.d.mts +76 -0
- package/dist/models/usage.mjs +149 -0
- package/dist/models/usage.mjs.map +1 -0
- package/dist/models/user.d.mts +1 -1
- package/dist/models/user.mjs +4 -3
- package/dist/models/user.mjs.map +1 -1
- package/dist/models/webhook.d.mts +15 -1
- package/dist/models/webhook.mjs +33 -4
- package/dist/models/webhook.mjs.map +1 -1
- package/dist/models.d.mts +26 -3
- package/dist/models.mjs +27 -5
- package/dist/options.d.mts +18 -0
- package/dist/options.mjs +90 -0
- package/dist/options.mjs.map +1 -0
- package/dist/paths.d.mts +19 -4
- package/dist/paths.mjs +51 -10
- package/dist/paths.mjs.map +1 -1
- package/dist/retention/ai.d.mts +18 -0
- package/dist/retention/ai.mjs +23 -0
- package/dist/retention/ai.mjs.map +1 -0
- package/dist/retention/pending.mjs +47 -0
- package/dist/retention/pending.mjs.map +1 -0
- package/dist/retention/screenshot-sweep.d.mts +51 -0
- package/dist/retention/screenshot-sweep.mjs +128 -0
- package/dist/retention/screenshot-sweep.mjs.map +1 -0
- package/dist/retention.d.mts +16 -2
- package/dist/retention.mjs +71 -14
- package/dist/retention.mjs.map +1 -1
- package/dist/schema/baseline.d.mts +6 -0
- package/dist/schema/build-component.d.mts +25 -0
- package/dist/schema/build-review.d.mts +17 -0
- package/dist/schema/build.d.mts +7 -0
- package/dist/schema/capture-job.d.mts +60 -0
- package/dist/schema/capture-shard.d.mts +35 -0
- package/dist/schema/comment.d.mts +2 -0
- package/dist/schema/connection.d.mts +24 -0
- package/dist/schema/design.d.mts +58 -0
- package/dist/schema/insight.d.mts +54 -0
- package/dist/schema/notification.d.mts +4 -0
- package/dist/schema/project.d.mts +21 -0
- package/dist/schema/site-setting.d.mts +21 -0
- package/dist/schema/snapshot.d.mts +12 -0
- package/dist/schema/usage.d.mts +30 -0
- package/dist/schema.d.mts +11 -3
- package/dist/serverless.d.mts +21 -0
- package/dist/serverless.mjs +37 -0
- package/dist/serverless.mjs.map +1 -0
- package/dist/test-helpers/create-project.d.mts +1 -1
- package/dist/test-helpers/deferred.d.mts +39 -0
- package/dist/test-helpers/deferred.mjs +61 -0
- package/dist/test-helpers/deferred.mjs.map +1 -0
- package/dist/test-helpers/design-contract.d.mts +21 -0
- package/dist/test-helpers/design-contract.mjs +40 -0
- package/dist/test-helpers/design-contract.mjs.map +1 -0
- package/dist/test-helpers/fake-database.mjs +14 -2
- package/dist/test-helpers/fake-database.mjs.map +1 -1
- package/dist/test-helpers/fake-design.d.mts +43 -0
- package/dist/test-helpers/fake-design.mjs +114 -0
- package/dist/test-helpers/fake-design.mjs.map +1 -0
- package/dist/test-helpers/fake-notifier.d.mts +21 -0
- package/dist/test-helpers/fake-notifier.mjs +44 -0
- package/dist/test-helpers/fake-notifier.mjs.map +1 -0
- package/dist/test-helpers/fake-tables.mjs +255 -67
- package/dist/test-helpers/fake-tables.mjs.map +1 -1
- package/dist/test-helpers/manual-clock.d.mts +27 -0
- package/dist/test-helpers/manual-clock.mjs +56 -0
- package/dist/test-helpers/manual-clock.mjs.map +1 -0
- package/dist/test-helpers/notifier-contract.d.mts +7 -1
- package/dist/test-helpers/notifier-contract.mjs +32 -3
- package/dist/test-helpers/notifier-contract.mjs.map +1 -1
- package/dist/test-helpers/queue-contract.d.mts +16 -2
- package/dist/test-helpers/queue-contract.mjs +195 -1
- package/dist/test-helpers/queue-contract.mjs.map +1 -1
- package/dist/test-helpers/queue-multi-instance.d.mts +20 -0
- package/dist/test-helpers/queue-multi-instance.mjs +206 -0
- package/dist/test-helpers/queue-multi-instance.mjs.map +1 -0
- package/dist/test-helpers/runner-contract.mjs +11 -0
- package/dist/test-helpers/runner-contract.mjs.map +1 -1
- package/dist/test-helpers/sql-chunks.mjs +23 -0
- package/dist/test-helpers/sql-chunks.mjs.map +1 -1
- package/dist/test-helpers/storage-contract.mjs +1 -1
- package/dist/test-helpers.d.mts +9 -4
- package/dist/test-helpers.mjs +8 -3
- package/dist/types.d.mts +14 -3
- package/dist/types.mjs +21 -4
- package/dist/types.mjs.map +1 -1
- package/dist/urls.d.mts +4 -0
- package/dist/urls.mjs +11 -2
- package/dist/urls.mjs.map +1 -1
- package/dist/utils/adaptive-concurrency.d.mts +66 -0
- package/dist/utils/adaptive-concurrency.mjs +149 -0
- package/dist/utils/adaptive-concurrency.mjs.map +1 -0
- package/dist/utils/connect-retry.d.mts +26 -0
- package/dist/utils/connect-retry.mjs +43 -0
- package/dist/utils/connect-retry.mjs.map +1 -0
- package/dist/utils/environment.d.mts +19 -0
- package/dist/utils/environment.mjs +59 -0
- package/dist/utils/environment.mjs.map +1 -0
- package/dist/utils/http.d.mts +17 -1
- package/dist/utils/http.mjs +22 -3
- package/dist/utils/http.mjs.map +1 -1
- package/dist/utils/map-adaptive.d.mts +15 -0
- package/dist/utils/map-adaptive.mjs +72 -0
- package/dist/utils/map-adaptive.mjs.map +1 -0
- package/dist/utils/map-limit.d.mts +10 -0
- package/dist/utils/map-limit.mjs +32 -0
- package/dist/utils/map-limit.mjs.map +1 -0
- package/dist/utils/page-limiter.d.mts +37 -0
- package/dist/utils/page-limiter.mjs +56 -0
- package/dist/utils/page-limiter.mjs.map +1 -0
- package/dist/utils/public-origin.d.mts +20 -0
- package/dist/utils/public-origin.mjs +75 -0
- package/dist/utils/public-origin.mjs.map +1 -0
- package/dist/utils/redact.mjs +3 -0
- package/dist/utils/redact.mjs.map +1 -1
- package/dist/utils/remote-environment.d.mts +39 -0
- package/dist/utils/remote-environment.mjs +114 -0
- package/dist/utils/remote-environment.mjs.map +1 -0
- package/dist/utils/resources.d.mts +64 -0
- package/dist/utils/resources.mjs +122 -0
- package/dist/utils/resources.mjs.map +1 -0
- package/dist/utils/screenshot-store.d.mts +15 -0
- package/dist/utils/screenshot-store.mjs +41 -0
- package/dist/utils/screenshot-store.mjs.map +1 -0
- package/dist/utils/session-gate.d.mts +22 -0
- package/dist/utils/session-gate.mjs +53 -0
- package/dist/utils/session-gate.mjs.map +1 -0
- package/dist/utils/storybook-token.d.mts +81 -0
- package/dist/utils/storybook-token.mjs +134 -0
- package/dist/utils/storybook-token.mjs.map +1 -0
- package/dist/utils/storybook-url.d.mts +15 -0
- package/dist/utils/storybook-url.mjs +21 -0
- package/dist/utils/storybook-url.mjs.map +1 -0
- package/dist/utils/timing.mjs +1 -1
- package/dist/utils/timing.mjs.map +1 -1
- package/dist/utils/zip-read.mjs +129 -0
- package/dist/utils/zip-read.mjs.map +1 -0
- package/dist/utils/zip-write.d.mts +18 -0
- package/dist/utils/zip-write.mjs +188 -0
- package/dist/utils/zip-write.mjs.map +1 -0
- package/dist/utils.d.mts +17 -3
- package/dist/utils.mjs +18 -4
- package/package.json +32 -20
- package/dist/adapter/capture-runner.mjs.map +0 -1
- package/dist/adapter/git-host/comments.d.mts +0 -41
- package/dist/adapter/git-host/comments.mjs +0 -54
- package/dist/adapter/git-host/comments.mjs.map +0 -1
- package/dist/adapter/git-host/helpers.d.mts +0 -13
- package/dist/adapter/git-host/helpers.mjs +0 -30
- package/dist/adapter/git-host/helpers.mjs.map +0 -1
- package/dist/adapter/git-host.d.mts +0 -42
- package/dist/capture/queue.d.mts +0 -38
- package/dist/capture/queue.mjs +0 -98
- package/dist/capture/queue.mjs.map +0 -1
- package/dist/capture/recovery.d.mts +0 -21
- package/dist/capture/recovery.mjs +0 -83
- package/dist/capture/recovery.mjs.map +0 -1
- package/dist/capture/status-fanout.mjs +0 -72
- package/dist/capture/status-fanout.mjs.map +0 -1
- package/dist/diff/engine.d.mts +0 -16
- package/dist/diff/engine.mjs +0 -87
- package/dist/diff/engine.mjs.map +0 -1
- package/dist/diff/options.d.mts +0 -42
- package/dist/diff/options.mjs +0 -15
- package/dist/diff/options.mjs.map +0 -1
- package/dist/models/status-config.d.mts +0 -38
- package/dist/models/status-config.mjs +0 -63
- package/dist/models/status-config.mjs.map +0 -1
- package/dist/schema/status-config.d.mts +0 -16
- package/dist/test-helpers/git-contract.d.mts +0 -11
- package/dist/test-helpers/git-contract.mjs +0 -34
- package/dist/test-helpers/git-contract.mjs.map +0 -1
- /package/dist/adapter/{git-host.mjs → oauth.mjs} +0 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
//#region src/adapters/notifier/build-comment.ts
|
|
5
|
+
const LABEL = {
|
|
6
|
+
testing: "Visual tests",
|
|
7
|
+
review: "Review",
|
|
8
|
+
storybook: "Storybook"
|
|
9
|
+
};
|
|
10
|
+
/** Comment body (below the idempotency marker): links to the build, the Storybook and a summary. */
|
|
11
|
+
function buildCommentMarkdown(input) {
|
|
12
|
+
const lines = [`**StoryShelf** — [View build](${input.buildUrl})`];
|
|
13
|
+
for (const gate of input.gates) lines.push(`- ${LABEL[gate.name]}: ${gate.description}`);
|
|
14
|
+
if (input.counts !== void 0 && input.counts.snapshotCount > 0) {
|
|
15
|
+
const { snapshotCount, changedCount, approvedCount, rejectedCount } = input.counts;
|
|
16
|
+
lines.push(`- Snapshots: ${String(snapshotCount)} total, ${String(changedCount)} changed, ${String(approvedCount)} approved, ${String(rejectedCount)} rejected`);
|
|
17
|
+
}
|
|
18
|
+
if (input.storybookUrl !== void 0) lines.push(`- Storybook: [Open the published Storybook](${input.storybookUrl})`);
|
|
19
|
+
return lines.join("\n");
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { buildCommentMarkdown };
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=build-comment.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-comment.mjs","names":[],"sources":["../../../src/adapters/notifier/build-comment.ts"],"sourcesContent":["/** Markdown of the pull-request comment, derived from the same inputs as the statuses (ADR 0014, decision 6). */\nimport type { DesiredStatus } from \"./build-state.ts\";\n\n/** Inputs of {@link buildCommentMarkdown}. */\nexport interface BuildCommentInput {\n buildUrl: string;\n /** Present once the Storybook is published. */\n storybookUrl?: string | undefined;\n /** The gating statuses (`testing` or `review`), in display order. */\n gates: readonly DesiredStatus[];\n /** Present when `testing` or review is on: the snapshot totals to summarize. */\n counts?:\n | {\n snapshotCount: number;\n changedCount: number;\n approvedCount: number;\n rejectedCount: number;\n }\n | undefined;\n}\n\nconst LABEL: Record<DesiredStatus[\"name\"], string> = {\n testing: \"Visual tests\",\n review: \"Review\",\n storybook: \"Storybook\",\n};\n\n/** Comment body (below the idempotency marker): links to the build, the Storybook and a summary. */\nexport function buildCommentMarkdown(input: BuildCommentInput): string {\n const lines = [`**StoryShelf** — [View build](${input.buildUrl})`];\n for (const gate of input.gates) {\n lines.push(`- ${LABEL[gate.name]}: ${gate.description}`);\n }\n if (input.counts !== undefined && input.counts.snapshotCount > 0) {\n const { snapshotCount, changedCount, approvedCount, rejectedCount } = input.counts;\n lines.push(\n `- Snapshots: ${String(snapshotCount)} total, ${String(changedCount)} changed, ${String(approvedCount)} approved, ${String(rejectedCount)} rejected`,\n );\n }\n if (input.storybookUrl !== undefined) {\n lines.push(`- Storybook: [Open the published Storybook](${input.storybookUrl})`);\n }\n return lines.join(\"\\n\");\n}\n"],"mappings":";;;;AAqBA,MAAM,QAA+C;CACnD,SAAS;CACT,QAAQ;CACR,WAAW;AACb;;AAGA,SAAgB,qBAAqB,OAAkC;CACrE,MAAM,QAAQ,CAAC,iCAAiC,MAAM,SAAS,EAAE;CACjE,KAAK,MAAM,QAAQ,MAAM,OACvB,MAAM,KAAK,KAAK,MAAM,KAAK,MAAM,IAAI,KAAK,aAAa;CAEzD,IAAI,MAAM,WAAW,KAAA,KAAa,MAAM,OAAO,gBAAgB,GAAG;EAChE,MAAM,EAAE,eAAe,cAAc,eAAe,kBAAkB,MAAM;EAC5E,MAAM,KACJ,gBAAgB,OAAO,aAAa,EAAE,UAAU,OAAO,YAAY,EAAE,YAAY,OAAO,aAAa,EAAE,aAAa,OAAO,aAAa,EAAE,UAC5I;CACF;CACA,IAAI,MAAM,iBAAiB,KAAA,GACzB,MAAM,KAAK,+CAA+C,MAAM,aAAa,EAAE;CAEjF,OAAO,MAAM,KAAK,IAAI;AACxB"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { NotifierState, StateAnnotation } from "./types.mjs";
|
|
4
|
+
import { UrlBuilder } from "../../urls.mjs";
|
|
5
|
+
//#region src/adapters/notifier/build-state.d.ts
|
|
6
|
+
/** Most per-snapshot annotations a build state carries; the rest are counted (`omitted`). */
|
|
7
|
+
declare const MAX_ANNOTATIONS = 200;
|
|
8
|
+
/** Names of the statuses below a channel's prefix. */
|
|
9
|
+
type StatusName = "testing" | "review" | "storybook";
|
|
10
|
+
/** How publishing the build's Storybook stands. */
|
|
11
|
+
type PublishingState = "pending" | "success" | "failure";
|
|
12
|
+
/** What decides which statuses exist (ADR 0014, decision 5). */
|
|
13
|
+
interface BuildStateFlags {
|
|
14
|
+
/** `testing` is on: `<prefix>/testing` is the merge gate. */
|
|
15
|
+
testing: boolean;
|
|
16
|
+
/** Review is on for the project: with `testing` off, `<prefix>/review` is the gate. */
|
|
17
|
+
review: boolean;
|
|
18
|
+
}
|
|
19
|
+
/** The part of a build row the function reads. */
|
|
20
|
+
interface BuildStateBuild {
|
|
21
|
+
id: string;
|
|
22
|
+
gitSha: string;
|
|
23
|
+
/** Build status: `pending`, `capturing`, `comparing`, `reviewing`, `approved`, `rejected`, `failed`, `cancelled`. */
|
|
24
|
+
status: string;
|
|
25
|
+
snapshotCount: number;
|
|
26
|
+
changedCount: number;
|
|
27
|
+
approvedCount: number;
|
|
28
|
+
rejectedCount: number;
|
|
29
|
+
}
|
|
30
|
+
/** The part of a snapshot row the annotations read. */
|
|
31
|
+
interface BuildStateChange {
|
|
32
|
+
id: string;
|
|
33
|
+
/** `changed` or `new`; anything else is not a change to review. */
|
|
34
|
+
status: string;
|
|
35
|
+
storyTitle: string;
|
|
36
|
+
storyName: string;
|
|
37
|
+
storyImportPath: string | null;
|
|
38
|
+
viewportName: string;
|
|
39
|
+
browser?: string | undefined;
|
|
40
|
+
}
|
|
41
|
+
/** Inputs of {@link statusesForBuild}. */
|
|
42
|
+
interface BuildStateInput {
|
|
43
|
+
build: BuildStateBuild;
|
|
44
|
+
projectSlug: string;
|
|
45
|
+
/** The channel's status-name prefix, without a trailing slash. */
|
|
46
|
+
prefix: string;
|
|
47
|
+
/**
|
|
48
|
+
* Absolute links (`createUrlBuilder(publicOrigin)`). A build's state is only ever shown once a
|
|
49
|
+
* public origin is resolved (ADR 0014, decision 11), so every status links and the comment exists.
|
|
50
|
+
*/
|
|
51
|
+
urls: Pick<UrlBuilder, "build" | "storybookBuild" | "snapshot">;
|
|
52
|
+
flags: BuildStateFlags;
|
|
53
|
+
publishing: PublishingState;
|
|
54
|
+
/**
|
|
55
|
+
* Approval progress of the build-level review: the approvals among each reviewer's latest
|
|
56
|
+
* verdict and the approvals the project requires. Shown on `<prefix>/review` while it waits.
|
|
57
|
+
*/
|
|
58
|
+
approvals?: {
|
|
59
|
+
given: number;
|
|
60
|
+
required: number;
|
|
61
|
+
} | undefined;
|
|
62
|
+
/** The build's snapshots; the `changed` and `new` ones become annotations of `<prefix>/testing`. */
|
|
63
|
+
changes?: readonly BuildStateChange[] | undefined;
|
|
64
|
+
}
|
|
65
|
+
/** One commit status to show. */
|
|
66
|
+
interface DesiredStatus {
|
|
67
|
+
name: StatusName;
|
|
68
|
+
/** Full name on the host, `<prefix>/<name>`. */
|
|
69
|
+
context: string;
|
|
70
|
+
state: NotifierState;
|
|
71
|
+
description: string;
|
|
72
|
+
/** What the status links to: the build page, or the published Storybook for `storybook`. */
|
|
73
|
+
url: string;
|
|
74
|
+
/** True for the Storybook status, which is never a gate. */
|
|
75
|
+
informational?: boolean;
|
|
76
|
+
/** Set on a `pending` status: `queued` before capture starts, else `running`. */
|
|
77
|
+
progress?: "queued" | "running";
|
|
78
|
+
/** Per-snapshot findings (only `testing`), at most {@link MAX_ANNOTATIONS}. */
|
|
79
|
+
annotations?: StateAnnotation[];
|
|
80
|
+
/** Changes beyond the cap. */
|
|
81
|
+
omitted?: number;
|
|
82
|
+
}
|
|
83
|
+
/** The pull-request comment to show. */
|
|
84
|
+
interface DesiredComment {
|
|
85
|
+
/** The build page; it keys the comment, one per build. */
|
|
86
|
+
url: string;
|
|
87
|
+
markdown: string;
|
|
88
|
+
state: NotifierState;
|
|
89
|
+
}
|
|
90
|
+
/** Everything a channel should show for a build. */
|
|
91
|
+
interface BuildStateSet {
|
|
92
|
+
statuses: DesiredStatus[];
|
|
93
|
+
comment: DesiredComment | null;
|
|
94
|
+
}
|
|
95
|
+
/** Statuses and comment for a build; empty without a real commit (no SHA or a local identity). */
|
|
96
|
+
declare function statusesForBuild(input: BuildStateInput): BuildStateSet;
|
|
97
|
+
//#endregion
|
|
98
|
+
export { BuildStateBuild, BuildStateChange, BuildStateFlags, BuildStateInput, BuildStateSet, DesiredComment, DesiredStatus, MAX_ANNOTATIONS, PublishingState, StatusName, statusesForBuild };
|
|
99
|
+
//# sourceMappingURL=build-state.d.mts.map
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { buildCommentMarkdown } from "./build-comment.mjs";
|
|
5
|
+
import { LOCAL_SHA_PREFIX } from "@storyshelf/affected";
|
|
6
|
+
//#region src/adapters/notifier/build-state.ts
|
|
7
|
+
/**
|
|
8
|
+
* The states a build's git channels show, as one pure function of the build (ADR 0014, decision 5).
|
|
9
|
+
* It reads no clock, database or provider: callers hand it the build, the channel's prefix and the
|
|
10
|
+
* flags, and write the result to the host. Every transition and the end of publishing call it
|
|
11
|
+
* again, so a build that was never captured still gets its statuses and a repeated call changes
|
|
12
|
+
* nothing.
|
|
13
|
+
*/
|
|
14
|
+
/** Most per-snapshot annotations a build state carries; the rest are counted (`omitted`). */
|
|
15
|
+
const MAX_ANNOTATIONS = 200;
|
|
16
|
+
/** Statuses and comment for a build; empty without a real commit (no SHA or a local identity). */
|
|
17
|
+
function statusesForBuild(input) {
|
|
18
|
+
const { build } = input;
|
|
19
|
+
if (build.gitSha.length === 0 || build.gitSha.startsWith(LOCAL_SHA_PREFIX)) return {
|
|
20
|
+
statuses: [],
|
|
21
|
+
comment: null
|
|
22
|
+
};
|
|
23
|
+
const links = linksOf(input);
|
|
24
|
+
const statuses = [...withAnnotations(gateStatus(input, links.build), input), storybookStatus(input.publishing, input.prefix, links)].map((status) => withProgress(status, input.build));
|
|
25
|
+
return {
|
|
26
|
+
statuses,
|
|
27
|
+
comment: commentOf(input, statuses, links)
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** `pending` is `queued` while the build waits to be captured, `running` after. */
|
|
31
|
+
function withProgress(status, build) {
|
|
32
|
+
if (status.state !== "pending") return status;
|
|
33
|
+
const queued = status.name !== "storybook" && build.status === "pending";
|
|
34
|
+
return {
|
|
35
|
+
...status,
|
|
36
|
+
progress: queued ? "queued" : "running"
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** One annotation per changed snapshot, on the `testing` gate. */
|
|
40
|
+
function withAnnotations(gates, input) {
|
|
41
|
+
const changed = (input.changes ?? []).filter((c) => c.status === "changed" || c.status === "new");
|
|
42
|
+
if (changed.length === 0) return gates;
|
|
43
|
+
const annotations = changed.slice(0, 200).map((change) => annotationOf(change, input));
|
|
44
|
+
const omitted = changed.length - annotations.length;
|
|
45
|
+
return gates.map((gate) => gate.name === "testing" ? {
|
|
46
|
+
...gate,
|
|
47
|
+
annotations,
|
|
48
|
+
...omitted > 0 ? { omitted } : {}
|
|
49
|
+
} : gate);
|
|
50
|
+
}
|
|
51
|
+
function annotationOf(change, input) {
|
|
52
|
+
const where = [change.viewportName, change.browser].filter(Boolean).join(", ");
|
|
53
|
+
const isNew = change.status === "new";
|
|
54
|
+
const name = `${change.storyTitle} / ${change.storyName}`;
|
|
55
|
+
return {
|
|
56
|
+
path: change.storyImportPath?.replace(/^(?:\.\/)+/u, "") ?? ".storybook",
|
|
57
|
+
title: where === "" ? name : `${name} (${where})`,
|
|
58
|
+
message: `${isNew ? "New snapshot" : "Visual change"}: review it at ${input.urls.snapshot(input.projectSlug, input.build.id, change.id)}`,
|
|
59
|
+
url: input.urls.snapshot(input.projectSlug, input.build.id, change.id),
|
|
60
|
+
level: isNew ? "notice" : "warning"
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function commentOf(input, statuses, links) {
|
|
64
|
+
const gates = statuses.filter((status) => status.name !== "storybook");
|
|
65
|
+
return {
|
|
66
|
+
url: links.build,
|
|
67
|
+
state: gates[0]?.state ?? "success",
|
|
68
|
+
markdown: buildCommentMarkdown({
|
|
69
|
+
buildUrl: links.build,
|
|
70
|
+
storybookUrl: input.publishing === "success" ? links.storybook : void 0,
|
|
71
|
+
gates,
|
|
72
|
+
counts: input.flags.testing || input.flags.review ? input.build : void 0
|
|
73
|
+
})
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/** The links to show. */
|
|
77
|
+
function linksOf(input) {
|
|
78
|
+
const { urls, projectSlug, build } = input;
|
|
79
|
+
return {
|
|
80
|
+
build: urls.build(projectSlug, build.id),
|
|
81
|
+
storybook: `${urls.storybookBuild(projectSlug, build.id).replace(/\/+$/u, "")}/`
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/** `testing` when it is on; `review` when only review is on; none for a hosting-only build. */
|
|
85
|
+
function gateStatus(input, url) {
|
|
86
|
+
const { flags, build, prefix } = input;
|
|
87
|
+
if (flags.testing) return [{
|
|
88
|
+
name: "testing",
|
|
89
|
+
context: `${prefix}/testing`,
|
|
90
|
+
url,
|
|
91
|
+
...testingOutcome(build)
|
|
92
|
+
}];
|
|
93
|
+
if (flags.review) return [{
|
|
94
|
+
name: "review",
|
|
95
|
+
context: `${prefix}/review`,
|
|
96
|
+
url,
|
|
97
|
+
...reviewOutcome(build, input.approvals)
|
|
98
|
+
}];
|
|
99
|
+
return reviewClosed(input, url);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Review is off and the build is accepted: a final `success`, so a `<prefix>/review` status left
|
|
103
|
+
* `pending` by the time review was on (the switch-off pass accepts such builds) does not stay on
|
|
104
|
+
* the pull request. The function sees no history, so every accepted build of a project with review
|
|
105
|
+
* off shows it, and a host that requires the status never waits for one that will not come.
|
|
106
|
+
*/
|
|
107
|
+
function reviewClosed(input, url) {
|
|
108
|
+
if (input.build.status !== "approved") return [];
|
|
109
|
+
const { prefix } = input;
|
|
110
|
+
return [{
|
|
111
|
+
name: "review",
|
|
112
|
+
context: `${prefix}/review`,
|
|
113
|
+
url,
|
|
114
|
+
state: "success",
|
|
115
|
+
description: "Review is off"
|
|
116
|
+
}];
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* A user cancelled the build (ADR 0006). It closes both gates the same way: the host shows
|
|
120
|
+
* `cancelled` where it has the state (GitLab `canceled`, GitHub `error`), so a required status does
|
|
121
|
+
* not wait for a result that will never come, and nothing says the build failed or was reviewed.
|
|
122
|
+
*/
|
|
123
|
+
const CANCELLED = {
|
|
124
|
+
state: "cancelled",
|
|
125
|
+
description: "Cancelled"
|
|
126
|
+
};
|
|
127
|
+
/** `pending` until captured, `pending` while review is open, then the review's result. */
|
|
128
|
+
function testingOutcome(build) {
|
|
129
|
+
switch (build.status) {
|
|
130
|
+
case "reviewing": return {
|
|
131
|
+
state: "pending",
|
|
132
|
+
description: `Review open: ${plural(openCount(build), "change")} to review`
|
|
133
|
+
};
|
|
134
|
+
case "approved": return {
|
|
135
|
+
state: "success",
|
|
136
|
+
description: approvedText(build)
|
|
137
|
+
};
|
|
138
|
+
case "rejected": return {
|
|
139
|
+
state: "failure",
|
|
140
|
+
description: `Review closed: ${plural(build.rejectedCount, "change")} rejected`
|
|
141
|
+
};
|
|
142
|
+
case "failed": return {
|
|
143
|
+
state: "failure",
|
|
144
|
+
description: "Capture failed"
|
|
145
|
+
};
|
|
146
|
+
case "cancelled": return CANCELLED;
|
|
147
|
+
default: return {
|
|
148
|
+
state: "pending",
|
|
149
|
+
description: "Capturing and comparing screenshots"
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/** The build-level verdict: `pending` until given, then `success` or `failure`. */
|
|
154
|
+
function reviewOutcome(build, approvals) {
|
|
155
|
+
switch (build.status) {
|
|
156
|
+
case "approved": return {
|
|
157
|
+
state: "success",
|
|
158
|
+
description: "Build approved"
|
|
159
|
+
};
|
|
160
|
+
case "rejected": return {
|
|
161
|
+
state: "failure",
|
|
162
|
+
description: "Build rejected"
|
|
163
|
+
};
|
|
164
|
+
case "failed": return {
|
|
165
|
+
state: "failure",
|
|
166
|
+
description: "Build failed"
|
|
167
|
+
};
|
|
168
|
+
case "cancelled": return CANCELLED;
|
|
169
|
+
default: return {
|
|
170
|
+
state: "pending",
|
|
171
|
+
description: waitingText(approvals)
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/** Informational: never a gate, linking to the published Storybook once there is one. */
|
|
176
|
+
function storybookStatus(publishing, prefix, links) {
|
|
177
|
+
const base = {
|
|
178
|
+
name: "storybook",
|
|
179
|
+
context: `${prefix}/storybook`,
|
|
180
|
+
informational: true
|
|
181
|
+
};
|
|
182
|
+
if (publishing === "success") return {
|
|
183
|
+
...base,
|
|
184
|
+
state: "success",
|
|
185
|
+
description: "Storybook published",
|
|
186
|
+
url: links.storybook
|
|
187
|
+
};
|
|
188
|
+
if (publishing === "failure") return {
|
|
189
|
+
...base,
|
|
190
|
+
state: "failure",
|
|
191
|
+
description: "Storybook could not be published",
|
|
192
|
+
url: links.build
|
|
193
|
+
};
|
|
194
|
+
return {
|
|
195
|
+
...base,
|
|
196
|
+
state: "pending",
|
|
197
|
+
description: "Publishing Storybook",
|
|
198
|
+
url: links.build
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
/** `1 of 2 approvals` when the project needs several; a single approval just waits for a verdict. */
|
|
202
|
+
function waitingText(approvals) {
|
|
203
|
+
if (approvals === void 0 || approvals.required <= 1) return "Waiting for a review verdict";
|
|
204
|
+
return `Waiting for approvals: ${String(approvals.given)} of ${String(approvals.required)}`;
|
|
205
|
+
}
|
|
206
|
+
function openCount(build) {
|
|
207
|
+
return Math.max(0, build.changedCount - build.approvedCount - build.rejectedCount);
|
|
208
|
+
}
|
|
209
|
+
function approvedText(build) {
|
|
210
|
+
return build.changedCount === 0 ? "Review closed: no visual changes" : `Review closed: ${plural(build.approvedCount, "change")} approved`;
|
|
211
|
+
}
|
|
212
|
+
function plural(count, noun) {
|
|
213
|
+
return `${String(count)} ${noun}${count === 1 ? "" : "s"}`;
|
|
214
|
+
}
|
|
215
|
+
//#endregion
|
|
216
|
+
export { MAX_ANNOTATIONS, statusesForBuild };
|
|
217
|
+
|
|
218
|
+
//# sourceMappingURL=build-state.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-state.mjs","names":[],"sources":["../../../src/adapters/notifier/build-state.ts"],"sourcesContent":["/**\n * The states a build's git channels show, as one pure function of the build (ADR 0014, decision 5).\n * It reads no clock, database or provider: callers hand it the build, the channel's prefix and the\n * flags, and write the result to the host. Every transition and the end of publishing call it\n * again, so a build that was never captured still gets its statuses and a repeated call changes\n * nothing.\n */\nimport { LOCAL_SHA_PREFIX } from \"@storyshelf/affected\";\nimport type { UrlBuilder } from \"../../urls.ts\";\nimport { buildCommentMarkdown } from \"./build-comment.ts\";\nimport type { NotifierState, StateAnnotation } from \"./types.ts\";\n\n/** Most per-snapshot annotations a build state carries; the rest are counted (`omitted`). */\nexport const MAX_ANNOTATIONS = 200;\n\n/** Names of the statuses below a channel's prefix. */\nexport type StatusName = \"testing\" | \"review\" | \"storybook\";\n\n/** How publishing the build's Storybook stands. */\nexport type PublishingState = \"pending\" | \"success\" | \"failure\";\n\n/** What decides which statuses exist (ADR 0014, decision 5). */\nexport interface BuildStateFlags {\n /** `testing` is on: `<prefix>/testing` is the merge gate. */\n testing: boolean;\n /** Review is on for the project: with `testing` off, `<prefix>/review` is the gate. */\n review: boolean;\n}\n\n/** The part of a build row the function reads. */\nexport interface BuildStateBuild {\n id: string;\n gitSha: string;\n /** Build status: `pending`, `capturing`, `comparing`, `reviewing`, `approved`, `rejected`, `failed`, `cancelled`. */\n status: string;\n snapshotCount: number;\n changedCount: number;\n approvedCount: number;\n rejectedCount: number;\n}\n\n/** The part of a snapshot row the annotations read. */\nexport interface BuildStateChange {\n id: string;\n /** `changed` or `new`; anything else is not a change to review. */\n status: string;\n storyTitle: string;\n storyName: string;\n storyImportPath: string | null;\n viewportName: string;\n browser?: string | undefined;\n}\n\n/** Inputs of {@link statusesForBuild}. */\nexport interface BuildStateInput {\n build: BuildStateBuild;\n projectSlug: string;\n /** The channel's status-name prefix, without a trailing slash. */\n prefix: string;\n /**\n * Absolute links (`createUrlBuilder(publicOrigin)`). A build's state is only ever shown once a\n * public origin is resolved (ADR 0014, decision 11), so every status links and the comment exists.\n */\n urls: Pick<UrlBuilder, \"build\" | \"storybookBuild\" | \"snapshot\">;\n flags: BuildStateFlags;\n publishing: PublishingState;\n /**\n * Approval progress of the build-level review: the approvals among each reviewer's latest\n * verdict and the approvals the project requires. Shown on `<prefix>/review` while it waits.\n */\n approvals?: { given: number; required: number } | undefined;\n /** The build's snapshots; the `changed` and `new` ones become annotations of `<prefix>/testing`. */\n changes?: readonly BuildStateChange[] | undefined;\n}\n\n/** One commit status to show. */\nexport interface DesiredStatus {\n name: StatusName;\n /** Full name on the host, `<prefix>/<name>`. */\n context: string;\n state: NotifierState;\n description: string;\n /** What the status links to: the build page, or the published Storybook for `storybook`. */\n url: string;\n /** True for the Storybook status, which is never a gate. */\n informational?: boolean;\n /** Set on a `pending` status: `queued` before capture starts, else `running`. */\n progress?: \"queued\" | \"running\";\n /** Per-snapshot findings (only `testing`), at most {@link MAX_ANNOTATIONS}. */\n annotations?: StateAnnotation[];\n /** Changes beyond the cap. */\n omitted?: number;\n}\n\n/** The pull-request comment to show. */\nexport interface DesiredComment {\n /** The build page; it keys the comment, one per build. */\n url: string;\n markdown: string;\n state: NotifierState;\n}\n\n/** Everything a channel should show for a build. */\nexport interface BuildStateSet {\n statuses: DesiredStatus[];\n comment: DesiredComment | null;\n}\n\n/** Statuses and comment for a build; empty without a real commit (no SHA or a local identity). */\nexport function statusesForBuild(input: BuildStateInput): BuildStateSet {\n const { build } = input;\n if (build.gitSha.length === 0 || build.gitSha.startsWith(LOCAL_SHA_PREFIX)) {\n return { statuses: [], comment: null };\n }\n const links = linksOf(input);\n const statuses = [\n ...withAnnotations(gateStatus(input, links.build), input),\n storybookStatus(input.publishing, input.prefix, links),\n ].map((status) => withProgress(status, input.build));\n return { statuses, comment: commentOf(input, statuses, links) };\n}\n\n/** `pending` is `queued` while the build waits to be captured, `running` after. */\nfunction withProgress(status: DesiredStatus, build: BuildStateBuild): DesiredStatus {\n if (status.state !== \"pending\") {\n return status;\n }\n const queued = status.name !== \"storybook\" && build.status === \"pending\";\n return { ...status, progress: queued ? \"queued\" : \"running\" };\n}\n\n/** One annotation per changed snapshot, on the `testing` gate. */\nfunction withAnnotations(gates: DesiredStatus[], input: BuildStateInput): DesiredStatus[] {\n const changed = (input.changes ?? []).filter((c) => c.status === \"changed\" || c.status === \"new\");\n if (changed.length === 0) {\n return gates;\n }\n const annotations = changed\n .slice(0, MAX_ANNOTATIONS)\n .map((change) => annotationOf(change, input));\n const omitted = changed.length - annotations.length;\n return gates.map((gate) =>\n gate.name === \"testing\" ? { ...gate, annotations, ...(omitted > 0 ? { omitted } : {}) } : gate,\n );\n}\n\nfunction annotationOf(change: BuildStateChange, input: BuildStateInput): StateAnnotation {\n const where = [change.viewportName, change.browser].filter(Boolean).join(\", \");\n const isNew = change.status === \"new\";\n const name = `${change.storyTitle} / ${change.storyName}`;\n return {\n path: change.storyImportPath?.replace(/^(?:\\.\\/)+/u, \"\") ?? \".storybook\",\n title: where === \"\" ? name : `${name} (${where})`,\n message: `${isNew ? \"New snapshot\" : \"Visual change\"}: review it at ${input.urls.snapshot(input.projectSlug, input.build.id, change.id)}`,\n url: input.urls.snapshot(input.projectSlug, input.build.id, change.id),\n level: isNew ? \"notice\" : \"warning\",\n };\n}\n\nfunction commentOf(\n input: BuildStateInput,\n statuses: DesiredStatus[],\n links: Links,\n): DesiredComment {\n const gates = statuses.filter((status) => status.name !== \"storybook\");\n return {\n url: links.build,\n state: gates[0]?.state ?? \"success\",\n markdown: buildCommentMarkdown({\n buildUrl: links.build,\n storybookUrl: input.publishing === \"success\" ? links.storybook : undefined,\n gates,\n counts: input.flags.testing || input.flags.review ? input.build : undefined,\n }),\n };\n}\n\ninterface Links {\n build: string;\n storybook: string;\n}\n\n/** The links to show. */\nfunction linksOf(input: BuildStateInput): Links {\n const { urls, projectSlug, build } = input;\n return {\n build: urls.build(projectSlug, build.id),\n storybook: `${urls.storybookBuild(projectSlug, build.id).replace(/\\/+$/u, \"\")}/`,\n };\n}\n\n/** `testing` when it is on; `review` when only review is on; none for a hosting-only build. */\nfunction gateStatus(input: BuildStateInput, url: string): DesiredStatus[] {\n const { flags, build, prefix } = input;\n if (flags.testing) {\n return [{ name: \"testing\", context: `${prefix}/testing`, url, ...testingOutcome(build) }];\n }\n if (flags.review) {\n return [\n {\n name: \"review\",\n context: `${prefix}/review`,\n url,\n ...reviewOutcome(build, input.approvals),\n },\n ];\n }\n return reviewClosed(input, url);\n}\n\n/**\n * Review is off and the build is accepted: a final `success`, so a `<prefix>/review` status left\n * `pending` by the time review was on (the switch-off pass accepts such builds) does not stay on\n * the pull request. The function sees no history, so every accepted build of a project with review\n * off shows it, and a host that requires the status never waits for one that will not come.\n */\nfunction reviewClosed(input: BuildStateInput, url: string): DesiredStatus[] {\n if (input.build.status !== \"approved\") {\n return [];\n }\n const { prefix } = input;\n return [\n {\n name: \"review\",\n context: `${prefix}/review`,\n url,\n state: \"success\",\n description: \"Review is off\",\n },\n ];\n}\n\ntype Outcome = Pick<DesiredStatus, \"state\" | \"description\">;\n\n/**\n * A user cancelled the build (ADR 0006). It closes both gates the same way: the host shows\n * `cancelled` where it has the state (GitLab `canceled`, GitHub `error`), so a required status does\n * not wait for a result that will never come, and nothing says the build failed or was reviewed.\n */\nconst CANCELLED: Outcome = { state: \"cancelled\", description: \"Cancelled\" };\n\n/** `pending` until captured, `pending` while review is open, then the review's result. */\nfunction testingOutcome(build: BuildStateBuild): Outcome {\n switch (build.status) {\n case \"reviewing\": {\n return {\n state: \"pending\",\n description: `Review open: ${plural(openCount(build), \"change\")} to review`,\n };\n }\n case \"approved\": {\n return { state: \"success\", description: approvedText(build) };\n }\n case \"rejected\": {\n return {\n state: \"failure\",\n description: `Review closed: ${plural(build.rejectedCount, \"change\")} rejected`,\n };\n }\n case \"failed\": {\n return { state: \"failure\", description: \"Capture failed\" };\n }\n case \"cancelled\": {\n return CANCELLED;\n }\n default: {\n return { state: \"pending\", description: \"Capturing and comparing screenshots\" };\n }\n }\n}\n\n/** The build-level verdict: `pending` until given, then `success` or `failure`. */\nfunction reviewOutcome(build: BuildStateBuild, approvals: BuildStateInput[\"approvals\"]): Outcome {\n switch (build.status) {\n case \"approved\": {\n return { state: \"success\", description: \"Build approved\" };\n }\n case \"rejected\": {\n return { state: \"failure\", description: \"Build rejected\" };\n }\n case \"failed\": {\n return { state: \"failure\", description: \"Build failed\" };\n }\n case \"cancelled\": {\n return CANCELLED;\n }\n default: {\n return { state: \"pending\", description: waitingText(approvals) };\n }\n }\n}\n\n/** Informational: never a gate, linking to the published Storybook once there is one. */\nfunction storybookStatus(publishing: PublishingState, prefix: string, links: Links): DesiredStatus {\n const base = { name: \"storybook\", context: `${prefix}/storybook`, informational: true } as const;\n if (publishing === \"success\") {\n return {\n ...base,\n state: \"success\",\n description: \"Storybook published\",\n url: links.storybook,\n };\n }\n if (publishing === \"failure\") {\n return {\n ...base,\n state: \"failure\",\n description: \"Storybook could not be published\",\n url: links.build,\n };\n }\n return { ...base, state: \"pending\", description: \"Publishing Storybook\", url: links.build };\n}\n\n/** `1 of 2 approvals` when the project needs several; a single approval just waits for a verdict. */\nfunction waitingText(approvals: BuildStateInput[\"approvals\"]): string {\n if (approvals === undefined || approvals.required <= 1) {\n return \"Waiting for a review verdict\";\n }\n return `Waiting for approvals: ${String(approvals.given)} of ${String(approvals.required)}`;\n}\n\nfunction openCount(build: BuildStateBuild): number {\n return Math.max(0, build.changedCount - build.approvedCount - build.rejectedCount);\n}\n\nfunction approvedText(build: BuildStateBuild): string {\n return build.changedCount === 0\n ? \"Review closed: no visual changes\"\n : `Review closed: ${plural(build.approvedCount, \"change\")} approved`;\n}\n\nfunction plural(count: number, noun: string): string {\n return `${String(count)} ${noun}${count === 1 ? \"\" : \"s\"}`;\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
|
-
import { NotificationBrand, NotificationChannel, NotificationEvent, SystemNotification } from "./types.mjs";
|
|
4
|
-
import { NotifierProvider } from "./provider.mjs";
|
|
5
3
|
import { Logger } from "../../logger.mjs";
|
|
4
|
+
import { ChannelOutcome, NotificationBrand, NotificationChannel, NotificationEvent, SystemNotification } from "./types.mjs";
|
|
5
|
+
import { NotifierProvider } from "./provider.mjs";
|
|
6
6
|
//#region src/adapters/notifier/events.d.ts
|
|
7
7
|
/** Options for project-event fan-out (inline best-effort, non-fatal). */
|
|
8
8
|
interface EmitNotificationsOptions {
|
|
@@ -10,6 +10,8 @@ interface EmitNotificationsOptions {
|
|
|
10
10
|
channels?: NotificationChannel[];
|
|
11
11
|
brand?: NotificationBrand;
|
|
12
12
|
logger?: Logger;
|
|
13
|
+
/** Called once per attempted channel so the host can record or clear its last error. */
|
|
14
|
+
onOutcome?: (outcome: ChannelOutcome) => Promise<void> | void;
|
|
13
15
|
}
|
|
14
16
|
/**
|
|
15
17
|
* Deliver a project event to every subscribed channel.
|
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
+
import { sanitizeErrorText } from "../../utils/redact.mjs";
|
|
4
5
|
import { formatNotification, formatSystemNotification } from "./format.mjs";
|
|
6
|
+
import { isStateProvider } from "./provider.mjs";
|
|
5
7
|
//#region src/adapters/notifier/events.ts
|
|
6
8
|
/** Check an event against a channel's allowlist (empty = all). */
|
|
7
9
|
function subscribed(channel, event) {
|
|
8
10
|
if (!channel.enabled) return false;
|
|
9
11
|
return channel.events.length === 0 || channel.events.includes(event);
|
|
10
12
|
}
|
|
13
|
+
/** `state` channels (git hosts) follow build state, never the message fan-out. */
|
|
14
|
+
function isMessageChannel(notifiers, channel) {
|
|
15
|
+
const provider = providerFor(notifiers, channel.provider);
|
|
16
|
+
return provider === void 0 || !isStateProvider(provider);
|
|
17
|
+
}
|
|
11
18
|
/** Resolve a provider for a channel kind, or undefined when unwired. */
|
|
12
19
|
function providerFor(notifiers, kind) {
|
|
13
20
|
return notifiers?.find((candidate) => candidate.metadata.kind === kind);
|
|
@@ -16,6 +23,7 @@ function providerFor(notifiers, kind) {
|
|
|
16
23
|
async function deliver(notifiers, channel, formatted) {
|
|
17
24
|
const provider = providerFor(notifiers, channel.provider);
|
|
18
25
|
if (!provider) throw new Error(`notifier missing: ${channel.provider}`);
|
|
26
|
+
if (isStateProvider(provider)) throw new Error(`notifier ${channel.provider} delivers state, not messages`);
|
|
19
27
|
await provider.create({
|
|
20
28
|
config: channel.config,
|
|
21
29
|
secret: channel.secret
|
|
@@ -24,22 +32,33 @@ async function deliver(notifiers, channel, formatted) {
|
|
|
24
32
|
formatted
|
|
25
33
|
});
|
|
26
34
|
}
|
|
35
|
+
/** Deliver to one channel, report the outcome and swallow the failure (logged). */
|
|
36
|
+
async function deliverReported(options, channel, formatted, failure) {
|
|
37
|
+
let problem;
|
|
38
|
+
try {
|
|
39
|
+
await deliver(options.notifiers, channel, formatted);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
options.logger?.warn({
|
|
42
|
+
err: error,
|
|
43
|
+
channelId: channel.id
|
|
44
|
+
}, failure);
|
|
45
|
+
problem = sanitizeErrorText(error instanceof Error ? error.message : String(error), 500);
|
|
46
|
+
}
|
|
47
|
+
await options.onOutcome?.({
|
|
48
|
+
channelId: channel.id,
|
|
49
|
+
...problem === void 0 ? {} : { error: problem }
|
|
50
|
+
});
|
|
51
|
+
}
|
|
27
52
|
/**
|
|
28
53
|
* Deliver a project event to every subscribed channel.
|
|
29
54
|
* Inline best-effort: `Promise.allSettled`, failures logged and swallowed.
|
|
30
55
|
*/
|
|
31
56
|
async function emitNotifications(event, options = {}) {
|
|
32
|
-
const targets = (options.channels ?? []).filter((channel) => subscribed(channel, event.event));
|
|
57
|
+
const targets = (options.channels ?? []).filter((channel) => subscribed(channel, event.event) && isMessageChannel(options.notifiers, channel));
|
|
33
58
|
if (targets.length === 0) return;
|
|
59
|
+
const formatted = formatNotification(event, options.brand);
|
|
34
60
|
await Promise.allSettled(targets.map(async (channel) => {
|
|
35
|
-
|
|
36
|
-
await deliver(options.notifiers, channel, formatNotification(event, options.brand));
|
|
37
|
-
} catch (error) {
|
|
38
|
-
options.logger?.warn({
|
|
39
|
-
err: error,
|
|
40
|
-
channelId: channel.id
|
|
41
|
-
}, "notification failed");
|
|
42
|
-
}
|
|
61
|
+
await deliverReported(options, channel, formatted, "notification failed");
|
|
43
62
|
}));
|
|
44
63
|
}
|
|
45
64
|
/**
|
|
@@ -47,17 +66,11 @@ async function emitNotifications(event, options = {}) {
|
|
|
47
66
|
* (e.g. `sys:user-created`, `sys:capture-failed`, `sys:purge-completed`).
|
|
48
67
|
*/
|
|
49
68
|
async function emitSystemNotification(event, options = {}) {
|
|
50
|
-
const targets = (options.channels ?? []).filter((channel) => channel.projectId === null && subscribed(channel, event.event));
|
|
69
|
+
const targets = (options.channels ?? []).filter((channel) => channel.projectId === null && subscribed(channel, event.event) && isMessageChannel(options.notifiers, channel));
|
|
51
70
|
if (targets.length === 0) return;
|
|
71
|
+
const formatted = formatSystemNotification(event, options.brand);
|
|
52
72
|
await Promise.allSettled(targets.map(async (channel) => {
|
|
53
|
-
|
|
54
|
-
await deliver(options.notifiers, channel, formatSystemNotification(event, options.brand));
|
|
55
|
-
} catch (error) {
|
|
56
|
-
options.logger?.warn({
|
|
57
|
-
err: error,
|
|
58
|
-
channelId: channel.id
|
|
59
|
-
}, "system notification failed");
|
|
60
|
-
}
|
|
73
|
+
await deliverReported(options, channel, formatted, "system notification failed");
|
|
61
74
|
}));
|
|
62
75
|
}
|
|
63
76
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.mjs","names":[],"sources":["../../../src/adapters/notifier/events.ts"],"sourcesContent":["import type { Logger } from \"../../logger.ts\";\nimport { formatNotification, formatSystemNotification } from \"./format.ts\";\nimport type
|
|
1
|
+
{"version":3,"file":"events.mjs","names":[],"sources":["../../../src/adapters/notifier/events.ts"],"sourcesContent":["import type { Logger } from \"../../logger.ts\";\nimport { sanitizeErrorText } from \"../../utils/redact.ts\";\nimport { formatNotification, formatSystemNotification } from \"./format.ts\";\nimport { isStateProvider, type NotifierProvider } from \"./provider.ts\";\nimport type {\n ChannelOutcome,\n NotificationBrand,\n NotificationChannel,\n NotificationEvent,\n SystemNotification,\n} from \"./types.ts\";\n\n/** Options for project-event fan-out (inline best-effort, non-fatal). */\nexport interface EmitNotificationsOptions {\n notifiers?: NotifierProvider[];\n channels?: NotificationChannel[];\n brand?: NotificationBrand;\n logger?: Logger;\n /** Called once per attempted channel so the host can record or clear its last error. */\n onOutcome?: (outcome: ChannelOutcome) => Promise<void> | void;\n}\n\n/** Check an event against a channel's allowlist (empty = all). */\nfunction subscribed(channel: NotificationChannel, event: string): boolean {\n if (!channel.enabled) {\n return false;\n }\n return channel.events.length === 0 || channel.events.includes(event);\n}\n\n/** `state` channels (git hosts) follow build state, never the message fan-out. */\nfunction isMessageChannel(\n notifiers: NotifierProvider[] | undefined,\n channel: NotificationChannel,\n): boolean {\n const provider = providerFor(notifiers, channel.provider);\n return provider === undefined || !isStateProvider(provider);\n}\n\n/** Resolve a provider for a channel kind, or undefined when unwired. */\nfunction providerFor(\n notifiers: NotifierProvider[] | undefined,\n kind: string,\n): NotifierProvider | undefined {\n return notifiers?.find((candidate) => candidate.metadata.kind === kind);\n}\n\n/** Send one formatted event through a channel's provider (throws on failure). */\nasync function deliver(\n notifiers: NotifierProvider[] | undefined,\n channel: NotificationChannel,\n formatted: ReturnType<typeof formatNotification>,\n): Promise<void> {\n const provider = providerFor(notifiers, channel.provider);\n if (!provider) {\n throw new Error(`notifier missing: ${channel.provider}`);\n }\n if (isStateProvider(provider)) {\n throw new Error(`notifier ${channel.provider} delivers state, not messages`);\n }\n const sender = provider.create({ config: channel.config, secret: channel.secret });\n await sender.send({ channel, formatted });\n}\n\n/** Deliver to one channel, report the outcome and swallow the failure (logged). */\nasync function deliverReported(\n options: EmitNotificationsOptions,\n channel: NotificationChannel,\n formatted: ReturnType<typeof formatNotification>,\n failure: string,\n): Promise<void> {\n let problem: string | undefined;\n try {\n await deliver(options.notifiers, channel, formatted);\n } catch (error) {\n options.logger?.warn({ err: error, channelId: channel.id }, failure);\n problem = sanitizeErrorText(error instanceof Error ? error.message : String(error), 500);\n }\n await options.onOutcome?.({\n channelId: channel.id,\n ...(problem === undefined ? {} : { error: problem }),\n });\n}\n\n/**\n * Deliver a project event to every subscribed channel.\n * Inline best-effort: `Promise.allSettled`, failures logged and swallowed.\n */\nexport async function emitNotifications(\n event: NotificationEvent,\n options: EmitNotificationsOptions = {},\n): Promise<void> {\n const targets = (options.channels ?? []).filter(\n (channel) => subscribed(channel, event.event) && isMessageChannel(options.notifiers, channel),\n );\n if (targets.length === 0) {\n return;\n }\n const formatted = formatNotification(event, options.brand);\n await Promise.allSettled(\n targets.map(async (channel) => {\n await deliverReported(options, channel, formatted, \"notification failed\");\n }),\n );\n}\n\n/**\n * Deliver a site-wide admin alert to project-less channels\n * (e.g. `sys:user-created`, `sys:capture-failed`, `sys:purge-completed`).\n */\nexport async function emitSystemNotification(\n event: SystemNotification,\n options: EmitNotificationsOptions = {},\n): Promise<void> {\n const targets = (options.channels ?? []).filter(\n (channel) =>\n channel.projectId === null &&\n subscribed(channel, event.event) &&\n isMessageChannel(options.notifiers, channel),\n );\n if (targets.length === 0) {\n return;\n }\n const formatted = formatSystemNotification(event, options.brand);\n await Promise.allSettled(\n targets.map(async (channel) => {\n await deliverReported(options, channel, formatted, \"system notification failed\");\n }),\n );\n}\n"],"mappings":";;;;;;;;AAuBA,SAAS,WAAW,SAA8B,OAAwB;CACxE,IAAI,CAAC,QAAQ,SACX,OAAO;CAET,OAAO,QAAQ,OAAO,WAAW,KAAK,QAAQ,OAAO,SAAS,KAAK;AACrE;;AAGA,SAAS,iBACP,WACA,SACS;CACT,MAAM,WAAW,YAAY,WAAW,QAAQ,QAAQ;CACxD,OAAO,aAAa,KAAA,KAAa,CAAC,gBAAgB,QAAQ;AAC5D;;AAGA,SAAS,YACP,WACA,MAC8B;CAC9B,OAAO,WAAW,MAAM,cAAc,UAAU,SAAS,SAAS,IAAI;AACxE;;AAGA,eAAe,QACb,WACA,SACA,WACe;CACf,MAAM,WAAW,YAAY,WAAW,QAAQ,QAAQ;CACxD,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,qBAAqB,QAAQ,UAAU;CAEzD,IAAI,gBAAgB,QAAQ,GAC1B,MAAM,IAAI,MAAM,YAAY,QAAQ,SAAS,8BAA8B;CAG7E,MADe,SAAS,OAAO;EAAE,QAAQ,QAAQ;EAAQ,QAAQ,QAAQ;CAAO,CACrE,CAAC,CAAC,KAAK;EAAE;EAAS;CAAU,CAAC;AAC1C;;AAGA,eAAe,gBACb,SACA,SACA,WACA,SACe;CACf,IAAI;CACJ,IAAI;EACF,MAAM,QAAQ,QAAQ,WAAW,SAAS,SAAS;CACrD,SAAS,OAAO;EACd,QAAQ,QAAQ,KAAK;GAAE,KAAK;GAAO,WAAW,QAAQ;EAAG,GAAG,OAAO;EACnE,UAAU,kBAAkB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG,GAAG;CACzF;CACA,MAAM,QAAQ,YAAY;EACxB,WAAW,QAAQ;EACnB,GAAI,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO,QAAQ;CACpD,CAAC;AACH;;;;;AAMA,eAAsB,kBACpB,OACA,UAAoC,CAAC,GACtB;CACf,MAAM,WAAW,QAAQ,YAAY,CAAC,EAAA,CAAG,QACtC,YAAY,WAAW,SAAS,MAAM,KAAK,KAAK,iBAAiB,QAAQ,WAAW,OAAO,CAC9F;CACA,IAAI,QAAQ,WAAW,GACrB;CAEF,MAAM,YAAY,mBAAmB,OAAO,QAAQ,KAAK;CACzD,MAAM,QAAQ,WACZ,QAAQ,IAAI,OAAO,YAAY;EAC7B,MAAM,gBAAgB,SAAS,SAAS,WAAW,qBAAqB;CAC1E,CAAC,CACH;AACF;;;;;AAMA,eAAsB,uBACpB,OACA,UAAoC,CAAC,GACtB;CACf,MAAM,WAAW,QAAQ,YAAY,CAAC,EAAA,CAAG,QACtC,YACC,QAAQ,cAAc,QACtB,WAAW,SAAS,MAAM,KAAK,KAC/B,iBAAiB,QAAQ,WAAW,OAAO,CAC/C;CACA,IAAI,QAAQ,WAAW,GACrB;CAEF,MAAM,YAAY,yBAAyB,OAAO,QAAQ,KAAK;CAC/D,MAAM,QAAQ,WACZ,QAAQ,IAAI,OAAO,YAAY;EAC7B,MAAM,gBAAgB,SAAS,SAAS,WAAW,4BAA4B;CACjF,CAAC,CACH;AACF"}
|
|
@@ -1,32 +1,78 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
|
-
import { FormattedNotification, NotificationChannel } from "./types.mjs";
|
|
3
|
+
import { FormattedNotification, NotificationChannel, NotifierState, NotifierStateTarget } from "./types.mjs";
|
|
4
4
|
import { Adapter, AdapterMetadata } from "../../adapter/metadata.mjs";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { Logger } from "pino";
|
|
7
7
|
//#region src/adapters/notifier/provider.d.ts
|
|
8
|
+
/** How a provider delivers: `message` sends once, `state` makes a host show a named state. */
|
|
9
|
+
type NotifierDelivery = "message" | "state";
|
|
8
10
|
/** Notifier-specific metadata (adds logo + config validation schema). */
|
|
9
11
|
interface NotifierMetadata extends AdapterMetadata {
|
|
10
12
|
readonly category: "notifier";
|
|
11
13
|
readonly logo?: string;
|
|
12
14
|
readonly schema: z.ZodType;
|
|
15
|
+
/** Delivery kind; absent means `message` (chat, email). */
|
|
16
|
+
readonly delivery?: NotifierDelivery;
|
|
13
17
|
}
|
|
14
|
-
/**
|
|
15
|
-
interface
|
|
18
|
+
/** Metadata of a `message` provider (Slack, Teams, email). */
|
|
19
|
+
interface MessageNotifierMetadata extends NotifierMetadata {
|
|
20
|
+
readonly delivery?: "message";
|
|
21
|
+
}
|
|
22
|
+
/** Metadata of a `state` provider (GitHub, GitLab): `delivery` is mandatory. */
|
|
23
|
+
interface StateNotifierMetadata extends NotifierMetadata {
|
|
24
|
+
readonly delivery: "state";
|
|
25
|
+
}
|
|
26
|
+
/** Descriptor of a `message` provider, registered at startup. */
|
|
27
|
+
interface MessageNotifierProvider extends Adapter<MessageNotifierMetadata> {
|
|
16
28
|
create(opts: {
|
|
17
29
|
config: unknown;
|
|
18
30
|
secret?: string;
|
|
19
31
|
logger?: Logger;
|
|
20
|
-
}):
|
|
32
|
+
}): MessageNotifierAdapter;
|
|
21
33
|
}
|
|
22
|
-
/**
|
|
23
|
-
interface
|
|
34
|
+
/** Descriptor of a `state` provider, registered at startup. */
|
|
35
|
+
interface StateNotifierProvider extends Adapter<StateNotifierMetadata> {
|
|
36
|
+
create(opts: {
|
|
37
|
+
config: unknown;
|
|
38
|
+
secret?: string;
|
|
39
|
+
logger?: Logger;
|
|
40
|
+
}): StateNotifierAdapter;
|
|
41
|
+
}
|
|
42
|
+
/** Descriptor — registered at startup, validates per-channel config. */
|
|
43
|
+
type NotifierProvider = MessageNotifierProvider | StateNotifierProvider;
|
|
44
|
+
/** Runtime of a `message` provider — bound to one channel config + secret, sends one message. */
|
|
45
|
+
interface MessageNotifierAdapter extends Adapter<MessageNotifierMetadata> {
|
|
24
46
|
send(input: {
|
|
25
47
|
channel: NotificationChannel;
|
|
26
48
|
formatted: FormattedNotification;
|
|
27
49
|
logger?: Logger;
|
|
28
50
|
}): Promise<void>;
|
|
29
51
|
}
|
|
52
|
+
/** Input of one idempotent state write. */
|
|
53
|
+
interface SetStateInput {
|
|
54
|
+
channel: NotificationChannel;
|
|
55
|
+
/** What to make the host show: a commit status or a pull-request comment. */
|
|
56
|
+
target: NotifierStateTarget;
|
|
57
|
+
state: NotifierState;
|
|
58
|
+
/**
|
|
59
|
+
* Link back to the build page; also keys the pull-request comment. Absent while no public origin
|
|
60
|
+
* is known (ADR 0007): a status then has no link and a comment is not written.
|
|
61
|
+
*/
|
|
62
|
+
url?: string | undefined;
|
|
63
|
+
logger?: Logger;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Runtime of a `state` provider: idempotent, latest call wins. A provider that
|
|
67
|
+
* cannot show a target kind (no comment support) skips it silently.
|
|
68
|
+
*/
|
|
69
|
+
interface StateNotifierAdapter extends Adapter<StateNotifierMetadata> {
|
|
70
|
+
setState(input: SetStateInput): Promise<void>;
|
|
71
|
+
}
|
|
72
|
+
/** Runtime — bound to one channel config + decrypted secret. */
|
|
73
|
+
type NotifierAdapter = MessageNotifierAdapter | StateNotifierAdapter;
|
|
74
|
+
/** True when the provider delivers state (git hosts) rather than messages. */
|
|
75
|
+
declare function isStateProvider(provider: NotifierProvider): provider is StateNotifierProvider;
|
|
30
76
|
//#endregion
|
|
31
|
-
export { NotifierAdapter, NotifierMetadata, NotifierProvider };
|
|
77
|
+
export { MessageNotifierAdapter, MessageNotifierMetadata, MessageNotifierProvider, NotifierAdapter, NotifierDelivery, NotifierMetadata, NotifierProvider, SetStateInput, StateNotifierAdapter, StateNotifierMetadata, StateNotifierProvider, isStateProvider };
|
|
32
78
|
//# sourceMappingURL=provider.d.mts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
//#region src/adapters/notifier/provider.ts
|
|
5
|
+
/** True when the provider delivers state (git hosts) rather than messages. */
|
|
6
|
+
function isStateProvider(provider) {
|
|
7
|
+
return provider.metadata.delivery === "state";
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
export { isStateProvider };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=provider.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.mjs","names":[],"sources":["../../../src/adapters/notifier/provider.ts"],"sourcesContent":["import type { Logger } from \"pino\";\nimport type { z } from \"zod\";\nimport type { Adapter, AdapterMetadata } from \"../metadata.ts\";\nimport type {\n FormattedNotification,\n NotificationChannel,\n NotifierState,\n NotifierStateTarget,\n} from \"./types.ts\";\n\n/** How a provider delivers: `message` sends once, `state` makes a host show a named state. */\nexport type NotifierDelivery = \"message\" | \"state\";\n\n/** Notifier-specific metadata (adds logo + config validation schema). */\nexport interface NotifierMetadata extends AdapterMetadata {\n readonly category: \"notifier\";\n readonly logo?: string;\n readonly schema: z.ZodType;\n /** Delivery kind; absent means `message` (chat, email). */\n readonly delivery?: NotifierDelivery;\n}\n\n/** Metadata of a `message` provider (Slack, Teams, email). */\nexport interface MessageNotifierMetadata extends NotifierMetadata {\n readonly delivery?: \"message\";\n}\n\n/** Metadata of a `state` provider (GitHub, GitLab): `delivery` is mandatory. */\nexport interface StateNotifierMetadata extends NotifierMetadata {\n readonly delivery: \"state\";\n}\n\n/** Descriptor of a `message` provider, registered at startup. */\nexport interface MessageNotifierProvider extends Adapter<MessageNotifierMetadata> {\n create(opts: { config: unknown; secret?: string; logger?: Logger }): MessageNotifierAdapter;\n}\n\n/** Descriptor of a `state` provider, registered at startup. */\nexport interface StateNotifierProvider extends Adapter<StateNotifierMetadata> {\n create(opts: { config: unknown; secret?: string; logger?: Logger }): StateNotifierAdapter;\n}\n\n/** Descriptor — registered at startup, validates per-channel config. */\nexport type NotifierProvider = MessageNotifierProvider | StateNotifierProvider;\n\n/** Runtime of a `message` provider — bound to one channel config + secret, sends one message. */\nexport interface MessageNotifierAdapter extends Adapter<MessageNotifierMetadata> {\n send(input: {\n channel: NotificationChannel;\n formatted: FormattedNotification;\n logger?: Logger;\n }): Promise<void>;\n}\n\n/** Input of one idempotent state write. */\nexport interface SetStateInput {\n channel: NotificationChannel;\n /** What to make the host show: a commit status or a pull-request comment. */\n target: NotifierStateTarget;\n state: NotifierState;\n /**\n * Link back to the build page; also keys the pull-request comment. Absent while no public origin\n * is known (ADR 0007): a status then has no link and a comment is not written.\n */\n url?: string | undefined;\n logger?: Logger;\n}\n\n/**\n * Runtime of a `state` provider: idempotent, latest call wins. A provider that\n * cannot show a target kind (no comment support) skips it silently.\n */\nexport interface StateNotifierAdapter extends Adapter<StateNotifierMetadata> {\n setState(input: SetStateInput): Promise<void>;\n}\n\n/** Runtime — bound to one channel config + decrypted secret. */\nexport type NotifierAdapter = MessageNotifierAdapter | StateNotifierAdapter;\n\n/** True when the provider delivers state (git hosts) rather than messages. */\nexport function isStateProvider(provider: NotifierProvider): provider is StateNotifierProvider {\n return provider.metadata.delivery === \"state\";\n}\n"],"mappings":";;;;;AAgFA,SAAgB,gBAAgB,UAA+D;CAC7F,OAAO,SAAS,SAAS,aAAa;AACxC"}
|