alepha 0.19.1 → 0.19.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 (531) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +6 -9
  3. package/dist/api/audits/index.d.ts +378 -346
  4. package/dist/api/audits/index.d.ts.map +1 -1
  5. package/dist/api/files/index.d.ts +216 -184
  6. package/dist/api/files/index.d.ts.map +1 -1
  7. package/dist/api/jobs/index.d.ts +534 -502
  8. package/dist/api/jobs/index.d.ts.map +1 -1
  9. package/dist/api/jobs/index.js +13 -7
  10. package/dist/api/jobs/index.js.map +1 -1
  11. package/dist/api/keys/index.d.ts +202 -202
  12. package/dist/api/keys/index.d.ts.map +1 -1
  13. package/dist/api/notifications/index.d.ts +152 -152
  14. package/dist/api/notifications/index.d.ts.map +1 -1
  15. package/dist/api/organizations/index.browser.js +48 -0
  16. package/dist/api/organizations/index.browser.js.map +1 -0
  17. package/dist/api/organizations/index.d.ts +516 -0
  18. package/dist/api/organizations/index.d.ts.map +1 -0
  19. package/dist/api/organizations/index.js +202 -0
  20. package/dist/api/organizations/index.js.map +1 -0
  21. package/dist/api/parameters/index.d.ts +391 -358
  22. package/dist/api/parameters/index.d.ts.map +1 -1
  23. package/dist/api/parameters/index.js +5 -1
  24. package/dist/api/parameters/index.js.map +1 -1
  25. package/dist/api/users/index.browser.js +7 -5
  26. package/dist/api/users/index.browser.js.map +1 -1
  27. package/dist/api/users/index.d.ts +986 -931
  28. package/dist/api/users/index.d.ts.map +1 -1
  29. package/dist/api/users/index.js +160 -112
  30. package/dist/api/users/index.js.map +1 -1
  31. package/dist/api/verifications/index.d.ts +137 -137
  32. package/dist/api/verifications/index.d.ts.map +1 -1
  33. package/dist/api/verifications/index.js +2 -2
  34. package/dist/api/verifications/index.js.map +1 -1
  35. package/dist/batch/index.d.ts +6 -6
  36. package/dist/batch/index.d.ts.map +1 -1
  37. package/dist/billing/index.d.ts +1048 -0
  38. package/dist/billing/index.d.ts.map +1 -0
  39. package/dist/billing/index.js +713 -0
  40. package/dist/billing/index.js.map +1 -0
  41. package/dist/bin/index.js +0 -2
  42. package/dist/bin/index.js.map +1 -1
  43. package/dist/bucket/index.d.ts +10 -10
  44. package/dist/bucket/index.d.ts.map +1 -1
  45. package/dist/bucket/index.js +2 -2
  46. package/dist/bucket/index.js.map +1 -1
  47. package/dist/cache/core/index.d.ts +9 -9
  48. package/dist/cache/core/index.d.ts.map +1 -1
  49. package/dist/cache/core/index.js +2 -2
  50. package/dist/cache/core/index.js.map +1 -1
  51. package/dist/cache/core/index.workerd.js +2 -2
  52. package/dist/cache/core/index.workerd.js.map +1 -1
  53. package/dist/cache/redis/index.d.ts +6 -6
  54. package/dist/cache/redis/index.d.ts.map +1 -1
  55. package/dist/cache/redis/index.js +2 -2
  56. package/dist/cache/redis/index.js.map +1 -1
  57. package/dist/cli/config/index.d.ts +6 -18
  58. package/dist/cli/config/index.d.ts.map +1 -1
  59. package/dist/cli/config/index.js +5 -6
  60. package/dist/cli/config/index.js.map +1 -1
  61. package/dist/cli/core/index.d.ts +11811 -323
  62. package/dist/cli/core/index.d.ts.map +1 -1
  63. package/dist/cli/core/index.js +324 -98
  64. package/dist/cli/core/index.js.map +1 -1
  65. package/dist/cli/devtools/index.d.ts +50 -0
  66. package/dist/cli/devtools/index.d.ts.map +1 -0
  67. package/dist/cli/devtools/index.js +174 -0
  68. package/dist/cli/devtools/index.js.map +1 -0
  69. package/dist/cli/platform/index.d.ts +438 -542
  70. package/dist/cli/platform/index.d.ts.map +1 -1
  71. package/dist/cli/platform/index.js +46 -511
  72. package/dist/cli/platform/index.js.map +1 -1
  73. package/dist/cli/vendor/index.d.ts +201 -0
  74. package/dist/cli/vendor/index.d.ts.map +1 -0
  75. package/dist/cli/vendor/index.js +388 -0
  76. package/dist/cli/vendor/index.js.map +1 -0
  77. package/dist/command/index.d.ts +18 -18
  78. package/dist/command/index.d.ts.map +1 -1
  79. package/dist/command/index.js +2 -2
  80. package/dist/command/index.js.map +1 -1
  81. package/dist/core/index.browser.js +4 -4
  82. package/dist/core/index.browser.js.map +1 -1
  83. package/dist/core/index.d.ts +10 -10
  84. package/dist/core/index.d.ts.map +1 -1
  85. package/dist/core/index.js +8 -4
  86. package/dist/core/index.js.map +1 -1
  87. package/dist/core/index.native.js +8 -4
  88. package/dist/core/index.native.js.map +1 -1
  89. package/dist/core/index.workerd.js +8 -4
  90. package/dist/core/index.workerd.js.map +1 -1
  91. package/dist/crypto/index.d.ts +7 -7
  92. package/dist/crypto/index.d.ts.map +1 -1
  93. package/dist/datetime/index.d.ts +4 -4
  94. package/dist/datetime/index.d.ts.map +1 -1
  95. package/dist/email/brevo/index.d.ts +4 -4
  96. package/dist/email/brevo/index.d.ts.map +1 -1
  97. package/dist/email/core/index.d.ts +15 -11
  98. package/dist/email/core/index.d.ts.map +1 -1
  99. package/dist/email/core/index.js +12 -35
  100. package/dist/email/core/index.js.map +1 -1
  101. package/dist/email/smtp/index.d.ts +12 -12
  102. package/dist/email/smtp/index.d.ts.map +1 -1
  103. package/dist/email/smtp/index.js +7 -4
  104. package/dist/email/smtp/index.js.map +1 -1
  105. package/dist/fake/index.d.ts +4 -8
  106. package/dist/fake/index.d.ts.map +1 -1
  107. package/dist/fake/index.js +55 -889
  108. package/dist/fake/index.js.map +1 -1
  109. package/dist/lock/core/index.d.ts +13 -13
  110. package/dist/lock/core/index.d.ts.map +1 -1
  111. package/dist/lock/core/index.js +2 -2
  112. package/dist/lock/core/index.js.map +1 -1
  113. package/dist/lock/redis/index.d.ts +4 -4
  114. package/dist/lock/redis/index.d.ts.map +1 -1
  115. package/dist/logger/index.d.ts +16 -15
  116. package/dist/logger/index.d.ts.map +1 -1
  117. package/dist/logger/index.js +6 -3
  118. package/dist/logger/index.js.map +1 -1
  119. package/dist/mcp/index.d.ts +11 -11
  120. package/dist/mcp/index.d.ts.map +1 -1
  121. package/dist/mcp/index.js +2 -2
  122. package/dist/mcp/index.js.map +1 -1
  123. package/dist/orm/core/index.browser.js +11 -1
  124. package/dist/orm/core/index.browser.js.map +1 -1
  125. package/dist/orm/core/index.bun.js +78 -72
  126. package/dist/orm/core/index.bun.js.map +1 -1
  127. package/dist/orm/core/index.d.ts +103 -69
  128. package/dist/orm/core/index.d.ts.map +1 -1
  129. package/dist/orm/core/index.js +80 -70
  130. package/dist/orm/core/index.js.map +1 -1
  131. package/dist/orm/postgres/index.d.ts +19 -17
  132. package/dist/orm/postgres/index.d.ts.map +1 -1
  133. package/dist/queue/core/index.d.ts +14 -14
  134. package/dist/queue/core/index.d.ts.map +1 -1
  135. package/dist/queue/core/index.js +2 -2
  136. package/dist/queue/core/index.js.map +1 -1
  137. package/dist/queue/core/index.workerd.js +2 -2
  138. package/dist/queue/core/index.workerd.js.map +1 -1
  139. package/dist/queue/redis/index.d.ts +4 -4
  140. package/dist/queue/redis/index.d.ts.map +1 -1
  141. package/dist/queue/redis/index.js +2 -2
  142. package/dist/queue/redis/index.js.map +1 -1
  143. package/dist/react/auth/index.d.ts +9 -9
  144. package/dist/react/auth/index.d.ts.map +1 -1
  145. package/dist/react/core/index.d.ts +6 -6
  146. package/dist/react/core/index.d.ts.map +1 -1
  147. package/dist/react/core/index.js +5 -4
  148. package/dist/react/core/index.js.map +1 -1
  149. package/dist/react/form/index.d.ts +4 -4
  150. package/dist/react/form/index.d.ts.map +1 -1
  151. package/dist/react/head/index.d.ts +4 -4
  152. package/dist/react/head/index.d.ts.map +1 -1
  153. package/dist/react/i18n/index.d.ts +9 -9
  154. package/dist/react/i18n/index.d.ts.map +1 -1
  155. package/dist/react/intro/index.d.ts +2 -2
  156. package/dist/react/intro/index.d.ts.map +1 -1
  157. package/dist/react/intro/index.js +1 -1
  158. package/dist/react/intro/index.js.map +1 -1
  159. package/dist/react/router/index.browser.js +4 -5
  160. package/dist/react/router/index.browser.js.map +1 -1
  161. package/dist/react/router/index.d.ts +215 -215
  162. package/dist/react/router/index.d.ts.map +1 -1
  163. package/dist/react/router/index.js +6 -7
  164. package/dist/react/router/index.js.map +1 -1
  165. package/dist/react/testing/index.d.ts +2 -2
  166. package/dist/react/testing/index.d.ts.map +1 -1
  167. package/dist/react/testing/index.js +2 -4
  168. package/dist/react/testing/index.js.map +1 -1
  169. package/dist/redis/index.d.ts +19 -19
  170. package/dist/redis/index.d.ts.map +1 -1
  171. package/dist/retry/index.d.ts +4 -4
  172. package/dist/retry/index.d.ts.map +1 -1
  173. package/dist/scheduler/index.d.ts +13 -13
  174. package/dist/scheduler/index.d.ts.map +1 -1
  175. package/dist/scheduler/index.js +2 -2
  176. package/dist/scheduler/index.js.map +1 -1
  177. package/dist/scheduler/index.workerd.js +2 -2
  178. package/dist/scheduler/index.workerd.js.map +1 -1
  179. package/dist/security/index.browser.js +1 -1
  180. package/dist/security/index.browser.js.map +1 -1
  181. package/dist/security/index.d.ts +47 -47
  182. package/dist/security/index.d.ts.map +1 -1
  183. package/dist/security/index.js +9 -12
  184. package/dist/security/index.js.map +1 -1
  185. package/dist/server/auth/index.d.ts +170 -169
  186. package/dist/server/auth/index.d.ts.map +1 -1
  187. package/dist/server/auth/index.js +16 -2
  188. package/dist/server/auth/index.js.map +1 -1
  189. package/dist/server/cookies/index.d.ts +7 -7
  190. package/dist/server/cookies/index.d.ts.map +1 -1
  191. package/dist/server/core/index.d.ts +76 -76
  192. package/dist/server/core/index.d.ts.map +1 -1
  193. package/dist/server/core/index.js +23 -17
  194. package/dist/server/core/index.js.map +1 -1
  195. package/dist/server/cors/index.d.ts +13 -13
  196. package/dist/server/cors/index.d.ts.map +1 -1
  197. package/dist/server/cors/index.js +2 -2
  198. package/dist/server/cors/index.js.map +1 -1
  199. package/dist/server/etag/index.d.ts +9 -9
  200. package/dist/server/etag/index.d.ts.map +1 -1
  201. package/dist/server/health/index.d.ts +20 -20
  202. package/dist/server/health/index.d.ts.map +1 -1
  203. package/dist/server/links/index.browser.js +2 -2
  204. package/dist/server/links/index.browser.js.map +1 -1
  205. package/dist/server/links/index.d.ts +66 -66
  206. package/dist/server/links/index.d.ts.map +1 -1
  207. package/dist/server/links/index.js +4 -4
  208. package/dist/server/links/index.js.map +1 -1
  209. package/dist/server/metrics/index.d.ts +7 -7
  210. package/dist/server/metrics/index.d.ts.map +1 -1
  211. package/dist/server/proxy/index.d.ts +5 -5
  212. package/dist/server/proxy/index.d.ts.map +1 -1
  213. package/dist/server/rate-limit/index.d.ts +12 -12
  214. package/dist/server/rate-limit/index.d.ts.map +1 -1
  215. package/dist/server/rate-limit/index.js +2 -2
  216. package/dist/server/rate-limit/index.js.map +1 -1
  217. package/dist/server/static/index.d.ts +5 -5
  218. package/dist/server/static/index.d.ts.map +1 -1
  219. package/dist/server/swagger/index.d.ts +7 -7
  220. package/dist/server/swagger/index.d.ts.map +1 -1
  221. package/dist/server/swagger/index.js +2 -2
  222. package/dist/server/swagger/index.js.map +1 -1
  223. package/dist/sms/index.d.ts +11 -7
  224. package/dist/sms/index.d.ts.map +1 -1
  225. package/dist/sms/index.js +9 -15
  226. package/dist/sms/index.js.map +1 -1
  227. package/dist/system/index.d.ts +4 -4
  228. package/dist/system/index.d.ts.map +1 -1
  229. package/dist/topic/core/index.d.ts +6 -6
  230. package/dist/topic/core/index.d.ts.map +1 -1
  231. package/dist/topic/redis/index.d.ts +7 -7
  232. package/dist/topic/redis/index.d.ts.map +1 -1
  233. package/dist/topic/redis/index.js +2 -2
  234. package/dist/topic/redis/index.js.map +1 -1
  235. package/dist/websocket/index.d.ts +36 -36
  236. package/dist/websocket/index.d.ts.map +1 -1
  237. package/dist/websocket/index.js +2 -2
  238. package/dist/websocket/index.js.map +1 -1
  239. package/package.json +37 -15
  240. package/src/api/jobs/{services → __tests__}/JobService.spec.ts +1 -1
  241. package/src/api/jobs/providers/JobProvider.ts +13 -9
  242. package/src/api/keys/{services → __tests__}/ApiKeyService.spec.ts +1 -1
  243. package/src/api/organizations/__tests__/OrganizationService.spec.ts +193 -0
  244. package/src/api/organizations/controllers/AdminOrganizationController.ts +103 -0
  245. package/src/api/organizations/entities/organizations.ts +20 -0
  246. package/src/api/organizations/index.browser.ts +10 -0
  247. package/src/api/organizations/index.ts +31 -0
  248. package/src/api/organizations/schemas/createOrganizationSchema.ts +10 -0
  249. package/src/api/organizations/schemas/organizationQuerySchema.ts +10 -0
  250. package/src/api/organizations/schemas/organizationResourceSchema.ts +6 -0
  251. package/src/api/organizations/schemas/updateOrganizationSchema.ts +7 -0
  252. package/src/api/organizations/services/OrganizationService.ts +75 -0
  253. package/src/api/parameters/services/ParameterProvider.ts +6 -1
  254. package/src/api/users/{services → __tests__}/SessionService.spec.ts +67 -0
  255. package/src/api/users/{jobs → __tests__}/UserJobs.spec.ts +1 -1
  256. package/src/api/users/entities/users.ts +9 -3
  257. package/src/api/users/index.ts +23 -4
  258. package/src/api/users/primitives/$realm.ts +6 -4
  259. package/src/api/users/providers/RealmProvider.ts +1 -1
  260. package/src/api/users/services/RegistrationService.ts +1 -1
  261. package/src/api/users/services/SessionService.ts +92 -5
  262. package/src/api/users/services/UserService.ts +1 -1
  263. package/src/api/verifications/{jobs → __tests__}/VerificationJobs.spec.ts +4 -2
  264. package/src/api/verifications/parameters/VerificationParameters.ts +2 -2
  265. package/src/billing/__tests__/BillingService.spec.ts +136 -0
  266. package/src/billing/__tests__/PaymentMethodService.spec.ts +78 -0
  267. package/src/billing/controllers/AdminBillingController.ts +149 -0
  268. package/src/billing/controllers/BillingController.ts +108 -0
  269. package/src/billing/entities/paymentIntents.ts +34 -0
  270. package/src/billing/entities/paymentMethods.ts +24 -0
  271. package/src/billing/entities/refunds.ts +22 -0
  272. package/src/billing/errors/BillingError.ts +5 -0
  273. package/src/billing/index.ts +76 -0
  274. package/src/billing/providers/BillingProvider.ts +79 -0
  275. package/src/billing/providers/MemoryBillingProvider.ts +139 -0
  276. package/src/billing/schemas/intentSchemas.ts +60 -0
  277. package/src/billing/schemas/paymentMethodSchemas.ts +13 -0
  278. package/src/billing/schemas/refundSchemas.ts +6 -0
  279. package/src/billing/services/BillingService.ts +325 -0
  280. package/src/billing/services/PaymentMethodService.ts +82 -0
  281. package/src/bin/index.ts +0 -2
  282. package/src/bucket/providers/LocalFileStorageProvider.ts +2 -2
  283. package/src/cache/core/{primitives → __tests__}/$cache.middleware.spec.ts +1 -1
  284. package/src/cache/core/{providers → __tests__}/MemoryCacheProvider.spec.ts +1 -1
  285. package/src/cache/core/primitives/$cache.ts +2 -2
  286. package/src/cache/redis/providers/RedisCacheProvider.ts +2 -2
  287. package/src/cli/config/defineConfig.ts +17 -26
  288. package/src/cli/core/{services → __tests__}/ProjectScaffolder.spec.ts +1 -1
  289. package/src/cli/core/{commands/gen → __tests__}/changelog.spec.ts +1 -1
  290. package/src/cli/core/{commands → __tests__}/init.spec.ts +2 -8
  291. package/src/cli/core/atoms/devOptions.ts +0 -5
  292. package/src/cli/core/commands/build.ts +2 -2
  293. package/src/cli/core/commands/dev.ts +165 -30
  294. package/src/cli/core/commands/gen/changelog.ts +2 -2
  295. package/src/cli/core/commands/init.ts +2 -7
  296. package/src/cli/core/commands/verify.ts +0 -1
  297. package/src/cli/core/providers/AppEntryProvider.ts +2 -2
  298. package/src/cli/core/providers/ViteDevServerProvider.ts +99 -69
  299. package/src/cli/core/services/PackageManagerUtils.ts +8 -1
  300. package/src/cli/core/services/ProjectScaffolder.ts +23 -23
  301. package/src/cli/core/tasks/BuildClientTask.ts +8 -0
  302. package/src/cli/core/tasks/BuildServerTask.ts +17 -4
  303. package/src/cli/core/templates/agentMd.ts +14 -5
  304. package/src/cli/core/templates/alephaConfigTs.ts +0 -6
  305. package/src/cli/core/templates/webAdminDashboardTsx.ts +17 -0
  306. package/src/cli/core/templates/webAppRouterTs.ts +85 -2
  307. package/src/cli/devtools/atoms/devtoolsOptions.ts +26 -0
  308. package/src/cli/devtools/index.ts +214 -0
  309. package/src/cli/platform/{adapters → __tests__}/CloudflareAdapter.spec.ts +2 -2
  310. package/src/cli/platform/{providers → __tests__}/GitHubSecretStore.spec.ts +1 -1
  311. package/src/cli/platform/{services → __tests__}/NamingService.spec.ts +1 -1
  312. package/src/cli/platform/{providers → __tests__}/PlatformCacheProvider.spec.ts +1 -1
  313. package/src/cli/platform/{services → __tests__}/PlatformInspector.spec.ts +1 -1
  314. package/src/cli/platform/{services → __tests__}/PlatformOrchestrator.spec.ts +3 -3
  315. package/src/cli/platform/{services → __tests__}/SecretFilterService.spec.ts +1 -1
  316. package/src/cli/platform/{commands → __tests__}/SecretsCommand.spec.ts +1 -1
  317. package/src/cli/platform/{adapters → __tests__}/VercelAdapter.spec.ts +2 -2
  318. package/src/cli/platform/atoms/platformOptions.ts +2 -10
  319. package/src/cli/platform/commands/SecretsCommand.ts +2 -2
  320. package/src/cli/platform/commands/platform.ts +2 -11
  321. package/src/cli/platform/index.ts +55 -11
  322. package/src/cli/platform/services/PlatformInspector.ts +2 -2
  323. package/src/cli/platform/services/PlatformOrchestrator.ts +0 -9
  324. package/src/cli/vendor/__tests__/VendorService.spec.ts +407 -0
  325. package/src/cli/vendor/atoms/vendorOptions.ts +41 -0
  326. package/src/cli/vendor/commands/VendorCommand.ts +204 -0
  327. package/src/cli/vendor/index.ts +60 -0
  328. package/src/cli/vendor/services/VendorService.ts +338 -0
  329. package/src/command/{providers → __tests__}/CliProvider.spec.ts +1 -1
  330. package/src/command/{helpers → __tests__}/EnvUtils.spec.ts +1 -1
  331. package/src/command/providers/CliProvider.ts +2 -2
  332. package/src/core/Alepha.ts +10 -0
  333. package/src/core/{primitives → __tests__}/$atom.spec.ts +2 -2
  334. package/src/core/{primitives → __tests__}/$memoize.spec.ts +1 -1
  335. package/src/core/{primitives → __tests__}/$mode.spec.ts +1 -1
  336. package/src/core/{primitives → __tests__}/$pipeline.spec.ts +1 -1
  337. package/src/core/{primitives → __tests__}/$scope.spec.ts +2 -2
  338. package/src/core/{providers → __tests__}/KeylessJsonSchemaCodec.spec.ts +1 -1
  339. package/src/core/{providers → __tests__}/SchemaValidator.spec.ts +1 -1
  340. package/src/core/{helpers → __tests__}/jsonSchemaToTypeBox.spec.ts +1 -1
  341. package/src/core/index.shared.ts +1 -1
  342. package/src/core/primitives/{$use.ts → $state.ts} +4 -4
  343. package/src/crypto/{providers → __tests__}/BrowserCryptoProvider.browser.spec.ts +1 -1
  344. package/src/crypto/{providers → __tests__}/CryptoProvider.spec.ts +1 -1
  345. package/src/datetime/{primitives → __tests__}/$debounce.spec.ts +1 -1
  346. package/src/datetime/{primitives → __tests__}/$throttle.spec.ts +1 -1
  347. package/src/datetime/{primitives → __tests__}/$timeout.spec.ts +1 -1
  348. package/src/email/brevo/{providers → __tests__}/BrevoEmailProvider.spec.ts +1 -1
  349. package/src/email/core/{providers → __tests__}/LocalEmailProvider.spec.ts +39 -150
  350. package/src/email/core/providers/LocalEmailProvider.ts +13 -51
  351. package/src/email/smtp/providers/NodemailerEmailProvider.ts +2 -2
  352. package/src/lock/core/{primitives → __tests__}/$lock.middleware.spec.ts +1 -1
  353. package/src/lock/core/primitives/$lock.ts +2 -2
  354. package/src/logger/index.ts +16 -5
  355. package/src/mcp/transports/SseMcpTransport.ts +2 -2
  356. package/src/orm/__tests__/ModelBuilder-tests.ts +53 -0
  357. package/src/orm/__tests__/ModelBuilder.spec.ts +80 -0
  358. package/src/orm/__tests__/organization-tests.ts +200 -0
  359. package/src/orm/__tests__/organization.spec.ts +103 -0
  360. package/src/orm/core/{providers/drivers → __tests__}/BunSqliteProvider.bun.spec.ts +5 -2
  361. package/src/orm/core/constants/PG_SYMBOLS.ts +2 -0
  362. package/src/orm/core/index.shared.ts +1 -0
  363. package/src/orm/core/primitives/$entity.ts +31 -0
  364. package/src/orm/core/providers/DatabaseTypeProvider.ts +11 -0
  365. package/src/orm/core/providers/DrizzleKitProvider.ts +57 -106
  366. package/src/orm/core/providers/drivers/BunSqliteProvider.ts +2 -2
  367. package/src/orm/core/providers/drivers/NodeSqliteProvider.ts +3 -3
  368. package/src/orm/core/services/ModelBuilder.ts +11 -0
  369. package/src/orm/core/services/QueryManager.ts +16 -2
  370. package/src/orm/core/services/Repository.ts +70 -10
  371. package/src/orm/postgres/{providers → __tests__}/BunPostgresProvider.bun.spec.ts +1 -1
  372. package/src/queue/core/providers/WorkerProvider.ts +2 -2
  373. package/src/queue/redis/providers/RedisQueueProvider.ts +2 -2
  374. package/src/react/core/{hooks → __tests__}/useAction.browser.spec.tsx +1 -1
  375. package/src/react/core/hooks/useAction.ts +7 -6
  376. package/src/react/head/{providers → __tests__}/BrowserHeadProvider.browser.spec.ts +1 -1
  377. package/src/react/head/{helpers → __tests__}/SeoExpander.spec.ts +1 -1
  378. package/src/react/i18n/{providers → __tests__}/I18nProvider.spec.ts +1 -1
  379. package/src/react/i18n/{hooks → __tests__}/useI18n.browser.spec.tsx +1 -1
  380. package/src/react/intro/components/GettingStartedDevtoolsSlide.tsx +1 -1
  381. package/src/react/router/{providers → __tests__}/ReactBrowserProvider.browser.spec.ts +1 -1
  382. package/src/react/router/providers/ReactBrowserProvider.ts +2 -2
  383. package/src/react/router/providers/ReactPageProvider.ts +2 -2
  384. package/src/react/router/providers/ReactServerProvider.ts +3 -3
  385. package/src/redis/{providers → __tests__}/BunRedisProvider.bun.spec.ts +4 -4
  386. package/src/retry/{primitives → __tests__}/$retry.middleware.spec.ts +1 -1
  387. package/src/router/{TemplatedPathParser.spec.ts → __tests__/TemplatedPathParser.spec.ts} +1 -1
  388. package/src/scheduler/primitives/$scheduler.ts +2 -2
  389. package/src/security/{primitives → __tests__}/$secure-browser.spec.ts +1 -1
  390. package/src/security/{primitives → __tests__}/$secure.spec.ts +1 -1
  391. package/src/security/primitives/$issuer.ts +1 -1
  392. package/src/security/providers/JwtProvider.ts +6 -10
  393. package/src/security/providers/SecurityProvider.ts +6 -11
  394. package/src/security/schemas/userAccountInfoSchema.ts +3 -3
  395. package/src/server/auth/providers/ServerAuthProvider.ts +24 -2
  396. package/src/server/cookies/{services → __tests__}/CookieParser.spec.ts +1 -1
  397. package/src/server/core/{primitives → __tests__}/$circuit.spec.ts +1 -1
  398. package/src/server/core/{providers → __tests__}/NodeHttpServerProvider.spec.ts +1 -1
  399. package/src/server/core/{providers → __tests__}/ServerBodyParserProvider.spec.ts +31 -1
  400. package/src/server/core/{providers → __tests__}/ServerCompressProvider.spec.ts +1 -1
  401. package/src/server/core/{providers → __tests__}/ServerHelmetProvider.spec.ts +4 -1
  402. package/src/server/core/{providers → __tests__}/ServerMultipartProvider.spec.ts +1 -1
  403. package/src/server/core/{services → __tests__}/ServerRequestParser.spec.ts +1 -1
  404. package/src/server/core/primitives/$action.ts +2 -2
  405. package/src/server/core/primitives/$sse.ts +2 -2
  406. package/src/server/core/providers/ServerBodyParserProvider.ts +21 -12
  407. package/src/server/core/providers/ServerCompressProvider.ts +2 -2
  408. package/src/server/core/providers/ServerHelmetProvider.ts +2 -2
  409. package/src/server/core/providers/ServerMultipartProvider.ts +2 -2
  410. package/src/server/core/providers/ServerRouterProvider.ts +1 -5
  411. package/src/server/cors/{primitives → __tests__}/$cors.spec.ts +1 -1
  412. package/src/server/cors/providers/ServerCorsProvider.ts +2 -2
  413. package/src/server/links/{services → __tests__}/BatchCollector.spec.ts +1 -1
  414. package/src/server/links/providers/LinkProvider.ts +2 -2
  415. package/src/server/links/providers/RemotePrimitiveProvider.ts +2 -2
  416. package/src/server/links/providers/ServerLinksProvider.ts +2 -2
  417. package/src/server/rate-limit/{primitives → __tests__}/$rateLimit.spec.ts +1 -1
  418. package/src/server/rate-limit/providers/ServerRateLimitProvider.ts +2 -2
  419. package/src/server/swagger/providers/ServerSwaggerProvider.ts +2 -2
  420. package/src/sms/{providers → __tests__}/LocalSmsProvider.spec.ts +35 -29
  421. package/src/sms/providers/LocalSmsProvider.ts +13 -24
  422. package/src/system/{providers → __tests__}/MemoryFileSystemProvider.spec.ts +1 -1
  423. package/src/system/{providers → __tests__}/MemoryShellProvider.spec.ts +1 -1
  424. package/src/topic/redis/providers/RedisTopicProvider.ts +2 -2
  425. package/src/websocket/{services → __tests__}/RoomManager.spec.ts +1 -1
  426. package/src/websocket/providers/NodeWebSocketServerProvider.ts +2 -2
  427. package/src/cli/platform/adapters/DockerAdapter.spec.ts +0 -378
  428. package/src/cli/platform/adapters/DockerAdapter.ts +0 -417
  429. package/src/cli/platform/services/DockerComposeGenerator.spec.ts +0 -490
  430. package/src/cli/platform/services/DockerComposeGenerator.ts +0 -353
  431. package/src/cli/platform/services/DockerSshService.spec.ts +0 -47
  432. package/src/cli/platform/services/DockerSshService.ts +0 -61
  433. /package/src/api/audits/{primitives → __tests__}/$audit.spec.ts +0 -0
  434. /package/src/api/audits/{services → __tests__}/AuditService.spec.ts +0 -0
  435. /package/src/api/files/{controllers → __tests__}/AdminFileStatsController.spec.ts +0 -0
  436. /package/src/api/files/{controllers → __tests__}/FileController.spec.ts +0 -0
  437. /package/src/api/files/{jobs → __tests__}/FileJobs.spec.ts +0 -0
  438. /package/src/api/files/{services → __tests__}/FileService.spec.ts +0 -0
  439. /package/src/api/jobs/{primitives → __tests__}/$job-middleware.spec.ts +0 -0
  440. /package/src/api/parameters/{primitives → __tests__}/$parameter.spec.ts +0 -0
  441. /package/src/api/users/{primitives → __tests__}/$realm.spec.ts +0 -0
  442. /package/src/api/users/{controllers → __tests__}/AdminIdentityController.spec.ts +0 -0
  443. /package/src/api/users/{controllers → __tests__}/AdminSessionController.spec.ts +0 -0
  444. /package/src/api/users/{controllers → __tests__}/AdminUserController.spec.ts +0 -0
  445. /package/src/api/users/{services → __tests__}/CredentialService.spec.ts +0 -0
  446. /package/src/api/users/{providers → __tests__}/RealmProvider.spec.ts +0 -0
  447. /package/src/api/users/{services → __tests__}/RegistrationService.spec.ts +0 -0
  448. /package/src/batch/{primitives → __tests__}/$batch.spec.ts +0 -0
  449. /package/src/batch/{providers → __tests__}/BatchProvider.spec.ts +0 -0
  450. /package/src/bucket/{primitives → __tests__}/$bucket.spec.ts +0 -0
  451. /package/src/bucket/{providers → __tests__}/FileStorageProvider.spec.ts +0 -0
  452. /package/src/bucket/{providers → __tests__}/LocalFileStorageProvider.spec.ts +0 -0
  453. /package/src/bucket/{providers → __tests__}/MemoryFileStorageProvider.spec.ts +0 -0
  454. /package/src/cache/core/{primitives → __tests__}/$cache.spec.ts +0 -0
  455. /package/src/cache/redis/{providers → __tests__}/RedisCacheProvider.spec.ts +0 -0
  456. /package/src/command/{primitives → __tests__}/$command.spec.ts +0 -0
  457. /package/src/command/{helpers → __tests__}/Asker.spec.ts +0 -0
  458. /package/src/command/{helpers → __tests__}/Runner.spec.ts +0 -0
  459. /package/src/core/{primitives → __tests__}/$context.spec.ts +0 -0
  460. /package/src/core/{primitives → __tests__}/$env.spec.ts +0 -0
  461. /package/src/core/{primitives → __tests__}/$hook.spec.ts +0 -0
  462. /package/src/core/{primitives → __tests__}/$inject.spec.ts +0 -0
  463. /package/src/core/{primitives → __tests__}/$module.spec.ts +0 -0
  464. /package/src/core/{providers → __tests__}/CodecManager.spec.ts +0 -0
  465. /package/src/core/{providers → __tests__}/EventManager.spec.ts +0 -0
  466. /package/src/core/{providers → __tests__}/StateManager.spec.ts +0 -0
  467. /package/src/core/{providers → __tests__}/TypeProvider.spec.ts +0 -0
  468. /package/src/datetime/{primitives → __tests__}/$interval.spec.ts +0 -0
  469. /package/src/datetime/{providers → __tests__}/DateTimeProvider.spec.ts +0 -0
  470. /package/src/email/core/{primitives → __tests__}/$email.spec.ts +0 -0
  471. /package/src/fake/{providers → __tests__}/FakeProvider.spec.ts +0 -0
  472. /package/src/lock/core/{providers → __tests__}/MemoryLockProvider.spec.ts +0 -0
  473. /package/src/lock/redis/{providers → __tests__}/RedisLockProvider.spec.ts +0 -0
  474. /package/src/logger/{primitives → __tests__}/$logger.spec.ts +0 -0
  475. /package/src/logger/{services → __tests__}/Logger.spec.ts +0 -0
  476. /package/src/mcp/{primitives → __tests__}/$prompt.spec.ts +0 -0
  477. /package/src/mcp/{primitives → __tests__}/$resource.spec.ts +0 -0
  478. /package/src/mcp/{primitives → __tests__}/$tool.spec.ts +0 -0
  479. /package/src/mcp/{providers → __tests__}/McpServerProvider.spec.ts +0 -0
  480. /package/src/mcp/{helpers → __tests__}/jsonrpc.spec.ts +0 -0
  481. /package/src/orm/core/{helpers → __tests__}/parseQueryString.spec.ts +0 -0
  482. /package/src/queue/core/{primitives → __tests__}/$consumer.spec.ts +0 -0
  483. /package/src/queue/core/{providers → __tests__}/MemoryQueueProvider.spec.ts +0 -0
  484. /package/src/queue/core/{providers → __tests__}/WorkerProvider.spec.ts +0 -0
  485. /package/src/queue/redis/{providers → __tests__}/RedisQueueProvider.spec.ts +0 -0
  486. /package/src/react/form/{hooks → __tests__}/useForm.browser.spec.tsx +0 -0
  487. /package/src/react/head/{hooks → __tests__}/useHead.spec.tsx +0 -0
  488. /package/src/react/i18n/{components → __tests__}/Localize.spec.tsx +0 -0
  489. /package/src/react/router/{primitives → __tests__}/$page.browser.spec.tsx +0 -0
  490. /package/src/react/router/{primitives → __tests__}/$page.middleware.spec.tsx +0 -0
  491. /package/src/react/router/{primitives → __tests__}/$page.spec.tsx +0 -0
  492. /package/src/react/router/{providers → __tests__}/ReactPreloadProvider.spec.ts +0 -0
  493. /package/src/react/router/{providers → __tests__}/ReactServerProvider.spec.tsx +0 -0
  494. /package/src/react/router/{providers → __tests__}/ReactServerTemplateProvider.spec.ts +0 -0
  495. /package/src/retry/{primitives → __tests__}/$retry.spec.ts +0 -0
  496. /package/src/retry/{providers → __tests__}/RetryProvider.spec.ts +0 -0
  497. /package/src/router/{providers → __tests__}/RouterProvider.spec.ts +0 -0
  498. /package/src/security/{primitives → __tests__}/$issuer.spec.ts +0 -0
  499. /package/src/security/{primitives → __tests__}/$permission.spec.ts +0 -0
  500. /package/src/security/{primitives → __tests__}/$role.spec.ts +0 -0
  501. /package/src/security/{primitives → __tests__}/$serviceAccount.spec.ts +0 -0
  502. /package/src/security/{providers → __tests__}/SecurityProvider.spec.ts +0 -0
  503. /package/src/server/cookies/{providers → __tests__}/ServerCookiesProvider.spec.ts +0 -0
  504. /package/src/server/core/{primitives → __tests__}/$action.spec.ts +0 -0
  505. /package/src/server/core/{primitives → __tests__}/$middleware.spec.ts +0 -0
  506. /package/src/server/core/{primitives → __tests__}/$route.spec.ts +0 -0
  507. /package/src/server/core/{primitives → __tests__}/$sse.spec.ts +0 -0
  508. /package/src/server/core/{providers → __tests__}/BunHttpServerProvider.bun.spec.ts +0 -0
  509. /package/src/server/core/{services → __tests__}/HttpClient.spec.ts +0 -0
  510. /package/src/server/core/{providers → __tests__}/ServerLoggerProvider.spec.ts +0 -0
  511. /package/src/server/core/{services → __tests__}/UserAgentParser.spec.ts +0 -0
  512. /package/src/server/cors/{providers → __tests__}/ServerCorsProvider.spec.ts +0 -0
  513. /package/src/server/etag/{providers → __tests__}/ServerEtagProvider.spec.ts +0 -0
  514. /package/src/server/health/{providers → __tests__}/ServerHealthProvider.spec.ts +0 -0
  515. /package/src/server/links/{primitives → __tests__}/$remote.spec.ts +0 -0
  516. /package/src/server/links/{services → __tests__}/BatchEndpoint.spec.ts +0 -0
  517. /package/src/server/links/{providers → __tests__}/LinkProvider.spec.ts +0 -0
  518. /package/src/server/links/{providers → __tests__}/ServerLinksProvider.spec.ts +0 -0
  519. /package/src/server/metrics/{providers → __tests__}/ServerMetricsProvider.spec.ts +0 -0
  520. /package/src/server/proxy/{primitives → __tests__}/$proxy.spec.ts +0 -0
  521. /package/src/server/rate-limit/{providers → __tests__}/ServerRateLimitProvider.spec.ts +0 -0
  522. /package/src/server/static/{primitives → __tests__}/$serve.spec.ts +0 -0
  523. /package/src/server/swagger/{primitives → __tests__}/$swagger.spec.ts +0 -0
  524. /package/src/sms/{primitives → __tests__}/$sms.spec.ts +0 -0
  525. /package/src/sms/{providers → __tests__}/MemorySmsProvider.spec.ts +0 -0
  526. /package/src/system/{services → __tests__}/FileDetector.spec.ts +0 -0
  527. /package/src/system/{providers → __tests__}/NodeFileSystemProvider.spec.ts +0 -0
  528. /package/src/topic/core/{primitives → __tests__}/$subscriber.spec.ts +0 -0
  529. /package/src/topic/core/{providers → __tests__}/MemoryTopicProvider.spec.ts +0 -0
  530. /package/src/topic/redis/{providers → __tests__}/RedisTopicProvider.spec.ts +0 -0
  531. /package/src/websocket/{primitives → __tests__}/$channel.spec.ts +0 -0
