@be-link/shield-for-tcb-node-sdk 0.0.1
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 +88 -0
- package/index.d.ts +2 -0
- package/index.js +6 -0
- package/modules/BaseService.d.ts +14 -0
- package/modules/BaseService.js +31 -0
- package/modules/config/service.d.ts +19 -0
- package/modules/config/service.js +35 -0
- package/modules/config/types.d.ts +65 -0
- package/modules/config/types.js +2 -0
- package/package.json +29 -0
- package/utils/env.d.ts +12 -0
- package/utils/env.js +18 -0
- package/utils/http.d.ts +5 -0
- package/utils/http.js +99 -0
- package/utils/string.d.ts +9 -0
- package/utils/string.js +14 -0
package/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# ShieldForTCB SDK
|
|
2
|
+
|
|
3
|
+
ShieldForTCB Node.js SDK - 敏感信息管理服务客户端
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @be-link/shield-for-tcb-node-sdk
|
|
9
|
+
# 或
|
|
10
|
+
pnpm add @be-link/shield-for-tcb-node-sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 使用
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { backendConfigService, frontendConfigService } from '@be-link/shield-for-tcb-node-sdk'
|
|
17
|
+
|
|
18
|
+
// 服务端使用(默认,包含异常处理)
|
|
19
|
+
const config = await backendConfigService.fetchConfig({ key: 'your-key', type: 'json' })
|
|
20
|
+
|
|
21
|
+
// 前端使用(跳过异常处理,直接抛出原始错误)
|
|
22
|
+
const frontendConfig = await frontendConfigService.fetchConfig({ key: 'your-key', type: 'json' })
|
|
23
|
+
|
|
24
|
+
// 获取全局动态配置
|
|
25
|
+
const globalConfig = await backendConfigService.fetchGlobalDynamicConfig()
|
|
26
|
+
|
|
27
|
+
// 获取 COS 临时密钥
|
|
28
|
+
const cosTempSecret = await backendConfigService.getCosTempSecret()
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## 发布流程
|
|
32
|
+
|
|
33
|
+
### 自动发布(推荐)
|
|
34
|
+
|
|
35
|
+
使用发布脚本,会自动完成版本更新、构建和发布:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# 在 SDK 目录下执行
|
|
39
|
+
cd packages/sdk
|
|
40
|
+
pnpm publish
|
|
41
|
+
|
|
42
|
+
# 或在根目录执行
|
|
43
|
+
pnpm publish:sdk
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
发布脚本会自动:
|
|
47
|
+
1. 设置 npm 配置和认证(需要 `NPM_DEPLOY_TOKEN` 环境变量)
|
|
48
|
+
2. 更新版本号(使用 `npm version patch`,只读模式)
|
|
49
|
+
3. 构建项目
|
|
50
|
+
4. 发布到 npm(使用 `pnpm publish --no-git-checks`,只读文件发布)
|
|
51
|
+
5. 发送飞书通知(需要 `FEISHU_WEBHOOK_URL` 环境变量)
|
|
52
|
+
|
|
53
|
+
### 环境变量
|
|
54
|
+
|
|
55
|
+
发布脚本需要以下环境变量(可选):
|
|
56
|
+
|
|
57
|
+
- `NPM_DEPLOY_TOKEN`: npm 发布 token
|
|
58
|
+
- `FEISHU_WEBHOOK_URL`: 飞书 webhook URL(用于发送发布通知)
|
|
59
|
+
|
|
60
|
+
### CI/CD 集成
|
|
61
|
+
|
|
62
|
+
在 Jenkins 或其他 CI/CD 平台中,可以这样使用:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
export NPM_DEPLOY_TOKEN="your-token"
|
|
66
|
+
export FEISHU_WEBHOOK_URL="https://open.feishu.cn/open-apis/bot/v2/hook/xxx"
|
|
67
|
+
cd packages/sdk
|
|
68
|
+
pnpm publish
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 手动发布
|
|
72
|
+
|
|
73
|
+
如果需要手动发布:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# 1. 构建
|
|
77
|
+
pnpm build
|
|
78
|
+
|
|
79
|
+
# 2. 进入 dist 目录发布(只读文件发布)
|
|
80
|
+
cd dist
|
|
81
|
+
pnpm publish --no-git-checks --access public
|
|
82
|
+
cd ..
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
ISC
|
|
88
|
+
|
package/index.d.ts
ADDED
package/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.frontendConfigService = exports.backendConfigService = void 0;
|
|
4
|
+
var service_1 = require("./modules/config/service");
|
|
5
|
+
Object.defineProperty(exports, "backendConfigService", { enumerable: true, get: function () { return service_1.backendConfigService; } });
|
|
6
|
+
Object.defineProperty(exports, "frontendConfigService", { enumerable: true, get: function () { return service_1.frontendConfigService; } });
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export default abstract class BaseService {
|
|
2
|
+
private isPublicEnv;
|
|
3
|
+
/** URL一级路径 */
|
|
4
|
+
protected abstract prefixUrl: string;
|
|
5
|
+
/** 子网域名 */
|
|
6
|
+
protected readonly natDevHost = "http://yayvmsbg.shield-for-tcb.07vmo6rk.by26x7u9.com:8090";
|
|
7
|
+
protected readonly natProdHost = "";
|
|
8
|
+
/** 公网域名 */
|
|
9
|
+
protected readonly publicDevHost = "https://shield-for-tcb-196345-5-1304510571.sh.run.tcloudbase.com";
|
|
10
|
+
protected readonly publicProdHost = "";
|
|
11
|
+
constructor();
|
|
12
|
+
/** 获取API URL */
|
|
13
|
+
protected getApiUrl(func: Function): string;
|
|
14
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const env_1 = __importDefault(require("../utils/env"));
|
|
7
|
+
const string_1 = require("../utils/string");
|
|
8
|
+
class BaseService {
|
|
9
|
+
constructor() {
|
|
10
|
+
/** 子网域名 */
|
|
11
|
+
this.natDevHost = 'http://yayvmsbg.shield-for-tcb.07vmo6rk.by26x7u9.com:8090';
|
|
12
|
+
this.natProdHost = '';
|
|
13
|
+
/** 公网域名 */
|
|
14
|
+
this.publicDevHost = 'https://shield-for-tcb-196345-5-1304510571.sh.run.tcloudbase.com';
|
|
15
|
+
this.publicProdHost = '';
|
|
16
|
+
/** 如果是云函数环境, 默认走公网访问 */
|
|
17
|
+
this.isPublicEnv = (process.env.CONTAINER_ENV || 'SCF') === 'SCF';
|
|
18
|
+
}
|
|
19
|
+
/** 获取API URL */
|
|
20
|
+
getApiUrl(func) {
|
|
21
|
+
const host = this.isPublicEnv
|
|
22
|
+
? env_1.default.isProduction()
|
|
23
|
+
? this.publicProdHost
|
|
24
|
+
: this.publicDevHost
|
|
25
|
+
: env_1.default.isProduction()
|
|
26
|
+
? this.natProdHost
|
|
27
|
+
: this.natDevHost;
|
|
28
|
+
return `${host}${this.prefixUrl}/${(0, string_1.camelToKebabCase)(func.name)}`;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.default = BaseService;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Service } from './types';
|
|
2
|
+
import BaseService from '../BaseService';
|
|
3
|
+
type CallerType = 'frontend' | 'backend';
|
|
4
|
+
export interface ConfigServiceOptions {
|
|
5
|
+
caller?: CallerType;
|
|
6
|
+
}
|
|
7
|
+
export declare class ConfigService extends BaseService implements Service.ConfigController {
|
|
8
|
+
protected prefixUrl: string;
|
|
9
|
+
private caller;
|
|
10
|
+
constructor(options?: ConfigServiceOptions);
|
|
11
|
+
private get shouldSkipErrorHandling();
|
|
12
|
+
fetchConfig(req: Service.Request.QueryFetchConfig): Promise<any>;
|
|
13
|
+
fetchGlobalDynamicConfig(): Promise<Service.Response.IFetchGlobalDynamicConfigResponse>;
|
|
14
|
+
getCosTempSecret(): Promise<Service.Response.CosTempSecretResponse>;
|
|
15
|
+
fetchSystemTime(): Promise<Date>;
|
|
16
|
+
}
|
|
17
|
+
export declare const backendConfigService: ConfigService;
|
|
18
|
+
export declare const frontendConfigService: ConfigService;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.frontendConfigService = exports.backendConfigService = exports.ConfigService = void 0;
|
|
7
|
+
const http_1 = require("../../utils/http");
|
|
8
|
+
const BaseService_1 = __importDefault(require("../BaseService"));
|
|
9
|
+
class ConfigService extends BaseService_1.default {
|
|
10
|
+
constructor(options) {
|
|
11
|
+
super();
|
|
12
|
+
this.prefixUrl = '/config';
|
|
13
|
+
this.caller = options?.caller ?? 'backend';
|
|
14
|
+
}
|
|
15
|
+
get shouldSkipErrorHandling() {
|
|
16
|
+
return this.caller === 'frontend';
|
|
17
|
+
}
|
|
18
|
+
fetchConfig(req) {
|
|
19
|
+
return (0, http_1.callApi)(this.getApiUrl(this.fetchConfig), req, {
|
|
20
|
+
skipErrorHandling: this.shouldSkipErrorHandling,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
fetchGlobalDynamicConfig() {
|
|
24
|
+
return (0, http_1.callApi)(this.getApiUrl(this.fetchGlobalDynamicConfig), undefined, { skipErrorHandling: this.shouldSkipErrorHandling });
|
|
25
|
+
}
|
|
26
|
+
getCosTempSecret() {
|
|
27
|
+
return (0, http_1.callApi)(this.getApiUrl(this.getCosTempSecret), undefined, { skipErrorHandling: this.shouldSkipErrorHandling });
|
|
28
|
+
}
|
|
29
|
+
fetchSystemTime() {
|
|
30
|
+
return (0, http_1.callApi)(this.getApiUrl(this.fetchSystemTime), undefined, { skipErrorHandling: this.shouldSkipErrorHandling });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.ConfigService = ConfigService;
|
|
34
|
+
exports.backendConfigService = new ConfigService({ caller: 'backend' });
|
|
35
|
+
exports.frontendConfigService = new ConfigService({ caller: 'frontend' });
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export declare namespace Service {
|
|
2
|
+
namespace Entity { }
|
|
3
|
+
namespace Request {
|
|
4
|
+
/**
|
|
5
|
+
* 获取配置请求参数
|
|
6
|
+
*/
|
|
7
|
+
interface QueryFetchConfig {
|
|
8
|
+
key: string;
|
|
9
|
+
type?: 'json' | 'yaml';
|
|
10
|
+
env?: string;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
namespace Response {
|
|
14
|
+
/**
|
|
15
|
+
* 获取全局动态配置响应
|
|
16
|
+
*/
|
|
17
|
+
interface IFetchGlobalDynamicConfigResponse {
|
|
18
|
+
[key: string]: any;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* COS 临时密钥凭证
|
|
22
|
+
*/
|
|
23
|
+
interface Credentials {
|
|
24
|
+
sessionToken: string;
|
|
25
|
+
tmpSecretId: string;
|
|
26
|
+
tmpSecretKey: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* COS 临时密钥响应
|
|
30
|
+
*/
|
|
31
|
+
interface CosTempSecretResponse {
|
|
32
|
+
expiredTime: number;
|
|
33
|
+
expiration: string;
|
|
34
|
+
credentials: Credentials;
|
|
35
|
+
requestId: string;
|
|
36
|
+
startTime: number;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Config Controller 接口定义
|
|
41
|
+
*/
|
|
42
|
+
interface ConfigController {
|
|
43
|
+
/**
|
|
44
|
+
* 获取配置
|
|
45
|
+
* @param req 请求参数
|
|
46
|
+
* @returns 配置数据
|
|
47
|
+
*/
|
|
48
|
+
fetchConfig(req: Service.Request.QueryFetchConfig): Promise<any>;
|
|
49
|
+
/**
|
|
50
|
+
* 获取全局动态配置
|
|
51
|
+
* @returns 全局动态配置数据
|
|
52
|
+
*/
|
|
53
|
+
fetchGlobalDynamicConfig(): Promise<Service.Response.IFetchGlobalDynamicConfigResponse>;
|
|
54
|
+
/**
|
|
55
|
+
* 获取 COS 临时密钥
|
|
56
|
+
* @returns COS 临时密钥数据
|
|
57
|
+
*/
|
|
58
|
+
getCosTempSecret(): Promise<Service.Response.CosTempSecretResponse>;
|
|
59
|
+
/**
|
|
60
|
+
* 获取系统时间
|
|
61
|
+
* @returns 系统时间
|
|
62
|
+
*/
|
|
63
|
+
fetchSystemTime(): Promise<Date>;
|
|
64
|
+
}
|
|
65
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@be-link/shield-for-tcb-node-sdk",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "ShieldForTCB Node.js SDK",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"shield",
|
|
9
|
+
"tcb",
|
|
10
|
+
"sdk"
|
|
11
|
+
],
|
|
12
|
+
"author": "",
|
|
13
|
+
"license": "ISC",
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"axios": "1.13.2",
|
|
19
|
+
"axios-retry": "^4.0.0",
|
|
20
|
+
"uuid": "^9.0.1"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/uuid": "^9.0.6"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "rm -rf ./dist && tsc && cp package.json README.md ./dist/ 2>/dev/null || true",
|
|
27
|
+
"test": "jest"
|
|
28
|
+
}
|
|
29
|
+
}
|
package/utils/env.d.ts
ADDED
package/utils/env.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class EnvUtils {
|
|
4
|
+
/**
|
|
5
|
+
* 判断是否为生产环境
|
|
6
|
+
*/
|
|
7
|
+
isProduction() {
|
|
8
|
+
return process.env.NODE_ENV === 'prod';
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 获取环境变量
|
|
12
|
+
*/
|
|
13
|
+
getEnv(key, defaultValue) {
|
|
14
|
+
return process.env[key] || defaultValue;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const envUtils = new EnvUtils();
|
|
18
|
+
exports.default = envUtils;
|
package/utils/http.d.ts
ADDED
package/utils/http.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.callApi = callApi;
|
|
40
|
+
const axios_1 = __importDefault(require("axios"));
|
|
41
|
+
const uuid_1 = require("uuid");
|
|
42
|
+
const axios_retry_1 = __importDefault(require("axios-retry"));
|
|
43
|
+
(0, axios_retry_1.default)(axios_1.default, {
|
|
44
|
+
retries: 1,
|
|
45
|
+
retryCondition(error) {
|
|
46
|
+
return error.response?.status === 502;
|
|
47
|
+
},
|
|
48
|
+
retryDelay: (retryCount) => {
|
|
49
|
+
console.info(`retryCount: ${retryCount}, retryDelay: ${retryCount * 500}`);
|
|
50
|
+
return retryCount * 500;
|
|
51
|
+
},
|
|
52
|
+
onRetry(retryCount, error, requestConfig) {
|
|
53
|
+
console.info(`retryCount: ${retryCount}, onRetry: ${error.message}, requestHeader: ${JSON.stringify(requestConfig.headers)}`);
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
async function callApi(url, request, options) {
|
|
57
|
+
const requestId = (0, uuid_1.v4)();
|
|
58
|
+
try {
|
|
59
|
+
console.info(`准备发起SHIELD-FOR-TCB请求[${requestId}]: ${url}, 参数: ${JSON.stringify(request)}`);
|
|
60
|
+
const response = await axios_1.default.post(url, request, { headers: { 'x-request-id': requestId } });
|
|
61
|
+
const responseData = response.data;
|
|
62
|
+
return responseData.data;
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
if (options?.skipErrorHandling) {
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
const axiosError = error;
|
|
69
|
+
if (axiosError.response) {
|
|
70
|
+
const response = axiosError.response;
|
|
71
|
+
const data = response.data;
|
|
72
|
+
console.error(`SHIELD 异常: ${axiosError.message},requestId: ${requestId}`);
|
|
73
|
+
console.info('响应信息', data.message);
|
|
74
|
+
console.error('异常堆栈', JSON.stringify(error.stack));
|
|
75
|
+
throw new Error(data.errorType + ' - ' + data.message);
|
|
76
|
+
}
|
|
77
|
+
// 调用dns模块解析url
|
|
78
|
+
const dns = await Promise.resolve().then(() => __importStar(require('dns')));
|
|
79
|
+
const dnsPromise = new Promise((resolve, reject) => {
|
|
80
|
+
const lookupRes = dns.lookup(url, (err, address) => {
|
|
81
|
+
if (err) {
|
|
82
|
+
console.error(err.message);
|
|
83
|
+
reject(err);
|
|
84
|
+
}
|
|
85
|
+
console.info(`lookup: ${JSON.stringify(lookupRes)}`);
|
|
86
|
+
resolve(address);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
try {
|
|
90
|
+
const address = await dnsPromise;
|
|
91
|
+
console.info(`address: ${JSON.stringify(address)}`);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
console.info(`error: ${JSON.stringify(error)}`);
|
|
95
|
+
}
|
|
96
|
+
console.error(`SHIELD 未知异常: ${axiosError.message}`, error.stack);
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 将驼峰命名转换为短横线命名
|
|
3
|
+
* @param str 驼峰命名字符串
|
|
4
|
+
* @returns 短横线命名字符串
|
|
5
|
+
* @example
|
|
6
|
+
* camelToKebabCase('fetchConfig') // 'fetch-config'
|
|
7
|
+
* camelToKebabCase('getCosTempSecret') // 'get-cos-temp-secret'
|
|
8
|
+
*/
|
|
9
|
+
export declare function camelToKebabCase(str: string): string;
|
package/utils/string.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.camelToKebabCase = camelToKebabCase;
|
|
4
|
+
/**
|
|
5
|
+
* 将驼峰命名转换为短横线命名
|
|
6
|
+
* @param str 驼峰命名字符串
|
|
7
|
+
* @returns 短横线命名字符串
|
|
8
|
+
* @example
|
|
9
|
+
* camelToKebabCase('fetchConfig') // 'fetch-config'
|
|
10
|
+
* camelToKebabCase('getCosTempSecret') // 'get-cos-temp-secret'
|
|
11
|
+
*/
|
|
12
|
+
function camelToKebabCase(str) {
|
|
13
|
+
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
14
|
+
}
|