alepha 0.14.3 → 0.15.0

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 (317) hide show
  1. package/README.md +2 -5
  2. package/dist/api/audits/index.d.ts +620 -811
  3. package/dist/api/audits/index.d.ts.map +1 -1
  4. package/dist/api/files/index.d.ts +185 -377
  5. package/dist/api/files/index.d.ts.map +1 -1
  6. package/dist/api/files/index.js +0 -1
  7. package/dist/api/files/index.js.map +1 -1
  8. package/dist/api/jobs/index.d.ts +245 -435
  9. package/dist/api/jobs/index.d.ts.map +1 -1
  10. package/dist/api/notifications/index.d.ts +238 -429
  11. package/dist/api/notifications/index.d.ts.map +1 -1
  12. package/dist/api/parameters/index.d.ts +236 -427
  13. package/dist/api/parameters/index.d.ts.map +1 -1
  14. package/dist/api/users/index.browser.js +1 -2
  15. package/dist/api/users/index.browser.js.map +1 -1
  16. package/dist/api/users/index.d.ts +1010 -1196
  17. package/dist/api/users/index.d.ts.map +1 -1
  18. package/dist/api/users/index.js +178 -151
  19. package/dist/api/users/index.js.map +1 -1
  20. package/dist/api/verifications/index.d.ts +17 -17
  21. package/dist/api/verifications/index.d.ts.map +1 -1
  22. package/dist/batch/index.d.ts +122 -122
  23. package/dist/batch/index.d.ts.map +1 -1
  24. package/dist/batch/index.js +1 -2
  25. package/dist/batch/index.js.map +1 -1
  26. package/dist/bucket/index.d.ts +163 -163
  27. package/dist/bucket/index.d.ts.map +1 -1
  28. package/dist/cache/core/index.d.ts +46 -46
  29. package/dist/cache/core/index.d.ts.map +1 -1
  30. package/dist/cache/redis/index.d.ts.map +1 -1
  31. package/dist/cli/index.d.ts +384 -285
  32. package/dist/cli/index.d.ts.map +1 -1
  33. package/dist/cli/index.js +1113 -623
  34. package/dist/cli/index.js.map +1 -1
  35. package/dist/command/index.d.ts +299 -300
  36. package/dist/command/index.d.ts.map +1 -1
  37. package/dist/command/index.js +13 -9
  38. package/dist/command/index.js.map +1 -1
  39. package/dist/core/index.browser.js +445 -103
  40. package/dist/core/index.browser.js.map +1 -1
  41. package/dist/core/index.d.ts +733 -625
  42. package/dist/core/index.d.ts.map +1 -1
  43. package/dist/core/index.js +446 -103
  44. package/dist/core/index.js.map +1 -1
  45. package/dist/core/index.native.js +445 -103
  46. package/dist/core/index.native.js.map +1 -1
  47. package/dist/datetime/index.d.ts +44 -44
  48. package/dist/datetime/index.d.ts.map +1 -1
  49. package/dist/datetime/index.js +4 -4
  50. package/dist/datetime/index.js.map +1 -1
  51. package/dist/email/index.d.ts +97 -50
  52. package/dist/email/index.d.ts.map +1 -1
  53. package/dist/email/index.js +129 -33
  54. package/dist/email/index.js.map +1 -1
  55. package/dist/fake/index.d.ts +7981 -14
  56. package/dist/fake/index.d.ts.map +1 -1
  57. package/dist/file/index.d.ts +523 -390
  58. package/dist/file/index.d.ts.map +1 -1
  59. package/dist/file/index.js +253 -1
  60. package/dist/file/index.js.map +1 -1
  61. package/dist/lock/core/index.d.ts +208 -208
  62. package/dist/lock/core/index.d.ts.map +1 -1
  63. package/dist/lock/redis/index.d.ts.map +1 -1
  64. package/dist/logger/index.d.ts +25 -26
  65. package/dist/logger/index.d.ts.map +1 -1
  66. package/dist/logger/index.js +12 -2
  67. package/dist/logger/index.js.map +1 -1
  68. package/dist/mcp/index.d.ts +197 -197
  69. package/dist/mcp/index.d.ts.map +1 -1
  70. package/dist/mcp/index.js +1 -1
  71. package/dist/mcp/index.js.map +1 -1
  72. package/dist/orm/chunk-DtkW-qnP.js +38 -0
  73. package/dist/orm/index.browser.js.map +1 -1
  74. package/dist/orm/index.bun.js +2814 -0
  75. package/dist/orm/index.bun.js.map +1 -0
  76. package/dist/orm/index.d.ts +1228 -1216
  77. package/dist/orm/index.d.ts.map +1 -1
  78. package/dist/orm/index.js +2041 -1967
  79. package/dist/orm/index.js.map +1 -1
  80. package/dist/queue/core/index.d.ts +248 -248
  81. package/dist/queue/core/index.d.ts.map +1 -1
  82. package/dist/queue/redis/index.d.ts.map +1 -1
  83. package/dist/redis/index.bun.js +285 -0
  84. package/dist/redis/index.bun.js.map +1 -0
  85. package/dist/redis/index.d.ts +118 -136
  86. package/dist/redis/index.d.ts.map +1 -1
  87. package/dist/redis/index.js +18 -38
  88. package/dist/redis/index.js.map +1 -1
  89. package/dist/retry/index.d.ts +69 -69
  90. package/dist/retry/index.d.ts.map +1 -1
  91. package/dist/router/index.d.ts +6 -6
  92. package/dist/router/index.d.ts.map +1 -1
  93. package/dist/scheduler/index.d.ts +25 -25
  94. package/dist/scheduler/index.d.ts.map +1 -1
  95. package/dist/security/index.browser.js +5 -1
  96. package/dist/security/index.browser.js.map +1 -1
  97. package/dist/security/index.d.ts +417 -254
  98. package/dist/security/index.d.ts.map +1 -1
  99. package/dist/security/index.js +386 -86
  100. package/dist/security/index.js.map +1 -1
  101. package/dist/server/auth/index.d.ts +110 -110
  102. package/dist/server/auth/index.d.ts.map +1 -1
  103. package/dist/server/auth/index.js +20 -20
  104. package/dist/server/auth/index.js.map +1 -1
  105. package/dist/server/cache/index.d.ts +62 -47
  106. package/dist/server/cache/index.d.ts.map +1 -1
  107. package/dist/server/cache/index.js +56 -3
  108. package/dist/server/cache/index.js.map +1 -1
  109. package/dist/server/compress/index.d.ts +6 -0
  110. package/dist/server/compress/index.d.ts.map +1 -1
  111. package/dist/server/compress/index.js +36 -1
  112. package/dist/server/compress/index.js.map +1 -1
  113. package/dist/server/cookies/index.d.ts +6 -6
  114. package/dist/server/cookies/index.d.ts.map +1 -1
  115. package/dist/server/cookies/index.js +3 -3
  116. package/dist/server/cookies/index.js.map +1 -1
  117. package/dist/server/core/index.browser.js +2 -2
  118. package/dist/server/core/index.browser.js.map +1 -1
  119. package/dist/server/core/index.d.ts +242 -150
  120. package/dist/server/core/index.d.ts.map +1 -1
  121. package/dist/server/core/index.js +294 -125
  122. package/dist/server/core/index.js.map +1 -1
  123. package/dist/server/cors/index.d.ts +11 -12
  124. package/dist/server/cors/index.d.ts.map +1 -1
  125. package/dist/server/health/index.d.ts +0 -1
  126. package/dist/server/health/index.d.ts.map +1 -1
  127. package/dist/server/helmet/index.d.ts +2 -2
  128. package/dist/server/helmet/index.d.ts.map +1 -1
  129. package/dist/server/links/index.browser.js.map +1 -1
  130. package/dist/server/links/index.d.ts +123 -124
  131. package/dist/server/links/index.d.ts.map +1 -1
  132. package/dist/server/links/index.js +1 -2
  133. package/dist/server/links/index.js.map +1 -1
  134. package/dist/server/metrics/index.d.ts.map +1 -1
  135. package/dist/server/multipart/index.d.ts +6 -6
  136. package/dist/server/multipart/index.d.ts.map +1 -1
  137. package/dist/server/proxy/index.d.ts +102 -103
  138. package/dist/server/proxy/index.d.ts.map +1 -1
  139. package/dist/server/rate-limit/index.d.ts +16 -16
  140. package/dist/server/rate-limit/index.d.ts.map +1 -1
  141. package/dist/server/static/index.d.ts +44 -44
  142. package/dist/server/static/index.d.ts.map +1 -1
  143. package/dist/server/static/index.js +4 -0
  144. package/dist/server/static/index.js.map +1 -1
  145. package/dist/server/swagger/index.d.ts +48 -49
  146. package/dist/server/swagger/index.d.ts.map +1 -1
  147. package/dist/server/swagger/index.js +3 -5
  148. package/dist/server/swagger/index.js.map +1 -1
  149. package/dist/sms/index.d.ts +13 -11
  150. package/dist/sms/index.d.ts.map +1 -1
  151. package/dist/sms/index.js +7 -7
  152. package/dist/sms/index.js.map +1 -1
  153. package/dist/thread/index.d.ts +71 -72
  154. package/dist/thread/index.d.ts.map +1 -1
  155. package/dist/topic/core/index.d.ts +318 -318
  156. package/dist/topic/core/index.d.ts.map +1 -1
  157. package/dist/topic/redis/index.d.ts +6 -6
  158. package/dist/topic/redis/index.d.ts.map +1 -1
  159. package/dist/vite/index.d.ts +5805 -249
  160. package/dist/vite/index.d.ts.map +1 -1
  161. package/dist/vite/index.js +599 -513
  162. package/dist/vite/index.js.map +1 -1
  163. package/dist/websocket/index.browser.js +6 -6
  164. package/dist/websocket/index.browser.js.map +1 -1
  165. package/dist/websocket/index.d.ts +247 -247
  166. package/dist/websocket/index.d.ts.map +1 -1
  167. package/dist/websocket/index.js +6 -6
  168. package/dist/websocket/index.js.map +1 -1
  169. package/package.json +9 -14
  170. package/src/api/files/controllers/AdminFileStatsController.ts +0 -1
  171. package/src/api/users/atoms/realmAuthSettingsAtom.ts +5 -0
  172. package/src/api/users/controllers/{UserRealmController.ts → RealmController.ts} +11 -11
  173. package/src/api/users/entities/users.ts +1 -1
  174. package/src/api/users/index.ts +8 -8
  175. package/src/api/users/primitives/{$userRealm.ts → $realm.ts} +17 -19
  176. package/src/api/users/providers/{UserRealmProvider.ts → RealmProvider.ts} +26 -30
  177. package/src/api/users/schemas/{userRealmConfigSchema.ts → realmConfigSchema.ts} +2 -2
  178. package/src/api/users/services/CredentialService.ts +7 -7
  179. package/src/api/users/services/IdentityService.ts +4 -4
  180. package/src/api/users/services/RegistrationService.spec.ts +25 -27
  181. package/src/api/users/services/RegistrationService.ts +38 -27
  182. package/src/api/users/services/SessionCrudService.ts +3 -3
  183. package/src/api/users/services/SessionService.spec.ts +3 -3
  184. package/src/api/users/services/SessionService.ts +28 -9
  185. package/src/api/users/services/UserService.ts +7 -7
  186. package/src/batch/providers/BatchProvider.ts +1 -2
  187. package/src/cli/apps/AlephaCli.ts +0 -2
  188. package/src/cli/apps/AlephaPackageBuilderCli.ts +38 -19
  189. package/src/cli/assets/apiHelloControllerTs.ts +18 -0
  190. package/src/cli/assets/apiIndexTs.ts +16 -0
  191. package/src/cli/assets/claudeMd.ts +303 -0
  192. package/src/cli/assets/mainBrowserTs.ts +2 -2
  193. package/src/cli/assets/mainServerTs.ts +24 -0
  194. package/src/cli/assets/webAppRouterTs.ts +15 -0
  195. package/src/cli/assets/webHelloComponentTsx.ts +16 -0
  196. package/src/cli/assets/webIndexTs.ts +16 -0
  197. package/src/cli/atoms/buildOptions.ts +88 -0
  198. package/src/cli/commands/build.ts +70 -87
  199. package/src/cli/commands/db.ts +21 -22
  200. package/src/cli/commands/deploy.ts +17 -5
  201. package/src/cli/commands/dev.ts +22 -14
  202. package/src/cli/commands/format.ts +8 -2
  203. package/src/cli/commands/gen/env.ts +53 -0
  204. package/src/cli/commands/gen/openapi.ts +1 -1
  205. package/src/cli/commands/gen/resource.ts +15 -0
  206. package/src/cli/commands/gen.ts +7 -1
  207. package/src/cli/commands/init.ts +74 -30
  208. package/src/cli/commands/lint.ts +8 -2
  209. package/src/cli/commands/test.ts +8 -3
  210. package/src/cli/commands/typecheck.ts +5 -1
  211. package/src/cli/commands/verify.ts +5 -3
  212. package/src/cli/defineConfig.ts +49 -7
  213. package/src/cli/index.ts +0 -1
  214. package/src/cli/services/AlephaCliUtils.ts +39 -589
  215. package/src/cli/services/PackageManagerUtils.ts +301 -0
  216. package/src/cli/services/ProjectScaffolder.ts +306 -0
  217. package/src/command/helpers/Runner.spec.ts +2 -2
  218. package/src/command/helpers/Runner.ts +16 -4
  219. package/src/command/primitives/$command.ts +0 -6
  220. package/src/command/providers/CliProvider.ts +1 -3
  221. package/src/core/Alepha.ts +42 -0
  222. package/src/core/__tests__/Alepha-graph.spec.ts +4 -0
  223. package/src/core/index.shared.ts +1 -0
  224. package/src/core/index.ts +2 -0
  225. package/src/core/primitives/$hook.ts +6 -2
  226. package/src/core/primitives/$module.spec.ts +4 -0
  227. package/src/core/providers/AlsProvider.ts +1 -1
  228. package/src/core/providers/CodecManager.spec.ts +12 -6
  229. package/src/core/providers/CodecManager.ts +26 -6
  230. package/src/core/providers/EventManager.ts +169 -13
  231. package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +621 -0
  232. package/src/core/providers/KeylessJsonSchemaCodec.ts +407 -0
  233. package/src/core/providers/StateManager.spec.ts +27 -16
  234. package/src/email/providers/LocalEmailProvider.spec.ts +111 -87
  235. package/src/email/providers/LocalEmailProvider.ts +52 -15
  236. package/src/email/providers/NodemailerEmailProvider.ts +167 -56
  237. package/src/file/errors/FileError.ts +7 -0
  238. package/src/file/index.ts +9 -1
  239. package/src/file/providers/MemoryFileSystemProvider.ts +393 -0
  240. package/src/logger/index.ts +15 -3
  241. package/src/mcp/transports/StdioMcpTransport.ts +1 -1
  242. package/src/orm/index.browser.ts +1 -19
  243. package/src/orm/index.bun.ts +77 -0
  244. package/src/orm/index.shared-server.ts +22 -0
  245. package/src/orm/index.shared.ts +15 -0
  246. package/src/orm/index.ts +13 -39
  247. package/src/orm/providers/drivers/BunPostgresProvider.ts +3 -5
  248. package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -1
  249. package/src/orm/providers/drivers/CloudflareD1Provider.ts +4 -0
  250. package/src/orm/providers/drivers/DatabaseProvider.ts +4 -0
  251. package/src/orm/providers/drivers/PglitePostgresProvider.ts +4 -0
  252. package/src/orm/services/Repository.ts +8 -0
  253. package/src/queue/core/providers/WorkerProvider.spec.ts +48 -32
  254. package/src/redis/index.bun.ts +35 -0
  255. package/src/redis/providers/BunRedisProvider.ts +12 -43
  256. package/src/redis/providers/BunRedisSubscriberProvider.ts +2 -3
  257. package/src/redis/providers/NodeRedisProvider.ts +16 -34
  258. package/src/{server/security → security}/__tests__/BasicAuth.spec.ts +11 -11
  259. package/src/{server/security → security}/__tests__/ServerSecurityProvider-realm.spec.ts +21 -16
  260. package/src/{server/security/providers → security/__tests__}/ServerSecurityProvider.spec.ts +5 -5
  261. package/src/security/index.browser.ts +5 -0
  262. package/src/security/index.ts +90 -7
  263. package/src/security/primitives/{$realm.spec.ts → $issuer.spec.ts} +11 -11
  264. package/src/security/primitives/{$realm.ts → $issuer.ts} +20 -17
  265. package/src/security/primitives/$role.ts +5 -5
  266. package/src/security/primitives/$serviceAccount.spec.ts +5 -5
  267. package/src/security/primitives/$serviceAccount.ts +3 -3
  268. package/src/{server/security → security}/providers/ServerSecurityProvider.ts +5 -7
  269. package/src/server/auth/primitives/$auth.ts +10 -10
  270. package/src/server/auth/primitives/$authCredentials.ts +3 -3
  271. package/src/server/auth/primitives/$authGithub.ts +3 -3
  272. package/src/server/auth/primitives/$authGoogle.ts +3 -3
  273. package/src/server/auth/providers/ServerAuthProvider.ts +13 -13
  274. package/src/server/cache/providers/ServerCacheProvider.spec.ts +183 -0
  275. package/src/server/cache/providers/ServerCacheProvider.ts +95 -10
  276. package/src/server/compress/providers/ServerCompressProvider.ts +61 -2
  277. package/src/server/cookies/providers/ServerCookiesProvider.ts +3 -3
  278. package/src/server/core/helpers/ServerReply.ts +2 -2
  279. package/src/server/core/providers/NodeHttpServerProvider.ts +25 -6
  280. package/src/server/core/providers/ServerBodyParserProvider.ts +19 -23
  281. package/src/server/core/providers/ServerLoggerProvider.ts +23 -19
  282. package/src/server/core/providers/ServerProvider.ts +155 -22
  283. package/src/server/core/providers/ServerRouterProvider.ts +259 -115
  284. package/src/server/core/providers/ServerTimingProvider.ts +2 -2
  285. package/src/server/links/index.ts +1 -1
  286. package/src/server/links/providers/LinkProvider.ts +1 -1
  287. package/src/server/static/providers/ServerStaticProvider.ts +10 -0
  288. package/src/server/swagger/index.ts +1 -1
  289. package/src/server/swagger/providers/ServerSwaggerProvider.ts +5 -8
  290. package/src/sms/providers/LocalSmsProvider.spec.ts +153 -111
  291. package/src/sms/providers/LocalSmsProvider.ts +8 -7
  292. package/src/vite/helpers/boot.ts +28 -17
  293. package/src/vite/helpers/importViteReact.ts +13 -0
  294. package/src/vite/index.ts +1 -21
  295. package/src/vite/plugins/viteAlephaDev.ts +16 -1
  296. package/src/vite/plugins/viteAlephaSsrPreload.ts +222 -0
  297. package/src/vite/tasks/buildClient.ts +11 -0
  298. package/src/vite/tasks/buildServer.ts +59 -4
  299. package/src/vite/tasks/devServer.ts +71 -0
  300. package/src/vite/tasks/generateCloudflare.ts +7 -0
  301. package/src/vite/tasks/index.ts +2 -1
  302. package/dist/server/security/index.browser.js +0 -13
  303. package/dist/server/security/index.browser.js.map +0 -1
  304. package/dist/server/security/index.d.ts +0 -173
  305. package/dist/server/security/index.d.ts.map +0 -1
  306. package/dist/server/security/index.js +0 -311
  307. package/dist/server/security/index.js.map +0 -1
  308. package/src/cli/assets/appRouterTs.ts +0 -9
  309. package/src/cli/assets/mainTs.ts +0 -13
  310. package/src/cli/assets/viteConfigTs.ts +0 -14
  311. package/src/cli/commands/run.ts +0 -24
  312. package/src/server/security/index.browser.ts +0 -10
  313. package/src/server/security/index.ts +0 -94
  314. package/src/vite/plugins/viteAlepha.ts +0 -37
  315. package/src/vite/plugins/viteAlephaBuild.ts +0 -281
  316. /package/src/{server/security → security}/primitives/$basicAuth.ts +0 -0
  317. /package/src/{server/security → security}/providers/ServerBasicAuthProvider.ts +0 -0
