alepha 0.21.2 → 0.23.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 (519) hide show
  1. package/README.md +0 -1
  2. package/dist/api/audits/index.browser.js.map +1 -1
  3. package/dist/api/audits/index.d.ts +393 -403
  4. package/dist/api/audits/index.d.ts.map +1 -1
  5. package/dist/api/audits/index.js +25 -56
  6. package/dist/api/audits/index.js.map +1 -1
  7. package/dist/api/files/index.browser.js +31 -1
  8. package/dist/api/files/index.browser.js.map +1 -1
  9. package/dist/api/files/index.d.ts +313 -208
  10. package/dist/api/files/index.d.ts.map +1 -1
  11. package/dist/api/files/index.js +152 -42
  12. package/dist/api/files/index.js.map +1 -1
  13. package/dist/api/jobs/index.browser.js +2 -2
  14. package/dist/api/jobs/index.browser.js.map +1 -1
  15. package/dist/api/jobs/index.d.ts +282 -285
  16. package/dist/api/jobs/index.d.ts.map +1 -1
  17. package/dist/api/jobs/index.js +39 -33
  18. package/dist/api/jobs/index.js.map +1 -1
  19. package/dist/api/keys/index.d.ts +217 -222
  20. package/dist/api/keys/index.d.ts.map +1 -1
  21. package/dist/api/keys/index.js.map +1 -1
  22. package/dist/api/notifications/index.browser.js.map +1 -1
  23. package/dist/api/notifications/index.d.ts +188 -195
  24. package/dist/api/notifications/index.d.ts.map +1 -1
  25. package/dist/api/notifications/index.js.map +1 -1
  26. package/dist/api/oauth/index.d.ts +71 -76
  27. package/dist/api/oauth/index.d.ts.map +1 -1
  28. package/dist/api/oauth/index.js.map +1 -1
  29. package/dist/api/organizations/index.browser.js.map +1 -1
  30. package/dist/api/organizations/index.d.ts +104 -109
  31. package/dist/api/organizations/index.d.ts.map +1 -1
  32. package/dist/api/organizations/index.js.map +1 -1
  33. package/dist/api/parameters/index.browser.js +43 -16
  34. package/dist/api/parameters/index.browser.js.map +1 -1
  35. package/dist/api/parameters/index.d.ts +488 -344
  36. package/dist/api/parameters/index.d.ts.map +1 -1
  37. package/dist/api/parameters/index.js +175 -35
  38. package/dist/api/parameters/index.js.map +1 -1
  39. package/dist/api/payments/index.d.ts +396 -402
  40. package/dist/api/payments/index.d.ts.map +1 -1
  41. package/dist/api/payments/index.js.map +1 -1
  42. package/dist/api/subscriptions/index.d.ts +644 -652
  43. package/dist/api/subscriptions/index.d.ts.map +1 -1
  44. package/dist/api/subscriptions/index.js +1 -1
  45. package/dist/api/subscriptions/index.js.map +1 -1
  46. package/dist/api/users/index.browser.js +7 -0
  47. package/dist/api/users/index.browser.js.map +1 -1
  48. package/dist/api/users/index.d.ts +1106 -1005
  49. package/dist/api/users/index.d.ts.map +1 -1
  50. package/dist/api/users/index.js +307 -64
  51. package/dist/api/users/index.js.map +1 -1
  52. package/dist/api/verifications/index.browser.js.map +1 -1
  53. package/dist/api/verifications/index.d.ts +137 -143
  54. package/dist/api/verifications/index.d.ts.map +1 -1
  55. package/dist/api/verifications/index.js.map +1 -1
  56. package/dist/background/index.d.ts +95 -0
  57. package/dist/background/index.d.ts.map +1 -0
  58. package/dist/background/index.js +121 -0
  59. package/dist/background/index.js.map +1 -0
  60. package/dist/background/index.workerd.js +110 -0
  61. package/dist/background/index.workerd.js.map +1 -0
  62. package/dist/batch/index.d.ts +5 -7
  63. package/dist/batch/index.d.ts.map +1 -1
  64. package/dist/batch/index.js.map +1 -1
  65. package/dist/bin/index.js.map +1 -1
  66. package/dist/bucket/index.d.ts +76 -54
  67. package/dist/bucket/index.d.ts.map +1 -1
  68. package/dist/bucket/index.js +58 -11
  69. package/dist/bucket/index.js.map +1 -1
  70. package/dist/bucket/index.workerd.js +200 -5
  71. package/dist/bucket/index.workerd.js.map +1 -1
  72. package/dist/cache/core/index.d.ts +7 -10
  73. package/dist/cache/core/index.d.ts.map +1 -1
  74. package/dist/cache/core/index.js.map +1 -1
  75. package/dist/cache/core/index.workerd.js.map +1 -1
  76. package/dist/cache/database/index.d.ts +22 -26
  77. package/dist/cache/database/index.d.ts.map +1 -1
  78. package/dist/cache/database/index.js.map +1 -1
  79. package/dist/cache/redis/index.d.ts +4 -7
  80. package/dist/cache/redis/index.d.ts.map +1 -1
  81. package/dist/cache/redis/index.js.map +1 -1
  82. package/dist/captcha/index.d.ts +3 -6
  83. package/dist/captcha/index.d.ts.map +1 -1
  84. package/dist/captcha/index.js.map +1 -1
  85. package/dist/cli/config/index.d.ts.map +1 -1
  86. package/dist/cli/config/index.js.map +1 -1
  87. package/dist/cli/core/index.d.ts +458 -249
  88. package/dist/cli/core/index.d.ts.map +1 -1
  89. package/dist/cli/core/index.js +372 -660
  90. package/dist/cli/core/index.js.map +1 -1
  91. package/dist/cli/devtools/index.d.ts +3 -5
  92. package/dist/cli/devtools/index.d.ts.map +1 -1
  93. package/dist/cli/devtools/index.js.map +1 -1
  94. package/dist/cli/i18n/index.d.ts +20 -17
  95. package/dist/cli/i18n/index.d.ts.map +1 -1
  96. package/dist/cli/i18n/index.js +45 -11
  97. package/dist/cli/i18n/index.js.map +1 -1
  98. package/dist/cli/platform/index.d.ts +126 -1342
  99. package/dist/cli/platform/index.d.ts.map +1 -1
  100. package/dist/cli/platform/index.js +136 -2374
  101. package/dist/cli/platform/index.js.map +1 -1
  102. package/dist/cli/platform-lib/index.d.ts +1472 -0
  103. package/dist/cli/platform-lib/index.d.ts.map +1 -0
  104. package/dist/cli/platform-lib/index.js +2660 -0
  105. package/dist/cli/platform-lib/index.js.map +1 -0
  106. package/dist/cli/vendor/index.d.ts +17 -21
  107. package/dist/cli/vendor/index.d.ts.map +1 -1
  108. package/dist/cli/vendor/index.js.map +1 -1
  109. package/dist/command/index.d.ts +20 -19
  110. package/dist/command/index.d.ts.map +1 -1
  111. package/dist/command/index.js +39 -10
  112. package/dist/command/index.js.map +1 -1
  113. package/dist/{containers → container}/core/index.d.ts +13 -15
  114. package/dist/container/core/index.d.ts.map +1 -0
  115. package/dist/{containers → container}/core/index.js +23 -14
  116. package/dist/container/core/index.js.map +1 -0
  117. package/dist/{containers → container}/core/index.workerd.js +37 -22
  118. package/dist/container/core/index.workerd.js.map +1 -0
  119. package/dist/core/index.browser.js +27 -1
  120. package/dist/core/index.browser.js.map +1 -1
  121. package/dist/core/index.d.ts +48 -24
  122. package/dist/core/index.d.ts.map +1 -1
  123. package/dist/core/index.js +27 -1
  124. package/dist/core/index.js.map +1 -1
  125. package/dist/core/index.native.js +27 -1
  126. package/dist/core/index.native.js.map +1 -1
  127. package/dist/core/index.workerd.js +27 -1
  128. package/dist/core/index.workerd.js.map +1 -1
  129. package/dist/crypto/index.browser.js.map +1 -1
  130. package/dist/crypto/index.d.ts +5 -8
  131. package/dist/crypto/index.d.ts.map +1 -1
  132. package/dist/crypto/index.js.map +1 -1
  133. package/dist/datetime/index.d.ts +3 -4
  134. package/dist/datetime/index.d.ts.map +1 -1
  135. package/dist/datetime/index.js.map +1 -1
  136. package/dist/email/brevo/index.d.ts +2 -4
  137. package/dist/email/brevo/index.d.ts.map +1 -1
  138. package/dist/email/brevo/index.js.map +1 -1
  139. package/dist/email/cloudflare/index.d.ts +20 -7
  140. package/dist/email/cloudflare/index.d.ts.map +1 -1
  141. package/dist/email/cloudflare/index.js +46 -9
  142. package/dist/email/cloudflare/index.js.map +1 -1
  143. package/dist/email/core/index.d.ts +6 -9
  144. package/dist/email/core/index.d.ts.map +1 -1
  145. package/dist/email/core/index.js.map +1 -1
  146. package/dist/email/core/index.workerd.js.map +1 -1
  147. package/dist/email/smtp/index.d.ts +10 -13
  148. package/dist/email/smtp/index.d.ts.map +1 -1
  149. package/dist/email/smtp/index.js +107 -32
  150. package/dist/email/smtp/index.js.map +1 -1
  151. package/dist/fake/index.d.ts +1 -2
  152. package/dist/fake/index.d.ts.map +1 -1
  153. package/dist/fake/index.js.map +1 -1
  154. package/dist/lock/core/index.d.ts +9 -14
  155. package/dist/lock/core/index.d.ts.map +1 -1
  156. package/dist/lock/core/index.js.map +1 -1
  157. package/dist/lock/redis/index.d.ts +2 -4
  158. package/dist/lock/redis/index.d.ts.map +1 -1
  159. package/dist/lock/redis/index.js.map +1 -1
  160. package/dist/logger/index.d.ts +105 -76
  161. package/dist/logger/index.d.ts.map +1 -1
  162. package/dist/logger/index.js +196 -174
  163. package/dist/logger/index.js.map +1 -1
  164. package/dist/mcp/index.d.ts +25 -20
  165. package/dist/mcp/index.d.ts.map +1 -1
  166. package/dist/mcp/index.js +23 -0
  167. package/dist/mcp/index.js.map +1 -1
  168. package/dist/orm/core/index.browser.js.map +1 -1
  169. package/dist/orm/core/index.bun.js +19 -1
  170. package/dist/orm/core/index.bun.js.map +1 -1
  171. package/dist/orm/core/index.d.ts +76 -62
  172. package/dist/orm/core/index.d.ts.map +1 -1
  173. package/dist/orm/core/index.js +20 -2
  174. package/dist/orm/core/index.js.map +1 -1
  175. package/dist/orm/postgres/index.bun.js.map +1 -1
  176. package/dist/orm/postgres/index.d.ts +28 -20
  177. package/dist/orm/postgres/index.d.ts.map +1 -1
  178. package/dist/orm/postgres/index.js.map +1 -1
  179. package/dist/queue/core/index.d.ts +12 -15
  180. package/dist/queue/core/index.d.ts.map +1 -1
  181. package/dist/queue/core/index.js.map +1 -1
  182. package/dist/queue/core/index.workerd.js.map +1 -1
  183. package/dist/queue/redis/index.d.ts +3 -5
  184. package/dist/queue/redis/index.d.ts.map +1 -1
  185. package/dist/queue/redis/index.js.map +1 -1
  186. package/dist/react/auth/index.browser.js +9 -2
  187. package/dist/react/auth/index.browser.js.map +1 -1
  188. package/dist/react/auth/index.d.ts +14 -9
  189. package/dist/react/auth/index.d.ts.map +1 -1
  190. package/dist/react/auth/index.js +9 -2
  191. package/dist/react/auth/index.js.map +1 -1
  192. package/dist/react/core/index.d.ts +7 -8
  193. package/dist/react/core/index.d.ts.map +1 -1
  194. package/dist/react/core/index.js +6 -3
  195. package/dist/react/core/index.js.map +1 -1
  196. package/dist/react/form/index.d.ts +2 -5
  197. package/dist/react/form/index.d.ts.map +1 -1
  198. package/dist/react/form/index.js +16 -15
  199. package/dist/react/form/index.js.map +1 -1
  200. package/dist/react/head/index.browser.js.map +1 -1
  201. package/dist/react/head/index.d.ts +2 -4
  202. package/dist/react/head/index.d.ts.map +1 -1
  203. package/dist/react/head/index.js.map +1 -1
  204. package/dist/react/i18n/index.d.ts +90 -11
  205. package/dist/react/i18n/index.d.ts.map +1 -1
  206. package/dist/react/i18n/index.js +147 -11
  207. package/dist/react/i18n/index.js.map +1 -1
  208. package/dist/react/intro/index.d.ts +1 -2
  209. package/dist/react/intro/index.d.ts.map +1 -1
  210. package/dist/react/intro/index.js +2 -2
  211. package/dist/react/intro/index.js.map +1 -1
  212. package/dist/react/router/index.browser.js +193 -24
  213. package/dist/react/router/index.browser.js.map +1 -1
  214. package/dist/react/router/index.d.ts +434 -222
  215. package/dist/react/router/index.d.ts.map +1 -1
  216. package/dist/react/router/index.js +249 -35
  217. package/dist/react/router/index.js.map +1 -1
  218. package/dist/react/sitemap/index.browser.js +35 -0
  219. package/dist/react/sitemap/index.browser.js.map +1 -0
  220. package/dist/react/sitemap/index.d.ts +92 -0
  221. package/dist/react/sitemap/index.d.ts.map +1 -0
  222. package/dist/react/sitemap/index.js +131 -0
  223. package/dist/react/sitemap/index.js.map +1 -0
  224. package/dist/react/testing/index.d.ts +1 -2
  225. package/dist/react/testing/index.d.ts.map +1 -1
  226. package/dist/react/testing/index.js +16 -17
  227. package/dist/react/testing/index.js.map +1 -1
  228. package/dist/react/ui/index.d.ts +20 -25
  229. package/dist/react/ui/index.d.ts.map +1 -1
  230. package/dist/react/ui/index.js.map +1 -1
  231. package/dist/redis/index.bun.js.map +1 -1
  232. package/dist/redis/index.d.ts +17 -19
  233. package/dist/redis/index.d.ts.map +1 -1
  234. package/dist/redis/index.js.map +1 -1
  235. package/dist/retry/index.d.ts +2 -4
  236. package/dist/retry/index.d.ts.map +1 -1
  237. package/dist/retry/index.js.map +1 -1
  238. package/dist/router/index.d.ts.map +1 -1
  239. package/dist/router/index.js.map +1 -1
  240. package/dist/scheduler/index.d.ts +10 -13
  241. package/dist/scheduler/index.d.ts.map +1 -1
  242. package/dist/scheduler/index.js.map +1 -1
  243. package/dist/scheduler/index.workerd.js.map +1 -1
  244. package/dist/security/index.browser.js.map +1 -1
  245. package/dist/security/index.d.ts +45 -48
  246. package/dist/security/index.d.ts.map +1 -1
  247. package/dist/security/index.js.map +1 -1
  248. package/dist/server/auth/index.browser.js.map +1 -1
  249. package/dist/server/auth/index.d.ts +272 -173
  250. package/dist/server/auth/index.d.ts.map +1 -1
  251. package/dist/server/auth/index.js +1608 -15
  252. package/dist/server/auth/index.js.map +1 -1
  253. package/dist/server/cookies/index.browser.js.map +1 -1
  254. package/dist/server/cookies/index.d.ts +20 -7
  255. package/dist/server/cookies/index.d.ts.map +1 -1
  256. package/dist/server/cookies/index.js +22 -3
  257. package/dist/server/cookies/index.js.map +1 -1
  258. package/dist/server/core/index.browser.js.map +1 -1
  259. package/dist/server/core/index.d.ts +106 -73
  260. package/dist/server/core/index.d.ts.map +1 -1
  261. package/dist/server/core/index.js +44 -0
  262. package/dist/server/core/index.js.map +1 -1
  263. package/dist/server/cors/index.d.ts +11 -14
  264. package/dist/server/cors/index.d.ts.map +1 -1
  265. package/dist/server/cors/index.js.map +1 -1
  266. package/dist/server/etag/index.d.ts +6 -9
  267. package/dist/server/etag/index.d.ts.map +1 -1
  268. package/dist/server/etag/index.js.map +1 -1
  269. package/dist/server/health/index.d.ts +18 -21
  270. package/dist/server/health/index.d.ts.map +1 -1
  271. package/dist/server/health/index.js.map +1 -1
  272. package/dist/server/links/index.browser.js +2 -0
  273. package/dist/server/links/index.browser.js.map +1 -1
  274. package/dist/server/links/index.d.ts +63 -67
  275. package/dist/server/links/index.d.ts.map +1 -1
  276. package/dist/server/links/index.js +2 -0
  277. package/dist/server/links/index.js.map +1 -1
  278. package/dist/server/metrics/index.d.ts +5 -7
  279. package/dist/server/metrics/index.d.ts.map +1 -1
  280. package/dist/server/metrics/index.js.map +1 -1
  281. package/dist/server/proxy/index.d.ts +3 -5
  282. package/dist/server/proxy/index.d.ts.map +1 -1
  283. package/dist/server/proxy/index.js.map +1 -1
  284. package/dist/server/rate-limit/index.d.ts +10 -13
  285. package/dist/server/rate-limit/index.d.ts.map +1 -1
  286. package/dist/server/rate-limit/index.js.map +1 -1
  287. package/dist/server/static/index.d.ts +3 -5
  288. package/dist/server/static/index.d.ts.map +1 -1
  289. package/dist/server/static/index.js.map +1 -1
  290. package/dist/server/swagger/index.d.ts +5 -8
  291. package/dist/server/swagger/index.d.ts.map +1 -1
  292. package/dist/server/swagger/index.js.map +1 -1
  293. package/dist/sms/index.d.ts +3 -5
  294. package/dist/sms/index.d.ts.map +1 -1
  295. package/dist/sms/index.js.map +1 -1
  296. package/dist/system/index.browser.js.map +1 -1
  297. package/dist/system/index.d.ts +2 -4
  298. package/dist/system/index.d.ts.map +1 -1
  299. package/dist/system/index.js.map +1 -1
  300. package/dist/system/index.workerd.js.map +1 -1
  301. package/dist/topic/core/index.d.ts +4 -6
  302. package/dist/topic/core/index.d.ts.map +1 -1
  303. package/dist/topic/core/index.js.map +1 -1
  304. package/dist/topic/redis/index.d.ts +5 -8
  305. package/dist/topic/redis/index.d.ts.map +1 -1
  306. package/dist/topic/redis/index.js.map +1 -1
  307. package/package.json +59 -23
  308. package/src/api/audits/__tests__/AuditService.spec.ts +18 -110
  309. package/src/api/audits/controllers/AdminAuditController.ts +14 -0
  310. package/src/api/audits/services/AuditService.ts +21 -88
  311. package/src/api/files/__tests__/FileService.spec.ts +207 -2
  312. package/src/api/files/index.ts +3 -0
  313. package/src/api/files/schemas/fileCreatorSummarySchema.ts +22 -0
  314. package/src/api/files/schemas/fileResourceSchema.ts +10 -1
  315. package/src/api/files/services/FileService.ts +170 -72
  316. package/src/api/jobs/__tests__/$job.spec.ts +24 -1
  317. package/src/api/jobs/index.ts +4 -3
  318. package/src/api/jobs/primitives/$job.ts +7 -3
  319. package/src/api/jobs/providers/DirectJobDispatcher.ts +17 -36
  320. package/src/api/jobs/providers/JobProvider.ts +53 -24
  321. package/src/api/jobs/schemas/jobConfigAtom.ts +1 -1
  322. package/src/api/jobs/schemas/jobExecutionResourceSchema.ts +4 -1
  323. package/src/api/keys/schemas/adminApiKeyResourceSchema.ts +3 -1
  324. package/src/api/parameters/__tests__/$parameter.spec.ts +19 -2
  325. package/src/api/parameters/audits/ParameterAudits.ts +17 -0
  326. package/src/api/parameters/controllers/AdminParameterController.ts +95 -19
  327. package/src/api/parameters/index.ts +3 -0
  328. package/src/api/parameters/schemas/activateParameterBodySchema.ts +3 -3
  329. package/src/api/parameters/schemas/createParameterVersionBodySchema.ts +3 -2
  330. package/src/api/parameters/schemas/parameterCreatorSummarySchema.ts +25 -0
  331. package/src/api/parameters/schemas/parameterResponseSchema.ts +5 -0
  332. package/src/api/parameters/schemas/rollbackParameterBodySchema.ts +4 -2
  333. package/src/api/parameters/services/ParameterProvider.ts +69 -6
  334. package/src/api/subscriptions/jobs/SubscriptionJobs.ts +1 -1
  335. package/src/api/users/__tests__/AdminSessionController.spec.ts +37 -0
  336. package/src/api/users/audits/SessionAudits.ts +33 -0
  337. package/src/api/users/audits/UserAudits.ts +19 -43
  338. package/src/api/users/controllers/AdminUserController.ts +66 -1
  339. package/src/api/users/controllers/RealmController.ts +1 -0
  340. package/src/api/users/entities/sessions.ts +6 -0
  341. package/src/api/users/entities/users.ts +2 -0
  342. package/src/api/users/index.ts +9 -1
  343. package/src/api/users/primitives/$realm.ts +29 -0
  344. package/src/api/users/providers/RealmProvider.ts +15 -0
  345. package/src/api/users/schemas/realmConfigSchema.ts +14 -0
  346. package/src/api/users/schemas/sessionResourceSchema.ts +16 -0
  347. package/src/api/users/schemas/updateUserSchema.ts +1 -8
  348. package/src/api/users/schemas/userQuerySchema.ts +7 -0
  349. package/src/api/users/services/CredentialService.ts +15 -6
  350. package/src/api/users/services/IdentityService.ts +2 -1
  351. package/src/api/users/services/RegistrationService.ts +2 -1
  352. package/src/api/users/services/SessionCrudService.ts +19 -2
  353. package/src/api/users/services/SessionService.ts +39 -19
  354. package/src/api/users/services/UserService.ts +106 -8
  355. package/src/background/__tests__/BackgroundTaskProvider.spec.ts +96 -0
  356. package/src/background/index.ts +37 -0
  357. package/src/background/index.workerd.ts +28 -0
  358. package/src/background/providers/BackgroundTaskProvider.ts +70 -0
  359. package/src/background/providers/WorkerdBackgroundTaskProvider.ts +43 -0
  360. package/src/bucket/__tests__/$bucket.spec.ts +18 -0
  361. package/src/bucket/__tests__/LocalFileStorageProvider.spec.ts +5 -0
  362. package/src/bucket/__tests__/MemoryFileStorageProvider.spec.ts +5 -0
  363. package/src/bucket/__tests__/NodeS3BucketProvider.spec.ts +23 -4
  364. package/src/bucket/__tests__/shared.ts +30 -0
  365. package/src/bucket/index.ts +5 -5
  366. package/src/bucket/index.workerd.ts +11 -4
  367. package/src/bucket/primitives/$bucket.ts +27 -0
  368. package/src/bucket/providers/FileStorageProvider.ts +13 -0
  369. package/src/bucket/providers/LocalFileStorageProvider.ts +17 -1
  370. package/src/bucket/providers/MemoryFileStorageProvider.ts +7 -0
  371. package/src/bucket/providers/{CloudflareR2Provider.ts → R2FileStorageProvider.ts} +10 -1
  372. package/src/bucket/providers/{NodeS3BucketProvider.ts → S3FileStorageProvider.ts} +27 -5
  373. package/src/cli/core/__tests__/BuildDockerTask.spec.ts +25 -1
  374. package/src/cli/core/__tests__/init.spec.ts +0 -219
  375. package/src/cli/core/atoms/buildOptions.ts +0 -12
  376. package/src/cli/core/commands/__tests__/BuildCommand.spec.ts +43 -0
  377. package/src/cli/core/commands/build.ts +105 -37
  378. package/src/cli/core/commands/init.ts +0 -12
  379. package/src/cli/core/commands/pack.ts +133 -0
  380. package/src/cli/core/index.ts +3 -3
  381. package/src/cli/core/providers/ViteDevServerProvider.ts +40 -16
  382. package/src/cli/core/services/PackageManagerUtils.ts +0 -16
  383. package/src/cli/core/services/ProjectScaffolder.ts +29 -291
  384. package/src/cli/core/tasks/BuildCloudflareTask.ts +382 -56
  385. package/src/cli/core/tasks/BuildDockerTask.ts +33 -3
  386. package/src/cli/core/tasks/BuildPrerenderTask.ts +44 -7
  387. package/src/cli/core/tasks/BuildTask.ts +34 -0
  388. package/src/cli/core/templates/apiIndexTs.ts +1 -22
  389. package/src/cli/core/templates/mainCss.ts +0 -1
  390. package/src/cli/core/templates/webAppRouterTs.ts +0 -99
  391. package/src/cli/core/templates/webIndexTs.ts +1 -22
  392. package/src/cli/i18n/__tests__/I18nCheckService.spec.ts +48 -0
  393. package/src/cli/i18n/services/I18nCheckService.ts +65 -11
  394. package/src/cli/platform/__tests__/SecretsCommand.spec.ts +5 -3
  395. package/src/cli/platform/commands/SecretsCommand.ts +8 -6
  396. package/src/cli/platform/commands/platform.ts +192 -46
  397. package/src/cli/platform/index.ts +12 -52
  398. package/src/cli/{platform → platform-lib}/__tests__/CloudflareAdapter.spec.ts +426 -169
  399. package/src/cli/{platform → platform-lib}/__tests__/NamingService.spec.ts +91 -4
  400. package/src/cli/{platform → platform-lib}/__tests__/VercelAdapter.spec.ts +56 -85
  401. package/src/cli/{platform → platform-lib}/adapters/CloudflareAdapter.ts +519 -190
  402. package/src/cli/{platform → platform-lib}/adapters/PlatformAdapter.ts +62 -35
  403. package/src/cli/{platform → platform-lib}/adapters/VercelAdapter.ts +6 -10
  404. package/src/cli/{platform → platform-lib}/atoms/platformOptions.ts +34 -1
  405. package/src/cli/platform-lib/index.ts +67 -0
  406. package/src/cli/platform-lib/services/NamingService.ts +136 -0
  407. package/src/cli/{platform → platform-lib}/services/PlatformInspector.ts +60 -13
  408. package/src/cli/{platform → platform-lib}/services/PlatformOrchestrator.ts +54 -43
  409. package/src/cli/{platform → platform-lib}/services/WranglerApi.ts +4 -2
  410. package/src/command/__tests__/Runner.spec.ts +20 -0
  411. package/src/command/helpers/EnvUtils.ts +19 -3
  412. package/src/command/helpers/Runner.ts +12 -2
  413. package/src/command/providers/CliProvider.ts +34 -1
  414. package/src/{containers → container}/core/__tests__/$container.spec.ts +5 -5
  415. package/src/{containers → container}/core/index.ts +4 -4
  416. package/src/{containers → container}/core/index.workerd.ts +19 -3
  417. package/src/{containers → container}/core/primitives/$container.ts +1 -1
  418. package/src/{containers → container}/core/providers/CloudflareContainerProvider.ts +17 -19
  419. package/src/{containers → container}/core/providers/ContainerProvider.ts +16 -2
  420. package/src/{containers → container}/core/providers/MockContainerProvider.ts +1 -1
  421. package/src/core/Alepha.ts +49 -1
  422. package/src/core/__tests__/$env.spec.ts +42 -0
  423. package/src/core/__tests__/dump.spec.ts +47 -0
  424. package/src/email/cloudflare/__tests__/CloudflareEmailProvider.spec.ts +42 -10
  425. package/src/email/cloudflare/index.ts +14 -5
  426. package/src/email/cloudflare/providers/CloudflareEmailProvider.ts +54 -9
  427. package/src/logger/__tests__/Logger.spec.ts +55 -0
  428. package/src/logger/index.ts +13 -0
  429. package/src/logger/services/Logger.ts +31 -1
  430. package/src/mcp/__tests__/McpServerProvider.spec.ts +71 -0
  431. package/src/mcp/providers/McpServerProvider.ts +55 -0
  432. package/src/orm/__tests__/orm-showcase-tests.ts +27 -0
  433. package/src/orm/__tests__/orm-showcase.spec.ts +12 -0
  434. package/src/orm/core/interfaces/PgQuery.ts +4 -1
  435. package/src/orm/core/services/Repository.ts +27 -11
  436. package/src/react/auth/hooks/useAuth.ts +10 -5
  437. package/src/react/core/__tests__/useQuery.browser.spec.tsx +25 -0
  438. package/src/react/core/hooks/useAction.ts +14 -3
  439. package/src/react/core/hooks/useQuery.ts +24 -4
  440. package/src/react/form/__tests__/FormModel-submit-loading.spec.ts +71 -0
  441. package/src/react/form/__tests__/form-submitting-reactive.browser.spec.tsx +96 -0
  442. package/src/react/form/services/FormModel.ts +57 -39
  443. package/src/react/i18n/__tests__/I18nProvider.spec.ts +89 -0
  444. package/src/react/i18n/__tests__/locale-routing.spec.ts +107 -0
  445. package/src/react/i18n/components/Translate.tsx +47 -0
  446. package/src/react/i18n/index.ts +2 -0
  447. package/src/react/i18n/providers/I18nProvider.ts +171 -12
  448. package/src/react/intro/components/GettingStartedAdminSlide.tsx +2 -2
  449. package/src/react/router/__tests__/$page.spec.tsx +3 -2
  450. package/src/react/router/__tests__/RouterLocaleProvider.spec.ts +127 -0
  451. package/src/react/router/__tests__/page-can.spec.ts +18 -13
  452. package/src/react/router/hooks/useQueryParams.ts +114 -14
  453. package/src/react/router/index.browser.ts +4 -0
  454. package/src/react/router/index.shared.ts +1 -0
  455. package/src/react/router/index.ts +9 -0
  456. package/src/react/router/primitives/$page.ts +85 -4
  457. package/src/react/router/providers/ReactBrowserRouterProvider.ts +18 -8
  458. package/src/react/router/providers/ReactPageProvider.ts +12 -1
  459. package/src/react/router/providers/ReactServerProvider.ts +96 -14
  460. package/src/react/router/providers/RootComponentsProvider.ts +13 -0
  461. package/src/react/router/providers/RouterLocaleProvider.ts +125 -0
  462. package/src/react/router/providers/__tests__/RootComponentsProvider.spec.ts +15 -0
  463. package/src/react/router/providers/__tests__/rootComponents.ssr.browser.spec.tsx +67 -0
  464. package/src/react/sitemap/__tests__/$sitemap.spec.ts +131 -0
  465. package/src/react/sitemap/index.browser.ts +21 -0
  466. package/src/react/sitemap/index.ts +25 -0
  467. package/src/react/sitemap/primitives/$sitemap.browser.ts +26 -0
  468. package/src/react/sitemap/primitives/$sitemap.ts +196 -0
  469. package/src/react/ui/services/SchemaControl.ts +3 -4
  470. package/src/server/auth/__tests__/appleClientSecret.spec.ts +34 -0
  471. package/src/server/auth/__tests__/authFederationClient.spec.ts +40 -0
  472. package/src/server/auth/__tests__/federationAssertion.spec.ts +146 -0
  473. package/src/server/auth/__tests__/federationRedirectReplay.spec.ts +44 -0
  474. package/src/server/auth/helpers/appleClientSecret.ts +24 -0
  475. package/src/server/auth/helpers/federationAssertion.ts +74 -0
  476. package/src/server/auth/helpers/jtiReplayGuard.ts +41 -0
  477. package/src/server/auth/helpers/safeRedirectPath.ts +19 -0
  478. package/src/server/auth/index.ts +4 -0
  479. package/src/server/auth/primitives/$authFederationBroker.ts +273 -0
  480. package/src/server/auth/primitives/$authFederationClient.ts +89 -0
  481. package/src/server/auth/providers/ServerAuthProvider.ts +18 -4
  482. package/src/server/cookies/__tests__/ServerCookiesProvider.spec.ts +70 -0
  483. package/src/server/cookies/providers/ServerCookiesProvider.ts +23 -3
  484. package/src/server/core/interfaces/ServerRequest.ts +8 -0
  485. package/src/server/core/primitives/$route.ts +27 -0
  486. package/src/server/core/providers/ServerMultipartProvider.ts +19 -0
  487. package/src/server/links/providers/LinkProvider.ts +10 -0
  488. package/dist/containers/core/index.d.ts.map +0 -1
  489. package/dist/containers/core/index.js.map +0 -1
  490. package/dist/containers/core/index.workerd.js.map +0 -1
  491. package/src/cli/core/tasks/BuildSitemapTask.ts +0 -130
  492. package/src/cli/core/templates/componentsJsonTs.ts +0 -39
  493. package/src/cli/core/templates/saasAdminLayoutTsx.ts +0 -77
  494. package/src/cli/core/templates/saasAdminPagesTsx.ts +0 -26
  495. package/src/cli/core/templates/saasAuthLayoutTsx.ts +0 -22
  496. package/src/cli/core/templates/saasAuthPagesTsx.ts +0 -62
  497. package/src/cli/core/templates/saasRealmProviderTs.ts +0 -52
  498. package/src/cli/platform/services/NamingService.ts +0 -54
  499. /package/dist/orm/core/{chunk-o8xxKEmq.js → chunk-B4FMCO8f.js} +0 -0
  500. /package/dist/react/testing/{chunk-6Ep1yQYe.js → chunk-BpyX8vjI.js} +0 -0
  501. /package/src/cli/{platform → platform-lib}/__tests__/GitHubSecretStore.spec.ts +0 -0
  502. /package/src/cli/{platform → platform-lib}/__tests__/PlatformCacheProvider.spec.ts +0 -0
  503. /package/src/cli/{platform → platform-lib}/__tests__/PlatformInspector.spec.ts +0 -0
  504. /package/src/cli/{platform → platform-lib}/__tests__/PlatformOrchestrator.spec.ts +0 -0
  505. /package/src/cli/{platform → platform-lib}/__tests__/SecretFilterService.spec.ts +0 -0
  506. /package/src/cli/{platform → platform-lib}/__tests__/detectResources.spec.ts +0 -0
  507. /package/src/cli/{platform → platform-lib}/providers/GitHubSecretStore.ts +0 -0
  508. /package/src/cli/{platform → platform-lib}/providers/MemorySecretStore.ts +0 -0
  509. /package/src/cli/{platform → platform-lib}/providers/PlatformCacheProvider.ts +0 -0
  510. /package/src/cli/{platform → platform-lib}/providers/SecretStoreProvider.ts +0 -0
  511. /package/src/cli/{platform → platform-lib}/schemas/cloudflare.ts +0 -0
  512. /package/src/cli/{platform → platform-lib}/schemas/platform.ts +0 -0
  513. /package/src/cli/{platform → platform-lib}/schemas/vercel.ts +0 -0
  514. /package/src/cli/{platform → platform-lib}/services/CloudflareApi.ts +0 -0
  515. /package/src/cli/{platform → platform-lib}/services/SecretFilterService.ts +0 -0
  516. /package/src/cli/{platform → platform-lib}/services/VercelApi.ts +0 -0
  517. /package/src/cli/{platform → platform-lib}/services/VercelCli.ts +0 -0
  518. /package/src/{containers → container}/core/interfaces/ContainerOptions.ts +0 -0
  519. /package/src/{containers → container}/core/providers/NodeContainerProvider.ts +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/topic/core/primitives/$subscriber.ts","../../../src/topic/core/errors/TopicTimeoutError.ts","../../../src/topic/core/providers/TopicProvider.ts","../../../src/topic/core/providers/MemoryTopicProvider.ts","../../../src/topic/core/primitives/$topic.ts","../../../src/topic/core/index.ts"],"sourcesContent":["import {\n createPrimitive,\n KIND,\n PipelinePrimitive,\n type PipelinePrimitiveOptions,\n} from \"alepha\";\nimport type {\n TopicHandler,\n TopicMessageSchema,\n TopicPrimitive,\n} from \"./$topic.ts\";\n\n/**\n * Creates a subscriber primitive to listen for messages from a specific topic.\n *\n * Provides a dedicated message subscriber that connects to a topic and processes messages\n * with custom handler logic, enabling scalable pub/sub architectures where multiple\n * subscribers can react to the same events independently.\n *\n * **Key Features**\n * - Seamless integration with any $topic primitive\n * - Full type safety inherited from topic schema\n * - Real-time message delivery when events are published\n * - Error isolation between subscribers\n * - Support for multiple independent subscribers per topic\n *\n * **Common Use Cases**\n * - Notification services and audit logging\n * - Analytics and metrics collection\n * - Data synchronization and real-time UI updates\n *\n * @example\n * ```ts\n * class UserActivityService {\n * userEvents = $topic({\n * name: \"user-activity\",\n * schema: {\n * payload: t.object({\n * userId: t.text(),\n * action: t.enum([\"login\", \"logout\", \"purchase\"]),\n * timestamp: t.number()\n * })\n * }\n * });\n *\n * activityLogger = $subscriber({\n * topic: this.userEvents,\n * handler: async (message) => {\n * const { userId, action, timestamp } = message.payload;\n * await this.auditLogger.log({\n * userId,\n * action,\n * timestamp\n * });\n * }\n * });\n *\n * async trackUserLogin(userId: string) {\n * await this.userEvents.publish({\n * userId,\n * action: \"login\",\n * timestamp: Date.now()\n * });\n * }\n * }\n * ```\n */\nexport const $subscriber = <T extends TopicMessageSchema>(\n options: SubscriberPrimitiveOptions<T>,\n): SubscriberPrimitive<T> => {\n return createPrimitive(SubscriberPrimitive<T>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface SubscriberPrimitiveOptions<T extends TopicMessageSchema>\n extends PipelinePrimitiveOptions {\n /**\n * The topic primitive that this subscriber will listen to for messages.\n *\n * This establishes the connection between the subscriber and its source topic:\n * - The subscriber inherits the topic's message schema for type safety\n * - Messages published to the topic will be automatically delivered to this subscriber\n * - Multiple subscribers can listen to the same topic independently\n * - The subscriber will use the topic's provider and configuration settings\n *\n * **Topic Integration Benefits**:\n * - Type safety: Subscriber handler gets fully typed message payloads\n * - Schema validation: Messages are validated before reaching the subscriber\n * - Real-time delivery: Messages are delivered immediately upon publication\n * - Error isolation: Subscriber errors don't affect the topic or other subscribers\n * - Monitoring: Topic metrics include subscriber processing statistics\n *\n * @example\n * ```ts\n * // First, define a topic\n * userEvents = $topic({\n * name: \"user-activity\",\n * schema: {\n * payload: t.object({ userId: t.text(), action: t.text() })\n * }\n * });\n *\n * // Then, create a subscriber for that topic\n * activitySubscriber = $subscriber({\n * topic: this.userEvents, // Reference the topic primitive\n * handler: async (message) => { } // Process messages here\n * });\n * ```\n */\n topic: TopicPrimitive<T>;\n\n /**\n * Message handler function that processes individual messages from the topic.\n *\n * This function:\n * - Receives fully typed and validated message payloads from the connected topic\n * - Executes immediately when messages are published to the topic\n * - Should implement the core business logic for reacting to these events\n * - Runs independently of other subscribers to the same topic\n * - Should handle errors gracefully to avoid affecting other subscribers\n * - Has access to the full Alepha dependency injection container\n *\n * **Handler Design Guidelines**:\n * - Keep handlers focused on a single responsibility\n * - Use proper error handling and logging\n * - Consider performance impact for high-frequency topics\n * - Make handlers idempotent when possible for reliability\n * - Validate business rules within the handler logic\n * - Log important processing steps for debugging and monitoring\n *\n * **Error Handling Strategy**:\n * - Log errors but don't re-throw to avoid affecting other subscribers\n * - Use try-catch blocks for external service calls\n * - Implement circuit breakers for resilience with external systems\n * - Monitor error rates and patterns for system health\n * - Consider implementing retry logic for temporary failures\n *\n * **Performance Considerations**:\n * - Keep handler execution time minimal for high-throughput topics\n * - Use background queues for heavy processing triggered by events\n * - Implement batching for efficiency when processing many similar events\n * - Consider async processing patterns for non-critical operations\n *\n * @param message - The topic message with validated payload and optional headers\n * @param message.payload - The typed message data based on the topic's schema\n * @returns Promise that resolves when processing is complete\n *\n * @example\n * ```ts\n * handler: async (message) => {\n * const { userId, eventType, timestamp } = message.payload;\n *\n * try {\n * // Log event receipt\n * this.logger.info(`Processing ${eventType} event for user ${userId}`, {\n * timestamp,\n * userId,\n * eventType\n * });\n *\n * // Perform event-specific processing\n * switch (eventType) {\n * case 'user.login':\n * await this.updateLastLogin(userId, timestamp);\n * await this.sendWelcomeNotification(userId);\n * break;\n * case 'user.logout':\n * await this.updateSessionDuration(userId, timestamp);\n * break;\n * case 'user.purchase':\n * await this.updateRewardsPoints(userId, message.payload.purchaseAmount);\n * await this.triggerRecommendations(userId);\n * break;\n * default:\n * this.logger.warn(`Unknown event type: ${eventType}`);\n * }\n *\n * // Update analytics\n * await this.analytics.track(eventType, {\n * userId,\n * timestamp,\n * source: 'topic-subscriber'\n * });\n *\n * this.logger.info(`Successfully processed ${eventType} for user ${userId}`);\n *\n * } catch (error) {\n * // Log error but don't re-throw to avoid affecting other subscribers\n * this.logger.error(`Failed to process ${eventType} for user ${userId}`, {\n * error: error.message,\n * stack: error.stack,\n * userId,\n * eventType,\n * timestamp\n * });\n *\n * // Optionally send to error tracking service\n * await this.errorTracker.captureException(error, {\n * context: { userId, eventType, timestamp },\n * tags: { component: 'topic-subscriber' }\n * });\n * }\n * }\n * ```\n */\n handler: TopicHandler<T>;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class SubscriberPrimitive<\n T extends TopicMessageSchema,\n> extends PipelinePrimitive<SubscriberPrimitiveOptions<T>> {}\n\n$subscriber[KIND] = SubscriberPrimitive;\n","import { AlephaError } from \"alepha\";\n\nexport class TopicTimeoutError extends AlephaError {\n public readonly topic: string;\n public readonly timeout: number;\n\n constructor(topic: string, timeout: number) {\n super(`Timeout of ${timeout}ms exceeded for topic ${topic}`);\n this.timeout = timeout;\n this.topic = topic;\n }\n}\n","import { $inject, Alepha } from \"alepha\";\nimport { DateTimeProvider, type Timeout } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { TemplatedPathParser } from \"alepha/router\";\nimport { TopicTimeoutError } from \"../errors/TopicTimeoutError.ts\";\nimport { $subscriber } from \"../primitives/$subscriber.ts\";\nimport {\n $topic,\n type TopicHandler,\n type TopicMessage,\n type TopicMessageSchema,\n type TopicWaitOptions,\n} from \"../primitives/$topic.ts\";\n\n/**\n * Base class for topic providers.\n */\nexport abstract class TopicProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n\n /**\n * Publish a raw message to a topic.\n *\n * @param topic - The topic to publish to.\n * @param message - The message to publish.\n */\n public abstract publish(\n topic: string,\n message: string,\n options?: TopicPublishOptions,\n ): Promise<void>;\n\n /**\n * Subscribe to a topic with a raw callback.\n *\n * @param topic - The topic to subscribe to.\n * @param callback - The callback to call when a message is received.\n */\n public abstract subscribe(\n topic: string,\n callback: SubscribeCallback,\n options?: TopicSubscribeOptions,\n ): Promise<UnSubscribeFn>;\n\n /**\n * Unsubscribe from a topic.\n *\n * @param topic - The topic to unsubscribe from.\n */\n public abstract unsubscribe(topic: string): Promise<void>;\n\n /**\n * Encode and publish a typed message to a topic.\n */\n public async publishMessage<T extends TopicMessageSchema>(\n name: string,\n schema: T[\"payload\"],\n payload: TopicMessage<T>[\"payload\"],\n options?: TopicPublishOptions,\n ): Promise<void> {\n let topicName = name;\n if (options?.params) {\n const parser = new TemplatedPathParser(name);\n topicName = parser.interpolate(options.params);\n }\n\n await this.publish(\n topicName,\n JSON.stringify({\n payload: this.alepha.codec.encode(schema, payload),\n }),\n options,\n );\n }\n\n /**\n * Parse a raw message string into a typed topic message.\n */\n public parseMessage<T extends TopicMessageSchema>(\n schema: T[\"payload\"],\n message: string,\n ): TopicMessage<T> {\n const { payload } = JSON.parse(message);\n return {\n payload: this.alepha.codec.decode(\n schema,\n payload,\n ) as TopicMessage<T>[\"payload\"],\n } as TopicMessage<T>;\n }\n\n /**\n * Subscribe a typed handler to a topic, with error wrapping and message parsing.\n */\n public async subscribeHandler<T extends TopicMessageSchema>(\n name: string,\n schema: T[\"payload\"],\n handler: TopicHandler<T>,\n options?: TopicSubscribeOptions,\n ): Promise<UnSubscribeFn> {\n const parser = new TemplatedPathParser(name);\n const subscribeTopic = parser.hasParams\n ? parser.wildcardize(this.wildcardChar())\n : name;\n\n return this.subscribe(\n subscribeTopic,\n async (message, receivedTopic?: string) => {\n try {\n const parsed = this.parseMessage<T>(schema, message);\n\n if (parser.hasParams && receivedTopic) {\n const params = parser.extract(receivedTopic) ?? {};\n await handler({ ...parsed, params } as TopicMessage<T>);\n } else {\n await handler(parsed);\n }\n } catch (error) {\n this.log.error(\"Message processing has failed\", error);\n }\n },\n options,\n );\n }\n\n /**\n * Wait for a single message matching an optional filter, with timeout.\n */\n public async waitForMessage<T extends TopicMessageSchema>(\n name: string,\n schema: T[\"payload\"],\n options: TopicWaitOptions<T> = {},\n ): Promise<TopicMessage<T>> {\n const filter = options.filter ?? (() => true);\n\n return new Promise((resolve, reject) => {\n const ref: { timeout?: Timeout; clear?: () => void } = {};\n\n (async () => {\n const clear = await this.subscribe(name, (raw) => {\n const message = this.parseMessage<T>(schema, raw);\n if (!filter(message)) {\n return;\n }\n\n ref.timeout?.clear();\n if (ref.clear) {\n ref.clear();\n }\n resolve(message);\n });\n\n ref.clear = clear;\n\n const timeoutDuration = options.timeout ?? [10, \"seconds\"];\n\n ref.timeout = this.dateTimeProvider.createTimeout(() => {\n clear();\n reject(\n new TopicTimeoutError(\n name,\n this.dateTimeProvider.duration(timeoutDuration).asMilliseconds(),\n ),\n );\n }, timeoutDuration);\n })();\n });\n }\n\n /**\n * The wildcard character used for pattern subscriptions.\n * Override in subclasses for provider-specific wildcards.\n */\n protected wildcardChar(): string {\n return \"+\";\n }\n\n /**\n * Returns the list of $subscribers for this provider.\n */\n protected subscribers(): Array<() => Promise<unknown>> {\n const handlers: Array<() => Promise<unknown>> = [];\n\n const topics = this.alepha.primitives($topic);\n\n for (const topic of topics) {\n if (topic.provider !== this) {\n continue;\n }\n\n const handler = topic.options.handler;\n if (handler && topic.provider === this) {\n handlers.push(() => topic.subscribe(handler));\n }\n }\n\n const subscribers = this.alepha.primitives($subscriber);\n for (const subscriber of subscribers) {\n if (subscriber.options.topic.provider !== this) {\n continue;\n }\n\n const handler = subscriber.handler.run.bind(subscriber.handler);\n handlers.push(() => subscriber.options.topic.subscribe(handler));\n }\n\n return handlers;\n }\n}\n\nexport type SubscribeCallback = (\n message: string,\n topic?: string,\n) => Promise<void> | void;\n\nexport type UnSubscribeFn = () => Promise<void>;\n\nexport interface TopicPublishOptions {\n retain?: boolean;\n params?: Record<string, string>;\n}\n\n// biome-ignore lint/suspicious/noEmptyInterface: augmented by provider-specific modules (e.g. MqttTopicProvider)\nexport interface TopicSubscribeOptions {}\n","import { $hook } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport {\n type SubscribeCallback,\n TopicProvider,\n type TopicPublishOptions,\n type TopicSubscribeOptions,\n type UnSubscribeFn,\n} from \"./TopicProvider.ts\";\n\nexport class MemoryTopicProvider extends TopicProvider {\n protected readonly log = $logger();\n protected readonly subscriptions: Record<string, SubscribeCallback[]> = {};\n protected readonly retained: Record<string, string> = {};\n\n protected readonly start = $hook({\n on: \"start\",\n handler: async () => {\n const subscribers = this.subscribers();\n if (subscribers.length) {\n await Promise.all(subscribers.map((fn) => fn()));\n for (const subscriber of subscribers) {\n this.log.debug(`Subscribed to topic '${subscriber.name}'`);\n }\n }\n },\n });\n\n /**\n * Publish a message to a topic.\n *\n * @param topic\n * @param message\n * @param options\n */\n public async publish(\n topic: string,\n message: string,\n options?: TopicPublishOptions,\n ): Promise<void> {\n if (options?.retain) {\n this.retained[topic] = message;\n }\n\n for (const [pattern, callbacks] of Object.entries(this.subscriptions)) {\n if (this.topicMatches(pattern, topic)) {\n for (const callback of callbacks) {\n await callback(message, topic);\n }\n }\n }\n }\n\n /**\n * Subscribe to a topic.\n *\n * @param topic - The topic to subscribe to.\n * @param callback\n */\n\n public async subscribe(\n topic: string,\n callback: SubscribeCallback,\n _options?: TopicSubscribeOptions,\n ): Promise<UnSubscribeFn> {\n if (!this.subscriptions[topic]) {\n this.subscriptions[topic] = [];\n }\n\n this.subscriptions[topic].push(callback);\n\n // Deliver retained messages matching the pattern\n for (const [retainedTopic, retainedMessage] of Object.entries(\n this.retained,\n )) {\n if (this.topicMatches(topic, retainedTopic)) {\n await callback(retainedMessage, retainedTopic);\n }\n }\n\n return async () => {\n const callbacks = this.subscriptions[topic];\n if (!callbacks) {\n return;\n }\n\n this.subscriptions[topic] = callbacks.filter((cb) => cb !== callback);\n if (this.subscriptions[topic].length === 0) {\n delete this.subscriptions[topic];\n }\n };\n }\n\n /**\n * Unsubscribe from a topic.\n *\n * @param topic - The topic to unsubscribe from.\n */\n public async unsubscribe(topic: string): Promise<void> {\n delete this.subscriptions[topic];\n }\n\n /**\n * Check if a topic matches a subscription pattern.\n * Supports `+` single-level wildcard.\n */\n protected topicMatches(pattern: string, topic: string): boolean {\n if (pattern === topic) {\n return true;\n }\n\n const patternParts = pattern.split(\"/\");\n const topicParts = topic.split(\"/\");\n\n if (patternParts.length !== topicParts.length) {\n return false;\n }\n\n return patternParts.every(\n (part, i) => part === \"+\" || part === topicParts[i],\n );\n }\n}\n","import {\n createPrimitive,\n KIND,\n Primitive,\n type Service,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport { MemoryTopicProvider } from \"../providers/MemoryTopicProvider.ts\";\nimport {\n TopicProvider,\n type TopicPublishOptions,\n type UnSubscribeFn,\n} from \"../providers/TopicProvider.ts\";\n\n/**\n * Creates a topic primitive for publish/subscribe messaging and event-driven architecture.\n *\n * Enables decoupled communication through a pub/sub pattern where publishers send messages\n * and multiple subscribers receive them. Supports type-safe messages, real-time delivery,\n * event filtering, and pluggable backends (memory, Redis, custom providers).\n *\n * **Use Cases**: User notifications, real-time chat, event broadcasting, microservice communication\n *\n * @example\n * ```ts\n * class NotificationService {\n * userActivity = $topic({\n * name: \"user-activity\",\n * schema: {\n * payload: t.object({\n * userId: t.text(),\n * action: t.enum([\"login\", \"logout\", \"purchase\"]),\n * timestamp: t.number()\n * })\n * },\n * handler: async (message) => {\n * console.log(`User ${message.payload.userId}: ${message.payload.action}`);\n * }\n * });\n *\n * async trackLogin(userId: string) {\n * await this.userActivity.publish({ userId, action: \"login\", timestamp: Date.now() });\n * }\n *\n * async subscribeToEvents() {\n * await this.userActivity.subscribe(async (message) => {\n * // Additional subscriber logic\n * });\n * }\n * }\n * ```\n */\nexport const $topic = <T extends TopicMessageSchema>(\n options: TopicPrimitiveOptions<T>,\n): TopicPrimitive<T> => {\n return createPrimitive(TopicPrimitive<T>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface TopicPrimitiveOptions<T extends TopicMessageSchema> {\n /**\n * Unique name identifier for the topic.\n *\n * This name is used for:\n * - Topic identification across the pub/sub system\n * - Message routing between publishers and subscribers\n * - Logging and debugging topic-related operations\n * - Provider-specific topic management (channels, keys, etc.)\n *\n * If not provided, defaults to the property key where the topic is declared.\n *\n * **Naming Conventions**:\n * - Use descriptive, hierarchical names: \"user.activity\", \"order.events\"\n * - Avoid spaces and special characters\n * - Consider using dot notation for categorization\n * - Keep names concise but meaningful\n *\n * @example \"user-activity\"\n * @example \"chat.messages\"\n * @example \"system.health.checks\"\n * @example \"payment.webhooks\"\n */\n name?: string;\n\n /**\n * Human-readable description of the topic's purpose and usage.\n *\n * Used for:\n * - Documentation generation and API references\n * - Developer onboarding and understanding\n * - Monitoring dashboards and admin interfaces\n * - Team communication about system architecture\n *\n * **Description Best Practices**:\n * - Explain what events/messages this topic handles\n * - Mention key use cases and subscribers\n * - Include any important timing or ordering guarantees\n * - Note any special processing requirements\n *\n * @example \"Real-time user activity events for analytics and notifications\"\n * @example \"Order lifecycle events from creation to delivery\"\n * @example \"Chat messages broadcast to all room participants\"\n * @example \"System health checks and service status updates\"\n */\n description?: string;\n\n /**\n * Topic provider configuration for message storage and delivery.\n *\n * Options:\n * - **\"memory\"**: In-memory provider (default for development, lost on restart)\n * - **Service<TopicProvider>**: Custom provider class (e.g., RedisTopicProvider)\n * - **undefined**: Uses the default topic provider from dependency injection\n *\n * **Provider Selection Guidelines**:\n * - **Development**: Use \"memory\" for fast, simple testing without external dependencies\n * - **Production**: Use Redis or message brokers for persistence and scalability\n * - **Distributed systems**: Use Redis/RabbitMQ for cross-service communication\n * - **High-throughput**: Use specialized providers with connection pooling\n * - **Real-time**: Ensure provider supports low-latency message delivery\n *\n * **Provider Capabilities**:\n * - Message persistence and durability\n * - Subscriber management and connection handling\n * - Message ordering and delivery guarantees\n * - Horizontal scaling and load distribution\n *\n * @default Uses injected TopicProvider\n * @example \"memory\"\n * @example RedisTopicProvider\n * @example RabbitMQTopicProvider\n */\n provider?: \"memory\" | Service<TopicProvider>;\n\n /**\n * TypeBox schema defining the structure of messages published to this topic.\n *\n * The schema must include:\n * - **payload**: Required schema for the main message data\n * - **headers**: Optional schema for message metadata\n *\n * This schema:\n * - Validates all messages published to the topic\n * - Provides full TypeScript type inference for subscribers\n * - Ensures type safety between publishers and subscribers\n * - Enables automatic serialization/deserialization\n *\n * **Schema Design Best Practices**:\n * - Keep payload schemas focused and cohesive\n * - Use optional fields for data that might not always be present\n * - Include timestamp fields for event ordering\n * - Consider versioning for schema evolution\n * - Use union types for different event types in the same topic\n *\n * @example\n * ```ts\n * {\n * payload: t.object({\n * eventId: t.text(),\n * eventType: t.enum([\"created\", \"updated\"]),\n * data: t.record(t.text(), t.any()),\n * timestamp: t.number(),\n * userId: t.optional(t.text())\n * }),\n * headers: t.optional(t.object({\n * source: t.text(),\n * correlationId: t.text()\n * }))\n * }\n * ```\n */\n schema: T;\n\n /**\n * Default subscriber handler function that processes messages published to this topic.\n *\n * This handler:\n * - Automatically subscribes when the topic is initialized\n * - Receives all messages published to the topic\n * - Runs for every message without additional subscription setup\n * - Can be supplemented with additional subscribers via `subscribe()` method\n * - Should handle errors gracefully to avoid breaking other subscribers\n *\n * **Handler Design Guidelines**:\n * - Keep handlers focused on a single responsibility\n * - Use proper error handling and logging\n * - Consider performance impact for high-frequency topics\n * - Make handlers idempotent when possible\n * - Validate business rules within the handler logic\n * - Log important processing steps for debugging\n *\n * **Error Handling Strategy**:\n * - Log errors but don't re-throw to avoid affecting other subscribers\n * - Use try-catch blocks for external service calls\n * - Consider implementing circuit breakers for resilience\n * - Monitor error rates and patterns for system health\n *\n * @param message - The topic message with validated payload and headers\n * @param message.payload - The typed message data based on the schema\n * @returns Promise that resolves when processing is complete\n *\n * @example\n * ```ts\n * handler: async (message) => {\n * const { eventType, data, timestamp } = message.payload;\n *\n * try {\n * // Log message receipt\n * this.logger.info(`Processing ${eventType} event`, { timestamp, data });\n *\n * // Process based on event type\n * switch (eventType) {\n * case \"created\":\n * await this.handleCreation(data);\n * break;\n * case \"updated\":\n * await this.handleUpdate(data);\n * break;\n * default:\n * this.logger.warn(`Unknown event type: ${eventType}`);\n * }\n *\n * this.logger.info(`Successfully processed ${eventType} event`);\n *\n * } catch (error) {\n * // Log error but don't re-throw to avoid affecting other subscribers\n * this.logger.error(`Failed to process ${eventType} event`, {\n * error: error.message,\n * eventType,\n * timestamp,\n * data\n * });\n * }\n * }\n * ```\n */\n handler?: TopicHandler<T>;\n\n /**\n * Whether the last published message should be retained and delivered to new subscribers.\n *\n * When enabled, the provider stores the last message for this topic.\n * New subscribers immediately receive the retained message upon subscribing.\n *\n * Supported by Memory, Redis, and MQTT providers.\n *\n * @default false\n */\n retain?: boolean;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class TopicPrimitive<T extends TopicMessageSchema> extends Primitive<\n TopicPrimitiveOptions<T>\n> {\n public readonly provider = this.$provider();\n\n public get name(): string {\n return this.options.name || this.config.propertyKey;\n }\n\n public async publish(\n message: T extends { params: TSchema }\n ? { params: Static<T[\"params\"]>; payload: TopicMessage<T>[\"payload\"] }\n : TopicMessage<T>[\"payload\"],\n ): Promise<void> {\n const hasParams = this.options.schema.params;\n const payload = hasParams ? (message as any).payload : message;\n const params = hasParams ? (message as any).params : undefined;\n\n await this.provider.publishMessage<T>(\n this.name,\n this.options.schema.payload,\n payload,\n {\n retain: this.options.retain,\n mqtt: (this.options as any).mqtt,\n params,\n } as TopicPublishOptions,\n );\n }\n\n public async subscribe(handler: TopicHandler<T>): Promise<UnSubscribeFn> {\n return this.provider.subscribeHandler<T>(\n this.name,\n this.options.schema.payload,\n handler,\n { mqtt: (this.options as any).mqtt },\n );\n }\n\n public async wait(\n options: TopicWaitOptions<T> = {},\n ): Promise<TopicMessage<T>> {\n return this.provider.waitForMessage<T>(\n this.name,\n this.options.schema.payload,\n options,\n );\n }\n\n protected $provider(): TopicProvider {\n if (!this.options.provider) {\n return this.alepha.inject(TopicProvider);\n }\n\n if (this.options.provider === \"memory\") {\n return this.alepha.inject(MemoryTopicProvider);\n }\n\n return this.alepha.inject(this.options.provider);\n }\n}\n\n$topic[KIND] = TopicPrimitive;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface TopicMessage<T extends TopicMessageSchema> {\n payload: Static<T[\"payload\"]>;\n params: T extends { params: TSchema } ? Static<T[\"params\"]> : never;\n}\n\nexport interface TopicWaitOptions<T extends TopicMessageSchema> {\n timeout?: DurationLike;\n filter?: (message: { payload: Static<T[\"payload\"]> }) => boolean;\n}\n\nexport interface TopicMessageSchema {\n payload: TSchema;\n params?: TSchema;\n}\n\nexport type TopicHandler<T extends TopicMessageSchema = TopicMessageSchema> = (\n message: TopicMessage<T>,\n) => unknown;\n","import { $module, type Alepha } from \"alepha\";\nimport { $subscriber } from \"./primitives/$subscriber.ts\";\nimport { $topic } from \"./primitives/$topic.ts\";\nimport { MemoryTopicProvider } from \"./providers/MemoryTopicProvider.ts\";\nimport { TopicProvider } from \"./providers/TopicProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./errors/TopicTimeoutError.ts\";\nexport * from \"./primitives/$subscriber.ts\";\nexport * from \"./primitives/$topic.ts\";\nexport * from \"./providers/MemoryTopicProvider.ts\";\nexport * from \"./providers/TopicProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Publish/subscribe messaging for event-driven architectures.\n *\n * **Features:**\n * - Pub/sub topics with type-safe messages\n * - Topic subscription handlers\n * - Multiple subscriber support\n * - Message filtering and routing\n * - Providers: Memory (dev), Redis (production)\n *\n * @module alepha.topic\n */\nexport const AlephaTopic = $module({\n name: \"alepha.topic\",\n primitives: [$topic, $subscriber],\n services: [TopicProvider],\n variants: [MemoryTopicProvider],\n register: (alepha: Alepha) =>\n alepha.with({\n optional: true,\n provide: TopicProvider,\n use: MemoryTopicProvider,\n }),\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/topic/core/primitives/$subscriber.ts","../../../src/topic/core/errors/TopicTimeoutError.ts","../../../src/topic/core/providers/TopicProvider.ts","../../../src/topic/core/providers/MemoryTopicProvider.ts","../../../src/topic/core/primitives/$topic.ts","../../../src/topic/core/index.ts"],"sourcesContent":["import {\n createPrimitive,\n KIND,\n PipelinePrimitive,\n type PipelinePrimitiveOptions,\n} from \"alepha\";\nimport type {\n TopicHandler,\n TopicMessageSchema,\n TopicPrimitive,\n} from \"./$topic.ts\";\n\n/**\n * Creates a subscriber primitive to listen for messages from a specific topic.\n *\n * Provides a dedicated message subscriber that connects to a topic and processes messages\n * with custom handler logic, enabling scalable pub/sub architectures where multiple\n * subscribers can react to the same events independently.\n *\n * **Key Features**\n * - Seamless integration with any $topic primitive\n * - Full type safety inherited from topic schema\n * - Real-time message delivery when events are published\n * - Error isolation between subscribers\n * - Support for multiple independent subscribers per topic\n *\n * **Common Use Cases**\n * - Notification services and audit logging\n * - Analytics and metrics collection\n * - Data synchronization and real-time UI updates\n *\n * @example\n * ```ts\n * class UserActivityService {\n * userEvents = $topic({\n * name: \"user-activity\",\n * schema: {\n * payload: t.object({\n * userId: t.text(),\n * action: t.enum([\"login\", \"logout\", \"purchase\"]),\n * timestamp: t.number()\n * })\n * }\n * });\n *\n * activityLogger = $subscriber({\n * topic: this.userEvents,\n * handler: async (message) => {\n * const { userId, action, timestamp } = message.payload;\n * await this.auditLogger.log({\n * userId,\n * action,\n * timestamp\n * });\n * }\n * });\n *\n * async trackUserLogin(userId: string) {\n * await this.userEvents.publish({\n * userId,\n * action: \"login\",\n * timestamp: Date.now()\n * });\n * }\n * }\n * ```\n */\nexport const $subscriber = <T extends TopicMessageSchema>(\n options: SubscriberPrimitiveOptions<T>,\n): SubscriberPrimitive<T> => {\n return createPrimitive(SubscriberPrimitive<T>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface SubscriberPrimitiveOptions<T extends TopicMessageSchema>\n extends PipelinePrimitiveOptions {\n /**\n * The topic primitive that this subscriber will listen to for messages.\n *\n * This establishes the connection between the subscriber and its source topic:\n * - The subscriber inherits the topic's message schema for type safety\n * - Messages published to the topic will be automatically delivered to this subscriber\n * - Multiple subscribers can listen to the same topic independently\n * - The subscriber will use the topic's provider and configuration settings\n *\n * **Topic Integration Benefits**:\n * - Type safety: Subscriber handler gets fully typed message payloads\n * - Schema validation: Messages are validated before reaching the subscriber\n * - Real-time delivery: Messages are delivered immediately upon publication\n * - Error isolation: Subscriber errors don't affect the topic or other subscribers\n * - Monitoring: Topic metrics include subscriber processing statistics\n *\n * @example\n * ```ts\n * // First, define a topic\n * userEvents = $topic({\n * name: \"user-activity\",\n * schema: {\n * payload: t.object({ userId: t.text(), action: t.text() })\n * }\n * });\n *\n * // Then, create a subscriber for that topic\n * activitySubscriber = $subscriber({\n * topic: this.userEvents, // Reference the topic primitive\n * handler: async (message) => { } // Process messages here\n * });\n * ```\n */\n topic: TopicPrimitive<T>;\n\n /**\n * Message handler function that processes individual messages from the topic.\n *\n * This function:\n * - Receives fully typed and validated message payloads from the connected topic\n * - Executes immediately when messages are published to the topic\n * - Should implement the core business logic for reacting to these events\n * - Runs independently of other subscribers to the same topic\n * - Should handle errors gracefully to avoid affecting other subscribers\n * - Has access to the full Alepha dependency injection container\n *\n * **Handler Design Guidelines**:\n * - Keep handlers focused on a single responsibility\n * - Use proper error handling and logging\n * - Consider performance impact for high-frequency topics\n * - Make handlers idempotent when possible for reliability\n * - Validate business rules within the handler logic\n * - Log important processing steps for debugging and monitoring\n *\n * **Error Handling Strategy**:\n * - Log errors but don't re-throw to avoid affecting other subscribers\n * - Use try-catch blocks for external service calls\n * - Implement circuit breakers for resilience with external systems\n * - Monitor error rates and patterns for system health\n * - Consider implementing retry logic for temporary failures\n *\n * **Performance Considerations**:\n * - Keep handler execution time minimal for high-throughput topics\n * - Use background queues for heavy processing triggered by events\n * - Implement batching for efficiency when processing many similar events\n * - Consider async processing patterns for non-critical operations\n *\n * @param message - The topic message with validated payload and optional headers\n * @param message.payload - The typed message data based on the topic's schema\n * @returns Promise that resolves when processing is complete\n *\n * @example\n * ```ts\n * handler: async (message) => {\n * const { userId, eventType, timestamp } = message.payload;\n *\n * try {\n * // Log event receipt\n * this.logger.info(`Processing ${eventType} event for user ${userId}`, {\n * timestamp,\n * userId,\n * eventType\n * });\n *\n * // Perform event-specific processing\n * switch (eventType) {\n * case 'user.login':\n * await this.updateLastLogin(userId, timestamp);\n * await this.sendWelcomeNotification(userId);\n * break;\n * case 'user.logout':\n * await this.updateSessionDuration(userId, timestamp);\n * break;\n * case 'user.purchase':\n * await this.updateRewardsPoints(userId, message.payload.purchaseAmount);\n * await this.triggerRecommendations(userId);\n * break;\n * default:\n * this.logger.warn(`Unknown event type: ${eventType}`);\n * }\n *\n * // Update analytics\n * await this.analytics.track(eventType, {\n * userId,\n * timestamp,\n * source: 'topic-subscriber'\n * });\n *\n * this.logger.info(`Successfully processed ${eventType} for user ${userId}`);\n *\n * } catch (error) {\n * // Log error but don't re-throw to avoid affecting other subscribers\n * this.logger.error(`Failed to process ${eventType} for user ${userId}`, {\n * error: error.message,\n * stack: error.stack,\n * userId,\n * eventType,\n * timestamp\n * });\n *\n * // Optionally send to error tracking service\n * await this.errorTracker.captureException(error, {\n * context: { userId, eventType, timestamp },\n * tags: { component: 'topic-subscriber' }\n * });\n * }\n * }\n * ```\n */\n handler: TopicHandler<T>;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class SubscriberPrimitive<\n T extends TopicMessageSchema,\n> extends PipelinePrimitive<SubscriberPrimitiveOptions<T>> {}\n\n$subscriber[KIND] = SubscriberPrimitive;\n","import { AlephaError } from \"alepha\";\n\nexport class TopicTimeoutError extends AlephaError {\n public readonly topic: string;\n public readonly timeout: number;\n\n constructor(topic: string, timeout: number) {\n super(`Timeout of ${timeout}ms exceeded for topic ${topic}`);\n this.timeout = timeout;\n this.topic = topic;\n }\n}\n","import { $inject, Alepha } from \"alepha\";\nimport { DateTimeProvider, type Timeout } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { TemplatedPathParser } from \"alepha/router\";\nimport { TopicTimeoutError } from \"../errors/TopicTimeoutError.ts\";\nimport { $subscriber } from \"../primitives/$subscriber.ts\";\nimport {\n $topic,\n type TopicHandler,\n type TopicMessage,\n type TopicMessageSchema,\n type TopicWaitOptions,\n} from \"../primitives/$topic.ts\";\n\n/**\n * Base class for topic providers.\n */\nexport abstract class TopicProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n\n /**\n * Publish a raw message to a topic.\n *\n * @param topic - The topic to publish to.\n * @param message - The message to publish.\n */\n public abstract publish(\n topic: string,\n message: string,\n options?: TopicPublishOptions,\n ): Promise<void>;\n\n /**\n * Subscribe to a topic with a raw callback.\n *\n * @param topic - The topic to subscribe to.\n * @param callback - The callback to call when a message is received.\n */\n public abstract subscribe(\n topic: string,\n callback: SubscribeCallback,\n options?: TopicSubscribeOptions,\n ): Promise<UnSubscribeFn>;\n\n /**\n * Unsubscribe from a topic.\n *\n * @param topic - The topic to unsubscribe from.\n */\n public abstract unsubscribe(topic: string): Promise<void>;\n\n /**\n * Encode and publish a typed message to a topic.\n */\n public async publishMessage<T extends TopicMessageSchema>(\n name: string,\n schema: T[\"payload\"],\n payload: TopicMessage<T>[\"payload\"],\n options?: TopicPublishOptions,\n ): Promise<void> {\n let topicName = name;\n if (options?.params) {\n const parser = new TemplatedPathParser(name);\n topicName = parser.interpolate(options.params);\n }\n\n await this.publish(\n topicName,\n JSON.stringify({\n payload: this.alepha.codec.encode(schema, payload),\n }),\n options,\n );\n }\n\n /**\n * Parse a raw message string into a typed topic message.\n */\n public parseMessage<T extends TopicMessageSchema>(\n schema: T[\"payload\"],\n message: string,\n ): TopicMessage<T> {\n const { payload } = JSON.parse(message);\n return {\n payload: this.alepha.codec.decode(\n schema,\n payload,\n ) as TopicMessage<T>[\"payload\"],\n } as TopicMessage<T>;\n }\n\n /**\n * Subscribe a typed handler to a topic, with error wrapping and message parsing.\n */\n public async subscribeHandler<T extends TopicMessageSchema>(\n name: string,\n schema: T[\"payload\"],\n handler: TopicHandler<T>,\n options?: TopicSubscribeOptions,\n ): Promise<UnSubscribeFn> {\n const parser = new TemplatedPathParser(name);\n const subscribeTopic = parser.hasParams\n ? parser.wildcardize(this.wildcardChar())\n : name;\n\n return this.subscribe(\n subscribeTopic,\n async (message, receivedTopic?: string) => {\n try {\n const parsed = this.parseMessage<T>(schema, message);\n\n if (parser.hasParams && receivedTopic) {\n const params = parser.extract(receivedTopic) ?? {};\n await handler({ ...parsed, params } as TopicMessage<T>);\n } else {\n await handler(parsed);\n }\n } catch (error) {\n this.log.error(\"Message processing has failed\", error);\n }\n },\n options,\n );\n }\n\n /**\n * Wait for a single message matching an optional filter, with timeout.\n */\n public async waitForMessage<T extends TopicMessageSchema>(\n name: string,\n schema: T[\"payload\"],\n options: TopicWaitOptions<T> = {},\n ): Promise<TopicMessage<T>> {\n const filter = options.filter ?? (() => true);\n\n return new Promise((resolve, reject) => {\n const ref: { timeout?: Timeout; clear?: () => void } = {};\n\n (async () => {\n const clear = await this.subscribe(name, (raw) => {\n const message = this.parseMessage<T>(schema, raw);\n if (!filter(message)) {\n return;\n }\n\n ref.timeout?.clear();\n if (ref.clear) {\n ref.clear();\n }\n resolve(message);\n });\n\n ref.clear = clear;\n\n const timeoutDuration = options.timeout ?? [10, \"seconds\"];\n\n ref.timeout = this.dateTimeProvider.createTimeout(() => {\n clear();\n reject(\n new TopicTimeoutError(\n name,\n this.dateTimeProvider.duration(timeoutDuration).asMilliseconds(),\n ),\n );\n }, timeoutDuration);\n })();\n });\n }\n\n /**\n * The wildcard character used for pattern subscriptions.\n * Override in subclasses for provider-specific wildcards.\n */\n protected wildcardChar(): string {\n return \"+\";\n }\n\n /**\n * Returns the list of $subscribers for this provider.\n */\n protected subscribers(): Array<() => Promise<unknown>> {\n const handlers: Array<() => Promise<unknown>> = [];\n\n const topics = this.alepha.primitives($topic);\n\n for (const topic of topics) {\n if (topic.provider !== this) {\n continue;\n }\n\n const handler = topic.options.handler;\n if (handler && topic.provider === this) {\n handlers.push(() => topic.subscribe(handler));\n }\n }\n\n const subscribers = this.alepha.primitives($subscriber);\n for (const subscriber of subscribers) {\n if (subscriber.options.topic.provider !== this) {\n continue;\n }\n\n const handler = subscriber.handler.run.bind(subscriber.handler);\n handlers.push(() => subscriber.options.topic.subscribe(handler));\n }\n\n return handlers;\n }\n}\n\nexport type SubscribeCallback = (\n message: string,\n topic?: string,\n) => Promise<void> | void;\n\nexport type UnSubscribeFn = () => Promise<void>;\n\nexport interface TopicPublishOptions {\n retain?: boolean;\n params?: Record<string, string>;\n}\n\n// biome-ignore lint/suspicious/noEmptyInterface: augmented by provider-specific modules (e.g. MqttTopicProvider)\nexport interface TopicSubscribeOptions {}\n","import { $hook } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport {\n type SubscribeCallback,\n TopicProvider,\n type TopicPublishOptions,\n type TopicSubscribeOptions,\n type UnSubscribeFn,\n} from \"./TopicProvider.ts\";\n\nexport class MemoryTopicProvider extends TopicProvider {\n protected readonly log = $logger();\n protected readonly subscriptions: Record<string, SubscribeCallback[]> = {};\n protected readonly retained: Record<string, string> = {};\n\n protected readonly start = $hook({\n on: \"start\",\n handler: async () => {\n const subscribers = this.subscribers();\n if (subscribers.length) {\n await Promise.all(subscribers.map((fn) => fn()));\n for (const subscriber of subscribers) {\n this.log.debug(`Subscribed to topic '${subscriber.name}'`);\n }\n }\n },\n });\n\n /**\n * Publish a message to a topic.\n *\n * @param topic\n * @param message\n * @param options\n */\n public async publish(\n topic: string,\n message: string,\n options?: TopicPublishOptions,\n ): Promise<void> {\n if (options?.retain) {\n this.retained[topic] = message;\n }\n\n for (const [pattern, callbacks] of Object.entries(this.subscriptions)) {\n if (this.topicMatches(pattern, topic)) {\n for (const callback of callbacks) {\n await callback(message, topic);\n }\n }\n }\n }\n\n /**\n * Subscribe to a topic.\n *\n * @param topic - The topic to subscribe to.\n * @param callback\n */\n\n public async subscribe(\n topic: string,\n callback: SubscribeCallback,\n _options?: TopicSubscribeOptions,\n ): Promise<UnSubscribeFn> {\n if (!this.subscriptions[topic]) {\n this.subscriptions[topic] = [];\n }\n\n this.subscriptions[topic].push(callback);\n\n // Deliver retained messages matching the pattern\n for (const [retainedTopic, retainedMessage] of Object.entries(\n this.retained,\n )) {\n if (this.topicMatches(topic, retainedTopic)) {\n await callback(retainedMessage, retainedTopic);\n }\n }\n\n return async () => {\n const callbacks = this.subscriptions[topic];\n if (!callbacks) {\n return;\n }\n\n this.subscriptions[topic] = callbacks.filter((cb) => cb !== callback);\n if (this.subscriptions[topic].length === 0) {\n delete this.subscriptions[topic];\n }\n };\n }\n\n /**\n * Unsubscribe from a topic.\n *\n * @param topic - The topic to unsubscribe from.\n */\n public async unsubscribe(topic: string): Promise<void> {\n delete this.subscriptions[topic];\n }\n\n /**\n * Check if a topic matches a subscription pattern.\n * Supports `+` single-level wildcard.\n */\n protected topicMatches(pattern: string, topic: string): boolean {\n if (pattern === topic) {\n return true;\n }\n\n const patternParts = pattern.split(\"/\");\n const topicParts = topic.split(\"/\");\n\n if (patternParts.length !== topicParts.length) {\n return false;\n }\n\n return patternParts.every(\n (part, i) => part === \"+\" || part === topicParts[i],\n );\n }\n}\n","import {\n createPrimitive,\n KIND,\n Primitive,\n type Service,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport { MemoryTopicProvider } from \"../providers/MemoryTopicProvider.ts\";\nimport {\n TopicProvider,\n type TopicPublishOptions,\n type UnSubscribeFn,\n} from \"../providers/TopicProvider.ts\";\n\n/**\n * Creates a topic primitive for publish/subscribe messaging and event-driven architecture.\n *\n * Enables decoupled communication through a pub/sub pattern where publishers send messages\n * and multiple subscribers receive them. Supports type-safe messages, real-time delivery,\n * event filtering, and pluggable backends (memory, Redis, custom providers).\n *\n * **Use Cases**: User notifications, real-time chat, event broadcasting, microservice communication\n *\n * @example\n * ```ts\n * class NotificationService {\n * userActivity = $topic({\n * name: \"user-activity\",\n * schema: {\n * payload: t.object({\n * userId: t.text(),\n * action: t.enum([\"login\", \"logout\", \"purchase\"]),\n * timestamp: t.number()\n * })\n * },\n * handler: async (message) => {\n * console.log(`User ${message.payload.userId}: ${message.payload.action}`);\n * }\n * });\n *\n * async trackLogin(userId: string) {\n * await this.userActivity.publish({ userId, action: \"login\", timestamp: Date.now() });\n * }\n *\n * async subscribeToEvents() {\n * await this.userActivity.subscribe(async (message) => {\n * // Additional subscriber logic\n * });\n * }\n * }\n * ```\n */\nexport const $topic = <T extends TopicMessageSchema>(\n options: TopicPrimitiveOptions<T>,\n): TopicPrimitive<T> => {\n return createPrimitive(TopicPrimitive<T>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface TopicPrimitiveOptions<T extends TopicMessageSchema> {\n /**\n * Unique name identifier for the topic.\n *\n * This name is used for:\n * - Topic identification across the pub/sub system\n * - Message routing between publishers and subscribers\n * - Logging and debugging topic-related operations\n * - Provider-specific topic management (channels, keys, etc.)\n *\n * If not provided, defaults to the property key where the topic is declared.\n *\n * **Naming Conventions**:\n * - Use descriptive, hierarchical names: \"user.activity\", \"order.events\"\n * - Avoid spaces and special characters\n * - Consider using dot notation for categorization\n * - Keep names concise but meaningful\n *\n * @example \"user-activity\"\n * @example \"chat.messages\"\n * @example \"system.health.checks\"\n * @example \"payment.webhooks\"\n */\n name?: string;\n\n /**\n * Human-readable description of the topic's purpose and usage.\n *\n * Used for:\n * - Documentation generation and API references\n * - Developer onboarding and understanding\n * - Monitoring dashboards and admin interfaces\n * - Team communication about system architecture\n *\n * **Description Best Practices**:\n * - Explain what events/messages this topic handles\n * - Mention key use cases and subscribers\n * - Include any important timing or ordering guarantees\n * - Note any special processing requirements\n *\n * @example \"Real-time user activity events for analytics and notifications\"\n * @example \"Order lifecycle events from creation to delivery\"\n * @example \"Chat messages broadcast to all room participants\"\n * @example \"System health checks and service status updates\"\n */\n description?: string;\n\n /**\n * Topic provider configuration for message storage and delivery.\n *\n * Options:\n * - **\"memory\"**: In-memory provider (default for development, lost on restart)\n * - **Service<TopicProvider>**: Custom provider class (e.g., RedisTopicProvider)\n * - **undefined**: Uses the default topic provider from dependency injection\n *\n * **Provider Selection Guidelines**:\n * - **Development**: Use \"memory\" for fast, simple testing without external dependencies\n * - **Production**: Use Redis or message brokers for persistence and scalability\n * - **Distributed systems**: Use Redis/RabbitMQ for cross-service communication\n * - **High-throughput**: Use specialized providers with connection pooling\n * - **Real-time**: Ensure provider supports low-latency message delivery\n *\n * **Provider Capabilities**:\n * - Message persistence and durability\n * - Subscriber management and connection handling\n * - Message ordering and delivery guarantees\n * - Horizontal scaling and load distribution\n *\n * @default Uses injected TopicProvider\n * @example \"memory\"\n * @example RedisTopicProvider\n * @example RabbitMQTopicProvider\n */\n provider?: \"memory\" | Service<TopicProvider>;\n\n /**\n * TypeBox schema defining the structure of messages published to this topic.\n *\n * The schema must include:\n * - **payload**: Required schema for the main message data\n * - **headers**: Optional schema for message metadata\n *\n * This schema:\n * - Validates all messages published to the topic\n * - Provides full TypeScript type inference for subscribers\n * - Ensures type safety between publishers and subscribers\n * - Enables automatic serialization/deserialization\n *\n * **Schema Design Best Practices**:\n * - Keep payload schemas focused and cohesive\n * - Use optional fields for data that might not always be present\n * - Include timestamp fields for event ordering\n * - Consider versioning for schema evolution\n * - Use union types for different event types in the same topic\n *\n * @example\n * ```ts\n * {\n * payload: t.object({\n * eventId: t.text(),\n * eventType: t.enum([\"created\", \"updated\"]),\n * data: t.record(t.text(), t.any()),\n * timestamp: t.number(),\n * userId: t.optional(t.text())\n * }),\n * headers: t.optional(t.object({\n * source: t.text(),\n * correlationId: t.text()\n * }))\n * }\n * ```\n */\n schema: T;\n\n /**\n * Default subscriber handler function that processes messages published to this topic.\n *\n * This handler:\n * - Automatically subscribes when the topic is initialized\n * - Receives all messages published to the topic\n * - Runs for every message without additional subscription setup\n * - Can be supplemented with additional subscribers via `subscribe()` method\n * - Should handle errors gracefully to avoid breaking other subscribers\n *\n * **Handler Design Guidelines**:\n * - Keep handlers focused on a single responsibility\n * - Use proper error handling and logging\n * - Consider performance impact for high-frequency topics\n * - Make handlers idempotent when possible\n * - Validate business rules within the handler logic\n * - Log important processing steps for debugging\n *\n * **Error Handling Strategy**:\n * - Log errors but don't re-throw to avoid affecting other subscribers\n * - Use try-catch blocks for external service calls\n * - Consider implementing circuit breakers for resilience\n * - Monitor error rates and patterns for system health\n *\n * @param message - The topic message with validated payload and headers\n * @param message.payload - The typed message data based on the schema\n * @returns Promise that resolves when processing is complete\n *\n * @example\n * ```ts\n * handler: async (message) => {\n * const { eventType, data, timestamp } = message.payload;\n *\n * try {\n * // Log message receipt\n * this.logger.info(`Processing ${eventType} event`, { timestamp, data });\n *\n * // Process based on event type\n * switch (eventType) {\n * case \"created\":\n * await this.handleCreation(data);\n * break;\n * case \"updated\":\n * await this.handleUpdate(data);\n * break;\n * default:\n * this.logger.warn(`Unknown event type: ${eventType}`);\n * }\n *\n * this.logger.info(`Successfully processed ${eventType} event`);\n *\n * } catch (error) {\n * // Log error but don't re-throw to avoid affecting other subscribers\n * this.logger.error(`Failed to process ${eventType} event`, {\n * error: error.message,\n * eventType,\n * timestamp,\n * data\n * });\n * }\n * }\n * ```\n */\n handler?: TopicHandler<T>;\n\n /**\n * Whether the last published message should be retained and delivered to new subscribers.\n *\n * When enabled, the provider stores the last message for this topic.\n * New subscribers immediately receive the retained message upon subscribing.\n *\n * Supported by Memory, Redis, and MQTT providers.\n *\n * @default false\n */\n retain?: boolean;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class TopicPrimitive<T extends TopicMessageSchema> extends Primitive<\n TopicPrimitiveOptions<T>\n> {\n public readonly provider = this.$provider();\n\n public get name(): string {\n return this.options.name || this.config.propertyKey;\n }\n\n public async publish(\n message: T extends { params: TSchema }\n ? { params: Static<T[\"params\"]>; payload: TopicMessage<T>[\"payload\"] }\n : TopicMessage<T>[\"payload\"],\n ): Promise<void> {\n const hasParams = this.options.schema.params;\n const payload = hasParams ? (message as any).payload : message;\n const params = hasParams ? (message as any).params : undefined;\n\n await this.provider.publishMessage<T>(\n this.name,\n this.options.schema.payload,\n payload,\n {\n retain: this.options.retain,\n mqtt: (this.options as any).mqtt,\n params,\n } as TopicPublishOptions,\n );\n }\n\n public async subscribe(handler: TopicHandler<T>): Promise<UnSubscribeFn> {\n return this.provider.subscribeHandler<T>(\n this.name,\n this.options.schema.payload,\n handler,\n { mqtt: (this.options as any).mqtt },\n );\n }\n\n public async wait(\n options: TopicWaitOptions<T> = {},\n ): Promise<TopicMessage<T>> {\n return this.provider.waitForMessage<T>(\n this.name,\n this.options.schema.payload,\n options,\n );\n }\n\n protected $provider(): TopicProvider {\n if (!this.options.provider) {\n return this.alepha.inject(TopicProvider);\n }\n\n if (this.options.provider === \"memory\") {\n return this.alepha.inject(MemoryTopicProvider);\n }\n\n return this.alepha.inject(this.options.provider);\n }\n}\n\n$topic[KIND] = TopicPrimitive;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface TopicMessage<T extends TopicMessageSchema> {\n payload: Static<T[\"payload\"]>;\n params: T extends { params: TSchema } ? Static<T[\"params\"]> : never;\n}\n\nexport interface TopicWaitOptions<T extends TopicMessageSchema> {\n timeout?: DurationLike;\n filter?: (message: { payload: Static<T[\"payload\"]> }) => boolean;\n}\n\nexport interface TopicMessageSchema {\n payload: TSchema;\n params?: TSchema;\n}\n\nexport type TopicHandler<T extends TopicMessageSchema = TopicMessageSchema> = (\n message: TopicMessage<T>,\n) => unknown;\n","import { $module, type Alepha } from \"alepha\";\nimport { $subscriber } from \"./primitives/$subscriber.ts\";\nimport { $topic } from \"./primitives/$topic.ts\";\nimport { MemoryTopicProvider } from \"./providers/MemoryTopicProvider.ts\";\nimport { TopicProvider } from \"./providers/TopicProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./errors/TopicTimeoutError.ts\";\nexport * from \"./primitives/$subscriber.ts\";\nexport * from \"./primitives/$topic.ts\";\nexport * from \"./providers/MemoryTopicProvider.ts\";\nexport * from \"./providers/TopicProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Publish/subscribe messaging for event-driven architectures.\n *\n * **Features:**\n * - Pub/sub topics with type-safe messages\n * - Topic subscription handlers\n * - Multiple subscriber support\n * - Message filtering and routing\n * - Providers: Memory (dev), Redis (production)\n *\n * @module alepha.topic\n */\nexport const AlephaTopic = $module({\n name: \"alepha.topic\",\n primitives: [$topic, $subscriber],\n services: [TopicProvider],\n variants: [MemoryTopicProvider],\n register: (alepha: Alepha) =>\n alepha.with({\n optional: true,\n provide: TopicProvider,\n use: MemoryTopicProvider,\n }),\n});\n"],"mappings":"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"}
