@storyshelf/app 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 +42 -4
- package/dist/app-types.d.mts +1 -3
- package/dist/asset-manifest.mjs +13 -5
- package/dist/asset-manifest.mjs.map +1 -1
- package/dist/assets/{htmx.min.js → htmx.mjs} +10 -1
- package/dist/assets/htmx.mjs.map +1 -0
- package/dist/assets/mark.mjs +10 -0
- package/dist/assets/mark.mjs.map +1 -0
- package/dist/boot-tasks.mjs +80 -0
- package/dist/boot-tasks.mjs.map +1 -0
- package/dist/build-state-flags.mjs +18 -0
- package/dist/build-state-flags.mjs.map +1 -0
- package/dist/build-state-setup.mjs +75 -0
- package/dist/build-state-setup.mjs.map +1 -0
- package/dist/capture-alerts.mjs +46 -0
- package/dist/capture-alerts.mjs.map +1 -0
- package/dist/capture-process.mjs +59 -0
- package/dist/capture-process.mjs.map +1 -0
- package/dist/capture-setup.mjs +95 -117
- package/dist/capture-setup.mjs.map +1 -1
- package/dist/connections.mjs +34 -0
- package/dist/connections.mjs.map +1 -0
- package/dist/consumer-setup.mjs +58 -0
- package/dist/consumer-setup.mjs.map +1 -0
- package/dist/credentials.mjs +3 -2
- package/dist/credentials.mjs.map +1 -1
- package/dist/git-status.mjs +46 -0
- package/dist/git-status.mjs.map +1 -0
- package/dist/hidden-data.mjs +24 -0
- package/dist/hidden-data.mjs.map +1 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +202 -124
- package/dist/index.mjs.map +1 -1
- package/dist/insights/budget.mjs +49 -0
- package/dist/insights/budget.mjs.map +1 -0
- package/dist/insights/deps.mjs +21 -0
- package/dist/insights/deps.mjs.map +1 -0
- package/dist/insights/errors.mjs +22 -0
- package/dist/insights/errors.mjs.map +1 -0
- package/dist/insights/health-panel.mjs +40 -0
- package/dist/insights/health-panel.mjs.map +1 -0
- package/dist/insights/job.mjs +103 -0
- package/dist/insights/job.mjs.map +1 -0
- package/dist/insights/load-health.mjs +59 -0
- package/dist/insights/load-health.mjs.map +1 -0
- package/dist/insights/load-triage.mjs +98 -0
- package/dist/insights/load-triage.mjs.map +1 -0
- package/dist/insights/panel.mjs +28 -0
- package/dist/insights/panel.mjs.map +1 -0
- package/dist/insights/request.mjs +113 -0
- package/dist/insights/request.mjs.map +1 -0
- package/dist/insights/stale.mjs +21 -0
- package/dist/insights/stale.mjs.map +1 -0
- package/dist/insights/usage-report.mjs +60 -0
- package/dist/insights/usage-report.mjs.map +1 -0
- package/dist/insights/view.mjs +48 -0
- package/dist/insights/view.mjs.map +1 -0
- package/dist/job-canceller.mjs +14 -0
- package/dist/job-canceller.mjs.map +1 -0
- package/dist/job-queue.mjs +51 -0
- package/dist/job-queue.mjs.map +1 -0
- package/dist/jobs-setup.mjs +132 -0
- package/dist/jobs-setup.mjs.map +1 -0
- package/dist/lifecycle.mjs +109 -19
- package/dist/lifecycle.mjs.map +1 -1
- package/dist/middleware/auth-gate.mjs +4 -2
- package/dist/middleware/auth-gate.mjs.map +1 -1
- package/dist/middleware/rate-limit.mjs +41 -33
- package/dist/middleware/rate-limit.mjs.map +1 -1
- package/dist/middleware/request-log.mjs +3 -2
- package/dist/middleware/request-log.mjs.map +1 -1
- package/dist/middleware/require-review.mjs +51 -0
- package/dist/middleware/require-review.mjs.map +1 -0
- package/dist/middleware/require-testing.mjs +41 -0
- package/dist/middleware/require-testing.mjs.map +1 -0
- package/dist/middleware/setup-gate.mjs.map +1 -1
- package/dist/middleware/store-scope.mjs +12 -2
- package/dist/middleware/store-scope.mjs.map +1 -1
- package/dist/notifications-pause.mjs +33 -0
- package/dist/notifications-pause.mjs.map +1 -0
- package/dist/notify.d.mts +2 -1
- package/dist/notify.mjs +78 -39
- package/dist/notify.mjs.map +1 -1
- package/dist/pages/a11y-violations.mjs +116 -0
- package/dist/pages/a11y-violations.mjs.map +1 -0
- package/dist/pages/admin-ai.mjs +78 -0
- package/dist/pages/admin-ai.mjs.map +1 -0
- package/dist/pages/admin-system-origin.mjs +123 -0
- package/dist/pages/admin-system-origin.mjs.map +1 -0
- package/dist/pages/admin-system-targets.mjs +53 -0
- package/dist/pages/admin-system-targets.mjs.map +1 -0
- package/dist/pages/admin-system.mjs +63 -13
- package/dist/pages/admin-system.mjs.map +1 -1
- package/dist/pages/build-detail.mjs +104 -76
- package/dist/pages/build-detail.mjs.map +1 -1
- package/dist/pages/build-diff-actions.mjs +87 -33
- package/dist/pages/build-diff-actions.mjs.map +1 -1
- package/dist/pages/build-diff-comments.mjs +16 -14
- package/dist/pages/build-diff-comments.mjs.map +1 -1
- package/dist/pages/build-diff-environment.mjs +82 -0
- package/dist/pages/build-diff-environment.mjs.map +1 -0
- package/dist/pages/build-diff-header.mjs +22 -4
- package/dist/pages/build-diff-header.mjs.map +1 -1
- package/dist/pages/build-diff-insight.mjs +113 -0
- package/dist/pages/build-diff-insight.mjs.map +1 -0
- package/dist/pages/build-diff-model.mjs +55 -10
- package/dist/pages/build-diff-model.mjs.map +1 -1
- package/dist/pages/build-diff-nav.mjs +28 -12
- package/dist/pages/build-diff-nav.mjs.map +1 -1
- package/dist/pages/build-diff-shortcuts.mjs +5 -1
- package/dist/pages/build-diff-shortcuts.mjs.map +1 -1
- package/dist/pages/build-diff-usage.mjs +63 -0
- package/dist/pages/build-diff-usage.mjs.map +1 -0
- package/dist/pages/build-diff-viewer.mjs +62 -5
- package/dist/pages/build-diff-viewer.mjs.map +1 -1
- package/dist/pages/build-diff.mjs +34 -13
- package/dist/pages/build-diff.mjs.map +1 -1
- package/dist/pages/build-tests.mjs +74 -0
- package/dist/pages/build-tests.mjs.map +1 -0
- package/dist/pages/component-detail.mjs +249 -0
- package/dist/pages/component-detail.mjs.map +1 -0
- package/dist/pages/component-stories.mjs +168 -0
- package/dist/pages/component-stories.mjs.map +1 -0
- package/dist/pages/compute-jobs.mjs +13 -26
- package/dist/pages/compute-jobs.mjs.map +1 -1
- package/dist/pages/design-links.mjs +182 -0
- package/dist/pages/design-links.mjs.map +1 -0
- package/dist/pages/environment-change.mjs +53 -0
- package/dist/pages/environment-change.mjs.map +1 -0
- package/dist/pages/health-panel.mjs +126 -0
- package/dist/pages/health-panel.mjs.map +1 -0
- package/dist/pages/library-grid.mjs +105 -0
- package/dist/pages/library-grid.mjs.map +1 -0
- package/dist/pages/library-model.mjs +47 -0
- package/dist/pages/library-model.mjs.map +1 -0
- package/dist/pages/library-snapshots.mjs +130 -0
- package/dist/pages/library-snapshots.mjs.map +1 -0
- package/dist/pages/library.mjs +112 -218
- package/dist/pages/library.mjs.map +1 -1
- package/dist/pages/notifications-paused.mjs +22 -0
- package/dist/pages/notifications-paused.mjs.map +1 -0
- package/dist/pages/profile.mjs +53 -48
- package/dist/pages/profile.mjs.map +1 -1
- package/dist/pages/project-builds-table.mjs +2 -2
- package/dist/pages/project-builds-table.mjs.map +1 -1
- package/dist/pages/project-builds.mjs +51 -21
- package/dist/pages/project-builds.mjs.map +1 -1
- package/dist/pages/project-settings.mjs +51 -12
- package/dist/pages/project-settings.mjs.map +1 -1
- package/dist/pages/projects-card.mjs +14 -1
- package/dist/pages/projects-card.mjs.map +1 -1
- package/dist/pages/projects.mjs +6 -2
- package/dist/pages/projects.mjs.map +1 -1
- package/dist/pages/review-changeset.mjs +116 -0
- package/dist/pages/review-changeset.mjs.map +1 -0
- package/dist/pages/review-comments.mjs +103 -0
- package/dist/pages/review-comments.mjs.map +1 -0
- package/dist/pages/review-verdict.mjs +102 -0
- package/dist/pages/review-verdict.mjs.map +1 -0
- package/dist/pages/settings-ai.mjs +69 -0
- package/dist/pages/settings-ai.mjs.map +1 -0
- package/dist/pages/settings-capture.mjs +124 -0
- package/dist/pages/settings-capture.mjs.map +1 -0
- package/dist/pages/settings-design-credential.mjs +99 -0
- package/dist/pages/settings-design-credential.mjs.map +1 -0
- package/dist/pages/settings-design-links.mjs +179 -0
- package/dist/pages/settings-design-links.mjs.map +1 -0
- package/dist/pages/settings-design.mjs +48 -0
- package/dist/pages/settings-design.mjs.map +1 -0
- package/dist/pages/settings-general.mjs +46 -21
- package/dist/pages/settings-general.mjs.map +1 -1
- package/dist/pages/settings-labels.mjs +43 -2
- package/dist/pages/settings-labels.mjs.map +1 -1
- package/dist/pages/settings-links.mjs +112 -0
- package/dist/pages/settings-links.mjs.map +1 -0
- package/dist/pages/settings-notifications-git.mjs +128 -0
- package/dist/pages/settings-notifications-git.mjs.map +1 -0
- package/dist/pages/settings-notifications.mjs +79 -42
- package/dist/pages/settings-notifications.mjs.map +1 -1
- package/dist/pages/settings-tests.mjs +33 -2
- package/dist/pages/settings-tests.mjs.map +1 -1
- package/dist/pages/target-choices.mjs +39 -0
- package/dist/pages/target-choices.mjs.map +1 -0
- package/dist/publish-setup.mjs +89 -0
- package/dist/publish-setup.mjs.map +1 -0
- package/dist/retention-timer.mjs +2 -1
- package/dist/retention-timer.mjs.map +1 -1
- package/dist/review-events.mjs +48 -0
- package/dist/review-events.mjs.map +1 -0
- package/dist/routers/admin-pages.mjs +45 -3
- package/dist/routers/admin-pages.mjs.map +1 -1
- package/dist/routers/admin-public-origin.mjs +85 -0
- package/dist/routers/admin-public-origin.mjs.map +1 -0
- package/dist/routers/assets.mjs +9 -2
- package/dist/routers/assets.mjs.map +1 -1
- package/dist/routers/auth.mjs +2 -2
- package/dist/routers/auth.mjs.map +1 -1
- package/dist/routers/baseline-guard.mjs +2 -2
- package/dist/routers/baseline-guard.mjs.map +1 -1
- package/dist/routers/baselines.handlers.mjs +64 -0
- package/dist/routers/baselines.handlers.mjs.map +1 -0
- package/dist/routers/baselines.mjs +125 -0
- package/dist/routers/baselines.mjs.map +1 -0
- package/dist/routers/build-cancel.mjs +68 -0
- package/dist/routers/build-cancel.mjs.map +1 -0
- package/dist/routers/build-changeset.mjs +70 -0
- package/dist/routers/build-changeset.mjs.map +1 -0
- package/dist/routers/build-reviews.handlers.mjs +86 -0
- package/dist/routers/build-reviews.handlers.mjs.map +1 -0
- package/dist/routers/build-reviews.mjs +74 -0
- package/dist/routers/build-reviews.mjs.map +1 -0
- package/dist/routers/builds.handlers.mjs +46 -55
- package/dist/routers/builds.handlers.mjs.map +1 -1
- package/dist/routers/builds.mjs +71 -17
- package/dist/routers/builds.mjs.map +1 -1
- package/dist/routers/capture-config.mjs +35 -0
- package/dist/routers/capture-config.mjs.map +1 -0
- package/dist/routers/comments.mjs +41 -15
- package/dist/routers/comments.mjs.map +1 -1
- package/dist/routers/component-catalog.handlers.mjs +71 -0
- package/dist/routers/component-catalog.handlers.mjs.map +1 -0
- package/dist/routers/component.handlers.mjs +121 -0
- package/dist/routers/component.handlers.mjs.map +1 -0
- package/dist/routers/connections.mjs +118 -0
- package/dist/routers/connections.mjs.map +1 -0
- package/dist/routers/design-connect.handlers.mjs +44 -0
- package/dist/routers/design-connect.handlers.mjs.map +1 -0
- package/dist/routers/design.handlers.mjs +114 -0
- package/dist/routers/design.handlers.mjs.map +1 -0
- package/dist/routers/health-report.mjs +2 -3
- package/dist/routers/health-report.mjs.map +1 -1
- package/dist/routers/health.mjs +4 -3
- package/dist/routers/health.mjs.map +1 -1
- package/dist/routers/helpers.mjs +19 -3
- package/dist/routers/helpers.mjs.map +1 -1
- package/dist/routers/insights-ui.mjs +64 -0
- package/dist/routers/insights-ui.mjs.map +1 -0
- package/dist/routers/insights.handlers.mjs +45 -0
- package/dist/routers/insights.handlers.mjs.map +1 -0
- package/dist/routers/insights.mjs +189 -0
- package/dist/routers/insights.mjs.map +1 -0
- package/dist/routers/links.handlers.mjs +80 -0
- package/dist/routers/links.handlers.mjs.map +1 -0
- package/dist/routers/links.mjs +139 -0
- package/dist/routers/links.mjs.map +1 -0
- package/dist/routers/media.mjs +18 -4
- package/dist/routers/media.mjs.map +1 -1
- package/dist/routers/members.mjs +2 -1
- package/dist/routers/members.mjs.map +1 -1
- package/dist/routers/notification-channels.mjs +17 -23
- package/dist/routers/notification-channels.mjs.map +1 -1
- package/dist/routers/oauth-state.mjs +45 -0
- package/dist/routers/oauth-state.mjs.map +1 -0
- package/dist/routers/preview-base.mjs +38 -0
- package/dist/routers/preview-base.mjs.map +1 -0
- package/dist/routers/preview-token.mjs +51 -0
- package/dist/routers/preview-token.mjs.map +1 -0
- package/dist/routers/profile.mjs +4 -2
- package/dist/routers/profile.mjs.map +1 -1
- package/dist/routers/projects.mjs +17 -1
- package/dist/routers/projects.mjs.map +1 -1
- package/dist/routers/publish-upload.mjs +78 -0
- package/dist/routers/publish-upload.mjs.map +1 -0
- package/dist/routers/published-access.mjs +52 -0
- package/dist/routers/published-access.mjs.map +1 -0
- package/dist/routers/published-files.mjs +52 -0
- package/dist/routers/published-files.mjs.map +1 -0
- package/dist/routers/review-forms.mjs +72 -0
- package/dist/routers/review-forms.mjs.map +1 -0
- package/dist/routers/review-section.handlers.mjs +85 -0
- package/dist/routers/review-section.handlers.mjs.map +1 -0
- package/dist/routers/review-switch-off.mjs +29 -0
- package/dist/routers/review-switch-off.mjs.map +1 -0
- package/dist/routers/schemas.mjs +140 -22
- package/dist/routers/schemas.mjs.map +1 -1
- package/dist/routers/scoped-models.mjs +22 -0
- package/dist/routers/scoped-models.mjs.map +1 -0
- package/dist/routers/settings-ai.mjs +35 -0
- package/dist/routers/settings-ai.mjs.map +1 -0
- package/dist/routers/settings-capture.mjs +77 -0
- package/dist/routers/settings-capture.mjs.map +1 -0
- package/dist/routers/settings-design.mjs +103 -0
- package/dist/routers/settings-design.mjs.map +1 -0
- package/dist/routers/settings-general.mjs +55 -10
- package/dist/routers/settings-general.mjs.map +1 -1
- package/dist/routers/settings-git-channel.mjs +77 -0
- package/dist/routers/settings-git-channel.mjs.map +1 -0
- package/dist/routers/settings-labels.mjs +27 -1
- package/dist/routers/settings-labels.mjs.map +1 -1
- package/dist/routers/settings-links.mjs +63 -0
- package/dist/routers/settings-links.mjs.map +1 -0
- package/dist/routers/settings-members.mjs +4 -7
- package/dist/routers/settings-members.mjs.map +1 -1
- package/dist/routers/settings-notifications.mjs +15 -10
- package/dist/routers/settings-notifications.mjs.map +1 -1
- package/dist/routers/settings-tokens.mjs +2 -5
- package/dist/routers/settings-tokens.mjs.map +1 -1
- package/dist/routers/settings-webhooks.mjs +7 -24
- package/dist/routers/settings-webhooks.mjs.map +1 -1
- package/dist/routers/settings.handlers.mjs +107 -26
- package/dist/routers/settings.handlers.mjs.map +1 -1
- package/dist/routers/settings.mjs +16 -4
- package/dist/routers/settings.mjs.map +1 -1
- package/dist/routers/signed-storybook.mjs +53 -0
- package/dist/routers/signed-storybook.mjs.map +1 -0
- package/dist/routers/snapshot-groups.mjs +63 -0
- package/dist/routers/snapshot-groups.mjs.map +1 -0
- package/dist/routers/snapshots.mjs +103 -5
- package/dist/routers/snapshots.mjs.map +1 -1
- package/dist/routers/storybook.mjs +37 -88
- package/dist/routers/storybook.mjs.map +1 -1
- package/dist/routers/tokens.mjs +2 -5
- package/dist/routers/tokens.mjs.map +1 -1
- package/dist/routers/ui.mjs +34 -5
- package/dist/routers/ui.mjs.map +1 -1
- package/dist/routers/webhooks.mjs +5 -11
- package/dist/routers/webhooks.mjs.map +1 -1
- package/dist/routers/well-known.mjs +11 -4
- package/dist/routers/well-known.mjs.map +1 -1
- package/dist/routes/api-routes.mjs +93 -0
- package/dist/routes/api-routes.mjs.map +1 -0
- package/dist/routes/classification.mjs +38 -0
- package/dist/routes/classification.mjs.map +1 -0
- package/dist/routes/openapi-classes.mjs +22 -0
- package/dist/routes/openapi-classes.mjs.map +1 -0
- package/dist/routes/page-routes.mjs +104 -0
- package/dist/routes/page-routes.mjs.map +1 -0
- package/dist/runtime.mjs +18 -13
- package/dist/runtime.mjs.map +1 -1
- package/dist/serverless.mjs +5 -0
- package/dist/store.mjs +19 -1
- package/dist/store.mjs.map +1 -1
- package/dist/targets.mjs +46 -0
- package/dist/targets.mjs.map +1 -0
- package/dist/ui/buttons.mjs +16 -3
- package/dist/ui/buttons.mjs.map +1 -1
- package/dist/ui/design-reference.mjs +97 -0
- package/dist/ui/design-reference.mjs.map +1 -0
- package/dist/ui/document.mjs +1 -2
- package/dist/ui/document.mjs.map +1 -1
- package/dist/ui/forms.mjs +6 -5
- package/dist/ui/forms.mjs.map +1 -1
- package/dist/ui/icons/paths.mjs +1 -0
- package/dist/ui/icons/paths.mjs.map +1 -1
- package/dist/ui/preview-embed.mjs +82 -0
- package/dist/ui/preview-embed.mjs.map +1 -0
- package/dist/ui/shell/client-script.mjs +85 -0
- package/dist/ui/shell/client-script.mjs.map +1 -1
- package/dist/ui/shell/nav.mjs +12 -8
- package/dist/ui/shell/nav.mjs.map +1 -1
- package/dist/ui/shell/review-script.mjs +8 -0
- package/dist/ui/shell/review-script.mjs.map +1 -1
- package/dist/ui/shell/sidebar.mjs +2 -2
- package/dist/ui/shell/sidebar.mjs.map +1 -1
- package/package.json +11 -6
- package/dist/asset-paths.mjs +0 -23
- package/dist/asset-paths.mjs.map +0 -1
- package/dist/assets/mark.svg +0 -1
- package/dist/pages/settings-status.mjs +0 -137
- package/dist/pages/settings-status.mjs.map +0 -1
- package/dist/routers/settings-status.mjs +0 -77
- package/dist/routers/settings-status.mjs.map +0 -1
- package/dist/routers/status-configs.mjs +0 -108
- package/dist/routers/status-configs.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-status.mjs","names":[],"sources":["../src/git-status.ts"],"sourcesContent":["/** Ask for a build's state to be shown on its project's git channels (ADR 0014, decision 5). */\nimport { createBuildStatePublisher, requestOpenBuildStates } from \"@storyshelf/core/capture\";\nimport { buildStateFlags } from \"./build-state-flags.ts\";\nimport { credentialKeys } from \"./credential-keys.ts\";\nimport { notificationOrigin } from \"./notifications-pause.ts\";\nimport { getStore, type Store } from \"./store.ts\";\n\n/**\n * Show the build's current state (commit statuses and the pull-request comment) on every git\n * channel of its project. Called on every transition a request causes: build creation, a review\n * outcome, a retry. Delivery reads the build when it runs, so the call needs no state of its own;\n * it never rejects, so a git host cannot fail the action that triggered it.\n */\nexport async function requestBuildState(buildId: string): Promise<void> {\n const store = getStore();\n await (store.buildState ?? inlinePublisher(store)).request(buildId);\n}\n\n/**\n * An origin just became resolved (an admin confirmed or reported one): requests made while none was\n * were dropped, so ask again for the builds that can still change (ADR 0014, decision 11).\n * Resolves to how many builds were asked; never rejects.\n */\nexport async function requestOpenBuildStatesNow(): Promise<number> {\n const store = getStore();\n return await requestOpenBuildStates(store, { request: requestBuildState });\n}\n\n/** Hand-built stores have no wiring: deliver inline through the store's own adapters. */\nfunction inlinePublisher(store: Store): ReturnType<typeof createBuildStatePublisher> {\n return createBuildStatePublisher({\n deps: {\n db: store.db,\n storage: store.storage,\n notifiers: store.notifiers,\n secret: credentialKeys(store.config),\n config: store.config,\n publicOrigin: async () => await notificationOrigin(store),\n flags: (project) => buildStateFlags(store.testing, project),\n logger: store.logger,\n },\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAaA,eAAsB,kBAAkB,SAAgC;CACtE,MAAM,QAAQ,SAAS;CACvB,OAAO,MAAM,cAAc,gBAAgB,KAAK,EAAA,CAAG,QAAQ,OAAO;AACpE;;;;;;AAOA,eAAsB,4BAA6C;CACjE,MAAM,QAAQ,SAAS;CACvB,OAAO,MAAM,uBAAuB,OAAO,EAAE,SAAS,kBAAkB,CAAC;AAC3E;;AAGA,SAAS,gBAAgB,OAA4D;CACnF,OAAO,0BAA0B,EAC/B,MAAM;EACJ,IAAI,MAAM;EACV,SAAS,MAAM;EACf,WAAW,MAAM;EACjB,QAAQ,eAAe,MAAM,MAAM;EACnC,QAAQ,MAAM;EACd,cAAc,YAAY,MAAM,mBAAmB,KAAK;EACxD,QAAQ,YAAY,gBAAgB,MAAM,SAAS,OAAO;EAC1D,QAAQ,MAAM;CAChB,EACF,CAAC;AACH"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
//#region src/hidden-data.ts
|
|
5
|
+
/**
|
|
6
|
+
* Snapshots stored in the database. With `testing` off they are hidden, not deleted (ADR 0006,
|
|
7
|
+
* decision 1): the System page and the startup log say how many.
|
|
8
|
+
*/
|
|
9
|
+
async function countHiddenSnapshots(db) {
|
|
10
|
+
return await db.count(db.tables.snapshots);
|
|
11
|
+
}
|
|
12
|
+
/** Log the mode at boot; with `testing` off, also how much captured data it hides. */
|
|
13
|
+
async function logHostingOnlyMode(db, logger) {
|
|
14
|
+
const hiddenSnapshots = await countHiddenSnapshots(db);
|
|
15
|
+
if (hiddenSnapshots > 0) {
|
|
16
|
+
logger.warn({ hiddenSnapshots }, "testing is off: existing snapshots are hidden, not deleted; they reappear when testing is turned on");
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
logger.info("testing is off: hosting Storybooks only (no capture, compare or baselines)");
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { countHiddenSnapshots, logHostingOnlyMode };
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=hidden-data.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hidden-data.mjs","names":[],"sources":["../src/hidden-data.ts"],"sourcesContent":["import type { DatabaseAdapter } from \"@storyshelf/core/adapter/database\";\nimport type { Logger } from \"@storyshelf/core/logger\";\n\n/**\n * Snapshots stored in the database. With `testing` off they are hidden, not deleted (ADR 0006,\n * decision 1): the System page and the startup log say how many.\n */\nexport async function countHiddenSnapshots(db: DatabaseAdapter): Promise<number> {\n return await db.count(db.tables.snapshots);\n}\n\n/** Log the mode at boot; with `testing` off, also how much captured data it hides. */\nexport async function logHostingOnlyMode(db: DatabaseAdapter, logger: Logger): Promise<void> {\n const hiddenSnapshots = await countHiddenSnapshots(db);\n if (hiddenSnapshots > 0) {\n logger.warn(\n { hiddenSnapshots },\n \"testing is off: existing snapshots are hidden, not deleted; they reappear when testing is turned on\",\n );\n return;\n }\n logger.info(\"testing is off: hosting Storybooks only (no capture, compare or baselines)\");\n}\n"],"mappings":";;;;;;;;AAOA,eAAsB,qBAAqB,IAAsC;CAC/E,OAAO,MAAM,GAAG,MAAM,GAAG,OAAO,SAAS;AAC3C;;AAGA,eAAsB,mBAAmB,IAAqB,QAA+B;CAC3F,MAAM,kBAAkB,MAAM,qBAAqB,EAAE;CACrD,IAAI,kBAAkB,GAAG;EACvB,OAAO,KACL,EAAE,gBAAgB,GAClB,qGACF;EACA;CACF;CACA,OAAO,KAAK,4EAA4E;AAC1F"}
|
package/dist/index.d.mts
CHANGED
|
@@ -2,7 +2,7 @@ import __tsdown_shims_path from 'node:path';
|
|
|
2
2
|
import __tsdown_shims_url from 'node:url';
|
|
3
3
|
import { ShelfApp, ShelfContext, ShelfLifecycle, ShelfRouter } from "./app-types.mjs";
|
|
4
4
|
import { createAuthSystemHook } from "./notify.mjs";
|
|
5
|
-
import { BrandTheme, NotificationsConfig, ShelfConfig, ShelfOptions, ShelfOptions as ShelfOptions$1, UIConfig } from "@storyshelf/core/config";
|
|
5
|
+
import { BrandTheme, NotificationsConfig, ShelfCompareOptions, ShelfConfig, ShelfNotificationsOptions, ShelfOptions, ShelfOptions as ShelfOptions$1, ShelfTestingOptions, UIConfig } from "@storyshelf/core/config";
|
|
6
6
|
import { Logger, LoggerOptions, PinoTransport, createShelfLogger } from "@storyshelf/core/logger";
|
|
7
7
|
//#region src/index.d.ts
|
|
8
8
|
/**
|
|
@@ -12,7 +12,7 @@ import { Logger, LoggerOptions, PinoTransport, createShelfLogger } from "@storys
|
|
|
12
12
|
* stays synchronous). Await `app.lifecycle.setup()` before serving for
|
|
13
13
|
* fail-fast startup, or let the first request gate on settlement.
|
|
14
14
|
*/
|
|
15
|
-
declare function createShelfApp(
|
|
15
|
+
declare function createShelfApp(rawOptions: ShelfOptions$1): ShelfApp;
|
|
16
16
|
//#endregion
|
|
17
|
-
export { type BrandTheme, type Logger, type LoggerOptions, type NotificationsConfig, type PinoTransport, type ShelfApp, type ShelfConfig, type ShelfContext, type ShelfLifecycle, type ShelfOptions, type ShelfRouter, type UIConfig, createAuthSystemHook, createShelfApp, createShelfLogger };
|
|
17
|
+
export { type BrandTheme, type Logger, type LoggerOptions, type NotificationsConfig, type PinoTransport, type ShelfApp, type ShelfCompareOptions, type ShelfConfig, type ShelfContext, type ShelfLifecycle, type ShelfNotificationsOptions, type ShelfOptions, type ShelfRouter, type ShelfTestingOptions, type UIConfig, createAuthSystemHook, createShelfApp, createShelfLogger };
|
|
18
18
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -2,49 +2,67 @@ import "node:path";
|
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { createAuthSystemHook } from "./notify.mjs";
|
|
5
|
-
import { setupCaptureQueue } from "./capture-setup.mjs";
|
|
6
|
-
import { checkCredentialsAfterSetup } from "./credentials.mjs";
|
|
7
5
|
import { attachLifecycle } from "./lifecycle.mjs";
|
|
8
|
-
import {
|
|
6
|
+
import { runBootTasks } from "./boot-tasks.mjs";
|
|
7
|
+
import { setupJobs } from "./jobs-setup.mjs";
|
|
8
|
+
import { createRateLimiter, rateLimitKey } from "./middleware/rate-limit.mjs";
|
|
9
9
|
import { csrf } from "./middleware/csrf.mjs";
|
|
10
10
|
import { requestLogging } from "./middleware/request-log.mjs";
|
|
11
11
|
import { storeScope } from "./middleware/store-scope.mjs";
|
|
12
12
|
import { authGate, resolveRequestUser } from "./middleware/auth-gate.mjs";
|
|
13
|
+
import { requireTesting } from "./middleware/require-testing.mjs";
|
|
14
|
+
import { requireReview } from "./middleware/require-review.mjs";
|
|
13
15
|
import { serverTiming } from "./middleware/server-timing.mjs";
|
|
14
16
|
import { setupGate } from "./middleware/setup-gate.mjs";
|
|
15
17
|
import { registerAdminPages } from "./routers/admin-pages.mjs";
|
|
18
|
+
import { registerPublicOriginRoutes } from "./routers/admin-public-origin.mjs";
|
|
16
19
|
import { registerAdmin } from "./routers/admin.mjs";
|
|
17
20
|
import { registerAssets } from "./routers/assets.mjs";
|
|
18
21
|
import { registerEngineAuth } from "./routers/auth.mjs";
|
|
22
|
+
import { registerBaselines } from "./routers/baselines.mjs";
|
|
19
23
|
import { registerBuilds } from "./routers/builds.mjs";
|
|
24
|
+
import { registerConnections } from "./routers/connections.mjs";
|
|
20
25
|
import { registerHealth } from "./routers/health.mjs";
|
|
26
|
+
import { registerInsightFragments } from "./routers/insights-ui.mjs";
|
|
27
|
+
import { registerInsights } from "./routers/insights.mjs";
|
|
21
28
|
import { registerLabels } from "./routers/labels.mjs";
|
|
29
|
+
import { registerLinks } from "./routers/links.mjs";
|
|
22
30
|
import { registerMedia } from "./routers/media.mjs";
|
|
23
31
|
import { registerMembers } from "./routers/members.mjs";
|
|
24
32
|
import { registerNotificationChannels, registerSystemChannels } from "./routers/notification-channels.mjs";
|
|
25
33
|
import { registerNotificationSubscriptions } from "./routers/notification-subscriptions.mjs";
|
|
34
|
+
import { registerPreviewToken } from "./routers/preview-token.mjs";
|
|
26
35
|
import { registerProfile } from "./routers/profile.mjs";
|
|
27
36
|
import { registerProjects } from "./routers/projects.mjs";
|
|
28
|
-
import {
|
|
37
|
+
import { registerReviewForms } from "./routers/review-forms.mjs";
|
|
38
|
+
import { registerSignedStorybook } from "./routers/signed-storybook.mjs";
|
|
29
39
|
import { registerStorybook } from "./routers/storybook.mjs";
|
|
30
40
|
import { registerTokens } from "./routers/tokens.mjs";
|
|
31
41
|
import { registerUiPages } from "./routers/ui.mjs";
|
|
32
42
|
import { registerWebhooks } from "./routers/webhooks.mjs";
|
|
33
43
|
import { registerWellKnown } from "./routers/well-known.mjs";
|
|
44
|
+
import { markRouteClasses } from "./routes/openapi-classes.mjs";
|
|
34
45
|
import { resolveRuntime } from "./runtime.mjs";
|
|
35
46
|
import { swaggerUI } from "@hono/swagger-ui";
|
|
36
47
|
import { OpenAPIHono } from "@hono/zod-openapi";
|
|
37
|
-
import { AdapterLifecycleError, validateBootAssembly } from "@storyshelf/core/adapter/setup";
|
|
48
|
+
import { AdapterLifecycleError, adapterSourcesOf, validateBootAssembly } from "@storyshelf/core/adapter/setup";
|
|
49
|
+
import { assertServerless, resolveShelfOptions } from "@storyshelf/core/config";
|
|
50
|
+
import { configureImages } from "@storyshelf/core/diff";
|
|
51
|
+
import { STORYBOOK_TOKEN_PREFIX } from "@storyshelf/core/utils";
|
|
38
52
|
import { createHttpMiddleware, createInstrumentedDatabase, createInstrumentedStorage } from "@storyshelf/observability";
|
|
39
53
|
import { requestId } from "hono/request-id";
|
|
40
54
|
import { createShelfLogger } from "@storyshelf/core/logger";
|
|
41
55
|
//#region src/index.tsx
|
|
42
56
|
/**
|
|
57
|
+
* StoryShelf app: compose database, storage, capture, auth, and notifier
|
|
58
|
+
* adapters into a complete self-hosted visual-testing Hono app.
|
|
59
|
+
*/
|
|
60
|
+
/**
|
|
43
61
|
* Fail fast on third-party adapters that cannot satisfy their slot.
|
|
44
62
|
* Throws before any route or lifecycle hook runs.
|
|
45
63
|
*/
|
|
46
64
|
function throwOnInvalidAdapters(options) {
|
|
47
|
-
const invalid = validateBootAssembly(options);
|
|
65
|
+
const invalid = validateBootAssembly(adapterSourcesOf(options));
|
|
48
66
|
if (invalid.length > 0) throw new AdapterLifecycleError("setup", invalid);
|
|
49
67
|
}
|
|
50
68
|
/** Request/capture-path adapters with OTEL wrappers (lifecycle keeps raw ones). */
|
|
@@ -55,23 +73,25 @@ function instrumentedOptions(options) {
|
|
|
55
73
|
storage: createInstrumentedStorage(options.storage)
|
|
56
74
|
};
|
|
57
75
|
}
|
|
58
|
-
/**
|
|
59
|
-
function
|
|
60
|
-
|
|
61
|
-
cell.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
logger.error({ err: error }, "stuck capture recovery failed");
|
|
65
|
-
});
|
|
76
|
+
/** Wire the job queue, its handlers and its consumer, and run the boot tasks that need them. */
|
|
77
|
+
function wireQueues(scoped, runtime, cell) {
|
|
78
|
+
const jobs = setupJobs(scoped, runtime);
|
|
79
|
+
cell.consumer = jobs.consumer;
|
|
80
|
+
runBootTasks(cell, scoped, runtime, jobs.buildState);
|
|
81
|
+
return jobs;
|
|
66
82
|
}
|
|
67
|
-
/**
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Serverless check (ADR 0006, decision 3): throws on AWS Lambda, warns on Vercel and Netlify. Only
|
|
85
|
+
* an instance with `testing` needs a process that outlives the request.
|
|
86
|
+
*/
|
|
87
|
+
function warnOnServerless(options, logger) {
|
|
88
|
+
const warning = assertServerless(options.testing?.runner !== void 0);
|
|
89
|
+
if (warning) logger.warn(warning);
|
|
71
90
|
}
|
|
72
91
|
/** Attach lifecycle, queue, middleware, routes, and docs to the app. */
|
|
73
92
|
function assembleApp(app, options, scoped) {
|
|
74
93
|
const runtime = resolveRuntime(options);
|
|
94
|
+
warnOnServerless(options, runtime.logger);
|
|
75
95
|
const cell = {
|
|
76
96
|
ready: Promise.resolve({
|
|
77
97
|
ok: true,
|
|
@@ -81,17 +101,15 @@ function assembleApp(app, options, scoped) {
|
|
|
81
101
|
};
|
|
82
102
|
attachLifecycle(app, options, runtime, cell);
|
|
83
103
|
if (runtime.config.serverTiming === true) app.use("*", serverTiming(true));
|
|
84
|
-
const
|
|
85
|
-
runAfterSetup(cell, scoped, runtime, recoverStuck);
|
|
104
|
+
const queues = wireQueues(scoped, runtime, cell);
|
|
86
105
|
wireMiddleware(app, {
|
|
87
106
|
...runtime,
|
|
88
|
-
|
|
89
|
-
enqueueCapture,
|
|
107
|
+
...queues,
|
|
90
108
|
options: scoped,
|
|
91
109
|
getReady: async () => await cell.ready
|
|
92
110
|
});
|
|
93
111
|
registerAllRoutes(app, options, {
|
|
94
|
-
sources: options,
|
|
112
|
+
sources: adapterSourcesOf(options),
|
|
95
113
|
getSettled: () => cell.settled,
|
|
96
114
|
bootTimeMs: Date.now(),
|
|
97
115
|
version: packageVersion()
|
|
@@ -105,7 +123,12 @@ function assembleApp(app, options, scoped) {
|
|
|
105
123
|
* stays synchronous). Await `app.lifecycle.setup()` before serving for
|
|
106
124
|
* fail-fast startup, or let the first request gate on settlement.
|
|
107
125
|
*/
|
|
108
|
-
function createShelfApp(
|
|
126
|
+
function createShelfApp(rawOptions) {
|
|
127
|
+
const options = resolveShelfOptions(rawOptions);
|
|
128
|
+
configureImages({
|
|
129
|
+
engine: options.testing?.images?.engine,
|
|
130
|
+
threads: options.testing?.compare?.threads
|
|
131
|
+
});
|
|
109
132
|
throwOnInvalidAdapters(options);
|
|
110
133
|
const app = new OpenAPIHono();
|
|
111
134
|
assembleApp(app, options, instrumentedOptions(options));
|
|
@@ -117,8 +140,16 @@ const CSRF_PATHS = [
|
|
|
117
140
|
"/auth/invites/*",
|
|
118
141
|
"/projects/:slug/settings/*",
|
|
119
142
|
"/profile/*",
|
|
143
|
+
"/connections/*",
|
|
120
144
|
"/profile",
|
|
121
|
-
"/admin/credentials/*"
|
|
145
|
+
"/admin/credentials/*",
|
|
146
|
+
"/admin/public-origin",
|
|
147
|
+
"/admin/public-origin/*",
|
|
148
|
+
"/projects/:slug/builds/:buildId/insights/generate",
|
|
149
|
+
"/projects/:slug/builds/:buildId/comments",
|
|
150
|
+
"/projects/:slug/builds/:buildId/comments/*",
|
|
151
|
+
"/projects/:slug/builds/:buildId/reviews",
|
|
152
|
+
"/projects/:slug/insights/health/generate"
|
|
122
153
|
];
|
|
123
154
|
/** Attach global middleware: ids, logging, init gate, limits, store scope, auth gate. */
|
|
124
155
|
function wireMiddleware(app, wiring) {
|
|
@@ -130,64 +161,76 @@ function wireMiddleware(app, wiring) {
|
|
|
130
161
|
}));
|
|
131
162
|
app.use("*", requestLogging(logger));
|
|
132
163
|
app.use("*", setupGate(getReady));
|
|
133
|
-
|
|
134
|
-
|
|
164
|
+
app.use("*", requireTesting(wiring.options.testing !== void 0));
|
|
165
|
+
mountGuards(app, config, wiring.options);
|
|
135
166
|
mountStoreScope(app, wiring);
|
|
136
167
|
app.use("*", authGate());
|
|
168
|
+
app.use("*", requireReview());
|
|
137
169
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
170
|
+
const MINUTE = 6e4;
|
|
171
|
+
/** Per-path limits; `bucket` gives a route family its own counter (default: shared `api:`). */
|
|
172
|
+
const RATE_LIMITS = [
|
|
173
|
+
{
|
|
174
|
+
paths: ["/api/v1/*"],
|
|
142
175
|
max: 100
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
paths: ["/api/v1/tokens/*"],
|
|
146
179
|
max: 10
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
paths: ["/api/v1/webhooks/*"],
|
|
150
183
|
max: 20
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
paths: ["/auth/*"],
|
|
154
187
|
max: 100,
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
|
|
188
|
+
bucket: "auth"
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
paths: ["/api/auth/*"],
|
|
159
192
|
max: 300,
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
/** Abuse floors for notification management endpoints (own counters). */
|
|
165
|
-
function mountNotificationLimits(app) {
|
|
166
|
-
const channel = {
|
|
167
|
-
windowMs: 6e4,
|
|
193
|
+
bucket: "engine"
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
paths: ["/api/v1/projects/:slug/notification-channels", "/api/v1/projects/:slug/notification-channels/:channelId"],
|
|
168
197
|
max: 20,
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
max: 20,
|
|
174
|
-
keyGenerator: rateLimitKey("notify-admin")
|
|
175
|
-
};
|
|
176
|
-
const subscription = {
|
|
177
|
-
windowMs: 6e4,
|
|
198
|
+
bucket: "notify-channels"
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
paths: ["/api/v1/projects/:slug/notifications", "/api/v1/projects/:slug/notifications/me"],
|
|
178
202
|
max: 30,
|
|
179
|
-
|
|
203
|
+
bucket: "notify-subs"
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
paths: ["/api/v1/admin/notification-channels", "/api/v1/admin/notification-channels/:channelId"],
|
|
207
|
+
max: 20,
|
|
208
|
+
bucket: "notify-admin"
|
|
209
|
+
}
|
|
210
|
+
];
|
|
211
|
+
/** Requests per minute per client IP on the signed Storybook URL route (`testing.storybookUrlRateLimit`). */
|
|
212
|
+
const DEFAULT_STORYBOOK_URL_RATE_LIMIT = 6e3;
|
|
213
|
+
/** Mount rate limits and CSRF guards (abuse floors, not strict protection). */
|
|
214
|
+
function mountGuards(app, config, options) {
|
|
215
|
+
const rateLimit = createRateLimiter();
|
|
216
|
+
const signedUrls = {
|
|
217
|
+
paths: [`${STORYBOOK_TOKEN_PREFIX}*`],
|
|
218
|
+
max: options.testing?.storybookUrlRateLimit ?? DEFAULT_STORYBOOK_URL_RATE_LIMIT,
|
|
219
|
+
bucket: "signed-storybook"
|
|
180
220
|
};
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
221
|
+
for (const { paths, max, bucket } of [...RATE_LIMITS, signedUrls]) {
|
|
222
|
+
const keyGenerator = bucket ? rateLimitKey(bucket) : void 0;
|
|
223
|
+
for (const path of paths) app.use(path, rateLimit({
|
|
224
|
+
windowMs: MINUTE,
|
|
225
|
+
max,
|
|
226
|
+
keyGenerator
|
|
227
|
+
}));
|
|
228
|
+
}
|
|
229
|
+
for (const path of CSRF_PATHS) app.use(path, csrf(config.secret));
|
|
187
230
|
}
|
|
188
231
|
/** Mount the request-scoped store (db/storage/config/user/adapters). */
|
|
189
232
|
function mountStoreScope(app, wiring) {
|
|
190
|
-
const { options, config, ui, logger, authEnabled, enqueueCapture,
|
|
233
|
+
const { options, config, ui, logger, authEnabled, enqueueCapture, captureQueue, capturesHere, publish, cancelBuildJobs, buildState, design, notifiers, publicOrigin } = wiring;
|
|
191
234
|
app.use("*", storeScope({
|
|
192
235
|
db: options.database,
|
|
193
236
|
storage: options.storage,
|
|
@@ -195,10 +238,20 @@ function mountStoreScope(app, wiring) {
|
|
|
195
238
|
ui,
|
|
196
239
|
logger,
|
|
197
240
|
authEnabled,
|
|
241
|
+
testing: options.testing !== void 0,
|
|
198
242
|
enqueueCapture,
|
|
199
|
-
captureQueue
|
|
200
|
-
|
|
243
|
+
captureQueue,
|
|
244
|
+
capturesHere,
|
|
245
|
+
publish,
|
|
246
|
+
cancelBuildJobs,
|
|
247
|
+
buildState,
|
|
248
|
+
captureConcurrency: options.testing?.concurrency,
|
|
249
|
+
runner: options.testing?.runner,
|
|
250
|
+
publicOrigin,
|
|
251
|
+
design,
|
|
201
252
|
notifiers,
|
|
253
|
+
notificationsConfig: options.notifications?.config,
|
|
254
|
+
ai: options.ai,
|
|
202
255
|
resolveUser: async (c) => await resolveRequestUser(c, options.auth)
|
|
203
256
|
}));
|
|
204
257
|
}
|
|
@@ -206,7 +259,6 @@ function mountStoreScope(app, wiring) {
|
|
|
206
259
|
function registerApiRoutes(app, health) {
|
|
207
260
|
registerProjectRoutes(app);
|
|
208
261
|
registerNotificationRoutes(app);
|
|
209
|
-
registerStatusConfigs(app);
|
|
210
262
|
registerHealth(app, health);
|
|
211
263
|
registerAdmin(app);
|
|
212
264
|
}
|
|
@@ -215,10 +267,13 @@ function registerProjectRoutes(app) {
|
|
|
215
267
|
registerProjects(app);
|
|
216
268
|
registerBuilds(app);
|
|
217
269
|
registerLabels(app);
|
|
270
|
+
registerLinks(app);
|
|
271
|
+
registerBaselines(app);
|
|
218
272
|
registerMedia(app);
|
|
219
273
|
registerMembers(app);
|
|
220
274
|
registerTokens(app);
|
|
221
275
|
registerWebhooks(app);
|
|
276
|
+
registerInsights(app);
|
|
222
277
|
}
|
|
223
278
|
/** Register the notification channel and subscription routers. */
|
|
224
279
|
function registerNotificationRoutes(app) {
|
|
@@ -232,77 +287,100 @@ function registerAuthFlow(app, options) {
|
|
|
232
287
|
registerEngineAuth(app, options.auth);
|
|
233
288
|
registerWellKnown(app, options.auth);
|
|
234
289
|
registerProfile(app, options.auth);
|
|
290
|
+
registerConnections(app);
|
|
235
291
|
}
|
|
236
292
|
/** Register HTML pages, assets, and the optional auth flow. */
|
|
237
293
|
function registerPageRoutes(app, options, health) {
|
|
238
294
|
registerAuthFlow(app, options);
|
|
239
295
|
registerAssets(app);
|
|
240
296
|
registerStorybook(app);
|
|
297
|
+
registerSignedStorybook(app);
|
|
298
|
+
registerPreviewToken(app);
|
|
241
299
|
registerAdminPages(app, health, options.auth);
|
|
300
|
+
registerPublicOriginRoutes(app);
|
|
242
301
|
registerUiPages(app);
|
|
302
|
+
registerReviewForms(app);
|
|
303
|
+
registerInsightFragments(app);
|
|
243
304
|
}
|
|
244
305
|
/** Register every API router, page set, and optional auth flow. */
|
|
245
306
|
function registerAllRoutes(app, options, health) {
|
|
246
307
|
registerApiRoutes(app, health);
|
|
247
308
|
registerPageRoutes(app, options, health);
|
|
248
309
|
}
|
|
310
|
+
/** The OpenAPI document's info and tag catalog. */
|
|
311
|
+
const OPENAPI_CONFIG = {
|
|
312
|
+
openapi: "3.0.0",
|
|
313
|
+
info: {
|
|
314
|
+
title: "StoryShelf API",
|
|
315
|
+
description: "REST API for the StoryShelf visual testing platform. JSON endpoints live under /api/v1; HTML pages are served at /.",
|
|
316
|
+
version: "1.0.0"
|
|
317
|
+
},
|
|
318
|
+
tags: [
|
|
319
|
+
{
|
|
320
|
+
name: "Projects",
|
|
321
|
+
description: "Create and manage projects (one project is one Storybook)."
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
name: "Builds",
|
|
325
|
+
description: "Upload Storybook builds, inspect capture attempts, retry, and delete."
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
name: "Review",
|
|
329
|
+
description: "Approve or reject snapshots, bulk actions, re-diff, build verdicts, the build changeset and review comments."
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
name: "Labels",
|
|
333
|
+
description: "Typed build labels and their link templates."
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
name: "Links",
|
|
337
|
+
description: "Link packages to the projects that publish them and see where stories use them."
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
name: "Baselines",
|
|
341
|
+
description: "Export and import baselines as a portable zip."
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
name: "Members",
|
|
345
|
+
description: "Project members, roles, and identity-provider group mappings."
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
name: "Insights",
|
|
349
|
+
description: "AI build triage and project health (advisory only; needs AI configured)."
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
name: "Tokens",
|
|
353
|
+
description: "API tokens used by the CLI and CI."
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
name: "Webhooks",
|
|
357
|
+
description: "Signed webhook deliveries for build events."
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
name: "Notifications",
|
|
361
|
+
description: "Email, chat and git notification channels and subscriptions."
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
name: "Admin",
|
|
365
|
+
description: "Site-wide administration."
|
|
366
|
+
}
|
|
367
|
+
]
|
|
368
|
+
};
|
|
249
369
|
/** Register the OpenAPI document and interactive docs page. */
|
|
250
370
|
function registerDocs(app) {
|
|
251
|
-
app.
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
description: "REST API for the StoryShelf visual testing platform. JSON endpoints live under /api/v1; HTML pages are served at /.",
|
|
256
|
-
version: "1.0.0"
|
|
257
|
-
},
|
|
258
|
-
tags: [
|
|
259
|
-
{
|
|
260
|
-
name: "Projects",
|
|
261
|
-
description: "Create and manage projects (one project is one Storybook)."
|
|
262
|
-
},
|
|
263
|
-
{
|
|
264
|
-
name: "Builds",
|
|
265
|
-
description: "Upload Storybook builds, inspect capture attempts, retry, and delete."
|
|
266
|
-
},
|
|
267
|
-
{
|
|
268
|
-
name: "Review",
|
|
269
|
-
description: "Approve or reject snapshots, bulk actions, re-diff, and review comments."
|
|
270
|
-
},
|
|
271
|
-
{
|
|
272
|
-
name: "Labels",
|
|
273
|
-
description: "Typed build labels and their link templates."
|
|
274
|
-
},
|
|
275
|
-
{
|
|
276
|
-
name: "Members",
|
|
277
|
-
description: "Project members, roles, and identity-provider group mappings."
|
|
278
|
-
},
|
|
279
|
-
{
|
|
280
|
-
name: "Tokens",
|
|
281
|
-
description: "API tokens used by the CLI and CI."
|
|
282
|
-
},
|
|
283
|
-
{
|
|
284
|
-
name: "Webhooks",
|
|
285
|
-
description: "Signed webhook deliveries for build events."
|
|
286
|
-
},
|
|
287
|
-
{
|
|
288
|
-
name: "Notifications",
|
|
289
|
-
description: "Email and chat notification channels and subscriptions."
|
|
290
|
-
},
|
|
291
|
-
{
|
|
292
|
-
name: "Git status",
|
|
293
|
-
description: "Commit-status integrations for GitHub and GitLab."
|
|
294
|
-
},
|
|
295
|
-
{
|
|
296
|
-
name: "Admin",
|
|
297
|
-
description: "Site-wide administration."
|
|
298
|
-
}
|
|
299
|
-
]
|
|
300
|
-
});
|
|
371
|
+
app.get("/api/v1/openapi.json", (c) => c.json(markRouteClasses(app.getOpenAPIDocument({
|
|
372
|
+
...OPENAPI_CONFIG,
|
|
373
|
+
tags: [...OPENAPI_CONFIG.tags]
|
|
374
|
+
}))));
|
|
301
375
|
app.get("/api/v1/docs", swaggerUI({ url: "/api/v1/openapi.json" }));
|
|
302
376
|
}
|
|
303
|
-
/**
|
|
377
|
+
/**
|
|
378
|
+
* Package version injected at build via the `__PKG_VERSION__` define. Read as a bare identifier:
|
|
379
|
+
* bundler defines replace identifiers, not `globalThis` property reads. Undefined when running
|
|
380
|
+
* from source (dev server, tests).
|
|
381
|
+
*/
|
|
304
382
|
function packageVersion() {
|
|
305
|
-
return
|
|
383
|
+
return "0.7.0";
|
|
306
384
|
}
|
|
307
385
|
//#endregion
|
|
308
386
|
export { createAuthSystemHook, createShelfApp, createShelfLogger };
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.tsx"],"sourcesContent":["import { swaggerUI } from \"@hono/swagger-ui\";\nimport { OpenAPIHono } from \"@hono/zod-openapi\";\nimport { AdapterLifecycleError, validateBootAssembly } from \"@storyshelf/core/adapter/setup\";\nimport type { ShelfConfig, ShelfOptions } from \"@storyshelf/core/config\";\n/**\n * StoryShelf app: compose database, storage, capture, auth, and git-host\n * adapters into a complete self-hosted visual-testing Hono app.\n */\nimport type { Logger } from \"@storyshelf/core/logger\";\nimport {\n createHttpMiddleware,\n createInstrumentedDatabase,\n createInstrumentedStorage,\n} from \"@storyshelf/observability\";\nimport { requestId } from \"hono/request-id\";\nimport type { ShelfApp, ShelfContext, ShelfRouter } from \"./app-types.ts\";\nimport { setupCaptureQueue } from \"./capture-setup.ts\";\nimport { checkCredentialsAfterSetup } from \"./credentials.ts\";\nimport { attachLifecycle, type LifecycleCell } from \"./lifecycle.ts\";\nimport {\n authGate,\n csrf,\n rateLimit,\n rateLimitKey,\n requestLogging,\n resolveRequestUser,\n storeScope,\n} from \"./middleware/index.ts\";\nimport { serverTiming } from \"./middleware/server-timing.ts\";\nimport { setupGate, type MiddlewareWiring } from \"./middleware/setup-gate.ts\";\nimport { registerAdminPages } from \"./routers/admin-pages.ts\";\nimport { registerAdmin } from \"./routers/admin.ts\";\nimport { registerAssets } from \"./routers/assets.ts\";\nimport { registerEngineAuth } from \"./routers/auth.ts\";\nimport { registerBuilds } from \"./routers/builds.ts\";\nimport { registerHealth, type HealthDeps } from \"./routers/health.ts\";\nimport { registerLabels } from \"./routers/labels.ts\";\nimport { registerMedia } from \"./routers/media.ts\";\nimport { registerMembers } from \"./routers/members.ts\";\nimport {\n registerNotificationChannels,\n registerSystemChannels,\n} from \"./routers/notification-channels.ts\";\nimport { registerNotificationSubscriptions } from \"./routers/notification-subscriptions.ts\";\nimport { registerProfile } from \"./routers/profile.ts\";\nimport { registerProjects } from \"./routers/projects.ts\";\nimport { registerStatusConfigs } from \"./routers/status-configs.ts\";\nimport { registerStorybook } from \"./routers/storybook.ts\";\nimport { registerTokens } from \"./routers/tokens.ts\";\nimport { registerUiPages } from \"./routers/ui.ts\";\nimport { registerWebhooks } from \"./routers/webhooks.ts\";\nimport { registerWellKnown } from \"./routers/well-known.ts\";\nimport { resolveRuntime } from \"./runtime.ts\";\n\n/**\n * Fail fast on third-party adapters that cannot satisfy their slot.\n * Throws before any route or lifecycle hook runs.\n */\nfunction throwOnInvalidAdapters(options: ShelfOptions): void {\n const invalid = validateBootAssembly(options);\n if (invalid.length > 0) {\n throw new AdapterLifecycleError(\"setup\", invalid);\n }\n}\n\n/** Request/capture-path adapters with OTEL wrappers (lifecycle keeps raw ones). */\nfunction instrumentedOptions(options: ShelfOptions): ShelfOptions {\n return {\n ...options,\n database: createInstrumentedDatabase(options.database),\n storage: createInstrumentedStorage(options.storage),\n };\n}\n\n/** Once adapters are ready, requeue-once or fail builds a restart left `capturing`. */\nfunction recoverAfterSetup(\n cell: LifecycleCell,\n recoverStuck: (() => Promise<unknown>) | undefined,\n logger: Logger,\n): void {\n if (!recoverStuck) {\n return;\n }\n cell.ready\n .then(async (setup) => {\n if (setup.ok) {\n await recoverStuck();\n }\n })\n .catch((error: unknown) => {\n logger.error({ err: error }, \"stuck capture recovery failed\");\n });\n}\n\n/** Boot-time checks that run once adapters are ready: stuck-capture recovery and credential state. */\nfunction runAfterSetup(\n cell: LifecycleCell,\n scoped: ShelfOptions,\n runtime: ReturnType<typeof resolveRuntime>,\n recoverStuck: (() => Promise<unknown>) | undefined,\n): void {\n recoverAfterSetup(cell, recoverStuck, runtime.logger);\n checkCredentialsAfterSetup(cell, scoped.database, runtime.config, runtime.logger);\n}\n\n/** Attach lifecycle, queue, middleware, routes, and docs to the app. */\nfunction assembleApp(app: ShelfApp, options: ShelfOptions, scoped: ShelfOptions): void {\n const runtime = resolveRuntime(options);\n const cell: LifecycleCell = { ready: Promise.resolve({ ok: true, failures: [] }), settled: null };\n attachLifecycle(app, options, runtime, cell);\n if (runtime.config.serverTiming === true) {\n app.use(\"*\", serverTiming(true));\n }\n const { queue, enqueueCapture, recoverStuck } = setupCaptureQueue(\n scoped,\n runtime.config,\n runtime.gitHosts,\n runtime.logger,\n );\n runAfterSetup(cell, scoped, runtime, recoverStuck);\n wireMiddleware(app, {\n ...runtime,\n queue,\n enqueueCapture,\n options: scoped,\n getReady: async () => await cell.ready,\n });\n registerAllRoutes(app, options, {\n sources: options,\n getSettled: () => cell.settled,\n bootTimeMs: Date.now(),\n version: packageVersion(),\n });\n registerDocs(app);\n}\n\n/**\n * Create the StoryShelf Hono app with all API routes and HTML pages.\n *\n * Adapter `init` hooks kick off eagerly in the background (the constructor\n * stays synchronous). Await `app.lifecycle.setup()` before serving for\n * fail-fast startup, or let the first request gate on settlement.\n */\nexport function createShelfApp(options: ShelfOptions): ShelfApp {\n throwOnInvalidAdapters(options);\n const app = new OpenAPIHono<{ Variables: ShelfContext }>() as ShelfApp;\n assembleApp(app, options, instrumentedOptions(options));\n\n return app;\n}\n\n/** Cookie-authenticated write paths guarded by CSRF tokens. */\nconst CSRF_PATHS = [\n \"/auth/logout\",\n \"/auth/invites/*\",\n \"/projects/:slug/settings/*\",\n \"/profile/*\",\n \"/profile\",\n \"/admin/credentials/*\",\n] as const;\n\n/** Attach global middleware: ids, logging, init gate, limits, store scope, auth gate. */\nfunction wireMiddleware(app: ShelfRouter, wiring: MiddlewareWiring): void {\n const { config, logger, getReady } = wiring;\n app.use(\"*\", requestId());\n // OTEL server spans (W3C extraction, route-template naming). Mounted right\n // after requestId so `storyshelf.req_id` resolves; `enduser.id` resolves\n // once storeScope runs downstream. Noop without an SDK.\n app.use(\n \"*\",\n createHttpMiddleware({ serviceName: \"storyshelf\", serviceVersion: packageVersion() }),\n );\n // Structured request logging. Uses a Hono-native middleware rather than\n // Pino-http, which expects a Node server response (`res.on`) incompatible\n // With Hono's Web `Request`/`Response` model.\n app.use(\"*\", requestLogging(logger));\n app.use(\"*\", setupGate(getReady));\n mountGuards(app, config);\n mountNotificationLimits(app);\n mountStoreScope(app, wiring);\n app.use(\"*\", authGate());\n}\n\n/** Mount rate limits and CSRF guards (abuse floors, not strict protection). */\nfunction mountGuards(app: ShelfRouter, config: ShelfConfig): void {\n app.use(\"/api/v1/*\", rateLimit({ windowMs: 60_000, max: 100 }));\n app.use(\"/api/v1/tokens/*\", rateLimit({ windowMs: 60_000, max: 10 }));\n app.use(\"/api/v1/webhooks/*\", rateLimit({ windowMs: 60_000, max: 20 }));\n // Auth endpoints ride their own buckets (the shared store keys by IP, so\n // prefix the key: login floods must not eat the API budget or vice versa).\n app.use(\"/auth/*\", rateLimit({ windowMs: 60_000, max: 100, keyGenerator: rateLimitKey(\"auth\") }));\n app.use(\n \"/api/auth/*\",\n rateLimit({ windowMs: 60_000, max: 300, keyGenerator: rateLimitKey(\"engine\") }),\n );\n for (const path of CSRF_PATHS) {\n app.use(path, csrf(config.secret));\n }\n}\n\n/** Abuse floors for notification management endpoints (own counters). */\nfunction mountNotificationLimits(app: ShelfRouter): void {\n const channel = { windowMs: 60_000, max: 20, keyGenerator: rateLimitKey(\"notify-channels\") };\n const admin = { windowMs: 60_000, max: 20, keyGenerator: rateLimitKey(\"notify-admin\") };\n const subscription = { windowMs: 60_000, max: 30, keyGenerator: rateLimitKey(\"notify-subs\") };\n app.use(\"/api/v1/projects/:slug/notification-channels\", rateLimit(channel));\n app.use(\"/api/v1/projects/:slug/notification-channels/:channelId\", rateLimit(channel));\n app.use(\"/api/v1/projects/:slug/notifications\", rateLimit(subscription));\n app.use(\"/api/v1/projects/:slug/notifications/me\", rateLimit(subscription));\n app.use(\"/api/v1/admin/notification-channels\", rateLimit(admin));\n app.use(\"/api/v1/admin/notification-channels/:channelId\", rateLimit(admin));\n}\n\n/** Mount the request-scoped store (db/storage/config/user/adapters). */\nfunction mountStoreScope(app: ShelfRouter, wiring: MiddlewareWiring): void {\n const { options, config, ui, logger, authEnabled, enqueueCapture, queue, gitHosts, notifiers } =\n wiring;\n app.use(\n \"*\",\n storeScope({\n db: options.database,\n storage: options.storage,\n config,\n ui,\n logger,\n authEnabled,\n enqueueCapture,\n captureQueue: queue,\n gitHosts,\n notifiers,\n resolveUser: async (c) => await resolveRequestUser(c, options.auth),\n }),\n );\n}\n\n/** Register every JSON API router. */\nfunction registerApiRoutes(app: ShelfRouter, health: HealthDeps): void {\n registerProjectRoutes(app);\n registerNotificationRoutes(app);\n registerStatusConfigs(app);\n registerHealth(app, health);\n registerAdmin(app);\n}\n\n/** Register the per-project resource routers. */\nfunction registerProjectRoutes(app: ShelfRouter): void {\n registerProjects(app);\n registerBuilds(app);\n registerLabels(app);\n registerMedia(app);\n registerMembers(app);\n registerTokens(app);\n registerWebhooks(app);\n}\n\n/** Register the notification channel and subscription routers. */\nfunction registerNotificationRoutes(app: ShelfRouter): void {\n registerNotificationChannels(app);\n registerNotificationSubscriptions(app);\n registerSystemChannels(app);\n}\n\n/** Register the auth flow: engine mount, relying-party documents, profile. */\nfunction registerAuthFlow(app: ShelfRouter, options: ShelfOptions): void {\n // Auth-gated pages mount only with auth: authEnabled=false mounts zero\n // auth routes. Static imports stay — true lazy loading would need an async\n // createShelfApp, which the sync Hono assembly cannot do.\n if (!options.auth) {\n return;\n }\n registerEngineAuth(app, options.auth);\n registerWellKnown(app, options.auth);\n registerProfile(app, options.auth);\n}\n\n/** Register HTML pages, assets, and the optional auth flow. */\nfunction registerPageRoutes(app: ShelfRouter, options: ShelfOptions, health: HealthDeps): void {\n registerAuthFlow(app, options);\n registerAssets(app);\n registerStorybook(app);\n registerAdminPages(app, health, options.auth);\n registerUiPages(app);\n}\n\n/** Register every API router, page set, and optional auth flow. */\nfunction registerAllRoutes(app: ShelfRouter, options: ShelfOptions, health: HealthDeps): void {\n registerApiRoutes(app, health);\n registerPageRoutes(app, options, health);\n}\n\n/** Register the OpenAPI document and interactive docs page. */\nfunction registerDocs(app: ShelfRouter): void {\n app.doc(\"/api/v1/openapi.json\", {\n openapi: \"3.0.0\",\n info: {\n title: \"StoryShelf API\",\n description:\n \"REST API for the StoryShelf visual testing platform. JSON endpoints live under /api/v1; HTML pages are served at /.\",\n version: \"1.0.0\",\n },\n tags: [\n {\n name: \"Projects\",\n description: \"Create and manage projects (one project is one Storybook).\",\n },\n {\n name: \"Builds\",\n description: \"Upload Storybook builds, inspect capture attempts, retry, and delete.\",\n },\n {\n name: \"Review\",\n description: \"Approve or reject snapshots, bulk actions, re-diff, and review comments.\",\n },\n { name: \"Labels\", description: \"Typed build labels and their link templates.\" },\n {\n name: \"Members\",\n description: \"Project members, roles, and identity-provider group mappings.\",\n },\n { name: \"Tokens\", description: \"API tokens used by the CLI and CI.\" },\n { name: \"Webhooks\", description: \"Signed webhook deliveries for build events.\" },\n {\n name: \"Notifications\",\n description: \"Email and chat notification channels and subscriptions.\",\n },\n { name: \"Git status\", description: \"Commit-status integrations for GitHub and GitLab.\" },\n { name: \"Admin\", description: \"Site-wide administration.\" },\n ],\n });\n app.get(\"/api/v1/docs\", swaggerUI({ url: \"/api/v1/openapi.json\" }));\n}\n\n/** Package version injected at build via __PKG_VERSION__. */\nfunction packageVersion(): string {\n return (globalThis as unknown as { __PKG_VERSION__?: string }).__PKG_VERSION__ ?? \"0.0.0\";\n}\n\n/**\n * Public surface: the app and its types only.\n *\n * `createShelfApp` plus the option/config types needed to call it. Adapter\n * interfaces live under `core/adapter/*`; runtime helpers under `core/logger`,\n * `core/capture`, `core/paths`, `core/urls`, `core/diff`; tables and row types\n * under `core/schema`. Importing the barrel must never pull Node-only modules\n * into edge bundles beyond what Hono itself needs.\n */\nexport { createAuthSystemHook } from \"./notify.ts\";\nexport type {\n ShelfOptions,\n ShelfConfig,\n UIConfig,\n BrandTheme,\n NotificationsConfig,\n} from \"@storyshelf/core/config\";\nexport {\n createShelfLogger,\n type Logger,\n type LoggerOptions,\n type PinoTransport,\n} from \"@storyshelf/core/logger\";\nexport type { ShelfApp, ShelfContext, ShelfLifecycle, ShelfRouter } from \"./app-types.ts\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.tsx"],"sourcesContent":["import { swaggerUI } from \"@hono/swagger-ui\";\nimport { OpenAPIHono } from \"@hono/zod-openapi\";\nimport {\n AdapterLifecycleError,\n adapterSourcesOf,\n validateBootAssembly,\n} from \"@storyshelf/core/adapter/setup\";\nimport { assertServerless, resolveShelfOptions } from \"@storyshelf/core/config\";\nimport type { ShelfConfig, ShelfOptions } from \"@storyshelf/core/config\";\nimport { configureImages } from \"@storyshelf/core/diff\";\nimport type { Logger } from \"@storyshelf/core/logger\";\nimport { STORYBOOK_TOKEN_PREFIX } from \"@storyshelf/core/utils\";\n/**\n * StoryShelf app: compose database, storage, capture, auth, and notifier\n * adapters into a complete self-hosted visual-testing Hono app.\n */\nimport {\n createHttpMiddleware,\n createInstrumentedDatabase,\n createInstrumentedStorage,\n} from \"@storyshelf/observability\";\nimport { requestId } from \"hono/request-id\";\nimport type { ShelfApp, ShelfContext, ShelfRouter } from \"./app-types.ts\";\nimport { runBootTasks } from \"./boot-tasks.ts\";\nimport { setupJobs, type JobsWiring } from \"./jobs-setup.ts\";\nimport { attachLifecycle, type LifecycleCell } from \"./lifecycle.ts\";\nimport {\n authGate,\n csrf,\n createRateLimiter,\n requireReview,\n requireTesting,\n rateLimitKey,\n requestLogging,\n resolveRequestUser,\n storeScope,\n} from \"./middleware/index.ts\";\nimport { serverTiming } from \"./middleware/server-timing.ts\";\nimport { setupGate, type MiddlewareWiring } from \"./middleware/setup-gate.ts\";\nimport { registerAdminPages } from \"./routers/admin-pages.ts\";\nimport { registerPublicOriginRoutes } from \"./routers/admin-public-origin.ts\";\nimport { registerAdmin } from \"./routers/admin.ts\";\nimport { registerAssets } from \"./routers/assets.ts\";\nimport { registerEngineAuth } from \"./routers/auth.ts\";\nimport { registerBaselines } from \"./routers/baselines.ts\";\nimport { registerBuilds } from \"./routers/builds.ts\";\nimport { registerConnections } from \"./routers/connections.ts\";\nimport { registerHealth, type HealthDeps } from \"./routers/health.ts\";\nimport { registerInsightFragments } from \"./routers/insights-ui.tsx\";\nimport { registerInsights } from \"./routers/insights.ts\";\nimport { registerLabels } from \"./routers/labels.ts\";\nimport { registerLinks } from \"./routers/links.ts\";\nimport { registerMedia } from \"./routers/media.ts\";\nimport { registerMembers } from \"./routers/members.ts\";\nimport {\n registerNotificationChannels,\n registerSystemChannels,\n} from \"./routers/notification-channels.ts\";\nimport { registerNotificationSubscriptions } from \"./routers/notification-subscriptions.ts\";\nimport { registerPreviewToken } from \"./routers/preview-token.ts\";\nimport { registerProfile } from \"./routers/profile.ts\";\nimport { registerProjects } from \"./routers/projects.ts\";\nimport { registerReviewForms } from \"./routers/review-forms.ts\";\nimport { registerSignedStorybook } from \"./routers/signed-storybook.ts\";\nimport { registerStorybook } from \"./routers/storybook.ts\";\nimport { registerTokens } from \"./routers/tokens.ts\";\nimport { registerUiPages } from \"./routers/ui.ts\";\nimport { registerWebhooks } from \"./routers/webhooks.ts\";\nimport { registerWellKnown } from \"./routers/well-known.ts\";\nimport { markRouteClasses } from \"./routes/openapi-classes.ts\";\nimport { resolveRuntime, type ServerRuntime } from \"./runtime.ts\";\n\n/**\n * Fail fast on third-party adapters that cannot satisfy their slot.\n * Throws before any route or lifecycle hook runs.\n */\nfunction throwOnInvalidAdapters(options: ShelfOptions): void {\n const invalid = validateBootAssembly(adapterSourcesOf(options));\n if (invalid.length > 0) {\n throw new AdapterLifecycleError(\"setup\", invalid);\n }\n}\n\n/** Request/capture-path adapters with OTEL wrappers (lifecycle keeps raw ones). */\nfunction instrumentedOptions(options: ShelfOptions): ShelfOptions {\n return {\n ...options,\n database: createInstrumentedDatabase(options.database),\n storage: createInstrumentedStorage(options.storage),\n };\n}\n\n/** Wire the job queue, its handlers and its consumer, and run the boot tasks that need them. */\nfunction wireQueues(scoped: ShelfOptions, runtime: ServerRuntime, cell: LifecycleCell): JobsWiring {\n const jobs = setupJobs(scoped, runtime);\n cell.consumer = jobs.consumer;\n runBootTasks(cell, scoped, runtime, jobs.buildState);\n return jobs;\n}\n\n/**\n * Serverless check (ADR 0006, decision 3): throws on AWS Lambda, warns on Vercel and Netlify. Only\n * an instance with `testing` needs a process that outlives the request.\n */\nfunction warnOnServerless(options: ShelfOptions, logger: Logger): void {\n const warning = assertServerless(options.testing?.runner !== undefined);\n if (warning) {\n logger.warn(warning);\n }\n}\n\n/** Attach lifecycle, queue, middleware, routes, and docs to the app. */\nfunction assembleApp(app: ShelfApp, options: ShelfOptions, scoped: ShelfOptions): void {\n const runtime = resolveRuntime(options);\n warnOnServerless(options, runtime.logger);\n const cell: LifecycleCell = { ready: Promise.resolve({ ok: true, failures: [] }), settled: null };\n attachLifecycle(app, options, runtime, cell);\n if (runtime.config.serverTiming === true) {\n app.use(\"*\", serverTiming(true));\n }\n const queues = wireQueues(scoped, runtime, cell);\n wireMiddleware(app, {\n ...runtime,\n ...queues,\n options: scoped,\n getReady: async () => await cell.ready,\n });\n registerAllRoutes(app, options, {\n sources: adapterSourcesOf(options),\n getSettled: () => cell.settled,\n bootTimeMs: Date.now(),\n version: packageVersion(),\n });\n registerDocs(app);\n}\n\n/**\n * Create the StoryShelf Hono app with all API routes and HTML pages.\n *\n * Adapter `init` hooks kick off eagerly in the background (the constructor\n * stays synchronous). Await `app.lifecycle.setup()` before serving for\n * fail-fast startup, or let the first request gate on settlement.\n */\nexport function createShelfApp(rawOptions: ShelfOptions): ShelfApp {\n const options = resolveShelfOptions(rawOptions);\n configureImages({\n engine: options.testing?.images?.engine,\n threads: options.testing?.compare?.threads,\n });\n throwOnInvalidAdapters(options);\n const app = new OpenAPIHono<{ Variables: ShelfContext }>() as ShelfApp;\n assembleApp(app, options, instrumentedOptions(options));\n\n return app;\n}\n\n/** Cookie-authenticated write paths guarded by CSRF tokens. */\nconst CSRF_PATHS = [\n \"/auth/logout\",\n \"/auth/invites/*\",\n \"/projects/:slug/settings/*\",\n \"/profile/*\",\n \"/connections/*\",\n \"/profile\",\n \"/admin/credentials/*\",\n \"/admin/public-origin\",\n \"/admin/public-origin/*\",\n \"/projects/:slug/builds/:buildId/insights/generate\",\n \"/projects/:slug/builds/:buildId/comments\",\n \"/projects/:slug/builds/:buildId/comments/*\",\n \"/projects/:slug/builds/:buildId/reviews\",\n \"/projects/:slug/insights/health/generate\",\n] as const;\n\n/** Attach global middleware: ids, logging, init gate, limits, store scope, auth gate. */\nfunction wireMiddleware(app: ShelfRouter, wiring: MiddlewareWiring): void {\n const { config, logger, getReady } = wiring;\n app.use(\"*\", requestId());\n // OTEL server spans (W3C extraction, route-template naming). Mounted right\n // after requestId so `storyshelf.req_id` resolves; `enduser.id` resolves\n // once storeScope runs downstream. Noop without an SDK.\n app.use(\n \"*\",\n createHttpMiddleware({ serviceName: \"storyshelf\", serviceVersion: packageVersion() }),\n );\n // Structured request logging. Uses a Hono-native middleware rather than\n // Pino-http, which expects a Node server response (`res.on`) incompatible\n // With Hono's Web `Request`/`Response` model.\n app.use(\"*\", requestLogging(logger));\n app.use(\"*\", setupGate(getReady));\n // Routes classified `testing` answer 404 without `options.testing` (ADR 0006, decision 8).\n app.use(\"*\", requireTesting(wiring.options.testing !== undefined));\n mountGuards(app, config, wiring.options);\n mountStoreScope(app, wiring);\n app.use(\"*\", authGate());\n // Routes classified `review` answer 404 for a project with review off (ADR 0013, decision 10).\n app.use(\"*\", requireReview());\n}\n\nconst MINUTE = 60_000;\n\n/** Per-path limits; `bucket` gives a route family its own counter (default: shared `api:`). */\nconst RATE_LIMITS: readonly { paths: string[]; max: number; bucket?: string }[] = [\n { paths: [\"/api/v1/*\"], max: 100 },\n { paths: [\"/api/v1/tokens/*\"], max: 10 },\n { paths: [\"/api/v1/webhooks/*\"], max: 20 },\n // Auth endpoints ride their own buckets (the store keys by IP, so a prefix\n // keeps login floods from eating the API budget or vice versa).\n { paths: [\"/auth/*\"], max: 100, bucket: \"auth\" },\n { paths: [\"/api/auth/*\"], max: 300, bucket: \"engine\" },\n {\n paths: [\n \"/api/v1/projects/:slug/notification-channels\",\n \"/api/v1/projects/:slug/notification-channels/:channelId\",\n ],\n max: 20,\n bucket: \"notify-channels\",\n },\n {\n paths: [\"/api/v1/projects/:slug/notifications\", \"/api/v1/projects/:slug/notifications/me\"],\n max: 30,\n bucket: \"notify-subs\",\n },\n {\n paths: [\n \"/api/v1/admin/notification-channels\",\n \"/api/v1/admin/notification-channels/:channelId\",\n ],\n max: 20,\n bucket: \"notify-admin\",\n },\n];\n\n/** Requests per minute per client IP on the signed Storybook URL route (`testing.storybookUrlRateLimit`). */\nconst DEFAULT_STORYBOOK_URL_RATE_LIMIT = 6000;\n\n/** Mount rate limits and CSRF guards (abuse floors, not strict protection). */\nfunction mountGuards(app: ShelfRouter, config: ShelfConfig, options: ShelfOptions): void {\n const rateLimit = createRateLimiter();\n // A render fetches thousands of assets from one farm IP, so this floor is generous; it counts\n // before the token is looked at and answers 429 alike for any token.\n const signedUrls = {\n paths: [`${STORYBOOK_TOKEN_PREFIX}*`],\n max: options.testing?.storybookUrlRateLimit ?? DEFAULT_STORYBOOK_URL_RATE_LIMIT,\n bucket: \"signed-storybook\",\n };\n for (const { paths, max, bucket } of [...RATE_LIMITS, signedUrls]) {\n const keyGenerator = bucket ? rateLimitKey(bucket) : undefined;\n for (const path of paths) {\n app.use(path, rateLimit({ windowMs: MINUTE, max, keyGenerator }));\n }\n }\n for (const path of CSRF_PATHS) {\n app.use(path, csrf(config.secret));\n }\n}\n\n/** Mount the request-scoped store (db/storage/config/user/adapters). */\nfunction mountStoreScope(app: ShelfRouter, wiring: MiddlewareWiring): void {\n const {\n options,\n config,\n ui,\n logger,\n authEnabled,\n enqueueCapture,\n captureQueue,\n capturesHere,\n publish,\n cancelBuildJobs,\n buildState,\n design,\n notifiers,\n publicOrigin,\n } = wiring;\n app.use(\n \"*\",\n storeScope({\n db: options.database,\n storage: options.storage,\n config,\n ui,\n logger,\n authEnabled,\n testing: options.testing !== undefined,\n enqueueCapture,\n captureQueue,\n capturesHere,\n publish,\n cancelBuildJobs,\n buildState,\n captureConcurrency: options.testing?.concurrency,\n runner: options.testing?.runner,\n publicOrigin,\n design,\n notifiers,\n notificationsConfig: options.notifications?.config,\n ai: options.ai,\n resolveUser: async (c) => await resolveRequestUser(c, options.auth),\n }),\n );\n}\n\n/** Register every JSON API router. */\nfunction registerApiRoutes(app: ShelfRouter, health: HealthDeps): void {\n registerProjectRoutes(app);\n registerNotificationRoutes(app);\n registerHealth(app, health);\n registerAdmin(app);\n}\n\n/** Register the per-project resource routers. */\nfunction registerProjectRoutes(app: ShelfRouter): void {\n registerProjects(app);\n registerBuilds(app);\n registerLabels(app);\n registerLinks(app);\n registerBaselines(app);\n registerMedia(app);\n registerMembers(app);\n registerTokens(app);\n registerWebhooks(app);\n registerInsights(app);\n}\n\n/** Register the notification channel and subscription routers. */\nfunction registerNotificationRoutes(app: ShelfRouter): void {\n registerNotificationChannels(app);\n registerNotificationSubscriptions(app);\n registerSystemChannels(app);\n}\n\n/** Register the auth flow: engine mount, relying-party documents, profile. */\nfunction registerAuthFlow(app: ShelfRouter, options: ShelfOptions): void {\n // Auth-gated pages mount only with auth: authEnabled=false mounts zero\n // auth routes. Static imports stay — true lazy loading would need an async\n // createShelfApp, which the sync Hono assembly cannot do.\n if (!options.auth) {\n return;\n }\n registerEngineAuth(app, options.auth);\n registerWellKnown(app, options.auth);\n registerProfile(app, options.auth);\n registerConnections(app);\n}\n\n/** Register HTML pages, assets, and the optional auth flow. */\nfunction registerPageRoutes(app: ShelfRouter, options: ShelfOptions, health: HealthDeps): void {\n registerAuthFlow(app, options);\n registerAssets(app);\n registerStorybook(app);\n registerSignedStorybook(app);\n registerPreviewToken(app);\n registerAdminPages(app, health, options.auth);\n registerPublicOriginRoutes(app);\n registerUiPages(app);\n registerReviewForms(app);\n registerInsightFragments(app);\n}\n\n/** Register every API router, page set, and optional auth flow. */\nfunction registerAllRoutes(app: ShelfRouter, options: ShelfOptions, health: HealthDeps): void {\n registerApiRoutes(app, health);\n registerPageRoutes(app, options, health);\n}\n\n/** The OpenAPI document's info and tag catalog. */\nconst OPENAPI_CONFIG = {\n openapi: \"3.0.0\",\n info: {\n title: \"StoryShelf API\",\n description:\n \"REST API for the StoryShelf visual testing platform. JSON endpoints live under /api/v1; HTML pages are served at /.\",\n version: \"1.0.0\",\n },\n tags: [\n {\n name: \"Projects\",\n description: \"Create and manage projects (one project is one Storybook).\",\n },\n {\n name: \"Builds\",\n description: \"Upload Storybook builds, inspect capture attempts, retry, and delete.\",\n },\n {\n name: \"Review\",\n description:\n \"Approve or reject snapshots, bulk actions, re-diff, build verdicts, the build changeset and review comments.\",\n },\n { name: \"Labels\", description: \"Typed build labels and their link templates.\" },\n {\n name: \"Links\",\n description:\n \"Link packages to the projects that publish them and see where stories use them.\",\n },\n { name: \"Baselines\", description: \"Export and import baselines as a portable zip.\" },\n {\n name: \"Members\",\n description: \"Project members, roles, and identity-provider group mappings.\",\n },\n {\n name: \"Insights\",\n description: \"AI build triage and project health (advisory only; needs AI configured).\",\n },\n { name: \"Tokens\", description: \"API tokens used by the CLI and CI.\" },\n { name: \"Webhooks\", description: \"Signed webhook deliveries for build events.\" },\n {\n name: \"Notifications\",\n description: \"Email, chat and git notification channels and subscriptions.\",\n },\n { name: \"Admin\", description: \"Site-wide administration.\" },\n ],\n} as const;\n\n/** Register the OpenAPI document and interactive docs page. */\nfunction registerDocs(app: ShelfRouter): void {\n // Every operation carries the class of its route (`x-storyshelf-class`), so a client can tell\n // what an instance hides: `testing` routes 404 without `testing`, `review` ones with review off.\n app.get(\"/api/v1/openapi.json\", (c) =>\n c.json(\n markRouteClasses(\n app.getOpenAPIDocument({ ...OPENAPI_CONFIG, tags: [...OPENAPI_CONFIG.tags] }),\n ),\n ),\n );\n app.get(\"/api/v1/docs\", swaggerUI({ url: \"/api/v1/openapi.json\" }));\n}\n\ndeclare const __PKG_VERSION__: string | undefined;\n\n/**\n * Package version injected at build via the `__PKG_VERSION__` define. Read as a bare identifier:\n * bundler defines replace identifiers, not `globalThis` property reads. Undefined when running\n * from source (dev server, tests).\n */\nfunction packageVersion(): string {\n return typeof __PKG_VERSION__ === \"string\" ? __PKG_VERSION__ : \"0.0.0\";\n}\n\n/**\n * Public surface: the app and its types only.\n *\n * `createShelfApp` plus the option/config types needed to call it. Adapter\n * interfaces live under `core/adapter/*`; runtime helpers under `core/logger`,\n * `core/capture`, `core/paths`, `core/urls`, `core/diff`; tables and row types\n * under `core/schema`. Importing the barrel must never pull Node-only modules\n * into edge bundles beyond what Hono itself needs.\n */\nexport { createAuthSystemHook } from \"./notify.ts\";\nexport type {\n ShelfOptions,\n ShelfTestingOptions,\n ShelfCompareOptions,\n ShelfNotificationsOptions,\n ShelfConfig,\n UIConfig,\n BrandTheme,\n NotificationsConfig,\n} from \"@storyshelf/core/config\";\nexport {\n createShelfLogger,\n type Logger,\n type LoggerOptions,\n type PinoTransport,\n} from \"@storyshelf/core/logger\";\nexport type { ShelfApp, ShelfContext, ShelfLifecycle, ShelfRouter } from \"./app-types.ts\";\n"],"mappings":"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"}
|