alepha 0.13.1 → 0.13.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (329) hide show
  1. package/README.md +1 -1
  2. package/dist/api-files/index.browser.js +80 -0
  3. package/dist/api-files/index.browser.js.map +1 -0
  4. package/dist/api-files/index.d.ts +28 -91
  5. package/dist/api-files/index.js +10 -755
  6. package/dist/api-files/index.js.map +1 -1
  7. package/dist/api-jobs/index.browser.js +56 -0
  8. package/dist/api-jobs/index.browser.js.map +1 -0
  9. package/dist/api-jobs/index.d.ts +46 -46
  10. package/dist/api-jobs/index.js +13 -13
  11. package/dist/api-jobs/index.js.map +1 -1
  12. package/dist/api-notifications/index.browser.js +382 -0
  13. package/dist/api-notifications/index.browser.js.map +1 -0
  14. package/dist/api-notifications/index.d.ts +231 -193
  15. package/dist/api-notifications/index.js +108 -78
  16. package/dist/api-notifications/index.js.map +1 -1
  17. package/dist/api-parameters/index.browser.js +29 -0
  18. package/dist/api-parameters/index.browser.js.map +1 -0
  19. package/dist/api-parameters/index.d.ts +21 -22
  20. package/dist/api-parameters/index.js +22 -22
  21. package/dist/api-parameters/index.js.map +1 -1
  22. package/dist/api-users/index.d.ts +237 -2001
  23. package/dist/api-users/index.js +969 -4795
  24. package/dist/api-users/index.js.map +1 -1
  25. package/dist/api-verifications/index.browser.js +52 -0
  26. package/dist/api-verifications/index.browser.js.map +1 -0
  27. package/dist/api-verifications/index.d.ts +119 -97
  28. package/dist/api-verifications/index.js +1 -1
  29. package/dist/api-verifications/index.js.map +1 -1
  30. package/dist/batch/index.d.ts +13 -13
  31. package/dist/batch/index.js +8 -13
  32. package/dist/batch/index.js.map +1 -1
  33. package/dist/bucket/index.d.ts +14 -14
  34. package/dist/bucket/index.js +19 -17
  35. package/dist/bucket/index.js.map +1 -1
  36. package/dist/cache/index.d.ts +11 -11
  37. package/dist/cache/index.js +9 -9
  38. package/dist/cache/index.js.map +1 -1
  39. package/dist/cli/{dist-Dl9Vl7Ur.js → dist-lGnqsKpu.js} +11 -15
  40. package/dist/cli/dist-lGnqsKpu.js.map +1 -0
  41. package/dist/cli/index.d.ts +32 -49
  42. package/dist/cli/index.js +90 -71
  43. package/dist/cli/index.js.map +1 -1
  44. package/dist/command/index.d.ts +20 -19
  45. package/dist/command/index.js +34 -25
  46. package/dist/command/index.js.map +1 -1
  47. package/dist/core/index.browser.js +218 -218
  48. package/dist/core/index.browser.js.map +1 -1
  49. package/dist/core/index.d.ts +232 -232
  50. package/dist/core/index.js +218 -218
  51. package/dist/core/index.js.map +1 -1
  52. package/dist/core/index.native.js +2113 -0
  53. package/dist/core/index.native.js.map +1 -0
  54. package/dist/datetime/index.d.ts +9 -9
  55. package/dist/datetime/index.js +7 -7
  56. package/dist/datetime/index.js.map +1 -1
  57. package/dist/email/index.d.ts +16 -16
  58. package/dist/email/index.js +14 -9
  59. package/dist/email/index.js.map +1 -1
  60. package/dist/file/index.js +1 -1
  61. package/dist/file/index.js.map +1 -1
  62. package/dist/lock/index.d.ts +9 -9
  63. package/dist/lock/index.js +8 -8
  64. package/dist/lock/index.js.map +1 -1
  65. package/dist/lock-redis/index.js +3 -66
  66. package/dist/lock-redis/index.js.map +1 -1
  67. package/dist/logger/index.d.ts +5 -5
  68. package/dist/logger/index.js +8 -8
  69. package/dist/logger/index.js.map +1 -1
  70. package/dist/orm/index.browser.js +114 -114
  71. package/dist/orm/index.browser.js.map +1 -1
  72. package/dist/orm/index.d.ts +218 -218
  73. package/dist/orm/index.js +49 -49
  74. package/dist/orm/index.js.map +1 -1
  75. package/dist/queue/index.d.ts +29 -29
  76. package/dist/queue/index.js +20 -20
  77. package/dist/queue/index.js.map +1 -1
  78. package/dist/queue-redis/index.d.ts +2 -2
  79. package/dist/redis/index.d.ts +4 -4
  80. package/dist/retry/index.d.ts +19 -19
  81. package/dist/retry/index.js +7 -7
  82. package/dist/retry/index.js.map +1 -1
  83. package/dist/scheduler/index.d.ts +16 -16
  84. package/dist/scheduler/index.js +9 -9
  85. package/dist/scheduler/index.js.map +1 -1
  86. package/dist/security/index.d.ts +53 -53
  87. package/dist/security/index.js +35 -35
  88. package/dist/security/index.js.map +1 -1
  89. package/dist/server/index.browser.js +1 -1
  90. package/dist/server/index.browser.js.map +1 -1
  91. package/dist/server/index.d.ts +92 -92
  92. package/dist/server/index.js +16 -16
  93. package/dist/server/index.js.map +1 -1
  94. package/dist/server-auth/index.browser.js +4 -982
  95. package/dist/server-auth/index.browser.js.map +1 -1
  96. package/dist/server-auth/index.d.ts +204 -785
  97. package/dist/server-auth/index.js +47 -1239
  98. package/dist/server-auth/index.js.map +1 -1
  99. package/dist/server-cache/index.d.ts +10 -10
  100. package/dist/server-cache/index.js +2 -2
  101. package/dist/server-cache/index.js.map +1 -1
  102. package/dist/server-compress/index.d.ts +4 -4
  103. package/dist/server-compress/index.js +1 -1
  104. package/dist/server-compress/index.js.map +1 -1
  105. package/dist/server-cookies/index.browser.js +8 -8
  106. package/dist/server-cookies/index.browser.js.map +1 -1
  107. package/dist/server-cookies/index.d.ts +17 -17
  108. package/dist/server-cookies/index.js +11 -11
  109. package/dist/server-cookies/index.js.map +1 -1
  110. package/dist/server-cors/index.d.ts +17 -17
  111. package/dist/server-cors/index.js +9 -9
  112. package/dist/server-cors/index.js.map +1 -1
  113. package/dist/server-health/index.d.ts +19 -19
  114. package/dist/server-helmet/index.d.ts +1 -1
  115. package/dist/server-links/index.browser.js +12 -12
  116. package/dist/server-links/index.browser.js.map +1 -1
  117. package/dist/server-links/index.d.ts +59 -251
  118. package/dist/server-links/index.js +23 -502
  119. package/dist/server-links/index.js.map +1 -1
  120. package/dist/server-metrics/index.d.ts +4 -4
  121. package/dist/server-multipart/index.d.ts +2 -2
  122. package/dist/server-proxy/index.d.ts +12 -12
  123. package/dist/server-proxy/index.js +10 -10
  124. package/dist/server-proxy/index.js.map +1 -1
  125. package/dist/server-rate-limit/index.d.ts +22 -22
  126. package/dist/server-rate-limit/index.js +12 -12
  127. package/dist/server-rate-limit/index.js.map +1 -1
  128. package/dist/server-security/index.d.ts +22 -22
  129. package/dist/server-security/index.js +15 -15
  130. package/dist/server-security/index.js.map +1 -1
  131. package/dist/server-static/index.d.ts +14 -14
  132. package/dist/server-static/index.js +26 -10
  133. package/dist/server-static/index.js.map +1 -1
  134. package/dist/server-swagger/index.d.ts +25 -184
  135. package/dist/server-swagger/index.js +21 -724
  136. package/dist/server-swagger/index.js.map +1 -1
  137. package/dist/sms/index.d.ts +14 -14
  138. package/dist/sms/index.js +9 -9
  139. package/dist/sms/index.js.map +1 -1
  140. package/dist/thread/index.d.ts +11 -11
  141. package/dist/thread/index.js +17 -17
  142. package/dist/thread/index.js.map +1 -1
  143. package/dist/topic/index.d.ts +26 -26
  144. package/dist/topic/index.js +16 -16
  145. package/dist/topic/index.js.map +1 -1
  146. package/dist/topic-redis/index.d.ts +1 -1
  147. package/dist/vite/index.d.ts +3 -3
  148. package/dist/vite/index.js +8 -8
  149. package/dist/vite/index.js.map +1 -1
  150. package/dist/websocket/index.browser.js +11 -11
  151. package/dist/websocket/index.browser.js.map +1 -1
  152. package/dist/websocket/index.d.ts +58 -58
  153. package/dist/websocket/index.js +13 -13
  154. package/dist/websocket/index.js.map +1 -1
  155. package/package.json +128 -57
  156. package/src/api-files/index.browser.ts +17 -0
  157. package/src/api-files/services/FileService.ts +5 -7
  158. package/src/api-jobs/index.browser.ts +15 -0
  159. package/src/api-jobs/index.ts +1 -1
  160. package/src/api-jobs/{descriptors → primitives}/$job.ts +8 -8
  161. package/src/api-jobs/providers/JobProvider.ts +9 -9
  162. package/src/api-jobs/services/JobService.ts +5 -5
  163. package/src/api-notifications/controllers/NotificationController.ts +26 -1
  164. package/src/api-notifications/index.browser.ts +17 -0
  165. package/src/api-notifications/index.ts +6 -15
  166. package/src/api-notifications/{descriptors → primitives}/$notification.ts +10 -10
  167. package/src/api-notifications/schemas/notificationQuerySchema.ts +13 -0
  168. package/src/api-notifications/services/NotificationSenderService.ts +3 -3
  169. package/src/api-notifications/services/NotificationService.ts +45 -2
  170. package/src/api-parameters/index.browser.ts +12 -0
  171. package/src/api-parameters/index.ts +1 -1
  172. package/src/api-parameters/{descriptors → primitives}/$config.ts +7 -12
  173. package/src/api-users/atoms/realmAuthSettingsAtom.ts +3 -1
  174. package/src/api-users/controllers/UserController.ts +21 -1
  175. package/src/api-users/index.ts +1 -1
  176. package/src/api-users/{descriptors → primitives}/$userRealm.ts +40 -17
  177. package/src/api-users/providers/UserRealmProvider.ts +2 -1
  178. package/src/api-users/services/SessionService.ts +2 -0
  179. package/src/api-users/services/UserService.ts +56 -16
  180. package/src/api-verifications/index.browser.ts +15 -0
  181. package/src/api-verifications/index.ts +1 -0
  182. package/src/batch/index.ts +3 -3
  183. package/src/batch/{descriptors → primitives}/$batch.ts +13 -16
  184. package/src/batch/providers/BatchProvider.ts +0 -7
  185. package/src/bucket/index.ts +15 -13
  186. package/src/bucket/{descriptors → primitives}/$bucket.ts +8 -8
  187. package/src/bucket/providers/LocalFileStorageProvider.ts +3 -3
  188. package/src/cache/index.ts +4 -4
  189. package/src/cache/{descriptors → primitives}/$cache.ts +15 -15
  190. package/src/cli/apps/AlephaCli.ts +27 -1
  191. package/src/cli/apps/AlephaPackageBuilderCli.ts +27 -2
  192. package/src/cli/commands/CoreCommands.ts +6 -2
  193. package/src/cli/commands/DrizzleCommands.ts +6 -6
  194. package/src/cli/commands/VerifyCommands.ts +1 -1
  195. package/src/cli/commands/ViteCommands.ts +8 -2
  196. package/src/cli/services/ProjectUtils.ts +74 -78
  197. package/src/command/helpers/Asker.ts +10 -0
  198. package/src/command/index.ts +5 -5
  199. package/src/command/{descriptors → primitives}/$command.ts +9 -12
  200. package/src/command/providers/CliProvider.ts +10 -10
  201. package/src/core/Alepha.ts +30 -33
  202. package/src/core/constants/KIND.ts +1 -1
  203. package/src/core/constants/OPTIONS.ts +1 -1
  204. package/src/core/helpers/{descriptor.ts → primitive.ts} +18 -18
  205. package/src/core/helpers/ref.ts +1 -1
  206. package/src/core/index.shared.ts +8 -8
  207. package/src/core/{descriptors → primitives}/$context.ts +5 -5
  208. package/src/core/{descriptors → primitives}/$hook.ts +4 -4
  209. package/src/core/{descriptors → primitives}/$inject.ts +2 -2
  210. package/src/core/{descriptors → primitives}/$module.ts +9 -9
  211. package/src/core/{descriptors → primitives}/$use.ts +2 -2
  212. package/src/core/providers/CodecManager.ts +1 -1
  213. package/src/core/providers/JsonSchemaCodec.ts +1 -1
  214. package/src/core/providers/StateManager.ts +2 -2
  215. package/src/datetime/index.ts +3 -3
  216. package/src/datetime/{descriptors → primitives}/$interval.ts +6 -6
  217. package/src/email/index.ts +17 -9
  218. package/src/email/{descriptors → primitives}/$email.ts +8 -8
  219. package/src/file/index.ts +1 -1
  220. package/src/lock/index.ts +3 -3
  221. package/src/lock/{descriptors → primitives}/$lock.ts +10 -10
  222. package/src/logger/index.ts +8 -8
  223. package/src/logger/{descriptors → primitives}/$logger.ts +2 -2
  224. package/src/logger/services/Logger.ts +1 -1
  225. package/src/orm/constants/PG_SYMBOLS.ts +2 -2
  226. package/src/orm/index.browser.ts +2 -2
  227. package/src/orm/index.ts +8 -8
  228. package/src/orm/{descriptors → primitives}/$entity.ts +11 -11
  229. package/src/orm/{descriptors → primitives}/$repository.ts +2 -2
  230. package/src/orm/{descriptors → primitives}/$sequence.ts +8 -8
  231. package/src/orm/{descriptors → primitives}/$transaction.ts +4 -4
  232. package/src/orm/providers/PostgresTypeProvider.ts +3 -3
  233. package/src/orm/providers/RepositoryProvider.ts +4 -4
  234. package/src/orm/providers/drivers/DatabaseProvider.ts +7 -7
  235. package/src/orm/providers/drivers/NodeSqliteProvider.ts +3 -3
  236. package/src/orm/services/ModelBuilder.ts +9 -9
  237. package/src/orm/services/PgRelationManager.ts +2 -2
  238. package/src/orm/services/PostgresModelBuilder.ts +5 -5
  239. package/src/orm/services/Repository.ts +7 -7
  240. package/src/orm/services/SqliteModelBuilder.ts +5 -5
  241. package/src/queue/index.ts +7 -7
  242. package/src/queue/{descriptors → primitives}/$consumer.ts +15 -15
  243. package/src/queue/{descriptors → primitives}/$queue.ts +12 -12
  244. package/src/queue/providers/WorkerProvider.ts +7 -7
  245. package/src/retry/index.ts +3 -3
  246. package/src/retry/{descriptors → primitives}/$retry.ts +14 -14
  247. package/src/scheduler/index.ts +3 -3
  248. package/src/scheduler/{descriptors → primitives}/$scheduler.ts +9 -9
  249. package/src/scheduler/providers/CronProvider.ts +1 -1
  250. package/src/security/index.ts +9 -9
  251. package/src/security/{descriptors → primitives}/$permission.ts +7 -7
  252. package/src/security/{descriptors → primitives}/$realm.ts +6 -12
  253. package/src/security/{descriptors → primitives}/$role.ts +12 -12
  254. package/src/security/{descriptors → primitives}/$serviceAccount.ts +8 -8
  255. package/src/server/index.browser.ts +1 -1
  256. package/src/server/index.ts +14 -14
  257. package/src/server/{descriptors → primitives}/$action.ts +13 -13
  258. package/src/server/{descriptors → primitives}/$route.ts +9 -9
  259. package/src/server/providers/NodeHttpServerProvider.ts +1 -1
  260. package/src/server/services/HttpClient.ts +1 -1
  261. package/src/server-auth/index.browser.ts +1 -1
  262. package/src/server-auth/index.ts +6 -6
  263. package/src/server-auth/{descriptors → primitives}/$auth.ts +10 -10
  264. package/src/server-auth/{descriptors → primitives}/$authCredentials.ts +4 -4
  265. package/src/server-auth/{descriptors → primitives}/$authGithub.ts +4 -4
  266. package/src/server-auth/{descriptors → primitives}/$authGoogle.ts +4 -4
  267. package/src/server-auth/providers/ServerAuthProvider.ts +4 -4
  268. package/src/server-cache/providers/ServerCacheProvider.ts +7 -7
  269. package/src/server-compress/providers/ServerCompressProvider.ts +3 -3
  270. package/src/server-cookies/index.browser.ts +2 -2
  271. package/src/server-cookies/index.ts +5 -5
  272. package/src/server-cookies/{descriptors → primitives}/$cookie.browser.ts +12 -12
  273. package/src/server-cookies/{descriptors → primitives}/$cookie.ts +13 -13
  274. package/src/server-cookies/providers/ServerCookiesProvider.ts +6 -5
  275. package/src/server-cookies/services/CookieParser.ts +1 -1
  276. package/src/server-cors/index.ts +3 -3
  277. package/src/server-cors/{descriptors → primitives}/$cors.ts +11 -13
  278. package/src/server-cors/providers/ServerCorsProvider.ts +5 -5
  279. package/src/server-links/index.browser.ts +5 -5
  280. package/src/server-links/index.ts +9 -9
  281. package/src/server-links/{descriptors → primitives}/$remote.ts +11 -11
  282. package/src/server-links/providers/LinkProvider.ts +7 -7
  283. package/src/server-links/providers/{RemoteDescriptorProvider.ts → RemotePrimitiveProvider.ts} +6 -6
  284. package/src/server-links/providers/ServerLinksProvider.ts +3 -3
  285. package/src/server-proxy/index.ts +3 -3
  286. package/src/server-proxy/{descriptors → primitives}/$proxy.ts +8 -8
  287. package/src/server-proxy/providers/ServerProxyProvider.ts +4 -4
  288. package/src/server-rate-limit/index.ts +6 -6
  289. package/src/server-rate-limit/{descriptors → primitives}/$rateLimit.ts +13 -13
  290. package/src/server-rate-limit/providers/ServerRateLimitProvider.ts +5 -5
  291. package/src/server-security/index.ts +3 -3
  292. package/src/server-security/{descriptors → primitives}/$basicAuth.ts +13 -13
  293. package/src/server-security/providers/ServerBasicAuthProvider.ts +5 -5
  294. package/src/server-security/providers/ServerSecurityProvider.ts +4 -4
  295. package/src/server-static/index.ts +3 -3
  296. package/src/server-static/{descriptors → primitives}/$serve.ts +8 -10
  297. package/src/server-static/providers/ServerStaticProvider.ts +24 -9
  298. package/src/server-swagger/index.ts +5 -5
  299. package/src/server-swagger/{descriptors → primitives}/$swagger.ts +9 -9
  300. package/src/server-swagger/providers/ServerSwaggerProvider.ts +11 -10
  301. package/src/sms/index.ts +4 -4
  302. package/src/sms/{descriptors → primitives}/$sms.ts +8 -8
  303. package/src/thread/index.ts +3 -3
  304. package/src/thread/{descriptors → primitives}/$thread.ts +13 -13
  305. package/src/thread/providers/ThreadProvider.ts +7 -9
  306. package/src/topic/index.ts +5 -5
  307. package/src/topic/{descriptors → primitives}/$subscriber.ts +14 -14
  308. package/src/topic/{descriptors → primitives}/$topic.ts +10 -10
  309. package/src/topic/providers/TopicProvider.ts +4 -4
  310. package/src/vite/tasks/copyAssets.ts +1 -1
  311. package/src/vite/tasks/generateSitemap.ts +3 -3
  312. package/src/vite/tasks/prerenderPages.ts +2 -2
  313. package/src/vite/tasks/runAlepha.ts +2 -2
  314. package/src/websocket/index.browser.ts +3 -3
  315. package/src/websocket/index.shared.ts +2 -2
  316. package/src/websocket/index.ts +4 -4
  317. package/src/websocket/interfaces/WebSocketInterfaces.ts +3 -3
  318. package/src/websocket/{descriptors → primitives}/$channel.ts +10 -10
  319. package/src/websocket/{descriptors → primitives}/$websocket.ts +8 -8
  320. package/src/websocket/providers/NodeWebSocketServerProvider.ts +7 -7
  321. package/src/websocket/providers/WebSocketServerProvider.ts +3 -3
  322. package/src/websocket/services/WebSocketClient.ts +5 -5
  323. package/dist/cli/dist-Dl9Vl7Ur.js.map +0 -1
  324. package/src/api-notifications/providers/MemorySmsProvider.ts +0 -20
  325. package/src/api-notifications/providers/SmsProvider.ts +0 -8
  326. /package/src/core/{descriptors → primitives}/$atom.ts +0 -0
  327. /package/src/core/{descriptors → primitives}/$env.ts +0 -0
  328. /package/src/server-auth/{descriptors → primitives}/$authApple.ts +0 -0
  329. /package/src/server-links/{descriptors → primitives}/$client.ts +0 -0
