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,463 @@
1
+ // 🔥 FluxStack Room System - Pub/Sub tipado para comunicação entre componentes
2
+
3
+ type EventHandler<T = any> = (data: T) => void
4
+ type Unsubscribe = () => void
5
+
6
+ // Eventos do sistema (prefixo $)
7
+ export type SystemEvents<TState> = {
8
+ '$room:created': { roomId: string; createdAt: number; initialState: TState }
9
+ '$room:destroyed': { roomId: string; reason: 'manual' | 'empty' | 'ttl'; finalState: TState }
10
+ '$sub:join': { subscriberId: string; event: string; count: number }
11
+ '$sub:leave': { subscriberId: string; event: string; count: number }
12
+ '$state:change': { path?: string; oldValue: any; newValue: any }
13
+ '$state:reset': { oldState: TState; newState: TState }
14
+ '$error': { error: Error; context: string }
15
+ }
16
+
17
+ // Combina eventos do usuário + sistema
18
+ type AllEvents<TState, TUserEvents> = TUserEvents & SystemEvents<TState>
19
+
20
+ // Subscription info
21
+ interface Subscription {
22
+ id: string
23
+ event: string
24
+ handler: EventHandler
25
+ }
26
+
27
+ // Room instance
28
+ export class Room<TState, TEvents extends Record<string, any>> {
29
+ public readonly id: string
30
+ public readonly createdAt: number
31
+
32
+ private _state: TState
33
+ private subscriptions = new Map<string, Set<Subscription>>()
34
+ private subscriberCounter = new Map<string, number>() // event -> count
35
+ private nextSubId = 0
36
+ private destroyed = false
37
+
38
+ // Callback para notificar o sistema global
39
+ private onSystemEvent?: (event: string, data: any) => void
40
+
41
+ constructor(
42
+ id: string,
43
+ initialState: TState,
44
+ onSystemEvent?: (event: string, data: any) => void
45
+ ) {
46
+ this.id = id
47
+ this.createdAt = Date.now()
48
+ this._state = structuredClone(initialState)
49
+ this.onSystemEvent = onSystemEvent
50
+ }
51
+
52
+ // ============================================
53
+ // Estado
54
+ // ============================================
55
+
56
+ get state(): Readonly<TState> {
57
+ return this._state
58
+ }
59
+
60
+ setState(partial: Partial<TState>): void {
61
+ this.checkDestroyed()
62
+ const oldState = this._state
63
+ this._state = { ...this._state, ...partial }
64
+
65
+ // Emitir evento de mudança para cada campo alterado
66
+ for (const key of Object.keys(partial) as (keyof TState)[]) {
67
+ if (oldState[key] !== partial[key]) {
68
+ this.emitSystem('$state:change', {
69
+ path: key as string,
70
+ oldValue: oldState[key],
71
+ newValue: partial[key]
72
+ })
73
+ }
74
+ }
75
+ }
76
+
77
+ updateState(updater: (prev: TState) => Partial<TState>): void {
78
+ this.checkDestroyed()
79
+ const updates = updater(this._state)
80
+ this.setState(updates)
81
+ }
82
+
83
+ resetState(newState: TState): void {
84
+ this.checkDestroyed()
85
+ const oldState = this._state
86
+ this._state = structuredClone(newState)
87
+
88
+ this.emitSystem('$state:reset', { oldState, newState })
89
+ }
90
+
91
+ // ============================================
92
+ // Eventos do usuário
93
+ // ============================================
94
+
95
+ on<K extends keyof AllEvents<TState, TEvents>>(
96
+ event: K,
97
+ handler: EventHandler<AllEvents<TState, TEvents>[K]>
98
+ ): Unsubscribe {
99
+ this.checkDestroyed()
100
+
101
+ const eventKey = event as string
102
+
103
+ if (!this.subscriptions.has(eventKey)) {
104
+ this.subscriptions.set(eventKey, new Set())
105
+ this.subscriberCounter.set(eventKey, 0)
106
+ }
107
+
108
+ const subId = `sub-${++this.nextSubId}`
109
+ const subscription: Subscription = {
110
+ id: subId,
111
+ event: eventKey,
112
+ handler
113
+ }
114
+
115
+ this.subscriptions.get(eventKey)!.add(subscription)
116
+ const count = (this.subscriberCounter.get(eventKey) || 0) + 1
117
+ this.subscriberCounter.set(eventKey, count)
118
+
119
+ // Emitir evento de sistema (só para eventos não-sistema)
120
+ if (!eventKey.startsWith('$')) {
121
+ this.emitSystem('$sub:join', {
122
+ subscriberId: subId,
123
+ event: eventKey,
124
+ count
125
+ })
126
+ }
127
+
128
+ return () => {
129
+ const subs = this.subscriptions.get(eventKey)
130
+ if (subs) {
131
+ subs.delete(subscription)
132
+ const newCount = Math.max(0, (this.subscriberCounter.get(eventKey) || 1) - 1)
133
+ this.subscriberCounter.set(eventKey, newCount)
134
+
135
+ if (!eventKey.startsWith('$')) {
136
+ this.emitSystem('$sub:leave', {
137
+ subscriberId: subId,
138
+ event: eventKey,
139
+ count: newCount
140
+ })
141
+ }
142
+
143
+ if (subs.size === 0) {
144
+ this.subscriptions.delete(eventKey)
145
+ }
146
+ }
147
+ }
148
+ }
149
+
150
+ emit<K extends keyof TEvents>(
151
+ event: K,
152
+ data: TEvents[K]
153
+ ): number {
154
+ this.checkDestroyed()
155
+ return this.emitInternal(event as string, data)
156
+ }
157
+
158
+ // Emitir para evento do sistema
159
+ private emitSystem<K extends keyof SystemEvents<TState>>(
160
+ event: K,
161
+ data: SystemEvents<TState>[K]
162
+ ): void {
163
+ // Emitir localmente
164
+ this.emitInternal(event as string, data)
165
+
166
+ // Notificar sistema global
167
+ this.onSystemEvent?.(event as string, { roomId: this.id, ...data })
168
+ }
169
+
170
+ private emitInternal(event: string, data: any): number {
171
+ const subs = this.subscriptions.get(event)
172
+ if (!subs || subs.size === 0) return 0
173
+
174
+ let notified = 0
175
+ for (const sub of subs) {
176
+ try {
177
+ sub.handler(data)
178
+ notified++
179
+ } catch (error) {
180
+ console.error(`[Room:${this.id}] Error in handler for '${event}':`, error)
181
+ this.emitSystem('$error', {
182
+ error: error as Error,
183
+ context: `Handler for event '${event}'`
184
+ })
185
+ }
186
+ }
187
+
188
+ return notified
189
+ }
190
+
191
+ // ============================================
192
+ // Utilitários
193
+ // ============================================
194
+
195
+ getSubscriberCount(event?: string): number {
196
+ if (event) {
197
+ return this.subscriberCounter.get(event) || 0
198
+ }
199
+ let total = 0
200
+ for (const count of this.subscriberCounter.values()) {
201
+ total += count
202
+ }
203
+ return total
204
+ }
205
+
206
+ getEvents(): string[] {
207
+ return Array.from(this.subscriptions.keys()).filter(e => !e.startsWith('$'))
208
+ }
209
+
210
+ destroy(reason: 'manual' | 'empty' | 'ttl' = 'manual'): void {
211
+ if (this.destroyed) return
212
+
213
+ this.emitSystem('$room:destroyed', {
214
+ roomId: this.id,
215
+ reason,
216
+ finalState: this._state
217
+ })
218
+
219
+ this.destroyed = true
220
+ this.subscriptions.clear()
221
+ this.subscriberCounter.clear()
222
+ }
223
+
224
+ isDestroyed(): boolean {
225
+ return this.destroyed
226
+ }
227
+
228
+ private checkDestroyed(): void {
229
+ if (this.destroyed) {
230
+ throw new Error(`Room '${this.id}' has been destroyed`)
231
+ }
232
+ }
233
+ }
234
+
235
+ // ============================================
236
+ // Room System (gerenciador global)
237
+ // ============================================
238
+
239
+ export interface RoomSystemOptions {
240
+ autoDestroy?: boolean // Destruir sala quando vazia
241
+ destroyDelay?: number // Delay antes de destruir (ms)
242
+ defaultTTL?: number // TTL padrão para salas
243
+ }
244
+
245
+ export class RoomSystem<TState, TEvents extends Record<string, any>> {
246
+ public readonly name: string
247
+ private rooms = new Map<string, Room<TState, TEvents>>()
248
+ private globalSubscriptions = new Map<string, Set<EventHandler>>()
249
+ private destroyTimers = new Map<string, ReturnType<typeof setTimeout>>()
250
+ private options: Required<RoomSystemOptions>
251
+
252
+ constructor(name: string, options: RoomSystemOptions = {}) {
253
+ this.name = name
254
+ this.options = {
255
+ autoDestroy: options.autoDestroy ?? false,
256
+ destroyDelay: options.destroyDelay ?? 5 * 60 * 1000, // 5 min
257
+ defaultTTL: options.defaultTTL ?? 0 // 0 = sem TTL
258
+ }
259
+ }
260
+
261
+ // ============================================
262
+ // Gerenciamento de salas
263
+ // ============================================
264
+
265
+ room(roomId: string, initialState: TState): Room<TState, TEvents> {
266
+ // Cancelar timer de destruição se existir
267
+ const timer = this.destroyTimers.get(roomId)
268
+ if (timer) {
269
+ clearTimeout(timer)
270
+ this.destroyTimers.delete(roomId)
271
+ }
272
+
273
+ // Retornar sala existente
274
+ const existing = this.rooms.get(roomId)
275
+ if (existing && !existing.isDestroyed()) {
276
+ return existing
277
+ }
278
+
279
+ // Criar nova sala
280
+ const room = new Room<TState, TEvents>(
281
+ roomId,
282
+ initialState,
283
+ (event, data) => this.handleSystemEvent(event, data)
284
+ )
285
+
286
+ this.rooms.set(roomId, room)
287
+
288
+ // Emitir evento global
289
+ this.emitGlobal('$room:created', {
290
+ roomId,
291
+ createdAt: room.createdAt,
292
+ initialState
293
+ })
294
+
295
+ // Configurar auto-destroy
296
+ if (this.options.autoDestroy) {
297
+ room.on('$sub:leave', ({ count }) => {
298
+ if (count === 0 && room.getSubscriberCount() === 0) {
299
+ this.scheduleDestroy(roomId)
300
+ }
301
+ })
302
+ }
303
+
304
+ // Configurar TTL
305
+ if (this.options.defaultTTL > 0) {
306
+ setTimeout(() => {
307
+ if (!room.isDestroyed()) {
308
+ this.destroyRoom(roomId, 'ttl')
309
+ }
310
+ }, this.options.defaultTTL)
311
+ }
312
+
313
+ return room
314
+ }
315
+
316
+ get(roomId: string): Room<TState, TEvents> | undefined {
317
+ const room = this.rooms.get(roomId)
318
+ return room && !room.isDestroyed() ? room : undefined
319
+ }
320
+
321
+ has(roomId: string): boolean {
322
+ const room = this.rooms.get(roomId)
323
+ return room !== undefined && !room.isDestroyed()
324
+ }
325
+
326
+ getOrCreate(roomId: string, initialState: TState): Room<TState, TEvents> {
327
+ return this.get(roomId) ?? this.room(roomId, initialState)
328
+ }
329
+
330
+ destroyRoom(roomId: string, reason: 'manual' | 'empty' | 'ttl' = 'manual'): boolean {
331
+ const room = this.rooms.get(roomId)
332
+ if (!room) return false
333
+
334
+ room.destroy(reason)
335
+ this.rooms.delete(roomId)
336
+
337
+ // Cancelar timer se existir
338
+ const timer = this.destroyTimers.get(roomId)
339
+ if (timer) {
340
+ clearTimeout(timer)
341
+ this.destroyTimers.delete(roomId)
342
+ }
343
+
344
+ return true
345
+ }
346
+
347
+ private scheduleDestroy(roomId: string): void {
348
+ // Cancelar timer anterior
349
+ const existing = this.destroyTimers.get(roomId)
350
+ if (existing) {
351
+ clearTimeout(existing)
352
+ }
353
+
354
+ const timer = setTimeout(() => {
355
+ const room = this.rooms.get(roomId)
356
+ if (room && room.getSubscriberCount() === 0) {
357
+ this.destroyRoom(roomId, 'empty')
358
+ }
359
+ this.destroyTimers.delete(roomId)
360
+ }, this.options.destroyDelay)
361
+
362
+ this.destroyTimers.set(roomId, timer)
363
+ }
364
+
365
+ // ============================================
366
+ // Eventos globais
367
+ // ============================================
368
+
369
+ on<K extends keyof SystemEvents<TState>>(
370
+ event: K,
371
+ handler: EventHandler<SystemEvents<TState>[K] & { roomId: string }>
372
+ ): Unsubscribe {
373
+ const eventKey = event as string
374
+
375
+ if (!this.globalSubscriptions.has(eventKey)) {
376
+ this.globalSubscriptions.set(eventKey, new Set())
377
+ }
378
+
379
+ this.globalSubscriptions.get(eventKey)!.add(handler)
380
+
381
+ return () => {
382
+ this.globalSubscriptions.get(eventKey)?.delete(handler)
383
+ }
384
+ }
385
+
386
+ private handleSystemEvent(event: string, data: any): void {
387
+ this.emitGlobal(event, data)
388
+ }
389
+
390
+ private emitGlobal(event: string, data: any): void {
391
+ const handlers = this.globalSubscriptions.get(event)
392
+ if (!handlers) return
393
+
394
+ for (const handler of handlers) {
395
+ try {
396
+ handler(data)
397
+ } catch (error) {
398
+ console.error(`[RoomSystem:${this.name}] Error in global handler for '${event}':`, error)
399
+ }
400
+ }
401
+ }
402
+
403
+ // ============================================
404
+ // Utilitários
405
+ // ============================================
406
+
407
+ getRooms(): string[] {
408
+ return Array.from(this.rooms.keys()).filter(id => !this.rooms.get(id)?.isDestroyed())
409
+ }
410
+
411
+ getRoomCount(): number {
412
+ return this.getRooms().length
413
+ }
414
+
415
+ getStats(): {
416
+ name: string
417
+ roomCount: number
418
+ rooms: Record<string, { subscriberCount: number; events: string[]; createdAt: number }>
419
+ } {
420
+ const rooms: Record<string, { subscriberCount: number; events: string[]; createdAt: number }> = {}
421
+
422
+ for (const [id, room] of this.rooms) {
423
+ if (!room.isDestroyed()) {
424
+ rooms[id] = {
425
+ subscriberCount: room.getSubscriberCount(),
426
+ events: room.getEvents(),
427
+ createdAt: room.createdAt
428
+ }
429
+ }
430
+ }
431
+
432
+ return {
433
+ name: this.name,
434
+ roomCount: Object.keys(rooms).length,
435
+ rooms
436
+ }
437
+ }
438
+
439
+ destroyAll(): void {
440
+ for (const roomId of this.rooms.keys()) {
441
+ this.destroyRoom(roomId, 'manual')
442
+ }
443
+ }
444
+ }
445
+
446
+ // ============================================
447
+ // Factory function
448
+ // ============================================
449
+
450
+ export function createRoomSystem<
451
+ TDef extends { state: any; events: Record<string, any> }
452
+ >(
453
+ name: string,
454
+ options?: RoomSystemOptions
455
+ ): RoomSystem<TDef['state'], TDef['events']> {
456
+ return new RoomSystem<TDef['state'], TDef['events']>(name, options)
457
+ }
458
+
459
+ // ============================================
460
+ // Exports
461
+ // ============================================
462
+
463
+ export type { EventHandler, Unsubscribe, Subscription }
@@ -0,0 +1,13 @@
1
+ // 🔥 FluxStack Room System - Exports
2
+
3
+ export { Room, RoomSystem, createRoomSystem } from './RoomSystem'
4
+ export type {
5
+ SystemEvents,
6
+ EventHandler,
7
+ Unsubscribe,
8
+ Subscription,
9
+ RoomSystemOptions
10
+ } from './RoomSystem'
11
+
12
+ export { RoomBroadcaster, createRoomBroadcaster } from './RoomBroadcaster'
13
+ export type { BroadcastMessage, RoomConnection } from './RoomBroadcaster'
@@ -8,7 +8,7 @@
8
8
  * - Service container integration
