alepha 0.14.2 → 0.14.4

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 (405) hide show
  1. package/README.md +1 -1
  2. package/dist/api/audits/index.browser.js +5 -5
  3. package/dist/api/audits/index.browser.js.map +1 -1
  4. package/dist/api/audits/index.d.ts +706 -785
  5. package/dist/api/audits/index.d.ts.map +1 -1
  6. package/dist/api/audits/index.js +13 -13
  7. package/dist/api/audits/index.js.map +1 -1
  8. package/dist/api/files/index.browser.js +5 -5
  9. package/dist/api/files/index.browser.js.map +1 -1
  10. package/dist/api/files/index.d.ts +58 -137
  11. package/dist/api/files/index.d.ts.map +1 -1
  12. package/dist/api/files/index.js +71 -71
  13. package/dist/api/files/index.js.map +1 -1
  14. package/dist/api/jobs/index.browser.js +5 -5
  15. package/dist/api/jobs/index.browser.js.map +1 -1
  16. package/dist/api/jobs/index.d.ts +29 -108
  17. package/dist/api/jobs/index.d.ts.map +1 -1
  18. package/dist/api/jobs/index.js +10 -10
  19. package/dist/api/jobs/index.js.map +1 -1
  20. package/dist/api/notifications/index.browser.js +10 -10
  21. package/dist/api/notifications/index.browser.js.map +1 -1
  22. package/dist/api/notifications/index.d.ts +504 -171
  23. package/dist/api/notifications/index.d.ts.map +1 -1
  24. package/dist/api/notifications/index.js +12 -12
  25. package/dist/api/notifications/index.js.map +1 -1
  26. package/dist/api/parameters/index.browser.js +163 -10
  27. package/dist/api/parameters/index.browser.js.map +1 -1
  28. package/dist/api/parameters/index.d.ts +277 -351
  29. package/dist/api/parameters/index.d.ts.map +1 -1
  30. package/dist/api/parameters/index.js +196 -91
  31. package/dist/api/parameters/index.js.map +1 -1
  32. package/dist/api/users/index.browser.js +19 -19
  33. package/dist/api/users/index.browser.js.map +1 -1
  34. package/dist/api/users/index.d.ts +787 -852
  35. package/dist/api/users/index.d.ts.map +1 -1
  36. package/dist/api/users/index.js +827 -596
  37. package/dist/api/users/index.js.map +1 -1
  38. package/dist/api/verifications/index.browser.js +6 -6
  39. package/dist/api/verifications/index.browser.js.map +1 -1
  40. package/dist/api/verifications/index.d.ts +128 -128
  41. package/dist/api/verifications/index.d.ts.map +1 -1
  42. package/dist/api/verifications/index.js +6 -6
  43. package/dist/api/verifications/index.js.map +1 -1
  44. package/dist/bin/index.d.ts +1 -2
  45. package/dist/bin/index.js +0 -1
  46. package/dist/bin/index.js.map +1 -1
  47. package/dist/cli/index.d.ts +252 -131
  48. package/dist/cli/index.d.ts.map +1 -1
  49. package/dist/cli/index.js +595 -395
  50. package/dist/cli/index.js.map +1 -1
  51. package/dist/command/index.d.ts +46 -11
  52. package/dist/command/index.d.ts.map +1 -1
  53. package/dist/command/index.js +99 -19
  54. package/dist/command/index.js.map +1 -1
  55. package/dist/core/index.browser.js +40 -22
  56. package/dist/core/index.browser.js.map +1 -1
  57. package/dist/core/index.d.ts +45 -1
  58. package/dist/core/index.d.ts.map +1 -1
  59. package/dist/core/index.js +40 -22
  60. package/dist/core/index.js.map +1 -1
  61. package/dist/core/index.native.js +40 -22
  62. package/dist/core/index.native.js.map +1 -1
  63. package/dist/fake/index.js +195 -168
  64. package/dist/fake/index.js.map +1 -1
  65. package/dist/file/index.d.ts +8 -0
  66. package/dist/file/index.d.ts.map +1 -1
  67. package/dist/file/index.js +3 -0
  68. package/dist/file/index.js.map +1 -1
  69. package/dist/logger/index.d.ts +1 -1
  70. package/dist/logger/index.d.ts.map +1 -1
  71. package/dist/logger/index.js +12 -2
  72. package/dist/logger/index.js.map +1 -1
  73. package/dist/mcp/index.js +1 -1
  74. package/dist/mcp/index.js.map +1 -1
  75. package/dist/orm/index.d.ts +59 -195
  76. package/dist/orm/index.d.ts.map +1 -1
  77. package/dist/orm/index.js +201 -430
  78. package/dist/orm/index.js.map +1 -1
  79. package/dist/security/index.d.ts +1 -1
  80. package/dist/security/index.d.ts.map +1 -1
  81. package/dist/security/index.js +1 -1
  82. package/dist/security/index.js.map +1 -1
  83. package/dist/server/auth/index.d.ts +171 -155
  84. package/dist/server/auth/index.d.ts.map +1 -1
  85. package/dist/server/auth/index.js +0 -1
  86. package/dist/server/auth/index.js.map +1 -1
  87. package/dist/server/cache/index.d.ts +12 -0
  88. package/dist/server/cache/index.d.ts.map +1 -1
  89. package/dist/server/cache/index.js +55 -2
  90. package/dist/server/cache/index.js.map +1 -1
  91. package/dist/server/compress/index.d.ts +6 -0
  92. package/dist/server/compress/index.d.ts.map +1 -1
  93. package/dist/server/compress/index.js +38 -1
  94. package/dist/server/compress/index.js.map +1 -1
  95. package/dist/server/core/index.browser.js +2 -2
  96. package/dist/server/core/index.browser.js.map +1 -1
  97. package/dist/server/core/index.d.ts +10 -10
  98. package/dist/server/core/index.d.ts.map +1 -1
  99. package/dist/server/core/index.js +7 -4
  100. package/dist/server/core/index.js.map +1 -1
  101. package/dist/server/links/index.browser.js +22 -6
  102. package/dist/server/links/index.browser.js.map +1 -1
  103. package/dist/server/links/index.d.ts +46 -44
  104. package/dist/server/links/index.d.ts.map +1 -1
  105. package/dist/server/links/index.js +24 -41
  106. package/dist/server/links/index.js.map +1 -1
  107. package/dist/server/static/index.d.ts.map +1 -1
  108. package/dist/server/static/index.js +4 -0
  109. package/dist/server/static/index.js.map +1 -1
  110. package/dist/server/swagger/index.d.ts +2 -1
  111. package/dist/server/swagger/index.d.ts.map +1 -1
  112. package/dist/server/swagger/index.js +9 -5
  113. package/dist/server/swagger/index.js.map +1 -1
  114. package/dist/vite/index.d.ts +101 -106
  115. package/dist/vite/index.d.ts.map +1 -1
  116. package/dist/vite/index.js +574 -503
  117. package/dist/vite/index.js.map +1 -1
  118. package/dist/websocket/index.d.ts +7 -7
  119. package/package.json +7 -7
  120. package/src/api/audits/controllers/{AuditController.ts → AdminAuditController.ts} +5 -6
  121. package/src/api/audits/entities/audits.ts +5 -5
  122. package/src/api/audits/index.browser.ts +1 -1
  123. package/src/api/audits/index.ts +3 -3
  124. package/src/api/audits/primitives/$audit.spec.ts +276 -0
  125. package/src/api/audits/services/AuditService.spec.ts +495 -0
  126. package/src/api/files/__tests__/$bucket.spec.ts +91 -0
  127. package/src/api/files/controllers/AdminFileStatsController.spec.ts +166 -0
  128. package/src/api/files/controllers/{StorageStatsController.ts → AdminFileStatsController.ts} +2 -2
  129. package/src/api/files/controllers/FileController.spec.ts +558 -0
  130. package/src/api/files/controllers/FileController.ts +4 -5
  131. package/src/api/files/entities/files.ts +5 -5
  132. package/src/api/files/index.browser.ts +1 -1
  133. package/src/api/files/index.ts +4 -4
  134. package/src/api/files/jobs/FileJobs.spec.ts +52 -0
  135. package/src/api/files/services/FileService.spec.ts +109 -0
  136. package/src/api/jobs/__tests__/JobController.spec.ts +343 -0
  137. package/src/api/jobs/controllers/{JobController.ts → AdminJobController.ts} +2 -2
  138. package/src/api/jobs/entities/jobExecutions.ts +5 -5
  139. package/src/api/jobs/index.ts +3 -3
  140. package/src/api/jobs/primitives/$job.spec.ts +476 -0
  141. package/src/api/notifications/controllers/{NotificationController.ts → AdminNotificationController.ts} +4 -5
  142. package/src/api/notifications/entities/notifications.ts +5 -5
  143. package/src/api/notifications/index.browser.ts +1 -1
  144. package/src/api/notifications/index.ts +4 -4
  145. package/src/api/parameters/controllers/{ConfigController.ts → AdminConfigController.ts} +46 -107
  146. package/src/api/parameters/entities/parameters.ts +7 -17
  147. package/src/api/parameters/index.ts +3 -3
  148. package/src/api/parameters/primitives/$config.spec.ts +356 -0
  149. package/src/api/parameters/schemas/activateConfigBodySchema.ts +12 -0
  150. package/src/api/parameters/schemas/checkScheduledResponseSchema.ts +8 -0
  151. package/src/api/parameters/schemas/configCurrentResponseSchema.ts +13 -0
  152. package/src/api/parameters/schemas/configHistoryResponseSchema.ts +9 -0
  153. package/src/api/parameters/schemas/configNameParamSchema.ts +10 -0
  154. package/src/api/parameters/schemas/configNamesResponseSchema.ts +8 -0
  155. package/src/api/parameters/schemas/configTreeNodeSchema.ts +13 -0
  156. package/src/api/parameters/schemas/configVersionParamSchema.ts +9 -0
  157. package/src/api/parameters/schemas/configVersionResponseSchema.ts +9 -0
  158. package/src/api/parameters/schemas/configsByStatusResponseSchema.ts +9 -0
  159. package/src/api/parameters/schemas/createConfigVersionBodySchema.ts +24 -0
  160. package/src/api/parameters/schemas/index.ts +15 -0
  161. package/src/api/parameters/schemas/parameterResponseSchema.ts +26 -0
  162. package/src/api/parameters/schemas/parameterStatusSchema.ts +13 -0
  163. package/src/api/parameters/schemas/rollbackConfigBodySchema.ts +15 -0
  164. package/src/api/parameters/schemas/statusParamSchema.ts +9 -0
  165. package/src/api/users/__tests__/EmailVerification.spec.ts +369 -0
  166. package/src/api/users/__tests__/PasswordReset.spec.ts +550 -0
  167. package/src/api/users/controllers/AdminIdentityController.spec.ts +365 -0
  168. package/src/api/users/controllers/{IdentityController.ts → AdminIdentityController.ts} +3 -4
  169. package/src/api/users/controllers/AdminSessionController.spec.ts +274 -0
  170. package/src/api/users/controllers/{SessionController.ts → AdminSessionController.ts} +3 -4
  171. package/src/api/users/controllers/AdminUserController.spec.ts +372 -0
  172. package/src/api/users/controllers/AdminUserController.ts +116 -0
  173. package/src/api/users/controllers/UserController.ts +4 -107
  174. package/src/api/users/controllers/UserRealmController.ts +3 -0
  175. package/src/api/users/entities/identities.ts +6 -6
  176. package/src/api/users/entities/sessions.ts +6 -6
  177. package/src/api/users/entities/users.ts +9 -9
  178. package/src/api/users/index.ts +9 -6
  179. package/src/api/users/primitives/$userRealm.ts +13 -8
  180. package/src/api/users/services/CredentialService.spec.ts +509 -0
  181. package/src/api/users/services/CredentialService.ts +46 -0
  182. package/src/api/users/services/IdentityService.ts +15 -0
  183. package/src/api/users/services/RegistrationService.spec.ts +630 -0
  184. package/src/api/users/services/RegistrationService.ts +18 -0
  185. package/src/api/users/services/SessionService.spec.ts +301 -0
  186. package/src/api/users/services/SessionService.ts +110 -1
  187. package/src/api/users/services/UserService.ts +67 -2
  188. package/src/api/verifications/__tests__/CodeVerification.spec.ts +318 -0
  189. package/src/api/verifications/__tests__/LinkVerification.spec.ts +279 -0
  190. package/src/api/verifications/entities/verifications.ts +6 -6
  191. package/src/api/verifications/jobs/VerificationJobs.spec.ts +50 -0
  192. package/src/batch/__tests__/startup-buffering.spec.ts +458 -0
  193. package/src/batch/primitives/$batch.spec.ts +766 -0
  194. package/src/batch/providers/BatchProvider.spec.ts +786 -0
  195. package/src/bin/index.ts +0 -1
  196. package/src/bucket/__tests__/shared.ts +194 -0
  197. package/src/bucket/primitives/$bucket.spec.ts +104 -0
  198. package/src/bucket/providers/FileStorageProvider.spec.ts +13 -0
  199. package/src/bucket/providers/LocalFileStorageProvider.spec.ts +77 -0
  200. package/src/bucket/providers/MemoryFileStorageProvider.spec.ts +82 -0
  201. package/src/cache/core/__tests__/shared.ts +377 -0
  202. package/src/cache/core/primitives/$cache.spec.ts +111 -0
  203. package/src/cache/redis/__tests__/cache-redis.spec.ts +70 -0
  204. package/src/cli/apps/AlephaCli.ts +25 -6
  205. package/src/cli/atoms/buildOptions.ts +88 -0
  206. package/src/cli/commands/build.ts +32 -69
  207. package/src/cli/commands/db.ts +0 -4
  208. package/src/cli/commands/dev.ts +34 -10
  209. package/src/cli/commands/gen/changelog.spec.ts +315 -0
  210. package/src/cli/commands/{changelog.ts → gen/changelog.ts} +9 -9
  211. package/src/cli/commands/gen/env.ts +53 -0
  212. package/src/cli/commands/gen/openapi.ts +71 -0
  213. package/src/cli/commands/gen/resource.ts +15 -0
  214. package/src/cli/commands/gen.ts +24 -0
  215. package/src/cli/commands/init.ts +2 -1
  216. package/src/cli/commands/root.ts +12 -3
  217. package/src/cli/commands/test.ts +0 -1
  218. package/src/cli/commands/typecheck.ts +5 -0
  219. package/src/cli/commands/verify.ts +1 -1
  220. package/src/cli/defineConfig.ts +49 -7
  221. package/src/cli/index.ts +2 -2
  222. package/src/cli/services/AlephaCliUtils.ts +105 -55
  223. package/src/cli/services/GitMessageParser.ts +1 -1
  224. package/src/command/helpers/Asker.spec.ts +127 -0
  225. package/src/command/helpers/Runner.spec.ts +126 -0
  226. package/src/command/helpers/Runner.ts +1 -1
  227. package/src/command/primitives/$command.spec.ts +1588 -0
  228. package/src/command/primitives/$command.ts +0 -6
  229. package/src/command/providers/CliProvider.ts +75 -27
  230. package/src/core/Alepha.ts +87 -0
  231. package/src/core/__tests__/Alepha-emit.spec.ts +22 -0
  232. package/src/core/__tests__/Alepha-graph.spec.ts +93 -0
  233. package/src/core/__tests__/Alepha-has.spec.ts +41 -0
  234. package/src/core/__tests__/Alepha-inject.spec.ts +93 -0
  235. package/src/core/__tests__/Alepha-register.spec.ts +81 -0
  236. package/src/core/__tests__/Alepha-start.spec.ts +176 -0
  237. package/src/core/__tests__/Alepha-with.spec.ts +14 -0
  238. package/src/core/__tests__/TypeBox-usecases.spec.ts +35 -0
  239. package/src/core/__tests__/TypeBoxLocale.spec.ts +15 -0
  240. package/src/core/__tests__/descriptor.spec.ts +34 -0
  241. package/src/core/__tests__/fixtures/A.ts +5 -0
  242. package/src/core/__tests__/pagination.spec.ts +77 -0
  243. package/src/core/helpers/jsonSchemaToTypeBox.ts +2 -2
  244. package/src/core/primitives/$atom.spec.ts +43 -0
  245. package/src/core/primitives/$hook.spec.ts +130 -0
  246. package/src/core/primitives/$inject.spec.ts +175 -0
  247. package/src/core/primitives/$module.spec.ts +115 -0
  248. package/src/core/providers/CodecManager.spec.ts +740 -0
  249. package/src/core/providers/EventManager.spec.ts +762 -0
  250. package/src/core/providers/EventManager.ts +4 -0
  251. package/src/core/providers/StateManager.spec.ts +365 -0
  252. package/src/core/providers/TypeProvider.spec.ts +1607 -0
  253. package/src/core/providers/TypeProvider.ts +20 -26
  254. package/src/datetime/primitives/$interval.spec.ts +103 -0
  255. package/src/datetime/providers/DateTimeProvider.spec.ts +86 -0
  256. package/src/email/primitives/$email.spec.ts +175 -0
  257. package/src/email/providers/LocalEmailProvider.spec.ts +341 -0
  258. package/src/fake/__tests__/keyName.example.ts +40 -0
  259. package/src/fake/__tests__/keyName.spec.ts +152 -0
  260. package/src/fake/__tests__/module.example.ts +32 -0
  261. package/src/fake/providers/FakeProvider.spec.ts +438 -0
  262. package/src/file/providers/FileSystemProvider.ts +8 -0
  263. package/src/file/providers/NodeFileSystemProvider.spec.ts +418 -0
  264. package/src/file/providers/NodeFileSystemProvider.ts +5 -0
  265. package/src/file/services/FileDetector.spec.ts +591 -0
  266. package/src/lock/core/__tests__/shared.ts +190 -0
  267. package/src/lock/core/providers/MemoryLockProvider.spec.ts +25 -0
  268. package/src/lock/redis/providers/RedisLockProvider.spec.ts +25 -0
  269. package/src/logger/__tests__/SimpleFormatterProvider.spec.ts +109 -0
  270. package/src/logger/index.ts +15 -3
  271. package/src/logger/primitives/$logger.spec.ts +108 -0
  272. package/src/logger/services/Logger.spec.ts +295 -0
  273. package/src/mcp/__tests__/errors.spec.ts +175 -0
  274. package/src/mcp/__tests__/integration.spec.ts +450 -0
  275. package/src/mcp/helpers/jsonrpc.spec.ts +380 -0
  276. package/src/mcp/primitives/$prompt.spec.ts +468 -0
  277. package/src/mcp/primitives/$resource.spec.ts +390 -0
  278. package/src/mcp/primitives/$tool.spec.ts +406 -0
  279. package/src/mcp/providers/McpServerProvider.spec.ts +797 -0
  280. package/src/mcp/transports/StdioMcpTransport.ts +1 -1
  281. package/src/orm/__tests__/$repository-crud.spec.ts +276 -0
  282. package/src/orm/__tests__/$repository-hooks.spec.ts +325 -0
  283. package/src/orm/__tests__/$repository-orderBy.spec.ts +128 -0
  284. package/src/orm/__tests__/$repository-pagination-sort.spec.ts +149 -0
  285. package/src/orm/__tests__/$repository-save.spec.ts +37 -0
  286. package/src/orm/__tests__/ModelBuilder-integration.spec.ts +490 -0
  287. package/src/orm/__tests__/ModelBuilder-types.spec.ts +186 -0
  288. package/src/orm/__tests__/PostgresProvider.spec.ts +46 -0
  289. package/src/orm/__tests__/delete-returning.spec.ts +256 -0
  290. package/src/orm/__tests__/deletedAt.spec.ts +80 -0
  291. package/src/orm/__tests__/enums.spec.ts +315 -0
  292. package/src/orm/__tests__/execute.spec.ts +72 -0
  293. package/src/orm/__tests__/fixtures/bigEntitySchema.ts +65 -0
  294. package/src/orm/__tests__/fixtures/userEntitySchema.ts +27 -0
  295. package/src/orm/__tests__/joins.spec.ts +1114 -0
  296. package/src/orm/__tests__/page.spec.ts +287 -0
  297. package/src/orm/__tests__/primaryKey.spec.ts +87 -0
  298. package/src/orm/__tests__/query-date-encoding.spec.ts +402 -0
  299. package/src/orm/__tests__/ref-auto-onDelete.spec.ts +156 -0
  300. package/src/orm/__tests__/references.spec.ts +102 -0
  301. package/src/orm/__tests__/security.spec.ts +710 -0
  302. package/src/orm/__tests__/sqlite.spec.ts +111 -0
  303. package/src/orm/__tests__/string-operators.spec.ts +429 -0
  304. package/src/orm/__tests__/timestamps.spec.ts +388 -0
  305. package/src/orm/__tests__/validation.spec.ts +183 -0
  306. package/src/orm/__tests__/version.spec.ts +64 -0
  307. package/src/orm/helpers/parseQueryString.spec.ts +196 -0
  308. package/src/orm/index.ts +2 -8
  309. package/src/orm/primitives/$repository.spec.ts +137 -0
  310. package/src/orm/primitives/$sequence.spec.ts +29 -0
  311. package/src/orm/primitives/$transaction.spec.ts +82 -0
  312. package/src/orm/providers/drivers/BunPostgresProvider.ts +3 -3
  313. package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -1
  314. package/src/orm/providers/drivers/CloudflareD1Provider.ts +1 -1
  315. package/src/orm/providers/drivers/DatabaseProvider.ts +1 -1
  316. package/src/orm/providers/drivers/NodePostgresProvider.ts +3 -3
  317. package/src/orm/providers/drivers/NodeSqliteProvider.ts +1 -1
  318. package/src/orm/providers/drivers/PglitePostgresProvider.ts +2 -2
  319. package/src/orm/services/ModelBuilder.spec.ts +575 -0
  320. package/src/orm/services/Repository.spec.ts +137 -0
  321. package/src/queue/core/__tests__/shared.ts +143 -0
  322. package/src/queue/core/providers/MemoryQueueProvider.spec.ts +23 -0
  323. package/src/queue/core/providers/WorkerProvider.spec.ts +394 -0
  324. package/src/queue/redis/providers/RedisQueueProvider.spec.ts +23 -0
  325. package/src/redis/__tests__/redis.spec.ts +58 -0
  326. package/src/retry/primitives/$retry.spec.ts +234 -0
  327. package/src/retry/providers/RetryProvider.spec.ts +438 -0
  328. package/src/router/__tests__/match.spec.ts +252 -0
  329. package/src/router/providers/RouterProvider.spec.ts +197 -0
  330. package/src/scheduler/__tests__/$scheduler-cron.spec.ts +25 -0
  331. package/src/scheduler/__tests__/$scheduler-interval.spec.ts +25 -0
  332. package/src/scheduler/__tests__/shared.ts +77 -0
  333. package/src/security/__tests__/bug-1-wildcard-after-start.spec.ts +229 -0
  334. package/src/security/__tests__/bug-2-password-validation.spec.ts +245 -0
  335. package/src/security/__tests__/bug-3-regex-vulnerability.spec.ts +407 -0
  336. package/src/security/__tests__/bug-4-oauth2-validation.spec.ts +439 -0
  337. package/src/security/__tests__/multi-layer-permissions.spec.ts +522 -0
  338. package/src/security/primitives/$permission.spec.ts +30 -0
  339. package/src/security/primitives/$permission.ts +2 -2
  340. package/src/security/primitives/$realm.spec.ts +101 -0
  341. package/src/security/primitives/$role.spec.ts +52 -0
  342. package/src/security/primitives/$serviceAccount.spec.ts +61 -0
  343. package/src/security/providers/SecurityProvider.spec.ts +350 -0
  344. package/src/server/auth/providers/ServerAuthProvider.ts +0 -2
  345. package/src/server/cache/providers/ServerCacheProvider.spec.ts +1125 -0
  346. package/src/server/cache/providers/ServerCacheProvider.ts +94 -9
  347. package/src/server/compress/providers/ServerCompressProvider.spec.ts +31 -0
  348. package/src/server/compress/providers/ServerCompressProvider.ts +63 -2
  349. package/src/server/cookies/providers/ServerCookiesProvider.spec.ts +253 -0
  350. package/src/server/core/__tests__/ServerRouterProvider-getRoutes.spec.ts +334 -0
  351. package/src/server/core/__tests__/ServerRouterProvider-requestId.spec.ts +129 -0
  352. package/src/server/core/helpers/ServerReply.ts +2 -2
  353. package/src/server/core/primitives/$action.spec.ts +191 -0
  354. package/src/server/core/primitives/$route.spec.ts +65 -0
  355. package/src/server/core/providers/ServerBodyParserProvider.spec.ts +93 -0
  356. package/src/server/core/providers/ServerLoggerProvider.spec.ts +100 -0
  357. package/src/server/core/providers/ServerProvider.ts +14 -2
  358. package/src/server/core/services/HttpClient.spec.ts +123 -0
  359. package/src/server/core/services/UserAgentParser.spec.ts +111 -0
  360. package/src/server/cors/providers/ServerCorsProvider.spec.ts +481 -0
  361. package/src/server/health/providers/ServerHealthProvider.spec.ts +22 -0
  362. package/src/server/helmet/providers/ServerHelmetProvider.spec.ts +105 -0
  363. package/src/server/links/__tests__/$action.spec.ts +238 -0
  364. package/src/server/links/__tests__/fixtures/CrudApp.ts +122 -0
  365. package/src/server/links/__tests__/requestId.spec.ts +120 -0
  366. package/src/server/links/primitives/$remote.spec.ts +228 -0
  367. package/src/server/links/providers/LinkProvider.spec.ts +54 -0
  368. package/src/server/links/providers/LinkProvider.ts +49 -3
  369. package/src/server/links/providers/ServerLinksProvider.ts +1 -53
  370. package/src/server/links/schemas/apiLinksResponseSchema.ts +7 -0
  371. package/src/server/metrics/providers/ServerMetricsProvider.spec.ts +25 -0
  372. package/src/server/multipart/providers/ServerMultipartProvider.spec.ts +528 -0
  373. package/src/server/proxy/primitives/$proxy.spec.ts +87 -0
  374. package/src/server/rate-limit/__tests__/ActionRateLimit.spec.ts +211 -0
  375. package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +344 -0
  376. package/src/server/security/__tests__/BasicAuth.spec.ts +684 -0
  377. package/src/server/security/__tests__/ServerSecurityProvider-realm.spec.ts +388 -0
  378. package/src/server/security/providers/ServerSecurityProvider.spec.ts +123 -0
  379. package/src/server/static/primitives/$serve.spec.ts +193 -0
  380. package/src/server/static/providers/ServerStaticProvider.ts +10 -0
  381. package/src/server/swagger/__tests__/ui.spec.ts +52 -0
  382. package/src/server/swagger/primitives/$swagger.spec.ts +193 -0
  383. package/src/server/swagger/providers/ServerSwaggerProvider.ts +19 -12
  384. package/src/sms/primitives/$sms.spec.ts +165 -0
  385. package/src/sms/providers/LocalSmsProvider.spec.ts +224 -0
  386. package/src/sms/providers/MemorySmsProvider.spec.ts +193 -0
  387. package/src/thread/primitives/$thread.spec.ts +186 -0
  388. package/src/topic/core/__tests__/shared.ts +144 -0
  389. package/src/topic/core/providers/MemoryTopicProvider.spec.ts +23 -0
  390. package/src/topic/redis/providers/RedisTopicProvider.spec.ts +23 -0
  391. package/src/vite/helpers/importViteReact.ts +13 -0
  392. package/src/vite/index.ts +1 -21
  393. package/src/vite/plugins/viteAlephaDev.ts +32 -5
  394. package/src/vite/plugins/viteAlephaSsrPreload.ts +222 -0
  395. package/src/vite/tasks/buildClient.ts +11 -0
  396. package/src/vite/tasks/buildServer.ts +47 -3
  397. package/src/vite/tasks/devServer.ts +69 -0
  398. package/src/vite/tasks/index.ts +2 -1
  399. package/src/vite/tasks/runAlepha.ts +7 -1
  400. package/src/websocket/__tests__/$websocket-new.spec.ts +195 -0
  401. package/src/websocket/primitives/$channel.spec.ts +30 -0
  402. package/src/cli/assets/viteConfigTs.ts +0 -14
  403. package/src/cli/commands/run.ts +0 -24
  404. package/src/vite/plugins/viteAlepha.ts +0 -37
  405. package/src/vite/plugins/viteAlephaBuild.ts +0 -281
