@cldmv/slothlet 3.6.0 โ 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +148 -731
- package/dist/lib/builders/api_builder.mjs +1 -1
- package/dist/lib/handlers/permission-manager.mjs +1 -1
- package/dist/lib/handlers/unified-wrapper.mjs +1 -1
- package/dist/lib/helpers/config.mjs +1 -1
- package/package.json +1 -1
- package/types/dist/lib/builders/api_builder.d.mts.map +1 -1
- package/types/dist/lib/handlers/permission-manager.d.mts +2 -0
- package/types/dist/lib/handlers/permission-manager.d.mts.map +1 -1
- package/types/dist/lib/handlers/unified-wrapper.d.mts.map +1 -1
- package/types/dist/lib/helpers/config.d.mts +2 -0
- package/types/dist/lib/helpers/config.d.mts.map +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @cldmv/slothlet
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
|
-
|
|
4
|
+
<img src="https://github.com/CLDMV/slothlet/raw/HEAD/images/slothlet-logo-v1-horizontal-transparent.png" alt="Slothlet Logo" width="600">
|
|
5
5
|
</div>
|
|
6
6
|
|
|
7
7
|
**@cldmv/slothlet** is a sophisticated module loading framework that revolutionizes how you work with massive APIs in Node.js. Built for developers who demand smart, efficient module loading without compromising performance or developer experience.
|
|
@@ -37,38 +37,24 @@ v3 rebuilds Slothlet from the inside out with a **Unified Wrapper architecture**
|
|
|
37
37
|
|
|
38
38
|
Every feature has been hardened with a comprehensive test suite - over **5,300 tests** across eager, lazy, CJS, ESM, TypeScript, and mixed module scenarios.
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
- ๐๏ธ **Unified Wrapper** - single consistent proxy layer for all modes; `console.log(api.math)` now shows real contents
|
|
43
|
-
- ๐ฃ **Redesigned Hook System** - new `hook:` config key, `api.slothlet.hook.*` access path, three-phase subset ordering (`before โ primary โ after`)
|
|
44
|
-
- ๐ **Full i18n** - all error and debug messages are translated and available in 9 languages: English, Spanish, French, German, Portuguese, Italian, Japanese, Chinese (Simplified), and Korean
|
|
45
|
-
- ๐ค **Background Materialization** - `backgroundMaterialize: true` pre-loads lazy modules without blocking; `api.slothlet.materialize.wait()` to await completion
|
|
46
|
-
- โก **Lifecycle Events** - subscribe to `impl:created`, `impl:changed`, `impl:removed`, and `materialized:complete` via `api.slothlet.lifecycle.on/off()`
|
|
47
|
-
- ๐ **Collision Modes** - replace `allowApiOverwrite` with typed modes: `merge`, `skip`, `overwrite`, `throw` - independently configurable for initial load vs runtime `add()`
|
|
48
|
-
- ๐ **Mutation Controls** - granular per-operation enable/disable for `add`, `remove`, and `reload`
|
|
49
|
-
- ๐งน **Sanitization Improvements** - runtime `api.slothlet.sanitize()` method; helper export renamed to `sanitizePropertyName`
|
|
50
|
-
- ๐ **Improved Context Isolation** - `.run()` and `.scope()` use a child-instance model; full isolation between Slothlet instances
|
|
51
|
-
|
|
52
|
-
๐ **[See the full v3.0 changelog](./docs/changelog/v3.0.md)**
|
|
40
|
+
๐ **[See the full v3.0 changelog](./docs/changelog/v3.0.md)** for the architecture rewrite, hook system redesign, i18n layer, background materialization, lifecycle events, collision modes, mutation controls, sanitization improvements, and context isolation upgrades.
|
|
53
41
|
|
|
54
42
|
---
|
|
55
43
|
|
|
56
44
|
## โจ What's New
|
|
57
45
|
|
|
58
|
-
### Latest: v3.
|
|
46
|
+
### Latest: v3.7.0 (May 2026)
|
|
59
47
|
|
|
60
|
-
-
|
|
61
|
-
-
|
|
62
|
-
-
|
|
63
|
-
- [View full v3.6.0 Changelog](./docs/changelog/v3/v3.6.0.md)
|
|
48
|
+
- **Read-level permission gating** โ the permission system now checks property _reads_ of data values, not just function _calls_. Until now a module exporting a `Buffer`, `TypedArray`, `Date`, or primitive left that value readable by any other module via `self.something.value` regardless of deny rules โ the check fired at invocation, and a data value has no invocation step. Reading a terminal data value off a module API path is now enforced against the rule set exactly like a call, targeting its leaf path. This is **on by default** when `permissions` is configured (set `readGating: false` to opt out) โ a `defaultPolicy: "deny"` config will now deny cross-module data reads unless an allow rule covers the path.
|
|
49
|
+
- **Runtime-toggleable** โ read gating can be flipped after instance creation via `api.slothlet.permissions.control.readGating(true|false)`. Namespace traversal stays ungated (no per-segment allow rules needed), callable functions remain call-gated, external user-code reads are exempt, and the self-call bypass still applies.
|
|
50
|
+
- [View full v3.7.0 Changelog](./docs/changelog/v3/v3.7.0.md)
|
|
64
51
|
|
|
65
52
|
### Recent Releases
|
|
66
53
|
|
|
54
|
+
- **v3.6.0** (May 2026) โ `self.slothlet.lockCaller()` / `bind()` pin caller identity onto callbacks; hooks and `run`/`scope` callbacks keep caller identity ([Changelog](./docs/changelog/v3/v3.6.0.md))
|
|
67
55
|
- **v3.5.1** (May 2026) โ Binary buffers (`Buffer` / `TypedArray` / `DataView`) cross `self` unwrapped; relative imports work from `.ts` / `.mts` modules ([Changelog](./docs/changelog/v3/v3.5.1.md))
|
|
68
56
|
- **v3.5.0** (May 2026) โ TypeScript runtime imports (`self` / `context` / `instanceID`) work from `.ts` / `.mts`; `slothlet typegen` CLI + programmatic API; runtime `self.X = โฆ` assignment now persists ([Changelog](./docs/changelog/v3/v3.5.0.md))
|
|
69
57
|
- **v3.4.1** (May 2026) โ Permission gating for all `api.slothlet.*` routes; metadata hardening against prototype-pollution and circular payloads ([Changelog](./docs/changelog/v3/v3.4.1.md))
|
|
70
|
-
- **v3.4.0** (May 2026) โ Context-conditional permission rules: optional `condition` field (plain object, function, or array) on rules evaluated against per-request ALS context ([Changelog](./docs/changelog/v3/v3.4.0.md))
|
|
71
|
-
|
|
72
58
|
|
|
73
59
|
๐ **For complete version history and detailed release notes, see [docs/changelog/](./docs/changelog/) folder.**
|
|
74
60
|
|
|
@@ -95,12 +81,7 @@ Every feature has been hardened with a comprehensive test suite - over **5,300 t
|
|
|
95
81
|
- **Zero dependencies**: Pure Node.js implementation
|
|
96
82
|
- **Memory efficiency**: Lazy mode loads modules on-demand, eager mode optimizes for predictable behavior
|
|
97
83
|
|
|
98
|
-
**
|
|
99
|
-
|
|
100
|
-
- **Eager Mode**: Best for production environments with maximum runtime performance and predictable behavior
|
|
101
|
-
- **Lazy Mode**: Best for development and applications with large APIs where startup time matters
|
|
102
|
-
|
|
103
|
-
๐ **For comprehensive performance benchmarks and analysis, see [docs/PERFORMANCE.md](https://github.com/CLDMV/slothlet/blob/master/docs/PERFORMANCE.md)**
|
|
84
|
+
๐ **For comprehensive performance benchmarks and analysis, see [docs/PERFORMANCE.md](./docs/PERFORMANCE.md)**
|
|
104
85
|
|
|
105
86
|
### ๐ฃ **Hook System** _(redesigned in v3)_
|
|
106
87
|
|
|
@@ -113,28 +94,27 @@ Powerful function interceptor system with 4 hook types and three-phase subset or
|
|
|
113
94
|
|
|
114
95
|
Each hook type supports three ordered execution **subsets**: `"before"` โ `"primary"` (default) โ `"after"`. Pattern matching, priority control, runtime enable/disable, and short-circuit support included.
|
|
115
96
|
|
|
116
|
-
๐ฃ **For complete hook system documentation, see [docs/HOOKS.md](
|
|
97
|
+
๐ฃ **For complete hook system documentation, see [docs/HOOKS.md](./docs/HOOKS.md)**
|
|
117
98
|
|
|
118
|
-
### ๐ **Permission System** _(new in v3.3)_
|
|
99
|
+
### ๐ **Permission System** _(new in v3.3, read-gated in v3.7)_
|
|
119
100
|
|
|
120
|
-
Path-based access control for inter-module API calls:
|
|
101
|
+
Path-based access control for inter-module API calls **and** data-value reads (v3.7+):
|
|
121
102
|
|
|
122
103
|
- **Glob pattern rules** โ same `*`, `**`, `?`, `{a,b}` syntax as hooks
|
|
123
104
|
- **Most-specific-wins** โ exact patterns override broad globs; tiebreak by registration order
|
|
124
105
|
- **Self-call bypass** โ calls within the same source file always succeed
|
|
125
|
-
- **
|
|
106
|
+
- **Read-level gating** _(v3.7)_ โ terminal data-value reads (`Buffer`, `TypedArray`, `Date`, `Map`, primitives, โฆ) checked against the rule set; on by default, `readGating: false` to opt out
|
|
126
107
|
- **Audit events** โ `permission:denied`, `permission:allowed`, `permission:default`, `permission:self-bypass`
|
|
127
108
|
- **Runtime management** โ `api.slothlet.permissions.addRule()`, `.removeRule()`, `.self.*`, `.global.*`, `.control.*`
|
|
128
|
-
- **Context conditions** _(
|
|
109
|
+
- **Context conditions** _(v3.4)_ โ optional `condition` field; accepts a plain object (deep leaf matching), function, or array (OR); evaluated against per-request ALS context
|
|
129
110
|
|
|
130
|
-
๐ **For complete permission system documentation, see [docs/PERMISSIONS.md](
|
|
111
|
+
๐ **For complete permission system documentation, see [docs/PERMISSIONS.md](./docs/PERMISSIONS.md)** ยท ๐ **For condition syntax, see [docs/PERMISSIONS-CONDITIONS.md](./docs/PERMISSIONS-CONDITIONS.md)**
|
|
131
112
|
|
|
132
113
|
### ๐ **Full Internationalization** _(new in v3)_
|
|
133
114
|
|
|
134
|
-
All error messages and debug output are translated. Supported languages:
|
|
135
|
-
English ยท Spanish ยท French ยท German ยท Portuguese ยท Italian ยท Japanese ยท Chinese (Simplified) ยท Korean
|
|
115
|
+
All error messages and debug output are translated. Supported languages: English ยท Spanish ยท French ยท German ยท Portuguese ยท Italian ยท Japanese ยท Chinese (Simplified) ยท Korean
|
|
136
116
|
|
|
137
|
-
Configure via `i18n: { language: "es" }` in your slothlet config.
|
|
117
|
+
Configure via `i18n: { language: "es" }` in your slothlet config. See **[docs/I18N.md](./docs/I18N.md)**.
|
|
138
118
|
|
|
139
119
|
### ๐ **Context Propagation**
|
|
140
120
|
|
|
@@ -145,7 +125,7 @@ Automatic context preservation across all asynchronous boundaries:
|
|
|
145
125
|
- **Class instance propagation**: Context preserved in class method calls
|
|
146
126
|
- **Zero configuration**: Works automatically with TCP servers, HTTP servers, and custom EventEmitters
|
|
147
127
|
|
|
148
|
-
๐ **For context propagation details, see [docs/CONTEXT-PROPAGATION.md](
|
|
128
|
+
๐ **For context propagation details, see [docs/CONTEXT-PROPAGATION.md](./docs/CONTEXT-PROPAGATION.md)**
|
|
149
129
|
|
|
150
130
|
### ๐ง **Smart API Management**
|
|
151
131
|
|
|
@@ -154,8 +134,7 @@ Automatic context preservation across all asynchronous boundaries:
|
|
|
154
134
|
- **Advanced Sanitization**: Custom naming rules with glob and boundary patterns; `api.slothlet.sanitize()` at runtime
|
|
155
135
|
- **Hybrid Exports**: Support for callable APIs with methods, default + named exports
|
|
156
136
|
|
|
157
|
-
๐๏ธ **
|
|
158
|
-
๐ **For API flattening rules, see [docs/API-RULES/API-FLATTENING.md](https://github.com/CLDMV/slothlet/blob/master/docs/API-RULES/API-FLATTENING.md)**
|
|
137
|
+
๐๏ธ **[Module structure](./docs/MODULE-STRUCTURE.md)** ยท ๐ **[API flattening](./docs/API-RULES/API-FLATTENING.md)** ยท ๐ก **[Sanitization](./docs/SANITIZATION.md)**
|
|
159
138
|
|
|
160
139
|
### ๐ **Runtime & Context System**
|
|
161
140
|
|
|
@@ -166,7 +145,7 @@ Automatic context preservation across all asynchronous boundaries:
|
|
|
166
145
|
|
|
167
146
|
### ๐ **Developer Experience**
|
|
168
147
|
|
|
169
|
-
- **TypeScript-Friendly**: Comprehensive JSDoc annotations with auto-generated declarations
|
|
148
|
+
- **TypeScript-Friendly**: Comprehensive JSDoc annotations with auto-generated declarations โ see **[docs/TYPESCRIPT.md](./docs/TYPESCRIPT.md)**
|
|
170
149
|
- **Configurable Debug**: Detailed logging via CLI flags or environment variables
|
|
171
150
|
- **Multiple Instances**: Parameter-based isolation for complex applications
|
|
172
151
|
- **Inspectable APIs**: `console.log(api.math)` and logical versioned paths like `console.log(api.auth)` show real module contents instead of proxy internals (v3+)
|
|
@@ -190,769 +169,179 @@ npm install @cldmv/slothlet
|
|
|
190
169
|
|
|
191
170
|
## ๐ Quick Start
|
|
192
171
|
|
|
193
|
-
### ESM (ES Modules)
|
|
194
|
-
|
|
195
172
|
```javascript
|
|
196
173
|
import slothlet from "@cldmv/slothlet";
|
|
197
174
|
|
|
198
|
-
//
|
|
175
|
+
// Eager mode (default) โ functions behave as originally defined
|
|
199
176
|
const api = await slothlet({
|
|
200
177
|
dir: "./api",
|
|
201
178
|
context: { user: "alice" }
|
|
202
179
|
});
|
|
203
180
|
|
|
204
|
-
|
|
205
|
-
const
|
|
206
|
-
const asyncResult = await api.async.processData({ data: "async" });
|
|
207
|
-
|
|
208
|
-
// Access both ESM and CJS modules seamlessly
|
|
209
|
-
const esmResult = api.mathEsm.multiply(4, 5);
|
|
210
|
-
const cjsResult = api.mathCjs.divide(10, 2);
|
|
181
|
+
const result = api.math.add(2, 3); // Sync stays sync
|
|
182
|
+
const asyncResult = await api.async.process(); // Async stays async
|
|
211
183
|
```
|
|
212
184
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
```javascript
|
|
216
|
-
const slothlet = require("@cldmv/slothlet");
|
|
217
|
-
|
|
218
|
-
const api = await slothlet({
|
|
219
|
-
dir: "./api",
|
|
220
|
-
context: { env: "production" }
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
const result = api.math.multiply(4, 5);
|
|
224
|
-
const mixedResult = await api.interop.processData({ data: "test" });
|
|
225
|
-
```
|
|
185
|
+
CommonJS works the same way: `const slothlet = require("@cldmv/slothlet")`.
|
|
226
186
|
|
|
227
|
-
|
|
187
|
+
**Lazy mode** with copy-left materialization โ all calls awaited, ~2.2ร faster startup:
|
|
228
188
|
|
|
229
189
|
```javascript
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
// Lazy mode with copy-left materialization
|
|
233
|
-
const api = await slothlet({
|
|
234
|
-
mode: "lazy",
|
|
235
|
-
dir: "./api",
|
|
236
|
-
apiDepth: 3
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
// First access: materialization overhead (~1.45ms average)
|
|
240
|
-
const result1 = await api.math.add(2, 3);
|
|
241
|
-
|
|
242
|
-
// Subsequent access: materialized function (near-eager performance)
|
|
243
|
-
const result2 = await api.math.add(5, 7);
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
### Hook System Example _(v3 API)_
|
|
247
|
-
|
|
248
|
-
```javascript
|
|
249
|
-
import slothlet from "@cldmv/slothlet";
|
|
250
|
-
|
|
251
|
-
const api = await slothlet({
|
|
252
|
-
dir: "./api",
|
|
253
|
-
hook: true // Enable hooks - note: "hook" singular (v3)
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
// Before hook: Modify arguments
|
|
257
|
-
api.slothlet.hook.on(
|
|
258
|
-
"before:math.add",
|
|
259
|
-
({ path, args }) => {
|
|
260
|
-
console.log(`Calling ${path} with args:`, args);
|
|
261
|
-
return [args[0] * 2, args[1] * 2]; // Return array to replace arguments
|
|
262
|
-
},
|
|
263
|
-
{ id: "double-args", priority: 100 }
|
|
264
|
-
);
|
|
265
|
-
|
|
266
|
-
// After hook: Transform result
|
|
267
|
-
api.slothlet.hook.on(
|
|
268
|
-
"after:math.*",
|
|
269
|
-
({ path, result }) => {
|
|
270
|
-
console.log(`${path} returned:`, result);
|
|
271
|
-
return result * 10;
|
|
272
|
-
},
|
|
273
|
-
{ id: "scale-result" }
|
|
274
|
-
);
|
|
275
|
-
|
|
276
|
-
// Always hook: Observe final result (read-only)
|
|
277
|
-
api.slothlet.hook.on(
|
|
278
|
-
"always:**",
|
|
279
|
-
({ path, result, hasError }) => {
|
|
280
|
-
console.log(hasError ? `${path} failed` : `${path} succeeded`);
|
|
281
|
-
},
|
|
282
|
-
{ id: "logger" }
|
|
283
|
-
);
|
|
284
|
-
|
|
285
|
-
// Error hook: Monitor errors with source tracking
|
|
286
|
-
api.slothlet.hook.on(
|
|
287
|
-
"error:**",
|
|
288
|
-
({ path, error, source }) => {
|
|
289
|
-
console.error(`Error in ${path}:`, error.message);
|
|
290
|
-
console.error(`Source: ${source.type}`); // 'before' | 'after' | 'function' | 'always'
|
|
291
|
-
},
|
|
292
|
-
{ id: "error-monitor" }
|
|
293
|
-
);
|
|
294
|
-
|
|
295
|
-
// Call function - hooks execute automatically
|
|
296
|
-
const result = await api.math.add(2, 3);
|
|
190
|
+
const api = await slothlet({ mode: "lazy", dir: "./api" });
|
|
191
|
+
const result = await api.math.add(2, 3); // ALL calls awaited in lazy mode
|
|
297
192
|
```
|
|
298
193
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
```javascript
|
|
302
|
-
import slothlet from "@cldmv/slothlet";
|
|
303
|
-
|
|
304
|
-
const api = await slothlet({ dir: "./api" });
|
|
305
|
-
|
|
306
|
-
// Add modules at runtime
|
|
307
|
-
await api.slothlet.api.add("plugins", "./plugins-folder");
|
|
308
|
-
api.plugins.myPlugin();
|
|
309
|
-
|
|
310
|
-
// Create nested API structures
|
|
311
|
-
await api.slothlet.api.add("runtime.plugins", "./more-plugins");
|
|
312
|
-
api.runtime.plugins.loader();
|
|
313
|
-
|
|
314
|
-
// Add with metadata for security/authorization
|
|
315
|
-
await api.slothlet.api.add("plugins.trusted", "./trusted-plugins", {
|
|
316
|
-
trusted: true,
|
|
317
|
-
permissions: ["read", "write", "admin"]
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
// Remove and reload
|
|
321
|
-
await api.slothlet.api.remove("oldModule");
|
|
322
|
-
await api.slothlet.api.reload("database.*");
|
|
323
|
-
```
|
|
194
|
+
**Hooks**, **dynamic API extension** (`api.slothlet.api.add/remove/reload`), **per-request context** (`api.slothlet.context.run/scope`), and **lifecycle events** are all covered in the linked technical guides below.
|
|
324
195
|
|
|
325
196
|
---
|
|
326
197
|
|
|
327
|
-
## ๐ Configuration
|
|
328
|
-
|
|
329
|
-
| Option | Type | Default | Description |
|
|
330
|
-
| ------------------------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
331
|
-
| `dir` | `string` | `"api"` | Directory to load API modules from (absolute or relative path) |
|
|
332
|
-
| `mode` | `string` | `"eager"` | Loading mode - `"lazy"` for on-demand loading, `"eager"` for immediate loading |
|
|
333
|
-
| `runtime` | `string` | `"async"` | Runtime binding system: `"async"` for AsyncLocalStorage (default), `"live"` for live-bindings |
|
|
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
|
-
| `context` | `object` | `{}` | Context data injected into live-binding (available via `import { context } from "@cldmv/slothlet/runtime"`) |
|
|
337
|
-
| `reference` | `object` | `{}` | Reference object merged into API root level |
|
|
338
|
-
| `sanitize` | `object` | `{}` | Advanced filename-to-API transformation control with `lowerFirst`, `preserveAllUpper`, `preserveAllLower`, and `rules` (supports exact matches, glob patterns `*json*`, and boundary patterns `**url**`) |
|
|
339
|
-
| `hook` | `mixed` | `false` | Enable hook system: `true` (enable all), `"pattern"` (enable with pattern), or object with `enabled`, `pattern`, `suppressErrors` options - **note: `hook` singular, not `hooks`** |
|
|
340
|
-
| `backgroundMaterialize` | `boolean` | `false` | In lazy mode: start background pre-loading of all modules immediately after init; automatically enables materialization tracking and the `materialized:complete` lifecycle event |
|
|
341
|
-
| `api.collision` | `mixed` | `"merge"` | Collision mode for API namespace conflicts: `"merge"`, `"skip"`, `"overwrite"`, `"throw"` - or `{ initial: "merge", api: "skip" }` to set independently for load vs runtime `add()` |
|
|
342
|
-
| `api.mutations` | `object` | all `true` | Per-operation mutation controls: `{ add: true, remove: true, reload: true, permissions: true }` - set any to `false` to disable |
|
|
343
|
-
| `versionDispatcher` | `mixed` | `undefined` | Version routing discriminator: `"version"` (or any string key) looks up that key in the caller's version metadata; a function receives `(allVersions, caller)` and returns a tag or `null`; `undefined` behaves like `"version"` |
|
|
344
|
-
| `permissions` | `object` | `undefined` | Permission system config: `{ defaultPolicy: "allow"\|"deny", enabled: true, audit: "default"\|"verbose", rules: [...] }` โ rules support optional `condition` field (plain object with deep leaf matching, function, or array of either for OR semantics) for per-request context matching โ see [PERMISSIONS.md](./docs/PERMISSIONS.md) |
|
|
345
|
-
| `i18n` | `object` | `{}` | Internationalization settings: `{ language: "en" }` - supported: `en`, `es`, `fr`, `de`, `pt`, `it`, `ja`, `zh`, `ko` |
|
|
198
|
+
## ๐ Configuration
|
|
346
199
|
|
|
347
|
-
|
|
200
|
+
The most-used options are summarized below. The complete reference โ every option, every diagnostic, every deprecated alias โ lives in **[docs/CONFIGURATION.md](./docs/CONFIGURATION.md)**.
|
|
348
201
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
subgraph EAGER ["โก Eager Mode"]
|
|
360
|
-
direction TB
|
|
361
|
-
EAGER0 ~~~ EAGER1
|
|
362
|
-
EAGER2 ~~~ EAGER3
|
|
363
|
-
|
|
364
|
-
EAGER0@{ shape: braces, label: "๐ฅ All modules loaded immediately" }
|
|
365
|
-
EAGER1@{ shape: braces, label: "โ
API methods available right away" }
|
|
366
|
-
EAGER2@{ shape: braces, label: "๐ Function calls behave as originally defined" }
|
|
367
|
-
EAGER3@{ shape: braces, label: "๐ Sync stays sync: api.math.add(2,3)<br/>๐ Async stays async: await api.async.process()" }
|
|
368
|
-
end
|
|
369
|
-
|
|
370
|
-
subgraph LAZY ["๐ค Lazy Mode"]
|
|
371
|
-
direction TB
|
|
372
|
-
LAZY0 ~~~ LAZY1
|
|
373
|
-
LAZY2 ~~~ LAZY3
|
|
374
|
-
LAZY4 ~~~ LAZY5
|
|
375
|
-
|
|
376
|
-
LAZY0@{ shape: braces, label: "๐ฆ Modules not loaded yet" }
|
|
377
|
-
LAZY1@{ shape: braces, label: "๐ญ API methods are placeholders/proxies" }
|
|
378
|
-
LAZY2@{ shape: braces, label: "๐ First call triggers materialization" }
|
|
379
|
-
LAZY3@{ shape: braces, label: "โณ All calls must be awaited<br/>await api.math.add(2,3)" }
|
|
380
|
-
LAZY4@{ shape: braces, label: "๐พ Module stays loaded after materialization<br/>Copy-left materialization" }
|
|
381
|
-
LAZY5@{ shape: braces, label: "๐ Subsequent calls nearly as fast as eager mode" }
|
|
382
|
-
end
|
|
383
|
-
|
|
384
|
-
subgraph EAGERCALL ["โก Eager Mode Calls"]
|
|
385
|
-
direction TB
|
|
386
|
-
end
|
|
387
|
-
|
|
388
|
-
subgraph LAZYCALL ["๐ค Lazy Mode Calls"]
|
|
389
|
-
direction TB
|
|
390
|
-
LAZYCALL0 --> LAZYCALL2
|
|
391
|
-
|
|
392
|
-
LAZYCALL0@{ shape: rounded, label: "๐ First call" }
|
|
393
|
-
LAZYCALL1@{ shape: rounded, label: "๐ Sequential calls" }
|
|
394
|
-
LAZYCALL2@{ shape: rounded, label: "๐งฉ Materialize" }
|
|
395
|
-
end
|
|
396
|
-
|
|
397
|
-
EAGER --> READYTOUSE
|
|
398
|
-
LAZY --> READYTOUSE
|
|
399
|
-
|
|
400
|
-
READYTOUSE --> CALL
|
|
401
|
-
CALL -.-> EAGERCALL
|
|
402
|
-
CALL -.-> LAZYCALL
|
|
403
|
-
|
|
404
|
-
EAGERCALL --> MATERIALIZEDFUNCTION
|
|
405
|
-
LAZYCALL1 --> MATERIALIZEDFUNCTION
|
|
406
|
-
LAZYCALL2 --> MATERIALIZEDFUNCTION
|
|
407
|
-
|
|
408
|
-
READYTOUSE@{ shape: rounded, label: "๐ฏ Ready to Use" }
|
|
409
|
-
MATERIALIZEDFUNCTION@{ shape: rounded, label: "โ
Materialized method/property" }
|
|
410
|
-
CALL@{ shape: trap-b, label: "๐ Call" }
|
|
411
|
-
|
|
412
|
-
subgraph ALWAYS ["โจ Extras Always On"]
|
|
413
|
-
direction TB
|
|
414
|
-
ALWAYS0 ~~~ ALWAYS1
|
|
415
|
-
ALWAYS1 ~~~ ALWAYS2
|
|
416
|
-
|
|
417
|
-
ALWAYS0@{ shape: rounded, label: "๐ Live Bindings ALS<br/>Per-instance context isolation" }
|
|
418
|
-
ALWAYS1@{ shape: rounded, label: "๐ท๏ธ Smart Naming & Flattening<br/>Multiple rules for clean APIs" }
|
|
419
|
-
ALWAYS2@{ shape: rounded, label: "๐ Mixed Module Support<br/>Seamlessly mix .mjs and .cjs" }
|
|
420
|
-
end
|
|
421
|
-
|
|
422
|
-
MODULEFOLDERS@{ shape: st-rect, label: "๐ Modules Folder<br/>.mjs and/or .cjs files<br/>math.mjs, string.cjs, async.mjs" }
|
|
423
|
-
SLOTHLET@{ shape: rounded, label: "๐ง Call slothlet(options)" }
|
|
424
|
-
CHOOSEMODE@{ shape: diamond, label: "Choose Mode<br/>in options" }
|
|
425
|
-
|
|
426
|
-
style EAGER0 stroke:#9BC66B,color:#9BC66B,opacity:0.5
|
|
427
|
-
style EAGER1 stroke:#9BC66B,color:#9BC66B,opacity:0.5
|
|
428
|
-
style EAGER2 stroke:#9BC66B,color:#9BC66B,opacity:0.5
|
|
429
|
-
style EAGER3 stroke:#9BC66B,color:#9BC66B,opacity:0.5
|
|
430
|
-
|
|
431
|
-
style LAZY0 stroke:#9BC66B,color:#9BC66B,opacity:0.5
|
|
432
|
-
style LAZY1 stroke:#9BC66B,color:#9BC66B,opacity:0.5
|
|
433
|
-
style LAZY2 stroke:#9BC66B,color:#9BC66B,opacity:0.5
|
|
434
|
-
style LAZY3 stroke:#9BC66B,color:#9BC66B,opacity:0.5
|
|
435
|
-
style LAZY4 stroke:#9BC66B,color:#9BC66B,opacity:0.5
|
|
436
|
-
style LAZY5 stroke:#9BC66B,color:#9BC66B,opacity:0.5
|
|
437
|
-
|
|
438
|
-
style MODULEFOLDERS fill:#1a1a1a,stroke:#9BC66B,stroke-width:2px,color:#9BC66B,opacity:0.5
|
|
439
|
-
style SLOTHLET fill:#1a1a1a,stroke:#9BC66B,stroke-width:2px,color:#9BC66B,opacity:0.5
|
|
440
|
-
style CHOOSEMODE fill:#1a1a1a,stroke:#9BC66B,stroke-width:2px,color:#9BC66B,opacity:0.5
|
|
441
|
-
style READYTOUSE fill:#1a1a1a,stroke:#9BC66B,stroke-width:2px,color:#9BC66B,opacity:0.5
|
|
442
|
-
style CALL fill:#1a1a1a,stroke:#9BC66B,stroke-width:2px,color:#9BC66B,opacity:0.5
|
|
443
|
-
style MATERIALIZEDFUNCTION fill:#1a1a1a,stroke:#9BC66B,stroke-width:2px,color:#9BC66B,opacity:0.5
|
|
444
|
-
|
|
445
|
-
style EAGER fill:#0d1a0d,stroke:#9BC66B,stroke-width:3px,color:#9BC66B,opacity:0.5
|
|
446
|
-
style EAGERCALL fill:#0d1a0d,stroke:#9BC66B,stroke-width:2px,color:#9BC66B,opacity:0.5
|
|
447
|
-
|
|
448
|
-
style LAZY fill:#0d1a0d,stroke:#B8D982,stroke-width:3px,color:#B8D982,opacity:0.5
|
|
449
|
-
style LAZYCALL fill:#0d1a0d,stroke:#B8D982,stroke-width:2px,color:#B8D982,opacity:0.5
|
|
450
|
-
style LAZYCALL0 fill:#1a1a1a,stroke:#B8D982,stroke-width:2px,color:#B8D982,opacity:0.5
|
|
451
|
-
style LAZYCALL1 fill:#1a1a1a,stroke:#B8D982,stroke-width:2px,color:#B8D982,opacity:0.5
|
|
452
|
-
style LAZYCALL2 fill:#1a1a1a,stroke:#B8D982,stroke-width:2px,color:#B8D982,opacity:0.5
|
|
453
|
-
|
|
454
|
-
style ALWAYS fill:#0d1a0d,stroke:#7FA94F,stroke-width:3px,color:#7FA94F,opacity:0.5
|
|
455
|
-
style ALWAYS0 fill:#1a1a1a,stroke:#7FA94F,stroke-width:1px,color:#7FA94F,opacity:0.5
|
|
456
|
-
style ALWAYS1 fill:#1a1a1a,stroke:#7FA94F,stroke-width:1px,color:#7FA94F,opacity:0.5
|
|
457
|
-
style ALWAYS2 fill:#1a1a1a,stroke:#7FA94F,stroke-width:1px,color:#7FA94F,opacity:0.5
|
|
458
|
-
|
|
459
|
-
linkStyle default stroke:#9BC66B,stroke-width:3px,opacity:0.5
|
|
460
|
-
linkStyle 4,5,6,7,8,18,19 stroke-width:0px
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
---
|
|
202
|
+
| Option | Type | Default | Description |
|
|
203
|
+
| ------------- | -------- | ----------- | ---------------------------------------------------------------------------------- |
|
|
204
|
+
| `dir` | `string` | `"api"` | Directory to load API modules from |
|
|
205
|
+
| `mode` | `string` | `"eager"` | `"eager"` (load upfront) or `"lazy"` (on-demand with copy-left materialization) |
|
|
206
|
+
| `runtime` | `string` | `"async"` | `"async"` (AsyncLocalStorage) or `"live"` (live-bindings) |
|
|
207
|
+
| `context` | `object` | `{}` | Per-request context โ read via `import { context } from "@cldmv/slothlet/runtime"` |
|
|
208
|
+
| `hook` | `mixed` | `false` | Enable hooks; see **[HOOKS.md](./docs/HOOKS.md)** |
|
|
209
|
+
| `permissions` | `object` | `undefined` | Path-based access control; see **[PERMISSIONS.md](./docs/PERMISSIONS.md)** |
|
|
210
|
+
| `i18n` | `object` | `{}` | Language for translated error/debug messages โ see **[I18N.md](./docs/I18N.md)** |
|
|
464
211
|
|
|
465
|
-
|
|
212
|
+
Also configurable: `apiDepth`, `debug`, `reference`, `sanitize`, `backgroundMaterialize`, `api.collision`, `api.mutations`, `versionDispatcher`, `typescript`, plus diagnostics and lifecycle internals. All documented in **[CONFIGURATION.md](./docs/CONFIGURATION.md)**.
|
|
466
213
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
**Best for:** Production environments, maximum runtime performance, predictable behavior
|
|
470
|
-
|
|
471
|
-
```javascript
|
|
472
|
-
const api = await slothlet({ dir: "./api" }); // mode: "eager" by default
|
|
473
|
-
|
|
474
|
-
// Functions behave as originally defined
|
|
475
|
-
const result = api.math.add(2, 3); // Sync - no await needed
|
|
476
|
-
const asyncResult = await api.async.processData({ data: "test" }); // Async needs await
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
**Benefits:**
|
|
480
|
-
|
|
481
|
-
- โ
Fast function calls (9.46ฮผs average - within 6% of lazy mode)
|
|
482
|
-
- โ
Predictable performance (no materialization delays)
|
|
483
|
-
- โ
Functions behave exactly as originally defined
|
|
484
|
-
|
|
485
|
-
### Lazy Mode with Copy-Left Materialization (Production Ready)
|
|
486
|
-
|
|
487
|
-
**Best for:** Startup-sensitive applications, memory efficiency, loading only what you use
|
|
488
|
-
|
|
489
|
-
```javascript
|
|
490
|
-
const api = await slothlet({ mode: "lazy", dir: "./api" });
|
|
491
|
-
|
|
492
|
-
// ALL calls must be awaited (materialization process)
|
|
493
|
-
const result1 = await api.math.add(2, 3); // First: ~538ฮผs avg (materialization)
|
|
494
|
-
const result2 = await api.math.add(5, 7); // Subsequent: ~10ฮผs (materialized)
|
|
495
|
-
```
|
|
496
|
-
|
|
497
|
-
**Benefits:**
|
|
498
|
-
|
|
499
|
-
- โ
2.2x faster startup (15.41ms vs 34.28ms)
|
|
500
|
-
- โ
Equal function call performance (9.99ฮผs vs 9.46ฮผs eager - within 6% measurement noise)
|
|
501
|
-
- โ
Memory efficient (loads only what you use)
|
|
502
|
-
- โ
Copy-left optimization (once loaded, stays loaded)
|
|
214
|
+
---
|
|
503
215
|
|
|
504
|
-
|
|
216
|
+
## ๐ Loading Modes
|
|
505
217
|
|
|
506
|
-
|
|
218
|
+
| Mode | Startup | Function calls | Best for |
|
|
219
|
+
| -------------------------------------- | -------------- | --------------------------------------------- | --------------------------------------------------- |
|
|
220
|
+
| **Eager** (default) | Loads upfront | Sync stays sync, async stays async | Production, predictable performance |
|
|
221
|
+
| **Lazy** | 2.2ร faster | All calls awaited; materialized on first use | Large APIs, startup-sensitive apps |
|
|
222
|
+
| **Lazy + `backgroundMaterialize`** | 2.2ร faster | Pre-warmed by background loader | Best of both โ lazy startup, eager runtime |
|
|
507
223
|
|
|
508
224
|
```javascript
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
});
|
|
514
|
-
|
|
515
|
-
// Subscribe to completion
|
|
516
|
-
api.slothlet.lifecycle.on("materialized:complete", (data) => {
|
|
517
|
-
console.log(`${data.total} modules materialized`);
|
|
518
|
-
});
|
|
519
|
-
|
|
520
|
-
// Or await all modules to be ready before serving traffic
|
|
521
|
-
await api.slothlet.materialize.wait();
|
|
522
|
-
|
|
523
|
-
// Check progress at any time
|
|
524
|
-
const stats = api.slothlet.materialize.get();
|
|
525
|
-
// { total, materialized, remaining, percentage }
|
|
225
|
+
// Lazy + background materialization
|
|
226
|
+
const api = await slothlet({ mode: "lazy", dir: "./api", backgroundMaterialize: true });
|
|
227
|
+
api.slothlet.lifecycle.on("materialized:complete", ({ total }) => console.log(`${total} modules ready`));
|
|
228
|
+
await api.slothlet.materialize.wait(); // optional: gate traffic on ready
|
|
526
229
|
```
|
|
527
230
|
|
|
528
|
-
|
|
529
|
-
> **Choose your strategy:**
|
|
530
|
-
>
|
|
531
|
-
> - **Startup-sensitive?** โ Lazy mode (2.2x faster startup)
|
|
532
|
-
> - **Call-intensive?** โ Either mode (function calls essentially equal after materialization)
|
|
533
|
-
> - **Need predictability?** โ Eager mode (no materialization delays)
|
|
534
|
-
> - **Large API, use subset?** โ Lazy mode (memory efficient)
|
|
535
|
-
> - **Want lazy startup + eager runtime?** โ Lazy mode + `backgroundMaterialize: true`
|
|
231
|
+
๐ **Benchmarks & analysis: [docs/PERFORMANCE.md](./docs/PERFORMANCE.md)** ยท ๐ **Visual pipeline diagram: [docs/MODULE-STRUCTURE.md#loading-pipeline-overview](./docs/MODULE-STRUCTURE.md#loading-pipeline-overview)** ยท โก **Lifecycle events: [docs/LIFECYCLE.md](./docs/LIFECYCLE.md)**
|
|
536
232
|
|
|
537
233
|
---
|
|
538
234
|
|
|
539
|
-
## ๐ฃ
|
|
235
|
+
## ๐ฃ Hooks
|
|
540
236
|
|
|
541
|
-
|
|
237
|
+
Four hook types (`before`, `after`, `always`, `error`) with three-phase subset ordering (`"before"` โ `"primary"` โ `"after"`), pattern matching, priority, and runtime enable/disable.
|
|
542
238
|
|
|
543
|
-
```
|
|
544
|
-
// Simple enable (default pattern "**")
|
|
239
|
+
```javascript
|
|
545
240
|
const api = await slothlet({ dir: "./api", hook: true });
|
|
546
241
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
const api = await slothlet({
|
|
552
|
-
dir: "./api",
|
|
553
|
-
hook: {
|
|
554
|
-
enabled: true,
|
|
555
|
-
pattern: "**",
|
|
556
|
-
suppressErrors: false // true = errors suppressed (returns undefined instead of throwing)
|
|
557
|
-
}
|
|
558
|
-
});
|
|
559
|
-
```
|
|
560
|
-
|
|
561
|
-
### Hook Types
|
|
562
|
-
|
|
563
|
-
- **`before`** - Executes before the function. Can modify arguments or short-circuit. **Must be synchronous.**
|
|
564
|
-
- **`after`** - Executes after successful completion. Can transform the return value.
|
|
565
|
-
- **`always`** - Read-only observer. Always executes (even on short-circuit). Return value ignored.
|
|
566
|
-
- **`error`** - Executes only when an error occurs. Receives error with source tracking.
|
|
567
|
-
|
|
568
|
-
### Basic Usage
|
|
569
|
-
|
|
570
|
-
The `hook.on(typePattern, handler, options)` signature uses `"type:pattern"` as the first argument:
|
|
571
|
-
|
|
572
|
-
```js
|
|
573
|
-
// Before hook - modify arguments
|
|
574
|
-
api.slothlet.hook.on(
|
|
575
|
-
"before:math.add",
|
|
576
|
-
({ path, args }) => {
|
|
577
|
-
return [args[0] * 2, args[1] * 2]; // Return array to replace arguments
|
|
578
|
-
// Return non-array non-undefined to short-circuit (skip function)
|
|
579
|
-
// Return undefined to continue with original args
|
|
580
|
-
},
|
|
581
|
-
{ id: "double-args", priority: 100 }
|
|
582
|
-
);
|
|
583
|
-
|
|
584
|
-
// After hook - transform result
|
|
585
|
-
api.slothlet.hook.on(
|
|
586
|
-
"after:math.*",
|
|
587
|
-
({ path, result }) => {
|
|
588
|
-
return result * 10; // Return value replaces result; undefined = no change
|
|
589
|
-
},
|
|
590
|
-
{ id: "scale-result" }
|
|
591
|
-
);
|
|
592
|
-
|
|
593
|
-
// Always hook - observe (read-only)
|
|
594
|
-
api.slothlet.hook.on(
|
|
595
|
-
"always:**",
|
|
596
|
-
({ path, result, hasError, errors }) => {
|
|
597
|
-
if (hasError) console.error(`${path} failed:`, errors);
|
|
598
|
-
else console.log(`${path} returned:`, result);
|
|
599
|
-
// Return value is ignored
|
|
600
|
-
},
|
|
601
|
-
{ id: "logger" }
|
|
602
|
-
);
|
|
603
|
-
|
|
604
|
-
// Error hook - monitor failures
|
|
605
|
-
api.slothlet.hook.on(
|
|
606
|
-
"error:**",
|
|
607
|
-
({ path, error, source }) => {
|
|
608
|
-
// source.type: "before" | "after" | "always" | "function"
|
|
609
|
-
console.error(`Error in ${path} (from ${source.type}):`, error.message);
|
|
610
|
-
},
|
|
611
|
-
{ id: "error-monitor" }
|
|
612
|
-
);
|
|
613
|
-
```
|
|
242
|
+
api.slothlet.hook.on("before:math.add", ({ args }) => [args[0] * 2, args[1] * 2], { id: "double" });
|
|
243
|
+
api.slothlet.hook.on("after:math.*", ({ result }) => result * 10, { id: "scale" });
|
|
244
|
+
api.slothlet.hook.on("always:**", ({ path, hasError }) => console.log(path, hasError));
|
|
245
|
+
api.slothlet.hook.on("error:**", ({ path, error, source }) => console.error(path, source.type, error));
|
|
614
246
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
Each hook type has three ordered execution phases:
|
|
618
|
-
|
|
619
|
-
| Subset | Order | Typical use |
|
|
620
|
-
|---|---|---|
|
|
621
|
-
| `"before"` | First | Auth checks, security validation |
|
|
622
|
-
| `"primary"` | Middle (default) | Main hook logic |
|
|
623
|
-
| `"after"` | Last | Audit trails, cleanup |
|
|
624
|
-
|
|
625
|
-
```js
|
|
626
|
-
// Auth check runs first - always
|
|
627
|
-
api.slothlet.hook.on(
|
|
628
|
-
"before:protected.*",
|
|
629
|
-
({ ctx }) => { if (!ctx.user) throw new Error("Unauthorized"); },
|
|
630
|
-
{ id: "auth", subset: "before", priority: 2000 }
|
|
631
|
-
);
|
|
632
|
-
|
|
633
|
-
// Main validation logic - default subset
|
|
634
|
-
api.slothlet.hook.on(
|
|
635
|
-
"before:protected.*",
|
|
636
|
-
({ args }) => { /* validate */ },
|
|
637
|
-
{ id: "validate" } // subset: "primary" by default
|
|
638
|
-
);
|
|
639
|
-
|
|
640
|
-
// Audit log always runs last
|
|
641
|
-
api.slothlet.hook.on(
|
|
642
|
-
"after:protected.*",
|
|
643
|
-
({ path, result }) => { /* log */ },
|
|
644
|
-
{ id: "audit", subset: "after" }
|
|
645
|
-
);
|
|
247
|
+
const out = await api.math.add(2, 3); // hooks fire automatically
|
|
646
248
|
```
|
|
647
249
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
| Syntax | Description | Example |
|
|
651
|
-
|---|---|---|
|
|
652
|
-
| `exact.path` | Exact match | `"before:math.add"` |
|
|
653
|
-
| `namespace.*` | All functions in namespace | `"after:math.*"` |
|
|
654
|
-
| `*.funcName` | Function name across namespaces | `"always:*.add"` |
|
|
655
|
-
| `**` | All functions | `"error:**"` |
|
|
656
|
-
| `{a,b}` | Brace expansion | `"before:{math,utils}.*"` |
|
|
657
|
-
| `!pattern` | Negation | `"before:!internal.*"` |
|
|
658
|
-
|
|
659
|
-
### Hook Management
|
|
660
|
-
|
|
661
|
-
```js
|
|
662
|
-
// Remove by ID
|
|
663
|
-
api.slothlet.hook.remove({ id: "my-hook" });
|
|
664
|
-
api.slothlet.hook.off("my-hook"); // alias
|
|
665
|
-
|
|
666
|
-
// Remove by filter
|
|
667
|
-
api.slothlet.hook.remove({ type: "before", pattern: "math.*" });
|
|
668
|
-
|
|
669
|
-
// Remove all
|
|
670
|
-
api.slothlet.hook.clear();
|
|
671
|
-
|
|
672
|
-
// List hooks
|
|
673
|
-
const all = api.slothlet.hook.list();
|
|
674
|
-
const active = api.slothlet.hook.list({ enabled: true });
|
|
675
|
-
|
|
676
|
-
// Enable / disable without unregistering
|
|
677
|
-
api.slothlet.hook.disable();
|
|
678
|
-
api.slothlet.hook.disable({ pattern: "math.*" });
|
|
679
|
-
api.slothlet.hook.enable();
|
|
680
|
-
api.slothlet.hook.enable({ type: "before" });
|
|
681
|
-
```
|
|
250
|
+
๐ฃ **Configuration, all four types, subsets, pattern syntax, management API: [docs/HOOKS.md](./docs/HOOKS.md)**
|
|
682
251
|
|
|
683
252
|
---
|
|
684
253
|
|
|
685
|
-
## ๐ Per-Request Context
|
|
686
|
-
|
|
687
|
-
```js
|
|
688
|
-
const api = await slothlet({
|
|
689
|
-
dir: "./api",
|
|
690
|
-
context: { appName: "MyApp", version: "3.0" }
|
|
691
|
-
});
|
|
254
|
+
## ๐ Per-Request Context
|
|
692
255
|
|
|
693
|
-
|
|
256
|
+
```javascript
|
|
257
|
+
// Scoped context for a single call
|
|
694
258
|
await api.slothlet.context.run({ userId: "alice", role: "admin" }, async () => {
|
|
695
|
-
// Inside this scope: context = { appName, version, userId, role }
|
|
696
259
|
await api.database.query();
|
|
697
260
|
await api.audit.log();
|
|
698
261
|
});
|
|
699
262
|
|
|
700
|
-
//
|
|
701
|
-
const
|
|
702
|
-
await
|
|
703
|
-
|
|
704
|
-
// Deep merge strategy
|
|
705
|
-
await api.slothlet.context.run(
|
|
706
|
-
{ nested: { prop: "value" } },
|
|
707
|
-
handler,
|
|
708
|
-
{ mergeStrategy: "deep" }
|
|
709
|
-
);
|
|
263
|
+
// Derived API with merged context
|
|
264
|
+
const scoped = api.slothlet.context.scope({ userId: "bob" });
|
|
265
|
+
await scoped.database.query();
|
|
710
266
|
```
|
|
711
267
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
Context propagates automatically through EventEmitter callbacks:
|
|
715
|
-
|
|
716
|
-
```js
|
|
717
|
-
import net from "net";
|
|
718
|
-
import { context } from "@cldmv/slothlet/runtime";
|
|
719
|
-
|
|
720
|
-
export const server = {
|
|
721
|
-
async start() {
|
|
722
|
-
const tcpServer = net.createServer((socket) => {
|
|
723
|
-
console.log(`User ${context.userId} connected`);
|
|
724
|
-
|
|
725
|
-
socket.on("data", (data) => {
|
|
726
|
-
// Context preserved in all nested callbacks
|
|
727
|
-
console.log(`Data from ${context.userId}: ${data}`);
|
|
728
|
-
});
|
|
729
|
-
});
|
|
730
|
-
tcpServer.listen(3000);
|
|
731
|
-
}
|
|
732
|
-
};
|
|
733
|
-
```
|
|
268
|
+
Context propagates automatically through `EventEmitter` callbacks (TCP/HTTP servers, custom emitters), class methods, and every async boundary. Inside modules: `import { context, instanceID } from "@cldmv/slothlet/runtime"`.
|
|
734
269
|
|
|
735
|
-
|
|
270
|
+
๐ **Full reference, isolation guarantees, merge strategies, TCP/HTTP examples: [docs/CONTEXT-PROPAGATION.md](./docs/CONTEXT-PROPAGATION.md)**
|
|
736
271
|
|
|
737
272
|
---
|
|
738
273
|
|
|
739
|
-
##
|
|
740
|
-
|
|
741
|
-
Tag API paths with metadata for authorization, auditing, and security.
|
|
742
|
-
|
|
743
|
-
```js
|
|
744
|
-
// Attach metadata when loading
|
|
745
|
-
await api.slothlet.api.add("plugins/trusted", "./trusted-dir", {
|
|
746
|
-
metadata: { trusted: true, securityLevel: "high" }
|
|
747
|
-
});
|
|
748
|
-
|
|
749
|
-
// Set metadata at runtime
|
|
750
|
-
api.slothlet.metadata.set("plugins.trusted.someFunc", { version: 2 });
|
|
751
|
-
api.slothlet.metadata.setGlobal({ environment: "production" });
|
|
752
|
-
api.slothlet.metadata.setFor("plugins/trusted", { owner: "core-team" });
|
|
753
|
-
api.slothlet.metadata.remove("plugins.old.func");
|
|
754
|
-
```
|
|
755
|
-
|
|
756
|
-
๐ **For complete metadata documentation, see [docs/METADATA.md](https://github.com/CLDMV/slothlet/blob/master/docs/METADATA.md)**
|
|
757
|
-
|
|
758
|
-
---
|
|
759
|
-
|
|
760
|
-
## ๐ Hot Reload / Dynamic API Management _(v3 API)_
|
|
761
|
-
|
|
762
|
-
```js
|
|
763
|
-
// Add new modules at runtime
|
|
764
|
-
await api.slothlet.api.add("newModule", "./new-module-path");
|
|
765
|
-
await api.slothlet.api.add("plugins", "./plugins", { collision: "merge" });
|
|
766
|
-
|
|
767
|
-
// Remove modules
|
|
768
|
-
await api.slothlet.api.remove("oldModule");
|
|
769
|
-
|
|
770
|
-
// Reload specific path or all modules
|
|
771
|
-
await api.slothlet.api.reload("database.*");
|
|
772
|
-
await api.slothlet.api.reload("plugins.auth");
|
|
773
|
-
```
|
|
774
|
-
|
|
775
|
-
> **Lazy mode reload behavior**: In lazy mode, reload restores modules to an unmaterialized proxy state - existing references are intentionally not preserved. Eager mode merges new module exports into the existing live wrapper, preserving references.
|
|
274
|
+
## ๐ Hot Reload & Dynamic API
|
|
776
275
|
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
const api = await slothlet({
|
|
783
|
-
dir: "./api",
|
|
784
|
-
api: {
|
|
785
|
-
collision: {
|
|
786
|
-
initial: "merge", // During initial load()
|
|
787
|
-
api: "skip" // During api.slothlet.api.add()
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
});
|
|
276
|
+
```javascript
|
|
277
|
+
await api.slothlet.api.add("plugins", "./plugins-folder"); // add at runtime
|
|
278
|
+
await api.slothlet.api.add("plugins.trusted", "./trusted", { metadata: { trusted: true } });
|
|
279
|
+
await api.slothlet.api.remove("oldModule"); // remove
|
|
280
|
+
await api.slothlet.api.reload("database.*"); // hot-reload
|
|
791
281
|
```
|
|
792
282
|
|
|
793
|
-
|
|
794
|
-
|---|---|
|
|
795
|
-
| `"overwrite"` | Replace existing (default) |
|
|
796
|
-
| `"merge"` | Deep-merge new into existing |
|
|
797
|
-
| `"skip"` | Keep existing, ignore new |
|
|
798
|
-
| `"throw"` | Throw an error on conflict |
|
|
799
|
-
|
|
800
|
-
### Mutation Controls _(new in v3)_
|
|
283
|
+
Collision modes (`merge` / `skip` / `overwrite` / `throw`) โ independently configurable for initial load vs runtime `add()`. Mutation controls let you disable `add` / `remove` / `reload` in production. Eager vs lazy reload semantics differ (eager merges into the live wrapper; lazy resets to an unmaterialized proxy).
|
|
801
284
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
```js
|
|
805
|
-
const api = await slothlet({
|
|
806
|
-
dir: "./api",
|
|
807
|
-
api: {
|
|
808
|
-
mutations: {
|
|
809
|
-
add: true,
|
|
810
|
-
remove: false, // Prevent removal in production
|
|
811
|
-
reload: false // Prevent reload in production
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
});
|
|
815
|
-
```
|
|
285
|
+
๐ **Full reference: [docs/RELOAD.md](./docs/RELOAD.md)** ยท ๐ท๏ธ **Metadata system: [docs/METADATA.md](./docs/METADATA.md)**
|
|
816
286
|
|
|
817
287
|
---
|
|
818
288
|
|
|
819
|
-
## โก Lifecycle Events
|
|
820
|
-
|
|
821
|
-
Subscribe to internal module lifecycle events:
|
|
289
|
+
## โก Lifecycle Events
|
|
822
290
|
|
|
823
|
-
```
|
|
824
|
-
|
|
825
|
-
api.slothlet.lifecycle.on("
|
|
826
|
-
|
|
291
|
+
```javascript
|
|
292
|
+
api.slothlet.lifecycle.on("materialized:complete", ({ total }) => console.log(`${total} modules ready`));
|
|
293
|
+
api.slothlet.lifecycle.on("impl:created", ({ apiPath }) => {
|
|
294
|
+
/* โฆ */
|
|
827
295
|
});
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
console.log(`Module created at ${data.apiPath}`);
|
|
296
|
+
api.slothlet.lifecycle.on("impl:changed", ({ apiPath }) => {
|
|
297
|
+
/* reload notify */
|
|
831
298
|
});
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
console.log(`Module at ${data.apiPath} was reloaded`);
|
|
299
|
+
api.slothlet.lifecycle.on("impl:removed", ({ apiPath }) => {
|
|
300
|
+
/* cleanup */
|
|
835
301
|
});
|
|
836
|
-
|
|
837
|
-
api.slothlet.lifecycle.on("impl:removed", (data) => {
|
|
838
|
-
console.log(`Module at ${data.apiPath} was removed`);
|
|
839
|
-
});
|
|
840
|
-
|
|
841
|
-
// Unsubscribe
|
|
842
|
-
const handler = (data) => console.log(data);
|
|
843
|
-
api.slothlet.lifecycle.on("impl:changed", handler);
|
|
844
|
-
api.slothlet.lifecycle.off("impl:changed", handler);
|
|
845
302
|
```
|
|
846
303
|
|
|
847
|
-
|
|
304
|
+
Events: `materialized:complete`, `impl:created`, `impl:changed`, `impl:removed`. Public surface is `on` / `off` only.
|
|
848
305
|
|
|
849
|
-
|
|
850
|
-
> `api.slothlet.lifecycle` exposes **`on` and `off` only**. `emit`, `subscribe`, and `unsubscribe` are internal - they are not present on the public API object.
|
|
306
|
+
โก **Full reference: [docs/LIFECYCLE.md](./docs/LIFECYCLE.md)**
|
|
851
307
|
|
|
852
308
|
---
|
|
853
309
|
|
|
854
|
-
## ๐
|
|
855
|
-
|
|
856
|
-
### โ
Clean Folder Structure
|
|
310
|
+
## ๐ Module Structure
|
|
857
311
|
|
|
858
312
|
```text
|
|
859
313
|
api/
|
|
860
314
|
โโโ config.mjs โ api.config.*
|
|
861
315
|
โโโ math/
|
|
862
|
-
โ โโโ math.mjs โ api.math.*
|
|
316
|
+
โ โโโ math.mjs โ api.math.* (flattened โ filename matches folder)
|
|
863
317
|
โโโ util/
|
|
864
|
-
โ โโโ util.mjs โ api.util.*
|
|
318
|
+
โ โโโ util.mjs โ api.util.* (flattened methods)
|
|
865
319
|
โ โโโ extract.mjs โ api.util.extract.*
|
|
866
320
|
โ โโโ controller.mjs โ api.util.controller.*
|
|
867
|
-
|
|
868
|
-
โ โโโ date/
|
|
869
|
-
โ โโโ date.mjs โ api.nested.date.*
|
|
870
|
-
โโโ multi/
|
|
871
|
-
โโโ alpha.mjs โ api.multi.alpha.*
|
|
872
|
-
โโโ beta.mjs โ api.multi.beta.*
|
|
321
|
+
โโโ nested/date/date.mjs โ api.nested.date.*
|
|
873
322
|
```
|
|
874
323
|
|
|
875
|
-
|
|
324
|
+
API modules **must never import each other directly** โ use the live-binding runtime:
|
|
876
325
|
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
- **Dash-separated names** โ camelCase API (`auto-ip.mjs` โ `api.autoIP`)
|
|
880
|
-
- **Function name preferred** โ Original capitalization kept over sanitized form (see [Rule 9](docs/API-RULES.md))
|
|
881
|
-
|
|
882
|
-
### โ
Use `self` for Cross-Module Access
|
|
883
|
-
|
|
884
|
-
API modules must never import each other directly. Use Slothlet's live-binding system instead:
|
|
885
|
-
|
|
886
|
-
```js
|
|
887
|
-
// โ WRONG - breaks lazy loading and context isolation
|
|
326
|
+
```javascript
|
|
327
|
+
// โ WRONG โ breaks lazy loading and context isolation
|
|
888
328
|
import { math } from "./math/math.mjs";
|
|
889
329
|
|
|
890
|
-
// โ
CORRECT
|
|
330
|
+
// โ
CORRECT โ always reflects current runtime state
|
|
891
331
|
import { self, context, instanceID } from "@cldmv/slothlet/runtime";
|
|
892
332
|
|
|
893
333
|
export const myModule = {
|
|
894
334
|
async processData(input) {
|
|
895
|
-
const
|
|
896
|
-
console.log(`[${instanceID}] caller=${context.userId}`);
|
|
897
|
-
return `Processed: ${input}, Math: ${
|
|
335
|
+
const r = self.math.add(2, 3);
|
|
336
|
+
console.log(`[${instanceID}] caller=${context.userId}`);
|
|
337
|
+
return `Processed: ${input}, Math: ${r}`;
|
|
898
338
|
}
|
|
899
339
|
};
|
|
900
340
|
```
|
|
901
341
|
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
---
|
|
905
|
-
|
|
906
|
-
## ๐ Performance Analysis
|
|
907
|
-
|
|
908
|
-
For comprehensive performance benchmarks, analysis, and recommendations:
|
|
909
|
-
|
|
910
|
-
**๐ [See docs/PERFORMANCE.md](https://github.com/CLDMV/slothlet/blob/master/docs/PERFORMANCE.md)**
|
|
911
|
-
|
|
912
|
-
Key highlights:
|
|
913
|
-
|
|
914
|
-
- Detailed startup vs runtime performance comparison
|
|
915
|
-
- Memory usage analysis by loading mode
|
|
916
|
-
- Materialization cost breakdown by module type
|
|
917
|
-
- Real-world performance recommendations
|
|
918
|
-
|
|
919
|
-
[![CodeFactor]][codefactor_url] [![npms.io score]][npms_url]
|
|
920
|
-
|
|
921
|
-
[![npm unpacked size]][npm_size_url] [![Repo size]][repo_size_url]
|
|
922
|
-
|
|
923
|
-
---
|
|
924
|
-
|
|
925
|
-
## ๐ Documentation
|
|
926
|
-
|
|
927
|
-
### Core Documentation
|
|
928
|
-
|
|
929
|
-
- **[Performance Analysis](https://github.com/CLDMV/slothlet/blob/master/docs/PERFORMANCE.md)** - Detailed benchmarks and recommendations
|
|
930
|
-
- **[Agent Usage Guide](AGENT-USAGE.md)** - Guide for AI agents building Slothlet API folders
|
|
931
|
-
- **[Contributing Guide](CONTRIBUTING.md)** - How to contribute to the project
|
|
932
|
-
- **[Security Policy](SECURITY.md)** - Security guidelines and reporting
|
|
933
|
-
- **[Test Documentation](api_tests)** - Comprehensive test module examples
|
|
934
|
-
|
|
935
|
-
### Technical Guides
|
|
936
|
-
|
|
937
|
-
- **[TypeScript Support](https://github.com/CLDMV/slothlet/blob/master/docs/TYPESCRIPT.md)** - Native TypeScript support: fast mode (esbuild), strict mode (tsc), and `.d.ts` type generation
|
|
938
|
-
- **[Hook System](https://github.com/CLDMV/slothlet/blob/master/docs/HOOKS.md)** - Complete hook system documentation with 4 hook types, three-phase subsets, pattern matching, and examples
|
|
939
|
-
- **[Context Propagation](https://github.com/CLDMV/slothlet/blob/master/docs/CONTEXT-PROPAGATION.md)** - EventEmitter and class instance context preservation
|
|
940
|
-
- **[Metadata System](https://github.com/CLDMV/slothlet/blob/master/docs/METADATA.md)** - Function metadata tagging and runtime introspection for security, authorization, and auditing
|
|
941
|
-
- **[Module Structure](https://github.com/CLDMV/slothlet/blob/master/docs/MODULE-STRUCTURE.md)** - Comprehensive module organization patterns and examples
|
|
942
|
-
- **[Sanitization](https://github.com/CLDMV/slothlet/blob/master/docs/SANITIZATION.md)** - Property name sanitization rules
|
|
943
|
-
- **[Internationalization](https://github.com/CLDMV/slothlet/blob/master/docs/I18N.md)** - i18n support, language configuration, and available translations
|
|
944
|
-
|
|
945
|
-
### API Rules & Transformation
|
|
946
|
-
|
|
947
|
-
- **[API Rules](docs/API-RULES.md)** - All 13 API transformation rules with verified test examples
|
|
948
|
-
- **[API Rules Conditions](docs/API-RULES/API-RULES-CONDITIONS.md)** - Complete technical reference of all conditional statements that control API generation
|
|
949
|
-
- **[API Flattening](docs/API-RULES/API-FLATTENING.md)** - Flattening rules with decision tree and benefits
|
|
950
|
-
|
|
951
|
-
---
|
|
952
|
-
|
|
953
|
-
## ๐ Migration from v2.x
|
|
342
|
+
The same import works from `.mjs`, `.cjs` (via `require`), `.ts`, and `.mts` (TypeScript path fixed in v3.5.0).
|
|
954
343
|
|
|
955
|
-
|
|
344
|
+
๐๏ธ **[Module structure patterns](./docs/MODULE-STRUCTURE.md)** ยท ๐ **[All 13 API transformation rules](./docs/API-RULES.md)**
|
|
956
345
|
|
|
957
346
|
---
|
|
958
347
|
|
|
@@ -965,42 +354,70 @@ try {
|
|
|
965
354
|
await api.slothlet.api.add("plugins", "./dir");
|
|
966
355
|
} catch (error) {
|
|
967
356
|
console.error(error.message); // Translated error message
|
|
968
|
-
console.error(error.hint);
|
|
969
|
-
console.error(error.code);
|
|
357
|
+
console.error(error.hint); // Contextual hint for resolution
|
|
358
|
+
console.error(error.code); // Machine-readable error code
|
|
970
359
|
}
|
|
971
360
|
```
|
|
972
361
|
|
|
973
362
|
---
|
|
974
363
|
|
|
975
|
-
## ๐๏ธ Production & Development
|
|
976
|
-
|
|
977
|
-
### Production Ready โ
|
|
364
|
+
## ๐๏ธ Production & Development
|
|
978
365
|
|
|
979
|
-
- **Eager Mode**: Stable, battle-tested, maximum performance
|
|
366
|
+
- **Eager Mode**: Stable, battle-tested, maximum runtime performance
|
|
980
367
|
- **Lazy Mode**: Production-ready with copy-left optimization
|
|
981
368
|
- **Background Materialization**: Lazy startup + eager runtime performance
|
|
982
369
|
- **Mixed Module Loading**: ESM/CJS interoperability fully supported
|
|
983
|
-
|
|
984
|
-
### Development Features ๐ ๏ธ
|
|
985
|
-
|
|
986
|
-
- **Debug Mode**: Comprehensive i18n-translated logging via `--slothletdebug` flag or `SLOTHLET_DEBUG=true`
|
|
987
|
-
- **Development Check**: `devcheck.mjs` for environment validation
|
|
370
|
+
- **Debug Mode**: i18n-translated logging via `--slothletdebug` flag or `SLOTHLET_DEBUG=true`
|
|
988
371
|
- **Source Detection**: Automatic `src/` vs `dist/` mode detection
|
|
989
|
-
- **API Inspection**: `console.log(api.math)` and versioned dispatcher paths
|
|
372
|
+
- **API Inspection**: `console.log(api.math)` and versioned dispatcher paths show real module contents (v3+)
|
|
990
373
|
|
|
991
374
|
---
|
|
992
375
|
|
|
993
|
-
##
|
|
376
|
+
## ๐ Documentation
|
|
377
|
+
|
|
378
|
+
### Reference
|
|
994
379
|
|
|
995
|
-
|
|
380
|
+
- **[Configuration Reference](./docs/CONFIGURATION.md)** โ every option with defaults, validation rules, and the `api.slothlet.diag.*` namespace
|
|
381
|
+
- **[Generated API Reference](./docs/generated/API.md)** โ auto-generated from JSDoc; the complete public surface
|
|
382
|
+
- **[Changelog](./docs/changelog/)** โ all release notes (v2 + v3)
|
|
383
|
+
- **[Migration Guide](./docs/MIGRATION.md)** โ upgrading from v2.x
|
|
996
384
|
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
385
|
+
### Technical Guides
|
|
386
|
+
|
|
387
|
+
- **[Performance Analysis](./docs/PERFORMANCE.md)** โ startup vs runtime benchmarks, memory analysis, materialization cost breakdown
|
|
388
|
+
- **[Hook System](./docs/HOOKS.md)** โ 4 types, three-phase subsets, pattern matching, management API
|
|
389
|
+
- **[Permission System](./docs/PERMISSIONS.md)** โ rules, glob patterns, self-call bypass, read gating, runtime management
|
|
390
|
+
- **[Permission Conditions](./docs/PERMISSIONS-CONDITIONS.md)** โ `condition` field syntax: deep object matching, functions, OR arrays
|
|
391
|
+
- **[Context Propagation](./docs/CONTEXT-PROPAGATION.md)** โ per-request isolation, EventEmitter / class propagation, merge strategies
|
|
392
|
+
- **[Lifecycle Events](./docs/LIFECYCLE.md)** โ `materialized:complete`, `impl:*` events, subscription API
|
|
393
|
+
- **[Hot Reload & Dynamic API](./docs/RELOAD.md)** โ `add`, `remove`, `reload`, collision modes, mutation controls, eager vs lazy semantics
|
|
394
|
+
- **[Versioning](./docs/VERSIONING.md)** โ multi-version module dispatch, `versionDispatcher`, version metadata
|
|
395
|
+
- **[Metadata System](./docs/METADATA.md)** โ function metadata tagging for security, authorization, auditing
|
|
396
|
+
- **[Module Structure](./docs/MODULE-STRUCTURE.md)** โ organization patterns, examples, and the loading-pipeline diagram
|
|
397
|
+
- **[Sanitization](./docs/SANITIZATION.md)** โ filename โ property-name transformation rules
|
|
398
|
+
- **[TypeScript Support](./docs/TYPESCRIPT.md)** โ fast mode (esbuild), strict mode (tsc), `.d.ts` generation
|
|
399
|
+
- **[Internationalization](./docs/I18N.md)** โ supported languages and configuration
|
|
400
|
+
|
|
401
|
+
### API Rules & Transformation
|
|
402
|
+
|
|
403
|
+
- **[API Rules](./docs/API-RULES.md)** โ all 13 transformation rules with verified test examples
|
|
404
|
+
- **[API Rules Conditions](./docs/API-RULES/API-RULES-CONDITIONS.md)** โ every conditional that controls API generation
|
|
405
|
+
- **[API Flattening](./docs/API-RULES/API-FLATTENING.md)** โ flattening rules with decision tree
|
|
406
|
+
|
|
407
|
+
### Repo
|
|
408
|
+
|
|
409
|
+
- **[Agent Usage Guide](./AGENT-USAGE.md)** โ for AI agents building Slothlet API folders
|
|
410
|
+
- **[Contributing](./CONTRIBUTING.md)** โ contribution guidelines
|
|
411
|
+
- **[Security Policy](./SECURITY.md)** โ security guidelines and reporting
|
|
412
|
+
- **[Test Documentation](./api_tests/)** โ comprehensive test module examples
|
|
413
|
+
|
|
414
|
+
[![CodeFactor]][codefactor_url] [![npms.io score]][npms_url] [![npm unpacked size]][npm_size_url] [![Repo size]][repo_size_url]
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
## ๐ค Contributing
|
|
1002
419
|
|
|
1003
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) for
|
|
420
|
+
We welcome contributions! See **[CONTRIBUTING.md](./CONTRIBUTING.md)** for guidelines.
|
|
1004
421
|
|
|
1005
422
|
[![Contributors]][contributors_url] [![Sponsor shinrai]][sponsor_url]
|
|
1006
423
|
|