create-fluxstack 1.13.0 → 1.15.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/LLMD/patterns/anti-patterns.md +100 -0
- package/LLMD/reference/routing.md +39 -39
- package/LLMD/resources/live-auth.md +20 -2
- package/LLMD/resources/live-components.md +300 -21
- package/LLMD/resources/live-logging.md +95 -33
- package/LLMD/resources/live-upload.md +59 -8
- package/app/client/.live-stubs/LiveAdminPanel.js +5 -0
- package/app/client/.live-stubs/LiveChat.js +7 -0
- package/app/client/.live-stubs/LiveCounter.js +9 -0
- package/app/client/.live-stubs/LiveForm.js +11 -0
- package/app/client/.live-stubs/LiveLocalCounter.js +8 -0
- package/app/client/.live-stubs/LiveRoomChat.js +10 -0
- package/app/client/.live-stubs/LiveTodoList.js +9 -0
- package/app/client/.live-stubs/LiveUpload.js +15 -0
- package/app/client/index.html +2 -2
- package/app/client/public/favicon.svg +46 -0
- package/app/client/src/App.tsx +13 -1
- package/app/client/src/assets/fluxstack-static.svg +46 -0
- package/app/client/src/assets/fluxstack.svg +183 -0
- package/app/client/src/components/AppLayout.tsx +146 -9
- package/app/client/src/components/BackButton.tsx +13 -13
- package/app/client/src/components/DemoPage.tsx +4 -4
- package/app/client/src/live/AuthDemo.tsx +23 -21
- package/app/client/src/live/ChatDemo.tsx +2 -2
- package/app/client/src/live/CounterDemo.tsx +12 -12
- package/app/client/src/live/FormDemo.tsx +2 -2
- package/app/client/src/live/LiveDebuggerPanel.tsx +779 -0
- package/app/client/src/live/RoomChatDemo.tsx +24 -16
- package/app/client/src/live/TodoListDemo.tsx +158 -0
- package/app/client/src/main.tsx +13 -13
- package/app/client/src/pages/ApiTestPage.tsx +6 -6
- package/app/client/src/pages/HomePage.tsx +80 -52
- package/app/server/auth/DevAuthProvider.ts +2 -2
- package/app/server/auth/JWTAuthProvider.example.ts +2 -2
- package/app/server/index.ts +2 -2
- package/app/server/live/LiveAdminPanel.ts +2 -1
- package/app/server/live/LiveChat.ts +78 -77
- package/app/server/live/LiveCounter.ts +1 -1
- package/app/server/live/LiveForm.ts +1 -0
- package/app/server/live/LiveLocalCounter.ts +38 -37
- package/app/server/live/LiveProtectedChat.ts +2 -1
- package/app/server/live/LiveRoomChat.ts +1 -0
- package/app/server/live/LiveTodoList.ts +110 -0
- package/app/server/live/LiveUpload.ts +1 -0
- package/app/server/live/register-components.ts +19 -19
- package/app/server/routes/room.routes.ts +1 -2
- package/config/system/runtime.config.ts +4 -0
- package/core/build/live-components-generator.ts +1 -1
- package/core/build/optimizer.ts +235 -235
- package/core/build/vite-plugins.ts +28 -0
- package/core/client/components/LiveDebugger.tsx +1324 -0
- package/core/client/hooks/useLiveUpload.ts +3 -4
- package/core/client/index.ts +41 -21
- package/core/framework/server.ts +1 -1
- package/core/plugins/built-in/index.ts +134 -134
- package/core/plugins/built-in/live-components/commands/create-live-component.ts +4 -0
- package/core/plugins/built-in/vite/index.ts +75 -21
- package/core/server/index.ts +14 -15
- package/core/server/live/auto-generated-components.ts +6 -3
- package/core/server/live/index.ts +95 -21
- package/core/server/live/websocket-plugin.ts +27 -862
- package/core/server/plugins/static-files-plugin.ts +179 -69
- package/core/types/build.ts +219 -219
- package/core/types/plugin.ts +107 -107
- package/core/types/types.ts +77 -890
- package/core/utils/logger/startup-banner.ts +82 -82
- package/core/utils/version.ts +6 -6
- package/create-fluxstack.ts +1 -1
- package/package.json +5 -1
- package/plugins/crypto-auth/index.ts +1 -1
- package/plugins/crypto-auth/server/CryptoAuthLiveProvider.ts +2 -2
- package/vite.config.ts +40 -12
- package/app/client/src/assets/react.svg +0 -1
- package/core/client/LiveComponentsProvider.tsx +0 -531
- package/core/client/components/Live.tsx +0 -105
- package/core/client/hooks/AdaptiveChunkSizer.ts +0 -215
- package/core/client/hooks/state-validator.ts +0 -130
- package/core/client/hooks/useChunkedUpload.ts +0 -359
- package/core/client/hooks/useLiveChunkedUpload.ts +0 -87
- package/core/client/hooks/useLiveComponent.ts +0 -843
- package/core/client/hooks/useRoom.ts +0 -409
- package/core/client/hooks/useRoomProxy.ts +0 -382
- package/core/server/live/ComponentRegistry.ts +0 -1099
- package/core/server/live/FileUploadManager.ts +0 -282
- package/core/server/live/LiveComponentPerformanceMonitor.ts +0 -931
- package/core/server/live/LiveLogger.ts +0 -111
- package/core/server/live/LiveRoomManager.ts +0 -262
- package/core/server/live/RoomEventBus.ts +0 -234
- package/core/server/live/RoomStateManager.ts +0 -172
- package/core/server/live/SingleConnectionManager.ts +0 -0
- package/core/server/live/StateSignature.ts +0 -645
- package/core/server/live/WebSocketConnectionManager.ts +0 -709
- package/core/server/live/auth/LiveAuthContext.ts +0 -71
- package/core/server/live/auth/LiveAuthManager.ts +0 -304
- package/core/server/live/auth/index.ts +0 -19
- package/core/server/live/auth/types.ts +0 -179
|
@@ -1,382 +0,0 @@
|
|
|
1
|
-
// 🔥 FluxStack Room Proxy - Sistema de salas integrado ao LiveComponent
|
|
2
|
-
//
|
|
3
|
-
// Uso no frontend:
|
|
4
|
-
// const chat = Live.use(LiveChat, { room: 'sala-principal' })
|
|
5
|
-
//
|
|
6
|
-
// // Sala padrão (definida no options.room)
|
|
7
|
-
// chat.$room.emit('typing', { user: 'João' })
|
|
8
|
-
// chat.$room.on('message:new', handler)
|
|
9
|
-
// chat.$room.state
|
|
10
|
-
//
|
|
11
|
-
// // Outras salas
|
|
12
|
-
// chat.$room('sala-vip').join()
|
|
13
|
-
// chat.$room('sala-vip').emit('evento', data)
|
|
14
|
-
// chat.$room('sala-vip').on('evento', handler)
|
|
15
|
-
// chat.$room('sala-vip').leave()
|
|
16
|
-
//
|
|
17
|
-
// // Listar salas
|
|
18
|
-
// chat.$rooms // ['sala-principal', 'sala-vip']
|
|
19
|
-
|
|
20
|
-
type EventHandler<T = any> = (data: T) => void
|
|
21
|
-
type Unsubscribe = () => void
|
|
22
|
-
|
|
23
|
-
// Mensagem do cliente para o servidor
|
|
24
|
-
export interface RoomClientMessage {
|
|
25
|
-
type: 'ROOM_JOIN' | 'ROOM_LEAVE' | 'ROOM_EMIT' | 'ROOM_STATE_GET' | 'ROOM_STATE_SET'
|
|
26
|
-
componentId: string
|
|
27
|
-
roomId: string
|
|
28
|
-
event?: string
|
|
29
|
-
data?: any
|
|
30
|
-
timestamp: number
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Mensagem do servidor para o cliente
|
|
34
|
-
export interface RoomServerMessage {
|
|
35
|
-
type: 'ROOM_EVENT' | 'ROOM_STATE' | 'ROOM_SYSTEM' | 'ROOM_JOINED' | 'ROOM_LEFT'
|
|
36
|
-
componentId: string
|
|
37
|
-
roomId: string
|
|
38
|
-
event: string
|
|
39
|
-
data: any
|
|
40
|
-
timestamp: number
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Interface de uma sala individual
|
|
44
|
-
export interface RoomHandle<TState = any, TEvents extends Record<string, any> = Record<string, any>> {
|
|
45
|
-
/** ID da sala */
|
|
46
|
-
readonly id: string
|
|
47
|
-
|
|
48
|
-
/** Se está participando desta sala */
|
|
49
|
-
readonly joined: boolean
|
|
50
|
-
|
|
51
|
-
/** Estado compartilhado da sala */
|
|
52
|
-
readonly state: TState
|
|
53
|
-
|
|
54
|
-
/** Entrar na sala */
|
|
55
|
-
join: (initialState?: TState) => Promise<void>
|
|
56
|
-
|
|
57
|
-
/** Sair da sala */
|
|
58
|
-
leave: () => Promise<void>
|
|
59
|
-
|
|
60
|
-
/** Emitir evento para a sala */
|
|
61
|
-
emit: <K extends keyof TEvents>(event: K, data: TEvents[K]) => void
|
|
62
|
-
|
|
63
|
-
/** Escutar evento da sala */
|
|
64
|
-
on: <K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>) => Unsubscribe
|
|
65
|
-
|
|
66
|
-
/** Escutar evento do sistema ($state:change, $sub:join, etc) */
|
|
67
|
-
onSystem: (event: string, handler: EventHandler) => Unsubscribe
|
|
68
|
-
|
|
69
|
-
/** Atualizar estado da sala */
|
|
70
|
-
setState: (updates: Partial<TState>) => void
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Interface do proxy $room
|
|
74
|
-
export interface RoomProxy<TState = any, TEvents extends Record<string, any> = Record<string, any>> {
|
|
75
|
-
// Quando chamado como função: $room('sala-id')
|
|
76
|
-
(roomId: string): RoomHandle<TState, TEvents>
|
|
77
|
-
|
|
78
|
-
// Quando acessado como objeto: $room.emit() (usa sala padrão)
|
|
79
|
-
readonly id: string | null
|
|
80
|
-
readonly joined: boolean
|
|
81
|
-
readonly state: TState
|
|
82
|
-
join: (initialState?: TState) => Promise<void>
|
|
83
|
-
leave: () => Promise<void>
|
|
84
|
-
emit: <K extends keyof TEvents>(event: K, data: TEvents[K]) => void
|
|
85
|
-
on: <K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>) => Unsubscribe
|
|
86
|
-
onSystem: (event: string, handler: EventHandler) => Unsubscribe
|
|
87
|
-
setState: (updates: Partial<TState>) => void
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Opções para criar o RoomManager
|
|
91
|
-
export interface RoomManagerOptions {
|
|
92
|
-
componentId: string | null
|
|
93
|
-
defaultRoom?: string
|
|
94
|
-
sendMessage: (msg: any) => void
|
|
95
|
-
sendMessageAndWait: (msg: any, timeout?: number) => Promise<any>
|
|
96
|
-
onMessage: (handler: (msg: RoomServerMessage) => void) => Unsubscribe
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Classe interna para gerenciar salas
|
|
100
|
-
export class RoomManager<TState = any, TEvents extends Record<string, any> = Record<string, any>> {
|
|
101
|
-
private componentId: string | null
|
|
102
|
-
private defaultRoom: string | null
|
|
103
|
-
private rooms = new Map<string, {
|
|
104
|
-
joined: boolean
|
|
105
|
-
state: TState
|
|
106
|
-
handlers: Map<string, Set<EventHandler>>
|
|
107
|
-
}>()
|
|
108
|
-
private handles = new Map<string, RoomHandle<TState, TEvents>>() // Cache de handles
|
|
109
|
-
private sendMessage: (msg: any) => void
|
|
110
|
-
private sendMessageAndWait: (msg: any, timeout?: number) => Promise<any>
|
|
111
|
-
private globalUnsubscribe: Unsubscribe | null = null
|
|
112
|
-
|
|
113
|
-
constructor(options: RoomManagerOptions) {
|
|
114
|
-
this.componentId = options.componentId
|
|
115
|
-
this.defaultRoom = options.defaultRoom || null
|
|
116
|
-
this.sendMessage = options.sendMessage
|
|
117
|
-
this.sendMessageAndWait = options.sendMessageAndWait
|
|
118
|
-
|
|
119
|
-
// Escutar mensagens do servidor
|
|
120
|
-
this.globalUnsubscribe = options.onMessage((msg) => this.handleServerMessage(msg))
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
private handleServerMessage(msg: RoomServerMessage): void {
|
|
124
|
-
if (msg.componentId !== this.componentId) return
|
|
125
|
-
|
|
126
|
-
const room = this.rooms.get(msg.roomId)
|
|
127
|
-
if (!room) return
|
|
128
|
-
|
|
129
|
-
switch (msg.type) {
|
|
130
|
-
case 'ROOM_EVENT':
|
|
131
|
-
case 'ROOM_SYSTEM':
|
|
132
|
-
// Chamar handlers registrados
|
|
133
|
-
const handlers = room.handlers.get(msg.event)
|
|
134
|
-
if (handlers) {
|
|
135
|
-
for (const handler of handlers) {
|
|
136
|
-
try {
|
|
137
|
-
handler(msg.data)
|
|
138
|
-
} catch (error) {
|
|
139
|
-
console.error(`[Room:${msg.roomId}] Handler error for '${msg.event}':`, error)
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
break
|
|
144
|
-
|
|
145
|
-
case 'ROOM_STATE':
|
|
146
|
-
// Atualizar estado local
|
|
147
|
-
room.state = { ...room.state, ...msg.data }
|
|
148
|
-
// Emitir evento de mudança
|
|
149
|
-
const stateHandlers = room.handlers.get('$state:change')
|
|
150
|
-
if (stateHandlers) {
|
|
151
|
-
for (const handler of stateHandlers) {
|
|
152
|
-
handler(msg.data)
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
break
|
|
156
|
-
|
|
157
|
-
case 'ROOM_JOINED':
|
|
158
|
-
room.joined = true
|
|
159
|
-
if (msg.data?.state) {
|
|
160
|
-
room.state = msg.data.state
|
|
161
|
-
}
|
|
162
|
-
break
|
|
163
|
-
|
|
164
|
-
case 'ROOM_LEFT':
|
|
165
|
-
room.joined = false
|
|
166
|
-
break
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
private getOrCreateRoom(roomId: string): typeof this.rooms extends Map<string, infer V> ? V : never {
|
|
171
|
-
if (!this.rooms.has(roomId)) {
|
|
172
|
-
this.rooms.set(roomId, {
|
|
173
|
-
joined: false,
|
|
174
|
-
state: {} as TState,
|
|
175
|
-
handlers: new Map()
|
|
176
|
-
})
|
|
177
|
-
}
|
|
178
|
-
return this.rooms.get(roomId)!
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Criar handle para uma sala específica (com cache)
|
|
182
|
-
createHandle(roomId: string): RoomHandle<TState, TEvents> {
|
|
183
|
-
// Retornar handle cacheado se existir
|
|
184
|
-
if (this.handles.has(roomId)) {
|
|
185
|
-
return this.handles.get(roomId)!
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const room = this.getOrCreateRoom(roomId)
|
|
189
|
-
|
|
190
|
-
const handle: RoomHandle<TState, TEvents> = {
|
|
191
|
-
get id() { return roomId },
|
|
192
|
-
get joined() { return room.joined },
|
|
193
|
-
get state() { return room.state },
|
|
194
|
-
|
|
195
|
-
join: async (initialState?: TState) => {
|
|
196
|
-
if (!this.componentId) throw new Error('Component not mounted')
|
|
197
|
-
if (room.joined) return
|
|
198
|
-
|
|
199
|
-
if (initialState) {
|
|
200
|
-
room.state = initialState
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const response = await this.sendMessageAndWait({
|
|
204
|
-
type: 'ROOM_JOIN',
|
|
205
|
-
componentId: this.componentId,
|
|
206
|
-
roomId,
|
|
207
|
-
data: { initialState: room.state },
|
|
208
|
-
timestamp: Date.now()
|
|
209
|
-
}, 5000)
|
|
210
|
-
|
|
211
|
-
if (response?.success) {
|
|
212
|
-
room.joined = true
|
|
213
|
-
if (response.state) {
|
|
214
|
-
room.state = response.state
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
},
|
|
218
|
-
|
|
219
|
-
leave: async () => {
|
|
220
|
-
if (!this.componentId || !room.joined) return
|
|
221
|
-
|
|
222
|
-
await this.sendMessageAndWait({
|
|
223
|
-
type: 'ROOM_LEAVE',
|
|
224
|
-
componentId: this.componentId,
|
|
225
|
-
roomId,
|
|
226
|
-
timestamp: Date.now()
|
|
227
|
-
}, 5000)
|
|
228
|
-
|
|
229
|
-
room.joined = false
|
|
230
|
-
room.handlers.clear()
|
|
231
|
-
},
|
|
232
|
-
|
|
233
|
-
emit: <K extends keyof TEvents>(event: K, data: TEvents[K]) => {
|
|
234
|
-
if (!this.componentId) return
|
|
235
|
-
|
|
236
|
-
this.sendMessage({
|
|
237
|
-
type: 'ROOM_EMIT',
|
|
238
|
-
componentId: this.componentId,
|
|
239
|
-
roomId,
|
|
240
|
-
event: event as string,
|
|
241
|
-
data,
|
|
242
|
-
timestamp: Date.now()
|
|
243
|
-
})
|
|
244
|
-
},
|
|
245
|
-
|
|
246
|
-
on: <K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): Unsubscribe => {
|
|
247
|
-
const eventKey = event as string
|
|
248
|
-
|
|
249
|
-
if (!room.handlers.has(eventKey)) {
|
|
250
|
-
room.handlers.set(eventKey, new Set())
|
|
251
|
-
}
|
|
252
|
-
room.handlers.get(eventKey)!.add(handler)
|
|
253
|
-
|
|
254
|
-
return () => {
|
|
255
|
-
room.handlers.get(eventKey)?.delete(handler)
|
|
256
|
-
}
|
|
257
|
-
},
|
|
258
|
-
|
|
259
|
-
onSystem: (event: string, handler: EventHandler): Unsubscribe => {
|
|
260
|
-
const eventKey = `$${event}`
|
|
261
|
-
|
|
262
|
-
if (!room.handlers.has(eventKey)) {
|
|
263
|
-
room.handlers.set(eventKey, new Set())
|
|
264
|
-
}
|
|
265
|
-
room.handlers.get(eventKey)!.add(handler)
|
|
266
|
-
|
|
267
|
-
return () => {
|
|
268
|
-
room.handlers.get(eventKey)?.delete(handler)
|
|
269
|
-
}
|
|
270
|
-
},
|
|
271
|
-
|
|
272
|
-
setState: (updates: Partial<TState>) => {
|
|
273
|
-
if (!this.componentId) return
|
|
274
|
-
|
|
275
|
-
// Atualiza localmente (otimista)
|
|
276
|
-
room.state = { ...room.state, ...updates }
|
|
277
|
-
|
|
278
|
-
// Envia para o servidor
|
|
279
|
-
this.sendMessage({
|
|
280
|
-
type: 'ROOM_STATE_SET',
|
|
281
|
-
componentId: this.componentId,
|
|
282
|
-
roomId,
|
|
283
|
-
data: updates,
|
|
284
|
-
timestamp: Date.now()
|
|
285
|
-
})
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// Cachear handle
|
|
290
|
-
this.handles.set(roomId, handle)
|
|
291
|
-
|
|
292
|
-
return handle
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Criar o proxy $room
|
|
296
|
-
createProxy(): RoomProxy<TState, TEvents> {
|
|
297
|
-
const self = this
|
|
298
|
-
|
|
299
|
-
// Função que também tem propriedades
|
|
300
|
-
const proxyFn = function(roomId: string): RoomHandle<TState, TEvents> {
|
|
301
|
-
return self.createHandle(roomId)
|
|
302
|
-
} as RoomProxy<TState, TEvents>
|
|
303
|
-
|
|
304
|
-
// Se tem sala padrão, expor métodos diretamente
|
|
305
|
-
const defaultHandle = this.defaultRoom ? this.createHandle(this.defaultRoom) : null
|
|
306
|
-
|
|
307
|
-
Object.defineProperties(proxyFn, {
|
|
308
|
-
id: {
|
|
309
|
-
get: () => this.defaultRoom
|
|
310
|
-
},
|
|
311
|
-
joined: {
|
|
312
|
-
get: () => defaultHandle?.joined ?? false
|
|
313
|
-
},
|
|
314
|
-
state: {
|
|
315
|
-
get: () => defaultHandle?.state ?? ({} as TState)
|
|
316
|
-
},
|
|
317
|
-
join: {
|
|
318
|
-
value: async (initialState?: TState) => {
|
|
319
|
-
if (!defaultHandle) throw new Error('No default room set')
|
|
320
|
-
return defaultHandle.join(initialState)
|
|
321
|
-
}
|
|
322
|
-
},
|
|
323
|
-
leave: {
|
|
324
|
-
value: async () => {
|
|
325
|
-
if (!defaultHandle) throw new Error('No default room set')
|
|
326
|
-
return defaultHandle.leave()
|
|
327
|
-
}
|
|
328
|
-
},
|
|
329
|
-
emit: {
|
|
330
|
-
value: <K extends keyof TEvents>(event: K, data: TEvents[K]) => {
|
|
331
|
-
if (!defaultHandle) throw new Error('No default room set')
|
|
332
|
-
return defaultHandle.emit(event, data)
|
|
333
|
-
}
|
|
334
|
-
},
|
|
335
|
-
on: {
|
|
336
|
-
value: <K extends keyof TEvents>(event: K, handler: EventHandler<TEvents[K]>): Unsubscribe => {
|
|
337
|
-
if (!defaultHandle) throw new Error('No default room set')
|
|
338
|
-
return defaultHandle.on(event, handler)
|
|
339
|
-
}
|
|
340
|
-
},
|
|
341
|
-
onSystem: {
|
|
342
|
-
value: (event: string, handler: EventHandler): Unsubscribe => {
|
|
343
|
-
if (!defaultHandle) throw new Error('No default room set')
|
|
344
|
-
return defaultHandle.onSystem(event, handler)
|
|
345
|
-
}
|
|
346
|
-
},
|
|
347
|
-
setState: {
|
|
348
|
-
value: (updates: Partial<TState>) => {
|
|
349
|
-
if (!defaultHandle) throw new Error('No default room set')
|
|
350
|
-
return defaultHandle.setState(updates)
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
})
|
|
354
|
-
|
|
355
|
-
return proxyFn
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// Lista de salas que está participando
|
|
359
|
-
getJoinedRooms(): string[] {
|
|
360
|
-
const joined: string[] = []
|
|
361
|
-
for (const [id, room] of this.rooms) {
|
|
362
|
-
if (room.joined) joined.push(id)
|
|
363
|
-
}
|
|
364
|
-
return joined
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// Atualizar componentId (quando monta)
|
|
368
|
-
setComponentId(id: string | null): void {
|
|
369
|
-
this.componentId = id
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// Cleanup
|
|
373
|
-
destroy(): void {
|
|
374
|
-
this.globalUnsubscribe?.()
|
|
375
|
-
for (const [, room] of this.rooms) {
|
|
376
|
-
room.handlers.clear()
|
|
377
|
-
}
|
|
378
|
-
this.rooms.clear()
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
export type { EventHandler, Unsubscribe }
|