@plolink/sdk 0.0.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.
Files changed (76) hide show
  1. package/README.md +259 -0
  2. package/dist/chunk-4H4RACSE.js +335 -0
  3. package/dist/chunk-4H4RACSE.js.map +1 -0
  4. package/dist/chunk-IHAAKFEJ.js +219 -0
  5. package/dist/chunk-IHAAKFEJ.js.map +1 -0
  6. package/dist/chunk-JR4HYYQI.cjs +221 -0
  7. package/dist/chunk-JR4HYYQI.cjs.map +1 -0
  8. package/dist/chunk-MD4O7FWT.js +46 -0
  9. package/dist/chunk-MD4O7FWT.js.map +1 -0
  10. package/dist/chunk-NS3DJP2O.cjs +349 -0
  11. package/dist/chunk-NS3DJP2O.cjs.map +1 -0
  12. package/dist/chunk-Y3UJVC2L.cjs +48 -0
  13. package/dist/chunk-Y3UJVC2L.cjs.map +1 -0
  14. package/dist/client-CAjIQKPm.d.cts +193 -0
  15. package/dist/client-CwNikk7i.d.ts +193 -0
  16. package/dist/common/index.cjs +65 -0
  17. package/dist/common/index.cjs.map +1 -0
  18. package/dist/common/index.d.cts +422 -0
  19. package/dist/common/index.d.ts +422 -0
  20. package/dist/common/index.js +4 -0
  21. package/dist/common/index.js.map +1 -0
  22. package/dist/core-77EbLgbp.d.cts +97 -0
  23. package/dist/core-77EbLgbp.d.ts +97 -0
  24. package/dist/index.cjs +350 -0
  25. package/dist/index.cjs.map +1 -0
  26. package/dist/index.d.cts +98 -0
  27. package/dist/index.d.ts +98 -0
  28. package/dist/index.js +306 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/modules/agent/index.cjs +21 -0
  31. package/dist/modules/agent/index.cjs.map +1 -0
  32. package/dist/modules/agent/index.d.cts +71 -0
  33. package/dist/modules/agent/index.d.ts +71 -0
  34. package/dist/modules/agent/index.js +19 -0
  35. package/dist/modules/agent/index.js.map +1 -0
  36. package/dist/modules/billing/index.cjs +413 -0
  37. package/dist/modules/billing/index.cjs.map +1 -0
  38. package/dist/modules/billing/index.d.cts +538 -0
  39. package/dist/modules/billing/index.d.ts +538 -0
  40. package/dist/modules/billing/index.js +411 -0
  41. package/dist/modules/billing/index.js.map +1 -0
  42. package/dist/modules/chat/index.cjs +20 -0
  43. package/dist/modules/chat/index.cjs.map +1 -0
  44. package/dist/modules/chat/index.d.cts +68 -0
  45. package/dist/modules/chat/index.d.ts +68 -0
  46. package/dist/modules/chat/index.js +18 -0
  47. package/dist/modules/chat/index.js.map +1 -0
  48. package/dist/modules/psych/index.cjs +20 -0
  49. package/dist/modules/psych/index.cjs.map +1 -0
  50. package/dist/modules/psych/index.d.cts +69 -0
  51. package/dist/modules/psych/index.d.ts +69 -0
  52. package/dist/modules/psych/index.js +18 -0
  53. package/dist/modules/psych/index.js.map +1 -0
  54. package/dist/modules/rbac/index.cjs +197 -0
  55. package/dist/modules/rbac/index.cjs.map +1 -0
  56. package/dist/modules/rbac/index.d.cts +293 -0
  57. package/dist/modules/rbac/index.d.ts +293 -0
  58. package/dist/modules/rbac/index.js +195 -0
  59. package/dist/modules/rbac/index.js.map +1 -0
  60. package/dist/modules/team/index.cjs +54 -0
  61. package/dist/modules/team/index.cjs.map +1 -0
  62. package/dist/modules/team/index.d.cts +91 -0
  63. package/dist/modules/team/index.d.ts +91 -0
  64. package/dist/modules/team/index.js +52 -0
  65. package/dist/modules/team/index.js.map +1 -0
  66. package/dist/modules/virtual-account/index.cjs +293 -0
  67. package/dist/modules/virtual-account/index.cjs.map +1 -0
  68. package/dist/modules/virtual-account/index.d.cts +366 -0
  69. package/dist/modules/virtual-account/index.d.ts +366 -0
  70. package/dist/modules/virtual-account/index.js +291 -0
  71. package/dist/modules/virtual-account/index.js.map +1 -0
  72. package/dist/poller-BlIRbwL4.d.cts +201 -0
  73. package/dist/poller-DWKZjuSw.d.ts +201 -0
  74. package/dist/shared-6ZepUSPW.d.cts +31 -0
  75. package/dist/shared-6ZepUSPW.d.ts +31 -0
  76. package/package.json +96 -0
