@tryfinch/react-connect 4.0.0 → 5.0.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.
@@ -1,35 +0,0 @@
1
- {
2
- "name": "react-finch-connect-example",
3
- "homepage": "https://.github.io/react-finch-connect",
4
- "version": "0.0.0",
5
- "license": "MIT",
6
- "private": true,
7
- "dependencies": {
8
- "@tryfinch/react-connect": "file:..",
9
- "@types/react": "^17.0.37",
10
- "prop-types": "^15.6.2",
11
- "react": "file:../node_modules/react",
12
- "react-dom": "^16.9.0"
13
- },
14
- "scripts": {
15
- "start": "react-scripts start",
16
- "build": "react-scripts build",
17
- "test": "react-scripts test --env=jsdom",
18
- "eject": "react-scripts eject"
19
- },
20
- "eslintConfig": {
21
- "extends": [
22
- "react-app",
23
- "react-app/jest"
24
- ]
25
- },
26
- "browserslist": [
27
- ">0.2%",
28
- "not dead",
29
- "not ie <= 11",
30
- "not op_mini all"
31
- ],
32
- "devDependencies": {
33
- "react-scripts": "^5.0.1"
34
- }
35
- }
@@ -1,20 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
6
- <meta name="theme-color" content="#000000">
7
-
8
- <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
9
-
10
- <title>react-finch-connect</title>
11
- </head>
12
-
13
- <body>
14
- <noscript>
15
- You need to enable JavaScript to run this app.
16
- </noscript>
17
-
18
- <div id="root"></div>
19
- </body>
20
- </html>
@@ -1,8 +0,0 @@
1
- {
2
- "short_name": "react-finch-connect",
3
- "name": "react-finch-connect",
4
- "start_url": "./index.html",
5
- "display": "standalone",
6
- "theme_color": "#000000",
7
- "background_color": "#ffffff"
8
- }
@@ -1,49 +0,0 @@
1
- .container {
2
- text-align: center;
3
- background-color: #282c34;
4
- min-height: 100vh;
5
- display: flex;
6
- flex-direction: column;
7
- align-items: center;
8
- justify-content: center;
9
- font-size: calc(7px + 2vmin);
10
- color: white;
11
- }
12
-
13
- a {
14
- color: white;
15
- }
16
-
17
- .actions {
18
- background-color: #4f4f4f;
19
- padding: 1em;
20
- border-radius: 10px;
21
- margin: 1em 0;
22
- }
23
-
24
- .cta {
25
- font-size: calc(7px + 2vmin);
26
- padding: .5em;
27
- }
28
-
29
- .row {
30
- margin: 2em 0;
31
- }
32
-
33
- .top-label {
34
- display: block;
35
- margin-bottom: .2em;
36
- }
37
-
38
- input[type=checkbox] {
39
- width: 2em;
40
- height: 2em;
41
- }
42
-
43
- .results {
44
- background-color: #4f4f4f;
45
- padding: 1em;
46
- border-radius: 10px;
47
- text-align: left;
48
- margin: 1em 0;
49
- }
@@ -1,80 +0,0 @@
1
- import React, { useState } from 'react';
2
- import { useFinchConnect, SuccessEvent, ErrorEvent } from '@tryfinch/react-connect';
3
-
4
- import Result, { ResultContainer } from './Result';
5
-
6
- import './App.css';
7
-
8
- const App = () => {
9
- const [sessionId, setSessionId] = useState<string>('');
10
- const [sendState, setSendState] = useState<boolean>(false);
11
- const [result, setResult] = useState<ResultContainer>();
12
-
13
- // Define callbacks
14
- const onSuccess = (value: SuccessEvent) => setResult({ kind: 'success', value });
15
- const onError = (value: ErrorEvent) => setResult({ kind: 'error', value });
16
- const onClose = () => setResult({ kind: 'closed' });
17
-
18
- // Initialize the FinchConnect hook
19
- const { open } = useFinchConnect({
20
- onSuccess,
21
- onError,
22
- onClose,
23
- });
24
-
25
- // Call the open method when submitting the form
26
- const submissionHandler: React.FormEventHandler<HTMLFormElement> = (e) => {
27
- e.preventDefault();
28
- open({
29
- // Generate a session ID using the /connect/sessions endpoint on the Finch API
30
- // See the docs here https://developer.tryfinch.com/api-reference/connect/new-session#create-a-new-connect-session
31
- sessionId: sessionId.trim(),
32
- // An optional state parameter can be passed
33
- // https://datatracker.ietf.org/doc/html/rfc6749#section-10.12
34
- ...(sendState ? { state: new Date().toISOString() } : {}),
35
- // An optional value for the z-index of the Finch Connect iframe
36
- // Defaults to 999 if not provided
37
- // zIndex: 998,
38
- });
39
- };
40
-
41
- return (
42
- <div className="container">
43
- <h2>
44
- <a href="https://www.npmjs.com/package/@tryfinch/react-connect">@tryfinch/react-connect</a>{' '}
45
- Example App
46
- </h2>
47
- <form className="actions" onSubmit={submissionHandler}>
48
- <div className="row">
49
- <label className="top-label">Session UUID:</label>
50
- <input
51
- type="text"
52
- placeholder="Enter session UUID"
53
- value={sessionId}
54
- onChange={(e) => setSessionId(e.target.value)}
55
- />
56
- </div>
57
- <div className="row">
58
- <label className="top-label">Include State:</label>
59
- <input
60
- type="checkbox"
61
- checked={sendState}
62
- onChange={() => setSendState((prev) => !prev)}
63
- />
64
- </div>
65
- <div className="row">
66
- <button className="cta" type="submit">
67
- Open Finch Connect
68
- </button>
69
- </div>
70
- </form>
71
- <div className="results">
72
- {!result && (
73
- <p>Complete a Finch Connect session and the success event will be displayed here</p>
74
- )}
75
- {result && <Result result={result} />}
76
- </div>
77
- </div>
78
- );
79
- };
80
- export default App;
@@ -1,26 +0,0 @@
1
- import { SuccessEvent, ErrorEvent } from '@tryfinch/react-connect';
2
-
3
- export type ResultContainer = {
4
- kind: 'success';
5
- value: SuccessEvent;
6
- } | {
7
- kind: 'error';
8
- value: ErrorEvent;
9
- } | {
10
- kind: 'closed',
11
- };
12
-
13
- const Result = ({ result }: { result: ResultContainer }) => {
14
- if (result.kind === 'closed') {
15
- return <>
16
- <p>Closed!</p>
17
- </>;
18
- }
19
-
20
- return <>
21
- <p>{ result.kind === 'error' ? 'Error' : 'Success' }:</p>
22
- <pre>{ JSON.stringify(result.value, null, 2) }</pre>
23
- </>;
24
- };
25
-
26
- export default Result
@@ -1,11 +0,0 @@
1
- body {
2
- margin: 0;
3
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
4
- 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
5
- -webkit-font-smoothing: antialiased;
6
- -moz-osx-font-smoothing: grayscale;
7
- }
8
-
9
- code {
10
- font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
11
- }
@@ -1,7 +0,0 @@
1
- import React from 'react';
2
- import ReactDOM from 'react-dom';
3
-
4
- import './index.css';
5
- import App from './App';
6
-
7
- ReactDOM.render(<App />, document.getElementById('root'));
@@ -1 +0,0 @@
1
- /// <reference types="react-scripts" />
@@ -1,26 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "es5",
4
- "lib": [
5
- "dom",
6
- "dom.iterable",
7
- "esnext"
8
- ],
9
- "allowJs": true,
10
- "skipLibCheck": true,
11
- "esModuleInterop": true,
12
- "allowSyntheticDefaultImports": true,
13
- "strict": true,
14
- "forceConsistentCasingInFileNames": true,
15
- "module": "esnext",
16
- "moduleResolution": "node",
17
- "resolveJsonModule": true,
18
- "isolatedModules": true,
19
- "noEmit": true,
20
- "jsx": "react-jsx",
21
- "noFallthroughCasesInSwitch": true
22
- },
23
- "include": [
24
- "src"
25
- ]
26
- }
package/jest.config.js DELETED
@@ -1,7 +0,0 @@
1
- module.exports = {
2
- preset: 'ts-jest',
3
- testEnvironment: 'jsdom',
4
- roots: ['<rootDir>/src'],
5
- testMatch: ['**/__tests__/**/*.test.ts', '**/*.test.ts'],
6
- collectCoverageFrom: ['src/**/*.{ts,tsx}', '!src/**/*.d.ts'],
7
- };
package/rollup.config.js DELETED
@@ -1,22 +0,0 @@
1
- import external from 'rollup-plugin-peer-deps-external';
2
- import replace from '@rollup/plugin-replace';
3
- import typescript from '@rollup/plugin-typescript';
4
-
5
- import pkg from './package.json';
6
-
7
- const plugins = [
8
- external(),
9
- replace({ SDK_VERSION: pkg.version }),
10
- typescript({ sourceRoot: '.' }),
11
- ];
12
-
13
- export default [
14
- {
15
- input: 'src/index.ts',
16
- output: [
17
- { file: pkg.main, format: 'cjs', sourcemap: true },
18
- { file: pkg.module, format: 'es', sourcemap: true },
19
- ],
20
- plugins,
21
- },
22
- ];
package/src/index.test.ts DELETED
@@ -1,54 +0,0 @@
1
- import { constructAuthUrl } from './index';
2
-
3
- const NOOP_CALLBACKS = {
4
- onSuccess: jest.fn(),
5
- onError: jest.fn(),
6
- onClose: jest.fn(),
7
- zIndex: 999,
8
- };
9
-
10
- describe('Finch React SDK', () => {
11
- describe('constructAuthUrl', () => {
12
- it('adds the session parameter', () => {
13
- const authUrl = constructAuthUrl({
14
- sessionId: 'test-session-id',
15
- });
16
- expect(authUrl).toContain('session=test-session-id');
17
- });
18
-
19
- it('adds all the expected base parameters to the auth URL', () => {
20
- const expectedParameters = {
21
- app_type: 'spa',
22
- sdk_host_url: encodeURIComponent('http://localhost'),
23
- mode: 'employer',
24
- sdk_version: 'react-SDK_VERSION',
25
- };
26
-
27
- const authUrl = constructAuthUrl({
28
- sessionId: 'test-session-id',
29
- });
30
-
31
- Object.entries(expectedParameters).forEach(([key, value]) => {
32
- expect(authUrl).toContain(`${key}=${value}`);
33
- });
34
- });
35
-
36
- it('adds the state parameter if it is provided', () => {
37
- const testOptions = { sessionId: 'test-session-id', state: 'test-state', ...NOOP_CALLBACKS };
38
- const authUrl = constructAuthUrl(testOptions);
39
-
40
- expect(authUrl).toContain('state=test-state');
41
- });
42
-
43
- it('uses the provided connectUrl if provided', () => {
44
- const authUrl = constructAuthUrl({
45
- sessionId: '123',
46
- apiConfig: {
47
- connectUrl: 'https://cool.site',
48
- },
49
- ...NOOP_CALLBACKS,
50
- });
51
- expect(authUrl.startsWith('https://cool.site/authorize?')).toBe(true);
52
- });
53
- });
54
- });
package/src/index.ts DELETED
@@ -1,186 +0,0 @@
1
- import { useEffect, useRef } from 'react';
2
-
3
- export type SuccessEvent = {
4
- code: string;
5
- state?: string;
6
- idpRedirectUri?: string;
7
- };
8
-
9
- type ErrorType = 'validation_error' | 'employer_connection_error';
10
- export type ErrorEvent = {
11
- errorMessage: string;
12
- errorType?: ErrorType;
13
- };
14
-
15
- type ApiConfig = {
16
- connectUrl: string;
17
- };
18
-
19
- export type ConnectInitializeArgs = {
20
- onSuccess: (e: SuccessEvent) => void;
21
- onError: (e: ErrorEvent) => void;
22
- onClose: () => void;
23
- apiConfig?: ApiConfig;
24
- };
25
-
26
- export type ConnectLaunchArgs = {
27
- sessionId: string;
28
- state?: string;
29
- zIndex?: number;
30
- };
31
-
32
- type OpenFn = (args: ConnectLaunchArgs) => void;
33
-
34
- const POST_MESSAGE_NAME = 'finch-auth-message-v2' as const;
35
-
36
- type FinchConnectAuthMessage = { name: typeof POST_MESSAGE_NAME } & (
37
- | {
38
- kind: 'closed';
39
- }
40
- | {
41
- kind: 'success';
42
- code: string;
43
- state?: string;
44
- idpRedirectUri?: string;
45
- }
46
- | {
47
- kind: 'error';
48
- error: { shouldClose: boolean; message: string; type: ErrorType };
49
- }
50
- );
51
-
52
- interface FinchConnectPostMessage {
53
- data: FinchConnectAuthMessage;
54
- origin: string;
55
- }
56
-
57
- const BASE_FINCH_CONNECT_URI = 'https://connect.tryfinch.com';
58
-
59
- const FINCH_CONNECT_IFRAME_ID = 'finch-connect-iframe';
60
-
61
- export const constructAuthUrl = ({
62
- sessionId,
63
- state,
64
- apiConfig,
65
- }: {
66
- sessionId: string;
67
- state?: string;
68
- apiConfig?: ApiConfig;
69
- }) => {
70
- const CONNECT_URL = apiConfig?.connectUrl || BASE_FINCH_CONNECT_URI;
71
-
72
- const authUrl = new URL(`${CONNECT_URL}/authorize`);
73
-
74
- authUrl.searchParams.append('session', sessionId);
75
- authUrl.searchParams.append('app_type', 'spa');
76
- /** The host URL of the SDK. This is used to store the referrer for postMessage purposes */
77
- authUrl.searchParams.append('sdk_host_url', window.location.origin);
78
- authUrl.searchParams.append('mode', 'employer');
79
- if (state) authUrl.searchParams.append('state', state);
80
- // replace with actual SDK version by rollup
81
- authUrl.searchParams.append('sdk_version', 'react-SDK_VERSION');
82
-
83
- return authUrl.href;
84
- };
85
-
86
- let isUseFinchConnectInitialized = false;
87
-
88
- export const useFinchConnect = (initializeArgs: ConnectInitializeArgs): { open: OpenFn } => {
89
- const isHookMounted = useRef(false);
90
-
91
- useEffect(() => {
92
- if (!isHookMounted.current) {
93
- if (isUseFinchConnectInitialized) {
94
- console.error(
95
- 'One useFinchConnect hook has already been registered. Please ensure to only call useFinchConnect once to avoid your event callbacks getting called more than once. You can pass in override options to the open function if you so require.'
96
- );
97
- } else {
98
- isUseFinchConnectInitialized = true;
99
- }
100
-
101
- isHookMounted.current = true;
102
- }
103
- }, []);
104
-
105
- const open: OpenFn = (launchArgs) => {
106
- if (!document.getElementById(FINCH_CONNECT_IFRAME_ID)) {
107
- const iframe = document.createElement('iframe');
108
- iframe.src = constructAuthUrl({
109
- sessionId: launchArgs.sessionId,
110
- state: launchArgs.state,
111
- apiConfig: initializeArgs.apiConfig,
112
- });
113
- iframe.frameBorder = '0';
114
- iframe.id = FINCH_CONNECT_IFRAME_ID;
115
- iframe.style.position = 'fixed';
116
- iframe.style.zIndex = launchArgs.zIndex?.toString() || '999';
117
- iframe.style.height = '100%';
118
- iframe.style.width = '100%';
119
- iframe.style.top = '0';
120
- iframe.style.backgroundColor = 'none transparent';
121
- iframe.style.border = 'none';
122
- iframe.allow = 'clipboard-write; clipboard-read';
123
- document.body.prepend(iframe);
124
- document.body.style.overflow = 'hidden';
125
- }
126
- };
127
-
128
- const close = () => {
129
- const frameToRemove = document.getElementById(FINCH_CONNECT_IFRAME_ID);
130
- if (frameToRemove) {
131
- frameToRemove.parentNode?.removeChild(frameToRemove);
132
- document.body.style.overflow = 'inherit';
133
- }
134
- };
135
-
136
- useEffect(() => {
137
- function handleFinchAuth(event: FinchConnectPostMessage) {
138
- const CONNECT_URL = initializeArgs.apiConfig?.connectUrl || BASE_FINCH_CONNECT_URI;
139
-
140
- if (!event.data) return;
141
- if (event.data.name !== POST_MESSAGE_NAME) return;
142
- if (!event.origin.startsWith(CONNECT_URL)) return;
143
-
144
- if (event.data.kind !== 'error') close();
145
-
146
- switch (event.data.kind) {
147
- case 'closed':
148
- initializeArgs.onClose();
149
- break;
150
- case 'error':
151
- if (event.data.error?.shouldClose) close();
152
-
153
- initializeArgs.onError({
154
- errorMessage: event.data.error?.message,
155
- errorType: event.data.error?.type,
156
- });
157
- break;
158
- case 'success':
159
- initializeArgs.onSuccess({
160
- code: event.data.code,
161
- state: event.data.state,
162
- idpRedirectUri: event.data.idpRedirectUri,
163
- });
164
- break;
165
- default: {
166
- // This case should never happen, if it does it should be reported to us
167
- initializeArgs.onError({
168
- errorMessage: `Report to developers@tryfinch.com: unable to handle window.postMessage for: ${JSON.stringify(
169
- event.data
170
- )}`,
171
- });
172
- }
173
- }
174
- }
175
-
176
- window.addEventListener('message', handleFinchAuth);
177
- return () => {
178
- window.removeEventListener('message', handleFinchAuth);
179
- isUseFinchConnectInitialized = false;
180
- };
181
- }, [initializeArgs.onClose, initializeArgs.onError, initializeArgs.onSuccess]);
182
-
183
- return {
184
- open,
185
- };
186
- };
package/tsconfig.json DELETED
@@ -1,15 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "es2016",
4
- "module": "esnext",
5
- "declaration": true,
6
- "declarationMap": true,
7
- "sourceMap": true,
8
- "outDir": "./",
9
- "esModuleInterop": true,
10
- "forceConsistentCasingInFileNames": true,
11
- "strict": true
12
- },
13
- "include": ["src/**/*"],
14
- "exclude": ["node_modules", "example/**/*"]
15
- }