@stytch/nextjs 21.4.4 → 21.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @stytch/nextjs
2
2
 
3
+ ## 21.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 1e9ab0e: Add a new `assumeHydrated` prop to the `StytchProvider` and `StytchB2BProvider` components. This prop allows the provider to assume whether or not the app might be hydrated in a browser environment after initially being rendered in a server environment. When `true`, the provider eagerly reads from the browser's local cache to retrieve cached session and related data. When `false`, the provider defers this initialization until after the first render, to avoid hydration errors. This prop defaults to `false` in `@stytch/nextjs`, which preserves the behavior from previous versions.
8
+
9
+ We recommend setting `assumeHydrated={false}` if your app hydrates server-rendered content on the client. Otherwise, it is safe to set `assumeHydrated={true}`. If you're unsure or notice hydration errors in your app, set `assumeHydrated={false}`.
10
+
11
+ Unless you set `assumeHydrated={true}`, you should continue to check the initialized state of the provider by checking the `isInitialized` value (returned from hooks like `useStytchUser`, `useStytchSession`, `useStytchMember`, `useStytchMemberSession`, `useStytchOrganization`, and `useStytchIsAuthorized`) or `*IsInitialized` prop (injected by HOCs like `withStytchUser`, `withStytchSession`, `withStytchMember`, `withStytchMemberSession`, and `withStytchOrganization`) before relying on any other values.
12
+
3
13
  ## 21.4.4
4
14
 
5
15
  ### Patch Changes
@@ -1,15 +1,15 @@
1
1
  /// <reference types="react" />
2
2
  import React from 'react';
3
3
  import { ReactNode } from "react";
4
+ import { PermissionsMap } from '@stytch/core/public';
4
5
  import { Member, MemberSession, Organization, StytchB2BUIClient } from '@stytch/vanilla-js/b2b';
5
6
  import { StytchB2BHeadlessClient, StytchProjectConfigurationInput } from '@stytch/vanilla-js/b2b/headless';
6
- import { PermissionsMap } from '@stytch/core/public';
7
7
  /**
8
8
  * The Stytch Client object passed in to <StytchB2BProvider /> in your application root.
9
9
  * Either a StytchB2BUIClient or StytchB2BHeadlessClient.
10
10
  */
11
11
  type StytchB2BClient<TProjectConfiguration extends StytchProjectConfigurationInput> = StytchB2BHeadlessClient<TProjectConfiguration> | StytchB2BUIClient<TProjectConfiguration>;
