@storyshelf/app 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app-types.d.mts +1 -3
- package/dist/asset-manifest.mjs +1 -2
- package/dist/assets/htmx.mjs +1 -2
- package/dist/assets/mark.mjs +1 -2
- package/dist/boot-tasks.mjs +1 -2
- package/dist/build-state-flags.mjs +1 -2
- package/dist/build-state-setup.mjs +1 -2
- package/dist/capture-alerts.mjs +1 -2
- package/dist/capture-process.mjs +1 -2
- package/dist/capture-setup.mjs +1 -2
- package/dist/connections.mjs +1 -2
- package/dist/consumer-setup.mjs +1 -2
- package/dist/credential-keys.mjs +1 -2
- package/dist/credentials.mjs +1 -2
- package/dist/git-status.mjs +1 -2
- package/dist/hidden-data.mjs +1 -2
- package/dist/index.d.mts +0 -2
- package/dist/index.mjs +2 -3
- package/dist/insights/budget.mjs +1 -2
- package/dist/insights/deps.mjs +1 -2
- package/dist/insights/errors.mjs +1 -2
- package/dist/insights/health-panel.mjs +0 -1
- package/dist/insights/job.mjs +1 -2
- package/dist/insights/load-health.mjs +1 -2
- package/dist/insights/load-triage.mjs +1 -2
- package/dist/insights/panel.mjs +1 -2
- package/dist/insights/request.mjs +1 -2
- package/dist/insights/stale.mjs +1 -2
- package/dist/insights/usage-report.mjs +1 -2
- package/dist/insights/view.mjs +1 -2
- package/dist/job-canceller.mjs +1 -2
- package/dist/job-queue.mjs +1 -2
- package/dist/jobs-setup.mjs +1 -2
- package/dist/lifecycle.mjs +2 -3
- package/dist/lifecycle.mjs.map +1 -1
- package/dist/middleware/auth-gate.mjs +1 -2
- package/dist/middleware/csrf.mjs +1 -2
- package/dist/middleware/rate-limit.mjs +1 -2
- package/dist/middleware/request-log.mjs +1 -2
- package/dist/middleware/require-review.mjs +1 -2
- package/dist/middleware/require-testing.mjs +1 -2
- package/dist/middleware/server-timing.mjs +1 -2
- package/dist/middleware/setup-gate.mjs +1 -2
- package/dist/middleware/store-scope.mjs +1 -2
- package/dist/notifications-pause.mjs +1 -2
- package/dist/notify.d.mts +1 -3
- package/dist/notify.mjs +1 -2
- package/dist/pages/a11y-violations.mjs +1 -2
- package/dist/pages/admin-ai.mjs +1 -2
- package/dist/pages/admin-system-origin.mjs +19 -10
- package/dist/pages/admin-system-origin.mjs.map +1 -1
- package/dist/pages/admin-system-targets.mjs +1 -2
- package/dist/pages/admin-system.mjs +1 -2
- package/dist/pages/build-detail.mjs +1 -2
- package/dist/pages/build-diff-actions.mjs +1 -2
- package/dist/pages/build-diff-comments.mjs +1 -2
- package/dist/pages/build-diff-default.mjs +53 -0
- package/dist/pages/build-diff-default.mjs.map +1 -0
- package/dist/pages/build-diff-environment.mjs +1 -2
- package/dist/pages/build-diff-header.mjs +1 -2
- package/dist/pages/build-diff-insight.mjs +1 -2
- package/dist/pages/build-diff-model.mjs +20 -3
- package/dist/pages/build-diff-model.mjs.map +1 -1
- package/dist/pages/build-diff-nav.mjs +1 -2
- package/dist/pages/build-diff-reference.mjs +84 -0
- package/dist/pages/build-diff-reference.mjs.map +1 -0
- package/dist/pages/build-diff-shortcuts.mjs +1 -2
- package/dist/pages/build-diff-stale.mjs +1 -2
- package/dist/pages/build-diff-usage.mjs +1 -2
- package/dist/pages/build-diff-viewer.mjs +38 -12
- package/dist/pages/build-diff-viewer.mjs.map +1 -1
- package/dist/pages/build-diff.mjs +6 -12
- package/dist/pages/build-diff.mjs.map +1 -1
- package/dist/pages/build-tests.mjs +1 -2
- package/dist/pages/component-detail.mjs +1 -2
- package/dist/pages/component-stories.mjs +1 -2
- package/dist/pages/compute-jobs.mjs +1 -2
- package/dist/pages/design-links.mjs +1 -2
- package/dist/pages/environment-change.mjs +1 -2
- package/dist/pages/health-panel.mjs +1 -2
- package/dist/pages/invite.mjs +1 -2
- package/dist/pages/label-detail.mjs +1 -2
- package/dist/pages/library-grid.mjs +1 -2
- package/dist/pages/library-model.mjs +1 -2
- package/dist/pages/library-snapshots.mjs +1 -2
- package/dist/pages/library.mjs +1 -2
- package/dist/pages/login.mjs +1 -2
- package/dist/pages/notifications-paused.mjs +1 -2
- package/dist/pages/passkey-ceremony.mjs +1 -2
- package/dist/pages/profile.mjs +1 -2
- package/dist/pages/project-builds-table.mjs +1 -2
- package/dist/pages/project-builds.mjs +1 -2
- package/dist/pages/project-create.mjs +1 -2
- package/dist/pages/project-settings.mjs +1 -2
- package/dist/pages/projects-card.mjs +1 -2
- package/dist/pages/projects.mjs +1 -2
- package/dist/pages/review-changeset.mjs +1 -2
- package/dist/pages/review-comments.mjs +1 -2
- package/dist/pages/review-verdict.mjs +1 -2
- package/dist/pages/root.mjs +1 -2
- package/dist/pages/settings-ai.mjs +1 -2
- package/dist/pages/settings-capture.mjs +1 -2
- package/dist/pages/settings-design-credential.mjs +1 -2
- package/dist/pages/settings-design-links.mjs +1 -2
- package/dist/pages/settings-design.mjs +1 -2
- package/dist/pages/settings-general.mjs +1 -2
- package/dist/pages/settings-labels.mjs +1 -2
- package/dist/pages/settings-links.mjs +1 -2
- package/dist/pages/settings-members.mjs +1 -2
- package/dist/pages/settings-notifications-git.mjs +1 -2
- package/dist/pages/settings-notifications.mjs +1 -2
- package/dist/pages/settings-tests.mjs +1 -2
- package/dist/pages/settings-tokens.mjs +1 -2
- package/dist/pages/settings-webhooks.mjs +1 -2
- package/dist/pages/storybook.mjs +1 -2
- package/dist/pages/target-choices.mjs +1 -2
- package/dist/publish-setup.mjs +1 -2
- package/dist/retention-timer.mjs +10 -11
- package/dist/retention-timer.mjs.map +1 -1
- package/dist/review-events.mjs +1 -2
- package/dist/routers/admin-pages.mjs +1 -2
- package/dist/routers/admin-public-origin.mjs +10 -3
- package/dist/routers/admin-public-origin.mjs.map +1 -1
- package/dist/routers/admin.mjs +8 -4
- package/dist/routers/admin.mjs.map +1 -1
- package/dist/routers/assets.mjs +1 -2
- package/dist/routers/attempts.mjs +1 -2
- package/dist/routers/auth-sync.mjs +1 -2
- package/dist/routers/auth.mjs +1 -2
- package/dist/routers/baseline-guard.mjs +2 -3
- package/dist/routers/baseline-guard.mjs.map +1 -1
- package/dist/routers/baseline-reference.mjs +72 -0
- package/dist/routers/baseline-reference.mjs.map +1 -0
- package/dist/routers/baselines.handlers.mjs +1 -2
- package/dist/routers/baselines.mjs +1 -2
- package/dist/routers/build-cancel.mjs +1 -2
- package/dist/routers/build-changeset.mjs +1 -2
- package/dist/routers/build-reviews.handlers.mjs +1 -2
- package/dist/routers/build-reviews.mjs +1 -2
- package/dist/routers/builds.handlers.mjs +23 -9
- package/dist/routers/builds.handlers.mjs.map +1 -1
- package/dist/routers/builds.mjs +7 -3
- package/dist/routers/builds.mjs.map +1 -1
- package/dist/routers/capture-cache.mjs +29 -0
- package/dist/routers/capture-cache.mjs.map +1 -0
- package/dist/routers/capture-config.mjs +1 -2
- package/dist/routers/comments.mjs +1 -2
- package/dist/routers/comparison-image.mjs +29 -0
- package/dist/routers/comparison-image.mjs.map +1 -0
- package/dist/routers/component-catalog.handlers.mjs +1 -2
- package/dist/routers/component.handlers.mjs +1 -2
- package/dist/routers/connections.mjs +1 -2
- package/dist/routers/design-connect.handlers.mjs +1 -2
- package/dist/routers/design.handlers.mjs +1 -2
- package/dist/routers/flash.mjs +1 -2
- package/dist/routers/health-report.mjs +1 -2
- package/dist/routers/health.mjs +1 -2
- package/dist/routers/helpers.mjs +1 -2
- package/dist/routers/htmx.mjs +1 -2
- package/dist/routers/insights-ui.mjs +1 -2
- package/dist/routers/insights.handlers.mjs +1 -2
- package/dist/routers/insights.mjs +1 -2
- package/dist/routers/labels.mjs +1 -2
- package/dist/routers/links.handlers.mjs +1 -2
- package/dist/routers/links.mjs +1 -2
- package/dist/routers/media.mjs +14 -5
- package/dist/routers/media.mjs.map +1 -1
- package/dist/routers/members.mjs +1 -2
- package/dist/routers/notification-channels.mjs +1 -2
- package/dist/routers/notification-subscriptions.mjs +1 -2
- package/dist/routers/oauth-state.mjs +1 -2
- package/dist/routers/preview-base.mjs +1 -2
- package/dist/routers/preview-token.mjs +1 -2
- package/dist/routers/profile.mjs +1 -2
- package/dist/routers/projects.mjs +1 -2
- package/dist/routers/publish-upload.mjs +19 -12
- package/dist/routers/publish-upload.mjs.map +1 -1
- package/dist/routers/published-access.mjs +1 -2
- package/dist/routers/published-compression.mjs +109 -0
- package/dist/routers/published-compression.mjs.map +1 -0
- package/dist/routers/published-files.mjs +26 -9
- package/dist/routers/published-files.mjs.map +1 -1
- package/dist/routers/review-forms.mjs +1 -2
- package/dist/routers/review-next.mjs +1 -2
- package/dist/routers/review-section.handlers.mjs +1 -2
- package/dist/routers/review-switch-off.mjs +1 -2
- package/dist/routers/schemas.mjs +1 -2
- package/dist/routers/scoped-models.mjs +1 -2
- package/dist/routers/settings-ai.mjs +1 -2
- package/dist/routers/settings-capture.mjs +1 -2
- package/dist/routers/settings-design.mjs +1 -2
- package/dist/routers/settings-general.mjs +1 -2
- package/dist/routers/settings-git-channel.mjs +1 -2
- package/dist/routers/settings-labels.mjs +1 -2
- package/dist/routers/settings-links.mjs +1 -2
- package/dist/routers/settings-members.mjs +1 -2
- package/dist/routers/settings-notifications.mjs +1 -2
- package/dist/routers/settings-tokens.mjs +1 -2
- package/dist/routers/settings-webhooks.mjs +1 -2
- package/dist/routers/settings.handlers.mjs +1 -2
- package/dist/routers/settings.mjs +1 -2
- package/dist/routers/signed-storybook.mjs +5 -3
- package/dist/routers/signed-storybook.mjs.map +1 -1
- package/dist/routers/snapshot-groups.mjs +1 -2
- package/dist/routers/snapshots.mjs +1 -2
- package/dist/routers/storybook.mjs +21 -17
- package/dist/routers/storybook.mjs.map +1 -1
- package/dist/routers/tokens.mjs +1 -2
- package/dist/routers/ui.mjs +4 -2
- package/dist/routers/ui.mjs.map +1 -1
- package/dist/routers/webhooks.mjs +1 -2
- package/dist/routers/well-known.mjs +1 -2
- package/dist/routes/api-routes.mjs +1 -2
- package/dist/routes/classification.mjs +1 -2
- package/dist/routes/openapi-classes.mjs +1 -2
- package/dist/routes/page-routes.mjs +2 -3
- package/dist/routes/page-routes.mjs.map +1 -1
- package/dist/runtime.mjs +1 -2
- package/dist/serverless.mjs +1 -2
- package/dist/store.mjs +1 -2
- package/dist/targets.mjs +1 -2
- package/dist/ui/avatar.mjs +1 -2
- package/dist/ui/buttons.mjs +1 -2
- package/dist/ui/code-block.mjs +1 -2
- package/dist/ui/compare-stage.mjs +86 -54
- package/dist/ui/compare-stage.mjs.map +1 -1
- package/dist/ui/csrf-field.mjs +1 -2
- package/dist/ui/css.mjs +1 -2
- package/dist/ui/design-reference.mjs +1 -2
- package/dist/ui/document.mjs +1 -2
- package/dist/ui/dropdown.mjs +1 -2
- package/dist/ui/feedback.mjs +1 -2
- package/dist/ui/filter-input.mjs +1 -2
- package/dist/ui/forms.mjs +1 -2
- package/dist/ui/icons/icon.mjs +1 -2
- package/dist/ui/icons/paths.mjs +1 -2
- package/dist/ui/icons/sprite.mjs +1 -2
- package/dist/ui/kbd.mjs +1 -2
- package/dist/ui/layout.mjs +1 -2
- package/dist/ui/preview-embed.mjs +1 -2
- package/dist/ui/progress.mjs +1 -2
- package/dist/ui/segmented.mjs +1 -2
- package/dist/ui/shell/account.mjs +1 -2
- package/dist/ui/shell/bare.mjs +1 -2
- package/dist/ui/shell/boot-script.mjs +1 -2
- package/dist/ui/shell/client-script.mjs +1 -2
- package/dist/ui/shell/nav.mjs +1 -2
- package/dist/ui/shell/review-script.mjs +17 -3
- package/dist/ui/shell/review-script.mjs.map +1 -1
- package/dist/ui/shell/sidebar.mjs +1 -2
- package/dist/ui/shell/topbar.mjs +1 -2
- package/dist/ui/stacks.mjs +1 -2
- package/dist/ui/styles/base.mjs +1 -2
- package/dist/ui/styles/behavior.mjs +1 -2
- package/dist/ui/styles/review-layout.mjs +1 -2
- package/dist/ui/styles/review.mjs +1 -2
- package/dist/ui/styles/tokens.mjs +1 -2
- package/dist/ui/styles.mjs +1 -2
- package/dist/ui/subnav.mjs +1 -2
- package/dist/ui/table.mjs +1 -2
- package/dist/ui/theme.mjs +1 -2
- package/dist/ui/thumbnail.mjs +1 -2
- package/dist/ui/time.mjs +1 -2
- package/package.json +9 -9
package/dist/lifecycle.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.mjs","names":[],"sources":["../src/lifecycle.ts"],"sourcesContent":["import type { AdapterSetupContext } from \"@storyshelf/core/adapter/metadata\";\nimport {\n AdapterLifecycleError,\n bindAdapterLoggers,\n adapterSourcesOf,\n collectSetups,\n collectTeardowns,\n runAdapterSetups,\n runAdapterTeardowns,\n validateBootAssembly,\n} from \"@storyshelf/core/adapter/setup\";\nimport type { AdapterSetupResult } from \"@storyshelf/core/adapter/setup\";\nimport type { CaptureConsumer } from \"@storyshelf/core/capture\";\nimport type { ShelfOptions } from \"@storyshelf/core/config\";\nimport { shutdownDiffPool, verifyImageEngine } from \"@storyshelf/core/diff\";\nimport type { Logger } from \"@storyshelf/core/logger\";\nimport { purgeAiData } from \"@storyshelf/core/retention\";\nimport { sanitizeErrorText } from \"@storyshelf/core/utils\";\nimport type { ShelfRouter, ShelfLifecycle } from \"./app-types.ts\";\nimport { settleInsightJobs } from \"./insights/job.ts\";\nimport { startBranchGcTimer } from \"./retention-timer.ts\";\nimport type { ServerRuntime } from \"./runtime.ts\";\n\n/** Mutable setup-settlement cell shared by the gate, health, and `setup()`. */\nexport interface LifecycleCell {\n ready: Promise<AdapterSetupResult>;\n settled: AdapterSetupResult | null;\n /** Fire-and-forget boot tasks (recovery, credential check) that teardown must outwait. */\n background?: Promise<unknown>[];\n /** Override of {@link BACKGROUND_DRAIN_MS} (tests). */\n drainMs?: number;\n /** The job consumer (null on hosts that run none); started once adapters are ready, drained by `teardown()`. */\n consumer?: CaptureConsumer | null;\n /** The running consumer session, settled once it was stopped and drained. */\n consumerRun?: Promise<void>;\n /** Teardown ran: the consumer stays stopped (a late \"adapters are ready\" must not restart it). */\n consumerStopped?: boolean;\n}\n\n/** Longest teardown waits for background boot tasks before closing adapters anyway. */\nconst BACKGROUND_DRAIN_MS = 5000;\n\n/** Register a boot task so `teardown()` does not close adapters underneath it. */\nexport function trackBackground(cell: LifecycleCell, task: Promise<unknown>): void {\n (cell.background ??= []).push(task);\n}\n\n/** Start the job consumer once adapters are set up; a no-op when none exists or it runs. */\nexport function startConsumer(cell: LifecycleCell, logger: Logger): void {\n const consumer = cell.consumer;\n if (!consumer || consumer.isRunning() || cell.consumerStopped) {\n return;\n }\n cell.consumerRun = consumer.start().catch((error: unknown) => {\n logger.error({ err: error }, \"job consumer stopped\");\n });\n}\n\n/** Stop polling and wait for the jobs in flight to finish (the SIGTERM drain). */\nasync function stopConsumer(cell: LifecycleCell): Promise<void> {\n cell.consumerStopped = true;\n await cell.consumer?.stop();\n await cell.consumerRun;\n}\n\n/** Wait for tracked boot tasks, bounded so a hung task can never block shutdown. */\nasync function drainBackground(cell: LifecycleCell): Promise<void> {\n const pending = Promise.allSettled(cell.background ?? []);\n let timer: NodeJS.Timeout | undefined;\n const timeout = new Promise<void>((resolve) => {\n timer = setTimeout(resolve, cell.drainMs ?? BACKGROUND_DRAIN_MS);\n timer.unref();\n });\n await Promise.race([pending, timeout]);\n clearTimeout(timer);\n}\n\nfunction trackSettlement(cell: LifecycleCell, promise: Promise<AdapterSetupResult>): void {\n promise.then(\n (result) => {\n cell.settled = result;\n },\n () => {\n cell.settled = { ok: false, failures: [] };\n },\n );\n}\n\n/** First failing boot check (assembly, auth, AI), or null when all pass. */\nasync function firstBootFailure(\n options: ShelfOptions,\n logger: Logger,\n): Promise<AdapterSetupResult | null> {\n const invalid = validateBootAssembly(adapterSourcesOf(options));\n if (invalid.length > 0) {\n logger.error({ failures: invalid }, \"adapter validation failed\");\n return { ok: false, failures: invalid };\n }\n for (const [label, run] of [\n [\"auth\", runAuthSetup],\n [\"ai\", runAiSetup],\n [\"images\", runImagesSetup],\n ] as const) {\n // eslint-disable-next-line no-await-in-loop -- boot checks run in order and stop at the first failure\n const failed = await run(options);\n if (failed) {\n logger.error({ failures: failed.failures }, `${label} setup failed`);\n return failed;\n }\n }\n return null;\n}\n\n/** Kick the eager background setup run (resolves; never rejects). */\nfunction kickSetup(\n options: ShelfOptions,\n ctx: AdapterSetupContext,\n logger: Logger,\n cell: LifecycleCell,\n): void {\n // Auth is not an adapter: its one-shot boot validation runs first, then\n // the adapter setups. A failing secret check fails readiness, like others.\n cell.ready = (async (): Promise<AdapterSetupResult> => {\n const failed = await firstBootFailure(options, logger);\n return (\n failed ?? (await runAdapterSetups(collectSetups(adapterSourcesOf(options)), ctx, logger))\n );\n })();\n trackSettlement(cell, cell.ready);\n}\n\n/** Login method count, or null when the engine throws during introspection. */\nfunction loginMethodCount(auth: NonNullable<ShelfOptions[\"auth\"]>): number | null {\n try {\n return auth.loginMethods().length;\n } catch {\n return null;\n }\n}\n\n/** Log which auth mode the server boots with (open servers stay visible). */\nfunction logAuthMode(options: ShelfOptions, logger: Logger): void {\n if (!options.auth) {\n logger.info(\"auth disabled (local default; every route is open)\");\n return;\n }\n const methods = loginMethodCount(options.auth);\n if (methods === null) {\n logger.warn(\"auth loginMethods() threw during boot logging\");\n return;\n }\n logger.info({ loginMethods: methods }, \"auth enabled\");\n}\n\n/** Kick eager setup and attach the `app.lifecycle` namespace (single run). */\nexport function attachLifecycle(\n app: ShelfRouter,\n options: ShelfOptions,\n runtime: ServerRuntime,\n cell: LifecycleCell,\n): void {\n const setupCtx: AdapterSetupContext = { config: runtime.config, logger: runtime.logger };\n bindAdapterLoggers(options, runtime.logger);\n options.ai?.setLogger?.(runtime.logger.child({ component: \"ai\" }));\n logAuthMode(options, runtime.logger);\n kickSetup(options, setupCtx, runtime.logger, cell);\n const timer = startBranchGcInterval(options, runtime);\n Object.assign(app, {\n lifecycle: createLifecycle(options, setupCtx, runtime.logger, cell, timer),\n });\n}\n\nfunction startBranchGcInterval(\n options: ShelfOptions,\n runtime: ServerRuntime,\n): { stop(): void } | null {\n return startBranchGcTimer(options.database, options.storage, runtime.config, runtime.logger);\n}\n\n/** Run auth boot validation, returning a failure result instead of throwing. */\nasync function runAuthSetup(options: ShelfOptions): Promise<AdapterSetupResult | null> {\n try {\n await options.auth?.setup?.();\n return null;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n ok: false,\n failures: [\n { category: \"auth\", kind: \"auth\", name: \"auth\", error: sanitizeErrorText(message, 500) },\n ],\n };\n }\n}\n/** Run AI boot checks and the stale-run sweep; failure fails readiness like auth. */\nasync function runAiSetup(options: ShelfOptions): Promise<AdapterSetupResult | null> {\n if (!options.ai) {\n return null;\n }\n try {\n await options.ai.setup();\n await purgeAiData(options.database);\n return null;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n ok: false,\n failures: [\n { category: \"ai\", kind: \"ai\", name: \"ai\", error: sanitizeErrorText(message, 500) },\n ],\n };\n }\n}\n/** Load the configured image engine now, so a missing `sharp` fails readiness instead of the first diff. */\nasync function runImagesSetup(): Promise<AdapterSetupResult | null> {\n try {\n await verifyImageEngine();\n return null;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n ok: false,\n failures: [\n {\n category: \"images\",\n kind: \"images\",\n name: \"images\",\n error: sanitizeErrorText(message, 500),\n },\n ],\n };\n }\n}\n/** Validation failure settled on the cell — null when the assembly is sound. */\nfunction invalidSources(options: ShelfOptions, cell: LifecycleCell): AdapterSetupResult | null {\n const invalid = validateBootAssembly(adapterSourcesOf(options));\n if (invalid.length === 0) {\n return null;\n }\n const result: AdapterSetupResult = { ok: false, failures: invalid };\n cell.ready = Promise.resolve(result);\n cell.settled = result;\n return result;\n}\n/** Build the `app.lifecycle` namespace closing over one settlement cell. */\nfunction createLifecycle(\n options: ShelfOptions,\n ctx: AdapterSetupContext,\n logger: Logger,\n cell: LifecycleCell,\n timer: { stop(): void } | null = null,\n): ShelfLifecycle {\n return {\n get ready() {\n return cell.ready;\n },\n logger,\n setup: async () => {\n const blocked = invalidSources(options, cell);\n if (blocked) {\n throw new AdapterLifecycleError(\"setup\", blocked.failures);\n }\n const result = await runAdapterSetups(collectSetups(adapterSourcesOf(options)), ctx, logger);\n cell.ready = Promise.resolve(result);\n cell.settled = result;\n if (!result.ok) {\n throw new AdapterLifecycleError(\"setup\", result.failures);\n }\n cell.consumerStopped = false;\n startConsumer(cell, logger);\n },\n teardown: async () => {\n timer?.stop();\n await stopConsumer(cell);\n await drainBackground(cell);\n await settleInsightJobs();\n await options.ai?.teardown();\n await shutdownDiffPool();\n const result = await runAdapterTeardowns(\n collectTeardowns(adapterSourcesOf(options)),\n ctx,\n logger,\n );\n await options.observability?.shutdown().catch((error: unknown) => {\n logger.error({ err: error }, \"observability shutdown failed\");\n });\n if (!result.ok) {\n throw new AdapterLifecycleError(\"teardown\", result.failures);\n }\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lifecycle.mjs","names":[],"sources":["../src/lifecycle.ts"],"sourcesContent":["import type { AdapterSetupContext } from \"@storyshelf/core/adapter/metadata\";\nimport {\n AdapterLifecycleError,\n bindAdapterLoggers,\n adapterSourcesOf,\n collectSetups,\n collectTeardowns,\n runAdapterSetups,\n runAdapterTeardowns,\n validateBootAssembly,\n} from \"@storyshelf/core/adapter/setup\";\nimport type { AdapterSetupResult } from \"@storyshelf/core/adapter/setup\";\nimport type { CaptureConsumer } from \"@storyshelf/core/capture\";\nimport type { ShelfOptions } from \"@storyshelf/core/config\";\nimport { shutdownDiffPool, verifyImageEngine } from \"@storyshelf/core/diff\";\nimport type { Logger } from \"@storyshelf/core/logger\";\nimport { purgeAiData } from \"@storyshelf/core/retention\";\nimport { sanitizeErrorText } from \"@storyshelf/core/utils\";\nimport type { ShelfRouter, ShelfLifecycle } from \"./app-types.ts\";\nimport { settleInsightJobs } from \"./insights/job.ts\";\nimport { startBranchGcTimer } from \"./retention-timer.ts\";\nimport type { ServerRuntime } from \"./runtime.ts\";\n\n/** Mutable setup-settlement cell shared by the gate, health, and `setup()`. */\nexport interface LifecycleCell {\n ready: Promise<AdapterSetupResult>;\n settled: AdapterSetupResult | null;\n /** Fire-and-forget boot tasks (recovery, credential check) that teardown must outwait. */\n background?: Promise<unknown>[];\n /** Override of {@link BACKGROUND_DRAIN_MS} (tests). */\n drainMs?: number;\n /** The job consumer (null on hosts that run none); started once adapters are ready, drained by `teardown()`. */\n consumer?: CaptureConsumer | null;\n /** The running consumer session, settled once it was stopped and drained. */\n consumerRun?: Promise<void>;\n /** Teardown ran: the consumer stays stopped (a late \"adapters are ready\" must not restart it). */\n consumerStopped?: boolean;\n}\n\n/** Longest teardown waits for background boot tasks before closing adapters anyway. */\nconst BACKGROUND_DRAIN_MS = 5000;\n\n/** Register a boot task so `teardown()` does not close adapters underneath it. */\nexport function trackBackground(cell: LifecycleCell, task: Promise<unknown>): void {\n (cell.background ??= []).push(task);\n}\n\n/** Start the job consumer once adapters are set up; a no-op when none exists or it runs. */\nexport function startConsumer(cell: LifecycleCell, logger: Logger): void {\n const consumer = cell.consumer;\n if (!consumer || consumer.isRunning() || cell.consumerStopped) {\n return;\n }\n cell.consumerRun = consumer.start().catch((error: unknown) => {\n logger.error({ err: error }, \"job consumer stopped\");\n });\n}\n\n/** Stop polling and wait for the jobs in flight to finish (the SIGTERM drain). */\nasync function stopConsumer(cell: LifecycleCell): Promise<void> {\n cell.consumerStopped = true;\n await cell.consumer?.stop();\n await cell.consumerRun;\n}\n\n/** Wait for tracked boot tasks, bounded so a hung task can never block shutdown. */\nasync function drainBackground(cell: LifecycleCell): Promise<void> {\n const pending = Promise.allSettled(cell.background ?? []);\n let timer: NodeJS.Timeout | undefined;\n const timeout = new Promise<void>((resolve) => {\n timer = setTimeout(resolve, cell.drainMs ?? BACKGROUND_DRAIN_MS);\n timer.unref();\n });\n await Promise.race([pending, timeout]);\n clearTimeout(timer);\n}\n\nfunction trackSettlement(cell: LifecycleCell, promise: Promise<AdapterSetupResult>): void {\n promise.then(\n (result) => {\n cell.settled = result;\n },\n () => {\n cell.settled = { ok: false, failures: [] };\n },\n );\n}\n\n/** First failing boot check (assembly, auth, AI), or null when all pass. */\nasync function firstBootFailure(\n options: ShelfOptions,\n logger: Logger,\n): Promise<AdapterSetupResult | null> {\n const invalid = validateBootAssembly(adapterSourcesOf(options));\n if (invalid.length > 0) {\n logger.error({ failures: invalid }, \"adapter validation failed\");\n return { ok: false, failures: invalid };\n }\n for (const [label, run] of [\n [\"auth\", runAuthSetup],\n [\"ai\", runAiSetup],\n [\"images\", runImagesSetup],\n ] as const) {\n // eslint-disable-next-line no-await-in-loop -- boot checks run in order and stop at the first failure\n const failed = await run(options);\n if (failed) {\n logger.error({ failures: failed.failures }, `${label} setup failed`);\n return failed;\n }\n }\n return null;\n}\n\n/** Kick the eager background setup run (resolves; never rejects). */\nfunction kickSetup(\n options: ShelfOptions,\n ctx: AdapterSetupContext,\n logger: Logger,\n cell: LifecycleCell,\n): void {\n // Auth is not an adapter: its one-shot boot validation runs first, then\n // the adapter setups. A failing secret check fails readiness, like others.\n cell.ready = (async (): Promise<AdapterSetupResult> => {\n const failed = await firstBootFailure(options, logger);\n return (\n failed ?? (await runAdapterSetups(collectSetups(adapterSourcesOf(options)), ctx, logger))\n );\n })();\n trackSettlement(cell, cell.ready);\n}\n\n/** Login method count, or null when the engine throws during introspection. */\nfunction loginMethodCount(auth: NonNullable<ShelfOptions[\"auth\"]>): number | null {\n try {\n return auth.loginMethods().length;\n } catch {\n return null;\n }\n}\n\n/** Log which auth mode the server boots with (open servers stay visible). */\nfunction logAuthMode(options: ShelfOptions, logger: Logger): void {\n if (!options.auth) {\n logger.info(\"auth disabled (local default; every route is open)\");\n return;\n }\n const methods = loginMethodCount(options.auth);\n if (methods === null) {\n logger.warn(\"auth loginMethods() threw during boot logging\");\n return;\n }\n logger.info({ loginMethods: methods }, \"auth enabled\");\n}\n\n/** Kick eager setup and attach the `app.lifecycle` namespace (single run). */\nexport function attachLifecycle(\n app: ShelfRouter,\n options: ShelfOptions,\n runtime: ServerRuntime,\n cell: LifecycleCell,\n): void {\n const setupCtx: AdapterSetupContext = { config: runtime.config, logger: runtime.logger };\n bindAdapterLoggers(options, runtime.logger);\n options.ai?.setLogger?.(runtime.logger.child({ component: \"ai\" }));\n logAuthMode(options, runtime.logger);\n kickSetup(options, setupCtx, runtime.logger, cell);\n const timer = startBranchGcInterval(options, runtime);\n Object.assign(app, {\n lifecycle: createLifecycle(options, setupCtx, runtime.logger, cell, timer),\n });\n}\n\nfunction startBranchGcInterval(\n options: ShelfOptions,\n runtime: ServerRuntime,\n): { stop(): void } | null {\n return startBranchGcTimer(\n options.database,\n options.storage,\n runtime.config,\n runtime.logger,\n Boolean(options.testing),\n );\n}\n\n/** Run auth boot validation, returning a failure result instead of throwing. */\nasync function runAuthSetup(options: ShelfOptions): Promise<AdapterSetupResult | null> {\n try {\n await options.auth?.setup?.();\n return null;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n ok: false,\n failures: [\n { category: \"auth\", kind: \"auth\", name: \"auth\", error: sanitizeErrorText(message, 500) },\n ],\n };\n }\n}\n/** Run AI boot checks and the stale-run sweep; failure fails readiness like auth. */\nasync function runAiSetup(options: ShelfOptions): Promise<AdapterSetupResult | null> {\n if (!options.ai) {\n return null;\n }\n try {\n await options.ai.setup();\n await purgeAiData(options.database);\n return null;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n ok: false,\n failures: [\n { category: \"ai\", kind: \"ai\", name: \"ai\", error: sanitizeErrorText(message, 500) },\n ],\n };\n }\n}\n/** Load the configured image engine now, so a missing `sharp` fails readiness instead of the first diff. */\nasync function runImagesSetup(): Promise<AdapterSetupResult | null> {\n try {\n await verifyImageEngine();\n return null;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n ok: false,\n failures: [\n {\n category: \"images\",\n kind: \"images\",\n name: \"images\",\n error: sanitizeErrorText(message, 500),\n },\n ],\n };\n }\n}\n/** Validation failure settled on the cell — null when the assembly is sound. */\nfunction invalidSources(options: ShelfOptions, cell: LifecycleCell): AdapterSetupResult | null {\n const invalid = validateBootAssembly(adapterSourcesOf(options));\n if (invalid.length === 0) {\n return null;\n }\n const result: AdapterSetupResult = { ok: false, failures: invalid };\n cell.ready = Promise.resolve(result);\n cell.settled = result;\n return result;\n}\n/** Build the `app.lifecycle` namespace closing over one settlement cell. */\nfunction createLifecycle(\n options: ShelfOptions,\n ctx: AdapterSetupContext,\n logger: Logger,\n cell: LifecycleCell,\n timer: { stop(): void } | null = null,\n): ShelfLifecycle {\n return {\n get ready() {\n return cell.ready;\n },\n logger,\n setup: async () => {\n const blocked = invalidSources(options, cell);\n if (blocked) {\n throw new AdapterLifecycleError(\"setup\", blocked.failures);\n }\n const result = await runAdapterSetups(collectSetups(adapterSourcesOf(options)), ctx, logger);\n cell.ready = Promise.resolve(result);\n cell.settled = result;\n if (!result.ok) {\n throw new AdapterLifecycleError(\"setup\", result.failures);\n }\n cell.consumerStopped = false;\n startConsumer(cell, logger);\n },\n teardown: async () => {\n timer?.stop();\n await stopConsumer(cell);\n await drainBackground(cell);\n await settleInsightJobs();\n await options.ai?.teardown();\n await shutdownDiffPool();\n const result = await runAdapterTeardowns(\n collectTeardowns(adapterSourcesOf(options)),\n ctx,\n logger,\n );\n await options.observability?.shutdown().catch((error: unknown) => {\n logger.error({ err: error }, \"observability shutdown failed\");\n });\n if (!result.ok) {\n throw new AdapterLifecycleError(\"teardown\", result.failures);\n }\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import "node:url";
|
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { getStore } from "../store.mjs";
|
|
5
5
|
import { STORYBOOK_TOKEN_PREFIX } from "@storyshelf/core/utils";
|
|
6
|
-
|
|
6
|
+
// src/middleware/auth-gate.ts
|
|
7
7
|
/** Resolve the request user from the auth adapter (null when auth is off or a token is used). */
|
|
8
8
|
async function resolveRequestUser(c, auth) {
|
|
9
9
|
if (!auth) return null;
|
|
@@ -32,7 +32,6 @@ function authGate() {
|
|
|
32
32
|
return c.redirect("/auth/login", 302);
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
|
-
//#endregion
|
|
36
35
|
export { authGate, resolveRequestUser };
|
|
37
36
|
|
|
38
37
|
//# sourceMappingURL=auth-gate.mjs.map
|
package/dist/middleware/csrf.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import.meta.url;
|
|
|
4
4
|
import { timingSafeEqualString } from "@storyshelf/core/utils";
|
|
5
5
|
import { SESSION_COOKIE } from "@storyshelf/core/types";
|
|
6
6
|
import { createHash, randomBytes } from "node:crypto";
|
|
7
|
-
|
|
7
|
+
// src/middleware/csrf.ts
|
|
8
8
|
const DEFAULT_SESSION = "default";
|
|
9
9
|
const FALLBACK_SECRET = randomBytes(32).toString("hex");
|
|
10
10
|
/** Use the app secret when provided so tokens survive restarts and instances. */
|
|
@@ -69,7 +69,6 @@ function csrf(secret) {
|
|
|
69
69
|
function getCsrfToken(secret, sessionId = DEFAULT_SESSION) {
|
|
70
70
|
return generateToken(resolveSecret(secret), sessionId);
|
|
71
71
|
}
|
|
72
|
-
//#endregion
|
|
73
72
|
export { csrf, getCsrfToken, sessionIdFrom };
|
|
74
73
|
|
|
75
74
|
//# sourceMappingURL=csrf.mjs.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "node:path";
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
|
-
|
|
4
|
+
// src/middleware/rate-limit.ts
|
|
5
5
|
/** Direct peer IP from server runtimes exposing the socket (unspoofable TCP source). */
|
|
6
6
|
function peerIp(c) {
|
|
7
7
|
const env = c.env ?? {};
|
|
@@ -70,7 +70,6 @@ function createRateLimiter() {
|
|
|
70
70
|
};
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
|
-
//#endregion
|
|
74
73
|
export { createRateLimiter, rateLimitKey };
|
|
75
74
|
|
|
76
75
|
//# sourceMappingURL=rate-limit.mjs.map
|
|
@@ -2,7 +2,7 @@ import "node:path";
|
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { redactSecrets } from "@storyshelf/core/utils";
|
|
5
|
-
|
|
5
|
+
// src/middleware/request-log.ts
|
|
6
6
|
/** Hono middleware logging request start/end with duration. */
|
|
7
7
|
function requestLogging(logger) {
|
|
8
8
|
return async (c, next) => {
|
|
@@ -25,7 +25,6 @@ function requestLogging(logger) {
|
|
|
25
25
|
}, "request end");
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
-
//#endregion
|
|
29
28
|
export { requestLogging };
|
|
30
29
|
|
|
31
30
|
//# sourceMappingURL=request-log.mjs.map
|
|
@@ -3,7 +3,7 @@ import "node:url";
|
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { matchedRouteClass } from "./require-testing.mjs";
|
|
5
5
|
import { resolveAuthorizedProject } from "../routers/helpers.mjs";
|
|
6
|
-
|
|
6
|
+
// src/middleware/require-review.ts
|
|
7
7
|
/** Body of the 404 a `review` route answers for a project with review off. */
|
|
8
8
|
const REVIEW_DISABLED_MESSAGE = "Review is off for this project.";
|
|
9
9
|
/** Roles that may see a project's routes at all; the gate answers only to them. */
|
|
@@ -45,7 +45,6 @@ async function reviewIsOff(c) {
|
|
|
45
45
|
return false;
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
-
//#endregion
|
|
49
48
|
export { REVIEW_DISABLED_MESSAGE, requireReview };
|
|
50
49
|
|
|
51
50
|
//# sourceMappingURL=require-review.mjs.map
|
|
@@ -3,7 +3,7 @@ import "node:url";
|
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { classOfRoute } from "../routes/classification.mjs";
|
|
5
5
|
import { matchedRoutes } from "hono/route";
|
|
6
|
-
|
|
6
|
+
// src/middleware/require-testing.ts
|
|
7
7
|
/** Body of the 404 a `testing` route answers on a hosting-only instance. */
|
|
8
8
|
const TESTING_DISABLED_MESSAGE = "Testing is not enabled on this instance.";
|
|
9
9
|
/**
|
|
@@ -35,7 +35,6 @@ function matchedRouteClass(c) {
|
|
|
35
35
|
function isTestingRoute(c) {
|
|
36
36
|
return matchedRouteClass(c) === "testing";
|
|
37
37
|
}
|
|
38
|
-
//#endregion
|
|
39
38
|
export { TESTING_DISABLED_MESSAGE, matchedRouteClass, requireTesting };
|
|
40
39
|
|
|
41
40
|
//# sourceMappingURL=require-testing.mjs.map
|
|
@@ -3,7 +3,7 @@ import "node:url";
|
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { runWithTimings } from "@storyshelf/core/utils";
|
|
5
5
|
import { setMetric, timing } from "hono/timing";
|
|
6
|
-
|
|
6
|
+
// src/middleware/server-timing.ts
|
|
7
7
|
/**
|
|
8
8
|
* Opt-in `Server-Timing` response headers over Hono's timing middleware.
|
|
9
9
|
*
|
|
@@ -33,7 +33,6 @@ function serverTiming(enabled) {
|
|
|
33
33
|
if (headers.length > 0) c.res.headers.append("Server-Timing", headers.join(","));
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
-
//#endregion
|
|
37
36
|
export { serverTiming };
|
|
38
37
|
|
|
39
38
|
//# sourceMappingURL=server-timing.mjs.map
|
|
@@ -2,7 +2,7 @@ import "node:path";
|
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { sanitizeErrorText } from "@storyshelf/core/utils";
|
|
5
|
-
|
|
5
|
+
// src/middleware/setup-gate.ts
|
|
6
6
|
/** Failures safe to expose on the setup gate's 503 (redacted, no secrets). */
|
|
7
7
|
function publicFailures(result) {
|
|
8
8
|
return result.failures.map((failure) => ({
|
|
@@ -31,7 +31,6 @@ function setupGate(getReady) {
|
|
|
31
31
|
await next();
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
|
-
//#endregion
|
|
35
34
|
export { setupGate };
|
|
36
35
|
|
|
37
36
|
//# sourceMappingURL=setup-gate.mjs.map
|
|
@@ -3,7 +3,7 @@ import "node:url";
|
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { runWithStore } from "../store.mjs";
|
|
5
5
|
import { sessionIdFrom } from "./csrf.mjs";
|
|
6
|
-
|
|
6
|
+
// src/middleware/store-scope.ts
|
|
7
7
|
/** Hono middleware running downstream handlers inside the request store. */
|
|
8
8
|
function storeScope(deps) {
|
|
9
9
|
return async (c, next) => {
|
|
@@ -37,7 +37,6 @@ function storeScope(deps) {
|
|
|
37
37
|
});
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
|
-
//#endregion
|
|
41
40
|
export { storeScope };
|
|
42
41
|
|
|
43
42
|
//# sourceMappingURL=store-scope.mjs.map
|
|
@@ -2,7 +2,7 @@ import "node:path";
|
|
|
2
2
|
import "node:url";
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { getStore } from "./store.mjs";
|
|
5
|
-
|
|
5
|
+
// src/notifications-pause.ts
|
|
6
6
|
/** What the System page and the notification settings tell the viewer. */
|
|
7
7
|
const NOTIFICATIONS_PAUSED_TEXT = "Notifications are paused until a public URL is set; set PUBLIC_BASE_URL or confirm the detected origin.";
|
|
8
8
|
/** The origin notifications link to, or `undefined` while none is resolved. */
|
|
@@ -27,7 +27,6 @@ function warnPaused(logger, event) {
|
|
|
27
27
|
function clearPaused(logger) {
|
|
28
28
|
warned.delete(logger);
|
|
29
29
|
}
|
|
30
|
-
//#endregion
|
|
31
30
|
export { NOTIFICATIONS_PAUSED_TEXT, clearPaused, notificationOrigin, notificationsPaused, warnPaused };
|
|
32
31
|
|
|
33
32
|
//# sourceMappingURL=notifications-pause.mjs.map
|
package/dist/notify.d.mts
CHANGED
|
@@ -3,16 +3,14 @@ import __tsdown_shims_url from 'node:url';
|
|
|
3
3
|
import { ShelfConfig, UIConfig } from "@storyshelf/core/config";
|
|
4
4
|
import "@storyshelf/core/models";
|
|
5
5
|
import { NotifierProvider } from "@storyshelf/core/adapter/notifier";
|
|
6
|
-
import { Logger } from "@storyshelf/core/logger";
|
|
7
6
|
import { DatabaseAdapter } from "@storyshelf/core/adapter/database";
|
|
7
|
+
import { Logger } from "@storyshelf/core/logger";
|
|
8
8
|
import "@storyshelf/core/schema";
|
|
9
|
-
//#region src/notify.d.ts
|
|
10
9
|
/**
|
|
11
10
|
* Host hook for the auth engine (`onAuthSystemEvent`): fans `sys:*` auth
|
|
12
11
|
* events out to admin channels. Safe to call anywhere — outside a request
|
|
13
12
|
* scope (e.g. boot) it resolves to a no-op instead of throwing.
|
|
14
13
|
*/
|
|
15
14
|
declare function createAuthSystemHook(): (event: "sys:user-created" | "sys:invite-issued", data: Record<string, unknown>) => Promise<void>;
|
|
16
|
-
//#endregion
|
|
17
15
|
export { createAuthSystemHook };
|
|
18
16
|
//# sourceMappingURL=notify.d.mts.map
|
package/dist/notify.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { getStore } from "./store.mjs";
|
|
|
6
6
|
import { clearPaused, notificationOrigin, warnPaused } from "./notifications-pause.mjs";
|
|
7
7
|
import { MemberModel, NotificationChannelModel, NotificationSubscriptionModel, UserModel } from "@storyshelf/core/models";
|
|
8
8
|
import { emitNotifications, emitSystemNotification } from "@storyshelf/core/adapter/notifier";
|
|
9
|
-
|
|
9
|
+
// src/notify.ts
|
|
10
10
|
/** Absolute review URL for a project page under the resolved public origin. */
|
|
11
11
|
function reviewUrlFor(origin, slug, suffix) {
|
|
12
12
|
return `${origin}/projects/${slug}${suffix}`;
|
|
@@ -174,7 +174,6 @@ function createAuthSystemHook() {
|
|
|
174
174
|
function now() {
|
|
175
175
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
176
176
|
}
|
|
177
|
-
//#endregion
|
|
178
177
|
export { createAuthSystemHook, notifyProject, notifyProjectWith, notifySystem, notifySystemWith };
|
|
179
178
|
|
|
180
179
|
//# sourceMappingURL=notify.mjs.map
|
|
@@ -3,7 +3,7 @@ import "node:url";
|
|
|
3
3
|
import.meta.url;
|
|
4
4
|
import { Alert, Badge, Meta } from "../ui/feedback.mjs";
|
|
5
5
|
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
6
|
-
|
|
6
|
+
// src/pages/a11y-violations.tsx
|
|
7
7
|
/** The axe violation texts stored on a snapshot (`rule (impact): help [selector] n node(s)`); empty when none. */
|
|
8
8
|
function a11yViolationsOf(snapshot) {
|
|
9
9
|
if (!snapshot.a11yViolations) return [];
|
|
@@ -110,7 +110,6 @@ function A11yViolationsDetails(props) {
|
|
|
110
110
|
]
|
|
111
111
|
}) }), violations.map((text) => /* @__PURE__ */ jsx(Meta, { children: /* @__PURE__ */ jsx("code", { children: text }) }, text))] });
|
|
112
112
|
}
|
|
113
|
-
//#endregion
|
|
114
113
|
export { A11yViolationsDetails, A11yViolationsNotice, a11yCarrierOf, a11yCountOf, a11yFindingsOf, a11ySummaryNote, a11ySummaryOf, a11yUnavailableNote, a11yUnavailableReasonOf, a11yViolationsOf, isAxeUnavailable };
|
|
115
114
|
|
|
116
115
|
//# sourceMappingURL=a11y-violations.mjs.map
|
package/dist/pages/admin-ai.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { Card, PageHeader, SectionTitle } from "../ui/layout.mjs";
|
|
|
6
6
|
import { Table } from "../ui/table.mjs";
|
|
7
7
|
import { DocumentLayout } from "../ui/document.mjs";
|
|
8
8
|
import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
9
|
-
|
|
9
|
+
// src/pages/admin-ai.tsx
|
|
10
10
|
function DaysTable(props) {
|
|
11
11
|
return /* @__PURE__ */ jsx(Table, {
|
|
12
12
|
dense: true,
|
|
@@ -72,7 +72,6 @@ function renderAdminAiPage(report) {
|
|
|
72
72
|
]
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
|
-
//#endregion
|
|
76
75
|
export { renderAdminAiPage };
|
|
77
76
|
|
|
78
77
|
//# sourceMappingURL=admin-ai.mjs.map
|
|
@@ -8,7 +8,7 @@ import { Card, SectionTitle } from "../ui/layout.mjs";
|
|
|
8
8
|
import { HStack } from "../ui/stacks.mjs";
|
|
9
9
|
import { csrfField } from "../ui/csrf-field.mjs";
|
|
10
10
|
import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
11
|
-
|
|
11
|
+
// src/pages/admin-system-origin.tsx
|
|
12
12
|
const SOURCE_LABELS = {
|
|
13
13
|
config: "configured (PUBLIC_BASE_URL)",
|
|
14
14
|
platform: "detected from the hosting platform",
|
|
@@ -17,7 +17,7 @@ const SOURCE_LABELS = {
|
|
|
17
17
|
/** Where absolute links in emails, commit statuses and signed Storybook URLs point (ADR 0007). */
|
|
18
18
|
function PublicOriginCard(props) {
|
|
19
19
|
const { resolved, stored, learns } = props.origin;
|
|
20
|
-
const
|
|
20
|
+
const fixed = resolved !== null && resolved.source !== "admin";
|
|
21
21
|
return /* @__PURE__ */ jsxs(Card, { children: [
|
|
22
22
|
/* @__PURE__ */ jsx(SectionTitle, { children: "Public origin" }),
|
|
23
23
|
/* @__PURE__ */ jsxs(Meta, { children: [
|
|
@@ -30,11 +30,17 @@ function PublicOriginCard(props) {
|
|
|
30
30
|
learns,
|
|
31
31
|
stored
|
|
32
32
|
}) : /* @__PURE__ */ jsx(Resolved, { resolved }),
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
learns && !fixed ? /* @__PURE__ */ jsx("div", { "data-report-origin": "/admin/public-origin/report" }) : null,
|
|
34
|
+
fixed ? null : /* @__PURE__ */ jsx(OriginForms, {
|
|
35
|
+
learns,
|
|
36
|
+
stored
|
|
37
|
+
})
|
|
36
38
|
] });
|
|
37
39
|
}
|
|
40
|
+
function OriginForms(props) {
|
|
41
|
+
if (!props.learns) return /* @__PURE__ */ jsx(Fragment, {});
|
|
42
|
+
return props.stored === null ? /* @__PURE__ */ jsx(EnterForm, {}) : /* @__PURE__ */ jsx(StoredForm, { stored: props.stored });
|
|
43
|
+
}
|
|
38
44
|
function NothingResolved(props) {
|
|
39
45
|
if (props.stored !== null && !props.stored.confirmed) return /* @__PURE__ */ jsxs(Alert, {
|
|
40
46
|
tone: "warning",
|
|
@@ -53,10 +59,14 @@ function NothingResolved(props) {
|
|
|
53
59
|
}
|
|
54
60
|
function Resolved(props) {
|
|
55
61
|
const { resolved } = props;
|
|
56
|
-
return /* @__PURE__ */ jsxs(HStack, { children: [
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
return /* @__PURE__ */ jsxs(HStack, { children: [
|
|
63
|
+
/* @__PURE__ */ jsx("code", { children: resolved.url }),
|
|
64
|
+
/* @__PURE__ */ jsx(Badge, {
|
|
65
|
+
tone: "success",
|
|
66
|
+
children: SOURCE_LABELS[resolved.source]
|
|
67
|
+
}),
|
|
68
|
+
resolved.source === "admin" ? null : /* @__PURE__ */ jsx(Meta, { children: "Nothing to confirm." })
|
|
69
|
+
] });
|
|
60
70
|
}
|
|
61
71
|
/** Confirm the detected origin, replace it, or drop it. */
|
|
62
72
|
function StoredForm(props) {
|
|
@@ -117,7 +127,6 @@ function EnterForm() {
|
|
|
117
127
|
]
|
|
118
128
|
});
|
|
119
129
|
}
|
|
120
|
-
//#endregion
|
|
121
130
|
export { PublicOriginCard };
|
|
122
131
|
|
|
123
132
|
//# sourceMappingURL=admin-system-origin.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin-system-origin.mjs","names":[],"sources":["../../src/pages/admin-system-origin.tsx"],"sourcesContent":["import type { ResolvedPublicOrigin, StoredPublicOrigin } from \"@storyshelf/core/models\";\nimport type { HtmlEscapedString } from \"hono/utils/html\";\nimport {\n Alert,\n Badge,\n Button,\n Card,\n Field,\n FormActions,\n HStack,\n Meta,\n SectionTitle,\n} from \"../ui/components.tsx\";\nimport { csrfField } from \"../ui/csrf-field.tsx\";\n\n/* eslint-disable promise-function-async -- Hono JSX components return HtmlEscapedString | Promise<HtmlEscapedString> */\n\n/** What the System page knows about the public origin. */\nexport interface PublicOriginView {\n /** What background jobs use now (configured, platform or confirmed); `null` when nothing is confirmed. */\n resolved: ResolvedPublicOrigin | null;\n /** The origin an admin's browser reported; `null` when none did. */\n stored: StoredPublicOrigin | null;\n /** Whether this deployment can learn an origin from an admin's browser (authentication is on). */\n learns: boolean;\n}\n\nconst SOURCE_LABELS = {\n config: \"configured (PUBLIC_BASE_URL)\",\n platform: \"detected from the hosting platform\",\n admin: \"confirmed by an admin\",\n} as const;\n\n/** Where absolute links in emails, commit statuses and signed Storybook URLs point (ADR 0007). */\nexport function PublicOriginCard(props: {\n origin: PublicOriginView;\n}): HtmlEscapedString | Promise<HtmlEscapedString> {\n const { resolved, stored, learns } = props.origin;\n // A configured or platform origin outranks what a browser reports
|
|
1
|
+
{"version":3,"file":"admin-system-origin.mjs","names":[],"sources":["../../src/pages/admin-system-origin.tsx"],"sourcesContent":["import type { ResolvedPublicOrigin, StoredPublicOrigin } from \"@storyshelf/core/models\";\nimport type { HtmlEscapedString } from \"hono/utils/html\";\nimport {\n Alert,\n Badge,\n Button,\n Card,\n Field,\n FormActions,\n HStack,\n Meta,\n SectionTitle,\n} from \"../ui/components.tsx\";\nimport { csrfField } from \"../ui/csrf-field.tsx\";\n\n/* eslint-disable promise-function-async -- Hono JSX components return HtmlEscapedString | Promise<HtmlEscapedString> */\n\n/** What the System page knows about the public origin. */\nexport interface PublicOriginView {\n /** What background jobs use now (configured, platform or confirmed); `null` when nothing is confirmed. */\n resolved: ResolvedPublicOrigin | null;\n /** The origin an admin's browser reported; `null` when none did. */\n stored: StoredPublicOrigin | null;\n /** Whether this deployment can learn an origin from an admin's browser (authentication is on). */\n learns: boolean;\n}\n\nconst SOURCE_LABELS = {\n config: \"configured (PUBLIC_BASE_URL)\",\n platform: \"detected from the hosting platform\",\n admin: \"confirmed by an admin\",\n} as const;\n\n/** Where absolute links in emails, commit statuses and signed Storybook URLs point (ADR 0007). */\nexport function PublicOriginCard(props: {\n origin: PublicOriginView;\n}): HtmlEscapedString | Promise<HtmlEscapedString> {\n const { resolved, stored, learns } = props.origin;\n // A configured or platform origin outranks what a browser reports: there is nothing to learn or\n // to confirm, so neither the beacon nor the forms are offered (#315).\n const fixed = resolved !== null && resolved.source !== \"admin\";\n const reports = learns && !fixed;\n return (\n <Card>\n <SectionTitle>Public origin</SectionTitle>\n <Meta>\n The address links in emails, commit statuses and signed Storybook URLs use. It comes from{\" \"}\n <code>PUBLIC_BASE_URL</code>, then the hosting platform, then an address an admin confirmed\n here. An address detected from a browser is used for none of these until it is confirmed;\n sign-in (OAuth redirects) may use the address of the request itself.\n </Meta>\n {resolved === null ? (\n <NothingResolved learns={learns} stored={stored} />\n ) : (\n <Resolved resolved={resolved} />\n )}\n {reports ? <div data-report-origin=\"/admin/public-origin/report\" /> : null}\n {fixed ? null : <OriginForms learns={learns} stored={stored} />}\n </Card>\n );\n}\n\nfunction OriginForms(props: {\n learns: boolean;\n stored: StoredPublicOrigin | null;\n}): HtmlEscapedString | Promise<HtmlEscapedString> {\n if (!props.learns) {\n return <></>;\n }\n return props.stored === null ? <EnterForm /> : <StoredForm stored={props.stored} />;\n}\n\nfunction NothingResolved(props: {\n learns: boolean;\n stored: StoredPublicOrigin | null;\n}): HtmlEscapedString | Promise<HtmlEscapedString> {\n if (props.stored !== null && !props.stored.confirmed) {\n return (\n <Alert tone=\"warning\" title=\"Public origin detected, not confirmed\">\n <code>{props.stored.origin}</code> was detected from your browser. Confirm it to enable\n notifications and signed Storybook URLs: until then no commit status or email is sent, and\n browsers that reach the Storybook by URL stay unavailable.\n </Alert>\n );\n }\n return (\n <Alert tone=\"warning\" title=\"No public origin is known\">\n Notifications are paused, and browsers that reach the Storybook by URL stay unavailable.{\" \"}\n {props.learns\n ? \"Reload this page: it reports the address you opened it with, for you to confirm.\"\n : \"Set PUBLIC_BASE_URL, or turn authentication on so an admin's browser can report it.\"}\n </Alert>\n );\n}\n\nfunction Resolved(props: {\n resolved: ResolvedPublicOrigin;\n}): HtmlEscapedString | Promise<HtmlEscapedString> {\n const { resolved } = props;\n return (\n <HStack>\n <code>{resolved.url}</code>\n <Badge tone=\"success\">{SOURCE_LABELS[resolved.source]}</Badge>\n {resolved.source === \"admin\" ? null : <Meta>Nothing to confirm.</Meta>}\n </HStack>\n );\n}\n\n/** Confirm the detected origin, replace it, or drop it. */\nfunction StoredForm(props: {\n stored: StoredPublicOrigin;\n}): HtmlEscapedString | Promise<HtmlEscapedString> {\n const { stored } = props;\n return (\n <>\n <form\n method=\"post\"\n action=\"/admin/public-origin\"\n hx-post=\"/admin/public-origin\"\n hx-target=\"body\"\n >\n {csrfField()}\n <Field\n label=\"Stored origin\"\n name=\"origin\"\n value={stored.origin}\n hint={\n stored.confirmed\n ? \"Confirmed. Change it if this is not the address people use.\"\n : \"Detected from your browser. Confirm it, or enter the address people use.\"\n }\n />\n <FormActions>\n <Button type=\"submit\" variant=\"secondary\" size=\"sm\">\n {stored.confirmed ? \"Save\" : \"Confirm\"}\n </Button>\n </FormActions>\n </form>\n <form\n method=\"post\"\n action=\"/admin/public-origin/clear\"\n hx-post=\"/admin/public-origin/clear\"\n hx-target=\"body\"\n >\n {csrfField()}\n <Button type=\"submit\" variant=\"secondary\" size=\"sm\">\n Forget stored origin\n </Button>\n </form>\n </>\n );\n}\n\n/** Enter an origin by hand when none was reported. */\nfunction EnterForm(): HtmlEscapedString | Promise<HtmlEscapedString> {\n return (\n <form\n method=\"post\"\n action=\"/admin/public-origin\"\n hx-post=\"/admin/public-origin\"\n hx-target=\"body\"\n >\n {csrfField()}\n <Field label=\"Origin\" name=\"origin\" placeholder=\"https://shelf.example.com\" />\n <FormActions>\n <Button type=\"submit\" variant=\"secondary\" size=\"sm\">\n Save\n </Button>\n </FormActions>\n </form>\n );\n}\n"],"mappings":"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"}
|
|
@@ -7,7 +7,7 @@ import { Table } from "../ui/table.mjs";
|
|
|
7
7
|
import { targetLabel } from "../targets.mjs";
|
|
8
8
|
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
9
9
|
import { unavailableReason } from "@storyshelf/core/adapter/capture-runner";
|
|
10
|
-
|
|
10
|
+
// src/pages/admin-system-targets.tsx
|
|
11
11
|
/** The browsers the capture runner offers and whether each can render now (ADR 0007, decision 7). */
|
|
12
12
|
function CaptureTargetsCard(props) {
|
|
13
13
|
const { targets } = props;
|
|
@@ -47,7 +47,6 @@ function CaptureTargetsCard(props) {
|
|
|
47
47
|
}) })] }) })
|
|
48
48
|
] });
|
|
49
49
|
}
|
|
50
|
-
//#endregion
|
|
51
50
|
export { CaptureTargetsCard };
|
|
52
51
|
|
|
53
52
|
//# sourceMappingURL=admin-system-targets.mjs.map
|
|
@@ -12,7 +12,7 @@ import { PublicOriginCard } from "./admin-system-origin.mjs";
|
|
|
12
12
|
import { CaptureTargetsCard } from "./admin-system-targets.mjs";
|
|
13
13
|
import { NotificationsPausedNotice } from "./notifications-paused.mjs";
|
|
14
14
|
import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
15
|
-
|
|
15
|
+
// src/pages/admin-system.tsx
|
|
16
16
|
function healthTone(state) {
|
|
17
17
|
if (state === "ok") return "success";
|
|
18
18
|
if (state === "starting") return "info";
|
|
@@ -287,7 +287,6 @@ function renderAdminSystemPage(data) {
|
|
|
287
287
|
]
|
|
288
288
|
});
|
|
289
289
|
}
|
|
290
|
-
//#endregion
|
|
291
290
|
export { renderAdminSystemPage };
|
|
292
291
|
|
|
293
292
|
//# sourceMappingURL=admin-system.mjs.map
|
|
@@ -26,7 +26,7 @@ import { BuildModel, CaptureAttemptModel, CaptureLogModel, LabelModel, ProjectMo
|
|
|
26
26
|
import { CANCELLABLE_BUILD_STATUSES } from "@storyshelf/core/types";
|
|
27
27
|
import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
28
28
|
import { createUrlBuilder } from "@storyshelf/core/urls";
|
|
29
|
-
|
|
29
|
+
// src/pages/build-detail.tsx
|
|
30
30
|
/** Map a capture log level to its badge tone. */
|
|
31
31
|
function logTone(level) {
|
|
32
32
|
if (level === "error") return "danger";
|
|
@@ -414,7 +414,6 @@ async function renderBuildDetailPage(buildId, usageByStory = /* @__PURE__ */ new
|
|
|
414
414
|
]
|
|
415
415
|
});
|
|
416
416
|
}
|
|
417
|
-
//#endregion
|
|
418
417
|
export { renderBuildDetailPage };
|
|
419
418
|
|
|
420
419
|
//# sourceMappingURL=build-detail.mjs.map
|
|
@@ -7,7 +7,7 @@ import { Kbd } from "../ui/kbd.mjs";
|
|
|
7
7
|
import { groupOf, neighbours, snapshotPageUrl } from "./build-diff-model.mjs";
|
|
8
8
|
import { actionBar, actionGroup, actionHints, actionPosition } from "../ui/styles/review-layout.mjs";
|
|
9
9
|
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
10
|
-
|
|
10
|
+
// src/pages/build-diff-actions.tsx
|
|
11
11
|
function apiBase(project, build) {
|
|
12
12
|
return `/api/v1/projects/${project.slug}/builds/${build.id}`;
|
|
13
13
|
}
|
|
@@ -210,7 +210,6 @@ function DiffActionBar(props) {
|
|
|
210
210
|
]
|
|
211
211
|
});
|
|
212
212
|
}
|
|
213
|
-
//#endregion
|
|
214
213
|
export { DiffActionBar };
|
|
215
214
|
|
|
216
215
|
//# sourceMappingURL=build-diff-actions.mjs.map
|
|
@@ -9,7 +9,7 @@ import { VStack } from "../ui/stacks.mjs";
|
|
|
9
9
|
import { csrfField } from "../ui/csrf-field.mjs";
|
|
10
10
|
import { reviewComment, reviewCommentActions, reviewCommentBody, reviewCommentHead } from "../ui/styles/review.mjs";
|
|
11
11
|
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
12
|
-
|
|
12
|
+
// src/pages/build-diff-comments.tsx
|
|
13
13
|
/** One comment with an optional resolve action. */
|
|
14
14
|
function CommentCard(props) {
|
|
15
15
|
const { project, build, comment, canReview, anchor } = props;
|
|
@@ -100,7 +100,6 @@ function DiffComments(props) {
|
|
|
100
100
|
})
|
|
101
101
|
] });
|
|
102
102
|
}
|
|
103
|
-
//#endregion
|
|
104
103
|
export { CommentCard, DiffComments };
|
|
105
104
|
|
|
106
105
|
//# sourceMappingURL=build-diff-comments.mjs.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { css } from "../ui/css.mjs";
|
|
5
|
+
import { HStack } from "../ui/stacks.mjs";
|
|
6
|
+
import { snapshotImageUrl } from "./build-diff-model.mjs";
|
|
7
|
+
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
8
|
+
// src/pages/build-diff-default.tsx
|
|
9
|
+
const comparisonImage = css`
|
|
10
|
+
/* default-comparison-image */
|
|
11
|
+
max-width: 100%;
|
|
12
|
+
`;
|
|
13
|
+
function DefaultComparisonView(props) {
|
|
14
|
+
const { project, build, snapshot, comparison } = props;
|
|
15
|
+
const params = new URLSearchParams({
|
|
16
|
+
reference: "default",
|
|
17
|
+
id: comparison.id,
|
|
18
|
+
version: comparison.version
|
|
19
|
+
});
|
|
20
|
+
const baseline = `${snapshotImageUrl(project, build, snapshot, "baseline")}?${params}`;
|
|
21
|
+
return /* @__PURE__ */ jsxs("details", { children: [
|
|
22
|
+
/* @__PURE__ */ jsxs("summary", { children: [
|
|
23
|
+
"Compare with current default branch (",
|
|
24
|
+
comparison.branch,
|
|
25
|
+
")"
|
|
26
|
+
] }),
|
|
27
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
28
|
+
"This is a secondary comparison. Approval still uses the primary comparison above. Default baseline updated ",
|
|
29
|
+
comparison.version,
|
|
30
|
+
"."
|
|
31
|
+
] }),
|
|
32
|
+
/* @__PURE__ */ jsx("p", { children: comparison.environment === "different" ? "Recorded environment fingerprints differ; some components may be unknown and differences may be environmental." : comparison.environment === "unknown" ? "Capture environment information is unavailable." : "Recorded capture environment fingerprints match; some components may be unknown." }),
|
|
33
|
+
comparison.available ? /* @__PURE__ */ jsxs(HStack, {
|
|
34
|
+
align: "start",
|
|
35
|
+
children: [/* @__PURE__ */ jsx(BaselineImage, { src: baseline }), /* @__PURE__ */ jsxs("figure", { children: [/* @__PURE__ */ jsx("figcaption", { children: "This build" }), /* @__PURE__ */ jsx("img", {
|
|
36
|
+
src: snapshotImageUrl(project, build, snapshot, "image"),
|
|
37
|
+
alt: "This build's screenshot",
|
|
38
|
+
class: comparisonImage
|
|
39
|
+
})] })]
|
|
40
|
+
}) : /* @__PURE__ */ jsx("p", { children: "The current default-branch baseline image is unavailable." })
|
|
41
|
+
] });
|
|
42
|
+
}
|
|
43
|
+
function BaselineImage(props) {
|
|
44
|
+
return /* @__PURE__ */ jsxs("figure", { children: [/* @__PURE__ */ jsx("figcaption", { children: "Current default-branch baseline" }), /* @__PURE__ */ jsx("img", {
|
|
45
|
+
src: props.src,
|
|
46
|
+
alt: "Current default-branch baseline",
|
|
47
|
+
loading: "lazy",
|
|
48
|
+
class: comparisonImage
|
|
49
|
+
})] });
|
|
50
|
+
}
|
|
51
|
+
export { DefaultComparisonView };
|
|
52
|
+
|
|
53
|
+
//# sourceMappingURL=build-diff-default.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-diff-default.mjs","names":[],"sources":["../../src/pages/build-diff-default.tsx"],"sourcesContent":["import type { Build, Project, Snapshot } from \"@storyshelf/core/schema\";\nimport type { HtmlEscapedString } from \"hono/utils/html\";\nimport type { DefaultComparison } from \"../routers/baseline-reference.ts\";\nimport { HStack } from \"../ui/components.tsx\";\nimport { css } from \"../ui/css.ts\";\nimport { snapshotImageUrl } from \"./build-diff-model.ts\";\n\nconst comparisonImage = css`\n /* default-comparison-image */\n max-width: 100%;\n`;\n\n/* eslint-disable promise-function-async -- Hono JSX components may return HTML or a promise */\nexport function DefaultComparisonView(props: {\n project: Project;\n build: Build;\n snapshot: Snapshot;\n comparison: DefaultComparison;\n}): HtmlEscapedString | Promise<HtmlEscapedString> {\n const { project, build, snapshot, comparison } = props;\n const params = new URLSearchParams({\n reference: \"default\",\n id: comparison.id,\n version: comparison.version,\n });\n const baseline = `${snapshotImageUrl(project, build, snapshot, \"baseline\")}?${params}`;\n return (\n <details>\n <summary>Compare with current default branch ({comparison.branch})</summary>\n <p>\n This is a secondary comparison. Approval still uses the primary comparison above. Default\n baseline updated {comparison.version}.\n </p>\n <p>\n {comparison.environment === \"different\"\n ? \"Recorded environment fingerprints differ; some components may be unknown and differences may be environmental.\"\n : comparison.environment === \"unknown\"\n ? \"Capture environment information is unavailable.\"\n : \"Recorded capture environment fingerprints match; some components may be unknown.\"}\n </p>\n {comparison.available ? (\n <HStack align=\"start\">\n <BaselineImage src={baseline} />\n <figure>\n <figcaption>This build</figcaption>\n <img\n src={snapshotImageUrl(project, build, snapshot, \"image\")}\n alt=\"This build's screenshot\"\n class={comparisonImage}\n />\n </figure>\n </HStack>\n ) : (\n <p>The current default-branch baseline image is unavailable.</p>\n )}\n </details>\n );\n}\n\nfunction BaselineImage(props: { src: string }): HtmlEscapedString | Promise<HtmlEscapedString> {\n return (\n <figure>\n <figcaption>Current default-branch baseline</figcaption>\n <img\n src={props.src}\n alt=\"Current default-branch baseline\"\n loading=\"lazy\"\n class={comparisonImage}\n />\n </figure>\n );\n}\n"],"mappings":";;;;;;;;AAOA,MAAM,kBAAkB,GAAG;;;;AAM3B,SAAgB,sBAAsB,OAKa;CACjD,MAAM,EAAE,SAAS,OAAO,UAAU,eAAe;CACjD,MAAM,SAAS,IAAI,gBAAgB;EACjC,WAAW;EACX,IAAI,WAAW;EACf,SAAS,WAAW;CACtB,CAAC;CACD,MAAM,WAAW,GAAG,iBAAiB,SAAS,OAAO,UAAU,UAAU,EAAE,GAAG;CAC9E,OACE,qBAAC,WAAD,EAAA,UAAA;EACE,qBAAC,WAAD,EAAA,UAAA;GAAS;GAAsC,WAAW;GAAO;EAAU,EAAA,CAAA;EAC3E,qBAAC,KAAD,EAAA,UAAA;GAAG;GAEiB,WAAW;GAAQ;EACpC,EAAA,CAAA;EACH,oBAAC,KAAD,EAAA,UACG,WAAW,gBAAgB,cACxB,mHACA,WAAW,gBAAgB,YACzB,oDACA,mFACL,CAAA;EACF,WAAW,YACV,qBAAC,QAAD;GAAQ,OAAM;GAAd,UAAA,CACE,oBAAC,eAAD,EAAe,KAAK,SAAW,CAAA,GAC/B,qBAAC,UAAD,EAAA,UAAA,CACE,oBAAC,cAAD,EAAA,UAAY,aAAsB,CAAA,GAClC,oBAAC,OAAD;IACE,KAAK,iBAAiB,SAAS,OAAO,UAAU,OAAO;IACvD,KAAI;IACJ,OAAO;GACR,CAAA,CACK,EAAA,CAAA,CACF;EAER,CAAA,IAAA,oBAAC,KAAD,EAAA,UAAG,4DAA4D,CAAA;CAE1D,EAAA,CAAA;AAEb;AAEA,SAAS,cAAc,OAAwE;CAC7F,OACE,qBAAC,UAAD,EAAA,UAAA,CACE,oBAAC,cAAD,EAAA,UAAY,kCAA2C,CAAA,GACvD,oBAAC,OAAD;EACE,KAAK,MAAM;EACX,KAAI;EACJ,SAAQ;EACR,OAAO;CACR,CAAA,CACK,EAAA,CAAA;AAEZ"}
|
|
@@ -5,7 +5,7 @@ import { Alert } from "../ui/feedback.mjs";
|
|
|
5
5
|
import { StaleBaselineNotice } from "./build-diff-stale.mjs";
|
|
6
6
|
import { environmentChangeOf, labelOf, summariseEnvironmentChanges } from "./environment-change.mjs";
|
|
7
7
|
import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
8
|
-
|
|
8
|
+
// src/pages/build-diff-environment.tsx
|
|
9
9
|
/**
|
|
10
10
|
* Build-level note: some snapshots were captured in a different environment than their baseline.
|
|
11
11
|
* The default branch migrates them without a diff; a feature branch still diffs them (ADR 0005).
|
|
@@ -76,7 +76,6 @@ function ReviewNotices(props) {
|
|
|
76
76
|
migrated: build.isDefault
|
|
77
77
|
})] });
|
|
78
78
|
}
|
|
79
|
-
//#endregion
|
|
80
79
|
export { EnvironmentChangeNotice, EnvironmentChangeSnapshotNotice, ReviewNotices };
|
|
81
80
|
|
|
82
81
|
//# sourceMappingURL=build-diff-environment.mjs.map
|
|
@@ -9,7 +9,7 @@ import { a11ySummaryOf, a11yUnavailableNote } from "./a11y-violations.mjs";
|
|
|
9
9
|
import { reviewProgress } from "./build-diff-model.mjs";
|
|
10
10
|
import { headerActions, headerMain, headerMeta, headerProgress, headerSide, headerTitle, reviewHeader } from "../ui/styles/review-layout.mjs";
|
|
11
11
|
import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
12
|
-
|
|
12
|
+
// src/pages/build-diff-header.tsx
|
|
13
13
|
/** Approve-all / reject-all bulk actions (each asks for confirmation). */
|
|
14
14
|
function BulkActions({ project, build, pendingCount }) {
|
|
15
15
|
const base = `/api/v1/projects/${project.slug}/builds/${build.id}`;
|
|
@@ -129,7 +129,6 @@ function DiffHeader(props) {
|
|
|
129
129
|
})]
|
|
130
130
|
});
|
|
131
131
|
}
|
|
132
|
-
//#endregion
|
|
133
132
|
export { DiffHeader };
|
|
134
133
|
|
|
135
134
|
//# sourceMappingURL=build-diff-header.mjs.map
|