@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.
Files changed (260) hide show
  1. package/dist/__tests__/autowired.test.d.ts +1 -0
  2. package/dist/__tests__/autowired.test.js +109 -0
  3. package/dist/__tests__/autowired.test.js.map +1 -0
  4. package/dist/__tests__/cinnabun-application.test.d.ts +1 -0
  5. package/dist/__tests__/cinnabun-application.test.js +96 -0
  6. package/dist/__tests__/cinnabun-application.test.js.map +1 -0
  7. package/dist/__tests__/cinnabun-factory.test.d.ts +1 -0
  8. package/dist/__tests__/cinnabun-factory.test.js +269 -0
  9. package/dist/__tests__/cinnabun-factory.test.js.map +1 -0
  10. package/dist/__tests__/circular-dependency.test.d.ts +1 -0
  11. package/dist/__tests__/circular-dependency.test.js +318 -0
  12. package/dist/__tests__/circular-dependency.test.js.map +1 -0
  13. package/dist/__tests__/compression.test.d.ts +1 -0
  14. package/dist/__tests__/compression.test.js +459 -0
  15. package/dist/__tests__/compression.test.js.map +1 -0
  16. package/dist/__tests__/config.test.d.ts +1 -0
  17. package/dist/__tests__/config.test.js +86 -0
  18. package/dist/__tests__/config.test.js.map +1 -0
  19. package/dist/__tests__/cors.test.d.ts +1 -0
  20. package/dist/__tests__/cors.test.js +575 -0
  21. package/dist/__tests__/cors.test.js.map +1 -0
  22. package/dist/__tests__/env-config.test.d.ts +1 -0
  23. package/dist/__tests__/env-config.test.js +367 -0
  24. package/dist/__tests__/env-config.test.js.map +1 -0
  25. package/dist/__tests__/exception.test.d.ts +1 -0
  26. package/dist/__tests__/exception.test.js +207 -0
  27. package/dist/__tests__/exception.test.js.map +1 -0
  28. package/dist/__tests__/guards-interceptors.test.d.ts +1 -0
  29. package/dist/__tests__/guards-interceptors.test.js +660 -0
  30. package/dist/__tests__/guards-interceptors.test.js.map +1 -0
  31. package/dist/__tests__/health-check.test.d.ts +1 -0
  32. package/dist/__tests__/health-check.test.js +240 -0
  33. package/dist/__tests__/health-check.test.js.map +1 -0
  34. package/dist/__tests__/http.test.d.ts +1 -0
  35. package/dist/__tests__/http.test.js +629 -0
  36. package/dist/__tests__/http.test.js.map +1 -0
  37. package/dist/__tests__/integration/e2e.test.d.ts +1 -0
  38. package/dist/__tests__/integration/e2e.test.js +192 -0
  39. package/dist/__tests__/integration/e2e.test.js.map +1 -0
  40. package/dist/__tests__/integration/performance.bench.d.ts +1 -0
  41. package/dist/__tests__/integration/performance.bench.js +129 -0
  42. package/dist/__tests__/integration/performance.bench.js.map +1 -0
  43. package/dist/__tests__/integration/validation.test.d.ts +1 -0
  44. package/dist/__tests__/integration/validation.test.js +133 -0
  45. package/dist/__tests__/integration/validation.test.js.map +1 -0
  46. package/dist/__tests__/lifecycle-management.test.d.ts +1 -0
  47. package/dist/__tests__/lifecycle-management.test.js +688 -0
  48. package/dist/__tests__/lifecycle-management.test.js.map +1 -0
  49. package/dist/__tests__/lifecycle.test.d.ts +1 -0
  50. package/dist/__tests__/lifecycle.test.js +196 -0
  51. package/dist/__tests__/lifecycle.test.js.map +1 -0
  52. package/dist/__tests__/logger.test.d.ts +1 -0
  53. package/dist/__tests__/logger.test.js +109 -0
  54. package/dist/__tests__/logger.test.js.map +1 -0
  55. package/dist/__tests__/middleware.test.d.ts +1 -0
  56. package/dist/__tests__/middleware.test.js +329 -0
  57. package/dist/__tests__/middleware.test.js.map +1 -0
  58. package/dist/__tests__/module.test.d.ts +1 -0
  59. package/dist/__tests__/module.test.js +280 -0
  60. package/dist/__tests__/module.test.js.map +1 -0
  61. package/dist/__tests__/plugin.test.d.ts +1 -0
  62. package/dist/__tests__/plugin.test.js +283 -0
  63. package/dist/__tests__/plugin.test.js.map +1 -0
  64. package/dist/__tests__/request-logger.test.d.ts +1 -0
  65. package/dist/__tests__/request-logger.test.js +342 -0
  66. package/dist/__tests__/request-logger.test.js.map +1 -0
  67. package/dist/__tests__/request-mapping.test.d.ts +1 -0
  68. package/dist/__tests__/request-mapping.test.js +201 -0
  69. package/dist/__tests__/request-mapping.test.js.map +1 -0
  70. package/dist/__tests__/routes.test.d.ts +1 -0
  71. package/dist/__tests__/routes.test.js +119 -0
  72. package/dist/__tests__/routes.test.js.map +1 -0
  73. package/dist/__tests__/scan-fixtures/controllers/hello.controller.d.ts +4 -0
  74. package/dist/__tests__/scan-fixtures/controllers/hello.controller.js +28 -0
  75. package/dist/__tests__/scan-fixtures/controllers/hello.controller.js.map +1 -0
  76. package/dist/__tests__/scan-fixtures/modules/feature.module.d.ts +6 -0
  77. package/dist/__tests__/scan-fixtures/modules/feature.module.js +28 -0
  78. package/dist/__tests__/scan-fixtures/modules/feature.module.js.map +1 -0
  79. package/dist/__tests__/scan-fixtures/services/greeting.service.d.ts +4 -0
  80. package/dist/__tests__/scan-fixtures/services/greeting.service.js +18 -0
  81. package/dist/__tests__/scan-fixtures/services/greeting.service.js.map +1 -0
  82. package/dist/__tests__/scanner.test.d.ts +1 -0
  83. package/dist/__tests__/scanner.test.js +49 -0
  84. package/dist/__tests__/scanner.test.js.map +1 -0
  85. package/dist/__tests__/validation.test.d.ts +1 -0
  86. package/dist/__tests__/validation.test.js +561 -0
  87. package/dist/__tests__/validation.test.js.map +1 -0
  88. package/dist/__tests__/websocket-auth.test.d.ts +1 -0
  89. package/dist/__tests__/websocket-auth.test.js +431 -0
  90. package/dist/__tests__/websocket-auth.test.js.map +1 -0
  91. package/dist/__tests__/websocket-decorators.test.d.ts +1 -0
  92. package/dist/__tests__/websocket-decorators.test.js +173 -0
  93. package/dist/__tests__/websocket-decorators.test.js.map +1 -0
  94. package/dist/__tests__/websocket-validation.test.d.ts +1 -0
  95. package/dist/__tests__/websocket-validation.test.js +827 -0
  96. package/dist/__tests__/websocket-validation.test.js.map +1 -0
  97. package/dist/__tests__/websocket.test.d.ts +1 -0
  98. package/dist/__tests__/websocket.test.js +415 -0
  99. package/dist/__tests__/websocket.test.js.map +1 -0
  100. package/dist/config/config.module.d.ts +2 -0
  101. package/dist/config/config.module.js +18 -0
  102. package/dist/config/config.module.js.map +1 -0
  103. package/dist/config/config.service.d.ts +15 -0
  104. package/dist/config/config.service.js +58 -0
  105. package/dist/config/config.service.js.map +1 -0
  106. package/dist/config/schemas.d.ts +107 -0
  107. package/dist/config/schemas.js +87 -0
  108. package/dist/config/schemas.js.map +1 -0
  109. package/dist/core/app.d.ts +44 -0
  110. package/dist/core/app.js +178 -0
  111. package/dist/core/app.js.map +1 -0
  112. package/dist/core/cinnabun-factory.d.ts +5 -0
  113. package/dist/core/cinnabun-factory.js +130 -0
  114. package/dist/core/cinnabun-factory.js.map +1 -0
  115. package/dist/core/config-loader.d.ts +2 -0
  116. package/dist/core/config-loader.js +76 -0
  117. package/dist/core/config-loader.js.map +1 -0
  118. package/dist/core/config.d.ts +12 -0
  119. package/dist/core/config.js +27 -0
  120. package/dist/core/config.js.map +1 -0
  121. package/dist/core/container.d.ts +10 -0
  122. package/dist/core/container.js +82 -0
  123. package/dist/core/container.js.map +1 -0
  124. package/dist/core/dependency-validator.d.ts +12 -0
  125. package/dist/core/dependency-validator.js +76 -0
  126. package/dist/core/dependency-validator.js.map +1 -0
  127. package/dist/core/guard.d.ts +3 -0
  128. package/dist/core/guard.js +2 -0
  129. package/dist/core/guard.js.map +1 -0
  130. package/dist/core/interceptor.d.ts +4 -0
  131. package/dist/core/interceptor.js +2 -0
  132. package/dist/core/interceptor.js.map +1 -0
  133. package/dist/core/logger.d.ts +15 -0
  134. package/dist/core/logger.js +71 -0
  135. package/dist/core/logger.js.map +1 -0
  136. package/dist/core/module-resolver.d.ts +6 -0
  137. package/dist/core/module-resolver.js +67 -0
  138. package/dist/core/module-resolver.js.map +1 -0
  139. package/dist/core/plugin.d.ts +12 -0
  140. package/dist/core/plugin.js +2 -0
  141. package/dist/core/plugin.js.map +1 -0
  142. package/dist/core/router.d.ts +38 -0
  143. package/dist/core/router.js +406 -0
  144. package/dist/core/router.js.map +1 -0
  145. package/dist/core/scanner.d.ts +7 -0
  146. package/dist/core/scanner.js +83 -0
  147. package/dist/core/scanner.js.map +1 -0
  148. package/dist/core/shutdown-manager.d.ts +15 -0
  149. package/dist/core/shutdown-manager.js +68 -0
  150. package/dist/core/shutdown-manager.js.map +1 -0
  151. package/dist/core/websocket-handler.d.ts +41 -0
  152. package/dist/core/websocket-handler.js +242 -0
  153. package/dist/core/websocket-handler.js.map +1 -0
  154. package/dist/decorators/autowired.d.ts +3 -0
  155. package/dist/decorators/autowired.js +11 -0
  156. package/dist/decorators/autowired.js.map +1 -0
  157. package/dist/decorators/cinnabun-application.d.ts +14 -0
  158. package/dist/decorators/cinnabun-application.js +17 -0
  159. package/dist/decorators/cinnabun-application.js.map +1 -0
  160. package/dist/decorators/lifecycle.d.ts +2 -0
  161. package/dist/decorators/lifecycle.js +12 -0
  162. package/dist/decorators/lifecycle.js.map +1 -0
  163. package/dist/decorators/middleware.d.ts +2 -0
  164. package/dist/decorators/middleware.js +12 -0
  165. package/dist/decorators/middleware.js.map +1 -0
  166. package/dist/decorators/module.d.ts +10 -0
  167. package/dist/decorators/module.js +13 -0
  168. package/dist/decorators/module.js.map +1 -0
  169. package/dist/decorators/on-shutdown.d.ts +1 -0
  170. package/dist/decorators/on-shutdown.js +10 -0
  171. package/dist/decorators/on-shutdown.js.map +1 -0
  172. package/dist/decorators/params.d.ts +6 -0
  173. package/dist/decorators/params.js +31 -0
  174. package/dist/decorators/params.js.map +1 -0
  175. package/dist/decorators/request-mapping.d.ts +7 -0
  176. package/dist/decorators/request-mapping.js +34 -0
  177. package/dist/decorators/request-mapping.js.map +1 -0
  178. package/dist/decorators/response.d.ts +2 -0
  179. package/dist/decorators/response.js +17 -0
  180. package/dist/decorators/response.js.map +1 -0
  181. package/dist/decorators/rest-controller.d.ts +1 -0
  182. package/dist/decorators/rest-controller.js +19 -0
  183. package/dist/decorators/rest-controller.js.map +1 -0
  184. package/dist/decorators/routes.d.ts +5 -0
  185. package/dist/decorators/routes.js +19 -0
  186. package/dist/decorators/routes.js.map +1 -0
  187. package/dist/decorators/service.d.ts +1 -0
  188. package/dist/decorators/service.js +7 -0
  189. package/dist/decorators/service.js.map +1 -0
  190. package/dist/decorators/use-guard.d.ts +2 -0
  191. package/dist/decorators/use-guard.js +12 -0
  192. package/dist/decorators/use-guard.js.map +1 -0
  193. package/dist/decorators/use-interceptor.d.ts +2 -0
  194. package/dist/decorators/use-interceptor.js +12 -0
  195. package/dist/decorators/use-interceptor.js.map +1 -0
  196. package/dist/decorators/validate.d.ts +12 -0
  197. package/dist/decorators/validate.js +7 -0
  198. package/dist/decorators/validate.js.map +1 -0
  199. package/dist/decorators/websocket.d.ts +9 -0
  200. package/dist/decorators/websocket.js +38 -0
  201. package/dist/decorators/websocket.js.map +1 -0
  202. package/dist/decorators/ws-event.d.ts +28 -0
  203. package/dist/decorators/ws-event.js +37 -0
  204. package/dist/decorators/ws-event.js.map +1 -0
  205. package/dist/decorators/ws-gateway.d.ts +18 -0
  206. package/dist/decorators/ws-gateway.js +24 -0
  207. package/dist/decorators/ws-gateway.js.map +1 -0
  208. package/dist/dev/index.d.ts +6 -0
  209. package/dist/dev/index.js +28 -0
  210. package/dist/dev/index.js.map +1 -0
  211. package/dist/exceptions/circular-dependency-error.d.ts +5 -0
  212. package/dist/exceptions/circular-dependency-error.js +16 -0
  213. package/dist/exceptions/circular-dependency-error.js.map +1 -0
  214. package/dist/exceptions/http-exception.d.ts +41 -0
  215. package/dist/exceptions/http-exception.js +96 -0
  216. package/dist/exceptions/http-exception.js.map +1 -0
  217. package/dist/guards/jwt-websocket.guard.d.ts +11 -0
  218. package/dist/guards/jwt-websocket.guard.js +37 -0
  219. package/dist/guards/jwt-websocket.guard.js.map +1 -0
  220. package/dist/guards/websocket-auth.guard.d.ts +16 -0
  221. package/dist/guards/websocket-auth.guard.js +43 -0
  222. package/dist/guards/websocket-auth.guard.js.map +1 -0
  223. package/dist/health/health-check.service.d.ts +45 -0
  224. package/dist/health/health-check.service.js +95 -0
  225. package/dist/health/health-check.service.js.map +1 -0
  226. package/dist/health/health.controller.d.ts +15 -0
  227. package/dist/health/health.controller.js +63 -0
  228. package/dist/health/health.controller.js.map +1 -0
  229. package/dist/health/health.module.d.ts +2 -0
  230. package/dist/health/health.module.js +20 -0
  231. package/dist/health/health.module.js.map +1 -0
  232. package/dist/index.d.ts +74 -11
  233. package/dist/index.js +54 -0
  234. package/dist/index.js.map +1 -0
  235. package/dist/metadata/storage.d.ts +171 -0
  236. package/dist/metadata/storage.js +257 -0
  237. package/dist/metadata/storage.js.map +1 -0
  238. package/dist/middleware/compression.middleware.d.ts +32 -0
  239. package/dist/middleware/compression.middleware.js +113 -0
  240. package/dist/middleware/compression.middleware.js.map +1 -0
  241. package/dist/middleware/cors.middleware.d.ts +18 -0
  242. package/dist/middleware/cors.middleware.js +79 -0
  243. package/dist/middleware/cors.middleware.js.map +1 -0
  244. package/dist/middleware/performance-tracker.middleware.d.ts +35 -0
  245. package/dist/middleware/performance-tracker.middleware.js +79 -0
  246. package/dist/middleware/performance-tracker.middleware.js.map +1 -0
  247. package/dist/middleware/request-logger.middleware.d.ts +32 -0
  248. package/dist/middleware/request-logger.middleware.js +125 -0
  249. package/dist/middleware/request-logger.middleware.js.map +1 -0
  250. package/dist/types/index.d.ts +14 -0
  251. package/dist/types/index.js +5 -0
  252. package/dist/types/index.js.map +1 -0
  253. package/dist/validation/helpers.d.ts +36 -0
  254. package/dist/validation/helpers.js +27 -0
  255. package/dist/validation/helpers.js.map +1 -0
  256. package/dist/websocket/error.d.ts +27 -0
  257. package/dist/websocket/error.js +38 -0
  258. package/dist/websocket/error.js.map +1 -0
  259. package/package.json +38 -5
  260. 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