@i.un/api-client 1.2.2 → 1.2.8

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/chain.cjs ADDED
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/chain.ts
21
+ var chain_exports = {};
22
+ __export(chain_exports, {
23
+ executeRequestChain: () => executeRequestChain
24
+ });
25
+ module.exports = __toCommonJS(chain_exports);
26
+ async function executeChainRequest(spec, handlers = []) {
27
+ const method = spec.method.toUpperCase();
28
+ for (const handler of handlers) {
29
+ if (handler.shouldHandle(spec.url, method)) {
30
+ try {
31
+ const { adapter } = handler;
32
+ const headers = spec.headers;
33
+ switch (method) {
34
+ case "GET":
35
+ return await adapter.get(spec.url, spec.body, headers);
36
+ case "POST":
37
+ return await adapter.post(spec.url, spec.body, headers);
38
+ case "PUT":
39
+ if (!adapter.put)
40
+ throw new Error(`Adapter ${handler.name} missing PUT method`);
41
+ return await adapter.put(spec.url, spec.body, headers);
42
+ case "DELETE":
43
+ if (!adapter.delete)
44
+ throw new Error(`Adapter ${handler.name} missing DELETE method`);
45
+ return await adapter.delete(spec.url, spec.body, headers);
46
+ case "PATCH":
47
+ if (!adapter.patch)
48
+ throw new Error(`Adapter ${handler.name} missing PATCH method`);
49
+ return await adapter.patch(spec.url, spec.body, headers);
50
+ default:
51
+ throw new Error(`Unsupported method: ${method}`);
52
+ }
53
+ } catch (err) {
54
+ console.warn(
55
+ `Handler [${handler.name || "Anonymous"}] failed for ${spec.url}`,
56
+ err
57
+ );
58
+ return null;
59
+ }
60
+ }
61
+ }
62
+ if (spec.url.startsWith("http://") || spec.url.startsWith("https://")) {
63
+ return await executeFallbackFetch(spec);
64
+ }
65
+ console.warn(`No handler found for url: ${spec.url}`);
66
+ return null;
67
+ }
68
+ async function executeFallbackFetch(spec) {
69
+ try {
70
+ const response = await fetch(spec.url, {
71
+ method: spec.method,
72
+ headers: spec.headers,
73
+ body: spec.body ? JSON.stringify(spec.body) : void 0,
74
+ credentials: "include"
75
+ });
76
+ if (!response.ok) {
77
+ console.log(`External Request failed: ${response.status} ${spec.url}`);
78
+ return null;
79
+ }
80
+ return await response.json();
81
+ } catch (err) {
82
+ console.log("External Request error:", err);
83
+ return null;
84
+ }
85
+ }
86
+ function getByPath(obj, path) {
87
+ if (!path) return obj;
88
+ return path.split(".").reduce((acc, part) => acc && acc[part], obj);
89
+ }
90
+ function substituteVariables(target, context) {
91
+ if (typeof target === "string") {
92
+ const directMatch = target.match(/^\{\{([\w\.]+)\}\}$/);
93
+ if (directMatch) {
94
+ const path = directMatch[1];
95
+ const val = getByPath(context, path);
96
+ return val !== void 0 ? val : "";
97
+ }
98
+ return target.replace(/\{\{([\w\.]+)\}\}/g, (_, path) => {
99
+ const val = getByPath(context, path);
100
+ return val !== void 0 ? String(val) : "";
101
+ });
102
+ }
103
+ if (Array.isArray(target)) {
104
+ return target.map((item) => substituteVariables(item, context));
105
+ }
106
+ if (target && typeof target === "object") {
107
+ const result = {};
108
+ for (const key in target) {
109
+ result[key] = substituteVariables(target[key], context);
110
+ }
111
+ return result;
112
+ }
113
+ return target;
114
+ }
115
+ async function executeRequestChain(requests, handlers = [], getChainRequests, initContext) {
116
+ const context = initContext || {};
117
+ let lastResult = null;
118
+ for (const rule of requests) {
119
+ try {
120
+ const requestSpec = substituteVariables(rule.request, context);
121
+ let rawData = await executeChainRequest(requestSpec, handlers);
122
+ if (getChainRequests) {
123
+ const chainRequests = getChainRequests(rawData);
124
+ if (chainRequests && chainRequests.length > 0) {
125
+ rawData = await executeRequestChain(
126
+ chainRequests,
127
+ handlers,
128
+ getChainRequests,
129
+ JSON.parse(JSON.stringify(context))
130
+ );
131
+ }
132
+ }
133
+ lastResult = rawData;
134
+ if (rawData) {
135
+ if (rule.key) {
136
+ const data = rule.selector ? getByPath(rawData, rule.selector) : rawData;
137
+ context[rule.key] = data;
138
+ }
139
+ } else if (!rule.optional) {
140
+ throw new Error(
141
+ `Failed to fetch required data for key: ${rule.key || "unknown"}`
142
+ );
143
+ }
144
+ } catch (err) {
145
+ if (!rule.optional) {
146
+ throw err;
147
+ }
148
+ console.warn(`Optional request failed for rule:`, rule, err);
149
+ }
150
+ }
151
+ return lastResult;
152
+ }
153
+ // Annotate the CommonJS export names for ESM import in node:
154
+ 0 && (module.exports = {
155
+ executeRequestChain
156
+ });
157
+ //# sourceMappingURL=chain.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/chain.ts"],"sourcesContent":["/**\n * Request Chain Runner\n * 负责解析和执行通用的 HTTP 请求链 (Chain Execution)\n * 适用于任何需要链式 HTTP 请求编排的场景\n */\n\n// ============================================================================\n// Type Definitions\n// ============================================================================\n\n/** Supported HTTP Methods */\ntype HttpMethod = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n\n/** HTTP 请求参数 */\nexport interface HttpRequestSpec {\n method: HttpMethod;\n url: string;\n headers?: Record<string, string>;\n body?: any;\n}\n\n/**\n * 网络适配器接口\n * 定义实际发送请求的能力\n */\n/**\n * 网络适配器接口\n * 定义实际发送请求的能力\n */\nexport interface NetworkAdapter {\n get<T>(\n url: string,\n params?: any,\n headers?: Record<string, string>,\n ): Promise<T>;\n post<T>(\n url: string,\n body?: any,\n headers?: Record<string, string>,\n ): Promise<T>;\n put?<T>(\n url: string,\n body?: any,\n headers?: Record<string, string>,\n ): Promise<T>;\n delete?<T>(\n url: string,\n params?: any,\n headers?: Record<string, string>,\n ): Promise<T>;\n patch?<T>(\n url: string,\n body?: any,\n headers?: Record<string, string>,\n ): Promise<T>;\n}\n\n/**\n * 网络处理器接口 (Strategy Pattern)\n * 组合了\"匹配规则\"和\"执行能力\"\n */\nexport interface NetworkHandler {\n /** 处理器名称 (用于调试) */\n name?: string;\n\n /** 判断该 URL 是否由此适配器处理 */\n shouldHandle(url: string, method: HttpMethod): boolean;\n\n /** 具体的网络适配器 */\n adapter: NetworkAdapter;\n}\n\n/**\n * 请求链规则 (Chain Step)\n */\nexport interface ChainRequestRule {\n /**\n * 结果存储的键名 (Context Key)\n * Fetch 结果存储: context[key] = response\n */\n key?: string;\n\n /**\n * HTTP 请求详情\n */\n request: HttpRequestSpec;\n\n /**\n * 数据提取选择器\n * 用于从响应中提取特定数据, e.g., \"elements.0\"\n */\n selector?: string;\n\n /** 是否允许请求失败 (默认 false) */\n optional?: boolean;\n}\n\n// ============================================================================\n// Internal Utilities\n// ============================================================================\n\n/**\n * 执行 HTTP 请求\n * 遍历 handlers 数组,找到第一个能处理该 URL 的适配器执行\n *\n * @param spec 请求详情\n * @param handlers 网络处理器链\n */\nasync function executeChainRequest<T>(\n spec: HttpRequestSpec,\n handlers: NetworkHandler[] = [],\n): Promise<T | null> {\n // Normalize method to upper case for robustness\n const method = spec.method.toUpperCase() as HttpMethod;\n\n // 1. 遍历处理器数组 (责任链)\n for (const handler of handlers) {\n if (handler.shouldHandle(spec.url, method)) {\n try {\n const { adapter } = handler;\n const headers = spec.headers;\n\n switch (method) {\n case \"GET\":\n return await adapter.get<T>(spec.url, spec.body, headers);\n case \"POST\":\n return await adapter.post<T>(spec.url, spec.body, headers);\n case \"PUT\":\n if (!adapter.put)\n throw new Error(`Adapter ${handler.name} missing PUT method`);\n return await adapter.put<T>(spec.url, spec.body, headers);\n case \"DELETE\":\n if (!adapter.delete)\n throw new Error(`Adapter ${handler.name} missing DELETE method`);\n return await adapter.delete<T>(spec.url, spec.body, headers);\n case \"PATCH\":\n if (!adapter.patch)\n throw new Error(`Adapter ${handler.name} missing PATCH method`);\n return await adapter.patch<T>(spec.url, spec.body, headers);\n default:\n throw new Error(`Unsupported method: ${method}`);\n }\n } catch (err) {\n console.warn(\n `Handler [${handler.name || \"Anonymous\"}] failed for ${spec.url}`,\n err,\n );\n // 如果需要继续尝试下一个 handler,可以在这里 continue,但通常由第一个匹配者全权负责\n return null;\n }\n }\n }\n\n // 2. 默认行为 (Fallback): 绝对路径自动走标准 fetch\n if (spec.url.startsWith(\"http://\") || spec.url.startsWith(\"https://\")) {\n return await executeFallbackFetch<T>(spec);\n }\n\n console.warn(`No handler found for url: ${spec.url}`);\n return null;\n}\n\n/**\n * 标准 Fetch 回退实现\n */\nasync function executeFallbackFetch<T>(\n spec: HttpRequestSpec,\n): Promise<T | null> {\n try {\n const response = await fetch(spec.url, {\n method: spec.method,\n headers: spec.headers,\n body: spec.body ? JSON.stringify(spec.body) : undefined,\n credentials: \"include\",\n });\n\n if (!response.ok) {\n console.log(`External Request failed: ${response.status} ${spec.url}`);\n return null;\n }\n\n return await response.json();\n } catch (err) {\n console.log(\"External Request error:\", err);\n return null;\n }\n}\n\n/**\n * 根据路径获取对象值\n */\nfunction getByPath(obj: any, path: string): any {\n if (!path) return obj;\n return path.split(\".\").reduce((acc, part) => acc && acc[part], obj);\n}\n\n/**\n * 变量替换 (Internal)\n * 支持字符串 {{key}} 和 {{key.prop}}\n */\nfunction substituteVariables(target: any, context: any): any {\n if (typeof target === \"string\") {\n // 1. 如果整个字符串就是一个变量占位符,例如 \"{{user}}\",则返回原始对象/值\n const directMatch = target.match(/^\\{\\{([\\w\\.]+)\\}\\}$/);\n if (directMatch) {\n const path = directMatch[1];\n const val = getByPath(context, path);\n return val !== undefined ? val : \"\";\n }\n\n // 2. 如果是混合字符串,例如 \"Hello {{user.name}}\",则执行字符串替换\n return target.replace(/\\{\\{([\\w\\.]+)\\}\\}/g, (_, path) => {\n const val = getByPath(context, path);\n return val !== undefined ? String(val) : \"\";\n });\n }\n\n if (Array.isArray(target)) {\n return target.map((item) => substituteVariables(item, context));\n }\n\n if (target && typeof target === \"object\") {\n const result: any = {};\n for (const key in target) {\n result[key] = substituteVariables(target[key], context);\n }\n return result;\n }\n\n return target;\n}\n\n// ============================================================================\n// Main Runner\n// ============================================================================\n\n/**\n * 请求链执行器 (Request Chain Engine)\n * 支持链式请求、上下文累积、智能路由、变量替换\n *\n * @param requests 链式执行规则列表\n * @param handlers 网络适配器处理器列表 (按顺序匹配)\n */\nexport async function executeRequestChain<T>(\n requests: ChainRequestRule[],\n handlers: NetworkHandler[] = [],\n getChainRequests?: (\n context: Record<string, any>,\n ) => ChainRequestRule[] | null | undefined,\n initContext?: Record<string, any>,\n): Promise<T> {\n const context: Record<string, any> = initContext || {};\n let lastResult: any = null;\n\n for (const rule of requests) {\n try {\n // 1. 变量替换 (支持从 Context 注入对象到 Request Body/URL/Headers)\n const requestSpec = substituteVariables(rule.request, context);\n\n // 2. 执行请求 (传入 handlers)\n let rawData = await executeChainRequest<any>(requestSpec, handlers);\n\n if (getChainRequests) {\n const chainRequests = getChainRequests(rawData);\n if (chainRequests && chainRequests.length > 0) {\n rawData = await executeRequestChain(\n chainRequests,\n handlers,\n getChainRequests,\n JSON.parse(JSON.stringify(context)),\n );\n }\n }\n\n lastResult = rawData;\n\n if (rawData) {\n // 5. 存储结果\n if (rule.key) {\n const data = rule.selector\n ? getByPath(rawData, rule.selector)\n : rawData;\n context[rule.key] = data;\n }\n } else if (!rule.optional) {\n throw new Error(\n `Failed to fetch required data for key: ${rule.key || \"unknown\"}`,\n );\n }\n } catch (err) {\n if (!rule.optional) {\n throw err;\n }\n console.warn(`Optional request failed for rule:`, rule, err);\n }\n }\n\n return lastResult as T;\n}\n"],"mappings":"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","names":[]}
package/dist/chain.js CHANGED
@@ -1,157 +1,7 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/chain.ts
21
- var chain_exports = {};
22
- __export(chain_exports, {
23
- executeRequestChain: () => executeRequestChain
24
- });
25
- module.exports = __toCommonJS(chain_exports);
26
- async function executeChainRequest(spec, handlers = []) {
27
- const method = spec.method.toUpperCase();
28
- for (const handler of handlers) {
29
- if (handler.shouldHandle(spec.url, method)) {
30
- try {
31
- const { adapter } = handler;
32
- const headers = spec.headers;
33
- switch (method) {
34
- case "GET":
35
- return await adapter.get(spec.url, spec.body, headers);
36
- case "POST":
37
- return await adapter.post(spec.url, spec.body, headers);
38
- case "PUT":
39
- if (!adapter.put)
40
- throw new Error(`Adapter ${handler.name} missing PUT method`);
41
- return await adapter.put(spec.url, spec.body, headers);
42
- case "DELETE":
43
- if (!adapter.delete)
44
- throw new Error(`Adapter ${handler.name} missing DELETE method`);
45
- return await adapter.delete(spec.url, spec.body, headers);
46
- case "PATCH":
47
- if (!adapter.patch)
48
- throw new Error(`Adapter ${handler.name} missing PATCH method`);
49
- return await adapter.patch(spec.url, spec.body, headers);
50
- default:
51
- throw new Error(`Unsupported method: ${method}`);
52
- }
53
- } catch (err) {
54
- console.warn(
55
- `Handler [${handler.name || "Anonymous"}] failed for ${spec.url}`,
56
- err
57
- );
58
- return null;
59
- }
60
- }
61
- }
62
- if (spec.url.startsWith("http://") || spec.url.startsWith("https://")) {
63
- return await executeFallbackFetch(spec);
64
- }
65
- console.warn(`No handler found for url: ${spec.url}`);
66
- return null;
67
- }
68
- async function executeFallbackFetch(spec) {
69
- try {
70
- const response = await fetch(spec.url, {
71
- method: spec.method,
72
- headers: spec.headers,
73
- body: spec.body ? JSON.stringify(spec.body) : void 0,
74
- credentials: "include"
75
- });
76
- if (!response.ok) {
77
- console.log(`External Request failed: ${response.status} ${spec.url}`);
78
- return null;
79
- }
80
- return await response.json();
81
- } catch (err) {
82
- console.log("External Request error:", err);
83
- return null;
84
- }
85
- }
86
- function getByPath(obj, path) {
87
- if (!path) return obj;
88
- return path.split(".").reduce((acc, part) => acc && acc[part], obj);
89
- }
90
- function substituteVariables(target, context) {
91
- if (typeof target === "string") {
92
- const directMatch = target.match(/^\{\{([\w\.]+)\}\}$/);
93
- if (directMatch) {
94
- const path = directMatch[1];
95
- const val = getByPath(context, path);
96
- return val !== void 0 ? val : "";
97
- }
98
- return target.replace(/\{\{([\w\.]+)\}\}/g, (_, path) => {
99
- const val = getByPath(context, path);
100
- return val !== void 0 ? String(val) : "";
101
- });
102
- }
103
- if (Array.isArray(target)) {
104
- return target.map((item) => substituteVariables(item, context));
105
- }
106
- if (target && typeof target === "object") {
107
- const result = {};
108
- for (const key in target) {
109
- result[key] = substituteVariables(target[key], context);
110
- }
111
- return result;
112
- }
113
- return target;
114
- }
115
- async function executeRequestChain(requests, handlers = [], getChainRequests, initContext) {
116
- const context = initContext || {};
117
- let lastResult = null;
118
- for (const rule of requests) {
119
- try {
120
- const requestSpec = substituteVariables(rule.request, context);
121
- let rawData = await executeChainRequest(requestSpec, handlers);
122
- if (getChainRequests) {
123
- const chainRequests = getChainRequests(rawData);
124
- if (chainRequests && chainRequests.length > 0) {
125
- rawData = await executeRequestChain(
126
- chainRequests,
127
- handlers,
128
- getChainRequests,
129
- JSON.parse(JSON.stringify(context))
130
- );
131
- }
132
- }
133
- lastResult = rawData;
134
- if (rawData) {
135
- if (rule.key) {
136
- const data = rule.selector ? getByPath(rawData, rule.selector) : rawData;
137
- context[rule.key] = data;
138
- }
139
- } else if (!rule.optional) {
140
- throw new Error(
141
- `Failed to fetch required data for key: ${rule.key || "unknown"}`
142
- );
143
- }
144
- } catch (err) {
145
- if (!rule.optional) {
146
- throw err;
147
- }
148
- console.warn(`Optional request failed for rule:`, rule, err);
149
- }
150
- }
151
- return lastResult;
152
- }
153
- // Annotate the CommonJS export names for ESM import in node:
154
- 0 && (module.exports = {
1
+ import {
155
2
  executeRequestChain
156
- });
3
+ } from "./chunk-LMHEH2D5.js";
4
+ export {
5
+ executeRequestChain
6
+ };
157
7
  //# sourceMappingURL=chain.js.map
