@ukeyfe/hardware-transport-http 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 +29 -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 +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +276 -0
- package/package.json +32 -0
- package/src/constants.ts +1 -0
- package/src/http.ts +103 -0
- package/src/index.ts +171 -0
- package/tsconfig.json +7 -0
package/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# `@ukeyfe/hardware-transport-http`
|
|
2
|
+
|
|
3
|
+
`@ukeyfe/hardware-transport-http` is a library that implements transport communication using http.
|
|
4
|
+
|
|
5
|
+
## What is the purpose
|
|
6
|
+
|
|
7
|
+
- translate JSON payloads to binary messages using protobuf definitions comprehensible to UKey devices
|
|
8
|
+
- chunking and reading chunked messages according to the [UKey protocol](./protocol.md)
|
|
9
|
+
- exposing single API for various transport methods:
|
|
10
|
+
- Http Transport
|
|
11
|
+
- React Native Transport
|
|
12
|
+
- WebUSB
|
|
13
|
+
- Create and expose typescript definitions based on protobuf definitions.
|
|
14
|
+
|
|
15
|
+
### The short version
|
|
16
|
+
|
|
17
|
+
In order to be able to use new features of ukey-firmware you need to update protobuf definitions.
|
|
18
|
+
|
|
19
|
+
1. `git submodule update --init --recursive` to initialize git submodules.
|
|
20
|
+
1. `yarn update-submodules` to update firmware submodule
|
|
21
|
+
1. `yarn update:protobuf` to generate new `./messages.json` and `./src/types/messages.ts`
|
|
22
|
+
|
|
23
|
+
git submodule update --init --recursive to initialize firmware submodule
|
|
24
|
+
yarn update-submodules to update firmware submodule
|
|
25
|
+
yar update:protobuf to generate new ./messages.json and ./src/types/messages.ts
|
|
26
|
+
|
|
27
|
+
## Docs
|
|
28
|
+
|
|
29
|
+
Documentation is available [hardware-js-sdk](https://developer.ukey.so/connect-to-hardware/hardware-sdk/start)
|
|
@@ -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":"AAQA,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,gBA+CxD"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as _ukeyfe_hardware_transport from '@ukeyfe/hardware-transport';
|
|
2
|
+
import _ukeyfe_hardware_transport__default, { UKeyDeviceInfoWithSession, AcquireInput } from '@ukeyfe/hardware-transport';
|
|
3
|
+
|
|
4
|
+
type IncompleteRequestOptions = {
|
|
5
|
+
body?: Array<any> | Record<string, unknown> | string;
|
|
6
|
+
url: string;
|
|
7
|
+
timeout?: number;
|
|
8
|
+
};
|
|
9
|
+
declare class HttpTransport {
|
|
10
|
+
_messages: ReturnType<typeof _ukeyfe_hardware_transport__default.parseConfigure> | undefined;
|
|
11
|
+
name: string;
|
|
12
|
+
configured: boolean;
|
|
13
|
+
stopped: boolean;
|
|
14
|
+
url: string;
|
|
15
|
+
Log?: any;
|
|
16
|
+
constructor(url?: string);
|
|
17
|
+
_post(options: IncompleteRequestOptions): Promise<any>;
|
|
18
|
+
init(logger: any): Promise<string>;
|
|
19
|
+
_silentInit(): Promise<string>;
|
|
20
|
+
configure(signedData: any): void;
|
|
21
|
+
listen(old?: Array<UKeyDeviceInfoWithSession>): Promise<UKeyDeviceInfoWithSession[]>;
|
|
22
|
+
enumerate(): Promise<UKeyDeviceInfoWithSession[]>;
|
|
23
|
+
_acquireMixed(input: AcquireInput): Promise<any>;
|
|
24
|
+
acquire(input: AcquireInput): Promise<string>;
|
|
25
|
+
release(session: string, onclose: boolean): Promise<void>;
|
|
26
|
+
call(session: string, name: string, data: Record<string, unknown>): Promise<_ukeyfe_hardware_transport.MessageFromUKey>;
|
|
27
|
+
post(session: string, name: string, data: Record<string, unknown>): Promise<void>;
|
|
28
|
+
read(session: string): Promise<_ukeyfe_hardware_transport.MessageFromUKey>;
|
|
29
|
+
requestDevice(): Promise<never>;
|
|
30
|
+
stop(): void;
|
|
31
|
+
cancel(): void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { HttpTransport as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,SAA8B,MAAM,4BAA4B,CAAC;AAExE,OAAO,KAAK,EAAE,YAAY,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAM1F,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,aAAa;IAChC,SAAS,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAEnE,IAAI,SAAmB;IAEvB,UAAU,UAAS;IAEnB,OAAO,UAAS;IAEhB,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,yBAAyB,CAAC;IAY7C,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,276 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var transport = require('@ukeyfe/hardware-transport');
|
|
4
|
+
var hardwareShared = require('@ukeyfe/hardware-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, _b;
|
|
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
|
+
try {
|
|
83
|
+
const res = yield axios__default["default"].request(fetchOptions);
|
|
84
|
+
if (+res.status === 200) {
|
|
85
|
+
return parseResult(res.data);
|
|
86
|
+
}
|
|
87
|
+
const resJson = parseResult(res.data);
|
|
88
|
+
if (typeof resJson === 'object' && resJson != null && resJson.error != null) {
|
|
89
|
+
throw new hardwareShared.HardwareError({
|
|
90
|
+
errorCode: hardwareShared.HardwareErrorCode.NetworkError,
|
|
91
|
+
message: resJson.error,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
throw new hardwareShared.HardwareError({ errorCode: hardwareShared.HardwareErrorCode.NetworkError, message: res.data });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
const axiosErr = err;
|
|
100
|
+
const respData = (_b = axiosErr === null || axiosErr === void 0 ? void 0 : axiosErr.response) === null || _b === void 0 ? void 0 : _b.data;
|
|
101
|
+
if (typeof respData === 'string') {
|
|
102
|
+
const parsed = parseResult(respData);
|
|
103
|
+
if (typeof parsed === 'object' && parsed !== null && parsed.error) {
|
|
104
|
+
throw hardwareShared.CreateHardwareErrorByBridgeError(String(parsed.error));
|
|
105
|
+
}
|
|
106
|
+
throw hardwareShared.CreateHardwareErrorByBridgeError(respData);
|
|
107
|
+
}
|
|
108
|
+
throw new hardwareShared.HardwareError({
|
|
109
|
+
errorCode: hardwareShared.HardwareErrorCode.BridgeNetworkError,
|
|
110
|
+
message: (axiosErr === null || axiosErr === void 0 ? void 0 : axiosErr.message) || 'Bridge network error',
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
axios__default["default"].interceptors.request.use((config) => {
|
|
116
|
+
var _a;
|
|
117
|
+
if (typeof window !== 'undefined') {
|
|
118
|
+
return config;
|
|
119
|
+
}
|
|
120
|
+
if ((_a = config.url) === null || _a === void 0 ? void 0 : _a.startsWith('http://localhost:21320')) {
|
|
121
|
+
if (!config.headers.get('Origin')) {
|
|
122
|
+
config.headers.set('Origin', 'https://jssdk.onekey.so');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return config;
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const DEFAULT_URL = 'http://localhost:21320';
|
|
129
|
+
|
|
130
|
+
const { check, buildOne, receiveOne, parseConfigure } = transport__default["default"];
|
|
131
|
+
class HttpTransport {
|
|
132
|
+
constructor(url) {
|
|
133
|
+
this.name = 'HttpTransport';
|
|
134
|
+
this.configured = false;
|
|
135
|
+
this.stopped = false;
|
|
136
|
+
this.url = url == null ? DEFAULT_URL : url;
|
|
137
|
+
}
|
|
138
|
+
_post(options) {
|
|
139
|
+
if (this.stopped) {
|
|
140
|
+
return Promise.reject(hardwareShared.ERRORS.TypedError('Transport stopped.'));
|
|
141
|
+
}
|
|
142
|
+
return request(Object.assign(Object.assign({}, options), { method: 'POST', url: this.url + options.url }));
|
|
143
|
+
}
|
|
144
|
+
init(logger) {
|
|
145
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
146
|
+
this.Log = logger;
|
|
147
|
+
const bridgeVersion = yield this._silentInit();
|
|
148
|
+
return bridgeVersion;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
_silentInit() {
|
|
152
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
153
|
+
const infoS = yield request({
|
|
154
|
+
url: this.url,
|
|
155
|
+
method: 'POST',
|
|
156
|
+
timeout: 3000,
|
|
157
|
+
});
|
|
158
|
+
const info = check.info(infoS);
|
|
159
|
+
return info.version;
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
configure(signedData) {
|
|
163
|
+
const messages = parseConfigure(signedData);
|
|
164
|
+
this.configured = true;
|
|
165
|
+
this._messages = messages;
|
|
166
|
+
}
|
|
167
|
+
listen(old) {
|
|
168
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
169
|
+
if (old === null) {
|
|
170
|
+
throw hardwareShared.ERRORS.TypedError('Http-Transport does not support listen without previous.');
|
|
171
|
+
}
|
|
172
|
+
const devicesS = yield this._post({
|
|
173
|
+
url: '/listen',
|
|
174
|
+
body: old,
|
|
175
|
+
});
|
|
176
|
+
const devices = check.devices(devicesS);
|
|
177
|
+
return devices;
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
enumerate() {
|
|
181
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
182
|
+
const devicesS = yield this._post({ url: '/enumerate' });
|
|
183
|
+
const devices = check.devices(devicesS);
|
|
184
|
+
return devices;
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
_acquireMixed(input) {
|
|
188
|
+
const previousStr = input.previous == null ? 'null' : encodeURIComponent(input.previous);
|
|
189
|
+
const path = encodeURIComponent(input.path);
|
|
190
|
+
const url = `/acquire/${path}/${previousStr}`;
|
|
191
|
+
return this._post({ url });
|
|
192
|
+
}
|
|
193
|
+
acquire(input) {
|
|
194
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
195
|
+
const acquireS = yield this._acquireMixed(input);
|
|
196
|
+
return check.acquire(acquireS);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
release(session, onclose) {
|
|
200
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
201
|
+
const res = this._post({
|
|
202
|
+
url: `/release/${session}`,
|
|
203
|
+
});
|
|
204
|
+
if (onclose) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
yield res;
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
call(session, name, data) {
|
|
211
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
212
|
+
if (this._messages == null) {
|
|
213
|
+
throw hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.TransportNotConfigured);
|
|
214
|
+
}
|
|
215
|
+
const messages = this._messages;
|
|
216
|
+
if (transport.LogBlockCommand.has(name)) {
|
|
217
|
+
this.Log.debug('call-', ' name: ', name);
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
this.Log.debug('call-', ' name: ', name, ' data: ', data);
|
|
221
|
+
}
|
|
222
|
+
const o = buildOne(messages, name, data);
|
|
223
|
+
const outData = o.toString('hex');
|
|
224
|
+
const resData = yield this._post({
|
|
225
|
+
url: `/call/${session}`,
|
|
226
|
+
body: outData,
|
|
227
|
+
timeout: name === 'Initialize' ? 10000 : undefined,
|
|
228
|
+
});
|
|
229
|
+
if (typeof resData !== 'string') {
|
|
230
|
+
throw hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.NetworkError, 'Returning data is not string.');
|
|
231
|
+
}
|
|
232
|
+
const jsonData = receiveOne(messages, resData);
|
|
233
|
+
return check.call(jsonData);
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
post(session, name, data) {
|
|
237
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
238
|
+
if (this._messages == null) {
|
|
239
|
+
throw hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.TransportNotConfigured);
|
|
240
|
+
}
|
|
241
|
+
const messages = this._messages;
|
|
242
|
+
const outData = buildOne(messages, name, data).toString('hex');
|
|
243
|
+
yield this._post({
|
|
244
|
+
url: `/post/${session}`,
|
|
245
|
+
body: outData,
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
read(session) {
|
|
250
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
251
|
+
if (this._messages == null) {
|
|
252
|
+
throw hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.TransportNotConfigured);
|
|
253
|
+
}
|
|
254
|
+
const messages = this._messages;
|
|
255
|
+
const resData = yield this._post({
|
|
256
|
+
url: `/read/${session}`,
|
|
257
|
+
});
|
|
258
|
+
if (typeof resData !== 'string') {
|
|
259
|
+
throw hardwareShared.ERRORS.TypedError(hardwareShared.HardwareErrorCode.NetworkError, 'Returning data is not string.');
|
|
260
|
+
}
|
|
261
|
+
const jsonData = receiveOne(messages, resData);
|
|
262
|
+
return check.call(jsonData);
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
requestDevice() {
|
|
266
|
+
return Promise.reject();
|
|
267
|
+
}
|
|
268
|
+
stop() {
|
|
269
|
+
this.stopped = true;
|
|
270
|
+
}
|
|
271
|
+
cancel() {
|
|
272
|
+
this.Log.debug('canceled');
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
module.exports = HttpTransport;
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ukeyfe/hardware-transport-http",
|
|
3
|
+
"version": "1.1.13",
|
|
4
|
+
"description": "hardware http transport",
|
|
5
|
+
"author": "UKey",
|
|
6
|
+
"homepage": "https://github.com/UKeyHQ/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/bestyourwallet/hardware-js-sdk"
|
|
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/UKeyHQ/hardware-js-sdk/issues"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@ukeyfe/hardware-shared": "1.1.13",
|
|
28
|
+
"@ukeyfe/hardware-transport": "1.1.13",
|
|
29
|
+
"axios": "1.12.2",
|
|
30
|
+
"secure-json-parse": "^4.0.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const DEFAULT_URL = 'http://localhost:21320';
|
package/src/http.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import axios, { AxiosError, AxiosRequestConfig, InternalAxiosRequestConfig } from 'axios';
|
|
2
|
+
import {
|
|
3
|
+
HardwareError,
|
|
4
|
+
HardwareErrorCode,
|
|
5
|
+
CreateHardwareErrorByBridgeError,
|
|
6
|
+
} from '@ukeyfe/hardware-shared';
|
|
7
|
+
import secureJSON from 'secure-json-parse';
|
|
8
|
+
|
|
9
|
+
export type HttpRequestOptions = {
|
|
10
|
+
body?: Array<any> | Record<string, unknown> | string;
|
|
11
|
+
url: string;
|
|
12
|
+
method: 'POST' | 'GET';
|
|
13
|
+
timeout?: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function contentType(body: any) {
|
|
17
|
+
if (typeof body === 'string') {
|
|
18
|
+
return 'text/plain';
|
|
19
|
+
}
|
|
20
|
+
return 'application/json';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function wrapBody(body: any) {
|
|
24
|
+
if (typeof body === 'string') {
|
|
25
|
+
return body;
|
|
26
|
+
}
|
|
27
|
+
return JSON.stringify(body);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function parseResult(text: string) {
|
|
31
|
+
try {
|
|
32
|
+
const result = secureJSON.parse(text);
|
|
33
|
+
if (typeof result !== 'object') {
|
|
34
|
+
throw new Error('Invalid response');
|
|
35
|
+
}
|
|
36
|
+
return result;
|
|
37
|
+
} catch (e) {
|
|
38
|
+
return text;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function request(options: HttpRequestOptions) {
|
|
43
|
+
const fetchOptions: AxiosRequestConfig = {
|
|
44
|
+
url: options.url,
|
|
45
|
+
method: options.method,
|
|
46
|
+
data: wrapBody(options.body),
|
|
47
|
+
withCredentials: false,
|
|
48
|
+
headers: {
|
|
49
|
+
'Content-Type': contentType(options.body == null ? '' : options.body),
|
|
50
|
+
},
|
|
51
|
+
timeout: options.timeout ?? undefined,
|
|
52
|
+
// Prevent string from converting to number
|
|
53
|
+
// see https://stackoverflow.com/questions/43787712/axios-how-to-deal-with-big-integers
|
|
54
|
+
transformResponse: data => data,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const res = await axios.request(fetchOptions);
|
|
59
|
+
|
|
60
|
+
if (+res.status === 200) {
|
|
61
|
+
return parseResult(res.data);
|
|
62
|
+
}
|
|
63
|
+
const resJson = parseResult(res.data);
|
|
64
|
+
if (typeof resJson === 'object' && resJson != null && resJson.error != null) {
|
|
65
|
+
throw new HardwareError({
|
|
66
|
+
errorCode: HardwareErrorCode.NetworkError,
|
|
67
|
+
message: resJson.error,
|
|
68
|
+
});
|
|
69
|
+
} else {
|
|
70
|
+
throw new HardwareError({ errorCode: HardwareErrorCode.NetworkError, message: res.data });
|
|
71
|
+
}
|
|
72
|
+
} catch (err) {
|
|
73
|
+
const axiosErr = err as AxiosError<string>;
|
|
74
|
+
const respData = axiosErr?.response?.data;
|
|
75
|
+
|
|
76
|
+
if (typeof respData === 'string') {
|
|
77
|
+
const parsed = parseResult(respData);
|
|
78
|
+
if (typeof parsed === 'object' && parsed !== null && parsed.error) {
|
|
79
|
+
throw CreateHardwareErrorByBridgeError(String(parsed.error));
|
|
80
|
+
}
|
|
81
|
+
throw CreateHardwareErrorByBridgeError(respData);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
throw new HardwareError({
|
|
85
|
+
errorCode: HardwareErrorCode.BridgeNetworkError,
|
|
86
|
+
message: axiosErr?.message || 'Bridge network error',
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
axios.interceptors.request.use((config: InternalAxiosRequestConfig) => {
|
|
92
|
+
if (typeof window !== 'undefined') {
|
|
93
|
+
return config;
|
|
94
|
+
}
|
|
95
|
+
// node environment
|
|
96
|
+
if (config.url?.startsWith('http://localhost:21320')) {
|
|
97
|
+
if (!config.headers.get('Origin')) {
|
|
98
|
+
// add Origin field for request headers
|
|
99
|
+
config.headers.set('Origin', 'https://jssdk.onekey.so');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return config;
|
|
103
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import transport, { LogBlockCommand } from '@ukeyfe/hardware-transport';
|
|
2
|
+
import { ERRORS, HardwareErrorCode } from '@ukeyfe/hardware-shared';
|
|
3
|
+
import type { AcquireInput, UKeyDeviceInfoWithSession } from '@ukeyfe/hardware-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 HttpTransport {
|
|
16
|
+
_messages: ReturnType<typeof transport.parseConfigure> | undefined;
|
|
17
|
+
|
|
18
|
+
name = 'HttpTransport';
|
|
19
|
+
|
|
20
|
+
configured = false;
|
|
21
|
+
|
|
22
|
+
stopped = false;
|
|
23
|
+
|
|
24
|
+
url: string;
|
|
25
|
+
|
|
26
|
+
Log?: any;
|
|
27
|
+
|
|
28
|
+
constructor(url?: string) {
|
|
29
|
+
this.url = url == null ? DEFAULT_URL : url;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
_post(options: IncompleteRequestOptions) {
|
|
33
|
+
if (this.stopped) {
|
|
34
|
+
return Promise.reject(ERRORS.TypedError('Transport stopped.'));
|
|
35
|
+
}
|
|
36
|
+
return http({
|
|
37
|
+
...options,
|
|
38
|
+
method: 'POST',
|
|
39
|
+
url: this.url + options.url,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async init(logger: any) {
|
|
44
|
+
this.Log = logger;
|
|
45
|
+
const bridgeVersion = await this._silentInit();
|
|
46
|
+
return bridgeVersion;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async _silentInit() {
|
|
50
|
+
const infoS = await http({
|
|
51
|
+
url: this.url,
|
|
52
|
+
method: 'POST',
|
|
53
|
+
timeout: 3000,
|
|
54
|
+
});
|
|
55
|
+
const info = check.info(infoS);
|
|
56
|
+
return info.version;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
configure(signedData: any) {
|
|
60
|
+
const messages = parseConfigure(signedData);
|
|
61
|
+
this.configured = true;
|
|
62
|
+
this._messages = messages;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async listen(old?: Array<UKeyDeviceInfoWithSession>) {
|
|
66
|
+
if (old === null) {
|
|
67
|
+
throw ERRORS.TypedError('Http-Transport does not support listen without previous.');
|
|
68
|
+
}
|
|
69
|
+
const devicesS = await this._post({
|
|
70
|
+
url: '/listen',
|
|
71
|
+
body: old,
|
|
72
|
+
});
|
|
73
|
+
const devices = check.devices(devicesS);
|
|
74
|
+
return devices;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async enumerate() {
|
|
78
|
+
const devicesS = await this._post({ url: '/enumerate' });
|
|
79
|
+
const devices = check.devices(devicesS);
|
|
80
|
+
return devices;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
_acquireMixed(input: AcquireInput) {
|
|
84
|
+
const previousStr = input.previous == null ? 'null' : encodeURIComponent(input.previous);
|
|
85
|
+
// @ts-expect-error
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
87
|
+
const path = encodeURIComponent(input.path);
|
|
88
|
+
const url = `/acquire/${path}/${previousStr}`;
|
|
89
|
+
return this._post({ url });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async acquire(input: AcquireInput) {
|
|
93
|
+
const acquireS = await this._acquireMixed(input);
|
|
94
|
+
return check.acquire(acquireS);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async release(session: string, onclose: boolean) {
|
|
98
|
+
const res = this._post({
|
|
99
|
+
url: `/release/${session}`,
|
|
100
|
+
});
|
|
101
|
+
if (onclose) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
await res;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async call(session: string, name: string, data: Record<string, unknown>) {
|
|
108
|
+
if (this._messages == null) {
|
|
109
|
+
throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
|
|
110
|
+
}
|
|
111
|
+
const messages = this._messages;
|
|
112
|
+
if (LogBlockCommand.has(name)) {
|
|
113
|
+
this.Log.debug('call-', ' name: ', name);
|
|
114
|
+
} else {
|
|
115
|
+
this.Log.debug('call-', ' name: ', name, ' data: ', data);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const o = buildOne(messages, name, data);
|
|
119
|
+
const outData = o.toString('hex');
|
|
120
|
+
const resData = await this._post({
|
|
121
|
+
url: `/call/${session}`,
|
|
122
|
+
body: outData,
|
|
123
|
+
timeout: name === 'Initialize' ? 10000 : undefined,
|
|
124
|
+
});
|
|
125
|
+
if (typeof resData !== 'string') {
|
|
126
|
+
throw ERRORS.TypedError(HardwareErrorCode.NetworkError, 'Returning data is not string.');
|
|
127
|
+
}
|
|
128
|
+
const jsonData = receiveOne(messages, resData);
|
|
129
|
+
return check.call(jsonData);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async post(session: string, name: string, data: Record<string, unknown>) {
|
|
133
|
+
if (this._messages == null) {
|
|
134
|
+
throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
|
|
135
|
+
}
|
|
136
|
+
const messages = this._messages;
|
|
137
|
+
const outData = buildOne(messages, name, data).toString('hex');
|
|
138
|
+
await this._post({
|
|
139
|
+
url: `/post/${session}`,
|
|
140
|
+
body: outData,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async read(session: string) {
|
|
145
|
+
if (this._messages == null) {
|
|
146
|
+
throw ERRORS.TypedError(HardwareErrorCode.TransportNotConfigured);
|
|
147
|
+
}
|
|
148
|
+
const messages = this._messages;
|
|
149
|
+
const resData = await this._post({
|
|
150
|
+
url: `/read/${session}`,
|
|
151
|
+
});
|
|
152
|
+
if (typeof resData !== 'string') {
|
|
153
|
+
throw ERRORS.TypedError(HardwareErrorCode.NetworkError, 'Returning data is not string.');
|
|
154
|
+
}
|
|
155
|
+
const jsonData = receiveOne(messages, resData);
|
|
156
|
+
return check.call(jsonData);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
requestDevice() {
|
|
160
|
+
// eslint-disable-next-line prefer-promise-reject-errors
|
|
161
|
+
return Promise.reject();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
stop() {
|
|
165
|
+
this.stopped = true;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
cancel() {
|
|
169
|
+
this.Log.debug('canceled');
|
|
170
|
+
}
|
|
171
|
+
}
|