@i.un/api-client 1.1.1 → 1.1.3

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/client.js ADDED
@@ -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.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client.ts"],"sourcesContent":["import {\n ofetch,\n type FetchOptions,\n type FetchContext,\n type $Fetch,\n} from \"ofetch\";\n\nexport interface ApiResult<T> {\n code: number;\n data: T;\n message: string;\n}\n\nexport interface ApiError extends Error {\n code: number; // 业务错误码\n data?: unknown; // 后端返回的 data 字段(如果有)\n status?: number; // HTTP 状态码(网络错误时)\n}\n\n// 类型守卫:判断是否是 API 业务错误\nexport const isApiError = (error: unknown): error is ApiError => {\n return error instanceof Error && \"code\" in error;\n};\n\nexport interface TokenStorage {\n getAccessToken: () => Promise<string> | string;\n setAccessToken: (token: string) => Promise<void> | void;\n}\n\nexport interface CreateApiClientOptions {\n baseURL: string;\n tokenStorage: TokenStorage;\n refreshToken?: (() => Promise<string>) | string | false;\n retry?: number; // 重试次数,默认 1\n retryDelay?: number; // 重试间隔,默认 1s\n isAuthError?: (code: number) => boolean;\n unwrapResponse?<T>(result: unknown, returnFullResponse: boolean): T;\n createErrorFromResult?(res: unknown): Error;\n /**\n * 自定义请求钩子\n * 在请求发送前执行,可用于编码请求体等\n */\n onRequest?: (context: FetchContext) => void | Promise<void>;\n /**\n * 自定义响应钩子\n * 在响应返回后执行,可用于解码响应体等\n */\n onResponse?: (context: FetchContext) => void | Promise<void>;\n}\n\ntype RequestOptions = Omit<FetchOptions<\"json\">, \"responseType\"> & {\n returnFullResponse?: boolean;\n responseType?: \"json\" | \"arrayBuffer\" | \"text\" | \"blob\";\n};\n\n// 解包后端统一响应格式(默认实现)\nconst defaultUnwrapResponse = <T>(\n result: unknown,\n returnFullResponse = false,\n): T => {\n if (result && typeof result === \"object\" && \"code\" in result) {\n const body = result as Record<string, unknown>;\n if (body.code === 0) {\n return returnFullResponse ? (body as T) : (body.data as T);\n }\n }\n return result as T;\n};\n\nconst defaultCreateErrorFromResult = (res: ApiResult<unknown>): ApiError => {\n const error = new Error(res.message || \"Request failed\") as ApiError;\n error.code = res.code;\n error.data = res.data;\n return error;\n};\n\nconst extractAccessToken = (data: unknown): string => {\n if (typeof data === \"string\" && data) {\n return data;\n }\n\n if (!data || typeof data !== \"object\") {\n throw new Error(\n \"Invalid refresh token response: data is not an object or string\",\n );\n }\n\n const anyData = data as Record<string, unknown>;\n\n const accessToken =\n anyData.access_token ?? anyData.accessToken ?? anyData.token;\n\n if (typeof accessToken === \"string\" && accessToken) {\n return accessToken;\n }\n\n throw new Error(\n \"Invalid refresh token response: no access_token or token field found\",\n );\n};\n\n// 全局共享的刷新状态,以 tokenStorage 为 Key\n// 这样即使有多个 ApiClient 实例,只要它们共用同一个 tokenStorage,刷新逻辑就是单例的\nconst refreshingPromises = new WeakMap<TokenStorage, Promise<string>>();\n\nexport function createApiClient(options: CreateApiClientOptions) {\n const {\n baseURL,\n tokenStorage,\n refreshToken = false,\n retry = 1,\n retryDelay = 1000,\n isAuthError = (code: number) => code === 401,\n unwrapResponse = defaultUnwrapResponse,\n createErrorFromResult = defaultCreateErrorFromResult,\n } = options;\n\n const refreshTokenFn: (() => Promise<string>) | null = !refreshToken\n ? null\n : typeof refreshToken === \"string\"\n ? async () => {\n const res = await ofetch<ApiResult<unknown>>(refreshToken, {\n baseURL,\n method: \"POST\",\n retry,\n retryDelay,\n });\n\n if (res.code !== 0) {\n throw createErrorFromResult(res as ApiResult<unknown>);\n }\n\n return extractAccessToken(res.data);\n }\n : refreshToken;\n\n const rawRequest = ofetch.create({\n baseURL,\n retry,\n retryDelay,\n\n async onRequest(context: FetchContext) {\n const { options: reqOptions } = context;\n const token = await tokenStorage.getAccessToken();\n\n const headers = new Headers(\n reqOptions.headers as HeadersInit | undefined,\n );\n\n if (token) {\n headers.set(\"Authorization\", `Bearer ${token}`);\n }\n\n reqOptions.headers = headers;\n\n // 执行用户自定义钩子\n if (options.onRequest) {\n await options.onRequest(context);\n }\n },\n async onResponse(context) {\n const { response } = context;\n // 不在这里处理 code,统一交给 fetchApi 层处理\n if (response.status === 204) {\n response._data = null;\n return;\n }\n\n // 执行用户自定义钩子\n if (options.onResponse) {\n await options.onResponse(context);\n }\n },\n\n async onResponseError(context: FetchContext) {\n // 后端统一返回 HTTP 200,此处只处理网络层错误(如超时、断网)\n const { response } = context;\n const message =\n (response?._data as Record<string, unknown>)?.message ||\n `HTTP ${response?.status || \"Network Error\"}`;\n\n const error = new Error(message as string) as ApiError;\n error.status = response?.status;\n error.data = response?._data;\n throw error;\n },\n }) as $Fetch;\n\n async function request<T = unknown>(\n url: string,\n options: RequestOptions & { _retry?: boolean } = {},\n ): Promise<T> {\n // 提取自定义选项,避免传递给 $fetch\n const { returnFullResponse, _retry, ...fetchOptions } = options;\n\n // const res = await rawRequest<ApiResult<T>>(url, fetchOptions);\n const res = (await rawRequest(url, fetchOptions as FetchOptions)) as ApiResult<T>;\n\n if (res.code === 0) {\n return unwrapResponse<T>(res, !!returnFullResponse);\n // if (returnFullResponse) {\n // return res as unknown as T;\n // }\n\n // return res.data;\n }\n\n if (isAuthError(res.code) && !_retry && refreshTokenFn) {\n try {\n let refreshingPromise = refreshingPromises.get(tokenStorage);\n\n if (!refreshingPromise) {\n refreshingPromise = refreshTokenFn().finally(() => {\n refreshingPromises.delete(tokenStorage);\n });\n refreshingPromises.set(tokenStorage, refreshingPromise);\n }\n\n const newToken = await refreshingPromise;\n\n if (newToken) {\n await tokenStorage.setAccessToken(newToken);\n }\n\n return await request<T>(url, {\n ...(options || {}),\n _retry: true,\n } as any);\n } catch (e) {\n await tokenStorage.setAccessToken(\"\");\n throw createErrorFromResult(res as ApiResult<unknown>);\n }\n }\n\n throw createErrorFromResult(res as ApiResult<unknown>);\n }\n\n async function get<T = unknown>(\n url: string,\n params: FetchOptions[\"query\"] = {},\n options?: RequestOptions,\n ) {\n return request<T>(url, {\n ...options,\n method: \"GET\",\n query: params,\n } as any);\n }\n\n async function post<T = unknown>(\n url: string,\n body: FetchOptions[\"body\"] = {},\n options?: RequestOptions,\n ) {\n return request<T>(url, {\n ...options,\n method: \"POST\",\n body,\n } as any);\n }\n\n async function put<T = unknown>(\n url: string,\n body: FetchOptions[\"body\"] = {},\n options?: RequestOptions,\n ) {\n return request<T>(url, {\n ...options,\n method: \"PUT\",\n body,\n } as any);\n }\n\n async function patch<T = unknown>(\n url: string,\n body: FetchOptions[\"body\"] = {},\n options?: RequestOptions,\n ) {\n return request<T>(url, {\n ...options,\n method: \"PATCH\",\n body,\n } as any);\n }\n\n async function del<T = unknown>(\n url: string,\n params: FetchOptions[\"query\"] = {},\n options?: RequestOptions,\n ) {\n return request<T>(url, {\n ...options,\n method: \"DELETE\",\n query: params,\n } as any);\n }\n\n return {\n rawRequest,\n request,\n get,\n post,\n put,\n patch,\n del,\n };\n}\n"],"mappings":"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","names":["options"]}
@@ -0,0 +1,9 @@
1
+ import {
2
+ createApiClient,
3
+ isApiError
4
+ } from "./chunk-XARZ5KSG.mjs";
5
+ export {
6
+ createApiClient,
7
+ isApiError
8
+ };
9
+ //# sourceMappingURL=client.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/index.d.mts CHANGED
@@ -1,221 +1,5 @@
1
- import { FetchContext, $Fetch, FetchOptions } from 'ofetch';
2
- import protobuf from 'protobufjs/light';
3
-
4
- interface ApiResult<T> {
5
- code: number;
6
- data: T;
7
- message: string;
8
- }
9
- interface ApiError extends Error {
10
- code: number;
11
- data?: unknown;
12
- status?: number;
13
- }
14
- declare const isApiError: (error: unknown) => error is ApiError;
15
- interface TokenStorage {
16
- getAccessToken: () => Promise<string> | string;
17
- setAccessToken: (token: string) => Promise<void> | void;
18
- }
19
- interface CreateApiClientOptions {
20
- baseURL: string;
21
- tokenStorage: TokenStorage;
22
- refreshToken?: (() => Promise<string>) | string | false;
23
- retry?: number;
24
- retryDelay?: number;
25
- isAuthError?: (code: number) => boolean;
26
- unwrapResponse?<T>(result: unknown, returnFullResponse: boolean): T;
27
- createErrorFromResult?(res: unknown): Error;
28
- /**
29
- * 自定义请求钩子
30
- * 在请求发送前执行,可用于编码请求体等
31
- */
32
- onRequest?: (context: FetchContext) => void | Promise<void>;
33
- /**
34
- * 自定义响应钩子
35
- * 在响应返回后执行,可用于解码响应体等
36
- */
37
- onResponse?: (context: FetchContext) => void | Promise<void>;
38
- }
39
- type RequestOptions = Omit<FetchOptions<"json">, "responseType"> & {
40
- returnFullResponse?: boolean;
41
- responseType?: "json" | "arrayBuffer" | "text" | "blob";
42
- };
43
- declare function createApiClient(options: CreateApiClientOptions): {
44
- rawRequest: $Fetch;
45
- request: <T = unknown>(url: string, options?: RequestOptions & {
46
- _retry?: boolean;
47
- }) => Promise<T>;
48
- get: <T = unknown>(url: string, params?: FetchOptions["query"], options?: RequestOptions) => Promise<T>;
49
- post: <T = unknown>(url: string, body?: FetchOptions["body"], options?: RequestOptions) => Promise<T>;
50
- put: <T = unknown>(url: string, body?: FetchOptions["body"], options?: RequestOptions) => Promise<T>;
51
- patch: <T = unknown>(url: string, body?: FetchOptions["body"], options?: RequestOptions) => Promise<T>;
52
- del: <T = unknown>(url: string, params?: FetchOptions["query"], options?: RequestOptions) => Promise<T>;
53
- };
54
-
55
- /**
56
- * Request Chain Runner
57
- * 负责解析和执行通用的 HTTP 请求链 (Chain Execution)
58
- * 适用于任何需要链式 HTTP 请求编排的场景
59
- */
60
- /** Supported HTTP Methods */
61
- type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
62
- /** HTTP 请求参数 */
63
- interface HttpRequestSpec {
64
- method: HttpMethod;
65
- url: string;
66
- headers?: Record<string, string>;
67
- body?: any;
68
- }
69
- /**
70
- * 网络适配器接口
71
- * 定义实际发送请求的能力
72
- */
73
- /**
74
- * 网络适配器接口
75
- * 定义实际发送请求的能力
76
- */
77
- interface NetworkAdapter {
78
- get<T>(url: string, params?: any, headers?: Record<string, string>): Promise<T>;
79
- post<T>(url: string, body?: any, headers?: Record<string, string>): Promise<T>;
80
- put?<T>(url: string, body?: any, headers?: Record<string, string>): Promise<T>;
81
- delete?<T>(url: string, params?: any, headers?: Record<string, string>): Promise<T>;
82
- patch?<T>(url: string, body?: any, headers?: Record<string, string>): Promise<T>;
83
- }
84
- /**
85
- * 网络处理器接口 (Strategy Pattern)
86
- * 组合了"匹配规则"和"执行能力"
87
- */
88
- interface NetworkHandler {
89
- /** 处理器名称 (用于调试) */
90
- name?: string;
91
- /** 判断该 URL 是否由此适配器处理 */
92
- shouldHandle(url: string, method: HttpMethod): boolean;
93
- /** 具体的网络适配器 */
94
- adapter: NetworkAdapter;
95
- }
96
- /**
97
- * 请求链规则 (Chain Step)
98
- */
99
- interface ChainRequestRule {
100
- /**
101
- * 结果存储的键名 (Context Key)
102
- * Fetch 结果存储: context[key] = response
103
- */
104
- key?: string;
105
- /**
106
- * HTTP 请求详情
107
- */
108
- request: HttpRequestSpec;
109
- /**
110
- * 数据提取选择器
111
- * 用于从响应中提取特定数据, e.g., "elements.0"
112
- */
113
- selector?: string;
114
- /** 是否允许请求失败 (默认 false) */
115
- optional?: boolean;
116
- /**
117
- * 是否包含上下文
118
- * 如果为 true,请求 Body 将自动合并当前 Context 和 Payload
119
- */
120
- includeContext?: boolean;
121
- /**
122
- * 上下文筛选列表 (优先级高于 includeContext)
123
- * 指定需要合并到 Body 中的 Context Key 列表
124
- * e.g., ["rawProfile", "rawCompany"]
125
- */
126
- pickContext?: string[];
127
- /** 额外 Payload (仅当 includeContext 为 true 或 pickContext 存在时合并) */
128
- payload?: Record<string, any>;
129
- }
130
- /**
131
- * 请求链执行器 (Request Chain Engine)
132
- * 支持链式请求、上下文累积、智能路由、变量替换
133
- *
134
- * @param requests 链式执行规则列表
135
- * @param handlers 网络适配器处理器列表 (按顺序匹配)
136
- */
137
- declare function executeRequestChain<T>(requests: ChainRequestRule[], handlers?: NetworkHandler[], getChainRequests?: (context: Record<string, any>) => ChainRequestRule[] | null | undefined, initContext?: Record<string, any>): Promise<T>;
138
-
139
- /**
140
- * Protobuf 安全通信模块
141
- *
142
- * 提供基于 Protobuf 的二进制序列化、XOR 混淆加密以及与 API Client 的无缝集成。
143
- */
144
-
145
- /**
146
- * 基础编解码配置
147
- * 控制如何将数据转换为二进制,以及是否混淆
148
- */
149
- interface ProtobufCodecOptions {
150
- /** 是否启用二进制 XOR 混淆 (默认 true) */
151
- obfuscate?: boolean;
152
- /**
153
- * 混淆密钥
154
- * 可以是静态字符串/字节数组,也可以是动态获取密钥的同步或异步函数
155
- */
156
- obfuscationKey?: string | Uint8Array | (() => string | Uint8Array | Promise<string | Uint8Array>);
157
- /** 预编译的 Protobuf 类型对象 (推荐,性能最高) */
158
- protoType?: protobuf.Type;
159
- /** 数据转换钩子:处理业务模型与 Proto 结构不一致的情况 */
160
- transform?: {
161
- beforeEncode?: (data: any) => any;
162
- afterDecode?: (payload: any) => any;
163
- };
164
- }
165
- /**
166
- * 完全自定义编解码接口
167
- * 用于外部项目完全接管序列化过程,同时复用基础库的混淆外壳
168
- */
169
- interface ProtobufCustomCodec {
170
- /** 外部定义的编码逻辑 (返回原始二进制) */
171
- encode?: (data: any) => Uint8Array | Promise<Uint8Array>;
172
- /** 外部定义的解码逻辑 (返回原始对象) */
173
- decode?: <T>(buffer: Uint8Array) => T | Promise<T>;
174
- }
175
- /**
176
- * 集成配置项
177
- * 用于 createProtobufHooks 或 API Client 全局配置
178
- */
179
- interface ProtobufHooksOptions extends ProtobufCodecOptions, ProtobufCustomCodec {
180
- }
181
- /**
182
- * API Client 内部使用的标准化配置
183
- */
184
- interface ProtobufConfig {
185
- encode: (data: any) => Uint8Array | Promise<Uint8Array>;
186
- decode: <T>(buffer: Uint8Array) => T | Promise<T>;
187
- options?: ProtobufCodecOptions;
188
- }
189
- /** 简单的二进制异或混淆转换 */
190
- declare function xorTransform(data: Uint8Array, key: string | Uint8Array): Uint8Array;
191
- /** 获取混淆密钥(支持异步/函数) */
192
- declare function getObfuscationKey(keyOption?: ProtobufCodecOptions["obfuscationKey"]): Promise<string | Uint8Array>;
193
- /**
194
- * 编码安全载荷
195
- *
196
- * 流程:业务数据 -> (自定义编码 / Proto 序列化) -> 二进制混淆
197
- */
198
- declare function encodeSecure<T>(data: T, options?: ProtobufHooksOptions): Promise<Uint8Array>;
199
- /**
200
- * 解码安全载荷
201
- *
202
- * 流程:二进制流 -> 二进制反混淆 -> (自定义解码 / Proto 反序列化) -> 业务数据
203
- */
204
- declare function decodeSecure<T>(buffer: Uint8Array | ArrayBuffer, options?: ProtobufHooksOptions): Promise<T>;
205
- /** 创建安全响应 (Worker/Server 端使用) */
206
- declare function secureResponse<T>(data: T, options?: ProtobufCodecOptions & {
207
- corsHeaders?: Record<string, string>;
208
- }): Promise<Response>;
209
- /** 解析安全请求体 (Worker/Server 端使用) */
210
- declare function parseSecureRequest<T>(request: Request, customDecode?: ProtobufCustomCodec["decode"]): Promise<T>;
211
- /** 检查是否为 Protobuf 响应头 */
212
- declare function isProtobufContentType(contentType: string | null): boolean;
213
- /** 标准化配置对象 */
214
- declare function normalizeProtobufConfig(config: boolean | ProtobufConfig | undefined, options?: ProtobufHooksOptions): ProtobufConfig | null;
215
- /** 创建 Protobuf 编解码钩子 (用于与 createApiClient 配合使用) */
216
- declare function createProtobufHooks(options?: ProtobufHooksOptions): {
217
- onRequest(context: FetchContext): Promise<void>;
218
- onResponse(context: FetchContext): Promise<void>;
219
- };
220
-
221
- export { type ApiError, type ApiResult, type ChainRequestRule, type CreateApiClientOptions, type HttpRequestSpec, type NetworkAdapter, type NetworkHandler, type ProtobufCodecOptions, type ProtobufConfig, type ProtobufCustomCodec, type ProtobufHooksOptions, type TokenStorage, createApiClient, createProtobufHooks, decodeSecure, encodeSecure, executeRequestChain, getObfuscationKey, isApiError, isProtobufContentType, normalizeProtobufConfig, parseSecureRequest, secureResponse, xorTransform };
1
+ export { ApiError, ApiResult, CreateApiClientOptions, TokenStorage, createApiClient, isApiError } from './client.mjs';
2
+ export { ChainRequestRule, HttpRequestSpec, NetworkAdapter, NetworkHandler, executeRequestChain } from './chain.mjs';
3
+ export { PROTOBUF_CONTENT_TYPE, ProtobufCodecOptions, ProtobufCustomCodec, ProtobufHooksOptions, ProtobufTypeLike, createProtobufHooks, decodeSecure, encodeSecure, isProtobufContentType, parseSecureRequest, secureResponse, setProtobufRequestHeaders, xorTransform } from './protobuf.mjs';
4
+ import 'ofetch';
5
+ import 'protobufjs/light';