package/dist/chain.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/chain.ts"],"sourcesContent":["/**\n * Request Chain Runner\n * 负责解析和执行通用的 HTTP 请求链 (Chain Execution)\n * 适用于任何需要链式 HTTP 请求编排的场景\n */\n\n// ============================================================================\n// Type Definitions\n// ============================================================================\n\n/** Supported HTTP Methods */\ntype HttpMethod = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n\n/** HTTP 请求参数 */\nexport interface HttpRequestSpec {\n method: HttpMethod;\n url: string;\n headers?: Record<string, string>;\n body?: any;\n}\n\n/**\n * 网络适配器接口\n * 定义实际发送请求的能力\n */\n/**\n * 网络适配器接口\n * 定义实际发送请求的能力\n */\nexport interface NetworkAdapter {\n get<T>(\n url: string,\n params?: any,\n headers?: Record<string, string>,\n ): Promise<T>;\n post<T>(\n url: string,\n body?: any,\n headers?: Record<string, string>,\n ): Promise<T>;\n put?<T>(\n url: string,\n body?: any,\n headers?: Record<string, string>,\n ): Promise<T>;\n delete?<T>(\n url: string,\n params?: any,\n headers?: Record<string, string>,\n ): Promise<T>;\n patch?<T>(\n url: string,\n body?: any,\n headers?: Record<string, string>,\n ): Promise<T>;\n}\n\n/**\n * 网络处理器接口 (Strategy Pattern)\n * 组合了\"匹配规则\"和\"执行能力\"\n */\nexport interface NetworkHandler {\n /** 处理器名称 (用于调试) */\n name?: string;\n\n /** 判断该 URL 是否由此适配器处理 */\n shouldHandle(url: string, method: HttpMethod): boolean;\n\n /** 具体的网络适配器 */\n adapter: NetworkAdapter;\n}\n\n/**\n * 请求链规则 (Chain Step)\n */\nexport interface ChainRequestRule {\n /**\n * 结果存储的键名 (Context Key)\n * Fetch 结果存储: context[key] = response\n */\n key?: string;\n\n /**\n * HTTP 请求详情\n */\n request: HttpRequestSpec;\n\n /**\n * 数据提取选择器\n * 用于从响应中提取特定数据, e.g., \"elements.0\"\n */\n selector?: string;\n\n /** 是否允许请求失败 (默认 false) */\n optional?: boolean;\n}\n\n// ============================================================================\n// Internal Utilities\n// ============================================================================\n\n/**\n * 执行 HTTP 请求\n * 遍历 handlers 数组,找到第一个能处理该 URL 的适配器执行\n *\n * @param spec 请求详情\n * @param handlers 网络处理器链\n */\nasync function executeChainRequest<T>(\n spec: HttpRequestSpec,\n handlers: NetworkHandler[] = [],\n): Promise<T | null> {\n // Normalize method to upper case for robustness\n const method = spec.method.toUpperCase() as HttpMethod;\n\n // 1. 遍历处理器数组 (责任链)\n for (const handler of handlers) {\n if (handler.shouldHandle(spec.url, method)) {\n try {\n const { adapter } = handler;\n const headers = spec.headers;\n\n switch (method) {\n case \"GET\":\n return await adapter.get<T>(spec.url, spec.body, headers);\n case \"POST\":\n return await adapter.post<T>(spec.url, spec.body, headers);\n case \"PUT\":\n if (!adapter.put)\n throw new Error(`Adapter ${handler.name} missing PUT method`);\n return await adapter.put<T>(spec.url, spec.body, headers);\n case \"DELETE\":\n if (!adapter.delete)\n throw new Error(`Adapter ${handler.name} missing DELETE method`);\n return await adapter.delete<T>(spec.url, spec.body, headers);\n case \"PATCH\":\n if (!adapter.patch)\n throw new Error(`Adapter ${handler.name} missing PATCH method`);\n return await adapter.patch<T>(spec.url, spec.body, headers);\n default:\n throw new Error(`Unsupported method: ${method}`);\n }\n } catch (err) {\n console.warn(\n `Handler [${handler.name || \"Anonymous\"}] failed for ${spec.url}`,\n err,\n );\n // 如果需要继续尝试下一个 handler,可以在这里 continue,但通常由第一个匹配者全权负责\n return null;\n }\n }\n }\n\n // 2. 默认行为 (Fallback): 绝对路径自动走标准 fetch\n if (spec.url.startsWith(\"http://\") || spec.url.startsWith(\"https://\")) {\n return await executeFallbackFetch<T>(spec);\n }\n\n console.warn(`No handler found for url: ${spec.url}`);\n return null;\n}\n\n/**\n * 标准 Fetch 回退实现\n */\nasync function executeFallbackFetch<T>(\n spec: HttpRequestSpec,\n): Promise<T | null> {\n try {\n const response = await fetch(spec.url, {\n method: spec.method,\n headers: spec.headers,\n body: spec.body ? JSON.stringify(spec.body) : undefined,\n credentials: \"include\",\n });\n\n if (!response.ok) {\n console.log(`External Request failed: ${response.status} ${spec.url}`);\n return null;\n }\n\n return await response.json();\n } catch (err) {\n console.log(\"External Request error:\", err);\n return null;\n }\n}\n\n/**\n * 根据路径获取对象值\n */\nfunction getByPath(obj: any, path: string): any {\n if (!path) return obj;\n return path.split(\".\").reduce((acc, part) => acc && acc[part], obj);\n}\n\n/**\n * 变量替换 (Internal)\n * 支持字符串 {{key}} 和 {{key.prop}}\n */\nfunction substituteVariables(target: any, context: any): any {\n if (typeof target === \"string\") {\n // 1. 如果整个字符串就是一个变量占位符,例如 \"{{user}}\",则返回原始对象/值\n const directMatch = target.match(/^\\{\\{([\\w\\.]+)\\}\\}$/);\n if (directMatch) {\n const path = directMatch[1];\n const val = getByPath(context, path);\n return val !== undefined ? val : \"\";\n }\n\n // 2. 如果是混合字符串,例如 \"Hello {{user.name}}\",则执行字符串替换\n return target.replace(/\\{\\{([\\w\\.]+)\\}\\}/g, (_, path) => {\n const val = getByPath(context, path);\n return val !== undefined ? String(val) : \"\";\n });\n }\n\n if (Array.isArray(target)) {\n return target.map((item) => substituteVariables(item, context));\n }\n\n if (target && typeof target === \"object\") {\n const result: any = {};\n for (const key in target) {\n result[key] = substituteVariables(target[key], context);\n }\n return result;\n }\n\n return target;\n}\n\n// ============================================================================\n// Main Runner\n// ============================================================================\n\n/**\n * 请求链执行器 (Request Chain Engine)\n * 支持链式请求、上下文累积、智能路由、变量替换\n *\n * @param requests 链式执行规则列表\n * @param handlers 网络适配器处理器列表 (按顺序匹配)\n */\nexport async function executeRequestChain<T>(\n requests: ChainRequestRule[],\n handlers: NetworkHandler[] = [],\n getChainRequests?: (\n context: Record<string, any>,\n ) => ChainRequestRule[] | null | undefined,\n initContext?: Record<string, any>,\n): Promise<T> {\n const context: Record<string, any> = initContext || {};\n let lastResult: any = null;\n\n for (const rule of requests) {\n try {\n // 1. 变量替换 (支持从 Context 注入对象到 Request Body/URL/Headers)\n const requestSpec = substituteVariables(rule.request, context);\n\n // 2. 执行请求 (传入 handlers)\n let rawData = await executeChainRequest<any>(requestSpec, handlers);\n\n if (getChainRequests) {\n const chainRequests = getChainRequests(rawData);\n if (chainRequests && chainRequests.length > 0) {\n rawData = await executeRequestChain(\n chainRequests,\n handlers,\n getChainRequests,\n JSON.parse(JSON.stringify(context)),\n );\n }\n }\n\n lastResult = rawData;\n\n if (rawData) {\n // 5. 存储结果\n if (rule.key) {\n const data = rule.selector\n ? getByPath(rawData, rule.selector)\n : rawData;\n context[rule.key] = data;\n }\n } else if (!rule.optional) {\n throw new Error(\n `Failed to fetch required data for key: ${rule.key || \"unknown\"}`,\n );\n }\n } catch (err) {\n if (!rule.optional) {\n throw err;\n }\n console.warn(`Optional request failed for rule:`, rule, err);\n }\n }\n\n return lastResult as T;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -130,4 +130,4 @@ async function executeRequestChain(requests, handlers = [], getChainRequests, in
130
130
  export {
131
131
  executeRequestChain
132
132
  };
