@ya-modbus/driver-loader 0.4.1-refactor-scope-driver-packages.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,353 @@
1
+ # @ya-modbus/driver-loader
2
+
3
+ Dynamic driver loader for ya-modbus device drivers.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @ya-modbus/driver-loader
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Auto-detect driver from current directory
14
+
15
+ ```typescript
16
+ import { loadDriver } from '@ya-modbus/driver-loader'
17
+
18
+ const driver = await loadDriver({})
19
+ ```
20
+
21
+ ### Load specific driver package
22
+
23
+ ```typescript
24
+ import { loadDriver } from '@ya-modbus/driver-loader'
25
+
26
+ const driver = await loadDriver({ driverPackage: '@ya-modbus/driver-xymd1' })
27
+ ```
28
+
29
+ ### Error Handling
30
+
31
+ The driver-loader exports custom error classes for type-safe error handling:
32
+
33
+ ```typescript
34
+ import {
35
+ loadDriver,
36
+ ValidationError,
37
+ DriverNotFoundError,
38
+ PackageJsonError,
39
+ } from '@ya-modbus/driver-loader'
40
+
41
+ try {
42
+ const driver = await loadDriver({ driverPackage: 'my-driver' })
43
+ } catch (error) {
44
+ // Type-safe error handling with instanceof
45
+ if (error instanceof DriverNotFoundError) {
46
+ console.error(`Package not found: ${error.packageName}`)
47
+ console.error(`Install with: npm install ${error.packageName}`)
48
+ } else if (error instanceof ValidationError) {
49
+ console.error(`Validation failed for field: ${error.field}`)
50
+ console.error(`Error: ${error.message}`)
51
+ } else if (error instanceof PackageJsonError) {
52
+ console.error('package.json issue:', error.message)
53
+ } else {
54
+ console.error('Unexpected error:', error)
55
+ }
56
+ }
57
+ ```
58
+
59
+ **Error Types:**
60
+
61
+ - **`ValidationError`**: Driver configuration validation failed
62
+ - `field?: string` - The configuration field that failed validation
63
+ - Example: Invalid DEFAULT_CONFIG, missing createDriver function
64
+
65
+ - **`DriverNotFoundError`**: Driver package not found or cannot be loaded
66
+ - `packageName: string` - The package that couldn't be found
67
+ - Example: Package not installed, wrong package name
68
+
69
+ - **`PackageJsonError`**: package.json not found or invalid
70
+ - Example: Missing package.json, invalid JSON, missing ya-modbus-driver keyword
71
+
72
+ ### Custom Logging
73
+
74
+ You can provide a custom logger to control warning and debug output:
75
+
76
+ ```typescript
77
+ import { loadDriver, type Logger } from '@ya-modbus/driver-loader'
78
+
79
+ const logger: Logger = {
80
+ warn: (msg) => myLogger.warning('[DRIVER]', msg),
81
+ debug: (msg) => myLogger.debug('[DRIVER]', msg), // Optional
82
+ }
83
+
84
+ const driver = await loadDriver({
85
+ driverPackage: 'my-driver',
86
+ logger,
87
+ })
88
+ ```
89
+
90
+ ## API
91
+
92
+ ### `loadDriver(options?: LoadDriverOptions): Promise<LoadedDriver>`
93
+
94
+ Loads a ya-modbus driver package and validates its exports.
95
+
96
+ **Options:**
97
+
98
+ - `driverPackage` (optional): Name of the driver package to load. If omitted, auto-detects from current directory's package.json.
99
+
100
+ **Returns:**
101
+
102
+ - `LoadedDriver` object containing:
103
+ - `createDriver`: Factory function to create driver instances (required)
104
+ - `devices`: Device registry for multi-device drivers (optional)
105
+ - `defaultConfig`: Default configuration for serial or TCP (optional)
106
+ - `supportedConfig`: Configuration constraints (optional)
107
+
108
+ **Throws:**
109
+
110
+ - Error if driver package is not found
111
+ - Error if driver exports are invalid
112
+ - Error if configuration validation fails
113
+
114
+ ### `clearDriverCache(): void`
115
+
116
+ Clears the driver cache. Useful for testing or when you need to reload drivers.
117
+
118
+ **When to use:**
119
+
120
+ - **In tests:** Clear cache between test cases to ensure isolation
121
+ - **After package updates:** Force reload of updated driver packages
122
+ - **During development:** Reload drivers after making changes
123
+
124
+ ```typescript
125
+ import { clearDriverCache } from '@ya-modbus/driver-loader'
126
+
127
+ // In test setup
128
+ beforeEach(() => {
129
+ clearDriverCache() // Ensure each test starts with clean cache
130
+ })
131
+
132
+ // After updating a driver package
133
+ await updateDriver('my-driver')
134
+ clearDriverCache() // Force reload on next loadDriver call
135
+ ```
136
+
137
+ ### `getDriverCacheStats(): DriverCacheStats`
138
+
139
+ Returns cache statistics including hits, misses, and current cache size.
140
+
141
+ **Returns:**
142
+
143
+ - `DriverCacheStats` object containing:
144
+ - `hits`: Number of cache hits
145
+ - `misses`: Number of cache misses
146
+ - `size`: Number of cached drivers
147
+
148
+ **Cache Behavior:**
149
+
150
+ - **Automatic caching:** Drivers are cached by package name after first load
151
+ - **Cache key:** Package name (e.g., '@ya-modbus/driver-xymd1')
152
+ - **Cache lifetime:** Persists for the lifetime of the Node.js process
153
+ - **Auto-detect mode:** Even auto-detected drivers are cached by their package name
154
+ - **No cache on errors:** Failed loads are not cached, allowing retry
155
+
156
+ **Example:**
157
+
158
+ ```typescript
159
+ import { loadDriver, getDriverCacheStats } from '@ya-modbus/driver-loader'
160
+
161
+ // First load - cache miss
162
+ await loadDriver({ driverPackage: 'my-driver' })
163
+ console.log(getDriverCacheStats()) // { hits: 0, misses: 1, size: 1 }
164
+
165
+ // Second load - cache hit
166
+ await loadDriver({ driverPackage: 'my-driver' })
167
+ console.log(getDriverCacheStats()) // { hits: 1, misses: 1, size: 1 }
168
+ ```
169
+
170
+ ## Testing Utilities
171
+
172
+ The package provides testing utilities for applications using driver-loader.
173
+
174
+ ```typescript
175
+ import { createMockDriver, mockSystemDeps } from '@ya-modbus/driver-loader/testing'
176
+
177
+ // Create a mock driver
178
+ const mockDriver = createMockDriver({
179
+ defaultConfig: { baudRate: 9600 },
180
+ devices: { test: { manufacturer: 'Test', model: 'Model' } },
181
+ })
182
+
183
+ // Create mock system dependencies
184
+ const deps = mockSystemDeps({
185
+ importModule: jest.fn().mockResolvedValue(mockDriver),
186
+ })
187
+
188
+ // Use with loadDriver in tests
189
+ const driver = await loadDriver({ driverPackage: 'test-driver' }, deps)
190
+ ```
191
+
192
+ ### `createMockDriver(options?: MockDriverOptions): LoadedDriver`
193
+
194
+ Creates a mock driver for testing.
195
+
196
+ **Options:**
197
+
198
+ - `createDriver`: Custom createDriver implementation (default: jest.fn())
199
+ - `defaultConfig`: Mock default configuration
200
+ - `supportedConfig`: Mock supported configuration
201
+ - `devices`: Mock device registry
202
+
203
+ ### `mockSystemDeps(options?: MockSystemDepsOptions): SystemDependencies`
204
+
205
+ Creates mock system dependencies for testing.
206
+
207
+ **Options:**
208
+
209
+ - `readFile`: Custom readFile implementation
210
+ - `importModule`: Custom importModule implementation
211
+ - `getCwd`: Custom getCwd implementation (default: '/mock/cwd')
212
+
213
+ ## Troubleshooting
214
+
215
+ ### Package Not Found
216
+
217
+ If you encounter `Driver package not found` errors:
218
+
219
+ 1. **Verify the package is installed:**
220
+
221
+ ```bash
222
+ npm list ya-modbus-driver-<name>
223
+ ```
224
+
225
+ 2. **Install the driver package:**
226
+
227
+ ```bash
228
+ npm install ya-modbus-driver-<name>
229
+ ```
230
+
231
+ 3. **Check the package name:**
232
+ - Ensure the package name in your code matches the actual npm package name
233
+ - Driver packages typically follow the naming convention: `ya-modbus-driver-<device>`
234
+
235
+ ### Module Resolution Issues
236
+
237
+ If TypeScript or Node.js can't find `@ya-modbus/driver-loader`:
238
+
239
+ 1. **Check your package.json:**
240
+
241
+ ```bash
242
+ npm list @ya-modbus/driver-loader
243
+ ```
244
+
245
+ 2. **Reinstall dependencies:**
246
+
247
+ ```bash
248
+ npm install
249
+ ```
250
+
251
+ 3. **TypeScript module resolution:**
252
+ - Ensure `"moduleResolution": "node"` or `"moduleResolution": "bundler"` in tsconfig.json
253
+ - Check that `"types"` field isn't excluding driver-loader
254
+
255
+ 4. **For local driver development:**
256
+ - The loader tries multiple import paths: `src/index.js`, `src/index.ts`, `dist/index.js`
257
+ - Build your driver before testing: `npm run build`
258
+
259
+ ### Validation Failures
260
+
261
+ If driver validation fails with configuration errors:
262
+
263
+ 1. **ValidationError - Invalid DEFAULT_CONFIG:**
264
+
265
+ ```typescript
266
+ // ❌ Wrong
267
+ export const DEFAULT_CONFIG = { speed: 9600 } // Should be "baudRate"
268
+
269
+ // ✅ Correct
270
+ export const DEFAULT_CONFIG = { baudRate: 9600 }
271
+ ```
272
+
273
+ 2. **ValidationError - Invalid SUPPORTED_CONFIG:**
274
+
275
+ ```typescript
276
+ // ❌ Wrong
277
+ export const SUPPORTED_CONFIG = { baudRates: [9600] } // Should be "validBaudRates"
278
+
279
+ // ✅ Correct
280
+ export const SUPPORTED_CONFIG = { validBaudRates: [9600, 19200] }
281
+ ```
282
+
283
+ 3. **ValidationError - Missing createDriver:**
284
+
285
+ ```typescript
286
+ // ❌ Wrong
287
+ export function makeDriver() {
288
+ ...
289
+ }
290
+
291
+ // ✅ Correct
292
+ export function createDriver() {
293
+ ...
294
+ }
295
+ ```
296
+
297
+ 4. **PackageJsonError - Not a driver package:**
298
+
299
+ Add the `ya-modbus-driver` keyword to your package.json:
300
+
301
+ ```json
302
+ {
303
+ "name": "ya-modbus-driver-mydevice",
304
+ "keywords": ["ya-modbus-driver"]
305
+ }
306
+ ```
307
+
308
+ 5. **Configuration inconsistency warnings:**
309
+
310
+ Ensure DEFAULT_CONFIG values are within SUPPORTED_CONFIG constraints:
311
+
312
+ ```typescript
313
+ // ❌ Inconsistent
314
+ export const DEFAULT_CONFIG = { baudRate: 115200 }
315
+ export const SUPPORTED_CONFIG = { validBaudRates: [9600, 19200] }
316
+
317
+ // ✅ Consistent
318
+ export const DEFAULT_CONFIG = { baudRate: 9600 }
319
+ export const SUPPORTED_CONFIG = { validBaudRates: [9600, 19200] }
320
+ ```
321
+
322
+ ### Getting More Help
323
+
324
+ For additional debugging:
325
+
326
+ 1. **Enable verbose logging:**
327
+
328
+ ```typescript
329
+ const logger = {
330
+ warn: (msg: string) => console.warn('[DRIVER]', msg),
331
+ debug: (msg: string) => console.debug('[DRIVER]', msg),
332
+ }
333
+
334
+ const driver = await loadDriver({ logger })
335
+ ```
336
+
337
+ 2. **Check cache statistics:**
338
+
339
+ ```typescript
340
+ import { getDriverCacheStats } from '@ya-modbus/driver-loader'
341
+
342
+ console.log(getDriverCacheStats())
343
+ ```
344
+
345
+ 3. **Clear the cache:**
346
+ ```typescript
347
+ import { clearDriverCache } from '@ya-modbus/driver-loader'
348
+ clearDriverCache()
349
+ ```
350
+
351
+ ## License
352
+
353
+ GPL-3.0-or-later
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Runtime validation for driver configuration exports
3
+ *
4
+ * Validates DEFAULT_CONFIG, SUPPORTED_CONFIG, and DEVICES from third-party drivers
5
+ * to ensure type safety at runtime.
6
+ */
7
+ import type { DefaultConfig, DeviceRegistry, SupportedConfig } from '@ya-modbus/driver-types';
8
+ import type { Logger } from './loader.js';
9
+ /**
10
+ * Output configuration warnings to logger
11
+ *
12
+ * @param context - Context description (e.g., "Driver DEFAULT_CONFIG")
13
+ * @param warnings - Array of warning messages
14
+ * @param logger - Logger to output warnings to
15
+ */
16
+ export declare function outputConfigWarnings(context: string, warnings: string[], logger?: Logger): void;
17
+ /**
18
+ * Validate DEFAULT_CONFIG export from a driver module
19
+ *
20
+ * @param config - The DEFAULT_CONFIG value to validate
21
+ * @returns Validated config
22
+ * @throws Error with helpful message if validation fails
23
+ */
24
+ export declare function validateDefaultConfig(config: unknown): DefaultConfig;
25
+ /**
26
+ * Validate SUPPORTED_CONFIG export from a driver module
27
+ *
28
+ * @param config - The SUPPORTED_CONFIG value to validate
29
+ * @returns Validated config
30
+ * @throws Error with helpful message if validation fails
31
+ */
32
+ export declare function validateSupportedConfig(config: unknown): SupportedConfig;
33
+ /**
34
+ * Validate DEVICES export from a driver module
35
+ *
36
+ * @param devices - The DEVICES value to validate
37
+ * @param logger - Optional logger for warnings (defaults to console)
38
+ * @returns Validated devices registry
39
+ * @throws Error with helpful message if validation fails
40
+ */
41
+ export declare function validateDevices(devices: unknown, logger?: Logger): DeviceRegistry;
42
+ /**
43
+ * Cross-validate DEFAULT_CONFIG against SUPPORTED_CONFIG constraints
44
+ *
45
+ * Checks that all DEFAULT_CONFIG values are within SUPPORTED_CONFIG constraints.
46
+ * This helps catch driver authoring errors where defaults don't match declared support.
47
+ *
48
+ * @param defaultConfig - The validated DEFAULT_CONFIG
49
+ * @param supportedConfig - The validated SUPPORTED_CONFIG
50
+ * @returns Array of warning messages for any inconsistencies found (empty if all valid)
51
+ */
52
+ export declare function crossValidateConfigs(defaultConfig: DefaultConfig, supportedConfig: SupportedConfig): string[];
53
+ //# sourceMappingURL=config-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-validator.d.ts","sourceRoot":"","sources":["../src/config-validator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAG7F,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAAE,EAClB,MAAM,GAAE,MAAgB,GACvB,IAAI,CAON;AA6BD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,OAAO,GAAG,aAAa,CAyBpE;AAoED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,OAAO,GAAG,eAAe,CAuExE;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,GAAE,MAAgB,GAAG,cAAc,CAsG1F;AAiBD;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,aAAa,EAAE,aAAa,EAC5B,eAAe,EAAE,eAAe,GAC/B,MAAM,EAAE,CA2GV"}