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.
- package/README.md +0 -1
- package/assets/swagger-ui/swagger-ui-bundle.js +1 -1
- package/assets/swagger-ui/swagger-ui.css +1 -1
- package/dist/api/audits/index.browser.js +49 -0
- package/dist/api/audits/index.browser.js.map +1 -1
- package/dist/api/audits/index.js +49 -0
- package/dist/api/audits/index.js.map +1 -1
- package/dist/api/files/index.js.map +1 -1
- package/dist/api/jobs/index.d.ts +2 -61
- package/dist/api/jobs/index.d.ts.map +1 -1
- package/dist/api/jobs/index.js.map +1 -1
- package/dist/api/keys/index.d.ts +4 -4
- package/dist/api/keys/index.js.map +1 -1
- package/dist/api/notifications/index.d.ts +1 -10
- package/dist/api/notifications/index.d.ts.map +1 -1
- package/dist/api/parameters/index.browser.js +37 -0
- package/dist/api/parameters/index.browser.js.map +1 -1
- package/dist/api/parameters/index.d.ts +12 -68
- package/dist/api/parameters/index.d.ts.map +1 -1
- package/dist/api/parameters/index.js +57 -4
- package/dist/api/parameters/index.js.map +1 -1
- package/dist/api/payments/index.js.map +1 -1
- package/dist/api/users/index.browser.js +6 -0
- package/dist/api/users/index.browser.js.map +1 -1
- package/dist/api/users/index.d.ts +148 -227
- package/dist/api/users/index.d.ts.map +1 -1
- package/dist/api/users/index.js +60 -14
- package/dist/api/users/index.js.map +1 -1
- package/dist/api/verifications/index.d.ts.map +1 -1
- package/dist/api/verifications/index.js +2 -1
- package/dist/api/verifications/index.js.map +1 -1
- package/dist/bucket/index.d.ts +77 -107
- package/dist/bucket/index.d.ts.map +1 -1
- package/dist/bucket/index.js +153 -5
- package/dist/bucket/index.js.map +1 -1
- package/dist/bucket/index.workerd.js +12 -2
- package/dist/bucket/index.workerd.js.map +1 -1
- package/dist/cache/core/index.d.ts +26 -0
- package/dist/cache/core/index.d.ts.map +1 -1
- package/dist/cache/core/index.js +11 -1
- package/dist/cache/core/index.js.map +1 -1
- package/dist/cache/core/index.workerd.js +11 -1
- package/dist/cache/core/index.workerd.js.map +1 -1
- package/dist/captcha/index.js.map +1 -1
- package/dist/cli/config/index.d.ts +7 -5
- package/dist/cli/config/index.d.ts.map +1 -1
- package/dist/cli/config/index.js +2 -3
- package/dist/cli/config/index.js.map +1 -1
- package/dist/cli/core/index.d.ts +637 -11660
- package/dist/cli/core/index.d.ts.map +1 -1
- package/dist/cli/core/index.js +707 -532
- package/dist/cli/core/index.js.map +1 -1
- package/dist/cli/devtools/index.d.ts +4 -8
- package/dist/cli/devtools/index.d.ts.map +1 -1
- package/dist/cli/devtools/index.js +20 -16
- package/dist/cli/devtools/index.js.map +1 -1
- package/dist/cli/platform/index.d.ts +51 -77
- package/dist/cli/platform/index.d.ts.map +1 -1
- package/dist/cli/platform/index.js +65 -15
- package/dist/cli/platform/index.js.map +1 -1
- package/dist/cli/vendor/index.d.ts +10 -13
- package/dist/cli/vendor/index.d.ts.map +1 -1
- package/dist/cli/vendor/index.js +30 -12
- package/dist/cli/vendor/index.js.map +1 -1
- package/dist/command/index.js +1 -1
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +27 -3
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +8 -11
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +27 -3
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +27 -3
- package/dist/core/index.native.js.map +1 -1
- package/dist/core/index.workerd.js +27 -3
- package/dist/core/index.workerd.js.map +1 -1
- package/dist/crypto/index.js.map +1 -1
- package/dist/datetime/index.d.ts +69 -10
- package/dist/datetime/index.d.ts.map +1 -1
- package/dist/datetime/index.js +135 -13
- package/dist/datetime/index.js.map +1 -1
- package/dist/email/core/index.js.map +1 -1
- package/dist/email/smtp/index.js +130 -16
- package/dist/email/smtp/index.js.map +1 -1
- package/dist/fake/index.js.map +1 -1
- package/dist/lock/core/index.d.ts +30 -2
- package/dist/lock/core/index.d.ts.map +1 -1
- package/dist/lock/core/index.js +35 -12
- package/dist/lock/core/index.js.map +1 -1
- package/dist/lock/redis/index.js.map +1 -1
- package/dist/logger/index.js +32 -1
- package/dist/logger/index.js.map +1 -1
- package/dist/mcp/index.d.ts +238 -31
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +198 -67
- package/dist/mcp/index.js.map +1 -1
- package/dist/orm/core/index.browser.js +2 -362
- package/dist/orm/core/index.browser.js.map +1 -1
- package/dist/orm/core/index.bun.js +18 -409
- package/dist/orm/core/index.bun.js.map +1 -1
- package/dist/orm/core/index.d.ts +41 -194
- package/dist/orm/core/index.d.ts.map +1 -1
- package/dist/orm/core/index.js +27 -422
- package/dist/orm/core/index.js.map +1 -1
- package/dist/orm/postgres/index.bun.js +17 -20
- package/dist/orm/postgres/index.bun.js.map +1 -1
- package/dist/orm/postgres/index.d.ts +1 -5
- package/dist/orm/postgres/index.d.ts.map +1 -1
- package/dist/orm/postgres/index.js +17 -20
- package/dist/orm/postgres/index.js.map +1 -1
- package/dist/react/core/index.d.ts +102 -1
- package/dist/react/core/index.d.ts.map +1 -1
- package/dist/react/core/index.js +65 -1
- package/dist/react/core/index.js.map +1 -1
- package/dist/react/form/index.d.ts +6 -0
- package/dist/react/form/index.d.ts.map +1 -1
- package/dist/react/form/index.js +7 -7
- package/dist/react/form/index.js.map +1 -1
- package/dist/react/i18n/index.d.ts +7 -1
- package/dist/react/i18n/index.d.ts.map +1 -1
- package/dist/react/i18n/index.js +6 -0
- package/dist/react/i18n/index.js.map +1 -1
- package/dist/react/intro/index.js +22 -17
- package/dist/react/intro/index.js.map +1 -1
- package/dist/react/router/index.browser.js +98 -4
- package/dist/react/router/index.browser.js.map +1 -1
- package/dist/react/router/index.d.ts +58 -5
- package/dist/react/router/index.d.ts.map +1 -1
- package/dist/react/router/index.js +122 -6
- package/dist/react/router/index.js.map +1 -1
- package/dist/react/testing/{chunk-DBEY4PJZ.js → chunk-6Ep1yQYe.js} +1 -1
- package/dist/react/testing/index.js +1 -1
- package/dist/react/testing/index.js.map +1 -1
- package/dist/react/ui/index.d.ts +195 -1
- package/dist/react/ui/index.d.ts.map +1 -1
- package/dist/react/ui/index.js +64 -1
- package/dist/react/ui/index.js.map +1 -1
- package/dist/react/websocket/index.js.map +1 -1
- package/dist/redis/index.js.map +1 -1
- package/dist/scheduler/index.d.ts +1 -2
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/index.js +1 -1
- package/dist/scheduler/index.js.map +1 -1
- package/dist/scheduler/index.workerd.js +1 -1
- package/dist/scheduler/index.workerd.js.map +1 -1
- package/dist/security/index.browser.js.map +1 -1
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +2 -2
- package/dist/security/index.js.map +1 -1
- package/dist/server/auth/index.d.ts.map +1 -1
- package/dist/server/auth/index.js +24 -10
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cookies/index.js.map +1 -1
- package/dist/server/core/index.browser.js +10 -3
- package/dist/server/core/index.browser.js.map +1 -1
- package/dist/server/core/index.d.ts +1 -4
- package/dist/server/core/index.d.ts.map +1 -1
- package/dist/server/core/index.js +47 -9
- package/dist/server/core/index.js.map +1 -1
- package/dist/server/links/index.browser.js.map +1 -1
- package/dist/server/links/index.js.map +1 -1
- package/dist/server/metrics/index.js +19 -1
- package/dist/server/metrics/index.js.map +1 -1
- package/dist/server/rate-limit/index.js.map +1 -1
- package/dist/server/static/index.js.map +1 -1
- package/dist/server/swagger/index.d.ts.map +1 -1
- package/dist/server/swagger/index.js +4 -5
- package/dist/server/swagger/index.js.map +1 -1
- package/dist/sms/index.js.map +1 -1
- package/dist/system/index.browser.js.map +1 -1
- package/dist/system/index.js.map +1 -1
- package/dist/system/index.workerd.js.map +1 -1
- package/dist/topic/core/index.js.map +1 -1
- package/dist/websocket/index.browser.js +32 -5
- package/dist/websocket/index.browser.js.map +1 -1
- package/dist/websocket/index.d.ts +3 -1
- package/dist/websocket/index.d.ts.map +1 -1
- package/dist/websocket/index.js +42 -6
- package/dist/websocket/index.js.map +1 -1
- package/package.json +685 -274
- package/src/api/files/__tests__/FileController.spec.ts +1 -1
- package/src/api/jobs/__tests__/$job.spec.ts +5 -1
- package/src/api/parameters/services/ParameterProvider.ts +21 -4
- package/src/api/users/__tests__/SessionService.spec.ts +99 -0
- package/src/api/users/__tests__/UserJobs.spec.ts +67 -0
- package/src/api/users/atoms/realmAuthSettingsAtom.ts +15 -0
- package/src/api/users/entities/sessions.ts +6 -0
- package/src/api/users/jobs/UserJobs.ts +44 -17
- package/src/api/users/providers/RealmProvider.ts +4 -0
- package/src/api/users/schemas/userQuerySchema.ts +0 -1
- package/src/api/users/services/SessionService.ts +27 -0
- package/src/api/users/services/UserService.ts +1 -5
- package/src/api/verifications/__tests__/CodeVerification.spec.ts +14 -0
- package/src/api/verifications/__tests__/LinkVerification.spec.ts +14 -0
- package/src/api/verifications/services/VerificationService.ts +1 -0
- package/src/bucket/__tests__/NodeS3BucketProvider.spec.ts +74 -0
- package/src/bucket/index.ts +19 -2
- package/src/bucket/primitives/$bucket.ts +9 -1
- package/src/bucket/providers/CloudflareR2Provider.ts +2 -137
- package/src/bucket/providers/NodeS3BucketProvider.ts +218 -0
- package/src/cache/core/index.ts +29 -0
- package/src/cache/core/primitives/$cache.ts +14 -1
- package/src/cli/config/defineConfig.ts +13 -15
- package/src/cli/core/__tests__/init.spec.ts +214 -7
- package/src/cli/core/commands/init.ts +12 -0
- package/src/cli/core/services/PackageManagerUtils.ts +23 -6
- package/src/cli/core/services/ProjectScaffolder.ts +315 -33
- package/src/cli/core/tasks/BuildCloudflareTask.ts +5 -0
- package/src/cli/core/tasks/BuildDockerTask.ts +9 -10
- package/src/cli/core/tasks/BuildServerTask.ts +8 -0
- package/src/cli/core/templates/agentMd.ts +2 -10
- package/src/cli/core/templates/apiIndexTs.ts +23 -1
- package/src/cli/core/templates/componentsJsonTs.ts +39 -0
- package/src/cli/core/templates/mainCss.ts +1 -0
- package/src/cli/core/templates/saasAdminLayoutTsx.ts +77 -0
- package/src/cli/core/templates/saasAdminPagesTsx.ts +26 -0
- package/src/cli/core/templates/saasAuthLayoutTsx.ts +20 -0
- package/src/cli/core/templates/saasAuthPagesTsx.ts +62 -0
- package/src/cli/core/templates/saasRealmProviderTs.ts +46 -0
- package/src/cli/core/templates/webAppRouterTs.ts +104 -1
- package/src/cli/core/templates/webIndexTs.ts +23 -1
- package/src/cli/devtools/index.ts +12 -26
- package/src/cli/platform/__tests__/SecretsCommand.spec.ts +2 -0
- package/src/cli/platform/index.ts +15 -24
- package/src/cli/vendor/atoms/vendorOptions.ts +1 -1
- package/src/cli/vendor/index.ts +14 -23
- package/src/command/providers/CliProvider.ts +1 -1
- package/src/core/Alepha.ts +11 -1
- package/src/core/helpers/ref.ts +18 -0
- package/src/core/index.shared.ts +1 -0
- package/src/core/interfaces/Service.ts +3 -1
- package/src/core/providers/SchemaValidator.ts +9 -1
- package/src/core/providers/TypeProvider.ts +2 -3
- package/src/datetime/REFACTORING.md +118 -0
- package/src/datetime/providers/DateTimeProvider.ts +203 -24
- package/src/lock/core/index.ts +31 -0
- package/src/lock/core/primitives/$lock.ts +14 -1
- package/src/logger/services/Logger.ts +1 -1
- package/src/mcp/__tests__/$resource.spec.ts +1 -1
- package/src/mcp/__tests__/$tool.spec.ts +1 -1
- package/src/mcp/__tests__/McpServerProvider.spec.ts +1 -1
- package/src/mcp/__tests__/jsonrpc.spec.ts +1 -1
- package/src/mcp/helpers/jsonrpc.ts +26 -1
- package/src/mcp/index.ts +10 -5
- package/src/mcp/interfaces/McpTypes.ts +83 -6
- package/src/mcp/primitives/$prompt.ts +18 -1
- package/src/mcp/primitives/$resource.ts +18 -1
- package/src/mcp/primitives/$tool.ts +83 -7
- package/src/mcp/providers/McpServerProvider.ts +74 -16
- package/src/mcp/transports/StreamableHttpMcpTransport.ts +226 -0
- package/src/orm/REFACTORING.md +330 -0
- package/src/orm/__tests__/$repository-tests.ts +1 -0
- package/src/orm/__tests__/orm-next-tests.ts +2 -67
- package/src/orm/__tests__/orm-next.spec.ts +0 -21
- package/src/orm/core/index.shared.ts +0 -2
- package/src/orm/core/index.ts +1 -2
- package/src/orm/core/primitives/$repository.ts +3 -6
- package/src/orm/core/primitives/$transactional.ts +11 -0
- package/src/orm/core/providers/drivers/DatabaseProvider.ts +0 -5
- package/src/orm/core/providers/drivers/NodeSqliteProvider.ts +11 -13
- package/src/orm/core/schemas/updateSchema.ts +1 -1
- package/src/orm/core/services/ModelBuilder.ts +1 -13
- package/src/orm/core/services/PgRelationManager.ts +4 -2
- package/src/orm/core/services/Repository.ts +1 -42
- package/src/orm/core/services/SqliteModelBuilder.ts +2 -33
- package/src/orm/postgres/services/PostgresModelBuilder.ts +10 -45
- package/src/react/core/__tests__/useQuery.browser.spec.tsx +86 -0
- package/src/react/core/hooks/useQuery.ts +153 -0
- package/src/react/core/index.ts +1 -0
- package/src/react/form/services/FormModel.ts +15 -6
- package/src/react/form/services/parseField.ts +8 -0
- package/src/react/i18n/providers/I18nProvider.ts +8 -2
- package/src/react/intro/components/GettingStartedAuthSlide.tsx +11 -4
- package/src/react/router/__tests__/$page.spec.tsx +0 -16
- package/src/react/router/__tests__/ReactBrowserProvider.browser.spec.ts +213 -2
- package/src/react/router/__tests__/ssr.spec.tsx +339 -0
- package/src/react/router/primitives/$page.ts +28 -4
- package/src/react/router/providers/ReactBrowserProvider.ts +73 -0
- package/src/react/router/providers/ReactBrowserRouterProvider.ts +1 -1
- package/src/react/router/providers/ReactPageProvider.ts +27 -9
- package/src/react/router/providers/ReactPreloadProvider.ts +1 -1
- package/src/react/router/providers/ReactServerProvider.ts +1 -0
- package/src/react/ui/atoms/uiThemeListAtom.ts +36 -0
- package/src/react/ui/index.ts +6 -0
- package/src/react/ui/services/SchemaControl.ts +209 -0
- package/src/scheduler/providers/CronProvider.ts +1 -1
- package/src/security/primitives/$basicAuth.ts +1 -1
- package/src/security/primitives/$issuer.ts +6 -3
- package/src/server/auth/providers/ServerAuthProvider.ts +5 -1
- package/src/server/core/__tests__/ServerRouterProvider-serializationError.spec.ts +75 -0
- package/src/server/core/__tests__/ServerRouterProvider-validationError.spec.ts +306 -0
- package/src/server/core/errors/ValidationError.ts +13 -1
- package/src/server/core/interfaces/ServerRequest.ts +1 -0
- package/src/server/core/primitives/$action.ts +16 -5
- package/src/server/core/providers/ServerProvider.ts +1 -1
- package/src/server/core/providers/ServerRouterProvider.ts +28 -6
- package/src/server/core/services/HttpClient.ts +1 -1
- package/src/server/swagger/providers/ServerSwaggerProvider.ts +6 -8
- package/src/websocket/providers/NodeWebSocketServerProvider.ts +10 -4
- package/src/websocket/services/WebSocketClient.ts +11 -5
- package/src/mcp/transports/SseMcpTransport.ts +0 -182
- package/src/orm/core/__tests__/parseQueryString.spec.ts +0 -196
- package/src/orm/core/helpers/parseQueryString.ts +0 -502
- 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
|
-
...
|
|
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
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
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) {
|
|
@@ -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
|
-
|
|
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
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
)
|
|
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
|
-
|
|
208
|
-
|
|
205
|
+
t.schema.isObject(route.options.schema.body) ||
|
|
206
|
+
t.schema.isArray(route.options.schema.body)
|
|
209
207
|
) {
|
|
210
208
|
if (
|
|
211
|
-
|
|
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 (
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
560
|
-
|
|
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: ${
|
|
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
|
-
|
|
350
|
-
|
|
351
|
-
|
|
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: ${
|
|
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
|
-
});
|