deps-upgrade(backend-api-most-significant): ⬆️ Update HTTP clients, React Query utilities, and related data-fetching dependencies

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Lilith 2026-02-20 11:10:54 -08:00
parent ab2e940dba
commit cabecd1011
7 changed files with 132 additions and 23 deletions

View file

@ -51,7 +51,7 @@ describe('useSendMessage', () => {
json: async () => mockResponse,
} as Response)
const { result } = renderHook(() => useSendMessage('thread-123'), {
const { result } = renderHook(() => useSendMessage('thread-123', 'user-1'), {
wrapper: createWrapper(),
})
@ -82,7 +82,7 @@ describe('useSendMessage', () => {
statusText: 'Bad Request',
} as Response)
const { result } = renderHook(() => useSendMessage('thread-123'), {
const { result } = renderHook(() => useSendMessage('thread-123', 'user-1'), {
wrapper: createWrapper(),
})
@ -104,7 +104,7 @@ describe('useSendMessage', () => {
statusText: 'Internal Server Error',
} as Response)
const { result } = renderHook(() => useSendMessage('thread-123'), {
const { result } = renderHook(() => useSendMessage('thread-123', 'user-1'), {
wrapper: createWrapper(),
})

View file

@ -40,7 +40,7 @@ async function sendMessageAPI(payload: SendMessagePayload): Promise<SendMessageR
/**
* Hook to send messages with optimistic updates
*/
export function useSendMessage(threadId: string) {
export function useSendMessage(threadId: string, userId: string) {
const queryClient = useQueryClient()
const mutation = useMutation({
@ -58,7 +58,7 @@ export function useSendMessage(threadId: string) {
const optimisticMessage: Message = {
id: `temp-${Date.now()}`,
roomId: payload.roomId,
senderId: 'current-user', // TODO: Get from auth context
senderId: userId,
content: payload.content,
messageType: payload.messageType || 'TEXT',
metadata: payload.metadata,

View file

@ -1,10 +1,16 @@
/**
* useSocket Hook
*
* React hook for WebSocket connection management
* React hook for WebSocket connection management using @lilith/websocket-client.
* Resolves the messaging service URL from the service registry and adapts the
* socket.io Socket to the typed SocketClient<TEmitEvents, TListenEvents> interface.
*/
import type { SocketClient } from '../types'
import { useEffect, useRef, useState, useCallback } from 'react'
import { WebSocketClient } from '@lilith/websocket-client'
import { getServiceRegistry } from '@lilith/service-registry'
import type { SocketClient, SocketEventHandler } from '../types'
export interface UseSocketOptions {
userId: string
@ -21,12 +27,115 @@ export interface UseSocketReturn<
disconnect: () => void
}
export function useSocket(_options: UseSocketOptions): UseSocketReturn {
// Stub implementation - to be fully implemented later
/**
* Derive the WebSocket URL for the messaging service from the service registry.
* Converts http:// → ws:// and https:// → wss://.
*/
function getMessagingWebSocketUrl(): string {
try {
const registry = getServiceRegistry()
const apiUrl = registry.getApiUrl('messaging')
if (apiUrl) {
return apiUrl.replace(/^http:\/\//, 'ws://').replace(/^https:\/\//, 'wss://')
}
} catch {
// Service registry unavailable in browser — fall through to window-based resolution
}
// Browser fallback: derive from current window location
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
return `${protocol}//${window.location.host}/messaging`
}
export function useSocket<
TEmitEvents extends Record<string, unknown> = Record<string, unknown>,
TListenEvents extends Record<string, unknown> = Record<string, unknown>,
>(options: UseSocketOptions): UseSocketReturn<TEmitEvents, TListenEvents> {
const { userId, autoConnect = true } = options
const clientRef = useRef<WebSocketClient | null>(null)
const [isConnected, setIsConnected] = useState(false)
const [socketClient, setSocketClient] = useState<SocketClient<TEmitEvents, TListenEvents> | null>(null)
const buildSocketClient = useCallback((wsClient: WebSocketClient): SocketClient<TEmitEvents, TListenEvents> => {
return {
emit<K extends keyof TEmitEvents>(event: K, data: TEmitEvents[K]): void {
wsClient.emit(event as string, data)
},
on<K extends keyof TListenEvents>(event: K, handler: SocketEventHandler<TListenEvents[K]>): void {
wsClient.on(event as string, handler as (data: unknown) => void)
},
off<K extends keyof TListenEvents>(event: K, handler: SocketEventHandler<TListenEvents[K]>): void {
wsClient.off(event as string, handler as (...args: unknown[]) => void)
},
}
}, [])
useEffect(() => {
const url = getMessagingWebSocketUrl()
const wsClient = new WebSocketClient({
url,
token: userId,
reconnection: true,
reconnectionAttempts: Infinity,
reconnectionDelay: 1000,
reconnectionDelayMax: 5000,
autoConnect: false,
})
clientRef.current = wsClient
setSocketClient(buildSocketClient(wsClient))
// Bind connection state listeners via internal socket
const trackConnection = () => {
const socket = wsClient.getSocket()
if (!socket) return
const onConnect = () => setIsConnected(true)
const onDisconnect = () => setIsConnected(false)
socket.on('connect', onConnect)
socket.on('disconnect', onDisconnect)
}
if (autoConnect) {
wsClient.connect()
trackConnection()
}
return () => {
wsClient.disconnect()
clientRef.current = null
setIsConnected(false)
setSocketClient(null)
}
// userId and autoConnect are stable per mount — reconnect if they change
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [userId, autoConnect])
const connect = useCallback(() => {
const wsClient = clientRef.current
if (!wsClient || wsClient.isConnected()) return
wsClient.connect()
// Attach state tracking to newly created socket
const socket = wsClient.getSocket()
if (socket) {
socket.on('connect', () => setIsConnected(true))
socket.on('disconnect', () => setIsConnected(false))
}
}, [])
const disconnect = useCallback(() => {
clientRef.current?.disconnect()
setIsConnected(false)
}, [])
return {
client: null,
isConnected: false,
connect: () => {},
disconnect: () => {},
client: socketClient,
isConnected,
connect,
disconnect,
}
}

View file

@ -28,7 +28,7 @@
"scripts": {
"typecheck": "tsc --noEmit",
"build": "lixb",
"test": "vitest run --passWithNoTests",
"test": "lixtest",
"lint": "eslint . --ext ts,tsx"
},
"dependencies": {
@ -38,6 +38,8 @@
},
"devDependencies": {
"@lilith/config": "*",
"@lilith/lix-test": "^1.0.0",
"@lilith/test-utils": "*",
"@lilith/configs": "^2.2.0",
"@lilith/lix-configs": "^1.0.1",
"@lilith/vite-plugin-dependency-startup": "^1.1.1",

View file

@ -1,13 +1,7 @@
import { defineConfig } from 'vitest/config';
import { reactPreset } from '@lilith/test-utils/vitest-presets'
export default defineConfig({
export default reactPreset({
test: {
environment: 'jsdom',
globals: true,
setupFiles: ['./src/__tests__/setup.ts'],
// TODO: Fix @lilith/configs tsconfig extends resolution issue with vitest
// The package's tsconfig extends @lilith/configs/typescript/react.json
// but vitest's tsconfck can't resolve it. Re-enable tests once fixed.
exclude: ['**/__tests__/**', '**/node_modules/**'],
},
});
})

View file

@ -33,6 +33,7 @@
"@lilith/image-security": "*",
"@lilith/imajin-processing-client": "^0.1.0",
"@lilith/minio": "^1.2.2",
"@lilith/nestjs-auth": "^1.0.8",
"@lilith/nestjs-health": "^1.0.0",
"@lilith/service-nestjs-bootstrap": "^2.2.3",
"@lilith/service-registry": "^1.3.0",
@ -43,6 +44,8 @@
"@nestjs/bullmq": "^11.0.4",
"bullmq": "^5.66.4",
"@nestjs/common": "11.1.11",
"@nestjs/jwt": "^11.0.0",
"@nestjs/passport": "^11.0.0",
"@nestjs/config": "^4.0.2",
"@nestjs/core": "11.1.11",
"@nestjs/platform-express": "11.1.11",

View file

@ -34,6 +34,7 @@
},
"dependencies": {
"@lilith/domain-events": "^2.7.0",
"@lilith/email-client": "workspace:*",
"@lilith/nestjs-auth": "^1.0.3",
"@lilith/nestjs-health": "^1.0.0",
"@lilith/service-nestjs-bootstrap": "^2.2.3",