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.
- package/.dockerignore +1 -2
- package/Dockerfile +8 -8
- package/LLMD/INDEX.md +64 -0
- package/LLMD/MAINTENANCE.md +197 -0
- package/LLMD/MIGRATION.md +156 -0
- package/LLMD/config/.gitkeep +1 -0
- package/LLMD/config/declarative-system.md +268 -0
- package/LLMD/config/environment-vars.md +327 -0
- package/LLMD/config/runtime-reload.md +401 -0
- package/LLMD/core/.gitkeep +1 -0
- package/LLMD/core/build-system.md +599 -0
- package/LLMD/core/framework-lifecycle.md +229 -0
- package/LLMD/core/plugin-system.md +451 -0
- package/LLMD/patterns/.gitkeep +1 -0
- package/LLMD/patterns/anti-patterns.md +297 -0
- package/LLMD/patterns/project-structure.md +264 -0
- package/LLMD/patterns/type-safety.md +440 -0
- package/LLMD/reference/.gitkeep +1 -0
- package/LLMD/reference/cli-commands.md +250 -0
- package/LLMD/reference/plugin-hooks.md +357 -0
- package/LLMD/reference/routing.md +39 -0
- package/LLMD/reference/troubleshooting.md +364 -0
- package/LLMD/resources/.gitkeep +1 -0
- package/LLMD/resources/controllers.md +465 -0
- package/LLMD/resources/live-components.md +703 -0
- package/LLMD/resources/live-rooms.md +482 -0
- package/LLMD/resources/live-upload.md +130 -0
- package/LLMD/resources/plugins-external.md +617 -0
- package/LLMD/resources/routes-eden.md +254 -0
- package/README.md +37 -17
- package/app/client/index.html +0 -1
- package/app/client/src/App.tsx +107 -150
- package/app/client/src/components/AppLayout.tsx +68 -0
- package/app/client/src/components/BackButton.tsx +13 -0
- package/app/client/src/components/DemoPage.tsx +20 -0
- package/app/client/src/components/LiveUploadWidget.tsx +204 -0
- package/app/client/src/lib/eden-api.ts +85 -60
- package/app/client/src/live/ChatDemo.tsx +107 -0
- package/app/client/src/live/CounterDemo.tsx +206 -0
- package/app/client/src/live/FormDemo.tsx +119 -0
- package/app/client/src/live/RoomChatDemo.tsx +242 -0
- package/app/client/src/live/UploadDemo.tsx +21 -0
- package/app/client/src/main.tsx +4 -1
- package/app/client/src/pages/ApiTestPage.tsx +108 -0
- package/app/client/src/pages/HomePage.tsx +76 -0
- package/app/server/app.ts +1 -4
- package/app/server/controllers/users.controller.ts +36 -44
- package/app/server/index.ts +25 -35
- package/app/server/live/LiveChat.ts +77 -0
- package/app/server/live/LiveCounter.ts +67 -0
- package/app/server/live/LiveForm.ts +63 -0
- package/app/server/live/LiveLocalCounter.ts +32 -0
- package/app/server/live/LiveRoomChat.ts +285 -0
- package/app/server/live/LiveUpload.ts +81 -0
- package/app/server/routes/index.ts +3 -1
- package/app/server/routes/room.routes.ts +117 -0
- package/app/server/routes/users.routes.ts +35 -27
- package/app/shared/types/index.ts +14 -2
- package/config/app.config.ts +2 -62
- package/config/client.config.ts +2 -95
- package/config/database.config.ts +2 -99
- package/config/fluxstack.config.ts +25 -45
- package/config/index.ts +57 -38
- package/config/monitoring.config.ts +2 -114
- package/config/plugins.config.ts +2 -80
- package/config/server.config.ts +2 -68
- package/config/services.config.ts +2 -130
- package/config/system/app.config.ts +29 -0
- package/config/system/build.config.ts +49 -0
- package/config/system/client.config.ts +68 -0
- package/config/system/database.config.ts +17 -0
- package/config/system/fluxstack.config.ts +114 -0
- package/config/{logger.config.ts → system/logger.config.ts} +3 -1
- package/config/system/monitoring.config.ts +114 -0
- package/config/system/plugins.config.ts +84 -0
- package/config/{runtime.config.ts → system/runtime.config.ts} +1 -1
- package/config/system/server.config.ts +68 -0
- package/config/system/services.config.ts +46 -0
- package/config/{system.config.ts → system/system.config.ts} +1 -1
- package/core/build/flux-plugins-generator.ts +325 -325
- package/core/build/index.ts +39 -27
- package/core/build/live-components-generator.ts +3 -3
- package/core/build/optimizer.ts +235 -235
- package/core/cli/command-registry.ts +6 -4
- package/core/cli/commands/build.ts +79 -0
- package/core/cli/commands/create.ts +54 -0
- package/core/cli/commands/dev.ts +101 -0
- package/core/cli/commands/help.ts +34 -0
- package/core/cli/commands/index.ts +34 -0
- package/core/cli/commands/make-plugin.ts +90 -0
- package/core/cli/commands/plugin-add.ts +197 -0
- package/core/cli/commands/plugin-deps.ts +2 -2
- package/core/cli/commands/plugin-list.ts +208 -0
- package/core/cli/commands/plugin-remove.ts +170 -0
- package/core/cli/generators/component.ts +769 -769
- package/core/cli/generators/controller.ts +1 -1
- package/core/cli/generators/index.ts +146 -146
- package/core/cli/generators/interactive.ts +227 -227
- package/core/cli/generators/plugin.ts +2 -2
- package/core/cli/generators/prompts.ts +82 -82
- package/core/cli/generators/route.ts +6 -6
- package/core/cli/generators/service.ts +2 -2
- package/core/cli/generators/template-engine.ts +4 -3
- package/core/cli/generators/types.ts +2 -2
- package/core/cli/generators/utils.ts +191 -191
- package/core/cli/index.ts +115 -686
- package/core/cli/plugin-discovery.ts +2 -2
- package/core/client/LiveComponentsProvider.tsx +60 -8
- package/core/client/api/eden.ts +183 -0
- package/core/client/api/index.ts +11 -0
- package/core/client/components/Live.tsx +104 -0
- package/core/client/fluxstack.ts +1 -9
- package/core/client/hooks/AdaptiveChunkSizer.ts +215 -215
- package/core/client/hooks/state-validator.ts +1 -1
- package/core/client/hooks/useAuth.ts +48 -48
- package/core/client/hooks/useChunkedUpload.ts +85 -35
- package/core/client/hooks/useLiveChunkedUpload.ts +87 -0
- package/core/client/hooks/useLiveComponent.ts +800 -0
- package/core/client/hooks/useLiveUpload.ts +71 -0
- package/core/client/hooks/useRoom.ts +409 -0
- package/core/client/hooks/useRoomProxy.ts +382 -0
- package/core/client/index.ts +17 -68
- package/core/client/standalone-entry.ts +8 -0
- package/core/client/standalone.ts +74 -53
- package/core/client/state/createStore.ts +192 -192
- package/core/client/state/index.ts +14 -14
- package/core/config/index.ts +70 -291
- package/core/config/schema.ts +42 -723
- package/core/framework/client.ts +131 -131
- package/core/framework/index.ts +7 -7
- package/core/framework/server.ts +47 -40
- package/core/framework/types.ts +2 -2
- package/core/index.ts +23 -4
- package/core/live/ComponentRegistry.ts +3 -3
- package/core/live/types.ts +77 -0
- package/core/plugins/built-in/index.ts +134 -134
- package/core/plugins/built-in/live-components/commands/create-live-component.ts +242 -1066
- package/core/plugins/built-in/live-components/index.ts +1 -1
- package/core/plugins/built-in/monitoring/index.ts +111 -47
- package/core/plugins/built-in/static/index.ts +1 -1
- package/core/plugins/built-in/swagger/index.ts +68 -265
- package/core/plugins/built-in/vite/index.ts +85 -185
- package/core/plugins/built-in/vite/vite-dev.ts +10 -16
- package/core/plugins/config.ts +9 -7
- package/core/plugins/dependency-manager.ts +31 -1
- package/core/plugins/discovery.ts +19 -7
- package/core/plugins/executor.ts +2 -2
- package/core/plugins/index.ts +203 -203
- package/core/plugins/manager.ts +27 -39
- package/core/plugins/module-resolver.ts +19 -8
- package/core/plugins/registry.ts +255 -19
- package/core/plugins/types.ts +20 -53
- package/core/server/framework.ts +66 -43
- package/core/server/index.ts +15 -15
- package/core/server/live/ComponentRegistry.ts +78 -71
- package/core/server/live/FileUploadManager.ts +23 -10
- package/core/server/live/LiveComponentPerformanceMonitor.ts +1 -1
- package/core/server/live/LiveRoomManager.ts +261 -0
- package/core/server/live/RoomEventBus.ts +234 -0
- package/core/server/live/RoomStateManager.ts +172 -0
- package/core/server/live/StateSignature.ts +643 -643
- package/core/server/live/WebSocketConnectionManager.ts +30 -19
- package/core/server/live/auto-generated-components.ts +21 -9
- package/core/server/live/index.ts +14 -0
- package/core/server/live/websocket-plugin.ts +214 -67
- package/core/server/middleware/elysia-helpers.ts +7 -2
- package/core/server/middleware/errorHandling.ts +1 -1
- package/core/server/middleware/index.ts +31 -31
- package/core/server/plugins/database.ts +180 -180
- package/core/server/plugins/static-files-plugin.ts +69 -69
- package/core/server/plugins/swagger.ts +1 -1
- package/core/server/rooms/RoomBroadcaster.ts +357 -0
- package/core/server/rooms/RoomSystem.ts +463 -0
- package/core/server/rooms/index.ts +13 -0
- package/core/server/services/BaseService.ts +1 -1
- package/core/server/services/ServiceContainer.ts +1 -1
- package/core/server/services/index.ts +8 -8
- package/core/templates/create-project.ts +12 -12
- package/core/testing/index.ts +9 -9
- package/core/testing/setup.ts +73 -73
- package/core/types/api.ts +168 -168
- package/core/types/build.ts +219 -219
- package/core/types/config.ts +56 -26
- package/core/types/index.ts +4 -4
- package/core/types/plugin.ts +107 -107
- package/core/types/types.ts +353 -14
- package/core/utils/build-logger.ts +324 -324
- package/core/utils/config-schema.ts +480 -480
- package/core/utils/env.ts +2 -8
- package/core/utils/errors/codes.ts +114 -114
- package/core/utils/errors/handlers.ts +36 -1
- package/core/utils/errors/index.ts +49 -5
- package/core/utils/errors/middleware.ts +113 -113
- package/core/utils/helpers.ts +6 -16
- package/core/utils/index.ts +17 -17
- package/core/utils/logger/colors.ts +114 -114
- package/core/utils/logger/config.ts +13 -9
- package/core/utils/logger/formatter.ts +82 -82
- package/core/utils/logger/group-logger.ts +101 -101
- package/core/utils/logger/index.ts +6 -1
- package/core/utils/logger/stack-trace.ts +3 -1
- package/core/utils/logger/startup-banner.ts +82 -82
- package/core/utils/logger/winston-logger.ts +152 -152
- package/core/utils/monitoring/index.ts +211 -211
- package/core/utils/sync-version.ts +66 -66
- package/core/utils/version.ts +1 -1
- package/create-fluxstack.ts +8 -7
- package/package.json +12 -13
- package/plugins/crypto-auth/cli/make-protected-route.command.ts +1 -1
- package/plugins/crypto-auth/client/CryptoAuthClient.ts +302 -302
- package/plugins/crypto-auth/client/components/index.ts +11 -11
- package/plugins/crypto-auth/client/index.ts +11 -11
- package/plugins/crypto-auth/config/index.ts +1 -1
- package/plugins/crypto-auth/index.ts +4 -4
- package/plugins/crypto-auth/package.json +65 -65
- package/plugins/crypto-auth/server/AuthMiddleware.ts +1 -1
- package/plugins/crypto-auth/server/CryptoAuthService.ts +185 -185
- package/plugins/crypto-auth/server/index.ts +21 -21
- package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +3 -3
- package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +2 -2
- package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +2 -2
- package/plugins/crypto-auth/server/middlewares/helpers.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/index.ts +22 -22
- package/tsconfig.api-strict.json +16 -0
- package/tsconfig.json +48 -52
- package/{app/client/tsconfig.node.json → tsconfig.node.json} +25 -25
- package/types/global.d.ts +29 -29
- package/types/vitest.d.ts +8 -8
- package/vite.config.ts +38 -62
- package/vitest.config.live.ts +10 -9
- package/vitest.config.ts +29 -17
- package/app/client/README.md +0 -69
- package/app/client/SIMPLIFICATION.md +0 -140
- package/app/client/frontend-only.ts +0 -12
- package/app/client/src/live/FileUploadExample.tsx +0 -359
- package/app/client/src/live/MinimalLiveClock.tsx +0 -47
- package/app/client/src/live/QuickUploadTest.tsx +0 -193
- package/app/client/tsconfig.app.json +0 -45
- package/app/client/tsconfig.json +0 -7
- package/app/client/zustand-setup.md +0 -65
- package/app/server/backend-only.ts +0 -18
- package/app/server/live/LiveClockComponent.ts +0 -215
- package/app/server/live/LiveFileUploadComponent.ts +0 -77
- package/app/server/routes/env-test.ts +0 -110
- package/core/client/hooks/index.ts +0 -7
- package/core/client/hooks/useHybridLiveComponent.ts +0 -685
- package/core/client/hooks/useTypedLiveComponent.ts +0 -133
- package/core/client/hooks/useWebSocket.ts +0 -361
- package/core/config/env.ts +0 -546
- package/core/config/loader.ts +0 -522
- package/core/config/runtime-config.ts +0 -327
- package/core/config/validator.ts +0 -540
- package/core/server/backend-entry.ts +0 -51
- package/core/server/standalone.ts +0 -106
- package/core/utils/regenerate-files.ts +0 -69
- 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 }
|