qtools-library-dot-d 1.0.10 → 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -229,6 +229,7 @@ This pattern is used extensively in the qtools ecosystem for building scalable,
229
229
 
230
230
  ## HISTORY
231
231
 
232
+ v1.11 - clarified error message "Ignoring invalid file".
232
233
  v1.10 - there was a reason I did not return the exception list. Undone.
233
234
  v1.09 - fixed broken error handling when library file path is bad; added demonstration application
234
235
  v1.08 - updated README (several times)
package/README2.md ADDED
@@ -0,0 +1,418 @@
1
+ ## NAME
2
+
3
+ **qtools-library-dot-d** is system for creating dynamic, modular libraries in Node.js. The dotD (dot-D) pattern provides a powerful way to build extensible applications through automatic module discovery and loading.
4
+
5
+ ## What is qtools-library-dot-d?
6
+
7
+ **qtools-library-dot-d** is a Node.js library that enables dynamic module loading from directories. It automatically discovers JavaScript files in specified directories and makes them available as methods on a library object, creating a plugin-like architecture without the complexity of traditional plugin systems.
8
+
9
+ ## The dotD Pattern
10
+
11
+ The "dotD" naming convention (short for "dot-D") refers to directories that contain dynamically loadable modules:
12
+
13
+ - `modules.d/` - Directory containing module files
14
+ - `endpoints.d/` - Directory containing API endpoint modules
15
+ - `accessPoints.d/` - Directory containing data access modules
16
+ - `mappers.d/` - Directory containing data mapping modules
17
+ - `processors.d/` - Directory containing processing modules
18
+
19
+ Each module, `.js` file or directory with package.json, in a dotD directory becomes available through the library object, with the filename determining the module identifier.
20
+
21
+ ## Two Primary Usage Patterns
22
+
23
+ ### Pattern 1: Function Registration (Classic dotD)
24
+
25
+ Modules register functions that become methods on the library object:
26
+
27
+ ```javascript
28
+ // In dotD module: myFunction.js
29
+ const moduleFunction = ({ moduleName } = {}) => ({ dotD, passThroughParameters } = {}) => {
30
+ const workingFunction = (params) => {
31
+ // Function logic here
32
+ return result;
33
+ };
34
+
35
+ // Register the function
36
+ dotD.library.add(moduleName, workingFunction);
37
+ };
38
+ module.exports = moduleFunction({ moduleName });
39
+ ```
40
+
41
+ ```javascript
42
+ // Usage after loading:
43
+ myLibrary.myFunction(params); // Calls the registered function
44
+ ```
45
+
46
+ ### Pattern 2: Initialization Scripts (Modern dotD)
47
+
48
+ Modules are initialization scripts that run once during `loadModules()`, performing setup work and optionally returning results:
49
+
50
+ ```javascript
51
+ // In dotD module: setupWatcher.js
52
+ const moduleFunction = ({ dotD: myLibraryDotD, passThroughParameters }) => {
53
+ const { xLog, config, userVisibleProcessLogger } = passThroughParameters;
54
+
55
+ // Do initialization work
56
+ const result = initializeWatcher(config.watchPath);
57
+
58
+ // Track what was done
59
+ myLibraryDotD.logList.push(`${moduleName}: ${result.status}`);
60
+
61
+ // Return result (optional)
62
+ return result;
63
+ };
64
+ module.exports = moduleFunction;
65
+ ```
66
+
67
+ This pattern is particularly useful for:
68
+
69
+ - Setting up file watchers, database connections, or other persistent resources
70
+ - Configuring Express routes or middleware
71
+ - Initializing services that run in the background
72
+ - One-time setup tasks that don't need to be called repeatedly
73
+
74
+ ## Why Use Dynamic dotD Libraries?
75
+
76
+ ### 1. **Automatic Module Discovery**
77
+
78
+ No need to manually import or register modules. Simply add a `.js` file to the dotD directory and it's automatically available.
79
+
80
+ ```javascript
81
+ // Traditional approach - manual imports
82
+ const module1 = require('./modules/module1');
83
+ const module2 = require('./modules/module2');
84
+ const module3 = require('./modules/module3');
85
+
86
+ // dotD approach - automatic discovery
87
+ const myLibrary = qtLibDotD({ libraryName: 'myLibrary' }); //libraryName is used as a label in reports
88
+ myLibrary.setLibraryPath('./modules.d');
89
+ myLibrary.loadModules();
90
+ // All modules in modules.d/ are now processed/available
91
+ ```
92
+
93
+ ### 2. **Plugin Architecture**
94
+
95
+ Create extensible applications where new functionality can be added by simply dropping files into directories.
96
+
97
+ ```javascript
98
+ // Add new functionality by creating a new file
99
+ // modules.d/newFeature.js -> automatically processed during loadModules()
100
+ ```
101
+
102
+ ### 3. **Shared Resources**
103
+
104
+ Pass common resources (configuration, database connections, utilities) to all modules through `passThroughParameters`. These are given, by that name, to the module when it is instantiated during the loadModules() process.
105
+
106
+ ```javascript
107
+ const passThroughParameters = {
108
+ config: getConfig(),
109
+ database: dbConnection,
110
+ logger: logger,
111
+ utilities: commonUtils,
112
+ queue: jobQueue,
113
+ eventHandler: eventHandler
114
+ };
115
+ myLibrary.loadModules({ passThroughParameters });
116
+ ```
117
+
118
+ ### 4. **Clean Separation of Concerns**
119
+
120
+ Each module is self-contained and focused on a single responsibility, making code easier to maintain and test.
121
+
122
+ ### 5. **Runtime Flexibility**
123
+
124
+ Modules can be loaded synchronously or asynchronously, and the library can be sealed to prevent modifications after loading.
125
+
126
+ ## Real-World Use Cases
127
+
128
+ ### API Endpoint Management (Initialization Pattern)
129
+
130
+ ```javascript
131
+ // endpoints.d/userEndpoints.js - Sets up Express routes
132
+ const moduleFunction = ({ dotD: endpointsDotD, passThroughParameters }) => {
133
+ const { expressApp, routingPrefix } = passThroughParameters;
134
+
135
+ // Set up routes
136
+ expressApp.get(`${routingPrefix}users`, getUserHandler);
137
+ expressApp.post(`${routingPrefix}users`, createUserHandler);
138
+
139
+ endpointsDotD.logList.push('User endpoints configured');
140
+ return { routesAdded: 2 };
141
+ };
142
+ ```
143
+
144
+ ### Service Initialization (Initialization Pattern)
145
+
146
+ ```javascript
147
+ // processors.d/fileWatcher.js - Sets up file system monitoring
148
+ const moduleFunction = ({ dotD: processorsDotD, passThroughParameters }) => {
149
+ const { config, eventHandler, logger } = passThroughParameters;
150
+
151
+ // Initialize file watcher
152
+ const watcher = setupFileWatcher({
153
+ path: config.watchedPath,
154
+ onFileDetected: (file) => eventHandler({ filePath: file })
155
+ });
156
+
157
+ logger(`File watcher initialized: ${config.watchedPath}`);
158
+ processorsDotD.logList.push(`fileWatcher: active`);
159
+
160
+ return { watcher, status: 'active' };
161
+ };
162
+ ```
163
+
164
+ ### Data Access Layer (Function Registration Pattern)
165
+
166
+ ```javascript
167
+ // accessPoints.d/getUserData.js
168
+ const moduleFunction = ({ moduleName } = {}) => ({ dotD, passThroughParameters } = {}) => {
169
+ const { database } = passThroughParameters;
170
+
171
+ const getUserData = (userId, callback) => {
172
+ // Database access logic
173
+ database.query('SELECT * FROM users WHERE id = ?', [userId], callback);
174
+ };
175
+
176
+ dotD.library.add(moduleName, getUserData);
177
+ // Later called as dotD.getUserData(userId, callback)
178
+ };
179
+ ```
180
+
181
+ ### Business Logic Modules (Function Registration with Init Pattern)
182
+
183
+ ```javascript
184
+ // processors.d/validateOrder.js
185
+ const moduleFunction = ({ dotD, passThroughParameters }) => {
186
+ const { xLog, getConfig } = process.global;
187
+ const localConfig = getConfig(moduleName);
188
+
189
+ let newInstanceData;
190
+
191
+ const init = () => {
192
+ // Initialization logic called after dotD loading completes
193
+ xLog.status(`${moduleName} validator initialized`);
194
+ // This is where you'd set up the module's initial state
195
+ return {
196
+ toolName: moduleName,
197
+ status: 'ready',
198
+ type: 'validator'
199
+ };
200
+ };
201
+
202
+ const validateOrder = (orderData) => {
203
+ // Validation logic using newInstanceData and orderData
204
+ return { valid: true, errors: [] };
205
+ };
206
+
207
+ // Register both the main function and the init function
208
+ dotD.library.add(moduleName, { validateOrder, init });
209
+ // Later called as dotD.validateOrder.validateOrder(orderData)
210
+ // Init called as dotD.validateOrder.init()
211
+
212
+ dotD.logList.push(`${moduleName}: loaded`);
213
+ };
214
+ ```
215
+
216
+ ## How It Works
217
+
218
+ ### Basic Setup (Both Patterns)
219
+
220
+ ```javascript
221
+ const qtLibDotD = require('qtools-library-dot-d');
222
+ const myLibrary = qtLibDotD({ libraryName: 'myLibrary' });
223
+ ```
224
+
225
+ ### Async Pipeline Integration (Recommended)
226
+
227
+ ```javascript
228
+ const {pipeRunner, taskListPlus} = new require('qtools-asynchronous-pipe-plus')();
229
+
230
+ const moduleFunction = ({ config, services }, callback) => {
231
+ const myLibraryDotD = qtLibDotD({ libraryName: 'MyLibrary' });
232
+ const taskList = new taskListPlus();
233
+
234
+ // Set library path
235
+ taskList.push((args, next) => {
236
+ const { myLibraryDotD } = args;
237
+ myLibraryDotD.setLibraryPath(
238
+ path.join(__dirname, 'modules.d'),
239
+ (err, libraryPath) => next(err, { ...args, libraryPath })
240
+ );
241
+ });
242
+
243
+ // Load modules
244
+ taskList.push((args, next) => {
245
+ const { myLibraryDotD } = args;
246
+ const passThroughParameters = { config, services };
247
+
248
+ myLibraryDotD.loadModules({ passThroughParameters }, (err, result) => {
249
+ next(err, { ...args, result });
250
+ });
251
+ });
252
+
253
+ // Execute pipeline
254
+ pipeRunner(taskList.getList(), { myLibraryDotD }, (err, args) => {
255
+ if (err) return callback(err);
256
+
257
+ myLibraryDotD.seal();
258
+ callback(null, myLibraryDotD);
259
+ });
260
+ };
261
+
262
+ // Usage in parent application with init activation:
263
+ const parentApp = ({ dependencies }, callback) => {
264
+ moduleFunction({ config, services }, (err, { myLibraryDotD }) => {
265
+ if (err) return callback(err);
266
+
267
+ // Activate all modules that have init functions
268
+ // or, activate specific ones with specific arguments
269
+ Object.keys(myLibraryDotD).forEach(name => {
270
+ if (myLibraryDotD[name].init) {
271
+ myLibraryDotD[name].init(); //could pass in args
272
+ }
273
+ });
274
+
275
+ callback(null, { myLibraryDotD });
276
+ });
277
+ };
278
+ ```
279
+
280
+ ### Simple Setup (Legacy)
281
+
282
+ ```javascript
283
+ // Synchronous
284
+ myLibrary.setLibraryPath('./modules.d');
285
+ myLibrary.loadModules({ passThroughParameters });
286
+
287
+ // Asynchronous
288
+ myLibrary.setLibraryPath('./modules.d', (err, path) => {
289
+ if (err) return callback(err);
290
+ myLibrary.loadModules({ passThroughParameters }, callback);
291
+ });
292
+ ```
293
+
294
+ ## Module Structure Patterns
295
+
296
+ ### Function Registration Module
297
+
298
+ ```javascript
299
+ const moduleName = __filename.replace(__dirname + '/', '').replace(/.js$/, '');
300
+
301
+ const moduleFunction = ({ moduleName } = {}) => ({ dotD, passThroughParameters } = {}) => {
302
+ const { config, logger } = passThroughParameters;
303
+
304
+ const workingFunction = (/* your parameters */) => {
305
+ // Your module logic here
306
+ return result;
307
+ };
308
+
309
+ // Register the function
310
+ dotD.library.add(moduleName, workingFunction);
311
+ };
312
+
313
+ module.exports = moduleFunction({ moduleName });
314
+ ```
315
+
316
+ ### Initialization Script Module
317
+
318
+ ```javascript
319
+ const moduleName = __filename.replace(__dirname + '/', '').replace(/.js$/, '');
320
+
321
+ const moduleFunction = ({ dotD: libraryDotD, passThroughParameters }) => {
322
+ const { xLog, getConfig } = process.global;
323
+ const localConfig = getConfig(moduleName);
324
+
325
+ const { config, services } = passThroughParameters;
326
+
327
+ // Do initialization work
328
+ const result = initializeService(config);
329
+
330
+ // Track what was done
331
+ libraryDotD.logList.push(`${moduleName}: ${result.status}`);
332
+
333
+ // Return result (optional)
334
+ return result;
335
+ };
336
+
337
+ module.exports = moduleFunction;
338
+ ```
339
+
340
+ ## Pattern Selection Guidelines
341
+
342
+ **Use Function Registration Pattern when:**
343
+
344
+ - You need callable functions/methods on the library object
345
+ - Building a traditional API or service layer
346
+ - Modules provide stateless utility functions
347
+ - You want `myLibrary.functionName()` style access
348
+
349
+ **Use Initialization Script Pattern when:**
350
+
351
+ - Setting up persistent services (file watchers, database connections)
352
+ - Configuring application routes or middleware
353
+ - One-time setup tasks during application startup
354
+ - Building plugin architectures where modules do setup work
355
+ - Working with qtools-asynchronous-pipe-plus pipelines
356
+
357
+ ## Benefits Over Traditional Approaches
358
+
359
+ | Traditional | dotD Libraries |
360
+ | --------------------- | --------------------------- |
361
+ | Manual imports | Automatic discovery |
362
+ | Tight coupling | Loose coupling |
363
+ | Hard to extend | Easy to extend |
364
+ | Configuration in code | Configuration through files |
365
+ | Monolithic | Modular |
366
+ | Static structure | Dynamic structure |
367
+
368
+ ## Getting Started
369
+
370
+ 1. **Install the dependency:**
371
+
372
+ ```bash
373
+ npm install qtools-library-dot-d
374
+ ```
375
+
376
+ 2. **Create your dotD directory:**
377
+
378
+ ```bash
379
+ mkdir myModules.d
380
+ ```
381
+
382
+ 3. **Choose your pattern and create modules:**
383
+
384
+ - For function registration: Follow the classic pattern
385
+ - For initialization scripts: Follow the modern pattern
386
+ - Access shared resources via `passThroughParameters`
387
+
388
+ ## Advanced Features
389
+
390
+ - **Async module loading** for modules that need initialization
391
+ - **Error handling** during module loading with detailed reporting
392
+ - **Library introspection** with `toString()` and `qtdProcessLog()`
393
+ - **Multiple libraries** in the same application
394
+ - **Nested module structures** for complex applications
395
+ - **Pipeline integration** with qtools-asynchronous-pipe-plus
396
+ - **Logging and tracking** via `dotD.logList` for debugging
397
+
398
+ ## Integration with Larger Systems
399
+
400
+ The dotD pattern works particularly well in:
401
+
402
+ - **Web frameworks** (Express, Koa) for route organization
403
+ - **API systems** for endpoint management
404
+ - **CLI tools** for command organization
405
+ - **Microservices** for feature modularity
406
+ - **Plugin systems** for extensible applications
407
+ - **Queue systems** for dynamic job processor loading
408
+ - **File processing pipelines** for modular processing sources
409
+
410
+ This pattern is used extensively in the qtools ecosystem for building scalable, maintainable Node.js applications.
411
+
412
+ ## HISTORY
413
+
414
+ v1.10 - there was a reason I did not return the exception list. Undone.
415
+ v1.09 - fixed broken error handling when library file path is bad; added demonstration application
416
+ v1.08 - updated README (several times)
417
+ v1.05 - cleaned up toString() report, returns exception list (or err if callback) if needed, added qtools-functional-library
418
+ v1.04 - Now tolerates (and reports) directories and files that are not modules.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qtools-library-dot-d",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "description": "Scan a directory for JS modules that can execute and addMe() that attaches it to some form of name/value list, eg, a Map() with functions as values or an xPressJS route/endpoint list.",
5
5
  "main": "qtools-library-dot-d.js",
6
6
  "scripts": {
@@ -47,7 +47,7 @@ const moduleFunction =
47
47
  require(item.endpointPath)(item.moduleArgs); //calls module with dotD object, including library.add()
48
48
  } catch (err) {
49
49
  item.exceptionList.push(
50
- `${path.basename(item.endpointPath)}: ${err.toString().split('\n').qtFirst('n/a')}`,
50
+ `ignoring invlaid file: ${path.basename(item.endpointPath)}: ${err.toString().split('\n').qtFirst('n/a')}`,
51
51
  );
52
52
  }
53
53