@plolink/sdk 0.0.5 → 0.0.6

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.
Files changed (56) hide show
  1. package/README.md +40 -26
  2. package/dist/{chunk-4H4RACSE.js → chunk-LHNCGCWW.js} +137 -3
  3. package/dist/chunk-LHNCGCWW.js.map +1 -0
  4. package/dist/{chunk-NS3DJP2O.cjs → chunk-WFBN23AH.cjs} +141 -2
  5. package/dist/chunk-WFBN23AH.cjs.map +1 -0
  6. package/dist/client-BuUQTr8R.d.cts +766 -0
  7. package/dist/client-D2HlV7GT.d.ts +766 -0
  8. package/dist/common/index.cjs +18 -14
  9. package/dist/common/index.d.cts +126 -1
  10. package/dist/common/index.d.ts +126 -1
  11. package/dist/common/index.js +1 -1
  12. package/dist/index.cjs +647 -13
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +410 -3
  15. package/dist/index.d.ts +410 -3
  16. package/dist/index.js +631 -3
  17. package/dist/index.js.map +1 -1
  18. package/dist/modules/billing/index.cjs +1 -1
  19. package/dist/modules/billing/index.d.cts +1 -1
  20. package/dist/modules/billing/index.d.ts +1 -1
  21. package/dist/modules/billing/index.js +1 -1
  22. package/dist/modules/rbac/index.d.cts +1 -1
  23. package/dist/modules/rbac/index.d.ts +1 -1
  24. package/dist/modules/team/index.d.cts +1 -1
  25. package/dist/modules/team/index.d.ts +1 -1
  26. package/dist/modules/video-psych-analysis/index.cjs +123 -4
  27. package/dist/modules/video-psych-analysis/index.cjs.map +1 -1
  28. package/dist/modules/video-psych-analysis/index.d.cts +231 -93
  29. package/dist/modules/video-psych-analysis/index.d.ts +231 -93
  30. package/dist/modules/video-psych-analysis/index.js +123 -4
  31. package/dist/modules/video-psych-analysis/index.js.map +1 -1
  32. package/dist/modules/virtual-account/index.d.cts +1 -1
  33. package/dist/modules/virtual-account/index.d.ts +1 -1
  34. package/package.json +1 -16
  35. package/dist/chunk-4H4RACSE.js.map +0 -1
  36. package/dist/chunk-NS3DJP2O.cjs.map +0 -1
  37. package/dist/client-CAjIQKPm.d.cts +0 -193
  38. package/dist/client-CwNikk7i.d.ts +0 -193
  39. package/dist/modules/agent/index.cjs +0 -21
  40. package/dist/modules/agent/index.cjs.map +0 -1
  41. package/dist/modules/agent/index.d.cts +0 -71
  42. package/dist/modules/agent/index.d.ts +0 -71
  43. package/dist/modules/agent/index.js +0 -19
  44. package/dist/modules/agent/index.js.map +0 -1
  45. package/dist/modules/chat/index.cjs +0 -20
  46. package/dist/modules/chat/index.cjs.map +0 -1
  47. package/dist/modules/chat/index.d.cts +0 -68
  48. package/dist/modules/chat/index.d.ts +0 -68
  49. package/dist/modules/chat/index.js +0 -18
  50. package/dist/modules/chat/index.js.map +0 -1
  51. package/dist/modules/psych/index.cjs +0 -20
  52. package/dist/modules/psych/index.cjs.map +0 -1
  53. package/dist/modules/psych/index.d.cts +0 -69
  54. package/dist/modules/psych/index.d.ts +0 -69
  55. package/dist/modules/psych/index.js +0 -18
  56. package/dist/modules/psych/index.js.map +0 -1
