alepha 0.15.1 → 0.15.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 (523) hide show
  1. package/README.md +68 -80
  2. package/dist/api/audits/index.d.ts +10 -33
  3. package/dist/api/audits/index.d.ts.map +1 -1
  4. package/dist/api/audits/index.js +10 -33
  5. package/dist/api/audits/index.js.map +1 -1
  6. package/dist/api/files/index.d.ts +10 -3
  7. package/dist/api/files/index.d.ts.map +1 -1
  8. package/dist/api/files/index.js +10 -3
  9. package/dist/api/files/index.js.map +1 -1
  10. package/dist/api/jobs/index.d.ts +162 -155
  11. package/dist/api/jobs/index.d.ts.map +1 -1
  12. package/dist/api/jobs/index.js +10 -3
  13. package/dist/api/jobs/index.js.map +1 -1
  14. package/dist/api/keys/index.d.ts +413 -0
  15. package/dist/api/keys/index.d.ts.map +1 -0
  16. package/dist/api/keys/index.js +476 -0
  17. package/dist/api/keys/index.js.map +1 -0
  18. package/dist/api/notifications/index.d.ts +10 -4
  19. package/dist/api/notifications/index.d.ts.map +1 -1
  20. package/dist/api/notifications/index.js +10 -4
  21. package/dist/api/notifications/index.js.map +1 -1
  22. package/dist/api/parameters/index.d.ts +43 -50
  23. package/dist/api/parameters/index.d.ts.map +1 -1
  24. package/dist/api/parameters/index.js +30 -37
  25. package/dist/api/parameters/index.js.map +1 -1
  26. package/dist/api/users/index.d.ts +1081 -760
  27. package/dist/api/users/index.d.ts.map +1 -1
  28. package/dist/api/users/index.js +2539 -218
  29. package/dist/api/users/index.js.map +1 -1
  30. package/dist/api/verifications/index.d.ts +138 -132
  31. package/dist/api/verifications/index.d.ts.map +1 -1
  32. package/dist/api/verifications/index.js +12 -4
  33. package/dist/api/verifications/index.js.map +1 -1
  34. package/dist/batch/index.d.ts +20 -40
  35. package/dist/batch/index.d.ts.map +1 -1
  36. package/dist/batch/index.js +31 -44
  37. package/dist/batch/index.js.map +1 -1
  38. package/dist/bucket/index.d.ts +440 -8
  39. package/dist/bucket/index.d.ts.map +1 -1
  40. package/dist/bucket/index.js +1861 -12
  41. package/dist/bucket/index.js.map +1 -1
  42. package/dist/cache/core/index.d.ts +179 -7
  43. package/dist/cache/core/index.d.ts.map +1 -1
  44. package/dist/cache/core/index.js +213 -7
  45. package/dist/cache/core/index.js.map +1 -1
  46. package/dist/cache/redis/index.d.ts +1 -0
  47. package/dist/cache/redis/index.d.ts.map +1 -1
  48. package/dist/cache/redis/index.js +4 -0
  49. package/dist/cache/redis/index.js.map +1 -1
  50. package/dist/cli/index.d.ts +638 -5645
  51. package/dist/cli/index.d.ts.map +1 -1
  52. package/dist/cli/index.js +2550 -368
  53. package/dist/cli/index.js.map +1 -1
  54. package/dist/command/index.d.ts +203 -45
  55. package/dist/command/index.d.ts.map +1 -1
  56. package/dist/command/index.js +2060 -71
  57. package/dist/command/index.js.map +1 -1
  58. package/dist/core/index.browser.js +70 -40
  59. package/dist/core/index.browser.js.map +1 -1
  60. package/dist/core/index.d.ts +34 -13
  61. package/dist/core/index.d.ts.map +1 -1
  62. package/dist/core/index.js +90 -40
  63. package/dist/core/index.js.map +1 -1
  64. package/dist/core/index.native.js +70 -40
  65. package/dist/core/index.native.js.map +1 -1
  66. package/dist/datetime/index.d.ts +15 -0
  67. package/dist/datetime/index.d.ts.map +1 -1
  68. package/dist/datetime/index.js +15 -0
  69. package/dist/datetime/index.js.map +1 -1
  70. package/dist/email/index.d.ts +323 -20
  71. package/dist/email/index.d.ts.map +1 -1
  72. package/dist/email/index.js +1857 -7
  73. package/dist/email/index.js.map +1 -1
  74. package/dist/fake/index.d.ts +90 -8
  75. package/dist/fake/index.d.ts.map +1 -1
  76. package/dist/fake/index.js +91 -20
  77. package/dist/fake/index.js.map +1 -1
  78. package/dist/lock/core/index.d.ts +11 -4
  79. package/dist/lock/core/index.d.ts.map +1 -1
  80. package/dist/lock/core/index.js +11 -4
  81. package/dist/lock/core/index.js.map +1 -1
  82. package/dist/logger/index.d.ts +17 -66
  83. package/dist/logger/index.d.ts.map +1 -1
  84. package/dist/logger/index.js +14 -63
  85. package/dist/logger/index.js.map +1 -1
  86. package/dist/mcp/index.d.ts +10 -30
  87. package/dist/mcp/index.d.ts.map +1 -1
  88. package/dist/mcp/index.js +12 -35
  89. package/dist/mcp/index.js.map +1 -1
  90. package/dist/orm/index.browser.js +3 -3
  91. package/dist/orm/index.browser.js.map +1 -1
  92. package/dist/orm/index.bun.js +39 -20
  93. package/dist/orm/index.bun.js.map +1 -1
  94. package/dist/orm/index.d.ts +517 -540
  95. package/dist/orm/index.d.ts.map +1 -1
  96. package/dist/orm/index.js +58 -71
  97. package/dist/orm/index.js.map +1 -1
  98. package/dist/queue/core/index.d.ts +18 -10
  99. package/dist/queue/core/index.d.ts.map +1 -1
  100. package/dist/queue/core/index.js +14 -6
  101. package/dist/queue/core/index.js.map +1 -1
  102. package/dist/react/auth/index.browser.js +108 -0
  103. package/dist/react/auth/index.browser.js.map +1 -0
  104. package/dist/react/auth/index.d.ts +100 -0
  105. package/dist/react/auth/index.d.ts.map +1 -0
  106. package/dist/react/auth/index.js +145 -0
  107. package/dist/react/auth/index.js.map +1 -0
  108. package/dist/react/core/index.d.ts +469 -0
  109. package/dist/react/core/index.d.ts.map +1 -0
  110. package/dist/react/core/index.js +464 -0
  111. package/dist/react/core/index.js.map +1 -0
  112. package/dist/react/form/index.d.ts +232 -0
  113. package/dist/react/form/index.d.ts.map +1 -0
  114. package/dist/react/form/index.js +432 -0
  115. package/dist/react/form/index.js.map +1 -0
  116. package/dist/react/head/index.browser.js +423 -0
  117. package/dist/react/head/index.browser.js.map +1 -0
  118. package/dist/react/head/index.d.ts +288 -0
  119. package/dist/react/head/index.d.ts.map +1 -0
  120. package/dist/react/head/index.js +465 -0
  121. package/dist/react/head/index.js.map +1 -0
  122. package/dist/react/i18n/index.d.ts +175 -0
  123. package/dist/react/i18n/index.d.ts.map +1 -0
  124. package/dist/react/i18n/index.js +224 -0
  125. package/dist/react/i18n/index.js.map +1 -0
  126. package/dist/react/router/index.browser.js +1974 -0
  127. package/dist/react/router/index.browser.js.map +1 -0
  128. package/dist/react/router/index.d.ts +1956 -0
  129. package/dist/react/router/index.d.ts.map +1 -0
  130. package/dist/react/router/index.js +4722 -0
  131. package/dist/react/router/index.js.map +1 -0
  132. package/dist/react/websocket/index.d.ts +117 -0
  133. package/dist/react/websocket/index.d.ts.map +1 -0
  134. package/dist/react/websocket/index.js +107 -0
  135. package/dist/react/websocket/index.js.map +1 -0
  136. package/dist/redis/index.bun.js +4 -0
  137. package/dist/redis/index.bun.js.map +1 -1
  138. package/dist/redis/index.d.ts +41 -44
  139. package/dist/redis/index.d.ts.map +1 -1
  140. package/dist/redis/index.js +16 -25
  141. package/dist/redis/index.js.map +1 -1
  142. package/dist/retry/index.d.ts +11 -2
  143. package/dist/retry/index.d.ts.map +1 -1
  144. package/dist/retry/index.js +11 -2
  145. package/dist/retry/index.js.map +1 -1
  146. package/dist/scheduler/index.d.ts +11 -2
  147. package/dist/scheduler/index.d.ts.map +1 -1
  148. package/dist/scheduler/index.js +11 -2
  149. package/dist/scheduler/index.js.map +1 -1
  150. package/dist/security/index.d.ts +140 -49
  151. package/dist/security/index.d.ts.map +1 -1
  152. package/dist/security/index.js +164 -32
  153. package/dist/security/index.js.map +1 -1
  154. package/dist/server/auth/index.d.ts +12 -7
  155. package/dist/server/auth/index.d.ts.map +1 -1
  156. package/dist/server/auth/index.js +12 -7
  157. package/dist/server/auth/index.js.map +1 -1
  158. package/dist/server/cache/index.d.ts +7 -22
  159. package/dist/server/cache/index.d.ts.map +1 -1
  160. package/dist/server/cache/index.js +7 -22
  161. package/dist/server/cache/index.js.map +1 -1
  162. package/dist/server/compress/index.d.ts +10 -2
  163. package/dist/server/compress/index.d.ts.map +1 -1
  164. package/dist/server/compress/index.js +10 -2
  165. package/dist/server/compress/index.js.map +1 -1
  166. package/dist/server/cookies/index.d.ts +40 -16
  167. package/dist/server/cookies/index.d.ts.map +1 -1
  168. package/dist/server/cookies/index.js +7 -5
  169. package/dist/server/cookies/index.js.map +1 -1
  170. package/dist/server/core/index.d.ts +124 -23
  171. package/dist/server/core/index.d.ts.map +1 -1
  172. package/dist/server/core/index.js +231 -14
  173. package/dist/server/core/index.js.map +1 -1
  174. package/dist/server/cors/index.d.ts +13 -23
  175. package/dist/server/cors/index.d.ts.map +1 -1
  176. package/dist/server/cors/index.js +7 -21
  177. package/dist/server/cors/index.js.map +1 -1
  178. package/dist/server/health/index.d.ts +8 -2
  179. package/dist/server/health/index.d.ts.map +1 -1
  180. package/dist/server/health/index.js +8 -2
  181. package/dist/server/health/index.js.map +1 -1
  182. package/dist/server/helmet/index.d.ts +11 -3
  183. package/dist/server/helmet/index.d.ts.map +1 -1
  184. package/dist/server/helmet/index.js +11 -3
  185. package/dist/server/helmet/index.js.map +1 -1
  186. package/dist/server/links/index.d.ts +11 -6
  187. package/dist/server/links/index.d.ts.map +1 -1
  188. package/dist/server/links/index.js +11 -6
  189. package/dist/server/links/index.js.map +1 -1
  190. package/dist/server/metrics/index.d.ts +10 -3
  191. package/dist/server/metrics/index.d.ts.map +1 -1
  192. package/dist/server/metrics/index.js +10 -3
  193. package/dist/server/metrics/index.js.map +1 -1
  194. package/dist/server/multipart/index.d.ts +9 -3
  195. package/dist/server/multipart/index.d.ts.map +1 -1
  196. package/dist/server/multipart/index.js +9 -3
  197. package/dist/server/multipart/index.js.map +1 -1
  198. package/dist/server/proxy/index.d.ts +8 -2
  199. package/dist/server/proxy/index.d.ts.map +1 -1
  200. package/dist/server/proxy/index.js +8 -2
  201. package/dist/server/proxy/index.js.map +1 -1
  202. package/dist/server/rate-limit/index.d.ts +30 -35
  203. package/dist/server/rate-limit/index.d.ts.map +1 -1
  204. package/dist/server/rate-limit/index.js +18 -55
  205. package/dist/server/rate-limit/index.js.map +1 -1
  206. package/dist/server/static/index.d.ts +137 -4
  207. package/dist/server/static/index.d.ts.map +1 -1
  208. package/dist/server/static/index.js +1853 -5
  209. package/dist/server/static/index.js.map +1 -1
  210. package/dist/server/swagger/index.d.ts +309 -6
  211. package/dist/server/swagger/index.d.ts.map +1 -1
  212. package/dist/server/swagger/index.js +1854 -6
  213. package/dist/server/swagger/index.js.map +1 -1
  214. package/dist/sms/index.d.ts +309 -7
  215. package/dist/sms/index.d.ts.map +1 -1
  216. package/dist/sms/index.js +1856 -7
  217. package/dist/sms/index.js.map +1 -1
  218. package/dist/system/index.browser.js +1218 -0
  219. package/dist/system/index.browser.js.map +1 -0
  220. package/dist/{file → system}/index.d.ts +343 -16
  221. package/dist/system/index.d.ts.map +1 -0
  222. package/dist/{file → system}/index.js +419 -22
  223. package/dist/system/index.js.map +1 -0
  224. package/dist/thread/index.d.ts +11 -2
  225. package/dist/thread/index.d.ts.map +1 -1
  226. package/dist/thread/index.js +11 -2
  227. package/dist/thread/index.js.map +1 -1
  228. package/dist/topic/core/index.d.ts +12 -5
  229. package/dist/topic/core/index.d.ts.map +1 -1
  230. package/dist/topic/core/index.js +12 -5
  231. package/dist/topic/core/index.js.map +1 -1
  232. package/dist/vite/index.d.ts +5 -6272
  233. package/dist/vite/index.d.ts.map +1 -1
  234. package/dist/vite/index.js +23 -10
  235. package/dist/vite/index.js.map +1 -1
  236. package/dist/websocket/index.d.ts +12 -8
  237. package/dist/websocket/index.d.ts.map +1 -1
  238. package/dist/websocket/index.js +12 -8
  239. package/dist/websocket/index.js.map +1 -1
  240. package/package.json +82 -11
  241. package/src/api/audits/index.ts +10 -33
  242. package/src/api/files/__tests__/$bucket.spec.ts +1 -1
  243. package/src/api/files/controllers/AdminFileStatsController.spec.ts +1 -1
  244. package/src/api/files/controllers/FileController.spec.ts +1 -1
  245. package/src/api/files/index.ts +10 -3
  246. package/src/api/files/jobs/FileJobs.spec.ts +1 -1
  247. package/src/api/files/services/FileService.spec.ts +1 -1
  248. package/src/api/jobs/index.ts +10 -3
  249. package/src/api/keys/controllers/AdminApiKeyController.ts +75 -0
  250. package/src/api/keys/controllers/ApiKeyController.ts +103 -0
  251. package/src/api/keys/entities/apiKeyEntity.ts +41 -0
  252. package/src/api/keys/index.ts +49 -0
  253. package/src/api/keys/schemas/adminApiKeyQuerySchema.ts +7 -0
  254. package/src/api/keys/schemas/adminApiKeyResourceSchema.ts +17 -0
  255. package/src/api/keys/schemas/createApiKeyBodySchema.ts +7 -0
  256. package/src/api/keys/schemas/createApiKeyResponseSchema.ts +11 -0
  257. package/src/api/keys/schemas/listApiKeyResponseSchema.ts +15 -0
  258. package/src/api/keys/schemas/revokeApiKeyParamsSchema.ts +5 -0
  259. package/src/api/keys/schemas/revokeApiKeyResponseSchema.ts +5 -0
  260. package/src/api/keys/services/ApiKeyService.spec.ts +553 -0
  261. package/src/api/keys/services/ApiKeyService.ts +306 -0
  262. package/src/api/logs/TODO.md +55 -0
  263. package/src/api/notifications/index.ts +10 -4
  264. package/src/api/parameters/index.ts +9 -30
  265. package/src/api/parameters/primitives/$config.ts +12 -4
  266. package/src/api/parameters/services/ConfigStore.ts +9 -3
  267. package/src/api/users/__tests__/ApiKeys-integration.spec.ts +1035 -0
  268. package/src/api/users/__tests__/ApiKeys.spec.ts +401 -0
  269. package/src/api/users/index.ts +14 -3
  270. package/src/api/users/primitives/$realm.ts +33 -5
  271. package/src/api/users/providers/RealmProvider.ts +1 -12
  272. package/src/api/users/services/SessionService.ts +1 -1
  273. package/src/api/verifications/controllers/VerificationController.ts +2 -0
  274. package/src/api/verifications/index.ts +10 -4
  275. package/src/batch/index.ts +9 -36
  276. package/src/batch/primitives/$batch.ts +0 -8
  277. package/src/batch/providers/BatchProvider.ts +29 -2
  278. package/src/bucket/__tests__/shared.ts +1 -1
  279. package/src/bucket/index.ts +13 -6
  280. package/src/bucket/primitives/$bucket.ts +1 -1
  281. package/src/bucket/providers/LocalFileStorageProvider.ts +1 -1
  282. package/src/bucket/providers/MemoryFileStorageProvider.ts +1 -1
  283. package/src/cache/core/__tests__/shared.ts +30 -0
  284. package/src/cache/core/index.ts +11 -6
  285. package/src/cache/core/primitives/$cache.spec.ts +5 -0
  286. package/src/cache/core/providers/CacheProvider.ts +17 -0
  287. package/src/cache/core/providers/MemoryCacheProvider.ts +300 -1
  288. package/src/cache/redis/__tests__/cache-redis.spec.ts +5 -0
  289. package/src/cache/redis/providers/RedisCacheProvider.ts +9 -0
  290. package/src/cli/apps/AlephaCli.ts +1 -14
  291. package/src/cli/apps/AlephaPackageBuilderCli.ts +10 -1
  292. package/src/cli/atoms/buildOptions.ts +99 -9
  293. package/src/cli/commands/build.ts +150 -37
  294. package/src/cli/commands/db.ts +22 -18
  295. package/src/cli/commands/deploy.ts +1 -1
  296. package/src/cli/commands/dev.ts +1 -20
  297. package/src/cli/commands/gen/env.ts +5 -2
  298. package/src/cli/commands/gen/openapi.ts +5 -2
  299. package/src/cli/commands/init.spec.ts +588 -0
  300. package/src/cli/commands/init.ts +115 -58
  301. package/src/cli/commands/lint.ts +7 -1
  302. package/src/cli/commands/typecheck.ts +11 -0
  303. package/src/cli/providers/AppEntryProvider.ts +1 -1
  304. package/src/cli/providers/ViteBuildProvider.ts +8 -50
  305. package/src/cli/providers/ViteDevServerProvider.ts +35 -16
  306. package/src/cli/services/AlephaCliUtils.ts +52 -121
  307. package/src/cli/services/PackageManagerUtils.ts +129 -11
  308. package/src/cli/services/ProjectScaffolder.spec.ts +97 -0
  309. package/src/cli/services/ProjectScaffolder.ts +148 -81
  310. package/src/cli/services/ViteUtils.ts +82 -0
  311. package/src/cli/{assets/claudeMd.ts → templates/agentMd.ts} +37 -24
  312. package/src/cli/templates/apiAppSecurityTs.ts +11 -0
  313. package/src/cli/templates/apiIndexTs.ts +30 -0
  314. package/src/cli/templates/gitignore.ts +39 -0
  315. package/src/cli/{assets → templates}/mainCss.ts +11 -2
  316. package/src/cli/templates/mainServerTs.ts +33 -0
  317. package/src/cli/templates/webAppRouterTs.ts +74 -0
  318. package/src/cli/templates/webHelloComponentTsx.ts +30 -0
  319. package/src/command/helpers/Runner.spec.ts +139 -0
  320. package/src/command/helpers/Runner.ts +7 -22
  321. package/src/command/index.ts +12 -4
  322. package/src/command/providers/CliProvider.spec.ts +1392 -0
  323. package/src/command/providers/CliProvider.ts +320 -47
  324. package/src/core/Alepha.ts +34 -27
  325. package/src/core/__tests__/Alepha-start.spec.ts +4 -4
  326. package/src/core/helpers/jsonSchemaToTypeBox.spec.ts +771 -0
  327. package/src/core/helpers/jsonSchemaToTypeBox.ts +62 -10
  328. package/src/core/index.shared.ts +1 -0
  329. package/src/core/index.ts +20 -0
  330. package/src/core/providers/EventManager.spec.ts +0 -71
  331. package/src/core/providers/EventManager.ts +3 -15
  332. package/src/core/providers/Json.ts +2 -14
  333. package/src/datetime/index.ts +15 -0
  334. package/src/email/index.ts +10 -5
  335. package/src/email/providers/LocalEmailProvider.spec.ts +1 -1
  336. package/src/email/providers/LocalEmailProvider.ts +1 -1
  337. package/src/fake/__tests__/keyName.example.ts +1 -1
  338. package/src/fake/__tests__/keyName.spec.ts +5 -5
  339. package/src/fake/index.ts +9 -6
  340. package/src/fake/providers/FakeProvider.spec.ts +258 -40
  341. package/src/fake/providers/FakeProvider.ts +133 -19
  342. package/src/lock/core/index.ts +11 -4
  343. package/src/logger/index.ts +17 -66
  344. package/src/mcp/index.ts +10 -27
  345. package/src/mcp/transports/SseMcpTransport.ts +0 -11
  346. package/src/orm/__tests__/PostgresProvider.spec.ts +2 -2
  347. package/src/orm/index.browser.ts +2 -2
  348. package/src/orm/index.bun.ts +5 -3
  349. package/src/orm/index.ts +23 -53
  350. package/src/orm/providers/drivers/BunSqliteProvider.ts +5 -1
  351. package/src/orm/providers/drivers/CloudflareD1Provider.ts +57 -30
  352. package/src/orm/providers/drivers/DatabaseProvider.ts +9 -1
  353. package/src/orm/providers/drivers/NodeSqliteProvider.ts +4 -1
  354. package/src/orm/services/Repository.ts +7 -3
  355. package/src/queue/core/index.ts +14 -6
  356. package/src/react/auth/__tests__/$auth.spec.ts +202 -0
  357. package/src/react/auth/hooks/useAuth.ts +32 -0
  358. package/src/react/auth/index.browser.ts +13 -0
  359. package/src/react/auth/index.shared.ts +2 -0
  360. package/src/react/auth/index.ts +48 -0
  361. package/src/react/auth/providers/ReactAuthProvider.ts +16 -0
  362. package/src/react/auth/services/ReactAuth.ts +135 -0
  363. package/src/react/core/__tests__/Router.spec.tsx +169 -0
  364. package/src/react/core/components/ClientOnly.tsx +49 -0
  365. package/src/react/core/components/ErrorBoundary.tsx +73 -0
  366. package/src/react/core/contexts/AlephaContext.ts +7 -0
  367. package/src/react/core/contexts/AlephaProvider.tsx +42 -0
  368. package/src/react/core/hooks/useAction.browser.spec.tsx +569 -0
  369. package/src/react/core/hooks/useAction.ts +480 -0
  370. package/src/react/core/hooks/useAlepha.ts +26 -0
  371. package/src/react/core/hooks/useClient.ts +17 -0
  372. package/src/react/core/hooks/useEvents.ts +51 -0
  373. package/src/react/core/hooks/useInject.ts +12 -0
  374. package/src/react/core/hooks/useStore.ts +52 -0
  375. package/src/react/core/index.ts +90 -0
  376. package/src/react/form/components/FormState.tsx +17 -0
  377. package/src/react/form/errors/FormValidationError.ts +18 -0
  378. package/src/react/form/hooks/useForm.browser.spec.tsx +366 -0
  379. package/src/react/form/hooks/useForm.ts +47 -0
  380. package/src/react/form/hooks/useFormState.ts +130 -0
  381. package/src/react/form/index.ts +44 -0
  382. package/src/react/form/services/FormModel.ts +614 -0
  383. package/src/react/head/helpers/SeoExpander.spec.ts +203 -0
  384. package/src/react/head/helpers/SeoExpander.ts +142 -0
  385. package/src/react/head/hooks/useHead.spec.tsx +288 -0
  386. package/src/react/head/hooks/useHead.ts +62 -0
  387. package/src/react/head/index.browser.ts +26 -0
  388. package/src/react/head/index.ts +44 -0
  389. package/src/react/head/interfaces/Head.ts +105 -0
  390. package/src/react/head/primitives/$head.ts +25 -0
  391. package/src/react/head/providers/BrowserHeadProvider.browser.spec.ts +196 -0
  392. package/src/react/head/providers/BrowserHeadProvider.ts +212 -0
  393. package/src/react/head/providers/HeadProvider.ts +168 -0
  394. package/src/react/head/providers/ServerHeadProvider.ts +31 -0
  395. package/src/react/i18n/__tests__/integration.spec.tsx +239 -0
  396. package/src/react/i18n/components/Localize.spec.tsx +357 -0
  397. package/src/react/i18n/components/Localize.tsx +35 -0
  398. package/src/react/i18n/hooks/useI18n.browser.spec.tsx +438 -0
  399. package/src/react/i18n/hooks/useI18n.ts +18 -0
  400. package/src/react/i18n/index.ts +41 -0
  401. package/src/react/i18n/primitives/$dictionary.ts +69 -0
  402. package/src/react/i18n/providers/I18nProvider.spec.ts +389 -0
  403. package/src/react/i18n/providers/I18nProvider.ts +278 -0
  404. package/src/react/router/__tests__/page-head-browser.browser.spec.ts +95 -0
  405. package/src/react/router/__tests__/page-head.spec.ts +48 -0
  406. package/src/react/router/__tests__/seo-head.spec.ts +125 -0
  407. package/src/react/router/atoms/ssrManifestAtom.ts +58 -0
  408. package/src/react/router/components/ErrorViewer.tsx +872 -0
  409. package/src/react/router/components/Link.tsx +23 -0
  410. package/src/react/router/components/NestedView.tsx +223 -0
  411. package/src/react/router/components/NotFound.tsx +30 -0
  412. package/src/react/router/constants/PAGE_PRELOAD_KEY.ts +6 -0
  413. package/src/react/router/contexts/RouterLayerContext.ts +12 -0
  414. package/src/react/router/errors/Redirection.ts +28 -0
  415. package/src/react/router/hooks/useActive.ts +52 -0
  416. package/src/react/router/hooks/useQueryParams.ts +63 -0
  417. package/src/react/router/hooks/useRouter.ts +20 -0
  418. package/src/react/router/hooks/useRouterState.ts +11 -0
  419. package/src/react/router/index.browser.ts +45 -0
  420. package/src/react/router/index.shared.ts +19 -0
  421. package/src/react/router/index.ts +146 -0
  422. package/src/react/router/primitives/$page.browser.spec.tsx +851 -0
  423. package/src/react/router/primitives/$page.spec.tsx +676 -0
  424. package/src/react/router/primitives/$page.ts +489 -0
  425. package/src/react/router/providers/ReactBrowserProvider.ts +312 -0
  426. package/src/react/router/providers/ReactBrowserRendererProvider.ts +25 -0
  427. package/src/react/router/providers/ReactBrowserRouterProvider.ts +168 -0
  428. package/src/react/router/providers/ReactPageProvider.ts +726 -0
  429. package/src/react/router/providers/ReactPreloadProvider.spec.ts +142 -0
  430. package/src/react/router/providers/ReactPreloadProvider.ts +85 -0
  431. package/src/react/router/providers/ReactServerProvider.spec.tsx +316 -0
  432. package/src/react/router/providers/ReactServerProvider.ts +487 -0
  433. package/src/react/router/providers/ReactServerTemplateProvider.spec.ts +210 -0
  434. package/src/react/router/providers/ReactServerTemplateProvider.ts +542 -0
  435. package/src/react/router/providers/SSRManifestProvider.ts +334 -0
  436. package/src/react/router/services/ReactPageServerService.ts +48 -0
  437. package/src/react/router/services/ReactPageService.ts +27 -0
  438. package/src/react/router/services/ReactRouter.ts +262 -0
  439. package/src/react/websocket/hooks/useRoom.tsx +242 -0
  440. package/src/react/websocket/index.ts +7 -0
  441. package/src/redis/__tests__/redis.spec.ts +13 -0
  442. package/src/redis/index.ts +9 -25
  443. package/src/redis/providers/BunRedisProvider.ts +9 -0
  444. package/src/redis/providers/NodeRedisProvider.ts +8 -0
  445. package/src/redis/providers/RedisProvider.ts +16 -0
  446. package/src/retry/index.ts +11 -2
  447. package/src/router/index.ts +15 -0
  448. package/src/scheduler/index.ts +11 -2
  449. package/src/security/__tests__/BasicAuth.spec.ts +2 -0
  450. package/src/security/__tests__/ServerSecurityProvider.spec.ts +90 -5
  451. package/src/security/index.ts +15 -10
  452. package/src/security/interfaces/IssuerResolver.ts +27 -0
  453. package/src/security/primitives/$issuer.ts +55 -0
  454. package/src/security/providers/SecurityProvider.ts +179 -0
  455. package/src/security/providers/ServerBasicAuthProvider.ts +6 -2
  456. package/src/security/providers/ServerSecurityProvider.ts +63 -41
  457. package/src/server/auth/index.ts +12 -7
  458. package/src/server/cache/index.ts +7 -22
  459. package/src/server/compress/index.ts +10 -2
  460. package/src/server/cookies/index.ts +7 -5
  461. package/src/server/cookies/primitives/$cookie.ts +33 -11
  462. package/src/server/core/index.ts +16 -6
  463. package/src/server/core/interfaces/ServerRequest.ts +83 -1
  464. package/src/server/core/primitives/$action.spec.ts +1 -1
  465. package/src/server/core/primitives/$action.ts +8 -3
  466. package/src/server/core/providers/NodeHttpServerProvider.spec.ts +9 -3
  467. package/src/server/core/providers/NodeHttpServerProvider.ts +9 -3
  468. package/src/server/core/services/ServerRequestParser.spec.ts +520 -0
  469. package/src/server/core/services/ServerRequestParser.ts +306 -13
  470. package/src/server/cors/index.ts +7 -21
  471. package/src/server/cors/primitives/$cors.ts +6 -2
  472. package/src/server/health/index.ts +8 -2
  473. package/src/server/helmet/index.ts +11 -3
  474. package/src/server/links/index.ts +11 -6
  475. package/src/server/metrics/index.ts +10 -3
  476. package/src/server/multipart/index.ts +9 -3
  477. package/src/server/proxy/index.ts +8 -2
  478. package/src/server/rate-limit/index.ts +21 -25
  479. package/src/server/rate-limit/primitives/$rateLimit.ts +6 -2
  480. package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +38 -14
  481. package/src/server/rate-limit/providers/ServerRateLimitProvider.ts +22 -56
  482. package/src/server/static/index.ts +8 -2
  483. package/src/server/static/providers/ServerStaticProvider.ts +1 -1
  484. package/src/server/swagger/index.ts +9 -4
  485. package/src/server/swagger/providers/ServerSwaggerProvider.ts +1 -1
  486. package/src/sms/index.ts +9 -5
  487. package/src/sms/providers/LocalSmsProvider.spec.ts +1 -1
  488. package/src/sms/providers/LocalSmsProvider.ts +1 -1
  489. package/src/system/index.browser.ts +36 -0
  490. package/src/system/index.ts +62 -0
  491. package/src/system/index.workerd.ts +1 -0
  492. package/src/{file → system}/providers/FileSystemProvider.ts +24 -0
  493. package/src/{file → system}/providers/MemoryFileSystemProvider.ts +116 -3
  494. package/src/system/providers/MemoryShellProvider.ts +164 -0
  495. package/src/{file → system}/providers/NodeFileSystemProvider.spec.ts +2 -2
  496. package/src/{file → system}/providers/NodeFileSystemProvider.ts +47 -2
  497. package/src/system/providers/NodeShellProvider.ts +184 -0
  498. package/src/system/providers/ShellProvider.ts +74 -0
  499. package/src/{file → system}/services/FileDetector.spec.ts +2 -2
  500. package/src/thread/index.ts +11 -2
  501. package/src/topic/core/index.ts +12 -5
  502. package/src/vite/tasks/buildClient.ts +2 -7
  503. package/src/vite/tasks/buildServer.ts +19 -13
  504. package/src/vite/tasks/generateCloudflare.ts +10 -7
  505. package/src/vite/tasks/generateDocker.ts +4 -0
  506. package/src/websocket/index.ts +12 -8
  507. package/dist/file/index.d.ts.map +0 -1
  508. package/dist/file/index.js.map +0 -1
  509. package/src/cli/assets/apiIndexTs.ts +0 -16
  510. package/src/cli/assets/mainServerTs.ts +0 -24
  511. package/src/cli/assets/webAppRouterTs.ts +0 -16
  512. package/src/cli/assets/webHelloComponentTsx.ts +0 -20
  513. package/src/cli/providers/ViteTemplateProvider.ts +0 -27
  514. package/src/file/index.ts +0 -43
  515. /package/src/cli/{assets → templates}/apiHelloControllerTs.ts +0 -0
  516. /package/src/cli/{assets → templates}/biomeJson.ts +0 -0
  517. /package/src/cli/{assets → templates}/dummySpecTs.ts +0 -0
  518. /package/src/cli/{assets → templates}/editorconfig.ts +0 -0
  519. /package/src/cli/{assets → templates}/mainBrowserTs.ts +0 -0
  520. /package/src/cli/{assets → templates}/tsconfigJson.ts +0 -0
  521. /package/src/cli/{assets → templates}/webIndexTs.ts +0 -0
  522. /package/src/{file → system}/errors/FileError.ts +0 -0
  523. /package/src/{file → system}/services/FileDetector.ts +0 -0