@@ -1,16 +1,13 @@
1
- import * as _$alepha from "alepha";
2
1
  import { Alepha, Static } from "alepha";
3
2
  import { SubscribeCallback, TopicProvider, TopicPublishOptions, TopicSubscribeOptions, UnSubscribeFn } from "alepha/topic";
4
- import * as _$alepha_logger0 from "alepha/logger";
5
3
  import { RedisProvider, RedisSubscriberProvider } from "alepha/redis";
6
- import * as _$typebox from "typebox";
7
4
 
8
5
  //#region ../../src/topic/redis/providers/RedisTopicProvider.d.ts
9
6
  /**
10
7
  * Redis topic configuration atom.
11
8
  */
12
- declare const redisTopicOptions: _$alepha.Atom<_$typebox.TObject<{
13
- prefix: _$typebox.TString;
9
+ declare const redisTopicOptions: import("alepha").Atom<import("typebox").TObject<{
10
+ prefix: import("typebox").TString;
14
11
  }>, "alepha.topic.redis.options">;
15
12
  type RedisTopicOptions = Static<typeof redisTopicOptions.schema>;
16
13
  declare module "alepha" {
@@ -25,8 +22,8 @@ declare class RedisTopicProvider extends TopicProvider {
25
22
  protected readonly alepha: Alepha;
26
23
  protected readonly redisProvider: RedisProvider;
27
24
  protected readonly redisSubscriberProvider: RedisSubscriberProvider;
28
- protected readonly log: _$alepha_logger0.Logger;
29
- protected readonly start: _$alepha.HookPrimitive<"start">;
25
+ protected readonly log: import("alepha/logger").Logger;
26
+ protected readonly start: import("alepha").HookPrimitive<"start">;
30
27
  protected wildcardChar(): string;
31
28
  prefix(queue: string): string;
32
29
  /**
@@ -50,7 +47,7 @@ declare class RedisTopicProvider extends TopicProvider {
50
47
  * @see {@link RedisTopicProvider}
51
48
  * @module alepha.topic.redis
52
49
  */
53
- declare const AlephaTopicRedis: _$alepha.Service<_$alepha.Module>;
50
+ declare const AlephaTopicRedis: import("alepha").Service<import("alepha").Module>;
54
51
  //#endregion
55
52
  export { AlephaTopicRedis, RedisTopicOptions, RedisTopicProvider, redisTopicOptions };
56
53
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/topic/redis/providers/RedisTopicProvider.ts","../../../src/topic/redis/index.ts"],"mappings":";;;;;;;;;;;cAgBa,iBAAA,EAAiB,QAAA,CAAA,IAAA,WAAA,OAAA;UAW5B,SAAA,CAAA,OAAA;AAAA;AAAA,KAEU,iBAAA,GAAoB,MAAA,QAAc,iBAAA,CAAkB,MAAA;AAAA;EAAA,UAGpD,KAAA;IAAA,CACP,iBAAA,CAAkB,GAAA,GAAM,iBAAA;EAAA;AAAA;AAAA,cAMhB,kBAAA,SAA2B,aAAA;EAAA,mBACnB,OAAA,EAAO,QAAA;;;qBACP,MAAA,EAAM,MAAA;EAAA,mBACN,aAAA,EAAa,aAAA;EAAA,mBACb,uBAAA,EAAuB,uBAAA;EAAA,mBAEvB,GAAA,EAFuB,gBAAA,CAEpB,MAAA;EAAA,mBAEH,KAAA,EAFG,QAAA,CAEE,aAAA;EAAA,UAaL,YAAA,CAAA;EAIZ,MAAA,CAAO,KAAA;EAnCgB;;;EA0CjB,OAAA,CACX,KAAA,UACA,OAAA,UACA,OAAA,GAAU,mBAAA,GACT,OAAA;EA9CiE;AAAE;;EAwDzD,SAAA,CACX,IAAA,UACA,QAAA,EAAU,iBAAA,EACV,QAAA,GAAW,qBAAA,GACV,OAAA,CAAQ,aAAA;EAxDa;;;EA0EX,WAAA,CACX,IAAA,UACA,QAAA,GAAW,iBAAA,GACV,OAAA;AAAA;;;;;;;;;cC9FQ,gBAAA,EAAgB,QAAA,CAAA,OAAA,CAW3B,QAAA,CAX2B,MAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/topic/redis/providers/RedisTopicProvider.ts","../../../src/topic/redis/index.ts"],"mappings":";;;;;;;AAgBA;cAAa,iBAAA,mBAAiB,IAAA,mBAAA,OAAA;;;KAalB,iBAAA,GAAoB,MAAM,QAAQ,iBAAA,CAAkB,MAAA;AAAA;EAAA,UAGpD,KAAA;IAAA,CACP,iBAAA,CAAkB,GAAG,GAAG,iBAAA;EAAA;AAAA;AAAA,cAMhB,kBAAA,SAA2B,aAAA;EAAA,mBACnB,OAAA,EAAO,QAAA;;;qBACP,MAAA,EAAM,MAAA;EAAA,mBACN,aAAA,EAAa,aAAA;EAAA,mBACb,uBAAA,EAAuB,uBAAA;EAAA,mBAEvB,GAAA,0BAAG,MAAA;EAAA,mBAEH,KAAA,mBAAK,aAAA;EAAA,UAaL,YAAA;EAIZ,MAAA,CAAO,KAAA;;;;EAOD,OAAA,CACX,KAAA,UACA,OAAA,UACA,OAAA,GAAU,mBAAA,GACT,OAAA;EA1CkB;;;EAoDR,SAAA,CACX,IAAA,UACA,QAAA,EAAU,iBAAA,EACV,QAAA,GAAW,qBAAA,GACV,OAAA,CAAQ,aAAA;EAxDiC;AAAA;AAM9C;EAoEe,WAAA,CACX,IAAA,UACA,QAAA,GAAW,iBAAA,GACV,OAAA;AAAA;;;;;;;AA9FL;;cCAa,gBAAA,mBAAgB,OAAA,kBAAA,MAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/topic/redis/providers/RedisTopicProvider.ts","../../../src/topic/redis/index.ts"],"sourcesContent":["import { $atom, $hook, $inject, $state, Alepha, type Static, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { RedisProvider, RedisSubscriberProvider } from \"alepha/redis\";\nimport {\n type SubscribeCallback,\n TopicProvider,\n type TopicPublishOptions,\n type TopicSubscribeOptions,\n type UnSubscribeFn,\n} from \"alepha/topic\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Redis topic configuration atom.\n */\nexport const redisTopicOptions = $atom({\n name: \"alepha.topic.redis.options\",\n schema: t.object({\n prefix: t.text({\n default: \"topic\",\n description: \"Prefix for all topic channels in Redis.\",\n }),\n }),\n default: {\n prefix: \"topic\",\n },\n});\n\nexport type RedisTopicOptions = Static<typeof redisTopicOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [redisTopicOptions.key]: RedisTopicOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class RedisTopicProvider extends TopicProvider {\n protected readonly options = $state(redisTopicOptions);\n protected readonly alepha = $inject(Alepha);\n protected readonly redisProvider = $inject(RedisProvider);\n protected readonly redisSubscriberProvider = $inject(RedisSubscriberProvider);\n\n protected readonly log = $logger();\n\n protected readonly start = $hook({\n on: \"start\",\n handler: async () => {\n const subscribers = this.subscribers();\n if (subscribers.length) {\n await Promise.all(subscribers.map((fn) => fn()));\n for (const subscriber of subscribers) {\n this.log.debug(`Subscribed to topic '${subscriber.name}'`);\n }\n }\n },\n });\n\n protected override wildcardChar(): string {\n return \"*\";\n }\n\n public prefix(queue: string): string {\n return `${this.options.prefix}:${queue}`;\n }\n\n /**\n * Publish a message to a topic.\n */\n public async publish(\n topic: string,\n message: string,\n options?: TopicPublishOptions,\n ): Promise<void> {\n if (options?.retain) {\n await this.redisProvider.set(`${this.prefix(topic)}:retained`, message);\n }\n await this.redisProvider.publish(this.prefix(topic), message);\n }\n\n /**\n * Subscribe to a topic.\n */\n public async subscribe(\n name: string,\n callback: SubscribeCallback,\n _options?: TopicSubscribeOptions,\n ): Promise<UnSubscribeFn> {\n const topic = this.prefix(name);\n\n // Subscribe first to avoid message loss window\n await this.redisSubscriberProvider.subscribe(topic, callback);\n\n // Then deliver retained message if exists\n const retained = await this.redisProvider.get(`${topic}:retained`);\n if (retained) {\n await callback(retained.toString());\n }\n\n return () => this.unsubscribe(name, callback);\n }\n\n /**\n * Unsubscribe from a topic.\n */\n public async unsubscribe(\n name: string,\n callback?: SubscribeCallback,\n ): Promise<void> {\n const topic = this.prefix(name);\n\n await this.redisSubscriberProvider.unsubscribe(topic, callback);\n }\n}\n","import { $module, type Alepha } from \"alepha\";\nimport { AlephaTopic, TopicProvider } from \"alepha/topic\";\nimport { RedisTopicProvider } from \"./providers/RedisTopicProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./providers/RedisTopicProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Plugin for Alepha Topic that provides Redis pub/sub capabilities.\n *\n * @see {@link RedisTopicProvider}\n * @module alepha.topic.redis\n */\nexport const AlephaTopicRedis = $module({\n name: \"alepha.topic.redis\",\n services: [RedisTopicProvider],\n register: (alepha: Alepha): Alepha =>\n alepha\n .with({\n optional: true,\n provide: TopicProvider,\n use: RedisTopicProvider,\n })\n .with(AlephaTopic),\n});\n"],"mappings":";;;;;;;;AAgBA,MAAa,oBAAoB,MAAM;CACrC,MAAM;CACN,QAAQ,EAAE,OAAO,EACf,QAAQ,EAAE,KAAK;EACb,SAAS;EACT,aAAa;EACd,CAAC,EACH,CAAC;CACF,SAAS,EACP,QAAQ,SACT;CACF,CAAC;AAYF,IAAa,qBAAb,cAAwC,cAAc;CACpD,UAA6B,OAAO,kBAAkB;CACtD,SAA4B,QAAQ,OAAO;CAC3C,gBAAmC,QAAQ,cAAc;CACzD,0BAA6C,QAAQ,wBAAwB;CAE7E,MAAyB,SAAS;CAElC,QAA2B,MAAM;EAC/B,IAAI;EACJ,SAAS,YAAY;GACnB,MAAM,cAAc,KAAK,aAAa;GACtC,IAAI,YAAY,QAAQ;IACtB,MAAM,QAAQ,IAAI,YAAY,KAAK,OAAO,IAAI,CAAC,CAAC;IAChD,KAAK,MAAM,cAAc,aACvB,KAAK,IAAI,MAAM,wBAAwB,WAAW,KAAK,GAAG;;;EAIjE,CAAC;CAEF,eAA0C;EACxC,OAAO;;CAGT,OAAc,OAAuB;EACnC,OAAO,GAAG,KAAK,QAAQ,OAAO,GAAG;;;;;CAMnC,MAAa,QACX,OACA,SACA,SACe;EACf,IAAI,SAAS,QACX,MAAM,KAAK,cAAc,IAAI,GAAG,KAAK,OAAO,MAAM,CAAC,YAAY,QAAQ;EAEzE,MAAM,KAAK,cAAc,QAAQ,KAAK,OAAO,MAAM,EAAE,QAAQ;;;;;CAM/D,MAAa,UACX,MACA,UACA,UACwB;EACxB,MAAM,QAAQ,KAAK,OAAO,KAAK;EAG/B,MAAM,KAAK,wBAAwB,UAAU,OAAO,SAAS;EAG7D,MAAM,WAAW,MAAM,KAAK,cAAc,IAAI,GAAG,MAAM,WAAW;EAClE,IAAI,UACF,MAAM,SAAS,SAAS,UAAU,CAAC;EAGrC,aAAa,KAAK,YAAY,MAAM,SAAS;;;;;CAM/C,MAAa,YACX,MACA,UACe;EACf,MAAM,QAAQ,KAAK,OAAO,KAAK;EAE/B,MAAM,KAAK,wBAAwB,YAAY,OAAO,SAAS;;;;;;;;;;;ACjGnE,MAAa,mBAAmB,QAAQ;CACtC,MAAM;CACN,UAAU,CAAC,mBAAmB;CAC9B,WAAW,WACT,OACG,KAAK;EACJ,UAAU;EACV,SAAS;EACT,KAAK;EACN,CAAC,CACD,KAAK,YAAY;CACvB,CAAC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/topic/redis/providers/RedisTopicProvider.ts","../../../src/topic/redis/index.ts"],"sourcesContent":["import { $atom, $hook, $inject, $state, Alepha, type Static, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { RedisProvider, RedisSubscriberProvider } from \"alepha/redis\";\nimport {\n type SubscribeCallback,\n TopicProvider,\n type TopicPublishOptions,\n type TopicSubscribeOptions,\n type UnSubscribeFn,\n} from \"alepha/topic\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Redis topic configuration atom.\n */\nexport const redisTopicOptions = $atom({\n name: \"alepha.topic.redis.options\",\n schema: t.object({\n prefix: t.text({\n default: \"topic\",\n description: \"Prefix for all topic channels in Redis.\",\n }),\n }),\n default: {\n prefix: \"topic\",\n },\n});\n\nexport type RedisTopicOptions = Static<typeof redisTopicOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [redisTopicOptions.key]: RedisTopicOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class RedisTopicProvider extends TopicProvider {\n protected readonly options = $state(redisTopicOptions);\n protected readonly alepha = $inject(Alepha);\n protected readonly redisProvider = $inject(RedisProvider);\n protected readonly redisSubscriberProvider = $inject(RedisSubscriberProvider);\n\n protected readonly log = $logger();\n\n protected readonly start = $hook({\n on: \"start\",\n handler: async () => {\n const subscribers = this.subscribers();\n if (subscribers.length) {\n await Promise.all(subscribers.map((fn) => fn()));\n for (const subscriber of subscribers) {\n this.log.debug(`Subscribed to topic '${subscriber.name}'`);\n }\n }\n },\n });\n\n protected override wildcardChar(): string {\n return \"*\";\n }\n\n public prefix(queue: string): string {\n return `${this.options.prefix}:${queue}`;\n }\n\n /**\n * Publish a message to a topic.\n */\n public async publish(\n topic: string,\n message: string,\n options?: TopicPublishOptions,\n ): Promise<void> {\n if (options?.retain) {\n await this.redisProvider.set(`${this.prefix(topic)}:retained`, message);\n }\n await this.redisProvider.publish(this.prefix(topic), message);\n }\n\n /**\n * Subscribe to a topic.\n */\n public async subscribe(\n name: string,\n callback: SubscribeCallback,\n _options?: TopicSubscribeOptions,\n ): Promise<UnSubscribeFn> {\n const topic = this.prefix(name);\n\n // Subscribe first to avoid message loss window\n await this.redisSubscriberProvider.subscribe(topic, callback);\n\n // Then deliver retained message if exists\n const retained = await this.redisProvider.get(`${topic}:retained`);\n if (retained) {\n await callback(retained.toString());\n }\n\n return () => this.unsubscribe(name, callback);\n }\n\n /**\n * Unsubscribe from a topic.\n */\n public async unsubscribe(\n name: string,\n callback?: SubscribeCallback,\n ): Promise<void> {\n const topic = this.prefix(name);\n\n await this.redisSubscriberProvider.unsubscribe(topic, callback);\n }\n}\n","import { $module, type Alepha } from \"alepha\";\nimport { AlephaTopic, TopicProvider } from \"alepha/topic\";\nimport { RedisTopicProvider } from \"./providers/RedisTopicProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./providers/RedisTopicProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Plugin for Alepha Topic that provides Redis pub/sub capabilities.\n *\n * @see {@link RedisTopicProvider}\n * @module alepha.topic.redis\n */\nexport const AlephaTopicRedis = $module({\n name: \"alepha.topic.redis\",\n services: [RedisTopicProvider],\n register: (alepha: Alepha): Alepha =>\n alepha\n .with({\n optional: true,\n provide: TopicProvider,\n use: RedisTopicProvider,\n })\n .with(AlephaTopic),\n});\n"],"mappings":";;;;;;;;AAgBA,MAAa,oBAAoB,MAAM;CACrC,MAAM;CACN,QAAQ,EAAE,OAAO,EACf,QAAQ,EAAE,KAAK;EACb,SAAS;EACT,aAAa;CACf,CAAC,EACH,CAAC;CACD,SAAS,EACP,QAAQ,QACV;AACF,CAAC;AAYD,IAAa,qBAAb,cAAwC,cAAc;CACpD,UAA6B,OAAO,iBAAiB;CACrD,SAA4B,QAAQ,MAAM;CAC1C,gBAAmC,QAAQ,aAAa;CACxD,0BAA6C,QAAQ,uBAAuB;CAE5E,MAAyB,QAAQ;CAEjC,QAA2B,MAAM;EAC/B,IAAI;EACJ,SAAS,YAAY;GACnB,MAAM,cAAc,KAAK,YAAY;GACrC,IAAI,YAAY,QAAQ;IACtB,MAAM,QAAQ,IAAI,YAAY,KAAK,OAAO,GAAG,CAAC,CAAC;IAC/C,KAAK,MAAM,cAAc,aACvB,KAAK,IAAI,MAAM,wBAAwB,WAAW,KAAK,EAAE;GAE7D;EACF;CACF,CAAC;CAED,eAA0C;EACxC,OAAO;CACT;CAEA,OAAc,OAAuB;EACnC,OAAO,GAAG,KAAK,QAAQ,OAAO,GAAG;CACnC;;;;CAKA,MAAa,QACX,OACA,SACA,SACe;EACf,IAAI,SAAS,QACX,MAAM,KAAK,cAAc,IAAI,GAAG,KAAK,OAAO,KAAK,EAAE,YAAY,OAAO;EAExE,MAAM,KAAK,cAAc,QAAQ,KAAK,OAAO,KAAK,GAAG,OAAO;CAC9D;;;;CAKA,MAAa,UACX,MACA,UACA,UACwB;EACxB,MAAM,QAAQ,KAAK,OAAO,IAAI;EAG9B,MAAM,KAAK,wBAAwB,UAAU,OAAO,QAAQ;EAG5D,MAAM,WAAW,MAAM,KAAK,cAAc,IAAI,GAAG,MAAM,UAAU;EACjE,IAAI,UACF,MAAM,SAAS,SAAS,SAAS,CAAC;EAGpC,aAAa,KAAK,YAAY,MAAM,QAAQ;CAC9C;;;;CAKA,MAAa,YACX,MACA,UACe;EACf,MAAM,QAAQ,KAAK,OAAO,IAAI;EAE9B,MAAM,KAAK,wBAAwB,YAAY,OAAO,QAAQ;CAChE;AACF;;;;;;;;;ACnGA,MAAa,mBAAmB,QAAQ;CACtC,MAAM;CACN,UAAU,CAAC,kBAAkB;CAC7B,WAAW,WACT,OACG,KAAK;EACJ,UAAU;EACV,SAAS;EACT,KAAK;CACP,CAAC,EACA,KAAK,WAAW;AACvB,CAAC"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "alepha",
3
3
  "description": "Easy-to-use modern TypeScript framework for building many kind of applications.",
4
4
  "author": "Feunard",
5
- "version": "0.21.2",
5
+ "version": "0.23.0",
6
6
  "type": "module",
7
7
  "engines": {
8
8
  "node": ">=22.0.0",
@@ -21,24 +21,25 @@
21
21
  "CLAUDE.md"
22
22
  ],
23
23
  "dependencies": {
24
- "@biomejs/biome": "^2.4.15",
25
- "@redis/client": "^5.12.1",
24
+ "@biomejs/biome": "^2.4.16",
25
+ "@cloudflare/containers": "^0.3.5",
26
+ "@redis/client": "^6.0.0",
26
27
  "@vitejs/plugin-react": "^6.0.2",
27
- "dayjs": "^1.11.20",
28
+ "dayjs": "^1.11.21",
28
29
  "drizzle-kit": "^0.31.10",
29
30
  "drizzle-orm": "^0.45.2",
30
31
  "postgres": "^3.4.9",
31
- "s3mini": "^0.9.4",
32
- "tsx": "^4.22.3",
33
- "typebox": "^1.1.38",
32
+ "s3mini": "^0.9.5",
33
+ "tsx": "^4.22.4",
34
+ "typebox": "^1.1.39",
34
35
  "typescript": "^6.0.3",
35
- "vite": "^8.0.13",
36
+ "vite": "^8.0.14",
36
37
  "vite-bundle-analyzer": "^1.3.8",
37
38
  "vitest": "^4.1.7"
38
39
  },
39
40
  "devDependencies": {
40
- "@cloudflare/workers-types": "^4.20260520.1",
41
- "@electric-sql/pglite": "^0.4.5",
41
+ "@cloudflare/workers-types": "^4.20260531.1",
42
+ "@electric-sql/pglite": "^0.4.6",
42
43
  "@faker-js/faker": "^10.4.0",
43
44
  "@tailwindcss/vite": "^4.3.0",
44
45
  "@testing-library/dom": "^10.4.1",
@@ -51,15 +52,14 @@
51
52
  "cron-schedule": "^6.0.0",
52
53
  "jose": "^6.2.3",
53
54
  "jsdom": "^29.1.1",
54
- "nodemailer": "^8.0.7",
55
+ "nodemailer": "^8.0.10",
55
56
  "openid-client": "^6.8.4",
56
57
  "prom-client": "^15.1.3",
57
58
  "react": "^19.2.6",
58
59
  "react-dom": "^19.2.6",
59
- "shadcn": "^4.7.0",
60
60
  "swagger-ui-dist": "^5.32.6",
61
61
  "tailwindcss": "^4.3.0",
62
- "tsdown": "^0.22.0"
62
+ "tsdown": "^0.22.1"
63
63
  },
64
64
  "peerDependencies": {
65
65
  "react": "^19",
@@ -174,6 +174,12 @@
174
174
  "import": "./dist/api/verifications/index.js",
175
175
  "default": "./dist/api/verifications/index.js"
176
176
  },
177
+ "./background": {
178
+ "types": "./dist/background/index.d.ts",
179
+ "workerd": "./dist/background/index.workerd.js",
180
+ "import": "./dist/background/index.js",
181
+ "default": "./dist/background/index.js"
182
+ },
177
183
  "./batch": {
178
184
  "types": "./dist/batch/index.d.ts",
179
185
  "import": "./dist/batch/index.js",
@@ -236,6 +242,11 @@
236
242
  "import": "./dist/cli/platform/index.js",
237
243
  "default": "./dist/cli/platform/index.js"
238
244
  },
245
+ "./cli/platform-lib": {
246
+ "types": "./dist/cli/platform-lib/index.d.ts",
247
+ "import": "./dist/cli/platform-lib/index.js",
248
+ "default": "./dist/cli/platform-lib/index.js"
249
+ },
239
250
  "./cli/vendor": {
240
251
  "types": "./dist/cli/vendor/index.d.ts",
241
252
  "import": "./dist/cli/vendor/index.js",
@@ -246,11 +257,11 @@
246
257
  "import": "./dist/command/index.js",
247
258
  "default": "./dist/command/index.js"
248
259
  },
249
- "./containers": {
250
- "types": "./dist/containers/core/index.d.ts",
251
- "workerd": "./dist/containers/core/index.workerd.js",
252
- "import": "./dist/containers/core/index.js",
253
- "default": "./dist/containers/core/index.js"
260
+ "./container": {
261
+ "types": "./dist/container/core/index.d.ts",
262
+ "workerd": "./dist/container/core/index.workerd.js",
263
+ "import": "./dist/container/core/index.js",
264
+ "default": "./dist/container/core/index.js"
254
265
  },
255
266
  ".": {
256
267
  "types": "./dist/core/index.d.ts",
@@ -384,6 +395,13 @@
384
395
  "import": "./dist/react/router/index.js",
385
396
  "default": "./dist/react/router/index.js"
386
397
  },
398
+ "./react/sitemap": {
399
+ "types": "./dist/react/sitemap/index.d.ts",
400
+ "react-native": "./dist/react/sitemap/index.browser.js",
401
+ "browser": "./dist/react/sitemap/index.browser.js",
402
+ "import": "./dist/react/sitemap/index.js",
403
+ "default": "./dist/react/sitemap/index.js"
404
+ },
387
405
  "./react/testing": {
388
406
  "types": "./dist/react/testing/index.d.ts",
389
407
  "import": "./dist/react/testing/index.js",
@@ -598,6 +616,12 @@
598
616
  "import": "./dist/api/verifications/index.js",
599
617
  "default": "./dist/api/verifications/index.js"
600
618
  },
619
+ "./background": {
620
+ "types": "./dist/background/index.d.ts",
621
+ "workerd": "./dist/background/index.workerd.js",
622
+ "import": "./dist/background/index.js",
623
+ "default": "./dist/background/index.js"
624
+ },
601
625
  "./batch": {
602
626
  "types": "./dist/batch/index.d.ts",
603
627
  "import": "./dist/batch/index.js",
@@ -660,6 +684,11 @@
660
684
  "import": "./dist/cli/platform/index.js",
661
685
  "default": "./dist/cli/platform/index.js"
662
686
  },
687
+ "./cli/platform-lib": {
688
+ "types": "./dist/cli/platform-lib/index.d.ts",
689
+ "import": "./dist/cli/platform-lib/index.js",
690
+ "default": "./dist/cli/platform-lib/index.js"
691
+ },
663
692
  "./cli/vendor": {
664
693
  "types": "./dist/cli/vendor/index.d.ts",
665
694
  "import": "./dist/cli/vendor/index.js",
@@ -670,11 +699,11 @@
670
699
  "import": "./dist/command/index.js",
671
700
  "default": "./dist/command/index.js"
672
701
  },
673
- "./containers": {
674
- "types": "./dist/containers/core/index.d.ts",
675
- "workerd": "./dist/containers/core/index.workerd.js",
676
- "import": "./dist/containers/core/index.js",
677
- "default": "./dist/containers/core/index.js"
702
+ "./container": {
703
+ "types": "./dist/container/core/index.d.ts",
704
+ "workerd": "./dist/container/core/index.workerd.js",
705
+ "import": "./dist/container/core/index.js",
706
+ "default": "./dist/container/core/index.js"
678
707
  },
679
708
  ".": {
680
709
  "types": "./dist/core/index.d.ts",
@@ -808,6 +837,13 @@
808
837
  "import": "./dist/react/router/index.js",
809
838
  "default": "./dist/react/router/index.js"
810
839
  },
840
+ "./react/sitemap": {
841
+ "types": "./dist/react/sitemap/index.d.ts",
842
+ "react-native": "./dist/react/sitemap/index.browser.js",
843
+ "browser": "./dist/react/sitemap/index.browser.js",
844
+ "import": "./dist/react/sitemap/index.js",
845
+ "default": "./dist/react/sitemap/index.js"
846
+ },
811
847
  "./react/testing": {
812
848
  "types": "./dist/react/testing/index.d.ts",
813
849
  "import": "./dist/react/testing/index.js",
@@ -115,136 +115,44 @@ describe("alepha/api/audits - AuditService", () => {
115
115
  });
116
116
  });
117
117
 
118
- describe("recordAuth", () => {
119
- it("should record login event", async ({ expect }) => {
118
+ describe("severity from success", () => {
119
+ it("defaults a failed audit to warning severity", async ({ expect }) => {
120
120
  const { auditService } = await setup();
121
121
 
122
- const entry = await auditService.recordAuth("login", {
123
- userId: "550e8400-e29b-41d4-a716-446655440000",
124
- ipAddress: "10.0.0.1",
125
- });
126
-
127
- expect(entry.type).toBe("auth");
128
- expect(entry.action).toBe("login");
129
- expect(entry.severity).toBe("info");
130
- });
131
-
132
- it("should record failed login with warning severity", async ({
133
- expect,
134
- }) => {
135
- const { auditService } = await setup();
136
-
137
- const entry = await auditService.recordAuth("login_failed", {
138
- userEmail: "attacker@example.com",
139
- ipAddress: "10.0.0.1",
122
+ const entry = await auditService.create({
123
+ type: "auth",
124
+ action: "login",
125
+ success: false,
140
126
  errorMessage: "Invalid password",
141
127
  });
142
128
 
143
- expect(entry.type).toBe("auth");
144
- expect(entry.action).toBe("login_failed");
145
- expect(entry.severity).toBe("warning");
146
- });
147
-
148
- it("should record logout event", async ({ expect }) => {
149
- const { auditService } = await setup();
150
-
151
- const entry = await auditService.recordAuth("logout", {
152
- userId: "550e8400-e29b-41d4-a716-446655440000",
153
- sessionId: "660e8400-e29b-41d4-a716-446655440001",
154
- });
155
-
156
- expect(entry.type).toBe("auth");
157
- expect(entry.action).toBe("logout");
158
- });
159
- });
160
-
161
- describe("recordUser", () => {
162
- it("should record user creation event", async ({ expect }) => {
163
- const { auditService } = await setup();
164
-
165
- const entry = await auditService.recordUser("create", {
166
- userId: "550e8400-e29b-41d4-a716-446655440000",
167
- resourceId: "660e8400-e29b-41d4-a716-446655440001",
168
- description: "New user registered",
169
- });
170
-
171
- expect(entry.type).toBe("user");
172
- expect(entry.action).toBe("create");
173
- expect(entry.resourceType).toBe("user");
174
- });
175
-
176
- it("should record role change event", async ({ expect }) => {
177
- const { auditService } = await setup();
178
-
179
- const entry = await auditService.recordUser("role_change", {
180
- userId: "550e8400-e29b-41d4-a716-446655440000",
181
- resourceId: "660e8400-e29b-41d4-a716-446655440001",
182
- metadata: { oldRoles: ["user"], newRoles: ["user", "admin"] },
183
- });
184
-
185
- expect(entry.action).toBe("role_change");
186
- expect(entry.metadata).toEqual({
187
- oldRoles: ["user"],
188
- newRoles: ["user", "admin"],
189
- });
190
- });
191
- });
192
-
193
- describe("recordSecurity", () => {
194
- it("should record security events with warning severity", async ({
195
- expect,
196
- }) => {
197
- const { auditService } = await setup();
198
-
199
- const entry = await auditService.recordSecurity("permission_denied", {
200
- userId: "550e8400-e29b-41d4-a716-446655440000",
201
- resourceType: "admin_panel",
202
- description: "Attempted to access admin panel without permission",
203
- });
204
-
205
- expect(entry.type).toBe("security");
206
- expect(entry.action).toBe("permission_denied");
129
+ expect(entry.success).toBe(false);
207
130
  expect(entry.severity).toBe("warning");
208
131
  });
209
132
 
210
- it("should record rate limiting event", async ({ expect }) => {
133
+ it("defaults a successful audit to info severity", async ({ expect }) => {
211
134
  const { auditService } = await setup();
212
135
 
213
- const entry = await auditService.recordSecurity("rate_limited", {
214
- ipAddress: "192.168.1.100",
215
- metadata: { endpoint: "/api/login", requests: 100 },
216
- });
217
-
218
- expect(entry.action).toBe("rate_limited");
219
- expect(entry.severity).toBe("warning");
220
- });
221
- });
222
-
223
- describe("recordSystem", () => {
224
- it("should record system startup", async ({ expect }) => {
225
- const { auditService } = await setup();
226
-
227
- const entry = await auditService.recordSystem("startup", {
228
- metadata: { version: "1.0.0" },
136
+ const entry = await auditService.create({
137
+ type: "auth",
138
+ action: "login",
229
139
  });
230
140
 
231
- expect(entry.type).toBe("system");
232
- expect(entry.action).toBe("startup");
233
141
  expect(entry.severity).toBe("info");
234
142
  });
235
143
 
236
- it("should record system error with critical severity", async ({
144
+ it("respects an explicit severity over the success default", async ({
237
145
  expect,
238
146
  }) => {
239
147
  const { auditService } = await setup();
240
148
 
241
- const entry = await auditService.recordSystem("error", {
242
- errorMessage: "Database connection failed",
243
- metadata: { error: "ECONNREFUSED" },
149
+ const entry = await auditService.create({
150
+ type: "security",
151
+ action: "sessions_invalidated",
152
+ severity: "warning",
244
153
  });
245
154
 
246
- expect(entry.action).toBe("error");
247
- expect(entry.severity).toBe("critical");
155
+ expect(entry.severity).toBe("warning");
248
156
  });
249
157
  });
250
158
 
@@ -396,7 +304,7 @@ describe("alepha/api/audits - AuditService", () => {
396
304
  });
397
305
  await auditService.create({
398
306
  type: "auth",
399
- action: "login_failed",
307
+ action: "login",
400
308
  severity: "warning",
401
309
  success: false,
402
310
  });
@@ -175,6 +175,20 @@ export class AdminAuditController {
175
175
  }),
176
176
  });
177
177
 
178
+ /**
179
+ * List distinct action names present in the audit log (for filters).
180
+ */
181
+ public readonly getAuditActions = $action({
182
+ path: `${this.url}/actions`,
183
+ group: this.group,
184
+ use: [$secure({ permissions: ["admin:audit:read"] })],
185
+ description: "List distinct action names present in the audit log",
186
+ schema: {
187
+ response: t.array(t.text()),
188
+ },
189
+ handler: () => this.auditService.getDistinctActions(),
190
+ });
191
+
178
192
  /**
179
193
  * Get registered audit types.
180
194
  */