12
- type SWRMember = {
12
+ type SWRMemberUninitialized = {
13
13
  /**
14
14
  * Either the active {@link Member} object, or null if the member is not logged in.
15
15
  */
@@ -22,7 +22,8 @@ type SWRMember = {
22
22
  * If true, indicates that the SDK has completed initialization.
23
23
  */
24
24
  isInitialized: false;
25
- } | {
25
+ };
26
+ type SWRMemberInitialized = {
26
27
  /**
27
28
  * Either the active {@link Member} object, or null if the member is not logged in.
28
29
  */
@@ -36,7 +37,8 @@ type SWRMember = {
36
37
  */
37
38
  isInitialized: true;
38
39
  };
39
- type SWRMemberSession = {
40
+ type SWRMember<TAlwaysInitialized extends boolean = boolean> = TAlwaysInitialized extends true ? SWRMemberInitialized : SWRMemberInitialized | SWRMemberUninitialized;
41
+ type SWRMemberSessionUninitialized = {
40
42
  /**
41
43
  * Either the active {@link MemberSession} object, or null if the member is not logged in.
42
44
  */
@@ -49,7 +51,8 @@ type SWRMemberSession = {
49
51
  * If true, indicates that the SDK has completed initialization.
50
52
  */
51
53
  isInitialized: false;
52
- } | {
54
+ };
55
+ type SWRMemberSessionInitialized = {
53
56
  /**
54
57
  * Either the active {@link MemberSession} object, or null if the member is not logged in.
55
58
  */
@@ -63,7 +66,8 @@ type SWRMemberSession = {
63
66
  */
64
67
  isInitialized: true;
65
68
  };
66
- type SWROrganization = {
69
+ type SWRMemberSession<TAlwaysInitialized extends boolean = boolean> = TAlwaysInitialized extends true ? SWRMemberSessionInitialized : SWRMemberSessionInitialized | SWRMemberSessionUninitialized;
70
+ type SWROrganizationUninitialized = {
67
71
  /**
68
72
  * Either the active {@link Organization} object, or null if the member is not logged in.
69
73
  */
@@ -76,7 +80,8 @@ type SWROrganization = {
76
80
  * If true, indicates that the SDK has completed initialization.
77
81
  */
78
82
  isInitialized: false;
79
- } | {
83
+ };
84
+ type SWROrganizationInitialized = {
80
85
  /**
81
86
  * Either the active {@link Organization} object, or null if the member is not logged in.
82
87
  */
@@ -90,6 +95,7 @@ type SWROrganization = {
90
95
  */
91
96
  isInitialized: true;
92
97
  };
98
+ type SWROrganization<TAlwaysInitialized extends boolean = boolean> = TAlwaysInitialized extends true ? SWROrganizationInitialized : SWROrganizationInitialized | SWROrganizationUninitialized;
93
99
  declare const useIsMounted__INTERNAL: () => boolean;
94
100
  declare const isUIClient: <TProjectConfiguration extends Partial<import("@stytch/core/public").StytchProjectConfiguration>>(client: StytchB2BClient<TProjectConfiguration>) => client is StytchB2BUIClient<TProjectConfiguration>;
95
101
  /**
@@ -104,7 +110,7 @@ declare const isUIClient: <TProjectConfiguration extends Partial<import("@stytch
104
110
  * }
105
111
  * return (<h1>Welcome, {member.name}</h1>);
106
112
  */
107
- declare const useStytchMember: () => SWRMember;
113
+ declare const useStytchMember: <TAssumeHydrated extends boolean = false>() => SWRMember<TAssumeHydrated>;
108
114
  /**
109
115
  * Returns the active member's Stytch member session.
110
116
  * The Stytch SDKs are used for client-side authentication and session management.
@@ -121,7 +127,7 @@ declare const useStytchMember: () => SWRMember;
121
127
  * }
122
128
  * }, [session, isInitialized]);
123
129
  */
124
- declare const useStytchMemberSession: () => SWRMemberSession;
130
+ declare const useStytchMemberSession: <TAssumeHydrated extends boolean = false>() => SWRMemberSession<TAssumeHydrated>;
125
131
  /**
126
132
  * Returns the active Stytch organization.
127
133
  * The Stytch SDKs are used for client-side authentication and session management.
@@ -134,8 +140,22 @@ declare const useStytchMemberSession: () => SWRMemberSession;
134
140
  * }
135
141
  * return (<p>Welcome to {organization.organization_name}</p>);
136
142
  */
137
- declare const useStytchOrganization: () => SWROrganization;
138
- type SWRIsAuthorized = {
143
+ declare const useStytchOrganization: <TAssumeHydrated extends boolean = false>() => SWROrganization<TAssumeHydrated>;
144
+ type SWRIsAuthorizedUninitialized = {
145
+ /**
146
+ * Whether the logged-in member is allowed to perform the specified action on the specified resource.
147
+ */
148
+ isAuthorized: false;
149
+ /**
150
+ * If true, indicates that the value returned is from the application cache and a state refresh is in progress.
151
+ */
152
+ fromCache: false;
153
+ /**
154
+ * If true, indicates that the SDK has completed initialization.
155
+ */
156
+ isInitialized: false;
157
+ };
158
+ type SWRIsAuthorizedInitialized = {
139
159
  /**
140
160
  * Whether the logged-in member is allowed to perform the specified action on the specified resource.
141
161
  */
@@ -149,6 +169,7 @@ type SWRIsAuthorized = {
149
169
  */
150
170
  isInitialized: boolean;
151
171
  };
172
+ type SWRIsAuthorized<TAlwaysInitialized extends boolean> = TAlwaysInitialized extends true ? SWRIsAuthorizedInitialized : SWRIsAuthorizedInitialized | SWRIsAuthorizedUninitialized;
152
173
  /**
153
174
  * Determines whether the logged-in member is allowed to perform the specified action on the specified resource.
154
175
  * Returns `true` if the member can perform the action, `false` otherwise.
@@ -158,10 +179,10 @@ type SWRIsAuthorized = {
158
179
  *
159
180
  * Remember - authorization checks for sensitive actions should always occur on the backend as well.
160
181
  * @example
161
- * const isAuthorized = useStytchIsAuthorized<Permissions>('documents', 'edit');
182
+ * const { isAuthorized } = useStytchIsAuthorized<Permissions>('documents', 'edit');
162
183
  * return <button disabled={!isAuthorized}>Edit</button>
163
184
  */
164
- declare const useStytchIsAuthorized: (resourceId: string, action: string) => SWRIsAuthorized;
185
+ declare const useStytchIsAuthorized: <TAssumeHydrated extends boolean = false>(resourceId: string, action: string) => SWRIsAuthorized<TAssumeHydrated>;
165
186
  /**
166
187
  * Returns the Stytch B2B client stored in the Stytch context.
167
188
  *
@@ -175,17 +196,17 @@ declare const useStytchB2BClient: <TProjectConfiguration extends Partial<import(
175
196
  declare const withStytchB2BClient: <T extends object, TProjectConfiguration extends Partial<import("@stytch/core/public").StytchProjectConfiguration>>(Component: React.ComponentType<T & {
176
197
  stytch: StytchB2BHeadlessClient<TProjectConfiguration>;
177
198
  }>) => React.ComponentType<T>;
178
- declare const withStytchMember: <T extends object>(Component: React.ComponentType<T & {
199
+ declare const withStytchMember: <T extends object, TAssumeHydrated extends boolean = false>(Component: React.ComponentType<T & {
179
200
  stytchMember: Member | null;
180
201
  stytchMemberIsInitialized: boolean;
181
202
  stytchMemberIsFromCache: boolean;
182
203
  }>) => React.ComponentType<T>;
183
- declare const withStytchMemberSession: <T extends object>(Component: React.ComponentType<T & {
204
+ declare const withStytchMemberSession: <T extends object, TAssumeHydrated extends boolean = false>(Component: React.ComponentType<T & {
184
205
  stytchMemberSession: MemberSession | null;
185
206
  stytchMemberSessionIsInitialized: boolean;
186
207
  stytchMemberSessionIsFromCache: boolean;
187
208
  }>) => React.ComponentType<T>;
188
- declare const withStytchOrganization: <T extends object>(Component: React.ComponentType<T & {
209
+ declare const withStytchOrganization: <T extends object, TAssumeHydrated extends boolean = false>(Component: React.ComponentType<T & {
189
210
  stytchOrganization: Organization | null;
190
211
  stytchOrganizationIsInitialized: boolean;
191
212
  stytchOrganizationIsFromCache: boolean;
@@ -219,6 +240,20 @@ interface StytchB2BProviderProps<TProjectConfiguration extends StytchProjectConf
219
240
  * A Stytch client instance, created using either {@link createStytchHeadlessClient} or {@link createStytchUIClient}
220
241
  */
221
242
  stytch: StytchB2BClient<TProjectConfiguration>;
243
+ /**
244
+ * When true, the provider will assume that the component will only be
245
+ * rendered in a browser environment, either in a single-page application or
246
+ * after completing hydration of a server-rendered application. This allows
247
+ * cached values to be retrieved from the browser on the first render, meaning
248
+ * that the `isInitialized` value returned from Stytch hooks will be `true`
249
+ * starting from the first render.
250
+ *
251
+ * When `false`, the provider will defer initialization until after the first
252
+ * render, and `isInitialized` will initially be `false`.
253
+ *
254
+ * This value defaults to `false` in `@stytch/nextjs`.
255
+ */
256
+ assumeHydrated?: boolean;
222
257
  children?: ReactNode;
223
258
  }
224
259
  /**
@@ -233,5 +268,5 @@ interface StytchB2BProviderProps<TProjectConfiguration extends StytchProjectConf
233
268
  * </StytchB2BProvider>
234
269
  * )
235
270
  */
236
- declare const StytchB2BProvider: <TProjectConfiguration extends Partial<import("@stytch/core/public").StytchProjectConfiguration> = Stytch.DefaultProjectConfiguration>({ stytch, children, }: StytchB2BProviderProps<TProjectConfiguration>) => JSX.Element;
271
+ declare const StytchB2BProvider: <TProjectConfiguration extends Partial<import("@stytch/core/public").StytchProjectConfiguration> = Stytch.DefaultProjectConfiguration>({ stytch, children, assumeHydrated, }: StytchB2BProviderProps<TProjectConfiguration>) => JSX.Element;
237
272
  export { useIsMounted__INTERNAL, isUIClient, useStytchMember, useStytchMemberSession, useStytchOrganization, useStytchIsAuthorized, useStytchB2BClient, withStytchB2BClient, withStytchMember, withStytchMemberSession, withStytchOrganization, withStytchPermissions, StytchB2BProviderProps, StytchB2BProvider };
@@ -1,6 +1,6 @@
1
- import React, { createContext, useContext, useEffect, useMemo } from 'react';
1
+ import React, { createContext, useContext, useEffect, useMemo, useCallback, useRef } from 'react';
2
2
  import { i as invariant, u as useAsyncState, m as mergeWithStableProps } from './useIsomorphicLayoutEffect-1babb81e.js';
3
- import { i as isStytchSSRProxy, n as noProviderError } from './StytchSSRProxy-4e34fb34.js';
3
+ import { i as isStytchSSRProxy, n as noProviderError } from './StytchSSRProxy-34c789b5.js';
4
4
 
5
5
  const initialMember = {
6
6
  member: null,
@@ -86,7 +86,7 @@ const useStytchOrganization = () => {
86
86
  *
87
87
  * Remember - authorization checks for sensitive actions should always occur on the backend as well.
88
88
  * @example
89
- * const isAuthorized = useStytchIsAuthorized<Permissions>('documents', 'edit');
89
+ * const { isAuthorized } = useStytchIsAuthorized<Permissions>('documents', 'edit');
90
90
  * return <button disabled={!isAuthorized}>Edit</button>
91
91
  */
92
92
  const useStytchIsAuthorized = (resourceId, action) => {
@@ -219,33 +219,40 @@ const withStytchPermissions = (Component) => {
219
219
  * </StytchB2BProvider>
220
220
  * )
221
221
  */
222
- const StytchB2BProvider = ({ stytch, children, }) => {
222
+ const StytchB2BProvider = ({ stytch, children, assumeHydrated = false, }) => {
223
+ invariant(!useIsMounted__INTERNAL(), 'You cannot render a <StytchB2BProvider> inside another <StytchB2BProvider>.');
224
+ invariant(!assumeHydrated || typeof window !== 'undefined', 'The `assumeHydrated` prop must be set to `false` when using StytchB2BProvider in a server environment.');
223
225
  const ctx = useMemo(() => ({ client: stytch, isMounted: true }), [stytch]);
224
- const [{ member, session, organization }, setClientState] = useAsyncState({
225
- member: initialMember,
226
- session: initialMemberSession,
227
- organization: initialOrganization,
228
- });
226
+ const getHydratedState = useCallback(() => {
227
+ return {
228
+ member: Object.assign(Object.assign({}, stytch.self.getInfo()), { isInitialized: true }),
229
+ session: Object.assign(Object.assign({}, stytch.session.getInfo()), { isInitialized: true }),
230
+ organization: Object.assign(Object.assign({}, stytch.organization.getInfo()), { isInitialized: true }),
231
+ };
232
+ }, [stytch]);
233
+ const getInitialState = () => {
234
+ return {
235
+ member: initialMember,
236
+ session: initialMemberSession,
237
+ organization: initialOrganization,
238
+ };
239
+ };
240
+ const [{ member, session, organization }, setClientState] = useAsyncState(() => assumeHydrated ? getHydratedState() : getInitialState());
241
+ // Store the initial value of `assumeHydrated` in a ref, because it is
242
+ // logically only relevant for the first render
243
+ const assumeHydratedRef = useRef(assumeHydrated);
229
244
  useEffect(() => {
230
245
  if (isStytchSSRProxy(stytch)) {
231
246
  return;
232
247
  }
233
- setClientState({
234
- member: Object.assign(Object.assign({}, stytch.self.getInfo()), { isInitialized: true }),
235
- session: Object.assign(Object.assign({}, stytch.session.getInfo()), { isInitialized: true }),
236
- organization: Object.assign(Object.assign({}, stytch.organization.getInfo()), { isInitialized: true }),
237
- });
238
- return stytch.onStateChange(() => {
239
- setClientState((oldState) => {
240
- const newState = {
241
- member: Object.assign(Object.assign({}, stytch.self.getInfo()), { isInitialized: true }),
242
- session: Object.assign(Object.assign({}, stytch.session.getInfo()), { isInitialized: true }),
243
- organization: Object.assign(Object.assign({}, stytch.organization.getInfo()), { isInitialized: true }),
244
- };
245
- return mergeWithStableProps(oldState, newState);
246
- });
247
- });
248
- }, [setClientState, stytch]);
248
+ const updateState = () => {
249
+ setClientState((oldState) => mergeWithStableProps(oldState, getHydratedState()));
250
+ };
251
+ if (!assumeHydratedRef.current) {
252
+ updateState();
253
+ }
254
+ return stytch.onStateChange(updateState);
255
+ }, [getHydratedState, setClientState, stytch]);
249
256
  return (React.createElement(StytchB2BContext.Provider, { value: ctx },
250
257
  React.createElement(StytchOrganizationContext.Provider, { value: organization },
251
258
  React.createElement(StytchMemberContext.Provider, { value: member },
@@ -1,15 +1,15 @@
1
1
  /// <reference types="react" />
2
2
  import React from 'react';
3
3
  import { ReactNode } from "react";
4
+ import { PermissionsMap } from '@stytch/core/public';
4
5
  import { Member, MemberSession, Organization, StytchB2BUIClient } from '@stytch/vanilla-js/b2b';
5
6
  import { StytchB2BHeadlessClient, StytchProjectConfigurationInput } from '@stytch/vanilla-js/b2b/headless';
6
- import { PermissionsMap } from '@stytch/core/public';
7
7
  /**
8
8
  * The Stytch Client object passed in to <StytchB2BProvider /> in your application root.
9
9
  * Either a StytchB2BUIClient or StytchB2BHeadlessClient.
10
10
  */
11
11
  type StytchB2BClient<TProjectConfiguration extends StytchProjectConfigurationInput> = StytchB2BHeadlessClient<TProjectConfiguration> | StytchB2BUIClient<TProjectConfiguration>;
12
- type SWRMember = {
12
+ type SWRMemberUninitialized = {
13
13
  /**
14
14
  * Either the active {@link Member} object, or null if the member is not logged in.
15
15
  */
@@ -22,7 +22,8 @@ type SWRMember = {
22
22
  * If true, indicates that the SDK has completed initialization.
23
23
  */
24
24
  isInitialized: false;
25
- } | {
25
+ };
26
+ type SWRMemberInitialized = {
26
27
  /**
27
28
  * Either the active {@link Member} object, or null if the member is not logged in.
28
29
  */
@@ -36,7 +37,8 @@ type SWRMember = {
36
37
  */
37
38
  isInitialized: true;
38
39
  };
39
- type SWRMemberSession = {
40
+ type SWRMember<TAlwaysInitialized extends boolean = boolean> = TAlwaysInitialized extends true ? SWRMemberInitialized : SWRMemberInitialized | SWRMemberUninitialized;
41
+ type SWRMemberSessionUninitialized = {
40
42
  /**
41
43
  * Either the active {@link MemberSession} object, or null if the member is not logged in.
42
44
  */
@@ -49,7 +51,8 @@ type SWRMemberSession = {
49
51
  * If true, indicates that the SDK has completed initialization.
50
52
  */
51
53
  isInitialized: false;
52
- } | {
54
+ };
55
+ type SWRMemberSessionInitialized = {
53
56
  /**
54
57
  * Either the active {@link MemberSession} object, or null if the member is not logged in.
55
58
  */
@@ -63,7 +66,8 @@ type SWRMemberSession = {
63
66
  */
64
67
  isInitialized: true;
65
68
  };
66
- type SWROrganization = {
69
+ type SWRMemberSession<TAlwaysInitialized extends boolean = boolean> = TAlwaysInitialized extends true ? SWRMemberSessionInitialized : SWRMemberSessionInitialized | SWRMemberSessionUninitialized;
70
+ type SWROrganizationUninitialized = {
67
71
  /**
68
72
  * Either the active {@link Organization} object, or null if the member is not logged in.
69
73
  */
@@ -76,7 +80,8 @@ type SWROrganization = {
76
80
  * If true, indicates that the SDK has completed initialization.
77
81
  */
78
82
  isInitialized: false;
79
- } | {
83
+ };
84
+ type SWROrganizationInitialized = {
80
85
  /**
81
86
  * Either the active {@link Organization} object, or null if the member is not logged in.
82
87
  */
@@ -90,6 +95,7 @@ type SWROrganization = {
90
95
  */
91
96
  isInitialized: true;
92
97
  };
98
+ type SWROrganization<TAlwaysInitialized extends boolean = boolean> = TAlwaysInitialized extends true ? SWROrganizationInitialized : SWROrganizationInitialized | SWROrganizationUninitialized;
93
99
  declare const useIsMounted__INTERNAL: () => boolean;
94
100
  declare const isUIClient: <TProjectConfiguration extends Partial<import("@stytch/core/public").StytchProjectConfiguration>>(client: StytchB2BClient<TProjectConfiguration>) => client is StytchB2BUIClient<TProjectConfiguration>;
95
101
  /**
@@ -104,7 +110,7 @@ declare const isUIClient: <TProjectConfiguration extends Partial<import("@stytch
104
110
  * }
105
111
  * return (<h1>Welcome, {member.name}</h1>);
106
112
  */
107
- declare const useStytchMember: () => SWRMember;
113
+ declare const useStytchMember: <TAssumeHydrated extends boolean = false>() => SWRMember<TAssumeHydrated>;
108
114
  /**
109
115
  * Returns the active member's Stytch member session.
110
116
  * The Stytch SDKs are used for client-side authentication and session management.
@@ -121,7 +127,7 @@ declare const useStytchMember: () => SWRMember;
121
127
  * }
122
128
  * }, [session, isInitialized]);
123
129
  */
124
- declare const useStytchMemberSession: () => SWRMemberSession;
130
+ declare const useStytchMemberSession: <TAssumeHydrated extends boolean = false>() => SWRMemberSession<TAssumeHydrated>;
125
131
  /**
126
132
  * Returns the active Stytch organization.
127
133
  * The Stytch SDKs are used for client-side authentication and session management.
@@ -134,8 +140,22 @@ declare const useStytchMemberSession: () => SWRMemberSession;
134
140
  * }
135
141
  * return (<p>Welcome to {organization.organization_name}</p>);
136
142
  */
137
- declare const useStytchOrganization: () => SWROrganization;
138
- type SWRIsAuthorized = {
143
+ declare const useStytchOrganization: <TAssumeHydrated extends boolean = false>() => SWROrganization<TAssumeHydrated>;
144
+ type SWRIsAuthorizedUninitialized = {
145
+ /**
146
+ * Whether the logged-in member is allowed to perform the specified action on the specified resource.
147
+ */
148
+ isAuthorized: false;
149
+ /**
150
+ * If true, indicates that the value returned is from the application cache and a state refresh is in progress.
151
+ */
152
+ fromCache: false;
153
+ /**
154
+ * If true, indicates that the SDK has completed initialization.
155
+ */
156
+ isInitialized: false;
157
+ };
158
+ type SWRIsAuthorizedInitialized = {
139
159
  /**
140
160
  * Whether the logged-in member is allowed to perform the specified action on the specified resource.
141
161
  */
@@ -149,6 +169,7 @@ type SWRIsAuthorized = {
149
169
  */
150
170
  isInitialized: boolean;
151
171
  };
172
+ type SWRIsAuthorized<TAlwaysInitialized extends boolean> = TAlwaysInitialized extends true ? SWRIsAuthorizedInitialized : SWRIsAuthorizedInitialized | SWRIsAuthorizedUninitialized;
152
173
  /**
153
174
  * Determines whether the logged-in member is allowed to perform the specified action on the specified resource.
154
175
  * Returns `true` if the member can perform the action, `false` otherwise.
@@ -158,10 +179,10 @@ type SWRIsAuthorized = {
158
179
  *
159
180
  * Remember - authorization checks for sensitive actions should always occur on the backend as well.
160
181
  * @example
161
- * const isAuthorized = useStytchIsAuthorized<Permissions>('documents', 'edit');
182
+ * const { isAuthorized } = useStytchIsAuthorized<Permissions>('documents', 'edit');
162
183
  * return <button disabled={!isAuthorized}>Edit</button>
163
184
  */
164
- declare const useStytchIsAuthorized: (resourceId: string, action: string) => SWRIsAuthorized;
185
+ declare const useStytchIsAuthorized: <TAssumeHydrated extends boolean = false>(resourceId: string, action: string) => SWRIsAuthorized<TAssumeHydrated>;
165
186
  /**
166
187
  * Returns the Stytch B2B client stored in the Stytch context.
167
188
  *
@@ -175,17 +196,17 @@ declare const useStytchB2BClient: <TProjectConfiguration extends Partial<import(
175
196
  declare const withStytchB2BClient: <T extends object, TProjectConfiguration extends Partial<import("@stytch/core/public").StytchProjectConfiguration>>(Component: React.ComponentType<T & {
176
197
  stytch: StytchB2BHeadlessClient<TProjectConfiguration>;
177
198
  }>) => React.ComponentType<T>;
178
- declare const withStytchMember: <T extends object>(Component: React.ComponentType<T & {
199
+ declare const withStytchMember: <T extends object, TAssumeHydrated extends boolean = false>(Component: React.ComponentType<T & {
179
200
  stytchMember: Member | null;
180
201
  stytchMemberIsInitialized: boolean;
181
202
  stytchMemberIsFromCache: boolean;
182
203
  }>) => React.ComponentType<T>;
183
- declare const withStytchMemberSession: <T extends object>(Component: React.ComponentType<T & {
204
+ declare const withStytchMemberSession: <T extends object, TAssumeHydrated extends boolean = false>(Component: React.ComponentType<T & {
184
205
  stytchMemberSession: MemberSession | null;
185
206
  stytchMemberSessionIsInitialized: boolean;
186
207
  stytchMemberSessionIsFromCache: boolean;
187
208
  }>) => React.ComponentType<T>;
188
- declare const withStytchOrganization: <T extends object>(Component: React.ComponentType<T & {
209
+ declare const withStytchOrganization: <T extends object, TAssumeHydrated extends boolean = false>(Component: React.ComponentType<T & {
189
210
  stytchOrganization: Organization | null;
190
211
  stytchOrganizationIsInitialized: boolean;
191
212
  stytchOrganizationIsFromCache: boolean;
@@ -219,6 +240,20 @@ interface StytchB2BProviderProps<TProjectConfiguration extends StytchProjectConf
219
240
  * A Stytch client instance, created using either {@link createStytchHeadlessClient} or {@link createStytchUIClient}
220
241
  */
221
242
  stytch: StytchB2BClient<TProjectConfiguration>;
243
+ /**
244
+ * When true, the provider will assume that the component will only be
245
+ * rendered in a browser environment, either in a single-page application or
246
+ * after completing hydration of a server-rendered application. This allows
247
+ * cached values to be retrieved from the browser on the first render, meaning
248
+ * that the `isInitialized` value returned from Stytch hooks will be `true`
249
+ * starting from the first render.
250
+ *
251
+ * When `false`, the provider will defer initialization until after the first
252
+ * render, and `isInitialized` will initially be `false`.
253
+ *
254
+ * This value defaults to `false` in `@stytch/nextjs`.
255
+ */
256
+ assumeHydrated?: boolean;
222
257
  children?: ReactNode;
223
258
  }
224
259
  /**
@@ -233,5 +268,5 @@ interface StytchB2BProviderProps<TProjectConfiguration extends StytchProjectConf
233
268
  * </StytchB2BProvider>
234
269
  * )
235
270
  */
236
- declare const StytchB2BProvider: <TProjectConfiguration extends Partial<import("@stytch/core/public").StytchProjectConfiguration> = Stytch.DefaultProjectConfiguration>({ stytch, children, }: StytchB2BProviderProps<TProjectConfiguration>) => JSX.Element;
271
+ declare const StytchB2BProvider: <TProjectConfiguration extends Partial<import("@stytch/core/public").StytchProjectConfiguration> = Stytch.DefaultProjectConfiguration>({ stytch, children, assumeHydrated, }: StytchB2BProviderProps<TProjectConfiguration>) => JSX.Element;
237
272
  export { useIsMounted__INTERNAL, isUIClient, useStytchMember, useStytchMemberSession, useStytchOrganization, useStytchIsAuthorized, useStytchB2BClient, withStytchB2BClient, withStytchMember, withStytchMemberSession, withStytchOrganization, withStytchPermissions, StytchB2BProviderProps, StytchB2BProvider };
@@ -2,7 +2,7 @@
2
2
 
3
3
  var React = require('react');
4
4
  var useIsomorphicLayoutEffect = require('./useIsomorphicLayoutEffect-65746ef3.js');
5
- var StytchSSRProxy = require('./StytchSSRProxy-c9d3bc41.js');
5
+ var StytchSSRProxy = require('./StytchSSRProxy-86bc42b3.js');
6
6
 
7
7
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
8
 
@@ -92,7 +92,7 @@ const useStytchOrganization = () => {
92
92
  *
93
93
  * Remember - authorization checks for sensitive actions should always occur on the backend as well.
94
94
  * @example
95
- * const isAuthorized = useStytchIsAuthorized<Permissions>('documents', 'edit');
95
+ * const { isAuthorized } = useStytchIsAuthorized<Permissions>('documents', 'edit');
96
96
  * return <button disabled={!isAuthorized}>Edit</button>
97
97
  */
98
98
  const useStytchIsAuthorized = (resourceId, action) => {
@@ -225,33 +225,40 @@ const withStytchPermissions = (Component) => {
225
225
  * </StytchB2BProvider>
226
226
  * )
227
227
  */
228
- const StytchB2BProvider = ({ stytch, children, }) => {
228
+ const StytchB2BProvider = ({ stytch, children, assumeHydrated = false, }) => {
229
+ useIsomorphicLayoutEffect.invariant(!useIsMounted__INTERNAL(), 'You cannot render a <StytchB2BProvider> inside another <StytchB2BProvider>.');
230
+ useIsomorphicLayoutEffect.invariant(!assumeHydrated || typeof window !== 'undefined', 'The `assumeHydrated` prop must be set to `false` when using StytchB2BProvider in a server environment.');
229
231
  const ctx = React.useMemo(() => ({ client: stytch, isMounted: true }), [stytch]);
230
- const [{ member, session, organization }, setClientState] = useIsomorphicLayoutEffect.useAsyncState({
231
- member: initialMember,
232
- session: initialMemberSession,
233
- organization: initialOrganization,
234
- });
232
+ const getHydratedState = React.useCallback(() => {
233
+ return {
234
+ member: Object.assign(Object.assign({}, stytch.self.getInfo()), { isInitialized: true }),
235
+ session: Object.assign(Object.assign({}, stytch.session.getInfo()), { isInitialized: true }),
236
+ organization: Object.assign(Object.assign({}, stytch.organization.getInfo()), { isInitialized: true }),
237
+ };
238
+ }, [stytch]);
239
+ const getInitialState = () => {
240
+ return {
241
+ member: initialMember,
242
+ session: initialMemberSession,
243
+ organization: initialOrganization,
244
+ };
245
+ };
246
+ const [{ member, session, organization }, setClientState] = useIsomorphicLayoutEffect.useAsyncState(() => assumeHydrated ? getHydratedState() : getInitialState());
247
+ // Store the initial value of `assumeHydrated` in a ref, because it is
248
+ // logically only relevant for the first render
249
+ const assumeHydratedRef = React.useRef(assumeHydrated);
235
250
  React.useEffect(() => {
236
251
  if (StytchSSRProxy.isStytchSSRProxy(stytch)) {
237
252
  return;
238
253
  }
239
- setClientState({
240
- member: Object.assign(Object.assign({}, stytch.self.getInfo()), { isInitialized: true }),
241
- session: Object.assign(Object.assign({}, stytch.session.getInfo()), { isInitialized: true }),
242
- organization: Object.assign(Object.assign({}, stytch.organization.getInfo()), { isInitialized: true }),
243
- });
244
- return stytch.onStateChange(() => {
245
- setClientState((oldState) => {
246
- const newState = {
247
- member: Object.assign(Object.assign({}, stytch.self.getInfo()), { isInitialized: true }),
248
- session: Object.assign(Object.assign({}, stytch.session.getInfo()), { isInitialized: true }),
249
- organization: Object.assign(Object.assign({}, stytch.organization.getInfo()), { isInitialized: true }),
250
- };
251
- return useIsomorphicLayoutEffect.mergeWithStableProps(oldState, newState);
252
- });
253
- });
254
- }, [setClientState, stytch]);
254
+ const updateState = () => {
255
+ setClientState((oldState) => useIsomorphicLayoutEffect.mergeWithStableProps(oldState, getHydratedState()));
256
+ };
257
+ if (!assumeHydratedRef.current) {
258
+ updateState();
259
+ }
260
+ return stytch.onStateChange(updateState);
261
+ }, [getHydratedState, setClientState, stytch]);
255
262
  return (React__default["default"].createElement(StytchB2BContext.Provider, { value: ctx },
256
263
  React__default["default"].createElement(StytchOrganizationContext.Provider, { value: organization },
257
264
  React__default["default"].createElement(StytchMemberContext.Provider, { value: member },
@@ -1,7 +1,8 @@
1
1
  const noProviderError = (item, provider = 'StytchProvider') => `${item} can only be used inside <${provider}>.`;
2
+ const providerMustBeUniqueError = 'You cannot render a <StytchProvider> inside another <StytchProvider>.';
2
3
  const noHeadlessClientError = `Tried to create a Stytch Login UI element using the Stytch Headless SDK.
3
4
  You must use the UI SDK to use UI elements.
4
- Please make sure you are importing createStytchHeadlessClient from @stytch/nextjs/ui and not from @stytch/nextjs/headless.`;
5
+ Please make sure you are using a Stytch UI client, not a Stytch Headless client.`;
5
6
  const cannotInvokeMethodOnServerError = (path) => `[Stytch] Invalid serverside function call to ${path}.
6
7
  The Stytch Javascript SDK is intended to ony be used on the client side.
7
8
  Make sure to wrap your API calls in a hook to ensure they are executed on the client.
@@ -44,4 +45,4 @@ const createProxy = (path) => {
44
45
  };
45
46
  const createStytchSSRProxy = () => createProxy('stytch');
46
47
 
47
- export { noHeadlessClientError as a, createStytchSSRProxy as c, isStytchSSRProxy as i, noProviderError as n };
48
+ export { noHeadlessClientError as a, createStytchSSRProxy as c, isStytchSSRProxy as i, noProviderError as n, providerMustBeUniqueError as p };
@@ -1,9 +1,10 @@
1
1
  'use strict';
2
2
 
3
3
  const noProviderError = (item, provider = 'StytchProvider') => `${item} can only be used inside <${provider}>.`;
4
+ const providerMustBeUniqueError = 'You cannot render a <StytchProvider> inside another <StytchProvider>.';
4
5
  const noHeadlessClientError = `Tried to create a Stytch Login UI element using the Stytch Headless SDK.
5
6
  You must use the UI SDK to use UI elements.
6
- Please make sure you are importing createStytchHeadlessClient from @stytch/nextjs/ui and not from @stytch/nextjs/headless.`;
7
+ Please make sure you are using a Stytch UI client, not a Stytch Headless client.`;
7
8
  const cannotInvokeMethodOnServerError = (path) => `[Stytch] Invalid serverside function call to ${path}.
8
9
  The Stytch Javascript SDK is intended to ony be used on the client side.
9
10
  Make sure to wrap your API calls in a hook to ensure they are executed on the client.
@@ -50,3 +51,4 @@ exports.createStytchSSRProxy = createStytchSSRProxy;
50
51
  exports.isStytchSSRProxy = isStytchSSRProxy;
51
52
  exports.noHeadlessClientError = noHeadlessClientError;
52
53
  exports.noProviderError = noProviderError;
54
+ exports.providerMustBeUniqueError = providerMustBeUniqueError;
@@ -1,8 +1,8 @@
1
1
  import { mountAdminPortalSSO, mountAdminPortalOrgSettings, mountAdminPortalMemberManagement, mountAdminPortalSCIM } from '@stytch/vanilla-js/b2b/adminPortal';
2
2
  export { AdminPortalB2BProducts } from '@stytch/vanilla-js/b2b/adminPortal';
3
3
  import React, { useRef } from 'react';
4
- import { u as useIsMounted__INTERNAL, a as useStytchB2BClient } from '../StytchB2BContext-c11ed503.js';
5
- import { n as noProviderError } from '../StytchSSRProxy-4e34fb34.js';
4
+ import { u as useIsMounted__INTERNAL, a as useStytchB2BClient } from '../StytchB2BContext-54aa990c.js';
5
+ import { n as noProviderError } from '../StytchSSRProxy-34c789b5.js';
6
6
  import { i as invariant, a as useIsomorphicLayoutEffect } from '../useIsomorphicLayoutEffect-1babb81e.js';
7
7
 
8
8
  const makeAdminPortalComponent = (mountFn, componentName) => {
@@ -4,8 +4,8 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var adminPortal = require('@stytch/vanilla-js/b2b/adminPortal');
6
6
  var React = require('react');
7
- var StytchB2BContext = require('../StytchB2BContext-a27d4ac7.js');
8
- var StytchSSRProxy = require('../StytchSSRProxy-c9d3bc41.js');
7
+ var StytchB2BContext = require('../StytchB2BContext-c5062f9b.js');
8
+ var StytchSSRProxy = require('../StytchSSRProxy-86bc42b3.js');
9
9
  var useIsomorphicLayoutEffect = require('../useIsomorphicLayoutEffect-65746ef3.js');
10
10
 
11
11
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
@@ -141,6 +141,6 @@ declare const StytchB2B: <TProjectConfiguration extends Partial<import("@stytch/
141
141
  * />
142
142
  */
143
143
  declare const B2BIdentityProvider: ({ styles, callbacks, getIDPConsentManifest }: B2BIdentityProviderProps) => React.JSX.Element;
144
- export { StytchB2BProvider, useStytchB2BClient, useStytchMemberSession, useStytchMember, useStytchIsAuthorized, useStytchOrganization, withStytchB2BClient, withStytchMemberSession, withStytchMember, withStytchOrganization, withStytchPermissions } from "../StytchB2BContext-a27d4ac7.js";
144
+ export { StytchB2BProvider, useStytchB2BClient, useStytchMemberSession, useStytchMember, useStytchIsAuthorized, useStytchOrganization, withStytchB2BClient, withStytchMemberSession, withStytchMember, withStytchOrganization, withStytchPermissions } from "../StytchB2BContext-c5062f9b.js";
145
145
  export { StytchB2B, B2BIdentityProvider };
146
- export type { StytchB2BProviderProps } from "../StytchB2BContext-a27d4ac7.js";
146
+ export type { StytchB2BProviderProps } from "../StytchB2BContext-c5062f9b.js";
@@ -141,6 +141,6 @@ declare const StytchB2B: <TProjectConfiguration extends Partial<import("@stytch/
141
141
  * />
142
142
  */
143
143
  declare const B2BIdentityProvider: ({ styles, callbacks, getIDPConsentManifest }: B2BIdentityProviderProps) => React.JSX.Element;
144
- export { StytchB2BProvider, useStytchB2BClient, useStytchMemberSession, useStytchMember, useStytchIsAuthorized, useStytchOrganization, withStytchB2BClient, withStytchMemberSession, withStytchMember, withStytchOrganization, withStytchPermissions } from "../StytchB2BContext-c11ed503.js";
144
+ export { StytchB2BProvider, useStytchB2BClient, useStytchMemberSession, useStytchMember, useStytchIsAuthorized, useStytchOrganization, withStytchB2BClient, withStytchMemberSession, withStytchMember, withStytchOrganization, withStytchPermissions } from "../StytchB2BContext-54aa990c.js";
145
145
  export { StytchB2B, B2BIdentityProvider };
146
- export type { StytchB2BProviderProps } from "../StytchB2BContext-c11ed503.js";
146
+ export type { StytchB2BProviderProps } from "../StytchB2BContext-54aa990c.js";
@@ -1,8 +1,8 @@
1
- import { u as useIsMounted__INTERNAL, a as useStytchB2BClient, i as isUIClient, b as useStytchMember } from '../StytchB2BContext-c11ed503.js';
2
- export { S as StytchB2BProvider, a as useStytchB2BClient, d as useStytchIsAuthorized, b as useStytchMember, c as useStytchMemberSession, e as useStytchOrganization, w as withStytchB2BClient, g as withStytchMember, f as withStytchMemberSession, h as withStytchOrganization, j as withStytchPermissions } from '../StytchB2BContext-c11ed503.js';
1
+ import { u as useIsMounted__INTERNAL, a as useStytchB2BClient, i as isUIClient, b as useStytchMember } from '../StytchB2BContext-54aa990c.js';
2
+ export { S as StytchB2BProvider, a as useStytchB2BClient, d as useStytchIsAuthorized, b as useStytchMember, c as useStytchMemberSession, e as useStytchOrganization, w as withStytchB2BClient, g as withStytchMember, f as withStytchMemberSession, h as withStytchOrganization, j as withStytchPermissions } from '../StytchB2BContext-54aa990c.js';
3
3
  import React, { useRef } from 'react';
4
4
  import { i as invariant, a as useIsomorphicLayoutEffect } from '../useIsomorphicLayoutEffect-1babb81e.js';
5
- import { a as noHeadlessClientError, n as noProviderError } from '../StytchSSRProxy-4e34fb34.js';
5
+ import { a as noHeadlessClientError, n as noProviderError } from '../StytchSSRProxy-34c789b5.js';
6
6
 
7
7
  /**
8
8
  * The Stytch B2B UI component.
@@ -1,5 +1,5 @@
1
1
  import { StytchB2BHeadlessClient } from '@stytch/vanilla-js/b2b/headless';
2
- import { c as createStytchSSRProxy } from '../StytchSSRProxy-4e34fb34.js';
2
+ import { c as createStytchSSRProxy } from '../StytchSSRProxy-34c789b5.js';
3
3
 
4
4
  /**
5
5
  * Creates a Headless Stytch client object to call the stytch B2B APIs.
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var headless = require('@stytch/vanilla-js/b2b/headless');
6
- var StytchSSRProxy = require('../StytchSSRProxy-c9d3bc41.js');
6
+ var StytchSSRProxy = require('../StytchSSRProxy-86bc42b3.js');
7
7
 
8
8
  /**
9
9
  * Creates a Headless Stytch client object to call the stytch B2B APIs.
package/dist/b2b/index.js CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var StytchB2BContext = require('../StytchB2BContext-a27d4ac7.js');
5
+ var StytchB2BContext = require('../StytchB2BContext-c5062f9b.js');
6
6
  var React = require('react');
7
7
  var useIsomorphicLayoutEffect = require('../useIsomorphicLayoutEffect-65746ef3.js');
8
- var StytchSSRProxy = require('../StytchSSRProxy-c9d3bc41.js');
8
+ var StytchSSRProxy = require('../StytchSSRProxy-86bc42b3.js');
9
9
 
10
10
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
11
11
 
@@ -1,5 +1,5 @@
1
1
  import { StytchB2BUIClient } from '@stytch/vanilla-js/b2b';
2
- import { c as createStytchSSRProxy } from '../StytchSSRProxy-4e34fb34.js';
2
+ import { c as createStytchSSRProxy } from '../StytchSSRProxy-34c789b5.js';
3
3
 
4
4
  /**
5
5
  * Creates a Stytch UI client object to call the Stytch APIs and render Stytch UI components.
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var b2b = require('@stytch/vanilla-js/b2b');
6
- var StytchSSRProxy = require('../StytchSSRProxy-c9d3bc41.js');
6
+ var StytchSSRProxy = require('../StytchSSRProxy-86bc42b3.js');
7
7
 
8
8
  /**
9
9
  * Creates a Stytch UI client object to call the Stytch APIs and render Stytch UI components.
@@ -0,0 +1,5 @@
1
+ declare const noProviderError: (item: string, provider?: string) => string;
2
+ declare const providerMustBeUniqueError = "You cannot render a <StytchProvider> inside another <StytchProvider>.";
3
+ declare const noHeadlessClientError = "Tried to create a Stytch Login UI element using the Stytch Headless SDK.\nYou must use the UI SDK to use UI elements.\nPlease make sure you are using a Stytch UI client, not a Stytch Headless client.";
4
+ declare const cannotInvokeMethodOnServerError: (path: string) => string;
5
+ export { noProviderError, providerMustBeUniqueError, noHeadlessClientError, cannotInvokeMethodOnServerError };
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@ import { StytchHeadlessClient } from "@stytch/vanilla-js/headless";
8
8
  * Either a StytchUIClient or StytchHeadlessClient.
9
9
  */
10
10
  type StytchClient<TProjectConfiguration extends StytchProjectConfigurationInput> = StytchUIClient<TProjectConfiguration> | StytchHeadlessClient<TProjectConfiguration>;
11
- type SWRUser = {
11
+ type SWRUserUninitialized = {
12
12
  /**
13
13
  * Either the active {@link User} object, or null if the user is not logged in.
14
14
  */
@@ -21,7 +21,8 @@ type SWRUser = {
21
21
  * If true, indicates that the SDK has completed initialization.
22
22
  */
23
23
  isInitialized: false;
24
- } | {
24
+ };
25
+ type SWRUserInitialized = {
25
26
  /**
26
27
  * Either the active {@link User} object, or null if the user is not logged in.
27
28
  */
@@ -35,7 +36,8 @@ type SWRUser = {
35
36
  */
36
37
  isInitialized: true;
37
38
  };
38
- type SWRSession = {
39
+ type SWRUser<TAlwaysInitialized extends boolean = boolean> = TAlwaysInitialized extends true ? SWRUserInitialized : SWRUserInitialized | SWRUserUninitialized;
40
+ type SWRSessionUninitialized = {
39
41
  /**
40
42
  * Either the active {@link Session} object, or null if the user is not logged in.
41
43
  */
@@ -48,7 +50,8 @@ type SWRSession = {
48
50
  * If true, indicates that the SDK has completed initialization.
49
51
  */
50
52
  isInitialized: false;
51
- } | {
53
+ };
54
+ type SWRSessionInitialized = {
52
55
  /**
53
56
  * Either the active {@link Session} object, or null if the user is not logged in.
54
57
  */
@@ -62,6 +65,7 @@ type SWRSession = {
62
65
  */
63
66
  isInitialized: true;
64
67
  };
68
+ type SWRSession<TAlwaysInitialized extends boolean = boolean> = TAlwaysInitialized extends true ? SWRSessionInitialized : SWRSessionInitialized | SWRSessionUninitialized;
65
69
  /**
66
70
  * Returns the active User.
67
71
  * The Stytch SDKs are used for client-side authentication and session management.
@@ -74,7 +78,7 @@ type SWRSession = {
74
78
  * }
75
79
  * return (<h1>Welcome, {user.name.first_name}</h1>);
76
80
  */
77
- declare const useStytchUser: () => SWRUser;
81
+ declare const useStytchUser: <TAssumeHydrated extends boolean = false>() => SWRUser<TAssumeHydrated>;
78
82
  /**
79
83
  * Returns the active user's Stytch session.
80
84
  * The Stytch SDKs are used for client-side authentication and session management.
@@ -91,7 +95,7 @@ declare const useStytchUser: () => SWRUser;
91
95
  * }
92
96
  * }, [session, isInitialized]);
93
97
  */
94
- declare const useStytchSession: () => SWRSession;
98
+ declare const useStytchSession: <TAssumeHydrated extends boolean = false>() => SWRSession<TAssumeHydrated>;
95
99
  /**
96
100
  * Returns the Stytch client stored in the Stytch context.
97
101
  *
@@ -105,12 +109,12 @@ declare const useStytch: <TProjectConfiguration extends Partial<import("@stytch/
105
109
  declare const withStytch: <T extends object, TProjectConfiguration extends Partial<import("@stytch/core/public").StytchProjectConfiguration> = Stytch.DefaultProjectConfiguration>(Component: React.ComponentType<T & {
106
110
  stytch: StytchClient<TProjectConfiguration>;
107
111
  }>) => React.ComponentType<T>;
108
- declare const withStytchUser: <T extends object>(Component: React.ComponentType<T & {
112
+ declare const withStytchUser: <T extends object, TAssumeHydrated extends boolean = false>(Component: React.ComponentType<T & {
109
113
  stytchUser: User | null;
110
114
  stytchUserIsInitialized: boolean;
111
115
  stytchUserIsFromCache: boolean;
112
116
  }>) => React.ComponentType<T>;
113
- declare const withStytchSession: <T extends object>(Component: React.ComponentType<T & {
117
+ declare const withStytchSession: <T extends object, TAssumeHydrated extends boolean = false>(Component: React.ComponentType<T & {
114
118
  stytchSession: Session | null;
115
119
  stytchSessionIsInitialized: boolean;
116
120
  stytchSessionIsFromCache: boolean;
@@ -120,6 +124,20 @@ interface StytchProviderProps<TProjectConfiguration extends StytchProjectConfigu
120
124
  * A Stytch client instance, created using either {@link createStytchHeadlessClient} or {@link createStytchUIClient}
121
125
  */
122
126
  stytch: StytchClient<TProjectConfiguration>;
127
+ /**
128
+ * When true, the provider will assume that the component will only be
129
+ * rendered in a browser environment, either in a single-page application or
130
+ * after completing hydration of a server-rendered application. This allows
131
+ * cached values to be retrieved from the browser on the first render, meaning
132
+ * that the `isInitialized` value returned from Stytch hooks will be `true`
133
+ * starting from the first render.
134
+ *
135
+ * When `false`, the provider will defer initialization until after the first
136
+ * render, and `isInitialized` will initially be `false`.
137
+ *
138
+ * This value defaults to `false` in `@stytch/nextjs`.
139
+ */
140
+ assumeHydrated?: boolean;
123
141
  children?: ReactNode;
124
142
  }
125
143
  /**
@@ -134,7 +152,7 @@ interface StytchProviderProps<TProjectConfiguration extends StytchProjectConfigu
134
152
  * </StytchProvider>
135
153
  * )
136
154
  */
137
- declare const StytchProvider: <TProjectConfiguration extends Partial<import("@stytch/core/public").StytchProjectConfiguration> = Stytch.DefaultProjectConfiguration>({ stytch, children }: StytchProviderProps<TProjectConfiguration>) => JSX.Element;
155
+ declare const StytchProvider: <TProjectConfiguration extends Partial<import("@stytch/core/public").StytchProjectConfiguration> = Stytch.DefaultProjectConfiguration>({ stytch, children, assumeHydrated }: StytchProviderProps<TProjectConfiguration>) => JSX.Element;
138
156
  interface StytchProps<TProjectConfiguration extends StytchProjectConfigurationInput = Stytch.DefaultProjectConfiguration> {
139
157
  /**
140
158
  * A {@link StytchLoginConfig} object. Add products and product-specific config to this object to change the login methods shown.
@@ -321,5 +339,5 @@ declare const StytchPasskeyRegistration: <TProjectConfiguration extends Partial<
321
339
  * />
322
340
  */
323
341
  declare const IdentityProvider: ({ styles, callbacks }: IdentityProviderProps) => React.JSX.Element;
324
- export { StytchProvider, useStytch, useStytchSession, useStytchUser, withStytch, withStytchSession, withStytchUser, StytchLogin, StytchPasswordReset, StytchPasskeyRegistration, IdentityProvider };
325
- export type { StytchProviderProps };
342
+ export { IdentityProvider, StytchLogin, StytchPasskeyRegistration, StytchPasswordReset, StytchProvider, useStytch, useStytchSession, useStytchUser, withStytch, withStytchSession, withStytchUser };
343
+ export type { StytchProviderProps, IdentityProviderProps, StytchProps, StytchResetPasswordProps };
@@ -8,7 +8,7 @@ import { StytchHeadlessClient } from "@stytch/vanilla-js/headless";
8
8
  * Either a StytchUIClient or StytchHeadlessClient.
9
9
  */
10
10
  type StytchClient<TProjectConfiguration extends StytchProjectConfigurationInput> = StytchUIClient<TProjectConfiguration> | StytchHeadlessClient<TProjectConfiguration>;
11
- type SWRUser = {
11
+ type SWRUserUninitialized = {
12
12
  /**
13
13
  * Either the active {@link User} object, or null if the user is not logged in.
14
14
  */
@@ -21,7 +21,8 @@ type SWRUser = {
21
21
  * If true, indicates that the SDK has completed initialization.
22
22
  */
23
23
  isInitialized: false;
24
- } | {
24
+ };
25
+ type SWRUserInitialized = {
25
26
  /**
26
27
  * Either the active {@link User} object, or null if the user is not logged in.
27
28
  */
@@ -35,7 +36,8 @@ type SWRUser = {
35
36
  */
36
37
  isInitialized: true;
37
38
  };
38
- type SWRSession = {
39
+ type SWRUser<TAlwaysInitialized extends boolean = boolean> = TAlwaysInitialized extends true ? SWRUserInitialized : SWRUserInitialized | SWRUserUninitialized;
40
+ type SWRSessionUninitialized = {
39
41
  /**
40
42
  * Either the active {@link Session} object, or null if the user is not logged in.
41
43
  */
@@ -48,7 +50,8 @@ type SWRSession = {
48
50
  * If true, indicates that the SDK has completed initialization.
49
51
  */
50
52
  isInitialized: false;
51
- } | {
53
+ };
54
+ type SWRSessionInitialized = {
52
55
  /**
53
56
  * Either the active {@link Session} object, or null if the user is not logged in.
54
57
  */
@@ -62,6 +65,7 @@ type SWRSession = {
62
65
  */
63
66
  isInitialized: true;
64
67
  };
68
+ type SWRSession<TAlwaysInitialized extends boolean = boolean> = TAlwaysInitialized extends true ? SWRSessionInitialized : SWRSessionInitialized | SWRSessionUninitialized;
65
69
  /**
66
70
  * Returns the active User.
67
71
  * The Stytch SDKs are used for client-side authentication and session management.
@@ -74,7 +78,7 @@ type SWRSession = {
74
78
  * }
75
79
  * return (<h1>Welcome, {user.name.first_name}</h1>);
76
80
  */
77
- declare const useStytchUser: () => SWRUser;
81
+ declare const useStytchUser: <TAssumeHydrated extends boolean = false>() => SWRUser<TAssumeHydrated>;
78
82
  /**
79
83
  * Returns the active user's Stytch session.
80
84
  * The Stytch SDKs are used for client-side authentication and session management.
@@ -91,7 +95,7 @@ declare const useStytchUser: () => SWRUser;
91
95
  * }
92
96
  * }, [session, isInitialized]);
93
97
  */
94
- declare const useStytchSession: () => SWRSession;
98
+ declare const useStytchSession: <TAssumeHydrated extends boolean = false>() => SWRSession<TAssumeHydrated>;
95
99
  /**
96
100
  * Returns the Stytch client stored in the Stytch context.
97
101
  *
@@ -105,12 +109,12 @@ declare const useStytch: <TProjectConfiguration extends Partial<import("@stytch/
105
109
  declare const withStytch: <T extends object, TProjectConfiguration extends Partial<import("@stytch/core/public").StytchProjectConfiguration> = Stytch.DefaultProjectConfiguration>(Component: React.ComponentType<T & {
106
110
  stytch: StytchClient<TProjectConfiguration>;
107
111
  }>) => React.ComponentType<T>;
108
- declare const withStytchUser: <T extends object>(Component: React.ComponentType<T & {
112
+ declare const withStytchUser: <T extends object, TAssumeHydrated extends boolean = false>(Component: React.ComponentType<T & {
109
113
  stytchUser: User | null;
110
114
  stytchUserIsInitialized: boolean;
111
115
  stytchUserIsFromCache: boolean;
112
116
  }>) => React.ComponentType<T>;
113
- declare const withStytchSession: <T extends object>(Component: React.ComponentType<T & {
117
+ declare const withStytchSession: <T extends object, TAssumeHydrated extends boolean = false>(Component: React.ComponentType<T & {
114
118
  stytchSession: Session | null;
115
119
  stytchSessionIsInitialized: boolean;
116
120
  stytchSessionIsFromCache: boolean;
@@ -120,6 +124,20 @@ interface StytchProviderProps<TProjectConfiguration extends StytchProjectConfigu
120
124
  * A Stytch client instance, created using either {@link createStytchHeadlessClient} or {@link createStytchUIClient}
121
125
  */
122
126
  stytch: StytchClient<TProjectConfiguration>;
127
+ /**
128
+ * When true, the provider will assume that the component will only be
129
+ * rendered in a browser environment, either in a single-page application or
130
+ * after completing hydration of a server-rendered application. This allows
131
+ * cached values to be retrieved from the browser on the first render, meaning
132
+ * that the `isInitialized` value returned from Stytch hooks will be `true`
133
+ * starting from the first render.
134
+ *
135
+ * When `false`, the provider will defer initialization until after the first
136
+ * render, and `isInitialized` will initially be `false`.
137
+ *
138
+ * This value defaults to `false` in `@stytch/nextjs`.
139
+ */
140
+ assumeHydrated?: boolean;
123
141
  children?: ReactNode;
124
142
  }
125
143
  /**
@@ -134,7 +152,7 @@ interface StytchProviderProps<TProjectConfiguration extends StytchProjectConfigu
134
152
  * </StytchProvider>
135
153
  * )
136
154
  */
137
- declare const StytchProvider: <TProjectConfiguration extends Partial<import("@stytch/core/public").StytchProjectConfiguration> = Stytch.DefaultProjectConfiguration>({ stytch, children }: StytchProviderProps<TProjectConfiguration>) => JSX.Element;
155
+ declare const StytchProvider: <TProjectConfiguration extends Partial<import("@stytch/core/public").StytchProjectConfiguration> = Stytch.DefaultProjectConfiguration>({ stytch, children, assumeHydrated }: StytchProviderProps<TProjectConfiguration>) => JSX.Element;
138
156
  interface StytchProps<TProjectConfiguration extends StytchProjectConfigurationInput = Stytch.DefaultProjectConfiguration> {
139
157
  /**
140
158
  * A {@link StytchLoginConfig} object. Add products and product-specific config to this object to change the login methods shown.
@@ -321,5 +339,5 @@ declare const StytchPasskeyRegistration: <TProjectConfiguration extends Partial<
321
339
  * />
322
340
  */
323
341
  declare const IdentityProvider: ({ styles, callbacks }: IdentityProviderProps) => React.JSX.Element;
324
- export { StytchProvider, useStytch, useStytchSession, useStytchUser, withStytch, withStytchSession, withStytchUser, StytchLogin, StytchPasswordReset, StytchPasskeyRegistration, IdentityProvider };
325
- export type { StytchProviderProps };
342
+ export { IdentityProvider, StytchLogin, StytchPasskeyRegistration, StytchPasswordReset, StytchProvider, useStytch, useStytchSession, useStytchUser, withStytch, withStytchSession, withStytchUser };
343
+ export type { StytchProviderProps, IdentityProviderProps, StytchProps, StytchResetPasswordProps };
package/dist/index.esm.js CHANGED
@@ -1,6 +1,6 @@
1
- import React, { createContext, useContext, useMemo, useEffect, useRef } from 'react';
1
+ import React, { createContext, useContext, useMemo, useCallback, useRef, useEffect } from 'react';
2
2
  import { i as invariant, u as useAsyncState, m as mergeWithStableProps, a as useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect-1babb81e.js';
3
- import { i as isStytchSSRProxy, n as noProviderError, a as noHeadlessClientError } from './StytchSSRProxy-4e34fb34.js';
3
+ import { i as isStytchSSRProxy, n as noProviderError, p as providerMustBeUniqueError, a as noHeadlessClientError } from './StytchSSRProxy-34c789b5.js';
4
4
 
5
5
  const initialUser = {
6
6
  user: null,
@@ -107,30 +107,38 @@ const withStytchSession = (Component) => {
107
107
  * </StytchProvider>
108
108
  * )
109
109
  */
110
- const StytchProvider = ({ stytch, children, }) => {
110
+ const StytchProvider = ({ stytch, children, assumeHydrated = false, }) => {
111
+ invariant(!useIsMounted__INTERNAL(), providerMustBeUniqueError);
112
+ invariant(!assumeHydrated || typeof window !== 'undefined', 'The `assumeHydrated` prop must be set to `false` when using StytchProvider in a server environment.');
111
113
  const ctx = useMemo(() => ({ client: stytch, isMounted: true }), [stytch]);
112
- const [{ user, session }, setClientState] = useAsyncState({
113
- session: initialSession,
114
- user: initialUser,
115
- });
114
+ const getHydratedState = useCallback(() => {
115
+ return {
116
+ session: Object.assign(Object.assign({}, stytch.session.getInfo()), { isInitialized: true }),
117
+ user: Object.assign(Object.assign({}, stytch.user.getInfo()), { isInitialized: true }),
118
+ };
119
+ }, [stytch]);
120
+ const getInitialState = () => {
121
+ return {
122
+ session: initialSession,
123
+ user: initialUser,
124
+ };
125
+ };
126
+ const [{ user, session }, setClientState] = useAsyncState(() => assumeHydrated ? getHydratedState() : getInitialState());
127
+ // Store the initial value of `assumeHydrated` in a ref, because it is
128
+ // logically only relevant for the first render
129
+ const assumeHydratedRef = useRef(assumeHydrated);
116
130
  useEffect(() => {
117
131
  if (isStytchSSRProxy(stytch)) {
118
132
  return;
119
133
  }
120
- setClientState({
121
- session: Object.assign(Object.assign({}, stytch.session.getInfo()), { isInitialized: true }),
122
- user: Object.assign(Object.assign({}, stytch.user.getInfo()), { isInitialized: true }),
123
- });
124
- return stytch.onStateChange(() => {
125
- setClientState((oldState) => {
126
- const newState = {
127
- session: Object.assign(Object.assign({}, stytch.session.getInfo()), { isInitialized: true }),
128
- user: Object.assign(Object.assign({}, stytch.user.getInfo()), { isInitialized: true }),
129
- };
130
- return mergeWithStableProps(oldState, newState);
131
- });
132
- });
133
- }, [setClientState, stytch]);
134
+ const updateState = () => {
135
+ setClientState((oldState) => mergeWithStableProps(oldState, getHydratedState()));
136
+ };
137
+ if (!assumeHydratedRef.current) {
138
+ updateState();
139
+ }
140
+ return stytch.onStateChange(updateState);
141
+ }, [getHydratedState, setClientState, stytch]);
134
142
  return (React.createElement(StytchContext.Provider, { value: ctx },
135
143
  React.createElement(StytchUserContext.Provider, { value: user },
136
144
  React.createElement(StytchSessionContext.Provider, { value: session }, children))));
@@ -1,5 +1,5 @@
1
1
  import { StytchHeadlessClient } from '@stytch/vanilla-js/headless';
2
- import { c as createStytchSSRProxy } from './StytchSSRProxy-4e34fb34.js';
2
+ import { c as createStytchSSRProxy } from './StytchSSRProxy-34c789b5.js';
3
3
 
4
4
  /**
5
5
  * Creates a Headless Stytch client object to call the stytch APIs.
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var headless = require('@stytch/vanilla-js/headless');
6
- var StytchSSRProxy = require('./StytchSSRProxy-c9d3bc41.js');
6
+ var StytchSSRProxy = require('./StytchSSRProxy-86bc42b3.js');
7
7
 
8
8
  /**
9
9
  * Creates a Headless Stytch client object to call the stytch APIs.
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var React = require('react');
6
6
  var useIsomorphicLayoutEffect = require('./useIsomorphicLayoutEffect-65746ef3.js');
7
- var StytchSSRProxy = require('./StytchSSRProxy-c9d3bc41.js');
7
+ var StytchSSRProxy = require('./StytchSSRProxy-86bc42b3.js');
8
8
 
9
9
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
10
10
 
@@ -115,30 +115,38 @@ const withStytchSession = (Component) => {
115
115
  * </StytchProvider>
116
116
  * )
117
117
  */
118
- const StytchProvider = ({ stytch, children, }) => {
118
+ const StytchProvider = ({ stytch, children, assumeHydrated = false, }) => {
119
+ useIsomorphicLayoutEffect.invariant(!useIsMounted__INTERNAL(), StytchSSRProxy.providerMustBeUniqueError);
120
+ useIsomorphicLayoutEffect.invariant(!assumeHydrated || typeof window !== 'undefined', 'The `assumeHydrated` prop must be set to `false` when using StytchProvider in a server environment.');
119
121
  const ctx = React.useMemo(() => ({ client: stytch, isMounted: true }), [stytch]);
120
- const [{ user, session }, setClientState] = useIsomorphicLayoutEffect.useAsyncState({
121
- session: initialSession,
122
- user: initialUser,
123
- });
122
+ const getHydratedState = React.useCallback(() => {
123
+ return {
124
+ session: Object.assign(Object.assign({}, stytch.session.getInfo()), { isInitialized: true }),
125
+ user: Object.assign(Object.assign({}, stytch.user.getInfo()), { isInitialized: true }),
126
+ };
127
+ }, [stytch]);
128
+ const getInitialState = () => {
129
+ return {
130
+ session: initialSession,
131
+ user: initialUser,
132
+ };
133
+ };
134
+ const [{ user, session }, setClientState] = useIsomorphicLayoutEffect.useAsyncState(() => assumeHydrated ? getHydratedState() : getInitialState());
135
+ // Store the initial value of `assumeHydrated` in a ref, because it is
136
+ // logically only relevant for the first render
137
+ const assumeHydratedRef = React.useRef(assumeHydrated);
124
138
  React.useEffect(() => {
125
139
  if (StytchSSRProxy.isStytchSSRProxy(stytch)) {
126
140
  return;
127
141
  }
128
- setClientState({
129
- session: Object.assign(Object.assign({}, stytch.session.getInfo()), { isInitialized: true }),
130
- user: Object.assign(Object.assign({}, stytch.user.getInfo()), { isInitialized: true }),
131
- });
132
- return stytch.onStateChange(() => {
133
- setClientState((oldState) => {
134
- const newState = {
135
- session: Object.assign(Object.assign({}, stytch.session.getInfo()), { isInitialized: true }),
136
- user: Object.assign(Object.assign({}, stytch.user.getInfo()), { isInitialized: true }),
137
- };
138
- return useIsomorphicLayoutEffect.mergeWithStableProps(oldState, newState);
139
- });
140
- });
141
- }, [setClientState, stytch]);
142
+ const updateState = () => {
143
+ setClientState((oldState) => useIsomorphicLayoutEffect.mergeWithStableProps(oldState, getHydratedState()));
144
+ };
145
+ if (!assumeHydratedRef.current) {
146
+ updateState();
147
+ }
148
+ return stytch.onStateChange(updateState);
149
+ }, [getHydratedState, setClientState, stytch]);
142
150
  return (React__default["default"].createElement(StytchContext.Provider, { value: ctx },
143
151
  React__default["default"].createElement(StytchUserContext.Provider, { value: user },
144
152
  React__default["default"].createElement(StytchSessionContext.Provider, { value: session }, children))));
@@ -1,5 +1,5 @@
1
1
  import { StytchUIClient } from '@stytch/vanilla-js';
2
- import { c as createStytchSSRProxy } from './StytchSSRProxy-4e34fb34.js';
2
+ import { c as createStytchSSRProxy } from './StytchSSRProxy-34c789b5.js';
3
3
 
4
4
  /**
5
5
  * Creates a Stytch UI client object to call the Stytch APIs and render Stytch UI components.
package/dist/index.ui.js CHANGED
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var vanillaJs = require('@stytch/vanilla-js');
6
- var StytchSSRProxy = require('./StytchSSRProxy-c9d3bc41.js');
6
+ var StytchSSRProxy = require('./StytchSSRProxy-86bc42b3.js');
7
7
 
8
8
  /**
9
9
  * Creates a Stytch UI client object to call the Stytch APIs and render Stytch UI components.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stytch/nextjs",
3
- "version": "21.4.4",
3
+ "version": "21.5.0",
4
4
  "description": "Stytch's official Next.js Library",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.esm.js",
@@ -15,15 +15,16 @@
15
15
  "build": "npm run clean && npm run compile",
16
16
  "clean": "rm -rf ./dist",
17
17
  "compile": "rollup -c",
18
- "dev": "tsc -p tsconfig.build.json --watch"
18
+ "dev": "tsc -p tsconfig.build.json --watch",
19
+ "test": "jest"
19
20
  },
20
21
  "license": "MIT",
21
22
  "author": "Stytch",
22
23
  "devDependencies": {
23
- "@babel/runtime": "7.26.10",
24
+ "@babel/runtime": "7.27.0",
24
25
  "@stytch/internal-tsconfigs": "0.0.0",
25
26
  "@stytch/js-utils": "0.0.1",
26
- "@stytch/vanilla-js": "5.22.3",
27
+ "@stytch/vanilla-js": "5.22.7",
27
28
  "@testing-library/react": "14.0.0",
28
29
  "@types/jest": "29.5.14",
29
30
  "eslint-config-custom": "0.0.1",
@@ -1,4 +0,0 @@
1
- declare const noProviderError: (item: string, provider?: string) => string;
2
- declare const noHeadlessClientError = "Tried to create a Stytch Login UI element using the Stytch Headless SDK.\nYou must use the UI SDK to use UI elements.\nPlease make sure you are importing createStytchHeadlessClient from @stytch/nextjs/ui and not from @stytch/nextjs/headless.";
3
- declare const cannotInvokeMethodOnServerError: (path: string) => string;
4
- export { noProviderError, noHeadlessClientError, cannotInvokeMethodOnServerError };