@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 +94 -8
- package/dist/index.d.ts +187 -12
- package/dist/index.esm.d.ts +187 -12
- package/dist/index.esm.js +211 -49
- package/dist/index.js +212 -65
- package/package.json +21 -9
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
3
|
-
|
|
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
|
|
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
|
-
*
|
|
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
|
-
|
|
16
|
-
|
|
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?:
|
|
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 {
|
|
207
|
+
export { StytchLogin, StytchPasswordReset, StytchProvider, useStytch, useStytchSession, useStytchUser, withStytch, withStytchSession, withStytchUser };
|
|
33
208
|
export type { StytchProviderProps };
|
package/dist/index.esm.d.ts
CHANGED
|
@@ -1,33 +1,208 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
|
|
3
|
-
|
|
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
|
|
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
|
-
*
|
|
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
|
-
|
|
16
|
-
|
|
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?:
|
|
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 {
|
|
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
|
|
1
|
+
import React, { useRef, useState, useEffect, useCallback, createContext, useContext, useMemo, useLayoutEffect } from 'react';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
*
|
|
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
|
|
195
|
+
const StytchLogin = ({ config, styles, callbacks }) => {
|
|
196
|
+
invariant(useIsMounted__INTERNAL(), noProviderError('<StytchLogin />'));
|
|
75
197
|
const stytchClient = useStytch();
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
if (!stytchClient
|
|
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 (!
|
|
82
|
-
|
|
83
|
-
|
|
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.
|
|
210
|
+
stytchClient.mountLogin({
|
|
88
211
|
config,
|
|
89
|
-
|
|
212
|
+
callbacks,
|
|
213
|
+
elementId: `#${containerEl.current.id}`,
|
|
90
214
|
styles,
|
|
91
215
|
});
|
|
92
|
-
}, [
|
|
93
|
-
return
|
|
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 {
|
|
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
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
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
|
|
28
|
-
|
|
29
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
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
|
-
*
|
|
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
|
|
203
|
+
const StytchLogin = ({ config, styles, callbacks }) => {
|
|
204
|
+
invariant(useIsMounted__INTERNAL(), noProviderError('<StytchLogin />'));
|
|
99
205
|
const stytchClient = useStytch();
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
if (!stytchClient
|
|
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 (!
|
|
106
|
-
|
|
107
|
-
|
|
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.
|
|
218
|
+
stytchClient.mountLogin({
|
|
112
219
|
config,
|
|
113
|
-
|
|
220
|
+
callbacks,
|
|
221
|
+
elementId: `#${containerEl.current.id}`,
|
|
114
222
|
styles,
|
|
115
223
|
});
|
|
116
|
-
}, [
|
|
117
|
-
return
|
|
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.
|
|
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.
|
|
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": "
|
|
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": "
|
|
22
|
-
"eslint-config-custom": "
|
|
34
|
+
"@stytch/vanilla-js": "workspace:*",
|
|
35
|
+
"eslint-config-custom": "workspace:*",
|
|
23
36
|
"rollup": "^2.56.3",
|
|
24
|
-
"typescript": "
|
|
37
|
+
"typescript": "4.7.4"
|
|
25
38
|
},
|
|
26
39
|
"peerDependencies": {
|
|
27
|
-
"@stytch/vanilla-js": "^0.0
|
|
40
|
+
"@stytch/vanilla-js": "^0.2.0",
|
|
28
41
|
"react": ">= 17.0.2",
|
|
29
42
|
"react-dom": ">= 17.0.2"
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
}
|
|
43
|
+
}
|
|
44
|
+
}
|