alepha 0.15.0 → 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 (551) hide show
  1. package/README.md +43 -98
  2. package/dist/api/audits/index.d.ts +630 -653
  3. package/dist/api/audits/index.d.ts.map +1 -1
  4. package/dist/api/audits/index.js +12 -35
  5. package/dist/api/audits/index.js.map +1 -1
  6. package/dist/api/files/index.d.ts +365 -358
  7. package/dist/api/files/index.d.ts.map +1 -1
  8. package/dist/api/files/index.js +12 -5
  9. package/dist/api/files/index.js.map +1 -1
  10. package/dist/api/jobs/index.d.ts +255 -248
  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.browser.js +4 -4
  19. package/dist/api/notifications/index.browser.js.map +1 -1
  20. package/dist/api/notifications/index.d.ts +84 -78
  21. package/dist/api/notifications/index.d.ts.map +1 -1
  22. package/dist/api/notifications/index.js +14 -8
  23. package/dist/api/notifications/index.js.map +1 -1
  24. package/dist/api/parameters/index.d.ts +528 -535
  25. package/dist/api/parameters/index.d.ts.map +1 -1
  26. package/dist/api/parameters/index.js +30 -37
  27. package/dist/api/parameters/index.js.map +1 -1
  28. package/dist/api/users/index.d.ts +1221 -910
  29. package/dist/api/users/index.d.ts.map +1 -1
  30. package/dist/api/users/index.js +2556 -248
  31. package/dist/api/users/index.js.map +1 -1
  32. package/dist/api/verifications/index.d.ts +142 -136
  33. package/dist/api/verifications/index.d.ts.map +1 -1
  34. package/dist/api/verifications/index.js +12 -4
  35. package/dist/api/verifications/index.js.map +1 -1
  36. package/dist/batch/index.d.ts +142 -162
  37. package/dist/batch/index.d.ts.map +1 -1
  38. package/dist/batch/index.js +31 -44
  39. package/dist/batch/index.js.map +1 -1
  40. package/dist/bucket/index.d.ts +595 -171
  41. package/dist/bucket/index.d.ts.map +1 -1
  42. package/dist/bucket/index.js +1856 -12
  43. package/dist/bucket/index.js.map +1 -1
  44. package/dist/cache/core/index.d.ts +225 -53
  45. package/dist/cache/core/index.d.ts.map +1 -1
  46. package/dist/cache/core/index.js +213 -7
  47. package/dist/cache/core/index.js.map +1 -1
  48. package/dist/cache/redis/index.d.ts +1 -0
  49. package/dist/cache/redis/index.d.ts.map +1 -1
  50. package/dist/cache/redis/index.js +6 -2
  51. package/dist/cache/redis/index.js.map +1 -1
  52. package/dist/cli/index.d.ts +834 -226
  53. package/dist/cli/index.d.ts.map +1 -1
  54. package/dist/cli/index.js +2872 -417
  55. package/dist/cli/index.js.map +1 -1
  56. package/dist/command/index.d.ts +458 -310
  57. package/dist/command/index.d.ts.map +1 -1
  58. package/dist/command/index.js +2011 -76
  59. package/dist/command/index.js.map +1 -1
  60. package/dist/core/index.browser.js +309 -97
  61. package/dist/core/index.browser.js.map +1 -1
  62. package/dist/core/index.d.ts +796 -701
  63. package/dist/core/index.d.ts.map +1 -1
  64. package/dist/core/index.js +329 -97
  65. package/dist/core/index.js.map +1 -1
  66. package/dist/core/index.native.js +309 -97
  67. package/dist/core/index.native.js.map +1 -1
  68. package/dist/datetime/index.d.ts +59 -44
  69. package/dist/datetime/index.d.ts.map +1 -1
  70. package/dist/datetime/index.js +15 -0
  71. package/dist/datetime/index.js.map +1 -1
  72. package/dist/email/index.d.ts +314 -19
  73. package/dist/email/index.d.ts.map +1 -1
  74. package/dist/email/index.js +1852 -7
  75. package/dist/email/index.js.map +1 -1
  76. package/dist/fake/index.d.ts +5500 -5418
  77. package/dist/fake/index.d.ts.map +1 -1
  78. package/dist/fake/index.js +113 -42
  79. package/dist/fake/index.js.map +1 -1
  80. package/dist/lock/core/index.d.ts +219 -212
  81. package/dist/lock/core/index.d.ts.map +1 -1
  82. package/dist/lock/core/index.js +11 -4
  83. package/dist/lock/core/index.js.map +1 -1
  84. package/dist/lock/redis/index.d.ts.map +1 -1
  85. package/dist/logger/index.d.ts +41 -90
  86. package/dist/logger/index.d.ts.map +1 -1
  87. package/dist/logger/index.js +15 -68
  88. package/dist/logger/index.js.map +1 -1
  89. package/dist/mcp/index.d.ts +228 -230
  90. package/dist/mcp/index.d.ts.map +1 -1
  91. package/dist/mcp/index.js +32 -31
  92. package/dist/mcp/index.js.map +1 -1
  93. package/dist/orm/index.browser.js +12 -12
  94. package/dist/orm/index.browser.js.map +1 -1
  95. package/dist/orm/index.bun.js +90 -80
  96. package/dist/orm/index.bun.js.map +1 -1
  97. package/dist/orm/index.d.ts +1434 -1459
  98. package/dist/orm/index.d.ts.map +1 -1
  99. package/dist/orm/index.js +112 -130
  100. package/dist/orm/index.js.map +1 -1
  101. package/dist/queue/core/index.d.ts +262 -254
  102. package/dist/queue/core/index.d.ts.map +1 -1
  103. package/dist/queue/core/index.js +14 -6
  104. package/dist/queue/core/index.js.map +1 -1
  105. package/dist/queue/redis/index.d.ts.map +1 -1
  106. package/dist/react/auth/index.browser.js +108 -0
  107. package/dist/react/auth/index.browser.js.map +1 -0
  108. package/dist/react/auth/index.d.ts +100 -0
  109. package/dist/react/auth/index.d.ts.map +1 -0
  110. package/dist/react/auth/index.js +145 -0
  111. package/dist/react/auth/index.js.map +1 -0
  112. package/dist/react/core/index.d.ts +469 -0
  113. package/dist/react/core/index.d.ts.map +1 -0
  114. package/dist/react/core/index.js +464 -0
  115. package/dist/react/core/index.js.map +1 -0
  116. package/dist/react/form/index.d.ts +232 -0
  117. package/dist/react/form/index.d.ts.map +1 -0
  118. package/dist/react/form/index.js +432 -0
  119. package/dist/react/form/index.js.map +1 -0
  120. package/dist/react/head/index.browser.js +423 -0
  121. package/dist/react/head/index.browser.js.map +1 -0
  122. package/dist/react/head/index.d.ts +288 -0
  123. package/dist/react/head/index.d.ts.map +1 -0
  124. package/dist/react/head/index.js +465 -0
  125. package/dist/react/head/index.js.map +1 -0
  126. package/dist/react/i18n/index.d.ts +175 -0
  127. package/dist/react/i18n/index.d.ts.map +1 -0
  128. package/dist/react/i18n/index.js +224 -0
  129. package/dist/react/i18n/index.js.map +1 -0
  130. package/dist/react/router/index.browser.js +1980 -0
  131. package/dist/react/router/index.browser.js.map +1 -0
  132. package/dist/react/router/index.d.ts +2068 -0
  133. package/dist/react/router/index.d.ts.map +1 -0
  134. package/dist/react/router/index.js +4932 -0
  135. package/dist/react/router/index.js.map +1 -0
  136. package/dist/react/websocket/index.d.ts +117 -0
  137. package/dist/react/websocket/index.d.ts.map +1 -0
  138. package/dist/react/websocket/index.js +107 -0
  139. package/dist/react/websocket/index.js.map +1 -0
  140. package/dist/redis/index.bun.js +4 -0
  141. package/dist/redis/index.bun.js.map +1 -1
  142. package/dist/redis/index.d.ts +127 -130
  143. package/dist/redis/index.d.ts.map +1 -1
  144. package/dist/redis/index.js +16 -25
  145. package/dist/redis/index.js.map +1 -1
  146. package/dist/retry/index.d.ts +80 -71
  147. package/dist/retry/index.d.ts.map +1 -1
  148. package/dist/retry/index.js +11 -2
  149. package/dist/retry/index.js.map +1 -1
  150. package/dist/router/index.d.ts +6 -6
  151. package/dist/router/index.d.ts.map +1 -1
  152. package/dist/scheduler/index.d.ts +119 -28
  153. package/dist/scheduler/index.d.ts.map +1 -1
  154. package/dist/scheduler/index.js +404 -3
  155. package/dist/scheduler/index.js.map +1 -1
  156. package/dist/security/index.d.ts +642 -228
  157. package/dist/security/index.d.ts.map +1 -1
  158. package/dist/security/index.js +1579 -37
  159. package/dist/security/index.js.map +1 -1
  160. package/dist/server/auth/index.d.ts +1141 -111
  161. package/dist/server/auth/index.d.ts.map +1 -1
  162. package/dist/server/auth/index.js +1261 -25
  163. package/dist/server/auth/index.js.map +1 -1
  164. package/dist/server/cache/index.d.ts +63 -78
  165. package/dist/server/cache/index.d.ts.map +1 -1
  166. package/dist/server/cache/index.js +7 -22
  167. package/dist/server/cache/index.js.map +1 -1
  168. package/dist/server/compress/index.d.ts +13 -5
  169. package/dist/server/compress/index.d.ts.map +1 -1
  170. package/dist/server/compress/index.js +10 -2
  171. package/dist/server/compress/index.js.map +1 -1
  172. package/dist/server/cookies/index.d.ts +46 -22
  173. package/dist/server/cookies/index.d.ts.map +1 -1
  174. package/dist/server/cookies/index.js +7 -5
  175. package/dist/server/cookies/index.js.map +1 -1
  176. package/dist/server/core/index.d.ts +307 -196
  177. package/dist/server/core/index.d.ts.map +1 -1
  178. package/dist/server/core/index.js +271 -38
  179. package/dist/server/core/index.js.map +1 -1
  180. package/dist/server/cors/index.d.ts +24 -34
  181. package/dist/server/cors/index.d.ts.map +1 -1
  182. package/dist/server/cors/index.js +7 -21
  183. package/dist/server/cors/index.js.map +1 -1
  184. package/dist/server/health/index.d.ts +25 -19
  185. package/dist/server/health/index.d.ts.map +1 -1
  186. package/dist/server/health/index.js +8 -2
  187. package/dist/server/health/index.js.map +1 -1
  188. package/dist/server/helmet/index.d.ts +13 -5
  189. package/dist/server/helmet/index.d.ts.map +1 -1
  190. package/dist/server/helmet/index.js +11 -3
  191. package/dist/server/helmet/index.js.map +1 -1
  192. package/dist/server/links/index.browser.js +9 -1
  193. package/dist/server/links/index.browser.js.map +1 -1
  194. package/dist/server/links/index.d.ts +133 -128
  195. package/dist/server/links/index.d.ts.map +1 -1
  196. package/dist/server/links/index.js +24 -11
  197. package/dist/server/links/index.js.map +1 -1
  198. package/dist/server/metrics/index.d.ts +524 -4
  199. package/dist/server/metrics/index.d.ts.map +1 -1
  200. package/dist/server/metrics/index.js +4472 -7
  201. package/dist/server/metrics/index.js.map +1 -1
  202. package/dist/server/multipart/index.d.ts +15 -9
  203. package/dist/server/multipart/index.d.ts.map +1 -1
  204. package/dist/server/multipart/index.js +9 -3
  205. package/dist/server/multipart/index.js.map +1 -1
  206. package/dist/server/proxy/index.d.ts +110 -104
  207. package/dist/server/proxy/index.d.ts.map +1 -1
  208. package/dist/server/proxy/index.js +8 -2
  209. package/dist/server/proxy/index.js.map +1 -1
  210. package/dist/server/rate-limit/index.d.ts +46 -51
  211. package/dist/server/rate-limit/index.d.ts.map +1 -1
  212. package/dist/server/rate-limit/index.js +18 -55
  213. package/dist/server/rate-limit/index.js.map +1 -1
  214. package/dist/server/static/index.d.ts +181 -48
  215. package/dist/server/static/index.d.ts.map +1 -1
  216. package/dist/server/static/index.js +1848 -5
  217. package/dist/server/static/index.js.map +1 -1
  218. package/dist/server/swagger/index.d.ts +348 -53
  219. package/dist/server/swagger/index.d.ts.map +1 -1
  220. package/dist/server/swagger/index.js +1849 -6
  221. package/dist/server/swagger/index.js.map +1 -1
  222. package/dist/sms/index.d.ts +312 -18
  223. package/dist/sms/index.d.ts.map +1 -1
  224. package/dist/sms/index.js +1854 -10
  225. package/dist/sms/index.js.map +1 -1
  226. package/dist/system/index.browser.js +496 -0
  227. package/dist/system/index.browser.js.map +1 -0
  228. package/dist/system/index.d.ts +1158 -0
  229. package/dist/system/index.d.ts.map +1 -0
  230. package/dist/{file → system}/index.js +412 -20
  231. package/dist/system/index.js.map +1 -0
  232. package/dist/thread/index.d.ts +82 -73
  233. package/dist/thread/index.d.ts.map +1 -1
  234. package/dist/thread/index.js +13 -4
  235. package/dist/thread/index.js.map +1 -1
  236. package/dist/topic/core/index.d.ts +330 -323
  237. package/dist/topic/core/index.d.ts.map +1 -1
  238. package/dist/topic/core/index.js +12 -5
  239. package/dist/topic/core/index.js.map +1 -1
  240. package/dist/topic/redis/index.d.ts +6 -6
  241. package/dist/topic/redis/index.d.ts.map +1 -1
  242. package/dist/vite/index.d.ts +163 -5825
  243. package/dist/vite/index.d.ts.map +1 -1
  244. package/dist/vite/index.js +130 -477
  245. package/dist/vite/index.js.map +1 -1
  246. package/dist/websocket/index.browser.js +3 -3
  247. package/dist/websocket/index.browser.js.map +1 -1
  248. package/dist/websocket/index.d.ts +287 -283
  249. package/dist/websocket/index.d.ts.map +1 -1
  250. package/dist/websocket/index.js +15 -11
  251. package/dist/websocket/index.js.map +1 -1
  252. package/package.json +86 -17
  253. package/src/api/audits/index.ts +10 -33
  254. package/src/api/files/__tests__/$bucket.spec.ts +1 -1
  255. package/src/api/files/controllers/AdminFileStatsController.spec.ts +1 -1
  256. package/src/api/files/controllers/FileController.spec.ts +1 -1
  257. package/src/api/files/index.ts +10 -3
  258. package/src/api/files/jobs/FileJobs.spec.ts +1 -1
  259. package/src/api/files/services/FileService.spec.ts +1 -1
  260. package/src/api/jobs/index.ts +10 -3
  261. package/src/api/keys/controllers/AdminApiKeyController.ts +75 -0
  262. package/src/api/keys/controllers/ApiKeyController.ts +103 -0
  263. package/src/api/keys/entities/apiKeyEntity.ts +41 -0
  264. package/src/api/keys/index.ts +49 -0
  265. package/src/api/keys/schemas/adminApiKeyQuerySchema.ts +7 -0
  266. package/src/api/keys/schemas/adminApiKeyResourceSchema.ts +17 -0
  267. package/src/api/keys/schemas/createApiKeyBodySchema.ts +7 -0
  268. package/src/api/keys/schemas/createApiKeyResponseSchema.ts +11 -0
  269. package/src/api/keys/schemas/listApiKeyResponseSchema.ts +15 -0
  270. package/src/api/keys/schemas/revokeApiKeyParamsSchema.ts +5 -0
  271. package/src/api/keys/schemas/revokeApiKeyResponseSchema.ts +5 -0
  272. package/src/api/keys/services/ApiKeyService.spec.ts +553 -0
  273. package/src/api/keys/services/ApiKeyService.ts +306 -0
  274. package/src/api/logs/TODO.md +52 -0
  275. package/src/api/notifications/index.ts +10 -4
  276. package/src/api/parameters/index.ts +9 -30
  277. package/src/api/parameters/primitives/$config.ts +12 -4
  278. package/src/api/parameters/services/ConfigStore.ts +9 -3
  279. package/src/api/users/__tests__/ApiKeys-integration.spec.ts +1035 -0
  280. package/src/api/users/__tests__/ApiKeys.spec.ts +401 -0
  281. package/src/api/users/index.ts +14 -3
  282. package/src/api/users/primitives/$realm.ts +33 -5
  283. package/src/api/users/providers/RealmProvider.ts +1 -12
  284. package/src/api/users/services/SessionService.ts +1 -11
  285. package/src/api/verifications/controllers/VerificationController.ts +2 -0
  286. package/src/api/verifications/index.ts +10 -4
  287. package/src/batch/index.ts +9 -36
  288. package/src/batch/primitives/$batch.ts +0 -8
  289. package/src/batch/providers/BatchProvider.ts +29 -2
  290. package/src/bucket/__tests__/shared.ts +1 -1
  291. package/src/bucket/index.ts +13 -6
  292. package/src/bucket/primitives/$bucket.ts +1 -1
  293. package/src/bucket/providers/LocalFileStorageProvider.ts +1 -1
  294. package/src/bucket/providers/MemoryFileStorageProvider.ts +1 -1
  295. package/src/cache/core/__tests__/shared.ts +30 -0
  296. package/src/cache/core/index.ts +11 -6
  297. package/src/cache/core/primitives/$cache.spec.ts +5 -0
  298. package/src/cache/core/providers/CacheProvider.ts +17 -0
  299. package/src/cache/core/providers/MemoryCacheProvider.ts +300 -1
  300. package/src/cache/redis/__tests__/cache-redis.spec.ts +5 -0
  301. package/src/cache/redis/providers/RedisCacheProvider.ts +9 -0
  302. package/src/cli/apps/AlephaCli.ts +3 -16
  303. package/src/cli/apps/AlephaPackageBuilderCli.ts +10 -2
  304. package/src/cli/atoms/appEntryOptions.ts +13 -0
  305. package/src/cli/atoms/buildOptions.ts +1 -1
  306. package/src/cli/atoms/changelogOptions.ts +1 -1
  307. package/src/cli/commands/build.ts +64 -52
  308. package/src/cli/commands/db.ts +17 -11
  309. package/src/cli/commands/deploy.ts +1 -1
  310. package/src/cli/commands/dev.ts +13 -49
  311. package/src/cli/commands/gen/env.ts +6 -3
  312. package/src/cli/commands/gen/openapi.ts +5 -2
  313. package/src/cli/commands/init.spec.ts +544 -0
  314. package/src/cli/commands/init.ts +101 -58
  315. package/src/cli/commands/lint.ts +8 -2
  316. package/src/cli/commands/typecheck.ts +11 -0
  317. package/src/cli/defineConfig.ts +9 -0
  318. package/src/cli/index.ts +2 -1
  319. package/src/cli/providers/AppEntryProvider.ts +131 -0
  320. package/src/cli/providers/ViteBuildProvider.ts +40 -0
  321. package/src/cli/providers/ViteDevServerProvider.ts +378 -0
  322. package/src/cli/services/AlephaCliUtils.ts +39 -93
  323. package/src/cli/services/PackageManagerUtils.ts +140 -17
  324. package/src/cli/services/ProjectScaffolder.ts +169 -101
  325. package/src/cli/services/ViteUtils.ts +82 -0
  326. package/src/cli/{assets/claudeMd.ts → templates/agentMd.ts} +41 -28
  327. package/src/cli/{assets → templates}/apiHelloControllerTs.ts +2 -1
  328. package/src/cli/{assets → templates}/biomeJson.ts +2 -1
  329. package/src/cli/{assets → templates}/dummySpecTs.ts +2 -1
  330. package/src/cli/{assets → templates}/editorconfig.ts +2 -1
  331. package/src/cli/templates/gitignore.ts +39 -0
  332. package/src/cli/{assets → templates}/mainBrowserTs.ts +2 -1
  333. package/src/cli/templates/mainCss.ts +33 -0
  334. package/src/cli/templates/mainServerTs.ts +33 -0
  335. package/src/cli/{assets → templates}/tsconfigJson.ts +2 -1
  336. package/src/cli/templates/webAppRouterTs.ts +50 -0
  337. package/src/cli/templates/webHelloComponentTsx.ts +20 -0
  338. package/src/command/helpers/Runner.spec.ts +4 -0
  339. package/src/command/helpers/Runner.ts +3 -21
  340. package/src/command/index.ts +12 -4
  341. package/src/command/providers/CliProvider.spec.ts +1067 -0
  342. package/src/command/providers/CliProvider.ts +203 -40
  343. package/src/core/Alepha.ts +3 -9
  344. package/src/core/__tests__/Alepha-start.spec.ts +4 -4
  345. package/src/core/helpers/jsonSchemaToTypeBox.spec.ts +771 -0
  346. package/src/core/helpers/jsonSchemaToTypeBox.ts +62 -10
  347. package/src/core/index.shared.ts +1 -0
  348. package/src/core/index.ts +20 -0
  349. package/src/core/primitives/$module.ts +12 -0
  350. package/src/core/providers/EventManager.spec.ts +0 -71
  351. package/src/core/providers/EventManager.ts +3 -15
  352. package/src/core/providers/Json.ts +2 -14
  353. package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +257 -0
  354. package/src/core/providers/KeylessJsonSchemaCodec.ts +396 -14
  355. package/src/core/providers/SchemaValidator.spec.ts +236 -0
  356. package/src/datetime/index.ts +15 -0
  357. package/src/email/index.ts +10 -5
  358. package/src/email/providers/LocalEmailProvider.spec.ts +1 -1
  359. package/src/email/providers/LocalEmailProvider.ts +1 -1
  360. package/src/fake/__tests__/keyName.example.ts +1 -1
  361. package/src/fake/__tests__/keyName.spec.ts +5 -5
  362. package/src/fake/index.ts +9 -6
  363. package/src/fake/providers/FakeProvider.spec.ts +258 -40
  364. package/src/fake/providers/FakeProvider.ts +133 -19
  365. package/src/lock/core/index.ts +11 -4
  366. package/src/logger/index.ts +17 -66
  367. package/src/logger/providers/PrettyFormatterProvider.ts +0 -9
  368. package/src/mcp/errors/McpError.ts +30 -0
  369. package/src/mcp/index.ts +13 -27
  370. package/src/mcp/transports/SseMcpTransport.ts +6 -7
  371. package/src/orm/__tests__/PostgresProvider.spec.ts +2 -2
  372. package/src/orm/index.browser.ts +2 -2
  373. package/src/orm/index.bun.ts +4 -2
  374. package/src/orm/index.ts +21 -47
  375. package/src/orm/providers/DrizzleKitProvider.ts +3 -5
  376. package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -0
  377. package/src/orm/services/Repository.ts +18 -3
  378. package/src/queue/core/index.ts +14 -6
  379. package/src/react/auth/__tests__/$auth.spec.ts +202 -0
  380. package/src/react/auth/hooks/useAuth.ts +32 -0
  381. package/src/react/auth/index.browser.ts +13 -0
  382. package/src/react/auth/index.shared.ts +2 -0
  383. package/src/react/auth/index.ts +48 -0
  384. package/src/react/auth/providers/ReactAuthProvider.ts +16 -0
  385. package/src/react/auth/services/ReactAuth.ts +135 -0
  386. package/src/react/core/__tests__/Router.spec.tsx +169 -0
  387. package/src/react/core/components/ClientOnly.tsx +49 -0
  388. package/src/react/core/components/ErrorBoundary.tsx +73 -0
  389. package/src/react/core/contexts/AlephaContext.ts +7 -0
  390. package/src/react/core/contexts/AlephaProvider.tsx +42 -0
  391. package/src/react/core/hooks/useAction.browser.spec.tsx +569 -0
  392. package/src/react/core/hooks/useAction.ts +480 -0
  393. package/src/react/core/hooks/useAlepha.ts +26 -0
  394. package/src/react/core/hooks/useClient.ts +17 -0
  395. package/src/react/core/hooks/useEvents.ts +51 -0
  396. package/src/react/core/hooks/useInject.ts +12 -0
  397. package/src/react/core/hooks/useStore.ts +52 -0
  398. package/src/react/core/index.ts +90 -0
  399. package/src/react/form/components/FormState.tsx +17 -0
  400. package/src/react/form/errors/FormValidationError.ts +18 -0
  401. package/src/react/form/hooks/useForm.browser.spec.tsx +366 -0
  402. package/src/react/form/hooks/useForm.ts +47 -0
  403. package/src/react/form/hooks/useFormState.ts +130 -0
  404. package/src/react/form/index.ts +44 -0
  405. package/src/react/form/services/FormModel.ts +614 -0
  406. package/src/react/head/helpers/SeoExpander.spec.ts +203 -0
  407. package/src/react/head/helpers/SeoExpander.ts +142 -0
  408. package/src/react/head/hooks/useHead.spec.tsx +288 -0
  409. package/src/react/head/hooks/useHead.ts +62 -0
  410. package/src/react/head/index.browser.ts +26 -0
  411. package/src/react/head/index.ts +44 -0
  412. package/src/react/head/interfaces/Head.ts +105 -0
  413. package/src/react/head/primitives/$head.ts +25 -0
  414. package/src/react/head/providers/BrowserHeadProvider.browser.spec.ts +196 -0
  415. package/src/react/head/providers/BrowserHeadProvider.ts +212 -0
  416. package/src/react/head/providers/HeadProvider.ts +168 -0
  417. package/src/react/head/providers/ServerHeadProvider.ts +31 -0
  418. package/src/react/i18n/__tests__/integration.spec.tsx +239 -0
  419. package/src/react/i18n/components/Localize.spec.tsx +357 -0
  420. package/src/react/i18n/components/Localize.tsx +35 -0
  421. package/src/react/i18n/hooks/useI18n.browser.spec.tsx +438 -0
  422. package/src/react/i18n/hooks/useI18n.ts +18 -0
  423. package/src/react/i18n/index.ts +41 -0
  424. package/src/react/i18n/primitives/$dictionary.ts +69 -0
  425. package/src/react/i18n/providers/I18nProvider.spec.ts +389 -0
  426. package/src/react/i18n/providers/I18nProvider.ts +278 -0
  427. package/src/react/router/__tests__/page-head-browser.browser.spec.ts +95 -0
  428. package/src/react/router/__tests__/page-head.spec.ts +48 -0
  429. package/src/react/router/__tests__/seo-head.spec.ts +125 -0
  430. package/src/react/router/atoms/ssrManifestAtom.ts +58 -0
  431. package/src/react/router/components/ErrorViewer.tsx +872 -0
  432. package/src/react/router/components/Link.tsx +23 -0
  433. package/src/react/router/components/NestedView.tsx +223 -0
  434. package/src/react/router/components/NotFound.tsx +30 -0
  435. package/src/react/router/constants/PAGE_PRELOAD_KEY.ts +6 -0
  436. package/src/react/router/contexts/RouterLayerContext.ts +12 -0
  437. package/src/react/router/errors/Redirection.ts +28 -0
  438. package/src/react/router/hooks/useActive.ts +52 -0
  439. package/src/react/router/hooks/useQueryParams.ts +63 -0
  440. package/src/react/router/hooks/useRouter.ts +20 -0
  441. package/src/react/router/hooks/useRouterState.ts +11 -0
  442. package/src/react/router/index.browser.ts +45 -0
  443. package/src/react/router/index.shared.ts +19 -0
  444. package/src/react/router/index.ts +142 -0
  445. package/src/react/router/primitives/$page.browser.spec.tsx +851 -0
  446. package/src/react/router/primitives/$page.spec.tsx +708 -0
  447. package/src/react/router/primitives/$page.ts +497 -0
  448. package/src/react/router/providers/ReactBrowserProvider.ts +309 -0
  449. package/src/react/router/providers/ReactBrowserRendererProvider.ts +25 -0
  450. package/src/react/router/providers/ReactBrowserRouterProvider.ts +168 -0
  451. package/src/react/router/providers/ReactPageProvider.ts +726 -0
  452. package/src/react/router/providers/ReactServerProvider.spec.tsx +316 -0
  453. package/src/react/router/providers/ReactServerProvider.ts +558 -0
  454. package/src/react/router/providers/ReactServerTemplateProvider.ts +979 -0
  455. package/src/react/router/providers/SSRManifestProvider.ts +334 -0
  456. package/src/react/router/services/ReactPageServerService.ts +48 -0
  457. package/src/react/router/services/ReactPageService.ts +27 -0
  458. package/src/react/router/services/ReactRouter.ts +262 -0
  459. package/src/react/websocket/hooks/useRoom.tsx +242 -0
  460. package/src/react/websocket/index.ts +7 -0
  461. package/src/redis/__tests__/redis.spec.ts +13 -0
  462. package/src/redis/index.ts +9 -25
  463. package/src/redis/providers/BunRedisProvider.ts +9 -0
  464. package/src/redis/providers/NodeRedisProvider.ts +8 -0
  465. package/src/redis/providers/RedisProvider.ts +16 -0
  466. package/src/retry/index.ts +11 -2
  467. package/src/router/index.ts +15 -0
  468. package/src/scheduler/index.ts +11 -2
  469. package/src/security/__tests__/BasicAuth.spec.ts +2 -0
  470. package/src/security/__tests__/ServerSecurityProvider.spec.ts +13 -5
  471. package/src/security/index.ts +15 -10
  472. package/src/security/interfaces/IssuerResolver.ts +27 -0
  473. package/src/security/primitives/$issuer.ts +55 -0
  474. package/src/security/providers/SecurityProvider.ts +179 -0
  475. package/src/security/providers/ServerBasicAuthProvider.ts +6 -2
  476. package/src/security/providers/ServerSecurityProvider.ts +36 -22
  477. package/src/server/auth/index.ts +12 -7
  478. package/src/server/cache/index.ts +7 -22
  479. package/src/server/compress/index.ts +10 -2
  480. package/src/server/cookies/index.ts +7 -5
  481. package/src/server/cookies/primitives/$cookie.ts +33 -11
  482. package/src/server/core/index.ts +17 -7
  483. package/src/server/core/interfaces/ServerRequest.ts +83 -1
  484. package/src/server/core/primitives/$action.spec.ts +1 -1
  485. package/src/server/core/primitives/$action.ts +8 -3
  486. package/src/server/core/providers/BunHttpServerProvider.ts +1 -1
  487. package/src/server/core/providers/NodeHttpServerProvider.spec.ts +125 -0
  488. package/src/server/core/providers/NodeHttpServerProvider.ts +77 -22
  489. package/src/server/core/providers/ServerLoggerProvider.ts +2 -2
  490. package/src/server/core/providers/ServerProvider.ts +9 -12
  491. package/src/server/core/services/ServerRequestParser.spec.ts +520 -0
  492. package/src/server/core/services/ServerRequestParser.ts +306 -13
  493. package/src/server/cors/index.ts +7 -21
  494. package/src/server/cors/primitives/$cors.ts +6 -2
  495. package/src/server/health/index.ts +8 -2
  496. package/src/server/helmet/index.ts +11 -3
  497. package/src/server/links/atoms/apiLinksAtom.ts +7 -0
  498. package/src/server/links/index.browser.ts +2 -0
  499. package/src/server/links/index.ts +13 -6
  500. package/src/server/metrics/index.ts +10 -3
  501. package/src/server/multipart/index.ts +9 -3
  502. package/src/server/proxy/index.ts +8 -2
  503. package/src/server/rate-limit/index.ts +21 -25
  504. package/src/server/rate-limit/primitives/$rateLimit.ts +6 -2
  505. package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +38 -14
  506. package/src/server/rate-limit/providers/ServerRateLimitProvider.ts +22 -56
  507. package/src/server/static/index.ts +8 -2
  508. package/src/server/static/providers/ServerStaticProvider.ts +1 -1
  509. package/src/server/swagger/index.ts +9 -4
  510. package/src/server/swagger/providers/ServerSwaggerProvider.ts +1 -1
  511. package/src/sms/index.ts +9 -5
  512. package/src/sms/providers/LocalSmsProvider.spec.ts +1 -1
  513. package/src/sms/providers/LocalSmsProvider.ts +1 -1
  514. package/src/system/index.browser.ts +11 -0
  515. package/src/system/index.ts +62 -0
  516. package/src/{file → system}/providers/FileSystemProvider.ts +16 -0
  517. package/src/{file → system}/providers/MemoryFileSystemProvider.ts +116 -3
  518. package/src/system/providers/MemoryShellProvider.ts +164 -0
  519. package/src/{file → system}/providers/NodeFileSystemProvider.spec.ts +2 -2
  520. package/src/{file → system}/providers/NodeFileSystemProvider.ts +36 -0
  521. package/src/system/providers/NodeShellProvider.ts +184 -0
  522. package/src/system/providers/ShellProvider.ts +74 -0
  523. package/src/{file → system}/services/FileDetector.spec.ts +2 -2
  524. package/src/thread/index.ts +11 -2
  525. package/src/topic/core/index.ts +12 -5
  526. package/src/vite/index.ts +3 -2
  527. package/src/vite/tasks/buildClient.ts +2 -8
  528. package/src/vite/tasks/buildServer.ts +84 -21
  529. package/src/vite/tasks/copyAssets.ts +5 -4
  530. package/src/vite/tasks/generateSitemap.ts +64 -23
  531. package/src/vite/tasks/index.ts +0 -2
  532. package/src/vite/tasks/prerenderPages.ts +49 -24
  533. package/src/websocket/index.ts +12 -8
  534. package/dist/file/index.d.ts +0 -839
  535. package/dist/file/index.d.ts.map +0 -1
  536. package/dist/file/index.js.map +0 -1
  537. package/src/cli/assets/indexHtml.ts +0 -15
  538. package/src/cli/assets/mainServerTs.ts +0 -24
  539. package/src/cli/assets/webAppRouterTs.ts +0 -15
  540. package/src/cli/assets/webHelloComponentTsx.ts +0 -16
  541. package/src/cli/commands/format.ts +0 -23
  542. package/src/file/index.ts +0 -43
  543. package/src/vite/helpers/boot.ts +0 -117
  544. package/src/vite/plugins/viteAlephaDev.ts +0 -177
  545. package/src/vite/tasks/devServer.ts +0 -71
  546. package/src/vite/tasks/runAlepha.ts +0 -270
  547. /package/dist/orm/{chunk-DtkW-qnP.js → chunk-DH6iiROE.js} +0 -0
  548. /package/src/cli/{assets → templates}/apiIndexTs.ts +0 -0
  549. /package/src/cli/{assets → templates}/webIndexTs.ts +0 -0
  550. /package/src/{file → system}/errors/FileError.ts +0 -0
  551. /package/src/{file → system}/services/FileDetector.ts +0 -0
