@clerk/shared 4.29.2-canary.v20260818012144 → 4.29.2
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/alternativePhoneCode.js +2 -1
- package/dist/apiUrlFromPublishableKey.js +2 -1
- package/dist/authorization-errors.js +2 -1
- package/dist/authorization.js +2 -1
- package/dist/browser.js +2 -1
- package/dist/buildAccountsBaseUrl.js +2 -1
- package/dist/clerkEventBus.js +2 -1
- package/dist/color.js +2 -1
- package/dist/constants.js +2 -1
- package/dist/cookie.js +4 -3
- package/dist/date.js +2 -1
- package/dist/deprecated.js +3 -2
- package/dist/deriveState.js +2 -1
- package/dist/devBrowser.js +2 -1
- package/dist/dom/waitForElement.js +2 -1
- package/dist/error.js +32 -42
- package/dist/eventBus.js +2 -1
- package/dist/file.js +2 -1
- package/dist/getEnvVariable.js +2 -1
- package/dist/getToken.js +3 -2
- package/dist/globs.js +4 -3
- package/dist/handleValueOrFn.js +3 -2
- package/dist/htmlSafeJson.js +2 -1
- package/dist/index.js +2 -1
- package/dist/inert.js +2 -1
- package/dist/internal/clerk-js/completeSignUpFlow.js +2 -1
- package/dist/internal/clerk-js/componentGuards.js +2 -1
- package/dist/internal/clerk-js/constants.js +2 -1
- package/dist/internal/clerk-js/email.js +2 -1
- package/dist/internal/clerk-js/encoders.js +2 -1
- package/dist/internal/clerk-js/errors.js +2 -1
- package/dist/internal/clerk-js/hex.js +2 -1
- package/dist/internal/clerk-js/injectedWeb3EthProviders.js +2 -1
- package/dist/internal/clerk-js/injectedWeb3SolanaProviders.js +3 -2
- package/dist/internal/clerk-js/navigateToNextStepSignUp.js +2 -1
- package/dist/internal/clerk-js/organization.js +2 -1
- package/dist/internal/clerk-js/passkeys.js +10 -10
- package/dist/internal/clerk-js/passwords/complexity.js +2 -1
- package/dist/internal/clerk-js/passwords/loadZxcvbn.js +2 -1
- package/dist/internal/clerk-js/passwords/password.js +3 -2
- package/dist/internal/clerk-js/passwords/strength.js +2 -1
- package/dist/internal/clerk-js/path.js +2 -1
- package/dist/internal/clerk-js/protectCheck.js +4 -3
- package/dist/internal/clerk-js/queryParams.js +2 -1
- package/dist/internal/clerk-js/queryStateParams.js +2 -1
- package/dist/internal/clerk-js/querystring.js +2 -1
- package/dist/internal/clerk-js/redirectUrls.js +2 -1
- package/dist/internal/clerk-js/runtime.js +2 -1
- package/dist/internal/clerk-js/sessionTasks.js +2 -1
- package/dist/internal/clerk-js/thirdPartyDomains.js +2 -1
- package/dist/internal/clerk-js/url.js +2 -1
- package/dist/internal/clerk-js/user.js +2 -1
- package/dist/internal/clerk-js/warnings.js +2 -1
- package/dist/internal/clerk-js/web3.js +5 -5
- package/dist/internal/clerk-js/windowNavigate.js +2 -1
- package/dist/isomorphicAtob.js +2 -1
- package/dist/isomorphicBtoa.js +2 -1
- package/dist/jwtPayloadParser.js +2 -1
- package/dist/keyless/index.js +369 -12
- package/dist/keys.js +2 -1
- package/dist/loadClerkJsScript.js +6 -6
- package/dist/loadClerkJsScript.js.map +1 -1
- package/dist/loadClerkJsScript.mjs +1 -1
- package/dist/loadClerkJsScript.mjs.map +1 -1
- package/dist/loadScript.js +2 -1
- package/dist/localStorageBroadcastChannel.js +2 -1
- package/dist/logger.js +2 -1
- package/dist/netlifyCacheHandler.js +2 -1
- package/dist/oauth.js +2 -1
- package/dist/object.js +2 -1
- package/dist/organization.js +2 -1
- package/dist/patchRequest.js +2 -1
- package/dist/pathMatcher.js +3 -2
- package/dist/pathToRegexp.js +3 -23
- package/dist/poller.js +4 -3
- package/dist/proxy.js +2 -1
- package/dist/react/index.js +3776 -93
- package/dist/retry.js +2 -1
- package/dist/router.js +150 -9
- package/dist/safeImport.js +2 -1
- package/dist/saml.js +2 -1
- package/dist/telemetry.js +11 -16
- package/dist/underscore.js +2 -1
- package/dist/url.js +3 -2
- package/dist/utils/index.js +12 -16
- package/dist/versionCheck.js +2 -1
- package/dist/versionSelector.js +3 -2
- package/dist/versionSelector.js.map +1 -1
- package/dist/versionSelector.mjs +1 -1
- package/dist/versionSelector.mjs.map +1 -1
- package/dist/web3.js +2 -1
- package/dist/webauthn.js +2 -1
- package/dist/workerTimers/index.js +2 -2
- package/package.json +1 -1
package/dist/react/index.js
CHANGED
|
@@ -1,94 +1,3777 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
2
|
+
const require_chunk = require('../_chunks/chunk-C_NdSu1c.js');
|
|
3
|
+
const require_authorization_errors = require('../authorization-errors.js');
|
|
4
|
+
const require_authorization = require('../authorization.js');
|
|
5
|
+
const require_deriveState = require('../deriveState.js');
|
|
6
|
+
const require_clerkRuntimeError = require('../_chunks/clerkRuntimeError-CYZdTZ_x.js');
|
|
7
|
+
const require_error = require('../_chunks/error-rHNfstgh.js');
|
|
8
|
+
const require_logger = require('../logger.js');
|
|
9
|
+
const require_organization = require('../organization.js');
|
|
10
|
+
const require_telemetry = require('../_chunks/telemetry-DZp9OI1j.js');
|
|
11
|
+
const require_createDeferredPromise = require('../_chunks/createDeferredPromise-BXa3LUQE.js');
|
|
12
|
+
const require_internal_clerk_js_errors = require('../internal/clerk-js/errors.js');
|
|
13
|
+
let react = require("react");
|
|
14
|
+
react = require_chunk.__toESM(react);
|
|
15
|
+
let _tanstack_query_core = require("@tanstack/query-core");
|
|
16
|
+
let dequal = require("dequal");
|
|
17
|
+
|
|
18
|
+
//#region src/react/hooks/createContextAndHook.ts
|
|
19
|
+
/**
|
|
20
|
+
* Assert that the context value exists, otherwise throw an error.
|
|
21
|
+
*
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
function assertContextExists(contextVal, msgOrCtx) {
|
|
25
|
+
if (!contextVal) throw typeof msgOrCtx === "string" ? new Error(msgOrCtx) : /* @__PURE__ */ new Error(`${msgOrCtx.displayName} not found`);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Create and return a Context and two hooks that return the context value.
|
|
29
|
+
* The Context type is derived from the type passed in by the user.
|
|
30
|
+
*
|
|
31
|
+
* The first hook returned guarantees that the context exists so the returned value is always `CtxValue`
|
|
32
|
+
* The second hook makes no guarantees, so the returned value can be `CtxValue | undefined`
|
|
33
|
+
*
|
|
34
|
+
* @internal
|
|
35
|
+
*/
|
|
36
|
+
const createContextAndHook = (displayName, options) => {
|
|
37
|
+
const { assertCtxFn = assertContextExists } = options || {};
|
|
38
|
+
const Ctx = react.default.createContext(void 0);
|
|
39
|
+
Ctx.displayName = displayName;
|
|
40
|
+
const useCtx = () => {
|
|
41
|
+
const ctx = react.default.useContext(Ctx);
|
|
42
|
+
assertCtxFn(ctx, `${displayName} not found`);
|
|
43
|
+
return ctx.value;
|
|
44
|
+
};
|
|
45
|
+
const useCtxWithoutGuarantee = () => {
|
|
46
|
+
const ctx = react.default.useContext(Ctx);
|
|
47
|
+
return ctx ? ctx.value : {};
|
|
48
|
+
};
|
|
49
|
+
return [
|
|
50
|
+
Ctx,
|
|
51
|
+
useCtx,
|
|
52
|
+
useCtxWithoutGuarantee
|
|
53
|
+
];
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/react/contexts.tsx
|
|
58
|
+
const [ClerkInstanceContext, useClerkInstanceContext] = createContextAndHook("ClerkInstanceContext");
|
|
59
|
+
const [InitialStateContext, _useInitialStateContext] = createContextAndHook("InitialStateContext");
|
|
60
|
+
/**
|
|
61
|
+
* Provides initial Clerk state (session, user, organization data) from server-side rendering
|
|
62
|
+
* to child components via React context.
|
|
63
|
+
*
|
|
64
|
+
* Passing in a promise is only supported for React >= 19.
|
|
65
|
+
*
|
|
66
|
+
* The initialState is snapshotted on mount and cannot change during the component lifecycle.
|
|
67
|
+
*
|
|
68
|
+
* Note that different parts of the React tree can use separate InitialStateProvider instances
|
|
69
|
+
* with different initialState values if needed.
|
|
70
|
+
*/
|
|
71
|
+
function InitialStateProvider({ children, initialState }) {
|
|
72
|
+
const [initialStateSnapshot] = (0, react.useState)(initialState);
|
|
73
|
+
const initialStateCtx = react.default.useMemo(() => ({ value: initialStateSnapshot }), [initialStateSnapshot]);
|
|
74
|
+
return /* @__PURE__ */ react.default.createElement(InitialStateContext.Provider, { value: initialStateCtx }, children);
|
|
75
|
+
}
|
|
76
|
+
function useInitialStateContext() {
|
|
77
|
+
const initialState = _useInitialStateContext();
|
|
78
|
+
if (initialState instanceof Promise) if ("use" in react.default && typeof react.default.use === "function") return react.default.use(initialState);
|
|
79
|
+
else throw new Error("initialState cannot be a promise if React version is less than 19");
|
|
80
|
+
return initialState;
|
|
81
|
+
}
|
|
82
|
+
const OptionsContext = react.default.createContext({});
|
|
83
|
+
const [CheckoutContext, useCheckoutContext] = createContextAndHook("CheckoutContext");
|
|
84
|
+
const __experimental_CheckoutProvider = ({ children, ...rest }) => {
|
|
85
|
+
return /* @__PURE__ */ react.default.createElement(CheckoutContext.Provider, { value: { value: rest } }, children);
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* @internal
|
|
89
|
+
*/
|
|
90
|
+
function useOptionsContext() {
|
|
91
|
+
const context = react.default.useContext(OptionsContext);
|
|
92
|
+
if (context === void 0) throw new Error("useOptions must be used within an OptionsContext");
|
|
93
|
+
return context;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* @internal
|
|
97
|
+
*/
|
|
98
|
+
function useAssertWrappedByClerkProvider(displayNameOrFn) {
|
|
99
|
+
if (!react.default.useContext(ClerkInstanceContext)) {
|
|
100
|
+
if (typeof displayNameOrFn === "function") {
|
|
101
|
+
displayNameOrFn();
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
throw new Error(`${displayNameOrFn} can only be used within the <ClerkProvider /> component.
|
|
105
|
+
|
|
106
|
+
Possible fixes:
|
|
107
|
+
1. Ensure that the <ClerkProvider /> is correctly wrapping your application where this component is used.
|
|
108
|
+
2. Check for multiple versions of the \`@clerk/shared\` package in your project. Use a tool like \`npm ls @clerk/shared\` to identify multiple versions, and update your dependencies to only rely on one.
|
|
109
|
+
|
|
110
|
+
Learn more: https://clerk.com/docs/components/clerk-provider`.trim());
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/react/stable-keys.ts
|
|
116
|
+
const USER_MEMBERSHIPS_KEY = "userMemberships";
|
|
117
|
+
const USER_INVITATIONS_KEY = "userInvitations";
|
|
118
|
+
const USER_SUGGESTIONS_KEY = "userSuggestions";
|
|
119
|
+
const DOMAINS_KEY = "domains";
|
|
120
|
+
const MEMBERSHIP_REQUESTS_KEY = "membershipRequests";
|
|
121
|
+
const MEMBERSHIPS_KEY = "memberships";
|
|
122
|
+
const INVITATIONS_KEY = "invitations";
|
|
123
|
+
const API_KEYS_KEY = "apiKeys";
|
|
124
|
+
const ORGANIZATION_CREATION_DEFAULTS_KEY = "organizationCreationDefaults";
|
|
125
|
+
const OAUTH_CONSENT_INFO_KEY = "oauthConsentInfo";
|
|
126
|
+
const PLANS_KEY = "billing-plans";
|
|
127
|
+
const SUBSCRIPTION_KEY = "billing-subscription";
|
|
128
|
+
const PAYMENT_METHODS_KEY = "billing-payment-methods";
|
|
129
|
+
const PAYMENT_ATTEMPTS_KEY = "billing-payment-attempts";
|
|
130
|
+
const STATEMENTS_KEY = "billing-statements";
|
|
131
|
+
const CREDIT_BALANCE_KEY = "billing-credit-balance";
|
|
132
|
+
const STABLE_KEYS = {
|
|
133
|
+
USER_MEMBERSHIPS_KEY,
|
|
134
|
+
USER_INVITATIONS_KEY,
|
|
135
|
+
USER_SUGGESTIONS_KEY,
|
|
136
|
+
DOMAINS_KEY,
|
|
137
|
+
MEMBERSHIP_REQUESTS_KEY,
|
|
138
|
+
MEMBERSHIPS_KEY,
|
|
139
|
+
INVITATIONS_KEY,
|
|
140
|
+
PLANS_KEY,
|
|
141
|
+
SUBSCRIPTION_KEY,
|
|
142
|
+
PAYMENT_METHODS_KEY,
|
|
143
|
+
PAYMENT_ATTEMPTS_KEY,
|
|
144
|
+
STATEMENTS_KEY,
|
|
145
|
+
API_KEYS_KEY,
|
|
146
|
+
ORGANIZATION_CREATION_DEFAULTS_KEY,
|
|
147
|
+
OAUTH_CONSENT_INFO_KEY,
|
|
148
|
+
CREDIT_BALANCE_KEY
|
|
149
|
+
};
|
|
150
|
+
/**
|
|
151
|
+
* Internal stable keys for queries only used by our UI components.
|
|
152
|
+
* These keys are not used by the hooks themselves.
|
|
153
|
+
*/
|
|
154
|
+
const PAYMENT_ATTEMPT_KEY = "billing-payment-attempt";
|
|
155
|
+
const BILLING_PLANS_KEY = "billing-plan";
|
|
156
|
+
const BILLING_STATEMENTS_KEY = "billing-statement";
|
|
157
|
+
const USER_ENTERPRISE_CONNECTIONS_KEY = "userEnterpriseConnections";
|
|
158
|
+
const ENTERPRISE_CONNECTION_TEST_RUNS_KEY = "enterpriseConnectionTestRuns";
|
|
159
|
+
const ORGANIZATION_ENTERPRISE_CONNECTIONS_KEY = "organizationEnterpriseConnections";
|
|
160
|
+
const ORGANIZATION_ENTERPRISE_CONNECTION_TEST_RUNS_KEY = "organizationEnterpriseConnectionTestRuns";
|
|
161
|
+
const ORGANIZATION_DOMAINS_KEY = "organizationDomains";
|
|
162
|
+
const CREDIT_HISTORY_KEY = "billing-credit-history";
|
|
163
|
+
const INTERNAL_STABLE_KEYS = {
|
|
164
|
+
PAYMENT_ATTEMPT_KEY,
|
|
165
|
+
BILLING_PLANS_KEY,
|
|
166
|
+
BILLING_STATEMENTS_KEY,
|
|
167
|
+
CREDIT_HISTORY_KEY,
|
|
168
|
+
USER_ENTERPRISE_CONNECTIONS_KEY,
|
|
169
|
+
ENTERPRISE_CONNECTION_TEST_RUNS_KEY,
|
|
170
|
+
ORGANIZATION_ENTERPRISE_CONNECTIONS_KEY,
|
|
171
|
+
ORGANIZATION_ENTERPRISE_CONNECTION_TEST_RUNS_KEY,
|
|
172
|
+
ORGANIZATION_DOMAINS_KEY
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
//#endregion
|
|
176
|
+
//#region src/react/hooks/createCacheKeys.ts
|
|
177
|
+
/**
|
|
178
|
+
* @internal
|
|
179
|
+
*/
|
|
180
|
+
function createCacheKeys(params) {
|
|
181
|
+
return {
|
|
182
|
+
queryKey: [
|
|
183
|
+
params.stablePrefix,
|
|
184
|
+
params.authenticated,
|
|
185
|
+
params.tracked,
|
|
186
|
+
params.untracked
|
|
187
|
+
],
|
|
188
|
+
invalidationKey: [
|
|
189
|
+
params.stablePrefix,
|
|
190
|
+
params.authenticated,
|
|
191
|
+
params.tracked
|
|
192
|
+
],
|
|
193
|
+
stableKey: params.stablePrefix,
|
|
194
|
+
authenticated: params.authenticated
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
//#endregion
|
|
199
|
+
//#region src/react/query/keep-previous-data.ts
|
|
200
|
+
/**
|
|
201
|
+
* @internal
|
|
202
|
+
*/
|
|
203
|
+
function defineKeepPreviousDataFn(enabled) {
|
|
204
|
+
if (enabled) return function KeepPreviousDataFn(previousData) {
|
|
205
|
+
return previousData;
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
//#endregion
|
|
210
|
+
//#region src/react/query/clerk-query-client.ts
|
|
211
|
+
/**
|
|
212
|
+
* The QueryClient backing every clerk-query hook. Owned by `@clerk/shared` so the
|
|
213
|
+
* `QueryObserver` that observes it and the `Query` objects inside it always
|
|
214
|
+
* resolve to the same `@tanstack/query-core` (no cross-bundle drift between
|
|
215
|
+
* the consumer-side `@clerk/shared` and the production CDN `clerk-js` bundle).
|
|
216
|
+
*
|
|
217
|
+
* Lazily instantiated on the client only. Server-side renders return
|
|
218
|
+
* `undefined` so per-request renders never share a cache across requests.
|
|
219
|
+
*/
|
|
220
|
+
let clerkQueryClient;
|
|
221
|
+
let initialized = false;
|
|
222
|
+
function getClerkQueryClient() {
|
|
223
|
+
if (typeof window === "undefined") return;
|
|
224
|
+
if (!initialized) {
|
|
225
|
+
clerkQueryClient = new _tanstack_query_core.QueryClient();
|
|
226
|
+
initialized = true;
|
|
227
|
+
}
|
|
228
|
+
return clerkQueryClient;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Test-only: install a custom client (for deterministic defaults like
|
|
232
|
+
* `staleTime: Infinity`) or pass `undefined` to simulate the "no client"
|
|
233
|
+
* state without triggering lazy creation on subsequent reads.
|
|
234
|
+
*/
|
|
235
|
+
function __setClerkQueryClientForTest(client) {
|
|
236
|
+
clerkQueryClient = client;
|
|
237
|
+
initialized = true;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Test-only: build and install a fresh `QueryClient` with deterministic
|
|
241
|
+
* defaults (no retries, infinite stale time, no refetching). Returns the
|
|
242
|
+
* client so the spec can read/write its cache directly.
|
|
243
|
+
*
|
|
244
|
+
* Avoids forcing every test consumer to depend on `@tanstack/query-core`.
|
|
245
|
+
*/
|
|
246
|
+
function __createClerkTestQueryClient() {
|
|
247
|
+
const client = new _tanstack_query_core.QueryClient({ defaultOptions: { queries: {
|
|
248
|
+
retry: false,
|
|
249
|
+
staleTime: Infinity,
|
|
250
|
+
refetchOnWindowFocus: false,
|
|
251
|
+
refetchOnReconnect: false,
|
|
252
|
+
refetchOnMount: false
|
|
253
|
+
} } });
|
|
254
|
+
__setClerkQueryClientForTest(client);
|
|
255
|
+
return client;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Test-only: clear both the override and the initialization flag so the
|
|
259
|
+
* next read lazy-creates a fresh client.
|
|
260
|
+
*/
|
|
261
|
+
function __resetClerkQueryClientForTest() {
|
|
262
|
+
clerkQueryClient = void 0;
|
|
263
|
+
initialized = false;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
//#endregion
|
|
267
|
+
//#region src/react/query/use-clerk-query-client.ts
|
|
268
|
+
/**
|
|
269
|
+
* Creates a recursively self-referential Proxy that safely handles:
|
|
270
|
+
* - Arbitrary property access (e.g., obj.any.prop.path)
|
|
271
|
+
* - Function calls at any level (e.g., obj.a().b.c())
|
|
272
|
+
* - Construction (e.g., new obj.a.b())
|
|
273
|
+
*
|
|
274
|
+
* Always returns itself to allow infinite chaining without throwing.
|
|
275
|
+
*/
|
|
276
|
+
function createRecursiveProxy(label) {
|
|
277
|
+
const callableTarget = function noop() {};
|
|
278
|
+
let self;
|
|
279
|
+
self = new Proxy(callableTarget, {
|
|
280
|
+
get(_target, prop) {
|
|
281
|
+
if (prop === "then") return;
|
|
282
|
+
if (prop === "toString") return () => `[${label}]`;
|
|
283
|
+
if (prop === Symbol.toPrimitive) return () => 0;
|
|
284
|
+
return self;
|
|
285
|
+
},
|
|
286
|
+
apply() {
|
|
287
|
+
return self;
|
|
288
|
+
},
|
|
289
|
+
construct() {
|
|
290
|
+
return self;
|
|
291
|
+
},
|
|
292
|
+
has() {
|
|
293
|
+
return false;
|
|
294
|
+
},
|
|
295
|
+
set() {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
return self;
|
|
300
|
+
}
|
|
301
|
+
const mockQueryClient = createRecursiveProxy("ClerkMockQueryClient");
|
|
302
|
+
/**
|
|
303
|
+
* Returns `[client, isLoaded]`. The real client is owned by `@clerk/shared`
|
|
304
|
+
* and lazily instantiated on the browser only — SSR returns the proxy mock
|
|
305
|
+
* + `isLoaded: false` so per-request renders never share a query cache.
|
|
306
|
+
*/
|
|
307
|
+
const useClerkQueryClient = () => {
|
|
308
|
+
const client = getClerkQueryClient();
|
|
309
|
+
return [client ?? mockQueryClient, Boolean(client)];
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
//#endregion
|
|
313
|
+
//#region src/react/query/useBaseQuery.ts
|
|
314
|
+
/**
|
|
315
|
+
* Stripped down version of useBaseQuery from @tanstack/query-core.
|
|
316
|
+
* This implementation allows for an observer to be created every time a query client changes.
|
|
317
|
+
*/
|
|
318
|
+
/**
|
|
319
|
+
* An alternative `useBaseQuery` implementation that allows for an observer to be created every time a query client changes.
|
|
320
|
+
*
|
|
321
|
+
* @internal
|
|
322
|
+
*/
|
|
323
|
+
function useBaseQuery(options, Observer) {
|
|
324
|
+
const [client, isQueryClientLoaded] = useClerkQueryClient();
|
|
325
|
+
const defaultedOptions = isQueryClientLoaded ? client.defaultQueryOptions(options) : options;
|
|
326
|
+
defaultedOptions._optimisticResults = "optimistic";
|
|
327
|
+
const observer = react.useMemo(() => {
|
|
328
|
+
return new Observer(client, defaultedOptions);
|
|
329
|
+
}, [client]);
|
|
330
|
+
const result = observer.getOptimisticResult(defaultedOptions);
|
|
331
|
+
const shouldSubscribe = options.subscribed !== false;
|
|
332
|
+
react.useSyncExternalStore(react.useCallback((onStoreChange) => {
|
|
333
|
+
const unsubscribe = shouldSubscribe ? observer.subscribe(_tanstack_query_core.notifyManager.batchCalls(onStoreChange)) : _tanstack_query_core.noop;
|
|
334
|
+
observer.updateResult();
|
|
335
|
+
return unsubscribe;
|
|
336
|
+
}, [observer, shouldSubscribe]), () => observer.getCurrentResult(), () => observer.getCurrentResult());
|
|
337
|
+
react.useEffect(() => {
|
|
338
|
+
observer.setOptions(defaultedOptions);
|
|
339
|
+
}, [defaultedOptions, observer]);
|
|
340
|
+
if (!isQueryClientLoaded) return {
|
|
341
|
+
data: void 0,
|
|
342
|
+
error: null,
|
|
343
|
+
isLoading: options.enabled !== false,
|
|
344
|
+
isFetching: false,
|
|
345
|
+
status: "pending"
|
|
346
|
+
};
|
|
347
|
+
return !defaultedOptions.notifyOnChangeProps ? observer.trackResult(result) : result;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
//#endregion
|
|
351
|
+
//#region src/react/query/useInfiniteQuery.ts
|
|
352
|
+
/**
|
|
353
|
+
*
|
|
354
|
+
*/
|
|
355
|
+
function useClerkInfiniteQuery(options) {
|
|
356
|
+
return useBaseQuery(options, _tanstack_query_core.InfiniteQueryObserver);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
//#endregion
|
|
360
|
+
//#region src/react/query/useQuery.ts
|
|
361
|
+
/**
|
|
362
|
+
*
|
|
363
|
+
*/
|
|
364
|
+
function useClerkQuery(options) {
|
|
365
|
+
return useBaseQuery(options, _tanstack_query_core.QueryObserver);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
//#endregion
|
|
369
|
+
//#region src/react/hooks/usePreviousValue.ts
|
|
370
|
+
/**
|
|
371
|
+
* A hook that retains the previous value of a primitive type.
|
|
372
|
+
* It uses a ref to prevent causing unnecessary re-renders.
|
|
373
|
+
*
|
|
374
|
+
* @internal
|
|
375
|
+
*
|
|
376
|
+
* @example
|
|
377
|
+
* ```
|
|
378
|
+
* Render 1: value = 'A' → returns null
|
|
379
|
+
* Render 2: value = 'B' → returns 'A'
|
|
380
|
+
* Render 3: value = 'B' → returns 'A'
|
|
381
|
+
* Render 4: value = 'B' → returns 'A'
|
|
382
|
+
* Render 5: value = 'C' → returns 'B'
|
|
383
|
+
* ```
|
|
384
|
+
*/
|
|
385
|
+
function usePreviousValue(value) {
|
|
386
|
+
const currentRef = (0, react.useRef)(value);
|
|
387
|
+
const previousRef = (0, react.useRef)(null);
|
|
388
|
+
if (currentRef.current !== value) {
|
|
389
|
+
previousRef.current = currentRef.current;
|
|
390
|
+
currentRef.current = value;
|
|
391
|
+
}
|
|
392
|
+
return previousRef.current;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
//#endregion
|
|
396
|
+
//#region src/react/hooks/useClearQueriesOnSignOut.ts
|
|
397
|
+
const withInfiniteKey = (key) => [key, `${key}-inf`];
|
|
398
|
+
/**
|
|
399
|
+
* Clears React Query caches associated with the given stable prefixes when
|
|
400
|
+
* the authenticated state transitions from signed-in to signed-out.
|
|
401
|
+
*
|
|
402
|
+
* @internal
|
|
403
|
+
*/
|
|
404
|
+
function useClearQueriesOnSignOut(options) {
|
|
405
|
+
const { isSignedOut, stableKeys, authenticated = true, onCleanup } = options;
|
|
406
|
+
const stableKeysRef = (0, react.useRef)(stableKeys);
|
|
407
|
+
const [queryClient] = useClerkQueryClient();
|
|
408
|
+
const previousIsSignedIn = usePreviousValue(!isSignedOut);
|
|
409
|
+
(0, react.useEffect)(() => {
|
|
410
|
+
if (authenticated !== true) return;
|
|
411
|
+
if (previousIsSignedIn && isSignedOut === true) {
|
|
412
|
+
queryClient.removeQueries({ predicate: (query) => {
|
|
413
|
+
const [cachedStableKey, queryAuthenticated] = query.queryKey;
|
|
414
|
+
return queryAuthenticated === true && typeof cachedStableKey === "string" && (Array.isArray(stableKeysRef.current) ? stableKeysRef.current.includes(cachedStableKey) : stableKeysRef.current === cachedStableKey);
|
|
415
|
+
} });
|
|
416
|
+
onCleanup?.();
|
|
417
|
+
}
|
|
418
|
+
}, [
|
|
419
|
+
authenticated,
|
|
420
|
+
isSignedOut,
|
|
421
|
+
previousIsSignedIn,
|
|
422
|
+
queryClient
|
|
423
|
+
]);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
//#endregion
|
|
427
|
+
//#region src/react/hooks/usePagesOrInfinite.shared.ts
|
|
428
|
+
/**
|
|
429
|
+
* A hook that safely merges user-provided pagination options with default values.
|
|
430
|
+
* It caches initial pagination values (page and size) until component unmount to prevent unwanted rerenders.
|
|
431
|
+
*
|
|
432
|
+
* @internal
|
|
433
|
+
*
|
|
434
|
+
* @example
|
|
435
|
+
* ```typescript
|
|
436
|
+
* // Example 1: With user-provided options
|
|
437
|
+
* const userOptions = { initialPage: 2, pageSize: 20, infinite: true };
|
|
438
|
+
* const defaults = { initialPage: 1, pageSize: 10, infinite: false };
|
|
439
|
+
* useWithSafeValues(userOptions, defaults);
|
|
440
|
+
* // Returns { initialPage: 2, pageSize: 20, infinite: true }
|
|
441
|
+
*
|
|
442
|
+
* // Example 2: With boolean true (use defaults)
|
|
443
|
+
* const params = true;
|
|
444
|
+
* const defaults = { initialPage: 1, pageSize: 10, infinite: false };
|
|
445
|
+
* useWithSafeValues(params, defaults);
|
|
446
|
+
* // Returns { initialPage: 1, pageSize: 10, infinite: false }
|
|
447
|
+
*
|
|
448
|
+
* // Example 3: With undefined options (fallback to defaults)
|
|
449
|
+
* const params = undefined;
|
|
450
|
+
* const defaults = { initialPage: 1, pageSize: 10, infinite: false };
|
|
451
|
+
* useWithSafeValues(params, defaults);
|
|
452
|
+
* // Returns { initialPage: 1, pageSize: 10, infinite: false }
|
|
453
|
+
* ```
|
|
454
|
+
*/
|
|
455
|
+
const useWithSafeValues = (params, defaultValues) => {
|
|
456
|
+
const shouldUseDefaults = typeof params === "boolean" && params;
|
|
457
|
+
const initialPageRef = (0, react.useRef)(shouldUseDefaults ? defaultValues.initialPage : params?.initialPage ?? defaultValues.initialPage);
|
|
458
|
+
const pageSizeRef = (0, react.useRef)(shouldUseDefaults ? defaultValues.pageSize : params?.pageSize ?? defaultValues.pageSize);
|
|
459
|
+
const newObj = {};
|
|
460
|
+
for (const key of Object.keys(defaultValues)) newObj[key] = shouldUseDefaults ? defaultValues[key] : params?.[key] ?? defaultValues[key];
|
|
461
|
+
return {
|
|
462
|
+
...newObj,
|
|
463
|
+
initialPage: initialPageRef.current,
|
|
464
|
+
pageSize: pageSizeRef.current
|
|
465
|
+
};
|
|
466
|
+
};
|
|
467
|
+
/**
|
|
468
|
+
* Calculates the offset count for pagination based on initial page and page size.
|
|
469
|
+
* This represents the number of items to skip before the first page.
|
|
470
|
+
*
|
|
471
|
+
* @param initialPage - The starting page number (1-based)
|
|
472
|
+
* @param pageSize - The number of items per page
|
|
473
|
+
* @returns The number of items to offset
|
|
474
|
+
*
|
|
475
|
+
* @example
|
|
476
|
+
* ```typescript
|
|
477
|
+
* calculateOffsetCount(1, 10); // Returns 0 (no offset for first page)
|
|
478
|
+
* calculateOffsetCount(2, 10); // Returns 10 (skip first 10 items)
|
|
479
|
+
* calculateOffsetCount(3, 20); // Returns 40 (skip first 40 items)
|
|
480
|
+
* ```
|
|
481
|
+
*/
|
|
482
|
+
function calculateOffsetCount(initialPage, pageSize) {
|
|
483
|
+
return (initialPage - 1) * pageSize;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Calculates the total number of pages based on total count, offset, and page size.
|
|
487
|
+
*
|
|
488
|
+
* @param totalCount - The total number of items
|
|
489
|
+
* @param offsetCount - The number of items to offset (from calculateOffsetCount)
|
|
490
|
+
* @param pageSize - The number of items per page
|
|
491
|
+
* @returns The total number of pages
|
|
492
|
+
*
|
|
493
|
+
* @example
|
|
494
|
+
* ```typescript
|
|
495
|
+
* calculatePageCount(100, 0, 10); // Returns 10
|
|
496
|
+
* calculatePageCount(95, 0, 10); // Returns 10 (rounds up)
|
|
497
|
+
* calculatePageCount(100, 20, 10); // Returns 8 (100 - 20 = 80 items, 8 pages)
|
|
498
|
+
* ```
|
|
499
|
+
*/
|
|
500
|
+
function calculatePageCount(totalCount, offsetCount, pageSize) {
|
|
501
|
+
return Math.ceil((totalCount - offsetCount) / pageSize);
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Determines if there is a next page available in non-infinite pagination mode.
|
|
505
|
+
*
|
|
506
|
+
* @param totalCount - The total number of items
|
|
507
|
+
* @param offsetCount - The number of items to offset
|
|
508
|
+
* @param currentPage - The current page number (1-based)
|
|
509
|
+
* @param pageSize - The number of items per page
|
|
510
|
+
* @returns True if there are more items beyond the current page
|
|
511
|
+
*
|
|
512
|
+
* @example
|
|
513
|
+
* ```typescript
|
|
514
|
+
* calculateHasNextPage(100, 0, 1, 10); // Returns true (page 1 of 10)
|
|
515
|
+
* calculateHasNextPage(100, 0, 10, 10); // Returns false (last page)
|
|
516
|
+
* calculateHasNextPage(25, 0, 2, 10); // Returns true (page 2, 5 more items)
|
|
517
|
+
* calculateHasNextPage(20, 0, 2, 10); // Returns false (exactly 2 pages)
|
|
518
|
+
* ```
|
|
519
|
+
*/
|
|
520
|
+
function calculateHasNextPage(totalCount, offsetCount, currentPage, pageSize) {
|
|
521
|
+
return totalCount - offsetCount > currentPage * pageSize;
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Determines if there is a previous page available in non-infinite pagination mode.
|
|
525
|
+
*
|
|
526
|
+
* @param currentPage - The current page number (1-based)
|
|
527
|
+
* @param pageSize - The number of items per page
|
|
528
|
+
* @param offsetCount - The number of items to offset
|
|
529
|
+
* @returns True if there are pages before the current page
|
|
530
|
+
*
|
|
531
|
+
* @example
|
|
532
|
+
* ```typescript
|
|
533
|
+
* calculateHasPreviousPage(1, 10, 0); // Returns false (first page)
|
|
534
|
+
* calculateHasPreviousPage(2, 10, 0); // Returns true (can go back to page 1)
|
|
535
|
+
* calculateHasPreviousPage(1, 10, 10); // Returns false (first page with offset)
|
|
536
|
+
* ```
|
|
537
|
+
*/
|
|
538
|
+
function calculateHasPreviousPage(currentPage, pageSize, offsetCount) {
|
|
539
|
+
return (currentPage - 1) * pageSize > offsetCount;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
//#endregion
|
|
543
|
+
//#region src/react/hooks/usePagesOrInfinite.tsx
|
|
544
|
+
const usePagesOrInfinite = (params) => {
|
|
545
|
+
const { fetcher, config, keys } = params;
|
|
546
|
+
const [paginatedPage, setPaginatedPage] = (0, react.useState)(config.initialPage ?? 1);
|
|
547
|
+
const initialPageRef = (0, react.useRef)(config.initialPage ?? 1);
|
|
548
|
+
const pageSizeRef = (0, react.useRef)(config.pageSize ?? 10);
|
|
549
|
+
const enabled = config.enabled ?? true;
|
|
550
|
+
const isSignedIn = config.isSignedIn;
|
|
551
|
+
const triggerInfinite = config.infinite ?? false;
|
|
552
|
+
const cacheMode = config.__experimental_mode === "cache";
|
|
553
|
+
const keepPreviousData = config.keepPreviousData ?? false;
|
|
554
|
+
const [queryClient] = useClerkQueryClient();
|
|
555
|
+
const queriesEnabled = enabled && Boolean(fetcher) && !cacheMode && isSignedIn !== false;
|
|
556
|
+
const [forceUpdateCounter, setForceUpdateCounter] = (0, react.useState)(0);
|
|
557
|
+
const forceUpdate = (0, react.useCallback)((updater) => {
|
|
558
|
+
setForceUpdateCounter(updater);
|
|
559
|
+
}, []);
|
|
560
|
+
const pagesQueryKey = (0, react.useMemo)(() => {
|
|
561
|
+
const [stablePrefix, authenticated, tracked, untracked] = keys.queryKey;
|
|
562
|
+
return [
|
|
563
|
+
stablePrefix,
|
|
564
|
+
authenticated,
|
|
565
|
+
tracked,
|
|
566
|
+
{
|
|
567
|
+
...untracked,
|
|
568
|
+
args: {
|
|
569
|
+
...untracked.args,
|
|
570
|
+
initialPage: paginatedPage,
|
|
571
|
+
pageSize: pageSizeRef.current
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
];
|
|
575
|
+
}, [keys.queryKey, paginatedPage]);
|
|
576
|
+
const singlePageQuery = useClerkQuery({
|
|
577
|
+
queryKey: pagesQueryKey,
|
|
578
|
+
queryFn: ({ queryKey }) => {
|
|
579
|
+
const { args } = queryKey[3];
|
|
580
|
+
if (!fetcher) return;
|
|
581
|
+
return fetcher(args);
|
|
582
|
+
},
|
|
583
|
+
staleTime: 6e4,
|
|
584
|
+
enabled: queriesEnabled && !triggerInfinite,
|
|
585
|
+
placeholderData: defineKeepPreviousDataFn(keepPreviousData)
|
|
586
|
+
});
|
|
587
|
+
const infiniteQueryKey = (0, react.useMemo)(() => {
|
|
588
|
+
const [stablePrefix, authenticated, tracked, untracked] = keys.queryKey;
|
|
589
|
+
return [
|
|
590
|
+
stablePrefix + "-inf",
|
|
591
|
+
authenticated,
|
|
592
|
+
tracked,
|
|
593
|
+
untracked
|
|
594
|
+
];
|
|
595
|
+
}, [keys.queryKey]);
|
|
596
|
+
const infiniteQuery = useClerkInfiniteQuery({
|
|
597
|
+
queryKey: infiniteQueryKey,
|
|
598
|
+
initialPageParam: config.initialPage ?? 1,
|
|
599
|
+
getNextPageParam: (lastPage, allPages, lastPageParam) => {
|
|
600
|
+
const total = lastPage?.total_count ?? 0;
|
|
601
|
+
return (allPages.length + (config.initialPage ? config.initialPage - 1 : 0)) * (config.pageSize ?? 10) < total ? lastPageParam + 1 : void 0;
|
|
602
|
+
},
|
|
603
|
+
queryFn: ({ pageParam, queryKey }) => {
|
|
604
|
+
const { args } = queryKey[3];
|
|
605
|
+
if (!fetcher) return;
|
|
606
|
+
return fetcher({
|
|
607
|
+
...args,
|
|
608
|
+
initialPage: pageParam,
|
|
609
|
+
pageSize: pageSizeRef.current
|
|
610
|
+
});
|
|
611
|
+
},
|
|
612
|
+
staleTime: 6e4,
|
|
613
|
+
enabled: queriesEnabled && triggerInfinite
|
|
614
|
+
});
|
|
615
|
+
useClearQueriesOnSignOut({
|
|
616
|
+
isSignedOut: isSignedIn === false,
|
|
617
|
+
authenticated: keys.authenticated,
|
|
618
|
+
stableKeys: withInfiniteKey(keys.stableKey),
|
|
619
|
+
onCleanup: () => {
|
|
620
|
+
setPaginatedPage(initialPageRef.current);
|
|
621
|
+
Promise.resolve().then(() => forceUpdate((n) => n + 1));
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
const { data, count, page } = (0, react.useMemo)(() => {
|
|
625
|
+
if (triggerInfinite) {
|
|
626
|
+
const cachedData = queryClient.getQueryData(infiniteQueryKey);
|
|
627
|
+
const pages = queriesEnabled ? infiniteQuery.data?.pages ?? cachedData?.pages ?? [] : cachedData?.pages ?? [];
|
|
628
|
+
const validPages = Array.isArray(pages) ? pages.filter(Boolean) : [];
|
|
629
|
+
return {
|
|
630
|
+
data: validPages.map((a) => a?.data).flat().filter(Boolean) ?? [],
|
|
631
|
+
count: validPages[validPages.length - 1]?.total_count ?? 0,
|
|
632
|
+
page: validPages.length > 0 ? validPages.length : initialPageRef.current
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
const pageData = queriesEnabled ? singlePageQuery.data ?? queryClient.getQueryData(pagesQueryKey) : queryClient.getQueryData(pagesQueryKey);
|
|
636
|
+
return {
|
|
637
|
+
data: Array.isArray(pageData?.data) ? pageData.data : [],
|
|
638
|
+
count: typeof pageData?.total_count === "number" ? pageData.total_count : 0,
|
|
639
|
+
page: paginatedPage
|
|
640
|
+
};
|
|
641
|
+
}, [
|
|
642
|
+
queriesEnabled,
|
|
643
|
+
forceUpdateCounter,
|
|
644
|
+
triggerInfinite,
|
|
645
|
+
infiniteQuery.data?.pages,
|
|
646
|
+
singlePageQuery.data,
|
|
647
|
+
queryClient,
|
|
648
|
+
infiniteQueryKey,
|
|
649
|
+
pagesQueryKey,
|
|
650
|
+
paginatedPage
|
|
651
|
+
]);
|
|
652
|
+
const fetchPage = (0, react.useCallback)((numberOrgFn) => {
|
|
653
|
+
if (triggerInfinite) {
|
|
654
|
+
const next = typeof numberOrgFn === "function" ? numberOrgFn(page) : numberOrgFn;
|
|
655
|
+
const targetCount = Math.max(0, next);
|
|
656
|
+
const cachedData = queryClient.getQueryData(infiniteQueryKey);
|
|
657
|
+
if (targetCount - (infiniteQuery.data?.pages ?? cachedData?.pages ?? []).length > 0) infiniteQuery.fetchNextPage({ cancelRefetch: false });
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
return setPaginatedPage(numberOrgFn);
|
|
661
|
+
}, [
|
|
662
|
+
infiniteQuery,
|
|
663
|
+
page,
|
|
664
|
+
triggerInfinite,
|
|
665
|
+
queryClient,
|
|
666
|
+
infiniteQueryKey
|
|
667
|
+
]);
|
|
668
|
+
const isLoading = triggerInfinite ? infiniteQuery.isLoading : singlePageQuery.isLoading;
|
|
669
|
+
const isFetching = triggerInfinite ? infiniteQuery.isFetching : singlePageQuery.isFetching;
|
|
670
|
+
const error = (triggerInfinite ? infiniteQuery.error : singlePageQuery.error) ?? null;
|
|
671
|
+
const isError = !!error;
|
|
672
|
+
const fetchNext = (0, react.useCallback)(() => {
|
|
673
|
+
if (triggerInfinite) {
|
|
674
|
+
infiniteQuery.fetchNextPage({ cancelRefetch: false });
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
setPaginatedPage((n) => Math.max(0, n + 1));
|
|
678
|
+
}, [infiniteQuery, triggerInfinite]);
|
|
679
|
+
const fetchPrevious = (0, react.useCallback)(() => {
|
|
680
|
+
if (triggerInfinite) return;
|
|
681
|
+
setPaginatedPage((n) => Math.max(0, n - 1));
|
|
682
|
+
}, [triggerInfinite]);
|
|
683
|
+
const offsetCount = calculateOffsetCount(initialPageRef.current, pageSizeRef.current);
|
|
684
|
+
const pageCount = calculatePageCount(count, offsetCount, pageSizeRef.current);
|
|
685
|
+
const hasNextPage = triggerInfinite ? Boolean(infiniteQuery.hasNextPage) : calculateHasNextPage(count, offsetCount, page, pageSizeRef.current);
|
|
686
|
+
const hasPreviousPage = triggerInfinite ? Boolean(infiniteQuery.hasPreviousPage) : calculateHasPreviousPage(page, pageSizeRef.current, offsetCount);
|
|
687
|
+
const setData = (value) => {
|
|
688
|
+
if (triggerInfinite) {
|
|
689
|
+
queryClient.setQueryData(infiniteQueryKey, (prevValue = {}) => {
|
|
690
|
+
const prevPages = Array.isArray(prevValue?.pages) ? prevValue.pages : [];
|
|
691
|
+
const nextPages = typeof value === "function" ? value(prevPages) : value;
|
|
692
|
+
return {
|
|
693
|
+
...prevValue,
|
|
694
|
+
pages: nextPages
|
|
695
|
+
};
|
|
696
|
+
});
|
|
697
|
+
forceUpdate((n) => n + 1);
|
|
698
|
+
return Promise.resolve();
|
|
699
|
+
}
|
|
700
|
+
queryClient.setQueryData(pagesQueryKey, (prevValue = {
|
|
701
|
+
data: [],
|
|
702
|
+
total_count: 0
|
|
703
|
+
}) => {
|
|
704
|
+
return typeof value === "function" ? value(prevValue) : value;
|
|
705
|
+
});
|
|
706
|
+
forceUpdate((n) => n + 1);
|
|
707
|
+
return Promise.resolve();
|
|
708
|
+
};
|
|
709
|
+
const revalidate = async () => {
|
|
710
|
+
await queryClient.invalidateQueries({ queryKey: keys.invalidationKey });
|
|
711
|
+
const [stablePrefix, ...rest] = keys.invalidationKey;
|
|
712
|
+
return queryClient.invalidateQueries({ queryKey: [stablePrefix + "-inf", ...rest] });
|
|
713
|
+
};
|
|
714
|
+
return {
|
|
715
|
+
data,
|
|
716
|
+
count,
|
|
717
|
+
error,
|
|
718
|
+
isLoading,
|
|
719
|
+
isFetching,
|
|
720
|
+
isError,
|
|
721
|
+
page,
|
|
722
|
+
pageCount,
|
|
723
|
+
fetchPage,
|
|
724
|
+
fetchNext,
|
|
725
|
+
fetchPrevious,
|
|
726
|
+
hasNextPage,
|
|
727
|
+
hasPreviousPage,
|
|
728
|
+
revalidate,
|
|
729
|
+
setData
|
|
730
|
+
};
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
//#endregion
|
|
734
|
+
//#region src/react/hooks/useAPIKeys.tsx
|
|
735
|
+
/**
|
|
736
|
+
* The `useAPIKeys()` hook provides access to paginated API keys for the current user or organization.
|
|
737
|
+
*
|
|
738
|
+
* @example
|
|
739
|
+
* ### Basic usage with default pagination
|
|
740
|
+
*
|
|
741
|
+
* ```tsx
|
|
742
|
+
* const { data, isLoading, page, pageCount, fetchNext, fetchPrevious } = useAPIKeys({
|
|
743
|
+
* subject: 'user_123',
|
|
744
|
+
* pageSize: 10,
|
|
745
|
+
* initialPage: 1,
|
|
746
|
+
* });
|
|
747
|
+
* ```
|
|
748
|
+
*
|
|
749
|
+
* @example
|
|
750
|
+
* ### With search query
|
|
751
|
+
*
|
|
752
|
+
* ```tsx
|
|
753
|
+
* const [searchValue, setSearchValue] = useState('');
|
|
754
|
+
* const debouncedSearch = useDebounce(searchValue, 500);
|
|
755
|
+
*
|
|
756
|
+
* const { data, isLoading } = useAPIKeys({
|
|
757
|
+
* subject: 'user_123',
|
|
758
|
+
* query: debouncedSearch.trim(),
|
|
759
|
+
* pageSize: 10,
|
|
760
|
+
* });
|
|
761
|
+
* ```
|
|
762
|
+
*
|
|
763
|
+
* @example
|
|
764
|
+
* ### Infinite scroll
|
|
765
|
+
*
|
|
766
|
+
* ```tsx
|
|
767
|
+
* const { data, isLoading, fetchNext, hasNextPage } = useAPIKeys({
|
|
768
|
+
* subject: 'user_123',
|
|
769
|
+
* infinite: true,
|
|
770
|
+
* });
|
|
771
|
+
* ```
|
|
772
|
+
*/
|
|
773
|
+
function useAPIKeys(params) {
|
|
774
|
+
useAssertWrappedByClerkProvider("useAPIKeys");
|
|
775
|
+
const safeValues = useWithSafeValues(params, {
|
|
776
|
+
initialPage: 1,
|
|
777
|
+
pageSize: 10,
|
|
778
|
+
keepPreviousData: false,
|
|
779
|
+
infinite: false,
|
|
780
|
+
subject: "",
|
|
781
|
+
query: "",
|
|
782
|
+
enabled: true
|
|
783
|
+
});
|
|
784
|
+
const clerk = useClerkInstanceContext();
|
|
785
|
+
clerk.telemetry?.record(require_telemetry.eventMethodCalled("useAPIKeys"));
|
|
786
|
+
const hookParams = {
|
|
787
|
+
initialPage: safeValues.initialPage,
|
|
788
|
+
pageSize: safeValues.pageSize,
|
|
789
|
+
...safeValues.subject ? { subject: safeValues.subject } : {},
|
|
790
|
+
...safeValues.query ? { query: safeValues.query } : {}
|
|
791
|
+
};
|
|
792
|
+
const isEnabled = (safeValues.enabled ?? true) && clerk.loaded;
|
|
793
|
+
return usePagesOrInfinite({
|
|
794
|
+
fetcher: clerk.apiKeys?.getAll ? (params) => clerk.apiKeys.getAll({
|
|
795
|
+
...params,
|
|
796
|
+
subject: safeValues.subject
|
|
797
|
+
}) : void 0,
|
|
798
|
+
config: {
|
|
799
|
+
keepPreviousData: safeValues.keepPreviousData,
|
|
800
|
+
infinite: safeValues.infinite,
|
|
801
|
+
enabled: isEnabled,
|
|
802
|
+
isSignedIn: clerk.user !== null,
|
|
803
|
+
initialPage: safeValues.initialPage,
|
|
804
|
+
pageSize: safeValues.pageSize
|
|
805
|
+
},
|
|
806
|
+
keys: createCacheKeys({
|
|
807
|
+
stablePrefix: STABLE_KEYS.API_KEYS_KEY,
|
|
808
|
+
authenticated: true,
|
|
809
|
+
tracked: { subject: safeValues.subject },
|
|
810
|
+
untracked: { args: hookParams }
|
|
811
|
+
})
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
//#endregion
|
|
816
|
+
//#region src/react/hooks/base/useUserBase.ts
|
|
817
|
+
function useUserBase() {
|
|
818
|
+
const clerk = useClerkInstanceContext();
|
|
819
|
+
const initialState = useInitialStateContext();
|
|
820
|
+
const getInitialState = (0, react.useCallback)(() => initialState?.user, [initialState?.user]);
|
|
821
|
+
return (0, react.useSyncExternalStore)((0, react.useCallback)((callback) => {
|
|
822
|
+
return clerk.addListener(callback, { skipInitialEmit: true });
|
|
823
|
+
}, [clerk]), (0, react.useCallback)(() => {
|
|
824
|
+
if (!clerk.loaded || !clerk.__internal_lastEmittedResources) return getInitialState();
|
|
825
|
+
return clerk.__internal_lastEmittedResources.user;
|
|
826
|
+
}, [clerk, getInitialState]), getInitialState);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
//#endregion
|
|
830
|
+
//#region src/react/hooks/useOAuthConsent.shared.ts
|
|
831
|
+
function useOAuthConsentCacheKeys(params) {
|
|
832
|
+
const { userId, oauthClientId, scope, redirectUri } = params;
|
|
833
|
+
return (0, react.useMemo)(() => {
|
|
834
|
+
const args = {
|
|
835
|
+
oauthClientId,
|
|
836
|
+
...scope !== void 0 && { scope },
|
|
837
|
+
...redirectUri !== void 0 && { redirectUri }
|
|
838
|
+
};
|
|
839
|
+
return createCacheKeys({
|
|
840
|
+
stablePrefix: STABLE_KEYS.OAUTH_CONSENT_INFO_KEY,
|
|
841
|
+
authenticated: true,
|
|
842
|
+
tracked: { userId: userId ?? null },
|
|
843
|
+
untracked: { args }
|
|
844
|
+
});
|
|
845
|
+
}, [
|
|
846
|
+
userId,
|
|
847
|
+
oauthClientId,
|
|
848
|
+
scope,
|
|
849
|
+
redirectUri
|
|
850
|
+
]);
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
//#endregion
|
|
854
|
+
//#region src/react/hooks/useOAuthConsent.tsx
|
|
855
|
+
const HOOK_NAME$4 = "useOAuthConsent";
|
|
856
|
+
/**
|
|
857
|
+
* The `useOAuthConsent()` hook loads OAuth application consent metadata for the **signed-in** user
|
|
858
|
+
* (`GET /me/oauth/consent/{oauthClientId}`). Ensure the user is authenticated before relying on this hook
|
|
859
|
+
* (e.g., redirect to sign-in on your custom consent route).
|
|
860
|
+
*
|
|
861
|
+
* @example
|
|
862
|
+
* ```tsx
|
|
863
|
+
* import { useOAuthConsent } from '@clerk/react/internal'
|
|
864
|
+
*
|
|
865
|
+
* const { data, isLoading, error } = useOAuthConsent({
|
|
866
|
+
* oauthClientId: clientIdFromProps,
|
|
867
|
+
* scope: scopeFromProps,
|
|
868
|
+
* })
|
|
869
|
+
* ```
|
|
870
|
+
*/
|
|
871
|
+
function useOAuthConsent(params) {
|
|
872
|
+
useAssertWrappedByClerkProvider(HOOK_NAME$4);
|
|
873
|
+
const { oauthClientId: oauthClientIdParam, scope, redirectUri, keepPreviousData = true, enabled = true } = params;
|
|
874
|
+
const clerk = useClerkInstanceContext();
|
|
875
|
+
const user = useUserBase();
|
|
876
|
+
const oauthClientId = (oauthClientIdParam ?? "").trim();
|
|
877
|
+
clerk.telemetry?.record(require_telemetry.eventMethodCalled(HOOK_NAME$4));
|
|
878
|
+
const { queryKey } = useOAuthConsentCacheKeys({
|
|
879
|
+
userId: user?.id ?? null,
|
|
880
|
+
oauthClientId,
|
|
881
|
+
scope,
|
|
882
|
+
redirectUri
|
|
883
|
+
});
|
|
884
|
+
const hasClientId = oauthClientId.length > 0;
|
|
885
|
+
const queryEnabled = Boolean(user) && hasClientId && enabled && clerk.loaded;
|
|
886
|
+
const query = useClerkQuery({
|
|
887
|
+
queryKey,
|
|
888
|
+
queryFn: () => fetchConsentInfo(clerk, {
|
|
889
|
+
oauthClientId,
|
|
890
|
+
scope,
|
|
891
|
+
redirectUri
|
|
892
|
+
}),
|
|
893
|
+
enabled: queryEnabled,
|
|
894
|
+
placeholderData: defineKeepPreviousDataFn(keepPreviousData && queryEnabled)
|
|
895
|
+
});
|
|
896
|
+
return {
|
|
897
|
+
data: query.data,
|
|
898
|
+
error: query.error ?? null,
|
|
899
|
+
isLoading: query.isLoading,
|
|
900
|
+
isFetching: query.isFetching
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
function fetchConsentInfo(clerk, params) {
|
|
904
|
+
return clerk.oauthApplication.getConsentInfo(params);
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
//#endregion
|
|
908
|
+
//#region src/react/hooks/base/useOrganizationBase.ts
|
|
909
|
+
function useOrganizationBase() {
|
|
910
|
+
const clerk = useClerkInstanceContext();
|
|
911
|
+
const initialState = useInitialStateContext();
|
|
912
|
+
const getInitialState = (0, react.useCallback)(() => initialState?.organization, [initialState?.organization]);
|
|
913
|
+
return (0, react.useSyncExternalStore)((0, react.useCallback)((callback) => clerk.addListener(callback, { skipInitialEmit: true }), [clerk]), (0, react.useCallback)(() => {
|
|
914
|
+
if (!clerk.loaded || !clerk.__internal_lastEmittedResources) return getInitialState();
|
|
915
|
+
return clerk.__internal_lastEmittedResources.organization;
|
|
916
|
+
}, [clerk, getInitialState]), getInitialState);
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
//#endregion
|
|
920
|
+
//#region src/react/hooks/base/useSessionBase.ts
|
|
921
|
+
function useSessionBase() {
|
|
922
|
+
const clerk = useClerkInstanceContext();
|
|
923
|
+
const initialState = useInitialStateContext();
|
|
924
|
+
const getInitialState = (0, react.useCallback)(() => {
|
|
925
|
+
return initialState ? require_deriveState.deriveFromSsrInitialState(initialState)?.session : void 0;
|
|
926
|
+
}, [initialState]);
|
|
927
|
+
return (0, react.useSyncExternalStore)((0, react.useCallback)((callback) => clerk.addListener(callback, { skipInitialEmit: true }), [clerk]), (0, react.useCallback)(() => {
|
|
928
|
+
if (!clerk.loaded || !clerk.__internal_lastEmittedResources) return getInitialState();
|
|
929
|
+
return clerk.__internal_lastEmittedResources.session;
|
|
930
|
+
}, [clerk, getInitialState]), getInitialState);
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
//#endregion
|
|
934
|
+
//#region src/react/hooks/useClerk.ts
|
|
935
|
+
/**
|
|
936
|
+
* > [!WARNING]
|
|
937
|
+
* > This hook should only be used for advanced use cases, such as building a completely custom OAuth flow or as an escape hatch to access to the `Clerk` object.
|
|
938
|
+
*
|
|
939
|
+
* The `useClerk()` hook provides access to the [`Clerk`](https://clerk.com/docs/reference/objects/clerk) object, allowing you to build alternatives to any Clerk Component.
|
|
940
|
+
*
|
|
941
|
+
* @function
|
|
942
|
+
*
|
|
943
|
+
* @returns The `useClerk()` hook returns the `Clerk` object, which includes all the methods and properties listed in the [`Clerk` reference](https://clerk.com/docs/reference/objects/clerk).
|
|
944
|
+
*
|
|
945
|
+
* @example
|
|
946
|
+
*
|
|
947
|
+
* The following example uses the `useClerk()` hook to access the `clerk` object. The `clerk` object is used to call the [`openSignIn()`](https://clerk.com/docs/reference/objects/clerk#sign-in) method to open the sign-in modal.
|
|
948
|
+
*
|
|
949
|
+
* <Tabs items='React,Next.js'>
|
|
950
|
+
* <Tab>
|
|
951
|
+
*
|
|
952
|
+
* ```tsx {{ filename: 'src/Home.tsx' }}
|
|
953
|
+
* import { useClerk } from '@clerk/react'
|
|
954
|
+
*
|
|
955
|
+
* export default function Home() {
|
|
956
|
+
* const clerk = useClerk()
|
|
957
|
+
*
|
|
958
|
+
* return <button onClick={() => clerk.openSignIn({})}>Sign in</button>
|
|
959
|
+
* }
|
|
960
|
+
* ```
|
|
961
|
+
*
|
|
962
|
+
* </Tab>
|
|
963
|
+
* <Tab>
|
|
964
|
+
*
|
|
965
|
+
* ```tsx {{ filename: 'app/page.tsx' }}
|
|
966
|
+
* 'use client';
|
|
967
|
+
*
|
|
968
|
+
* import { useClerk } from '@clerk/nextjs';
|
|
969
|
+
*
|
|
970
|
+
* export default function HomePage() {
|
|
971
|
+
* const clerk = useClerk();
|
|
972
|
+
*
|
|
973
|
+
* return <button onClick={() => clerk.openSignIn({})}>Sign in</button>;
|
|
974
|
+
* }
|
|
975
|
+
* ```
|
|
976
|
+
*
|
|
977
|
+
* </Tab>
|
|
978
|
+
* </Tabs>
|
|
979
|
+
*/
|
|
980
|
+
const useClerk = () => {
|
|
981
|
+
useAssertWrappedByClerkProvider("useClerk");
|
|
982
|
+
return useClerkInstanceContext();
|
|
983
|
+
};
|
|
984
|
+
|
|
985
|
+
//#endregion
|
|
986
|
+
//#region src/react/hooks/useAttemptToEnableOrganizations.ts
|
|
987
|
+
/**
|
|
988
|
+
* Attempts to enable the organizations environment setting for a given caller
|
|
989
|
+
*
|
|
990
|
+
* @internal
|
|
991
|
+
*/
|
|
992
|
+
function useAttemptToEnableOrganizations(caller) {
|
|
993
|
+
const clerk = useClerk();
|
|
994
|
+
const hasAttempted = (0, react.useRef)(false);
|
|
995
|
+
(0, react.useEffect)(() => {
|
|
996
|
+
if (hasAttempted.current) return;
|
|
997
|
+
hasAttempted.current = true;
|
|
998
|
+
clerk.__internal_attemptToEnableEnvironmentSetting?.({
|
|
999
|
+
for: "organizations",
|
|
1000
|
+
caller
|
|
1001
|
+
});
|
|
1002
|
+
}, [clerk, caller]);
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
//#endregion
|
|
1006
|
+
//#region src/react/hooks/useOrganization.tsx
|
|
1007
|
+
const undefinedPaginatedResource$1 = {
|
|
1008
|
+
data: void 0,
|
|
1009
|
+
count: void 0,
|
|
1010
|
+
error: void 0,
|
|
1011
|
+
isLoading: false,
|
|
1012
|
+
isFetching: false,
|
|
1013
|
+
isError: false,
|
|
1014
|
+
page: void 0,
|
|
1015
|
+
pageCount: void 0,
|
|
1016
|
+
fetchPage: void 0,
|
|
1017
|
+
fetchNext: void 0,
|
|
1018
|
+
fetchPrevious: void 0,
|
|
1019
|
+
hasNextPage: false,
|
|
1020
|
+
hasPreviousPage: false,
|
|
1021
|
+
revalidate: void 0,
|
|
1022
|
+
setData: void 0
|
|
1023
|
+
};
|
|
1024
|
+
/**
|
|
1025
|
+
* The `useOrganization()` hook retrieves attributes of the currently Active Organization.
|
|
1026
|
+
*
|
|
1027
|
+
* @example
|
|
1028
|
+
* ### Expand and paginate attributes
|
|
1029
|
+
*
|
|
1030
|
+
* To keep network usage to a minimum, developers are required to opt-in by specifying which resource they need to fetch and paginate through. By default, the `memberships`, `invitations`, `membershipRequests`, and `domains` attributes are not populated. You must pass `true` or an object with the desired properties to fetch and paginate the data.
|
|
1031
|
+
*
|
|
1032
|
+
* ```tsx
|
|
1033
|
+
* // invitations.data will never be populated.
|
|
1034
|
+
* const { invitations } = useOrganization()
|
|
1035
|
+
*
|
|
1036
|
+
* // Use default values to fetch invitations, such as initialPage = 1 and pageSize = 10
|
|
1037
|
+
* const { invitations } = useOrganization({
|
|
1038
|
+
* invitations: true,
|
|
1039
|
+
* })
|
|
1040
|
+
*
|
|
1041
|
+
* // Pass your own values to fetch invitations
|
|
1042
|
+
* const { invitations } = useOrganization({
|
|
1043
|
+
* invitations: {
|
|
1044
|
+
* pageSize: 20,
|
|
1045
|
+
* initialPage: 2, // skips the first page
|
|
1046
|
+
* },
|
|
1047
|
+
* })
|
|
1048
|
+
*
|
|
1049
|
+
* // Aggregate pages in order to render an infinite list
|
|
1050
|
+
* const { invitations } = useOrganization({
|
|
1051
|
+
* invitations: {
|
|
1052
|
+
* infinite: true,
|
|
1053
|
+
* },
|
|
1054
|
+
* })
|
|
1055
|
+
* ```
|
|
1056
|
+
*
|
|
1057
|
+
* @example
|
|
1058
|
+
* ### Infinite pagination
|
|
1059
|
+
*
|
|
1060
|
+
* The following example demonstrates how to use the `infinite` property to fetch and append new data to the existing list. The `memberships` attribute will be populated with the first page of the Organization's memberships. When the "Load more" button is clicked, the `fetchNext` helper function will be called to append the next page of memberships to the list.
|
|
1061
|
+
*
|
|
1062
|
+
* ```tsx
|
|
1063
|
+
* import { useOrganization } from '@clerk/react'
|
|
1064
|
+
*
|
|
1065
|
+
* export default function MemberList() {
|
|
1066
|
+
* const { memberships } = useOrganization({
|
|
1067
|
+
* memberships: {
|
|
1068
|
+
* infinite: true, // Append new data to the existing list
|
|
1069
|
+
* keepPreviousData: true, // Persist the cached data until the new data has been fetched
|
|
1070
|
+
* },
|
|
1071
|
+
* })
|
|
1072
|
+
*
|
|
1073
|
+
* if (!memberships) {
|
|
1074
|
+
* // Handle loading state
|
|
1075
|
+
* return null
|
|
1076
|
+
* }
|
|
1077
|
+
*
|
|
1078
|
+
* return (
|
|
1079
|
+
* <div>
|
|
1080
|
+
* <h2>Organization members</h2>
|
|
1081
|
+
* <ul>
|
|
1082
|
+
* {memberships.data?.map((membership) => (
|
|
1083
|
+
* <li key={membership.id}>
|
|
1084
|
+
* {membership.publicUserData.firstName} {membership.publicUserData.lastName} <
|
|
1085
|
+
* {membership.publicUserData.identifier}> :: {membership.role}
|
|
1086
|
+
* </li>
|
|
1087
|
+
* ))}
|
|
1088
|
+
* </ul>
|
|
1089
|
+
*
|
|
1090
|
+
* <button
|
|
1091
|
+
* disabled={!memberships.hasNextPage} // Disable the button if there are no more available pages to be fetched
|
|
1092
|
+
* onClick={memberships.fetchNext}
|
|
1093
|
+
* >
|
|
1094
|
+
* Load more
|
|
1095
|
+
* </button>
|
|
1096
|
+
* </div>
|
|
1097
|
+
* )
|
|
1098
|
+
* }
|
|
1099
|
+
* ```
|
|
1100
|
+
*
|
|
1101
|
+
* @example
|
|
1102
|
+
* ### Simple pagination
|
|
1103
|
+
*
|
|
1104
|
+
* The following example demonstrates how to use the `fetchPrevious` and `fetchNext` helper functions to paginate through the data. The `memberships` attribute will be populated with the first page of the Organization's memberships. When the "Previous page" or "Next page" button is clicked, the `fetchPrevious` or `fetchNext` helper function will be called to fetch the previous or next page of memberships.
|
|
1105
|
+
*
|
|
1106
|
+
* Notice the difference between this example's pagination and the infinite pagination example above.
|
|
1107
|
+
*
|
|
1108
|
+
* ```tsx
|
|
1109
|
+
* import { useOrganization } from '@clerk/react'
|
|
1110
|
+
*
|
|
1111
|
+
* export default function MemberList() {
|
|
1112
|
+
* const { memberships } = useOrganization({
|
|
1113
|
+
* memberships: {
|
|
1114
|
+
* keepPreviousData: true, // Persist the cached data until the new data has been fetched
|
|
1115
|
+
* },
|
|
1116
|
+
* })
|
|
1117
|
+
*
|
|
1118
|
+
* if (!memberships) {
|
|
1119
|
+
* // Handle loading state
|
|
1120
|
+
* return null
|
|
1121
|
+
* }
|
|
1122
|
+
*
|
|
1123
|
+
* return (
|
|
1124
|
+
* <div>
|
|
1125
|
+
* <h2>Organization members</h2>
|
|
1126
|
+
* <ul>
|
|
1127
|
+
* {memberships.data?.map((membership) => (
|
|
1128
|
+
* <li key={membership.id}>
|
|
1129
|
+
* {membership.publicUserData.firstName} {membership.publicUserData.lastName} <
|
|
1130
|
+
* {membership.publicUserData.identifier}> :: {membership.role}
|
|
1131
|
+
* </li>
|
|
1132
|
+
* ))}
|
|
1133
|
+
* </ul>
|
|
1134
|
+
*
|
|
1135
|
+
* <button disabled={!memberships.hasPreviousPage} onClick={memberships.fetchPrevious}>
|
|
1136
|
+
* Previous page
|
|
1137
|
+
* </button>
|
|
1138
|
+
*
|
|
1139
|
+
* <button disabled={!memberships.hasNextPage} onClick={memberships.fetchNext}>
|
|
1140
|
+
* Next page
|
|
1141
|
+
* </button>
|
|
1142
|
+
* </div>
|
|
1143
|
+
* )
|
|
1144
|
+
* }
|
|
1145
|
+
* ```
|
|
1146
|
+
*/
|
|
1147
|
+
function useOrganization(params) {
|
|
1148
|
+
const { domains: domainListParams, membershipRequests: membershipRequestsListParams, memberships: membersListParams, invitations: invitationsListParams } = params || {};
|
|
1149
|
+
useAssertWrappedByClerkProvider("useOrganization");
|
|
1150
|
+
useAttemptToEnableOrganizations("useOrganization");
|
|
1151
|
+
const organization = useOrganizationBase();
|
|
1152
|
+
const session = useSessionBase();
|
|
1153
|
+
const domainSafeValues = useWithSafeValues(domainListParams, {
|
|
1154
|
+
initialPage: 1,
|
|
1155
|
+
pageSize: 10,
|
|
1156
|
+
keepPreviousData: false,
|
|
1157
|
+
infinite: false,
|
|
1158
|
+
enrollmentMode: void 0
|
|
1159
|
+
});
|
|
1160
|
+
const membershipRequestSafeValues = useWithSafeValues(membershipRequestsListParams, {
|
|
1161
|
+
initialPage: 1,
|
|
1162
|
+
pageSize: 10,
|
|
1163
|
+
status: "pending",
|
|
1164
|
+
keepPreviousData: false,
|
|
1165
|
+
infinite: false
|
|
1166
|
+
});
|
|
1167
|
+
const membersSafeValues = useWithSafeValues(membersListParams, {
|
|
1168
|
+
initialPage: 1,
|
|
1169
|
+
pageSize: 10,
|
|
1170
|
+
role: void 0,
|
|
1171
|
+
keepPreviousData: false,
|
|
1172
|
+
infinite: false,
|
|
1173
|
+
query: void 0
|
|
1174
|
+
});
|
|
1175
|
+
const invitationsSafeValues = useWithSafeValues(invitationsListParams, {
|
|
1176
|
+
initialPage: 1,
|
|
1177
|
+
pageSize: 10,
|
|
1178
|
+
status: ["pending"],
|
|
1179
|
+
keepPreviousData: false,
|
|
1180
|
+
infinite: false
|
|
1181
|
+
});
|
|
1182
|
+
const clerk = useClerkInstanceContext();
|
|
1183
|
+
clerk.telemetry?.record(require_telemetry.eventMethodCalled("useOrganization"));
|
|
1184
|
+
const domainParams = typeof domainListParams === "undefined" ? void 0 : {
|
|
1185
|
+
initialPage: domainSafeValues.initialPage,
|
|
1186
|
+
pageSize: domainSafeValues.pageSize,
|
|
1187
|
+
enrollmentMode: domainSafeValues.enrollmentMode
|
|
1188
|
+
};
|
|
1189
|
+
const membershipRequestParams = typeof membershipRequestsListParams === "undefined" ? void 0 : {
|
|
1190
|
+
initialPage: membershipRequestSafeValues.initialPage,
|
|
1191
|
+
pageSize: membershipRequestSafeValues.pageSize,
|
|
1192
|
+
status: membershipRequestSafeValues.status
|
|
1193
|
+
};
|
|
1194
|
+
const membersParams = typeof membersListParams === "undefined" ? void 0 : {
|
|
1195
|
+
initialPage: membersSafeValues.initialPage,
|
|
1196
|
+
pageSize: membersSafeValues.pageSize,
|
|
1197
|
+
role: membersSafeValues.role,
|
|
1198
|
+
query: membersSafeValues.query
|
|
1199
|
+
};
|
|
1200
|
+
const invitationsParams = typeof invitationsListParams === "undefined" ? void 0 : {
|
|
1201
|
+
initialPage: invitationsSafeValues.initialPage,
|
|
1202
|
+
pageSize: invitationsSafeValues.pageSize,
|
|
1203
|
+
status: invitationsSafeValues.status
|
|
1204
|
+
};
|
|
1205
|
+
const domains = usePagesOrInfinite({
|
|
1206
|
+
fetcher: organization?.getDomains,
|
|
1207
|
+
config: {
|
|
1208
|
+
keepPreviousData: domainSafeValues.keepPreviousData,
|
|
1209
|
+
infinite: domainSafeValues.infinite,
|
|
1210
|
+
enabled: !!domainParams,
|
|
1211
|
+
isSignedIn: organization !== null,
|
|
1212
|
+
initialPage: domainSafeValues.initialPage,
|
|
1213
|
+
pageSize: domainSafeValues.pageSize
|
|
1214
|
+
},
|
|
1215
|
+
keys: createCacheKeys({
|
|
1216
|
+
stablePrefix: STABLE_KEYS.DOMAINS_KEY,
|
|
1217
|
+
authenticated: true,
|
|
1218
|
+
tracked: { organizationId: organization?.id },
|
|
1219
|
+
untracked: { args: domainParams }
|
|
1220
|
+
})
|
|
1221
|
+
});
|
|
1222
|
+
const membershipRequests = usePagesOrInfinite({
|
|
1223
|
+
fetcher: organization?.getMembershipRequests,
|
|
1224
|
+
config: {
|
|
1225
|
+
keepPreviousData: membershipRequestSafeValues.keepPreviousData,
|
|
1226
|
+
infinite: membershipRequestSafeValues.infinite,
|
|
1227
|
+
enabled: !!membershipRequestParams,
|
|
1228
|
+
isSignedIn: organization !== null,
|
|
1229
|
+
initialPage: membershipRequestSafeValues.initialPage,
|
|
1230
|
+
pageSize: membershipRequestSafeValues.pageSize
|
|
1231
|
+
},
|
|
1232
|
+
keys: createCacheKeys({
|
|
1233
|
+
stablePrefix: STABLE_KEYS.MEMBERSHIP_REQUESTS_KEY,
|
|
1234
|
+
authenticated: true,
|
|
1235
|
+
tracked: { organizationId: organization?.id },
|
|
1236
|
+
untracked: { args: membershipRequestParams }
|
|
1237
|
+
})
|
|
1238
|
+
});
|
|
1239
|
+
const memberships = usePagesOrInfinite({
|
|
1240
|
+
fetcher: organization?.getMemberships,
|
|
1241
|
+
config: {
|
|
1242
|
+
keepPreviousData: membersSafeValues.keepPreviousData,
|
|
1243
|
+
infinite: membersSafeValues.infinite,
|
|
1244
|
+
enabled: !!membersParams,
|
|
1245
|
+
isSignedIn: organization !== null,
|
|
1246
|
+
initialPage: membersSafeValues.initialPage,
|
|
1247
|
+
pageSize: membersSafeValues.pageSize
|
|
1248
|
+
},
|
|
1249
|
+
keys: createCacheKeys({
|
|
1250
|
+
stablePrefix: STABLE_KEYS.MEMBERSHIPS_KEY,
|
|
1251
|
+
authenticated: true,
|
|
1252
|
+
tracked: { organizationId: organization?.id },
|
|
1253
|
+
untracked: { args: membersParams }
|
|
1254
|
+
})
|
|
1255
|
+
});
|
|
1256
|
+
const invitations = usePagesOrInfinite({
|
|
1257
|
+
fetcher: organization?.getInvitations,
|
|
1258
|
+
config: {
|
|
1259
|
+
keepPreviousData: invitationsSafeValues.keepPreviousData,
|
|
1260
|
+
infinite: invitationsSafeValues.infinite,
|
|
1261
|
+
enabled: !!invitationsParams,
|
|
1262
|
+
isSignedIn: organization !== null,
|
|
1263
|
+
initialPage: invitationsSafeValues.initialPage,
|
|
1264
|
+
pageSize: invitationsSafeValues.pageSize
|
|
1265
|
+
},
|
|
1266
|
+
keys: createCacheKeys({
|
|
1267
|
+
stablePrefix: STABLE_KEYS.INVITATIONS_KEY,
|
|
1268
|
+
authenticated: true,
|
|
1269
|
+
tracked: { organizationId: organization?.id },
|
|
1270
|
+
untracked: { args: invitationsParams }
|
|
1271
|
+
})
|
|
1272
|
+
});
|
|
1273
|
+
if (organization === void 0) return {
|
|
1274
|
+
isLoaded: false,
|
|
1275
|
+
organization: void 0,
|
|
1276
|
+
membership: void 0,
|
|
1277
|
+
domains: undefinedPaginatedResource$1,
|
|
1278
|
+
membershipRequests: undefinedPaginatedResource$1,
|
|
1279
|
+
memberships: undefinedPaginatedResource$1,
|
|
1280
|
+
invitations: undefinedPaginatedResource$1
|
|
1281
|
+
};
|
|
1282
|
+
if (organization === null) return {
|
|
1283
|
+
isLoaded: true,
|
|
1284
|
+
organization: null,
|
|
1285
|
+
membership: null,
|
|
1286
|
+
domains: null,
|
|
1287
|
+
membershipRequests: null,
|
|
1288
|
+
memberships: null,
|
|
1289
|
+
invitations: null
|
|
1290
|
+
};
|
|
1291
|
+
/** In SSR context we include only the organization object when loadOrg is set to true. */
|
|
1292
|
+
if (!clerk.loaded && organization) return {
|
|
1293
|
+
isLoaded: true,
|
|
1294
|
+
organization,
|
|
1295
|
+
membership: void 0,
|
|
1296
|
+
domains: undefinedPaginatedResource$1,
|
|
1297
|
+
membershipRequests: undefinedPaginatedResource$1,
|
|
1298
|
+
memberships: undefinedPaginatedResource$1,
|
|
1299
|
+
invitations: undefinedPaginatedResource$1
|
|
1300
|
+
};
|
|
1301
|
+
return {
|
|
1302
|
+
isLoaded: clerk.loaded,
|
|
1303
|
+
organization,
|
|
1304
|
+
membership: require_organization.getCurrentOrganizationMembership(session.user.organizationMemberships, organization.id),
|
|
1305
|
+
domains,
|
|
1306
|
+
membershipRequests,
|
|
1307
|
+
memberships,
|
|
1308
|
+
invitations
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
//#endregion
|
|
1313
|
+
//#region src/react/hooks/useOrganizationCreationDefaults.shared.ts
|
|
1314
|
+
function useOrganizationCreationDefaultsCacheKeys(params) {
|
|
1315
|
+
const { userId } = params;
|
|
1316
|
+
return (0, react.useMemo)(() => {
|
|
1317
|
+
return createCacheKeys({
|
|
1318
|
+
stablePrefix: STABLE_KEYS.ORGANIZATION_CREATION_DEFAULTS_KEY,
|
|
1319
|
+
authenticated: Boolean(userId),
|
|
1320
|
+
tracked: { userId: userId ?? null },
|
|
1321
|
+
untracked: { args: {} }
|
|
1322
|
+
});
|
|
1323
|
+
}, [userId]);
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
//#endregion
|
|
1327
|
+
//#region src/react/hooks/useOrganizationCreationDefaults.tsx
|
|
1328
|
+
const HOOK_NAME$3 = "useOrganizationCreationDefaults";
|
|
1329
|
+
/**
|
|
1330
|
+
* The `useOrganizationCreationDefaults()` hook retrieves the organization creation defaults for the current user.
|
|
1331
|
+
*
|
|
1332
|
+
* @example
|
|
1333
|
+
* ### Basic usage
|
|
1334
|
+
*
|
|
1335
|
+
* ```tsx
|
|
1336
|
+
* import { useOrganizationCreationDefaults } from '@clerk/clerk-react'
|
|
1337
|
+
*
|
|
1338
|
+
* export default function CreateOrganizationForm() {
|
|
1339
|
+
* const { data, isLoading } = useOrganizationCreationDefaults()
|
|
1340
|
+
*
|
|
1341
|
+
* if (isLoading) return <div>Loading...</div>
|
|
1342
|
+
*
|
|
1343
|
+
* return (
|
|
1344
|
+
* <form>
|
|
1345
|
+
* <input defaultValue={data?.form.name} placeholder="Organization name" />
|
|
1346
|
+
* <input defaultValue={data?.form.slug} placeholder="Slug" />
|
|
1347
|
+
* <button type="submit">Create</button>
|
|
1348
|
+
* </form>
|
|
1349
|
+
* )
|
|
1350
|
+
* }
|
|
1351
|
+
* ```
|
|
1352
|
+
*/
|
|
1353
|
+
function useOrganizationCreationDefaults(params = {}) {
|
|
1354
|
+
useAssertWrappedByClerkProvider(HOOK_NAME$3);
|
|
1355
|
+
const { keepPreviousData = true, enabled = true } = params;
|
|
1356
|
+
const clerk = useClerkInstanceContext();
|
|
1357
|
+
const user = useUserBase();
|
|
1358
|
+
const featureEnabled = clerk.__internal_environment?.organizationSettings?.organizationCreationDefaults?.enabled ?? false;
|
|
1359
|
+
clerk.telemetry?.record(require_telemetry.eventMethodCalled(HOOK_NAME$3));
|
|
1360
|
+
const { queryKey } = useOrganizationCreationDefaultsCacheKeys({ userId: user?.id ?? null });
|
|
1361
|
+
const queryEnabled = Boolean(user) && enabled && featureEnabled && clerk.loaded;
|
|
1362
|
+
const query = useClerkQuery({
|
|
1363
|
+
queryKey,
|
|
1364
|
+
queryFn: user?.getOrganizationCreationDefaults,
|
|
1365
|
+
enabled: queryEnabled,
|
|
1366
|
+
placeholderData: defineKeepPreviousDataFn(keepPreviousData)
|
|
1367
|
+
});
|
|
1368
|
+
return {
|
|
1369
|
+
data: query.data,
|
|
1370
|
+
error: query.error ?? null,
|
|
1371
|
+
isLoading: query.isLoading,
|
|
1372
|
+
isFetching: query.isFetching
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
//#endregion
|
|
1377
|
+
//#region src/react/hooks/useOrganizationList.tsx
|
|
1378
|
+
const undefinedPaginatedResource = {
|
|
1379
|
+
data: void 0,
|
|
1380
|
+
count: void 0,
|
|
1381
|
+
error: void 0,
|
|
1382
|
+
isLoading: false,
|
|
1383
|
+
isFetching: false,
|
|
1384
|
+
isError: false,
|
|
1385
|
+
page: void 0,
|
|
1386
|
+
pageCount: void 0,
|
|
1387
|
+
fetchPage: void 0,
|
|
1388
|
+
fetchNext: void 0,
|
|
1389
|
+
fetchPrevious: void 0,
|
|
1390
|
+
hasNextPage: false,
|
|
1391
|
+
hasPreviousPage: false,
|
|
1392
|
+
revalidate: void 0,
|
|
1393
|
+
setData: void 0
|
|
1394
|
+
};
|
|
1395
|
+
/**
|
|
1396
|
+
* The `useOrganizationList()` hook provides access to the current user's organization memberships, invitations, and suggestions. It also includes methods for creating new organizations and managing the active organization.
|
|
1397
|
+
*
|
|
1398
|
+
* @example
|
|
1399
|
+
* ### Expanding and paginating attributes
|
|
1400
|
+
*
|
|
1401
|
+
* To keep network usage to a minimum, developers are required to opt-in by specifying which resource they need to fetch and paginate through. So by default, the `userMemberships`, `userInvitations`, and `userSuggestions` attributes are not populated. You must pass true or an object with the desired properties to fetch and paginate the data.
|
|
1402
|
+
*
|
|
1403
|
+
* ```tsx
|
|
1404
|
+
* // userMemberships.data will never be populated
|
|
1405
|
+
* const { userMemberships } = useOrganizationList()
|
|
1406
|
+
*
|
|
1407
|
+
* // Use default values to fetch userMemberships, such as initialPage = 1 and pageSize = 10
|
|
1408
|
+
* const { userMemberships } = useOrganizationList({
|
|
1409
|
+
* userMemberships: true,
|
|
1410
|
+
* })
|
|
1411
|
+
*
|
|
1412
|
+
* // Pass your own values to fetch userMemberships
|
|
1413
|
+
* const { userMemberships } = useOrganizationList({
|
|
1414
|
+
* userMemberships: {
|
|
1415
|
+
* pageSize: 20,
|
|
1416
|
+
* initialPage: 2, // skips the first page
|
|
1417
|
+
* },
|
|
1418
|
+
* })
|
|
1419
|
+
*
|
|
1420
|
+
* // Aggregate pages in order to render an infinite list
|
|
1421
|
+
* const { userMemberships } = useOrganizationList({
|
|
1422
|
+
* userMemberships: {
|
|
1423
|
+
* infinite: true,
|
|
1424
|
+
* },
|
|
1425
|
+
* })
|
|
1426
|
+
* ```
|
|
1427
|
+
*
|
|
1428
|
+
* @example
|
|
1429
|
+
* ### Infinite pagination
|
|
1430
|
+
*
|
|
1431
|
+
* The following example demonstrates how to use the `infinite` property to fetch and append new data to the existing list. The `userMemberships` attribute will be populated with the first page of the user's Organization memberships. When the "Load more" button is clicked, the `fetchNext` helper function will be called to append the next page of memberships to the list.
|
|
1432
|
+
*
|
|
1433
|
+
* ```tsx {{ filename: 'src/components/JoinedOrganizations.tsx' }}
|
|
1434
|
+
* import { useOrganizationList } from '@clerk/react'
|
|
1435
|
+
* import React from 'react'
|
|
1436
|
+
*
|
|
1437
|
+
* const JoinedOrganizations = () => {
|
|
1438
|
+
* const { isLoaded, setActive, userMemberships } = useOrganizationList({
|
|
1439
|
+
* userMemberships: {
|
|
1440
|
+
* infinite: true,
|
|
1441
|
+
* },
|
|
1442
|
+
* })
|
|
1443
|
+
*
|
|
1444
|
+
* if (!isLoaded) {
|
|
1445
|
+
* return <>Loading</>
|
|
1446
|
+
* }
|
|
1447
|
+
*
|
|
1448
|
+
* return (
|
|
1449
|
+
* <>
|
|
1450
|
+
* <ul>
|
|
1451
|
+
* {userMemberships.data?.map((mem) => (
|
|
1452
|
+
* <li key={mem.id}>
|
|
1453
|
+
* <span>{mem.organization.name}</span>
|
|
1454
|
+
* <button onClick={() => setActive({ organization: mem.organization.id })}>Select</button>
|
|
1455
|
+
* </li>
|
|
1456
|
+
* ))}
|
|
1457
|
+
* </ul>
|
|
1458
|
+
*
|
|
1459
|
+
* <button disabled={!userMemberships.hasNextPage} onClick={() => userMemberships.fetchNext()}>
|
|
1460
|
+
* Load more
|
|
1461
|
+
* </button>
|
|
1462
|
+
* </>
|
|
1463
|
+
* )
|
|
1464
|
+
* }
|
|
1465
|
+
*
|
|
1466
|
+
* export default JoinedOrganizations
|
|
1467
|
+
* ```
|
|
1468
|
+
*
|
|
1469
|
+
* @example
|
|
1470
|
+
* ### Simple pagination
|
|
1471
|
+
*
|
|
1472
|
+
* The following example demonstrates how to use the `fetchPrevious` and `fetchNext` helper functions to paginate through the data. The `userInvitations` attribute will be populated with the first page of invitations. When the "Previous page" or "Next page" button is clicked, the `fetchPrevious` or `fetchNext` helper function will be called to fetch the previous or next page of invitations.
|
|
1473
|
+
*
|
|
1474
|
+
* Notice the difference between this example's pagination and the infinite pagination example above.
|
|
1475
|
+
*
|
|
1476
|
+
* ```tsx {{ filename: 'src/components/UserInvitationsTable.tsx' }}
|
|
1477
|
+
* import { useOrganizationList } from '@clerk/react'
|
|
1478
|
+
* import React from 'react'
|
|
1479
|
+
*
|
|
1480
|
+
* const UserInvitationsTable = () => {
|
|
1481
|
+
* const { isLoaded, userInvitations } = useOrganizationList({
|
|
1482
|
+
* userInvitations: {
|
|
1483
|
+
* infinite: true,
|
|
1484
|
+
* keepPreviousData: true,
|
|
1485
|
+
* },
|
|
1486
|
+
* })
|
|
1487
|
+
*
|
|
1488
|
+
* if (!isLoaded || userInvitations.isLoading) {
|
|
1489
|
+
* return <>Loading</>
|
|
1490
|
+
* }
|
|
1491
|
+
*
|
|
1492
|
+
* return (
|
|
1493
|
+
* <>
|
|
1494
|
+
* <table>
|
|
1495
|
+
* <thead>
|
|
1496
|
+
* <tr>
|
|
1497
|
+
* <th>Email</th>
|
|
1498
|
+
* <th>Org name</th>
|
|
1499
|
+
* </tr>
|
|
1500
|
+
* </thead>
|
|
1501
|
+
*
|
|
1502
|
+
* <tbody>
|
|
1503
|
+
* {userInvitations.data?.map((inv) => (
|
|
1504
|
+
* <tr key={inv.id}>
|
|
1505
|
+
* <th>{inv.emailAddress}</th>
|
|
1506
|
+
* <th>{inv.publicOrganizationData.name}</th>
|
|
1507
|
+
* </tr>
|
|
1508
|
+
* ))}
|
|
1509
|
+
* </tbody>
|
|
1510
|
+
* </table>
|
|
1511
|
+
*
|
|
1512
|
+
* <button disabled={!userInvitations.hasPreviousPage} onClick={userInvitations.fetchPrevious}>
|
|
1513
|
+
* Prev
|
|
1514
|
+
* </button>
|
|
1515
|
+
* <button disabled={!userInvitations.hasNextPage} onClick={userInvitations.fetchNext}>
|
|
1516
|
+
* Next
|
|
1517
|
+
* </button>
|
|
1518
|
+
* </>
|
|
1519
|
+
* )
|
|
1520
|
+
* }
|
|
1521
|
+
*
|
|
1522
|
+
* export default UserInvitationsTable
|
|
1523
|
+
* ```
|
|
1524
|
+
*/
|
|
1525
|
+
function useOrganizationList(params) {
|
|
1526
|
+
const { userMemberships, userInvitations, userSuggestions } = params || {};
|
|
1527
|
+
useAssertWrappedByClerkProvider("useOrganizationList");
|
|
1528
|
+
useAttemptToEnableOrganizations("useOrganizationList");
|
|
1529
|
+
const userMembershipsSafeValues = useWithSafeValues(userMemberships, {
|
|
1530
|
+
initialPage: 1,
|
|
1531
|
+
pageSize: 10,
|
|
1532
|
+
keepPreviousData: false,
|
|
1533
|
+
infinite: false
|
|
1534
|
+
});
|
|
1535
|
+
const userInvitationsSafeValues = useWithSafeValues(userInvitations, {
|
|
1536
|
+
initialPage: 1,
|
|
1537
|
+
pageSize: 10,
|
|
1538
|
+
status: "pending",
|
|
1539
|
+
keepPreviousData: false,
|
|
1540
|
+
infinite: false
|
|
1541
|
+
});
|
|
1542
|
+
const userSuggestionsSafeValues = useWithSafeValues(userSuggestions, {
|
|
1543
|
+
initialPage: 1,
|
|
1544
|
+
pageSize: 10,
|
|
1545
|
+
status: "pending",
|
|
1546
|
+
keepPreviousData: false,
|
|
1547
|
+
infinite: false
|
|
1548
|
+
});
|
|
1549
|
+
const clerk = useClerkInstanceContext();
|
|
1550
|
+
const user = useUserBase();
|
|
1551
|
+
clerk.telemetry?.record(require_telemetry.eventMethodCalled("useOrganizationList"));
|
|
1552
|
+
const userMembershipsParams = typeof userMemberships === "undefined" ? void 0 : {
|
|
1553
|
+
initialPage: userMembershipsSafeValues.initialPage,
|
|
1554
|
+
pageSize: userMembershipsSafeValues.pageSize
|
|
1555
|
+
};
|
|
1556
|
+
const userInvitationsParams = typeof userInvitations === "undefined" ? void 0 : {
|
|
1557
|
+
initialPage: userInvitationsSafeValues.initialPage,
|
|
1558
|
+
pageSize: userInvitationsSafeValues.pageSize,
|
|
1559
|
+
status: userInvitationsSafeValues.status
|
|
1560
|
+
};
|
|
1561
|
+
const userSuggestionsParams = typeof userSuggestions === "undefined" ? void 0 : {
|
|
1562
|
+
initialPage: userSuggestionsSafeValues.initialPage,
|
|
1563
|
+
pageSize: userSuggestionsSafeValues.pageSize,
|
|
1564
|
+
status: userSuggestionsSafeValues.status
|
|
1565
|
+
};
|
|
1566
|
+
const isClerkLoaded = !!(clerk.loaded && user);
|
|
1567
|
+
const memberships = usePagesOrInfinite({
|
|
1568
|
+
fetcher: user?.getOrganizationMemberships,
|
|
1569
|
+
config: {
|
|
1570
|
+
keepPreviousData: userMembershipsSafeValues.keepPreviousData,
|
|
1571
|
+
infinite: userMembershipsSafeValues.infinite,
|
|
1572
|
+
enabled: !!userMembershipsParams,
|
|
1573
|
+
isSignedIn: user !== null,
|
|
1574
|
+
initialPage: userMembershipsSafeValues.initialPage,
|
|
1575
|
+
pageSize: userMembershipsSafeValues.pageSize
|
|
1576
|
+
},
|
|
1577
|
+
keys: createCacheKeys({
|
|
1578
|
+
stablePrefix: STABLE_KEYS.USER_MEMBERSHIPS_KEY,
|
|
1579
|
+
authenticated: true,
|
|
1580
|
+
tracked: { userId: user?.id },
|
|
1581
|
+
untracked: { args: userMembershipsParams }
|
|
1582
|
+
})
|
|
1583
|
+
});
|
|
1584
|
+
const invitations = usePagesOrInfinite({
|
|
1585
|
+
fetcher: user?.getOrganizationInvitations,
|
|
1586
|
+
config: {
|
|
1587
|
+
keepPreviousData: userInvitationsSafeValues.keepPreviousData,
|
|
1588
|
+
infinite: userInvitationsSafeValues.infinite,
|
|
1589
|
+
enabled: !!userInvitationsParams,
|
|
1590
|
+
isSignedIn: user !== null,
|
|
1591
|
+
initialPage: userInvitationsSafeValues.initialPage,
|
|
1592
|
+
pageSize: userInvitationsSafeValues.pageSize
|
|
1593
|
+
},
|
|
1594
|
+
keys: createCacheKeys({
|
|
1595
|
+
stablePrefix: STABLE_KEYS.USER_INVITATIONS_KEY,
|
|
1596
|
+
authenticated: true,
|
|
1597
|
+
tracked: { userId: user?.id },
|
|
1598
|
+
untracked: { args: userInvitationsParams }
|
|
1599
|
+
})
|
|
1600
|
+
});
|
|
1601
|
+
const suggestions = usePagesOrInfinite({
|
|
1602
|
+
fetcher: user?.getOrganizationSuggestions,
|
|
1603
|
+
config: {
|
|
1604
|
+
keepPreviousData: userSuggestionsSafeValues.keepPreviousData,
|
|
1605
|
+
infinite: userSuggestionsSafeValues.infinite,
|
|
1606
|
+
enabled: !!userSuggestionsParams,
|
|
1607
|
+
isSignedIn: user !== null,
|
|
1608
|
+
initialPage: userSuggestionsSafeValues.initialPage,
|
|
1609
|
+
pageSize: userSuggestionsSafeValues.pageSize
|
|
1610
|
+
},
|
|
1611
|
+
keys: createCacheKeys({
|
|
1612
|
+
stablePrefix: STABLE_KEYS.USER_SUGGESTIONS_KEY,
|
|
1613
|
+
authenticated: true,
|
|
1614
|
+
tracked: { userId: user?.id },
|
|
1615
|
+
untracked: { args: userSuggestionsParams }
|
|
1616
|
+
})
|
|
1617
|
+
});
|
|
1618
|
+
if (!isClerkLoaded) return {
|
|
1619
|
+
isLoaded: false,
|
|
1620
|
+
createOrganization: void 0,
|
|
1621
|
+
setActive: void 0,
|
|
1622
|
+
userMemberships: undefinedPaginatedResource,
|
|
1623
|
+
userInvitations: undefinedPaginatedResource,
|
|
1624
|
+
userSuggestions: undefinedPaginatedResource
|
|
1625
|
+
};
|
|
1626
|
+
return {
|
|
1627
|
+
isLoaded: isClerkLoaded,
|
|
1628
|
+
setActive: clerk.setActive,
|
|
1629
|
+
createOrganization: clerk.createOrganization,
|
|
1630
|
+
userMemberships: memberships,
|
|
1631
|
+
userInvitations: invitations,
|
|
1632
|
+
userSuggestions: suggestions
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
//#endregion
|
|
1637
|
+
//#region src/react/hooks/useSafeLayoutEffect.tsx
|
|
1638
|
+
/**
|
|
1639
|
+
* @internal
|
|
1640
|
+
*/
|
|
1641
|
+
const useSafeLayoutEffect = typeof window !== "undefined" ? react.default.useLayoutEffect : react.default.useEffect;
|
|
1642
|
+
|
|
1643
|
+
//#endregion
|
|
1644
|
+
//#region src/react/hooks/useSession.ts
|
|
1645
|
+
const hookName$2 = `useSession`;
|
|
1646
|
+
/**
|
|
1647
|
+
* The `useSession()` hook provides access to the current user's [`Session`](https://clerk.com/docs/reference/objects/session) object, as well as helpers for setting the active session.
|
|
1648
|
+
*
|
|
1649
|
+
* @unionReturnHeadings
|
|
1650
|
+
* ["Loading", "Signed out", "Signed in"]
|
|
1651
|
+
*
|
|
1652
|
+
* @function
|
|
1653
|
+
*
|
|
1654
|
+
* @param [options] - An object containing options for the `useSession()` hook.
|
|
1655
|
+
* @example
|
|
1656
|
+
* ### Access the `Session` object
|
|
1657
|
+
*
|
|
1658
|
+
* The following example uses the `useSession()` hook to access the `Session` object, which has the `lastActiveAt` property. The `lastActiveAt` property is a `Date` object used to show the time the session was last active.
|
|
1659
|
+
*
|
|
1660
|
+
* <Tabs items='React,Next.js'>
|
|
1661
|
+
* <Tab>
|
|
1662
|
+
*
|
|
1663
|
+
* ```tsx {{ filename: 'src/Home.tsx' }}
|
|
1664
|
+
* import { useSession } from '@clerk/react'
|
|
1665
|
+
*
|
|
1666
|
+
* export default function Home() {
|
|
1667
|
+
* const { isLoaded, session, isSignedIn } = useSession()
|
|
1668
|
+
*
|
|
1669
|
+
* if (!isLoaded) {
|
|
1670
|
+
* // Handle loading state
|
|
1671
|
+
* return null
|
|
1672
|
+
* }
|
|
1673
|
+
* if (!isSignedIn) {
|
|
1674
|
+
* // Handle signed out state
|
|
1675
|
+
* return null
|
|
1676
|
+
* }
|
|
1677
|
+
*
|
|
1678
|
+
* return (
|
|
1679
|
+
* <div>
|
|
1680
|
+
* <p>This session has been active since {session.lastActiveAt.toLocaleString()}</p>
|
|
1681
|
+
* </div>
|
|
1682
|
+
* )
|
|
1683
|
+
* }
|
|
1684
|
+
* ```
|
|
1685
|
+
*
|
|
1686
|
+
* </Tab>
|
|
1687
|
+
* <Tab>
|
|
1688
|
+
*
|
|
1689
|
+
* ```tsx {{ filename: 'app/page.tsx' }}
|
|
1690
|
+
* 'use client';
|
|
1691
|
+
*
|
|
1692
|
+
* import { useSession } from '@clerk/nextjs';
|
|
1693
|
+
*
|
|
1694
|
+
* export default function HomePage() {
|
|
1695
|
+
* const { isLoaded, session, isSignedIn } = useSession();
|
|
1696
|
+
*
|
|
1697
|
+
* if (!isLoaded) {
|
|
1698
|
+
* // Handle loading state
|
|
1699
|
+
* return null;
|
|
1700
|
+
* }
|
|
1701
|
+
* if (!isSignedIn) {
|
|
1702
|
+
* // Handle signed out state
|
|
1703
|
+
* return null;
|
|
1704
|
+
* }
|
|
1705
|
+
*
|
|
1706
|
+
* return (
|
|
1707
|
+
* <div>
|
|
1708
|
+
* <p>This session has been active since {session.lastActiveAt.toLocaleString()}</p>
|
|
1709
|
+
* </div>
|
|
1710
|
+
* );
|
|
1711
|
+
* }
|
|
1712
|
+
* ```
|
|
1713
|
+
*
|
|
1714
|
+
* </Tab>
|
|
1715
|
+
* </Tabs>
|
|
1716
|
+
*/
|
|
1717
|
+
const useSession = () => {
|
|
1718
|
+
useAssertWrappedByClerkProvider(hookName$2);
|
|
1719
|
+
const session = useSessionBase();
|
|
1720
|
+
const clerk = useClerkInstanceContext();
|
|
1721
|
+
clerk.telemetry?.record(require_telemetry.eventMethodCalled(hookName$2));
|
|
1722
|
+
if (session === void 0) return {
|
|
1723
|
+
isLoaded: false,
|
|
1724
|
+
isSignedIn: void 0,
|
|
1725
|
+
session: void 0
|
|
1726
|
+
};
|
|
1727
|
+
if (session === null) return {
|
|
1728
|
+
isLoaded: true,
|
|
1729
|
+
isSignedIn: false,
|
|
1730
|
+
session: null
|
|
1731
|
+
};
|
|
1732
|
+
return {
|
|
1733
|
+
isLoaded: true,
|
|
1734
|
+
isSignedIn: clerk.isSignedIn,
|
|
1735
|
+
session
|
|
1736
|
+
};
|
|
1737
|
+
};
|
|
1738
|
+
|
|
1739
|
+
//#endregion
|
|
1740
|
+
//#region src/react/hooks/base/useClientBase.ts
|
|
1741
|
+
const initialSnapshot = void 0;
|
|
1742
|
+
const getInitialSnapshot = () => initialSnapshot;
|
|
1743
|
+
function useClientBase() {
|
|
1744
|
+
const clerk = useClerkInstanceContext();
|
|
1745
|
+
return (0, react.useSyncExternalStore)((0, react.useCallback)((callback) => clerk.addListener(callback, { skipInitialEmit: true }), [clerk]), (0, react.useCallback)(() => {
|
|
1746
|
+
if (!clerk.loaded || !clerk.__internal_lastEmittedResources) return;
|
|
1747
|
+
return clerk.__internal_lastEmittedResources.client;
|
|
1748
|
+
}, [clerk]), getInitialSnapshot);
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
//#endregion
|
|
1752
|
+
//#region src/react/hooks/useSessionList.ts
|
|
1753
|
+
const hookName$1 = "useSessionList";
|
|
1754
|
+
/**
|
|
1755
|
+
* The `useSessionList()` hook returns an array of [`Session`](https://clerk.com/docs/reference/objects/session) objects that have been registered on the client device.
|
|
1756
|
+
*
|
|
1757
|
+
* @unionReturnHeadings
|
|
1758
|
+
* ["Initialization", "Loaded"]
|
|
1759
|
+
*
|
|
1760
|
+
* @function
|
|
1761
|
+
*
|
|
1762
|
+
* @example
|
|
1763
|
+
* ### Get a list of sessions
|
|
1764
|
+
*
|
|
1765
|
+
* The following example uses `useSessionList()` to get a list of sessions that have been registered on the client device. The `sessions` property is used to show the number of times the user has visited the page.
|
|
1766
|
+
*
|
|
1767
|
+
* <Tabs items='React,Next.js'>
|
|
1768
|
+
* <Tab>
|
|
1769
|
+
*
|
|
1770
|
+
* ```tsx {{ filename: 'src/Home.tsx' }}
|
|
1771
|
+
* import { useSessionList } from '@clerk/react'
|
|
1772
|
+
*
|
|
1773
|
+
* export default function Home() {
|
|
1774
|
+
* const { isLoaded, sessions } = useSessionList()
|
|
1775
|
+
*
|
|
1776
|
+
* if (!isLoaded) {
|
|
1777
|
+
* // Handle loading state
|
|
1778
|
+
* return null
|
|
1779
|
+
* }
|
|
1780
|
+
*
|
|
1781
|
+
* return (
|
|
1782
|
+
* <div>
|
|
1783
|
+
* <p>Welcome back. You've been here {sessions.length} times before.</p>
|
|
1784
|
+
* </div>
|
|
1785
|
+
* )
|
|
1786
|
+
* }
|
|
1787
|
+
* ```
|
|
1788
|
+
*
|
|
1789
|
+
* </Tab>
|
|
1790
|
+
* <Tab>
|
|
1791
|
+
*
|
|
1792
|
+
* ```tsx {{ filename: 'app/page.tsx' }}
|
|
1793
|
+
* 'use client';
|
|
1794
|
+
*
|
|
1795
|
+
* import { useSessionList } from '@clerk/nextjs';
|
|
1796
|
+
*
|
|
1797
|
+
* export default function HomePage() {
|
|
1798
|
+
* const { isLoaded, sessions } = useSessionList();
|
|
1799
|
+
*
|
|
1800
|
+
* if (!isLoaded) {
|
|
1801
|
+
* // Handle loading state
|
|
1802
|
+
* return null;
|
|
1803
|
+
* }
|
|
1804
|
+
*
|
|
1805
|
+
* return (
|
|
1806
|
+
* <div>
|
|
1807
|
+
* <p>Welcome back. You've been here {sessions.length} times before.</p>
|
|
1808
|
+
* </div>
|
|
1809
|
+
* );
|
|
1810
|
+
* }
|
|
1811
|
+
* ```
|
|
1812
|
+
*
|
|
1813
|
+
* </Tab>
|
|
1814
|
+
* </Tabs>
|
|
1815
|
+
*/
|
|
1816
|
+
const useSessionList = () => {
|
|
1817
|
+
useAssertWrappedByClerkProvider(hookName$1);
|
|
1818
|
+
const isomorphicClerk = useClerkInstanceContext();
|
|
1819
|
+
const client = useClientBase();
|
|
1820
|
+
useClerkInstanceContext().telemetry?.record(require_telemetry.eventMethodCalled(hookName$1));
|
|
1821
|
+
if (!client) return {
|
|
1822
|
+
isLoaded: false,
|
|
1823
|
+
sessions: void 0,
|
|
1824
|
+
setActive: void 0
|
|
1825
|
+
};
|
|
1826
|
+
return {
|
|
1827
|
+
isLoaded: true,
|
|
1828
|
+
sessions: client.sessions,
|
|
1829
|
+
setActive: isomorphicClerk.setActive
|
|
1830
|
+
};
|
|
1831
|
+
};
|
|
1832
|
+
|
|
1833
|
+
//#endregion
|
|
1834
|
+
//#region src/react/hooks/useUser.ts
|
|
1835
|
+
const hookName = "useUser";
|
|
1836
|
+
/**
|
|
1837
|
+
* The `useUser()` hook provides access to the current user's [`User`](https://clerk.com/docs/reference/objects/user) object, which contains all the data for a single user in your application and provides methods to manage their account. This hook also allows you to check if the user is signed in and if Clerk has loaded.
|
|
1838
|
+
*
|
|
1839
|
+
* @unionReturnHeadings
|
|
1840
|
+
* ["Loading", "Signed out", "Signed in"]
|
|
1841
|
+
*
|
|
1842
|
+
* @example
|
|
1843
|
+
* ### Get the current user
|
|
1844
|
+
*
|
|
1845
|
+
* The following example uses the `useUser()` hook to access the [`User`](https://clerk.com/docs/reference/objects/user) object, which contains the current user's data such as their full name. The `isLoaded` and `isSignedIn` properties are used to handle the loading state and to check if the user is signed in, respectively.
|
|
1846
|
+
*
|
|
1847
|
+
* ```tsx {{ filename: 'src/Example.tsx' }}
|
|
1848
|
+
* import { useUser } from '@clerk/react'
|
|
1849
|
+
*
|
|
1850
|
+
* export default function Example() {
|
|
1851
|
+
* const { isSignedIn, user, isLoaded } = useUser()
|
|
1852
|
+
*
|
|
1853
|
+
* if (!isLoaded) {
|
|
1854
|
+
* return <div>Loading...</div>
|
|
1855
|
+
* }
|
|
1856
|
+
*
|
|
1857
|
+
* if (!isSignedIn) {
|
|
1858
|
+
* return <div>Sign in to view this page</div>
|
|
1859
|
+
* }
|
|
1860
|
+
*
|
|
1861
|
+
* return <div>Hello {user.firstName}!</div>
|
|
1862
|
+
* }
|
|
1863
|
+
* ```
|
|
1864
|
+
*
|
|
1865
|
+
* @example
|
|
1866
|
+
* ### Update user data
|
|
1867
|
+
*
|
|
1868
|
+
* The following example uses the `useUser()` hook to access the [`User`](https://clerk.com/docs/reference/objects/user) object, which calls the [`update()`](https://clerk.com/docs/reference/objects/user#update) method to update the current user's information.
|
|
1869
|
+
*
|
|
1870
|
+
* <Tabs items='React,Next.js'>
|
|
1871
|
+
* <Tab>
|
|
1872
|
+
*
|
|
1873
|
+
* ```tsx {{ filename: 'src/Home.tsx' }}
|
|
1874
|
+
* import { useUser } from '@clerk/react'
|
|
1875
|
+
*
|
|
1876
|
+
* export default function Home() {
|
|
1877
|
+
* const { isSignedIn, isLoaded, user } = useUser()
|
|
1878
|
+
*
|
|
1879
|
+
* if (!isLoaded) {
|
|
1880
|
+
* // Handle loading state
|
|
1881
|
+
* return null
|
|
1882
|
+
* }
|
|
1883
|
+
*
|
|
1884
|
+
* if (!isSignedIn) return null
|
|
1885
|
+
*
|
|
1886
|
+
* const updateUser = async () => {
|
|
1887
|
+
* await user.update({
|
|
1888
|
+
* firstName: 'John',
|
|
1889
|
+
* lastName: 'Doe',
|
|
1890
|
+
* })
|
|
1891
|
+
* }
|
|
1892
|
+
*
|
|
1893
|
+
* return (
|
|
1894
|
+
* <>
|
|
1895
|
+
* <button onClick={updateUser}>Update your name</button>
|
|
1896
|
+
* <p>user.firstName: {user.firstName}</p>
|
|
1897
|
+
* <p>user.lastName: {user.lastName}</p>
|
|
1898
|
+
* </>
|
|
1899
|
+
* )
|
|
1900
|
+
* }
|
|
1901
|
+
* ```
|
|
1902
|
+
* </Tab>
|
|
1903
|
+
* <Tab>
|
|
1904
|
+
*
|
|
1905
|
+
* ```tsx {{ filename: 'app/page.tsx' }}
|
|
1906
|
+
* 'use client';
|
|
1907
|
+
*
|
|
1908
|
+
* import { useUser } from '@clerk/nextjs';
|
|
1909
|
+
*
|
|
1910
|
+
* export default function HomePage() {
|
|
1911
|
+
* const { isSignedIn, isLoaded, user } = useUser();
|
|
1912
|
+
*
|
|
1913
|
+
* if (!isLoaded) {
|
|
1914
|
+
* // Handle loading state
|
|
1915
|
+
* return null;
|
|
1916
|
+
* }
|
|
1917
|
+
*
|
|
1918
|
+
* if (!isSignedIn) return null;
|
|
1919
|
+
*
|
|
1920
|
+
* const updateUser = async () => {
|
|
1921
|
+
* await user.update({
|
|
1922
|
+
* firstName: 'John',
|
|
1923
|
+
* lastName: 'Doe',
|
|
1924
|
+
* });
|
|
1925
|
+
* };
|
|
1926
|
+
*
|
|
1927
|
+
* return (
|
|
1928
|
+
* <>
|
|
1929
|
+
* <button onClick={updateUser}>Update your name</button>
|
|
1930
|
+
* <p>user.firstName: {user.firstName}</p>
|
|
1931
|
+
* <p>user.lastName: {user.lastName}</p>
|
|
1932
|
+
* </>
|
|
1933
|
+
* );
|
|
1934
|
+
* }
|
|
1935
|
+
* ```
|
|
1936
|
+
*
|
|
1937
|
+
* </Tab>
|
|
1938
|
+
* </Tabs>
|
|
1939
|
+
*
|
|
1940
|
+
* @example
|
|
1941
|
+
* ### Reload user data
|
|
1942
|
+
*
|
|
1943
|
+
* The following example uses the `useUser()` hook to access the [`User`](https://clerk.com/docs/reference/objects/user) object, which calls the [`reload()`](https://clerk.com/docs/reference/objects/user#reload) method to get the latest user's information.
|
|
1944
|
+
*
|
|
1945
|
+
* <Tabs items='React,Next.js'>
|
|
1946
|
+
* <Tab>
|
|
1947
|
+
*
|
|
1948
|
+
* ```tsx {{ filename: 'src/Home.tsx' }}
|
|
1949
|
+
* import { useUser } from '@clerk/react'
|
|
1950
|
+
*
|
|
1951
|
+
* export default function Home() {
|
|
1952
|
+
* const { isSignedIn, isLoaded, user } = useUser();
|
|
1953
|
+
*
|
|
1954
|
+
* if (!isLoaded) {
|
|
1955
|
+
* // Handle loading state
|
|
1956
|
+
* return null;
|
|
1957
|
+
* }
|
|
1958
|
+
*
|
|
1959
|
+
* if (!isSignedIn) return null;
|
|
1960
|
+
*
|
|
1961
|
+
* const updateUser = async () => {
|
|
1962
|
+
* // Update data via an API endpoint
|
|
1963
|
+
* const updateMetadata = await fetch('/api/updateMetadata', {
|
|
1964
|
+
* method: 'POST',
|
|
1965
|
+
* body: JSON.stringify({
|
|
1966
|
+
* role: 'admin'
|
|
1967
|
+
* })
|
|
1968
|
+
* });
|
|
1969
|
+
*
|
|
1970
|
+
* // Check if the update was successful
|
|
1971
|
+
* if ((await updateMetadata.json()).message !== 'success') {
|
|
1972
|
+
* throw new Error('Error updating');
|
|
1973
|
+
* }
|
|
1974
|
+
*
|
|
1975
|
+
* // If the update was successful, reload the user data
|
|
1976
|
+
* await user.reload();
|
|
1977
|
+
* };
|
|
1978
|
+
*
|
|
1979
|
+
* return (
|
|
1980
|
+
* <>
|
|
1981
|
+
* <button onClick={updateUser}>Update your metadata</button>
|
|
1982
|
+
* <p>user role: {user.publicMetadata.role}</p>
|
|
1983
|
+
* </>
|
|
1984
|
+
* );
|
|
1985
|
+
* }
|
|
1986
|
+
* ```
|
|
1987
|
+
*
|
|
1988
|
+
* </Tab>
|
|
1989
|
+
* <Tab>
|
|
1990
|
+
*
|
|
1991
|
+
* ```tsx {{ filename: 'app/page.tsx' }}
|
|
1992
|
+
* 'use client';
|
|
1993
|
+
*
|
|
1994
|
+
* import { useUser } from '@clerk/nextjs';
|
|
1995
|
+
*
|
|
1996
|
+
* export default function HomePage() {
|
|
1997
|
+
* const { isSignedIn, isLoaded, user } = useUser();
|
|
1998
|
+
*
|
|
1999
|
+
* if (!isLoaded) {
|
|
2000
|
+
* // Handle loading state
|
|
2001
|
+
* return null;
|
|
2002
|
+
* }
|
|
2003
|
+
*
|
|
2004
|
+
* if (!isSignedIn) return null;
|
|
2005
|
+
*
|
|
2006
|
+
* const updateUser = async () => {
|
|
2007
|
+
* // Update data via an API endpoint
|
|
2008
|
+
* const updateMetadata = await fetch('/api/updateMetadata', {
|
|
2009
|
+
* method: 'POST',
|
|
2010
|
+
* body: JSON.stringify({
|
|
2011
|
+
* role: 'admin',
|
|
2012
|
+
* }),
|
|
2013
|
+
* });
|
|
2014
|
+
*
|
|
2015
|
+
* // Check if the update was successful
|
|
2016
|
+
* if ((await updateMetadata.json()).message !== 'success') {
|
|
2017
|
+
* throw new Error('Error updating');
|
|
2018
|
+
* }
|
|
2019
|
+
*
|
|
2020
|
+
* // If the update was successful, reload the user data
|
|
2021
|
+
* await user.reload();
|
|
2022
|
+
* };
|
|
2023
|
+
*
|
|
2024
|
+
* return (
|
|
2025
|
+
* <>
|
|
2026
|
+
* <button onClick={updateUser}>Update your metadata</button>
|
|
2027
|
+
* <p>user role: {user.publicMetadata.role}</p>
|
|
2028
|
+
* </>
|
|
2029
|
+
* );
|
|
2030
|
+
* }
|
|
2031
|
+
* ```
|
|
2032
|
+
*
|
|
2033
|
+
* </Tab>
|
|
2034
|
+
* </Tabs>
|
|
2035
|
+
*/
|
|
2036
|
+
function useUser() {
|
|
2037
|
+
useAssertWrappedByClerkProvider(hookName);
|
|
2038
|
+
const user = useUserBase();
|
|
2039
|
+
useClerkInstanceContext().telemetry?.record(require_telemetry.eventMethodCalled(hookName));
|
|
2040
|
+
if (user === void 0) return {
|
|
2041
|
+
isLoaded: false,
|
|
2042
|
+
isSignedIn: void 0,
|
|
2043
|
+
user: void 0
|
|
2044
|
+
};
|
|
2045
|
+
if (user === null) return {
|
|
2046
|
+
isLoaded: true,
|
|
2047
|
+
isSignedIn: false,
|
|
2048
|
+
user: null
|
|
2049
|
+
};
|
|
2050
|
+
return {
|
|
2051
|
+
isLoaded: true,
|
|
2052
|
+
isSignedIn: true,
|
|
2053
|
+
user
|
|
2054
|
+
};
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
//#endregion
|
|
2058
|
+
//#region src/react/hooks/useDeepEqualMemo.ts
|
|
2059
|
+
const useDeepEqualMemoize = (value) => {
|
|
2060
|
+
const ref = react.default.useRef(value);
|
|
2061
|
+
if (!(0, dequal.dequal)(value, ref.current)) ref.current = value;
|
|
2062
|
+
return react.default.useMemo(() => ref.current, [ref.current]);
|
|
2063
|
+
};
|
|
2064
|
+
/**
|
|
2065
|
+
* @internal
|
|
2066
|
+
*/
|
|
2067
|
+
const useDeepEqualMemo = (factory, dependencyArray) => {
|
|
2068
|
+
return react.default.useMemo(factory, useDeepEqualMemoize(dependencyArray));
|
|
2069
|
+
};
|
|
2070
|
+
/**
|
|
2071
|
+
* @internal
|
|
2072
|
+
*/
|
|
2073
|
+
const isDeeplyEqual = dequal.dequal;
|
|
2074
|
+
|
|
2075
|
+
//#endregion
|
|
2076
|
+
//#region src/react/hooks/useReverification.ts
|
|
2077
|
+
const CLERK_API_REVERIFICATION_ERROR_CODE = "session_reverification_required";
|
|
2078
|
+
/**
|
|
2079
|
+
*
|
|
2080
|
+
*/
|
|
2081
|
+
async function resolveResult(result) {
|
|
2082
|
+
try {
|
|
2083
|
+
const r = await result;
|
|
2084
|
+
if (r instanceof Response) return r.json();
|
|
2085
|
+
return r;
|
|
2086
|
+
} catch (e) {
|
|
2087
|
+
if (require_error.isClerkAPIResponseError(e) && e.errors.find(({ code }) => code === CLERK_API_REVERIFICATION_ERROR_CODE)) return require_authorization_errors.reverificationError();
|
|
2088
|
+
throw e;
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
/**
|
|
2092
|
+
*
|
|
2093
|
+
*/
|
|
2094
|
+
function createReverificationHandler(params) {
|
|
2095
|
+
/**
|
|
2096
|
+
*
|
|
2097
|
+
*/
|
|
2098
|
+
function assertReverification(fetcher) {
|
|
2099
|
+
return (async (...args) => {
|
|
2100
|
+
let result = await resolveResult(fetcher(...args));
|
|
2101
|
+
if (require_authorization_errors.isReverificationHint(result)) {
|
|
2102
|
+
/**
|
|
2103
|
+
* Create a promise
|
|
2104
|
+
*/
|
|
2105
|
+
const resolvers = require_createDeferredPromise.createDeferredPromise();
|
|
2106
|
+
const isValidMetadata = require_authorization.validateReverificationConfig(result.clerk_error.metadata?.reverification);
|
|
2107
|
+
const level = isValidMetadata ? isValidMetadata().level : void 0;
|
|
2108
|
+
const cancel = () => {
|
|
2109
|
+
resolvers.reject(new require_clerkRuntimeError.ClerkRuntimeError("User cancelled attempted verification", { code: "reverification_cancelled" }));
|
|
2110
|
+
};
|
|
2111
|
+
const complete = () => {
|
|
2112
|
+
resolvers.resolve(true);
|
|
2113
|
+
};
|
|
2114
|
+
if (params.onNeedsReverification === void 0)
|
|
2115
|
+
/**
|
|
2116
|
+
* On success resolve the pending promise
|
|
2117
|
+
* On cancel reject the pending promise
|
|
2118
|
+
*/
|
|
2119
|
+
params.openUIComponent?.({
|
|
2120
|
+
level,
|
|
2121
|
+
afterVerification: complete,
|
|
2122
|
+
afterVerificationCancelled: cancel
|
|
2123
|
+
});
|
|
2124
|
+
else params.onNeedsReverification({
|
|
2125
|
+
cancel,
|
|
2126
|
+
complete,
|
|
2127
|
+
level
|
|
2128
|
+
});
|
|
2129
|
+
/**
|
|
2130
|
+
* Wait until the promise from above have been resolved or rejected
|
|
2131
|
+
*/
|
|
2132
|
+
await resolvers.promise;
|
|
2133
|
+
/**
|
|
2134
|
+
* After the promise resolved successfully try the original request one more time
|
|
2135
|
+
*/
|
|
2136
|
+
result = await resolveResult(fetcher(...args));
|
|
2137
|
+
}
|
|
2138
|
+
return result;
|
|
2139
|
+
});
|
|
2140
|
+
}
|
|
2141
|
+
return assertReverification;
|
|
2142
|
+
}
|
|
2143
|
+
/**
|
|
2144
|
+
* > [!WARNING]
|
|
2145
|
+
* >
|
|
2146
|
+
* > Depending on the SDK you're using, this feature requires `@clerk/nextjs@6.12.7` or later, `@clerk/react@5.25.1` or later, and `@clerk/clerk-js@5.57.1` or later.
|
|
2147
|
+
*
|
|
2148
|
+
* The `useReverification()` hook is used to handle a session's reverification flow. If a request requires reverification, a modal will display, prompting the user to verify their credentials. Upon successful verification, the original request will automatically retry.
|
|
2149
|
+
*
|
|
2150
|
+
* @function
|
|
2151
|
+
*
|
|
2152
|
+
* @returns The `useReverification()` hook returns an array with the "enhanced" fetcher.
|
|
2153
|
+
*
|
|
2154
|
+
* @example
|
|
2155
|
+
* ### Handle cancellation of the reverification process
|
|
2156
|
+
*
|
|
2157
|
+
* The following example demonstrates how to handle scenarios where a user cancels the reverification flow, such as closing the modal, which might result in `myData` being `null`.
|
|
2158
|
+
*
|
|
2159
|
+
* In the following example, `myFetcher` would be a function in your backend that fetches data from the route that requires reverification. See the [guide on how to require reverification](https://clerk.com/docs/guides/secure/reverification) for more information.
|
|
2160
|
+
*
|
|
2161
|
+
* ```tsx {{ filename: 'src/components/MyButton.tsx' }}
|
|
2162
|
+
* import { useReverification } from '@clerk/react'
|
|
2163
|
+
* import { isReverificationCancelledError } from '@clerk/react/error'
|
|
2164
|
+
*
|
|
2165
|
+
* type MyData = {
|
|
2166
|
+
* balance: number
|
|
2167
|
+
* }
|
|
2168
|
+
*
|
|
2169
|
+
* export function MyButton() {
|
|
2170
|
+
* const fetchMyData = () => fetch('/api/balance').then(res=> res.json() as Promise<MyData>)
|
|
2171
|
+
* const enhancedFetcher = useReverification(fetchMyData);
|
|
2172
|
+
*
|
|
2173
|
+
* const handleClick = async () => {
|
|
2174
|
+
* try {
|
|
2175
|
+
* const myData = await enhancedFetcher()
|
|
2176
|
+
* // ^ is types as `MyData`
|
|
2177
|
+
* } catch (e) {
|
|
2178
|
+
* // Handle error returned from the fetcher here
|
|
2179
|
+
*
|
|
2180
|
+
* // You can also handle cancellation with the following
|
|
2181
|
+
* if (isReverificationCancelledError(err)) {
|
|
2182
|
+
* // Handle the cancellation error here
|
|
2183
|
+
* }
|
|
2184
|
+
* }
|
|
2185
|
+
* }
|
|
2186
|
+
*
|
|
2187
|
+
* return <button onClick={handleClick}>Update User</button>
|
|
2188
|
+
* }
|
|
2189
|
+
* ```
|
|
2190
|
+
*/
|
|
2191
|
+
const useReverification = (fetcher, options) => {
|
|
2192
|
+
const { __internal_openReverification, telemetry } = useClerk();
|
|
2193
|
+
const fetcherRef = (0, react.useRef)(fetcher);
|
|
2194
|
+
const optionsRef = (0, react.useRef)(options);
|
|
2195
|
+
telemetry?.record(require_telemetry.eventMethodCalled("useReverification", { onNeedsReverification: Boolean(options?.onNeedsReverification) }));
|
|
2196
|
+
useSafeLayoutEffect(() => {
|
|
2197
|
+
fetcherRef.current = fetcher;
|
|
2198
|
+
optionsRef.current = options;
|
|
2199
|
+
});
|
|
2200
|
+
return (0, react.useCallback)((...args) => {
|
|
2201
|
+
return createReverificationHandler({
|
|
2202
|
+
openUIComponent: __internal_openReverification,
|
|
2203
|
+
telemetry,
|
|
2204
|
+
...optionsRef.current
|
|
2205
|
+
})(fetcherRef.current)(...args);
|
|
2206
|
+
}, [__internal_openReverification, telemetry]);
|
|
2207
|
+
};
|
|
2208
|
+
|
|
2209
|
+
//#endregion
|
|
2210
|
+
//#region src/react/hooks/useBillingIsEnabled.ts
|
|
2211
|
+
/**
|
|
2212
|
+
* @internal
|
|
2213
|
+
*/
|
|
2214
|
+
function useBillingIsEnabled(params) {
|
|
2215
|
+
const clerk = useClerkInstanceContext();
|
|
2216
|
+
const enabledFromParam = params?.enabled ?? true;
|
|
2217
|
+
const environment = clerk.__internal_environment;
|
|
2218
|
+
const user = useUserBase();
|
|
2219
|
+
const organization = useOrganizationBase();
|
|
2220
|
+
const userBillingEnabled = environment?.commerceSettings.billing.user.enabled;
|
|
2221
|
+
const orgBillingEnabled = environment?.commerceSettings.billing.organization.enabled;
|
|
2222
|
+
const billingEnabled = params?.for === "organization" ? orgBillingEnabled : params?.for === "user" ? userBillingEnabled : userBillingEnabled || orgBillingEnabled;
|
|
2223
|
+
const isOrganization = params?.for === "organization";
|
|
2224
|
+
const requireUserAndOrganizationWhenAuthenticated = params?.authenticated ?? true ? (isOrganization ? Boolean(organization?.id) : true) && Boolean(user?.id) : true;
|
|
2225
|
+
return billingEnabled && enabledFromParam && clerk.loaded && requireUserAndOrganizationWhenAuthenticated;
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
//#endregion
|
|
2229
|
+
//#region src/react/hooks/createBillingPaginatedHook.tsx
|
|
2230
|
+
/**
|
|
2231
|
+
* A hook factory that creates paginated data fetching hooks for commerce-related resources.
|
|
2232
|
+
* It provides a standardized way to create hooks that can fetch either user or Organization resources
|
|
2233
|
+
* with built-in pagination support.
|
|
2234
|
+
*
|
|
2235
|
+
* The generated hooks handle:
|
|
2236
|
+
* - Clerk authentication context
|
|
2237
|
+
* - Resource-specific data fetching
|
|
2238
|
+
* - Pagination (both traditional and infinite scroll)
|
|
2239
|
+
* - Telemetry tracking
|
|
2240
|
+
* - Type safety for the specific resource.
|
|
2241
|
+
*
|
|
2242
|
+
* @internal
|
|
2243
|
+
*/
|
|
2244
|
+
function createBillingPaginatedHook({ hookName, resourceType, useFetcher, options }) {
|
|
2245
|
+
return function useBillingHook(params) {
|
|
2246
|
+
const { for: _for, enabled: externalEnabled, ...paginationParams } = params || {};
|
|
2247
|
+
const safeFor = _for || "user";
|
|
2248
|
+
useAssertWrappedByClerkProvider(hookName);
|
|
2249
|
+
const fetchFn = useFetcher(safeFor);
|
|
2250
|
+
const safeValues = useWithSafeValues(paginationParams, {
|
|
2251
|
+
initialPage: 1,
|
|
2252
|
+
pageSize: 10,
|
|
2253
|
+
keepPreviousData: false,
|
|
2254
|
+
infinite: false,
|
|
2255
|
+
__experimental_mode: void 0
|
|
2256
|
+
});
|
|
2257
|
+
const clerk = useClerkInstanceContext();
|
|
2258
|
+
const user = useUserBase();
|
|
2259
|
+
const organization = useOrganizationBase();
|
|
2260
|
+
clerk.telemetry?.record(require_telemetry.eventMethodCalled(hookName));
|
|
2261
|
+
const isForOrganization = safeFor === "organization";
|
|
2262
|
+
const billingEnabled = useBillingIsEnabled({
|
|
2263
|
+
for: safeFor,
|
|
2264
|
+
enabled: externalEnabled,
|
|
2265
|
+
authenticated: !options?.unauthenticated
|
|
2266
|
+
});
|
|
2267
|
+
const hookParams = typeof paginationParams === "undefined" ? void 0 : {
|
|
2268
|
+
initialPage: safeValues.initialPage,
|
|
2269
|
+
pageSize: safeValues.pageSize,
|
|
2270
|
+
...options?.unauthenticated ? {} : isForOrganization ? { orgId: organization?.id } : {}
|
|
2271
|
+
};
|
|
2272
|
+
const isEnabled = !!hookParams && clerk.loaded && !!billingEnabled;
|
|
2273
|
+
return usePagesOrInfinite({
|
|
2274
|
+
fetcher: fetchFn,
|
|
2275
|
+
config: {
|
|
2276
|
+
keepPreviousData: safeValues.keepPreviousData,
|
|
2277
|
+
infinite: safeValues.infinite,
|
|
2278
|
+
enabled: isEnabled,
|
|
2279
|
+
...options?.unauthenticated ? {} : { isSignedIn: user !== null },
|
|
2280
|
+
__experimental_mode: safeValues.__experimental_mode,
|
|
2281
|
+
initialPage: safeValues.initialPage,
|
|
2282
|
+
pageSize: safeValues.pageSize
|
|
2283
|
+
},
|
|
2284
|
+
keys: createCacheKeys({
|
|
2285
|
+
stablePrefix: resourceType,
|
|
2286
|
+
authenticated: !options?.unauthenticated,
|
|
2287
|
+
tracked: options?.unauthenticated ? { for: safeFor } : {
|
|
2288
|
+
userId: user?.id,
|
|
2289
|
+
...isForOrganization ? { orgId: organization?.id } : {}
|
|
2290
|
+
},
|
|
2291
|
+
untracked: { args: hookParams }
|
|
2292
|
+
})
|
|
2293
|
+
});
|
|
2294
|
+
};
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
//#endregion
|
|
2298
|
+
//#region src/react/hooks/useStatements.tsx
|
|
2299
|
+
/**
|
|
2300
|
+
* @internal
|
|
2301
|
+
*/
|
|
2302
|
+
const useStatements = createBillingPaginatedHook({
|
|
2303
|
+
hookName: "useStatements",
|
|
2304
|
+
resourceType: STABLE_KEYS.STATEMENTS_KEY,
|
|
2305
|
+
useFetcher: () => {
|
|
2306
|
+
const clerk = useClerkInstanceContext();
|
|
2307
|
+
if (clerk.loaded) return clerk.billing.getStatements;
|
|
2308
|
+
}
|
|
2309
|
+
});
|
|
2310
|
+
|
|
2311
|
+
//#endregion
|
|
2312
|
+
//#region src/react/hooks/usePaymentAttempts.tsx
|
|
2313
|
+
/**
|
|
2314
|
+
* @internal
|
|
2315
|
+
*/
|
|
2316
|
+
const usePaymentAttempts = createBillingPaginatedHook({
|
|
2317
|
+
hookName: "usePaymentAttempts",
|
|
2318
|
+
resourceType: STABLE_KEYS.PAYMENT_ATTEMPTS_KEY,
|
|
2319
|
+
useFetcher: () => {
|
|
2320
|
+
const clerk = useClerkInstanceContext();
|
|
2321
|
+
if (clerk.loaded) return clerk.billing.getPaymentAttempts;
|
|
2322
|
+
}
|
|
2323
|
+
});
|
|
2324
|
+
|
|
2325
|
+
//#endregion
|
|
2326
|
+
//#region src/react/hooks/usePaymentMethods.tsx
|
|
2327
|
+
/**
|
|
2328
|
+
* @internal
|
|
2329
|
+
*/
|
|
2330
|
+
const usePaymentMethods = createBillingPaginatedHook({
|
|
2331
|
+
hookName: "usePaymentMethods",
|
|
2332
|
+
resourceType: STABLE_KEYS.PAYMENT_METHODS_KEY,
|
|
2333
|
+
useFetcher: (resource) => {
|
|
2334
|
+
const organization = useOrganizationBase();
|
|
2335
|
+
const user = useUserBase();
|
|
2336
|
+
if (resource === "organization") return organization?.getPaymentMethods;
|
|
2337
|
+
return user?.getPaymentMethods;
|
|
2338
|
+
}
|
|
2339
|
+
});
|
|
2340
|
+
|
|
2341
|
+
//#endregion
|
|
2342
|
+
//#region src/react/hooks/usePlans.tsx
|
|
2343
|
+
/**
|
|
2344
|
+
* @internal
|
|
2345
|
+
*/
|
|
2346
|
+
const usePlans = createBillingPaginatedHook({
|
|
2347
|
+
hookName: "usePlans",
|
|
2348
|
+
resourceType: STABLE_KEYS.PLANS_KEY,
|
|
2349
|
+
useFetcher: (_for) => {
|
|
2350
|
+
const clerk = useClerkInstanceContext();
|
|
2351
|
+
if (!clerk.loaded) return;
|
|
2352
|
+
return (params) => clerk.billing.getPlans({
|
|
2353
|
+
...params,
|
|
2354
|
+
for: _for
|
|
2355
|
+
});
|
|
2356
|
+
},
|
|
2357
|
+
options: { unauthenticated: true }
|
|
2358
|
+
});
|
|
2359
|
+
|
|
2360
|
+
//#endregion
|
|
2361
|
+
//#region src/react/hooks/useSubscription.shared.ts
|
|
2362
|
+
function useSubscriptionCacheKeys(params) {
|
|
2363
|
+
const { userId, orgId, for: forType } = params;
|
|
2364
|
+
return (0, react.useMemo)(() => {
|
|
2365
|
+
const safeOrgId = forType === "organization" ? orgId : void 0;
|
|
2366
|
+
return createCacheKeys({
|
|
2367
|
+
stablePrefix: STABLE_KEYS.SUBSCRIPTION_KEY,
|
|
2368
|
+
authenticated: true,
|
|
2369
|
+
tracked: {
|
|
2370
|
+
userId,
|
|
2371
|
+
orgId: safeOrgId
|
|
2372
|
+
},
|
|
2373
|
+
untracked: { args: { orgId: safeOrgId } }
|
|
2374
|
+
});
|
|
2375
|
+
}, [
|
|
2376
|
+
userId,
|
|
2377
|
+
orgId,
|
|
2378
|
+
forType
|
|
2379
|
+
]);
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
//#endregion
|
|
2383
|
+
//#region src/react/hooks/useSubscription.tsx
|
|
2384
|
+
const HOOK_NAME$2 = "useSubscription";
|
|
2385
|
+
/**
|
|
2386
|
+
* @internal
|
|
2387
|
+
*/
|
|
2388
|
+
function useSubscription(params) {
|
|
2389
|
+
useAssertWrappedByClerkProvider(HOOK_NAME$2);
|
|
2390
|
+
const clerk = useClerkInstanceContext();
|
|
2391
|
+
const user = useUserBase();
|
|
2392
|
+
const organization = useOrganizationBase();
|
|
2393
|
+
const billingEnabled = useBillingIsEnabled(params);
|
|
2394
|
+
const recordedRef = (0, react.useRef)(false);
|
|
2395
|
+
(0, react.useEffect)(() => {
|
|
2396
|
+
if (!recordedRef.current && clerk?.telemetry) {
|
|
2397
|
+
clerk.telemetry.record(require_telemetry.eventMethodCalled(HOOK_NAME$2));
|
|
2398
|
+
recordedRef.current = true;
|
|
2399
|
+
}
|
|
2400
|
+
}, [clerk]);
|
|
2401
|
+
const keepPreviousData = params?.keepPreviousData ?? false;
|
|
2402
|
+
const [queryClient] = useClerkQueryClient();
|
|
2403
|
+
const { queryKey, invalidationKey, stableKey, authenticated } = useSubscriptionCacheKeys({
|
|
2404
|
+
userId: user?.id,
|
|
2405
|
+
orgId: organization?.id,
|
|
2406
|
+
for: params?.for
|
|
2407
|
+
});
|
|
2408
|
+
const queriesEnabled = Boolean(user?.id && billingEnabled);
|
|
2409
|
+
useClearQueriesOnSignOut({
|
|
2410
|
+
isSignedOut: user === null,
|
|
2411
|
+
authenticated,
|
|
2412
|
+
stableKeys: stableKey
|
|
2413
|
+
});
|
|
2414
|
+
const query = useClerkQuery({
|
|
2415
|
+
queryKey,
|
|
2416
|
+
queryFn: ({ queryKey }) => {
|
|
2417
|
+
const obj = queryKey[3];
|
|
2418
|
+
return clerk.billing.getSubscription(obj.args);
|
|
2419
|
+
},
|
|
2420
|
+
staleTime: 1e3 * 60,
|
|
2421
|
+
enabled: queriesEnabled,
|
|
2422
|
+
placeholderData: defineKeepPreviousDataFn(keepPreviousData && queriesEnabled)
|
|
2423
|
+
});
|
|
2424
|
+
const revalidate = (0, react.useCallback)(() => queryClient.invalidateQueries({ queryKey: invalidationKey }), [queryClient, invalidationKey]);
|
|
2425
|
+
return {
|
|
2426
|
+
data: query.data,
|
|
2427
|
+
error: query.error ?? void 0,
|
|
2428
|
+
isLoading: query.isLoading,
|
|
2429
|
+
isFetching: query.isFetching,
|
|
2430
|
+
revalidate
|
|
2431
|
+
};
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
//#endregion
|
|
2435
|
+
//#region src/react/hooks/useCheckout.ts
|
|
2436
|
+
/**
|
|
2437
|
+
* @function
|
|
2438
|
+
*
|
|
2439
|
+
* @param [options] - An object containing the configuration for the checkout flow.
|
|
2440
|
+
*
|
|
2441
|
+
* **Required** if the hook is used without a `<CheckoutProvider />` wrapping the component tree.
|
|
2442
|
+
*/
|
|
2443
|
+
const useCheckout = (options) => {
|
|
2444
|
+
const contextOptions = useCheckoutContext();
|
|
2445
|
+
const { for: forOrganization, planId, planPeriod, seatsQuantity, priceId } = options || contextOptions;
|
|
2446
|
+
const organization = useOrganizationBase();
|
|
2447
|
+
const { isLoaded, user } = useUser();
|
|
2448
|
+
const clerk = useClerkInstanceContext();
|
|
2449
|
+
if (user === null && isLoaded) throw new Error("Clerk: Ensure that `useCheckout` is inside a component wrapped with `<Show when=\"signed-in\" />`.");
|
|
2450
|
+
if (isLoaded && forOrganization === "organization" && organization === null) throw new Error("Clerk: Ensure your flow checks for an active organization. Retrieve `orgId` from `useAuth()` and confirm it is defined. For SSR, see: https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object");
|
|
2451
|
+
const signal = (0, react.useCallback)(() => {
|
|
2452
|
+
return clerk.__experimental_checkout({
|
|
2453
|
+
planId,
|
|
2454
|
+
planPeriod,
|
|
2455
|
+
for: forOrganization,
|
|
2456
|
+
seatsQuantity,
|
|
2457
|
+
priceId
|
|
2458
|
+
});
|
|
2459
|
+
}, [
|
|
2460
|
+
user?.id,
|
|
2461
|
+
organization?.id,
|
|
2462
|
+
planId,
|
|
2463
|
+
planPeriod,
|
|
2464
|
+
forOrganization,
|
|
2465
|
+
seatsQuantity,
|
|
2466
|
+
priceId
|
|
2467
|
+
]);
|
|
2468
|
+
const subscribe = (0, react.useCallback)((callback) => {
|
|
2469
|
+
if (!clerk.loaded) return () => {};
|
|
2470
|
+
return clerk.__internal_state.__internal_effect(() => {
|
|
2471
|
+
signal();
|
|
2472
|
+
callback();
|
|
2473
|
+
});
|
|
2474
|
+
}, [
|
|
2475
|
+
signal,
|
|
2476
|
+
clerk.loaded,
|
|
2477
|
+
clerk.__internal_state
|
|
2478
|
+
]);
|
|
2479
|
+
const getSnapshot = (0, react.useCallback)(() => {
|
|
2480
|
+
return signal();
|
|
2481
|
+
}, [signal]);
|
|
2482
|
+
return (0, react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
|
|
2483
|
+
};
|
|
2484
|
+
|
|
2485
|
+
//#endregion
|
|
2486
|
+
//#region src/react/hooks/useCreditBalance.tsx
|
|
2487
|
+
const HOOK_NAME$1 = "useCreditBalance";
|
|
2488
|
+
/**
|
|
2489
|
+
* @internal
|
|
2490
|
+
*/
|
|
2491
|
+
function __internal_useCreditBalanceQuery(params) {
|
|
2492
|
+
useAssertWrappedByClerkProvider(HOOK_NAME$1);
|
|
2493
|
+
const clerk = useClerkInstanceContext();
|
|
2494
|
+
const user = useUserBase();
|
|
2495
|
+
const organization = useOrganizationBase();
|
|
2496
|
+
const billingEnabled = useBillingIsEnabled(params);
|
|
2497
|
+
const recordedRef = (0, react.useRef)(false);
|
|
2498
|
+
(0, react.useEffect)(() => {
|
|
2499
|
+
if (!recordedRef.current && clerk?.telemetry) {
|
|
2500
|
+
clerk.telemetry.record(require_telemetry.eventMethodCalled(HOOK_NAME$1));
|
|
2501
|
+
recordedRef.current = true;
|
|
2502
|
+
}
|
|
2503
|
+
}, [clerk]);
|
|
2504
|
+
const keepPreviousData = params?.keepPreviousData ?? false;
|
|
2505
|
+
const [queryClient] = useClerkQueryClient();
|
|
2506
|
+
const { queryKey, invalidationKey, stableKey, authenticated } = (0, react.useMemo)(() => {
|
|
2507
|
+
const safeOrgId = params?.for === "organization" ? organization?.id : void 0;
|
|
2508
|
+
return createCacheKeys({
|
|
2509
|
+
stablePrefix: STABLE_KEYS.CREDIT_BALANCE_KEY,
|
|
2510
|
+
authenticated: true,
|
|
2511
|
+
tracked: {
|
|
2512
|
+
userId: user?.id,
|
|
2513
|
+
orgId: safeOrgId
|
|
2514
|
+
},
|
|
2515
|
+
untracked: { args: { orgId: safeOrgId } }
|
|
2516
|
+
});
|
|
2517
|
+
}, [
|
|
2518
|
+
user?.id,
|
|
2519
|
+
organization?.id,
|
|
2520
|
+
params?.for
|
|
2521
|
+
]);
|
|
2522
|
+
const queriesEnabled = Boolean(user?.id && billingEnabled && (params?.enabled ?? true));
|
|
2523
|
+
useClearQueriesOnSignOut({
|
|
2524
|
+
isSignedOut: user === null,
|
|
2525
|
+
authenticated,
|
|
2526
|
+
stableKeys: stableKey
|
|
2527
|
+
});
|
|
2528
|
+
const query = useClerkQuery({
|
|
2529
|
+
queryKey,
|
|
2530
|
+
queryFn: ({ queryKey }) => {
|
|
2531
|
+
const obj = queryKey[3];
|
|
2532
|
+
return clerk.billing.getCreditBalance(obj.args);
|
|
2533
|
+
},
|
|
2534
|
+
staleTime: 1e3 * 60,
|
|
2535
|
+
enabled: queriesEnabled,
|
|
2536
|
+
placeholderData: defineKeepPreviousDataFn(keepPreviousData && queriesEnabled)
|
|
2537
|
+
});
|
|
2538
|
+
const revalidate = (0, react.useCallback)(() => queryClient.invalidateQueries({ queryKey: invalidationKey }), [queryClient, invalidationKey]);
|
|
2539
|
+
return {
|
|
2540
|
+
data: query.data,
|
|
2541
|
+
error: query.error ?? void 0,
|
|
2542
|
+
isLoading: query.isLoading,
|
|
2543
|
+
isFetching: query.isFetching,
|
|
2544
|
+
revalidate
|
|
2545
|
+
};
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2548
|
+
//#endregion
|
|
2549
|
+
//#region src/react/hooks/useCreditHistory.tsx
|
|
2550
|
+
const HOOK_NAME = "useCreditHistory";
|
|
2551
|
+
/**
|
|
2552
|
+
* @internal
|
|
2553
|
+
*/
|
|
2554
|
+
function __internal_useCreditHistoryQuery(params) {
|
|
2555
|
+
useAssertWrappedByClerkProvider(HOOK_NAME);
|
|
2556
|
+
const clerk = useClerkInstanceContext();
|
|
2557
|
+
const user = useUserBase();
|
|
2558
|
+
const organization = useOrganizationBase();
|
|
2559
|
+
const billingEnabled = useBillingIsEnabled(params);
|
|
2560
|
+
const recordedRef = (0, react.useRef)(false);
|
|
2561
|
+
(0, react.useEffect)(() => {
|
|
2562
|
+
if (!recordedRef.current && clerk?.telemetry) {
|
|
2563
|
+
clerk.telemetry.record(require_telemetry.eventMethodCalled(HOOK_NAME));
|
|
2564
|
+
recordedRef.current = true;
|
|
2565
|
+
}
|
|
2566
|
+
}, [clerk]);
|
|
2567
|
+
const [queryClient] = useClerkQueryClient();
|
|
2568
|
+
const { queryKey, invalidationKey, stableKey, authenticated } = (0, react.useMemo)(() => {
|
|
2569
|
+
const safeOrgId = params?.for === "organization" ? organization?.id : void 0;
|
|
2570
|
+
return createCacheKeys({
|
|
2571
|
+
stablePrefix: INTERNAL_STABLE_KEYS.CREDIT_HISTORY_KEY,
|
|
2572
|
+
authenticated: true,
|
|
2573
|
+
tracked: {
|
|
2574
|
+
userId: user?.id,
|
|
2575
|
+
orgId: safeOrgId
|
|
2576
|
+
},
|
|
2577
|
+
untracked: { args: { orgId: safeOrgId } }
|
|
2578
|
+
});
|
|
2579
|
+
}, [
|
|
2580
|
+
user?.id,
|
|
2581
|
+
organization?.id,
|
|
2582
|
+
params?.for
|
|
2583
|
+
]);
|
|
2584
|
+
const queriesEnabled = Boolean(user?.id && billingEnabled && (params?.enabled ?? true));
|
|
2585
|
+
useClearQueriesOnSignOut({
|
|
2586
|
+
isSignedOut: user === null,
|
|
2587
|
+
authenticated,
|
|
2588
|
+
stableKeys: stableKey
|
|
2589
|
+
});
|
|
2590
|
+
const query = useClerkQuery({
|
|
2591
|
+
queryKey,
|
|
2592
|
+
queryFn: ({ queryKey }) => {
|
|
2593
|
+
const obj = queryKey[3];
|
|
2594
|
+
return clerk.billing.getCreditHistory(obj.args);
|
|
2595
|
+
},
|
|
2596
|
+
staleTime: 1e3 * 60,
|
|
2597
|
+
enabled: queriesEnabled
|
|
2598
|
+
});
|
|
2599
|
+
const revalidate = (0, react.useCallback)(() => queryClient.invalidateQueries({ queryKey: invalidationKey }), [queryClient, invalidationKey]);
|
|
2600
|
+
return {
|
|
2601
|
+
data: query.data,
|
|
2602
|
+
error: query.error ?? void 0,
|
|
2603
|
+
isLoading: query.isLoading,
|
|
2604
|
+
isFetching: query.isFetching,
|
|
2605
|
+
revalidate
|
|
2606
|
+
};
|
|
2607
|
+
}
|
|
2608
|
+
|
|
2609
|
+
//#endregion
|
|
2610
|
+
//#region src/react/hooks/useStatementQuery.shared.ts
|
|
2611
|
+
function useStatementQueryCacheKeys(params) {
|
|
2612
|
+
const { statementId, userId, orgId, for: forType } = params;
|
|
2613
|
+
return (0, react.useMemo)(() => {
|
|
2614
|
+
return createCacheKeys({
|
|
2615
|
+
stablePrefix: INTERNAL_STABLE_KEYS.BILLING_STATEMENTS_KEY,
|
|
2616
|
+
authenticated: true,
|
|
2617
|
+
tracked: {
|
|
2618
|
+
statementId,
|
|
2619
|
+
forType,
|
|
2620
|
+
userId,
|
|
2621
|
+
orgId
|
|
2622
|
+
},
|
|
2623
|
+
untracked: { args: {
|
|
2624
|
+
id: statementId ?? void 0,
|
|
2625
|
+
orgId: orgId ?? void 0
|
|
2626
|
+
} }
|
|
2627
|
+
});
|
|
2628
|
+
}, [
|
|
2629
|
+
statementId,
|
|
2630
|
+
forType,
|
|
2631
|
+
userId,
|
|
2632
|
+
orgId
|
|
2633
|
+
]);
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2636
|
+
//#endregion
|
|
2637
|
+
//#region src/react/hooks/useStatementQuery.tsx
|
|
2638
|
+
/**
|
|
2639
|
+
* @internal
|
|
2640
|
+
*/
|
|
2641
|
+
function useStatementQuery(params = {}) {
|
|
2642
|
+
const { statementId = null, keepPreviousData = false, for: forType = "user" } = params;
|
|
2643
|
+
const clerk = useClerkInstanceContext();
|
|
2644
|
+
const user = useUserBase();
|
|
2645
|
+
const organization = useOrganizationBase();
|
|
2646
|
+
const organizationId = forType === "organization" ? organization?.id ?? null : null;
|
|
2647
|
+
const { queryKey, stableKey, authenticated } = useStatementQueryCacheKeys({
|
|
2648
|
+
statementId,
|
|
2649
|
+
userId: user?.id ?? null,
|
|
2650
|
+
orgId: organizationId,
|
|
2651
|
+
for: forType
|
|
2652
|
+
});
|
|
2653
|
+
const billingEnabled = useBillingIsEnabled(params);
|
|
2654
|
+
const queryEnabled = Boolean(statementId) && billingEnabled;
|
|
2655
|
+
useClearQueriesOnSignOut({
|
|
2656
|
+
isSignedOut: user === null,
|
|
2657
|
+
authenticated,
|
|
2658
|
+
stableKeys: stableKey
|
|
2659
|
+
});
|
|
2660
|
+
const query = useClerkQuery({
|
|
2661
|
+
queryKey,
|
|
2662
|
+
queryFn: () => {
|
|
2663
|
+
if (!statementId) throw new Error("statementId is required to fetch a statement");
|
|
2664
|
+
return clerk.billing.getStatement({
|
|
2665
|
+
id: statementId,
|
|
2666
|
+
orgId: organizationId ?? void 0
|
|
2667
|
+
});
|
|
2668
|
+
},
|
|
2669
|
+
enabled: queryEnabled,
|
|
2670
|
+
placeholderData: defineKeepPreviousDataFn(keepPreviousData),
|
|
2671
|
+
staleTime: 1e3 * 60
|
|
2672
|
+
});
|
|
2673
|
+
return {
|
|
2674
|
+
data: query.data,
|
|
2675
|
+
error: query.error ?? null,
|
|
2676
|
+
isLoading: query.isLoading,
|
|
2677
|
+
isFetching: query.isFetching
|
|
2678
|
+
};
|
|
2679
|
+
}
|
|
2680
|
+
|
|
2681
|
+
//#endregion
|
|
2682
|
+
//#region src/react/hooks/usePlanDetailsQuery.shared.ts
|
|
2683
|
+
function usePlanDetailsQueryCacheKeys(params) {
|
|
2684
|
+
const { planId } = params;
|
|
2685
|
+
return (0, react.useMemo)(() => {
|
|
2686
|
+
return createCacheKeys({
|
|
2687
|
+
stablePrefix: INTERNAL_STABLE_KEYS.BILLING_PLANS_KEY,
|
|
2688
|
+
authenticated: false,
|
|
2689
|
+
tracked: { planId: planId ?? null },
|
|
2690
|
+
untracked: { args: { id: planId ?? void 0 } }
|
|
2691
|
+
});
|
|
2692
|
+
}, [planId]);
|
|
2693
|
+
}
|
|
2694
|
+
|
|
2695
|
+
//#endregion
|
|
2696
|
+
//#region src/react/hooks/usePlanDetailsQuery.tsx
|
|
2697
|
+
/**
|
|
2698
|
+
* @internal
|
|
2699
|
+
*/
|
|
2700
|
+
function __internal_usePlanDetailsQuery(params = {}) {
|
|
2701
|
+
const { planId, initialPlan = null, keepPreviousData = true } = params;
|
|
2702
|
+
const clerk = useClerkInstanceContext();
|
|
2703
|
+
const targetPlanId = planId ?? initialPlan?.id ?? null;
|
|
2704
|
+
const { queryKey } = usePlanDetailsQueryCacheKeys({ planId: targetPlanId });
|
|
2705
|
+
const billingEnabled = useBillingIsEnabled({ authenticated: false });
|
|
2706
|
+
const query = useClerkQuery({
|
|
2707
|
+
queryKey,
|
|
2708
|
+
queryFn: () => {
|
|
2709
|
+
if (!targetPlanId) throw new Error("planId is required to fetch plan details");
|
|
2710
|
+
return clerk.billing.getPlan({ id: targetPlanId });
|
|
2711
|
+
},
|
|
2712
|
+
enabled: Boolean(targetPlanId) && billingEnabled,
|
|
2713
|
+
initialData: initialPlan ?? void 0,
|
|
2714
|
+
placeholderData: defineKeepPreviousDataFn(keepPreviousData),
|
|
2715
|
+
initialDataUpdatedAt: 0
|
|
2716
|
+
});
|
|
2717
|
+
return {
|
|
2718
|
+
data: query.data,
|
|
2719
|
+
error: query.error ?? null,
|
|
2720
|
+
isLoading: query.isLoading,
|
|
2721
|
+
isFetching: query.isFetching
|
|
2722
|
+
};
|
|
2723
|
+
}
|
|
2724
|
+
|
|
2725
|
+
//#endregion
|
|
2726
|
+
//#region src/react/hooks/usePaymentAttemptQuery.shared.ts
|
|
2727
|
+
function usePaymentAttemptQueryCacheKeys(params) {
|
|
2728
|
+
const { paymentAttemptId, userId, orgId, for: forType } = params;
|
|
2729
|
+
return (0, react.useMemo)(() => {
|
|
2730
|
+
return createCacheKeys({
|
|
2731
|
+
stablePrefix: INTERNAL_STABLE_KEYS.PAYMENT_ATTEMPT_KEY,
|
|
2732
|
+
authenticated: true,
|
|
2733
|
+
tracked: {
|
|
2734
|
+
paymentAttemptId,
|
|
2735
|
+
forType,
|
|
2736
|
+
userId,
|
|
2737
|
+
orgId
|
|
2738
|
+
},
|
|
2739
|
+
untracked: { args: {
|
|
2740
|
+
id: paymentAttemptId ?? void 0,
|
|
2741
|
+
orgId: orgId ?? void 0
|
|
2742
|
+
} }
|
|
2743
|
+
});
|
|
2744
|
+
}, [
|
|
2745
|
+
paymentAttemptId,
|
|
2746
|
+
forType,
|
|
2747
|
+
userId,
|
|
2748
|
+
orgId
|
|
2749
|
+
]);
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2752
|
+
//#endregion
|
|
2753
|
+
//#region src/react/hooks/usePaymentAttemptQuery.tsx
|
|
2754
|
+
/**
|
|
2755
|
+
* @internal
|
|
2756
|
+
*/
|
|
2757
|
+
function usePaymentAttemptQuery(params) {
|
|
2758
|
+
const { paymentAttemptId, keepPreviousData = false, for: forType = "user" } = params;
|
|
2759
|
+
const clerk = useClerkInstanceContext();
|
|
2760
|
+
const user = useUserBase();
|
|
2761
|
+
const organization = useOrganizationBase();
|
|
2762
|
+
const organizationId = forType === "organization" ? organization?.id ?? null : null;
|
|
2763
|
+
const { queryKey, stableKey, authenticated } = usePaymentAttemptQueryCacheKeys({
|
|
2764
|
+
paymentAttemptId,
|
|
2765
|
+
userId: user?.id ?? null,
|
|
2766
|
+
orgId: organizationId,
|
|
2767
|
+
for: forType
|
|
2768
|
+
});
|
|
2769
|
+
const billingEnabled = useBillingIsEnabled(params);
|
|
2770
|
+
const queryEnabled = Boolean(paymentAttemptId) && billingEnabled;
|
|
2771
|
+
useClearQueriesOnSignOut({
|
|
2772
|
+
isSignedOut: user === null,
|
|
2773
|
+
authenticated,
|
|
2774
|
+
stableKeys: stableKey
|
|
2775
|
+
});
|
|
2776
|
+
const query = useClerkQuery({
|
|
2777
|
+
queryKey,
|
|
2778
|
+
queryFn: ({ queryKey }) => {
|
|
2779
|
+
const args = queryKey[3].args;
|
|
2780
|
+
return clerk.billing.getPaymentAttempt(args);
|
|
2781
|
+
},
|
|
2782
|
+
enabled: queryEnabled,
|
|
2783
|
+
placeholderData: defineKeepPreviousDataFn(keepPreviousData),
|
|
2784
|
+
staleTime: 1e3 * 60
|
|
2785
|
+
});
|
|
2786
|
+
return {
|
|
2787
|
+
data: query.data,
|
|
2788
|
+
error: query.error ?? null,
|
|
2789
|
+
isLoading: query.isLoading,
|
|
2790
|
+
isFetching: query.isFetching
|
|
2791
|
+
};
|
|
2792
|
+
}
|
|
2793
|
+
|
|
2794
|
+
//#endregion
|
|
2795
|
+
//#region src/react/hooks/useUserEnterpriseConnections.shared.ts
|
|
2796
|
+
/**
|
|
2797
|
+
* @internal
|
|
2798
|
+
*/
|
|
2799
|
+
function useUserEnterpriseConnectionsCacheKeys(params) {
|
|
2800
|
+
const { userId, withOrganizationAccountLinking = false } = params;
|
|
2801
|
+
return (0, react.useMemo)(() => {
|
|
2802
|
+
return createCacheKeys({
|
|
2803
|
+
stablePrefix: INTERNAL_STABLE_KEYS.USER_ENTERPRISE_CONNECTIONS_KEY,
|
|
2804
|
+
authenticated: Boolean(userId),
|
|
2805
|
+
tracked: {
|
|
2806
|
+
userId: userId ?? null,
|
|
2807
|
+
withOrganizationAccountLinking
|
|
2808
|
+
},
|
|
2809
|
+
untracked: { args: {} }
|
|
2810
|
+
});
|
|
2811
|
+
}, [userId, withOrganizationAccountLinking]);
|
|
2812
|
+
}
|
|
2813
|
+
|
|
2814
|
+
//#endregion
|
|
2815
|
+
//#region src/react/hooks/useUserEnterpriseConnections.tsx
|
|
2816
|
+
/**
|
|
2817
|
+
* Enterprise connections for the signed-in user
|
|
2818
|
+
*
|
|
2819
|
+
* @internal
|
|
2820
|
+
*/
|
|
2821
|
+
function useUserEnterpriseConnections(params = {}) {
|
|
2822
|
+
const { keepPreviousData = true, enabled = true, withOrganizationAccountLinking = false } = params;
|
|
2823
|
+
const clerk = useClerkInstanceContext();
|
|
2824
|
+
const user = useUserBase();
|
|
2825
|
+
const [queryClient] = useClerkQueryClient();
|
|
2826
|
+
const { queryKey, stableKey, authenticated } = useUserEnterpriseConnectionsCacheKeys({
|
|
2827
|
+
userId: user?.id ?? null,
|
|
2828
|
+
withOrganizationAccountLinking
|
|
2829
|
+
});
|
|
2830
|
+
const queryEnabled = enabled && clerk.loaded && Boolean(user);
|
|
2831
|
+
useClearQueriesOnSignOut({
|
|
2832
|
+
isSignedOut: user === null,
|
|
2833
|
+
authenticated,
|
|
2834
|
+
stableKeys: stableKey
|
|
2835
|
+
});
|
|
2836
|
+
const query = useClerkQuery({
|
|
2837
|
+
queryKey,
|
|
2838
|
+
queryFn: () => user?.getEnterpriseConnections({ withOrganizationAccountLinking }),
|
|
2839
|
+
enabled: queryEnabled,
|
|
2840
|
+
placeholderData: defineKeepPreviousDataFn(keepPreviousData)
|
|
2841
|
+
});
|
|
2842
|
+
const revalidate = (0, react.useCallback)(() => queryClient.invalidateQueries({ queryKey: [stableKey] }), [queryClient, stableKey]);
|
|
2843
|
+
return {
|
|
2844
|
+
data: query.data,
|
|
2845
|
+
error: query.error ?? null,
|
|
2846
|
+
isLoading: query.isLoading,
|
|
2847
|
+
isFetching: query.isFetching,
|
|
2848
|
+
revalidate
|
|
2849
|
+
};
|
|
2850
|
+
}
|
|
2851
|
+
|
|
2852
|
+
//#endregion
|
|
2853
|
+
//#region src/react/hooks/useOrganizationEnterpriseConnections.shared.ts
|
|
2854
|
+
/**
|
|
2855
|
+
* @internal
|
|
2856
|
+
*/
|
|
2857
|
+
function useOrganizationEnterpriseConnectionsCacheKeys(params) {
|
|
2858
|
+
const { organizationId, withOrganizationAccountLinking = false } = params;
|
|
2859
|
+
return (0, react.useMemo)(() => {
|
|
2860
|
+
return createCacheKeys({
|
|
2861
|
+
stablePrefix: INTERNAL_STABLE_KEYS.ORGANIZATION_ENTERPRISE_CONNECTIONS_KEY,
|
|
2862
|
+
authenticated: Boolean(organizationId),
|
|
2863
|
+
tracked: {
|
|
2864
|
+
organizationId: organizationId ?? null,
|
|
2865
|
+
withOrganizationAccountLinking
|
|
2866
|
+
},
|
|
2867
|
+
untracked: { args: {} }
|
|
2868
|
+
});
|
|
2869
|
+
}, [organizationId, withOrganizationAccountLinking]);
|
|
2870
|
+
}
|
|
2871
|
+
|
|
2872
|
+
//#endregion
|
|
2873
|
+
//#region src/react/hooks/useOrganizationEnterpriseConnections.tsx
|
|
2874
|
+
/**
|
|
2875
|
+
* Enterprise connections for the active organization
|
|
2876
|
+
*
|
|
2877
|
+
* @internal
|
|
2878
|
+
*/
|
|
2879
|
+
function useOrganizationEnterpriseConnections(params = {}) {
|
|
2880
|
+
const { keepPreviousData = true, enabled = true, withOrganizationAccountLinking = false } = params;
|
|
2881
|
+
const clerk = useClerkInstanceContext();
|
|
2882
|
+
const organization = useOrganizationBase();
|
|
2883
|
+
const [queryClient] = useClerkQueryClient();
|
|
2884
|
+
const { queryKey, stableKey, authenticated } = useOrganizationEnterpriseConnectionsCacheKeys({
|
|
2885
|
+
organizationId: organization?.id ?? null,
|
|
2886
|
+
withOrganizationAccountLinking
|
|
2887
|
+
});
|
|
2888
|
+
const queryEnabled = enabled && clerk.loaded && Boolean(organization);
|
|
2889
|
+
useClearQueriesOnSignOut({
|
|
2890
|
+
isSignedOut: organization === null,
|
|
2891
|
+
authenticated,
|
|
2892
|
+
stableKeys: stableKey
|
|
2893
|
+
});
|
|
2894
|
+
const query = useClerkQuery({
|
|
2895
|
+
queryKey,
|
|
2896
|
+
queryFn: () => organization?.getEnterpriseConnections({ withOrganizationAccountLinking }),
|
|
2897
|
+
enabled: queryEnabled,
|
|
2898
|
+
placeholderData: defineKeepPreviousDataFn(keepPreviousData)
|
|
2899
|
+
});
|
|
2900
|
+
const revalidate = (0, react.useCallback)(() => queryClient.invalidateQueries({ queryKey: [stableKey] }), [queryClient, stableKey]);
|
|
2901
|
+
const createEnterpriseConnection = (0, react.useCallback)(async (createParams) => {
|
|
2902
|
+
const created = await organization?.createEnterpriseConnection(createParams);
|
|
2903
|
+
await revalidate();
|
|
2904
|
+
return created;
|
|
2905
|
+
}, [organization, revalidate]);
|
|
2906
|
+
const updateEnterpriseConnection = (0, react.useCallback)(async (enterpriseConnectionId, updateParams) => {
|
|
2907
|
+
const updated = await organization?.updateEnterpriseConnection(enterpriseConnectionId, updateParams);
|
|
2908
|
+
await revalidate();
|
|
2909
|
+
return updated;
|
|
2910
|
+
}, [organization, revalidate]);
|
|
2911
|
+
const deleteEnterpriseConnection = (0, react.useCallback)(async (enterpriseConnectionId) => {
|
|
2912
|
+
const deleted = await organization?.deleteEnterpriseConnection(enterpriseConnectionId);
|
|
2913
|
+
await revalidate();
|
|
2914
|
+
return deleted;
|
|
2915
|
+
}, [organization, revalidate]);
|
|
2916
|
+
return {
|
|
2917
|
+
data: query.data,
|
|
2918
|
+
error: query.error ?? null,
|
|
2919
|
+
isLoading: query.isLoading,
|
|
2920
|
+
isFetching: query.isFetching,
|
|
2921
|
+
createEnterpriseConnection,
|
|
2922
|
+
updateEnterpriseConnection,
|
|
2923
|
+
deleteEnterpriseConnection,
|
|
2924
|
+
revalidate
|
|
2925
|
+
};
|
|
2926
|
+
}
|
|
2927
|
+
|
|
2928
|
+
//#endregion
|
|
2929
|
+
//#region src/react/hooks/useOrganizationDomains.shared.ts
|
|
2930
|
+
/**
|
|
2931
|
+
* @internal
|
|
2932
|
+
*/
|
|
2933
|
+
function useOrganizationDomainsCacheKeys(params) {
|
|
2934
|
+
const { organizationId, enrollmentMode } = params;
|
|
2935
|
+
return (0, react.useMemo)(() => {
|
|
2936
|
+
return createCacheKeys({
|
|
2937
|
+
stablePrefix: INTERNAL_STABLE_KEYS.ORGANIZATION_DOMAINS_KEY,
|
|
2938
|
+
authenticated: Boolean(organizationId),
|
|
2939
|
+
tracked: {
|
|
2940
|
+
organizationId: organizationId ?? null,
|
|
2941
|
+
enrollmentMode: enrollmentMode ?? null
|
|
2942
|
+
},
|
|
2943
|
+
untracked: { args: {} }
|
|
2944
|
+
});
|
|
2945
|
+
}, [organizationId, enrollmentMode]);
|
|
2946
|
+
}
|
|
2947
|
+
|
|
2948
|
+
//#endregion
|
|
2949
|
+
//#region src/react/hooks/useOrganizationDomains.tsx
|
|
2950
|
+
const OWNERSHIP_VERIFICATION_POLL_INTERVAL_MS = 1e4;
|
|
2951
|
+
/**
|
|
2952
|
+
* Domains for the active organization.
|
|
2953
|
+
*
|
|
2954
|
+
* @internal
|
|
2955
|
+
*/
|
|
2956
|
+
function useOrganizationDomains(params = {}) {
|
|
2957
|
+
const { keepPreviousData = true, enabled = true, enrollmentMode, onOwnershipVerified } = params;
|
|
2958
|
+
const clerk = useClerkInstanceContext();
|
|
2959
|
+
const organization = useOrganizationBase();
|
|
2960
|
+
const [queryClient] = useClerkQueryClient();
|
|
2961
|
+
const onOwnershipVerifiedRef = (0, react.useRef)(onOwnershipVerified);
|
|
2962
|
+
onOwnershipVerifiedRef.current = onOwnershipVerified;
|
|
2963
|
+
const { queryKey, stableKey, authenticated } = useOrganizationDomainsCacheKeys({
|
|
2964
|
+
organizationId: organization?.id ?? null,
|
|
2965
|
+
enrollmentMode
|
|
2966
|
+
});
|
|
2967
|
+
const queryEnabled = enabled && clerk.loaded && Boolean(organization);
|
|
2968
|
+
useClearQueriesOnSignOut({
|
|
2969
|
+
isSignedOut: organization === null,
|
|
2970
|
+
authenticated,
|
|
2971
|
+
stableKeys: stableKey
|
|
2972
|
+
});
|
|
2973
|
+
const fetchParams = enrollmentMode ? { enrollmentMode } : void 0;
|
|
2974
|
+
const query = useClerkQuery({
|
|
2975
|
+
queryKey,
|
|
2976
|
+
queryFn: () => organization?.getDomains(fetchParams),
|
|
2977
|
+
enabled: queryEnabled,
|
|
2978
|
+
placeholderData: defineKeepPreviousDataFn(keepPreviousData)
|
|
2979
|
+
});
|
|
2980
|
+
const revalidate = (0, react.useCallback)(() => queryClient.invalidateQueries({ queryKey: [stableKey] }), [queryClient, stableKey]);
|
|
2981
|
+
const createDomain = (0, react.useCallback)(async (name) => {
|
|
2982
|
+
let created = await organization?.createDomain(name, enrollmentMode ? { enrollmentMode } : void 0);
|
|
2983
|
+
if (created && enrollmentMode === "enterprise_sso") created = (await organization?.prepareOwnershipVerification([created.id]))?.data[0] ?? created;
|
|
2984
|
+
await revalidate();
|
|
2985
|
+
return created;
|
|
2986
|
+
}, [
|
|
2987
|
+
organization,
|
|
2988
|
+
revalidate,
|
|
2989
|
+
enrollmentMode
|
|
2990
|
+
]);
|
|
2991
|
+
const prepareOwnershipVerification = (0, react.useCallback)(async (domains) => {
|
|
2992
|
+
const prepared = await organization?.prepareOwnershipVerification(domains.map((domain) => domain.id));
|
|
2993
|
+
await revalidate();
|
|
2994
|
+
return prepared;
|
|
2995
|
+
}, [organization, revalidate]);
|
|
2996
|
+
const attemptOwnershipVerification = (0, react.useCallback)(async (domains) => {
|
|
2997
|
+
const attempted = await organization?.attemptOwnershipVerification(domains.map((domain) => domain.id));
|
|
2998
|
+
await revalidate();
|
|
2999
|
+
return attempted;
|
|
3000
|
+
}, [organization, revalidate]);
|
|
3001
|
+
const response = query.data;
|
|
3002
|
+
const unverifiedOwnershipKey = (0, react.useMemo)(() => (response?.data ?? []).filter((domain) => domain.ownershipVerification?.status === "unverified").map((domain) => domain.id), [response?.data]).join(",");
|
|
3003
|
+
(0, react.useEffect)(() => {
|
|
3004
|
+
if (!queryEnabled || !organization || !unverifiedOwnershipKey) return;
|
|
3005
|
+
let cancelled = false;
|
|
3006
|
+
let timeoutId;
|
|
3007
|
+
const scheduleNext = () => {
|
|
3008
|
+
timeoutId = setTimeout(() => void runAttempt(), OWNERSHIP_VERIFICATION_POLL_INTERVAL_MS);
|
|
3009
|
+
};
|
|
3010
|
+
const domainIds = unverifiedOwnershipKey.split(",");
|
|
3011
|
+
const runAttempt = async () => {
|
|
3012
|
+
const result = await organization.attemptOwnershipVerification(domainIds).catch((error) => {
|
|
3013
|
+
require_logger.logger.warnOnce(`Clerk: failed to attempt organization domain ownership verification: ${error}`);
|
|
3014
|
+
});
|
|
3015
|
+
if (cancelled) return;
|
|
3016
|
+
await revalidate();
|
|
3017
|
+
if (cancelled) return;
|
|
3018
|
+
const verifiedDomains = result?.data.filter((domain) => domain.ownershipVerification?.status === "verified") ?? [];
|
|
3019
|
+
if (verifiedDomains.length) await onOwnershipVerifiedRef.current?.(verifiedDomains);
|
|
3020
|
+
if (cancelled) return;
|
|
3021
|
+
if (!!result?.data.length && result.data.every((domain) => domain.ownershipVerification?.status === "verified")) return;
|
|
3022
|
+
scheduleNext();
|
|
3023
|
+
};
|
|
3024
|
+
scheduleNext();
|
|
3025
|
+
return () => {
|
|
3026
|
+
cancelled = true;
|
|
3027
|
+
clearTimeout(timeoutId);
|
|
3028
|
+
};
|
|
3029
|
+
}, [unverifiedOwnershipKey, queryEnabled]);
|
|
3030
|
+
return {
|
|
3031
|
+
data: response?.data,
|
|
3032
|
+
totalCount: response?.total_count,
|
|
3033
|
+
error: query.error ?? null,
|
|
3034
|
+
isLoading: query.isLoading,
|
|
3035
|
+
isFetching: query.isFetching,
|
|
3036
|
+
createDomain,
|
|
3037
|
+
prepareOwnershipVerification,
|
|
3038
|
+
attemptOwnershipVerification,
|
|
3039
|
+
revalidate
|
|
3040
|
+
};
|
|
3041
|
+
}
|
|
3042
|
+
|
|
3043
|
+
//#endregion
|
|
3044
|
+
//#region src/react/hooks/useOrganizationEnterpriseConnectionTestRuns.shared.ts
|
|
3045
|
+
/**
|
|
3046
|
+
* @internal
|
|
3047
|
+
*/
|
|
3048
|
+
function useOrganizationEnterpriseConnectionTestRunsCacheKeys(params) {
|
|
3049
|
+
const { organizationId, enterpriseConnectionId, args } = params;
|
|
3050
|
+
return (0, react.useMemo)(() => {
|
|
3051
|
+
return createCacheKeys({
|
|
3052
|
+
stablePrefix: INTERNAL_STABLE_KEYS.ORGANIZATION_ENTERPRISE_CONNECTION_TEST_RUNS_KEY,
|
|
3053
|
+
authenticated: Boolean(organizationId),
|
|
3054
|
+
tracked: {
|
|
3055
|
+
organizationId: organizationId ?? null,
|
|
3056
|
+
enterpriseConnectionId: enterpriseConnectionId ?? null
|
|
3057
|
+
},
|
|
3058
|
+
untracked: { args }
|
|
3059
|
+
});
|
|
3060
|
+
}, [
|
|
3061
|
+
organizationId,
|
|
3062
|
+
enterpriseConnectionId,
|
|
3063
|
+
JSON.stringify(args)
|
|
3064
|
+
]);
|
|
3065
|
+
}
|
|
3066
|
+
|
|
3067
|
+
//#endregion
|
|
3068
|
+
//#region src/react/hooks/useOrganizationEnterpriseConnectionTestRuns.tsx
|
|
3069
|
+
const DEFAULT_POLL_INTERVAL_MS = 2e3;
|
|
3070
|
+
/**
|
|
3071
|
+
* Subscribes to the list of enterprise-connection test runs for the active organization
|
|
3072
|
+
*
|
|
3073
|
+
* @internal
|
|
3074
|
+
*/
|
|
3075
|
+
function useOrganizationEnterpriseConnectionTestRuns(params) {
|
|
3076
|
+
const { enterpriseConnectionId, params: fetchParams = {
|
|
3077
|
+
initialPage: 1,
|
|
3078
|
+
pageSize: 10
|
|
3079
|
+
}, pollIntervalMs = DEFAULT_POLL_INTERVAL_MS, enabled = true, keepPreviousData = false } = params;
|
|
3080
|
+
const clerk = useClerkInstanceContext();
|
|
3081
|
+
const organization = useOrganizationBase();
|
|
3082
|
+
const [queryClient] = useClerkQueryClient();
|
|
3083
|
+
const { queryKey, invalidationKey, stableKey, authenticated } = useOrganizationEnterpriseConnectionTestRunsCacheKeys({
|
|
3084
|
+
organizationId: organization?.id ?? null,
|
|
3085
|
+
enterpriseConnectionId,
|
|
3086
|
+
args: fetchParams
|
|
3087
|
+
});
|
|
3088
|
+
useClearQueriesOnSignOut({
|
|
3089
|
+
isSignedOut: organization === null,
|
|
3090
|
+
authenticated,
|
|
3091
|
+
stableKeys: stableKey
|
|
3092
|
+
});
|
|
3093
|
+
const queryEnabled = enabled && clerk.loaded && Boolean(organization) && Boolean(enterpriseConnectionId);
|
|
3094
|
+
const [shouldPoll, setShouldPoll] = (0, react.useState)(false);
|
|
3095
|
+
(0, react.useEffect)(() => {
|
|
3096
|
+
setShouldPoll(false);
|
|
3097
|
+
}, [enterpriseConnectionId]);
|
|
3098
|
+
const query = useClerkQuery({
|
|
3099
|
+
queryKey,
|
|
3100
|
+
queryFn: () => {
|
|
3101
|
+
if (!enterpriseConnectionId) throw new Error("enterpriseConnectionId is required to fetch test runs");
|
|
3102
|
+
return organization?.getEnterpriseConnectionTestRuns(enterpriseConnectionId, fetchParams);
|
|
3103
|
+
},
|
|
3104
|
+
refetchInterval: (q) => {
|
|
3105
|
+
if (!shouldPoll) return false;
|
|
3106
|
+
return (q.state.data?.data?.length ?? 0) > 0 ? false : pollIntervalMs;
|
|
3107
|
+
},
|
|
3108
|
+
enabled: queryEnabled,
|
|
3109
|
+
refetchIntervalInBackground: false,
|
|
3110
|
+
placeholderData: defineKeepPreviousDataFn(keepPreviousData)
|
|
3111
|
+
});
|
|
3112
|
+
const hasRows = (query.data?.data?.length ?? 0) > 0;
|
|
3113
|
+
(0, react.useEffect)(() => {
|
|
3114
|
+
if (shouldPoll && hasRows) setShouldPoll(false);
|
|
3115
|
+
}, [shouldPoll, hasRows]);
|
|
3116
|
+
const revalidate = (0, react.useCallback)(async (options) => {
|
|
3117
|
+
if ((options?.armPolling ?? true) && !hasRows) setShouldPoll(true);
|
|
3118
|
+
if (options?.exact) await queryClient.invalidateQueries({
|
|
3119
|
+
queryKey,
|
|
3120
|
+
exact: true
|
|
3121
|
+
});
|
|
3122
|
+
else await queryClient.invalidateQueries({ queryKey: invalidationKey });
|
|
3123
|
+
const fresh = queryClient.getQueryData(queryKey);
|
|
3124
|
+
return {
|
|
3125
|
+
data: fresh?.data,
|
|
3126
|
+
totalCount: fresh?.total_count
|
|
3127
|
+
};
|
|
3128
|
+
}, [
|
|
3129
|
+
queryClient,
|
|
3130
|
+
invalidationKey,
|
|
3131
|
+
queryKey,
|
|
3132
|
+
hasRows
|
|
3133
|
+
]);
|
|
3134
|
+
const isPolling = queryEnabled && shouldPoll && !hasRows;
|
|
3135
|
+
return {
|
|
3136
|
+
data: query.data?.data,
|
|
3137
|
+
totalCount: query.data?.total_count,
|
|
3138
|
+
error: query.error ?? null,
|
|
3139
|
+
isLoading: query.isLoading,
|
|
3140
|
+
isFetching: query.isFetching,
|
|
3141
|
+
isPolling,
|
|
3142
|
+
revalidate
|
|
3143
|
+
};
|
|
3144
|
+
}
|
|
3145
|
+
|
|
3146
|
+
//#endregion
|
|
3147
|
+
//#region src/react/utils.ts
|
|
3148
|
+
function assertClerkSingletonExists(clerk) {
|
|
3149
|
+
if (!clerk) require_internal_clerk_js_errors.clerkCoreErrorNoClerkSingleton();
|
|
3150
|
+
}
|
|
3151
|
+
|
|
3152
|
+
//#endregion
|
|
3153
|
+
//#region src/react/ClerkContextProvider.tsx
|
|
3154
|
+
function ClerkContextProvider(props) {
|
|
3155
|
+
const clerk = props.clerk;
|
|
3156
|
+
assertClerkSingletonExists(clerk);
|
|
3157
|
+
if (props.initialState instanceof Promise && !("use" in react.default && typeof react.default.use === "function")) throw new Error("initialState cannot be a promise if React version is less than 19");
|
|
3158
|
+
const clerkCtx = react.default.useMemo(() => ({ value: clerk }), [props.clerkStatus]);
|
|
3159
|
+
return /* @__PURE__ */ react.default.createElement(InitialStateProvider, { initialState: props.initialState }, /* @__PURE__ */ react.default.createElement(ClerkInstanceContext.Provider, { value: clerkCtx }, /* @__PURE__ */ react.default.createElement(__experimental_CheckoutProvider, { value: void 0 }, props.children)));
|
|
3160
|
+
}
|
|
3161
|
+
|
|
3162
|
+
//#endregion
|
|
3163
|
+
//#region src/react/stripe-react/utils.ts
|
|
3164
|
+
const usePrevious = (value) => {
|
|
3165
|
+
const ref = (0, react.useRef)(value);
|
|
3166
|
+
(0, react.useEffect)(() => {
|
|
3167
|
+
ref.current = value;
|
|
3168
|
+
}, [value]);
|
|
3169
|
+
return ref.current;
|
|
3170
|
+
};
|
|
3171
|
+
const useAttachEvent = (element, event, cb) => {
|
|
3172
|
+
const cbDefined = !!cb;
|
|
3173
|
+
const cbRef = (0, react.useRef)(cb);
|
|
3174
|
+
(0, react.useEffect)(() => {
|
|
3175
|
+
cbRef.current = cb;
|
|
3176
|
+
}, [cb]);
|
|
3177
|
+
(0, react.useEffect)(() => {
|
|
3178
|
+
if (!cbDefined || !element) return () => {};
|
|
3179
|
+
const decoratedCb = (...args) => {
|
|
3180
|
+
if (cbRef.current) cbRef.current(...args);
|
|
3181
|
+
};
|
|
3182
|
+
element.on(event, decoratedCb);
|
|
3183
|
+
return () => {
|
|
3184
|
+
element.off(event, decoratedCb);
|
|
3185
|
+
};
|
|
3186
|
+
}, [
|
|
3187
|
+
cbDefined,
|
|
3188
|
+
event,
|
|
3189
|
+
element,
|
|
3190
|
+
cbRef
|
|
3191
|
+
]);
|
|
3192
|
+
};
|
|
3193
|
+
|
|
3194
|
+
//#endregion
|
|
3195
|
+
//#region src/react/stripe-react/index.tsx
|
|
3196
|
+
const ElementsContext = react.default.createContext(null);
|
|
3197
|
+
ElementsContext.displayName = "ElementsContext";
|
|
3198
|
+
const parseElementsContext = (ctx, useCase) => {
|
|
3199
|
+
if (!ctx) throw new Error(`Could not find Elements context; You need to wrap the part of your app that ${useCase} in an <Elements> provider.`);
|
|
3200
|
+
return ctx;
|
|
3201
|
+
};
|
|
3202
|
+
/**
|
|
3203
|
+
* The `Elements` provider allows you to use [Element components](https://stripe.com/docs/stripe-js/react#element-components) and access the [Stripe object](https://stripe.com/docs/js/initializing) in any nested component.
|
|
3204
|
+
* Render an `Elements` provider at the root of your React app so that it is available everywhere you need it.
|
|
3205
|
+
*
|
|
3206
|
+
* To use the `Elements` provider, call `loadStripe` from `@stripe/stripe-js` with your publishable key.
|
|
3207
|
+
* The `loadStripe` function will asynchronously load the Stripe.js script and initialize a `Stripe` object.
|
|
3208
|
+
* Pass the returned `Promise` to `Elements`.
|
|
3209
|
+
*
|
|
3210
|
+
* @docs https://stripe.com/docs/stripe-js/react#elements-provider
|
|
3211
|
+
*/
|
|
3212
|
+
const Elements = (({ stripe: rawStripeProp, options, children }) => {
|
|
3213
|
+
const parsed = react.default.useMemo(() => parseStripeProp(rawStripeProp), [rawStripeProp]);
|
|
3214
|
+
const [ctx, setContext] = react.default.useState(() => ({
|
|
3215
|
+
stripe: parsed.tag === "sync" ? parsed.stripe : null,
|
|
3216
|
+
elements: parsed.tag === "sync" ? parsed.stripe.elements(options) : null
|
|
3217
|
+
}));
|
|
3218
|
+
react.default.useEffect(() => {
|
|
3219
|
+
let isMounted = true;
|
|
3220
|
+
const safeSetContext = (stripe) => {
|
|
3221
|
+
setContext((ctx) => {
|
|
3222
|
+
if (ctx.stripe) return ctx;
|
|
3223
|
+
return {
|
|
3224
|
+
stripe,
|
|
3225
|
+
elements: stripe.elements(options)
|
|
3226
|
+
};
|
|
3227
|
+
});
|
|
3228
|
+
};
|
|
3229
|
+
if (parsed.tag === "async" && !ctx.stripe) parsed.stripePromise.then((stripe) => {
|
|
3230
|
+
if (stripe && isMounted) safeSetContext(stripe);
|
|
3231
|
+
});
|
|
3232
|
+
else if (parsed.tag === "sync" && !ctx.stripe) safeSetContext(parsed.stripe);
|
|
3233
|
+
return () => {
|
|
3234
|
+
isMounted = false;
|
|
3235
|
+
};
|
|
3236
|
+
}, [
|
|
3237
|
+
parsed,
|
|
3238
|
+
ctx,
|
|
3239
|
+
options
|
|
3240
|
+
]);
|
|
3241
|
+
const prevStripe = usePrevious(rawStripeProp);
|
|
3242
|
+
react.default.useEffect(() => {
|
|
3243
|
+
if (prevStripe !== null && prevStripe !== rawStripeProp) console.warn("Unsupported prop change on Elements: You cannot change the `stripe` prop after setting it.");
|
|
3244
|
+
}, [prevStripe, rawStripeProp]);
|
|
3245
|
+
const prevOptions = usePrevious(options);
|
|
3246
|
+
react.default.useEffect(() => {
|
|
3247
|
+
if (!ctx.elements) return;
|
|
3248
|
+
const updates = extractAllowedOptionsUpdates(options, prevOptions, ["clientSecret", "fonts"]);
|
|
3249
|
+
if (updates) ctx.elements.update(updates);
|
|
3250
|
+
}, [
|
|
3251
|
+
options,
|
|
3252
|
+
prevOptions,
|
|
3253
|
+
ctx.elements
|
|
3254
|
+
]);
|
|
3255
|
+
return /* @__PURE__ */ react.default.createElement(ElementsContext.Provider, { value: ctx }, children);
|
|
3256
|
+
});
|
|
3257
|
+
const useElementsContextWithUseCase = (useCaseMessage) => {
|
|
3258
|
+
return parseElementsContext(react.default.useContext(ElementsContext), useCaseMessage);
|
|
3259
|
+
};
|
|
3260
|
+
const useElements = () => {
|
|
3261
|
+
const { elements } = useElementsContextWithUseCase("calls useElements()");
|
|
3262
|
+
return elements;
|
|
3263
|
+
};
|
|
3264
|
+
const INVALID_STRIPE_ERROR = "Invalid prop `stripe` supplied to `Elements`. We recommend using the `loadStripe` utility from `@stripe/stripe-js`. See https://stripe.com/docs/stripe-js/react#elements-props-stripe for details.";
|
|
3265
|
+
const validateStripe = (maybeStripe, errorMsg = INVALID_STRIPE_ERROR) => {
|
|
3266
|
+
if (maybeStripe === null || isStripe(maybeStripe)) return maybeStripe;
|
|
3267
|
+
throw new Error(errorMsg);
|
|
3268
|
+
};
|
|
3269
|
+
const parseStripeProp = (raw, errorMsg = INVALID_STRIPE_ERROR) => {
|
|
3270
|
+
if (isPromise(raw)) return {
|
|
3271
|
+
tag: "async",
|
|
3272
|
+
stripePromise: Promise.resolve(raw).then((result) => validateStripe(result, errorMsg))
|
|
3273
|
+
};
|
|
3274
|
+
const stripe = validateStripe(raw, errorMsg);
|
|
3275
|
+
if (stripe === null) return { tag: "empty" };
|
|
3276
|
+
return {
|
|
3277
|
+
tag: "sync",
|
|
3278
|
+
stripe
|
|
3279
|
+
};
|
|
3280
|
+
};
|
|
3281
|
+
const isUnknownObject = (raw) => {
|
|
3282
|
+
return raw !== null && typeof raw === "object";
|
|
3283
|
+
};
|
|
3284
|
+
const isPromise = (raw) => {
|
|
3285
|
+
return isUnknownObject(raw) && typeof raw.then === "function";
|
|
3286
|
+
};
|
|
3287
|
+
const isStripe = (raw) => {
|
|
3288
|
+
return isUnknownObject(raw) && typeof raw.elements === "function" && typeof raw.createToken === "function" && typeof raw.createPaymentMethod === "function" && typeof raw.confirmCardPayment === "function";
|
|
3289
|
+
};
|
|
3290
|
+
const extractAllowedOptionsUpdates = (options, prevOptions, immutableKeys) => {
|
|
3291
|
+
if (!isUnknownObject(options)) return null;
|
|
3292
|
+
return Object.keys(options).reduce((newOptions, key) => {
|
|
3293
|
+
const isUpdated = !isUnknownObject(prevOptions) || !isEqual(options[key], prevOptions[key]);
|
|
3294
|
+
if (immutableKeys.includes(key)) {
|
|
3295
|
+
if (isUpdated) console.warn(`Unsupported prop change: options.${key} is not a mutable property.`);
|
|
3296
|
+
return newOptions;
|
|
3297
|
+
}
|
|
3298
|
+
if (!isUpdated) return newOptions;
|
|
3299
|
+
return {
|
|
3300
|
+
...newOptions || {},
|
|
3301
|
+
[key]: options[key]
|
|
3302
|
+
};
|
|
3303
|
+
}, null);
|
|
3304
|
+
};
|
|
3305
|
+
const PLAIN_OBJECT_STR = "[object Object]";
|
|
3306
|
+
const isEqual = (left, right) => {
|
|
3307
|
+
if (!isUnknownObject(left) || !isUnknownObject(right)) return left === right;
|
|
3308
|
+
const leftArray = Array.isArray(left);
|
|
3309
|
+
if (leftArray !== Array.isArray(right)) return false;
|
|
3310
|
+
const leftPlainObject = Object.prototype.toString.call(left) === PLAIN_OBJECT_STR;
|
|
3311
|
+
if (leftPlainObject !== (Object.prototype.toString.call(right) === PLAIN_OBJECT_STR)) return false;
|
|
3312
|
+
if (!leftPlainObject && !leftArray) return left === right;
|
|
3313
|
+
const leftKeys = Object.keys(left);
|
|
3314
|
+
const rightKeys = Object.keys(right);
|
|
3315
|
+
if (leftKeys.length !== rightKeys.length) return false;
|
|
3316
|
+
const keySet = {};
|
|
3317
|
+
for (let i = 0; i < leftKeys.length; i += 1) keySet[leftKeys[i]] = true;
|
|
3318
|
+
for (let i = 0; i < rightKeys.length; i += 1) keySet[rightKeys[i]] = true;
|
|
3319
|
+
const allKeys = Object.keys(keySet);
|
|
3320
|
+
if (allKeys.length !== leftKeys.length) return false;
|
|
3321
|
+
const l = left;
|
|
3322
|
+
const r = right;
|
|
3323
|
+
const pred = (key) => {
|
|
3324
|
+
return isEqual(l[key], r[key]);
|
|
3325
|
+
};
|
|
3326
|
+
return allKeys.every(pred);
|
|
3327
|
+
};
|
|
3328
|
+
const useStripe = () => {
|
|
3329
|
+
const { stripe } = useElementsOrCheckoutSdkContextWithUseCase("calls useStripe()");
|
|
3330
|
+
return stripe;
|
|
3331
|
+
};
|
|
3332
|
+
const useElementsOrCheckoutSdkContextWithUseCase = (useCaseString) => {
|
|
3333
|
+
return parseElementsContext(react.default.useContext(ElementsContext), useCaseString);
|
|
3334
|
+
};
|
|
3335
|
+
const capitalized = (str) => str.charAt(0).toUpperCase() + str.slice(1);
|
|
3336
|
+
const createElementComponent = (type, isServer) => {
|
|
3337
|
+
const displayName = `${capitalized(type)}Element`;
|
|
3338
|
+
const ClientElement = ({ id, className, fallback, options = {}, onBlur, onFocus, onReady, onChange, onEscape, onClick, onLoadError, onLoaderStart, onNetworksChange, onConfirm, onCancel, onShippingAddressChange, onShippingRateChange }) => {
|
|
3339
|
+
const ctx = useElementsOrCheckoutSdkContextWithUseCase(`mounts <${displayName}>`);
|
|
3340
|
+
const elements = "elements" in ctx ? ctx.elements : null;
|
|
3341
|
+
const [element, setElement] = react.default.useState(null);
|
|
3342
|
+
const elementRef = react.default.useRef(null);
|
|
3343
|
+
const domNode = react.default.useRef(null);
|
|
3344
|
+
const [isReady, setReady] = (0, react.useState)(false);
|
|
3345
|
+
useAttachEvent(element, "blur", onBlur);
|
|
3346
|
+
useAttachEvent(element, "focus", onFocus);
|
|
3347
|
+
useAttachEvent(element, "escape", onEscape);
|
|
3348
|
+
useAttachEvent(element, "click", onClick);
|
|
3349
|
+
useAttachEvent(element, "loaderror", onLoadError);
|
|
3350
|
+
useAttachEvent(element, "loaderstart", onLoaderStart);
|
|
3351
|
+
useAttachEvent(element, "networkschange", onNetworksChange);
|
|
3352
|
+
useAttachEvent(element, "confirm", onConfirm);
|
|
3353
|
+
useAttachEvent(element, "cancel", onCancel);
|
|
3354
|
+
useAttachEvent(element, "shippingaddresschange", onShippingAddressChange);
|
|
3355
|
+
useAttachEvent(element, "shippingratechange", onShippingRateChange);
|
|
3356
|
+
useAttachEvent(element, "change", onChange);
|
|
3357
|
+
let readyCallback;
|
|
3358
|
+
if (onReady) readyCallback = () => {
|
|
3359
|
+
setReady(true);
|
|
3360
|
+
onReady(element);
|
|
3361
|
+
};
|
|
3362
|
+
useAttachEvent(element, "ready", readyCallback);
|
|
3363
|
+
react.default.useLayoutEffect(() => {
|
|
3364
|
+
if (elementRef.current === null && domNode.current !== null && elements) {
|
|
3365
|
+
let newElement = null;
|
|
3366
|
+
if (elements) newElement = elements.create(type, options);
|
|
3367
|
+
elementRef.current = newElement;
|
|
3368
|
+
setElement(newElement);
|
|
3369
|
+
if (newElement) newElement.mount(domNode.current);
|
|
3370
|
+
}
|
|
3371
|
+
}, [elements, options]);
|
|
3372
|
+
const prevOptions = usePrevious(options);
|
|
3373
|
+
react.default.useEffect(() => {
|
|
3374
|
+
if (!elementRef.current) return;
|
|
3375
|
+
const updates = extractAllowedOptionsUpdates(options, prevOptions, ["paymentRequest"]);
|
|
3376
|
+
if (updates && "update" in elementRef.current) elementRef.current.update(updates);
|
|
3377
|
+
}, [options, prevOptions]);
|
|
3378
|
+
react.default.useLayoutEffect(() => {
|
|
3379
|
+
return () => {
|
|
3380
|
+
if (elementRef.current && typeof elementRef.current.destroy === "function") try {
|
|
3381
|
+
elementRef.current.destroy();
|
|
3382
|
+
elementRef.current = null;
|
|
3383
|
+
} catch {}
|
|
3384
|
+
};
|
|
3385
|
+
}, []);
|
|
3386
|
+
return /* @__PURE__ */ react.default.createElement(react.default.Fragment, null, !isReady && fallback, /* @__PURE__ */ react.default.createElement("div", {
|
|
3387
|
+
id,
|
|
3388
|
+
style: {
|
|
3389
|
+
height: isReady ? "unset" : "0px",
|
|
3390
|
+
visibility: isReady ? "visible" : "hidden"
|
|
3391
|
+
},
|
|
3392
|
+
className,
|
|
3393
|
+
ref: domNode
|
|
3394
|
+
}));
|
|
3395
|
+
};
|
|
3396
|
+
const ServerElement = (props) => {
|
|
3397
|
+
useElementsOrCheckoutSdkContextWithUseCase(`mounts <${displayName}>`);
|
|
3398
|
+
const { id, className } = props;
|
|
3399
|
+
return /* @__PURE__ */ react.default.createElement("div", {
|
|
3400
|
+
id,
|
|
3401
|
+
className
|
|
3402
|
+
});
|
|
3403
|
+
};
|
|
3404
|
+
const Element = isServer ? ServerElement : ClientElement;
|
|
3405
|
+
Element.displayName = displayName;
|
|
3406
|
+
Element.__elementType = type;
|
|
3407
|
+
return Element;
|
|
3408
|
+
};
|
|
3409
|
+
const PaymentElement$1 = createElementComponent("payment", typeof window === "undefined");
|
|
3410
|
+
|
|
3411
|
+
//#endregion
|
|
3412
|
+
//#region src/react/billing/useInitializePaymentMethod.tsx
|
|
3413
|
+
/**
|
|
3414
|
+
* @internal
|
|
3415
|
+
*/
|
|
3416
|
+
function useInitializePaymentMethod(options) {
|
|
3417
|
+
const { for: forType } = options ?? {};
|
|
3418
|
+
const organization = useOrganizationBase();
|
|
3419
|
+
const user = useUserBase();
|
|
3420
|
+
const resource = forType === "organization" ? organization : user;
|
|
3421
|
+
const billingEnabled = useBillingIsEnabled(options);
|
|
3422
|
+
const stableKey = "billing-payment-method-initialize";
|
|
3423
|
+
const authenticated = true;
|
|
3424
|
+
const queryKey = (0, react.useMemo)(() => {
|
|
3425
|
+
return [
|
|
3426
|
+
stableKey,
|
|
3427
|
+
authenticated,
|
|
3428
|
+
{ resourceId: resource?.id },
|
|
3429
|
+
{}
|
|
3430
|
+
];
|
|
3431
|
+
}, [resource?.id]);
|
|
3432
|
+
const isEnabled = Boolean(resource?.id) && billingEnabled;
|
|
3433
|
+
useClearQueriesOnSignOut({
|
|
3434
|
+
isSignedOut: user === null,
|
|
3435
|
+
authenticated,
|
|
3436
|
+
stableKeys: stableKey
|
|
3437
|
+
});
|
|
3438
|
+
const query = useClerkQuery({
|
|
3439
|
+
queryKey,
|
|
3440
|
+
queryFn: async () => {
|
|
3441
|
+
if (!resource) return;
|
|
3442
|
+
return resource.initializePaymentMethod({ gateway: "stripe" });
|
|
3443
|
+
},
|
|
3444
|
+
enabled: isEnabled,
|
|
3445
|
+
staleTime: 1e3 * 60,
|
|
3446
|
+
refetchOnWindowFocus: false,
|
|
3447
|
+
placeholderData: defineKeepPreviousDataFn(isEnabled)
|
|
3448
|
+
});
|
|
3449
|
+
const [queryClient] = useClerkQueryClient();
|
|
3450
|
+
const initializePaymentMethod = (0, react.useCallback)(async () => {
|
|
3451
|
+
if (!resource) return;
|
|
3452
|
+
const result = await resource.initializePaymentMethod({ gateway: "stripe" });
|
|
3453
|
+
queryClient.setQueryData(queryKey, result);
|
|
3454
|
+
return result;
|
|
3455
|
+
}, [
|
|
3456
|
+
queryClient,
|
|
3457
|
+
queryKey,
|
|
3458
|
+
resource
|
|
3459
|
+
]);
|
|
3460
|
+
return {
|
|
3461
|
+
initializedPaymentMethod: query.data ?? void 0,
|
|
3462
|
+
initializePaymentMethod
|
|
3463
|
+
};
|
|
3464
|
+
}
|
|
3465
|
+
|
|
3466
|
+
//#endregion
|
|
3467
|
+
//#region src/react/billing/useStripeClerkLibs.tsx
|
|
3468
|
+
/**
|
|
3469
|
+
* @internal
|
|
3470
|
+
*/
|
|
3471
|
+
function useStripeClerkLibs() {
|
|
3472
|
+
const clerk = useClerk();
|
|
3473
|
+
return useClerkQuery({
|
|
3474
|
+
queryKey: ["clerk-stripe-sdk"],
|
|
3475
|
+
queryFn: async () => {
|
|
3476
|
+
return { loadStripe: await clerk.__internal_loadStripeJs() };
|
|
3477
|
+
},
|
|
3478
|
+
staleTime: Infinity,
|
|
3479
|
+
refetchOnWindowFocus: false,
|
|
3480
|
+
placeholderData: defineKeepPreviousDataFn(true)
|
|
3481
|
+
}).data ?? null;
|
|
3482
|
+
}
|
|
3483
|
+
|
|
3484
|
+
//#endregion
|
|
3485
|
+
//#region src/react/billing/useStripeLoader.tsx
|
|
3486
|
+
/**
|
|
3487
|
+
* @internal
|
|
3488
|
+
*/
|
|
3489
|
+
function useStripeLoader(options) {
|
|
3490
|
+
const { stripeClerkLibs, externalGatewayId, stripePublishableKey } = options;
|
|
3491
|
+
const queryKey = (0, react.useMemo)(() => {
|
|
3492
|
+
return ["stripe-sdk", {
|
|
3493
|
+
externalGatewayId,
|
|
3494
|
+
stripePublishableKey
|
|
3495
|
+
}];
|
|
3496
|
+
}, [externalGatewayId, stripePublishableKey]);
|
|
3497
|
+
const billingEnabled = useBillingIsEnabled({ authenticated: true });
|
|
3498
|
+
return useClerkQuery({
|
|
3499
|
+
queryKey,
|
|
3500
|
+
queryFn: () => {
|
|
3501
|
+
if (!stripeClerkLibs || !externalGatewayId || !stripePublishableKey) return null;
|
|
3502
|
+
return stripeClerkLibs.loadStripe(stripePublishableKey, { stripeAccount: externalGatewayId });
|
|
3503
|
+
},
|
|
3504
|
+
enabled: Boolean(stripeClerkLibs && externalGatewayId && stripePublishableKey) && billingEnabled,
|
|
3505
|
+
staleTime: 1e3 * 60,
|
|
3506
|
+
refetchOnWindowFocus: false,
|
|
3507
|
+
placeholderData: defineKeepPreviousDataFn(true)
|
|
3508
|
+
}).data;
|
|
3509
|
+
}
|
|
3510
|
+
|
|
3511
|
+
//#endregion
|
|
3512
|
+
//#region src/react/billing/payment-element.tsx
|
|
3513
|
+
const useInternalEnvironment = () => {
|
|
3514
|
+
return useClerk().__internal_environment;
|
|
3515
|
+
};
|
|
3516
|
+
const useLocalization = () => {
|
|
3517
|
+
const clerk = useClerk();
|
|
3518
|
+
let locale = "en";
|
|
3519
|
+
try {
|
|
3520
|
+
locale = clerk.__internal_getOption("localization")?.locale || "en";
|
|
3521
|
+
} catch {}
|
|
3522
|
+
return locale.split("-")[0];
|
|
3523
|
+
};
|
|
3524
|
+
const usePaymentSourceUtils = (forResource = "user") => {
|
|
3525
|
+
const stripeClerkLibs = useStripeClerkLibs();
|
|
3526
|
+
const environment = useInternalEnvironment();
|
|
3527
|
+
const { initializedPaymentMethod, initializePaymentMethod } = useInitializePaymentMethod({ for: forResource });
|
|
3528
|
+
const stripePublishableKey = environment?.commerceSettings.billing.stripePublishableKey ?? void 0;
|
|
3529
|
+
return {
|
|
3530
|
+
stripe: useStripeLoader({
|
|
3531
|
+
stripeClerkLibs,
|
|
3532
|
+
externalGatewayId: initializedPaymentMethod?.externalGatewayId,
|
|
3533
|
+
stripePublishableKey
|
|
3534
|
+
}),
|
|
3535
|
+
initializePaymentMethod,
|
|
3536
|
+
externalClientSecret: initializedPaymentMethod?.externalClientSecret,
|
|
3537
|
+
paymentMethodOrder: initializedPaymentMethod?.paymentMethodOrder
|
|
3538
|
+
};
|
|
3539
|
+
};
|
|
3540
|
+
const [PaymentElementContext, usePaymentElementContext] = createContextAndHook("PaymentElementContext");
|
|
3541
|
+
const [StripeUtilsContext, useStripeUtilsContext] = createContextAndHook("StripeUtilsContext");
|
|
3542
|
+
const ValidateStripeUtils = ({ children }) => {
|
|
3543
|
+
const stripe = useStripe();
|
|
3544
|
+
const elements = useElements();
|
|
3545
|
+
return /* @__PURE__ */ react.default.createElement(StripeUtilsContext.Provider, { value: { value: {
|
|
3546
|
+
stripe,
|
|
3547
|
+
elements
|
|
3548
|
+
} } }, children);
|
|
3549
|
+
};
|
|
3550
|
+
const DummyStripeUtils = ({ children }) => {
|
|
3551
|
+
return /* @__PURE__ */ react.default.createElement(StripeUtilsContext.Provider, { value: { value: {} } }, children);
|
|
3552
|
+
};
|
|
3553
|
+
const PropsProvider = ({ children, ...props }) => {
|
|
3554
|
+
const utils = usePaymentSourceUtils(props.for);
|
|
3555
|
+
const [isPaymentElementReady, setIsPaymentElementReady] = (0, react.useState)(false);
|
|
3556
|
+
return /* @__PURE__ */ react.default.createElement(PaymentElementContext.Provider, { value: { value: {
|
|
3557
|
+
...props,
|
|
3558
|
+
...utils,
|
|
3559
|
+
setIsPaymentElementReady,
|
|
3560
|
+
isPaymentElementReady
|
|
3561
|
+
} } }, children);
|
|
3562
|
+
};
|
|
3563
|
+
const PaymentElementProvider = ({ children, ...props }) => {
|
|
3564
|
+
return /* @__PURE__ */ react.default.createElement(PropsProvider, props, /* @__PURE__ */ react.default.createElement(PaymentElementInternalRoot, null, children));
|
|
3565
|
+
};
|
|
3566
|
+
const PaymentElementInternalRoot = (props) => {
|
|
3567
|
+
const { stripe, externalClientSecret, stripeAppearance } = usePaymentElementContext();
|
|
3568
|
+
const locale = useLocalization();
|
|
3569
|
+
if (stripe && externalClientSecret) return /* @__PURE__ */ react.default.createElement(Elements, {
|
|
3570
|
+
key: externalClientSecret,
|
|
3571
|
+
stripe,
|
|
3572
|
+
options: {
|
|
3573
|
+
loader: "never",
|
|
3574
|
+
clientSecret: externalClientSecret,
|
|
3575
|
+
appearance: { variables: stripeAppearance },
|
|
3576
|
+
locale
|
|
3577
|
+
}
|
|
3578
|
+
}, /* @__PURE__ */ react.default.createElement(ValidateStripeUtils, null, props.children));
|
|
3579
|
+
return /* @__PURE__ */ react.default.createElement(DummyStripeUtils, null, props.children);
|
|
3580
|
+
};
|
|
3581
|
+
const PaymentElement = ({ fallback }) => {
|
|
3582
|
+
const { setIsPaymentElementReady, paymentMethodOrder, checkout, stripe, externalClientSecret, paymentDescription, for: _for } = usePaymentElementContext();
|
|
3583
|
+
const environment = useInternalEnvironment();
|
|
3584
|
+
const applePay = (0, react.useMemo)(() => {
|
|
3585
|
+
if (!checkout || !checkout.totals || !checkout.plan) return;
|
|
3586
|
+
return { recurringPaymentRequest: {
|
|
3587
|
+
paymentDescription: paymentDescription || "",
|
|
3588
|
+
managementURL: _for === "organization" ? environment?.displayConfig.organizationProfileUrl || "" : environment?.displayConfig.userProfileUrl || "",
|
|
3589
|
+
regularBilling: {
|
|
3590
|
+
amount: checkout.totals.totalDueNow?.amount || checkout.totals.grandTotal.amount,
|
|
3591
|
+
label: checkout.plan.name,
|
|
3592
|
+
recurringPaymentIntervalUnit: checkout.planPeriod === "annual" ? "year" : "month"
|
|
3593
|
+
}
|
|
3594
|
+
} };
|
|
3595
|
+
}, [
|
|
3596
|
+
checkout,
|
|
3597
|
+
paymentDescription,
|
|
3598
|
+
_for,
|
|
3599
|
+
environment
|
|
3600
|
+
]);
|
|
3601
|
+
const options = (0, react.useMemo)(() => {
|
|
3602
|
+
return {
|
|
3603
|
+
layout: {
|
|
3604
|
+
type: "tabs",
|
|
3605
|
+
defaultCollapsed: false
|
|
3606
|
+
},
|
|
3607
|
+
paymentMethodOrder,
|
|
3608
|
+
applePay
|
|
3609
|
+
};
|
|
3610
|
+
}, [applePay, paymentMethodOrder]);
|
|
3611
|
+
const onReady = (0, react.useCallback)(() => {
|
|
3612
|
+
setIsPaymentElementReady(true);
|
|
3613
|
+
}, [setIsPaymentElementReady]);
|
|
3614
|
+
if (!stripe || !externalClientSecret) return /* @__PURE__ */ react.default.createElement(react.default.Fragment, null, fallback);
|
|
3615
|
+
return /* @__PURE__ */ react.default.createElement(PaymentElement$1, {
|
|
3616
|
+
fallback,
|
|
3617
|
+
onReady,
|
|
3618
|
+
options
|
|
3619
|
+
});
|
|
3620
|
+
};
|
|
3621
|
+
const throwLibsMissingError = () => {
|
|
3622
|
+
throw new Error("Clerk: Unable to submit, Stripe libraries are not yet loaded. Be sure to check `isFormReady` before calling `submit`.");
|
|
3623
|
+
};
|
|
3624
|
+
const usePaymentElement = () => {
|
|
3625
|
+
const { isPaymentElementReady, initializePaymentMethod } = usePaymentElementContext();
|
|
3626
|
+
const { stripe, elements } = useStripeUtilsContext();
|
|
3627
|
+
const { externalClientSecret } = usePaymentElementContext();
|
|
3628
|
+
const submit = (0, react.useCallback)(async () => {
|
|
3629
|
+
if (!stripe || !elements) return throwLibsMissingError();
|
|
3630
|
+
const { setupIntent, error } = await stripe.confirmSetup({
|
|
3631
|
+
elements,
|
|
3632
|
+
confirmParams: { return_url: window.location.href },
|
|
3633
|
+
redirect: "if_required"
|
|
3634
|
+
});
|
|
3635
|
+
if (error) return {
|
|
3636
|
+
data: null,
|
|
3637
|
+
error: {
|
|
3638
|
+
gateway: "stripe",
|
|
3639
|
+
error: {
|
|
3640
|
+
code: error.code,
|
|
3641
|
+
message: error.message,
|
|
3642
|
+
type: error.type
|
|
3643
|
+
}
|
|
3644
|
+
}
|
|
3645
|
+
};
|
|
3646
|
+
return {
|
|
3647
|
+
data: {
|
|
3648
|
+
gateway: "stripe",
|
|
3649
|
+
paymentToken: setupIntent.payment_method
|
|
3650
|
+
},
|
|
3651
|
+
error: null
|
|
3652
|
+
};
|
|
3653
|
+
}, [stripe, elements]);
|
|
3654
|
+
const reset = (0, react.useCallback)(async () => {
|
|
3655
|
+
if (!stripe || !elements) return throwLibsMissingError();
|
|
3656
|
+
await initializePaymentMethod();
|
|
3657
|
+
}, [
|
|
3658
|
+
stripe,
|
|
3659
|
+
elements,
|
|
3660
|
+
initializePaymentMethod
|
|
3661
|
+
]);
|
|
3662
|
+
const isProviderReady = Boolean(stripe && externalClientSecret);
|
|
3663
|
+
if (!isProviderReady) return {
|
|
3664
|
+
submit: throwLibsMissingError,
|
|
3665
|
+
reset: throwLibsMissingError,
|
|
3666
|
+
isFormReady: false,
|
|
3667
|
+
provider: void 0,
|
|
3668
|
+
isProviderReady: false
|
|
3669
|
+
};
|
|
3670
|
+
return {
|
|
3671
|
+
submit,
|
|
3672
|
+
reset,
|
|
3673
|
+
isFormReady: isPaymentElementReady,
|
|
3674
|
+
provider: { name: "stripe" },
|
|
3675
|
+
isProviderReady
|
|
3676
|
+
};
|
|
3677
|
+
};
|
|
3678
|
+
|
|
3679
|
+
//#endregion
|
|
3680
|
+
//#region src/react/PortalProvider.tsx
|
|
3681
|
+
const [PortalContext, , usePortalContextWithoutGuarantee] = createContextAndHook("PortalProvider");
|
|
3682
|
+
/**
|
|
3683
|
+
* UNSAFE_PortalProvider allows you to specify a custom container for Clerk floating UI elements
|
|
3684
|
+
* (popovers, modals, tooltips, etc.) that use portals.
|
|
3685
|
+
*
|
|
3686
|
+
* Only components within this provider will be affected. Components outside the provider
|
|
3687
|
+
* will continue to use the default document.body for portals.
|
|
3688
|
+
*
|
|
3689
|
+
* This is particularly useful when using Clerk components inside external UI libraries
|
|
3690
|
+
* like Radix Dialog or React Aria Components, where portaled elements need to render
|
|
3691
|
+
* within the dialog's container to remain interactable.
|
|
3692
|
+
*
|
|
3693
|
+
* @example
|
|
3694
|
+
* ```tsx
|
|
3695
|
+
* function Example() {
|
|
3696
|
+
* const containerRef = useRef(null);
|
|
3697
|
+
* return (
|
|
3698
|
+
* <RadixDialog ref={containerRef}>
|
|
3699
|
+
* <UNSAFE_PortalProvider getContainer={() => containerRef.current}>
|
|
3700
|
+
* <UserButton />
|
|
3701
|
+
* </UNSAFE_PortalProvider>
|
|
3702
|
+
* </RadixDialog>
|
|
3703
|
+
* );
|
|
3704
|
+
* }
|
|
3705
|
+
* ```
|
|
3706
|
+
*/
|
|
3707
|
+
const UNSAFE_PortalProvider = ({ children, getContainer }) => {
|
|
3708
|
+
const contextValue = react.default.useMemo(() => ({ value: { getContainer } }), [getContainer]);
|
|
3709
|
+
return /* @__PURE__ */ react.default.createElement(PortalContext.Provider, { value: contextValue }, children);
|
|
3710
|
+
};
|
|
3711
|
+
UNSAFE_PortalProvider.displayName = "UNSAFE_PortalProvider";
|
|
3712
|
+
/**
|
|
3713
|
+
* Hook to get the current portal root container.
|
|
3714
|
+
* Returns the getContainer function from context if inside a PortalProvider,
|
|
3715
|
+
* otherwise returns a function that returns null (default behavior).
|
|
3716
|
+
*/
|
|
3717
|
+
const usePortalRoot = () => {
|
|
3718
|
+
const contextValue = usePortalContextWithoutGuarantee();
|
|
3719
|
+
if (contextValue && "getContainer" in contextValue && contextValue.getContainer) return contextValue.getContainer;
|
|
3720
|
+
return () => null;
|
|
3721
|
+
};
|
|
3722
|
+
|
|
3723
|
+
//#endregion
|
|
3724
|
+
exports.ClerkContextProvider = ClerkContextProvider;
|
|
3725
|
+
exports.ClerkInstanceContext = ClerkInstanceContext;
|
|
3726
|
+
exports.InitialStateProvider = InitialStateProvider;
|
|
3727
|
+
exports.OptionsContext = OptionsContext;
|
|
3728
|
+
exports.UNSAFE_PortalProvider = UNSAFE_PortalProvider;
|
|
3729
|
+
exports.__createClerkTestQueryClient = __createClerkTestQueryClient;
|
|
3730
|
+
exports.__experimental_CheckoutProvider = __experimental_CheckoutProvider;
|
|
3731
|
+
exports.__experimental_PaymentElement = PaymentElement;
|
|
3732
|
+
exports.__experimental_PaymentElementProvider = PaymentElementProvider;
|
|
3733
|
+
exports.__experimental_useCheckout = useCheckout;
|
|
3734
|
+
exports.__experimental_usePaymentAttempts = usePaymentAttempts;
|
|
3735
|
+
exports.__experimental_usePaymentElement = usePaymentElement;
|
|
3736
|
+
exports.__experimental_usePaymentMethods = usePaymentMethods;
|
|
3737
|
+
exports.__experimental_usePlans = usePlans;
|
|
3738
|
+
exports.__experimental_useStatements = useStatements;
|
|
3739
|
+
exports.__experimental_useSubscription = useSubscription;
|
|
3740
|
+
exports.__internal_useClientBase = useClientBase;
|
|
3741
|
+
exports.__internal_useCreditBalanceQuery = __internal_useCreditBalanceQuery;
|
|
3742
|
+
exports.__internal_useCreditHistoryQuery = __internal_useCreditHistoryQuery;
|
|
3743
|
+
exports.__internal_useOrganizationBase = useOrganizationBase;
|
|
3744
|
+
exports.__internal_useOrganizationDomains = useOrganizationDomains;
|
|
3745
|
+
exports.__internal_useOrganizationEnterpriseConnectionTestRuns = useOrganizationEnterpriseConnectionTestRuns;
|
|
3746
|
+
exports.__internal_useOrganizationEnterpriseConnections = useOrganizationEnterpriseConnections;
|
|
3747
|
+
exports.__internal_usePaymentAttemptQuery = usePaymentAttemptQuery;
|
|
3748
|
+
exports.__internal_usePlanDetailsQuery = __internal_usePlanDetailsQuery;
|
|
3749
|
+
exports.__internal_useSessionBase = useSessionBase;
|
|
3750
|
+
exports.__internal_useStatementQuery = useStatementQuery;
|
|
3751
|
+
exports.__internal_useUserBase = useUserBase;
|
|
3752
|
+
exports.__internal_useUserEnterpriseConnections = useUserEnterpriseConnections;
|
|
3753
|
+
exports.__resetClerkQueryClientForTest = __resetClerkQueryClientForTest;
|
|
3754
|
+
exports.__setClerkQueryClientForTest = __setClerkQueryClientForTest;
|
|
3755
|
+
exports.assertContextExists = assertContextExists;
|
|
3756
|
+
exports.createContextAndHook = createContextAndHook;
|
|
3757
|
+
exports.getClerkQueryClient = getClerkQueryClient;
|
|
3758
|
+
exports.isDeeplyEqual = isDeeplyEqual;
|
|
3759
|
+
exports.useAPIKeys = useAPIKeys;
|
|
3760
|
+
exports.useAssertWrappedByClerkProvider = useAssertWrappedByClerkProvider;
|
|
3761
|
+
exports.useAttemptToEnableOrganizations = useAttemptToEnableOrganizations;
|
|
3762
|
+
exports.useClerk = useClerk;
|
|
3763
|
+
exports.useClerkInstanceContext = useClerkInstanceContext;
|
|
3764
|
+
exports.useDeepEqualMemo = useDeepEqualMemo;
|
|
3765
|
+
exports.useInitialStateContext = useInitialStateContext;
|
|
3766
|
+
exports.useOAuthConsent = useOAuthConsent;
|
|
3767
|
+
exports.useOptionsContext = useOptionsContext;
|
|
3768
|
+
exports.useOrganization = useOrganization;
|
|
3769
|
+
exports.useOrganizationCreationDefaults = useOrganizationCreationDefaults;
|
|
3770
|
+
exports.useOrganizationList = useOrganizationList;
|
|
3771
|
+
exports.usePortalRoot = usePortalRoot;
|
|
3772
|
+
exports.useReverification = useReverification;
|
|
3773
|
+
exports.useSafeLayoutEffect = useSafeLayoutEffect;
|
|
3774
|
+
exports.useSession = useSession;
|
|
3775
|
+
exports.useSessionList = useSessionList;
|
|
3776
|
+
exports.useUser = useUser;
|
|
3777
|
+
//# sourceMappingURL=index.js.map
|