@stytch/react 0.0.2-0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Stytch-React
1
+ # @stytch/react
2
2
 
3
3
  Stytch's React Library
4
4
 
@@ -9,7 +9,7 @@ With `npm`
9
9
 
10
10
  ## Documentation
11
11
 
12
- For full documentation please refer to Stytch's javascript SDK documentation on https://stytch.com/docs/sdks.
12
+ For full documentation please refer to Stytch's javascript SDK documentation at https://stytch.com/docs/sdks.
13
13
 
14
14
  ## Example Usage
15
15
 
@@ -30,7 +30,7 @@ ReactDOM.render(
30
30
  // Now use Stytch in your components
31
31
  const App = () => {
32
32
  const stytchProps = {
33
- loginOrSignupView: {
33
+ config: {
34
34
  products: ['emailMagicLinks'],
35
35
  emailMagicLinksOptions: {
36
36
  loginRedirectURL: 'https://example.com/authenticate',
@@ -40,7 +40,7 @@ const App = () => {
40
40
  createUserAsPending: true,
41
41
  },
42
42
  },
43
- style: {
43
+ styles: {
44
44
  fontFamily: '"Helvetica New", Helvetica, sans-serif',
45
45
  width: '321px',
46
46
  primaryColor: '#0577CA',
@@ -54,10 +54,9 @@ const App = () => {
54
54
 
55
55
  return (
56
56
  <div id="login">
57
- <Stytch
58
- publicToken={stytchProps.publicToken}
59
- loginOrSignupView={stytchProps.loginOrSignupView}
60
- style={stytchProps.style}
57
+ <StytchLogin
58
+ config={stytchProps.loginOrSignupView}
59
+ styles={stytchProps.style}
61
60
  callbacks={stytchProps.callbacks}
62
61
  />
63
62
  </div>
@@ -68,3 +67,90 @@ const App = () => {
68
67
  ## Typescript Support
69
68
 
70
69
  There are built in typescript definitions in the npm package.
70
+
71
+ ## Migrating from @stytch/stytch-react
72
+
73
+ If you are migrating from [@stytch/stytch-react](https://www.npmjs.com/package/@stytch/stytch-react), follow the steps below:
74
+
75
+ ### Step 1: Install the new libraries
76
+
77
+ - The core SDK is now bundled in its own module - [@stytch/vanilla-js](https://www.npmjs.com/package/@stytch/vanilla-js)
78
+ - We now have a library specifically for React bindings - [@stytch/react](https://www.npmjs.com/package/@stytch/react)
79
+
80
+ ```shell
81
+ # NPM
82
+ npm install @stytch/vanilla-js @stytch/react
83
+ # Yarn
84
+ yarn add @stytch/vanilla-js @stytch/react
85
+ ```
86
+
87
+ ### Step 2: Remove the old SDK
88
+
89
+ - Remove the `@styth/stytch-react` package
90
+ - If you are explicitly loading the `stytch.js` script in the document header, remove it. It isn't needed anymore.
91
+
92
+ ```shell
93
+ # NPM
94
+ npm rm @stytch/stytch-react
95
+ # Yarn
96
+ yarn remove @stytch/stytch-react
97
+ ```
98
+
99
+ ### Step 3: Initialize the client in your Application Root
100
+
101
+ - Determine if you need the Headless or UI client. If you plan to use the `<StytchLogin />` component in your application then you should use the the UI client. Otherwise use the Headless client to minimize application bundle size.
102
+ - To create the Stytch Headless client, use `StytchHeadlessClient` from `@stytch/vanilla-js/headless`
103
+ - To create the Stytch UI client, use `StytchUIClient` from `@stytch/vanilla-js`
104
+ - Pass it into `<StytchProvider />` component from `@stytch/react`
105
+
106
+ ```jsx
107
+ import React from 'react';
108
+ import { StytchProvider } from '@stytch/react';
109
+ import { StytchHeadlessClient } from '@stytch/vanilla-js/headless';
110
+ // Or alternately
111
+ // import { StytchUIClient } from '@stytch/vanilla-js';
112
+
113
+ const stytch = new StytchHeadlessClient(process.env.REACT_APP_STYTCH_PUBLIC_TOKEN);
114
+
115
+ function WrappedApp() {
116
+ return (
117
+ <StytchProvider stytch={stytch}>
118
+ <App />
119
+ </StytchProvider>
120
+ );
121
+ }
122
+
123
+ export default WrappedApp;
124
+ ```
125
+
126
+ ### Step 4: Update calls to `useStytchUser` , `useStytchSession`, and `useStytchLazy`
127
+
128
+ - `useStytchUser` and `useStytchSession` hooks now return envelope objects - `{ user, isCached }` and `{ session, isCached }` respectively.
129
+ - On first render the SDK will read user/session out of persistent storage, and serve them with `isCached: true` - at this point you’re reading the stale-while-revalidating value
130
+ - The SDK will make network requests to pull the most up-to-date user and session objects, and serve them with `isCached: false`
131
+ - `useStytchLazy` is no longer required - you may always call `useStytch` now
132
+
133
+ ```jsx
134
+ import React, { useEffect } from 'react';
135
+ import { useStytchUser } from '@stytch/react';
136
+
137
+ export default function Profile() {
138
+ const router = useRouter();
139
+ const { user, isCached } = useStytchUser();
140
+
141
+ return (
142
+ <Layout>
143
+ <h1>Your {isCached ? 'Cached' : null} Profile</h1>
144
+ <pre>{JSON.stringify(user, null, 2)}</pre>
145
+ </Layout>
146
+ );
147
+ }
148
+ ```
149
+
150
+ ### Step 5: UI Naming Changes
151
+
152
+ We've made a number of small changes to our naming conventions to make the API cleaner and easier to understand.
153
+
154
+ - The `<Stytch />` login component is now called `<StytchLogin />`
155
+ - The `OAuthProvidersTypes` enum is now called `OAuthProviders`
156
+ - The `SDKProductTypes` enum is now called `Products`
package/dist/index.d.ts CHANGED
@@ -1,33 +1,208 @@
1
1
  /// <reference types="react" />
2
- import { Config, StyleConfig, User, Session, StytchUIClient } from "@stytch/vanilla-js";
3
- import * as React from "react";
2
+ // We need to import the StytchUIClient type to give the TSDoc parser a hint as to where it is from
3
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
4
+ import { Callbacks, StytchLoginConfig, StyleConfig, User, Session, StytchUIClient } from "@stytch/vanilla-js";
5
+ import React from "react";
6
+ import { ReactNode } from "react";
4
7
  import { StytchHeadlessClient } from "@stytch/vanilla-js/headless";
8
+ interface StytchProps {
9
+ /**
10
+ * A {@link StytchLoginConfig} object. Add products and product-specific config to this object to change the login methods shown.
11
+ *
12
+ * @example
13
+ * {
14
+ * products: ['crypto', 'otps']
15
+ * }
16
+ *
17
+ * @example
18
+ * {
19
+ * products: ['emailMagicLinks'>]
20
+ * emailMagicLinksOptions: {
21
+ * loginRedirectURL: 'https://example.com/authenticate',
22
+ * signupRedirectURL: 'https://example.com/authenticate',
23
+ * }
24
+ * }
25
+ *
26
+ * @example
27
+ * {
28
+ * products: ['oauth'>]
29
+ * oauthOptions: {
30
+ * providers: [
31
+ * { type: 'google', one_tap: true, position: 'embedded' },
32
+ * { type: 'microsoft' },
33
+ * { type: 'apple' },
34
+ * { type: 'facebook' },
35
+ * ],
36
+ * }
37
+ * }
38
+ */
39
+ config: StytchLoginConfig;
40
+ /**
41
+ * An optional {@link StyleConfig} to customize the look and feel of the screen.
42
+ *
43
+ * @example
44
+ * {
45
+ * fontFamily: 'Arial, Helvetica, sans-serif',
46
+ * width: '360px',
47
+ * primaryColor: '#19303D',
48
+ * }
49
+ */
50
+ styles?: StyleConfig;
51
+ /**
52
+ * An optional {@link Callbacks} object.
53
+ *
54
+ * @example
55
+ * {
56
+ * onError: ({message}) => {
57
+ * console.error('Stytch login screen error', message)
58
+ * }
59
+ * }
60
+ *
61
+ * @example
62
+ * {
63
+ * onEvent: ({type, data}) => {
64
+ * if(type === StytchEventType.CryptoWalletAuthenticate) {
65
+ * console.log('Logged in with crypto wallet', data);
66
+ * }
67
+ * }
68
+ * }
69
+ */
70
+ callbacks?: Callbacks;
71
+ }
5
72
  /**
6
- * Stytch JS React Component
73
+ * The Stytch Login Screen component.
74
+ * This component can only be used with a {@link StytchUIClient} client constructor
75
+ * passed into the {@link StytchProvider}
7
76
  *
8
- * [Documentation](https://stytch.com/docs/javascript-sdk)
77
+ * See the {@link https://stytch.com/docs/sdks/javascript-sdk online reference}
78
+ * and {@link https://storybook.stytch.com interactive examples} for more.
79
+ *
80
+ * @example
81
+ * <StytchLogin
82
+ * config={{
83
+ * products: ['emailMagicLinks', 'oauth'],
84
+ * emailMagicLinksOptions: {
85
+ * loginRedirectURL: 'https://example.com/authenticate',
86
+ * signupRedirectURL: 'https://example.com/authenticate',
87
+ * },
88
+ * oauthOptions: {
89
+ * providers: [{ type: OAuthProviders.Google }, { type: OAuthProviders.Microsoft }],
90
+ * },
91
+ * }}
92
+ * styles={{
93
+ * fontFamily: '"Helvetica New", Helvetica, sans-serif',
94
+ * primaryColor: '#0577CA',
95
+ * width: '321px',
96
+ * }}
97
+ * callbacks={{
98
+ * onEvent: (event) => console.log(event)
99
+ * }}
100
+ * />
101
+ * @param props {@link StytchProps}
102
+ */
103
+ declare const StytchLogin: ({ config, styles, callbacks }: StytchProps) => JSX.Element;
104
+ interface StytchResetPasswordProps extends StytchProps {
105
+ passwordResetToken: string;
106
+ }
107
+ /**
108
+ * The Stytch Reset Password component.
109
+ * This component can only be used with a {@link StytchUIClient} client constructor
110
+ * passed into the {@link StytchProvider}
111
+ *
112
+ * See the {@link https://stytch.com/docs/sdks/javascript-sdk online reference}
113
+ * and {@link https://storybook.stytch.com interactive examples} for more.
114
+ *
115
+ * @example
116
+ * TODO
117
+ */
118
+ declare const StytchPasswordReset: ({ config, styles, callbacks, passwordResetToken }: StytchResetPasswordProps) => JSX.Element;
119
+ /**
120
+ * The Stytch Client object passed in to <StytchProvider /> in your application root.
121
+ * Either a StytchUIClient or StytchHeadlessClient.
9
122
  */
10
- declare const Stytch: ({ config, styles }: {
11
- config: Config;
12
- styles: StyleConfig;
13
- }) => JSX.Element | null;
14
123
  type StytchClient = StytchUIClient | StytchHeadlessClient;
15
- declare const useStytchUser: () => User | null;
16
- declare const useStytchSession: () => Session | null;
124
+ type SWRUser = {
125
+ /**
126
+ * Either the active {@link User} object, or null if the user is not logged in.
127
+ */
128
+ user: User | null;
129
+ /**
130
+ * If true, indicates that the value returned is from the application cache and a state refresh is in progress.
131
+ */
132
+ fromCache: boolean;
133
+ };
134
+ type SWRSession = {
135
+ /**
136
+ * Either the active {@link Session} object, or null if the user is not logged in.
137
+ */
138
+ session: Session | null;
139
+ /**
140
+ * If true, indicates that the value returned is from the application cache and a state refresh is in progress.
141
+ */
142
+ fromCache: boolean;
143
+ };
144
+ /**
145
+ * Returns the active User.
146
+ * Check the fromCache property to determine if the user data is from persistent storage.
147
+ * @example
148
+ * const {user} = useStytchUser();
149
+ * return (<h1>Welcome, {user.name.first_name}</h1>);
150
+ * @returns A {@link SWRUser}
151
+ */
152
+ declare const useStytchUser: () => SWRUser;
153
+ /**
154
+ * Returns the active user's Stytch session.
155
+ * @example
156
+ * const {session} = useStytchSession();
157
+ * useEffect(() => {
158
+ * if (!session) {
159
+ * router.replace('/login')
160
+ * }
161
+ * }, [session]);
162
+ * @returns A {@link SWRSession}
163
+ */
164
+ declare const useStytchSession: () => SWRSession;
165
+ /**
166
+ * Returns the Stytch client stored in the Stytch context.
167
+ *
168
+ * @example
169
+ * const stytch = useStytch();
170
+ * useEffect(() => {
171
+ * stytch.magicLinks.authenticate('...')
172
+ * }, [stytch]);
173
+ */
17
174
  declare const useStytch: () => StytchClient;
18
175
  declare const withStytch: <T extends object>(Component: React.ComponentType<T & {
19
176
  stytch: StytchClient;
20
177
  }>) => React.ComponentType<T>;
21
178
  declare const withStytchUser: <T extends object>(Component: React.ComponentType<T & {
22
179
  stytchUser: User | null;
180
+ stytchUserIsFromCache: boolean;
23
181
  }>) => React.ComponentType<T>;
24
182
  declare const withStytchSession: <T extends object>(Component: React.ComponentType<T & {
25
183
  stytchSession: Session | null;
184
+ stytchSessionIsFromCache: boolean;
26
185
  }>) => React.ComponentType<T>;
27
186
  type StytchProviderProps = {
187
+ /**
188
+ * A Stytch client instance, either a {@link StytchUIClient} or {@link StytchHeadlessClient}
189
+ */
28
190
  stytch: StytchClient;
29
- children?: React.ReactNode;
191
+ children?: ReactNode;
30
192
  };
193
+ /**
194
+ * The Stytch Context Provider.
195
+ * Wrap your application with this component in the root file in order to use Stytch everywhere in your app.
196
+ * @example
197
+ * const stytch = new StytchHeadlessClient('public-token-<find yours in the stytch dashboard>')
198
+ *
199
+ * ReactDOM.render(
200
+ * <StytchProvider stytch={stytch}>
201
+ * <App />
202
+ * </StytchProvider>,
203
+ * document.getElementById('root'),
204
+ * )
205
+ */
31
206
  declare const StytchProvider: ({ stytch, children }: StytchProviderProps) => JSX.Element;
32
- export { Stytch, StytchProvider, useStytch, useStytchSession, useStytchUser, withStytch, withStytchSession, withStytchUser };
207
+ export { StytchLogin, StytchPasswordReset, StytchProvider, useStytch, useStytchSession, useStytchUser, withStytch, withStytchSession, withStytchUser };
33
208
  export type { StytchProviderProps };
@@ -1,33 +1,208 @@
1
1
  /// <reference types="react" />
2
- import { Config, StyleConfig, User, Session, StytchUIClient } from "@stytch/vanilla-js";
3
- import * as React from "react";
2
+ // We need to import the StytchUIClient type to give the TSDoc parser a hint as to where it is from
3
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
4
+ import { Callbacks, StytchLoginConfig, StyleConfig, User, Session, StytchUIClient } from "@stytch/vanilla-js";
5
+ import React from "react";
6
+ import { ReactNode } from "react";
4
7
  import { StytchHeadlessClient } from "@stytch/vanilla-js/headless";
8
+ interface StytchProps {
9
+ /**
10
+ * A {@link StytchLoginConfig} object. Add products and product-specific config to this object to change the login methods shown.
11
+ *
12
+ * @example
13
+ * {
14
+ * products: ['crypto', 'otps']
15
+ * }
16
+ *
17
+ * @example
18
+ * {
19
+ * products: ['emailMagicLinks'>]
20
+ * emailMagicLinksOptions: {
21
+ * loginRedirectURL: 'https://example.com/authenticate',
22
+ * signupRedirectURL: 'https://example.com/authenticate',
23
+ * }
24
+ * }
25
+ *
26
+ * @example
27
+ * {
28
+ * products: ['oauth'>]
29
+ * oauthOptions: {
30
+ * providers: [
31
+ * { type: 'google', one_tap: true, position: 'embedded' },
32
+ * { type: 'microsoft' },
33
+ * { type: 'apple' },
34
+ * { type: 'facebook' },
35
+ * ],
36
+ * }
37
+ * }
38
+ */
39
+ config: StytchLoginConfig;
40
+ /**
41
+ * An optional {@link StyleConfig} to customize the look and feel of the screen.
42
+ *
43
+ * @example
44
+ * {
45
+ * fontFamily: 'Arial, Helvetica, sans-serif',
46
+ * width: '360px',
47
+ * primaryColor: '#19303D',
48
+ * }
49
+ */
50
+ styles?: StyleConfig;
51
+ /**
52
+ * An optional {@link Callbacks} object.
53
+ *
54
+ * @example
55
+ * {
56
+ * onError: ({message}) => {
57
+ * console.error('Stytch login screen error', message)
58
+ * }
59
+ * }
60
+ *
61
+ * @example
62
+ * {
63
+ * onEvent: ({type, data}) => {
64
+ * if(type === StytchEventType.CryptoWalletAuthenticate) {
65
+ * console.log('Logged in with crypto wallet', data);
66
+ * }
67
+ * }
68
+ * }
69
+ */
70
+ callbacks?: Callbacks;
71
+ }
5
72
  /**
6
- * Stytch JS React Component
73
+ * The Stytch Login Screen component.
74
+ * This component can only be used with a {@link StytchUIClient} client constructor
75
+ * passed into the {@link StytchProvider}
7
76
  *
8
- * [Documentation](https://stytch.com/docs/javascript-sdk)
77
+ * See the {@link https://stytch.com/docs/sdks/javascript-sdk online reference}
78
+ * and {@link https://storybook.stytch.com interactive examples} for more.
79
+ *
80
+ * @example
81
+ * <StytchLogin
82
+ * config={{
83
+ * products: ['emailMagicLinks', 'oauth'],
84
+ * emailMagicLinksOptions: {
85
+ * loginRedirectURL: 'https://example.com/authenticate',
86
+ * signupRedirectURL: 'https://example.com/authenticate',
87
+ * },
88
+ * oauthOptions: {
89
+ * providers: [{ type: OAuthProviders.Google }, { type: OAuthProviders.Microsoft }],
90
+ * },
91
+ * }}
92
+ * styles={{
93
+ * fontFamily: '"Helvetica New", Helvetica, sans-serif',
94
+ * primaryColor: '#0577CA',
95
+ * width: '321px',
96
+ * }}
97
+ * callbacks={{
98
+ * onEvent: (event) => console.log(event)
99
+ * }}
100
+ * />
101
+ * @param props {@link StytchProps}
102
+ */
103
+ declare const StytchLogin: ({ config, styles, callbacks }: StytchProps) => JSX.Element;
104
+ interface StytchResetPasswordProps extends StytchProps {
105
+ passwordResetToken: string;
106
+ }
107
+ /**
108
+ * The Stytch Reset Password component.
109
+ * This component can only be used with a {@link StytchUIClient} client constructor
110
+ * passed into the {@link StytchProvider}
111
+ *
112
+ * See the {@link https://stytch.com/docs/sdks/javascript-sdk online reference}
113
+ * and {@link https://storybook.stytch.com interactive examples} for more.
114
+ *
115
+ * @example
116
+ * TODO
117
+ */
118
+ declare const StytchPasswordReset: ({ config, styles, callbacks, passwordResetToken }: StytchResetPasswordProps) => JSX.Element;
119
+ /**
120
+ * The Stytch Client object passed in to <StytchProvider /> in your application root.
121
+ * Either a StytchUIClient or StytchHeadlessClient.
9
122
  */
10
- declare const Stytch: ({ config, styles }: {
11
- config: Config;
12
- styles: StyleConfig;
13
- }) => JSX.Element | null;
14
123
  type StytchClient = StytchUIClient | StytchHeadlessClient;
15
- declare const useStytchUser: () => User | null;
16
- declare const useStytchSession: () => Session | null;
124
+ type SWRUser = {
125
+ /**
126
+ * Either the active {@link User} object, or null if the user is not logged in.
127
+ */
128
+ user: User | null;
129
+ /**
130
+ * If true, indicates that the value returned is from the application cache and a state refresh is in progress.
131
+ */
132
+ fromCache: boolean;
133
+ };
134
+ type SWRSession = {
135
+ /**
136
+ * Either the active {@link Session} object, or null if the user is not logged in.
137
+ */
138
+ session: Session | null;
139
+ /**
140
+ * If true, indicates that the value returned is from the application cache and a state refresh is in progress.
141
+ */
142
+ fromCache: boolean;
143
+ };
144
+ /**
145
+ * Returns the active User.
146
+ * Check the fromCache property to determine if the user data is from persistent storage.
147
+ * @example
148
+ * const {user} = useStytchUser();
149
+ * return (<h1>Welcome, {user.name.first_name}</h1>);
150
+ * @returns A {@link SWRUser}
151
+ */
152
+ declare const useStytchUser: () => SWRUser;
153
+ /**
154
+ * Returns the active user's Stytch session.
155
+ * @example
156
+ * const {session} = useStytchSession();
157
+ * useEffect(() => {
158
+ * if (!session) {
159
+ * router.replace('/login')
160
+ * }
161
+ * }, [session]);
162
+ * @returns A {@link SWRSession}
163
+ */
164
+ declare const useStytchSession: () => SWRSession;
165
+ /**
166
+ * Returns the Stytch client stored in the Stytch context.
167
+ *
168
+ * @example
169
+ * const stytch = useStytch();
170
+ * useEffect(() => {
171
+ * stytch.magicLinks.authenticate('...')
172
+ * }, [stytch]);
173
+ */
17
174
  declare const useStytch: () => StytchClient;
18
175
  declare const withStytch: <T extends object>(Component: React.ComponentType<T & {
19
176
  stytch: StytchClient;
20
177
  }>) => React.ComponentType<T>;
21
178
  declare const withStytchUser: <T extends object>(Component: React.ComponentType<T & {
22
179
  stytchUser: User | null;
180
+ stytchUserIsFromCache: boolean;
23
181
  }>) => React.ComponentType<T>;
24
182
  declare const withStytchSession: <T extends object>(Component: React.ComponentType<T & {
25
183
  stytchSession: Session | null;
184
+ stytchSessionIsFromCache: boolean;
26
185
  }>) => React.ComponentType<T>;
27
186
  type StytchProviderProps = {
187
+ /**
188
+ * A Stytch client instance, either a {@link StytchUIClient} or {@link StytchHeadlessClient}
189
+ */
28
190
  stytch: StytchClient;
29
- children?: React.ReactNode;
191
+ children?: ReactNode;
30
192
  };
193
+ /**
194
+ * The Stytch Context Provider.
195
+ * Wrap your application with this component in the root file in order to use Stytch everywhere in your app.
196
+ * @example
197
+ * const stytch = new StytchHeadlessClient('public-token-<find yours in the stytch dashboard>')
198
+ *
199
+ * ReactDOM.render(
200
+ * <StytchProvider stytch={stytch}>
201
+ * <App />
202
+ * </StytchProvider>,
203
+ * document.getElementById('root'),
204
+ * )
205
+ */
31
206
  declare const StytchProvider: ({ stytch, children }: StytchProviderProps) => JSX.Element;
32
- export { Stytch, StytchProvider, useStytch, useStytchSession, useStytchUser, withStytch, withStytchSession, withStytchUser };
207
+ export { StytchLogin, StytchPasswordReset, StytchProvider, useStytch, useStytchSession, useStytchUser, withStytch, withStytchSession, withStytchUser };
33
208
  export type { StytchProviderProps };
package/dist/index.esm.js CHANGED
@@ -1,96 +1,258 @@
1
- import * as React from 'react';
1
+ import React, { useRef, useState, useEffect, useCallback, createContext, useContext, useMemo, useLayoutEffect } from 'react';
2
2
 
3
- const StytchUserContext = React.createContext(null);
4
- const StytchSessionContext = React.createContext(null);
5
- const StytchContext = React.createContext({});
3
+ const noProviderError = (item) => `${item} can only be used inside <StytchProvider>.`;
4
+ const providerMustBeUniqueError = 'You cannot render a <StytchProvider> inside another <StytchProvider>.';
5
+ const noSSRError = `The @stytch/react library is not meant for use with serverside environments like NextJS.
6
+ Use the @stytch/nextjs library instead -
7
+ npm remove @stytch/react && npm install @stytch/nextjs
8
+ `;
9
+ const noHeadlessClientError = `Tried to create a Stytch Login UI element using the Stytch Headless SDK.
10
+ You must use the UI SDK to use UI elements.
11
+ Please make sure you are importing from @stytch/vanilla-js and not from the @stytch/vanilla-js/headless.`;
12
+
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
+ function invariant(cond, message) {
15
+ if (!cond)
16
+ throw new Error(message);
17
+ }
18
+
19
+ // useState can cause memory leaks if it is set after the component unmounted. For example, if it is
20
+ // set after `await`, or in a `then`, `catch`, or `finally`, or in a setTimout/setInterval.
21
+ const useAsyncState = (initialState) => {
22
+ const isMounted = useRef(true);
23
+ const [state, setState] = useState(initialState);
24
+ useEffect(() => {
25
+ isMounted.current = true;
26
+ return () => {
27
+ isMounted.current = false;
28
+ };
29
+ }, []);
30
+ const setStateAction = useCallback((newState) => {
31
+ isMounted.current && setState(newState);
32
+ }, []);
33
+ return [state, setStateAction];
34
+ };
35
+
36
+ const initialUser = {
37
+ user: null,
38
+ fromCache: false,
39
+ };
40
+ const initialSession = {
41
+ session: null,
42
+ fromCache: false,
43
+ };
44
+ const StytchContext = createContext({ isMounted: false });
45
+ const StytchUserContext = createContext(initialUser);
46
+ const StytchSessionContext = createContext(initialSession);
47
+ const useIsMounted__INTERNAL = () => useContext(StytchContext).isMounted;
6
48
  const isUIClient = (client) => {
7
- return client.mount !== undefined;
49
+ return client.mountLogin !== undefined;
8
50
  };
51
+ /**
52
+ * Returns the active User.
53
+ * Check the fromCache property to determine if the user data is from persistent storage.
54
+ * @example
55
+ * const {user} = useStytchUser();
56
+ * return (<h1>Welcome, {user.name.first_name}</h1>);
57
+ * @returns A {@link SWRUser}
58
+ */
9
59
  const useStytchUser = () => {
10
- return React.useContext(StytchUserContext);
60
+ invariant(useIsMounted__INTERNAL(), noProviderError('useStytchUser'));
61
+ return useContext(StytchUserContext);
11
62
  };
63
+ /**
64
+ * Returns the active user's Stytch session.
65
+ * @example
66
+ * const {session} = useStytchSession();
67
+ * useEffect(() => {
68
+ * if (!session) {
69
+ * router.replace('/login')
70
+ * }
71
+ * }, [session]);
72
+ * @returns A {@link SWRSession}
73
+ */
12
74
  const useStytchSession = () => {
13
- return React.useContext(StytchSessionContext);
75
+ invariant(useIsMounted__INTERNAL(), noProviderError('useStytchSession'));
76
+ return useContext(StytchSessionContext);
14
77
  };
78
+ /**
79
+ * Returns the Stytch client stored in the Stytch context.
80
+ *
81
+ * @example
82
+ * const stytch = useStytch();
83
+ * useEffect(() => {
84
+ * stytch.magicLinks.authenticate('...')
85
+ * }, [stytch]);
86
+ */
15
87
  const useStytch = () => {
16
- return React.useContext(StytchContext);
88
+ const ctx = useContext(StytchContext);
89
+ invariant(ctx.isMounted, noProviderError('useStytch'));
90
+ return ctx.client;
17
91
  };
18
92
  const withStytch = (Component) => {
19
93
  const WithStytch = (props) => {
20
- return React.createElement(Component, { ...props, stytch: useStytch() });
94
+ invariant(useIsMounted__INTERNAL(), noProviderError('withStytch'));
95
+ return React.createElement(Component, Object.assign({}, props, { stytch: useStytch() }));
21
96
  };
22
97
  WithStytch.displayName = `withStytch(${Component.displayName || Component.name || 'Component'})`;
23
98
  return WithStytch;
24
99
  };
25
100
  const withStytchUser = (Component) => {
26
101
  const WithStytchUser = (props) => {
27
- return React.createElement(Component, { ...props, stytchUser: useStytchUser() });
102
+ invariant(useIsMounted__INTERNAL(), noProviderError('withStytchUser'));
103
+ const { user, fromCache } = useStytchUser();
104
+ return React.createElement(Component, Object.assign({}, props, { stytchUser: user, stytchUserIsFromCache: fromCache }));
28
105
  };
29
106
  WithStytchUser.displayName = `withStytchUser(${Component.displayName || Component.name || 'Component'})`;
30
107
  return WithStytchUser;
31
108
  };
32
109
  const withStytchSession = (Component) => {
33
110
  const WithStytchSession = (props) => {
34
- return React.createElement(Component, { ...props, stytchSession: useStytchSession() });
111
+ invariant(useIsMounted__INTERNAL(), noProviderError('withStytchSession'));
112
+ const { session, fromCache } = useStytchSession();
113
+ return React.createElement(Component, Object.assign({}, props, { stytchSession: session, stytchSessionIsFromCache: fromCache }));
35
114
  };
36
115
  WithStytchSession.displayName = `withStytchSession(${Component.displayName || Component.name || 'Component'})`;
37
116
  return WithStytchSession;
38
117
  };
118
+ /**
119
+ * The Stytch Context Provider.
120
+ * Wrap your application with this component in the root file in order to use Stytch everywhere in your app.
121
+ * @example
122
+ * const stytch = new StytchHeadlessClient('public-token-<find yours in the stytch dashboard>')
123
+ *
124
+ * ReactDOM.render(
125
+ * <StytchProvider stytch={stytch}>
126
+ * <App />
127
+ * </StytchProvider>,
128
+ * document.getElementById('root'),
129
+ * )
130
+ */
39
131
  const StytchProvider = ({ stytch, children }) => {
40
- const [user, setUser] = React.useState(null);
41
- const [session, setSession] = React.useState(null);
42
- React.useEffect(() => {
43
- const unsubscribeUser = stytch.user.onChange((user) => setUser(user));
44
- const unsubscribeSession = stytch.session.onChange((session) => setSession(session));
132
+ invariant(!useIsMounted__INTERNAL(), providerMustBeUniqueError);
133
+ invariant(typeof window !== 'undefined', noSSRError);
134
+ const ctx = useMemo(() => ({ client: stytch, isMounted: true }), [stytch]);
135
+ const [user, setUser] = useAsyncState({
136
+ user: stytch.user.getSync(),
137
+ fromCache: true,
138
+ });
139
+ const [session, setSession] = useAsyncState({
140
+ session: stytch.session.getSync(),
141
+ fromCache: true,
142
+ });
143
+ useEffect(() => {
144
+ const unsubscribeUser = stytch.user.onChange((user) => setUser({
145
+ user,
146
+ fromCache: false,
147
+ }));
148
+ const unsubscribeSession = stytch.session.onChange((session) => setSession({
149
+ session,
150
+ fromCache: false,
151
+ }));
45
152
  return () => {
46
153
  unsubscribeUser();
47
154
  unsubscribeSession();
48
155
  };
49
156
  }, [stytch, setUser, setSession]);
50
- return (React.createElement(StytchContext.Provider, { value: stytch },
51
- React.createElement(StytchUserContext.Provider, { value: session && user },
52
- React.createElement(StytchSessionContext.Provider, { value: user && session }, children))));
157
+ const finalSess = !!session.session === !!user.user ? session : initialSession;
158
+ const finalUser = !!session.session === !!user.user ? user : initialUser;
159
+ return (React.createElement(StytchContext.Provider, { value: ctx },
160
+ React.createElement(StytchUserContext.Provider, { value: finalUser },
161
+ React.createElement(StytchSessionContext.Provider, { value: finalSess }, children))));
53
162
  };
54
163
 
55
164
  /**
56
- * Returns a unique element ID.
57
- * Client-side only - will return null for server-side
58
- * resolves: https://stytch.slack.com/archives/C015UDB4X33/p1641450131000800
59
- * based on: https://github.com/vercel/next.js/issues/7322#issuecomment-968858477
60
- */
61
- const useUniqueElementId = () => {
62
- const [elementId, setElementId] = React.useState(null);
63
- React.useEffect(() => {
64
- const randId = Math.floor(Math.random() * 1e6);
65
- setElementId(`stytch-magic-link-${randId}`);
66
- }, []);
67
- return elementId;
68
- };
69
- /**
70
- * Stytch JS React Component
165
+ * The Stytch Login Screen component.
166
+ * This component can only be used with a {@link StytchUIClient} client constructor
167
+ * passed into the {@link StytchProvider}
168
+ *
169
+ * See the {@link https://stytch.com/docs/sdks/javascript-sdk online reference}
170
+ * and {@link https://storybook.stytch.com interactive examples} for more.
71
171
  *
72
- * [Documentation](https://stytch.com/docs/javascript-sdk)
172
+ * @example
173
+ * <StytchLogin
174
+ * config={{
175
+ * products: ['emailMagicLinks', 'oauth'],
176
+ * emailMagicLinksOptions: {
177
+ * loginRedirectURL: 'https://example.com/authenticate',
178
+ * signupRedirectURL: 'https://example.com/authenticate',
179
+ * },
180
+ * oauthOptions: {
181
+ * providers: [{ type: OAuthProviders.Google }, { type: OAuthProviders.Microsoft }],
182
+ * },
183
+ * }}
184
+ * styles={{
185
+ * fontFamily: '"Helvetica New", Helvetica, sans-serif',
186
+ * primaryColor: '#0577CA',
187
+ * width: '321px',
188
+ * }}
189
+ * callbacks={{
190
+ * onEvent: (event) => console.log(event)
191
+ * }}
192
+ * />
193
+ * @param props {@link StytchProps}
73
194
  */
74
- const Stytch = ({ config, styles }) => {
195
+ const StytchLogin = ({ config, styles, callbacks }) => {
196
+ invariant(useIsMounted__INTERNAL(), noProviderError('<StytchLogin />'));
75
197
  const stytchClient = useStytch();
76
- const elementId = useUniqueElementId();
77
- React.useEffect(() => {
78
- if (!stytchClient || !elementId) {
198
+ const containerEl = useRef(null);
199
+ useLayoutEffect(() => {
200
+ if (!isUIClient(stytchClient)) {
201
+ throw Error(noHeadlessClientError);
202
+ }
203
+ if (!containerEl.current) {
79
204
  return;
80
205
  }
81
- if (!isUIClient(stytchClient)) {
82
- throw Error(`Tried to create a Stytch Login UI element using the Stytch Headless SDK.
83
- You must use the UI SDK to use UI elements.
84
- Please make sure you are importing from @stytch/vanilla-js and not from the @stytch/vanilla-js/headless.
85
- `);
206
+ if (!containerEl.current.id) {
207
+ const randId = Math.floor(Math.random() * 1e6);
208
+ containerEl.current.id = `stytch-magic-link-${randId}`;
86
209
  }
87
- stytchClient.mount({
210
+ stytchClient.mountLogin({
88
211
  config,
89
- elementId: `#${elementId}`,
212
+ callbacks,
213
+ elementId: `#${containerEl.current.id}`,
90
214
  styles,
91
215
  });
92
- }, [elementId, stytchClient]);
93
- return elementId ? React.createElement("div", { id: elementId }) : null;
216
+ }, [stytchClient, config, styles, callbacks]);
217
+ return React.createElement("div", { ref: containerEl });
218
+ };
219
+ /**
220
+ * The Stytch Reset Password component.
221
+ * This component can only be used with a {@link StytchUIClient} client constructor
222
+ * passed into the {@link StytchProvider}
223
+ *
224
+ * See the {@link https://stytch.com/docs/sdks/javascript-sdk online reference}
225
+ * and {@link https://storybook.stytch.com interactive examples} for more.
226
+ *
227
+ * @example
228
+ * TODO
229
+ */
230
+ const StytchPasswordReset = ({ config, styles, callbacks, passwordResetToken }) => {
231
+ invariant(useIsMounted__INTERNAL(), noProviderError('<StytchResetPassword />'));
232
+ const stytchClient = useStytch();
233
+ const containerEl = useRef(null);
234
+ useLayoutEffect(() => {
235
+ if (!isUIClient(stytchClient)) {
236
+ throw Error(noHeadlessClientError);
237
+ }
238
+ if (!containerEl.current) {
239
+ return;
240
+ }
241
+ if (!containerEl.current.id) {
242
+ const randId = Math.floor(Math.random() * 1e6);
243
+ containerEl.current.id = `stytch-reset-password-${randId}`;
244
+ }
245
+ if (passwordResetToken) {
246
+ stytchClient.mountResetPassword({
247
+ config,
248
+ callbacks,
249
+ elementId: `#${containerEl.current.id}`,
250
+ styles,
251
+ passwordResetToken,
252
+ });
253
+ }
254
+ }, [stytchClient, config, styles, callbacks, passwordResetToken]);
255
+ return React.createElement("div", { ref: containerEl });
94
256
  };
95
257
 
96
- export { Stytch, StytchProvider, useStytch, useStytchSession, useStytchUser, withStytch, withStytchSession, withStytchUser };
258
+ export { StytchLogin, StytchPasswordReset, StytchProvider, useStytch, useStytchSession, useStytchUser, withStytch, withStytchSession, withStytchUser };
package/dist/index.js CHANGED
@@ -4,120 +4,267 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var React = require('react');
6
6
 
7
- function _interopNamespace(e) {
8
- if (e && e.__esModule) return e;
9
- var n = Object.create(null);
10
- if (e) {
11
- Object.keys(e).forEach(function (k) {
12
- if (k !== 'default') {
13
- var d = Object.getOwnPropertyDescriptor(e, k);
14
- Object.defineProperty(n, k, d.get ? d : {
15
- enumerable: true,
16
- get: function () { return e[k]; }
17
- });
18
- }
19
- });
20
- }
21
- n["default"] = e;
22
- return Object.freeze(n);
7
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
+
9
+ var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
10
+
11
+ const noProviderError = (item) => `${item} can only be used inside <StytchProvider>.`;
12
+ const providerMustBeUniqueError = 'You cannot render a <StytchProvider> inside another <StytchProvider>.';
13
+ const noSSRError = `The @stytch/react library is not meant for use with serverside environments like NextJS.
14
+ Use the @stytch/nextjs library instead -
15
+ npm remove @stytch/react && npm install @stytch/nextjs
16
+ `;
17
+ const noHeadlessClientError = `Tried to create a Stytch Login UI element using the Stytch Headless SDK.
18
+ You must use the UI SDK to use UI elements.
19
+ Please make sure you are importing from @stytch/vanilla-js and not from the @stytch/vanilla-js/headless.`;
20
+
21
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
+ function invariant(cond, message) {
23
+ if (!cond)
24
+ throw new Error(message);
23
25
  }
24
26
 
25
- var React__namespace = /*#__PURE__*/_interopNamespace(React);
27
+ // useState can cause memory leaks if it is set after the component unmounted. For example, if it is
28
+ // set after `await`, or in a `then`, `catch`, or `finally`, or in a setTimout/setInterval.
29
+ const useAsyncState = (initialState) => {
30
+ const isMounted = React.useRef(true);
31
+ const [state, setState] = React.useState(initialState);
32
+ React.useEffect(() => {
33
+ isMounted.current = true;
34
+ return () => {
35
+ isMounted.current = false;
36
+ };
37
+ }, []);
38
+ const setStateAction = React.useCallback((newState) => {
39
+ isMounted.current && setState(newState);
40
+ }, []);
41
+ return [state, setStateAction];
42
+ };
26
43
 
27
- const StytchUserContext = React__namespace.createContext(null);
28
- const StytchSessionContext = React__namespace.createContext(null);
29
- const StytchContext = React__namespace.createContext({});
44
+ const initialUser = {
45
+ user: null,
46
+ fromCache: false,
47
+ };
48
+ const initialSession = {
49
+ session: null,
50
+ fromCache: false,
51
+ };
52
+ const StytchContext = React.createContext({ isMounted: false });
53
+ const StytchUserContext = React.createContext(initialUser);
54
+ const StytchSessionContext = React.createContext(initialSession);
55
+ const useIsMounted__INTERNAL = () => React.useContext(StytchContext).isMounted;
30
56
  const isUIClient = (client) => {
31
- return client.mount !== undefined;
57
+ return client.mountLogin !== undefined;
32
58
  };
59
+ /**
60
+ * Returns the active User.
61
+ * Check the fromCache property to determine if the user data is from persistent storage.
62
+ * @example
63
+ * const {user} = useStytchUser();
64
+ * return (<h1>Welcome, {user.name.first_name}</h1>);
65
+ * @returns A {@link SWRUser}
66
+ */
33
67
  const useStytchUser = () => {
34
- return React__namespace.useContext(StytchUserContext);
68
+ invariant(useIsMounted__INTERNAL(), noProviderError('useStytchUser'));
69
+ return React.useContext(StytchUserContext);
35
70
  };
71
+ /**
72
+ * Returns the active user's Stytch session.
73
+ * @example
74
+ * const {session} = useStytchSession();
75
+ * useEffect(() => {
76
+ * if (!session) {
77
+ * router.replace('/login')
78
+ * }
79
+ * }, [session]);
80
+ * @returns A {@link SWRSession}
81
+ */
36
82
  const useStytchSession = () => {
37
- return React__namespace.useContext(StytchSessionContext);
83
+ invariant(useIsMounted__INTERNAL(), noProviderError('useStytchSession'));
84
+ return React.useContext(StytchSessionContext);
38
85
  };
86
+ /**
87
+ * Returns the Stytch client stored in the Stytch context.
88
+ *
89
+ * @example
90
+ * const stytch = useStytch();
91
+ * useEffect(() => {
92
+ * stytch.magicLinks.authenticate('...')
93
+ * }, [stytch]);
94
+ */
39
95
  const useStytch = () => {
40
- return React__namespace.useContext(StytchContext);
96
+ const ctx = React.useContext(StytchContext);
97
+ invariant(ctx.isMounted, noProviderError('useStytch'));
98
+ return ctx.client;
41
99
  };
42
100
  const withStytch = (Component) => {
43
101
  const WithStytch = (props) => {
44
- return React__namespace.createElement(Component, { ...props, stytch: useStytch() });
102
+ invariant(useIsMounted__INTERNAL(), noProviderError('withStytch'));
103
+ return React__default["default"].createElement(Component, Object.assign({}, props, { stytch: useStytch() }));
45
104
  };
46
105
  WithStytch.displayName = `withStytch(${Component.displayName || Component.name || 'Component'})`;
47
106
  return WithStytch;
48
107
  };
49
108
  const withStytchUser = (Component) => {
50
109
  const WithStytchUser = (props) => {
51
- return React__namespace.createElement(Component, { ...props, stytchUser: useStytchUser() });
110
+ invariant(useIsMounted__INTERNAL(), noProviderError('withStytchUser'));
111
+ const { user, fromCache } = useStytchUser();
112
+ return React__default["default"].createElement(Component, Object.assign({}, props, { stytchUser: user, stytchUserIsFromCache: fromCache }));
52
113
  };
53
114
  WithStytchUser.displayName = `withStytchUser(${Component.displayName || Component.name || 'Component'})`;
54
115
  return WithStytchUser;
55
116
  };
56
117
  const withStytchSession = (Component) => {
57
118
  const WithStytchSession = (props) => {
58
- return React__namespace.createElement(Component, { ...props, stytchSession: useStytchSession() });
119
+ invariant(useIsMounted__INTERNAL(), noProviderError('withStytchSession'));
120
+ const { session, fromCache } = useStytchSession();
121
+ return React__default["default"].createElement(Component, Object.assign({}, props, { stytchSession: session, stytchSessionIsFromCache: fromCache }));
59
122
  };
60
123
  WithStytchSession.displayName = `withStytchSession(${Component.displayName || Component.name || 'Component'})`;
61
124
  return WithStytchSession;
62
125
  };
126
+ /**
127
+ * The Stytch Context Provider.
128
+ * Wrap your application with this component in the root file in order to use Stytch everywhere in your app.
129
+ * @example
130
+ * const stytch = new StytchHeadlessClient('public-token-<find yours in the stytch dashboard>')
131
+ *
132
+ * ReactDOM.render(
133
+ * <StytchProvider stytch={stytch}>
134
+ * <App />
135
+ * </StytchProvider>,
136
+ * document.getElementById('root'),
137
+ * )
138
+ */
63
139
  const StytchProvider = ({ stytch, children }) => {
64
- const [user, setUser] = React__namespace.useState(null);
65
- const [session, setSession] = React__namespace.useState(null);
66
- React__namespace.useEffect(() => {
67
- const unsubscribeUser = stytch.user.onChange((user) => setUser(user));
68
- const unsubscribeSession = stytch.session.onChange((session) => setSession(session));
140
+ invariant(!useIsMounted__INTERNAL(), providerMustBeUniqueError);
141
+ invariant(typeof window !== 'undefined', noSSRError);
142
+ const ctx = React.useMemo(() => ({ client: stytch, isMounted: true }), [stytch]);
143
+ const [user, setUser] = useAsyncState({
144
+ user: stytch.user.getSync(),
145
+ fromCache: true,
146
+ });
147
+ const [session, setSession] = useAsyncState({
148
+ session: stytch.session.getSync(),
149
+ fromCache: true,
150
+ });
151
+ React.useEffect(() => {
152
+ const unsubscribeUser = stytch.user.onChange((user) => setUser({
153
+ user,
154
+ fromCache: false,
155
+ }));
156
+ const unsubscribeSession = stytch.session.onChange((session) => setSession({
157
+ session,
158
+ fromCache: false,
159
+ }));
69
160
  return () => {
70
161
  unsubscribeUser();
71
162
  unsubscribeSession();
72
163
  };
73
164
  }, [stytch, setUser, setSession]);
74
- return (React__namespace.createElement(StytchContext.Provider, { value: stytch },
75
- React__namespace.createElement(StytchUserContext.Provider, { value: session && user },
76
- React__namespace.createElement(StytchSessionContext.Provider, { value: user && session }, children))));
165
+ const finalSess = !!session.session === !!user.user ? session : initialSession;
166
+ const finalUser = !!session.session === !!user.user ? user : initialUser;
167
+ return (React__default["default"].createElement(StytchContext.Provider, { value: ctx },
168
+ React__default["default"].createElement(StytchUserContext.Provider, { value: finalUser },
169
+ React__default["default"].createElement(StytchSessionContext.Provider, { value: finalSess }, children))));
77
170
  };
78
171
 
79
172
  /**
80
- * Returns a unique element ID.
81
- * Client-side only - will return null for server-side
82
- * resolves: https://stytch.slack.com/archives/C015UDB4X33/p1641450131000800
83
- * based on: https://github.com/vercel/next.js/issues/7322#issuecomment-968858477
84
- */
85
- const useUniqueElementId = () => {
86
- const [elementId, setElementId] = React__namespace.useState(null);
87
- React__namespace.useEffect(() => {
88
- const randId = Math.floor(Math.random() * 1e6);
89
- setElementId(`stytch-magic-link-${randId}`);
90
- }, []);
91
- return elementId;
92
- };
93
- /**
94
- * Stytch JS React Component
173
+ * The Stytch Login Screen component.
174
+ * This component can only be used with a {@link StytchUIClient} client constructor
175
+ * passed into the {@link StytchProvider}
95
176
  *
96
- * [Documentation](https://stytch.com/docs/javascript-sdk)
177
+ * See the {@link https://stytch.com/docs/sdks/javascript-sdk online reference}
178
+ * and {@link https://storybook.stytch.com interactive examples} for more.
179
+ *
180
+ * @example
181
+ * <StytchLogin
182
+ * config={{
183
+ * products: ['emailMagicLinks', 'oauth'],
184
+ * emailMagicLinksOptions: {
185
+ * loginRedirectURL: 'https://example.com/authenticate',
186
+ * signupRedirectURL: 'https://example.com/authenticate',
187
+ * },
188
+ * oauthOptions: {
189
+ * providers: [{ type: OAuthProviders.Google }, { type: OAuthProviders.Microsoft }],
190
+ * },
191
+ * }}
192
+ * styles={{
193
+ * fontFamily: '"Helvetica New", Helvetica, sans-serif',
194
+ * primaryColor: '#0577CA',
195
+ * width: '321px',
196
+ * }}
197
+ * callbacks={{
198
+ * onEvent: (event) => console.log(event)
199
+ * }}
200
+ * />
201
+ * @param props {@link StytchProps}
97
202
  */
98
- const Stytch = ({ config, styles }) => {
203
+ const StytchLogin = ({ config, styles, callbacks }) => {
204
+ invariant(useIsMounted__INTERNAL(), noProviderError('<StytchLogin />'));
99
205
  const stytchClient = useStytch();
100
- const elementId = useUniqueElementId();
101
- React__namespace.useEffect(() => {
102
- if (!stytchClient || !elementId) {
206
+ const containerEl = React.useRef(null);
207
+ React.useLayoutEffect(() => {
208
+ if (!isUIClient(stytchClient)) {
209
+ throw Error(noHeadlessClientError);
210
+ }
211
+ if (!containerEl.current) {
103
212
  return;
104
213
  }
105
- if (!isUIClient(stytchClient)) {
106
- throw Error(`Tried to create a Stytch Login UI element using the Stytch Headless SDK.
107
- You must use the UI SDK to use UI elements.
108
- Please make sure you are importing from @stytch/vanilla-js and not from the @stytch/vanilla-js/headless.
109
- `);
214
+ if (!containerEl.current.id) {
215
+ const randId = Math.floor(Math.random() * 1e6);
216
+ containerEl.current.id = `stytch-magic-link-${randId}`;
110
217
  }
111
- stytchClient.mount({
218
+ stytchClient.mountLogin({
112
219
  config,
113
- elementId: `#${elementId}`,
220
+ callbacks,
221
+ elementId: `#${containerEl.current.id}`,
114
222
  styles,
115
223
  });
116
- }, [elementId, stytchClient]);
117
- return elementId ? React__namespace.createElement("div", { id: elementId }) : null;
224
+ }, [stytchClient, config, styles, callbacks]);
225
+ return React__default["default"].createElement("div", { ref: containerEl });
226
+ };
227
+ /**
228
+ * The Stytch Reset Password component.
229
+ * This component can only be used with a {@link StytchUIClient} client constructor
230
+ * passed into the {@link StytchProvider}
231
+ *
232
+ * See the {@link https://stytch.com/docs/sdks/javascript-sdk online reference}
233
+ * and {@link https://storybook.stytch.com interactive examples} for more.
234
+ *
235
+ * @example
236
+ * TODO
237
+ */
238
+ const StytchPasswordReset = ({ config, styles, callbacks, passwordResetToken }) => {
239
+ invariant(useIsMounted__INTERNAL(), noProviderError('<StytchResetPassword />'));
240
+ const stytchClient = useStytch();
241
+ const containerEl = React.useRef(null);
242
+ React.useLayoutEffect(() => {
243
+ if (!isUIClient(stytchClient)) {
244
+ throw Error(noHeadlessClientError);
245
+ }
246
+ if (!containerEl.current) {
247
+ return;
248
+ }
249
+ if (!containerEl.current.id) {
250
+ const randId = Math.floor(Math.random() * 1e6);
251
+ containerEl.current.id = `stytch-reset-password-${randId}`;
252
+ }
253
+ if (passwordResetToken) {
254
+ stytchClient.mountResetPassword({
255
+ config,
256
+ callbacks,
257
+ elementId: `#${containerEl.current.id}`,
258
+ styles,
259
+ passwordResetToken,
260
+ });
261
+ }
262
+ }, [stytchClient, config, styles, callbacks, passwordResetToken]);
263
+ return React__default["default"].createElement("div", { ref: containerEl });
118
264
  };
119
265
 
120
- exports.Stytch = Stytch;
266
+ exports.StytchLogin = StytchLogin;
267
+ exports.StytchPasswordReset = StytchPasswordReset;
121
268
  exports.StytchProvider = StytchProvider;
122
269
  exports.useStytch = useStytch;
123
270
  exports.useStytchSession = useStytchSession;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stytch/react",
3
- "version": "0.0.2-0",
3
+ "version": "0.2.0",
4
4
  "description": "Stytch's official React Library",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.esm.js",
@@ -12,21 +12,33 @@
12
12
  "build": "npm run clean && npm run compile",
13
13
  "clean": "rm -rf ./dist",
14
14
  "compile": "rollup -c",
15
- "dev": "tsc -p tsconfig.build.json --watch"
15
+ "dev": "rollup -c -w",
16
+ "test": "jest"
16
17
  },
17
18
  "license": "MIT",
19
+ "homepage": "https://stytch.com/docs/sdks/javascript-sdk",
18
20
  "author": "Stytch",
21
+ "keywords": [
22
+ "stytch",
23
+ "react",
24
+ "nextjs",
25
+ "typescript",
26
+ "auth",
27
+ "authentication",
28
+ "session",
29
+ "jwt",
30
+ "user"
31
+ ],
19
32
  "devDependencies": {
20
33
  "@babel/runtime": "^7.18.6",
21
- "@stytch/vanilla-js": "0.0.1",
22
- "eslint-config-custom": "0.0.0",
34
+ "@stytch/vanilla-js": "workspace:*",
35
+ "eslint-config-custom": "workspace:*",
23
36
  "rollup": "^2.56.3",
24
- "typescript": "^4.5.3"
37
+ "typescript": "4.7.4"
25
38
  },
26
39
  "peerDependencies": {
27
- "@stytch/vanilla-js": "^0.0.1",
40
+ "@stytch/vanilla-js": "^0.2.0",
28
41
  "react": ">= 17.0.2",
29
42
  "react-dom": ">= 17.0.2"
30
- },
31
- "stableVersion": "0.0.1"
32
- }
43
+ }
44
+ }