package/README.md ADDED
@@ -0,0 +1,259 @@
1
+ # @plolink/sdk
2
+
3
+ Plolink 开放平台官方 SDK,提供完整的 API 调用能力。
4
+
5
+ ## 特性
6
+
7
+ - 🔐 **统一认证**:使用 Bearer Token 认证,支持 ApiKey 和 SessionId
8
+ - 🔄 **智能轮询**:内置指数退避轮询机制,自动处理异步任务
9
+ - 📦 **分包导入**:支持按需导入,减少打包体积
10
+ - 🌍 **跨环境**:同时支持 Node.js 和浏览器环境
11
+ - 📝 **类型安全**:完整的 TypeScript 类型定义
12
+ - 🔌 **可扩展**:支持自定义日志钩子,轻松集成 Sentry 等监控系统
13
+ - 📚 **文档齐全**:代码即文档,完整的 TSDoc 注释
14
+
15
+ ## 安装
16
+
17
+ ```bash
18
+ npm install @plolink/sdk
19
+ # 或
20
+ pnpm add @plolink/sdk
21
+ # 或
22
+ yarn add @plolink/sdk
23
+ ```
24
+
25
+ ## 快速开始
26
+
27
+ ### 使用 ApiKey
28
+
29
+ ```typescript
30
+ import { PlolinkClient } from '@plolink/sdk';
31
+
32
+ const client = new PlolinkClient({
33
+ token: 'pl_your-api-key',
34
+ logger: (level, message, data) => {
35
+ console.log(`[${level}] ${message}`, data);
36
+ }
37
+ });
38
+
39
+ // 检查健康状态
40
+ const health = client.healthCheck();
41
+ console.log(health);
42
+ ```
43
+
44
+ ### 使用 SessionId(登录后获取)
45
+
46
+ ```typescript
47
+ import { PlolinkClient } from '@plolink/sdk';
48
+
49
+ // 先登录获取 sessionId
50
+ const loginResponse = await fetch('https://api.plolink.com/api/v1/auth/login/password', {
51
+ method: 'POST',
52
+ headers: { 'Content-Type': 'application/json' },
53
+ body: JSON.stringify({ email: 'user@example.com', password: 'password' })
54
+ });
55
+ const { sessionId } = await loginResponse.json();
56
+
57
+ // 使用 sessionId 创建客户端
58
+ const client = new PlolinkClient({
59
+ token: sessionId,
60
+ baseUrl: 'https://api.plolink.com'
61
+ });
62
+ ```
63
+
64
+ ## 业务模块
65
+
66
+ SDK 提供多个业务模块,支持按需导入:
67
+
68
+ ### 充值消费模块 (Billing)
69
+
70
+ ```typescript
71
+ import { Billing } from '@plolink/sdk/billing';
72
+
73
+ const billing = new Billing(client);
74
+ const balance = await billing.getBalance();
75
+ ```
76
+
77
+ ### 虚拟账号模块 (VirtualAccount)
78
+
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
+ }]
94
+ });
95
+
96
+ // 管理 API Keys
97
+ const keys = await virtualAccount.listVirtualAccountApiKeys(result.virtualAccount.id);
98
+ ```
99
+
100
+ 详细文档请参考 docs/modules/
101
+
102
+ ## 高级功能
103
+
104
+ ### 自定义日志
105
+
106
+ ```typescript
107
+ import { PlolinkClient } from '@plolink/sdk';
108
+ import * as Sentry from '@sentry/node';
109
+
110
+ const client = new PlolinkClient({
111
+ token: 'pl_your-api-key',
112
+ logger: (level, message, data) => {
113
+ if (level === 'error') {
114
+ Sentry.captureMessage(message, {
115
+ level: 'error',
116
+ extra: data
117
+ });
118
+ }
119
+ }
120
+ });
121
+ ```
122
+
123
+ ### 使用轮询器
124
+
125
+ ```typescript
126
+ import { Poller } from '@plolink/sdk';
127
+
128
+ const poller = new Poller({
129
+ task: () => fetch('/api/order/123').then(r => r.json()),
130
+ onResult: (data) => {
131
+ console.log('Order status:', data.status);
132
+ },
133
+ shouldStop: (data) => data.status === 'success',
134
+ initialInterval: 1000,
135
+ maxInterval: 30000,
136
+ backoffFactor: 1.5
137
+ });
138
+
139
+ // 启动轮询
140
+ poller.start();
141
+
142
+ // 停止轮询
143
+ poller.stop();
144
+ ```
145
+
146
+ ### 使用事件发射器
147
+
148
+ ```typescript
149
+ import { EventEmitter } from '@plolink/sdk';
150
+
151
+ const emitter = new EventEmitter();
152
+
153
+ // 订阅事件
154
+ const unsubscribe = emitter.on('status-change', (data) => {
155
+ console.log('Status changed:', data);
156
+ });
157
+
158
+ // 触发事件
159
+ emitter.emit('status-change', { status: 'completed' });
160
+
161
+ // 取消订阅
162
+ unsubscribe();
163
+ ```
164
+
165
+ ## 配置选项
166
+
167
+ ```typescript
168
+ interface SDKConfig {
169
+ /**
170
+ * API 基础 URL
171
+ * @default 'https://hiring.djangotech.com/'
172
+ */
173
+ baseUrl?: string;
174
+
175
+ /**
176
+ * 认证 Token (ApiKey 或 SessionId)
177
+ * - ApiKey: pl_ 开头的字符串
178
+ * - SessionId: 登录后返回的 UUID 格式字符串
179
+ */
180
+ token: string;
181
+
182
+ /**
183
+ * 自定义日志钩子函数
184
+ */
185
+ logger?: (level: string, message: string, data?: any) => void;
186
+
187
+ /**
188
+ * 请求超时时间(毫秒)
189
+ * @default 10000
190
+ */
191
+ timeout?: number;
192
+ }
193
+ ```
194
+
195
+ ## 错误处理
196
+
197
+ 所有错误都会被转换为 `PlolinkError`:
198
+
199
+ ```typescript
200
+ import { PlolinkClient, PlolinkError } from '@plolink/sdk';
201
+
202
+ try {
203
+ const result = await client.someMethod();
204
+ } catch (error) {
205
+ if (error instanceof PlolinkError) {
206
+ console.error(`Error ${error.code}: ${error.message}`);
207
+ console.error('Additional data:', error.data);
208
+ }
209
+ }
210
+ ```
211
+
212
+ ## 环境检测
213
+
214
+ ```typescript
215
+ import { isNode, isBrowser, getEnvironment } from '@plolink/sdk';
216
+
217
+ if (isNode) {
218
+ console.log('Running in Node.js');
219
+ }
220
+
221
+ if (isBrowser) {
222
+ console.log('Running in browser');
223
+ }
224
+
225
+ console.log('Environment:', getEnvironment());
226
+ ```
227
+
228
+ ## 开发
229
+
230
+ ### 构建
231
+
232
+ ```bash
233
+ pnpm install
234
+ pnpm build
235
+ ```
236
+
237
+ ### 测试
238
+
239
+ ```bash
240
+ pnpm test
241
+ pnpm test:watch
242
+ ```
243
+
244
+ ### 生成文档
245
+
246
+ ```bash
247
+ pnpm docs
248
+ ```
249
+
250
+ ### 类型检查
251
+
252
+ ```bash
253
+ pnpm typecheck
254
+ ```
255
+
256
+ ## 技术支持
257
+
258
+ - 📧 Email: admin@wj2015.com
259
+
@@ -0,0 +1,335 @@
1
+ // src/common/hooks.ts
2
+ var EventEmitter = class {
3
+ constructor() {
4
+ /**
5
+ * 事件处理器映射表
6
+ */
7
+ this.handlers = /* @__PURE__ */ new Map();
8
+ /**
9
+ * 一次性事件处理器集合
10
+ */
11
+ this.onceHandlers = /* @__PURE__ */ new Map();
12
+ }
13
+ /**
14
+ * 订阅事件
15
+ *
16
+ * @param event - 事件名称
17
+ * @param handler - 事件处理函数
18
+ * @returns 取消订阅的函数
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const unsubscribe = emitter.on('data', (payload) => {
23
+ * console.log('Received:', payload);
24
+ * });
25
+ *
26
+ * // 不再需要时取消订阅
27
+ * unsubscribe();
28
+ * ```
29
+ */
30
+ on(event, handler) {
31
+ if (!this.handlers.has(event)) {
32
+ this.handlers.set(event, /* @__PURE__ */ new Set());
33
+ }
34
+ const handlers = this.handlers.get(event);
35
+ handlers.add(handler);
36
+ return () => {
37
+ handlers.delete(handler);
38
+ if (handlers.size === 0) {
39
+ this.handlers.delete(event);
40
+ }
41
+ };
42
+ }
43
+ /**
44
+ * 订阅一次性事件
45
+ *
46
+ * @description
47
+ * 事件处理函数只会执行一次,执行后自动取消订阅。
48
+ *
49
+ * @param event - 事件名称
50
+ * @param handler - 事件处理函数
51
+ * @returns 取消订阅的函数
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * emitter.once('complete', (result) => {
56
+ * console.log('Task completed:', result);
57
+ * });
58
+ * ```
59
+ */
60
+ once(event, handler) {
61
+ if (!this.onceHandlers.has(event)) {
62
+ this.onceHandlers.set(event, /* @__PURE__ */ new Set());
63
+ }
64
+ const handlers = this.onceHandlers.get(event);
65
+ handlers.add(handler);
66
+ return () => {
67
+ handlers.delete(handler);
68
+ if (handlers.size === 0) {
69
+ this.onceHandlers.delete(event);
70
+ }
71
+ };
72
+ }
73
+ /**
74
+ * 触发事件
75
+ *
76
+ * @param event - 事件名称
77
+ * @param data - 传递给处理函数的数据
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * emitter.emit('status-change', {
82
+ * from: 'pending',
83
+ * to: 'completed'
84
+ * });
85
+ * ```
86
+ */
87
+ emit(event, data) {
88
+ const handlers = this.handlers.get(event);
89
+ if (handlers) {
90
+ handlers.forEach((handler) => {
91
+ try {
92
+ handler(data);
93
+ } catch (error) {
94
+ console.error(`Error in event handler for "${event}":`, error);
95
+ }
96
+ });
97
+ }
98
+ const onceHandlers = this.onceHandlers.get(event);
99
+ if (onceHandlers) {
100
+ const handlersToExecute = Array.from(onceHandlers);
101
+ this.onceHandlers.delete(event);
102
+ handlersToExecute.forEach((handler) => {
103
+ try {
104
+ handler(data);
105
+ } catch (error) {
106
+ console.error(`Error in once event handler for "${event}":`, error);
107
+ }
108
+ });
109
+ }
110
+ }
111
+ /**
112
+ * 取消订阅指定事件的处理器
113
+ *
114
+ * @param event - 事件名称
115
+ * @param handler - 可选,要取消的事件处理函数。如果不提供,则取消所有处理器
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * // 取消所有处理器
120
+ * emitter.off('status-change');
121
+ *
122
+ * // 取消特定处理器
123
+ * const handler = (data) => console.log(data);
124
+ * emitter.on('data', handler);
125
+ * emitter.off('data', handler);
126
+ * ```
127
+ */
128
+ off(event, handler) {
129
+ if (!handler) {
130
+ this.handlers.delete(event);
131
+ this.onceHandlers.delete(event);
132
+ return;
133
+ }
134
+ const handlers = this.handlers.get(event);
135
+ if (handlers) {
136
+ handlers.delete(handler);
137
+ if (handlers.size === 0) {
138
+ this.handlers.delete(event);
139
+ }
140
+ }
141
+ const onceHandlers = this.onceHandlers.get(event);
142
+ if (onceHandlers) {
143
+ onceHandlers.delete(handler);
144
+ if (onceHandlers.size === 0) {
145
+ this.onceHandlers.delete(event);
146
+ }
147
+ }
148
+ }
149
+ /**
150
+ * 清除所有事件处理器
151
+ *
152
+ * @example
153
+ * ```typescript
154
+ * emitter.clear();
155
+ * ```
156
+ */
157
+ clear() {
158
+ this.handlers.clear();
159
+ this.onceHandlers.clear();
160
+ }
161
+ /**
162
+ * 获取指定事件的处理器数量
163
+ *
164
+ * @param event - 事件名称
165
+ * @returns 处理器数量
166
+ *
167
+ * @example
168
+ * ```typescript
169
+ * const count = emitter.listenerCount('status-change');
170
+ * console.log(`${count} listeners registered`);
171
+ * ```
172
+ */
173
+ listenerCount(event) {
174
+ const regularCount = this.handlers.get(event)?.size || 0;
175
+ const onceCount = this.onceHandlers.get(event)?.size || 0;
176
+ return regularCount + onceCount;
177
+ }
178
+ /**
179
+ * 获取所有已注册的事件名称
180
+ *
181
+ * @returns 事件名称数组
182
+ *
183
+ * @example
184
+ * ```typescript
185
+ * const events = emitter.eventNames();
186
+ * console.log('Registered events:', events);
187
+ * ```
188
+ */
189
+ eventNames() {
190
+ const regularEvents = Array.from(this.handlers.keys());
191
+ const onceEvents = Array.from(this.onceHandlers.keys());
192
+ return Array.from(/* @__PURE__ */ new Set([...regularEvents, ...onceEvents]));
193
+ }
194
+ };
195
+ var TypedEventEmitter = class {
196
+ constructor() {
197
+ this.emitter = new EventEmitter();
198
+ }
199
+ /**
200
+ * 订阅事件(类型安全)
201
+ */
202
+ on(event, handler) {
203
+ return this.emitter.on(event, handler);
204
+ }
205
+ /**
206
+ * 订阅一次性事件(类型安全)
207
+ */
208
+ once(event, handler) {
209
+ return this.emitter.once(event, handler);
210
+ }
211
+ /**
212
+ * 触发事件(类型安全)
213
+ */
214
+ emit(event, data) {
215
+ this.emitter.emit(event, data);
216
+ }
217
+ /**
218
+ * 取消订阅(类型安全)
219
+ */
220
+ off(event, handler) {
221
+ if (handler) {
222
+ this.emitter.off(event, handler);
223
+ } else {
224
+ this.emitter.off(event);
225
+ }
226
+ }
227
+ /**
228
+ * 清除所有事件处理器
229
+ */
230
+ clear() {
231
+ this.emitter.clear();
232
+ }
233
+ /**
234
+ * 获取指定事件的处理器数量
235
+ */
236
+ listenerCount(event) {
237
+ return this.emitter.listenerCount(event);
238
+ }
239
+ /**
240
+ * 获取所有已注册的事件名称
241
+ */
242
+ eventNames() {
243
+ return this.emitter.eventNames();
244
+ }
245
+ };
246
+
247
+ // src/common/adapter.ts
248
+ var isNode = typeof process !== "undefined" && process.versions !== null && process.versions.node !== null;
249
+ var isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined";
250
+ var isWebWorker = typeof self === "object" && // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
251
+ typeof self.importScripts === "function";
252
+ function getEnvironment() {
253
+ if (isNode) return "node";
254
+ if (isWebWorker) return "worker";
255
+ if (isBrowser) return "browser";
256
+ return "unknown";
257
+ }
258
+ function isValidFileInput(input) {
259
+ if (isNode) {
260
+ return Buffer.isBuffer(input);
261
+ }
262
+ if (isBrowser) {
263
+ return input instanceof Blob || input instanceof File;
264
+ }
265
+ return false;
266
+ }
267
+ function getFileSize(input) {
268
+ if (isNode && Buffer.isBuffer(input)) {
269
+ return input.length;
270
+ }
271
+ if ((isBrowser || isWebWorker) && (input instanceof Blob || input instanceof File)) {
272
+ return input.size;
273
+ }
274
+ throw new Error("Unsupported file input type");
275
+ }
276
+ function getFileName(input) {
277
+ if (input instanceof File) {
278
+ return input.name;
279
+ }
280
+ return void 0;
281
+ }
282
+ var features = {
283
+ /**
284
+ * 是否支持 FormData
285
+ */
286
+ hasFormData: typeof FormData !== "undefined",
287
+ /**
288
+ * 是否支持 Blob
289
+ */
290
+ hasBlob: typeof Blob !== "undefined",
291
+ /**
292
+ * 是否支持 File
293
+ */
294
+ hasFile: typeof File !== "undefined",
295
+ /**
296
+ * 是否支持 FileReader
297
+ */
298
+ hasFileReader: typeof FileReader !== "undefined",
299
+ /**
300
+ * 是否支持 fetch API
301
+ */
302
+ hasFetch: typeof fetch !== "undefined",
303
+ /**
304
+ * 是否支持 Stream API
305
+ */
306
+ hasStream: typeof ReadableStream !== "undefined"
307
+ };
308
+ var environmentInfo = {
309
+ type: getEnvironment(),
310
+ isNode,
311
+ isBrowser,
312
+ isWebWorker,
313
+ features
314
+ };
315
+ function assertEnvironment(expectedEnv, message) {
316
+ const currentEnv = getEnvironment();
317
+ if (currentEnv !== expectedEnv) {
318
+ throw new Error(
319
+ message || `This feature requires ${expectedEnv} environment, but running in ${currentEnv}`
320
+ );
321
+ }
322
+ }
323
+ async function safeDynamicImport(moduleName) {
324
+ assertEnvironment("node", `Cannot import module "${moduleName}" in non-Node.js environment`);
325
+ try {
326
+ return await import(moduleName);
327
+ } catch (error) {
328
+ const errorMessage = error instanceof Error ? error.message : String(error);
329
+ throw new Error(`Failed to import module "${moduleName}": ${errorMessage}`);
330
+ }
331
+ }
332
+
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
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/common/hooks.ts","../src/common/adapter.ts"],"names":[],"mappings":"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","file":"chunk-4H4RACSE.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"]}