@i.un/api-client 1.0.8 → 1.0.9
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/index.d.mts +29 -14
- package/dist/index.d.ts +29 -14
- package/dist/index.js +91 -48
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +87 -44
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -146,10 +146,24 @@ declare function executeRequestChain<T>(requests: ChainRequestRule[], handlers?:
|
|
|
146
146
|
*/
|
|
147
147
|
|
|
148
148
|
interface ProtobufCodecOptions {
|
|
149
|
-
/**
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
|
|
149
|
+
/** 是否启用混淆 (默认 true) */
|
|
150
|
+
obfuscate?: boolean;
|
|
151
|
+
/**
|
|
152
|
+
* 混淆密钥
|
|
153
|
+
* 可以是静态字符串/字节数组,也可以是动态获取密钥的同步或异步函数
|
|
154
|
+
*/
|
|
155
|
+
obfuscationKey?: string | Uint8Array | (() => string | Uint8Array | Promise<string | Uint8Array>);
|
|
156
|
+
/** 自定义 Schema (proto3 syntax) */
|
|
157
|
+
schema?: string;
|
|
158
|
+
/** 自定义消息类型名称 (默认 "SecurePayload") */
|
|
159
|
+
messageName?: string;
|
|
160
|
+
/** 自定义数据转换逻辑 (用于非默认 Schema) */
|
|
161
|
+
transform?: {
|
|
162
|
+
/** 编码前转换:将原始数据转为符合 Schema 的对象 */
|
|
163
|
+
beforeEncode?: (data: any) => any;
|
|
164
|
+
/** 解码后转换:将 Schema 对象转回原始数据 */
|
|
165
|
+
afterDecode?: (payload: any) => any;
|
|
166
|
+
};
|
|
153
167
|
}
|
|
154
168
|
interface ProtobufConfig {
|
|
155
169
|
/** 自定义编码函数 */
|
|
@@ -160,20 +174,21 @@ interface ProtobufConfig {
|
|
|
160
174
|
options?: ProtobufCodecOptions;
|
|
161
175
|
}
|
|
162
176
|
/**
|
|
163
|
-
*
|
|
177
|
+
* 编码安全载荷
|
|
164
178
|
*
|
|
165
179
|
* @param data - 任意 JSON 可序列化数据
|
|
166
|
-
* @param options -
|
|
167
|
-
* @returns Uint8Array
|
|
180
|
+
* @param options - 编解码选项
|
|
181
|
+
* @returns Uint8Array 混淆后的二进制数据
|
|
168
182
|
*/
|
|
169
|
-
declare function
|
|
183
|
+
declare function encodeSecure<T>(data: T, options?: ProtobufCodecOptions): Promise<Uint8Array>;
|
|
170
184
|
/**
|
|
171
|
-
*
|
|
185
|
+
* 解码安全载荷
|
|
172
186
|
*
|
|
173
187
|
* @param buffer - 二进制数据
|
|
188
|
+
* @param options - 编解码选项
|
|
174
189
|
* @returns 解码后的原始数据
|
|
175
190
|
*/
|
|
176
|
-
declare function
|
|
191
|
+
declare function decodeSecure<T>(buffer: Uint8Array | ArrayBuffer, options?: ProtobufCodecOptions): Promise<T>;
|
|
177
192
|
/**
|
|
178
193
|
* 创建 Protobuf 二进制响应
|
|
179
194
|
*
|
|
@@ -181,9 +196,9 @@ declare function decodeProtobuf<T>(buffer: Uint8Array | ArrayBuffer): T;
|
|
|
181
196
|
* @param options - 编码选项和 CORS headers
|
|
182
197
|
* @returns Response 对象
|
|
183
198
|
*/
|
|
184
|
-
declare function
|
|
199
|
+
declare function secureResponse<T>(data: T, options?: ProtobufCodecOptions & {
|
|
185
200
|
corsHeaders?: Record<string, string>;
|
|
186
|
-
}): Response
|
|
201
|
+
}): Promise<Response>;
|
|
187
202
|
/**
|
|
188
203
|
* 检查 Content-Type 是否是 Protobuf 格式
|
|
189
204
|
*/
|
|
@@ -200,7 +215,7 @@ declare function normalizeProtobufConfig(config: boolean | ProtobufConfig | unde
|
|
|
200
215
|
* @param customDecode - 可选的自定义解码函数
|
|
201
216
|
* @returns 解码后的数据
|
|
202
217
|
*/
|
|
203
|
-
declare function
|
|
218
|
+
declare function parseSecureRequest<T>(request: Request, customDecode?: (buffer: Uint8Array) => T | Promise<T>): Promise<T>;
|
|
204
219
|
/**
|
|
205
220
|
* Protobuf 钩子选项
|
|
206
221
|
*/
|
|
@@ -248,4 +263,4 @@ declare function createProtobufHooks(options?: CreateProtobufHooksOptions): {
|
|
|
248
263
|
onResponse(context: FetchContext): Promise<void>;
|
|
249
264
|
};
|
|
250
265
|
|
|
251
|
-
export { type ApiError, type ApiResult, type ChainRequestRule, type CreateApiClientOptions, type CreateProtobufHooksOptions, type HttpRequestSpec, type NetworkAdapter, type NetworkHandler, type ProtobufCodecOptions, type ProtobufConfig, type TokenStorage, createApiClient, createProtobufHooks,
|
|
266
|
+
export { type ApiError, type ApiResult, type ChainRequestRule, type CreateApiClientOptions, type CreateProtobufHooksOptions, type HttpRequestSpec, type NetworkAdapter, type NetworkHandler, type ProtobufCodecOptions, type ProtobufConfig, type TokenStorage, createApiClient, createProtobufHooks, decodeSecure, encodeSecure, executeRequestChain, isApiError, isProtobufContentType, normalizeProtobufConfig, parseSecureRequest, secureResponse };
|
package/dist/index.d.ts
CHANGED
|
@@ -146,10 +146,24 @@ declare function executeRequestChain<T>(requests: ChainRequestRule[], handlers?:
|
|
|
146
146
|
*/
|
|
147
147
|
|
|
148
148
|
interface ProtobufCodecOptions {
|
|
149
|
-
/**
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
|
|
149
|
+
/** 是否启用混淆 (默认 true) */
|
|
150
|
+
obfuscate?: boolean;
|
|
151
|
+
/**
|
|
152
|
+
* 混淆密钥
|
|
153
|
+
* 可以是静态字符串/字节数组,也可以是动态获取密钥的同步或异步函数
|
|
154
|
+
*/
|
|
155
|
+
obfuscationKey?: string | Uint8Array | (() => string | Uint8Array | Promise<string | Uint8Array>);
|
|
156
|
+
/** 自定义 Schema (proto3 syntax) */
|
|
157
|
+
schema?: string;
|
|
158
|
+
/** 自定义消息类型名称 (默认 "SecurePayload") */
|
|
159
|
+
messageName?: string;
|
|
160
|
+
/** 自定义数据转换逻辑 (用于非默认 Schema) */
|
|
161
|
+
transform?: {
|
|
162
|
+
/** 编码前转换:将原始数据转为符合 Schema 的对象 */
|
|
163
|
+
beforeEncode?: (data: any) => any;
|
|
164
|
+
/** 解码后转换:将 Schema 对象转回原始数据 */
|
|
165
|
+
afterDecode?: (payload: any) => any;
|
|
166
|
+
};
|
|
153
167
|
}
|
|
154
168
|
interface ProtobufConfig {
|
|
155
169
|
/** 自定义编码函数 */
|
|
@@ -160,20 +174,21 @@ interface ProtobufConfig {
|
|
|
160
174
|
options?: ProtobufCodecOptions;
|
|
161
175
|
}
|
|
162
176
|
/**
|
|
163
|
-
*
|
|
177
|
+
* 编码安全载荷
|
|
164
178
|
*
|
|
165
179
|
* @param data - 任意 JSON 可序列化数据
|
|
166
|
-
* @param options -
|
|
167
|
-
* @returns Uint8Array
|
|
180
|
+
* @param options - 编解码选项
|
|
181
|
+
* @returns Uint8Array 混淆后的二进制数据
|
|
168
182
|
*/
|
|
169
|
-
declare function
|
|
183
|
+
declare function encodeSecure<T>(data: T, options?: ProtobufCodecOptions): Promise<Uint8Array>;
|
|
170
184
|
/**
|
|
171
|
-
*
|
|
185
|
+
* 解码安全载荷
|
|
172
186
|
*
|
|
173
187
|
* @param buffer - 二进制数据
|
|
188
|
+
* @param options - 编解码选项
|
|
174
189
|
* @returns 解码后的原始数据
|
|
175
190
|
*/
|
|
176
|
-
declare function
|
|
191
|
+
declare function decodeSecure<T>(buffer: Uint8Array | ArrayBuffer, options?: ProtobufCodecOptions): Promise<T>;
|
|
177
192
|
/**
|
|
178
193
|
* 创建 Protobuf 二进制响应
|
|
179
194
|
*
|
|
@@ -181,9 +196,9 @@ declare function decodeProtobuf<T>(buffer: Uint8Array | ArrayBuffer): T;
|
|
|
181
196
|
* @param options - 编码选项和 CORS headers
|
|
182
197
|
* @returns Response 对象
|
|
183
198
|
*/
|
|
184
|
-
declare function
|
|
199
|
+
declare function secureResponse<T>(data: T, options?: ProtobufCodecOptions & {
|
|
185
200
|
corsHeaders?: Record<string, string>;
|
|
186
|
-
}): Response
|
|
201
|
+
}): Promise<Response>;
|
|
187
202
|
/**
|
|
188
203
|
* 检查 Content-Type 是否是 Protobuf 格式
|
|
189
204
|
*/
|
|
@@ -200,7 +215,7 @@ declare function normalizeProtobufConfig(config: boolean | ProtobufConfig | unde
|
|
|
200
215
|
* @param customDecode - 可选的自定义解码函数
|
|
201
216
|
* @returns 解码后的数据
|
|
202
217
|
*/
|
|
203
|
-
declare function
|
|
218
|
+
declare function parseSecureRequest<T>(request: Request, customDecode?: (buffer: Uint8Array) => T | Promise<T>): Promise<T>;
|
|
204
219
|
/**
|
|
205
220
|
* Protobuf 钩子选项
|
|
206
221
|
*/
|
|
@@ -248,4 +263,4 @@ declare function createProtobufHooks(options?: CreateProtobufHooksOptions): {
|
|
|
248
263
|
onResponse(context: FetchContext): Promise<void>;
|
|
249
264
|
};
|
|
250
265
|
|
|
251
|
-
export { type ApiError, type ApiResult, type ChainRequestRule, type CreateApiClientOptions, type CreateProtobufHooksOptions, type HttpRequestSpec, type NetworkAdapter, type NetworkHandler, type ProtobufCodecOptions, type ProtobufConfig, type TokenStorage, createApiClient, createProtobufHooks,
|
|
266
|
+
export { type ApiError, type ApiResult, type ChainRequestRule, type CreateApiClientOptions, type CreateProtobufHooksOptions, type HttpRequestSpec, type NetworkAdapter, type NetworkHandler, type ProtobufCodecOptions, type ProtobufConfig, type TokenStorage, createApiClient, createProtobufHooks, decodeSecure, encodeSecure, executeRequestChain, isApiError, isProtobufContentType, normalizeProtobufConfig, parseSecureRequest, secureResponse };
|
package/dist/index.js
CHANGED
|
@@ -32,14 +32,14 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
createApiClient: () => createApiClient,
|
|
34
34
|
createProtobufHooks: () => createProtobufHooks,
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
decodeSecure: () => decodeSecure,
|
|
36
|
+
encodeSecure: () => encodeSecure,
|
|
37
37
|
executeRequestChain: () => executeRequestChain,
|
|
38
38
|
isApiError: () => isApiError,
|
|
39
39
|
isProtobufContentType: () => isProtobufContentType,
|
|
40
40
|
normalizeProtobufConfig: () => normalizeProtobufConfig,
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
parseSecureRequest: () => parseSecureRequest,
|
|
42
|
+
secureResponse: () => secureResponse
|
|
43
43
|
});
|
|
44
44
|
module.exports = __toCommonJS(index_exports);
|
|
45
45
|
|
|
@@ -360,53 +360,96 @@ async function executeRequestChain(requests, handlers = [], getChainRequests, in
|
|
|
360
360
|
|
|
361
361
|
// src/protobuf.ts
|
|
362
362
|
var import_protobufjs = __toESM(require("protobufjs"));
|
|
363
|
-
var
|
|
363
|
+
var SECURE_SCHEMA = `
|
|
364
364
|
syntax = "proto3";
|
|
365
365
|
|
|
366
|
-
message
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
bytes data = 3;
|
|
366
|
+
message SecurePayload {
|
|
367
|
+
int64 ts = 1;
|
|
368
|
+
bytes data = 2;
|
|
370
369
|
}
|
|
371
370
|
`;
|
|
372
371
|
var encoder = new TextEncoder();
|
|
373
372
|
var decoder = new TextDecoder();
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
if (
|
|
377
|
-
|
|
378
|
-
|
|
373
|
+
function xorTransform(data, key) {
|
|
374
|
+
const keyBytes = typeof key === "string" ? encoder.encode(key) : key;
|
|
375
|
+
if (keyBytes.length === 0) return data;
|
|
376
|
+
const result = new Uint8Array(data.length);
|
|
377
|
+
for (let i = 0; i < data.length; i++) {
|
|
378
|
+
result[i] = data[i] ^ keyBytes[i % keyBytes.length];
|
|
379
379
|
}
|
|
380
|
-
return
|
|
380
|
+
return result;
|
|
381
|
+
}
|
|
382
|
+
var typeCache = /* @__PURE__ */ new Map();
|
|
383
|
+
function getProtobufType(schema, messageName = "SecurePayload") {
|
|
384
|
+
const cacheKey = `${schema}:${messageName}`;
|
|
385
|
+
let type = typeCache.get(cacheKey);
|
|
386
|
+
if (!type) {
|
|
387
|
+
const root = import_protobufjs.default.parse(schema).root;
|
|
388
|
+
type = root.lookupType(messageName);
|
|
389
|
+
typeCache.set(cacheKey, type);
|
|
390
|
+
}
|
|
391
|
+
return type;
|
|
392
|
+
}
|
|
393
|
+
function getSecurePayloadType() {
|
|
394
|
+
return getProtobufType(SECURE_SCHEMA, "SecurePayload");
|
|
381
395
|
}
|
|
382
|
-
function
|
|
383
|
-
|
|
384
|
-
|
|
396
|
+
async function getObfuscationKey(keyOption) {
|
|
397
|
+
const defaultKey = "api-client-default-key";
|
|
398
|
+
if (!keyOption) return defaultKey;
|
|
399
|
+
if (typeof keyOption === "function") {
|
|
400
|
+
return await keyOption();
|
|
385
401
|
}
|
|
386
|
-
return
|
|
402
|
+
return keyOption;
|
|
387
403
|
}
|
|
388
|
-
function
|
|
389
|
-
const {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
404
|
+
async function encodeSecure(data, options = {}) {
|
|
405
|
+
const {
|
|
406
|
+
obfuscate = true,
|
|
407
|
+
obfuscationKey,
|
|
408
|
+
schema,
|
|
409
|
+
messageName,
|
|
410
|
+
transform
|
|
411
|
+
} = options;
|
|
412
|
+
const type = schema ? getProtobufType(schema, messageName) : getSecurePayloadType();
|
|
413
|
+
const payload = schema && transform?.beforeEncode ? transform.beforeEncode(data) : {
|
|
414
|
+
ts: Date.now(),
|
|
415
|
+
data: data instanceof Uint8Array ? data : encoder.encode(JSON.stringify(data))
|
|
397
416
|
};
|
|
398
417
|
const message = type.create(payload);
|
|
399
|
-
|
|
418
|
+
const buffer = type.encode(message).finish();
|
|
419
|
+
if (!obfuscate) return buffer;
|
|
420
|
+
const finalKey = await getObfuscationKey(obfuscationKey);
|
|
421
|
+
return xorTransform(buffer, finalKey);
|
|
400
422
|
}
|
|
401
|
-
function
|
|
402
|
-
const
|
|
403
|
-
|
|
423
|
+
async function decodeSecure(buffer, options = {}) {
|
|
424
|
+
const {
|
|
425
|
+
obfuscate = true,
|
|
426
|
+
obfuscationKey,
|
|
427
|
+
schema,
|
|
428
|
+
messageName,
|
|
429
|
+
transform
|
|
430
|
+
} = options;
|
|
431
|
+
const type = schema ? getProtobufType(schema, messageName) : getSecurePayloadType();
|
|
432
|
+
let uint8 = buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : buffer;
|
|
433
|
+
if (uint8.length > 0 && obfuscate) {
|
|
434
|
+
const finalKey = await getObfuscationKey(obfuscationKey);
|
|
435
|
+
uint8 = xorTransform(uint8, finalKey);
|
|
436
|
+
}
|
|
404
437
|
const decoded = type.decode(uint8);
|
|
405
|
-
|
|
406
|
-
|
|
438
|
+
if (schema && transform?.afterDecode) {
|
|
439
|
+
return transform.afterDecode(decoded);
|
|
440
|
+
}
|
|
441
|
+
if (!schema && decoded.data) {
|
|
442
|
+
const jsonString = decoder.decode(decoded.data);
|
|
443
|
+
try {
|
|
444
|
+
return JSON.parse(jsonString);
|
|
445
|
+
} catch {
|
|
446
|
+
return jsonString;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return decoded;
|
|
407
450
|
}
|
|
408
|
-
function
|
|
409
|
-
const buffer =
|
|
451
|
+
async function secureResponse(data, options) {
|
|
452
|
+
const buffer = await encodeSecure(data, options);
|
|
410
453
|
const arrayBuffer = buffer.buffer.slice(
|
|
411
454
|
buffer.byteOffset,
|
|
412
455
|
buffer.byteOffset + buffer.byteLength
|
|
@@ -427,26 +470,26 @@ function normalizeProtobufConfig(config) {
|
|
|
427
470
|
}
|
|
428
471
|
if (config === true) {
|
|
429
472
|
return {
|
|
430
|
-
encode: (data) =>
|
|
431
|
-
decode: (buffer) =>
|
|
473
|
+
encode: (data) => encodeSecure(data),
|
|
474
|
+
decode: (buffer) => decodeSecure(buffer)
|
|
432
475
|
};
|
|
433
476
|
}
|
|
434
477
|
return {
|
|
435
|
-
encode: config.encode || ((data) =>
|
|
436
|
-
decode: config.decode || ((buffer) =>
|
|
478
|
+
encode: config.encode || ((data) => encodeSecure(data, config.options)),
|
|
479
|
+
decode: config.decode || ((buffer) => decodeSecure(buffer, config.options))
|
|
437
480
|
};
|
|
438
481
|
}
|
|
439
|
-
async function
|
|
482
|
+
async function parseSecureRequest(request, customDecode) {
|
|
440
483
|
const buffer = await request.arrayBuffer();
|
|
441
484
|
const uint8 = new Uint8Array(buffer);
|
|
442
485
|
if (customDecode) {
|
|
443
486
|
return customDecode(uint8);
|
|
444
487
|
}
|
|
445
|
-
return
|
|
488
|
+
return decodeSecure(uint8);
|
|
446
489
|
}
|
|
447
490
|
function createProtobufHooks(options = {}) {
|
|
448
|
-
const encode = options.encode || ((data) =>
|
|
449
|
-
const decode = options.decode || ((buffer) =>
|
|
491
|
+
const encode = options.encode || ((data) => encodeSecure(data, options));
|
|
492
|
+
const decode = options.decode || ((buffer) => decodeSecure(buffer, options));
|
|
450
493
|
return {
|
|
451
494
|
/**
|
|
452
495
|
* 请求钩子:独立处理请求编码和响应类型设置
|
|
@@ -496,13 +539,13 @@ function createProtobufHooks(options = {}) {
|
|
|
496
539
|
0 && (module.exports = {
|
|
497
540
|
createApiClient,
|
|
498
541
|
createProtobufHooks,
|
|
499
|
-
|
|
500
|
-
|
|
542
|
+
decodeSecure,
|
|
543
|
+
encodeSecure,
|
|
501
544
|
executeRequestChain,
|
|
502
545
|
isApiError,
|
|
503
546
|
isProtobufContentType,
|
|
504
547
|
normalizeProtobufConfig,
|
|
505
|
-
|
|
506
|
-
|
|
548
|
+
parseSecureRequest,
|
|
549
|
+
secureResponse
|
|
507
550
|
});
|
|
508
551
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/chain.ts","../src/protobuf.ts"],"sourcesContent":["export * from \"./client\";\nexport * from \"./chain\";\nexport * from \"./protobuf\";\n","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","/**\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 * 如果为 true,请求 Body 将自动合并当前 Context 和 Payload\n */\n includeContext?: boolean;\n\n /**\n * 上下文筛选列表 (优先级高于 includeContext)\n * 指定需要合并到 Body 中的 Context Key 列表\n * e.g., [\"rawProfile\", \"rawCompany\"]\n */\n pickContext?: string[];\n\n /** 额外 Payload (仅当 includeContext 为 true 或 pickContext 存在时合并) */\n payload?: Record<string, any>;\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 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 Data (用于 Submit)\n let contextData: Record<string, any> = {};\n if (rule.pickContext) {\n rule.pickContext.forEach((key) => {\n if (key in context) contextData[key] = context[key];\n });\n } else if (rule.includeContext) {\n contextData = context;\n }\n const hasContextData = Object.keys(contextData).length > 0;\n\n // 2. 准备 Request Spec (合并 Payload 和 Context)\n let requestSpec = { ...rule.request };\n if (hasContextData || rule.payload) {\n requestSpec.body = {\n ...(rule.payload || {}),\n ...contextData,\n ...(requestSpec.body || {}),\n };\n }\n\n // 3. 变量替换\n requestSpec = substituteVariables(requestSpec, context);\n\n // 4. 执行请求 (传入 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","/**\n * Protobuf 通用编解码模块\n *\n * 特性:\n * - 支持加盐混淆(每次编码结果不同)\n * - 直接输出二进制格式\n * - 内置通用容器 schema(任意 JSON 数据)\n * - 支持自定义编解码函数\n */\n\nimport protobuf from \"protobufjs\";\nimport type { FetchContext } from \"ofetch\";\n\n// ============================================================================\n// Schema 定义\n// ============================================================================\n\n/**\n * 内置的加盐消息 Schema\n * 支持任意 JSON 数据的安全传输\n */\nconst SALTED_SCHEMA = `\nsyntax = \"proto3\";\n\nmessage SaltedPayload {\n string salt = 1;\n int64 timestamp = 2;\n bytes data = 3;\n}\n`;\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n// ============================================================================\n// 类型定义\n// ============================================================================\n\nexport interface ProtobufCodecOptions {\n /** 是否启用加盐 (默认 true) */\n useSalt?: boolean;\n /** 自定义盐值生成器 */\n saltGenerator?: () => string;\n}\n\nexport interface ProtobufConfig {\n /** 自定义编码函数 */\n encode?: (data: unknown) => Uint8Array | Promise<Uint8Array>;\n /** 自定义解码函数 */\n decode?: <T>(buffer: Uint8Array) => T | Promise<T>;\n /** 编码选项(仅内置编解码器使用) */\n options?: ProtobufCodecOptions;\n}\n\ninterface SaltedPayload {\n salt: string;\n timestamp: number;\n data: Uint8Array;\n}\n\n// ============================================================================\n// Schema 缓存\n// ============================================================================\n\nlet saltedPayloadType: protobuf.Type | null = null;\n\nfunction getSaltedPayloadType(): protobuf.Type {\n if (!saltedPayloadType) {\n const root = protobuf.parse(SALTED_SCHEMA).root;\n saltedPayloadType = root.lookupType(\"SaltedPayload\");\n }\n return saltedPayloadType;\n}\n\n// ============================================================================\n// 工具函数\n// ============================================================================\n\n/**\n * 生成随机盐值\n */\nfunction generateSalt(): string {\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for older environments\n return Math.random().toString(36).substring(2) + Date.now().toString(36);\n}\n\n// ============================================================================\n// 内置编解码器\n// ============================================================================\n\n/**\n * 使用内置 SaltedPayload schema 编码数据\n *\n * @param data - 任意 JSON 可序列化数据\n * @param options - 编码选项\n * @returns Uint8Array 二进制数据\n */\nexport function encodeProtobuf<T>(\n data: T,\n options: ProtobufCodecOptions = {},\n): Uint8Array {\n const { useSalt = true, saltGenerator = generateSalt } = options;\n\n const type = getSaltedPayloadType();\n const jsonString = JSON.stringify(data);\n const dataBytes = encoder.encode(jsonString);\n\n const payload: SaltedPayload = {\n salt: useSalt ? saltGenerator() : \"\",\n timestamp: Date.now(),\n data: dataBytes,\n };\n\n const message = type.create(payload);\n return type.encode(message).finish();\n}\n\n/**\n * 使用内置 SaltedPayload schema 解码数据\n *\n * @param buffer - 二进制数据\n * @returns 解码后的原始数据\n */\nexport function decodeProtobuf<T>(buffer: Uint8Array | ArrayBuffer): T {\n const type = getSaltedPayloadType();\n const uint8 = buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : buffer;\n const decoded = type.decode(uint8) as unknown as SaltedPayload;\n const jsonString = decoder.decode(decoded.data);\n return JSON.parse(jsonString) as T;\n}\n\n// ============================================================================\n// HTTP Response 工具 (Worker/Server 端使用)\n// ============================================================================\n\n/**\n * 创建 Protobuf 二进制响应\n *\n * @param data - 要编码的数据\n * @param options - 编码选项和 CORS headers\n * @returns Response 对象\n */\nexport function protobufResponse<T>(\n data: T,\n options?: ProtobufCodecOptions & { corsHeaders?: Record<string, string> },\n): Response {\n const buffer = encodeProtobuf(data, options);\n // 使用类型断言确保兼容 Response 构造函数\n const arrayBuffer = buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n ) as ArrayBuffer;\n return new Response(arrayBuffer, {\n headers: {\n \"Content-Type\": \"application/x-protobuf\",\n ...options?.corsHeaders,\n },\n });\n}\n\n/**\n * 检查 Content-Type 是否是 Protobuf 格式\n */\nexport function isProtobufContentType(contentType: string | null): boolean {\n return !!contentType?.includes(\"application/x-protobuf\");\n}\n\n// ============================================================================\n// 规范化配置\n// ============================================================================\n\n/**\n * 规范化 protobuf 配置\n * 将 boolean | ProtobufConfig 统一为 ProtobufConfig 或 null\n */\nexport function normalizeProtobufConfig(\n config: boolean | ProtobufConfig | undefined,\n): ProtobufConfig | null {\n if (!config) {\n return null;\n }\n\n if (config === true) {\n return {\n encode: (data) => encodeProtobuf(data),\n decode: <T>(buffer: Uint8Array) => decodeProtobuf<T>(buffer),\n };\n }\n\n return {\n encode: config.encode || ((data) => encodeProtobuf(data, config.options)),\n decode:\n config.decode || (<T>(buffer: Uint8Array) => decodeProtobuf<T>(buffer)),\n };\n}\n\n// ============================================================================\n// 请求体处理\n// ============================================================================\n\n/**\n * 解析 Protobuf 请求体 (Worker/Server 端使用)\n *\n * @param request - Request 对象\n * @param customDecode - 可选的自定义解码函数\n * @returns 解码后的数据\n */\nexport async function parseProtobufRequest<T>(\n request: Request,\n customDecode?: (buffer: Uint8Array) => T | Promise<T>,\n): Promise<T> {\n const buffer = await request.arrayBuffer();\n const uint8 = new Uint8Array(buffer);\n\n if (customDecode) {\n return customDecode(uint8);\n }\n\n return decodeProtobuf<T>(uint8);\n}\n\n// ============================================================================\n// API Client 钩子\n// ============================================================================\n\n/**\n * Protobuf 钩子选项\n */\nexport interface CreateProtobufHooksOptions extends ProtobufCodecOptions {\n /** 自定义编码函数 */\n encode?: (data: unknown) => Uint8Array | Promise<Uint8Array>;\n /** 自定义解码函数 */\n decode?: <T>(buffer: Uint8Array) => T | Promise<T>;\n}\n\n/**\n * 创建 Protobuf 编解码钩子\n * 用于与 createApiClient 配合使用\n *\n * 钩子会自动:\n * - 设置 responseType 为 arrayBuffer\n * - 编码请求体为 Protobuf 格式\n * - 根据响应 Content-Type 自动解码(protobuf 或 JSON)\n *\n * @example\n * ```ts\n * import { createApiClient } from '@i.un/api-client';\n * import { createProtobufHooks } from '@i.un/api-client/protobuf';\n *\n * const { onRequest, onResponse } = createProtobufHooks();\n *\n * const client = createApiClient({\n * baseURL: 'https://api.example.com',\n * tokenStorage,\n * onRequest,\n * onResponse,\n * });\n *\n * // 自动处理,无需手动指定 responseType\n * const data = await client.post('/api/data', body);\n * ```\n */\nexport function createProtobufHooks(options: CreateProtobufHooksOptions = {}) {\n const encode =\n options.encode || ((data: unknown) => encodeProtobuf(data, options));\n const decode =\n options.decode || (<T>(buffer: Uint8Array) => decodeProtobuf<T>(buffer));\n\n return {\n /**\n * 请求钩子:独立处理请求编码和响应类型设置\n */\n async onRequest(context: FetchContext) {\n const { options: reqOptions } = context;\n\n const headers =\n reqOptions.headers instanceof Headers\n ? reqOptions.headers\n : new Headers(reqOptions.headers as HeadersInit | undefined);\n\n // 1. 处理请求体编码 (Content-Type)\n const contentType = headers.get(\"Content-Type\");\n const isProtobufRequest = isProtobufContentType(contentType);\n\n if (\n isProtobufRequest &&\n reqOptions.body &&\n !(reqOptions.body instanceof Uint8Array)\n ) {\n const encoded = await encode(reqOptions.body);\n reqOptions.body = encoded;\n }\n\n // 2. 处理响应期望类型 (Accept)\n // 如果 Accept 包含 protobuf,或者用户显式要求 arrayBuffer,则设置 responseType\n const accept = headers.get(\"Accept\");\n const prefersProtobufResponse = isProtobufContentType(accept);\n\n if (prefersProtobufResponse && !reqOptions.responseType) {\n reqOptions.responseType = \"arrayBuffer\";\n }\n\n reqOptions.headers = headers;\n },\n\n /**\n * 响应钩子:解码 Protobuf 响应\n */\n async onResponse(context: FetchContext) {\n const { response } = context;\n // 跳过空响应 (如 204 No Content)\n if (!response?._data) {\n return;\n }\n\n const contentType = response.headers.get(\"Content-Type\");\n\n if (isProtobufContentType(contentType)) {\n const buffer = response._data as ArrayBuffer;\n if (buffer && buffer.byteLength > 0) {\n response._data = await decode(new Uint8Array(buffer));\n }\n } else if (response._data instanceof ArrayBuffer) {\n // 如果响应不是 protobuf(比如普通 JSON),需要手动解析\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"],"mappings":"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","names":["options","protobuf"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/chain.ts","../src/protobuf.ts"],"sourcesContent":["export * from \"./client\";\nexport * from \"./chain\";\nexport * from \"./protobuf\";\n","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","/**\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 * 如果为 true,请求 Body 将自动合并当前 Context 和 Payload\n */\n includeContext?: boolean;\n\n /**\n * 上下文筛选列表 (优先级高于 includeContext)\n * 指定需要合并到 Body 中的 Context Key 列表\n * e.g., [\"rawProfile\", \"rawCompany\"]\n */\n pickContext?: string[];\n\n /** 额外 Payload (仅当 includeContext 为 true 或 pickContext 存在时合并) */\n payload?: Record<string, any>;\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 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 Data (用于 Submit)\n let contextData: Record<string, any> = {};\n if (rule.pickContext) {\n rule.pickContext.forEach((key) => {\n if (key in context) contextData[key] = context[key];\n });\n } else if (rule.includeContext) {\n contextData = context;\n }\n const hasContextData = Object.keys(contextData).length > 0;\n\n // 2. 准备 Request Spec (合并 Payload 和 Context)\n let requestSpec = { ...rule.request };\n if (hasContextData || rule.payload) {\n requestSpec.body = {\n ...(rule.payload || {}),\n ...contextData,\n ...(requestSpec.body || {}),\n };\n }\n\n // 3. 变量替换\n requestSpec = substituteVariables(requestSpec, context);\n\n // 4. 执行请求 (传入 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","/**\n * Protobuf 通用编解码模块\n *\n * 特性:\n * - 支持加盐混淆(每次编码结果不同)\n * - 直接输出二进制格式\n * - 内置通用容器 schema(任意 JSON 数据)\n * - 支持自定义编解码函数\n */\n\nimport protobuf from \"protobufjs\";\nimport type { FetchContext } from \"ofetch\";\n\n// ============================================================================\n// Schema 定义\n// ============================================================================\n\n/**\n * 通用安全容器 Schema\n * 采用二进制混淆保护数据\n */\nconst SECURE_SCHEMA = `\nsyntax = \"proto3\";\n\nmessage SecurePayload {\n int64 ts = 1;\n bytes data = 2;\n}\n`;\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n/**\n * 简单的二进制异或混淆\n */\nfunction xorTransform(data: Uint8Array, key: string | Uint8Array): Uint8Array {\n const keyBytes = typeof key === \"string\" ? encoder.encode(key) : key;\n if (keyBytes.length === 0) return data;\n\n const result = new Uint8Array(data.length);\n for (let i = 0; i < data.length; i++) {\n result[i] = data[i] ^ keyBytes[i % keyBytes.length];\n }\n return result;\n}\n\n// ============================================================================\n// 类型定义\n// ============================================================================\n\nexport interface ProtobufCodecOptions {\n /** 是否启用混淆 (默认 true) */\n obfuscate?: boolean;\n /**\n * 混淆密钥\n * 可以是静态字符串/字节数组,也可以是动态获取密钥的同步或异步函数\n */\n obfuscationKey?:\n | string\n | Uint8Array\n | (() => string | Uint8Array | Promise<string | Uint8Array>);\n /** 自定义 Schema (proto3 syntax) */\n schema?: string;\n /** 自定义消息类型名称 (默认 \"SecurePayload\") */\n messageName?: string;\n /** 自定义数据转换逻辑 (用于非默认 Schema) */\n transform?: {\n /** 编码前转换:将原始数据转为符合 Schema 的对象 */\n beforeEncode?: (data: any) => any;\n /** 解码后转换:将 Schema 对象转回原始数据 */\n afterDecode?: (payload: any) => any;\n };\n}\n\nexport interface ProtobufConfig {\n /** 自定义编码函数 */\n encode?: (data: unknown) => Uint8Array | Promise<Uint8Array>;\n /** 自定义解码函数 */\n decode?: <T>(buffer: Uint8Array) => T | Promise<T>;\n /** 编码选项(仅内置编解码器使用) */\n options?: ProtobufCodecOptions;\n}\n\ninterface SecurePayload {\n ts: number;\n data: Uint8Array;\n}\n\n// ============================================================================\n// Schema 缓存\n// ============================================================================\n\nconst typeCache = new Map<string, protobuf.Type>();\n\nfunction getProtobufType(\n schema: string,\n messageName: string = \"SecurePayload\",\n): protobuf.Type {\n const cacheKey = `${schema}:${messageName}`;\n let type = typeCache.get(cacheKey);\n if (!type) {\n const root = protobuf.parse(schema).root;\n type = root.lookupType(messageName);\n typeCache.set(cacheKey, type);\n }\n return type;\n}\n\n// 兼容旧逻辑\nfunction getSecurePayloadType(): protobuf.Type {\n return getProtobufType(SECURE_SCHEMA, \"SecurePayload\");\n}\n\n/**\n * 获取混淆密钥\n */\nasync function getObfuscationKey(\n keyOption?: ProtobufCodecOptions[\"obfuscationKey\"],\n): Promise<string | Uint8Array> {\n const defaultKey = \"api-client-default-key\";\n if (!keyOption) return defaultKey;\n if (typeof keyOption === \"function\") {\n return await keyOption();\n }\n return keyOption;\n}\n\n// ============================================================================\n// 内置编解码器\n// ============================================================================\n\n/**\n * 编码安全载荷\n *\n * @param data - 任意 JSON 可序列化数据\n * @param options - 编解码选项\n * @returns Uint8Array 混淆后的二进制数据\n */\nexport async function encodeSecure<T>(\n data: T,\n options: ProtobufCodecOptions = {},\n): Promise<Uint8Array> {\n const {\n obfuscate = true,\n obfuscationKey,\n schema,\n messageName,\n transform,\n } = options;\n\n const type = schema\n ? getProtobufType(schema, messageName)\n : getSecurePayloadType();\n\n // 默认转换逻辑 (针对内置 SecurePayload)\n const payload =\n schema && transform?.beforeEncode\n ? transform.beforeEncode(data)\n : {\n ts: Date.now(),\n data:\n data instanceof Uint8Array ? data : encoder.encode(JSON.stringify(data)),\n };\n\n const message = type.create(payload);\n const buffer = type.encode(message).finish();\n\n if (!obfuscate) return buffer;\n\n const finalKey = await getObfuscationKey(obfuscationKey);\n return xorTransform(buffer, finalKey);\n}\n\n/**\n * 解码安全载荷\n *\n * @param buffer - 二进制数据\n * @param options - 编解码选项\n * @returns 解码后的原始数据\n */\nexport async function decodeSecure<T>(\n buffer: Uint8Array | ArrayBuffer,\n options: ProtobufCodecOptions = {},\n): Promise<T> {\n const {\n obfuscate = true,\n obfuscationKey,\n schema,\n messageName,\n transform,\n } = options;\n\n const type = schema\n ? getProtobufType(schema, messageName)\n : getSecurePayloadType();\n\n let uint8 = buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : buffer;\n\n if (uint8.length > 0 && obfuscate) {\n const finalKey = await getObfuscationKey(obfuscationKey);\n uint8 = xorTransform(uint8, finalKey);\n }\n\n const decoded = type.decode(uint8) as any;\n\n // 默认还原逻辑\n if (schema && transform?.afterDecode) {\n return transform.afterDecode(decoded);\n }\n\n // 针对内置 SecurePayload 的还原\n if (!schema && decoded.data) {\n const jsonString = decoder.decode(decoded.data);\n try {\n return JSON.parse(jsonString) as T;\n } catch {\n return jsonString as unknown as T;\n }\n }\n\n return decoded as T;\n}\n\n// ============================================================================\n// HTTP Response 工具 (Worker/Server 端使用)\n// ============================================================================\n\n/**\n * 创建 Protobuf 二进制响应\n *\n * @param data - 要编码的数据\n * @param options - 编码选项和 CORS headers\n * @returns Response 对象\n */\nexport async function secureResponse<T>(\n data: T,\n options?: ProtobufCodecOptions & { corsHeaders?: Record<string, string> },\n): Promise<Response> {\n const buffer = await encodeSecure(data, options);\n // 使用类型断言确保兼容 Response 构造函数\n const arrayBuffer = buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n ) as ArrayBuffer;\n return new Response(arrayBuffer, {\n headers: {\n \"Content-Type\": \"application/x-protobuf\",\n ...options?.corsHeaders,\n },\n });\n}\n\n/**\n * 检查 Content-Type 是否是 Protobuf 格式\n */\nexport function isProtobufContentType(contentType: string | null): boolean {\n return !!contentType?.includes(\"application/x-protobuf\");\n}\n\n// ============================================================================\n// 规范化配置\n// ============================================================================\n\n/**\n * 规范化 protobuf 配置\n * 将 boolean | ProtobufConfig 统一为 ProtobufConfig 或 null\n */\nexport function normalizeProtobufConfig(\n config: boolean | ProtobufConfig | undefined,\n): ProtobufConfig | null {\n if (!config) {\n return null;\n }\n\n if (config === true) {\n return {\n encode: (data) => encodeSecure(data),\n decode: <T>(buffer: Uint8Array) => decodeSecure<T>(buffer),\n };\n }\n\n return {\n encode: config.encode || ((data) => encodeSecure(data, config.options)),\n decode:\n config.decode || (<T>(buffer: Uint8Array) => decodeSecure<T>(buffer, config.options)),\n };\n}\n\n// ============================================================================\n// 请求体处理\n// ============================================================================\n\n/**\n * 解析 Protobuf 请求体 (Worker/Server 端使用)\n *\n * @param request - Request 对象\n * @param customDecode - 可选的自定义解码函数\n * @returns 解码后的数据\n */\nexport async function parseSecureRequest<T>(\n request: Request,\n customDecode?: (buffer: Uint8Array) => T | Promise<T>,\n): Promise<T> {\n const buffer = await request.arrayBuffer();\n const uint8 = new Uint8Array(buffer);\n\n if (customDecode) {\n return customDecode(uint8);\n }\n\n return decodeSecure<T>(uint8);\n}\n\n// ============================================================================\n// API Client 钩子\n// ============================================================================\n\n/**\n * Protobuf 钩子选项\n */\nexport interface CreateProtobufHooksOptions extends ProtobufCodecOptions {\n /** 自定义编码函数 */\n encode?: (data: unknown) => Uint8Array | Promise<Uint8Array>;\n /** 自定义解码函数 */\n decode?: <T>(buffer: Uint8Array) => T | Promise<T>;\n}\n\n/**\n * 创建 Protobuf 编解码钩子\n * 用于与 createApiClient 配合使用\n *\n * 钩子会自动:\n * - 设置 responseType 为 arrayBuffer\n * - 编码请求体为 Protobuf 格式\n * - 根据响应 Content-Type 自动解码(protobuf 或 JSON)\n *\n * @example\n * ```ts\n * import { createApiClient } from '@i.un/api-client';\n * import { createProtobufHooks } from '@i.un/api-client/protobuf';\n *\n * const { onRequest, onResponse } = createProtobufHooks();\n *\n * const client = createApiClient({\n * baseURL: 'https://api.example.com',\n * tokenStorage,\n * onRequest,\n * onResponse,\n * });\n *\n * // 自动处理,无需手动指定 responseType\n * const data = await client.post('/api/data', body);\n * ```\n */\nexport function createProtobufHooks(options: CreateProtobufHooksOptions = {}) {\n const encode =\n options.encode || ((data: unknown) => encodeSecure(data, options));\n const decode =\n options.decode ||\n (<T>(buffer: Uint8Array) => decodeSecure<T>(buffer, options));\n\n return {\n /**\n * 请求钩子:独立处理请求编码和响应类型设置\n */\n async onRequest(context: FetchContext) {\n const { options: reqOptions } = context;\n\n const headers =\n reqOptions.headers instanceof Headers\n ? reqOptions.headers\n : new Headers(reqOptions.headers as HeadersInit | undefined);\n\n // 1. 处理请求体编码 (Content-Type)\n const contentType = headers.get(\"Content-Type\");\n const isProtobufRequest = isProtobufContentType(contentType);\n\n if (\n isProtobufRequest &&\n reqOptions.body &&\n !(reqOptions.body instanceof Uint8Array)\n ) {\n const encoded = await encode(reqOptions.body);\n reqOptions.body = encoded;\n }\n\n // 2. 处理响应期望类型 (Accept)\n // 如果 Accept 包含 protobuf,或者用户显式要求 arrayBuffer,则设置 responseType\n const accept = headers.get(\"Accept\");\n const prefersProtobufResponse = isProtobufContentType(accept);\n\n if (prefersProtobufResponse && !reqOptions.responseType) {\n reqOptions.responseType = \"arrayBuffer\";\n }\n\n reqOptions.headers = headers;\n },\n\n /**\n * 响应钩子:解码 Protobuf 响应\n */\n async onResponse(context: FetchContext) {\n const { response } = context;\n // 跳过空响应 (如 204 No Content)\n if (!response?._data) {\n return;\n }\n\n const contentType = response.headers.get(\"Content-Type\");\n\n if (isProtobufContentType(contentType)) {\n const buffer = response._data as ArrayBuffer;\n if (buffer && buffer.byteLength > 0) {\n response._data = await decode(new Uint8Array(buffer));\n }\n } else if (response._data instanceof ArrayBuffer) {\n // 如果响应不是 protobuf(比如普通 JSON),需要手动解析\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"],"mappings":"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","names":["options","protobuf"]}
|
package/dist/index.mjs
CHANGED
|
@@ -317,53 +317,96 @@ async function executeRequestChain(requests, handlers = [], getChainRequests, in
|
|
|
317
317
|
|
|
318
318
|
// src/protobuf.ts
|
|
319
319
|
import protobuf from "protobufjs";
|
|
320
|
-
var
|
|
320
|
+
var SECURE_SCHEMA = `
|
|
321
321
|
syntax = "proto3";
|
|
322
322
|
|
|
323
|
-
message
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
bytes data = 3;
|
|
323
|
+
message SecurePayload {
|
|
324
|
+
int64 ts = 1;
|
|
325
|
+
bytes data = 2;
|
|
327
326
|
}
|
|
328
327
|
`;
|
|
329
328
|
var encoder = new TextEncoder();
|
|
330
329
|
var decoder = new TextDecoder();
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
if (
|
|
334
|
-
|
|
335
|
-
|
|
330
|
+
function xorTransform(data, key) {
|
|
331
|
+
const keyBytes = typeof key === "string" ? encoder.encode(key) : key;
|
|
332
|
+
if (keyBytes.length === 0) return data;
|
|
333
|
+
const result = new Uint8Array(data.length);
|
|
334
|
+
for (let i = 0; i < data.length; i++) {
|
|
335
|
+
result[i] = data[i] ^ keyBytes[i % keyBytes.length];
|
|
336
336
|
}
|
|
337
|
-
return
|
|
337
|
+
return result;
|
|
338
|
+
}
|
|
339
|
+
var typeCache = /* @__PURE__ */ new Map();
|
|
340
|
+
function getProtobufType(schema, messageName = "SecurePayload") {
|
|
341
|
+
const cacheKey = `${schema}:${messageName}`;
|
|
342
|
+
let type = typeCache.get(cacheKey);
|
|
343
|
+
if (!type) {
|
|
344
|
+
const root = protobuf.parse(schema).root;
|
|
345
|
+
type = root.lookupType(messageName);
|
|
346
|
+
typeCache.set(cacheKey, type);
|
|
347
|
+
}
|
|
348
|
+
return type;
|
|
349
|
+
}
|
|
350
|
+
function getSecurePayloadType() {
|
|
351
|
+
return getProtobufType(SECURE_SCHEMA, "SecurePayload");
|
|
338
352
|
}
|
|
339
|
-
function
|
|
340
|
-
|
|
341
|
-
|
|
353
|
+
async function getObfuscationKey(keyOption) {
|
|
354
|
+
const defaultKey = "api-client-default-key";
|
|
355
|
+
if (!keyOption) return defaultKey;
|
|
356
|
+
if (typeof keyOption === "function") {
|
|
357
|
+
return await keyOption();
|
|
342
358
|
}
|
|
343
|
-
return
|
|
359
|
+
return keyOption;
|
|
344
360
|
}
|
|
345
|
-
function
|
|
346
|
-
const {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
361
|
+
async function encodeSecure(data, options = {}) {
|
|
362
|
+
const {
|
|
363
|
+
obfuscate = true,
|
|
364
|
+
obfuscationKey,
|
|
365
|
+
schema,
|
|
366
|
+
messageName,
|
|
367
|
+
transform
|
|
368
|
+
} = options;
|
|
369
|
+
const type = schema ? getProtobufType(schema, messageName) : getSecurePayloadType();
|
|
370
|
+
const payload = schema && transform?.beforeEncode ? transform.beforeEncode(data) : {
|
|
371
|
+
ts: Date.now(),
|
|
372
|
+
data: data instanceof Uint8Array ? data : encoder.encode(JSON.stringify(data))
|
|
354
373
|
};
|
|
355
374
|
const message = type.create(payload);
|
|
356
|
-
|
|
375
|
+
const buffer = type.encode(message).finish();
|
|
376
|
+
if (!obfuscate) return buffer;
|
|
377
|
+
const finalKey = await getObfuscationKey(obfuscationKey);
|
|
378
|
+
return xorTransform(buffer, finalKey);
|
|
357
379
|
}
|
|
358
|
-
function
|
|
359
|
-
const
|
|
360
|
-
|
|
380
|
+
async function decodeSecure(buffer, options = {}) {
|
|
381
|
+
const {
|
|
382
|
+
obfuscate = true,
|
|
383
|
+
obfuscationKey,
|
|
384
|
+
schema,
|
|
385
|
+
messageName,
|
|
386
|
+
transform
|
|
387
|
+
} = options;
|
|
388
|
+
const type = schema ? getProtobufType(schema, messageName) : getSecurePayloadType();
|
|
389
|
+
let uint8 = buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : buffer;
|
|
390
|
+
if (uint8.length > 0 && obfuscate) {
|
|
391
|
+
const finalKey = await getObfuscationKey(obfuscationKey);
|
|
392
|
+
uint8 = xorTransform(uint8, finalKey);
|
|
393
|
+
}
|
|
361
394
|
const decoded = type.decode(uint8);
|
|
362
|
-
|
|
363
|
-
|
|
395
|
+
if (schema && transform?.afterDecode) {
|
|
396
|
+
return transform.afterDecode(decoded);
|
|
397
|
+
}
|
|
398
|
+
if (!schema && decoded.data) {
|
|
399
|
+
const jsonString = decoder.decode(decoded.data);
|
|
400
|
+
try {
|
|
401
|
+
return JSON.parse(jsonString);
|
|
402
|
+
} catch {
|
|
403
|
+
return jsonString;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return decoded;
|
|
364
407
|
}
|
|
365
|
-
function
|
|
366
|
-
const buffer =
|
|
408
|
+
async function secureResponse(data, options) {
|
|
409
|
+
const buffer = await encodeSecure(data, options);
|
|
367
410
|
const arrayBuffer = buffer.buffer.slice(
|
|
368
411
|
buffer.byteOffset,
|
|
369
412
|
buffer.byteOffset + buffer.byteLength
|
|
@@ -384,26 +427,26 @@ function normalizeProtobufConfig(config) {
|
|
|
384
427
|
}
|
|
385
428
|
if (config === true) {
|
|
386
429
|
return {
|
|
387
|
-
encode: (data) =>
|
|
388
|
-
decode: (buffer) =>
|
|
430
|
+
encode: (data) => encodeSecure(data),
|
|
431
|
+
decode: (buffer) => decodeSecure(buffer)
|
|
389
432
|
};
|
|
390
433
|
}
|
|
391
434
|
return {
|
|
392
|
-
encode: config.encode || ((data) =>
|
|
393
|
-
decode: config.decode || ((buffer) =>
|
|
435
|
+
encode: config.encode || ((data) => encodeSecure(data, config.options)),
|
|
436
|
+
decode: config.decode || ((buffer) => decodeSecure(buffer, config.options))
|
|
394
437
|
};
|
|
395
438
|
}
|
|
396
|
-
async function
|
|
439
|
+
async function parseSecureRequest(request, customDecode) {
|
|
397
440
|
const buffer = await request.arrayBuffer();
|
|
398
441
|
const uint8 = new Uint8Array(buffer);
|
|
399
442
|
if (customDecode) {
|
|
400
443
|
return customDecode(uint8);
|
|
401
444
|
}
|
|
402
|
-
return
|
|
445
|
+
return decodeSecure(uint8);
|
|
403
446
|
}
|
|
404
447
|
function createProtobufHooks(options = {}) {
|
|
405
|
-
const encode = options.encode || ((data) =>
|
|
406
|
-
const decode = options.decode || ((buffer) =>
|
|
448
|
+
const encode = options.encode || ((data) => encodeSecure(data, options));
|
|
449
|
+
const decode = options.decode || ((buffer) => decodeSecure(buffer, options));
|
|
407
450
|
return {
|
|
408
451
|
/**
|
|
409
452
|
* 请求钩子:独立处理请求编码和响应类型设置
|
|
@@ -452,13 +495,13 @@ function createProtobufHooks(options = {}) {
|
|
|
452
495
|
export {
|
|
453
496
|
createApiClient,
|
|
454
497
|
createProtobufHooks,
|
|
455
|
-
|
|
456
|
-
|
|
498
|
+
decodeSecure,
|
|
499
|
+
encodeSecure,
|
|
457
500
|
executeRequestChain,
|
|
458
501
|
isApiError,
|
|
459
502
|
isProtobufContentType,
|
|
460
503
|
normalizeProtobufConfig,
|
|
461
|
-
|
|
462
|
-
|
|
504
|
+
parseSecureRequest,
|
|
505
|
+
secureResponse
|
|
463
506
|
};
|
|
464
507
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/chain.ts","../src/protobuf.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","/**\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 * 如果为 true,请求 Body 将自动合并当前 Context 和 Payload\n */\n includeContext?: boolean;\n\n /**\n * 上下文筛选列表 (优先级高于 includeContext)\n * 指定需要合并到 Body 中的 Context Key 列表\n * e.g., [\"rawProfile\", \"rawCompany\"]\n */\n pickContext?: string[];\n\n /** 额外 Payload (仅当 includeContext 为 true 或 pickContext 存在时合并) */\n payload?: Record<string, any>;\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 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 Data (用于 Submit)\n let contextData: Record<string, any> = {};\n if (rule.pickContext) {\n rule.pickContext.forEach((key) => {\n if (key in context) contextData[key] = context[key];\n });\n } else if (rule.includeContext) {\n contextData = context;\n }\n const hasContextData = Object.keys(contextData).length > 0;\n\n // 2. 准备 Request Spec (合并 Payload 和 Context)\n let requestSpec = { ...rule.request };\n if (hasContextData || rule.payload) {\n requestSpec.body = {\n ...(rule.payload || {}),\n ...contextData,\n ...(requestSpec.body || {}),\n };\n }\n\n // 3. 变量替换\n requestSpec = substituteVariables(requestSpec, context);\n\n // 4. 执行请求 (传入 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","/**\n * Protobuf 通用编解码模块\n *\n * 特性:\n * - 支持加盐混淆(每次编码结果不同)\n * - 直接输出二进制格式\n * - 内置通用容器 schema(任意 JSON 数据)\n * - 支持自定义编解码函数\n */\n\nimport protobuf from \"protobufjs\";\nimport type { FetchContext } from \"ofetch\";\n\n// ============================================================================\n// Schema 定义\n// ============================================================================\n\n/**\n * 内置的加盐消息 Schema\n * 支持任意 JSON 数据的安全传输\n */\nconst SALTED_SCHEMA = `\nsyntax = \"proto3\";\n\nmessage SaltedPayload {\n string salt = 1;\n int64 timestamp = 2;\n bytes data = 3;\n}\n`;\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n// ============================================================================\n// 类型定义\n// ============================================================================\n\nexport interface ProtobufCodecOptions {\n /** 是否启用加盐 (默认 true) */\n useSalt?: boolean;\n /** 自定义盐值生成器 */\n saltGenerator?: () => string;\n}\n\nexport interface ProtobufConfig {\n /** 自定义编码函数 */\n encode?: (data: unknown) => Uint8Array | Promise<Uint8Array>;\n /** 自定义解码函数 */\n decode?: <T>(buffer: Uint8Array) => T | Promise<T>;\n /** 编码选项(仅内置编解码器使用) */\n options?: ProtobufCodecOptions;\n}\n\ninterface SaltedPayload {\n salt: string;\n timestamp: number;\n data: Uint8Array;\n}\n\n// ============================================================================\n// Schema 缓存\n// ============================================================================\n\nlet saltedPayloadType: protobuf.Type | null = null;\n\nfunction getSaltedPayloadType(): protobuf.Type {\n if (!saltedPayloadType) {\n const root = protobuf.parse(SALTED_SCHEMA).root;\n saltedPayloadType = root.lookupType(\"SaltedPayload\");\n }\n return saltedPayloadType;\n}\n\n// ============================================================================\n// 工具函数\n// ============================================================================\n\n/**\n * 生成随机盐值\n */\nfunction generateSalt(): string {\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for older environments\n return Math.random().toString(36).substring(2) + Date.now().toString(36);\n}\n\n// ============================================================================\n// 内置编解码器\n// ============================================================================\n\n/**\n * 使用内置 SaltedPayload schema 编码数据\n *\n * @param data - 任意 JSON 可序列化数据\n * @param options - 编码选项\n * @returns Uint8Array 二进制数据\n */\nexport function encodeProtobuf<T>(\n data: T,\n options: ProtobufCodecOptions = {},\n): Uint8Array {\n const { useSalt = true, saltGenerator = generateSalt } = options;\n\n const type = getSaltedPayloadType();\n const jsonString = JSON.stringify(data);\n const dataBytes = encoder.encode(jsonString);\n\n const payload: SaltedPayload = {\n salt: useSalt ? saltGenerator() : \"\",\n timestamp: Date.now(),\n data: dataBytes,\n };\n\n const message = type.create(payload);\n return type.encode(message).finish();\n}\n\n/**\n * 使用内置 SaltedPayload schema 解码数据\n *\n * @param buffer - 二进制数据\n * @returns 解码后的原始数据\n */\nexport function decodeProtobuf<T>(buffer: Uint8Array | ArrayBuffer): T {\n const type = getSaltedPayloadType();\n const uint8 = buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : buffer;\n const decoded = type.decode(uint8) as unknown as SaltedPayload;\n const jsonString = decoder.decode(decoded.data);\n return JSON.parse(jsonString) as T;\n}\n\n// ============================================================================\n// HTTP Response 工具 (Worker/Server 端使用)\n// ============================================================================\n\n/**\n * 创建 Protobuf 二进制响应\n *\n * @param data - 要编码的数据\n * @param options - 编码选项和 CORS headers\n * @returns Response 对象\n */\nexport function protobufResponse<T>(\n data: T,\n options?: ProtobufCodecOptions & { corsHeaders?: Record<string, string> },\n): Response {\n const buffer = encodeProtobuf(data, options);\n // 使用类型断言确保兼容 Response 构造函数\n const arrayBuffer = buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n ) as ArrayBuffer;\n return new Response(arrayBuffer, {\n headers: {\n \"Content-Type\": \"application/x-protobuf\",\n ...options?.corsHeaders,\n },\n });\n}\n\n/**\n * 检查 Content-Type 是否是 Protobuf 格式\n */\nexport function isProtobufContentType(contentType: string | null): boolean {\n return !!contentType?.includes(\"application/x-protobuf\");\n}\n\n// ============================================================================\n// 规范化配置\n// ============================================================================\n\n/**\n * 规范化 protobuf 配置\n * 将 boolean | ProtobufConfig 统一为 ProtobufConfig 或 null\n */\nexport function normalizeProtobufConfig(\n config: boolean | ProtobufConfig | undefined,\n): ProtobufConfig | null {\n if (!config) {\n return null;\n }\n\n if (config === true) {\n return {\n encode: (data) => encodeProtobuf(data),\n decode: <T>(buffer: Uint8Array) => decodeProtobuf<T>(buffer),\n };\n }\n\n return {\n encode: config.encode || ((data) => encodeProtobuf(data, config.options)),\n decode:\n config.decode || (<T>(buffer: Uint8Array) => decodeProtobuf<T>(buffer)),\n };\n}\n\n// ============================================================================\n// 请求体处理\n// ============================================================================\n\n/**\n * 解析 Protobuf 请求体 (Worker/Server 端使用)\n *\n * @param request - Request 对象\n * @param customDecode - 可选的自定义解码函数\n * @returns 解码后的数据\n */\nexport async function parseProtobufRequest<T>(\n request: Request,\n customDecode?: (buffer: Uint8Array) => T | Promise<T>,\n): Promise<T> {\n const buffer = await request.arrayBuffer();\n const uint8 = new Uint8Array(buffer);\n\n if (customDecode) {\n return customDecode(uint8);\n }\n\n return decodeProtobuf<T>(uint8);\n}\n\n// ============================================================================\n// API Client 钩子\n// ============================================================================\n\n/**\n * Protobuf 钩子选项\n */\nexport interface CreateProtobufHooksOptions extends ProtobufCodecOptions {\n /** 自定义编码函数 */\n encode?: (data: unknown) => Uint8Array | Promise<Uint8Array>;\n /** 自定义解码函数 */\n decode?: <T>(buffer: Uint8Array) => T | Promise<T>;\n}\n\n/**\n * 创建 Protobuf 编解码钩子\n * 用于与 createApiClient 配合使用\n *\n * 钩子会自动:\n * - 设置 responseType 为 arrayBuffer\n * - 编码请求体为 Protobuf 格式\n * - 根据响应 Content-Type 自动解码(protobuf 或 JSON)\n *\n * @example\n * ```ts\n * import { createApiClient } from '@i.un/api-client';\n * import { createProtobufHooks } from '@i.un/api-client/protobuf';\n *\n * const { onRequest, onResponse } = createProtobufHooks();\n *\n * const client = createApiClient({\n * baseURL: 'https://api.example.com',\n * tokenStorage,\n * onRequest,\n * onResponse,\n * });\n *\n * // 自动处理,无需手动指定 responseType\n * const data = await client.post('/api/data', body);\n * ```\n */\nexport function createProtobufHooks(options: CreateProtobufHooksOptions = {}) {\n const encode =\n options.encode || ((data: unknown) => encodeProtobuf(data, options));\n const decode =\n options.decode || (<T>(buffer: Uint8Array) => decodeProtobuf<T>(buffer));\n\n return {\n /**\n * 请求钩子:独立处理请求编码和响应类型设置\n */\n async onRequest(context: FetchContext) {\n const { options: reqOptions } = context;\n\n const headers =\n reqOptions.headers instanceof Headers\n ? reqOptions.headers\n : new Headers(reqOptions.headers as HeadersInit | undefined);\n\n // 1. 处理请求体编码 (Content-Type)\n const contentType = headers.get(\"Content-Type\");\n const isProtobufRequest = isProtobufContentType(contentType);\n\n if (\n isProtobufRequest &&\n reqOptions.body &&\n !(reqOptions.body instanceof Uint8Array)\n ) {\n const encoded = await encode(reqOptions.body);\n reqOptions.body = encoded;\n }\n\n // 2. 处理响应期望类型 (Accept)\n // 如果 Accept 包含 protobuf,或者用户显式要求 arrayBuffer,则设置 responseType\n const accept = headers.get(\"Accept\");\n const prefersProtobufResponse = isProtobufContentType(accept);\n\n if (prefersProtobufResponse && !reqOptions.responseType) {\n reqOptions.responseType = \"arrayBuffer\";\n }\n\n reqOptions.headers = headers;\n },\n\n /**\n * 响应钩子:解码 Protobuf 响应\n */\n async onResponse(context: FetchContext) {\n const { response } = context;\n // 跳过空响应 (如 204 No Content)\n if (!response?._data) {\n return;\n }\n\n const contentType = response.headers.get(\"Content-Type\");\n\n if (isProtobufContentType(contentType)) {\n const buffer = response._data as ArrayBuffer;\n if (buffer && buffer.byteLength > 0) {\n response._data = await decode(new Uint8Array(buffer));\n }\n } else if (response._data instanceof ArrayBuffer) {\n // 如果响应不是 protobuf(比如普通 JSON),需要手动解析\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"],"mappings":";AAAA;AAAA,EACE;AAAA,OAIK;AAeA,IAAM,aAAa,CAAC,UAAsC;AAC/D,SAAO,iBAAiB,SAAS,UAAU;AAC7C;AAkCA,IAAM,wBAAwB,CAC5B,QACA,qBAAqB,UACf;AACN,MAAI,UAAU,OAAO,WAAW,YAAY,UAAU,QAAQ;AAC5D,UAAM,OAAO;AACb,QAAI,KAAK,SAAS,GAAG;AACnB,aAAO,qBAAsB,OAAc,KAAK;AAAA,IAClD;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAM,+BAA+B,CAAC,QAAsC;AAC1E,QAAM,QAAQ,IAAI,MAAM,IAAI,WAAW,gBAAgB;AACvD,QAAM,OAAO,IAAI;AACjB,QAAM,OAAO,IAAI;AACjB,SAAO;AACT;AAEA,IAAM,qBAAqB,CAAC,SAA0B;AACpD,MAAI,OAAO,SAAS,YAAY,MAAM;AACpC,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU;AAEhB,QAAM,cACJ,QAAQ,gBAAgB,QAAQ,eAAe,QAAQ;AAEzD,MAAI,OAAO,gBAAgB,YAAY,aAAa;AAClD,WAAO;AAAA,EACT;AAEA,QAAM,IAAI;AAAA,IACR;AAAA,EACF;AACF;AAIA,IAAM,qBAAqB,oBAAI,QAAuC;AAE/D,SAAS,gBAAgB,SAAiC;AAC/D,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,cAAc,CAAC,SAAiB,SAAS;AAAA,IACzC,iBAAiB;AAAA,IACjB,wBAAwB;AAAA,EAC1B,IAAI;AAEJ,QAAM,iBAAiD,CAAC,eACpD,OACA,OAAO,iBAAiB,WACtB,YAAY;AACV,UAAM,MAAM,MAAM,OAA2B,cAAc;AAAA,MACzD;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,IACF,CAAC;AAED,QAAI,IAAI,SAAS,GAAG;AAClB,YAAM,sBAAsB,GAAyB;AAAA,IACvD;AAEA,WAAO,mBAAmB,IAAI,IAAI;AAAA,EACpC,IACA;AAEN,QAAM,aAAa,OAAO,OAAO;AAAA,IAC/B;AAAA,IACA;AAAA,IACA;AAAA,IAEA,MAAM,UAAU,SAAuB;AACrC,YAAM,EAAE,SAAS,WAAW,IAAI;AAChC,YAAM,QAAQ,MAAM,aAAa,eAAe;AAEhD,YAAM,UAAU,IAAI;AAAA,QAClB,WAAW;AAAA,MACb;AAEA,UAAI,OAAO;AACT,gBAAQ,IAAI,iBAAiB,UAAU,KAAK,EAAE;AAAA,MAChD;AAEA,iBAAW,UAAU;AAGrB,UAAI,QAAQ,WAAW;AACrB,cAAM,QAAQ,UAAU,OAAO;AAAA,MACjC;AAAA,IACF;AAAA,IACA,MAAM,WAAW,SAAS;AACxB,YAAM,EAAE,SAAS,IAAI;AAErB,UAAI,SAAS,WAAW,KAAK;AAC3B,iBAAS,QAAQ;AACjB;AAAA,MACF;AAGA,UAAI,QAAQ,YAAY;AACtB,cAAM,QAAQ,WAAW,OAAO;AAAA,MAClC;AAAA,IACF;AAAA,IAEA,MAAM,gBAAgB,SAAuB;AAE3C,YAAM,EAAE,SAAS,IAAI;AACrB,YAAM,UACH,UAAU,OAAmC,WAC9C,QAAQ,UAAU,UAAU,eAAe;AAE7C,YAAM,QAAQ,IAAI,MAAM,OAAiB;AACzC,YAAM,SAAS,UAAU;AACzB,YAAM,OAAO,UAAU;AACvB,YAAM;AAAA,IACR;AAAA,EACF,CAAC;AAED,iBAAe,QACb,KACAA,WAAiD,CAAC,GACtC;AAEZ,UAAM,EAAE,oBAAoB,QAAQ,GAAG,aAAa,IAAIA;AAGxD,UAAM,MAAO,MAAM,WAAW,KAAK,YAA4B;AAE/D,QAAI,IAAI,SAAS,GAAG;AAClB,aAAO,eAAkB,KAAK,CAAC,CAAC,kBAAkB;AAAA,IAMpD;AAEA,QAAI,YAAY,IAAI,IAAI,KAAK,CAAC,UAAU,gBAAgB;AACtD,UAAI;AACF,YAAI,oBAAoB,mBAAmB,IAAI,YAAY;AAE3D,YAAI,CAAC,mBAAmB;AACtB,8BAAoB,eAAe,EAAE,QAAQ,MAAM;AACjD,+BAAmB,OAAO,YAAY;AAAA,UACxC,CAAC;AACD,6BAAmB,IAAI,cAAc,iBAAiB;AAAA,QACxD;AAEA,cAAM,WAAW,MAAM;AAEvB,YAAI,UAAU;AACZ,gBAAM,aAAa,eAAe,QAAQ;AAAA,QAC5C;AAEA,eAAO,MAAM,QAAW,KAAK;AAAA,UAC3B,GAAIA,YAAW,CAAC;AAAA,UAChB,QAAQ;AAAA,QACV,CAAQ;AAAA,MACV,SAAS,GAAG;AACV,cAAM,aAAa,eAAe,EAAE;AACpC,cAAM,sBAAsB,GAAyB;AAAA,MACvD;AAAA,IACF;AAEA,UAAM,sBAAsB,GAAyB;AAAA,EACvD;AAEA,iBAAe,IACb,KACA,SAAgC,CAAC,GACjCA,UACA;AACA,WAAO,QAAW,KAAK;AAAA,MACrB,GAAGA;AAAA,MACH,QAAQ;AAAA,MACR,OAAO;AAAA,IACT,CAAQ;AAAA,EACV;AAEA,iBAAe,KACb,KACA,OAA6B,CAAC,GAC9BA,UACA;AACA,WAAO,QAAW,KAAK;AAAA,MACrB,GAAGA;AAAA,MACH,QAAQ;AAAA,MACR;AAAA,IACF,CAAQ;AAAA,EACV;AAEA,iBAAe,IACb,KACA,OAA6B,CAAC,GAC9BA,UACA;AACA,WAAO,QAAW,KAAK;AAAA,MACrB,GAAGA;AAAA,MACH,QAAQ;AAAA,MACR;AAAA,IACF,CAAQ;AAAA,EACV;AAEA,iBAAe,MACb,KACA,OAA6B,CAAC,GAC9BA,UACA;AACA,WAAO,QAAW,KAAK;AAAA,MACrB,GAAGA;AAAA,MACH,QAAQ;AAAA,MACR;AAAA,IACF,CAAQ;AAAA,EACV;AAEA,iBAAe,IACb,KACA,SAAgC,CAAC,GACjCA,UACA;AACA,WAAO,QAAW,KAAK;AAAA,MACrB,GAAGA;AAAA,MACH,QAAQ;AAAA,MACR,OAAO;AAAA,IACT,CAAQ;AAAA,EACV;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACtLA,eAAe,oBACb,MACA,WAA6B,CAAC,GACX;AAEnB,QAAM,SAAS,KAAK,OAAO,YAAY;AAGvC,aAAW,WAAW,UAAU;AAC9B,QAAI,QAAQ,aAAa,KAAK,KAAK,MAAM,GAAG;AAC1C,UAAI;AACF,cAAM,EAAE,QAAQ,IAAI;AACpB,cAAM,UAAU,KAAK;AAErB,gBAAQ,QAAQ;AAAA,UACd,KAAK;AACH,mBAAO,MAAM,QAAQ,IAAO,KAAK,KAAK,KAAK,MAAM,OAAO;AAAA,UAC1D,KAAK;AACH,mBAAO,MAAM,QAAQ,KAAQ,KAAK,KAAK,KAAK,MAAM,OAAO;AAAA,UAC3D,KAAK;AACH,gBAAI,CAAC,QAAQ;AACX,oBAAM,IAAI,MAAM,WAAW,QAAQ,IAAI,qBAAqB;AAC9D,mBAAO,MAAM,QAAQ,IAAO,KAAK,KAAK,KAAK,MAAM,OAAO;AAAA,UAC1D,KAAK;AACH,gBAAI,CAAC,QAAQ;AACX,oBAAM,IAAI,MAAM,WAAW,QAAQ,IAAI,wBAAwB;AACjE,mBAAO,MAAM,QAAQ,OAAU,KAAK,KAAK,KAAK,MAAM,OAAO;AAAA,UAC7D,KAAK;AACH,gBAAI,CAAC,QAAQ;AACX,oBAAM,IAAI,MAAM,WAAW,QAAQ,IAAI,uBAAuB;AAChE,mBAAO,MAAM,QAAQ,MAAS,KAAK,KAAK,KAAK,MAAM,OAAO;AAAA,UAC5D;AACE,kBAAM,IAAI,MAAM,uBAAuB,MAAM,EAAE;AAAA,QACnD;AAAA,MACF,SAAS,KAAK;AACZ,gBAAQ;AAAA,UACN,YAAY,QAAQ,QAAQ,WAAW,gBAAgB,KAAK,GAAG;AAAA,UAC/D;AAAA,QACF;AAEA,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAGA,MAAI,KAAK,IAAI,WAAW,SAAS,KAAK,KAAK,IAAI,WAAW,UAAU,GAAG;AACrE,WAAO,MAAM,qBAAwB,IAAI;AAAA,EAC3C;AAEA,UAAQ,KAAK,6BAA6B,KAAK,GAAG,EAAE;AACpD,SAAO;AACT;AAKA,eAAe,qBACb,MACmB;AACnB,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,KAAK,KAAK;AAAA,MACrC,QAAQ,KAAK;AAAA,MACb,SAAS,KAAK;AAAA,MACd,MAAM,KAAK,OAAO,KAAK,UAAU,KAAK,IAAI,IAAI;AAAA,MAC9C,aAAa;AAAA,IACf,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,cAAQ,IAAI,4BAA4B,SAAS,MAAM,IAAI,KAAK,GAAG,EAAE;AACrE,aAAO;AAAA,IACT;AAEA,WAAO,MAAM,SAAS,KAAK;AAAA,EAC7B,SAAS,KAAK;AACZ,YAAQ,IAAI,2BAA2B,GAAG;AAC1C,WAAO;AAAA,EACT;AACF;AAKA,SAAS,UAAU,KAAU,MAAmB;AAC9C,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,KAAK,MAAM,GAAG,EAAE,OAAO,CAAC,KAAK,SAAS,OAAO,IAAI,IAAI,GAAG,GAAG;AACpE;AAMA,SAAS,oBAAoB,QAAa,SAAmB;AAC3D,MAAI,OAAO,WAAW,UAAU;AAC9B,WAAO,OAAO,QAAQ,sBAAsB,CAAC,GAAG,SAAS;AACvD,YAAM,MAAM,UAAU,SAAS,IAAI;AACnC,aAAO,QAAQ,SAAY,OAAO,GAAG,IAAI;AAAA,IAC3C,CAAC;AAAA,EACH;AAEA,MAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,WAAO,OAAO,IAAI,CAAC,SAAS,oBAAoB,MAAM,OAAO,CAAC;AAAA,EAChE;AAEA,MAAI,UAAU,OAAO,WAAW,UAAU;AACxC,UAAM,SAAc,CAAC;AACrB,eAAW,OAAO,QAAQ;AACxB,aAAO,GAAG,IAAI,oBAAoB,OAAO,GAAG,GAAG,OAAO;AAAA,IACxD;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAaA,eAAsB,oBACpB,UACA,WAA6B,CAAC,GAC9B,kBAGA,aACY;AACZ,QAAM,UAA+B,eAAe,CAAC;AACrD,MAAI,aAAkB;AAEtB,aAAW,QAAQ,UAAU;AAC3B,QAAI;AAEF,UAAI,cAAmC,CAAC;AACxC,UAAI,KAAK,aAAa;AACpB,aAAK,YAAY,QAAQ,CAAC,QAAQ;AAChC,cAAI,OAAO,QAAS,aAAY,GAAG,IAAI,QAAQ,GAAG;AAAA,QACpD,CAAC;AAAA,MACH,WAAW,KAAK,gBAAgB;AAC9B,sBAAc;AAAA,MAChB;AACA,YAAM,iBAAiB,OAAO,KAAK,WAAW,EAAE,SAAS;AAGzD,UAAI,cAAc,EAAE,GAAG,KAAK,QAAQ;AACpC,UAAI,kBAAkB,KAAK,SAAS;AAClC,oBAAY,OAAO;AAAA,UACjB,GAAI,KAAK,WAAW,CAAC;AAAA,UACrB,GAAG;AAAA,UACH,GAAI,YAAY,QAAQ,CAAC;AAAA,QAC3B;AAAA,MACF;AAGA,oBAAc,oBAAoB,aAAa,OAAO;AAGtD,UAAI,UAAU,MAAM,oBAAyB,aAAa,QAAQ;AAElE,UAAI,kBAAkB;AACpB,cAAM,gBAAgB,iBAAiB,OAAO;AAC9C,YAAI,iBAAiB,cAAc,SAAS,GAAG;AAC7C,oBAAU,MAAM;AAAA,YACd;AAAA,YACA;AAAA,YACA;AAAA,YACA,KAAK,MAAM,KAAK,UAAU,OAAO,CAAC;AAAA,UACpC;AAAA,QACF;AAAA,MACF;AAEA,mBAAa;AAEb,UAAI,SAAS;AAEX,YAAI,KAAK,KAAK;AACZ,gBAAM,OAAO,KAAK,WACd,UAAU,SAAS,KAAK,QAAQ,IAChC;AACJ,kBAAQ,KAAK,GAAG,IAAI;AAAA,QACtB;AAAA,MACF,WAAW,CAAC,KAAK,UAAU;AACzB,cAAM,IAAI;AAAA,UACR,0CAA0C,KAAK,OAAO,SAAS;AAAA,QACjE;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,CAAC,KAAK,UAAU;AAClB,cAAM;AAAA,MACR;AACA,cAAQ,KAAK,qCAAqC,MAAM,GAAG;AAAA,IAC7D;AAAA,EACF;AAEA,SAAO;AACT;;;AC5TA,OAAO,cAAc;AAWrB,IAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUtB,IAAM,UAAU,IAAI,YAAY;AAChC,IAAM,UAAU,IAAI,YAAY;AAgChC,IAAI,oBAA0C;AAE9C,SAAS,uBAAsC;AAC7C,MAAI,CAAC,mBAAmB;AACtB,UAAM,OAAO,SAAS,MAAM,aAAa,EAAE;AAC3C,wBAAoB,KAAK,WAAW,eAAe;AAAA,EACrD;AACA,SAAO;AACT;AASA,SAAS,eAAuB;AAC9B,MAAI,OAAO,WAAW,eAAe,OAAO,YAAY;AACtD,WAAO,OAAO,WAAW;AAAA,EAC3B;AAEA,SAAO,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,CAAC,IAAI,KAAK,IAAI,EAAE,SAAS,EAAE;AACzE;AAaO,SAAS,eACd,MACA,UAAgC,CAAC,GACrB;AACZ,QAAM,EAAE,UAAU,MAAM,gBAAgB,aAAa,IAAI;AAEzD,QAAM,OAAO,qBAAqB;AAClC,QAAM,aAAa,KAAK,UAAU,IAAI;AACtC,QAAM,YAAY,QAAQ,OAAO,UAAU;AAE3C,QAAM,UAAyB;AAAA,IAC7B,MAAM,UAAU,cAAc,IAAI;AAAA,IAClC,WAAW,KAAK,IAAI;AAAA,IACpB,MAAM;AAAA,EACR;AAEA,QAAM,UAAU,KAAK,OAAO,OAAO;AACnC,SAAO,KAAK,OAAO,OAAO,EAAE,OAAO;AACrC;AAQO,SAAS,eAAkB,QAAqC;AACrE,QAAM,OAAO,qBAAqB;AAClC,QAAM,QAAQ,kBAAkB,cAAc,IAAI,WAAW,MAAM,IAAI;AACvE,QAAM,UAAU,KAAK,OAAO,KAAK;AACjC,QAAM,aAAa,QAAQ,OAAO,QAAQ,IAAI;AAC9C,SAAO,KAAK,MAAM,UAAU;AAC9B;AAaO,SAAS,iBACd,MACA,SACU;AACV,QAAM,SAAS,eAAe,MAAM,OAAO;AAE3C,QAAM,cAAc,OAAO,OAAO;AAAA,IAChC,OAAO;AAAA,IACP,OAAO,aAAa,OAAO;AAAA,EAC7B;AACA,SAAO,IAAI,SAAS,aAAa;AAAA,IAC/B,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,GAAG,SAAS;AAAA,IACd;AAAA,EACF,CAAC;AACH;AAKO,SAAS,sBAAsB,aAAqC;AACzE,SAAO,CAAC,CAAC,aAAa,SAAS,wBAAwB;AACzD;AAUO,SAAS,wBACd,QACuB;AACvB,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,EACT;AAEA,MAAI,WAAW,MAAM;AACnB,WAAO;AAAA,MACL,QAAQ,CAAC,SAAS,eAAe,IAAI;AAAA,MACrC,QAAQ,CAAI,WAAuB,eAAkB,MAAM;AAAA,IAC7D;AAAA,EACF;AAEA,SAAO;AAAA,IACL,QAAQ,OAAO,WAAW,CAAC,SAAS,eAAe,MAAM,OAAO,OAAO;AAAA,IACvE,QACE,OAAO,WAAW,CAAI,WAAuB,eAAkB,MAAM;AAAA,EACzE;AACF;AAaA,eAAsB,qBACpB,SACA,cACY;AACZ,QAAM,SAAS,MAAM,QAAQ,YAAY;AACzC,QAAM,QAAQ,IAAI,WAAW,MAAM;AAEnC,MAAI,cAAc;AAChB,WAAO,aAAa,KAAK;AAAA,EAC3B;AAEA,SAAO,eAAkB,KAAK;AAChC;AA2CO,SAAS,oBAAoB,UAAsC,CAAC,GAAG;AAC5E,QAAM,SACJ,QAAQ,WAAW,CAAC,SAAkB,eAAe,MAAM,OAAO;AACpE,QAAM,SACJ,QAAQ,WAAW,CAAI,WAAuB,eAAkB,MAAM;AAExE,SAAO;AAAA;AAAA;AAAA;AAAA,IAIL,MAAM,UAAU,SAAuB;AACrC,YAAM,EAAE,SAAS,WAAW,IAAI;AAEhC,YAAM,UACJ,WAAW,mBAAmB,UAC1B,WAAW,UACX,IAAI,QAAQ,WAAW,OAAkC;AAG/D,YAAM,cAAc,QAAQ,IAAI,cAAc;AAC9C,YAAM,oBAAoB,sBAAsB,WAAW;AAE3D,UACE,qBACA,WAAW,QACX,EAAE,WAAW,gBAAgB,aAC7B;AACA,cAAM,UAAU,MAAM,OAAO,WAAW,IAAI;AAC5C,mBAAW,OAAO;AAAA,MACpB;AAIA,YAAM,SAAS,QAAQ,IAAI,QAAQ;AACnC,YAAM,0BAA0B,sBAAsB,MAAM;AAE5D,UAAI,2BAA2B,CAAC,WAAW,cAAc;AACvD,mBAAW,eAAe;AAAA,MAC5B;AAEA,iBAAW,UAAU;AAAA,IACvB;AAAA;AAAA;AAAA;AAAA,IAKA,MAAM,WAAW,SAAuB;AACtC,YAAM,EAAE,SAAS,IAAI;AAErB,UAAI,CAAC,UAAU,OAAO;AACpB;AAAA,MACF;AAEA,YAAM,cAAc,SAAS,QAAQ,IAAI,cAAc;AAEvD,UAAI,sBAAsB,WAAW,GAAG;AACtC,cAAM,SAAS,SAAS;AACxB,YAAI,UAAU,OAAO,aAAa,GAAG;AACnC,mBAAS,QAAQ,MAAM,OAAO,IAAI,WAAW,MAAM,CAAC;AAAA,QACtD;AAAA,MACF,WAAW,SAAS,iBAAiB,aAAa;AAEhD,cAAM,OAAO,QAAQ,OAAO,SAAS,KAAK;AAC1C,YAAI;AACF,mBAAS,QAAQ,KAAK,MAAM,IAAI;AAAA,QAClC,QAAQ;AACN,mBAAS,QAAQ;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":["options"]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/chain.ts","../src/protobuf.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","/**\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 * 如果为 true,请求 Body 将自动合并当前 Context 和 Payload\n */\n includeContext?: boolean;\n\n /**\n * 上下文筛选列表 (优先级高于 includeContext)\n * 指定需要合并到 Body 中的 Context Key 列表\n * e.g., [\"rawProfile\", \"rawCompany\"]\n */\n pickContext?: string[];\n\n /** 额外 Payload (仅当 includeContext 为 true 或 pickContext 存在时合并) */\n payload?: Record<string, any>;\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 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 Data (用于 Submit)\n let contextData: Record<string, any> = {};\n if (rule.pickContext) {\n rule.pickContext.forEach((key) => {\n if (key in context) contextData[key] = context[key];\n });\n } else if (rule.includeContext) {\n contextData = context;\n }\n const hasContextData = Object.keys(contextData).length > 0;\n\n // 2. 准备 Request Spec (合并 Payload 和 Context)\n let requestSpec = { ...rule.request };\n if (hasContextData || rule.payload) {\n requestSpec.body = {\n ...(rule.payload || {}),\n ...contextData,\n ...(requestSpec.body || {}),\n };\n }\n\n // 3. 变量替换\n requestSpec = substituteVariables(requestSpec, context);\n\n // 4. 执行请求 (传入 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","/**\n * Protobuf 通用编解码模块\n *\n * 特性:\n * - 支持加盐混淆(每次编码结果不同)\n * - 直接输出二进制格式\n * - 内置通用容器 schema(任意 JSON 数据)\n * - 支持自定义编解码函数\n */\n\nimport protobuf from \"protobufjs\";\nimport type { FetchContext } from \"ofetch\";\n\n// ============================================================================\n// Schema 定义\n// ============================================================================\n\n/**\n * 通用安全容器 Schema\n * 采用二进制混淆保护数据\n */\nconst SECURE_SCHEMA = `\nsyntax = \"proto3\";\n\nmessage SecurePayload {\n int64 ts = 1;\n bytes data = 2;\n}\n`;\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n/**\n * 简单的二进制异或混淆\n */\nfunction xorTransform(data: Uint8Array, key: string | Uint8Array): Uint8Array {\n const keyBytes = typeof key === \"string\" ? encoder.encode(key) : key;\n if (keyBytes.length === 0) return data;\n\n const result = new Uint8Array(data.length);\n for (let i = 0; i < data.length; i++) {\n result[i] = data[i] ^ keyBytes[i % keyBytes.length];\n }\n return result;\n}\n\n// ============================================================================\n// 类型定义\n// ============================================================================\n\nexport interface ProtobufCodecOptions {\n /** 是否启用混淆 (默认 true) */\n obfuscate?: boolean;\n /**\n * 混淆密钥\n * 可以是静态字符串/字节数组,也可以是动态获取密钥的同步或异步函数\n */\n obfuscationKey?:\n | string\n | Uint8Array\n | (() => string | Uint8Array | Promise<string | Uint8Array>);\n /** 自定义 Schema (proto3 syntax) */\n schema?: string;\n /** 自定义消息类型名称 (默认 \"SecurePayload\") */\n messageName?: string;\n /** 自定义数据转换逻辑 (用于非默认 Schema) */\n transform?: {\n /** 编码前转换:将原始数据转为符合 Schema 的对象 */\n beforeEncode?: (data: any) => any;\n /** 解码后转换:将 Schema 对象转回原始数据 */\n afterDecode?: (payload: any) => any;\n };\n}\n\nexport interface ProtobufConfig {\n /** 自定义编码函数 */\n encode?: (data: unknown) => Uint8Array | Promise<Uint8Array>;\n /** 自定义解码函数 */\n decode?: <T>(buffer: Uint8Array) => T | Promise<T>;\n /** 编码选项(仅内置编解码器使用) */\n options?: ProtobufCodecOptions;\n}\n\ninterface SecurePayload {\n ts: number;\n data: Uint8Array;\n}\n\n// ============================================================================\n// Schema 缓存\n// ============================================================================\n\nconst typeCache = new Map<string, protobuf.Type>();\n\nfunction getProtobufType(\n schema: string,\n messageName: string = \"SecurePayload\",\n): protobuf.Type {\n const cacheKey = `${schema}:${messageName}`;\n let type = typeCache.get(cacheKey);\n if (!type) {\n const root = protobuf.parse(schema).root;\n type = root.lookupType(messageName);\n typeCache.set(cacheKey, type);\n }\n return type;\n}\n\n// 兼容旧逻辑\nfunction getSecurePayloadType(): protobuf.Type {\n return getProtobufType(SECURE_SCHEMA, \"SecurePayload\");\n}\n\n/**\n * 获取混淆密钥\n */\nasync function getObfuscationKey(\n keyOption?: ProtobufCodecOptions[\"obfuscationKey\"],\n): Promise<string | Uint8Array> {\n const defaultKey = \"api-client-default-key\";\n if (!keyOption) return defaultKey;\n if (typeof keyOption === \"function\") {\n return await keyOption();\n }\n return keyOption;\n}\n\n// ============================================================================\n// 内置编解码器\n// ============================================================================\n\n/**\n * 编码安全载荷\n *\n * @param data - 任意 JSON 可序列化数据\n * @param options - 编解码选项\n * @returns Uint8Array 混淆后的二进制数据\n */\nexport async function encodeSecure<T>(\n data: T,\n options: ProtobufCodecOptions = {},\n): Promise<Uint8Array> {\n const {\n obfuscate = true,\n obfuscationKey,\n schema,\n messageName,\n transform,\n } = options;\n\n const type = schema\n ? getProtobufType(schema, messageName)\n : getSecurePayloadType();\n\n // 默认转换逻辑 (针对内置 SecurePayload)\n const payload =\n schema && transform?.beforeEncode\n ? transform.beforeEncode(data)\n : {\n ts: Date.now(),\n data:\n data instanceof Uint8Array ? data : encoder.encode(JSON.stringify(data)),\n };\n\n const message = type.create(payload);\n const buffer = type.encode(message).finish();\n\n if (!obfuscate) return buffer;\n\n const finalKey = await getObfuscationKey(obfuscationKey);\n return xorTransform(buffer, finalKey);\n}\n\n/**\n * 解码安全载荷\n *\n * @param buffer - 二进制数据\n * @param options - 编解码选项\n * @returns 解码后的原始数据\n */\nexport async function decodeSecure<T>(\n buffer: Uint8Array | ArrayBuffer,\n options: ProtobufCodecOptions = {},\n): Promise<T> {\n const {\n obfuscate = true,\n obfuscationKey,\n schema,\n messageName,\n transform,\n } = options;\n\n const type = schema\n ? getProtobufType(schema, messageName)\n : getSecurePayloadType();\n\n let uint8 = buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : buffer;\n\n if (uint8.length > 0 && obfuscate) {\n const finalKey = await getObfuscationKey(obfuscationKey);\n uint8 = xorTransform(uint8, finalKey);\n }\n\n const decoded = type.decode(uint8) as any;\n\n // 默认还原逻辑\n if (schema && transform?.afterDecode) {\n return transform.afterDecode(decoded);\n }\n\n // 针对内置 SecurePayload 的还原\n if (!schema && decoded.data) {\n const jsonString = decoder.decode(decoded.data);\n try {\n return JSON.parse(jsonString) as T;\n } catch {\n return jsonString as unknown as T;\n }\n }\n\n return decoded as T;\n}\n\n// ============================================================================\n// HTTP Response 工具 (Worker/Server 端使用)\n// ============================================================================\n\n/**\n * 创建 Protobuf 二进制响应\n *\n * @param data - 要编码的数据\n * @param options - 编码选项和 CORS headers\n * @returns Response 对象\n */\nexport async function secureResponse<T>(\n data: T,\n options?: ProtobufCodecOptions & { corsHeaders?: Record<string, string> },\n): Promise<Response> {\n const buffer = await encodeSecure(data, options);\n // 使用类型断言确保兼容 Response 构造函数\n const arrayBuffer = buffer.buffer.slice(\n buffer.byteOffset,\n buffer.byteOffset + buffer.byteLength,\n ) as ArrayBuffer;\n return new Response(arrayBuffer, {\n headers: {\n \"Content-Type\": \"application/x-protobuf\",\n ...options?.corsHeaders,\n },\n });\n}\n\n/**\n * 检查 Content-Type 是否是 Protobuf 格式\n */\nexport function isProtobufContentType(contentType: string | null): boolean {\n return !!contentType?.includes(\"application/x-protobuf\");\n}\n\n// ============================================================================\n// 规范化配置\n// ============================================================================\n\n/**\n * 规范化 protobuf 配置\n * 将 boolean | ProtobufConfig 统一为 ProtobufConfig 或 null\n */\nexport function normalizeProtobufConfig(\n config: boolean | ProtobufConfig | undefined,\n): ProtobufConfig | null {\n if (!config) {\n return null;\n }\n\n if (config === true) {\n return {\n encode: (data) => encodeSecure(data),\n decode: <T>(buffer: Uint8Array) => decodeSecure<T>(buffer),\n };\n }\n\n return {\n encode: config.encode || ((data) => encodeSecure(data, config.options)),\n decode:\n config.decode || (<T>(buffer: Uint8Array) => decodeSecure<T>(buffer, config.options)),\n };\n}\n\n// ============================================================================\n// 请求体处理\n// ============================================================================\n\n/**\n * 解析 Protobuf 请求体 (Worker/Server 端使用)\n *\n * @param request - Request 对象\n * @param customDecode - 可选的自定义解码函数\n * @returns 解码后的数据\n */\nexport async function parseSecureRequest<T>(\n request: Request,\n customDecode?: (buffer: Uint8Array) => T | Promise<T>,\n): Promise<T> {\n const buffer = await request.arrayBuffer();\n const uint8 = new Uint8Array(buffer);\n\n if (customDecode) {\n return customDecode(uint8);\n }\n\n return decodeSecure<T>(uint8);\n}\n\n// ============================================================================\n// API Client 钩子\n// ============================================================================\n\n/**\n * Protobuf 钩子选项\n */\nexport interface CreateProtobufHooksOptions extends ProtobufCodecOptions {\n /** 自定义编码函数 */\n encode?: (data: unknown) => Uint8Array | Promise<Uint8Array>;\n /** 自定义解码函数 */\n decode?: <T>(buffer: Uint8Array) => T | Promise<T>;\n}\n\n/**\n * 创建 Protobuf 编解码钩子\n * 用于与 createApiClient 配合使用\n *\n * 钩子会自动:\n * - 设置 responseType 为 arrayBuffer\n * - 编码请求体为 Protobuf 格式\n * - 根据响应 Content-Type 自动解码(protobuf 或 JSON)\n *\n * @example\n * ```ts\n * import { createApiClient } from '@i.un/api-client';\n * import { createProtobufHooks } from '@i.un/api-client/protobuf';\n *\n * const { onRequest, onResponse } = createProtobufHooks();\n *\n * const client = createApiClient({\n * baseURL: 'https://api.example.com',\n * tokenStorage,\n * onRequest,\n * onResponse,\n * });\n *\n * // 自动处理,无需手动指定 responseType\n * const data = await client.post('/api/data', body);\n * ```\n */\nexport function createProtobufHooks(options: CreateProtobufHooksOptions = {}) {\n const encode =\n options.encode || ((data: unknown) => encodeSecure(data, options));\n const decode =\n options.decode ||\n (<T>(buffer: Uint8Array) => decodeSecure<T>(buffer, options));\n\n return {\n /**\n * 请求钩子:独立处理请求编码和响应类型设置\n */\n async onRequest(context: FetchContext) {\n const { options: reqOptions } = context;\n\n const headers =\n reqOptions.headers instanceof Headers\n ? reqOptions.headers\n : new Headers(reqOptions.headers as HeadersInit | undefined);\n\n // 1. 处理请求体编码 (Content-Type)\n const contentType = headers.get(\"Content-Type\");\n const isProtobufRequest = isProtobufContentType(contentType);\n\n if (\n isProtobufRequest &&\n reqOptions.body &&\n !(reqOptions.body instanceof Uint8Array)\n ) {\n const encoded = await encode(reqOptions.body);\n reqOptions.body = encoded;\n }\n\n // 2. 处理响应期望类型 (Accept)\n // 如果 Accept 包含 protobuf,或者用户显式要求 arrayBuffer,则设置 responseType\n const accept = headers.get(\"Accept\");\n const prefersProtobufResponse = isProtobufContentType(accept);\n\n if (prefersProtobufResponse && !reqOptions.responseType) {\n reqOptions.responseType = \"arrayBuffer\";\n }\n\n reqOptions.headers = headers;\n },\n\n /**\n * 响应钩子:解码 Protobuf 响应\n */\n async onResponse(context: FetchContext) {\n const { response } = context;\n // 跳过空响应 (如 204 No Content)\n if (!response?._data) {\n return;\n }\n\n const contentType = response.headers.get(\"Content-Type\");\n\n if (isProtobufContentType(contentType)) {\n const buffer = response._data as ArrayBuffer;\n if (buffer && buffer.byteLength > 0) {\n response._data = await decode(new Uint8Array(buffer));\n }\n } else if (response._data instanceof ArrayBuffer) {\n // 如果响应不是 protobuf(比如普通 JSON),需要手动解析\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"],"mappings":"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","names":["options"]}
|