alepha 0.15.1 → 0.15.2

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 (507) hide show
  1. package/dist/api/audits/index.d.ts +342 -365
  2. package/dist/api/audits/index.d.ts.map +1 -1
  3. package/dist/api/audits/index.js +10 -33
  4. package/dist/api/audits/index.js.map +1 -1
  5. package/dist/api/files/index.d.ts +180 -173
  6. package/dist/api/files/index.d.ts.map +1 -1
  7. package/dist/api/files/index.js +10 -3
  8. package/dist/api/files/index.js.map +1 -1
  9. package/dist/api/jobs/index.d.ts +162 -155
  10. package/dist/api/jobs/index.d.ts.map +1 -1
  11. package/dist/api/jobs/index.js +10 -3
  12. package/dist/api/jobs/index.js.map +1 -1
  13. package/dist/api/keys/index.d.ts +413 -0
  14. package/dist/api/keys/index.d.ts.map +1 -0
  15. package/dist/api/keys/index.js +476 -0
  16. package/dist/api/keys/index.js.map +1 -0
  17. package/dist/api/notifications/index.d.ts +10 -4
  18. package/dist/api/notifications/index.d.ts.map +1 -1
  19. package/dist/api/notifications/index.js +10 -4
  20. package/dist/api/notifications/index.js.map +1 -1
  21. package/dist/api/parameters/index.d.ts +294 -301
  22. package/dist/api/parameters/index.d.ts.map +1 -1
  23. package/dist/api/parameters/index.js +30 -37
  24. package/dist/api/parameters/index.js.map +1 -1
  25. package/dist/api/users/index.d.ts +1079 -769
  26. package/dist/api/users/index.d.ts.map +1 -1
  27. package/dist/api/users/index.js +2534 -218
  28. package/dist/api/users/index.js.map +1 -1
  29. package/dist/api/verifications/index.d.ts +10 -4
  30. package/dist/api/verifications/index.d.ts.map +1 -1
  31. package/dist/api/verifications/index.js +12 -4
  32. package/dist/api/verifications/index.js.map +1 -1
  33. package/dist/batch/index.d.ts +20 -40
  34. package/dist/batch/index.d.ts.map +1 -1
  35. package/dist/batch/index.js +31 -44
  36. package/dist/batch/index.js.map +1 -1
  37. package/dist/bucket/index.d.ts +432 -8
  38. package/dist/bucket/index.d.ts.map +1 -1
  39. package/dist/bucket/index.js +1856 -12
  40. package/dist/bucket/index.js.map +1 -1
  41. package/dist/cache/core/index.d.ts +179 -7
  42. package/dist/cache/core/index.d.ts.map +1 -1
  43. package/dist/cache/core/index.js +213 -7
  44. package/dist/cache/core/index.js.map +1 -1
  45. package/dist/cache/redis/index.d.ts +1 -0
  46. package/dist/cache/redis/index.d.ts.map +1 -1
  47. package/dist/cache/redis/index.js +4 -0
  48. package/dist/cache/redis/index.js.map +1 -1
  49. package/dist/cli/index.d.ts +488 -5612
  50. package/dist/cli/index.d.ts.map +1 -1
  51. package/dist/cli/index.js +2326 -311
  52. package/dist/cli/index.js.map +1 -1
  53. package/dist/command/index.d.ts +194 -46
  54. package/dist/command/index.d.ts.map +1 -1
  55. package/dist/command/index.js +1995 -60
  56. package/dist/command/index.js.map +1 -1
  57. package/dist/core/index.browser.js +42 -19
  58. package/dist/core/index.browser.js.map +1 -1
  59. package/dist/core/index.d.ts +34 -13
  60. package/dist/core/index.d.ts.map +1 -1
  61. package/dist/core/index.js +62 -19
  62. package/dist/core/index.js.map +1 -1
  63. package/dist/core/index.native.js +42 -19
  64. package/dist/core/index.native.js.map +1 -1
  65. package/dist/datetime/index.d.ts +15 -0
  66. package/dist/datetime/index.d.ts.map +1 -1
  67. package/dist/datetime/index.js +15 -0
  68. package/dist/datetime/index.js.map +1 -1
  69. package/dist/email/index.d.ts +315 -20
  70. package/dist/email/index.d.ts.map +1 -1
  71. package/dist/email/index.js +1852 -7
  72. package/dist/email/index.js.map +1 -1
  73. package/dist/fake/index.d.ts +90 -8
  74. package/dist/fake/index.d.ts.map +1 -1
  75. package/dist/fake/index.js +91 -20
  76. package/dist/fake/index.js.map +1 -1
  77. package/dist/lock/core/index.d.ts +11 -4
  78. package/dist/lock/core/index.d.ts.map +1 -1
  79. package/dist/lock/core/index.js +11 -4
  80. package/dist/lock/core/index.js.map +1 -1
  81. package/dist/logger/index.d.ts +17 -66
  82. package/dist/logger/index.d.ts.map +1 -1
  83. package/dist/logger/index.js +14 -63
  84. package/dist/logger/index.js.map +1 -1
  85. package/dist/mcp/index.d.ts +15 -35
  86. package/dist/mcp/index.d.ts.map +1 -1
  87. package/dist/mcp/index.js +12 -35
  88. package/dist/mcp/index.js.map +1 -1
  89. package/dist/orm/index.browser.js +3 -3
  90. package/dist/orm/index.browser.js.map +1 -1
  91. package/dist/orm/index.bun.js +7 -4
  92. package/dist/orm/index.bun.js.map +1 -1
  93. package/dist/orm/index.d.ts +514 -540
  94. package/dist/orm/index.d.ts.map +1 -1
  95. package/dist/orm/index.js +24 -49
  96. package/dist/orm/index.js.map +1 -1
  97. package/dist/queue/core/index.d.ts +18 -10
  98. package/dist/queue/core/index.d.ts.map +1 -1
  99. package/dist/queue/core/index.js +14 -6
  100. package/dist/queue/core/index.js.map +1 -1
  101. package/dist/react/auth/index.browser.js +108 -0
  102. package/dist/react/auth/index.browser.js.map +1 -0
  103. package/dist/react/auth/index.d.ts +100 -0
  104. package/dist/react/auth/index.d.ts.map +1 -0
  105. package/dist/react/auth/index.js +145 -0
  106. package/dist/react/auth/index.js.map +1 -0
  107. package/dist/react/core/index.d.ts +469 -0
  108. package/dist/react/core/index.d.ts.map +1 -0
  109. package/dist/react/core/index.js +464 -0
  110. package/dist/react/core/index.js.map +1 -0
  111. package/dist/react/form/index.d.ts +232 -0
  112. package/dist/react/form/index.d.ts.map +1 -0
  113. package/dist/react/form/index.js +432 -0
  114. package/dist/react/form/index.js.map +1 -0
  115. package/dist/react/head/index.browser.js +423 -0
  116. package/dist/react/head/index.browser.js.map +1 -0
  117. package/dist/react/head/index.d.ts +288 -0
  118. package/dist/react/head/index.d.ts.map +1 -0
  119. package/dist/react/head/index.js +465 -0
  120. package/dist/react/head/index.js.map +1 -0
  121. package/dist/react/i18n/index.d.ts +175 -0
  122. package/dist/react/i18n/index.d.ts.map +1 -0
  123. package/dist/react/i18n/index.js +224 -0
  124. package/dist/react/i18n/index.js.map +1 -0
  125. package/dist/react/router/index.browser.js +1980 -0
  126. package/dist/react/router/index.browser.js.map +1 -0
  127. package/dist/react/router/index.d.ts +2068 -0
  128. package/dist/react/router/index.d.ts.map +1 -0
  129. package/dist/react/router/index.js +4932 -0
  130. package/dist/react/router/index.js.map +1 -0
  131. package/dist/react/websocket/index.d.ts +117 -0
  132. package/dist/react/websocket/index.d.ts.map +1 -0
  133. package/dist/react/websocket/index.js +107 -0
  134. package/dist/react/websocket/index.js.map +1 -0
  135. package/dist/redis/index.bun.js +4 -0
  136. package/dist/redis/index.bun.js.map +1 -1
  137. package/dist/redis/index.d.ts +22 -25
  138. package/dist/redis/index.d.ts.map +1 -1
  139. package/dist/redis/index.js +16 -25
  140. package/dist/redis/index.js.map +1 -1
  141. package/dist/retry/index.d.ts +11 -2
  142. package/dist/retry/index.d.ts.map +1 -1
  143. package/dist/retry/index.js +11 -2
  144. package/dist/retry/index.js.map +1 -1
  145. package/dist/scheduler/index.d.ts +11 -2
  146. package/dist/scheduler/index.d.ts.map +1 -1
  147. package/dist/scheduler/index.js +11 -2
  148. package/dist/scheduler/index.js.map +1 -1
  149. package/dist/security/index.d.ts +110 -19
  150. package/dist/security/index.d.ts.map +1 -1
  151. package/dist/security/index.js +157 -26
  152. package/dist/security/index.js.map +1 -1
  153. package/dist/server/auth/index.d.ts +179 -174
  154. package/dist/server/auth/index.d.ts.map +1 -1
  155. package/dist/server/auth/index.js +12 -7
  156. package/dist/server/auth/index.js.map +1 -1
  157. package/dist/server/cache/index.d.ts +7 -22
  158. package/dist/server/cache/index.d.ts.map +1 -1
  159. package/dist/server/cache/index.js +7 -22
  160. package/dist/server/cache/index.js.map +1 -1
  161. package/dist/server/compress/index.d.ts +10 -2
  162. package/dist/server/compress/index.d.ts.map +1 -1
  163. package/dist/server/compress/index.js +10 -2
  164. package/dist/server/compress/index.js.map +1 -1
  165. package/dist/server/cookies/index.d.ts +40 -16
  166. package/dist/server/cookies/index.d.ts.map +1 -1
  167. package/dist/server/cookies/index.js +7 -5
  168. package/dist/server/cookies/index.js.map +1 -1
  169. package/dist/server/core/index.d.ts +115 -14
  170. package/dist/server/core/index.d.ts.map +1 -1
  171. package/dist/server/core/index.js +231 -14
  172. package/dist/server/core/index.js.map +1 -1
  173. package/dist/server/cors/index.d.ts +13 -23
  174. package/dist/server/cors/index.d.ts.map +1 -1
  175. package/dist/server/cors/index.js +7 -21
  176. package/dist/server/cors/index.js.map +1 -1
  177. package/dist/server/health/index.d.ts +25 -19
  178. package/dist/server/health/index.d.ts.map +1 -1
  179. package/dist/server/health/index.js +8 -2
  180. package/dist/server/health/index.js.map +1 -1
  181. package/dist/server/helmet/index.d.ts +11 -3
  182. package/dist/server/helmet/index.d.ts.map +1 -1
  183. package/dist/server/helmet/index.js +11 -3
  184. package/dist/server/helmet/index.js.map +1 -1
  185. package/dist/server/links/index.d.ts +50 -45
  186. package/dist/server/links/index.d.ts.map +1 -1
  187. package/dist/server/links/index.js +11 -6
  188. package/dist/server/links/index.js.map +1 -1
  189. package/dist/server/metrics/index.d.ts +10 -3
  190. package/dist/server/metrics/index.d.ts.map +1 -1
  191. package/dist/server/metrics/index.js +10 -3
  192. package/dist/server/metrics/index.js.map +1 -1
  193. package/dist/server/multipart/index.d.ts +9 -3
  194. package/dist/server/multipart/index.d.ts.map +1 -1
  195. package/dist/server/multipart/index.js +9 -3
  196. package/dist/server/multipart/index.js.map +1 -1
  197. package/dist/server/proxy/index.d.ts +8 -2
  198. package/dist/server/proxy/index.d.ts.map +1 -1
  199. package/dist/server/proxy/index.js +8 -2
  200. package/dist/server/proxy/index.js.map +1 -1
  201. package/dist/server/rate-limit/index.d.ts +30 -35
  202. package/dist/server/rate-limit/index.d.ts.map +1 -1
  203. package/dist/server/rate-limit/index.js +18 -55
  204. package/dist/server/rate-limit/index.js.map +1 -1
  205. package/dist/server/static/index.d.ts +137 -4
  206. package/dist/server/static/index.d.ts.map +1 -1
  207. package/dist/server/static/index.js +1848 -5
  208. package/dist/server/static/index.js.map +1 -1
  209. package/dist/server/swagger/index.d.ts +301 -6
  210. package/dist/server/swagger/index.d.ts.map +1 -1
  211. package/dist/server/swagger/index.js +1849 -6
  212. package/dist/server/swagger/index.js.map +1 -1
  213. package/dist/sms/index.d.ts +301 -7
  214. package/dist/sms/index.d.ts.map +1 -1
  215. package/dist/sms/index.js +1851 -7
  216. package/dist/sms/index.js.map +1 -1
  217. package/dist/system/index.browser.js +496 -0
  218. package/dist/system/index.browser.js.map +1 -0
  219. package/dist/{file → system}/index.d.ts +335 -16
  220. package/dist/system/index.d.ts.map +1 -0
  221. package/dist/{file → system}/index.js +412 -20
  222. package/dist/system/index.js.map +1 -0
  223. package/dist/thread/index.d.ts +11 -2
  224. package/dist/thread/index.d.ts.map +1 -1
  225. package/dist/thread/index.js +11 -2
  226. package/dist/thread/index.js.map +1 -1
  227. package/dist/topic/core/index.d.ts +12 -5
  228. package/dist/topic/core/index.d.ts.map +1 -1
  229. package/dist/topic/core/index.js +12 -5
  230. package/dist/topic/core/index.js.map +1 -1
  231. package/dist/vite/index.d.ts +4 -6271
  232. package/dist/vite/index.d.ts.map +1 -1
  233. package/dist/vite/index.js +8 -3
  234. package/dist/vite/index.js.map +1 -1
  235. package/dist/websocket/index.d.ts +12 -8
  236. package/dist/websocket/index.d.ts.map +1 -1
  237. package/dist/websocket/index.js +12 -8
  238. package/dist/websocket/index.js.map +1 -1
  239. package/package.json +80 -11
  240. package/src/api/audits/index.ts +10 -33
  241. package/src/api/files/__tests__/$bucket.spec.ts +1 -1
  242. package/src/api/files/controllers/AdminFileStatsController.spec.ts +1 -1
  243. package/src/api/files/controllers/FileController.spec.ts +1 -1
  244. package/src/api/files/index.ts +10 -3
  245. package/src/api/files/jobs/FileJobs.spec.ts +1 -1
  246. package/src/api/files/services/FileService.spec.ts +1 -1
  247. package/src/api/jobs/index.ts +10 -3
  248. package/src/api/keys/controllers/AdminApiKeyController.ts +75 -0
  249. package/src/api/keys/controllers/ApiKeyController.ts +103 -0
  250. package/src/api/keys/entities/apiKeyEntity.ts +41 -0
  251. package/src/api/keys/index.ts +49 -0
  252. package/src/api/keys/schemas/adminApiKeyQuerySchema.ts +7 -0
  253. package/src/api/keys/schemas/adminApiKeyResourceSchema.ts +17 -0
  254. package/src/api/keys/schemas/createApiKeyBodySchema.ts +7 -0
  255. package/src/api/keys/schemas/createApiKeyResponseSchema.ts +11 -0
  256. package/src/api/keys/schemas/listApiKeyResponseSchema.ts +15 -0
  257. package/src/api/keys/schemas/revokeApiKeyParamsSchema.ts +5 -0
  258. package/src/api/keys/schemas/revokeApiKeyResponseSchema.ts +5 -0
  259. package/src/api/keys/services/ApiKeyService.spec.ts +553 -0
  260. package/src/api/keys/services/ApiKeyService.ts +306 -0
  261. package/src/api/logs/TODO.md +52 -0
  262. package/src/api/notifications/index.ts +10 -4
  263. package/src/api/parameters/index.ts +9 -30
  264. package/src/api/parameters/primitives/$config.ts +12 -4
  265. package/src/api/parameters/services/ConfigStore.ts +9 -3
  266. package/src/api/users/__tests__/ApiKeys-integration.spec.ts +1035 -0
  267. package/src/api/users/__tests__/ApiKeys.spec.ts +401 -0
  268. package/src/api/users/index.ts +14 -3
  269. package/src/api/users/primitives/$realm.ts +33 -5
  270. package/src/api/users/providers/RealmProvider.ts +1 -12
  271. package/src/api/users/services/SessionService.ts +1 -1
  272. package/src/api/verifications/controllers/VerificationController.ts +2 -0
  273. package/src/api/verifications/index.ts +10 -4
  274. package/src/batch/index.ts +9 -36
  275. package/src/batch/primitives/$batch.ts +0 -8
  276. package/src/batch/providers/BatchProvider.ts +29 -2
  277. package/src/bucket/__tests__/shared.ts +1 -1
  278. package/src/bucket/index.ts +13 -6
  279. package/src/bucket/primitives/$bucket.ts +1 -1
  280. package/src/bucket/providers/LocalFileStorageProvider.ts +1 -1
  281. package/src/bucket/providers/MemoryFileStorageProvider.ts +1 -1
  282. package/src/cache/core/__tests__/shared.ts +30 -0
  283. package/src/cache/core/index.ts +11 -6
  284. package/src/cache/core/primitives/$cache.spec.ts +5 -0
  285. package/src/cache/core/providers/CacheProvider.ts +17 -0
  286. package/src/cache/core/providers/MemoryCacheProvider.ts +300 -1
  287. package/src/cache/redis/__tests__/cache-redis.spec.ts +5 -0
  288. package/src/cache/redis/providers/RedisCacheProvider.ts +9 -0
  289. package/src/cli/apps/AlephaCli.ts +1 -14
  290. package/src/cli/apps/AlephaPackageBuilderCli.ts +1 -1
  291. package/src/cli/commands/build.ts +1 -5
  292. package/src/cli/commands/db.ts +17 -11
  293. package/src/cli/commands/deploy.ts +1 -1
  294. package/src/cli/commands/dev.ts +1 -20
  295. package/src/cli/commands/gen/env.ts +5 -2
  296. package/src/cli/commands/gen/openapi.ts +5 -2
  297. package/src/cli/commands/init.spec.ts +544 -0
  298. package/src/cli/commands/init.ts +89 -55
  299. package/src/cli/commands/lint.ts +7 -1
  300. package/src/cli/commands/typecheck.ts +11 -0
  301. package/src/cli/providers/AppEntryProvider.ts +1 -1
  302. package/src/cli/providers/ViteBuildProvider.ts +8 -50
  303. package/src/cli/providers/ViteDevServerProvider.ts +36 -8
  304. package/src/cli/services/AlephaCliUtils.ts +37 -122
  305. package/src/cli/services/PackageManagerUtils.ts +127 -11
  306. package/src/cli/services/ProjectScaffolder.ts +122 -77
  307. package/src/cli/services/ViteUtils.ts +82 -0
  308. package/src/cli/{assets/claudeMd.ts → templates/agentMd.ts} +32 -24
  309. package/src/cli/templates/gitignore.ts +39 -0
  310. package/src/cli/{assets → templates}/mainCss.ts +11 -2
  311. package/src/cli/templates/mainServerTs.ts +33 -0
  312. package/src/cli/templates/webAppRouterTs.ts +50 -0
  313. package/src/cli/{assets → templates}/webHelloComponentTsx.ts +2 -2
  314. package/src/command/helpers/Runner.spec.ts +4 -0
  315. package/src/command/helpers/Runner.ts +3 -21
  316. package/src/command/index.ts +12 -4
  317. package/src/command/providers/CliProvider.spec.ts +1067 -0
  318. package/src/command/providers/CliProvider.ts +203 -40
  319. package/src/core/Alepha.ts +2 -2
  320. package/src/core/__tests__/Alepha-start.spec.ts +4 -4
  321. package/src/core/helpers/jsonSchemaToTypeBox.spec.ts +771 -0
  322. package/src/core/helpers/jsonSchemaToTypeBox.ts +62 -10
  323. package/src/core/index.shared.ts +1 -0
  324. package/src/core/index.ts +20 -0
  325. package/src/core/providers/EventManager.spec.ts +0 -71
  326. package/src/core/providers/EventManager.ts +3 -15
  327. package/src/core/providers/Json.ts +2 -14
  328. package/src/datetime/index.ts +15 -0
  329. package/src/email/index.ts +10 -5
  330. package/src/email/providers/LocalEmailProvider.spec.ts +1 -1
  331. package/src/email/providers/LocalEmailProvider.ts +1 -1
  332. package/src/fake/__tests__/keyName.example.ts +1 -1
  333. package/src/fake/__tests__/keyName.spec.ts +5 -5
  334. package/src/fake/index.ts +9 -6
  335. package/src/fake/providers/FakeProvider.spec.ts +258 -40
  336. package/src/fake/providers/FakeProvider.ts +133 -19
  337. package/src/lock/core/index.ts +11 -4
  338. package/src/logger/index.ts +17 -66
  339. package/src/mcp/index.ts +10 -27
  340. package/src/mcp/transports/SseMcpTransport.ts +0 -11
  341. package/src/orm/__tests__/PostgresProvider.spec.ts +2 -2
  342. package/src/orm/index.browser.ts +2 -2
  343. package/src/orm/index.bun.ts +4 -2
  344. package/src/orm/index.ts +21 -47
  345. package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -0
  346. package/src/orm/services/Repository.ts +7 -3
  347. package/src/queue/core/index.ts +14 -6
  348. package/src/react/auth/__tests__/$auth.spec.ts +202 -0
  349. package/src/react/auth/hooks/useAuth.ts +32 -0
  350. package/src/react/auth/index.browser.ts +13 -0
  351. package/src/react/auth/index.shared.ts +2 -0
  352. package/src/react/auth/index.ts +48 -0
  353. package/src/react/auth/providers/ReactAuthProvider.ts +16 -0
  354. package/src/react/auth/services/ReactAuth.ts +135 -0
  355. package/src/react/core/__tests__/Router.spec.tsx +169 -0
  356. package/src/react/core/components/ClientOnly.tsx +49 -0
  357. package/src/react/core/components/ErrorBoundary.tsx +73 -0
  358. package/src/react/core/contexts/AlephaContext.ts +7 -0
  359. package/src/react/core/contexts/AlephaProvider.tsx +42 -0
  360. package/src/react/core/hooks/useAction.browser.spec.tsx +569 -0
  361. package/src/react/core/hooks/useAction.ts +480 -0
  362. package/src/react/core/hooks/useAlepha.ts +26 -0
  363. package/src/react/core/hooks/useClient.ts +17 -0
  364. package/src/react/core/hooks/useEvents.ts +51 -0
  365. package/src/react/core/hooks/useInject.ts +12 -0
  366. package/src/react/core/hooks/useStore.ts +52 -0
  367. package/src/react/core/index.ts +90 -0
  368. package/src/react/form/components/FormState.tsx +17 -0
  369. package/src/react/form/errors/FormValidationError.ts +18 -0
  370. package/src/react/form/hooks/useForm.browser.spec.tsx +366 -0
  371. package/src/react/form/hooks/useForm.ts +47 -0
  372. package/src/react/form/hooks/useFormState.ts +130 -0
  373. package/src/react/form/index.ts +44 -0
  374. package/src/react/form/services/FormModel.ts +614 -0
  375. package/src/react/head/helpers/SeoExpander.spec.ts +203 -0
  376. package/src/react/head/helpers/SeoExpander.ts +142 -0
  377. package/src/react/head/hooks/useHead.spec.tsx +288 -0
  378. package/src/react/head/hooks/useHead.ts +62 -0
  379. package/src/react/head/index.browser.ts +26 -0
  380. package/src/react/head/index.ts +44 -0
  381. package/src/react/head/interfaces/Head.ts +105 -0
  382. package/src/react/head/primitives/$head.ts +25 -0
  383. package/src/react/head/providers/BrowserHeadProvider.browser.spec.ts +196 -0
  384. package/src/react/head/providers/BrowserHeadProvider.ts +212 -0
  385. package/src/react/head/providers/HeadProvider.ts +168 -0
  386. package/src/react/head/providers/ServerHeadProvider.ts +31 -0
  387. package/src/react/i18n/__tests__/integration.spec.tsx +239 -0
  388. package/src/react/i18n/components/Localize.spec.tsx +357 -0
  389. package/src/react/i18n/components/Localize.tsx +35 -0
  390. package/src/react/i18n/hooks/useI18n.browser.spec.tsx +438 -0
  391. package/src/react/i18n/hooks/useI18n.ts +18 -0
  392. package/src/react/i18n/index.ts +41 -0
  393. package/src/react/i18n/primitives/$dictionary.ts +69 -0
  394. package/src/react/i18n/providers/I18nProvider.spec.ts +389 -0
  395. package/src/react/i18n/providers/I18nProvider.ts +278 -0
  396. package/src/react/router/__tests__/page-head-browser.browser.spec.ts +95 -0
  397. package/src/react/router/__tests__/page-head.spec.ts +48 -0
  398. package/src/react/router/__tests__/seo-head.spec.ts +125 -0
  399. package/src/react/router/atoms/ssrManifestAtom.ts +58 -0
  400. package/src/react/router/components/ErrorViewer.tsx +872 -0
  401. package/src/react/router/components/Link.tsx +23 -0
  402. package/src/react/router/components/NestedView.tsx +223 -0
  403. package/src/react/router/components/NotFound.tsx +30 -0
  404. package/src/react/router/constants/PAGE_PRELOAD_KEY.ts +6 -0
  405. package/src/react/router/contexts/RouterLayerContext.ts +12 -0
  406. package/src/react/router/errors/Redirection.ts +28 -0
  407. package/src/react/router/hooks/useActive.ts +52 -0
  408. package/src/react/router/hooks/useQueryParams.ts +63 -0
  409. package/src/react/router/hooks/useRouter.ts +20 -0
  410. package/src/react/router/hooks/useRouterState.ts +11 -0
  411. package/src/react/router/index.browser.ts +45 -0
  412. package/src/react/router/index.shared.ts +19 -0
  413. package/src/react/router/index.ts +142 -0
  414. package/src/react/router/primitives/$page.browser.spec.tsx +851 -0
  415. package/src/react/router/primitives/$page.spec.tsx +708 -0
  416. package/src/react/router/primitives/$page.ts +497 -0
  417. package/src/react/router/providers/ReactBrowserProvider.ts +309 -0
  418. package/src/react/router/providers/ReactBrowserRendererProvider.ts +25 -0
  419. package/src/react/router/providers/ReactBrowserRouterProvider.ts +168 -0
  420. package/src/react/router/providers/ReactPageProvider.ts +726 -0
  421. package/src/react/router/providers/ReactServerProvider.spec.tsx +316 -0
  422. package/src/react/router/providers/ReactServerProvider.ts +558 -0
  423. package/src/react/router/providers/ReactServerTemplateProvider.ts +979 -0
  424. package/src/react/router/providers/SSRManifestProvider.ts +334 -0
  425. package/src/react/router/services/ReactPageServerService.ts +48 -0
  426. package/src/react/router/services/ReactPageService.ts +27 -0
  427. package/src/react/router/services/ReactRouter.ts +262 -0
  428. package/src/react/websocket/hooks/useRoom.tsx +242 -0
  429. package/src/react/websocket/index.ts +7 -0
  430. package/src/redis/__tests__/redis.spec.ts +13 -0
  431. package/src/redis/index.ts +9 -25
  432. package/src/redis/providers/BunRedisProvider.ts +9 -0
  433. package/src/redis/providers/NodeRedisProvider.ts +8 -0
  434. package/src/redis/providers/RedisProvider.ts +16 -0
  435. package/src/retry/index.ts +11 -2
  436. package/src/router/index.ts +15 -0
  437. package/src/scheduler/index.ts +11 -2
  438. package/src/security/__tests__/BasicAuth.spec.ts +2 -0
  439. package/src/security/__tests__/ServerSecurityProvider.spec.ts +13 -5
  440. package/src/security/index.ts +15 -10
  441. package/src/security/interfaces/IssuerResolver.ts +27 -0
  442. package/src/security/primitives/$issuer.ts +55 -0
  443. package/src/security/providers/SecurityProvider.ts +179 -0
  444. package/src/security/providers/ServerBasicAuthProvider.ts +6 -2
  445. package/src/security/providers/ServerSecurityProvider.ts +36 -22
  446. package/src/server/auth/index.ts +12 -7
  447. package/src/server/cache/index.ts +7 -22
  448. package/src/server/compress/index.ts +10 -2
  449. package/src/server/cookies/index.ts +7 -5
  450. package/src/server/cookies/primitives/$cookie.ts +33 -11
  451. package/src/server/core/index.ts +16 -6
  452. package/src/server/core/interfaces/ServerRequest.ts +83 -1
  453. package/src/server/core/primitives/$action.spec.ts +1 -1
  454. package/src/server/core/primitives/$action.ts +8 -3
  455. package/src/server/core/providers/NodeHttpServerProvider.ts +9 -3
  456. package/src/server/core/services/ServerRequestParser.spec.ts +520 -0
  457. package/src/server/core/services/ServerRequestParser.ts +306 -13
  458. package/src/server/cors/index.ts +7 -21
  459. package/src/server/cors/primitives/$cors.ts +6 -2
  460. package/src/server/health/index.ts +8 -2
  461. package/src/server/helmet/index.ts +11 -3
  462. package/src/server/links/index.ts +11 -6
  463. package/src/server/metrics/index.ts +10 -3
  464. package/src/server/multipart/index.ts +9 -3
  465. package/src/server/proxy/index.ts +8 -2
  466. package/src/server/rate-limit/index.ts +21 -25
  467. package/src/server/rate-limit/primitives/$rateLimit.ts +6 -2
  468. package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +38 -14
  469. package/src/server/rate-limit/providers/ServerRateLimitProvider.ts +22 -56
  470. package/src/server/static/index.ts +8 -2
  471. package/src/server/static/providers/ServerStaticProvider.ts +1 -1
  472. package/src/server/swagger/index.ts +9 -4
  473. package/src/server/swagger/providers/ServerSwaggerProvider.ts +1 -1
  474. package/src/sms/index.ts +9 -5
  475. package/src/sms/providers/LocalSmsProvider.spec.ts +1 -1
  476. package/src/sms/providers/LocalSmsProvider.ts +1 -1
  477. package/src/system/index.browser.ts +11 -0
  478. package/src/system/index.ts +62 -0
  479. package/src/{file → system}/providers/FileSystemProvider.ts +16 -0
  480. package/src/{file → system}/providers/MemoryFileSystemProvider.ts +116 -3
  481. package/src/system/providers/MemoryShellProvider.ts +164 -0
  482. package/src/{file → system}/providers/NodeFileSystemProvider.spec.ts +2 -2
  483. package/src/{file → system}/providers/NodeFileSystemProvider.ts +36 -0
  484. package/src/system/providers/NodeShellProvider.ts +184 -0
  485. package/src/system/providers/ShellProvider.ts +74 -0
  486. package/src/{file → system}/services/FileDetector.spec.ts +2 -2
  487. package/src/thread/index.ts +11 -2
  488. package/src/topic/core/index.ts +12 -5
  489. package/src/vite/tasks/buildClient.ts +2 -7
  490. package/src/vite/tasks/buildServer.ts +17 -1
  491. package/src/websocket/index.ts +12 -8
  492. package/dist/file/index.d.ts.map +0 -1
  493. package/dist/file/index.js.map +0 -1
  494. package/src/cli/assets/mainServerTs.ts +0 -24
  495. package/src/cli/assets/webAppRouterTs.ts +0 -16
  496. package/src/cli/providers/ViteTemplateProvider.ts +0 -27
  497. package/src/file/index.ts +0 -43
  498. /package/src/cli/{assets → templates}/apiHelloControllerTs.ts +0 -0
  499. /package/src/cli/{assets → templates}/apiIndexTs.ts +0 -0
  500. /package/src/cli/{assets → templates}/biomeJson.ts +0 -0
  501. /package/src/cli/{assets → templates}/dummySpecTs.ts +0 -0
  502. /package/src/cli/{assets → templates}/editorconfig.ts +0 -0
  503. /package/src/cli/{assets → templates}/mainBrowserTs.ts +0 -0
  504. /package/src/cli/{assets → templates}/tsconfigJson.ts +0 -0
  505. /package/src/cli/{assets → templates}/webIndexTs.ts +0 -0
  506. /package/src/{file → system}/errors/FileError.ts +0 -0
  507. /package/src/{file → system}/services/FileDetector.ts +0 -0
