tsvesync 1.4.4 → 1.4.6
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/dist/lib/constants.js +1 -0
- package/dist/lib/helpers.d.ts +5 -1
- package/dist/lib/helpers.js +25 -4
- package/package.json +2 -2
- package/src/lib/constants.ts +1 -0
- package/src/lib/helpers.ts +38 -6
package/dist/lib/constants.js
CHANGED
|
@@ -22,6 +22,7 @@ exports.CROSS_REGION_ERROR_CODES = [
|
|
|
22
22
|
// pyvesync parity: `-11001000` maps to TOKEN_EXPIRED (invalid token)
|
|
23
23
|
exports.TOKEN_ERROR_CODES = [
|
|
24
24
|
-11001000, // token expired/invalid
|
|
25
|
+
-11001022, // token has expired (observed from device list responses)
|
|
25
26
|
];
|
|
26
27
|
// Other error codes
|
|
27
28
|
exports.APP_VERSION_ERROR_CODES = [
|
package/dist/lib/helpers.d.ts
CHANGED
|
@@ -34,6 +34,9 @@ export declare const AUTH_ERROR_CODES: {
|
|
|
34
34
|
CROSS_REGION: number;
|
|
35
35
|
CROSS_REGION_ALT: number;
|
|
36
36
|
};
|
|
37
|
+
type CallApiOptions = {
|
|
38
|
+
didRetryAuth?: boolean;
|
|
39
|
+
};
|
|
37
40
|
/**
|
|
38
41
|
* Get list of country codes that use the US endpoint
|
|
39
42
|
* These are tried when we get a cross-region error
|
|
@@ -154,7 +157,7 @@ export declare class Helpers {
|
|
|
154
157
|
/**
|
|
155
158
|
* Call VeSync API
|
|
156
159
|
*/
|
|
157
|
-
static callApi(endpoint: string, method: string, data: any | undefined, headers: Record<string, string> | undefined, manager: VeSync): Promise<[any, number]>;
|
|
160
|
+
static callApi(endpoint: string, method: string, data: any | undefined, headers: Record<string, string> | undefined, manager: VeSync, options?: CallApiOptions): Promise<[any, number]>;
|
|
158
161
|
/**
|
|
159
162
|
* Calculate hex value from energy response
|
|
160
163
|
*/
|
|
@@ -180,3 +183,4 @@ export declare class Helpers {
|
|
|
180
183
|
*/
|
|
181
184
|
static authLegacyFlow(manager: VeSync): Promise<[boolean, string | null, string | null, string | null]>;
|
|
182
185
|
}
|
|
186
|
+
export {};
|
package/dist/lib/helpers.js
CHANGED
|
@@ -93,6 +93,9 @@ exports.AUTH_ERROR_CODES = {
|
|
|
93
93
|
CROSS_REGION: -11260022,
|
|
94
94
|
CROSS_REGION_ALT: -11261022
|
|
95
95
|
};
|
|
96
|
+
function isTokenExpiredMessage(msg) {
|
|
97
|
+
return typeof msg === 'string' && /token\s*(?:has\s*)?expired/i.test(msg);
|
|
98
|
+
}
|
|
96
99
|
/**
|
|
97
100
|
* Get list of country codes that use the US endpoint
|
|
98
101
|
* These are tried when we get a cross-region error
|
|
@@ -436,7 +439,8 @@ class Helpers {
|
|
|
436
439
|
/**
|
|
437
440
|
* Call VeSync API
|
|
438
441
|
*/
|
|
439
|
-
static async callApi(endpoint, method, data = null, headers = {}, manager) {
|
|
442
|
+
static async callApi(endpoint, method, data = null, headers = {}, manager, options = {}) {
|
|
443
|
+
var _a, _b;
|
|
440
444
|
let url = '';
|
|
441
445
|
try {
|
|
442
446
|
// Prefer manager-scoped base URL (pyvesync resolves per-manager from current region).
|
|
@@ -457,6 +461,21 @@ class Helpers {
|
|
|
457
461
|
headers,
|
|
458
462
|
timeout: exports.API_TIMEOUT
|
|
459
463
|
});
|
|
464
|
+
// Some VeSync API calls return HTTP 200 with an error `code` in the body.
|
|
465
|
+
// Treat token-expiration responses as recoverable: re-auth once and retry.
|
|
466
|
+
const didRetryAuth = options.didRetryAuth === true;
|
|
467
|
+
const responseCode = (_a = response.data) === null || _a === void 0 ? void 0 : _a.code;
|
|
468
|
+
const responseMsg = (_b = response.data) === null || _b === void 0 ? void 0 : _b.msg;
|
|
469
|
+
if (!didRetryAuth &&
|
|
470
|
+
response.status === 200 &&
|
|
471
|
+
((typeof responseCode === 'number' && (0, constants_1.isTokenError)(responseCode)) ||
|
|
472
|
+
isTokenExpiredMessage(responseMsg))) {
|
|
473
|
+
logger_1.logger.debug('Token expired, attempting to re-login...');
|
|
474
|
+
if (await manager.login()) {
|
|
475
|
+
logger_1.logger.debug('Re-login successful, retrying original request...');
|
|
476
|
+
return await this.callApi(endpoint, method, data, headers, manager, { ...options, didRetryAuth: true });
|
|
477
|
+
}
|
|
478
|
+
}
|
|
460
479
|
return [response.data, response.status];
|
|
461
480
|
}
|
|
462
481
|
catch (error) {
|
|
@@ -465,16 +484,18 @@ class Helpers {
|
|
|
465
484
|
// Check for token expiration or auth invalidation
|
|
466
485
|
const httpStatus = error.response.status;
|
|
467
486
|
const msg = responseData === null || responseData === void 0 ? void 0 : responseData.msg;
|
|
468
|
-
|
|
487
|
+
const didRetryAuth = options.didRetryAuth === true;
|
|
488
|
+
if (!didRetryAuth && (httpStatus === 401 ||
|
|
469
489
|
httpStatus === 419 ||
|
|
470
490
|
(responseData === null || responseData === void 0 ? void 0 : responseData.code) === 4001004 ||
|
|
471
|
-
(typeof
|
|
491
|
+
(typeof (responseData === null || responseData === void 0 ? void 0 : responseData.code) === 'number' && (0, constants_1.isTokenError)(responseData.code)) ||
|
|
492
|
+
isTokenExpiredMessage(msg))) {
|
|
472
493
|
logger_1.logger.debug('Token expired, attempting to re-login...');
|
|
473
494
|
// Re-login
|
|
474
495
|
if (await manager.login()) {
|
|
475
496
|
// Retry the original request
|
|
476
497
|
logger_1.logger.debug('Re-login successful, retrying original request...');
|
|
477
|
-
return await this.callApi(endpoint, method, data, headers, manager);
|
|
498
|
+
return await this.callApi(endpoint, method, data, headers, manager, { ...options, didRetryAuth: true });
|
|
478
499
|
}
|
|
479
500
|
}
|
|
480
501
|
// Log specific error details for debugging
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tsvesync",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.6",
|
|
4
4
|
"description": "A TypeScript library for interacting with VeSync smart home devices",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
"homepage": "https://github.com/mickgiles/tsvesync#readme",
|
|
35
35
|
"engines": {
|
|
36
|
-
"node": "^18.20.4 || ^20.15.1 || ^22"
|
|
36
|
+
"node": "^18.20.4 || ^20.15.1 || ^22 || ^24"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"axios": "^1.7.9",
|
package/src/lib/constants.ts
CHANGED
|
@@ -19,6 +19,7 @@ export const CROSS_REGION_ERROR_CODES = [
|
|
|
19
19
|
// pyvesync parity: `-11001000` maps to TOKEN_EXPIRED (invalid token)
|
|
20
20
|
export const TOKEN_ERROR_CODES = [
|
|
21
21
|
-11001000, // token expired/invalid
|
|
22
|
+
-11001022, // token has expired (observed from device list responses)
|
|
22
23
|
];
|
|
23
24
|
|
|
24
25
|
// Other error codes
|
package/src/lib/helpers.ts
CHANGED
|
@@ -6,7 +6,7 @@ import axios from 'axios';
|
|
|
6
6
|
import crypto from 'crypto';
|
|
7
7
|
import { VeSync } from './vesync';
|
|
8
8
|
import { logger } from './logger';
|
|
9
|
-
import { isCredentialError, isCrossRegionError } from './constants';
|
|
9
|
+
import { isCredentialError, isCrossRegionError, isTokenError } from './constants';
|
|
10
10
|
|
|
11
11
|
// API configuration - Support regional endpoints
|
|
12
12
|
let _apiBaseUrl = 'https://smartapi.vesync.com';
|
|
@@ -91,6 +91,14 @@ export const AUTH_ERROR_CODES = {
|
|
|
91
91
|
CROSS_REGION_ALT: -11261022
|
|
92
92
|
};
|
|
93
93
|
|
|
94
|
+
function isTokenExpiredMessage(msg: unknown): boolean {
|
|
95
|
+
return typeof msg === 'string' && /token\s*(?:has\s*)?expired/i.test(msg);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
type CallApiOptions = {
|
|
99
|
+
didRetryAuth?: boolean;
|
|
100
|
+
};
|
|
101
|
+
|
|
94
102
|
/**
|
|
95
103
|
* Get list of country codes that use the US endpoint
|
|
96
104
|
* These are tried when we get a cross-region error
|
|
@@ -522,7 +530,8 @@ export class Helpers {
|
|
|
522
530
|
method: string,
|
|
523
531
|
data: any = null,
|
|
524
532
|
headers: Record<string, string> = {},
|
|
525
|
-
manager: VeSync
|
|
533
|
+
manager: VeSync,
|
|
534
|
+
options: CallApiOptions = {}
|
|
526
535
|
): Promise<[any, number]> {
|
|
527
536
|
let url = '';
|
|
528
537
|
try {
|
|
@@ -548,6 +557,27 @@ export class Helpers {
|
|
|
548
557
|
timeout: API_TIMEOUT
|
|
549
558
|
});
|
|
550
559
|
|
|
560
|
+
// Some VeSync API calls return HTTP 200 with an error `code` in the body.
|
|
561
|
+
// Treat token-expiration responses as recoverable: re-auth once and retry.
|
|
562
|
+
const didRetryAuth = options.didRetryAuth === true;
|
|
563
|
+
const responseCode = response.data?.code;
|
|
564
|
+
const responseMsg = response.data?.msg;
|
|
565
|
+
if (
|
|
566
|
+
!didRetryAuth &&
|
|
567
|
+
response.status === 200 &&
|
|
568
|
+
(
|
|
569
|
+
(typeof responseCode === 'number' && isTokenError(responseCode)) ||
|
|
570
|
+
isTokenExpiredMessage(responseMsg)
|
|
571
|
+
)
|
|
572
|
+
) {
|
|
573
|
+
logger.debug('Token expired, attempting to re-login...');
|
|
574
|
+
|
|
575
|
+
if (await manager.login()) {
|
|
576
|
+
logger.debug('Re-login successful, retrying original request...');
|
|
577
|
+
return await this.callApi(endpoint, method, data, headers, manager, { ...options, didRetryAuth: true });
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
551
581
|
return [response.data, response.status];
|
|
552
582
|
} catch (error: any) {
|
|
553
583
|
if (error.response) {
|
|
@@ -556,19 +586,21 @@ export class Helpers {
|
|
|
556
586
|
// Check for token expiration or auth invalidation
|
|
557
587
|
const httpStatus = error.response.status;
|
|
558
588
|
const msg: string | undefined = responseData?.msg;
|
|
559
|
-
|
|
589
|
+
const didRetryAuth = options.didRetryAuth === true;
|
|
590
|
+
if (!didRetryAuth && (
|
|
560
591
|
httpStatus === 401 ||
|
|
561
592
|
httpStatus === 419 ||
|
|
562
593
|
responseData?.code === 4001004 ||
|
|
563
|
-
(typeof
|
|
564
|
-
|
|
594
|
+
(typeof responseData?.code === 'number' && isTokenError(responseData.code)) ||
|
|
595
|
+
isTokenExpiredMessage(msg)
|
|
596
|
+
)) {
|
|
565
597
|
logger.debug('Token expired, attempting to re-login...');
|
|
566
598
|
|
|
567
599
|
// Re-login
|
|
568
600
|
if (await manager.login()) {
|
|
569
601
|
// Retry the original request
|
|
570
602
|
logger.debug('Re-login successful, retrying original request...');
|
|
571
|
-
return await this.callApi(endpoint, method, data, headers, manager);
|
|
603
|
+
return await this.callApi(endpoint, method, data, headers, manager, { ...options, didRetryAuth: true });
|
|
572
604
|
}
|
|
573
605
|
}
|
|
574
606
|
|