@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
package/dist/config.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
+
import { DEFAULT_SCRATCH_DIR, resolveShelfOptions } from "./options.mjs";
|
|
5
|
+
import { ALLOW_SERVERLESS_ENV, assertServerless, isServerlessHost } from "./serverless.mjs";
|
|
4
6
|
import { z } from "zod";
|
|
5
7
|
//#region src/config.ts
|
|
6
8
|
/** Default cap for a single Storybook zip upload (1 GiB). */
|
|
@@ -48,7 +50,7 @@ const adapterSnapshotSchema = z.object({
|
|
|
48
50
|
"storage",
|
|
49
51
|
"capture-runner",
|
|
50
52
|
"capture-queue",
|
|
51
|
-
"
|
|
53
|
+
"design",
|
|
52
54
|
"notifier"
|
|
53
55
|
])
|
|
54
56
|
});
|
|
@@ -62,6 +64,7 @@ const authUiConfigSchema = z.object({
|
|
|
62
64
|
helpText: z.string().optional(),
|
|
63
65
|
footerText: z.string().optional()
|
|
64
66
|
});
|
|
67
|
+
/** Zod schema validating `notifications.config`. */
|
|
65
68
|
const notificationsConfigSchema = z.object({
|
|
66
69
|
fromEmail: z.string().min(1).optional(),
|
|
67
70
|
fromName: z.string().min(1).optional(),
|
|
@@ -76,17 +79,17 @@ const shelfConfigSchema = z.object({
|
|
|
76
79
|
adminToken: z.string().min(1).optional(),
|
|
77
80
|
publishedBaseDomain: z.string().optional(),
|
|
78
81
|
publicBaseUrl: z.string().url().optional(),
|
|
79
|
-
captureConcurrency: z.number().int().positive().optional(),
|
|
80
82
|
scratchDir: z.string().optional(),
|
|
81
83
|
purgeTtlDays: z.number().int().positive().optional(),
|
|
82
84
|
branchTtlDays: z.number().int().positive().nullable().optional(),
|
|
83
85
|
branchGcIntervalMs: z.number().int().positive().optional(),
|
|
84
86
|
maxUploadBytes: z.number().int().positive().optional(),
|
|
85
87
|
serverTiming: z.boolean().optional(),
|
|
86
|
-
maxInlineUnzipSize: z.number().int().
|
|
88
|
+
maxInlineUnzipSize: z.number().int().min(0).optional(),
|
|
89
|
+
maxZipEntries: z.number().int().positive().optional(),
|
|
90
|
+
maxUnzippedBytes: z.number().int().positive().optional(),
|
|
87
91
|
viewports: z.array(viewportSchema).min(1, "at least one viewport required").optional(),
|
|
88
|
-
adapters: z.record(z.string(), adapterSnapshotSchema).optional()
|
|
89
|
-
notifications: notificationsConfigSchema.optional()
|
|
92
|
+
adapters: z.record(z.string(), adapterSnapshotSchema).optional()
|
|
90
93
|
}).strict().superRefine((config, ctx) => {
|
|
91
94
|
if (config.previousSecret !== void 0 && config.secret === void 0) ctx.addIssue({
|
|
92
95
|
code: "custom",
|
|
@@ -137,6 +140,6 @@ function validateUiConfig(config) {
|
|
|
137
140
|
return result.data;
|
|
138
141
|
}
|
|
139
142
|
//#endregion
|
|
140
|
-
export { DEFAULT_MAX_UPLOAD_BYTES, shelfConfigSchema, uiConfigSchema, validateConfig, validateUiConfig };
|
|
143
|
+
export { ALLOW_SERVERLESS_ENV, DEFAULT_MAX_UPLOAD_BYTES, DEFAULT_SCRATCH_DIR, assertServerless, isServerlessHost, notificationsConfigSchema, resolveShelfOptions, shelfConfigSchema, uiConfigSchema, validateConfig, validateUiConfig };
|
|
141
144
|
|
|
142
145
|
//# sourceMappingURL=config.mjs.map
|
package/dist/config.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.mjs","names":[],"sources":["../src/config.ts"],"sourcesContent":["import type { Logger } from \"pino\";\nimport { z } from \"zod\";\nimport type { CaptureQueue } from \"./adapters/capture-queue.ts\";\nimport type { CaptureRunner } from \"./adapters/capture-runner.ts\";\nimport type { DatabaseAdapter } from \"./adapters/database.ts\";\nimport type { EmailSender } from \"./adapters/email-sender.ts\";\nimport type { GitHostProvider } from \"./adapters/git-host/index.ts\";\nimport type { AdapterCategory } from \"./adapters/metadata.ts\";\nimport type { NotifierProvider } from \"./adapters/notifier/provider.ts\";\nimport type { StorageAdapter } from \"./adapters/storage.ts\";\nimport type { Auth } from \"./auth.ts\";\n\n/** Brand color theme for the server-rendered UI. */\nexport interface BrandTheme {\n accent: string;\n accentContrast?: string;\n ring?: string;\n surface: { base: string; card: string; muted?: string; subtle?: string };\n text: { primary: string; secondary: string; muted?: string };\n border: string;\n borderSubtle?: string;\n status: { approved: string; new: string; rejected: string };\n sidebarBg?: string;\n topbarBg?: string;\n radius?: string;\n radiusSm?: string;\n shadow?: string;\n}\n\n/** Text overrides for the auth (login) UI. */\nexport interface AuthUiConfig {\n /** Page title, default \"Sign in\". */\n title?: string;\n /** Optional subtitle under the title. */\n subtitle?: string;\n /** Password field label, default \"Password\". */\n passwordLabel?: string;\n /** Password field placeholder. */\n passwordPlaceholder?: string;\n /** Submit button label, default \"Sign in\". */\n submitLabel?: string;\n /**\n * Template for SSO buttons, default \"Sign in with {label}\".\n * Must contain \"{label}\" which is replaced with the provider label.\n */\n ssoLabelTemplate?: string;\n /** Help text shown under the form. */\n helpText?: string;\n /** Footer text shown at the bottom of the card. */\n footerText?: string;\n}\n\n/** Branding overrides for the server-rendered UI. */\nexport interface UIConfig {\n name?: string;\n logo?: string;\n /**\n * @deprecated Ignored. The favicon is the product's own mark, served by the\n * app itself; set `logo` to rebrand the sidebar instead.\n */\n favicon?: string;\n lightTheme?: BrandTheme;\n darkTheme?: BrandTheme;\n auth?: AuthUiConfig;\n}\n\n/** Default cap for a single Storybook zip upload (1 GiB). */\nexport const DEFAULT_MAX_UPLOAD_BYTES = 1024 * 1024 * 1024;\n\n/** A viewport in which stories are captured. */\nexport interface ShelfViewport {\n name: string;\n width: number;\n height: number;\n}\n\n/** Metadata snapshot of a configured adapter. */\nexport interface AdapterSnapshot {\n name: string;\n version: string;\n description?: string;\n kind: string;\n category: AdapterCategory;\n}\n\n/** Site-wide notification defaults (brand + sender identity for admin alerts). */\nexport interface NotificationsConfig {\n fromEmail?: string;\n fromName?: string;\n footerText?: string;\n slackUsername?: string;\n}\n/** Shelf-level configuration (validated by {@link shelfConfigSchema}). */\nexport interface ShelfConfig {\n secret?: string;\n /**\n * The secret `secret` replaced. Used only to decrypt stored credentials\n * (webhook, git-token, and notification secrets) written before a rotation;\n * new writes always use `secret`. Remove once credentials are re-encrypted.\n */\n previousSecret?: string;\n /**\n * Re-encrypt credentials still under `previousSecret` at boot. Default false:\n * site admins can run it from the System page instead. No effect without\n * `previousSecret`.\n */\n migrateCredentialsOnBoot?: boolean;\n /**\n * Bootstrap site-admin bearer token (`STORYSHELF_ADMIN_TOKEN`/`ADMIN_TOKEN`).\n * Grants site-admin API access when no admin user exists yet; never\n * mint sessions from it. Distinct from `secret` (session signing).\n */\n adminToken?: string;\n publishedBaseDomain?: string;\n /**\n * Public base URL of this deployment (e.g. `https://shelf.example.com`).\n * Used as the issuer in the `/.well-known/openid-configuration` relying-party\n * helper document; falls back to the request origin when unset.\n */\n publicBaseUrl?: string;\n captureConcurrency?: number;\n scratchDir?: string;\n purgeTtlDays?: number;\n /** Branch baseline TTL, days; null disables branch GC (default 30). */\n branchTtlDays?: number | null;\n /** Branch GC interval, ms; daily sweep via interval clock (default 86_400_000). */\n branchGcIntervalMs?: number;\n maxUploadBytes?: number;\n /**\n * Emit `Server-Timing` response headers (total + db/storage roll-ups).\n * Opt-in, off by default — some deployments prefer no timing headers.\n */\n serverTiming?: boolean;\n /**\n * Max zip size (bytes) eligible for inline statics extraction at upload.\n * Unset (or 0) disables inline extraction — statics land via capture.\n */\n maxInlineUnzipSize?: number;\n viewports?: ShelfViewport[];\n adapters?: Record<string, AdapterSnapshot>;\n notifications?: NotificationsConfig;\n}\n\nconst viewportSchema: z.ZodType<ShelfViewport> = z.object({\n name: z.string().min(1),\n width: z.number().int().positive(),\n height: z.number().int().positive(),\n});\n\nconst brandThemeSchema: z.ZodType<BrandTheme> = z.object({\n accent: z.string(),\n accentContrast: z.string().optional(),\n ring: z.string().optional(),\n surface: z.object({\n base: z.string(),\n card: z.string(),\n muted: z.string().optional(),\n subtle: z.string().optional(),\n }),\n text: z.object({ primary: z.string(), secondary: z.string(), muted: z.string().optional() }),\n border: z.string(),\n borderSubtle: z.string().optional(),\n status: z.object({ approved: z.string(), new: z.string(), rejected: z.string() }),\n sidebarBg: z.string().optional(),\n topbarBg: z.string().optional(),\n radius: z.string().optional(),\n radiusSm: z.string().optional(),\n shadow: z.string().optional(),\n});\n\nconst adapterSnapshotSchema: z.ZodType<AdapterSnapshot> = z.object({\n name: z.string(),\n version: z.string(),\n description: z.string().optional(),\n kind: z.string(),\n category: z.enum([\n \"database\",\n \"storage\",\n \"capture-runner\",\n \"capture-queue\",\n \"git-host\",\n \"notifier\",\n ]),\n});\n\nconst authUiConfigSchema: z.ZodType<AuthUiConfig> = z.object({\n title: z.string().min(1).optional(),\n subtitle: z.string().optional(),\n passwordLabel: z.string().min(1).optional(),\n passwordPlaceholder: z.string().optional(),\n submitLabel: z.string().min(1).optional(),\n ssoLabelTemplate: z.string().min(1).optional(),\n helpText: z.string().optional(),\n footerText: z.string().optional(),\n});\n\nconst notificationsConfigSchema: z.ZodType<NotificationsConfig> = z.object({\n fromEmail: z.string().min(1).optional(),\n fromName: z.string().min(1).optional(),\n footerText: z.string().min(1).optional(),\n slackUsername: z.string().min(1).optional(),\n});\n\n/** Zod schema validating the shelf-level configuration. */\nexport const shelfConfigSchema: z.ZodType<ShelfConfig> = z\n .object({\n secret: z.string().min(1).optional(),\n previousSecret: z.string().min(1).optional(),\n migrateCredentialsOnBoot: z.boolean().optional(),\n adminToken: z.string().min(1).optional(),\n publishedBaseDomain: z.string().optional(),\n // oxlint-disable-next-line typescript/no-deprecated -- z.string().url() kept for zod v3 API compat\n publicBaseUrl: z.string().url().optional(),\n captureConcurrency: z.number().int().positive().optional(),\n scratchDir: z.string().optional(),\n purgeTtlDays: z.number().int().positive().optional(),\n branchTtlDays: z.number().int().positive().nullable().optional(),\n branchGcIntervalMs: z.number().int().positive().optional(),\n maxUploadBytes: z.number().int().positive().optional(),\n serverTiming: z.boolean().optional(),\n maxInlineUnzipSize: z.number().int().positive().optional(),\n viewports: z.array(viewportSchema).min(1, \"at least one viewport required\").optional(),\n adapters: z.record(z.string(), adapterSnapshotSchema).optional(),\n notifications: notificationsConfigSchema.optional(),\n })\n .strict()\n .superRefine((config, ctx) => {\n if (config.previousSecret !== undefined && config.secret === undefined) {\n ctx.addIssue({\n code: \"custom\",\n path: [\"previousSecret\"],\n message: \"previousSecret requires secret\",\n });\n }\n if (config.previousSecret !== undefined && config.previousSecret === config.secret) {\n ctx.addIssue({\n code: \"custom\",\n path: [\"previousSecret\"],\n message: \"previousSecret must differ from secret\",\n });\n }\n });\n\n/** Zod schema validating the UI branding configuration. */\nexport const uiConfigSchema: z.ZodType<UIConfig> = z\n .object({\n name: z.string().optional(),\n // oxlint-disable-next-line typescript/no-deprecated -- z.string().url() kept for zod v3 API compat\n logo: z.string().url().optional(),\n // Accepted but ignored (see UIConfig.favicon) so existing configs keep\n // validating under .strict().\n // oxlint-disable-next-line typescript/no-deprecated -- z.string().url() kept for zod v3 API compat\n favicon: z.string().url().optional(),\n lightTheme: brandThemeSchema.optional(),\n darkTheme: brandThemeSchema.optional(),\n auth: authUiConfigSchema.optional(),\n })\n .strict();\n\n/**\n * Parse and validate a raw shelf-level configuration object.\n *\n * @param config - Unvalidated configuration record.\n * @returns The validated shelf configuration.\n */\nexport function validateConfig(config: Record<string, unknown>): ShelfConfig {\n const result = shelfConfigSchema.safeParse(config);\n if (!result.success) {\n const issues = result.error.issues.map((i) => `${i.path.join(\".\")}: ${i.message}`).join(\"; \");\n throw new Error(`Invalid ShelfConfig: ${issues}`);\n }\n return result.data;\n}\n\n/**\n * Parse and validate a raw UI branding configuration object.\n *\n * @param config - Unvalidated UI configuration record.\n * @returns The validated UI configuration.\n */\nexport function validateUiConfig(config: Record<string, unknown>): UIConfig {\n const result = uiConfigSchema.safeParse(config);\n if (!result.success) {\n const issues = result.error.issues.map((i) => `${i.path.join(\".\")}: ${i.message}`).join(\"; \");\n throw new Error(`Invalid UIConfig: ${issues}`);\n }\n return result.data;\n}\n\n/** Adapter and configuration options for creating the shelf router. */\nexport interface ShelfOptions {\n database: DatabaseAdapter;\n storage: StorageAdapter;\n captureRunner?: CaptureRunner;\n captureQueue?: CaptureQueue;\n auth?: Auth;\n gitHosts?: GitHostProvider[];\n notifiers?: NotifierProvider[];\n emailSender?: EmailSender;\n logger?: Logger;\n ui?: UIConfig;\n config?: ShelfConfig;\n /**\n * Externally-managed observability SDK handle (e.g. from\n * `initObservabilityFromEnv`). Structural — core never imports the\n * observability package. Flushed on `app.lifecycle.teardown()`.\n */\n observability?: ObservabilityHandle;\n}\n\n/**\n * Shutdown hook for an externally-managed observability SDK.\n * Structural so core stays dependency-free.\n */\nexport interface ObservabilityHandle {\n /** Flush exporters and release SDK resources. Must be idempotent. */\n shutdown(): Promise<void>;\n}\n"],"mappings":";;;;;;AAmEA,MAAa,2BAA2B;AA4ExC,MAAM,iBAA2C,EAAE,OAAO;CACxD,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;CACtB,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS;CACjC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS;AACpC,CAAC;AAED,MAAM,mBAA0C,EAAE,OAAO;CACvD,QAAQ,EAAE,OAAO;CACjB,gBAAgB,EAAE,OAAO,CAAC,CAAC,SAAS;CACpC,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS;CAC1B,SAAS,EAAE,OAAO;EAChB,MAAM,EAAE,OAAO;EACf,MAAM,EAAE,OAAO;EACf,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS;EAC3B,QAAQ,EAAE,OAAO,CAAC,CAAC,SAAS;CAC9B,CAAC;CACD,MAAM,EAAE,OAAO;EAAE,SAAS,EAAE,OAAO;EAAG,WAAW,EAAE,OAAO;EAAG,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS;CAAE,CAAC;CAC3F,QAAQ,EAAE,OAAO;CACjB,cAAc,EAAE,OAAO,CAAC,CAAC,SAAS;CAClC,QAAQ,EAAE,OAAO;EAAE,UAAU,EAAE,OAAO;EAAG,KAAK,EAAE,OAAO;EAAG,UAAU,EAAE,OAAO;CAAE,CAAC;CAChF,WAAW,EAAE,OAAO,CAAC,CAAC,SAAS;CAC/B,UAAU,EAAE,OAAO,CAAC,CAAC,SAAS;CAC9B,QAAQ,EAAE,OAAO,CAAC,CAAC,SAAS;CAC5B,UAAU,EAAE,OAAO,CAAC,CAAC,SAAS;CAC9B,QAAQ,EAAE,OAAO,CAAC,CAAC,SAAS;AAC9B,CAAC;AAED,MAAM,wBAAoD,EAAE,OAAO;CACjE,MAAM,EAAE,OAAO;CACf,SAAS,EAAE,OAAO;CAClB,aAAa,EAAE,OAAO,CAAC,CAAC,SAAS;CACjC,MAAM,EAAE,OAAO;CACf,UAAU,EAAE,KAAK;EACf;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;AACH,CAAC;AAED,MAAM,qBAA8C,EAAE,OAAO;CAC3D,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;CAClC,UAAU,EAAE,OAAO,CAAC,CAAC,SAAS;CAC9B,eAAe,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;CAC1C,qBAAqB,EAAE,OAAO,CAAC,CAAC,SAAS;CACzC,aAAa,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;CACxC,kBAAkB,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;CAC7C,UAAU,EAAE,OAAO,CAAC,CAAC,SAAS;CAC9B,YAAY,EAAE,OAAO,CAAC,CAAC,SAAS;AAClC,CAAC;AAED,MAAM,4BAA4D,EAAE,OAAO;CACzE,WAAW,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;CACtC,UAAU,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;CACrC,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;CACvC,eAAe,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;AAC5C,CAAC;;AAGD,MAAa,oBAA4C,EACtD,OAAO;CACN,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;CACnC,gBAAgB,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;CAC3C,0BAA0B,EAAE,QAAQ,CAAC,CAAC,SAAS;CAC/C,YAAY,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;CACvC,qBAAqB,EAAE,OAAO,CAAC,CAAC,SAAS;CAEzC,eAAe,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS;CACzC,oBAAoB,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS;CACzD,YAAY,EAAE,OAAO,CAAC,CAAC,SAAS;CAChC,cAAc,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS;CACnD,eAAe,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS;CAC/D,oBAAoB,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS;CACzD,gBAAgB,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS;CACrD,cAAc,EAAE,QAAQ,CAAC,CAAC,SAAS;CACnC,oBAAoB,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS;CACzD,WAAW,EAAE,MAAM,cAAc,CAAC,CAAC,IAAI,GAAG,gCAAgC,CAAC,CAAC,SAAS;CACrF,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,qBAAqB,CAAC,CAAC,SAAS;CAC/D,eAAe,0BAA0B,SAAS;AACpD,CAAC,CAAC,CACD,OAAO,CAAC,CACR,aAAa,QAAQ,QAAQ;CAC5B,IAAI,OAAO,mBAAmB,KAAA,KAAa,OAAO,WAAW,KAAA,GAC3D,IAAI,SAAS;EACX,MAAM;EACN,MAAM,CAAC,gBAAgB;EACvB,SAAS;CACX,CAAC;CAEH,IAAI,OAAO,mBAAmB,KAAA,KAAa,OAAO,mBAAmB,OAAO,QAC1E,IAAI,SAAS;EACX,MAAM;EACN,MAAM,CAAC,gBAAgB;EACvB,SAAS;CACX,CAAC;AAEL,CAAC;;AAGH,MAAa,iBAAsC,EAChD,OAAO;CACN,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS;CAE1B,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS;CAIhC,SAAS,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS;CACnC,YAAY,iBAAiB,SAAS;CACtC,WAAW,iBAAiB,SAAS;CACrC,MAAM,mBAAmB,SAAS;AACpC,CAAC,CAAC,CACD,OAAO;;;;;;;AAQV,SAAgB,eAAe,QAA8C;CAC3E,MAAM,SAAS,kBAAkB,UAAU,MAAM;CACjD,IAAI,CAAC,OAAO,SAAS;EACnB,MAAM,SAAS,OAAO,MAAM,OAAO,KAAK,MAAM,GAAG,EAAE,KAAK,KAAK,GAAG,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,KAAK,IAAI;EAC5F,MAAM,IAAI,MAAM,wBAAwB,QAAQ;CAClD;CACA,OAAO,OAAO;AAChB;;;;;;;AAQA,SAAgB,iBAAiB,QAA2C;CAC1E,MAAM,SAAS,eAAe,UAAU,MAAM;CAC9C,IAAI,CAAC,OAAO,SAAS;EACnB,MAAM,SAAS,OAAO,MAAM,OAAO,KAAK,MAAM,GAAG,EAAE,KAAK,KAAK,GAAG,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,KAAK,IAAI;EAC5F,MAAM,IAAI,MAAM,qBAAqB,QAAQ;CAC/C;CACA,OAAO,OAAO;AAChB"}
|
|
1
|
+
{"version":3,"file":"config.mjs","names":[],"sources":["../src/config.ts"],"sourcesContent":["import type { Logger } from \"pino\";\nimport { z } from \"zod\";\nimport type { CaptureRunner } from \"./adapters/capture-runner.ts\";\nimport type { DatabaseAdapter } from \"./adapters/database.ts\";\nimport type { DesignProvider } from \"./adapters/design.ts\";\nimport type { EmailSender } from \"./adapters/email-sender.ts\";\nimport type { AdapterCategory } from \"./adapters/metadata.ts\";\nimport type { NotifierProvider } from \"./adapters/notifier/provider.ts\";\nimport type { StorageAdapter } from \"./adapters/storage.ts\";\nimport type { Ai } from \"./ai.ts\";\nimport type { Auth } from \"./auth.ts\";\n\n/** Brand color theme for the server-rendered UI. */\nexport interface BrandTheme {\n accent: string;\n accentContrast?: string;\n ring?: string;\n surface: { base: string; card: string; muted?: string; subtle?: string };\n text: { primary: string; secondary: string; muted?: string };\n border: string;\n borderSubtle?: string;\n status: { approved: string; new: string; rejected: string };\n sidebarBg?: string;\n topbarBg?: string;\n radius?: string;\n radiusSm?: string;\n shadow?: string;\n}\n\n/** Text overrides for the auth (login) UI. */\nexport interface AuthUiConfig {\n /** Page title, default \"Sign in\". */\n title?: string;\n /** Optional subtitle under the title. */\n subtitle?: string;\n /** Password field label, default \"Password\". */\n passwordLabel?: string;\n /** Password field placeholder. */\n passwordPlaceholder?: string;\n /** Submit button label, default \"Sign in\". */\n submitLabel?: string;\n /**\n * Template for SSO buttons, default \"Sign in with {label}\".\n * Must contain \"{label}\" which is replaced with the provider label.\n */\n ssoLabelTemplate?: string;\n /** Help text shown under the form. */\n helpText?: string;\n /** Footer text shown at the bottom of the card. */\n footerText?: string;\n}\n\n/** Branding overrides for the server-rendered UI. */\nexport interface UIConfig {\n name?: string;\n logo?: string;\n /**\n * @deprecated Ignored. The favicon is the product's own mark, served by the\n * app itself; set `logo` to rebrand the sidebar instead.\n */\n favicon?: string;\n lightTheme?: BrandTheme;\n darkTheme?: BrandTheme;\n auth?: AuthUiConfig;\n}\n\n/** Default cap for a single Storybook zip upload (1 GiB). */\nexport const DEFAULT_MAX_UPLOAD_BYTES = 1024 * 1024 * 1024;\n\n/** A viewport in which stories are captured. */\nexport interface ShelfViewport {\n name: string;\n width: number;\n height: number;\n}\n\n/** Metadata snapshot of a configured adapter. */\nexport interface AdapterSnapshot {\n name: string;\n version: string;\n description?: string;\n kind: string;\n category: AdapterCategory;\n}\n\n/** Site-wide notification defaults (brand + sender identity for admin alerts). */\nexport interface NotificationsConfig {\n fromEmail?: string;\n fromName?: string;\n footerText?: string;\n slackUsername?: string;\n}\n/** Shelf-level configuration (validated by {@link shelfConfigSchema}). */\nexport interface ShelfConfig {\n secret?: string;\n /**\n * The secret `secret` replaced. Used only to decrypt stored credentials\n * (webhook, git-token, and notification secrets) written before a rotation;\n * new writes always use `secret`. Remove once credentials are re-encrypted.\n */\n previousSecret?: string;\n /**\n * Re-encrypt credentials still under `previousSecret` at boot. Default false:\n * site admins can run it from the System page instead. No effect without\n * `previousSecret`.\n */\n migrateCredentialsOnBoot?: boolean;\n /**\n * Bootstrap site-admin bearer token (`STORYSHELF_ADMIN_TOKEN`/`ADMIN_TOKEN`).\n * Grants site-admin API access when no admin user exists yet; never\n * mint sessions from it. Distinct from `secret` (session signing).\n */\n adminToken?: string;\n publishedBaseDomain?: string;\n /**\n * Public base URL of this deployment (e.g. `https://shelf.example.com`).\n * Used as the issuer in the `/.well-known/openid-configuration` relying-party\n * helper document; falls back to the request origin when unset.\n */\n publicBaseUrl?: string;\n /**\n * Working directory for capture and inline statics extraction. Defaults to\n * `<os.tmpdir()>/storyshelf`; publishing and capture extract there.\n */\n scratchDir?: string;\n purgeTtlDays?: number;\n /** Branch baseline TTL, days; null disables branch GC (default 30). */\n branchTtlDays?: number | null;\n /** Branch GC interval, ms; daily sweep via interval clock (default 86_400_000). */\n branchGcIntervalMs?: number;\n maxUploadBytes?: number;\n /**\n * Emit `Server-Timing` response headers (total + db/storage roll-ups).\n * Opt-in, off by default — some deployments prefer no timing headers.\n */\n serverTiming?: boolean;\n /**\n * Largest zip (bytes) extracted inline in the upload request; a larger zip is published by a\n * `publish` job (or refused with 413 on a host that runs no consumer). The count is taken\n * while the upload streams, never from `Content-Length`. Default 50 MB; `0` sends every zip to\n * a `publish` job.\n */\n maxInlineUnzipSize?: number;\n /** Most entries an uploaded Storybook zip may hold (default 50 000); more fails the publish. */\n maxZipEntries?: number;\n /**\n * Most uncompressed bytes an uploaded Storybook zip may expand to (default 2 GiB), counted while\n * extracting; more fails the publish. The compressed size is no bound on its own.\n */\n maxUnzippedBytes?: number;\n viewports?: ShelfViewport[];\n adapters?: Record<string, AdapterSnapshot>;\n}\n\nconst viewportSchema: z.ZodType<ShelfViewport> = z.object({\n name: z.string().min(1),\n width: z.number().int().positive(),\n height: z.number().int().positive(),\n});\n\nconst brandThemeSchema: z.ZodType<BrandTheme> = z.object({\n accent: z.string(),\n accentContrast: z.string().optional(),\n ring: z.string().optional(),\n surface: z.object({\n base: z.string(),\n card: z.string(),\n muted: z.string().optional(),\n subtle: z.string().optional(),\n }),\n text: z.object({ primary: z.string(), secondary: z.string(), muted: z.string().optional() }),\n border: z.string(),\n borderSubtle: z.string().optional(),\n status: z.object({ approved: z.string(), new: z.string(), rejected: z.string() }),\n sidebarBg: z.string().optional(),\n topbarBg: z.string().optional(),\n radius: z.string().optional(),\n radiusSm: z.string().optional(),\n shadow: z.string().optional(),\n});\n\nconst adapterSnapshotSchema: z.ZodType<AdapterSnapshot> = z.object({\n name: z.string(),\n version: z.string(),\n description: z.string().optional(),\n kind: z.string(),\n category: z.enum([\n \"database\",\n \"storage\",\n \"capture-runner\",\n \"capture-queue\",\n \"design\",\n \"notifier\",\n ]),\n});\n\nconst authUiConfigSchema: z.ZodType<AuthUiConfig> = z.object({\n title: z.string().min(1).optional(),\n subtitle: z.string().optional(),\n passwordLabel: z.string().min(1).optional(),\n passwordPlaceholder: z.string().optional(),\n submitLabel: z.string().min(1).optional(),\n ssoLabelTemplate: z.string().min(1).optional(),\n helpText: z.string().optional(),\n footerText: z.string().optional(),\n});\n\n/** Zod schema validating `notifications.config`. */\nexport const notificationsConfigSchema: z.ZodType<NotificationsConfig> = z.object({\n fromEmail: z.string().min(1).optional(),\n fromName: z.string().min(1).optional(),\n footerText: z.string().min(1).optional(),\n slackUsername: z.string().min(1).optional(),\n});\n\n/** Zod schema validating the shelf-level configuration. */\nexport const shelfConfigSchema: z.ZodType<ShelfConfig> = z\n .object({\n secret: z.string().min(1).optional(),\n previousSecret: z.string().min(1).optional(),\n migrateCredentialsOnBoot: z.boolean().optional(),\n adminToken: z.string().min(1).optional(),\n publishedBaseDomain: z.string().optional(),\n // oxlint-disable-next-line typescript/no-deprecated -- z.string().url() kept for zod v3 API compat\n publicBaseUrl: z.string().url().optional(),\n scratchDir: z.string().optional(),\n purgeTtlDays: z.number().int().positive().optional(),\n branchTtlDays: z.number().int().positive().nullable().optional(),\n branchGcIntervalMs: z.number().int().positive().optional(),\n maxUploadBytes: z.number().int().positive().optional(),\n serverTiming: z.boolean().optional(),\n maxInlineUnzipSize: z.number().int().min(0).optional(),\n maxZipEntries: z.number().int().positive().optional(),\n maxUnzippedBytes: z.number().int().positive().optional(),\n viewports: z.array(viewportSchema).min(1, \"at least one viewport required\").optional(),\n adapters: z.record(z.string(), adapterSnapshotSchema).optional(),\n })\n .strict()\n .superRefine((config, ctx) => {\n if (config.previousSecret !== undefined && config.secret === undefined) {\n ctx.addIssue({\n code: \"custom\",\n path: [\"previousSecret\"],\n message: \"previousSecret requires secret\",\n });\n }\n if (config.previousSecret !== undefined && config.previousSecret === config.secret) {\n ctx.addIssue({\n code: \"custom\",\n path: [\"previousSecret\"],\n message: \"previousSecret must differ from secret\",\n });\n }\n });\n\n/** Zod schema validating the UI branding configuration. */\nexport const uiConfigSchema: z.ZodType<UIConfig> = z\n .object({\n name: z.string().optional(),\n // oxlint-disable-next-line typescript/no-deprecated -- z.string().url() kept for zod v3 API compat\n logo: z.string().url().optional(),\n // Accepted but ignored (see UIConfig.favicon) so existing configs keep\n // validating under .strict().\n // oxlint-disable-next-line typescript/no-deprecated -- z.string().url() kept for zod v3 API compat\n favicon: z.string().url().optional(),\n lightTheme: brandThemeSchema.optional(),\n darkTheme: brandThemeSchema.optional(),\n auth: authUiConfigSchema.optional(),\n })\n .strict();\n\n/**\n * Parse and validate a raw shelf-level configuration object.\n *\n * @param config - Unvalidated configuration record.\n * @returns The validated shelf configuration.\n */\nexport function validateConfig(config: Record<string, unknown>): ShelfConfig {\n const result = shelfConfigSchema.safeParse(config);\n if (!result.success) {\n const issues = result.error.issues.map((i) => `${i.path.join(\".\")}: ${i.message}`).join(\"; \");\n throw new Error(`Invalid ShelfConfig: ${issues}`);\n }\n return result.data;\n}\n\n/**\n * Parse and validate a raw UI branding configuration object.\n *\n * @param config - Unvalidated UI configuration record.\n * @returns The validated UI configuration.\n */\nexport function validateUiConfig(config: Record<string, unknown>): UIConfig {\n const result = uiConfigSchema.safeParse(config);\n if (!result.success) {\n const issues = result.error.issues.map((i) => `${i.path.join(\".\")}: ${i.message}`).join(\"; \");\n throw new Error(`Invalid UIConfig: ${issues}`);\n }\n return result.data;\n}\n\n/** Testing options: what renders, how many run at once, and how screenshots are compared. */\nexport interface ShelfTestingOptions {\n /** Pure renderer. Required: `testing` without a `runner` throws. */\n runner: CaptureRunner;\n /** Jobs this instance runs at once; auto-sized from CPU and memory when unset. */\n concurrency?: number;\n /**\n * Retries after a first failed attempt before a capture job fails for good (default 2: three\n * attempts). One limit covers failed attempts, lost leases and forked-child crashes.\n */\n retries?: number;\n /**\n * Lease of a job of the database queue, in seconds (default 300: 5 minutes). A running job\n * renews it on a heartbeat (every `min(30 s, lease / 3)`) and on progress, so a stage that prints\n * nothing is not redelivered while its process lives; a crashed holder's job is claimed by\n * another instance once the lease runs out, so a shorter lease recovers crashes sooner.\n */\n leaseSeconds?: number;\n /**\n * Module run in a forked capture child (it calls `runCaptureChild` from `@storyshelf/core/capture`).\n * The app consumes the queue and pushes each job to the child over IPC. Needs `runner`, which\n * serves as the in-process fallback.\n */\n workerEntry?: string;\n /**\n * Split a big build across the queue: captures (story x viewport x engine) per shard. A build of\n * up to twice this size still renders in one piece. Unset keeps every build in one job. Needs\n * `runner`. With `workerEntry` the forked child plans and renders shards and puts them on the\n * database queue itself.\n */\n shardSize?: number;\n /**\n * Requests per minute one client IP may make to the signed Storybook URL route, `/_s/<token>/*`\n * (ADR 0007, decision 5). Default 6000, enough for a render fetching thousands of assets from one\n * farm IP; a larger farm behind one address raises it. Over the limit the route answers `429`\n * before it looks at the token, so the answer says nothing about a token's validity.\n */\n storybookUrlRateLimit?: number;\n /**\n * Where captures run. `auto` (default) forks when `workerEntry` is set and the host can afford\n * a second process; `in-process` never forks; `fork` forks regardless of the host and fails at\n * boot if it cannot.\n */\n mode?: \"auto\" | \"in-process\" | \"fork\";\n /** Screenshot comparison, shared by capture and the re-diff endpoint. */\n compare?: ShelfCompareOptions;\n /** Image library selection; see {@link ShelfImagesOptions}. */\n images?: ShelfImagesOptions;\n /**\n * Clock and timers of the database job queue. Tests inject a manual clock so lease and poll\n * transitions do not depend on wall time; production leaves it unset (`Date.now` and the global\n * timers).\n */\n queueClock?: {\n now: () => number;\n timers: {\n setTimeout(callback: () => void, ms: number): unknown;\n clearTimeout(handle: unknown): void;\n };\n };\n}\n\n/** Notification options: who can deliver, over which transport, with what branding. */\nexport interface ShelfNotificationsOptions {\n /** Provider adapters (Slack, Teams, email, ...); a channel's `provider` must name one of these. */\n notifiers?: NotifierProvider[];\n /** Email transport. Listed here so its setup/teardown/validation hooks run with the app's. */\n emailSender?: EmailSender;\n /** Site-wide brand and sender settings for outgoing notifications. */\n config?: NotificationsConfig;\n}\n\n/** Comparison options. */\nexport interface ShelfCompareOptions {\n /**\n * Worker threads for pixel diffs: default CPU count minus one (min 1), `0` diffs on the main\n * thread. Takes precedence over `STORYSHELF_DIFF_WORKERS`.\n */\n threads?: number;\n}\n\n/** Image-handling options: which library decodes, encodes and resizes the PNGs. */\nexport interface ShelfImagesOptions {\n /**\n * Image library for diffs, diff overlays and AI thumbnails: `\"pngjs\"` (default, pure JavaScript,\n * runs anywhere) or `\"sharp\"` (native, several times faster on large PNGs; needs the optional\n * `sharp` package installed). Falls back to `STORYSHELF_IMAGE_ENGINE`. The pixel comparison is\n * pixelmatch either way, so verdicts match.\n */\n engine?: \"pngjs\" | \"sharp\";\n}\n\n/** Adapter and configuration options for creating the shelf router. */\nexport interface ShelfOptions {\n database: DatabaseAdapter;\n storage: StorageAdapter;\n /**\n * Testing (render + diff in the background). Absent means Storybook hosting only: no renderer,\n * no queue, and the tuning below is unreachable rather than silently ignored.\n */\n testing?: ShelfTestingOptions;\n auth?: Auth;\n /** AI insights surface (ADR 0016). Set ⇒ AI on site-wide; absent ⇒ off. */\n ai?: Ai;\n /** Design-tool providers (Figma, …) that resolve links, metadata and previews. */\n design?: DesignProvider[];\n /** Notification subsystem: providers, the email transport, and shared brand/sender settings. */\n notifications?: ShelfNotificationsOptions;\n logger?: Logger;\n ui?: UIConfig;\n config?: ShelfConfig;\n /**\n * Externally-managed observability SDK handle (e.g. from\n * `initObservabilityFromEnv`). Structural — core never imports the\n * observability package. Flushed on `app.lifecycle.teardown()`.\n */\n observability?: ObservabilityHandle;\n}\n\n/**\n * Shutdown hook for an externally-managed observability SDK.\n * Structural so core stays dependency-free.\n */\nexport interface ObservabilityHandle {\n /** Flush exporters and release SDK resources. Must be idempotent. */\n shutdown(): Promise<void>;\n}\nexport { DEFAULT_SCRATCH_DIR, resolveShelfOptions } from \"./options.ts\";\nexport { ALLOW_SERVERLESS_ENV, assertServerless, isServerlessHost } from \"./serverless.ts\";\n"],"mappings":"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"}
|
package/dist/diff.d.mts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
|
-
import { DEFAULT_DIFF_OPTIONS, DiffOptions, DiffResult } from "
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import { DEFAULT_DIFF_OPTIONS, DIFF_WORKERS_ENV, DiffOptions, DiffResult, IMAGE_ENGINE_ENV, ImageEngine, configureImages, diffImages, diffImagesAsync, shutdownDiffPool, verifyImageEngine } from "@storyshelf/image";
|
|
4
|
+
//#region src/diff/index.d.ts
|
|
5
|
+
/** @deprecated Use `configureImages`, which also takes the image engine. */
|
|
6
|
+
declare function configureDiffPool(options: {
|
|
7
|
+
threads?: number;
|
|
8
|
+
}): void;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { DEFAULT_DIFF_OPTIONS, DIFF_WORKERS_ENV, type DiffOptions, type DiffResult, IMAGE_ENGINE_ENV, type ImageEngine, configureDiffPool, configureImages, diffImages, diffImagesAsync, shutdownDiffPool, verifyImageEngine };
|
|
11
|
+
//# sourceMappingURL=diff.d.mts.map
|
package/dist/diff.mjs
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
import { diffImages } from "
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
import { DEFAULT_DIFF_OPTIONS, DIFF_WORKERS_ENV, IMAGE_ENGINE_ENV, configureImages, configureImages as configureImages$1, diffImages, diffImagesAsync, shutdownDiffPool, verifyImageEngine } from "@storyshelf/image";
|
|
5
|
+
//#region src/diff/index.ts
|
|
6
|
+
/**
|
|
7
|
+
* Pixel diff engine (re-exported from `@storyshelf/image`, which owns the codecs and worker pool).
|
|
8
|
+
*
|
|
9
|
+
* `diffImages` compares a baseline buffer against a current screenshot; `diffImagesAsync` does
|
|
10
|
+
* the same on a worker-thread pool; `DiffOptions` tunes thresholds (also settable per-project).
|
|
11
|
+
*/
|
|
12
|
+
/** @deprecated Use `configureImages`, which also takes the image engine. */
|
|
13
|
+
function configureDiffPool(options) {
|
|
14
|
+
configureImages$1(options);
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { DEFAULT_DIFF_OPTIONS, DIFF_WORKERS_ENV, IMAGE_ENGINE_ENV, configureDiffPool, configureImages, diffImages, diffImagesAsync, shutdownDiffPool, verifyImageEngine };
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=diff.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.mjs","names":[],"sources":["../src/diff/index.ts"],"sourcesContent":["/**\n * Pixel diff engine (re-exported from `@storyshelf/image`, which owns the codecs and worker pool).\n *\n * `diffImages` compares a baseline buffer against a current screenshot; `diffImagesAsync` does\n * the same on a worker-thread pool; `DiffOptions` tunes thresholds (also settable per-project).\n */\nimport { configureImages } from \"@storyshelf/image\";\n\nexport {\n configureImages,\n DEFAULT_DIFF_OPTIONS,\n DIFF_WORKERS_ENV,\n diffImages,\n diffImagesAsync,\n type DiffOptions,\n type DiffResult,\n IMAGE_ENGINE_ENV,\n type ImageEngine,\n shutdownDiffPool,\n verifyImageEngine,\n} from \"@storyshelf/image\";\n\n/** @deprecated Use `configureImages`, which also takes the image engine. */\nexport function configureDiffPool(options: { threads?: number }): void {\n configureImages(options);\n}\n"],"mappings":";;;;;;;;;;;;AAuBA,SAAgB,kBAAkB,SAAqC;CACrE,kBAAgB,OAAO;AACzB"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import __tsdown_shims_path from 'node:path';
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { Logger, LoggerOptions, PinoTransport, createShelfLogger } from "./logger.mjs";
|
|
3
4
|
import { BUILD_STATUSES, BuildStatus, ProjectRole, SNAPSHOT_STATUSES, SiteRole, SnapshotStatus, TERMINAL_BUILD_STATUSES } from "./types.mjs";
|
|
4
5
|
import { AdapterSnapshot, BrandTheme, DEFAULT_MAX_UPLOAD_BYTES, ShelfConfig, ShelfOptions, ShelfViewport, UIConfig, shelfConfigSchema, uiConfigSchema, validateConfig, validateUiConfig } from "./config.mjs";
|
|
5
|
-
import { Logger, LoggerOptions, PinoTransport, createShelfLogger } from "./logger.mjs";
|
|
6
6
|
import { BrandPack, brandPack, brandPacks } from "./brand-packs.mjs";
|
|
7
7
|
export { type AdapterSnapshot, BUILD_STATUSES, type BrandPack, type BrandTheme, type BuildStatus, DEFAULT_MAX_UPLOAD_BYTES, type Logger, type LoggerOptions, type PinoTransport, type ProjectRole, SNAPSHOT_STATUSES, type ShelfConfig, type ShelfOptions, type ShelfViewport, type SiteRole, type SnapshotStatus, TERMINAL_BUILD_STATUSES, type UIConfig, brandPack, brandPacks, createShelfLogger, shelfConfigSchema, uiConfigSchema, validateConfig, validateUiConfig };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { AiBudget } from "../ai.mjs";
|
|
4
|
+
//#region src/insights/budget.d.ts
|
|
5
|
+
/** Alert thresholds as percentages of the daily cap. */
|
|
6
|
+
declare const BUDGET_THRESHOLDS: readonly [50, 75, 90, 100];
|
|
7
|
+
/** UTC day key (`YYYY-MM-DD`). */
|
|
8
|
+
declare function utcDay(date: Date): string;
|
|
9
|
+
/** Start of the UTC day containing `date`, as an ISO string. */
|
|
10
|
+
declare function utcDayStart(date: Date): string;
|
|
11
|
+
/** Tokens counted toward the budget for one call (vision weighting applies). */
|
|
12
|
+
declare function countedTokens(usage: {
|
|
13
|
+
inputTokens: number;
|
|
14
|
+
outputTokens: number;
|
|
15
|
+
}, images: number, budget: Pick<AiBudget, "visionWeight">): number;
|
|
16
|
+
/** Thresholds crossed when moving from `before` to `after` tokens of `cap`. */
|
|
17
|
+
declare function crossedThresholds(before: number, after: number, cap: number): number[];
|
|
18
|
+
//#endregion
|
|
19
|
+
export { BUDGET_THRESHOLDS, countedTokens, crossedThresholds, utcDay, utcDayStart };
|
|
20
|
+
//# sourceMappingURL=budget.d.mts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
//#region src/insights/budget.ts
|
|
5
|
+
/** Alert thresholds as percentages of the daily cap. */
|
|
6
|
+
const BUDGET_THRESHOLDS = [
|
|
7
|
+
50,
|
|
8
|
+
75,
|
|
9
|
+
90,
|
|
10
|
+
100
|
|
11
|
+
];
|
|
12
|
+
/** UTC day key (`YYYY-MM-DD`). */
|
|
13
|
+
function utcDay(date) {
|
|
14
|
+
return date.toISOString().slice(0, 10);
|
|
15
|
+
}
|
|
16
|
+
/** Start of the UTC day containing `date`, as an ISO string. */
|
|
17
|
+
function utcDayStart(date) {
|
|
18
|
+
return `${utcDay(date)}T00:00:00.000Z`;
|
|
19
|
+
}
|
|
20
|
+
/** Tokens counted toward the budget for one call (vision weighting applies). */
|
|
21
|
+
function countedTokens(usage, images, budget) {
|
|
22
|
+
const raw = usage.inputTokens + usage.outputTokens;
|
|
23
|
+
return images > 0 ? Math.ceil(raw * budget.visionWeight) : raw;
|
|
24
|
+
}
|
|
25
|
+
/** Thresholds crossed when moving from `before` to `after` tokens of `cap`. */
|
|
26
|
+
function crossedThresholds(before, after, cap) {
|
|
27
|
+
if (cap <= 0) return [];
|
|
28
|
+
return BUDGET_THRESHOLDS.filter((t) => before < cap * t / 100 && after >= cap * t / 100);
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
export { BUDGET_THRESHOLDS, countedTokens, crossedThresholds, utcDay, utcDayStart };
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=budget.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget.mjs","names":[],"sources":["../../src/insights/budget.ts"],"sourcesContent":["/** Pure budget arithmetic (thresholds, UTC day windows, weighted counting). */\nimport type { AiBudget } from \"../ai.ts\";\n\n/** Alert thresholds as percentages of the daily cap. */\nexport const BUDGET_THRESHOLDS = [50, 75, 90, 100] as const;\n\n/** UTC day key (`YYYY-MM-DD`). */\nexport function utcDay(date: Date): string {\n return date.toISOString().slice(0, 10);\n}\n\n/** Start of the UTC day containing `date`, as an ISO string. */\nexport function utcDayStart(date: Date): string {\n return `${utcDay(date)}T00:00:00.000Z`;\n}\n\n/** Tokens counted toward the budget for one call (vision weighting applies). */\nexport function countedTokens(\n usage: { inputTokens: number; outputTokens: number },\n images: number,\n budget: Pick<AiBudget, \"visionWeight\">,\n): number {\n const raw = usage.inputTokens + usage.outputTokens;\n return images > 0 ? Math.ceil(raw * budget.visionWeight) : raw;\n}\n\n/** Thresholds crossed when moving from `before` to `after` tokens of `cap`. */\nexport function crossedThresholds(before: number, after: number, cap: number): number[] {\n if (cap <= 0) {\n return [];\n }\n return BUDGET_THRESHOLDS.filter((t) => before < (cap * t) / 100 && after >= (cap * t) / 100);\n}\n"],"mappings":";;;;;AAIA,MAAa,oBAAoB;CAAC;CAAI;CAAI;CAAI;AAAG;;AAGjD,SAAgB,OAAO,MAAoB;CACzC,OAAO,KAAK,YAAY,CAAC,CAAC,MAAM,GAAG,EAAE;AACvC;;AAGA,SAAgB,YAAY,MAAoB;CAC9C,OAAO,GAAG,OAAO,IAAI,EAAE;AACzB;;AAGA,SAAgB,cACd,OACA,QACA,QACQ;CACR,MAAM,MAAM,MAAM,cAAc,MAAM;CACtC,OAAO,SAAS,IAAI,KAAK,KAAK,MAAM,OAAO,YAAY,IAAI;AAC7D;;AAGA,SAAgB,kBAAkB,QAAgB,OAAe,KAAuB;CACtF,IAAI,OAAO,GACT,OAAO,CAAC;CAEV,OAAO,kBAAkB,QAAQ,MAAM,SAAU,MAAM,IAAK,OAAO,SAAU,MAAM,IAAK,GAAG;AAC7F"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
//#region src/insights/envelope.d.ts
|
|
5
|
+
/** Build triage envelope. */
|
|
6
|
+
declare const triageEnvelopeSchema: z.ZodObject<{
|
|
7
|
+
verdict: z.ZodEnum<{
|
|
8
|
+
"likely-intended": "likely-intended";
|
|
9
|
+
"likely-regression": "likely-regression";
|
|
10
|
+
"needs-review": "needs-review";
|
|
11
|
+
}>;
|
|
12
|
+
summary: z.ZodString;
|
|
13
|
+
items: z.ZodArray<z.ZodObject<{
|
|
14
|
+
snapshotKey: z.ZodString;
|
|
15
|
+
note: z.ZodString;
|
|
16
|
+
severity: z.ZodEnum<{
|
|
17
|
+
high: "high";
|
|
18
|
+
info: "info";
|
|
19
|
+
low: "low";
|
|
20
|
+
medium: "medium";
|
|
21
|
+
}>;
|
|
22
|
+
}, z.core.$strip>>;
|
|
23
|
+
confidence: z.ZodEnum<{
|
|
24
|
+
high: "high";
|
|
25
|
+
low: "low";
|
|
26
|
+
medium: "medium";
|
|
27
|
+
}>;
|
|
28
|
+
}, z.core.$strip>;
|
|
29
|
+
/** Project-health digest envelope. */
|
|
30
|
+
declare const healthEnvelopeSchema: z.ZodObject<{
|
|
31
|
+
verdict: z.ZodEnum<{
|
|
32
|
+
healthy: "healthy";
|
|
33
|
+
unhealthy: "unhealthy";
|
|
34
|
+
watch: "watch";
|
|
35
|
+
}>;
|
|
36
|
+
summary: z.ZodString;
|
|
37
|
+
score: z.ZodNumber;
|
|
38
|
+
trends: z.ZodArray<z.ZodObject<{
|
|
39
|
+
label: z.ZodString;
|
|
40
|
+
note: z.ZodString;
|
|
41
|
+
direction: z.ZodEnum<{
|
|
42
|
+
down: "down";
|
|
43
|
+
flat: "flat";
|
|
44
|
+
up: "up";
|
|
45
|
+
}>;
|
|
46
|
+
}, z.core.$strip>>;
|
|
47
|
+
confidence: z.ZodEnum<{
|
|
48
|
+
high: "high";
|
|
49
|
+
low: "low";
|
|
50
|
+
medium: "medium";
|
|
51
|
+
}>;
|
|
52
|
+
}, z.core.$strip>;
|
|
53
|
+
/** A parsed triage result. */
|
|
54
|
+
type TriageEnvelope = z.infer<typeof triageEnvelopeSchema>;
|
|
55
|
+
/** A parsed health result. */
|
|
56
|
+
type HealthEnvelope = z.infer<typeof healthEnvelopeSchema>;
|
|
57
|
+
//#endregion
|
|
58
|
+
export { HealthEnvelope, TriageEnvelope, healthEnvelopeSchema, triageEnvelopeSchema };
|
|
59
|
+
//# sourceMappingURL=envelope.d.mts.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
//#region src/insights/envelope.ts
|
|
6
|
+
/** Result envelopes the model must produce (validated; stored as opaque JSON text). */
|
|
7
|
+
const severitySchema = z.enum([
|
|
8
|
+
"info",
|
|
9
|
+
"low",
|
|
10
|
+
"medium",
|
|
11
|
+
"high"
|
|
12
|
+
]);
|
|
13
|
+
const confidenceSchema = z.enum([
|
|
14
|
+
"low",
|
|
15
|
+
"medium",
|
|
16
|
+
"high"
|
|
17
|
+
]);
|
|
18
|
+
/** Verdicts for a build triage (advisory only; never approves or waives). */
|
|
19
|
+
const TRIAGE_VERDICTS = [
|
|
20
|
+
"likely-intended",
|
|
21
|
+
"needs-review",
|
|
22
|
+
"likely-regression"
|
|
23
|
+
];
|
|
24
|
+
/** Verdicts for a project-health digest. */
|
|
25
|
+
const HEALTH_VERDICTS = [
|
|
26
|
+
"healthy",
|
|
27
|
+
"watch",
|
|
28
|
+
"unhealthy"
|
|
29
|
+
];
|
|
30
|
+
/** Build triage envelope. */
|
|
31
|
+
const triageEnvelopeSchema = z.object({
|
|
32
|
+
verdict: z.enum(TRIAGE_VERDICTS),
|
|
33
|
+
summary: z.string().max(1200),
|
|
34
|
+
items: z.array(z.object({
|
|
35
|
+
snapshotKey: z.string().max(200),
|
|
36
|
+
note: z.string().max(400),
|
|
37
|
+
severity: severitySchema
|
|
38
|
+
})).max(50),
|
|
39
|
+
confidence: confidenceSchema
|
|
40
|
+
});
|
|
41
|
+
/** Project-health digest envelope. */
|
|
42
|
+
const healthEnvelopeSchema = z.object({
|
|
43
|
+
verdict: z.enum(HEALTH_VERDICTS),
|
|
44
|
+
summary: z.string().max(1200),
|
|
45
|
+
score: z.number().min(0).max(100),
|
|
46
|
+
trends: z.array(z.object({
|
|
47
|
+
label: z.string().max(80),
|
|
48
|
+
note: z.string().max(300),
|
|
49
|
+
direction: z.enum([
|
|
50
|
+
"up",
|
|
51
|
+
"down",
|
|
52
|
+
"flat"
|
|
53
|
+
])
|
|
54
|
+
})).max(10),
|
|
55
|
+
confidence: confidenceSchema
|
|
56
|
+
});
|
|
57
|
+
//#endregion
|
|
58
|
+
export { HEALTH_VERDICTS, TRIAGE_VERDICTS, healthEnvelopeSchema, triageEnvelopeSchema };
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=envelope.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.mjs","names":[],"sources":["../../src/insights/envelope.ts"],"sourcesContent":["/** Result envelopes the model must produce (validated; stored as opaque JSON text). */\nimport { z } from \"zod\";\n\nconst severitySchema = z.enum([\"info\", \"low\", \"medium\", \"high\"]);\nconst confidenceSchema = z.enum([\"low\", \"medium\", \"high\"]);\n\n/** Verdicts for a build triage (advisory only; never approves or waives). */\nexport const TRIAGE_VERDICTS = [\"likely-intended\", \"needs-review\", \"likely-regression\"] as const;\n\n/** Verdicts for a project-health digest. */\nexport const HEALTH_VERDICTS = [\"healthy\", \"watch\", \"unhealthy\"] as const;\n\n/** Build triage envelope. */\nexport const triageEnvelopeSchema = z.object({\n verdict: z.enum(TRIAGE_VERDICTS),\n summary: z.string().max(1200),\n items: z\n .array(\n z.object({\n snapshotKey: z.string().max(200),\n note: z.string().max(400),\n severity: severitySchema,\n }),\n )\n .max(50),\n confidence: confidenceSchema,\n});\n\n/** Project-health digest envelope. */\nexport const healthEnvelopeSchema = z.object({\n verdict: z.enum(HEALTH_VERDICTS),\n summary: z.string().max(1200),\n score: z.number().min(0).max(100),\n trends: z\n .array(\n z.object({\n label: z.string().max(80),\n note: z.string().max(300),\n direction: z.enum([\"up\", \"down\", \"flat\"]),\n }),\n )\n .max(10),\n confidence: confidenceSchema,\n});\n\n/** A parsed triage result. */\nexport type TriageEnvelope = z.infer<typeof triageEnvelopeSchema>;\n/** A parsed health result. */\nexport type HealthEnvelope = z.infer<typeof healthEnvelopeSchema>;\n"],"mappings":";;;;;;AAGA,MAAM,iBAAiB,EAAE,KAAK;CAAC;CAAQ;CAAO;CAAU;AAAM,CAAC;AAC/D,MAAM,mBAAmB,EAAE,KAAK;CAAC;CAAO;CAAU;AAAM,CAAC;;AAGzD,MAAa,kBAAkB;CAAC;CAAmB;CAAgB;AAAmB;;AAGtF,MAAa,kBAAkB;CAAC;CAAW;CAAS;AAAW;;AAG/D,MAAa,uBAAuB,EAAE,OAAO;CAC3C,SAAS,EAAE,KAAK,eAAe;CAC/B,SAAS,EAAE,OAAO,CAAC,CAAC,IAAI,IAAI;CAC5B,OAAO,EACJ,MACC,EAAE,OAAO;EACP,aAAa,EAAE,OAAO,CAAC,CAAC,IAAI,GAAG;EAC/B,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,GAAG;EACxB,UAAU;CACZ,CAAC,CACH,CAAC,CACA,IAAI,EAAE;CACT,YAAY;AACd,CAAC;;AAGD,MAAa,uBAAuB,EAAE,OAAO;CAC3C,SAAS,EAAE,KAAK,eAAe;CAC/B,SAAS,EAAE,OAAO,CAAC,CAAC,IAAI,IAAI;CAC5B,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG;CAChC,QAAQ,EACL,MACC,EAAE,OAAO;EACP,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE;EACxB,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,GAAG;EACxB,WAAW,EAAE,KAAK;GAAC;GAAM;GAAQ;EAAM,CAAC;CAC1C,CAAC,CACH,CAAC,CACA,IAAI,EAAE;CACT,YAAY;AACd,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { EvidenceLimits } from "./limits.mjs";
|
|
4
|
+
import { BuiltEvidence } from "./evidence.mjs";
|
|
5
|
+
//#region src/insights/evidence-health.d.ts
|
|
6
|
+
/** One build in the window, summarized. */
|
|
7
|
+
interface BuildSummary {
|
|
8
|
+
createdAt: string;
|
|
9
|
+
gitBranch: string;
|
|
10
|
+
status: string;
|
|
11
|
+
snapshotCount: number;
|
|
12
|
+
changedCount: number;
|
|
13
|
+
approvedCount: number;
|
|
14
|
+
rejectedCount: number;
|
|
15
|
+
}
|
|
16
|
+
/** Input for the health digest (all pre-loaded). */
|
|
17
|
+
interface HealthEvidenceInput {
|
|
18
|
+
projectName: string;
|
|
19
|
+
window: string;
|
|
20
|
+
builds: BuildSummary[];
|
|
21
|
+
/** Stories that changed most often in the window. */
|
|
22
|
+
churnyStories: {
|
|
23
|
+
key: string;
|
|
24
|
+
changedBuilds: number;
|
|
25
|
+
}[];
|
|
26
|
+
limits?: Partial<EvidenceLimits>;
|
|
27
|
+
}
|
|
28
|
+
/** Assemble health evidence deterministically. */
|
|
29
|
+
declare function buildHealthEvidence(input: HealthEvidenceInput): BuiltEvidence;
|
|
30
|
+
//#endregion
|
|
31
|
+
export { BuildSummary, HealthEvidenceInput, buildHealthEvidence };
|
|
32
|
+
//# sourceMappingURL=evidence-health.d.mts.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { resolveLimits } from "./limits.mjs";
|
|
5
|
+
import { capBytes, field } from "./text.mjs";
|
|
6
|
+
//#region src/insights/evidence-health.ts
|
|
7
|
+
/** Assemble health evidence deterministically. */
|
|
8
|
+
function buildHealthEvidence(input) {
|
|
9
|
+
const limits = resolveLimits(input.limits);
|
|
10
|
+
const builds = input.builds.toSorted((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
11
|
+
const lines = [
|
|
12
|
+
"<evidence>",
|
|
13
|
+
`PROJECT ${field(input.projectName)} WINDOW ${field(input.window, 20)} BUILDS ${builds.length}`,
|
|
14
|
+
...builds.slice(-limits.maxSnapshots).map((b) => `BUILD day=${b.createdAt.slice(0, 10)} branch=${field(b.gitBranch, 80)} status=${field(b.status, 30)} total=${b.snapshotCount} changed=${b.changedCount} approved=${b.approvedCount} rejected=${b.rejectedCount}`),
|
|
15
|
+
...input.churnyStories.slice(0, 20).map((s) => `CHURN key=${field(s.key)} changedBuilds=${s.changedBuilds}`),
|
|
16
|
+
"</evidence>"
|
|
17
|
+
];
|
|
18
|
+
return {
|
|
19
|
+
text: capBytes(lines, limits.maxTextBytes),
|
|
20
|
+
images: [],
|
|
21
|
+
listed: builds.length,
|
|
22
|
+
omitted: 0
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { buildHealthEvidence };
|
|
27
|
+
|
|
28
|
+
//# sourceMappingURL=evidence-health.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evidence-health.mjs","names":[],"sources":["../../src/insights/evidence-health.ts"],"sourcesContent":["/** Project-health evidence: build-level aggregates only (no screenshots). */\nimport type { BuiltEvidence } from \"./evidence.ts\";\nimport { type EvidenceLimits, resolveLimits } from \"./limits.ts\";\nimport { capBytes, field } from \"./text.ts\";\n\n/** One build in the window, summarized. */\nexport interface BuildSummary {\n createdAt: string;\n gitBranch: string;\n status: string;\n snapshotCount: number;\n changedCount: number;\n approvedCount: number;\n rejectedCount: number;\n}\n\n/** Input for the health digest (all pre-loaded). */\nexport interface HealthEvidenceInput {\n projectName: string;\n window: string;\n builds: BuildSummary[];\n /** Stories that changed most often in the window. */\n churnyStories: { key: string; changedBuilds: number }[];\n limits?: Partial<EvidenceLimits>;\n}\n\n/** Assemble health evidence deterministically. */\nexport function buildHealthEvidence(input: HealthEvidenceInput): BuiltEvidence {\n const limits: EvidenceLimits = resolveLimits(input.limits);\n const builds = input.builds.toSorted((a, b) => a.createdAt.localeCompare(b.createdAt));\n const lines = [\n \"<evidence>\",\n `PROJECT ${field(input.projectName)} WINDOW ${field(input.window, 20)} BUILDS ${builds.length}`,\n ...builds\n .slice(-limits.maxSnapshots)\n .map(\n (b) =>\n `BUILD day=${b.createdAt.slice(0, 10)} branch=${field(b.gitBranch, 80)} status=${field(b.status, 30)} total=${b.snapshotCount} changed=${b.changedCount} approved=${b.approvedCount} rejected=${b.rejectedCount}`,\n ),\n ...input.churnyStories\n .slice(0, 20)\n .map((s) => `CHURN key=${field(s.key)} changedBuilds=${s.changedBuilds}`),\n \"</evidence>\",\n ];\n return {\n text: capBytes(lines, limits.maxTextBytes),\n images: [],\n listed: builds.length,\n omitted: 0,\n };\n}\n"],"mappings":";;;;;;;AA2BA,SAAgB,oBAAoB,OAA2C;CAC7E,MAAM,SAAyB,cAAc,MAAM,MAAM;CACzD,MAAM,SAAS,MAAM,OAAO,UAAU,GAAG,MAAM,EAAE,UAAU,cAAc,EAAE,SAAS,CAAC;CACrF,MAAM,QAAQ;EACZ;EACA,WAAW,MAAM,MAAM,WAAW,EAAE,UAAU,MAAM,MAAM,QAAQ,EAAE,EAAE,UAAU,OAAO;EACvF,GAAG,OACA,MAAM,CAAC,OAAO,YAAY,CAAC,CAC3B,KACE,MACC,aAAa,EAAE,UAAU,MAAM,GAAG,EAAE,EAAE,UAAU,MAAM,EAAE,WAAW,EAAE,EAAE,UAAU,MAAM,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,cAAc,WAAW,EAAE,aAAa,YAAY,EAAE,cAAc,YAAY,EAAE,eACtM;EACF,GAAG,MAAM,cACN,MAAM,GAAG,EAAE,CAAC,CACZ,KAAK,MAAM,aAAa,MAAM,EAAE,GAAG,EAAE,iBAAiB,EAAE,eAAe;EAC1E;CACF;CACA,OAAO;EACL,MAAM,SAAS,OAAO,OAAO,YAAY;EACzC,QAAQ,CAAC;EACT,QAAQ,OAAO;EACf,SAAS;CACX;AACF"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
import { EvidenceLimits } from "./limits.mjs";
|
|
4
|
+
import { AiEvidence } from "../ai.mjs";
|
|
5
|
+
//#region src/insights/evidence.d.ts
|
|
6
|
+
/** One changed (or failed) snapshot as evidence. */
|
|
7
|
+
interface SnapshotEvidence {
|
|
8
|
+
storyId: string;
|
|
9
|
+
title: string;
|
|
10
|
+
name: string;
|
|
11
|
+
viewport: string;
|
|
12
|
+
status: string;
|
|
13
|
+
diffRatio: number | null;
|
|
14
|
+
inherited: boolean;
|
|
15
|
+
/** Raw play/a11y/capture log text for this snapshot, if any. */
|
|
16
|
+
log?: string;
|
|
17
|
+
/** Screenshot PNG, loaded by the caller (only used for the top-ranked few). */
|
|
18
|
+
screenshot?: Uint8Array;
|
|
19
|
+
}
|
|
20
|
+
/** Everything the triage builder needs (all pre-loaded). */
|
|
21
|
+
interface TriageEvidenceInput {
|
|
22
|
+
projectName: string;
|
|
23
|
+
build: {
|
|
24
|
+
gitBranch: string;
|
|
25
|
+
gitSha: string;
|
|
26
|
+
message: string | null;
|
|
27
|
+
status: string;
|
|
28
|
+
snapshotCount: number;
|
|
29
|
+
changedCount: number;
|
|
30
|
+
approvedCount: number;
|
|
31
|
+
rejectedCount: number;
|
|
32
|
+
changedFiles: string[];
|
|
33
|
+
};
|
|
34
|
+
snapshots: SnapshotEvidence[];
|
|
35
|
+
comments: {
|
|
36
|
+
author: string;
|
|
37
|
+
body: string;
|
|
38
|
+
}[];
|
|
39
|
+
limits?: Partial<EvidenceLimits>;
|
|
40
|
+
}
|
|
41
|
+
/** Evidence plus bookkeeping the caller needs for hashing and rows. */
|
|
42
|
+
interface BuiltEvidence extends AiEvidence {
|
|
43
|
+
/** Snapshots listed individually / total changed. */
|
|
44
|
+
listed: number;
|
|
45
|
+
omitted: number;
|
|
46
|
+
}
|
|
47
|
+
/** Stable story key shown to the model and echoed back in `snapshotKey`. */
|
|
48
|
+
declare function snapshotKey(s: Pick<SnapshotEvidence, "title" | "name" | "viewport">): string;
|
|
49
|
+
/** Evidence ranking: failures first, then diff ratio, then key (deterministic). */
|
|
50
|
+
declare function rankSnapshots(a: SnapshotEvidence, b: SnapshotEvidence): number;
|
|
51
|
+
/** Assemble triage evidence deterministically (redacted, capped, ranked). */
|
|
52
|
+
declare function buildTriageEvidence(input: TriageEvidenceInput): Promise<BuiltEvidence>;
|
|
53
|
+
//#endregion
|
|
54
|
+
export { BuiltEvidence, SnapshotEvidence, TriageEvidenceInput, buildTriageEvidence, rankSnapshots, snapshotKey };
|
|
55
|
+
//# sourceMappingURL=evidence.d.mts.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { resolveLimits } from "./limits.mjs";
|
|
5
|
+
import { capBytes, field, headTail } from "./text.mjs";
|
|
6
|
+
import { downscalePng } from "@storyshelf/image";
|
|
7
|
+
//#region src/insights/evidence.ts
|
|
8
|
+
/** Pure evidence builders: already-loaded inputs in, delimited text + images out. */
|
|
9
|
+
/** Stable story key shown to the model and echoed back in `snapshotKey`. */
|
|
10
|
+
function snapshotKey(s) {
|
|
11
|
+
return field(`${s.title}/${s.name}@${s.viewport}`, 200);
|
|
12
|
+
}
|
|
13
|
+
/** Evidence ranking: failures first, then diff ratio, then key (deterministic). */
|
|
14
|
+
function rankSnapshots(a, b) {
|
|
15
|
+
return Number(b.status === "failed") - Number(a.status === "failed") || (b.diffRatio ?? 0) - (a.diffRatio ?? 0) || snapshotKey(a).localeCompare(snapshotKey(b));
|
|
16
|
+
}
|
|
17
|
+
function snapshotLines(s, limits) {
|
|
18
|
+
const ratio = s.diffRatio === null ? "n/a" : s.diffRatio.toFixed(4);
|
|
19
|
+
const lines = [`SNAPSHOT key=${snapshotKey(s)} status=${field(s.status, 30)} diffRatio=${ratio}`];
|
|
20
|
+
if (s.log) lines.push(` LOG: ${headTail(s.log, limits.logHeadBytes, limits.logTailBytes)}`);
|
|
21
|
+
return lines;
|
|
22
|
+
}
|
|
23
|
+
/** Assemble triage evidence deterministically (redacted, capped, ranked). */
|
|
24
|
+
async function buildTriageEvidence(input) {
|
|
25
|
+
const limits = resolveLimits(input.limits);
|
|
26
|
+
const changed = input.snapshots.filter((s) => !s.inherited).toSorted(rankSnapshots);
|
|
27
|
+
const listed = changed.slice(0, limits.maxSnapshots);
|
|
28
|
+
const b = input.build;
|
|
29
|
+
const lines = [
|
|
30
|
+
"<evidence>",
|
|
31
|
+
`PROJECT ${field(input.projectName)}`,
|
|
32
|
+
`BUILD branch=${field(b.gitBranch)} sha=${field(b.gitSha.slice(0, 12), 12)} status=${field(b.status, 30)}`,
|
|
33
|
+
`COUNTS total=${b.snapshotCount} changed=${b.changedCount} approved=${b.approvedCount} rejected=${b.rejectedCount}`,
|
|
34
|
+
`COMMIT ${field(b.message, 300)}`,
|
|
35
|
+
`CHANGED_FILES (${b.changedFiles.length}): ${b.changedFiles.slice(0, 40).map((f) => field(f, 120)).join(", ")}`,
|
|
36
|
+
...listed.flatMap((s) => snapshotLines(s, limits)),
|
|
37
|
+
`OMITTED_SNAPSHOTS ${changed.length - listed.length}`,
|
|
38
|
+
...input.comments.slice(0, 10).map((c) => `COMMENT ${field(c.author, 60)}: ${field(c.body, 300)}`),
|
|
39
|
+
"</evidence>"
|
|
40
|
+
];
|
|
41
|
+
const images = await triageImages(listed, limits);
|
|
42
|
+
return {
|
|
43
|
+
text: capBytes(lines, limits.maxTextBytes),
|
|
44
|
+
images,
|
|
45
|
+
listed: listed.length,
|
|
46
|
+
omitted: changed.length - listed.length
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/** Downscaled screenshots of the listed snapshots, capped at `maxImages` (downscales only what is kept). */
|
|
50
|
+
async function triageImages(listed, limits) {
|
|
51
|
+
const withScreenshots = listed.flatMap((item) => item.screenshot ? [{
|
|
52
|
+
item,
|
|
53
|
+
screenshot: item.screenshot
|
|
54
|
+
}] : []).slice(0, limits.maxImages);
|
|
55
|
+
return await Promise.all(withScreenshots.map(async ({ item, screenshot }) => ({
|
|
56
|
+
mediaType: "image/png",
|
|
57
|
+
data: await downscalePng(screenshot, limits.imageMaxEdge),
|
|
58
|
+
label: snapshotKey(item)
|
|
59
|
+
})));
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
export { buildTriageEvidence, rankSnapshots, snapshotKey };
|
|
63
|
+
|
|
64
|
+
//# sourceMappingURL=evidence.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evidence.mjs","names":[],"sources":["../../src/insights/evidence.ts"],"sourcesContent":["/** Pure evidence builders: already-loaded inputs in, delimited text + images out. */\nimport { downscalePng } from \"@storyshelf/image\";\nimport type { AiEvidence } from \"../ai.ts\";\nimport { type EvidenceLimits, resolveLimits } from \"./limits.ts\";\nimport { capBytes, field, headTail } from \"./text.ts\";\n\n/** One changed (or failed) snapshot as evidence. */\nexport interface SnapshotEvidence {\n storyId: string;\n title: string;\n name: string;\n viewport: string;\n status: string;\n diffRatio: number | null;\n inherited: boolean;\n /** Raw play/a11y/capture log text for this snapshot, if any. */\n log?: string;\n /** Screenshot PNG, loaded by the caller (only used for the top-ranked few). */\n screenshot?: Uint8Array;\n}\n\n/** Everything the triage builder needs (all pre-loaded). */\nexport interface TriageEvidenceInput {\n projectName: string;\n build: {\n gitBranch: string;\n gitSha: string;\n message: string | null;\n status: string;\n snapshotCount: number;\n changedCount: number;\n approvedCount: number;\n rejectedCount: number;\n changedFiles: string[];\n };\n snapshots: SnapshotEvidence[];\n comments: { author: string; body: string }[];\n limits?: Partial<EvidenceLimits>;\n}\n\n/** Evidence plus bookkeeping the caller needs for hashing and rows. */\nexport interface BuiltEvidence extends AiEvidence {\n /** Snapshots listed individually / total changed. */\n listed: number;\n omitted: number;\n}\n\n/** Stable story key shown to the model and echoed back in `snapshotKey`. */\nexport function snapshotKey(s: Pick<SnapshotEvidence, \"title\" | \"name\" | \"viewport\">): string {\n return field(`${s.title}/${s.name}@${s.viewport}`, 200);\n}\n\n/** Evidence ranking: failures first, then diff ratio, then key (deterministic). */\nexport function rankSnapshots(a: SnapshotEvidence, b: SnapshotEvidence): number {\n const failed = Number(b.status === \"failed\") - Number(a.status === \"failed\");\n return (\n failed ||\n (b.diffRatio ?? 0) - (a.diffRatio ?? 0) ||\n snapshotKey(a).localeCompare(snapshotKey(b))\n );\n}\n\nfunction snapshotLines(s: SnapshotEvidence, limits: EvidenceLimits): string[] {\n const ratio = s.diffRatio === null ? \"n/a\" : s.diffRatio.toFixed(4);\n const lines = [`SNAPSHOT key=${snapshotKey(s)} status=${field(s.status, 30)} diffRatio=${ratio}`];\n if (s.log) {\n lines.push(` LOG: ${headTail(s.log, limits.logHeadBytes, limits.logTailBytes)}`);\n }\n return lines;\n}\n\n/** Assemble triage evidence deterministically (redacted, capped, ranked). */\nexport async function buildTriageEvidence(input: TriageEvidenceInput): Promise<BuiltEvidence> {\n const limits = resolveLimits(input.limits);\n const changed = input.snapshots.filter((s) => !s.inherited).toSorted(rankSnapshots);\n const listed = changed.slice(0, limits.maxSnapshots);\n const b = input.build;\n const lines = [\n \"<evidence>\",\n `PROJECT ${field(input.projectName)}`,\n `BUILD branch=${field(b.gitBranch)} sha=${field(b.gitSha.slice(0, 12), 12)} status=${field(b.status, 30)}`,\n `COUNTS total=${b.snapshotCount} changed=${b.changedCount} approved=${b.approvedCount} rejected=${b.rejectedCount}`,\n `COMMIT ${field(b.message, 300)}`,\n `CHANGED_FILES (${b.changedFiles.length}): ${b.changedFiles\n .slice(0, 40)\n .map((f) => field(f, 120))\n .join(\", \")}`,\n ...listed.flatMap((s) => snapshotLines(s, limits)),\n `OMITTED_SNAPSHOTS ${changed.length - listed.length}`,\n ...input.comments\n .slice(0, 10)\n .map((c) => `COMMENT ${field(c.author, 60)}: ${field(c.body, 300)}`),\n \"</evidence>\",\n ];\n const images = await triageImages(listed, limits);\n return {\n text: capBytes(lines, limits.maxTextBytes),\n images,\n listed: listed.length,\n omitted: changed.length - listed.length,\n };\n}\n\n/** Downscaled screenshots of the listed snapshots, capped at `maxImages` (downscales only what is kept). */\nasync function triageImages(\n listed: SnapshotEvidence[],\n limits: { imageMaxEdge: number; maxImages: number },\n): Promise<AiEvidence[\"images\"]> {\n const withScreenshots = listed\n .flatMap((item) => (item.screenshot ? [{ item, screenshot: item.screenshot }] : []))\n .slice(0, limits.maxImages);\n return await Promise.all(\n withScreenshots.map(async ({ item, screenshot }) => ({\n mediaType: \"image/png\" as const,\n data: await downscalePng(screenshot, limits.imageMaxEdge),\n label: snapshotKey(item),\n })),\n );\n}\n"],"mappings":";;;;;;;;;AAgDA,SAAgB,YAAY,GAAkE;CAC5F,OAAO,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE,KAAK,GAAG,EAAE,YAAY,GAAG;AACxD;;AAGA,SAAgB,cAAc,GAAqB,GAA6B;CAE9E,OADe,OAAO,EAAE,WAAW,QAAQ,IAAI,OAAO,EAAE,WAAW,QAAQ,MAGxE,EAAE,aAAa,MAAM,EAAE,aAAa,MACrC,YAAY,CAAC,CAAC,CAAC,cAAc,YAAY,CAAC,CAAC;AAE/C;AAEA,SAAS,cAAc,GAAqB,QAAkC;CAC5E,MAAM,QAAQ,EAAE,cAAc,OAAO,QAAQ,EAAE,UAAU,QAAQ,CAAC;CAClE,MAAM,QAAQ,CAAC,gBAAgB,YAAY,CAAC,EAAE,UAAU,MAAM,EAAE,QAAQ,EAAE,EAAE,aAAa,OAAO;CAChG,IAAI,EAAE,KACJ,MAAM,KAAK,UAAU,SAAS,EAAE,KAAK,OAAO,cAAc,OAAO,YAAY,GAAG;CAElF,OAAO;AACT;;AAGA,eAAsB,oBAAoB,OAAoD;CAC5F,MAAM,SAAS,cAAc,MAAM,MAAM;CACzC,MAAM,UAAU,MAAM,UAAU,QAAQ,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC,SAAS,aAAa;CAClF,MAAM,SAAS,QAAQ,MAAM,GAAG,OAAO,YAAY;CACnD,MAAM,IAAI,MAAM;CAChB,MAAM,QAAQ;EACZ;EACA,WAAW,MAAM,MAAM,WAAW;EAClC,gBAAgB,MAAM,EAAE,SAAS,EAAE,OAAO,MAAM,EAAE,OAAO,MAAM,GAAG,EAAE,GAAG,EAAE,EAAE,UAAU,MAAM,EAAE,QAAQ,EAAE;EACvG,gBAAgB,EAAE,cAAc,WAAW,EAAE,aAAa,YAAY,EAAE,cAAc,YAAY,EAAE;EACpG,UAAU,MAAM,EAAE,SAAS,GAAG;EAC9B,kBAAkB,EAAE,aAAa,OAAO,KAAK,EAAE,aAC5C,MAAM,GAAG,EAAE,CAAC,CACZ,KAAK,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,CACzB,KAAK,IAAI;EACZ,GAAG,OAAO,SAAS,MAAM,cAAc,GAAG,MAAM,CAAC;EACjD,qBAAqB,QAAQ,SAAS,OAAO;EAC7C,GAAG,MAAM,SACN,MAAM,GAAG,EAAE,CAAC,CACZ,KAAK,MAAM,WAAW,MAAM,EAAE,QAAQ,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,GAAG,GAAG;EACrE;CACF;CACA,MAAM,SAAS,MAAM,aAAa,QAAQ,MAAM;CAChD,OAAO;EACL,MAAM,SAAS,OAAO,OAAO,YAAY;EACzC;EACA,QAAQ,OAAO;EACf,SAAS,QAAQ,SAAS,OAAO;CACnC;AACF;;AAGA,eAAe,aACb,QACA,QAC+B;CAC/B,MAAM,kBAAkB,OACrB,SAAS,SAAU,KAAK,aAAa,CAAC;EAAE;EAAM,YAAY,KAAK;CAAW,CAAC,IAAI,CAAC,CAAE,CAAC,CACnF,MAAM,GAAG,OAAO,SAAS;CAC5B,OAAO,MAAM,QAAQ,IACnB,gBAAgB,IAAI,OAAO,EAAE,MAAM,kBAAkB;EACnD,WAAW;EACX,MAAM,MAAM,aAAa,YAAY,OAAO,YAAY;EACxD,OAAO,YAAY,IAAI;CACzB,EAAE,CACJ;AACF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import __tsdown_shims_path from 'node:path';
|
|
2
|
+
import __tsdown_shims_url from 'node:url';
|
|
3
|
+
//#region src/insights/hash.d.ts
|
|
4
|
+
/** Parts that define whether a cached insight is still valid. */
|
|
5
|
+
interface InputHashParts {
|
|
6
|
+
evidenceText: string;
|
|
7
|
+
/** Image digests, in send order. */
|
|
8
|
+
imageDigests: string[];
|
|
9
|
+
promptVersion: string;
|
|
10
|
+
task: string;
|
|
11
|
+
profile: string;
|
|
12
|
+
model: string;
|
|
13
|
+
}
|
|
14
|
+
/** Hash the final assembled evidence plus prompt/task/profile/model. */
|
|
15
|
+
declare function computeInputHash(parts: InputHashParts): string;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { InputHashParts, computeInputHash };
|
|
18
|
+
//# sourceMappingURL=hash.d.mts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { sha256 } from "../utils/hash.mjs";
|
|
5
|
+
//#region src/insights/hash.ts
|
|
6
|
+
/** Cache key for an insight: identical evidence + prompt + model is a free hit. */
|
|
7
|
+
/** Hash the final assembled evidence plus prompt/task/profile/model. */
|
|
8
|
+
function computeInputHash(parts) {
|
|
9
|
+
return sha256(JSON.stringify([
|
|
10
|
+
parts.promptVersion,
|
|
11
|
+
parts.task,
|
|
12
|
+
parts.profile,
|
|
13
|
+
parts.model,
|
|
14
|
+
parts.imageDigests,
|
|
15
|
+
parts.evidenceText
|
|
16
|
+
]));
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { computeInputHash };
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=hash.mjs.map
|