create-fluxstack 1.10.1 → 1.12.0

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 (257) hide show
  1. package/.dockerignore +1 -2
  2. package/Dockerfile +8 -8
  3. package/LLMD/INDEX.md +64 -0
  4. package/LLMD/MAINTENANCE.md +197 -0
  5. package/LLMD/MIGRATION.md +156 -0
  6. package/LLMD/config/.gitkeep +1 -0
  7. package/LLMD/config/declarative-system.md +268 -0
  8. package/LLMD/config/environment-vars.md +327 -0
  9. package/LLMD/config/runtime-reload.md +401 -0
  10. package/LLMD/core/.gitkeep +1 -0
  11. package/LLMD/core/build-system.md +599 -0
  12. package/LLMD/core/framework-lifecycle.md +229 -0
  13. package/LLMD/core/plugin-system.md +451 -0
  14. package/LLMD/patterns/.gitkeep +1 -0
  15. package/LLMD/patterns/anti-patterns.md +297 -0
  16. package/LLMD/patterns/project-structure.md +264 -0
  17. package/LLMD/patterns/type-safety.md +440 -0
  18. package/LLMD/reference/.gitkeep +1 -0
  19. package/LLMD/reference/cli-commands.md +250 -0
  20. package/LLMD/reference/plugin-hooks.md +357 -0
  21. package/LLMD/reference/routing.md +39 -0
  22. package/LLMD/reference/troubleshooting.md +364 -0
  23. package/LLMD/resources/.gitkeep +1 -0
  24. package/LLMD/resources/controllers.md +465 -0
  25. package/LLMD/resources/live-components.md +703 -0
  26. package/LLMD/resources/live-rooms.md +482 -0
  27. package/LLMD/resources/live-upload.md +130 -0
  28. package/LLMD/resources/plugins-external.md +617 -0
  29. package/LLMD/resources/routes-eden.md +254 -0
  30. package/README.md +37 -17
  31. package/app/client/index.html +0 -1
  32. package/app/client/src/App.tsx +107 -150
  33. package/app/client/src/components/AppLayout.tsx +68 -0
  34. package/app/client/src/components/BackButton.tsx +13 -0
  35. package/app/client/src/components/DemoPage.tsx +20 -0
  36. package/app/client/src/components/LiveUploadWidget.tsx +204 -0
  37. package/app/client/src/lib/eden-api.ts +85 -60
  38. package/app/client/src/live/ChatDemo.tsx +107 -0
  39. package/app/client/src/live/CounterDemo.tsx +206 -0
  40. package/app/client/src/live/FormDemo.tsx +119 -0
  41. package/app/client/src/live/RoomChatDemo.tsx +242 -0
  42. package/app/client/src/live/UploadDemo.tsx +21 -0
  43. package/app/client/src/main.tsx +4 -1
  44. package/app/client/src/pages/ApiTestPage.tsx +108 -0
  45. package/app/client/src/pages/HomePage.tsx +76 -0
  46. package/app/server/app.ts +1 -4
  47. package/app/server/controllers/users.controller.ts +36 -44
  48. package/app/server/index.ts +25 -35
  49. package/app/server/live/LiveChat.ts +77 -0
  50. package/app/server/live/LiveCounter.ts +67 -0
  51. package/app/server/live/LiveForm.ts +63 -0
  52. package/app/server/live/LiveLocalCounter.ts +32 -0
  53. package/app/server/live/LiveRoomChat.ts +285 -0
  54. package/app/server/live/LiveUpload.ts +81 -0
  55. package/app/server/routes/index.ts +3 -1
  56. package/app/server/routes/room.routes.ts +117 -0
  57. package/app/server/routes/users.routes.ts +35 -27
  58. package/app/shared/types/index.ts +14 -2
  59. package/config/app.config.ts +2 -62
  60. package/config/client.config.ts +2 -95
  61. package/config/database.config.ts +2 -99
  62. package/config/fluxstack.config.ts +25 -45
  63. package/config/index.ts +57 -38
  64. package/config/monitoring.config.ts +2 -114
  65. package/config/plugins.config.ts +2 -80
  66. package/config/server.config.ts +2 -68
  67. package/config/services.config.ts +2 -130
  68. package/config/system/app.config.ts +29 -0
  69. package/config/system/build.config.ts +49 -0
  70. package/config/system/client.config.ts +68 -0
  71. package/config/system/database.config.ts +17 -0
  72. package/config/system/fluxstack.config.ts +114 -0
  73. package/config/{logger.config.ts → system/logger.config.ts} +3 -1
  74. package/config/system/monitoring.config.ts +114 -0
  75. package/config/system/plugins.config.ts +84 -0
  76. package/config/{runtime.config.ts → system/runtime.config.ts} +1 -1
  77. package/config/system/server.config.ts +68 -0
  78. package/config/system/services.config.ts +46 -0
  79. package/config/{system.config.ts → system/system.config.ts} +1 -1
  80. package/core/build/flux-plugins-generator.ts +325 -325
  81. package/core/build/index.ts +39 -27
  82. package/core/build/live-components-generator.ts +3 -3
  83. package/core/build/optimizer.ts +235 -235
  84. package/core/cli/command-registry.ts +6 -4
  85. package/core/cli/commands/build.ts +79 -0
  86. package/core/cli/commands/create.ts +54 -0
  87. package/core/cli/commands/dev.ts +101 -0
  88. package/core/cli/commands/help.ts +34 -0
  89. package/core/cli/commands/index.ts +34 -0
  90. package/core/cli/commands/make-plugin.ts +90 -0
  91. package/core/cli/commands/plugin-add.ts +197 -0
  92. package/core/cli/commands/plugin-deps.ts +2 -2
  93. package/core/cli/commands/plugin-list.ts +208 -0
  94. package/core/cli/commands/plugin-remove.ts +170 -0
  95. package/core/cli/generators/component.ts +769 -769
  96. package/core/cli/generators/controller.ts +1 -1
  97. package/core/cli/generators/index.ts +146 -146
  98. package/core/cli/generators/interactive.ts +227 -227
  99. package/core/cli/generators/plugin.ts +2 -2
  100. package/core/cli/generators/prompts.ts +82 -82
  101. package/core/cli/generators/route.ts +6 -6
  102. package/core/cli/generators/service.ts +2 -2
  103. package/core/cli/generators/template-engine.ts +4 -3
  104. package/core/cli/generators/types.ts +2 -2
  105. package/core/cli/generators/utils.ts +191 -191
  106. package/core/cli/index.ts +115 -686
  107. package/core/cli/plugin-discovery.ts +2 -2
  108. package/core/client/LiveComponentsProvider.tsx +60 -8
  109. package/core/client/api/eden.ts +183 -0
  110. package/core/client/api/index.ts +11 -0
  111. package/core/client/components/Live.tsx +104 -0
  112. package/core/client/fluxstack.ts +1 -9
  113. package/core/client/hooks/AdaptiveChunkSizer.ts +215 -215
  114. package/core/client/hooks/state-validator.ts +1 -1
  115. package/core/client/hooks/useAuth.ts +48 -48
  116. package/core/client/hooks/useChunkedUpload.ts +85 -35
  117. package/core/client/hooks/useLiveChunkedUpload.ts +87 -0
  118. package/core/client/hooks/useLiveComponent.ts +800 -0
  119. package/core/client/hooks/useLiveUpload.ts +71 -0
  120. package/core/client/hooks/useRoom.ts +409 -0
  121. package/core/client/hooks/useRoomProxy.ts +382 -0
  122. package/core/client/index.ts +17 -68
  123. package/core/client/standalone-entry.ts +8 -0
  124. package/core/client/standalone.ts +74 -53
  125. package/core/client/state/createStore.ts +192 -192
  126. package/core/client/state/index.ts +14 -14
  127. package/core/config/index.ts +70 -291
  128. package/core/config/schema.ts +42 -723
  129. package/core/framework/client.ts +131 -131
  130. package/core/framework/index.ts +7 -7
  131. package/core/framework/server.ts +47 -40
  132. package/core/framework/types.ts +2 -2
  133. package/core/index.ts +23 -4
  134. package/core/live/ComponentRegistry.ts +3 -3
  135. package/core/live/types.ts +77 -0
  136. package/core/plugins/built-in/index.ts +134 -134
  137. package/core/plugins/built-in/live-components/commands/create-live-component.ts +242 -1066
  138. package/core/plugins/built-in/live-components/index.ts +1 -1
  139. package/core/plugins/built-in/monitoring/index.ts +111 -47
  140. package/core/plugins/built-in/static/index.ts +1 -1
  141. package/core/plugins/built-in/swagger/index.ts +68 -265
  142. package/core/plugins/built-in/vite/index.ts +85 -185
  143. package/core/plugins/built-in/vite/vite-dev.ts +10 -16
  144. package/core/plugins/config.ts +9 -7
  145. package/core/plugins/dependency-manager.ts +31 -1
  146. package/core/plugins/discovery.ts +19 -7
  147. package/core/plugins/executor.ts +2 -2
  148. package/core/plugins/index.ts +203 -203
  149. package/core/plugins/manager.ts +27 -39
  150. package/core/plugins/module-resolver.ts +19 -8
  151. package/core/plugins/registry.ts +255 -19
  152. package/core/plugins/types.ts +20 -53
  153. package/core/server/framework.ts +66 -43
  154. package/core/server/index.ts +15 -15
  155. package/core/server/live/ComponentRegistry.ts +78 -71
  156. package/core/server/live/FileUploadManager.ts +23 -10
  157. package/core/server/live/LiveComponentPerformanceMonitor.ts +1 -1
  158. package/core/server/live/LiveRoomManager.ts +261 -0
  159. package/core/server/live/RoomEventBus.ts +234 -0
  160. package/core/server/live/RoomStateManager.ts +172 -0
  161. package/core/server/live/StateSignature.ts +643 -643
  162. package/core/server/live/WebSocketConnectionManager.ts +30 -19
  163. package/core/server/live/auto-generated-components.ts +21 -9
  164. package/core/server/live/index.ts +14 -0
  165. package/core/server/live/websocket-plugin.ts +214 -67
  166. package/core/server/middleware/elysia-helpers.ts +7 -2
  167. package/core/server/middleware/errorHandling.ts +1 -1
  168. package/core/server/middleware/index.ts +31 -31
  169. package/core/server/plugins/database.ts +180 -180
  170. package/core/server/plugins/static-files-plugin.ts +69 -69
  171. package/core/server/plugins/swagger.ts +1 -1
  172. package/core/server/rooms/RoomBroadcaster.ts +357 -0
  173. package/core/server/rooms/RoomSystem.ts +463 -0
  174. package/core/server/rooms/index.ts +13 -0
  175. package/core/server/services/BaseService.ts +1 -1
  176. package/core/server/services/ServiceContainer.ts +1 -1
  177. package/core/server/services/index.ts +8 -8
  178. package/core/templates/create-project.ts +12 -12
  179. package/core/testing/index.ts +9 -9
  180. package/core/testing/setup.ts +73 -73
  181. package/core/types/api.ts +168 -168
  182. package/core/types/build.ts +219 -219
  183. package/core/types/config.ts +56 -26
  184. package/core/types/index.ts +4 -4
  185. package/core/types/plugin.ts +107 -107
  186. package/core/types/types.ts +353 -14
  187. package/core/utils/build-logger.ts +324 -324
  188. package/core/utils/config-schema.ts +480 -480
  189. package/core/utils/env.ts +2 -8
  190. package/core/utils/errors/codes.ts +114 -114
  191. package/core/utils/errors/handlers.ts +36 -1
  192. package/core/utils/errors/index.ts +49 -5
  193. package/core/utils/errors/middleware.ts +113 -113
  194. package/core/utils/helpers.ts +6 -16
  195. package/core/utils/index.ts +17 -17
  196. package/core/utils/logger/colors.ts +114 -114
  197. package/core/utils/logger/config.ts +13 -9
  198. package/core/utils/logger/formatter.ts +82 -82
  199. package/core/utils/logger/group-logger.ts +101 -101
  200. package/core/utils/logger/index.ts +6 -1
  201. package/core/utils/logger/stack-trace.ts +3 -1
  202. package/core/utils/logger/startup-banner.ts +82 -82
  203. package/core/utils/logger/winston-logger.ts +152 -152
  204. package/core/utils/monitoring/index.ts +211 -211
  205. package/core/utils/sync-version.ts +66 -66
  206. package/core/utils/version.ts +1 -1
  207. package/create-fluxstack.ts +8 -7
  208. package/package.json +12 -13
  209. package/plugins/crypto-auth/cli/make-protected-route.command.ts +1 -1
  210. package/plugins/crypto-auth/client/CryptoAuthClient.ts +302 -302
  211. package/plugins/crypto-auth/client/components/index.ts +11 -11
  212. package/plugins/crypto-auth/client/index.ts +11 -11
  213. package/plugins/crypto-auth/config/index.ts +1 -1
  214. package/plugins/crypto-auth/index.ts +4 -4
  215. package/plugins/crypto-auth/package.json +65 -65
  216. package/plugins/crypto-auth/server/AuthMiddleware.ts +1 -1
  217. package/plugins/crypto-auth/server/CryptoAuthService.ts +185 -185
  218. package/plugins/crypto-auth/server/index.ts +21 -21
  219. package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +3 -3
  220. package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +1 -1
  221. package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +2 -2
  222. package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +2 -2
  223. package/plugins/crypto-auth/server/middlewares/helpers.ts +1 -1
  224. package/plugins/crypto-auth/server/middlewares/index.ts +22 -22
  225. package/tsconfig.api-strict.json +16 -0
  226. package/tsconfig.json +48 -52
  227. package/{app/client/tsconfig.node.json → tsconfig.node.json} +25 -25
  228. package/types/global.d.ts +29 -29
  229. package/types/vitest.d.ts +8 -8
  230. package/vite.config.ts +38 -62
  231. package/vitest.config.live.ts +10 -9
  232. package/vitest.config.ts +29 -17
  233. package/app/client/README.md +0 -69
  234. package/app/client/SIMPLIFICATION.md +0 -140
  235. package/app/client/frontend-only.ts +0 -12
  236. package/app/client/src/live/FileUploadExample.tsx +0 -359
  237. package/app/client/src/live/MinimalLiveClock.tsx +0 -47
  238. package/app/client/src/live/QuickUploadTest.tsx +0 -193
  239. package/app/client/tsconfig.app.json +0 -45
  240. package/app/client/tsconfig.json +0 -7
  241. package/app/client/zustand-setup.md +0 -65
  242. package/app/server/backend-only.ts +0 -18
  243. package/app/server/live/LiveClockComponent.ts +0 -215
  244. package/app/server/live/LiveFileUploadComponent.ts +0 -77
  245. package/app/server/routes/env-test.ts +0 -110
  246. package/core/client/hooks/index.ts +0 -7
  247. package/core/client/hooks/useHybridLiveComponent.ts +0 -685
  248. package/core/client/hooks/useTypedLiveComponent.ts +0 -133
  249. package/core/client/hooks/useWebSocket.ts +0 -361
  250. package/core/config/env.ts +0 -546
  251. package/core/config/loader.ts +0 -522
  252. package/core/config/runtime-config.ts +0 -327
  253. package/core/config/validator.ts +0 -540
  254. package/core/server/backend-entry.ts +0 -51
  255. package/core/server/standalone.ts +0 -106
  256. package/core/utils/regenerate-files.ts +0 -69
  257. package/fluxstack.config.ts +0 -354
