@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 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: { method: "PUT", url: "/sync" },
321
- pickContext: ["profile"], // body will be { profile: context.profile, ...payload }
322
- payload: { source: "web-client" },
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 { createApiClient, createProtobufHooks } from "@i.un/api-client";
365
+ import {
366
+ createApiClient,
367
+ createProtobufHooks,
368
+ createXorObfuscator,
369
+ } from "@i.un/api-client";
364
370
 
365
371
  const hooks = createProtobufHooks({
366
- // 1. XOR Encryption key (optional but recommended)
367
- obfuscationKey: () => fetchSecretKeyFromServer(),
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 { parseSecureRequest, secureResponse } from "@i.un/api-client";
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, { obfuscationKey: "key" });
474
+ const body = await parseSecureRequest(request, {
475
+ obfuscator: createXorObfuscator("key"),
476
+ });
430
477
 
431
478
  // 2. Send secure response
432
- return await secureResponse({ success: true }, { obfuscationKey: "key" });
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: { method: "PUT", url: "/sync" },
313
- pickContext: ["profile"], // body 将会是 { profile: context.profile, ...payload }
314
- payload: { source: "web-client" },
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 { createApiClient, createProtobufHooks } from "@i.un/api-client";
357
+ import {
358
+ createApiClient,
359
+ createProtobufHooks,
360
+ createXorObfuscator,
361
+ } from "@i.un/api-client";
356
362
 
357
363
  const hooks = createProtobufHooks({
358
- // 1. XOR 加密密钥 (可选,但推荐)
359
- obfuscationKey: () => fetchSecretKeyFromServer(),
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 { parseSecureRequest, secureResponse } from "@i.un/api-client";
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, { obfuscationKey: "key" });
466
+ const body = await parseSecureRequest(request, {
467
+ obfuscator: createXorObfuscator("key"),
468
+ });
422
469
 
423
470
  // 2. 发送加密响应
424
- return await secureResponse({ success: true }, { obfuscationKey: "key" });
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
@@ -70,8 +70,6 @@ interface ChainRequestRule {
70
70
  * e.g., ["rawProfile", "rawCompany"]
71
71
  */
72
72
  pickContext?: string[];
73
- /** 额外 Payload (仅当 includeContext 为 true 或 pickContext 存在时合并) */
74
- payload?: Record<string, any>;
75
73
  }
76
74
  /**
77
75
  * 请求链执行器 (Request Chain Engine)
package/dist/chain.d.ts CHANGED
@@ -70,8 +70,6 @@ interface ChainRequestRule {
70
70
  * e.g., ["rawProfile", "rawCompany"]
71
71
  */
72
72
  pickContext?: string[];
73
- /** 额外 Payload (仅当 includeContext 为 true 或 pickContext 存在时合并) */
74
- payload?: Record<string, any>;
75
73
  }
76
74
  /**
77
75
  * 请求链执行器 (Request Chain Engine)
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 || rule.payload) {
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":"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","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
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  executeRequestChain
3
- } from "./chunk-SJA6WQ2W.mjs";
3
+ } from "./chunk-5CXSMOKF.mjs";
4
4
  export {
5
5
  executeRequestChain
6
6
  };
@@ -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 || rule.payload) {
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-SJA6WQ2W.mjs.map
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
- async function getObfuscationKey(keyOption) {
171
- const defaultKey = "api-client-default-key";
172
- if (!keyOption) return defaultKey;
173
- if (typeof keyOption === "function") {
174
- return await keyOption();
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 (!obfuscate) return buffer;
204
- const finalKey = await getObfuscationKey(obfuscationKey);
205
- return xorTransform(buffer, finalKey);
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 && obfuscate) {
220
- const finalKey = await getObfuscationKey(obfuscationKey);
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-YUPTRDEZ.mjs.map
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 || rule.payload) {
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
- async function getObfuscationKey(keyOption) {
533
- const defaultKey = "api-client-default-key";
534
- if (!keyOption) return defaultKey;
535
- if (typeof keyOption === "function") {
536
- return await keyOption();
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 (!obfuscate) return buffer;
566
- const finalKey = await getObfuscationKey(obfuscationKey);
567
- return xorTransform(buffer, finalKey);
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 && obfuscate) {
582
- const finalKey = await getObfuscationKey(obfuscationKey);
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,