9
9
  */
10
10
 
11
- import type { Logger } from '@/core/utils/logger/index'
11
+ import type { Logger } from '@core/utils/logger/index'
12
12
 
13
13
  export interface ServiceContext {
14
14
  config: any
@@ -5,7 +5,7 @@
5
5
  * Provides service registration, resolution, and lifecycle management
6
6
  */
7
7
 
8
- import type { Logger } from '@/core/utils/logger/index'
8
+ import type { Logger } from '@core/utils/logger/index'
9
9
 
10
10
  export interface ServiceDefinition {
11
11
  factory: (container: ServiceContainer) => any
@@ -1,9 +1,9 @@
1
- /**
2
- * Core Server Services
3
- * FluxStack service infrastructure exports
4
- */
5
-
6
- export { BaseService } from './BaseService'
7
- export { ServiceContainer } from './ServiceContainer'
8
- export type { ServiceContext, ServiceContainer as IServiceContainer } from './BaseService'
1
+ /**
2
+ * Core Server Services
3
+ * FluxStack service infrastructure exports
4
+ */
5
+
6
+ export { BaseService } from './BaseService'
7
+ export { ServiceContainer } from './ServiceContainer'
8
+ export type { ServiceContext, ServiceContainer as IServiceContainer } from './BaseService'
9
9
  export type { ServiceDefinition } from './ServiceContainer'
@@ -212,10 +212,10 @@ export class ProjectCreator {
212
212
  baseUrl: ".",
213
213
  paths: {
214
214
  "@/*": ["./*"],
215
- "@/core/*": ["./core/*"],
216
- "@/app/*": ["./app/*"],
217
- "@/config/*": ["./config/*"],
218
- "@/shared/*": ["./app/shared/*"]
215
+ "@core/*": ["./core/*"],
216
+ "@app/*": ["./app/*"],
217
+ "@config/*": ["./config/*"],
218
+ "@shared/*": ["./app/shared/*"]
219
219
  },
220
220
  strict: true,
221
221
  skipLibCheck: true,
@@ -243,10 +243,10 @@ lockfile = true
243
243
  # Path mapping (alias support)
244
244
  [build.alias]
245
245
  "@" = "."
246
- "@/core" = "./core"
247
- "@/app" = "./app"
248
- "@/config" = "./config"
249
- "@/shared" = "./app/shared"
246
+ "@core" = "./core"
247
+ "@app" = "./app"
248
+ "@config" = "./config"
249
+ "@shared" = "./app/shared"
250
250
  `
251
251
 
252
252
  await Bun.write(join(this.targetDir, "bunfig.toml"), bunConfig)
@@ -261,10 +261,10 @@ export default defineConfig({
261
261
  resolve: {
262
262
  alias: {
263
263
  '@': resolve(__dirname, '.'),
264
- '@/core': resolve(__dirname, './core'),
265
- '@/app': resolve(__dirname, './app'),
266
- '@/config': resolve(__dirname, './config'),
267
- '@/shared': resolve(__dirname, './app/shared')
264
+ '@core': resolve(__dirname, './core'),
265
+ '@app': resolve(__dirname, './app'),
266
+ '@config': resolve(__dirname, './config'),
267
+ '@shared': resolve(__dirname, './app/shared')
268
268
  }
269
269
  },
270
270
  server: {
@@ -1,10 +1,10 @@
1
- /**
2
- * Core Testing Utilities
3
- * FluxStack testing infrastructure exports
4
- */
5
-
6
- export {
7
- setupFluxStackTests,
8
- createMockLogger,
9
- createMockServiceContext
1
+ /**
2
+ * Core Testing Utilities
3
+ * FluxStack testing infrastructure exports
4
+ */
5
+
6
+ export {
7
+ setupFluxStackTests,
8
+ createMockLogger,
9
+ createMockServiceContext
10
10
  } from './setup'