@sylphx/lens-react 2.3.4 → 2.4.1
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/index.d.ts +24 -2
- package/dist/index.js +9 -4
- package/package.json +3 -3
- package/src/create.tsx +36 -7
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,35 @@
|
|
|
1
1
|
import { LensClientConfig, QueryResult, SelectionObject, TypedClientConfig } from "@sylphx/lens-client";
|
|
2
2
|
import { MutationDef, QueryDef, RouterDef, RouterRoutes } from "@sylphx/lens-core";
|
|
3
|
+
/**
|
|
4
|
+
* Debug callbacks for query hooks.
|
|
5
|
+
* @internal For debugging purposes only - not recommended for production use.
|
|
6
|
+
*/
|
|
7
|
+
interface QueryDebugOptions<T> {
|
|
8
|
+
/** Called when data is received */
|
|
9
|
+
onData?: (data: T) => void;
|
|
10
|
+
/** Called when an error occurs */
|
|
11
|
+
onError?: (error: Error) => void;
|
|
12
|
+
/** Called when subscription starts */
|
|
13
|
+
onSubscribe?: () => void;
|
|
14
|
+
/** Called when subscription ends */
|
|
15
|
+
onUnsubscribe?: () => void;
|
|
16
|
+
}
|
|
3
17
|
/** Query hook options */
|
|
4
|
-
interface QueryHookOptions<
|
|
18
|
+
interface QueryHookOptions<
|
|
19
|
+
TInput,
|
|
20
|
+
TOutput = unknown
|
|
21
|
+
> {
|
|
5
22
|
/** Query input parameters */
|
|
6
23
|
input?: TInput;
|
|
7
24
|
/** Field selection */
|
|
8
25
|
select?: SelectionObject;
|
|
9
26
|
/** Skip query execution */
|
|
10
27
|
skip?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Debug callbacks for development.
|
|
30
|
+
* @internal For debugging purposes only - not recommended for production use.
|
|
31
|
+
*/
|
|
32
|
+
debug?: QueryDebugOptions<TOutput>;
|
|
11
33
|
}
|
|
12
34
|
/** Query hook result */
|
|
13
35
|
interface QueryHookResult<T> {
|
|
@@ -59,7 +81,7 @@ interface QueryEndpoint<
|
|
|
59
81
|
select?: SelectionObject;
|
|
60
82
|
}): QueryResult<TOutput>;
|
|
61
83
|
/** React hook for reactive queries */
|
|
62
|
-
useQuery: (options?: TInput extends void ? QueryHookOptions<void> | void : QueryHookOptions<TInput>) => QueryHookResult<TOutput>;
|
|
84
|
+
useQuery: (options?: TInput extends void ? QueryHookOptions<void, TOutput> | void : QueryHookOptions<TInput, TOutput>) => QueryHookResult<TOutput>;
|
|
63
85
|
}
|
|
64
86
|
/** Mutation endpoint with React hooks */
|
|
65
87
|
interface MutationEndpoint<
|
package/dist/index.js
CHANGED
|
@@ -23,6 +23,8 @@ function createUseQueryHook(getEndpoint) {
|
|
|
23
23
|
return function useQuery(options) {
|
|
24
24
|
const inputKey = JSON.stringify(options?.input);
|
|
25
25
|
const selectKey = JSON.stringify(options?.select);
|
|
26
|
+
const debugRef = useRef(options?.debug);
|
|
27
|
+
debugRef.current = options?.debug;
|
|
26
28
|
const query = useMemo(() => {
|
|
27
29
|
if (options?.skip)
|
|
28
30
|
return null;
|
|
@@ -45,32 +47,35 @@ function createUseQueryHook(getEndpoint) {
|
|
|
45
47
|
return;
|
|
46
48
|
}
|
|
47
49
|
dispatch({ type: "START" });
|
|
50
|
+
debugRef.current?.onSubscribe?.();
|
|
48
51
|
let hasReceivedData = false;
|
|
49
52
|
const unsubscribe = query.subscribe((value) => {
|
|
50
53
|
if (mountedRef.current) {
|
|
51
54
|
hasReceivedData = true;
|
|
52
55
|
dispatch({ type: "SUCCESS", data: value });
|
|
56
|
+
debugRef.current?.onData?.(value);
|
|
53
57
|
}
|
|
54
58
|
});
|
|
55
59
|
query.then((value) => {
|
|
56
60
|
if (mountedRef.current) {
|
|
57
61
|
if (!hasReceivedData) {
|
|
58
62
|
dispatch({ type: "SUCCESS", data: value });
|
|
63
|
+
debugRef.current?.onData?.(value);
|
|
59
64
|
} else {
|
|
60
65
|
dispatch({ type: "LOADING_DONE" });
|
|
61
66
|
}
|
|
62
67
|
}
|
|
63
68
|
}, (err) => {
|
|
64
69
|
if (mountedRef.current) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
});
|
|
70
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
71
|
+
dispatch({ type: "ERROR", error });
|
|
72
|
+
debugRef.current?.onError?.(error);
|
|
69
73
|
}
|
|
70
74
|
});
|
|
71
75
|
return () => {
|
|
72
76
|
mountedRef.current = false;
|
|
73
77
|
unsubscribe();
|
|
78
|
+
debugRef.current?.onUnsubscribe?.();
|
|
74
79
|
};
|
|
75
80
|
}, [query]);
|
|
76
81
|
const refetch = useCallback(() => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sylphx/lens-react",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.1",
|
|
4
4
|
"description": "React bindings for Lens API framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"author": "SylphxAI",
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@sylphx/lens-client": "^2.3.
|
|
34
|
-
"@sylphx/lens-core": "^2.
|
|
33
|
+
"@sylphx/lens-client": "^2.3.2",
|
|
34
|
+
"@sylphx/lens-core": "^2.3.0"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"react": ">=18.0.0"
|
package/src/create.tsx
CHANGED
|
@@ -39,14 +39,34 @@ import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from "r
|
|
|
39
39
|
// Types
|
|
40
40
|
// =============================================================================
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Debug callbacks for query hooks.
|
|
44
|
+
* @internal For debugging purposes only - not recommended for production use.
|
|
45
|
+
*/
|
|
46
|
+
export interface QueryDebugOptions<T> {
|
|
47
|
+
/** Called when data is received */
|
|
48
|
+
onData?: (data: T) => void;
|
|
49
|
+
/** Called when an error occurs */
|
|
50
|
+
onError?: (error: Error) => void;
|
|
51
|
+
/** Called when subscription starts */
|
|
52
|
+
onSubscribe?: () => void;
|
|
53
|
+
/** Called when subscription ends */
|
|
54
|
+
onUnsubscribe?: () => void;
|
|
55
|
+
}
|
|
56
|
+
|
|
42
57
|
/** Query hook options */
|
|
43
|
-
export interface QueryHookOptions<TInput> {
|
|
58
|
+
export interface QueryHookOptions<TInput, TOutput = unknown> {
|
|
44
59
|
/** Query input parameters */
|
|
45
60
|
input?: TInput;
|
|
46
61
|
/** Field selection */
|
|
47
62
|
select?: SelectionObject;
|
|
48
63
|
/** Skip query execution */
|
|
49
64
|
skip?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Debug callbacks for development.
|
|
67
|
+
* @internal For debugging purposes only - not recommended for production use.
|
|
68
|
+
*/
|
|
69
|
+
debug?: QueryDebugOptions<TOutput>;
|
|
50
70
|
}
|
|
51
71
|
|
|
52
72
|
/** Query hook result */
|
|
@@ -92,7 +112,9 @@ export interface QueryEndpoint<TInput, TOutput> {
|
|
|
92
112
|
|
|
93
113
|
/** React hook for reactive queries */
|
|
94
114
|
useQuery: (
|
|
95
|
-
options?: TInput extends void
|
|
115
|
+
options?: TInput extends void
|
|
116
|
+
? QueryHookOptions<void, TOutput> | void
|
|
117
|
+
: QueryHookOptions<TInput, TOutput>,
|
|
96
118
|
) => QueryHookResult<TOutput>;
|
|
97
119
|
}
|
|
98
120
|
|
|
@@ -164,12 +186,16 @@ function queryReducer<T>(state: QueryState<T>, action: QueryAction<T>): QuerySta
|
|
|
164
186
|
function createUseQueryHook<TInput, TOutput>(
|
|
165
187
|
getEndpoint: () => (options: unknown) => QueryResult<TOutput>,
|
|
166
188
|
) {
|
|
167
|
-
return function useQuery(options?: QueryHookOptions<TInput>): QueryHookResult<TOutput> {
|
|
189
|
+
return function useQuery(options?: QueryHookOptions<TInput, TOutput>): QueryHookResult<TOutput> {
|
|
168
190
|
// Use JSON.stringify for stable dependency comparison
|
|
169
191
|
// This prevents re-fetching when input object reference changes but content is the same
|
|
170
192
|
const inputKey = JSON.stringify(options?.input);
|
|
171
193
|
const selectKey = JSON.stringify(options?.select);
|
|
172
194
|
|
|
195
|
+
// Store debug callbacks in ref to avoid dependency issues
|
|
196
|
+
const debugRef = useRef(options?.debug);
|
|
197
|
+
debugRef.current = options?.debug;
|
|
198
|
+
|
|
173
199
|
// Get query result from base client
|
|
174
200
|
// biome-ignore lint/correctness/useExhaustiveDependencies: Using JSON.stringify keys (inputKey, selectKey) for stable comparison instead of object references
|
|
175
201
|
const query = useMemo(() => {
|
|
@@ -201,6 +227,7 @@ function createUseQueryHook<TInput, TOutput>(
|
|
|
201
227
|
}
|
|
202
228
|
|
|
203
229
|
dispatch({ type: "START" });
|
|
230
|
+
debugRef.current?.onSubscribe?.();
|
|
204
231
|
|
|
205
232
|
let hasReceivedData = false;
|
|
206
233
|
|
|
@@ -208,6 +235,7 @@ function createUseQueryHook<TInput, TOutput>(
|
|
|
208
235
|
if (mountedRef.current) {
|
|
209
236
|
hasReceivedData = true;
|
|
210
237
|
dispatch({ type: "SUCCESS", data: value });
|
|
238
|
+
debugRef.current?.onData?.(value);
|
|
211
239
|
}
|
|
212
240
|
});
|
|
213
241
|
|
|
@@ -216,6 +244,7 @@ function createUseQueryHook<TInput, TOutput>(
|
|
|
216
244
|
if (mountedRef.current) {
|
|
217
245
|
if (!hasReceivedData) {
|
|
218
246
|
dispatch({ type: "SUCCESS", data: value });
|
|
247
|
+
debugRef.current?.onData?.(value);
|
|
219
248
|
} else {
|
|
220
249
|
dispatch({ type: "LOADING_DONE" });
|
|
221
250
|
}
|
|
@@ -223,10 +252,9 @@ function createUseQueryHook<TInput, TOutput>(
|
|
|
223
252
|
},
|
|
224
253
|
(err) => {
|
|
225
254
|
if (mountedRef.current) {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
});
|
|
255
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
256
|
+
dispatch({ type: "ERROR", error });
|
|
257
|
+
debugRef.current?.onError?.(error);
|
|
230
258
|
}
|
|
231
259
|
},
|
|
232
260
|
);
|
|
@@ -234,6 +262,7 @@ function createUseQueryHook<TInput, TOutput>(
|
|
|
234
262
|
return () => {
|
|
235
263
|
mountedRef.current = false;
|
|
236
264
|
unsubscribe();
|
|
265
|
+
debugRef.current?.onUnsubscribe?.();
|
|
237
266
|
};
|
|
238
267
|
}, [query]);
|
|
239
268
|
|