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,234 @@
1
+ // 🔥 FluxStack Live - Room Event Bus (Pub/Sub server-side)
2
+
3
+ type EventHandler<T = any> = (data: T) => void
4
+
5
+ interface RoomSubscription {
6
+ roomType: string
7
+ roomId: string
8
+ event: string
9
+ handler: EventHandler
10
+ componentId: string
11
+ }
12
+
13
+ export function createTypedRoomEventBus<TRoomEvents extends Record<string, Record<string, any>>>() {
14
+ const subscriptions = new Map<string, Set<RoomSubscription>>()
15
+
16
+ const getKey = (roomType: string, roomId: string, event: string) =>
17
+ `${roomType}:${roomId}:${event}`
18
+
19
+ const getRoomKey = (roomType: string, roomId: string) =>
20
+ `${roomType}:${roomId}`
21
+
22
+ return {
23
+ on<K extends keyof TRoomEvents, E extends keyof TRoomEvents[K]>(
24
+ roomType: K,
25
+ roomId: string,
26
+ event: E,
27
+ componentId: string,
28
+ handler: EventHandler<TRoomEvents[K][E]>
29
+ ): () => void {
30
+ const key = getKey(roomType as string, roomId, event as string)
31
+
32
+ if (!subscriptions.has(key)) {
33
+ subscriptions.set(key, new Set())
34
+ }
35
+
36
+ const subscription: RoomSubscription = {
37
+ roomType: roomType as string,
38
+ roomId,
39
+ event: event as string,
40
+ handler,
41
+ componentId
42
+ }
43
+
44
+ subscriptions.get(key)!.add(subscription)
45
+
46
+ return () => {
47
+ subscriptions.get(key)?.delete(subscription)
48
+ if (subscriptions.get(key)?.size === 0) {
49
+ subscriptions.delete(key)
50
+ }
51
+ }
52
+ },
53
+
54
+ emit<K extends keyof TRoomEvents, E extends keyof TRoomEvents[K]>(
55
+ roomType: K,
56
+ roomId: string,
57
+ event: E,
58
+ data: TRoomEvents[K][E],
59
+ excludeComponentId?: string
60
+ ): number {
61
+ const key = getKey(roomType as string, roomId, event as string)
62
+ const subs = subscriptions.get(key)
63
+
64
+ if (!subs || subs.size === 0) return 0
65
+
66
+ let notified = 0
67
+ for (const sub of subs) {
68
+ if (excludeComponentId && sub.componentId === excludeComponentId) continue
69
+
70
+ try {
71
+ sub.handler(data)
72
+ notified++
73
+ } catch (error) {
74
+ console.error(`❌ RoomEventBus error [${key}]:`, error)
75
+ }
76
+ }
77
+
78
+ return notified
79
+ },
80
+
81
+ unsubscribeAll(componentId: string): number {
82
+ let removed = 0
83
+
84
+ for (const [key, subs] of subscriptions) {
85
+ for (const sub of subs) {
86
+ if (sub.componentId === componentId) {
87
+ subs.delete(sub)
88
+ removed++
89
+ }
90
+ }
91
+ if (subs.size === 0) {
92
+ subscriptions.delete(key)
93
+ }
94
+ }
95
+
96
+ return removed
97
+ },
98
+
99
+ clearRoom<K extends keyof TRoomEvents>(roomType: K, roomId: string): number {
100
+ const prefix = getRoomKey(roomType as string, roomId)
101
+ let removed = 0
102
+
103
+ for (const key of subscriptions.keys()) {
104
+ if (key.startsWith(prefix)) {
105
+ removed += subscriptions.get(key)?.size ?? 0
106
+ subscriptions.delete(key)
107
+ }
108
+ }
109
+
110
+ return removed
111
+ },
112
+
113
+ getStats(): { totalSubscriptions: number; rooms: Record<string, { events: Record<string, number> }> } {
114
+ const rooms: Record<string, { events: Record<string, number> }> = {}
115
+ let total = 0
116
+
117
+ for (const [key, subs] of subscriptions) {
118
+ const [roomType, roomId, event] = key.split(':')
119
+ const roomKey = `${roomType}:${roomId}`
120
+
121
+ if (!rooms[roomKey]) {
122
+ rooms[roomKey] = { events: {} }
123
+ }
124
+
125
+ rooms[roomKey].events[event] = subs.size
126
+ total += subs.size
127
+ }
128
+
129
+ return { totalSubscriptions: total, rooms }
130
+ }
131
+ }
132
+ }
133
+
134
+ class RoomEventBus {
135
+ private subscriptions = new Map<string, Set<RoomSubscription>>()
136
+
137
+ private getKey(roomType: string, roomId: string, event: string): string {
138
+ return `${roomType}:${roomId}:${event}`
139
+ }
140
+
141
+ on(roomType: string, roomId: string, event: string, componentId: string, handler: EventHandler): () => void {
142
+ const key = this.getKey(roomType, roomId, event)
143
+
144
+ if (!this.subscriptions.has(key)) {
145
+ this.subscriptions.set(key, new Set())
146
+ }
147
+
148
+ const subscription: RoomSubscription = { roomType, roomId, event, handler, componentId }
149
+ this.subscriptions.get(key)!.add(subscription)
150
+
151
+ return () => {
152
+ this.subscriptions.get(key)?.delete(subscription)
153
+ if (this.subscriptions.get(key)?.size === 0) {
154
+ this.subscriptions.delete(key)
155
+ }
156
+ }
157
+ }
158
+
159
+ emit(roomType: string, roomId: string, event: string, data: any, excludeComponentId?: string): number {
160
+ const key = this.getKey(roomType, roomId, event)
161
+ const subs = this.subscriptions.get(key)
162
+
163
+ if (!subs || subs.size === 0) return 0
164
+
165
+ let notified = 0
166
+ for (const sub of subs) {
167
+ if (excludeComponentId && sub.componentId === excludeComponentId) continue
168
+
169
+ try {
170
+ sub.handler(data)
171
+ notified++
172
+ } catch (error) {
173
+ console.error(`❌ RoomEventBus error [${key}]:`, error)
174
+ }
175
+ }
176
+
177
+ return notified
178
+ }
179
+
180
+ unsubscribeAll(componentId: string): number {
181
+ let removed = 0
182
+
183
+ for (const [key, subs] of this.subscriptions) {
184
+ for (const sub of subs) {
185
+ if (sub.componentId === componentId) {
186
+ subs.delete(sub)
187
+ removed++
188
+ }
189
+ }
190
+ if (subs.size === 0) {
191
+ this.subscriptions.delete(key)
192
+ }
193
+ }
194
+
195
+ return removed
196
+ }
197
+
198
+ clearRoom(roomType: string, roomId: string): number {
199
+ const prefix = `${roomType}:${roomId}`
200
+ let removed = 0
201
+
202
+ for (const key of this.subscriptions.keys()) {
203
+ if (key.startsWith(prefix)) {
204
+ removed += this.subscriptions.get(key)?.size ?? 0
205
+ this.subscriptions.delete(key)
206
+ }
207
+ }
208
+
209
+ return removed
210
+ }
211
+
212
+ getStats() {
213
+ const rooms: Record<string, { events: Record<string, number> }> = {}
214
+ let total = 0
215
+
216
+ for (const [key, subs] of this.subscriptions) {
217
+ const [roomType, roomId, event] = key.split(':')
218
+ const roomKey = `${roomType}:${roomId}`
219
+
220
+ if (!rooms[roomKey]) {
221
+ rooms[roomKey] = { events: {} }
222
+ }
223
+
224
+ rooms[roomKey].events[event] = subs.size
225
+ total += subs.size
226
+ }
227
+
228
+ return { totalSubscriptions: total, rooms }
229
+ }
230
+ }
231
+
232
+ export const roomEvents = new RoomEventBus()
233
+
234
+ export type { EventHandler, RoomSubscription }
@@ -0,0 +1,172 @@
1
+ // 🔥 FluxStack Live - Room State Manager (In-memory storage per room)
2
+
3
+ type RoomStateData = Record<string, any>
4
+
5
+ interface RoomInfo {
6
+ state: RoomStateData
7
+ componentCount: number
8
+ createdAt: number
9
+ lastUpdate: number
10
+ }
11
+
12
+ export function createTypedRoomState<TRoomTypes extends Record<string, RoomStateData>>() {
13
+ const rooms = new Map<string, RoomInfo>()
14
+ const getKey = (type: string, roomId: string) => `${type}:${roomId}`
15
+
16
+ return {
17
+ get<K extends keyof TRoomTypes>(type: K, roomId: string, defaultState: TRoomTypes[K]): TRoomTypes[K] {
18
+ const key = getKey(type as string, roomId)
19
+ const room = rooms.get(key)
20
+
21
+ if (room) return room.state as TRoomTypes[K]
22
+
23
+ rooms.set(key, { state: defaultState, componentCount: 0, createdAt: Date.now(), lastUpdate: Date.now() })
24
+ return defaultState
25
+ },
26
+
27
+ update<K extends keyof TRoomTypes>(type: K, roomId: string, updates: Partial<TRoomTypes[K]>): TRoomTypes[K] {
28
+ const key = getKey(type as string, roomId)
29
+ const room = rooms.get(key)
30
+
31
+ if (room) {
32
+ room.state = { ...room.state, ...updates }
33
+ room.lastUpdate = Date.now()
34
+ return room.state as TRoomTypes[K]
35
+ }
36
+
37
+ const newState = updates as TRoomTypes[K]
38
+ rooms.set(key, { state: newState, componentCount: 0, createdAt: Date.now(), lastUpdate: Date.now() })
39
+ return newState
40
+ },
41
+
42
+ set<K extends keyof TRoomTypes>(type: K, roomId: string, state: TRoomTypes[K]): void {
43
+ const key = getKey(type as string, roomId)
44
+ const room = rooms.get(key)
45
+
46
+ if (room) {
47
+ room.state = state
48
+ room.lastUpdate = Date.now()
49
+ } else {
50
+ rooms.set(key, { state, componentCount: 0, createdAt: Date.now(), lastUpdate: Date.now() })
51
+ }
52
+ },
53
+
54
+ join<K extends keyof TRoomTypes>(type: K, roomId: string): void {
55
+ const room = rooms.get(getKey(type as string, roomId))
56
+ if (room) room.componentCount++
57
+ },
58
+
59
+ leave<K extends keyof TRoomTypes>(type: K, roomId: string): void {
60
+ const key = getKey(type as string, roomId)
61
+ const room = rooms.get(key)
62
+ if (room) {
63
+ room.componentCount--
64
+ if (room.componentCount <= 0) {
65
+ setTimeout(() => {
66
+ const current = rooms.get(key)
67
+ if (current && current.componentCount <= 0) {
68
+ rooms.delete(key)
69
+ }
70
+ }, 5 * 60 * 1000)
71
+ }
72
+ }
73
+ },
74
+
75
+ has<K extends keyof TRoomTypes>(type: K, roomId: string): boolean {
76
+ return rooms.has(getKey(type as string, roomId))
77
+ },
78
+
79
+ delete<K extends keyof TRoomTypes>(type: K, roomId: string): boolean {
80
+ return rooms.delete(getKey(type as string, roomId))
81
+ },
82
+
83
+ getStats(): { totalRooms: number; rooms: Record<string, { componentCount: number; stateKeys: string[] }> } {
84
+ const roomStats: Record<string, { componentCount: number; stateKeys: string[] }> = {}
85
+ for (const [key, info] of rooms) {
86
+ roomStats[key] = { componentCount: info.componentCount, stateKeys: Object.keys(info.state) }
87
+ }
88
+ return { totalRooms: rooms.size, rooms: roomStats }
89
+ }
90
+ }
91
+ }
92
+
93
+ class RoomStateManager {
94
+ private rooms = new Map<string, RoomInfo>()
95
+
96
+ get<T extends RoomStateData>(roomId: string, defaultState?: T): T {
97
+ const room = this.rooms.get(roomId)
98
+ if (room) return room.state as T
99
+
100
+ if (defaultState) {
101
+ this.rooms.set(roomId, { state: defaultState, componentCount: 0, createdAt: Date.now(), lastUpdate: Date.now() })
102
+ return defaultState
103
+ }
104
+
105
+ return {} as T
106
+ }
107
+
108
+ update<T extends RoomStateData>(roomId: string, updates: Partial<T>): T {
109
+ const room = this.rooms.get(roomId)
110
+
111
+ if (room) {
112
+ room.state = { ...room.state, ...updates }
113
+ room.lastUpdate = Date.now()
114
+ return room.state as T
115
+ }
116
+
117
+ const newState = updates as T
118
+ this.rooms.set(roomId, { state: newState, componentCount: 0, createdAt: Date.now(), lastUpdate: Date.now() })
119
+ return newState
120
+ }
121
+
122
+ set<T extends RoomStateData>(roomId: string, state: T): void {
123
+ const room = this.rooms.get(roomId)
124
+
125
+ if (room) {
126
+ room.state = state
127
+ room.lastUpdate = Date.now()
128
+ } else {
129
+ this.rooms.set(roomId, { state, componentCount: 0, createdAt: Date.now(), lastUpdate: Date.now() })
130
+ }
131
+ }
132
+
133
+ join(roomId: string): void {
134
+ const room = this.rooms.get(roomId)
135
+ if (room) room.componentCount++
136
+ }
137
+
138
+ leave(roomId: string): void {
139
+ const room = this.rooms.get(roomId)
140
+ if (room) {
141
+ room.componentCount--
142
+ if (room.componentCount <= 0) {
143
+ setTimeout(() => {
144
+ const current = this.rooms.get(roomId)
145
+ if (current && current.componentCount <= 0) {
146
+ this.rooms.delete(roomId)
147
+ }
148
+ }, 5 * 60 * 1000)
149
+ }
150
+ }
151
+ }
152
+
153
+ has(roomId: string): boolean {
154
+ return this.rooms.has(roomId)
155
+ }
156
+
157
+ delete(roomId: string): boolean {
158
+ return this.rooms.delete(roomId)
159
+ }
160
+
161
+ getStats(): { totalRooms: number; rooms: Record<string, { componentCount: number; stateKeys: string[] }> } {
162
+ const rooms: Record<string, { componentCount: number; stateKeys: string[] }> = {}
163
+ for (const [roomId, info] of this.rooms) {
164
+ rooms[roomId] = { componentCount: info.componentCount, stateKeys: Object.keys(info.state) }
165
+ }
166
+ return { totalRooms: this.rooms.size, rooms }
167
+ }
168
+ }
169
+
170
+ export const roomState = new RoomStateManager()
171
+
172
+ export type { RoomStateData, RoomInfo }