@cinnabun/core 0.0.1 → 0.0.2
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/dist/__tests__/autowired.test.d.ts +1 -0
- package/dist/__tests__/autowired.test.js +109 -0
- package/dist/__tests__/autowired.test.js.map +1 -0
- package/dist/__tests__/cinnabun-application.test.d.ts +1 -0
- package/dist/__tests__/cinnabun-application.test.js +96 -0
- package/dist/__tests__/cinnabun-application.test.js.map +1 -0
- package/dist/__tests__/cinnabun-factory.test.d.ts +1 -0
- package/dist/__tests__/cinnabun-factory.test.js +269 -0
- package/dist/__tests__/cinnabun-factory.test.js.map +1 -0
- package/dist/__tests__/circular-dependency.test.d.ts +1 -0
- package/dist/__tests__/circular-dependency.test.js +318 -0
- package/dist/__tests__/circular-dependency.test.js.map +1 -0
- package/dist/__tests__/compression.test.d.ts +1 -0
- package/dist/__tests__/compression.test.js +459 -0
- package/dist/__tests__/compression.test.js.map +1 -0
- package/dist/__tests__/config.test.d.ts +1 -0
- package/dist/__tests__/config.test.js +86 -0
- package/dist/__tests__/config.test.js.map +1 -0
- package/dist/__tests__/cors.test.d.ts +1 -0
- package/dist/__tests__/cors.test.js +575 -0
- package/dist/__tests__/cors.test.js.map +1 -0
- package/dist/__tests__/env-config.test.d.ts +1 -0
- package/dist/__tests__/env-config.test.js +367 -0
- package/dist/__tests__/env-config.test.js.map +1 -0
- package/dist/__tests__/exception.test.d.ts +1 -0
- package/dist/__tests__/exception.test.js +207 -0
- package/dist/__tests__/exception.test.js.map +1 -0
- package/dist/__tests__/guards-interceptors.test.d.ts +1 -0
- package/dist/__tests__/guards-interceptors.test.js +660 -0
- package/dist/__tests__/guards-interceptors.test.js.map +1 -0
- package/dist/__tests__/health-check.test.d.ts +1 -0
- package/dist/__tests__/health-check.test.js +240 -0
- package/dist/__tests__/health-check.test.js.map +1 -0
- package/dist/__tests__/http.test.d.ts +1 -0
- package/dist/__tests__/http.test.js +629 -0
- package/dist/__tests__/http.test.js.map +1 -0
- package/dist/__tests__/integration/e2e.test.d.ts +1 -0
- package/dist/__tests__/integration/e2e.test.js +192 -0
- package/dist/__tests__/integration/e2e.test.js.map +1 -0
- package/dist/__tests__/integration/performance.bench.d.ts +1 -0
- package/dist/__tests__/integration/performance.bench.js +129 -0
- package/dist/__tests__/integration/performance.bench.js.map +1 -0
- package/dist/__tests__/integration/validation.test.d.ts +1 -0
- package/dist/__tests__/integration/validation.test.js +133 -0
- package/dist/__tests__/integration/validation.test.js.map +1 -0
- package/dist/__tests__/lifecycle-management.test.d.ts +1 -0
- package/dist/__tests__/lifecycle-management.test.js +688 -0
- package/dist/__tests__/lifecycle-management.test.js.map +1 -0
- package/dist/__tests__/lifecycle.test.d.ts +1 -0
- package/dist/__tests__/lifecycle.test.js +196 -0
- package/dist/__tests__/lifecycle.test.js.map +1 -0
- package/dist/__tests__/logger.test.d.ts +1 -0
- package/dist/__tests__/logger.test.js +109 -0
- package/dist/__tests__/logger.test.js.map +1 -0
- package/dist/__tests__/middleware.test.d.ts +1 -0
- package/dist/__tests__/middleware.test.js +329 -0
- package/dist/__tests__/middleware.test.js.map +1 -0
- package/dist/__tests__/module.test.d.ts +1 -0
- package/dist/__tests__/module.test.js +280 -0
- package/dist/__tests__/module.test.js.map +1 -0
- package/dist/__tests__/plugin.test.d.ts +1 -0
- package/dist/__tests__/plugin.test.js +283 -0
- package/dist/__tests__/plugin.test.js.map +1 -0
- package/dist/__tests__/request-logger.test.d.ts +1 -0
- package/dist/__tests__/request-logger.test.js +342 -0
- package/dist/__tests__/request-logger.test.js.map +1 -0
- package/dist/__tests__/request-mapping.test.d.ts +1 -0
- package/dist/__tests__/request-mapping.test.js +201 -0
- package/dist/__tests__/request-mapping.test.js.map +1 -0
- package/dist/__tests__/routes.test.d.ts +1 -0
- package/dist/__tests__/routes.test.js +119 -0
- package/dist/__tests__/routes.test.js.map +1 -0
- package/dist/__tests__/scan-fixtures/controllers/hello.controller.d.ts +4 -0
- package/dist/__tests__/scan-fixtures/controllers/hello.controller.js +28 -0
- package/dist/__tests__/scan-fixtures/controllers/hello.controller.js.map +1 -0
- package/dist/__tests__/scan-fixtures/modules/feature.module.d.ts +6 -0
- package/dist/__tests__/scan-fixtures/modules/feature.module.js +28 -0
- package/dist/__tests__/scan-fixtures/modules/feature.module.js.map +1 -0
- package/dist/__tests__/scan-fixtures/services/greeting.service.d.ts +4 -0
- package/dist/__tests__/scan-fixtures/services/greeting.service.js +18 -0
- package/dist/__tests__/scan-fixtures/services/greeting.service.js.map +1 -0
- package/dist/__tests__/scanner.test.d.ts +1 -0
- package/dist/__tests__/scanner.test.js +49 -0
- package/dist/__tests__/scanner.test.js.map +1 -0
- package/dist/__tests__/validation.test.d.ts +1 -0
- package/dist/__tests__/validation.test.js +561 -0
- package/dist/__tests__/validation.test.js.map +1 -0
- package/dist/__tests__/websocket-auth.test.d.ts +1 -0
- package/dist/__tests__/websocket-auth.test.js +431 -0
- package/dist/__tests__/websocket-auth.test.js.map +1 -0
- package/dist/__tests__/websocket-decorators.test.d.ts +1 -0
- package/dist/__tests__/websocket-decorators.test.js +173 -0
- package/dist/__tests__/websocket-decorators.test.js.map +1 -0
- package/dist/__tests__/websocket-validation.test.d.ts +1 -0
- package/dist/__tests__/websocket-validation.test.js +827 -0
- package/dist/__tests__/websocket-validation.test.js.map +1 -0
- package/dist/__tests__/websocket.test.d.ts +1 -0
- package/dist/__tests__/websocket.test.js +415 -0
- package/dist/__tests__/websocket.test.js.map +1 -0
- package/dist/config/config.module.d.ts +2 -0
- package/dist/config/config.module.js +18 -0
- package/dist/config/config.module.js.map +1 -0
- package/dist/config/config.service.d.ts +15 -0
- package/dist/config/config.service.js +58 -0
- package/dist/config/config.service.js.map +1 -0
- package/dist/config/schemas.d.ts +107 -0
- package/dist/config/schemas.js +87 -0
- package/dist/config/schemas.js.map +1 -0
- package/dist/core/app.d.ts +44 -0
- package/dist/core/app.js +178 -0
- package/dist/core/app.js.map +1 -0
- package/dist/core/cinnabun-factory.d.ts +5 -0
- package/dist/core/cinnabun-factory.js +130 -0
- package/dist/core/cinnabun-factory.js.map +1 -0
- package/dist/core/config-loader.d.ts +2 -0
- package/dist/core/config-loader.js +76 -0
- package/dist/core/config-loader.js.map +1 -0
- package/dist/core/config.d.ts +12 -0
- package/dist/core/config.js +27 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/container.d.ts +10 -0
- package/dist/core/container.js +82 -0
- package/dist/core/container.js.map +1 -0
- package/dist/core/dependency-validator.d.ts +12 -0
- package/dist/core/dependency-validator.js +76 -0
- package/dist/core/dependency-validator.js.map +1 -0
- package/dist/core/guard.d.ts +3 -0
- package/dist/core/guard.js +2 -0
- package/dist/core/guard.js.map +1 -0
- package/dist/core/interceptor.d.ts +4 -0
- package/dist/core/interceptor.js +2 -0
- package/dist/core/interceptor.js.map +1 -0
- package/dist/core/logger.d.ts +15 -0
- package/dist/core/logger.js +71 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/module-resolver.d.ts +6 -0
- package/dist/core/module-resolver.js +67 -0
- package/dist/core/module-resolver.js.map +1 -0
- package/dist/core/plugin.d.ts +12 -0
- package/dist/core/plugin.js +2 -0
- package/dist/core/plugin.js.map +1 -0
- package/dist/core/router.d.ts +38 -0
- package/dist/core/router.js +406 -0
- package/dist/core/router.js.map +1 -0
- package/dist/core/scanner.d.ts +7 -0
- package/dist/core/scanner.js +83 -0
- package/dist/core/scanner.js.map +1 -0
- package/dist/core/shutdown-manager.d.ts +15 -0
- package/dist/core/shutdown-manager.js +68 -0
- package/dist/core/shutdown-manager.js.map +1 -0
- package/dist/core/websocket-handler.d.ts +41 -0
- package/dist/core/websocket-handler.js +242 -0
- package/dist/core/websocket-handler.js.map +1 -0
- package/dist/decorators/autowired.d.ts +3 -0
- package/dist/decorators/autowired.js +11 -0
- package/dist/decorators/autowired.js.map +1 -0
- package/dist/decorators/cinnabun-application.d.ts +14 -0
- package/dist/decorators/cinnabun-application.js +17 -0
- package/dist/decorators/cinnabun-application.js.map +1 -0
- package/dist/decorators/lifecycle.d.ts +2 -0
- package/dist/decorators/lifecycle.js +12 -0
- package/dist/decorators/lifecycle.js.map +1 -0
- package/dist/decorators/middleware.d.ts +2 -0
- package/dist/decorators/middleware.js +12 -0
- package/dist/decorators/middleware.js.map +1 -0
- package/dist/decorators/module.d.ts +10 -0
- package/dist/decorators/module.js +13 -0
- package/dist/decorators/module.js.map +1 -0
- package/dist/decorators/on-shutdown.d.ts +1 -0
- package/dist/decorators/on-shutdown.js +10 -0
- package/dist/decorators/on-shutdown.js.map +1 -0
- package/dist/decorators/params.d.ts +6 -0
- package/dist/decorators/params.js +31 -0
- package/dist/decorators/params.js.map +1 -0
- package/dist/decorators/request-mapping.d.ts +7 -0
- package/dist/decorators/request-mapping.js +34 -0
- package/dist/decorators/request-mapping.js.map +1 -0
- package/dist/decorators/response.d.ts +2 -0
- package/dist/decorators/response.js +17 -0
- package/dist/decorators/response.js.map +1 -0
- package/dist/decorators/rest-controller.d.ts +1 -0
- package/dist/decorators/rest-controller.js +19 -0
- package/dist/decorators/rest-controller.js.map +1 -0
- package/dist/decorators/routes.d.ts +5 -0
- package/dist/decorators/routes.js +19 -0
- package/dist/decorators/routes.js.map +1 -0
- package/dist/decorators/service.d.ts +1 -0
- package/dist/decorators/service.js +7 -0
- package/dist/decorators/service.js.map +1 -0
- package/dist/decorators/use-guard.d.ts +2 -0
- package/dist/decorators/use-guard.js +12 -0
- package/dist/decorators/use-guard.js.map +1 -0
- package/dist/decorators/use-interceptor.d.ts +2 -0
- package/dist/decorators/use-interceptor.js +12 -0
- package/dist/decorators/use-interceptor.js.map +1 -0
- package/dist/decorators/validate.d.ts +12 -0
- package/dist/decorators/validate.js +7 -0
- package/dist/decorators/validate.js.map +1 -0
- package/dist/decorators/websocket.d.ts +9 -0
- package/dist/decorators/websocket.js +38 -0
- package/dist/decorators/websocket.js.map +1 -0
- package/dist/decorators/ws-event.d.ts +28 -0
- package/dist/decorators/ws-event.js +37 -0
- package/dist/decorators/ws-event.js.map +1 -0
- package/dist/decorators/ws-gateway.d.ts +18 -0
- package/dist/decorators/ws-gateway.js +24 -0
- package/dist/decorators/ws-gateway.js.map +1 -0
- package/dist/dev/index.d.ts +6 -0
- package/dist/dev/index.js +28 -0
- package/dist/dev/index.js.map +1 -0
- package/dist/exceptions/circular-dependency-error.d.ts +5 -0
- package/dist/exceptions/circular-dependency-error.js +16 -0
- package/dist/exceptions/circular-dependency-error.js.map +1 -0
- package/dist/exceptions/http-exception.d.ts +41 -0
- package/dist/exceptions/http-exception.js +96 -0
- package/dist/exceptions/http-exception.js.map +1 -0
- package/dist/guards/jwt-websocket.guard.d.ts +11 -0
- package/dist/guards/jwt-websocket.guard.js +37 -0
- package/dist/guards/jwt-websocket.guard.js.map +1 -0
- package/dist/guards/websocket-auth.guard.d.ts +16 -0
- package/dist/guards/websocket-auth.guard.js +43 -0
- package/dist/guards/websocket-auth.guard.js.map +1 -0
- package/dist/health/health-check.service.d.ts +45 -0
- package/dist/health/health-check.service.js +95 -0
- package/dist/health/health-check.service.js.map +1 -0
- package/dist/health/health.controller.d.ts +15 -0
- package/dist/health/health.controller.js +63 -0
- package/dist/health/health.controller.js.map +1 -0
- package/dist/health/health.module.d.ts +2 -0
- package/dist/health/health.module.js +20 -0
- package/dist/health/health.module.js.map +1 -0
- package/dist/index.d.ts +74 -11
- package/dist/index.js +54 -0
- package/dist/index.js.map +1 -0
- package/dist/metadata/storage.d.ts +171 -0
- package/dist/metadata/storage.js +257 -0
- package/dist/metadata/storage.js.map +1 -0
- package/dist/middleware/compression.middleware.d.ts +32 -0
- package/dist/middleware/compression.middleware.js +113 -0
- package/dist/middleware/compression.middleware.js.map +1 -0
- package/dist/middleware/cors.middleware.d.ts +18 -0
- package/dist/middleware/cors.middleware.js +79 -0
- package/dist/middleware/cors.middleware.js.map +1 -0
- package/dist/middleware/performance-tracker.middleware.d.ts +35 -0
- package/dist/middleware/performance-tracker.middleware.js +79 -0
- package/dist/middleware/performance-tracker.middleware.js.map +1 -0
- package/dist/middleware/request-logger.middleware.d.ts +32 -0
- package/dist/middleware/request-logger.middleware.js +125 -0
- package/dist/middleware/request-logger.middleware.js.map +1 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/index.js +5 -0
- package/dist/types/index.js.map +1 -0
- package/dist/validation/helpers.d.ts +36 -0
- package/dist/validation/helpers.js +27 -0
- package/dist/validation/helpers.js.map +1 -0
- package/dist/websocket/error.d.ts +27 -0
- package/dist/websocket/error.js +38 -0
- package/dist/websocket/error.js.map +1 -0
- package/package.json +38 -5
- package/LICENSE +0 -9
|
@@ -0,0 +1,827 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import "reflect-metadata";
|
|
11
|
+
import { describe, it, expect, afterEach } from "bun:test";
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
import { CinnabunFactory } from "../core/cinnabun-factory.js";
|
|
14
|
+
import { Service } from "../decorators/service.js";
|
|
15
|
+
import { RestController } from "../decorators/rest-controller.js";
|
|
16
|
+
import { GetMapping } from "../decorators/routes.js";
|
|
17
|
+
import { CinnabunApplication as CinnabunAppDecorator } from "../decorators/cinnabun-application.js";
|
|
18
|
+
import { WsGateway } from "../decorators/ws-gateway.js";
|
|
19
|
+
import { WsEvent } from "../decorators/ws-event.js";
|
|
20
|
+
import { EnableWebSocketMessageBroker } from "../decorators/websocket.js";
|
|
21
|
+
let app = null;
|
|
22
|
+
afterEach(async () => {
|
|
23
|
+
if (app) {
|
|
24
|
+
await app.close();
|
|
25
|
+
app = null;
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
// Helper to create WebSocket connection
|
|
29
|
+
function createWsClient(port) {
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
const ws = new WebSocket(`ws://localhost:${port}/ws`);
|
|
32
|
+
ws.onopen = () => resolve(ws);
|
|
33
|
+
ws.onerror = (err) => reject(err);
|
|
34
|
+
// Clean up listeners after connection
|
|
35
|
+
setTimeout(() => {
|
|
36
|
+
ws.onopen = null;
|
|
37
|
+
ws.onerror = null;
|
|
38
|
+
}, 100);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
// Helper to wait for WebSocket message
|
|
42
|
+
function waitForMessage(ws, timeout = 1000) {
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
const timer = setTimeout(() => {
|
|
45
|
+
ws.onmessage = null;
|
|
46
|
+
reject(new Error("Message timeout"));
|
|
47
|
+
}, timeout);
|
|
48
|
+
ws.onmessage = (event) => {
|
|
49
|
+
clearTimeout(timer);
|
|
50
|
+
ws.onmessage = null;
|
|
51
|
+
const data = typeof event.data === "string" ? JSON.parse(event.data) : event.data;
|
|
52
|
+
resolve(data);
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
describe("WebSocket Validation", () => {
|
|
57
|
+
describe("@WsGateway and @WsEvent", () => {
|
|
58
|
+
it("handles valid message with validation", async () => {
|
|
59
|
+
const MessageSchema = z.object({
|
|
60
|
+
text: z.string().min(1).max(100),
|
|
61
|
+
room: z.string(),
|
|
62
|
+
});
|
|
63
|
+
const receivedMessages = [];
|
|
64
|
+
let ChatGateway = class ChatGateway {
|
|
65
|
+
async handleMessage(data, _ws) {
|
|
66
|
+
receivedMessages.push(data);
|
|
67
|
+
return { success: true, received: data.text };
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
__decorate([
|
|
71
|
+
WsEvent("message:send", MessageSchema),
|
|
72
|
+
__metadata("design:type", Function),
|
|
73
|
+
__metadata("design:paramtypes", [void 0, Object]),
|
|
74
|
+
__metadata("design:returntype", Promise)
|
|
75
|
+
], ChatGateway.prototype, "handleMessage", null);
|
|
76
|
+
ChatGateway = __decorate([
|
|
77
|
+
WsGateway(),
|
|
78
|
+
Service()
|
|
79
|
+
], ChatGateway);
|
|
80
|
+
let App = class App {
|
|
81
|
+
gateway;
|
|
82
|
+
constructor(gateway) {
|
|
83
|
+
this.gateway = gateway;
|
|
84
|
+
}
|
|
85
|
+
index() {
|
|
86
|
+
return { status: "ok" };
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
__decorate([
|
|
90
|
+
GetMapping("/"),
|
|
91
|
+
__metadata("design:type", Function),
|
|
92
|
+
__metadata("design:paramtypes", []),
|
|
93
|
+
__metadata("design:returntype", void 0)
|
|
94
|
+
], App.prototype, "index", null);
|
|
95
|
+
App = __decorate([
|
|
96
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
97
|
+
EnableWebSocketMessageBroker({
|
|
98
|
+
broker: ["/topic"],
|
|
99
|
+
appDestinationPrefixes: ["/app"],
|
|
100
|
+
endpoints: ["/ws"],
|
|
101
|
+
}),
|
|
102
|
+
RestController(),
|
|
103
|
+
__metadata("design:paramtypes", [ChatGateway])
|
|
104
|
+
], App);
|
|
105
|
+
app = await CinnabunFactory.run(App);
|
|
106
|
+
await app.listen(0);
|
|
107
|
+
const port = app.getPort();
|
|
108
|
+
const ws = await createWsClient(port);
|
|
109
|
+
// Skip CONNECTED message
|
|
110
|
+
await waitForMessage(ws);
|
|
111
|
+
// Send valid message
|
|
112
|
+
ws.send(JSON.stringify({
|
|
113
|
+
event: "message:send",
|
|
114
|
+
data: {
|
|
115
|
+
text: "Hello, world!",
|
|
116
|
+
room: "general",
|
|
117
|
+
},
|
|
118
|
+
}));
|
|
119
|
+
const response = await waitForMessage(ws);
|
|
120
|
+
expect(response.type).toBe("response");
|
|
121
|
+
expect(response.event).toBe("message:send");
|
|
122
|
+
expect(response.data.success).toBe(true);
|
|
123
|
+
expect(response.data.received).toBe("Hello, world!");
|
|
124
|
+
expect(receivedMessages).toHaveLength(1);
|
|
125
|
+
expect(receivedMessages[0]).toEqual({
|
|
126
|
+
text: "Hello, world!",
|
|
127
|
+
room: "general",
|
|
128
|
+
});
|
|
129
|
+
ws.close();
|
|
130
|
+
});
|
|
131
|
+
it("rejects invalid message (missing field)", async () => {
|
|
132
|
+
const MessageSchema = z.object({
|
|
133
|
+
text: z.string().min(1),
|
|
134
|
+
room: z.string(),
|
|
135
|
+
});
|
|
136
|
+
let ChatGateway = class ChatGateway {
|
|
137
|
+
async handleMessage(data, _ws) {
|
|
138
|
+
return { success: true };
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
__decorate([
|
|
142
|
+
WsEvent("message:send", MessageSchema),
|
|
143
|
+
__metadata("design:type", Function),
|
|
144
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
145
|
+
__metadata("design:returntype", Promise)
|
|
146
|
+
], ChatGateway.prototype, "handleMessage", null);
|
|
147
|
+
ChatGateway = __decorate([
|
|
148
|
+
WsGateway(),
|
|
149
|
+
Service()
|
|
150
|
+
], ChatGateway);
|
|
151
|
+
let App = class App {
|
|
152
|
+
gateway;
|
|
153
|
+
constructor(gateway) {
|
|
154
|
+
this.gateway = gateway;
|
|
155
|
+
}
|
|
156
|
+
index() {
|
|
157
|
+
return { status: "ok" };
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
__decorate([
|
|
161
|
+
GetMapping("/"),
|
|
162
|
+
__metadata("design:type", Function),
|
|
163
|
+
__metadata("design:paramtypes", []),
|
|
164
|
+
__metadata("design:returntype", void 0)
|
|
165
|
+
], App.prototype, "index", null);
|
|
166
|
+
App = __decorate([
|
|
167
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
168
|
+
EnableWebSocketMessageBroker({
|
|
169
|
+
broker: ["/topic"],
|
|
170
|
+
appDestinationPrefixes: ["/app"],
|
|
171
|
+
endpoints: ["/ws"],
|
|
172
|
+
}),
|
|
173
|
+
RestController(),
|
|
174
|
+
__metadata("design:paramtypes", [ChatGateway])
|
|
175
|
+
], App);
|
|
176
|
+
app = await CinnabunFactory.run(App);
|
|
177
|
+
await app.listen(0);
|
|
178
|
+
const port = app.getPort();
|
|
179
|
+
const ws = await createWsClient(port);
|
|
180
|
+
// Skip CONNECTED message
|
|
181
|
+
await waitForMessage(ws);
|
|
182
|
+
// Send invalid message (missing 'room')
|
|
183
|
+
ws.send(JSON.stringify({
|
|
184
|
+
event: "message:send",
|
|
185
|
+
data: {
|
|
186
|
+
text: "Hello",
|
|
187
|
+
// Missing 'room' field
|
|
188
|
+
},
|
|
189
|
+
}));
|
|
190
|
+
const response = await waitForMessage(ws);
|
|
191
|
+
expect(response.type).toBe("error");
|
|
192
|
+
expect(response.error).toBe("ValidationError");
|
|
193
|
+
expect(response.message).toContain("message:send");
|
|
194
|
+
expect(response.errors).toBeDefined();
|
|
195
|
+
expect(response.errors.room).toBeDefined();
|
|
196
|
+
expect(response.timestamp).toBeDefined();
|
|
197
|
+
ws.close();
|
|
198
|
+
});
|
|
199
|
+
it("rejects invalid message (validation failed)", async () => {
|
|
200
|
+
const MessageSchema = z.object({
|
|
201
|
+
text: z.string().min(5).max(100),
|
|
202
|
+
room: z.string(),
|
|
203
|
+
});
|
|
204
|
+
let ValidationTestGateway = class ValidationTestGateway {
|
|
205
|
+
async handleMessage(data, _ws) {
|
|
206
|
+
return { success: true };
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
__decorate([
|
|
210
|
+
WsEvent("message:validated", MessageSchema),
|
|
211
|
+
__metadata("design:type", Function),
|
|
212
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
213
|
+
__metadata("design:returntype", Promise)
|
|
214
|
+
], ValidationTestGateway.prototype, "handleMessage", null);
|
|
215
|
+
ValidationTestGateway = __decorate([
|
|
216
|
+
WsGateway(),
|
|
217
|
+
Service()
|
|
218
|
+
], ValidationTestGateway);
|
|
219
|
+
let App = class App {
|
|
220
|
+
gateway;
|
|
221
|
+
constructor(gateway) {
|
|
222
|
+
this.gateway = gateway;
|
|
223
|
+
}
|
|
224
|
+
index() {
|
|
225
|
+
return { status: "ok" };
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
__decorate([
|
|
229
|
+
GetMapping("/"),
|
|
230
|
+
__metadata("design:type", Function),
|
|
231
|
+
__metadata("design:paramtypes", []),
|
|
232
|
+
__metadata("design:returntype", void 0)
|
|
233
|
+
], App.prototype, "index", null);
|
|
234
|
+
App = __decorate([
|
|
235
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
236
|
+
EnableWebSocketMessageBroker({
|
|
237
|
+
broker: ["/topic"],
|
|
238
|
+
appDestinationPrefixes: ["/app"],
|
|
239
|
+
endpoints: ["/ws"],
|
|
240
|
+
}),
|
|
241
|
+
RestController(),
|
|
242
|
+
__metadata("design:paramtypes", [ValidationTestGateway])
|
|
243
|
+
], App);
|
|
244
|
+
app = await CinnabunFactory.run(App);
|
|
245
|
+
await app.listen(0);
|
|
246
|
+
const port = app.getPort();
|
|
247
|
+
const ws = await createWsClient(port);
|
|
248
|
+
// Skip CONNECTED message
|
|
249
|
+
await waitForMessage(ws);
|
|
250
|
+
// Send invalid message (text too short)
|
|
251
|
+
ws.send(JSON.stringify({
|
|
252
|
+
event: "message:validated",
|
|
253
|
+
data: {
|
|
254
|
+
text: "Hi", // Too short (min 5)
|
|
255
|
+
room: "general",
|
|
256
|
+
},
|
|
257
|
+
}));
|
|
258
|
+
const response = await waitForMessage(ws);
|
|
259
|
+
expect(response.type).toBe("error");
|
|
260
|
+
expect(response.error).toBe("ValidationError");
|
|
261
|
+
expect(response.errors.text).toBeDefined();
|
|
262
|
+
ws.close();
|
|
263
|
+
});
|
|
264
|
+
it("rejects unknown event", async () => {
|
|
265
|
+
let ChatGateway = class ChatGateway {
|
|
266
|
+
async handleMessage(data, _ws) {
|
|
267
|
+
return { success: true };
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
__decorate([
|
|
271
|
+
WsEvent("message:send"),
|
|
272
|
+
__metadata("design:type", Function),
|
|
273
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
274
|
+
__metadata("design:returntype", Promise)
|
|
275
|
+
], ChatGateway.prototype, "handleMessage", null);
|
|
276
|
+
ChatGateway = __decorate([
|
|
277
|
+
WsGateway(),
|
|
278
|
+
Service()
|
|
279
|
+
], ChatGateway);
|
|
280
|
+
let App = class App {
|
|
281
|
+
gateway;
|
|
282
|
+
constructor(gateway) {
|
|
283
|
+
this.gateway = gateway;
|
|
284
|
+
}
|
|
285
|
+
index() {
|
|
286
|
+
return { status: "ok" };
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
__decorate([
|
|
290
|
+
GetMapping("/"),
|
|
291
|
+
__metadata("design:type", Function),
|
|
292
|
+
__metadata("design:paramtypes", []),
|
|
293
|
+
__metadata("design:returntype", void 0)
|
|
294
|
+
], App.prototype, "index", null);
|
|
295
|
+
App = __decorate([
|
|
296
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
297
|
+
EnableWebSocketMessageBroker({
|
|
298
|
+
broker: ["/topic"],
|
|
299
|
+
appDestinationPrefixes: ["/app"],
|
|
300
|
+
endpoints: ["/ws"],
|
|
301
|
+
}),
|
|
302
|
+
RestController(),
|
|
303
|
+
__metadata("design:paramtypes", [ChatGateway])
|
|
304
|
+
], App);
|
|
305
|
+
app = await CinnabunFactory.run(App);
|
|
306
|
+
await app.listen(0);
|
|
307
|
+
const port = app.getPort();
|
|
308
|
+
const ws = await createWsClient(port);
|
|
309
|
+
// Skip CONNECTED message
|
|
310
|
+
await waitForMessage(ws);
|
|
311
|
+
// Send unknown event
|
|
312
|
+
ws.send(JSON.stringify({
|
|
313
|
+
event: "unknown:event",
|
|
314
|
+
data: {},
|
|
315
|
+
}));
|
|
316
|
+
const response = await waitForMessage(ws);
|
|
317
|
+
expect(response.type).toBe("error");
|
|
318
|
+
expect(response.error).toBe("UnknownEvent");
|
|
319
|
+
expect(response.message).toContain("unknown:event");
|
|
320
|
+
ws.close();
|
|
321
|
+
});
|
|
322
|
+
it("handles invalid JSON", async () => {
|
|
323
|
+
let ChatGateway = class ChatGateway {
|
|
324
|
+
async handleMessage(data, _ws) {
|
|
325
|
+
return { success: true };
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
__decorate([
|
|
329
|
+
WsEvent("message:send"),
|
|
330
|
+
__metadata("design:type", Function),
|
|
331
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
332
|
+
__metadata("design:returntype", Promise)
|
|
333
|
+
], ChatGateway.prototype, "handleMessage", null);
|
|
334
|
+
ChatGateway = __decorate([
|
|
335
|
+
WsGateway(),
|
|
336
|
+
Service()
|
|
337
|
+
], ChatGateway);
|
|
338
|
+
let App = class App {
|
|
339
|
+
gateway;
|
|
340
|
+
constructor(gateway) {
|
|
341
|
+
this.gateway = gateway;
|
|
342
|
+
}
|
|
343
|
+
index() {
|
|
344
|
+
return { status: "ok" };
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
__decorate([
|
|
348
|
+
GetMapping("/"),
|
|
349
|
+
__metadata("design:type", Function),
|
|
350
|
+
__metadata("design:paramtypes", []),
|
|
351
|
+
__metadata("design:returntype", void 0)
|
|
352
|
+
], App.prototype, "index", null);
|
|
353
|
+
App = __decorate([
|
|
354
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
355
|
+
EnableWebSocketMessageBroker({
|
|
356
|
+
broker: ["/topic"],
|
|
357
|
+
appDestinationPrefixes: ["/app"],
|
|
358
|
+
endpoints: ["/ws"],
|
|
359
|
+
}),
|
|
360
|
+
RestController(),
|
|
361
|
+
__metadata("design:paramtypes", [ChatGateway])
|
|
362
|
+
], App);
|
|
363
|
+
app = await CinnabunFactory.run(App);
|
|
364
|
+
await app.listen(0);
|
|
365
|
+
const port = app.getPort();
|
|
366
|
+
const ws = await createWsClient(port);
|
|
367
|
+
// Skip CONNECTED message
|
|
368
|
+
await waitForMessage(ws);
|
|
369
|
+
// Send invalid JSON
|
|
370
|
+
ws.send("not valid json");
|
|
371
|
+
const response = await waitForMessage(ws);
|
|
372
|
+
expect(response.type).toBe("error");
|
|
373
|
+
expect(response.error).toBe("ParseError");
|
|
374
|
+
expect(response.message).toContain("Invalid JSON");
|
|
375
|
+
ws.close();
|
|
376
|
+
});
|
|
377
|
+
it("handles event without validation schema", async () => {
|
|
378
|
+
const receivedData = [];
|
|
379
|
+
let NotificationGateway = class NotificationGateway {
|
|
380
|
+
async handleNotify(data, _ws) {
|
|
381
|
+
receivedData.push(data);
|
|
382
|
+
return { acknowledged: true };
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
__decorate([
|
|
386
|
+
WsEvent("notify"),
|
|
387
|
+
__metadata("design:type", Function),
|
|
388
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
389
|
+
__metadata("design:returntype", Promise)
|
|
390
|
+
], NotificationGateway.prototype, "handleNotify", null);
|
|
391
|
+
NotificationGateway = __decorate([
|
|
392
|
+
WsGateway(),
|
|
393
|
+
Service()
|
|
394
|
+
], NotificationGateway);
|
|
395
|
+
let App = class App {
|
|
396
|
+
gateway;
|
|
397
|
+
constructor(gateway) {
|
|
398
|
+
this.gateway = gateway;
|
|
399
|
+
}
|
|
400
|
+
index() {
|
|
401
|
+
return { status: "ok" };
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
__decorate([
|
|
405
|
+
GetMapping("/"),
|
|
406
|
+
__metadata("design:type", Function),
|
|
407
|
+
__metadata("design:paramtypes", []),
|
|
408
|
+
__metadata("design:returntype", void 0)
|
|
409
|
+
], App.prototype, "index", null);
|
|
410
|
+
App = __decorate([
|
|
411
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
412
|
+
EnableWebSocketMessageBroker({
|
|
413
|
+
broker: ["/topic"],
|
|
414
|
+
appDestinationPrefixes: ["/app"],
|
|
415
|
+
endpoints: ["/ws"],
|
|
416
|
+
}),
|
|
417
|
+
RestController(),
|
|
418
|
+
__metadata("design:paramtypes", [NotificationGateway])
|
|
419
|
+
], App);
|
|
420
|
+
app = await CinnabunFactory.run(App);
|
|
421
|
+
await app.listen(0);
|
|
422
|
+
const port = app.getPort();
|
|
423
|
+
const ws = await createWsClient(port);
|
|
424
|
+
// Skip CONNECTED message
|
|
425
|
+
await waitForMessage(ws);
|
|
426
|
+
// Send event without schema
|
|
427
|
+
ws.send(JSON.stringify({
|
|
428
|
+
event: "notify",
|
|
429
|
+
data: {
|
|
430
|
+
message: "Any data is accepted",
|
|
431
|
+
arbitrary: 123,
|
|
432
|
+
},
|
|
433
|
+
}));
|
|
434
|
+
const response = await waitForMessage(ws);
|
|
435
|
+
expect(response.type).toBe("response");
|
|
436
|
+
expect(response.data.acknowledged).toBe(true);
|
|
437
|
+
expect(receivedData).toHaveLength(1);
|
|
438
|
+
expect(receivedData[0]).toEqual({
|
|
439
|
+
message: "Any data is accepted",
|
|
440
|
+
arbitrary: 123,
|
|
441
|
+
});
|
|
442
|
+
ws.close();
|
|
443
|
+
});
|
|
444
|
+
it("handles multiple events in same gateway", async () => {
|
|
445
|
+
const JoinSchema = z.object({
|
|
446
|
+
room: z.string(),
|
|
447
|
+
});
|
|
448
|
+
const MessageSchema = z.object({
|
|
449
|
+
text: z.string(),
|
|
450
|
+
});
|
|
451
|
+
const events = [];
|
|
452
|
+
let MultiEventGateway = class MultiEventGateway {
|
|
453
|
+
async handleJoin(data, _ws) {
|
|
454
|
+
events.push(`join:${data.room}`);
|
|
455
|
+
return { joined: data.room };
|
|
456
|
+
}
|
|
457
|
+
async handleMessage(data, _ws) {
|
|
458
|
+
events.push(`message:${data.text}`);
|
|
459
|
+
return { sent: true };
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
__decorate([
|
|
463
|
+
WsEvent("room:join", JoinSchema),
|
|
464
|
+
__metadata("design:type", Function),
|
|
465
|
+
__metadata("design:paramtypes", [void 0, Object]),
|
|
466
|
+
__metadata("design:returntype", Promise)
|
|
467
|
+
], MultiEventGateway.prototype, "handleJoin", null);
|
|
468
|
+
__decorate([
|
|
469
|
+
WsEvent("message", MessageSchema),
|
|
470
|
+
__metadata("design:type", Function),
|
|
471
|
+
__metadata("design:paramtypes", [void 0, Object]),
|
|
472
|
+
__metadata("design:returntype", Promise)
|
|
473
|
+
], MultiEventGateway.prototype, "handleMessage", null);
|
|
474
|
+
MultiEventGateway = __decorate([
|
|
475
|
+
WsGateway(),
|
|
476
|
+
Service()
|
|
477
|
+
], MultiEventGateway);
|
|
478
|
+
let App = class App {
|
|
479
|
+
gateway;
|
|
480
|
+
constructor(gateway) {
|
|
481
|
+
this.gateway = gateway;
|
|
482
|
+
}
|
|
483
|
+
index() {
|
|
484
|
+
return { status: "ok" };
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
__decorate([
|
|
488
|
+
GetMapping("/"),
|
|
489
|
+
__metadata("design:type", Function),
|
|
490
|
+
__metadata("design:paramtypes", []),
|
|
491
|
+
__metadata("design:returntype", void 0)
|
|
492
|
+
], App.prototype, "index", null);
|
|
493
|
+
App = __decorate([
|
|
494
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
495
|
+
EnableWebSocketMessageBroker({
|
|
496
|
+
broker: ["/topic"],
|
|
497
|
+
appDestinationPrefixes: ["/app"],
|
|
498
|
+
endpoints: ["/ws"],
|
|
499
|
+
}),
|
|
500
|
+
RestController(),
|
|
501
|
+
__metadata("design:paramtypes", [MultiEventGateway])
|
|
502
|
+
], App);
|
|
503
|
+
app = await CinnabunFactory.run(App);
|
|
504
|
+
await app.listen(0);
|
|
505
|
+
const port = app.getPort();
|
|
506
|
+
const ws = await createWsClient(port);
|
|
507
|
+
// Skip CONNECTED message
|
|
508
|
+
await waitForMessage(ws);
|
|
509
|
+
// Send join event
|
|
510
|
+
ws.send(JSON.stringify({
|
|
511
|
+
event: "room:join",
|
|
512
|
+
data: { room: "general" },
|
|
513
|
+
}));
|
|
514
|
+
let response = await waitForMessage(ws);
|
|
515
|
+
expect(response.data.joined).toBe("general");
|
|
516
|
+
// Send message event
|
|
517
|
+
ws.send(JSON.stringify({
|
|
518
|
+
event: "message",
|
|
519
|
+
data: { text: "Hello!" },
|
|
520
|
+
}));
|
|
521
|
+
response = await waitForMessage(ws);
|
|
522
|
+
expect(response.data.sent).toBe(true);
|
|
523
|
+
expect(events).toEqual(["join:general", "message:Hello!"]);
|
|
524
|
+
ws.close();
|
|
525
|
+
});
|
|
526
|
+
it("handler that returns undefined does not send response", async () => {
|
|
527
|
+
let SilentGateway = class SilentGateway {
|
|
528
|
+
async handleSilent(_data, _ws) {
|
|
529
|
+
// Explicitly return undefined
|
|
530
|
+
return undefined;
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
__decorate([
|
|
534
|
+
WsEvent("silent"),
|
|
535
|
+
__metadata("design:type", Function),
|
|
536
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
537
|
+
__metadata("design:returntype", Promise)
|
|
538
|
+
], SilentGateway.prototype, "handleSilent", null);
|
|
539
|
+
SilentGateway = __decorate([
|
|
540
|
+
WsGateway(),
|
|
541
|
+
Service()
|
|
542
|
+
], SilentGateway);
|
|
543
|
+
let App = class App {
|
|
544
|
+
gateway;
|
|
545
|
+
constructor(gateway) {
|
|
546
|
+
this.gateway = gateway;
|
|
547
|
+
}
|
|
548
|
+
index() {
|
|
549
|
+
return { status: "ok" };
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
__decorate([
|
|
553
|
+
GetMapping("/"),
|
|
554
|
+
__metadata("design:type", Function),
|
|
555
|
+
__metadata("design:paramtypes", []),
|
|
556
|
+
__metadata("design:returntype", void 0)
|
|
557
|
+
], App.prototype, "index", null);
|
|
558
|
+
App = __decorate([
|
|
559
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
560
|
+
EnableWebSocketMessageBroker({
|
|
561
|
+
broker: ["/topic"],
|
|
562
|
+
appDestinationPrefixes: ["/app"],
|
|
563
|
+
endpoints: ["/ws"],
|
|
564
|
+
}),
|
|
565
|
+
RestController(),
|
|
566
|
+
__metadata("design:paramtypes", [SilentGateway])
|
|
567
|
+
], App);
|
|
568
|
+
app = await CinnabunFactory.run(App);
|
|
569
|
+
await app.listen(0);
|
|
570
|
+
const port = app.getPort();
|
|
571
|
+
const ws = await createWsClient(port);
|
|
572
|
+
// Skip CONNECTED message
|
|
573
|
+
const connectedMsg = await waitForMessage(ws);
|
|
574
|
+
expect(connectedMsg.type).toBe("CONNECTED");
|
|
575
|
+
let messageReceived = false;
|
|
576
|
+
ws.onmessage = () => {
|
|
577
|
+
messageReceived = true;
|
|
578
|
+
};
|
|
579
|
+
// Send event
|
|
580
|
+
ws.send(JSON.stringify({
|
|
581
|
+
event: "silent",
|
|
582
|
+
data: {},
|
|
583
|
+
}));
|
|
584
|
+
// Wait a bit to ensure no message comes back
|
|
585
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
586
|
+
expect(messageReceived).toBe(false);
|
|
587
|
+
ws.close();
|
|
588
|
+
});
|
|
589
|
+
it("handles handler errors gracefully", async () => {
|
|
590
|
+
let FailingGateway = class FailingGateway {
|
|
591
|
+
async handleFail(_data, _ws) {
|
|
592
|
+
throw new Error("Handler failed");
|
|
593
|
+
}
|
|
594
|
+
};
|
|
595
|
+
__decorate([
|
|
596
|
+
WsEvent("fail"),
|
|
597
|
+
__metadata("design:type", Function),
|
|
598
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
599
|
+
__metadata("design:returntype", Promise)
|
|
600
|
+
], FailingGateway.prototype, "handleFail", null);
|
|
601
|
+
FailingGateway = __decorate([
|
|
602
|
+
WsGateway(),
|
|
603
|
+
Service()
|
|
604
|
+
], FailingGateway);
|
|
605
|
+
let App = class App {
|
|
606
|
+
gateway;
|
|
607
|
+
constructor(gateway) {
|
|
608
|
+
this.gateway = gateway;
|
|
609
|
+
}
|
|
610
|
+
index() {
|
|
611
|
+
return { status: "ok" };
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
__decorate([
|
|
615
|
+
GetMapping("/"),
|
|
616
|
+
__metadata("design:type", Function),
|
|
617
|
+
__metadata("design:paramtypes", []),
|
|
618
|
+
__metadata("design:returntype", void 0)
|
|
619
|
+
], App.prototype, "index", null);
|
|
620
|
+
App = __decorate([
|
|
621
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
622
|
+
EnableWebSocketMessageBroker({
|
|
623
|
+
broker: ["/topic"],
|
|
624
|
+
appDestinationPrefixes: ["/app"],
|
|
625
|
+
endpoints: ["/ws"],
|
|
626
|
+
}),
|
|
627
|
+
RestController(),
|
|
628
|
+
__metadata("design:paramtypes", [FailingGateway])
|
|
629
|
+
], App);
|
|
630
|
+
app = await CinnabunFactory.run(App);
|
|
631
|
+
await app.listen(0);
|
|
632
|
+
const port = app.getPort();
|
|
633
|
+
const ws = await createWsClient(port);
|
|
634
|
+
// Skip CONNECTED message
|
|
635
|
+
await waitForMessage(ws);
|
|
636
|
+
// Send event that will cause error
|
|
637
|
+
ws.send(JSON.stringify({
|
|
638
|
+
event: "fail",
|
|
639
|
+
data: {},
|
|
640
|
+
}));
|
|
641
|
+
const response = await waitForMessage(ws);
|
|
642
|
+
expect(response.type).toBe("error");
|
|
643
|
+
expect(response.error).toBe("InternalError");
|
|
644
|
+
expect(response.errorId).toBeDefined();
|
|
645
|
+
ws.close();
|
|
646
|
+
});
|
|
647
|
+
});
|
|
648
|
+
describe("Complex Validation", () => {
|
|
649
|
+
it("validates nested objects", async () => {
|
|
650
|
+
const ProfileSchema = z.object({
|
|
651
|
+
user: z.object({
|
|
652
|
+
name: z.string().min(1),
|
|
653
|
+
email: z.string().email(),
|
|
654
|
+
}),
|
|
655
|
+
settings: z.object({
|
|
656
|
+
theme: z.enum(["light", "dark"]),
|
|
657
|
+
}),
|
|
658
|
+
});
|
|
659
|
+
let ProfileGateway = class ProfileGateway {
|
|
660
|
+
async updateProfile(data, _ws) {
|
|
661
|
+
return { updated: true, user: data.user.name };
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
__decorate([
|
|
665
|
+
WsEvent("profile:update", ProfileSchema),
|
|
666
|
+
__metadata("design:type", Function),
|
|
667
|
+
__metadata("design:paramtypes", [void 0, Object]),
|
|
668
|
+
__metadata("design:returntype", Promise)
|
|
669
|
+
], ProfileGateway.prototype, "updateProfile", null);
|
|
670
|
+
ProfileGateway = __decorate([
|
|
671
|
+
WsGateway(),
|
|
672
|
+
Service()
|
|
673
|
+
], ProfileGateway);
|
|
674
|
+
let App = class App {
|
|
675
|
+
gateway;
|
|
676
|
+
constructor(gateway) {
|
|
677
|
+
this.gateway = gateway;
|
|
678
|
+
}
|
|
679
|
+
index() {
|
|
680
|
+
return { status: "ok" };
|
|
681
|
+
}
|
|
682
|
+
};
|
|
683
|
+
__decorate([
|
|
684
|
+
GetMapping("/"),
|
|
685
|
+
__metadata("design:type", Function),
|
|
686
|
+
__metadata("design:paramtypes", []),
|
|
687
|
+
__metadata("design:returntype", void 0)
|
|
688
|
+
], App.prototype, "index", null);
|
|
689
|
+
App = __decorate([
|
|
690
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
691
|
+
EnableWebSocketMessageBroker({
|
|
692
|
+
broker: ["/topic"],
|
|
693
|
+
appDestinationPrefixes: ["/app"],
|
|
694
|
+
endpoints: ["/ws"],
|
|
695
|
+
}),
|
|
696
|
+
RestController(),
|
|
697
|
+
__metadata("design:paramtypes", [ProfileGateway])
|
|
698
|
+
], App);
|
|
699
|
+
app = await CinnabunFactory.run(App);
|
|
700
|
+
await app.listen(0);
|
|
701
|
+
const port = app.getPort();
|
|
702
|
+
const ws = await createWsClient(port);
|
|
703
|
+
// Skip CONNECTED message
|
|
704
|
+
await waitForMessage(ws);
|
|
705
|
+
// Send valid nested data
|
|
706
|
+
ws.send(JSON.stringify({
|
|
707
|
+
event: "profile:update",
|
|
708
|
+
data: {
|
|
709
|
+
user: {
|
|
710
|
+
name: "Alice",
|
|
711
|
+
email: "alice@example.com",
|
|
712
|
+
},
|
|
713
|
+
settings: {
|
|
714
|
+
theme: "dark",
|
|
715
|
+
},
|
|
716
|
+
},
|
|
717
|
+
}));
|
|
718
|
+
const response = await waitForMessage(ws);
|
|
719
|
+
expect(response.type).toBe("response");
|
|
720
|
+
expect(response.data.updated).toBe(true);
|
|
721
|
+
expect(response.data.user).toBe("Alice");
|
|
722
|
+
ws.close();
|
|
723
|
+
});
|
|
724
|
+
it("validates arrays", async () => {
|
|
725
|
+
const BatchSchema = z.object({
|
|
726
|
+
items: z.array(z.number()).min(1).max(10),
|
|
727
|
+
});
|
|
728
|
+
let BatchGateway = class BatchGateway {
|
|
729
|
+
async processBatch(data, _ws) {
|
|
730
|
+
return { count: data.items.length };
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
__decorate([
|
|
734
|
+
WsEvent("batch:process", BatchSchema),
|
|
735
|
+
__metadata("design:type", Function),
|
|
736
|
+
__metadata("design:paramtypes", [void 0, Object]),
|
|
737
|
+
__metadata("design:returntype", Promise)
|
|
738
|
+
], BatchGateway.prototype, "processBatch", null);
|
|
739
|
+
BatchGateway = __decorate([
|
|
740
|
+
WsGateway(),
|
|
741
|
+
Service()
|
|
742
|
+
], BatchGateway);
|
|
743
|
+
let App = class App {
|
|
744
|
+
gateway;
|
|
745
|
+
constructor(gateway) {
|
|
746
|
+
this.gateway = gateway;
|
|
747
|
+
}
|
|
748
|
+
index() {
|
|
749
|
+
return { status: "ok" };
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
__decorate([
|
|
753
|
+
GetMapping("/"),
|
|
754
|
+
__metadata("design:type", Function),
|
|
755
|
+
__metadata("design:paramtypes", []),
|
|
756
|
+
__metadata("design:returntype", void 0)
|
|
757
|
+
], App.prototype, "index", null);
|
|
758
|
+
App = __decorate([
|
|
759
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
760
|
+
EnableWebSocketMessageBroker({
|
|
761
|
+
broker: ["/topic"],
|
|
762
|
+
appDestinationPrefixes: ["/app"],
|
|
763
|
+
endpoints: ["/ws"],
|
|
764
|
+
}),
|
|
765
|
+
RestController(),
|
|
766
|
+
__metadata("design:paramtypes", [BatchGateway])
|
|
767
|
+
], App);
|
|
768
|
+
app = await CinnabunFactory.run(App);
|
|
769
|
+
await app.listen(0);
|
|
770
|
+
const port = app.getPort();
|
|
771
|
+
const ws = await createWsClient(port);
|
|
772
|
+
// Skip CONNECTED message
|
|
773
|
+
await waitForMessage(ws);
|
|
774
|
+
// Send valid array
|
|
775
|
+
ws.send(JSON.stringify({
|
|
776
|
+
event: "batch:process",
|
|
777
|
+
data: {
|
|
778
|
+
items: [1, 2, 3, 4, 5],
|
|
779
|
+
},
|
|
780
|
+
}));
|
|
781
|
+
const response = await waitForMessage(ws);
|
|
782
|
+
expect(response.type).toBe("response");
|
|
783
|
+
expect(response.data.count).toBe(5);
|
|
784
|
+
ws.close();
|
|
785
|
+
});
|
|
786
|
+
});
|
|
787
|
+
describe("Backward Compatibility", () => {
|
|
788
|
+
it("still supports destination-based messages", async () => {
|
|
789
|
+
let App = class App {
|
|
790
|
+
index() {
|
|
791
|
+
return { status: "ok" };
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
__decorate([
|
|
795
|
+
GetMapping("/"),
|
|
796
|
+
__metadata("design:type", Function),
|
|
797
|
+
__metadata("design:paramtypes", []),
|
|
798
|
+
__metadata("design:returntype", void 0)
|
|
799
|
+
], App.prototype, "index", null);
|
|
800
|
+
App = __decorate([
|
|
801
|
+
CinnabunAppDecorator({ port: 0, scanPaths: [] }),
|
|
802
|
+
EnableWebSocketMessageBroker({
|
|
803
|
+
broker: ["/topic"],
|
|
804
|
+
appDestinationPrefixes: ["/app"],
|
|
805
|
+
endpoints: ["/ws"],
|
|
806
|
+
}),
|
|
807
|
+
RestController()
|
|
808
|
+
], App);
|
|
809
|
+
app = await CinnabunFactory.run(App);
|
|
810
|
+
await app.listen(0);
|
|
811
|
+
const port = app.getPort();
|
|
812
|
+
const ws = await createWsClient(port);
|
|
813
|
+
// Skip CONNECTED message
|
|
814
|
+
await waitForMessage(ws);
|
|
815
|
+
// Send old-style destination message
|
|
816
|
+
ws.send(JSON.stringify({
|
|
817
|
+
destination: "/app/unknown",
|
|
818
|
+
body: { test: "data" },
|
|
819
|
+
}));
|
|
820
|
+
const response = await waitForMessage(ws);
|
|
821
|
+
// Should get error for unknown destination (old behavior)
|
|
822
|
+
expect(response.type).toBe("ERROR");
|
|
823
|
+
ws.close();
|
|
824
|
+
});
|
|
825
|
+
});
|
|
826
|
+
});
|
|
827
|
+
//# sourceMappingURL=websocket-validation.test.js.map
|