@@ -0,0 +1,357 @@
1
+ // 🔥 FluxStack Room Broadcaster - WebSocket integration for rooms
2
+
3
+ import type { ServerWebSocket } from 'bun'
4
+ import type { Room, RoomSystem, SystemEvents } from './RoomSystem'
5
+
6
+ type WebSocketLike = {
7
+ send: (data: string) => void
8
+ readyState?: number
9
+ }
10
+
11
+ interface RoomConnection {
12
+ id: string
13
+ ws: WebSocketLike
14
+ rooms: Set<string>
15
+ userId?: string
16
+ }
17
+
18
+ interface BroadcastMessage {
19
+ type: 'room:event' | 'room:state' | 'room:system'
20
+ roomId: string
21
+ event: string
22
+ data: any
23
+ timestamp: number
24
+ senderId?: string
25
+ }
26
+
27
+ export class RoomBroadcaster<TState, TEvents extends Record<string, any>> {
28
+ private connections = new Map<string, RoomConnection>()
29
+ private roomConnections = new Map<string, Set<string>>() // roomId -> connectionIds
30
+ private roomSystem: RoomSystem<TState, TEvents>
31
+ private systemListeners: (() => void)[] = []
32
+
33
+ constructor(roomSystem: RoomSystem<TState, TEvents>) {
34
+ this.roomSystem = roomSystem
35
+ this.setupSystemListeners()
36
+ }
37
+
38
+ private setupSystemListeners(): void {
39
+ // Escutar eventos globais do sistema
40
+ const unsub1 = this.roomSystem.on('$room:destroyed', ({ roomId }) => {
41
+ // Limpar conexões quando sala é destruída
42
+ this.roomConnections.delete(roomId)
43
+ })
44
+
45
+ this.systemListeners.push(unsub1)
46
+ }
47
+
48
+ // ============================================
49
+ // Gerenciamento de conexões
50
+ // ============================================
51
+
52
+ /**
53
+ * Registra uma nova conexão WebSocket
54
+ */
55
+ connect(ws: WebSocketLike, userId?: string): string {
56
+ const connectionId = this.generateId()
57
+
58
+ this.connections.set(connectionId, {
59
+ id: connectionId,
60
+ ws,
61
+ rooms: new Set(),
62
+ userId
63
+ })
64
+
65
+ return connectionId
66
+ }
67
+
68
+ /**
69
+ * Remove uma conexão
70
+ */
71
+ disconnect(connectionId: string): void {
72
+ const connection = this.connections.get(connectionId)
73
+ if (!connection) return
74
+
75
+ // Sair de todas as salas
76
+ for (const roomId of connection.rooms) {
77
+ this.leave(connectionId, roomId)
78
+ }
79
+
80
+ this.connections.delete(connectionId)
81
+ }
82
+
83
+ /**
84
+ * Entra em uma sala
85
+ */
86
+ join(connectionId: string, roomId: string, initialState: TState): Room<TState, TEvents> | null {
87
+ const connection = this.connections.get(connectionId)
88
+ if (!connection) return null
89
+
90
+ // Adicionar à sala
91
+ connection.rooms.add(roomId)
92
+
93
+ if (!this.roomConnections.has(roomId)) {
94
+ this.roomConnections.set(roomId, new Set())
95
+ }
96
+ this.roomConnections.get(roomId)!.add(connectionId)
97
+
98
+ // Obter ou criar sala
99
+ const room = this.roomSystem.getOrCreate(roomId, initialState)
100
+
101
+ // Enviar estado atual para o cliente
102
+ this.sendTo(connectionId, {
103
+ type: 'room:state',
104
+ roomId,
105
+ event: '$state:sync',
106
+ data: { state: room.state },
107
+ timestamp: Date.now()
108
+ })
109
+
110
+ return room
111
+ }
112
+
113
+ /**
114
+ * Sai de uma sala
115
+ */
116
+ leave(connectionId: string, roomId: string): boolean {
117
+ const connection = this.connections.get(connectionId)
118
+ if (!connection) return false
119
+
120
+ connection.rooms.delete(roomId)
121
+
122
+ const roomConns = this.roomConnections.get(roomId)
123
+ if (roomConns) {
124
+ roomConns.delete(connectionId)
125
+ if (roomConns.size === 0) {
126
+ this.roomConnections.delete(roomId)
127
+ }
128
+ }
129
+
130
+ return true
131
+ }
132
+
133
+ /**
134
+ * Retorna as salas de uma conexão
135
+ */
136
+ getRooms(connectionId: string): string[] {
137
+ const connection = this.connections.get(connectionId)
138
+ return connection ? Array.from(connection.rooms) : []
139
+ }
140
+
141
+ /**
142
+ * Verifica se conexão está em uma sala
143
+ */
144
+ isInRoom(connectionId: string, roomId: string): boolean {
145
+ const connection = this.connections.get(connectionId)
146
+ return connection?.rooms.has(roomId) ?? false
147
+ }
148
+
149
+ // ============================================
150
+ // Broadcast
151
+ // ============================================
152
+
153
+ /**
154
+ * Envia evento para todos na sala
155
+ */
156
+ broadcast(
157
+ roomId: string,
158
+ event: string,
159
+ data: any,
160
+ options?: { exclude?: string | string[] }
161
+ ): number {
162
+ const roomConns = this.roomConnections.get(roomId)
163
+ if (!roomConns || roomConns.size === 0) return 0
164
+
165
+ const excludeSet = new Set(
166
+ Array.isArray(options?.exclude)
167
+ ? options.exclude
168
+ : options?.exclude
169
+ ? [options.exclude]
170
+ : []
171
+ )
172
+
173
+ const message: BroadcastMessage = {
174
+ type: 'room:event',
175
+ roomId,
176
+ event,
177
+ data,
178
+ timestamp: Date.now()
179
+ }
180
+
181
+ let sent = 0
182
+ for (const connId of roomConns) {
183
+ if (excludeSet.has(connId)) continue
184
+
185
+ if (this.sendTo(connId, message)) {
186
+ sent++
187
+ }
188
+ }
189
+
190
+ return sent
191
+ }
192
+
193
+ /**
194
+ * Broadcast de mudança de estado
195
+ */
196
+ broadcastState(roomId: string, state: Partial<TState>, exclude?: string): number {
197
+ return this.broadcast(roomId, '$state:update', { state }, { exclude })
198
+ }
199
+
200
+ /**
201
+ * Envia evento do sistema para todos na sala
202
+ */
203
+ broadcastSystem(roomId: string, event: string, data: any): number {
204
+ const roomConns = this.roomConnections.get(roomId)
205
+ if (!roomConns || roomConns.size === 0) return 0
206
+
207
+ const message: BroadcastMessage = {
208
+ type: 'room:system',
209
+ roomId,
210
+ event,
211
+ data,
212
+ timestamp: Date.now()
213
+ }
214
+
215
+ let sent = 0
216
+ for (const connId of roomConns) {
217
+ if (this.sendTo(connId, message)) {
218
+ sent++
219
+ }
220
+ }
221
+
222
+ return sent
223
+ }
224
+
225
+ /**
226
+ * Envia para uma conexão específica
227
+ */
228
+ sendTo(connectionId: string, message: BroadcastMessage): boolean {
229
+ const connection = this.connections.get(connectionId)
230
+ if (!connection) return false
231
+
232
+ try {
233
+ // Verificar se WebSocket está aberto (readyState 1 = OPEN)
234
+ if (connection.ws.readyState !== undefined && connection.ws.readyState !== 1) {
235
+ return false
236
+ }
237
+
238
+ connection.ws.send(JSON.stringify(message))
239
+ return true
240
+ } catch (error) {
241
+ console.error(`[RoomBroadcaster] Error sending to ${connectionId}:`, error)
242
+ return false
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Envia para um usuário específico (todas as conexões do usuário)
248
+ */
249
+ sendToUser(userId: string, roomId: string, event: string, data: any): number {
250
+ let sent = 0
251
+
252
+ for (const [connId, conn] of this.connections) {
253
+ if (conn.userId === userId && conn.rooms.has(roomId)) {
254
+ if (this.sendTo(connId, {
255
+ type: 'room:event',
256
+ roomId,
257
+ event,
258
+ data,
259
+ timestamp: Date.now()
260
+ })) {
261
+ sent++
262
+ }
263
+ }
264
+ }
265
+
266
+ return sent
267
+ }
268
+
269
+ // ============================================
270
+ // Helpers para uso com Room
271
+ // ============================================
272
+
273
+ /**
274
+ * Cria um wrapper que faz broadcast automático ao emitir eventos
275
+ */
276
+ createBroadcastingRoom(
277
+ roomId: string,
278
+ initialState: TState,
279
+ connectionId?: string
280
+ ): Room<TState, TEvents> & { broadcastEmit: (event: keyof TEvents, data: any) => void } {
281
+ const room = this.roomSystem.getOrCreate(roomId, initialState)
282
+
283
+ // Adicionar método de broadcast
284
+ const enhanced = room as Room<TState, TEvents> & {
285
+ broadcastEmit: (event: keyof TEvents, data: any) => void
286
+ }
287
+
288
+ enhanced.broadcastEmit = (event: keyof TEvents, data: any) => {
289
+ // Emitir localmente
290
+ room.emit(event, data)
291
+ // Broadcast via WebSocket
292
+ this.broadcast(roomId, event as string, data, { exclude: connectionId })
293
+ }
294
+
295
+ // Auto-broadcast mudanças de estado
296
+ room.on('$state:change', ({ path, newValue }) => {
297
+ this.broadcast(roomId, '$state:change', { path, newValue }, { exclude: connectionId })
298
+ })
299
+
300
+ return enhanced
301
+ }
302
+
303
+ // ============================================
304
+ // Estatísticas
305
+ // ============================================
306
+
307
+ getStats(): {
308
+ totalConnections: number
309
+ totalRoomMemberships: number
310
+ rooms: Record<string, { connections: number }>
311
+ } {
312
+ const rooms: Record<string, { connections: number }> = {}
313
+
314
+ for (const [roomId, conns] of this.roomConnections) {
315
+ rooms[roomId] = { connections: conns.size }
316
+ }
317
+
318
+ let totalMemberships = 0
319
+ for (const conn of this.connections.values()) {
320
+ totalMemberships += conn.rooms.size
321
+ }
322
+
323
+ return {
324
+ totalConnections: this.connections.size,
325
+ totalRoomMemberships: totalMemberships,
326
+ rooms
327
+ }
328
+ }
329
+
330
+ // ============================================
331
+ // Cleanup
332
+ // ============================================
333
+
334
+ destroy(): void {
335
+ for (const unsub of this.systemListeners) {
336
+ unsub()
337
+ }
338
+ this.systemListeners = []
339
+ this.connections.clear()
340
+ this.roomConnections.clear()
341
+ }
342
+
343
+ private generateId(): string {
344
+ return `conn-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
345
+ }
346
+ }
347
+
348
+ // Factory
349
+ export function createRoomBroadcaster<
350
+ TDef extends { state: any; events: Record<string, any> }
351
+ >(
352
+ roomSystem: RoomSystem<TDef['state'], TDef['events']>
353
+ ): RoomBroadcaster<TDef['state'], TDef['events']> {
354
+ return new RoomBroadcaster(roomSystem)
355
+ }
356
+
357
+ export type { BroadcastMessage, RoomConnection }