@@ -0,0 +1,242 @@
1
+ import type { Static } from "alepha";
2
+ import { useAlepha, useInject } from "alepha/react";
3
+ import type { ChannelPrimitive, TWSObject } from "alepha/websocket";
4
+ import { WebSocketClient } from "alepha/websocket";
5
+ import { useEffect, useRef, useState } from "react";
6
+
7
+ /**
8
+ * UseRoom options
9
+ */
10
+ export interface UseRoomOptions<
11
+ TClient extends TWSObject,
12
+ TServer extends TWSObject,
13
+ > {
14
+ /**
15
+ * Room ID to connect to
16
+ */
17
+ roomId: string;
18
+
19
+ /**
20
+ * Channel primitive defining the schemas
21
+ */
22
+ channel: ChannelPrimitive<TClient, TServer>;
23
+
24
+ /**
25
+ * Handler for incoming messages from the server
26
+ */
27
+ handler: (message: Static<TClient>) => void;
28
+
29
+ /**
30
+ * Optional WebSocket URL override
31
+ * Defaults to auto-detected URL based on window.location
32
+ */
33
+ url?: string;
34
+
35
+ /**
36
+ * Enable automatic reconnection on disconnect
37
+ * @default true
38
+ */
39
+ autoReconnect?: boolean;
40
+
41
+ /**
42
+ * Reconnection interval in milliseconds
43
+ * @default 3000
44
+ */
45
+ reconnectInterval?: number;
46
+
47
+ /**
48
+ * Maximum reconnection attempts (-1 for infinite)
49
+ * @default 10
50
+ */
51
+ maxReconnectAttempts?: number;
52
+
53
+ /**
54
+ * Called when connection is established
55
+ */
56
+ onConnect?: () => void;
57
+
58
+ /**
59
+ * Called when connection is closed
60
+ */
61
+ onDisconnect?: () => void;
62
+
63
+ /**
64
+ * Called on connection error
65
+ */
66
+ onError?: (error: Error) => void;
67
+ }
68
+
69
+ /**
70
+ * UseRoom return value
71
+ */
72
+ export interface UseRoomReturn<TServer extends TWSObject> {
73
+ /**
74
+ * Send a message to the server
75
+ */
76
+ send: (message: Static<TServer>) => Promise<void>;
77
+
78
+ /**
79
+ * Whether the connection is established
80
+ */
81
+ isConnected: boolean;
82
+
83
+ /**
84
+ * Whether the connection is in progress
85
+ */
86
+ isConnecting: boolean;
87
+
88
+ /**
89
+ * Whether there was an error
90
+ */
91
+ isError: boolean;
92
+
93
+ /**
94
+ * The error object if any
95
+ */
96
+ error?: Error;
97
+
98
+ /**
99
+ * Manually reconnect
100
+ */
101
+ reconnect: () => void;
102
+
103
+ /**
104
+ * Manually disconnect
105
+ */
106
+ disconnect: () => void;
107
+ }
108
+
109
+ /**
110
+ * React hook for WebSocket room communication
111
+ *
112
+ * Provides automatic connection management, reconnection, and type-safe messaging
113
+ * for WebSocket rooms using the injected WebSocketClient service.
114
+ *
115
+ * Multiple useRoom hooks on the same channel will share a single WebSocket connection.
116
+ *
117
+ * @example
118
+ * ```tsx
119
+ * const chat = useRoom({
120
+ * roomId: "room-123",
121
+ * channel: chatChannel,
122
+ * handler: (message) => {
123
+ * if (message.type === "append") {
124
+ * setMessages(prev => [...prev, message]);
125
+ * }
126
+ * }
127
+ * }, [roomId]);
128
+ *
129
+ * const sendMessage = async () => {
130
+ * await chat.send({
131
+ * content: "Hello, world!"
132
+ * });
133
+ * };
134
+ * ```
135
+ */
136
+ export const useRoom = <TClient extends TWSObject, TServer extends TWSObject>(
137
+ options: UseRoomOptions<TClient, TServer>,
138
+ deps: unknown[],
139
+ ): UseRoomReturn<TServer> => {
140
+ const webSocketClient = useInject(WebSocketClient);
141
+ const unsubscribeRef = useRef<(() => void) | null>(null);
142
+
143
+ const [isConnected, setIsConnected] = useState(false);
144
+ const [isConnecting, setIsConnecting] = useState(false);
145
+ const [isError, setIsError] = useState(false);
146
+ const [error, setError] = useState<Error | undefined>(undefined);
147
+
148
+ const {
149
+ roomId,
150
+ channel,
151
+ handler,
152
+ url,
153
+ autoReconnect,
154
+ reconnectInterval,
155
+ maxReconnectAttempts,
156
+ onConnect,
157
+ onDisconnect,
158
+ onError,
159
+ } = options;
160
+
161
+ useEffect(() => {
162
+ // Subscribe to room
163
+ const unsubscribe = webSocketClient.subscribe(roomId, channel, handler, {
164
+ url,
165
+ autoReconnect,
166
+ reconnectInterval,
167
+ maxReconnectAttempts,
168
+ onConnect: () => {
169
+ setIsConnected(true);
170
+ setIsConnecting(false);
171
+ setIsError(false);
172
+ setError(undefined);
173
+ onConnect?.();
174
+ },
175
+ onDisconnect: () => {
176
+ setIsConnected(false);
177
+ setIsConnecting(false);
178
+ onDisconnect?.();
179
+ },
180
+ onError: (err) => {
181
+ setIsError(true);
182
+ setError(err);
183
+ setIsConnecting(false);
184
+ onError?.(err);
185
+ },
186
+ });
187
+
188
+ unsubscribeRef.current = unsubscribe;
189
+
190
+ // Get initial state from connection
191
+ const connection = webSocketClient.getConnection(channel);
192
+ if (connection) {
193
+ setIsConnected(connection.isConnected);
194
+ setIsConnecting(connection.isConnecting);
195
+ setIsError(connection.isError);
196
+ setError(connection.error);
197
+ }
198
+
199
+ // Cleanup on unmount or deps change
200
+ return () => {
201
+ unsubscribe();
202
+ unsubscribeRef.current = null;
203
+ };
204
+ }, deps);
205
+
206
+ const alepha = useAlepha();
207
+
208
+ if (!alepha.isBrowser()) {
209
+ return {
210
+ send: async (_message: Static<TServer>) => {
211
+ // No-op on server
212
+ },
213
+ isConnected: false,
214
+ isConnecting: false,
215
+ isError: false,
216
+ error: undefined,
217
+ reconnect: () => {
218
+ // No-op on server
219
+ },
220
+ disconnect: () => {
221
+ // No-op on server
222
+ },
223
+ };
224
+ }
225
+
226
+ return {
227
+ send: async (message: Static<TServer>) => {
228
+ await webSocketClient.send(roomId, channel, message);
229
+ },
230
+ isConnected,
231
+ isConnecting,
232
+ isError,
233
+ error,
234
+ reconnect: () => {
235
+ const connection = webSocketClient.getConnection(channel);
236
+ connection?.reconnect();
237
+ },
238
+ disconnect: () => {
239
+ unsubscribeRef.current?.();
240
+ },
241
+ };
242
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * alepha/react/websocket
3
+ *
4
+ * React hooks for real-time WebSocket communication
5
+ */
6
+
7
+ export * from "./hooks/useRoom.tsx";
@@ -55,4 +55,17 @@ describe("Redis", () => {
55
55
  await redis.lpush("test:b", "b");
56
56
  await alepha.stop();
57
57
  });
58
+
59
+ it("should increment values atomically", async ({ expect }) => {
60
+ const key = `test:incr:${randomUUID()}`;
61
+
62
+ const val1 = await redis.incr(key, 1);
63
+ expect(val1).toBe(1);
64
+
65
+ const val2 = await redis.incr(key, 5);
66
+ expect(val2).toBe(6);
67
+
68
+ const val3 = await redis.incr(key, -2);
69
+ expect(val3).toBe(4);
70
+ });
58
71
  });
