From 2266232d76008eac41b04e9cf265ea499589d6a5 Mon Sep 17 00:00:00 2001 From: MarcosBrendonDePaula Date: Fri, 3 Apr 2026 00:26:36 -0300 Subject: [PATCH] refactor: remove deprecated exports (PluginDiscovery, ComponentRegistry, singletons) Closes #84 - Remove all deprecated lazy proxy singletons from core/server/live/index.ts (componentRegistry, connectionManager, liveRoomManager, roomEvents, fileUploadManager, performanceMonitor, stateSignature, roomState, liveAuthManager) - Add registerAuthProvider() as clean replacement for liveAuthManager.register() - Migrate app/server/index.ts to use registerAuthProvider() - Migrate app/server/routes/room.routes.ts to use liveServer.roomManager/roomEvents - Migrate core/framework/server.ts to use liveServer.registry - Migrate plugins/crypto-auth to use registerAuthProvider() - Remove pluginDiscovery singleton export from core/index.ts and core/plugins/index.ts - Remove staticPlugin re-export from core/server/index.ts - Update tests to match new exports Co-Authored-By: Claude Opus 4.6 (1M context) --- app/server/index.ts | 5 +- app/server/routes/room.routes.ts | 12 +- core/__tests__/integration.test.ts | 2 - core/framework/server.ts | 4 +- core/index.ts | 2 +- core/plugins/index.ts | 2 +- core/server/index.ts | 4 +- core/server/live/index.ts | 78 +-------- create-fluxstack.ts | 2 +- plugins/crypto-auth/index.ts | 4 +- tests/unit/core/live-compat-guard.test.ts | 185 ++++------------------ 11 files changed, 55 insertions(+), 245 deletions(-) diff --git a/app/server/index.ts b/app/server/index.ts index 5e746fbb..6844ed8b 100644 --- a/app/server/index.ts +++ b/app/server/index.ts @@ -13,19 +13,18 @@ import { FluxStackFramework } from "@core/server" import { vitePlugin } from "@core/plugins/built-in/vite" import { swaggerPlugin } from "@core/plugins/built-in/swagger" -import { liveComponentsPlugin } from "@core/server/live" +import { liveComponentsPlugin, registerAuthProvider } from "@core/server/live" import { appInstance } from "@server/app" import { appConfig } from "@config" // 🔒 Auth provider para Live Components -import { liveAuthManager } from "@core/server/live" import { DevAuthProvider } from "./auth/DevAuthProvider" // 🔐 Auth system (Guard + Provider, Laravel-inspired) import { initAuth } from "@server/auth" // Registrar provider de desenvolvimento (tokens simples para testes) -liveAuthManager.register(new DevAuthProvider()) +registerAuthProvider(new DevAuthProvider()) console.log('🔓 DevAuthProvider registered') // Inicializar sistema de autenticação diff --git a/app/server/routes/room.routes.ts b/app/server/routes/room.routes.ts index 6fd959e1..1319b22c 100644 --- a/app/server/routes/room.routes.ts +++ b/app/server/routes/room.routes.ts @@ -4,7 +4,7 @@ // enviem mensagens para salas de chat via API REST import { Elysia, t } from 'elysia' -import { liveRoomManager, roomEvents } from '@core/server/live' +import { liveServer } from '@core/server/live' export const roomRoutes = new Elysia({ prefix: '/rooms' }) @@ -22,9 +22,9 @@ export const roomRoutes = new Elysia({ prefix: '/rooms' }) // Emitir evento para a sala // Isso vai: - // 1. Notificar handlers server-side via roomEvents + // 1. Notificar handlers server-side via liveServer!.roomEvents // 2. Broadcast via WebSocket para frontends - const notified = liveRoomManager.emitToRoom(roomId, 'message:new', message) + const notified = liveServer!.roomManager.emitToRoom(roomId, 'message:new', message) return { success: true, @@ -63,7 +63,7 @@ export const roomRoutes = new Elysia({ prefix: '/rooms' }) const { roomId } = params const { event, data } = body - const notified = liveRoomManager.emitToRoom(roomId, event, data) + const notified = liveServer!.roomManager.emitToRoom(roomId, event, data) return { success: true, @@ -94,8 +94,8 @@ export const roomRoutes = new Elysia({ prefix: '/rooms' }) // Obter estatísticas das salas .get('/stats', () => { - const roomStats = liveRoomManager.getStats() - const eventStats = roomEvents.getStats() + const roomStats = liveServer!.roomManager.getStats() + const eventStats = liveServer!.roomEvents.getStats() return { success: true, diff --git a/core/__tests__/integration.test.ts b/core/__tests__/integration.test.ts index 09cde6f4..2fdf543c 100644 --- a/core/__tests__/integration.test.ts +++ b/core/__tests__/integration.test.ts @@ -186,9 +186,7 @@ describe('Core Framework Integration', () => { expect(serverExports.FluxStackFramework).toBeDefined() expect(serverExports.PluginRegistry).toBeDefined() - expect(serverExports.loggerPlugin).toBeDefined() expect(serverExports.vitePlugin).toBeDefined() - expect(serverExports.staticPlugin).toBeDefined() expect(serverExports.swaggerPlugin).toBeDefined() } catch (error) { // Skip this test if there are environment issues (e.g., esbuild + Windows + Bun) diff --git a/core/framework/server.ts b/core/framework/server.ts index c0d3fbb5..25516683 100644 --- a/core/framework/server.ts +++ b/core/framework/server.ts @@ -7,7 +7,7 @@ import { fluxStackConfig } from "@config" import { getEnvironmentInfo } from "@core/config" import { logger, type Logger } from "@core/utils/logger" import { displayStartupBanner, type StartupInfo } from "@core/utils/logger/startup-banner" -import { componentRegistry } from "@core/server/live" +import { liveServer } from "@core/server/live" import { FluxStackError } from "@core/utils/errors" import { createTimer, formatBytes, isProduction, isDevelopment } from "@core/utils/helpers" import { createHash } from "crypto" @@ -826,7 +826,7 @@ export class FluxStackFramework { vitePort: this.cfg.client?.port, viteEmbedded: vitePluginActive, // Vite is embedded when plugin is active swaggerPath: '/swagger', // TODO: Get from swagger plugin config - liveComponents: componentRegistry.getRegisteredComponentNames() + liveComponents: liveServer?.registry.getRegisteredComponentNames() ?? [] } // Display banner if enabled diff --git a/core/index.ts b/core/index.ts index 5c626a05..cae1b4f3 100644 --- a/core/index.ts +++ b/core/index.ts @@ -24,7 +24,7 @@ export * from './cli/generators' // Plugin system (avoid wildcard to prevent conflicts) export { PluginRegistry } from './plugins/registry' -export { PluginDiscovery, pluginDiscovery } from './plugins/discovery' +export { PluginDiscovery } from './plugins/discovery' export { PluginManager } from './plugins/manager' export { PluginUtils } from './plugins' diff --git a/core/plugins/index.ts b/core/plugins/index.ts index a0428039..f790ae10 100644 --- a/core/plugins/index.ts +++ b/core/plugins/index.ts @@ -34,7 +34,7 @@ export { PluginRegistry } from './registry' export type { PluginRegistryConfig } from './registry' // Plugin discovery -export { PluginDiscovery, pluginDiscovery } from './discovery' +export { PluginDiscovery } from './discovery' export type { PluginDiscoveryConfig } from './discovery' // Plugin configuration management diff --git a/core/server/index.ts b/core/server/index.ts index fa2623be..79a982b4 100644 --- a/core/server/index.ts +++ b/core/server/index.ts @@ -1,12 +1,12 @@ // FluxStack framework exports export { FluxStackFramework } from "../framework/server" -export { vitePlugin, staticPlugin } from "../plugins/built-in" +export { vitePlugin } from "../plugins/built-in" export { swaggerPlugin } from "../plugins/built-in/swagger" export { PluginRegistry } from "../plugins/registry" export * from "../types" // Live Components exports -export { liveComponentsPlugin, componentRegistry } from "./live" +export { liveComponentsPlugin, liveServer, registerAuthProvider } from "./live" export { LiveComponent } from "../types/types" // Static Files Plugin diff --git a/core/server/live/index.ts b/core/server/live/index.ts index 31ad3014..1a0f7c7f 100644 --- a/core/server/live/index.ts +++ b/core/server/live/index.ts @@ -33,78 +33,14 @@ export type { LiveAuthResult, } from '@fluxstack/live' -// Backward-compatible singleton accessors -// These lazily access the LiveServer instance created by the plugin -import { liveServer, pendingAuthProviders } from './websocket-plugin' +// Register auth provider — buffers if LiveServer not yet initialized +import { liveServer as _ls, pendingAuthProviders } from './websocket-plugin' import type { LiveAuthProvider as _LiveAuthProvider } from '@fluxstack/live' -import type { ComponentRegistry as _ComponentRegistry } from '@fluxstack/live' -import type { WebSocketConnectionManager as _WebSocketConnectionManager } from '@fluxstack/live' -import type { RoomStateManager as _RoomStateManager } from '@fluxstack/live' -import type { LiveRoomManager as _LiveRoomManager } from '@fluxstack/live' -import type { RoomEventBus as _RoomEventBus } from '@fluxstack/live' -import type { FileUploadManager as _FileUploadManager } from '@fluxstack/live' -import type { PerformanceMonitor as _PerformanceMonitor } from '@fluxstack/live' -import type { StateSignatureManager as _StateSignatureManager } from '@fluxstack/live' -import type { LiveAuthManager as _LiveAuthManager } from '@fluxstack/live' -function requireLiveServer() { - if (!liveServer) { - throw new Error( - 'LiveComponents plugin not initialized. ' + - 'Ensure the live-components plugin is loaded before accessing Live singletons.' - ) +export function registerAuthProvider(provider: _LiveAuthProvider) { + if (_ls) { + _ls.useAuth(provider) + } else { + pendingAuthProviders.push(provider) } - return liveServer } - -/** - * Backward-compatible liveAuthManager. - * Buffers register() calls that happen before the plugin setup(), - * then delegates to liveServer.authManager once available. - * @deprecated Access via liveServer.authManager instead - */ -export const liveAuthManager: Pick<_LiveAuthManager, 'authenticate' | 'hasProviders' | 'authorizeRoom' | 'authorizeAction' | 'authorizeComponent'> & { register: (provider: _LiveAuthProvider) => void } = { - register(provider: _LiveAuthProvider) { - if (liveServer) { - liveServer.useAuth(provider) - } else { - pendingAuthProviders.push(provider) - } - }, - get authenticate() { return requireLiveServer().authManager.authenticate.bind(requireLiveServer().authManager) }, - get hasProviders() { return requireLiveServer().authManager.hasProviders.bind(requireLiveServer().authManager) }, - get authorizeRoom() { return requireLiveServer().authManager.authorizeRoom.bind(requireLiveServer().authManager) }, - get authorizeAction() { return requireLiveServer().authManager.authorizeAction.bind(requireLiveServer().authManager) }, - get authorizeComponent() { return requireLiveServer().authManager.authorizeComponent.bind(requireLiveServer().authManager) }, -} - -/** Helper to create a typed lazy proxy that delegates to a LiveServer property */ -function createLazyProxy(accessor: () => T): T { - return new Proxy({} as T, { - get(_, prop) { return (accessor() as Record)[prop] } - }) -} - -/** @deprecated Access via liveServer.registry instead */ -export const componentRegistry = createLazyProxy<_ComponentRegistry>(() => requireLiveServer().registry) - -/** @deprecated Access via liveServer.connectionManager instead */ -export const connectionManager = createLazyProxy<_WebSocketConnectionManager>(() => requireLiveServer().connectionManager) - -/** @deprecated Access via liveServer.roomManager instead */ -export const liveRoomManager = createLazyProxy<_LiveRoomManager>(() => requireLiveServer().roomManager) - -/** @deprecated Access via liveServer.roomEvents instead */ -export const roomEvents = createLazyProxy<_RoomEventBus>(() => requireLiveServer().roomEvents) - -/** @deprecated Access via liveServer.fileUploadManager instead */ -export const fileUploadManager = createLazyProxy<_FileUploadManager>(() => requireLiveServer().fileUploadManager) - -/** @deprecated Access via liveServer.performanceMonitor instead */ -export const performanceMonitor = createLazyProxy<_PerformanceMonitor>(() => requireLiveServer().performanceMonitor) - -/** @deprecated Access via liveServer.stateSignature instead */ -export const stateSignature = createLazyProxy<_StateSignatureManager>(() => requireLiveServer().stateSignature) - -// Room state backward compat -export const roomState = createLazyProxy<_LiveRoomManager>(() => requireLiveServer().roomManager) diff --git a/create-fluxstack.ts b/create-fluxstack.ts index c6d1d02d..831c3a0b 100644 --- a/create-fluxstack.ts +++ b/create-fluxstack.ts @@ -418,7 +418,7 @@ FluxStack includes several built-in plugins that are ready to use: \`\`\`typescript // app/server/index.ts -import { loggerPlugin, swaggerPlugin, staticPlugin } from "@core/server" +import { swaggerPlugin } from "@core/server" // Add built-in plugins app.use(loggerPlugin) diff --git a/plugins/crypto-auth/index.ts b/plugins/crypto-auth/index.ts index 9d07fae7..336a7013 100644 --- a/plugins/crypto-auth/index.ts +++ b/plugins/crypto-auth/index.ts @@ -9,7 +9,7 @@ type Plugin = FluxStack.Plugin import { Elysia, t } from "elysia" import { CryptoAuthService, AuthMiddleware } from "./server" import { CryptoAuthLiveProvider } from "./server/CryptoAuthLiveProvider" -import { liveAuthManager } from "@core/server/live" +import { registerAuthProvider } from "@core/server/live" import { makeProtectedRouteCommand } from "./cli/make-protected-route.command" // ✅ Plugin carrega sua própria configuração (da pasta config/ do plugin) @@ -91,7 +91,7 @@ export const cryptoAuthPlugin: Plugin = { ;(global as any).cryptoAuthMiddleware = authMiddleware // 🔒 Register as LiveAuthProvider for Live Components WebSocket auth - liveAuthManager.register(new CryptoAuthLiveProvider(authService)) + registerAuthProvider(new CryptoAuthLiveProvider(authService)) context.logger.info('🔒 Crypto Auth registered as Live Components auth provider') // Store plugin info for table display diff --git a/tests/unit/core/live-compat-guard.test.ts b/tests/unit/core/live-compat-guard.test.ts index a341df9a..90e9634d 100644 --- a/tests/unit/core/live-compat-guard.test.ts +++ b/tests/unit/core/live-compat-guard.test.ts @@ -1,171 +1,48 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' /** - * Tests for the Live Components compat layer null guard (#85). + * Tests for registerAuthProvider (#84). * * When the LiveComponents plugin has not been initialized yet, - * accessing any deprecated singleton should throw a descriptive error - * instead of crashing with an opaque null dereference. + * registerAuthProvider should buffer the provider in pendingAuthProviders. + * When liveServer is available, it should delegate directly to liveServer.useAuth(). */ -// We need to control the value of liveServer exported from websocket-plugin. -// The compat layer in core/server/live/index.ts imports liveServer at module -// level, so we mock the websocket-plugin module to control its value. - -const EXPECTED_ERROR = 'LiveComponents plugin not initialized' - -describe('Live Components compat layer null guard (#85)', () => { - describe('when liveServer is null (plugin not initialized)', () => { - beforeEach(() => { - vi.resetModules() - }) - - afterEach(() => { - vi.restoreAllMocks() - }) - - it('componentRegistry proxy throws descriptive error', async () => { - vi.doMock('../../../core/server/live/websocket-plugin', () => ({ - liveServer: null, - pendingAuthProviders: [], - })) - const { componentRegistry } = await import('../../../core/server/live/index') - - expect(() => componentRegistry.someMethod).toThrowError(EXPECTED_ERROR) - }) - - it('connectionManager proxy throws descriptive error', async () => { - vi.doMock('../../../core/server/live/websocket-plugin', () => ({ - liveServer: null, - pendingAuthProviders: [], - })) - const { connectionManager } = await import('../../../core/server/live/index') - - expect(() => connectionManager.someMethod).toThrowError(EXPECTED_ERROR) - }) - - it('liveRoomManager proxy throws descriptive error', async () => { - vi.doMock('../../../core/server/live/websocket-plugin', () => ({ - liveServer: null, - pendingAuthProviders: [], - })) - const { liveRoomManager } = await import('../../../core/server/live/index') - - expect(() => liveRoomManager.someMethod).toThrowError(EXPECTED_ERROR) - }) - - it('roomEvents proxy throws descriptive error', async () => { - vi.doMock('../../../core/server/live/websocket-plugin', () => ({ - liveServer: null, - pendingAuthProviders: [], - })) - const { roomEvents } = await import('../../../core/server/live/index') - - expect(() => roomEvents.someMethod).toThrowError(EXPECTED_ERROR) - }) - - it('fileUploadManager proxy throws descriptive error', async () => { - vi.doMock('../../../core/server/live/websocket-plugin', () => ({ - liveServer: null, - pendingAuthProviders: [], - })) - const { fileUploadManager } = await import('../../../core/server/live/index') - - expect(() => fileUploadManager.someMethod).toThrowError(EXPECTED_ERROR) - }) - - it('performanceMonitor proxy throws descriptive error', async () => { - vi.doMock('../../../core/server/live/websocket-plugin', () => ({ - liveServer: null, - pendingAuthProviders: [], - })) - const { performanceMonitor } = await import('../../../core/server/live/index') - - expect(() => performanceMonitor.someMethod).toThrowError(EXPECTED_ERROR) - }) - - it('stateSignature proxy throws descriptive error', async () => { - vi.doMock('../../../core/server/live/websocket-plugin', () => ({ - liveServer: null, - pendingAuthProviders: [], - })) - const { stateSignature } = await import('../../../core/server/live/index') - - expect(() => stateSignature.someMethod).toThrowError(EXPECTED_ERROR) - }) - - it('roomState proxy throws descriptive error', async () => { - vi.doMock('../../../core/server/live/websocket-plugin', () => ({ - liveServer: null, - pendingAuthProviders: [], - })) - const { roomState } = await import('../../../core/server/live/index') - - expect(() => roomState.someMethod).toThrowError(EXPECTED_ERROR) - }) - - it('liveAuthManager getters throw descriptive error', async () => { - vi.doMock('../../../core/server/live/websocket-plugin', () => ({ - liveServer: null, - pendingAuthProviders: [], - })) - const { liveAuthManager } = await import('../../../core/server/live/index') - - expect(() => liveAuthManager.authenticate).toThrowError(EXPECTED_ERROR) - expect(() => liveAuthManager.hasProviders).toThrowError(EXPECTED_ERROR) - expect(() => liveAuthManager.authorizeRoom).toThrowError(EXPECTED_ERROR) - expect(() => liveAuthManager.authorizeAction).toThrowError(EXPECTED_ERROR) - expect(() => liveAuthManager.authorizeComponent).toThrowError(EXPECTED_ERROR) - }) - - it('liveAuthManager.register() buffers provider when liveServer is null', async () => { - const pending: any[] = [] - vi.doMock('../../../core/server/live/websocket-plugin', () => ({ - liveServer: null, - pendingAuthProviders: pending, - })) - const { liveAuthManager } = await import('../../../core/server/live/index') - - const fakeProvider = { authenticate: vi.fn() } - liveAuthManager.register(fakeProvider) - - expect(pending).toHaveLength(1) - expect(pending[0]).toBe(fakeProvider) - }) +describe('registerAuthProvider (#84)', () => { + beforeEach(() => { + vi.resetModules() }) - describe('when liveServer is initialized', () => { - beforeEach(() => { - vi.resetModules() - }) + afterEach(() => { + vi.restoreAllMocks() + }) - afterEach(() => { - vi.restoreAllMocks() - }) + it('buffers provider when liveServer is null', async () => { + const pending: any[] = [] + vi.doMock('../../../core/server/live/websocket-plugin', () => ({ + liveServer: null, + pendingAuthProviders: pending, + })) + const { registerAuthProvider } = await import('../../../core/server/live/index') - it('componentRegistry proxy delegates to liveServer.registry', async () => { - const mockRegistry = { register: vi.fn(), get: vi.fn(() => 'found') } - vi.doMock('../../../core/server/live/websocket-plugin', () => ({ - liveServer: { registry: mockRegistry }, - pendingAuthProviders: [], - })) - const { componentRegistry } = await import('../../../core/server/live/index') + const fakeProvider = { authenticate: vi.fn() } + registerAuthProvider(fakeProvider) - expect(componentRegistry.get()).toBe('found') - }) + expect(pending).toHaveLength(1) + expect(pending[0]).toBe(fakeProvider) + }) - it('liveAuthManager.register() delegates to liveServer.useAuth', async () => { - const useAuth = vi.fn() - vi.doMock('../../../core/server/live/websocket-plugin', () => ({ - liveServer: { useAuth }, - pendingAuthProviders: [], - })) - const { liveAuthManager } = await import('../../../core/server/live/index') + it('delegates to liveServer.useAuth when initialized', async () => { + const useAuth = vi.fn() + vi.doMock('../../../core/server/live/websocket-plugin', () => ({ + liveServer: { useAuth }, + pendingAuthProviders: [], + })) + const { registerAuthProvider } = await import('../../../core/server/live/index') - const fakeProvider = { authenticate: vi.fn() } - liveAuthManager.register(fakeProvider) + const fakeProvider = { authenticate: vi.fn() } + registerAuthProvider(fakeProvider) - expect(useAuth).toHaveBeenCalledWith(fakeProvider) - }) + expect(useAuth).toHaveBeenCalledWith(fakeProvider) }) })