@@ -1,7 +1,7 @@
1
1
  import * as alepha1 from "alepha";
2
2
  import { KIND, Primitive, Static } from "alepha";
3
3
  import { ServerRequest, ServerRouterProvider } from "alepha/server";
4
- import * as alepha_cache0 from "alepha/cache";
4
+ import { CacheProvider } from "alepha/cache";
5
5
  import * as alepha_logger0 from "alepha/logger";
6
6
 
7
7
  //#region ../../src/server/rate-limit/providers/ServerRateLimitProvider.d.ts
@@ -34,40 +34,35 @@ declare class ServerRateLimitProvider {
34
34
  RATE_LIMIT_WINDOW_MS: number;
35
35
  RATE_LIMIT_MAX_REQUESTS: number;
36
36
  };
37
- protected readonly cache: alepha_cache0.CachePrimitiveFn<RateLimitData, any[]>;
37
+ protected readonly cacheProvider: CacheProvider;
38
38
  protected readonly globalOptions: Readonly<{
39
39
  windowMs?: number | undefined;
40
40
  max?: number | undefined;
41
41
  skipFailedRequests?: boolean | undefined;
42
42
  skipSuccessfulRequests?: boolean | undefined;
43
43
  }>;
44
+ protected static readonly CACHE_NAME = "rate-limit";
44
45
  /**
45
- * Registered rate limit configurations with their path patterns
46
- */
46
+ * Registered rate limit configurations with their path patterns
47
+ */
47
48
  readonly registeredConfigs: RateLimitPrimitiveOptions[];