@@ -18,34 +18,18 @@ export * from "./providers/RedisSubscriberProvider.ts";
18
18
  // ---------------------------------------------------------------------------------------------------------------------
19
19
 
20
20
  /**
21
- * Redis client provider for Alepha applications.
21
+ * | type | quality | stability |
22
+ * |------|---------|-----------|
23
+ * | backend | standard | stable |
22
24
  *
23
- * Automatically selects the appropriate provider based on runtime:
24
- * - Bun: Uses `BunRedisProvider` with Bun's native Redis client (7.9x faster than ioredis)
25
- * - Node.js: Uses `NodeRedisProvider` with `@redis/client`
25
+ * Redis client wrapper.
26
26
  *
27
- * @example
28
- * ```ts
29
- * // Inject the abstract provider - runtime selects the implementation
30
- * const redis = alepha.inject(RedisProvider);
27
+ * **Features:**
28
+ * - Connection pooling
29
+ * - Automatic reconnection
30
+ * - Command pipelining
31
+ * - Pub/sub support
31
32
  *
32
- * // Use common operations
33
- * await redis.set("key", "value");
34
- * const value = await redis.get("key");
35
- *
36
- * // For pub/sub
37
- * const subscriber = alepha.inject(RedisSubscriberProvider);
38
- * await subscriber.subscribe("channel", (message, channel) => {
39
- * console.log(`Received: ${message} on ${channel}`);
40
- * });
41
- * ```
42
- *
43
- * @see {@link RedisProvider} - Abstract base class
44
- * @see {@link NodeRedisProvider} - Node.js implementation
45
- * @see {@link BunRedisProvider} - Bun implementation
46
- * @see {@link RedisSubscriberProvider} - Abstract subscriber base class
47
- * @see {@link NodeRedisSubscriberProvider} - Node.js subscriber implementation
48
- * @see {@link BunRedisSubscriberProvider} - Bun subscriber implementation
49
33
  * @module alepha.redis
50
34
  */
