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
@@ -1,4 +1,4 @@
1
- import { $inject, type Alepha } from "alepha";
1
+ import { $hook, $inject, type Alepha } from "alepha";
2
2
  import { DateTimeProvider, type DurationLike } from "alepha/datetime";
3
3
  import { $logger } from "alepha/logger";
4
4
  import { type RetryBackoffOptions, RetryProvider } from "alepha/retry";
@@ -130,6 +130,11 @@ export class BatchProvider {
130
130
  protected readonly dateTime = $inject(DateTimeProvider);
131
131
  protected readonly retryProvider = $inject(RetryProvider);
132
132
 
133
+ /**
134
+ * All active batch contexts managed by this provider.
135
+ */
136
+ protected readonly contexts = new Set<BatchContext<any, any>>();
137
+
133
138
  /**
134
139
  * Creates a new batch context with the given options.
135
140
  */
@@ -137,7 +142,7 @@ export class BatchProvider {
137
142
  alepha: Alepha,
138
143
  options: BatchOptions<TItem, TResponse>,
139
144
  ): BatchContext<TItem, TResponse> {
140
- return {
145
+ const context: BatchContext<TItem, TResponse> = {
141
146
  options,
142
147
  itemStates: new Map(),
143
148
  partitions: new Map(),
@@ -146,8 +151,30 @@ export class BatchProvider {
146
151
  isReady: false,
147
152
  alepha,
148
153
  };
154
+ this.contexts.add(context);
155
+ return context;
149
156
  }
150
157
 
158
+ /**
159
+ * Shutdown hook - flushes all batch contexts on application stop.
160
+ */
161
+ protected readonly onStop = $hook({
162
+ on: "stop",
163
+ priority: "first",
164
+ handler: async () => {
165
+ if (this.contexts.size === 0) {
166
+ return;
167
+ }
168
+ this.log.debug(`Shutting down ${this.contexts.size} batch context(s)...`);
169
+ const promises: Promise<void>[] = [];
170
+ for (const context of this.contexts) {
171
+ promises.push(this.shutdown(context));
172
+ }
173
+ await Promise.all(promises);
174
+ this.log.debug("All batch contexts shut down");
175
+ },
176
+ });
177
+
151
178
  /**
152
179
  * Get the effective maxSize for a context.
153
180
  */
@@ -1,5 +1,5 @@
1
1
  import { Alepha } from "alepha";
2
- import { FileSystemProvider } from "alepha/file";
2
+ import { FileSystemProvider } from "alepha/system";
3
3
  import { expect } from "vitest";
4
4
  import {
5
5
  $bucket,
@@ -43,14 +43,21 @@ declare module "alepha" {
43
43
  // ---------------------------------------------------------------------------------------------------------------------
44
44
 
45
45
  /**
46
- * Provides file storage capabilities through declarative bucket primitives with support for multiple storage backends.
46
+ * | type | quality | stability |
47
+ * |------|---------|-----------|
48
+ * | backend | rare | stable |
47
49
  *
48
- * The bucket module enables unified file operations across different storage systems using the `$bucket` primitive
49
- * on class properties. It abstracts storage provider differences, offering consistent APIs for local filesystem,
50
- * cloud storage, or in-memory storage for testing environments.
50
+ * Unified file storage abstraction across multiple backends.
51
+ *
52
+ * **Features:**
53
+ * - File storage buckets with constraints
54
+ * - Unified API across all storage backends
55
+ * - MIME type validation
56
+ * - File size limits
57
+ * - Upload/download/delete operations
58
+ * - TTL-based file expiration
59
+ * - Providers: Memory (testing), Local filesystem, AWS S3 / Cloudflare R2 / MinIO, Azure Blob Storage, Vercel Blob
51
60
  *
52
- * @see {@link $bucket}
53
- * @see {@link FileStorageProvider}
54
61
  * @module alepha.bucket
55
62
  */
56
63
  export const AlephaBucket = $module({
@@ -6,7 +6,7 @@ import {
6
6
  Primitive,
7
7
  type Service,
8
8
  } from "alepha";
9
- import { FileSystemProvider } from "alepha/file";
9
+ import { FileSystemProvider } from "alepha/system";
10
10
  import { InvalidFileError } from "../errors/InvalidFileError.ts";
11
11
  import { FileStorageProvider } from "../providers/FileStorageProvider.ts";
12
12
  import { MemoryFileStorageProvider } from "../providers/MemoryFileStorageProvider.ts";
@@ -15,8 +15,8 @@ import {
15
15
  type Static,
16
16
  t,
17
17
  } from "alepha";
18
- import { FileDetector, FileSystemProvider } from "alepha/file";
19
18
  import { $logger } from "alepha/logger";
19
+ import { FileDetector, FileSystemProvider } from "alepha/system";
20
20
  import { FileNotFoundError } from "../errors/FileNotFoundError.ts";
21
21
  import { $bucket } from "../primitives/$bucket.ts";
22
22
  import type { FileStorageProvider } from "./FileStorageProvider.ts";
@@ -1,6 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { $inject, type FileLike } from "alepha";
3
- import { FileDetector, FileSystemProvider } from "alepha/file";
3
+ import { FileDetector, FileSystemProvider } from "alepha/system";
4
4
  import { FileNotFoundError } from "../errors/FileNotFoundError.ts";
5
5
  import type { FileStorageProvider } from "./FileStorageProvider.ts";
6
6
 
@@ -321,6 +321,36 @@ export const testSimpleKeyMappingHandler = async (
321
321
  expect(await app.run("C")).toBe("C=3");
322
322
  };
323
323
 
324
+ export const testCacheIncr = async (
325
+ env: Env = {},
326
+ cacheProvider: Service<CacheProvider> = MemoryCacheProvider,
327
+ ): Promise<void> => {
328
+ const app = Alepha.create({
329
+ env,
330
+ }).with({
331
+ provide: CacheProvider,
332
+ use: cacheProvider,
333
+ });
334
+
335
+ const provider = app.inject(CacheProvider);
336
+ await app.start();
337
+
338
+ const name = "test-incr";
339
+ const key = `counter:${randomUUID()}`;
340
+
341
+ // First increment creates the key
342
+ const val1 = await provider.incr(name, key, 1);
343
+ expect(val1).toBe(1);
344
+
345
+ // Subsequent increments add to existing value
346
+ const val2 = await provider.incr(name, key, 5);
347
+ expect(val2).toBe(6);
348
+
349
+ // Negative amounts work (decrement)
350
+ const val3 = await provider.incr(name, key, -2);
351
+ expect(val3).toBe(4);
352
+ };
353
+
324
354
  export const testCacheProviderClear = async (
325
355
  env: Env = {},
326
356
  cacheProvider: Service<CacheProvider> = MemoryCacheProvider,
@@ -12,14 +12,19 @@ export * from "./providers/MemoryCacheProvider.ts";
12
12
  // ---------------------------------------------------------------------------------------------------------------------
13
13
 
14
14
  /**
15
- * Provides high-performance caching capabilities for Alepha applications with configurable TTL and multiple storage backends.
15
+ * | type | quality | stability |
16
+ * |------|---------|-----------|
17
+ * | backend | rare | stable |
16
18
  *
17
- * The cache module enables declarative caching through the `$cache` primitive, allowing you to cache method results,
18
- * API responses, or computed values with automatic invalidation and type safety. It supports both in-memory and
19
- * persistent storage backends for different performance and durability requirements.
19
+ * Type-safe caching with TTL support.
20
+ *
21
+ * **Features:**
22
+ * - Cached computations with type-safe keys and values
23
+ * - Configurable TTL
24
+ * - Cache invalidation
25
+ * - Automatic cache population
26
+ * - Providers: Memory (default), Redis
20
27
  *
21
- * @see {@link $cache}
22
- * @see {@link CacheProvider}
23
28
  * @module alepha.cache
24
29
  */
25
30
  export const AlephaCache = $module({
@@ -7,6 +7,7 @@ import {
7
7
  testCacheBasic,
8
8
  testCacheClear,
9
9
  testCacheDisabled,
10
+ testCacheIncr,
10
11
  testCacheInvalidateAll,
11
12
  testCacheInvalidateByArgs,
12
13
  testCacheInvalidateByKey,
@@ -108,4 +109,8 @@ describe("$cache", () => {
108
109
  it("should clear provider cache", async () => {
109
110
  await testCacheProviderClear();
110
111
  });
112
+
113
+ it("should increment values atomically", async () => {
114
+ await testCacheIncr();
115
+ });
111
116
  });
@@ -51,4 +51,21 @@ export abstract class CacheProvider {
51
51
  * Remove all keys from all cache names.
52
52
  */
53
53
  public abstract clear(): Promise<void>;
54
+
55
+ /**
56
+ * Increment the integer value of a key by the given amount.
57
+ *
58
+ * If the key does not exist, it is set to 0 before performing the operation.
59
+ * This operation is atomic when using Redis.
60
+ *
61
+ * @param name Cache name, used to group keys.
62
+ * @param key The key to increment.
63
+ * @param amount The amount to increment by.
64
+ * @returns The new value after incrementing.
65
+ */
66
+ public abstract incr(
67
+ name: string,
68
+ key: string,
69
+ amount: number,
70
+ ): Promise<number>;
54
71
  }
@@ -10,14 +10,150 @@ type CacheValue = {
10
10
  timeout?: Timeout;
11
11
  };
12
12
 
13
+ // ---------------------------------------------------------------------------------------------------------------------
14
+
15
+ export interface MemoryCacheCall {
16
+ name: string;
17
+ key: string;
18
+ timestamp: number;
19
+ }
20
+
21
+ export interface MemoryCacheSetCall extends MemoryCacheCall {
22
+ value: Uint8Array;
23
+ ttl?: number;
24
+ }
25
+
26
+ export interface MemoryCacheDelCall {
27
+ name: string;
28
+ keys: string[];
29
+ timestamp: number;
30
+ }
31
+
32
+ export interface MemoryCacheStats {
33
+ hits: number;
34
+ misses: number;
35
+ sets: number;
36
+ deletes: number;
37
+ }
38
+
39
+ export interface MemoryCacheProviderOptions {
40
+ /**
41
+ * Error to throw on get operations (for testing error handling)
42
+ */
43
+ getError?: Error | null;
44
+ /**
45
+ * Error to throw on set operations (for testing error handling)
46
+ */
47
+ setError?: Error | null;
48
+ /**
49
+ * Error to throw on del operations (for testing error handling)
50
+ */
51
+ delError?: Error | null;
52
+ }
53
+
54
+ // ---------------------------------------------------------------------------------------------------------------------
55
+
56
+ /**
57
+ * In-memory implementation of CacheProvider for testing.
58
+ *
59
+ * This provider stores all cache entries in memory, making it ideal for
60
+ * unit tests that need to verify cache operations without touching Redis or other backends.
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * // In tests, substitute the real CacheProvider with MemoryCacheProvider
65
+ * const alepha = Alepha.create().with({
66
+ * provide: CacheProvider,
67
+ * use: MemoryCacheProvider,
68
+ * });
69
+ *
70
+ * // Run code that uses caching
71
+ * const service = alepha.inject(MyService);
72
+ * await service.fetchWithCache("key");
73
+ *
74
+ * // Verify cache behavior
75
+ * const cache = alepha.inject(MemoryCacheProvider);
76
+ * expect(cache.stats().misses).toBe(1);
77
+ * await service.fetchWithCache("key");
78
+ * expect(cache.stats().hits).toBe(1);
79
+ * ```
80
+ */
13
81
  export class MemoryCacheProvider implements CacheProvider {
14
82
  protected readonly dateTimeProvider = $inject(DateTimeProvider);
15
83
  protected readonly log = $logger();
16
84
 
17
85
  protected store: Record<CacheName, Record<CacheKey, CacheValue>> = {};
18
86
 
87
+ // ─────────────────────────────────────────────────────────────────────────────
88
+ // Test tracking
89
+ // ─────────────────────────────────────────────────────────────────────────────
90
+
91
+ /**
92
+ * All recorded get calls.
93
+ */
94
+ public getCalls: MemoryCacheCall[] = [];
95
+
96
+ /**
97
+ * All recorded set calls.
98
+ */
99
+ public setCalls: MemoryCacheSetCall[] = [];
100
+
101
+ /**
102
+ * All recorded del calls.
103
+ */
104
+ public delCalls: MemoryCacheDelCall[] = [];
105
+
106
+ /**
107
+ * Cache statistics.
108
+ */
109
+ protected _stats: MemoryCacheStats = {
110
+ hits: 0,
111
+ misses: 0,
112
+ sets: 0,
113
+ deletes: 0,
114
+ };
115
+
116
+ /**
117
+ * Error to throw on get (for testing error handling)
118
+ */
119
+ public getError: Error | null = null;
120
+
121
+ /**
122
+ * Error to throw on set (for testing error handling)
123
+ */
124
+ public setError: Error | null = null;
125
+
126
+ /**
127
+ * Error to throw on del (for testing error handling)
128
+ */
129
+ public delError: Error | null = null;
130
+
131
+ constructor(options: MemoryCacheProviderOptions = {}) {
132
+ this.getError = options.getError ?? null;
133
+ this.setError = options.setError ?? null;
134
+ this.delError = options.delError ?? null;
135
+ }
136
+
137
+ // ─────────────────────────────────────────────────────────────────────────────
138
+ // CacheProvider implementation
139
+ // ─────────────────────────────────────────────────────────────────────────────
140
+
19
141
  public async get(name: string, key: string): Promise<Uint8Array | undefined> {
20
- return this.store[name]?.[key]?.data;
142
+ this.getCalls.push({ name, key, timestamp: Date.now() });
143
+
144
+ if (this.getError) {
145
+ throw this.getError;
146
+ }
147
+
148
+ const data = this.store[name]?.[key]?.data;
149
+
150
+ if (data !== undefined) {
151
+ this._stats.hits++;
152
+ } else {
153
+ this._stats.misses++;
154
+ }
155
+
156
+ return data;
21
157
  }
22
158
 
23
159
  public async set(
@@ -26,6 +162,13 @@ export class MemoryCacheProvider implements CacheProvider {
26
162
  value: Uint8Array,
27
163
  ttl?: number,
28
164
  ): Promise<Uint8Array> {
165
+ this.setCalls.push({ name, key, value, ttl, timestamp: Date.now() });
166
+ this._stats.sets++;
167
+
168
+ if (this.setError) {
169
+ throw this.setError;
170
+ }
171
+
29
172
  if (this.store[name] == null) {
30
173
  this.store[name] = {};
31
174
  }
@@ -52,6 +195,13 @@ export class MemoryCacheProvider implements CacheProvider {
52
195
  }
53
196
 
54
197
  public async del(name: string, ...keys: string[]): Promise<void> {
198
+ this.delCalls.push({ name, keys, timestamp: Date.now() });
199
+ this._stats.deletes++;
200
+
201
+ if (this.delError) {
202
+ throw this.delError;
203
+ }
204
+
55
205
  // delete all keys in name
56
206
  if (keys.length === 0) {
57
207
  this.log.debug(`Deleting all cache for name`, { name });
@@ -116,4 +266,153 @@ export class MemoryCacheProvider implements CacheProvider {
116
266
 
117
267
  this.store = {};
118
268
  }
269
+
270
+ public async incr(
271
+ name: string,
272
+ key: string,
273
+ amount: number,
274
+ ): Promise<number> {
275
+ if (this.store[name] == null) {
276
+ this.store[name] = {};
277
+ }
278
+
279
+ const existing = this.store[name][key]?.data;
280
+ let current = 0;
281
+
282
+ if (existing) {
283
+ const str = new TextDecoder().decode(existing);
284
+ current = Number.parseInt(str, 10) || 0;
285
+ }
286
+
287
+ const newValue = current + amount;
288
+ this.store[name][key] ??= {};
289
+ this.store[name][key].data = new TextEncoder().encode(String(newValue));
290
+
291
+ return newValue;
292
+ }
293
+
294
+ // ─────────────────────────────────────────────────────────────────────────────
295
+ // Test utilities
296
+ // ─────────────────────────────────────────────────────────────────────────────
297
+
298
+ /**
299
+ * Get cache statistics (hits, misses, sets, deletes).
300
+ *
301
+ * @example
302
+ * ```typescript
303
+ * expect(cache.stats().hits).toBe(1);
304
+ * expect(cache.stats().misses).toBe(0);
305
+ * ```
306
+ */
307
+ public stats(): MemoryCacheStats {
308
+ return { ...this._stats };
309
+ }
310
+
311
+ /**
312
+ * Check if a key was set during the test.
313
+ *
314
+ * @example
315
+ * ```typescript
316
+ * expect(cache.wasSet("my-cache", "user:123")).toBe(true);
317
+ * ```
318
+ */
319
+ public wasSet(name: string, key?: string): boolean {
320
+ if (key === undefined) {
321
+ return this.setCalls.some((call) => call.name === name);
322
+ }
323
+ return this.setCalls.some((call) => call.name === name && call.key === key);
324
+ }
325
+
326
+ /**
327
+ * Check if a key was retrieved during the test.
328
+ *
329
+ * @example
330
+ * ```typescript
331
+ * expect(cache.wasGet("my-cache", "user:123")).toBe(true);
332
+ * ```
333
+ */
334
+ public wasGet(name: string, key?: string): boolean {
335
+ if (key === undefined) {
336
+ return this.getCalls.some((call) => call.name === name);
337
+ }
338
+ return this.getCalls.some((call) => call.name === name && call.key === key);
339
+ }
340
+
341
+ /**
342
+ * Check if a key was deleted during the test.
343
+ *
344
+ * @example
345
+ * ```typescript
346
+ * expect(cache.wasDeleted("my-cache", "user:123")).toBe(true);
347
+ * ```
348
+ */
349
+ public wasDeleted(name: string, key?: string): boolean {
350
+ if (key === undefined) {
351
+ return this.delCalls.some((call) => call.name === name);
352
+ }
353
+ return this.delCalls.some(
354
+ (call) => call.name === name && call.keys.includes(key),
355
+ );
356
+ }
357
+
358
+ /**
359
+ * Get the number of cached entries for a specific cache name.
360
+ *
361
+ * @example
362
+ * ```typescript
363
+ * expect(cache.size("my-cache")).toBe(5);
364
+ * ```
365
+ */
366
+ public size(name?: string): number {
367
+ if (name === undefined) {
368
+ return Object.values(this.store).reduce(
369
+ (total, entries) => total + Object.keys(entries).length,
370
+ 0,
371
+ );
372
+ }
373
+ return Object.keys(this.store[name] ?? {}).length;
374
+ }
375
+
376
+ /**
377
+ * Get all cache names.
378
+ *
379
+ * @example
380
+ * ```typescript
381
+ * expect(cache.names()).toContain("my-cache");
382
+ * ```
383
+ */
384
+ public names(): string[] {
385
+ return Object.keys(this.store);
386
+ }
387
+
388
+ /**
389
+ * Reset all in-memory state (useful between tests).
390
+ *
391
+ * @example
392
+ * ```typescript
393
+ * beforeEach(() => {
394
+ * cache.reset();
395
+ * });
396
+ * ```
397
+ */
398
+ public reset(): void {
399
+ // Clear all timeouts
400
+ for (const name of Object.keys(this.store)) {
401
+ for (const key of Object.keys(this.store[name])) {
402
+ const timeout = this.store[name][key]?.timeout;
403
+ if (timeout) {
404
+ this.dateTimeProvider.clearTimeout(timeout);
405
+ }
406
+ }
407
+ }
408
+
409
+ this.store = {};
410
+ this.getCalls = [];
411
+ this.setCalls = [];
412
+ this.delCalls = [];
413
+ this._stats = { hits: 0, misses: 0, sets: 0, deletes: 0 };
414
+ this.getError = null;
415
+ this.setError = null;
416
+ this.delError = null;
417
+ }
119
418
  }
@@ -4,6 +4,7 @@ import {
4
4
  testCacheBasic,
5
5
  testCacheClear,
6
6
  testCacheDisabled,
7
+ testCacheIncr,
7
8
  testCacheInvalidateAll,
8
9
  testCacheInvalidateByArgs,
9
10
  testCacheInvalidateByKey,
@@ -67,4 +68,8 @@ describe("$cache - redis", () => {
67
68
  it("should clear provider cache", async () => {
68
69
  await testCacheProviderClear(env(), provider);
69
70
  });
71
+
72
+ it("should increment values atomically", async () => {
73
+ await testCacheIncr(env(), provider);
74
+ });
70
75
  });
@@ -99,6 +99,15 @@ export class RedisCacheProvider implements CacheProvider {
99
99
  await this.redisProvider.del(keys);
100
100
  }
101
101
 
102
+ public async incr(
103
+ name: string,
104
+ key: string,
105
+ amount: number,
106
+ ): Promise<number> {
107
+ const keyWithPrefix = this.prefix(name, key);
108
+ return this.redisProvider.incr(keyWithPrefix, amount);
109
+ }
110
+
102
111
  protected prefix(...path: string[]): string {
103
112
  const parts = ["cache", ...path];
104
113
 
@@ -1,5 +1,5 @@
1
1
  import { $hook, $inject, $module, Alepha } from "alepha";
2
- import { FileSystemProvider } from "alepha/file";
2
+ import { FileSystemProvider } from "alepha/system";
3
3
  import { BuildCommand } from "../commands/build.ts";
4
4
  import { CleanCommand } from "../commands/clean.ts";
5
5
  import { DbCommand } from "../commands/db.ts";
@@ -17,19 +17,6 @@ import { AppEntryProvider } from "../providers/AppEntryProvider.ts";
17
17
 
18
18
  // ---------------------------------------------------------------------------------------------------------------------
19
19
 
20
- /**
21
- * Register `tsx` when running in Node.js, ignore for Bun.
22
- *
23
- * It's required to have a full TypeScript support. (mostly .tsx files)
24
- */
25
-
26
- if (typeof Bun === "undefined") {
27
- const { register } = await import("tsx/esm/api");
28
- register();
29
- }
30
-
31
- // ---------------------------------------------------------------------------------------------------------------------
32
-
33
20
  /**
34
21
  * Allow to extend Alepha CLI via `alepha.config.ts` file located in the project root.
35
22
  */
@@ -3,7 +3,7 @@ import * as os from "node:os";
3
3
  import { join } from "node:path";
4
4
  import { $inject, AlephaError } from "alepha";
5
5
  import { $command } from "alepha/command";
6
- import { FileSystemProvider } from "alepha/file";
6
+ import { FileSystemProvider } from "alepha/system";
7
7
  import type { InlineConfig } from "tsdown";
8
8
 
9
9
  interface Module {
@@ -11,6 +11,7 @@ interface Module {
11
11
  dependencies: string[];
12
12
  native?: boolean;
13
13
  browser?: boolean;
14
+ workerd?: boolean;
14
15
  bun?: boolean;
15
16
  node?: boolean;
16
17
  }
@@ -53,6 +54,10 @@ export class AlephaPackageBuilderCli {
53
54
  `./src/${item.name}/index.browser.ts`;
54
55
  }
55
56
 
57
+ if (item.workerd) {
58
+ pkgData.exports[path].workerd = `./src/${item.name}/index.workerd.ts`;
59
+ }
60
+
56
61
  if (item.browser) {
57
62
  pkgData.exports[path].browser = `./src/${item.name}/index.browser.ts`;
58
63
  }
@@ -321,6 +326,9 @@ export async function analyzeModules(
321
326
  );
322
327
  const hasBun = await fileExists(join(modulePath, "index.bun.ts"));
323
328
  const hasNode = await fileExists(join(modulePath, "index.node.ts"));
329
+ const hasEdge = await fileExists(
330
+ join(modulePath, "index.workerd.ts"),
331
+ );
324
332
 
325
333
  // Get all .ts/.tsx files in this module
326
334
  const files = await getAllFiles(modulePath);
@@ -353,6 +361,7 @@ export async function analyzeModules(
353
361
  };
354
362
 
355
363
  if (hasNative) module.native = true;
364
+ if (hasEdge) module.workerd = true;
356
365
  if (hasBrowser) module.browser = true;
357
366
  if (hasBun) module.bun = true;
358
367
  if (hasNode) module.node = true;