@onekeyfe/hd-transport-emulator 1.0.33-alpha.10
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 +48 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/http.d.ts +8 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +262 -0
- package/package.json +33 -0
- package/src/constants.ts +1 -0
- package/src/http.ts +85 -0
- package/src/index.ts +175 -0
- package/tsconfig.json +7 -0
package/README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# @onekeyfe/hd-transport-emulator
|
|
2
|
+
|
|
3
|
+
Hardware emulator transport for OneKey hardware SDK.
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
This package provides a transport layer for communicating with OneKey hardware emulators running on localhost:21321.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- Connects to OneKey hardware emulator on port 21321
|
|
12
|
+
- Compatible with OneKey hardware SDK
|
|
13
|
+
- Implements the same interface as other transport layers
|
|
14
|
+
- Supports all standard hardware operations
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @onekeyfe/hd-transport-emulator
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
import EmulatorTransport from '@onekeyfe/hd-transport-emulator';
|
|
26
|
+
|
|
27
|
+
// Create transport instance (default URL: http://localhost:21321)
|
|
28
|
+
const transport = new EmulatorTransport();
|
|
29
|
+
|
|
30
|
+
// Or specify custom URL
|
|
31
|
+
const transport = new EmulatorTransport('http://localhost:21321');
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Configuration
|
|
35
|
+
|
|
36
|
+
The transport uses the following default configuration:
|
|
37
|
+
- Port: 21321
|
|
38
|
+
- Protocol: HTTP
|
|
39
|
+
- Host: localhost
|
|
40
|
+
|
|
41
|
+
## Requirements
|
|
42
|
+
|
|
43
|
+
- OneKey hardware emulator running on port 21321
|
|
44
|
+
- Node.js environment or modern browser with CORS support
|
|
45
|
+
|
|
46
|
+
## License
|
|
47
|
+
|
|
48
|
+
ISC
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,2BAA2B,CAAC"}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type HttpRequestOptions = {
|
|
2
|
+
body?: Array<any> | Record<string, unknown> | string;
|
|
3
|
+
url: string;
|
|
4
|
+
method: 'POST' | 'GET';
|
|
5
|
+
timeout?: number;
|
|
6
|
+
};
|
|
7
|
+
export declare function request(options: HttpRequestOptions): Promise<any>;
|
|
8
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;IACrD,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AA4BF,wBAAsB,OAAO,CAAC,OAAO,EAAE,kBAAkB,gBA6BxD"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as _onekeyfe_hd_transport from '@onekeyfe/hd-transport';
|
|
2
|
+
import _onekeyfe_hd_transport__default, { OneKeyDeviceInfoWithSession, AcquireInput } from '@onekeyfe/hd-transport';
|
|
3
|
+
|
|
4
|
+
type IncompleteRequestOptions = {
|
|
5
|
+
body?: Array<any> | Record<string, unknown> | string;
|
|
6
|
+
url: string;
|
|
7
|
+
timeout?: number;
|
|
8
|
+
};
|
|
9
|
+
declare class EmulatorTransport {
|
|
10
|
+
_messages: ReturnType<typeof _onekeyfe_hd_transport__default.parseConfigure> | undefined;
|
|
11
|
+
name: string;
|
|
12
|
+
version: string;
|
|
13
|
+
configured: boolean;
|
|
14
|
+
stopped: boolean;
|
|
15
|
+
isOutdated: boolean;
|
|
16
|
+
url: string;
|
|
17
|
+
Log?: any;
|
|
18
|
+
constructor(url?: string);
|
|
19
|
+
_post(options: IncompleteRequestOptions): Promise<any>;
|
|
20
|
+
init(logger: any): Promise<string>;
|
|
21
|
+
_silentInit(): Promise<string>;
|
|
22
|
+
configure(signedData: any): void;
|
|
23
|
+
listen(old?: Array<OneKeyDeviceInfoWithSession>): Promise<OneKeyDeviceInfoWithSession[]>;
|
|
24
|
+
enumerate(): Promise<OneKeyDeviceInfoWithSession[]>;
|
|
25
|
+
_acquireMixed(input: AcquireInput): Promise<any>;
|
|
26
|
+
acquire(input: AcquireInput): Promise<string>;
|
|
27
|
+
release(session: string, onclose: boolean): Promise<void>;
|
|
28
|
+
call(session: string, name: string, data: Record<string, unknown>): Promise<_onekeyfe_hd_transport.MessageFromOneKey>;
|
|
29
|
+
post(session: string, name: string, data: Record<string, unknown>): Promise<void>;
|
|
30
|
+
read(session: string): Promise<_onekeyfe_hd_transport.MessageFromOneKey>;
|
|
31
|
+
requestDevice(): Promise<never>;
|
|
32
|
+
stop(): void;
|
|
33
|
+
cancel(): void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { EmulatorTransport as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,SAA8B,MAAM,wBAAwB,CAAC;AAEpE,OAAO,KAAK,EAAE,YAAY,EAAE,2BAA2B,EAAE,MAAM,wBAAwB,CAAC;AAMxF,KAAK,wBAAwB,GAAG;IAC9B,IAAI,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;IACrD,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,CAAC,OAAO,OAAO,iBAAiB;IACpC,SAAS,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAEnE,IAAI,SAAuB;IAE3B,OAAO,SAAW;IAElB,UAAU,UAAS;IAEnB,OAAO,UAAS;IAEhB,UAAU,UAAS;IAEnB,GAAG,EAAE,MAAM,CAAC;IAEZ,GAAG,CAAC,EAAE,GAAG,CAAC;gBAEE,GAAG,CAAC,EAAE,MAAM;IAIxB,KAAK,CAAC,OAAO,EAAE,wBAAwB;IAWjC,IAAI,CAAC,MAAM,EAAE,GAAG;IAMhB,WAAW;IAUjB,SAAS,CAAC,UAAU,EAAE,GAAG;IAMnB,MAAM,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,2BAA2B,CAAC;IAY/C,SAAS;IAMf,aAAa,CAAC,KAAK,EAAE,YAAY;IAS3B,OAAO,CAAC,KAAK,EAAE,YAAY;IAK3B,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;IAUzC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAyBjE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAYjE,IAAI,CAAC,OAAO,EAAE,MAAM;IAe1B,aAAa;IAKb,IAAI;IAIJ,MAAM;CAGP"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var transport = require('@onekeyfe/hd-transport');
|
|
4
|
+
var hdShared = require('@onekeyfe/hd-shared');
|
|
5
|
+
var axios = require('axios');
|
|
6
|
+
var secureJSON = require('secure-json-parse');
|
|
7
|
+
|
|
8
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
9
|
+
|
|
10
|
+
var transport__default = /*#__PURE__*/_interopDefaultLegacy(transport);
|
|
11
|
+
var axios__default = /*#__PURE__*/_interopDefaultLegacy(axios);
|
|
12
|
+
var secureJSON__default = /*#__PURE__*/_interopDefaultLegacy(secureJSON);
|
|
13
|
+
|
|
14
|
+
/******************************************************************************
|
|
15
|
+
Copyright (c) Microsoft Corporation.
|
|
16
|
+
|
|
17
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
18
|
+
purpose with or without fee is hereby granted.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
21
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
22
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
23
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
24
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
25
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
26
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
27
|
+
***************************************************************************** */
|
|
28
|
+
|
|
29
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
30
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
31
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
32
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
33
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
34
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
35
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
40
|
+
var e = new Error(message);
|
|
41
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function contentType(body) {
|
|
45
|
+
if (typeof body === 'string') {
|
|
46
|
+
return 'text/plain';
|
|
47
|
+
}
|
|
48
|
+
return 'application/json';
|
|
49
|
+
}
|
|
50
|
+
function wrapBody(body) {
|
|
51
|
+
if (typeof body === 'string') {
|
|
52
|
+
return body;
|
|
53
|
+
}
|
|
54
|
+
return JSON.stringify(body);
|
|
55
|
+
}
|
|
56
|
+
function parseResult(text) {
|
|
57
|
+
try {
|
|
58
|
+
const result = secureJSON__default["default"].parse(text);
|
|
59
|
+
if (typeof result !== 'object') {
|
|
60
|
+
throw new Error('Invalid response');
|
|
61
|
+
}
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
return text;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function request(options) {
|
|
69
|
+
var _a;
|
|
70
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
71
|
+
const fetchOptions = {
|
|
72
|
+
url: options.url,
|
|
73
|
+
method: options.method,
|
|
74
|
+
data: wrapBody(options.body),
|
|
75
|
+
withCredentials: false,
|
|
76
|
+
headers: {
|
|
77
|
+
'Content-Type': contentType(options.body == null ? '' : options.body),
|
|
78
|
+
},
|
|
79
|
+
timeout: (_a = options.timeout) !== null && _a !== void 0 ? _a : undefined,
|
|
80
|
+
transformResponse: data => data,
|
|
81
|
+
};
|
|
82
|
+
const res = yield axios__default["default"].request(fetchOptions);
|
|
83
|
+
if (+res.status === 200) {
|
|
84
|
+
return parseResult(res.data);
|
|
85
|
+
}
|
|
86
|
+
const resJson = parseResult(res.data);
|
|
87
|
+
if (typeof resJson === 'object' && resJson != null && resJson.error != null) {
|
|
88
|
+
throw new hdShared.HardwareError({
|
|
89
|
+
errorCode: hdShared.HardwareErrorCode.NetworkError,
|
|
90
|
+
message: resJson.error,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
throw new hdShared.HardwareError({ errorCode: hdShared.HardwareErrorCode.NetworkError, message: res.data });
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
axios__default["default"].interceptors.request.use(config => {
|
|
99
|
+
var _a, _b;
|
|
100
|
+
if (typeof window !== 'undefined') {
|
|
101
|
+
return config;
|
|
102
|
+
}
|
|
103
|
+
if ((_a = config.url) === null || _a === void 0 ? void 0 : _a.startsWith('http://localhost:21321')) {
|
|
104
|
+
if (!((_b = config === null || config === void 0 ? void 0 : config.headers) === null || _b === void 0 ? void 0 : _b.Origin)) {
|
|
105
|
+
console.log('set node request origin for emulator');
|
|
106
|
+
config.headers = Object.assign(Object.assign({}, config.headers), { Origin: 'https://jssdk.onekey.so' });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return config;
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const DEFAULT_URL = 'http://localhost:21321';
|
|
113
|
+
|
|
114
|
+
const { check, buildOne, receiveOne, parseConfigure } = transport__default["default"];
|
|
115
|
+
class EmulatorTransport {
|
|
116
|
+
constructor(url) {
|
|
117
|
+
this.name = 'EmulatorTransport';
|
|
118
|
+
this.version = '1.0.0';
|
|
119
|
+
this.configured = false;
|
|
120
|
+
this.stopped = false;
|
|
121
|
+
this.isOutdated = false;
|
|
122
|
+
this.url = url == null ? DEFAULT_URL : url;
|
|
123
|
+
}
|
|
124
|
+
_post(options) {
|
|
125
|
+
if (this.stopped) {
|
|
126
|
+
return Promise.reject(hdShared.ERRORS.TypedError('Transport stopped.'));
|
|
127
|
+
}
|
|
128
|
+
return request(Object.assign(Object.assign({}, options), { method: 'POST', url: this.url + options.url }));
|
|
129
|
+
}
|
|
130
|
+
init(logger) {
|
|
131
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
132
|
+
this.Log = logger;
|
|
133
|
+
const bridgeVersion = yield this._silentInit();
|
|
134
|
+
return bridgeVersion;
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
_silentInit() {
|
|
138
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
139
|
+
const infoS = yield request({
|
|
140
|
+
url: this.url,
|
|
141
|
+
method: 'POST',
|
|
142
|
+
timeout: 3000,
|
|
143
|
+
});
|
|
144
|
+
const info = check.info(infoS);
|
|
145
|
+
return info.version;
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
configure(signedData) {
|
|
149
|
+
const messages = parseConfigure(signedData);
|
|
150
|
+
this.configured = true;
|
|
151
|
+
this._messages = messages;
|
|
152
|
+
}
|
|
153
|
+
listen(old) {
|
|
154
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
155
|
+
if (old === null) {
|
|
156
|
+
throw hdShared.ERRORS.TypedError('Emulator-Transport does not support listen without previous.');
|
|
157
|
+
}
|
|
158
|
+
const devicesS = yield this._post({
|
|
159
|
+
url: '/listen',
|
|
160
|
+
body: old,
|
|
161
|
+
});
|
|
162
|
+
const devices = check.devices(devicesS);
|
|
163
|
+
return devices;
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
enumerate() {
|
|
167
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
168
|
+
const devicesS = yield this._post({ url: '/enumerate' });
|
|
169
|
+
const devices = check.devices(devicesS);
|
|
170
|
+
return devices;
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
_acquireMixed(input) {
|
|
174
|
+
const previousStr = input.previous == null ? 'null' : encodeURIComponent(input.previous);
|
|
175
|
+
const path = encodeURIComponent(input.path);
|
|
176
|
+
const url = `/acquire/${path}/${previousStr}`;
|
|
177
|
+
return this._post({ url });
|
|
178
|
+
}
|
|
179
|
+
acquire(input) {
|
|
180
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
181
|
+
const acquireS = yield this._acquireMixed(input);
|
|
182
|
+
return check.acquire(acquireS);
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
release(session, onclose) {
|
|
186
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
187
|
+
const res = this._post({
|
|
188
|
+
url: `/release/${session}`,
|
|
189
|
+
});
|
|
190
|
+
if (onclose) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
yield res;
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
call(session, name, data) {
|
|
197
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
198
|
+
if (this._messages == null) {
|
|
199
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
200
|
+
}
|
|
201
|
+
const messages = this._messages;
|
|
202
|
+
if (transport.LogBlockCommand.has(name)) {
|
|
203
|
+
this.Log.debug('call-', ' name: ', name);
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
this.Log.debug('call-', ' name: ', name, ' data: ', data);
|
|
207
|
+
}
|
|
208
|
+
const o = buildOne(messages, name, data);
|
|
209
|
+
const outData = o.toString('hex');
|
|
210
|
+
const resData = yield this._post({
|
|
211
|
+
url: `/call/${session}`,
|
|
212
|
+
body: outData,
|
|
213
|
+
timeout: name === 'Initialize' ? 10000 : undefined,
|
|
214
|
+
});
|
|
215
|
+
if (typeof resData !== 'string') {
|
|
216
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.NetworkError, 'Returning data is not string.');
|
|
217
|
+
}
|
|
218
|
+
const jsonData = receiveOne(messages, resData);
|
|
219
|
+
return check.call(jsonData);
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
post(session, name, data) {
|
|
223
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
224
|
+
if (this._messages == null) {
|
|
225
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
226
|
+
}
|
|
227
|
+
const messages = this._messages;
|
|
228
|
+
const outData = buildOne(messages, name, data).toString('hex');
|
|
229
|
+
yield this._post({
|
|
230
|
+
url: `/post/${session}`,
|
|
231
|
+
body: outData,
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
read(session) {
|
|
236
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
237
|
+
if (this._messages == null) {
|
|
238
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.TransportNotConfigured);
|
|
239
|
+
}
|
|
240
|
+
const messages = this._messages;
|
|
241
|
+
const resData = yield this._post({
|
|
242
|
+
url: `/read/${session}`,
|
|
243
|
+
});
|
|
244
|
+
if (typeof resData !== 'string') {
|
|
245
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.NetworkError, 'Returning data is not string.');
|
|
246
|
+
}
|
|
247
|
+
const jsonData = receiveOne(messages, resData);
|
|
248
|
+
return check.call(jsonData);
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
requestDevice() {
|
|
252
|
+
return Promise.reject();
|
|
253
|
+
}
|
|
254
|
+
stop() {
|
|
255
|
+
this.stopped = true;
|
|
256
|
+
}
|
|
257
|
+
cancel() {
|
|
258
|
+
this.Log.debug('canceled');
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
module.exports = EmulatorTransport;
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@onekeyfe/hd-transport-emulator",
|
|
3
|
+
"version": "1.0.33-alpha.10",
|
|
4
|
+
"description": "hardware emulator transport",
|
|
5
|
+
"author": "OneKey",
|
|
6
|
+
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
7
|
+
"license": "ISC",
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/OneKeyHQ/hardware-js-sdk.git"
|
|
13
|
+
},
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"dev": "rimraf dist && rollup -c ../../build/rollup.config.js -w",
|
|
19
|
+
"build": "rimraf dist && rollup -c ../../build/rollup.config.js",
|
|
20
|
+
"lint": "eslint .",
|
|
21
|
+
"lint:fix": "eslint . --fix"
|
|
22
|
+
},
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/OneKeyHQ/hardware-js-sdk/issues"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@onekeyfe/hd-shared": "^1.0.33-alpha.10",
|
|
28
|
+
"@onekeyfe/hd-transport": "^1.0.33-alpha.10",
|
|
29
|
+
"axios": "^0.27.2",
|
|
30
|
+
"secure-json-parse": "^4.0.0"
|
|
31
|
+
},
|
|
32
|
+
"gitHead": "a42d94fe3da1db3d2a2c0821441d884ec928260f"
|
|
33
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const DEFAULT_URL = 'http://localhost:21321';
|
package/src/http.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import axios, { AxiosRequestConfig } from 'axios';
|
|
2
|
+
import { HardwareError, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
3
|
+
import secureJSON from 'secure-json-parse';
|
|
4
|
+
|
|
5
|
+
export type HttpRequestOptions = {
|
|
6
|
+
body?: Array<any> | Record<string, unknown> | string;
|
|
7
|
+
url: string;
|
|
8
|
+
method: 'POST' | 'GET';
|
|
9
|
+
timeout?: number;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
function contentType(body: any) {
|
|
13
|
+
if (typeof body === 'string') {
|
|
14
|
+
return 'text/plain';
|
|
15
|
+
}
|
|
16
|
+
return 'application/json';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function wrapBody(body: any) {
|
|
20
|
+
if (typeof body === 'string') {
|
|
21
|
+
return body;
|
|
22
|
+
}
|
|
23
|
+
return JSON.stringify(body);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function parseResult(text: string) {
|
|
27
|
+
try {
|
|
28
|
+
const result = secureJSON.parse(text);
|
|
29
|
+
if (typeof result !== 'object') {
|
|
30
|
+
throw new Error('Invalid response');
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
} catch (e) {
|
|
34
|
+
return text;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function request(options: HttpRequestOptions) {
|
|
39
|
+
const fetchOptions: AxiosRequestConfig = {
|
|
40
|
+
url: options.url,
|
|
41
|
+
method: options.method,
|
|
42
|
+
data: wrapBody(options.body),
|
|
43
|
+
withCredentials: false,
|
|
44
|
+
headers: {
|
|
45
|
+
'Content-Type': contentType(options.body == null ? '' : options.body),
|
|
46
|
+
},
|
|
47
|
+
timeout: options.timeout ?? undefined,
|
|
48
|
+
// Prevent string from converting to number
|
|
49
|
+
// see https://stackoverflow.com/questions/43787712/axios-how-to-deal-with-big-integers
|
|
50
|
+
transformResponse: data => data,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const res = await axios.request(fetchOptions);
|
|
54
|
+
|
|
55
|
+
if (+res.status === 200) {
|
|
56
|
+
return parseResult(res.data);
|
|
57
|
+
}
|
|
58
|
+
const resJson = parseResult(res.data);
|
|
59
|
+
if (typeof resJson === 'object' && resJson != null && resJson.error != null) {
|
|
60
|
+
throw new HardwareError({
|
|
61
|
+
errorCode: HardwareErrorCode.NetworkError,
|
|
62
|
+
message: resJson.error,
|
|
63
|
+
});
|
|
64
|
+
} else {
|
|
65
|
+
throw new HardwareError({ errorCode: HardwareErrorCode.NetworkError, message: res.data });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
axios.interceptors.request.use(config => {
|
|
70
|
+
if (typeof window !== 'undefined') {
|
|
71
|
+
return config;
|
|
72
|
+
}
|
|
73
|
+
// node environment
|
|
74
|
+
if (config.url?.startsWith('http://localhost:21321')) {
|
|
75
|
+
if (!config?.headers?.Origin) {
|
|
76
|
+
console.log('set node request origin for emulator');
|
|
77
|
+
// add Origin field for request headers
|
|
78
|
+
config.headers = {
|
|
79
|
+
...config.headers,
|
|
80
|
+
Origin: 'https://jssdk.onekey.so',
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return config;
|
|
85
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import transport, { LogBlockCommand } from '@onekeyfe/hd-transport';
|
|
2
|
+
import { ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
3
|
+
import type { AcquireInput, OneKeyDeviceInfoWithSession } from '@onekeyfe/hd-transport';
|
|
4
|
+
import { request as http } from './http';
|
|
5
|
+
import { DEFAULT_URL } from './constants';
|
|
6
|
+
|
|
7
|
+
const { check, buildOne, receiveOne, parseConfigure } = transport;
|
|
8
|
+
|
|
9
|
+
type IncompleteRequestOptions = {
|
|
10
|
+
body?: Array<any> | Record<string, unknown> | string;
|
|
11
|
+
url: string;
|
|
12
|
+
timeout?: number;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default class EmulatorTransport {
|
|
16
|
+
_messages: ReturnType<typeof transport.parseConfigure> | undefined;
|
|
17
|
+
|
|
18
|
+
name = 'EmulatorTransport';
|
|
19
|
+
|
|
20
|
+
version = '1.0.0';
|
|
21
|
+
|
|
22
|
+
configured = false;
|
|
23
|
+
|
|
24
|
+
stopped = false;
|
|
25
|
+
|
|
26
|
+
isOutdated = false;
|
|
27
|
+
|
|
28
|
+
url: string;
|
|
29
|
+
|
|
30
|
+
Log?: any;
|
|
31
|
+
|
|
32
|
+
constructor(url?: string) {
|
|
33
|
+
this.url = url == null ? DEFAULT_URL : url;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
_post(options: IncompleteRequestOptions) {
|
|
37
|
+
if (this.stopped) {
|
|
38
|
+
return Promise.reject(ERRORS.TypedError('Transport stopped.'));
|
|
39
|
+
}
|
|
40
|
+
return http({
|
|
41
|
+
...options,
|
|
42
|
+
method: 'POST',
|
|
43
|
+
url: this.url + options.url,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async init(logger: any) {
|
|
48
|
+
this.Log = logger;
|
|
49
|
+
const bridgeVersion = await this._silentInit();
|
|
50
|
+
return bridgeVersion;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async _silentInit() {
|
|
54
|
+
const infoS = await http({
|
|
55
|
+
url: this.url,
|
|
56
|
+
method: 'POST',
|
|
57
|
+
timeout: 3000,
|
|
58
|
+
});
|
|
59
|
+
const info = check.info(infoS);
|
|
60
|
+
return info.version;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
configure(signedData: any) {
|
|
64
|
+
const messages = parseConfigure(signedData);
|
|
65
|
+
this.configured = true;
|
|
66
|
+
this._messages = messages;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async listen(old?: Array<OneKeyDeviceInfoWithSession>) {
|
|
70
|
+
if (old === null) {
|
|
71
|
+
throw ERRORS.TypedError('Emulator-Transport does not support listen without previous.');
|
|
72
|
+
}
|
|
73
|
+
const devicesS = await this._post({
|
|
74
|
+
url: '/listen',
|
|
75
|
+
body: old,
|
|
76
|
+
});
|
|
77
|
+
const devices = check.devices(devicesS);
|
|
78
|
+
return devices;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async enumerate() {
|
|
82
|
+
const devicesS = await this._post({ url: '/enumerate' });
|
|
83
|
+
const devices = check.devices(devicesS);
|
|
84
|
+
return devices;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
_acquireMixed(input: AcquireInput) {
|
|
88
|
+
const previousStr = input.previous == null ? 'null' : encodeURIComponent(input.previous);
|
|
89
|
+
// @ts-expect-error
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
91
|
+
const path = encodeURIComponent(input.path);
|
|
92
|
+
const url = `/acquire/${path}/${previousStr}`;
|
|
93
|
+
return this._post({ url });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async acquire(input: AcquireInput) {
|
|
97
|
+
const acquireS = await this._acquireMixed(input);
|
|
98
|
+
return check.acquire(acquireS);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async release(session: string, onclose: boolean) {
|
|
102
|
+
const res = this._post({
|
|
103
|
+
url: `/release/${session}`,
|
|
104
|
+
});
|
|
105
|
+
if (onclose) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
await res;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async call(session: string, name: string, data: Record<string, unknown>) {
|
|
112
|
+
if (this._messages == null) {
|
|
113
|
+
throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
|
|
114
|
+
}
|
|
115
|
+
const messages = this._messages;
|
|
116
|
+
if (LogBlockCommand.has(name)) {
|
|
117
|
+
this.Log.debug('call-', ' name: ', name);
|
|
118
|
+
} else {
|
|
119
|
+
this.Log.debug('call-', ' name: ', name, ' data: ', data);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const o = buildOne(messages, name, data);
|
|
123
|
+
const outData = o.toString('hex');
|
|
124
|
+
const resData = await this._post({
|
|
125
|
+
url: `/call/${session}`,
|
|
126
|
+
body: outData,
|
|
127
|
+
timeout: name === 'Initialize' ? 10000 : undefined,
|
|
128
|
+
});
|
|
129
|
+
if (typeof resData !== 'string') {
|
|
130
|
+
throw ERRORS.TypedError(HardwareErrorCode.NetworkError, 'Returning data is not string.');
|
|
131
|
+
}
|
|
132
|
+
const jsonData = receiveOne(messages, resData);
|
|
133
|
+
return check.call(jsonData);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async post(session: string, name: string, data: Record<string, unknown>) {
|
|
137
|
+
if (this._messages == null) {
|
|
138
|
+
throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
|
|
139
|
+
}
|
|
140
|
+
const messages = this._messages;
|
|
141
|
+
const outData = buildOne(messages, name, data).toString('hex');
|
|
142
|
+
await this._post({
|
|
143
|
+
url: `/post/${session}`,
|
|
144
|
+
body: outData,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async read(session: string) {
|
|
149
|
+
if (this._messages == null) {
|
|
150
|
+
throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
|
|
151
|
+
}
|
|
152
|
+
const messages = this._messages;
|
|
153
|
+
const resData = await this._post({
|
|
154
|
+
url: `/read/${session}`,
|
|
155
|
+
});
|
|
156
|
+
if (typeof resData !== 'string') {
|
|
157
|
+
throw ERRORS.TypedError(HardwareErrorCode.NetworkError, 'Returning data is not string.');
|
|
158
|
+
}
|
|
159
|
+
const jsonData = receiveOne(messages, resData);
|
|
160
|
+
return check.call(jsonData);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
requestDevice() {
|
|
164
|
+
// eslint-disable-next-line prefer-promise-reject-errors
|
|
165
|
+
return Promise.reject();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
stop() {
|
|
169
|
+
this.stopped = true;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
cancel() {
|
|
173
|
+
this.Log.debug('canceled');
|
|
174
|
+
}
|
|
175
|
+
}
|