@@ -0,0 +1,73 @@
1
+ import React, {
2
+ type ErrorInfo,
3
+ type PropsWithChildren,
4
+ type ReactNode,
5
+ } from "react";
6
+
7
+ /**
8
+ * Props for the ErrorBoundary component.
9
+ */
10
+ export interface ErrorBoundaryProps {
11
+ /**
12
+ * Fallback React node to render when an error is caught.
13
+ * If not provided, a default error message will be shown.
14
+ */
15
+ fallback: (error: Error) => ReactNode;
16
+
17
+ /**
18
+ * Optional callback that receives the error and error info.
19
+ * Use this to log errors to a monitoring service.
20
+ */
21
+ onError?: (error: Error, info: ErrorInfo) => void;
22
+ }
23
+
24
+ /**
25
+ * State of the ErrorBoundary component.
26
+ */
27
+ interface ErrorBoundaryState {
28
+ error?: Error;
29
+ }
30
+
31
+ /**
32
+ * A reusable error boundary for catching rendering errors in any part of the React component tree.
33
+ *
34
+ * It's already included in the Alepha React framework when using page or layout components.
35
+ */
36
+ export class ErrorBoundary extends React.Component<
37
+ PropsWithChildren<ErrorBoundaryProps>,
38
+ ErrorBoundaryState
39
+ > {
40
+ constructor(props: ErrorBoundaryProps) {
41
+ super(props);
42
+ this.state = {};
43
+ }
44
+
45
+ /**
46
+ * Update state so the next render shows the fallback UI.
47
+ */
48
+ static getDerivedStateFromError(error: Error): ErrorBoundaryState {
49
+ return {
50
+ error,
51
+ };
52
+ }
53
+
54
+ /**
55
+ * Lifecycle method called when an error is caught.
56
+ * You can log the error or perform side effects here.
57
+ */
58
+ componentDidCatch(error: Error, info: ErrorInfo): void {
59
+ if (this.props.onError) {
60
+ this.props.onError(error, info);
61
+ }
62
+ }
63
+
64
+ render(): ReactNode {
65
+ if (this.state.error) {
66
+ return this.props.fallback(this.state.error);
67
+ }
68
+
69
+ return this.props.children;
70
+ }
71
+ }
72
+
73
+ export default ErrorBoundary;
@@ -0,0 +1,7 @@
1
+ import type { Alepha } from "alepha";
2
+ import { createContext } from "react";
3
+
4
+ /**
5
+ * React context to provide the Alepha instance throughout the component tree.
6
+ */
7
+ export const AlephaContext = createContext<Alepha | undefined>(undefined);
@@ -0,0 +1,42 @@
1
+ import { Alepha } from "alepha";
2
+ import { type ReactNode, useEffect, useMemo, useState } from "react";
3
+ import { AlephaContext } from "./AlephaContext.ts";
4
+
5
+ export interface AlephaProviderProps {
6
+ children: ReactNode;
7
+ onError: (error: Error) => ReactNode;
8
+ onLoading: () => ReactNode;
9
+ }
10
+
11
+ /**
12
+ * AlephaProvider component to initialize and provide Alepha instance to the app.
13
+ *
14
+ * This isn't recommended for apps using `alepha/react/router`, as Router will handle this for you.
15
+ */
16
+ export const AlephaProvider = (props: AlephaProviderProps) => {
17
+ const alepha = useMemo(() => Alepha.create(), []);
18
+
19
+ const [started, setStarted] = useState(false);
20
+ const [error, setError] = useState<Error | undefined>();
21
+
22
+ useEffect(() => {
23
+ alepha
24
+ .start()
25
+ .then(() => setStarted(true))
26
+ .catch((err) => setError(err));
27
+ }, [alepha]);
28
+
29
+ if (error) {
30
+ return props.onError(error);
31
+ }
32
+
33
+ if (!started) {
34
+ return props.onLoading();
35
+ }
36
+
37
+ return (
38
+ <AlephaContext.Provider value={alepha}>
39
+ {props.children}
40
+ </AlephaContext.Provider>
41
+ );
42
+ };
@@ -0,0 +1,569 @@
1
+ import { renderHook } from "@testing-library/react";
2
+ import { Alepha } from "alepha";
3
+ import { AlephaDateTime } from "alepha/datetime";
4
+ import type { ReactNode } from "react";
5
+ import { describe, test, vi } from "vitest";
6
+ import { AlephaContext } from "../contexts/AlephaContext.ts";
7
+ import { useAction } from "./useAction.ts";
8
+
9
+ describe("useAction", () => {
10
+ test("should handle successful action", async ({ expect }) => {
11
+ const alepha = Alepha.create().with(AlephaDateTime);
12
+ await alepha.start();
13
+
14
+ const mockAction = vi.fn(async (value: string, ctx) => {
15
+ return `result: ${value}`;
16
+ });
17
+
18
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
19
+ <AlephaContext.Provider value={alepha}>{children}</AlephaContext.Provider>
20
+ );
21
+
22
+ const { result } = renderHook(
23
+ () => useAction({ handler: mockAction }, []),
24
+ {
25
+ wrapper,
26
+ },
27
+ );
28
+
29
+ const action = result.current;
30
+
31
+ expect(action.loading).toBe(false);
32
+ expect(action.error).toBe(undefined);
33
+ expect(action.cancel).toBeDefined();
34
+ expect(action.run).toBeDefined();
35
+
36
+ const actionResult = await action.run("test");
37
+
38
+ expect(actionResult).toBe("result: test");
39
+ expect(mockAction).toHaveBeenCalledWith(
40
+ "test",
41
+ expect.objectContaining({
42
+ signal: expect.any(AbortSignal),
43
+ }),
44
+ );
45
+
46
+ expect(result.current.loading).toBe(false);
47
+ expect(result.current.error).toBe(undefined);
48
+ });
49
+
50
+ test("should emit react:action events", async ({ expect }) => {
51
+ const alepha = Alepha.create().with(AlephaDateTime);
52
+ await alepha.start();
53
+
54
+ const events: string[] = [];
55
+
56
+ alepha.events.on("react:action:begin", () => {
57
+ events.push("begin");
58
+ });
59
+ alepha.events.on("react:action:success", () => {
60
+ events.push("success");
61
+ });
62
+ alepha.events.on("react:action:end", () => {
63
+ events.push("end");
64
+ });
65
+
66
+ const mockAction = vi.fn(async (ctx) => "done");
67
+
68
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
69
+ <AlephaContext.Provider value={alepha}>{children}</AlephaContext.Provider>
70
+ );
71
+
72
+ const { result } = renderHook(
73
+ () => useAction({ handler: mockAction }, []),
74
+ {
75
+ wrapper,
76
+ },
77
+ );
78
+
79
+ const action = result.current;
80
+ await action.run();
81
+
82
+ expect(events).toEqual(["begin", "success", "end"]);
83
+ });
84
+
85
+ test.skip("should handle errors", async ({ expect }) => {
86
+ const alepha = Alepha.create().with(AlephaDateTime);
87
+ await alepha.start();
88
+
89
+ const error = new Error("Test error");
90
+ const mockAction = vi.fn(async (ctx) => {
91
+ throw error;
92
+ });
93
+
94
+ const wrapper = ({ children }: { children: ReactNode }) => (
95
+ <AlephaContext.Provider value={alepha}>{children}</AlephaContext.Provider>
96
+ );
97
+
98
+ const { result } = renderHook(
99
+ () => useAction({ handler: mockAction }, []),
100
+ {
101
+ wrapper,
102
+ },
103
+ );
104
+
105
+ const action = result.current;
106
+
107
+ await expect(() => action.run()).rejects.toThrow("Test error");
108
+
109
+ expect(result.current.error).toBe(error);
110
+ expect(result.current.loading).toBe(false);
111
+ });
112
+
113
+ test("should emit react:action:error on failure", async ({ expect }) => {
114
+ const alepha = Alepha.create().with(AlephaDateTime);
115
+ await alepha.start();
116
+
117
+ const events: Array<{ type: string; error?: Error }> = [];
118
+
119
+ alepha.events.on("react:action:error", (ev) => {
120
+ events.push({ type: "error", error: ev.error });
121
+ });
122
+
123
+ const error = new Error("Test error");
124
+ const mockAction = vi.fn(async (ctx) => {
125
+ throw error;
126
+ });
127
+
128
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
129
+ <AlephaContext.Provider value={alepha}>{children}</AlephaContext.Provider>
130
+ );
131
+
132
+ const { result } = renderHook(
133
+ () => useAction({ handler: mockAction }, []),
134
+ {
135
+ wrapper,
136
+ },
137
+ );
138
+
139
+ const action = result.current;
140
+
141
+ try {
142
+ await action.run();
143
+ } catch {
144
+ // Expected
145
+ }
146
+
147
+ expect(events).toHaveLength(1);
148
+ expect(events[0].type).toBe("error");
149
+ expect(events[0].error).toBe(error);
150
+ });
151
+
152
+ test("should call custom error handler", async ({ expect }) => {
153
+ const alepha = Alepha.create().with(AlephaDateTime);
154
+ await alepha.start();
155
+
156
+ const error = new Error("Test error");
157
+ const mockAction = vi.fn(async (ctx) => {
158
+ throw error;
159
+ });
160
+ const onError = vi.fn();
161
+
162
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
163
+ <AlephaContext.Provider value={alepha}>{children}</AlephaContext.Provider>
164
+ );
165
+
166
+ const { result } = renderHook(
167
+ () => useAction({ handler: mockAction, onError }, []),
168
+ { wrapper },
169
+ );
170
+
171
+ const action = result.current;
172
+ await action.run();
173
+
174
+ expect(onError).toHaveBeenCalledWith(error);
175
+ });
176
+
177
+ test("should call custom success handler", async ({ expect }) => {
178
+ const alepha = Alepha.create().with(AlephaDateTime);
179
+ await alepha.start();
180
+
181
+ const mockAction = vi.fn(async (ctx) => "result");
182
+ const onSuccess = vi.fn();
183
+
184
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
185
+ <AlephaContext.Provider value={alepha}>{children}</AlephaContext.Provider>
186
+ );
187
+
188
+ const { result } = renderHook(
189
+ () => useAction({ handler: mockAction, onSuccess }, []),
190
+ { wrapper },
191
+ );
192
+
193
+ const action = result.current;
194
+ await action.run();
195
+
196
+ expect(onSuccess).toHaveBeenCalledWith("result");
197
+ });
198
+
199
+ test("should include action id in events", async ({ expect }) => {
200
+ const alepha = Alepha.create().with(AlephaDateTime);
201
+ await alepha.start();
202
+
203
+ const events: Array<{ id?: string }> = [];
204
+
205
+ alepha.events.on("react:action:begin", (ev) => {
206
+ events.push({ id: ev.id });
207
+ });
208
+
209
+ const mockAction = vi.fn(async (ctx) => "done");
210
+
211
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
212
+ <AlephaContext.Provider value={alepha}>{children}</AlephaContext.Provider>
213
+ );
214
+
215
+ const { result } = renderHook(
216
+ () => useAction({ handler: mockAction, id: "test-action" }, []),
217
+ { wrapper },
218
+ );
219
+
220
+ const action = result.current;
221
+ await action.run();
222
+
223
+ expect(events[0].id).toBe("test-action");
224
+ });
225
+
226
+ test("should prevent concurrent executions", async ({ expect }) => {
227
+ const alepha = Alepha.create().with(AlephaDateTime);
228
+ await alepha.start();
229
+
230
+ let executionCount = 0;
231
+ const mockAction = vi.fn(async (ctx) => {
232
+ executionCount++;
233
+ // Simulate slow operation
234
+ await new Promise((resolve) => setTimeout(resolve, 100));
235
+ return executionCount;
236
+ });
237
+
238
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
239
+ <AlephaContext.Provider value={alepha}>{children}</AlephaContext.Provider>
240
+ );
241
+
242
+ const { result } = renderHook(
243
+ () => useAction({ handler: mockAction }, []),
244
+ { wrapper },
245
+ );
246
+
247
+ const action = result.current;
248
+
249
+ // Call 100 times rapidly
250
+ const promises = Array.from({ length: 100 }, () => action.run());
251
+
252
+ // Wait for all to complete
253
+ await Promise.all(promises);
254
+
255
+ // Should have only executed once
256
+ expect(mockAction).toHaveBeenCalledTimes(1);
257
+ expect(executionCount).toBe(1);
258
+ });
259
+
260
+ test("should debounce action calls", async ({ expect }) => {
261
+ const alepha = Alepha.create().with(AlephaDateTime);
262
+ await alepha.start();
263
+
264
+ const mockAction = vi.fn(async (value: string, ctx) => value);
265
+
266
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
267
+ <AlephaContext.Provider value={alepha}>{children}</AlephaContext.Provider>
268
+ );
269
+
270
+ const { result } = renderHook(
271
+ () => useAction({ handler: mockAction, debounce: 50 }, []),
272
+ { wrapper },
273
+ );
274
+
275
+ const action = result.current;
276
+
277
+ // Call 100 times rapidly with different values
278
+ for (let i = 0; i < 100; i++) {
279
+ action.run(`value-${i}`);
280
+ }
281
+
282
+ // Wait for debounce to complete
283
+ await new Promise((resolve) => setTimeout(resolve, 100));
284
+
285
+ // Should have only executed once with the last value
286
+ expect(mockAction).toHaveBeenCalledTimes(1);
287
+ expect(mockAction).toHaveBeenCalledWith("value-99", expect.any(Object));
288
+ });
289
+
290
+ test("should reset debounce timer on each call", async ({ expect }) => {
291
+ const alepha = Alepha.create().with(AlephaDateTime);
292
+ await alepha.start();
293
+
294
+ const mockAction = vi.fn(async (value: string, ctx) => value);
295
+
296
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
297
+ <AlephaContext.Provider value={alepha}>{children}</AlephaContext.Provider>
298
+ );
299
+
300
+ const { result } = renderHook(
301
+ () => useAction({ handler: mockAction, debounce: 50 }, []),
302
+ { wrapper },
303
+ );
304
+
305
+ const action = result.current;
306
+
307
+ // Call multiple times with delays
308
+ action.run("first");
309
+ await new Promise((resolve) => setTimeout(resolve, 30));
310
+ action.run("second");
311
+ await new Promise((resolve) => setTimeout(resolve, 30));
312
+ action.run("third");
313
+
314
+ // Wait for final debounce
315
+ await new Promise((resolve) => setTimeout(resolve, 60));
316
+
317
+ // Should have only executed once with the last value
318
+ expect(mockAction).toHaveBeenCalledTimes(1);
319
+ expect(mockAction).toHaveBeenCalledWith("third", expect.any(Object));
320
+ });
321
+
322
+ test("should pass AbortSignal to handler", async ({ expect }) => {
323
+ const alepha = Alepha.create().with(AlephaDateTime);
324
+ await alepha.start();
325
+
326
+ let receivedSignal: AbortSignal | undefined;
327
+
328
+ const mockAction = vi.fn(async (value: string, { signal }) => {
329
+ receivedSignal = signal as AbortSignal;
330
+ return value;
331
+ });
332
+
333
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
334
+ <AlephaContext.Provider value={alepha}>{children}</AlephaContext.Provider>
335
+ );
336
+
337
+ const { result } = renderHook(
338
+ () => useAction({ handler: mockAction }, []),
339
+ { wrapper },
340
+ );
341
+
342
+ const action = result.current;
343
+ await action.run("test");
344
+
345
+ expect(receivedSignal).toBeInstanceOf(AbortSignal);
346
+ expect(receivedSignal?.aborted).toBe(false);
347
+ });
348
+
349
+ test("should cancel in-flight request with cancel()", async ({ expect }) => {
350
+ const alepha = Alepha.create().with(AlephaDateTime);
351
+ await alepha.start();
352
+
353
+ let wasAborted = false;
354
+ const mockAction = vi.fn(async (ctx) => {
355
+ await new Promise((resolve) => setTimeout(resolve, 100));
356
+ wasAborted = ctx.signal.aborted;
357
+ return "done";
358
+ });
359
+
360
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
361
+ <AlephaContext.Provider value={alepha}>{children}</AlephaContext.Provider>
362
+ );
363
+
364
+ const { result } = renderHook(
365
+ () => useAction({ handler: mockAction }, []),
366
+ { wrapper },
367
+ );
368
+
369
+ const action = result.current;
370
+
371
+ // Start action
372
+ const promise = action.run();
373
+
374
+ // Cancel after 50ms
375
+ await new Promise((resolve) => setTimeout(resolve, 50));
376
+ action.cancel();
377
+
378
+ await promise;
379
+
380
+ expect(wasAborted).toBe(true);
381
+ });
382
+
383
+ test("should cancel debounced action", async ({ expect }) => {
384
+ const alepha = Alepha.create().with(AlephaDateTime);
385
+ await alepha.start();
386
+
387
+ const mockAction = vi.fn(async (value: string, ctx) => value);
388
+
389
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
390
+ <AlephaContext.Provider value={alepha}>{children}</AlephaContext.Provider>
391
+ );
392
+
393
+ const { result } = renderHook(
394
+ () => useAction({ handler: mockAction, debounce: 100 }, []),
395
+ { wrapper },
396
+ );
397
+
398
+ const action = result.current;
399
+
400
+ // Call handler
401
+ action.run("test");
402
+
403
+ // Cancel before debounce completes
404
+ await new Promise((resolve) => setTimeout(resolve, 50));
405
+ action.cancel();
406
+
407
+ // Wait for debounce period
408
+ await new Promise((resolve) => setTimeout(resolve, 100));
409
+
410
+ // Should not have executed
411
+ expect(mockAction).not.toHaveBeenCalled();
412
+ });
413
+
414
+ test("should handle AbortError gracefully", async ({ expect }) => {
415
+ const alepha = Alepha.create().with(AlephaDateTime);
416
+ await alepha.start();
417
+
418
+ const mockAction = vi.fn(async (ctx) => {
419
+ const abortError = new Error("The operation was aborted");
420
+ abortError.name = "AbortError";
421
+ throw abortError;
422
+ });
423
+
424
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
425
+ <AlephaContext.Provider value={alepha}>{children}</AlephaContext.Provider>
426
+ );
427
+
428
+ const { result } = renderHook(
429
+ () => useAction({ handler: mockAction }, []),
430
+ { wrapper },
431
+ );
432
+
433
+ const action = result.current;
434
+ await action.run();
435
+
436
+ // Should not set error state for abort errors
437
+ expect(result.current.error).toBe(undefined);
438
+ });
439
+
440
+ test("should run action on mount with runOnInit", async ({ expect }) => {
441
+ const alepha = Alepha.create().with(AlephaDateTime);
442
+ await alepha.start();
443
+
444
+ const mockAction = vi.fn(async (ctx) => "initialized");
445
+
446
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
447
+ <AlephaContext.Provider value={alepha}>{children}</AlephaContext.Provider>
448
+ );
449
+
450
+ renderHook(() => useAction({ handler: mockAction, runOnInit: true }, []), {
451
+ wrapper,
452
+ });
453
+
454
+ // Wait a tick for the effect to run
455
+ await new Promise((resolve) => setTimeout(resolve, 0));
456
+
457
+ // Should have been called once on mount
458
+ expect(mockAction).toHaveBeenCalledTimes(1);
459
+ });
460
+
461
+ test("should not run action on mount without runOnInit", async ({
462
+ expect,
463
+ }) => {
464
+ const alepha = Alepha.create().with(AlephaDateTime);
465
+ await alepha.start();
466
+
467
+ const mockAction = vi.fn(async (ctx) => "result");
468
+
469
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
470
+ <AlephaContext.Provider value={alepha}>{children}</AlephaContext.Provider>
471
+ );
472
+
473
+ renderHook(() => useAction({ handler: mockAction }, []), {
474
+ wrapper,
475
+ });
476
+
477
+ // Wait a tick
478
+ await new Promise((resolve) => setTimeout(resolve, 0));
479
+
480
+ // Should not have been called
481
+ expect(mockAction).not.toHaveBeenCalled();
482
+ });
483
+
484
+ test("should run action periodically with runEvery (milliseconds)", async ({
485
+ expect,
486
+ }) => {
487
+ const alepha = Alepha.create().with(AlephaDateTime);
488
+ await alepha.start();
489
+
490
+ const mockAction = vi.fn(async (ctx) => "fired");
491
+
492
+ const wrapper = ({ children }: { children: ReactNode }) => (
493
+ <AlephaContext.Provider value={alepha}>{children}</AlephaContext.Provider>
494
+ );
495
+
496
+ const { unmount } = renderHook(
497
+ () => useAction({ handler: mockAction, runEvery: 60 }, []),
498
+ { wrapper },
499
+ );
500
+
501
+ // Wait for multiple intervals
502
+ await new Promise((resolve) => setTimeout(resolve, 160));
503
+
504
+ // Should have been called approximately 3 times (at 50ms, 100ms, 150ms)
505
+ expect(mockAction.mock.calls.length).toBeGreaterThanOrEqual(2);
506
+ expect(mockAction.mock.calls.length).toBeLessThanOrEqual(4);
507
+
508
+ // Cleanup
509
+ unmount();
510
+ });
511
+
512
+ test("should run action periodically with runEvery (duration tuple)", async ({
513
+ expect,
514
+ }) => {
515
+ const alepha = Alepha.create().with(AlephaDateTime);
516
+ await alepha.start();
517
+
518
+ const mockAction = vi.fn(async (ctx) => "polled");
519
+
520
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
521
+ <AlephaContext.Provider value={alepha}>{children}</AlephaContext.Provider>
522
+ );
523
+
524
+ const { unmount } = renderHook(
525
+ () =>
526
+ useAction({ handler: mockAction, runEvery: [50, "milliseconds"] }, []),
527
+ { wrapper },
528
+ );
529
+
530
+ // Wait for multiple intervals
531
+ await new Promise((resolve) => setTimeout(resolve, 160));
532
+
533
+ // Should have been called approximately 3 times
534
+ expect(mockAction.mock.calls.length).toBeGreaterThanOrEqual(2);
535
+ expect(mockAction.mock.calls.length).toBeLessThanOrEqual(4);
536
+
537
+ // Cleanup
538
+ unmount();
539
+ });
540
+
541
+ test("should cleanup interval on unmount", async ({ expect }) => {
542
+ const alepha = Alepha.create().with(AlephaDateTime);
543
+ await alepha.start();
544
+
545
+ const mockAction = vi.fn(async (ctx) => "polled");
546
+
547
+ const wrapper = ({ children }: { children: ReactNode }) => (
548
+ <AlephaContext.Provider value={alepha}>{children}</AlephaContext.Provider>
549
+ );
550
+
551
+ const { unmount } = renderHook(
552
+ () => useAction({ handler: mockAction, runEvery: 50 }, []),
553
+ { wrapper },
554
+ );
555
+
556
+ // Wait for one interval
557
+ await new Promise((resolve) => setTimeout(resolve, 75));
558
+ const callsBeforeUnmount = mockAction.mock.calls.length;
559
+
560
+ // Unmount
561
+ unmount();
562
+
563
+ // Wait for what would be another interval
564
+ await new Promise((resolve) => setTimeout(resolve, 100));
565
+
566
+ // Should not have been called again after unmount
567
+ expect(mockAction.mock.calls.length).toBe(callsBeforeUnmount);
568
+ });
569
+ });