@cldmv/slothlet 2.8.0 → 2.10.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/AGENT-USAGE.md +1 -1
- package/README.md +300 -1557
- package/dist/lib/engine/slothlet_child.mjs +1 -1
- package/dist/lib/engine/slothlet_engine.mjs +1 -1
- package/dist/lib/engine/slothlet_esm.mjs +1 -1
- package/dist/lib/engine/slothlet_helpers.mjs +1 -1
- package/dist/lib/engine/slothlet_worker.mjs +1 -1
- package/dist/lib/helpers/als-eventemitter.mjs +5 -6
- package/dist/lib/helpers/api_builder/add_api.mjs +292 -0
- package/dist/lib/helpers/api_builder/analysis.mjs +532 -0
- package/dist/lib/helpers/api_builder/construction.mjs +457 -0
- package/dist/lib/helpers/api_builder/decisions.mjs +737 -0
- package/dist/lib/helpers/api_builder/metadata.mjs +248 -0
- package/dist/lib/helpers/api_builder.mjs +17 -1568
- package/dist/lib/helpers/auto-wrap.mjs +1 -1
- package/dist/lib/helpers/hooks.mjs +1 -1
- package/dist/lib/helpers/instance-manager.mjs +1 -1
- package/dist/lib/helpers/metadata-api.mjs +201 -0
- package/dist/lib/helpers/multidefault.mjs +12 -3
- package/dist/lib/helpers/resolve-from-caller.mjs +1 -1
- package/dist/lib/helpers/sanitize.mjs +1 -1
- package/dist/lib/helpers/utilities.mjs +121 -0
- package/dist/lib/modes/slothlet_eager.mjs +1 -1
- package/dist/lib/modes/slothlet_lazy.mjs +10 -1
- package/dist/lib/runtime/runtime-asynclocalstorage.mjs +49 -18
- package/dist/lib/runtime/runtime-livebindings.mjs +23 -4
- package/dist/lib/runtime/runtime.mjs +15 -4
- package/dist/slothlet.mjs +164 -748
- package/docs/API-RULES-CONDITIONS.md +508 -0
- package/{API-RULES.md → docs/API-RULES.md} +127 -72
- package/package.json +11 -9
- package/types/dist/lib/helpers/als-eventemitter.d.mts.map +1 -1
- package/types/dist/lib/helpers/api_builder/add_api.d.mts +76 -0
- package/types/dist/lib/helpers/api_builder/add_api.d.mts.map +1 -0
- package/types/dist/lib/helpers/api_builder/analysis.d.mts +189 -0
- package/types/dist/lib/helpers/api_builder/analysis.d.mts.map +1 -0
- package/types/dist/lib/helpers/api_builder/construction.d.mts +107 -0
- package/types/dist/lib/helpers/api_builder/construction.d.mts.map +1 -0
- package/types/dist/lib/helpers/api_builder/decisions.d.mts +213 -0
- package/types/dist/lib/helpers/api_builder/decisions.d.mts.map +1 -0
- package/types/dist/lib/helpers/api_builder/metadata.d.mts +99 -0
- package/types/dist/lib/helpers/api_builder/metadata.d.mts.map +1 -0
- package/types/dist/lib/helpers/api_builder.d.mts +5 -448
- package/types/dist/lib/helpers/api_builder.d.mts.map +1 -1
- package/types/dist/lib/helpers/metadata-api.d.mts +132 -0
- package/types/dist/lib/helpers/metadata-api.d.mts.map +1 -0
- package/types/dist/lib/helpers/multidefault.d.mts.map +1 -1
- package/types/dist/lib/helpers/utilities.d.mts +120 -0
- package/types/dist/lib/helpers/utilities.d.mts.map +1 -0
- package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts +9 -0
- package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts.map +1 -1
- package/types/dist/lib/runtime/runtime-livebindings.d.mts +10 -0
- package/types/dist/lib/runtime/runtime-livebindings.d.mts.map +1 -1
- package/types/dist/lib/runtime/runtime.d.mts +1 -0
- package/types/dist/lib/runtime/runtime.d.mts.map +1 -1
- package/types/dist/slothlet.d.mts +0 -11
- package/types/dist/slothlet.d.mts.map +1 -1
- package/types/index.d.mts +0 -1
- package/API-RULES-CONDITIONS.md +0 -367
package/README.md
CHANGED
|
@@ -33,1541 +33,316 @@ The name might suggest we're taking it easy, but don't be fooled. **Slothlet del
|
|
|
33
33
|
|
|
34
34
|
---
|
|
35
35
|
|
|
36
|
-
## ✨ What's New
|
|
36
|
+
## ✨ What's New
|
|
37
37
|
|
|
38
|
-
###
|
|
38
|
+
### Latest: v2.9 (December 30, 2025)
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
- **Per-Request Context Isolation** - New `api.run()` and `api.scope()` methods for isolated context execution ([Documentation](https://github.com/CLDMV/slothlet/blob/master/docs/CONTEXT-PROPAGATION.md#per-request-context-isolation))
|
|
41
|
+
- API Builder Modularization - Improved maintainability and code organization
|
|
42
|
+
- [View Changelog](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.9.md)
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
- **Dual Runtime System**: Choose AsyncLocalStorage or live-bindings for context isolation
|
|
44
|
-
- **4.3x Faster Startup**: Lazy mode achieves 564.17μs vs 2.45ms in eager mode
|
|
45
|
-
- **Copy-Left Materialization**: Once loaded, modules stay materialized for optimal performance
|
|
46
|
-
- **Zero Dependencies**: Pure Node.js implementation with no external dependencies
|
|
44
|
+
### Recent Releases
|
|
47
45
|
|
|
48
|
-
|
|
46
|
+
- **v2.8** - NPM security fixes and package workflow updates ([Changelog](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.8.md))
|
|
47
|
+
- **v2.7** - Security updates ([Changelog](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.7.md))
|
|
48
|
+
- **v2.6** - Hook System with 4 interceptor types ([Changelog](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.6.md))
|
|
49
|
+
- **v2.5** - Architectural consolidation and API consistency ([Changelog](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.5.md))
|
|
50
|
+
- **v2.4** - Multi-default export handling ([Changelog](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.4.md))
|
|
51
|
+
- **v2.3** - EventEmitter & Class Context Propagation ([Changelog](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.3.md))
|
|
52
|
+
- **v2.2** - Case preservation options ([Changelog](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.2.md))
|
|
53
|
+
- **v2.1** - Advanced sanitization patterns ([Changelog](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.1.md))
|
|
54
|
+
- **v2.0** - Complete Architectural Rewrite ([Changelog](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.0.md))
|
|
49
55
|
|
|
50
|
-
|
|
51
|
-
- **Live-Binding System**: Dynamic context and reference binding with runtime coordination
|
|
52
|
-
- **Smart Function Naming**: Preserves original capitalization (`autoIP`, `parseJSON`, `getHTTPStatus`)
|
|
53
|
-
- **Multi-Execution Environments**: Singleton, VM, worker, fork isolation modes (experimental)
|
|
54
|
-
|
|
55
|
-
### 🔧 **Advanced Sanitization Control** ⭐ NEW
|
|
56
|
-
|
|
57
|
-
- **Custom API Naming**: Control how filenames become API property names through sanitize options
|
|
58
|
-
- **Boundary Pattern Matching**: Use `**string**` patterns for precise transformations (`**url**` → `buildURLWithParams`)
|
|
59
|
-
- **Glob Pattern Support**: Apply rules with wildcards (`*json*`, `auto*`, `http*`) for flexible naming control
|
|
60
|
-
- **Case-Sensitive Rules**: Preserve important naming patterns (acronyms, technical terms, branding)
|
|
61
|
-
- **Mixed Rule Types**: Combine exact matches, globs, and boundary patterns for sophisticated naming strategies
|
|
62
|
-
|
|
63
|
-
### 📊 **Performance Optimizations**
|
|
64
|
-
|
|
65
|
-
- **Startup**: Lazy mode 4.3x faster (564.17μs vs 2.45ms)
|
|
66
|
-
- **Function Calls**: Eager mode 1.1x faster (0.65μs vs 0.72μs) after materialization
|
|
67
|
-
- **Memory**: On-demand loading scales with actual usage
|
|
68
|
-
- **Predictability**: Consistent performance characteristics per mode
|
|
69
|
-
|
|
70
|
-
### 🔄 **Context Propagation (v2.3)** ⭐ NEW
|
|
71
|
-
|
|
72
|
-
- **EventEmitter Context Propagation**: Automatic context preservation across EventEmitter callbacks using AsyncResource patterns
|
|
73
|
-
- **Class Instance Context Propagation**: Automatic context preservation across class method calls with transparent wrapping
|
|
74
|
-
- **AsyncResource Integration**: Production-ready context management following Node.js best practices
|
|
75
|
-
- **Zero Configuration**: Works automatically with TCP servers, HTTP servers, and any EventEmitter-based patterns
|
|
76
|
-
|
|
77
|
-
### 🎣 **Hook System (v2.6.4)** ⭐ NEW
|
|
78
|
-
|
|
79
|
-
- **3-Hook Types**: `before` (modify args or cancel), `after` (transform results), `always` (observe final result)
|
|
80
|
-
- **Cross-Mode Compatibility**: Works seamlessly across all 4 combinations (eager/lazy × async/live)
|
|
81
|
-
- **Pattern Matching**: Target specific functions or use wildcards (`math.*`, `*.add`, `**`)
|
|
82
|
-
- **Priority Control**: Order hook execution with numeric priorities
|
|
83
|
-
- **Runtime Control**: Enable/disable hooks at runtime, globally or by pattern
|
|
84
|
-
- **Short-Circuit Support**: Cancel execution and return custom values from `before` hooks
|
|
85
|
-
|
|
86
|
-
---
|
|
87
|
-
|
|
88
|
-
## 🚀 Key Features
|
|
89
|
-
|
|
90
|
-
### 🎯 **Dual Loading Strategies**
|
|
91
|
-
|
|
92
|
-
- **Eager Loading**: Immediate loading for maximum performance in production environments
|
|
93
|
-
- **Lazy Loading**: Copy-left materialization with look-ahead proxies (4.3x faster startup, 1.1x slower calls after materialization)
|
|
94
|
-
|
|
95
|
-
> [!IMPORTANT]
|
|
96
|
-
> **Function Call Patterns:**
|
|
97
|
-
>
|
|
98
|
-
> - **Lazy Mode**: ALL function calls must be awaited (`await api.math.add(2, 3)`) due to materialization process
|
|
99
|
-
> - **Eager Mode**: Functions behave as originally defined - sync functions are sync (`api.math.add(2, 3)`), async functions are async (`await api.async.process()`)
|
|
100
|
-
|
|
101
|
-
### ⚡ Performance Excellence
|
|
102
|
-
|
|
103
|
-
- **📊 For comprehensive performance analysis, benchmarks, and recommendations, see [PERFORMANCE.md](https://github.com/CLDMV/slothlet/blob/HEAD/PERFORMANCE.md)**
|
|
104
|
-
|
|
105
|
-
### 🔧 **Smart API Management**
|
|
106
|
-
|
|
107
|
-
- **Callable Interface**: Use `slothlet(options)` for direct API creation
|
|
108
|
-
- **Smart Flattening**: Intelligent rules create clean APIs (`math/math.mjs` → `api.math`, `connection.mjs` → `api.connect()`)
|
|
109
|
-
- **Intelligent Naming**: Dash-separated names convert automatically (`root-math.mjs` → `api.rootMath`)
|
|
110
|
-
- **Function Name Preservation**: Maintains original capitalization (`auto-ip.mjs` with `autoIP` → `api.autoIP`)
|
|
111
|
-
- **Hybrid Exports**: Support for callable APIs with methods, default + named exports, and mixed patterns
|
|
112
|
-
|
|
113
|
-
> [!TIP]
|
|
114
|
-
> **📁 For comprehensive examples of API flattening, naming conventions, and function preservation patterns, see the test modules in [api_tests/](https://github.com/CLDMV/slothlet/blob/HEAD/api_tests) and their documentation in [docs/api_tests/](https://github.com/CLDMV/slothlet/blob/HEAD/docs/api_tests)**
|
|
115
|
-
|
|
116
|
-
> [!NOTE]
|
|
117
|
-
> **🔍 For detailed technical documentation on API transformation rules:**
|
|
118
|
-
>
|
|
119
|
-
> - **[API-RULES.md](https://github.com/CLDMV/slothlet/blob/HEAD/API-RULES.md)** - Verified API transformation rules with examples and test cases
|
|
120
|
-
> - **[API-RULES-CONDITIONS.md](https://github.com/CLDMV/slothlet/blob/HEAD/API-RULES-CONDITIONS.md)** - Complete technical reference of all conditional logic that controls API generation
|
|
121
|
-
|
|
122
|
-
### 🔗 **Advanced Binding System**
|
|
123
|
-
|
|
124
|
-
- **Live Bindings**: Dynamic context and reference binding for runtime API mutation
|
|
125
|
-
- **Context Isolation**: Dual runtime options for per-instance context isolation with seamless integration
|
|
126
|
-
- **Copy-Left Preservation**: Materialized functions stay materialized, preserving performance gains
|
|
127
|
-
- **Bubble-Up Updates**: Parent API synchronization ensures consistency across the API tree
|
|
128
|
-
- **Mixed Module Support**: Seamlessly blend ESM and CommonJS modules in the same API
|
|
129
|
-
|
|
130
|
-
### 🛠 **Developer Experience**
|
|
131
|
-
|
|
132
|
-
- **Enhanced Error Handling**: Clear JavaScript errors with module suggestions and descriptive errors (planned for v3.0.0)
|
|
133
|
-
- **TypeScript-Friendly**: Comprehensive JSDoc annotations for excellent editor support with auto-generated declarations
|
|
134
|
-
- **Configurable Debug**: Detailed logging for development and troubleshooting via CLI flags or environment variables
|
|
135
|
-
- **Multiple Instances**: Parameter-based isolation for complex applications with instance ID management
|
|
136
|
-
- **Development Checks**: Built-in environment detection with silent production behavior
|
|
137
|
-
|
|
138
|
-
### 🏗 **Architecture & Compatibility**
|
|
139
|
-
|
|
140
|
-
- **ESM-First**: Built for modern JavaScript with full ES module support
|
|
141
|
-
- **Universal Loading**: CommonJS and ESM files work together seamlessly
|
|
142
|
-
- **Zero Dependencies**: Lightweight footprint with no external dependencies
|
|
143
|
-
- **Cross-Platform**: Works seamlessly across all Node.js environments
|
|
144
|
-
- **Extensible**: Modular architecture with flexible API composition patterns
|
|
145
|
-
|
|
146
|
-
---
|
|
147
|
-
|
|
148
|
-
## 📦 Installation
|
|
149
|
-
|
|
150
|
-
### Requirements
|
|
151
|
-
|
|
152
|
-
- **Node.js v12.20.0 or higher** (for ESM support with `import`/`export`)
|
|
153
|
-
- **Node.js v16.4.0 or higher** (recommended for AsyncLocalStorage runtime - `runtime: "async"`)
|
|
154
|
-
|
|
155
|
-
> [!IMPORTANT]
|
|
156
|
-
> **v2.x Runtime Options**: Slothlet v2.x supports two runtime systems:
|
|
157
|
-
>
|
|
158
|
-
> - **AsyncLocalStorage Runtime** (`runtime: "async"`) - Default, requires Node.js v16.4.0+ for context isolation
|
|
159
|
-
> - **Live Bindings Runtime** (`runtime: "live"`) - Advanced system, works on Node.js v12.20.0+ without AsyncLocalStorage
|
|
160
|
-
>
|
|
161
|
-
> Both runtimes provide full live-binding capabilities with `self`, `context`, and `reference` support across ESM and CommonJS modules. Use `runtime: "live"` for older Node.js versions or advanced binding scenarios.
|
|
162
|
-
|
|
163
|
-
### Install
|
|
164
|
-
|
|
165
|
-
```bash
|
|
166
|
-
npm install @cldmv/slothlet
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
---
|
|
170
|
-
|
|
171
|
-
## 🚀 Quick Start
|
|
172
|
-
|
|
173
|
-
### ESM (ES Modules)
|
|
174
|
-
|
|
175
|
-
```javascript
|
|
176
|
-
import slothlet from "@cldmv/slothlet";
|
|
177
|
-
|
|
178
|
-
// Direct usage - eager mode by default (auto-detects callable interface)
|
|
179
|
-
const api = await slothlet({
|
|
180
|
-
dir: "./api",
|
|
181
|
-
context: { user: "alice" }
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
// Eager mode: Functions behave as originally defined
|
|
185
|
-
const result = api.math.add(2, 3); // Sync function - no await needed
|
|
186
|
-
const greeting = api("World"); // Instant if callable
|
|
187
|
-
|
|
188
|
-
// Original async functions still need await in eager mode
|
|
189
|
-
const asyncResult = await api.async.processData({ data: "async" });
|
|
190
|
-
|
|
191
|
-
// Access both ESM and CJS modules seamlessly
|
|
192
|
-
const esmResult = api.mathEsm.multiply(4, 5); // 20 (sync)
|
|
193
|
-
const cjsResult = await api.mathCjs.divide(10, 2); // 5 (if originally async)
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
### CommonJS (CJS)
|
|
197
|
-
|
|
198
|
-
```javascript
|
|
199
|
-
const slothlet = require("@cldmv/slothlet");
|
|
200
|
-
|
|
201
|
-
// Same usage pattern works with CommonJS
|
|
202
|
-
const api = await slothlet({
|
|
203
|
-
dir: "./api",
|
|
204
|
-
context: { env: "production" }
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
const result = api.math.multiply(4, 5); // 20
|
|
208
|
-
const mixedResult = await api.interop.processData({ data: "test" }); // CJS+ESM interop
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
### Runtime Selection
|
|
212
|
-
|
|
213
|
-
```javascript
|
|
214
|
-
// AsyncLocalStorage runtime (default) - requires Node.js v16.4.0+
|
|
215
|
-
const apiAsync = await slothlet({
|
|
216
|
-
dir: "./api",
|
|
217
|
-
runtime: "async", // or "asynclocalstorage"
|
|
218
|
-
context: { user: "alice" }
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
// Live bindings runtime - works on Node.js v12.20.0+
|
|
222
|
-
const apiLive = await slothlet({
|
|
223
|
-
dir: "./api",
|
|
224
|
-
runtime: "live", // or "livebindings"
|
|
225
|
-
context: { user: "bob" }
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
// Both provide identical live-binding capabilities
|
|
229
|
-
import { self, context, reference } from "@cldmv/slothlet/runtime";
|
|
230
|
-
// context.user is available in your modules regardless of runtime choice
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
### Lazy Loading Mode
|
|
234
|
-
|
|
235
|
-
```javascript
|
|
236
|
-
import slothlet from "@cldmv/slothlet";
|
|
237
|
-
|
|
238
|
-
// Lazy mode with copy-left materialization
|
|
239
|
-
const api = await slothlet({
|
|
240
|
-
mode: "lazy", // New preferred syntax
|
|
241
|
-
dir: "./api",
|
|
242
|
-
apiDepth: 3
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
// Or use legacy syntax (still supported)
|
|
246
|
-
const apiLegacy = await slothlet({
|
|
247
|
-
lazy: true, // Legacy syntax
|
|
248
|
-
dir: "./api",
|
|
249
|
-
apiDepth: 3
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
// First access: ~310μs (materialization overhead)
|
|
253
|
-
const result1 = await api.math.add(2, 3);
|
|
254
|
-
|
|
255
|
-
// Subsequent access: ~0.5μs (materialized function)
|
|
256
|
-
const result2 = await api.math.add(5, 7); // 700x faster than first call!
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
### Advanced Configuration
|
|
260
|
-
|
|
261
|
-
```javascript
|
|
262
|
-
import slothlet from "@cldmv/slothlet";
|
|
263
|
-
|
|
264
|
-
const api = await slothlet({
|
|
265
|
-
dir: "./api",
|
|
266
|
-
mode: "eager", // New: Loading strategy (lazy/eager)
|
|
267
|
-
engine: "singleton", // New: Execution environment
|
|
268
|
-
api_mode: "auto", // API structure behavior
|
|
269
|
-
apiDepth: Infinity, // Directory traversal depth
|
|
270
|
-
debug: false, // Enable verbose logging
|
|
271
|
-
context: {
|
|
272
|
-
// Injected into live-binding
|
|
273
|
-
user: "alice",
|
|
274
|
-
env: "production",
|
|
275
|
-
config: { timeout: 5000 }
|
|
276
|
-
},
|
|
277
|
-
reference: {
|
|
278
|
-
// Merged into API root
|
|
279
|
-
version: "2.0.0",
|
|
280
|
-
helpers: {
|
|
281
|
-
/* ... */
|
|
282
|
-
}
|
|
283
|
-
},
|
|
284
|
-
sanitize: {
|
|
285
|
-
// 🔧 NEW: Control API property naming
|
|
286
|
-
lowerFirst: false, // Keep first character casing
|
|
287
|
-
rules: {
|
|
288
|
-
leave: ["parseJSON", "autoIP"], // Preserve exact names
|
|
289
|
-
leaveInsensitive: ["*xml*"], // Case-insensitive preservation
|
|
290
|
-
upper: ["**url**", "api", "http*"], // Force uppercase (including boundary patterns)
|
|
291
|
-
lower: ["id", "uuid", "*id"] // Force lowercase
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
});
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
### Sanitize Options Examples
|
|
298
|
-
|
|
299
|
-
Transform module filenames into clean, professional API property names with sophisticated control:
|
|
300
|
-
|
|
301
|
-
```javascript
|
|
302
|
-
// Without sanitize options (default behavior)
|
|
303
|
-
const api = await slothlet({ dir: "./api" });
|
|
304
|
-
// Files: build-url-with-params.mjs, parse-json-data.mjs, auto-ip.mjs
|
|
305
|
-
// Result: api.buildUrlWithParams, api.parseJsonData, api.autoIp
|
|
306
|
-
|
|
307
|
-
// With sanitize options (custom naming control)
|
|
308
|
-
const api = await slothlet({
|
|
309
|
-
dir: "./api",
|
|
310
|
-
sanitize: {
|
|
311
|
-
lowerFirst: false, // Keep first character casing
|
|
312
|
-
preserveAllUpper: true, // Preserve identifiers like "COMMON_APPS"
|
|
313
|
-
preserveAllLower: false, // Transform identifiers like "common_apps"
|
|
314
|
-
rules: {
|
|
315
|
-
leave: ["parseJSON"], // Exact match preservation (case-sensitive)
|
|
316
|
-
leaveInsensitive: ["*xml*"], // Case-insensitive preservation
|
|
317
|
-
upper: ["**url**", "ip", "http*"], // Force uppercase transformations
|
|
318
|
-
lower: ["id", "*id"] // Force lowercase transformations
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
});
|
|
322
|
-
// Result: api.buildURLWithParams, api.parseJSON, api.autoIP
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
**Comprehensive Sanitize Configuration:**
|
|
326
|
-
|
|
327
|
-
```javascript
|
|
328
|
-
const api = await slothlet({
|
|
329
|
-
dir: "./api",
|
|
330
|
-
sanitize: {
|
|
331
|
-
// Basic Options
|
|
332
|
-
lowerFirst: true, // Default: lowercase first character for camelCase
|
|
333
|
-
preserveAllUpper: true, // Keep "COMMON_APPS" unchanged
|
|
334
|
-
preserveAllLower: false, // Transform "common_apps" → "commonApps"
|
|
335
|
-
|
|
336
|
-
rules: {
|
|
337
|
-
// Preserve exact matches (case-sensitive)
|
|
338
|
-
leave: ["parseJSON", "autoIP", "getHTTPStatus"],
|
|
339
|
-
|
|
340
|
-
// Preserve patterns (case-insensitive)
|
|
341
|
-
leaveInsensitive: ["*xml*", "*html*"],
|
|
342
|
-
|
|
343
|
-
// Force uppercase transformations
|
|
344
|
-
upper: [
|
|
345
|
-
"api", // Exact: "api" → "API"
|
|
346
|
-
"http*", // Glob: "httpGet" → "HTTPGet"
|
|
347
|
-
"**url**", // Boundary: "buildUrlPath" → "buildURLPath"
|
|
348
|
-
"**json**" // Boundary: "parseJsonData" → "parseJSONData"
|
|
349
|
-
],
|
|
350
|
-
|
|
351
|
-
// Force lowercase transformations
|
|
352
|
-
lower: [
|
|
353
|
-
"id", // Exact: "ID" → "id"
|
|
354
|
-
"*id", // Glob: "userId" → "userid"
|
|
355
|
-
"uuid*" // Glob: "UUIDGenerator" → "uuidGenerator"
|
|
356
|
-
]
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
});
|
|
360
|
-
```
|
|
361
|
-
|
|
362
|
-
**Sanitize Pattern Types:**
|
|
363
|
-
|
|
364
|
-
- **Exact Match**: `"parseJSON"` - Matches exact string only
|
|
365
|
-
- **Glob Patterns**:
|
|
366
|
-
- `"*json*"` - Matches anywhere in string (`parseJsonData`)
|
|
367
|
-
- `"auto*"` - Matches at start (`autoGenerateId`)
|
|
368
|
-
- `"*id"` - Matches at end (`userId`)
|
|
369
|
-
- **Boundary Patterns**:
|
|
370
|
-
- `"**url**"` - Only matches when surrounded by characters (`buildUrlPath` ✓, `url` ✗)
|
|
371
|
-
- `"**json**"` - Matches `parseJsonData` but not standalone `json`
|
|
372
|
-
- **Case Control**:
|
|
373
|
-
- `leave` - Case-sensitive exact preservation
|
|
374
|
-
- `leaveInsensitive` - Case-insensitive preservation
|
|
375
|
-
- `preserveAllUpper`/`preserveAllLower` - Automatic case detection
|
|
376
|
-
|
|
377
|
-
**Advanced Pattern Examples:**
|
|
378
|
-
|
|
379
|
-
```javascript
|
|
380
|
-
// File transformations with patterns:
|
|
381
|
-
sanitizePathName("build-url-with-params", {
|
|
382
|
-
rules: { upper: ["**url**"] }
|
|
383
|
-
}); // → "buildURLWithParams"
|
|
384
|
-
|
|
385
|
-
sanitizePathName("parse-json-data", {
|
|
386
|
-
rules: { upper: ["**json**"] }
|
|
387
|
-
}); // → "parseJSONData"
|
|
388
|
-
|
|
389
|
-
sanitizePathName("get-http-status", {
|
|
390
|
-
rules: { upper: ["http*"] }
|
|
391
|
-
}); // → "getHTTPStatus"
|
|
392
|
-
|
|
393
|
-
sanitizePathName("validate-user-id", {
|
|
394
|
-
rules: { lower: ["*id"] }
|
|
395
|
-
}); // → "validateUserid"
|
|
396
|
-
|
|
397
|
-
sanitizePathName("XML_PARSER", {
|
|
398
|
-
preserveAllUpper: true
|
|
399
|
-
}); // → "XML_PARSER" (preserved)
|
|
400
|
-
```
|
|
401
|
-
|
|
402
|
-
### Multiple Instances
|
|
403
|
-
|
|
404
|
-
In v2.x, each call to `slothlet(options)` automatically creates a new isolated instance with its own context and configuration:
|
|
405
|
-
|
|
406
|
-
#### ESM (ES Modules)
|
|
407
|
-
|
|
408
|
-
```javascript
|
|
409
|
-
import slothlet from "@cldmv/slothlet";
|
|
410
|
-
|
|
411
|
-
// Each call creates a new isolated instance automatically
|
|
412
|
-
const api1 = await slothlet({ dir: "./api1", context: { tenant: "alice" } });
|
|
413
|
-
const api2 = await slothlet({ dir: "./api2", context: { tenant: "bob" } });
|
|
414
|
-
|
|
415
|
-
// Instances are completely isolated
|
|
416
|
-
console.log(api1.context.tenant); // "alice"
|
|
417
|
-
console.log(api2.context.tenant); // "bob"
|
|
418
|
-
```
|
|
419
|
-
|
|
420
|
-
#### CommonJS (CJS)
|
|
421
|
-
|
|
422
|
-
```javascript
|
|
423
|
-
const slothlet = require("@cldmv/slothlet");
|
|
424
|
-
|
|
425
|
-
// Each call creates a new isolated instance automatically
|
|
426
|
-
const api1 = await slothlet({ dir: "./api1", context: { tenant: "alice" } });
|
|
427
|
-
const api2 = await slothlet({ dir: "./api2", context: { tenant: "bob" } });
|
|
428
|
-
|
|
429
|
-
// Instances are completely isolated with their own AsyncLocalStorage contexts
|
|
430
|
-
console.log(api1.context.tenant); // "alice"
|
|
431
|
-
console.log(api2.context.tenant); // "bob"
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
> [!NOTE]
|
|
435
|
-
> **v2.x Simplification**: Unlike v1.x which required query string parameters or `withInstanceId()` methods, v2.x automatically creates isolated instances with each `slothlet()` call, using your chosen runtime system (`async` or `live`) for complete context separation.
|
|
436
|
-
|
|
437
|
-
---
|
|
438
|
-
|
|
439
|
-
## 📚 API Reference
|
|
440
|
-
|
|
441
|
-
### Core Methods
|
|
442
|
-
|
|
443
|
-
#### `slothlet(options)` ⇒ `Promise<object>`
|
|
444
|
-
|
|
445
|
-
Creates and loads an API instance with the specified configuration.
|
|
446
|
-
|
|
447
|
-
**Parameters:**
|
|
448
|
-
|
|
449
|
-
| Param | Type | Description |
|
|
450
|
-
| ------- | -------- | --------------------- |
|
|
451
|
-
| options | `object` | Configuration options |
|
|
452
|
-
|
|
453
|
-
**Returns:** `Promise<object>` - The bound API object
|
|
454
|
-
|
|
455
|
-
**Options:**
|
|
456
|
-
|
|
457
|
-
| Option | Type | Default | Description |
|
|
458
|
-
| ----------- | --------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
459
|
-
| `dir` | `string` | `"api"` | Directory to load API modules from. Can be absolute or relative path. If relative, resolved from process.cwd(). |
|
|
460
|
-
| `lazy` | `boolean` | `false` | **Legacy** loading strategy - `true` for lazy loading (on-demand), `false` for eager loading (immediate). Use `mode` option instead. |
|
|
461
|
-
| `mode` | `string` | - | **New** loading mode - `"lazy"` for on-demand loading, `"eager"` for immediate loading. Takes precedence over `lazy` option. Also supports execution modes for backward compatibility. |
|
|
462
|
-
| `engine` | `string` | `"singleton"` | **New** execution environment mode - `"singleton"`, `"vm"`, `"worker"`, or `"fork"` |
|
|
463
|
-
| `runtime` | `string` | `"async"` | Runtime binding system: `"async"` for AsyncLocalStorage-based context isolation (default, requires Node.js v16.4.0+), `"live"` for advanced live-binding system (works on Node.js v12.20.0+). Both provide full live-binding capabilities. |
|
|
464
|
-
| `apiDepth` | `number` | `Infinity` | Directory traversal depth control - `0` for root only, `Infinity` for all levels |
|
|
465
|
-
| `debug` | `boolean` | `false` | Enable verbose logging. Can also be set via `--slothletdebug` command line flag or `SLOTHLET_DEBUG=true` environment variable |
|
|
466
|
-
| `api_mode` | `string` | `"auto"` | API structure behavior when root-level default functions exist:<br/>• `"auto"`: Automatically detects if root has default function export and creates callable API<br/>• `"function"`: Forces API to be callable (use when you have root-level default function exports)<br/>• `"object"`: Forces API to be object-only (use when you want object interface regardless of exports) |
|
|
467
|
-
| `allowApiOverwrite` | `boolean` | `true` | Controls whether `addApi()` can overwrite existing API endpoints:<br/>• `true`: Allow overwrites (default, backwards compatible)<br/>• `false`: Prevent overwrites - logs warning and skips when attempting to overwrite existing endpoints<br/>Applies to both function and object overwrites at the final key of the API path |
|
|
468
|
-
| `context` | `object` | `{}` | Context data object injected into live-binding `context` reference. Available to all loaded modules via `import { context } from "@cldmv/slothlet/runtime"` |
|
|
469
|
-
| `reference` | `object` | `{}` | Reference object merged into the API root level. Properties not conflicting with loaded modules are added directly to the API |
|
|
470
|
-
| `sanitize` | `object` | `{}` | **🔧 NEW**: Advanced filename-to-API transformation control. Options: `lowerFirst` (boolean), `preserveAllUpper` (boolean), `preserveAllLower` (boolean), `rules` object with `leave` (exact case-sensitive), `leaveInsensitive` (case-insensitive), `upper`/`lower` arrays. Supports exact matches, glob patterns (`*json*`, `http*`), and boundary patterns (`**url**` for surrounded matches only) |
|
|
471
|
-
|
|
472
|
-
#### ✨ Current Option Format
|
|
473
|
-
|
|
474
|
-
The option structure has been improved for better clarity:
|
|
475
|
-
|
|
476
|
-
```javascript
|
|
477
|
-
// ✅ New recommended syntax
|
|
478
|
-
const api = await slothlet({
|
|
479
|
-
mode: "lazy", // Loading strategy: "lazy" | "eager"
|
|
480
|
-
engine: "singleton", // Execution environment: "singleton" | "vm" | "worker" | "fork"
|
|
481
|
-
dir: "./api"
|
|
482
|
-
});
|
|
483
|
-
|
|
484
|
-
// ✅ Legacy syntax (still fully supported)
|
|
485
|
-
const api = await slothlet({
|
|
486
|
-
lazy: true, // Boolean loading strategy
|
|
487
|
-
mode: "singleton", // Execution environment (legacy placement)
|
|
488
|
-
dir: "./api"
|
|
489
|
-
});
|
|
490
|
-
|
|
491
|
-
// ✅ Mixed usage (mode takes precedence)
|
|
492
|
-
const api = await slothlet({
|
|
493
|
-
lazy: false, // Will be overridden
|
|
494
|
-
mode: "lazy", // Takes precedence - results in lazy loading
|
|
495
|
-
engine: "singleton"
|
|
496
|
-
});
|
|
497
|
-
```
|
|
498
|
-
|
|
499
|
-
**Benefits of the new syntax:**
|
|
500
|
-
|
|
501
|
-
- **Clearer separation**: `mode` for loading strategy, `engine` for execution environment
|
|
502
|
-
- **Better discoverability**: String values are more self-documenting than boolean flags
|
|
503
|
-
- **Future-proof**: Easier to extend with additional loading strategies
|
|
504
|
-
- **Backward compatible**: All existing code continues to work unchanged
|
|
505
|
-
|
|
506
|
-
#### `slothlet.getApi()` ⇒ `object`
|
|
507
|
-
|
|
508
|
-
Returns the raw API object (Proxy or plain object).
|
|
509
|
-
|
|
510
|
-
**Returns:** `function | object` - The raw API object or function
|
|
511
|
-
|
|
512
|
-
#### `slothlet.getBoundApi()` ⇒ `object`
|
|
513
|
-
|
|
514
|
-
Returns the bound API object with context and reference.
|
|
515
|
-
|
|
516
|
-
**Returns:** `function | object` - The bound API object or function with live bindings and context
|
|
517
|
-
|
|
518
|
-
#### `slothlet.isLoaded()` ⇒ `boolean`
|
|
519
|
-
|
|
520
|
-
Returns true if the API is loaded.
|
|
521
|
-
|
|
522
|
-
**Returns:** `boolean` - Whether the API has been loaded
|
|
523
|
-
|
|
524
|
-
#### `slothlet.shutdown()` ⇒ `Promise<void>`
|
|
525
|
-
|
|
526
|
-
Gracefully shuts down the API and performs comprehensive resource cleanup to prevent hanging processes.
|
|
527
|
-
|
|
528
|
-
**Cleanup includes:**
|
|
529
|
-
|
|
530
|
-
- Hook manager state and registered hooks
|
|
531
|
-
- AsyncLocalStorage context and bindings
|
|
532
|
-
- EventEmitter listeners and AsyncResource instances (including third-party libraries)
|
|
533
|
-
- Instance data and runtime coordination
|
|
534
|
-
|
|
535
|
-
**Returns:** `Promise<void>` - Resolves when shutdown is complete
|
|
536
|
-
|
|
537
|
-
> [!IMPORTANT]
|
|
538
|
-
> **🛡️ Process Cleanup**: The shutdown method now performs comprehensive cleanup of all EventEmitter listeners created after slothlet loads, including those from third-party libraries like pg-pool. This prevents hanging AsyncResource instances that could prevent your Node.js process from exiting cleanly.
|
|
539
|
-
|
|
540
|
-
#### `api.addApi(apiPath, folderPath)` ⇒ `Promise<void>` ⭐ NEW
|
|
541
|
-
|
|
542
|
-
Dynamically extend your API at runtime by loading additional modules and merging them into a specified path.
|
|
543
|
-
|
|
544
|
-
**Parameters:**
|
|
545
|
-
|
|
546
|
-
| Param | Type | Description |
|
|
547
|
-
| ------------ | -------- | ------------------------------------------------------------------- |
|
|
548
|
-
| `apiPath` | `string` | Dotted path where modules will be added (e.g., `"runtime.plugins"`) |
|
|
549
|
-
| `folderPath` | `string` | Path to folder containing modules to load (relative or absolute) |
|
|
550
|
-
|
|
551
|
-
**Returns:** `Promise<void>` - Resolves when the API extension is complete
|
|
552
|
-
|
|
553
|
-
**Features:**
|
|
554
|
-
|
|
555
|
-
- ✅ **Dynamic Loading**: Add modules after initial API creation
|
|
556
|
-
- ✅ **Path Creation**: Automatically creates intermediate objects for nested paths
|
|
557
|
-
- ✅ **Smart Merging**: Merges into existing objects or creates new namespaces
|
|
558
|
-
- ✅ **Mode Respect**: Uses same loading mode (lazy/eager) as parent API
|
|
559
|
-
- ✅ **Live Binding Updates**: Automatically updates `self`, `context`, and `reference`
|
|
560
|
-
- ✅ **Function Support**: Can traverse through functions (slothlet's function.property pattern)
|
|
561
|
-
- ✅ **Validation**: Prevents extension through primitives, validates path format
|
|
562
|
-
- ✅ **Overwrite Protection**: Optional `allowApiOverwrite` config prevents accidental endpoint overwrites
|
|
563
|
-
|
|
564
|
-
**Configuration:**
|
|
565
|
-
|
|
566
|
-
The `allowApiOverwrite` option controls whether `addApi` can overwrite existing endpoints:
|
|
567
|
-
|
|
568
|
-
```javascript
|
|
569
|
-
// Default behavior - allows overwrites (backwards compatible)
|
|
570
|
-
const api = await slothlet({
|
|
571
|
-
dir: "./api",
|
|
572
|
-
allowApiOverwrite: true // default
|
|
573
|
-
});
|
|
574
|
-
await api.addApi("tools", "./new-tools"); // Overwrites existing api.tools
|
|
575
|
-
|
|
576
|
-
// Protected mode - prevents overwrites
|
|
577
|
-
const api = await slothlet({
|
|
578
|
-
dir: "./api",
|
|
579
|
-
allowApiOverwrite: false // protection enabled
|
|
580
|
-
});
|
|
581
|
-
await api.addApi("tools", "./new-tools"); // Logs warning and skips
|
|
582
|
-
// Console: "[slothlet] Skipping addApi: API path "tools" final key "tools" already exists..."
|
|
583
|
-
```
|
|
584
|
-
|
|
585
|
-
**Usage Examples:**
|
|
586
|
-
|
|
587
|
-
```javascript
|
|
588
|
-
const api = await slothlet({ dir: "./api" });
|
|
589
|
-
|
|
590
|
-
// Add modules to nested path
|
|
591
|
-
await api.addApi("runtime.plugins", "./plugins");
|
|
592
|
-
api.runtime.plugins.myPlugin(); // New modules accessible
|
|
593
|
-
|
|
594
|
-
// Add to root level
|
|
595
|
-
await api.addApi("utilities", "./utils");
|
|
596
|
-
api.utilities.helperFunc(); // Root-level addition
|
|
597
|
-
|
|
598
|
-
// Deep nesting (creates intermediate objects)
|
|
599
|
-
await api.addApi("services.external.github", "./integrations/github");
|
|
600
|
-
api.services.external.github.getUser(); // Deep path created
|
|
601
|
-
|
|
602
|
-
// Merge into existing namespace
|
|
603
|
-
await api.addApi("math", "./advanced-math"); // Merges with existing api.math
|
|
604
|
-
api.math.add(2, 3); // Original functions preserved
|
|
605
|
-
api.math.advancedCalc(); // New functions added
|
|
606
|
-
```
|
|
607
|
-
|
|
608
|
-
**Path Validation:**
|
|
609
|
-
|
|
610
|
-
```javascript
|
|
611
|
-
// ❌ Invalid paths throw errors
|
|
612
|
-
await api.addApi("", "./modules"); // Empty string
|
|
613
|
-
await api.addApi(".path", "./modules"); // Leading dot
|
|
614
|
-
await api.addApi("path.", "./modules"); // Trailing dot
|
|
615
|
-
await api.addApi("path..name", "./modules"); // Consecutive dots
|
|
616
|
-
|
|
617
|
-
// ✅ Valid paths
|
|
618
|
-
await api.addApi("simple", "./modules"); // Single segment
|
|
619
|
-
await api.addApi("nested.path", "./modules"); // Dotted path
|
|
620
|
-
await api.addApi("very.deep.nested", "./modules"); // Multi-level
|
|
621
|
-
```
|
|
622
|
-
|
|
623
|
-
**Type Safety:**
|
|
624
|
-
|
|
625
|
-
```javascript
|
|
626
|
-
// ✅ Can extend through objects and functions
|
|
627
|
-
api.logger = { info: () => {} };
|
|
628
|
-
await api.addApi("logger.plugins", "./logger-plugins"); // Works - object
|
|
629
|
-
|
|
630
|
-
api.handler = () => "handler";
|
|
631
|
-
await api.addApi("handler.middleware", "./middleware"); // Works - function
|
|
632
|
-
|
|
633
|
-
// ❌ Cannot extend through primitives
|
|
634
|
-
api.config.timeout = 5000;
|
|
635
|
-
await api.addApi("config.timeout.advanced", "./modules"); // Throws error
|
|
636
|
-
```
|
|
637
|
-
|
|
638
|
-
> [!WARNING]
|
|
639
|
-
> **⚠️ Concurrency Limitation:** The `addApi` method is **not thread-safe**. Concurrent calls to `addApi` with overlapping paths may result in race conditions and inconsistent API state. Ensure calls are properly sequenced using `await` or other synchronization mechanisms. Do not call `addApi` from multiple threads/workers simultaneously.
|
|
640
|
-
|
|
641
|
-
> [!NOTE]
|
|
642
|
-
> **📚 For detailed API documentation with comprehensive parameter descriptions, method signatures, and examples, see [docs/API.md](https://github.com/CLDMV/slothlet/blob/HEAD/docs/API.md)**
|
|
643
|
-
|
|
644
|
-
### Live Bindings
|
|
645
|
-
|
|
646
|
-
Access live-bound references in your API modules:
|
|
647
|
-
|
|
648
|
-
```javascript
|
|
649
|
-
// Create API with reference functions
|
|
650
|
-
const api = await slothlet({
|
|
651
|
-
dir: "./api",
|
|
652
|
-
reference: {
|
|
653
|
-
md5: (str) => crypto.createHash("md5").update(str).digest("hex"),
|
|
654
|
-
version: "2.0.0",
|
|
655
|
-
utils: { format: (msg) => `[LOG] ${msg}` }
|
|
656
|
-
}
|
|
657
|
-
});
|
|
658
|
-
```
|
|
659
|
-
|
|
660
|
-
```javascript
|
|
661
|
-
// In your API modules (ESM)
|
|
662
|
-
import { self, context, reference } from "@cldmv/slothlet/runtime";
|
|
663
|
-
|
|
664
|
-
export function myFunction() {
|
|
665
|
-
console.log(context.user); // Access live context
|
|
666
|
-
return self.otherModule.helper(); // Access other API modules
|
|
667
|
-
|
|
668
|
-
// Reference functions are available directly on self
|
|
669
|
-
const hash = self.md5("hello world"); // Access reference function
|
|
670
|
-
console.log(self.version); // Access reference data
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
// Mixed module example (ESM accessing CJS)
|
|
674
|
-
export function processData(data) {
|
|
675
|
-
// Call a CJS module from ESM
|
|
676
|
-
const processed = self.cjsModule.process(data);
|
|
677
|
-
|
|
678
|
-
// Use reference utilities directly
|
|
679
|
-
const logged = self.utils.format(`Processed: ${processed}`);
|
|
680
|
-
return self.md5(logged); // Hash the result
|
|
681
|
-
}
|
|
682
|
-
```
|
|
683
|
-
|
|
684
|
-
```javascript
|
|
685
|
-
// In your CJS modules
|
|
686
|
-
const { self, context, reference } = require("@cldmv/slothlet/runtime");
|
|
687
|
-
|
|
688
|
-
function cjsFunction(data) {
|
|
689
|
-
console.log(context.env); // Access live context
|
|
690
|
-
|
|
691
|
-
// Reference functions available directly on self
|
|
692
|
-
const hash = self.md5(data); // Direct access to reference function
|
|
693
|
-
|
|
694
|
-
return self.esmModule.transform(hash); // Access ESM modules from CJS
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
module.exports = { cjsFunction };
|
|
698
|
-
```
|
|
699
|
-
|
|
700
|
-
### EventEmitter Context Propagation
|
|
701
|
-
|
|
702
|
-
Slothlet automatically preserves AsyncLocalStorage context across all EventEmitter callbacks using Node.js AsyncResource patterns. This ensures your API modules maintain full context access in event handlers without any configuration.
|
|
703
|
-
|
|
704
|
-
```javascript
|
|
705
|
-
// api/tcp-server.mjs - Your API module
|
|
706
|
-
import { self, context } from "@cldmv/slothlet/runtime";
|
|
707
|
-
import net from "node:net";
|
|
708
|
-
|
|
709
|
-
export function createTcpServer() {
|
|
710
|
-
const server = net.createServer();
|
|
711
|
-
|
|
712
|
-
// Connection handler maintains full context automatically
|
|
713
|
-
server.on("connection", (socket) => {
|
|
714
|
-
console.log(`User: ${context.user}`); // ✅ Context preserved
|
|
715
|
-
console.log(`API keys: ${Object.keys(self).length}`); // ✅ Full API access
|
|
716
|
-
|
|
717
|
-
// Socket data handler also maintains context automatically
|
|
718
|
-
socket.on("data", (data) => {
|
|
719
|
-
console.log(`Session: ${context.session}`); // ✅ Context preserved
|
|
720
|
-
console.log(`Processing for: ${context.user}`); // ✅ Context preserved
|
|
721
|
-
|
|
722
|
-
// Full API access in nested event handlers
|
|
723
|
-
const processed = self.dataProcessor.handle(data.toString());
|
|
724
|
-
socket.write(processed);
|
|
725
|
-
});
|
|
726
|
-
|
|
727
|
-
socket.on("error", (err) => {
|
|
728
|
-
// Error handlers also maintain context
|
|
729
|
-
self.logger.error(`Error for user ${context.user}: ${err.message}`);
|
|
730
|
-
});
|
|
731
|
-
});
|
|
732
|
-
|
|
733
|
-
return server;
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
export function startServer(port = 3000) {
|
|
737
|
-
const server = createTcpServer();
|
|
738
|
-
server.listen(port);
|
|
739
|
-
return server;
|
|
740
|
-
}
|
|
741
|
-
```
|
|
742
|
-
|
|
743
|
-
```javascript
|
|
744
|
-
// Usage in your application
|
|
745
|
-
import slothlet from "@cldmv/slothlet";
|
|
746
|
-
|
|
747
|
-
const api = await slothlet({
|
|
748
|
-
dir: "./api",
|
|
749
|
-
context: { user: "alice", session: "tcp-session" }
|
|
750
|
-
});
|
|
751
|
-
|
|
752
|
-
// Start the server - all event handlers will have full context
|
|
753
|
-
const server = api.startServer(8080);
|
|
754
|
-
console.log("TCP server started with context preservation");
|
|
755
|
-
```
|
|
756
|
-
|
|
757
|
-
**Key Benefits:**
|
|
758
|
-
|
|
759
|
-
- ✅ **Automatic**: No configuration needed - works transparently in all API modules
|
|
760
|
-
- ✅ **Complete Context**: Full `context` object and `self` API access in all event handlers
|
|
761
|
-
- ✅ **Nested Events**: Works with any depth of EventEmitter nesting (server → socket → custom emitters)
|
|
762
|
-
- ✅ **Universal Support**: All EventEmitter methods (`on`, `once`, `addListener`) are automatically context-aware
|
|
763
|
-
- ✅ **Production Ready**: Uses Node.js AsyncResource patterns for reliable context propagation
|
|
764
|
-
- ✅ **Clean Shutdown**: Automatically cleans up all AsyncResource instances during shutdown to prevent hanging processes
|
|
765
|
-
- ✅ **Zero Overhead**: Only wraps listeners when context is active, minimal performance impact
|
|
766
|
-
|
|
767
|
-
> [!TIP]
|
|
768
|
-
> **Automatic Context Propagation**: EventEmitter context propagation works automatically in both lazy and eager modes. TCP servers, HTTP servers, custom EventEmitters, and any other event-driven patterns in your API modules will maintain full slothlet context and API access without any code changes.
|
|
769
|
-
|
|
770
|
-
### Class Instance Context Propagation
|
|
771
|
-
|
|
772
|
-
Slothlet automatically preserves AsyncLocalStorage context across all class instance method calls. When your API functions return class instances, slothlet wraps them transparently to ensure all method calls maintain full context access.
|
|
773
|
-
|
|
774
|
-
```javascript
|
|
775
|
-
// api/data-processor.mjs - Your API module
|
|
776
|
-
import { self, context } from "@cldmv/slothlet/runtime";
|
|
777
|
-
|
|
778
|
-
class DataProcessor {
|
|
779
|
-
constructor(config) {
|
|
780
|
-
this.config = config;
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
process(data) {
|
|
784
|
-
// Context automatically available in all methods
|
|
785
|
-
console.log(`Processing for user: ${context.user}`); // ✅ Context preserved
|
|
786
|
-
console.log(`Request ID: ${context.requestId}`); // ✅ Context preserved
|
|
787
|
-
|
|
788
|
-
// Full API access in class methods
|
|
789
|
-
const validated = self.validator.check(data);
|
|
790
|
-
return this.transform(validated);
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
transform(data) {
|
|
794
|
-
// Context preserved in nested method calls
|
|
795
|
-
console.log(`Transforming for: ${context.user}`); // ✅ Context preserved
|
|
796
|
-
|
|
797
|
-
// Call other API modules from class methods
|
|
798
|
-
return self.utils.format(data);
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
export function createProcessor(config) {
|
|
803
|
-
// Return class instance - slothlet automatically wraps it
|
|
804
|
-
return new DataProcessor(config);
|
|
805
|
-
}
|
|
806
|
-
```
|
|
807
|
-
|
|
808
|
-
```javascript
|
|
809
|
-
// Usage in your application
|
|
810
|
-
import slothlet from "@cldmv/slothlet";
|
|
811
|
-
|
|
812
|
-
const api = await slothlet({
|
|
813
|
-
dir: "./api",
|
|
814
|
-
context: { user: "alice", requestId: "req-123" }
|
|
815
|
-
});
|
|
816
|
-
|
|
817
|
-
// Create processor instance - all methods will have full context
|
|
818
|
-
const processor = api.createProcessor({ format: "json" });
|
|
819
|
-
|
|
820
|
-
// All method calls maintain context automatically
|
|
821
|
-
const result = processor.process({ data: "test" });
|
|
822
|
-
console.log("Processing completed with context preservation");
|
|
823
|
-
```
|
|
824
|
-
|
|
825
|
-
**Key Benefits:**
|
|
826
|
-
|
|
827
|
-
- ✅ **Automatic**: Class instances returned from API functions are automatically context-aware
|
|
828
|
-
- ✅ **Transparent**: No code changes needed - works with existing class patterns
|
|
829
|
-
- ✅ **Complete Context**: Full `context` object and `self` API access in all class methods
|
|
830
|
-
- ✅ **Nested Methods**: Context preserved across method chains and internal calls
|
|
831
|
-
- ✅ **Constructor Support**: Context preserved for both function calls and `new` constructor usage
|
|
832
|
-
- ✅ **Performance Optimized**: Method wrapping is cached to avoid overhead on repeated calls
|
|
833
|
-
|
|
834
|
-
> [!TIP]
|
|
835
|
-
> **Universal Class Support**: Any class instance returned from your API functions automatically maintains slothlet context. This includes database models, service classes, utility classes, and any other object-oriented patterns in your codebase.
|
|
836
|
-
|
|
837
|
-
### Hook System
|
|
838
|
-
|
|
839
|
-
Slothlet provides a powerful hook system for intercepting, modifying, and observing API function calls. Hooks work seamlessly across all loading modes (eager/lazy) and runtime types (async/live).
|
|
840
|
-
|
|
841
|
-
#### Hook Configuration
|
|
842
|
-
|
|
843
|
-
Hooks can be configured when creating a slothlet instance:
|
|
844
|
-
|
|
845
|
-
```javascript
|
|
846
|
-
// Enable hooks (simple boolean)
|
|
847
|
-
const api = await slothlet({
|
|
848
|
-
dir: "./api",
|
|
849
|
-
hooks: true // Enable all hooks with default pattern "**"
|
|
850
|
-
});
|
|
851
|
-
|
|
852
|
-
// Enable with custom pattern
|
|
853
|
-
const api = await slothlet({
|
|
854
|
-
dir: "./api",
|
|
855
|
-
hooks: "database.*" // Only enable for database functions
|
|
856
|
-
});
|
|
857
|
-
|
|
858
|
-
// Full configuration object
|
|
859
|
-
const api = await slothlet({
|
|
860
|
-
dir: "./api",
|
|
861
|
-
hooks: {
|
|
862
|
-
enabled: true,
|
|
863
|
-
pattern: "**", // Default pattern for filtering
|
|
864
|
-
suppressErrors: false // Control error throwing behavior
|
|
865
|
-
}
|
|
866
|
-
});
|
|
867
|
-
```
|
|
868
|
-
|
|
869
|
-
**Configuration Options:**
|
|
870
|
-
|
|
871
|
-
- **`enabled`** (boolean): Enable or disable hook execution
|
|
872
|
-
- **`pattern`** (string): Default pattern for filtering which functions hooks apply to
|
|
873
|
-
- **`suppressErrors`** (boolean): Control error throwing behavior
|
|
874
|
-
- `false` (default): Errors are sent to error hooks, THEN thrown (normal behavior)
|
|
875
|
-
- `true`: Errors are sent to error hooks, BUT NOT thrown (returns `undefined`)
|
|
876
|
-
|
|
877
|
-
**Error Suppression Behavior:**
|
|
878
|
-
|
|
879
|
-
Error hooks **ALWAYS receive errors** regardless of this setting. The `suppressErrors` option only controls whether errors are thrown after error hooks execute.
|
|
880
|
-
|
|
881
|
-
> [!IMPORTANT]
|
|
882
|
-
> **Hooks Must Be Enabled**: Error hooks (and all hooks) only execute when `hooks.enabled: true`. If hooks are disabled, errors are thrown normally without any hook execution.
|
|
883
|
-
|
|
884
|
-
When `suppressErrors: true`, errors are caught and sent to error hooks, but not thrown:
|
|
885
|
-
|
|
886
|
-
```javascript
|
|
887
|
-
const api = await slothlet({
|
|
888
|
-
dir: "./api",
|
|
889
|
-
hooks: {
|
|
890
|
-
enabled: true,
|
|
891
|
-
pattern: "**",
|
|
892
|
-
suppressErrors: true // Suppress all errors
|
|
893
|
-
}
|
|
894
|
-
});
|
|
895
|
-
|
|
896
|
-
// Register error hook to monitor failures
|
|
897
|
-
api.hooks.on(
|
|
898
|
-
"error-monitor",
|
|
899
|
-
"error",
|
|
900
|
-
({ path, error, source }) => {
|
|
901
|
-
console.error(`Error in ${path}:`, error.message);
|
|
902
|
-
// Log to monitoring service without crashing
|
|
903
|
-
},
|
|
904
|
-
{ pattern: "**" }
|
|
905
|
-
);
|
|
906
|
-
|
|
907
|
-
// Function errors won't crash the application
|
|
908
|
-
const result = await api.riskyOperation();
|
|
909
|
-
if (result === undefined) {
|
|
910
|
-
// Function failed but didn't throw
|
|
911
|
-
console.log("Operation failed gracefully");
|
|
912
|
-
}
|
|
913
|
-
```
|
|
914
|
-
|
|
915
|
-
**Error Flow:**
|
|
916
|
-
|
|
917
|
-
1. Error occurs (in before hook, function, or after hook)
|
|
918
|
-
2. Error hooks execute and receive the error
|
|
919
|
-
3. **If `suppressErrors: false`** → Error is thrown (crashes if uncaught)
|
|
920
|
-
4. **If `suppressErrors: true`** → Error is NOT thrown, function returns `undefined`
|
|
921
|
-
|
|
922
|
-
**What Gets Suppressed (when `suppressErrors: true`):**
|
|
923
|
-
|
|
924
|
-
- ✅ Before hook errors → Sent to error hooks, NOT thrown
|
|
925
|
-
- ✅ Function execution errors → Sent to error hooks, NOT thrown
|
|
926
|
-
- ✅ After hook errors → Sent to error hooks, NOT thrown
|
|
927
|
-
- ✅ Always hook errors → Sent to error hooks, never thrown (regardless of setting)
|
|
928
|
-
|
|
929
|
-
> [!TIP]
|
|
930
|
-
> **Use Case**: Enable `suppressErrors: true` for resilient systems where you want to monitor failures without crashing. Perfect for background workers, batch processors, or systems with comprehensive error monitoring.
|
|
931
|
-
|
|
932
|
-
> [!CAUTION]
|
|
933
|
-
> **Critical Operations**: For validation or authorization hooks where errors MUST stop execution, use `suppressErrors: false` (default) to ensure errors propagate normally.
|
|
934
|
-
|
|
935
|
-
#### Hook Types
|
|
936
|
-
|
|
937
|
-
**Four hook types with distinct responsibilities:**
|
|
938
|
-
|
|
939
|
-
- **`before`**: Intercept before function execution
|
|
940
|
-
- Modify arguments passed to functions
|
|
941
|
-
- Cancel execution and return custom values (short-circuit)
|
|
942
|
-
- Execute validation or logging before function runs
|
|
943
|
-
- **`after`**: Transform results after successful execution
|
|
944
|
-
- Transform function return values
|
|
945
|
-
- Only runs if function executes (skipped on short-circuit)
|
|
946
|
-
- Chain multiple transformations in priority order
|
|
947
|
-
- **`always`**: Observe final result with full execution context
|
|
948
|
-
- Always executes after function completes
|
|
949
|
-
- Runs even when `before` hooks cancel execution or errors occur
|
|
950
|
-
- Receives complete context: `{ path, result, hasError, errors }`
|
|
951
|
-
- Cannot modify result (read-only observation)
|
|
952
|
-
- Perfect for unified logging of both success and error scenarios
|
|
953
|
-
- **`error`**: Monitor and handle errors
|
|
954
|
-
- Receives detailed error context with source tracking
|
|
955
|
-
- Error source types: 'before', 'function', 'after', 'always', 'unknown'
|
|
956
|
-
- Includes error type, hook ID, hook tag, timestamp, and stack trace
|
|
957
|
-
- Perfect for error monitoring, logging, and alerting
|
|
958
|
-
|
|
959
|
-
#### Basic Usage
|
|
960
|
-
|
|
961
|
-
```javascript
|
|
962
|
-
import slothlet from "@cldmv/slothlet";
|
|
963
|
-
|
|
964
|
-
const api = await slothlet({
|
|
965
|
-
dir: "./api",
|
|
966
|
-
hooks: true // Enable hooks
|
|
967
|
-
});
|
|
968
|
-
|
|
969
|
-
// Before hook: Modify arguments
|
|
970
|
-
api.hooks.on(
|
|
971
|
-
"validate-input",
|
|
972
|
-
"before",
|
|
973
|
-
({ path, args }) => {
|
|
974
|
-
console.log(`Calling ${path} with args:`, args);
|
|
975
|
-
// Return modified args or original
|
|
976
|
-
return [args[0] * 2, args[1] * 2];
|
|
977
|
-
},
|
|
978
|
-
{ pattern: "math.add", priority: 100 }
|
|
979
|
-
);
|
|
980
|
-
|
|
981
|
-
// After hook: Transform result
|
|
982
|
-
api.hooks.on(
|
|
983
|
-
"format-output",
|
|
984
|
-
"after",
|
|
985
|
-
({ path, result }) => {
|
|
986
|
-
console.log(`${path} returned:`, result);
|
|
987
|
-
// Return transformed result
|
|
988
|
-
return result * 10;
|
|
989
|
-
},
|
|
990
|
-
{ pattern: "math.*", priority: 100 }
|
|
991
|
-
);
|
|
992
|
-
|
|
993
|
-
// Always hook: Observe final result with error context
|
|
994
|
-
api.hooks.on(
|
|
995
|
-
"log-execution",
|
|
996
|
-
"always",
|
|
997
|
-
({ path, result, hasError, errors }) => {
|
|
998
|
-
if (hasError) {
|
|
999
|
-
console.log(`${path} failed with ${errors.length} error(s):`, errors);
|
|
1000
|
-
} else {
|
|
1001
|
-
console.log(`${path} succeeded with result:`, result);
|
|
1002
|
-
}
|
|
1003
|
-
// Return value ignored - read-only observer
|
|
1004
|
-
},
|
|
1005
|
-
{ pattern: "**" } // All functions
|
|
1006
|
-
);
|
|
1007
|
-
|
|
1008
|
-
// Call function - hooks execute automatically
|
|
1009
|
-
const result = await api.math.add(2, 3);
|
|
1010
|
-
// Logs: "Calling math.add with args: [2, 3]"
|
|
1011
|
-
// Logs: "math.add returned: 10" (4+6)
|
|
1012
|
-
// Logs: "Final result for math.add: 100" (10*10)
|
|
1013
|
-
// result === 100
|
|
1014
|
-
```
|
|
1015
|
-
|
|
1016
|
-
#### Short-Circuit Execution
|
|
1017
|
-
|
|
1018
|
-
`before` hooks can cancel function execution and return custom values:
|
|
1019
|
-
|
|
1020
|
-
```javascript
|
|
1021
|
-
// Caching hook example
|
|
1022
|
-
const cache = new Map();
|
|
1023
|
-
|
|
1024
|
-
api.hooks.on(
|
|
1025
|
-
"cache-check",
|
|
1026
|
-
"before",
|
|
1027
|
-
({ path, args }) => {
|
|
1028
|
-
const key = JSON.stringify({ path, args });
|
|
1029
|
-
if (cache.has(key)) {
|
|
1030
|
-
console.log(`Cache hit for ${path}`);
|
|
1031
|
-
return cache.get(key); // Short-circuit: return cached value
|
|
1032
|
-
}
|
|
1033
|
-
// Return undefined to continue to function
|
|
1034
|
-
},
|
|
1035
|
-
{ pattern: "**", priority: 1000 } // High priority
|
|
1036
|
-
);
|
|
1037
|
-
|
|
1038
|
-
api.hooks.on(
|
|
1039
|
-
"cache-store",
|
|
1040
|
-
"after",
|
|
1041
|
-
({ path, args, result }) => {
|
|
1042
|
-
const key = JSON.stringify({ path, args });
|
|
1043
|
-
cache.set(key, result);
|
|
1044
|
-
return result; // Pass through
|
|
1045
|
-
},
|
|
1046
|
-
{ pattern: "**", priority: 100 }
|
|
1047
|
-
);
|
|
1048
|
-
|
|
1049
|
-
// First call - executes function and caches
|
|
1050
|
-
await api.math.add(2, 3); // Computes and stores
|
|
1051
|
-
|
|
1052
|
-
// Second call - returns cached value (function not executed)
|
|
1053
|
-
await api.math.add(2, 3); // Cache hit! No computation
|
|
1054
|
-
```
|
|
1055
|
-
|
|
1056
|
-
#### Pattern Matching
|
|
1057
|
-
|
|
1058
|
-
Hooks support flexible pattern matching:
|
|
1059
|
-
|
|
1060
|
-
```javascript
|
|
1061
|
-
// Exact match
|
|
1062
|
-
api.hooks.on("hook1", "before", handler, { pattern: "math.add" });
|
|
1063
|
-
|
|
1064
|
-
// Wildcard: all functions in namespace
|
|
1065
|
-
api.hooks.on("hook2", "before", handler, { pattern: "math.*" });
|
|
1066
|
-
|
|
1067
|
-
// Wildcard: specific function in all namespaces
|
|
1068
|
-
api.hooks.on("hook3", "before", handler, { pattern: "*.add" });
|
|
1069
|
-
|
|
1070
|
-
// Global: all functions
|
|
1071
|
-
api.hooks.on("hook4", "before", handler, { pattern: "**" });
|
|
1072
|
-
```
|
|
1073
|
-
|
|
1074
|
-
#### Priority and Chaining
|
|
1075
|
-
|
|
1076
|
-
Multiple hooks execute in priority order (highest first):
|
|
1077
|
-
|
|
1078
|
-
```javascript
|
|
1079
|
-
// High priority - runs first
|
|
1080
|
-
api.hooks.on(
|
|
1081
|
-
"validate",
|
|
1082
|
-
"before",
|
|
1083
|
-
({ args }) => {
|
|
1084
|
-
if (args[0] < 0) throw new Error("Negative numbers not allowed");
|
|
1085
|
-
return args;
|
|
1086
|
-
},
|
|
1087
|
-
{ pattern: "math.*", priority: 1000 }
|
|
1088
|
-
);
|
|
1089
|
-
|
|
1090
|
-
// Medium priority - runs second
|
|
1091
|
-
api.hooks.on("double", "before", ({ args }) => [args[0] * 2, args[1] * 2], { pattern: "math.*", priority: 500 });
|
|
1092
|
-
|
|
1093
|
-
// Low priority - runs last
|
|
1094
|
-
api.hooks.on(
|
|
1095
|
-
"log",
|
|
1096
|
-
"before",
|
|
1097
|
-
({ path, args }) => {
|
|
1098
|
-
console.log(`Final args for ${path}:`, args);
|
|
1099
|
-
return args;
|
|
1100
|
-
},
|
|
1101
|
-
{ pattern: "math.*", priority: 100 }
|
|
1102
|
-
);
|
|
1103
|
-
```
|
|
56
|
+
---
|
|
1104
57
|
|
|
1105
|
-
|
|
58
|
+
## 🚀 Key Features
|
|
1106
59
|
|
|
1107
|
-
|
|
60
|
+
### 🎯 **Dual Loading Strategies**
|
|
1108
61
|
|
|
1109
|
-
|
|
1110
|
-
|
|
62
|
+
- **Eager Loading**: Immediate loading for maximum performance in production environments
|
|
63
|
+
- **Lazy Loading**: Copy-left materialization with look-ahead proxies (4.3x faster startup, 1.4x faster calls after materialization)
|
|
1111
64
|
|
|
1112
|
-
|
|
1113
|
-
|
|
65
|
+
> [!IMPORTANT]
|
|
66
|
+
> **Function Call Patterns:**
|
|
67
|
+
>
|
|
68
|
+
> - **Lazy Mode**: ALL function calls must be awaited (`await api.math.add(2, 3)`) due to materialization process
|
|
69
|
+
> - **Eager Mode**: Functions behave as originally defined - sync functions are sync (`api.math.add(2, 3)`), async functions are async (`await api.async.process()`)
|
|
1114
70
|
|
|
1115
|
-
|
|
1116
|
-
api.hooks.disable();
|
|
1117
|
-
await api.math.add(2, 3); // No hooks execute
|
|
71
|
+
### ⚡ Performance Excellence
|
|
1118
72
|
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
73
|
+
- **2.9x faster startup** in lazy mode (4.89ms vs 14.29ms)
|
|
74
|
+
- **1.1x faster function calls** in eager mode (0.90μs vs 0.99μs)
|
|
75
|
+
- **Copy-left materialization**: Once loaded, modules stay materialized
|
|
76
|
+
- **Zero dependencies**: Pure Node.js implementation
|
|
1122
77
|
|
|
1123
|
-
|
|
1124
|
-
api.hooks.disable();
|
|
1125
|
-
api.hooks.enable("math.*"); // Only math.* pattern enabled
|
|
1126
|
-
await api.math.add(2, 3); // math.* hooks execute
|
|
1127
|
-
await api.other.func(); // No hooks execute
|
|
1128
|
-
```
|
|
78
|
+
📊 **For comprehensive performance analysis, benchmarks, and recommendations, see [docs/PERFORMANCE.md](https://github.com/CLDMV/slothlet/blob/master/docs/PERFORMANCE.md)**
|
|
1129
79
|
|
|
1130
|
-
|
|
80
|
+
### 🎣 **Hook System**
|
|
1131
81
|
|
|
1132
|
-
|
|
1133
|
-
// List registered hooks
|
|
1134
|
-
const beforeHooks = api.hooks.list("before");
|
|
1135
|
-
const afterHooks = api.hooks.list("after");
|
|
1136
|
-
const allHooks = api.hooks.list(); // All types
|
|
82
|
+
Powerful function interceptor system with 4 hook types:
|
|
1137
83
|
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
84
|
+
- **`before`** - Modify arguments or cancel execution
|
|
85
|
+
- **`after`** - Transform return values
|
|
86
|
+
- **`always`** - Observe final results (read-only)
|
|
87
|
+
- **`error`** - Monitor and handle errors with detailed source tracking
|
|
1141
88
|
|
|
1142
|
-
|
|
1143
|
-
api.hooks.off("math.*");
|
|
89
|
+
Pattern matching, priority control, runtime enable/disable, and short-circuit support included.
|
|
1144
90
|
|
|
1145
|
-
|
|
1146
|
-
api.hooks.clear("before"); // Remove all before hooks
|
|
1147
|
-
api.hooks.clear(); // Remove all hooks
|
|
1148
|
-
```
|
|
91
|
+
🎣 **For complete hook system documentation, see [docs/HOOKS.md](https://github.com/CLDMV/slothlet/blob/master/docs/HOOKS.md)**
|
|
1149
92
|
|
|
1150
|
-
|
|
93
|
+
### 🔄 **Context Propagation**
|
|
1151
94
|
|
|
1152
|
-
|
|
95
|
+
Automatic context preservation across all asynchronous boundaries:
|
|
1153
96
|
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
"error",
|
|
1158
|
-
({ path, error, source }) => {
|
|
1159
|
-
console.error(`Error in ${path}:`, error.message);
|
|
1160
|
-
console.error(`Source: ${source.type}`); // 'before', 'after', 'always', 'function', 'unknown'
|
|
97
|
+
- **EventEmitter propagation**: Context maintained across all event callbacks
|
|
98
|
+
- **Class instance propagation**: Context preserved in class method calls
|
|
99
|
+
- **Zero configuration**: Works automatically with TCP servers, HTTP servers, and custom EventEmitters
|
|
1161
100
|
|
|
1162
|
-
|
|
1163
|
-
console.error("Error occurred in function execution");
|
|
1164
|
-
} else if (["before", "after", "always"].includes(source.type)) {
|
|
1165
|
-
console.error(`Error occurred in ${source.type} hook:`);
|
|
1166
|
-
console.error(` Hook ID: ${source.hookId}`);
|
|
1167
|
-
console.error(` Hook Tag: ${source.hookTag}`);
|
|
1168
|
-
}
|
|
101
|
+
🔄 **For context propagation details, see [docs/CONTEXT-PROPAGATION.md](https://github.com/CLDMV/slothlet/blob/master/docs/CONTEXT-PROPAGATION.md)**
|
|
1169
102
|
|
|
1170
|
-
|
|
1171
|
-
console.error(`Stack trace:\n${source.stack}`);
|
|
103
|
+
### 🔧 **Smart API Management**
|
|
1172
104
|
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
);
|
|
105
|
+
- **Intelligent Flattening**: Clean APIs with automatic structure optimization (`math/math.mjs` → `api.math`)
|
|
106
|
+
- **Smart Naming**: Preserves original capitalization (`auto-ip.mjs` with `autoIP` → `api.autoIP`)
|
|
107
|
+
- **Advanced Sanitization**: Custom naming rules with glob and boundary patterns
|
|
108
|
+
- **Hybrid Exports**: Support for callable APIs with methods, default + named exports
|
|
1178
109
|
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
} catch (error) {
|
|
1182
|
-
// Error hooks executed before this catch block
|
|
1183
|
-
console.log("Caught error:", error);
|
|
1184
|
-
}
|
|
1185
|
-
```
|
|
110
|
+
🏗️ **For module structure examples, see [docs/MODULE-STRUCTURE.md](https://github.com/CLDMV/slothlet/blob/master/docs/MODULE-STRUCTURE.md)**
|
|
111
|
+
📐 **For API flattening rules, see [docs/API-FLATTENING.md](https://github.com/CLDMV/slothlet/blob/master/docs/API-FLATTENING.md)**
|
|
1186
112
|
|
|
1187
|
-
|
|
113
|
+
### 🔗 **Advanced Binding System**
|
|
1188
114
|
|
|
1189
|
-
|
|
115
|
+
- **Live Bindings**: Dynamic context and reference binding for runtime API mutation
|
|
116
|
+
- **Context Isolation**: Dual runtime options (AsyncLocalStorage or live-bindings)
|
|
117
|
+
- **Mixed Module Support**: Seamlessly blend ESM and CommonJS modules
|
|
118
|
+
- **Copy-Left Preservation**: Materialized functions stay materialized
|
|
1190
119
|
|
|
1191
|
-
**
|
|
120
|
+
### 🛠 **Developer Experience**
|
|
1192
121
|
|
|
1193
|
-
-
|
|
1194
|
-
-
|
|
1195
|
-
-
|
|
1196
|
-
-
|
|
1197
|
-
- `"unknown"`: Error source could not be determined
|
|
122
|
+
- **TypeScript-Friendly**: Comprehensive JSDoc annotations with auto-generated declarations
|
|
123
|
+
- **Configurable Debug**: Detailed logging via CLI flags or environment variables
|
|
124
|
+
- **Multiple Instances**: Parameter-based isolation for complex applications
|
|
125
|
+
- **Development Checks**: Built-in environment detection with silent production behavior
|
|
1198
126
|
|
|
1199
|
-
|
|
127
|
+
---
|
|
1200
128
|
|
|
1201
|
-
|
|
1202
|
-
- `source.hookId`: Hook identifier (for hook errors)
|
|
1203
|
-
- `source.hookTag`: Hook tag/name (for hook errors)
|
|
1204
|
-
- `source.timestamp`: ISO timestamp when error occurred
|
|
1205
|
-
- `source.stack`: Full stack trace
|
|
129
|
+
## 📦 Installation
|
|
1206
130
|
|
|
1207
|
-
|
|
131
|
+
### Requirements
|
|
1208
132
|
|
|
1209
|
-
|
|
1210
|
-
const errorStats = {
|
|
1211
|
-
function: 0,
|
|
1212
|
-
before: 0,
|
|
1213
|
-
after: 0,
|
|
1214
|
-
always: 0,
|
|
1215
|
-
byHook: {}
|
|
1216
|
-
};
|
|
133
|
+
- **Node.js v16.4.0 or higher** (required for AsyncLocalStorage support)
|
|
1217
134
|
|
|
1218
|
-
|
|
1219
|
-
"error-analytics",
|
|
1220
|
-
"error",
|
|
1221
|
-
({ path, error, source }) => {
|
|
1222
|
-
// Track error source statistics
|
|
1223
|
-
errorStats[source.type]++;
|
|
1224
|
-
|
|
1225
|
-
if (source.hookId) {
|
|
1226
|
-
if (!errorStats.byHook[source.hookTag]) {
|
|
1227
|
-
errorStats.byHook[source.hookTag] = 0;
|
|
1228
|
-
}
|
|
1229
|
-
errorStats.byHook[source.hookTag]++;
|
|
1230
|
-
}
|
|
1231
|
-
|
|
1232
|
-
// Log detailed error info
|
|
1233
|
-
console.error(`[${source.timestamp}] Error in ${path}:`);
|
|
1234
|
-
console.error(` Type: ${source.type}`);
|
|
1235
|
-
console.error(` Message: ${error.message}`);
|
|
1236
|
-
|
|
1237
|
-
if (source.type === "function") {
|
|
1238
|
-
// Function-level error - might be a bug in implementation
|
|
1239
|
-
console.error(" Action: Review function implementation");
|
|
1240
|
-
} else {
|
|
1241
|
-
// Hook-level error - might be a bug in hook logic
|
|
1242
|
-
console.error(` Action: Review ${source.hookTag} hook (${source.type})`);
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
// Send to monitoring service
|
|
1246
|
-
sendToMonitoring({
|
|
1247
|
-
timestamp: source.timestamp,
|
|
1248
|
-
path,
|
|
1249
|
-
errorType: source.type,
|
|
1250
|
-
hookId: source.hookId,
|
|
1251
|
-
hookTag: source.hookTag,
|
|
1252
|
-
message: error.message,
|
|
1253
|
-
stack: source.stack
|
|
1254
|
-
});
|
|
1255
|
-
},
|
|
1256
|
-
{ pattern: "**" }
|
|
1257
|
-
);
|
|
135
|
+
### Install
|
|
1258
136
|
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
// {
|
|
1262
|
-
// function: 5,
|
|
1263
|
-
// before: 2,
|
|
1264
|
-
// after: 1,
|
|
1265
|
-
// always: 0,
|
|
1266
|
-
// byHook: {
|
|
1267
|
-
// "validate-input": 2,
|
|
1268
|
-
// "format-output": 1
|
|
1269
|
-
// }
|
|
1270
|
-
// }
|
|
137
|
+
```bash
|
|
138
|
+
npm install @cldmv/slothlet
|
|
1271
139
|
```
|
|
1272
140
|
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
- Errors from `before` and `after` hooks are re-thrown after error hooks execute
|
|
1276
|
-
- Errors from `always` hooks are caught and logged but do NOT crash execution
|
|
1277
|
-
- Error hooks themselves do not receive errors from other error hooks (no recursion)
|
|
1278
|
-
- The `_hookSourceReported` flag prevents double-reporting of errors
|
|
141
|
+
---
|
|
1279
142
|
|
|
1280
|
-
|
|
143
|
+
## 🚀 Quick Start
|
|
1281
144
|
|
|
1282
|
-
|
|
145
|
+
### ESM (ES Modules)
|
|
1283
146
|
|
|
1284
147
|
```javascript
|
|
1285
|
-
|
|
1286
|
-
const api1 = await slothlet({ dir: "./api", lazy: false, runtime: "async", hooks: true });
|
|
1287
|
-
|
|
1288
|
-
// Eager + Live Bindings
|
|
1289
|
-
const api2 = await slothlet({ dir: "./api", lazy: false, runtime: "live", hooks: true });
|
|
1290
|
-
|
|
1291
|
-
// Lazy + AsyncLocalStorage
|
|
1292
|
-
const api3 = await slothlet({ dir: "./api", lazy: true, runtime: "async", hooks: true });
|
|
1293
|
-
|
|
1294
|
-
// Lazy + Live Bindings
|
|
1295
|
-
const api4 = await slothlet({ dir: "./api", lazy: true, runtime: "live", hooks: true });
|
|
1296
|
-
|
|
1297
|
-
// Same hook code works with all configurations
|
|
1298
|
-
[api1, api2, api3, api4].forEach((api) => {
|
|
1299
|
-
api.hooks.on(
|
|
1300
|
-
"universal",
|
|
1301
|
-
"before",
|
|
1302
|
-
({ args }) => {
|
|
1303
|
-
return [args[0] * 10, args[1] * 10];
|
|
1304
|
-
},
|
|
1305
|
-
{ pattern: "math.add" }
|
|
1306
|
-
);
|
|
1307
|
-
});
|
|
1308
|
-
```
|
|
1309
|
-
|
|
1310
|
-
**Key Benefits:**
|
|
1311
|
-
|
|
1312
|
-
- ✅ **Universal**: Works across all 4 mode/runtime combinations
|
|
1313
|
-
- ✅ **Flexible**: Pattern matching with wildcards and priorities
|
|
1314
|
-
- ✅ **Powerful**: Modify args, transform results, observe execution
|
|
1315
|
-
- ✅ **Composable**: Chain multiple hooks with priority control
|
|
1316
|
-
- ✅ **Dynamic**: Enable/disable at runtime globally or by pattern
|
|
1317
|
-
- ✅ **Observable**: Separate hook types for different responsibilities
|
|
1318
|
-
|
|
1319
|
-
### API Mode Configuration
|
|
1320
|
-
|
|
1321
|
-
The `api_mode` option controls how slothlet handles root-level default function exports:
|
|
1322
|
-
|
|
1323
|
-
#### Auto-Detection (Recommended)
|
|
148
|
+
import slothlet from "@cldmv/slothlet";
|
|
1324
149
|
|
|
1325
|
-
|
|
150
|
+
// Direct usage - eager mode by default
|
|
1326
151
|
const api = await slothlet({
|
|
1327
|
-
|
|
152
|
+
dir: "./api",
|
|
153
|
+
context: { user: "alice" }
|
|
1328
154
|
});
|
|
1329
155
|
|
|
1330
|
-
//
|
|
1331
|
-
|
|
1332
|
-
|
|
156
|
+
// Eager mode: Functions behave as originally defined
|
|
157
|
+
const result = api.math.add(2, 3); // Sync function - no await needed
|
|
158
|
+
const asyncResult = await api.async.processData({ data: "async" });
|
|
1333
159
|
|
|
1334
|
-
//
|
|
1335
|
-
|
|
160
|
+
// Access both ESM and CJS modules seamlessly
|
|
161
|
+
const esmResult = api.mathEsm.multiply(4, 5);
|
|
162
|
+
const cjsResult = api.mathCjs.divide(10, 2);
|
|
1336
163
|
```
|
|
1337
164
|
|
|
1338
|
-
|
|
165
|
+
### CommonJS (CJS)
|
|
1339
166
|
|
|
1340
167
|
```javascript
|
|
168
|
+
const slothlet = require("@cldmv/slothlet");
|
|
169
|
+
|
|
170
|
+
// Same usage pattern works with CommonJS
|
|
1341
171
|
const api = await slothlet({
|
|
1342
|
-
|
|
172
|
+
dir: "./api",
|
|
173
|
+
context: { env: "production" }
|
|
1343
174
|
});
|
|
1344
175
|
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
const result = api("World"); // Calls root default function
|
|
1348
|
-
const math = api.math.add(2, 3); // Also access other modules
|
|
176
|
+
const result = api.math.multiply(4, 5);
|
|
177
|
+
const mixedResult = await api.interop.processData({ data: "test" });
|
|
1349
178
|
```
|
|
1350
179
|
|
|
1351
|
-
|
|
180
|
+
### Lazy Loading Mode
|
|
1352
181
|
|
|
1353
182
|
```javascript
|
|
183
|
+
import slothlet from "@cldmv/slothlet";
|
|
184
|
+
|
|
185
|
+
// Lazy mode with copy-left materialization
|
|
1354
186
|
const api = await slothlet({
|
|
1355
|
-
|
|
187
|
+
mode: "lazy", // Preferred syntax
|
|
188
|
+
dir: "./api",
|
|
189
|
+
apiDepth: 3
|
|
1356
190
|
});
|
|
1357
191
|
|
|
1358
|
-
//
|
|
1359
|
-
|
|
1360
|
-
const result = api.rootFunction("World"); // Access via property
|
|
1361
|
-
const math = api.math.add(2, 3); // Normal module access
|
|
1362
|
-
```
|
|
1363
|
-
|
|
1364
|
-
---
|
|
1365
|
-
|
|
1366
|
-
## 🏗 Module Structure & Examples
|
|
1367
|
-
|
|
1368
|
-
Slothlet supports sophisticated module organization patterns with seamless ESM/CJS interoperability:
|
|
1369
|
-
|
|
1370
|
-
### Root-Level Modules
|
|
1371
|
-
|
|
1372
|
-
```text
|
|
1373
|
-
root-math.mjs → api.rootMath (dash-to-camelCase)
|
|
1374
|
-
rootstring.mjs → api.rootstring
|
|
1375
|
-
config.mjs → api.config
|
|
1376
|
-
```
|
|
1377
|
-
|
|
1378
|
-
### Filename-Folder Matching Modules
|
|
1379
|
-
|
|
1380
|
-
```text
|
|
1381
|
-
math/math.mjs → api.math (filename matches folder)
|
|
1382
|
-
string/string.mjs → api.string (filename matches folder)
|
|
1383
|
-
util/util.cjs → api.util (CJS support with filename matching)
|
|
1384
|
-
```
|
|
1385
|
-
|
|
1386
|
-
### Multi-File Modules
|
|
1387
|
-
|
|
1388
|
-
```text
|
|
1389
|
-
multi/
|
|
1390
|
-
├── alpha.mjs → api.multi.alpha
|
|
1391
|
-
├── beta.mjs → api.multi.beta
|
|
1392
|
-
└── gamma.cjs → api.multi.gamma (mixed ESM/CJS)
|
|
1393
|
-
```
|
|
1394
|
-
|
|
1395
|
-
### Function-Based Modules
|
|
1396
|
-
|
|
1397
|
-
```text
|
|
1398
|
-
funcmod/funcmod.mjs → api.funcmod() (callable function)
|
|
1399
|
-
multi_func/
|
|
1400
|
-
├── alpha.mjs → api.multi_func.alpha()
|
|
1401
|
-
└── beta.cjs → api.multi_func.beta() (CJS callable)
|
|
1402
|
-
```
|
|
1403
|
-
|
|
1404
|
-
### Mixed ESM/CJS Modules
|
|
1405
|
-
|
|
1406
|
-
```text
|
|
1407
|
-
interop/
|
|
1408
|
-
├── esm-module.mjs → api.interop.esmModule
|
|
1409
|
-
├── cjs-module.cjs → api.interop.cjsModule
|
|
1410
|
-
└── mixed.mjs → api.interop.mixed (calls both ESM and CJS)
|
|
1411
|
-
```
|
|
1412
|
-
|
|
1413
|
-
### Hybrid Export Patterns
|
|
1414
|
-
|
|
1415
|
-
```text
|
|
1416
|
-
exportDefault/exportDefault.mjs → api.exportDefault() (callable with methods)
|
|
1417
|
-
objectDefaultMethod/ → api.objectDefaultMethod() (object with default)
|
|
1418
|
-
```
|
|
1419
|
-
|
|
1420
|
-
### Nested Structure
|
|
192
|
+
// First access: materialization overhead (~1.45ms average)
|
|
193
|
+
const result1 = await api.math.add(2, 3);
|
|
1421
194
|
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
└── date/
|
|
1425
|
-
├── date.mjs → api.nested.date
|
|
1426
|
-
└── util.cjs → api.nested.dateUtil
|
|
1427
|
-
advanced/
|
|
1428
|
-
├── selfObject/ → api.advanced.selfObject
|
|
1429
|
-
└── nest*/ → Various nesting examples
|
|
195
|
+
// Subsequent access: materialized function (near-eager performance)
|
|
196
|
+
const result2 = await api.math.add(5, 7);
|
|
1430
197
|
```
|
|
1431
198
|
|
|
1432
|
-
###
|
|
1433
|
-
|
|
1434
|
-
```text
|
|
1435
|
-
util/
|
|
1436
|
-
├── controller.mjs → api.util.controller
|
|
1437
|
-
├── extract.cjs → api.util.extract (CJS utility)
|
|
1438
|
-
└── url/
|
|
1439
|
-
├── parser.mjs → api.util.url.parser
|
|
1440
|
-
└── builder.cjs → api.util.url.builder (mixed)
|
|
1441
|
-
```
|
|
199
|
+
### Hook System Example
|
|
1442
200
|
|
|
1443
|
-
|
|
201
|
+
```javascript
|
|
202
|
+
import slothlet from "@cldmv/slothlet";
|
|
1444
203
|
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
```
|
|
204
|
+
const api = await slothlet({
|
|
205
|
+
dir: "./api",
|
|
206
|
+
hooks: true // Enable hooks
|
|
207
|
+
});
|
|
1450
208
|
|
|
1451
|
-
|
|
209
|
+
// Before hook: Modify arguments
|
|
210
|
+
api.hooks.on(
|
|
211
|
+
"validate",
|
|
212
|
+
"before",
|
|
213
|
+
({ path, args }) => {
|
|
214
|
+
console.log(`Calling ${path} with args:`, args);
|
|
215
|
+
return [args[0] * 2, args[1] * 2];
|
|
216
|
+
},
|
|
217
|
+
{ pattern: "math.add", priority: 100 }
|
|
218
|
+
);
|
|
1452
219
|
|
|
1453
|
-
|
|
220
|
+
// After hook: Transform result
|
|
221
|
+
api.hooks.on(
|
|
222
|
+
"format",
|
|
223
|
+
"after",
|
|
224
|
+
({ path, result }) => {
|
|
225
|
+
console.log(`${path} returned:`, result);
|
|
226
|
+
return result * 10;
|
|
227
|
+
},
|
|
228
|
+
{ pattern: "math.*", priority: 100 }
|
|
229
|
+
);
|
|
1454
230
|
|
|
1455
|
-
|
|
231
|
+
// Always hook: Observe final result
|
|
232
|
+
api.hooks.on(
|
|
233
|
+
"observe",
|
|
234
|
+
"always",
|
|
235
|
+
({ path, result, hasError }) => {
|
|
236
|
+
console.log(hasError ? `${path} failed` : `${path} succeeded`);
|
|
237
|
+
},
|
|
238
|
+
{ pattern: "**" }
|
|
239
|
+
);
|
|
1456
240
|
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
241
|
+
// Error hook: Monitor errors with source tracking
|
|
242
|
+
api.hooks.on(
|
|
243
|
+
"error-logger",
|
|
244
|
+
"error",
|
|
245
|
+
({ path, error, source }) => {
|
|
246
|
+
console.error(`Error in ${path}:`, error.message);
|
|
247
|
+
console.error(`Source: ${source.type}`); // 'before', 'after', 'function', 'always'
|
|
248
|
+
},
|
|
249
|
+
{ pattern: "**" }
|
|
250
|
+
);
|
|
1460
251
|
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
string/string.mjs (exports { string: {...} }) → api.string (flattened)
|
|
1464
|
-
util/util.mjs (exports { util: {...} }) → api.util (flattened)
|
|
252
|
+
// Call function - hooks execute automatically
|
|
253
|
+
const result = await api.math.add(2, 3);
|
|
1465
254
|
```
|
|
1466
255
|
|
|
1467
|
-
###
|
|
1468
|
-
|
|
1469
|
-
**When:** A file has **no default export** and **only named exports**
|
|
1470
|
-
**Why:** The file acts as a pure function collection, not a module with a main export
|
|
1471
|
-
**Reasoning:** If there's no "main thing" (default export), treat all functions as equals at the root level
|
|
256
|
+
### Dynamic API Extension with addApi()
|
|
1472
257
|
|
|
1473
|
-
|
|
1474
|
-
connection.mjs (exports { connect, disconnect, isConnected })
|
|
1475
|
-
→ api.connect(), api.disconnect(), api.isConnected()
|
|
1476
|
-
Because: No default export = no main "connection" object needed
|
|
258
|
+
Load additional modules at runtime and extend your API dynamically:
|
|
1477
259
|
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
Because: No default export = these are standalone utility functions
|
|
260
|
+
```javascript
|
|
261
|
+
import slothlet from "@cldmv/slothlet";
|
|
1481
262
|
|
|
1482
|
-
|
|
1483
|
-
→ api.cloneState(), api.emitLog(), api.reset(), api.update()
|
|
1484
|
-
Because: No default export = treat as individual state utilities
|
|
1485
|
-
```
|
|
263
|
+
const api = await slothlet({ dir: "./api" });
|
|
1486
264
|
|
|
1487
|
-
|
|
265
|
+
// Add plugins at runtime
|
|
266
|
+
await api.addApi("plugins", "./plugins-folder");
|
|
267
|
+
api.plugins.myPlugin();
|
|
1488
268
|
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
269
|
+
// Create nested API structures
|
|
270
|
+
await api.addApi("runtime.plugins", "./more-plugins");
|
|
271
|
+
api.runtime.plugins.loader();
|
|
1492
272
|
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
273
|
+
// Add with metadata for security/authorization
|
|
274
|
+
await api.addApi("plugins.trusted", "./trusted-plugins", {
|
|
275
|
+
trusted: true,
|
|
276
|
+
permissions: ["read", "write", "admin"],
|
|
277
|
+
version: "1.0.0"
|
|
278
|
+
});
|
|
1496
279
|
|
|
1497
|
-
|
|
1498
|
-
|
|
280
|
+
await api.addApi("plugins.external", "./third-party", {
|
|
281
|
+
trusted: false,
|
|
282
|
+
permissions: ["read"]
|
|
283
|
+
});
|
|
1499
284
|
|
|
1500
|
-
|
|
1501
|
-
|
|
285
|
+
// Access metadata on functions
|
|
286
|
+
const meta = api.plugins.trusted.someFunc.__metadata;
|
|
287
|
+
console.log(meta.trusted); // true
|
|
288
|
+
console.log(meta.permissions); // ["read", "write", "admin"]
|
|
1502
289
|
```
|
|
1503
290
|
|
|
1504
|
-
|
|
291
|
+
**Security & Authorization with metadataAPI:**
|
|
1505
292
|
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
293
|
+
```javascript
|
|
294
|
+
// Inside your modules, use metadataAPI for runtime introspection
|
|
295
|
+
import { metadataAPI } from "@cldmv/slothlet/runtime";
|
|
1509
296
|
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
297
|
+
export async function sensitiveOperation() {
|
|
298
|
+
// Check caller's metadata
|
|
299
|
+
const caller = await metadataAPI.caller();
|
|
1513
300
|
|
|
1514
|
-
|
|
1515
|
-
|
|
301
|
+
if (!caller?.trusted) {
|
|
302
|
+
throw new Error("Unauthorized: Caller is not trusted");
|
|
303
|
+
}
|
|
1516
304
|
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
305
|
+
if (!caller.permissions.includes("admin")) {
|
|
306
|
+
throw new Error("Unauthorized: Admin permission required");
|
|
307
|
+
}
|
|
1520
308
|
|
|
1521
|
-
|
|
309
|
+
// Proceed with secure operation
|
|
310
|
+
return "Success";
|
|
311
|
+
}
|
|
1522
312
|
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
**Reasoning:** When file/folder names match, assume they represent the same logical concept
|
|
313
|
+
// Get metadata by path
|
|
314
|
+
const meta = await metadataAPI.get("plugins.trusted.someFunc");
|
|
1526
315
|
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
316
|
+
// Get current function's metadata
|
|
317
|
+
const self = await metadataAPI.self();
|
|
318
|
+
console.log("My version:", self.version);
|
|
1530
319
|
```
|
|
1531
320
|
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
```mermaid
|
|
1535
|
-
flowchart TD
|
|
1536
|
-
FILE[Module File] --> ROOT{Root Level?}
|
|
1537
|
-
|
|
1538
|
-
ROOT -->|Yes| PRESERVE[Preserve Namespace<br/>api.rootMath, api.config]
|
|
1539
|
-
|
|
1540
|
-
ROOT -->|No| SELFREFER{Self-Referential?}
|
|
1541
|
-
SELFREFER -->|Yes| NAMESPACE[Use Namespace<br/>api.config]
|
|
1542
|
-
|
|
1543
|
-
SELFREFER -->|No| HASDEFAULT{Has Default Export?}
|
|
1544
|
-
HASDEFAULT -->|Yes| NAMESPACE
|
|
1545
|
-
|
|
1546
|
-
HASDEFAULT -->|No| NAMEDONLY{Only Named Exports?}
|
|
1547
|
-
NAMEDONLY -->|Yes| FLATTEN["Flatten All Named Exports<br/>api.connect, api.disconnect"]
|
|
321
|
+
🔒 **For complete metadata system documentation, see [docs/METADATA.md](https://github.com/CLDMV/slothlet/blob/master/docs/METADATA.md)**
|
|
1548
322
|
|
|
1549
|
-
|
|
1550
|
-
SINGLENAMED -->|Yes| FLATTENSINGLE[Flatten Single Export<br/>api.math]
|
|
1551
|
-
SINGLENAMED -->|No| NAMESPACE
|
|
1552
|
-
|
|
1553
|
-
style FLATTEN fill:#e1f5fe,stroke:#0277bd,stroke-width:2px,color:#000
|
|
1554
|
-
style FLATTENSINGLE fill:#e8f5e8,stroke:#2e7d32,stroke-width:2px,color:#000
|
|
1555
|
-
style NAMESPACE fill:#fff3e0,stroke:#e65100,stroke-width:2px,color:#000
|
|
1556
|
-
style PRESERVE fill:#fce4ec,stroke:#c2185b,stroke-width:2px,color:#000
|
|
1557
|
-
```
|
|
323
|
+
---
|
|
1558
324
|
|
|
1559
|
-
|
|
325
|
+
## 📚 Configuration Options
|
|
326
|
+
|
|
327
|
+
| Option | Type | Default | Description |
|
|
328
|
+
| ------------------- | --------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
329
|
+
| `dir` | `string` | `"api"` | Directory to load API modules from (absolute or relative path) |
|
|
330
|
+
| `mode` | `string` | `"eager"` | **New** loading mode - `"lazy"` for on-demand loading, `"eager"` for immediate loading |
|
|
331
|
+
| `lazy` | `boolean` | `false` | **Legacy** loading strategy (use `mode` instead) |
|
|
332
|
+
| `engine` | `string` | `"singleton"` | Execution environment: `"singleton"`, `"vm"`, `"worker"`, or `"fork"` (experimental modes) |
|
|
333
|
+
| `runtime` | `string` | `"async"` | Runtime binding system: `"async"` for AsyncLocalStorage (requires Node.js v16.4.0+), `"live"` for live-bindings (works on Node.js v12.20.0+) |
|
|
334
|
+
| `apiDepth` | `number` | `Infinity` | Directory traversal depth - `0` for root only, `Infinity` for all levels |
|
|
335
|
+
| `debug` | `boolean` | `false` | Enable verbose logging (also via `--slothletdebug` flag or `SLOTHLET_DEBUG=true` env var) |
|
|
336
|
+
| `api_mode` | `string` | `"auto"` | API structure behavior: `"auto"` (detect), `"function"` (force callable), `"object"` (force object) |
|
|
337
|
+
| `allowApiOverwrite` | `boolean` | `true` | Allow `addApi()` to overwrite existing endpoints (`false` = prevent overwrites with warning) |
|
|
338
|
+
| `context` | `object` | `{}` | Context data injected into live-binding (available via `import { context } from "@cldmv/slothlet/runtime"`) |
|
|
339
|
+
| `reference` | `object` | `{}` | Reference object merged into API root level |
|
|
340
|
+
| `sanitize` | `object` | `{}` | Advanced filename-to-API transformation control with `lowerFirst`, `preserveAllUpper`, `preserveAllLower`, and `rules` (supports exact matches, glob patterns `*json*`, and boundary patterns `**url**`) |
|
|
341
|
+
| `hooks` | `mixed` | `false` | Enable hook system: `true` (enable all), `"pattern"` (enable with pattern), or object with `enabled`, `pattern`, `suppressErrors` options |
|
|
342
|
+
|
|
343
|
+
**For complete API documentation with detailed parameter descriptions and examples, see [docs/generated/API.md](https://github.com/CLDMV/slothlet/blob/master/docs/generated/API.md)**
|
|
1560
344
|
|
|
1561
|
-
|
|
1562
|
-
_Why it matters:_ Reduces typing, improves readability, and matches how you'd naturally call connection functions
|
|
1563
|
-
- **Intuitive Structure**: File organization matches API usage patterns
|
|
1564
|
-
_Why it matters:_ Files with only utility functions flatten (no main export = no namespace needed), while files with main exports preserve their intended structure
|
|
1565
|
-
- **Flexible Organization**: Mix flattened and nested patterns as needed
|
|
1566
|
-
_Why it matters:_ You can organize files by purpose (`connection.mjs` for utilities, `config.mjs` for main objects) and slothlet automatically creates the right API structure
|
|
1567
|
-
- **Developer Intent Respected**: Export structure signals your architectural intentions
|
|
1568
|
-
_Why it matters:_ Default exports = "this is a main thing with methods", named exports only = "these are utility functions"
|
|
1569
|
-
- **Backward Compatibility**: Existing APIs continue to work as expected
|
|
1570
|
-
_Why it matters:_ The rules are additive - existing filename-matching and default export patterns still work exactly the same
|
|
345
|
+
---
|
|
1571
346
|
|
|
1572
347
|
## 🔀 How Slothlet Works: Loading Modes Explained
|
|
1573
348
|
|
|
@@ -1695,59 +470,105 @@ flowchart TD
|
|
|
1695
470
|
|
|
1696
471
|
### Eager Mode (Default - Production Ready)
|
|
1697
472
|
|
|
473
|
+
**Best for:** Production environments, maximum runtime performance, predictable behavior
|
|
474
|
+
|
|
1698
475
|
```javascript
|
|
1699
476
|
const api = await slothlet({ dir: "./api" }); // lazy: false by default
|
|
1700
477
|
|
|
1701
|
-
// Functions behave as originally defined
|
|
1702
|
-
const result = api.math.add(2, 3); // Sync
|
|
1703
|
-
const
|
|
1704
|
-
|
|
1705
|
-
// Async functions still need await (as originally defined)
|
|
1706
|
-
const asyncResult = await api.async.processData({ data: "test" }); // Original async function
|
|
1707
|
-
|
|
1708
|
-
// ESM+CJS works seamlessly with native behavior
|
|
1709
|
-
const mixed = api.interop.process({ data: "test" }); // Sync or async as defined
|
|
478
|
+
// Functions behave as originally defined
|
|
479
|
+
const result = api.math.add(2, 3); // Sync - no await needed
|
|
480
|
+
const asyncResult = await api.async.processData({ data: "test" }); // Async needs await
|
|
1710
481
|
```
|
|
1711
482
|
|
|
1712
483
|
**Benefits:**
|
|
1713
484
|
|
|
1714
|
-
- ✅ Fastest function calls (0.
|
|
1715
|
-
- ✅ Predictable performance
|
|
1716
|
-
- ✅
|
|
1717
|
-
- ✅ Functions behave exactly as originally defined (sync stays sync, async stays async)
|
|
1718
|
-
- ✅ Optimal for production environments
|
|
485
|
+
- ✅ Fastest function calls (0.90μs average)
|
|
486
|
+
- ✅ Predictable performance (no materialization delays)
|
|
487
|
+
- ✅ Functions behave exactly as originally defined
|
|
1719
488
|
|
|
1720
489
|
### Lazy Mode with Copy-Left Materialization (Production Ready)
|
|
1721
490
|
|
|
1722
|
-
|
|
1723
|
-
const api = await slothlet({ lazy: true, dir: "./api" });
|
|
491
|
+
**Best for:** Startup-sensitive applications, memory efficiency, loading only what you use
|
|
1724
492
|
|
|
1725
|
-
|
|
1726
|
-
const
|
|
1727
|
-
const result2 = await api.math.add(5, 7); // Subsequent: ~0.5μs (materialized)
|
|
493
|
+
```javascript
|
|
494
|
+
const api = await slothlet({ mode: "lazy", dir: "./api" });
|
|
1728
495
|
|
|
1729
|
-
//
|
|
1730
|
-
const
|
|
1731
|
-
const
|
|
496
|
+
// ALL calls must be awaited (materialization process)
|
|
497
|
+
const result1 = await api.math.add(2, 3); // First: ~371μs (materialization)
|
|
498
|
+
const result2 = await api.math.add(5, 7); // Subsequent: 0.99μs (materialized)
|
|
1732
499
|
```
|
|
1733
500
|
|
|
1734
501
|
**Benefits:**
|
|
1735
502
|
|
|
1736
|
-
- ✅
|
|
503
|
+
- ✅ 2.9x faster startup (4.89ms vs 14.29ms)
|
|
504
|
+
- ✅ Near-equal function call performance (0.99μs vs 0.90μs eager)
|
|
1737
505
|
- ✅ Memory efficient (loads only what you use)
|
|
1738
506
|
- ✅ Copy-left optimization (once loaded, stays loaded)
|
|
1739
|
-
- ✅ Optimal for startup-sensitive applications
|
|
1740
|
-
- ⚠️ All function calls require await (regardless of original sync/async nature)
|
|
1741
|
-
|
|
1742
|
-
**Performance Summary:**
|
|
1743
507
|
|
|
1744
508
|
> [!TIP]
|
|
1745
|
-
> **Choose your
|
|
509
|
+
> **Choose your strategy:**
|
|
1746
510
|
>
|
|
1747
|
-
> - **Startup
|
|
1748
|
-
> - **
|
|
1749
|
-
> - **
|
|
1750
|
-
> - **
|
|
511
|
+
> - **Startup-sensitive?** → Lazy mode (2.9x faster startup)
|
|
512
|
+
> - **Call-intensive?** → Eager mode (1.1x faster calls)
|
|
513
|
+
> - **Need predictability?** → Eager mode (no materialization delays)
|
|
514
|
+
> - **Large API, use subset?** → Lazy mode (memory efficient)
|
|
515
|
+
|
|
516
|
+
---
|
|
517
|
+
|
|
518
|
+
## 📊 Performance Analysis
|
|
519
|
+
|
|
520
|
+
For comprehensive performance benchmarks, analysis, and recommendations:
|
|
521
|
+
|
|
522
|
+
**📈 [See docs/PERFORMANCE.md](https://github.com/CLDMV/slothlet/blob/master/docs/PERFORMANCE.md)**
|
|
523
|
+
|
|
524
|
+
Key highlights:
|
|
525
|
+
|
|
526
|
+
- Detailed startup vs runtime performance comparison
|
|
527
|
+
- Memory usage analysis by loading mode
|
|
528
|
+
- Materialization cost breakdown by module type
|
|
529
|
+
- Real-world performance recommendations
|
|
530
|
+
|
|
531
|
+
[![CodeFactor]][codefactor_url] [![npms.io score]][npms_url]
|
|
532
|
+
|
|
533
|
+
[![npm unpacked size]][npm_size_url] [![Repo size]][repo_size_url]
|
|
534
|
+
|
|
535
|
+
---
|
|
536
|
+
|
|
537
|
+
## 📚 Documentation
|
|
538
|
+
|
|
539
|
+
### Core Documentation
|
|
540
|
+
|
|
541
|
+
- **[API Documentation](https://github.com/CLDMV/slothlet/blob/master/docs/generated/API.md)** - Complete API reference with examples and detailed parameter descriptions
|
|
542
|
+
- **[Performance Analysis](https://github.com/CLDMV/slothlet/blob/master/docs/PERFORMANCE.md)** - Detailed benchmarks and recommendations
|
|
543
|
+
- **[Contributing Guide](CONTRIBUTING.md)** - How to contribute to the project
|
|
544
|
+
- **[Security Policy](SECURITY.md)** - Security guidelines and reporting
|
|
545
|
+
- **[Test Documentation](api_tests)** - Comprehensive test module examples
|
|
546
|
+
|
|
547
|
+
### Technical Guides
|
|
548
|
+
|
|
549
|
+
- **[Hook System](https://github.com/CLDMV/slothlet/blob/master/docs/HOOKS.md)** - Complete hook system documentation with 4 hook types, pattern matching, and examples
|
|
550
|
+
- **[Context Propagation](https://github.com/CLDMV/slothlet/blob/master/docs/CONTEXT-PROPAGATION.md)** - EventEmitter and class instance context preservation
|
|
551
|
+
- **[Metadata System](https://github.com/CLDMV/slothlet/blob/master/docs/METADATA.md)** - Function metadata tagging and runtime introspection for security, authorization, and auditing
|
|
552
|
+
- **[Module Structure](https://github.com/CLDMV/slothlet/blob/master/docs/MODULE-STRUCTURE.md)** - Comprehensive module organization patterns and examples
|
|
553
|
+
- **[API Flattening](https://github.com/CLDMV/slothlet/blob/master/docs/API-FLATTENING.md)** - The 5 flattening rules with decision tree and benefits
|
|
554
|
+
|
|
555
|
+
### API Rules & Transformation
|
|
556
|
+
|
|
557
|
+
- **[API Rules](https://github.com/CLDMV/slothlet/blob/master/docs/API-RULES.md)** - Systematically verified API transformation rules with real examples and test cases
|
|
558
|
+
- **[API Rules Conditions](https://github.com/CLDMV/slothlet/blob/master/docs/API-RULES-CONDITIONS.md)** - Complete technical reference of all 26 conditional statements that control API generation
|
|
559
|
+
|
|
560
|
+
### Changelog
|
|
561
|
+
|
|
562
|
+
- **[v2.9](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.9.md)** - Per-Request Context Isolation & API Builder Modularization (December 30, 2025)
|
|
563
|
+
- **[v2.8](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.8.md)** - NPM security fixes and package workflow updates (December 26, 2025)
|
|
564
|
+
- **[v2.7](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.7.md)** - Hook System with 4 interceptor types (December 20, 2025)
|
|
565
|
+
- **[v2.6](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.6.md)** - Mode/Engine options and deep nested path fixes (November 10, 2025)
|
|
566
|
+
- **[v2.5](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.5.md)** - Architectural consolidation and API consistency (October 20, 2025)
|
|
567
|
+
- **[v2.4](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.4.md)** - Multi-default export handling with file-based naming (October 18, 2025)
|
|
568
|
+
- **[v2.3](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.3.md)** - EventEmitter & Class Context Propagation (October 16, 2025)
|
|
569
|
+
- **[v2.2](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.2.md)** - Case preservation options (preserveAllUpper/preserveAllLower) (October 14, 2025)
|
|
570
|
+
- **[v2.1](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.1.md)** - Advanced sanitization with boundary patterns (October 12, 2025)
|
|
571
|
+
- **[v2.0](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.0.md)** - Complete Architectural Rewrite (September 9, 2025)
|
|
1751
572
|
|
|
1752
573
|
---
|
|
1753
574
|
|
|
@@ -1767,27 +588,6 @@ try {
|
|
|
1767
588
|
}
|
|
1768
589
|
```
|
|
1769
590
|
|
|
1770
|
-
**Planned Enhanced Error Features (v3.0.0):**
|
|
1771
|
-
|
|
1772
|
-
> [!TIP]
|
|
1773
|
-
> **Coming Soon**: Enhanced error handling with descriptive messages and module suggestions:
|
|
1774
|
-
>
|
|
1775
|
-
> ```javascript
|
|
1776
|
-
> try {
|
|
1777
|
-
> await api.nonexistent.method();
|
|
1778
|
-
> } catch (error) {
|
|
1779
|
-
> console.error(error.message);
|
|
1780
|
-
> // Planned: "Module 'nonexistent' not found in './api'. Available modules: math, string, util. Did you mean 'util'?"
|
|
1781
|
-
> }
|
|
1782
|
-
> ```
|
|
1783
|
-
>
|
|
1784
|
-
> **Planned Features:**
|
|
1785
|
-
>
|
|
1786
|
-
> - 🔍 **Module discovery**: Show available modules and suggest alternatives
|
|
1787
|
-
> - 📍 **Context information**: Include directory path and configuration details
|
|
1788
|
-
> - 🎯 **Actionable suggestions**: Provide specific guidance for resolution
|
|
1789
|
-
> - 🚀 **Development mode**: Additional debugging information when debug flag is enabled
|
|
1790
|
-
|
|
1791
591
|
---
|
|
1792
592
|
|
|
1793
593
|
## 🔧 Production vs Development Modes
|
|
@@ -1815,8 +615,6 @@ try {
|
|
|
1815
615
|
> - **Fork Mode**: Process isolation (in development)
|
|
1816
616
|
> - **Child Mode**: Child process execution (in development)
|
|
1817
617
|
> - **VM Mode**: Virtual machine context (in development)
|
|
1818
|
-
>
|
|
1819
|
-
> The experimental modes are located in `src/lib/engine/` and should not be used in production environments.
|
|
1820
618
|
|
|
1821
619
|
---
|
|
1822
620
|
|
|
@@ -1825,47 +623,26 @@ try {
|
|
|
1825
623
|
### Key Changes
|
|
1826
624
|
|
|
1827
625
|
1. **Import paths**: `@cldmv/slothlet` instead of specific file paths
|
|
1828
|
-
2. **Configuration**: New options (`api_mode`, `context`, `reference`)
|
|
626
|
+
2. **Configuration**: New options (`api_mode`, `context`, `reference`, `hooks`)
|
|
1829
627
|
3. **Function names**: Enhanced preservation of original capitalization
|
|
1830
628
|
4. **Module structure**: Mixed ESM/CJS support
|
|
1831
629
|
5. **Live bindings**: Dual runtime system with AsyncLocalStorage and live-bindings options
|
|
630
|
+
6. **Automatic instances**: No more query strings or `withInstanceId()` methods
|
|
1832
631
|
|
|
1833
632
|
### Migration Steps
|
|
1834
633
|
|
|
1835
634
|
```javascript
|
|
1836
|
-
// v1.3.x - API creation (same pattern as v2.x)
|
|
1837
|
-
import slothlet from "@cldmv/slothlet";
|
|
1838
|
-
const api = await slothlet({
|
|
1839
|
-
dir: "./api",
|
|
1840
|
-
lazy: true
|
|
1841
|
-
});
|
|
1842
|
-
|
|
1843
635
|
// v1.3.x - Multiple instances required query strings or withInstanceId()
|
|
1844
636
|
const api1 = await slothlet({ dir: "./api?instanceId=alice" });
|
|
1845
637
|
const api2 = slothlet.withInstanceId("bob");
|
|
1846
638
|
const bobApi = await api2({ dir: "./api" });
|
|
1847
639
|
|
|
1848
|
-
// v2.
|
|
1849
|
-
import slothlet from "@cldmv/slothlet";
|
|
1850
|
-
const api = await slothlet({
|
|
1851
|
-
dir: "./api",
|
|
1852
|
-
lazy: true,
|
|
1853
|
-
context: { user: "alice" }, // New: context injection
|
|
1854
|
-
api_mode: "auto" // New: API mode control
|
|
1855
|
-
});
|
|
1856
|
-
|
|
1857
|
-
// v2.0 - Multiple instances automatically isolated (no query strings needed)
|
|
640
|
+
// v2.x - Automatic instance isolation (no query strings needed)
|
|
1858
641
|
const api1 = await slothlet({ dir: "./api", context: { tenant: "alice" } });
|
|
1859
642
|
const api2 = await slothlet({ dir: "./api", context: { tenant: "bob" } });
|
|
643
|
+
// Instances completely isolated with their own contexts
|
|
1860
644
|
```
|
|
1861
645
|
|
|
1862
|
-
### Performance Improvements
|
|
1863
|
-
|
|
1864
|
-
- **Architectural optimizations** with copy-left materialization and AsyncLocalStorage integration
|
|
1865
|
-
- **Zero dependencies** - pure Node.js implementation reduces overhead
|
|
1866
|
-
- **Enhanced materialization** with copy-left optimization in lazy mode
|
|
1867
|
-
- **Modular design** improves maintainability and potential optimization opportunities
|
|
1868
|
-
|
|
1869
646
|
---
|
|
1870
647
|
|
|
1871
648
|
## 🤝 Contributing
|
|
@@ -1878,46 +655,12 @@ We welcome contributions! The experimental modes in particular need development
|
|
|
1878
655
|
4. **Provide feedback** on API design and performance
|
|
1879
656
|
5. **Documentation improvements** are always appreciated
|
|
1880
657
|
|
|
1881
|
-
See [CONTRIBUTING.md](
|
|
658
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed contribution guidelines.
|
|
1882
659
|
|
|
1883
660
|
[![Contributors]][contributors_url] [![Sponsor shinrai]][sponsor_url]
|
|
1884
661
|
|
|
1885
662
|
---
|
|
1886
663
|
|
|
1887
|
-
## 📊 Performance Analysis
|
|
1888
|
-
|
|
1889
|
-
For comprehensive performance benchmarks, analysis, and recommendations:
|
|
1890
|
-
|
|
1891
|
-
**📈 [See PERFORMANCE.md](https://github.com/CLDMV/slothlet/blob/HEAD/PERFORMANCE.md)**
|
|
1892
|
-
|
|
1893
|
-
Key highlights:
|
|
1894
|
-
|
|
1895
|
-
- Detailed startup vs runtime performance comparison
|
|
1896
|
-
- Memory usage analysis by loading mode
|
|
1897
|
-
- Materialization cost breakdown by module type
|
|
1898
|
-
- Real-world performance recommendations
|
|
1899
|
-
|
|
1900
|
-
[![CodeFactor]][codefactor_url] [![npms.io score]][npms_url]
|
|
1901
|
-
|
|
1902
|
-
[![npm unpacked size]][npm_size_url] [![Repo size]][repo_size_url]
|
|
1903
|
-
|
|
1904
|
-
---
|
|
1905
|
-
|
|
1906
|
-
## 📚 Documentation
|
|
1907
|
-
|
|
1908
|
-
- **[API Documentation](https://github.com/CLDMV/slothlet/blob/HEAD/docs/API.md)** - Complete API reference with examples
|
|
1909
|
-
- **[Performance Analysis](https://github.com/CLDMV/slothlet/blob/HEAD/PERFORMANCE.md)** - Detailed benchmarks and recommendations
|
|
1910
|
-
- **[Contributing Guide](https://github.com/CLDMV/slothlet/blob/HEAD/CONTRIBUTING.md)** - How to contribute to the project
|
|
1911
|
-
- **[Security Policy](https://github.com/CLDMV/slothlet/blob/HEAD/SECURITY.md)** - Security guidelines and reporting
|
|
1912
|
-
- **[Test Documentation](https://github.com/CLDMV/slothlet/blob/HEAD/api_tests)** - Comprehensive test module examples
|
|
1913
|
-
|
|
1914
|
-
### 🔧 Technical Documentation
|
|
1915
|
-
|
|
1916
|
-
- **[API Rules](https://github.com/CLDMV/slothlet/blob/HEAD/API-RULES.md)** - Systematically verified API transformation rules with real examples and test cases
|
|
1917
|
-
- **[API Rules Conditions](https://github.com/CLDMV/slothlet/blob/HEAD/API-RULES-CONDITIONS.md)** - Complete technical reference of all 26 conditional statements that control API generation
|
|
1918
|
-
|
|
1919
|
-
---
|
|
1920
|
-
|
|
1921
664
|
## 🔗 Links
|
|
1922
665
|
|
|
1923
666
|
- **npm**: [@cldmv/slothlet](https://www.npmjs.com/package/@cldmv/slothlet)
|