@ukeyfe/hardware-web-sdk 1.1.13
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 +34 -0
- package/build/data/coins/bitcoin.json +44 -0
- package/build/data/config.ts +25 -0
- package/build/data/messages/messages.json +13066 -0
- package/build/data/messages/messages_legacy_v1.json +10270 -0
- package/build/iframe.html +32 -0
- package/build/js/iframe.5135b718e3017cf5f9ef.js +3 -0
- package/build/js/iframe.5135b718e3017cf5f9ef.js.LICENSE.txt +51 -0
- package/build/js/iframe.5135b718e3017cf5f9ef.js.map +1 -0
- package/build/ukey-js-sdk.js +89919 -0
- package/build/ukey-js-sdk.js.map +1 -0
- package/build/ukey-js-sdk.min.js +1 -0
- package/build/ukey-js-sdk.min.js.map +1 -0
- package/package.json +48 -0
- package/src/iframe/bridge-config.ts +13 -0
- package/src/iframe/builder.ts +98 -0
- package/src/iframe/index.ts +127 -0
- package/src/index.ts +302 -0
- package/src/utils/bridgeUtils.ts +59 -0
- package/src/utils/urlUtils.ts +20 -0
- package/static/iframe.html +32 -0
- package/tsconfig.json +8 -0
- package/webpack/dev.webpack.config.ts +32 -0
- package/webpack/https_dev.crt +31 -0
- package/webpack/https_dev.key +51 -0
- package/webpack/iframe.webpack.config.ts +71 -0
- package/webpack/prod.webpack.config.ts +69 -0
- package/webpack/webpack.config.ts +27 -0
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ukeyfe/hardware-web-sdk",
|
|
3
|
+
"version": "1.1.13",
|
|
4
|
+
"author": "UKey",
|
|
5
|
+
"homepage": "https://github.com/UKeyHQ/hardware-js-sdk#readme",
|
|
6
|
+
"license": "ISC",
|
|
7
|
+
"type": "commonjs",
|
|
8
|
+
"main": "build/ukey-js-sdk.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"dev": "rimraf build && cross-env TS_NODE_PROJECT=\"../../tsconfig.webpack.json\" webpack --config ./webpack/dev.webpack.config.ts",
|
|
15
|
+
"build:lib": "rimraf ./dist && tsc --build tsconfig.json",
|
|
16
|
+
"build:iframe": "cross-env TS_NODE_PROJECT=\"../../tsconfig.webpack.json\" webpack --config ./webpack/iframe.webpack.config.ts",
|
|
17
|
+
"build:sdk": "rimraf build && cross-env TS_NODE_PROJECT=\"../../tsconfig.webpack.json\" webpack --config ./webpack/prod.webpack.config.ts",
|
|
18
|
+
"build": "yarn build:sdk && yarn build:iframe && yarn build:lib",
|
|
19
|
+
"lint": "eslint .",
|
|
20
|
+
"lint:fix": "eslint . --fix"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@ukeyfe/cross-inpage-provider-core": "^2.2.47",
|
|
24
|
+
"@ukeyfe/hardware-core": "1.1.13",
|
|
25
|
+
"@ukeyfe/hardware-shared": "1.1.13",
|
|
26
|
+
"@ukeyfe/hardware-transport-http": "1.1.13",
|
|
27
|
+
"@ukeyfe/hardware-transport-web-device": "1.1.13"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@babel/plugin-proposal-optional-chaining": "^7.17.12",
|
|
31
|
+
"@types/node": "^17.0.35",
|
|
32
|
+
"@types/webpack": "^5.28.0",
|
|
33
|
+
"@types/webpack-plugin-serve": "^1.4.2",
|
|
34
|
+
"assert": "^2.0.0",
|
|
35
|
+
"copy-webpack-plugin": "^11.0.0",
|
|
36
|
+
"crypto-browserify": "^3.12.0",
|
|
37
|
+
"es6-promise": "^4.2.8",
|
|
38
|
+
"events": "^3.3.0",
|
|
39
|
+
"html-webpack-plugin": "^5.5.0",
|
|
40
|
+
"process": "^0.11.10",
|
|
41
|
+
"stream-browserify": "^3.0.0",
|
|
42
|
+
"terser-webpack-plugin": "^5.3.1",
|
|
43
|
+
"util": "^0.12.4",
|
|
44
|
+
"webpack": "^5.76.0",
|
|
45
|
+
"webpack-cli": "^4.9.2",
|
|
46
|
+
"webpack-plugin-serve": "^1.6.0"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const config: {
|
|
2
|
+
iframeName: string;
|
|
3
|
+
hostName: string;
|
|
4
|
+
channel: string;
|
|
5
|
+
scope: '$hardware_sdk';
|
|
6
|
+
} = {
|
|
7
|
+
iframeName: 'onekey-iframe', //todo 应该到我们自己版本后改为ukey
|
|
8
|
+
hostName: 'onekey-host',
|
|
9
|
+
channel: 'onekey-js-sdk',
|
|
10
|
+
scope: '$hardware_sdk',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default config;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { Deferred, createDeferred, HardwareErrorCode, ERRORS } from '@ukeyfe/hardware-shared';
|
|
2
|
+
import { getOrigin } from '../utils/urlUtils';
|
|
3
|
+
|
|
4
|
+
/* eslint-disable import/no-mutable-exports */
|
|
5
|
+
export let instance: HTMLIFrameElement | null;
|
|
6
|
+
export let origin: string;
|
|
7
|
+
export let initPromise: Deferred<void> = createDeferred();
|
|
8
|
+
export let timeout = 0;
|
|
9
|
+
/* eslint-disable import/no-mutable-exports */
|
|
10
|
+
|
|
11
|
+
export const init = async (settings: any) => {
|
|
12
|
+
initPromise = createDeferred();
|
|
13
|
+
const existedFrame = document.getElementById('ukey-connect') as HTMLIFrameElement;
|
|
14
|
+
if (existedFrame) {
|
|
15
|
+
instance = existedFrame;
|
|
16
|
+
} else {
|
|
17
|
+
instance = document.createElement('iframe');
|
|
18
|
+
instance.frameBorder = '0';
|
|
19
|
+
instance.width = '0px';
|
|
20
|
+
instance.height = '0px';
|
|
21
|
+
instance.style.position = 'absolute';
|
|
22
|
+
instance.style.display = 'none';
|
|
23
|
+
instance.style.border = '0px';
|
|
24
|
+
instance.style.width = '0px';
|
|
25
|
+
instance.style.height = '0px';
|
|
26
|
+
instance.id = 'ukey-connect';
|
|
27
|
+
instance.allow = 'usb';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// const manifest = `version=${settings.version as string}`;
|
|
31
|
+
const src = `${settings.iframeSrc as string}`;
|
|
32
|
+
|
|
33
|
+
instance.setAttribute('src', src);
|
|
34
|
+
|
|
35
|
+
origin = getOrigin(instance.src);
|
|
36
|
+
timeout = window.setTimeout(() => {
|
|
37
|
+
initPromise.reject(ERRORS.TypedError(HardwareErrorCode.IframeTimeout));
|
|
38
|
+
}, 10000);
|
|
39
|
+
|
|
40
|
+
const onLoad = () => {
|
|
41
|
+
if (!instance) {
|
|
42
|
+
initPromise.reject(ERRORS.TypedError(HardwareErrorCode.IframeBlocked));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
instance.contentWindow?.postMessage(
|
|
47
|
+
{
|
|
48
|
+
type: 'iframe-init',
|
|
49
|
+
payload: {
|
|
50
|
+
settings: { ...settings },
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
origin
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
instance.onload = null;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// IE hack
|
|
60
|
+
// @ts-ignore
|
|
61
|
+
if (instance.attachEvent) {
|
|
62
|
+
// @ts-ignore
|
|
63
|
+
instance.attachEvent('onload', onLoad);
|
|
64
|
+
} else {
|
|
65
|
+
instance.onload = onLoad;
|
|
66
|
+
}
|
|
67
|
+
// inject iframe into host document body
|
|
68
|
+
if (document.body) {
|
|
69
|
+
document.body.appendChild(instance);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
await initPromise.promise;
|
|
74
|
+
} catch (e) {
|
|
75
|
+
if (instance) {
|
|
76
|
+
if (instance.parentNode) {
|
|
77
|
+
instance.parentNode.removeChild(instance);
|
|
78
|
+
}
|
|
79
|
+
instance = null;
|
|
80
|
+
}
|
|
81
|
+
throw e;
|
|
82
|
+
} finally {
|
|
83
|
+
window.clearTimeout(timeout);
|
|
84
|
+
timeout = 0;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const dispose = () => {
|
|
89
|
+
if (instance && instance.parentNode) {
|
|
90
|
+
try {
|
|
91
|
+
instance.parentNode.removeChild(instance);
|
|
92
|
+
} catch (e) {
|
|
93
|
+
// do nothing
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
instance = null;
|
|
97
|
+
timeout = 0;
|
|
98
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import HttpTransport from '@ukeyfe/hardware-transport-http';
|
|
2
|
+
import EmulatorTransport from '@ukeyfe/hardware-transport-emulator';
|
|
3
|
+
import { WebUsbTransport } from '@ukeyfe/hardware-transport-web-device';
|
|
4
|
+
import {
|
|
5
|
+
PostMessageEvent,
|
|
6
|
+
IFRAME,
|
|
7
|
+
parseMessage,
|
|
8
|
+
DataManager,
|
|
9
|
+
parseConnectSettings,
|
|
10
|
+
IFrameInit,
|
|
11
|
+
createIFrameMessage,
|
|
12
|
+
createErrorMessage,
|
|
13
|
+
initCore,
|
|
14
|
+
switchTransport,
|
|
15
|
+
Core,
|
|
16
|
+
CORE_EVENT,
|
|
17
|
+
getLogger,
|
|
18
|
+
LoggerNames,
|
|
19
|
+
LogBlockEvent,
|
|
20
|
+
ConnectSettings,
|
|
21
|
+
} from '@ukeyfe/hardware-core';
|
|
22
|
+
import { get } from 'lodash';
|
|
23
|
+
import { getOrigin } from '../utils/urlUtils';
|
|
24
|
+
import { sendMessage, createJsBridge } from '../utils/bridgeUtils';
|
|
25
|
+
|
|
26
|
+
import JSBridgeConfig from './bridge-config';
|
|
27
|
+
import { isExtensionWhitelisted, isOriginWhitelisted } from '..';
|
|
28
|
+
|
|
29
|
+
let _core: Core | undefined;
|
|
30
|
+
const Log = getLogger(LoggerNames.Iframe);
|
|
31
|
+
|
|
32
|
+
const getTransport = (env: ConnectSettings['env']) => {
|
|
33
|
+
if (env === 'webusb') return WebUsbTransport;
|
|
34
|
+
if (env === 'emulator') return EmulatorTransport;
|
|
35
|
+
return HttpTransport;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const handleMessage = (event: PostMessageEvent) => {
|
|
39
|
+
if (event.source === window || !event.data) return;
|
|
40
|
+
|
|
41
|
+
// is message from popup or extension
|
|
42
|
+
const whitelist = isOriginWhitelisted(event.origin) || isExtensionWhitelisted(event.origin);
|
|
43
|
+
const isTrustedDomain = event.origin === window.location.origin || !!whitelist;
|
|
44
|
+
|
|
45
|
+
// ignore messages from domain other then parent.window or popup.window or chrome extension
|
|
46
|
+
const eventOrigin = getOrigin(event.origin);
|
|
47
|
+
|
|
48
|
+
if (
|
|
49
|
+
!isTrustedDomain &&
|
|
50
|
+
eventOrigin !== DataManager.getSettings('origin') &&
|
|
51
|
+
eventOrigin !== getOrigin(document.referrer)
|
|
52
|
+
) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const message = parseMessage(event);
|
|
57
|
+
|
|
58
|
+
if (message.type === IFRAME.INIT) {
|
|
59
|
+
init(message.payload ?? {});
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export async function init(payload: IFrameInit['payload']) {
|
|
64
|
+
if (DataManager.getSettings('origin')) return;
|
|
65
|
+
|
|
66
|
+
const settings = parseConnectSettings({
|
|
67
|
+
...(payload.settings ?? {}),
|
|
68
|
+
isFrame: true,
|
|
69
|
+
});
|
|
70
|
+
// set origin manually
|
|
71
|
+
settings.origin = !origin || origin === 'null' ? payload.settings.origin : origin;
|
|
72
|
+
|
|
73
|
+
Log.enabled = !!settings.debug;
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
const Transport = getTransport(settings.env);
|
|
77
|
+
_core = await initCore(settings, Transport);
|
|
78
|
+
_core?.on(CORE_EVENT, messages => sendMessage(messages, false));
|
|
79
|
+
} catch (error) {
|
|
80
|
+
return createErrorMessage(error);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
createJsBridge({
|
|
84
|
+
isHost: false,
|
|
85
|
+
remoteFrame: window.parent,
|
|
86
|
+
remoteFrameName: JSBridgeConfig.hostName,
|
|
87
|
+
selfFrameName: JSBridgeConfig.iframeName,
|
|
88
|
+
channel: JSBridgeConfig.channel,
|
|
89
|
+
targetOrigin: getOrigin(settings.parentOrigin as string),
|
|
90
|
+
receiveHandler: async messageEvent => {
|
|
91
|
+
const message = parseMessage(messageEvent);
|
|
92
|
+
const blockLog = LogBlockEvent.has(get(message, 'type')) ? message.type : undefined;
|
|
93
|
+
if (blockLog) {
|
|
94
|
+
Log.debug('Frame Bridge Receive message: ', blockLog);
|
|
95
|
+
} else {
|
|
96
|
+
Log.debug('Frame Bridge Receive message: ', message);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (message.event === IFRAME.SWITCH_TRANSPORT) {
|
|
100
|
+
switchCoreTransport(message.payload.env);
|
|
101
|
+
return { success: true, payload: {} };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const response = await _core?.handleMessage(message);
|
|
105
|
+
if (blockLog) {
|
|
106
|
+
Log.debug('Frame Bridge response message: ', blockLog);
|
|
107
|
+
} else {
|
|
108
|
+
Log.debug('Frame Bridge response message: ', message);
|
|
109
|
+
}
|
|
110
|
+
return response;
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
await sendMessage(createIFrameMessage(IFRAME.INIT_BRIDGE, {}), false);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const switchCoreTransport = (env: ConnectSettings['env']) => {
|
|
118
|
+
if (_core) {
|
|
119
|
+
const Transport = getTransport(env);
|
|
120
|
+
switchTransport({
|
|
121
|
+
env,
|
|
122
|
+
Transport,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
window.addEventListener('message', handleMessage, false);
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import EventEmitter from 'events';
|
|
2
|
+
import HardwareSdk, {
|
|
3
|
+
HardwareSDKLowLevel as HardwareLowLevelSdk,
|
|
4
|
+
HardwareTopLevelSdk,
|
|
5
|
+
parseConnectSettings,
|
|
6
|
+
enableLog,
|
|
7
|
+
PostMessageEvent,
|
|
8
|
+
IFRAME,
|
|
9
|
+
createErrorMessage,
|
|
10
|
+
parseMessage,
|
|
11
|
+
UI_EVENT,
|
|
12
|
+
CoreMessage,
|
|
13
|
+
ConnectSettings,
|
|
14
|
+
UiResponseEvent,
|
|
15
|
+
LOG_EVENT,
|
|
16
|
+
setLoggerPostMessage,
|
|
17
|
+
getLogger,
|
|
18
|
+
LoggerNames,
|
|
19
|
+
FIRMWARE_EVENT,
|
|
20
|
+
DEVICE_EVENT,
|
|
21
|
+
DEVICE,
|
|
22
|
+
UI_REQUEST,
|
|
23
|
+
whitelist,
|
|
24
|
+
executeCallback,
|
|
25
|
+
} from '@ukeyfe/hardware-core';
|
|
26
|
+
import { ERRORS, HardwareError, HardwareErrorCode } from '@ukeyfe/hardware-shared';
|
|
27
|
+
import * as iframe from './iframe/builder';
|
|
28
|
+
import JSBridgeConfig from './iframe/bridge-config';
|
|
29
|
+
import { sendMessage, createJsBridge, hostBridge, resetListenerFlag } from './utils/bridgeUtils';
|
|
30
|
+
import { getHost } from './utils/urlUtils';
|
|
31
|
+
|
|
32
|
+
const eventEmitter = new EventEmitter();
|
|
33
|
+
const Log = getLogger(LoggerNames.Connect);
|
|
34
|
+
|
|
35
|
+
let _settings = parseConnectSettings();
|
|
36
|
+
|
|
37
|
+
export const isOriginWhitelisted = (origin: string) => {
|
|
38
|
+
const host = getHost(origin);
|
|
39
|
+
|
|
40
|
+
return whitelist.find(item => item.origin === origin || item.origin === host);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// easy to test and then open
|
|
44
|
+
// @ts-expect-error
|
|
45
|
+
export const isExtensionWhitelisted = (origin: string) => true;
|
|
46
|
+
// whitelistExtension.find(item => item === origin);
|
|
47
|
+
|
|
48
|
+
const handleMessage = async (message: CoreMessage) => {
|
|
49
|
+
switch (message.event) {
|
|
50
|
+
case UI_EVENT:
|
|
51
|
+
if (message.type === IFRAME.INIT_BRIDGE) {
|
|
52
|
+
iframe.initPromise.resolve();
|
|
53
|
+
return Promise.resolve({ success: true, payload: 'JSBridge Handshake Success' });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// pass UI event up
|
|
57
|
+
eventEmitter.emit(message.event, message);
|
|
58
|
+
eventEmitter.emit(message.type, message.payload);
|
|
59
|
+
break;
|
|
60
|
+
|
|
61
|
+
case LOG_EVENT:
|
|
62
|
+
case FIRMWARE_EVENT:
|
|
63
|
+
eventEmitter.emit(message.event, message);
|
|
64
|
+
break;
|
|
65
|
+
|
|
66
|
+
case DEVICE_EVENT:
|
|
67
|
+
if (
|
|
68
|
+
(
|
|
69
|
+
[DEVICE.CONNECT, DEVICE.DISCONNECT, DEVICE.FEATURES, DEVICE.SUPPORT_FEATURES] as string[]
|
|
70
|
+
).includes(message.type)
|
|
71
|
+
) {
|
|
72
|
+
eventEmitter.emit(message.type, message.payload);
|
|
73
|
+
}
|
|
74
|
+
break;
|
|
75
|
+
|
|
76
|
+
case IFRAME.CALLBACK: {
|
|
77
|
+
const { callbackId, data, error } = message.payload;
|
|
78
|
+
executeCallback(callbackId, data, error);
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
default:
|
|
83
|
+
Log.warn('No need to be captured message', message.event);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
function checkTrust(settings: ConnectSettings) {
|
|
88
|
+
const hasTrust =
|
|
89
|
+
isOriginWhitelisted(settings.parentOrigin ?? '') ||
|
|
90
|
+
isExtensionWhitelisted(settings.extension ?? '');
|
|
91
|
+
|
|
92
|
+
if (!hasTrust) {
|
|
93
|
+
throw ERRORS.TypedError(HardwareErrorCode.IframeDistrust, JSON.stringify(settings));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const dispose = () => {
|
|
98
|
+
checkTrust(_settings);
|
|
99
|
+
eventEmitter.removeAllListeners();
|
|
100
|
+
iframe.dispose();
|
|
101
|
+
_settings = parseConnectSettings();
|
|
102
|
+
window.removeEventListener('message', createJSBridge);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const uiResponse = (response: UiResponseEvent) => {
|
|
106
|
+
if (!iframe.instance) {
|
|
107
|
+
throw ERRORS.TypedError(HardwareErrorCode.IFrameNotInitialized);
|
|
108
|
+
}
|
|
109
|
+
const { type, payload } = response;
|
|
110
|
+
sendMessage({ event: UI_EVENT, type, payload } as CoreMessage);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const cancel = (connectId?: string) => {
|
|
114
|
+
sendMessage({ event: IFRAME.CANCEL, type: IFRAME.CANCEL, payload: { connectId } });
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
let prevFrameInstance: Window | null | undefined = null;
|
|
118
|
+
const createJSBridge = (messageEvent: PostMessageEvent) => {
|
|
119
|
+
if (messageEvent.origin !== iframe.origin) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (!hostBridge || prevFrameInstance !== iframe.instance?.contentWindow) {
|
|
123
|
+
resetListenerFlag();
|
|
124
|
+
createJsBridge({
|
|
125
|
+
isHost: true,
|
|
126
|
+
remoteFrame: iframe.instance?.contentWindow as Window,
|
|
127
|
+
remoteFrameName: JSBridgeConfig.iframeName,
|
|
128
|
+
selfFrameName: JSBridgeConfig.hostName,
|
|
129
|
+
channel: JSBridgeConfig.channel,
|
|
130
|
+
targetOrigin: iframe.origin,
|
|
131
|
+
|
|
132
|
+
receiveHandler: async messageEvent => {
|
|
133
|
+
const message = parseMessage(messageEvent);
|
|
134
|
+
if (message.event !== 'LOG_EVENT') {
|
|
135
|
+
if (['DEVICE_EVENT', 'FIRMWARE_EVENT'].includes(message.event)) {
|
|
136
|
+
// Log.debug('Host Bridge Receive message: ', message);
|
|
137
|
+
} else {
|
|
138
|
+
Log.debug('Host Bridge Receive message: ', message);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const response = await handleMessage(message);
|
|
142
|
+
if (message.event !== 'LOG_EVENT') {
|
|
143
|
+
if (['DEVICE_EVENT', 'FIRMWARE_EVENT'].includes(message.event)) {
|
|
144
|
+
// Log.debug('Host Bridge response: ', message);
|
|
145
|
+
} else {
|
|
146
|
+
Log.debug('Host Bridge response: ', message);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return response;
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
prevFrameInstance = iframe.instance?.contentWindow;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const init = async (settings: Partial<ConnectSettings>) => {
|
|
158
|
+
if (iframe.instance) {
|
|
159
|
+
throw ERRORS.TypedError(HardwareErrorCode.IFrameAleradyInitialized);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
_settings = parseConnectSettings({ ..._settings, ...settings });
|
|
163
|
+
checkTrust(_settings);
|
|
164
|
+
|
|
165
|
+
enableLog(!!settings.debug);
|
|
166
|
+
setLoggerPostMessage(handleMessage);
|
|
167
|
+
|
|
168
|
+
Log.debug('init');
|
|
169
|
+
|
|
170
|
+
window.addEventListener('message', createJSBridge);
|
|
171
|
+
window.addEventListener('unload', dispose);
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
await iframe.init({ ..._settings, version: process.env.VERSION });
|
|
175
|
+
return true;
|
|
176
|
+
} catch (e) {
|
|
177
|
+
console.log('init error: ', e);
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const call = async (params: any) => {
|
|
183
|
+
Log.debug('call : ', params);
|
|
184
|
+
/**
|
|
185
|
+
* Try to recreate iframe if it's initialize failed
|
|
186
|
+
*/
|
|
187
|
+
if (!iframe.instance && !iframe.timeout) {
|
|
188
|
+
_settings = parseConnectSettings(_settings);
|
|
189
|
+
checkTrust(_settings);
|
|
190
|
+
Log.debug("Try to recreate iframe if it's initialize failed: ", _settings);
|
|
191
|
+
try {
|
|
192
|
+
const initResult = await init(_settings);
|
|
193
|
+
if (!initResult) {
|
|
194
|
+
Log.debug('Recreate iframe failed');
|
|
195
|
+
return createErrorMessage(ERRORS.TypedError(HardwareErrorCode.IFrameLoadFail));
|
|
196
|
+
}
|
|
197
|
+
Log.debug('Recreate iframe success');
|
|
198
|
+
} catch (error) {
|
|
199
|
+
Log.debug('Recreate iframe failed: ', error);
|
|
200
|
+
return createErrorMessage(error);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (iframe.timeout) {
|
|
205
|
+
return createErrorMessage(ERRORS.TypedError(HardwareErrorCode.IFrameLoadFail));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
const response = await sendMessage({ event: IFRAME.CALL, type: IFRAME.CALL, payload: params });
|
|
210
|
+
if (response) {
|
|
211
|
+
return response;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return createErrorMessage(ERRORS.TypedError(HardwareErrorCode.CallMethodNotResponse));
|
|
215
|
+
} catch (error) {
|
|
216
|
+
Log.error('__call error: ', error);
|
|
217
|
+
let err = error;
|
|
218
|
+
if (!(err instanceof HardwareError)) {
|
|
219
|
+
err = ERRORS.CreateErrorByMessage(error.message);
|
|
220
|
+
}
|
|
221
|
+
return createErrorMessage(err);
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const updateSettings = async (settings: Partial<ConnectSettings>) => {
|
|
226
|
+
if (iframe.instance) {
|
|
227
|
+
throw ERRORS.TypedError(HardwareErrorCode.IFrameAleradyInitialized);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
checkTrust(_settings);
|
|
231
|
+
Log.debug('updateSettings API Called =>: old settings: ', _settings);
|
|
232
|
+
_settings = parseConnectSettings({ ..._settings, ...settings });
|
|
233
|
+
Log.debug('updateSettings API Called =>: new settings: ', _settings);
|
|
234
|
+
return Promise.resolve(true);
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const switchTransport = async (env: ConnectSettings['env']) => {
|
|
238
|
+
if (iframe.instance) {
|
|
239
|
+
const response = await sendMessage({
|
|
240
|
+
event: IFRAME.SWITCH_TRANSPORT,
|
|
241
|
+
type: IFRAME.SWITCH_TRANSPORT,
|
|
242
|
+
payload: { env },
|
|
243
|
+
});
|
|
244
|
+
return response;
|
|
245
|
+
}
|
|
246
|
+
throw ERRORS.TypedError(HardwareErrorCode.IFrameNotInitialized);
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const addHardwareGlobalEventListener = (listener: (message: CoreMessage) => void) => {
|
|
250
|
+
[
|
|
251
|
+
UI_EVENT,
|
|
252
|
+
LOG_EVENT,
|
|
253
|
+
FIRMWARE_EVENT,
|
|
254
|
+
DEVICE.CONNECT,
|
|
255
|
+
DEVICE.DISCONNECT,
|
|
256
|
+
DEVICE.FEATURES,
|
|
257
|
+
DEVICE.SUPPORT_FEATURES,
|
|
258
|
+
UI_REQUEST.FIRMWARE_PROGRESS,
|
|
259
|
+
UI_REQUEST.FIRMWARE_TIP,
|
|
260
|
+
UI_REQUEST.PREVIOUS_ADDRESS_RESULT,
|
|
261
|
+
].forEach(eventName => {
|
|
262
|
+
eventEmitter.on(eventName, (message: CoreMessage) => {
|
|
263
|
+
let emitMessage = message;
|
|
264
|
+
if (!message.event && !(message as CoreMessage).type) {
|
|
265
|
+
emitMessage = {
|
|
266
|
+
// @ts-expect-error
|
|
267
|
+
...message,
|
|
268
|
+
event: eventName,
|
|
269
|
+
type: eventName,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
listener?.(emitMessage);
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
const HardwareSDKLowLevel = HardwareLowLevelSdk({
|
|
278
|
+
eventEmitter,
|
|
279
|
+
init,
|
|
280
|
+
call,
|
|
281
|
+
cancel,
|
|
282
|
+
dispose,
|
|
283
|
+
addHardwareGlobalEventListener,
|
|
284
|
+
uiResponse,
|
|
285
|
+
updateSettings,
|
|
286
|
+
switchTransport,
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
const HardwareSDKTopLevel = HardwareTopLevelSdk();
|
|
290
|
+
|
|
291
|
+
const HardwareWebSdk = HardwareSdk({
|
|
292
|
+
eventEmitter,
|
|
293
|
+
init,
|
|
294
|
+
call,
|
|
295
|
+
cancel,
|
|
296
|
+
dispose,
|
|
297
|
+
uiResponse,
|
|
298
|
+
updateSettings,
|
|
299
|
+
switchTransport,
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
export default { HardwareSDKLowLevel, HardwareSDKTopLevel, HardwareWebSdk };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IJsBridgeIframeConfig,
|
|
3
|
+
JsBridgeIframe,
|
|
4
|
+
setPostMessageListenerFlag,
|
|
5
|
+
} from '@ukeyfe/cross-inpage-provider-core';
|
|
6
|
+
import { CoreMessage, getLogger, LoggerNames, LogBlockEvent } from '@ukeyfe/hardware-core';
|
|
7
|
+
import { ERRORS } from '@ukeyfe/hardware-shared';
|
|
8
|
+
import { get } from 'lodash';
|
|
9
|
+
import JSBridgeConfig from '../iframe/bridge-config';
|
|
10
|
+
|
|
11
|
+
// eslint-disable-next-line import/no-mutable-exports
|
|
12
|
+
let frameBridge: JsBridgeIframe;
|
|
13
|
+
// eslint-disable-next-line import/no-mutable-exports
|
|
14
|
+
let hostBridge: JsBridgeIframe;
|
|
15
|
+
|
|
16
|
+
const Log = getLogger(LoggerNames.SendMessage);
|
|
17
|
+
|
|
18
|
+
export const resetListenerFlag = () => setPostMessageListenerFlag(false);
|
|
19
|
+
|
|
20
|
+
export const createJsBridge = (params: IJsBridgeIframeConfig & { isHost: boolean }) => {
|
|
21
|
+
const bridge = new JsBridgeIframe(params);
|
|
22
|
+
if (params.isHost) {
|
|
23
|
+
hostBridge = bridge;
|
|
24
|
+
} else {
|
|
25
|
+
frameBridge = bridge;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const sendMessage = async (messages: CoreMessage, isHost = true): Promise<any> => {
|
|
30
|
+
const bridge = isHost ? hostBridge : frameBridge;
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const blockLog = LogBlockEvent.has(get(messages, 'type')) ? messages.type : undefined;
|
|
34
|
+
if (messages.event !== 'LOG_EVENT') {
|
|
35
|
+
if (blockLog) {
|
|
36
|
+
Log.debug('request: ', blockLog);
|
|
37
|
+
} else {
|
|
38
|
+
Log.debug('request: ', messages);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const result = await bridge?.request({
|
|
42
|
+
scope: JSBridgeConfig.scope,
|
|
43
|
+
data: { ...messages },
|
|
44
|
+
});
|
|
45
|
+
if (messages.event !== 'LOG_EVENT') {
|
|
46
|
+
if (blockLog) {
|
|
47
|
+
Log.debug('request result: ', blockLog);
|
|
48
|
+
} else {
|
|
49
|
+
Log.debug('request result: ', result);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
} catch (error) {
|
|
54
|
+
Log.error(error);
|
|
55
|
+
throw ERRORS.CreateErrorByMessage(error.message);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export { frameBridge, hostBridge };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const getOrigin = (url: string) => {
|
|
2
|
+
if (typeof url !== 'string') return 'unknown';
|
|
3
|
+
if (url.indexOf('file://') === 0) return 'file://';
|
|
4
|
+
// eslint-disable-next-line no-useless-escape
|
|
5
|
+
const parts = url.match(/^.+\:\/\/[^\/]+/);
|
|
6
|
+
return Array.isArray(parts) && parts.length > 0 ? parts[0] : 'unknown';
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const getHost = (url: unknown) => {
|
|
10
|
+
if (typeof url !== 'string') return;
|
|
11
|
+
const [, , uri] = url.match(/^(https?):\/\/([^:/]+)?/i) ?? [];
|
|
12
|
+
if (uri) {
|
|
13
|
+
const parts = uri.split('.');
|
|
14
|
+
|
|
15
|
+
return parts.length > 2
|
|
16
|
+
? // slice subdomain
|
|
17
|
+
parts.slice(parts.length - 2, parts.length).join('.')
|
|
18
|
+
: uri;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
|
|
6
|
+
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
7
|
+
<title>UKeyConnect | UKey</title>
|
|
8
|
+
<meta name="description" content="" />
|
|
9
|
+
<meta name="keywords" content="" />
|
|
10
|
+
<meta name="author" content="UKey Wallet" />
|
|
11
|
+
<meta name="robots" content="index, follow" />
|
|
12
|
+
<meta name="title" content="UKeyConnect | UKey" />
|
|
13
|
+
<meta http-equiv="Cache-control" content="no-cache" />
|
|
14
|
+
<meta http-equiv="pragma" content="no-cache" />
|
|
15
|
+
</head>
|
|
16
|
+
<body>
|
|
17
|
+
|
|
18
|
+
<script type="text/javascript">
|
|
19
|
+
|
|
20
|
+
window.parent.postMessage({
|
|
21
|
+
event: 'UI_EVENT',
|
|
22
|
+
type: 'iframe-bootstrap',
|
|
23
|
+
}, '*');
|
|
24
|
+
|
|
25
|
+
const iframeScript = document.createElement('script');
|
|
26
|
+
iframeScript.setAttribute('type', 'text/javascript');
|
|
27
|
+
iframeScript.setAttribute('src', '<%= htmlWebpackPlugin.files.js[0] %>');
|
|
28
|
+
iframeScript.setAttribute('async', 'false');
|
|
29
|
+
document.body.appendChild(iframeScript);
|
|
30
|
+
</script>
|
|
31
|
+
</body>
|
|
32
|
+
</html>
|