@fitzzero/quickdraw-core 3.1.0 → 3.2.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 +3 -3
- package/dist/client/index.js +89 -10
- package/dist/client/index.js.map +1 -1
- package/dist/client/testing.d.ts +1 -1
- package/dist/client/testing.js +2 -0
- package/dist/client/testing.js.map +1 -1
- package/dist/server/index.d.ts +31 -1
- package/dist/server/index.js +131 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/testing.d.ts +1 -1
- package/dist/server/testing.js +48 -0
- package/dist/server/testing.js.map +1 -1
- package/dist/{types-e6WPGE70.d.ts → types-Cr8YC7Kg.d.ts} +22 -1
- package/dist/{types-D7oHWh4O.d.ts → types-DniMxkQs.d.ts} +1 -0
- package/package.json +1 -1
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 } 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 } from '../types-Cr8YC7Kg.js';
|
|
3
|
+
export { C as ClientServiceMethodMap, S as SubscriptionDataMap } from '../types-Cr8YC7Kg.js';
|
|
4
4
|
import 'socket.io-client';
|
|
5
5
|
import '@tanstack/react-query';
|
|
6
6
|
import '../shared/index.js';
|
|
@@ -28,7 +28,7 @@ declare function useQuickdrawSocket(): QuickdrawSocketContextValue;
|
|
|
28
28
|
* }
|
|
29
29
|
* ```
|
|
30
30
|
*/
|
|
31
|
-
declare function QuickdrawProvider({ children, serverUrl, queryClient, authToken, autoConnect, }: QuickdrawProviderProps): React.ReactElement;
|
|
31
|
+
declare function QuickdrawProvider({ children, serverUrl, queryClient, authToken, autoConnect, withCredentials, }: 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
|
@@ -42,6 +42,75 @@ function createSubscriptionRegistry() {
|
|
|
42
42
|
}
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
|
+
function createSubscriptionBatcher(socketRef) {
|
|
46
|
+
const pending = /* @__PURE__ */ new Map();
|
|
47
|
+
let flushScheduled = false;
|
|
48
|
+
function flush() {
|
|
49
|
+
flushScheduled = false;
|
|
50
|
+
const socket = socketRef.current;
|
|
51
|
+
if (!socket) {
|
|
52
|
+
for (const entries of pending.values()) {
|
|
53
|
+
for (const entry of entries) {
|
|
54
|
+
entry.callback({ success: false, error: "Socket not connected" });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
pending.clear();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
for (const [serviceName, entries] of pending) {
|
|
61
|
+
if (entries.length === 0) continue;
|
|
62
|
+
const entryIds = entries.map((e) => e.entryId);
|
|
63
|
+
const requiredLevel = entries[0].requiredLevel;
|
|
64
|
+
socket.emit(
|
|
65
|
+
`${serviceName}:batchSubscribe`,
|
|
66
|
+
{ entryIds, requiredLevel },
|
|
67
|
+
(response) => {
|
|
68
|
+
if (response.success && response.data) {
|
|
69
|
+
const dataMap = response.data;
|
|
70
|
+
for (const entry of entries) {
|
|
71
|
+
const entityData = dataMap[entry.entryId];
|
|
72
|
+
if (entityData) {
|
|
73
|
+
entry.callback({
|
|
74
|
+
success: true,
|
|
75
|
+
data: entityData
|
|
76
|
+
});
|
|
77
|
+
} else {
|
|
78
|
+
entry.callback({
|
|
79
|
+
success: false,
|
|
80
|
+
error: "Access denied or entry not found",
|
|
81
|
+
code: 403
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
const errMsg = !response.success ? response.error : "Batch subscription failed";
|
|
87
|
+
for (const entry of entries) {
|
|
88
|
+
entry.callback({
|
|
89
|
+
success: false,
|
|
90
|
+
error: errMsg
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
pending.clear();
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
enqueue(serviceName, entryId, requiredLevel, callback) {
|
|
101
|
+
let entries = pending.get(serviceName);
|
|
102
|
+
if (!entries) {
|
|
103
|
+
entries = [];
|
|
104
|
+
pending.set(serviceName, entries);
|
|
105
|
+
}
|
|
106
|
+
entries.push({ entryId, requiredLevel, callback });
|
|
107
|
+
if (!flushScheduled) {
|
|
108
|
+
flushScheduled = true;
|
|
109
|
+
queueMicrotask(flush);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
45
114
|
var QuickdrawSocketContext = React.createContext(null);
|
|
46
115
|
function useQuickdrawSocket() {
|
|
47
116
|
const context = React.useContext(QuickdrawSocketContext);
|
|
@@ -71,7 +140,8 @@ function QuickdrawProvider({
|
|
|
71
140
|
serverUrl,
|
|
72
141
|
queryClient,
|
|
73
142
|
authToken,
|
|
74
|
-
autoConnect = !!authToken
|
|
143
|
+
autoConnect = !!authToken,
|
|
144
|
+
withCredentials = true
|
|
75
145
|
}) {
|
|
76
146
|
const [defaultQueryClient] = React.useState(() => createQueryClient());
|
|
77
147
|
const actualQueryClient = queryClient ?? defaultQueryClient;
|
|
@@ -84,6 +154,9 @@ function QuickdrawProvider({
|
|
|
84
154
|
);
|
|
85
155
|
const prevAuthTokenRef = React.useRef(authToken);
|
|
86
156
|
const socketRef = React.useRef(null);
|
|
157
|
+
const subscriptionBatcherRef = React.useRef(
|
|
158
|
+
createSubscriptionBatcher(socketRef)
|
|
159
|
+
);
|
|
87
160
|
const connect = React.useCallback(
|
|
88
161
|
(token) => {
|
|
89
162
|
if (socketRef.current) return;
|
|
@@ -92,6 +165,7 @@ function QuickdrawProvider({
|
|
|
92
165
|
subscriptionRegistryRef.current = createSubscriptionRegistry();
|
|
93
166
|
const newSocket = io(serverUrl, {
|
|
94
167
|
auth: authToUse ? { token: authToUse } : void 0,
|
|
168
|
+
withCredentials,
|
|
95
169
|
transports: ["websocket", "polling"],
|
|
96
170
|
autoConnect: true
|
|
97
171
|
});
|
|
@@ -115,7 +189,7 @@ function QuickdrawProvider({
|
|
|
115
189
|
socketRef.current = newSocket;
|
|
116
190
|
setSocket(newSocket);
|
|
117
191
|
},
|
|
118
|
-
[serverUrl]
|
|
192
|
+
[serverUrl, withCredentials]
|
|
119
193
|
);
|
|
120
194
|
const disconnect = React.useCallback(() => {
|
|
121
195
|
if (socketRef.current) {
|
|
@@ -160,7 +234,8 @@ function QuickdrawProvider({
|
|
|
160
234
|
serviceAccess,
|
|
161
235
|
connect,
|
|
162
236
|
disconnect,
|
|
163
|
-
subscriptionRegistry: subscriptionRegistryRef.current
|
|
237
|
+
subscriptionRegistry: subscriptionRegistryRef.current,
|
|
238
|
+
subscriptionBatcher: subscriptionBatcherRef.current
|
|
164
239
|
}),
|
|
165
240
|
[socket, isConnected, userId, serviceAccess, connect, disconnect]
|
|
166
241
|
);
|
|
@@ -397,7 +472,7 @@ function useServiceQuery(serviceName, methodName, payload, options) {
|
|
|
397
472
|
);
|
|
398
473
|
}
|
|
399
474
|
function useSubscription(serviceName, entryId, options = {}) {
|
|
400
|
-
const { socket, isConnected, subscriptionRegistry } = useQuickdrawSocket();
|
|
475
|
+
const { socket, isConnected, subscriptionRegistry, subscriptionBatcher } = useQuickdrawSocket();
|
|
401
476
|
const queryClient = useQueryClient();
|
|
402
477
|
const [isSubscribed, setIsSubscribed] = React.useState(false);
|
|
403
478
|
const [error, setError] = React.useState(null);
|
|
@@ -440,7 +515,6 @@ function useSubscription(serviceName, entryId, options = {}) {
|
|
|
440
515
|
subscriptionRegistry.release(subscriptionKey);
|
|
441
516
|
};
|
|
442
517
|
}
|
|
443
|
-
const subscribeEvent = `${serviceName}:subscribe`;
|
|
444
518
|
const updateEvent = `${serviceName}:update:${entryId}`;
|
|
445
519
|
const handleUpdate = (updateData) => {
|
|
446
520
|
const asAny = updateData;
|
|
@@ -458,12 +532,16 @@ function useSubscription(serviceName, entryId, options = {}) {
|
|
|
458
532
|
});
|
|
459
533
|
};
|
|
460
534
|
socket.on(updateEvent, handleUpdate);
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
535
|
+
subscriptionBatcher.enqueue(
|
|
536
|
+
serviceName,
|
|
537
|
+
entryId,
|
|
538
|
+
requiredLevel,
|
|
464
539
|
(response) => {
|
|
465
540
|
if (response.success && response.data) {
|
|
466
|
-
queryClient.setQueryData(
|
|
541
|
+
queryClient.setQueryData(
|
|
542
|
+
queryKey,
|
|
543
|
+
response.data
|
|
544
|
+
);
|
|
467
545
|
setIsSubscribed(true);
|
|
468
546
|
setError(null);
|
|
469
547
|
onDataRef.current?.(response.data);
|
|
@@ -493,7 +571,8 @@ function useSubscription(serviceName, entryId, options = {}) {
|
|
|
493
571
|
requiredLevel,
|
|
494
572
|
queryClient,
|
|
495
573
|
queryKey,
|
|
496
|
-
subscriptionRegistry
|
|
574
|
+
subscriptionRegistry,
|
|
575
|
+
subscriptionBatcher
|
|
497
576
|
]);
|
|
498
577
|
const subscribe = React.useCallback(() => {
|
|
499
578
|
if (!socket || !isConnected || !entryId) return;
|
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/QuickdrawProvider.tsx","../../src/client/useService.ts","../../src/client/useServiceQuery.ts","../../src/client/useSubscription.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","React5","React6","jsx","React7","React8","React9","React10"],"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 {\n QuickdrawSocketContextValue,\n QuickdrawProviderProps,\n SubscriptionRegistry,\n SubscriptionEntry,\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// Socket Context\n// ============================================================================\n\nconst QuickdrawSocketContext =\n 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(\n \"useQuickdrawSocket must be used within a QuickdrawProvider\"\n );\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}: 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<\n Record<string, AccessLevel>\n >({});\n\n // Create subscription registry - recreated when socket changes\n const subscriptionRegistryRef = React.useRef<SubscriptionRegistry>(\n createSubscriptionRegistry()\n );\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 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 transports: [\"websocket\", \"polling\"],\n autoConnect: true,\n });\n\n newSocket.on(\"connect\", () => {\n setIsConnected(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; serviceAccess: Record<string, AccessLevel> }) => {\n setUserId(info.userId);\n setServiceAccess(info.serviceAccess);\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]\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 }),\n [socket, isConnected, userId, serviceAccess, connect, disconnect]\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(\n eventName,\n payload,\n (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 },\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(\n eventName,\n payload,\n (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 },\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","\"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 { 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 } = 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 } = 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(\n eventName,\n payload,\n (response: ServiceResponse<TResponse>) => {\n clearTimeout(timeoutId);\n\n if (response.success) {\n resolve(response.data);\n } else {\n reject(new Error(response.error));\n }\n }\n );\n });\n }, [socket, isConnected, serviceName, methodName, payload, timeout]);\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 const query = useQuery<TResponse, Error>({\n queryKey,\n queryFn,\n enabled: enabled && !!socket && isConnected,\n staleTime,\n gcTime,\n refetchOnMount,\n refetchOnWindowFocus,\n retry,\n retryDelay,\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 } = 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 } = 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 subscribeEvent = `${serviceName}:subscribe`;\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 // Send subscription request\n socket.emit(\n subscribeEvent,\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 onDataRef.current?.(response.data);\n } else if (!response.success) {\n setError(response.error);\n setIsSubscribed(false);\n onErrorRef.current?.(response.error);\n }\n }\n );\n\n // Set cleanup function in registry\n const cleanup = () => {\n socket.off(updateEvent, handleUpdate);\n socket.emit(`${serviceName}:unsubscribe`, { entryId });\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 ]);\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","\"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<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n>(\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<React.InputHTMLAttributes<HTMLInputElement>, \"checked\" | \"onChange\" | \"type\" | \"onError\" | \"property\"> {\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<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n>(props: SocketCheckboxProps<TEntry, TAllowedUpdate, TKey>): 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<React.InputHTMLAttributes<HTMLInputElement>, \"value\" | \"onChange\" | \"onError\" | \"property\"> {\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<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n>(props: SocketTextFieldProps<TEntry, TAllowedUpdate, TKey>): 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 ...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<\n TEntry,\n TAllowedUpdate,\n TKey\n >({\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 return (\n <input\n {...inputProps}\n type=\"text\"\n value={String(value)}\n onChange={handleChange}\n onBlur={onBlur}\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<React.SelectHTMLAttributes<HTMLSelectElement>, \"value\" | \"onChange\" | \"onError\" | \"property\"> {\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<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n>(props: SocketSelectProps<TEntry, TAllowedUpdate, TKey>): 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<React.InputHTMLAttributes<HTMLInputElement>, \"value\" | \"onChange\" | \"type\" | \"onError\" | \"property\"> {\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<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n>(props: SocketSliderProps<TEntry, TAllowedUpdate, TKey>): 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<React.InputHTMLAttributes<HTMLInputElement>, \"checked\" | \"onChange\" | \"type\" | \"onError\" | \"property\"> {\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<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n>(props: SocketSwitchProps<TEntry, TAllowedUpdate, TKey>): 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(\n value: string | null | undefined,\n maxLength = 50,\n): 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(\n provider: string,\n apiUrl?: string,\n): 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(\n items: NavItem[],\n href: string,\n): 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(\n items: NavItem[],\n href: string,\n): 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(\n pathname: string,\n navigation: NavItem[],\n): 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/useServiceQuery.ts","../../src/client/useSubscription.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","React5","React6","jsx","React7","React8","React9","React10"],"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(\n socketRef: { current: Socket | null }\n): 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\n ? response.error\n : \"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 =\n 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(\n \"useQuickdrawSocket must be used within a QuickdrawProvider\"\n );\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}: 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<\n Record<string, AccessLevel>\n >({});\n\n // Create subscription registry - recreated when socket changes\n const subscriptionRegistryRef = React.useRef<SubscriptionRegistry>(\n createSubscriptionRegistry()\n );\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 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 });\n\n newSocket.on(\"connect\", () => {\n setIsConnected(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; serviceAccess: Record<string, AccessLevel> }) => {\n setUserId(info.userId);\n setServiceAccess(info.serviceAccess);\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]\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 }),\n [socket, isConnected, userId, serviceAccess, connect, disconnect]\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(\n eventName,\n payload,\n (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 },\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(\n eventName,\n payload,\n (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 },\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","\"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 { 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 } = 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 } = 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(\n eventName,\n payload,\n (response: ServiceResponse<TResponse>) => {\n clearTimeout(timeoutId);\n\n if (response.success) {\n resolve(response.data);\n } else {\n reject(new Error(response.error));\n }\n }\n );\n });\n }, [socket, isConnected, serviceName, methodName, payload, timeout]);\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 const query = useQuery<TResponse, Error>({\n queryKey,\n queryFn,\n enabled: enabled && !!socket && isConnected,\n staleTime,\n gcTime,\n refetchOnMount,\n refetchOnWindowFocus,\n retry,\n retryDelay,\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 } =\n 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 } = 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 subscriptionBatcher.enqueue(\n serviceName,\n entryId,\n requiredLevel,\n (response) => {\n if (response.success && response.data) {\n queryClient.setQueryData<TData | null>(\n queryKey,\n response.data as TData\n );\n setIsSubscribed(true);\n setError(null);\n onDataRef.current?.(response.data as TData);\n } else if (!response.success) {\n setError(response.error);\n setIsSubscribed(false);\n onErrorRef.current?.(response.error);\n }\n }\n );\n\n // Set cleanup function in registry\n const cleanup = () => {\n socket.off(updateEvent, handleUpdate);\n socket.emit(`${serviceName}:unsubscribe`, { entryId });\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 ]);\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","\"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<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n>(\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<React.InputHTMLAttributes<HTMLInputElement>, \"checked\" | \"onChange\" | \"type\" | \"onError\" | \"property\"> {\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<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n>(props: SocketCheckboxProps<TEntry, TAllowedUpdate, TKey>): 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<React.InputHTMLAttributes<HTMLInputElement>, \"value\" | \"onChange\" | \"onError\" | \"property\"> {\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<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n>(props: SocketTextFieldProps<TEntry, TAllowedUpdate, TKey>): 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 ...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<\n TEntry,\n TAllowedUpdate,\n TKey\n >({\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 return (\n <input\n {...inputProps}\n type=\"text\"\n value={String(value)}\n onChange={handleChange}\n onBlur={onBlur}\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<React.SelectHTMLAttributes<HTMLSelectElement>, \"value\" | \"onChange\" | \"onError\" | \"property\"> {\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<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n>(props: SocketSelectProps<TEntry, TAllowedUpdate, TKey>): 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<React.InputHTMLAttributes<HTMLInputElement>, \"value\" | \"onChange\" | \"type\" | \"onError\" | \"property\"> {\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<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n>(props: SocketSliderProps<TEntry, TAllowedUpdate, TKey>): 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<React.InputHTMLAttributes<HTMLInputElement>, \"checked\" | \"onChange\" | \"type\" | \"onError\" | \"property\"> {\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<\n TEntry,\n TAllowedUpdate,\n TKey extends keyof TAllowedUpdate,\n>(props: SocketSwitchProps<TEntry, TAllowedUpdate, TKey>): 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(\n value: string | null | undefined,\n maxLength = 50,\n): 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(\n provider: string,\n apiUrl?: string,\n): 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(\n items: NavItem[],\n href: string,\n): 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(\n items: NavItem[],\n href: string,\n): 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(\n pathname: string,\n navigation: NavItem[],\n): 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"]}
|