@dynamic-labs-sdk/react-hooks 1.21.1 → 1.22.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/core.cjs +12 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.esm.js +9 -0
- package/dist/core.esm.js.map +1 -0
- package/dist/exports/core.d.ts +8 -0
- package/dist/exports/core.d.ts.map +1 -0
- package/dist/exports/index.d.ts +1 -0
- package/dist/exports/index.d.ts.map +1 -1
- package/dist/hooks/wrappers/functions/useDelegateWaasKeyShares/useDelegateWaasKeyShares.d.ts +1 -0
- package/dist/hooks/wrappers/functions/useDelegateWaasKeyShares/useDelegateWaasKeyShares.d.ts.map +1 -1
- package/dist/hooks/wrappers/functions/useTransferBusinessAccountOwnership/index.d.ts +2 -0
- package/dist/hooks/wrappers/functions/useTransferBusinessAccountOwnership/index.d.ts.map +1 -0
- package/dist/hooks/wrappers/functions/useTransferBusinessAccountOwnership/useTransferBusinessAccountOwnership.d.ts +52 -0
- package/dist/hooks/wrappers/functions/useTransferBusinessAccountOwnership/useTransferBusinessAccountOwnership.d.ts.map +1 -0
- package/dist/index.cjs +150 -379
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +31 -261
- package/dist/index.esm.js.map +1 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/dist/useBaseState-BUOWuhEh.esm.js +263 -0
- package/dist/useBaseState-BUOWuhEh.esm.js.map +1 -0
- package/dist/useBaseState-D4H0aBjD.cjs +310 -0
- package/dist/useBaseState-D4H0aBjD.cjs.map +1 -0
- package/package.json +9 -4
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { BaseError, onEvent, waitForClientInitialized } from "@dynamic-labs-sdk/client";
|
|
2
|
+
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
3
|
+
import { createContext, useContext, useEffect, useMemo, useRef } from "react";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
import { getCore } from "@dynamic-labs-sdk/client/core";
|
|
6
|
+
|
|
7
|
+
//#region package.json
|
|
8
|
+
var name = "@dynamic-labs-sdk/react-hooks";
|
|
9
|
+
var version = "1.22.0";
|
|
10
|
+
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/modules/DynamicContext/DynamicContext.ts
|
|
13
|
+
/**
|
|
14
|
+
* Internal React context for providing the DynamicClient instance to hooks.
|
|
15
|
+
*/
|
|
16
|
+
const DynamicContext = createContext(null);
|
|
17
|
+
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/modules/DynamicContext/DynamicProvider.tsx
|
|
20
|
+
/**
|
|
21
|
+
* Provides the DynamicClient instance to all child hooks.
|
|
22
|
+
*
|
|
23
|
+
* Wrap your application with this provider and pass your client instance
|
|
24
|
+
* so that hooks like useUser, useGetWalletAccounts, etc. can access it.
|
|
25
|
+
*/
|
|
26
|
+
const DynamicProvider = ({ children, client }) => /* @__PURE__ */ jsx(DynamicContext.Provider, {
|
|
27
|
+
value: client,
|
|
28
|
+
children
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/errors/MissingProviderError/MissingProviderError.ts
|
|
33
|
+
var MissingProviderError = class extends BaseError {
|
|
34
|
+
constructor() {
|
|
35
|
+
super({
|
|
36
|
+
cause: null,
|
|
37
|
+
code: "MISSING_PROVIDER",
|
|
38
|
+
docsUrl: null,
|
|
39
|
+
name: "MissingProviderError",
|
|
40
|
+
shortMessage: "Hook must be used within a <DynamicProvider>.\nWrap your component tree with <DynamicProvider client={client}>."
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region src/modules/DynamicContext/useDynamicClient.ts
|
|
47
|
+
/**
|
|
48
|
+
* Internal hook to access the DynamicClient from context.
|
|
49
|
+
* Throws if used outside of DynamicProvider.
|
|
50
|
+
*/
|
|
51
|
+
const useDynamicClient = () => {
|
|
52
|
+
const client = useContext(DynamicContext);
|
|
53
|
+
if (!client) throw new MissingProviderError();
|
|
54
|
+
return client;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/foundation/useBaseMutation/useBaseMutation.ts
|
|
59
|
+
const EMPTY_MIDDLEWARES$1 = [];
|
|
60
|
+
/**
|
|
61
|
+
* Internal hook that wraps a react-query `useMutation` with the conventions
|
|
62
|
+
* used by every async-write hook in this package:
|
|
63
|
+
*
|
|
64
|
+
* - `mutationFn` receives the resolved `DynamicClient` from context plus
|
|
65
|
+
* the caller-supplied `variables`, and is gated behind
|
|
66
|
+
* `waitForClientInitialized`.
|
|
67
|
+
* - Callers pass `mutateParams` to override any default
|
|
68
|
+
* `UseMutationOptions`, so consumers can wire `onSuccess` / `onError` /
|
|
69
|
+
* etc.
|
|
70
|
+
* - Callers pass `middlewares` (named hooks like `invalidatesQueries`) and
|
|
71
|
+
* each contributed `onSuccess` runs, in order, ahead of the consumer's own.
|
|
72
|
+
*
|
|
73
|
+
* Returns the full `useMutation` result — `mutate`, `mutateAsync`, `data`,
|
|
74
|
+
* `error`, `isPending`, `isSuccess`, `reset`, `status`, etc.
|
|
75
|
+
*/
|
|
76
|
+
const useBaseMutation = ({ middlewares = EMPTY_MIDDLEWARES$1, mutationFn, mutateParams }) => {
|
|
77
|
+
const client = useDynamicClient();
|
|
78
|
+
const contributions = middlewares.map((useMiddleware) => useMiddleware());
|
|
79
|
+
return useMutation({
|
|
80
|
+
mutationFn: async (variables) => {
|
|
81
|
+
await waitForClientInitialized(client);
|
|
82
|
+
return mutationFn({
|
|
83
|
+
client,
|
|
84
|
+
variables
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
...mutateParams,
|
|
88
|
+
onSuccess: async (...args) => {
|
|
89
|
+
for (const contribution of contributions) await contribution.onSuccess?.(...args);
|
|
90
|
+
return mutateParams?.onSuccess?.(...args);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
//#endregion
|
|
96
|
+
//#region src/foundation/hookKeyNamespace/hookKeyNamespace.ts
|
|
97
|
+
/**
|
|
98
|
+
* Root namespace shared by every react-query key this package creates.
|
|
99
|
+
* Both `QUERY_KEY_PREFIX` (async-read hooks) and the state-hook key
|
|
100
|
+
* prefix derive from this constant so the namespace is defined exactly
|
|
101
|
+
* once.
|
|
102
|
+
*/
|
|
103
|
+
const HOOK_KEY_NAMESPACE = "@dynamic-labs-sdk/react-hooks";
|
|
104
|
+
|
|
105
|
+
//#endregion
|
|
106
|
+
//#region src/foundation/useBaseQuery/queryKeyPrefix.ts
|
|
107
|
+
/**
|
|
108
|
+
* Shared prefix for all query keys created by `useBaseQuery`. Mutation hooks
|
|
109
|
+
* reference this when invalidating related queries on success.
|
|
110
|
+
*/
|
|
111
|
+
const QUERY_KEY_PREFIX = [HOOK_KEY_NAMESPACE, "query"];
|
|
112
|
+
|
|
113
|
+
//#endregion
|
|
114
|
+
//#region src/foundation/useBaseQuery/useBaseQuery.ts
|
|
115
|
+
/**
|
|
116
|
+
* Stable empty default for the `middlewares` param so the common case never
|
|
117
|
+
* allocates a fresh array per render.
|
|
118
|
+
*/
|
|
119
|
+
const EMPTY_MIDDLEWARES = [];
|
|
120
|
+
/**
|
|
121
|
+
* Internal hook that wraps a react-query `useQuery` with the conventions used
|
|
122
|
+
* by every async-read hook in this package:
|
|
123
|
+
*
|
|
124
|
+
* - `queryFn` receives the resolved `DynamicClient` from context, and is
|
|
125
|
+
* gated behind `waitForClientInitialized` so customers never see "client
|
|
126
|
+
* not ready" errors on first render.
|
|
127
|
+
* - Callers pass `queryParams` to override any default `UseQueryOptions`,
|
|
128
|
+
* so consumers can opt into retries / custom staleTime / etc.
|
|
129
|
+
* - Callers pass `enabled: false` (typically derived from whether their
|
|
130
|
+
* pseudo-required arguments are present) to short-circuit fetches.
|
|
131
|
+
* - Callers pass `middlewares` (named hooks that run reactive subscriptions
|
|
132
|
+
* such as cache invalidation); each runs on every render.
|
|
133
|
+
*
|
|
134
|
+
* Returns the full `useQuery` result — `data`, `error`, `isLoading`,
|
|
135
|
+
* `isFetching`, `isSuccess`, `refetch`, `status`, etc.
|
|
136
|
+
*/
|
|
137
|
+
const useBaseQuery = ({ enabled = true, middlewares = EMPTY_MIDDLEWARES, queryFn, queryKey, queryParams }) => {
|
|
138
|
+
const client = useDynamicClient();
|
|
139
|
+
const fullQueryKey = useMemo(() => [...QUERY_KEY_PREFIX, ...queryKey], [queryKey]);
|
|
140
|
+
middlewares.forEach((useMiddleware) => useMiddleware({ queryKey: fullQueryKey }));
|
|
141
|
+
return useQuery({
|
|
142
|
+
queryFn: async () => {
|
|
143
|
+
await waitForClientInitialized(client);
|
|
144
|
+
return queryFn(client);
|
|
145
|
+
},
|
|
146
|
+
queryKey: fullQueryKey,
|
|
147
|
+
...queryParams,
|
|
148
|
+
enabled: enabled && queryParams?.enabled !== false
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
//#endregion
|
|
153
|
+
//#region src/foundation/useBaseState/useBaseState.ts
|
|
154
|
+
/**
|
|
155
|
+
* Internal hook that bridges Dynamic client event-driven state to React via
|
|
156
|
+
* react-query.
|
|
157
|
+
*
|
|
158
|
+
* - `queryFn` waits for the client to finish initializing (so the first
|
|
159
|
+
* render never sees a half-built client) and then runs `selector`. Init
|
|
160
|
+
* failures surface on `error`.
|
|
161
|
+
* - The supplied `event`(s) invalidate the query whenever they fire — a
|
|
162
|
+
* per-event predicate can narrow this so unrelated fires don't refetch.
|
|
163
|
+
* - Returns the full `useQuery` result so consumers can read every field
|
|
164
|
+
* react-query exposes (`data`, `error`, `isLoading`, `isFetching`,
|
|
165
|
+
* `refetch`, `status`, ...).
|
|
166
|
+
*/
|
|
167
|
+
const normalizeEvents = (event) => {
|
|
168
|
+
if (typeof event !== "string") return event;
|
|
169
|
+
const map = {};
|
|
170
|
+
map[event] = true;
|
|
171
|
+
return map;
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* Per-event subscription wrapper. Extracted from `useBaseState` so the
|
|
175
|
+
* listener generics resolve at the per-event call site rather than as the
|
|
176
|
+
* intersection across all events.
|
|
177
|
+
*/
|
|
178
|
+
const subscribeWithPredicate = ({ event, predicate, onMatch }, client) => {
|
|
179
|
+
const listener = (args) => {
|
|
180
|
+
if (predicate === true || predicate(args)) onMatch();
|
|
181
|
+
};
|
|
182
|
+
return onEvent({
|
|
183
|
+
event,
|
|
184
|
+
listener
|
|
185
|
+
}, client);
|
|
186
|
+
};
|
|
187
|
+
const useBaseState = ({ event, queryKey, queryParams, selector, defaultValue, skipWaitForInit = false }) => {
|
|
188
|
+
const client = useDynamicClient();
|
|
189
|
+
const queryClient = useQueryClient();
|
|
190
|
+
/**
|
|
191
|
+
* Normalize the `event` shorthand into an events map. `useMemo` keyed
|
|
192
|
+
* on a stable JSON shape so callers can pass an inline literal each
|
|
193
|
+
* render without churning the event-subscription effect.
|
|
194
|
+
*/
|
|
195
|
+
const eventsMapJson = useMemo(() => {
|
|
196
|
+
const map = normalizeEvents(event);
|
|
197
|
+
const sortedKeys = Object.keys(map).sort((a, b) => a.localeCompare(b));
|
|
198
|
+
return JSON.stringify(sortedKeys.map((k) => ({
|
|
199
|
+
event: k,
|
|
200
|
+
predicate: map[k] === true ? "__always__" : "__fn__"
|
|
201
|
+
})));
|
|
202
|
+
}, [event]);
|
|
203
|
+
/**
|
|
204
|
+
* Per-event predicates resolved from the latest render. The effect
|
|
205
|
+
* stays subscribed across re-renders (its deps are stable), so it
|
|
206
|
+
* reads predicates through this ref to pick up the most recent
|
|
207
|
+
* functions a caller passed inline.
|
|
208
|
+
*/
|
|
209
|
+
const eventsMapRef = useRef(normalizeEvents(event));
|
|
210
|
+
eventsMapRef.current = normalizeEvents(event);
|
|
211
|
+
/**
|
|
212
|
+
* Stable list of event names the effect needs to subscribe to. Driven
|
|
213
|
+
* by `eventsMapJson` so callers can swap predicate identities each
|
|
214
|
+
* render without re-subscribing.
|
|
215
|
+
*/
|
|
216
|
+
const eventNames = useMemo(() => {
|
|
217
|
+
const map = eventsMapRef.current;
|
|
218
|
+
return Object.keys(map).sort((a, b) => a.localeCompare(b));
|
|
219
|
+
}, [eventsMapJson]);
|
|
220
|
+
const fullQueryKey = useMemo(() => [
|
|
221
|
+
HOOK_KEY_NAMESPACE,
|
|
222
|
+
"state",
|
|
223
|
+
...queryKey
|
|
224
|
+
], [JSON.stringify(queryKey)]);
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
const core = getCore(client);
|
|
227
|
+
const invalidate = () => {
|
|
228
|
+
queryClient.invalidateQueries({ queryKey: fullQueryKey }).catch((invalidateError) => {
|
|
229
|
+
core.logger.error("[useBaseState] invalidateQueries failed", invalidateError);
|
|
230
|
+
});
|
|
231
|
+
};
|
|
232
|
+
const unsubscribers = eventNames.map((eventName) => subscribeWithPredicate({
|
|
233
|
+
event: eventName,
|
|
234
|
+
onMatch: invalidate,
|
|
235
|
+
predicate: (args) => {
|
|
236
|
+
const predicate = eventsMapRef.current[eventName];
|
|
237
|
+
if (predicate === void 0) return false;
|
|
238
|
+
return predicate === true || predicate(args);
|
|
239
|
+
}
|
|
240
|
+
}, client));
|
|
241
|
+
return () => {
|
|
242
|
+
for (const unsubscribe of unsubscribers) unsubscribe();
|
|
243
|
+
};
|
|
244
|
+
}, [
|
|
245
|
+
client,
|
|
246
|
+
eventNames,
|
|
247
|
+
queryClient,
|
|
248
|
+
fullQueryKey
|
|
249
|
+
]);
|
|
250
|
+
return useQuery({
|
|
251
|
+
...queryParams,
|
|
252
|
+
placeholderData: defaultValue,
|
|
253
|
+
queryFn: skipWaitForInit ? () => selector(client) : async () => {
|
|
254
|
+
await waitForClientInitialized(client);
|
|
255
|
+
return selector(client);
|
|
256
|
+
},
|
|
257
|
+
queryKey: fullQueryKey
|
|
258
|
+
});
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
//#endregion
|
|
262
|
+
export { useDynamicClient as a, version as c, useBaseMutation as i, useBaseQuery as n, DynamicProvider as o, QUERY_KEY_PREFIX as r, name as s, useBaseState as t };
|
|
263
|
+
//# sourceMappingURL=useBaseState-BUOWuhEh.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBaseState-BUOWuhEh.esm.js","names":["EMPTY_MIDDLEWARES: ReadonlyArray<MutationMiddleware>","EMPTY_MIDDLEWARES","EMPTY_MIDDLEWARES: ReadonlyArray<QueryMiddleware>","map: UseBaseStateEventsMap"],"sources":["../package.json","../src/modules/DynamicContext/DynamicContext.ts","../src/modules/DynamicContext/DynamicProvider.tsx","../src/errors/MissingProviderError/MissingProviderError.ts","../src/modules/DynamicContext/useDynamicClient.ts","../src/foundation/useBaseMutation/useBaseMutation.ts","../src/foundation/hookKeyNamespace/hookKeyNamespace.ts","../src/foundation/useBaseQuery/queryKeyPrefix.ts","../src/foundation/useBaseQuery/useBaseQuery.ts","../src/foundation/useBaseState/useBaseState.ts"],"sourcesContent":["","import type { DynamicClient } from '@dynamic-labs-sdk/client';\nimport { createContext } from 'react';\n\n/**\n * Internal React context for providing the DynamicClient instance to hooks.\n */\nexport const DynamicContext = createContext<DynamicClient | null>(null);\n","import type { DynamicClient } from '@dynamic-labs-sdk/client';\nimport type { PropsWithChildren } from 'react';\n\nimport { DynamicContext } from './DynamicContext';\n\ntype DynamicProviderProps = PropsWithChildren<{\n client: DynamicClient;\n}>;\n\n/**\n * Provides the DynamicClient instance to all child hooks.\n *\n * Wrap your application with this provider and pass your client instance\n * so that hooks like useUser, useGetWalletAccounts, etc. can access it.\n */\nexport const DynamicProvider = ({ children, client }: DynamicProviderProps) => (\n <DynamicContext.Provider value={client}>{children}</DynamicContext.Provider>\n);\n","import { BaseError } from '@dynamic-labs-sdk/client';\n\nexport class MissingProviderError extends BaseError {\n constructor() {\n super({\n cause: null,\n code: 'MISSING_PROVIDER',\n docsUrl: null,\n name: 'MissingProviderError',\n shortMessage:\n 'Hook must be used within a <DynamicProvider>.\\nWrap your component tree with <DynamicProvider client={client}>.',\n });\n }\n}\n","import type { DynamicClient } from '@dynamic-labs-sdk/client';\nimport { useContext } from 'react';\n\nimport { MissingProviderError } from '../../errors/MissingProviderError/MissingProviderError';\nimport { DynamicContext } from './DynamicContext';\n\n/**\n * Internal hook to access the DynamicClient from context.\n * Throws if used outside of DynamicProvider.\n */\nexport const useDynamicClient = (): DynamicClient => {\n const client = useContext(DynamicContext);\n\n if (!client) {\n throw new MissingProviderError();\n }\n\n return client;\n};\n","import type { DynamicClient } from '@dynamic-labs-sdk/client';\nimport { waitForClientInitialized } from '@dynamic-labs-sdk/client';\nimport type {\n UseMutationOptions,\n UseMutationResult,\n} from '@tanstack/react-query';\nimport { useMutation } from '@tanstack/react-query';\n\nimport { useDynamicClient } from '../../modules/DynamicContext';\n\n/**\n * A mutation middleware is a named hook that contributes mutation lifecycle\n * behavior — currently an `onSuccess` handler merged into the react-query\n * options.\n *\n * It owns its own reactive subscriptions (e.g. `invalidatesQueries` reads\n * `useQueryClient()`) and the behavior lives in the middleware itself.\n * `useBaseMutation` chains the `onSuccess` of every middleware it receives\n * and has zero knowledge of what any individual middleware does — to add a\n * success side effect you write a new `MutationMiddleware`, never edit this\n * file.\n */\nexport type MutationMiddleware<TData = unknown, TVariables = unknown> = () => Pick<\n UseMutationOptions<TData, Error, TVariables>,\n 'onSuccess'\n>;\n\nconst EMPTY_MIDDLEWARES: ReadonlyArray<MutationMiddleware> = [];\n\nexport type UseBaseMutationParams<TData, TVariables, TError = Error> = {\n /**\n * Named middlewares the mutation runs on success. Each is invoked on every\n * render and the `onSuccess` it contributes is chained (in order) ahead of\n * the consumer's own `onSuccess`. The behavior lives in the middleware\n * itself (see `MutationMiddleware`).\n */\n middlewares?: ReadonlyArray<MutationMiddleware>;\n /**\n * Override-only react-query mutation options. Anything set here wins\n * over the defaults (mutationFn). Customers use this to attach\n * `onSuccess` / `onError` callbacks, retry policies, etc.\n */\n mutateParams?: Omit<\n UseMutationOptions<TData, TError, TVariables>,\n 'mutationFn'\n >;\n /**\n * The action. Receives the live `DynamicClient` from context plus the\n * variables passed when the caller invokes `mutate(variables)`.\n */\n mutationFn: (params: {\n client: DynamicClient;\n variables: TVariables;\n }) => Promise<TData>;\n};\n\n/**\n * Internal hook that wraps a react-query `useMutation` with the conventions\n * used by every async-write hook in this package:\n *\n * - `mutationFn` receives the resolved `DynamicClient` from context plus\n * the caller-supplied `variables`, and is gated behind\n * `waitForClientInitialized`.\n * - Callers pass `mutateParams` to override any default\n * `UseMutationOptions`, so consumers can wire `onSuccess` / `onError` /\n * etc.\n * - Callers pass `middlewares` (named hooks like `invalidatesQueries`) and\n * each contributed `onSuccess` runs, in order, ahead of the consumer's own.\n *\n * Returns the full `useMutation` result — `mutate`, `mutateAsync`, `data`,\n * `error`, `isPending`, `isSuccess`, `reset`, `status`, etc.\n */\nexport const useBaseMutation = <TData, TVariables = void, TError = Error>({\n middlewares = EMPTY_MIDDLEWARES,\n mutationFn,\n mutateParams,\n}: UseBaseMutationParams<\n TData,\n TVariables,\n TError\n>): UseMutationResult<TData, TError, TVariables> => {\n const client = useDynamicClient();\n\n // Each middleware is a hook. The `middlewares` array a hook passes is a\n // stable module-level constant, so call order is constant across renders.\n const contributions = middlewares.map((useMiddleware) => useMiddleware());\n\n return useMutation<TData, TError, TVariables>({\n mutationFn: async (variables: TVariables) => {\n await waitForClientInitialized(client);\n return mutationFn({ client, variables });\n },\n ...mutateParams,\n onSuccess: async (...args) => {\n // Await each contribution so async middlewares (e.g. `invalidatesQueries`,\n // whose `onSuccess` returns the `invalidateQueries` promise) run to\n // completion in order before the consumer's own `onSuccess`, and so their\n // rejections surface instead of becoming unhandled promise rejections.\n for (const contribution of contributions) {\n await contribution.onSuccess?.(...args);\n }\n\n return mutateParams?.onSuccess?.(...args);\n },\n });\n};\n","/**\n * Root namespace shared by every react-query key this package creates.\n * Both `QUERY_KEY_PREFIX` (async-read hooks) and the state-hook key\n * prefix derive from this constant so the namespace is defined exactly\n * once.\n */\nexport const HOOK_KEY_NAMESPACE = '@dynamic-labs-sdk/react-hooks' as const;\n","import { HOOK_KEY_NAMESPACE } from '../hookKeyNamespace/hookKeyNamespace';\n\n/**\n * Shared prefix for all query keys created by `useBaseQuery`. Mutation hooks\n * reference this when invalidating related queries on success.\n */\nexport const QUERY_KEY_PREFIX = [HOOK_KEY_NAMESPACE, 'query'] as const;\n","import type { DynamicClient } from '@dynamic-labs-sdk/client';\nimport { waitForClientInitialized } from '@dynamic-labs-sdk/client';\nimport type { UseQueryOptions, UseQueryResult } from '@tanstack/react-query';\nimport { useQuery } from '@tanstack/react-query';\nimport { useMemo } from 'react';\n\nimport { useDynamicClient } from '../../modules/DynamicContext';\nimport { QUERY_KEY_PREFIX } from './queryKeyPrefix';\n\n/**\n * Context `useBaseQuery` hands to every middleware on each render.\n *\n * `queryKey` is the fully namespaced cache key of the query the middleware is\n * attached to, so side-effecting middlewares (such as the one that invalidates\n * a query on a wallet provider event) can invalidate exactly that query\n * without the hook having to thread its key through by hand.\n */\nexport type QueryMiddlewareContext = {\n queryKey: ReadonlyArray<unknown>;\n};\n\n/**\n * A query middleware is a named hook that plugs reactive behavior into a query.\n *\n * It owns its own subscriptions (e.g. `invalidateOnWalletProviderEvent` listens\n * to a wallet provider event) and invalidates the query's cache key when\n * something it cares about changes. Middlewares are purely side-effecting:\n * `useBaseQuery` runs every one it receives on each render and has zero\n * knowledge of what any individual middleware does — to add behavior you write\n * a new `QueryMiddleware`, never edit this file.\n *\n * The context is optional so a middleware that ignores it (and tests that\n * invoke a middleware directly) can be called with no arguments; `useBaseQuery`\n * always supplies it at runtime.\n */\nexport type QueryMiddleware = (context?: QueryMiddlewareContext) => void;\n\n/**\n * Stable empty default for the `middlewares` param so the common case never\n * allocates a fresh array per render.\n */\nconst EMPTY_MIDDLEWARES: ReadonlyArray<QueryMiddleware> = [];\n\nexport type UseBaseQueryParams<TData, TError = Error> = {\n /**\n * When false, the query is disabled (react-query returns the\n * placeholder/empty result instead of calling `queryFn`). Hooks set this\n * to false when their pseudo-required arguments are missing — see hook\n * JSDocs for what those are.\n */\n enabled?: boolean;\n /**\n * Named middleware hooks that plug behavior into the query. Each is invoked\n * on every render with the query's context to run its reactive subscription\n * (e.g. invalidating the query on an event). The behavior lives in the\n * middleware itself (see `QueryMiddleware`).\n */\n middlewares?: ReadonlyArray<QueryMiddleware>;\n /**\n * The fetcher. Receives the live `DynamicClient` from context so callers\n * don't have to pull it themselves.\n */\n queryFn: (client: DynamicClient) => Promise<TData>;\n /**\n * Unique key for this hook's queries. The dynamic-labs-sdk prefix + the\n * supplied `keyExtension` (often the hook's argument shape) namespace the\n * react-query cache entry.\n */\n queryKey: ReadonlyArray<unknown>;\n /**\n * Override-only react-query options. Anything set here wins over the\n * defaults (queryFn, queryKey, enabled). Customers use this to tune\n * behavior such as `retry`, `staleTime`, `placeholderData`, etc.\n */\n queryParams?: Omit<UseQueryOptions<TData, TError>, 'queryFn' | 'queryKey'>;\n};\n\n/**\n * Internal hook that wraps a react-query `useQuery` with the conventions used\n * by every async-read hook in this package:\n *\n * - `queryFn` receives the resolved `DynamicClient` from context, and is\n * gated behind `waitForClientInitialized` so customers never see \"client\n * not ready\" errors on first render.\n * - Callers pass `queryParams` to override any default `UseQueryOptions`,\n * so consumers can opt into retries / custom staleTime / etc.\n * - Callers pass `enabled: false` (typically derived from whether their\n * pseudo-required arguments are present) to short-circuit fetches.\n * - Callers pass `middlewares` (named hooks that run reactive subscriptions\n * such as cache invalidation); each runs on every render.\n *\n * Returns the full `useQuery` result — `data`, `error`, `isLoading`,\n * `isFetching`, `isSuccess`, `refetch`, `status`, etc.\n */\nexport const useBaseQuery = <TData, TError = Error>({\n enabled = true,\n middlewares = EMPTY_MIDDLEWARES,\n queryFn,\n queryKey,\n queryParams,\n}: UseBaseQueryParams<TData, TError>): UseQueryResult<TData, TError> => {\n const client = useDynamicClient();\n\n const fullQueryKey = useMemo(\n () => [...QUERY_KEY_PREFIX, ...queryKey],\n [queryKey]\n );\n\n // Each middleware is a hook, so every one must run unconditionally on each\n // render: a plain loop keeps the same number and order of hook calls no\n // matter what. A hook always passes the same fixed middlewares in the same\n // order.\n middlewares.forEach((useMiddleware) =>\n useMiddleware({ queryKey: fullQueryKey })\n );\n\n return useQuery({\n queryFn: async () => {\n await waitForClientInitialized(client);\n return queryFn(client);\n },\n queryKey: fullQueryKey,\n ...queryParams,\n enabled: enabled && queryParams?.enabled !== false,\n });\n};\n","import type { DynamicClient } from '@dynamic-labs-sdk/client';\nimport { onEvent, waitForClientInitialized } from '@dynamic-labs-sdk/client';\nimport { getCore } from '@dynamic-labs-sdk/client/core';\nimport type { UseQueryOptions } from '@tanstack/react-query';\nimport { useQuery, useQueryClient } from '@tanstack/react-query';\nimport { useEffect, useMemo, useRef } from 'react';\n\nimport { useDynamicClient } from '../../modules/DynamicContext';\nimport { HOOK_KEY_NAMESPACE } from '../hookKeyNamespace/hookKeyNamespace';\n\n/**\n * Per-event invalidation predicate. Either `true` (always invalidate on\n * fire) or a callback that receives the event's args object and returns\n * `true` iff invalidation should occur.\n */\ntype EventPredicate<E extends keyof DynamicEvents> =\n | true\n | ((args: Parameters<DynamicEvents[E]>[0]) => boolean);\n\n/**\n * Map of event names → predicates. Listing an event opts in to listening\n * for it; the predicate decides whether each individual fire should\n * trigger a refetch.\n *\n * Loose typing here (`unknown` args, not a per-event mapped type) keeps\n * the inferred union small enough for TS to expand; the per-event\n * generic kicks in inside `subscribeWithPredicate` at the call site.\n */\nexport type UseBaseStateEventsMap = Partial<\n Record<keyof DynamicEvents, true | ((args: unknown) => boolean)>\n>;\n\nexport type UseBaseStateParams<T> = {\n /**\n * Value surfaced through `data` as the react-query placeholder while\n * the client is initializing and the selector has not yet resolved.\n * On first render, `data` equals `defaultValue`, `isPlaceholderData`\n * is `true`, and `isFetching` is `true` — callers can rely on those\n * flags to distinguish \"still resolving\" from \"resolved and the\n * answer happens to match the default\".\n */\n defaultValue: T;\n /**\n * The events that should invalidate the query. Pass a single event name\n * (shorthand for `{ [name]: true }`) or a map of `eventName → true |\n * predicate`. A `true` value invalidates on every fire; a predicate\n * receives the event's args and returns `true` iff invalidation should\n * occur for that fire (useful for narrowing — e.g. only invalidate when\n * the changed wallet account is the one this hook cares about).\n */\n event: keyof DynamicEvents | UseBaseStateEventsMap;\n /**\n * react-query `queryKey` for this hook. Internally namespaced under\n * `[HOOK_KEY_NAMESPACE, 'state', ...queryKey]`. Multiple\n * state hooks may observe the same event (e.g. `useUser`,\n * `useSessionExpiresAt`, `useGetUserSocialAccounts` all listen to\n * `userChanged`); without a unique `queryKey` they would share a cache\n * entry. Pass an array so callers can interpolate runtime values\n * (`['useGetNativeBalance', walletAccountId]`).\n */\n queryKey: ReadonlyArray<unknown>;\n /**\n * Optional override of any react-query `UseQueryOptions` (retry,\n * staleTime, refetchInterval, etc.). `queryFn`, `queryKey`, and\n * `placeholderData` are owned by `useBaseState` and not overridable.\n */\n queryParams?: Omit<\n UseQueryOptions<T>,\n 'queryFn' | 'queryKey' | 'placeholderData'\n >;\n selector: (client: DynamicClient) => T;\n /**\n * Escape hatch for hooks whose entire purpose is to expose state during\n * client initialization (e.g. `useInitStatus`). When `true`, `queryFn`\n * runs the selector synchronously without awaiting\n * `waitForClientInitialized`. Defaults to `false`.\n */\n skipWaitForInit?: boolean;\n};\n\n/**\n * Internal hook that bridges Dynamic client event-driven state to React via\n * react-query.\n *\n * - `queryFn` waits for the client to finish initializing (so the first\n * render never sees a half-built client) and then runs `selector`. Init\n * failures surface on `error`.\n * - The supplied `event`(s) invalidate the query whenever they fire — a\n * per-event predicate can narrow this so unrelated fires don't refetch.\n * - Returns the full `useQuery` result so consumers can read every field\n * react-query exposes (`data`, `error`, `isLoading`, `isFetching`,\n * `refetch`, `status`, ...).\n */\n// eslint-disable-next-line custom-rules/require-single-object-param -- helper takes the single `event` value, not an options bag\nconst normalizeEvents = (\n event: keyof DynamicEvents | UseBaseStateEventsMap\n): UseBaseStateEventsMap => {\n if (typeof event !== 'string') return event;\n const map: UseBaseStateEventsMap = {};\n map[event] = true;\n return map;\n};\n\ntype SubscribeWithPredicateParams<E extends keyof DynamicEvents> = {\n event: E;\n onMatch: VoidFunction;\n predicate: EventPredicate<E>;\n};\n\n/**\n * Per-event subscription wrapper. Extracted from `useBaseState` so the\n * listener generics resolve at the per-event call site rather than as the\n * intersection across all events.\n */\n// eslint-disable-next-line custom-rules/one-function-per-file -- small internal helper kept colocated with useBaseState\nconst subscribeWithPredicate = <E extends keyof DynamicEvents>(\n { event, predicate, onMatch }: SubscribeWithPredicateParams<E>,\n client: DynamicClient\n): VoidFunction => {\n // eslint-disable-next-line custom-rules/require-single-object-param -- forwards the event's own args shape\n const listener = (args: Parameters<DynamicEvents[E]>[0]) => {\n if (predicate === true || predicate(args)) {\n onMatch();\n }\n };\n return onEvent(\n { event, listener: listener as DynamicEvents[E] },\n client\n );\n};\n\n// eslint-disable-next-line custom-rules/one-function-per-file -- helpers above are colocated by design\nexport const useBaseState = <T>({\n event,\n queryKey,\n queryParams,\n selector,\n defaultValue,\n skipWaitForInit = false,\n}: UseBaseStateParams<T>) => {\n const client = useDynamicClient();\n const queryClient = useQueryClient();\n\n /**\n * Normalize the `event` shorthand into an events map. `useMemo` keyed\n * on a stable JSON shape so callers can pass an inline literal each\n * render without churning the event-subscription effect.\n */\n const eventsMapJson = useMemo(() => {\n const map = normalizeEvents(event);\n const sortedKeys = Object.keys(map).sort((a, b) => a.localeCompare(b));\n return JSON.stringify(\n sortedKeys.map((k) => ({\n event: k,\n predicate:\n map[k as keyof DynamicEvents] === true ? '__always__' : '__fn__',\n }))\n );\n }, [event]);\n\n /**\n * Per-event predicates resolved from the latest render. The effect\n * stays subscribed across re-renders (its deps are stable), so it\n * reads predicates through this ref to pick up the most recent\n * functions a caller passed inline.\n */\n const eventsMapRef = useRef<UseBaseStateEventsMap>(normalizeEvents(event));\n eventsMapRef.current = normalizeEvents(event);\n\n /**\n * Stable list of event names the effect needs to subscribe to. Driven\n * by `eventsMapJson` so callers can swap predicate identities each\n * render without re-subscribing.\n */\n const eventNames = useMemo<ReadonlyArray<keyof DynamicEvents>>(() => {\n const map = eventsMapRef.current;\n return Object.keys(map).sort((a, b) =>\n a.localeCompare(b)\n ) as Array<keyof DynamicEvents>;\n }, [eventsMapJson]);\n\n const fullQueryKey = useMemo(\n () => [HOOK_KEY_NAMESPACE, 'state', ...queryKey],\n [JSON.stringify(queryKey)]\n );\n\n useEffect(() => {\n const core = getCore(client);\n\n const invalidate = () => {\n queryClient\n .invalidateQueries({ queryKey: fullQueryKey })\n .catch((invalidateError) => {\n core.logger.error(\n '[useBaseState] invalidateQueries failed',\n invalidateError\n );\n });\n };\n\n const unsubscribers = eventNames.map((eventName) =>\n subscribeWithPredicate(\n {\n event: eventName,\n onMatch: invalidate,\n // Resolve the predicate fresh on each event fire so inline-fn\n // callers pick up their latest closure without re-subscribing.\n predicate: (args: unknown) => {\n const predicate = eventsMapRef.current[eventName];\n if (predicate === undefined) return false;\n return predicate === true || predicate(args);\n },\n },\n client\n )\n );\n\n return () => {\n for (const unsubscribe of unsubscribers) unsubscribe();\n };\n }, [client, eventNames, queryClient, fullQueryKey]);\n\n return useQuery({\n ...queryParams,\n // react-query types `placeholderData` with an internal\n // `NonFunctionGuard<T>` to disambiguate a static placeholder value\n // from `PlaceholderDataFunction`. Our `T` is unconstrained, so TS\n // can't prove the value isn't a function — state values are never\n // function-typed in practice, so cast through `never`.\n placeholderData: defaultValue as never,\n queryFn: skipWaitForInit\n ? () => selector(client)\n : async () => {\n await waitForClientInitialized(client);\n return selector(client);\n },\n queryKey: fullQueryKey,\n });\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
let _dynamic_labs_sdk_client = require("@dynamic-labs-sdk/client");
|
|
2
|
+
let _tanstack_react_query = require("@tanstack/react-query");
|
|
3
|
+
let react = require("react");
|
|
4
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
5
|
+
let _dynamic_labs_sdk_client_core = require("@dynamic-labs-sdk/client/core");
|
|
6
|
+
|
|
7
|
+
//#region package.json
|
|
8
|
+
var name = "@dynamic-labs-sdk/react-hooks";
|
|
9
|
+
var version = "1.22.0";
|
|
10
|
+
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/modules/DynamicContext/DynamicContext.ts
|
|
13
|
+
/**
|
|
14
|
+
* Internal React context for providing the DynamicClient instance to hooks.
|
|
15
|
+
*/
|
|
16
|
+
const DynamicContext = (0, react.createContext)(null);
|
|
17
|
+
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/modules/DynamicContext/DynamicProvider.tsx
|
|
20
|
+
/**
|
|
21
|
+
* Provides the DynamicClient instance to all child hooks.
|
|
22
|
+
*
|
|
23
|
+
* Wrap your application with this provider and pass your client instance
|
|
24
|
+
* so that hooks like useUser, useGetWalletAccounts, etc. can access it.
|
|
25
|
+
*/
|
|
26
|
+
const DynamicProvider = ({ children, client }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DynamicContext.Provider, {
|
|
27
|
+
value: client,
|
|
28
|
+
children
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/errors/MissingProviderError/MissingProviderError.ts
|
|
33
|
+
var MissingProviderError = class extends _dynamic_labs_sdk_client.BaseError {
|
|
34
|
+
constructor() {
|
|
35
|
+
super({
|
|
36
|
+
cause: null,
|
|
37
|
+
code: "MISSING_PROVIDER",
|
|
38
|
+
docsUrl: null,
|
|
39
|
+
name: "MissingProviderError",
|
|
40
|
+
shortMessage: "Hook must be used within a <DynamicProvider>.\nWrap your component tree with <DynamicProvider client={client}>."
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region src/modules/DynamicContext/useDynamicClient.ts
|
|
47
|
+
/**
|
|
48
|
+
* Internal hook to access the DynamicClient from context.
|
|
49
|
+
* Throws if used outside of DynamicProvider.
|
|
50
|
+
*/
|
|
51
|
+
const useDynamicClient = () => {
|
|
52
|
+
const client = (0, react.useContext)(DynamicContext);
|
|
53
|
+
if (!client) throw new MissingProviderError();
|
|
54
|
+
return client;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/foundation/useBaseMutation/useBaseMutation.ts
|
|
59
|
+
const EMPTY_MIDDLEWARES$1 = [];
|
|
60
|
+
/**
|
|
61
|
+
* Internal hook that wraps a react-query `useMutation` with the conventions
|
|
62
|
+
* used by every async-write hook in this package:
|
|
63
|
+
*
|
|
64
|
+
* - `mutationFn` receives the resolved `DynamicClient` from context plus
|
|
65
|
+
* the caller-supplied `variables`, and is gated behind
|
|
66
|
+
* `waitForClientInitialized`.
|
|
67
|
+
* - Callers pass `mutateParams` to override any default
|
|
68
|
+
* `UseMutationOptions`, so consumers can wire `onSuccess` / `onError` /
|
|
69
|
+
* etc.
|
|
70
|
+
* - Callers pass `middlewares` (named hooks like `invalidatesQueries`) and
|
|
71
|
+
* each contributed `onSuccess` runs, in order, ahead of the consumer's own.
|
|
72
|
+
*
|
|
73
|
+
* Returns the full `useMutation` result — `mutate`, `mutateAsync`, `data`,
|
|
74
|
+
* `error`, `isPending`, `isSuccess`, `reset`, `status`, etc.
|
|
75
|
+
*/
|
|
76
|
+
const useBaseMutation = ({ middlewares = EMPTY_MIDDLEWARES$1, mutationFn, mutateParams }) => {
|
|
77
|
+
const client = useDynamicClient();
|
|
78
|
+
const contributions = middlewares.map((useMiddleware) => useMiddleware());
|
|
79
|
+
return (0, _tanstack_react_query.useMutation)({
|
|
80
|
+
mutationFn: async (variables) => {
|
|
81
|
+
await (0, _dynamic_labs_sdk_client.waitForClientInitialized)(client);
|
|
82
|
+
return mutationFn({
|
|
83
|
+
client,
|
|
84
|
+
variables
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
...mutateParams,
|
|
88
|
+
onSuccess: async (...args) => {
|
|
89
|
+
for (const contribution of contributions) await contribution.onSuccess?.(...args);
|
|
90
|
+
return mutateParams?.onSuccess?.(...args);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
//#endregion
|
|
96
|
+
//#region src/foundation/hookKeyNamespace/hookKeyNamespace.ts
|
|
97
|
+
/**
|
|
98
|
+
* Root namespace shared by every react-query key this package creates.
|
|
99
|
+
* Both `QUERY_KEY_PREFIX` (async-read hooks) and the state-hook key
|
|
100
|
+
* prefix derive from this constant so the namespace is defined exactly
|
|
101
|
+
* once.
|
|
102
|
+
*/
|
|
103
|
+
const HOOK_KEY_NAMESPACE = "@dynamic-labs-sdk/react-hooks";
|
|
104
|
+
|
|
105
|
+
//#endregion
|
|
106
|
+
//#region src/foundation/useBaseQuery/queryKeyPrefix.ts
|
|
107
|
+
/**
|
|
108
|
+
* Shared prefix for all query keys created by `useBaseQuery`. Mutation hooks
|
|
109
|
+
* reference this when invalidating related queries on success.
|
|
110
|
+
*/
|
|
111
|
+
const QUERY_KEY_PREFIX = [HOOK_KEY_NAMESPACE, "query"];
|
|
112
|
+
|
|
113
|
+
//#endregion
|
|
114
|
+
//#region src/foundation/useBaseQuery/useBaseQuery.ts
|
|
115
|
+
/**
|
|
116
|
+
* Stable empty default for the `middlewares` param so the common case never
|
|
117
|
+
* allocates a fresh array per render.
|
|
118
|
+
*/
|
|
119
|
+
const EMPTY_MIDDLEWARES = [];
|
|
120
|
+
/**
|
|
121
|
+
* Internal hook that wraps a react-query `useQuery` with the conventions used
|
|
122
|
+
* by every async-read hook in this package:
|
|
123
|
+
*
|
|
124
|
+
* - `queryFn` receives the resolved `DynamicClient` from context, and is
|
|
125
|
+
* gated behind `waitForClientInitialized` so customers never see "client
|
|
126
|
+
* not ready" errors on first render.
|
|
127
|
+
* - Callers pass `queryParams` to override any default `UseQueryOptions`,
|
|
128
|
+
* so consumers can opt into retries / custom staleTime / etc.
|
|
129
|
+
* - Callers pass `enabled: false` (typically derived from whether their
|
|
130
|
+
* pseudo-required arguments are present) to short-circuit fetches.
|
|
131
|
+
* - Callers pass `middlewares` (named hooks that run reactive subscriptions
|
|
132
|
+
* such as cache invalidation); each runs on every render.
|
|
133
|
+
*
|
|
134
|
+
* Returns the full `useQuery` result — `data`, `error`, `isLoading`,
|
|
135
|
+
* `isFetching`, `isSuccess`, `refetch`, `status`, etc.
|
|
136
|
+
*/
|
|
137
|
+
const useBaseQuery = ({ enabled = true, middlewares = EMPTY_MIDDLEWARES, queryFn, queryKey, queryParams }) => {
|
|
138
|
+
const client = useDynamicClient();
|
|
139
|
+
const fullQueryKey = (0, react.useMemo)(() => [...QUERY_KEY_PREFIX, ...queryKey], [queryKey]);
|
|
140
|
+
middlewares.forEach((useMiddleware) => useMiddleware({ queryKey: fullQueryKey }));
|
|
141
|
+
return (0, _tanstack_react_query.useQuery)({
|
|
142
|
+
queryFn: async () => {
|
|
143
|
+
await (0, _dynamic_labs_sdk_client.waitForClientInitialized)(client);
|
|
144
|
+
return queryFn(client);
|
|
145
|
+
},
|
|
146
|
+
queryKey: fullQueryKey,
|
|
147
|
+
...queryParams,
|
|
148
|
+
enabled: enabled && queryParams?.enabled !== false
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
//#endregion
|
|
153
|
+
//#region src/foundation/useBaseState/useBaseState.ts
|
|
154
|
+
/**
|
|
155
|
+
* Internal hook that bridges Dynamic client event-driven state to React via
|
|
156
|
+
* react-query.
|
|
157
|
+
*
|
|
158
|
+
* - `queryFn` waits for the client to finish initializing (so the first
|
|
159
|
+
* render never sees a half-built client) and then runs `selector`. Init
|
|
160
|
+
* failures surface on `error`.
|
|
161
|
+
* - The supplied `event`(s) invalidate the query whenever they fire — a
|
|
162
|
+
* per-event predicate can narrow this so unrelated fires don't refetch.
|
|
163
|
+
* - Returns the full `useQuery` result so consumers can read every field
|
|
164
|
+
* react-query exposes (`data`, `error`, `isLoading`, `isFetching`,
|
|
165
|
+
* `refetch`, `status`, ...).
|
|
166
|
+
*/
|
|
167
|
+
const normalizeEvents = (event) => {
|
|
168
|
+
if (typeof event !== "string") return event;
|
|
169
|
+
const map = {};
|
|
170
|
+
map[event] = true;
|
|
171
|
+
return map;
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* Per-event subscription wrapper. Extracted from `useBaseState` so the
|
|
175
|
+
* listener generics resolve at the per-event call site rather than as the
|
|
176
|
+
* intersection across all events.
|
|
177
|
+
*/
|
|
178
|
+
const subscribeWithPredicate = ({ event, predicate, onMatch }, client) => {
|
|
179
|
+
const listener = (args) => {
|
|
180
|
+
if (predicate === true || predicate(args)) onMatch();
|
|
181
|
+
};
|
|
182
|
+
return (0, _dynamic_labs_sdk_client.onEvent)({
|
|
183
|
+
event,
|
|
184
|
+
listener
|
|
185
|
+
}, client);
|
|
186
|
+
};
|
|
187
|
+
const useBaseState = ({ event, queryKey, queryParams, selector, defaultValue, skipWaitForInit = false }) => {
|
|
188
|
+
const client = useDynamicClient();
|
|
189
|
+
const queryClient = (0, _tanstack_react_query.useQueryClient)();
|
|
190
|
+
/**
|
|
191
|
+
* Normalize the `event` shorthand into an events map. `useMemo` keyed
|
|
192
|
+
* on a stable JSON shape so callers can pass an inline literal each
|
|
193
|
+
* render without churning the event-subscription effect.
|
|
194
|
+
*/
|
|
195
|
+
const eventsMapJson = (0, react.useMemo)(() => {
|
|
196
|
+
const map = normalizeEvents(event);
|
|
197
|
+
const sortedKeys = Object.keys(map).sort((a, b) => a.localeCompare(b));
|
|
198
|
+
return JSON.stringify(sortedKeys.map((k) => ({
|
|
199
|
+
event: k,
|
|
200
|
+
predicate: map[k] === true ? "__always__" : "__fn__"
|
|
201
|
+
})));
|
|
202
|
+
}, [event]);
|
|
203
|
+
/**
|
|
204
|
+
* Per-event predicates resolved from the latest render. The effect
|
|
205
|
+
* stays subscribed across re-renders (its deps are stable), so it
|
|
206
|
+
* reads predicates through this ref to pick up the most recent
|
|
207
|
+
* functions a caller passed inline.
|
|
208
|
+
*/
|
|
209
|
+
const eventsMapRef = (0, react.useRef)(normalizeEvents(event));
|
|
210
|
+
eventsMapRef.current = normalizeEvents(event);
|
|
211
|
+
/**
|
|
212
|
+
* Stable list of event names the effect needs to subscribe to. Driven
|
|
213
|
+
* by `eventsMapJson` so callers can swap predicate identities each
|
|
214
|
+
* render without re-subscribing.
|
|
215
|
+
*/
|
|
216
|
+
const eventNames = (0, react.useMemo)(() => {
|
|
217
|
+
const map = eventsMapRef.current;
|
|
218
|
+
return Object.keys(map).sort((a, b) => a.localeCompare(b));
|
|
219
|
+
}, [eventsMapJson]);
|
|
220
|
+
const fullQueryKey = (0, react.useMemo)(() => [
|
|
221
|
+
HOOK_KEY_NAMESPACE,
|
|
222
|
+
"state",
|
|
223
|
+
...queryKey
|
|
224
|
+
], [JSON.stringify(queryKey)]);
|
|
225
|
+
(0, react.useEffect)(() => {
|
|
226
|
+
const core = (0, _dynamic_labs_sdk_client_core.getCore)(client);
|
|
227
|
+
const invalidate = () => {
|
|
228
|
+
queryClient.invalidateQueries({ queryKey: fullQueryKey }).catch((invalidateError) => {
|
|
229
|
+
core.logger.error("[useBaseState] invalidateQueries failed", invalidateError);
|
|
230
|
+
});
|
|
231
|
+
};
|
|
232
|
+
const unsubscribers = eventNames.map((eventName) => subscribeWithPredicate({
|
|
233
|
+
event: eventName,
|
|
234
|
+
onMatch: invalidate,
|
|
235
|
+
predicate: (args) => {
|
|
236
|
+
const predicate = eventsMapRef.current[eventName];
|
|
237
|
+
if (predicate === void 0) return false;
|
|
238
|
+
return predicate === true || predicate(args);
|
|
239
|
+
}
|
|
240
|
+
}, client));
|
|
241
|
+
return () => {
|
|
242
|
+
for (const unsubscribe of unsubscribers) unsubscribe();
|
|
243
|
+
};
|
|
244
|
+
}, [
|
|
245
|
+
client,
|
|
246
|
+
eventNames,
|
|
247
|
+
queryClient,
|
|
248
|
+
fullQueryKey
|
|
249
|
+
]);
|
|
250
|
+
return (0, _tanstack_react_query.useQuery)({
|
|
251
|
+
...queryParams,
|
|
252
|
+
placeholderData: defaultValue,
|
|
253
|
+
queryFn: skipWaitForInit ? () => selector(client) : async () => {
|
|
254
|
+
await (0, _dynamic_labs_sdk_client.waitForClientInitialized)(client);
|
|
255
|
+
return selector(client);
|
|
256
|
+
},
|
|
257
|
+
queryKey: fullQueryKey
|
|
258
|
+
});
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
//#endregion
|
|
262
|
+
Object.defineProperty(exports, 'DynamicProvider', {
|
|
263
|
+
enumerable: true,
|
|
264
|
+
get: function () {
|
|
265
|
+
return DynamicProvider;
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
Object.defineProperty(exports, 'QUERY_KEY_PREFIX', {
|
|
269
|
+
enumerable: true,
|
|
270
|
+
get: function () {
|
|
271
|
+
return QUERY_KEY_PREFIX;
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
Object.defineProperty(exports, 'name', {
|
|
275
|
+
enumerable: true,
|
|
276
|
+
get: function () {
|
|
277
|
+
return name;
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
Object.defineProperty(exports, 'useBaseMutation', {
|
|
281
|
+
enumerable: true,
|
|
282
|
+
get: function () {
|
|
283
|
+
return useBaseMutation;
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
Object.defineProperty(exports, 'useBaseQuery', {
|
|
287
|
+
enumerable: true,
|
|
288
|
+
get: function () {
|
|
289
|
+
return useBaseQuery;
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
Object.defineProperty(exports, 'useBaseState', {
|
|
293
|
+
enumerable: true,
|
|
294
|
+
get: function () {
|
|
295
|
+
return useBaseState;
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
Object.defineProperty(exports, 'useDynamicClient', {
|
|
299
|
+
enumerable: true,
|
|
300
|
+
get: function () {
|
|
301
|
+
return useDynamicClient;
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
Object.defineProperty(exports, 'version', {
|
|
305
|
+
enumerable: true,
|
|
306
|
+
get: function () {
|
|
307
|
+
return version;
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
//# sourceMappingURL=useBaseState-D4H0aBjD.cjs.map
|