alepha 0.14.1 → 0.14.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 (402) hide show
  1. package/README.md +3 -3
  2. package/dist/api/audits/index.browser.js +5 -5
  3. package/dist/api/audits/index.browser.js.map +1 -1
  4. package/dist/api/audits/index.d.ts +784 -784
  5. package/dist/api/audits/index.d.ts.map +1 -1
  6. package/dist/api/audits/index.js +13 -13
  7. package/dist/api/audits/index.js.map +1 -1
  8. package/dist/api/files/index.browser.js +5 -5
  9. package/dist/api/files/index.browser.js.map +1 -1
  10. package/dist/api/files/index.d.ts +57 -57
  11. package/dist/api/files/index.d.ts.map +1 -1
  12. package/dist/api/files/index.js +71 -71
  13. package/dist/api/files/index.js.map +1 -1
  14. package/dist/api/jobs/index.browser.js +5 -5
  15. package/dist/api/jobs/index.browser.js.map +1 -1
  16. package/dist/api/jobs/index.d.ts +165 -165
  17. package/dist/api/jobs/index.d.ts.map +1 -1
  18. package/dist/api/jobs/index.js +10 -10
  19. package/dist/api/jobs/index.js.map +1 -1
  20. package/dist/api/notifications/index.browser.js +10 -10
  21. package/dist/api/notifications/index.browser.js.map +1 -1
  22. package/dist/api/notifications/index.d.ts +583 -171
  23. package/dist/api/notifications/index.d.ts.map +1 -1
  24. package/dist/api/notifications/index.js +12 -12
  25. package/dist/api/notifications/index.js.map +1 -1
  26. package/dist/api/parameters/index.browser.js +163 -10
  27. package/dist/api/parameters/index.browser.js.map +1 -1
  28. package/dist/api/parameters/index.d.ts +281 -276
  29. package/dist/api/parameters/index.d.ts.map +1 -1
  30. package/dist/api/parameters/index.js +196 -91
  31. package/dist/api/parameters/index.js.map +1 -1
  32. package/dist/api/users/index.browser.js +19 -19
  33. package/dist/api/users/index.browser.js.map +1 -1
  34. package/dist/api/users/index.d.ts +778 -764
  35. package/dist/api/users/index.d.ts.map +1 -1
  36. package/dist/api/users/index.js +831 -596
  37. package/dist/api/users/index.js.map +1 -1
  38. package/dist/api/verifications/index.browser.js +6 -6
  39. package/dist/api/verifications/index.browser.js.map +1 -1
  40. package/dist/api/verifications/index.d.ts +125 -125
  41. package/dist/api/verifications/index.d.ts.map +1 -1
  42. package/dist/api/verifications/index.js +6 -6
  43. package/dist/api/verifications/index.js.map +1 -1
  44. package/dist/batch/index.js.map +1 -1
  45. package/dist/bin/index.d.ts +1 -2
  46. package/dist/bin/index.js +0 -1
  47. package/dist/bin/index.js.map +1 -1
  48. package/dist/cache/core/index.js.map +1 -1
  49. package/dist/cli/index.d.ts +249 -218
  50. package/dist/cli/index.d.ts.map +1 -1
  51. package/dist/cli/index.js +951 -821
  52. package/dist/cli/index.js.map +1 -1
  53. package/dist/command/index.d.ts +40 -0
  54. package/dist/command/index.d.ts.map +1 -1
  55. package/dist/command/index.js +97 -17
  56. package/dist/command/index.js.map +1 -1
  57. package/dist/core/index.browser.js +14 -18
  58. package/dist/core/index.browser.js.map +1 -1
  59. package/dist/core/index.d.ts +29 -0
  60. package/dist/core/index.d.ts.map +1 -1
  61. package/dist/core/index.js +21 -24
  62. package/dist/core/index.js.map +1 -1
  63. package/dist/core/index.native.js +21 -24
  64. package/dist/core/index.native.js.map +1 -1
  65. package/dist/datetime/index.js.map +1 -1
  66. package/dist/fake/index.js +195 -168
  67. package/dist/fake/index.js.map +1 -1
  68. package/dist/file/index.d.ts +8 -0
  69. package/dist/file/index.d.ts.map +1 -1
  70. package/dist/file/index.js +3 -0
  71. package/dist/file/index.js.map +1 -1
  72. package/dist/lock/redis/index.js.map +1 -1
  73. package/dist/logger/index.js.map +1 -1
  74. package/dist/mcp/index.d.ts.map +1 -1
  75. package/dist/mcp/index.js.map +1 -1
  76. package/dist/orm/index.browser.js +26 -5
  77. package/dist/orm/index.browser.js.map +1 -1
  78. package/dist/orm/index.d.ts +146 -121
  79. package/dist/orm/index.d.ts.map +1 -1
  80. package/dist/orm/index.js +49 -24
  81. package/dist/orm/index.js.map +1 -1
  82. package/dist/redis/index.js.map +1 -1
  83. package/dist/retry/index.js.map +1 -1
  84. package/dist/router/index.js.map +1 -1
  85. package/dist/scheduler/index.d.ts +6 -6
  86. package/dist/scheduler/index.js.map +1 -1
  87. package/dist/security/index.d.ts +29 -29
  88. package/dist/security/index.d.ts.map +1 -1
  89. package/dist/security/index.js +1 -1
  90. package/dist/security/index.js.map +1 -1
  91. package/dist/server/auth/index.d.ts +171 -155
  92. package/dist/server/auth/index.d.ts.map +1 -1
  93. package/dist/server/auth/index.js +0 -1
  94. package/dist/server/auth/index.js.map +1 -1
  95. package/dist/server/cache/index.js.map +1 -1
  96. package/dist/server/compress/index.d.ts.map +1 -1
  97. package/dist/server/compress/index.js +2 -0
  98. package/dist/server/compress/index.js.map +1 -1
  99. package/dist/server/cookies/index.browser.js.map +1 -1
  100. package/dist/server/cookies/index.js.map +1 -1
  101. package/dist/server/core/index.browser.js.map +1 -1
  102. package/dist/server/core/index.d.ts.map +1 -1
  103. package/dist/server/core/index.js +1 -1
  104. package/dist/server/core/index.js.map +1 -1
  105. package/dist/server/health/index.d.ts +17 -17
  106. package/dist/server/helmet/index.js.map +1 -1
  107. package/dist/server/links/index.browser.js +22 -6
  108. package/dist/server/links/index.browser.js.map +1 -1
  109. package/dist/server/links/index.d.ts +46 -44
  110. package/dist/server/links/index.d.ts.map +1 -1
  111. package/dist/server/links/index.js +24 -41
  112. package/dist/server/links/index.js.map +1 -1
  113. package/dist/server/multipart/index.js.map +1 -1
  114. package/dist/server/rate-limit/index.js.map +1 -1
  115. package/dist/server/security/index.js.map +1 -1
  116. package/dist/server/swagger/index.d.ts +2 -1
  117. package/dist/server/swagger/index.d.ts.map +1 -1
  118. package/dist/server/swagger/index.js +8 -3
  119. package/dist/server/swagger/index.js.map +1 -1
  120. package/dist/thread/index.js.map +1 -1
  121. package/dist/topic/core/index.js.map +1 -1
  122. package/dist/vite/index.d.ts.map +1 -1
  123. package/dist/vite/index.js +12 -4
  124. package/dist/vite/index.js.map +1 -1
  125. package/dist/websocket/index.browser.js.map +1 -1
  126. package/dist/websocket/index.js.map +1 -1
  127. package/package.json +7 -7
  128. package/src/api/audits/controllers/{AuditController.ts → AdminAuditController.ts} +5 -6
  129. package/src/api/audits/entities/audits.ts +5 -5
  130. package/src/api/audits/index.browser.ts +1 -1
  131. package/src/api/audits/index.ts +3 -3
  132. package/src/api/audits/primitives/$audit.spec.ts +276 -0
  133. package/src/api/audits/services/AuditService.spec.ts +495 -0
  134. package/src/api/files/__tests__/$bucket.spec.ts +91 -0
  135. package/src/api/files/controllers/AdminFileStatsController.spec.ts +166 -0
  136. package/src/api/files/controllers/{StorageStatsController.ts → AdminFileStatsController.ts} +2 -2
  137. package/src/api/files/controllers/FileController.spec.ts +558 -0
  138. package/src/api/files/controllers/FileController.ts +4 -5
  139. package/src/api/files/entities/files.ts +5 -5
  140. package/src/api/files/index.browser.ts +1 -1
  141. package/src/api/files/index.ts +4 -4
  142. package/src/api/files/jobs/FileJobs.spec.ts +52 -0
  143. package/src/api/files/services/FileService.spec.ts +109 -0
  144. package/src/api/jobs/__tests__/JobController.spec.ts +343 -0
  145. package/src/api/jobs/controllers/{JobController.ts → AdminJobController.ts} +2 -2
  146. package/src/api/jobs/entities/jobExecutions.ts +5 -5
  147. package/src/api/jobs/index.ts +3 -3
  148. package/src/api/jobs/primitives/$job.spec.ts +476 -0
  149. package/src/api/notifications/controllers/{NotificationController.ts → AdminNotificationController.ts} +4 -5
  150. package/src/api/notifications/entities/notifications.ts +5 -5
  151. package/src/api/notifications/index.browser.ts +1 -1
  152. package/src/api/notifications/index.ts +4 -4
  153. package/src/api/parameters/controllers/{ConfigController.ts → AdminConfigController.ts} +46 -107
  154. package/src/api/parameters/entities/parameters.ts +7 -17
  155. package/src/api/parameters/index.ts +3 -3
  156. package/src/api/parameters/primitives/$config.spec.ts +356 -0
  157. package/src/api/parameters/schemas/activateConfigBodySchema.ts +12 -0
  158. package/src/api/parameters/schemas/checkScheduledResponseSchema.ts +8 -0
  159. package/src/api/parameters/schemas/configCurrentResponseSchema.ts +13 -0
  160. package/src/api/parameters/schemas/configHistoryResponseSchema.ts +9 -0
  161. package/src/api/parameters/schemas/configNameParamSchema.ts +10 -0
  162. package/src/api/parameters/schemas/configNamesResponseSchema.ts +8 -0
  163. package/src/api/parameters/schemas/configTreeNodeSchema.ts +13 -0
  164. package/src/api/parameters/schemas/configVersionParamSchema.ts +9 -0
  165. package/src/api/parameters/schemas/configVersionResponseSchema.ts +9 -0
  166. package/src/api/parameters/schemas/configsByStatusResponseSchema.ts +9 -0
  167. package/src/api/parameters/schemas/createConfigVersionBodySchema.ts +24 -0
  168. package/src/api/parameters/schemas/index.ts +15 -0
  169. package/src/api/parameters/schemas/parameterResponseSchema.ts +26 -0
  170. package/src/api/parameters/schemas/parameterStatusSchema.ts +13 -0
  171. package/src/api/parameters/schemas/rollbackConfigBodySchema.ts +15 -0
  172. package/src/api/parameters/schemas/statusParamSchema.ts +9 -0
  173. package/src/api/users/__tests__/EmailVerification.spec.ts +369 -0
  174. package/src/api/users/__tests__/PasswordReset.spec.ts +550 -0
  175. package/src/api/users/controllers/AdminIdentityController.spec.ts +365 -0
  176. package/src/api/users/controllers/{IdentityController.ts → AdminIdentityController.ts} +3 -4
  177. package/src/api/users/controllers/AdminSessionController.spec.ts +274 -0
  178. package/src/api/users/controllers/{SessionController.ts → AdminSessionController.ts} +3 -4
  179. package/src/api/users/controllers/AdminUserController.spec.ts +372 -0
  180. package/src/api/users/controllers/AdminUserController.ts +116 -0
  181. package/src/api/users/controllers/UserController.ts +4 -107
  182. package/src/api/users/controllers/UserRealmController.ts +3 -0
  183. package/src/api/users/entities/identities.ts +6 -6
  184. package/src/api/users/entities/sessions.ts +6 -6
  185. package/src/api/users/entities/users.ts +9 -9
  186. package/src/api/users/index.ts +13 -6
  187. package/src/api/users/primitives/$userRealm.ts +13 -8
  188. package/src/api/users/services/CredentialService.spec.ts +509 -0
  189. package/src/api/users/services/CredentialService.ts +46 -0
  190. package/src/api/users/services/IdentityService.ts +15 -0
  191. package/src/api/users/services/RegistrationService.spec.ts +630 -0
  192. package/src/api/users/services/RegistrationService.ts +18 -0
  193. package/src/api/users/services/SessionService.spec.ts +301 -0
  194. package/src/api/users/services/SessionService.ts +110 -1
  195. package/src/api/users/services/UserService.ts +67 -2
  196. package/src/api/verifications/__tests__/CodeVerification.spec.ts +318 -0
  197. package/src/api/verifications/__tests__/LinkVerification.spec.ts +279 -0
  198. package/src/api/verifications/entities/verifications.ts +6 -6
  199. package/src/api/verifications/jobs/VerificationJobs.spec.ts +50 -0
  200. package/src/batch/__tests__/startup-buffering.spec.ts +458 -0
  201. package/src/batch/primitives/$batch.spec.ts +766 -0
  202. package/src/batch/providers/BatchProvider.spec.ts +786 -0
  203. package/src/bin/index.ts +0 -1
  204. package/src/bucket/__tests__/shared.ts +194 -0
  205. package/src/bucket/primitives/$bucket.spec.ts +104 -0
  206. package/src/bucket/providers/FileStorageProvider.spec.ts +13 -0
  207. package/src/bucket/providers/LocalFileStorageProvider.spec.ts +77 -0
  208. package/src/bucket/providers/MemoryFileStorageProvider.spec.ts +82 -0
  209. package/src/cache/core/__tests__/shared.ts +377 -0
  210. package/src/cache/core/primitives/$cache.spec.ts +111 -0
  211. package/src/cache/redis/__tests__/cache-redis.spec.ts +70 -0
  212. package/src/cli/apps/AlephaCli.ts +54 -16
  213. package/src/cli/apps/AlephaPackageBuilderCli.ts +2 -1
  214. package/src/cli/assets/appRouterTs.ts +1 -1
  215. package/src/cli/commands/{ViteCommands.ts → build.ts} +2 -105
  216. package/src/cli/commands/clean.ts +14 -0
  217. package/src/cli/commands/{DrizzleCommands.ts → db.ts} +10 -117
  218. package/src/cli/commands/{DeployCommands.ts → deploy.ts} +1 -1
  219. package/src/cli/commands/dev.ts +69 -0
  220. package/src/cli/commands/format.ts +17 -0
  221. package/src/cli/commands/gen/changelog.spec.ts +315 -0
  222. package/src/cli/commands/{ChangelogCommands.ts → gen/changelog.ts} +16 -31
  223. package/src/cli/commands/gen/openapi.ts +71 -0
  224. package/src/cli/commands/gen.ts +18 -0
  225. package/src/cli/commands/{CoreCommands.ts → init.ts} +4 -40
  226. package/src/cli/commands/lint.ts +17 -0
  227. package/src/cli/commands/root.ts +41 -0
  228. package/src/cli/commands/run.ts +24 -0
  229. package/src/cli/commands/test.ts +42 -0
  230. package/src/cli/commands/typecheck.ts +24 -0
  231. package/src/cli/commands/{VerifyCommands.ts → verify.ts} +1 -13
  232. package/src/cli/defineConfig.ts +10 -1
  233. package/src/cli/index.ts +17 -7
  234. package/src/cli/services/AlephaCliUtils.ts +71 -32
  235. package/src/cli/services/GitMessageParser.ts +1 -1
  236. package/src/command/helpers/Asker.spec.ts +127 -0
  237. package/src/command/helpers/Runner.spec.ts +126 -0
  238. package/src/command/primitives/$command.spec.ts +1588 -0
  239. package/src/command/providers/CliProvider.ts +74 -24
  240. package/src/core/Alepha.ts +52 -4
  241. package/src/core/__tests__/Alepha-emit.spec.ts +22 -0
  242. package/src/core/__tests__/Alepha-graph.spec.ts +93 -0
  243. package/src/core/__tests__/Alepha-has.spec.ts +41 -0
  244. package/src/core/__tests__/Alepha-inject.spec.ts +93 -0
  245. package/src/core/__tests__/Alepha-register.spec.ts +81 -0
  246. package/src/core/__tests__/Alepha-start.spec.ts +176 -0
  247. package/src/core/__tests__/Alepha-with.spec.ts +14 -0
  248. package/src/core/__tests__/TypeBox-usecases.spec.ts +35 -0
  249. package/src/core/__tests__/TypeBoxLocale.spec.ts +15 -0
  250. package/src/core/__tests__/descriptor.spec.ts +34 -0
  251. package/src/core/__tests__/fixtures/A.ts +5 -0
  252. package/src/core/__tests__/pagination.spec.ts +77 -0
  253. package/src/core/helpers/jsonSchemaToTypeBox.ts +2 -2
  254. package/src/core/primitives/$atom.spec.ts +43 -0
  255. package/src/core/primitives/$hook.spec.ts +130 -0
  256. package/src/core/primitives/$inject.spec.ts +175 -0
  257. package/src/core/primitives/$module.spec.ts +115 -0
  258. package/src/core/providers/CodecManager.spec.ts +740 -0
  259. package/src/core/providers/EventManager.spec.ts +762 -0
  260. package/src/core/providers/EventManager.ts +4 -0
  261. package/src/core/providers/StateManager.spec.ts +365 -0
  262. package/src/core/providers/TypeProvider.spec.ts +1607 -0
  263. package/src/core/providers/TypeProvider.ts +20 -26
  264. package/src/datetime/primitives/$interval.spec.ts +103 -0
  265. package/src/datetime/providers/DateTimeProvider.spec.ts +86 -0
  266. package/src/email/primitives/$email.spec.ts +175 -0
  267. package/src/email/providers/LocalEmailProvider.spec.ts +341 -0
  268. package/src/fake/__tests__/keyName.example.ts +40 -0
  269. package/src/fake/__tests__/keyName.spec.ts +152 -0
  270. package/src/fake/__tests__/module.example.ts +32 -0
  271. package/src/fake/providers/FakeProvider.spec.ts +438 -0
  272. package/src/file/providers/FileSystemProvider.ts +8 -0
  273. package/src/file/providers/NodeFileSystemProvider.spec.ts +418 -0
  274. package/src/file/providers/NodeFileSystemProvider.ts +5 -0
  275. package/src/file/services/FileDetector.spec.ts +591 -0
  276. package/src/lock/core/__tests__/shared.ts +190 -0
  277. package/src/lock/core/providers/MemoryLockProvider.spec.ts +25 -0
  278. package/src/lock/redis/providers/RedisLockProvider.spec.ts +25 -0
  279. package/src/logger/__tests__/SimpleFormatterProvider.spec.ts +109 -0
  280. package/src/logger/primitives/$logger.spec.ts +108 -0
  281. package/src/logger/services/Logger.spec.ts +295 -0
  282. package/src/mcp/__tests__/errors.spec.ts +175 -0
  283. package/src/mcp/__tests__/integration.spec.ts +450 -0
  284. package/src/mcp/helpers/jsonrpc.spec.ts +380 -0
  285. package/src/mcp/primitives/$prompt.spec.ts +468 -0
  286. package/src/mcp/primitives/$resource.spec.ts +390 -0
  287. package/src/mcp/primitives/$tool.spec.ts +406 -0
  288. package/src/mcp/providers/McpServerProvider.spec.ts +797 -0
  289. package/src/orm/__tests__/$repository-crud.spec.ts +276 -0
  290. package/src/orm/__tests__/$repository-hooks.spec.ts +325 -0
  291. package/src/orm/__tests__/$repository-orderBy.spec.ts +128 -0
  292. package/src/orm/__tests__/$repository-pagination-sort.spec.ts +149 -0
  293. package/src/orm/__tests__/$repository-save.spec.ts +37 -0
  294. package/src/orm/__tests__/ModelBuilder-integration.spec.ts +490 -0
  295. package/src/orm/__tests__/ModelBuilder-types.spec.ts +186 -0
  296. package/src/orm/__tests__/PostgresProvider.spec.ts +46 -0
  297. package/src/orm/__tests__/delete-returning.spec.ts +256 -0
  298. package/src/orm/__tests__/deletedAt.spec.ts +80 -0
  299. package/src/orm/__tests__/enums.spec.ts +315 -0
  300. package/src/orm/__tests__/execute.spec.ts +72 -0
  301. package/src/orm/__tests__/fixtures/bigEntitySchema.ts +65 -0
  302. package/src/orm/__tests__/fixtures/userEntitySchema.ts +27 -0
  303. package/src/orm/__tests__/joins.spec.ts +1114 -0
  304. package/src/orm/__tests__/page.spec.ts +287 -0
  305. package/src/orm/__tests__/primaryKey.spec.ts +87 -0
  306. package/src/orm/__tests__/query-date-encoding.spec.ts +402 -0
  307. package/src/orm/__tests__/ref-auto-onDelete.spec.ts +156 -0
  308. package/src/orm/__tests__/references.spec.ts +102 -0
  309. package/src/orm/__tests__/security.spec.ts +710 -0
  310. package/src/orm/__tests__/sqlite.spec.ts +111 -0
  311. package/src/orm/__tests__/string-operators.spec.ts +429 -0
  312. package/src/orm/__tests__/timestamps.spec.ts +388 -0
  313. package/src/orm/__tests__/validation.spec.ts +183 -0
  314. package/src/orm/__tests__/version.spec.ts +64 -0
  315. package/src/orm/helpers/parseQueryString.spec.ts +196 -0
  316. package/src/orm/index.browser.ts +1 -1
  317. package/src/orm/index.ts +10 -6
  318. package/src/orm/primitives/$repository.spec.ts +137 -0
  319. package/src/orm/primitives/$sequence.spec.ts +29 -0
  320. package/src/orm/primitives/$transaction.spec.ts +82 -0
  321. package/src/orm/providers/{PostgresTypeProvider.ts → DatabaseTypeProvider.ts} +25 -3
  322. package/src/orm/providers/drivers/BunPostgresProvider.ts +3 -3
  323. package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -1
  324. package/src/orm/providers/drivers/CloudflareD1Provider.ts +1 -1
  325. package/src/orm/providers/drivers/DatabaseProvider.ts +1 -1
  326. package/src/orm/providers/drivers/NodePostgresProvider.ts +3 -3
  327. package/src/orm/providers/drivers/NodeSqliteProvider.ts +1 -1
  328. package/src/orm/providers/drivers/PglitePostgresProvider.ts +2 -2
  329. package/src/orm/services/ModelBuilder.spec.ts +575 -0
  330. package/src/orm/services/Repository.spec.ts +137 -0
  331. package/src/queue/core/__tests__/shared.ts +143 -0
  332. package/src/queue/core/providers/MemoryQueueProvider.spec.ts +23 -0
  333. package/src/queue/core/providers/WorkerProvider.spec.ts +378 -0
  334. package/src/queue/redis/providers/RedisQueueProvider.spec.ts +23 -0
  335. package/src/redis/__tests__/redis.spec.ts +58 -0
  336. package/src/retry/primitives/$retry.spec.ts +234 -0
  337. package/src/retry/providers/RetryProvider.spec.ts +438 -0
  338. package/src/router/__tests__/match.spec.ts +252 -0
  339. package/src/router/providers/RouterProvider.spec.ts +197 -0
  340. package/src/scheduler/__tests__/$scheduler-cron.spec.ts +25 -0
  341. package/src/scheduler/__tests__/$scheduler-interval.spec.ts +25 -0
  342. package/src/scheduler/__tests__/shared.ts +77 -0
  343. package/src/security/__tests__/bug-1-wildcard-after-start.spec.ts +229 -0
  344. package/src/security/__tests__/bug-2-password-validation.spec.ts +245 -0
  345. package/src/security/__tests__/bug-3-regex-vulnerability.spec.ts +407 -0
  346. package/src/security/__tests__/bug-4-oauth2-validation.spec.ts +439 -0
  347. package/src/security/__tests__/multi-layer-permissions.spec.ts +522 -0
  348. package/src/security/primitives/$permission.spec.ts +30 -0
  349. package/src/security/primitives/$permission.ts +2 -2
  350. package/src/security/primitives/$realm.spec.ts +101 -0
  351. package/src/security/primitives/$role.spec.ts +52 -0
  352. package/src/security/primitives/$serviceAccount.spec.ts +61 -0
  353. package/src/security/providers/SecurityProvider.spec.ts +350 -0
  354. package/src/server/auth/providers/ServerAuthProvider.ts +0 -2
  355. package/src/server/cache/providers/ServerCacheProvider.spec.ts +942 -0
  356. package/src/server/compress/providers/ServerCompressProvider.spec.ts +31 -0
  357. package/src/server/compress/providers/ServerCompressProvider.ts +2 -0
  358. package/src/server/cookies/providers/ServerCookiesProvider.spec.ts +253 -0
  359. package/src/server/core/__tests__/ServerRouterProvider-getRoutes.spec.ts +334 -0
  360. package/src/server/core/__tests__/ServerRouterProvider-requestId.spec.ts +129 -0
  361. package/src/server/core/primitives/$action.spec.ts +191 -0
  362. package/src/server/core/primitives/$route.spec.ts +65 -0
  363. package/src/server/core/providers/ServerBodyParserProvider.spec.ts +93 -0
  364. package/src/server/core/providers/ServerLoggerProvider.spec.ts +100 -0
  365. package/src/server/core/providers/ServerProvider.ts +3 -1
  366. package/src/server/core/services/HttpClient.spec.ts +123 -0
  367. package/src/server/core/services/UserAgentParser.spec.ts +111 -0
  368. package/src/server/cors/providers/ServerCorsProvider.spec.ts +481 -0
  369. package/src/server/health/providers/ServerHealthProvider.spec.ts +22 -0
  370. package/src/server/helmet/providers/ServerHelmetProvider.spec.ts +105 -0
  371. package/src/server/links/__tests__/$action.spec.ts +238 -0
  372. package/src/server/links/__tests__/fixtures/CrudApp.ts +122 -0
  373. package/src/server/links/__tests__/requestId.spec.ts +120 -0
  374. package/src/server/links/primitives/$remote.spec.ts +228 -0
  375. package/src/server/links/providers/LinkProvider.spec.ts +54 -0
  376. package/src/server/links/providers/LinkProvider.ts +49 -3
  377. package/src/server/links/providers/ServerLinksProvider.ts +1 -53
  378. package/src/server/links/schemas/apiLinksResponseSchema.ts +7 -0
  379. package/src/server/metrics/providers/ServerMetricsProvider.spec.ts +25 -0
  380. package/src/server/multipart/providers/ServerMultipartProvider.spec.ts +528 -0
  381. package/src/server/proxy/primitives/$proxy.spec.ts +87 -0
  382. package/src/server/rate-limit/__tests__/ActionRateLimit.spec.ts +211 -0
  383. package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +344 -0
  384. package/src/server/security/__tests__/BasicAuth.spec.ts +684 -0
  385. package/src/server/security/__tests__/ServerSecurityProvider-realm.spec.ts +388 -0
  386. package/src/server/security/providers/ServerSecurityProvider.spec.ts +123 -0
  387. package/src/server/static/primitives/$serve.spec.ts +193 -0
  388. package/src/server/swagger/__tests__/ui.spec.ts +52 -0
  389. package/src/server/swagger/primitives/$swagger.spec.ts +193 -0
  390. package/src/server/swagger/providers/ServerSwaggerProvider.ts +18 -8
  391. package/src/sms/primitives/$sms.spec.ts +165 -0
  392. package/src/sms/providers/LocalSmsProvider.spec.ts +224 -0
  393. package/src/sms/providers/MemorySmsProvider.spec.ts +193 -0
  394. package/src/thread/primitives/$thread.spec.ts +186 -0
  395. package/src/topic/core/__tests__/shared.ts +144 -0
  396. package/src/topic/core/providers/MemoryTopicProvider.spec.ts +23 -0
  397. package/src/topic/redis/providers/RedisTopicProvider.spec.ts +23 -0
  398. package/src/vite/plugins/viteAlephaDev.ts +16 -4
  399. package/src/vite/tasks/runAlepha.ts +7 -1
  400. package/src/websocket/__tests__/$websocket-new.spec.ts +195 -0
  401. package/src/websocket/primitives/$channel.spec.ts +30 -0
  402. package/src/cli/commands/BiomeCommands.ts +0 -29
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["envSchema","args: string[]","setOptions: SetOptions"],"sources":["../../src/redis/providers/RedisProvider.ts","../../src/redis/providers/BunRedisProvider.ts","../../src/redis/providers/RedisSubscriberProvider.ts","../../src/redis/providers/BunRedisSubscriberProvider.ts","../../src/redis/providers/NodeRedisProvider.ts","../../src/redis/providers/NodeRedisSubscriberProvider.ts","../../src/redis/index.ts"],"sourcesContent":["/**\n * Abstract Redis provider interface.\n *\n * This abstract class defines the common interface for Redis operations.\n * Implementations include:\n * - {@link NodeRedisProvider} - Uses `@redis/client` for Node.js runtime\n * - {@link BunRedisProvider} - Uses Bun's native `RedisClient` for Bun runtime\n *\n * @example\n * ```ts\n * // Inject the abstract provider - runtime selects the implementation\n * const redis = alepha.inject(RedisProvider);\n *\n * // Use common operations\n * await redis.set(\"key\", \"value\");\n * const value = await redis.get(\"key\");\n * ```\n */\nexport abstract class RedisProvider {\n /**\n * Whether the Redis client is ready to accept commands.\n */\n public abstract readonly isReady: boolean;\n\n /**\n * Connect to the Redis server.\n */\n public abstract connect(): Promise<void>;\n\n /**\n * Close the connection to the Redis server.\n */\n public abstract close(): Promise<void>;\n\n /**\n * Get the value of a key.\n *\n * @param key The key to get.\n * @returns The value as a Buffer, or undefined if the key does not exist.\n */\n public abstract get(key: string): Promise<Buffer | undefined>;\n\n /**\n * Set the value of a key.\n *\n * @param key The key to set.\n * @param value The value to set (Buffer or string).\n * @param options Optional set options (EX, PX, NX, XX, etc.).\n * @returns The value as a Buffer.\n */\n public abstract set(\n key: string,\n value: Buffer | string,\n options?: RedisSetOptions,\n ): Promise<Buffer>;\n\n /**\n * Check if a key exists.\n *\n * @param key The key to check.\n * @returns True if the key exists.\n */\n public abstract has(key: string): Promise<boolean>;\n\n /**\n * Get all keys matching a pattern.\n *\n * @param pattern The glob-style pattern to match.\n * @returns Array of matching key names.\n */\n public abstract keys(pattern: string): Promise<string[]>;\n\n /**\n * Delete one or more keys.\n *\n * @param keys The keys to delete.\n */\n public abstract del(keys: string[]): Promise<void>;\n\n // ---------------------------------------------------------\n // Queue operations (for alepha/queue-redis)\n // ---------------------------------------------------------\n\n /**\n * Push a value to the left (head) of a list.\n *\n * @param key The list key.\n * @param value The value to push.\n */\n public abstract lpush(key: string, value: string): Promise<void>;\n\n /**\n * Pop a value from the right (tail) of a list.\n *\n * @param key The list key.\n * @returns The value, or undefined if the list is empty.\n */\n public abstract rpop(key: string): Promise<string | undefined>;\n\n // ---------------------------------------------------------\n // Pub/Sub operations (for alepha/topic-redis)\n // ---------------------------------------------------------\n\n /**\n * Publish a message to a channel.\n *\n * @param channel The channel name.\n * @param message The message to publish.\n */\n public abstract publish(channel: string, message: string): Promise<void>;\n}\n\n/**\n * Common Redis SET command options.\n * Compatible with @redis/client SetOptions format.\n */\nexport interface RedisSetOptions {\n /**\n * Set the specified expire time, in seconds.\n */\n EX?: number;\n /**\n * Set the specified expire time, in milliseconds.\n */\n PX?: number;\n /**\n * Set the specified Unix time at which the key will expire, in seconds.\n */\n EXAT?: number;\n /**\n * Set the specified Unix time at which the key will expire, in milliseconds.\n */\n PXAT?: number;\n /**\n * Only set the key if it does not already exist.\n */\n NX?: boolean;\n /**\n * Only set the key if it already exists.\n */\n XX?: boolean;\n /**\n * Retain the time to live associated with the key.\n */\n KEEPTTL?: boolean;\n /**\n * Return the old string stored at key, or nil if key did not exist.\n */\n GET?: boolean;\n /**\n * Alternative expiration format (compatible with @redis/client).\n */\n expiration?: {\n type: \"EX\" | \"PX\" | \"EXAT\" | \"PXAT\" | \"KEEPTTL\";\n value: number;\n };\n /**\n * Alternative condition format (compatible with @redis/client).\n */\n condition?: \"NX\" | \"XX\";\n}\n","import {\n $env,\n $hook,\n $inject,\n Alepha,\n AlephaError,\n type Static,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { RedisClient as BunRedisClient } from \"bun\";\nimport { RedisProvider, type RedisSetOptions } from \"./RedisProvider.ts\";\n\nconst envSchema = t.object({\n REDIS_URL: t.optional(t.text()),\n REDIS_PORT: t.integer({\n default: \"6379\",\n }),\n REDIS_HOST: t.text({\n default: \"localhost\",\n }),\n REDIS_PASSWORD: t.optional(t.text()),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\n/**\n * Bun Redis client provider using Bun's native Redis client.\n *\n * This provider uses Bun's built-in `RedisClient` class for Redis connections,\n * which provides excellent performance (7.9x faster than ioredis) on the Bun runtime.\n *\n * @example\n * ```ts\n * // Set REDIS_URL environment variable\n * // REDIS_URL=redis://localhost:6379\n *\n * // Or configure via REDIS_HOST, REDIS_PORT, REDIS_PASSWORD\n *\n * // Or configure programmatically\n * alepha.with({\n * provide: RedisProvider,\n * use: BunRedisProvider,\n * });\n * ```\n */\nexport class BunRedisProvider extends RedisProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly env = $env(envSchema);\n protected client?: BunRedisClient;\n\n public get publisher(): BunRedisClient {\n if (!this.client?.connected) {\n throw new AlephaError(\"Redis client is not ready\");\n }\n\n return this.client;\n }\n\n public override get isReady(): boolean {\n return this.client?.connected ?? false;\n }\n\n protected readonly start = $hook({\n on: \"start\",\n handler: () => this.connect(),\n });\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: () => this.close(),\n });\n\n /**\n * Connect to the Redis server.\n */\n public override async connect(): Promise<void> {\n // Check if we're running in Bun\n if (typeof Bun === \"undefined\") {\n throw new AlephaError(\n \"BunRedisProvider requires the Bun runtime. Use NodeRedisProvider for Node.js.\",\n );\n }\n\n this.log.debug(\"Connecting...\");\n\n const { RedisClient } = await import(\"bun\");\n\n this.client = new RedisClient(this.getUrl(), {\n autoReconnect: true,\n enableAutoPipelining: true,\n });\n\n this.client.onconnect = () => {\n this.log.trace(\"Redis connected\");\n };\n\n this.client.onclose = (error) => {\n if (this.alepha.isStarted() && error) {\n this.log.error(\"Redis connection closed\", error);\n }\n };\n\n await this.client.connect();\n\n this.log.info(\"Connection OK\");\n }\n\n /**\n * Close the connection to the Redis server.\n */\n public override async close(): Promise<void> {\n if (this.client) {\n this.log.debug(\"Closing connection...\");\n this.client.close();\n this.client = undefined;\n this.log.info(\"Connection closed\");\n }\n }\n\n /**\n * Create a duplicate connection for pub/sub or other isolated operations.\n */\n public async duplicate(): Promise<BunRedisClient> {\n if (typeof Bun === \"undefined\") {\n throw new AlephaError(\"BunRedisProvider requires the Bun runtime.\");\n }\n\n const { RedisClient } = await import(\"bun\");\n\n const client = new RedisClient(this.getUrl(), {\n autoReconnect: true,\n enableAutoPipelining: true,\n });\n\n client.onclose = (error) => {\n if (this.alepha.isStarted() && error) {\n this.log.error(\"Redis duplicate connection closed\", error);\n }\n };\n\n await client.connect();\n\n return client;\n }\n\n public override async get(key: string): Promise<Buffer | undefined> {\n this.log.trace(`Getting key ${key}`);\n const resp = await this.publisher.getBuffer(key);\n\n if (resp === null) {\n return undefined;\n }\n\n return Buffer.from(resp);\n }\n\n public override async set(\n key: string,\n value: Buffer | string,\n options?: RedisSetOptions,\n ): Promise<Buffer> {\n const buf = Buffer.isBuffer(value) ? value : Buffer.from(value, \"utf-8\");\n\n // Build SET command arguments\n const args: string[] = [key, buf.toString(\"binary\")];\n\n // Handle expiration object format (from alepha/cache-redis, alepha/lock-redis)\n if (options?.expiration) {\n if (options.expiration.type === \"KEEPTTL\") {\n args.push(\"KEEPTTL\");\n } else {\n args.push(options.expiration.type, String(options.expiration.value));\n }\n }\n\n // Handle direct expiration properties\n if (options?.EX !== undefined) {\n args.push(\"EX\", String(options.EX));\n }\n if (options?.PX !== undefined) {\n args.push(\"PX\", String(options.PX));\n }\n if (options?.EXAT !== undefined) {\n args.push(\"EXAT\", String(options.EXAT));\n }\n if (options?.PXAT !== undefined) {\n args.push(\"PXAT\", String(options.PXAT));\n }\n if (options?.KEEPTTL) {\n args.push(\"KEEPTTL\");\n }\n\n // Handle condition object format\n if (options?.condition === \"NX\") {\n args.push(\"NX\");\n } else if (options?.condition === \"XX\") {\n args.push(\"XX\");\n }\n\n // Handle direct condition properties\n if (options?.NX) {\n args.push(\"NX\");\n }\n if (options?.XX) {\n args.push(\"XX\");\n }\n if (options?.GET) {\n args.push(\"GET\");\n }\n\n if (args.length === 2) {\n // Simple set without options\n await this.publisher.set(key, buf);\n } else {\n // Set with options via raw command\n await this.publisher.send(\"SET\", args);\n }\n\n return buf;\n }\n\n public override async has(key: string): Promise<boolean> {\n return this.publisher.exists(key);\n }\n\n public override async keys(pattern: string): Promise<string[]> {\n const keys = await this.publisher.send(\"KEYS\", [pattern]);\n if (!Array.isArray(keys)) {\n return [];\n }\n return keys.map((key) =>\n key instanceof Uint8Array ? Buffer.from(key).toString() : String(key),\n );\n }\n\n public override async del(keys: string[]): Promise<void> {\n if (keys.length === 0) {\n return;\n }\n\n await this.publisher.send(\"DEL\", keys);\n }\n\n // ---------------------------------------------------------\n // Queue operations\n // ---------------------------------------------------------\n\n public override async lpush(key: string, value: string): Promise<void> {\n await this.publisher.send(\"LPUSH\", [key, value]);\n }\n\n public override async rpop(key: string): Promise<string | undefined> {\n const value = await this.publisher.send(\"RPOP\", [key]);\n if (value == null) {\n return undefined;\n }\n if (value instanceof Uint8Array) {\n return Buffer.from(value).toString();\n }\n return String(value);\n }\n\n // ---------------------------------------------------------\n // Pub/Sub operations\n // ---------------------------------------------------------\n\n public override async publish(\n channel: string,\n message: string,\n ): Promise<void> {\n await this.publisher.publish(channel, message);\n }\n\n /**\n * Get the Redis connection URL.\n */\n protected getUrl(): string {\n // Prefer REDIS_URL if set\n if (this.env.REDIS_URL) {\n return this.env.REDIS_URL;\n }\n\n // Build URL from components\n const url = new URL(\"redis://127.0.0.1:6379\");\n\n if (this.env.REDIS_PASSWORD) {\n url.password = this.env.REDIS_PASSWORD;\n }\n\n if (this.env.REDIS_HOST) {\n url.hostname = this.env.REDIS_HOST;\n }\n\n if (this.env.REDIS_PORT) {\n url.port = String(this.env.REDIS_PORT);\n }\n\n return url.toString();\n }\n}\n","/**\n * Abstract Redis subscriber provider interface.\n *\n * This abstract class defines the common interface for Redis pub/sub subscriptions.\n * Implementations include:\n * - {@link NodeRedisSubscriberProvider} - Uses `@redis/client` for Node.js runtime\n * - {@link BunRedisSubscriberProvider} - Uses Bun's native `RedisClient` for Bun runtime\n *\n * Redis requires separate connections for pub/sub operations, so this provider\n * creates a dedicated connection for subscriptions.\n *\n * @example\n * ```ts\n * // Inject the abstract provider - runtime selects the implementation\n * const subscriber = alepha.inject(RedisSubscriberProvider);\n *\n * // Subscribe to a channel\n * await subscriber.subscribe(\"my-channel\", (message, channel) => {\n * console.log(`Received: ${message} on ${channel}`);\n * });\n * ```\n */\nexport abstract class RedisSubscriberProvider {\n /**\n * Whether the Redis subscriber client is ready to accept commands.\n */\n public abstract readonly isReady: boolean;\n\n /**\n * Connect to the Redis server for subscriptions.\n */\n public abstract connect(): Promise<void>;\n\n /**\n * Close the subscriber connection.\n */\n public abstract close(): Promise<void>;\n\n /**\n * Subscribe to a channel.\n *\n * @param channel The channel name.\n * @param callback The callback to invoke when a message is received.\n */\n public abstract subscribe(\n channel: string,\n callback: SubscribeCallback,\n ): Promise<void>;\n\n /**\n * Unsubscribe from a channel.\n *\n * @param channel The channel name.\n * @param callback Optional specific callback to remove.\n */\n public abstract unsubscribe(\n channel: string,\n callback?: SubscribeCallback,\n ): Promise<void>;\n}\n\n/**\n * Callback for subscription messages.\n */\nexport type SubscribeCallback = (message: string, channel: string) => void;\n","import { $hook, $inject, Alepha, AlephaError } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { RedisClient as BunRedisClient } from \"bun\";\nimport { BunRedisProvider } from \"./BunRedisProvider.ts\";\nimport {\n RedisSubscriberProvider,\n type SubscribeCallback,\n} from \"./RedisSubscriberProvider.ts\";\n\n/**\n * Bun Redis subscriber provider for pub/sub operations.\n *\n * This provider creates a dedicated Redis connection for subscriptions,\n * as Redis requires separate connections for pub/sub operations.\n *\n * @example\n * ```ts\n * const subscriber = alepha.inject(RedisSubscriberProvider);\n * await subscriber.subscribe(\"channel\", (message, channel) => {\n * console.log(`Received: ${message} on ${channel}`);\n * });\n * ```\n */\nexport class BunRedisSubscriberProvider extends RedisSubscriberProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly redisProvider = $inject(BunRedisProvider);\n protected client?: BunRedisClient;\n\n public get subscriber(): BunRedisClient {\n if (!this.client?.connected) {\n throw new AlephaError(\"Redis subscriber client is not ready\");\n }\n\n return this.client;\n }\n\n public override get isReady(): boolean {\n return this.client?.connected ?? false;\n }\n\n protected readonly start = $hook({\n on: \"start\",\n handler: () => this.connect(),\n });\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: () => this.close(),\n });\n\n /**\n * Connect to the Redis server for subscriptions.\n */\n public override async connect(): Promise<void> {\n this.log.debug(\"Connecting subscriber...\");\n this.client = await this.redisProvider.duplicate();\n this.log.info(\"Subscriber connection OK\");\n }\n\n /**\n * Close the subscriber connection.\n */\n public override async close(): Promise<void> {\n if (this.client) {\n this.log.debug(\"Closing subscriber connection...\");\n this.client.close();\n this.client = undefined;\n this.log.info(\"Subscriber connection closed\");\n }\n }\n\n public override async subscribe(\n channel: string,\n callback: SubscribeCallback,\n ): Promise<void> {\n await this.subscriber.subscribe(channel, (message, ch) => {\n // Bun's callback provides Buffer or string, normalize to string\n const msg =\n typeof message === \"object\" && message !== null\n ? Buffer.from(message as Uint8Array).toString()\n : String(message);\n callback(msg, ch);\n });\n }\n\n public override async unsubscribe(\n channel: string,\n _callback?: SubscribeCallback,\n ): Promise<void> {\n // Bun's unsubscribe doesn't support callback filtering\n await this.subscriber.unsubscribe(channel);\n }\n}\n","import {\n createClient,\n RESP_TYPES,\n type RedisClientType,\n type SetOptions,\n} from \"@redis/client\";\nimport { $env, $hook, $inject, Alepha, type Static, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { RedisProvider, type RedisSetOptions } from \"./RedisProvider.ts\";\n\nconst envSchema = t.object({\n REDIS_URL: t.optional(t.text()),\n REDIS_PORT: t.integer({\n default: \"6379\",\n }),\n REDIS_HOST: t.text({\n default: \"localhost\",\n }),\n REDIS_PASSWORD: t.optional(t.text()),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\nexport type NodeRedisClient = RedisClientType<\n {},\n {},\n {},\n 3,\n { 36: BufferConstructor }\n>;\nexport type NodeRedisClientOptions = Parameters<typeof createClient>[0];\n\n/**\n * Node.js Redis client provider using `@redis/client`.\n *\n * This provider uses the official Redis client for Node.js runtime.\n *\n * @example\n * ```ts\n * // Set REDIS_URL environment variable\n * // REDIS_URL=redis://localhost:6379\n *\n * // Or configure via REDIS_HOST, REDIS_PORT, REDIS_PASSWORD\n *\n * // Or configure programmatically\n * alepha.with({\n * provide: RedisProvider,\n * use: NodeRedisProvider,\n * });\n * ```\n */\nexport class NodeRedisProvider extends RedisProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly env = $env(envSchema);\n protected readonly client = this.createClient();\n\n public get publisher(): NodeRedisClient {\n if (!this.client.isReady) {\n throw new Error(\"Redis client is not ready\");\n }\n\n return this.client;\n }\n\n public override get isReady(): boolean {\n return this.client.isReady;\n }\n\n protected readonly start = $hook({\n on: \"start\",\n handler: () => this.connect(),\n });\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: () => this.close(),\n });\n\n /**\n * Connect to the Redis server.\n */\n public override async connect(): Promise<void> {\n this.log.debug(\"Connecting...\");\n await this.client.connect();\n this.log.info(\"Connection OK\");\n }\n\n /**\n * Close the connection to the Redis server.\n */\n public override async close(): Promise<void> {\n this.log.debug(\"Closing connection...\");\n await this.client.close();\n this.log.info(\"Connection closed\");\n }\n\n public duplicate(options?: Partial<NodeRedisClientOptions>): NodeRedisClient {\n return this.client\n .duplicate({\n ...options,\n RESP: 3,\n })\n .withTypeMapping({\n [RESP_TYPES.BLOB_STRING]: Buffer,\n });\n }\n\n public override async get(key: string): Promise<Buffer | undefined> {\n this.log.trace(`Getting key ${key}`);\n const resp = await this.publisher.get(key);\n\n if (resp === null) {\n return undefined;\n }\n\n return Buffer.from(resp);\n }\n\n public override async set(\n key: string,\n value: Buffer | string,\n options?: RedisSetOptions,\n ): Promise<Buffer> {\n const buf = Buffer.isBuffer(value) ? value : Buffer.from(value, \"utf-8\");\n\n // Convert RedisSetOptions to @redis/client SetOptions\n const setOptions: SetOptions = {};\n\n // Handle expiration object format (from alepha/cache-redis, alepha/lock-redis)\n if (options?.expiration) {\n if (options.expiration.type === \"KEEPTTL\") {\n setOptions.KEEPTTL = true;\n } else {\n setOptions[options.expiration.type] = options.expiration.value;\n }\n }\n\n // Handle direct expiration properties\n if (options?.EX !== undefined) {\n setOptions.EX = options.EX;\n }\n if (options?.PX !== undefined) {\n setOptions.PX = options.PX;\n }\n if (options?.EXAT !== undefined) {\n setOptions.EXAT = options.EXAT;\n }\n if (options?.PXAT !== undefined) {\n setOptions.PXAT = options.PXAT;\n }\n if (options?.KEEPTTL) {\n setOptions.KEEPTTL = true;\n }\n\n // Handle condition object format\n if (options?.condition === \"NX\") {\n setOptions.NX = true;\n } else if (options?.condition === \"XX\") {\n setOptions.XX = true;\n }\n\n // Handle direct condition properties\n if (options?.NX) {\n setOptions.NX = true;\n }\n if (options?.XX) {\n setOptions.XX = true;\n }\n if (options?.GET) {\n setOptions.GET = true;\n }\n\n const resp = await this.publisher.set(\n key,\n buf,\n Object.keys(setOptions).length > 0 ? setOptions : undefined,\n );\n\n if (resp === \"OK\" || !resp) {\n return buf;\n }\n\n return Buffer.from(resp);\n }\n\n public override async has(key: string): Promise<boolean> {\n const resp = await this.publisher.exists(key);\n return resp > 0;\n }\n\n public override async keys(pattern: string): Promise<string[]> {\n const keys = await this.publisher.keys(pattern);\n return keys.map((key) => key.toString());\n }\n\n public override async del(keys: string[]): Promise<void> {\n if (keys.length === 0) {\n return;\n }\n\n await this.publisher.del(keys);\n }\n\n // ---------------------------------------------------------\n // Queue operations\n // ---------------------------------------------------------\n\n public override async lpush(key: string, value: string): Promise<void> {\n await this.publisher.LPUSH(key, value);\n }\n\n public override async rpop(key: string): Promise<string | undefined> {\n const value = await this.publisher.RPOP(key);\n if (value == null) {\n return undefined;\n }\n return String(value);\n }\n\n // ---------------------------------------------------------\n // Pub/Sub operations\n // ---------------------------------------------------------\n\n public override async publish(\n channel: string,\n message: string,\n ): Promise<void> {\n await this.publisher.publish(channel, message);\n }\n\n /**\n * Get the Redis connection URL.\n */\n protected getUrl(): string {\n // Prefer REDIS_URL if set\n if (this.env.REDIS_URL) {\n return this.env.REDIS_URL;\n }\n\n // Build URL from components\n const url = new URL(\"redis://127.0.0.1:6379\");\n\n if (this.env.REDIS_PASSWORD) {\n url.password = this.env.REDIS_PASSWORD;\n }\n\n if (this.env.REDIS_HOST) {\n url.hostname = this.env.REDIS_HOST;\n }\n\n if (this.env.REDIS_PORT) {\n url.port = String(this.env.REDIS_PORT);\n }\n\n return url.toString();\n }\n\n /**\n * Redis client factory method.\n */\n protected createClient(): NodeRedisClient {\n const client = createClient({\n url: this.getUrl(),\n RESP: 3,\n }).withTypeMapping({\n [RESP_TYPES.BLOB_STRING]: Buffer,\n });\n\n client.on(\"error\", (error) => {\n if (this.alepha.isStarted()) {\n this.log.error(error);\n }\n });\n\n return client;\n }\n}\n","import { $hook, $inject, Alepha } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport {\n type NodeRedisClient,\n NodeRedisProvider,\n} from \"./NodeRedisProvider.ts\";\nimport {\n RedisSubscriberProvider,\n type SubscribeCallback,\n} from \"./RedisSubscriberProvider.ts\";\n\n/**\n * Node.js Redis subscriber provider using `@redis/client`.\n *\n * This provider creates a dedicated Redis connection for subscriptions,\n * as Redis requires separate connections for pub/sub operations.\n *\n * @example\n * ```ts\n * const subscriber = alepha.inject(RedisSubscriberProvider);\n * await subscriber.subscribe(\"channel\", (message, channel) => {\n * console.log(`Received: ${message} on ${channel}`);\n * });\n * ```\n */\nexport class NodeRedisSubscriberProvider extends RedisSubscriberProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly redisProvider = $inject(NodeRedisProvider);\n protected readonly client: NodeRedisClient = this.createClient();\n\n public get subscriber(): NodeRedisClient {\n if (!this.client.isReady) {\n throw new Error(\"Redis subscriber client is not ready\");\n }\n\n return this.client;\n }\n\n public override get isReady(): boolean {\n return this.client.isReady;\n }\n\n protected readonly start = $hook({\n on: \"start\",\n handler: () => this.connect(),\n });\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: () => this.close(),\n });\n\n public override async connect(): Promise<void> {\n this.log.debug(\"Connecting subscriber...\");\n await this.client.connect();\n this.log.info(\"Subscriber connection OK\");\n }\n\n public override async close(): Promise<void> {\n this.log.debug(\"Closing subscriber connection...\");\n await this.subscriber.close();\n this.log.info(\"Subscriber connection closed\");\n }\n\n public override async subscribe(\n channel: string,\n callback: SubscribeCallback,\n ): Promise<void> {\n await this.subscriber.subscribe(channel, callback);\n }\n\n public override async unsubscribe(\n channel: string,\n callback?: SubscribeCallback,\n ): Promise<void> {\n await this.subscriber.unsubscribe(channel, callback);\n }\n\n /**\n * Redis subscriber client factory method.\n */\n protected createClient(): NodeRedisClient {\n const client = this.redisProvider.duplicate();\n\n client.on(\"error\", (error) => {\n if (this.alepha.isStarted()) {\n this.log.error(error);\n }\n });\n\n return client;\n }\n}\n","import { $module, type Alepha } from \"alepha\";\nimport { BunRedisProvider } from \"./providers/BunRedisProvider.ts\";\nimport { BunRedisSubscriberProvider } from \"./providers/BunRedisSubscriberProvider.ts\";\nimport { NodeRedisProvider } from \"./providers/NodeRedisProvider.ts\";\nimport { NodeRedisSubscriberProvider } from \"./providers/NodeRedisSubscriberProvider.ts\";\nimport { RedisProvider } from \"./providers/RedisProvider.ts\";\nimport { RedisSubscriberProvider } from \"./providers/RedisSubscriberProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./providers/BunRedisProvider.ts\";\nexport * from \"./providers/BunRedisSubscriberProvider.ts\";\nexport * from \"./providers/NodeRedisProvider.ts\";\nexport * from \"./providers/NodeRedisSubscriberProvider.ts\";\nexport * from \"./providers/RedisProvider.ts\";\nexport * from \"./providers/RedisSubscriberProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Redis client provider for Alepha applications.\n *\n * Automatically selects the appropriate provider based on runtime:\n * - Bun: Uses `BunRedisProvider` with Bun's native Redis client (7.9x faster than ioredis)\n * - Node.js: Uses `NodeRedisProvider` with `@redis/client`\n *\n * @example\n * ```ts\n * // Inject the abstract provider - runtime selects the implementation\n * const redis = alepha.inject(RedisProvider);\n *\n * // Use common operations\n * await redis.set(\"key\", \"value\");\n * const value = await redis.get(\"key\");\n *\n * // For pub/sub\n * const subscriber = alepha.inject(RedisSubscriberProvider);\n * await subscriber.subscribe(\"channel\", (message, channel) => {\n * console.log(`Received: ${message} on ${channel}`);\n * });\n * ```\n *\n * @see {@link RedisProvider} - Abstract base class\n * @see {@link NodeRedisProvider} - Node.js implementation\n * @see {@link BunRedisProvider} - Bun implementation\n * @see {@link RedisSubscriberProvider} - Abstract subscriber base class\n * @see {@link NodeRedisSubscriberProvider} - Node.js subscriber implementation\n * @see {@link BunRedisSubscriberProvider} - Bun subscriber implementation\n * @module alepha.redis\n */\nexport const AlephaRedis = $module({\n name: \"alepha.redis\",\n services: [\n NodeRedisProvider,\n NodeRedisSubscriberProvider,\n BunRedisProvider,\n BunRedisSubscriberProvider,\n RedisProvider,\n RedisSubscriberProvider,\n ],\n register: (alepha: Alepha) => {\n if (alepha.isBun()) {\n alepha\n .with({\n provide: RedisProvider,\n use: BunRedisProvider,\n })\n .with({\n provide: RedisSubscriberProvider,\n use: BunRedisSubscriberProvider,\n });\n } else {\n alepha\n .with({\n provide: RedisProvider,\n use: NodeRedisProvider,\n })\n .with({\n provide: RedisSubscriberProvider,\n use: NodeRedisSubscriberProvider,\n });\n }\n },\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["envSchema"],"sources":["../../src/redis/providers/RedisProvider.ts","../../src/redis/providers/BunRedisProvider.ts","../../src/redis/providers/RedisSubscriberProvider.ts","../../src/redis/providers/BunRedisSubscriberProvider.ts","../../src/redis/providers/NodeRedisProvider.ts","../../src/redis/providers/NodeRedisSubscriberProvider.ts","../../src/redis/index.ts"],"sourcesContent":["/**\n * Abstract Redis provider interface.\n *\n * This abstract class defines the common interface for Redis operations.\n * Implementations include:\n * - {@link NodeRedisProvider} - Uses `@redis/client` for Node.js runtime\n * - {@link BunRedisProvider} - Uses Bun's native `RedisClient` for Bun runtime\n *\n * @example\n * ```ts\n * // Inject the abstract provider - runtime selects the implementation\n * const redis = alepha.inject(RedisProvider);\n *\n * // Use common operations\n * await redis.set(\"key\", \"value\");\n * const value = await redis.get(\"key\");\n * ```\n */\nexport abstract class RedisProvider {\n /**\n * Whether the Redis client is ready to accept commands.\n */\n public abstract readonly isReady: boolean;\n\n /**\n * Connect to the Redis server.\n */\n public abstract connect(): Promise<void>;\n\n /**\n * Close the connection to the Redis server.\n */\n public abstract close(): Promise<void>;\n\n /**\n * Get the value of a key.\n *\n * @param key The key to get.\n * @returns The value as a Buffer, or undefined if the key does not exist.\n */\n public abstract get(key: string): Promise<Buffer | undefined>;\n\n /**\n * Set the value of a key.\n *\n * @param key The key to set.\n * @param value The value to set (Buffer or string).\n * @param options Optional set options (EX, PX, NX, XX, etc.).\n * @returns The value as a Buffer.\n */\n public abstract set(\n key: string,\n value: Buffer | string,\n options?: RedisSetOptions,\n ): Promise<Buffer>;\n\n /**\n * Check if a key exists.\n *\n * @param key The key to check.\n * @returns True if the key exists.\n */\n public abstract has(key: string): Promise<boolean>;\n\n /**\n * Get all keys matching a pattern.\n *\n * @param pattern The glob-style pattern to match.\n * @returns Array of matching key names.\n */\n public abstract keys(pattern: string): Promise<string[]>;\n\n /**\n * Delete one or more keys.\n *\n * @param keys The keys to delete.\n */\n public abstract del(keys: string[]): Promise<void>;\n\n // ---------------------------------------------------------\n // Queue operations (for alepha/queue-redis)\n // ---------------------------------------------------------\n\n /**\n * Push a value to the left (head) of a list.\n *\n * @param key The list key.\n * @param value The value to push.\n */\n public abstract lpush(key: string, value: string): Promise<void>;\n\n /**\n * Pop a value from the right (tail) of a list.\n *\n * @param key The list key.\n * @returns The value, or undefined if the list is empty.\n */\n public abstract rpop(key: string): Promise<string | undefined>;\n\n // ---------------------------------------------------------\n // Pub/Sub operations (for alepha/topic-redis)\n // ---------------------------------------------------------\n\n /**\n * Publish a message to a channel.\n *\n * @param channel The channel name.\n * @param message The message to publish.\n */\n public abstract publish(channel: string, message: string): Promise<void>;\n}\n\n/**\n * Common Redis SET command options.\n * Compatible with @redis/client SetOptions format.\n */\nexport interface RedisSetOptions {\n /**\n * Set the specified expire time, in seconds.\n */\n EX?: number;\n /**\n * Set the specified expire time, in milliseconds.\n */\n PX?: number;\n /**\n * Set the specified Unix time at which the key will expire, in seconds.\n */\n EXAT?: number;\n /**\n * Set the specified Unix time at which the key will expire, in milliseconds.\n */\n PXAT?: number;\n /**\n * Only set the key if it does not already exist.\n */\n NX?: boolean;\n /**\n * Only set the key if it already exists.\n */\n XX?: boolean;\n /**\n * Retain the time to live associated with the key.\n */\n KEEPTTL?: boolean;\n /**\n * Return the old string stored at key, or nil if key did not exist.\n */\n GET?: boolean;\n /**\n * Alternative expiration format (compatible with @redis/client).\n */\n expiration?: {\n type: \"EX\" | \"PX\" | \"EXAT\" | \"PXAT\" | \"KEEPTTL\";\n value: number;\n };\n /**\n * Alternative condition format (compatible with @redis/client).\n */\n condition?: \"NX\" | \"XX\";\n}\n","import {\n $env,\n $hook,\n $inject,\n Alepha,\n AlephaError,\n type Static,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { RedisClient as BunRedisClient } from \"bun\";\nimport { RedisProvider, type RedisSetOptions } from \"./RedisProvider.ts\";\n\nconst envSchema = t.object({\n REDIS_URL: t.optional(t.text()),\n REDIS_PORT: t.integer({\n default: \"6379\",\n }),\n REDIS_HOST: t.text({\n default: \"localhost\",\n }),\n REDIS_PASSWORD: t.optional(t.text()),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\n/**\n * Bun Redis client provider using Bun's native Redis client.\n *\n * This provider uses Bun's built-in `RedisClient` class for Redis connections,\n * which provides excellent performance (7.9x faster than ioredis) on the Bun runtime.\n *\n * @example\n * ```ts\n * // Set REDIS_URL environment variable\n * // REDIS_URL=redis://localhost:6379\n *\n * // Or configure via REDIS_HOST, REDIS_PORT, REDIS_PASSWORD\n *\n * // Or configure programmatically\n * alepha.with({\n * provide: RedisProvider,\n * use: BunRedisProvider,\n * });\n * ```\n */\nexport class BunRedisProvider extends RedisProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly env = $env(envSchema);\n protected client?: BunRedisClient;\n\n public get publisher(): BunRedisClient {\n if (!this.client?.connected) {\n throw new AlephaError(\"Redis client is not ready\");\n }\n\n return this.client;\n }\n\n public override get isReady(): boolean {\n return this.client?.connected ?? false;\n }\n\n protected readonly start = $hook({\n on: \"start\",\n handler: () => this.connect(),\n });\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: () => this.close(),\n });\n\n /**\n * Connect to the Redis server.\n */\n public override async connect(): Promise<void> {\n // Check if we're running in Bun\n if (typeof Bun === \"undefined\") {\n throw new AlephaError(\n \"BunRedisProvider requires the Bun runtime. Use NodeRedisProvider for Node.js.\",\n );\n }\n\n this.log.debug(\"Connecting...\");\n\n const { RedisClient } = await import(\"bun\");\n\n this.client = new RedisClient(this.getUrl(), {\n autoReconnect: true,\n enableAutoPipelining: true,\n });\n\n this.client.onconnect = () => {\n this.log.trace(\"Redis connected\");\n };\n\n this.client.onclose = (error) => {\n if (this.alepha.isStarted() && error) {\n this.log.error(\"Redis connection closed\", error);\n }\n };\n\n await this.client.connect();\n\n this.log.info(\"Connection OK\");\n }\n\n /**\n * Close the connection to the Redis server.\n */\n public override async close(): Promise<void> {\n if (this.client) {\n this.log.debug(\"Closing connection...\");\n this.client.close();\n this.client = undefined;\n this.log.info(\"Connection closed\");\n }\n }\n\n /**\n * Create a duplicate connection for pub/sub or other isolated operations.\n */\n public async duplicate(): Promise<BunRedisClient> {\n if (typeof Bun === \"undefined\") {\n throw new AlephaError(\"BunRedisProvider requires the Bun runtime.\");\n }\n\n const { RedisClient } = await import(\"bun\");\n\n const client = new RedisClient(this.getUrl(), {\n autoReconnect: true,\n enableAutoPipelining: true,\n });\n\n client.onclose = (error) => {\n if (this.alepha.isStarted() && error) {\n this.log.error(\"Redis duplicate connection closed\", error);\n }\n };\n\n await client.connect();\n\n return client;\n }\n\n public override async get(key: string): Promise<Buffer | undefined> {\n this.log.trace(`Getting key ${key}`);\n const resp = await this.publisher.getBuffer(key);\n\n if (resp === null) {\n return undefined;\n }\n\n return Buffer.from(resp);\n }\n\n public override async set(\n key: string,\n value: Buffer | string,\n options?: RedisSetOptions,\n ): Promise<Buffer> {\n const buf = Buffer.isBuffer(value) ? value : Buffer.from(value, \"utf-8\");\n\n // Build SET command arguments\n const args: string[] = [key, buf.toString(\"binary\")];\n\n // Handle expiration object format (from alepha/cache-redis, alepha/lock-redis)\n if (options?.expiration) {\n if (options.expiration.type === \"KEEPTTL\") {\n args.push(\"KEEPTTL\");\n } else {\n args.push(options.expiration.type, String(options.expiration.value));\n }\n }\n\n // Handle direct expiration properties\n if (options?.EX !== undefined) {\n args.push(\"EX\", String(options.EX));\n }\n if (options?.PX !== undefined) {\n args.push(\"PX\", String(options.PX));\n }\n if (options?.EXAT !== undefined) {\n args.push(\"EXAT\", String(options.EXAT));\n }\n if (options?.PXAT !== undefined) {\n args.push(\"PXAT\", String(options.PXAT));\n }\n if (options?.KEEPTTL) {\n args.push(\"KEEPTTL\");\n }\n\n // Handle condition object format\n if (options?.condition === \"NX\") {\n args.push(\"NX\");\n } else if (options?.condition === \"XX\") {\n args.push(\"XX\");\n }\n\n // Handle direct condition properties\n if (options?.NX) {\n args.push(\"NX\");\n }\n if (options?.XX) {\n args.push(\"XX\");\n }\n if (options?.GET) {\n args.push(\"GET\");\n }\n\n if (args.length === 2) {\n // Simple set without options\n await this.publisher.set(key, buf);\n } else {\n // Set with options via raw command\n await this.publisher.send(\"SET\", args);\n }\n\n return buf;\n }\n\n public override async has(key: string): Promise<boolean> {\n return this.publisher.exists(key);\n }\n\n public override async keys(pattern: string): Promise<string[]> {\n const keys = await this.publisher.send(\"KEYS\", [pattern]);\n if (!Array.isArray(keys)) {\n return [];\n }\n return keys.map((key) =>\n key instanceof Uint8Array ? Buffer.from(key).toString() : String(key),\n );\n }\n\n public override async del(keys: string[]): Promise<void> {\n if (keys.length === 0) {\n return;\n }\n\n await this.publisher.send(\"DEL\", keys);\n }\n\n // ---------------------------------------------------------\n // Queue operations\n // ---------------------------------------------------------\n\n public override async lpush(key: string, value: string): Promise<void> {\n await this.publisher.send(\"LPUSH\", [key, value]);\n }\n\n public override async rpop(key: string): Promise<string | undefined> {\n const value = await this.publisher.send(\"RPOP\", [key]);\n if (value == null) {\n return undefined;\n }\n if (value instanceof Uint8Array) {\n return Buffer.from(value).toString();\n }\n return String(value);\n }\n\n // ---------------------------------------------------------\n // Pub/Sub operations\n // ---------------------------------------------------------\n\n public override async publish(\n channel: string,\n message: string,\n ): Promise<void> {\n await this.publisher.publish(channel, message);\n }\n\n /**\n * Get the Redis connection URL.\n */\n protected getUrl(): string {\n // Prefer REDIS_URL if set\n if (this.env.REDIS_URL) {\n return this.env.REDIS_URL;\n }\n\n // Build URL from components\n const url = new URL(\"redis://127.0.0.1:6379\");\n\n if (this.env.REDIS_PASSWORD) {\n url.password = this.env.REDIS_PASSWORD;\n }\n\n if (this.env.REDIS_HOST) {\n url.hostname = this.env.REDIS_HOST;\n }\n\n if (this.env.REDIS_PORT) {\n url.port = String(this.env.REDIS_PORT);\n }\n\n return url.toString();\n }\n}\n","/**\n * Abstract Redis subscriber provider interface.\n *\n * This abstract class defines the common interface for Redis pub/sub subscriptions.\n * Implementations include:\n * - {@link NodeRedisSubscriberProvider} - Uses `@redis/client` for Node.js runtime\n * - {@link BunRedisSubscriberProvider} - Uses Bun's native `RedisClient` for Bun runtime\n *\n * Redis requires separate connections for pub/sub operations, so this provider\n * creates a dedicated connection for subscriptions.\n *\n * @example\n * ```ts\n * // Inject the abstract provider - runtime selects the implementation\n * const subscriber = alepha.inject(RedisSubscriberProvider);\n *\n * // Subscribe to a channel\n * await subscriber.subscribe(\"my-channel\", (message, channel) => {\n * console.log(`Received: ${message} on ${channel}`);\n * });\n * ```\n */\nexport abstract class RedisSubscriberProvider {\n /**\n * Whether the Redis subscriber client is ready to accept commands.\n */\n public abstract readonly isReady: boolean;\n\n /**\n * Connect to the Redis server for subscriptions.\n */\n public abstract connect(): Promise<void>;\n\n /**\n * Close the subscriber connection.\n */\n public abstract close(): Promise<void>;\n\n /**\n * Subscribe to a channel.\n *\n * @param channel The channel name.\n * @param callback The callback to invoke when a message is received.\n */\n public abstract subscribe(\n channel: string,\n callback: SubscribeCallback,\n ): Promise<void>;\n\n /**\n * Unsubscribe from a channel.\n *\n * @param channel The channel name.\n * @param callback Optional specific callback to remove.\n */\n public abstract unsubscribe(\n channel: string,\n callback?: SubscribeCallback,\n ): Promise<void>;\n}\n\n/**\n * Callback for subscription messages.\n */\nexport type SubscribeCallback = (message: string, channel: string) => void;\n","import { $hook, $inject, Alepha, AlephaError } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport type { RedisClient as BunRedisClient } from \"bun\";\nimport { BunRedisProvider } from \"./BunRedisProvider.ts\";\nimport {\n RedisSubscriberProvider,\n type SubscribeCallback,\n} from \"./RedisSubscriberProvider.ts\";\n\n/**\n * Bun Redis subscriber provider for pub/sub operations.\n *\n * This provider creates a dedicated Redis connection for subscriptions,\n * as Redis requires separate connections for pub/sub operations.\n *\n * @example\n * ```ts\n * const subscriber = alepha.inject(RedisSubscriberProvider);\n * await subscriber.subscribe(\"channel\", (message, channel) => {\n * console.log(`Received: ${message} on ${channel}`);\n * });\n * ```\n */\nexport class BunRedisSubscriberProvider extends RedisSubscriberProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly redisProvider = $inject(BunRedisProvider);\n protected client?: BunRedisClient;\n\n public get subscriber(): BunRedisClient {\n if (!this.client?.connected) {\n throw new AlephaError(\"Redis subscriber client is not ready\");\n }\n\n return this.client;\n }\n\n public override get isReady(): boolean {\n return this.client?.connected ?? false;\n }\n\n protected readonly start = $hook({\n on: \"start\",\n handler: () => this.connect(),\n });\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: () => this.close(),\n });\n\n /**\n * Connect to the Redis server for subscriptions.\n */\n public override async connect(): Promise<void> {\n this.log.debug(\"Connecting subscriber...\");\n this.client = await this.redisProvider.duplicate();\n this.log.info(\"Subscriber connection OK\");\n }\n\n /**\n * Close the subscriber connection.\n */\n public override async close(): Promise<void> {\n if (this.client) {\n this.log.debug(\"Closing subscriber connection...\");\n this.client.close();\n this.client = undefined;\n this.log.info(\"Subscriber connection closed\");\n }\n }\n\n public override async subscribe(\n channel: string,\n callback: SubscribeCallback,\n ): Promise<void> {\n await this.subscriber.subscribe(channel, (message, ch) => {\n // Bun's callback provides Buffer or string, normalize to string\n const msg =\n typeof message === \"object\" && message !== null\n ? Buffer.from(message as Uint8Array).toString()\n : String(message);\n callback(msg, ch);\n });\n }\n\n public override async unsubscribe(\n channel: string,\n _callback?: SubscribeCallback,\n ): Promise<void> {\n // Bun's unsubscribe doesn't support callback filtering\n await this.subscriber.unsubscribe(channel);\n }\n}\n","import {\n createClient,\n RESP_TYPES,\n type RedisClientType,\n type SetOptions,\n} from \"@redis/client\";\nimport { $env, $hook, $inject, Alepha, type Static, t } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { RedisProvider, type RedisSetOptions } from \"./RedisProvider.ts\";\n\nconst envSchema = t.object({\n REDIS_URL: t.optional(t.text()),\n REDIS_PORT: t.integer({\n default: \"6379\",\n }),\n REDIS_HOST: t.text({\n default: \"localhost\",\n }),\n REDIS_PASSWORD: t.optional(t.text()),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\nexport type NodeRedisClient = RedisClientType<\n {},\n {},\n {},\n 3,\n { 36: BufferConstructor }\n>;\nexport type NodeRedisClientOptions = Parameters<typeof createClient>[0];\n\n/**\n * Node.js Redis client provider using `@redis/client`.\n *\n * This provider uses the official Redis client for Node.js runtime.\n *\n * @example\n * ```ts\n * // Set REDIS_URL environment variable\n * // REDIS_URL=redis://localhost:6379\n *\n * // Or configure via REDIS_HOST, REDIS_PORT, REDIS_PASSWORD\n *\n * // Or configure programmatically\n * alepha.with({\n * provide: RedisProvider,\n * use: NodeRedisProvider,\n * });\n * ```\n */\nexport class NodeRedisProvider extends RedisProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly env = $env(envSchema);\n protected readonly client = this.createClient();\n\n public get publisher(): NodeRedisClient {\n if (!this.client.isReady) {\n throw new Error(\"Redis client is not ready\");\n }\n\n return this.client;\n }\n\n public override get isReady(): boolean {\n return this.client.isReady;\n }\n\n protected readonly start = $hook({\n on: \"start\",\n handler: () => this.connect(),\n });\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: () => this.close(),\n });\n\n /**\n * Connect to the Redis server.\n */\n public override async connect(): Promise<void> {\n this.log.debug(\"Connecting...\");\n await this.client.connect();\n this.log.info(\"Connection OK\");\n }\n\n /**\n * Close the connection to the Redis server.\n */\n public override async close(): Promise<void> {\n this.log.debug(\"Closing connection...\");\n await this.client.close();\n this.log.info(\"Connection closed\");\n }\n\n public duplicate(options?: Partial<NodeRedisClientOptions>): NodeRedisClient {\n return this.client\n .duplicate({\n ...options,\n RESP: 3,\n })\n .withTypeMapping({\n [RESP_TYPES.BLOB_STRING]: Buffer,\n });\n }\n\n public override async get(key: string): Promise<Buffer | undefined> {\n this.log.trace(`Getting key ${key}`);\n const resp = await this.publisher.get(key);\n\n if (resp === null) {\n return undefined;\n }\n\n return Buffer.from(resp);\n }\n\n public override async set(\n key: string,\n value: Buffer | string,\n options?: RedisSetOptions,\n ): Promise<Buffer> {\n const buf = Buffer.isBuffer(value) ? value : Buffer.from(value, \"utf-8\");\n\n // Convert RedisSetOptions to @redis/client SetOptions\n const setOptions: SetOptions = {};\n\n // Handle expiration object format (from alepha/cache-redis, alepha/lock-redis)\n if (options?.expiration) {\n if (options.expiration.type === \"KEEPTTL\") {\n setOptions.KEEPTTL = true;\n } else {\n setOptions[options.expiration.type] = options.expiration.value;\n }\n }\n\n // Handle direct expiration properties\n if (options?.EX !== undefined) {\n setOptions.EX = options.EX;\n }\n if (options?.PX !== undefined) {\n setOptions.PX = options.PX;\n }\n if (options?.EXAT !== undefined) {\n setOptions.EXAT = options.EXAT;\n }\n if (options?.PXAT !== undefined) {\n setOptions.PXAT = options.PXAT;\n }\n if (options?.KEEPTTL) {\n setOptions.KEEPTTL = true;\n }\n\n // Handle condition object format\n if (options?.condition === \"NX\") {\n setOptions.NX = true;\n } else if (options?.condition === \"XX\") {\n setOptions.XX = true;\n }\n\n // Handle direct condition properties\n if (options?.NX) {\n setOptions.NX = true;\n }\n if (options?.XX) {\n setOptions.XX = true;\n }\n if (options?.GET) {\n setOptions.GET = true;\n }\n\n const resp = await this.publisher.set(\n key,\n buf,\n Object.keys(setOptions).length > 0 ? setOptions : undefined,\n );\n\n if (resp === \"OK\" || !resp) {\n return buf;\n }\n\n return Buffer.from(resp);\n }\n\n public override async has(key: string): Promise<boolean> {\n const resp = await this.publisher.exists(key);\n return resp > 0;\n }\n\n public override async keys(pattern: string): Promise<string[]> {\n const keys = await this.publisher.keys(pattern);\n return keys.map((key) => key.toString());\n }\n\n public override async del(keys: string[]): Promise<void> {\n if (keys.length === 0) {\n return;\n }\n\n await this.publisher.del(keys);\n }\n\n // ---------------------------------------------------------\n // Queue operations\n // ---------------------------------------------------------\n\n public override async lpush(key: string, value: string): Promise<void> {\n await this.publisher.LPUSH(key, value);\n }\n\n public override async rpop(key: string): Promise<string | undefined> {\n const value = await this.publisher.RPOP(key);\n if (value == null) {\n return undefined;\n }\n return String(value);\n }\n\n // ---------------------------------------------------------\n // Pub/Sub operations\n // ---------------------------------------------------------\n\n public override async publish(\n channel: string,\n message: string,\n ): Promise<void> {\n await this.publisher.publish(channel, message);\n }\n\n /**\n * Get the Redis connection URL.\n */\n protected getUrl(): string {\n // Prefer REDIS_URL if set\n if (this.env.REDIS_URL) {\n return this.env.REDIS_URL;\n }\n\n // Build URL from components\n const url = new URL(\"redis://127.0.0.1:6379\");\n\n if (this.env.REDIS_PASSWORD) {\n url.password = this.env.REDIS_PASSWORD;\n }\n\n if (this.env.REDIS_HOST) {\n url.hostname = this.env.REDIS_HOST;\n }\n\n if (this.env.REDIS_PORT) {\n url.port = String(this.env.REDIS_PORT);\n }\n\n return url.toString();\n }\n\n /**\n * Redis client factory method.\n */\n protected createClient(): NodeRedisClient {\n const client = createClient({\n url: this.getUrl(),\n RESP: 3,\n }).withTypeMapping({\n [RESP_TYPES.BLOB_STRING]: Buffer,\n });\n\n client.on(\"error\", (error) => {\n if (this.alepha.isStarted()) {\n this.log.error(error);\n }\n });\n\n return client;\n }\n}\n","import { $hook, $inject, Alepha } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport {\n type NodeRedisClient,\n NodeRedisProvider,\n} from \"./NodeRedisProvider.ts\";\nimport {\n RedisSubscriberProvider,\n type SubscribeCallback,\n} from \"./RedisSubscriberProvider.ts\";\n\n/**\n * Node.js Redis subscriber provider using `@redis/client`.\n *\n * This provider creates a dedicated Redis connection for subscriptions,\n * as Redis requires separate connections for pub/sub operations.\n *\n * @example\n * ```ts\n * const subscriber = alepha.inject(RedisSubscriberProvider);\n * await subscriber.subscribe(\"channel\", (message, channel) => {\n * console.log(`Received: ${message} on ${channel}`);\n * });\n * ```\n */\nexport class NodeRedisSubscriberProvider extends RedisSubscriberProvider {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly redisProvider = $inject(NodeRedisProvider);\n protected readonly client: NodeRedisClient = this.createClient();\n\n public get subscriber(): NodeRedisClient {\n if (!this.client.isReady) {\n throw new Error(\"Redis subscriber client is not ready\");\n }\n\n return this.client;\n }\n\n public override get isReady(): boolean {\n return this.client.isReady;\n }\n\n protected readonly start = $hook({\n on: \"start\",\n handler: () => this.connect(),\n });\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: () => this.close(),\n });\n\n public override async connect(): Promise<void> {\n this.log.debug(\"Connecting subscriber...\");\n await this.client.connect();\n this.log.info(\"Subscriber connection OK\");\n }\n\n public override async close(): Promise<void> {\n this.log.debug(\"Closing subscriber connection...\");\n await this.subscriber.close();\n this.log.info(\"Subscriber connection closed\");\n }\n\n public override async subscribe(\n channel: string,\n callback: SubscribeCallback,\n ): Promise<void> {\n await this.subscriber.subscribe(channel, callback);\n }\n\n public override async unsubscribe(\n channel: string,\n callback?: SubscribeCallback,\n ): Promise<void> {\n await this.subscriber.unsubscribe(channel, callback);\n }\n\n /**\n * Redis subscriber client factory method.\n */\n protected createClient(): NodeRedisClient {\n const client = this.redisProvider.duplicate();\n\n client.on(\"error\", (error) => {\n if (this.alepha.isStarted()) {\n this.log.error(error);\n }\n });\n\n return client;\n }\n}\n","import { $module, type Alepha } from \"alepha\";\nimport { BunRedisProvider } from \"./providers/BunRedisProvider.ts\";\nimport { BunRedisSubscriberProvider } from \"./providers/BunRedisSubscriberProvider.ts\";\nimport { NodeRedisProvider } from \"./providers/NodeRedisProvider.ts\";\nimport { NodeRedisSubscriberProvider } from \"./providers/NodeRedisSubscriberProvider.ts\";\nimport { RedisProvider } from \"./providers/RedisProvider.ts\";\nimport { RedisSubscriberProvider } from \"./providers/RedisSubscriberProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./providers/BunRedisProvider.ts\";\nexport * from \"./providers/BunRedisSubscriberProvider.ts\";\nexport * from \"./providers/NodeRedisProvider.ts\";\nexport * from \"./providers/NodeRedisSubscriberProvider.ts\";\nexport * from \"./providers/RedisProvider.ts\";\nexport * from \"./providers/RedisSubscriberProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Redis client provider for Alepha applications.\n *\n * Automatically selects the appropriate provider based on runtime:\n * - Bun: Uses `BunRedisProvider` with Bun's native Redis client (7.9x faster than ioredis)\n * - Node.js: Uses `NodeRedisProvider` with `@redis/client`\n *\n * @example\n * ```ts\n * // Inject the abstract provider - runtime selects the implementation\n * const redis = alepha.inject(RedisProvider);\n *\n * // Use common operations\n * await redis.set(\"key\", \"value\");\n * const value = await redis.get(\"key\");\n *\n * // For pub/sub\n * const subscriber = alepha.inject(RedisSubscriberProvider);\n * await subscriber.subscribe(\"channel\", (message, channel) => {\n * console.log(`Received: ${message} on ${channel}`);\n * });\n * ```\n *\n * @see {@link RedisProvider} - Abstract base class\n * @see {@link NodeRedisProvider} - Node.js implementation\n * @see {@link BunRedisProvider} - Bun implementation\n * @see {@link RedisSubscriberProvider} - Abstract subscriber base class\n * @see {@link NodeRedisSubscriberProvider} - Node.js subscriber implementation\n * @see {@link BunRedisSubscriberProvider} - Bun subscriber implementation\n * @module alepha.redis\n */\nexport const AlephaRedis = $module({\n name: \"alepha.redis\",\n services: [\n NodeRedisProvider,\n NodeRedisSubscriberProvider,\n BunRedisProvider,\n BunRedisSubscriberProvider,\n RedisProvider,\n RedisSubscriberProvider,\n ],\n register: (alepha: Alepha) => {\n if (alepha.isBun()) {\n alepha\n .with({\n provide: RedisProvider,\n use: BunRedisProvider,\n })\n .with({\n provide: RedisSubscriberProvider,\n use: BunRedisSubscriberProvider,\n });\n } else {\n alepha\n .with({\n provide: RedisProvider,\n use: NodeRedisProvider,\n })\n .with({\n provide: RedisSubscriberProvider,\n use: NodeRedisSubscriberProvider,\n });\n }\n },\n});\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["lastError: Error | undefined"],"sources":["../../src/retry/errors/RetryCancelError.ts","../../src/retry/errors/RetryTimeoutError.ts","../../src/retry/providers/RetryProvider.ts","../../src/retry/primitives/$retry.ts","../../src/retry/index.ts"],"sourcesContent":["import { AlephaError } from \"alepha\";\n\nexport class RetryCancelError extends AlephaError {\n constructor() {\n super(\"Retry operation was cancelled.\");\n this.name = \"RetryCancelError\";\n }\n}\n","import { AlephaError } from \"alepha\";\n\nexport class RetryTimeoutError extends AlephaError {\n constructor(duration: number) {\n super(`Retry operation timed out after ${duration}ms.`);\n this.name = \"RetryTimeoutError\";\n }\n}\n","import { $inject } from \"alepha\";\nimport { DateTimeProvider, type DurationLike } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { RetryCancelError } from \"../errors/RetryCancelError.ts\";\nimport { RetryTimeoutError } from \"../errors/RetryTimeoutError.ts\";\n\nexport interface RetryOptions<T extends (...args: any[]) => any> {\n /**\n * The function to retry.\n */\n handler: T;\n\n /**\n * The maximum number of attempts.\n *\n * @default 3\n */\n max?: number;\n\n /**\n * The backoff strategy for delays between retries.\n * Can be a fixed number (in ms) or a configuration object for exponential backoff.\n *\n * @default { initial: 200, factor: 2, jitter: true }\n */\n backoff?: number | RetryBackoffOptions;\n\n /**\n * An overall time limit for all retry attempts combined.\n *\n * e.g., `[5, 'seconds']`\n */\n maxDuration?: DurationLike;\n\n /**\n * A function that determines if a retry should be attempted based on the error.\n *\n * @default (error) => true (retries on any error)\n */\n when?: (error: Error) => boolean;\n\n /**\n * A custom callback for when a retry attempt fails.\n * This is called before the delay.\n */\n onError?: (error: Error, attempt: number, ...args: Parameters<T>) => void;\n\n /**\n * An AbortSignal to allow for external cancellation of the retry loop.\n */\n signal?: AbortSignal;\n\n /**\n * An additional AbortSignal to combine with the provided signal.\n * Used internally by $retry to handle app lifecycle.\n */\n additionalSignal?: AbortSignal;\n}\n\nexport interface RetryBackoffOptions {\n /**\n * Initial delay in milliseconds.\n *\n * @default 200\n */\n initial?: number;\n\n /**\n * Multiplier for each subsequent delay.\n *\n * @default 2\n */\n factor?: number;\n\n /**\n * Maximum delay in milliseconds.\n */\n max?: number;\n\n /**\n * If true, adds a random jitter to the delay to prevent thundering herd.\n *\n * @default true\n */\n jitter?: boolean;\n}\n\n/**\n * Service for executing functions with automatic retry logic.\n * Supports exponential backoff, max duration, conditional retries, and cancellation.\n */\nexport class RetryProvider {\n protected readonly log = $logger();\n protected readonly dateTime = $inject(DateTimeProvider);\n\n /**\n * Execute a function with automatic retry logic.\n */\n async retry<T extends (...args: any[]) => any>(\n options: RetryOptions<T>,\n ...args: Parameters<T>\n ): Promise<ReturnType<T>> {\n const maxAttempts = options.max ?? 3;\n const when = options.when ?? (() => true);\n const { handler, onError } = options;\n\n let lastError: Error | undefined;\n const startTime = Date.now();\n\n const maxDurationMs = options.maxDuration\n ? this.dateTime.duration(options.maxDuration).asMilliseconds()\n : Infinity;\n\n // Combine user-provided signal with additional signal (e.g., app lifecycle)\n const signals = [options.signal, options.additionalSignal].filter(Boolean);\n const onAbort = () => {\n // Always set RetryCancelError when aborted, even if another error exists\n // This ensures cancellation takes precedence over retry errors\n lastError = new RetryCancelError();\n };\n\n // Add abort listeners to all signals\n for (const signal of signals) {\n signal?.addEventListener(\"abort\", onAbort);\n }\n\n // FIX BUG #8: Create combined signal ONCE at the start instead of on each backoff\n // This prevents memory leak from creating multiple AbortSignal.any() instances\n const waitSignals = [options.signal, options.additionalSignal].filter(\n Boolean,\n ) as AbortSignal[];\n const combinedSignal =\n waitSignals.length > 0 ? AbortSignal.any(waitSignals) : undefined;\n\n try {\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n // Check for cancellation\n if (signals.some((signal) => signal?.aborted)) {\n throw new RetryCancelError();\n }\n\n // Check for timeout before attempting\n if (Date.now() - startTime >= maxDurationMs) {\n throw new RetryTimeoutError(maxDurationMs);\n }\n\n try {\n const result = await handler(...args);\n\n // Check for timeout after handler execution\n if (Date.now() - startTime >= maxDurationMs) {\n throw new RetryTimeoutError(maxDurationMs);\n }\n\n return result;\n } catch (err) {\n lastError = err as Error;\n\n // Check for timeout after error\n if (Date.now() - startTime >= maxDurationMs) {\n throw new RetryTimeoutError(maxDurationMs);\n }\n\n // Log the error with warning level\n this.log.warn(\"Retry attempt failed\", {\n attempt,\n maxAttempts,\n remainingAttempts: maxAttempts - attempt,\n error: lastError.message,\n errorName: lastError.name,\n });\n\n if (!(err instanceof Error) || !when(err)) {\n throw err; // don't retry if it's not an Error or `when` returns false\n }\n\n // FIX BUG #7: Call onError BEFORE checking if this is the final attempt\n // This ensures onError is called for ALL failed attempts, including the last one\n if (onError) {\n onError(err, attempt, ...args);\n }\n\n if (attempt >= maxAttempts) {\n break; // will throw lastError after the loop\n }\n\n // Calculate and wait for backoff delay\n const delay = this.calculateBackoff(attempt, options.backoff);\n if (delay > 0) {\n await this.dateTime.wait(delay, { signal: combinedSignal });\n }\n\n // Check for timeout after backoff wait before next attempt\n if (Date.now() - startTime >= maxDurationMs) {\n throw new RetryTimeoutError(maxDurationMs);\n }\n }\n }\n } finally {\n // Clean up listeners to prevent memory leaks\n for (const signal of signals) {\n signal?.removeEventListener(\"abort\", onAbort);\n }\n }\n\n throw lastError;\n }\n\n /**\n * Calculate the backoff delay for a given attempt.\n */\n protected calculateBackoff(\n attempt: number,\n options?: number | RetryBackoffOptions,\n ): number {\n if (typeof options === \"number\") {\n return options;\n }\n\n const initial = options?.initial ?? 200;\n const factor = options?.factor ?? 2;\n const max = options?.max ?? 10000;\n const useJitter = options?.jitter !== false;\n\n const exponential = initial * factor ** (attempt - 1);\n let delay = Math.min(exponential, max);\n\n if (useJitter) {\n // Add a random amount of jitter (e.g., up to 50% of the delay)\n delay = delay * (1 + Math.random() * 0.5);\n }\n\n return Math.floor(delay);\n }\n}\n","import {\n $inject,\n createPrimitive,\n KIND,\n Primitive,\n type PrimitiveArgs,\n} from \"alepha\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport type { RetryBackoffOptions } from \"../providers/RetryProvider.ts\";\nimport { RetryProvider } from \"../providers/RetryProvider.ts\";\n\n/**\n * Creates a function that automatically retries a handler upon failure,\n * with support for exponential backoff, max duration, and cancellation.\n */\nexport const $retry = <T extends (...args: any[]) => any>(\n options: RetryPrimitiveOptions<T>,\n): RetryPrimitiveFn<T> => {\n const instance = createPrimitive(RetryPrimitive, options);\n const fn = (...args: Parameters<T>) => instance.run(...args);\n return Object.setPrototypeOf(fn, instance) as RetryPrimitiveFn<T>;\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface RetryPrimitiveOptions<T extends (...args: any[]) => any> {\n /**\n * The function to retry.\n */\n handler: T;\n\n /**\n * The maximum number of attempts.\n *\n * @default 3\n */\n max?: number;\n\n /**\n * The backoff strategy for delays between retries.\n * Can be a fixed number (in ms) or a configuration object for exponential backoff.\n *\n * @default { initial: 200, factor: 2, jitter: true }\n */\n backoff?: number | RetryBackoffOptions;\n\n /**\n * An overall time limit for all retry attempts combined.\n *\n * e.g., `[5, 'seconds']`\n */\n maxDuration?: DurationLike;\n\n /**\n * A function that determines if a retry should be attempted based on the error.\n *\n * @default (error) => true (retries on any error)\n */\n when?: (error: Error) => boolean;\n\n /**\n * A custom callback for when a retry attempt fails.\n * This is called before the delay.\n */\n onError?: (error: Error, attempt: number, ...args: Parameters<T>) => void;\n\n /**\n * An AbortSignal to allow for external cancellation of the retry loop.\n */\n signal?: AbortSignal;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class RetryPrimitive<\n T extends (...args: any[]) => any,\n> extends Primitive<RetryPrimitiveOptions<T>> {\n protected readonly retryProvider = $inject(RetryProvider);\n protected appAbortController?: AbortController;\n\n constructor(args: PrimitiveArgs<RetryPrimitiveOptions<T>>) {\n super(args);\n\n this.alepha.events.on(\"stop\", () => {\n this.appAbortController?.abort();\n });\n }\n\n async run(...args: Parameters<T>): Promise<ReturnType<T>> {\n // Nov 25: Cloudflare does not like 'new AbortController' outside main handler, we can't pre-create it in the constructor.\n this.appAbortController ??= new AbortController();\n\n return this.retryProvider.retry(\n {\n ...this.options,\n additionalSignal: this.appAbortController.signal,\n },\n ...args,\n );\n }\n}\n\nexport interface RetryPrimitiveFn<T extends (...args: any[]) => any>\n extends RetryPrimitive<T> {\n (...args: Parameters<T>): Promise<ReturnType<T>>;\n}\n\n$retry[KIND] = RetryPrimitive;\n","import { $module } from \"alepha\";\nimport { $retry } from \"./primitives/$retry.ts\";\nimport { RetryProvider } from \"./providers/RetryProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./errors/RetryCancelError.ts\";\nexport * from \"./errors/RetryTimeoutError.ts\";\nexport * from \"./primitives/$retry.ts\";\nexport * from \"./providers/RetryProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Retry mechanism provider for Alepha applications.\n *\n * @see {@link RetryProvider}\n * @module alepha.retry\n */\nexport const AlephaRetry = $module({\n name: \"alepha.retry\",\n primitives: [$retry],\n services: [RetryProvider],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/retry/errors/RetryCancelError.ts","../../src/retry/errors/RetryTimeoutError.ts","../../src/retry/providers/RetryProvider.ts","../../src/retry/primitives/$retry.ts","../../src/retry/index.ts"],"sourcesContent":["import { AlephaError } from \"alepha\";\n\nexport class RetryCancelError extends AlephaError {\n constructor() {\n super(\"Retry operation was cancelled.\");\n this.name = \"RetryCancelError\";\n }\n}\n","import { AlephaError } from \"alepha\";\n\nexport class RetryTimeoutError extends AlephaError {\n constructor(duration: number) {\n super(`Retry operation timed out after ${duration}ms.`);\n this.name = \"RetryTimeoutError\";\n }\n}\n","import { $inject } from \"alepha\";\nimport { DateTimeProvider, type DurationLike } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { RetryCancelError } from \"../errors/RetryCancelError.ts\";\nimport { RetryTimeoutError } from \"../errors/RetryTimeoutError.ts\";\n\nexport interface RetryOptions<T extends (...args: any[]) => any> {\n /**\n * The function to retry.\n */\n handler: T;\n\n /**\n * The maximum number of attempts.\n *\n * @default 3\n */\n max?: number;\n\n /**\n * The backoff strategy for delays between retries.\n * Can be a fixed number (in ms) or a configuration object for exponential backoff.\n *\n * @default { initial: 200, factor: 2, jitter: true }\n */\n backoff?: number | RetryBackoffOptions;\n\n /**\n * An overall time limit for all retry attempts combined.\n *\n * e.g., `[5, 'seconds']`\n */\n maxDuration?: DurationLike;\n\n /**\n * A function that determines if a retry should be attempted based on the error.\n *\n * @default (error) => true (retries on any error)\n */\n when?: (error: Error) => boolean;\n\n /**\n * A custom callback for when a retry attempt fails.\n * This is called before the delay.\n */\n onError?: (error: Error, attempt: number, ...args: Parameters<T>) => void;\n\n /**\n * An AbortSignal to allow for external cancellation of the retry loop.\n */\n signal?: AbortSignal;\n\n /**\n * An additional AbortSignal to combine with the provided signal.\n * Used internally by $retry to handle app lifecycle.\n */\n additionalSignal?: AbortSignal;\n}\n\nexport interface RetryBackoffOptions {\n /**\n * Initial delay in milliseconds.\n *\n * @default 200\n */\n initial?: number;\n\n /**\n * Multiplier for each subsequent delay.\n *\n * @default 2\n */\n factor?: number;\n\n /**\n * Maximum delay in milliseconds.\n */\n max?: number;\n\n /**\n * If true, adds a random jitter to the delay to prevent thundering herd.\n *\n * @default true\n */\n jitter?: boolean;\n}\n\n/**\n * Service for executing functions with automatic retry logic.\n * Supports exponential backoff, max duration, conditional retries, and cancellation.\n */\nexport class RetryProvider {\n protected readonly log = $logger();\n protected readonly dateTime = $inject(DateTimeProvider);\n\n /**\n * Execute a function with automatic retry logic.\n */\n async retry<T extends (...args: any[]) => any>(\n options: RetryOptions<T>,\n ...args: Parameters<T>\n ): Promise<ReturnType<T>> {\n const maxAttempts = options.max ?? 3;\n const when = options.when ?? (() => true);\n const { handler, onError } = options;\n\n let lastError: Error | undefined;\n const startTime = Date.now();\n\n const maxDurationMs = options.maxDuration\n ? this.dateTime.duration(options.maxDuration).asMilliseconds()\n : Infinity;\n\n // Combine user-provided signal with additional signal (e.g., app lifecycle)\n const signals = [options.signal, options.additionalSignal].filter(Boolean);\n const onAbort = () => {\n // Always set RetryCancelError when aborted, even if another error exists\n // This ensures cancellation takes precedence over retry errors\n lastError = new RetryCancelError();\n };\n\n // Add abort listeners to all signals\n for (const signal of signals) {\n signal?.addEventListener(\"abort\", onAbort);\n }\n\n // FIX BUG #8: Create combined signal ONCE at the start instead of on each backoff\n // This prevents memory leak from creating multiple AbortSignal.any() instances\n const waitSignals = [options.signal, options.additionalSignal].filter(\n Boolean,\n ) as AbortSignal[];\n const combinedSignal =\n waitSignals.length > 0 ? AbortSignal.any(waitSignals) : undefined;\n\n try {\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n // Check for cancellation\n if (signals.some((signal) => signal?.aborted)) {\n throw new RetryCancelError();\n }\n\n // Check for timeout before attempting\n if (Date.now() - startTime >= maxDurationMs) {\n throw new RetryTimeoutError(maxDurationMs);\n }\n\n try {\n const result = await handler(...args);\n\n // Check for timeout after handler execution\n if (Date.now() - startTime >= maxDurationMs) {\n throw new RetryTimeoutError(maxDurationMs);\n }\n\n return result;\n } catch (err) {\n lastError = err as Error;\n\n // Check for timeout after error\n if (Date.now() - startTime >= maxDurationMs) {\n throw new RetryTimeoutError(maxDurationMs);\n }\n\n // Log the error with warning level\n this.log.warn(\"Retry attempt failed\", {\n attempt,\n maxAttempts,\n remainingAttempts: maxAttempts - attempt,\n error: lastError.message,\n errorName: lastError.name,\n });\n\n if (!(err instanceof Error) || !when(err)) {\n throw err; // don't retry if it's not an Error or `when` returns false\n }\n\n // FIX BUG #7: Call onError BEFORE checking if this is the final attempt\n // This ensures onError is called for ALL failed attempts, including the last one\n if (onError) {\n onError(err, attempt, ...args);\n }\n\n if (attempt >= maxAttempts) {\n break; // will throw lastError after the loop\n }\n\n // Calculate and wait for backoff delay\n const delay = this.calculateBackoff(attempt, options.backoff);\n if (delay > 0) {\n await this.dateTime.wait(delay, { signal: combinedSignal });\n }\n\n // Check for timeout after backoff wait before next attempt\n if (Date.now() - startTime >= maxDurationMs) {\n throw new RetryTimeoutError(maxDurationMs);\n }\n }\n }\n } finally {\n // Clean up listeners to prevent memory leaks\n for (const signal of signals) {\n signal?.removeEventListener(\"abort\", onAbort);\n }\n }\n\n throw lastError;\n }\n\n /**\n * Calculate the backoff delay for a given attempt.\n */\n protected calculateBackoff(\n attempt: number,\n options?: number | RetryBackoffOptions,\n ): number {\n if (typeof options === \"number\") {\n return options;\n }\n\n const initial = options?.initial ?? 200;\n const factor = options?.factor ?? 2;\n const max = options?.max ?? 10000;\n const useJitter = options?.jitter !== false;\n\n const exponential = initial * factor ** (attempt - 1);\n let delay = Math.min(exponential, max);\n\n if (useJitter) {\n // Add a random amount of jitter (e.g., up to 50% of the delay)\n delay = delay * (1 + Math.random() * 0.5);\n }\n\n return Math.floor(delay);\n }\n}\n","import {\n $inject,\n createPrimitive,\n KIND,\n Primitive,\n type PrimitiveArgs,\n} from \"alepha\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport type { RetryBackoffOptions } from \"../providers/RetryProvider.ts\";\nimport { RetryProvider } from \"../providers/RetryProvider.ts\";\n\n/**\n * Creates a function that automatically retries a handler upon failure,\n * with support for exponential backoff, max duration, and cancellation.\n */\nexport const $retry = <T extends (...args: any[]) => any>(\n options: RetryPrimitiveOptions<T>,\n): RetryPrimitiveFn<T> => {\n const instance = createPrimitive(RetryPrimitive, options);\n const fn = (...args: Parameters<T>) => instance.run(...args);\n return Object.setPrototypeOf(fn, instance) as RetryPrimitiveFn<T>;\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface RetryPrimitiveOptions<T extends (...args: any[]) => any> {\n /**\n * The function to retry.\n */\n handler: T;\n\n /**\n * The maximum number of attempts.\n *\n * @default 3\n */\n max?: number;\n\n /**\n * The backoff strategy for delays between retries.\n * Can be a fixed number (in ms) or a configuration object for exponential backoff.\n *\n * @default { initial: 200, factor: 2, jitter: true }\n */\n backoff?: number | RetryBackoffOptions;\n\n /**\n * An overall time limit for all retry attempts combined.\n *\n * e.g., `[5, 'seconds']`\n */\n maxDuration?: DurationLike;\n\n /**\n * A function that determines if a retry should be attempted based on the error.\n *\n * @default (error) => true (retries on any error)\n */\n when?: (error: Error) => boolean;\n\n /**\n * A custom callback for when a retry attempt fails.\n * This is called before the delay.\n */\n onError?: (error: Error, attempt: number, ...args: Parameters<T>) => void;\n\n /**\n * An AbortSignal to allow for external cancellation of the retry loop.\n */\n signal?: AbortSignal;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class RetryPrimitive<\n T extends (...args: any[]) => any,\n> extends Primitive<RetryPrimitiveOptions<T>> {\n protected readonly retryProvider = $inject(RetryProvider);\n protected appAbortController?: AbortController;\n\n constructor(args: PrimitiveArgs<RetryPrimitiveOptions<T>>) {\n super(args);\n\n this.alepha.events.on(\"stop\", () => {\n this.appAbortController?.abort();\n });\n }\n\n async run(...args: Parameters<T>): Promise<ReturnType<T>> {\n // Nov 25: Cloudflare does not like 'new AbortController' outside main handler, we can't pre-create it in the constructor.\n this.appAbortController ??= new AbortController();\n\n return this.retryProvider.retry(\n {\n ...this.options,\n additionalSignal: this.appAbortController.signal,\n },\n ...args,\n );\n }\n}\n\nexport interface RetryPrimitiveFn<T extends (...args: any[]) => any>\n extends RetryPrimitive<T> {\n (...args: Parameters<T>): Promise<ReturnType<T>>;\n}\n\n$retry[KIND] = RetryPrimitive;\n","import { $module } from \"alepha\";\nimport { $retry } from \"./primitives/$retry.ts\";\nimport { RetryProvider } from \"./providers/RetryProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./errors/RetryCancelError.ts\";\nexport * from \"./errors/RetryTimeoutError.ts\";\nexport * from \"./primitives/$retry.ts\";\nexport * from \"./providers/RetryProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Retry mechanism provider for Alepha applications.\n *\n * @see {@link RetryProvider}\n * @module alepha.retry\n */\nexport const AlephaRetry = $module({\n name: \"alepha.retry\",\n primitives: [$retry],\n services: [RetryProvider],\n});\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["wildcard: { route: T } | undefined","params: Record<string, string>"],"sources":["../../src/router/providers/RouterProvider.ts"],"sourcesContent":["import { AlephaError } from \"alepha\";\n\nexport abstract class RouterProvider<T extends Route = Route> {\n protected routePathRegex: RegExp = /^\\/[A-Za-z0-9._~!$&%'()*+,;=:@{}?/-]*$/;\n\n protected tree: Tree<T> = { children: {} };\n protected cache = new Map<string, RouteMatch<T>>();\n\n public match(path: string): RouteMatch<T> {\n if (this.cache.has(path)) {\n return this.cache.get(path)!;\n }\n const result = this.mapParams(this.createRouteMatch(path));\n this.cache.set(path, result);\n return result;\n }\n\n protected test(path: string): void {\n if (!this.routePathRegex.test(path)) {\n throw new AlephaError(`Route '${path}' is not valid`);\n }\n }\n\n protected push(route: T): void {\n const path = route.path.replaceAll(\"//\", \"/\");\n\n this.test(path);\n\n const parts = this.createParts(path);\n\n let cursor = this.tree;\n for (let i = 0; i < parts.length; i++) {\n const isLast = i === parts.length - 1;\n let part = parts[i].toLowerCase(); // url is case-insensitive\n if (part === \"*\" && isLast) {\n cursor.wildcard = { route };\n break;\n }\n\n if (part.includes(\"*\")) {\n throw new AlephaError(`Route '${path}' has an invalid wildcard syntax`);\n }\n\n if (part.includes(\"{\") || part.includes(\"}\")) {\n if (part.startsWith(\"{\") && part.endsWith(\"}\")) {\n part = `:${part.slice(1, -1)}`; // convert {param} to :param\n } else {\n throw new AlephaError(`Route '${path}' has an invalid param syntax`);\n }\n }\n\n if (part.startsWith(\":\")) {\n const name = parts[i].slice(1).replaceAll(\"}\", \"\");\n if (!name) {\n throw new AlephaError(`Route '${path}' has an empty param name`);\n }\n if (!cursor.param) {\n cursor.param = { name, children: {} };\n } else if (cursor.param.name !== name) {\n // damn, 2 url params with different names\n // got this case with /customers/:id and /customers/:userId/payments\n route.mapParams ??= {};\n route.mapParams[cursor.param.name] = name;\n }\n\n if (isLast) {\n cursor.param.route = route;\n }\n\n cursor = cursor.param;\n continue;\n }\n\n if (!cursor.children[part]) {\n cursor.children[part] = { children: {} };\n }\n\n if (isLast) {\n cursor.children[part].route = route;\n }\n\n cursor = cursor.children[part];\n }\n }\n\n protected createRouteMatch(path: string): RouteMatch<T> {\n if (path[0] !== \"/\") {\n throw new AlephaError(`Path '${path}' must start with \"/\"`);\n }\n\n const parts = this.createParts(path);\n\n let cursor = this.tree;\n let wildcard: { route: T } | undefined;\n const params: Record<string, string> = {};\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i].toLowerCase(); // url is case-insensitive\n if (cursor.children[part]) {\n if (cursor.wildcard) {\n wildcard = cursor.wildcard;\n }\n cursor = cursor.children[part];\n } else if (cursor.param) {\n if (cursor.wildcard) {\n wildcard = cursor.wildcard;\n }\n params[cursor.param.name] = parts[i];\n cursor = cursor.param;\n } else if (cursor.wildcard) {\n params[\"*\"] = parts.slice(i).join(\"/\");\n return { route: cursor.wildcard.route, params };\n } else {\n return { route: wildcard?.route, params };\n }\n }\n\n if (!cursor?.route) {\n // when \"/a/*\" - trigger if \"/a\"\n if (cursor.wildcard) {\n return { route: cursor.wildcard.route, params };\n }\n // return deep wildcard or nothing\n return { route: wildcard?.route, params };\n }\n\n return { route: cursor.route, params };\n }\n\n protected mapParams(match: RouteMatch<T>): RouteMatch<T> {\n if (match.route?.mapParams && match.params) {\n for (const [key, value] of Object.entries(match.route.mapParams)) {\n if (match.params[key]) {\n match.params[value] = match.params[key];\n delete match.params[key];\n }\n }\n }\n\n return match;\n }\n\n protected createParts(path: string): string[] {\n let pathname = path.split(\"?\")[0].replaceAll(\"//\", \"/\");\n\n // remove trailing slash\n if (pathname.endsWith(\"/\") && pathname.length > 1) {\n pathname = pathname.slice(0, -1);\n }\n\n return pathname.split(\"/\").slice(1);\n }\n}\n\nexport interface RouteMatch<T extends Route> {\n route?: T;\n params?: Record<string, string>;\n}\n\nexport interface Route {\n path: string;\n\n /**\n * Rename a param in the route.\n * This is automatically filled when you have scenarios like:\n * `/customers/:id` and `/customers/:userId/payments`\n *\n * In this case, `:id` will be renamed to `:userId` in the second route.\n */\n mapParams?: Record<string, string>;\n}\n\nexport interface Tree<T extends Route> {\n route?: T;\n children: {\n [key: string]: Tree<T>;\n };\n param?: {\n route?: T;\n name: string;\n children: {\n [key: string]: Tree<T>;\n };\n };\n wildcard?: {\n route: T;\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/router/providers/RouterProvider.ts"],"sourcesContent":["import { AlephaError } from \"alepha\";\n\nexport abstract class RouterProvider<T extends Route = Route> {\n protected routePathRegex: RegExp = /^\\/[A-Za-z0-9._~!$&%'()*+,;=:@{}?/-]*$/;\n\n protected tree: Tree<T> = { children: {} };\n protected cache = new Map<string, RouteMatch<T>>();\n\n public match(path: string): RouteMatch<T> {\n if (this.cache.has(path)) {\n return this.cache.get(path)!;\n }\n const result = this.mapParams(this.createRouteMatch(path));\n this.cache.set(path, result);\n return result;\n }\n\n protected test(path: string): void {\n if (!this.routePathRegex.test(path)) {\n throw new AlephaError(`Route '${path}' is not valid`);\n }\n }\n\n protected push(route: T): void {\n const path = route.path.replaceAll(\"//\", \"/\");\n\n this.test(path);\n\n const parts = this.createParts(path);\n\n let cursor = this.tree;\n for (let i = 0; i < parts.length; i++) {\n const isLast = i === parts.length - 1;\n let part = parts[i].toLowerCase(); // url is case-insensitive\n if (part === \"*\" && isLast) {\n cursor.wildcard = { route };\n break;\n }\n\n if (part.includes(\"*\")) {\n throw new AlephaError(`Route '${path}' has an invalid wildcard syntax`);\n }\n\n if (part.includes(\"{\") || part.includes(\"}\")) {\n if (part.startsWith(\"{\") && part.endsWith(\"}\")) {\n part = `:${part.slice(1, -1)}`; // convert {param} to :param\n } else {\n throw new AlephaError(`Route '${path}' has an invalid param syntax`);\n }\n }\n\n if (part.startsWith(\":\")) {\n const name = parts[i].slice(1).replaceAll(\"}\", \"\");\n if (!name) {\n throw new AlephaError(`Route '${path}' has an empty param name`);\n }\n if (!cursor.param) {\n cursor.param = { name, children: {} };\n } else if (cursor.param.name !== name) {\n // damn, 2 url params with different names\n // got this case with /customers/:id and /customers/:userId/payments\n route.mapParams ??= {};\n route.mapParams[cursor.param.name] = name;\n }\n\n if (isLast) {\n cursor.param.route = route;\n }\n\n cursor = cursor.param;\n continue;\n }\n\n if (!cursor.children[part]) {\n cursor.children[part] = { children: {} };\n }\n\n if (isLast) {\n cursor.children[part].route = route;\n }\n\n cursor = cursor.children[part];\n }\n }\n\n protected createRouteMatch(path: string): RouteMatch<T> {\n if (path[0] !== \"/\") {\n throw new AlephaError(`Path '${path}' must start with \"/\"`);\n }\n\n const parts = this.createParts(path);\n\n let cursor = this.tree;\n let wildcard: { route: T } | undefined;\n const params: Record<string, string> = {};\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i].toLowerCase(); // url is case-insensitive\n if (cursor.children[part]) {\n if (cursor.wildcard) {\n wildcard = cursor.wildcard;\n }\n cursor = cursor.children[part];\n } else if (cursor.param) {\n if (cursor.wildcard) {\n wildcard = cursor.wildcard;\n }\n params[cursor.param.name] = parts[i];\n cursor = cursor.param;\n } else if (cursor.wildcard) {\n params[\"*\"] = parts.slice(i).join(\"/\");\n return { route: cursor.wildcard.route, params };\n } else {\n return { route: wildcard?.route, params };\n }\n }\n\n if (!cursor?.route) {\n // when \"/a/*\" - trigger if \"/a\"\n if (cursor.wildcard) {\n return { route: cursor.wildcard.route, params };\n }\n // return deep wildcard or nothing\n return { route: wildcard?.route, params };\n }\n\n return { route: cursor.route, params };\n }\n\n protected mapParams(match: RouteMatch<T>): RouteMatch<T> {\n if (match.route?.mapParams && match.params) {\n for (const [key, value] of Object.entries(match.route.mapParams)) {\n if (match.params[key]) {\n match.params[value] = match.params[key];\n delete match.params[key];\n }\n }\n }\n\n return match;\n }\n\n protected createParts(path: string): string[] {\n let pathname = path.split(\"?\")[0].replaceAll(\"//\", \"/\");\n\n // remove trailing slash\n if (pathname.endsWith(\"/\") && pathname.length > 1) {\n pathname = pathname.slice(0, -1);\n }\n\n return pathname.split(\"/\").slice(1);\n }\n}\n\nexport interface RouteMatch<T extends Route> {\n route?: T;\n params?: Record<string, string>;\n}\n\nexport interface Route {\n path: string;\n\n /**\n * Rename a param in the route.\n * This is automatically filled when you have scenarios like:\n * `/customers/:id` and `/customers/:userId/payments`\n *\n * In this case, `:id` will be renamed to `:userId` in the second route.\n */\n mapParams?: Record<string, string>;\n}\n\nexport interface Tree<T extends Route> {\n route?: T;\n children: {\n [key: string]: Tree<T>;\n };\n param?: {\n route?: T;\n name: string;\n children: {\n [key: string]: Tree<T>;\n };\n };\n wildcard?: {\n route: T;\n };\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import * as alepha4 from "alepha";
1
+ import * as alepha1 from "alepha";
2
2
  import { Alepha, Async, KIND, Primitive, Static } from "alepha";
3
3
  import * as alepha_lock0 from "alepha/lock";
4
4
  import { DateTime, DateTimeProvider, DurationLike } from "alepha/datetime";
@@ -23,8 +23,8 @@ declare class CronProvider {
23
23
  protected readonly log: alepha_logger0.Logger;
24
24
  protected readonly cronJobs: Array<CronJob>;
25
25
  getCronJobs(): Array<CronJob>;
26
- protected readonly start: alepha4.HookPrimitive<"start">;
27
- protected readonly stop: alepha4.HookPrimitive<"stop">;
26
+ protected readonly start: alepha1.HookPrimitive<"start">;
27
+ protected readonly stop: alepha1.HookPrimitive<"stop">;
28
28
  protected boot(name: string | CronJob): void;
29
29
  abort(name: string | CronJob): void;
30
30
  /**
@@ -87,8 +87,8 @@ type SchedulerPrimitiveOptions = {
87
87
  */
88
88
  lock?: boolean;
89
89
  };
90
- declare const envSchema: alepha4.TObject<{
91
- SCHEDULER_PREFIX: alepha4.TOptional<alepha4.TString>;
90
+ declare const envSchema: alepha1.TObject<{
91
+ SCHEDULER_PREFIX: alepha1.TOptional<alepha1.TString>;
92
92
  }>;
93
93
  declare module "alepha" {
94
94
  interface Env extends Partial<Static<typeof envSchema>> {}
@@ -139,7 +139,7 @@ declare module "alepha" {
139
139
  * @see {@link $scheduler}
140
140
  * @module alepha.scheduler
141
141
  */
142
- declare const AlephaScheduler: alepha4.Service<alepha4.Module>;
142
+ declare const AlephaScheduler: alepha1.Service<alepha1.Module>;
143
143
  //#endregion
144
144
  export { $scheduler, AlephaScheduler, CRON, CronJob, CronProvider, SchedulerHandlerArguments, SchedulerPrimitive, SchedulerPrimitiveOptions };
145
145
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["cron: CronJob"],"sources":["../../src/scheduler/providers/CronProvider.ts","../../src/scheduler/primitives/$scheduler.ts","../../src/scheduler/constants/CRON.ts","../../src/scheduler/index.ts"],"sourcesContent":["import { $hook, $inject, Alepha } from \"alepha\";\nimport { type DateTime, DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { type Cron, parseCronExpression } from \"cron-schedule\";\n\nexport class CronProvider {\n protected readonly dt = $inject(DateTimeProvider);\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly cronJobs: Array<CronJob> = [];\n\n public getCronJobs(): Array<CronJob> {\n return this.cronJobs;\n }\n\n protected readonly start = $hook({\n on: \"start\",\n handler: () => {\n for (const cron of this.cronJobs) {\n if (!cron.running) {\n cron.running = true;\n this.log.debug(\n `Starting cron task '${cron.name}' with '${cron.expression}'`,\n );\n this.run(cron);\n }\n }\n },\n });\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: () => {\n for (const cron of this.cronJobs) {\n this.abort(cron);\n }\n },\n });\n\n protected boot(name: string | CronJob) {\n const cron =\n typeof name === \"string\"\n ? this.cronJobs.find((c) => c.name === name)\n : name;\n\n if (!cron) {\n return;\n }\n\n cron.running = true;\n\n this.log.debug(\n `Starting cron task '${cron.name}' with '${cron.expression}'`,\n );\n\n this.run(cron);\n }\n\n public abort(name: string | CronJob): void {\n const cron =\n typeof name === \"string\"\n ? this.cronJobs.find((c) => c.name === name)\n : name;\n\n if (!cron) {\n return;\n }\n\n cron.running = false;\n cron.abort.abort();\n this.log.debug(`Cron task '${cron.name}' stopped`);\n }\n\n /**\n * Registers a cron job.\n *\n * It's automatically done when using the `$scheduler` primitive but can also be used manually.\n */\n public createCronJob(\n name: string,\n expression: string,\n handler: (context: { now: DateTime }) => Promise<void>,\n start?: boolean,\n ): void {\n const cron: CronJob = {\n name,\n cron: parseCronExpression(expression),\n expression,\n handler,\n loop: true,\n abort: new AbortController(),\n };\n\n this.cronJobs.push(cron);\n\n if (start && this.alepha.isStarted()) {\n this.boot(cron);\n }\n }\n\n protected run(task: CronJob, now = this.dt.now()): void {\n if (!task.running) {\n return;\n }\n\n const [next] = task.cron.getNextDates(1, now.toDate());\n if (!next) {\n return;\n }\n\n const duration = next.getTime() - now.toDate().getTime();\n\n task.abort = new AbortController();\n\n this.dt\n .wait(duration, {\n now: now.valueOf(),\n signal: task.abort.signal,\n })\n .then(() => {\n if (!task.running) {\n this.log.trace(\"Cron task stopped before execution\");\n return;\n }\n\n this.log.trace(\"Running cron task\");\n\n task.handler({ now: this.dt.of(next) }).catch((err) => {\n if (task.onError) {\n task.onError(err);\n } else {\n this.log.error(\"Error in cron task:\", err);\n }\n });\n\n if (task.loop) {\n this.run(task, this.dt.of(next));\n }\n })\n .catch((err) => {\n this.log.warn(\"Issue during cron waiting timer\", err as Error);\n });\n }\n}\n\nexport interface CronJob {\n name: string;\n expression: string;\n handler: (context: { now: DateTime }) => Promise<void>;\n cron: Cron;\n loop: boolean;\n running?: boolean;\n onError?: (error: Error) => void;\n abort: AbortController;\n}\n","import {\n $env,\n $inject,\n Alepha,\n type Async,\n createPrimitive,\n KIND,\n Primitive,\n type Static,\n t,\n} from \"alepha\";\nimport {\n type DateTime,\n DateTimeProvider,\n type DurationLike,\n} from \"alepha/datetime\";\nimport { $lock } from \"alepha/lock\";\nimport { $logger } from \"alepha/logger\";\nimport { CronProvider } from \"../providers/CronProvider.ts\";\n\n/**\n * Scheduler primitive.\n */\nexport const $scheduler = (\n options: SchedulerPrimitiveOptions,\n): SchedulerPrimitive => {\n return createPrimitive(SchedulerPrimitive, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport type SchedulerPrimitiveOptions = {\n /**\n * Function to run on schedule.\n */\n handler: (args: SchedulerHandlerArguments) => Async<void>;\n\n /**\n * Name of the scheduler. Defaults to the function name.\n */\n name?: string;\n\n /**\n * Optional description of the scheduler.\n */\n description?: string;\n\n /**\n * Cron expression or interval to run the scheduler.\n */\n cron?: string;\n\n /**\n * Cron expression or interval to run the scheduler.\n */\n interval?: DurationLike;\n\n /**\n * If true, the scheduler will be locked and only one instance will run at a time.\n * You probably need to import {@link AlephaLockRedis} for distributed locking.\n *\n * @default true\n */\n lock?: boolean;\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nconst envSchema = t.object({\n SCHEDULER_PREFIX: t.optional(\n t.text({\n description: \"Prefix store key\",\n }),\n ),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\nexport class SchedulerPrimitive extends Primitive<SchedulerPrimitiveOptions> {\n protected readonly log = $logger();\n protected readonly env = $env(envSchema);\n protected readonly alepha = $inject(Alepha);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly cronProvider = $inject(CronProvider);\n\n public get name(): string {\n return (\n this.options.name ??\n `${this.config.service.name}.${this.config.propertyKey}`\n );\n }\n\n protected onInit() {\n if (this.options.interval) {\n this.dateTimeProvider.createInterval(\n () => this.trigger(),\n this.options.interval,\n );\n }\n if (this.options.cron) {\n this.cronProvider.createCronJob(this.name, this.options.cron, () =>\n this.trigger(),\n );\n }\n }\n\n public async trigger(): Promise<void> {\n if (!this.alepha.isStarted()) {\n return;\n }\n\n const context = this.alepha.context.createContextId();\n\n await this.alepha.context.run(\n async () => {\n try {\n const now = this.dateTimeProvider.now();\n\n await this.alepha.events.emit(\"scheduler:begin\", {\n name: this.name,\n now,\n context,\n });\n\n if (this.options.lock !== false) {\n await this.schedulerLock.run({ now });\n } else {\n await this.options.handler({ now });\n }\n\n await this.alepha.events.emit(\n \"scheduler:success\",\n {\n name: this.name,\n context,\n },\n {\n catch: true,\n },\n );\n } catch (error) {\n await this.alepha.events.emit(\n \"scheduler:error\",\n {\n name: this.name,\n error: error as Error,\n context,\n },\n {\n catch: true,\n },\n );\n\n this.log.error(\"Error running scheduler:\", error);\n }\n\n await this.alepha.events.emit(\n \"scheduler:end\",\n {\n name: this.name,\n context,\n },\n {\n catch: true,\n },\n );\n },\n {\n context,\n },\n );\n }\n\n protected schedulerLock = $lock({\n name: () => {\n const prefix = this.env.SCHEDULER_PREFIX\n ? `${this.env.SCHEDULER_PREFIX}:`\n : \"\";\n return `${prefix}scheduler:${this.name}`;\n },\n handler: async (args: SchedulerHandlerArguments) => {\n await this.options.handler(args);\n },\n });\n}\n\n$scheduler[KIND] = SchedulerPrimitive;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface SchedulerHandlerArguments {\n now: DateTime;\n}\n","export const CRON = {\n EVERY_MINUTE: \"* * * * *\",\n EVERY_5_MINUTES: \"*/5 * * * *\",\n EVERY_15_MINUTES: \"*/15 * * * *\",\n EVERY_30_MINUTES: \"*/30 * * * *\",\n EVERY_HOUR: \"0 * * * *\",\n EVERY_DAY_AT_MIDNIGHT: \"0 0 * * *\",\n};\n","import { $module } from \"alepha\";\nimport type { DateTime } from \"alepha/datetime\";\nimport { AlephaLock } from \"alepha/lock\";\nimport { $scheduler } from \"./primitives/$scheduler.ts\";\nimport { CronProvider } from \"./providers/CronProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./constants/CRON.ts\";\nexport * from \"./primitives/$scheduler.ts\";\nexport * from \"./providers/CronProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface Hooks {\n \"scheduler:begin\": {\n name: string;\n now: DateTime;\n context: string;\n };\n\n \"scheduler:success\": { name: string; context: string };\n\n \"scheduler:error\": {\n name: string;\n error: Error;\n context: string;\n };\n\n \"scheduler:end\": { name: string; context: string };\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Generic interface for scheduling tasks.\n *\n * @see {@link $scheduler}\n * @module alepha.scheduler\n */\nexport const AlephaScheduler = $module({\n name: \"alepha.scheduler\",\n primitives: [$scheduler],\n services: [AlephaLock, CronProvider],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/scheduler/providers/CronProvider.ts","../../src/scheduler/primitives/$scheduler.ts","../../src/scheduler/constants/CRON.ts","../../src/scheduler/index.ts"],"sourcesContent":["import { $hook, $inject, Alepha } from \"alepha\";\nimport { type DateTime, DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { type Cron, parseCronExpression } from \"cron-schedule\";\n\nexport class CronProvider {\n protected readonly dt = $inject(DateTimeProvider);\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly cronJobs: Array<CronJob> = [];\n\n public getCronJobs(): Array<CronJob> {\n return this.cronJobs;\n }\n\n protected readonly start = $hook({\n on: \"start\",\n handler: () => {\n for (const cron of this.cronJobs) {\n if (!cron.running) {\n cron.running = true;\n this.log.debug(\n `Starting cron task '${cron.name}' with '${cron.expression}'`,\n );\n this.run(cron);\n }\n }\n },\n });\n\n protected readonly stop = $hook({\n on: \"stop\",\n handler: () => {\n for (const cron of this.cronJobs) {\n this.abort(cron);\n }\n },\n });\n\n protected boot(name: string | CronJob) {\n const cron =\n typeof name === \"string\"\n ? this.cronJobs.find((c) => c.name === name)\n : name;\n\n if (!cron) {\n return;\n }\n\n cron.running = true;\n\n this.log.debug(\n `Starting cron task '${cron.name}' with '${cron.expression}'`,\n );\n\n this.run(cron);\n }\n\n public abort(name: string | CronJob): void {\n const cron =\n typeof name === \"string\"\n ? this.cronJobs.find((c) => c.name === name)\n : name;\n\n if (!cron) {\n return;\n }\n\n cron.running = false;\n cron.abort.abort();\n this.log.debug(`Cron task '${cron.name}' stopped`);\n }\n\n /**\n * Registers a cron job.\n *\n * It's automatically done when using the `$scheduler` primitive but can also be used manually.\n */\n public createCronJob(\n name: string,\n expression: string,\n handler: (context: { now: DateTime }) => Promise<void>,\n start?: boolean,\n ): void {\n const cron: CronJob = {\n name,\n cron: parseCronExpression(expression),\n expression,\n handler,\n loop: true,\n abort: new AbortController(),\n };\n\n this.cronJobs.push(cron);\n\n if (start && this.alepha.isStarted()) {\n this.boot(cron);\n }\n }\n\n protected run(task: CronJob, now = this.dt.now()): void {\n if (!task.running) {\n return;\n }\n\n const [next] = task.cron.getNextDates(1, now.toDate());\n if (!next) {\n return;\n }\n\n const duration = next.getTime() - now.toDate().getTime();\n\n task.abort = new AbortController();\n\n this.dt\n .wait(duration, {\n now: now.valueOf(),\n signal: task.abort.signal,\n })\n .then(() => {\n if (!task.running) {\n this.log.trace(\"Cron task stopped before execution\");\n return;\n }\n\n this.log.trace(\"Running cron task\");\n\n task.handler({ now: this.dt.of(next) }).catch((err) => {\n if (task.onError) {\n task.onError(err);\n } else {\n this.log.error(\"Error in cron task:\", err);\n }\n });\n\n if (task.loop) {\n this.run(task, this.dt.of(next));\n }\n })\n .catch((err) => {\n this.log.warn(\"Issue during cron waiting timer\", err as Error);\n });\n }\n}\n\nexport interface CronJob {\n name: string;\n expression: string;\n handler: (context: { now: DateTime }) => Promise<void>;\n cron: Cron;\n loop: boolean;\n running?: boolean;\n onError?: (error: Error) => void;\n abort: AbortController;\n}\n","import {\n $env,\n $inject,\n Alepha,\n type Async,\n createPrimitive,\n KIND,\n Primitive,\n type Static,\n t,\n} from \"alepha\";\nimport {\n type DateTime,\n DateTimeProvider,\n type DurationLike,\n} from \"alepha/datetime\";\nimport { $lock } from \"alepha/lock\";\nimport { $logger } from \"alepha/logger\";\nimport { CronProvider } from \"../providers/CronProvider.ts\";\n\n/**\n * Scheduler primitive.\n */\nexport const $scheduler = (\n options: SchedulerPrimitiveOptions,\n): SchedulerPrimitive => {\n return createPrimitive(SchedulerPrimitive, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport type SchedulerPrimitiveOptions = {\n /**\n * Function to run on schedule.\n */\n handler: (args: SchedulerHandlerArguments) => Async<void>;\n\n /**\n * Name of the scheduler. Defaults to the function name.\n */\n name?: string;\n\n /**\n * Optional description of the scheduler.\n */\n description?: string;\n\n /**\n * Cron expression or interval to run the scheduler.\n */\n cron?: string;\n\n /**\n * Cron expression or interval to run the scheduler.\n */\n interval?: DurationLike;\n\n /**\n * If true, the scheduler will be locked and only one instance will run at a time.\n * You probably need to import {@link AlephaLockRedis} for distributed locking.\n *\n * @default true\n */\n lock?: boolean;\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nconst envSchema = t.object({\n SCHEDULER_PREFIX: t.optional(\n t.text({\n description: \"Prefix store key\",\n }),\n ),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\nexport class SchedulerPrimitive extends Primitive<SchedulerPrimitiveOptions> {\n protected readonly log = $logger();\n protected readonly env = $env(envSchema);\n protected readonly alepha = $inject(Alepha);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly cronProvider = $inject(CronProvider);\n\n public get name(): string {\n return (\n this.options.name ??\n `${this.config.service.name}.${this.config.propertyKey}`\n );\n }\n\n protected onInit() {\n if (this.options.interval) {\n this.dateTimeProvider.createInterval(\n () => this.trigger(),\n this.options.interval,\n );\n }\n if (this.options.cron) {\n this.cronProvider.createCronJob(this.name, this.options.cron, () =>\n this.trigger(),\n );\n }\n }\n\n public async trigger(): Promise<void> {\n if (!this.alepha.isStarted()) {\n return;\n }\n\n const context = this.alepha.context.createContextId();\n\n await this.alepha.context.run(\n async () => {\n try {\n const now = this.dateTimeProvider.now();\n\n await this.alepha.events.emit(\"scheduler:begin\", {\n name: this.name,\n now,\n context,\n });\n\n if (this.options.lock !== false) {\n await this.schedulerLock.run({ now });\n } else {\n await this.options.handler({ now });\n }\n\n await this.alepha.events.emit(\n \"scheduler:success\",\n {\n name: this.name,\n context,\n },\n {\n catch: true,\n },\n );\n } catch (error) {\n await this.alepha.events.emit(\n \"scheduler:error\",\n {\n name: this.name,\n error: error as Error,\n context,\n },\n {\n catch: true,\n },\n );\n\n this.log.error(\"Error running scheduler:\", error);\n }\n\n await this.alepha.events.emit(\n \"scheduler:end\",\n {\n name: this.name,\n context,\n },\n {\n catch: true,\n },\n );\n },\n {\n context,\n },\n );\n }\n\n protected schedulerLock = $lock({\n name: () => {\n const prefix = this.env.SCHEDULER_PREFIX\n ? `${this.env.SCHEDULER_PREFIX}:`\n : \"\";\n return `${prefix}scheduler:${this.name}`;\n },\n handler: async (args: SchedulerHandlerArguments) => {\n await this.options.handler(args);\n },\n });\n}\n\n$scheduler[KIND] = SchedulerPrimitive;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface SchedulerHandlerArguments {\n now: DateTime;\n}\n","export const CRON = {\n EVERY_MINUTE: \"* * * * *\",\n EVERY_5_MINUTES: \"*/5 * * * *\",\n EVERY_15_MINUTES: \"*/15 * * * *\",\n EVERY_30_MINUTES: \"*/30 * * * *\",\n EVERY_HOUR: \"0 * * * *\",\n EVERY_DAY_AT_MIDNIGHT: \"0 0 * * *\",\n};\n","import { $module } from \"alepha\";\nimport type { DateTime } from \"alepha/datetime\";\nimport { AlephaLock } from \"alepha/lock\";\nimport { $scheduler } from \"./primitives/$scheduler.ts\";\nimport { CronProvider } from \"./providers/CronProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./constants/CRON.ts\";\nexport * from \"./primitives/$scheduler.ts\";\nexport * from \"./providers/CronProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha\" {\n interface Hooks {\n \"scheduler:begin\": {\n name: string;\n now: DateTime;\n context: string;\n };\n\n \"scheduler:success\": { name: string; context: string };\n\n \"scheduler:error\": {\n name: string;\n error: Error;\n context: string;\n };\n\n \"scheduler:end\": { name: string; context: string };\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Generic interface for scheduling tasks.\n *\n * @see {@link $scheduler}\n * @module alepha.scheduler\n */\nexport const AlephaScheduler = $module({\n name: \"alepha.scheduler\",\n primitives: [$scheduler],\n services: [AlephaLock, CronProvider],\n});\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import * as alepha15 from "alepha";
1
+ import * as alepha1 from "alepha";
2
2
  import { Alepha, KIND, Primitive, Static } from "alepha";
3
3
  import * as alepha_logger0 from "alepha/logger";
4
4
  import { DateTimeProvider, Duration, DurationLike } from "alepha/datetime";
@@ -7,15 +7,15 @@ import { UnauthorizedError } from "alepha/server";
7
7
  import { JWTVerifyOptions } from "jose/jwt/verify";
8
8
 
9
9
  //#region ../../src/security/schemas/userAccountInfoSchema.d.ts
10
- declare const userAccountInfoSchema: alepha15.TObject<{
11
- id: alepha15.TString;
12
- name: alepha15.TOptional<alepha15.TString>;
13
- email: alepha15.TOptional<alepha15.TString>;
14
- username: alepha15.TOptional<alepha15.TString>;
15
- picture: alepha15.TOptional<alepha15.TString>;
16
- sessionId: alepha15.TOptional<alepha15.TString>;
17
- organizations: alepha15.TOptional<alepha15.TArray<alepha15.TString>>;
18
- roles: alepha15.TOptional<alepha15.TArray<alepha15.TString>>;
10
+ declare const userAccountInfoSchema: alepha1.TObject<{
11
+ id: alepha1.TString;
12
+ name: alepha1.TOptional<alepha1.TString>;
13
+ email: alepha1.TOptional<alepha1.TString>;
14
+ username: alepha1.TOptional<alepha1.TString>;
15
+ picture: alepha1.TOptional<alepha1.TString>;
16
+ sessionId: alepha1.TOptional<alepha1.TString>;
17
+ organizations: alepha1.TOptional<alepha1.TArray<alepha1.TString>>;
18
+ roles: alepha1.TOptional<alepha1.TArray<alepha1.TString>>;
19
19
  }>;
20
20
  type UserAccount = Static<typeof userAccountInfoSchema>;
21
21
  //#endregion
@@ -64,24 +64,24 @@ interface UserAccountToken extends UserAccount {
64
64
  }
65
65
  //#endregion
66
66
  //#region ../../src/security/schemas/permissionSchema.d.ts
67
- declare const permissionSchema: alepha15.TObject<{
68
- name: alepha15.TString;
69
- group: alepha15.TOptional<alepha15.TString>;
70
- description: alepha15.TOptional<alepha15.TString>;
71
- method: alepha15.TOptional<alepha15.TString>;
72
- path: alepha15.TOptional<alepha15.TString>;
67
+ declare const permissionSchema: alepha1.TObject<{
68
+ name: alepha1.TString;
69
+ group: alepha1.TOptional<alepha1.TString>;
70
+ description: alepha1.TOptional<alepha1.TString>;
71
+ method: alepha1.TOptional<alepha1.TString>;
72
+ path: alepha1.TOptional<alepha1.TString>;
73
73
  }>;
74
74
  type Permission = Static<typeof permissionSchema>;
75
75
  //#endregion
76
76
  //#region ../../src/security/schemas/roleSchema.d.ts
77
- declare const roleSchema: alepha15.TObject<{
78
- name: alepha15.TString;
79
- description: alepha15.TOptional<alepha15.TString>;
80
- default: alepha15.TOptional<alepha15.TBoolean>;
81
- permissions: alepha15.TArray<alepha15.TObject<{
82
- name: alepha15.TString;
83
- ownership: alepha15.TOptional<alepha15.TBoolean>;
84
- exclude: alepha15.TOptional<alepha15.TArray<alepha15.TString>>;
77
+ declare const roleSchema: alepha1.TObject<{
78
+ name: alepha1.TString;
79
+ description: alepha1.TOptional<alepha1.TString>;
80
+ default: alepha1.TOptional<alepha1.TBoolean>;
81
+ permissions: alepha1.TArray<alepha1.TObject<{
82
+ name: alepha1.TString;
83
+ ownership: alepha1.TOptional<alepha1.TBoolean>;
84
+ exclude: alepha1.TOptional<alepha1.TArray<alepha1.TString>>;
85
85
  }>>;
86
86
  }>;
87
87
  type Role = Static<typeof roleSchema>;
@@ -154,8 +154,8 @@ interface JwtParseResult {
154
154
  //#endregion
155
155
  //#region ../../src/security/providers/SecurityProvider.d.ts
156
156
  declare const DEFAULT_APP_SECRET = "05759934015388327323179852515731";
157
- declare const envSchema: alepha15.TObject<{
158
- APP_SECRET: alepha15.TString;
157
+ declare const envSchema: alepha1.TObject<{
158
+ APP_SECRET: alepha1.TString;
159
159
  }>;
160
160
  declare module "alepha" {
161
161
  interface Env extends Partial<Static<typeof envSchema>> {}
@@ -179,7 +179,7 @@ declare class SecurityProvider {
179
179
  * The realms configured for the security provider.
180
180
  */
181
181
  protected readonly realms: Realm[];
182
- protected start: alepha15.HookPrimitive<"start">;
182
+ protected start: alepha1.HookPrimitive<"start">;
183
183
  /**
184
184
  * Adds a role to one or more realms.
185
185
  *
@@ -345,7 +345,7 @@ declare class PermissionPrimitive extends Primitive<PermissionPrimitiveOptions>
345
345
  /**
346
346
  * Check if the user has the permission.
347
347
  */
348
- can(user: UserAccount): boolean;
348
+ can(user?: UserAccount): boolean;
349
349
  }
350
350
  //#endregion
351
351
  //#region ../../src/security/primitives/$realm.d.ts
@@ -592,7 +592,7 @@ declare module "alepha" {
592
592
  * @see {@link $permission}
593
593
  * @module alepha.security
594
594
  */
595
- declare const AlephaSecurity: alepha15.Service<alepha15.Module>;
595
+ declare const AlephaSecurity: alepha1.Service<alepha1.Module>;
596
596
  //#endregion
597
597
  export { $permission, $realm, $role, $serviceAccount, AccessTokenResponse, AlephaSecurity, CreateTokenOptions, CryptoProvider, DEFAULT_APP_SECRET, ExtendedJWTPayload, InvalidCredentialsError, InvalidPermissionError, JwtParseResult, JwtProvider, JwtSignOptions, KeyLoader, KeyLoaderHolder, Oauth2ServiceAccountPrimitiveOptions, Permission, PermissionPrimitive, PermissionPrimitiveOptions, Realm, RealmExternal, RealmInternal, RealmPrimitive, RealmPrimitiveOptions, RealmSettings, Role, RolePrimitive, RolePrimitiveOptions, SecurityCheckResult, SecurityError, SecurityProvider, ServiceAccountPrimitive, ServiceAccountPrimitiveOptions, ServiceAccountStore, UserAccount, UserAccountToken, permissionSchema, roleSchema, userAccountInfoSchema };
598
598
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/security/schemas/userAccountInfoSchema.ts","../../src/security/errors/InvalidCredentialsError.ts","../../src/security/errors/InvalidPermissionError.ts","../../src/security/errors/SecurityError.ts","../../src/security/interfaces/UserAccountToken.ts","../../src/security/schemas/permissionSchema.ts","../../src/security/schemas/roleSchema.ts","../../src/security/providers/JwtProvider.ts","../../src/security/providers/SecurityProvider.ts","../../src/security/primitives/$permission.ts","../../src/security/primitives/$realm.ts","../../src/security/primitives/$role.ts","../../src/security/primitives/$serviceAccount.ts","../../src/security/providers/CryptoProvider.ts","../../src/security/index.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/security/schemas/userAccountInfoSchema.ts","../../src/security/errors/InvalidCredentialsError.ts","../../src/security/errors/InvalidPermissionError.ts","../../src/security/errors/SecurityError.ts","../../src/security/interfaces/UserAccountToken.ts","../../src/security/schemas/permissionSchema.ts","../../src/security/schemas/roleSchema.ts","../../src/security/providers/JwtProvider.ts","../../src/security/providers/SecurityProvider.ts","../../src/security/primitives/$permission.ts","../../src/security/primitives/$realm.ts","../../src/security/primitives/$role.ts","../../src/security/primitives/$serviceAccount.ts","../../src/security/providers/CryptoProvider.ts","../../src/security/index.ts"],"sourcesContent":[],"mappings":"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"}
@@ -559,7 +559,7 @@ var PermissionPrimitive = class extends Primitive {
559
559
  * Check if the user has the permission.
560
560
  */
561
561
  can(user) {
562
- if (!user.roles) return false;
562
+ if (!user?.roles) return false;
563
563
  return this.securityProvider.checkPermission(this, ...user.roles).isAuthorized;
564
564
  }
565
565
  };