@ukeyfe/hardware-transport-emulator 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 ADDED
@@ -0,0 +1,93 @@
1
+ # UKey Hardware Emulator Transport
2
+
3
+ This package provides HTTP-based transport for connecting to UKey hardware emulator.
4
+
5
+ ## Features
6
+
7
+ - HTTP-based communication with emulator server
8
+ - Compatible with UKey Connect SDK
9
+ - Support for switchTransport functionality
10
+ - Default emulator server URL: `http://localhost:21333`
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install @ukeyfe/hardware-transport-emulator
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ### Basic Usage
21
+
22
+ ```javascript
23
+ import EmulatorTransport from '@ukeyfe/hardware-transport-emulator';
24
+
25
+ // Create transport instance
26
+ const transport = new EmulatorTransport();
27
+ // or with custom URL
28
+ const transport = new EmulatorTransport('http://localhost:21333');
29
+
30
+ // Initialize transport
31
+ await transport.init(logger);
32
+
33
+ // Configure with protobuf messages
34
+ await transport.configure(signedData);
35
+ ```
36
+
37
+ ### With UKey Connect SDK
38
+
39
+ ```javascript
40
+ import HardwareSDK from '@ukeyfe/hardware-web-sdk';
41
+
42
+ // Initialize with emulator environment
43
+ await HardwareSDK.init({
44
+ env: 'emulator',
45
+ debug: true
46
+ });
47
+
48
+ // Switch to emulator transport
49
+ await HardwareSDK.switchTransport('emulator');
50
+ ```
51
+
52
+ ### In Connect Examples
53
+
54
+ The emulator transport is integrated into the connect examples:
55
+
56
+ 1. **Expo Example**: Select "Emulator" from the transport picker
57
+ 2. **Electron Example**: Use switchTransport API to switch to emulator
58
+
59
+ ## API
60
+
61
+ ### Constructor
62
+
63
+ ```javascript
64
+ new EmulatorTransport(url?: string)
65
+ ```
66
+
67
+ - `url` (optional): Emulator server URL, defaults to `http://localhost:21333`
68
+
69
+ ### Methods
70
+
71
+ All methods implement the standard UKey Transport interface:
72
+
73
+ - `init(logger)`: Initialize transport
74
+ - `configure(signedData)`: Configure protobuf messages
75
+ - `enumerate()`: List available devices
76
+ - `acquire(input)`: Acquire device session
77
+ - `release(session, onclose)`: Release device session
78
+ - `call(session, name, data)`: Call device method
79
+ - `stop()`: Stop transport
80
+
81
+ ## Emulator Server
82
+
83
+ Make sure your UKey emulator server is running on the configured URL (default: `http://localhost:21333`) before using this transport.
84
+
85
+ ## Development
86
+
87
+ ```bash
88
+ # Build
89
+ npm run build
90
+
91
+ # Development mode
92
+ npm run dev
93
+ ```
@@ -0,0 +1,2 @@
1
+ export declare const DEFAULT_URL = "http://localhost:21333";
2
+ //# sourceMappingURL=constants.d.ts.map
@@ -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"}
@@ -0,0 +1,36 @@
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 EmulatorTransport {
10
+ _messages: ReturnType<typeof _ukeyfe_hardware_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<UKeyDeviceInfoWithSession>): Promise<UKeyDeviceInfoWithSession[]>;
24
+ enumerate(): Promise<UKeyDeviceInfoWithSession[]>;
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<_ukeyfe_hardware_transport.MessageFromUKey>;
29
+ post(session: string, name: string, data: Record<string, unknown>): Promise<void>;
30
+ read(session: string): Promise<_ukeyfe_hardware_transport.MessageFromUKey>;
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,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,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,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,262 @@
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;
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 hardwareShared.HardwareError({
89
+ errorCode: hardwareShared.HardwareErrorCode.NetworkError,
90
+ message: resJson.error,
91
+ });
92
+ }
93
+ else {
94
+ throw new hardwareShared.HardwareError({ errorCode: hardwareShared.HardwareErrorCode.NetworkError, message: res.data });
95
+ }
96
+ });
97
+ }
98
+ axios__default["default"].interceptors.request.use((config) => {
99
+ var _a;
100
+ if (typeof window !== 'undefined') {
101
+ return config;
102
+ }
103
+ if ((_a = config.url) === null || _a === void 0 ? void 0 : _a.startsWith('http://localhost:21333')) {
104
+ if (!config.headers.get('Origin')) {
105
+ console.log('set node request origin');
106
+ config.headers.set('Origin', 'https://jssdk.onekey.so');
107
+ }
108
+ }
109
+ return config;
110
+ });
111
+
112
+ const DEFAULT_URL = 'http://localhost:21333';
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(hardwareShared.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 hardwareShared.ERRORS.TypedError('Http-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 hardwareShared.ERRORS.TypedError(hardwareShared.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 hardwareShared.ERRORS.TypedError(hardwareShared.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 hardwareShared.ERRORS.TypedError(hardwareShared.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 hardwareShared.ERRORS.TypedError(hardwareShared.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 hardwareShared.ERRORS.TypedError(hardwareShared.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,32 @@
1
+ {
2
+ "name": "@ukeyfe/hardware-transport-emulator",
3
+ "version": "1.1.13",
4
+ "description": "hardware emulator 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
+ }
@@ -0,0 +1 @@
1
+ export const DEFAULT_URL = 'http://localhost:21333';
package/src/http.ts ADDED
@@ -0,0 +1,82 @@
1
+ import axios, { AxiosRequestConfig, InternalAxiosRequestConfig } from 'axios';
2
+ import { HardwareError, HardwareErrorCode } from '@ukeyfe/hardware-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: InternalAxiosRequestConfig) => {
70
+ if (typeof window !== 'undefined') {
71
+ return config;
72
+ }
73
+ // node environment
74
+ if (config.url?.startsWith('http://localhost:21333')) {
75
+ if (!config.headers.get('Origin')) {
76
+ console.log('set node request origin');
77
+ // add Origin field for request headers
78
+ config.headers.set('Origin', 'https://jssdk.onekey.so');
79
+ }
80
+ }
81
+ return config;
82
+ });
package/src/index.ts ADDED
@@ -0,0 +1,175 @@
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 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<UKeyDeviceInfoWithSession>) {
70
+ if (old === null) {
71
+ throw ERRORS.TypedError('Http-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
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "../../tsconfig.lib.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ },
6
+ "include": ["src"]
7
+ }