alepha 0.15.3 → 0.15.5

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 (318) hide show
  1. package/README.md +26 -11
  2. package/dist/api/audits/index.d.ts +335 -335
  3. package/dist/api/audits/index.d.ts.map +1 -1
  4. package/dist/api/audits/index.js +11 -3
  5. package/dist/api/audits/index.js.map +1 -1
  6. package/dist/api/files/index.d.ts +3 -3
  7. package/dist/api/files/index.js +4 -3
  8. package/dist/api/files/index.js.map +1 -1
  9. package/dist/api/jobs/index.d.ts +198 -155
  10. package/dist/api/jobs/index.d.ts.map +1 -1
  11. package/dist/api/jobs/index.js +103 -5
  12. package/dist/api/jobs/index.js.map +1 -1
  13. package/dist/api/keys/index.d.ts +198 -198
  14. package/dist/api/keys/index.d.ts.map +1 -1
  15. package/dist/api/keys/index.js +3 -3
  16. package/dist/api/keys/index.js.map +1 -1
  17. package/dist/api/notifications/index.browser.js +1 -0
  18. package/dist/api/notifications/index.browser.js.map +1 -1
  19. package/dist/api/notifications/index.d.ts +3 -3
  20. package/dist/api/notifications/index.js +4 -3
  21. package/dist/api/notifications/index.js.map +1 -1
  22. package/dist/api/parameters/index.d.ts +263 -263
  23. package/dist/api/parameters/index.d.ts.map +1 -1
  24. package/dist/api/parameters/index.js +41 -30
  25. package/dist/api/parameters/index.js.map +1 -1
  26. package/dist/api/users/index.d.ts +383 -77
  27. package/dist/api/users/index.d.ts.map +1 -1
  28. package/dist/api/users/index.js +284 -72
  29. package/dist/api/users/index.js.map +1 -1
  30. package/dist/api/verifications/index.d.ts +131 -131
  31. package/dist/api/verifications/index.d.ts.map +1 -1
  32. package/dist/api/verifications/index.js +3 -3
  33. package/dist/api/verifications/index.js.map +1 -1
  34. package/dist/batch/index.d.ts +3 -3
  35. package/dist/batch/index.js +3 -3
  36. package/dist/batch/index.js.map +1 -1
  37. package/dist/bucket/index.d.ts +3 -3
  38. package/dist/bucket/index.js +6 -6
  39. package/dist/bucket/index.js.map +1 -1
  40. package/dist/cache/core/index.d.ts +3 -3
  41. package/dist/cache/core/index.js +3 -3
  42. package/dist/cache/core/index.js.map +1 -1
  43. package/dist/cli/index.d.ts +5612 -20
  44. package/dist/cli/index.d.ts.map +1 -1
  45. package/dist/cli/index.js +122 -91
  46. package/dist/cli/index.js.map +1 -1
  47. package/dist/command/index.d.ts +11 -4
  48. package/dist/command/index.d.ts.map +1 -1
  49. package/dist/command/index.js +8 -6
  50. package/dist/command/index.js.map +1 -1
  51. package/dist/core/index.browser.js.map +1 -1
  52. package/dist/core/index.d.ts +4 -8
  53. package/dist/core/index.d.ts.map +1 -1
  54. package/dist/core/index.js +3 -3
  55. package/dist/core/index.js.map +1 -1
  56. package/dist/core/index.native.js.map +1 -1
  57. package/dist/datetime/index.d.ts +3 -3
  58. package/dist/datetime/index.js +3 -3
  59. package/dist/datetime/index.js.map +1 -1
  60. package/dist/email/index.d.ts +16 -16
  61. package/dist/email/index.d.ts.map +1 -1
  62. package/dist/email/index.js +10562 -10
  63. package/dist/email/index.js.map +1 -1
  64. package/dist/fake/index.d.ts +3 -3
  65. package/dist/fake/index.js +3 -3
  66. package/dist/fake/index.js.map +1 -1
  67. package/dist/lock/core/index.d.ts +9 -4
  68. package/dist/lock/core/index.d.ts.map +1 -1
  69. package/dist/lock/core/index.js +12 -4
  70. package/dist/lock/core/index.js.map +1 -1
  71. package/dist/logger/index.d.ts +3 -3
  72. package/dist/logger/index.js +6 -3
  73. package/dist/logger/index.js.map +1 -1
  74. package/dist/mcp/index.d.ts +3 -3
  75. package/dist/mcp/index.js +3 -3
  76. package/dist/mcp/index.js.map +1 -1
  77. package/dist/orm/index.d.ts +12 -12
  78. package/dist/orm/index.js +4 -4
  79. package/dist/orm/index.js.map +1 -1
  80. package/dist/queue/core/index.d.ts +3 -3
  81. package/dist/queue/core/index.js +3 -3
  82. package/dist/queue/core/index.js.map +1 -1
  83. package/dist/react/auth/index.browser.js +2 -1
  84. package/dist/react/auth/index.browser.js.map +1 -1
  85. package/dist/react/auth/index.d.ts +3 -3
  86. package/dist/react/auth/index.js +5 -4
  87. package/dist/react/auth/index.js.map +1 -1
  88. package/dist/react/core/index.d.ts +6 -6
  89. package/dist/react/core/index.js +3 -3
  90. package/dist/react/core/index.js.map +1 -1
  91. package/dist/react/form/index.d.ts +3 -3
  92. package/dist/react/form/index.js +3 -3
  93. package/dist/react/form/index.js.map +1 -1
  94. package/dist/react/head/index.d.ts +3 -3
  95. package/dist/react/head/index.js +3 -3
  96. package/dist/react/head/index.js.map +1 -1
  97. package/dist/react/i18n/index.d.ts +3 -3
  98. package/dist/react/i18n/index.js +3 -3
  99. package/dist/react/i18n/index.js.map +1 -1
  100. package/dist/react/intro/index.css +337 -0
  101. package/dist/react/intro/index.css.map +1 -0
  102. package/dist/react/intro/index.d.ts +10 -0
  103. package/dist/react/intro/index.d.ts.map +1 -0
  104. package/dist/react/intro/index.js +222 -0
  105. package/dist/react/intro/index.js.map +1 -0
  106. package/dist/react/router/index.browser.js +2 -2
  107. package/dist/react/router/index.browser.js.map +1 -1
  108. package/dist/react/router/index.d.ts +11 -1
  109. package/dist/react/router/index.d.ts.map +1 -1
  110. package/dist/react/router/index.js +21 -11
  111. package/dist/react/router/index.js.map +1 -1
  112. package/dist/redis/index.d.ts +22 -22
  113. package/dist/redis/index.js +3 -3
  114. package/dist/redis/index.js.map +1 -1
  115. package/dist/retry/index.d.ts +3 -3
  116. package/dist/retry/index.js +3 -3
  117. package/dist/retry/index.js.map +1 -1
  118. package/dist/scheduler/index.d.ts +16 -4
  119. package/dist/scheduler/index.d.ts.map +1 -1
  120. package/dist/scheduler/index.js +45 -7
  121. package/dist/scheduler/index.js.map +1 -1
  122. package/dist/security/index.d.ts +3 -3
  123. package/dist/security/index.js +5 -5
  124. package/dist/security/index.js.map +1 -1
  125. package/dist/server/auth/index.d.ts +3 -3
  126. package/dist/server/auth/index.js +3 -3
  127. package/dist/server/auth/index.js.map +1 -1
  128. package/dist/server/cache/index.d.ts +3 -3
  129. package/dist/server/cache/index.js +3 -3
  130. package/dist/server/cache/index.js.map +1 -1
  131. package/dist/server/compress/index.d.ts +3 -3
  132. package/dist/server/compress/index.d.ts.map +1 -1
  133. package/dist/server/compress/index.js +4 -3
  134. package/dist/server/compress/index.js.map +1 -1
  135. package/dist/server/cookies/index.d.ts +3 -3
  136. package/dist/server/cookies/index.js +3 -3
  137. package/dist/server/cookies/index.js.map +1 -1
  138. package/dist/server/core/index.d.ts +14 -25
  139. package/dist/server/core/index.d.ts.map +1 -1
  140. package/dist/server/core/index.js +13 -29
  141. package/dist/server/core/index.js.map +1 -1
  142. package/dist/server/cors/index.d.ts +3 -3
  143. package/dist/server/cors/index.js +3 -3
  144. package/dist/server/cors/index.js.map +1 -1
  145. package/dist/server/health/index.d.ts +20 -20
  146. package/dist/server/health/index.js +3 -3
  147. package/dist/server/health/index.js.map +1 -1
  148. package/dist/server/helmet/index.d.ts +3 -3
  149. package/dist/server/helmet/index.js +3 -3
  150. package/dist/server/helmet/index.js.map +1 -1
  151. package/dist/server/links/index.d.ts +42 -42
  152. package/dist/server/links/index.d.ts.map +1 -1
  153. package/dist/server/links/index.js +4 -4
  154. package/dist/server/links/index.js.map +1 -1
  155. package/dist/server/metrics/index.d.ts +3 -3
  156. package/dist/server/metrics/index.js +3 -3
  157. package/dist/server/metrics/index.js.map +1 -1
  158. package/dist/server/multipart/index.d.ts +3 -3
  159. package/dist/server/multipart/index.js +3 -3
  160. package/dist/server/multipart/index.js.map +1 -1
  161. package/dist/server/proxy/index.d.ts +3 -3
  162. package/dist/server/proxy/index.js +3 -3
  163. package/dist/server/proxy/index.js.map +1 -1
  164. package/dist/server/rate-limit/index.d.ts +3 -3
  165. package/dist/server/rate-limit/index.js +3 -3
  166. package/dist/server/rate-limit/index.js.map +1 -1
  167. package/dist/server/static/index.d.ts +3 -3
  168. package/dist/server/static/index.js +6 -6
  169. package/dist/server/static/index.js.map +1 -1
  170. package/dist/server/swagger/index.d.ts +3 -3
  171. package/dist/server/swagger/index.js +6 -6
  172. package/dist/server/swagger/index.js.map +1 -1
  173. package/dist/sms/index.d.ts +3 -3
  174. package/dist/sms/index.js +6 -6
  175. package/dist/sms/index.js.map +1 -1
  176. package/dist/system/index.d.ts +3 -3
  177. package/dist/system/index.js +3 -3
  178. package/dist/system/index.js.map +1 -1
  179. package/dist/thread/index.d.ts +3 -3
  180. package/dist/thread/index.js +3 -3
  181. package/dist/thread/index.js.map +1 -1
  182. package/dist/topic/core/index.d.ts +3 -3
  183. package/dist/topic/core/index.js +3 -3
  184. package/dist/topic/core/index.js.map +1 -1
  185. package/dist/vite/index.d.ts +6286 -4
  186. package/dist/vite/index.d.ts.map +1 -1
  187. package/dist/vite/index.js +28 -2
  188. package/dist/vite/index.js.map +1 -1
  189. package/dist/websocket/index.d.ts +37 -37
  190. package/dist/websocket/index.d.ts.map +1 -1
  191. package/dist/websocket/index.js +3 -3
  192. package/dist/websocket/index.js.map +1 -1
  193. package/package.json +12 -4
  194. package/src/api/audits/controllers/AdminAuditController.ts +8 -0
  195. package/src/api/audits/index.ts +3 -3
  196. package/src/api/files/controllers/AdminFileStatsController.ts +1 -0
  197. package/src/api/files/index.ts +3 -3
  198. package/src/api/jobs/controllers/AdminJobController.ts +18 -2
  199. package/src/api/jobs/index.ts +4 -3
  200. package/src/api/jobs/services/JobAudits.spec.ts +89 -0
  201. package/src/api/jobs/services/JobAudits.ts +101 -0
  202. package/src/api/keys/index.ts +3 -3
  203. package/src/api/notifications/controllers/AdminNotificationController.ts +1 -0
  204. package/src/api/notifications/index.ts +3 -3
  205. package/src/api/parameters/controllers/AdminConfigController.ts +10 -0
  206. package/src/api/parameters/index.ts +5 -3
  207. package/src/api/users/__tests__/ApiKeys-integration.spec.ts +1 -1
  208. package/src/api/users/__tests__/ApiKeys.spec.ts +1 -1
  209. package/src/api/users/__tests__/EmailVerification.spec.ts +16 -1
  210. package/src/api/users/__tests__/PasswordReset.spec.ts +11 -0
  211. package/src/api/users/atoms/realmAuthSettingsAtom.ts +10 -0
  212. package/src/api/users/controllers/AdminIdentityController.ts +3 -0
  213. package/src/api/users/controllers/AdminSessionController.ts +3 -0
  214. package/src/api/users/controllers/AdminUserController.ts +5 -0
  215. package/src/api/users/index.ts +8 -9
  216. package/src/api/users/primitives/$realm.ts +117 -19
  217. package/src/api/users/providers/RealmProvider.ts +15 -7
  218. package/src/api/users/services/CredentialService.spec.ts +11 -0
  219. package/src/api/users/services/CredentialService.ts +47 -24
  220. package/src/api/users/services/IdentityService.ts +12 -4
  221. package/src/api/users/services/RegistrationService.spec.ts +11 -0
  222. package/src/api/users/services/RegistrationService.ts +33 -12
  223. package/src/api/users/services/SessionService.ts +83 -12
  224. package/src/api/users/services/UserAudits.ts +47 -0
  225. package/src/api/users/services/UserFiles.ts +19 -0
  226. package/src/api/users/services/UserJobs.spec.ts +107 -0
  227. package/src/api/users/services/UserJobs.ts +62 -0
  228. package/src/api/users/services/UserParameters.ts +23 -0
  229. package/src/api/users/services/UserService.ts +34 -17
  230. package/src/api/verifications/index.ts +3 -3
  231. package/src/batch/index.ts +3 -3
  232. package/src/bucket/index.ts +3 -3
  233. package/src/cache/core/index.ts +3 -3
  234. package/src/cli/commands/build.ts +1 -0
  235. package/src/cli/commands/db.ts +9 -0
  236. package/src/cli/commands/init.spec.ts +2 -17
  237. package/src/cli/commands/init.ts +37 -1
  238. package/src/cli/providers/ViteDevServerProvider.ts +36 -2
  239. package/src/cli/services/AlephaCliUtils.ts +17 -0
  240. package/src/cli/services/PackageManagerUtils.ts +15 -1
  241. package/src/cli/services/ProjectScaffolder.ts +8 -13
  242. package/src/cli/templates/agentMd.ts +2 -25
  243. package/src/cli/templates/apiAppSecurityTs.ts +37 -2
  244. package/src/cli/templates/mainCss.ts +2 -32
  245. package/src/cli/templates/webAppRouterTs.ts +5 -5
  246. package/src/cli/templates/webHomeComponentTsx.ts +10 -0
  247. package/src/command/helpers/Runner.ts +14 -1
  248. package/src/command/index.ts +3 -3
  249. package/src/core/helpers/primitive.ts +0 -5
  250. package/src/core/index.ts +3 -3
  251. package/src/datetime/index.ts +3 -3
  252. package/src/email/index.ts +3 -3
  253. package/src/email/index.workerd.ts +36 -0
  254. package/src/email/providers/LocalEmailProvider.ts +2 -2
  255. package/src/email/providers/WorkermailerEmailProvider.ts +221 -0
  256. package/src/fake/index.ts +3 -3
  257. package/src/lock/core/index.ts +3 -3
  258. package/src/lock/core/primitives/$lock.ts +13 -1
  259. package/src/logger/index.ts +3 -3
  260. package/src/logger/providers/PrettyFormatterProvider.ts +7 -0
  261. package/src/mcp/index.ts +3 -3
  262. package/src/orm/index.ts +3 -3
  263. package/src/orm/providers/drivers/NodeSqliteProvider.ts +1 -1
  264. package/src/queue/core/index.ts +3 -3
  265. package/src/react/auth/index.ts +3 -3
  266. package/src/react/auth/services/ReactAuth.ts +3 -1
  267. package/src/react/core/index.ts +3 -3
  268. package/src/react/form/index.ts +3 -3
  269. package/src/react/head/index.ts +3 -3
  270. package/src/react/i18n/index.ts +3 -3
  271. package/src/react/intro/components/GettingStarted.css +334 -0
  272. package/src/react/intro/components/GettingStarted.tsx +276 -0
  273. package/src/react/intro/index.ts +1 -0
  274. package/src/react/router/atoms/ssrManifestAtom.ts +7 -0
  275. package/src/react/router/index.browser.ts +2 -0
  276. package/src/react/router/index.ts +2 -0
  277. package/src/react/router/providers/ReactServerProvider.ts +14 -4
  278. package/src/react/router/providers/SSRManifestProvider.ts +7 -0
  279. package/src/redis/index.ts +3 -3
  280. package/src/retry/index.ts +3 -3
  281. package/src/router/index.ts +3 -3
  282. package/src/scheduler/index.ts +3 -3
  283. package/src/scheduler/index.workerd.ts +43 -0
  284. package/src/scheduler/providers/CronProvider.ts +53 -6
  285. package/src/scheduler/providers/WorkerdCronProvider.ts +102 -0
  286. package/src/security/index.ts +3 -3
  287. package/src/security/providers/JwtProvider.ts +2 -2
  288. package/src/server/auth/index.ts +3 -3
  289. package/src/server/cache/index.ts +3 -3
  290. package/src/server/compress/index.ts +3 -3
  291. package/src/server/compress/providers/ServerCompressProvider.ts +6 -0
  292. package/src/server/cookies/index.ts +3 -3
  293. package/src/server/core/index.ts +3 -3
  294. package/src/server/core/primitives/$action.spec.ts +3 -2
  295. package/src/server/core/primitives/$action.ts +6 -2
  296. package/src/server/core/providers/NodeHttpServerProvider.ts +2 -15
  297. package/src/server/core/providers/ServerProvider.ts +4 -2
  298. package/src/server/core/providers/ServerRouterProvider.ts +5 -27
  299. package/src/server/cors/index.ts +3 -3
  300. package/src/server/health/index.ts +3 -3
  301. package/src/server/helmet/index.ts +3 -3
  302. package/src/server/links/index.ts +3 -3
  303. package/src/server/links/providers/ServerLinksProvider.spec.ts +332 -0
  304. package/src/server/links/providers/ServerLinksProvider.ts +1 -1
  305. package/src/server/metrics/index.ts +3 -3
  306. package/src/server/multipart/index.ts +3 -3
  307. package/src/server/proxy/index.ts +3 -3
  308. package/src/server/rate-limit/index.ts +3 -3
  309. package/src/server/static/index.ts +3 -3
  310. package/src/server/swagger/index.ts +3 -3
  311. package/src/sms/index.ts +3 -3
  312. package/src/system/index.ts +3 -3
  313. package/src/thread/index.ts +3 -3
  314. package/src/topic/core/index.ts +3 -3
  315. package/src/vite/tasks/generateCloudflare.ts +38 -2
  316. package/src/websocket/index.ts +3 -3
  317. package/src/cli/templates/webHelloComponentTsx.ts +0 -30
  318. /package/src/api/users/{notifications → services}/UserNotifications.ts +0 -0