48
49
  /**
49
- * Register a rate limit configuration (called by primitives)
50
- */
50
+ * Register a rate limit configuration (called by primitives)
51
+ */
51
52
  registerRateLimit(config: RateLimitPrimitiveOptions): void;
52
53
  protected readonly onStart: alepha1.HookPrimitive<"start">;
53
54
  readonly onRequest: alepha1.HookPrimitive<"server:onRequest">;
54
55
  readonly onActionRequest: alepha1.HookPrimitive<"action:onRequest">;
55
56
  /**
56
- * Build complete rate limit options by merging with global defaults
57
- */
57
+ * Build complete rate limit options by merging with global defaults
58
+ */
58
59
  protected buildRateLimitOptions(config: RateLimitPrimitiveOptions): RateLimitOptions;
59
60
  /**
60
- * Set rate limit headers on the response
61
- */
61
+ * Set rate limit headers on the response
62
+ */
62
63
  protected setRateLimitHeaders(request: ServerRequest, result: RateLimitResult): void;
63
64
  checkLimit(req: ServerRequest, options?: RateLimitOptions): Promise<RateLimitResult>;
64
65
  protected generateKey(req: ServerRequest): string;
65
- protected getClientIP(req: ServerRequest): string;
66
- }
67
- interface RateLimitData {
68
- count: number;
69
- windowStart: number;
70
- hits: number[];
71
66
  }
