@ic-reactor/react 3.3.0 → 3.4.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/README.md +117 -4
- package/dist/createActorHooks.d.ts +1 -1
- package/dist/createActorHooks.d.ts.map +1 -1
- package/dist/createActorHooks.js +10 -25
- package/dist/createActorHooks.js.map +1 -1
- package/dist/createAuthHooks.d.ts +13 -5
- package/dist/createAuthHooks.d.ts.map +1 -1
- package/dist/createAuthHooks.js +51 -1
- package/dist/createAuthHooks.js.map +1 -1
- package/dist/createInfiniteQuery.d.ts.map +1 -1
- package/dist/createInfiniteQuery.js +2 -12
- package/dist/createInfiniteQuery.js.map +1 -1
- package/dist/createMutation.d.ts.map +1 -1
- package/dist/createMutation.js +39 -56
- package/dist/createMutation.js.map +1 -1
- package/dist/createQuery.d.ts.map +1 -1
- package/dist/createQuery.js +31 -39
- package/dist/createQuery.js.map +1 -1
- package/dist/createSuspenseInfiniteQuery.d.ts.map +1 -1
- package/dist/createSuspenseInfiniteQuery.js +1 -14
- package/dist/createSuspenseInfiniteQuery.js.map +1 -1
- package/dist/createSuspenseQuery.d.ts.map +1 -1
- package/dist/createSuspenseQuery.js +30 -39
- package/dist/createSuspenseQuery.js.map +1 -1
- package/dist/hooks/useActorInfiniteQuery.d.ts.map +1 -1
- package/dist/hooks/useActorInfiniteQuery.js +4 -5
- package/dist/hooks/useActorInfiniteQuery.js.map +1 -1
- package/dist/hooks/useActorMutation.d.ts +9 -2
- package/dist/hooks/useActorMutation.d.ts.map +1 -1
- package/dist/hooks/useActorMutation.js +15 -15
- package/dist/hooks/useActorMutation.js.map +1 -1
- package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts.map +1 -1
- package/dist/hooks/useActorSuspenseInfiniteQuery.js +4 -5
- package/dist/hooks/useActorSuspenseInfiniteQuery.js.map +1 -1
- package/dist/types.d.ts +26 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +26 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +41 -0
- package/dist/utils.js.map +1 -0
- package/package.json +17 -11
- package/src/createActorHooks.ts +32 -31
- package/src/createAuthHooks.ts +93 -6
- package/src/createInfiniteQuery.ts +2 -19
- package/src/createMutation.ts +52 -69
- package/src/createQuery.ts +41 -52
- package/src/createSuspenseInfiniteQuery.ts +1 -22
- package/src/createSuspenseQuery.ts +42 -56
- package/src/hooks/useActorInfiniteQuery.ts +4 -9
- package/src/hooks/useActorMutation.ts +40 -14
- package/src/hooks/useActorSuspenseInfiniteQuery.ts +4 -9
- package/src/types.ts +32 -0
- package/src/utils.ts +52 -0
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared internal utilities for query and mutation factories.
|
|
3
|
+
*/
|
|
4
|
+
import type { QueryKey } from "@tanstack/react-query";
|
|
5
|
+
/**
|
|
6
|
+
* Internal query-key segment used to distinguish per-call factory args
|
|
7
|
+
* from the base query key. Not part of the public API.
|
|
8
|
+
*/
|
|
9
|
+
export declare const FACTORY_KEY_ARGS_QUERY_KEY = "__ic_reactor_factory_key_args";
|
|
10
|
+
/**
|
|
11
|
+
* Merge a base query key, optional per-call query key, and optional key-args
|
|
12
|
+
* into a single query key array.
|
|
13
|
+
*
|
|
14
|
+
* Used by createInfiniteQueryFactory and createSuspenseInfiniteQueryFactory to
|
|
15
|
+
* ensure each unique set of factory args produces a distinct cache entry.
|
|
16
|
+
*/
|
|
17
|
+
export declare function mergeFactoryQueryKey(baseQueryKey?: QueryKey, callQueryKey?: QueryKey, keyArgs?: unknown): QueryKey | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Build a chained select function that first applies the config-level select
|
|
20
|
+
* (if any) and then the hook-level select (if any).
|
|
21
|
+
*
|
|
22
|
+
* This enables `createQuery` / `createSuspenseQuery` to support two-level
|
|
23
|
+
* select chaining without duplicating the logic.
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildChainedSelect<TData, TSelected, TFinal = TSelected>(configSelect: ((data: TData) => TSelected) | undefined, hookSelect: ((data: TSelected) => TFinal) | undefined): (rawData: TData) => TSelected | TFinal;
|
|
26
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAErD;;;GAGG;AACH,eAAO,MAAM,0BAA0B,kCAAkC,CAAA;AAEzE;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,YAAY,CAAC,EAAE,QAAQ,EACvB,YAAY,CAAC,EAAE,QAAQ,EACvB,OAAO,CAAC,EAAE,OAAO,GAChB,QAAQ,GAAG,SAAS,CAStB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,EACrE,YAAY,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,KAAK,SAAS,CAAC,GAAG,SAAS,EACtD,UAAU,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,KAAK,MAAM,CAAC,GAAG,SAAS,GACpD,CAAC,OAAO,EAAE,KAAK,KAAK,SAAS,GAAG,MAAM,CAOxC"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared internal utilities for query and mutation factories.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Internal query-key segment used to distinguish per-call factory args
|
|
6
|
+
* from the base query key. Not part of the public API.
|
|
7
|
+
*/
|
|
8
|
+
export const FACTORY_KEY_ARGS_QUERY_KEY = "__ic_reactor_factory_key_args";
|
|
9
|
+
/**
|
|
10
|
+
* Merge a base query key, optional per-call query key, and optional key-args
|
|
11
|
+
* into a single query key array.
|
|
12
|
+
*
|
|
13
|
+
* Used by createInfiniteQueryFactory and createSuspenseInfiniteQueryFactory to
|
|
14
|
+
* ensure each unique set of factory args produces a distinct cache entry.
|
|
15
|
+
*/
|
|
16
|
+
export function mergeFactoryQueryKey(baseQueryKey, callQueryKey, keyArgs) {
|
|
17
|
+
const merged = [];
|
|
18
|
+
if (baseQueryKey)
|
|
19
|
+
merged.push(...baseQueryKey);
|
|
20
|
+
if (callQueryKey)
|
|
21
|
+
merged.push(...callQueryKey);
|
|
22
|
+
if (keyArgs !== undefined)
|
|
23
|
+
merged.push({ [FACTORY_KEY_ARGS_QUERY_KEY]: keyArgs });
|
|
24
|
+
return merged.length > 0 ? merged : undefined;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Build a chained select function that first applies the config-level select
|
|
28
|
+
* (if any) and then the hook-level select (if any).
|
|
29
|
+
*
|
|
30
|
+
* This enables `createQuery` / `createSuspenseQuery` to support two-level
|
|
31
|
+
* select chaining without duplicating the logic.
|
|
32
|
+
*/
|
|
33
|
+
export function buildChainedSelect(configSelect, hookSelect) {
|
|
34
|
+
return (rawData) => {
|
|
35
|
+
const firstPass = configSelect
|
|
36
|
+
? configSelect(rawData)
|
|
37
|
+
: rawData;
|
|
38
|
+
return hookSelect ? hookSelect(firstPass) : firstPass;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;;GAGG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,+BAA+B,CAAA;AAEzE;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAClC,YAAuB,EACvB,YAAuB,EACvB,OAAiB;IAEjB,MAAM,MAAM,GAAc,EAAE,CAAA;IAE5B,IAAI,YAAY;QAAE,MAAM,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,CAAA;IAC9C,IAAI,YAAY;QAAE,MAAM,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,CAAA;IAC9C,IAAI,OAAO,KAAK,SAAS;QACvB,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,0BAA0B,CAAC,EAAE,OAAO,EAAE,CAAC,CAAA;IAExD,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAA;AAC/C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAChC,YAAsD,EACtD,UAAqD;IAErD,OAAO,CAAC,OAAc,EAAE,EAAE;QACxB,MAAM,SAAS,GAAG,YAAY;YAC5B,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC;YACvB,CAAC,CAAE,OAAgC,CAAA;QACrC,OAAO,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IACvD,CAAC,CAAA;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ic-reactor/react",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "IC Reactor React Library",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -42,29 +42,35 @@
|
|
|
42
42
|
"author": "Behrad Deylami",
|
|
43
43
|
"license": "MIT",
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@ic-reactor/core": "^3.
|
|
45
|
+
"@ic-reactor/core": "^3.4.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
|
+
"@icp-sdk/auth": "^6.0.0",
|
|
48
49
|
"@tanstack/react-query": "^5.0.0",
|
|
49
50
|
"react": ">=18.0.0",
|
|
50
51
|
"react-dom": ">=18.0.0"
|
|
51
52
|
},
|
|
53
|
+
"peerDependenciesMeta": {
|
|
54
|
+
"@icp-sdk/auth": {
|
|
55
|
+
"optional": true
|
|
56
|
+
}
|
|
57
|
+
},
|
|
52
58
|
"devDependencies": {
|
|
53
|
-
"@icp-sdk/auth": "^
|
|
54
|
-
"@icp-sdk/core": "^5.
|
|
55
|
-
"@size-limit/preset-small-lib": "^12.0
|
|
56
|
-
"@tanstack/react-query": "^5.
|
|
59
|
+
"@icp-sdk/auth": "^6.0.0",
|
|
60
|
+
"@icp-sdk/core": "^5.3.1",
|
|
61
|
+
"@size-limit/preset-small-lib": "^12.1.0",
|
|
62
|
+
"@tanstack/react-query": "^5.100.5",
|
|
57
63
|
"@testing-library/dom": "^10.4.1",
|
|
58
64
|
"@testing-library/jest-dom": "^6.9.1",
|
|
59
65
|
"@testing-library/react": "^16.3.2",
|
|
60
66
|
"@types/react": "^19.2.14",
|
|
61
67
|
"@types/react-dom": "^19.2.3",
|
|
62
68
|
"fake-indexeddb": "^6.2.5",
|
|
63
|
-
"jsdom": "^29.0
|
|
64
|
-
"react": "^19.2.
|
|
65
|
-
"react-dom": "^19.2.
|
|
66
|
-
"size-limit": "^12.0
|
|
67
|
-
"vitest": "^4.1.
|
|
69
|
+
"jsdom": "^29.1.0",
|
|
70
|
+
"react": "^19.2.5",
|
|
71
|
+
"react-dom": "^19.2.5",
|
|
72
|
+
"size-limit": "^12.1.0",
|
|
73
|
+
"vitest": "^4.1.5"
|
|
68
74
|
},
|
|
69
75
|
"size-limit": [
|
|
70
76
|
{
|
package/src/createActorHooks.ts
CHANGED
|
@@ -33,18 +33,17 @@ import {
|
|
|
33
33
|
UseMutationResult,
|
|
34
34
|
InfiniteData,
|
|
35
35
|
} from "@tanstack/react-query"
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
|
|
41
|
-
SuspenseInfiniteQueryConfig,
|
|
42
|
-
} from "./createSuspenseInfiniteQuery"
|
|
43
|
-
import { createMutation } from "./createMutation"
|
|
36
|
+
import { useActorQuery } from "./hooks/useActorQuery"
|
|
37
|
+
import { useActorSuspenseQuery } from "./hooks/useActorSuspenseQuery"
|
|
38
|
+
import { useActorInfiniteQuery } from "./hooks/useActorInfiniteQuery"
|
|
39
|
+
import { useActorSuspenseInfiniteQuery } from "./hooks/useActorSuspenseInfiniteQuery"
|
|
40
|
+
import { useActorMutation } from "./hooks/useActorMutation"
|
|
44
41
|
import {
|
|
45
42
|
useActorMethod,
|
|
46
43
|
UseActorMethodParameters,
|
|
47
44
|
} from "./hooks/useActorMethod"
|
|
45
|
+
import { InfiniteQueryConfig } from "./createInfiniteQuery"
|
|
46
|
+
import { SuspenseInfiniteQueryConfig } from "./createSuspenseInfiniteQuery"
|
|
48
47
|
import { QueryConfig, SuspenseQueryConfig, MutationConfig } from "./types"
|
|
49
48
|
|
|
50
49
|
export type ActorHooks<A, T extends TransformKey> = {
|
|
@@ -108,33 +107,35 @@ export function createActorHooks<A, T extends TransformKey>(
|
|
|
108
107
|
reactor: Reactor<A, T>
|
|
109
108
|
): ActorHooks<A, T> {
|
|
110
109
|
return {
|
|
111
|
-
useActorQuery: ((config: any) =>
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
110
|
+
useActorQuery: ((config: any) =>
|
|
111
|
+
useActorQuery({ ...config, reactor })) as ActorHooks<
|
|
112
|
+
A,
|
|
113
|
+
T
|
|
114
|
+
>["useActorQuery"],
|
|
115
115
|
|
|
116
|
-
useActorSuspenseQuery: ((config: any) =>
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
useActorSuspenseQuery: ((config: any) =>
|
|
117
|
+
useActorSuspenseQuery({ ...config, reactor })) as ActorHooks<
|
|
118
|
+
A,
|
|
119
|
+
T
|
|
120
|
+
>["useActorSuspenseQuery"],
|
|
120
121
|
|
|
121
|
-
useActorInfiniteQuery: ((config) =>
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
useActorInfiniteQuery: ((config: any) =>
|
|
123
|
+
useActorInfiniteQuery({ ...config, reactor })) as ActorHooks<
|
|
124
|
+
A,
|
|
125
|
+
T
|
|
126
|
+
>["useActorInfiniteQuery"],
|
|
125
127
|
|
|
126
|
-
useActorSuspenseInfiniteQuery: ((config) =>
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
).useSuspenseInfiniteQuery(options)
|
|
132
|
-
}) as ActorHooks<A, T>["useActorSuspenseInfiniteQuery"],
|
|
128
|
+
useActorSuspenseInfiniteQuery: ((config: any) =>
|
|
129
|
+
useActorSuspenseInfiniteQuery({ ...config, reactor })) as ActorHooks<
|
|
130
|
+
A,
|
|
131
|
+
T
|
|
132
|
+
>["useActorSuspenseInfiniteQuery"],
|
|
133
133
|
|
|
134
|
-
useActorMutation: ((config) =>
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
134
|
+
useActorMutation: ((config: any) =>
|
|
135
|
+
useActorMutation({ ...config, reactor })) as ActorHooks<
|
|
136
|
+
A,
|
|
137
|
+
T
|
|
138
|
+
>["useActorMutation"],
|
|
138
139
|
|
|
139
140
|
useActorMethod: (config) =>
|
|
140
141
|
useActorMethod({ ...config, reactor } as UseActorMethodParameters<
|
package/src/createAuthHooks.ts
CHANGED
|
@@ -1,12 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import {
|
|
2
|
+
useSyncExternalStore,
|
|
3
|
+
useEffect,
|
|
4
|
+
useRef,
|
|
5
|
+
useMemo,
|
|
6
|
+
useState,
|
|
7
|
+
useCallback,
|
|
8
|
+
} from "react"
|
|
9
|
+
import {
|
|
10
|
+
ClientManager,
|
|
11
|
+
AgentState,
|
|
12
|
+
AuthState,
|
|
13
|
+
ClientManagerSignInOptions,
|
|
14
|
+
IdentityAttributeResult,
|
|
15
|
+
RequestIdentityAttributesParameters,
|
|
16
|
+
RequestOpenIdIdentityAttributesParameters,
|
|
17
|
+
} from "@ic-reactor/core"
|
|
18
|
+
import type { Principal } from "@icp-sdk/core/principal"
|
|
19
|
+
import type { Identity } from "@icp-sdk/core/agent"
|
|
6
20
|
|
|
7
21
|
export interface UseAuthReturn {
|
|
8
22
|
authenticate: () => Promise<Identity | undefined>
|
|
9
|
-
login: (options?:
|
|
23
|
+
login: (options?: ClientManagerSignInOptions) => Promise<void>
|
|
10
24
|
logout: (options?: { returnTo?: string }) => Promise<void>
|
|
11
25
|
isAuthenticated: boolean
|
|
12
26
|
isAuthenticating: boolean
|
|
@@ -19,6 +33,20 @@ export interface CreateAuthHooksReturn {
|
|
|
19
33
|
useAgentState: () => AgentState
|
|
20
34
|
useUserPrincipal: () => Principal | null
|
|
21
35
|
useAuth: () => UseAuthReturn
|
|
36
|
+
useIdentityAttributes: () => UseIdentityAttributesReturn
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface UseIdentityAttributesReturn {
|
|
40
|
+
requestAttributes: (
|
|
41
|
+
params: RequestIdentityAttributesParameters
|
|
42
|
+
) => Promise<IdentityAttributeResult>
|
|
43
|
+
requestOpenIdAttributes: (
|
|
44
|
+
params: RequestOpenIdIdentityAttributesParameters
|
|
45
|
+
) => Promise<IdentityAttributeResult>
|
|
46
|
+
attributes: IdentityAttributeResult | null
|
|
47
|
+
isRequestingAttributes: boolean
|
|
48
|
+
attributeError: Error | null
|
|
49
|
+
clearAttributes: () => void
|
|
22
50
|
}
|
|
23
51
|
|
|
24
52
|
/**
|
|
@@ -129,9 +157,68 @@ export const createAuthHooks = (
|
|
|
129
157
|
return identity ? identity.getPrincipal() : null
|
|
130
158
|
}
|
|
131
159
|
|
|
160
|
+
const useIdentityAttributes = (): UseIdentityAttributesReturn => {
|
|
161
|
+
const [attributes, setAttributes] =
|
|
162
|
+
useState<IdentityAttributeResult | null>(null)
|
|
163
|
+
const [isRequestingAttributes, setIsRequestingAttributes] = useState(false)
|
|
164
|
+
const [attributeError, setAttributeError] = useState<Error | null>(null)
|
|
165
|
+
|
|
166
|
+
const requestAttributes = useCallback(
|
|
167
|
+
async (params: RequestIdentityAttributesParameters) => {
|
|
168
|
+
setIsRequestingAttributes(true)
|
|
169
|
+
setAttributeError(null)
|
|
170
|
+
try {
|
|
171
|
+
const result = await clientManager.requestIdentityAttributes(params)
|
|
172
|
+
setAttributes(result)
|
|
173
|
+
return result
|
|
174
|
+
} catch (error) {
|
|
175
|
+
setAttributeError(error as Error)
|
|
176
|
+
throw error
|
|
177
|
+
} finally {
|
|
178
|
+
setIsRequestingAttributes(false)
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
[]
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
const requestOpenIdAttributes = useCallback(
|
|
185
|
+
async (params: RequestOpenIdIdentityAttributesParameters) => {
|
|
186
|
+
setIsRequestingAttributes(true)
|
|
187
|
+
setAttributeError(null)
|
|
188
|
+
try {
|
|
189
|
+
const result =
|
|
190
|
+
await clientManager.requestOpenIdIdentityAttributes(params)
|
|
191
|
+
setAttributes(result)
|
|
192
|
+
return result
|
|
193
|
+
} catch (error) {
|
|
194
|
+
setAttributeError(error as Error)
|
|
195
|
+
throw error
|
|
196
|
+
} finally {
|
|
197
|
+
setIsRequestingAttributes(false)
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
[]
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
const clearAttributes = useCallback(() => {
|
|
204
|
+
setAttributes(null)
|
|
205
|
+
setAttributeError(null)
|
|
206
|
+
}, [])
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
requestAttributes,
|
|
210
|
+
requestOpenIdAttributes,
|
|
211
|
+
attributes,
|
|
212
|
+
isRequestingAttributes,
|
|
213
|
+
attributeError,
|
|
214
|
+
clearAttributes,
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
132
218
|
return {
|
|
133
219
|
useAuth,
|
|
134
220
|
useAgentState,
|
|
135
221
|
useUserPrincipal,
|
|
222
|
+
useIdentityAttributes,
|
|
136
223
|
}
|
|
137
224
|
}
|
|
@@ -43,8 +43,7 @@ import {
|
|
|
43
43
|
} from "@tanstack/react-query"
|
|
44
44
|
import { CallConfig } from "@icp-sdk/core/agent"
|
|
45
45
|
import { NoInfer } from "./types"
|
|
46
|
-
|
|
47
|
-
const FACTORY_KEY_ARGS_QUERY_KEY = "__ic_reactor_factory_key_args"
|
|
46
|
+
import { mergeFactoryQueryKey } from "./utils"
|
|
48
47
|
|
|
49
48
|
type InfiniteQueryFactoryFn<
|
|
50
49
|
A,
|
|
@@ -63,22 +62,6 @@ type InfiniteQueryFactoryFn<
|
|
|
63
62
|
>
|
|
64
63
|
}
|
|
65
64
|
|
|
66
|
-
const mergeFactoryQueryKey = (
|
|
67
|
-
baseQueryKey?: QueryKey,
|
|
68
|
-
keyArgs?: unknown
|
|
69
|
-
): QueryKey | undefined => {
|
|
70
|
-
const merged: unknown[] = []
|
|
71
|
-
|
|
72
|
-
if (baseQueryKey) {
|
|
73
|
-
merged.push(...baseQueryKey)
|
|
74
|
-
}
|
|
75
|
-
if (keyArgs !== undefined) {
|
|
76
|
-
merged.push({ [FACTORY_KEY_ARGS_QUERY_KEY]: keyArgs })
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return merged.length > 0 ? merged : undefined
|
|
80
|
-
}
|
|
81
|
-
|
|
82
65
|
// ============================================================================
|
|
83
66
|
// Type Definitions
|
|
84
67
|
// ============================================================================
|
|
@@ -510,7 +493,7 @@ export function createInfiniteQueryFactory<
|
|
|
510
493
|
) => {
|
|
511
494
|
const initialArgs = getArgs(config.initialPageParam)
|
|
512
495
|
const keyArgs = config.getKeyArgs?.(initialArgs) ?? initialArgs
|
|
513
|
-
const queryKey = mergeFactoryQueryKey(config.queryKey, keyArgs)
|
|
496
|
+
const queryKey = mergeFactoryQueryKey(config.queryKey, undefined, keyArgs)
|
|
514
497
|
|
|
515
498
|
return createInfiniteQueryImpl<A, M, T, TPageParam, TSelected>(reactor, {
|
|
516
499
|
...(({ getKeyArgs: _getKeyArgs, ...rest }) => rest)(
|
package/src/createMutation.ts
CHANGED
|
@@ -30,6 +30,22 @@ import type {
|
|
|
30
30
|
NoInfer,
|
|
31
31
|
} from "./types"
|
|
32
32
|
|
|
33
|
+
// ============================================================================
|
|
34
|
+
// Internal helpers
|
|
35
|
+
// ============================================================================
|
|
36
|
+
|
|
37
|
+
/** Invalidate a list of query keys in parallel, filtering out undefineds. */
|
|
38
|
+
async function invalidateAll(
|
|
39
|
+
queryClient: Reactor<any, any>["queryClient"],
|
|
40
|
+
keys: (import("@tanstack/react-query").QueryKey | undefined)[]
|
|
41
|
+
): Promise<void> {
|
|
42
|
+
await Promise.all(
|
|
43
|
+
keys.map((queryKey) =>
|
|
44
|
+
queryKey ? queryClient.invalidateQueries({ queryKey }) : Promise.resolve()
|
|
45
|
+
)
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
33
49
|
// ============================================================================
|
|
34
50
|
// Internal Implementation
|
|
35
51
|
// ============================================================================
|
|
@@ -52,113 +68,80 @@ const createMutationImpl = <
|
|
|
52
68
|
...factoryOptions
|
|
53
69
|
} = config
|
|
54
70
|
|
|
55
|
-
|
|
71
|
+
/**
|
|
72
|
+
* Raw call without any invalidation logic.
|
|
73
|
+
* Used as mutationFn so that onSuccess handles all post-mutation work
|
|
74
|
+
* and there is no double-invalidation.
|
|
75
|
+
*/
|
|
76
|
+
const callFn = (
|
|
77
|
+
args: ReactorArgs<A, M, T>
|
|
78
|
+
): Promise<ReactorReturnOk<A, M, T>> =>
|
|
79
|
+
reactor.callMethod({ functionName, args, callConfig })
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Imperative execution for non-React usage.
|
|
83
|
+
* Calls the canister method and invalidates factory-level queries.
|
|
84
|
+
* Use this in route loaders, scripts, or server-side code.
|
|
85
|
+
*/
|
|
56
86
|
const execute = async (
|
|
57
87
|
args: ReactorArgs<A, M, T>
|
|
58
88
|
): Promise<ReactorReturnOk<A, M, T>> => {
|
|
59
|
-
const result = await
|
|
60
|
-
functionName,
|
|
61
|
-
args,
|
|
62
|
-
callConfig,
|
|
63
|
-
})
|
|
64
|
-
|
|
89
|
+
const result = await callFn(args)
|
|
65
90
|
if (factoryInvalidateQueries) {
|
|
66
|
-
await
|
|
67
|
-
factoryInvalidateQueries.map((queryKey) => {
|
|
68
|
-
return reactor.queryClient.invalidateQueries({ queryKey })
|
|
69
|
-
})
|
|
70
|
-
)
|
|
91
|
+
await invalidateAll(reactor.queryClient, factoryInvalidateQueries)
|
|
71
92
|
}
|
|
72
|
-
|
|
73
93
|
return result
|
|
74
94
|
}
|
|
75
95
|
|
|
76
96
|
// Hook implementation
|
|
77
97
|
const useMutationHook = (options?: MutationHookOptions<A, M, T>) => {
|
|
78
|
-
const baseOptions = reactor.getQueryOptions({
|
|
79
|
-
functionName,
|
|
80
|
-
})
|
|
81
|
-
// Extract our custom options
|
|
98
|
+
const baseOptions = reactor.getQueryOptions({ functionName })
|
|
82
99
|
const {
|
|
83
100
|
invalidateQueries: hookInvalidateQueries,
|
|
84
101
|
onCanisterError: hookOnCanisterError,
|
|
85
102
|
...restOptions
|
|
86
|
-
} = options
|
|
103
|
+
} = options ?? {}
|
|
87
104
|
|
|
88
105
|
return useMutation(
|
|
89
106
|
{
|
|
90
107
|
mutationKey: baseOptions.queryKey,
|
|
91
108
|
...factoryOptions,
|
|
92
109
|
...restOptions,
|
|
93
|
-
|
|
110
|
+
// Use callFn (not execute) to avoid double-invalidation:
|
|
111
|
+
// factoryInvalidateQueries are handled in onSuccess below.
|
|
112
|
+
mutationFn: callFn,
|
|
94
113
|
onSuccess: async (...args) => {
|
|
95
|
-
// 1.
|
|
114
|
+
// 1. Factory-level invalidation
|
|
96
115
|
if (factoryInvalidateQueries) {
|
|
97
|
-
await
|
|
98
|
-
factoryInvalidateQueries.map((queryKey) => {
|
|
99
|
-
return reactor.queryClient.invalidateQueries({ queryKey })
|
|
100
|
-
})
|
|
101
|
-
)
|
|
116
|
+
await invalidateAll(reactor.queryClient, factoryInvalidateQueries)
|
|
102
117
|
}
|
|
103
|
-
|
|
104
|
-
// 2. Handle hook-level invalidateQueries
|
|
118
|
+
// 2. Hook-level invalidation
|
|
105
119
|
if (hookInvalidateQueries) {
|
|
106
|
-
await
|
|
107
|
-
hookInvalidateQueries.map((queryKey) => {
|
|
108
|
-
if (queryKey) {
|
|
109
|
-
return reactor.queryClient.invalidateQueries({ queryKey })
|
|
110
|
-
}
|
|
111
|
-
return Promise.resolve()
|
|
112
|
-
})
|
|
113
|
-
)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// 3. Call factory onSuccess
|
|
117
|
-
if (factoryOnSuccess) {
|
|
118
|
-
await factoryOnSuccess(...args)
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// 4. Call hook-local onSuccess
|
|
122
|
-
if (restOptions?.onSuccess) {
|
|
123
|
-
await restOptions.onSuccess(...args)
|
|
120
|
+
await invalidateAll(reactor.queryClient, hookInvalidateQueries)
|
|
124
121
|
}
|
|
122
|
+
// 3. Factory onSuccess
|
|
123
|
+
await factoryOnSuccess?.(...args)
|
|
124
|
+
// 4. Hook onSuccess
|
|
125
|
+
await restOptions.onSuccess?.(...args)
|
|
125
126
|
},
|
|
126
127
|
onError: (error, variables, context, mutation) => {
|
|
127
|
-
// Check if this is a CanisterError (from Result { Err: E })
|
|
128
128
|
if (isCanisterError(error)) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
factoryOnCanisterError(error, variables)
|
|
132
|
-
}
|
|
133
|
-
// 2. Call hook-level onCanisterError
|
|
134
|
-
if (hookOnCanisterError) {
|
|
135
|
-
hookOnCanisterError(error, variables)
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// 3. Call factory-level onError (for all errors)
|
|
140
|
-
if (factoryOnError) {
|
|
141
|
-
factoryOnError(error, variables, context, mutation)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// 4. Call hook-level onError (for all errors)
|
|
145
|
-
if (restOptions?.onError) {
|
|
146
|
-
restOptions.onError(error, variables, context, mutation)
|
|
129
|
+
factoryOnCanisterError?.(error, variables)
|
|
130
|
+
hookOnCanisterError?.(error, variables)
|
|
147
131
|
}
|
|
132
|
+
factoryOnError?.(error, variables, context, mutation)
|
|
133
|
+
restOptions.onError?.(error, variables, context, mutation)
|
|
148
134
|
},
|
|
149
135
|
},
|
|
150
136
|
reactor.queryClient
|
|
151
137
|
)
|
|
152
138
|
}
|
|
153
139
|
|
|
154
|
-
return {
|
|
155
|
-
useMutation: useMutationHook,
|
|
156
|
-
execute,
|
|
157
|
-
}
|
|
140
|
+
return { useMutation: useMutationHook, execute }
|
|
158
141
|
}
|
|
159
142
|
|
|
160
143
|
// ============================================================================
|
|
161
|
-
// Factory Function
|
|
144
|
+
// Public Factory Function
|
|
162
145
|
// ============================================================================
|
|
163
146
|
|
|
164
147
|
export function createMutation<
|