@jayfong/x-server 2.16.3 → 2.16.5
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/lib/_cjs/index.js +6 -0
- package/lib/_cjs/services/captcha.js +27 -15
- package/lib/_cjs/services/rate_limit.js +23 -0
- package/lib/_cjs/services/sms.js +38 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/services/captcha.d.ts +14 -6
- package/lib/services/captcha.js +27 -15
- package/lib/services/rate_limit.d.ts +19 -2
- package/lib/services/rate_limit.js +23 -0
- package/lib/services/sms.d.ts +33 -0
- package/lib/services/sms.js +32 -0
- package/package.json +2 -1
package/lib/_cjs/index.js
CHANGED
|
@@ -193,6 +193,12 @@ Object.keys(_sensitive_words).forEach(function (key) {
|
|
|
193
193
|
if (key in exports && exports[key] === _sensitive_words[key]) return;
|
|
194
194
|
exports[key] = _sensitive_words[key];
|
|
195
195
|
});
|
|
196
|
+
var _sms = require("./services/sms");
|
|
197
|
+
Object.keys(_sms).forEach(function (key) {
|
|
198
|
+
if (key === "default" || key === "__esModule") return;
|
|
199
|
+
if (key in exports && exports[key] === _sms[key]) return;
|
|
200
|
+
exports[key] = _sms[key];
|
|
201
|
+
});
|
|
196
202
|
var _index = require("./types/index");
|
|
197
203
|
Object.keys(_index).forEach(function (key) {
|
|
198
204
|
if (key === "default" || key === "__esModule") return;
|
|
@@ -3,36 +3,51 @@
|
|
|
3
3
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
|
4
4
|
exports.__esModule = true;
|
|
5
5
|
exports.CaptchaService = void 0;
|
|
6
|
-
var _svgCaptcha = _interopRequireDefault(require("svg-captcha"));
|
|
7
6
|
var _miniSvgDataUri = _interopRequireDefault(require("mini-svg-data-uri"));
|
|
7
|
+
var _svgCaptcha = _interopRequireDefault(require("svg-captcha"));
|
|
8
|
+
var _vtils = require("vtils");
|
|
8
9
|
var _x = require("../x");
|
|
9
10
|
class CaptchaService {
|
|
10
|
-
constructor(
|
|
11
|
-
this.options = options;
|
|
11
|
+
constructor() {
|
|
12
12
|
this.serviceName = 'captcha';
|
|
13
|
+
this.charsetMap = {
|
|
14
|
+
'09': '0123456789'.split(''),
|
|
15
|
+
'19': '123456789'.split(''),
|
|
16
|
+
'az': 'abcdefghijklmnopqrstuvwxyz'.split(''),
|
|
17
|
+
'AZ': 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''),
|
|
18
|
+
'aZ': 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''),
|
|
19
|
+
'0z': '0123456789abcdefghijklmnopqrstuvwxyz'.split(''),
|
|
20
|
+
'0Z': '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''),
|
|
21
|
+
'1z': '123456789abcdefghijklmnopqrstuvwxyz'.split(''),
|
|
22
|
+
'1Z': '123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('')
|
|
23
|
+
};
|
|
13
24
|
}
|
|
14
|
-
async generate(
|
|
15
|
-
const
|
|
25
|
+
async generate(payload) {
|
|
26
|
+
const chars = this.charsetMap[payload.charset];
|
|
27
|
+
let code = '';
|
|
28
|
+
for (let i = 0; i < payload.size; i++) {
|
|
29
|
+
code += (0, _vtils.sample)(chars);
|
|
30
|
+
}
|
|
16
31
|
const token = await _x.x.cache.save({
|
|
17
|
-
code,
|
|
18
|
-
scene
|
|
19
|
-
},
|
|
32
|
+
code: code,
|
|
33
|
+
scene: payload.scene
|
|
34
|
+
}, payload.ttl);
|
|
20
35
|
return {
|
|
21
36
|
token: token,
|
|
22
37
|
code: code
|
|
23
38
|
};
|
|
24
39
|
}
|
|
25
|
-
async generateDataUrl(
|
|
40
|
+
async generateDataUrl(payload) {
|
|
26
41
|
const {
|
|
27
42
|
text: code,
|
|
28
43
|
data: svg
|
|
29
44
|
} = _svgCaptcha.default.create({
|
|
30
|
-
size:
|
|
45
|
+
size: payload.size
|
|
31
46
|
});
|
|
32
47
|
const token = await _x.x.cache.save({
|
|
33
48
|
code,
|
|
34
|
-
scene
|
|
35
|
-
},
|
|
49
|
+
scene: payload.scene
|
|
50
|
+
}, payload.ttl);
|
|
36
51
|
const dataUrl = (0, _miniSvgDataUri.default)(svg);
|
|
37
52
|
return {
|
|
38
53
|
token: token,
|
|
@@ -41,9 +56,6 @@ class CaptchaService {
|
|
|
41
56
|
};
|
|
42
57
|
}
|
|
43
58
|
async verify(options) {
|
|
44
|
-
if (options.code.length !== this.options.size) {
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
59
|
const expected = await _x.x.cache.get(options.token);
|
|
48
60
|
if (expected == null) {
|
|
49
61
|
return false;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
exports.__esModule = true;
|
|
4
4
|
exports.RateLimitService = void 0;
|
|
5
|
+
var _vtils = require("vtils");
|
|
5
6
|
var _http_error = require("../core/http_error");
|
|
6
7
|
var _x = require("../x");
|
|
7
8
|
class RateLimitService {
|
|
@@ -28,5 +29,27 @@ class RateLimitService {
|
|
|
28
29
|
}
|
|
29
30
|
return count;
|
|
30
31
|
}
|
|
32
|
+
async limitByManyCount(options) {
|
|
33
|
+
for (let i = 0; i < options.limits.length; i++) {
|
|
34
|
+
const item = options.limits[i];
|
|
35
|
+
const ttl = (0, _vtils.ms)(item.ttl);
|
|
36
|
+
const remainingCount = await this.limitByCount({
|
|
37
|
+
key: `${options.key}_${ttl}`,
|
|
38
|
+
ttl: ttl,
|
|
39
|
+
count: item.count
|
|
40
|
+
});
|
|
41
|
+
if (remainingCount === 0) {
|
|
42
|
+
return i;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
async limitByManyCountOrFail(options) {
|
|
48
|
+
const pass = await this.limitByManyCount(options);
|
|
49
|
+
if (pass !== true) {
|
|
50
|
+
var _options$limits$pass$;
|
|
51
|
+
throw new _http_error.HttpError.Forbidden((_options$limits$pass$ = options.limits[pass].message) != null ? _options$limits$pass$ : options.message);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
31
54
|
}
|
|
32
55
|
exports.RateLimitService = RateLimitService;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
|
4
|
+
exports.__esModule = true;
|
|
5
|
+
exports.SmsService = void 0;
|
|
6
|
+
var _tencentcloudSdkNodejsSms = _interopRequireDefault(require("tencentcloud-sdk-nodejs-sms"));
|
|
7
|
+
var _http_error = require("../core/http_error");
|
|
8
|
+
class SmsService {
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this.options = options;
|
|
11
|
+
this.serviceName = 'sms';
|
|
12
|
+
}
|
|
13
|
+
async send(payload) {
|
|
14
|
+
if (payload.provider === 'tencentCloud') {
|
|
15
|
+
const SmsClient = _tencentcloudSdkNodejsSms.default.sms.v20210111.Client;
|
|
16
|
+
const smsClient = new SmsClient({
|
|
17
|
+
credential: {
|
|
18
|
+
secretId: this.options.providerOptions.secretId,
|
|
19
|
+
secretKey: this.options.providerOptions.secretKey
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
const res = await smsClient.SendSms({
|
|
23
|
+
SmsSdkAppId: this.options.providerOptions.appId,
|
|
24
|
+
SignName: payload.signature,
|
|
25
|
+
TemplateId: payload.templateId,
|
|
26
|
+
TemplateParamSet: payload.templateParamValues,
|
|
27
|
+
PhoneNumberSet: [payload.phoneNumber[0] === '+' ? payload.phoneNumber : `+86${payload.phoneNumber}`]
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
success: res.SendStatusSet[0].Code === 'Ok',
|
|
31
|
+
message: res.SendStatusSet[0].Message,
|
|
32
|
+
smsId: res.SendStatusSet[0].SerialNo
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
throw new _http_error.HttpError.BadRequest(`no sms provider: ${payload.provider}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.SmsService = SmsService;
|
package/lib/index.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ export * from './services/pay';
|
|
|
30
30
|
export * from './services/rate_limit';
|
|
31
31
|
export * from './services/redis';
|
|
32
32
|
export * from './services/sensitive_words';
|
|
33
|
+
export * from './services/sms';
|
|
33
34
|
export * from './types/index';
|
|
34
35
|
export * from './x';
|
|
35
36
|
export * from '.x/models';
|
package/lib/index.js
CHANGED
|
@@ -31,6 +31,7 @@ export * from "./services/pay";
|
|
|
31
31
|
export * from "./services/rate_limit";
|
|
32
32
|
export * from "./services/redis";
|
|
33
33
|
export * from "./services/sensitive_words";
|
|
34
|
+
export * from "./services/sms";
|
|
34
35
|
export * from "./types/index";
|
|
35
36
|
export * from "./x";
|
|
36
37
|
// @endindex
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import { type MsValue } from 'vtils';
|
|
1
2
|
import { BaseService } from './base';
|
|
2
|
-
|
|
3
|
-
export interface
|
|
3
|
+
export type CaptchaServiceGenerateCharset = '09' | '19' | 'az' | 'AZ' | 'aZ' | '0z' | '0Z' | '1z' | '1Z';
|
|
4
|
+
export interface CaptchaServiceGeneratePayload {
|
|
5
|
+
/**
|
|
6
|
+
* 场景值
|
|
7
|
+
*/
|
|
8
|
+
scene: string;
|
|
4
9
|
/**
|
|
5
10
|
* 验证码长度
|
|
6
11
|
*/
|
|
@@ -9,6 +14,10 @@ export interface CaptchaServiceOptions {
|
|
|
9
14
|
* 验证码存活时长
|
|
10
15
|
*/
|
|
11
16
|
ttl: MsValue;
|
|
17
|
+
/**
|
|
18
|
+
* 字符集
|
|
19
|
+
*/
|
|
20
|
+
charset: CaptchaServiceGenerateCharset;
|
|
12
21
|
}
|
|
13
22
|
export interface CaptchaServiceGenerateResult {
|
|
14
23
|
token: string;
|
|
@@ -30,11 +39,10 @@ export interface CaptchaServiceVerifyOptions {
|
|
|
30
39
|
scene: string;
|
|
31
40
|
}
|
|
32
41
|
export declare class CaptchaService implements BaseService {
|
|
33
|
-
private options;
|
|
34
42
|
serviceName: string;
|
|
35
|
-
|
|
36
|
-
generate(
|
|
37
|
-
generateDataUrl(
|
|
43
|
+
private charsetMap;
|
|
44
|
+
generate(payload: CaptchaServiceGeneratePayload): Promise<CaptchaServiceGenerateResult>;
|
|
45
|
+
generateDataUrl(payload: CaptchaServiceGeneratePayload): Promise<CaptchaServiceGenerateDataUrlResult>;
|
|
38
46
|
verify(options: CaptchaServiceVerifyOptions): Promise<boolean>;
|
|
39
47
|
}
|
|
40
48
|
declare module '../x' {
|
package/lib/services/captcha.js
CHANGED
|
@@ -1,33 +1,48 @@
|
|
|
1
|
-
import svgCaptcha from 'svg-captcha';
|
|
2
1
|
import svgToDataUrl from 'mini-svg-data-uri';
|
|
2
|
+
import svgCaptcha from 'svg-captcha';
|
|
3
|
+
import { sample } from 'vtils';
|
|
3
4
|
import { x } from "../x";
|
|
4
5
|
export class CaptchaService {
|
|
5
|
-
constructor(
|
|
6
|
-
this.options = options;
|
|
6
|
+
constructor() {
|
|
7
7
|
this.serviceName = 'captcha';
|
|
8
|
+
this.charsetMap = {
|
|
9
|
+
'09': '0123456789'.split(''),
|
|
10
|
+
'19': '123456789'.split(''),
|
|
11
|
+
'az': 'abcdefghijklmnopqrstuvwxyz'.split(''),
|
|
12
|
+
'AZ': 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''),
|
|
13
|
+
'aZ': 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''),
|
|
14
|
+
'0z': '0123456789abcdefghijklmnopqrstuvwxyz'.split(''),
|
|
15
|
+
'0Z': '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''),
|
|
16
|
+
'1z': '123456789abcdefghijklmnopqrstuvwxyz'.split(''),
|
|
17
|
+
'1Z': '123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('')
|
|
18
|
+
};
|
|
8
19
|
}
|
|
9
|
-
async generate(
|
|
10
|
-
const
|
|
20
|
+
async generate(payload) {
|
|
21
|
+
const chars = this.charsetMap[payload.charset];
|
|
22
|
+
let code = '';
|
|
23
|
+
for (let i = 0; i < payload.size; i++) {
|
|
24
|
+
code += sample(chars);
|
|
25
|
+
}
|
|
11
26
|
const token = await x.cache.save({
|
|
12
|
-
code,
|
|
13
|
-
scene
|
|
14
|
-
},
|
|
27
|
+
code: code,
|
|
28
|
+
scene: payload.scene
|
|
29
|
+
}, payload.ttl);
|
|
15
30
|
return {
|
|
16
31
|
token: token,
|
|
17
32
|
code: code
|
|
18
33
|
};
|
|
19
34
|
}
|
|
20
|
-
async generateDataUrl(
|
|
35
|
+
async generateDataUrl(payload) {
|
|
21
36
|
const {
|
|
22
37
|
text: code,
|
|
23
38
|
data: svg
|
|
24
39
|
} = svgCaptcha.create({
|
|
25
|
-
size:
|
|
40
|
+
size: payload.size
|
|
26
41
|
});
|
|
27
42
|
const token = await x.cache.save({
|
|
28
43
|
code,
|
|
29
|
-
scene
|
|
30
|
-
},
|
|
44
|
+
scene: payload.scene
|
|
45
|
+
}, payload.ttl);
|
|
31
46
|
const dataUrl = svgToDataUrl(svg);
|
|
32
47
|
return {
|
|
33
48
|
token: token,
|
|
@@ -36,9 +51,6 @@ export class CaptchaService {
|
|
|
36
51
|
};
|
|
37
52
|
}
|
|
38
53
|
async verify(options) {
|
|
39
|
-
if (options.code.length !== this.options.size) {
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
54
|
const expected = await x.cache.get(options.token);
|
|
43
55
|
if (expected == null) {
|
|
44
56
|
return false;
|
|
@@ -1,17 +1,34 @@
|
|
|
1
|
-
import { BaseService } from './base';
|
|
2
1
|
import { MsValue } from 'vtils';
|
|
2
|
+
import { BaseService } from './base';
|
|
3
3
|
export interface RateLimitServiceLimitByCountOptions {
|
|
4
4
|
key: string;
|
|
5
|
-
count: number;
|
|
6
5
|
ttl: MsValue;
|
|
6
|
+
count: number;
|
|
7
7
|
}
|
|
8
8
|
export interface RateLimitServiceLimitByCountOrFailOptions extends RateLimitServiceLimitByCountOptions {
|
|
9
9
|
message?: string;
|
|
10
10
|
}
|
|
11
|
+
export interface RateLimitServiceLimitByManyCountOptions {
|
|
12
|
+
key: string;
|
|
13
|
+
limits: Array<{
|
|
14
|
+
ttl: MsValue;
|
|
15
|
+
count: number;
|
|
16
|
+
}>;
|
|
17
|
+
}
|
|
18
|
+
export interface RateLimitServiceLimitByManyCountOrFailOptions extends RateLimitServiceLimitByManyCountOptions {
|
|
19
|
+
message?: string;
|
|
20
|
+
limits: Array<{
|
|
21
|
+
ttl: MsValue;
|
|
22
|
+
count: number;
|
|
23
|
+
message?: string;
|
|
24
|
+
}>;
|
|
25
|
+
}
|
|
11
26
|
export declare class RateLimitService implements BaseService {
|
|
12
27
|
serviceName: string;
|
|
13
28
|
limitByCount(options: RateLimitServiceLimitByCountOptions): Promise<number>;
|
|
14
29
|
limitByCountOrFail(options: RateLimitServiceLimitByCountOrFailOptions): Promise<number>;
|
|
30
|
+
limitByManyCount(options: RateLimitServiceLimitByManyCountOptions): Promise<true | number>;
|
|
31
|
+
limitByManyCountOrFail(options: RateLimitServiceLimitByManyCountOrFailOptions): Promise<void>;
|
|
15
32
|
}
|
|
16
33
|
declare module '../x' {
|
|
17
34
|
interface X {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ms } from 'vtils';
|
|
1
2
|
import { HttpError } from "../core/http_error";
|
|
2
3
|
import { x } from "../x";
|
|
3
4
|
export class RateLimitService {
|
|
@@ -24,4 +25,26 @@ export class RateLimitService {
|
|
|
24
25
|
}
|
|
25
26
|
return count;
|
|
26
27
|
}
|
|
28
|
+
async limitByManyCount(options) {
|
|
29
|
+
for (let i = 0; i < options.limits.length; i++) {
|
|
30
|
+
const item = options.limits[i];
|
|
31
|
+
const ttl = ms(item.ttl);
|
|
32
|
+
const remainingCount = await this.limitByCount({
|
|
33
|
+
key: `${options.key}_${ttl}`,
|
|
34
|
+
ttl: ttl,
|
|
35
|
+
count: item.count
|
|
36
|
+
});
|
|
37
|
+
if (remainingCount === 0) {
|
|
38
|
+
return i;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
async limitByManyCountOrFail(options) {
|
|
44
|
+
const pass = await this.limitByManyCount(options);
|
|
45
|
+
if (pass !== true) {
|
|
46
|
+
var _options$limits$pass$;
|
|
47
|
+
throw new HttpError.Forbidden((_options$limits$pass$ = options.limits[pass].message) != null ? _options$limits$pass$ : options.message);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
27
50
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { BaseService } from './base';
|
|
2
|
+
export type SmsServiceProvider = 'tencentCloud';
|
|
3
|
+
export type SmsServiceOptions = {
|
|
4
|
+
provider: 'tencentCloud';
|
|
5
|
+
providerOptions: {
|
|
6
|
+
appId: string;
|
|
7
|
+
secretId: string;
|
|
8
|
+
secretKey: string;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
export type SmsServiceSendPayload = {
|
|
12
|
+
provider: SmsServiceProvider;
|
|
13
|
+
signature: string;
|
|
14
|
+
templateId: string;
|
|
15
|
+
templateParamValues: string[];
|
|
16
|
+
phoneNumber: string;
|
|
17
|
+
};
|
|
18
|
+
export type SmsServiceSendResult = {
|
|
19
|
+
success: boolean;
|
|
20
|
+
message: string;
|
|
21
|
+
smsId: string;
|
|
22
|
+
};
|
|
23
|
+
export declare class SmsService implements BaseService {
|
|
24
|
+
private options;
|
|
25
|
+
serviceName: string;
|
|
26
|
+
constructor(options: SmsServiceOptions);
|
|
27
|
+
send(payload: SmsServiceSendPayload): Promise<SmsServiceSendResult>;
|
|
28
|
+
}
|
|
29
|
+
declare module '../x' {
|
|
30
|
+
interface X {
|
|
31
|
+
sms: SmsService;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import TencentCloud from 'tencentcloud-sdk-nodejs-sms';
|
|
2
|
+
import { HttpError } from "../core/http_error";
|
|
3
|
+
export class SmsService {
|
|
4
|
+
constructor(options) {
|
|
5
|
+
this.options = options;
|
|
6
|
+
this.serviceName = 'sms';
|
|
7
|
+
}
|
|
8
|
+
async send(payload) {
|
|
9
|
+
if (payload.provider === 'tencentCloud') {
|
|
10
|
+
const SmsClient = TencentCloud.sms.v20210111.Client;
|
|
11
|
+
const smsClient = new SmsClient({
|
|
12
|
+
credential: {
|
|
13
|
+
secretId: this.options.providerOptions.secretId,
|
|
14
|
+
secretKey: this.options.providerOptions.secretKey
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
const res = await smsClient.SendSms({
|
|
18
|
+
SmsSdkAppId: this.options.providerOptions.appId,
|
|
19
|
+
SignName: payload.signature,
|
|
20
|
+
TemplateId: payload.templateId,
|
|
21
|
+
TemplateParamSet: payload.templateParamValues,
|
|
22
|
+
PhoneNumberSet: [payload.phoneNumber[0] === '+' ? payload.phoneNumber : `+86${payload.phoneNumber}`]
|
|
23
|
+
});
|
|
24
|
+
return {
|
|
25
|
+
success: res.SendStatusSet[0].Code === 'Ok',
|
|
26
|
+
message: res.SendStatusSet[0].Message,
|
|
27
|
+
smsId: res.SendStatusSet[0].SerialNo
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
throw new HttpError.BadRequest(`no sms provider: ${payload.provider}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jayfong/x-server",
|
|
3
|
-
"version": "2.16.
|
|
3
|
+
"version": "2.16.5",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "lib/_cjs/index.js",
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
"select-run": "^1.1.2",
|
|
63
63
|
"supports-color": "^8",
|
|
64
64
|
"svg-captcha": "^1.4.0",
|
|
65
|
+
"tencentcloud-sdk-nodejs-sms": "^4.0.649",
|
|
65
66
|
"ts-morph": "^12.2.0",
|
|
66
67
|
"tsx": "^3.12.7",
|
|
67
68
|
"utf-8-validate": "^5.0.9",
|