alepha 0.20.2 → 0.20.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 (304) hide show
  1. package/README.md +0 -1
  2. package/assets/swagger-ui/swagger-ui-bundle.js +1 -1
  3. package/assets/swagger-ui/swagger-ui.css +1 -1
  4. package/dist/api/audits/index.browser.js +49 -0
  5. package/dist/api/audits/index.browser.js.map +1 -1
  6. package/dist/api/audits/index.js +49 -0
  7. package/dist/api/audits/index.js.map +1 -1
  8. package/dist/api/files/index.js.map +1 -1
  9. package/dist/api/jobs/index.d.ts +2 -61
  10. package/dist/api/jobs/index.d.ts.map +1 -1
  11. package/dist/api/jobs/index.js.map +1 -1
  12. package/dist/api/keys/index.d.ts +4 -4
  13. package/dist/api/keys/index.js.map +1 -1
  14. package/dist/api/notifications/index.d.ts +1 -10
  15. package/dist/api/notifications/index.d.ts.map +1 -1
  16. package/dist/api/parameters/index.browser.js +37 -0
  17. package/dist/api/parameters/index.browser.js.map +1 -1
  18. package/dist/api/parameters/index.d.ts +12 -68
  19. package/dist/api/parameters/index.d.ts.map +1 -1
  20. package/dist/api/parameters/index.js +57 -4
  21. package/dist/api/parameters/index.js.map +1 -1
  22. package/dist/api/payments/index.js.map +1 -1
  23. package/dist/api/users/index.browser.js +6 -0
  24. package/dist/api/users/index.browser.js.map +1 -1
  25. package/dist/api/users/index.d.ts +148 -227
  26. package/dist/api/users/index.d.ts.map +1 -1
  27. package/dist/api/users/index.js +60 -14
  28. package/dist/api/users/index.js.map +1 -1
  29. package/dist/api/verifications/index.d.ts.map +1 -1
  30. package/dist/api/verifications/index.js +2 -1
  31. package/dist/api/verifications/index.js.map +1 -1
  32. package/dist/bucket/index.d.ts +77 -107
  33. package/dist/bucket/index.d.ts.map +1 -1
  34. package/dist/bucket/index.js +153 -5
  35. package/dist/bucket/index.js.map +1 -1
  36. package/dist/bucket/index.workerd.js +12 -2
  37. package/dist/bucket/index.workerd.js.map +1 -1
  38. package/dist/cache/core/index.d.ts +26 -0
  39. package/dist/cache/core/index.d.ts.map +1 -1
  40. package/dist/cache/core/index.js +11 -1
  41. package/dist/cache/core/index.js.map +1 -1
  42. package/dist/cache/core/index.workerd.js +11 -1
  43. package/dist/cache/core/index.workerd.js.map +1 -1
  44. package/dist/captcha/index.js.map +1 -1
  45. package/dist/cli/config/index.d.ts +7 -5
  46. package/dist/cli/config/index.d.ts.map +1 -1
  47. package/dist/cli/config/index.js +2 -3
  48. package/dist/cli/config/index.js.map +1 -1
  49. package/dist/cli/core/index.d.ts +637 -11660
  50. package/dist/cli/core/index.d.ts.map +1 -1
  51. package/dist/cli/core/index.js +707 -532
  52. package/dist/cli/core/index.js.map +1 -1
  53. package/dist/cli/devtools/index.d.ts +4 -8
  54. package/dist/cli/devtools/index.d.ts.map +1 -1
  55. package/dist/cli/devtools/index.js +20 -16
  56. package/dist/cli/devtools/index.js.map +1 -1
  57. package/dist/cli/platform/index.d.ts +51 -77
  58. package/dist/cli/platform/index.d.ts.map +1 -1
  59. package/dist/cli/platform/index.js +65 -15
  60. package/dist/cli/platform/index.js.map +1 -1
  61. package/dist/cli/vendor/index.d.ts +10 -13
  62. package/dist/cli/vendor/index.d.ts.map +1 -1
  63. package/dist/cli/vendor/index.js +30 -12
  64. package/dist/cli/vendor/index.js.map +1 -1
  65. package/dist/command/index.js +1 -1
  66. package/dist/command/index.js.map +1 -1
  67. package/dist/core/index.browser.js +27 -3
  68. package/dist/core/index.browser.js.map +1 -1
  69. package/dist/core/index.d.ts +8 -11
  70. package/dist/core/index.d.ts.map +1 -1
  71. package/dist/core/index.js +27 -3
  72. package/dist/core/index.js.map +1 -1
  73. package/dist/core/index.native.js +27 -3
  74. package/dist/core/index.native.js.map +1 -1
  75. package/dist/core/index.workerd.js +27 -3
  76. package/dist/core/index.workerd.js.map +1 -1
  77. package/dist/crypto/index.js.map +1 -1
  78. package/dist/datetime/index.d.ts +69 -10
  79. package/dist/datetime/index.d.ts.map +1 -1
  80. package/dist/datetime/index.js +135 -13
  81. package/dist/datetime/index.js.map +1 -1
  82. package/dist/email/core/index.js.map +1 -1
  83. package/dist/email/smtp/index.js +130 -16
  84. package/dist/email/smtp/index.js.map +1 -1
  85. package/dist/fake/index.js.map +1 -1
  86. package/dist/lock/core/index.d.ts +30 -2
  87. package/dist/lock/core/index.d.ts.map +1 -1
  88. package/dist/lock/core/index.js +35 -12
  89. package/dist/lock/core/index.js.map +1 -1
  90. package/dist/lock/redis/index.js.map +1 -1
  91. package/dist/logger/index.js +32 -1
  92. package/dist/logger/index.js.map +1 -1
  93. package/dist/mcp/index.d.ts +238 -31
  94. package/dist/mcp/index.d.ts.map +1 -1
  95. package/dist/mcp/index.js +198 -67
  96. package/dist/mcp/index.js.map +1 -1
  97. package/dist/orm/core/index.browser.js +2 -362
  98. package/dist/orm/core/index.browser.js.map +1 -1
  99. package/dist/orm/core/index.bun.js +18 -409
  100. package/dist/orm/core/index.bun.js.map +1 -1
  101. package/dist/orm/core/index.d.ts +41 -194
  102. package/dist/orm/core/index.d.ts.map +1 -1
  103. package/dist/orm/core/index.js +27 -422
  104. package/dist/orm/core/index.js.map +1 -1
  105. package/dist/orm/postgres/index.bun.js +17 -20
  106. package/dist/orm/postgres/index.bun.js.map +1 -1
  107. package/dist/orm/postgres/index.d.ts +1 -5
  108. package/dist/orm/postgres/index.d.ts.map +1 -1
  109. package/dist/orm/postgres/index.js +17 -20
  110. package/dist/orm/postgres/index.js.map +1 -1
  111. package/dist/react/core/index.d.ts +102 -1
  112. package/dist/react/core/index.d.ts.map +1 -1
  113. package/dist/react/core/index.js +65 -1
  114. package/dist/react/core/index.js.map +1 -1
  115. package/dist/react/form/index.d.ts +6 -0
  116. package/dist/react/form/index.d.ts.map +1 -1
  117. package/dist/react/form/index.js +7 -7
  118. package/dist/react/form/index.js.map +1 -1
  119. package/dist/react/i18n/index.d.ts +7 -1
  120. package/dist/react/i18n/index.d.ts.map +1 -1
  121. package/dist/react/i18n/index.js +6 -0
  122. package/dist/react/i18n/index.js.map +1 -1
  123. package/dist/react/intro/index.js +22 -17
  124. package/dist/react/intro/index.js.map +1 -1
  125. package/dist/react/router/index.browser.js +98 -4
  126. package/dist/react/router/index.browser.js.map +1 -1
  127. package/dist/react/router/index.d.ts +58 -5
  128. package/dist/react/router/index.d.ts.map +1 -1
  129. package/dist/react/router/index.js +122 -6
  130. package/dist/react/router/index.js.map +1 -1
  131. package/dist/react/testing/{chunk-DBEY4PJZ.js → chunk-6Ep1yQYe.js} +1 -1
  132. package/dist/react/testing/index.js +1 -1
  133. package/dist/react/testing/index.js.map +1 -1
  134. package/dist/react/ui/index.d.ts +195 -1
  135. package/dist/react/ui/index.d.ts.map +1 -1
  136. package/dist/react/ui/index.js +64 -1
  137. package/dist/react/ui/index.js.map +1 -1
  138. package/dist/react/websocket/index.js.map +1 -1
  139. package/dist/redis/index.js.map +1 -1
  140. package/dist/scheduler/index.d.ts +1 -2
  141. package/dist/scheduler/index.d.ts.map +1 -1
  142. package/dist/scheduler/index.js +1 -1
  143. package/dist/scheduler/index.js.map +1 -1
  144. package/dist/scheduler/index.workerd.js +1 -1
  145. package/dist/scheduler/index.workerd.js.map +1 -1
  146. package/dist/security/index.browser.js.map +1 -1
  147. package/dist/security/index.d.ts.map +1 -1
  148. package/dist/security/index.js +2 -2
  149. package/dist/security/index.js.map +1 -1
  150. package/dist/server/auth/index.d.ts.map +1 -1
  151. package/dist/server/auth/index.js +24 -10
  152. package/dist/server/auth/index.js.map +1 -1
  153. package/dist/server/cookies/index.js.map +1 -1
  154. package/dist/server/core/index.browser.js +10 -3
  155. package/dist/server/core/index.browser.js.map +1 -1
  156. package/dist/server/core/index.d.ts +1 -4
  157. package/dist/server/core/index.d.ts.map +1 -1
  158. package/dist/server/core/index.js +47 -9
  159. package/dist/server/core/index.js.map +1 -1
  160. package/dist/server/links/index.browser.js.map +1 -1
  161. package/dist/server/links/index.js.map +1 -1
  162. package/dist/server/metrics/index.js +19 -1
  163. package/dist/server/metrics/index.js.map +1 -1
  164. package/dist/server/rate-limit/index.js.map +1 -1
  165. package/dist/server/static/index.js.map +1 -1
  166. package/dist/server/swagger/index.d.ts.map +1 -1
  167. package/dist/server/swagger/index.js +4 -5
  168. package/dist/server/swagger/index.js.map +1 -1
  169. package/dist/sms/index.js.map +1 -1
  170. package/dist/system/index.browser.js.map +1 -1
  171. package/dist/system/index.js.map +1 -1
  172. package/dist/system/index.workerd.js.map +1 -1
  173. package/dist/topic/core/index.js.map +1 -1
  174. package/dist/websocket/index.browser.js +32 -5
  175. package/dist/websocket/index.browser.js.map +1 -1
  176. package/dist/websocket/index.d.ts +3 -1
  177. package/dist/websocket/index.d.ts.map +1 -1
  178. package/dist/websocket/index.js +42 -6
  179. package/dist/websocket/index.js.map +1 -1
  180. package/package.json +685 -274
  181. package/src/api/files/__tests__/FileController.spec.ts +1 -1
  182. package/src/api/jobs/__tests__/$job.spec.ts +5 -1
  183. package/src/api/parameters/services/ParameterProvider.ts +21 -4
  184. package/src/api/users/__tests__/SessionService.spec.ts +99 -0
  185. package/src/api/users/__tests__/UserJobs.spec.ts +67 -0
  186. package/src/api/users/atoms/realmAuthSettingsAtom.ts +15 -0
  187. package/src/api/users/entities/sessions.ts +6 -0
  188. package/src/api/users/jobs/UserJobs.ts +44 -17
  189. package/src/api/users/providers/RealmProvider.ts +4 -0
  190. package/src/api/users/schemas/userQuerySchema.ts +0 -1
  191. package/src/api/users/services/SessionService.ts +27 -0
  192. package/src/api/users/services/UserService.ts +1 -5
  193. package/src/api/verifications/__tests__/CodeVerification.spec.ts +14 -0
  194. package/src/api/verifications/__tests__/LinkVerification.spec.ts +14 -0
  195. package/src/api/verifications/services/VerificationService.ts +1 -0
  196. package/src/bucket/__tests__/NodeS3BucketProvider.spec.ts +74 -0
  197. package/src/bucket/index.ts +19 -2
  198. package/src/bucket/primitives/$bucket.ts +9 -1
  199. package/src/bucket/providers/CloudflareR2Provider.ts +2 -137
  200. package/src/bucket/providers/NodeS3BucketProvider.ts +218 -0
  201. package/src/cache/core/index.ts +29 -0
  202. package/src/cache/core/primitives/$cache.ts +14 -1
  203. package/src/cli/config/defineConfig.ts +13 -15
  204. package/src/cli/core/__tests__/init.spec.ts +214 -7
  205. package/src/cli/core/commands/init.ts +12 -0
  206. package/src/cli/core/services/PackageManagerUtils.ts +23 -6
  207. package/src/cli/core/services/ProjectScaffolder.ts +315 -33
  208. package/src/cli/core/tasks/BuildCloudflareTask.ts +5 -0
  209. package/src/cli/core/tasks/BuildDockerTask.ts +9 -10
  210. package/src/cli/core/tasks/BuildServerTask.ts +8 -0
  211. package/src/cli/core/templates/agentMd.ts +2 -10
  212. package/src/cli/core/templates/apiIndexTs.ts +23 -1
  213. package/src/cli/core/templates/componentsJsonTs.ts +39 -0
  214. package/src/cli/core/templates/mainCss.ts +1 -0
  215. package/src/cli/core/templates/saasAdminLayoutTsx.ts +77 -0
  216. package/src/cli/core/templates/saasAdminPagesTsx.ts +26 -0
  217. package/src/cli/core/templates/saasAuthLayoutTsx.ts +20 -0
  218. package/src/cli/core/templates/saasAuthPagesTsx.ts +62 -0
  219. package/src/cli/core/templates/saasRealmProviderTs.ts +46 -0
  220. package/src/cli/core/templates/webAppRouterTs.ts +104 -1
  221. package/src/cli/core/templates/webIndexTs.ts +23 -1
  222. package/src/cli/devtools/index.ts +12 -26
  223. package/src/cli/platform/__tests__/SecretsCommand.spec.ts +2 -0
  224. package/src/cli/platform/index.ts +15 -24
  225. package/src/cli/vendor/atoms/vendorOptions.ts +1 -1
  226. package/src/cli/vendor/index.ts +14 -23
  227. package/src/command/providers/CliProvider.ts +1 -1
  228. package/src/core/Alepha.ts +11 -1
  229. package/src/core/helpers/ref.ts +18 -0
  230. package/src/core/index.shared.ts +1 -0
  231. package/src/core/interfaces/Service.ts +3 -1
  232. package/src/core/providers/SchemaValidator.ts +9 -1
  233. package/src/core/providers/TypeProvider.ts +2 -3
  234. package/src/datetime/REFACTORING.md +118 -0
  235. package/src/datetime/providers/DateTimeProvider.ts +203 -24
  236. package/src/lock/core/index.ts +31 -0
  237. package/src/lock/core/primitives/$lock.ts +14 -1
  238. package/src/logger/services/Logger.ts +1 -1
  239. package/src/mcp/__tests__/$resource.spec.ts +1 -1
  240. package/src/mcp/__tests__/$tool.spec.ts +1 -1
  241. package/src/mcp/__tests__/McpServerProvider.spec.ts +1 -1
  242. package/src/mcp/__tests__/jsonrpc.spec.ts +1 -1
  243. package/src/mcp/helpers/jsonrpc.ts +26 -1
  244. package/src/mcp/index.ts +10 -5
  245. package/src/mcp/interfaces/McpTypes.ts +83 -6
  246. package/src/mcp/primitives/$prompt.ts +18 -1
  247. package/src/mcp/primitives/$resource.ts +18 -1
  248. package/src/mcp/primitives/$tool.ts +83 -7
  249. package/src/mcp/providers/McpServerProvider.ts +74 -16
  250. package/src/mcp/transports/StreamableHttpMcpTransport.ts +226 -0
  251. package/src/orm/REFACTORING.md +330 -0
  252. package/src/orm/__tests__/$repository-tests.ts +1 -0
  253. package/src/orm/__tests__/orm-next-tests.ts +2 -67
  254. package/src/orm/__tests__/orm-next.spec.ts +0 -21
  255. package/src/orm/core/index.shared.ts +0 -2
  256. package/src/orm/core/index.ts +1 -2
  257. package/src/orm/core/primitives/$repository.ts +3 -6
  258. package/src/orm/core/primitives/$transactional.ts +11 -0
  259. package/src/orm/core/providers/drivers/DatabaseProvider.ts +0 -5
  260. package/src/orm/core/providers/drivers/NodeSqliteProvider.ts +11 -13
  261. package/src/orm/core/schemas/updateSchema.ts +1 -1
  262. package/src/orm/core/services/ModelBuilder.ts +1 -13
  263. package/src/orm/core/services/PgRelationManager.ts +4 -2
  264. package/src/orm/core/services/Repository.ts +1 -42
  265. package/src/orm/core/services/SqliteModelBuilder.ts +2 -33
  266. package/src/orm/postgres/services/PostgresModelBuilder.ts +10 -45
  267. package/src/react/core/__tests__/useQuery.browser.spec.tsx +86 -0
  268. package/src/react/core/hooks/useQuery.ts +153 -0
  269. package/src/react/core/index.ts +1 -0
  270. package/src/react/form/services/FormModel.ts +15 -6
  271. package/src/react/form/services/parseField.ts +8 -0
  272. package/src/react/i18n/providers/I18nProvider.ts +8 -2
  273. package/src/react/intro/components/GettingStartedAuthSlide.tsx +11 -4
  274. package/src/react/router/__tests__/$page.spec.tsx +0 -16
  275. package/src/react/router/__tests__/ReactBrowserProvider.browser.spec.ts +213 -2
  276. package/src/react/router/__tests__/ssr.spec.tsx +339 -0
  277. package/src/react/router/primitives/$page.ts +28 -4
  278. package/src/react/router/providers/ReactBrowserProvider.ts +73 -0
  279. package/src/react/router/providers/ReactBrowserRouterProvider.ts +1 -1
  280. package/src/react/router/providers/ReactPageProvider.ts +27 -9
  281. package/src/react/router/providers/ReactPreloadProvider.ts +1 -1
  282. package/src/react/router/providers/ReactServerProvider.ts +1 -0
  283. package/src/react/ui/atoms/uiThemeListAtom.ts +36 -0
  284. package/src/react/ui/index.ts +6 -0
  285. package/src/react/ui/services/SchemaControl.ts +209 -0
  286. package/src/scheduler/providers/CronProvider.ts +1 -1
  287. package/src/security/primitives/$basicAuth.ts +1 -1
  288. package/src/security/primitives/$issuer.ts +6 -3
  289. package/src/server/auth/providers/ServerAuthProvider.ts +5 -1
  290. package/src/server/core/__tests__/ServerRouterProvider-serializationError.spec.ts +75 -0
  291. package/src/server/core/__tests__/ServerRouterProvider-validationError.spec.ts +306 -0
  292. package/src/server/core/errors/ValidationError.ts +13 -1
  293. package/src/server/core/interfaces/ServerRequest.ts +1 -0
  294. package/src/server/core/primitives/$action.ts +16 -5
  295. package/src/server/core/providers/ServerProvider.ts +1 -1
  296. package/src/server/core/providers/ServerRouterProvider.ts +28 -6
  297. package/src/server/core/services/HttpClient.ts +1 -1
  298. package/src/server/swagger/providers/ServerSwaggerProvider.ts +6 -8
  299. package/src/websocket/providers/NodeWebSocketServerProvider.ts +10 -4
  300. package/src/websocket/services/WebSocketClient.ts +11 -5
  301. package/src/mcp/transports/SseMcpTransport.ts +0 -182
  302. package/src/orm/core/__tests__/parseQueryString.spec.ts +0 -196
  303. package/src/orm/core/helpers/parseQueryString.ts +0 -502
  304. package/src/orm/core/primitives/$view.ts +0 -88