51
35
  export const AlephaRedis = $module({
@@ -264,6 +264,15 @@ export class BunRedisProvider extends RedisProvider {
264
264
  await this.publisher.publish(channel, message);
265
265
  }
266
266
 
267
+ // ---------------------------------------------------------
268
+ // Counter operations
269
+ // ---------------------------------------------------------
270
+
271
+ public override async incr(key: string, amount: number): Promise<number> {
272
+ const result = await this.publisher.send("INCRBY", [key, String(amount)]);
273
+ return Number(result);
274
+ }
275
+
267
276
  /**
268
277
  * Get the Redis connection URL.
269
278
  */
@@ -233,6 +233,14 @@ export class NodeRedisProvider extends RedisProvider {
233
233
  await this.publisher.publish(channel, message);
234
234
  }
235
235
 
236
+ // ---------------------------------------------------------
237
+ // Counter operations
238
+ // ---------------------------------------------------------
239
+
240
+ public override async incr(key: string, amount: number): Promise<number> {
241
+ return this.publisher.INCRBY(key, amount);
242
+ }
243
+
236
244
  /**
237
245
  * Get the Redis connection URL.
238
246
  */
@@ -108,6 +108,22 @@ export abstract class RedisProvider {
108
108
  * @param message The message to publish.
109
109
  */
110
110
  public abstract publish(channel: string, message: string): Promise<void>;
111
+
112
+ // ---------------------------------------------------------
113
+ // Counter operations
114
+ // ---------------------------------------------------------
115
+
116
+ /**
117
+ * Increment the integer value of a key by the given amount.
118
+ *
119
+ * If the key does not exist, it is set to 0 before performing the operation.
120
+ * This operation is atomic.
121
+ *
122
+ * @param key The key to increment.
123
+ * @param amount The amount to increment by.
124
+ * @returns The new value after incrementing.
125
+ */
126
+ public abstract incr(key: string, amount: number): Promise<number>;
111
127
  }
112
128
 
113
129
  /**
@@ -12,9 +12,18 @@ export * from "./providers/RetryProvider.ts";
12
12
  // ---------------------------------------------------------------------------------------------------------------------
13
13
 
14
14
  /**
15
- * Retry mechanism provider for Alepha applications.
15
+ * | type | quality | stability |
16
+ * |------|---------|-----------|
17
+ * | backend | standard | stable |
18
+ *
19
+ * Automatic retry with backoff.
20
+ *
21
+ * **Features:**
22
+ * - Retry configuration
23
+ * - Exponential backoff
24
+ * - Max retry limits
25
+ * - Custom retry predicates
16
26
  *
17
- * @see {@link RetryProvider}
18
27
  * @module alepha.retry
19
28
  */
20
29
  export const AlephaRetry = $module({
@@ -1 +1,16 @@
1
+ /**
2
+ * | type | quality | stability |
3
+ * |------|---------|-----------|
4
+ * | frontend | standard | stable |
5
+ *
6
+ * Frontend routing infrastructure.
7
+ *
8
+ * **Features:**
9
+ * - Route state management
10
+ * - Navigation methods
11
+ * - Route matching
12
+ *
13
+ * @module alepha.router
14
+ */
15
+
1
16
  export * from "./providers/RouterProvider.ts";
@@ -35,9 +35,18 @@ declare module "alepha" {
35
35
  // ---------------------------------------------------------------------------------------------------------------------
36
36
 
37
37
  /**
38
- * Generic interface for scheduling tasks.
38
+ * | type | quality | stability |
39
+ * |------|---------|-----------|
40
+ * | backend | rare | stable |
41
+ *
42
+ * Cron and interval-based task execution.
43
+ *
44
+ * **Features:**
45
+ * - Scheduled tasks with cron expressions (e.g., `0 0 * * *`)
46
+ * - Interval-based scheduling
47
+ * - Distributed locking to prevent duplicate execution
48
+ * - Lifecycle hooks: `begin`, `success`, `error`, `end`
39
49
  *
40
- * @see {@link $scheduler}
41
50
  * @module alepha.scheduler
42
51
  */
43
52
  export const AlephaScheduler = $module({
@@ -24,6 +24,7 @@ describe("Basic Authentication", () => {
24
24
 
25
25
  // Action without basic auth
26
26
  publicAction = $action({
27
+ secure: false,
27
28
  handler: () => "public success",
28
29
  });
29
30
 
@@ -48,6 +49,7 @@ describe("Basic Authentication", () => {
48
49
  });
49
50
 
50
51
  customAuthAction = $action({
52
+ secure: false,
51
53
  handler: async (request) => {
52
54
  this.customAuth.check(request);
53
55
  return "custom auth success";
@@ -15,6 +15,7 @@ describe("ServerSecurityProvider", () => {
15
15
  it("should protect action from unauthorized users", async () => {
16
16
  class TestApp {
17
17
  ok = $action({
18
+ secure: true,
18
19
  handler: () => "OK",
19
20
  });
20
21
  }
@@ -23,16 +24,24 @@ describe("ServerSecurityProvider", () => {
23
24
  const app = alepha.inject(TestApp);
24
25
  await alepha.start();
25
26
 
26
- // in testing environment, .run() a dummy user is created
27
- expect(await app.ok.run({})).toBe("OK");
28
-
29
- // but you can force empty user
27
+ await expect(app.ok.run({})).rejects.toThrowError(UnauthorizedError);
30
28
  await expect(app.ok.run({}, { user: undefined })).rejects.toThrowError(
31
29
  UnauthorizedError,
32
30
  );
33
31
 
34
32
  // .fetch() will also generates a dummy user in testing environment
35
- expect(await app.ok.fetch({}).then((it) => it.data)).toBe("OK");
33
+ expect(
34
+ await app.ok
35
+ .fetch(
36
+ {},
37
+ {
38
+ user: {
39
+ id: randomUUID(),
40
+ },
41
+ },
42
+ )
43
+ .then((it) => it.data),
44
+ ).toBe("OK");
36
45
 
37
46
  // but you can also force empty user
38
47
  await expect(app.ok.fetch({}, { user: undefined })).rejects.toThrowError(
@@ -55,10 +64,12 @@ describe("ServerSecurityProvider", () => {
55
64
  it("should guard by permission", async () => {
56
65
  class TestApp {
57
66
  admin = $action({
67
+ secure: true,
58
68
  group: "read",
59
69
  handler: () => "ADMIN",
60
70
  });
61
71
  user = $action({
72
+ secure: true,
62
73
  group: "read",
63
74
  handler: () => "USER",
64
75
  });
@@ -120,4 +131,78 @@ describe("ServerSecurityProvider", () => {
120
131
  await app.admin.fetch({}, { user: admin }).then((it) => it.data),
121
132
  ).toBe("ADMIN");
122
133
  });
134
+
135
+ it("should allow public actions by default (no secure option)", async () => {
136
+ class TestApp {
137
+ public = $action({
138
+ handler: () => "PUBLIC",
139
+ });
140
+ issuer = $issuer({
141
+ secret: "test",
142
+ roles: [{ name: "user", permissions: [{ name: "*" }] }],
143
+ });
144
+ }
145
+
146
+ const alepha = Alepha.create().with(AlephaServer).with(AlephaSecurity);
147
+ const app = alepha.inject(TestApp);
148
+ await alepha.start();
149
+
150
+ // Should work without authentication via .run()
151
+ expect(await app.public.run({})).toBe("PUBLIC");
152
+
153
+ // Should work without authentication via .fetch()
154
+ expect(await app.public.fetch({}).then((it) => it.data)).toBe("PUBLIC");
155
+
156
+ // Should work via HTTP without token
157
+ const response = await fetch(
158
+ `${alepha.inject(ServerProvider).hostname}${app.public.route.path}`,
159
+ );
160
+ expect(response.status).toBe(200);
161
+ expect(await response.text()).toBe("PUBLIC");
162
+ });
163
+
164
+ it("should allow explicit secure: false", async () => {
165
+ class TestApp {
166
+ public = $action({
167
+ secure: false,
168
+ handler: () => "PUBLIC",
169
+ });
170
+ issuer = $issuer({
171
+ secret: "test",
172
+ roles: [{ name: "user", permissions: [{ name: "*" }] }],
173
+ });
174
+ }
175
+
176
+ const alepha = Alepha.create().with(AlephaServer).with(AlephaSecurity);
177
+ const app = alepha.inject(TestApp);
178
+ await alepha.start();
179
+
180
+ // Should work without authentication
181
+ expect(await app.public.run({})).toBe("PUBLIC");
182
+ expect(await app.public.fetch({}).then((it) => it.data)).toBe("PUBLIC");
183
+ });
184
+
185
+ it("should require auth when secure: true is explicit", async () => {
186
+ class TestApp {
187
+ protected = $action({
188
+ secure: true,
189
+ handler: () => "PROTECTED",
190
+ });
191
+ issuer = $issuer({
192
+ secret: "test",
193
+ roles: [{ name: "user", permissions: [{ name: "*" }] }],
194
+ });
195
+ }
196
+
197
+ const alepha = Alepha.create().with(AlephaServer).with(AlephaSecurity);
198
+ const app = alepha.inject(TestApp);
199
+ await alepha.start();
200
+
201
+ // Should fail without user
202
+ await expect(app.protected.run({})).rejects.toThrowError(UnauthorizedError);
203
+
204
+ // Should succeed with user
205
+ const user = { id: randomUUID(), roles: ["user"] };
206
+ expect(await app.protected.run({}, { user })).toBe("PROTECTED");
207
+ });
123
208
  });
@@ -15,6 +15,7 @@ import type { UserAccount } from "./schemas/userAccountInfoSchema.ts";
15
15
  export * from "./errors/InvalidCredentialsError.ts";
16
16
  export * from "./errors/InvalidPermissionError.ts";
17
17
  export * from "./errors/SecurityError.ts";
18
+ export * from "./interfaces/IssuerResolver.ts";
18
19
  export * from "./interfaces/UserAccountToken.ts";
19
20
  export * from "./primitives/$basicAuth.ts";
20
21
  export * from "./primitives/$issuer.ts";
@@ -88,19 +89,23 @@ declare module "alepha/server" {
88
89
  }
89
90
 
90
91
  /**
91
- * Provides comprehensive authentication and authorization capabilities with JWT tokens, role-based access control, and user management.
92
+ * | type | quality | stability |
93
+ * |------|---------|-----------|
94
+ * | backend | epic | stable |
92
95
  *
93
- * The security module enables building secure applications using primitives like `$issuer`, `$role`, and `$permission`
94
- * on class properties. It offers JWT-based authentication, fine-grained permissions, service accounts, and seamless
95
- * integration with various authentication providers and user management systems.
96
+ * Complete authentication and authorization system with JWT, RBAC, and multi-issuer support.
96
97
  *
97
- * When used with `AlephaServer`, this module automatically registers `ServerSecurityProvider` and `ServerBasicAuthProvider`
98
- * to protect HTTP routes and actions with JWT and Basic Auth.
98
+ * **Features:**
99
+ * - JWT token issuer with role definitions
100
+ * - Role-based access control (RBAC)
101
+ * - Fine-grained permissions
102
+ * - HTTP Basic Authentication
103
+ * - Service-to-service authentication
104
+ * - Multi-issuer support for federated auth
105
+ * - JWKS (JSON Web Key Set) for external issuers
106
+ * - Token refresh logic
107
+ * - User profile extraction from JWT
99
108
  *
100
- * @see {@link $issuer}
101
- * @see {@link $role}
102
- * @see {@link $permission}
103
- * @see {@link $basicAuth}
104
109
  * @module alepha.security
105
110
  */
106
111
  export const AlephaSecurity = $module({
@@ -0,0 +1,27 @@
1
+ import type { ServerRequest } from "alepha/server";
2
+ import type { UserAccount } from "../schemas/userAccountInfoSchema.ts";
3
+
4
+ /**
5
+ * User info that a resolver returns.
6
+ * This is the input to `SecurityProvider.createUser()`.
7
+ */
8
+ export type UserInfo = Omit<UserAccount, "sessionId"> & {
9
+ sessionId?: string;
10
+ };
11
+
12
+ /**
13
+ * Resolver definition for authenticating users from requests.
14
+ */
15
+ export interface IssuerResolver {
16
+ /**
17
+ * Priority (lower = first). Default: 100
18
+ */
19
+ priority?: number;
20
+
21
+ /**
22
+ * Resolve user from HTTP request.
23
+ * Return UserInfo if authenticated, null to try next resolver.
24
+ * Throw UnauthorizedError to stop chain.
25
+ */
26
+ onRequest: (req: ServerRequest) => Promise<UserInfo | null>;
27
+ }