@@ -18,9 +18,9 @@ export * from "./providers/RedisSubscriberProvider.ts";
18
18
  // ---------------------------------------------------------------------------------------------------------------------
19
19
 
20
20
  /**
21
- * | type | quality | stability |
22
- * |------|---------|-----------|
23
- * | backend | standard | stable |
21
+ * | Stability | Since | Runtime |
22
+ * |-----------|-------|---------|
23
+ * | 3 - stable | 0.1.0 | node, bun|
24
24
  *
25
25
  * Redis client wrapper.
26
26
  *
@@ -12,9 +12,9 @@ export * from "./providers/RetryProvider.ts";
12
12
  // ---------------------------------------------------------------------------------------------------------------------
13
13
 
14
14
  /**
15
- * | type | quality | stability |
16
- * |------|---------|-----------|
17
- * | backend | standard | stable |
15
+ * | Stability | Since | Runtime |
16
+ * |-----------|-------|---------|
17
+ * | 3 - stable | 0.12.0 | node, bun, workerd, browser, expo|
18
18
  *
19
19
  * Automatic retry with backoff.
20
20
  *
@@ -1,7 +1,7 @@
1
1
  /**
2
- * | type | quality | stability |
3
- * |------|---------|-----------|
4
- * | frontend | standard | stable |
2
+ * | Stability | Since | Runtime |
3
+ * |-----------|-------|---------|
4
+ * | 3 - stable | 0.2.0 | node, bun, workerd, browser, expo|
5
5
  *
6
6
  * Frontend routing infrastructure.
7
7
  *
@@ -35,9 +35,9 @@ declare module "alepha" {
35
35
  // ---------------------------------------------------------------------------------------------------------------------
36
36
 
37
37
  /**
38
- * | type | quality | stability |
39
- * |------|---------|-----------|
40
- * | backend | rare | stable |
38
+ * | Stability | Since | Runtime |
39
+ * |-----------|-------|---------|
40
+ * | 3 - stable | 0.1.0 | node, bun, workerd|
41
41
  *
42
42
  * Cron and interval-based task execution.
43
43
  *
@@ -0,0 +1,43 @@
1
+ import { $module } from "alepha";
2
+ import { AlephaLock } from "alepha/lock";
3
+ import { $scheduler } from "./primitives/$scheduler.ts";
4
+ import { CronProvider } from "./providers/CronProvider.ts";
5
+ import { WorkerdCronProvider } from "./providers/WorkerdCronProvider.ts";
6
+
7
+ // ---------------------------------------------------------------------------------------------------------------------
8
+
9
+ export * from "./constants/CRON.ts";
10
+ export * from "./primitives/$scheduler.ts";
11
+ export * from "./providers/CronProvider.ts";
12
+ export * from "./providers/WorkerdCronProvider.ts";
13
+
14
+ // ---------------------------------------------------------------------------------------------------------------------
15
+
16
+ declare module "alepha" {
17
+ interface Hooks {
18
+ /**
19
+ * Cloudflare Workers scheduled event.
20
+ *
21
+ * Emitted when a cron trigger fires in Cloudflare Workers.
22
+ */
23
+ "cloudflare:scheduled": {
24
+ cron: string;
25
+ scheduledTime: number;
26
+ };
27
+ }
28
+ }
29
+
30
+ // ---------------------------------------------------------------------------------------------------------------------
31
+
32
+ export const AlephaScheduler = $module({
33
+ name: "alepha.scheduler",
34
+ primitives: [$scheduler],
35
+ services: [AlephaLock, CronProvider, WorkerdCronProvider],
36
+ register: (alepha) => {
37
+ // Replace CronProvider with WorkerdCronProvider for Cloudflare Workers
38
+ alepha.with({
39
+ provide: CronProvider,
40
+ use: WorkerdCronProvider,
41
+ });
42
+ },
43
+ });
@@ -16,6 +16,11 @@ export class CronProvider {
16
16
  protected readonly start = $hook({
17
17
  on: "start",
18
18
  handler: () => {
19
+ if (this.alepha.isServerless()) {
20
+ this.log.info("Ignoring cron jobs in serverless environment");
21
+ return;
22
+ }
23
+
19
24
  for (const cron of this.cronJobs) {
20
25
  if (!cron.running) {
21
26
  cron.running = true;
@@ -62,12 +67,12 @@ export class CronProvider {
62
67
  ? this.cronJobs.find((c) => c.name === name)
63
68
  : name;
64
69
 
65
- if (!cron) {
70
+ if (!cron || !cron.running) {
66
71
  return;
67
72
  }
68
73
 
69
74
  cron.running = false;
70
- cron.abort.abort();
75
+ cron.abort?.abort();
71
76
  this.log.debug(`Cron task '${cron.name}' stopped`);
72
77
  }
73
78
 
@@ -109,13 +114,13 @@ export class CronProvider {
109
114
  }
110
115
 
111
116
  const duration = next.getTime() - now.toDate().getTime();
112
-
113
- task.abort = new AbortController();
117
+ const abort = new AbortController();
118
+ task.abort = abort;
114
119
 
115
120
  this.dt
116
121
  .wait(duration, {
117
122
  now: now.valueOf(),
118
- signal: task.abort.signal,
123
+ signal: abort.signal,
119
124
  })
120
125
  .then(() => {
121
126
  if (!task.running) {
@@ -141,6 +146,48 @@ export class CronProvider {
141
146
  this.log.warn("Issue during cron waiting timer", err as Error);
142
147
  });
143
148
  }
149
+
150
+ /**
151
+ * Trigger a specific cron job by name.
152
+ */
153
+ public async trigger(name: string): Promise<void> {
154
+ const job = this.cronJobs.find((j) => j.name === name);
155
+ if (!job) {
156
+ this.log.warn(`Cron job '${name}' not found`);
157
+ return;
158
+ }
159
+ await this.runJobs([job], this.dt.now());
160
+ }
161
+
162
+ /**
163
+ * Trigger all registered cron jobs.
164
+ */
165
+ public async triggerAll(): Promise<void> {
166
+ await this.runJobs(this.cronJobs, this.dt.now());
167
+ }
168
+
169
+ /**
170
+ * Run multiple cron jobs in parallel.
171
+ */
172
+ protected async runJobs(jobs: CronJob[], now: DateTime): Promise<void> {
173
+ const results = await Promise.allSettled(
174
+ jobs.map(async (job) => {
175
+ this.log.debug(`Running cron job '${job.name}'`);
176
+ try {
177
+ await job.handler({ now });
178
+ this.log.debug(`Cron job '${job.name}' completed`);
179
+ } catch (error) {
180
+ this.log.error(`Cron job '${job.name}' failed`, error);
181
+ throw error;
182
+ }
183
+ }),
184
+ );
185
+
186
+ const failures = results.filter((r) => r.status === "rejected");
187
+ if (failures.length > 0) {
188
+ this.log.error(`${failures.length}/${jobs.length} cron jobs failed`);
189
+ }
190
+ }
144
191
  }
145
192
 
146
193
  export interface CronJob {
@@ -151,5 +198,5 @@ export interface CronJob {
151
198
  loop: boolean;
152
199
  running?: boolean;
153
200
  onError?: (error: Error) => void;
154
- abort: AbortController;
201
+ abort?: AbortController;
155
202
  }
@@ -0,0 +1,102 @@
1
+ import { $hook } from "alepha";
2
+ import type { DateTime } from "alepha/datetime";
3
+ import { parseCronExpression } from "cron-schedule";
4
+ import { CronProvider } from "./CronProvider.ts";
5
+
6
+ // ---------------------------------------------------------------------------------------------------------------------
7
+
8
+ declare module "alepha" {
9
+ interface Hooks {
10
+ /**
11
+ * Cloudflare Workers scheduled event.
12
+ *
13
+ * Emitted when a cron trigger fires in Cloudflare Workers.
14
+ */
15
+ "cloudflare:scheduled": {
16
+ cron: string;
17
+ scheduledTime: number;
18
+ };
19
+ }
20
+ }
21
+
22
+ // ---------------------------------------------------------------------------------------------------------------------
23
+
24
+ /**
25
+ * Cloudflare Workers cron provider.
26
+ *
27
+ * This provider handles scheduled events from Cloudflare Workers Cron Triggers.
28
+ * Unlike the Node.js CronProvider, this doesn't use intervals/timeouts - instead,
29
+ * it reacts to scheduled events triggered by Cloudflare.
30
+ *
31
+ * **Usage:**
32
+ * 1. Define schedulers with `$scheduler({ cron: "0 * * * *", handler: ... })`
33
+ * 2. Build your app with `alepha build` - cron triggers are automatically added to `wrangler.jsonc`
34
+ * 3. Deploy to Cloudflare Workers
35
+ *
36
+ * **How it works:**
37
+ * - During build, all registered `$scheduler` cron expressions are collected
38
+ * - The build generates `wrangler.jsonc` with `triggers.crons` automatically filled
39
+ * - When Cloudflare fires a cron trigger, the `scheduled` handler emits `cloudflare:scheduled`
40
+ * - This provider listens to that event and runs matching schedulers
41
+ *
42
+ * @see https://developers.cloudflare.com/workers/configuration/cron-triggers/
43
+ */
44
+ export class WorkerdCronProvider extends CronProvider {
45
+ /**
46
+ * Override to avoid creating AbortController in global scope.
47
+ * Cloudflare Workers doesn't allow this during initialization.
48
+ */
49
+ public override createCronJob(
50
+ name: string,
51
+ expression: string,
52
+ handler: (context: { now: DateTime }) => Promise<void>,
53
+ ): void {
54
+ this.cronJobs.push({
55
+ name,
56
+ cron: parseCronExpression(expression),
57
+ expression,
58
+ handler,
59
+ loop: false,
60
+ });
61
+ }
62
+
63
+ /**
64
+ * Handle a scheduled event from Cloudflare Workers.
65
+ */
66
+ protected readonly onScheduledEvent = $hook({
67
+ on: "cloudflare:scheduled",
68
+ handler: async (event) => {
69
+ const now = this.dt.of(event.scheduledTime);
70
+
71
+ this.log.info("Received scheduled event", {
72
+ cron: event.cron,
73
+ scheduledTime: now.format(),
74
+ });
75
+
76
+ // Find jobs that match this cron expression
77
+ const matchingJobs = this.cronJobs.filter(
78
+ (job) => job.expression === event.cron,
79
+ );
80
+
81
+ if (matchingJobs.length === 0) {
82
+ // No exact match - try to find jobs that would fire at this time
83
+ const matchingByTime = this.cronJobs.filter((job) =>
84
+ job.cron.matchDate(now.toDate()),
85
+ );
86
+
87
+ if (matchingByTime.length > 0) {
88
+ this.log.debug(
89
+ `No exact cron match for '${event.cron}', found ${matchingByTime.length} jobs matching by time`,
90
+ );
91
+ await this.runJobs(matchingByTime, now);
92
+ return;
93
+ }
94
+
95
+ this.log.warn(`No cron jobs found for expression '${event.cron}'`);
96
+ return;
97
+ }
98
+
99
+ await this.runJobs(matchingJobs, now);
100
+ },
101
+ });
102
+ }
@@ -89,9 +89,9 @@ declare module "alepha/server" {
89
89
  }
90
90
 
91
91
  /**
92
- * | type | quality | stability |
93
- * |------|---------|-----------|
94
- * | backend | epic | stable |
92
+ * | Stability | Since | Runtime |
93
+ * |-----------|-------|---------|
94
+ * | 3 - stable | 0.1.0 | node, bun, workerd, browser|
95
95
  *
96
96
  * Complete authentication and authorization system with JWT, RBAC, and multi-issuer support.
97
97
  *
@@ -47,7 +47,7 @@ export class JwtProvider {
47
47
  } else if (this.isSecretKey(secretKeyOrJwks)) {
48
48
  const secretKey = this.encoder.encode(secretKeyOrJwks);
49
49
  this.log.info(
50
- `will verify JWTs from '${name}' with secret a key (${secretKey.length} bytes)`,
50
+ `will verify JWTs from issuer '${name}' with secret key (${secretKey.length} bytes)`,
51
51
  );
52
52
  this.keystore.push({
53
53
  name,
@@ -56,7 +56,7 @@ export class JwtProvider {
56
56
  });
57
57
  } else {
58
58
  this.log.info(
59
- `will verify JWTs from '${name}' with JWKS ${secretKeyOrJwks}`,
59
+ `will verify JWTs from issuer '${name}' with JWKS ${secretKeyOrJwks}`,
60
60
  );
61
61
  this.keystore.push({
62
62
  name,
@@ -29,9 +29,9 @@ declare module "alepha" {
29
29
  // ---------------------------------------------------------------------------------------------------------------------
30
30
 
31
31
  /**
32
- * | type | quality | stability |
33
- * |------|---------|-----------|
34
- * | backend | rare | stable |
32
+ * | Stability | Since | Runtime |
33
+ * |-----------|-------|---------|
34
+ * | 3 - stable | 0.8.0 | node, bun, workerd|
35
35
  *
36
36
  * OAuth2/OIDC authentication with social login providers.
37
37
  *
@@ -9,9 +9,9 @@ export * from "./providers/ServerCacheProvider.ts";
9
9
  // ---------------------------------------------------------------------------------------------------------------------
10
10
 
11
11
  /**
12
- * | type | quality | stability |
13
- * |------|---------|-----------|
14
- * | backend | standard | stable |
12
+ * | Stability | Since | Runtime |
13
+ * |-----------|-------|---------|
14
+ * | 3 - stable | 0.10.0 | node, bun, workerd|
15
15
  *
16
16
  * ETag-based response caching.
17
17
  *
@@ -9,9 +9,9 @@ export * from "./providers/ServerCompressProvider.ts";
9
9
  // ---------------------------------------------------------------------------------------------------------------------
10
10
 
11
11
  /**
12
- * | type | quality | stability |
13
- * |------|---------|-----------|
14
- * | backend | standard | stable |
12
+ * | Stability | Since | Runtime |
13
+ * |-----------|-------|---------|
14
+ * | 3 - stable | 0.13.0 | node, bun|
15
15
  *
16
16
  * Response compression.
17
17
  *
@@ -62,6 +62,12 @@ export class ServerCompressProvider {
62
62
  public readonly onResponse = $hook({
63
63
  on: "server:onResponse",
64
64
  handler: async ({ request, response }) => {
65
+ // In serverless (Cloudflare Workers), skip compression entirely:
66
+ // Cloudflare's edge network automatically compresses responses
67
+ if (this.alepha.isServerless()) {
68
+ return;
69
+ }
70
+
65
71
  // skip if already compressed
66
72
  if (response.headers["content-encoding"]) {
67
73
  return;
@@ -18,9 +18,9 @@ declare module "alepha/server" {
18
18
  }
19
19
 
20
20
  /**
21
- * | type | quality | stability |
22
- * |------|---------|-----------|
23
- * | backend | standard | stable |
21
+ * | Stability | Since | Runtime |
22
+ * |-----------|-------|---------|
23
+ * | 3 - stable | 0.3.0 | node, bun, workerd|
24
24
  *
25
25
  * Server and browser-safe cookie handling.
26
26
  *
@@ -114,9 +114,9 @@ export * from "./services/UserAgentParser.ts";
114
114
  // ---------------------------------------------------------------------------------------------------------------------
115
115
 
116
116
  /**
117
- * | type | quality | stability |
118
- * |------|---------|-----------|
119
- * | backend | epic | stable |
117
+ * | Stability | Since | Runtime |
118
+ * |-----------|-------|---------|
119
+ * | 3 - stable | 0.1.0 | node, bun, workerd|
120
120
  *
121
121
  * Convention-driven HTTP server with automatic validation and type inference.
122
122
  *
@@ -81,8 +81,9 @@ describe("$action", () => {
81
81
 
82
82
  expect(await app.a1.fetch({}).then((it) => it.data)).toBe("ok:a1");
83
83
  expect(await app.a2.fetch({}).then((it) => it.data)).toBe("Not Found");
84
- // note: $action disabled is callable locally
85
- expect(await app.a2.run({})).toBe("ok:a2");
84
+ await expect(app.a2.run({})).rejects.toThrowError(
85
+ "Action 'a2' is disabled.",
86
+ );
86
87
  });
87
88
 
88
89
  test("should return nothing", async ({ expect }) => {
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  $env,
3
3
  $inject,
4
+ AlephaError,
4
5
  type Async,
5
6
  createPrimitive,
6
7
  isTypeFile,
@@ -188,8 +189,8 @@ export interface ActionPrimitiveOptions<TConfig extends RequestConfigSchema>
188
189
  description?: string;
189
190
 
190
191
  /**
191
- * Disable the route. Useful with env variables do disable one specific route.
192
- * Route won't be available in the API but can still be called locally!
192
+ * Disable the route. Useful with env variables to disable one specific route.
193
+ * Route won't be available in the API nor locally.
193
194
  */
194
195
  disabled?: boolean;
195
196
 
@@ -317,6 +318,9 @@ export class ActionPrimitive<
317
318
  config?: ClientRequestEntry<TConfig>,
318
319
  options: ClientRequestOptions = {}, // most of the options are ignored here
319
320
  ): Promise<ClientRequestResponse<TConfig>> {
321
+ if (this.options.disabled) {
322
+ throw new AlephaError(`Action '${this.name}' is disabled.`);
323
+ }
320
324
  const handler = this.options.handler;
321
325
  const {
322
326
  body,
@@ -76,22 +76,9 @@ export class NodeHttpServerProvider extends ServerProvider {
76
76
  res.end("Internal Server Error");
77
77
  };
78
78
 
79
- // P3: Pre-allocated event object to avoid { req, res } allocation per request
80
- // Safe because handleNodeRequest completes before the next request reuses this object
81
- protected readonly nodeRequestEvent = {
82
- req: null as unknown as IncomingMessage,
83
- res: null as unknown as ServerResponse,
84
- };
85
-
86
79
  public readonly server = this.createHttpServer((req, res) => {
87
- // Reuse pre-allocated event object instead of creating { req, res } per request
88
- const ev = this.nodeRequestEvent;
89
- ev.req = req;
90
- ev.res = res;
91
- // Note: handleNodeRequest returns a promise that resolves after response is sent
92
- this.handleNodeRequest(ev).catch((err) =>
93
- this.handleRequestError(res, err),
94
- );
80
+ const promise = this.handleNodeRequest({ req, res });
81
+ promise.catch((err) => this.handleRequestError(res, err));
95
82
  });
96
83
 
97
84
  public readonly start = $hook({
@@ -220,13 +220,15 @@ export class ServerProvider {
220
220
 
221
221
  // empty body - just send status & headers
222
222
  if (!response.body) {
223
- res.writeHead(response.status, response.headers).end();
223
+ res.writeHead(response.status, response.headers);
224
+ res.end();
224
225
  return;
225
226
  }
226
227
 
227
228
  // if response.body is string or buffer
228
229
  if (typeof response.body === "string" || Buffer.isBuffer(response.body)) {
229
- res.writeHead(response.status, response.headers).end(response.body);
230
+ res.writeHead(response.status, response.headers);
231
+ res.end(response.body);
230
232
  return;
231
233
  }
232
234
 
@@ -45,23 +45,6 @@ export class ServerRouterProvider extends RouterProvider<ServerRouteMatcher> {
45
45
  >;
46
46
  protected compiledOnError?: CompiledEventExecutor<Hooks["server:onError"]>;
47
47
 
48
- // Reusable context.run options object - mutated per request
49
- // Includes slots for request data to avoid closure allocation in context.run
50
- protected readonly contextRunOptions = {
51
- context: "",
52
- // Request data slots - populated before context.run, read by processRequestBound
53
- _request: null as unknown as ServerRequest,
54
- _route: null as unknown as ServerRoute,
55
- _responseKind: "any" as ResponseKind,
56
- };
57
-
58
- // Pre-bound method reference - created once at instantiation, reused for all requests
59
- // Reads arguments from contextRunOptions to avoid closure allocation per request
60
- protected readonly processRequestBound = (): Promise<any> => {
61
- const opts = this.contextRunOptions;
62
- return this.processRequest(opts._request, opts._route, opts._responseKind);
63
- };
64
-
65
48
  // Cache query schema keys to avoid property enumeration on every request
66
49
  // WeakMap allows GC of schemas that are no longer referenced
67
50
  protected readonly queryKeysCache = new WeakMap<object, string[]>();
@@ -137,16 +120,11 @@ export class ServerRouterProvider extends RouterProvider<ServerRouteMatcher> {
137
120
  const request =
138
121
  this.serverRequestParser.createServerRequest(rawRequest);
139
122
 
140
- // Populate pre-allocated options with request data
141
- // This avoids closure allocation - processRequestBound reads from these slots
142
- const opts = this.contextRunOptions;
143
- opts.context = this.getContextId(rawRequest.headers);
144
- opts._request = request;
145
- opts._route = route;
146
- opts._responseKind = responseKind;
147
-
148
- // Use pre-bound method reference instead of creating closure per request
149
- return this.alepha.context.run(this.processRequestBound, opts);
123
+ // Create context options per request to avoid race conditions with concurrent requests
124
+ return this.alepha.context.run(
125
+ () => this.processRequest(request, route, responseKind),
126
+ { context: this.getContextId(rawRequest.headers) },
127
+ );
150
128
  },
151
129
  });
152
130
  }
@@ -25,9 +25,9 @@ declare module "alepha/server" {
25
25
  // ---------------------------------------------------------------------------------------------------------------------
26
26
 
27
27
  /**
28
- * | type | quality | stability |
29
- * |------|---------|-----------|
30
- * | backend | standard | stable |
28
+ * | Stability | Since | Runtime |
29
+ * |-----------|-------|---------|
30
+ * | 3 - stable | 0.3.0 | node, bun, workerd|
31
31
  *
32
32
  * Cross-Origin Resource Sharing configuration.
33
33
  *
@@ -10,9 +10,9 @@ export * from "./schemas/healthSchema.ts";
10
10
  // ---------------------------------------------------------------------------------------------------------------------
11
11
 
12
12
  /**
13
- * | type | quality | stability |
14
- * |------|---------|-----------|
15
- * | devops | standard | stable |
13
+ * | Stability | Since | Runtime |
14
+ * |-----------|-------|---------|
15
+ * | 3 - stable | 0.7.0 | node, bun, workerd|
16
16
  *
17
17
  * Application health monitoring endpoints.
18
18
  *
@@ -9,9 +9,9 @@ export * from "./providers/ServerHelmetProvider.ts";
9
9
  // ---------------------------------------------------------------------------------------------------------------------
10
10
 
11
11
  /**
12
- * | type | quality | stability |
13
- * |------|---------|-----------|
14
- * | backend | standard | stable |
12
+ * | Stability | Since | Runtime |
13
+ * |-----------|-------|---------|
14
+ * | 3 - stable | 0.15.0 | node, bun, workerd|
15
15
  *
16
16
  * HTTP security headers.
17
17
  *
@@ -35,9 +35,9 @@ declare module "alepha" {
35
35
  // ---------------------------------------------------------------------------------------------------------------------
36
36
 
37
37
  /**
38
- * | type | quality | stability |
39
- * |------|---------|-----------|
40
- * | backend | standard | stable |
38
+ * | Stability | Since | Runtime |
39
+ * |-----------|-------|---------|
40
+ * | 3 - stable | 0.6.0 | node, bun, workerd, expo |
41
41
  *
42
42
  * Type-safe API client with request deduplication.
43
43
  *