taro-bluetooth-print 2.8.4 → 2.9.1

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 (86) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +53 -4
  3. package/dist/index.cjs.js +1 -1
  4. package/dist/index.es.js +1 -1
  5. package/dist/index.umd.js +1 -1
  6. package/dist/types/core/BluetoothPrinter.d.ts +21 -4
  7. package/dist/types/core/di/Container.d.ts +84 -0
  8. package/dist/types/core/di/Tokens.d.ts +29 -0
  9. package/dist/types/core/di/index.d.ts +3 -0
  10. package/dist/types/core/event/EventBus.d.ts +66 -0
  11. package/dist/types/core/event/index.d.ts +2 -0
  12. package/dist/types/core/index.d.ts +5 -4
  13. package/dist/types/core/plugin/PluginManager.d.ts +64 -0
  14. package/dist/types/core/plugin/index.d.ts +2 -0
  15. package/dist/types/device/MultiPrinterManager.d.ts +2 -0
  16. package/dist/types/errors/CommandBuildError.d.ts +40 -0
  17. package/dist/types/errors/ConnectionError.d.ts +45 -0
  18. package/dist/types/errors/PrintJobError.d.ts +42 -0
  19. package/dist/types/errors/index.d.ts +9 -0
  20. package/dist/types/factory/PrinterFactory.d.ts +108 -0
  21. package/dist/types/factory/di-factory.d.ts +52 -0
  22. package/dist/types/factory/index.d.ts +6 -0
  23. package/dist/types/index.d.ts +9 -1
  24. package/dist/types/providers/ServiceProvider.d.ts +56 -0
  25. package/dist/types/providers/index.d.ts +2 -0
  26. package/dist/types/services/interfaces/ICommandBuilder.d.ts +123 -0
  27. package/dist/types/services/interfaces/IConnectionManager.d.ts +49 -0
  28. package/dist/types/services/interfaces/IPrintJobManager.d.ts +67 -0
  29. package/dist/types/services/interfaces/index.d.ts +6 -233
  30. package/dist/types/template/TemplateEngine.d.ts +24 -68
  31. package/dist/types/template/engines/TemplateRenderer.d.ts +71 -0
  32. package/dist/types/template/parsers/TemplateParser.d.ts +23 -0
  33. package/dist/types/utils/index.d.ts +8 -0
  34. package/dist/types/utils/logger.d.ts +4 -3
  35. package/dist/types/utils/outputLimiter.d.ts +87 -0
  36. package/dist/types/utils/validation.d.ts +11 -309
  37. package/dist/types/utils/validators/array.d.ts +19 -0
  38. package/dist/types/utils/validators/buffer.d.ts +18 -0
  39. package/dist/types/utils/validators/chain.d.ts +31 -0
  40. package/dist/types/utils/validators/common.d.ts +22 -0
  41. package/dist/types/utils/validators/number.d.ts +20 -0
  42. package/dist/types/utils/validators/object.d.ts +24 -0
  43. package/dist/types/utils/validators/printer.d.ts +40 -0
  44. package/dist/types/utils/validators/types.d.ts +125 -0
  45. package/dist/types/utils/validators/uuid.d.ts +23 -0
  46. package/package.json +1 -1
  47. package/src/core/BluetoothPrinter.ts +48 -50
  48. package/src/core/di/Container.ts +330 -0
  49. package/src/core/di/Tokens.ts +45 -0
  50. package/src/core/di/index.ts +3 -0
  51. package/src/core/event/EventBus.ts +251 -0
  52. package/src/core/event/index.ts +2 -0
  53. package/src/core/index.ts +10 -4
  54. package/src/core/plugin/PluginManager.ts +161 -0
  55. package/src/core/plugin/index.ts +2 -0
  56. package/src/device/MultiPrinterManager.ts +15 -6
  57. package/src/errors/CommandBuildError.ts +72 -0
  58. package/src/errors/ConnectionError.ts +78 -0
  59. package/src/errors/PrintJobError.ts +75 -0
  60. package/src/errors/index.ts +10 -0
  61. package/src/factory/PrinterFactory.ts +139 -0
  62. package/src/factory/di-factory.ts +61 -0
  63. package/src/factory/index.ts +12 -0
  64. package/src/index.ts +61 -1
  65. package/src/providers/ServiceProvider.ts +213 -0
  66. package/src/providers/index.ts +2 -0
  67. package/src/services/interfaces/ICommandBuilder.ts +145 -0
  68. package/src/services/interfaces/IConnectionManager.ts +58 -0
  69. package/src/services/interfaces/IPrintJobManager.ts +83 -0
  70. package/src/services/interfaces/index.ts +5 -265
  71. package/src/template/TemplateEngine.ts +27 -792
  72. package/src/template/engines/TemplateRenderer.ts +762 -0
  73. package/src/template/parsers/TemplateParser.ts +94 -0
  74. package/src/utils/index.ts +9 -0
  75. package/src/utils/logger.ts +17 -4
  76. package/src/utils/outputLimiter.ts +227 -0
  77. package/src/utils/validation.ts +21 -1138
  78. package/src/utils/validators/array.ts +95 -0
  79. package/src/utils/validators/buffer.ts +81 -0
  80. package/src/utils/validators/chain.ts +181 -0
  81. package/src/utils/validators/common.ts +216 -0
  82. package/src/utils/validators/number.ts +101 -0
  83. package/src/utils/validators/object.ts +63 -0
  84. package/src/utils/validators/printer.ts +294 -0
  85. package/src/utils/validators/types.ts +105 -0
  86. package/src/utils/validators/uuid.ts +49 -0
