create-fluxstack 1.9.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/LIVE_COMPONENTS_REVIEW.md +781 -0
- 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 +109 -156
- 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 -65
- 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 +13 -10
- package/app/client/src/pages/ApiTestPage.tsx +108 -0
- package/app/client/src/pages/HomePage.tsx +76 -0
- package/app/client/src/vite-env.d.ts +1 -1
- package/app/server/app.ts +1 -4
- package/app/server/controllers/users.controller.ts +36 -44
- package/app/server/index.ts +24 -107
- 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/live/register-components.ts +19 -19
- 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/bundler.ts +4 -1
- package/core/build/flux-plugins-generator.ts +325 -325
- package/core/build/index.ts +159 -27
- package/core/build/live-components-generator.ts +70 -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 -558
- package/core/cli/plugin-discovery.ts +2 -2
- package/core/client/LiveComponentsProvider.tsx +63 -17
- 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 -0
- package/core/client/hooks/state-validator.ts +1 -1
- package/core/client/hooks/useAuth.ts +48 -48
- package/core/client/hooks/useChunkedUpload.ts +170 -69
- 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 +18 -51
- 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 +227 -47
- package/core/framework/types.ts +2 -2
- package/core/index.ts +23 -4
- package/core/live/ComponentRegistry.ts +7 -3
- package/core/live/types.ts +77 -0
- package/core/plugins/built-in/index.ts +134 -131
- package/core/plugins/built-in/live-components/commands/create-live-component.ts +242 -1074
- 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 +94 -306
- package/core/plugins/built-in/vite/vite-dev.ts +82 -0
- 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 +309 -21
- package/core/plugins/types.ts +106 -55
- package/core/server/framework.ts +66 -43
- package/core/server/index.ts +15 -16
- package/core/server/live/ComponentRegistry.ts +91 -75
- package/core/server/live/FileUploadManager.ts +41 -31
- 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 +41 -26
- package/core/server/live/index.ts +14 -0
- package/core/server/live/websocket-plugin.ts +233 -72
- 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 -260
- package/core/server/plugins/swagger.ts +33 -33
- 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 -218
- package/core/types/config.ts +56 -26
- package/core/types/index.ts +4 -4
- package/core/types/plugin.ts +107 -99
- package/core/types/types.ts +490 -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 -66
- 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/eslint.config.js +23 -23
- package/package.json +14 -15
- 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/plugins/crypto-auth/server/middlewares.ts +19 -19
- package/tsconfig.api-strict.json +16 -0
- package/tsconfig.json +10 -14
- package/{app/client/tsconfig.node.json → tsconfig.node.json} +1 -1
- 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/workspace.json +5 -5
- 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/tsconfig.app.json +0 -44
- 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/routes/env-test.ts +0 -110
- package/core/client/hooks/index.ts +0 -7
- package/core/client/hooks/useHybridLiveComponent.ts +0 -631
- package/core/client/hooks/useWebSocket.ts +0 -373
- 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,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'
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Provides service registration, resolution, and lifecycle management
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type { Logger } from '
|
|
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
|
-
"
|
|
216
|
-
"
|
|
217
|
-
"
|
|
218
|
-
"
|
|
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
|
-
"
|
|
247
|
-
"
|
|
248
|
-
"
|
|
249
|
-
"
|
|
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
|
-
'
|
|
265
|
-
'
|
|
266
|
-
'
|
|
267
|
-
'
|
|
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: {
|
package/core/testing/index.ts
CHANGED
|
@@ -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'
|