72
67
  //#endregion
73
68
  //#region ../../src/server/rate-limit/primitives/$rateLimit.d.ts
@@ -102,9 +97,13 @@ declare const $rateLimit: {
102
97
  [KIND]: typeof RateLimitPrimitive;
103
98
  };
104
99
  interface RateLimitPrimitiveOptions extends RateLimitOptions {
105
- /** Name identifier for this rate limit (default: property key) */
100
+ /**
101
+ * Name identifier for this rate limit (default: property key).
102
+ */
106
103
  name?: string;
107
- /** Path patterns to match (supports wildcards like /api/*) */
104
+ /**
105
+ * Path patterns to match (supports wildcards like /api/*).
106
+ */
108
107
  paths?: string[];
109
108
  }
110
109
  interface AbstractRateLimitPrimitive {
@@ -117,8 +116,8 @@ declare class RateLimitPrimitive extends Primitive<RateLimitPrimitiveOptions> im
117
116
  get name(): string;
118
117
  protected onInit(): void;
119
118
  /**
120
- * Checks rate limit for the given request using this primitive's configuration.
121
- */
119
+ * Checks rate limit for the given request using this primitive's configuration.
120
+ */
122
121
  check(request: ServerRequest, options?: RateLimitOptions): Promise<RateLimitResult>;
123
122
  }
124
123
  //#endregion
@@ -126,55 +125,51 @@ declare class RateLimitPrimitive extends Primitive<RateLimitPrimitiveOptions> im
126
125
  declare module "alepha/server" {
127
126
  interface ActionPrimitiveOptions<TConfig> {
128
127
  /**
129
- * Rate limiting configuration for this action.
130
- * When specified, the action will be rate limited according to these settings.
131
- */
128
+ * Rate limiting configuration for this action.
129
+ * When specified, the action will be rate limited according to these settings.
130
+ */
132
131
  rateLimit?: RateLimitOptions;
133
132
  }
134
133
  interface ServerRoute {
135
134
  /**
136
- * Route-specific rate limit configuration.
137
- * If set, overrides the global rate limit options for this route.
138
- */
135
+ * Route-specific rate limit configuration.
136
+ * If set, overrides the global rate limit options for this route.
137
+ */
139
138
  rateLimit?: RateLimitOptions;
140
139
  }
141
140
  }
142
141
  interface RateLimitOptions {
143
- /** Maximum number of requests per window (default: 100) */
142
+ /**
143
+ * Maximum number of requests per window (default: 100).
144
+ */
144
145
  max?: number;
145
- /** Window duration in milliseconds (default: 15 minutes) */
146
+ /**
147
+ * Window duration in milliseconds (default: 15 minutes).
148
+ */
146
149
  windowMs?: number;
147
- /** Custom key generator function */
150
+ /**
151
+ * Custom key generator function.
152
+ */
148
153
  keyGenerator?: (req: any) => string;
149
- /** Skip rate limiting for failed requests */
154
+ /**
155
+ * Skip rate limiting for failed requests.
156
+ */
150
157
  skipFailedRequests?: boolean;
151
- /** Skip rate limiting for successful requests */
158
+ /**
159
+ * Skip rate limiting for successful requests.
160
+ */
152
161
  skipSuccessfulRequests?: boolean;
153
162
  }
154
163
  /**
155
- * Provides rate limiting capabilities for server routes and actions with configurable limits and windows.
156
- *
157
- * The server-rate-limit module enables per-route and per-action rate limiting using either:
158
- * - The `$rateLimit` primitive with `paths` option for path-based rate limiting
159
- * - The `rateLimit` option in action primitives for action-specific limiting
160
- *
161
- * It offers sliding window rate limiting, custom key generation, and seamless integration with server routes.
164
+ * | type | quality | stability |
165
+ * |------|---------|-----------|
166
+ * | backend | standard | stable |
162
167
  *
163
- * @example
164
- * ```ts
165
- * import { $rateLimit, AlephaServerRateLimit } from "alepha/server/rate-limit";
168
+ * Request rate limiting on actions.
166
169
  *
167
- * class ApiService {
168
- * // Path-specific rate limiting
169
- * apiRateLimit = $rateLimit({
170
- * paths: ["/api/*"],
171
- * max: 100,
172
- * windowMs: 15 * 60 * 1000, // 15 minutes
173
- * });
174
- * }
175
- * ```
170
+ * **Features:**
171
+ * - Rate limit configuration per action
176
172
  *
177
- * @see {@link $rateLimit}
178
173
  * @module alepha.server.rate-limit
179
174
  */
