@storyshelf/app 0.10.0-next-e7aa534 → 0.10.0-next-1e4feb2

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/index.mjs CHANGED
@@ -46,7 +46,7 @@ import { resolveRuntime } from "./runtime.mjs";
46
46
  import { swaggerUI } from "@hono/swagger-ui";
47
47
  import { OpenAPIHono } from "@hono/zod-openapi";
48
48
  import { AdapterLifecycleError, adapterSourcesOf, validateBootAssembly } from "@storyshelf/core/adapter/setup";
49
- import { assertServerless, resolveShelfOptions } from "@storyshelf/core/config";
49
+ import { assertServerless, resolveShelfOptions, sequentialTransactionsWarning } from "@storyshelf/core/config";
50
50
  import { configureImages } from "@storyshelf/core/diff";
51
51
  import { STORYBOOK_TOKEN_PREFIX } from "@storyshelf/core/utils";
52
52
  import { createHttpMiddleware, createInstrumentedDatabase, createInstrumentedStorage } from "@storyshelf/observability";
@@ -87,6 +87,8 @@ function wireQueues(scoped, runtime, cell) {
87
87
  function warnOnServerless(options, logger) {
88
88
  const warning = assertServerless(options.testing?.runner !== void 0);
89
89
  if (warning) logger.warn(warning);
90
+ const sequential = sequentialTransactionsWarning(options);
91
+ if (sequential) logger.warn(sequential);
90
92
  }
91
93
  /** Attach lifecycle, queue, middleware, routes, and docs to the app. */
92
94
  function assembleApp(app, options, scoped) {
@@ -380,7 +382,7 @@ function registerDocs(app) {
380
382
  * from source (dev server, tests).
381
383
  */
382
384
  function packageVersion() {
383
- return "0.10.0-next-e7aa534";
385
+ return "0.10.0-next-1e4feb2";
384
386
  }
385
387
  export { createAuthSystemHook, createShelfApp, createShelfLogger };
386
388
 
@@ -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 {\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, true);\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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"}
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 {\n assertServerless,\n resolveShelfOptions,\n sequentialTransactionsWarning,\n} 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, true);\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 const sequential = sequentialTransactionsWarning(options);\n if (sequential) {\n logger.warn(sequential);\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgFA,SAAS,uBAAuB,SAA6B;CAC3D,MAAM,UAAU,qBAAqB,iBAAiB,OAAO,CAAC;CAC9D,IAAI,QAAQ,SAAS,GACnB,MAAM,IAAI,sBAAsB,SAAS,SAAS,IAAI;AAE1D;;AAGA,SAAS,oBAAoB,SAAqC;CAChE,OAAO;EACL,GAAG;EACH,UAAU,2BAA2B,QAAQ,QAAQ;EACrD,SAAS,0BAA0B,QAAQ,OAAO;CACpD;AACF;;AAGA,SAAS,WAAW,QAAsB,SAAwB,MAAiC;CACjG,MAAM,OAAO,UAAU,QAAQ,OAAO;CACtC,KAAK,WAAW,KAAK;CACrB,aAAa,MAAM,QAAQ,SAAS,KAAK,UAAU;CACnD,OAAO;AACT;;;;;AAMA,SAAS,iBAAiB,SAAuB,QAAsB;CACrE,MAAM,UAAU,iBAAiB,QAAQ,SAAS,WAAW,KAAA,CAAS;CACtE,IAAI,SACF,OAAO,KAAK,OAAO;CAErB,MAAM,aAAa,8BAA8B,OAAO;CACxD,IAAI,YACF,OAAO,KAAK,UAAU;AAE1B;;AAGA,SAAS,YAAY,KAAe,SAAuB,QAA4B;CACrF,MAAM,UAAU,eAAe,OAAO;CACtC,iBAAiB,SAAS,QAAQ,MAAM;CACxC,MAAM,OAAsB;EAAE,OAAO,QAAQ,QAAQ;GAAE,IAAI;GAAM,UAAU,CAAC;EAAE,CAAC;EAAG,SAAS;CAAK;CAChG,gBAAgB,KAAK,SAAS,SAAS,IAAI;CAC3C,IAAI,QAAQ,OAAO,iBAAiB,MAClC,IAAI,IAAI,KAAK,aAAa,IAAI,CAAC;CAEjC,MAAM,SAAS,WAAW,QAAQ,SAAS,IAAI;CAC/C,eAAe,KAAK;EAClB,GAAG;EACH,GAAG;EACH,SAAS;EACT,UAAU,YAAY,MAAM,KAAK;CACnC,CAAC;CACD,kBAAkB,KAAK,SAAS;EAC9B,SAAS,iBAAiB,OAAO;EACjC,kBAAkB,KAAK;EACvB,YAAY,KAAK,IAAI;EACrB,SAAS,eAAe;CAC1B,CAAC;CACD,aAAa,GAAG;AAClB;;;;;;;;AASA,SAAgB,eAAe,YAAoC;CACjE,MAAM,UAAU,oBAAoB,UAAU;CAC9C,gBAAgB;EACd,QAAQ,QAAQ,SAAS,QAAQ;EACjC,SAAS,QAAQ,SAAS,SAAS;CACrC,CAAC;CACD,uBAAuB,OAAO;CAC9B,MAAM,MAAM,IAAI,YAAyC;CACzD,YAAY,KAAK,SAAS,oBAAoB,OAAO,CAAC;CAEtD,OAAO;AACT;;AAGA,MAAM,aAAa;CACjB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;AAGA,SAAS,eAAe,KAAkB,QAAgC;CACxE,MAAM,EAAE,QAAQ,QAAQ,aAAa;CACrC,IAAI,IAAI,KAAK,UAAU,CAAC;CAIxB,IAAI,IACF,KACA,qBAAqB;EAAE,aAAa;EAAc,gBAAgB,eAAe;CAAE,CAAC,CACtF;CAIA,IAAI,IAAI,KAAK,eAAe,MAAM,CAAC;CACnC,IAAI,IAAI,KAAK,UAAU,QAAQ,CAAC;CAEhC,IAAI,IAAI,KAAK,eAAe,OAAO,QAAQ,YAAY,KAAA,CAAS,CAAC;CACjE,YAAY,KAAK,QAAQ,OAAO,OAAO;CACvC,gBAAgB,KAAK,MAAM;CAC3B,IAAI,IAAI,KAAK,SAAS,CAAC;CAEvB,IAAI,IAAI,KAAK,cAAc,CAAC;AAC9B;AAEA,MAAM,SAAS;;AAGf,MAAM,cAA4E;CAChF;EAAE,OAAO,CAAC,WAAW;EAAG,KAAK;CAAI;CACjC;EAAE,OAAO,CAAC,kBAAkB;EAAG,KAAK;CAAG;CACvC;EAAE,OAAO,CAAC,oBAAoB;EAAG,KAAK;CAAG;CAGzC;EAAE,OAAO,CAAC,SAAS;EAAG,KAAK;EAAK,QAAQ;CAAO;CAC/C;EAAE,OAAO,CAAC,aAAa;EAAG,KAAK;EAAK,QAAQ;CAAS;CACrD;EACE,OAAO,CACL,gDACA,yDACF;EACA,KAAK;EACL,QAAQ;CACV;CACA;EACE,OAAO,CAAC,wCAAwC,yCAAyC;EACzF,KAAK;EACL,QAAQ;CACV;CACA;EACE,OAAO,CACL,uCACA,gDACF;EACA,KAAK;EACL,QAAQ;CACV;AACF;;AAGA,MAAM,mCAAmC;;AAGzC,SAAS,YAAY,KAAkB,QAAqB,SAA6B;CACvF,MAAM,YAAY,kBAAkB;CAGpC,MAAM,aAAa;EACjB,OAAO,CAAC,GAAG,uBAAuB,EAAE;EACpC,KAAK,QAAQ,SAAS,yBAAyB;EAC/C,QAAQ;CACV;CACA,KAAK,MAAM,EAAE,OAAO,KAAK,YAAY,CAAC,GAAG,aAAa,UAAU,GAAG;EACjE,MAAM,eAAe,SAAS,aAAa,MAAM,IAAI,KAAA;EACrD,KAAK,MAAM,QAAQ,OACjB,IAAI,IAAI,MAAM,UAAU;GAAE,UAAU;GAAQ;GAAK;EAAa,CAAC,CAAC;CAEpE;CACA,KAAK,MAAM,QAAQ,YACjB,IAAI,IAAI,MAAM,KAAK,OAAO,MAAM,CAAC;AAErC;;AAGA,SAAS,gBAAgB,KAAkB,QAAgC;CACzE,MAAM,EACJ,SACA,QACA,IACA,QACA,aACA,gBACA,cACA,cACA,SACA,iBACA,YACA,QACA,WACA,iBACE;CACJ,IAAI,IACF,KACA,WAAW;EACT,IAAI,QAAQ;EACZ,SAAS,QAAQ;EACjB;EACA;EACA;EACA;EACA,SAAS,QAAQ,YAAY,KAAA;EAC7B;EACA;EACA;EACA;EACA;EACA;EACA,oBAAoB,QAAQ,SAAS;EACrC,QAAQ,QAAQ,SAAS;EACzB;EACA;EACA;EACA,qBAAqB,QAAQ,eAAe;EAC5C,IAAI,QAAQ;EACZ,aAAa,OAAO,MAAM,MAAM,mBAAmB,GAAG,QAAQ,IAAI;CACpE,CAAC,CACH;AACF;;AAGA,SAAS,kBAAkB,KAAkB,QAA0B;CACrE,sBAAsB,GAAG;CACzB,2BAA2B,GAAG;CAC9B,eAAe,KAAK,MAAM;CAC1B,cAAc,GAAG;AACnB;;AAGA,SAAS,sBAAsB,KAAwB;CACrD,iBAAiB,GAAG;CACpB,eAAe,GAAG;CAClB,eAAe,GAAG;CAClB,cAAc,GAAG;CACjB,kBAAkB,GAAG;CACrB,cAAc,GAAG;CACjB,gBAAgB,GAAG;CACnB,eAAe,GAAG;CAClB,iBAAiB,GAAG;CACpB,iBAAiB,GAAG;AACtB;;AAGA,SAAS,2BAA2B,KAAwB;CAC1D,6BAA6B,GAAG;CAChC,kCAAkC,GAAG;CACrC,uBAAuB,GAAG;AAC5B;;AAGA,SAAS,iBAAiB,KAAkB,SAA6B;CAIvE,IAAI,CAAC,QAAQ,MACX;CAEF,mBAAmB,KAAK,QAAQ,IAAI;CACpC,kBAAkB,KAAK,QAAQ,IAAI;CACnC,gBAAgB,KAAK,QAAQ,IAAI;CACjC,oBAAoB,GAAG;AACzB;;AAGA,SAAS,mBAAmB,KAAkB,SAAuB,QAA0B;CAC7F,iBAAiB,KAAK,OAAO;CAC7B,eAAe,GAAG;CAClB,kBAAkB,GAAG;CACrB,wBAAwB,GAAG;CAC3B,qBAAqB,GAAG;CACxB,mBAAmB,KAAK,QAAQ,QAAQ,IAAI;CAC5C,2BAA2B,GAAG;CAC9B,gBAAgB,GAAG;CACnB,oBAAoB,GAAG;CACvB,yBAAyB,GAAG;AAC9B;;AAGA,SAAS,kBAAkB,KAAkB,SAAuB,QAA0B;CAC5F,kBAAkB,KAAK,MAAM;CAC7B,mBAAmB,KAAK,SAAS,MAAM;AACzC;;AAGA,MAAM,iBAAiB;CACrB,SAAS;CACT,MAAM;EACJ,OAAO;EACP,aACE;EACF,SAAS;CACX;CACA,MAAM;EACJ;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aACE;EACJ;EACA;GAAE,MAAM;GAAU,aAAa;EAA+C;EAC9E;GACE,MAAM;GACN,aACE;EACJ;EACA;GAAE,MAAM;GAAa,aAAa;EAAiD;EACnF;GACE,MAAM;GACN,aAAa;EACf;EACA;GACE,MAAM;GACN,aAAa;EACf;EACA;GAAE,MAAM;GAAU,aAAa;EAAqC;EACpE;GAAE,MAAM;GAAY,aAAa;EAA8C;EAC/E;GACE,MAAM;GACN,aAAa;EACf;EACA;GAAE,MAAM;GAAS,aAAa;EAA4B;CAC5D;AACF;;AAGA,SAAS,aAAa,KAAwB;CAG5C,IAAI,IAAI,yBAAyB,MAC/B,EAAE,KACA,iBACE,IAAI,mBAAmB;EAAE,GAAG;EAAgB,MAAM,CAAC,GAAG,eAAe,IAAI;CAAE,CAAC,CAC9E,CACF,CACF;CACA,IAAI,IAAI,gBAAgB,UAAU,EAAE,KAAK,uBAAuB,CAAC,CAAC;AACpE;;;;;;AASA,SAAS,iBAAyB;CAChC,OAAA;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storyshelf/app",
3
- "version": "0.10.0-next-e7aa534",
3
+ "version": "0.10.0-next-1e4feb2",
4
4
  "description": "StoryShelf app: Hono app, API routes, and server-rendered UI over @storyshelf/core.",
5
5
  "keywords": [
6
6
  "chromatic-alternative",
@@ -61,8 +61,8 @@
61
61
  "dependencies": {
62
62
  "@hono/swagger-ui": "^0.6.1",
63
63
  "@hono/zod-openapi": "^1.1.5",
64
- "@storyshelf/core": "0.10.0-next-e7aa534",
65
- "@storyshelf/observability": "0.10.0-next-e7aa534",
64
+ "@storyshelf/core": "0.10.0-next-1e4feb2",
65
+ "@storyshelf/observability": "0.10.0-next-1e4feb2",
66
66
  "hono": "^4.11.0",
67
67
  "zod": "^4.6.7"
68
68
  },
@@ -72,9 +72,9 @@
72
72
  "@opentelemetry/core": "^2.11.0",
73
73
  "@opentelemetry/sdk-trace-base": "^2.11.0",
74
74
  "@storybook/html-vite": "10.6.5",
75
- "@storyshelf/auth": "0.10.0-next-e7aa534",
76
- "@storyshelf/db-sqlite": "0.10.0-next-e7aa534",
77
- "@storyshelf/design-figma": "0.10.0-next-e7aa534",
75
+ "@storyshelf/auth": "0.10.0-next-1e4feb2",
76
+ "@storyshelf/db-sqlite": "0.10.0-next-1e4feb2",
77
+ "@storyshelf/design-figma": "0.10.0-next-1e4feb2",
78
78
  "@types/node": "^26.0.0",
79
79
  "@vitest/coverage-v8": "^4.0.0",
80
80
  "adm-zip": "0.6.1",