133
- //# sourceMappingURL=chunk-2OSF7OWV.mjs.map
133
+ //# sourceMappingURL=chunk-LMHEH2D5.js.map
@@ -179,4 +179,4 @@ export {
179
179
  isApiError,
180
180
  createApiClient
181
181
  };
182
- //# sourceMappingURL=chunk-XARZ5KSG.mjs.map
182
+ //# sourceMappingURL=chunk-R553M6QQ.js.map
@@ -169,4 +169,4 @@ export {
169
169
  parseSecureRequest,
170
170
  createProtobufHooks
171
171
  };
172
- //# sourceMappingURL=chunk-VJDS4J5H.mjs.map
172
+ //# sourceMappingURL=chunk-WYNYLDCF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/protobuf.ts","../src/gen/secure_pb.ts"],"sourcesContent":["/**\n * Protobuf 安全通信模块\n *\n * 提供基于 Protobuf 的二进制序列化、XOR 混淆加密以及与 API Client 的无缝集成。\n */\n\nimport {\n create,\n toBinary,\n fromBinary,\n toJson,\n type DescMessage,\n} from \"@bufbuild/protobuf\";\nimport { SecurePayloadSchema } from \"./gen/secure_pb.js\";\nimport type { FetchContext } from \"ofetch\";\n\n// ============================================================================\n// 1. 类型定义 (Types)\n// ============================================================================\n\n/**\n * 适配 @bufbuild/protobuf v2 的类型占位\n * 在 v2 中,Schema (MessageDesc) 是核心对象\n */\nexport type ProtobufTypeLike = DescMessage;\n\n/**\n * 二进制混淆接口\n * 允许外部定义混淆逻辑,以便与不同语言实现的后端兼容\n */\nexport interface ProtobufObfuscator {\n encrypt(data: Uint8Array): Uint8Array | Promise<Uint8Array>;\n decrypt(data: Uint8Array): Uint8Array | Promise<Uint8Array>;\n}\n\n/**\n * Protobuf 编解码集成配置项\n * 用于控制序列化、混淆、钩子集成等全流程\n */\nexport interface ProtobufOptions {\n /**\n * 混淆器实现\n * 提供该选项时才会启用混淆\n */\n obfuscator?: ProtobufObfuscator;\n /** 预编译的 Protobuf 类型 Schema (推荐,性能最高) */\n protoType?: ProtobufTypeLike;\n /** 数据转换钩子:处理业务模型与 Proto 结构不一致的情况 */\n transform?: {\n beforeEncode?: (data: any) => any;\n afterDecode?: (payload: any) => any;\n };\n /** 外部定义的编码逻辑 (返回原始二进制) */\n encode?: (data: any) => Uint8Array | Promise<Uint8Array>;\n /** 外部定义的解码逻辑 (返回原始对象) */\n decode?: <T>(buffer: Uint8Array) => T | Promise<T>;\n}\n\nexport const PROTOBUF_CONTENT_TYPE = \"application/x-protobuf\";\n\n/** 检查是否为 Protobuf 响应头 */\nexport function isProtobufContentType(contentType: string | null): boolean {\n return !!contentType?.toLowerCase().includes(PROTOBUF_CONTENT_TYPE);\n}\n\n/**\n * 获取 Protobuf 相关的 Header 配置对象\n */\nexport function getProtobufHeaders({\n send = false,\n receive = false,\n}: {\n send?: boolean;\n receive?: boolean;\n} = {}): Record<string, string> {\n const headers: Record<string, string> = {};\n\n if (send) {\n headers[\"Content-Type\"] = PROTOBUF_CONTENT_TYPE;\n }\n\n if (receive) {\n headers[\"Accept\"] = PROTOBUF_CONTENT_TYPE;\n }\n\n return headers;\n}\n\n// ============================================================================\n// 2. 内部工具函数 (Internal Utilities)\n// ============================================================================\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n/** 简单的二进制异或混淆转换 */\nexport function xorTransform(\n data: Uint8Array,\n key: string | Uint8Array,\n): Uint8Array {\n const keyBytes = typeof key === \"string\" ? encoder.encode(key) : key;\n if (keyBytes.length === 0) return data;\n\n for (let i = 0; i < data.length; i++) {\n data[i] ^= keyBytes[i % keyBytes.length];\n }\n return data;\n}\n\n/** 创建一个简单的 XOR 混淆器 */\nexport function createXorObfuscator(\n key: string | Uint8Array,\n): ProtobufObfuscator {\n return {\n encrypt: (data) => xorTransform(data, key),\n decrypt: (data) => xorTransform(data, key),\n };\n}\n\n// ============================================================================\n// 3. 核心编解码逻辑 (Core Codecs)\n// ============================================================================\n\n/**\n * 编码安全载荷\n *\n * 流程:业务数据 -> (自定义编码 / Proto 序列化) -> 二进制混淆\n */\nexport async function encodeSecure<T>(\n data: T,\n options: ProtobufOptions = {},\n): Promise<Uint8Array> {\n const { obfuscator, protoType, transform, encode: customEncode } = options;\n\n let buffer: Uint8Array;\n\n // 1. 预处理阶段:无论后续走哪条路径,只要定义了 beforeEncode 就先执行\n const processedData = transform?.beforeEncode\n ? transform.beforeEncode(data)\n : data;\n\n // 1. 序列化阶段\n if (customEncode) {\n buffer = await customEncode(processedData);\n } else {\n // 构造最终要交给 Protobuf 序列化的对象\n if (protoType) {\n // 自定义模式:直接使用预处理后的数据\n const message = create(protoType, processedData as any);\n buffer = toBinary(protoType, message);\n } else {\n // 默认容器模式:将预处理后的数据封装进信封\n const payload = {\n ts: BigInt(Date.now()),\n data:\n processedData instanceof Uint8Array\n ? processedData\n : encoder.encode(JSON.stringify(processedData)),\n };\n const message = create(SecurePayloadSchema, payload);\n buffer = toBinary(SecurePayloadSchema, message);\n }\n }\n\n // 2. 混淆阶段\n if (obfuscator) {\n return await obfuscator.encrypt(buffer);\n }\n return buffer;\n}\n\n/**\n * 解码安全载荷\n *\n * 流程:二进制流 -> 二进制反混淆 -> (自定义解码 / Proto 反序列化) -> 业务数据\n */\nexport async function decodeSecure<T>(\n buffer: Uint8Array | ArrayBuffer,\n options: ProtobufOptions = {},\n): Promise<T> {\n const { obfuscator, protoType, transform, decode: customDecode } = options;\n\n // 1. 标准化为 Uint8Array 视图 (跨环境最稳写法)\n let uint8 = ArrayBuffer.isView(buffer)\n ? new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength)\n : new Uint8Array(buffer);\n\n if (uint8.length === 0) {\n return null as T;\n }\n\n // 1. 混淆阶段\n if (uint8.length > 0 && obfuscator) {\n uint8 = await obfuscator.decrypt(uint8);\n }\n\n // 2. 反序列化阶段\n if (customDecode) {\n return await customDecode<T>(uint8);\n }\n\n const schema = protoType || SecurePayloadSchema;\n const message = fromBinary(schema, uint8);\n const plainObj = toJson(schema, message) as any;\n\n // 3. 转换阶段\n if (transform?.afterDecode) {\n return transform.afterDecode(plainObj);\n }\n\n // 内置容器模式的额外还原逻辑\n if (!protoType && plainObj.data) {\n // Note: toJson converts bytes to base64 string in pb-es v2\n // If we want raw Uint8Array, we might need a different conversion or manual handling\n // However, @bufbuild/protobuf's toJson by default base64 encodes bytes.\n // Let's check if we can get the raw object.\n\n // In pb-es v2, if we want the \"plain\" object with raw types,\n // we can just use the message itself as it is a standard JS object,\n // but with some non-enumerable properties.\n\n const rawData = (message as any).data as Uint8Array;\n if (rawData) {\n const jsonString = decoder.decode(rawData);\n try {\n return JSON.parse(jsonString) as T;\n } catch {\n return jsonString as unknown as T;\n }\n }\n }\n\n return plainObj as T;\n}\n\n// ============================================================================\n// 4. HTTP/环境适配工具 (HTTP Tools)\n// ============================================================================\n\n/** 创建安全响应 (Worker/Server 端使用) */\nexport async function secureResponse<T>(\n data: T,\n options?: ProtobufOptions & { corsHeaders?: Record<string, string> },\n): Promise<Response> {\n const buffer = await encodeSecure(data, options);\n const cleanBuffer = buffer.slice();\n\n return new Response(cleanBuffer, {\n headers: {\n ...getProtobufHeaders({ send: true }),\n ...options?.corsHeaders,\n },\n });\n}\n\n/** * 解析安全请求体 (Worker/Server 端使用)\n * @param request 原生 Request 对象\n * @param options 编解码配置(可包含当前接口对应的 protoType 和自定义 obfuscator)\n */\nexport async function parseSecureRequest<T>(\n request: Request,\n options: ProtobufOptions = {}, // 统一使用这个配置对象\n): Promise<T> {\n const buffer = await request.arrayBuffer();\n return decodeSecure<T>(buffer, options);\n}\n\n// ============================================================================\n// 5. API Client 钩子逻辑 (Integration Hooks)\n// ============================================================================\n\n/** 创建 Protobuf 编解码钩子 (用于与 createApiClient 配合使用) */\nexport function createProtobufHooks(globalOptions: ProtobufOptions = {}) {\n return {\n async onRequest(context: FetchContext) {\n const { options: reqOptions } = context;\n\n // 1. 动态合并配置\n const mergedOptions: ProtobufOptions = {\n ...globalOptions,\n ...reqOptions,\n };\n\n const headers =\n reqOptions.headers instanceof Headers\n ? reqOptions.headers\n : new Headers(reqOptions.headers as HeadersInit | undefined);\n\n // 自动编码请求体\n if (\n isProtobufContentType(headers.get(\"Content-Type\")) &&\n reqOptions.body &&\n !(reqOptions.body instanceof Uint8Array)\n ) {\n reqOptions.body = await encodeSecure(reqOptions.body, mergedOptions);\n }\n\n // 自动设置期望响应类型\n if (\n isProtobufContentType(headers.get(\"Accept\")) &&\n !reqOptions.responseType\n ) {\n reqOptions.responseType = \"arrayBuffer\";\n }\n\n reqOptions.headers = headers;\n },\n\n async onResponse(context: FetchContext) {\n const { response, options: reqOptions } = context;\n if (!response?._data || response.status === 204) return;\n\n // 同样在响应阶段合并 options\n const mergedOptions: ProtobufOptions = {\n ...globalOptions,\n ...reqOptions,\n };\n\n if (isProtobufContentType(response.headers.get(\"Content-Type\"))) {\n try {\n response._data = await decodeSecure(response._data, mergedOptions);\n // const buffer = response._data as ArrayBuffer;\n // if (buffer && buffer.byteLength > 0) {\n // response._data = await decode(new Uint8Array(buffer));\n // }\n } catch (e) {\n console.log(\"Error [Protobuf Decode Error]\", e);\n response._data = null;\n }\n } else if (response._data instanceof ArrayBuffer) {\n const text = decoder.decode(response._data);\n try {\n response._data = JSON.parse(text);\n } catch {\n response._data = text;\n }\n }\n },\n };\n}\n","// @generated by protoc-gen-es v2.10.2 with parameter \"target=ts\"\n// @generated from file secure.proto (package secure, syntax proto3)\n/* eslint-disable */\n\nimport type { GenFile, GenMessage } from \"@bufbuild/protobuf/codegenv2\";\nimport { fileDesc, messageDesc } from \"@bufbuild/protobuf/codegenv2\";\nimport type { Message } from \"@bufbuild/protobuf\";\n\n/**\n * Describes the file secure.proto.\n */\nexport const file_secure: GenFile = /*@__PURE__*/\n fileDesc(\"CgxzZWN1cmUucHJvdG8SBnNlY3VyZSIpCg1TZWN1cmVQYXlsb2FkEgoKAnRzGAEgASgDEgwKBGRhdGEYAiABKAxCUQoKY29tLnNlY3VyZUILU2VjdXJlUHJvdG9QAaICA1NYWKoCBlNlY3VyZcoCBlNlY3VyZeICElNlY3VyZVxHUEJNZXRhZGF0YeoCBlNlY3VyZWIGcHJvdG8z\");\n\n/**\n * @generated from message secure.SecurePayload\n */\nexport type SecurePayload = Message<\"secure.SecurePayload\"> & {\n /**\n * @generated from field: int64 ts = 1;\n */\n ts: bigint;\n\n /**\n * @generated from field: bytes data = 2;\n */\n data: Uint8Array;\n};\n\n/**\n * Describes the message secure.SecurePayload.\n * Use `create(SecurePayloadSchema)` to create a new message.\n */\nexport const SecurePayloadSchema: GenMessage<SecurePayload> = /*@__PURE__*/\n messageDesc(file_secure, 0);\n\n"],"mappings":"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","names":[]}
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/client.ts
21
+ var client_exports = {};
22
+ __export(client_exports, {
23
+ createApiClient: () => createApiClient,
24
+ isApiError: () => isApiError
25
+ });
26
+ module.exports = __toCommonJS(client_exports);
27
+ var import_ofetch = require("ofetch");
28
+ var isApiError = (error) => {
29
+ return error instanceof Error && "code" in error;
30
+ };
31
+ var defaultUnwrapResponse = (result, returnFullResponse = false) => {
32
+ if (result && typeof result === "object" && "code" in result) {
33
+ const body = result;
34
+ if (body.code === 0) {
35
+ return returnFullResponse ? body : body.data;
36
+ }
37
+ }
38
+ return result;
39
+ };
40
+ var defaultCreateErrorFromResult = (res) => {
41
+ const error = new Error(res.message || "Request failed");
42
+ error.code = res.code;
43
+ error.data = res.data;
44
+ return error;
45
+ };
46
+ var extractAccessToken = (data) => {
47
+ if (typeof data === "string" && data) {
48
+ return data;
49
+ }
50
+ if (!data || typeof data !== "object") {
51
+ throw new Error(
52
+ "Invalid refresh token response: data is not an object or string"
53
+ );
54
+ }
55
+ const anyData = data;
56
+ const accessToken = anyData.access_token ?? anyData.accessToken ?? anyData.token;
57
+ if (typeof accessToken === "string" && accessToken) {
58
+ return accessToken;
59
+ }
60
+ throw new Error(
61
+ "Invalid refresh token response: no access_token or token field found"
62
+ );
63
+ };
64
+ var refreshingPromises = /* @__PURE__ */ new WeakMap();
65
+ function createApiClient(options) {
66
+ const {
67
+ baseURL,
68
+ tokenStorage,
69
+ refreshToken = false,
70
+ retry = 1,
71
+ retryDelay = 1e3,
72
+ isAuthError = (code) => code === 401,
73
+ unwrapResponse = defaultUnwrapResponse,
74
+ createErrorFromResult = defaultCreateErrorFromResult
75
+ } = options;
76
+ const refreshTokenFn = !refreshToken ? null : typeof refreshToken === "string" ? async () => {
77
+ const res = await (0, import_ofetch.ofetch)(refreshToken, {
78
+ baseURL,
79
+ method: "POST",
80
+ retry,
81
+ retryDelay
82
+ });
83
+ if (res.code !== 0) {
84
+ throw createErrorFromResult(res);
85
+ }
86
+ return extractAccessToken(res.data);
87
+ } : refreshToken;
88
+ const rawRequest = import_ofetch.ofetch.create({
89
+ baseURL,
90
+ retry,
91
+ retryDelay,
92
+ async onRequest(context) {
93
+ const { options: reqOptions } = context;
94
+ const token = await tokenStorage.getAccessToken();
95
+ const headers = new Headers(
96
+ reqOptions.headers
97
+ );
98
+ if (token) {
99
+ headers.set("Authorization", `Bearer ${token}`);
100
+ }
101
+ reqOptions.headers = headers;
102
+ if (options.onRequest) {
103
+ await options.onRequest(context);
104
+ }
105
+ },
106
+ async onResponse(context) {
107
+ const { response } = context;
108
+ if (response.status === 204) {
109
+ response._data = null;
110
+ return;
111
+ }
112
+ if (options.onResponse) {
113
+ await options.onResponse(context);
114
+ }
115
+ },
116
+ async onResponseError(context) {
117
+ const { response } = context;
118
+ const message = response?._data?.message || `HTTP ${response?.status || "Network Error"}`;
119
+ const error = new Error(message);
120
+ error.status = response?.status;
121
+ error.data = response?._data;
122
+ throw error;
123
+ }
124
+ });
125
+ async function request(url, options2 = {}) {
126
+ const { returnFullResponse, _retry, ...fetchOptions } = options2;
127
+ const res = await rawRequest(url, fetchOptions);
128
+ if (res.code === 0) {
129
+ return unwrapResponse(res, !!returnFullResponse);
130
+ }
131
+ if (isAuthError(res.code) && !_retry && refreshTokenFn) {
132
+ try {
133
+ let refreshingPromise = refreshingPromises.get(tokenStorage);
134
+ if (!refreshingPromise) {
135
+ refreshingPromise = refreshTokenFn().finally(() => {
136
+ refreshingPromises.delete(tokenStorage);
137
+ });
138
+ refreshingPromises.set(tokenStorage, refreshingPromise);
139
+ }
140
+ const newToken = await refreshingPromise;
141
+ if (newToken) {
142
+ await tokenStorage.setAccessToken(newToken);
143
+ }
144
+ return await request(url, {
145
+ ...options2 || {},
146
+ _retry: true
147
+ });
148
+ } catch (e) {
149
+ await tokenStorage.setAccessToken("");
150
+ throw createErrorFromResult(res);
151
+ }
152
+ }
153
+ throw createErrorFromResult(res);
154
+ }
155
+ async function get(url, params = {}, options2) {
156
+ return request(url, {
157
+ ...options2,
158
+ method: "GET",
159
+ query: params
160
+ });
161
+ }
162
+ async function post(url, body = {}, options2) {
163
+ return request(url, {
164
+ ...options2,
165
+ method: "POST",
166
+ body
167
+ });
168
+ }
169
+ async function put(url, body = {}, options2) {
170
+ return request(url, {
171
+ ...options2,
172
+ method: "PUT",
173
+ body
174
+ });
175
+ }
176
+ async function patch(url, body = {}, options2) {
177
+ return request(url, {
178
+ ...options2,
179
+ method: "PATCH",
180
+ body
181
+ });
182
+ }
183
+ async function del(url, params = {}, options2) {
184
+ return request(url, {
185
+ ...options2,
186
+ method: "DELETE",
187
+ query: params
188
+ });
189
+ }
190
+ return {
191
+ rawRequest,
192
+ request,
193
+ get,
194
+ post,
195
+ put,
196
+ patch,
197
+ del
198
+ };
199
+ }
200
+ // Annotate the CommonJS export names for ESM import in node:
201
+ 0 && (module.exports = {
202
+ createApiClient,
203
+ isApiError
204
+ });
205
+ //# sourceMappingURL=client.cjs.map