@waywake/youzanyun-sdk 2.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 Youzan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,115 @@
1
+ YouzanYun SDK
2
+ =======
3
+
4
+ [![NPM Version](https://img.shields.io/npm/v/@waywake/youzanyun-sdk.svg?style=flat)](https://www.npmjs.com/package/@waywake/youzanyun-sdk)
5
+ [![License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md)
6
+ [![Downloads](https://img.shields.io/npm/dt/@waywake/youzanyun-sdk.svg)]()
7
+ [![Build Status](https://travis-ci.org/youzan/open-sdk-node.png)](https://travis-ci.org/youzan/open-sdk-node)
8
+ [![Coverage Status](https://coveralls.io/repos/github/youzan/open-sdk-node/badge.svg?branch=master)](https://coveralls.io/github/youzan/open-sdk-node?branch=master)
9
+
10
+ YouzanYun SDK for Node.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ npm i @waywake/youzanyun-sdk --save
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ 可参考 [examples](examples)
21
+
22
+ ### 1. 获取及刷新access_token
23
+
24
+ #### 工具型应用 获取access_token
25
+
26
+ ```node
27
+ const youzanyun = require('@waywake/youzanyun-sdk');
28
+
29
+ // 获取token
30
+ const resp = youzanyun.token.get({
31
+ authorize_type: 'authorization_code',
32
+ client_id: 'YOUR_CLIENT_ID',
33
+ client_secret: 'YOUR_CLIENT_SECRET',
34
+ code: 'YOUR_CODE',
35
+ redirect_uri: 'YOUR_REDIRECT_URI',
36
+ });
37
+ ```
38
+
39
+ #### 自用型应用 获取access_token
40
+
41
+ ```node
42
+ const youzanyun = require('@waywake/youzanyun-sdk');
43
+
44
+ const resp = youzanyun.token.get({
45
+ authorize_type: 'silent',
46
+ client_id: 'YOUR_CLIENT_ID',
47
+ client_secret: 'YOUR_CLIENT_SECRET',
48
+ grant_id: 110,
49
+ refresh: true, // 是否获取refresh_token(可通过refresh_token刷新token)
50
+ });
51
+ ```
52
+
53
+ #### 工具型应用及自用型应用 刷新access_token
54
+
55
+ ```node
56
+ const youzanyun = require('@waywake/youzanyun-sdk');
57
+
58
+ // 刷新token
59
+ const resp = youzanyun.token.get({
60
+ authorize_type: 'refresh_token',
61
+ client_id: 'YOUR_CLIENT_ID',
62
+ client_secret: 'YOUR_CLIENT_SECRET',
63
+ refresh_token: 'YOUR_REFRESH_TOKEN',
64
+ });
65
+ ```
66
+
67
+ ### 2. 接口调用
68
+
69
+ #### Token方式
70
+
71
+ ```node
72
+ const youzanyun = require('@waywake/youzanyun-sdk');
73
+
74
+ const token = 'f59b1a6bb04f4eqweqd1c6af315d';
75
+ const params = {tid: 'E20190509110527067500013'};
76
+
77
+ const resp = youzanyun.client.call({
78
+ api: 'youzan.trade.get',
79
+ version: '4.0.0',
80
+ token,
81
+ params,
82
+ });
83
+ ```
84
+
85
+ #### 文件上传
86
+
87
+ ```node
88
+ const youzanyun = require('@waywake/youzanyun-sdk');
89
+
90
+ const token = 'f59b1a6bb0asdasq613d1c6af315d';
91
+ const files = {'image': path.resolve(__dirname, './pic.png')};
92
+
93
+ const resp = youzanyun.client.call({
94
+ api: 'youzan.materials.storage.platform.img.upload',
95
+ version: '3.0.0',
96
+ token,
97
+ params: {},
98
+ files,
99
+ });
100
+ ```
101
+
102
+ ### 3. 消息解密
103
+
104
+ ```node
105
+ const youzanyun = require('@waywake/youzanyun-sdk');
106
+
107
+ const messages = 'YOUR_RECEIVED_MESSAGES';
108
+ const clientSecret = 'YOUR_CLIENT_SECRET';
109
+
110
+ const resp = youzanyun.crypto.decrypt(messages, clientSecret);
111
+ ```
112
+
113
+ ## License
114
+
115
+ [MIT](LICENSE)
@@ -0,0 +1,11 @@
1
+ /**
2
+ * API 调用客户端
3
+ */
4
+ import type { ApiCallParams } from './types';
5
+ /**
6
+ * 发起接口调用
7
+ *
8
+ * @param apiParam 接口调用参数 { api, version, token?, params?, files?, config?, host? }
9
+ */
10
+ export declare function call(apiParam: ApiCallParams): Promise<import("axios").AxiosResponse<any, any, {}>>;
11
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAa7C;;;;GAIG;AACH,wBAAgB,IAAI,CAAC,QAAQ,EAAE,aAAa,wDAgC3C"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * HTTP URL 配置
3
+ */
4
+ export declare function getBaseUrl(): string;
5
+ export declare function getUrlToken(): string;
6
+ export declare function getUrlAPI(api: string, version: string, token: string): string;
7
+ export declare function getUrlAPIExempt(api: string, version: string): string;
8
+ export declare function getUrlTextArea(api: string, version: string): string;
9
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../src/config/http.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE7E;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnE"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * 密文解密
3
+ */
4
+ /**
5
+ * 密文解密,适用于订购等消息
6
+ *
7
+ * @param messages 应用收到的密文(收到的原始密文消息,无需解码)
8
+ * @param clientSecret 应用的 clientSecret(可在有赞云控制台查看)
9
+ */
10
+ export declare function decrypt(messages: string, clientSecret: string): string;
11
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/crypto.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAatE"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * YouzanYun SDK for Node
3
+ *
4
+ * @example
5
+ * import { client, token, crypto } from '@waywake/youzanyun-sdk';
6
+ */
7
+ export { call } from './client';
8
+ export { get } from './token';
9
+ export { decrypt } from './crypto';
10
+ export type { AuthorizeType, ApiCallParams, TokenParams, SilentTokenParams, AuthorizationCodeTokenParams, RefreshTokenParams, ClientConfig } from './types';
11
+ import * as client from './client';
12
+ import * as token from './token';
13
+ import * as crypto from './crypto';
14
+ export { client, token, crypto };
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,GAAG,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,iBAAiB,EAAE,4BAA4B,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5J,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AAEnC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC"}
@@ -0,0 +1,231 @@
1
+ var __create = Object.create;
2
+ var __getProtoOf = Object.getPrototypeOf;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
12
+ var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
20
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
21
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
22
+ for (let key of __getOwnPropNames(mod))
23
+ if (!__hasOwnProp.call(to, key))
24
+ __defProp(to, key, {
25
+ get: __accessProp.bind(mod, key),
26
+ enumerable: true
27
+ });
28
+ if (canCache)
29
+ cache.set(mod, to);
30
+ return to;
31
+ };
32
+ var __toCommonJS = (from) => {
33
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
34
+ if (entry)
35
+ return entry;
36
+ entry = __defProp({}, "__esModule", { value: true });
37
+ if (from && typeof from === "object" || typeof from === "function") {
38
+ for (var key of __getOwnPropNames(from))
39
+ if (!__hasOwnProp.call(entry, key))
40
+ __defProp(entry, key, {
41
+ get: __accessProp.bind(from, key),
42
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
43
+ });
44
+ }
45
+ __moduleCache.set(from, entry);
46
+ return entry;
47
+ };
48
+ var __moduleCache;
49
+ var __returnValue = (v) => v;
50
+ function __exportSetter(name, newValue) {
51
+ this[name] = __returnValue.bind(null, newValue);
52
+ }
53
+ var __export = (target, all) => {
54
+ for (var name in all)
55
+ __defProp(target, name, {
56
+ get: all[name],
57
+ enumerable: true,
58
+ configurable: true,
59
+ set: __exportSetter.bind(all, name)
60
+ });
61
+ };
62
+
63
+ // src/index.ts
64
+ var exports_src = {};
65
+ __export(exports_src, {
66
+ token: () => exports_token,
67
+ get: () => get,
68
+ decrypt: () => decrypt,
69
+ crypto: () => exports_crypto,
70
+ client: () => exports_client,
71
+ call: () => call
72
+ });
73
+ module.exports = __toCommonJS(exports_src);
74
+
75
+ // src/client.ts
76
+ var exports_client = {};
77
+ __export(exports_client, {
78
+ call: () => call
79
+ });
80
+
81
+ // src/config/http.ts
82
+ var BASE_URL = "https://open.youzanyun.com";
83
+ function getBaseUrl() {
84
+ return BASE_URL;
85
+ }
86
+ function getUrlToken() {
87
+ return "/auth/token";
88
+ }
89
+ function getUrlAPI(api, version, token) {
90
+ return `/api/${api}/${version}?access_token=${token}`;
91
+ }
92
+ function getUrlAPIExempt(api, version) {
93
+ return `/api/auth_exempt/${api}/${version}`;
94
+ }
95
+ function getUrlTextArea(api, version) {
96
+ return `/api/_textarea_/${api}/${version}`;
97
+ }
98
+
99
+ // src/utils/http.ts
100
+ var import_fs = __toESM(require("fs"));
101
+ var import_axios = __toESM(require("axios"));
102
+ var import_form_data = __toESM(require("form-data"));
103
+ var httpClient = import_axios.default.create({
104
+ baseURL: getBaseUrl(),
105
+ headers: {
106
+ "User-Agent": "YZY-Open-Client 1.0.0 - Node"
107
+ }
108
+ });
109
+ async function post(url, params) {
110
+ const resp = await httpClient.post(url, params, {
111
+ headers: { "Content-type": "application/json;charset=UTF-8" }
112
+ });
113
+ return resp;
114
+ }
115
+ async function upload(url, files) {
116
+ const form = new import_form_data.default;
117
+ if (files instanceof Map) {
118
+ files.forEach((filePath, key) => {
119
+ form.append(key, import_fs.default.createReadStream(filePath));
120
+ });
121
+ } else {
122
+ for (const [key, filePath] of Object.entries(files)) {
123
+ form.append(key, import_fs.default.createReadStream(filePath));
124
+ }
125
+ }
126
+ const resp = await httpClient.post(url, form, {
127
+ headers: form.getHeaders()
128
+ });
129
+ return resp;
130
+ }
131
+
132
+ // src/client.ts
133
+ function hasFiles(files) {
134
+ if (!files)
135
+ return false;
136
+ if (files instanceof Map)
137
+ return files.size > 0;
138
+ return Object.keys(files).length > 0;
139
+ }
140
+ function call(apiParam) {
141
+ if (!apiParam || typeof apiParam !== "object") {
142
+ throw new Error("参数异常: api 必传");
143
+ }
144
+ if (!apiParam.api) {
145
+ throw new Error("参数异常: api 必传");
146
+ }
147
+ if (!apiParam.version) {
148
+ throw new Error("参数异常: version 必传");
149
+ }
150
+ let urlPath;
151
+ if (apiParam.token != null) {
152
+ urlPath = getUrlAPI(apiParam.api, apiParam.version, apiParam.token);
153
+ if (apiParam.config?.isRichText) {
154
+ urlPath = getUrlTextArea(apiParam.api, apiParam.version);
155
+ }
156
+ } else {
157
+ urlPath = getUrlAPIExempt(apiParam.api, apiParam.version);
158
+ }
159
+ if (apiParam.host != null) {
160
+ urlPath = apiParam.host + urlPath;
161
+ }
162
+ if (hasFiles(apiParam.files)) {
163
+ return upload(urlPath, apiParam.files);
164
+ }
165
+ return post(urlPath, apiParam.params);
166
+ }
167
+ // src/token.ts
168
+ var exports_token = {};
169
+ __export(exports_token, {
170
+ get: () => get
171
+ });
172
+ var VALID_AUTHORIZE_TYPES = new Set([
173
+ "silent",
174
+ "authorization_code",
175
+ "refresh_token"
176
+ ]);
177
+ function get(params) {
178
+ if (!params || typeof params !== "object") {
179
+ throw new Error("参数异常: 参数缺少必要字段");
180
+ }
181
+ if (!params.client_id || !params.client_secret) {
182
+ throw new Error("参数异常: client_id client_secret 必传");
183
+ }
184
+ if (!params.authorize_type || !VALID_AUTHORIZE_TYPES.has(params.authorize_type)) {
185
+ throw new Error("参数异常: authorize_type 类型错误");
186
+ }
187
+ switch (params.authorize_type) {
188
+ case "silent":
189
+ if (!params.grant_id) {
190
+ throw new Error("参数异常: grant_id 必传");
191
+ }
192
+ break;
193
+ case "authorization_code":
194
+ if (!params.code || !params.redirect_uri) {
195
+ throw new Error("参数异常: code redirect_uri 必传");
196
+ }
197
+ break;
198
+ case "refresh_token":
199
+ if (!params.refresh_token) {
200
+ throw new Error("参数异常: refresh_token 必传");
201
+ }
202
+ break;
203
+ default: {
204
+ const _exhaustive = params;
205
+ throw new Error(`参数异常: 未知的 authorize_type`);
206
+ }
207
+ }
208
+ let urlPath = getUrlToken();
209
+ if (params.host != null) {
210
+ urlPath = params.host + urlPath;
211
+ }
212
+ return post(urlPath, params);
213
+ }
214
+ // src/crypto.ts
215
+ var exports_crypto = {};
216
+ __export(exports_crypto, {
217
+ decrypt: () => decrypt
218
+ });
219
+ var import_crypto = require("crypto");
220
+ function decrypt(messages, clientSecret) {
221
+ if (!messages || !clientSecret) {
222
+ throw new Error("参数异常: messages clientSecret 不可为空");
223
+ }
224
+ const algorithm = decodeURIComponent(messages);
225
+ const key = clientSecret.substring(0, 16);
226
+ const iv = "0102030405060708";
227
+ const decipher = import_crypto.createDecipheriv("aes-128-cbc", key, iv);
228
+ let dec = decipher.update(algorithm, "base64", "utf-8");
229
+ dec += decipher.final();
230
+ return dec;
231
+ }
@@ -0,0 +1 @@
1
+ {"type":"commonjs"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Token 管理
3
+ */
4
+ import type { TokenParams } from './types';
5
+ /**
6
+ * 获取 Token
7
+ *
8
+ * 必要字段: authorize_type client_id client_secret
9
+ *
10
+ * 自用型应用获取 Token: authorize_type = silent, 必传 grant_id
11
+ * 工具型应用获取 Token: authorize_type = authorization_code, 必传 code redirect_uri
12
+ * 工具型应用/自用型应用刷新 Token: authorize_type = refresh_token, 必传 refresh_token
13
+ */
14
+ export declare function get(params: TokenParams): Promise<import("axios").AxiosResponse<any, any, {}>>;
15
+ //# sourceMappingURL=token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/token.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAU3C;;;;;;;;GAQG;AACH,wBAAgB,GAAG,CAAC,MAAM,EAAE,WAAW,wDA4CtC"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * OAuth 授权类型
3
+ */
4
+ export type AuthorizeType = 'silent' | 'authorization_code' | 'refresh_token';
5
+ /** Token 请求公共字段 */
6
+ interface TokenBaseParams {
7
+ client_id: string;
8
+ client_secret: string;
9
+ host?: string;
10
+ }
11
+ /** 自用型应用获取 Token */
12
+ export interface SilentTokenParams extends TokenBaseParams {
13
+ authorize_type: 'silent';
14
+ grant_id: number;
15
+ refresh?: boolean;
16
+ }
17
+ /** 工具型应用获取 Token(授权码模式) */
18
+ export interface AuthorizationCodeTokenParams extends TokenBaseParams {
19
+ authorize_type: 'authorization_code';
20
+ code: string;
21
+ redirect_uri: string;
22
+ }
23
+ /** 刷新 Token */
24
+ export interface RefreshTokenParams extends TokenBaseParams {
25
+ authorize_type: 'refresh_token';
26
+ refresh_token: string;
27
+ }
28
+ /** Token 请求参数联合类型 */
29
+ export type TokenParams = SilentTokenParams | AuthorizationCodeTokenParams | RefreshTokenParams;
30
+ /** API 调用配置 */
31
+ export interface ClientConfig {
32
+ /** 是否使用富文本接口 */
33
+ isRichText?: boolean;
34
+ }
35
+ /** API 调用参数 */
36
+ export interface ApiCallParams {
37
+ /** 接口名称,如 'youzan.trade.get'(必传) */
38
+ api: string;
39
+ /** 接口版本,如 '4.0.0'(必传) */
40
+ version: string;
41
+ /** OAuth access token(免登接口可不传) */
42
+ token?: string;
43
+ /** 请求参数 */
44
+ params?: Record<string, unknown>;
45
+ /** 上传文件:字段名到文件路径的映射 */
46
+ files?: Map<string, string> | Record<string, string>;
47
+ /** 额外配置 */
48
+ config?: ClientConfig;
49
+ /** 自定义主机地址 */
50
+ host?: string;
51
+ }
52
+ export {};
53
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,oBAAoB,GAAG,eAAe,CAAC;AAE9E,mBAAmB;AACnB,UAAU,eAAe;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,oBAAoB;AACpB,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,cAAc,EAAE,QAAQ,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,2BAA2B;AAC3B,MAAM,WAAW,4BAA6B,SAAQ,eAAe;IACnE,cAAc,EAAE,oBAAoB,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,eAAe;AACf,MAAM,WAAW,kBAAmB,SAAQ,eAAe;IACzD,cAAc,EAAE,eAAe,CAAC;IAChC,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,qBAAqB;AACrB,MAAM,MAAM,WAAW,GACnB,iBAAiB,GACjB,4BAA4B,GAC5B,kBAAkB,CAAC;AAIvB,eAAe;AACf,MAAM,WAAW,YAAY;IAC3B,gBAAgB;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAe;AACf,MAAM,WAAW,aAAa;IAC5B,oCAAoC;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,yBAAyB;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW;IACX,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,uBAAuB;IACvB,KAAK,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrD,WAAW;IACX,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,cAAc;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * HTTP 请求工具
3
+ */
4
+ /**
5
+ * 发起 POST 请求
6
+ *
7
+ * @param url 支持绝对路径、相对路径
8
+ * @param params POST 参数
9
+ */
10
+ export declare function post(url: string, params?: Record<string, unknown>): Promise<import("axios").AxiosResponse<any, any, {}>>;
11
+ /**
12
+ * 发起上传文件请求
13
+ *
14
+ * @param url 支持绝对路径、相对路径
15
+ * @param files 上传文件参数,支持 Map 或 Object。示例: {"image": "/path/to/filename.jpg"}
16
+ */
17
+ export declare function upload(url: string, files: Map<string, string> | Record<string, string>): Promise<import("axios").AxiosResponse<any, any, {}>>;
18
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../src/utils/http.ts"],"names":[],"mappings":"AAAA;;GAEG;AAeH;;;;;GAKG;AACH,wBAAsB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,wDAKvE;AAED;;;;;GAKG;AACH,wBAAsB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,wDAiB5F"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * API 调用客户端
3
+ */
4
+ import type { ApiCallParams } from './types';
5
+ /**
6
+ * 发起接口调用
7
+ *
8
+ * @param apiParam 接口调用参数 { api, version, token?, params?, files?, config?, host? }
9
+ */
10
+ export declare function call(apiParam: ApiCallParams): Promise<import("axios").AxiosResponse<any, any, {}>>;
11
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAa7C;;;;GAIG;AACH,wBAAgB,IAAI,CAAC,QAAQ,EAAE,aAAa,wDAgC3C"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * HTTP URL 配置
3
+ */
4
+ export declare function getBaseUrl(): string;
5
+ export declare function getUrlToken(): string;
6
+ export declare function getUrlAPI(api: string, version: string, token: string): string;
7
+ export declare function getUrlAPIExempt(api: string, version: string): string;
8
+ export declare function getUrlTextArea(api: string, version: string): string;
9
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../src/config/http.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE7E;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnE"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * 密文解密
3
+ */
4
+ /**
5
+ * 密文解密,适用于订购等消息
6
+ *
7
+ * @param messages 应用收到的密文(收到的原始密文消息,无需解码)
8
+ * @param clientSecret 应用的 clientSecret(可在有赞云控制台查看)
9
+ */
10
+ export declare function decrypt(messages: string, clientSecret: string): string;
11
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/crypto.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAatE"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * YouzanYun SDK for Node
3
+ *
4
+ * @example
5
+ * import { client, token, crypto } from '@waywake/youzanyun-sdk';
6
+ */
7
+ export { call } from './client';
8
+ export { get } from './token';
9
+ export { decrypt } from './crypto';
10
+ export type { AuthorizeType, ApiCallParams, TokenParams, SilentTokenParams, AuthorizationCodeTokenParams, RefreshTokenParams, ClientConfig } from './types';
11
+ import * as client from './client';
12
+ import * as token from './token';
13
+ import * as crypto from './crypto';
14
+ export { client, token, crypto };
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,GAAG,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,iBAAiB,EAAE,4BAA4B,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5J,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AAEnC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC"}
@@ -0,0 +1,180 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __returnValue = (v) => v;
3
+ function __exportSetter(name, newValue) {
4
+ this[name] = __returnValue.bind(null, newValue);
5
+ }
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, {
9
+ get: all[name],
10
+ enumerable: true,
11
+ configurable: true,
12
+ set: __exportSetter.bind(all, name)
13
+ });
14
+ };
15
+
16
+ // src/client.ts
17
+ var exports_client = {};
18
+ __export(exports_client, {
19
+ call: () => call
20
+ });
21
+
22
+ // src/config/http.ts
23
+ var BASE_URL = "https://open.youzanyun.com";
24
+ function getBaseUrl() {
25
+ return BASE_URL;
26
+ }
27
+ function getUrlToken() {
28
+ return "/auth/token";
29
+ }
30
+ function getUrlAPI(api, version, token) {
31
+ return `/api/${api}/${version}?access_token=${token}`;
32
+ }
33
+ function getUrlAPIExempt(api, version) {
34
+ return `/api/auth_exempt/${api}/${version}`;
35
+ }
36
+ function getUrlTextArea(api, version) {
37
+ return `/api/_textarea_/${api}/${version}`;
38
+ }
39
+
40
+ // src/utils/http.ts
41
+ import fs from "fs";
42
+ import axios from "axios";
43
+ import FormData from "form-data";
44
+ var httpClient = axios.create({
45
+ baseURL: getBaseUrl(),
46
+ headers: {
47
+ "User-Agent": "YZY-Open-Client 1.0.0 - Node"
48
+ }
49
+ });
50
+ async function post(url, params) {
51
+ const resp = await httpClient.post(url, params, {
52
+ headers: { "Content-type": "application/json;charset=UTF-8" }
53
+ });
54
+ return resp;
55
+ }
56
+ async function upload(url, files) {
57
+ const form = new FormData;
58
+ if (files instanceof Map) {
59
+ files.forEach((filePath, key) => {
60
+ form.append(key, fs.createReadStream(filePath));
61
+ });
62
+ } else {
63
+ for (const [key, filePath] of Object.entries(files)) {
64
+ form.append(key, fs.createReadStream(filePath));
65
+ }
66
+ }
67
+ const resp = await httpClient.post(url, form, {
68
+ headers: form.getHeaders()
69
+ });
70
+ return resp;
71
+ }
72
+
73
+ // src/client.ts
74
+ function hasFiles(files) {
75
+ if (!files)
76
+ return false;
77
+ if (files instanceof Map)
78
+ return files.size > 0;
79
+ return Object.keys(files).length > 0;
80
+ }
81
+ function call(apiParam) {
82
+ if (!apiParam || typeof apiParam !== "object") {
83
+ throw new Error("参数异常: api 必传");
84
+ }
85
+ if (!apiParam.api) {
86
+ throw new Error("参数异常: api 必传");
87
+ }
88
+ if (!apiParam.version) {
89
+ throw new Error("参数异常: version 必传");
90
+ }
91
+ let urlPath;
92
+ if (apiParam.token != null) {
93
+ urlPath = getUrlAPI(apiParam.api, apiParam.version, apiParam.token);
94
+ if (apiParam.config?.isRichText) {
95
+ urlPath = getUrlTextArea(apiParam.api, apiParam.version);
96
+ }
97
+ } else {
98
+ urlPath = getUrlAPIExempt(apiParam.api, apiParam.version);
99
+ }
100
+ if (apiParam.host != null) {
101
+ urlPath = apiParam.host + urlPath;
102
+ }
103
+ if (hasFiles(apiParam.files)) {
104
+ return upload(urlPath, apiParam.files);
105
+ }
106
+ return post(urlPath, apiParam.params);
107
+ }
108
+ // src/token.ts
109
+ var exports_token = {};
110
+ __export(exports_token, {
111
+ get: () => get
112
+ });
113
+ var VALID_AUTHORIZE_TYPES = new Set([
114
+ "silent",
115
+ "authorization_code",
116
+ "refresh_token"
117
+ ]);
118
+ function get(params) {
119
+ if (!params || typeof params !== "object") {
120
+ throw new Error("参数异常: 参数缺少必要字段");
121
+ }
122
+ if (!params.client_id || !params.client_secret) {
123
+ throw new Error("参数异常: client_id client_secret 必传");
124
+ }
125
+ if (!params.authorize_type || !VALID_AUTHORIZE_TYPES.has(params.authorize_type)) {
126
+ throw new Error("参数异常: authorize_type 类型错误");
127
+ }
128
+ switch (params.authorize_type) {
129
+ case "silent":
130
+ if (!params.grant_id) {
131
+ throw new Error("参数异常: grant_id 必传");
132
+ }
133
+ break;
134
+ case "authorization_code":
135
+ if (!params.code || !params.redirect_uri) {
136
+ throw new Error("参数异常: code redirect_uri 必传");
137
+ }
138
+ break;
139
+ case "refresh_token":
140
+ if (!params.refresh_token) {
141
+ throw new Error("参数异常: refresh_token 必传");
142
+ }
143
+ break;
144
+ default: {
145
+ const _exhaustive = params;
146
+ throw new Error(`参数异常: 未知的 authorize_type`);
147
+ }
148
+ }
149
+ let urlPath = getUrlToken();
150
+ if (params.host != null) {
151
+ urlPath = params.host + urlPath;
152
+ }
153
+ return post(urlPath, params);
154
+ }
155
+ // src/crypto.ts
156
+ var exports_crypto = {};
157
+ __export(exports_crypto, {
158
+ decrypt: () => decrypt
159
+ });
160
+ import { createDecipheriv } from "crypto";
161
+ function decrypt(messages, clientSecret) {
162
+ if (!messages || !clientSecret) {
163
+ throw new Error("参数异常: messages clientSecret 不可为空");
164
+ }
165
+ const algorithm = decodeURIComponent(messages);
166
+ const key = clientSecret.substring(0, 16);
167
+ const iv = "0102030405060708";
168
+ const decipher = createDecipheriv("aes-128-cbc", key, iv);
169
+ let dec = decipher.update(algorithm, "base64", "utf-8");
170
+ dec += decipher.final();
171
+ return dec;
172
+ }
173
+ export {
174
+ exports_token as token,
175
+ get,
176
+ decrypt,
177
+ exports_crypto as crypto,
178
+ exports_client as client,
179
+ call
180
+ };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Token 管理
3
+ */
4
+ import type { TokenParams } from './types';
5
+ /**
6
+ * 获取 Token
7
+ *
8
+ * 必要字段: authorize_type client_id client_secret
9
+ *
10
+ * 自用型应用获取 Token: authorize_type = silent, 必传 grant_id
11
+ * 工具型应用获取 Token: authorize_type = authorization_code, 必传 code redirect_uri
12
+ * 工具型应用/自用型应用刷新 Token: authorize_type = refresh_token, 必传 refresh_token
13
+ */
14
+ export declare function get(params: TokenParams): Promise<import("axios").AxiosResponse<any, any, {}>>;
15
+ //# sourceMappingURL=token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/token.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAU3C;;;;;;;;GAQG;AACH,wBAAgB,GAAG,CAAC,MAAM,EAAE,WAAW,wDA4CtC"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * OAuth 授权类型
3
+ */
4
+ export type AuthorizeType = 'silent' | 'authorization_code' | 'refresh_token';
5
+ /** Token 请求公共字段 */
6
+ interface TokenBaseParams {
7
+ client_id: string;
8
+ client_secret: string;
9
+ host?: string;
10
+ }
11
+ /** 自用型应用获取 Token */
12
+ export interface SilentTokenParams extends TokenBaseParams {
13
+ authorize_type: 'silent';
14
+ grant_id: number;
15
+ refresh?: boolean;
16
+ }
17
+ /** 工具型应用获取 Token(授权码模式) */
18
+ export interface AuthorizationCodeTokenParams extends TokenBaseParams {
19
+ authorize_type: 'authorization_code';
20
+ code: string;
21
+ redirect_uri: string;
22
+ }
23
+ /** 刷新 Token */
24
+ export interface RefreshTokenParams extends TokenBaseParams {
25
+ authorize_type: 'refresh_token';
26
+ refresh_token: string;
27
+ }
28
+ /** Token 请求参数联合类型 */
29
+ export type TokenParams = SilentTokenParams | AuthorizationCodeTokenParams | RefreshTokenParams;
30
+ /** API 调用配置 */
31
+ export interface ClientConfig {
32
+ /** 是否使用富文本接口 */
33
+ isRichText?: boolean;
34
+ }
35
+ /** API 调用参数 */
36
+ export interface ApiCallParams {
37
+ /** 接口名称,如 'youzan.trade.get'(必传) */
38
+ api: string;
39
+ /** 接口版本,如 '4.0.0'(必传) */
40
+ version: string;
41
+ /** OAuth access token(免登接口可不传) */
42
+ token?: string;
43
+ /** 请求参数 */
44
+ params?: Record<string, unknown>;
45
+ /** 上传文件:字段名到文件路径的映射 */
46
+ files?: Map<string, string> | Record<string, string>;
47
+ /** 额外配置 */
48
+ config?: ClientConfig;
49
+ /** 自定义主机地址 */
50
+ host?: string;
51
+ }
52
+ export {};
53
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,oBAAoB,GAAG,eAAe,CAAC;AAE9E,mBAAmB;AACnB,UAAU,eAAe;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,oBAAoB;AACpB,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,cAAc,EAAE,QAAQ,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,2BAA2B;AAC3B,MAAM,WAAW,4BAA6B,SAAQ,eAAe;IACnE,cAAc,EAAE,oBAAoB,CAAC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,eAAe;AACf,MAAM,WAAW,kBAAmB,SAAQ,eAAe;IACzD,cAAc,EAAE,eAAe,CAAC;IAChC,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,qBAAqB;AACrB,MAAM,MAAM,WAAW,GACnB,iBAAiB,GACjB,4BAA4B,GAC5B,kBAAkB,CAAC;AAIvB,eAAe;AACf,MAAM,WAAW,YAAY;IAC3B,gBAAgB;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAe;AACf,MAAM,WAAW,aAAa;IAC5B,oCAAoC;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,yBAAyB;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW;IACX,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,uBAAuB;IACvB,KAAK,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrD,WAAW;IACX,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,cAAc;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * HTTP 请求工具
3
+ */
4
+ /**
5
+ * 发起 POST 请求
6
+ *
7
+ * @param url 支持绝对路径、相对路径
8
+ * @param params POST 参数
9
+ */
10
+ export declare function post(url: string, params?: Record<string, unknown>): Promise<import("axios").AxiosResponse<any, any, {}>>;
11
+ /**
12
+ * 发起上传文件请求
13
+ *
14
+ * @param url 支持绝对路径、相对路径
15
+ * @param files 上传文件参数,支持 Map 或 Object。示例: {"image": "/path/to/filename.jpg"}
16
+ */
17
+ export declare function upload(url: string, files: Map<string, string> | Record<string, string>): Promise<import("axios").AxiosResponse<any, any, {}>>;
18
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../src/utils/http.ts"],"names":[],"mappings":"AAAA;;GAEG;AAeH;;;;;GAKG;AACH,wBAAsB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,wDAKvE;AAED;;;;;GAKG;AACH,wBAAsB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,wDAiB5F"}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@waywake/youzanyun-sdk",
3
+ "version": "2.0.0",
4
+ "description": "YouzanYun SDK for Node",
5
+ "type": "module",
6
+ "main": "./dist/cjs/index.js",
7
+ "module": "./dist/esm/index.js",
8
+ "types": "./dist/esm/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/esm/index.d.ts",
13
+ "default": "./dist/esm/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/cjs/index.d.ts",
17
+ "default": "./dist/cjs/index.js"
18
+ }
19
+ }
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "scripts": {
25
+ "build": "bun run build:cjs && bun run build:esm",
26
+ "build:cjs": "bun build src/index.ts --outdir dist/cjs --target node --format cjs --external axios --external form-data && tsc -p tsconfig.cjs.json --emitDeclarationOnly && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json",
27
+ "build:esm": "bun build src/index.ts --outdir dist/esm --target node --format esm --external axios --external form-data && tsc -p tsconfig.esm.json --emitDeclarationOnly",
28
+ "clean": "rm -rf dist",
29
+ "test": "bun test",
30
+ "prepublishOnly": "bun run clean && bun run build"
31
+ },
32
+ "keywords": [
33
+ "youzan",
34
+ "youzanyun",
35
+ "sdk",
36
+ "open"
37
+ ],
38
+ "author": "youzan",
39
+ "license": "MIT",
40
+ "homepage": "https://github.com/waywake/youzan-sdk-ts#readme",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "https://github.com/waywake/youzan-sdk-ts.git"
44
+ },
45
+ "bugs": {
46
+ "url": "https://github.com/waywake/youzan-sdk-ts/issues"
47
+ },
48
+ "engines": {
49
+ "node": ">=16.0.0"
50
+ },
51
+ "dependencies": {
52
+ "axios": "^1.7.0",
53
+ "form-data": "^4.0.0"
54
+ },
55
+ "devDependencies": {
56
+ "@types/bun": "^1.1.0",
57
+ "@types/node": "^20.0.0",
58
+ "typescript": "^5.5.0"
59
+ }
60
+ }