@dynamic-labs/react-native-extension 4.0.0-alpha.4 → 4.0.0-alpha.5
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/index.cjs +121 -2879
- package/index.js +281 -0
- package/package.json +8 -8
package/index.js
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { useRef, useEffect, useCallback, useState } from 'react';
|
|
2
|
+
import { WebView as WebView$1 } from 'react-native-webview';
|
|
3
|
+
import { parseMessageTransportData, createRequestChannel } from '@dynamic-labs/message-transport';
|
|
4
|
+
import { Logger } from '@dynamic-labs/logger';
|
|
5
|
+
import { StyleSheet } from 'react-native';
|
|
6
|
+
import { jsx } from 'react/jsx-runtime';
|
|
7
|
+
import { createPasskey, PasskeyStamper } from '@turnkey/react-native-passkey-stamper';
|
|
8
|
+
import { createURL, openURL } from 'expo-linking';
|
|
9
|
+
import { openAuthSessionAsync } from 'expo-web-browser';
|
|
10
|
+
import { getItemAsync, setItemAsync, deleteItemAsync } from 'expo-secure-store';
|
|
11
|
+
|
|
12
|
+
var version = "4.0.0-alpha.5";
|
|
13
|
+
|
|
14
|
+
function _extends() {
|
|
15
|
+
_extends = Object.assign ? Object.assign.bind() : function (target) {
|
|
16
|
+
for (var i = 1; i < arguments.length; i++) {
|
|
17
|
+
var source = arguments[i];
|
|
18
|
+
for (var key in source) {
|
|
19
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
20
|
+
target[key] = source[key];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return target;
|
|
25
|
+
};
|
|
26
|
+
return _extends.apply(this, arguments);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const logger = new Logger('react-native-extension');
|
|
30
|
+
|
|
31
|
+
const useIsMounted = () => {
|
|
32
|
+
const isMountedRef = useRef(true);
|
|
33
|
+
useEffect(() => () => {
|
|
34
|
+
isMountedRef.current = false;
|
|
35
|
+
}, []);
|
|
36
|
+
return useCallback(() => isMountedRef.current, [isMountedRef]);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const useMessageTransportWebViewBridge = (core, webViewRef) => {
|
|
40
|
+
const canBroadcastMessages = useIsMounted();
|
|
41
|
+
/**
|
|
42
|
+
* Receive a message from the webview and forward it to the client
|
|
43
|
+
* message transport
|
|
44
|
+
*/
|
|
45
|
+
const onMessageHandler = event => {
|
|
46
|
+
if (!canBroadcastMessages()) return;
|
|
47
|
+
let parsedData = null;
|
|
48
|
+
try {
|
|
49
|
+
parsedData = JSON.parse(event.nativeEvent.data);
|
|
50
|
+
} catch (err) {
|
|
51
|
+
logger.debug('error parsing data', err);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const message = parseMessageTransportData(parsedData);
|
|
55
|
+
if (!message) {
|
|
56
|
+
logger.debug('invalid message', message);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Only forward messages from the created by the webview
|
|
61
|
+
*/
|
|
62
|
+
if (message.origin === 'webview') {
|
|
63
|
+
core.messageTransport.emit(message);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Send messages created by the client to the webview
|
|
68
|
+
*/
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
const sendMessageToWebView = message => {
|
|
71
|
+
if (!canBroadcastMessages()) return;
|
|
72
|
+
/**
|
|
73
|
+
* Only forward messages to the webview
|
|
74
|
+
* that where created by the client/host
|
|
75
|
+
*/
|
|
76
|
+
if (message.origin === 'host' && webViewRef.current) {
|
|
77
|
+
webViewRef.current.postMessage(JSON.stringify(message));
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
core.messageTransport.on(sendMessageToWebView);
|
|
81
|
+
return () => {
|
|
82
|
+
core.messageTransport.off(sendMessageToWebView);
|
|
83
|
+
};
|
|
84
|
+
}, [core.messageTransport, webViewRef, canBroadcastMessages]);
|
|
85
|
+
return {
|
|
86
|
+
onMessageHandler
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const useWebViewVisibility = core => {
|
|
91
|
+
const webViewVisibilityRequestChannelRef = useRef(createRequestChannel(core.messageTransport));
|
|
92
|
+
const [visible, setVisible] = useState(false);
|
|
93
|
+
useEffect(() => webViewVisibilityRequestChannelRef.current.handle('setVisibility', setVisible), [setVisible]);
|
|
94
|
+
return {
|
|
95
|
+
visible
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const styles = StyleSheet.create({
|
|
100
|
+
container: {
|
|
101
|
+
backgroundColor: 'transparent',
|
|
102
|
+
bottom: 0,
|
|
103
|
+
flex: 1,
|
|
104
|
+
left: 0,
|
|
105
|
+
position: 'absolute',
|
|
106
|
+
right: 0,
|
|
107
|
+
top: 0,
|
|
108
|
+
width: '100%'
|
|
109
|
+
},
|
|
110
|
+
hide: {
|
|
111
|
+
elevation: 0,
|
|
112
|
+
opacity: 0,
|
|
113
|
+
zIndex: -10000
|
|
114
|
+
},
|
|
115
|
+
show: {
|
|
116
|
+
elevation: 10000,
|
|
117
|
+
opacity: 1,
|
|
118
|
+
zIndex: 10000
|
|
119
|
+
},
|
|
120
|
+
webview: {
|
|
121
|
+
backgroundColor: 'transparent',
|
|
122
|
+
flex: 1
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const WebView = ({
|
|
127
|
+
webviewUrl,
|
|
128
|
+
core,
|
|
129
|
+
webviewDebuggingEnabled: _webviewDebuggingEnabled = false
|
|
130
|
+
}) => {
|
|
131
|
+
const webViewRef = useRef(null);
|
|
132
|
+
const {
|
|
133
|
+
visible
|
|
134
|
+
} = useWebViewVisibility(core);
|
|
135
|
+
const {
|
|
136
|
+
onMessageHandler
|
|
137
|
+
} = useMessageTransportWebViewBridge(core, webViewRef);
|
|
138
|
+
const containerStyles = [styles['container'], visible ? styles.show : styles.hide];
|
|
139
|
+
return /*#__PURE__*/jsx(WebView$1, {
|
|
140
|
+
ref: webViewRef,
|
|
141
|
+
source: {
|
|
142
|
+
uri: webviewUrl
|
|
143
|
+
},
|
|
144
|
+
containerStyle: containerStyles,
|
|
145
|
+
style: styles['webview'],
|
|
146
|
+
onMessage: onMessageHandler,
|
|
147
|
+
hideKeyboardAccessoryView: true,
|
|
148
|
+
webviewDebuggingEnabled: _webviewDebuggingEnabled,
|
|
149
|
+
onError: () => core.initialization.error = new Error('Could not load Dynamic WebView')
|
|
150
|
+
}, 'webview');
|
|
151
|
+
};
|
|
152
|
+
const createWebView = props => {
|
|
153
|
+
const WebViewWrapper = () => /*#__PURE__*/jsx(WebView, _extends({}, props));
|
|
154
|
+
return WebViewWrapper;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/******************************************************************************
|
|
158
|
+
Copyright (c) Microsoft Corporation.
|
|
159
|
+
|
|
160
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
161
|
+
purpose with or without fee is hereby granted.
|
|
162
|
+
|
|
163
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
164
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
165
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
166
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
167
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
168
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
169
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
170
|
+
***************************************************************************** */
|
|
171
|
+
|
|
172
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
173
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
174
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
175
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
176
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
177
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
178
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
183
|
+
var e = new Error(message);
|
|
184
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const setupFetchHandler = core => {
|
|
188
|
+
const fetchRequestChannel = createRequestChannel(core.messageTransport);
|
|
189
|
+
fetchRequestChannel.handle('fetch', (input, init) => __awaiter(void 0, void 0, void 0, function* () {
|
|
190
|
+
const response = yield fetch(input, init);
|
|
191
|
+
const responseBody = yield response.text();
|
|
192
|
+
return {
|
|
193
|
+
body: responseBody,
|
|
194
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
195
|
+
headers: Object.fromEntries(response.headers.entries()),
|
|
196
|
+
status: response.status,
|
|
197
|
+
statusText: response.statusText
|
|
198
|
+
};
|
|
199
|
+
}));
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const setupPasskeyHandler = core => {
|
|
203
|
+
const passkeysRequestChannel = createRequestChannel(core.messageTransport);
|
|
204
|
+
passkeysRequestChannel.handle('createPasskey', _a => __awaiter(void 0, [_a], void 0, function* ({
|
|
205
|
+
publicKey
|
|
206
|
+
}) {
|
|
207
|
+
try {
|
|
208
|
+
const {
|
|
209
|
+
attestation
|
|
210
|
+
} = yield createPasskey(publicKey);
|
|
211
|
+
return {
|
|
212
|
+
attestation
|
|
213
|
+
};
|
|
214
|
+
} catch (err) {
|
|
215
|
+
logger.error(err);
|
|
216
|
+
throw err;
|
|
217
|
+
}
|
|
218
|
+
}));
|
|
219
|
+
passkeysRequestChannel.handle('passkeyStamp', (rpId, payload) => __awaiter(void 0, void 0, void 0, function* () {
|
|
220
|
+
try {
|
|
221
|
+
const stamper = new PasskeyStamper({
|
|
222
|
+
rpId
|
|
223
|
+
});
|
|
224
|
+
const stamped = yield stamper.stamp(payload);
|
|
225
|
+
return stamped;
|
|
226
|
+
} catch (err) {
|
|
227
|
+
logger.error(err);
|
|
228
|
+
throw err;
|
|
229
|
+
}
|
|
230
|
+
}));
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const setupPlatformHandler = core => {
|
|
234
|
+
const platformChannel = createRequestChannel(core.messageTransport);
|
|
235
|
+
core.manifest.setPlatform('react-native');
|
|
236
|
+
core.manifest.setRedirectUrl(createURL(''));
|
|
237
|
+
platformChannel.handle('openURL', url => __awaiter(void 0, void 0, void 0, function* () {
|
|
238
|
+
yield openURL(url);
|
|
239
|
+
}));
|
|
240
|
+
platformChannel.handle('openAuthenticationWindow', _a => __awaiter(void 0, [_a], void 0, function* ({
|
|
241
|
+
url,
|
|
242
|
+
redirectUrl
|
|
243
|
+
}) {
|
|
244
|
+
const result = yield openAuthSessionAsync(url, redirectUrl);
|
|
245
|
+
// When not a success, result can only have a type which is a string describing
|
|
246
|
+
// what happened in a single word.
|
|
247
|
+
if (result.type !== 'success') throw new Error(result.type);
|
|
248
|
+
return result.url;
|
|
249
|
+
}));
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const setupSecureStorageHandler = core => {
|
|
253
|
+
const secureStorageRequestChannel = createRequestChannel(core.messageTransport);
|
|
254
|
+
secureStorageRequestChannel.handle('getSecureItem', getItemAsync);
|
|
255
|
+
secureStorageRequestChannel.handle('setSecureItem', setItemAsync);
|
|
256
|
+
secureStorageRequestChannel.handle('deleteSecureItem', deleteItemAsync);
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const defaultWebviewUrl = `https://webview.dynamicauth.com/${version}`;
|
|
260
|
+
const ReactNativeExtension = ({
|
|
261
|
+
webviewUrl: _webviewUrl = defaultWebviewUrl,
|
|
262
|
+
webviewDebuggingEnabled,
|
|
263
|
+
appOrigin
|
|
264
|
+
} = {}) => (_, core) => {
|
|
265
|
+
if (appOrigin) core.manifest.setAppOrigin(appOrigin);
|
|
266
|
+
setupPasskeyHandler(core);
|
|
267
|
+
setupFetchHandler(core);
|
|
268
|
+
setupPlatformHandler(core);
|
|
269
|
+
setupSecureStorageHandler(core);
|
|
270
|
+
return {
|
|
271
|
+
reactNative: {
|
|
272
|
+
WebView: createWebView({
|
|
273
|
+
core,
|
|
274
|
+
webviewDebuggingEnabled,
|
|
275
|
+
webviewUrl: _webviewUrl
|
|
276
|
+
})
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
export { ReactNativeExtension };
|
package/package.json
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dynamic-labs/react-native-extension",
|
|
3
|
-
"version": "4.0.0-alpha.
|
|
3
|
+
"version": "4.0.0-alpha.5",
|
|
4
4
|
"main": "./index.cjs",
|
|
5
|
-
"module": "./
|
|
5
|
+
"module": "./index.js",
|
|
6
6
|
"types": "./src/index.d.ts",
|
|
7
|
-
"type": "
|
|
7
|
+
"type": "module",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
10
|
"types": "./src/index.d.ts",
|
|
11
|
-
"import": "./
|
|
12
|
-
"require": "./
|
|
11
|
+
"import": "./index.js",
|
|
12
|
+
"require": "./index.cjs"
|
|
13
13
|
},
|
|
14
14
|
"./package.json": "./package.json"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@turnkey/react-native-passkey-stamper": "0.2.5",
|
|
18
|
-
"@dynamic-labs/client": "4.0.0-alpha.
|
|
19
|
-
"@dynamic-labs/logger": "4.0.0-alpha.
|
|
20
|
-
"@dynamic-labs/message-transport": "4.0.0-alpha.
|
|
18
|
+
"@dynamic-labs/client": "4.0.0-alpha.5",
|
|
19
|
+
"@dynamic-labs/logger": "4.0.0-alpha.5",
|
|
20
|
+
"@dynamic-labs/message-transport": "4.0.0-alpha.5"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
23
|
"react": "^18.2.0",
|