@@ -256,7 +256,7 @@ export class ActionPrimitive<
256
256
 
257
257
  public get route(): ServerRoute {
258
258
  return {
259
- ...(this.options as any), // TODO: fix schema.header mapping
259
+ ...this.options,
260
260
  method: this.method,
261
261
  path: `${this.prefix}${this.path}`,
262
262
  handler: this.handler,
@@ -399,10 +399,21 @@ export class ActionPrimitive<
399
399
  }
400
400
 
401
401
  if (serverActionRequest.headers && this.options.schema?.headers) {
402
- serverActionRequest.headers = this.alepha.codec.encode(
403
- this.options.schema.headers,
404
- serverActionRequest.headers,
405
- ) as Record<string, any>;
402
+ // Per-key encode (matches the server-side decode pattern in
403
+ // ServerRouterProvider.validateRequest): coerces declared headers via
404
+ // the schema, leaves undeclared ones untouched. Schema keys are
405
+ // lowercased to match Node's incoming header convention.
406
+ const schemaHeaders = this.options.schema.headers;
407
+ const headers = serverActionRequest.headers as Record<string, unknown>;
408
+ for (const key of Object.keys(schemaHeaders.properties)) {
409
+ const lcKey = key.toLowerCase();
410
+ if (headers[lcKey] !== undefined) {
411
+ headers[lcKey] = this.alepha.codec.encode(
412
+ schemaHeaders.properties[key],
413
+ headers[lcKey],
414
+ );
415
+ }
416
+ }
406
417
  }
407
418
 
408
419
  if (serverActionRequest.body && this.options.schema?.body) {
@@ -411,7 +411,7 @@ export class ServerProvider {
411
411
 
412
412
  url = url?.split("?")[0];
413
413
 
414
- if (!!params?.["*"] && `/${params?.["*"]}` === url) {
414
+ if (params?.["*"] && `/${params?.["*"]}` === url) {
415
415
  return true;
416
416
  }
417
417
  }
@@ -319,7 +319,7 @@ export class ServerRouterProvider extends RouterProvider<ServerRouteMatcher> {
319
319
  }
320
320
 
321
321
  if (reply.body == null || responseKind === "void") {
322
- delete headers["content-type"];
322
+ delete (headers as Record<string, unknown>)["content-type"];
323
323
  reply.body = undefined;
324
324
  return;
325
325
  }
@@ -416,7 +416,7 @@ export class ServerRouterProvider extends RouterProvider<ServerRouteMatcher> {
416
416
  if (
417
417
  "status" in error &&
418
418
  typeof error.status === "number" &&
419
- !!errorNameByStatus[error.status]
419
+ errorNameByStatus[error.status]
420
420
  ) {
421
421
  const status = error.status;
422
422
  reply.status = status;
@@ -484,10 +484,32 @@ export class ServerRouterProvider extends RouterProvider<ServerRouteMatcher> {
484
484
 
485
485
  if (route.schema?.headers) {
486
486
  try {
487
- request.headers = this.alepha.codec.validate(
488
- route.schema.headers,
489
- request.headers,
490
- ) as any;
487
+ const schemaHeaders = route.schema.headers;
488
+
489
+ // Per-key decode (mirrors `query` handling): coerces declared header
490
+ // values from strings to their schema types (int/bool/date). Then
491
+ // validate the decoded subset against the full schema so TypeBox
492
+ // produces consistent error messages (missing-required, type
493
+ // mismatch). Finally merge the decoded values back into
494
+ // `request.headers` so undeclared headers (auth, cookie, user-agent,
495
+ // ...) survive the validation step intact.
496
+ const decoded: Record<string, unknown> = {};
497
+ for (const key of Object.keys(schemaHeaders.properties)) {
498
+ const lcKey = key.toLowerCase();
499
+ const value = request.headers[lcKey];
500
+ if (value == null) continue;
501
+ decoded[key] = this.alepha.codec.decode(
502
+ schemaHeaders.properties[key],
503
+ value,
504
+ );
505
+ }
506
+
507
+ this.alepha.codec.validate(schemaHeaders, decoded);
508
+
509
+ for (const [key, value] of Object.entries(decoded)) {
510
+ (request.headers as Record<string, unknown>)[key.toLowerCase()] =
511
+ value;
512
+ }
491
513
  } catch (error) {
492
514
  throw new ValidationError("Invalid request header", error);
493
515
  }
@@ -213,7 +213,7 @@ export class HttpClient {
213
213
 
214
214
  if (hasHeader || isMultipart(action)) {
215
215
  if (typeof init.headers === "object" && "content-type" in init.headers) {
216
- delete init.headers["content-type"]; // fetch() will fill this for us
216
+ delete (init.headers as Record<string, unknown>)["content-type"]; // fetch() will fill this for us
217
217
  }
218
218
 
219
219
  const formData = new FormData();
@@ -201,14 +201,12 @@ export class ServerSwaggerProvider {
201
201
  hasSecurity = true;
202
202
  }
203
203
 
204
- const g = t.raw;
205
-
206
204
  if (
207
- g.IsObject(route.options.schema.body) ||
208
- g.IsArray(route.options.schema.body)
205
+ t.schema.isObject(route.options.schema.body) ||
206
+ t.schema.isArray(route.options.schema.body)
209
207
  ) {
210
208
  if (
211
- g.IsObject(route.options.schema.body) &&
209
+ t.schema.isObject(route.options.schema.body) &&
212
210
  this.isBodyMultipart(route.options.schema.body)
213
211
  ) {
214
212
  operation.requestBody = {
@@ -231,7 +229,7 @@ export class ServerSwaggerProvider {
231
229
  }
232
230
  }
233
231
 
234
- if (g.IsObject(route.options.schema.query)) {
232
+ if (t.schema.isObject(route.options.schema.query)) {
235
233
  operation.parameters ??= [];
236
234
  const requiredKeys: string[] =
237
235
  route.options.schema.query.required ?? [];
@@ -250,7 +248,7 @@ export class ServerSwaggerProvider {
250
248
  }
251
249
  }
252
250
 
253
- if (g.IsObject(route.options.schema.params)) {
251
+ if (t.schema.isObject(route.options.schema.params)) {
254
252
  operation.parameters ??= [];
255
253
  for (const [key, value] of Object.entries(
256
254
  route.options.schema.params.properties,
@@ -260,7 +258,7 @@ export class ServerSwaggerProvider {
260
258
  ? value.description
261
259
  : undefined;
262
260
  const ref = schema(value);
263
- delete ref.description;
261
+ ref.description = undefined;
264
262
  const param: any = {
265
263
  name: key,
266
264
  in: "path",
@@ -6,9 +6,9 @@ import {
6
6
  $state,
7
7
  Alepha,
8
8
  AlephaError,
9
+ SchemaValidator,
9
10
  type Static,
10
11
  t,
11
- Value,
12
12
  } from "alepha";
13
13
  import { $logger } from "alepha/logger";
14
14
  import { WebSocket, WebSocketServer } from "ws";
@@ -495,6 +495,7 @@ export class NodeWebSocketServerProvider extends WebSocketServerProvider {
495
495
 
496
496
  export class NodeWebSocketConnection implements WebSocketConnection {
497
497
  protected readonly log = $logger();
498
+ protected readonly schemaValidator = $inject(SchemaValidator);
498
499
  public metadata?: Record<string, any>;
499
500
 
500
501
  constructor(
@@ -556,10 +557,15 @@ export class NodeWebSocketConnection implements WebSocketConnection {
556
557
 
557
558
  // Validate message against schema (out = client→server)
558
559
  const outSchema = this.endpoint.channel.options.schema.out;
559
- if (!Value.Check(outSchema, message)) {
560
- const errors = Array.from(Value.Errors(outSchema, message));
560
+ try {
561
+ this.schemaValidator.validate(outSchema, message, {
562
+ trim: false,
563
+ nullToUndefined: false,
564
+ deleteUndefined: false,
565
+ });
566
+ } catch (err) {
561
567
  throw new WebSocketValidationError(
562
- `Message validation failed: ${errors.map((e: any) => e.message).join(", ")}`,
568
+ `Message validation failed: ${(err as Error).message}`,
563
569
  );
564
570
  }
565
571
 
@@ -3,9 +3,9 @@ import {
3
3
  $inject,
4
4
  Alepha,
5
5
  AlephaError,
6
+ SchemaValidator,
6
7
  type Static,
7
8
  t,
8
- Value,
9
9
  } from "alepha";
10
10
  import { $logger } from "alepha/logger";
11
11
  import type { ChannelPrimitive, TWSObject } from "../primitives/$channel.ts";
@@ -50,6 +50,7 @@ export class WebSocketChannelConnection<
50
50
  TServer extends TWSObject,
51
51
  > {
52
52
  protected readonly alepha = $inject(Alepha);
53
+ protected readonly schemaValidator = $inject(SchemaValidator);
53
54
  protected readonly log = $logger();
54
55
  protected ws?: WebSocket;
55
56
  protected reconnectAttempts = 0;
@@ -346,11 +347,16 @@ export class WebSocketChannelConnection<
346
347
 
347
348
  // Validate outgoing message against schema
348
349
  const outSchema = this.channel.options.schema.out;
349
- if (!Value.Check(outSchema, message)) {
350
- const errors = Array.from(Value.Errors(outSchema, message));
351
- this.log.warn("Message validation failed", { errors });
350
+ try {
351
+ this.schemaValidator.validate(outSchema, message, {
352
+ trim: false,
353
+ nullToUndefined: false,
354
+ deleteUndefined: false,
355
+ });
356
+ } catch (err) {
357
+ this.log.warn("Message validation failed", { error: err });
352
358
  throw new AlephaError(
353
- `Message validation failed: ${errors.map((e) => e.message).join(", ")}`,
359
+ `Message validation failed: ${(err as Error).message}`,
354
360
  );
355
361
  }
356
362
 
@@ -1,182 +0,0 @@
1
- import { $atom, $inject, $state, t } from "alepha";
2
- import { $logger } from "alepha/logger";
3
- import { $route } from "alepha/server";
4
- import {
5
- createErrorResponse,
6
- createNotification,
7
- createParseError,
8
- JsonRpcParseError,
9
- parseMessage,
10
- } from "../helpers/jsonrpc.ts";
11
- import type { McpContext } from "../interfaces/McpTypes.ts";
12
- import { McpServerProvider } from "../providers/McpServerProvider.ts";
13
-
14
- // ---------------------------------------------------------------------------------------------------------------------
15
-
16
- export const mcpSseOptions = $atom({
17
- name: "alepha.mcp.sse.options",
18
- description: "Configuration options for the MCP SSE transport.",
19
- schema: t.object({
20
- /**
21
- * Path for the MCP SSE endpoint.
22
- */
23
- path: t.text({ default: "/mcp" }),
24
- }),
25
- default: {
26
- path: "/mcp",
27
- },
28
- });
29
-
30
- // ---------------------------------------------------------------------------------------------------------------------
31
-
32
- /**
33
- * SSE (Server-Sent Events) transport for MCP communication.
34
- *
35
- * This transport uses HTTP with SSE for server-to-client messages
36
- * and POST requests for client-to-server messages.
37
- *
38
- * Endpoints:
39
- * - GET /mcp - SSE stream for server events
40
- * - POST /mcp - JSON-RPC request endpoint
41
- *
42
- * @example
43
- * ```ts
44
- * import { Alepha, run } from "alepha";
45
- * import { AlephaServer } from "alepha/server";
46
- * import { AlephaMcp, AlephaMcpSse } from "alepha/mcp";
47
- *
48
- * class MyTools {
49
- * // ... tool definitions
50
- * }
51
- *
52
- * run(
53
- * Alepha.create()
54
- * .with(AlephaServer)
55
- * .with(AlephaMcp)
56
- * .with(AlephaMcpSse)
57
- * .with(MyTools)
58
- * );
59
- * ```
60
- */
61
- export class SseMcpTransport {
62
- protected readonly log = $logger();
63
- protected readonly options = $state(mcpSseOptions);
64
- protected readonly mcpServer = $inject(McpServerProvider);
65
-
66
- /**
67
- * SSE endpoint for server-to-client messages.
68
- *
69
- * Returns a text/event-stream response with server capabilities
70
- * and keeps the connection open for notifications.
71
- */
72
- sse = $route({
73
- method: "GET",
74
- path: this.options.path,
75
- handler: async (request) => {
76
- this.log.debug("MCP SSE connection established");
77
-
78
- const encoder = new TextEncoder();
79
-
80
- // Create SSE stream
81
- const stream = new ReadableStream({
82
- start: (controller) => {
83
- // Send initial endpoint info
84
- const endpointEvent = this.formatSseEvent(
85
- "endpoint",
86
- `${this.options.path}`,
87
- );
88
- controller.enqueue(encoder.encode(endpointEvent));
89
-
90
- // Send capabilities notification
91
- const capabilitiesNotification = createNotification(
92
- "notifications/capabilities",
93
- { capabilities: this.mcpServer.getCapabilities() },
94
- );
95
- const capabilitiesEvent = this.formatSseEvent(
96
- "message",
97
- JSON.stringify(capabilitiesNotification),
98
- );
99
- controller.enqueue(encoder.encode(capabilitiesEvent));
100
- },
101
- cancel: () => {
102
- this.log.debug("MCP SSE connection closed");
103
- },
104
- });
105
-
106
- request.reply.status = 200;
107
- request.reply.headers = {
108
- "content-type": "text/event-stream",
109
- "cache-control": "no-cache",
110
- connection: "keep-alive",
111
- };
112
- request.reply.body = stream;
113
- },
114
- });
115
-
116
- /**
117
- * POST endpoint for client-to-server JSON-RPC messages.
118
- */
119
- message = $route({
120
- method: "POST",
121
- path: this.options.path,
122
- schema: {
123
- body: t.json(),
124
- },
125
- handler: async (request) => {
126
- try {
127
- const body =
128
- typeof request.body === "string"
129
- ? request.body
130
- : JSON.stringify(request.body);
131
-
132
- this.log.debug("MCP request body", {
133
- body,
134
- bodyType: typeof request.body,
135
- });
136
-
137
- const rpcRequest = parseMessage(body);
138
-
139
- // Build context from request headers
140
- const headers = { ...request.headers } as Record<
141
- string,
142
- string | string[] | undefined
143
- >;
144
-
145
- const context: McpContext = { headers };
146
-
147
- const response = await this.mcpServer.handleMessage(
148
- rpcRequest,
149
- context,
150
- );
151
-
152
- if (response) {
153
- request.reply.headers["content-type"] = "application/json";
154
- request.reply.body = JSON.stringify(response);
155
- } else {
156
- request.reply.status = 204;
157
- }
158
- } catch (error) {
159
- if (error instanceof JsonRpcParseError) {
160
- request.reply.status = 400;
161
- request.reply.headers["content-type"] = "application/json";
162
- request.reply.body = JSON.stringify(
163
- createErrorResponse(0, createParseError(error.message)),
164
- );
165
- } else {
166
- this.log.error("Failed to process MCP message", error);
167
- request.reply.status = 500;
168
- request.reply.body = JSON.stringify({
169
- error: (error as Error).message,
170
- });
171
- }
172
- }
173
- },
174
- });
175
-
176
- /**
177
- * Format a message as an SSE event.
178
- */
179
- protected formatSseEvent(event: string, data: string): string {
180
- return `event: ${event}\ndata: ${data}\n\n`;
181
- }
182
- }
@@ -1,196 +0,0 @@
1
- import { describe, test } from "vitest";
2
- import { buildQueryString, parseQueryString } from "../index.ts";
3
-
4
- describe("parseQueryString with wildcard patterns", () => {
5
- test("should parse wildcard patterns with = operator", ({ expect }) => {
6
- // Test startsWith pattern
7
- const result1 = parseQueryString("name=John*");
8
- expect(result1).toEqual({ name: { startsWith: "John" } });
9
-
10
- // Test endsWith pattern
11
- const result2 = parseQueryString("name=*Smith");
12
- expect(result2).toEqual({ name: { endsWith: "Smith" } });
13
-
14
- // Test contains pattern
15
- const result3 = parseQueryString("name=*oh*");
16
- expect(result3).toEqual({ name: { contains: "oh" } });
17
-
18
- // Test literal asterisk in the middle (not at beginning or end)
19
- const result4 = parseQueryString("name=Jo*hn");
20
- expect(result4).toEqual({ name: { eq: "Jo*hn" } });
21
-
22
- // Test literal equals (no wildcards)
23
- const result5 = parseQueryString("name=John");
24
- expect(result5).toEqual({ name: { eq: "John" } });
25
- });
26
-
27
- test("should handle wildcard patterns in complex queries", ({ expect }) => {
28
- // Multiple conditions with wildcards
29
- const result1 = parseQueryString("name=*John&email=*@example.com");
30
- expect(result1).toEqual({
31
- and: [
32
- { name: { endsWith: "John" } },
33
- { email: { endsWith: "@example.com" } },
34
- ],
35
- });
36
-
37
- // OR conditions with wildcards
38
- const result2 = parseQueryString("name=John*|name=*Smith");
39
- expect(result2).toEqual({
40
- or: [{ name: { startsWith: "John" } }, { name: { endsWith: "Smith" } }],
41
- });
42
-
43
- // Nested conditions with wildcards
44
- const result3 = parseQueryString("(name=*John*|email=admin*)&age>18");
45
- expect(result3).toEqual({
46
- and: [
47
- {
48
- or: [
49
- { name: { contains: "John" } },
50
- { email: { startsWith: "admin" } },
51
- ],
52
- },
53
- { age: { gt: 18 } },
54
- ],
55
- });
56
- });
57
-
58
- test("should handle wildcard patterns in quoted strings", ({ expect }) => {
59
- // Quoted string with wildcards should be treated as wildcards
60
- const result1 = parseQueryString('name="*John*"');
61
- expect(result1).toEqual({ name: { contains: "John" } });
62
-
63
- // Quoted string with asterisk in middle
64
- const result2 = parseQueryString('name="Jo*hn"');
65
- expect(result2).toEqual({ name: { eq: "Jo*hn" } });
66
-
67
- // Single quotes
68
- const result3 = parseQueryString("name='*Smith'");
69
- expect(result3).toEqual({ name: { endsWith: "Smith" } });
70
- });
71
-
72
- test("should handle wildcard patterns with special characters", ({
73
- expect,
74
- }) => {
75
- // Wildcard with spaces
76
- const result1 = parseQueryString("name=*John Smith*");
77
- expect(result1).toEqual({ name: { contains: "John Smith" } });
78
-
79
- // Wildcard with special characters
80
- const result2 = parseQueryString("email=*@example.com");
81
- expect(result2).toEqual({ email: { endsWith: "@example.com" } });
82
-
83
- // Wildcard with numbers
84
- const result3 = parseQueryString("code=ABC*123");
85
- expect(result3).toEqual({ code: { eq: "ABC*123" } }); // Asterisk in middle is literal
86
- });
87
-
88
- test("should handle wildcard patterns in arrays", ({ expect }) => {
89
- // Array values don't support wildcards (treated as literal)
90
- const result = parseQueryString("status=[active*,*pending,*idle*]");
91
- expect(result).toEqual({
92
- status: { inArray: ["active*", "*pending", "*idle*"] },
93
- });
94
- });
95
-
96
- test("should handle wildcard patterns with != operator", ({ expect }) => {
97
- // != operator doesn't support wildcards, treats as literal
98
- const result1 = parseQueryString("name!=*John");
99
- expect(result1).toEqual({ name: { ne: "*John" } });
100
-
101
- const result2 = parseQueryString("name!=John*");
102
- expect(result2).toEqual({ name: { ne: "John*" } });
103
- });
104
-
105
- test("should handle empty wildcard patterns", ({ expect }) => {
106
- // Just asterisks - a single asterisk at the end means "starts with nothing" = everything
107
- const result1 = parseQueryString("name=*");
108
- expect(result1).toEqual({ name: { contains: "" } }); // Since * alone has both start and end asterisk
109
-
110
- const result2 = parseQueryString("name=**");
111
- expect(result2).toEqual({ name: { contains: "" } });
112
-
113
- const result3 = parseQueryString("name=***");
114
- expect(result3).toEqual({ name: { contains: "*" } });
115
- });
116
-
117
- test("should handle JSONB nested queries with wildcards", ({ expect }) => {
118
- const result = parseQueryString("profile.city=*Paris*&profile.name=John*");
119
- expect(result).toEqual({
120
- and: [
121
- { profile: { city: { contains: "Paris" } } },
122
- { profile: { name: { startsWith: "John" } } },
123
- ],
124
- });
125
- });
126
-
127
- test("should not apply wildcards to non-string operators", ({ expect }) => {
128
- // Numeric comparison operators don't use wildcards
129
- const result1 = parseQueryString("age>18");
130
- expect(result1).toEqual({ age: { gt: 18 } });
131
-
132
- // NULL checks don't use wildcards
133
- const result2 = parseQueryString("name=null");
134
- expect(result2).toEqual({ name: { isNull: true } });
135
-
136
- const result3 = parseQueryString("name!=null");
137
- expect(result3).toEqual({ name: { isNotNull: true } });
138
- });
139
- });
140
-
141
- describe("buildQueryString with wildcard patterns", () => {
142
- test("should build query strings from wildcard conditions", ({ expect }) => {
143
- // startsWith
144
- const query1 = buildQueryString({ name: { startsWith: "John" } });
145
- expect(query1).toBe("name=John*");
146
-
147
- // endsWith
148
- const query2 = buildQueryString({ name: { endsWith: "Smith" } });
149
- expect(query2).toBe("name=*Smith");
150
-
151
- // contains
152
- const query3 = buildQueryString({ name: { contains: "oh" } });
153
- expect(query3).toBe("name=*oh*");
154
- });
155
-
156
- test("should build complex query strings with wildcards", ({ expect }) => {
157
- const query = buildQueryString({
158
- and: [
159
- { name: { startsWith: "John" } },
160
- { email: { endsWith: "@example.com" } },
161
- { bio: { contains: "developer" } },
162
- ],
163
- });
164
- expect(query).toBe("name=John*&email=*@example.com&bio=*developer*");
165
- });
166
-
167
- test("should handle OR conditions with wildcards", ({ expect }) => {
168
- const query = buildQueryString({
169
- or: [
170
- { name: { startsWith: "Admin" } },
171
- { role: { contains: "manager" } },
172
- ],
173
- });
174
- expect(query).toBe("(name=Admin*|role=*manager*)");
175
- });
176
-
177
- test("round-trip conversion should preserve wildcard semantics", ({
178
- expect,
179
- }) => {
180
- const testCases = [
181
- "name=John*",
182
- "name=*Smith",
183
- "name=*John*",
184
- "email=admin*&role=*manager*",
185
- "(name=*John*|name=*Jane*)&active=true",
186
- "profile.city=*Paris*",
187
- ];
188
-
189
- for (const original of testCases) {
190
- const parsed = parseQueryString(original);
191
- const rebuilt = buildQueryString(parsed);
192
- const reparsed = parseQueryString(rebuilt);
193
- expect(reparsed).toEqual(parsed);
194
- }
195
- });
196
- });