@i.un/api-client 1.1.3 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -10
- package/README.zh-CN.md +62 -10
- package/dist/chain.d.mts +0 -2
- package/dist/chain.d.ts +0 -2
- package/dist/chain.js +1 -2
- package/dist/chain.js.map +1 -1
- package/dist/chain.mjs +1 -1
- package/dist/{chunk-SJA6WQ2W.mjs → chunk-5CXSMOKF.mjs} +2 -3
- package/dist/chunk-5CXSMOKF.mjs.map +1 -0
- package/dist/{chunk-YUPTRDEZ.mjs → chunk-SAIJRTYF.mjs} +15 -28
- package/dist/chunk-SAIJRTYF.mjs.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +16 -29
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4 -2
- package/dist/protobuf.d.mts +15 -7
- package/dist/protobuf.d.ts +15 -7
- package/dist/protobuf.js +15 -27
- package/dist/protobuf.js.map +1 -1
- package/dist/protobuf.mjs +3 -1
- package/package.json +1 -1
- package/dist/chunk-SJA6WQ2W.mjs.map +0 -1
- package/dist/chunk-YUPTRDEZ.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -286,7 +286,6 @@ The Request Chain engine (`executeRequestChain`) allows you to coordinate multip
|
|
|
286
286
|
| `selector` | `string` | Optional. A dot-notation path (e.g., `user.id`) to extract specific data from the response. |
|
|
287
287
|
| `includeContext` | `boolean` | If true, the entire Context is merged into the request `body`. |
|
|
288
288
|
| `pickContext` | `string[]` | Specific keys from the Context to merge into the request `body`. |
|
|
289
|
-
| `payload` | `object` | Static data to merge into the request `body`. |
|
|
290
289
|
| `optional` | `boolean` | If true, the chain continues even if this request fails. |
|
|
291
290
|
|
|
292
291
|
### Variable Substitution
|
|
@@ -317,9 +316,12 @@ const rules = [
|
|
|
317
316
|
// Step 3: Combine previous data into a new request
|
|
318
317
|
{
|
|
319
318
|
key: "update",
|
|
320
|
-
request: {
|
|
321
|
-
|
|
322
|
-
|
|
319
|
+
request: {
|
|
320
|
+
method: "PUT",
|
|
321
|
+
url: "/sync",
|
|
322
|
+
body: { source: "web-client" }, // static data directly in body
|
|
323
|
+
},
|
|
324
|
+
pickContext: ["profile"], // contextData will be merged into body
|
|
323
325
|
},
|
|
324
326
|
];
|
|
325
327
|
|
|
@@ -360,11 +362,15 @@ The Protobuf module provides a security layer over `ofetch`. It supports binary
|
|
|
360
362
|
Use `createProtobufHooks` to add Protobuf support to your `api-client` instance.
|
|
361
363
|
|
|
362
364
|
```ts
|
|
363
|
-
import {
|
|
365
|
+
import {
|
|
366
|
+
createApiClient,
|
|
367
|
+
createProtobufHooks,
|
|
368
|
+
createXorObfuscator,
|
|
369
|
+
} from "@i.un/api-client";
|
|
364
370
|
|
|
365
371
|
const hooks = createProtobufHooks({
|
|
366
|
-
// 1.
|
|
367
|
-
|
|
372
|
+
// 1. Custom Obfuscator (Optional)
|
|
373
|
+
// obfuscator: createXorObfuscator("my-secret-key"),
|
|
368
374
|
|
|
369
375
|
// 2. Custom transformations
|
|
370
376
|
transform: {
|
|
@@ -418,18 +424,64 @@ const hooks = createProtobufHooks({
|
|
|
418
424
|
});
|
|
419
425
|
```
|
|
420
426
|
|
|
427
|
+
### Custom Obfuscator
|
|
428
|
+
|
|
429
|
+
No obfuscation is enabled by default. If you need to obfuscate the binary data (e.g., to bypass certain network filters), you can provide an `obfuscator` implementation.
|
|
430
|
+
|
|
431
|
+
To use the built-in XOR obfuscator:
|
|
432
|
+
|
|
433
|
+
```ts
|
|
434
|
+
import { createProtobufHooks, createXorObfuscator } from "@i.un/api-client";
|
|
435
|
+
|
|
436
|
+
const hooks = createProtobufHooks({
|
|
437
|
+
obfuscator: createXorObfuscator("your-secret-key"),
|
|
438
|
+
});
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
Or provide a completely custom implementation:
|
|
442
|
+
|
|
443
|
+
```ts
|
|
444
|
+
import { createProtobufHooks, type ProtobufObfuscator } from "@i.un/api-client";
|
|
445
|
+
|
|
446
|
+
const myObfuscator: ProtobufObfuscator = {
|
|
447
|
+
encrypt: (data: Uint8Array) => {
|
|
448
|
+
// your encryption logic
|
|
449
|
+
return data;
|
|
450
|
+
},
|
|
451
|
+
decrypt: (data: Uint8Array) => {
|
|
452
|
+
// your decryption logic
|
|
453
|
+
return data;
|
|
454
|
+
},
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
const hooks = createProtobufHooks({
|
|
458
|
+
obfuscator: myObfuscator,
|
|
459
|
+
});
|
|
460
|
+
```
|
|
461
|
+
|
|
421
462
|
### Server-Side Usage (Node/Workers)
|
|
422
463
|
|
|
423
464
|
The library provides utilities for the server/worker side to handle these secure requests.
|
|
424
465
|
|
|
425
466
|
```ts
|
|
426
|
-
import {
|
|
467
|
+
import {
|
|
468
|
+
parseSecureRequest,
|
|
469
|
+
secureResponse,
|
|
470
|
+
createXorObfuscator,
|
|
471
|
+
} from "@i.un/api-client";
|
|
427
472
|
|
|
428
473
|
// 1. Parse incoming request
|
|
429
|
-
const body = await parseSecureRequest(request, {
|
|
474
|
+
const body = await parseSecureRequest(request, {
|
|
475
|
+
obfuscator: createXorObfuscator("key"),
|
|
476
|
+
});
|
|
430
477
|
|
|
431
478
|
// 2. Send secure response
|
|
432
|
-
return await secureResponse(
|
|
479
|
+
return await secureResponse(
|
|
480
|
+
{ success: true },
|
|
481
|
+
{
|
|
482
|
+
obfuscator: createXorObfuscator("key"),
|
|
483
|
+
},
|
|
484
|
+
);
|
|
433
485
|
```
|
|
434
486
|
|
|
435
487
|
---
|
package/README.zh-CN.md
CHANGED
|
@@ -278,7 +278,6 @@ const client = createApiClient({
|
|
|
278
278
|
| `selector` | `string` | 可选。使用点分隔的路径(如 `user.id`)从响应中提取特定数据。 |
|
|
279
279
|
| `includeContext` | `boolean` | 如果为 true,则将整个上下文合并到请求的 `body` 中。 |
|
|
280
280
|
| `pickContext` | `string[]` | 指定要合并到请求 `body` 中的上下文键列表。 |
|
|
281
|
-
| `payload` | `object` | 要合并到请求 `body` 中的静态数据。 |
|
|
282
281
|
| `optional` | `boolean` | 如果为 true,即使该请求失败,链式执行也会继续。 |
|
|
283
282
|
|
|
284
283
|
### 变量替换
|
|
@@ -309,9 +308,12 @@ const rules = [
|
|
|
309
308
|
// 步骤 3: 结合之前的步骤数据发起新请求
|
|
310
309
|
{
|
|
311
310
|
key: "update",
|
|
312
|
-
request: {
|
|
313
|
-
|
|
314
|
-
|
|
311
|
+
request: {
|
|
312
|
+
method: "PUT",
|
|
313
|
+
url: "/sync",
|
|
314
|
+
body: { source: "web-client" }, // 静态数据直接写在 body
|
|
315
|
+
},
|
|
316
|
+
pickContext: ["profile"], // contextData 将合并到 body
|
|
315
317
|
},
|
|
316
318
|
];
|
|
317
319
|
|
|
@@ -352,11 +354,15 @@ Protobuf 模块为 `ofetch` 提供了一层安全保障。它支持二进制序
|
|
|
352
354
|
由于使用了 `createProtobufHooks`,你可以轻松地为 `api-client` 实例添加 Protobuf 支持。
|
|
353
355
|
|
|
354
356
|
```ts
|
|
355
|
-
import {
|
|
357
|
+
import {
|
|
358
|
+
createApiClient,
|
|
359
|
+
createProtobufHooks,
|
|
360
|
+
createXorObfuscator,
|
|
361
|
+
} from "@i.un/api-client";
|
|
356
362
|
|
|
357
363
|
const hooks = createProtobufHooks({
|
|
358
|
-
// 1.
|
|
359
|
-
|
|
364
|
+
// 1. 自定义混淆器 (可选)
|
|
365
|
+
// obfuscator: createXorObfuscator("my-secret-key"),
|
|
360
366
|
|
|
361
367
|
// 2. 自定义数据转换
|
|
362
368
|
transform: {
|
|
@@ -410,18 +416,64 @@ const hooks = createProtobufHooks({
|
|
|
410
416
|
});
|
|
411
417
|
```
|
|
412
418
|
|
|
419
|
+
### 自定义混淆逻辑 (Obfuscator)
|
|
420
|
+
|
|
421
|
+
默认不启用任何混淆。如果需要对二进制数据进行混淆(例如为了绕过某些网络审查或简单的防篡改),你可以提供 `obfuscator` 实现。
|
|
422
|
+
|
|
423
|
+
如果你想使用库内置的 XOR 混淆,可以使用 `createXorObfuscator`:
|
|
424
|
+
|
|
425
|
+
```ts
|
|
426
|
+
import { createProtobufHooks, createXorObfuscator } from "@i.un/api-client";
|
|
427
|
+
|
|
428
|
+
const hooks = createProtobufHooks({
|
|
429
|
+
obfuscator: createXorObfuscator("your-secret-key"),
|
|
430
|
+
});
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
或者完全自定义混淆算法:
|
|
434
|
+
|
|
435
|
+
```ts
|
|
436
|
+
import { createProtobufHooks, type ProtobufObfuscator } from "@i.un/api-client";
|
|
437
|
+
|
|
438
|
+
const myObfuscator: ProtobufObfuscator = {
|
|
439
|
+
encrypt: (data: Uint8Array) => {
|
|
440
|
+
// 自定义加密逻辑,返回 Uint8Array
|
|
441
|
+
return data;
|
|
442
|
+
},
|
|
443
|
+
decrypt: (data: Uint8Array) => {
|
|
444
|
+
// 自定义解密逻辑
|
|
445
|
+
return data;
|
|
446
|
+
},
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
const hooks = createProtobufHooks({
|
|
450
|
+
obfuscator: myObfuscator,
|
|
451
|
+
});
|
|
452
|
+
```
|
|
453
|
+
|
|
413
454
|
### 服务端/边缘计算使用 (Node/Workers)
|
|
414
455
|
|
|
415
456
|
本库提供了服务端工具,用于处理这些安全请求。
|
|
416
457
|
|
|
417
458
|
```ts
|
|
418
|
-
import {
|
|
459
|
+
import {
|
|
460
|
+
parseSecureRequest,
|
|
461
|
+
secureResponse,
|
|
462
|
+
createXorObfuscator,
|
|
463
|
+
} from "@i.un/api-client";
|
|
419
464
|
|
|
420
465
|
// 1. 解析进入的加密请求
|
|
421
|
-
const body = await parseSecureRequest(request, {
|
|
466
|
+
const body = await parseSecureRequest(request, {
|
|
467
|
+
obfuscator: createXorObfuscator("key"),
|
|
468
|
+
});
|
|
422
469
|
|
|
423
470
|
// 2. 发送加密响应
|
|
424
|
-
return await secureResponse(
|
|
471
|
+
return await secureResponse(
|
|
472
|
+
{ success: true },
|
|
473
|
+
{
|
|
474
|
+
obfuscator: createXorObfuscator("key"),
|
|
475
|
+
},
|
|
476
|
+
);
|
|
425
477
|
```
|
|
426
478
|
|
|
427
479
|
---
|
package/dist/chain.d.mts
CHANGED
package/dist/chain.d.ts
CHANGED
package/dist/chain.js
CHANGED
|
@@ -121,9 +121,8 @@ async function executeRequestChain(requests, handlers = [], getChainRequests, in
|
|
|
121
121
|
}
|
|
122
122
|
const hasContextData = Object.keys(contextData).length > 0;
|
|
123
123
|
let requestSpec = { ...rule.request };
|
|
124
|
-
if (hasContextData
|
|
124
|
+
if (hasContextData) {
|
|
125
125
|
requestSpec.body = {
|
|
126
|
-
...rule.payload || {},
|
|
127
126
|
...contextData,
|
|
128
127
|
...requestSpec.body || {}
|
|
129
128
|
};
|
package/dist/chain.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/chain.ts"],"sourcesContent":["/**\n * Request Chain Runner\n * 负责解析和执行通用的 HTTP 请求链 (Chain Execution)\n * 适用于任何需要链式 HTTP 请求编排的场景\n */\n\n// ============================================================================\n// Type Definitions\n// ============================================================================\n\n/** Supported HTTP Methods */\ntype HttpMethod = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n\n/** HTTP 请求参数 */\nexport interface HttpRequestSpec {\n method: HttpMethod;\n url: string;\n headers?: Record<string, string>;\n body?: any;\n}\n\n/**\n * 网络适配器接口\n * 定义实际发送请求的能力\n */\n/**\n * 网络适配器接口\n * 定义实际发送请求的能力\n */\nexport interface NetworkAdapter {\n get<T>(\n url: string,\n params?: any,\n headers?: Record<string, string>\n ): Promise<T>;\n post<T>(\n url: string,\n body?: any,\n headers?: Record<string, string>\n ): Promise<T>;\n put?<T>(\n url: string,\n body?: any,\n headers?: Record<string, string>\n ): Promise<T>;\n delete?<T>(\n url: string,\n params?: any,\n headers?: Record<string, string>\n ): Promise<T>;\n patch?<T>(\n url: string,\n body?: any,\n headers?: Record<string, string>\n ): Promise<T>;\n}\n\n/**\n * 网络处理器接口 (Strategy Pattern)\n * 组合了\"匹配规则\"和\"执行能力\"\n */\nexport interface NetworkHandler {\n /** 处理器名称 (用于调试) */\n name?: string;\n\n /** 判断该 URL 是否由此适配器处理 */\n shouldHandle(url: string, method: HttpMethod): boolean;\n\n /** 具体的网络适配器 */\n adapter: NetworkAdapter;\n}\n\n/**\n * 请求链规则 (Chain Step)\n */\nexport interface ChainRequestRule {\n /**\n * 结果存储的键名 (Context Key)\n * Fetch 结果存储: context[key] = response\n */\n key?: string;\n\n /**\n * HTTP 请求详情\n */\n request: HttpRequestSpec;\n\n /**\n * 数据提取选择器\n * 用于从响应中提取特定数据, e.g., \"elements.0\"\n */\n selector?: string;\n\n /** 是否允许请求失败 (默认 false) */\n optional?: boolean;\n\n /**\n * 是否包含上下文\n * 如果为 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"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA4HA,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;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/chain.ts"],"sourcesContent":["/**\n * Request Chain Runner\n * 负责解析和执行通用的 HTTP 请求链 (Chain Execution)\n * 适用于任何需要链式 HTTP 请求编排的场景\n */\n\n// ============================================================================\n// Type Definitions\n// ============================================================================\n\n/** Supported HTTP Methods */\ntype HttpMethod = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n\n/** HTTP 请求参数 */\nexport interface HttpRequestSpec {\n method: HttpMethod;\n url: string;\n headers?: Record<string, string>;\n body?: any;\n}\n\n/**\n * 网络适配器接口\n * 定义实际发送请求的能力\n */\n/**\n * 网络适配器接口\n * 定义实际发送请求的能力\n */\nexport interface NetworkAdapter {\n get<T>(\n url: string,\n params?: any,\n headers?: Record<string, string>,\n ): Promise<T>;\n post<T>(\n url: string,\n body?: any,\n headers?: Record<string, string>,\n ): Promise<T>;\n put?<T>(\n url: string,\n body?: any,\n headers?: Record<string, string>,\n ): Promise<T>;\n delete?<T>(\n url: string,\n params?: any,\n headers?: Record<string, string>,\n ): Promise<T>;\n patch?<T>(\n url: string,\n body?: any,\n headers?: Record<string, string>,\n ): Promise<T>;\n}\n\n/**\n * 网络处理器接口 (Strategy Pattern)\n * 组合了\"匹配规则\"和\"执行能力\"\n */\nexport interface NetworkHandler {\n /** 处理器名称 (用于调试) */\n name?: string;\n\n /** 判断该 URL 是否由此适配器处理 */\n shouldHandle(url: string, method: HttpMethod): boolean;\n\n /** 具体的网络适配器 */\n adapter: NetworkAdapter;\n}\n\n/**\n * 请求链规则 (Chain Step)\n */\nexport interface ChainRequestRule {\n /**\n * 结果存储的键名 (Context Key)\n * Fetch 结果存储: context[key] = response\n */\n key?: string;\n\n /**\n * HTTP 请求详情\n */\n request: HttpRequestSpec;\n\n /**\n * 数据提取选择器\n * 用于从响应中提取特定数据, e.g., \"elements.0\"\n */\n selector?: string;\n\n /** 是否允许请求失败 (默认 false) */\n optional?: boolean;\n\n /**\n * 是否包含上下文\n * 如果为 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\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 (合并 Context 到 Body)\n let requestSpec = { ...rule.request };\n if (hasContextData) {\n requestSpec.body = {\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"],"mappings":"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","names":[]}
|
package/dist/chain.mjs
CHANGED
|
@@ -97,9 +97,8 @@ async function executeRequestChain(requests, handlers = [], getChainRequests, in
|
|
|
97
97
|
}
|
|
98
98
|
const hasContextData = Object.keys(contextData).length > 0;
|
|
99
99
|
let requestSpec = { ...rule.request };
|
|
100
|
-
if (hasContextData
|
|
100
|
+
if (hasContextData) {
|
|
101
101
|
requestSpec.body = {
|
|
102
|
-
...rule.payload || {},
|
|
103
102
|
...contextData,
|
|
104
103
|
...requestSpec.body || {}
|
|
105
104
|
};
|
|
@@ -141,4 +140,4 @@ async function executeRequestChain(requests, handlers = [], getChainRequests, in
|
|
|
141
140
|
export {
|
|
142
141
|
executeRequestChain
|
|
143
142
|
};
|
|
144
|
-
//# sourceMappingURL=chunk-
|
|
143
|
+
//# sourceMappingURL=chunk-5CXSMOKF.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/chain.ts"],"sourcesContent":["/**\n * Request Chain Runner\n * 负责解析和执行通用的 HTTP 请求链 (Chain Execution)\n * 适用于任何需要链式 HTTP 请求编排的场景\n */\n\n// ============================================================================\n// Type Definitions\n// ============================================================================\n\n/** Supported HTTP Methods */\ntype HttpMethod = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n\n/** HTTP 请求参数 */\nexport interface HttpRequestSpec {\n method: HttpMethod;\n url: string;\n headers?: Record<string, string>;\n body?: any;\n}\n\n/**\n * 网络适配器接口\n * 定义实际发送请求的能力\n */\n/**\n * 网络适配器接口\n * 定义实际发送请求的能力\n */\nexport interface NetworkAdapter {\n get<T>(\n url: string,\n params?: any,\n headers?: Record<string, string>,\n ): Promise<T>;\n post<T>(\n url: string,\n body?: any,\n headers?: Record<string, string>,\n ): Promise<T>;\n put?<T>(\n url: string,\n body?: any,\n headers?: Record<string, string>,\n ): Promise<T>;\n delete?<T>(\n url: string,\n params?: any,\n headers?: Record<string, string>,\n ): Promise<T>;\n patch?<T>(\n url: string,\n body?: any,\n headers?: Record<string, string>,\n ): Promise<T>;\n}\n\n/**\n * 网络处理器接口 (Strategy Pattern)\n * 组合了\"匹配规则\"和\"执行能力\"\n */\nexport interface NetworkHandler {\n /** 处理器名称 (用于调试) */\n name?: string;\n\n /** 判断该 URL 是否由此适配器处理 */\n shouldHandle(url: string, method: HttpMethod): boolean;\n\n /** 具体的网络适配器 */\n adapter: NetworkAdapter;\n}\n\n/**\n * 请求链规则 (Chain Step)\n */\nexport interface ChainRequestRule {\n /**\n * 结果存储的键名 (Context Key)\n * Fetch 结果存储: context[key] = response\n */\n key?: string;\n\n /**\n * HTTP 请求详情\n */\n request: HttpRequestSpec;\n\n /**\n * 数据提取选择器\n * 用于从响应中提取特定数据, e.g., \"elements.0\"\n */\n selector?: string;\n\n /** 是否允许请求失败 (默认 false) */\n optional?: boolean;\n\n /**\n * 是否包含上下文\n * 如果为 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\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 (合并 Context 到 Body)\n let requestSpec = { ...rule.request };\n if (hasContextData) {\n requestSpec.body = {\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"],"mappings":"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","names":[]}
|
|
@@ -167,25 +167,17 @@ function xorTransform(data, key) {
|
|
|
167
167
|
}
|
|
168
168
|
return data;
|
|
169
169
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
return keyOption;
|
|
170
|
+
function createXorObfuscator(key) {
|
|
171
|
+
return {
|
|
172
|
+
encrypt: (data) => xorTransform(data, key),
|
|
173
|
+
decrypt: (data) => xorTransform(data, key)
|
|
174
|
+
};
|
|
177
175
|
}
|
|
178
176
|
function getInternalSecureType() {
|
|
179
177
|
return secure.SecurePayload;
|
|
180
178
|
}
|
|
181
179
|
async function encodeSecure(data, options = {}) {
|
|
182
|
-
const {
|
|
183
|
-
obfuscate = true,
|
|
184
|
-
obfuscationKey,
|
|
185
|
-
protoType,
|
|
186
|
-
transform,
|
|
187
|
-
encode: customEncode
|
|
188
|
-
} = options;
|
|
180
|
+
const { obfuscator, protoType, transform, encode: customEncode } = options;
|
|
189
181
|
let buffer;
|
|
190
182
|
const processedData = transform?.beforeEncode ? transform.beforeEncode(data) : data;
|
|
191
183
|
if (customEncode) {
|
|
@@ -200,25 +192,19 @@ async function encodeSecure(data, options = {}) {
|
|
|
200
192
|
const message = type.create(payload);
|
|
201
193
|
buffer = type.encode(message).finish();
|
|
202
194
|
}
|
|
203
|
-
if (
|
|
204
|
-
|
|
205
|
-
|
|
195
|
+
if (obfuscator) {
|
|
196
|
+
return await obfuscator.encrypt(buffer);
|
|
197
|
+
}
|
|
198
|
+
return buffer;
|
|
206
199
|
}
|
|
207
200
|
async function decodeSecure(buffer, options = {}) {
|
|
208
|
-
const {
|
|
209
|
-
obfuscate = true,
|
|
210
|
-
obfuscationKey,
|
|
211
|
-
protoType,
|
|
212
|
-
transform,
|
|
213
|
-
decode: customDecode
|
|
214
|
-
} = options;
|
|
201
|
+
const { obfuscator, protoType, transform, decode: customDecode } = options;
|
|
215
202
|
let uint8 = ArrayBuffer.isView(buffer) ? new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength) : new Uint8Array(buffer);
|
|
216
203
|
if (uint8.length === 0) {
|
|
217
204
|
return null;
|
|
218
205
|
}
|
|
219
|
-
if (uint8.length > 0 &&
|
|
220
|
-
|
|
221
|
-
uint8 = xorTransform(uint8, finalKey);
|
|
206
|
+
if (uint8.length > 0 && obfuscator) {
|
|
207
|
+
uint8 = await obfuscator.decrypt(uint8);
|
|
222
208
|
}
|
|
223
209
|
if (customDecode) {
|
|
224
210
|
return await customDecode(uint8);
|
|
@@ -306,10 +292,11 @@ export {
|
|
|
306
292
|
isProtobufContentType,
|
|
307
293
|
setProtobufRequestHeaders,
|
|
308
294
|
xorTransform,
|
|
295
|
+
createXorObfuscator,
|
|
309
296
|
encodeSecure,
|
|
310
297
|
decodeSecure,
|
|
311
298
|
secureResponse,
|
|
312
299
|
parseSecureRequest,
|
|
313
300
|
createProtobufHooks
|
|
314
301
|
};
|
|
315
|
-
//# sourceMappingURL=chunk-
|
|
302
|
+
//# sourceMappingURL=chunk-SAIJRTYF.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/secure.js","../src/protobuf.ts"],"sourcesContent":["/*eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars*/\nimport * as $protobuf from \"protobufjs/minimal\";\n\n// Common aliases\nconst $Reader = $protobuf.Reader, $Writer = $protobuf.Writer, $util = $protobuf.util;\n\n// Exported root namespace\nconst $root = $protobuf.roots[\"default\"] || ($protobuf.roots[\"default\"] = {});\n\nexport const secure = $root.secure = (() => {\n\n /**\n * Namespace secure.\n * @exports secure\n * @namespace\n */\n const secure = {};\n\n secure.SecurePayload = (function() {\n\n /**\n * Properties of a SecurePayload.\n * @memberof secure\n * @interface ISecurePayload\n * @property {number|Long|null} [ts] SecurePayload ts\n * @property {Uint8Array|null} [data] SecurePayload data\n */\n\n /**\n * Constructs a new SecurePayload.\n * @memberof secure\n * @classdesc Represents a SecurePayload.\n * @implements ISecurePayload\n * @constructor\n * @param {secure.ISecurePayload=} [properties] Properties to set\n */\n function SecurePayload(properties) {\n if (properties)\n for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)\n if (properties[keys[i]] != null)\n this[keys[i]] = properties[keys[i]];\n }\n\n /**\n * SecurePayload ts.\n * @member {number|Long} ts\n * @memberof secure.SecurePayload\n * @instance\n */\n SecurePayload.prototype.ts = $util.Long ? $util.Long.fromBits(0,0,false) : 0;\n\n /**\n * SecurePayload data.\n * @member {Uint8Array} data\n * @memberof secure.SecurePayload\n * @instance\n */\n SecurePayload.prototype.data = $util.newBuffer([]);\n\n /**\n * Creates a new SecurePayload instance using the specified properties.\n * @function create\n * @memberof secure.SecurePayload\n * @static\n * @param {secure.ISecurePayload=} [properties] Properties to set\n * @returns {secure.SecurePayload} SecurePayload instance\n */\n SecurePayload.create = function create(properties) {\n return new SecurePayload(properties);\n };\n\n /**\n * Encodes the specified SecurePayload message. Does not implicitly {@link secure.SecurePayload.verify|verify} messages.\n * @function encode\n * @memberof secure.SecurePayload\n * @static\n * @param {secure.ISecurePayload} message SecurePayload message or plain object to encode\n * @param {$protobuf.Writer} [writer] Writer to encode to\n * @returns {$protobuf.Writer} Writer\n */\n SecurePayload.encode = function encode(message, writer) {\n if (!writer)\n writer = $Writer.create();\n if (message.ts != null && Object.hasOwnProperty.call(message, \"ts\"))\n writer.uint32(/* id 1, wireType 0 =*/8).int64(message.ts);\n if (message.data != null && Object.hasOwnProperty.call(message, \"data\"))\n writer.uint32(/* id 2, wireType 2 =*/18).bytes(message.data);\n return writer;\n };\n\n /**\n * Encodes the specified SecurePayload message, length delimited. Does not implicitly {@link secure.SecurePayload.verify|verify} messages.\n * @function encodeDelimited\n * @memberof secure.SecurePayload\n * @static\n * @param {secure.ISecurePayload} message SecurePayload message or plain object to encode\n * @param {$protobuf.Writer} [writer] Writer to encode to\n * @returns {$protobuf.Writer} Writer\n */\n SecurePayload.encodeDelimited = function encodeDelimited(message, writer) {\n return this.encode(message, writer).ldelim();\n };\n\n /**\n * Decodes a SecurePayload message from the specified reader or buffer.\n * @function decode\n * @memberof secure.SecurePayload\n * @static\n * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from\n * @param {number} [length] Message length if known beforehand\n * @returns {secure.SecurePayload} SecurePayload\n * @throws {Error} If the payload is not a reader or valid buffer\n * @throws {$protobuf.util.ProtocolError} If required fields are missing\n */\n SecurePayload.decode = function decode(reader, length, error) {\n if (!(reader instanceof $Reader))\n reader = $Reader.create(reader);\n let end = length === undefined ? reader.len : reader.pos + length, message = new $root.secure.SecurePayload();\n while (reader.pos < end) {\n let tag = reader.uint32();\n if (tag === error)\n break;\n switch (tag >>> 3) {\n case 1: {\n message.ts = reader.int64();\n break;\n }\n case 2: {\n message.data = reader.bytes();\n break;\n }\n default:\n reader.skipType(tag & 7);\n break;\n }\n }\n return message;\n };\n\n /**\n * Decodes a SecurePayload message from the specified reader or buffer, length delimited.\n * @function decodeDelimited\n * @memberof secure.SecurePayload\n * @static\n * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from\n * @returns {secure.SecurePayload} SecurePayload\n * @throws {Error} If the payload is not a reader or valid buffer\n * @throws {$protobuf.util.ProtocolError} If required fields are missing\n */\n SecurePayload.decodeDelimited = function decodeDelimited(reader) {\n if (!(reader instanceof $Reader))\n reader = new $Reader(reader);\n return this.decode(reader, reader.uint32());\n };\n\n /**\n * Verifies a SecurePayload message.\n * @function verify\n * @memberof secure.SecurePayload\n * @static\n * @param {Object.<string,*>} message Plain object to verify\n * @returns {string|null} `null` if valid, otherwise the reason why it is not\n */\n SecurePayload.verify = function verify(message) {\n if (typeof message !== \"object\" || message === null)\n return \"object expected\";\n if (message.ts != null && message.hasOwnProperty(\"ts\"))\n if (!$util.isInteger(message.ts) && !(message.ts && $util.isInteger(message.ts.low) && $util.isInteger(message.ts.high)))\n return \"ts: integer|Long expected\";\n if (message.data != null && message.hasOwnProperty(\"data\"))\n if (!(message.data && typeof message.data.length === \"number\" || $util.isString(message.data)))\n return \"data: buffer expected\";\n return null;\n };\n\n /**\n * Creates a SecurePayload message from a plain object. Also converts values to their respective internal types.\n * @function fromObject\n * @memberof secure.SecurePayload\n * @static\n * @param {Object.<string,*>} object Plain object\n * @returns {secure.SecurePayload} SecurePayload\n */\n SecurePayload.fromObject = function fromObject(object) {\n if (object instanceof $root.secure.SecurePayload)\n return object;\n let message = new $root.secure.SecurePayload();\n if (object.ts != null)\n if ($util.Long)\n (message.ts = $util.Long.fromValue(object.ts)).unsigned = false;\n else if (typeof object.ts === \"string\")\n message.ts = parseInt(object.ts, 10);\n else if (typeof object.ts === \"number\")\n message.ts = object.ts;\n else if (typeof object.ts === \"object\")\n message.ts = new $util.LongBits(object.ts.low >>> 0, object.ts.high >>> 0).toNumber();\n if (object.data != null)\n if (typeof object.data === \"string\")\n $util.base64.decode(object.data, message.data = $util.newBuffer($util.base64.length(object.data)), 0);\n else if (object.data.length >= 0)\n message.data = object.data;\n return message;\n };\n\n /**\n * Creates a plain object from a SecurePayload message. Also converts values to other types if specified.\n * @function toObject\n * @memberof secure.SecurePayload\n * @static\n * @param {secure.SecurePayload} message SecurePayload\n * @param {$protobuf.IConversionOptions} [options] Conversion options\n * @returns {Object.<string,*>} Plain object\n */\n SecurePayload.toObject = function toObject(message, options) {\n if (!options)\n options = {};\n let object = {};\n if (options.defaults) {\n if ($util.Long) {\n let long = new $util.Long(0, 0, false);\n object.ts = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long;\n } else\n object.ts = options.longs === String ? \"0\" : 0;\n if (options.bytes === String)\n object.data = \"\";\n else {\n object.data = [];\n if (options.bytes !== Array)\n object.data = $util.newBuffer(object.data);\n }\n }\n if (message.ts != null && message.hasOwnProperty(\"ts\"))\n if (typeof message.ts === \"number\")\n object.ts = options.longs === String ? String(message.ts) : message.ts;\n else\n object.ts = options.longs === String ? $util.Long.prototype.toString.call(message.ts) : options.longs === Number ? new $util.LongBits(message.ts.low >>> 0, message.ts.high >>> 0).toNumber() : message.ts;\n if (message.data != null && message.hasOwnProperty(\"data\"))\n object.data = options.bytes === String ? $util.base64.encode(message.data, 0, message.data.length) : options.bytes === Array ? Array.prototype.slice.call(message.data) : message.data;\n return object;\n };\n\n /**\n * Converts this SecurePayload to JSON.\n * @function toJSON\n * @memberof secure.SecurePayload\n * @instance\n * @returns {Object.<string,*>} JSON object\n */\n SecurePayload.prototype.toJSON = function toJSON() {\n return this.constructor.toObject(this, $protobuf.util.toJSONOptions);\n };\n\n /**\n * Gets the default type url for SecurePayload\n * @function getTypeUrl\n * @memberof secure.SecurePayload\n * @static\n * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default \"type.googleapis.com\")\n * @returns {string} The default type url\n */\n SecurePayload.getTypeUrl = function getTypeUrl(typeUrlPrefix) {\n if (typeUrlPrefix === undefined) {\n typeUrlPrefix = \"type.googleapis.com\";\n }\n return typeUrlPrefix + \"/secure.SecurePayload\";\n };\n\n return SecurePayload;\n })();\n\n return secure;\n})();\n\nexport { $root as default };\n","/**\n * Protobuf 安全通信模块\n *\n * 提供基于 Protobuf 的二进制序列化、XOR 混淆加密以及与 API Client 的无缝集成。\n */\n\nimport protobuf from \"protobufjs/light\";\nimport { secure } from \"./secure.js\";\nimport type { FetchContext } from \"ofetch\";\n\n// ============================================================================\n// 1. 类型定义 (Types)\n// ============================================================================\n// export type ProtobufTypeLike = Pick<protobuf.Type, \"create\" | \"encode\" | \"decode\" | \"toObject\">;\nexport interface ProtobufTypeLike {\n // 1. 允许 properties 为任何对象,返回结果不再强制要求 $type\n create(properties?: Record<string, any>): any;\n // 2. 借用原生的参数类型约束,但手动简化返回类型\n encode(message: any): { finish(): Uint8Array };\n // 3. 借用原生的 decode 签名(支持 Uint8Array 和 Reader)\n decode(reader: Uint8Array | protobuf.Reader): any;\n // 4. 借用原生的 toObject 签名,保留 IConversionOptions 的类型检查\n toObject(message: any, options?: protobuf.IConversionOptions): any;\n}\n/**\n * 二进制混淆接口\n * 允许外部定义混淆逻辑,以便与不同语言实现的后端兼容\n */\nexport interface ProtobufObfuscator {\n encrypt(data: Uint8Array): Uint8Array | Promise<Uint8Array>;\n decrypt(data: Uint8Array): Uint8Array | Promise<Uint8Array>;\n}\n\n/**\n * 基础编解码配置\n * 控制如何将数据转换为二进制,以及是否混淆\n */\nexport interface ProtobufCodecOptions {\n /**\n * 混淆器实现\n * 提供该选项时才会启用混淆\n */\n obfuscator?: ProtobufObfuscator;\n /** 预编译的 Protobuf 类型对象 (推荐,性能最高) */\n protoType?: ProtobufTypeLike;\n /** 数据转换钩子:处理业务模型与 Proto 结构不一致的情况 */\n transform?: {\n beforeEncode?: (data: any) => any;\n afterDecode?: (payload: any) => any;\n };\n}\n\n/**\n * 完全自定义编解码接口\n * 用于外部项目完全接管序列化过程,同时复用基础库的混淆外壳\n */\nexport interface ProtobufCustomCodec {\n /** 外部定义的编码逻辑 (返回原始二进制) */\n encode?: (data: any) => Uint8Array | Promise<Uint8Array>;\n /** 外部定义的解码逻辑 (返回原始对象) */\n decode?: <T>(buffer: Uint8Array) => T | Promise<T>;\n}\n\n/**\n * 集成配置项\n * 用于 createProtobufHooks 或 API Client 全局配置\n */\nexport interface ProtobufHooksOptions\n extends ProtobufCodecOptions, ProtobufCustomCodec {}\n\nexport const PROTOBUF_CONTENT_TYPE = \"application/x-protobuf\";\n\n/** 检查是否为 Protobuf 响应头 */\nexport function isProtobufContentType(contentType: string | null): boolean {\n return !!contentType?.toLowerCase().includes(PROTOBUF_CONTENT_TYPE);\n}\n\n/** 配置请求头\n * @param existingHeaders 现有的 Header 集合\n * @param mode 配置模式:'send' (仅CT), 'receive' (仅Accept), 'both' (默认)\n */\nexport function setProtobufRequestHeaders(\n existingHeaders?: HeadersInit,\n mode: \"send\" | \"receive\" | \"both\" = \"both\",\n): Headers {\n const headers = new Headers(existingHeaders);\n\n if (mode === \"send\" || mode === \"both\") {\n headers.set(\"Content-Type\", PROTOBUF_CONTENT_TYPE);\n }\n\n if (mode === \"receive\" || mode === \"both\") {\n headers.set(\"Accept\", PROTOBUF_CONTENT_TYPE);\n }\n\n return headers;\n}\n\n/**\n * API Client 内部使用的标准化配置\n */\n// export interface ProtobufConfig {\n// encode: (data: any) => Uint8Array | Promise<Uint8Array>;\n// decode: <T>(buffer: Uint8Array) => T | Promise<T>;\n// options?: ProtobufCodecOptions;\n// }\n\n// ============================================================================\n// 2. 内部工具函数 (Internal Utilities)\n// ============================================================================\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\n/** 简单的二进制异或混淆转换 */\nexport function xorTransform(\n data: Uint8Array,\n key: string | Uint8Array,\n): Uint8Array {\n const keyBytes = typeof key === \"string\" ? encoder.encode(key) : key;\n if (keyBytes.length === 0) return data;\n\n for (let i = 0; i < data.length; i++) {\n data[i] ^= keyBytes[i % keyBytes.length];\n }\n return data;\n}\n\n/** 创建一个简单的 XOR 混淆器 */\nexport function createXorObfuscator(\n key: string | Uint8Array,\n): ProtobufObfuscator {\n return {\n encrypt: (data) => xorTransform(data, key),\n decrypt: (data) => xorTransform(data, key),\n };\n}\n\n/** 获取内置的 SecurePayload 类型(使用预编译代码) */\nfunction getInternalSecureType(): protobuf.Type {\n return secure.SecurePayload as unknown as protobuf.Type;\n}\n\n// ============================================================================\n// 3. 核心编解码逻辑 (Core Codecs)\n// ============================================================================\n\n/**\n * 编码安全载荷\n *\n * 流程:业务数据 -> (自定义编码 / Proto 序列化) -> 二进制混淆\n */\nexport async function encodeSecure<T>(\n data: T,\n options: ProtobufHooksOptions = {},\n): Promise<Uint8Array> {\n const { obfuscator, protoType, transform, encode: customEncode } = options;\n\n let buffer: Uint8Array;\n\n // 1. 预处理阶段:无论后续走哪条路径,只要定义了 beforeEncode 就先执行\n const processedData = transform?.beforeEncode\n ? transform.beforeEncode(data)\n : data;\n\n // 1. 序列化阶段\n if (customEncode) {\n buffer = await customEncode(processedData);\n } else {\n const type = protoType || getInternalSecureType();\n\n // 构造最终要交给 Protobuf 序列化的对象\n const payload = protoType\n ? processedData // 自定义模式:直接使用预处理后的数据\n : {\n // 默认容器模式:将预处理后的数据封装进信封\n ts: Date.now(),\n data:\n processedData instanceof Uint8Array\n ? processedData\n : encoder.encode(JSON.stringify(processedData)),\n };\n\n const message = type.create(payload);\n buffer = type.encode(message).finish();\n }\n\n // 2. 混淆阶段\n if (obfuscator) {\n return await obfuscator.encrypt(buffer);\n }\n return buffer;\n}\n\n/**\n * 解码安全载荷\n *\n * 流程:二进制流 -> 二进制反混淆 -> (自定义解码 / Proto 反序列化) -> 业务数据\n */\nexport async function decodeSecure<T>(\n buffer: Uint8Array | ArrayBuffer,\n options: ProtobufHooksOptions = {},\n): Promise<T> {\n const { obfuscator, protoType, transform, decode: customDecode } = options;\n\n // let uint8 = buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : buffer;\n // let uint8 =\n // buffer instanceof ArrayBuffer\n // ? new Uint8Array(buffer)\n // : new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);\n\n // 1. 标准化为 Uint8Array 视图 (跨环境最稳写法)\n let uint8 = ArrayBuffer.isView(buffer)\n ? new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength)\n : new Uint8Array(buffer);\n\n if (uint8.length === 0) {\n return null as T; // 或者返回 {},根据业务定\n }\n\n // 1. 混淆阶段\n if (uint8.length > 0 && obfuscator) {\n uint8 = await obfuscator.decrypt(uint8);\n }\n\n // 2. 反序列化阶段\n if (customDecode) {\n return await customDecode<T>(uint8);\n }\n\n const type = protoType || getInternalSecureType();\n const decoded = type.decode(uint8);\n const plainObj = type.toObject(decoded, {\n longs: String,\n enums: String,\n bytes: Uint8Array as any,\n defaults: true,\n });\n\n // 3. 转换阶段\n if (transform?.afterDecode) {\n return transform.afterDecode(plainObj);\n }\n\n // 内置容器模式的额外还原逻辑\n if (!protoType && plainObj.data) {\n const jsonString = decoder.decode(plainObj.data as Uint8Array);\n try {\n return JSON.parse(jsonString) as T;\n } catch {\n return jsonString as unknown as T;\n }\n }\n\n return plainObj as T;\n}\n\n// ============================================================================\n// 4. HTTP/环境适配工具 (HTTP Tools)\n// ============================================================================\n\n/** 创建安全响应 (Worker/Server 端使用) */\nexport async function secureResponse<T>(\n data: T,\n options?: ProtobufCodecOptions & { corsHeaders?: Record<string, string> },\n): Promise<Response> {\n const buffer = await encodeSecure(data, options);\n // const arrayBuffer = buffer.buffer.slice(\n // buffer.byteOffset,\n // buffer.byteOffset + buffer.byteLength,\n // ) as ArrayBuffer;\n const cleanBuffer = buffer.slice();\n\n return new Response(cleanBuffer, {\n headers: {\n \"Content-Type\": \"application/x-protobuf\",\n ...options?.corsHeaders,\n },\n });\n}\n\n/** * 解析安全请求体 (Worker/Server 端使用)\n * @param request 原生 Request 对象\n * @param options 编解码配置(可包含当前接口对应的 protoType 和自定义 obfuscator)\n */\nexport async function parseSecureRequest<T>(\n request: Request,\n options: ProtobufHooksOptions = {}, // 统一使用这个配置对象\n): Promise<T> {\n const buffer = await request.arrayBuffer();\n return decodeSecure<T>(buffer, options);\n}\n\n/** 标准化配置对象 */\n// export function normalizeProtobufConfig(\n// config: boolean | ProtobufConfig | undefined,\n// options?: ProtobufHooksOptions, // 接收全局配置\n// ): ProtobufConfig | null {\n// if (!config) return null;\n// if (config === true) {\n// return {\n// encode: (data) => encodeSecure(data, options),\n// decode: <T>(buffer: Uint8Array) => decodeSecure<T>(buffer, options),\n// options,\n// };\n// }\n// return config;\n// }\n\n// ============================================================================\n// 5. API Client 钩子逻辑 (Integration Hooks)\n// ============================================================================\n\n/** 创建 Protobuf 编解码钩子 (用于与 createApiClient 配合使用) */\nexport function createProtobufHooks(globalOptions: ProtobufHooksOptions = {}) {\n // const encode = (data: any) => encodeSecure(data, globalOptions);\n // const decode = <T>(buffer: Uint8Array) => decodeSecure<T>(buffer, globalOptions);\n\n return {\n async onRequest(context: FetchContext) {\n const { options: reqOptions } = context;\n\n // 1. 动态合并配置:将全局 globalOptions 和单次请求传的 reqOptions 合并\n // 这确保了 encodeSecure 能拿到当前请求特有的 protoType 或 transform\n const mergedOptions: ProtobufHooksOptions = {\n ...globalOptions,\n ...reqOptions,\n };\n\n const headers =\n reqOptions.headers instanceof Headers\n ? reqOptions.headers\n : new Headers(reqOptions.headers as HeadersInit | undefined);\n\n // 自动编码请求体\n if (\n isProtobufContentType(headers.get(\"Content-Type\")) &&\n reqOptions.body &&\n !(reqOptions.body instanceof Uint8Array)\n ) {\n reqOptions.body = await encodeSecure(reqOptions.body, mergedOptions);\n }\n\n // 自动设置期望响应类型\n if (\n isProtobufContentType(headers.get(\"Accept\")) &&\n !reqOptions.responseType\n ) {\n reqOptions.responseType = \"arrayBuffer\";\n }\n\n reqOptions.headers = headers;\n },\n\n async onResponse(context: FetchContext) {\n const { response, options: reqOptions } = context;\n if (!response?._data || response.status === 204) return;\n\n // 同样在响应阶段合并 options,以获取单次请求指定的 protoType 来解码\n const mergedOptions: ProtobufHooksOptions = {\n ...globalOptions,\n ...reqOptions,\n };\n\n if (isProtobufContentType(response.headers.get(\"Content-Type\"))) {\n try {\n // 这里的 _data 可能是 ArrayBuffer (浏览器) 或 Buffer (Node)\n // decodeSecure 内部已处理好兼容性\n response._data = await decodeSecure(response._data, mergedOptions);\n // const buffer = response._data as ArrayBuffer;\n // if (buffer && buffer.byteLength > 0) {\n // response._data = await decode(new Uint8Array(buffer));\n // }\n } catch (e) {\n console.log(\"Error [Protobuf Decode Error]\", e);\n response._data = null;\n }\n } else if (response._data instanceof ArrayBuffer) {\n const text = decoder.decode(response._data);\n try {\n response._data = JSON.parse(text);\n } catch {\n response._data = text;\n }\n }\n },\n };\n}\n"],"mappings":"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","names":["secure"]}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { ApiError, ApiResult, CreateApiClientOptions, TokenStorage, createApiClient, isApiError } from './client.mjs';
|
|
2
2
|
export { ChainRequestRule, HttpRequestSpec, NetworkAdapter, NetworkHandler, executeRequestChain } from './chain.mjs';
|
|
3
|
-
export { PROTOBUF_CONTENT_TYPE, ProtobufCodecOptions, ProtobufCustomCodec, ProtobufHooksOptions, ProtobufTypeLike, createProtobufHooks, decodeSecure, encodeSecure, isProtobufContentType, parseSecureRequest, secureResponse, setProtobufRequestHeaders, xorTransform } from './protobuf.mjs';
|
|
3
|
+
export { PROTOBUF_CONTENT_TYPE, ProtobufCodecOptions, ProtobufCustomCodec, ProtobufHooksOptions, ProtobufObfuscator, ProtobufTypeLike, createProtobufHooks, createXorObfuscator, decodeSecure, encodeSecure, isProtobufContentType, parseSecureRequest, secureResponse, setProtobufRequestHeaders, xorTransform } from './protobuf.mjs';
|
|
4
4
|
import 'ofetch';
|
|
5
5
|
import 'protobufjs/light';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { ApiError, ApiResult, CreateApiClientOptions, TokenStorage, createApiClient, isApiError } from './client.js';
|
|
2
2
|
export { ChainRequestRule, HttpRequestSpec, NetworkAdapter, NetworkHandler, executeRequestChain } from './chain.js';
|
|
3
|
-
export { PROTOBUF_CONTENT_TYPE, ProtobufCodecOptions, ProtobufCustomCodec, ProtobufHooksOptions, ProtobufTypeLike, createProtobufHooks, decodeSecure, encodeSecure, isProtobufContentType, parseSecureRequest, secureResponse, setProtobufRequestHeaders, xorTransform } from './protobuf.js';
|
|
3
|
+
export { PROTOBUF_CONTENT_TYPE, ProtobufCodecOptions, ProtobufCustomCodec, ProtobufHooksOptions, ProtobufObfuscator, ProtobufTypeLike, createProtobufHooks, createXorObfuscator, decodeSecure, encodeSecure, isProtobufContentType, parseSecureRequest, secureResponse, setProtobufRequestHeaders, xorTransform } from './protobuf.js';
|
|
4
4
|
import 'ofetch';
|
|
5
5
|
import 'protobufjs/light';
|
package/dist/index.js
CHANGED
|
@@ -33,6 +33,7 @@ __export(index_exports, {
|
|
|
33
33
|
PROTOBUF_CONTENT_TYPE: () => PROTOBUF_CONTENT_TYPE,
|
|
34
34
|
createApiClient: () => createApiClient,
|
|
35
35
|
createProtobufHooks: () => createProtobufHooks,
|
|
36
|
+
createXorObfuscator: () => createXorObfuscator,
|
|
36
37
|
decodeSecure: () => decodeSecure,
|
|
37
38
|
encodeSecure: () => encodeSecure,
|
|
38
39
|
executeRequestChain: () => executeRequestChain,
|
|
@@ -319,9 +320,8 @@ async function executeRequestChain(requests, handlers = [], getChainRequests, in
|
|
|
319
320
|
}
|
|
320
321
|
const hasContextData = Object.keys(contextData).length > 0;
|
|
321
322
|
let requestSpec = { ...rule.request };
|
|
322
|
-
if (hasContextData
|
|
323
|
+
if (hasContextData) {
|
|
323
324
|
requestSpec.body = {
|
|
324
|
-
...rule.payload || {},
|
|
325
325
|
...contextData,
|
|
326
326
|
...requestSpec.body || {}
|
|
327
327
|
};
|
|
@@ -529,25 +529,17 @@ function xorTransform(data, key) {
|
|
|
529
529
|
}
|
|
530
530
|
return data;
|
|
531
531
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
}
|
|
538
|
-
return keyOption;
|
|
532
|
+
function createXorObfuscator(key) {
|
|
533
|
+
return {
|
|
534
|
+
encrypt: (data) => xorTransform(data, key),
|
|
535
|
+
decrypt: (data) => xorTransform(data, key)
|
|
536
|
+
};
|
|
539
537
|
}
|
|
540
538
|
function getInternalSecureType() {
|
|
541
539
|
return secure.SecurePayload;
|
|
542
540
|
}
|
|
543
541
|
async function encodeSecure(data, options = {}) {
|
|
544
|
-
const {
|
|
545
|
-
obfuscate = true,
|
|
546
|
-
obfuscationKey,
|
|
547
|
-
protoType,
|
|
548
|
-
transform,
|
|
549
|
-
encode: customEncode
|
|
550
|
-
} = options;
|
|
542
|
+
const { obfuscator, protoType, transform, encode: customEncode } = options;
|
|
551
543
|
let buffer;
|
|
552
544
|
const processedData = transform?.beforeEncode ? transform.beforeEncode(data) : data;
|
|
553
545
|
if (customEncode) {
|
|
@@ -562,25 +554,19 @@ async function encodeSecure(data, options = {}) {
|
|
|
562
554
|
const message = type.create(payload);
|
|
563
555
|
buffer = type.encode(message).finish();
|
|
564
556
|
}
|
|
565
|
-
if (
|
|
566
|
-
|
|
567
|
-
|
|
557
|
+
if (obfuscator) {
|
|
558
|
+
return await obfuscator.encrypt(buffer);
|
|
559
|
+
}
|
|
560
|
+
return buffer;
|
|
568
561
|
}
|
|
569
562
|
async function decodeSecure(buffer, options = {}) {
|
|
570
|
-
const {
|
|
571
|
-
obfuscate = true,
|
|
572
|
-
obfuscationKey,
|
|
573
|
-
protoType,
|
|
574
|
-
transform,
|
|
575
|
-
decode: customDecode
|
|
576
|
-
} = options;
|
|
563
|
+
const { obfuscator, protoType, transform, decode: customDecode } = options;
|
|
577
564
|
let uint8 = ArrayBuffer.isView(buffer) ? new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength) : new Uint8Array(buffer);
|
|
578
565
|
if (uint8.length === 0) {
|
|
579
566
|
return null;
|
|
580
567
|
}
|
|
581
|
-
if (uint8.length > 0 &&
|
|
582
|
-
|
|
583
|
-
uint8 = xorTransform(uint8, finalKey);
|
|
568
|
+
if (uint8.length > 0 && obfuscator) {
|
|
569
|
+
uint8 = await obfuscator.decrypt(uint8);
|
|
584
570
|
}
|
|
585
571
|
if (customDecode) {
|
|
586
572
|
return await customDecode(uint8);
|
|
@@ -667,6 +653,7 @@ function createProtobufHooks(globalOptions = {}) {
|
|
|
667
653
|
PROTOBUF_CONTENT_TYPE,
|
|
668
654
|
createApiClient,
|
|
669
655
|
createProtobufHooks,
|
|
656
|
+
createXorObfuscator,
|
|
670
657
|
decodeSecure,
|
|
671
658
|
encodeSecure,
|
|
672
659
|
executeRequestChain,
|