180
175
  declare const AlephaServerRateLimit: alepha1.Service<alepha1.Module>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/server/rate-limit/providers/ServerRateLimitProvider.ts","../../../src/server/rate-limit/primitives/$rateLimit.ts","../../../src/server/rate-limit/index.ts"],"mappings":";;;;;;;UAaiB,eAAA;EAAA,OAAA;EAAA,KAAA;EAAA,SAAA;EAAA,SAAA;EAAA,UAAA;AAAA;AAAA;AAWjB;;AAXiB,cAWJ,gBAAA,EAAgB,OAAA,CAAA,IAAA,SAAA,OAAA;EAAA,QAAA,oBAyB3B,OAAA,CAAA,OAAA;EAAA,GAAA;;;;KAEU,oBAAA,GAAuB,MAAA,QAAc,gBAAA,CAAiB,MAAA;AAAA;EAAA,UAAA,KAAA;IAAA,CAI7D,gBAAA,CAAiB,GAAA,GAAM,oBAAA;EAAA;AAAA;AAAA,cAiBf,uBAAA;EAAA,mBAAA,GAAA,EAAuB,cAAA,CACZ,MAAA;EAAA,mBAAA,oBAAA,EACiB,oBAAA;EAAA,mBAAA,GAAA;IAAA,oBAAA;IAAA,uBAAA;EAAA;EAAA,mBAAA,KAAA,EAGf,aAAA,CAAA,gBAAA,CAAA,aAAA;EAAA,mBAAA,aAAA,EAKQ,QAAA;IAAA,QAAA;IAAA,GAAA;IAAA,kBAAA;IAAA,sBAAA;EAAA;EAAA;;;EAAA,SAAA,iBAAA,EAKG,yBAAA;EAAA;;;EAAA,kBAAA,MAAA,EAKF,yBAAA;EAAA,mBAAA,OAAA,EAAyB,OAAA,CAIhC,aAAA;EAAA,SAAA,SAAA,EAAA,OAAA,CAuBD,aAAA;EAAA,SAAA,eAAA,EAAA,OAAA,CAuBM,aAAA;EAAA;;;EAAA,UAAA,sBAAA,MAAA,EA4BrB,yBAAA,GACP,gBAAA;EAAA;;;EAAA,UAAA,oBAAA,OAAA,EAiBQ,aAAA,EAAA,MAAA,EACD,eAAA;EAAA,WAAA,GAAA,EAkBH,aAAA,EAAA,OAAA,GACI,gBAAA,GACR,OAAA,CAAQ,eAAA;EAAA,UAAA,YAAA,GAAA,EAiDgB,aAAA;EAAA,UAAA,YAAA,GAAA,EAMA,aAAA;AAAA;AAAA,UAanB,aAAA;EAAA,KAAA;EAAA,WAAA;EAAA,IAAA;AAAA;;;;ACnPV;;;;;;AAQA;AAOA;;;;;;;AASA;;;;;;;;;;;cAxBa,UAAA;EAAA,CAAA,OAAA,GACF,yBAAA,GACR,0BAAA;EAAA;;UAMc,yBAAA,SAAkC,gBAAA;EAAA;EAAA,IAAA;EAAA;EAAA,KAAA;AAAA;AAAA,UAOlC,0BAAA;EAAA,SAAA,IAAA;EAAA,SAAA,OAAA,EAEG,yBAAA;EAAA,KAAA,CAAA,OAAA,EAEP,aAAA,EAAA,OAAA,GACC,gBAAA,GACT,OAAA,CAAQ,eAAA;AAAA;AAAA,cAGA,kBAAA,SACH,SAAA,CAAU,yBAAA,aACP,0BAAA;EAAA,mBAAA,uBAAA,EAE+B,uBAAA;EAAA,IAAA,KAAA;EAAA,UAAA,OAAA;EAAA;;;EAAA,MAAA,OAAA,EAe/B,aAAA,EAAA,OAAA,GACC,gBAAA,GACT,OAAA,CAAQ,eAAA;AAAA;;;;;;ACvE0C;;;gBAUvC,gBAAA;EAAA;EAAA,UAAA,WAAA;IAAA;;;AAchB;IAdgB,SAAA,GAQA,gBAAA;EAAA;AAAA;AAAA,UAMC,gBAAA;EAAA;EAAA,GAAA;EAAA;EAAA,QAAA;EAAA;EAAA,YAAA,IAAA,GAAA;EAAA;EAAA,kBAAA;EAAA;EAAA,sBAAA;AAAA;AAAA;AAuCjB;;;;;;;;;;;;;;;;;;;;;;;;;AAvCiB,cAuCJ,qBAAA,EAAqB,OAAA,CAAA,OAAA,CAIhC,OAAA,CAJgC,MAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/server/rate-limit/providers/ServerRateLimitProvider.ts","../../../src/server/rate-limit/primitives/$rateLimit.ts","../../../src/server/rate-limit/index.ts"],"mappings":";;;;;;;UAaiB,eAAA;EACf,OAAA;EACA,KAAA;EACA,SAAA;EACA,SAAA;EACA,UAAA;AAAA;;;;cAMW,gBAAA,EAAgB,OAAA,CAAA,IAAA,SAAA,OAAA;8BAyB3B,OAAA,CAAA,OAAA;;;;;KAEU,oBAAA,GAAuB,MAAA,QAAc,gBAAA,CAAiB,MAAA;AAAA;EAAA,UAGtD,KAAA;IAAA,CACP,gBAAA,CAAiB,GAAA,GAAM,oBAAA;EAAA;AAAA;AAAA,cAiBf,uBAAA;EAAA,mBACQ,GAAA,EADe,cAAA,CACZ,MAAA;EAAA,mBACH,oBAAA,EAAoB,oBAAA;EAAA,mBACpB,GAAA;;;;qBACA,aAAA,EAAa,aAAA;EAAA,mBACb,aAAA,EAAa,QAAA;;;;;;4BAEN,UAAA;;;;WAKV,iBAAA,EAAmB,yBAAA;;;;EAK5B,iBAAA,CAAkB,MAAA,EAAQ,yBAAA;EAAA,mBAId,OAAA,EAJuC,OAAA,CAIhC,aAAA;EAAA,SAuBV,SAAA,EAvBU,OAAA,CAuBD,aAAA;EAAA,SAuBT,eAAA,EAvBS,OAAA,CAuBM,aAAA;EAxFE;;;EAAA,UAmHvB,qBAAA,CACR,MAAA,EAAQ,yBAAA,GACP,gBAAA;EArHmE;AAAE;;EAAF,UAqI5D,mBAAA,CACR,OAAA,EAAS,aAAA,EACT,MAAA,EAAQ,eAAA;EAiBG,UAAA,CACX,GAAA,EAAK,aAAA,EACL,OAAA,GAAS,gBAAA,GACR,OAAA,CAAQ,eAAA;EAAA,UAqCD,WAAA,CAAY,GAAA,EAAK,aAAA;AAAA;;;;;;AAtO7B;;;;;;;;;;;AAWA;;;;;;;;;;;;cCUa,UAAA;EAAA,WACF,yBAAA,GACR,0BAAA;EAAA;;UAMc,yBAAA,SAAkC,gBAAA;;;;EAIjD,IAAA;;;;EAIA,KAAA;AAAA;AAAA,UAGe,0BAAA;EAAA,SACN,IAAA;EAAA,SACA,OAAA,EAAS,yBAAA;EAClB,KAAA,CACE,OAAA,EAAS,aAAA,EACT,OAAA,GAAU,gBAAA,GACT,OAAA,CAAQ,eAAA;AAAA;AAAA,cAGA,kBAAA,SACH,SAAA,CAAU,yBAAA,aACP,0BAAA;EAAA,mBAEQ,uBAAA,EAAuB,uBAAA;EAAA,IAE/B,IAAA,CAAA;EAAA,UAID,MAAA,CAAA;EDrBuB;;;EC6BpB,KAAA,CACX,OAAA,EAAS,aAAA,EACT,OAAA,GAAU,gBAAA,GACT,OAAA,CAAQ,eAAA;AAAA;;;;YCtED,sBAAA;;;;AFAZ;IEKI,SAAA,GAAY,gBAAA;EAAA;EAAA,UAGJ,WAAA;IFPV;;;;IEYE,SAAA,GAAY,gBAAA;EAAA;AAAA;AAAA,UAMC,gBAAA;EFiBf;;;EEbA,GAAA;;;;EAIA,QAAA;;;;EAIA,YAAA,IAAgB,GAAA;EFpBW;;;EEwB3B,kBAAA;;;;EAIA,sBAAA;AAAA;;;;;;;;;;AFDF;;;cEgBa,qBAAA,EAAqB,OAAA,CAAA,OAAA,CAIhC,OAAA,CAJgC,MAAA"}
@@ -1,6 +1,6 @@
1
1
  import { $atom, $env, $hook, $inject, $module, $use, KIND, Primitive, createPrimitive, t } from "alepha";
2
2
  import { AlephaServer, HttpError, ServerRouterProvider } from "alepha/server";
3
- import { $cache } from "alepha/cache";
3
+ import { CacheProvider } from "alepha/cache";
4
4
  import { $logger } from "alepha/logger";
5
5
 
6
6
  //#region ../../src/server/rate-limit/providers/ServerRateLimitProvider.ts
@@ -27,15 +27,13 @@ const envSchema = t.object({
27
27
  description: "Maximum requests per window"
28
28
  })
29
29
  });
