@storyshelf/core 0.3.2 → 0.5.3

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 (393) hide show
  1. package/README.md +38 -31
  2. package/dist/adapter/capture-queue.d.mts +29 -1
  3. package/dist/adapter/capture-runner.d.mts +18 -2
  4. package/dist/adapter/capture-runner.mjs +12 -1
  5. package/dist/adapter/capture-runner.mjs.map +1 -0
  6. package/dist/adapter/database.d.mts +67 -17
  7. package/dist/adapter/database.mjs +22 -55
  8. package/dist/adapter/database.mjs.map +1 -1
  9. package/dist/adapter/email-sender.d.mts +27 -0
  10. package/dist/adapter/email-sender.mjs +4 -0
  11. package/dist/adapter/metadata.d.mts +21 -15
  12. package/dist/adapter/notifier.d.mts +7 -0
  13. package/dist/adapter/notifier.mjs +6 -0
  14. package/dist/adapter/setup.d.mts +61 -0
  15. package/dist/adapter/setup.mjs +29 -0
  16. package/dist/adapter/setup.mjs.map +1 -0
  17. package/dist/adapter/webhook-events.d.mts +29 -0
  18. package/dist/adapter/webhook-events.mjs +83 -0
  19. package/dist/adapter/webhook-events.mjs.map +1 -0
  20. package/dist/adapters/notifier/events.d.mts +26 -0
  21. package/dist/adapters/notifier/events.mjs +66 -0
  22. package/dist/adapters/notifier/events.mjs.map +1 -0
  23. package/dist/adapters/notifier/format.d.mts +14 -0
  24. package/dist/adapters/notifier/format.mjs +96 -0
  25. package/dist/adapters/notifier/format.mjs.map +1 -0
  26. package/dist/adapters/notifier/provider.d.mts +32 -0
  27. package/dist/adapters/notifier/types.d.mts +56 -0
  28. package/dist/adapters/setup-runner.d.mts +23 -0
  29. package/dist/adapters/setup-runner.mjs +149 -0
  30. package/dist/adapters/setup-runner.mjs.map +1 -0
  31. package/dist/adapters/validate.d.mts +30 -0
  32. package/dist/adapters/validate.mjs +313 -0
  33. package/dist/adapters/validate.mjs.map +1 -0
  34. package/dist/auth.d.mts +94 -0
  35. package/dist/auth.mjs +4 -0
  36. package/dist/brand-packs.d.mts +16 -0
  37. package/dist/brand-packs.mjs +130 -0
  38. package/dist/brand-packs.mjs.map +1 -0
  39. package/dist/capture/adapter.d.mts +51 -1
  40. package/dist/capture/adapter.mjs.map +1 -1
  41. package/dist/capture/affected.d.mts +16 -0
  42. package/dist/capture/affected.mjs +64 -0
  43. package/dist/capture/affected.mjs.map +1 -0
  44. package/dist/capture/diff-record.mjs +30 -0
  45. package/dist/capture/diff-record.mjs.map +1 -0
  46. package/dist/capture/dispatch.d.mts +36 -0
  47. package/dist/capture/dispatch.mjs +88 -31
  48. package/dist/capture/dispatch.mjs.map +1 -1
  49. package/dist/capture/guards.mjs +31 -0
  50. package/dist/capture/guards.mjs.map +1 -0
  51. package/dist/capture/orchestrator.d.mts +41 -6
  52. package/dist/capture/orchestrator.mjs +151 -28
  53. package/dist/capture/orchestrator.mjs.map +1 -1
  54. package/dist/capture/pipeline.d.mts +30 -16
  55. package/dist/capture/pipeline.mjs +143 -39
  56. package/dist/capture/pipeline.mjs.map +1 -1
  57. package/dist/capture/recovery.d.mts +21 -0
  58. package/dist/capture/recovery.mjs +83 -0
  59. package/dist/capture/recovery.mjs.map +1 -0
  60. package/dist/capture/rediff.d.mts +35 -0
  61. package/dist/capture/rediff.mjs +77 -0
  62. package/dist/capture/rediff.mjs.map +1 -0
  63. package/dist/capture/sizing.d.mts +58 -0
  64. package/dist/capture/sizing.mjs +119 -0
  65. package/dist/capture/sizing.mjs.map +1 -0
  66. package/dist/capture/skip-checks.mjs +3 -3
  67. package/dist/capture/skip-checks.mjs.map +1 -1
  68. package/dist/capture/statics.d.mts +38 -0
  69. package/dist/capture/statics.mjs +38 -4
  70. package/dist/capture/statics.mjs.map +1 -1
  71. package/dist/capture/status-fanout.mjs +7 -2
  72. package/dist/capture/status-fanout.mjs.map +1 -1
  73. package/dist/capture/storybook.d.mts +31 -2
  74. package/dist/capture/storybook.mjs +77 -2
  75. package/dist/capture/storybook.mjs.map +1 -1
  76. package/dist/capture.d.mts +10 -4
  77. package/dist/capture.mjs +7 -2
  78. package/dist/config.d.mts +121 -4
  79. package/dist/config.mjs +55 -8
  80. package/dist/config.mjs.map +1 -1
  81. package/dist/index.d.mts +5 -56
  82. package/dist/index.mjs +4 -284
  83. package/dist/logger.d.mts +26 -17
  84. package/dist/logger.mjs +10 -5
  85. package/dist/logger.mjs.map +1 -1
  86. package/dist/models/baseline-status.d.mts +29 -0
  87. package/dist/models/baseline-status.mjs +26 -0
  88. package/dist/models/baseline-status.mjs.map +1 -0
  89. package/dist/models/baseline.d.mts +33 -0
  90. package/dist/models/baseline.mjs +35 -13
  91. package/dist/models/baseline.mjs.map +1 -1
  92. package/dist/models/build.d.mts +87 -0
  93. package/dist/models/build.mjs +83 -35
  94. package/dist/models/build.mjs.map +1 -1
  95. package/dist/models/capture-attempt.d.mts +40 -0
  96. package/dist/models/capture-attempt.mjs +85 -0
  97. package/dist/models/capture-attempt.mjs.map +1 -0
  98. package/dist/models/capture-log.d.mts +21 -0
  99. package/dist/models/capture-log.mjs +44 -0
  100. package/dist/models/capture-log.mjs.map +1 -0
  101. package/dist/models/comment.d.mts +29 -0
  102. package/dist/models/comment.mjs +11 -8
  103. package/dist/models/comment.mjs.map +1 -1
  104. package/dist/models/content-ref.d.mts +21 -0
  105. package/dist/models/content-ref.mjs +49 -0
  106. package/dist/models/content-ref.mjs.map +1 -0
  107. package/dist/models/credentials.d.mts +36 -0
  108. package/dist/models/credentials.mjs +103 -0
  109. package/dist/models/credentials.mjs.map +1 -0
  110. package/dist/models/label.d.mts +58 -0
  111. package/dist/models/label.mjs +32 -27
  112. package/dist/models/label.mjs.map +1 -1
  113. package/dist/models/member.d.mts +34 -0
  114. package/dist/models/member.mjs +20 -11
  115. package/dist/models/member.mjs.map +1 -1
  116. package/dist/models/notification-channel.d.mts +52 -0
  117. package/dist/models/notification-channel.mjs +93 -0
  118. package/dist/models/notification-channel.mjs.map +1 -0
  119. package/dist/models/notification-subscription.d.mts +44 -0
  120. package/dist/models/notification-subscription.mjs +89 -0
  121. package/dist/models/notification-subscription.mjs.map +1 -0
  122. package/dist/models/project-group-mapping.d.mts +30 -0
  123. package/dist/models/project-group-mapping.mjs +46 -0
  124. package/dist/models/project-group-mapping.mjs.map +1 -0
  125. package/dist/models/project.d.mts +54 -0
  126. package/dist/models/project.mjs +15 -11
  127. package/dist/models/project.mjs.map +1 -1
  128. package/dist/models/snapshot.d.mts +58 -0
  129. package/dist/models/snapshot.mjs +13 -7
  130. package/dist/models/snapshot.mjs.map +1 -1
  131. package/dist/models/status-config.d.mts +38 -0
  132. package/dist/models/status-config.mjs +12 -11
  133. package/dist/models/status-config.mjs.map +1 -1
  134. package/dist/models/token.d.mts +44 -0
  135. package/dist/models/token.mjs +18 -15
  136. package/dist/models/token.mjs.map +1 -1
  137. package/dist/models/user.d.mts +42 -0
  138. package/dist/models/user.mjs +65 -0
  139. package/dist/models/user.mjs.map +1 -0
  140. package/dist/models/webhook.d.mts +50 -0
  141. package/dist/models/webhook.mjs +12 -10
  142. package/dist/models/webhook.mjs.map +1 -1
  143. package/dist/models.d.mts +22 -0
  144. package/dist/models.mjs +23 -0
  145. package/dist/paths.mjs +29 -0
  146. package/dist/paths.mjs.map +1 -1
  147. package/dist/retention.d.mts +45 -0
  148. package/dist/retention.mjs +145 -0
  149. package/dist/retention.mjs.map +1 -0
  150. package/dist/schema/baseline.d.mts +1 -0
  151. package/dist/schema/build.d.mts +8 -0
  152. package/dist/schema/capture-attempt.d.mts +38 -0
  153. package/dist/schema/comment.d.mts +1 -1
  154. package/dist/schema/content-ref.d.mts +13 -0
  155. package/dist/schema/member.d.mts +2 -0
  156. package/dist/schema/notification.d.mts +35 -0
  157. package/dist/schema/project-group-mapping.d.mts +15 -0
  158. package/dist/schema/project.d.mts +4 -0
  159. package/dist/schema/snapshot.d.mts +7 -0
  160. package/dist/schema/token.d.mts +2 -0
  161. package/dist/schema/user.d.mts +11 -1
  162. package/dist/schema.d.mts +9 -46
  163. package/dist/schema.mjs +1 -55
  164. package/dist/test-helpers/create-project.d.mts +15 -0
  165. package/dist/test-helpers/create-project.mjs +28 -0
  166. package/dist/test-helpers/create-project.mjs.map +1 -0
  167. package/dist/test-helpers/database-contract.d.mts +13 -0
  168. package/dist/test-helpers/database-contract.mjs +58 -0
  169. package/dist/test-helpers/database-contract.mjs.map +1 -0
  170. package/dist/test-helpers/fake-adapters.d.mts +14 -0
  171. package/dist/test-helpers/fake-adapters.mjs +20 -0
  172. package/dist/test-helpers/fake-adapters.mjs.map +1 -0
  173. package/dist/test-helpers/fake-database.d.mts +11 -0
  174. package/dist/test-helpers/fake-database.mjs +104 -0
  175. package/dist/test-helpers/fake-database.mjs.map +1 -0
  176. package/dist/test-helpers/fake-storage.d.mts +14 -0
  177. package/dist/test-helpers/fake-storage.mjs +49 -0
  178. package/dist/test-helpers/fake-storage.mjs.map +1 -0
  179. package/dist/test-helpers/fake-tables.mjs +276 -0
  180. package/dist/test-helpers/fake-tables.mjs.map +1 -0
  181. package/dist/test-helpers/git-contract.d.mts +11 -0
  182. package/dist/test-helpers/git-contract.mjs +34 -0
  183. package/dist/test-helpers/git-contract.mjs.map +1 -0
  184. package/dist/test-helpers/notifier-contract.d.mts +11 -0
  185. package/dist/test-helpers/notifier-contract.mjs +34 -0
  186. package/dist/test-helpers/notifier-contract.mjs.map +1 -0
  187. package/dist/test-helpers/queue-contract.d.mts +11 -0
  188. package/dist/test-helpers/queue-contract.mjs +64 -0
  189. package/dist/test-helpers/queue-contract.mjs.map +1 -0
  190. package/dist/test-helpers/runner-contract.d.mts +11 -0
  191. package/dist/test-helpers/runner-contract.mjs +32 -0
  192. package/dist/test-helpers/runner-contract.mjs.map +1 -0
  193. package/dist/test-helpers/sql-chunks.mjs +138 -0
  194. package/dist/test-helpers/sql-chunks.mjs.map +1 -0
  195. package/dist/test-helpers/storage-contract.d.mts +11 -0
  196. package/dist/test-helpers/storage-contract.mjs +87 -0
  197. package/dist/test-helpers/storage-contract.mjs.map +1 -0
  198. package/dist/test-helpers.d.mts +13 -0
  199. package/dist/test-helpers.mjs +14 -0
  200. package/dist/tracing.mjs +63 -0
  201. package/dist/tracing.mjs.map +1 -0
  202. package/dist/types.d.mts +38 -5
  203. package/dist/types.mjs +18 -5
  204. package/dist/types.mjs.map +1 -1
  205. package/dist/urls.d.mts +12 -1
  206. package/dist/urls.mjs +22 -3
  207. package/dist/urls.mjs.map +1 -1
  208. package/dist/utils/encrypt.d.mts +48 -0
  209. package/dist/utils/encrypt.mjs +67 -13
  210. package/dist/utils/encrypt.mjs.map +1 -1
  211. package/dist/utils/hash.d.mts +17 -0
  212. package/dist/utils/hash.mjs +8 -2
  213. package/dist/utils/hash.mjs.map +1 -1
  214. package/dist/utils/http.d.mts +58 -0
  215. package/dist/utils/http.mjs +147 -0
  216. package/dist/utils/http.mjs.map +1 -0
  217. package/dist/utils/mime.d.mts +9 -0
  218. package/dist/utils/mime.mjs +33 -0
  219. package/dist/utils/mime.mjs.map +1 -0
  220. package/dist/utils/path-security.d.mts +12 -0
  221. package/dist/utils/path-security.mjs +24 -0
  222. package/dist/utils/path-security.mjs.map +1 -0
  223. package/dist/utils/redact.d.mts +17 -0
  224. package/dist/utils/redact.mjs +42 -0
  225. package/dist/utils/redact.mjs.map +1 -0
  226. package/dist/utils/timing.d.mts +19 -0
  227. package/dist/utils/timing.mjs +46 -0
  228. package/dist/utils/timing.mjs.map +1 -0
  229. package/dist/utils/ulid.d.mts +10 -0
  230. package/dist/utils/ulid.mjs +8 -8
  231. package/dist/utils/ulid.mjs.map +1 -1
  232. package/dist/utils.d.mts +12 -0
  233. package/dist/utils.mjs +13 -0
  234. package/package.json +69 -28
  235. package/dist/adapter/auth.d.mts +0 -50
  236. package/dist/adapter/auth.mjs +0 -10
  237. package/dist/adapter/auth.mjs.map +0 -1
  238. package/dist/adapter/init.d.mts +0 -58
  239. package/dist/adapter/init.mjs +0 -93
  240. package/dist/adapter/init.mjs.map +0 -1
  241. package/dist/adapters/webhook-events.mjs +0 -55
  242. package/dist/adapters/webhook-events.mjs.map +0 -1
  243. package/dist/ddl.d.mts +0 -8
  244. package/dist/ddl.mjs +0 -158
  245. package/dist/ddl.mjs.map +0 -1
  246. package/dist/index.mjs.map +0 -1
  247. package/dist/middleware/auth-gate.mjs +0 -36
  248. package/dist/middleware/auth-gate.mjs.map +0 -1
  249. package/dist/middleware/csrf.mjs +0 -40
  250. package/dist/middleware/csrf.mjs.map +0 -1
  251. package/dist/middleware/rate-limit.mjs +0 -39
  252. package/dist/middleware/rate-limit.mjs.map +0 -1
  253. package/dist/middleware/request-log.mjs +0 -28
  254. package/dist/middleware/request-log.mjs.map +0 -1
  255. package/dist/middleware/store-scope.mjs +0 -29
  256. package/dist/middleware/store-scope.mjs.map +0 -1
  257. package/dist/pages/build-detail.mjs +0 -339
  258. package/dist/pages/build-detail.mjs.map +0 -1
  259. package/dist/pages/build-diff-comments.mjs +0 -113
  260. package/dist/pages/build-diff-comments.mjs.map +0 -1
  261. package/dist/pages/build-diff-header.mjs +0 -163
  262. package/dist/pages/build-diff-header.mjs.map +0 -1
  263. package/dist/pages/build-diff-nav.mjs +0 -66
  264. package/dist/pages/build-diff-nav.mjs.map +0 -1
  265. package/dist/pages/build-diff-viewer.mjs +0 -197
  266. package/dist/pages/build-diff-viewer.mjs.map +0 -1
  267. package/dist/pages/build-diff.mjs +0 -90
  268. package/dist/pages/build-diff.mjs.map +0 -1
  269. package/dist/pages/compute-jobs.mjs +0 -198
  270. package/dist/pages/compute-jobs.mjs.map +0 -1
  271. package/dist/pages/label-detail.mjs +0 -228
  272. package/dist/pages/label-detail.mjs.map +0 -1
  273. package/dist/pages/login.mjs +0 -78
  274. package/dist/pages/login.mjs.map +0 -1
  275. package/dist/pages/project-builds.mjs +0 -231
  276. package/dist/pages/project-builds.mjs.map +0 -1
  277. package/dist/pages/project-create.mjs +0 -169
  278. package/dist/pages/project-create.mjs.map +0 -1
  279. package/dist/pages/project-settings.mjs +0 -102
  280. package/dist/pages/project-settings.mjs.map +0 -1
  281. package/dist/pages/projects.mjs +0 -184
  282. package/dist/pages/projects.mjs.map +0 -1
  283. package/dist/pages/root.mjs +0 -94
  284. package/dist/pages/root.mjs.map +0 -1
  285. package/dist/pages/settings-general.mjs +0 -192
  286. package/dist/pages/settings-general.mjs.map +0 -1
  287. package/dist/pages/settings-labels.mjs +0 -150
  288. package/dist/pages/settings-labels.mjs.map +0 -1
  289. package/dist/pages/settings-members.mjs +0 -125
  290. package/dist/pages/settings-members.mjs.map +0 -1
  291. package/dist/pages/settings-status.mjs +0 -152
  292. package/dist/pages/settings-status.mjs.map +0 -1
  293. package/dist/pages/settings-tests.mjs +0 -149
  294. package/dist/pages/settings-tests.mjs.map +0 -1
  295. package/dist/pages/settings-tokens.mjs +0 -96
  296. package/dist/pages/settings-tokens.mjs.map +0 -1
  297. package/dist/pages/settings-webhooks.mjs +0 -150
  298. package/dist/pages/settings-webhooks.mjs.map +0 -1
  299. package/dist/pages/storybook.mjs +0 -96
  300. package/dist/pages/storybook.mjs.map +0 -1
  301. package/dist/retention/purge.mjs +0 -70
  302. package/dist/retention/purge.mjs.map +0 -1
  303. package/dist/routers/admin.mjs +0 -42
  304. package/dist/routers/admin.mjs.map +0 -1
  305. package/dist/routers/assets.mjs +0 -25
  306. package/dist/routers/assets.mjs.map +0 -1
  307. package/dist/routers/auth.mjs +0 -78
  308. package/dist/routers/auth.mjs.map +0 -1
  309. package/dist/routers/builds.handlers.mjs +0 -184
  310. package/dist/routers/builds.handlers.mjs.map +0 -1
  311. package/dist/routers/builds.mjs +0 -185
  312. package/dist/routers/builds.mjs.map +0 -1
  313. package/dist/routers/comments.mjs +0 -90
  314. package/dist/routers/comments.mjs.map +0 -1
  315. package/dist/routers/health.mjs +0 -137
  316. package/dist/routers/health.mjs.map +0 -1
  317. package/dist/routers/helpers.mjs +0 -82
  318. package/dist/routers/helpers.mjs.map +0 -1
  319. package/dist/routers/htmx.mjs +0 -20
  320. package/dist/routers/htmx.mjs.map +0 -1
  321. package/dist/routers/labels.mjs +0 -110
  322. package/dist/routers/labels.mjs.map +0 -1
  323. package/dist/routers/media.mjs +0 -55
  324. package/dist/routers/media.mjs.map +0 -1
  325. package/dist/routers/members.mjs +0 -102
  326. package/dist/routers/members.mjs.map +0 -1
  327. package/dist/routers/projects.mjs +0 -122
  328. package/dist/routers/projects.mjs.map +0 -1
  329. package/dist/routers/schemas.mjs +0 -224
  330. package/dist/routers/schemas.mjs.map +0 -1
  331. package/dist/routers/settings-general.mjs +0 -95
  332. package/dist/routers/settings-general.mjs.map +0 -1
  333. package/dist/routers/settings-labels.mjs +0 -49
  334. package/dist/routers/settings-labels.mjs.map +0 -1
  335. package/dist/routers/settings-members.mjs +0 -30
  336. package/dist/routers/settings-members.mjs.map +0 -1
  337. package/dist/routers/settings-status.mjs +0 -73
  338. package/dist/routers/settings-status.mjs.map +0 -1
  339. package/dist/routers/settings-tokens.mjs +0 -40
  340. package/dist/routers/settings-tokens.mjs.map +0 -1
  341. package/dist/routers/settings-webhooks.mjs +0 -63
  342. package/dist/routers/settings-webhooks.mjs.map +0 -1
  343. package/dist/routers/settings.handlers.mjs +0 -93
  344. package/dist/routers/settings.handlers.mjs.map +0 -1
  345. package/dist/routers/settings.mjs +0 -23
  346. package/dist/routers/settings.mjs.map +0 -1
  347. package/dist/routers/snapshots.mjs +0 -144
  348. package/dist/routers/snapshots.mjs.map +0 -1
  349. package/dist/routers/status-configs.mjs +0 -101
  350. package/dist/routers/status-configs.mjs.map +0 -1
  351. package/dist/routers/storybook.mjs +0 -120
  352. package/dist/routers/storybook.mjs.map +0 -1
  353. package/dist/routers/tokens.mjs +0 -81
  354. package/dist/routers/tokens.mjs.map +0 -1
  355. package/dist/routers/ui.mjs +0 -165
  356. package/dist/routers/ui.mjs.map +0 -1
  357. package/dist/routers/webhooks.mjs +0 -90
  358. package/dist/routers/webhooks.mjs.map +0 -1
  359. package/dist/schema/baseline.mjs +0 -22
  360. package/dist/schema/baseline.mjs.map +0 -1
  361. package/dist/schema/build.mjs +0 -29
  362. package/dist/schema/build.mjs.map +0 -1
  363. package/dist/schema/comment.mjs +0 -26
  364. package/dist/schema/comment.mjs.map +0 -1
  365. package/dist/schema/label.mjs +0 -30
  366. package/dist/schema/label.mjs.map +0 -1
  367. package/dist/schema/member.mjs +0 -19
  368. package/dist/schema/member.mjs.map +0 -1
  369. package/dist/schema/project.mjs +0 -25
  370. package/dist/schema/project.mjs.map +0 -1
  371. package/dist/schema/snapshot.mjs +0 -34
  372. package/dist/schema/snapshot.mjs.map +0 -1
  373. package/dist/schema/status-config.mjs +0 -20
  374. package/dist/schema/status-config.mjs.map +0 -1
  375. package/dist/schema/token.mjs +0 -19
  376. package/dist/schema/token.mjs.map +0 -1
  377. package/dist/schema/user.mjs +0 -19
  378. package/dist/schema/user.mjs.map +0 -1
  379. package/dist/schema/webhook.mjs +0 -20
  380. package/dist/schema/webhook.mjs.map +0 -1
  381. package/dist/schema.mjs.map +0 -1
  382. package/dist/store.mjs +0 -20
  383. package/dist/store.mjs.map +0 -1
  384. package/dist/ui/document.mjs +0 -348
  385. package/dist/ui/document.mjs.map +0 -1
  386. package/dist/ui/feedback.mjs +0 -24
  387. package/dist/ui/feedback.mjs.map +0 -1
  388. package/dist/ui/forms.mjs +0 -117
  389. package/dist/ui/forms.mjs.map +0 -1
  390. package/dist/ui/styles.mjs +0 -174
  391. package/dist/ui/styles.mjs.map +0 -1
  392. package/dist/ui/theme.mjs +0 -44
  393. package/dist/ui/theme.mjs.map +0 -1
