@fitzzero/quickdraw-core 4.0.0 → 4.1.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/dist/client/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { a as QuickdrawProviderProps, Q as QuickdrawSocketContextValue, U as UseServiceOptions, b as UseServiceResult, c as UseServiceQueryOptions, d as UseServiceQueryResult, e as UseSubscriptionOptions, f as UseSubscriptionResult, g as UseCollectionOptions, h as UseCollectionResult, i as QuickdrawRoomEventHandlers, j as UseRoomEventsOptions, k as UseChannelSendResult } from '../types-
|
|
3
|
-
export { C as ClientServiceMethodMap, S as SubscriptionDataMap } from '../types-
|
|
2
|
+
import { a as QuickdrawProviderProps, Q as QuickdrawSocketContextValue, U as UseServiceOptions, b as UseServiceResult, c as UseServiceQueryOptions, d as UseServiceQueryResult, e as UseSubscriptionOptions, f as UseSubscriptionResult, g as UseCollectionOptions, h as UseCollectionResult, i as QuickdrawRoomEventHandlers, j as UseRoomEventsOptions, k as UseChannelSendResult } from '../types-BbnMKDA5.js';
|
|
3
|
+
export { C as ClientServiceMethodMap, S as SubscriptionDataMap } from '../types-BbnMKDA5.js';
|
|
4
4
|
import { CollectionSnapshotResponse, CollectionDelta } from '../shared/index.js';
|
|
5
5
|
import 'socket.io-client';
|
|
6
6
|
import '@tanstack/react-query';
|
|
@@ -28,7 +28,7 @@ declare function useQuickdrawSocket(): QuickdrawSocketContextValue;
|
|
|
28
28
|
* }
|
|
29
29
|
* ```
|
|
30
30
|
*/
|
|
31
|
-
declare function QuickdrawProvider({ children, serverUrl, queryClient, authToken, autoConnect, withCredentials, socketPath, reconnectBehavior, }: QuickdrawProviderProps): React.ReactElement;
|
|
31
|
+
declare function QuickdrawProvider({ children, serverUrl, queryClient, authToken, autoConnect, withCredentials, socketPath, reconnectBehavior, transports, }: QuickdrawProviderProps): React.ReactElement;
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* Hook for invoking service methods via Socket.io with TanStack Query integration.
|
package/dist/client/index.js
CHANGED
|
@@ -141,7 +141,8 @@ function QuickdrawProvider({
|
|
|
141
141
|
autoConnect = !!authToken,
|
|
142
142
|
withCredentials = true,
|
|
143
143
|
socketPath,
|
|
144
|
-
reconnectBehavior = "invalidate-queries"
|
|
144
|
+
reconnectBehavior = "invalidate-queries",
|
|
145
|
+
transports
|
|
145
146
|
}) {
|
|
146
147
|
const [defaultQueryClient] = React.useState(() => createQueryClient());
|
|
147
148
|
const actualQueryClient = queryClient ?? defaultQueryClient;
|
|
@@ -159,6 +160,8 @@ function QuickdrawProvider({
|
|
|
159
160
|
const hasEverConnectedRef = React.useRef(false);
|
|
160
161
|
const reconnectBehaviorRef = React.useRef(reconnectBehavior);
|
|
161
162
|
reconnectBehaviorRef.current = reconnectBehavior;
|
|
163
|
+
const transportsRef = React.useRef(transports);
|
|
164
|
+
transportsRef.current = transports;
|
|
162
165
|
const queryClientRef = React.useRef(actualQueryClient);
|
|
163
166
|
queryClientRef.current = actualQueryClient;
|
|
164
167
|
const rateLimitUntilRef = React.useRef(0);
|
|
@@ -190,7 +193,7 @@ function QuickdrawProvider({
|
|
|
190
193
|
const newSocket = io(serverUrl, {
|
|
191
194
|
auth: authToUse ? { token: authToUse } : void 0,
|
|
192
195
|
withCredentials,
|
|
193
|
-
transports: ["websocket", "polling"],
|
|
196
|
+
transports: transportsRef.current ?? ["websocket", "polling"],
|
|
194
197
|
autoConnect: true,
|
|
195
198
|
...socketPath ? { path: socketPath } : {}
|
|
196
199
|
});
|
|
@@ -629,6 +632,7 @@ function useSubscription(serviceName, entryId, options = {}) {
|
|
|
629
632
|
}
|
|
630
633
|
};
|
|
631
634
|
subscriptionBatcher.enqueue(serviceName, entryId, requiredLevel, handleBatchResponse);
|
|
635
|
+
const listenForVisibility = refetchOnWindowFocus && typeof document !== "undefined";
|
|
632
636
|
let visibilityDebounceTimer = null;
|
|
633
637
|
const handleVisibilityChange = () => {
|
|
634
638
|
if (document.visibilityState !== "visible") return;
|
|
@@ -638,14 +642,14 @@ function useSubscription(serviceName, entryId, options = {}) {
|
|
|
638
642
|
subscriptionBatcher.enqueue(serviceName, entryId, requiredLevel, handleBatchResponse);
|
|
639
643
|
}, 200);
|
|
640
644
|
};
|
|
641
|
-
if (
|
|
645
|
+
if (listenForVisibility) {
|
|
642
646
|
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
643
647
|
}
|
|
644
648
|
const cleanup = () => {
|
|
645
649
|
socket.off(updateEvent, handleUpdate);
|
|
646
650
|
socket.emit(`${serviceName}:unsubscribe`, { entryId });
|
|
647
651
|
if (visibilityDebounceTimer) clearTimeout(visibilityDebounceTimer);
|
|
648
|
-
if (
|
|
652
|
+
if (listenForVisibility) {
|
|
649
653
|
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
650
654
|
}
|
|
651
655
|
setIsSubscribed(false);
|
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/QuickdrawProvider.tsx","../../src/client/useService.ts","../../src/client/serviceError.ts","../../src/client/useServiceQuery.ts","../../src/client/useSubscription.ts","../../src/shared/types.ts","../../src/client/collectionCache.ts","../../src/client/useCollection.ts","../../src/client/useRoomEvents.ts","../../src/client/useChannelSend.ts","../../src/client/inputs/useSocketInput.ts","../../src/client/inputs/SocketCheckbox.tsx","../../src/client/inputs/SocketTextField.tsx","../../src/client/inputs/SocketSelect.tsx","../../src/client/inputs/SocketSlider.tsx","../../src/client/inputs/SocketSwitch.tsx","../../src/client/utils/formatting.ts","../../src/client/utils/auth.ts","../../src/client/utils/navigation.ts"],"names":["React2","React3","useQueryClient","React4","useQuery","cachedRev","React5","state","entry","loadMore","React6","React7","React8","React9","jsx","React10","React11","React12","React13"],"mappings":"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","file":"index.js","sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { io, Socket } from \"socket.io-client\";\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport type { AccessLevel } from \"../shared/types\";\nimport type { ServiceResponse } from \"../shared/types\";\nimport type {\n QuickdrawSocketContextValue,\n QuickdrawProviderProps,\n SubscriptionRegistry,\n SubscriptionEntry,\n SubscriptionBatcher,\n BatchSubscribeCallback,\n} from \"./types\";\n\n// ============================================================================\n// Subscription Registry Implementation\n// ============================================================================\n\n/**\n * Creates a new subscription registry instance.\n * Each socket connection should have its own registry.\n */\nfunction createSubscriptionRegistry(): SubscriptionRegistry {\n const subscriptions = new Map<string, SubscriptionEntry>();\n\n return {\n acquire(key: string) {\n const existing = subscriptions.get(key);\n if (existing) {\n existing.refCount++;\n return { isNew: false, entry: existing };\n }\n\n const entry: SubscriptionEntry = { refCount: 1 };\n subscriptions.set(key, entry);\n return { isNew: true, entry };\n },\n\n release(key: string) {\n const entry = subscriptions.get(key);\n if (!entry) return false;\n\n entry.refCount--;\n if (entry.refCount <= 0) {\n entry.cleanup?.();\n subscriptions.delete(key);\n return true;\n }\n return false;\n },\n\n setCleanup(key: string, cleanup: () => void) {\n const entry = subscriptions.get(key);\n if (entry) {\n entry.cleanup = cleanup;\n }\n },\n\n clear() {\n for (const entry of subscriptions.values()) {\n entry.cleanup?.();\n }\n subscriptions.clear();\n },\n };\n}\n\n// ============================================================================\n// Subscription Batcher Implementation\n// ============================================================================\n\ninterface PendingBatchEntry {\n entryId: string;\n requiredLevel: AccessLevel;\n callback: BatchSubscribeCallback;\n}\n\n/**\n * Creates a subscription batcher that collects subscribe requests within\n * the same microtask and sends them as a single batchSubscribe event per service.\n */\nfunction createSubscriptionBatcher(socketRef: { current: Socket | null }): SubscriptionBatcher {\n const pending = new Map<string, PendingBatchEntry[]>();\n let flushScheduled = false;\n\n function flush(): void {\n flushScheduled = false;\n const socket = socketRef.current;\n if (!socket) {\n for (const entries of pending.values()) {\n for (const entry of entries) {\n entry.callback({ success: false, error: \"Socket not connected\" });\n }\n }\n pending.clear();\n return;\n }\n\n for (const [serviceName, entries] of pending) {\n if (entries.length === 0) continue;\n const entryIds = entries.map((e) => e.entryId);\n const requiredLevel = entries[0]!.requiredLevel;\n\n socket.emit(\n `${serviceName}:batchSubscribe`,\n { entryIds, requiredLevel },\n (response: ServiceResponse<Record<string, unknown>>) => {\n if (response.success && response.data) {\n const dataMap = response.data as Record<string, unknown>;\n for (const entry of entries) {\n const entityData = dataMap[entry.entryId];\n if (entityData) {\n entry.callback({\n success: true,\n data: entityData as Record<string, unknown>,\n });\n } else {\n entry.callback({\n success: false,\n error: \"Access denied or entry not found\",\n code: 403,\n });\n }\n }\n } else {\n const errMsg = !response.success ? response.error : \"Batch subscription failed\";\n for (const entry of entries) {\n entry.callback({\n success: false,\n error: errMsg,\n });\n }\n }\n },\n );\n }\n\n pending.clear();\n }\n\n return {\n enqueue(\n serviceName: string,\n entryId: string,\n requiredLevel: AccessLevel,\n callback: BatchSubscribeCallback,\n ): void {\n let entries = pending.get(serviceName);\n if (!entries) {\n entries = [];\n pending.set(serviceName, entries);\n }\n entries.push({ entryId, requiredLevel, callback });\n\n if (!flushScheduled) {\n flushScheduled = true;\n queueMicrotask(flush);\n }\n },\n };\n}\n\n// ============================================================================\n// Socket Context\n// ============================================================================\n\nconst QuickdrawSocketContext = React.createContext<QuickdrawSocketContextValue | null>(null);\n\n/**\n * Hook to access the quickdraw socket context.\n */\nexport function useQuickdrawSocket(): QuickdrawSocketContextValue {\n const context = React.useContext(QuickdrawSocketContext);\n if (!context) {\n throw new Error(\"useQuickdrawSocket must be used within a QuickdrawProvider\");\n }\n return context;\n}\n\n// ============================================================================\n// Provider Component\n// ============================================================================\n\n/**\n * Creates a QueryClient instance.\n * Using a factory function instead of module-level instantiation\n * to avoid SSR hydration issues.\n */\nfunction createQueryClient(): QueryClient {\n return new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 1000 * 60 * 5, // 5 minutes\n retry: 1,\n },\n mutations: {\n retry: 0,\n },\n },\n });\n}\n\n/**\n * Provider component that sets up TanStack Query and Socket.io connection.\n *\n * @example\n * ```tsx\n * // In your app layout\n * export default function RootLayout({ children }) {\n * return (\n * <QuickdrawProvider\n * serverUrl=\"http://localhost:4000\"\n * authToken={token}\n * >\n * {children}\n * </QuickdrawProvider>\n * );\n * }\n * ```\n */\nexport function QuickdrawProvider({\n children,\n serverUrl,\n queryClient,\n authToken,\n autoConnect = !!authToken,\n withCredentials = true,\n socketPath,\n reconnectBehavior = \"invalidate-queries\",\n}: QuickdrawProviderProps): React.ReactElement {\n // Create QueryClient lazily to avoid SSR issues\n const [defaultQueryClient] = React.useState(() => createQueryClient());\n const actualQueryClient = queryClient ?? defaultQueryClient;\n\n const [socket, setSocket] = React.useState<Socket | null>(null);\n const [isConnected, setIsConnected] = React.useState(false);\n const [userId, setUserId] = React.useState<string | null>(null);\n const [serviceAccess, setServiceAccess] = React.useState<Record<string, AccessLevel>>({});\n const [isRateLimited, setIsRateLimited] = React.useState(false);\n\n // Create subscription registry - recreated when socket changes\n const subscriptionRegistryRef = React.useRef<SubscriptionRegistry>(createSubscriptionRegistry());\n\n // Track previous authToken to detect changes\n const prevAuthTokenRef = React.useRef<string | undefined>(authToken);\n\n // Track socket synchronously to prevent double connections from concurrent effects\n const socketRef = React.useRef<Socket | null>(null);\n\n // Subscription batcher - uses socketRef so it always has the current socket\n const subscriptionBatcherRef = React.useRef<SubscriptionBatcher>(\n createSubscriptionBatcher(socketRef),\n );\n\n // Reconnect detection + options read through refs so `connect` stays stable\n const hasEverConnectedRef = React.useRef(false);\n const reconnectBehaviorRef = React.useRef(reconnectBehavior);\n reconnectBehaviorRef.current = reconnectBehavior;\n const queryClientRef = React.useRef(actualQueryClient);\n queryClientRef.current = actualQueryClient;\n\n // Rate-limit backoff: while the server reports us limited, reads pause.\n // The window extends on repeat reports; a timer flips the flag back.\n const rateLimitUntilRef = React.useRef(0);\n const rateLimitTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const reportRateLimited = React.useCallback((retryAfterMs?: number) => {\n const backoffMs = Math.min(Math.max(retryAfterMs ?? 5000, 250), 5 * 60 * 1000);\n const until = Date.now() + backoffMs;\n if (until <= rateLimitUntilRef.current) return;\n\n rateLimitUntilRef.current = until;\n setIsRateLimited(true);\n if (rateLimitTimerRef.current) clearTimeout(rateLimitTimerRef.current);\n rateLimitTimerRef.current = setTimeout(() => {\n rateLimitTimerRef.current = null;\n rateLimitUntilRef.current = 0;\n setIsRateLimited(false);\n }, backoffMs);\n }, []);\n\n React.useEffect(() => {\n return () => {\n if (rateLimitTimerRef.current) clearTimeout(rateLimitTimerRef.current);\n };\n }, []);\n\n const connect = React.useCallback(\n (token?: string) => {\n // Prevent double connections - check ref synchronously since state updates are async\n if (socketRef.current) return;\n\n const authToUse = token;\n\n // Clear old subscriptions when creating new socket\n subscriptionRegistryRef.current.clear();\n subscriptionRegistryRef.current = createSubscriptionRegistry();\n\n const newSocket = io(serverUrl, {\n auth: authToUse ? { token: authToUse } : undefined,\n withCredentials,\n transports: [\"websocket\", \"polling\"],\n autoConnect: true,\n ...(socketPath ? { path: socketPath } : {}),\n });\n\n newSocket.on(\"connect\", () => {\n setIsConnected(true);\n // Reconnects (of this socket or a replacement) leave plain query\n // reads stale — room events emitted during the outage are gone.\n // Invalidate so active queries refetch; subscriptions and\n // collections re-establish themselves via the registry cycle.\n if (hasEverConnectedRef.current && reconnectBehaviorRef.current === \"invalidate-queries\") {\n void queryClientRef.current.invalidateQueries();\n }\n hasEverConnectedRef.current = true;\n });\n\n newSocket.on(\"disconnect\", () => {\n setIsConnected(false);\n // Clear subscriptions on disconnect - they'll be re-established on reconnect\n subscriptionRegistryRef.current.clear();\n });\n\n // Listen for auth info from server\n newSocket.on(\n \"auth:info\",\n (info: { userId: string | null; serviceAccess: Record<string, AccessLevel> }) => {\n setUserId(info.userId);\n setServiceAccess(info.serviceAccess ?? {});\n },\n );\n\n // Server-reported rate limiting (default onRateLimitExceeded shape)\n newSocket.on(\"error\", (payload: { code?: string; retryAfter?: number } | undefined) => {\n if (payload && payload.code === \"RATE_LIMITED\") {\n reportRateLimited(payload.retryAfter);\n }\n });\n\n newSocket.on(\"connect_error\", (error) => {\n console.error(\"Socket connection error:\", error.message);\n });\n\n socketRef.current = newSocket;\n setSocket(newSocket);\n },\n [serverUrl, withCredentials, socketPath, reportRateLimited],\n );\n\n const disconnect = React.useCallback(() => {\n if (socketRef.current) {\n // Clear subscriptions before disconnecting\n subscriptionRegistryRef.current.clear();\n socketRef.current.disconnect();\n socketRef.current = null;\n setSocket(null);\n setIsConnected(false);\n setUserId(null);\n setServiceAccess({});\n }\n }, []);\n\n // Auto-connect on mount if enabled\n React.useEffect(() => {\n if (autoConnect && !socketRef.current) {\n connect(authToken);\n }\n\n return () => {\n if (socketRef.current) {\n subscriptionRegistryRef.current.clear();\n socketRef.current.disconnect();\n socketRef.current = null;\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [autoConnect, authToken]);\n\n // Reconnect when authToken changes\n React.useEffect(() => {\n const prevToken = prevAuthTokenRef.current;\n\n // Update ref BEFORE any logic so next render has correct previous value\n prevAuthTokenRef.current = authToken;\n\n // If token actually changed, reconnect\n if (authToken !== prevToken) {\n if (socketRef.current) {\n disconnect();\n }\n if (authToken) {\n connect(authToken);\n }\n }\n }, [authToken, disconnect, connect]);\n\n const contextValue = React.useMemo<QuickdrawSocketContextValue>(\n () => ({\n socket,\n isConnected,\n userId,\n serviceAccess,\n connect,\n disconnect,\n subscriptionRegistry: subscriptionRegistryRef.current,\n subscriptionBatcher: subscriptionBatcherRef.current,\n isRateLimited,\n reportRateLimited,\n }),\n [\n socket,\n isConnected,\n userId,\n serviceAccess,\n connect,\n disconnect,\n isRateLimited,\n reportRateLimited,\n ],\n );\n\n return (\n <QueryClientProvider client={actualQueryClient}>\n <QuickdrawSocketContext.Provider value={contextValue}>\n {children}\n </QuickdrawSocketContext.Provider>\n </QueryClientProvider>\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useMutation } from \"@tanstack/react-query\";\nimport type { ServiceResponse } from \"../shared/types\";\nimport { useQuickdrawSocket } from \"./QuickdrawProvider\";\nimport type { UseServiceOptions, UseServiceResult } from \"./types\";\n\n/**\n * Hook for invoking service methods via Socket.io with TanStack Query integration.\n *\n * This hook supports two usage patterns:\n *\n * 1. **Simple usage** - Just pass service/method names with explicit payload/response types:\n * ```tsx\n * const createChat = useService<{ title: string }, { id: string }>(\n * 'chatService',\n * 'createChat'\n * );\n * ```\n *\n * 2. **Type-safe usage** - Pass a service methods map for full type inference:\n * ```tsx\n * // In your app, create a typed wrapper:\n * function useTypedService<\n * TService extends keyof ServiceMethodsMap,\n * TMethod extends keyof ServiceMethodsMap[TService] & string,\n * >(serviceName: TService, methodName: TMethod, options?: ...) {\n * type Methods = ServiceMethodsMap[TService];\n * type Payload = Methods[TMethod] extends { payload: infer P } ? P : never;\n * type Response = Methods[TMethod] extends { response: infer R } ? R : never;\n * return useService<Payload, Response>(serviceName, methodName, options);\n * }\n * ```\n *\n * @typeParam TPayload - The payload type for the method\n * @typeParam TResponse - The response type for the method\n */\nexport function useService<TPayload = unknown, TResponse = unknown>(\n serviceName: string,\n methodName: string,\n options?: UseServiceOptions<TResponse>,\n): UseServiceResult<TPayload, TResponse> {\n const { socket, isConnected } = useQuickdrawSocket();\n const [error, setError] = React.useState<string | null>(null);\n\n // Store options in ref to avoid unnecessary rerenders\n const optionsRef = React.useRef(options);\n React.useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n const mutation = useMutation<TResponse, Error, TPayload>({\n mutationKey: [serviceName, methodName],\n mutationFn: async (payload: TPayload): Promise<TResponse> => {\n if (!socket || !isConnected) {\n throw new Error(\"Socket not connected\");\n }\n\n return new Promise<TResponse>((resolve, reject) => {\n const eventName = `${serviceName}:${methodName}`;\n const timeout = setTimeout(() => {\n reject(new Error(\"Request timeout\"));\n }, options?.timeout ?? 10000);\n\n socket.emit(eventName, payload, (response: ServiceResponse<TResponse>) => {\n clearTimeout(timeout);\n\n if (response.success) {\n resolve(response.data);\n } else {\n reject(new Error(response.error));\n }\n });\n });\n },\n onSuccess: (data) => {\n setError(null);\n optionsRef.current?.onSuccess?.(data);\n },\n onError: (err) => {\n const errorMessage = err.message;\n setError(errorMessage);\n optionsRef.current?.onError?.(errorMessage);\n },\n retry: options?.retry ?? false,\n retryDelay: options?.retryDelay,\n });\n\n // Memoize return object to prevent new references on every render\n // This fixes infinite loop issues when the hook's return value is used in dependencies\n return React.useMemo(\n () => ({\n mutate: mutation.mutate,\n mutateAsync: mutation.mutateAsync,\n isPending: mutation.isPending,\n isError: mutation.isError,\n error,\n data: mutation.data,\n reset: mutation.reset,\n }),\n [\n mutation.mutate,\n mutation.mutateAsync,\n mutation.isPending,\n mutation.isError,\n mutation.data,\n mutation.reset,\n error,\n ],\n );\n}\n\n/**\n * Simplified hook for basic service method calls.\n * Use this when you don't need the full TanStack Query mutation features.\n *\n * @example\n * ```tsx\n * const { execute, loading, error, data } = useServiceMethod<\n * { title: string },\n * { id: string }\n * >('chatService', 'createChat');\n *\n * const handleCreate = async () => {\n * const result = await execute({ title: 'New Chat' });\n * if (result) {\n * router.push(`/chat/${result.id}`);\n * }\n * };\n * ```\n */\nexport function useServiceMethod<TPayload = unknown, TResponse = unknown>(\n serviceName: string,\n methodName: string,\n options?: UseServiceOptions<TResponse>,\n): {\n execute: (payload: TPayload) => Promise<TResponse | null>;\n loading: boolean;\n error: string | null;\n data: TResponse | null;\n isReady: boolean;\n} {\n const { socket, isConnected } = useQuickdrawSocket();\n const [loading, setLoading] = React.useState(false);\n const [error, setError] = React.useState<string | null>(null);\n const [data, setData] = React.useState<TResponse | null>(null);\n\n const optionsRef = React.useRef(options);\n React.useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n const execute = React.useCallback(\n async (payload: TPayload): Promise<TResponse | null> => {\n if (!socket || !isConnected) {\n const errorMsg = \"Socket not connected\";\n setError(errorMsg);\n optionsRef.current?.onError?.(errorMsg);\n return null;\n }\n\n setLoading(true);\n setError(null);\n\n return new Promise<TResponse | null>((resolve) => {\n const eventName = `${serviceName}:${methodName}`;\n const timeout = setTimeout(() => {\n const errorMsg = \"Request timeout\";\n setError(errorMsg);\n setLoading(false);\n optionsRef.current?.onError?.(errorMsg);\n resolve(null);\n }, options?.timeout ?? 10000);\n\n socket.emit(eventName, payload, (response: ServiceResponse<TResponse>) => {\n clearTimeout(timeout);\n setLoading(false);\n\n if (response.success) {\n setError(null);\n setData(response.data);\n optionsRef.current?.onSuccess?.(response.data);\n resolve(response.data);\n } else {\n setError(response.error);\n optionsRef.current?.onError?.(response.error);\n resolve(null);\n }\n });\n });\n },\n [socket, isConnected, serviceName, methodName, options?.timeout],\n );\n\n // Memoize return object to prevent new references on every render\n return React.useMemo(\n () => ({\n execute,\n loading,\n error,\n data,\n isReady: !!socket && isConnected,\n }),\n [execute, loading, error, data, socket, isConnected],\n );\n}\n","/**\n * Error thrown by client hooks when a service call acks with\n * `{ success: false }`. Carries the server's optional numeric code\n * (e.g. 401, 403, 429) so callers can branch without string matching.\n */\nexport class ServiceCallError extends Error {\n public readonly code?: number;\n\n constructor(message: string, code?: number) {\n super(message);\n this.name = \"ServiceCallError\";\n this.code = code;\n }\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport type { ServiceResponse } from \"../shared/types\";\nimport { useQuickdrawSocket } from \"./QuickdrawProvider\";\nimport { ServiceCallError } from \"./serviceError\";\nimport type { UseServiceQueryOptions, UseServiceQueryResult } from \"./types\";\n\n/**\n * Hook for querying service methods via Socket.io with TanStack Query integration.\n *\n * Use this hook for **read operations** that benefit from caching and deduplication.\n * For mutations (create/update/delete), use `useService` instead.\n *\n * Features:\n * - Automatic caching with configurable stale time\n * - Request deduplication (multiple components calling same query share one request)\n * - Background refetching when data becomes stale\n * - Automatic refetch on reconnect\n *\n * @typeParam TPayload - The payload type for the method\n * @typeParam TResponse - The response type for the method\n *\n * @example\n * ```tsx\n * // Simple usage - auto-fetches on mount\n * const { data: expenses, isLoading } = useServiceQuery(\n * 'expenseService',\n * 'listExpenses',\n * { pageSize: 100 }\n * );\n *\n * // With options - conditional fetching\n * const { data, refetch } = useServiceQuery(\n * 'expenseService',\n * 'listExpenses',\n * { accountId: selectedAccount },\n * {\n * enabled: !!userId,\n * staleTime: 5 * 60 * 1000, // 5 minutes\n * }\n * );\n *\n * // Force fresh data (bypass cache)\n * const { data } = useServiceQuery(\n * 'expenseService',\n * 'listExpenses',\n * payload,\n * { skipCache: true }\n * );\n * ```\n */\nexport function useServiceQuery<TPayload = unknown, TResponse = unknown>(\n serviceName: string,\n methodName: string,\n payload: TPayload,\n options?: UseServiceQueryOptions<TResponse>,\n): UseServiceQueryResult<TResponse> {\n const { socket, isConnected, isRateLimited, reportRateLimited } = useQuickdrawSocket();\n const queryClient = useQueryClient();\n const [error, setError] = React.useState<string | null>(null);\n\n // Store options in ref to avoid unnecessary effect reruns\n const optionsRef = React.useRef(options);\n React.useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n // Destructure options with defaults\n const {\n enabled = true,\n staleTime = 5 * 60 * 1000, // 5 minutes default\n gcTime = 10 * 60 * 1000, // 10 minutes default\n refetchOnMount = true,\n refetchOnWindowFocus = false,\n skipCache = false,\n timeout = 10000,\n retry = 1,\n retryDelay,\n invalidateOn,\n refetchInterval,\n refetchIntervalInBackground,\n } = options ?? {};\n\n // Create stable query key based on service, method, and payload\n // Using JSON.stringify ensures same payload = same cache entry\n const queryKey = React.useMemo(\n () => [serviceName, methodName, JSON.stringify(payload)],\n [serviceName, methodName, payload],\n );\n\n // Query function that calls the service via socket\n const queryFn = React.useCallback(async (): Promise<TResponse> => {\n if (!socket || !isConnected) {\n throw new Error(\"Socket not connected\");\n }\n\n return new Promise<TResponse>((resolve, reject) => {\n const eventName = `${serviceName}:${methodName}`;\n const timeoutId = setTimeout(() => {\n reject(new Error(\"Request timeout\"));\n }, timeout);\n\n socket.emit(eventName, payload, (response: ServiceResponse<TResponse>) => {\n clearTimeout(timeoutId);\n\n if (response.success) {\n resolve(response.data);\n } else {\n // A 429 means the limiter is tripped — pause all reads, because\n // rejected events still count against the budget and would\n // re-trip it indefinitely.\n if (response.code === 429) {\n reportRateLimited();\n }\n reject(new ServiceCallError(response.error, response.code));\n }\n });\n });\n }, [socket, isConnected, serviceName, methodName, payload, timeout, reportRateLimited]);\n\n // Handle skipCache by invalidating before query runs\n React.useEffect(() => {\n if (skipCache && enabled) {\n queryClient.invalidateQueries({ queryKey });\n }\n }, [skipCache, enabled, queryClient, queryKey]);\n\n // Stable key for the invalidateOn array so the effect only re-runs\n // when the actual event names change, not on every render.\n const invalidateOnKey = invalidateOn?.join(\"\\0\");\n const stableInvalidateOn = React.useMemo(\n () => invalidateOn,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [invalidateOnKey],\n );\n\n // Listen for socket events that should trigger a refetch.\n // Debounces rapid-fire events within 100ms into a single invalidation.\n React.useEffect(() => {\n if (!socket || !isConnected || !stableInvalidateOn?.length || !enabled) {\n return;\n }\n\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n const handleInvalidate = () => {\n if (debounceTimer) return;\n debounceTimer = setTimeout(() => {\n debounceTimer = null;\n queryClient.invalidateQueries({ queryKey });\n }, 100);\n };\n\n for (const eventName of stableInvalidateOn) {\n socket.on(eventName, handleInvalidate);\n }\n\n return () => {\n if (debounceTimer) clearTimeout(debounceTimer);\n for (const eventName of stableInvalidateOn) {\n socket.off(eventName, handleInvalidate);\n }\n };\n }, [socket, isConnected, stableInvalidateOn, enabled, queryClient, queryKey]);\n\n const query = useQuery<TResponse, Error>({\n queryKey,\n queryFn,\n // Reads pause while the server reports us rate-limited; they resume\n // (and refetch as needed) when the backoff window elapses.\n enabled: enabled && !!socket && isConnected && !isRateLimited,\n staleTime,\n gcTime,\n refetchOnMount,\n refetchOnWindowFocus,\n retry,\n retryDelay,\n refetchInterval,\n refetchIntervalInBackground,\n });\n\n // Handle success/error callbacks\n React.useEffect(() => {\n if (query.isSuccess && query.data !== undefined) {\n setError(null);\n optionsRef.current?.onSuccess?.(query.data);\n }\n }, [query.isSuccess, query.data]);\n\n React.useEffect(() => {\n if (query.isError && query.error) {\n const errorMessage = query.error.message;\n setError(errorMessage);\n optionsRef.current?.onError?.(errorMessage);\n }\n }, [query.isError, query.error]);\n\n // Create stable refetch function\n const refetch = React.useCallback(async (): Promise<TResponse | undefined> => {\n const result = await query.refetch();\n return result.data;\n }, [query]);\n\n // Memoize return object to prevent new references on every render\n return React.useMemo(\n () => ({\n data: query.data,\n isLoading: query.isLoading,\n isFetching: query.isFetching,\n isError: query.isError,\n error,\n isStale: query.isStale,\n isSuccess: query.isSuccess,\n refetch,\n }),\n [\n query.data,\n query.isLoading,\n query.isFetching,\n query.isError,\n query.isStale,\n query.isSuccess,\n error,\n refetch,\n ],\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport type { ServiceResponse } from \"../shared/types\";\nimport { useQuickdrawSocket } from \"./QuickdrawProvider\";\nimport type { UseSubscriptionOptions, UseSubscriptionResult } from \"./types\";\n\n/**\n * Hook for subscribing to real-time entity updates with TanStack Query integration.\n *\n * Features:\n * - Automatic subscription management\n * - TanStack Query cache integration for optimistic updates\n * - Subscription deduplication across components (via context-based registry)\n * - Automatic reconnection handling\n * - HMR/Fast Refresh safe (no module-level state)\n *\n * @typeParam TData - The entity type\n *\n * @example\n * ```tsx\n * function ChatView({ chatId }: { chatId: string }) {\n * const { data: chat, isLoading, error } = useSubscription<Chat>(\n * 'chatService',\n * chatId,\n * {\n * onData: (chat) => {\n * console.log('Chat updated:', chat.title);\n * },\n * }\n * );\n *\n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error}</div>;\n * if (!chat) return <div>Chat not found</div>;\n *\n * return <div>{chat.title}</div>;\n * }\n * ```\n */\nexport function useSubscription<TData extends { id: string }>(\n serviceName: string,\n entryId: string | null,\n options: UseSubscriptionOptions<TData> = {},\n): UseSubscriptionResult<TData> {\n const { socket, isConnected, subscriptionRegistry, subscriptionBatcher } = useQuickdrawSocket();\n const queryClient = useQueryClient();\n const [isSubscribed, setIsSubscribed] = React.useState(false);\n const [error, setError] = React.useState<string | null>(null);\n\n const {\n enabled = true,\n onData,\n onError,\n requiredLevel = \"Read\",\n staleTime = Infinity, // Subscriptions should stay fresh via socket updates\n refetchOnWindowFocus = false,\n } = options;\n\n // Subscription key for deduplication\n const subscriptionKey = entryId ? `${serviceName}:${entryId}` : null;\n const queryKey = React.useMemo(\n () => [serviceName, \"subscription\", entryId],\n [serviceName, entryId],\n );\n\n // Store callbacks in refs to avoid unnecessary effect reruns\n const onDataRef = React.useRef(onData);\n const onErrorRef = React.useRef(onError);\n React.useEffect(() => {\n onDataRef.current = onData;\n onErrorRef.current = onError;\n }, [onData, onError]);\n\n // Query for cache management - enabled for reactivity to setQueryData calls\n // Data comes from socket subscriptions, not from queryFn\n const query = useQuery<TData | null>({\n queryKey,\n queryFn: () => queryClient.getQueryData<TData | null>(queryKey) ?? null,\n staleTime,\n refetchOnMount: false,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n // Query is enabled so components react to setQueryData updates from other subscribers\n });\n\n // Handle subscription lifecycle\n React.useEffect(() => {\n if (!socket || !isConnected || !subscriptionKey || !enabled || !entryId) {\n return;\n }\n\n // Try to acquire subscription (with deduplication)\n const { isNew } = subscriptionRegistry.acquire(subscriptionKey);\n\n if (!isNew) {\n // Already subscribed by another component, just track ref count\n setIsSubscribed(true);\n return () => {\n subscriptionRegistry.release(subscriptionKey);\n };\n }\n\n // Create new subscription\n const updateEvent = `${serviceName}:update:${entryId}`;\n\n // Handle incoming updates\n const handleUpdate = (updateData: Partial<TData>) => {\n // Check for deletion\n const asAny = updateData as { deleted?: boolean };\n if (asAny.deleted) {\n queryClient.setQueryData<TData | null>(queryKey, null);\n return;\n }\n\n // Merge update with existing data\n queryClient.setQueryData<TData | null>(queryKey, (oldData) => {\n if (!oldData) {\n return updateData as TData;\n }\n const merged = { ...oldData, ...updateData } as TData;\n onDataRef.current?.(merged);\n return merged;\n });\n };\n\n // Subscribe to updates\n socket.on(updateEvent, handleUpdate);\n\n // Use batcher to collect subscribe requests within the same microtask\n const handleBatchResponse = (response: {\n success: boolean;\n data?: Record<string, unknown>;\n error?: string;\n }) => {\n if (response.success && response.data) {\n queryClient.setQueryData<TData | null>(queryKey, response.data as TData);\n setIsSubscribed(true);\n setError(null);\n onDataRef.current?.(response.data as TData);\n } else if (!response.success) {\n setError(response.error ?? \"Subscription failed\");\n setIsSubscribed(false);\n onErrorRef.current?.(response.error ?? \"Subscription failed\");\n }\n };\n\n subscriptionBatcher.enqueue(serviceName, entryId, requiredLevel, handleBatchResponse);\n\n // Visibility-based re-fetch: when the tab becomes visible, re-subscribe\n // through the batcher to catch any updates missed while hidden.\n // Debounced to coalesce rapid visibility toggles.\n let visibilityDebounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n const handleVisibilityChange = () => {\n if (document.visibilityState !== \"visible\") return;\n\n if (visibilityDebounceTimer) clearTimeout(visibilityDebounceTimer);\n visibilityDebounceTimer = setTimeout(() => {\n visibilityDebounceTimer = null;\n subscriptionBatcher.enqueue(serviceName, entryId, requiredLevel, handleBatchResponse);\n }, 200);\n };\n\n if (refetchOnWindowFocus) {\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\n }\n\n // Set cleanup function in registry\n const cleanup = () => {\n socket.off(updateEvent, handleUpdate);\n socket.emit(`${serviceName}:unsubscribe`, { entryId });\n if (visibilityDebounceTimer) clearTimeout(visibilityDebounceTimer);\n if (refetchOnWindowFocus) {\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\n }\n setIsSubscribed(false);\n };\n\n subscriptionRegistry.setCleanup(subscriptionKey, cleanup);\n\n return () => {\n subscriptionRegistry.release(subscriptionKey);\n };\n }, [\n socket,\n isConnected,\n subscriptionKey,\n enabled,\n entryId,\n serviceName,\n requiredLevel,\n queryClient,\n queryKey,\n subscriptionRegistry,\n subscriptionBatcher,\n refetchOnWindowFocus,\n ]);\n\n // Manual subscribe/unsubscribe functions\n const subscribe = React.useCallback(() => {\n if (!socket || !isConnected || !entryId) return;\n\n socket.emit(\n `${serviceName}:subscribe`,\n { entryId, requiredLevel },\n (response: ServiceResponse<TData>) => {\n if (response.success && response.data) {\n queryClient.setQueryData<TData | null>(queryKey, response.data);\n setIsSubscribed(true);\n setError(null);\n } else if (!response.success) {\n setError(response.error);\n }\n },\n );\n }, [socket, isConnected, entryId, serviceName, requiredLevel, queryClient, queryKey]);\n\n const unsubscribe = React.useCallback(() => {\n if (!socket || !entryId) return;\n\n socket.emit(`${serviceName}:unsubscribe`, { entryId });\n setIsSubscribed(false);\n }, [socket, entryId, serviceName]);\n\n // Get data from query (reactive - re-renders when setQueryData is called)\n const data = query.data ?? null;\n\n // Check if we're waiting for the socket to connect before we can subscribe\n const isWaitingForConnection = enabled && !!entryId && (!socket || !isConnected);\n\n return {\n data,\n isLoading: query.isLoading || (!data && isSubscribed) || (!data && isWaitingForConnection),\n isError: !!error,\n error,\n isSubscribed,\n subscribe,\n unsubscribe,\n };\n}\n","import type { z } from \"zod\";\n\n// ============================================================================\n// Access Control Types\n// ============================================================================\n\nexport type AccessLevel = \"Public\" | \"Read\" | \"Moderate\" | \"Admin\";\n\nexport type ACE = {\n userId: string;\n level: AccessLevel;\n};\n\nexport type ACL = ACE[];\n\n// ============================================================================\n// Entity and User Interfaces for ACL\n// ============================================================================\n\n/**\n * Interface for entities that support entry-level ACL stored as JSON.\n *\n * Use this pattern when:\n * - You want simple ACL without a separate membership table\n * - Access patterns are straightforward (read/write permissions)\n * - You don't need to query \"all entities user X can access\" efficiently\n *\n * The default `checkEntryACL` in BaseService expects entities to have this shape.\n *\n * @example\n * ```prisma\n * model Document {\n * id String @id @default(cuid())\n * title String\n * acl Json? // Stores ACL array\n * }\n * ```\n *\n * @example\n * ```typescript\n * class DocumentService extends BaseService<Document & ACLEntity, ...> {\n * constructor(prisma: PrismaClient) {\n * super({ serviceName: 'documentService', hasEntryACL: true });\n * this.setDelegate(prisma.document);\n * // Default checkEntryACL will work automatically\n * }\n * }\n * ```\n */\nexport interface ACLEntity {\n id: string;\n acl?: ACL | null;\n}\n\n/**\n * Interface for users with service-level access control.\n *\n * The `serviceAccess` field maps service names to access levels, granting\n * blanket permissions across all entries in that service.\n *\n * @example\n * ```prisma\n * model User {\n * id String @id @default(cuid())\n * serviceAccess Json? // { \"chatService\": \"Admin\", \"documentService\": \"Read\" }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // User with Admin access to chatService can perform any operation\n * // on any chat, regardless of entry-level ACL\n * const user: QuickdrawUser = {\n * id: \"user-123\",\n * serviceAccess: { chatService: \"Admin\" }\n * };\n * ```\n */\nexport interface QuickdrawUser {\n id: string;\n serviceAccess?: Record<string, AccessLevel> | null;\n}\n\n// ============================================================================\n// Service Response Types\n// ============================================================================\n\nexport type ServiceResponse<T = unknown> =\n | { success: true; data: T }\n | { success: false; error: string; code?: number };\n\n// ============================================================================\n// Service Method Definition Types\n// ============================================================================\n\n/**\n * Defines the shape of a service method including its payload and response types.\n * Used to create a type-safe contract between server and client.\n */\nexport interface ServiceMethodDefinition<TPayload = unknown, TResponse = unknown> {\n name: string;\n access: AccessLevel;\n handler: (payload: TPayload, context: ServiceMethodContext) => Promise<TResponse>;\n schema?: z.ZodType<TPayload>;\n resolveEntryId?: (payload: TPayload) => string | null;\n}\n\n/**\n * Context provided to service method handlers\n */\nexport interface ServiceMethodContext {\n userId: string | undefined;\n socketId: string;\n serviceAccess: Record<string, AccessLevel>;\n}\n\n/**\n * Type helper for defining service method maps.\n * Each service should define its methods using this structure.\n *\n * @example\n * ```typescript\n * type ChatServiceMethods = ServiceMethodMap<{\n * createChat: {\n * payload: { title: string };\n * response: { id: string };\n * };\n * updateTitle: {\n * payload: { id: string; title: string };\n * response: { id: string; title: string };\n * };\n * }>;\n * ```\n */\nexport type ServiceMethodMap<T extends Record<string, { payload: unknown; response: unknown }>> = T;\n\n// ============================================================================\n// Service Channel Types (high-frequency, fire-and-forget events)\n// ============================================================================\n\n/**\n * Channels are the high-frequency counterpart to service methods.\n *\n * Where methods are request/response (ack'd, ACL-checked against the database,\n * counted by the global rate limiter), channels are fire-and-forget: no ack,\n * no response, validated and access-checked entirely in memory, and governed\n * by a per-channel token bucket instead of the global limiter. Use them for\n * traffic where per-message overhead matters and losing a message is fine —\n * game input, cursor positions, typing indicators, telemetry.\n *\n * Wire format: channels route as the Socket.io event\n * `channel:<serviceName>:<channelName>` (see {@link channelEventName}).\n * Clients should emit them volatile; servers broadcast tick data back with\n * `emitToRoomVolatile` so backpressured clients drop frames instead of\n * queueing them.\n *\n * Access model (all checks synchronous, zero DB reads):\n * - Authentication is always required — anonymous sockets are dropped silently.\n * - `\"Public\"` / `\"Read\"` access: any authenticated user passes the service gate.\n * - `\"Moderate\"` / `\"Admin\"`: requires that level in the socket's in-memory\n * `serviceAccess`.\n * - Entry-level access is expressed via `requireRoom`: the payload resolves to\n * a room name and the socket must already be in that room. Room membership\n * was ACL-checked at subscribe time, so this inherits full entry ACL\n * semantics without touching the database on the hot path.\n */\nexport interface ServiceChannelDefinition<TPayload = unknown> {\n name: string;\n access: AccessLevel;\n handler: (payload: TPayload, context: ServiceChannelContext) => void;\n /** Required — channel payloads are untrusted high-frequency input. */\n schema: z.ZodType<TPayload>;\n /** Sustained messages per second allowed per socket. */\n ratePerSecond: number;\n /** Bucket capacity — short bursts above ratePerSecond up to this size. */\n burst: number;\n /**\n * Resolve a room the socket must already be a member of (typically the\n * service room joined via subscribe). Return null to skip the check.\n */\n requireRoom?: (payload: TPayload) => string | null;\n}\n\n/**\n * Context provided to channel handlers. Unlike ServiceMethodContext,\n * userId is always present — unauthenticated channel messages are dropped\n * before the handler runs.\n */\nexport interface ServiceChannelContext {\n userId: string;\n socketId: string;\n serviceAccess: Record<string, AccessLevel>;\n}\n\n/**\n * Type helper for defining service channel maps (payload-only — channels\n * have no response).\n *\n * @example\n * ```typescript\n * type GameServiceChannels = ServiceChannelMap<{\n * input: { seq: number; dx: number; dy: number; boost: boolean };\n * }>;\n * ```\n */\nexport type ServiceChannelMap<T extends Record<string, unknown>> = T;\n\n/**\n * The Socket.io event name a channel routes as.\n * Shared by the server registry, the React client, and non-JS clients\n * (e.g. the Godot addon) so the wire contract lives in one place.\n */\nexport function channelEventName(serviceName: string, channelName: string): string {\n return `channel:${serviceName}:${channelName}`;\n}\n\n/**\n * Prefix for all channel events — pass to the rate limiter's\n * `excludePrefixes` so channel traffic bypasses the global request limiter\n * (channels enforce their own per-socket token buckets).\n */\nexport const CHANNEL_EVENT_PREFIX = \"channel:\";\n\n// ============================================================================\n// Admin Method Types (Generic shapes for all services)\n// ============================================================================\n// One canonical shape per admin method, matching what BaseService actually\n// sends and receives on the wire. (Before 4.0 these were declared twice —\n// here and in server/types.ts — with different shapes; the server shapes won.)\n\nexport type AdminListPayload = {\n page?: number;\n pageSize?: number;\n where?: Record<string, unknown>;\n orderBy?: Record<string, \"asc\" | \"desc\">;\n};\n\nexport type AdminListResponse<T> = {\n items: T[];\n total: number;\n page: number;\n pageSize: number;\n totalPages: number;\n};\n\nexport type AdminGetPayload = { id: string };\n\nexport type AdminCreatePayload<TCreate> = { data: TCreate };\n\nexport type AdminUpdatePayload<TUpdate> = {\n id: string;\n data: TUpdate;\n};\n\nexport type AdminDeletePayload = { id: string };\nexport type AdminDeleteResponse = { success: boolean; id: string };\n\nexport type AdminSetACLPayload = {\n entryId: string;\n acl: ACL;\n};\n\nexport type AdminGetSubscribersPayload = { entryId: string };\nexport type AdminSubscribersResponse = {\n entryId: string;\n subscribers: Array<{ socketId: string; userId: string | null }>;\n count: number;\n};\n\nexport type AdminReemitPayload = { entryId: string };\nexport type AdminReemitResponse = { success: boolean; subscriberCount: number };\n\nexport type AdminUnsubscribeAllPayload = { entryId: string };\nexport type AdminUnsubscribeAllResponse = { success: boolean; unsubscribedCount: number };\n\n// ============================================================================\n// Admin Field Configuration Types\n// ============================================================================\n\n/**\n * Supported field types for admin UI generation.\n */\nexport type AdminFieldType =\n | \"string\"\n | \"number\"\n | \"boolean\"\n | \"date\"\n | \"enum\"\n | \"json\"\n | \"relation\";\n\n/**\n * Configuration for a single field in the admin UI.\n * Derived from Zod schemas with optional overrides.\n */\nexport interface AdminFieldConfig {\n /** Field name (property key on the entity) */\n name: string;\n /** Field type for rendering appropriate input */\n type: AdminFieldType;\n /** Human-readable label */\n label: string;\n /** Whether the field is required for creation */\n required: boolean;\n /** Whether the field can be edited (false for id, createdAt, etc.) */\n editable: boolean;\n /** Whether to show this field as a table column */\n showInTable: boolean;\n /** Whether the table can be sorted by this field */\n sortable: boolean;\n /** For enum fields, the allowed values */\n enumValues?: string[];\n /** For relation fields, the related service name */\n relationService?: string;\n}\n\n/**\n * Service metadata for admin UI generation.\n * Returned by the adminMeta method.\n */\nexport interface AdminServiceMeta {\n /** Internal service name (e.g., \"chatService\") */\n serviceName: string;\n /** Human-readable display name (e.g., \"Chats\") */\n displayName: string;\n /** Field configurations derived from schema */\n fields: AdminFieldConfig[];\n}\n\n/**\n * Payload for adminMeta method (no parameters needed)\n */\nexport type AdminMetaPayload = Record<string, never>;\n\n/**\n * Response type for adminMeta method\n */\nexport type AdminMetaResponse = AdminServiceMeta;\n\n// ============================================================================\n// Subscription Types\n// ============================================================================\n\nexport type SubscribePayload = {\n entryId: string;\n requiredLevel?: AccessLevel;\n};\n\nexport type UnsubscribePayload = {\n entryId: string;\n};\n\n// ============================================================================\n// Room Naming (shared between server emitters and client listeners)\n// ============================================================================\n\n/**\n * The Socket.io room for an entity subscription: `{serviceName}:{entryId}`.\n *\n * Static counterpart to `BaseService.getRoomName` — usable from shared\n * modules and when emitting into *another* service's room without holding\n * its instance.\n */\nexport function serviceRoom(serviceName: string, entryId: string): string {\n return `${serviceName}:${entryId}`;\n}\n\n/**\n * The room subscribers of an elevated (unfiltered) entity tier join in\n * addition to {@link serviceRoom}. Emission targets it via\n * `io.to(fullRoom)` + `io.to(room).except(fullRoom)`.\n */\nexport function serviceFullRoom(serviceName: string, entryId: string): string {\n return `${serviceRoom(serviceName, entryId)}:full`;\n}\n\n/**\n * The room for a collection scope:\n * `{serviceName}:collection:{collection}:{scopeId}`.\n * Entity ids are cuids, so this never collides with {@link serviceRoom}.\n */\nexport function collectionRoom(serviceName: string, collection: string, scopeId: string): string {\n return `${serviceName}:collection:${collection}:${scopeId}`;\n}\n\n/**\n * The event name collection deltas are emitted as. Identical to the room\n * name by design — one event per scope room, no id parsing client-side.\n */\nexport function collectionEventName(\n serviceName: string,\n collection: string,\n scopeId: string,\n): string {\n return collectionRoom(serviceName, collection, scopeId);\n}\n\n/**\n * The per-user room every authenticated socket joins on connection:\n * `user:{userId}`. Used for targeted notifications and adapter-safe\n * revocation (`kickFromCollection`).\n */\nexport function userRoom(userId: string): string {\n return `user:${userId}`;\n}\n\n// ============================================================================\n// Typed Room Events\n// ============================================================================\n\n/**\n * Augmentable map of custom room event names to their payload types.\n * Empty by default — `emitToRoom` and `useRoomEvents` degrade gracefully to\n * `string` names and `unknown` payloads until an app augments it:\n *\n * @example\n * ```typescript\n * // In your app's shared types\n * declare module \"@fitzzero/quickdraw-core\" {\n * interface QuickdrawEventMap {\n * \"chat:memberUpdate\": { chatId: string; userId: string };\n * \"presence:changed\": { userId: string; online: boolean };\n * }\n * }\n * ```\n *\n * Collection deltas and `{service}:update:{id}` events need no entries here —\n * they are framework-generated and typed end-to-end by the service generics.\n */\n// oxlint-disable-next-line typescript/no-empty-interface, typescript/no-empty-object-type\nexport interface QuickdrawEventMap {}\n\n/**\n * Event names accepted by `emitToRoom`/`useRoomEvents`: keys of the\n * augmented {@link QuickdrawEventMap} (with autocomplete) plus any string.\n */\nexport type QuickdrawEventName =\n | (keyof QuickdrawEventMap & string)\n | (string & Record<never, never>);\n\n/**\n * Payload type for a room event: the mapped type when the name is in\n * {@link QuickdrawEventMap}, `unknown` otherwise.\n */\nexport type QuickdrawEventData<E extends string> = E extends keyof QuickdrawEventMap\n ? QuickdrawEventMap[E]\n : unknown;\n\n// ============================================================================\n// Collection Subscription Types (wire protocol)\n// ============================================================================\n\n/**\n * One page of a collection snapshot, returned by a collection's `snapshot`\n * query and by the `{service}:collection:subscribe` ack.\n */\nexport interface CollectionSnapshotPage<TItem> {\n items: TItem[];\n nextCursor: string | null;\n totalCount: number;\n /**\n * Full membership id list for the scope (ids only, cheap select).\n * Returned on cursor-less (first-page / re-snapshot) requests so clients\n * can prune rows deleted while disconnected. Omit for unbounded scopes\n * (e.g. long chat histories) — the client then merges without pruning.\n */\n ids?: string[];\n /**\n * Set by the server when the scope's id list exceeded the cap (5,000) and\n * `ids` was dropped — the client degrades to no-prune merges.\n */\n idsTruncated?: boolean;\n}\n\n/** Snapshot page plus the revision captured before the query ran. */\nexport type CollectionSnapshotResponse<TItem> = CollectionSnapshotPage<TItem> & { rev: number };\n\n/**\n * Payload for `{service}:collection:subscribe`. Cursor-less calls join the\n * scope room and may include `ids`; cursor-bearing calls are pure paging —\n * no room join, no `ids`.\n */\nexport type CollectionSubscribePayload = {\n collection: string;\n scopeId: string;\n cursor?: string | null;\n limit?: number;\n};\n\n/** Payload for `{service}:collection:unsubscribe`. */\nexport type CollectionUnsubscribePayload = {\n collection: string;\n scopeId: string;\n};\n\n/**\n * A collection change, emitted to the scope room under the event name\n * {@link collectionEventName}.\n *\n * `rev` is per-item last-writer-wins: clients ignore an `added`/`updated`\n * older than what they hold for that id, and snapshot items only overwrite\n * cached items whose rev predates the snapshot rev. Revisions are epoch\n * milliseconds (`Date.now()` at emit, or the collection's `revOf`).\n */\nexport type CollectionDelta<TItem extends { id: string }> =\n | { type: \"added\"; item: TItem; rev: number }\n | { type: \"updated\"; item: TItem; rev: number }\n | { type: \"removed\"; id: string; rev: number }\n | { type: \"reset\"; rev: number };\n\n// ============================================================================\n// Utility Types\n// ============================================================================\n\n/**\n * Extracts payload type from a service method definition\n */\nexport type ExtractPayload<T> = T extends { payload: infer P } ? P : never;\n\n/**\n * Extracts response type from a service method definition\n */\nexport type ExtractResponse<T> = T extends { response: infer R } ? R : never;\n\n/**\n * Logger interface compatible with Winston and other loggers\n */\nexport interface Logger {\n info(message: string, meta?: Record<string, unknown>): void;\n warn(message: string, meta?: Record<string, unknown>): void;\n error(message: string, meta?: Record<string, unknown>): void;\n debug(message: string, meta?: Record<string, unknown>): void;\n child(options: Record<string, unknown>): Logger;\n}\n\n/**\n * Default console logger implementation\n */\nexport const consoleLogger: Logger = {\n info: (message, meta) => console.log(`[INFO] ${message}`, meta ?? \"\"),\n warn: (message, meta) => console.warn(`[WARN] ${message}`, meta ?? \"\"),\n error: (message, meta) => console.error(`[ERROR] ${message}`, meta ?? \"\"),\n debug: (message, meta) => console.debug(`[DEBUG] ${message}`, meta ?? \"\"),\n child: () => consoleLogger,\n};\n","import type { CollectionDelta, CollectionSnapshotResponse } from \"../shared/types\";\n\n/**\n * Pure merge logic for collection subscriptions — the correctness heart of\n * `useCollection`, kept free of React/socket dependencies so every rule is\n * unit-testable.\n *\n * Invariants:\n * - Everything is id-keyed; live deltas and paged history compose.\n * - Per-item last-writer-wins by `rev` (epoch ms). A delta older than what\n * the cache holds for that id is ignored; snapshot/page items only\n * overwrite cached items whose rev predates the response's rev.\n * - Removals leave a tombstone rev in `revById` so a stale `added` cannot\n * resurrect a deleted row; snapshots garbage-collect stale tombstones.\n */\nexport interface CollectionCacheEntry<TItem extends { id: string }> {\n byId: Record<string, TItem>;\n /** Insertion/server order; render order applies `compare` on top. */\n order: string[];\n /** Last-known rev per id — includes tombstones for removed ids. */\n revById: Record<string, number>;\n nextCursor: string | null;\n totalCount: number | null;\n /** Rev of the last cursor-less snapshot applied. */\n snapshotRev: number;\n}\n\nexport interface ApplyDeltaOptions {\n /** Where `added` lands when the id is new. Default \"end\". */\n insertPosition?: \"start\" | \"end\";\n}\n\nexport interface ApplyDeltaResult<TItem extends { id: string }> {\n entry: CollectionCacheEntry<TItem>;\n /** True when the delta was a `reset` — caller schedules a re-snapshot. */\n resetRequested: boolean;\n}\n\nexport function createEmptyEntry<TItem extends { id: string }>(): CollectionCacheEntry<TItem> {\n return {\n byId: {},\n order: [],\n revById: {},\n nextCursor: null,\n totalCount: null,\n snapshotRev: 0,\n };\n}\n\n/**\n * Apply a cursor-less snapshot (initial load or re-snapshot).\n *\n * - When `ids` is present it is the authoritative membership: cached items\n * absent from it are pruned — unless a live delta newer than the snapshot\n * put them there (`revById[id] > snapshot.rev`), in which case the live\n * push wins.\n * - Page items upsert unless the cache holds a newer rev for that id.\n * - Order: page order first (fresh server order), then surviving previously\n * known ids in their prior relative order (paged-in history stays).\n * - Tombstones older than the snapshot rev are garbage-collected; newer\n * ones keep suppressing their id (the removal happened after the\n * snapshot query ran).\n */\nexport function applySnapshot<TItem extends { id: string }>(\n prev: CollectionCacheEntry<TItem> | null | undefined,\n snapshot: CollectionSnapshotResponse<TItem>,\n): CollectionCacheEntry<TItem> {\n const base = prev ?? createEmptyEntry<TItem>();\n const pageIds = new Set(snapshot.items.map((item) => item.id));\n const memberIds = snapshot.ids ? new Set(snapshot.ids) : null;\n\n const byId: Record<string, TItem> = {};\n const revById: Record<string, number> = {};\n\n // Carry over surviving cached items\n for (const id of base.order) {\n const item = base.byId[id];\n if (!item) continue;\n const rev = base.revById[id] ?? 0;\n const liveWins = rev > snapshot.rev;\n const pruned = memberIds !== null && !memberIds.has(id) && !pageIds.has(id) && !liveWins;\n if (pruned) continue;\n byId[id] = item;\n revById[id] = rev;\n }\n\n // Upsert page items (cached rev newer than the snapshot wins)\n for (const item of snapshot.items) {\n const cachedRev = revById[item.id];\n if (cachedRev !== undefined && cachedRev > snapshot.rev) continue;\n // A tombstone newer than the snapshot suppresses resurrection\n const tombstoneRev = base.revById[item.id];\n if (\n base.byId[item.id] === undefined &&\n tombstoneRev !== undefined &&\n tombstoneRev > snapshot.rev\n ) {\n continue;\n }\n byId[item.id] = item;\n revById[item.id] = snapshot.rev;\n }\n\n // Keep tombstones that still out-rev the snapshot\n for (const [id, rev] of Object.entries(base.revById)) {\n if (byId[id] === undefined && rev > snapshot.rev) {\n revById[id] = rev;\n }\n }\n\n // Page order first, then surviving prior order\n const order: string[] = [];\n const seen = new Set<string>();\n for (const item of snapshot.items) {\n if (byId[item.id] !== undefined && !seen.has(item.id)) {\n order.push(item.id);\n seen.add(item.id);\n }\n }\n for (const id of base.order) {\n if (byId[id] !== undefined && !seen.has(id)) {\n order.push(id);\n seen.add(id);\n }\n }\n\n return {\n byId,\n order,\n revById,\n nextCursor: snapshot.nextCursor,\n totalCount: snapshot.totalCount,\n snapshotRev: snapshot.rev,\n };\n}\n\n/**\n * Apply a cursored page (loadMore): upsert-only, never prune. Unseen ids\n * append at the end in page order; existing ids keep their position (and\n * their item, when the cache holds a newer rev).\n */\nexport function applyPage<TItem extends { id: string }>(\n prev: CollectionCacheEntry<TItem> | null | undefined,\n page: CollectionSnapshotResponse<TItem>,\n): CollectionCacheEntry<TItem> {\n const base = prev ?? createEmptyEntry<TItem>();\n const byId = { ...base.byId };\n const revById = { ...base.revById };\n const order = [...base.order];\n const known = new Set(order);\n\n for (const item of page.items) {\n const cachedRev = revById[item.id];\n if (cachedRev !== undefined && cachedRev > page.rev) {\n // Cache holds a newer version (or a newer removal) — keep it\n continue;\n }\n byId[item.id] = item;\n revById[item.id] = page.rev;\n if (!known.has(item.id)) {\n order.push(item.id);\n known.add(item.id);\n }\n }\n\n return {\n byId,\n order,\n revById,\n nextCursor: page.nextCursor,\n totalCount: page.totalCount,\n snapshotRev: base.snapshotRev,\n };\n}\n\n/**\n * Apply a live delta. Returns the previous entry object unchanged when the\n * delta is stale (rev LWW) so referential equality can skip re-renders.\n */\nexport function applyDelta<TItem extends { id: string }>(\n prev: CollectionCacheEntry<TItem> | null | undefined,\n delta: CollectionDelta<TItem>,\n options?: ApplyDeltaOptions,\n): ApplyDeltaResult<TItem> {\n const base = prev ?? createEmptyEntry<TItem>();\n\n if (delta.type === \"reset\") {\n return { entry: base, resetRequested: true };\n }\n\n if (delta.type === \"removed\") {\n const cachedRev = base.revById[delta.id];\n if (cachedRev !== undefined && delta.rev < cachedRev) {\n return { entry: base, resetRequested: false };\n }\n const existed = base.byId[delta.id] !== undefined;\n const byId = { ...base.byId };\n delete byId[delta.id];\n return {\n entry: {\n ...base,\n byId,\n order: existed ? base.order.filter((id) => id !== delta.id) : base.order,\n // Tombstone: keep the removal rev so a stale `added` can't resurrect\n revById: { ...base.revById, [delta.id]: delta.rev },\n totalCount:\n existed && base.totalCount !== null ? Math.max(0, base.totalCount - 1) : base.totalCount,\n },\n resetRequested: false,\n };\n }\n\n // added / updated — upsert semantics; the distinction is cosmetic\n const { item, rev } = delta;\n const cachedRev = base.revById[item.id];\n if (cachedRev !== undefined && rev < cachedRev) {\n return { entry: base, resetRequested: false };\n }\n\n const isNew = base.byId[item.id] === undefined;\n const insertPosition = options?.insertPosition ?? \"end\";\n\n return {\n entry: {\n ...base,\n byId: { ...base.byId, [item.id]: item },\n order: isNew\n ? insertPosition === \"start\"\n ? [item.id, ...base.order]\n : [...base.order, item.id]\n : base.order,\n revById: { ...base.revById, [item.id]: rev },\n totalCount: isNew && base.totalCount !== null ? base.totalCount + 1 : base.totalCount,\n },\n resetRequested: false,\n };\n}\n\n/**\n * Apply a batch of buffered deltas in arrival order (used when deltas\n * raced an in-flight snapshot). `resetRequested` is true if any was a reset.\n */\nexport function applyDeltas<TItem extends { id: string }>(\n prev: CollectionCacheEntry<TItem> | null | undefined,\n deltas: CollectionDelta<TItem>[],\n options?: ApplyDeltaOptions,\n): ApplyDeltaResult<TItem> {\n let entry = prev ?? createEmptyEntry<TItem>();\n let resetRequested = false;\n for (const delta of deltas) {\n const result = applyDelta(entry, delta, options);\n entry = result.entry;\n resetRequested = resetRequested || result.resetRequested;\n }\n return { entry, resetRequested };\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport type { CollectionDelta, CollectionSnapshotResponse, ServiceResponse } from \"../shared/types\";\nimport { collectionRoom } from \"../shared/types\";\nimport {\n applyDeltas,\n applyDelta,\n applyPage,\n applySnapshot,\n type CollectionCacheEntry,\n} from \"./collectionCache\";\nimport { useQuickdrawSocket } from \"./QuickdrawProvider\";\nimport type { SubscriptionEntry, UseCollectionOptions, UseCollectionResult } from \"./types\";\n\nconst RESET_DEBOUNCE_MS = 100;\nconst REQUEST_TIMEOUT_MS = 10_000;\n\n/** Cache value under [service, \"collection\", name, scopeId]. */\ninterface CollectionQueryState<TItem extends { id: string }> {\n entry: CollectionCacheEntry<TItem> | null;\n error: string | null;\n}\n\nconst EMPTY_STATE: CollectionQueryState<{ id: string }> = { entry: null, error: null };\n\n/**\n * Shared per-subscription pipeline, stashed on the registry entry by the\n * owning component so every component on the same key drives one snapshot\n * and one loadMore at a time.\n */\ninterface CollectionController {\n requestSnapshot: () => Promise<void>;\n loadMore: () => Promise<void>;\n}\n\n/**\n * Hook for subscribing to a server-declared collection: a scope-keyed,\n * live-updating list (`defineCollection` server-side).\n *\n * The initial snapshot arrives via `{service}:collection:subscribe`;\n * `added`/`updated`/`removed` deltas stream to the scope room and merge\n * id-keyed with per-item last-writer-wins; `reset` deltas trigger a\n * debounced re-snapshot. Reconnects re-snapshot automatically (the same\n * registry cycle entity subscriptions use), pruning rows deleted while\n * offline when the server provides membership `ids`.\n *\n * Subscriptions are deduped across components by scope; pagination reuses\n * the subscribe event with a cursor via `loadMore`.\n *\n * @example\n * ```tsx\n * function ChatWindow({ chatId }: { chatId: string }) {\n * const { items: messages, loadMore, hasMore } = useCollection<MessageDTO>(\n * \"messageService\",\n * \"byChat\",\n * chatId,\n * { compare: (a, b) => a.createdAt.localeCompare(b.createdAt) },\n * );\n * // messages update live; loadMore() pages in older history\n * }\n * ```\n */\nexport function useCollection<TItem extends { id: string }>(\n serviceName: string,\n collection: string,\n scopeId: string | null,\n options?: UseCollectionOptions<TItem>,\n): UseCollectionResult<TItem> {\n const { socket, isConnected, subscriptionRegistry } = useQuickdrawSocket();\n const queryClient = useQueryClient();\n const [isLoadingMore, setIsLoadingMore] = React.useState(false);\n\n const {\n enabled = true,\n limit,\n compare,\n insertPosition = \"end\",\n onDelta,\n onError,\n } = options ?? {};\n\n // Callbacks and merge options read through refs so the owner closure and\n // per-component listeners never need re-subscribing on identity changes.\n const onDeltaRef = React.useRef(onDelta);\n const onErrorRef = React.useRef(onError);\n const insertPositionRef = React.useRef(insertPosition);\n React.useEffect(() => {\n onDeltaRef.current = onDelta;\n onErrorRef.current = onError;\n insertPositionRef.current = insertPosition;\n }, [onDelta, onError, insertPosition]);\n\n // Registry key doubles as the delta event name (== room name)\n const subscriptionKey = scopeId ? collectionRoom(serviceName, collection, scopeId) : null;\n const queryKey = React.useMemo(\n () => [serviceName, \"collection\", collection, scopeId],\n [serviceName, collection, scopeId],\n );\n\n // Reactive cache read — data is written via setQueryData by the owner.\n // initialData seeds the cache without an initial fetch, so a snapshot ack\n // landing between mount and a queryFn resolution can never be clobbered.\n const query = useQuery<CollectionQueryState<TItem>>({\n queryKey,\n queryFn: () =>\n queryClient.getQueryData<CollectionQueryState<TItem>>(queryKey) ??\n (EMPTY_STATE as CollectionQueryState<TItem>),\n initialData: () => EMPTY_STATE as CollectionQueryState<TItem>,\n staleTime: Infinity,\n refetchOnMount: false,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n });\n\n // The registry entry this component holds (owner or joiner) — the shared\n // controller for refresh/loadMore lives on it.\n const regEntryRef = React.useRef<SubscriptionEntry | null>(null);\n\n React.useEffect(() => {\n if (!socket || !isConnected || !enabled || !subscriptionKey || !scopeId) {\n return;\n }\n\n const { isNew, entry: regEntry } = subscriptionRegistry.acquire(subscriptionKey);\n regEntryRef.current = regEntry;\n\n if (!isNew) {\n return () => {\n regEntryRef.current = null;\n subscriptionRegistry.release(subscriptionKey);\n };\n }\n\n // ---- Owner path: this closure runs the shared pipeline for the key.\n // It stays alive as long as any component holds the subscription.\n const eventName = subscriptionKey;\n let disposed = false;\n let snapshotInFlight = false;\n let buffered: CollectionDelta<TItem>[] = [];\n let inFlightSnapshot: Promise<void> | null = null;\n let inFlightLoadMore: Promise<void> | null = null;\n let resetTimer: ReturnType<typeof setTimeout> | null = null;\n\n const readState = (): CollectionQueryState<TItem> =>\n queryClient.getQueryData<CollectionQueryState<TItem>>(queryKey) ??\n (EMPTY_STATE as CollectionQueryState<TItem>);\n\n const writeState = (state: CollectionQueryState<TItem>): void => {\n queryClient.setQueryData(queryKey, state);\n };\n\n const emitSubscribe = (\n cursor: string | null,\n ): Promise<ServiceResponse<CollectionSnapshotResponse<TItem>>> =>\n new Promise((resolve) => {\n let settled = false;\n const timeoutId = setTimeout(() => {\n if (!settled) {\n settled = true;\n resolve({ success: false, error: \"Request timeout\" });\n }\n }, REQUEST_TIMEOUT_MS);\n\n socket.emit(\n `${serviceName}:collection:subscribe`,\n {\n collection,\n scopeId,\n ...(cursor === null ? {} : { cursor }),\n ...(limit === undefined ? {} : { limit }),\n },\n (response: ServiceResponse<CollectionSnapshotResponse<TItem>>) => {\n if (settled) return;\n settled = true;\n clearTimeout(timeoutId);\n resolve(response);\n },\n );\n });\n\n const scheduleReset = (): void => {\n if (resetTimer || disposed) return;\n resetTimer = setTimeout(() => {\n resetTimer = null;\n void requestSnapshot();\n }, RESET_DEBOUNCE_MS);\n };\n\n const requestSnapshot = (): Promise<void> => {\n if (inFlightSnapshot) return inFlightSnapshot;\n snapshotInFlight = true;\n buffered = [];\n inFlightSnapshot = emitSubscribe(null).then((response) => {\n snapshotInFlight = false;\n inFlightSnapshot = null;\n const raced = buffered;\n buffered = [];\n if (disposed) return;\n\n if (response.success) {\n const prev = readState();\n const fresh = applySnapshot(prev.entry, response.data);\n // Deltas that raced the snapshot apply on top of it (rev LWW)\n const { entry, resetRequested } = applyDeltas(fresh, raced, {\n insertPosition: insertPositionRef.current,\n });\n writeState({ entry, error: null });\n if (resetRequested) scheduleReset();\n } else {\n // Keep stale data; surface the error\n const prev = readState();\n writeState({ entry: prev.entry, error: response.error });\n }\n });\n return inFlightSnapshot;\n };\n\n const loadMore = (): Promise<void> => {\n if (inFlightLoadMore) return inFlightLoadMore;\n const cursor = readState().entry?.nextCursor ?? null;\n if (cursor === null) return Promise.resolve();\n\n inFlightLoadMore = emitSubscribe(cursor).then((response) => {\n inFlightLoadMore = null;\n if (disposed) return;\n\n const prev = readState();\n if (response.success) {\n writeState({ entry: applyPage(prev.entry, response.data), error: prev.error });\n } else {\n writeState({ entry: prev.entry, error: response.error });\n }\n });\n return inFlightLoadMore;\n };\n\n const handleDelta = (delta: CollectionDelta<TItem>): void => {\n if (delta.type === \"reset\") {\n scheduleReset();\n return;\n }\n if (snapshotInFlight) {\n buffered.push(delta);\n return;\n }\n const prev = readState();\n const { entry } = applyDelta(prev.entry, delta, {\n insertPosition: insertPositionRef.current,\n });\n if (entry !== prev.entry) {\n writeState({ entry, error: prev.error });\n }\n };\n\n const controller: CollectionController = { requestSnapshot, loadMore };\n regEntry.controller = controller;\n\n socket.on(eventName, handleDelta);\n void requestSnapshot();\n\n subscriptionRegistry.setCleanup(subscriptionKey, () => {\n disposed = true;\n if (resetTimer) clearTimeout(resetTimer);\n socket.off(eventName, handleDelta);\n socket.emit(`${serviceName}:collection:unsubscribe`, { collection, scopeId });\n regEntry.controller = undefined;\n });\n\n return () => {\n regEntryRef.current = null;\n subscriptionRegistry.release(subscriptionKey);\n };\n }, [\n socket,\n isConnected,\n enabled,\n subscriptionKey,\n scopeId,\n serviceName,\n collection,\n limit,\n queryClient,\n queryKey,\n subscriptionRegistry,\n ]);\n\n // Per-component onDelta listener (cache application happens once, in the\n // owner). Attached only when the component asked for it.\n const wantsDelta = !!onDelta;\n React.useEffect(() => {\n if (!socket || !isConnected || !enabled || !subscriptionKey || !wantsDelta) {\n return;\n }\n const listener = (delta: CollectionDelta<TItem>): void => {\n onDeltaRef.current?.(delta);\n };\n socket.on(subscriptionKey, listener);\n return () => {\n socket.off(subscriptionKey, listener);\n };\n }, [socket, isConnected, enabled, subscriptionKey, wantsDelta]);\n\n const state = query.data ?? (EMPTY_STATE as CollectionQueryState<TItem>);\n const entry = state.entry;\n\n // Per-component error callback, fired on shared error transitions\n React.useEffect(() => {\n if (state.error) {\n onErrorRef.current?.(state.error);\n }\n }, [state.error]);\n\n const getController = (): CollectionController | null =>\n (regEntryRef.current?.controller as CollectionController | undefined) ?? null;\n const getControllerRef = React.useRef(getController);\n getControllerRef.current = getController;\n\n const refresh = React.useCallback(async (): Promise<void> => {\n await getControllerRef.current()?.requestSnapshot();\n }, []);\n\n const loadMore = React.useCallback(async (): Promise<void> => {\n const controller = getControllerRef.current();\n if (!controller) return;\n setIsLoadingMore(true);\n try {\n await controller.loadMore();\n } finally {\n setIsLoadingMore(false);\n }\n }, []);\n\n const items = React.useMemo(() => {\n if (!entry) return [] as TItem[];\n const list = entry.order\n .map((id) => entry.byId[id])\n .filter((item): item is TItem => item !== undefined);\n if (compare) list.sort(compare);\n return list;\n }, [entry, compare]);\n\n const byId = React.useMemo(() => {\n const map = new Map<string, TItem>();\n if (entry) {\n for (const id of entry.order) {\n const item = entry.byId[id];\n if (item !== undefined) map.set(id, item);\n }\n }\n return map as ReadonlyMap<string, TItem>;\n }, [entry]);\n\n const active = enabled && !!scopeId;\n\n return {\n items,\n byId,\n totalCount: entry?.totalCount ?? null,\n isLoading: active && !entry && !state.error,\n isError: state.error !== null,\n error: state.error,\n hasMore: (entry?.nextCursor ?? null) !== null,\n isLoadingMore,\n loadMore,\n refresh,\n };\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useQuickdrawSocket } from \"./QuickdrawProvider\";\nimport type { QuickdrawRoomEventHandlers, UseRoomEventsOptions } from \"./types\";\n\n/**\n * Hook for listening to custom socket events with managed lifecycle.\n *\n * Handles `socket.on`/`socket.off` cleanup, reconnection re-attachment,\n * and connection-awareness. Each component gets its own listeners (no dedup needed).\n * Room scoping is handled server-side via `emitToRoom`.\n *\n * Event names and payloads are typed via the augmentable `QuickdrawEventMap`\n * (from the package root): augmented names get payload checking and\n * autocomplete; unlisted names still work, untyped — nothing breaks if the\n * map stays empty.\n *\n * Pair with `useSubscription` to ensure the socket is in the correct room:\n * `useSubscription` manages room membership, `useRoomEvents` manages event listeners.\n *\n * @param handlers - Map of event names to handler functions. Event names should be\n * stable (don't change between renders). Handler functions can change freely.\n * @param options - Optional configuration\n *\n * @example\n * ```tsx\n * function ChatView({ chatId }: { chatId: string }) {\n * const [messages, setMessages] = useState<Message[]>([]);\n * const [typing, setTyping] = useState(false);\n *\n * // Subscribe to the chat room (manages room membership)\n * const { data: chat } = useSubscription('chatService', chatId);\n *\n * // Listen for custom events broadcast to the chat room\n * useRoomEvents({\n * 'chat:message': (msg: Message) => setMessages(prev => [...prev, msg]),\n * 'agent_typing_start': () => setTyping(true),\n * 'agent_typing_stop': () => setTyping(false),\n * });\n *\n * return <div>{chat?.title}</div>;\n * }\n * ```\n */\nexport function useRoomEvents(\n handlers: QuickdrawRoomEventHandlers,\n options?: UseRoomEventsOptions,\n): void {\n const { socket, isConnected } = useQuickdrawSocket();\n const { enabled = true } = options ?? {};\n\n // Store handlers in a ref so handler identity changes don't trigger effect reruns.\n // The event names (keys) are tracked separately for structural changes.\n const handlersRef = React.useRef(handlers);\n React.useEffect(() => {\n handlersRef.current = handlers;\n }, [handlers]);\n\n // Memoize the sorted event names so the effect only re-runs when the\n // set of listened events actually changes, not on every render.\n const eventNames = Object.keys(handlers).filter((name) => handlers[name] != null);\n const eventNamesKey = eventNames.join(\"\\0\");\n const stableEventNames = React.useMemo(\n () => eventNames,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [eventNamesKey],\n );\n\n React.useEffect(() => {\n if (!socket || !isConnected || !enabled || stableEventNames.length === 0) {\n return;\n }\n\n // Create stable wrapper functions that delegate to the ref'd handlers.\n // This lets consumers change handler implementations without re-subscribing.\n const wrappers = new Map<string, (...args: unknown[]) => void>();\n\n for (const eventName of stableEventNames) {\n const wrapper = (...args: unknown[]) => {\n handlersRef.current[eventName]?.(...(args as [never]));\n };\n wrappers.set(eventName, wrapper);\n socket.on(eventName, wrapper);\n }\n\n return () => {\n for (const [eventName, wrapper] of wrappers) {\n socket.off(eventName, wrapper);\n }\n };\n }, [socket, isConnected, enabled, stableEventNames]);\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { channelEventName } from \"../shared/types\";\nimport { useQuickdrawSocket } from \"./QuickdrawProvider\";\nimport type { UseChannelSendResult } from \"./types\";\n\n/**\n * Hook for sending fire-and-forget channel messages (the high-frequency\n * counterpart to useService).\n *\n * Channel messages are emitted volatile: if the connection is backpressured,\n * the message is dropped instead of queued — the right behavior for input\n * streams where the next message supersedes the last. There is no ack and no\n * response; the server validates, rate-limits (per-channel token bucket), and\n * access-checks each message, silently dropping any that fail.\n *\n * Receiving server broadcasts needs nothing new: volatile room emits arrive\n * as ordinary events, so pair this with `useRoomEvents` (and `useSubscription`\n * for room membership).\n *\n * @typeParam TPayload - The channel payload type (from the service's channel map)\n *\n * @example\n * ```tsx\n * function GameInput({ worldId }: { worldId: string }) {\n * // Room membership (ACL-checked server-side) gates the channel\n * useSubscription(\"gameService\", worldId);\n * const { send, isReady } = useChannelSend<GameInput>(\"gameService\", \"input\");\n *\n * // e.g. called from a 20Hz game loop\n * const onTick = (input: GameInput) => send(input);\n * }\n * ```\n */\nexport function useChannelSend<TPayload = unknown>(\n serviceName: string,\n channelName: string,\n): UseChannelSendResult<TPayload> {\n const { socket, isConnected } = useQuickdrawSocket();\n\n const eventName = channelEventName(serviceName, channelName);\n\n const send = React.useCallback(\n (payload: TPayload) => {\n if (!socket?.connected) return;\n socket.volatile.emit(eventName, payload);\n },\n [socket, eventName],\n );\n\n return { send, isReady: isConnected && !!socket };\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nexport type CommitMode = \"change\" | \"blur\" | \"debounce\";\n\nexport interface UseSocketInputOptions<TEntry, TAllowedUpdate, TKey extends keyof TAllowedUpdate> {\n /**\n * Current state of the entity\n */\n state: TEntry | null;\n /**\n * Property key to update\n */\n property: TKey;\n /**\n * Update function that sends the patch to the server\n */\n update: (patch: Partial<TAllowedUpdate>) => Promise<TEntry | undefined | null>;\n /**\n * When to commit changes to the server\n * - \"change\": immediately on every change\n * - \"blur\": when input loses focus\n * - \"debounce\": after debounceMs of inactivity\n */\n commitMode: CommitMode;\n /**\n * Debounce delay in milliseconds (only used when commitMode is \"debounce\")\n */\n debounceMs?: number;\n /**\n * Format the value from state for display\n */\n format?: (value: unknown) => unknown;\n /**\n * Parse the display value back to the state format\n */\n parse?: (value: unknown) => unknown;\n /**\n * Callback on successful update\n */\n onSuccess?: (entry: TEntry | undefined | null) => void;\n /**\n * Callback on error\n */\n onError?: (error: string) => void;\n}\n\nexport interface UseSocketInputResult<T> {\n /**\n * Current local value (may differ from server while editing)\n */\n value: T;\n /**\n * Update the local value\n */\n onLocalChange: (newValue: T) => void;\n /**\n * Manually commit the current value to the server\n */\n commit: () => void;\n /**\n * Handle blur event (commits if commitMode is \"blur\")\n */\n onBlur: () => void;\n /**\n * Whether an update is currently in flight\n */\n inFlight: boolean;\n /**\n * Whether the local value differs from the server value\n */\n isDirty: boolean;\n}\n\n/**\n * Hook for managing input state that syncs with the server.\n * Handles optimistic updates, debouncing, and conflict resolution.\n */\nexport function useSocketInput<TEntry, TAllowedUpdate, TKey extends keyof TAllowedUpdate>(\n options: UseSocketInputOptions<TEntry, TAllowedUpdate, TKey>,\n): UseSocketInputResult<unknown> {\n const {\n state,\n property,\n update,\n commitMode,\n debounceMs = 300,\n format = (v) => v,\n parse = (v) => v,\n onSuccess,\n onError,\n } = options;\n\n // Get server value\n const serverValue = state ? (state as Record<string, unknown>)[property as string] : undefined;\n const formattedServerValue = format(serverValue);\n\n // Local state\n const [localValue, setLocalValue] = React.useState<unknown>(formattedServerValue);\n const [inFlight, setInFlight] = React.useState(false);\n const [isDirty, setIsDirty] = React.useState(false);\n\n // Refs for debouncing\n const debounceTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n const pendingValueRef = React.useRef<unknown>(null);\n\n // Sync local value with server when server value changes (and we're not dirty)\n React.useEffect(() => {\n if (!isDirty && !inFlight) {\n setLocalValue(formattedServerValue);\n }\n }, [formattedServerValue, isDirty, inFlight]);\n\n // Commit function\n const commit = React.useCallback(async () => {\n if (!isDirty) return;\n\n const valueToCommit = pendingValueRef.current ?? localValue;\n const parsedValue = parse(valueToCommit);\n\n setInFlight(true);\n\n try {\n const result = await update({ [property]: parsedValue } as Partial<TAllowedUpdate>);\n setIsDirty(false);\n pendingValueRef.current = null;\n onSuccess?.(result);\n } catch (err) {\n onError?.(err instanceof Error ? err.message : \"Update failed\");\n // Revert to server value on error\n setLocalValue(formattedServerValue);\n setIsDirty(false);\n } finally {\n setInFlight(false);\n }\n }, [isDirty, localValue, parse, property, update, onSuccess, onError, formattedServerValue]);\n\n // Handle local change\n const onLocalChange = React.useCallback(\n (newValue: unknown) => {\n setLocalValue(newValue);\n setIsDirty(true);\n pendingValueRef.current = newValue;\n\n if (commitMode === \"change\") {\n // Commit immediately\n void (async () => {\n const parsedValue = parse(newValue);\n setInFlight(true);\n try {\n const result = await update({ [property]: parsedValue } as Partial<TAllowedUpdate>);\n setIsDirty(false);\n pendingValueRef.current = null;\n onSuccess?.(result);\n } catch (err) {\n onError?.(err instanceof Error ? err.message : \"Update failed\");\n } finally {\n setInFlight(false);\n }\n })();\n } else if (commitMode === \"debounce\") {\n // Clear existing timer\n if (debounceTimerRef.current) {\n clearTimeout(debounceTimerRef.current);\n }\n // Set new timer\n debounceTimerRef.current = setTimeout(() => {\n void commit();\n }, debounceMs);\n }\n // For \"blur\" mode, we just update local state and wait for blur\n },\n [commitMode, parse, property, update, onSuccess, onError, debounceMs, commit],\n );\n\n // Handle blur\n const onBlur = React.useCallback(() => {\n if (commitMode === \"blur\" && isDirty) {\n void commit();\n }\n }, [commitMode, isDirty, commit]);\n\n // Cleanup debounce timer\n React.useEffect(() => {\n return () => {\n if (debounceTimerRef.current) {\n clearTimeout(debounceTimerRef.current);\n }\n };\n }, []);\n\n return {\n value: localValue,\n onLocalChange,\n commit,\n onBlur,\n inFlight,\n isDirty,\n };\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useSocketInput, type CommitMode } from \"./useSocketInput\";\n\nexport interface SocketCheckboxProps<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n> extends Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n \"checked\" | \"onChange\" | \"type\" | \"onError\" | \"property\"\n> {\n state: TEntry | null;\n update: (patch: Partial<TAllowedUpdate>) => Promise<TEntry | undefined | null>;\n property: TKey;\n commitMode?: CommitMode;\n debounceMs?: number;\n format?: (v: unknown) => unknown;\n parse?: (v: unknown) => unknown;\n onSuccess?: (entry: TEntry | undefined | null) => void;\n onError?: (error: string) => void;\n}\n\n/**\n * A checkbox input that syncs its checked state with the server via socket.\n *\n * @example\n * ```tsx\n * <SocketCheckbox\n * state={chat}\n * update={(patch) => updateChat.mutateAsync({ id: chat.id, ...patch })}\n * property=\"isArchived\"\n * commitMode=\"change\"\n * />\n * ```\n */\nexport function SocketCheckbox<TEntry, TAllowedUpdate, TKey extends keyof TAllowedUpdate>(\n props: SocketCheckboxProps<TEntry, TAllowedUpdate, TKey>,\n): React.ReactElement {\n const {\n state,\n update,\n property,\n commitMode = \"change\",\n debounceMs,\n format,\n parse,\n onSuccess,\n onError,\n disabled,\n ...inputProps\n } = props;\n\n const defaultFormat = React.useCallback((v: unknown) => Boolean(v), []);\n const defaultParse = React.useCallback((v: unknown) => Boolean(v), []);\n\n const { value, onLocalChange, inFlight } = useSocketInput<TEntry, TAllowedUpdate, TKey>({\n state,\n property,\n update,\n commitMode,\n debounceMs,\n format: format ?? defaultFormat,\n parse: parse ?? defaultParse,\n onSuccess,\n onError,\n });\n\n const handleChange = React.useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n onLocalChange(event.target.checked);\n },\n [onLocalChange],\n );\n\n return (\n <input\n {...inputProps}\n type=\"checkbox\"\n checked={Boolean(value)}\n onChange={handleChange}\n disabled={Boolean(disabled) || inFlight}\n />\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useSocketInput, type CommitMode } from \"./useSocketInput\";\n\nexport interface SocketTextFieldProps<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n> extends Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n \"value\" | \"onChange\" | \"onError\" | \"property\"\n> {\n state: TEntry | null;\n update: (patch: Partial<TAllowedUpdate>) => Promise<TEntry | undefined | null>;\n property: TKey;\n commitMode?: CommitMode;\n debounceMs?: number;\n format?: (v: unknown) => unknown;\n parse?: (v: unknown) => unknown;\n onSuccess?: (entry: TEntry | undefined | null) => void;\n onError?: (error: string) => void;\n}\n\n/**\n * A text input that syncs its value with the server via socket.\n *\n * @example\n * ```tsx\n * <SocketTextField\n * state={chat}\n * update={(patch) => updateChat.mutateAsync({ id: chat.id, ...patch })}\n * property=\"title\"\n * commitMode=\"debounce\"\n * debounceMs={500}\n * placeholder=\"Chat title...\"\n * />\n * ```\n */\nexport function SocketTextField<TEntry, TAllowedUpdate, TKey extends keyof TAllowedUpdate>(\n props: SocketTextFieldProps<TEntry, TAllowedUpdate, TKey>,\n): React.ReactElement {\n const {\n state,\n update,\n property,\n commitMode = \"blur\",\n debounceMs = 300,\n format,\n parse,\n onSuccess,\n onError,\n disabled,\n type = \"text\",\n onBlur: consumerOnBlur,\n ...inputProps\n } = props;\n\n const defaultFormat = React.useCallback((v: unknown) => String(v ?? \"\"), []);\n const defaultParse = React.useCallback((v: unknown) => String(v), []);\n\n const { value, onLocalChange, onBlur, inFlight } = useSocketInput<TEntry, TAllowedUpdate, TKey>({\n state,\n property,\n update,\n commitMode,\n debounceMs,\n format: format ?? defaultFormat,\n parse: parse ?? defaultParse,\n onSuccess,\n onError,\n });\n\n const handleChange = React.useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n onLocalChange(event.target.value);\n },\n [onLocalChange],\n );\n\n // Chain the commit-on-blur handler with the consumer's onBlur — spreading\n // inputProps before the controlled onBlur used to silently swallow it.\n const handleBlur = React.useCallback(\n (event: React.FocusEvent<HTMLInputElement>) => {\n onBlur();\n consumerOnBlur?.(event);\n },\n [onBlur, consumerOnBlur],\n );\n\n return (\n <input\n {...inputProps}\n type={type}\n value={String(value)}\n onChange={handleChange}\n onBlur={handleBlur}\n disabled={Boolean(disabled) || inFlight}\n />\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useSocketInput, type CommitMode } from \"./useSocketInput\";\n\nexport interface SocketSelectProps<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n> extends Omit<\n React.SelectHTMLAttributes<HTMLSelectElement>,\n \"value\" | \"onChange\" | \"onError\" | \"property\"\n> {\n state: TEntry | null;\n update: (patch: Partial<TAllowedUpdate>) => Promise<TEntry | undefined | null>;\n property: TKey;\n children: React.ReactNode;\n commitMode?: CommitMode;\n debounceMs?: number;\n format?: (v: unknown) => unknown;\n parse?: (v: unknown) => unknown;\n onSuccess?: (entry: TEntry | undefined | null) => void;\n onError?: (error: string) => void;\n}\n\n/**\n * A select input that syncs its value with the server via socket.\n *\n * @example\n * ```tsx\n * <SocketSelect\n * state={chat}\n * update={(patch) => updateChat.mutateAsync({ id: chat.id, ...patch })}\n * property=\"status\"\n * commitMode=\"change\"\n * >\n * <option value=\"active\">Active</option>\n * <option value=\"archived\">Archived</option>\n * </SocketSelect>\n * ```\n */\nexport function SocketSelect<TEntry, TAllowedUpdate, TKey extends keyof TAllowedUpdate>(\n props: SocketSelectProps<TEntry, TAllowedUpdate, TKey>,\n): React.ReactElement {\n const {\n state,\n update,\n property,\n children,\n commitMode = \"change\",\n debounceMs,\n format,\n parse,\n onSuccess,\n onError,\n disabled,\n ...selectProps\n } = props;\n\n const defaultFormat = React.useCallback((v: unknown) => String(v ?? \"\"), []);\n const defaultParse = React.useCallback((v: unknown) => String(v), []);\n\n const { value, onLocalChange, inFlight } = useSocketInput<TEntry, TAllowedUpdate, TKey>({\n state,\n property,\n update,\n commitMode,\n debounceMs,\n format: format ?? defaultFormat,\n parse: parse ?? defaultParse,\n onSuccess,\n onError,\n });\n\n const handleChange = React.useCallback(\n (event: React.ChangeEvent<HTMLSelectElement>) => {\n onLocalChange(event.target.value);\n },\n [onLocalChange],\n );\n\n return (\n <select\n {...selectProps}\n value={String(value)}\n onChange={handleChange}\n disabled={Boolean(disabled) || inFlight}\n >\n {children}\n </select>\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useSocketInput, type CommitMode } from \"./useSocketInput\";\n\nexport interface SocketSliderProps<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n> extends Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n \"value\" | \"onChange\" | \"type\" | \"onError\" | \"property\"\n> {\n state: TEntry | null;\n update: (patch: Partial<TAllowedUpdate>) => Promise<TEntry | undefined | null>;\n property: TKey;\n commitMode?: CommitMode;\n debounceMs?: number;\n format?: (v: unknown) => unknown;\n parse?: (v: unknown) => unknown;\n onSuccess?: (entry: TEntry | undefined | null) => void;\n onError?: (error: string) => void;\n}\n\n/**\n * A range slider input that syncs its value with the server via socket.\n *\n * @example\n * ```tsx\n * <SocketSlider\n * state={settings}\n * update={(patch) => updateSettings.mutateAsync({ id: settings.id, ...patch })}\n * property=\"volume\"\n * min={0}\n * max={100}\n * commitMode=\"debounce\"\n * debounceMs={200}\n * />\n * ```\n */\nexport function SocketSlider<TEntry, TAllowedUpdate, TKey extends keyof TAllowedUpdate>(\n props: SocketSliderProps<TEntry, TAllowedUpdate, TKey>,\n): React.ReactElement {\n const {\n state,\n update,\n property,\n commitMode = \"debounce\",\n debounceMs = 200,\n format,\n parse,\n onSuccess,\n onError,\n disabled,\n ...inputProps\n } = props;\n\n const defaultFormat = React.useCallback((v: unknown) => Number(v ?? 0), []);\n const defaultParse = React.useCallback((v: unknown) => Number(v), []);\n\n const { value, onLocalChange, inFlight } = useSocketInput<TEntry, TAllowedUpdate, TKey>({\n state,\n property,\n update,\n commitMode,\n debounceMs,\n format: format ?? defaultFormat,\n parse: parse ?? defaultParse,\n onSuccess,\n onError,\n });\n\n const handleChange = React.useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n onLocalChange(Number(event.target.value));\n },\n [onLocalChange],\n );\n\n return (\n <input\n {...inputProps}\n type=\"range\"\n value={Number(value)}\n onChange={handleChange}\n disabled={Boolean(disabled) || inFlight}\n />\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useSocketInput, type CommitMode } from \"./useSocketInput\";\n\nexport interface SocketSwitchProps<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n> extends Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n \"checked\" | \"onChange\" | \"type\" | \"onError\" | \"property\"\n> {\n state: TEntry | null;\n update: (patch: Partial<TAllowedUpdate>) => Promise<TEntry | undefined | null>;\n property: TKey;\n commitMode?: CommitMode;\n debounceMs?: number;\n format?: (v: unknown) => unknown;\n parse?: (v: unknown) => unknown;\n onSuccess?: (entry: TEntry | undefined | null) => void;\n onError?: (error: string) => void;\n}\n\n/**\n * A switch/toggle input that syncs its state with the server via socket.\n * Styled as a switch by default using CSS.\n *\n * @example\n * ```tsx\n * <SocketSwitch\n * state={settings}\n * update={(patch) => updateSettings.mutateAsync({ id: settings.id, ...patch })}\n * property=\"darkMode\"\n * commitMode=\"change\"\n * />\n * ```\n */\nexport function SocketSwitch<TEntry, TAllowedUpdate, TKey extends keyof TAllowedUpdate>(\n props: SocketSwitchProps<TEntry, TAllowedUpdate, TKey>,\n): React.ReactElement {\n const {\n state,\n update,\n property,\n commitMode = \"change\",\n debounceMs,\n format,\n parse,\n onSuccess,\n onError,\n disabled,\n className,\n ...inputProps\n } = props;\n\n const defaultFormat = React.useCallback((v: unknown) => Boolean(v), []);\n const defaultParse = React.useCallback((v: unknown) => Boolean(v), []);\n\n const { value, onLocalChange, inFlight } = useSocketInput<TEntry, TAllowedUpdate, TKey>({\n state,\n property,\n update,\n commitMode,\n debounceMs,\n format: format ?? defaultFormat,\n parse: parse ?? defaultParse,\n onSuccess,\n onError,\n });\n\n const handleChange = React.useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n onLocalChange(event.target.checked);\n },\n [onLocalChange],\n );\n\n // Role=\"switch\" for accessibility\n return (\n <input\n {...inputProps}\n type=\"checkbox\"\n role=\"switch\"\n aria-checked={Boolean(value)}\n checked={Boolean(value)}\n onChange={handleChange}\n disabled={Boolean(disabled) || inFlight}\n className={className}\n />\n );\n}\n","/**\n * Format a currency value for display.\n */\nexport function formatCurrency(\n value: string | number | null | undefined,\n currency = \"USD\",\n locale = \"en-US\",\n): string {\n if (value == null) return \"-\";\n const num = typeof value === \"string\" ? parseFloat(value) : value;\n if (isNaN(num)) return \"-\";\n return new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency,\n }).format(num);\n}\n\n/**\n * Format a number for display with optional decimal places.\n */\nexport function formatNumber(\n value: string | number | null | undefined,\n options?: Intl.NumberFormatOptions,\n locale = \"en-US\",\n): string {\n if (value == null) return \"-\";\n const num = typeof value === \"string\" ? parseFloat(value) : value;\n if (isNaN(num)) return \"-\";\n return new Intl.NumberFormat(locale, options).format(num);\n}\n\n/**\n * Format a date for display.\n */\nexport function formatDate(\n value: string | Date | null | undefined,\n options?: Intl.DateTimeFormatOptions,\n locale = \"en-US\",\n): string {\n if (!value) return \"-\";\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (isNaN(date.getTime())) return \"-\";\n return date.toLocaleDateString(\n locale,\n options ?? {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n },\n );\n}\n\n/**\n * Format a date with time for display.\n */\nexport function formatDateTime(\n value: string | Date | null | undefined,\n options?: Intl.DateTimeFormatOptions,\n locale = \"en-US\",\n): string {\n if (!value) return \"-\";\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (isNaN(date.getTime())) return \"-\";\n return date.toLocaleString(\n locale,\n options ?? {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n },\n );\n}\n\n/**\n * Truncate a string to a maximum length.\n */\nexport function truncate(value: string | null | undefined, maxLength = 50): string {\n if (!value) return \"-\";\n if (value.length <= maxLength) return value;\n return `${value.slice(0, maxLength - 3)}...`;\n}\n\n/**\n * Format a percentage for display.\n */\nexport function formatPercent(\n value: string | number | null | undefined,\n decimals = 1,\n locale = \"en-US\",\n): string {\n if (value == null) return \"-\";\n const num = typeof value === \"string\" ? parseFloat(value) : value;\n if (isNaN(num)) return \"-\";\n return new Intl.NumberFormat(locale, {\n style: \"percent\",\n minimumFractionDigits: decimals,\n maximumFractionDigits: decimals,\n }).format(num / 100);\n}\n","const AUTH_TOKEN_KEY = \"auth_token\";\n\n/**\n * Get the stored auth token from localStorage.\n */\nexport function getAuthToken(): string | null {\n if (typeof window === \"undefined\") return null;\n return localStorage.getItem(AUTH_TOKEN_KEY);\n}\n\n/**\n * Store the auth token in localStorage and dispatch a change event.\n */\nexport function setAuthToken(token: string): void {\n if (typeof window === \"undefined\") return;\n localStorage.setItem(AUTH_TOKEN_KEY, token);\n window.dispatchEvent(new Event(\"auth-token-changed\"));\n}\n\n/**\n * Remove the auth token from localStorage and dispatch a change event.\n */\nexport function clearAuthToken(): void {\n if (typeof window === \"undefined\") return;\n localStorage.removeItem(AUTH_TOKEN_KEY);\n window.dispatchEvent(new Event(\"auth-token-changed\"));\n}\n\nexport interface JWTPayload {\n userId: string;\n email?: string;\n}\n\n/**\n * Parse JWT payload client-side (not verified, for display purposes only).\n */\nexport function parseJWTPayload(token: string): JWTPayload | null {\n try {\n const parts = token.split(\".\");\n if (parts.length !== 3 || !parts[1]) return null;\n\n const decoded = atob(parts[1]);\n const payload: unknown = JSON.parse(decoded);\n\n if (\n typeof payload === \"object\" &&\n payload !== null &&\n \"userId\" in payload &&\n typeof (payload as { userId: unknown }).userId === \"string\"\n ) {\n const typedPayload = payload as { userId: string; email?: string };\n return {\n userId: typedPayload.userId,\n email: typedPayload.email,\n };\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Build an OAuth redirect URL for a given provider.\n */\nexport function getOAuthUrl(provider: string, apiUrl?: string): string {\n const base = apiUrl ?? process.env.NEXT_PUBLIC_API_URL ?? \"http://localhost:4000\";\n return `${base}/auth/${provider}`;\n}\n\n/**\n * Logout from current session (invalidate token on server and clear localStorage).\n */\nexport async function logout(apiUrl?: string): Promise<void> {\n const base = apiUrl ?? process.env.NEXT_PUBLIC_API_URL ?? \"http://localhost:4000\";\n const token = getAuthToken();\n\n if (token) {\n try {\n await fetch(`${base}/auth/logout`, {\n method: \"DELETE\",\n headers: { Authorization: `Bearer ${token}` },\n });\n } catch {\n // Clear local token regardless of network errors\n }\n }\n\n clearAuthToken();\n}\n\n/**\n * Logout from all devices (invalidate all sessions and clear localStorage).\n * Returns the number of sessions invalidated.\n */\nexport async function logoutAllDevices(apiUrl?: string): Promise<number> {\n const base = apiUrl ?? process.env.NEXT_PUBLIC_API_URL ?? \"http://localhost:4000\";\n const token = getAuthToken();\n\n if (!token) {\n clearAuthToken();\n return 0;\n }\n\n try {\n const response = await fetch(`${base}/auth/sessions`, {\n method: \"DELETE\",\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (response.ok) {\n const data = (await response.json()) as {\n sessionsDeleted?: number;\n };\n clearAuthToken();\n return data.sessionsDeleted ?? 0;\n }\n } catch {\n // Clear local token regardless of network errors\n }\n\n clearAuthToken();\n return 0;\n}\n","export interface NavItem {\n id: string;\n label: string;\n href: string;\n icon?: unknown;\n children?: NavItem[];\n defaultExpanded?: boolean;\n requireAuth?: boolean;\n dynamicChildren?: boolean;\n serviceName?: string;\n minAccessLevel?: string;\n}\n\nexport interface BreadcrumbItem {\n label: string;\n href: string;\n siblings?: { label: string; href: string }[];\n}\n\n/**\n * Find a nav item by its href (exact match, searching children recursively).\n */\nexport function findNavItemByHref(items: NavItem[], href: string): NavItem | undefined {\n for (const item of items) {\n if (item.href === href) return item;\n if (item.children) {\n const found = findNavItemByHref(item.children, href);\n if (found) return found;\n }\n }\n return undefined;\n}\n\n/**\n * Find the parent nav item containing a child with the given href.\n */\nexport function findParentNavItem(items: NavItem[], href: string): NavItem | undefined {\n for (const item of items) {\n if (item.children) {\n const childMatch = item.children.find((c) => c.href === href);\n if (childMatch) return item;\n const nested = findParentNavItem(item.children, href);\n if (nested) return nested;\n }\n }\n return undefined;\n}\n\n/**\n * Build breadcrumb items from a pathname using the provided navigation config.\n */\nexport function buildBreadcrumbs(pathname: string, navigation: NavItem[]): BreadcrumbItem[] {\n const breadcrumbs: BreadcrumbItem[] = [];\n\n if (pathname === \"/\") {\n breadcrumbs.push({\n label: \"Home\",\n href: \"/\",\n siblings: navigation\n .filter((item) => item.href === \"/\" || !item.requireAuth)\n .map((item) => ({ label: item.label, href: item.href })),\n });\n return breadcrumbs;\n }\n\n const segments = pathname.split(\"/\").filter(Boolean);\n let currentPath = \"\";\n\n for (const segment of segments) {\n currentPath += `/${segment}`;\n const navItem = findNavItemByHref(navigation, currentPath);\n\n if (navItem) {\n const parent = findParentNavItem(navigation, currentPath);\n const siblingItems = parent?.children ?? navigation;\n\n breadcrumbs.push({\n label: navItem.label,\n href: navItem.href,\n siblings: siblingItems.map((item) => ({\n label: item.label,\n href: item.href,\n })),\n });\n } else {\n breadcrumbs.push({\n label: segment,\n href: currentPath,\n });\n }\n }\n\n return breadcrumbs;\n}\n\n/**\n * Check if a route requires authentication by searching the navigation config.\n */\nexport function routeRequiresAuth(\n pathname: string,\n navigation: NavItem[],\n userMenuItems?: NavItem[],\n): boolean {\n const navItem = findNavItemByHref(navigation, pathname);\n if (navItem) return navItem.requireAuth ?? false;\n\n if (userMenuItems) {\n const userItem = findNavItemByHref(userMenuItems, pathname);\n if (userItem) return userItem.requireAuth ?? false;\n }\n\n const segments = pathname.split(\"/\").filter(Boolean);\n let currentPath = \"\";\n for (const segment of segments) {\n currentPath += `/${segment}`;\n const item = findNavItemByHref(navigation, currentPath);\n if (item?.requireAuth) return true;\n }\n\n return false;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/client/QuickdrawProvider.tsx","../../src/client/useService.ts","../../src/client/serviceError.ts","../../src/client/useServiceQuery.ts","../../src/client/useSubscription.ts","../../src/shared/types.ts","../../src/client/collectionCache.ts","../../src/client/useCollection.ts","../../src/client/useRoomEvents.ts","../../src/client/useChannelSend.ts","../../src/client/inputs/useSocketInput.ts","../../src/client/inputs/SocketCheckbox.tsx","../../src/client/inputs/SocketTextField.tsx","../../src/client/inputs/SocketSelect.tsx","../../src/client/inputs/SocketSlider.tsx","../../src/client/inputs/SocketSwitch.tsx","../../src/client/utils/formatting.ts","../../src/client/utils/auth.ts","../../src/client/utils/navigation.ts"],"names":["React2","React3","useQueryClient","React4","useQuery","cachedRev","React5","state","entry","loadMore","React6","React7","React8","React9","jsx","React10","React11","React12","React13"],"mappings":"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","file":"index.js","sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { io, Socket } from \"socket.io-client\";\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport type { AccessLevel } from \"../shared/types\";\nimport type { ServiceResponse } from \"../shared/types\";\nimport type {\n QuickdrawSocketContextValue,\n QuickdrawProviderProps,\n SubscriptionRegistry,\n SubscriptionEntry,\n SubscriptionBatcher,\n BatchSubscribeCallback,\n} from \"./types\";\n\n// ============================================================================\n// Subscription Registry Implementation\n// ============================================================================\n\n/**\n * Creates a new subscription registry instance.\n * Each socket connection should have its own registry.\n */\nfunction createSubscriptionRegistry(): SubscriptionRegistry {\n const subscriptions = new Map<string, SubscriptionEntry>();\n\n return {\n acquire(key: string) {\n const existing = subscriptions.get(key);\n if (existing) {\n existing.refCount++;\n return { isNew: false, entry: existing };\n }\n\n const entry: SubscriptionEntry = { refCount: 1 };\n subscriptions.set(key, entry);\n return { isNew: true, entry };\n },\n\n release(key: string) {\n const entry = subscriptions.get(key);\n if (!entry) return false;\n\n entry.refCount--;\n if (entry.refCount <= 0) {\n entry.cleanup?.();\n subscriptions.delete(key);\n return true;\n }\n return false;\n },\n\n setCleanup(key: string, cleanup: () => void) {\n const entry = subscriptions.get(key);\n if (entry) {\n entry.cleanup = cleanup;\n }\n },\n\n clear() {\n for (const entry of subscriptions.values()) {\n entry.cleanup?.();\n }\n subscriptions.clear();\n },\n };\n}\n\n// ============================================================================\n// Subscription Batcher Implementation\n// ============================================================================\n\ninterface PendingBatchEntry {\n entryId: string;\n requiredLevel: AccessLevel;\n callback: BatchSubscribeCallback;\n}\n\n/**\n * Creates a subscription batcher that collects subscribe requests within\n * the same microtask and sends them as a single batchSubscribe event per service.\n */\nfunction createSubscriptionBatcher(socketRef: { current: Socket | null }): SubscriptionBatcher {\n const pending = new Map<string, PendingBatchEntry[]>();\n let flushScheduled = false;\n\n function flush(): void {\n flushScheduled = false;\n const socket = socketRef.current;\n if (!socket) {\n for (const entries of pending.values()) {\n for (const entry of entries) {\n entry.callback({ success: false, error: \"Socket not connected\" });\n }\n }\n pending.clear();\n return;\n }\n\n for (const [serviceName, entries] of pending) {\n if (entries.length === 0) continue;\n const entryIds = entries.map((e) => e.entryId);\n const requiredLevel = entries[0]!.requiredLevel;\n\n socket.emit(\n `${serviceName}:batchSubscribe`,\n { entryIds, requiredLevel },\n (response: ServiceResponse<Record<string, unknown>>) => {\n if (response.success && response.data) {\n const dataMap = response.data as Record<string, unknown>;\n for (const entry of entries) {\n const entityData = dataMap[entry.entryId];\n if (entityData) {\n entry.callback({\n success: true,\n data: entityData as Record<string, unknown>,\n });\n } else {\n entry.callback({\n success: false,\n error: \"Access denied or entry not found\",\n code: 403,\n });\n }\n }\n } else {\n const errMsg = !response.success ? response.error : \"Batch subscription failed\";\n for (const entry of entries) {\n entry.callback({\n success: false,\n error: errMsg,\n });\n }\n }\n },\n );\n }\n\n pending.clear();\n }\n\n return {\n enqueue(\n serviceName: string,\n entryId: string,\n requiredLevel: AccessLevel,\n callback: BatchSubscribeCallback,\n ): void {\n let entries = pending.get(serviceName);\n if (!entries) {\n entries = [];\n pending.set(serviceName, entries);\n }\n entries.push({ entryId, requiredLevel, callback });\n\n if (!flushScheduled) {\n flushScheduled = true;\n queueMicrotask(flush);\n }\n },\n };\n}\n\n// ============================================================================\n// Socket Context\n// ============================================================================\n\nconst QuickdrawSocketContext = React.createContext<QuickdrawSocketContextValue | null>(null);\n\n/**\n * Hook to access the quickdraw socket context.\n */\nexport function useQuickdrawSocket(): QuickdrawSocketContextValue {\n const context = React.useContext(QuickdrawSocketContext);\n if (!context) {\n throw new Error(\"useQuickdrawSocket must be used within a QuickdrawProvider\");\n }\n return context;\n}\n\n// ============================================================================\n// Provider Component\n// ============================================================================\n\n/**\n * Creates a QueryClient instance.\n * Using a factory function instead of module-level instantiation\n * to avoid SSR hydration issues.\n */\nfunction createQueryClient(): QueryClient {\n return new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 1000 * 60 * 5, // 5 minutes\n retry: 1,\n },\n mutations: {\n retry: 0,\n },\n },\n });\n}\n\n/**\n * Provider component that sets up TanStack Query and Socket.io connection.\n *\n * @example\n * ```tsx\n * // In your app layout\n * export default function RootLayout({ children }) {\n * return (\n * <QuickdrawProvider\n * serverUrl=\"http://localhost:4000\"\n * authToken={token}\n * >\n * {children}\n * </QuickdrawProvider>\n * );\n * }\n * ```\n */\nexport function QuickdrawProvider({\n children,\n serverUrl,\n queryClient,\n authToken,\n autoConnect = !!authToken,\n withCredentials = true,\n socketPath,\n reconnectBehavior = \"invalidate-queries\",\n transports,\n}: QuickdrawProviderProps): React.ReactElement {\n // Create QueryClient lazily to avoid SSR issues\n const [defaultQueryClient] = React.useState(() => createQueryClient());\n const actualQueryClient = queryClient ?? defaultQueryClient;\n\n const [socket, setSocket] = React.useState<Socket | null>(null);\n const [isConnected, setIsConnected] = React.useState(false);\n const [userId, setUserId] = React.useState<string | null>(null);\n const [serviceAccess, setServiceAccess] = React.useState<Record<string, AccessLevel>>({});\n const [isRateLimited, setIsRateLimited] = React.useState(false);\n\n // Create subscription registry - recreated when socket changes\n const subscriptionRegistryRef = React.useRef<SubscriptionRegistry>(createSubscriptionRegistry());\n\n // Track previous authToken to detect changes\n const prevAuthTokenRef = React.useRef<string | undefined>(authToken);\n\n // Track socket synchronously to prevent double connections from concurrent effects\n const socketRef = React.useRef<Socket | null>(null);\n\n // Subscription batcher - uses socketRef so it always has the current socket\n const subscriptionBatcherRef = React.useRef<SubscriptionBatcher>(\n createSubscriptionBatcher(socketRef),\n );\n\n // Reconnect detection + options read through refs so `connect` stays stable\n // (an inline `transports` array literal would otherwise churn `connect` —\n // and with it the context value — on every render)\n const hasEverConnectedRef = React.useRef(false);\n const reconnectBehaviorRef = React.useRef(reconnectBehavior);\n reconnectBehaviorRef.current = reconnectBehavior;\n const transportsRef = React.useRef(transports);\n transportsRef.current = transports;\n const queryClientRef = React.useRef(actualQueryClient);\n queryClientRef.current = actualQueryClient;\n\n // Rate-limit backoff: while the server reports us limited, reads pause.\n // The window extends on repeat reports; a timer flips the flag back.\n const rateLimitUntilRef = React.useRef(0);\n const rateLimitTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const reportRateLimited = React.useCallback((retryAfterMs?: number) => {\n const backoffMs = Math.min(Math.max(retryAfterMs ?? 5000, 250), 5 * 60 * 1000);\n const until = Date.now() + backoffMs;\n if (until <= rateLimitUntilRef.current) return;\n\n rateLimitUntilRef.current = until;\n setIsRateLimited(true);\n if (rateLimitTimerRef.current) clearTimeout(rateLimitTimerRef.current);\n rateLimitTimerRef.current = setTimeout(() => {\n rateLimitTimerRef.current = null;\n rateLimitUntilRef.current = 0;\n setIsRateLimited(false);\n }, backoffMs);\n }, []);\n\n React.useEffect(() => {\n return () => {\n if (rateLimitTimerRef.current) clearTimeout(rateLimitTimerRef.current);\n };\n }, []);\n\n const connect = React.useCallback(\n (token?: string) => {\n // Prevent double connections - check ref synchronously since state updates are async\n if (socketRef.current) return;\n\n const authToUse = token;\n\n // Clear old subscriptions when creating new socket\n subscriptionRegistryRef.current.clear();\n subscriptionRegistryRef.current = createSubscriptionRegistry();\n\n const newSocket = io(serverUrl, {\n auth: authToUse ? { token: authToUse } : undefined,\n withCredentials,\n transports: transportsRef.current ?? [\"websocket\", \"polling\"],\n autoConnect: true,\n ...(socketPath ? { path: socketPath } : {}),\n });\n\n newSocket.on(\"connect\", () => {\n setIsConnected(true);\n // Reconnects (of this socket or a replacement) leave plain query\n // reads stale — room events emitted during the outage are gone.\n // Invalidate so active queries refetch; subscriptions and\n // collections re-establish themselves via the registry cycle.\n if (hasEverConnectedRef.current && reconnectBehaviorRef.current === \"invalidate-queries\") {\n void queryClientRef.current.invalidateQueries();\n }\n hasEverConnectedRef.current = true;\n });\n\n newSocket.on(\"disconnect\", () => {\n setIsConnected(false);\n // Clear subscriptions on disconnect - they'll be re-established on reconnect\n subscriptionRegistryRef.current.clear();\n });\n\n // Listen for auth info from server\n newSocket.on(\n \"auth:info\",\n (info: { userId: string | null; serviceAccess: Record<string, AccessLevel> }) => {\n setUserId(info.userId);\n setServiceAccess(info.serviceAccess ?? {});\n },\n );\n\n // Server-reported rate limiting (default onRateLimitExceeded shape)\n newSocket.on(\"error\", (payload: { code?: string; retryAfter?: number } | undefined) => {\n if (payload && payload.code === \"RATE_LIMITED\") {\n reportRateLimited(payload.retryAfter);\n }\n });\n\n newSocket.on(\"connect_error\", (error) => {\n console.error(\"Socket connection error:\", error.message);\n });\n\n socketRef.current = newSocket;\n setSocket(newSocket);\n },\n [serverUrl, withCredentials, socketPath, reportRateLimited],\n );\n\n const disconnect = React.useCallback(() => {\n if (socketRef.current) {\n // Clear subscriptions before disconnecting\n subscriptionRegistryRef.current.clear();\n socketRef.current.disconnect();\n socketRef.current = null;\n setSocket(null);\n setIsConnected(false);\n setUserId(null);\n setServiceAccess({});\n }\n }, []);\n\n // Auto-connect on mount if enabled\n React.useEffect(() => {\n if (autoConnect && !socketRef.current) {\n connect(authToken);\n }\n\n return () => {\n if (socketRef.current) {\n subscriptionRegistryRef.current.clear();\n socketRef.current.disconnect();\n socketRef.current = null;\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [autoConnect, authToken]);\n\n // Reconnect when authToken changes\n React.useEffect(() => {\n const prevToken = prevAuthTokenRef.current;\n\n // Update ref BEFORE any logic so next render has correct previous value\n prevAuthTokenRef.current = authToken;\n\n // If token actually changed, reconnect\n if (authToken !== prevToken) {\n if (socketRef.current) {\n disconnect();\n }\n if (authToken) {\n connect(authToken);\n }\n }\n }, [authToken, disconnect, connect]);\n\n const contextValue = React.useMemo<QuickdrawSocketContextValue>(\n () => ({\n socket,\n isConnected,\n userId,\n serviceAccess,\n connect,\n disconnect,\n subscriptionRegistry: subscriptionRegistryRef.current,\n subscriptionBatcher: subscriptionBatcherRef.current,\n isRateLimited,\n reportRateLimited,\n }),\n [\n socket,\n isConnected,\n userId,\n serviceAccess,\n connect,\n disconnect,\n isRateLimited,\n reportRateLimited,\n ],\n );\n\n return (\n <QueryClientProvider client={actualQueryClient}>\n <QuickdrawSocketContext.Provider value={contextValue}>\n {children}\n </QuickdrawSocketContext.Provider>\n </QueryClientProvider>\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useMutation } from \"@tanstack/react-query\";\nimport type { ServiceResponse } from \"../shared/types\";\nimport { useQuickdrawSocket } from \"./QuickdrawProvider\";\nimport type { UseServiceOptions, UseServiceResult } from \"./types\";\n\n/**\n * Hook for invoking service methods via Socket.io with TanStack Query integration.\n *\n * This hook supports two usage patterns:\n *\n * 1. **Simple usage** - Just pass service/method names with explicit payload/response types:\n * ```tsx\n * const createChat = useService<{ title: string }, { id: string }>(\n * 'chatService',\n * 'createChat'\n * );\n * ```\n *\n * 2. **Type-safe usage** - Pass a service methods map for full type inference:\n * ```tsx\n * // In your app, create a typed wrapper:\n * function useTypedService<\n * TService extends keyof ServiceMethodsMap,\n * TMethod extends keyof ServiceMethodsMap[TService] & string,\n * >(serviceName: TService, methodName: TMethod, options?: ...) {\n * type Methods = ServiceMethodsMap[TService];\n * type Payload = Methods[TMethod] extends { payload: infer P } ? P : never;\n * type Response = Methods[TMethod] extends { response: infer R } ? R : never;\n * return useService<Payload, Response>(serviceName, methodName, options);\n * }\n * ```\n *\n * @typeParam TPayload - The payload type for the method\n * @typeParam TResponse - The response type for the method\n */\nexport function useService<TPayload = unknown, TResponse = unknown>(\n serviceName: string,\n methodName: string,\n options?: UseServiceOptions<TResponse>,\n): UseServiceResult<TPayload, TResponse> {\n const { socket, isConnected } = useQuickdrawSocket();\n const [error, setError] = React.useState<string | null>(null);\n\n // Store options in ref to avoid unnecessary rerenders\n const optionsRef = React.useRef(options);\n React.useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n const mutation = useMutation<TResponse, Error, TPayload>({\n mutationKey: [serviceName, methodName],\n mutationFn: async (payload: TPayload): Promise<TResponse> => {\n if (!socket || !isConnected) {\n throw new Error(\"Socket not connected\");\n }\n\n return new Promise<TResponse>((resolve, reject) => {\n const eventName = `${serviceName}:${methodName}`;\n const timeout = setTimeout(() => {\n reject(new Error(\"Request timeout\"));\n }, options?.timeout ?? 10000);\n\n socket.emit(eventName, payload, (response: ServiceResponse<TResponse>) => {\n clearTimeout(timeout);\n\n if (response.success) {\n resolve(response.data);\n } else {\n reject(new Error(response.error));\n }\n });\n });\n },\n onSuccess: (data) => {\n setError(null);\n optionsRef.current?.onSuccess?.(data);\n },\n onError: (err) => {\n const errorMessage = err.message;\n setError(errorMessage);\n optionsRef.current?.onError?.(errorMessage);\n },\n retry: options?.retry ?? false,\n retryDelay: options?.retryDelay,\n });\n\n // Memoize return object to prevent new references on every render\n // This fixes infinite loop issues when the hook's return value is used in dependencies\n return React.useMemo(\n () => ({\n mutate: mutation.mutate,\n mutateAsync: mutation.mutateAsync,\n isPending: mutation.isPending,\n isError: mutation.isError,\n error,\n data: mutation.data,\n reset: mutation.reset,\n }),\n [\n mutation.mutate,\n mutation.mutateAsync,\n mutation.isPending,\n mutation.isError,\n mutation.data,\n mutation.reset,\n error,\n ],\n );\n}\n\n/**\n * Simplified hook for basic service method calls.\n * Use this when you don't need the full TanStack Query mutation features.\n *\n * @example\n * ```tsx\n * const { execute, loading, error, data } = useServiceMethod<\n * { title: string },\n * { id: string }\n * >('chatService', 'createChat');\n *\n * const handleCreate = async () => {\n * const result = await execute({ title: 'New Chat' });\n * if (result) {\n * router.push(`/chat/${result.id}`);\n * }\n * };\n * ```\n */\nexport function useServiceMethod<TPayload = unknown, TResponse = unknown>(\n serviceName: string,\n methodName: string,\n options?: UseServiceOptions<TResponse>,\n): {\n execute: (payload: TPayload) => Promise<TResponse | null>;\n loading: boolean;\n error: string | null;\n data: TResponse | null;\n isReady: boolean;\n} {\n const { socket, isConnected } = useQuickdrawSocket();\n const [loading, setLoading] = React.useState(false);\n const [error, setError] = React.useState<string | null>(null);\n const [data, setData] = React.useState<TResponse | null>(null);\n\n const optionsRef = React.useRef(options);\n React.useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n const execute = React.useCallback(\n async (payload: TPayload): Promise<TResponse | null> => {\n if (!socket || !isConnected) {\n const errorMsg = \"Socket not connected\";\n setError(errorMsg);\n optionsRef.current?.onError?.(errorMsg);\n return null;\n }\n\n setLoading(true);\n setError(null);\n\n return new Promise<TResponse | null>((resolve) => {\n const eventName = `${serviceName}:${methodName}`;\n const timeout = setTimeout(() => {\n const errorMsg = \"Request timeout\";\n setError(errorMsg);\n setLoading(false);\n optionsRef.current?.onError?.(errorMsg);\n resolve(null);\n }, options?.timeout ?? 10000);\n\n socket.emit(eventName, payload, (response: ServiceResponse<TResponse>) => {\n clearTimeout(timeout);\n setLoading(false);\n\n if (response.success) {\n setError(null);\n setData(response.data);\n optionsRef.current?.onSuccess?.(response.data);\n resolve(response.data);\n } else {\n setError(response.error);\n optionsRef.current?.onError?.(response.error);\n resolve(null);\n }\n });\n });\n },\n [socket, isConnected, serviceName, methodName, options?.timeout],\n );\n\n // Memoize return object to prevent new references on every render\n return React.useMemo(\n () => ({\n execute,\n loading,\n error,\n data,\n isReady: !!socket && isConnected,\n }),\n [execute, loading, error, data, socket, isConnected],\n );\n}\n","/**\n * Error thrown by client hooks when a service call acks with\n * `{ success: false }`. Carries the server's optional numeric code\n * (e.g. 401, 403, 429) so callers can branch without string matching.\n */\nexport class ServiceCallError extends Error {\n public readonly code?: number;\n\n constructor(message: string, code?: number) {\n super(message);\n this.name = \"ServiceCallError\";\n this.code = code;\n }\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport type { ServiceResponse } from \"../shared/types\";\nimport { useQuickdrawSocket } from \"./QuickdrawProvider\";\nimport { ServiceCallError } from \"./serviceError\";\nimport type { UseServiceQueryOptions, UseServiceQueryResult } from \"./types\";\n\n/**\n * Hook for querying service methods via Socket.io with TanStack Query integration.\n *\n * Use this hook for **read operations** that benefit from caching and deduplication.\n * For mutations (create/update/delete), use `useService` instead.\n *\n * Features:\n * - Automatic caching with configurable stale time\n * - Request deduplication (multiple components calling same query share one request)\n * - Background refetching when data becomes stale\n * - Automatic refetch on reconnect\n *\n * @typeParam TPayload - The payload type for the method\n * @typeParam TResponse - The response type for the method\n *\n * @example\n * ```tsx\n * // Simple usage - auto-fetches on mount\n * const { data: expenses, isLoading } = useServiceQuery(\n * 'expenseService',\n * 'listExpenses',\n * { pageSize: 100 }\n * );\n *\n * // With options - conditional fetching\n * const { data, refetch } = useServiceQuery(\n * 'expenseService',\n * 'listExpenses',\n * { accountId: selectedAccount },\n * {\n * enabled: !!userId,\n * staleTime: 5 * 60 * 1000, // 5 minutes\n * }\n * );\n *\n * // Force fresh data (bypass cache)\n * const { data } = useServiceQuery(\n * 'expenseService',\n * 'listExpenses',\n * payload,\n * { skipCache: true }\n * );\n * ```\n */\nexport function useServiceQuery<TPayload = unknown, TResponse = unknown>(\n serviceName: string,\n methodName: string,\n payload: TPayload,\n options?: UseServiceQueryOptions<TResponse>,\n): UseServiceQueryResult<TResponse> {\n const { socket, isConnected, isRateLimited, reportRateLimited } = useQuickdrawSocket();\n const queryClient = useQueryClient();\n const [error, setError] = React.useState<string | null>(null);\n\n // Store options in ref to avoid unnecessary effect reruns\n const optionsRef = React.useRef(options);\n React.useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n // Destructure options with defaults\n const {\n enabled = true,\n staleTime = 5 * 60 * 1000, // 5 minutes default\n gcTime = 10 * 60 * 1000, // 10 minutes default\n refetchOnMount = true,\n refetchOnWindowFocus = false,\n skipCache = false,\n timeout = 10000,\n retry = 1,\n retryDelay,\n invalidateOn,\n refetchInterval,\n refetchIntervalInBackground,\n } = options ?? {};\n\n // Create stable query key based on service, method, and payload\n // Using JSON.stringify ensures same payload = same cache entry\n const queryKey = React.useMemo(\n () => [serviceName, methodName, JSON.stringify(payload)],\n [serviceName, methodName, payload],\n );\n\n // Query function that calls the service via socket\n const queryFn = React.useCallback(async (): Promise<TResponse> => {\n if (!socket || !isConnected) {\n throw new Error(\"Socket not connected\");\n }\n\n return new Promise<TResponse>((resolve, reject) => {\n const eventName = `${serviceName}:${methodName}`;\n const timeoutId = setTimeout(() => {\n reject(new Error(\"Request timeout\"));\n }, timeout);\n\n socket.emit(eventName, payload, (response: ServiceResponse<TResponse>) => {\n clearTimeout(timeoutId);\n\n if (response.success) {\n resolve(response.data);\n } else {\n // A 429 means the limiter is tripped — pause all reads, because\n // rejected events still count against the budget and would\n // re-trip it indefinitely.\n if (response.code === 429) {\n reportRateLimited();\n }\n reject(new ServiceCallError(response.error, response.code));\n }\n });\n });\n }, [socket, isConnected, serviceName, methodName, payload, timeout, reportRateLimited]);\n\n // Handle skipCache by invalidating before query runs\n React.useEffect(() => {\n if (skipCache && enabled) {\n queryClient.invalidateQueries({ queryKey });\n }\n }, [skipCache, enabled, queryClient, queryKey]);\n\n // Stable key for the invalidateOn array so the effect only re-runs\n // when the actual event names change, not on every render.\n const invalidateOnKey = invalidateOn?.join(\"\\0\");\n const stableInvalidateOn = React.useMemo(\n () => invalidateOn,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [invalidateOnKey],\n );\n\n // Listen for socket events that should trigger a refetch.\n // Debounces rapid-fire events within 100ms into a single invalidation.\n React.useEffect(() => {\n if (!socket || !isConnected || !stableInvalidateOn?.length || !enabled) {\n return;\n }\n\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n const handleInvalidate = () => {\n if (debounceTimer) return;\n debounceTimer = setTimeout(() => {\n debounceTimer = null;\n queryClient.invalidateQueries({ queryKey });\n }, 100);\n };\n\n for (const eventName of stableInvalidateOn) {\n socket.on(eventName, handleInvalidate);\n }\n\n return () => {\n if (debounceTimer) clearTimeout(debounceTimer);\n for (const eventName of stableInvalidateOn) {\n socket.off(eventName, handleInvalidate);\n }\n };\n }, [socket, isConnected, stableInvalidateOn, enabled, queryClient, queryKey]);\n\n const query = useQuery<TResponse, Error>({\n queryKey,\n queryFn,\n // Reads pause while the server reports us rate-limited; they resume\n // (and refetch as needed) when the backoff window elapses.\n enabled: enabled && !!socket && isConnected && !isRateLimited,\n staleTime,\n gcTime,\n refetchOnMount,\n refetchOnWindowFocus,\n retry,\n retryDelay,\n refetchInterval,\n refetchIntervalInBackground,\n });\n\n // Handle success/error callbacks\n React.useEffect(() => {\n if (query.isSuccess && query.data !== undefined) {\n setError(null);\n optionsRef.current?.onSuccess?.(query.data);\n }\n }, [query.isSuccess, query.data]);\n\n React.useEffect(() => {\n if (query.isError && query.error) {\n const errorMessage = query.error.message;\n setError(errorMessage);\n optionsRef.current?.onError?.(errorMessage);\n }\n }, [query.isError, query.error]);\n\n // Create stable refetch function\n const refetch = React.useCallback(async (): Promise<TResponse | undefined> => {\n const result = await query.refetch();\n return result.data;\n }, [query]);\n\n // Memoize return object to prevent new references on every render\n return React.useMemo(\n () => ({\n data: query.data,\n isLoading: query.isLoading,\n isFetching: query.isFetching,\n isError: query.isError,\n error,\n isStale: query.isStale,\n isSuccess: query.isSuccess,\n refetch,\n }),\n [\n query.data,\n query.isLoading,\n query.isFetching,\n query.isError,\n query.isStale,\n query.isSuccess,\n error,\n refetch,\n ],\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport type { ServiceResponse } from \"../shared/types\";\nimport { useQuickdrawSocket } from \"./QuickdrawProvider\";\nimport type { UseSubscriptionOptions, UseSubscriptionResult } from \"./types\";\n\n/**\n * Hook for subscribing to real-time entity updates with TanStack Query integration.\n *\n * Features:\n * - Automatic subscription management\n * - TanStack Query cache integration for optimistic updates\n * - Subscription deduplication across components (via context-based registry)\n * - Automatic reconnection handling\n * - HMR/Fast Refresh safe (no module-level state)\n *\n * @typeParam TData - The entity type\n *\n * @example\n * ```tsx\n * function ChatView({ chatId }: { chatId: string }) {\n * const { data: chat, isLoading, error } = useSubscription<Chat>(\n * 'chatService',\n * chatId,\n * {\n * onData: (chat) => {\n * console.log('Chat updated:', chat.title);\n * },\n * }\n * );\n *\n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error}</div>;\n * if (!chat) return <div>Chat not found</div>;\n *\n * return <div>{chat.title}</div>;\n * }\n * ```\n */\nexport function useSubscription<TData extends { id: string }>(\n serviceName: string,\n entryId: string | null,\n options: UseSubscriptionOptions<TData> = {},\n): UseSubscriptionResult<TData> {\n const { socket, isConnected, subscriptionRegistry, subscriptionBatcher } = useQuickdrawSocket();\n const queryClient = useQueryClient();\n const [isSubscribed, setIsSubscribed] = React.useState(false);\n const [error, setError] = React.useState<string | null>(null);\n\n const {\n enabled = true,\n onData,\n onError,\n requiredLevel = \"Read\",\n staleTime = Infinity, // Subscriptions should stay fresh via socket updates\n refetchOnWindowFocus = false,\n } = options;\n\n // Subscription key for deduplication\n const subscriptionKey = entryId ? `${serviceName}:${entryId}` : null;\n const queryKey = React.useMemo(\n () => [serviceName, \"subscription\", entryId],\n [serviceName, entryId],\n );\n\n // Store callbacks in refs to avoid unnecessary effect reruns\n const onDataRef = React.useRef(onData);\n const onErrorRef = React.useRef(onError);\n React.useEffect(() => {\n onDataRef.current = onData;\n onErrorRef.current = onError;\n }, [onData, onError]);\n\n // Query for cache management - enabled for reactivity to setQueryData calls\n // Data comes from socket subscriptions, not from queryFn\n const query = useQuery<TData | null>({\n queryKey,\n queryFn: () => queryClient.getQueryData<TData | null>(queryKey) ?? null,\n staleTime,\n refetchOnMount: false,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n // Query is enabled so components react to setQueryData updates from other subscribers\n });\n\n // Handle subscription lifecycle\n React.useEffect(() => {\n if (!socket || !isConnected || !subscriptionKey || !enabled || !entryId) {\n return;\n }\n\n // Try to acquire subscription (with deduplication)\n const { isNew } = subscriptionRegistry.acquire(subscriptionKey);\n\n if (!isNew) {\n // Already subscribed by another component, just track ref count\n setIsSubscribed(true);\n return () => {\n subscriptionRegistry.release(subscriptionKey);\n };\n }\n\n // Create new subscription\n const updateEvent = `${serviceName}:update:${entryId}`;\n\n // Handle incoming updates\n const handleUpdate = (updateData: Partial<TData>) => {\n // Check for deletion\n const asAny = updateData as { deleted?: boolean };\n if (asAny.deleted) {\n queryClient.setQueryData<TData | null>(queryKey, null);\n return;\n }\n\n // Merge update with existing data\n queryClient.setQueryData<TData | null>(queryKey, (oldData) => {\n if (!oldData) {\n return updateData as TData;\n }\n const merged = { ...oldData, ...updateData } as TData;\n onDataRef.current?.(merged);\n return merged;\n });\n };\n\n // Subscribe to updates\n socket.on(updateEvent, handleUpdate);\n\n // Use batcher to collect subscribe requests within the same microtask\n const handleBatchResponse = (response: {\n success: boolean;\n data?: Record<string, unknown>;\n error?: string;\n }) => {\n if (response.success && response.data) {\n queryClient.setQueryData<TData | null>(queryKey, response.data as TData);\n setIsSubscribed(true);\n setError(null);\n onDataRef.current?.(response.data as TData);\n } else if (!response.success) {\n setError(response.error ?? \"Subscription failed\");\n setIsSubscribed(false);\n onErrorRef.current?.(response.error ?? \"Subscription failed\");\n }\n };\n\n subscriptionBatcher.enqueue(serviceName, entryId, requiredLevel, handleBatchResponse);\n\n // Visibility-based re-fetch: when the tab becomes visible, re-subscribe\n // through the batcher to catch any updates missed while hidden.\n // Debounced to coalesce rapid visibility toggles.\n // Inert in non-DOM runtimes (React Native, workers): there is no\n // `document` to observe, and reconnect re-subscription already covers\n // the app-resume case there.\n const listenForVisibility = refetchOnWindowFocus && typeof document !== \"undefined\";\n let visibilityDebounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n const handleVisibilityChange = () => {\n if (document.visibilityState !== \"visible\") return;\n\n if (visibilityDebounceTimer) clearTimeout(visibilityDebounceTimer);\n visibilityDebounceTimer = setTimeout(() => {\n visibilityDebounceTimer = null;\n subscriptionBatcher.enqueue(serviceName, entryId, requiredLevel, handleBatchResponse);\n }, 200);\n };\n\n if (listenForVisibility) {\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\n }\n\n // Set cleanup function in registry\n const cleanup = () => {\n socket.off(updateEvent, handleUpdate);\n socket.emit(`${serviceName}:unsubscribe`, { entryId });\n if (visibilityDebounceTimer) clearTimeout(visibilityDebounceTimer);\n if (listenForVisibility) {\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\n }\n setIsSubscribed(false);\n };\n\n subscriptionRegistry.setCleanup(subscriptionKey, cleanup);\n\n return () => {\n subscriptionRegistry.release(subscriptionKey);\n };\n }, [\n socket,\n isConnected,\n subscriptionKey,\n enabled,\n entryId,\n serviceName,\n requiredLevel,\n queryClient,\n queryKey,\n subscriptionRegistry,\n subscriptionBatcher,\n refetchOnWindowFocus,\n ]);\n\n // Manual subscribe/unsubscribe functions\n const subscribe = React.useCallback(() => {\n if (!socket || !isConnected || !entryId) return;\n\n socket.emit(\n `${serviceName}:subscribe`,\n { entryId, requiredLevel },\n (response: ServiceResponse<TData>) => {\n if (response.success && response.data) {\n queryClient.setQueryData<TData | null>(queryKey, response.data);\n setIsSubscribed(true);\n setError(null);\n } else if (!response.success) {\n setError(response.error);\n }\n },\n );\n }, [socket, isConnected, entryId, serviceName, requiredLevel, queryClient, queryKey]);\n\n const unsubscribe = React.useCallback(() => {\n if (!socket || !entryId) return;\n\n socket.emit(`${serviceName}:unsubscribe`, { entryId });\n setIsSubscribed(false);\n }, [socket, entryId, serviceName]);\n\n // Get data from query (reactive - re-renders when setQueryData is called)\n const data = query.data ?? null;\n\n // Check if we're waiting for the socket to connect before we can subscribe\n const isWaitingForConnection = enabled && !!entryId && (!socket || !isConnected);\n\n return {\n data,\n isLoading: query.isLoading || (!data && isSubscribed) || (!data && isWaitingForConnection),\n isError: !!error,\n error,\n isSubscribed,\n subscribe,\n unsubscribe,\n };\n}\n","import type { z } from \"zod\";\n\n// ============================================================================\n// Access Control Types\n// ============================================================================\n\nexport type AccessLevel = \"Public\" | \"Read\" | \"Moderate\" | \"Admin\";\n\nexport type ACE = {\n userId: string;\n level: AccessLevel;\n};\n\nexport type ACL = ACE[];\n\n// ============================================================================\n// Entity and User Interfaces for ACL\n// ============================================================================\n\n/**\n * Interface for entities that support entry-level ACL stored as JSON.\n *\n * Use this pattern when:\n * - You want simple ACL without a separate membership table\n * - Access patterns are straightforward (read/write permissions)\n * - You don't need to query \"all entities user X can access\" efficiently\n *\n * The default `checkEntryACL` in BaseService expects entities to have this shape.\n *\n * @example\n * ```prisma\n * model Document {\n * id String @id @default(cuid())\n * title String\n * acl Json? // Stores ACL array\n * }\n * ```\n *\n * @example\n * ```typescript\n * class DocumentService extends BaseService<Document & ACLEntity, ...> {\n * constructor(prisma: PrismaClient) {\n * super({ serviceName: 'documentService', hasEntryACL: true });\n * this.setDelegate(prisma.document);\n * // Default checkEntryACL will work automatically\n * }\n * }\n * ```\n */\nexport interface ACLEntity {\n id: string;\n acl?: ACL | null;\n}\n\n/**\n * Interface for users with service-level access control.\n *\n * The `serviceAccess` field maps service names to access levels, granting\n * blanket permissions across all entries in that service.\n *\n * @example\n * ```prisma\n * model User {\n * id String @id @default(cuid())\n * serviceAccess Json? // { \"chatService\": \"Admin\", \"documentService\": \"Read\" }\n * }\n * ```\n *\n * @example\n * ```typescript\n * // User with Admin access to chatService can perform any operation\n * // on any chat, regardless of entry-level ACL\n * const user: QuickdrawUser = {\n * id: \"user-123\",\n * serviceAccess: { chatService: \"Admin\" }\n * };\n * ```\n */\nexport interface QuickdrawUser {\n id: string;\n serviceAccess?: Record<string, AccessLevel> | null;\n}\n\n// ============================================================================\n// Service Response Types\n// ============================================================================\n\nexport type ServiceResponse<T = unknown> =\n | { success: true; data: T }\n | { success: false; error: string; code?: number };\n\n// ============================================================================\n// Service Method Definition Types\n// ============================================================================\n\n/**\n * Defines the shape of a service method including its payload and response types.\n * Used to create a type-safe contract between server and client.\n */\nexport interface ServiceMethodDefinition<TPayload = unknown, TResponse = unknown> {\n name: string;\n access: AccessLevel;\n handler: (payload: TPayload, context: ServiceMethodContext) => Promise<TResponse>;\n schema?: z.ZodType<TPayload>;\n resolveEntryId?: (payload: TPayload) => string | null;\n}\n\n/**\n * Context provided to service method handlers\n */\nexport interface ServiceMethodContext {\n userId: string | undefined;\n socketId: string;\n serviceAccess: Record<string, AccessLevel>;\n}\n\n/**\n * Type helper for defining service method maps.\n * Each service should define its methods using this structure.\n *\n * @example\n * ```typescript\n * type ChatServiceMethods = ServiceMethodMap<{\n * createChat: {\n * payload: { title: string };\n * response: { id: string };\n * };\n * updateTitle: {\n * payload: { id: string; title: string };\n * response: { id: string; title: string };\n * };\n * }>;\n * ```\n */\nexport type ServiceMethodMap<T extends Record<string, { payload: unknown; response: unknown }>> = T;\n\n// ============================================================================\n// Service Channel Types (high-frequency, fire-and-forget events)\n// ============================================================================\n\n/**\n * Channels are the high-frequency counterpart to service methods.\n *\n * Where methods are request/response (ack'd, ACL-checked against the database,\n * counted by the global rate limiter), channels are fire-and-forget: no ack,\n * no response, validated and access-checked entirely in memory, and governed\n * by a per-channel token bucket instead of the global limiter. Use them for\n * traffic where per-message overhead matters and losing a message is fine —\n * game input, cursor positions, typing indicators, telemetry.\n *\n * Wire format: channels route as the Socket.io event\n * `channel:<serviceName>:<channelName>` (see {@link channelEventName}).\n * Clients should emit them volatile; servers broadcast tick data back with\n * `emitToRoomVolatile` so backpressured clients drop frames instead of\n * queueing them.\n *\n * Access model (all checks synchronous, zero DB reads):\n * - Authentication is always required — anonymous sockets are dropped silently.\n * - `\"Public\"` / `\"Read\"` access: any authenticated user passes the service gate.\n * - `\"Moderate\"` / `\"Admin\"`: requires that level in the socket's in-memory\n * `serviceAccess`.\n * - Entry-level access is expressed via `requireRoom`: the payload resolves to\n * a room name and the socket must already be in that room. Room membership\n * was ACL-checked at subscribe time, so this inherits full entry ACL\n * semantics without touching the database on the hot path.\n */\nexport interface ServiceChannelDefinition<TPayload = unknown> {\n name: string;\n access: AccessLevel;\n handler: (payload: TPayload, context: ServiceChannelContext) => void;\n /** Required — channel payloads are untrusted high-frequency input. */\n schema: z.ZodType<TPayload>;\n /** Sustained messages per second allowed per socket. */\n ratePerSecond: number;\n /** Bucket capacity — short bursts above ratePerSecond up to this size. */\n burst: number;\n /**\n * Resolve a room the socket must already be a member of (typically the\n * service room joined via subscribe). Return null to skip the check.\n */\n requireRoom?: (payload: TPayload) => string | null;\n}\n\n/**\n * Context provided to channel handlers. Unlike ServiceMethodContext,\n * userId is always present — unauthenticated channel messages are dropped\n * before the handler runs.\n */\nexport interface ServiceChannelContext {\n userId: string;\n socketId: string;\n serviceAccess: Record<string, AccessLevel>;\n}\n\n/**\n * Type helper for defining service channel maps (payload-only — channels\n * have no response).\n *\n * @example\n * ```typescript\n * type GameServiceChannels = ServiceChannelMap<{\n * input: { seq: number; dx: number; dy: number; boost: boolean };\n * }>;\n * ```\n */\nexport type ServiceChannelMap<T extends Record<string, unknown>> = T;\n\n/**\n * The Socket.io event name a channel routes as.\n * Shared by the server registry, the React client, and non-JS clients\n * (e.g. the Godot addon) so the wire contract lives in one place.\n */\nexport function channelEventName(serviceName: string, channelName: string): string {\n return `channel:${serviceName}:${channelName}`;\n}\n\n/**\n * Prefix for all channel events — pass to the rate limiter's\n * `excludePrefixes` so channel traffic bypasses the global request limiter\n * (channels enforce their own per-socket token buckets).\n */\nexport const CHANNEL_EVENT_PREFIX = \"channel:\";\n\n// ============================================================================\n// Admin Method Types (Generic shapes for all services)\n// ============================================================================\n// One canonical shape per admin method, matching what BaseService actually\n// sends and receives on the wire. (Before 4.0 these were declared twice —\n// here and in server/types.ts — with different shapes; the server shapes won.)\n\nexport type AdminListPayload = {\n page?: number;\n pageSize?: number;\n where?: Record<string, unknown>;\n orderBy?: Record<string, \"asc\" | \"desc\">;\n};\n\nexport type AdminListResponse<T> = {\n items: T[];\n total: number;\n page: number;\n pageSize: number;\n totalPages: number;\n};\n\nexport type AdminGetPayload = { id: string };\n\nexport type AdminCreatePayload<TCreate> = { data: TCreate };\n\nexport type AdminUpdatePayload<TUpdate> = {\n id: string;\n data: TUpdate;\n};\n\nexport type AdminDeletePayload = { id: string };\nexport type AdminDeleteResponse = { success: boolean; id: string };\n\nexport type AdminSetACLPayload = {\n entryId: string;\n acl: ACL;\n};\n\nexport type AdminGetSubscribersPayload = { entryId: string };\nexport type AdminSubscribersResponse = {\n entryId: string;\n subscribers: Array<{ socketId: string; userId: string | null }>;\n count: number;\n};\n\nexport type AdminReemitPayload = { entryId: string };\nexport type AdminReemitResponse = { success: boolean; subscriberCount: number };\n\nexport type AdminUnsubscribeAllPayload = { entryId: string };\nexport type AdminUnsubscribeAllResponse = { success: boolean; unsubscribedCount: number };\n\n// ============================================================================\n// Admin Field Configuration Types\n// ============================================================================\n\n/**\n * Supported field types for admin UI generation.\n */\nexport type AdminFieldType =\n | \"string\"\n | \"number\"\n | \"boolean\"\n | \"date\"\n | \"enum\"\n | \"json\"\n | \"relation\";\n\n/**\n * Configuration for a single field in the admin UI.\n * Derived from Zod schemas with optional overrides.\n */\nexport interface AdminFieldConfig {\n /** Field name (property key on the entity) */\n name: string;\n /** Field type for rendering appropriate input */\n type: AdminFieldType;\n /** Human-readable label */\n label: string;\n /** Whether the field is required for creation */\n required: boolean;\n /** Whether the field can be edited (false for id, createdAt, etc.) */\n editable: boolean;\n /** Whether to show this field as a table column */\n showInTable: boolean;\n /** Whether the table can be sorted by this field */\n sortable: boolean;\n /** For enum fields, the allowed values */\n enumValues?: string[];\n /** For relation fields, the related service name */\n relationService?: string;\n}\n\n/**\n * Service metadata for admin UI generation.\n * Returned by the adminMeta method.\n */\nexport interface AdminServiceMeta {\n /** Internal service name (e.g., \"chatService\") */\n serviceName: string;\n /** Human-readable display name (e.g., \"Chats\") */\n displayName: string;\n /** Field configurations derived from schema */\n fields: AdminFieldConfig[];\n}\n\n/**\n * Payload for adminMeta method (no parameters needed)\n */\nexport type AdminMetaPayload = Record<string, never>;\n\n/**\n * Response type for adminMeta method\n */\nexport type AdminMetaResponse = AdminServiceMeta;\n\n// ============================================================================\n// Subscription Types\n// ============================================================================\n\nexport type SubscribePayload = {\n entryId: string;\n requiredLevel?: AccessLevel;\n};\n\nexport type UnsubscribePayload = {\n entryId: string;\n};\n\n// ============================================================================\n// Room Naming (shared between server emitters and client listeners)\n// ============================================================================\n\n/**\n * The Socket.io room for an entity subscription: `{serviceName}:{entryId}`.\n *\n * Static counterpart to `BaseService.getRoomName` — usable from shared\n * modules and when emitting into *another* service's room without holding\n * its instance.\n */\nexport function serviceRoom(serviceName: string, entryId: string): string {\n return `${serviceName}:${entryId}`;\n}\n\n/**\n * The room subscribers of an elevated (unfiltered) entity tier join in\n * addition to {@link serviceRoom}. Emission targets it via\n * `io.to(fullRoom)` + `io.to(room).except(fullRoom)`.\n */\nexport function serviceFullRoom(serviceName: string, entryId: string): string {\n return `${serviceRoom(serviceName, entryId)}:full`;\n}\n\n/**\n * The room for a collection scope:\n * `{serviceName}:collection:{collection}:{scopeId}`.\n * Entity ids are cuids, so this never collides with {@link serviceRoom}.\n */\nexport function collectionRoom(serviceName: string, collection: string, scopeId: string): string {\n return `${serviceName}:collection:${collection}:${scopeId}`;\n}\n\n/**\n * The event name collection deltas are emitted as. Identical to the room\n * name by design — one event per scope room, no id parsing client-side.\n */\nexport function collectionEventName(\n serviceName: string,\n collection: string,\n scopeId: string,\n): string {\n return collectionRoom(serviceName, collection, scopeId);\n}\n\n/**\n * The per-user room every authenticated socket joins on connection:\n * `user:{userId}`. Used for targeted notifications and adapter-safe\n * revocation (`kickFromCollection`).\n */\nexport function userRoom(userId: string): string {\n return `user:${userId}`;\n}\n\n// ============================================================================\n// Typed Room Events\n// ============================================================================\n\n/**\n * Augmentable map of custom room event names to their payload types.\n * Empty by default — `emitToRoom` and `useRoomEvents` degrade gracefully to\n * `string` names and `unknown` payloads until an app augments it:\n *\n * @example\n * ```typescript\n * // In your app's shared types\n * declare module \"@fitzzero/quickdraw-core\" {\n * interface QuickdrawEventMap {\n * \"chat:memberUpdate\": { chatId: string; userId: string };\n * \"presence:changed\": { userId: string; online: boolean };\n * }\n * }\n * ```\n *\n * Collection deltas and `{service}:update:{id}` events need no entries here —\n * they are framework-generated and typed end-to-end by the service generics.\n */\n// oxlint-disable-next-line typescript/no-empty-interface, typescript/no-empty-object-type\nexport interface QuickdrawEventMap {}\n\n/**\n * Event names accepted by `emitToRoom`/`useRoomEvents`: keys of the\n * augmented {@link QuickdrawEventMap} (with autocomplete) plus any string.\n */\nexport type QuickdrawEventName =\n | (keyof QuickdrawEventMap & string)\n | (string & Record<never, never>);\n\n/**\n * Payload type for a room event: the mapped type when the name is in\n * {@link QuickdrawEventMap}, `unknown` otherwise.\n */\nexport type QuickdrawEventData<E extends string> = E extends keyof QuickdrawEventMap\n ? QuickdrawEventMap[E]\n : unknown;\n\n// ============================================================================\n// Collection Subscription Types (wire protocol)\n// ============================================================================\n\n/**\n * One page of a collection snapshot, returned by a collection's `snapshot`\n * query and by the `{service}:collection:subscribe` ack.\n */\nexport interface CollectionSnapshotPage<TItem> {\n items: TItem[];\n nextCursor: string | null;\n totalCount: number;\n /**\n * Full membership id list for the scope (ids only, cheap select).\n * Returned on cursor-less (first-page / re-snapshot) requests so clients\n * can prune rows deleted while disconnected. Omit for unbounded scopes\n * (e.g. long chat histories) — the client then merges without pruning.\n */\n ids?: string[];\n /**\n * Set by the server when the scope's id list exceeded the cap (5,000) and\n * `ids` was dropped — the client degrades to no-prune merges.\n */\n idsTruncated?: boolean;\n}\n\n/** Snapshot page plus the revision captured before the query ran. */\nexport type CollectionSnapshotResponse<TItem> = CollectionSnapshotPage<TItem> & { rev: number };\n\n/**\n * Payload for `{service}:collection:subscribe`. Cursor-less calls join the\n * scope room and may include `ids`; cursor-bearing calls are pure paging —\n * no room join, no `ids`.\n */\nexport type CollectionSubscribePayload = {\n collection: string;\n scopeId: string;\n cursor?: string | null;\n limit?: number;\n};\n\n/** Payload for `{service}:collection:unsubscribe`. */\nexport type CollectionUnsubscribePayload = {\n collection: string;\n scopeId: string;\n};\n\n/**\n * A collection change, emitted to the scope room under the event name\n * {@link collectionEventName}.\n *\n * `rev` is per-item last-writer-wins: clients ignore an `added`/`updated`\n * older than what they hold for that id, and snapshot items only overwrite\n * cached items whose rev predates the snapshot rev. Revisions are epoch\n * milliseconds (`Date.now()` at emit, or the collection's `revOf`).\n */\nexport type CollectionDelta<TItem extends { id: string }> =\n | { type: \"added\"; item: TItem; rev: number }\n | { type: \"updated\"; item: TItem; rev: number }\n | { type: \"removed\"; id: string; rev: number }\n | { type: \"reset\"; rev: number };\n\n// ============================================================================\n// Utility Types\n// ============================================================================\n\n/**\n * Extracts payload type from a service method definition\n */\nexport type ExtractPayload<T> = T extends { payload: infer P } ? P : never;\n\n/**\n * Extracts response type from a service method definition\n */\nexport type ExtractResponse<T> = T extends { response: infer R } ? R : never;\n\n/**\n * Logger interface compatible with Winston and other loggers\n */\nexport interface Logger {\n info(message: string, meta?: Record<string, unknown>): void;\n warn(message: string, meta?: Record<string, unknown>): void;\n error(message: string, meta?: Record<string, unknown>): void;\n debug(message: string, meta?: Record<string, unknown>): void;\n child(options: Record<string, unknown>): Logger;\n}\n\n/**\n * Default console logger implementation\n */\nexport const consoleLogger: Logger = {\n info: (message, meta) => console.log(`[INFO] ${message}`, meta ?? \"\"),\n warn: (message, meta) => console.warn(`[WARN] ${message}`, meta ?? \"\"),\n error: (message, meta) => console.error(`[ERROR] ${message}`, meta ?? \"\"),\n debug: (message, meta) => console.debug(`[DEBUG] ${message}`, meta ?? \"\"),\n child: () => consoleLogger,\n};\n","import type { CollectionDelta, CollectionSnapshotResponse } from \"../shared/types\";\n\n/**\n * Pure merge logic for collection subscriptions — the correctness heart of\n * `useCollection`, kept free of React/socket dependencies so every rule is\n * unit-testable.\n *\n * Invariants:\n * - Everything is id-keyed; live deltas and paged history compose.\n * - Per-item last-writer-wins by `rev` (epoch ms). A delta older than what\n * the cache holds for that id is ignored; snapshot/page items only\n * overwrite cached items whose rev predates the response's rev.\n * - Removals leave a tombstone rev in `revById` so a stale `added` cannot\n * resurrect a deleted row; snapshots garbage-collect stale tombstones.\n */\nexport interface CollectionCacheEntry<TItem extends { id: string }> {\n byId: Record<string, TItem>;\n /** Insertion/server order; render order applies `compare` on top. */\n order: string[];\n /** Last-known rev per id — includes tombstones for removed ids. */\n revById: Record<string, number>;\n nextCursor: string | null;\n totalCount: number | null;\n /** Rev of the last cursor-less snapshot applied. */\n snapshotRev: number;\n}\n\nexport interface ApplyDeltaOptions {\n /** Where `added` lands when the id is new. Default \"end\". */\n insertPosition?: \"start\" | \"end\";\n}\n\nexport interface ApplyDeltaResult<TItem extends { id: string }> {\n entry: CollectionCacheEntry<TItem>;\n /** True when the delta was a `reset` — caller schedules a re-snapshot. */\n resetRequested: boolean;\n}\n\nexport function createEmptyEntry<TItem extends { id: string }>(): CollectionCacheEntry<TItem> {\n return {\n byId: {},\n order: [],\n revById: {},\n nextCursor: null,\n totalCount: null,\n snapshotRev: 0,\n };\n}\n\n/**\n * Apply a cursor-less snapshot (initial load or re-snapshot).\n *\n * - When `ids` is present it is the authoritative membership: cached items\n * absent from it are pruned — unless a live delta newer than the snapshot\n * put them there (`revById[id] > snapshot.rev`), in which case the live\n * push wins.\n * - Page items upsert unless the cache holds a newer rev for that id.\n * - Order: page order first (fresh server order), then surviving previously\n * known ids in their prior relative order (paged-in history stays).\n * - Tombstones older than the snapshot rev are garbage-collected; newer\n * ones keep suppressing their id (the removal happened after the\n * snapshot query ran).\n */\nexport function applySnapshot<TItem extends { id: string }>(\n prev: CollectionCacheEntry<TItem> | null | undefined,\n snapshot: CollectionSnapshotResponse<TItem>,\n): CollectionCacheEntry<TItem> {\n const base = prev ?? createEmptyEntry<TItem>();\n const pageIds = new Set(snapshot.items.map((item) => item.id));\n const memberIds = snapshot.ids ? new Set(snapshot.ids) : null;\n\n const byId: Record<string, TItem> = {};\n const revById: Record<string, number> = {};\n\n // Carry over surviving cached items\n for (const id of base.order) {\n const item = base.byId[id];\n if (!item) continue;\n const rev = base.revById[id] ?? 0;\n const liveWins = rev > snapshot.rev;\n const pruned = memberIds !== null && !memberIds.has(id) && !pageIds.has(id) && !liveWins;\n if (pruned) continue;\n byId[id] = item;\n revById[id] = rev;\n }\n\n // Upsert page items (cached rev newer than the snapshot wins)\n for (const item of snapshot.items) {\n const cachedRev = revById[item.id];\n if (cachedRev !== undefined && cachedRev > snapshot.rev) continue;\n // A tombstone newer than the snapshot suppresses resurrection\n const tombstoneRev = base.revById[item.id];\n if (\n base.byId[item.id] === undefined &&\n tombstoneRev !== undefined &&\n tombstoneRev > snapshot.rev\n ) {\n continue;\n }\n byId[item.id] = item;\n revById[item.id] = snapshot.rev;\n }\n\n // Keep tombstones that still out-rev the snapshot\n for (const [id, rev] of Object.entries(base.revById)) {\n if (byId[id] === undefined && rev > snapshot.rev) {\n revById[id] = rev;\n }\n }\n\n // Page order first, then surviving prior order\n const order: string[] = [];\n const seen = new Set<string>();\n for (const item of snapshot.items) {\n if (byId[item.id] !== undefined && !seen.has(item.id)) {\n order.push(item.id);\n seen.add(item.id);\n }\n }\n for (const id of base.order) {\n if (byId[id] !== undefined && !seen.has(id)) {\n order.push(id);\n seen.add(id);\n }\n }\n\n return {\n byId,\n order,\n revById,\n nextCursor: snapshot.nextCursor,\n totalCount: snapshot.totalCount,\n snapshotRev: snapshot.rev,\n };\n}\n\n/**\n * Apply a cursored page (loadMore): upsert-only, never prune. Unseen ids\n * append at the end in page order; existing ids keep their position (and\n * their item, when the cache holds a newer rev).\n */\nexport function applyPage<TItem extends { id: string }>(\n prev: CollectionCacheEntry<TItem> | null | undefined,\n page: CollectionSnapshotResponse<TItem>,\n): CollectionCacheEntry<TItem> {\n const base = prev ?? createEmptyEntry<TItem>();\n const byId = { ...base.byId };\n const revById = { ...base.revById };\n const order = [...base.order];\n const known = new Set(order);\n\n for (const item of page.items) {\n const cachedRev = revById[item.id];\n if (cachedRev !== undefined && cachedRev > page.rev) {\n // Cache holds a newer version (or a newer removal) — keep it\n continue;\n }\n byId[item.id] = item;\n revById[item.id] = page.rev;\n if (!known.has(item.id)) {\n order.push(item.id);\n known.add(item.id);\n }\n }\n\n return {\n byId,\n order,\n revById,\n nextCursor: page.nextCursor,\n totalCount: page.totalCount,\n snapshotRev: base.snapshotRev,\n };\n}\n\n/**\n * Apply a live delta. Returns the previous entry object unchanged when the\n * delta is stale (rev LWW) so referential equality can skip re-renders.\n */\nexport function applyDelta<TItem extends { id: string }>(\n prev: CollectionCacheEntry<TItem> | null | undefined,\n delta: CollectionDelta<TItem>,\n options?: ApplyDeltaOptions,\n): ApplyDeltaResult<TItem> {\n const base = prev ?? createEmptyEntry<TItem>();\n\n if (delta.type === \"reset\") {\n return { entry: base, resetRequested: true };\n }\n\n if (delta.type === \"removed\") {\n const cachedRev = base.revById[delta.id];\n if (cachedRev !== undefined && delta.rev < cachedRev) {\n return { entry: base, resetRequested: false };\n }\n const existed = base.byId[delta.id] !== undefined;\n const byId = { ...base.byId };\n delete byId[delta.id];\n return {\n entry: {\n ...base,\n byId,\n order: existed ? base.order.filter((id) => id !== delta.id) : base.order,\n // Tombstone: keep the removal rev so a stale `added` can't resurrect\n revById: { ...base.revById, [delta.id]: delta.rev },\n totalCount:\n existed && base.totalCount !== null ? Math.max(0, base.totalCount - 1) : base.totalCount,\n },\n resetRequested: false,\n };\n }\n\n // added / updated — upsert semantics; the distinction is cosmetic\n const { item, rev } = delta;\n const cachedRev = base.revById[item.id];\n if (cachedRev !== undefined && rev < cachedRev) {\n return { entry: base, resetRequested: false };\n }\n\n const isNew = base.byId[item.id] === undefined;\n const insertPosition = options?.insertPosition ?? \"end\";\n\n return {\n entry: {\n ...base,\n byId: { ...base.byId, [item.id]: item },\n order: isNew\n ? insertPosition === \"start\"\n ? [item.id, ...base.order]\n : [...base.order, item.id]\n : base.order,\n revById: { ...base.revById, [item.id]: rev },\n totalCount: isNew && base.totalCount !== null ? base.totalCount + 1 : base.totalCount,\n },\n resetRequested: false,\n };\n}\n\n/**\n * Apply a batch of buffered deltas in arrival order (used when deltas\n * raced an in-flight snapshot). `resetRequested` is true if any was a reset.\n */\nexport function applyDeltas<TItem extends { id: string }>(\n prev: CollectionCacheEntry<TItem> | null | undefined,\n deltas: CollectionDelta<TItem>[],\n options?: ApplyDeltaOptions,\n): ApplyDeltaResult<TItem> {\n let entry = prev ?? createEmptyEntry<TItem>();\n let resetRequested = false;\n for (const delta of deltas) {\n const result = applyDelta(entry, delta, options);\n entry = result.entry;\n resetRequested = resetRequested || result.resetRequested;\n }\n return { entry, resetRequested };\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport type { CollectionDelta, CollectionSnapshotResponse, ServiceResponse } from \"../shared/types\";\nimport { collectionRoom } from \"../shared/types\";\nimport {\n applyDeltas,\n applyDelta,\n applyPage,\n applySnapshot,\n type CollectionCacheEntry,\n} from \"./collectionCache\";\nimport { useQuickdrawSocket } from \"./QuickdrawProvider\";\nimport type { SubscriptionEntry, UseCollectionOptions, UseCollectionResult } from \"./types\";\n\nconst RESET_DEBOUNCE_MS = 100;\nconst REQUEST_TIMEOUT_MS = 10_000;\n\n/** Cache value under [service, \"collection\", name, scopeId]. */\ninterface CollectionQueryState<TItem extends { id: string }> {\n entry: CollectionCacheEntry<TItem> | null;\n error: string | null;\n}\n\nconst EMPTY_STATE: CollectionQueryState<{ id: string }> = { entry: null, error: null };\n\n/**\n * Shared per-subscription pipeline, stashed on the registry entry by the\n * owning component so every component on the same key drives one snapshot\n * and one loadMore at a time.\n */\ninterface CollectionController {\n requestSnapshot: () => Promise<void>;\n loadMore: () => Promise<void>;\n}\n\n/**\n * Hook for subscribing to a server-declared collection: a scope-keyed,\n * live-updating list (`defineCollection` server-side).\n *\n * The initial snapshot arrives via `{service}:collection:subscribe`;\n * `added`/`updated`/`removed` deltas stream to the scope room and merge\n * id-keyed with per-item last-writer-wins; `reset` deltas trigger a\n * debounced re-snapshot. Reconnects re-snapshot automatically (the same\n * registry cycle entity subscriptions use), pruning rows deleted while\n * offline when the server provides membership `ids`.\n *\n * Subscriptions are deduped across components by scope; pagination reuses\n * the subscribe event with a cursor via `loadMore`.\n *\n * @example\n * ```tsx\n * function ChatWindow({ chatId }: { chatId: string }) {\n * const { items: messages, loadMore, hasMore } = useCollection<MessageDTO>(\n * \"messageService\",\n * \"byChat\",\n * chatId,\n * { compare: (a, b) => a.createdAt.localeCompare(b.createdAt) },\n * );\n * // messages update live; loadMore() pages in older history\n * }\n * ```\n */\nexport function useCollection<TItem extends { id: string }>(\n serviceName: string,\n collection: string,\n scopeId: string | null,\n options?: UseCollectionOptions<TItem>,\n): UseCollectionResult<TItem> {\n const { socket, isConnected, subscriptionRegistry } = useQuickdrawSocket();\n const queryClient = useQueryClient();\n const [isLoadingMore, setIsLoadingMore] = React.useState(false);\n\n const {\n enabled = true,\n limit,\n compare,\n insertPosition = \"end\",\n onDelta,\n onError,\n } = options ?? {};\n\n // Callbacks and merge options read through refs so the owner closure and\n // per-component listeners never need re-subscribing on identity changes.\n const onDeltaRef = React.useRef(onDelta);\n const onErrorRef = React.useRef(onError);\n const insertPositionRef = React.useRef(insertPosition);\n React.useEffect(() => {\n onDeltaRef.current = onDelta;\n onErrorRef.current = onError;\n insertPositionRef.current = insertPosition;\n }, [onDelta, onError, insertPosition]);\n\n // Registry key doubles as the delta event name (== room name)\n const subscriptionKey = scopeId ? collectionRoom(serviceName, collection, scopeId) : null;\n const queryKey = React.useMemo(\n () => [serviceName, \"collection\", collection, scopeId],\n [serviceName, collection, scopeId],\n );\n\n // Reactive cache read — data is written via setQueryData by the owner.\n // initialData seeds the cache without an initial fetch, so a snapshot ack\n // landing between mount and a queryFn resolution can never be clobbered.\n const query = useQuery<CollectionQueryState<TItem>>({\n queryKey,\n queryFn: () =>\n queryClient.getQueryData<CollectionQueryState<TItem>>(queryKey) ??\n (EMPTY_STATE as CollectionQueryState<TItem>),\n initialData: () => EMPTY_STATE as CollectionQueryState<TItem>,\n staleTime: Infinity,\n refetchOnMount: false,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n });\n\n // The registry entry this component holds (owner or joiner) — the shared\n // controller for refresh/loadMore lives on it.\n const regEntryRef = React.useRef<SubscriptionEntry | null>(null);\n\n React.useEffect(() => {\n if (!socket || !isConnected || !enabled || !subscriptionKey || !scopeId) {\n return;\n }\n\n const { isNew, entry: regEntry } = subscriptionRegistry.acquire(subscriptionKey);\n regEntryRef.current = regEntry;\n\n if (!isNew) {\n return () => {\n regEntryRef.current = null;\n subscriptionRegistry.release(subscriptionKey);\n };\n }\n\n // ---- Owner path: this closure runs the shared pipeline for the key.\n // It stays alive as long as any component holds the subscription.\n const eventName = subscriptionKey;\n let disposed = false;\n let snapshotInFlight = false;\n let buffered: CollectionDelta<TItem>[] = [];\n let inFlightSnapshot: Promise<void> | null = null;\n let inFlightLoadMore: Promise<void> | null = null;\n let resetTimer: ReturnType<typeof setTimeout> | null = null;\n\n const readState = (): CollectionQueryState<TItem> =>\n queryClient.getQueryData<CollectionQueryState<TItem>>(queryKey) ??\n (EMPTY_STATE as CollectionQueryState<TItem>);\n\n const writeState = (state: CollectionQueryState<TItem>): void => {\n queryClient.setQueryData(queryKey, state);\n };\n\n const emitSubscribe = (\n cursor: string | null,\n ): Promise<ServiceResponse<CollectionSnapshotResponse<TItem>>> =>\n new Promise((resolve) => {\n let settled = false;\n const timeoutId = setTimeout(() => {\n if (!settled) {\n settled = true;\n resolve({ success: false, error: \"Request timeout\" });\n }\n }, REQUEST_TIMEOUT_MS);\n\n socket.emit(\n `${serviceName}:collection:subscribe`,\n {\n collection,\n scopeId,\n ...(cursor === null ? {} : { cursor }),\n ...(limit === undefined ? {} : { limit }),\n },\n (response: ServiceResponse<CollectionSnapshotResponse<TItem>>) => {\n if (settled) return;\n settled = true;\n clearTimeout(timeoutId);\n resolve(response);\n },\n );\n });\n\n const scheduleReset = (): void => {\n if (resetTimer || disposed) return;\n resetTimer = setTimeout(() => {\n resetTimer = null;\n void requestSnapshot();\n }, RESET_DEBOUNCE_MS);\n };\n\n const requestSnapshot = (): Promise<void> => {\n if (inFlightSnapshot) return inFlightSnapshot;\n snapshotInFlight = true;\n buffered = [];\n inFlightSnapshot = emitSubscribe(null).then((response) => {\n snapshotInFlight = false;\n inFlightSnapshot = null;\n const raced = buffered;\n buffered = [];\n if (disposed) return;\n\n if (response.success) {\n const prev = readState();\n const fresh = applySnapshot(prev.entry, response.data);\n // Deltas that raced the snapshot apply on top of it (rev LWW)\n const { entry, resetRequested } = applyDeltas(fresh, raced, {\n insertPosition: insertPositionRef.current,\n });\n writeState({ entry, error: null });\n if (resetRequested) scheduleReset();\n } else {\n // Keep stale data; surface the error\n const prev = readState();\n writeState({ entry: prev.entry, error: response.error });\n }\n });\n return inFlightSnapshot;\n };\n\n const loadMore = (): Promise<void> => {\n if (inFlightLoadMore) return inFlightLoadMore;\n const cursor = readState().entry?.nextCursor ?? null;\n if (cursor === null) return Promise.resolve();\n\n inFlightLoadMore = emitSubscribe(cursor).then((response) => {\n inFlightLoadMore = null;\n if (disposed) return;\n\n const prev = readState();\n if (response.success) {\n writeState({ entry: applyPage(prev.entry, response.data), error: prev.error });\n } else {\n writeState({ entry: prev.entry, error: response.error });\n }\n });\n return inFlightLoadMore;\n };\n\n const handleDelta = (delta: CollectionDelta<TItem>): void => {\n if (delta.type === \"reset\") {\n scheduleReset();\n return;\n }\n if (snapshotInFlight) {\n buffered.push(delta);\n return;\n }\n const prev = readState();\n const { entry } = applyDelta(prev.entry, delta, {\n insertPosition: insertPositionRef.current,\n });\n if (entry !== prev.entry) {\n writeState({ entry, error: prev.error });\n }\n };\n\n const controller: CollectionController = { requestSnapshot, loadMore };\n regEntry.controller = controller;\n\n socket.on(eventName, handleDelta);\n void requestSnapshot();\n\n subscriptionRegistry.setCleanup(subscriptionKey, () => {\n disposed = true;\n if (resetTimer) clearTimeout(resetTimer);\n socket.off(eventName, handleDelta);\n socket.emit(`${serviceName}:collection:unsubscribe`, { collection, scopeId });\n regEntry.controller = undefined;\n });\n\n return () => {\n regEntryRef.current = null;\n subscriptionRegistry.release(subscriptionKey);\n };\n }, [\n socket,\n isConnected,\n enabled,\n subscriptionKey,\n scopeId,\n serviceName,\n collection,\n limit,\n queryClient,\n queryKey,\n subscriptionRegistry,\n ]);\n\n // Per-component onDelta listener (cache application happens once, in the\n // owner). Attached only when the component asked for it.\n const wantsDelta = !!onDelta;\n React.useEffect(() => {\n if (!socket || !isConnected || !enabled || !subscriptionKey || !wantsDelta) {\n return;\n }\n const listener = (delta: CollectionDelta<TItem>): void => {\n onDeltaRef.current?.(delta);\n };\n socket.on(subscriptionKey, listener);\n return () => {\n socket.off(subscriptionKey, listener);\n };\n }, [socket, isConnected, enabled, subscriptionKey, wantsDelta]);\n\n const state = query.data ?? (EMPTY_STATE as CollectionQueryState<TItem>);\n const entry = state.entry;\n\n // Per-component error callback, fired on shared error transitions\n React.useEffect(() => {\n if (state.error) {\n onErrorRef.current?.(state.error);\n }\n }, [state.error]);\n\n const getController = (): CollectionController | null =>\n (regEntryRef.current?.controller as CollectionController | undefined) ?? null;\n const getControllerRef = React.useRef(getController);\n getControllerRef.current = getController;\n\n const refresh = React.useCallback(async (): Promise<void> => {\n await getControllerRef.current()?.requestSnapshot();\n }, []);\n\n const loadMore = React.useCallback(async (): Promise<void> => {\n const controller = getControllerRef.current();\n if (!controller) return;\n setIsLoadingMore(true);\n try {\n await controller.loadMore();\n } finally {\n setIsLoadingMore(false);\n }\n }, []);\n\n const items = React.useMemo(() => {\n if (!entry) return [] as TItem[];\n const list = entry.order\n .map((id) => entry.byId[id])\n .filter((item): item is TItem => item !== undefined);\n if (compare) list.sort(compare);\n return list;\n }, [entry, compare]);\n\n const byId = React.useMemo(() => {\n const map = new Map<string, TItem>();\n if (entry) {\n for (const id of entry.order) {\n const item = entry.byId[id];\n if (item !== undefined) map.set(id, item);\n }\n }\n return map as ReadonlyMap<string, TItem>;\n }, [entry]);\n\n const active = enabled && !!scopeId;\n\n return {\n items,\n byId,\n totalCount: entry?.totalCount ?? null,\n isLoading: active && !entry && !state.error,\n isError: state.error !== null,\n error: state.error,\n hasMore: (entry?.nextCursor ?? null) !== null,\n isLoadingMore,\n loadMore,\n refresh,\n };\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useQuickdrawSocket } from \"./QuickdrawProvider\";\nimport type { QuickdrawRoomEventHandlers, UseRoomEventsOptions } from \"./types\";\n\n/**\n * Hook for listening to custom socket events with managed lifecycle.\n *\n * Handles `socket.on`/`socket.off` cleanup, reconnection re-attachment,\n * and connection-awareness. Each component gets its own listeners (no dedup needed).\n * Room scoping is handled server-side via `emitToRoom`.\n *\n * Event names and payloads are typed via the augmentable `QuickdrawEventMap`\n * (from the package root): augmented names get payload checking and\n * autocomplete; unlisted names still work, untyped — nothing breaks if the\n * map stays empty.\n *\n * Pair with `useSubscription` to ensure the socket is in the correct room:\n * `useSubscription` manages room membership, `useRoomEvents` manages event listeners.\n *\n * @param handlers - Map of event names to handler functions. Event names should be\n * stable (don't change between renders). Handler functions can change freely.\n * @param options - Optional configuration\n *\n * @example\n * ```tsx\n * function ChatView({ chatId }: { chatId: string }) {\n * const [messages, setMessages] = useState<Message[]>([]);\n * const [typing, setTyping] = useState(false);\n *\n * // Subscribe to the chat room (manages room membership)\n * const { data: chat } = useSubscription('chatService', chatId);\n *\n * // Listen for custom events broadcast to the chat room\n * useRoomEvents({\n * 'chat:message': (msg: Message) => setMessages(prev => [...prev, msg]),\n * 'agent_typing_start': () => setTyping(true),\n * 'agent_typing_stop': () => setTyping(false),\n * });\n *\n * return <div>{chat?.title}</div>;\n * }\n * ```\n */\nexport function useRoomEvents(\n handlers: QuickdrawRoomEventHandlers,\n options?: UseRoomEventsOptions,\n): void {\n const { socket, isConnected } = useQuickdrawSocket();\n const { enabled = true } = options ?? {};\n\n // Store handlers in a ref so handler identity changes don't trigger effect reruns.\n // The event names (keys) are tracked separately for structural changes.\n const handlersRef = React.useRef(handlers);\n React.useEffect(() => {\n handlersRef.current = handlers;\n }, [handlers]);\n\n // Memoize the sorted event names so the effect only re-runs when the\n // set of listened events actually changes, not on every render.\n const eventNames = Object.keys(handlers).filter((name) => handlers[name] != null);\n const eventNamesKey = eventNames.join(\"\\0\");\n const stableEventNames = React.useMemo(\n () => eventNames,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [eventNamesKey],\n );\n\n React.useEffect(() => {\n if (!socket || !isConnected || !enabled || stableEventNames.length === 0) {\n return;\n }\n\n // Create stable wrapper functions that delegate to the ref'd handlers.\n // This lets consumers change handler implementations without re-subscribing.\n const wrappers = new Map<string, (...args: unknown[]) => void>();\n\n for (const eventName of stableEventNames) {\n const wrapper = (...args: unknown[]) => {\n handlersRef.current[eventName]?.(...(args as [never]));\n };\n wrappers.set(eventName, wrapper);\n socket.on(eventName, wrapper);\n }\n\n return () => {\n for (const [eventName, wrapper] of wrappers) {\n socket.off(eventName, wrapper);\n }\n };\n }, [socket, isConnected, enabled, stableEventNames]);\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { channelEventName } from \"../shared/types\";\nimport { useQuickdrawSocket } from \"./QuickdrawProvider\";\nimport type { UseChannelSendResult } from \"./types\";\n\n/**\n * Hook for sending fire-and-forget channel messages (the high-frequency\n * counterpart to useService).\n *\n * Channel messages are emitted volatile: if the connection is backpressured,\n * the message is dropped instead of queued — the right behavior for input\n * streams where the next message supersedes the last. There is no ack and no\n * response; the server validates, rate-limits (per-channel token bucket), and\n * access-checks each message, silently dropping any that fail.\n *\n * Receiving server broadcasts needs nothing new: volatile room emits arrive\n * as ordinary events, so pair this with `useRoomEvents` (and `useSubscription`\n * for room membership).\n *\n * @typeParam TPayload - The channel payload type (from the service's channel map)\n *\n * @example\n * ```tsx\n * function GameInput({ worldId }: { worldId: string }) {\n * // Room membership (ACL-checked server-side) gates the channel\n * useSubscription(\"gameService\", worldId);\n * const { send, isReady } = useChannelSend<GameInput>(\"gameService\", \"input\");\n *\n * // e.g. called from a 20Hz game loop\n * const onTick = (input: GameInput) => send(input);\n * }\n * ```\n */\nexport function useChannelSend<TPayload = unknown>(\n serviceName: string,\n channelName: string,\n): UseChannelSendResult<TPayload> {\n const { socket, isConnected } = useQuickdrawSocket();\n\n const eventName = channelEventName(serviceName, channelName);\n\n const send = React.useCallback(\n (payload: TPayload) => {\n if (!socket?.connected) return;\n socket.volatile.emit(eventName, payload);\n },\n [socket, eventName],\n );\n\n return { send, isReady: isConnected && !!socket };\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nexport type CommitMode = \"change\" | \"blur\" | \"debounce\";\n\nexport interface UseSocketInputOptions<TEntry, TAllowedUpdate, TKey extends keyof TAllowedUpdate> {\n /**\n * Current state of the entity\n */\n state: TEntry | null;\n /**\n * Property key to update\n */\n property: TKey;\n /**\n * Update function that sends the patch to the server\n */\n update: (patch: Partial<TAllowedUpdate>) => Promise<TEntry | undefined | null>;\n /**\n * When to commit changes to the server\n * - \"change\": immediately on every change\n * - \"blur\": when input loses focus\n * - \"debounce\": after debounceMs of inactivity\n */\n commitMode: CommitMode;\n /**\n * Debounce delay in milliseconds (only used when commitMode is \"debounce\")\n */\n debounceMs?: number;\n /**\n * Format the value from state for display\n */\n format?: (value: unknown) => unknown;\n /**\n * Parse the display value back to the state format\n */\n parse?: (value: unknown) => unknown;\n /**\n * Callback on successful update\n */\n onSuccess?: (entry: TEntry | undefined | null) => void;\n /**\n * Callback on error\n */\n onError?: (error: string) => void;\n}\n\nexport interface UseSocketInputResult<T> {\n /**\n * Current local value (may differ from server while editing)\n */\n value: T;\n /**\n * Update the local value\n */\n onLocalChange: (newValue: T) => void;\n /**\n * Manually commit the current value to the server\n */\n commit: () => void;\n /**\n * Handle blur event (commits if commitMode is \"blur\")\n */\n onBlur: () => void;\n /**\n * Whether an update is currently in flight\n */\n inFlight: boolean;\n /**\n * Whether the local value differs from the server value\n */\n isDirty: boolean;\n}\n\n/**\n * Hook for managing input state that syncs with the server.\n * Handles optimistic updates, debouncing, and conflict resolution.\n */\nexport function useSocketInput<TEntry, TAllowedUpdate, TKey extends keyof TAllowedUpdate>(\n options: UseSocketInputOptions<TEntry, TAllowedUpdate, TKey>,\n): UseSocketInputResult<unknown> {\n const {\n state,\n property,\n update,\n commitMode,\n debounceMs = 300,\n format = (v) => v,\n parse = (v) => v,\n onSuccess,\n onError,\n } = options;\n\n // Get server value\n const serverValue = state ? (state as Record<string, unknown>)[property as string] : undefined;\n const formattedServerValue = format(serverValue);\n\n // Local state\n const [localValue, setLocalValue] = React.useState<unknown>(formattedServerValue);\n const [inFlight, setInFlight] = React.useState(false);\n const [isDirty, setIsDirty] = React.useState(false);\n\n // Refs for debouncing\n const debounceTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n const pendingValueRef = React.useRef<unknown>(null);\n\n // Sync local value with server when server value changes (and we're not dirty)\n React.useEffect(() => {\n if (!isDirty && !inFlight) {\n setLocalValue(formattedServerValue);\n }\n }, [formattedServerValue, isDirty, inFlight]);\n\n // Commit function\n const commit = React.useCallback(async () => {\n if (!isDirty) return;\n\n const valueToCommit = pendingValueRef.current ?? localValue;\n const parsedValue = parse(valueToCommit);\n\n setInFlight(true);\n\n try {\n const result = await update({ [property]: parsedValue } as Partial<TAllowedUpdate>);\n setIsDirty(false);\n pendingValueRef.current = null;\n onSuccess?.(result);\n } catch (err) {\n onError?.(err instanceof Error ? err.message : \"Update failed\");\n // Revert to server value on error\n setLocalValue(formattedServerValue);\n setIsDirty(false);\n } finally {\n setInFlight(false);\n }\n }, [isDirty, localValue, parse, property, update, onSuccess, onError, formattedServerValue]);\n\n // Handle local change\n const onLocalChange = React.useCallback(\n (newValue: unknown) => {\n setLocalValue(newValue);\n setIsDirty(true);\n pendingValueRef.current = newValue;\n\n if (commitMode === \"change\") {\n // Commit immediately\n void (async () => {\n const parsedValue = parse(newValue);\n setInFlight(true);\n try {\n const result = await update({ [property]: parsedValue } as Partial<TAllowedUpdate>);\n setIsDirty(false);\n pendingValueRef.current = null;\n onSuccess?.(result);\n } catch (err) {\n onError?.(err instanceof Error ? err.message : \"Update failed\");\n } finally {\n setInFlight(false);\n }\n })();\n } else if (commitMode === \"debounce\") {\n // Clear existing timer\n if (debounceTimerRef.current) {\n clearTimeout(debounceTimerRef.current);\n }\n // Set new timer\n debounceTimerRef.current = setTimeout(() => {\n void commit();\n }, debounceMs);\n }\n // For \"blur\" mode, we just update local state and wait for blur\n },\n [commitMode, parse, property, update, onSuccess, onError, debounceMs, commit],\n );\n\n // Handle blur\n const onBlur = React.useCallback(() => {\n if (commitMode === \"blur\" && isDirty) {\n void commit();\n }\n }, [commitMode, isDirty, commit]);\n\n // Cleanup debounce timer\n React.useEffect(() => {\n return () => {\n if (debounceTimerRef.current) {\n clearTimeout(debounceTimerRef.current);\n }\n };\n }, []);\n\n return {\n value: localValue,\n onLocalChange,\n commit,\n onBlur,\n inFlight,\n isDirty,\n };\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useSocketInput, type CommitMode } from \"./useSocketInput\";\n\nexport interface SocketCheckboxProps<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n> extends Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n \"checked\" | \"onChange\" | \"type\" | \"onError\" | \"property\"\n> {\n state: TEntry | null;\n update: (patch: Partial<TAllowedUpdate>) => Promise<TEntry | undefined | null>;\n property: TKey;\n commitMode?: CommitMode;\n debounceMs?: number;\n format?: (v: unknown) => unknown;\n parse?: (v: unknown) => unknown;\n onSuccess?: (entry: TEntry | undefined | null) => void;\n onError?: (error: string) => void;\n}\n\n/**\n * A checkbox input that syncs its checked state with the server via socket.\n *\n * @example\n * ```tsx\n * <SocketCheckbox\n * state={chat}\n * update={(patch) => updateChat.mutateAsync({ id: chat.id, ...patch })}\n * property=\"isArchived\"\n * commitMode=\"change\"\n * />\n * ```\n */\nexport function SocketCheckbox<TEntry, TAllowedUpdate, TKey extends keyof TAllowedUpdate>(\n props: SocketCheckboxProps<TEntry, TAllowedUpdate, TKey>,\n): React.ReactElement {\n const {\n state,\n update,\n property,\n commitMode = \"change\",\n debounceMs,\n format,\n parse,\n onSuccess,\n onError,\n disabled,\n ...inputProps\n } = props;\n\n const defaultFormat = React.useCallback((v: unknown) => Boolean(v), []);\n const defaultParse = React.useCallback((v: unknown) => Boolean(v), []);\n\n const { value, onLocalChange, inFlight } = useSocketInput<TEntry, TAllowedUpdate, TKey>({\n state,\n property,\n update,\n commitMode,\n debounceMs,\n format: format ?? defaultFormat,\n parse: parse ?? defaultParse,\n onSuccess,\n onError,\n });\n\n const handleChange = React.useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n onLocalChange(event.target.checked);\n },\n [onLocalChange],\n );\n\n return (\n <input\n {...inputProps}\n type=\"checkbox\"\n checked={Boolean(value)}\n onChange={handleChange}\n disabled={Boolean(disabled) || inFlight}\n />\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useSocketInput, type CommitMode } from \"./useSocketInput\";\n\nexport interface SocketTextFieldProps<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n> extends Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n \"value\" | \"onChange\" | \"onError\" | \"property\"\n> {\n state: TEntry | null;\n update: (patch: Partial<TAllowedUpdate>) => Promise<TEntry | undefined | null>;\n property: TKey;\n commitMode?: CommitMode;\n debounceMs?: number;\n format?: (v: unknown) => unknown;\n parse?: (v: unknown) => unknown;\n onSuccess?: (entry: TEntry | undefined | null) => void;\n onError?: (error: string) => void;\n}\n\n/**\n * A text input that syncs its value with the server via socket.\n *\n * @example\n * ```tsx\n * <SocketTextField\n * state={chat}\n * update={(patch) => updateChat.mutateAsync({ id: chat.id, ...patch })}\n * property=\"title\"\n * commitMode=\"debounce\"\n * debounceMs={500}\n * placeholder=\"Chat title...\"\n * />\n * ```\n */\nexport function SocketTextField<TEntry, TAllowedUpdate, TKey extends keyof TAllowedUpdate>(\n props: SocketTextFieldProps<TEntry, TAllowedUpdate, TKey>,\n): React.ReactElement {\n const {\n state,\n update,\n property,\n commitMode = \"blur\",\n debounceMs = 300,\n format,\n parse,\n onSuccess,\n onError,\n disabled,\n type = \"text\",\n onBlur: consumerOnBlur,\n ...inputProps\n } = props;\n\n const defaultFormat = React.useCallback((v: unknown) => String(v ?? \"\"), []);\n const defaultParse = React.useCallback((v: unknown) => String(v), []);\n\n const { value, onLocalChange, onBlur, inFlight } = useSocketInput<TEntry, TAllowedUpdate, TKey>({\n state,\n property,\n update,\n commitMode,\n debounceMs,\n format: format ?? defaultFormat,\n parse: parse ?? defaultParse,\n onSuccess,\n onError,\n });\n\n const handleChange = React.useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n onLocalChange(event.target.value);\n },\n [onLocalChange],\n );\n\n // Chain the commit-on-blur handler with the consumer's onBlur — spreading\n // inputProps before the controlled onBlur used to silently swallow it.\n const handleBlur = React.useCallback(\n (event: React.FocusEvent<HTMLInputElement>) => {\n onBlur();\n consumerOnBlur?.(event);\n },\n [onBlur, consumerOnBlur],\n );\n\n return (\n <input\n {...inputProps}\n type={type}\n value={String(value)}\n onChange={handleChange}\n onBlur={handleBlur}\n disabled={Boolean(disabled) || inFlight}\n />\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useSocketInput, type CommitMode } from \"./useSocketInput\";\n\nexport interface SocketSelectProps<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n> extends Omit<\n React.SelectHTMLAttributes<HTMLSelectElement>,\n \"value\" | \"onChange\" | \"onError\" | \"property\"\n> {\n state: TEntry | null;\n update: (patch: Partial<TAllowedUpdate>) => Promise<TEntry | undefined | null>;\n property: TKey;\n children: React.ReactNode;\n commitMode?: CommitMode;\n debounceMs?: number;\n format?: (v: unknown) => unknown;\n parse?: (v: unknown) => unknown;\n onSuccess?: (entry: TEntry | undefined | null) => void;\n onError?: (error: string) => void;\n}\n\n/**\n * A select input that syncs its value with the server via socket.\n *\n * @example\n * ```tsx\n * <SocketSelect\n * state={chat}\n * update={(patch) => updateChat.mutateAsync({ id: chat.id, ...patch })}\n * property=\"status\"\n * commitMode=\"change\"\n * >\n * <option value=\"active\">Active</option>\n * <option value=\"archived\">Archived</option>\n * </SocketSelect>\n * ```\n */\nexport function SocketSelect<TEntry, TAllowedUpdate, TKey extends keyof TAllowedUpdate>(\n props: SocketSelectProps<TEntry, TAllowedUpdate, TKey>,\n): React.ReactElement {\n const {\n state,\n update,\n property,\n children,\n commitMode = \"change\",\n debounceMs,\n format,\n parse,\n onSuccess,\n onError,\n disabled,\n ...selectProps\n } = props;\n\n const defaultFormat = React.useCallback((v: unknown) => String(v ?? \"\"), []);\n const defaultParse = React.useCallback((v: unknown) => String(v), []);\n\n const { value, onLocalChange, inFlight } = useSocketInput<TEntry, TAllowedUpdate, TKey>({\n state,\n property,\n update,\n commitMode,\n debounceMs,\n format: format ?? defaultFormat,\n parse: parse ?? defaultParse,\n onSuccess,\n onError,\n });\n\n const handleChange = React.useCallback(\n (event: React.ChangeEvent<HTMLSelectElement>) => {\n onLocalChange(event.target.value);\n },\n [onLocalChange],\n );\n\n return (\n <select\n {...selectProps}\n value={String(value)}\n onChange={handleChange}\n disabled={Boolean(disabled) || inFlight}\n >\n {children}\n </select>\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useSocketInput, type CommitMode } from \"./useSocketInput\";\n\nexport interface SocketSliderProps<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n> extends Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n \"value\" | \"onChange\" | \"type\" | \"onError\" | \"property\"\n> {\n state: TEntry | null;\n update: (patch: Partial<TAllowedUpdate>) => Promise<TEntry | undefined | null>;\n property: TKey;\n commitMode?: CommitMode;\n debounceMs?: number;\n format?: (v: unknown) => unknown;\n parse?: (v: unknown) => unknown;\n onSuccess?: (entry: TEntry | undefined | null) => void;\n onError?: (error: string) => void;\n}\n\n/**\n * A range slider input that syncs its value with the server via socket.\n *\n * @example\n * ```tsx\n * <SocketSlider\n * state={settings}\n * update={(patch) => updateSettings.mutateAsync({ id: settings.id, ...patch })}\n * property=\"volume\"\n * min={0}\n * max={100}\n * commitMode=\"debounce\"\n * debounceMs={200}\n * />\n * ```\n */\nexport function SocketSlider<TEntry, TAllowedUpdate, TKey extends keyof TAllowedUpdate>(\n props: SocketSliderProps<TEntry, TAllowedUpdate, TKey>,\n): React.ReactElement {\n const {\n state,\n update,\n property,\n commitMode = \"debounce\",\n debounceMs = 200,\n format,\n parse,\n onSuccess,\n onError,\n disabled,\n ...inputProps\n } = props;\n\n const defaultFormat = React.useCallback((v: unknown) => Number(v ?? 0), []);\n const defaultParse = React.useCallback((v: unknown) => Number(v), []);\n\n const { value, onLocalChange, inFlight } = useSocketInput<TEntry, TAllowedUpdate, TKey>({\n state,\n property,\n update,\n commitMode,\n debounceMs,\n format: format ?? defaultFormat,\n parse: parse ?? defaultParse,\n onSuccess,\n onError,\n });\n\n const handleChange = React.useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n onLocalChange(Number(event.target.value));\n },\n [onLocalChange],\n );\n\n return (\n <input\n {...inputProps}\n type=\"range\"\n value={Number(value)}\n onChange={handleChange}\n disabled={Boolean(disabled) || inFlight}\n />\n );\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { useSocketInput, type CommitMode } from \"./useSocketInput\";\n\nexport interface SocketSwitchProps<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n> extends Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n \"checked\" | \"onChange\" | \"type\" | \"onError\" | \"property\"\n> {\n state: TEntry | null;\n update: (patch: Partial<TAllowedUpdate>) => Promise<TEntry | undefined | null>;\n property: TKey;\n commitMode?: CommitMode;\n debounceMs?: number;\n format?: (v: unknown) => unknown;\n parse?: (v: unknown) => unknown;\n onSuccess?: (entry: TEntry | undefined | null) => void;\n onError?: (error: string) => void;\n}\n\n/**\n * A switch/toggle input that syncs its state with the server via socket.\n * Styled as a switch by default using CSS.\n *\n * @example\n * ```tsx\n * <SocketSwitch\n * state={settings}\n * update={(patch) => updateSettings.mutateAsync({ id: settings.id, ...patch })}\n * property=\"darkMode\"\n * commitMode=\"change\"\n * />\n * ```\n */\nexport function SocketSwitch<TEntry, TAllowedUpdate, TKey extends keyof TAllowedUpdate>(\n props: SocketSwitchProps<TEntry, TAllowedUpdate, TKey>,\n): React.ReactElement {\n const {\n state,\n update,\n property,\n commitMode = \"change\",\n debounceMs,\n format,\n parse,\n onSuccess,\n onError,\n disabled,\n className,\n ...inputProps\n } = props;\n\n const defaultFormat = React.useCallback((v: unknown) => Boolean(v), []);\n const defaultParse = React.useCallback((v: unknown) => Boolean(v), []);\n\n const { value, onLocalChange, inFlight } = useSocketInput<TEntry, TAllowedUpdate, TKey>({\n state,\n property,\n update,\n commitMode,\n debounceMs,\n format: format ?? defaultFormat,\n parse: parse ?? defaultParse,\n onSuccess,\n onError,\n });\n\n const handleChange = React.useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n onLocalChange(event.target.checked);\n },\n [onLocalChange],\n );\n\n // Role=\"switch\" for accessibility\n return (\n <input\n {...inputProps}\n type=\"checkbox\"\n role=\"switch\"\n aria-checked={Boolean(value)}\n checked={Boolean(value)}\n onChange={handleChange}\n disabled={Boolean(disabled) || inFlight}\n className={className}\n />\n );\n}\n","/**\n * Format a currency value for display.\n */\nexport function formatCurrency(\n value: string | number | null | undefined,\n currency = \"USD\",\n locale = \"en-US\",\n): string {\n if (value == null) return \"-\";\n const num = typeof value === \"string\" ? parseFloat(value) : value;\n if (isNaN(num)) return \"-\";\n return new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency,\n }).format(num);\n}\n\n/**\n * Format a number for display with optional decimal places.\n */\nexport function formatNumber(\n value: string | number | null | undefined,\n options?: Intl.NumberFormatOptions,\n locale = \"en-US\",\n): string {\n if (value == null) return \"-\";\n const num = typeof value === \"string\" ? parseFloat(value) : value;\n if (isNaN(num)) return \"-\";\n return new Intl.NumberFormat(locale, options).format(num);\n}\n\n/**\n * Format a date for display.\n */\nexport function formatDate(\n value: string | Date | null | undefined,\n options?: Intl.DateTimeFormatOptions,\n locale = \"en-US\",\n): string {\n if (!value) return \"-\";\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (isNaN(date.getTime())) return \"-\";\n return date.toLocaleDateString(\n locale,\n options ?? {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n },\n );\n}\n\n/**\n * Format a date with time for display.\n */\nexport function formatDateTime(\n value: string | Date | null | undefined,\n options?: Intl.DateTimeFormatOptions,\n locale = \"en-US\",\n): string {\n if (!value) return \"-\";\n const date = typeof value === \"string\" ? new Date(value) : value;\n if (isNaN(date.getTime())) return \"-\";\n return date.toLocaleString(\n locale,\n options ?? {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n },\n );\n}\n\n/**\n * Truncate a string to a maximum length.\n */\nexport function truncate(value: string | null | undefined, maxLength = 50): string {\n if (!value) return \"-\";\n if (value.length <= maxLength) return value;\n return `${value.slice(0, maxLength - 3)}...`;\n}\n\n/**\n * Format a percentage for display.\n */\nexport function formatPercent(\n value: string | number | null | undefined,\n decimals = 1,\n locale = \"en-US\",\n): string {\n if (value == null) return \"-\";\n const num = typeof value === \"string\" ? parseFloat(value) : value;\n if (isNaN(num)) return \"-\";\n return new Intl.NumberFormat(locale, {\n style: \"percent\",\n minimumFractionDigits: decimals,\n maximumFractionDigits: decimals,\n }).format(num / 100);\n}\n","const AUTH_TOKEN_KEY = \"auth_token\";\n\n/**\n * Get the stored auth token from localStorage.\n */\nexport function getAuthToken(): string | null {\n if (typeof window === \"undefined\") return null;\n return localStorage.getItem(AUTH_TOKEN_KEY);\n}\n\n/**\n * Store the auth token in localStorage and dispatch a change event.\n */\nexport function setAuthToken(token: string): void {\n if (typeof window === \"undefined\") return;\n localStorage.setItem(AUTH_TOKEN_KEY, token);\n window.dispatchEvent(new Event(\"auth-token-changed\"));\n}\n\n/**\n * Remove the auth token from localStorage and dispatch a change event.\n */\nexport function clearAuthToken(): void {\n if (typeof window === \"undefined\") return;\n localStorage.removeItem(AUTH_TOKEN_KEY);\n window.dispatchEvent(new Event(\"auth-token-changed\"));\n}\n\nexport interface JWTPayload {\n userId: string;\n email?: string;\n}\n\n/**\n * Parse JWT payload client-side (not verified, for display purposes only).\n */\nexport function parseJWTPayload(token: string): JWTPayload | null {\n try {\n const parts = token.split(\".\");\n if (parts.length !== 3 || !parts[1]) return null;\n\n const decoded = atob(parts[1]);\n const payload: unknown = JSON.parse(decoded);\n\n if (\n typeof payload === \"object\" &&\n payload !== null &&\n \"userId\" in payload &&\n typeof (payload as { userId: unknown }).userId === \"string\"\n ) {\n const typedPayload = payload as { userId: string; email?: string };\n return {\n userId: typedPayload.userId,\n email: typedPayload.email,\n };\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Build an OAuth redirect URL for a given provider.\n */\nexport function getOAuthUrl(provider: string, apiUrl?: string): string {\n const base = apiUrl ?? process.env.NEXT_PUBLIC_API_URL ?? \"http://localhost:4000\";\n return `${base}/auth/${provider}`;\n}\n\n/**\n * Logout from current session (invalidate token on server and clear localStorage).\n */\nexport async function logout(apiUrl?: string): Promise<void> {\n const base = apiUrl ?? process.env.NEXT_PUBLIC_API_URL ?? \"http://localhost:4000\";\n const token = getAuthToken();\n\n if (token) {\n try {\n await fetch(`${base}/auth/logout`, {\n method: \"DELETE\",\n headers: { Authorization: `Bearer ${token}` },\n });\n } catch {\n // Clear local token regardless of network errors\n }\n }\n\n clearAuthToken();\n}\n\n/**\n * Logout from all devices (invalidate all sessions and clear localStorage).\n * Returns the number of sessions invalidated.\n */\nexport async function logoutAllDevices(apiUrl?: string): Promise<number> {\n const base = apiUrl ?? process.env.NEXT_PUBLIC_API_URL ?? \"http://localhost:4000\";\n const token = getAuthToken();\n\n if (!token) {\n clearAuthToken();\n return 0;\n }\n\n try {\n const response = await fetch(`${base}/auth/sessions`, {\n method: \"DELETE\",\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (response.ok) {\n const data = (await response.json()) as {\n sessionsDeleted?: number;\n };\n clearAuthToken();\n return data.sessionsDeleted ?? 0;\n }\n } catch {\n // Clear local token regardless of network errors\n }\n\n clearAuthToken();\n return 0;\n}\n","export interface NavItem {\n id: string;\n label: string;\n href: string;\n icon?: unknown;\n children?: NavItem[];\n defaultExpanded?: boolean;\n requireAuth?: boolean;\n dynamicChildren?: boolean;\n serviceName?: string;\n minAccessLevel?: string;\n}\n\nexport interface BreadcrumbItem {\n label: string;\n href: string;\n siblings?: { label: string; href: string }[];\n}\n\n/**\n * Find a nav item by its href (exact match, searching children recursively).\n */\nexport function findNavItemByHref(items: NavItem[], href: string): NavItem | undefined {\n for (const item of items) {\n if (item.href === href) return item;\n if (item.children) {\n const found = findNavItemByHref(item.children, href);\n if (found) return found;\n }\n }\n return undefined;\n}\n\n/**\n * Find the parent nav item containing a child with the given href.\n */\nexport function findParentNavItem(items: NavItem[], href: string): NavItem | undefined {\n for (const item of items) {\n if (item.children) {\n const childMatch = item.children.find((c) => c.href === href);\n if (childMatch) return item;\n const nested = findParentNavItem(item.children, href);\n if (nested) return nested;\n }\n }\n return undefined;\n}\n\n/**\n * Build breadcrumb items from a pathname using the provided navigation config.\n */\nexport function buildBreadcrumbs(pathname: string, navigation: NavItem[]): BreadcrumbItem[] {\n const breadcrumbs: BreadcrumbItem[] = [];\n\n if (pathname === \"/\") {\n breadcrumbs.push({\n label: \"Home\",\n href: \"/\",\n siblings: navigation\n .filter((item) => item.href === \"/\" || !item.requireAuth)\n .map((item) => ({ label: item.label, href: item.href })),\n });\n return breadcrumbs;\n }\n\n const segments = pathname.split(\"/\").filter(Boolean);\n let currentPath = \"\";\n\n for (const segment of segments) {\n currentPath += `/${segment}`;\n const navItem = findNavItemByHref(navigation, currentPath);\n\n if (navItem) {\n const parent = findParentNavItem(navigation, currentPath);\n const siblingItems = parent?.children ?? navigation;\n\n breadcrumbs.push({\n label: navItem.label,\n href: navItem.href,\n siblings: siblingItems.map((item) => ({\n label: item.label,\n href: item.href,\n })),\n });\n } else {\n breadcrumbs.push({\n label: segment,\n href: currentPath,\n });\n }\n }\n\n return breadcrumbs;\n}\n\n/**\n * Check if a route requires authentication by searching the navigation config.\n */\nexport function routeRequiresAuth(\n pathname: string,\n navigation: NavItem[],\n userMenuItems?: NavItem[],\n): boolean {\n const navItem = findNavItemByHref(navigation, pathname);\n if (navItem) return navItem.requireAuth ?? false;\n\n if (userMenuItems) {\n const userItem = findNavItemByHref(userMenuItems, pathname);\n if (userItem) return userItem.requireAuth ?? false;\n }\n\n const segments = pathname.split(\"/\").filter(Boolean);\n let currentPath = \"\";\n for (const segment of segments) {\n currentPath += `/${segment}`;\n const item = findNavItemByHref(navigation, currentPath);\n if (item?.requireAuth) return true;\n }\n\n return false;\n}\n"]}
|
package/dist/client/testing.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { QueryClient } from '@tanstack/react-query';
|
|
3
3
|
import { ServiceResponse } from '../shared/index.js';
|
|
4
|
-
import { Q as QuickdrawSocketContextValue } from '../types-
|
|
4
|
+
import { Q as QuickdrawSocketContextValue } from '../types-BbnMKDA5.js';
|
|
5
5
|
import 'zod';
|
|
6
6
|
import 'socket.io-client';
|
|
7
7
|
|
|
@@ -46,6 +46,16 @@ interface QuickdrawProviderProps {
|
|
|
46
46
|
* @default "invalidate-queries"
|
|
47
47
|
*/
|
|
48
48
|
reconnectBehavior?: "invalidate-queries" | "none";
|
|
49
|
+
/**
|
|
50
|
+
* Socket.io transports to negotiate, in order.
|
|
51
|
+
* The default matches socket.io's browser behavior. React Native clients
|
|
52
|
+
* should pass `["websocket"]` — the polling fallback assumes browser XHR
|
|
53
|
+
* semantics and adds no value there.
|
|
54
|
+
* Applies when the socket is (re)created; changing it alone does not
|
|
55
|
+
* reconnect an existing socket.
|
|
56
|
+
* @default ["websocket", "polling"]
|
|
57
|
+
*/
|
|
58
|
+
transports?: Array<"websocket" | "polling">;
|
|
49
59
|
}
|
|
50
60
|
/**
|
|
51
61
|
* Return type for the useChannelSend hook.
|