30
- var ServerRateLimitProvider = class {
30
+ var ServerRateLimitProvider = class ServerRateLimitProvider {
31
31
  log = $logger();
32
32
  serverRouterProvider = $inject(ServerRouterProvider);
33
33
  env = $env(envSchema);
34
- cache = $cache({
35
- name: "server-rate-limit",
36
- ttl: [this.env.RATE_LIMIT_WINDOW_MS, "milliseconds"]
37
- });
34
+ cacheProvider = $inject(CacheProvider);
38
35
  globalOptions = $use(rateLimitOptions);
36
+ static CACHE_NAME = "rate-limit";
39
37
  /**
40
38
  * Registered rate limit configurations with their path patterns
41
39
  */
@@ -104,45 +102,24 @@ var ServerRateLimitProvider = class {
104
102
  async checkLimit(req, options = {}) {
105
103
  const windowMs = options.windowMs ?? this.env.RATE_LIMIT_WINDOW_MS;
106
104
  const max = options.max ?? this.env.RATE_LIMIT_MAX_REQUESTS;
107
- const key = this.generateKey(req);
105
+ const baseKey = this.generateKey(req);
108
106
  const now = Date.now();
109
- const windowStart = now - windowMs;
110
- const currentData = await this.cache.get(key) || {
111
- count: 0,
112
- windowStart: now,
113
- hits: []
114
- };
115
- const validHits = currentData.hits.filter((hit) => hit >= windowStart);
116
- const allowed = validHits.length < max;
117
- const remaining = Math.max(0, max - validHits.length);
118
- const resetTime = Math.max(...validHits, windowStart) + windowMs;
119
- if (allowed) {
120
- validHits.push(now);
121
- await this.cache.set(key, {
122
- count: validHits.length,
123
- windowStart: Math.min(currentData.windowStart, windowStart),
124
- hits: validHits
125
- });
126
- }
107
+ const windowStart = Math.floor(now / windowMs) * windowMs;
108
+ const resetTime = windowStart + windowMs;
109
+ const key = `${baseKey}:${windowStart}`;
110
+ const count = await this.cacheProvider.incr(ServerRateLimitProvider.CACHE_NAME, key, 1);
111
+ const allowed = count <= max;
127
112
  const result = {
128
113
  allowed,
129
114
  limit: max,
130
- remaining: allowed ? remaining - 1 : remaining,
115
+ remaining: Math.max(0, max - count),
131
116
  resetTime
132
117
  };
133
118
  if (!allowed) result.retryAfter = Math.ceil((resetTime - now) / 1e3);
134
119
  return result;
135
120
  }
136
121
  generateKey(req) {
137
- return `ip:${this.getClientIP(req)}`;
138
- }
139
- getClientIP(req) {
140
- const forwarded = req.headers?.["x-forwarded-for"];
141
- if (forwarded) {
142
- const firstIp = forwarded.split(",")[0].trim();
143
- if (firstIp) return firstIp;
144
- }
145
- return req.ip || "unknown";
122
+ return `ip:${req.ip || "unknown"}`;
146
123
  }
147
124
  };
148
125
 
@@ -201,29 +178,15 @@ $rateLimit[KIND] = RateLimitPrimitive;
201
178
  //#endregion
202
179
  //#region ../../src/server/rate-limit/index.ts
203
180
  /**
204
- * Provides rate limiting capabilities for server routes and actions with configurable limits and windows.
205
- *
206
- * The server-rate-limit module enables per-route and per-action rate limiting using either:
207
- * - The `$rateLimit` primitive with `paths` option for path-based rate limiting
208
- * - The `rateLimit` option in action primitives for action-specific limiting
181
+ * | type | quality | stability |
182
+ * |------|---------|-----------|
183
+ * | backend | standard | stable |
209
184
  *
210
- * It offers sliding window rate limiting, custom key generation, and seamless integration with server routes.
211
- *
212
- * @example
213
- * ```ts
214
- * import { $rateLimit, AlephaServerRateLimit } from "alepha/server/rate-limit";
185
+ * Request rate limiting on actions.
215
186
  *
216
- * class ApiService {
217
- * // Path-specific rate limiting
218
- * apiRateLimit = $rateLimit({
219
- * paths: ["/api/*"],
220
- * max: 100,
221
- * windowMs: 15 * 60 * 1000, // 15 minutes
222
- * });
223
- * }
224
- * ```
187
+ * **Features:**
188
+ * - Rate limit configuration per action
225
189
  *
226
- * @see {@link $rateLimit}
227
190
  * @module alepha.server.rate-limit
228
191
  */
229
192
  const AlephaServerRateLimit = $module({
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/server/rate-limit/providers/ServerRateLimitProvider.ts","../../../src/server/rate-limit/primitives/$rateLimit.ts","../../../src/server/rate-limit/index.ts"],"sourcesContent":["import { $atom, $env, $hook, $inject, $use, type Static, t } from \"alepha\";\nimport { $cache } from \"alepha/cache\";\nimport { $logger } from \"alepha/logger\";\nimport {\n HttpError,\n type ServerRequest,\n ServerRouterProvider,\n} from \"alepha/server\";\nimport type { RateLimitOptions } from \"../index.ts\";\nimport type { RateLimitPrimitiveOptions } from \"../primitives/$rateLimit.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface RateLimitResult {\n allowed: boolean;\n limit: number;\n remaining: number;\n resetTime: number;\n retryAfter?: number;\n}\n\n/**\n * Rate limit configuration atom (global defaults)\n */\nexport const rateLimitOptions = $atom({\n name: \"alepha.server.rate-limit.options\",\n schema: t.object({\n windowMs: t.optional(\n t.number({\n description: \"Window duration in milliseconds\",\n }),\n ),\n max: t.optional(\n t.number({\n description: \"Maximum number of requests per window\",\n }),\n ),\n skipFailedRequests: t.optional(\n t.boolean({\n description: \"Skip rate limiting for failed requests\",\n }),\n ),\n skipSuccessfulRequests: t.optional(\n t.boolean({\n description: \"Skip rate limiting for successful requests\",\n }),\n ),\n }),\n default: {},\n});\n\nexport type RateLimitAtomOptions = Static<typeof rateLimitOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [rateLimitOptions.key]: RateLimitAtomOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nconst envSchema = t.object({\n RATE_LIMIT_WINDOW_MS: t.number({\n default: 15 * 60 * 1000, // 15 minutes\n description: \"Rate limit window in milliseconds\",\n }),\n RATE_LIMIT_MAX_REQUESTS: t.number({\n default: 100,\n description: \"Maximum requests per window\",\n }),\n});\n\nexport class ServerRateLimitProvider {\n protected readonly log = $logger();\n protected readonly serverRouterProvider = $inject(ServerRouterProvider);\n protected readonly env = $env(envSchema);\n\n protected readonly cache = $cache<RateLimitData>({\n name: \"server-rate-limit\",\n ttl: [this.env.RATE_LIMIT_WINDOW_MS, \"milliseconds\"],\n });\n\n protected readonly globalOptions = $use(rateLimitOptions);\n\n /**\n * Registered rate limit configurations with their path patterns\n */\n public readonly registeredConfigs: RateLimitPrimitiveOptions[] = [];\n\n /**\n * Register a rate limit configuration (called by primitives)\n */\n public registerRateLimit(config: RateLimitPrimitiveOptions): void {\n this.registeredConfigs.push(config);\n }\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n // Apply path-specific rate limit configs to routes\n for (const config of this.registeredConfigs) {\n if (config.paths) {\n for (const pattern of config.paths) {\n const matchedRoutes = this.serverRouterProvider.getRoutes(pattern);\n for (const route of matchedRoutes) {\n route.rateLimit = this.buildRateLimitOptions(config);\n }\n }\n }\n }\n\n if (this.registeredConfigs.length > 0) {\n this.log.info(\n `Initialized with ${this.registeredConfigs.length} registered rate-limit configurations.`,\n );\n }\n },\n });\n\n public readonly onRequest = $hook({\n on: \"server:onRequest\",\n handler: async ({ route, request }) => {\n // Use route-specific rate limit if defined, otherwise use global options\n const rateLimitConfig = route.rateLimit ?? this.globalOptions;\n\n // Skip if no rate limiting configured\n if (!rateLimitConfig.max && !rateLimitConfig.windowMs) {\n return;\n }\n\n const result = await this.checkLimit(request, rateLimitConfig);\n this.setRateLimitHeaders(request, result);\n\n if (!result.allowed) {\n throw new HttpError({\n status: 429,\n message: \"Too Many Requests\",\n });\n }\n },\n });\n\n public readonly onActionRequest = $hook({\n on: \"action:onRequest\",\n handler: async ({ action, request }) => {\n // Check if this action has rate limiting enabled\n const rateLimit = action.options?.rateLimit;\n if (!rateLimit) {\n return; // No rate limiting for this action\n }\n\n const result = await this.checkLimit(request, rateLimit);\n\n if (!result.allowed) {\n // Actions are internal - don't set HTTP headers\n // Only throw error to prevent action execution\n throw new HttpError({\n status: 429,\n message: \"Too Many Requests\",\n });\n }\n\n // Action allowed - no headers to set since actions are internal\n },\n });\n\n /**\n * Build complete rate limit options by merging with global defaults\n */\n protected buildRateLimitOptions(\n config: RateLimitPrimitiveOptions,\n ): RateLimitOptions {\n return {\n max: config.max ?? this.globalOptions.max,\n windowMs: config.windowMs ?? this.globalOptions.windowMs,\n keyGenerator: config.keyGenerator,\n skipFailedRequests:\n config.skipFailedRequests ?? this.globalOptions.skipFailedRequests,\n skipSuccessfulRequests:\n config.skipSuccessfulRequests ??\n this.globalOptions.skipSuccessfulRequests,\n };\n }\n\n /**\n * Set rate limit headers on the response\n */\n protected setRateLimitHeaders(\n request: ServerRequest,\n result: RateLimitResult,\n ): void {\n request.reply.setHeader(\"X-RateLimit-Limit\", result.limit.toString());\n request.reply.setHeader(\n \"X-RateLimit-Remaining\",\n result.remaining.toString(),\n );\n request.reply.setHeader(\n \"X-RateLimit-Reset\",\n Math.ceil(result.resetTime / 1000).toString(),\n );\n\n if (!result.allowed && result.retryAfter) {\n request.reply.setHeader(\"Retry-After\", result.retryAfter.toString());\n }\n }\n\n public async checkLimit(\n req: ServerRequest,\n options: RateLimitOptions = {},\n ): Promise<RateLimitResult> {\n const windowMs = options.windowMs ?? this.env.RATE_LIMIT_WINDOW_MS;\n const max = options.max ?? this.env.RATE_LIMIT_MAX_REQUESTS;\n const key = this.generateKey(req);\n\n const now = Date.now();\n const windowStart = now - windowMs;\n\n // Get current rate limit data\n const currentData = (await this.cache.get(key)) || {\n count: 0,\n windowStart: now,\n hits: [],\n };\n\n // Clean old hits outside the current window\n const validHits = currentData.hits.filter(\n (hit: number) => hit >= windowStart,\n );\n\n // Check if limit exceeded\n const allowed = validHits.length < max;\n const remaining = Math.max(0, max - validHits.length);\n const resetTime = Math.max(...validHits, windowStart) + windowMs;\n\n // If allowed, record this request\n if (allowed) {\n validHits.push(now);\n await this.cache.set(key, {\n count: validHits.length,\n windowStart: Math.min(currentData.windowStart, windowStart),\n hits: validHits,\n });\n }\n\n const result: RateLimitResult = {\n allowed,\n limit: max,\n remaining: allowed ? remaining - 1 : remaining,\n resetTime,\n };\n\n if (!allowed) {\n result.retryAfter = Math.ceil((resetTime - now) / 1000);\n }\n\n return result;\n }\n\n protected generateKey(req: ServerRequest): string {\n // Default to IP-based rate limiting\n const ip = this.getClientIP(req);\n return `ip:${ip}`;\n }\n\n protected getClientIP(req: ServerRequest): string {\n // Check x-forwarded-for header first (for proxies/load balancers)\n const forwarded = req.headers?.[\"x-forwarded-for\"];\n if (forwarded) {\n // x-forwarded-for can contain multiple IPs, get the first one (original client)\n const firstIp = forwarded.split(\",\")[0].trim();\n if (firstIp) return firstIp;\n }\n\n return req.ip || \"unknown\";\n }\n}\n\ninterface RateLimitData {\n count: number;\n windowStart: number;\n hits: number[];\n}\n","import { $inject, createPrimitive, KIND, Primitive } from \"alepha\";\nimport type { ServerRequest } from \"alepha/server\";\nimport type { RateLimitOptions } from \"../index.ts\";\nimport {\n type RateLimitResult,\n ServerRateLimitProvider,\n} from \"../providers/ServerRateLimitProvider.ts\";\n\n/**\n * Declares rate limiting for server routes or custom usage.\n * This primitive provides methods to check rate limits and configure behavior\n * within the server request/response cycle.\n *\n * @example\n * ```ts\n * class ApiService {\n * // Apply rate limiting to specific paths\n * apiRateLimit = $rateLimit({\n * paths: [\"/api/*\"],\n * max: 100,\n * windowMs: 15 * 60 * 1000, // 15 minutes\n * });\n *\n * // Or use check() method for manual rate limiting\n * customAction = $action({\n * handler: async (req) => {\n * const result = await this.apiRateLimit.check(req);\n * if (!result.allowed) throw new Error(\"Rate limited\");\n * return \"ok\";\n * },\n * });\n * }\n * ```\n */\nexport const $rateLimit = (\n options: RateLimitPrimitiveOptions = {},\n): AbstractRateLimitPrimitive => {\n return createPrimitive(RateLimitPrimitive, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface RateLimitPrimitiveOptions extends RateLimitOptions {\n /** Name identifier for this rate limit (default: property key) */\n name?: string;\n /** Path patterns to match (supports wildcards like /api/*) */\n paths?: string[];\n}\n\nexport interface AbstractRateLimitPrimitive {\n readonly name: string;\n readonly options: RateLimitPrimitiveOptions;\n check(\n request: ServerRequest,\n options?: RateLimitOptions,\n ): Promise<RateLimitResult>;\n}\n\nexport class RateLimitPrimitive\n extends Primitive<RateLimitPrimitiveOptions>\n implements AbstractRateLimitPrimitive\n{\n protected readonly serverRateLimitProvider = $inject(ServerRateLimitProvider);\n\n public get name(): string {\n return this.options.name ?? `${this.config.propertyKey}`;\n }\n\n protected onInit() {\n // Register this rate limit configuration with the provider\n this.serverRateLimitProvider.registerRateLimit(this.options);\n }\n\n /**\n * Checks rate limit for the given request using this primitive's configuration.\n */\n public async check(\n request: ServerRequest,\n options?: RateLimitOptions,\n ): Promise<RateLimitResult> {\n const mergedOptions = { ...this.options, ...options };\n return this.serverRateLimitProvider.checkLimit(request, mergedOptions);\n }\n}\n\n$rateLimit[KIND] = RateLimitPrimitive;\n","import { $module } from \"alepha\";\nimport { AlephaServer } from \"alepha/server\";\nimport { $rateLimit } from \"./primitives/$rateLimit.ts\";\nimport { ServerRateLimitProvider } from \"./providers/ServerRateLimitProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$rateLimit.ts\";\nexport * from \"./providers/ServerRateLimitProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha/server\" {\n interface ActionPrimitiveOptions<TConfig> {\n /**\n * Rate limiting configuration for this action.\n * When specified, the action will be rate limited according to these settings.\n */\n rateLimit?: RateLimitOptions;\n }\n\n interface ServerRoute {\n /**\n * Route-specific rate limit configuration.\n * If set, overrides the global rate limit options for this route.\n */\n rateLimit?: RateLimitOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface RateLimitOptions {\n /** Maximum number of requests per window (default: 100) */\n max?: number;\n /** Window duration in milliseconds (default: 15 minutes) */\n windowMs?: number;\n /** Custom key generator function */\n keyGenerator?: (req: any) => string;\n /** Skip rate limiting for failed requests */\n skipFailedRequests?: boolean;\n /** Skip rate limiting for successful requests */\n skipSuccessfulRequests?: boolean;\n}\n\n/**\n * Provides rate limiting capabilities for server routes and actions with configurable limits and windows.\n *\n * The server-rate-limit module enables per-route and per-action rate limiting using either:\n * - The `$rateLimit` primitive with `paths` option for path-based rate limiting\n * - The `rateLimit` option in action primitives for action-specific limiting\n *\n * It offers sliding window rate limiting, custom key generation, and seamless integration with server routes.\n *\n * @example\n * ```ts\n * import { $rateLimit, AlephaServerRateLimit } from \"alepha/server/rate-limit\";\n *\n * class ApiService {\n * // Path-specific rate limiting\n * apiRateLimit = $rateLimit({\n * paths: [\"/api/*\"],\n * max: 100,\n * windowMs: 15 * 60 * 1000, // 15 minutes\n * });\n * }\n * ```\n *\n * @see {@link $rateLimit}\n * @module alepha.server.rate-limit\n */\nexport const AlephaServerRateLimit = $module({\n name: \"alepha.server.rate-limit\",\n primitives: [$rateLimit],\n services: [AlephaServer, ServerRateLimitProvider],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/server/rate-limit/providers/ServerRateLimitProvider.ts","../../../src/server/rate-limit/primitives/$rateLimit.ts","../../../src/server/rate-limit/index.ts"],"sourcesContent":["import { $atom, $env, $hook, $inject, $use, type Static, t } from \"alepha\";\nimport { CacheProvider } from \"alepha/cache\";\nimport { $logger } from \"alepha/logger\";\nimport {\n HttpError,\n type ServerRequest,\n ServerRouterProvider,\n} from \"alepha/server\";\nimport type { RateLimitOptions } from \"../index.ts\";\nimport type { RateLimitPrimitiveOptions } from \"../primitives/$rateLimit.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface RateLimitResult {\n allowed: boolean;\n limit: number;\n remaining: number;\n resetTime: number;\n retryAfter?: number;\n}\n\n/**\n * Rate limit configuration atom (global defaults)\n */\nexport const rateLimitOptions = $atom({\n name: \"alepha.server.rate-limit.options\",\n schema: t.object({\n windowMs: t.optional(\n t.number({\n description: \"Window duration in milliseconds\",\n }),\n ),\n max: t.optional(\n t.number({\n description: \"Maximum number of requests per window\",\n }),\n ),\n skipFailedRequests: t.optional(\n t.boolean({\n description: \"Skip rate limiting for failed requests\",\n }),\n ),\n skipSuccessfulRequests: t.optional(\n t.boolean({\n description: \"Skip rate limiting for successful requests\",\n }),\n ),\n }),\n default: {},\n});\n\nexport type RateLimitAtomOptions = Static<typeof rateLimitOptions.schema>;\n\ndeclare module \"alepha\" {\n interface State {\n [rateLimitOptions.key]: RateLimitAtomOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nconst envSchema = t.object({\n RATE_LIMIT_WINDOW_MS: t.number({\n default: 15 * 60 * 1000, // 15 minutes\n description: \"Rate limit window in milliseconds\",\n }),\n RATE_LIMIT_MAX_REQUESTS: t.number({\n default: 100,\n description: \"Maximum requests per window\",\n }),\n});\n\nexport class ServerRateLimitProvider {\n protected readonly log = $logger();\n protected readonly serverRouterProvider = $inject(ServerRouterProvider);\n protected readonly env = $env(envSchema);\n protected readonly cacheProvider = $inject(CacheProvider);\n protected readonly globalOptions = $use(rateLimitOptions);\n\n protected static readonly CACHE_NAME = \"rate-limit\";\n\n /**\n * Registered rate limit configurations with their path patterns\n */\n public readonly registeredConfigs: RateLimitPrimitiveOptions[] = [];\n\n /**\n * Register a rate limit configuration (called by primitives)\n */\n public registerRateLimit(config: RateLimitPrimitiveOptions): void {\n this.registeredConfigs.push(config);\n }\n\n protected readonly onStart = $hook({\n on: \"start\",\n handler: async () => {\n // Apply path-specific rate limit configs to routes\n for (const config of this.registeredConfigs) {\n if (config.paths) {\n for (const pattern of config.paths) {\n const matchedRoutes = this.serverRouterProvider.getRoutes(pattern);\n for (const route of matchedRoutes) {\n route.rateLimit = this.buildRateLimitOptions(config);\n }\n }\n }\n }\n\n if (this.registeredConfigs.length > 0) {\n this.log.info(\n `Initialized with ${this.registeredConfigs.length} registered rate-limit configurations.`,\n );\n }\n },\n });\n\n public readonly onRequest = $hook({\n on: \"server:onRequest\",\n handler: async ({ route, request }) => {\n // Use route-specific rate limit if defined, otherwise use global options\n const rateLimitConfig = route.rateLimit ?? this.globalOptions;\n\n // Skip if no rate limiting configured\n if (!rateLimitConfig.max && !rateLimitConfig.windowMs) {\n return;\n }\n\n const result = await this.checkLimit(request, rateLimitConfig);\n this.setRateLimitHeaders(request, result);\n\n if (!result.allowed) {\n throw new HttpError({\n status: 429,\n message: \"Too Many Requests\",\n });\n }\n },\n });\n\n public readonly onActionRequest = $hook({\n on: \"action:onRequest\",\n handler: async ({ action, request }) => {\n // Check if this action has rate limiting enabled\n const rateLimit = action.options?.rateLimit;\n if (!rateLimit) {\n return; // No rate limiting for this action\n }\n\n const result = await this.checkLimit(request, rateLimit);\n\n if (!result.allowed) {\n // Actions are internal - don't set HTTP headers\n // Only throw error to prevent action execution\n throw new HttpError({\n status: 429,\n message: \"Too Many Requests\",\n });\n }\n\n // Action allowed - no headers to set since actions are internal\n },\n });\n\n /**\n * Build complete rate limit options by merging with global defaults\n */\n protected buildRateLimitOptions(\n config: RateLimitPrimitiveOptions,\n ): RateLimitOptions {\n return {\n max: config.max ?? this.globalOptions.max,\n windowMs: config.windowMs ?? this.globalOptions.windowMs,\n keyGenerator: config.keyGenerator,\n skipFailedRequests:\n config.skipFailedRequests ?? this.globalOptions.skipFailedRequests,\n skipSuccessfulRequests:\n config.skipSuccessfulRequests ??\n this.globalOptions.skipSuccessfulRequests,\n };\n }\n\n /**\n * Set rate limit headers on the response\n */\n protected setRateLimitHeaders(\n request: ServerRequest,\n result: RateLimitResult,\n ): void {\n request.reply.setHeader(\"X-RateLimit-Limit\", result.limit.toString());\n request.reply.setHeader(\n \"X-RateLimit-Remaining\",\n result.remaining.toString(),\n );\n request.reply.setHeader(\n \"X-RateLimit-Reset\",\n Math.ceil(result.resetTime / 1000).toString(),\n );\n\n if (!result.allowed && result.retryAfter) {\n request.reply.setHeader(\"Retry-After\", result.retryAfter.toString());\n }\n }\n\n public async checkLimit(\n req: ServerRequest,\n options: RateLimitOptions = {},\n ): Promise<RateLimitResult> {\n const windowMs = options.windowMs ?? this.env.RATE_LIMIT_WINDOW_MS;\n const max = options.max ?? this.env.RATE_LIMIT_MAX_REQUESTS;\n const baseKey = this.generateKey(req);\n\n const now = Date.now();\n // Fixed window: round down to nearest window boundary\n const windowStart = Math.floor(now / windowMs) * windowMs;\n const resetTime = windowStart + windowMs;\n\n // Include window timestamp in key for automatic expiration of old windows\n const key = `${baseKey}:${windowStart}`;\n\n // Atomic increment - returns the new count after incrementing\n const count = await this.cacheProvider.incr(\n ServerRateLimitProvider.CACHE_NAME,\n key,\n 1,\n );\n\n const allowed = count <= max;\n const remaining = Math.max(0, max - count);\n\n const result: RateLimitResult = {\n allowed,\n limit: max,\n remaining,\n resetTime,\n };\n\n if (!allowed) {\n result.retryAfter = Math.ceil((resetTime - now) / 1000);\n }\n\n return result;\n }\n\n protected generateKey(req: ServerRequest): string {\n // Use req.ip which is resolved by ServerRequestParser with proper trust proxy handling\n return `ip:${req.ip || \"unknown\"}`;\n }\n}\n","import { $inject, createPrimitive, KIND, Primitive } from \"alepha\";\nimport type { ServerRequest } from \"alepha/server\";\nimport type { RateLimitOptions } from \"../index.ts\";\nimport {\n type RateLimitResult,\n ServerRateLimitProvider,\n} from \"../providers/ServerRateLimitProvider.ts\";\n\n/**\n * Declares rate limiting for server routes or custom usage.\n * This primitive provides methods to check rate limits and configure behavior\n * within the server request/response cycle.\n *\n * @example\n * ```ts\n * class ApiService {\n * // Apply rate limiting to specific paths\n * apiRateLimit = $rateLimit({\n * paths: [\"/api/*\"],\n * max: 100,\n * windowMs: 15 * 60 * 1000, // 15 minutes\n * });\n *\n * // Or use check() method for manual rate limiting\n * customAction = $action({\n * handler: async (req) => {\n * const result = await this.apiRateLimit.check(req);\n * if (!result.allowed) throw new Error(\"Rate limited\");\n * return \"ok\";\n * },\n * });\n * }\n * ```\n */\nexport const $rateLimit = (\n options: RateLimitPrimitiveOptions = {},\n): AbstractRateLimitPrimitive => {\n return createPrimitive(RateLimitPrimitive, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface RateLimitPrimitiveOptions extends RateLimitOptions {\n /**\n * Name identifier for this rate limit (default: property key).\n */\n name?: string;\n /**\n * Path patterns to match (supports wildcards like /api/*).\n */\n paths?: string[];\n}\n\nexport interface AbstractRateLimitPrimitive {\n readonly name: string;\n readonly options: RateLimitPrimitiveOptions;\n check(\n request: ServerRequest,\n options?: RateLimitOptions,\n ): Promise<RateLimitResult>;\n}\n\nexport class RateLimitPrimitive\n extends Primitive<RateLimitPrimitiveOptions>\n implements AbstractRateLimitPrimitive\n{\n protected readonly serverRateLimitProvider = $inject(ServerRateLimitProvider);\n\n public get name(): string {\n return this.options.name ?? `${this.config.propertyKey}`;\n }\n\n protected onInit() {\n // Register this rate limit configuration with the provider\n this.serverRateLimitProvider.registerRateLimit(this.options);\n }\n\n /**\n * Checks rate limit for the given request using this primitive's configuration.\n */\n public async check(\n request: ServerRequest,\n options?: RateLimitOptions,\n ): Promise<RateLimitResult> {\n const mergedOptions = { ...this.options, ...options };\n return this.serverRateLimitProvider.checkLimit(request, mergedOptions);\n }\n}\n\n$rateLimit[KIND] = RateLimitPrimitive;\n","import { $module } from \"alepha\";\nimport { AlephaServer } from \"alepha/server\";\nimport { $rateLimit } from \"./primitives/$rateLimit.ts\";\nimport { ServerRateLimitProvider } from \"./providers/ServerRateLimitProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$rateLimit.ts\";\nexport * from \"./providers/ServerRateLimitProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha/server\" {\n interface ActionPrimitiveOptions<TConfig> {\n /**\n * Rate limiting configuration for this action.\n * When specified, the action will be rate limited according to these settings.\n */\n rateLimit?: RateLimitOptions;\n }\n\n interface ServerRoute {\n /**\n * Route-specific rate limit configuration.\n * If set, overrides the global rate limit options for this route.\n */\n rateLimit?: RateLimitOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface RateLimitOptions {\n /**\n * Maximum number of requests per window (default: 100).\n */\n max?: number;\n /**\n * Window duration in milliseconds (default: 15 minutes).\n */\n windowMs?: number;\n /**\n * Custom key generator function.\n */\n keyGenerator?: (req: any) => string;\n /**\n * Skip rate limiting for failed requests.\n */\n skipFailedRequests?: boolean;\n /**\n * Skip rate limiting for successful requests.\n */\n skipSuccessfulRequests?: boolean;\n}\n\n/**\n * | type | quality | stability |\n * |------|---------|-----------|\n * | backend | standard | stable |\n *\n * Request rate limiting on actions.\n *\n * **Features:**\n * - Rate limit configuration per action\n *\n * @module alepha.server.rate-limit\n */\nexport const AlephaServerRateLimit = $module({\n name: \"alepha.server.rate-limit\",\n primitives: [$rateLimit],\n services: [AlephaServer, ServerRateLimitProvider],\n});\n"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
1
  import * as alepha1 from "alepha";
2
- import { Alepha, KIND, Primitive } from "alepha";
2
+ import { Alepha, Json, KIND, Primitive } from "alepha";
3
3
  import { ServerHandler, ServerRouterProvider } from "alepha/server";
4
4
  import { DateTimeProvider, DurationLike } from "alepha/datetime";
5
- import { FileDetector } from "alepha/file";
6
5
  import * as alepha_logger0 from "alepha/logger";
6
+ import { Readable } from "node:stream";
7
7
 
8
8
  //#region ../../src/server/static/primitives/$serve.d.ts
9
9
  /**
@@ -15,76 +15,203 @@ declare const $serve: {
15
15
  };
16
16
  interface ServePrimitiveOptions {
17
17
  /**
18
- * Prefix for the served path.
19
- *
20
- * @default "/"
21
- */
18
+ * Prefix for the served path.
19
+ *
20
+ * @default "/"
21
+ */
22
22
  path?: string;
23
23
  /**
24
- * Path to the directory to serve.
25
- *
26
- * @default process.cwd()
27
- */
24
+ * Path to the directory to serve.
25
+ *
26
+ * @default process.cwd()
27
+ */
28
28
  root?: string;
29
29
  /**
30
- * If true, primitive will be ignored.
31
- *
32
- * @default false
33
- */
30
+ * If true, primitive will be ignored.
31
+ *
32
+ * @default false
33
+ */
34
34
  disabled?: boolean;
35
35
  /**
36
- * Whether to keep dot files (e.g. `.gitignore`, `.env`) in the served directory.
37
- *
38
- * @default true
39
- */
36
+ * Whether to keep dot files (e.g. `.gitignore`, `.env`) in the served directory.
37
+ *
38
+ * @default true
39
+ */
40
40
  ignoreDotEnvFiles?: boolean;
41
41
  /**
42
- * Whether to use the index.html file when the path is a directory.
43
- *
44
- * @default true
45
- */
42
+ * Whether to use the index.html file when the path is a directory.
43
+ *
44
+ * @default true
45
+ */
46
46
  indexFallback?: boolean;
47
47
  /**
48
- * Force all requests "not found" to be served with the index.html file.
49
- * This is useful for single-page applications (SPAs) that use client-side only routing.
50
- */
48
+ * Force all requests "not found" to be served with the index.html file.
49
+ * This is useful for single-page applications (SPAs) that use client-side only routing.
50
+ */
51
51
  historyApiFallback?: boolean;
52
52
  /**
53
- * Optional name of the primitive.
54
- * This is used for logging and debugging purposes.
55
- *
56
- * @default Key name.
57
- */
53
+ * Optional name of the primitive.
54
+ * This is used for logging and debugging purposes.
55
+ *
56
+ * @default Key name.
57
+ */
58
58
  name?: string;
59
59
  /**
60
- * Whether to use cache control headers.
61
- *
62
- * @default {}
63
- */
60
+ * Whether to use cache control headers.
61
+ *
62
+ * @default {}
63
+ */
64
64
  cacheControl?: Partial<CacheControlOptions> | false;
65
65
  }
66
66
  interface CacheControlOptions {
67
67
  /**
68
- * Whether to use cache control headers.
69
- *
70
- * @default [.js, .css]
71
- */
68
+ * Whether to use cache control headers.
69
+ *
70
+ * @default [.js, .css]
71
+ */
72
72
  fileTypes: string[];
73
73
  /**
74
- * The maximum age of the cache in seconds.
75
- *
76
- * @default 60 * 60 * 24 * 2 // 2 days
77
- */
74
+ * The maximum age of the cache in seconds.
75
+ *
76
+ * @default 60 * 60 * 24 * 2 // 2 days
77
+ */
78
78
  maxAge: DurationLike;
79
79
  /**
80
- * Whether to use immutable cache control headers.
81
- *
82
- * @default true
83
- */
80
+ * Whether to use immutable cache control headers.
81
+ *
82
+ * @default true
83
+ */
84
84
  immutable: boolean;
85
85
  }
86
86
  declare class ServePrimitive extends Primitive<ServePrimitiveOptions> {}
87
87
  //#endregion
88
+ //#region ../../src/system/services/FileDetector.d.ts
89
+ interface FileTypeResult {
90
+ /**
91
+ * The detected MIME type
92
+ */
93
+ mimeType: string;
94
+ /**
95
+ * The detected file extension
96
+ */
97
+ extension: string;
98
+ /**
99
+ * Whether the file type was verified by magic bytes
100
+ */
101
+ verified: boolean;
102
+ /**
103
+ * The stream (potentially wrapped to allow re-reading)
104
+ */
105
+ stream: Readable;
106
+ }
107
+ /**
108
+ * Service for detecting file types and getting content types.
109
+ *
110
+ * @example
111
+ * ```typescript
112
+ * const detector = alepha.inject(FileDetector);
113
+ *
114
+ * // Get content type from filename
115
+ * const mimeType = detector.getContentType("image.png"); // "image/png"
116
+ *
117
+ * // Detect file type by magic bytes
118
+ * const stream = createReadStream('image.png');
119
+ * const result = await detector.detectFileType(stream, 'image.png');
120
+ * console.log(result.mimeType); // 'image/png'
121
+ * console.log(result.verified); // true if magic bytes match
122
+ * ```
123
+ */
124
+ declare class FileDetector {
125
+ /**
126
+ * Magic byte signatures for common file formats.
127
+ * Each signature is represented as an array of bytes or null (wildcard).
128
+ */
129
+ protected static readonly MAGIC_BYTES: Record<string, {
130
+ signature: (number | null)[];
131
+ mimeType: string;
132
+ }[]>;
133
+ /**
134
+ * All possible format signatures for checking against actual file content
135
+ */
136
+ protected static readonly ALL_SIGNATURES: {
137
+ signature: (number | null)[];
138
+ mimeType: string;
139
+ ext: string;
140
+ }[];
141
+ /**
142
+ * MIME type map for file extensions.
143
+ *
144
+ * Can be used to get the content type of file based on its extension.
145
+ * Feel free to add more mime types in your project!
146
+ */
147
+ static readonly mimeMap: Record<string, string>;
148
+ /**
149
+ * Reverse MIME type map for looking up extensions from MIME types.
150
+ * Prefers shorter, more common extensions when multiple exist.
151
+ */
152
+ protected static readonly reverseMimeMap: Record<string, string>;
153
+ /**
154
+ * Returns the file extension for a given MIME type.
155
+ *
156
+ * @param mimeType - The MIME type to look up
157
+ * @returns The file extension (without dot), or "bin" if not found
158
+ *
159
+ * @example
160
+ * ```typescript
161
+ * const detector = alepha.inject(FileDetector);
162
+ * const ext = detector.getExtensionFromMimeType("image/png"); // "png"
163
+ * const ext2 = detector.getExtensionFromMimeType("application/octet-stream"); // "bin"
164
+ * ```
165
+ */
166
+ getExtensionFromMimeType(mimeType: string): string;
167
+ /**
168
+ * Returns the content type of file based on its filename.
169
+ *
170
+ * @param filename - The filename to check
171
+ * @returns The MIME type
172
+ *
173
+ * @example
174
+ * ```typescript
175
+ * const detector = alepha.inject(FileDetector);
176
+ * const mimeType = detector.getContentType("image.png"); // "image/png"
177
+ * ```
178
+ */
179
+ getContentType(filename: string): string;
180
+ /**
181
+ * Detects the file type by checking magic bytes against the stream content.
182
+ *
183
+ * @param stream - The readable stream to check
184
+ * @param filename - The filename (used to get the extension)
185
+ * @returns File type information including MIME type, extension, and verification status
186
+ *
187
+ * @example
188
+ * ```typescript
189
+ * const detector = alepha.inject(FileDetector);
190
+ * const stream = createReadStream('image.png');
191
+ * const result = await detector.detectFileType(stream, 'image.png');
192
+ * console.log(result.mimeType); // 'image/png'
193
+ * console.log(result.verified); // true if magic bytes match
194
+ * ```
195
+ */
196
+ detectFileType(stream: Readable, filename: string): Promise<FileTypeResult>;
197
+ /**
198
+ * Reads all bytes from a stream and returns the first N bytes along with a new stream containing all data.
199
+ * This approach reads the entire stream upfront to avoid complex async handling issues.
200
+ *
201
+ * @protected
202
+ */
203
+ protected peekBytes(stream: Readable, numBytes: number): Promise<{
204
+ buffer: Buffer;
205
+ stream: Readable;
206
+ }>;
207
+ /**
208
+ * Checks if a buffer matches a magic byte signature.
209
+ *
210
+ * @protected
211
+ */
212
+ protected matchesSignature(buffer: Buffer, signature: (number | null)[]): boolean;
213
+ }
214
+ //#endregion
88
215
  //#region ../../src/server/static/providers/ServerStaticProvider.d.ts
89
216
  declare class ServerStaticProvider {
90
217
  protected readonly alepha: Alepha;
@@ -110,9 +237,15 @@ interface ServeDirectory {
110
237
  //#endregion
111
238
  //#region ../../src/server/static/index.d.ts
112
239
  /**
113
- * Create static file server with `$static()`.
240
+ * | type | quality | stability |
241
+ * |------|---------|-----------|
242
+ * | backend | standard | stable |
243
+ *
244
+ * Static file serving.
245
+ *
246
+ * **Features:**
247
+ * - Serve static files from directory
114
248
  *
115
- * @see {@link ServerStaticProvider}
116
249
  * @module alepha.server.static
117
250
  */
118
251
  declare const AlephaServerStatic: alepha1.Service<alepha1.Module>;