@@ -165,6 +165,13 @@ export class Alepha {
165
165
  ...state.env,
166
166
  ...process.env,
167
167
  };
168
+
169
+ // remove empty env variables
170
+ for (const key in state.env) {
171
+ if (state.env[key] === "") {
172
+ delete (state.env as any)[key];
173
+ }
174
+ }
168
175
  }
169
176
 
170
177
  // force production mode when building with vite
@@ -908,6 +915,27 @@ export class Alepha {
908
915
  return graph;
909
916
  }
910
917
 
918
+ public dump(): AlephaDump {
919
+ const env: Record<string, AlephaDumpEnvVariable> = {};
920
+ for (const [schema] of this.cacheEnv.entries()) {
921
+ const ref = schema as any;
922
+ for (const [key, value] of Object.entries(ref.properties)) {
923
+ const prop = value as any;
924
+ env[key] = {
925
+ description: prop.description,
926
+ default: prop.default,
927
+ required: ref.required?.includes(key) ?? undefined,
928
+ enum: prop.enum ? ([...prop.enum] as Array<string>) : undefined,
929
+ };
930
+ }
931
+ }
932
+
933
+ return {
934
+ env,
935
+ providers: this.graph(),
936
+ };
937
+ }
938
+
911
939
  public services<T extends object>(base: Service<T>): Array<T> {
912
940
  const list: Array<T> = [];
913
941
  for (const [key, value] of this.registry.entries()) {
@@ -1012,6 +1040,20 @@ export interface Hook<T extends keyof Hooks = any> {
1012
1040
 
1013
1041
  // ---------------------------------------------------------------------------------------------------------------------
1014
1042
 
1043
+ export interface AlephaDump {
1044
+ env: Record<string, AlephaDumpEnvVariable>;
1045
+ providers: Record<string, { from: string[]; as?: string[]; module?: string }>;
1046
+ }
1047
+
1048
+ export interface AlephaDumpEnvVariable {
1049
+ description: string;
1050
+ default?: string;
1051
+ required?: boolean;
1052
+ enum?: Array<string>;
1053
+ }
1054
+
1055
+ // ---------------------------------------------------------------------------------------------------------------------
1056
+
1015
1057
  /**
1016
1058
  * This is how we store services in the Alepha container.
1017
1059
  */
@@ -80,6 +80,10 @@ describe("Alepha#graph", () => {
80
80
  from: ["StateManager", "CodecManager"],
81
81
  module: "alepha.core",
82
82
  },
83
+ KeylessJsonSchemaCodec: {
84
+ from: ["CodecManager"],
85
+ module: "alepha.core",
86
+ },
83
87
  CodecManager: {
84
88
  from: ["Alepha"],
85
89
  module: "alepha.core",
@@ -28,6 +28,7 @@ export * from "./providers/AlsProvider.ts";
28
28
  export * from "./providers/CodecManager.ts";
29
29
  export * from "./providers/EventManager.ts";
30
30
  export * from "./providers/JsonSchemaCodec.ts";
31
+ export * from "./providers/KeylessJsonSchemaCodec.ts";
31
32
  export * from "./providers/SchemaCodec.ts";
32
33
  export * from "./providers/StateManager.ts";
33
34
  export * from "./providers/TypeProvider.ts";
package/src/core/index.ts CHANGED
@@ -11,6 +11,7 @@ import { CodecManager } from "./providers/CodecManager.ts";
11
11
  import { EventManager } from "./providers/EventManager.ts";
12
12
  import { Json } from "./providers/Json.ts";
13
13
  import { JsonSchemaCodec } from "./providers/JsonSchemaCodec.ts";
14
+ import { KeylessJsonSchemaCodec } from "./providers/KeylessJsonSchemaCodec.ts";
14
15
  import { SchemaCodec } from "./providers/SchemaCodec.ts";
15
16
  import { SchemaValidator } from "./providers/SchemaValidator.ts";
16
17
  import { StateManager } from "./providers/StateManager.ts";
@@ -30,6 +31,7 @@ export const AlephaCore = $module({
30
31
  AlsProvider,
31
32
  Json,
32
33
  JsonSchemaCodec,
34
+ KeylessJsonSchemaCodec,
33
35
  SchemaCodec,
34
36
  SchemaValidator,
35
37
  ],
@@ -82,12 +82,16 @@ export class HookPrimitive<T extends keyof Hooks> extends Primitive<
82
82
  public called = 0;
83
83
 
84
84
  protected onInit() {
85
+ // Store reference to handler to avoid property access in hot path
86
+ const handler = this.options.handler;
87
+
85
88
  this.alepha.events.on(this.options.on, {
86
89
  caller: this.config.service,
87
90
  priority: this.options.priority,
88
- callback: async (args: any) => {
91
+ // Return handler result directly - EventManager checks if it's a promise
92
+ callback: (args: any) => {
89
93
  this.called += 1;
90
- await this.options.handler(args);
94
+ return handler(args);
91
95
  },
92
96
  });
93
97
  }
@@ -51,6 +51,10 @@ describe("$module", () => {
51
51
  from: ["StateManager", "CodecManager"],
52
52
  module: "alepha.core",
53
53
  },
54
+ KeylessJsonSchemaCodec: {
55
+ from: ["CodecManager"],
56
+ module: "alepha.core",
57
+ },
54
58
  CodecManager: {
55
59
  from: ["Alepha"],
56
60
  module: "alepha.core",
@@ -25,7 +25,7 @@ export class AlsProvider {
25
25
  data.registry ??= new Map();
26
26
  data.context ??= this.createContextId();
27
27
 
28
- return this.als.run(data, callback);
28
+ return this.als.run({ ...data }, callback);
29
29
  }
30
30
 
31
31
  public exists(): boolean {
@@ -44,7 +44,10 @@ describe("CodecManager", () => {
44
44
  const codecManager = alepha.codec;
45
45
  const customCodec = new CustomCodec();
46
46
 
47
- codecManager.register("custom", customCodec);
47
+ codecManager.register({
48
+ name: "custom",
49
+ codec: customCodec,
50
+ });
48
51
 
49
52
  const retrieved = codecManager.getCodec("custom");
50
53
  expect(retrieved).toBe(customCodec);
@@ -74,10 +77,13 @@ describe("CodecManager", () => {
74
77
 
75
78
  const alepha = Alepha.create();
76
79
  const codecManager = alepha.codec;
77
- codecManager.register("mock", new MockCodec());
80
+ codecManager.register({
81
+ name: "mock",
82
+ codec: new MockCodec(),
83
+ });
78
84
 
79
85
  expect(() => codecManager.getCodec("missing")).toThrow(
80
- 'Codec "missing" not found. Available codecs: json, mock',
86
+ 'Codec "missing" not found. Available codecs: json, keyless, mock',
81
87
  );
82
88
  });
83
89
  });
@@ -298,7 +304,7 @@ describe("CodecManager", () => {
298
304
 
299
305
  const alepha = Alepha.create();
300
306
  const codecManager = alepha.codec;
301
- codecManager.register("custom", new CustomCodec());
307
+ codecManager.register({ name: "custom", codec: new CustomCodec() });
302
308
 
303
309
  const schema = t.object({ value: t.text() });
304
310
  const result = codecManager.encode(
@@ -513,7 +519,7 @@ describe("CodecManager", () => {
513
519
 
514
520
  const alepha = Alepha.create();
515
521
  const codecManager = alepha.codec;
516
- codecManager.register("custom", new CustomCodec());
522
+ codecManager.register({ name: "custom", codec: new CustomCodec() });
517
523
 
518
524
  const schema = t.object({ value: t.text() });
519
525
  const result = codecManager.decode(
@@ -631,7 +637,7 @@ describe("CodecManager", () => {
631
637
  codec = $inject(CodecManager);
632
638
 
633
639
  constructor() {
634
- this.codec.register("custom", new CustomCodec());
640
+ this.codec.register({ name: "custom", codec: new CustomCodec() });
635
641
  }
636
642
  }
637
643
 
@@ -2,6 +2,7 @@ import type { StaticEncode, TSchema } from "typebox";
2
2
  import { AlephaError } from "../errors/AlephaError.ts";
3
3
  import { $inject } from "../primitives/$inject.ts";
4
4
  import { JsonSchemaCodec } from "./JsonSchemaCodec.ts";
5
+ import { KeylessJsonSchemaCodec } from "./KeylessJsonSchemaCodec.ts";
5
6
  import type { SchemaCodec } from "./SchemaCodec.ts";
6
7
  import { SchemaValidator, type ValidateOptions } from "./SchemaValidator.ts";
7
8
  import type { Static } from "./TypeProvider.ts";
@@ -61,23 +62,34 @@ export interface DecodeOptions {
61
62
  export class CodecManager {
62
63
  protected readonly codecs: Map<string, SchemaCodec> = new Map();
63
64
  protected readonly jsonCodec = $inject(JsonSchemaCodec);
65
+ protected readonly keylessCodec = $inject(KeylessJsonSchemaCodec);
64
66
  protected readonly schemaValidator = $inject(SchemaValidator);
65
67
 
66
68
  public default = "json";
67
69
 
68
70
  constructor() {
69
71
  // Register default JSON codec
70
- this.register(this.default, this.jsonCodec);
72
+ this.register({
73
+ name: "json",
74
+ codec: this.jsonCodec,
75
+ default: true,
76
+ });
77
+
78
+ // Register keyless JSON codec (smaller, faster decoding)
79
+ this.register({
80
+ name: "keyless",
81
+ codec: this.keylessCodec,
82
+ });
71
83
  }
72
84
 
73
85
  /**
74
86
  * Register a new codec format.
75
- *
76
- * @param name - The name of the codec (e.g., 'json', 'protobuf')
77
- * @param codec - The codec implementation
78
87
  */
79
- public register(name: string, codec: SchemaCodec): void {
80
- this.codecs.set(name, codec);
88
+ public register(opts: CodecRegisterOptions): void {
89
+ this.codecs.set(opts.name, opts.codec);
90
+ if (opts.default) {
91
+ this.default = opts.name;
92
+ }
81
93
  }
82
94
 
83
95
  /**
@@ -164,3 +176,11 @@ export class CodecManager {
164
176
  return this.schemaValidator.validate(schema, value, options);
165
177
  }
166
178
  }
179
+
180
+ // ---------------------------------------------------------------------------------------------------------------------
181
+
182
+ export interface CodecRegisterOptions {
183
+ name: string;
184
+ codec: SchemaCodec;
185
+ default?: boolean;
186
+ }
@@ -3,6 +3,23 @@ import { AlephaError } from "../errors/AlephaError.ts";
3
3
  import type { Async } from "../interfaces/Async.ts";
4
4
  import type { LoggerInterface } from "../interfaces/LoggerInterface.ts";
5
5
 
6
+ /**
7
+ * Compiled event executor - optimized for hot paths.
8
+ * Returns void for sync-only chains, Promise<void> for chains with async hooks.
9
+ */
10
+ export type CompiledEventExecutor<T> = (payload: T) => void | Promise<void>;
11
+
12
+ /**
13
+ * Options for compiled event executors.
14
+ */
15
+ export interface CompileOptions {
16
+ /**
17
+ * If true, errors will be caught and logged instead of throwing.
18
+ * @default false
19
+ */
20
+ catch?: boolean;
21
+ }
22
+
6
23
  export class EventManager {
7
24
  public logFn?: () => LoggerInterface | undefined;
8
25
 
@@ -59,8 +76,114 @@ export class EventManager {
59
76
  };
60
77
  }
61
78
 
79
+ /**
80
+ * Compiles an event into an optimized executor function.
81
+ *
82
+ * Call this after all hooks are registered (e.g., after Alepha.start()).
83
+ * The returned function checks each hook's return value and awaits promises.
84
+ * Returns undefined if all hooks are sync, or a Promise if any hook returns one.
85
+ *
86
+ * @example
87
+ * ```ts
88
+ * // At startup (after hooks are registered)
89
+ * const onRequest = alepha.events.compile("server:onRequest", { catch: true });
90
+ *
91
+ * // In hot path - only await if promise returned
92
+ * const result = onRequest({ request, route });
93
+ * if (result) await result;
94
+ * ```
95
+ */
96
+ public compile<T extends keyof Hooks>(
97
+ event: T,
98
+ options: CompileOptions = {},
99
+ ): CompiledEventExecutor<Hooks[T]> {
100
+ const hooks = this.events[event];
101
+
102
+ // No hooks - return no-op
103
+ if (!hooks || hooks.length === 0) {
104
+ return () => {};
105
+ }
106
+
107
+ const catchErrors = options.catch ?? false;
108
+ const log = this.log;
109
+
110
+ // Helper to run remaining hooks sequentially after first async hook
111
+ const runRemainingAsync = async (
112
+ startIndex: number,
113
+ payload: Hooks[T],
114
+ ): Promise<void> => {
115
+ for (let i = startIndex; i < hooks.length; i++) {
116
+ const hook = hooks[i];
117
+ try {
118
+ const result = hook.callback(payload);
119
+ if (result && typeof result === "object" && "then" in result) {
120
+ if (catchErrors) {
121
+ await (result as Promise<void>).catch((error) => {
122
+ log?.error(
123
+ `${String(event)}(${hook.caller?.name ?? "unknown"}) ERROR`,
124
+ error,
125
+ );
126
+ });
127
+ } else {
128
+ await result;
129
+ }
130
+ }
131
+ } catch (error) {
132
+ if (catchErrors) {
133
+ log?.error(
134
+ `${String(event)}(${hook.caller?.name ?? "unknown"}) ERROR`,
135
+ error,
136
+ );
137
+ } else {
138
+ throw error;
139
+ }
140
+ }
141
+ }
142
+ };
143
+
144
+ // Return executor that runs sync hooks synchronously, then switches to async
145
+ // when encountering the first async hook. Returns void if all sync.
146
+ return (payload: Hooks[T]): void | Promise<void> => {
147
+ for (let i = 0; i < hooks.length; i++) {
148
+ const hook = hooks[i];
149
+ try {
150
+ const result = hook.callback(payload);
151
+ if (result && typeof result === "object" && "then" in result) {
152
+ // Hit an async hook - await it and continue remaining hooks async
153
+ if (catchErrors) {
154
+ return (result as Promise<void>)
155
+ .catch((error) => {
156
+ log?.error(
157
+ `${String(event)}(${hook.caller?.name ?? "unknown"}) ERROR`,
158
+ error,
159
+ );
160
+ })
161
+ .then(() => runRemainingAsync(i + 1, payload));
162
+ }
163
+ return (result as Promise<void>).then(() =>
164
+ runRemainingAsync(i + 1, payload),
165
+ );
166
+ }
167
+ } catch (error) {
168
+ if (catchErrors) {
169
+ log?.error(
170
+ `${String(event)}(${hook.caller?.name ?? "unknown"}) ERROR`,
171
+ error,
172
+ );
173
+ } else {
174
+ throw error;
175
+ }
176
+ }
177
+ }
178
+ // All hooks were sync - return void
179
+ };
180
+ }
181
+
62
182
  /**
63
183
  * Emits the specified event with the given payload.
184
+ *
185
+ * For hot paths (like HTTP request handling), use compile() instead
186
+ * to get an optimized executor.
64
187
  */
65
188
  public async emit<T extends keyof Hooks>(
66
189
  func: T,
@@ -87,36 +210,65 @@ export class EventManager {
87
210
  catch?: boolean;
88
211
  } = {},
89
212
  ): Promise<void> {
213
+ // Fast path: no listeners for this event
214
+ const events = this.events[func];
215
+ if (!events || events.length === 0) {
216
+ return;
217
+ }
218
+
219
+ // Fast path: single listener, no logging, no reverse
220
+ if (events.length === 1 && !options.log && !options.reverse) {
221
+ const hook = events[0];
222
+ try {
223
+ const result = hook.callback(payload);
224
+ if (result && typeof result === "object" && "then" in result) {
225
+ await result;
226
+ }
227
+ } catch (error) {
228
+ if (options.catch) {
229
+ this.log?.error(
230
+ `${String(func)}(${hook.caller?.name ?? "unknown"}) ERROR`,
231
+ error,
232
+ );
233
+ return;
234
+ }
235
+ throw error;
236
+ }
237
+ return;
238
+ }
239
+
90
240
  const ctx: any = {};
91
241
 
92
242
  if (options.log) {
93
- ctx.now = Date.now();
94
- this.log?.trace(`${func} ...`);
243
+ ctx.now = performance.now();
244
+ this.log?.trace(`${String(func)} ...`);
95
245
  }
96
246
 
97
- let events = this.events[func] ?? [];
98
-
247
+ let eventList = events;
99
248
  if (options.reverse) {
100
- events = events.toReversed();
249
+ eventList = events.toReversed();
101
250
  }
102
251
 
103
- for (const hook of events) {
252
+ for (const hook of eventList) {
104
253
  const name = hook.caller?.name ?? "unknown";
105
254
  if (options.log) {
106
- ctx.now2 = Date.now();
107
- this.log?.trace(`${func}(${name}) ...`);
255
+ ctx.now2 = performance.now();
256
+ this.log?.trace(`${String(func)}(${name}) ...`);
108
257
  }
109
258
 
110
259
  try {
111
- await hook.callback(payload);
260
+ const result = hook.callback(payload);
261
+ if (result && typeof result === "object" && "then" in result) {
262
+ await result;
263
+ }
112
264
  } catch (error) {
113
265
  if (options.catch) {
114
- this.log?.error(`${func}(${name}) ERROR`, error);
266
+ this.log?.error(`${String(func)}(${name}) ERROR`, error);
115
267
  continue;
116
268
  }
117
269
  if (options.log) {
118
270
  throw new AlephaError(
119
- `Failed during '${func}()' hook for service: ${name}`,
271
+ `Failed during '${String(func)}()' hook for service: ${name}`,
120
272
  { cause: error },
121
273
  );
122
274
  }
@@ -124,12 +276,16 @@ export class EventManager {
124
276
  }
125
277
 
126
278
  if (options.log) {
127
- this.log?.debug(`${func}(${name}) OK [${Date.now() - ctx.now2}ms]`);
279
+ this.log?.debug(
280
+ `${String(func)}(${name}) OK [${(performance.now() - ctx.now2).toFixed(1)}ms]`,
281
+ );
128
282
  }
129
283
  }
130
284
 
131
285
  if (options.log) {
132
- this.log?.debug(`${func} OK [${Date.now() - ctx.now}ms]`);
286
+ this.log?.debug(
287
+ `${String(func)} OK [${(performance.now() - ctx.now).toFixed(1)}ms]`,
288
+ );
133
289
  }
134
290
  }
135
291
  }