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,382 @@
|
|
|
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 }
|
package/core/client/index.ts
CHANGED
|
@@ -1,63 +1,30 @@
|
|
|
1
1
|
// 🔥 FluxStack Client Core - Main Export
|
|
2
2
|
|
|
3
|
+
// API Client (Eden Treaty)
|
|
4
|
+
export {
|
|
5
|
+
createEdenClient,
|
|
6
|
+
getErrorMessage,
|
|
7
|
+
getDefaultBaseUrl,
|
|
8
|
+
treaty,
|
|
9
|
+
type EdenClientOptions
|
|
10
|
+
} from './api'
|
|
11
|
+
|
|
3
12
|
// Live Components Provider (Singleton WebSocket Connection)
|
|
4
13
|
export {
|
|
5
14
|
LiveComponentsProvider,
|
|
6
|
-
useLiveComponents
|
|
7
|
-
// Deprecated exports for backward compatibility
|
|
8
|
-
WebSocketProvider,
|
|
9
|
-
useWebSocketContext
|
|
15
|
+
useLiveComponents
|
|
10
16
|
} from './LiveComponentsProvider'
|
|
11
17
|
export type {
|
|
12
18
|
LiveComponentsProviderProps,
|
|
13
|
-
LiveComponentsContextValue
|
|
14
|
-
// Deprecated types for backward compatibility
|
|
15
|
-
WebSocketProviderProps,
|
|
16
|
-
WebSocketContextValue
|
|
19
|
+
LiveComponentsContextValue
|
|
17
20
|
} from './LiveComponentsProvider'
|
|
18
21
|
|
|
19
|
-
//
|
|
20
|
-
export { useWebSocket } from './hooks/useWebSocket'
|
|
21
|
-
export { useHybridLiveComponent } from './hooks/useHybridLiveComponent'
|
|
22
|
+
// Chunked Upload Hook
|
|
22
23
|
export { useChunkedUpload } from './hooks/useChunkedUpload'
|
|
23
|
-
export {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
export
|
|
27
|
-
// Live Components types
|
|
28
|
-
LiveMessage,
|
|
29
|
-
ComponentState,
|
|
30
|
-
LiveComponentInstance,
|
|
31
|
-
WebSocketData,
|
|
32
|
-
ComponentDefinition,
|
|
33
|
-
BroadcastMessage,
|
|
34
|
-
LiveComponent,
|
|
35
|
-
|
|
36
|
-
// WebSocket types
|
|
37
|
-
WebSocketMessage,
|
|
38
|
-
WebSocketResponse,
|
|
39
|
-
|
|
40
|
-
// Hybrid Live Component types
|
|
41
|
-
HybridState,
|
|
42
|
-
StateValidation,
|
|
43
|
-
StateConflict,
|
|
44
|
-
HybridComponentOptions,
|
|
45
|
-
|
|
46
|
-
// File Upload types
|
|
47
|
-
FileChunkData,
|
|
48
|
-
FileUploadStartMessage,
|
|
49
|
-
FileUploadChunkMessage,
|
|
50
|
-
FileUploadCompleteMessage,
|
|
51
|
-
FileUploadProgressResponse,
|
|
52
|
-
FileUploadCompleteResponse,
|
|
53
|
-
ActiveUpload,
|
|
54
|
-
|
|
55
|
-
// Utility types
|
|
56
|
-
ComponentActions,
|
|
57
|
-
ComponentProps,
|
|
58
|
-
ActionParameters,
|
|
59
|
-
ActionReturnType
|
|
60
|
-
} from '../types/types'
|
|
24
|
+
export type { ChunkedUploadOptions, ChunkedUploadState } from './hooks/useChunkedUpload'
|
|
25
|
+
export { useLiveChunkedUpload } from './hooks/useLiveChunkedUpload'
|
|
26
|
+
export type { LiveChunkedUploadOptions } from './hooks/useLiveChunkedUpload'
|
|
27
|
+
export { useLiveUpload } from './hooks/useLiveUpload'
|
|
61
28
|
|
|
62
|
-
// Hook
|
|
63
|
-
export
|
|
29
|
+
// Live Component Hook (API principal)
|
|
30
|
+
export { Live } from './components/Live'
|
|
@@ -1,57 +1,78 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Standalone Frontend Development
|
|
3
|
+
* Starts Vite dev server directly without Elysia backend
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { clientConfig } from '@config'
|
|
7
|
+
import type { LogLevel } from 'vite'
|
|
8
|
+
|
|
9
|
+
type ViteDevServer = Awaited<ReturnType<typeof import('vite')['createServer']>>
|
|
10
|
+
|
|
11
|
+
let viteServer: ViteDevServer | null = null
|
|
12
|
+
|
|
13
|
+
export const startFrontendOnly = async (config: any = {}) => {
|
|
14
|
+
const port = config.vitePort || clientConfig.vite.port || 5173
|
|
15
|
+
const host = config.viteHost || clientConfig.vite.host || 'localhost'
|
|
16
|
+
const logLevel = (config.logLevel || clientConfig.vite.logLevel || 'info') as LogLevel
|
|
17
|
+
|
|
18
|
+
console.log(`⚛️ FluxStack Frontend Only`)
|
|
19
|
+
console.log(`🌐 http://${host}:${port}`)
|
|
13
20
|
console.log()
|
|
14
|
-
|
|
15
|
-
const viteProcess = spawn({
|
|
16
|
-
cmd: ["bun", "run", "dev"],
|
|
17
|
-
cwd: join(process.cwd(), clientPath),
|
|
18
|
-
stdout: "pipe",
|
|
19
|
-
stderr: "pipe",
|
|
20
|
-
env: {
|
|
21
|
-
...process.env,
|
|
22
|
-
VITE_API_URL: apiUrl,
|
|
23
|
-
PORT: port.toString(),
|
|
24
|
-
HOST: process.env.HOST || 'localhost'
|
|
25
|
-
}
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
if (viteProcess.stdout) {
|
|
29
|
-
viteProcess.stdout.pipeTo(new WritableStream({
|
|
30
|
-
write(chunk) {
|
|
31
|
-
const output = new TextDecoder().decode(chunk)
|
|
32
|
-
// Filtrar mensagens desnecessárias do Vite
|
|
33
|
-
if (!output.includes("hmr update") && !output.includes("Local:")) {
|
|
34
|
-
console.log(output)
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
})).catch(() => {}) // Ignore pipe errors
|
|
38
|
-
}
|
|
39
21
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
22
|
+
try {
|
|
23
|
+
// Dynamic import of vite
|
|
24
|
+
const { createServer } = await import('vite')
|
|
25
|
+
|
|
26
|
+
// Start Vite dev server programmatically
|
|
27
|
+
viteServer = await createServer({
|
|
28
|
+
configFile: './vite.config.ts',
|
|
29
|
+
server: {
|
|
30
|
+
port,
|
|
31
|
+
host,
|
|
32
|
+
strictPort: clientConfig.vite.strictPort
|
|
33
|
+
},
|
|
34
|
+
logLevel
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
await viteServer.listen()
|
|
38
|
+
|
|
39
|
+
console.log(`✅ Frontend server ready!`)
|
|
40
|
+
console.log()
|
|
41
|
+
|
|
42
|
+
// Setup cleanup on process exit
|
|
43
|
+
const cleanup = async () => {
|
|
44
|
+
if (viteServer) {
|
|
45
|
+
console.log('\n🛑 Stopping frontend...')
|
|
46
|
+
await viteServer.close()
|
|
47
|
+
viteServer = null
|
|
48
|
+
process.exit(0)
|
|
45
49
|
}
|
|
46
|
-
}
|
|
47
|
-
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
process.on('SIGINT', cleanup)
|
|
53
|
+
process.on('SIGTERM', cleanup)
|
|
54
|
+
process.on('exit', cleanup)
|
|
48
55
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
return viteServer
|
|
57
|
+
|
|
58
|
+
} catch (error) {
|
|
59
|
+
// Check if error is related to port already in use
|
|
60
|
+
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
61
|
+
const isPortInUse = errorMessage.includes('EADDRINUSE') ||
|
|
62
|
+
errorMessage.includes('address already in use') ||
|
|
63
|
+
(errorMessage.includes('Port') && errorMessage.includes('is in use'))
|
|
64
|
+
|
|
65
|
+
if (isPortInUse) {
|
|
66
|
+
console.error(`❌ Failed to start Vite: Port ${port} is already in use`)
|
|
67
|
+
console.log(`💡 Try one of these solutions:`)
|
|
68
|
+
console.log(` 1. Stop the process using port ${port}`)
|
|
69
|
+
console.log(` 2. Change VITE_PORT in your .env file`)
|
|
70
|
+
console.log(` 3. Kill the process: ${process.platform === 'win32' ? `netstat -ano | findstr :${port}` : `lsof -ti:${port} | xargs kill -9`}`)
|
|
71
|
+
process.exit(1)
|
|
72
|
+
} else {
|
|
73
|
+
console.error('❌ Failed to start Vite server:', errorMessage)
|
|
74
|
+
console.error('Full error:', error)
|
|
75
|
+
process.exit(1)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|