@@ -1,15 +1,13 @@
1
- import { $env, $inject, $module, Alepha, AlephaError, Descriptor, KIND, createDescriptor, t } from "alepha";
1
+ import { $env, $inject, $module, Alepha, AlephaError, KIND, Primitive, createPrimitive, t } from "alepha";
2
+ import { $entity, $repository, pageQuerySchema, pg } from "alepha/orm";
3
+ import { $action } from "alepha/server";
2
4
  import { $batch } from "alepha/batch";
3
5
  import { DateTimeProvider } from "alepha/datetime";
4
6
  import { $logger } from "alepha/logger";
5
- import { $entity, $repository, pg } from "alepha/orm";
6
7
  import { $queue } from "alepha/queue";
7
8
  import { EmailProvider } from "alepha/email";
9
+ import { SmsProvider } from "alepha/sms";
8
10
 
9
- //#region src/api-notifications/controllers/NotificationController.ts
10
- var NotificationController = class {};
11
-
12
- //#endregion
13
11
  //#region src/api-notifications/entities/notifications.ts
14
12
  const notifications = $entity({
15
13
  name: "notifications",
@@ -36,8 +34,67 @@ const notifications = $entity({
36
34
  });
37
35
 
38
36
  //#endregion
39
- //#region src/api-notifications/providers/SmsProvider.ts
40
- var SmsProvider = class {};
37
+ //#region src/api-notifications/schemas/notificationQuerySchema.ts
38
+ const notificationQuerySchema = t.extend(pageQuerySchema, {
39
+ type: t.optional(t.enum(["email", "sms"])),
40
+ template: t.optional(t.string()),
41
+ contact: t.optional(t.string()),
42
+ category: t.optional(t.string()),
43
+ status: t.optional(t.enum([
44
+ "pending",
45
+ "sent",
46
+ "failed"
47
+ ]))
48
+ });
49
+
50
+ //#endregion
51
+ //#region src/api-notifications/primitives/$notification.ts
52
+ /**
53
+ * Creates a notification primitive for managing email/SMS notification templates.
54
+ *
55
+ * Provides type-safe, reusable notification templates with multi-language support,
56
+ * variable substitution, and categorization for different notification channels.
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * class NotificationTemplates {
61
+ * welcomeEmail = $notification({
62
+ * name: "welcome-email",
63
+ * category: "onboarding",
64
+ * schema: t.object({ username: t.text(), activationLink: t.text() }),
65
+ * email: {
66
+ * subject: "Welcome to our platform!",
67
+ * body: (vars) => `Hello ${vars.username}, click: ${vars.activationLink}`
68
+ * }
69
+ * });
70
+ *
71
+ * async sendWelcome(user: User) {
72
+ * await this.welcomeEmail.push({
73
+ * variables: { username: user.name, activationLink: generateLink() },
74
+ * contact: user.email
75
+ * });
76
+ * }
77
+ * }
78
+ * ```
79
+ */
80
+ const $notification = (options) => createPrimitive(NotificationPrimitive, options);
81
+ var NotificationPrimitive = class extends Primitive {
82
+ notificationService = $inject(NotificationService);
83
+ get name() {
84
+ return this.options.name ?? `${this.config.propertyKey}`;
85
+ }
86
+ async push(options) {
87
+ if (this.options.email) await this.notificationService.createNotification({
88
+ ...options,
89
+ type: "email",
90
+ template: this.name
91
+ });
92
+ }
93
+ configure(options) {
94
+ Object.assign(this.options, options);
95
+ }
96
+ };
97
+ $notification[KIND] = NotificationPrimitive;
41
98
 
42
99
  //#endregion
43
100
  //#region src/api-notifications/services/NotificationSenderService.ts
@@ -151,7 +208,7 @@ var NotificationSenderService = class {
151
208
  load(notification) {
152
209
  const variables = notification.variables || {};
153
210
  const contact = notification.contact;
154
- const template = this.alepha.descriptors($notification).find((it) => it.name === notification.template);
211
+ const template = this.alepha.primitives($notification).find((it) => it.name === notification.template);
155
212
  if (!template) {
156
213
  this.log.error("Notification template not found", {
157
214
  id: notification.id,
@@ -220,6 +277,26 @@ var NotificationService = class {
220
277
  this.log.trace("Finding notification by ID", { id });
221
278
  return this.notificationRepository.findOne({ where: { id } });
222
279
  }
280
+ async findNotifications(q = {}) {
281
+ this.log.trace("Finding notifications", { query: q });
282
+ q.sort ??= "-createdAt";
283
+ const where = this.notificationRepository.createQueryWhere();
284
+ if (q.type) where.type = { eq: q.type };
285
+ if (q.template) where.template = { like: `%${q.template}%` };
286
+ if (q.contact) where.contact = { like: `%${q.contact}%` };
287
+ if (q.category) where.category = { eq: q.category };
288
+ if (q.status) {
289
+ if (q.status === "sent") {
290
+ where.sentAt = { isNotNull: true };
291
+ where.error = { isNull: true };
292
+ } else if (q.status === "failed") where.error = { isNotNull: true };
293
+ else if (q.status === "pending") {
294
+ where.sentAt = { isNull: true };
295
+ where.error = { isNull: true };
296
+ }
297
+ }
298
+ return this.notificationRepository.paginate(q, { where }, { count: true });
299
+ }
223
300
  /**
224
301
  * Create a new notification.
225
302
  */
@@ -256,72 +333,30 @@ var NotificationService = class {
256
333
  };
257
334
 
258
335
  //#endregion
259
- //#region src/api-notifications/descriptors/$notification.ts
260
- /**
261
- * Creates a notification descriptor for managing email/SMS notification templates.
262
- *
263
- * Provides type-safe, reusable notification templates with multi-language support,
264
- * variable substitution, and categorization for different notification channels.
265
- *
266
- * @example
267
- * ```ts
268
- * class NotificationTemplates {
269
- * welcomeEmail = $notification({
270
- * name: "welcome-email",
271
- * category: "onboarding",
272
- * schema: t.object({ username: t.text(), activationLink: t.text() }),
273
- * email: {
274
- * subject: "Welcome to our platform!",
275
- * body: (vars) => `Hello ${vars.username}, click: ${vars.activationLink}`
276
- * }
277
- * });
278
- *
279
- * async sendWelcome(user: User) {
280
- * await this.welcomeEmail.push({
281
- * variables: { username: user.name, activationLink: generateLink() },
282
- * contact: user.email
283
- * });
284
- * }
285
- * }
286
- * ```
287
- */
288
- const $notification = (options) => createDescriptor(NotificationDescriptor, options);
289
- var NotificationDescriptor = class extends Descriptor {
336
+ //#region src/api-notifications/controllers/NotificationController.ts
337
+ var NotificationController = class {
338
+ url = "/notifications";
339
+ group = "notifications";
290
340
  notificationService = $inject(NotificationService);
291
- get name() {
292
- return this.options.name ?? `${this.config.propertyKey}`;
293
- }
294
- async push(options) {
295
- if (this.options.email) await this.notificationService.createNotification({
296
- ...options,
297
- type: "email",
298
- template: this.name
299
- });
300
- }
301
- configure(options) {
302
- Object.assign(this.options, options);
303
- }
341
+ /**
342
+ * Find notifications with pagination and filtering.
343
+ */
344
+ findNotifications = $action({
345
+ path: this.url,
346
+ group: this.group,
347
+ description: "Find notifications with pagination and filtering",
348
+ schema: {
349
+ query: notificationQuerySchema,
350
+ response: pg.page(notifications.schema)
351
+ },
352
+ handler: ({ query }) => this.notificationService.findNotifications(query)
353
+ });
304
354
  };
305
- $notification[KIND] = NotificationDescriptor;
306
355
 
307
356
  //#endregion
308
357
  //#region src/api-notifications/jobs/NotificationJobs.ts
309
358
  var NotificationJobs = class {};
310
359
 
311
- //#endregion
312
- //#region src/api-notifications/providers/MemorySmsProvider.ts
313
- var MemorySmsProvider = class extends SmsProvider {
314
- records = [];
315
- async send(options) {
316
- const { to, message } = options;
317
- this.records.push({
318
- to,
319
- message,
320
- sentAt: /* @__PURE__ */ new Date()
321
- });
322
- }
323
- };
324
-
325
360
  //#endregion
326
361
  //#region src/api-notifications/schemas/notificationContactPreferencesSchema.ts
327
362
  const notificationContactPreferencesSchema = t.object({
@@ -337,31 +372,26 @@ const notificationContactPreferencesSchema = t.object({
337
372
  * This module includes notification sending, retrieval, status tracking,
338
373
  * and user notification preferences management.
339
374
  *
375
+ * Requires `AlephaSms` module to be loaded for SMS notifications.
376
+ *
340
377
  * @module alepha.api.notifications
341
378
  */
342
379
  const AlephaApiNotifications = $module({
343
380
  name: "alepha.api.notifications",
344
- descriptors: [$notification],
381
+ primitives: [$notification],
345
382
  services: [
346
383
  NotificationController,
347
384
  NotificationService,
348
385
  NotificationSenderService,
349
386
  NotificationQueues,
350
- NotificationJobs,
351
- SmsProvider,
352
- MemorySmsProvider
387
+ NotificationJobs
353
388
  ],
354
389
  register: (alepha) => {
355
- alepha.with({
356
- optional: true,
357
- provide: SmsProvider,
358
- use: MemorySmsProvider
359
- });
360
390
  if (alepha.parseEnv(notificationServiceEnvSchema).NOTIFICATION_QUEUE) alepha.with(NotificationQueues);
361
391
  alepha.with(NotificationController).with(NotificationService).with(NotificationSenderService).with(NotificationJobs);
362
392
  }
363
393
  });
364
394
 
365
395
  //#endregion
366
- export { $notification, AlephaApiNotifications, MemorySmsProvider, NotificationController, NotificationDescriptor, NotificationJobs, NotificationQueues, NotificationSenderService, NotificationService, SmsProvider, notificationContactPreferencesSchema, notificationCreateSchema, notificationServiceEnvSchema, notifications };
396
+ export { $notification, AlephaApiNotifications, NotificationController, NotificationJobs, NotificationPrimitive, NotificationQueues, NotificationSenderService, NotificationService, notificationContactPreferencesSchema, notificationCreateSchema, notificationQuerySchema, notificationServiceEnvSchema, notifications };
367
397
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["notifications"],"sources":["../../src/api-notifications/controllers/NotificationController.ts","../../src/api-notifications/entities/notifications.ts","../../src/api-notifications/providers/SmsProvider.ts","../../src/api-notifications/services/NotificationSenderService.ts","../../src/api-notifications/queues/NotificationQueues.ts","../../src/api-notifications/schemas/notificationCreateSchema.ts","../../src/api-notifications/services/NotificationService.ts","../../src/api-notifications/descriptors/$notification.ts","../../src/api-notifications/jobs/NotificationJobs.ts","../../src/api-notifications/providers/MemorySmsProvider.ts","../../src/api-notifications/schemas/notificationContactPreferencesSchema.ts","../../src/api-notifications/index.ts"],"sourcesContent":["export class NotificationController {}\n","import { type Static, t } from \"alepha\";\nimport { $entity, pg } from \"alepha/orm\";\n\nexport const notifications = $entity({\n name: \"notifications\",\n schema: t.object({\n id: pg.primaryKey(t.uuid()),\n\n version: pg.version(),\n\n createdAt: pg.createdAt(),\n\n updatedAt: pg.updatedAt(),\n\n // -----------------------------------------------------------------------------------------------------------------\n\n type: t.enum([\"email\", \"sms\"]),\n\n template: t.text(), // e.g. 'resetPassword'\n\n category: t.optional(\n t.text({\n description:\n \"For grouping related notifications (e.g., 'authentication', 'marketing'). Contact can filter notifications by category.\",\n }),\n ),\n\n critical: t.optional(\n t.boolean({\n description:\n \"Prioritize delivery of this notification. Set to true for important system alerts.\",\n }),\n ),\n\n sensitive: t.optional(\n t.boolean({\n description:\n \"Message won't be logged or stored in plain text. Set to true when notification contains passwords or codes.\",\n }),\n ),\n\n // -----------------------------------------------------------------------------------------------------------------\n\n contact: t.text(), // e.g. email address or phone number or user ID or whatever\n\n variables: t.optional(t.record(t.text(), t.any())),\n\n scheduledAt: t.optional(\n t.datetime({\n description:\n \"When set, the notification will be sent at or after this date/time.\",\n }),\n ),\n\n // -----------------------------------------------------------------------------------------------------------------\n\n sentAt: t.optional(t.datetime()),\n\n error: t.optional(\n t.object({\n at: t.datetime(),\n name: t.text(),\n message: t.text({ size: \"rich\" }),\n }),\n ),\n\n // TODO: retryCount, lastRetryAt, etc.\n }),\n});\n\nexport type NotificationEntity = Static<typeof notifications.schema>;\n","export abstract class SmsProvider {\n public abstract send(options: SmsSendOptions): Promise<void>;\n}\n\nexport interface SmsSendOptions {\n to: string;\n message: string;\n}\n","import { $inject, Alepha, AlephaError } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { EmailProvider } from \"alepha/email\";\nimport { $logger } from \"alepha/logger\";\nimport { $repository } from \"alepha/orm\";\nimport { $notification } from \"../descriptors/$notification.ts\";\nimport {\n type NotificationEntity,\n notifications,\n} from \"../entities/notifications.ts\";\nimport { SmsProvider } from \"../providers/SmsProvider.ts\";\n\nexport class NotificationSenderService {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly notificationRepository = $repository(notifications);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly emailProvider = $inject(EmailProvider);\n protected readonly smsProvider = $inject(SmsProvider);\n\n public async send(notificationId: string | NotificationEntity) {\n this.log.trace(\"Sending notification\", {\n notificationId:\n typeof notificationId === \"string\" ? notificationId : notificationId.id,\n });\n\n const notification =\n typeof notificationId === \"string\"\n ? await this.notificationRepository.findById(notificationId)\n : notificationId;\n\n if (notification.sentAt) {\n this.log.debug(\"Notification already sent\", {\n notificationId: notification.id,\n sentAt: notification.sentAt,\n });\n return;\n }\n\n this.log.debug(\"Processing notification\", {\n id: notification.id,\n type: notification.type,\n template: notification.template,\n contact: notification.contact,\n });\n\n try {\n if (notification.type === \"email\") {\n await this.emailProvider.send(this.renderEmail(notification));\n notification.sentAt = this.dateTimeProvider.nowISOString();\n this.log.info(\"Email notification sent\", {\n id: notification.id,\n template: notification.template,\n contact: notification.contact,\n });\n }\n if (notification.type === \"sms\") {\n await this.smsProvider.send(this.renderSms(notification));\n notification.sentAt = this.dateTimeProvider.nowISOString();\n this.log.info(\"SMS notification sent\", {\n id: notification.id,\n template: notification.template,\n contact: notification.contact,\n });\n }\n } catch (e) {\n this.log.error(\"Failed to send notification\", {\n id: notification.id,\n type: notification.type,\n template: notification.template,\n contact: notification.contact,\n error: e,\n });\n if (e instanceof Error) {\n notification.error = {\n at: this.dateTimeProvider.nowISOString(),\n name: e.name,\n message: e.message,\n };\n }\n } finally {\n await this.notificationRepository.save(notification);\n }\n }\n\n public renderSms(notification: NotificationEntity) {\n this.log.trace(\"Rendering SMS notification\", {\n id: notification.id,\n template: notification.template,\n });\n\n const { variables, contact, template } = this.load(notification);\n\n const sms = template.options.sms;\n if (!sms) {\n this.log.error(\"Notification template has no SMS defined\", {\n id: notification.id,\n template: notification.template,\n });\n throw new AlephaError(\n `Notification template ${notification.template} has no sms defined`,\n );\n }\n\n this.log.debug(\"Rendering SMS\", {\n template: notification.template,\n contact,\n });\n\n const message =\n typeof sms.message === \"function\"\n ? sms.message(variables as any)\n : sms.message;\n\n return {\n to: contact,\n message,\n };\n }\n\n public renderEmail(notification: NotificationEntity) {\n this.log.trace(\"Rendering email notification\", {\n id: notification.id,\n template: notification.template,\n });\n\n const { variables, contact, template } = this.load(notification);\n\n const email = template.options.email;\n if (!email) {\n this.log.error(\"Notification template has no email defined\", {\n id: notification.id,\n template: notification.template,\n });\n throw new AlephaError(\n `Notification template ${notification.template} has no email defined`,\n );\n }\n\n this.log.debug(\"Rendering email\", {\n template: notification.template,\n contact,\n subject: email.subject,\n });\n\n const subject = email.subject;\n\n const body =\n typeof email.body === \"function\"\n ? email.body(variables as any)\n : email.body;\n\n return {\n to: contact,\n subject,\n body,\n };\n }\n\n protected load(notification: NotificationEntity) {\n const variables = notification.variables || {};\n const contact = notification.contact;\n const template = this.alepha\n .descriptors($notification)\n .find((it) => it.name === notification.template);\n\n if (!template) {\n this.log.error(\"Notification template not found\", {\n id: notification.id,\n template: notification.template,\n });\n throw new AlephaError(\n `No notification template found for ${notification.template}`,\n );\n }\n\n return {\n template,\n variables,\n contact,\n };\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { $queue } from \"alepha/queue\";\nimport { NotificationSenderService } from \"../services/NotificationSenderService.ts\";\n\nexport class NotificationQueues {\n protected readonly notificationSenderService = $inject(\n NotificationSenderService,\n );\n\n public readonly processNotification = $queue({\n description: \"Queue for processing notifications\",\n schema: t.object({\n notificationId: t.string({ format: \"uuid\" }),\n }),\n handler: async (message) => {\n await this.notificationSenderService.send(message.payload.notificationId);\n },\n });\n}\n","import { type Static, t } from \"alepha\";\nimport { notifications } from \"../entities/notifications.ts\";\n\nexport const notificationCreateSchema = t.pick(notifications.schema, [\n \"type\",\n \"contact\",\n \"template\",\n \"variables\",\n]);\n\nexport type NotificationCreate = Static<typeof notificationCreateSchema>;\n","import { $env, $inject, Alepha, type Static, t } from \"alepha\";\nimport { $batch } from \"alepha/batch\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { $repository } from \"alepha/orm\";\nimport { notifications } from \"../entities/notifications.ts\";\nimport { NotificationQueues } from \"../queues/NotificationQueues.ts\";\nimport {\n type NotificationCreate,\n notificationCreateSchema,\n} from \"../schemas/notificationCreateSchema.ts\";\nimport { NotificationSenderService } from \"./NotificationSenderService.ts\";\n\nexport const notificationServiceEnvSchema = t.object({\n NOTIFICATION_QUEUE: t.optional(\n t.boolean({\n description:\n \"If true, notifications will be queued instead of sent immediately\",\n }),\n ),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof notificationServiceEnvSchema>> {}\n}\n\nexport class NotificationService {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly env = $env(notificationServiceEnvSchema);\n protected readonly notificationRepository = $repository(notifications);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly notificationSenderService = $inject(\n NotificationSenderService,\n );\n\n public readonly notificationBatch = $batch({\n maxSize: 100,\n maxDuration: [15, \"seconds\"],\n schema: notificationCreateSchema,\n handler: async (notifications: NotificationCreate[]) => {\n this.log.debug(\"Processing notification batch\", {\n size: notifications.length,\n templates: [...new Set(notifications.map((n) => n.template))],\n });\n\n const entities =\n await this.notificationRepository.createMany(notifications);\n\n await this.alepha\n .inject(NotificationQueues)\n .processNotification.push(\n ...entities.map((it) => ({ notificationId: it.id })),\n );\n\n this.log.info(\"Notification batch queued\", {\n count: entities.length,\n ids: entities.map((it) => it.id),\n });\n },\n });\n\n public async findNotificationById(id: string) {\n this.log.trace(\"Finding notification by ID\", { id });\n return this.notificationRepository.findOne({ where: { id } });\n }\n\n /**\n * Create a new notification.\n */\n public async createNotification(entry: NotificationCreate): Promise<void> {\n this.log.trace(\"Creating notification\", {\n template: entry.template,\n type: entry.type,\n contact: entry.contact,\n });\n\n if (\n this.env.NOTIFICATION_QUEUE !== true ||\n this.alepha.isServerless() ||\n this.alepha.isTest()\n ) {\n this.log.debug(\"Sending notification immediately\", {\n template: entry.template,\n type: entry.type,\n contact: entry.contact,\n });\n const notification = await this.notificationRepository.create(entry);\n await this.notificationSenderService.send(notification);\n return;\n }\n\n this.log.debug(\"Queuing notification to batch\", {\n template: entry.template,\n type: entry.type,\n contact: entry.contact,\n });\n\n this.notificationBatch.push(entry).catch((e) => {\n this.log.error(\"Failed to push notification to batch\", {\n template: entry.template,\n type: entry.type,\n contact: entry.contact,\n error: e,\n });\n });\n }\n}\n","import {\n $inject,\n createDescriptor,\n Descriptor,\n KIND,\n type Static,\n type StaticEncode,\n type TObject,\n} from \"alepha\";\nimport { NotificationService } from \"../services/NotificationService.ts\";\n\n/**\n * Creates a notification descriptor for managing email/SMS notification templates.\n *\n * Provides type-safe, reusable notification templates with multi-language support,\n * variable substitution, and categorization for different notification channels.\n *\n * @example\n * ```ts\n * class NotificationTemplates {\n * welcomeEmail = $notification({\n * name: \"welcome-email\",\n * category: \"onboarding\",\n * schema: t.object({ username: t.text(), activationLink: t.text() }),\n * email: {\n * subject: \"Welcome to our platform!\",\n * body: (vars) => `Hello ${vars.username}, click: ${vars.activationLink}`\n * }\n * });\n *\n * async sendWelcome(user: User) {\n * await this.welcomeEmail.push({\n * variables: { username: user.name, activationLink: generateLink() },\n * contact: user.email\n * });\n * }\n * }\n * ```\n */\nexport const $notification = <T extends TObject>(\n options: NotificationDescriptorOptions<T>,\n) => createDescriptor(NotificationDescriptor<T>, options);\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface NotificationDescriptorOptions<T extends TObject>\n extends NotificationMessage<T> {\n name?: string;\n description?: string;\n category?: string;\n critical?: boolean;\n sensitive?: boolean;\n translations?: {\n // e.g., \"en\", \"fr\", even \"en-US\"\n [lang: string]: NotificationMessage<T>;\n };\n schema: T;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class NotificationDescriptor<T extends TObject> extends Descriptor<\n NotificationDescriptorOptions<T>\n> {\n protected readonly notificationService = $inject(NotificationService);\n\n public get name() {\n return this.options.name ?? `${this.config.propertyKey}`;\n }\n\n public async push(options: NotificationPushOptions<T>) {\n if (this.options.email) {\n await this.notificationService.createNotification({\n ...options,\n type: \"email\",\n template: this.name,\n });\n }\n }\n\n public configure(options: Partial<NotificationDescriptorOptions<T>>) {\n Object.assign(this.options, options);\n }\n}\n\n$notification[KIND] = NotificationDescriptor;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface NotificationPushOptions<T extends TObject> {\n variables: StaticEncode<T>;\n contact: string;\n}\n\nexport interface NotificationMessage<T extends TObject> {\n email?: {\n subject: string;\n body: string | ((variables: Static<T>) => string);\n };\n sms?: {\n message: string | ((variables: Static<T>) => string);\n };\n}\n","export class NotificationJobs {\n // - retry (lost, failed) notifications\n // - purge old notifications\n}\n","import { SmsProvider, type SmsSendOptions } from \"./SmsProvider.ts\";\n\nexport interface SmsRecord {\n to: string;\n message: string;\n sentAt: Date;\n}\n\nexport class MemorySmsProvider extends SmsProvider {\n protected records: SmsRecord[] = [];\n\n public async send(options: SmsSendOptions): Promise<void> {\n const { to, message } = options;\n this.records.push({\n to,\n message,\n sentAt: new Date(),\n });\n }\n}\n","import { type Static, t } from \"alepha\";\n\nexport const notificationContactPreferencesSchema = t.object({\n language: t.optional(t.text()),\n exclude: t.array(t.text()),\n});\n\nexport type NotificationContactPreferences = Static<\n typeof notificationContactPreferencesSchema\n>;\n","import { $module } from \"alepha\";\nimport { NotificationController } from \"./controllers/NotificationController.ts\";\nimport { $notification } from \"./descriptors/$notification.ts\";\nimport { NotificationJobs } from \"./jobs/NotificationJobs.ts\";\nimport { MemorySmsProvider } from \"./providers/MemorySmsProvider.ts\";\nimport { SmsProvider } from \"./providers/SmsProvider.ts\";\nimport { NotificationQueues } from \"./queues/NotificationQueues.ts\";\nimport { NotificationSenderService } from \"./services/NotificationSenderService.ts\";\nimport {\n NotificationService,\n notificationServiceEnvSchema,\n} from \"./services/NotificationService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./controllers/NotificationController.ts\";\nexport * from \"./descriptors/$notification.ts\";\nexport * from \"./entities/notifications.ts\";\nexport * from \"./jobs/NotificationJobs.ts\";\nexport * from \"./providers/MemorySmsProvider.ts\";\nexport * from \"./providers/SmsProvider.ts\";\nexport * from \"./queues/NotificationQueues.ts\";\nexport * from \"./schemas/notificationContactPreferencesSchema.ts\";\nexport * from \"./schemas/notificationCreateSchema.ts\";\nexport * from \"./services/NotificationSenderService.ts\";\nexport * from \"./services/NotificationService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Provides notification management API endpoints for Alepha applications.\n *\n * This module includes notification sending, retrieval, status tracking,\n * and user notification preferences management.\n *\n * @module alepha.api.notifications\n */\nexport const AlephaApiNotifications = $module({\n name: \"alepha.api.notifications\",\n descriptors: [$notification],\n services: [\n NotificationController,\n NotificationService,\n NotificationSenderService,\n NotificationQueues,\n NotificationJobs,\n SmsProvider,\n MemorySmsProvider,\n ],\n register: (alepha) => {\n alepha.with({\n optional: true,\n provide: SmsProvider,\n use: MemorySmsProvider,\n });\n\n const env = alepha.parseEnv(notificationServiceEnvSchema);\n if (env.NOTIFICATION_QUEUE) {\n alepha.with(NotificationQueues);\n }\n\n alepha\n .with(NotificationController)\n .with(NotificationService)\n .with(NotificationSenderService)\n .with(NotificationJobs);\n },\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["notifications"],"sources":["../../src/api-notifications/entities/notifications.ts","../../src/api-notifications/schemas/notificationQuerySchema.ts","../../src/api-notifications/primitives/$notification.ts","../../src/api-notifications/services/NotificationSenderService.ts","../../src/api-notifications/queues/NotificationQueues.ts","../../src/api-notifications/schemas/notificationCreateSchema.ts","../../src/api-notifications/services/NotificationService.ts","../../src/api-notifications/controllers/NotificationController.ts","../../src/api-notifications/jobs/NotificationJobs.ts","../../src/api-notifications/schemas/notificationContactPreferencesSchema.ts","../../src/api-notifications/index.ts"],"sourcesContent":["import { type Static, t } from \"alepha\";\nimport { $entity, pg } from \"alepha/orm\";\n\nexport const notifications = $entity({\n name: \"notifications\",\n schema: t.object({\n id: pg.primaryKey(t.uuid()),\n\n version: pg.version(),\n\n createdAt: pg.createdAt(),\n\n updatedAt: pg.updatedAt(),\n\n // -----------------------------------------------------------------------------------------------------------------\n\n type: t.enum([\"email\", \"sms\"]),\n\n template: t.text(), // e.g. 'resetPassword'\n\n category: t.optional(\n t.text({\n description:\n \"For grouping related notifications (e.g., 'authentication', 'marketing'). Contact can filter notifications by category.\",\n }),\n ),\n\n critical: t.optional(\n t.boolean({\n description:\n \"Prioritize delivery of this notification. Set to true for important system alerts.\",\n }),\n ),\n\n sensitive: t.optional(\n t.boolean({\n description:\n \"Message won't be logged or stored in plain text. Set to true when notification contains passwords or codes.\",\n }),\n ),\n\n // -----------------------------------------------------------------------------------------------------------------\n\n contact: t.text(), // e.g. email address or phone number or user ID or whatever\n\n variables: t.optional(t.record(t.text(), t.any())),\n\n scheduledAt: t.optional(\n t.datetime({\n description:\n \"When set, the notification will be sent at or after this date/time.\",\n }),\n ),\n\n // -----------------------------------------------------------------------------------------------------------------\n\n sentAt: t.optional(t.datetime()),\n\n error: t.optional(\n t.object({\n at: t.datetime(),\n name: t.text(),\n message: t.text({ size: \"rich\" }),\n }),\n ),\n\n // TODO: retryCount, lastRetryAt, etc.\n }),\n});\n\nexport type NotificationEntity = Static<typeof notifications.schema>;\n","import type { Static } from \"alepha\";\nimport { t } from \"alepha\";\nimport { pageQuerySchema } from \"alepha/orm\";\n\nexport const notificationQuerySchema = t.extend(pageQuerySchema, {\n type: t.optional(t.enum([\"email\", \"sms\"])),\n template: t.optional(t.string()),\n contact: t.optional(t.string()),\n category: t.optional(t.string()),\n status: t.optional(t.enum([\"pending\", \"sent\", \"failed\"])),\n});\n\nexport type NotificationQuery = Static<typeof notificationQuerySchema>;\n","import {\n $inject,\n createPrimitive,\n KIND,\n Primitive,\n type Static,\n type StaticEncode,\n type TObject,\n} from \"alepha\";\nimport { NotificationService } from \"../services/NotificationService.ts\";\n\n/**\n * Creates a notification primitive for managing email/SMS notification templates.\n *\n * Provides type-safe, reusable notification templates with multi-language support,\n * variable substitution, and categorization for different notification channels.\n *\n * @example\n * ```ts\n * class NotificationTemplates {\n * welcomeEmail = $notification({\n * name: \"welcome-email\",\n * category: \"onboarding\",\n * schema: t.object({ username: t.text(), activationLink: t.text() }),\n * email: {\n * subject: \"Welcome to our platform!\",\n * body: (vars) => `Hello ${vars.username}, click: ${vars.activationLink}`\n * }\n * });\n *\n * async sendWelcome(user: User) {\n * await this.welcomeEmail.push({\n * variables: { username: user.name, activationLink: generateLink() },\n * contact: user.email\n * });\n * }\n * }\n * ```\n */\nexport const $notification = <T extends TObject>(\n options: NotificationPrimitiveOptions<T>,\n) => createPrimitive(NotificationPrimitive<T>, options);\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface NotificationPrimitiveOptions<T extends TObject>\n extends NotificationMessage<T> {\n name?: string;\n description?: string;\n category?: string;\n critical?: boolean;\n sensitive?: boolean;\n translations?: {\n // e.g., \"en\", \"fr\", even \"en-US\"\n [lang: string]: NotificationMessage<T>;\n };\n schema: T;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class NotificationPrimitive<T extends TObject> extends Primitive<\n NotificationPrimitiveOptions<T>\n> {\n protected readonly notificationService = $inject(NotificationService);\n\n public get name() {\n return this.options.name ?? `${this.config.propertyKey}`;\n }\n\n public async push(options: NotificationPushOptions<T>) {\n if (this.options.email) {\n await this.notificationService.createNotification({\n ...options,\n type: \"email\",\n template: this.name,\n });\n }\n }\n\n public configure(options: Partial<NotificationPrimitiveOptions<T>>) {\n Object.assign(this.options, options);\n }\n}\n\n$notification[KIND] = NotificationPrimitive;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface NotificationPushOptions<T extends TObject> {\n variables: StaticEncode<T>;\n contact: string;\n}\n\nexport interface NotificationMessage<T extends TObject> {\n email?: {\n subject: string;\n body: string | ((variables: Static<T>) => string);\n };\n sms?: {\n message: string | ((variables: Static<T>) => string);\n };\n}\n","import { $inject, Alepha, AlephaError } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { EmailProvider } from \"alepha/email\";\nimport { $logger } from \"alepha/logger\";\nimport { $repository } from \"alepha/orm\";\nimport { SmsProvider } from \"alepha/sms\";\nimport {\n type NotificationEntity,\n notifications,\n} from \"../entities/notifications.ts\";\nimport { $notification } from \"../primitives/$notification.ts\";\n\nexport class NotificationSenderService {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly notificationRepository = $repository(notifications);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly emailProvider = $inject(EmailProvider);\n protected readonly smsProvider = $inject(SmsProvider);\n\n public async send(notificationId: string | NotificationEntity) {\n this.log.trace(\"Sending notification\", {\n notificationId:\n typeof notificationId === \"string\" ? notificationId : notificationId.id,\n });\n\n const notification =\n typeof notificationId === \"string\"\n ? await this.notificationRepository.findById(notificationId)\n : notificationId;\n\n if (notification.sentAt) {\n this.log.debug(\"Notification already sent\", {\n notificationId: notification.id,\n sentAt: notification.sentAt,\n });\n return;\n }\n\n this.log.debug(\"Processing notification\", {\n id: notification.id,\n type: notification.type,\n template: notification.template,\n contact: notification.contact,\n });\n\n try {\n if (notification.type === \"email\") {\n await this.emailProvider.send(this.renderEmail(notification));\n notification.sentAt = this.dateTimeProvider.nowISOString();\n this.log.info(\"Email notification sent\", {\n id: notification.id,\n template: notification.template,\n contact: notification.contact,\n });\n }\n if (notification.type === \"sms\") {\n await this.smsProvider.send(this.renderSms(notification));\n notification.sentAt = this.dateTimeProvider.nowISOString();\n this.log.info(\"SMS notification sent\", {\n id: notification.id,\n template: notification.template,\n contact: notification.contact,\n });\n }\n } catch (e) {\n this.log.error(\"Failed to send notification\", {\n id: notification.id,\n type: notification.type,\n template: notification.template,\n contact: notification.contact,\n error: e,\n });\n if (e instanceof Error) {\n notification.error = {\n at: this.dateTimeProvider.nowISOString(),\n name: e.name,\n message: e.message,\n };\n }\n } finally {\n await this.notificationRepository.save(notification);\n }\n }\n\n public renderSms(notification: NotificationEntity) {\n this.log.trace(\"Rendering SMS notification\", {\n id: notification.id,\n template: notification.template,\n });\n\n const { variables, contact, template } = this.load(notification);\n\n const sms = template.options.sms;\n if (!sms) {\n this.log.error(\"Notification template has no SMS defined\", {\n id: notification.id,\n template: notification.template,\n });\n throw new AlephaError(\n `Notification template ${notification.template} has no sms defined`,\n );\n }\n\n this.log.debug(\"Rendering SMS\", {\n template: notification.template,\n contact,\n });\n\n const message =\n typeof sms.message === \"function\"\n ? sms.message(variables as any)\n : sms.message;\n\n return {\n to: contact,\n message,\n };\n }\n\n public renderEmail(notification: NotificationEntity) {\n this.log.trace(\"Rendering email notification\", {\n id: notification.id,\n template: notification.template,\n });\n\n const { variables, contact, template } = this.load(notification);\n\n const email = template.options.email;\n if (!email) {\n this.log.error(\"Notification template has no email defined\", {\n id: notification.id,\n template: notification.template,\n });\n throw new AlephaError(\n `Notification template ${notification.template} has no email defined`,\n );\n }\n\n this.log.debug(\"Rendering email\", {\n template: notification.template,\n contact,\n subject: email.subject,\n });\n\n const subject = email.subject;\n\n const body =\n typeof email.body === \"function\"\n ? email.body(variables as any)\n : email.body;\n\n return {\n to: contact,\n subject,\n body,\n };\n }\n\n protected load(notification: NotificationEntity) {\n const variables = notification.variables || {};\n const contact = notification.contact;\n const template = this.alepha\n .primitives($notification)\n .find((it) => it.name === notification.template);\n\n if (!template) {\n this.log.error(\"Notification template not found\", {\n id: notification.id,\n template: notification.template,\n });\n throw new AlephaError(\n `No notification template found for ${notification.template}`,\n );\n }\n\n return {\n template,\n variables,\n contact,\n };\n }\n}\n","import { $inject, t } from \"alepha\";\nimport { $queue } from \"alepha/queue\";\nimport { NotificationSenderService } from \"../services/NotificationSenderService.ts\";\n\nexport class NotificationQueues {\n protected readonly notificationSenderService = $inject(\n NotificationSenderService,\n );\n\n public readonly processNotification = $queue({\n description: \"Queue for processing notifications\",\n schema: t.object({\n notificationId: t.string({ format: \"uuid\" }),\n }),\n handler: async (message) => {\n await this.notificationSenderService.send(message.payload.notificationId);\n },\n });\n}\n","import { type Static, t } from \"alepha\";\nimport { notifications } from \"../entities/notifications.ts\";\n\nexport const notificationCreateSchema = t.pick(notifications.schema, [\n \"type\",\n \"contact\",\n \"template\",\n \"variables\",\n]);\n\nexport type NotificationCreate = Static<typeof notificationCreateSchema>;\n","import { $env, $inject, Alepha, type Static, t } from \"alepha\";\nimport { $batch } from \"alepha/batch\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { $repository, type Page } from \"alepha/orm\";\nimport {\n type NotificationEntity,\n notifications,\n} from \"../entities/notifications.ts\";\nimport { NotificationQueues } from \"../queues/NotificationQueues.ts\";\nimport {\n type NotificationCreate,\n notificationCreateSchema,\n} from \"../schemas/notificationCreateSchema.ts\";\nimport type { NotificationQuery } from \"../schemas/notificationQuerySchema.ts\";\nimport { NotificationSenderService } from \"./NotificationSenderService.ts\";\n\nexport const notificationServiceEnvSchema = t.object({\n NOTIFICATION_QUEUE: t.optional(\n t.boolean({\n description:\n \"If true, notifications will be queued instead of sent immediately\",\n }),\n ),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof notificationServiceEnvSchema>> {}\n}\n\nexport class NotificationService {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly env = $env(notificationServiceEnvSchema);\n protected readonly notificationRepository = $repository(notifications);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly notificationSenderService = $inject(\n NotificationSenderService,\n );\n\n public readonly notificationBatch = $batch({\n maxSize: 100,\n maxDuration: [15, \"seconds\"],\n schema: notificationCreateSchema,\n handler: async (notifications: NotificationCreate[]) => {\n this.log.debug(\"Processing notification batch\", {\n size: notifications.length,\n templates: [...new Set(notifications.map((n) => n.template))],\n });\n\n const entities =\n await this.notificationRepository.createMany(notifications);\n\n await this.alepha\n .inject(NotificationQueues)\n .processNotification.push(\n ...entities.map((it) => ({ notificationId: it.id })),\n );\n\n this.log.info(\"Notification batch queued\", {\n count: entities.length,\n ids: entities.map((it) => it.id),\n });\n },\n });\n\n public async findNotificationById(id: string) {\n this.log.trace(\"Finding notification by ID\", { id });\n return this.notificationRepository.findOne({ where: { id } });\n }\n\n public async findNotifications(\n q: NotificationQuery = {},\n ): Promise<Page<NotificationEntity>> {\n this.log.trace(\"Finding notifications\", { query: q });\n q.sort ??= \"-createdAt\";\n\n const where = this.notificationRepository.createQueryWhere();\n\n if (q.type) {\n where.type = { eq: q.type };\n }\n\n if (q.template) {\n where.template = { like: `%${q.template}%` };\n }\n\n if (q.contact) {\n where.contact = { like: `%${q.contact}%` };\n }\n\n if (q.category) {\n where.category = { eq: q.category };\n }\n\n if (q.status) {\n if (q.status === \"sent\") {\n where.sentAt = { isNotNull: true };\n where.error = { isNull: true };\n } else if (q.status === \"failed\") {\n where.error = { isNotNull: true };\n } else if (q.status === \"pending\") {\n where.sentAt = { isNull: true };\n where.error = { isNull: true };\n }\n }\n\n return this.notificationRepository.paginate(q, { where }, { count: true });\n }\n\n /**\n * Create a new notification.\n */\n public async createNotification(entry: NotificationCreate): Promise<void> {\n this.log.trace(\"Creating notification\", {\n template: entry.template,\n type: entry.type,\n contact: entry.contact,\n });\n\n if (\n this.env.NOTIFICATION_QUEUE !== true ||\n this.alepha.isServerless() ||\n this.alepha.isTest()\n ) {\n this.log.debug(\"Sending notification immediately\", {\n template: entry.template,\n type: entry.type,\n contact: entry.contact,\n });\n const notification = await this.notificationRepository.create(entry);\n await this.notificationSenderService.send(notification);\n return;\n }\n\n this.log.debug(\"Queuing notification to batch\", {\n template: entry.template,\n type: entry.type,\n contact: entry.contact,\n });\n\n this.notificationBatch.push(entry).catch((e) => {\n this.log.error(\"Failed to push notification to batch\", {\n template: entry.template,\n type: entry.type,\n contact: entry.contact,\n error: e,\n });\n });\n }\n}\n","import { $inject } from \"alepha\";\nimport { pg } from \"alepha/orm\";\nimport { $action } from \"alepha/server\";\nimport { notifications } from \"../entities/notifications.ts\";\nimport { notificationQuerySchema } from \"../schemas/notificationQuerySchema.ts\";\nimport { NotificationService } from \"../services/NotificationService.ts\";\n\nexport class NotificationController {\n protected readonly url = \"/notifications\";\n protected readonly group = \"notifications\";\n protected readonly notificationService = $inject(NotificationService);\n\n /**\n * Find notifications with pagination and filtering.\n */\n public readonly findNotifications = $action({\n path: this.url,\n group: this.group,\n description: \"Find notifications with pagination and filtering\",\n schema: {\n query: notificationQuerySchema,\n response: pg.page(notifications.schema),\n },\n handler: ({ query }) => this.notificationService.findNotifications(query),\n });\n}\n","export class NotificationJobs {\n // - retry (lost, failed) notifications\n // - purge old notifications\n}\n","import { type Static, t } from \"alepha\";\n\nexport const notificationContactPreferencesSchema = t.object({\n language: t.optional(t.text()),\n exclude: t.array(t.text()),\n});\n\nexport type NotificationContactPreferences = Static<\n typeof notificationContactPreferencesSchema\n>;\n","import { $module } from \"alepha\";\nimport { NotificationController } from \"./controllers/NotificationController.ts\";\nimport { NotificationJobs } from \"./jobs/NotificationJobs.ts\";\nimport { $notification } from \"./primitives/$notification.ts\";\nimport { NotificationQueues } from \"./queues/NotificationQueues.ts\";\nimport { NotificationSenderService } from \"./services/NotificationSenderService.ts\";\nimport {\n NotificationService,\n notificationServiceEnvSchema,\n} from \"./services/NotificationService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./controllers/NotificationController.ts\";\nexport * from \"./entities/notifications.ts\";\nexport * from \"./jobs/NotificationJobs.ts\";\nexport * from \"./primitives/$notification.ts\";\nexport * from \"./queues/NotificationQueues.ts\";\nexport * from \"./schemas/notificationContactPreferencesSchema.ts\";\nexport * from \"./schemas/notificationCreateSchema.ts\";\nexport * from \"./schemas/notificationQuerySchema.ts\";\nexport * from \"./services/NotificationSenderService.ts\";\nexport * from \"./services/NotificationService.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Provides notification management API endpoints for Alepha applications.\n *\n * This module includes notification sending, retrieval, status tracking,\n * and user notification preferences management.\n *\n * Requires `AlephaSms` module to be loaded for SMS notifications.\n *\n * @module alepha.api.notifications\n */\nexport const AlephaApiNotifications = $module({\n name: \"alepha.api.notifications\",\n primitives: [$notification],\n services: [\n NotificationController,\n NotificationService,\n NotificationSenderService,\n NotificationQueues,\n NotificationJobs,\n ],\n register: (alepha) => {\n const env = alepha.parseEnv(notificationServiceEnvSchema);\n if (env.NOTIFICATION_QUEUE) {\n alepha.with(NotificationQueues);\n }\n\n alepha\n .with(NotificationController)\n .with(NotificationService)\n .with(NotificationSenderService)\n .with(NotificationJobs);\n },\n});\n"],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import { $module, t } from "alepha";
2
+ import { $entity, pg } from "alepha/orm";
3
+
4
+ //#region src/api-parameters/entities/parameters.ts
5
+ const parameters = $entity({
6
+ name: "parameters",
7
+ schema: t.object({
8
+ id: pg.primaryKey(t.uuid()),
9
+ createdAt: pg.createdAt(),
10
+ updatedAt: pg.updatedAt(),
11
+ name: t.string(),
12
+ content: t.json(),
13
+ tags: t.optional(t.array(t.string())),
14
+ creatorId: t.optional(t.uuid()),
15
+ creatorName: t.optional(t.string()),
16
+ activationDate: t.datetime({ description: "Optional activation date. Default to now. Must be now or later." })
17
+ })
18
+ });
19
+
20
+ //#endregion
21
+ //#region src/api-parameters/index.browser.ts
22
+ const AlephaApiParameters = $module({
23
+ name: "alepha.api.parameters",
24
+ services: []
25
+ });
26
+
27
+ //#endregion
28
+ export { AlephaApiParameters, parameters };
29
+ //# sourceMappingURL=index.browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.browser.js","names":[],"sources":["../../src/api-parameters/entities/parameters.ts","../../src/api-parameters/index.browser.ts"],"sourcesContent":["import { type Static, t } from \"alepha\";\nimport { $entity, pg } from \"alepha/orm\";\n\nexport const parameters = $entity({\n name: \"parameters\",\n schema: t.object({\n id: pg.primaryKey(t.uuid()),\n\n createdAt: pg.createdAt(),\n\n updatedAt: pg.updatedAt(),\n\n name: t.string(),\n\n content: t.json(),\n\n tags: t.optional(t.array(t.string())),\n\n creatorId: t.optional(t.uuid()),\n\n creatorName: t.optional(t.string()),\n\n activationDate: t.datetime({\n description:\n \"Optional activation date. Default to now. Must be now or later.\",\n }),\n }),\n});\n\nexport type ParameterEntity = Static<typeof parameters.schema>;\n","import { $module } from \"alepha\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./entities/parameters.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport const AlephaApiParameters = $module({\n name: \"alepha.api.parameters\",\n services: [],\n});\n"],"mappings":";;;;AAGA,MAAa,aAAa,QAAQ;CAChC,MAAM;CACN,QAAQ,EAAE,OAAO;EACf,IAAI,GAAG,WAAW,EAAE,MAAM,CAAC;EAE3B,WAAW,GAAG,WAAW;EAEzB,WAAW,GAAG,WAAW;EAEzB,MAAM,EAAE,QAAQ;EAEhB,SAAS,EAAE,MAAM;EAEjB,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;EAErC,WAAW,EAAE,SAAS,EAAE,MAAM,CAAC;EAE/B,aAAa,EAAE,SAAS,EAAE,QAAQ,CAAC;EAEnC,gBAAgB,EAAE,SAAS,EACzB,aACE,mEACH,CAAC;EACH,CAAC;CACH,CAAC;;;;ACnBF,MAAa,sBAAsB,QAAQ;CACzC,MAAM;CACN,UAAU,EAAE;CACb,CAAC"}
@@ -1,12 +1,25 @@
1
1
  import * as alepha1 from "alepha";
2
- import { Descriptor, Static, TObject } from "alepha";
2
+ import { Primitive, Static, TObject } from "alepha";
3
3
  import * as alepha_orm0 from "alepha/orm";
4
4
  import { UserAccount } from "alepha/security";
5
5
 
6
- //#region src/api-parameters/descriptors/$config.d.ts
7
-
6
+ //#region src/api-parameters/entities/parameters.d.ts
7
+ declare const parameters: alepha_orm0.EntityPrimitive<alepha1.TObject<{
8
+ id: alepha_orm0.PgAttr<alepha_orm0.PgAttr<alepha1.TString, typeof alepha_orm0.PG_PRIMARY_KEY>, typeof alepha_orm0.PG_DEFAULT>;
9
+ createdAt: alepha_orm0.PgAttr<alepha_orm0.PgAttr<alepha1.TString, typeof alepha_orm0.PG_CREATED_AT>, typeof alepha_orm0.PG_DEFAULT>;
10
+ updatedAt: alepha_orm0.PgAttr<alepha_orm0.PgAttr<alepha1.TString, typeof alepha_orm0.PG_UPDATED_AT>, typeof alepha_orm0.PG_DEFAULT>;
11
+ name: alepha1.TString;
12
+ content: alepha1.TRecord<string, alepha1.TAny>;
13
+ tags: alepha1.TOptional<alepha1.TArray<alepha1.TString>>;
14
+ creatorId: alepha1.TOptional<alepha1.TString>;
15
+ creatorName: alepha1.TOptional<alepha1.TString>;
16
+ activationDate: alepha1.TString;
17
+ }>>;
18
+ type ParameterEntity = Static<typeof parameters.schema>;
19
+ //#endregion
20
+ //#region src/api-parameters/primitives/$config.d.ts
8
21
  /**
9
- * Creates a configuration parameter descriptor for managing application settings.
22
+ * Creates a configuration parameter primitive for managing application settings.
10
23
  *
11
24
  * Provides type-safe, versioned configuration with schema validation, default values,
12
25
  * and scheduled activation. Useful for feature flags, system parameters, and runtime settings.
@@ -32,13 +45,13 @@ import { UserAccount } from "alepha/security";
32
45
  * }
33
46
  * ```
34
47
  */
35
- interface ConfigDescriptorOptions<T extends TObject> {
48
+ interface ConfigPrimitiveOptions<T extends TObject> {
36
49
  name?: string;
37
50
  description?: string;
38
51
  schema: T;
39
52
  default: Static<T>;
40
53
  }
41
- declare class ConfigDescriptor<T extends TObject> extends Descriptor<ConfigDescriptorOptions<T>> {
54
+ declare class ConfigPrimitive<T extends TObject> extends Primitive<ConfigPrimitiveOptions<T>> {
42
55
  get name(): string;
43
56
  get schema(): T;
44
57
  get current(): Static<T>;
@@ -53,21 +66,7 @@ declare class ConfigDescriptor<T extends TObject> extends Descriptor<ConfigDescr
53
66
  }): Promise<void>;
54
67
  sub(fn: (curr: Static<T>) => void): void;
55
68
  }
56
- declare const $config: <T extends TObject>(options: ConfigDescriptorOptions<T>) => ConfigDescriptor<T>;
57
- //#endregion
58
- //#region src/api-parameters/entities/parameters.d.ts
59
- declare const parameters: alepha_orm0.EntityDescriptor<alepha1.TObject<{
60
- id: alepha_orm0.PgAttr<alepha_orm0.PgAttr<alepha1.TString, typeof alepha_orm0.PG_PRIMARY_KEY>, typeof alepha_orm0.PG_DEFAULT>;
61
- createdAt: alepha_orm0.PgAttr<alepha_orm0.PgAttr<alepha1.TString, typeof alepha_orm0.PG_CREATED_AT>, typeof alepha_orm0.PG_DEFAULT>;
62
- updatedAt: alepha_orm0.PgAttr<alepha_orm0.PgAttr<alepha1.TString, typeof alepha_orm0.PG_UPDATED_AT>, typeof alepha_orm0.PG_DEFAULT>;
63
- name: alepha1.TString;
64
- content: alepha1.TRecord<string, alepha1.TAny>;
65
- tags: alepha1.TOptional<alepha1.TArray<alepha1.TString>>;
66
- creatorId: alepha1.TOptional<alepha1.TString>;
67
- creatorName: alepha1.TOptional<alepha1.TString>;
68
- activationDate: alepha1.TString;
69
- }>>;
70
- type ParameterEntity = Static<typeof parameters.schema>;
69
+ declare const $config: <T extends TObject>(options: ConfigPrimitiveOptions<T>) => ConfigPrimitive<T>;
71
70
  //#endregion
72
71
  //#region src/api-parameters/index.d.ts
73
72
  /**
@@ -80,5 +79,5 @@ type ParameterEntity = Static<typeof parameters.schema>;
80
79
  */
81
80
  declare const AlephaApiParameters: alepha1.Service<alepha1.Module>;
82
81
  //#endregion
83
- export { $config, AlephaApiParameters, ConfigDescriptor, ConfigDescriptorOptions, ParameterEntity, parameters };
82
+ export { $config, AlephaApiParameters, ConfigPrimitive, ConfigPrimitiveOptions, ParameterEntity, parameters };
84
83
  //# sourceMappingURL=index.d.ts.map
@@ -1,8 +1,25 @@
1
- import { $module, Descriptor, createDescriptor, t } from "alepha";
1
+ import { $module, Primitive, createPrimitive, t } from "alepha";
2
2
  import { $entity, pg } from "alepha/orm";
3
3
 
4
- //#region src/api-parameters/descriptors/$config.ts
5
- var ConfigDescriptor = class extends Descriptor {
4
+ //#region src/api-parameters/entities/parameters.ts
5
+ const parameters = $entity({
6
+ name: "parameters",
7
+ schema: t.object({
8
+ id: pg.primaryKey(t.uuid()),
9
+ createdAt: pg.createdAt(),
10
+ updatedAt: pg.updatedAt(),
11
+ name: t.string(),
12
+ content: t.json(),
13
+ tags: t.optional(t.array(t.string())),
14
+ creatorId: t.optional(t.uuid()),
15
+ creatorName: t.optional(t.string()),
16
+ activationDate: t.datetime({ description: "Optional activation date. Default to now. Must be now or later." })
17
+ })
18
+ });
19
+
20
+ //#endregion
21
+ //#region src/api-parameters/primitives/$config.ts
22
+ var ConfigPrimitive = class extends Primitive {
6
23
  get name() {
7
24
  return this.options.name || this.config.propertyKey;
8
25
  }
@@ -23,26 +40,9 @@ var ConfigDescriptor = class extends Descriptor {
23
40
  sub(fn) {}
24
41
  };
25
42
  const $config = (options) => {
26
- return createDescriptor(ConfigDescriptor, options);
43
+ return createPrimitive(ConfigPrimitive, options);
27
44
  };
28
45
 
29
- //#endregion
30
- //#region src/api-parameters/entities/parameters.ts
31
- const parameters = $entity({
32
- name: "parameters",
33
- schema: t.object({
34
- id: pg.primaryKey(t.uuid()),
35
- createdAt: pg.createdAt(),
36
- updatedAt: pg.updatedAt(),
37
- name: t.string(),
38
- content: t.json(),
39
- tags: t.optional(t.array(t.string())),
40
- creatorId: t.optional(t.uuid()),
41
- creatorName: t.optional(t.string()),
42
- activationDate: t.datetime({ description: "Optional activation date. Default to now. Must be now or later." })
43
- })
44
- });
45
-
46
46
  //#endregion
47
47
  //#region src/api-parameters/index.ts
48
48
  /**
@@ -59,5 +59,5 @@ const AlephaApiParameters = $module({
59
59
  });
60
60
 
61
61
  //#endregion
62
- export { $config, AlephaApiParameters, ConfigDescriptor, parameters };
62
+ export { $config, AlephaApiParameters, ConfigPrimitive, parameters };
63
63
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/api-parameters/descriptors/$config.ts","../../src/api-parameters/entities/parameters.ts","../../src/api-parameters/index.ts"],"sourcesContent":["import {\n createDescriptor,\n Descriptor,\n type Static,\n type TObject,\n} from \"alepha\";\nimport type { UserAccount } from \"alepha/security\";\n\n/**\n * Creates a configuration parameter descriptor for managing application settings.\n *\n * Provides type-safe, versioned configuration with schema validation, default values,\n * and scheduled activation. Useful for feature flags, system parameters, and runtime settings.\n *\n * @example\n * ```ts\n * class AppConfig {\n * features = $config({\n * name: \"feature-flags\",\n * schema: t.object({\n * enableBeta: t.boolean(),\n * maxUploadSize: t.number()\n * }),\n * default: { enableBeta: false, maxUploadSize: 10485760 }\n * });\n *\n * async updateFeatures() {\n * await this.features.set(\n * { enableBeta: true, maxUploadSize: 20971520 },\n * { user: currentUser, activationDate: tomorrow }\n * );\n * }\n * }\n * ```\n */\nexport interface ConfigDescriptorOptions<T extends TObject> {\n name?: string;\n description?: string;\n schema: T;\n default: Static<T>;\n}\n\nexport class ConfigDescriptor<T extends TObject> extends Descriptor<\n ConfigDescriptorOptions<T>\n> {\n public get name() {\n return this.options.name || this.config.propertyKey;\n }\n\n public get schema(): T {\n return this.options.schema;\n }\n\n public get current(): Static<T> {\n return this.options.default;\n }\n\n public get next(): Static<T> | undefined {\n return undefined;\n }\n\n public get<Key extends keyof Static<T>>(key: Key): Static<T>[Key] {\n return this.current[key];\n }\n\n /**\n * Apply a new configuration object.\n */\n public async set(\n value: Static<T>,\n options: {\n user?: UserAccount;\n activationDate?: Date; // default to now\n },\n ): Promise<void> {}\n\n public sub(fn: (curr: Static<T>) => void): void {}\n}\n\nexport const $config = <T extends TObject>(\n options: ConfigDescriptorOptions<T>,\n) => {\n return createDescriptor(ConfigDescriptor<T>, options);\n};\n","import { type Static, t } from \"alepha\";\nimport { $entity, pg } from \"alepha/orm\";\n\nexport const parameters = $entity({\n name: \"parameters\",\n schema: t.object({\n id: pg.primaryKey(t.uuid()),\n\n createdAt: pg.createdAt(),\n\n updatedAt: pg.updatedAt(),\n\n name: t.string(),\n\n content: t.json(),\n\n tags: t.optional(t.array(t.string())),\n\n creatorId: t.optional(t.uuid()),\n\n creatorName: t.optional(t.string()),\n\n activationDate: t.datetime({\n description:\n \"Optional activation date. Default to now. Must be now or later.\",\n }),\n }),\n});\n\nexport type ParameterEntity = Static<typeof parameters.schema>;\n","import { $module } from \"alepha\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./descriptors/$config.ts\";\nexport * from \"./entities/parameters.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Provides parameter management API endpoints for Alepha applications.\n *\n * This module includes configuration parameter storage, retrieval,\n * and dynamic application settings management.\n *\n * @module alepha.api.parameters\n */\nexport const AlephaApiParameters = $module({\n name: \"alepha.api.parameters\",\n services: [],\n});\n"],"mappings":";;;;AA0CA,IAAa,mBAAb,cAAyD,WAEvD;CACA,IAAW,OAAO;AAChB,SAAO,KAAK,QAAQ,QAAQ,KAAK,OAAO;;CAG1C,IAAW,SAAY;AACrB,SAAO,KAAK,QAAQ;;CAGtB,IAAW,UAAqB;AAC9B,SAAO,KAAK,QAAQ;;CAGtB,IAAW,OAA8B;CAIzC,AAAO,IAAiC,KAA0B;AAChE,SAAO,KAAK,QAAQ;;;;;CAMtB,MAAa,IACX,OACA,SAIe;CAEjB,AAAO,IAAI,IAAqC;;AAGlD,MAAa,WACX,YACG;AACH,QAAO,iBAAiB,kBAAqB,QAAQ;;;;;AC/EvD,MAAa,aAAa,QAAQ;CAChC,MAAM;CACN,QAAQ,EAAE,OAAO;EACf,IAAI,GAAG,WAAW,EAAE,MAAM,CAAC;EAE3B,WAAW,GAAG,WAAW;EAEzB,WAAW,GAAG,WAAW;EAEzB,MAAM,EAAE,QAAQ;EAEhB,SAAS,EAAE,MAAM;EAEjB,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;EAErC,WAAW,EAAE,SAAS,EAAE,MAAM,CAAC;EAE/B,aAAa,EAAE,SAAS,EAAE,QAAQ,CAAC;EAEnC,gBAAgB,EAAE,SAAS,EACzB,aACE,mEACH,CAAC;EACH,CAAC;CACH,CAAC;;;;;;;;;;;;ACVF,MAAa,sBAAsB,QAAQ;CACzC,MAAM;CACN,UAAU,EAAE;CACb,CAAC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/api-parameters/entities/parameters.ts","../../src/api-parameters/primitives/$config.ts","../../src/api-parameters/index.ts"],"sourcesContent":["import { type Static, t } from \"alepha\";\nimport { $entity, pg } from \"alepha/orm\";\n\nexport const parameters = $entity({\n name: \"parameters\",\n schema: t.object({\n id: pg.primaryKey(t.uuid()),\n\n createdAt: pg.createdAt(),\n\n updatedAt: pg.updatedAt(),\n\n name: t.string(),\n\n content: t.json(),\n\n tags: t.optional(t.array(t.string())),\n\n creatorId: t.optional(t.uuid()),\n\n creatorName: t.optional(t.string()),\n\n activationDate: t.datetime({\n description:\n \"Optional activation date. Default to now. Must be now or later.\",\n }),\n }),\n});\n\nexport type ParameterEntity = Static<typeof parameters.schema>;\n","import { createPrimitive, Primitive, type Static, type TObject } from \"alepha\";\nimport type { UserAccount } from \"alepha/security\";\n\n/**\n * Creates a configuration parameter primitive for managing application settings.\n *\n * Provides type-safe, versioned configuration with schema validation, default values,\n * and scheduled activation. Useful for feature flags, system parameters, and runtime settings.\n *\n * @example\n * ```ts\n * class AppConfig {\n * features = $config({\n * name: \"feature-flags\",\n * schema: t.object({\n * enableBeta: t.boolean(),\n * maxUploadSize: t.number()\n * }),\n * default: { enableBeta: false, maxUploadSize: 10485760 }\n * });\n *\n * async updateFeatures() {\n * await this.features.set(\n * { enableBeta: true, maxUploadSize: 20971520 },\n * { user: currentUser, activationDate: tomorrow }\n * );\n * }\n * }\n * ```\n */\nexport interface ConfigPrimitiveOptions<T extends TObject> {\n name?: string;\n description?: string;\n schema: T;\n default: Static<T>;\n}\n\nexport class ConfigPrimitive<T extends TObject> extends Primitive<\n ConfigPrimitiveOptions<T>\n> {\n public get name() {\n return this.options.name || this.config.propertyKey;\n }\n\n public get schema(): T {\n return this.options.schema;\n }\n\n public get current(): Static<T> {\n return this.options.default;\n }\n\n public get next(): Static<T> | undefined {\n return undefined;\n }\n\n public get<Key extends keyof Static<T>>(key: Key): Static<T>[Key] {\n return this.current[key];\n }\n\n /**\n * Apply a new configuration object.\n */\n public async set(\n value: Static<T>,\n options: {\n user?: UserAccount;\n activationDate?: Date; // default to now\n },\n ): Promise<void> {}\n\n public sub(fn: (curr: Static<T>) => void): void {}\n}\n\nexport const $config = <T extends TObject>(\n options: ConfigPrimitiveOptions<T>,\n) => {\n return createPrimitive(ConfigPrimitive<T>, options);\n};\n","import { $module } from \"alepha\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./entities/parameters.ts\";\nexport * from \"./primitives/$config.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Provides parameter management API endpoints for Alepha applications.\n *\n * This module includes configuration parameter storage, retrieval,\n * and dynamic application settings management.\n *\n * @module alepha.api.parameters\n */\nexport const AlephaApiParameters = $module({\n name: \"alepha.api.parameters\",\n services: [],\n});\n"],"mappings":";;;;AAGA,MAAa,aAAa,QAAQ;CAChC,MAAM;CACN,QAAQ,EAAE,OAAO;EACf,IAAI,GAAG,WAAW,EAAE,MAAM,CAAC;EAE3B,WAAW,GAAG,WAAW;EAEzB,WAAW,GAAG,WAAW;EAEzB,MAAM,EAAE,QAAQ;EAEhB,SAAS,EAAE,MAAM;EAEjB,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;EAErC,WAAW,EAAE,SAAS,EAAE,MAAM,CAAC;EAE/B,aAAa,EAAE,SAAS,EAAE,QAAQ,CAAC;EAEnC,gBAAgB,EAAE,SAAS,EACzB,aACE,mEACH,CAAC;EACH,CAAC;CACH,CAAC;;;;ACUF,IAAa,kBAAb,cAAwD,UAEtD;CACA,IAAW,OAAO;AAChB,SAAO,KAAK,QAAQ,QAAQ,KAAK,OAAO;;CAG1C,IAAW,SAAY;AACrB,SAAO,KAAK,QAAQ;;CAGtB,IAAW,UAAqB;AAC9B,SAAO,KAAK,QAAQ;;CAGtB,IAAW,OAA8B;CAIzC,AAAO,IAAiC,KAA0B;AAChE,SAAO,KAAK,QAAQ;;;;;CAMtB,MAAa,IACX,OACA,SAIe;CAEjB,AAAO,IAAI,IAAqC;;AAGlD,MAAa,WACX,YACG;AACH,QAAO,gBAAgB,iBAAoB,QAAQ;;;;;;;;;;;;;AC5DrD,MAAa,sBAAsB,QAAQ;CACzC,MAAM;CACN,UAAU,EAAE;CACb,CAAC"}