@@ -0,0 +1,222 @@
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
3
+ import { dirname, join, relative, resolve } from "node:path";
4
+ import type { Plugin } from "vite";
5
+
6
+ /**
7
+ * Preload manifest mapping short keys to source paths.
8
+ * Generated at build time, consumed by SSRManifestProvider at runtime.
9
+ */
10
+ export interface PreloadManifest {
11
+ [key: string]: string;
12
+ }
13
+
14
+ /**
15
+ * Vite plugin that generates a preload manifest for SSR module preloading.
16
+ *
17
+ * Instead of injecting source paths directly into $page definitions (which would
18
+ * leak component paths in the browser bundle), this plugin:
19
+ *
20
+ * 1. Collects all lazy import paths from $page definitions during transform
21
+ * 2. Generates a manifest file mapping short keys to resolved source paths
22
+ * 3. Injects only the short key into $page definitions
23
+ *
24
+ * The manifest is written to `.vite/preload-manifest.json` alongside Vite's
25
+ * other manifests. The CLI build command moves all manifests to
26
+ * `dist/server/.ssr/` where SSRManifestProvider loads them at runtime.
27
+ *
28
+ * Before:
29
+ * ```typescript
30
+ * $page({
31
+ * path: '/users/:id',
32
+ * lazy: () => import('./UserDetail.tsx'),
33
+ * })
34
+ * ```
35
+ *
36
+ * After:
37
+ * ```typescript
38
+ * $page({
39
+ * path: '/users/:id',
40
+ * lazy: () => import('./UserDetail.tsx'),
41
+ * [Symbol.for("alepha.page.preload")]: "a1b2c3",
42
+ * })
43
+ * ```
44
+ *
45
+ * Manifest (.alepha/preload-manifest.json):
46
+ * ```json
47
+ * {
48
+ * "a1b2c3": "src/pages/UserDetail.tsx"
49
+ * }
50
+ * ```
51
+ */
52
+ export function viteAlephaSsrPreload(): Plugin {
53
+ let root = "";
54
+ const preloadMap = new Map<string, string>(); // key -> sourcePath
55
+
56
+ /**
57
+ * Generate a short hash key for a source path.
58
+ * Uses first 8 chars of MD5 hash for brevity while avoiding collisions.
59
+ */
60
+ function generateKey(sourcePath: string): string {
61
+ return createHash("md5").update(sourcePath).digest("hex").slice(0, 8);
62
+ }
63
+
64
+ return {
65
+ name: "alepha-preload",
66
+ configResolved(config) {
67
+ root = config.root;
68
+ },
69
+ transform(code, id) {
70
+ // Only process TypeScript/JavaScript files
71
+ if (!id.match(/\.[tj]sx?$/)) {
72
+ return null;
73
+ }
74
+
75
+ // Skip node_modules
76
+ if (id.includes("node_modules")) {
77
+ return null;
78
+ }
79
+
80
+ // Quick check if file contains $page with lazy
81
+ if (!code.includes("$page") || !code.includes("lazy")) {
82
+ return null;
83
+ }
84
+
85
+ // Collect all insertions first, then apply in reverse order
86
+ const insertions: Array<{ position: number; text: string }> = [];
87
+
88
+ // Find all $page({ occurrences
89
+ const pageStartRegex = /\$page\s*\(\s*\{/g;
90
+ let pageMatch: RegExpExecArray | null = pageStartRegex.exec(code);
91
+
92
+ while (pageMatch !== null) {
93
+ const startIndex = pageMatch.index;
94
+ const objectStartIndex = startIndex + pageMatch[0].length - 1; // Position of '{'
95
+
96
+ // Find the matching closing brace using brace counting
97
+ let braceCount = 1;
98
+ let i = objectStartIndex + 1;
99
+ while (i < code.length && braceCount > 0) {
100
+ if (code[i] === "{") braceCount++;
101
+ else if (code[i] === "}") braceCount--;
102
+ i++;
103
+ }
104
+
105
+ // Malformed, skip
106
+ if (braceCount !== 0) {
107
+ pageMatch = pageStartRegex.exec(code);
108
+ continue;
109
+ }
110
+
111
+ const objectEndIndex = i - 1; // Position of matching '}'
112
+ const pageContent = code.slice(objectStartIndex, objectEndIndex + 1);
113
+
114
+ // Skip if already has preload symbol
115
+ if (pageContent.includes("alepha.page.preload")) {
116
+ pageMatch = pageStartRegex.exec(code);
117
+ continue;
118
+ }
119
+
120
+ // Find lazy: () => import('...') within this $page block
121
+ const lazyRegex =
122
+ /lazy\s*:\s*\(\s*\)\s*=>\s*import\s*\(\s*['"]([^'"]+)['"]\s*\)/;
123
+ const lazyMatch = lazyRegex.exec(pageContent);
124
+
125
+ if (!lazyMatch) {
126
+ pageMatch = pageStartRegex.exec(code);
127
+ continue;
128
+ }
129
+
130
+ const importPath = lazyMatch[1];
131
+
132
+ // Resolve the import path relative to the current file
133
+ const currentDir = dirname(id);
134
+ let resolvedPath: string;
135
+
136
+ if (importPath.startsWith(".")) {
137
+ // Relative import
138
+ resolvedPath = resolve(currentDir, importPath);
139
+ } else if (importPath.startsWith("/")) {
140
+ // Absolute import from root
141
+ resolvedPath = resolve(root, importPath.slice(1));
142
+ } else {
143
+ // Package import - skip preloading for external packages
144
+ pageMatch = pageStartRegex.exec(code);
145
+ continue;
146
+ }
147
+
148
+ // Make path relative to root for SSR manifest lookup
149
+ let relativePath = relative(root, resolvedPath);
150
+
151
+ // Normalize path separators for cross-platform compatibility
152
+ relativePath = relativePath.replace(/\\/g, "/");
153
+
154
+ // Handle extension - Vite resolves .jsx imports to .tsx files
155
+ // Try to use .tsx when the source has .jsx since TypeScript is more common
156
+ if (!relativePath.match(/\.[tj]sx?$/)) {
157
+ relativePath = `${relativePath}.tsx`;
158
+ } else if (relativePath.endsWith(".jsx")) {
159
+ // Import said .jsx but actual file is likely .tsx
160
+ relativePath = relativePath.replace(/\.jsx$/, ".tsx");
161
+ } else if (relativePath.endsWith(".js")) {
162
+ // Import said .js but actual file is likely .ts
163
+ relativePath = relativePath.replace(/\.js$/, ".ts");
164
+ }
165
+
166
+ // Generate short key and store mapping
167
+ const key = generateKey(relativePath);
168
+ preloadMap.set(key, relativePath);
169
+
170
+ // Check if we need a comma (look at character before closing brace)
171
+ const beforeBrace = code.slice(0, objectEndIndex).trimEnd();
172
+ const needsComma = !beforeBrace.endsWith(",");
173
+ // Use Symbol.for() so it can be looked up at runtime without importing
174
+ // Only inject the short key, not the full path
175
+ const preloadProperty = `${needsComma ? "," : ""} [Symbol.for("alepha.page.preload")]: "${key}"`;
176
+
177
+ insertions.push({ position: objectEndIndex, text: preloadProperty });
178
+
179
+ pageMatch = pageStartRegex.exec(code);
180
+ }
181
+
182
+ if (insertions.length === 0) {
183
+ return null;
184
+ }
185
+
186
+ // Apply insertions in reverse order to preserve positions
187
+ let result = code;
188
+ for (let j = insertions.length - 1; j >= 0; j--) {
189
+ const { position, text } = insertions[j];
190
+ result = result.slice(0, position) + text + result.slice(position);
191
+ }
192
+
193
+ return {
194
+ code: result,
195
+ map: null,
196
+ };
197
+ },
198
+ writeBundle(options) {
199
+ // Only process client build (outputs to dist/public or similar)
200
+ // Skip server build which outputs to dist/server
201
+ const outDir = options.dir || "";
202
+ if (outDir.includes("server")) {
203
+ return;
204
+ }
205
+
206
+ // Write the preload manifest to the same .vite directory Vite uses
207
+ // The CLI build command will move all manifests to dist/server/.ssr/
208
+ if (preloadMap.size > 0) {
209
+ const viteDir = join(outDir, ".vite");
210
+
211
+ // Ensure .vite directory exists
212
+ if (!existsSync(viteDir)) {
213
+ mkdirSync(viteDir, { recursive: true });
214
+ }
215
+
216
+ const manifest: PreloadManifest = Object.fromEntries(preloadMap);
217
+ const manifestPath = join(viteDir, "preload-manifest.json");
218
+ writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
219
+ }
220
+ },
221
+ };
222
+ }
@@ -2,6 +2,8 @@ import type { UserConfig } from "vite";
2
2
  import { analyzer as viteAnalyzer } from "vite-bundle-analyzer";
3
3
  import { createBufferedLogger } from "../helpers/createBufferedLogger.ts";
4
4
  import { importVite } from "../helpers/importVite.ts";
5
+ import { importViteReact } from "../helpers/importViteReact.ts";
6
+ import { viteAlephaSsrPreload } from "../plugins/viteAlephaSsrPreload.ts";
5
7
  import {
6
8
  type ViteCompressOptions,
7
9
  viteCompress,
@@ -62,6 +64,12 @@ export async function buildClient(opts: BuildClientOptions): Promise<void> {
62
64
  const { build: viteBuild, mergeConfig } = await importVite();
63
65
  const plugins: any[] = [];
64
66
 
67
+ const viteReact = await importViteReact();
68
+ if (viteReact) plugins.push(viteReact());
69
+
70
+ // Add preload plugin for SSR module preloading
71
+ plugins.push(viteAlephaSsrPreload());
72
+
65
73
  const compress: ViteCompressOptions | undefined = opts.precompress
66
74
  ? typeof opts.precompress === "object"
67
75
  ? opts.precompress
@@ -93,6 +101,9 @@ export async function buildClient(opts: BuildClientOptions): Promise<void> {
93
101
  build: {
94
102
  chunkSizeWarningLimit: 1000,
95
103
  outDir: opts.dist,
104
+ // Generate manifest for SSR module preloading
105
+ manifest: true,
106
+ ssrManifest: true,
96
107
  rollupOptions: {
97
108
  output: {
98
109
  entryFileNames: "entry.[hash].js",
@@ -1,4 +1,4 @@
1
- import { readFile, writeFile } from "node:fs/promises";
1
+ import { readFile, rm, writeFile } from "node:fs/promises";
2
2
  import { join } from "node:path";
3
3
  import { AlephaError } from "alepha";
4
4
  import type * as vite from "vite";
@@ -6,6 +6,8 @@ import type { UserConfig } from "vite";
6
6
  import { analyzer as viteAnalyzer } from "vite-bundle-analyzer";
7
7
  import { createBufferedLogger } from "../helpers/createBufferedLogger.ts";
8
8
  import { importVite } from "../helpers/importVite.ts";
9
+ import { importViteReact } from "../helpers/importViteReact.ts";
10
+ import { viteAlephaSsrPreload } from "../plugins/viteAlephaSsrPreload.ts";
9
11
  import { generateExternals } from "./generateExternals.ts";
10
12
 
11
13
  export interface BuildServerOptions {
@@ -63,6 +65,13 @@ export async function buildServer(
63
65
  const { build: viteBuild, mergeConfig } = await importVite();
64
66
  const plugins: any[] = [];
65
67
 
68
+ const viteReact = await importViteReact();
69
+ if (viteReact && opts.clientDir) {
70
+ plugins.push(viteReact());
71
+ }
72
+
73
+ plugins.push(viteAlephaSsrPreload());
74
+
66
75
  if (opts.stats) {
67
76
  plugins.push(
68
77
  viteAnalyzer({
@@ -85,7 +94,7 @@ export async function buildServer(
85
94
  noExternal: true,
86
95
  },
87
96
  build: {
88
- sourcemap: true, // or "hidden" if you don't want to expose source maps
97
+ sourcemap: true,
89
98
  ssr: opts.entry,
90
99
  outDir: `${opts.distDir}/server`,
91
100
  minify: true,
@@ -138,6 +147,29 @@ export async function buildServer(
138
147
  template = `__alepha.set("alepha.react.server.template", \`${index.replace(/>\s*</g, "><").trim()}\`);\n`;
139
148
  }
140
149
 
150
+ // Embed SSR manifests if client was built
151
+ // This bundles all manifest data into index.js for serverless deployments
152
+ let manifest = "";
153
+ if (opts.clientDir) {
154
+ const viteDir = `${opts.distDir}/${opts.clientDir}/.vite`;
155
+ const ssrManifest = await loadJsonFile(`${viteDir}/ssr-manifest.json`);
156
+ const clientManifest = await loadJsonFile(`${viteDir}/manifest.json`);
157
+ const preloadManifest = await loadJsonFile(
158
+ `${viteDir}/preload-manifest.json`,
159
+ );
160
+
161
+ const combined = {
162
+ ssr: ssrManifest,
163
+ client: clientManifest,
164
+ preload: preloadManifest,
165
+ };
166
+
167
+ manifest = `__alepha.set("alepha.react.ssr.manifest", ${JSON.stringify(combined)});\n`;
168
+
169
+ // Remove .vite directory - no longer needed at runtime
170
+ await rm(viteDir, { recursive: true, force: true });
171
+ }
172
+
141
173
  const warning =
142
174
  "// This file was automatically generated. DO NOT MODIFY." +
143
175
  "\n" +
@@ -145,12 +177,24 @@ export async function buildServer(
145
177
 
146
178
  await writeFile(
147
179
  `${opts.distDir}/index.js`,
148
- `${warning}\nimport './server/${entryFile}';\n\n${template}`.trim(),
180
+ `${warning}\n${template}${manifest}import './server/${entryFile}';\n`.trim(),
149
181
  );
150
182
 
151
183
  return { entryFile };
152
184
  }
153
185
 
186
+ /**
187
+ * Load a JSON file, returning undefined if it doesn't exist.
188
+ */
189
+ async function loadJsonFile(path: string): Promise<any> {
190
+ try {
191
+ const content = await readFile(path, "utf-8");
192
+ return JSON.parse(content);
193
+ } catch {
194
+ return undefined;
195
+ }
196
+ }
197
+
154
198
  /**
155
199
  * Extract entry filename from Vite build result.
156
200
  */
@@ -0,0 +1,69 @@
1
+ import type { InlineConfig } from "vite";
2
+ import { importVite } from "../helpers/importVite.ts";
3
+ import { importViteReact } from "../helpers/importViteReact.ts";
4
+ import { viteAlephaDev } from "../plugins/viteAlephaDev.ts";
5
+ import { viteAlephaSsrPreload } from "../plugins/viteAlephaSsrPreload.ts";
6
+
7
+ export interface DevServerOptions {
8
+ /**
9
+ * Path to the server entry file.
10
+ * If not provided, will auto-detect.
11
+ */
12
+ entry?: string;
13
+
14
+ /**
15
+ * Port to run the dev server on.
16
+ */
17
+ port?: number;
18
+
19
+ /**
20
+ * Host to bind the dev server to.
21
+ */
22
+ host?: string | boolean;
23
+
24
+ /**
25
+ * Enable debug logging.
26
+ */
27
+ debug?: boolean;
28
+ }
29
+
30
+ /**
31
+ * Start Vite development server with Alepha plugins.
32
+ *
33
+ * This task starts the Vite dev server with all required plugins:
34
+ * - @vitejs/plugin-react (JSX/TSX compilation)
35
+ * - viteAlephaDev (Alepha server integration)
36
+ * - viteAlephaSsrPreload (SSR module preloading)
37
+ */
38
+ export async function devServer(opts: DevServerOptions = {}): Promise<void> {
39
+ const { createServer, mergeConfig } = await importVite();
40
+ const plugins: any[] = [];
41
+
42
+ // Add React plugin for JSX/TSX compilation
43
+ const viteReact = await importViteReact();
44
+ if (viteReact) plugins.push(viteReact());
45
+
46
+ // Add SSR preload plugin
47
+ plugins.push(viteAlephaSsrPreload());
48
+
49
+ // Add Alepha dev plugin
50
+ plugins.push(
51
+ await viteAlephaDev({
52
+ serverEntry: opts.entry,
53
+ debug: opts.debug,
54
+ }),
55
+ );
56
+
57
+ const config: InlineConfig = {
58
+ plugins,
59
+ server: {
60
+ port: opts.port,
61
+ host: opts.host,
62
+ },
63
+ };
64
+
65
+ const server = await createServer(mergeConfig(config, {}));
66
+ await server.listen();
67
+
68
+ // server.printUrls();
69
+ }
@@ -2,7 +2,7 @@
2
2
  * Build tasks for Alepha applications.
3
3
  *
4
4
  * These tasks can be used by:
5
- * - Vite plugins (viteAlephaDev, viteAlephaBuild)
5
+ * - Vite plugins (viteAlephaDev, viteAlephaSsrPreload)
6
6
  * - CLI commands (alepha build, alepha dev)
7
7
  *
8
8
  * Each task is a standalone async function with explicit inputs and outputs.
@@ -13,6 +13,7 @@
13
13
  export * from "./buildClient.ts";
14
14
  export * from "./buildServer.ts";
15
15
  export * from "./copyAssets.ts";
16
+ export * from "./devServer.ts";
16
17
  export * from "./generateCloudflare.ts";
17
18
  export * from "./generateDocker.ts";
18
19
  export * from "./generateExternals.ts";
@@ -132,13 +132,19 @@ export class AlephaRunner {
132
132
  process.env[key] = env[key];
133
133
  }
134
134
 
135
+ let port = 5173;
136
+ const address = server.httpServer?.address();
137
+ if (typeof address === "object" && address?.port) {
138
+ port = address.port;
139
+ }
140
+
135
141
  process.env.NODE_ENV ??= "development";
136
142
  process.env.VITE_ALEPHA_DEV = "true";
137
143
  process.env.SERVER_HOST ??=
138
144
  typeof server.config.server.host === "string"
139
145
  ? server.config.server.host
140
146
  : "localhost";
141
- process.env.SERVER_PORT ??= String(server.config.server.port || "5173");
147
+ process.env.SERVER_PORT ??= String(port);
142
148
 
143
149
  try {
144
150
  const now = Date.now();
@@ -0,0 +1,195 @@
1
+ import { Alepha, t } from "alepha";
2
+ import { NodeHttpServerProvider } from "alepha/server";
3
+ import { test } from "vitest";
4
+ import WebSocket from "ws";
5
+ import { AlephaWebSocket } from "../index.ts";
6
+ import { $channel } from "../primitives/$channel.ts";
7
+ import { $websocket } from "../primitives/$websocket.ts";
8
+
9
+ test("$websocket with channel-based architecture", async ({ expect }) => {
10
+ const alepha = Alepha.create().with(AlephaWebSocket);
11
+
12
+ const messages: any[] = [];
13
+
14
+ class ChatController {
15
+ // Define channel inside the class
16
+ chatChannel = $channel({
17
+ path: "/ws/chat",
18
+ schema: {
19
+ // Server → Client messages
20
+ in: t.union([
21
+ t.object({
22
+ type: t.const("append"),
23
+ content: t.text(),
24
+ username: t.text(),
25
+ }),
26
+ t.object({
27
+ type: t.const("system"),
28
+ message: t.text(),
29
+ }),
30
+ ]),
31
+ // Client → Server messages
32
+ out: t.object({
33
+ content: t.text(),
34
+ }),
35
+ },
36
+ });
37
+
38
+ chat = $websocket({
39
+ channel: this.chatChannel,
40
+ handler: async ({ connectionId, roomId, message, reply }) => {
41
+ // Broadcast to all in room except sender
42
+ await reply({
43
+ message: {
44
+ type: "append",
45
+ content: message.content,
46
+ username: connectionId,
47
+ },
48
+ exceptSelf: true,
49
+ });
50
+ },
51
+ });
52
+ }
53
+
54
+ const controller = alepha.inject(ChatController);
55
+ await alepha.start();
56
+
57
+ const httpServer = alepha.inject(NodeHttpServerProvider);
58
+ const hostname = httpServer.hostname.replace("http://", "ws://");
59
+
60
+ // Connect two clients to the same room
61
+ const ws1 = new WebSocket(`${hostname}/ws/chat?roomId=room-1`);
62
+ const ws2 = new WebSocket(`${hostname}/ws/chat?roomId=room-1`);
63
+
64
+ await Promise.all([
65
+ new Promise((resolve) => ws1.on("open", resolve)),
66
+ new Promise((resolve) => ws2.on("open", resolve)),
67
+ ]);
68
+
69
+ // Listen for messages on ws2
70
+ ws2.on("message", (data) => {
71
+ const message = JSON.parse(data.toString());
72
+ messages.push(message);
73
+ });
74
+
75
+ // Wait a bit for connections to be established
76
+ await new Promise((resolve) => setTimeout(resolve, 100));
77
+
78
+ // Send message from ws1
79
+ ws1.send(
80
+ JSON.stringify({
81
+ roomId: "room-1",
82
+ message: {
83
+ content: "Hello from client 1!",
84
+ },
85
+ }),
86
+ );
87
+
88
+ // Wait for message to be received
89
+ await new Promise((resolve) => setTimeout(resolve, 200));
90
+
91
+ // ws2 should receive the message, but not ws1 (exceptSelf)
92
+ expect(messages.length).toBe(1);
93
+ expect(messages[0].type).toBe("append");
94
+ expect(messages[0].content).toBe("Hello from client 1!");
95
+ expect(messages[0].username).toContain("ws-");
96
+
97
+ // Test emit API from server
98
+ await controller.chat.emit({
99
+ roomId: "room-1",
100
+ message: {
101
+ type: "system",
102
+ message: "Server announcement",
103
+ },
104
+ });
105
+
106
+ await new Promise((resolve) => setTimeout(resolve, 200));
107
+
108
+ // Both clients should receive the system message
109
+ expect(messages.length).toBe(2);
110
+ expect(messages[1].type).toBe("system");
111
+ expect(messages[1].message).toBe("Server announcement");
112
+
113
+ ws1.close();
114
+ ws2.close();
115
+ await alepha.stop();
116
+ });
117
+
118
+ test("$websocket room isolation", async ({ expect }) => {
119
+ const alepha = Alepha.create().with(AlephaWebSocket);
120
+
121
+ class ChatController {
122
+ chatChannel = $channel({
123
+ path: "/ws/chat",
124
+ schema: {
125
+ in: t.object({
126
+ type: t.const("message"),
127
+ content: t.text(),
128
+ }),
129
+ out: t.object({
130
+ content: t.text(),
131
+ }),
132
+ },
133
+ });
134
+
135
+ chat = $websocket({
136
+ channel: this.chatChannel,
137
+ handler: async ({ roomId, message, reply }) => {
138
+ await reply({
139
+ message: {
140
+ type: "message",
141
+ content: message.content,
142
+ },
143
+ });
144
+ },
145
+ });
146
+ }
147
+
148
+ const controller = alepha.inject(ChatController);
149
+ await alepha.start();
150
+
151
+ const httpServer = alepha.inject(NodeHttpServerProvider);
152
+ const hostname = httpServer.hostname.replace("http://", "ws://");
153
+
154
+ // Connect clients to different rooms
155
+ const room1Client = new WebSocket(`${hostname}/ws/chat?roomId=room-1`);
156
+ const room2Client = new WebSocket(`${hostname}/ws/chat?roomId=room-2`);
157
+
158
+ await Promise.all([
159
+ new Promise((resolve) => room1Client.on("open", resolve)),
160
+ new Promise((resolve) => room2Client.on("open", resolve)),
161
+ ]);
162
+
163
+ const room1Messages: any[] = [];
164
+ const room2Messages: any[] = [];
165
+
166
+ room1Client.on("message", (data) => {
167
+ room1Messages.push(JSON.parse(data.toString()));
168
+ });
169
+
170
+ room2Client.on("message", (data) => {
171
+ room2Messages.push(JSON.parse(data.toString()));
172
+ });
173
+
174
+ await new Promise((resolve) => setTimeout(resolve, 100));
175
+
176
+ // Send to room-1 only
177
+ await controller.chat.emit({
178
+ roomId: "room-1",
179
+ message: {
180
+ type: "message",
181
+ content: "Message for room 1",
182
+ },
183
+ });
184
+
185
+ await new Promise((resolve) => setTimeout(resolve, 200));
186
+
187
+ // Only room1Client should receive the message
188
+ expect(room1Messages.length).toBe(1);
189
+ expect(room1Messages[0].content).toBe("Message for room 1");
190
+ expect(room2Messages.length).toBe(0);
191
+
192
+ room1Client.close();
193
+ room2Client.close();
194
+ await alepha.stop();
195
+ });
@@ -0,0 +1,30 @@
1
+ import { Alepha, t } from "alepha";
2
+ import { test } from "vitest";
3
+ import { $channel } from "../primitives/$channel.ts";
4
+
5
+ test("$channel should create a channel primitive", async ({ expect }) => {
6
+ const alepha = Alepha.create();
7
+
8
+ class TestApp {
9
+ chat = $channel({
10
+ path: "/ws/chat",
11
+ description: "Chat channel",
12
+ schema: {
13
+ in: t.object({
14
+ type: t.const("message"),
15
+ content: t.text(),
16
+ }),
17
+ out: t.object({
18
+ content: t.text(),
19
+ }),
20
+ },
21
+ });
22
+ }
23
+
24
+ const app = alepha.inject(TestApp);
25
+
26
+ expect(app.chat).toBeDefined();
27
+ expect(app.chat.options.path).toBe("/ws/chat");
28
+ expect(app.chat.options.schema.in).toBeDefined();
29
+ expect(app.chat.options.schema.out).toBeDefined();
30
+ });
@@ -1,14 +0,0 @@
1
- export const viteConfigTs = (
2
- serverEntry?: string,
3
- ) => `
4
- import { viteAlepha } from "alepha/vite";
5
-
6
- export default {
7
- plugins: [
8
- viteAlepha(${serverEntry ? `{ serverEntry: "${serverEntry}" }` : ""}),
9
- ],
10
- test: {
11
- globals: true,
12
- },
13
- };
14
- `.trim();