@@ -11,7 +11,7 @@ import { z } from "zod";
11
11
  * `category` tells a reader which slot the adapter fills without knowing
12
12
  * which adapter instance supplied the metadata.
13
13
  */
14
- type AdapterCategory = "database" | "storage" | "auth" | "capture-runner" | "capture-queue" | "git-host";
14
+ type AdapterCategory = "database" | "storage" | "capture-runner" | "capture-queue" | "git-host" | "notifier";
15
15
  /** Common adapter identity — every adapter instance exposes this. */
16
16
  interface AdapterMetadata {
17
17
  /** Human label for UI/logs. */
@@ -31,39 +31,39 @@ interface GitAdapterMetadata extends AdapterMetadata {
31
31
  readonly logo?: string;
32
32
  readonly schema: z.ZodType;
33
33
  }
34
- /** Context passed to an adapter's `init` on server startup. */
35
- interface AdapterInitContext {
34
+ /** Context passed to an adapter's `setup` on server startup. */
35
+ interface AdapterSetupContext {
36
36
  /** Resolved shelf configuration (scratchDir, secret, …). Never log the secret. */
37
37
  readonly config: ShelfConfig;
38
- /** Scoped logger for init-time diagnostics. */
38
+ /** Scoped logger for setup-time diagnostics. */
39
39
  readonly logger: Logger;
40
40
  }
41
41
  /** Result of an adapter's ongoing health probe. */
42
42
  interface AdapterHealth {
43
43
  /** Whether the adapter is currently usable. */
44
44
  readonly ok: boolean;
45
- /** Probe latency in milliseconds, when measured. */
46
- readonly latencyMs?: number;
47
45
  /** Short human-readable detail (must not contain secrets). */
48
46
  readonly detail?: string;
49
47
  }
50
48
  /**
51
- * Optional lifecycle every adapter can expose.
49
+ * Optional lifecycle every adapter can expose — all or nothing.
52
50
  *
53
- * Grouped in a sub-object (rather than flattened onto each adapter) so
54
- * future hooks (`drain?`, `reinit?`, …) land in one place. All hooks must
55
- * be idempotent — the router may run them more than once per process.
51
+ * An adapter either omits `lifecycle` entirely or implements all three
52
+ * hooks; partial implementations are not allowed. Grouped in a sub-object
53
+ * (rather than flattened onto each adapter) so future hooks land in one
54
+ * place. All hooks must be idempotent — the router may run them more than
55
+ * once per process.
56
56
  */
57
57
  interface AdapterLifecycle {
58
58
  /**
59
59
  * One-shot setup: migrations, directory creation, credential validation.
60
60
  * Throw when something required is missing.
61
61
  */
62
- init?: (ctx: AdapterInitContext) => Promise<void>;
63
- /** Teardown: close clients and handles. Must tolerate repeated calls. */
64
- close?: () => Promise<void>;
62
+ setup: (ctx: AdapterSetupContext) => Promise<void>;
63
+ /** Teardown: destroy clients and handles. Must tolerate repeated calls. */
64
+ teardown: () => Promise<void>;
65
65
  /** Ongoing liveness probe (cheap: SELECT 1, access(), head-bucket). */
66
- health?: () => Promise<AdapterHealth>;
66
+ health: () => Promise<AdapterHealth>;
67
67
  }
68
68
  /**
69
69
  * Shared adapter base.
@@ -77,7 +77,13 @@ interface Adapter<Extra extends object = Record<string, unknown>> {
77
77
  readonly metadata: AdapterMetadata & Extra;
78
78
  /** Optional lifecycle hooks. */
79
79
  readonly lifecycle?: AdapterLifecycle;
80
+ /**
81
+ * Bind the host-owned runtime logger. Called by the app/worker when
82
+ * present; factories must never create their own loggers — they only
83
+ * store what they are given (explicit `options.logger` wins over bound).
84
+ */
85
+ setLogger?(logger: Logger): void;
80
86
  }
81
87
  //#endregion
82
- export { Adapter, AdapterCategory, AdapterHealth, AdapterInitContext, AdapterLifecycle, AdapterMetadata, GitAdapterMetadata };
88
+ export { Adapter, AdapterCategory, AdapterHealth, AdapterLifecycle, AdapterMetadata, AdapterSetupContext, GitAdapterMetadata };
83
89
  //# sourceMappingURL=metadata.d.mts.map
@@ -0,0 +1,7 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { ChannelToggles, FormattedNotification, NotificationBrand, NotificationChannel, NotificationEvent, SystemEventName, SystemNotification } from "../adapters/notifier/types.mjs";
4
+ import { NotifierAdapter, NotifierMetadata, NotifierProvider } from "../adapters/notifier/provider.mjs";
5
+ import { emitNotifications, emitSystemNotification } from "../adapters/notifier/events.mjs";
6
+ import { formatNotification, formatSystemNotification } from "../adapters/notifier/format.mjs";
7
+ export { type ChannelToggles, type FormattedNotification, type NotificationBrand, type NotificationChannel, type NotificationEvent, type NotifierAdapter, type NotifierMetadata, type NotifierProvider, type SystemEventName, type SystemNotification, emitNotifications, emitSystemNotification, formatNotification, formatSystemNotification };
@@ -0,0 +1,6 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { formatNotification, formatSystemNotification } from "../adapters/notifier/format.mjs";
5
+ import { emitNotifications, emitSystemNotification } from "../adapters/notifier/events.mjs";
6
+ export { emitNotifications, emitSystemNotification, formatNotification, formatSystemNotification };
@@ -0,0 +1,61 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { CaptureRunner } from "./capture-runner.mjs";
4
+ import { DatabaseAdapter } from "./database.mjs";
5
+ import { EmailSender } from "./email-sender.mjs";
6
+ import { GitHostProvider } from "./git-host.mjs";
7
+ import { NotifierProvider } from "../adapters/notifier/provider.mjs";
8
+ import { StorageAdapter } from "./storage.mjs";
9
+ import { AdapterSetupContext } from "./metadata.mjs";
10
+ import { CaptureQueue } from "./capture-queue.mjs";
11
+ import { bindAdapterLoggers, collectSetups, collectTeardowns, runAdapterSetups, runAdapterTeardowns } from "../adapters/setup-runner.mjs";
12
+ import { validateAdapter, validateAdapterSources, validateAuth, validateBootAssembly } from "../adapters/validate.mjs";
13
+ //#region src/adapters/setup.d.ts
14
+ /** Adapters that can take part in lifecycle runs. */
15
+ interface AdapterSetupSources {
16
+ database: DatabaseAdapter;
17
+ storage: StorageAdapter;
18
+ captureRunner?: CaptureRunner;
19
+ captureQueue?: CaptureQueue;
20
+ gitHosts?: GitHostProvider[];
21
+ notifiers?: NotifierProvider[];
22
+ emailSender?: EmailSender;
23
+ }
24
+ /** One collected hook bound to its adapter identity. */
25
+ interface HookEntry {
26
+ readonly category: string;
27
+ readonly kind: string;
28
+ readonly name: string;
29
+ readonly run: (ctx: AdapterSetupContext) => Promise<void>;
30
+ }
31
+ /** One failed hook, with its adapter identity attached. */
32
+ interface AdapterSetupFailure {
33
+ readonly category: string;
34
+ readonly kind: string;
35
+ readonly name: string;
36
+ readonly error: string;
37
+ }
38
+ /** Settled outcome of a lifecycle run (never rejects — use `ok`). */
39
+ interface AdapterSetupResult {
40
+ readonly ok: boolean;
41
+ readonly failures: AdapterSetupFailure[];
42
+ }
43
+ /** Lifecycle phase a run covers. */
44
+ type LifecyclePhase = "setup" | "teardown";
45
+ /**
46
+ * Thrown when an `app.lifecycle.setup()` or `teardown()` run finishes with failures.
47
+ *
48
+ * Inspect `phase` to know which hook failed and `failures` for per-adapter
49
+ * diagnostics. The router converts this into a 500 with structured details
50
+ * during health checks.
51
+ */
52
+ declare class AdapterLifecycleError extends Error {
53
+ /** Which lifecycle phase failed (`setup` or `teardown`). */
54
+ readonly phase: LifecyclePhase;
55
+ /** One entry per adapter hook that failed. */
56
+ readonly failures: AdapterSetupFailure[];
57
+ constructor(phase: LifecyclePhase, failures: AdapterSetupFailure[]);
58
+ }
59
+ //#endregion
60
+ export { AdapterLifecycleError, AdapterSetupFailure, AdapterSetupResult, AdapterSetupSources, HookEntry, LifecyclePhase, bindAdapterLoggers, collectSetups, collectTeardowns, runAdapterSetups, runAdapterTeardowns, validateAdapter, validateAdapterSources, validateAuth, validateBootAssembly };
61
+ //# sourceMappingURL=setup.d.mts.map
@@ -0,0 +1,29 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { bindAdapterLoggers, collectSetups, collectTeardowns, runAdapterSetups, runAdapterTeardowns } from "../adapters/setup-runner.mjs";
5
+ import { validateAdapter, validateAdapterSources, validateAuth, validateBootAssembly } from "../adapters/validate.mjs";
6
+ //#region src/adapters/setup.ts
7
+ /**
8
+ * Thrown when an `app.lifecycle.setup()` or `teardown()` run finishes with failures.
9
+ *
10
+ * Inspect `phase` to know which hook failed and `failures` for per-adapter
11
+ * diagnostics. The router converts this into a 500 with structured details
12
+ * during health checks.
13
+ */
14
+ var AdapterLifecycleError = class extends Error {
15
+ /** Which lifecycle phase failed (`setup` or `teardown`). */
16
+ phase;
17
+ /** One entry per adapter hook that failed. */
18
+ failures;
19
+ constructor(phase, failures) {
20
+ super(`Adapter ${phase} failed: ${failures.map((f) => `${f.category}/${f.kind}`).join(", ")}`);
21
+ this.name = "AdapterLifecycleError";
22
+ this.phase = phase;
23
+ this.failures = failures;
24
+ }
25
+ };
26
+ //#endregion
27
+ export { AdapterLifecycleError, bindAdapterLoggers, collectSetups, collectTeardowns, runAdapterSetups, runAdapterTeardowns, validateAdapter, validateAdapterSources, validateAuth, validateBootAssembly };
28
+
29
+ //# sourceMappingURL=setup.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.mjs","names":[],"sources":["../../src/adapters/setup.ts"],"sourcesContent":["/**\n * Adapter lifecycle contract: hooks every adapter can expose, plus the\n * settled-result and error types. The runner (`setup-runner.ts`) collects\n * and executes the hooks; `createShelfApp` kicks `setup` eagerly and\n * gates the first request on the shared result. All hooks must be\n * idempotent.\n */\nimport type { CaptureQueue } from \"./capture-queue.ts\";\nimport type { CaptureRunner } from \"./capture-runner.ts\";\nimport type { DatabaseAdapter } from \"./database.ts\";\nimport type { EmailSender } from \"./email-sender.ts\";\nimport type { GitHostProvider } from \"./git-host/index.ts\";\nimport type { AdapterSetupContext } from \"./metadata.ts\";\nimport type { NotifierProvider } from \"./notifier/provider.ts\";\nimport type { StorageAdapter } from \"./storage.ts\";\n\n/** Adapters that can take part in lifecycle runs. */\nexport interface AdapterSetupSources {\n database: DatabaseAdapter;\n storage: StorageAdapter;\n captureRunner?: CaptureRunner;\n captureQueue?: CaptureQueue;\n gitHosts?: GitHostProvider[];\n notifiers?: NotifierProvider[];\n emailSender?: EmailSender;\n}\n\n/** One collected hook bound to its adapter identity. */\nexport interface HookEntry {\n readonly category: string;\n readonly kind: string;\n readonly name: string;\n readonly run: (ctx: AdapterSetupContext) => Promise<void>;\n}\n\n/** One failed hook, with its adapter identity attached. */\nexport interface AdapterSetupFailure {\n readonly category: string;\n readonly kind: string;\n readonly name: string;\n readonly error: string;\n}\n\n/** Settled outcome of a lifecycle run (never rejects — use `ok`). */\nexport interface AdapterSetupResult {\n readonly ok: boolean;\n readonly failures: AdapterSetupFailure[];\n}\n\n/** Lifecycle phase a run covers. */\nexport type LifecyclePhase = \"setup\" | \"teardown\";\n\n/**\n * Thrown when an `app.lifecycle.setup()` or `teardown()` run finishes with failures.\n *\n * Inspect `phase` to know which hook failed and `failures` for per-adapter\n * diagnostics. The router converts this into a 500 with structured details\n * during health checks.\n */\nexport class AdapterLifecycleError extends Error {\n /** Which lifecycle phase failed (`setup` or `teardown`). */\n readonly phase: LifecyclePhase;\n /** One entry per adapter hook that failed. */\n readonly failures: AdapterSetupFailure[];\n constructor(phase: LifecyclePhase, failures: AdapterSetupFailure[]) {\n super(`Adapter ${phase} failed: ${failures.map((f) => `${f.category}/${f.kind}`).join(\", \")}`);\n this.name = \"AdapterLifecycleError\";\n this.phase = phase;\n this.failures = failures;\n }\n}\n\n/** Collect every `setup` hook from the configured adapters. */\nexport {\n bindAdapterLoggers,\n collectSetups,\n collectTeardowns,\n runAdapterSetups,\n runAdapterTeardowns,\n} from \"./setup-runner.ts\";\n\n/** Boot-time shape validation for third-party adapters. */\nexport {\n validateAdapter,\n validateAdapterSources,\n validateAuth,\n validateBootAssembly,\n} from \"./validate.ts\";\n"],"mappings":";;;;;;;;;;;;;AA2DA,IAAa,wBAAb,cAA2C,MAAM;;CAE/C;;CAEA;CACA,YAAY,OAAuB,UAAiC;EAClE,MAAM,WAAW,MAAM,WAAW,SAAS,KAAK,MAAM,GAAG,EAAE,SAAS,GAAG,EAAE,MAAM,CAAC,CAAC,KAAK,IAAI,GAAG;EAC7F,KAAK,OAAO;EACZ,KAAK,QAAQ;EACb,KAAK,WAAW;CAClB;AACF"}
@@ -0,0 +1,29 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { DatabaseAdapter } from "./database.mjs";
4
+ import { SecretInput } from "../utils/encrypt.mjs";
5
+ import { WebhookTables } from "../models/webhook.mjs";
6
+ import { Logger } from "pino";
7
+ //#region src/adapters/webhook-events.d.ts
8
+ /**
9
+ * Deliver an event to every subscribed webhook of a project.
10
+ *
11
+ * @param db - Database adapter for loading subscriptions.
12
+ * @param tables - Webhook table handles.
13
+ * @param projectId - Project whose webhooks receive the event.
14
+ * @param event - Event name (e.g. "baseline:created").
15
+ * @param data - Event payload.
16
+ * @param secret - Server secret for decrypting webhook secrets (in memory only).
17
+ * @param logger - Optional logger for per-delivery outcomes.
18
+ */
19
+ declare function emitWebhookEvent(db: DatabaseAdapter, tables: WebhookTables, projectId: string, event: string, data: Record<string, unknown>, secret: SecretInput, logger?: Logger): Promise<void>;
20
+ /** Outbound webhook payload delivered to subscribers. */
21
+ interface WebhookEvent {
22
+ event: string;
23
+ projectId: string;
24
+ data: Record<string, unknown>;
25
+ timestamp: string;
26
+ }
27
+ //#endregion
28
+ export { WebhookEvent, emitWebhookEvent };
29
+ //# sourceMappingURL=webhook-events.d.mts.map
@@ -0,0 +1,83 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { WebhookModel } from "../models/webhook.mjs";
5
+ import { hmacSha256 } from "../utils/hash.mjs";
6
+ //#region src/adapters/webhook-events.ts
7
+ /** Per-delivery budget: single attempt, no retries (events already fan out). */
8
+ const WEBHOOK_TIMEOUT_MS = 1e4;
9
+ /**
10
+ * Deliver an event to every subscribed webhook of a project.
11
+ *
12
+ * @param db - Database adapter for loading subscriptions.
13
+ * @param tables - Webhook table handles.
14
+ * @param projectId - Project whose webhooks receive the event.
15
+ * @param event - Event name (e.g. "baseline:created").
16
+ * @param data - Event payload.
17
+ * @param secret - Server secret for decrypting webhook secrets (in memory only).
18
+ * @param logger - Optional logger for per-delivery outcomes.
19
+ */
20
+ async function emitWebhookEvent(db, tables, projectId, event, data, secret, logger) {
21
+ const webhookModel = new WebhookModel(db, tables, secret);
22
+ const webhooks = await webhookModel.list(projectId);
23
+ const eventPayload = {
24
+ event,
25
+ projectId,
26
+ data,
27
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
28
+ };
29
+ await Promise.allSettled(webhooks.map(async (webhook) => {
30
+ const events = WebhookModel.eventsOf(webhook);
31
+ if (events.length > 0 && !events.includes(event)) return;
32
+ await deliverToWebhook(webhookModel, webhook, eventPayload, logger);
33
+ }));
34
+ }
35
+ /** Decrypt, send, and log one webhook delivery (failures stay non-fatal). */
36
+ async function deliverToWebhook(webhookModel, webhook, eventPayload, logger) {
37
+ let plaintext;
38
+ try {
39
+ plaintext = webhookModel.decryptSecret(webhook);
40
+ } catch {
41
+ logger?.warn({ webhookId: webhook.id }, "skipping webhook with undecryptable secret");
42
+ return;
43
+ }
44
+ try {
45
+ await sendWebhook(webhook.url, plaintext, eventPayload);
46
+ logger?.info({
47
+ webhookId: webhook.id,
48
+ host: hostOf(webhook.url)
49
+ }, "webhook delivered");
50
+ } catch (error) {
51
+ logger?.warn({
52
+ webhookId: webhook.id,
53
+ host: hostOf(webhook.url),
54
+ err: error
55
+ }, "webhook delivery failed");
56
+ }
57
+ }
58
+ /** Hostname of a webhook URL for logs (never the full URL — may carry secrets). */
59
+ function hostOf(url) {
60
+ try {
61
+ return new URL(url).host;
62
+ } catch {
63
+ return "[invalid-url]";
64
+ }
65
+ }
66
+ async function sendWebhook(url, secret, event) {
67
+ const body = JSON.stringify(event);
68
+ const signature = hmacSha256(secret, body);
69
+ await fetch(url, {
70
+ method: "POST",
71
+ headers: {
72
+ "Content-Type": "application/json",
73
+ "X-StoryShelf-Event": event.event,
74
+ "X-StoryShelf-Signature": signature
75
+ },
76
+ body,
77
+ signal: AbortSignal.timeout(WEBHOOK_TIMEOUT_MS)
78
+ });
79
+ }
80
+ //#endregion
81
+ export { emitWebhookEvent };
82
+
83
+ //# sourceMappingURL=webhook-events.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook-events.mjs","names":[],"sources":["../../src/adapters/webhook-events.ts"],"sourcesContent":["import type { Logger } from \"pino\";\nimport { WebhookModel, type WebhookTables } from \"../models/webhook.ts\";\nimport type { Webhook } from \"../schema/webhook.ts\";\nimport type { SecretInput } from \"../utils/encrypt.ts\";\nimport { hmacSha256 } from \"../utils/hash.ts\";\nimport type { DatabaseAdapter } from \"./database.ts\";\n\n/** Per-delivery budget: single attempt, no retries (events already fan out). */\nconst WEBHOOK_TIMEOUT_MS = 10_000;\n\n/**\n * Deliver an event to every subscribed webhook of a project.\n *\n * @param db - Database adapter for loading subscriptions.\n * @param tables - Webhook table handles.\n * @param projectId - Project whose webhooks receive the event.\n * @param event - Event name (e.g. \"baseline:created\").\n * @param data - Event payload.\n * @param secret - Server secret for decrypting webhook secrets (in memory only).\n * @param logger - Optional logger for per-delivery outcomes.\n */\nexport async function emitWebhookEvent(\n db: DatabaseAdapter,\n tables: WebhookTables,\n projectId: string,\n event: string,\n data: Record<string, unknown>,\n secret: SecretInput,\n logger?: Logger,\n): Promise<void> {\n const webhookModel = new WebhookModel(db, tables, secret);\n const webhooks = await webhookModel.list(projectId);\n const eventPayload: WebhookEvent = {\n event,\n projectId,\n data,\n timestamp: new Date().toISOString(),\n };\n\n await Promise.allSettled(\n webhooks.map(async (webhook) => {\n const events = WebhookModel.eventsOf(webhook);\n if (events.length > 0 && !events.includes(event)) {\n return;\n }\n await deliverToWebhook(webhookModel, webhook, eventPayload, logger);\n }),\n );\n}\n\n/** Decrypt, send, and log one webhook delivery (failures stay non-fatal). */\nasync function deliverToWebhook(\n webhookModel: WebhookModel,\n webhook: Webhook,\n eventPayload: WebhookEvent,\n logger: Logger | undefined,\n): Promise<void> {\n let plaintext: string;\n try {\n plaintext = webhookModel.decryptSecret(webhook);\n } catch {\n // Undecryptable secret (e.g. rotated server SECRET) — skip, don't leak\n logger?.warn({ webhookId: webhook.id }, \"skipping webhook with undecryptable secret\");\n return;\n }\n try {\n await sendWebhook(webhook.url, plaintext, eventPayload);\n logger?.info({ webhookId: webhook.id, host: hostOf(webhook.url) }, \"webhook delivered\");\n } catch (error) {\n // Webhook delivery failures are non-fatal\n logger?.warn(\n { webhookId: webhook.id, host: hostOf(webhook.url), err: error },\n \"webhook delivery failed\",\n );\n }\n}\n\n/** Outbound webhook payload delivered to subscribers. */\nexport interface WebhookEvent {\n event: string;\n projectId: string;\n data: Record<string, unknown>;\n timestamp: string;\n}\n\n/** Hostname of a webhook URL for logs (never the full URL — may carry secrets). */\nfunction hostOf(url: string): string {\n try {\n return new URL(url).host;\n } catch {\n return \"[invalid-url]\";\n }\n}\n\nasync function sendWebhook(url: string, secret: string, event: WebhookEvent): Promise<void> {\n const body = JSON.stringify(event);\n const signature = hmacSha256(secret, body);\n await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-StoryShelf-Event\": event.event,\n \"X-StoryShelf-Signature\": signature,\n },\n body,\n signal: AbortSignal.timeout(WEBHOOK_TIMEOUT_MS),\n });\n}\n"],"mappings":";;;;;;;AAQA,MAAM,qBAAqB;;;;;;;;;;;;AAa3B,eAAsB,iBACpB,IACA,QACA,WACA,OACA,MACA,QACA,QACe;CACf,MAAM,eAAe,IAAI,aAAa,IAAI,QAAQ,MAAM;CACxD,MAAM,WAAW,MAAM,aAAa,KAAK,SAAS;CAClD,MAAM,eAA6B;EACjC;EACA;EACA;EACA,4BAAW,IAAI,KAAK,EAAA,CAAE,YAAY;CACpC;CAEA,MAAM,QAAQ,WACZ,SAAS,IAAI,OAAO,YAAY;EAC9B,MAAM,SAAS,aAAa,SAAS,OAAO;EAC5C,IAAI,OAAO,SAAS,KAAK,CAAC,OAAO,SAAS,KAAK,GAC7C;EAEF,MAAM,iBAAiB,cAAc,SAAS,cAAc,MAAM;CACpE,CAAC,CACH;AACF;;AAGA,eAAe,iBACb,cACA,SACA,cACA,QACe;CACf,IAAI;CACJ,IAAI;EACF,YAAY,aAAa,cAAc,OAAO;CAChD,QAAQ;EAEN,QAAQ,KAAK,EAAE,WAAW,QAAQ,GAAG,GAAG,4CAA4C;EACpF;CACF;CACA,IAAI;EACF,MAAM,YAAY,QAAQ,KAAK,WAAW,YAAY;EACtD,QAAQ,KAAK;GAAE,WAAW,QAAQ;GAAI,MAAM,OAAO,QAAQ,GAAG;EAAE,GAAG,mBAAmB;CACxF,SAAS,OAAO;EAEd,QAAQ,KACN;GAAE,WAAW,QAAQ;GAAI,MAAM,OAAO,QAAQ,GAAG;GAAG,KAAK;EAAM,GAC/D,yBACF;CACF;AACF;;AAWA,SAAS,OAAO,KAAqB;CACnC,IAAI;EACF,OAAO,IAAI,IAAI,GAAG,CAAC,CAAC;CACtB,QAAQ;EACN,OAAO;CACT;AACF;AAEA,eAAe,YAAY,KAAa,QAAgB,OAAoC;CAC1F,MAAM,OAAO,KAAK,UAAU,KAAK;CACjC,MAAM,YAAY,WAAW,QAAQ,IAAI;CACzC,MAAM,MAAM,KAAK;EACf,QAAQ;EACR,SAAS;GACP,gBAAgB;GAChB,sBAAsB,MAAM;GAC5B,0BAA0B;EAC5B;EACA;EACA,QAAQ,YAAY,QAAQ,kBAAkB;CAChD,CAAC;AACH"}
@@ -0,0 +1,26 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { NotificationBrand, NotificationChannel, NotificationEvent, SystemNotification } from "./types.mjs";
4
+ import { NotifierProvider } from "./provider.mjs";
5
+ import { Logger } from "../../logger.mjs";
6
+ //#region src/adapters/notifier/events.d.ts
7
+ /** Options for project-event fan-out (inline best-effort, non-fatal). */
8
+ interface EmitNotificationsOptions {
9
+ notifiers?: NotifierProvider[];
10
+ channels?: NotificationChannel[];
11
+ brand?: NotificationBrand;
12
+ logger?: Logger;
13
+ }
14
+ /**
15
+ * Deliver a project event to every subscribed channel.
16
+ * Inline best-effort: `Promise.allSettled`, failures logged and swallowed.
17
+ */
18
+ declare function emitNotifications(event: NotificationEvent, options?: EmitNotificationsOptions): Promise<void>;
19
+ /**
20
+ * Deliver a site-wide admin alert to project-less channels
21
+ * (e.g. `sys:user-created`, `sys:capture-failed`, `sys:purge-completed`).
22
+ */
23
+ declare function emitSystemNotification(event: SystemNotification, options?: EmitNotificationsOptions): Promise<void>;
24
+ //#endregion
25
+ export { EmitNotificationsOptions, emitNotifications, emitSystemNotification };
26
+ //# sourceMappingURL=events.d.mts.map
@@ -0,0 +1,66 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ import { formatNotification, formatSystemNotification } from "./format.mjs";
5
+ //#region src/adapters/notifier/events.ts
6
+ /** Check an event against a channel's allowlist (empty = all). */
7
+ function subscribed(channel, event) {
8
+ if (!channel.enabled) return false;
9
+ return channel.events.length === 0 || channel.events.includes(event);
10
+ }
11
+ /** Resolve a provider for a channel kind, or undefined when unwired. */
12
+ function providerFor(notifiers, kind) {
13
+ return notifiers?.find((candidate) => candidate.metadata.kind === kind);
14
+ }
15
+ /** Send one formatted event through a channel's provider (throws on failure). */
16
+ async function deliver(notifiers, channel, formatted) {
17
+ const provider = providerFor(notifiers, channel.provider);
18
+ if (!provider) throw new Error(`notifier missing: ${channel.provider}`);
19
+ await provider.create({
20
+ config: channel.config,
21
+ secret: channel.secret
22
+ }).send({
23
+ channel,
24
+ formatted
25
+ });
26
+ }
27
+ /**
28
+ * Deliver a project event to every subscribed channel.
29
+ * Inline best-effort: `Promise.allSettled`, failures logged and swallowed.
30
+ */
31
+ async function emitNotifications(event, options = {}) {
32
+ const targets = (options.channels ?? []).filter((channel) => subscribed(channel, event.event));
33
+ if (targets.length === 0) return;
34
+ await Promise.allSettled(targets.map(async (channel) => {
35
+ try {
36
+ await deliver(options.notifiers, channel, formatNotification(event, options.brand));
37
+ } catch (error) {
38
+ options.logger?.warn({
39
+ err: error,
40
+ channelId: channel.id
41
+ }, "notification failed");
42
+ }
43
+ }));
44
+ }
45
+ /**
46
+ * Deliver a site-wide admin alert to project-less channels
47
+ * (e.g. `sys:user-created`, `sys:capture-failed`, `sys:purge-completed`).
48
+ */
49
+ async function emitSystemNotification(event, options = {}) {
50
+ const targets = (options.channels ?? []).filter((channel) => channel.projectId === null && subscribed(channel, event.event));
51
+ if (targets.length === 0) return;
52
+ await Promise.allSettled(targets.map(async (channel) => {
53
+ try {
54
+ await deliver(options.notifiers, channel, formatSystemNotification(event, options.brand));
55
+ } catch (error) {
56
+ options.logger?.warn({
57
+ err: error,
58
+ channelId: channel.id
59
+ }, "system notification failed");
60
+ }
61
+ }));
62
+ }
63
+ //#endregion
64
+ export { emitNotifications, emitSystemNotification };
65
+
66
+ //# sourceMappingURL=events.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.mjs","names":[],"sources":["../../../src/adapters/notifier/events.ts"],"sourcesContent":["import type { Logger } from \"../../logger.ts\";\nimport { formatNotification, formatSystemNotification } from \"./format.ts\";\nimport type { NotifierProvider } from \"./provider.ts\";\nimport type {\n NotificationBrand,\n NotificationChannel,\n NotificationEvent,\n SystemNotification,\n} from \"./types.ts\";\n\n/** Options for project-event fan-out (inline best-effort, non-fatal). */\nexport interface EmitNotificationsOptions {\n notifiers?: NotifierProvider[];\n channels?: NotificationChannel[];\n brand?: NotificationBrand;\n logger?: Logger;\n}\n\n/** Check an event against a channel's allowlist (empty = all). */\nfunction subscribed(channel: NotificationChannel, event: string): boolean {\n if (!channel.enabled) {\n return false;\n }\n return channel.events.length === 0 || channel.events.includes(event);\n}\n\n/** Resolve a provider for a channel kind, or undefined when unwired. */\nfunction providerFor(\n notifiers: NotifierProvider[] | undefined,\n kind: string,\n): NotifierProvider | undefined {\n return notifiers?.find((candidate) => candidate.metadata.kind === kind);\n}\n\n/** Send one formatted event through a channel's provider (throws on failure). */\nasync function deliver(\n notifiers: NotifierProvider[] | undefined,\n channel: NotificationChannel,\n formatted: ReturnType<typeof formatNotification>,\n): Promise<void> {\n const provider = providerFor(notifiers, channel.provider);\n if (!provider) {\n throw new Error(`notifier missing: ${channel.provider}`);\n }\n const sender = provider.create({ config: channel.config, secret: channel.secret });\n await sender.send({ channel, formatted });\n}\n\n/**\n * Deliver a project event to every subscribed channel.\n * Inline best-effort: `Promise.allSettled`, failures logged and swallowed.\n */\nexport async function emitNotifications(\n event: NotificationEvent,\n options: EmitNotificationsOptions = {},\n): Promise<void> {\n const targets = (options.channels ?? []).filter((channel) => subscribed(channel, event.event));\n if (targets.length === 0) {\n return;\n }\n await Promise.allSettled(\n targets.map(async (channel) => {\n try {\n await deliver(options.notifiers, channel, formatNotification(event, options.brand));\n } catch (error) {\n options.logger?.warn({ err: error, channelId: channel.id }, \"notification failed\");\n }\n }),\n );\n}\n\n/**\n * Deliver a site-wide admin alert to project-less channels\n * (e.g. `sys:user-created`, `sys:capture-failed`, `sys:purge-completed`).\n */\nexport async function emitSystemNotification(\n event: SystemNotification,\n options: EmitNotificationsOptions = {},\n): Promise<void> {\n const targets = (options.channels ?? []).filter(\n (channel) => channel.projectId === null && subscribed(channel, event.event),\n );\n if (targets.length === 0) {\n return;\n }\n await Promise.allSettled(\n targets.map(async (channel) => {\n try {\n await deliver(options.notifiers, channel, formatSystemNotification(event, options.brand));\n } catch (error) {\n options.logger?.warn({ err: error, channelId: channel.id }, \"system notification failed\");\n }\n }),\n );\n}\n"],"mappings":";;;;;;AAmBA,SAAS,WAAW,SAA8B,OAAwB;CACxE,IAAI,CAAC,QAAQ,SACX,OAAO;CAET,OAAO,QAAQ,OAAO,WAAW,KAAK,QAAQ,OAAO,SAAS,KAAK;AACrE;;AAGA,SAAS,YACP,WACA,MAC8B;CAC9B,OAAO,WAAW,MAAM,cAAc,UAAU,SAAS,SAAS,IAAI;AACxE;;AAGA,eAAe,QACb,WACA,SACA,WACe;CACf,MAAM,WAAW,YAAY,WAAW,QAAQ,QAAQ;CACxD,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,qBAAqB,QAAQ,UAAU;CAGzD,MADe,SAAS,OAAO;EAAE,QAAQ,QAAQ;EAAQ,QAAQ,QAAQ;CAAO,CACrE,CAAC,CAAC,KAAK;EAAE;EAAS;CAAU,CAAC;AAC1C;;;;;AAMA,eAAsB,kBACpB,OACA,UAAoC,CAAC,GACtB;CACf,MAAM,WAAW,QAAQ,YAAY,CAAC,EAAA,CAAG,QAAQ,YAAY,WAAW,SAAS,MAAM,KAAK,CAAC;CAC7F,IAAI,QAAQ,WAAW,GACrB;CAEF,MAAM,QAAQ,WACZ,QAAQ,IAAI,OAAO,YAAY;EAC7B,IAAI;GACF,MAAM,QAAQ,QAAQ,WAAW,SAAS,mBAAmB,OAAO,QAAQ,KAAK,CAAC;EACpF,SAAS,OAAO;GACd,QAAQ,QAAQ,KAAK;IAAE,KAAK;IAAO,WAAW,QAAQ;GAAG,GAAG,qBAAqB;EACnF;CACF,CAAC,CACH;AACF;;;;;AAMA,eAAsB,uBACpB,OACA,UAAoC,CAAC,GACtB;CACf,MAAM,WAAW,QAAQ,YAAY,CAAC,EAAA,CAAG,QACtC,YAAY,QAAQ,cAAc,QAAQ,WAAW,SAAS,MAAM,KAAK,CAC5E;CACA,IAAI,QAAQ,WAAW,GACrB;CAEF,MAAM,QAAQ,WACZ,QAAQ,IAAI,OAAO,YAAY;EAC7B,IAAI;GACF,MAAM,QAAQ,QAAQ,WAAW,SAAS,yBAAyB,OAAO,QAAQ,KAAK,CAAC;EAC1F,SAAS,OAAO;GACd,QAAQ,QAAQ,KAAK;IAAE,KAAK;IAAO,WAAW,QAAQ;GAAG,GAAG,4BAA4B;EAC1F;CACF,CAAC,CACH;AACF"}
@@ -0,0 +1,14 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { ChannelToggles, FormattedNotification, NotificationBrand, NotificationEvent, SystemNotification } from "./types.mjs";
4
+ //#region src/adapters/notifier/format.d.ts
5
+ /**
6
+ * Format a project event into subject/text/markdown/html using brand +
7
+ * channel toggles. Pure (no I/O) so settings preview and senders share it.
8
+ */
9
+ declare function formatNotification(event: NotificationEvent, brand?: NotificationBrand, toggles?: ChannelToggles): FormattedNotification;
10
+ /** Format a site-wide admin alert (no project scope, always verbose). */
11
+ declare function formatSystemNotification(event: SystemNotification, brand?: NotificationBrand): FormattedNotification;
12
+ //#endregion
13
+ export { formatNotification, formatSystemNotification };
14
+ //# sourceMappingURL=format.d.mts.map
@@ -0,0 +1,96 @@
1
+ import "node:path";
2
+ import "node:url";
3
+ import.meta.url;
4
+ //#region src/adapters/notifier/format.ts
5
+ /** Escape HTML special chars in free-form values (commit messages, names). */
6
+ function esc(value) {
7
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
8
+ }
9
+ /** Read a string field from event data without throwing on shapes. */
10
+ function str(data, key) {
11
+ const value = data[key];
12
+ return typeof value === "string" ? value : "";
13
+ }
14
+ /** Resolve toggle defaults (Brand + toggles v1: no custom templates). */
15
+ function togglesOf(toggles) {
16
+ return {
17
+ style: toggles?.style ?? "compact",
18
+ subjectPrefix: toggles?.subjectPrefix ?? "[Shelf]",
19
+ includeAuthor: toggles?.includeAuthor ?? true,
20
+ includeMessage: toggles?.includeMessage ?? true,
21
+ includeCounts: toggles?.includeCounts ?? true
22
+ };
23
+ }
24
+ /** One-line summary for an event (used in subject + compact bodies). */
25
+ function headline(event) {
26
+ const branch = str(event.data, "gitBranch") || str(event.data, "branch");
27
+ const status = str(event.data, "status");
28
+ const story = str(event.data, "storyId");
29
+ if (story) return `${event.event} ${story}${branch ? ` on ${branch}` : ""}`;
30
+ return `${event.event}${branch ? ` on ${branch}` : ""}${status ? ` (${status})` : ""}`;
31
+ }
32
+ /** Append author/message/counts lines for verbose bodies. */
33
+ function appendDetails(lines, event, resolved) {
34
+ if (resolved.style !== "verbose") return;
35
+ const author = str(event.data, "authorName") || str(event.data, "authorEmail");
36
+ if (resolved.includeAuthor && author) lines.push(`by ${author}`);
37
+ const message = str(event.data, "message");
38
+ if (resolved.includeMessage && message) lines.push(message);
39
+ appendCounts(lines, event, resolved);
40
+ }
41
+ /** Append numeric count summary (snapshot/changed/approved/rejected). */
42
+ function appendCounts(lines, event, resolved) {
43
+ if (!resolved.includeCounts) return;
44
+ const counts = [
45
+ "snapshotCount",
46
+ "changedCount",
47
+ "approvedCount",
48
+ "rejectedCount"
49
+ ].map((key) => {
50
+ const value = event.data[key];
51
+ return typeof value === "number" ? `${key}=${value}` : "";
52
+ }).filter((part) => part !== "").join(" ");
53
+ if (counts) lines.push(counts);
54
+ }
55
+ /** Render text/html envelopes around body lines plus brand footer. */
56
+ function envelope(subject, lines, brand) {
57
+ const name = brand?.name ?? "StoryShelf";
58
+ const text = lines.join("\n");
59
+ const footer = brand?.footerText ?? `Sent by ${name}`;
60
+ const htmlBody = lines.map((line) => esc(line)).join("<br/>");
61
+ const logo = brand?.logo ? `<img src="${esc(brand.logo)}" alt="" width="24"/>` : "";
62
+ return {
63
+ subject,
64
+ text: `${text}\n\n${footer}`,
65
+ markdown: `${text}\n\n_${esc(footer)}_`,
66
+ html: `${logo}<p>${htmlBody}</p><p><small>${esc(footer)}</small></p>`
67
+ };
68
+ }
69
+ /**
70
+ * Format a project event into subject/text/markdown/html using brand +
71
+ * channel toggles. Pure (no I/O) so settings preview and senders share it.
72
+ */
73
+ function formatNotification(event, brand, toggles) {
74
+ const resolved = togglesOf(toggles);
75
+ const name = brand?.name ?? "StoryShelf";
76
+ const subject = `${resolved.subjectPrefix} ${headline(event)}`.trim();
77
+ const lines = [`${name}: ${headline(event)}`];
78
+ appendDetails(lines, event, resolved);
79
+ if (brand?.reviewUrl) lines.push(brand.reviewUrl);
80
+ return envelope(subject, lines, brand);
81
+ }
82
+ /** Format a site-wide admin alert (no project scope, always verbose). */
83
+ function formatSystemNotification(event, brand) {
84
+ return formatNotification({
85
+ event: event.event,
86
+ data: event.data,
87
+ timestamp: event.timestamp
88
+ }, brand, {
89
+ style: "verbose",
90
+ subjectPrefix: "[Shelf sys]"
91
+ });
92
+ }
93
+ //#endregion
94
+ export { formatNotification, formatSystemNotification };
95
+
96
+ //# sourceMappingURL=format.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.mjs","names":[],"sources":["../../../src/adapters/notifier/format.ts"],"sourcesContent":["import type {\n ChannelToggles,\n FormattedNotification,\n NotificationBrand,\n NotificationEvent,\n SystemNotification,\n} from \"./types.ts\";\n\n/** Escape HTML special chars in free-form values (commit messages, names). */\nfunction esc(value: string): string {\n return value.replaceAll(\"&\", \"&amp;\").replaceAll(\"<\", \"&lt;\").replaceAll(\">\", \"&gt;\");\n}\n\n/** Read a string field from event data without throwing on shapes. */\nfunction str(data: Record<string, unknown>, key: string): string {\n const value = data[key];\n return typeof value === \"string\" ? value : \"\";\n}\n\n/** Resolve toggle defaults (Brand + toggles v1: no custom templates). */\nfunction togglesOf(toggles?: ChannelToggles): Required<ChannelToggles> {\n return {\n style: toggles?.style ?? \"compact\",\n subjectPrefix: toggles?.subjectPrefix ?? \"[Shelf]\",\n includeAuthor: toggles?.includeAuthor ?? true,\n includeMessage: toggles?.includeMessage ?? true,\n includeCounts: toggles?.includeCounts ?? true,\n };\n}\n\n/** One-line summary for an event (used in subject + compact bodies). */\nfunction headline(event: NotificationEvent): string {\n const branch = str(event.data, \"gitBranch\") || str(event.data, \"branch\");\n const status = str(event.data, \"status\");\n const story = str(event.data, \"storyId\");\n if (story) {\n return `${event.event} ${story}${branch ? ` on ${branch}` : \"\"}`;\n }\n return `${event.event}${branch ? ` on ${branch}` : \"\"}${status ? ` (${status})` : \"\"}`;\n}\n\n/** Append author/message/counts lines for verbose bodies. */\nfunction appendDetails(\n lines: string[],\n event: NotificationEvent,\n resolved: Required<ChannelToggles>,\n): void {\n if (resolved.style !== \"verbose\") {\n return;\n }\n const author = str(event.data, \"authorName\") || str(event.data, \"authorEmail\");\n if (resolved.includeAuthor && author) {\n lines.push(`by ${author}`);\n }\n const message = str(event.data, \"message\");\n if (resolved.includeMessage && message) {\n lines.push(message);\n }\n appendCounts(lines, event, resolved);\n}\n\n/** Append numeric count summary (snapshot/changed/approved/rejected). */\nfunction appendCounts(\n lines: string[],\n event: NotificationEvent,\n resolved: Required<ChannelToggles>,\n): void {\n if (!resolved.includeCounts) {\n return;\n }\n const counts = [\"snapshotCount\", \"changedCount\", \"approvedCount\", \"rejectedCount\"]\n .map((key) => {\n const value = event.data[key];\n return typeof value === \"number\" ? `${key}=${value}` : \"\";\n })\n .filter((part) => part !== \"\")\n .join(\" \");\n if (counts) {\n lines.push(counts);\n }\n}\n\n/** Render text/html envelopes around body lines plus brand footer. */\nfunction envelope(\n subject: string,\n lines: string[],\n brand?: NotificationBrand,\n): FormattedNotification {\n const name = brand?.name ?? \"StoryShelf\";\n const text = lines.join(\"\\n\");\n const footer = brand?.footerText ?? `Sent by ${name}`;\n const htmlBody = lines.map((line) => esc(line)).join(\"<br/>\");\n const logo = brand?.logo ? `<img src=\"${esc(brand.logo)}\" alt=\"\" width=\"24\"/>` : \"\";\n return {\n subject,\n text: `${text}\\n\\n${footer}`,\n markdown: `${text}\\n\\n_${esc(footer)}_`,\n html: `${logo}<p>${htmlBody}</p><p><small>${esc(footer)}</small></p>`,\n };\n}\n\n/**\n * Format a project event into subject/text/markdown/html using brand +\n * channel toggles. Pure (no I/O) so settings preview and senders share it.\n */\nexport function formatNotification(\n event: NotificationEvent,\n brand?: NotificationBrand,\n toggles?: ChannelToggles,\n): FormattedNotification {\n const resolved = togglesOf(toggles);\n const name = brand?.name ?? \"StoryShelf\";\n const subject = `${resolved.subjectPrefix} ${headline(event)}`.trim();\n const lines = [`${name}: ${headline(event)}`];\n appendDetails(lines, event, resolved);\n if (brand?.reviewUrl) {\n lines.push(brand.reviewUrl);\n }\n return envelope(subject, lines, brand);\n}\n\n/** Format a site-wide admin alert (no project scope, always verbose). */\nexport function formatSystemNotification(\n event: SystemNotification,\n brand?: NotificationBrand,\n): FormattedNotification {\n return formatNotification(\n { event: event.event, data: event.data, timestamp: event.timestamp },\n brand,\n { style: \"verbose\", subjectPrefix: \"[Shelf sys]\" },\n );\n}\n"],"mappings":";;;;;AASA,SAAS,IAAI,OAAuB;CAClC,OAAO,MAAM,WAAW,KAAK,OAAO,CAAC,CAAC,WAAW,KAAK,MAAM,CAAC,CAAC,WAAW,KAAK,MAAM;AACtF;;AAGA,SAAS,IAAI,MAA+B,KAAqB;CAC/D,MAAM,QAAQ,KAAK;CACnB,OAAO,OAAO,UAAU,WAAW,QAAQ;AAC7C;;AAGA,SAAS,UAAU,SAAoD;CACrE,OAAO;EACL,OAAO,SAAS,SAAS;EACzB,eAAe,SAAS,iBAAiB;EACzC,eAAe,SAAS,iBAAiB;EACzC,gBAAgB,SAAS,kBAAkB;EAC3C,eAAe,SAAS,iBAAiB;CAC3C;AACF;;AAGA,SAAS,SAAS,OAAkC;CAClD,MAAM,SAAS,IAAI,MAAM,MAAM,WAAW,KAAK,IAAI,MAAM,MAAM,QAAQ;CACvE,MAAM,SAAS,IAAI,MAAM,MAAM,QAAQ;CACvC,MAAM,QAAQ,IAAI,MAAM,MAAM,SAAS;CACvC,IAAI,OACF,OAAO,GAAG,MAAM,MAAM,GAAG,QAAQ,SAAS,OAAO,WAAW;CAE9D,OAAO,GAAG,MAAM,QAAQ,SAAS,OAAO,WAAW,KAAK,SAAS,KAAK,OAAO,KAAK;AACpF;;AAGA,SAAS,cACP,OACA,OACA,UACM;CACN,IAAI,SAAS,UAAU,WACrB;CAEF,MAAM,SAAS,IAAI,MAAM,MAAM,YAAY,KAAK,IAAI,MAAM,MAAM,aAAa;CAC7E,IAAI,SAAS,iBAAiB,QAC5B,MAAM,KAAK,MAAM,QAAQ;CAE3B,MAAM,UAAU,IAAI,MAAM,MAAM,SAAS;CACzC,IAAI,SAAS,kBAAkB,SAC7B,MAAM,KAAK,OAAO;CAEpB,aAAa,OAAO,OAAO,QAAQ;AACrC;;AAGA,SAAS,aACP,OACA,OACA,UACM;CACN,IAAI,CAAC,SAAS,eACZ;CAEF,MAAM,SAAS;EAAC;EAAiB;EAAgB;EAAiB;CAAe,CAAC,CAC/E,KAAK,QAAQ;EACZ,MAAM,QAAQ,MAAM,KAAK;EACzB,OAAO,OAAO,UAAU,WAAW,GAAG,IAAI,GAAG,UAAU;CACzD,CAAC,CAAC,CACD,QAAQ,SAAS,SAAS,EAAE,CAAC,CAC7B,KAAK,GAAG;CACX,IAAI,QACF,MAAM,KAAK,MAAM;AAErB;;AAGA,SAAS,SACP,SACA,OACA,OACuB;CACvB,MAAM,OAAO,OAAO,QAAQ;CAC5B,MAAM,OAAO,MAAM,KAAK,IAAI;CAC5B,MAAM,SAAS,OAAO,cAAc,WAAW;CAC/C,MAAM,WAAW,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,OAAO;CAC5D,MAAM,OAAO,OAAO,OAAO,aAAa,IAAI,MAAM,IAAI,EAAE,yBAAyB;CACjF,OAAO;EACL;EACA,MAAM,GAAG,KAAK,MAAM;EACpB,UAAU,GAAG,KAAK,OAAO,IAAI,MAAM,EAAE;EACrC,MAAM,GAAG,KAAK,KAAK,SAAS,gBAAgB,IAAI,MAAM,EAAE;CAC1D;AACF;;;;;AAMA,SAAgB,mBACd,OACA,OACA,SACuB;CACvB,MAAM,WAAW,UAAU,OAAO;CAClC,MAAM,OAAO,OAAO,QAAQ;CAC5B,MAAM,UAAU,GAAG,SAAS,cAAc,GAAG,SAAS,KAAK,IAAI,KAAK;CACpE,MAAM,QAAQ,CAAC,GAAG,KAAK,IAAI,SAAS,KAAK,GAAG;CAC5C,cAAc,OAAO,OAAO,QAAQ;CACpC,IAAI,OAAO,WACT,MAAM,KAAK,MAAM,SAAS;CAE5B,OAAO,SAAS,SAAS,OAAO,KAAK;AACvC;;AAGA,SAAgB,yBACd,OACA,OACuB;CACvB,OAAO,mBACL;EAAE,OAAO,MAAM;EAAO,MAAM,MAAM;EAAM,WAAW,MAAM;CAAU,GACnE,OACA;EAAE,OAAO;EAAW,eAAe;CAAc,CACnD;AACF"}
@@ -0,0 +1,32 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ import { FormattedNotification, NotificationChannel } from "./types.mjs";
4
+ import { Adapter, AdapterMetadata } from "../../adapter/metadata.mjs";
5
+ import { z } from "zod";
6
+ import { Logger } from "pino";
7
+ //#region src/adapters/notifier/provider.d.ts
8
+ /** Notifier-specific metadata (adds logo + config validation schema). */
9
+ interface NotifierMetadata extends AdapterMetadata {
10
+ readonly category: "notifier";
11
+ readonly logo?: string;
12
+ readonly schema: z.ZodType;
13
+ }
14
+ /** Descriptor — registered at startup, validates per-channel config. */
15
+ interface NotifierProvider extends Adapter<NotifierMetadata> {
16
+ create(opts: {
17
+ config: unknown;
18
+ secret?: string;
19
+ logger?: Logger;
20
+ }): NotifierAdapter;
21
+ }
22
+ /** Runtime — bound to one channel config + decrypted secret, sends one message. */
23
+ interface NotifierAdapter extends Adapter<NotifierMetadata> {
24
+ send(input: {
25
+ channel: NotificationChannel;
26
+ formatted: FormattedNotification;
27
+ logger?: Logger;
28
+ }): Promise<void>;
29
+ }
30
+ //#endregion
31
+ export { NotifierAdapter, NotifierMetadata, NotifierProvider };
32
+ //# sourceMappingURL=provider.d.mts.map
@@ -0,0 +1,56 @@
1
+ import __tsdown_shims_path from 'node:path';
2
+ import __tsdown_shims_url from 'node:url';
3
+ //#region src/adapters/notifier/types.d.ts
4
+ /** Shared event envelope for human-facing notifications. */
5
+ interface NotificationEvent {
6
+ event: string;
7
+ projectId?: string;
8
+ projectSlug?: string;
9
+ data: Record<string, unknown>;
10
+ timestamp: string;
11
+ }
12
+ /** Per-channel display toggles (Brand + toggles v1, no custom templates). */
13
+ interface ChannelToggles {
14
+ style?: "compact" | "verbose";
15
+ subjectPrefix?: string;
16
+ includeAuthor?: boolean;
17
+ includeMessage?: boolean;
18
+ includeCounts?: boolean;
19
+ }
20
+ /** Brand inputs reused from UIConfig plus the review URL. */
21
+ interface NotificationBrand {
22
+ name?: string;
23
+ logo?: string;
24
+ reviewUrl?: string;
25
+ fromEmail?: string;
26
+ fromName?: string;
27
+ footerText?: string;
28
+ }
29
+ /** Formatted output a provider transports (email body, chat markdown). */
30
+ interface FormattedNotification {
31
+ subject: string;
32
+ text: string;
33
+ markdown: string;
34
+ html: string;
35
+ }
36
+ /** A configured project-level channel row (storage-agnostic view). */
37
+ interface NotificationChannel {
38
+ id: string;
39
+ projectId: string | null;
40
+ provider: string;
41
+ config: Record<string, unknown>;
42
+ secret?: string;
43
+ events: string[];
44
+ enabled: boolean;
45
+ }
46
+ /** App-level (site-wide) admin alert — no project scope. */
47
+ interface SystemNotification {
48
+ event: SystemEventName;
49
+ data: Record<string, unknown>;
50
+ timestamp: string;
51
+ }
52
+ /** Site-wide events for admins (new users, failures, purge). */
53
+ type SystemEventName = "sys:user-created" | "sys:invite-issued" | "sys:auth-failed" | "sys:capture-failed" | "sys:purge-completed";
54
+ //#endregion
55
+ export { ChannelToggles, FormattedNotification, NotificationBrand, NotificationChannel, NotificationEvent, SystemEventName, SystemNotification };
56
+ //# sourceMappingURL=types.d.mts.map