@@ -0,0 +1,40 @@
1
+ import { ValidationResult, PrinterDataSchema, PrintJobSchema } from './types';
2
+ /**
3
+ * Validate printer connection data
4
+ *
5
+ * @param data - Data to validate
6
+ * @param schema - Validation schema
7
+ * @returns Validation result
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const result = validatePrinterData({
12
+ * deviceId: 'device-123',
13
+ * deviceName: 'Thermal Printer',
14
+ * }, {
15
+ * deviceId: { required: true },
16
+ * deviceName: { required: true, maxLength: 50 }
17
+ * });
18
+ * ```
19
+ */
20
+ export declare function validatePrinterData(data: Record<string, unknown>, schema: PrinterDataSchema): ValidationResult;
21
+ /**
22
+ * Validate print job data
23
+ *
24
+ * @param data - Data to validate
25
+ * @param schema - Validation schema
26
+ * @returns Validation result
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * const result = validatePrintJob({
31
+ * jobId: 'job-001',
32
+ * data: new ArrayBuffer(1024),
33
+ * priority: 5,
34
+ * }, {
35
+ * jobId: { required: true },
36
+ * data: { required: true, minSize: 1 }
37
+ * });
38
+ * ```
39
+ */
40
+ export declare function validatePrintJob(data: Record<string, unknown>, schema: PrintJobSchema): ValidationResult;
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Core validation types
3
+ */
4
+ /**
5
+ * Validation error details
6
+ */
7
+ export interface ValidationError {
8
+ /** Field that failed validation */
9
+ field: string;
10
+ /** Human-readable error message */
11
+ message: string;
12
+ /** Error code for programmatic handling */
13
+ code: string;
14
+ /** Actual value that failed (optional) */
15
+ value?: unknown;
16
+ }
17
+ /**
18
+ * Validation result
19
+ */
20
+ export interface ValidationResult {
21
+ /** Whether validation passed */
22
+ valid: boolean;
23
+ /** List of validation errors */
24
+ errors: ValidationError[];
25
+ /** Warnings (non-fatal issues) */
26
+ warnings?: ValidationError[];
27
+ }
28
+ /**
29
+ * Validator function type
30
+ */
31
+ export type ValidatorFn<T = unknown> = (value: T) => ValidationResult;
32
+ /**
33
+ * Validation rule definition
34
+ */
35
+ export interface ValidationRule<T = unknown> {
36
+ /** Rule name */
37
+ name: string;
38
+ /** Validation function */
39
+ validate: (value: T) => boolean;
40
+ /** Error message if validation fails */
41
+ message: string;
42
+ /** Error code */
43
+ code: string;
44
+ }
45
+ /**
46
+ * Common error codes
47
+ */
48
+ export declare const ValidationCodes: {
49
+ readonly REQUIRED: "REQUIRED";
50
+ readonly INVALID_TYPE: "INVALID_TYPE";
51
+ readonly INVALID_FORMAT: "INVALID_FORMAT";
52
+ readonly OUT_OF_RANGE: "OUT_OF_RANGE";
53
+ readonly TOO_SHORT: "TOO_SHORT";
54
+ readonly TOO_LONG: "TOO_LONG";
55
+ readonly INVALID_ENUM: "INVALID_ENUM";
56
+ readonly PATTERN_MISMATCH: "PATTERN_MISMATCH";
57
+ readonly INVALID_BUFFER: "INVALID_BUFFER";
58
+ readonly INVALID_ENCODING: "INVALID_ENCODING";
59
+ readonly EMPTY_ARRAY: "EMPTY_ARRAY";
60
+ readonly ARRAY_TOO_SHORT: "ARRAY_TOO_SHORT";
61
+ readonly ARRAY_TOO_LONG: "ARRAY_TOO_LONG";
62
+ readonly DUPLICATE_VALUE: "DUPLICATE_VALUE";
63
+ readonly NEGATIVE_VALUE: "NEGATIVE_VALUE";
64
+ readonly ZERO_VALUE: "ZERO_VALUE";
65
+ readonly NOT_A_NUMBER: "NOT_A_NUMBER";
66
+ readonly INFINITY_VALUE: "INFINITY_VALUE";
67
+ readonly INVALID_JSON: "INVALID_JSON";
68
+ readonly UNSUPPORTED_VALUE: "UNSUPPORTED_VALUE";
69
+ readonly INVALID_VERSION: "INVALID_VERSION";
70
+ };
71
+ /**
72
+ * Printer data validation schema
73
+ */
74
+ export interface PrinterDataSchema {
75
+ /** Device ID */
76
+ deviceId?: {
77
+ required?: boolean;
78
+ pattern?: RegExp;
79
+ };
80
+ /** Device name */
81
+ deviceName?: {
82
+ required?: boolean;
83
+ maxLength?: number;
84
+ };
85
+ /** Service UUID */
86
+ serviceUUID?: {
87
+ required?: boolean;
88
+ pattern?: RegExp;
89
+ };
90
+ /** Characteristic UUID */
91
+ characteristicUUID?: {
92
+ required?: boolean;
93
+ pattern?: RegExp;
94
+ };
95
+ }
96
+ /**
97
+ * Print job data validation schema
98
+ */
99
+ export interface PrintJobSchema {
100
+ /** Job ID */
101
+ jobId?: {
102
+ required?: boolean;
103
+ maxLength?: number;
104
+ };
105
+ /** Data buffer */
106
+ data?: {
107
+ required?: boolean;
108
+ minSize?: number;
109
+ maxSize?: number;
110
+ };
111
+ /** Priority */
112
+ priority?: {
113
+ min?: number;
114
+ max?: number;
115
+ };
116
+ /** Retry count */
117
+ retryCount?: {
118
+ min?: number;
119
+ max?: number;
120
+ };
121
+ /** Metadata */
122
+ metadata?: {
123
+ required?: boolean;
124
+ };
125
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * UUID validation
3
+ */
4
+ /**
5
+ * Validate a string is valid UUID
6
+ *
7
+ * @param value - Value to validate
8
+ * @param options - Validation options
9
+ * @returns Validation result
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * const result = isValidUUID(value, { required: true });
14
+ * ```
15
+ */
16
+ export declare function isValidUUID(value: unknown, options?: {
17
+ required?: boolean;
18
+ versions?: number[];
19
+ }): {
20
+ valid: boolean;
21
+ version?: number;
22
+ error?: string;
23
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taro-bluetooth-print",
3
- "version": "2.8.4",
3
+ "version": "2.9.1",
4
4
  "description": "Taro 蓝牙打印库 v2.6 - 轻量级、高性能、跨平台支持微信、支付宝、百度、字节跳动小程序及H5 Web Bluetooth",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs.js",
@@ -77,44 +77,53 @@ export class BluetoothPrinter extends EventEmitter<PrinterEvents> {
77
77
  /**
78
78
  * Creates a new BluetoothPrinter instance
79
79
  *
80
- * @param connectionManagerOrAdapter - Connection manager instance or printer adapter instance (for backward compatibility)
81
- * @param printJobManagerOrDriver - Print job manager instance or printer driver instance (for backward compatibility)
82
- * @param commandBuilder - Command builder instance (optional)
80
+ * Supports two calling conventions:
81
+ * - Modern DI: new BluetoothPrinter(connectionManager, printJobManager, commandBuilder)
82
+ * - Legacy API: new BluetoothPrinter(adapter) - adapter is wrapped in ConnectionManager
83
+ *
84
+ * @param connectionManagerOrAdapter - Connection manager (recommended) or IPrinterAdapter (legacy)
85
+ * @param printJobManager - Print job manager instance
86
+ * @param commandBuilder - Command builder instance
87
+ *
88
+ * @example
89
+ * ```typescript
90
+ * // Recommended: use the factory
91
+ * import { createBluetoothPrinter } from 'taro-bluetooth-print';
92
+ * const printer = createBluetoothPrinter({ adapter: myAdapter });
93
+ *
94
+ * // Direct instantiation with DI
95
+ * const printer = new BluetoothPrinter(connectionManager, printJobManager, commandBuilder);
96
+ *
97
+ * // Legacy API (backward compatible)
98
+ * const printer = new BluetoothPrinter(adapter);
99
+ * ```
83
100
  */
84
101
  constructor(
85
102
  connectionManagerOrAdapter?: IConnectionManager | IPrinterAdapter,
86
- printJobManagerOrDriver?: IPrintJobManager,
103
+ printJobManager?: IPrintJobManager,
87
104
  commandBuilder?: ICommandBuilder
88
105
  ) {
89
106
  super();
90
107
 
91
- // Handle backward compatibility
92
- if (
93
- connectionManagerOrAdapter &&
94
- typeof (connectionManagerOrAdapter as IPrinterAdapter).connect === 'function'
95
- ) {
96
- // Old API: new BluetoothPrinter(adapter)
97
- const adapter = connectionManagerOrAdapter as IPrinterAdapter;
108
+ // Detect legacy API: first arg is an adapter (has connect method) vs connection manager
109
+ const isAdapter =
110
+ connectionManagerOrAdapter != null &&
111
+ typeof (connectionManagerOrAdapter as IPrinterAdapter).connect === 'function';
98
112
 
99
- // Create services manually
113
+ if (isAdapter) {
114
+ // Legacy API: wrap adapter in ConnectionManager
115
+ const adapter = connectionManagerOrAdapter as IPrinterAdapter;
100
116
  this.connectionManager = new ConnectionManager(adapter);
101
- this.printJobManager = new PrintJobManager(this.connectionManager);
102
- this.commandBuilder = commandBuilder || new CommandBuilder();
117
+ this.printJobManager = printJobManager ?? new PrintJobManager(this.connectionManager);
118
+ this.commandBuilder = commandBuilder ?? new CommandBuilder();
103
119
  } else {
104
- // New API: dependency injection or manual service creation
105
- this.connectionManager = connectionManagerOrAdapter as IConnectionManager;
106
- this.printJobManager = printJobManagerOrDriver as IPrintJobManager;
107
- this.commandBuilder = commandBuilder as ICommandBuilder;
108
-
109
- // If services are not provided, create them using default implementations
110
- if (!this.connectionManager) {
111
- this.connectionManager = new ConnectionManager();
112
- this.printJobManager = new PrintJobManager(this.connectionManager);
113
- this.commandBuilder = new CommandBuilder();
114
- }
120
+ // Modern DI
121
+ this.connectionManager =
122
+ (connectionManagerOrAdapter as IConnectionManager) ?? new ConnectionManager();
123
+ this.printJobManager = printJobManager ?? new PrintJobManager(this.connectionManager);
124
+ this.commandBuilder = commandBuilder ?? new CommandBuilder();
115
125
  }
116
126
 
117
- // Listen to connection manager state changes
118
127
  this.updateState();
119
128
  }
120
129
 
@@ -122,28 +131,19 @@ export class BluetoothPrinter extends EventEmitter<PrinterEvents> {
122
131
  * Updates the current state based on the connection manager and print job manager states
123
132
  */
124
133
  private updateState(): void {
125
- // Get connection state (with fallback for backward compatibility)
126
- let connectionState: PrinterState;
127
- if (this.connectionManager && typeof this.connectionManager.getState === 'function') {
128
- connectionState = this.connectionManager.getState();
129
- } else {
130
- // Default to CONNECTED for backward compatibility
131
- connectionState = PrinterState.CONNECTED;
132
- }
134
+ // Safe fallbacks for mock objects in tests
135
+ const connectionState =
136
+ typeof this.connectionManager.getState === 'function'
137
+ ? this.connectionManager.getState()
138
+ : PrinterState.CONNECTED;
133
139
 
134
- // Get print job state (with fallback for backward compatibility)
135
- let isPrinting = false;
136
- let isPaused = false;
137
- if (this.printJobManager) {
138
- isPrinting =
139
- typeof this.printJobManager.isInProgress === 'function'
140
- ? this.printJobManager.isInProgress()
141
- : false;
142
- isPaused =
143
- typeof this.printJobManager.isPaused === 'function'
144
- ? this.printJobManager.isPaused()
145
- : false;
146
- }
140
+ const isPrinting =
141
+ typeof this.printJobManager.isInProgress === 'function'
142
+ ? this.printJobManager.isInProgress()
143
+ : false;
144
+
145
+ const isPaused =
146
+ typeof this.printJobManager.isPaused === 'function' ? this.printJobManager.isPaused() : false;
147
147
 
148
148
  // Determine the final state
149
149
  if (isPaused) {
@@ -425,11 +425,10 @@ export class BluetoothPrinter extends EventEmitter<PrinterEvents> {
425
425
  * ```
426
426
  */
427
427
  async print(): Promise<void> {
428
- // Check if connectionManager has isConnected method, default to true for mock objects in tests
429
428
  const isConnected =
430
429
  typeof this.connectionManager.isConnected === 'function'
431
430
  ? this.connectionManager.isConnected()
432
- : true;
431
+ : true; // Default to true for mock objects
433
432
 
434
433
  if (!isConnected) {
435
434
  throw new BluetoothPrintError(
@@ -454,7 +453,6 @@ export class BluetoothPrinter extends EventEmitter<PrinterEvents> {
454
453
  try {
455
454
  await this.printJobManager.start(buffer);
456
455
 
457
- // Check if the job was paused
458
456
  const isPaused =
459
457
  typeof this.printJobManager.isPaused === 'function'
460
458
  ? this.printJobManager.isPaused()
@@ -0,0 +1,330 @@
1
+ /**
2
+ * 依赖注入容器
3
+ * 提供现代化的依赖管理和服务定位功能
4
+ */
5
+
6
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
+ export type Constructor<T = object> = new (...args: any[]) => T;
8
+ export type Factory<T> = (container: Container) => T;
9
+ export type Provider<T> = Constructor<T> | Factory<T> | T;
10
+
11
+ export interface RegistrationOptions {
12
+ /** 是否单例 */
13
+ singleton?: boolean;
14
+ /** 生命周期 */
15
+ lifecycle?: 'transient' | 'singleton' | 'scoped';
16
+ /** 标签,用于区分同一接口的不同实现 */
17
+ tag?: string;
18
+ }
19
+
20
+ /**
21
+ * Angular 风格的服务提供者配置
22
+ */
23
+ export interface ServiceProviderConfig<T = unknown> {
24
+ /** 使用类 */
25
+ useClass?: Constructor<T>;
26
+ /** 使用工厂函数 */
27
+ useFactory?: Factory<T>;
28
+ /** 使用实例值 */
29
+ useValue?: T;
30
+ /** 生命周期 */
31
+ lifecycle?: 'transient' | 'singleton' | 'scoped';
32
+ /** 依赖项 */
33
+ dependencies?: (string | symbol | Constructor<unknown>)[];
34
+ }
35
+
36
+ interface Registration<T> {
37
+ provider: Provider<T>;
38
+ options: RegistrationOptions;
39
+ instance?: T;
40
+ }
41
+
42
+ export class Container {
43
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
+ private registrations = new Map<string | symbol, Registration<any>[]>();
45
+ private parent?: Container;
46
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
47
+ private scopedInstances = new Map<string | symbol, any>();
48
+
49
+ constructor(parent?: Container) {
50
+ this.parent = parent;
51
+ }
52
+
53
+ /**
54
+ * 创建子容器
55
+ */
56
+ createChild(): Container {
57
+ return new Container(this);
58
+ }
59
+
60
+ /**
61
+ * 注册服务
62
+ */
63
+ register<T>(
64
+ token: string | symbol | Constructor<T>,
65
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
66
+ provider: Provider<T> | ServiceProviderConfig<T>,
67
+ options: RegistrationOptions = {}
68
+ ): this {
69
+ const key = this.getTokenKey(token);
70
+
71
+ // 支持 ServiceProviderConfig 格式
72
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
+ const config = provider as ServiceProviderConfig<T>;
74
+
75
+ // 如果是对象语法(Angular 风格)
76
+ if (config && typeof config === 'object' && (config.useClass || config.useFactory || config.useValue !== undefined)) {
77
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
+ let actualProvider: Provider<any>;
79
+
80
+ if (config.useClass) {
81
+ // 使用类 - 创建工厂函数来解析依赖
82
+ const Cls = config.useClass;
83
+ actualProvider = (container: Container) => {
84
+ // 简化:直接实例化(实际应该解析依赖)
85
+ return container.createInstance(Cls);
86
+ };
87
+ } else if (config.useFactory) {
88
+ actualProvider = config.useFactory;
89
+ } else {
90
+ actualProvider = config.useValue as T;
91
+ }
92
+
93
+ const registration: Registration<T> = {
94
+ provider: actualProvider,
95
+ options: {
96
+ lifecycle: config.lifecycle || 'transient',
97
+ ...options,
98
+ },
99
+ };
100
+
101
+ if (!this.registrations.has(key)) {
102
+ this.registrations.set(key, []);
103
+ }
104
+ this.registrations.get(key)!.push(registration);
105
+ return this;
106
+ }
107
+
108
+ // 原有逻辑:Provider<T> 格式
109
+ const registration: Registration<T> = {
110
+ provider: provider as Provider<T>,
111
+ options: {
112
+ lifecycle: 'transient',
113
+ ...options,
114
+ },
115
+ };
116
+
117
+ if (!this.registrations.has(key)) {
118
+ this.registrations.set(key, []);
119
+ }
120
+ this.registrations.get(key)!.push(registration);
121
+ return this;
122
+ }
123
+
124
+ /**
125
+ * 注册单例服务
126
+ */
127
+ registerSingleton<T>(token: string | symbol | Constructor<T>, provider: Provider<T>): this {
128
+ return this.register(token, provider, { lifecycle: 'singleton' });
129
+ }
130
+
131
+ /**
132
+ * 注册类型映射
133
+ */
134
+ registerType<T>(
135
+ from: string | symbol | Constructor<T>,
136
+ to: Constructor<T>,
137
+ options: RegistrationOptions = {}
138
+ ): this {
139
+ return this.register(from, c => c.resolve(to), options);
140
+ }
141
+
142
+ /**
143
+ * 注册实例
144
+ */
145
+ registerInstance<T>(token: string | symbol | Constructor<T>, instance: T): this {
146
+ return this.register(token, instance, { lifecycle: 'singleton' });
147
+ }
148
+
149
+ /**
150
+ * 解析服务
151
+ */
152
+ resolve<T>(token: string | symbol | Constructor<T>, tag?: string): T {
153
+ const key = this.getTokenKey(token);
154
+
155
+ // 1. 检查当前容器的注册
156
+ const registrations = this.registrations.get(key);
157
+ if (registrations) {
158
+ const registration = tag
159
+ ? registrations.find(r => r.options.tag === tag)
160
+ : registrations[registrations.length - 1];
161
+
162
+ if (registration) {
163
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
164
+ return this.getOrCreateInstance(registration, key);
165
+ }
166
+ }
167
+
168
+ // 2. 检查父容器
169
+ if (this.parent) {
170
+ return this.parent.resolve(token, tag);
171
+ }
172
+
173
+ // 3. 如果是构造函数,自动创建实例
174
+ if (typeof token === 'function' && this.isConstructor(token)) {
175
+ return this.createInstance(token);
176
+ }
177
+
178
+ throw new Error(`No registration found for token: ${String(key)}`);
179
+ }
180
+
181
+ /**
182
+ * 解析所有注册的服务
183
+ */
184
+ resolveAll<T>(token: string | symbol | Constructor<T>): T[] {
185
+ const key = this.getTokenKey(token);
186
+ const results: T[] = [];
187
+
188
+ // 从当前容器获取
189
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
190
+ const registrations = this.registrations.get(key);
191
+ if (registrations) {
192
+ for (const r of registrations) {
193
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
194
+ results.push(this.getOrCreateInstance(r, key));
195
+ }
196
+ }
197
+
198
+ // 从父容器获取
199
+ if (this.parent) {
200
+ results.push(...this.parent.resolveAll(token));
201
+ }
202
+
203
+ return results;
204
+ }
205
+
206
+ /**
207
+ * 检查是否已注册
208
+ */
209
+ isRegistered<T>(token: string | symbol | Constructor<T>): boolean {
210
+ const key = this.getTokenKey(token);
211
+ return this.registrations.has(key) || (this.parent?.isRegistered(token) ?? false);
212
+ }
213
+
214
+ /**
215
+ * 清除所有注册
216
+ */
217
+ clear(): void {
218
+ this.registrations.clear();
219
+ this.scopedInstances.clear();
220
+ }
221
+
222
+ /**
223
+ * 创建作用域
224
+ */
225
+ createScope(): Container {
226
+ const scope = this.createChild();
227
+ return scope;
228
+ }
229
+
230
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
231
+ private getTokenKey(token: string | symbol | Constructor<any>): string | symbol {
232
+ if (typeof token === 'function') {
233
+ return token.name || (token as unknown as symbol);
234
+ }
235
+ return token;
236
+ }
237
+
238
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
239
+ private isConstructor(fn: Function): boolean {
240
+ return (
241
+ typeof fn.prototype === 'object' &&
242
+ fn.prototype !== null &&
243
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
244
+ fn.prototype.constructor === fn
245
+ );
246
+ }
247
+
248
+ private getOrCreateInstance<T>(registration: Registration<T>, key: string | symbol): T {
249
+ const { provider, options } = registration;
250
+
251
+ // 单例模式
252
+ if (options.lifecycle === 'singleton') {
253
+ if (registration.instance === undefined) {
254
+ registration.instance = this.createProviderInstance(provider);
255
+ }
256
+ return registration.instance;
257
+ }
258
+
259
+ // 作用域模式
260
+ if (options.lifecycle === 'scoped') {
261
+ if (this.scopedInstances.has(key)) {
262
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
263
+ return this.scopedInstances.get(key) as T;
264
+ }
265
+ const instance = this.createProviderInstance(provider);
266
+ this.scopedInstances.set(key, instance);
267
+ return instance;
268
+ }
269
+
270
+ // 瞬态模式
271
+ return this.createProviderInstance(provider);
272
+ }
273
+
274
+ private createProviderInstance<T>(provider: Provider<T>): T {
275
+ if (typeof provider === 'function') {
276
+ // 工厂函数
277
+ if (provider.length === 1 && !this.isConstructor(provider)) {
278
+ return (provider as Factory<T>)(this);
279
+ }
280
+ // 构造函数
281
+ return this.createInstance(provider as Constructor<T>);
282
+ }
283
+ // 实例
284
+ return provider;
285
+ }
286
+
287
+ private createInstance<T>(constructor: Constructor<T>): T {
288
+ // 获取构造函数的参数类型(简化实现,实际可能需要反射元数据)
289
+ const paramCount = constructor.length;
290
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
291
+ const args: any[] = [];
292
+
293
+ for (let i = 0; i < paramCount; i++) {
294
+ // 尝试从容器解析参数
295
+ // 这里简化处理,实际应该使用参数装饰器
296
+ args.push(undefined);
297
+ }
298
+
299
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
300
+ return new constructor(...args);
301
+ }
302
+ }
303
+
304
+ // 全局容器实例
305
+ export const rootContainer = new Container();
306
+
307
+ // 装饰器辅助函数
308
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
309
+ export function injectable<T extends Constructor>(constructor: T): T {
310
+ // 标记类为可注入
311
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
312
+ (constructor as any).__injectable = true;
313
+ return constructor;
314
+ }
315
+
316
+ export function inject(token: string | symbol) {
317
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
318
+ return function (target: any, _propertyKey: string | symbol, parameterIndex: number) {
319
+ // 存储注入元数据
320
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
321
+ const existingInjections: (string | symbol)[] =
322
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
323
+ (target.__injections as (string | symbol)[]) || [];
324
+ existingInjections[parameterIndex] = token;
325
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
326
+ target.__injections = existingInjections;
327
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
328
+ return target;
329
+ };
330
+ }