@storyshelf/app 0.6.2 → 0.7.0

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