@ic-reactor/react 3.7.0 → 3.9.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 +158 -9
- package/dist/auth/authentication-manager.d.ts +37 -5
- package/dist/auth/authentication-manager.d.ts.map +1 -1
- package/dist/auth/authentication-manager.js +242 -52
- package/dist/auth/authentication-manager.js.map +1 -1
- package/dist/auth/createIdentityAttributeHooks.d.ts +2 -2
- package/dist/auth/createIdentityAttributeHooks.d.ts.map +1 -1
- package/dist/auth/identity-attributes-manager.d.ts +12 -5
- package/dist/auth/identity-attributes-manager.d.ts.map +1 -1
- package/dist/auth/identity-attributes-manager.js +42 -28
- package/dist/auth/identity-attributes-manager.js.map +1 -1
- package/dist/auth/identity-attributes.d.ts +1 -1
- package/dist/auth/identity-attributes.d.ts.map +1 -1
- package/dist/auth/identity-attributes.js +1 -1
- package/dist/auth/identity-attributes.js.map +1 -1
- package/dist/auth/index.d.ts +7 -7
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +7 -7
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/types.d.ts +123 -16
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/createActorHooks.d.ts +4 -4
- package/dist/createActorHooks.d.ts.map +1 -1
- package/dist/createActorHooks.js +6 -6
- package/dist/createActorHooks.js.map +1 -1
- package/dist/createInfiniteQuery.d.ts +12 -10
- package/dist/createInfiniteQuery.d.ts.map +1 -1
- package/dist/createInfiniteQuery.js +20 -17
- package/dist/createInfiniteQuery.js.map +1 -1
- package/dist/createMutation.d.ts +1 -1
- package/dist/createMutation.d.ts.map +1 -1
- package/dist/createMutation.js +22 -2
- package/dist/createMutation.js.map +1 -1
- package/dist/createQuery.d.ts +1 -1
- package/dist/createQuery.d.ts.map +1 -1
- package/dist/createQuery.js +6 -2
- package/dist/createQuery.js.map +1 -1
- package/dist/createSuspenseInfiniteQuery.d.ts +11 -10
- package/dist/createSuspenseInfiniteQuery.d.ts.map +1 -1
- package/dist/createSuspenseInfiniteQuery.js +18 -16
- package/dist/createSuspenseInfiniteQuery.js.map +1 -1
- package/dist/createSuspenseQuery.d.ts +1 -1
- package/dist/createSuspenseQuery.d.ts.map +1 -1
- package/dist/createSuspenseQuery.js +6 -2
- package/dist/createSuspenseQuery.js.map +1 -1
- package/dist/defineReactor.d.ts +54 -4
- package/dist/defineReactor.d.ts.map +1 -1
- package/dist/defineReactor.js +77 -7
- package/dist/defineReactor.js.map +1 -1
- package/dist/hooks/createAuthHooks.d.ts +2 -2
- package/dist/hooks/createAuthHooks.d.ts.map +1 -1
- package/dist/hooks/createAuthHooks.js +7 -2
- package/dist/hooks/createAuthHooks.js.map +1 -1
- package/dist/hooks/useActorInfiniteQuery.d.ts.map +1 -1
- package/dist/hooks/useActorInfiniteQuery.js +16 -2
- package/dist/hooks/useActorInfiniteQuery.js.map +1 -1
- package/dist/hooks/useActorMethod.d.ts.map +1 -1
- package/dist/hooks/useActorMethod.js +66 -33
- package/dist/hooks/useActorMethod.js.map +1 -1
- package/dist/hooks/useActorMutation.d.ts +8 -1
- package/dist/hooks/useActorMutation.d.ts.map +1 -1
- package/dist/hooks/useActorMutation.js +9 -1
- package/dist/hooks/useActorMutation.js.map +1 -1
- package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts.map +1 -1
- package/dist/hooks/useActorSuspenseInfiniteQuery.js +16 -2
- package/dist/hooks/useActorSuspenseInfiniteQuery.js.map +1 -1
- package/dist/index.d.ts +11 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -11
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +8 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +14 -5
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +21 -10
- package/dist/utils.js.map +1 -1
- package/llms.txt +1 -1
- package/package.json +14 -9
- package/src/auth/authentication-manager.ts +267 -64
- package/src/auth/createIdentityAttributeHooks.ts +2 -2
- package/src/auth/identity-attributes-manager.ts +53 -44
- package/src/auth/identity-attributes.ts +2 -2
- package/src/auth/index.ts +7 -7
- package/src/auth/types.ts +134 -18
- package/src/createActorHooks.ts +9 -9
- package/src/createInfiniteQuery.ts +49 -35
- package/src/createMutation.ts +33 -3
- package/src/createQuery.ts +10 -3
- package/src/createSuspenseInfiniteQuery.ts +45 -33
- package/src/createSuspenseQuery.ts +10 -3
- package/src/defineReactor.ts +136 -14
- package/src/hooks/createAuthHooks.ts +8 -3
- package/src/hooks/useActorInfiniteQuery.ts +25 -3
- package/src/hooks/useActorMethod.ts +82 -34
- package/src/hooks/useActorMutation.ts +19 -4
- package/src/hooks/useActorSuspenseInfiniteQuery.ts +25 -3
- package/src/index.ts +11 -11
- package/src/types.ts +8 -2
- package/src/utils.ts +21 -10
- package/src/defineReactor.test.ts +0 -70
- package/src/hooks/useActorInfiniteQuery.test.tsx +0 -457
- package/src/hooks/useActorMethod.test.tsx +0 -798
- package/src/hooks/useActorMutation.test.tsx +0 -220
- package/src/hooks/useActorQuery.test.tsx +0 -287
- package/src/hooks/useActorSuspenseInfiniteQuery.test.tsx +0 -472
- package/src/hooks/useActorSuspenseQuery.test.tsx +0 -254
- package/src/utils.test.ts +0 -14
package/src/auth/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export * from "./authentication-manager"
|
|
2
|
-
export * from "./identity-attributes-manager"
|
|
3
|
-
export * from "./identity-attributes"
|
|
4
|
-
export * from "./constants"
|
|
5
|
-
export * from "./types"
|
|
6
|
-
export * from "../hooks/createAuthHooks"
|
|
7
|
-
export * from "./createIdentityAttributeHooks"
|
|
1
|
+
export * from "./authentication-manager.js"
|
|
2
|
+
export * from "./identity-attributes-manager.js"
|
|
3
|
+
export * from "./identity-attributes.js"
|
|
4
|
+
export * from "./constants.js"
|
|
5
|
+
export * from "./types.js"
|
|
6
|
+
export * from "../hooks/createAuthHooks.js"
|
|
7
|
+
export * from "./createIdentityAttributeHooks.js"
|
package/src/auth/types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { Identity } from "@icp-sdk/core/agent"
|
|
1
|
+
import type { Identity, SignIdentity } from "@icp-sdk/core/agent"
|
|
2
|
+
import type { PartialIdentity } from "@icp-sdk/core/identity"
|
|
2
3
|
import type { Principal } from "@icp-sdk/core/principal"
|
|
3
4
|
|
|
4
5
|
export interface SignedIdentityAttributes {
|
|
@@ -6,9 +7,21 @@ export interface SignedIdentityAttributes {
|
|
|
6
7
|
signature: Uint8Array
|
|
7
8
|
}
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* The 32-byte nonce issued by the relying-party canister.
|
|
12
|
+
*
|
|
13
|
+
* A function (or promise) lets the Internet Identity window open while the
|
|
14
|
+
* nonce is still being fetched, so the popup is opened inside the user gesture
|
|
15
|
+
* instead of after it. `@icp-sdk/auth` v8 requires the function form; v7
|
|
16
|
+
* accepts the promise form. IC Reactor normalizes whichever you pass to the
|
|
17
|
+
* shape the installed client expects.
|
|
18
|
+
*/
|
|
19
|
+
export type IdentityAttributeNonce =
|
|
20
|
+
Uint8Array | Promise<Uint8Array> | (() => Uint8Array | Promise<Uint8Array>)
|
|
21
|
+
|
|
9
22
|
export interface IdentityAttributeRequest {
|
|
10
23
|
keys: string[]
|
|
11
|
-
nonce:
|
|
24
|
+
nonce: IdentityAttributeNonce
|
|
12
25
|
}
|
|
13
26
|
|
|
14
27
|
export interface IdentityAttributeValues {
|
|
@@ -26,15 +39,102 @@ export interface IdentityAttributeResult {
|
|
|
26
39
|
completedAt: string
|
|
27
40
|
}
|
|
28
41
|
|
|
42
|
+
/**
|
|
43
|
+
* The providers that Internet Identity supports for one-click sign-in. Only
|
|
44
|
+
* these three reach the underlying auth client.
|
|
45
|
+
*/
|
|
29
46
|
type IdentityAttributeOpenIdProviderAlias = "google" | "apple" | "microsoft"
|
|
30
47
|
|
|
48
|
+
/**
|
|
49
|
+
* An OpenID provider, either as a well-known alias or as a raw issuer URL.
|
|
50
|
+
*
|
|
51
|
+
* The two forms are **not** interchangeable, and what each one does depends on
|
|
52
|
+
* where it is passed:
|
|
53
|
+
*
|
|
54
|
+
* - An alias (`"google" | "apple" | "microsoft"`) is forwarded to the auth
|
|
55
|
+
* client, which adds an `openid` search param to the identity provider URL so
|
|
56
|
+
* the user signs in through that provider directly.
|
|
57
|
+
* - Any other string is treated as a raw issuer URL and is **never** forwarded
|
|
58
|
+
* to the auth client, so it never affects how the user signs in. It is only
|
|
59
|
+
* meaningful when passed to {@link IdentityAttributesManager.requestOpenId}
|
|
60
|
+
* (or `identityAttributeKeys`), which scopes the requested keys to
|
|
61
|
+
* `openid:<issuer>:<key>`. Passed anywhere else it has no effect at all.
|
|
62
|
+
*/
|
|
31
63
|
export type IdentityAttributeOpenIdProvider =
|
|
32
64
|
IdentityAttributeOpenIdProviderAlias | (string & {})
|
|
33
65
|
|
|
66
|
+
/** Session key algorithm used for each sign-in. */
|
|
67
|
+
export type AuthClientKeyType = "ECDSA" | "Ed25519"
|
|
68
|
+
|
|
69
|
+
/** Structural shape of an `@icp-sdk/auth` `AuthClientStorage`. */
|
|
70
|
+
export interface AuthClientStorageLike {
|
|
71
|
+
get(key: string): Promise<string | CryptoKeyPair | null>
|
|
72
|
+
set(key: string, value: string | CryptoKeyPair): Promise<void>
|
|
73
|
+
remove(key: string): Promise<void>
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Idle-session configuration forwarded to the underlying AuthClient. */
|
|
77
|
+
export interface AuthClientIdleOptions {
|
|
78
|
+
/** Called once the user has been idle for `idleTimeout` ms. */
|
|
79
|
+
onIdle?: () => unknown
|
|
80
|
+
/** Idle timeout in ms. @default 600_000 (10 minutes) */
|
|
81
|
+
idleTimeout?: number
|
|
82
|
+
/** Treat scroll events as activity. @default false */
|
|
83
|
+
captureScroll?: boolean
|
|
84
|
+
/** Scroll debounce in ms. @default 100 */
|
|
85
|
+
scrollDebounce?: number
|
|
86
|
+
/** Disable idle detection entirely. @default false */
|
|
87
|
+
disableIdle?: boolean
|
|
88
|
+
/**
|
|
89
|
+
* Disable the built-in idle callback, which signs the user out and reloads
|
|
90
|
+
* the page. @default false
|
|
91
|
+
*/
|
|
92
|
+
disableDefaultIdleCallback?: boolean
|
|
93
|
+
}
|
|
94
|
+
|
|
34
95
|
export interface AuthenticationClientOptions {
|
|
96
|
+
/** Identity provider URL. @default "https://id.ai/authorize" */
|
|
35
97
|
identityProvider?: string | URL
|
|
98
|
+
/** `window.open` features string for the identity provider popup. */
|
|
36
99
|
windowOpenerFeatures?: string
|
|
100
|
+
/**
|
|
101
|
+
* Provider for one-click sign-in.
|
|
102
|
+
*
|
|
103
|
+
* Only the `"google" | "apple" | "microsoft"` aliases reach the auth client.
|
|
104
|
+
* Any other value is dropped here and has **no effect** — raw issuer URLs are
|
|
105
|
+
* only meaningful on `requestOpenId`, where they scope the requested keys.
|
|
106
|
+
* @see {@link IdentityAttributeOpenIdProvider}
|
|
107
|
+
*/
|
|
37
108
|
openIdProvider?: IdentityAttributeOpenIdProvider
|
|
109
|
+
/**
|
|
110
|
+
* Derivation origin for the identity provider.
|
|
111
|
+
*
|
|
112
|
+
* Required when the app is served from more than one origin (e.g. a custom
|
|
113
|
+
* domain plus `<canisterId>.icp0.io`) and every origin must resolve to the
|
|
114
|
+
* same principal.
|
|
115
|
+
* @see https://github.com/dfinity/internet-identity/blob/main/docs/internet-identity-spec.adoc
|
|
116
|
+
*/
|
|
117
|
+
derivationOrigin?: string | URL
|
|
118
|
+
/** Persistent storage backend. @default IndexedDB */
|
|
119
|
+
storage?: AuthClientStorageLike
|
|
120
|
+
/**
|
|
121
|
+
* Session key algorithm. Use `"Ed25519"` when the storage backend cannot
|
|
122
|
+
* hold a `CryptoKey`. @default "ECDSA"
|
|
123
|
+
*/
|
|
124
|
+
keyType?: AuthClientKeyType
|
|
125
|
+
/**
|
|
126
|
+
* Idle timeout configuration. Pass `{ disableIdle: true }` to opt out of the
|
|
127
|
+
* default behaviour, which signs the user out and reloads after 10 minutes.
|
|
128
|
+
*/
|
|
129
|
+
idleOptions?: AuthClientIdleOptions
|
|
130
|
+
/** An existing identity to authenticate via delegation. */
|
|
131
|
+
identity?: SignIdentity | PartialIdentity
|
|
132
|
+
/**
|
|
133
|
+
* How the client talks to the identity provider. `"redirect"` requires
|
|
134
|
+
* `@icp-sdk/auth` v8 or later and is ignored by older versions.
|
|
135
|
+
* @default "window"
|
|
136
|
+
*/
|
|
137
|
+
transport?: "window" | "redirect"
|
|
38
138
|
}
|
|
39
139
|
|
|
40
140
|
export interface AuthClientSignInOptions {
|
|
@@ -48,36 +148,52 @@ export interface AuthenticationSignInOptions
|
|
|
48
148
|
onError?: (error?: string) => void | Promise<void>
|
|
49
149
|
}
|
|
50
150
|
|
|
51
|
-
export interface RequestIdentityAttributesParameters {
|
|
151
|
+
export interface RequestIdentityAttributesParameters extends AuthenticationClientOptions {
|
|
52
152
|
keys: string[]
|
|
53
|
-
nonce:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
153
|
+
nonce: IdentityAttributeNonce
|
|
154
|
+
/**
|
|
155
|
+
* Sign in as part of the same identity-provider interaction.
|
|
156
|
+
* @default true
|
|
157
|
+
*/
|
|
57
158
|
signIn?: boolean
|
|
58
159
|
maxTimeToLive?: bigint
|
|
59
160
|
targets?: Principal[]
|
|
60
161
|
}
|
|
61
162
|
|
|
62
|
-
export interface RequestOpenIdIdentityAttributesParameters
|
|
63
|
-
|
|
163
|
+
export interface RequestOpenIdIdentityAttributesParameters extends Omit<
|
|
164
|
+
RequestIdentityAttributesParameters,
|
|
165
|
+
"openIdProvider"
|
|
166
|
+
> {
|
|
167
|
+
/**
|
|
168
|
+
* Provider the requested `keys` are scoped to, producing
|
|
169
|
+
* `openid:<issuer>:<key>`.
|
|
170
|
+
*
|
|
171
|
+
* Passing one of the `"google" | "apple" | "microsoft"` aliases additionally
|
|
172
|
+
* routes sign-in through that provider, so the user grants attribute access
|
|
173
|
+
* in the same step. A raw issuer URL only scopes the keys.
|
|
174
|
+
* @see {@link IdentityAttributeOpenIdProvider}
|
|
175
|
+
*/
|
|
64
176
|
openIdProvider: IdentityAttributeOpenIdProvider
|
|
65
|
-
keys: string[]
|
|
66
|
-
identityProvider?: string | URL
|
|
67
|
-
windowOpenerFeatures?: string
|
|
68
|
-
signIn?: boolean
|
|
69
|
-
maxTimeToLive?: bigint
|
|
70
|
-
targets?: Principal[]
|
|
71
177
|
}
|
|
72
178
|
|
|
179
|
+
/**
|
|
180
|
+
* Structural subset of `@icp-sdk/auth`'s `AuthClient` that IC Reactor relies
|
|
181
|
+
* on. Declared locally so `@icp-sdk/auth` stays an optional peer dependency.
|
|
182
|
+
*/
|
|
73
183
|
export interface AuthClientLike {
|
|
74
184
|
getIdentity(): Promise<Identity> | Identity
|
|
75
185
|
isAuthenticated(): Promise<boolean> | boolean
|
|
76
186
|
signIn(options?: AuthClientSignInOptions): Promise<Identity>
|
|
77
187
|
signOut(options?: { returnTo?: string }): Promise<void>
|
|
78
|
-
requestAttributes(
|
|
79
|
-
|
|
80
|
-
|
|
188
|
+
requestAttributes(params: {
|
|
189
|
+
keys: string[]
|
|
190
|
+
// Widened across `@icp-sdk/auth` v7 (value or promise) and v8 (thunk).
|
|
191
|
+
// Method-shorthand parameters are bivariant, so both real signatures are
|
|
192
|
+
// assignable to this one.
|
|
193
|
+
nonce: Uint8Array | Promise<Uint8Array> | (() => Promise<Uint8Array>)
|
|
194
|
+
}): Promise<SignedIdentityAttributes>
|
|
195
|
+
/** Present from `@icp-sdk/auth` v8; used to detect the nonce contract. */
|
|
196
|
+
memoize?<T>(produce: () => T | Promise<T>): Promise<T>
|
|
81
197
|
}
|
|
82
198
|
|
|
83
199
|
export interface AuthState {
|
package/src/createActorHooks.ts
CHANGED
|
@@ -34,18 +34,18 @@ import {
|
|
|
34
34
|
UseMutationResult,
|
|
35
35
|
InfiniteData,
|
|
36
36
|
} from "@tanstack/react-query"
|
|
37
|
-
import { useActorQuery } from "./hooks/useActorQuery"
|
|
38
|
-
import { useActorSuspenseQuery } from "./hooks/useActorSuspenseQuery"
|
|
39
|
-
import { useActorInfiniteQuery } from "./hooks/useActorInfiniteQuery"
|
|
40
|
-
import { useActorSuspenseInfiniteQuery } from "./hooks/useActorSuspenseInfiniteQuery"
|
|
41
|
-
import { useActorMutation } from "./hooks/useActorMutation"
|
|
37
|
+
import { useActorQuery } from "./hooks/useActorQuery.js"
|
|
38
|
+
import { useActorSuspenseQuery } from "./hooks/useActorSuspenseQuery.js"
|
|
39
|
+
import { useActorInfiniteQuery } from "./hooks/useActorInfiniteQuery.js"
|
|
40
|
+
import { useActorSuspenseInfiniteQuery } from "./hooks/useActorSuspenseInfiniteQuery.js"
|
|
41
|
+
import { useActorMutation } from "./hooks/useActorMutation.js"
|
|
42
42
|
import {
|
|
43
43
|
useActorMethod,
|
|
44
44
|
UseActorMethodParameters,
|
|
45
|
-
} from "./hooks/useActorMethod"
|
|
46
|
-
import { InfiniteQueryConfig } from "./createInfiniteQuery"
|
|
47
|
-
import { SuspenseInfiniteQueryConfig } from "./createSuspenseInfiniteQuery"
|
|
48
|
-
import { QueryConfig, SuspenseQueryConfig, MutationConfig } from "./types"
|
|
45
|
+
} from "./hooks/useActorMethod.js"
|
|
46
|
+
import { InfiniteQueryConfig } from "./createInfiniteQuery.js"
|
|
47
|
+
import { SuspenseInfiniteQueryConfig } from "./createSuspenseInfiniteQuery.js"
|
|
48
|
+
import { QueryConfig, SuspenseQueryConfig, MutationConfig } from "./types.js"
|
|
49
49
|
|
|
50
50
|
export type ActorHooks<Service, Transform extends TransformKey> = {
|
|
51
51
|
useActorQuery: {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* postsQuery.invalidate()
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
+
import { useMemo } from "react"
|
|
25
26
|
import type {
|
|
26
27
|
Reactor,
|
|
27
28
|
FunctionName,
|
|
@@ -43,8 +44,12 @@ import {
|
|
|
43
44
|
InfiniteQueryObserverOptions,
|
|
44
45
|
} from "@tanstack/react-query"
|
|
45
46
|
import { CallConfig } from "@icp-sdk/core/agent"
|
|
46
|
-
import { NoInfer } from "./types"
|
|
47
|
-
import {
|
|
47
|
+
import { NoInfer } from "./types.js"
|
|
48
|
+
import {
|
|
49
|
+
buildChainedSelect,
|
|
50
|
+
mergeFactoryQueryKey,
|
|
51
|
+
normalizeQueryData,
|
|
52
|
+
} from "./utils.js"
|
|
48
53
|
|
|
49
54
|
type InfiniteQueryFactoryFn<
|
|
50
55
|
Service,
|
|
@@ -126,6 +131,16 @@ export interface InfiniteQueryConfig<
|
|
|
126
131
|
initialPageParam: TPageParam
|
|
127
132
|
/** Function to get args from page parameter */
|
|
128
133
|
getArgs: (pageParam: TPageParam) => ReactorArgs<Service, Method, Transform>
|
|
134
|
+
/**
|
|
135
|
+
* Narrows what the cache key derives from the call arguments.
|
|
136
|
+
*
|
|
137
|
+
* By default the key is scoped by `getArgs(initialPageParam)`, so two
|
|
138
|
+
* infinite queries on the same method with different arguments stay in
|
|
139
|
+
* separate cache entries. Supply this when those args embed the cursor and
|
|
140
|
+
* only part of them identifies the query — return the stable, serializable
|
|
141
|
+
* portion (typically everything except the pagination field).
|
|
142
|
+
*/
|
|
143
|
+
getKeyArgs?: (args: ReactorArgs<Service, Method, Transform>) => unknown
|
|
129
144
|
/** Function to determine next page parameter */
|
|
130
145
|
getNextPageParam: (
|
|
131
146
|
lastPage: InfiniteQueryPageData<Service, Method, Transform>,
|
|
@@ -155,17 +170,12 @@ export type InfiniteQueryFactoryConfig<
|
|
|
155
170
|
TPageParam
|
|
156
171
|
>,
|
|
157
172
|
> = Omit<
|
|
173
|
+
// `getArgs` is supplied per factory call. `getKeyArgs` is inherited from
|
|
174
|
+
// InfiniteQueryConfig, which now applies it for every infinite query rather
|
|
175
|
+
// than only for factory-created ones.
|
|
158
176
|
InfiniteQueryConfig<Service, Method, Transform, TPageParam, Selected>,
|
|
159
177
|
"getArgs"
|
|
160
|
-
>
|
|
161
|
-
/**
|
|
162
|
-
* Optional key-args derivation for factory calls.
|
|
163
|
-
* Receives the resolved args from `getArgs(initialPageParam)` and should return
|
|
164
|
-
* a stable serializable representation of the logical query identity
|
|
165
|
-
* (typically excluding pagination/cursor fields).
|
|
166
|
-
*/
|
|
167
|
-
getKeyArgs?: (args: ReactorArgs<Service, Method, Transform>) => unknown
|
|
168
|
-
}
|
|
178
|
+
>
|
|
169
179
|
|
|
170
180
|
// ============================================================================
|
|
171
181
|
// Hook Interface
|
|
@@ -292,6 +302,7 @@ const createInfiniteQueryImpl = <
|
|
|
292
302
|
queryKey: customQueryKey,
|
|
293
303
|
initialPageParam,
|
|
294
304
|
getArgs,
|
|
305
|
+
getKeyArgs,
|
|
295
306
|
getNextPageParam,
|
|
296
307
|
getPreviousPageParam,
|
|
297
308
|
maxPages,
|
|
@@ -300,12 +311,22 @@ const createInfiniteQueryImpl = <
|
|
|
300
311
|
...rest
|
|
301
312
|
} = config
|
|
302
313
|
|
|
303
|
-
//
|
|
314
|
+
// Fold the call arguments into the key. Without them every infinite query on
|
|
315
|
+
// a method shares one cache entry, so two lists with different `getArgs` (a
|
|
316
|
+
// different page size, a different filter) serve each other's pages — the
|
|
317
|
+
// arguments live in a closure, not in the config, so nothing else
|
|
318
|
+
// distinguishes them.
|
|
319
|
+
//
|
|
320
|
+
// The first page's args stand for the query's identity; `getKeyArgs` lets a
|
|
321
|
+
// caller drop the cursor field when it is part of those args.
|
|
304
322
|
const getQueryKey = (): QueryKey => {
|
|
323
|
+
const initialArgs = getArgs(initialPageParam)
|
|
324
|
+
const keyArgs = getKeyArgs?.(initialArgs) ?? initialArgs
|
|
325
|
+
|
|
305
326
|
return reactor.generateQueryKey(
|
|
306
327
|
{
|
|
307
328
|
functionName,
|
|
308
|
-
queryKey: customQueryKey,
|
|
329
|
+
queryKey: mergeFactoryQueryKey(customQueryKey, undefined, keyArgs),
|
|
309
330
|
},
|
|
310
331
|
callConfig
|
|
311
332
|
)
|
|
@@ -368,14 +389,12 @@ const createInfiniteQueryImpl = <
|
|
|
368
389
|
Selected,
|
|
369
390
|
TError
|
|
370
391
|
> = (options: any): any => {
|
|
371
|
-
//
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
return firstPass
|
|
378
|
-
}
|
|
392
|
+
// Memoized and identity-preserving; see buildChainedSelect for why the
|
|
393
|
+
// function identity matters to the observer's select-result cache.
|
|
394
|
+
const chainedSelect = useMemo(
|
|
395
|
+
() => buildChainedSelect(select as never, options?.select),
|
|
396
|
+
[options?.select]
|
|
397
|
+
)
|
|
379
398
|
|
|
380
399
|
return useInfiniteQuery(
|
|
381
400
|
{
|
|
@@ -535,10 +554,8 @@ export function createInfiniteQueryFactory<
|
|
|
535
554
|
> = (
|
|
536
555
|
getArgs: (pageParam: TPageParam) => ReactorArgs<Service, Method, Transform>
|
|
537
556
|
) => {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
const queryKey = mergeFactoryQueryKey(config.queryKey, undefined, keyArgs)
|
|
541
|
-
|
|
557
|
+
// `getKeyArgs` and the args-derived key segment are applied by the impl,
|
|
558
|
+
// which now does it for every infinite query rather than only for factories.
|
|
542
559
|
return createInfiniteQueryImpl<
|
|
543
560
|
Service,
|
|
544
561
|
Method,
|
|
@@ -546,16 +563,13 @@ export function createInfiniteQueryFactory<
|
|
|
546
563
|
TPageParam,
|
|
547
564
|
Selected
|
|
548
565
|
>(reactor, {
|
|
549
|
-
...(
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
>
|
|
557
|
-
),
|
|
558
|
-
queryKey,
|
|
566
|
+
...(config as InfiniteQueryFactoryConfig<
|
|
567
|
+
Service,
|
|
568
|
+
Method,
|
|
569
|
+
Transform,
|
|
570
|
+
TPageParam,
|
|
571
|
+
Selected
|
|
572
|
+
>),
|
|
559
573
|
getArgs,
|
|
560
574
|
})
|
|
561
575
|
}
|
package/src/createMutation.ts
CHANGED
|
@@ -39,7 +39,7 @@ import type {
|
|
|
39
39
|
MutationResult,
|
|
40
40
|
MutationHookOptions,
|
|
41
41
|
NoInfer,
|
|
42
|
-
} from "./types"
|
|
42
|
+
} from "./types.js"
|
|
43
43
|
|
|
44
44
|
// ============================================================================
|
|
45
45
|
// Internal helpers
|
|
@@ -91,16 +91,46 @@ const createMutationImpl = <
|
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
93
|
* Imperative execution for non-React usage.
|
|
94
|
-
*
|
|
94
|
+
*
|
|
95
|
+
* Runs the same factory-level chain the hook does — invalidation, then
|
|
96
|
+
* `onSuccess`, or `onCanisterError`/`onError` on failure — so a mutation
|
|
97
|
+
* object behaves the same through both call paths. Only hook-level callbacks
|
|
98
|
+
* are absent, because there is no hook here to supply them.
|
|
99
|
+
*
|
|
100
|
+
* The error is rethrown after the callbacks run, so `await execute(...)`
|
|
101
|
+
* still rejects for the caller.
|
|
102
|
+
*
|
|
95
103
|
* Use this in route loaders, scripts, or server-side code.
|
|
96
104
|
*/
|
|
97
105
|
const execute = async (
|
|
98
106
|
args: ReactorArgs<Service, Method, Transform>
|
|
99
107
|
): Promise<ReactorReturnOk<Service, Method, Transform>> => {
|
|
100
|
-
|
|
108
|
+
let result: ReactorReturnOk<Service, Method, Transform>
|
|
109
|
+
try {
|
|
110
|
+
result = await callFn(args)
|
|
111
|
+
} catch (error) {
|
|
112
|
+
if (isCanisterError(error)) {
|
|
113
|
+
factoryOnCanisterError?.(error, args)
|
|
114
|
+
}
|
|
115
|
+
// No mutation context or instance exists on the imperative path.
|
|
116
|
+
factoryOnError?.(
|
|
117
|
+
error as Parameters<NonNullable<typeof factoryOnError>>[0],
|
|
118
|
+
args,
|
|
119
|
+
undefined as never,
|
|
120
|
+
undefined as never
|
|
121
|
+
)
|
|
122
|
+
throw error
|
|
123
|
+
}
|
|
124
|
+
|
|
101
125
|
if (factoryInvalidateQueries) {
|
|
102
126
|
await invalidateAll(reactor.queryClient, factoryInvalidateQueries)
|
|
103
127
|
}
|
|
128
|
+
await factoryOnSuccess?.(
|
|
129
|
+
result,
|
|
130
|
+
args,
|
|
131
|
+
undefined as never,
|
|
132
|
+
undefined as never
|
|
133
|
+
)
|
|
104
134
|
return result
|
|
105
135
|
}
|
|
106
136
|
|
package/src/createQuery.ts
CHANGED
|
@@ -30,6 +30,7 @@ import type {
|
|
|
30
30
|
ReactorArgs,
|
|
31
31
|
TransformKey,
|
|
32
32
|
} from "@ic-reactor/core"
|
|
33
|
+
import { useMemo } from "react"
|
|
33
34
|
import {
|
|
34
35
|
QueryKey,
|
|
35
36
|
useQuery,
|
|
@@ -44,8 +45,8 @@ import type {
|
|
|
44
45
|
QueryResult,
|
|
45
46
|
QueryFactoryConfig,
|
|
46
47
|
NoInfer,
|
|
47
|
-
} from "./types"
|
|
48
|
-
import { buildChainedSelect } from "./utils"
|
|
48
|
+
} from "./types.js"
|
|
49
|
+
import { buildChainedSelect } from "./utils.js"
|
|
49
50
|
|
|
50
51
|
// ============================================================================
|
|
51
52
|
// Internal Implementation
|
|
@@ -110,6 +111,12 @@ const createQueryImpl = <
|
|
|
110
111
|
|
|
111
112
|
const useQueryHook = ((options?: UseQueryHookOptions) => {
|
|
112
113
|
const baseOptions = reactor.getQueryOptions(params)
|
|
114
|
+
// Memoized so the observer's select-result cache can hit; see
|
|
115
|
+
// buildChainedSelect. `select` comes from the factory config and is stable.
|
|
116
|
+
const chainedSelect = useMemo(
|
|
117
|
+
() => buildChainedSelect(select, options?.select),
|
|
118
|
+
[options?.select]
|
|
119
|
+
)
|
|
113
120
|
return useQuery(
|
|
114
121
|
{
|
|
115
122
|
queryKey: baseOptions.queryKey,
|
|
@@ -117,7 +124,7 @@ const createQueryImpl = <
|
|
|
117
124
|
...rest,
|
|
118
125
|
...options,
|
|
119
126
|
queryFn: baseOptions.queryFn,
|
|
120
|
-
select:
|
|
127
|
+
select: chainedSelect,
|
|
121
128
|
},
|
|
122
129
|
reactor.queryClient
|
|
123
130
|
)
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
* const allPosts = data.pages.flatMap(page => page.posts)
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
|
+
import { useMemo } from "react"
|
|
27
28
|
import type {
|
|
28
29
|
Reactor,
|
|
29
30
|
FunctionName,
|
|
@@ -45,8 +46,12 @@ import {
|
|
|
45
46
|
InfiniteQueryObserverOptions,
|
|
46
47
|
} from "@tanstack/react-query"
|
|
47
48
|
import { CallConfig } from "@icp-sdk/core/agent"
|
|
48
|
-
import { NoInfer } from "./types"
|
|
49
|
-
import {
|
|
49
|
+
import { NoInfer } from "./types.js"
|
|
50
|
+
import {
|
|
51
|
+
buildChainedSelect,
|
|
52
|
+
mergeFactoryQueryKey,
|
|
53
|
+
normalizeQueryData,
|
|
54
|
+
} from "./utils.js"
|
|
50
55
|
|
|
51
56
|
type SuspenseInfiniteFactoryCallOptions = {
|
|
52
57
|
queryKey?: QueryKey
|
|
@@ -137,6 +142,15 @@ export interface SuspenseInfiniteQueryConfig<
|
|
|
137
142
|
queryKey?: QueryKey
|
|
138
143
|
/** Function to get args from page parameter */
|
|
139
144
|
getArgs: (pageParam: TPageParam) => ReactorArgs<Service, Method, Transform>
|
|
145
|
+
/**
|
|
146
|
+
* Narrows what the cache key derives from the call arguments.
|
|
147
|
+
*
|
|
148
|
+
* By default the key is scoped by `getArgs(initialPageParam)`, so two
|
|
149
|
+
* infinite queries on the same method with different arguments stay in
|
|
150
|
+
* separate cache entries. Supply this when those args embed the cursor and
|
|
151
|
+
* only part of them identifies the query.
|
|
152
|
+
*/
|
|
153
|
+
getKeyArgs?: (args: ReactorArgs<Service, Method, Transform>) => unknown
|
|
140
154
|
}
|
|
141
155
|
|
|
142
156
|
/**
|
|
@@ -152,17 +166,11 @@ export type SuspenseInfiniteQueryFactoryConfig<
|
|
|
152
166
|
TPageParam
|
|
153
167
|
>,
|
|
154
168
|
> = Omit<
|
|
169
|
+
// `getArgs` is supplied per factory call. `getKeyArgs` is inherited from
|
|
170
|
+
// SuspenseInfiniteQueryConfig, which now applies it for every infinite query.
|
|
155
171
|
SuspenseInfiniteQueryConfig<Service, Method, Transform, TPageParam, Selected>,
|
|
156
172
|
"getArgs"
|
|
157
|
-
>
|
|
158
|
-
/**
|
|
159
|
-
* Optional key-args derivation for factory calls.
|
|
160
|
-
* Receives the resolved args from `getArgs(initialPageParam)` and should return
|
|
161
|
-
* a stable serializable representation of the logical query identity
|
|
162
|
-
* (typically excluding pagination/cursor fields).
|
|
163
|
-
*/
|
|
164
|
-
getKeyArgs?: (args: ReactorArgs<Service, Method, Transform>) => unknown
|
|
165
|
-
}
|
|
173
|
+
>
|
|
166
174
|
|
|
167
175
|
// ============================================================================
|
|
168
176
|
// Hook Interface
|
|
@@ -301,6 +309,7 @@ const createSuspenseInfiniteQueryImpl = <
|
|
|
301
309
|
queryKey: customQueryKey,
|
|
302
310
|
initialPageParam,
|
|
303
311
|
getArgs,
|
|
312
|
+
getKeyArgs,
|
|
304
313
|
getNextPageParam,
|
|
305
314
|
getPreviousPageParam,
|
|
306
315
|
maxPages,
|
|
@@ -309,12 +318,18 @@ const createSuspenseInfiniteQueryImpl = <
|
|
|
309
318
|
...rest
|
|
310
319
|
} = config
|
|
311
320
|
|
|
312
|
-
//
|
|
321
|
+
// Fold the call arguments into the key — see the matching comment in
|
|
322
|
+
// createInfiniteQuery. Without them every infinite query on a method shares
|
|
323
|
+
// one cache entry, because the arguments live in a `getArgs` closure rather
|
|
324
|
+
// than in the config.
|
|
313
325
|
const getQueryKey = (): QueryKey => {
|
|
326
|
+
const initialArgs = getArgs(initialPageParam)
|
|
327
|
+
const keyArgs = getKeyArgs?.(initialArgs) ?? initialArgs
|
|
328
|
+
|
|
314
329
|
return reactor.generateQueryKey(
|
|
315
330
|
{
|
|
316
331
|
functionName,
|
|
317
|
-
queryKey: customQueryKey,
|
|
332
|
+
queryKey: mergeFactoryQueryKey(customQueryKey, undefined, keyArgs),
|
|
318
333
|
},
|
|
319
334
|
callConfig
|
|
320
335
|
)
|
|
@@ -370,14 +385,12 @@ const createSuspenseInfiniteQueryImpl = <
|
|
|
370
385
|
Selected,
|
|
371
386
|
TError
|
|
372
387
|
> = (options: any): any => {
|
|
373
|
-
//
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
return firstPass
|
|
380
|
-
}
|
|
388
|
+
// Memoized and identity-preserving; see buildChainedSelect for why the
|
|
389
|
+
// function identity matters to the observer's select-result cache.
|
|
390
|
+
const chainedSelect = useMemo(
|
|
391
|
+
() => buildChainedSelect(select as never, options?.select),
|
|
392
|
+
[options?.select]
|
|
393
|
+
)
|
|
381
394
|
|
|
382
395
|
return useSuspenseInfiniteQuery(
|
|
383
396
|
{
|
|
@@ -550,12 +563,13 @@ export function createSuspenseInfiniteQueryFactory<
|
|
|
550
563
|
getArgs: (pageParam: TPageParam) => ReactorArgs<Service, Method, Transform>,
|
|
551
564
|
options?: SuspenseInfiniteFactoryCallOptions
|
|
552
565
|
) => {
|
|
553
|
-
|
|
554
|
-
|
|
566
|
+
// Only the caller-supplied segments are merged here; the args-derived
|
|
567
|
+
// segment is added by the impl, which does it for every infinite query.
|
|
568
|
+
// Applying it in both places would append the args twice.
|
|
555
569
|
const queryKey = mergeFactoryQueryKey(
|
|
556
570
|
config.queryKey,
|
|
557
571
|
options?.queryKey,
|
|
558
|
-
|
|
572
|
+
undefined
|
|
559
573
|
)
|
|
560
574
|
|
|
561
575
|
return createSuspenseInfiniteQueryImpl<
|
|
@@ -565,15 +579,13 @@ export function createSuspenseInfiniteQueryFactory<
|
|
|
565
579
|
TPageParam,
|
|
566
580
|
Selected
|
|
567
581
|
>(reactor, {
|
|
568
|
-
...(
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
>
|
|
576
|
-
),
|
|
582
|
+
...(config as SuspenseInfiniteQueryFactoryConfig<
|
|
583
|
+
Service,
|
|
584
|
+
Method,
|
|
585
|
+
Transform,
|
|
586
|
+
TPageParam,
|
|
587
|
+
Selected
|
|
588
|
+
>),
|
|
577
589
|
queryKey,
|
|
578
590
|
getArgs,
|
|
579
591
|
} as SuspenseInfiniteQueryConfig<
|