@onekeyfe/hd-core 1.1.34-alpha.0 → 1.1.34-alpha.2
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/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +394 -0
- package/__tests__/networkUtils.test.ts +386 -0
- package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
- package/dist/api/firmware/getBinary.d.ts +7 -3
- package/dist/api/firmware/getBinary.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +318 -44
- package/dist/utils/assets.d.ts +2 -1
- package/dist/utils/assets.d.ts.map +1 -1
- package/dist/utils/networkUtils.d.ts +9 -1
- package/dist/utils/networkUtils.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV2.ts +122 -27
- package/src/api/firmware/getBinary.ts +8 -3
- package/src/core/index.ts +11 -0
- package/src/utils/assets.ts +4 -1
- package/src/utils/networkUtils.ts +270 -14
package/dist/index.js
CHANGED
|
@@ -939,30 +939,230 @@ const LoggerMap = {
|
|
|
939
939
|
};
|
|
940
940
|
const getLogger = (key) => LoggerMap[key];
|
|
941
941
|
|
|
942
|
-
const
|
|
942
|
+
const MAX_RETRY_COUNT = 3;
|
|
943
|
+
const REQUEST_PHASE_TIMEOUT_CODES = {
|
|
944
|
+
connect: 'ECONNECTTIMEDOUT',
|
|
945
|
+
read: 'EREADTIMEDOUT',
|
|
946
|
+
};
|
|
947
|
+
const RETRYABLE_HTTP_STATUSES = new Set([500, 502, 503, 504]);
|
|
948
|
+
const RETRYABLE_NETWORK_ERROR_CODES = new Set([
|
|
949
|
+
REQUEST_PHASE_TIMEOUT_CODES.connect,
|
|
950
|
+
REQUEST_PHASE_TIMEOUT_CODES.read,
|
|
951
|
+
'ECONNABORTED',
|
|
952
|
+
'ECONNREFUSED',
|
|
953
|
+
'ECONNRESET',
|
|
954
|
+
'EHOSTUNREACH',
|
|
955
|
+
'ENETDOWN',
|
|
956
|
+
'ENETUNREACH',
|
|
957
|
+
'ENOTFOUND',
|
|
958
|
+
'EAI_AGAIN',
|
|
959
|
+
'ERR_NETWORK',
|
|
960
|
+
'ETIMEDOUT',
|
|
961
|
+
]);
|
|
962
|
+
const createAbortError = () => {
|
|
963
|
+
const error = new Error('httpRequest aborted');
|
|
964
|
+
error.name = 'AbortError';
|
|
965
|
+
return error;
|
|
966
|
+
};
|
|
967
|
+
const createOverallTimeoutError = (url, timeoutMs) => {
|
|
968
|
+
const error = new Error(`httpRequest overall timeout: ${url} ${timeoutMs}ms`);
|
|
969
|
+
error.name = 'HttpRequestOverallTimeoutError';
|
|
970
|
+
return error;
|
|
971
|
+
};
|
|
972
|
+
const createRequestPhaseTimeoutError = (url, phase, timeoutMs) => {
|
|
973
|
+
const error = new Error(`httpRequest ${phase} timeout: ${url} ${timeoutMs}ms`);
|
|
974
|
+
error.name = 'HttpRequestPhaseTimeoutError';
|
|
975
|
+
return Object.assign(error, {
|
|
976
|
+
code: REQUEST_PHASE_TIMEOUT_CODES[phase],
|
|
977
|
+
});
|
|
978
|
+
};
|
|
979
|
+
const waitForRetry = (delayMs, attempt, signal) => __awaiter(void 0, void 0, void 0, function* () {
|
|
980
|
+
const backoffMs = delayMs * Math.pow(2, attempt);
|
|
981
|
+
if (backoffMs <= 0) {
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
yield new Promise((resolve, reject) => {
|
|
985
|
+
const timeoutTimer = setTimeout(() => {
|
|
986
|
+
signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', handleAbort);
|
|
987
|
+
resolve();
|
|
988
|
+
}, backoffMs);
|
|
989
|
+
const handleAbort = () => {
|
|
990
|
+
clearTimeout(timeoutTimer);
|
|
991
|
+
reject(createAbortError());
|
|
992
|
+
};
|
|
993
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
994
|
+
handleAbort();
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
signal === null || signal === void 0 ? void 0 : signal.addEventListener('abort', handleAbort, { once: true });
|
|
998
|
+
});
|
|
999
|
+
});
|
|
1000
|
+
const isRetryableRequestError = (error) => {
|
|
1001
|
+
var _a;
|
|
1002
|
+
if (axios__default["default"].isCancel(error)) {
|
|
1003
|
+
return false;
|
|
1004
|
+
}
|
|
1005
|
+
if (typeof error === 'object' &&
|
|
1006
|
+
error !== null &&
|
|
1007
|
+
'code' in error &&
|
|
1008
|
+
typeof error.code === 'string' &&
|
|
1009
|
+
RETRYABLE_NETWORK_ERROR_CODES.has(error.code)) {
|
|
1010
|
+
return true;
|
|
1011
|
+
}
|
|
1012
|
+
if (!axios__default["default"].isAxiosError(error)) {
|
|
1013
|
+
return false;
|
|
1014
|
+
}
|
|
1015
|
+
const status = (_a = error.response) === null || _a === void 0 ? void 0 : _a.status;
|
|
1016
|
+
if (status !== undefined) {
|
|
1017
|
+
return RETRYABLE_HTTP_STATUSES.has(Number(status));
|
|
1018
|
+
}
|
|
1019
|
+
return false;
|
|
1020
|
+
};
|
|
1021
|
+
const httpRequest$1 = (url, type = 'text', options = {}) => __awaiter(void 0, void 0, void 0, function* () {
|
|
943
1022
|
const headers = {};
|
|
944
1023
|
if (url.indexOf('ngrok-free.app') > -1) {
|
|
945
1024
|
headers['ngrok-skip-browser-warning'] = true;
|
|
946
1025
|
}
|
|
947
|
-
const
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
1026
|
+
const timeoutMs = Number.isSafeInteger(options.timeoutMs) && Number(options.timeoutMs) > 0
|
|
1027
|
+
? Number(options.timeoutMs)
|
|
1028
|
+
: undefined;
|
|
1029
|
+
const connectTimeoutMs = Number.isSafeInteger(options.connectTimeoutMs) && Number(options.connectTimeoutMs) > 0
|
|
1030
|
+
? Number(options.connectTimeoutMs)
|
|
1031
|
+
: undefined;
|
|
1032
|
+
const readTimeoutMs = Number.isSafeInteger(options.readTimeoutMs) && Number(options.readTimeoutMs) > 0
|
|
1033
|
+
? Number(options.readTimeoutMs)
|
|
1034
|
+
: undefined;
|
|
1035
|
+
const overallTimeoutMs = Number.isSafeInteger(options.overallTimeoutMs) && Number(options.overallTimeoutMs) > 0
|
|
1036
|
+
? Number(options.overallTimeoutMs)
|
|
1037
|
+
: undefined;
|
|
1038
|
+
const maxRetries = Number.isSafeInteger(options.maxRetries) && Number(options.maxRetries) > 0
|
|
1039
|
+
? Math.min(Number(options.maxRetries), MAX_RETRY_COUNT)
|
|
1040
|
+
: 0;
|
|
1041
|
+
const retryDelayMs = Number.isSafeInteger(options.retryDelayMs) && Number(options.retryDelayMs) > 0
|
|
1042
|
+
? Number(options.retryDelayMs)
|
|
1043
|
+
: 0;
|
|
1044
|
+
const overallTimeoutError = overallTimeoutMs
|
|
1045
|
+
? createOverallTimeoutError(url, overallTimeoutMs)
|
|
1046
|
+
: undefined;
|
|
1047
|
+
const overallDeadlineAt = overallTimeoutMs ? Date.now() + overallTimeoutMs : undefined;
|
|
1048
|
+
const assertWithinOverallDeadline = () => {
|
|
1049
|
+
if (overallDeadlineAt !== undefined && overallTimeoutError && Date.now() >= overallDeadlineAt) {
|
|
1050
|
+
throw overallTimeoutError;
|
|
956
1051
|
}
|
|
957
|
-
|
|
1052
|
+
};
|
|
1053
|
+
const request = (attempt, signal) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1054
|
+
assertWithinOverallDeadline();
|
|
1055
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
1056
|
+
throw createAbortError();
|
|
1057
|
+
}
|
|
1058
|
+
let response;
|
|
1059
|
+
let requestError;
|
|
1060
|
+
let requestFailed = false;
|
|
1061
|
+
let phaseTimeoutError;
|
|
1062
|
+
let phaseTimeoutTimer;
|
|
1063
|
+
const hasPhaseDeadline = Boolean(connectTimeoutMs || readTimeoutMs);
|
|
1064
|
+
const attemptController = signal || hasPhaseDeadline ? new AbortController() : undefined;
|
|
1065
|
+
let attemptSettled = false;
|
|
1066
|
+
let lastProgressLoaded = 0;
|
|
1067
|
+
const handleParentAbort = () => {
|
|
1068
|
+
attemptController === null || attemptController === void 0 ? void 0 : attemptController.abort();
|
|
1069
|
+
};
|
|
1070
|
+
const clearPhaseTimeout = () => {
|
|
1071
|
+
if (phaseTimeoutTimer) {
|
|
1072
|
+
clearTimeout(phaseTimeoutTimer);
|
|
1073
|
+
phaseTimeoutTimer = undefined;
|
|
1074
|
+
}
|
|
1075
|
+
};
|
|
1076
|
+
const armPhaseTimeout = (phase, phaseTimeoutMs) => {
|
|
1077
|
+
clearPhaseTimeout();
|
|
1078
|
+
if (!phaseTimeoutMs || !attemptController) {
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
phaseTimeoutTimer = setTimeout(() => {
|
|
1082
|
+
phaseTimeoutError = createRequestPhaseTimeoutError(url, phase, phaseTimeoutMs);
|
|
1083
|
+
attemptController.abort();
|
|
1084
|
+
}, phaseTimeoutMs);
|
|
1085
|
+
};
|
|
1086
|
+
if (signal) {
|
|
1087
|
+
signal.addEventListener('abort', handleParentAbort, { once: true });
|
|
1088
|
+
}
|
|
1089
|
+
armPhaseTimeout('connect', connectTimeoutMs);
|
|
1090
|
+
try {
|
|
1091
|
+
response = yield axios__default["default"].request(Object.assign(Object.assign(Object.assign({ url, withCredentials: false, responseType: type === 'binary' ? 'arraybuffer' : 'json', headers }, (timeoutMs ? { timeout: timeoutMs } : {})), (attemptController ? { signal: attemptController.signal } : {})), (hasPhaseDeadline
|
|
1092
|
+
? {
|
|
1093
|
+
adapter: ['xhr', 'http'],
|
|
1094
|
+
onDownloadProgress: (progressEvent) => {
|
|
1095
|
+
if (attemptSettled ||
|
|
1096
|
+
(attemptController === null || attemptController === void 0 ? void 0 : attemptController.signal.aborted) ||
|
|
1097
|
+
!Number.isFinite(progressEvent.loaded) ||
|
|
1098
|
+
progressEvent.loaded <= lastProgressLoaded) {
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
lastProgressLoaded = progressEvent.loaded;
|
|
1102
|
+
armPhaseTimeout('read', readTimeoutMs);
|
|
1103
|
+
},
|
|
1104
|
+
}
|
|
1105
|
+
: {})));
|
|
1106
|
+
if (phaseTimeoutError) {
|
|
1107
|
+
requestFailed = true;
|
|
1108
|
+
requestError = phaseTimeoutError;
|
|
1109
|
+
response = undefined;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
catch (error) {
|
|
1113
|
+
requestFailed = true;
|
|
1114
|
+
requestError = phaseTimeoutError !== null && phaseTimeoutError !== void 0 ? phaseTimeoutError : ((signal === null || signal === void 0 ? void 0 : signal.aborted) ? createAbortError() : error);
|
|
1115
|
+
}
|
|
1116
|
+
finally {
|
|
1117
|
+
attemptSettled = true;
|
|
1118
|
+
clearPhaseTimeout();
|
|
1119
|
+
signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', handleParentAbort);
|
|
1120
|
+
}
|
|
1121
|
+
assertWithinOverallDeadline();
|
|
1122
|
+
if (requestFailed) {
|
|
1123
|
+
if (attempt >= maxRetries || !isRetryableRequestError(requestError)) {
|
|
1124
|
+
throw requestError;
|
|
1125
|
+
}
|
|
1126
|
+
yield waitForRetry(retryDelayMs, attempt, signal);
|
|
1127
|
+
assertWithinOverallDeadline();
|
|
1128
|
+
return request(attempt + 1, signal);
|
|
1129
|
+
}
|
|
1130
|
+
if (!response) {
|
|
1131
|
+
throw new Error(`httpRequest completed without a response: ${url}`);
|
|
1132
|
+
}
|
|
1133
|
+
if (+response.status === 200) {
|
|
958
1134
|
return response.data;
|
|
959
1135
|
}
|
|
960
|
-
|
|
1136
|
+
if (RETRYABLE_HTTP_STATUSES.has(Number(response.status)) && attempt < maxRetries) {
|
|
1137
|
+
yield waitForRetry(retryDelayMs, attempt, signal);
|
|
1138
|
+
assertWithinOverallDeadline();
|
|
1139
|
+
return request(attempt + 1, signal);
|
|
1140
|
+
}
|
|
1141
|
+
throw new Error(`httpRequest error: ${url} ${response.statusText}`);
|
|
1142
|
+
});
|
|
1143
|
+
if (!overallTimeoutMs) {
|
|
1144
|
+
return request(0);
|
|
1145
|
+
}
|
|
1146
|
+
const controller = new AbortController();
|
|
1147
|
+
const requestPromise = request(0, controller.signal);
|
|
1148
|
+
let timeoutTimer;
|
|
1149
|
+
try {
|
|
1150
|
+
return yield new Promise((resolve, reject) => {
|
|
1151
|
+
timeoutTimer = setTimeout(() => {
|
|
1152
|
+
reject(overallTimeoutError);
|
|
1153
|
+
controller.abort();
|
|
1154
|
+
}, overallTimeoutMs);
|
|
1155
|
+
requestPromise.then(resolve, reject);
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
finally {
|
|
1159
|
+
if (timeoutTimer) {
|
|
1160
|
+
clearTimeout(timeoutTimer);
|
|
1161
|
+
}
|
|
961
1162
|
}
|
|
962
|
-
throw new Error(`httpRequest error: ${url} ${response.statusText}`);
|
|
963
1163
|
});
|
|
964
1164
|
|
|
965
|
-
const httpRequest = (url, type) => httpRequest$1(url, type);
|
|
1165
|
+
const httpRequest = (url, type, options) => httpRequest$1(url, type, options);
|
|
966
1166
|
const getTimeStamp = () => new Date().getTime();
|
|
967
1167
|
|
|
968
1168
|
const VER_NUMS = 3;
|
|
@@ -29699,7 +29899,7 @@ class DeviceWipe extends BaseMethod {
|
|
|
29699
29899
|
}
|
|
29700
29900
|
}
|
|
29701
29901
|
|
|
29702
|
-
const getBinary = ({ features, updateType, version, isUpdateBootloader, firmwareType, }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
29902
|
+
const getBinary = ({ features, updateType, version, isUpdateBootloader, firmwareType, requestOptions, }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
29703
29903
|
const releaseInfo = getInfo({
|
|
29704
29904
|
features,
|
|
29705
29905
|
updateType,
|
|
@@ -29723,7 +29923,7 @@ const getBinary = ({ features, updateType, version, isUpdateBootloader, firmware
|
|
|
29723
29923
|
: releaseInfo.url;
|
|
29724
29924
|
let fw;
|
|
29725
29925
|
try {
|
|
29726
|
-
fw = yield httpRequest(url, 'binary');
|
|
29926
|
+
fw = yield httpRequest(url, 'binary', requestOptions);
|
|
29727
29927
|
}
|
|
29728
29928
|
catch (_a) {
|
|
29729
29929
|
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'Method_FirmwareUpdate_DownloadFailed');
|
|
@@ -30793,6 +30993,59 @@ class FirmwareUpdate extends BaseMethod {
|
|
|
30793
30993
|
}
|
|
30794
30994
|
|
|
30795
30995
|
const Log$5 = getLogger(exports.LoggerNames.Method);
|
|
30996
|
+
const FIRMWARE_DOWNLOAD_REQUEST_OPTIONS = {
|
|
30997
|
+
connectTimeoutMs: 60000,
|
|
30998
|
+
readTimeoutMs: 60000,
|
|
30999
|
+
overallTimeoutMs: 180000,
|
|
31000
|
+
maxRetries: 2,
|
|
31001
|
+
retryDelayMs: 500,
|
|
31002
|
+
};
|
|
31003
|
+
const normalizeFirmwareBinary = (binary) => {
|
|
31004
|
+
var _a;
|
|
31005
|
+
if (typeof binary !== 'object' || binary === null) {
|
|
31006
|
+
return undefined;
|
|
31007
|
+
}
|
|
31008
|
+
const isNodeBuffer = typeof Buffer !== 'undefined' &&
|
|
31009
|
+
typeof Buffer.isBuffer === 'function' &&
|
|
31010
|
+
Buffer.isBuffer(binary);
|
|
31011
|
+
if (isNodeBuffer) {
|
|
31012
|
+
return binary.byteLength > 0 ? binary : undefined;
|
|
31013
|
+
}
|
|
31014
|
+
if (typeof ArrayBuffer !== 'undefined' && binary instanceof ArrayBuffer) {
|
|
31015
|
+
return binary.byteLength > 0 ? binary : undefined;
|
|
31016
|
+
}
|
|
31017
|
+
if (typeof ArrayBuffer !== 'undefined' &&
|
|
31018
|
+
typeof ArrayBuffer.isView === 'function' &&
|
|
31019
|
+
ArrayBuffer.isView(binary)) {
|
|
31020
|
+
if (binary.byteLength <= 0) {
|
|
31021
|
+
return undefined;
|
|
31022
|
+
}
|
|
31023
|
+
const source = new Uint8Array(binary.buffer, binary.byteOffset, binary.byteLength);
|
|
31024
|
+
const normalized = new Uint8Array(binary.byteLength);
|
|
31025
|
+
normalized.set(source);
|
|
31026
|
+
return normalized.buffer;
|
|
31027
|
+
}
|
|
31028
|
+
const customBuffer = binary;
|
|
31029
|
+
if (typeof ((_a = customBuffer.constructor) === null || _a === void 0 ? void 0 : _a.isBuffer) !== 'function' ||
|
|
31030
|
+
!customBuffer.constructor.isBuffer(binary) ||
|
|
31031
|
+
typeof customBuffer.byteLength !== 'number' ||
|
|
31032
|
+
!Number.isSafeInteger(customBuffer.byteLength) ||
|
|
31033
|
+
customBuffer.byteLength <= 0 ||
|
|
31034
|
+
typeof customBuffer.length !== 'number' ||
|
|
31035
|
+
customBuffer.length !== customBuffer.byteLength) {
|
|
31036
|
+
return undefined;
|
|
31037
|
+
}
|
|
31038
|
+
const { byteLength } = customBuffer;
|
|
31039
|
+
const normalized = new Uint8Array(byteLength);
|
|
31040
|
+
for (let index = 0; index < byteLength; index += 1) {
|
|
31041
|
+
const { [index]: byte } = customBuffer;
|
|
31042
|
+
if (typeof byte !== 'number' || !Number.isInteger(byte) || byte < 0 || byte > 255) {
|
|
31043
|
+
return undefined;
|
|
31044
|
+
}
|
|
31045
|
+
normalized[index] = byte;
|
|
31046
|
+
}
|
|
31047
|
+
return normalized.buffer;
|
|
31048
|
+
};
|
|
30796
31049
|
class FirmwareUpdateV2 extends BaseMethod {
|
|
30797
31050
|
constructor() {
|
|
30798
31051
|
super(...arguments);
|
|
@@ -30973,7 +31226,7 @@ class FirmwareUpdateV2 extends BaseMethod {
|
|
|
30973
31226
|
}
|
|
30974
31227
|
}
|
|
30975
31228
|
run() {
|
|
30976
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
31229
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
30977
31230
|
return __awaiter(this, void 0, void 0, function* () {
|
|
30978
31231
|
const { device, params } = this;
|
|
30979
31232
|
const { features, commands } = device;
|
|
@@ -30981,6 +31234,43 @@ class FirmwareUpdateV2 extends BaseMethod {
|
|
|
30981
31234
|
const deviceFirmwareType = getFirmwareType(device.features);
|
|
30982
31235
|
const firmwareType = (_a = params.firmwareType) !== null && _a !== void 0 ? _a : deviceFirmwareType;
|
|
30983
31236
|
this.checkVersionForCopyTouchResource(features, firmwareType);
|
|
31237
|
+
let preparedBinary;
|
|
31238
|
+
const acquireFirmwareBinary = () => __awaiter(this, void 0, void 0, function* () {
|
|
31239
|
+
var _l;
|
|
31240
|
+
try {
|
|
31241
|
+
if (preparedBinary) {
|
|
31242
|
+
return preparedBinary;
|
|
31243
|
+
}
|
|
31244
|
+
if (params.binary !== undefined) {
|
|
31245
|
+
preparedBinary = normalizeFirmwareBinary(params.binary);
|
|
31246
|
+
if (!preparedBinary) {
|
|
31247
|
+
throw new Error('firmware binary is empty or invalid');
|
|
31248
|
+
}
|
|
31249
|
+
return preparedBinary;
|
|
31250
|
+
}
|
|
31251
|
+
if (!device.features) {
|
|
31252
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'no features found for this device');
|
|
31253
|
+
}
|
|
31254
|
+
this.postTipMessage('DownloadFirmware');
|
|
31255
|
+
const firmware = yield getBinary({
|
|
31256
|
+
features: device.features,
|
|
31257
|
+
version: params.version,
|
|
31258
|
+
updateType: params.updateType,
|
|
31259
|
+
isUpdateBootloader: params.isUpdateBootloader,
|
|
31260
|
+
firmwareType,
|
|
31261
|
+
requestOptions: FIRMWARE_DOWNLOAD_REQUEST_OPTIONS,
|
|
31262
|
+
});
|
|
31263
|
+
preparedBinary = normalizeFirmwareBinary(firmware.binary);
|
|
31264
|
+
if (!preparedBinary) {
|
|
31265
|
+
throw new Error('downloaded firmware binary is empty or invalid');
|
|
31266
|
+
}
|
|
31267
|
+
this.postTipMessage('DownloadFirmwareSuccess');
|
|
31268
|
+
return preparedBinary;
|
|
31269
|
+
}
|
|
31270
|
+
catch (err) {
|
|
31271
|
+
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, (_l = err.message) !== null && _l !== void 0 ? _l : err);
|
|
31272
|
+
}
|
|
31273
|
+
});
|
|
30984
31274
|
if (!(features === null || features === void 0 ? void 0 : features.bootloader_mode) && features) {
|
|
30985
31275
|
const uuid = getDeviceUUID(features);
|
|
30986
31276
|
if (this.isEnteredManuallyBoot(features)) {
|
|
@@ -31003,6 +31293,8 @@ class FirmwareUpdateV2 extends BaseMethod {
|
|
|
31003
31293
|
}
|
|
31004
31294
|
}
|
|
31005
31295
|
(_c = (_b = this.device) === null || _b === void 0 ? void 0 : _b.commands) === null || _c === void 0 ? void 0 : _c.checkDisposed();
|
|
31296
|
+
yield acquireFirmwareBinary();
|
|
31297
|
+
(_e = (_d = this.device) === null || _d === void 0 ? void 0 : _d.commands) === null || _e === void 0 ? void 0 : _e.checkDisposed();
|
|
31006
31298
|
try {
|
|
31007
31299
|
this.postTipMessage('AutoRebootToBootloader');
|
|
31008
31300
|
const bootRes = yield commands.typedCall('DeviceBackToBoot', 'Success');
|
|
@@ -31015,9 +31307,9 @@ class FirmwareUpdateV2 extends BaseMethod {
|
|
|
31015
31307
|
DevicePool.clearDeviceCache(uuid);
|
|
31016
31308
|
}
|
|
31017
31309
|
delete DevicePool.devicesCache[''];
|
|
31018
|
-
yield ((
|
|
31310
|
+
yield ((_f = this.checkPromise) === null || _f === void 0 ? void 0 : _f.promise);
|
|
31019
31311
|
this.checkPromise = null;
|
|
31020
|
-
(
|
|
31312
|
+
(_h = (_g = this.device) === null || _g === void 0 ? void 0 : _g.commands) === null || _h === void 0 ? void 0 : _h.checkDisposed();
|
|
31021
31313
|
const isTouch = DeviceModelToTypes.model_touch.includes(deviceType);
|
|
31022
31314
|
yield wait(isTouch ? 3000 : 1500);
|
|
31023
31315
|
}
|
|
@@ -31029,31 +31321,8 @@ class FirmwareUpdateV2 extends BaseMethod {
|
|
|
31029
31321
|
return Promise.reject(hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateAutoEnterBootFailure));
|
|
31030
31322
|
}
|
|
31031
31323
|
}
|
|
31032
|
-
|
|
31033
|
-
|
|
31034
|
-
if (params.binary) {
|
|
31035
|
-
binary = this.params.binary;
|
|
31036
|
-
}
|
|
31037
|
-
else {
|
|
31038
|
-
if (!device.features) {
|
|
31039
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.RuntimeError, 'no features found for this device');
|
|
31040
|
-
}
|
|
31041
|
-
this.postTipMessage('DownloadFirmware');
|
|
31042
|
-
const firmware = yield getBinary({
|
|
31043
|
-
features: device.features,
|
|
31044
|
-
version: params.version,
|
|
31045
|
-
updateType: params.updateType,
|
|
31046
|
-
isUpdateBootloader: params.isUpdateBootloader,
|
|
31047
|
-
firmwareType,
|
|
31048
|
-
});
|
|
31049
|
-
binary = firmware.binary;
|
|
31050
|
-
this.postTipMessage('DownloadFirmwareSuccess');
|
|
31051
|
-
}
|
|
31052
|
-
}
|
|
31053
|
-
catch (err) {
|
|
31054
|
-
throw hdShared.ERRORS.TypedError(hdShared.HardwareErrorCode.FirmwareUpdateDownloadFailed, (_g = err.message) !== null && _g !== void 0 ? _g : err);
|
|
31055
|
-
}
|
|
31056
|
-
(_j = (_h = this.device) === null || _h === void 0 ? void 0 : _h.commands) === null || _j === void 0 ? void 0 : _j.checkDisposed();
|
|
31324
|
+
const binary = yield acquireFirmwareBinary();
|
|
31325
|
+
(_k = (_j = this.device) === null || _j === void 0 ? void 0 : _j.commands) === null || _k === void 0 ? void 0 : _k.checkDisposed();
|
|
31057
31326
|
yield this.device.acquire();
|
|
31058
31327
|
const response = yield uploadFirmware(params.updateType, this.device.getCommands().typedCall.bind(this.device.getCommands()), this.postMessage, device, { payload: binary, rebootOnSuccess: true }, params.isUpdateBootloader);
|
|
31059
31328
|
if (this.connectId) {
|
|
@@ -41631,6 +41900,11 @@ function connectDeviceForBle(method, device, retryCount = 0) {
|
|
|
41631
41900
|
}
|
|
41632
41901
|
}
|
|
41633
41902
|
catch (err) {
|
|
41903
|
+
if (hdShared.ERROR_CODES_REQUIRE_DISCONNECT.includes(err.errorCode) &&
|
|
41904
|
+
device.mainId &&
|
|
41905
|
+
device.deviceConnector) {
|
|
41906
|
+
yield device.deviceConnector.disconnect(device.mainId).catch(() => undefined);
|
|
41907
|
+
}
|
|
41634
41908
|
if (err.errorCode === hdShared.HardwareErrorCode.BleTimeoutError && retryCount < 6) {
|
|
41635
41909
|
const nextRetry = retryCount + 1;
|
|
41636
41910
|
Log.debug(`Bluetooth connect timeout and will retry, retry count: ${nextRetry}`);
|
package/dist/utils/assets.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import type { HttpRequestOptions } from './networkUtils';
|
|
2
|
+
export declare const httpRequest: (url: string, type: string, options?: HttpRequestOptions) => any;
|
|
2
3
|
export declare const getTimeStamp: () => number;
|
|
3
4
|
//# sourceMappingURL=assets.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../src/utils/assets.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,WAAW,QAAS,MAAM,QAAQ,MAAM,KAAG,
|
|
1
|
+
{"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../src/utils/assets.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,eAAO,MAAM,WAAW,QAAS,MAAM,QAAQ,MAAM,YAAY,kBAAkB,KAAG,GAC9C,CAAC;AAEzC,eAAO,MAAM,YAAY,cAA6B,CAAC"}
|
|
@@ -1,2 +1,10 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type HttpRequestOptions = {
|
|
2
|
+
timeoutMs?: number;
|
|
3
|
+
connectTimeoutMs?: number;
|
|
4
|
+
readTimeoutMs?: number;
|
|
5
|
+
overallTimeoutMs?: number;
|
|
6
|
+
maxRetries?: number;
|
|
7
|
+
retryDelayMs?: number;
|
|
8
|
+
};
|
|
9
|
+
export declare const httpRequest: <T = unknown>(url: string, type?: string, options?: HttpRequestOptions) => Promise<T>;
|
|
2
10
|
//# sourceMappingURL=networkUtils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"networkUtils.d.ts","sourceRoot":"","sources":["../../src/utils/networkUtils.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"networkUtils.d.ts","sourceRoot":"","sources":["../../src/utils/networkUtils.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,kBAAkB,GAAG;IAE/B,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AA4FF,eAAO,MAAM,WAAW,qBACjB,MAAM,2BAEF,kBAAkB,eA4K5B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hd-core",
|
|
3
|
-
"version": "1.1.34-alpha.
|
|
3
|
+
"version": "1.1.34-alpha.2",
|
|
4
4
|
"description": "Core processes and APIs for communicating with OneKey hardware devices.",
|
|
5
5
|
"author": "OneKey",
|
|
6
6
|
"homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"url": "https://github.com/OneKeyHQ/hardware-js-sdk/issues"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@onekeyfe/hd-shared": "1.1.34-alpha.
|
|
29
|
-
"@onekeyfe/hd-transport": "1.1.34-alpha.
|
|
28
|
+
"@onekeyfe/hd-shared": "1.1.34-alpha.2",
|
|
29
|
+
"@onekeyfe/hd-transport": "1.1.34-alpha.2",
|
|
30
30
|
"axios": "1.15.2",
|
|
31
31
|
"bignumber.js": "^9.0.2",
|
|
32
32
|
"bytebuffer": "^5.0.1",
|
|
@@ -44,5 +44,5 @@
|
|
|
44
44
|
"@types/w3c-web-usb": "^1.0.10",
|
|
45
45
|
"@types/web-bluetooth": "^0.0.21"
|
|
46
46
|
},
|
|
47
|
-
"gitHead": "
|
|
47
|
+
"gitHead": "dee167c35d191f3f4e02c4cb6228e1f926f929ec"
|
|
48
48
|
}
|
|
@@ -31,6 +31,7 @@ import { DEVICE } from '../events';
|
|
|
31
31
|
|
|
32
32
|
import type { Features, KnownDevice } from '../types';
|
|
33
33
|
import type { Device } from '../device/Device';
|
|
34
|
+
import type { FirmwareBinary } from './firmware/getBinary';
|
|
34
35
|
|
|
35
36
|
type Params = {
|
|
36
37
|
binary?: ArrayBuffer;
|
|
@@ -43,6 +44,77 @@ type Params = {
|
|
|
43
44
|
|
|
44
45
|
const Log = getLogger(LoggerNames.Method);
|
|
45
46
|
|
|
47
|
+
const FIRMWARE_DOWNLOAD_REQUEST_OPTIONS = {
|
|
48
|
+
connectTimeoutMs: 60_000,
|
|
49
|
+
readTimeoutMs: 60_000,
|
|
50
|
+
overallTimeoutMs: 180_000,
|
|
51
|
+
maxRetries: 2,
|
|
52
|
+
retryDelayMs: 500,
|
|
53
|
+
} as const;
|
|
54
|
+
|
|
55
|
+
const normalizeFirmwareBinary = (binary: unknown): FirmwareBinary | undefined => {
|
|
56
|
+
if (typeof binary !== 'object' || binary === null) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const isNodeBuffer =
|
|
61
|
+
typeof Buffer !== 'undefined' &&
|
|
62
|
+
typeof Buffer.isBuffer === 'function' &&
|
|
63
|
+
Buffer.isBuffer(binary);
|
|
64
|
+
if (isNodeBuffer) {
|
|
65
|
+
return binary.byteLength > 0 ? binary : undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (typeof ArrayBuffer !== 'undefined' && binary instanceof ArrayBuffer) {
|
|
69
|
+
return binary.byteLength > 0 ? binary : undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (
|
|
73
|
+
typeof ArrayBuffer !== 'undefined' &&
|
|
74
|
+
typeof ArrayBuffer.isView === 'function' &&
|
|
75
|
+
ArrayBuffer.isView(binary)
|
|
76
|
+
) {
|
|
77
|
+
if (binary.byteLength <= 0) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
const source = new Uint8Array(binary.buffer, binary.byteOffset, binary.byteLength);
|
|
81
|
+
const normalized = new Uint8Array(binary.byteLength);
|
|
82
|
+
normalized.set(source);
|
|
83
|
+
return normalized.buffer;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const customBuffer = binary as {
|
|
87
|
+
[index: number]: unknown;
|
|
88
|
+
byteLength?: unknown;
|
|
89
|
+
constructor?: {
|
|
90
|
+
isBuffer?: (value: unknown) => boolean;
|
|
91
|
+
};
|
|
92
|
+
length?: unknown;
|
|
93
|
+
};
|
|
94
|
+
if (
|
|
95
|
+
typeof customBuffer.constructor?.isBuffer !== 'function' ||
|
|
96
|
+
!customBuffer.constructor.isBuffer(binary) ||
|
|
97
|
+
typeof customBuffer.byteLength !== 'number' ||
|
|
98
|
+
!Number.isSafeInteger(customBuffer.byteLength) ||
|
|
99
|
+
customBuffer.byteLength <= 0 ||
|
|
100
|
+
typeof customBuffer.length !== 'number' ||
|
|
101
|
+
customBuffer.length !== customBuffer.byteLength
|
|
102
|
+
) {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const { byteLength } = customBuffer;
|
|
107
|
+
const normalized = new Uint8Array(byteLength);
|
|
108
|
+
for (let index = 0; index < byteLength; index += 1) {
|
|
109
|
+
const { [index]: byte } = customBuffer;
|
|
110
|
+
if (typeof byte !== 'number' || !Number.isInteger(byte) || byte < 0 || byte > 255) {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
normalized[index] = byte;
|
|
114
|
+
}
|
|
115
|
+
return normalized.buffer;
|
|
116
|
+
};
|
|
117
|
+
|
|
46
118
|
export default class FirmwareUpdateV2 extends BaseMethod<Params> {
|
|
47
119
|
checkPromise: Deferred<any> | null = null;
|
|
48
120
|
|
|
@@ -286,6 +358,48 @@ export default class FirmwareUpdateV2 extends BaseMethod<Params> {
|
|
|
286
358
|
|
|
287
359
|
this.checkVersionForCopyTouchResource(features, firmwareType);
|
|
288
360
|
|
|
361
|
+
let preparedBinary: FirmwareBinary | undefined;
|
|
362
|
+
const acquireFirmwareBinary = async (): Promise<FirmwareBinary> => {
|
|
363
|
+
try {
|
|
364
|
+
if (preparedBinary) {
|
|
365
|
+
return preparedBinary;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (params.binary !== undefined) {
|
|
369
|
+
preparedBinary = normalizeFirmwareBinary(params.binary);
|
|
370
|
+
if (!preparedBinary) {
|
|
371
|
+
throw new Error('firmware binary is empty or invalid');
|
|
372
|
+
}
|
|
373
|
+
return preparedBinary;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (!device.features) {
|
|
377
|
+
throw ERRORS.TypedError(
|
|
378
|
+
HardwareErrorCode.RuntimeError,
|
|
379
|
+
'no features found for this device'
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
this.postTipMessage('DownloadFirmware');
|
|
384
|
+
const firmware = await getBinary({
|
|
385
|
+
features: device.features,
|
|
386
|
+
version: params.version,
|
|
387
|
+
updateType: params.updateType,
|
|
388
|
+
isUpdateBootloader: params.isUpdateBootloader,
|
|
389
|
+
firmwareType,
|
|
390
|
+
requestOptions: FIRMWARE_DOWNLOAD_REQUEST_OPTIONS,
|
|
391
|
+
});
|
|
392
|
+
preparedBinary = normalizeFirmwareBinary(firmware.binary);
|
|
393
|
+
if (!preparedBinary) {
|
|
394
|
+
throw new Error('downloaded firmware binary is empty or invalid');
|
|
395
|
+
}
|
|
396
|
+
this.postTipMessage('DownloadFirmwareSuccess');
|
|
397
|
+
return preparedBinary;
|
|
398
|
+
} catch (err) {
|
|
399
|
+
throw ERRORS.TypedError(HardwareErrorCode.FirmwareUpdateDownloadFailed, err.message ?? err);
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
|
|
289
403
|
if (!features?.bootloader_mode && features) {
|
|
290
404
|
const uuid = getDeviceUUID(features);
|
|
291
405
|
// should go to bootloader mode manually
|
|
@@ -319,6 +433,12 @@ export default class FirmwareUpdateV2 extends BaseMethod<Params> {
|
|
|
319
433
|
// check if the device commands has been disposed
|
|
320
434
|
this.device?.commands?.checkDisposed();
|
|
321
435
|
|
|
436
|
+
// A failed firmware download must leave the device in normal mode.
|
|
437
|
+
await acquireFirmwareBinary();
|
|
438
|
+
|
|
439
|
+
// The request may outlive the current transport command instance.
|
|
440
|
+
this.device?.commands?.checkDisposed();
|
|
441
|
+
|
|
322
442
|
// auto go to bootloader mode
|
|
323
443
|
try {
|
|
324
444
|
this.postTipMessage('AutoRebootToBootloader');
|
|
@@ -357,33 +477,8 @@ export default class FirmwareUpdateV2 extends BaseMethod<Params> {
|
|
|
357
477
|
}
|
|
358
478
|
}
|
|
359
479
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
try {
|
|
363
|
-
if (params.binary) {
|
|
364
|
-
binary = this.params.binary;
|
|
365
|
-
} else {
|
|
366
|
-
if (!device.features) {
|
|
367
|
-
throw ERRORS.TypedError(
|
|
368
|
-
HardwareErrorCode.RuntimeError,
|
|
369
|
-
'no features found for this device'
|
|
370
|
-
);
|
|
371
|
-
}
|
|
372
|
-
this.postTipMessage('DownloadFirmware');
|
|
373
|
-
|
|
374
|
-
const firmware = await getBinary({
|
|
375
|
-
features: device.features,
|
|
376
|
-
version: params.version,
|
|
377
|
-
updateType: params.updateType,
|
|
378
|
-
isUpdateBootloader: params.isUpdateBootloader,
|
|
379
|
-
firmwareType,
|
|
380
|
-
});
|
|
381
|
-
binary = firmware.binary;
|
|
382
|
-
this.postTipMessage('DownloadFirmwareSuccess');
|
|
383
|
-
}
|
|
384
|
-
} catch (err) {
|
|
385
|
-
throw ERRORS.TypedError(HardwareErrorCode.FirmwareUpdateDownloadFailed, err.message ?? err);
|
|
386
|
-
}
|
|
480
|
+
// Devices already in bootloader mode still acquire through the same helper.
|
|
481
|
+
const binary = await acquireFirmwareBinary();
|
|
387
482
|
|
|
388
483
|
// check if the device commands has been disposed
|
|
389
484
|
this.device?.commands?.checkDisposed();
|