@@ -63,7 +63,7 @@ export const $realm = (options: RealmOptions = {}): RealmPrimitive => {
63
63
 
64
64
  // Merge features with defaults
65
65
  const features: RealmFeatures = {
66
- sessionPurge: false,
66
+ jobs: false,
67
67
  notifications: false,
68
68
  apiKeys: false,
69
69
  parameters: false,
@@ -95,7 +95,7 @@ export const $realm = (options: RealmOptions = {}): RealmPrimitive => {
95
95
  alepha.with(UserAudits);
96
96
  }
97
97
 
98
- if (features.sessionPurge) {
98
+ if (features.jobs) {
99
99
  alepha.with(UserJobs);
100
100
  }
101
101
 
@@ -227,11 +227,13 @@ export const $realm = (options: RealmOptions = {}): RealmPrimitive => {
227
227
 
228
228
  export interface RealmFeatures {
229
229
  /**
230
- * Enable session purge functionality for cleaning up expired sessions.
230
+ * Will enable Job module.
231
+ *
232
+ * - Enable session purge functionality for cleaning up expired sessions.
231
233
  *
232
234
  * @default false
233
235
  */
234
- sessionPurge?: boolean;
236
+ jobs?: boolean;
235
237
 
236
238
  /**
237
239
  * Enable notification system for password reset, verification emails, etc.
@@ -43,7 +43,7 @@ export class RealmProvider {
43
43
 
44
44
  // Merge features with defaults
45
45
  const features: RealmFeatures = {
46
- sessionPurge: false,
46
+ jobs: false,
47
47
  notifications: false,
48
48
  apiKeys: false,
49
49
  parameters: false,
@@ -339,7 +339,7 @@ export class RegistrationService {
339
339
 
340
340
  if (body.username) {
341
341
  const existingUser = await userRepository.findOne({
342
- where: { username: { eq: body.username } },
342
+ where: { username: { ilike: body.username } },
343
343
  });
344
344
  if (existingUser) {
345
345
  this.log.debug("Username already taken", { username: body.username });
@@ -9,7 +9,7 @@ import {
9
9
  InvalidCredentialsError,
10
10
  type UserAccount,
11
11
  } from "alepha/security";
12
- import { UnauthorizedError } from "alepha/server";
12
+ import { BadRequestError, UnauthorizedError } from "alepha/server";
13
13
  import type { OAuth2Profile } from "alepha/server/auth";
14
14
  import { $client } from "alepha/server/links";
15
15
  import { FileSystemProvider } from "alepha/system";
@@ -98,6 +98,78 @@ export class SessionService {
98
98
  return true;
99
99
  }
100
100
 
101
+ /**
102
+ * Generate a unique username from an OAuth profile.
103
+ *
104
+ * 1. Extract candidate from email prefix
105
+ * 2. Sanitize against realm's usernameRegExp (strip invalid chars, truncate)
106
+ * 3. Check case-insensitive uniqueness, append suffix (2, 3, ...) if taken
107
+ * 4. Fall back to "user" + random 6-char alphanumeric if all else fails
108
+ */
109
+ protected async generateUniqueUsername(
110
+ profile: OAuth2Profile,
111
+ realmSettings: any,
112
+ users: any,
113
+ ): Promise<string> {
114
+ const maxLength = 30;
115
+ const maxSuffixAttempts = 10;
116
+
117
+ // Extract candidate from email or profile name
118
+ let candidate = profile.email?.split("@")[0] ?? profile.name ?? "";
119
+
120
+ // Strip characters not allowed in usernames (keep alphanumeric, underscore, dot, hyphen)
121
+ candidate = candidate.replace(/[^a-zA-Z0-9_.-]/g, "");
122
+
123
+ // If realm has a custom regex, further sanitize
124
+ if (realmSettings?.usernameRegExp) {
125
+ try {
126
+ const regex = new RegExp(realmSettings.usernameRegExp);
127
+ if (!regex.test(candidate)) {
128
+ // Strip to basic alphanumeric as safe fallback
129
+ candidate = candidate.replace(/[^a-zA-Z0-9_]/g, "");
130
+ }
131
+ } catch {
132
+ // Invalid regex, continue with sanitized candidate
133
+ }
134
+ }
135
+
136
+ // Ensure minimum length
137
+ if (candidate.length < 3) {
138
+ candidate = `user${candidate}`;
139
+ }
140
+
141
+ // Truncate to leave room for suffix
142
+ candidate = candidate.slice(0, maxLength - 2);
143
+
144
+ // Check uniqueness (case-insensitive)
145
+ const isAvailable = async (name: string) => {
146
+ const existing = await users.findMany({
147
+ where: { username: { contains: name } },
148
+ limit: 1,
149
+ });
150
+ // Case-insensitive check
151
+ return !existing.some(
152
+ (u: any) => u.username?.toLowerCase() === name.toLowerCase(),
153
+ );
154
+ };
155
+
156
+ if (await isAvailable(candidate)) {
157
+ return candidate;
158
+ }
159
+
160
+ // Try with numeric suffix
161
+ for (let i = 2; i <= maxSuffixAttempts + 1; i++) {
162
+ const withSuffix = `${candidate}${i}`;
163
+ if (withSuffix.length <= maxLength && (await isAvailable(withSuffix))) {
164
+ return withSuffix;
165
+ }
166
+ }
167
+
168
+ // Final fallback: random username
169
+ const random = Math.random().toString(36).slice(2, 8);
170
+ return `user${random}`;
171
+ }
172
+
101
173
  /**
102
174
  * Random delay to prevent timing attacks (50-200ms)
103
175
  * Uses cryptographically secure random number generation
@@ -222,7 +294,7 @@ export class SessionService {
222
294
  throw new InvalidCredentialsError();
223
295
  }
224
296
  }
225
- where.username = username;
297
+ where.username = { ilike: username };
226
298
  } else if (settings.email !== "none" && isEmail) {
227
299
  where.email = username;
228
300
  } else if (settings.phoneNumber !== "none" && isPhone) {
@@ -596,12 +668,27 @@ export class SessionService {
596
668
  return existing;
597
669
  }
598
670
 
599
- // TODO: check usernames for uniqueness, add suffix if needed (e.g. john.doe1)
600
- // TODO: username must match a-zA-Z0-9._-
671
+ const realmSettings = await realm.getSettings();
672
+ const adminEmails = realmSettings?.adminEmails ?? [];
673
+ const isAdmin = profile.email && adminEmails.includes(profile.email);
674
+
675
+ if (realmSettings?.registrationAllowed === false && !isAdmin) {
676
+ this.log.warn("Registration not allowed for realm via OAuth2", {
677
+ provider,
678
+ userRealmName,
679
+ });
680
+ throw new BadRequestError("Account doesn't exist");
681
+ }
682
+
683
+ const username = await this.generateUniqueUsername(
684
+ profile,
685
+ realmSettings,
686
+ users,
687
+ );
601
688
 
602
689
  const user = await users.create({
603
690
  realm: realm.name,
604
- username: profile.email.split("@")[0],
691
+ username,
605
692
  email: profile.email,
606
693
  // we trust the OAuth2 provider
607
694
  emailVerified: true,
@@ -276,7 +276,7 @@ export class UserService {
276
276
  // Check for existing user based on provided unique fields
277
277
  if (data.username) {
278
278
  const existingUser = await this.users(userRealmName).findOne({
279
- where: { username: { eq: data.username } },
279
+ where: { username: { ilike: data.username } },
280
280
  });
281
281
 
282
282
  if (existingUser) {
@@ -23,6 +23,8 @@ describe("VerificationJobs", () => {
23
23
  const time = alepha.inject(DateTimeProvider);
24
24
  const jobs = alepha.inject(VerificationJobs);
25
25
 
26
+ await db.verifications.deleteMany({ target: { eq: "hello@mail.com" } });
27
+
26
28
  const entry = {
27
29
  type: "link",
28
30
  target: "hello@mail.com",
@@ -36,12 +38,12 @@ describe("VerificationJobs", () => {
36
38
 
37
39
  await db.verifications.create({
38
40
  ...entry,
39
- createdAt: time.now().subtract(1, "days").toISOString(),
41
+ createdAt: time.now().subtract(2, "days").toISOString(),
40
42
  });
41
43
 
42
44
  await db.verifications.create({
43
45
  ...entry,
44
- createdAt: time.now().subtract(2, "days").toISOString(),
46
+ createdAt: time.now().subtract(3, "days").toISOString(),
45
47
  });
46
48
 
47
49
  expect(await db.verifications.count()).toBe(3);
@@ -1,4 +1,4 @@
1
- import { $atom, $use, type Static } from "alepha";
1
+ import { $atom, $state, type Static } from "alepha";
2
2
  import {
3
3
  type VerificationSettings,
4
4
  verificationSettingsSchema,
@@ -41,7 +41,7 @@ declare module "alepha" {
41
41
  // ---------------------------------------------------------------------------------------------------------------------
42
42
 
43
43
  export class VerificationParameters {
44
- protected readonly options = $use(verificationOptions);
44
+ protected readonly options = $state(verificationOptions);
45
45
 
46
46
  public get<K extends keyof VerificationSettings>(
47
47
  key: K,
@@ -0,0 +1,136 @@
1
+ import { Alepha } from "alepha";
2
+ import { AlephaOrmPostgres } from "alepha/orm/postgres";
3
+ import { describe, it } from "vitest";
4
+ import { AlephaBilling } from "../index.ts";
5
+ import { BillingService } from "../services/BillingService.ts";
6
+
7
+ describe("BillingService", () => {
8
+ it("should create an intent in 'created' status", async ({ expect }) => {
9
+ const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
10
+ const billing = alepha.inject(BillingService);
11
+ await alepha.start();
12
+
13
+ const intent = await billing.createIntent(1500, "eur");
14
+ expect(intent.amount).toBe(1500);
15
+ expect(intent.currency).toBe("eur");
16
+ expect(intent.status).toBe("created");
17
+ });
18
+
19
+ it("should create a session and transition to 'processing'", async ({
20
+ expect,
21
+ }) => {
22
+ const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
23
+ const billing = alepha.inject(BillingService);
24
+ await alepha.start();
25
+
26
+ const intent = await billing.createIntent(1500, "eur");
27
+ const session = await billing.createSession(
28
+ intent.id,
29
+ "https://example.com/return",
30
+ );
31
+
32
+ expect(session.url).toContain("/billing/mock-checkout/");
33
+ expect(session.intentId).toBe(intent.id);
34
+ });
35
+
36
+ it("should capture an authorized intent", async ({ expect }) => {
37
+ const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
38
+ const billing = alepha.inject(BillingService);
39
+ await alepha.start();
40
+
41
+ const intent = await billing.createIntent(1500, "eur");
42
+ await billing.createSession(intent.id, "https://example.com", true);
43
+ await billing.handleWebhookEvent(intent.id, "authorized");
44
+
45
+ const captured = await billing.capture(intent.id);
46
+ expect(captured.status).toBe("captured");
47
+ });
48
+
49
+ it("should void an authorized intent", async ({ expect }) => {
50
+ const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
51
+ const billing = alepha.inject(BillingService);
52
+ await alepha.start();
53
+
54
+ const intent = await billing.createIntent(1500, "eur");
55
+ await billing.createSession(intent.id, "https://example.com", true);
56
+ await billing.handleWebhookEvent(intent.id, "authorized");
57
+
58
+ const voided = await billing.void(intent.id);
59
+ expect(voided.status).toBe("voided");
60
+ });
61
+
62
+ it("should refund a captured intent", async ({ expect }) => {
63
+ const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
64
+ const billing = alepha.inject(BillingService);
65
+ await alepha.start();
66
+
67
+ const intent = await billing.createIntent(1500, "eur");
68
+ await billing.createSession(intent.id, "https://example.com");
69
+ await billing.handleWebhookEvent(intent.id, "captured");
70
+
71
+ const refund = await billing.refund(intent.id, 500, "Customer request");
72
+ expect(refund.amount).toBe(500);
73
+ expect(refund.status).toBe("completed");
74
+ });
75
+
76
+ it("should record a cash payment directly as captured", async ({
77
+ expect,
78
+ }) => {
79
+ const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
80
+ const billing = alepha.inject(BillingService);
81
+ await alepha.start();
82
+
83
+ const intent = await billing.recordCashPayment(1500, "eur", {
84
+ orderId: "order-1",
85
+ });
86
+ expect(intent.status).toBe("captured");
87
+ expect(intent.metadata).toEqual({ orderId: "order-1" });
88
+ });
89
+
90
+ it("should cancel a created intent", async ({ expect }) => {
91
+ const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
92
+ const billing = alepha.inject(BillingService);
93
+ await alepha.start();
94
+
95
+ const intent = await billing.createIntent(1500, "eur");
96
+ const cancelled = await billing.cancel(intent.id);
97
+ expect(cancelled.status).toBe("cancelled");
98
+ });
99
+
100
+ it("should reject capture from wrong status", async ({ expect }) => {
101
+ const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
102
+ const billing = alepha.inject(BillingService);
103
+ await alepha.start();
104
+
105
+ const intent = await billing.createIntent(1500, "eur");
106
+ await expect(billing.capture(intent.id)).rejects.toThrowError();
107
+ });
108
+
109
+ it("should reject refund from wrong status", async ({ expect }) => {
110
+ const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
111
+ const billing = alepha.inject(BillingService);
112
+ await alepha.start();
113
+
114
+ const intent = await billing.createIntent(1500, "eur");
115
+ await expect(billing.refund(intent.id, 500)).rejects.toThrowError();
116
+ });
117
+
118
+ it("should reject void from wrong status", async ({ expect }) => {
119
+ const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
120
+ const billing = alepha.inject(BillingService);
121
+ await alepha.start();
122
+
123
+ const intent = await billing.createIntent(1500, "eur");
124
+ await expect(billing.void(intent.id)).rejects.toThrowError();
125
+ });
126
+
127
+ it("should reject cancel from wrong status", async ({ expect }) => {
128
+ const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
129
+ const billing = alepha.inject(BillingService);
130
+ await alepha.start();
131
+
132
+ const intent = await billing.createIntent(1500, "eur");
133
+ await billing.createSession(intent.id, "https://example.com");
134
+ await expect(billing.cancel(intent.id)).rejects.toThrowError();
135
+ });
136
+ });
@@ -0,0 +1,78 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { Alepha } from "alepha";
3
+ import { AlephaOrmPostgres } from "alepha/orm/postgres";
4
+ import { describe, it } from "vitest";
5
+ import { AlephaBilling } from "../index.ts";
6
+ import { PaymentMethodService } from "../services/PaymentMethodService.ts";
7
+
8
+ describe("PaymentMethodService", () => {
9
+ const userId = randomUUID();
10
+ const userId2 = randomUUID();
11
+ const orgId = randomUUID();
12
+ it("should add a payment method", async ({ expect }) => {
13
+ const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
14
+ const service = alepha.inject(PaymentMethodService);
15
+ await alepha.start();
16
+
17
+ const method = await service.addPaymentMethod(userId, orgId, "tok_visa");
18
+ expect(method.type).toBe("card");
19
+ expect(method.last4).toBe("4242");
20
+ expect(method.isDefault).toBe(true);
21
+ });
22
+
23
+ it("should list payment methods for a user", async ({ expect }) => {
24
+ const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
25
+ const service = alepha.inject(PaymentMethodService);
26
+ await alepha.start();
27
+
28
+ await service.addPaymentMethod(userId, orgId, "tok_visa");
29
+ await service.addPaymentMethod(userId, orgId, "tok_mastercard");
30
+
31
+ const methods = await service.listPaymentMethods(userId);
32
+ expect(methods).toHaveLength(2);
33
+ });
34
+
35
+ it("should remove a payment method", async ({ expect }) => {
36
+ const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
37
+ const service = alepha.inject(PaymentMethodService);
38
+ await alepha.start();
39
+
40
+ const method = await service.addPaymentMethod(userId, orgId, "tok_visa");
41
+ await service.removePaymentMethod(method.id, userId);
42
+
43
+ const methods = await service.listPaymentMethods(userId);
44
+ expect(methods).toHaveLength(0);
45
+ });
46
+
47
+ it("should set a default payment method", async ({ expect }) => {
48
+ const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
49
+ const service = alepha.inject(PaymentMethodService);
50
+ await alepha.start();
51
+
52
+ const method1 = await service.addPaymentMethod(userId, orgId, "tok_visa");
53
+ const method2 = await service.addPaymentMethod(
54
+ userId,
55
+ orgId,
56
+ "tok_mastercard",
57
+ );
58
+
59
+ await service.setDefault(method1.id, userId);
60
+
61
+ const methods = await service.listPaymentMethods(userId);
62
+ const defaultMethod = methods.find((m) => m.isDefault);
63
+ expect(defaultMethod?.id).toBe(method1.id);
64
+ });
65
+
66
+ it("should reject removing another user's payment method", async ({
67
+ expect,
68
+ }) => {
69
+ const alepha = Alepha.create().with(AlephaOrmPostgres).with(AlephaBilling);
70
+ const service = alepha.inject(PaymentMethodService);
71
+ await alepha.start();
72
+
73
+ const method = await service.addPaymentMethod(userId, orgId, "tok_visa");
74
+ await expect(
75
+ service.removePaymentMethod(method.id, userId2),
76
+ ).rejects.toThrowError();
77
+ });
78
+ });
@@ -0,0 +1,149 @@
1
+ import { $inject, t } from "alepha";
2
+ import { $secure } from "alepha/security";
3
+ import { $action, okSchema } from "alepha/server";
4
+ import {
5
+ captureIntentSchema,
6
+ intentQuerySchema,
7
+ intentResourceSchema,
8
+ recordCashSchema,
9
+ refundIntentSchema,
10
+ } from "../schemas/intentSchemas.ts";
11
+ import { refundResourceSchema } from "../schemas/refundSchemas.ts";
12
+ import { BillingService } from "../services/BillingService.ts";
13
+
14
+ export class AdminBillingController {
15
+ protected readonly url = "/admin/billing";
16
+ protected readonly group = "admin:billing";
17
+ protected readonly billing = $inject(BillingService);
18
+
19
+ /**
20
+ * List payment intents with pagination and filtering.
21
+ */
22
+ public readonly listIntents = $action({
23
+ path: `${this.url}/intents`,
24
+ group: this.group,
25
+ use: [$secure({ permissions: ["billing:read"] })],
26
+ description: "List payment intents",
27
+ schema: {
28
+ query: intentQuerySchema,
29
+ response: t.page(intentResourceSchema),
30
+ },
31
+ handler: ({ query }) => this.billing.findIntents(query),
32
+ });
33
+
34
+ /**
35
+ * Get a payment intent by ID.
36
+ */
37
+ public readonly getIntent = $action({
38
+ path: `${this.url}/intents/:id`,
39
+ group: this.group,
40
+ use: [$secure({ permissions: ["billing:read"] })],
41
+ description: "Get payment intent details",
42
+ schema: {
43
+ params: t.object({ id: t.uuid() }),
44
+ response: intentResourceSchema,
45
+ },
46
+ handler: ({ params }) => this.billing.getIntent(params.id),
47
+ });
48
+
49
+ /**
50
+ * Capture an authorized intent.
51
+ */
52
+ public readonly captureIntent = $action({
53
+ method: "POST",
54
+ path: `${this.url}/intents/:id/capture`,
55
+ group: this.group,
56
+ use: [$secure({ permissions: ["billing:write"] })],
57
+ description: "Capture an authorized payment intent",
58
+ schema: {
59
+ params: t.object({ id: t.uuid() }),
60
+ body: captureIntentSchema,
61
+ response: intentResourceSchema,
62
+ },
63
+ handler: ({ params, body }) => this.billing.capture(params.id, body.amount),
64
+ });
65
+
66
+ /**
67
+ * Void an authorized intent.
68
+ */
69
+ public readonly voidIntent = $action({
70
+ method: "POST",
71
+ path: `${this.url}/intents/:id/void`,
72
+ group: this.group,
73
+ use: [$secure({ permissions: ["billing:write"] })],
74
+ description: "Void an authorized payment intent",
75
+ schema: {
76
+ params: t.object({ id: t.uuid() }),
77
+ response: intentResourceSchema,
78
+ },
79
+ handler: ({ params }) => this.billing.void(params.id),
80
+ });
81
+
82
+ /**
83
+ * Refund a captured intent.
84
+ */
85
+ public readonly refundIntent = $action({
86
+ method: "POST",
87
+ path: `${this.url}/intents/:id/refund`,
88
+ group: this.group,
89
+ use: [$secure({ permissions: ["billing:write"] })],
90
+ description: "Issue partial or full refund",
91
+ schema: {
92
+ params: t.object({ id: t.uuid() }),
93
+ body: refundIntentSchema,
94
+ response: refundResourceSchema,
95
+ },
96
+ handler: ({ params, body }) =>
97
+ this.billing.refund(params.id, body.amount, body.reason),
98
+ });
99
+
100
+ /**
101
+ * Cancel a created intent.
102
+ */
103
+ public readonly cancelIntent = $action({
104
+ method: "POST",
105
+ path: `${this.url}/intents/:id/cancel`,
106
+ group: this.group,
107
+ use: [$secure({ permissions: ["billing:write"] })],
108
+ description: "Cancel a created payment intent",
109
+ schema: {
110
+ params: t.object({ id: t.uuid() }),
111
+ response: intentResourceSchema,
112
+ },
113
+ handler: ({ params }) => this.billing.cancel(params.id),
114
+ });
115
+
116
+ /**
117
+ * Record a cash payment.
118
+ */
119
+ public readonly recordCash = $action({
120
+ method: "POST",
121
+ path: `${this.url}/cash`,
122
+ group: this.group,
123
+ use: [$secure({ permissions: ["billing:write"] })],
124
+ description: "Record a cash payment",
125
+ schema: {
126
+ body: recordCashSchema,
127
+ response: intentResourceSchema,
128
+ },
129
+ handler: ({ body }) =>
130
+ this.billing.recordCashPayment(body.amount, body.currency, body.metadata),
131
+ });
132
+
133
+ /**
134
+ * PSP webhook endpoint (not under /admin, no auth — verified by provider).
135
+ */
136
+ public readonly webhook = $action({
137
+ method: "POST",
138
+ path: "/billing/webhook",
139
+ group: this.group,
140
+ description: "PSP webhook endpoint",
141
+ schema: {
142
+ response: okSchema,
143
+ },
144
+ handler: async (request) => {
145
+ await this.billing.handleWebhook(request.raw.web!.req);
146
+ return { ok: true };
147
+ },
148
+ });
149
+ }