package/README.md CHANGED
@@ -63,41 +63,55 @@ const client = new PlolinkClient({
63
63
 
64
64
  ## 业务模块
65
65
 
66
- SDK 提供多个业务模块,支持按需导入:
66
+ SDK 提供多个业务模块,支持按需导入。
67
67
 
68
- ### 充值消费模块 (Billing)
68
+ ### 心理评测模块 (PsychologyTest)
69
69
 
70
- ```typescript
71
- import { Billing } from '@plolink/sdk/billing';
72
-
73
- const billing = new Billing(client);
74
- const balance = await billing.getBalance();
75
- ```
70
+ 心理评测模块提供完整的心理评测能力,包括:
76
71
 
77
- ### 虚拟账号模块 (VirtualAccount)
72
+ - **发起评测** (`initiate`): 根据题组代码创建评测会话并生成题目
73
+ - **提交答题** (`submit`): 提交用户答题记录并自动计算结果
74
+ - **获取详情** (`getDetail`): 获取评测会话详情和AI分析结果
75
+ - **获取答题记录** (`getAnswers`): 获取完整的答题记录列表
76
+ - **列表查询** (`listTestSessions`): 查询评测会话列表,支持分页和筛选 ✨
77
+ - **删除会话** (`deleteTestSession`): 软删除评测会话记录 ✨
78
78
 
79
79
  ```typescript
80
- import { VirtualAccount } from '@plolink/sdk/virtual-account';
81
-
82
- const virtualAccount = new VirtualAccount(client);
83
-
84
- // 创建虚拟账号
85
- const result = await virtualAccount.createVirtualAccount({
86
- mode: 'EXISTING_TEAM',
87
- teamId: 'team-123',
88
- virtualAccountName: 'API Service',
89
- presetRoles: [{
90
- roleType: 'SYSTEM',
91
- roleCode: 'TEAM_MEMBER',
92
- scopeType: 'TEAM'
93
- }]
80
+ // 发起心理评测
81
+ const initResult = await client.psychologyTest.initiate({
82
+ groupCodes: ['family_parenting', 'disc'],
83
+ businessModule: 'talent_assessment',
84
+ businessCode: 'job_match',
85
+ businessId: 'candidate_123',
86
+ needAnalysis: true,
87
+ analysisPrompt: '请分析候选人的性格特点',
88
+ teamId: 'team_123'
89
+ });
90
+
91
+ // 提交答题
92
+ const submitResult = await client.psychologyTest.submit({
93
+ sessionId: initResult.sessionId,
94
+ answers: [...],
95
+ duration: 480000
94
96
  });
95
97
 
96
- // 管理 API Keys
97
- const keys = await virtualAccount.listVirtualAccountApiKeys(result.virtualAccount.id);
98
+ // 列表查询评测会话 新增
99
+ const sessions = await client.psychologyTest.listTestSessions({
100
+ page: 1,
101
+ size: 20,
102
+ status: 'COMPLETED',
103
+ analysisStatus: 'COMPLETED'
104
+ });
105
+
106
+ // 删除评测会话 ✨ 新增
107
+ await client.psychologyTest.deleteTestSession(sessionId);
98
108
  ```
99
109
 
100
- 详细文档请参考 docs/modules/
110
+ 详细文档请参考 [docs/modules/psychology-test.md](./docs/modules/psychology-test.md)
111
+
112
+ ### 其他模块
113
+
114
+ 其他业务模块的详细文档请参考 docs/modules/ 目录
101
115
 
102
116
  ## 高级功能
103
117
 
@@ -1,3 +1,5 @@
1
+ import crypto from 'crypto';
2
+
1
3
  // src/common/hooks.ts
2
4
  var EventEmitter = class {
3
5
  constructor() {
@@ -329,7 +331,139 @@ async function safeDynamicImport(moduleName) {
329
331
  throw new Error(`Failed to import module "${moduleName}": ${errorMessage}`);
330
332
  }
331
333
  }
334
+ var InfoSyncSignature = class _InfoSyncSignature {
335
+ /**
336
+ * 生成 HMAC-SHA256 签名
337
+ *
338
+ * 签名算法:
339
+ * - message = `${timestamp}.${payload}`
340
+ * - signature = HMAC-SHA256(secretKey, message)
341
+ *
342
+ * @param secretKey - 密钥(从创建 Webhook 配置时获得)
343
+ * @param timestamp - 时间戳(毫秒)
344
+ * @param payload - 负载字符串(通常是 JSON.stringify 后的请求体)
345
+ * @returns 签名(hex 格式)
346
+ *
347
+ * @example
348
+ * ```typescript
349
+ * const timestamp = Date.now();
350
+ * const payload = JSON.stringify({ foo: 'bar' });
351
+ * const signature = InfoSyncSignature.generateSignature(
352
+ * 'your-secret-key',
353
+ * timestamp,
354
+ * payload
355
+ * );
356
+ * ```
357
+ */
358
+ static generateSignature(secretKey, timestamp, payload) {
359
+ const message = `${timestamp}.${payload}`;
360
+ const signature = crypto.createHmac("sha256", secretKey).update(message).digest("hex");
361
+ return signature;
362
+ }
363
+ /**
364
+ * 验证 HMAC-SHA256 签名
365
+ *
366
+ * @param secretKey - 密钥
367
+ * @param timestamp - 时间戳(毫秒)
368
+ * @param payload - 负载字符串
369
+ * @param signature - 待验证的签名
370
+ * @returns 签名是否有效
371
+ *
372
+ * @example
373
+ * ```typescript
374
+ * const isValid = InfoSyncSignature.verifySignature(
375
+ * secretKey,
376
+ * timestamp,
377
+ * payload,
378
+ * signature
379
+ * );
380
+ *
381
+ * if (!isValid) {
382
+ * throw new Error('Invalid signature');
383
+ * }
384
+ * ```
385
+ */
386
+ static verifySignature(secretKey, timestamp, payload, signature) {
387
+ try {
388
+ const expected = _InfoSyncSignature.generateSignature(
389
+ secretKey,
390
+ timestamp,
391
+ payload
392
+ );
393
+ return crypto.timingSafeEqual(
394
+ Buffer.from(expected, "hex"),
395
+ Buffer.from(signature, "hex")
396
+ );
397
+ } catch (error) {
398
+ return false;
399
+ }
400
+ }
401
+ /**
402
+ * 验证时间戳是否在有效期内
403
+ *
404
+ * 用于防止重放攻击,确保请求是最近发送的
405
+ *
406
+ * @param timestamp - 时间戳(毫秒)
407
+ * @param maxAge - 最大有效期(毫秒),默认 5 分钟
408
+ * @returns 时间戳是否有效
409
+ *
410
+ * @example
411
+ * ```typescript
412
+ * const timestamp = parseInt(req.headers['x-sync-timestamp']);
413
+ *
414
+ * // 验证时间戳是否在 5 分钟内
415
+ * if (!InfoSyncSignature.verifyTimestamp(timestamp, 5 * 60 * 1000)) {
416
+ * return res.status(401).json({ error: 'Timestamp expired' });
417
+ * }
418
+ * ```
419
+ */
420
+ static verifyTimestamp(timestamp, maxAge = 5 * 60 * 1e3) {
421
+ const now = Date.now();
422
+ return Math.abs(now - timestamp) <= maxAge;
423
+ }
424
+ /**
425
+ * 完整的 Webhook 请求验证
426
+ *
427
+ * 同时验证签名和时间戳,是推荐的验证方式
428
+ *
429
+ * @param secretKey - 密钥
430
+ * @param timestamp - 时间戳(毫秒)
431
+ * @param payload - 负载字符串
432
+ * @param signature - 待验证的签名
433
+ * @param maxAge - 最大有效期(毫秒),默认 5 分钟
434
+ * @returns 验证结果
435
+ *
436
+ * @example
437
+ * ```typescript
438
+ * const result = InfoSyncSignature.verifyWebhookRequest(
439
+ * secretKey,
440
+ * timestamp,
441
+ * payload,
442
+ * signature
443
+ * );
444
+ *
445
+ * if (!result.valid) {
446
+ * return res.status(401).json({ error: result.error });
447
+ * }
448
+ * ```
449
+ */
450
+ static verifyWebhookRequest(secretKey, timestamp, payload, signature, maxAge = 5 * 60 * 1e3) {
451
+ if (!_InfoSyncSignature.verifyTimestamp(timestamp, maxAge)) {
452
+ return {
453
+ valid: false,
454
+ error: "Timestamp expired or invalid"
455
+ };
456
+ }
457
+ if (!_InfoSyncSignature.verifySignature(secretKey, timestamp, payload, signature)) {
458
+ return {
459
+ valid: false,
460
+ error: "Invalid signature"
461
+ };
462
+ }
463
+ return { valid: true };
464
+ }
465
+ };
332
466
 
333
- export { EventEmitter, TypedEventEmitter, assertEnvironment, environmentInfo, features, getEnvironment, getFileName, getFileSize, isBrowser, isNode, isValidFileInput, isWebWorker, safeDynamicImport };
334
- //# sourceMappingURL=chunk-4H4RACSE.js.map
335
- //# sourceMappingURL=chunk-4H4RACSE.js.map
467
+ export { EventEmitter, InfoSyncSignature, TypedEventEmitter, assertEnvironment, environmentInfo, features, getEnvironment, getFileName, getFileSize, isBrowser, isNode, isValidFileInput, isWebWorker, safeDynamicImport };
468
+ //# sourceMappingURL=chunk-LHNCGCWW.js.map
469
+ //# sourceMappingURL=chunk-LHNCGCWW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/common/hooks.ts","../src/common/adapter.ts","../src/common/signature.ts"],"names":[],"mappings":"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","file":"chunk-LHNCGCWW.js","sourcesContent":["/**\n * 事件处理函数类型\n */\ntype EventHandler<T = unknown> = (data: T) => void;\n\n/**\n * 事件处理器映射类型\n */\ntype EventHandlerMap = Map<string, Set<EventHandler>>;\n\n/**\n * 事件发射器基础类\n * \n * @description\n * 提供简单的事件订阅/发布机制,用于业务模块的状态变化通知。\n * 支持类型安全的事件处理,适用于状态变化、进度更新等场景。\n * \n * @example\n * ```typescript\n * // 创建事件发射器\n * const emitter = new EventEmitter();\n * \n * // 订阅事件\n * const unsubscribe = emitter.on('status-change', (data) => {\n * console.log('Status changed:', data);\n * });\n * \n * // 触发事件\n * emitter.emit('status-change', { status: 'completed' });\n * \n * // 取消订阅\n * unsubscribe();\n * \n * // 一次性事件\n * emitter.once('complete', (data) => {\n * console.log('Completed:', data);\n * });\n * ```\n */\nexport class EventEmitter {\n /**\n * 事件处理器映射表\n */\n private handlers: EventHandlerMap = new Map();\n\n /**\n * 一次性事件处理器集合\n */\n private onceHandlers: EventHandlerMap = new Map();\n\n /**\n * 订阅事件\n * \n * @param event - 事件名称\n * @param handler - 事件处理函数\n * @returns 取消订阅的函数\n * \n * @example\n * ```typescript\n * const unsubscribe = emitter.on('data', (payload) => {\n * console.log('Received:', payload);\n * });\n * \n * // 不再需要时取消订阅\n * unsubscribe();\n * ```\n */\n public on<T = unknown>(event: string, handler: EventHandler<T>): () => void {\n if (!this.handlers.has(event)) {\n this.handlers.set(event, new Set());\n }\n\n const handlers = this.handlers.get(event)!;\n handlers.add(handler as EventHandler);\n\n // 返回取消订阅函数\n return () => {\n handlers.delete(handler as EventHandler);\n if (handlers.size === 0) {\n this.handlers.delete(event);\n }\n };\n }\n\n /**\n * 订阅一次性事件\n * \n * @description\n * 事件处理函数只会执行一次,执行后自动取消订阅。\n * \n * @param event - 事件名称\n * @param handler - 事件处理函数\n * @returns 取消订阅的函数\n * \n * @example\n * ```typescript\n * emitter.once('complete', (result) => {\n * console.log('Task completed:', result);\n * });\n * ```\n */\n public once<T = unknown>(event: string, handler: EventHandler<T>): () => void {\n if (!this.onceHandlers.has(event)) {\n this.onceHandlers.set(event, new Set());\n }\n\n const handlers = this.onceHandlers.get(event)!;\n handlers.add(handler as EventHandler);\n\n // 返回取消订阅函数\n return () => {\n handlers.delete(handler as EventHandler);\n if (handlers.size === 0) {\n this.onceHandlers.delete(event);\n }\n };\n }\n\n /**\n * 触发事件\n * \n * @param event - 事件名称\n * @param data - 传递给处理函数的数据\n * \n * @example\n * ```typescript\n * emitter.emit('status-change', { \n * from: 'pending', \n * to: 'completed' \n * });\n * ```\n */\n public emit<T = unknown>(event: string, data: T): void {\n // 执行常规事件处理器\n const handlers = this.handlers.get(event);\n if (handlers) {\n handlers.forEach((handler) => {\n try {\n handler(data);\n } catch (error) {\n console.error(`Error in event handler for \"${event}\":`, error);\n }\n });\n }\n\n // 执行一次性事件处理器\n const onceHandlers = this.onceHandlers.get(event);\n if (onceHandlers) {\n const handlersToExecute = Array.from(onceHandlers);\n // 清空一次性处理器\n this.onceHandlers.delete(event);\n\n handlersToExecute.forEach((handler) => {\n try {\n handler(data);\n } catch (error) {\n console.error(`Error in once event handler for \"${event}\":`, error);\n }\n });\n }\n }\n\n /**\n * 取消订阅指定事件的处理器\n * \n * @param event - 事件名称\n * @param handler - 可选,要取消的事件处理函数。如果不提供,则取消所有处理器\n * \n * @example\n * ```typescript\n * // 取消所有处理器\n * emitter.off('status-change');\n * \n * // 取消特定处理器\n * const handler = (data) => console.log(data);\n * emitter.on('data', handler);\n * emitter.off('data', handler);\n * ```\n */\n public off<T = unknown>(event: string, handler?: EventHandler<T>): void {\n if (!handler) {\n // 取消所有处理器\n this.handlers.delete(event);\n this.onceHandlers.delete(event);\n return;\n }\n\n // 取消特定处理器\n const handlers = this.handlers.get(event);\n if (handlers) {\n handlers.delete(handler as EventHandler);\n if (handlers.size === 0) {\n this.handlers.delete(event);\n }\n }\n\n const onceHandlers = this.onceHandlers.get(event);\n if (onceHandlers) {\n onceHandlers.delete(handler as EventHandler);\n if (onceHandlers.size === 0) {\n this.onceHandlers.delete(event);\n }\n }\n }\n\n /**\n * 清除所有事件处理器\n * \n * @example\n * ```typescript\n * emitter.clear();\n * ```\n */\n public clear(): void {\n this.handlers.clear();\n this.onceHandlers.clear();\n }\n\n /**\n * 获取指定事件的处理器数量\n * \n * @param event - 事件名称\n * @returns 处理器数量\n * \n * @example\n * ```typescript\n * const count = emitter.listenerCount('status-change');\n * console.log(`${count} listeners registered`);\n * ```\n */\n public listenerCount(event: string): number {\n const regularCount = this.handlers.get(event)?.size || 0;\n const onceCount = this.onceHandlers.get(event)?.size || 0;\n return regularCount + onceCount;\n }\n\n /**\n * 获取所有已注册的事件名称\n * \n * @returns 事件名称数组\n * \n * @example\n * ```typescript\n * const events = emitter.eventNames();\n * console.log('Registered events:', events);\n * ```\n */\n public eventNames(): string[] {\n const regularEvents = Array.from(this.handlers.keys());\n const onceEvents = Array.from(this.onceHandlers.keys());\n return Array.from(new Set([...regularEvents, ...onceEvents]));\n }\n}\n\n/**\n * 类型安全的事件发射器\n * \n * @description\n * 提供类型约束的事件发射器,确保事件名称和数据类型的正确性。\n * \n * @template TEvents - 事件映射类型\n * \n * @example\n * ```typescript\n * // 定义事件类型\n * interface TaskEvents {\n * 'status-change': { status: string };\n * 'progress': { percent: number };\n * 'error': { message: string };\n * }\n * \n * // 创建类型安全的发射器\n * const emitter = new TypedEventEmitter<TaskEvents>();\n * \n * // TypeScript 会检查事件名称和数据类型\n * emitter.on('status-change', (data) => {\n * console.log(data.status); // TypeScript 知道 data 有 status 属性\n * });\n * \n * emitter.emit('status-change', { status: 'completed' }); // OK\n * emitter.emit('status-change', { wrong: 'data' }); // TypeScript 报错\n * ```\n */\nexport class TypedEventEmitter<TEvents extends Record<string, unknown>> {\n private emitter = new EventEmitter();\n\n /**\n * 订阅事件(类型安全)\n */\n public on<K extends keyof TEvents>(\n event: K,\n handler: EventHandler<TEvents[K]>\n ): () => void {\n return this.emitter.on(event as string, handler);\n }\n\n /**\n * 订阅一次性事件(类型安全)\n */\n public once<K extends keyof TEvents>(\n event: K,\n handler: EventHandler<TEvents[K]>\n ): () => void {\n return this.emitter.once(event as string, handler);\n }\n\n /**\n * 触发事件(类型安全)\n */\n public emit<K extends keyof TEvents>(event: K, data: TEvents[K]): void {\n this.emitter.emit(event as string, data);\n }\n\n /**\n * 取消订阅(类型安全)\n */\n public off<K extends keyof TEvents>(event: K, handler?: EventHandler<TEvents[K]>): void {\n if (handler) {\n this.emitter.off(event as string, handler);\n } else {\n this.emitter.off(event as string);\n }\n }\n\n /**\n * 清除所有事件处理器\n */\n public clear(): void {\n this.emitter.clear();\n }\n\n /**\n * 获取指定事件的处理器数量\n */\n public listenerCount<K extends keyof TEvents>(event: K): number {\n return this.emitter.listenerCount(event as string);\n }\n\n /**\n * 获取所有已注册的事件名称\n */\n public eventNames(): Array<keyof TEvents> {\n return this.emitter.eventNames() as Array<keyof TEvents>;\n }\n}\n\n","/**\n * 环境适配工具\n * \n * @description\n * 提供跨环境(Node.js / 浏览器)的工具函数和类型检测。\n * 用于处理文件上传、网络请求等在不同环境下有差异的功能。\n */\n\n/**\n * 检测当前是否运行在 Node.js 环境\n * \n * @returns 如果在 Node.js 环境返回 true,否则返回 false\n * \n * @example\n * ```typescript\n * if (isNode) {\n * // 使用 Node.js 特有的 API\n * const fs = require('fs');\n * }\n * ```\n */\nexport const isNode: boolean =\n typeof process !== 'undefined' &&\n process.versions !== null &&\n process.versions.node !== null;\n\n/**\n * 检测当前是否运行在浏览器环境\n * \n * @returns 如果在浏览器环境返回 true,否则返回 false\n * \n * @example\n * ```typescript\n * if (isBrowser) {\n * // 使用浏览器 API\n * const file = new File(['content'], 'file.txt');\n * }\n * ```\n */\nexport const isBrowser: boolean =\n typeof window !== 'undefined' && typeof window.document !== 'undefined';\n\n/**\n * 检测当前是否支持 Web Worker\n * \n * @returns 如果支持 Web Worker 返回 true,否则返回 false\n */\nexport const isWebWorker: boolean =\n typeof self === 'object' &&\n // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access\n typeof (self as any).importScripts === 'function';\n\n/**\n * 获取运行环境类型\n * \n * @returns 环境类型字符串\n * \n * @example\n * ```typescript\n * const env = getEnvironment();\n * console.log(`Running in ${env}`); // \"node\" 或 \"browser\" 或 \"worker\"\n * ```\n */\nexport function getEnvironment(): 'node' | 'browser' | 'worker' | 'unknown' {\n if (isNode) return 'node';\n if (isWebWorker) return 'worker';\n if (isBrowser) return 'browser';\n return 'unknown';\n}\n\n/**\n * 文件类型联合类型\n * 在 Node.js 中是 Buffer,在浏览器中是 Blob 或 File\n */\nexport type FileInput = Buffer | Blob | File;\n\n/**\n * 检测是否为有效的文件输入\n * \n * @param input - 待检测的输入\n * @returns 如果是有效的文件输入返回 true\n * \n * @example\n * ```typescript\n * const file = new File(['content'], 'test.txt');\n * if (isValidFileInput(file)) {\n * // 处理文件\n * }\n * ```\n */\nexport function isValidFileInput(input: unknown): input is FileInput {\n if (isNode) {\n // Node.js 环境:检查是否为 Buffer\n return Buffer.isBuffer(input);\n }\n\n if (isBrowser) {\n // 浏览器环境:检查是否为 Blob 或 File\n return input instanceof Blob || input instanceof File;\n }\n\n return false;\n}\n\n/**\n * 获取文件输入的大小(字节)\n * \n * @param input - 文件输入\n * @returns 文件大小(字节)\n * @throws {Error} 如果输入类型不支持\n * \n * @example\n * ```typescript\n * const file = new File(['content'], 'test.txt');\n * const size = getFileSize(file);\n * console.log(`File size: ${size} bytes`);\n * ```\n */\nexport function getFileSize(input: FileInput): number {\n if (isNode && Buffer.isBuffer(input)) {\n return input.length;\n }\n\n if ((isBrowser || isWebWorker) && (input instanceof Blob || input instanceof File)) {\n return input.size;\n }\n\n throw new Error('Unsupported file input type');\n}\n\n/**\n * 获取文件名(仅适用于 File 对象)\n * \n * @param input - 文件输入\n * @returns 文件名,如果无法获取则返回 undefined\n * \n * @example\n * ```typescript\n * const file = new File(['content'], 'test.txt');\n * const name = getFileName(file);\n * console.log(name); // \"test.txt\"\n * ```\n */\nexport function getFileName(input: FileInput): string | undefined {\n if (input instanceof File) {\n return input.name;\n }\n return undefined;\n}\n\n/**\n * 环境特定功能检测\n */\nexport const features = {\n /**\n * 是否支持 FormData\n */\n hasFormData: typeof FormData !== 'undefined',\n\n /**\n * 是否支持 Blob\n */\n hasBlob: typeof Blob !== 'undefined',\n\n /**\n * 是否支持 File\n */\n hasFile: typeof File !== 'undefined',\n\n /**\n * 是否支持 FileReader\n */\n hasFileReader: typeof FileReader !== 'undefined',\n\n /**\n * 是否支持 fetch API\n */\n hasFetch: typeof fetch !== 'undefined',\n\n /**\n * 是否支持 Stream API\n */\n hasStream: typeof ReadableStream !== 'undefined',\n} as const;\n\n/**\n * 环境信息对象\n * \n * @example\n * ```typescript\n * console.log(environmentInfo);\n * // {\n * // type: 'browser',\n * // isNode: false,\n * // isBrowser: true,\n * // features: { ... }\n * // }\n * ```\n */\nexport const environmentInfo = {\n type: getEnvironment(),\n isNode,\n isBrowser,\n isWebWorker,\n features,\n} as const;\n\n/**\n * 断言当前环境\n * \n * @param expectedEnv - 期望的环境类型\n * @param message - 自定义错误消息\n * @throws {Error} 如果当前环境不符合预期\n * \n * @example\n * ```typescript\n * // 确保在 Node.js 环境中运行\n * assertEnvironment('node', 'This feature requires Node.js');\n * ```\n */\nexport function assertEnvironment(\n expectedEnv: 'node' | 'browser' | 'worker',\n message?: string\n): void {\n const currentEnv = getEnvironment();\n if (currentEnv !== expectedEnv) {\n throw new Error(\n message ||\n `This feature requires ${expectedEnv} environment, but running in ${currentEnv}`\n );\n }\n}\n\n/**\n * 安全地动态导入模块(仅 Node.js)\n * \n * @description\n * 在 Node.js 环境中动态导入模块,避免在浏览器环境中引起错误。\n * \n * @param moduleName - 模块名称\n * @returns Promise,resolve 为模块对象\n * @throws {Error} 如果不在 Node.js 环境或模块加载失败\n * \n * @example\n * ```typescript\n * if (isNode) {\n * const fs = await safeDynamicImport('fs');\n * const content = fs.readFileSync('file.txt', 'utf-8');\n * }\n * ```\n */\nexport async function safeDynamicImport<T = unknown>(moduleName: string): Promise<T> {\n assertEnvironment('node', `Cannot import module \"${moduleName}\" in non-Node.js environment`);\n \n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return await import(moduleName);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to import module \"${moduleName}\": ${errorMessage}`);\n }\n}\n\n","import crypto from 'crypto';\n\n/**\n * 信息同步签名工具\n * \n * 用于生成和验证 Webhook 请求的 HMAC-SHA256 签名\n * \n * @example\n * ```typescript\n * // 验证 Webhook 签名\n * const timestamp = parseInt(req.headers['x-sync-timestamp']);\n * const signature = req.headers['x-sync-signature'];\n * const payload = JSON.stringify(req.body);\n * \n * const isValid = InfoSyncSignature.verifySignature(\n * secretKey,\n * timestamp,\n * payload,\n * signature\n * );\n * \n * if (!isValid) {\n * return res.status(401).json({ error: 'Invalid signature' });\n * }\n * ```\n */\nexport class InfoSyncSignature {\n /**\n * 生成 HMAC-SHA256 签名\n * \n * 签名算法:\n * - message = `${timestamp}.${payload}`\n * - signature = HMAC-SHA256(secretKey, message)\n * \n * @param secretKey - 密钥(从创建 Webhook 配置时获得)\n * @param timestamp - 时间戳(毫秒)\n * @param payload - 负载字符串(通常是 JSON.stringify 后的请求体)\n * @returns 签名(hex 格式)\n * \n * @example\n * ```typescript\n * const timestamp = Date.now();\n * const payload = JSON.stringify({ foo: 'bar' });\n * const signature = InfoSyncSignature.generateSignature(\n * 'your-secret-key',\n * timestamp,\n * payload\n * );\n * ```\n */\n public static generateSignature(\n secretKey: string,\n timestamp: number,\n payload: string\n ): string {\n const message = `${timestamp}.${payload}`;\n const signature = crypto\n .createHmac('sha256', secretKey)\n .update(message)\n .digest('hex');\n return signature;\n }\n\n /**\n * 验证 HMAC-SHA256 签名\n * \n * @param secretKey - 密钥\n * @param timestamp - 时间戳(毫秒)\n * @param payload - 负载字符串\n * @param signature - 待验证的签名\n * @returns 签名是否有效\n * \n * @example\n * ```typescript\n * const isValid = InfoSyncSignature.verifySignature(\n * secretKey,\n * timestamp,\n * payload,\n * signature\n * );\n * \n * if (!isValid) {\n * throw new Error('Invalid signature');\n * }\n * ```\n */\n public static verifySignature(\n secretKey: string,\n timestamp: number,\n payload: string,\n signature: string\n ): boolean {\n try {\n const expected = InfoSyncSignature.generateSignature(\n secretKey,\n timestamp,\n payload\n );\n \n // 使用时间安全的比较方法,防止时序攻击\n return crypto.timingSafeEqual(\n Buffer.from(expected, 'hex'),\n Buffer.from(signature, 'hex')\n );\n } catch (error) {\n // timingSafeEqual 在长度不匹配时会抛出异常\n return false;\n }\n }\n\n /**\n * 验证时间戳是否在有效期内\n * \n * 用于防止重放攻击,确保请求是最近发送的\n * \n * @param timestamp - 时间戳(毫秒)\n * @param maxAge - 最大有效期(毫秒),默认 5 分钟\n * @returns 时间戳是否有效\n * \n * @example\n * ```typescript\n * const timestamp = parseInt(req.headers['x-sync-timestamp']);\n * \n * // 验证时间戳是否在 5 分钟内\n * if (!InfoSyncSignature.verifyTimestamp(timestamp, 5 * 60 * 1000)) {\n * return res.status(401).json({ error: 'Timestamp expired' });\n * }\n * ```\n */\n public static verifyTimestamp(\n timestamp: number,\n maxAge: number = 5 * 60 * 1000\n ): boolean {\n const now = Date.now();\n return Math.abs(now - timestamp) <= maxAge;\n }\n\n /**\n * 完整的 Webhook 请求验证\n * \n * 同时验证签名和时间戳,是推荐的验证方式\n * \n * @param secretKey - 密钥\n * @param timestamp - 时间戳(毫秒)\n * @param payload - 负载字符串\n * @param signature - 待验证的签名\n * @param maxAge - 最大有效期(毫秒),默认 5 分钟\n * @returns 验证结果\n * \n * @example\n * ```typescript\n * const result = InfoSyncSignature.verifyWebhookRequest(\n * secretKey,\n * timestamp,\n * payload,\n * signature\n * );\n * \n * if (!result.valid) {\n * return res.status(401).json({ error: result.error });\n * }\n * ```\n */\n public static verifyWebhookRequest(\n secretKey: string,\n timestamp: number,\n payload: string,\n signature: string,\n maxAge: number = 5 * 60 * 1000\n ): { valid: boolean; error?: string } {\n // 1. 验证时间戳\n if (!InfoSyncSignature.verifyTimestamp(timestamp, maxAge)) {\n return {\n valid: false,\n error: 'Timestamp expired or invalid'\n };\n }\n\n // 2. 验证签名\n if (!InfoSyncSignature.verifySignature(secretKey, timestamp, payload, signature)) {\n return {\n valid: false,\n error: 'Invalid signature'\n };\n }\n\n return { valid: true };\n }\n}\n"]}
@@ -1,5 +1,11 @@
1
1
  'use strict';
2
2
 
3
+ var crypto = require('crypto');
4
+
5
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
6
+
7
+ var crypto__default = /*#__PURE__*/_interopDefault(crypto);
8
+
3
9
  // src/common/hooks.ts
4
10
  var EventEmitter = class {
5
11
  constructor() {
@@ -331,8 +337,141 @@ async function safeDynamicImport(moduleName) {
331
337
  throw new Error(`Failed to import module "${moduleName}": ${errorMessage}`);
332
338
  }
333
339
  }
340
+ var InfoSyncSignature = class _InfoSyncSignature {
341
+ /**
342
+ * 生成 HMAC-SHA256 签名
343
+ *
344
+ * 签名算法:
345
+ * - message = `${timestamp}.${payload}`
346
+ * - signature = HMAC-SHA256(secretKey, message)
347
+ *
348
+ * @param secretKey - 密钥(从创建 Webhook 配置时获得)
349
+ * @param timestamp - 时间戳(毫秒)
350
+ * @param payload - 负载字符串(通常是 JSON.stringify 后的请求体)
351
+ * @returns 签名(hex 格式)
352
+ *
353
+ * @example
354
+ * ```typescript
355
+ * const timestamp = Date.now();
356
+ * const payload = JSON.stringify({ foo: 'bar' });
357
+ * const signature = InfoSyncSignature.generateSignature(
358
+ * 'your-secret-key',
359
+ * timestamp,
360
+ * payload
361
+ * );
362
+ * ```
363
+ */
364
+ static generateSignature(secretKey, timestamp, payload) {
365
+ const message = `${timestamp}.${payload}`;
366
+ const signature = crypto__default.default.createHmac("sha256", secretKey).update(message).digest("hex");
367
+ return signature;
368
+ }
369
+ /**
370
+ * 验证 HMAC-SHA256 签名
371
+ *
372
+ * @param secretKey - 密钥
373
+ * @param timestamp - 时间戳(毫秒)
374
+ * @param payload - 负载字符串
375
+ * @param signature - 待验证的签名
376
+ * @returns 签名是否有效
377
+ *
378
+ * @example
379
+ * ```typescript
380
+ * const isValid = InfoSyncSignature.verifySignature(
381
+ * secretKey,
382
+ * timestamp,
383
+ * payload,
384
+ * signature
385
+ * );
386
+ *
387
+ * if (!isValid) {
388
+ * throw new Error('Invalid signature');
389
+ * }
390
+ * ```
391
+ */
392
+ static verifySignature(secretKey, timestamp, payload, signature) {
393
+ try {
394
+ const expected = _InfoSyncSignature.generateSignature(
395
+ secretKey,
396
+ timestamp,
397
+ payload
398
+ );
399
+ return crypto__default.default.timingSafeEqual(
400
+ Buffer.from(expected, "hex"),
401
+ Buffer.from(signature, "hex")
402
+ );
403
+ } catch (error) {
404
+ return false;
405
+ }
406
+ }
407
+ /**
408
+ * 验证时间戳是否在有效期内
409
+ *
410
+ * 用于防止重放攻击,确保请求是最近发送的
411
+ *
412
+ * @param timestamp - 时间戳(毫秒)
413
+ * @param maxAge - 最大有效期(毫秒),默认 5 分钟
414
+ * @returns 时间戳是否有效
415
+ *
416
+ * @example
417
+ * ```typescript
418
+ * const timestamp = parseInt(req.headers['x-sync-timestamp']);
419
+ *
420
+ * // 验证时间戳是否在 5 分钟内
421
+ * if (!InfoSyncSignature.verifyTimestamp(timestamp, 5 * 60 * 1000)) {
422
+ * return res.status(401).json({ error: 'Timestamp expired' });
423
+ * }
424
+ * ```
425
+ */
426
+ static verifyTimestamp(timestamp, maxAge = 5 * 60 * 1e3) {
427
+ const now = Date.now();
428
+ return Math.abs(now - timestamp) <= maxAge;
429
+ }
430
+ /**
431
+ * 完整的 Webhook 请求验证
432
+ *
433
+ * 同时验证签名和时间戳,是推荐的验证方式
434
+ *
435
+ * @param secretKey - 密钥
436
+ * @param timestamp - 时间戳(毫秒)
437
+ * @param payload - 负载字符串
438
+ * @param signature - 待验证的签名
439
+ * @param maxAge - 最大有效期(毫秒),默认 5 分钟
440
+ * @returns 验证结果
441
+ *
442
+ * @example
443
+ * ```typescript
444
+ * const result = InfoSyncSignature.verifyWebhookRequest(
445
+ * secretKey,
446
+ * timestamp,
447
+ * payload,
448
+ * signature
449
+ * );
450
+ *
451
+ * if (!result.valid) {
452
+ * return res.status(401).json({ error: result.error });
453
+ * }
454
+ * ```
455
+ */
456
+ static verifyWebhookRequest(secretKey, timestamp, payload, signature, maxAge = 5 * 60 * 1e3) {
457
+ if (!_InfoSyncSignature.verifyTimestamp(timestamp, maxAge)) {
458
+ return {
459
+ valid: false,
460
+ error: "Timestamp expired or invalid"
461
+ };
462
+ }
463
+ if (!_InfoSyncSignature.verifySignature(secretKey, timestamp, payload, signature)) {
464
+ return {
465
+ valid: false,
466
+ error: "Invalid signature"
467
+ };
468
+ }
469
+ return { valid: true };
470
+ }
471
+ };
334
472
 
335
473
  exports.EventEmitter = EventEmitter;
474
+ exports.InfoSyncSignature = InfoSyncSignature;
336
475
  exports.TypedEventEmitter = TypedEventEmitter;
337
476
  exports.assertEnvironment = assertEnvironment;
338
477
  exports.environmentInfo = environmentInfo;
@@ -345,5 +484,5 @@ exports.isNode = isNode;
345
484
  exports.isValidFileInput = isValidFileInput;
346
485
  exports.isWebWorker = isWebWorker;
347
486
  exports.safeDynamicImport = safeDynamicImport;
348
- //# sourceMappingURL=chunk-NS3DJP2O.cjs.map
349
- //# sourceMappingURL=chunk-NS3DJP2O.cjs.map
487
+ //# sourceMappingURL=chunk-WFBN23AH.cjs.map
488
+ //# sourceMappingURL=chunk-WFBN23AH.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/common/hooks.ts","../src/common/adapter.ts","../src/common/signature.ts"],"names":["crypto"],"mappings":"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","file":"chunk-WFBN23AH.cjs","sourcesContent":["/**\n * 事件处理函数类型\n */\ntype EventHandler<T = unknown> = (data: T) => void;\n\n/**\n * 事件处理器映射类型\n */\ntype EventHandlerMap = Map<string, Set<EventHandler>>;\n\n/**\n * 事件发射器基础类\n * \n * @description\n * 提供简单的事件订阅/发布机制,用于业务模块的状态变化通知。\n * 支持类型安全的事件处理,适用于状态变化、进度更新等场景。\n * \n * @example\n * ```typescript\n * // 创建事件发射器\n * const emitter = new EventEmitter();\n * \n * // 订阅事件\n * const unsubscribe = emitter.on('status-change', (data) => {\n * console.log('Status changed:', data);\n * });\n * \n * // 触发事件\n * emitter.emit('status-change', { status: 'completed' });\n * \n * // 取消订阅\n * unsubscribe();\n * \n * // 一次性事件\n * emitter.once('complete', (data) => {\n * console.log('Completed:', data);\n * });\n * ```\n */\nexport class EventEmitter {\n /**\n * 事件处理器映射表\n */\n private handlers: EventHandlerMap = new Map();\n\n /**\n * 一次性事件处理器集合\n */\n private onceHandlers: EventHandlerMap = new Map();\n\n /**\n * 订阅事件\n * \n * @param event - 事件名称\n * @param handler - 事件处理函数\n * @returns 取消订阅的函数\n * \n * @example\n * ```typescript\n * const unsubscribe = emitter.on('data', (payload) => {\n * console.log('Received:', payload);\n * });\n * \n * // 不再需要时取消订阅\n * unsubscribe();\n * ```\n */\n public on<T = unknown>(event: string, handler: EventHandler<T>): () => void {\n if (!this.handlers.has(event)) {\n this.handlers.set(event, new Set());\n }\n\n const handlers = this.handlers.get(event)!;\n handlers.add(handler as EventHandler);\n\n // 返回取消订阅函数\n return () => {\n handlers.delete(handler as EventHandler);\n if (handlers.size === 0) {\n this.handlers.delete(event);\n }\n };\n }\n\n /**\n * 订阅一次性事件\n * \n * @description\n * 事件处理函数只会执行一次,执行后自动取消订阅。\n * \n * @param event - 事件名称\n * @param handler - 事件处理函数\n * @returns 取消订阅的函数\n * \n * @example\n * ```typescript\n * emitter.once('complete', (result) => {\n * console.log('Task completed:', result);\n * });\n * ```\n */\n public once<T = unknown>(event: string, handler: EventHandler<T>): () => void {\n if (!this.onceHandlers.has(event)) {\n this.onceHandlers.set(event, new Set());\n }\n\n const handlers = this.onceHandlers.get(event)!;\n handlers.add(handler as EventHandler);\n\n // 返回取消订阅函数\n return () => {\n handlers.delete(handler as EventHandler);\n if (handlers.size === 0) {\n this.onceHandlers.delete(event);\n }\n };\n }\n\n /**\n * 触发事件\n * \n * @param event - 事件名称\n * @param data - 传递给处理函数的数据\n * \n * @example\n * ```typescript\n * emitter.emit('status-change', { \n * from: 'pending', \n * to: 'completed' \n * });\n * ```\n */\n public emit<T = unknown>(event: string, data: T): void {\n // 执行常规事件处理器\n const handlers = this.handlers.get(event);\n if (handlers) {\n handlers.forEach((handler) => {\n try {\n handler(data);\n } catch (error) {\n console.error(`Error in event handler for \"${event}\":`, error);\n }\n });\n }\n\n // 执行一次性事件处理器\n const onceHandlers = this.onceHandlers.get(event);\n if (onceHandlers) {\n const handlersToExecute = Array.from(onceHandlers);\n // 清空一次性处理器\n this.onceHandlers.delete(event);\n\n handlersToExecute.forEach((handler) => {\n try {\n handler(data);\n } catch (error) {\n console.error(`Error in once event handler for \"${event}\":`, error);\n }\n });\n }\n }\n\n /**\n * 取消订阅指定事件的处理器\n * \n * @param event - 事件名称\n * @param handler - 可选,要取消的事件处理函数。如果不提供,则取消所有处理器\n * \n * @example\n * ```typescript\n * // 取消所有处理器\n * emitter.off('status-change');\n * \n * // 取消特定处理器\n * const handler = (data) => console.log(data);\n * emitter.on('data', handler);\n * emitter.off('data', handler);\n * ```\n */\n public off<T = unknown>(event: string, handler?: EventHandler<T>): void {\n if (!handler) {\n // 取消所有处理器\n this.handlers.delete(event);\n this.onceHandlers.delete(event);\n return;\n }\n\n // 取消特定处理器\n const handlers = this.handlers.get(event);\n if (handlers) {\n handlers.delete(handler as EventHandler);\n if (handlers.size === 0) {\n this.handlers.delete(event);\n }\n }\n\n const onceHandlers = this.onceHandlers.get(event);\n if (onceHandlers) {\n onceHandlers.delete(handler as EventHandler);\n if (onceHandlers.size === 0) {\n this.onceHandlers.delete(event);\n }\n }\n }\n\n /**\n * 清除所有事件处理器\n * \n * @example\n * ```typescript\n * emitter.clear();\n * ```\n */\n public clear(): void {\n this.handlers.clear();\n this.onceHandlers.clear();\n }\n\n /**\n * 获取指定事件的处理器数量\n * \n * @param event - 事件名称\n * @returns 处理器数量\n * \n * @example\n * ```typescript\n * const count = emitter.listenerCount('status-change');\n * console.log(`${count} listeners registered`);\n * ```\n */\n public listenerCount(event: string): number {\n const regularCount = this.handlers.get(event)?.size || 0;\n const onceCount = this.onceHandlers.get(event)?.size || 0;\n return regularCount + onceCount;\n }\n\n /**\n * 获取所有已注册的事件名称\n * \n * @returns 事件名称数组\n * \n * @example\n * ```typescript\n * const events = emitter.eventNames();\n * console.log('Registered events:', events);\n * ```\n */\n public eventNames(): string[] {\n const regularEvents = Array.from(this.handlers.keys());\n const onceEvents = Array.from(this.onceHandlers.keys());\n return Array.from(new Set([...regularEvents, ...onceEvents]));\n }\n}\n\n/**\n * 类型安全的事件发射器\n * \n * @description\n * 提供类型约束的事件发射器,确保事件名称和数据类型的正确性。\n * \n * @template TEvents - 事件映射类型\n * \n * @example\n * ```typescript\n * // 定义事件类型\n * interface TaskEvents {\n * 'status-change': { status: string };\n * 'progress': { percent: number };\n * 'error': { message: string };\n * }\n * \n * // 创建类型安全的发射器\n * const emitter = new TypedEventEmitter<TaskEvents>();\n * \n * // TypeScript 会检查事件名称和数据类型\n * emitter.on('status-change', (data) => {\n * console.log(data.status); // TypeScript 知道 data 有 status 属性\n * });\n * \n * emitter.emit('status-change', { status: 'completed' }); // OK\n * emitter.emit('status-change', { wrong: 'data' }); // TypeScript 报错\n * ```\n */\nexport class TypedEventEmitter<TEvents extends Record<string, unknown>> {\n private emitter = new EventEmitter();\n\n /**\n * 订阅事件(类型安全)\n */\n public on<K extends keyof TEvents>(\n event: K,\n handler: EventHandler<TEvents[K]>\n ): () => void {\n return this.emitter.on(event as string, handler);\n }\n\n /**\n * 订阅一次性事件(类型安全)\n */\n public once<K extends keyof TEvents>(\n event: K,\n handler: EventHandler<TEvents[K]>\n ): () => void {\n return this.emitter.once(event as string, handler);\n }\n\n /**\n * 触发事件(类型安全)\n */\n public emit<K extends keyof TEvents>(event: K, data: TEvents[K]): void {\n this.emitter.emit(event as string, data);\n }\n\n /**\n * 取消订阅(类型安全)\n */\n public off<K extends keyof TEvents>(event: K, handler?: EventHandler<TEvents[K]>): void {\n if (handler) {\n this.emitter.off(event as string, handler);\n } else {\n this.emitter.off(event as string);\n }\n }\n\n /**\n * 清除所有事件处理器\n */\n public clear(): void {\n this.emitter.clear();\n }\n\n /**\n * 获取指定事件的处理器数量\n */\n public listenerCount<K extends keyof TEvents>(event: K): number {\n return this.emitter.listenerCount(event as string);\n }\n\n /**\n * 获取所有已注册的事件名称\n */\n public eventNames(): Array<keyof TEvents> {\n return this.emitter.eventNames() as Array<keyof TEvents>;\n }\n}\n\n","/**\n * 环境适配工具\n * \n * @description\n * 提供跨环境(Node.js / 浏览器)的工具函数和类型检测。\n * 用于处理文件上传、网络请求等在不同环境下有差异的功能。\n */\n\n/**\n * 检测当前是否运行在 Node.js 环境\n * \n * @returns 如果在 Node.js 环境返回 true,否则返回 false\n * \n * @example\n * ```typescript\n * if (isNode) {\n * // 使用 Node.js 特有的 API\n * const fs = require('fs');\n * }\n * ```\n */\nexport const isNode: boolean =\n typeof process !== 'undefined' &&\n process.versions !== null &&\n process.versions.node !== null;\n\n/**\n * 检测当前是否运行在浏览器环境\n * \n * @returns 如果在浏览器环境返回 true,否则返回 false\n * \n * @example\n * ```typescript\n * if (isBrowser) {\n * // 使用浏览器 API\n * const file = new File(['content'], 'file.txt');\n * }\n * ```\n */\nexport const isBrowser: boolean =\n typeof window !== 'undefined' && typeof window.document !== 'undefined';\n\n/**\n * 检测当前是否支持 Web Worker\n * \n * @returns 如果支持 Web Worker 返回 true,否则返回 false\n */\nexport const isWebWorker: boolean =\n typeof self === 'object' &&\n // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access\n typeof (self as any).importScripts === 'function';\n\n/**\n * 获取运行环境类型\n * \n * @returns 环境类型字符串\n * \n * @example\n * ```typescript\n * const env = getEnvironment();\n * console.log(`Running in ${env}`); // \"node\" 或 \"browser\" 或 \"worker\"\n * ```\n */\nexport function getEnvironment(): 'node' | 'browser' | 'worker' | 'unknown' {\n if (isNode) return 'node';\n if (isWebWorker) return 'worker';\n if (isBrowser) return 'browser';\n return 'unknown';\n}\n\n/**\n * 文件类型联合类型\n * 在 Node.js 中是 Buffer,在浏览器中是 Blob 或 File\n */\nexport type FileInput = Buffer | Blob | File;\n\n/**\n * 检测是否为有效的文件输入\n * \n * @param input - 待检测的输入\n * @returns 如果是有效的文件输入返回 true\n * \n * @example\n * ```typescript\n * const file = new File(['content'], 'test.txt');\n * if (isValidFileInput(file)) {\n * // 处理文件\n * }\n * ```\n */\nexport function isValidFileInput(input: unknown): input is FileInput {\n if (isNode) {\n // Node.js 环境:检查是否为 Buffer\n return Buffer.isBuffer(input);\n }\n\n if (isBrowser) {\n // 浏览器环境:检查是否为 Blob 或 File\n return input instanceof Blob || input instanceof File;\n }\n\n return false;\n}\n\n/**\n * 获取文件输入的大小(字节)\n * \n * @param input - 文件输入\n * @returns 文件大小(字节)\n * @throws {Error} 如果输入类型不支持\n * \n * @example\n * ```typescript\n * const file = new File(['content'], 'test.txt');\n * const size = getFileSize(file);\n * console.log(`File size: ${size} bytes`);\n * ```\n */\nexport function getFileSize(input: FileInput): number {\n if (isNode && Buffer.isBuffer(input)) {\n return input.length;\n }\n\n if ((isBrowser || isWebWorker) && (input instanceof Blob || input instanceof File)) {\n return input.size;\n }\n\n throw new Error('Unsupported file input type');\n}\n\n/**\n * 获取文件名(仅适用于 File 对象)\n * \n * @param input - 文件输入\n * @returns 文件名,如果无法获取则返回 undefined\n * \n * @example\n * ```typescript\n * const file = new File(['content'], 'test.txt');\n * const name = getFileName(file);\n * console.log(name); // \"test.txt\"\n * ```\n */\nexport function getFileName(input: FileInput): string | undefined {\n if (input instanceof File) {\n return input.name;\n }\n return undefined;\n}\n\n/**\n * 环境特定功能检测\n */\nexport const features = {\n /**\n * 是否支持 FormData\n */\n hasFormData: typeof FormData !== 'undefined',\n\n /**\n * 是否支持 Blob\n */\n hasBlob: typeof Blob !== 'undefined',\n\n /**\n * 是否支持 File\n */\n hasFile: typeof File !== 'undefined',\n\n /**\n * 是否支持 FileReader\n */\n hasFileReader: typeof FileReader !== 'undefined',\n\n /**\n * 是否支持 fetch API\n */\n hasFetch: typeof fetch !== 'undefined',\n\n /**\n * 是否支持 Stream API\n */\n hasStream: typeof ReadableStream !== 'undefined',\n} as const;\n\n/**\n * 环境信息对象\n * \n * @example\n * ```typescript\n * console.log(environmentInfo);\n * // {\n * // type: 'browser',\n * // isNode: false,\n * // isBrowser: true,\n * // features: { ... }\n * // }\n * ```\n */\nexport const environmentInfo = {\n type: getEnvironment(),\n isNode,\n isBrowser,\n isWebWorker,\n features,\n} as const;\n\n/**\n * 断言当前环境\n * \n * @param expectedEnv - 期望的环境类型\n * @param message - 自定义错误消息\n * @throws {Error} 如果当前环境不符合预期\n * \n * @example\n * ```typescript\n * // 确保在 Node.js 环境中运行\n * assertEnvironment('node', 'This feature requires Node.js');\n * ```\n */\nexport function assertEnvironment(\n expectedEnv: 'node' | 'browser' | 'worker',\n message?: string\n): void {\n const currentEnv = getEnvironment();\n if (currentEnv !== expectedEnv) {\n throw new Error(\n message ||\n `This feature requires ${expectedEnv} environment, but running in ${currentEnv}`\n );\n }\n}\n\n/**\n * 安全地动态导入模块(仅 Node.js)\n * \n * @description\n * 在 Node.js 环境中动态导入模块,避免在浏览器环境中引起错误。\n * \n * @param moduleName - 模块名称\n * @returns Promise,resolve 为模块对象\n * @throws {Error} 如果不在 Node.js 环境或模块加载失败\n * \n * @example\n * ```typescript\n * if (isNode) {\n * const fs = await safeDynamicImport('fs');\n * const content = fs.readFileSync('file.txt', 'utf-8');\n * }\n * ```\n */\nexport async function safeDynamicImport<T = unknown>(moduleName: string): Promise<T> {\n assertEnvironment('node', `Cannot import module \"${moduleName}\" in non-Node.js environment`);\n \n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return await import(moduleName);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to import module \"${moduleName}\": ${errorMessage}`);\n }\n}\n\n","import crypto from 'crypto';\n\n/**\n * 信息同步签名工具\n * \n * 用于生成和验证 Webhook 请求的 HMAC-SHA256 签名\n * \n * @example\n * ```typescript\n * // 验证 Webhook 签名\n * const timestamp = parseInt(req.headers['x-sync-timestamp']);\n * const signature = req.headers['x-sync-signature'];\n * const payload = JSON.stringify(req.body);\n * \n * const isValid = InfoSyncSignature.verifySignature(\n * secretKey,\n * timestamp,\n * payload,\n * signature\n * );\n * \n * if (!isValid) {\n * return res.status(401).json({ error: 'Invalid signature' });\n * }\n * ```\n */\nexport class InfoSyncSignature {\n /**\n * 生成 HMAC-SHA256 签名\n * \n * 签名算法:\n * - message = `${timestamp}.${payload}`\n * - signature = HMAC-SHA256(secretKey, message)\n * \n * @param secretKey - 密钥(从创建 Webhook 配置时获得)\n * @param timestamp - 时间戳(毫秒)\n * @param payload - 负载字符串(通常是 JSON.stringify 后的请求体)\n * @returns 签名(hex 格式)\n * \n * @example\n * ```typescript\n * const timestamp = Date.now();\n * const payload = JSON.stringify({ foo: 'bar' });\n * const signature = InfoSyncSignature.generateSignature(\n * 'your-secret-key',\n * timestamp,\n * payload\n * );\n * ```\n */\n public static generateSignature(\n secretKey: string,\n timestamp: number,\n payload: string\n ): string {\n const message = `${timestamp}.${payload}`;\n const signature = crypto\n .createHmac('sha256', secretKey)\n .update(message)\n .digest('hex');\n return signature;\n }\n\n /**\n * 验证 HMAC-SHA256 签名\n * \n * @param secretKey - 密钥\n * @param timestamp - 时间戳(毫秒)\n * @param payload - 负载字符串\n * @param signature - 待验证的签名\n * @returns 签名是否有效\n * \n * @example\n * ```typescript\n * const isValid = InfoSyncSignature.verifySignature(\n * secretKey,\n * timestamp,\n * payload,\n * signature\n * );\n * \n * if (!isValid) {\n * throw new Error('Invalid signature');\n * }\n * ```\n */\n public static verifySignature(\n secretKey: string,\n timestamp: number,\n payload: string,\n signature: string\n ): boolean {\n try {\n const expected = InfoSyncSignature.generateSignature(\n secretKey,\n timestamp,\n payload\n );\n \n // 使用时间安全的比较方法,防止时序攻击\n return crypto.timingSafeEqual(\n Buffer.from(expected, 'hex'),\n Buffer.from(signature, 'hex')\n );\n } catch (error) {\n // timingSafeEqual 在长度不匹配时会抛出异常\n return false;\n }\n }\n\n /**\n * 验证时间戳是否在有效期内\n * \n * 用于防止重放攻击,确保请求是最近发送的\n * \n * @param timestamp - 时间戳(毫秒)\n * @param maxAge - 最大有效期(毫秒),默认 5 分钟\n * @returns 时间戳是否有效\n * \n * @example\n * ```typescript\n * const timestamp = parseInt(req.headers['x-sync-timestamp']);\n * \n * // 验证时间戳是否在 5 分钟内\n * if (!InfoSyncSignature.verifyTimestamp(timestamp, 5 * 60 * 1000)) {\n * return res.status(401).json({ error: 'Timestamp expired' });\n * }\n * ```\n */\n public static verifyTimestamp(\n timestamp: number,\n maxAge: number = 5 * 60 * 1000\n ): boolean {\n const now = Date.now();\n return Math.abs(now - timestamp) <= maxAge;\n }\n\n /**\n * 完整的 Webhook 请求验证\n * \n * 同时验证签名和时间戳,是推荐的验证方式\n * \n * @param secretKey - 密钥\n * @param timestamp - 时间戳(毫秒)\n * @param payload - 负载字符串\n * @param signature - 待验证的签名\n * @param maxAge - 最大有效期(毫秒),默认 5 分钟\n * @returns 验证结果\n * \n * @example\n * ```typescript\n * const result = InfoSyncSignature.verifyWebhookRequest(\n * secretKey,\n * timestamp,\n * payload,\n * signature\n * );\n * \n * if (!result.valid) {\n * return res.status(401).json({ error: result.error });\n * }\n * ```\n */\n public static verifyWebhookRequest(\n secretKey: string,\n timestamp: number,\n payload: string,\n signature: string,\n maxAge: number = 5 * 60 * 1000\n ): { valid: boolean; error?: string } {\n // 1. 验证时间戳\n if (!InfoSyncSignature.verifyTimestamp(timestamp, maxAge)) {\n return {\n valid: false,\n error: 'Timestamp expired or invalid'\n };\n }\n\n // 2. 验证签名\n if (!InfoSyncSignature.verifySignature(secretKey, timestamp, payload, signature)) {\n return {\n valid: false,\n error: 'Invalid signature'\n };\n }\n\n return { valid: true };\n }\n}\n"]}