@cldmv/slothlet 2.10.0 → 3.0.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 +365 -294
- package/README.md +556 -233
- package/dist/lib/builders/api-assignment.mjs +605 -0
- package/dist/lib/builders/api_builder.mjs +1073 -0
- package/dist/lib/builders/builder.mjs +94 -0
- package/dist/lib/builders/modes-processor.mjs +1816 -0
- package/dist/lib/errors.mjs +227 -0
- package/dist/lib/factories/component-base.mjs +96 -0
- package/dist/lib/factories/context.mjs +38 -0
- package/dist/lib/handlers/api-cache-manager.mjs +216 -0
- package/dist/lib/handlers/api-manager.mjs +2364 -0
- package/dist/lib/handlers/context-async.mjs +184 -0
- package/dist/lib/handlers/context-live.mjs +184 -0
- package/dist/lib/handlers/hook-manager.mjs +789 -0
- package/dist/lib/handlers/lifecycle-token.mjs +44 -0
- package/dist/lib/handlers/lifecycle.mjs +131 -0
- package/dist/lib/handlers/materialize-manager.mjs +64 -0
- package/dist/lib/handlers/metadata.mjs +500 -0
- package/dist/lib/handlers/ownership.mjs +338 -0
- package/dist/lib/handlers/unified-wrapper.mjs +3031 -0
- package/dist/lib/helpers/class-instance-wrapper.mjs +125 -0
- package/dist/lib/helpers/config.mjs +343 -0
- package/dist/lib/helpers/eventemitter-context.mjs +365 -0
- package/dist/lib/helpers/hint-detector.mjs +63 -0
- package/dist/lib/helpers/modes-utils.mjs +53 -0
- package/dist/lib/helpers/resolve-from-caller.mjs +125 -85
- package/dist/lib/helpers/sanitize.mjs +247 -168
- package/dist/lib/helpers/utilities.mjs +46 -81
- package/dist/lib/i18n/languages/de-de.json +377 -0
- package/dist/lib/i18n/languages/en-gb.json +377 -0
- package/dist/lib/i18n/languages/en-us.json +377 -0
- package/dist/lib/i18n/languages/es-mx.json +377 -0
- package/dist/lib/i18n/languages/fr-fr.json +377 -0
- package/dist/lib/i18n/languages/hi-in.json +377 -0
- package/dist/lib/i18n/languages/ja-jp.json +377 -0
- package/dist/lib/i18n/languages/ko-kr.json +377 -0
- package/dist/lib/i18n/languages/pt-br.json +377 -0
- package/dist/lib/i18n/languages/ru-ru.json +377 -0
- package/dist/lib/i18n/languages/zh-cn.json +377 -0
- package/dist/lib/i18n/translations.mjs +140 -0
- package/dist/lib/modes/eager.mjs +75 -0
- package/dist/lib/modes/lazy.mjs +97 -0
- package/dist/lib/processors/flatten.mjs +453 -0
- package/dist/lib/processors/loader.mjs +355 -0
- package/dist/lib/processors/type-generator.mjs +291 -0
- package/dist/lib/processors/typescript.mjs +188 -0
- package/dist/lib/runtime/runtime-asynclocalstorage.mjs +80 -522
- package/dist/lib/runtime/runtime-livebindings.mjs +45 -390
- package/dist/lib/runtime/runtime.mjs +39 -159
- package/dist/slothlet.mjs +529 -700
- package/docs/API-RULES.md +497 -617
- package/index.cjs +4 -4
- package/index.mjs +82 -45
- package/package.json +139 -26
- package/types/dist/lib/builders/api-assignment.d.mts +97 -0
- package/types/dist/lib/builders/api-assignment.d.mts.map +1 -0
- package/types/dist/lib/builders/api_builder.d.mts +96 -0
- package/types/dist/lib/builders/api_builder.d.mts.map +1 -0
- package/types/dist/lib/builders/builder.d.mts +60 -0
- package/types/dist/lib/builders/builder.d.mts.map +1 -0
- package/types/dist/lib/builders/modes-processor.d.mts +32 -0
- package/types/dist/lib/builders/modes-processor.d.mts.map +1 -0
- package/types/dist/lib/errors.d.mts +118 -0
- package/types/dist/lib/errors.d.mts.map +1 -0
- package/types/dist/lib/factories/component-base.d.mts +182 -0
- package/types/dist/lib/factories/component-base.d.mts.map +1 -0
- package/types/dist/lib/factories/context.d.mts +26 -0
- package/types/dist/lib/factories/context.d.mts.map +1 -0
- package/types/dist/lib/handlers/api-cache-manager.d.mts +208 -0
- package/types/dist/lib/handlers/api-cache-manager.d.mts.map +1 -0
- package/types/dist/lib/handlers/api-manager.d.mts +392 -0
- package/types/dist/lib/handlers/api-manager.d.mts.map +1 -0
- package/types/dist/lib/handlers/context-async.d.mts +66 -0
- package/types/dist/lib/handlers/context-async.d.mts.map +1 -0
- package/types/dist/lib/handlers/context-live.d.mts +65 -0
- package/types/dist/lib/handlers/context-live.d.mts.map +1 -0
- package/types/dist/lib/handlers/hook-manager.d.mts +199 -0
- package/types/dist/lib/handlers/hook-manager.d.mts.map +1 -0
- package/types/dist/lib/handlers/lifecycle-token.d.mts +49 -0
- package/types/dist/lib/handlers/lifecycle-token.d.mts.map +1 -0
- package/types/dist/lib/handlers/lifecycle.d.mts +90 -0
- package/types/dist/lib/handlers/lifecycle.d.mts.map +1 -0
- package/types/dist/lib/handlers/materialize-manager.d.mts +75 -0
- package/types/dist/lib/handlers/materialize-manager.d.mts.map +1 -0
- package/types/dist/lib/handlers/metadata.d.mts +215 -0
- package/types/dist/lib/handlers/metadata.d.mts.map +1 -0
- package/types/dist/lib/handlers/ownership.d.mts +170 -0
- package/types/dist/lib/handlers/ownership.d.mts.map +1 -0
- package/types/dist/lib/handlers/unified-wrapper.d.mts +250 -0
- package/types/dist/lib/handlers/unified-wrapper.d.mts.map +1 -0
- package/types/dist/lib/helpers/class-instance-wrapper.d.mts +54 -0
- package/types/dist/lib/helpers/class-instance-wrapper.d.mts.map +1 -0
- package/types/dist/lib/helpers/config.d.mts +96 -0
- package/types/dist/lib/helpers/config.d.mts.map +1 -0
- package/types/dist/lib/helpers/eventemitter-context.d.mts +31 -0
- package/types/dist/lib/helpers/eventemitter-context.d.mts.map +1 -0
- package/types/dist/lib/helpers/hint-detector.d.mts +20 -0
- package/types/dist/lib/helpers/hint-detector.d.mts.map +1 -0
- package/types/dist/lib/helpers/modes-utils.d.mts +35 -0
- package/types/dist/lib/helpers/modes-utils.d.mts.map +1 -0
- package/types/dist/lib/helpers/resolve-from-caller.d.mts +29 -145
- package/types/dist/lib/helpers/resolve-from-caller.d.mts.map +1 -1
- package/types/dist/lib/helpers/sanitize.d.mts +95 -94
- package/types/dist/lib/helpers/sanitize.d.mts.map +1 -1
- package/types/dist/lib/helpers/utilities.d.mts +53 -116
- package/types/dist/lib/helpers/utilities.d.mts.map +1 -1
- package/types/dist/lib/i18n/translations.d.mts +39 -0
- package/types/dist/lib/i18n/translations.d.mts.map +1 -0
- package/types/dist/lib/modes/eager.d.mts +36 -0
- package/types/dist/lib/modes/eager.d.mts.map +1 -0
- package/types/dist/lib/modes/lazy.d.mts +49 -0
- package/types/dist/lib/modes/lazy.d.mts.map +1 -0
- package/types/dist/lib/processors/flatten.d.mts +114 -0
- package/types/dist/lib/processors/flatten.d.mts.map +1 -0
- package/types/dist/lib/processors/loader.d.mts +47 -0
- package/types/dist/lib/processors/loader.d.mts.map +1 -0
- package/types/dist/lib/processors/type-generator.d.mts +19 -0
- package/types/dist/lib/processors/type-generator.d.mts.map +1 -0
- package/types/dist/lib/processors/typescript.d.mts +55 -0
- package/types/dist/lib/processors/typescript.d.mts.map +1 -0
- package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts +47 -42
- package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts.map +1 -1
- package/types/dist/lib/runtime/runtime-livebindings.d.mts +34 -65
- package/types/dist/lib/runtime/runtime-livebindings.d.mts.map +1 -1
- package/types/dist/lib/runtime/runtime.d.mts +39 -9
- package/types/dist/lib/runtime/runtime.d.mts.map +1 -1
- package/types/dist/slothlet.d.mts +186 -105
- package/types/dist/slothlet.d.mts.map +1 -1
- package/types/index.d.mts +1 -3
- package/dist/lib/engine/README.md +0 -21
- package/dist/lib/engine/slothlet_child.mjs +0 -59
- package/dist/lib/engine/slothlet_engine.mjs +0 -372
- package/dist/lib/engine/slothlet_esm.mjs +0 -230
- package/dist/lib/engine/slothlet_helpers.mjs +0 -455
- package/dist/lib/engine/slothlet_worker.mjs +0 -149
- package/dist/lib/helpers/als-eventemitter.mjs +0 -256
- package/dist/lib/helpers/api_builder/add_api.mjs +0 -292
- package/dist/lib/helpers/api_builder/analysis.mjs +0 -532
- package/dist/lib/helpers/api_builder/construction.mjs +0 -457
- package/dist/lib/helpers/api_builder/decisions.mjs +0 -737
- package/dist/lib/helpers/api_builder/metadata.mjs +0 -248
- package/dist/lib/helpers/api_builder.mjs +0 -41
- package/dist/lib/helpers/auto-wrap.mjs +0 -62
- package/dist/lib/helpers/hooks.mjs +0 -389
- package/dist/lib/helpers/instance-manager.mjs +0 -111
- package/dist/lib/helpers/metadata-api.mjs +0 -201
- package/dist/lib/helpers/multidefault.mjs +0 -216
- package/dist/lib/modes/slothlet_eager.mjs +0 -126
- package/dist/lib/modes/slothlet_lazy.mjs +0 -513
- package/docs/API-RULES-CONDITIONS.md +0 -508
- package/types/dist/lib/engine/slothlet_child.d.mts +0 -2
- package/types/dist/lib/engine/slothlet_child.d.mts.map +0 -1
- package/types/dist/lib/engine/slothlet_engine.d.mts +0 -31
- package/types/dist/lib/engine/slothlet_engine.d.mts.map +0 -1
- package/types/dist/lib/engine/slothlet_esm.d.mts +0 -19
- package/types/dist/lib/engine/slothlet_esm.d.mts.map +0 -1
- package/types/dist/lib/engine/slothlet_helpers.d.mts +0 -25
- package/types/dist/lib/engine/slothlet_helpers.d.mts.map +0 -1
- package/types/dist/lib/engine/slothlet_worker.d.mts +0 -2
- package/types/dist/lib/engine/slothlet_worker.d.mts.map +0 -1
- package/types/dist/lib/helpers/als-eventemitter.d.mts +0 -56
- package/types/dist/lib/helpers/als-eventemitter.d.mts.map +0 -1
- package/types/dist/lib/helpers/api_builder/add_api.d.mts +0 -76
- package/types/dist/lib/helpers/api_builder/add_api.d.mts.map +0 -1
- package/types/dist/lib/helpers/api_builder/analysis.d.mts +0 -189
- package/types/dist/lib/helpers/api_builder/analysis.d.mts.map +0 -1
- package/types/dist/lib/helpers/api_builder/construction.d.mts +0 -107
- package/types/dist/lib/helpers/api_builder/construction.d.mts.map +0 -1
- package/types/dist/lib/helpers/api_builder/decisions.d.mts +0 -213
- package/types/dist/lib/helpers/api_builder/decisions.d.mts.map +0 -1
- package/types/dist/lib/helpers/api_builder/metadata.d.mts +0 -99
- package/types/dist/lib/helpers/api_builder/metadata.d.mts.map +0 -1
- package/types/dist/lib/helpers/api_builder.d.mts +0 -6
- package/types/dist/lib/helpers/api_builder.d.mts.map +0 -1
- package/types/dist/lib/helpers/auto-wrap.d.mts +0 -49
- package/types/dist/lib/helpers/auto-wrap.d.mts.map +0 -1
- package/types/dist/lib/helpers/hooks.d.mts +0 -342
- package/types/dist/lib/helpers/hooks.d.mts.map +0 -1
- package/types/dist/lib/helpers/instance-manager.d.mts +0 -41
- package/types/dist/lib/helpers/instance-manager.d.mts.map +0 -1
- package/types/dist/lib/helpers/metadata-api.d.mts +0 -132
- package/types/dist/lib/helpers/metadata-api.d.mts.map +0 -1
- package/types/dist/lib/helpers/multidefault.d.mts +0 -90
- package/types/dist/lib/helpers/multidefault.d.mts.map +0 -1
- package/types/dist/lib/modes/slothlet_eager.d.mts +0 -65
- package/types/dist/lib/modes/slothlet_eager.d.mts.map +0 -1
- package/types/dist/lib/modes/slothlet_lazy.d.mts +0 -31
- package/types/dist/lib/modes/slothlet_lazy.d.mts.map +0 -1
package/README.md
CHANGED
|
@@ -8,50 +8,72 @@
|
|
|
8
8
|
|
|
9
9
|
Choose your loading strategy based on your needs: **lazy mode** loads modules on-demand for faster startup and lower memory usage, while **eager mode** loads everything upfront for maximum runtime performance and predictable behavior.
|
|
10
10
|
|
|
11
|
-
With our **copy-left materialization** in lazy mode, you get the best of both worlds: the memory efficiency of on-demand loading with near-eager performance on repeated calls. Once a module is materialized, it stays materialized
|
|
11
|
+
With our **copy-left materialization** in lazy mode, you get the best of both worlds: the memory efficiency of on-demand loading with near-eager performance on repeated calls. Once a module is materialized, it stays materialized-no re-processing overhead.
|
|
12
12
|
|
|
13
13
|
The name might suggest we're taking it easy, but don't be fooled. **Slothlet delivers speed where it counts**, with smart optimizations that make your APIs fly.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
**🎉 Welcome to the future of module loading with Slothlet v3!**
|
|
16
|
+
|
|
17
|
+
> _Where sophisticated architecture meets blazing performance - slothlet is anything but slow._
|
|
16
18
|
|
|
17
19
|
[![npm version]][npm_version_url] [![npm downloads]][npm_downloads_url] <!-- [![GitHub release]][github_release_url] -->[![GitHub downloads]][github_downloads_url] [![Last commit]][last_commit_url] <!-- [![Release date]][release_date_url] -->[![npm last update]][npm_last_update_url]
|
|
18
20
|
|
|
19
|
-
> [!NOTE]
|
|
21
|
+
> [!NOTE]
|
|
20
22
|
> **🚀 Production Ready Modes:**
|
|
21
23
|
>
|
|
22
24
|
> - **Eager Mode**: Fully stable and production-ready for immediate module loading
|
|
23
|
-
> - **Lazy Mode**: Production-ready with advanced copy-left materialization and
|
|
24
|
-
|
|
25
|
-
> [!CAUTION]
|
|
26
|
-
> **⚙️ Experimental Modes:**
|
|
27
|
-
>
|
|
28
|
-
> - **Worker, Fork, Child, VM modes**: In active development, not recommended for production use
|
|
29
|
-
>
|
|
30
|
-
> Please report issues and contribute feedback to help improve the experimental features.
|
|
25
|
+
> - **Lazy Mode**: Production-ready with advanced copy-left materialization and 2.2x faster startup (function calls within 6% of eager - essentially equal)
|
|
31
26
|
|
|
32
27
|
[![Contributors]][contributors_url] [![Sponsor shinrai]][sponsor_url]
|
|
33
28
|
|
|
34
29
|
---
|
|
35
30
|
|
|
31
|
+
## 🎉 Introducing Slothlet v3.0
|
|
32
|
+
|
|
33
|
+
> [!IMPORTANT]
|
|
34
|
+
> **Slothlet v3.0 is a major release - the biggest since v2.0.**
|
|
35
|
+
|
|
36
|
+
v3 rebuilds Slothlet from the inside out with a **Unified Wrapper architecture** that delivers consistent, inspectable, hook-intercepted API proxies across every loading mode. On top of this foundation comes a redesigned hook system with three-phase subset ordering, per-request context isolation improvements, a full internationalization layer, background materialization with progress tracking, granular API mutation controls, collision modes for runtime API management, and lifecycle events for every stage of the module lifecycle.
|
|
37
|
+
|
|
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
|
+
|
|
40
|
+
### What's New in v3.0
|
|
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)**
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
36
56
|
## ✨ What's New
|
|
37
57
|
|
|
38
|
-
### Latest:
|
|
58
|
+
### Latest: v3.0.0 (February 2026)
|
|
39
59
|
|
|
40
|
-
- **
|
|
41
|
-
-
|
|
42
|
-
-
|
|
60
|
+
- **Unified Wrapper Architecture** - consistent proxy surface for all modes and operations
|
|
61
|
+
- **Hook System Redesigned** - `hook:` config key, `api.slothlet.hook.*`, three-phase subset ordering
|
|
62
|
+
- **Full i18n Support** - 9 languages for all error and debug messages
|
|
63
|
+
- **Background Materialization** - pre-load lazy modules without blocking startup
|
|
64
|
+
- **Lifecycle Event System** - `api.slothlet.lifecycle.on/off()` for module lifecycle events
|
|
65
|
+
- **Collision Modes** - fine-grained control over API namespace conflicts
|
|
66
|
+
- **Mutation Controls** - per-operation access restrictions for API mutations
|
|
67
|
+
- [View full v3.0 Changelog](./docs/changelog/v3.0.md)
|
|
43
68
|
|
|
44
69
|
### Recent Releases
|
|
45
70
|
|
|
46
|
-
- **v2.
|
|
47
|
-
- **v2.
|
|
48
|
-
- **v2.
|
|
49
|
-
- **v2.
|
|
50
|
-
|
|
51
|
-
|
|
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))
|
|
71
|
+
- **v2.11.0** - AddApi Special File Pattern (Rule 11), smart flattening enhancements ([Changelog](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.11.md))
|
|
72
|
+
- **v2.10.0** - Function metadata tagging and introspection capabilities ([Changelog](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.10.md))
|
|
73
|
+
- **v2.9** - Per-Request Context Isolation ([Changelog](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.9.md))
|
|
74
|
+
- **v2.7** - Hook system introduced ([Changelog](https://github.com/CLDMV/slothlet/blob/master/docs/changelog/v2.7.md))
|
|
75
|
+
|
|
76
|
+
📚 **For complete version history and detailed release notes, see [docs/changelog/](./docs/changelog/) folder.**
|
|
55
77
|
|
|
56
78
|
---
|
|
57
79
|
|
|
@@ -60,9 +82,9 @@ The name might suggest we're taking it easy, but don't be fooled. **Slothlet del
|
|
|
60
82
|
### 🎯 **Dual Loading Strategies**
|
|
61
83
|
|
|
62
84
|
- **Eager Loading**: Immediate loading for maximum performance in production environments
|
|
63
|
-
- **Lazy Loading**: Copy-left materialization with look-ahead proxies (
|
|
85
|
+
- **Lazy Loading**: Copy-left materialization with look-ahead proxies (2.2x faster startup, function calls equal to eager after materialization)
|
|
64
86
|
|
|
65
|
-
> [!IMPORTANT]
|
|
87
|
+
> [!IMPORTANT]
|
|
66
88
|
> **Function Call Patterns:**
|
|
67
89
|
>
|
|
68
90
|
> - **Lazy Mode**: ALL function calls must be awaited (`await api.math.add(2, 3)`) due to materialization process
|
|
@@ -70,30 +92,44 @@ The name might suggest we're taking it easy, but don't be fooled. **Slothlet del
|
|
|
70
92
|
|
|
71
93
|
### ⚡ Performance Excellence
|
|
72
94
|
|
|
73
|
-
- **2.
|
|
74
|
-
- **
|
|
75
|
-
- **Copy-left materialization**: Once loaded, modules stay materialized
|
|
95
|
+
- **Startup Performance**: 2.2x faster startup in lazy mode (15.41ms vs 34.28ms)
|
|
96
|
+
- **Runtime Performance**: Function calls essentially equal between modes (9.99μs lazy vs 9.46μs eager - within 6% measurement noise)
|
|
97
|
+
- **Copy-left materialization**: Once loaded, modules stay materialized - no re-processing overhead
|
|
76
98
|
- **Zero dependencies**: Pure Node.js implementation
|
|
99
|
+
- **Memory efficiency**: Lazy mode loads modules on-demand, eager mode optimizes for predictable behavior
|
|
100
|
+
|
|
101
|
+
**Mode Selection Guide:**
|
|
102
|
+
|
|
103
|
+
- **Eager Mode**: Best for production environments with maximum runtime performance and predictable behavior
|
|
104
|
+
- **Lazy Mode**: Best for development and applications with large APIs where startup time matters
|
|
77
105
|
|
|
78
|
-
📊 **For comprehensive performance
|
|
106
|
+
📊 **For comprehensive performance benchmarks and analysis, see [docs/PERFORMANCE.md](https://github.com/CLDMV/slothlet/blob/master/docs/PERFORMANCE.md)**
|
|
79
107
|
|
|
80
|
-
### 🎣 **Hook System**
|
|
108
|
+
### 🎣 **Hook System** _(redesigned in v3)_
|
|
81
109
|
|
|
82
|
-
Powerful function interceptor system with 4 hook types:
|
|
110
|
+
Powerful function interceptor system with 4 hook types and three-phase subset ordering:
|
|
83
111
|
|
|
84
|
-
- **`before`** - Modify arguments or cancel execution
|
|
112
|
+
- **`before`** - Modify arguments or cancel execution (**must be synchronous**)
|
|
85
113
|
- **`after`** - Transform return values
|
|
86
|
-
- **`always`** - Observe final results (read-only)
|
|
114
|
+
- **`always`** - Observe final results (read-only; fires even on short-circuit)
|
|
87
115
|
- **`error`** - Monitor and handle errors with detailed source tracking
|
|
88
116
|
|
|
89
|
-
Pattern matching, priority control, runtime enable/disable, and short-circuit support included.
|
|
117
|
+
Each hook type supports three ordered execution **subsets**: `"before"` → `"primary"` (default) → `"after"`. Pattern matching, priority control, runtime enable/disable, and short-circuit support included.
|
|
90
118
|
|
|
91
119
|
🎣 **For complete hook system documentation, see [docs/HOOKS.md](https://github.com/CLDMV/slothlet/blob/master/docs/HOOKS.md)**
|
|
92
120
|
|
|
121
|
+
### 🌍 **Full Internationalization** _(new in v3)_
|
|
122
|
+
|
|
123
|
+
All error messages and debug output are translated. Supported languages:
|
|
124
|
+
English · Spanish · French · German · Portuguese · Italian · Japanese · Chinese (Simplified) · Korean
|
|
125
|
+
|
|
126
|
+
Configure via `i18n: { language: "es" }` in your slothlet config.
|
|
127
|
+
|
|
93
128
|
### 🔄 **Context Propagation**
|
|
94
129
|
|
|
95
130
|
Automatic context preservation across all asynchronous boundaries:
|
|
96
131
|
|
|
132
|
+
- **Per-request isolation**: `api.slothlet.context.run(ctx, fn)` and `api.slothlet.context.scope(ctx)`
|
|
97
133
|
- **EventEmitter propagation**: Context maintained across all event callbacks
|
|
98
134
|
- **Class instance propagation**: Context preserved in class method calls
|
|
99
135
|
- **Zero configuration**: Works automatically with TCP servers, HTTP servers, and custom EventEmitters
|
|
@@ -104,16 +140,16 @@ Automatic context preservation across all asynchronous boundaries:
|
|
|
104
140
|
|
|
105
141
|
- **Intelligent Flattening**: Clean APIs with automatic structure optimization (`math/math.mjs` → `api.math`)
|
|
106
142
|
- **Smart Naming**: Preserves original capitalization (`auto-ip.mjs` with `autoIP` → `api.autoIP`)
|
|
107
|
-
- **Advanced Sanitization**: Custom naming rules with glob and boundary patterns
|
|
143
|
+
- **Advanced Sanitization**: Custom naming rules with glob and boundary patterns; `api.slothlet.sanitize()` at runtime
|
|
108
144
|
- **Hybrid Exports**: Support for callable APIs with methods, default + named exports
|
|
109
145
|
|
|
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)**
|
|
146
|
+
🏗️ **For module structure examples, see [docs/MODULE-STRUCTURE.md](https://github.com/CLDMV/slothlet/blob/master/docs/MODULE-STRUCTURE.md)**
|
|
147
|
+
📐 **For API flattening rules, see [docs/API-RULES/API-FLATTENING.md](https://github.com/CLDMV/slothlet/blob/master/docs/API-RULES/API-FLATTENING.md)**
|
|
112
148
|
|
|
113
|
-
### 🔗 **
|
|
149
|
+
### 🔗 **Runtime & Context System**
|
|
114
150
|
|
|
115
|
-
- **
|
|
116
|
-
- **
|
|
151
|
+
- **Context Isolation**: Automatic per-request isolation using AsyncLocalStorage (default); switchable to live-bindings mode via `runtime: "live"` config option
|
|
152
|
+
- **Cross-Module Access**: `self` and `context` always available inside API modules via `@cldmv/slothlet/runtime`
|
|
117
153
|
- **Mixed Module Support**: Seamlessly blend ESM and CommonJS modules
|
|
118
154
|
- **Copy-Left Preservation**: Materialized functions stay materialized
|
|
119
155
|
|
|
@@ -122,6 +158,7 @@ Automatic context preservation across all asynchronous boundaries:
|
|
|
122
158
|
- **TypeScript-Friendly**: Comprehensive JSDoc annotations with auto-generated declarations
|
|
123
159
|
- **Configurable Debug**: Detailed logging via CLI flags or environment variables
|
|
124
160
|
- **Multiple Instances**: Parameter-based isolation for complex applications
|
|
161
|
+
- **Inspectable APIs**: `console.log(api.math)` now shows real module contents (v3+)
|
|
125
162
|
- **Development Checks**: Built-in environment detection with silent production behavior
|
|
126
163
|
|
|
127
164
|
---
|
|
@@ -130,7 +167,8 @@ Automatic context preservation across all asynchronous boundaries:
|
|
|
130
167
|
|
|
131
168
|
### Requirements
|
|
132
169
|
|
|
133
|
-
- **Node.js v16.
|
|
170
|
+
- **Node.js v16.20.2 or higher** (required for stack trace API fixes used in path resolution)
|
|
171
|
+
- Node.js 16.4–16.19 has a stack trace regression. For these versions, use slothlet 2.10.0: `npm install @cldmv/slothlet@2.10.0`
|
|
134
172
|
|
|
135
173
|
### Install
|
|
136
174
|
|
|
@@ -167,7 +205,6 @@ const cjsResult = api.mathCjs.divide(10, 2);
|
|
|
167
205
|
```javascript
|
|
168
206
|
const slothlet = require("@cldmv/slothlet");
|
|
169
207
|
|
|
170
|
-
// Same usage pattern works with CommonJS
|
|
171
208
|
const api = await slothlet({
|
|
172
209
|
dir: "./api",
|
|
173
210
|
context: { env: "production" }
|
|
@@ -184,7 +221,7 @@ import slothlet from "@cldmv/slothlet";
|
|
|
184
221
|
|
|
185
222
|
// Lazy mode with copy-left materialization
|
|
186
223
|
const api = await slothlet({
|
|
187
|
-
mode: "lazy",
|
|
224
|
+
mode: "lazy",
|
|
188
225
|
dir: "./api",
|
|
189
226
|
apiDepth: 3
|
|
190
227
|
});
|
|
@@ -196,151 +233,104 @@ const result1 = await api.math.add(2, 3);
|
|
|
196
233
|
const result2 = await api.math.add(5, 7);
|
|
197
234
|
```
|
|
198
235
|
|
|
199
|
-
### Hook System Example
|
|
236
|
+
### Hook System Example _(v3 API)_
|
|
200
237
|
|
|
201
238
|
```javascript
|
|
202
239
|
import slothlet from "@cldmv/slothlet";
|
|
203
240
|
|
|
204
241
|
const api = await slothlet({
|
|
205
242
|
dir: "./api",
|
|
206
|
-
|
|
243
|
+
hook: true // Enable hooks - note: "hook" singular (v3)
|
|
207
244
|
});
|
|
208
245
|
|
|
209
246
|
// Before hook: Modify arguments
|
|
210
|
-
api.
|
|
211
|
-
"
|
|
212
|
-
"before",
|
|
247
|
+
api.slothlet.hook.on(
|
|
248
|
+
"before:math.add",
|
|
213
249
|
({ path, args }) => {
|
|
214
250
|
console.log(`Calling ${path} with args:`, args);
|
|
215
|
-
return [args[0] * 2, args[1] * 2];
|
|
251
|
+
return [args[0] * 2, args[1] * 2]; // Return array to replace arguments
|
|
216
252
|
},
|
|
217
|
-
{
|
|
253
|
+
{ id: "double-args", priority: 100 }
|
|
218
254
|
);
|
|
219
255
|
|
|
220
256
|
// After hook: Transform result
|
|
221
|
-
api.
|
|
222
|
-
"
|
|
223
|
-
"after",
|
|
257
|
+
api.slothlet.hook.on(
|
|
258
|
+
"after:math.*",
|
|
224
259
|
({ path, result }) => {
|
|
225
260
|
console.log(`${path} returned:`, result);
|
|
226
261
|
return result * 10;
|
|
227
262
|
},
|
|
228
|
-
{
|
|
263
|
+
{ id: "scale-result" }
|
|
229
264
|
);
|
|
230
265
|
|
|
231
|
-
// Always hook: Observe final result
|
|
232
|
-
api.
|
|
233
|
-
"
|
|
234
|
-
"always",
|
|
266
|
+
// Always hook: Observe final result (read-only)
|
|
267
|
+
api.slothlet.hook.on(
|
|
268
|
+
"always:**",
|
|
235
269
|
({ path, result, hasError }) => {
|
|
236
270
|
console.log(hasError ? `${path} failed` : `${path} succeeded`);
|
|
237
271
|
},
|
|
238
|
-
{
|
|
272
|
+
{ id: "logger" }
|
|
239
273
|
);
|
|
240
274
|
|
|
241
275
|
// Error hook: Monitor errors with source tracking
|
|
242
|
-
api.
|
|
243
|
-
"error
|
|
244
|
-
"error",
|
|
276
|
+
api.slothlet.hook.on(
|
|
277
|
+
"error:**",
|
|
245
278
|
({ path, error, source }) => {
|
|
246
279
|
console.error(`Error in ${path}:`, error.message);
|
|
247
|
-
console.error(`Source: ${source.type}`); // 'before'
|
|
280
|
+
console.error(`Source: ${source.type}`); // 'before' | 'after' | 'function' | 'always'
|
|
248
281
|
},
|
|
249
|
-
{
|
|
282
|
+
{ id: "error-monitor" }
|
|
250
283
|
);
|
|
251
284
|
|
|
252
285
|
// Call function - hooks execute automatically
|
|
253
286
|
const result = await api.math.add(2, 3);
|
|
254
287
|
```
|
|
255
288
|
|
|
256
|
-
### Dynamic API Extension
|
|
257
|
-
|
|
258
|
-
Load additional modules at runtime and extend your API dynamically:
|
|
289
|
+
### Dynamic API Extension _(v3 API)_
|
|
259
290
|
|
|
260
291
|
```javascript
|
|
261
292
|
import slothlet from "@cldmv/slothlet";
|
|
262
293
|
|
|
263
294
|
const api = await slothlet({ dir: "./api" });
|
|
264
295
|
|
|
265
|
-
// Add
|
|
266
|
-
await api.
|
|
296
|
+
// Add modules at runtime
|
|
297
|
+
await api.slothlet.api.add("plugins", "./plugins-folder");
|
|
267
298
|
api.plugins.myPlugin();
|
|
268
299
|
|
|
269
300
|
// Create nested API structures
|
|
270
|
-
await api.
|
|
301
|
+
await api.slothlet.api.add("runtime.plugins", "./more-plugins");
|
|
271
302
|
api.runtime.plugins.loader();
|
|
272
303
|
|
|
273
304
|
// Add with metadata for security/authorization
|
|
274
|
-
await api.
|
|
305
|
+
await api.slothlet.api.add("plugins.trusted", "./trusted-plugins", {
|
|
275
306
|
trusted: true,
|
|
276
|
-
permissions: ["read", "write", "admin"]
|
|
277
|
-
version: "1.0.0"
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
await api.addApi("plugins.external", "./third-party", {
|
|
281
|
-
trusted: false,
|
|
282
|
-
permissions: ["read"]
|
|
307
|
+
permissions: ["read", "write", "admin"]
|
|
283
308
|
});
|
|
284
309
|
|
|
285
|
-
//
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
console.log(meta.permissions); // ["read", "write", "admin"]
|
|
310
|
+
// Remove and reload
|
|
311
|
+
await api.slothlet.api.remove("oldModule");
|
|
312
|
+
await api.slothlet.api.reload("database.*");
|
|
289
313
|
```
|
|
290
314
|
|
|
291
|
-
**Security & Authorization with metadataAPI:**
|
|
292
|
-
|
|
293
|
-
```javascript
|
|
294
|
-
// Inside your modules, use metadataAPI for runtime introspection
|
|
295
|
-
import { metadataAPI } from "@cldmv/slothlet/runtime";
|
|
296
|
-
|
|
297
|
-
export async function sensitiveOperation() {
|
|
298
|
-
// Check caller's metadata
|
|
299
|
-
const caller = await metadataAPI.caller();
|
|
300
|
-
|
|
301
|
-
if (!caller?.trusted) {
|
|
302
|
-
throw new Error("Unauthorized: Caller is not trusted");
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
if (!caller.permissions.includes("admin")) {
|
|
306
|
-
throw new Error("Unauthorized: Admin permission required");
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// Proceed with secure operation
|
|
310
|
-
return "Success";
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// Get metadata by path
|
|
314
|
-
const meta = await metadataAPI.get("plugins.trusted.someFunc");
|
|
315
|
-
|
|
316
|
-
// Get current function's metadata
|
|
317
|
-
const self = await metadataAPI.self();
|
|
318
|
-
console.log("My version:", self.version);
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
🔒 **For complete metadata system documentation, see [docs/METADATA.md](https://github.com/CLDMV/slothlet/blob/master/docs/METADATA.md)**
|
|
322
|
-
|
|
323
315
|
---
|
|
324
316
|
|
|
325
317
|
## 📚 Configuration Options
|
|
326
318
|
|
|
327
|
-
| Option
|
|
328
|
-
|
|
|
329
|
-
| `dir`
|
|
330
|
-
| `mode`
|
|
331
|
-
| `
|
|
332
|
-
| `
|
|
333
|
-
| `
|
|
334
|
-
| `
|
|
335
|
-
| `
|
|
336
|
-
| `
|
|
337
|
-
| `
|
|
338
|
-
| `
|
|
339
|
-
| `
|
|
340
|
-
| `
|
|
341
|
-
| `
|
|
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)**
|
|
319
|
+
| Option | Type | Default | Description |
|
|
320
|
+
| ------------------------- | --------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
321
|
+
| `dir` | `string` | `"api"` | Directory to load API modules from (absolute or relative path) |
|
|
322
|
+
| `mode` | `string` | `"eager"` | Loading mode - `"lazy"` for on-demand loading, `"eager"` for immediate loading |
|
|
323
|
+
| `runtime` | `string` | `"async"` | Runtime binding system: `"async"` for AsyncLocalStorage (default), `"live"` for live-bindings |
|
|
324
|
+
| `apiDepth` | `number` | `Infinity` | Directory traversal depth - `0` for root only, `Infinity` for all levels |
|
|
325
|
+
| `debug` | `boolean` | `false` | Enable verbose logging (also via `--slothletdebug` flag or `SLOTHLET_DEBUG=true` env var) |
|
|
326
|
+
| `context` | `object` | `{}` | Context data injected into live-binding (available via `import { context } from "@cldmv/slothlet/runtime"`) |
|
|
327
|
+
| `reference` | `object` | `{}` | Reference object merged into API root level |
|
|
328
|
+
| `sanitize` | `object` | `{}` | Advanced filename-to-API transformation control with `lowerFirst`, `preserveAllUpper`, `preserveAllLower`, and `rules` (supports exact matches, glob patterns `*json*`, and boundary patterns `**url**`) |
|
|
329
|
+
| `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`** |
|
|
330
|
+
| `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 |
|
|
331
|
+
| `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()` |
|
|
332
|
+
| `api.mutations` | `object` | all `true` | Per-operation mutation controls: `{ add: true, remove: true, reload: true }` - set any to `false` to disable |
|
|
333
|
+
| `i18n` | `object` | `{}` | Internationalization settings: `{ language: "en" }` - supported: `en`, `es`, `fr`, `de`, `pt`, `it`, `ja`, `zh`, `ko` |
|
|
344
334
|
|
|
345
335
|
---
|
|
346
336
|
|
|
@@ -387,9 +377,9 @@ flowchart TD
|
|
|
387
377
|
direction TB
|
|
388
378
|
LAZYCALL0 --> LAZYCALL2
|
|
389
379
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
380
|
+
LAZYCALL0@{ shape: rounded, label: "📞 First call" }
|
|
381
|
+
LAZYCALL1@{ shape: rounded, label: "🔁 Sequential calls" }
|
|
382
|
+
LAZYCALL2@{ shape: rounded, label: "🧩 Materialize" }
|
|
393
383
|
end
|
|
394
384
|
|
|
395
385
|
EAGER --> READYTOUSE
|
|
@@ -403,24 +393,23 @@ flowchart TD
|
|
|
403
393
|
LAZYCALL1 --> MATERIALIZEDFUNCTION
|
|
404
394
|
LAZYCALL2 --> MATERIALIZEDFUNCTION
|
|
405
395
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
396
|
+
READYTOUSE@{ shape: rounded, label: "🎯 Ready to Use" }
|
|
397
|
+
MATERIALIZEDFUNCTION@{ shape: rounded, label: "✅ Materialized method/property" }
|
|
398
|
+
CALL@{ shape: trap-b, label: "📞 Call" }
|
|
409
399
|
|
|
410
|
-
%% Notes as unattached nodes with braces shape
|
|
411
400
|
subgraph ALWAYS ["✨ Extras Always On"]
|
|
412
401
|
direction TB
|
|
413
402
|
ALWAYS0 ~~~ ALWAYS1
|
|
414
403
|
ALWAYS1 ~~~ ALWAYS2
|
|
415
404
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
405
|
+
ALWAYS0@{ shape: rounded, label: "🔗 Live Bindings ALS<br/>Per-instance context isolation" }
|
|
406
|
+
ALWAYS1@{ shape: rounded, label: "🏷️ Smart Naming & Flattening<br/>Multiple rules for clean APIs" }
|
|
407
|
+
ALWAYS2@{ shape: rounded, label: "🔄 Mixed Module Support<br/>Seamlessly mix .mjs and .cjs" }
|
|
419
408
|
end
|
|
420
409
|
|
|
421
410
|
MODULEFOLDERS@{ shape: st-rect, label: "📁 Modules Folder<br/>.mjs and/or .cjs files<br/>math.mjs, string.cjs, async.mjs" }
|
|
422
|
-
|
|
423
|
-
|
|
411
|
+
SLOTHLET@{ shape: rounded, label: "🔧 Call slothlet(options)" }
|
|
412
|
+
CHOOSEMODE@{ shape: diamond, label: "Choose Mode<br/>in options" }
|
|
424
413
|
|
|
425
414
|
style EAGER0 stroke:#9BC66B,color:#9BC66B,opacity:0.5
|
|
426
415
|
style EAGER1 stroke:#9BC66B,color:#9BC66B,opacity:0.5
|
|
@@ -434,7 +423,6 @@ flowchart TD
|
|
|
434
423
|
style LAZY4 stroke:#9BC66B,color:#9BC66B,opacity:0.5
|
|
435
424
|
style LAZY5 stroke:#9BC66B,color:#9BC66B,opacity:0.5
|
|
436
425
|
|
|
437
|
-
%% Slothlet brand colors - #9BC66B primary on dark theme
|
|
438
426
|
style MODULEFOLDERS fill:#1a1a1a,stroke:#9BC66B,stroke-width:2px,color:#9BC66B,opacity:0.5
|
|
439
427
|
style SLOTHLET fill:#1a1a1a,stroke:#9BC66B,stroke-width:2px,color:#9BC66B,opacity:0.5
|
|
440
428
|
style CHOOSEMODE fill:#1a1a1a,stroke:#9BC66B,stroke-width:2px,color:#9BC66B,opacity:0.5
|
|
@@ -442,24 +430,20 @@ flowchart TD
|
|
|
442
430
|
style CALL fill:#1a1a1a,stroke:#9BC66B,stroke-width:2px,color:#9BC66B,opacity:0.5
|
|
443
431
|
style MATERIALIZEDFUNCTION fill:#1a1a1a,stroke:#9BC66B,stroke-width:2px,color:#9BC66B,opacity:0.5
|
|
444
432
|
|
|
445
|
-
%% Eager mode - primary green
|
|
446
433
|
style EAGER fill:#0d1a0d,stroke:#9BC66B,stroke-width:3px,color:#9BC66B,opacity:0.5
|
|
447
434
|
style EAGERCALL fill:#0d1a0d,stroke:#9BC66B,stroke-width:2px,color:#9BC66B,opacity:0.5
|
|
448
435
|
|
|
449
|
-
%% Lazy mode - lighter green tint
|
|
450
436
|
style LAZY fill:#0d1a0d,stroke:#B8D982,stroke-width:3px,color:#B8D982,opacity:0.5
|
|
451
437
|
style LAZYCALL fill:#0d1a0d,stroke:#B8D982,stroke-width:2px,color:#B8D982,opacity:0.5
|
|
452
438
|
style LAZYCALL0 fill:#1a1a1a,stroke:#B8D982,stroke-width:2px,color:#B8D982,opacity:0.5
|
|
453
439
|
style LAZYCALL1 fill:#1a1a1a,stroke:#B8D982,stroke-width:2px,color:#B8D982,opacity:0.5
|
|
454
440
|
style LAZYCALL2 fill:#1a1a1a,stroke:#B8D982,stroke-width:2px,color:#B8D982,opacity:0.5
|
|
455
441
|
|
|
456
|
-
%% Always available - accent green
|
|
457
442
|
style ALWAYS fill:#0d1a0d,stroke:#7FA94F,stroke-width:3px,color:#7FA94F,opacity:0.5
|
|
458
443
|
style ALWAYS0 fill:#1a1a1a,stroke:#7FA94F,stroke-width:1px,color:#7FA94F,opacity:0.5
|
|
459
444
|
style ALWAYS1 fill:#1a1a1a,stroke:#7FA94F,stroke-width:1px,color:#7FA94F,opacity:0.5
|
|
460
445
|
style ALWAYS2 fill:#1a1a1a,stroke:#7FA94F,stroke-width:1px,color:#7FA94F,opacity:0.5
|
|
461
446
|
|
|
462
|
-
%% Arrow styling
|
|
463
447
|
linkStyle default stroke:#9BC66B,stroke-width:3px,opacity:0.5
|
|
464
448
|
linkStyle 4,5,6,7,8,18,19 stroke-width:0px
|
|
465
449
|
```
|
|
@@ -473,7 +457,7 @@ flowchart TD
|
|
|
473
457
|
**Best for:** Production environments, maximum runtime performance, predictable behavior
|
|
474
458
|
|
|
475
459
|
```javascript
|
|
476
|
-
const api = await slothlet({ dir: "./api" }); //
|
|
460
|
+
const api = await slothlet({ dir: "./api" }); // mode: "eager" by default
|
|
477
461
|
|
|
478
462
|
// Functions behave as originally defined
|
|
479
463
|
const result = api.math.add(2, 3); // Sync - no await needed
|
|
@@ -482,7 +466,7 @@ const asyncResult = await api.async.processData({ data: "test" }); // Async need
|
|
|
482
466
|
|
|
483
467
|
**Benefits:**
|
|
484
468
|
|
|
485
|
-
- ✅
|
|
469
|
+
- ✅ Fast function calls (9.46μs average - within 6% of lazy mode)
|
|
486
470
|
- ✅ Predictable performance (no materialization delays)
|
|
487
471
|
- ✅ Functions behave exactly as originally defined
|
|
488
472
|
|
|
@@ -494,24 +478,414 @@ const asyncResult = await api.async.processData({ data: "test" }); // Async need
|
|
|
494
478
|
const api = await slothlet({ mode: "lazy", dir: "./api" });
|
|
495
479
|
|
|
496
480
|
// ALL calls must be awaited (materialization process)
|
|
497
|
-
const result1 = await api.math.add(2, 3); // First: ~
|
|
498
|
-
const result2 = await api.math.add(5, 7); // Subsequent:
|
|
481
|
+
const result1 = await api.math.add(2, 3); // First: ~538μs avg (materialization)
|
|
482
|
+
const result2 = await api.math.add(5, 7); // Subsequent: ~10μs (materialized)
|
|
499
483
|
```
|
|
500
484
|
|
|
501
485
|
**Benefits:**
|
|
502
486
|
|
|
503
|
-
- ✅ 2.
|
|
504
|
-
- ✅
|
|
487
|
+
- ✅ 2.2x faster startup (15.41ms vs 34.28ms)
|
|
488
|
+
- ✅ Equal function call performance (9.99μs vs 9.46μs eager - within 6% measurement noise)
|
|
505
489
|
- ✅ Memory efficient (loads only what you use)
|
|
506
490
|
- ✅ Copy-left optimization (once loaded, stays loaded)
|
|
507
491
|
|
|
508
|
-
|
|
492
|
+
### Lazy Mode with Background Materialization _(new in v3)_
|
|
493
|
+
|
|
494
|
+
**Best for:** Lazy startup performance with eager runtime performance - pre-warm everything in the background
|
|
495
|
+
|
|
496
|
+
```javascript
|
|
497
|
+
const api = await slothlet({
|
|
498
|
+
mode: "lazy",
|
|
499
|
+
dir: "./api",
|
|
500
|
+
backgroundMaterialize: true
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
// Subscribe to completion
|
|
504
|
+
api.slothlet.lifecycle.on("materialized:complete", (data) => {
|
|
505
|
+
console.log(`${data.total} modules materialized`);
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
// Or await all modules to be ready before serving traffic
|
|
509
|
+
await api.slothlet.materialize.wait();
|
|
510
|
+
|
|
511
|
+
// Check progress at any time
|
|
512
|
+
const stats = api.slothlet.materialize.get();
|
|
513
|
+
// { total, materialized, remaining, percentage }
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
> [!TIP]
|
|
509
517
|
> **Choose your strategy:**
|
|
510
518
|
>
|
|
511
|
-
> - **Startup-sensitive?** → Lazy mode (2.
|
|
512
|
-
> - **Call-intensive?** →
|
|
519
|
+
> - **Startup-sensitive?** → Lazy mode (2.2x faster startup)
|
|
520
|
+
> - **Call-intensive?** → Either mode (function calls essentially equal after materialization)
|
|
513
521
|
> - **Need predictability?** → Eager mode (no materialization delays)
|
|
514
522
|
> - **Large API, use subset?** → Lazy mode (memory efficient)
|
|
523
|
+
> - **Want lazy startup + eager runtime?** → Lazy mode + `backgroundMaterialize: true`
|
|
524
|
+
|
|
525
|
+
---
|
|
526
|
+
|
|
527
|
+
## 🎣 Hook System _(v3)_
|
|
528
|
+
|
|
529
|
+
### Hook Configuration
|
|
530
|
+
|
|
531
|
+
```js
|
|
532
|
+
// Simple enable (default pattern "**")
|
|
533
|
+
const api = await slothlet({ dir: "./api", hook: true });
|
|
534
|
+
|
|
535
|
+
// Enable with default pattern filter
|
|
536
|
+
const api = await slothlet({ dir: "./api", hook: "database.*" });
|
|
537
|
+
|
|
538
|
+
// Full configuration
|
|
539
|
+
const api = await slothlet({
|
|
540
|
+
dir: "./api",
|
|
541
|
+
hook: {
|
|
542
|
+
enabled: true,
|
|
543
|
+
pattern: "**",
|
|
544
|
+
suppressErrors: false // true = errors suppressed (returns undefined instead of throwing)
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
### Hook Types
|
|
550
|
+
|
|
551
|
+
- **`before`** - Executes before the function. Can modify arguments or short-circuit. **Must be synchronous.**
|
|
552
|
+
- **`after`** - Executes after successful completion. Can transform the return value.
|
|
553
|
+
- **`always`** - Read-only observer. Always executes (even on short-circuit). Return value ignored.
|
|
554
|
+
- **`error`** - Executes only when an error occurs. Receives error with source tracking.
|
|
555
|
+
|
|
556
|
+
### Basic Usage
|
|
557
|
+
|
|
558
|
+
The `hook.on(typePattern, handler, options)` signature uses `"type:pattern"` as the first argument:
|
|
559
|
+
|
|
560
|
+
```js
|
|
561
|
+
// Before hook - modify arguments
|
|
562
|
+
api.slothlet.hook.on(
|
|
563
|
+
"before:math.add",
|
|
564
|
+
({ path, args }) => {
|
|
565
|
+
return [args[0] * 2, args[1] * 2]; // Return array to replace arguments
|
|
566
|
+
// Return non-array non-undefined to short-circuit (skip function)
|
|
567
|
+
// Return undefined to continue with original args
|
|
568
|
+
},
|
|
569
|
+
{ id: "double-args", priority: 100 }
|
|
570
|
+
);
|
|
571
|
+
|
|
572
|
+
// After hook - transform result
|
|
573
|
+
api.slothlet.hook.on(
|
|
574
|
+
"after:math.*",
|
|
575
|
+
({ path, result }) => {
|
|
576
|
+
return result * 10; // Return value replaces result; undefined = no change
|
|
577
|
+
},
|
|
578
|
+
{ id: "scale-result" }
|
|
579
|
+
);
|
|
580
|
+
|
|
581
|
+
// Always hook - observe (read-only)
|
|
582
|
+
api.slothlet.hook.on(
|
|
583
|
+
"always:**",
|
|
584
|
+
({ path, result, hasError, errors }) => {
|
|
585
|
+
if (hasError) console.error(`${path} failed:`, errors);
|
|
586
|
+
else console.log(`${path} returned:`, result);
|
|
587
|
+
// Return value is ignored
|
|
588
|
+
},
|
|
589
|
+
{ id: "logger" }
|
|
590
|
+
);
|
|
591
|
+
|
|
592
|
+
// Error hook - monitor failures
|
|
593
|
+
api.slothlet.hook.on(
|
|
594
|
+
"error:**",
|
|
595
|
+
({ path, error, source }) => {
|
|
596
|
+
// source.type: "before" | "after" | "always" | "function"
|
|
597
|
+
console.error(`Error in ${path} (from ${source.type}):`, error.message);
|
|
598
|
+
},
|
|
599
|
+
{ id: "error-monitor" }
|
|
600
|
+
);
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
### Hook Subsets _(new in v3)_
|
|
604
|
+
|
|
605
|
+
Each hook type has three ordered execution phases:
|
|
606
|
+
|
|
607
|
+
| Subset | Order | Typical use |
|
|
608
|
+
|---|---|---|
|
|
609
|
+
| `"before"` | First | Auth checks, security validation |
|
|
610
|
+
| `"primary"` | Middle (default) | Main hook logic |
|
|
611
|
+
| `"after"` | Last | Audit trails, cleanup |
|
|
612
|
+
|
|
613
|
+
```js
|
|
614
|
+
// Auth check runs first - always
|
|
615
|
+
api.slothlet.hook.on(
|
|
616
|
+
"before:protected.*",
|
|
617
|
+
({ ctx }) => { if (!ctx.user) throw new Error("Unauthorized"); },
|
|
618
|
+
{ id: "auth", subset: "before", priority: 2000 }
|
|
619
|
+
);
|
|
620
|
+
|
|
621
|
+
// Main validation logic - default subset
|
|
622
|
+
api.slothlet.hook.on(
|
|
623
|
+
"before:protected.*",
|
|
624
|
+
({ args }) => { /* validate */ },
|
|
625
|
+
{ id: "validate" } // subset: "primary" by default
|
|
626
|
+
);
|
|
627
|
+
|
|
628
|
+
// Audit log always runs last
|
|
629
|
+
api.slothlet.hook.on(
|
|
630
|
+
"after:protected.*",
|
|
631
|
+
({ path, result }) => { /* log */ },
|
|
632
|
+
{ id: "audit", subset: "after" }
|
|
633
|
+
);
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
### Pattern Matching
|
|
637
|
+
|
|
638
|
+
| Syntax | Description | Example |
|
|
639
|
+
|---|---|---|
|
|
640
|
+
| `exact.path` | Exact match | `"before:math.add"` |
|
|
641
|
+
| `namespace.*` | All functions in namespace | `"after:math.*"` |
|
|
642
|
+
| `*.funcName` | Function name across namespaces | `"always:*.add"` |
|
|
643
|
+
| `**` | All functions | `"error:**"` |
|
|
644
|
+
| `{a,b}` | Brace expansion | `"before:{math,utils}.*"` |
|
|
645
|
+
| `!pattern` | Negation | `"before:!internal.*"` |
|
|
646
|
+
|
|
647
|
+
### Hook Management
|
|
648
|
+
|
|
649
|
+
```js
|
|
650
|
+
// Remove by ID
|
|
651
|
+
api.slothlet.hook.remove({ id: "my-hook" });
|
|
652
|
+
api.slothlet.hook.off("my-hook"); // alias
|
|
653
|
+
|
|
654
|
+
// Remove by filter
|
|
655
|
+
api.slothlet.hook.remove({ type: "before", pattern: "math.*" });
|
|
656
|
+
|
|
657
|
+
// Remove all
|
|
658
|
+
api.slothlet.hook.clear();
|
|
659
|
+
|
|
660
|
+
// List hooks
|
|
661
|
+
const all = api.slothlet.hook.list();
|
|
662
|
+
const active = api.slothlet.hook.list({ enabled: true });
|
|
663
|
+
|
|
664
|
+
// Enable / disable without unregistering
|
|
665
|
+
api.slothlet.hook.disable();
|
|
666
|
+
api.slothlet.hook.disable({ pattern: "math.*" });
|
|
667
|
+
api.slothlet.hook.enable();
|
|
668
|
+
api.slothlet.hook.enable({ type: "before" });
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
---
|
|
672
|
+
|
|
673
|
+
## 🔄 Per-Request Context _(v3 API)_
|
|
674
|
+
|
|
675
|
+
```js
|
|
676
|
+
const api = await slothlet({
|
|
677
|
+
dir: "./api",
|
|
678
|
+
context: { appName: "MyApp", version: "3.0" }
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
// run() - execute a function inside a scoped context
|
|
682
|
+
await api.slothlet.context.run({ userId: "alice", role: "admin" }, async () => {
|
|
683
|
+
// Inside this scope: context = { appName, version, userId, role }
|
|
684
|
+
await api.database.query();
|
|
685
|
+
await api.audit.log();
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
// scope() - return a new API object with merged context
|
|
689
|
+
const scopedApi = api.slothlet.context.scope({ userId: "bob" });
|
|
690
|
+
await scopedApi.database.query(); // context includes userId: "bob"
|
|
691
|
+
|
|
692
|
+
// Deep merge strategy
|
|
693
|
+
await api.slothlet.context.run(
|
|
694
|
+
{ nested: { prop: "value" } },
|
|
695
|
+
handler,
|
|
696
|
+
{ mergeStrategy: "deep" }
|
|
697
|
+
);
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
### Automatic EventEmitter Context Propagation
|
|
701
|
+
|
|
702
|
+
Context propagates automatically through EventEmitter callbacks:
|
|
703
|
+
|
|
704
|
+
```js
|
|
705
|
+
import net from "net";
|
|
706
|
+
import { context } from "@cldmv/slothlet/runtime";
|
|
707
|
+
|
|
708
|
+
export const server = {
|
|
709
|
+
async start() {
|
|
710
|
+
const tcpServer = net.createServer((socket) => {
|
|
711
|
+
console.log(`User ${context.userId} connected`);
|
|
712
|
+
|
|
713
|
+
socket.on("data", (data) => {
|
|
714
|
+
// Context preserved in all nested callbacks
|
|
715
|
+
console.log(`Data from ${context.userId}: ${data}`);
|
|
716
|
+
});
|
|
717
|
+
});
|
|
718
|
+
tcpServer.listen(3000);
|
|
719
|
+
}
|
|
720
|
+
};
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
> 📖 See [`docs/CONTEXT-PROPAGATION.md`](docs/CONTEXT-PROPAGATION.md)
|
|
724
|
+
|
|
725
|
+
---
|
|
726
|
+
|
|
727
|
+
## 🏷️ Metadata System
|
|
728
|
+
|
|
729
|
+
Tag API paths with metadata for authorization, auditing, and security.
|
|
730
|
+
|
|
731
|
+
```js
|
|
732
|
+
// Attach metadata when loading
|
|
733
|
+
await api.slothlet.api.add("plugins/trusted", "./trusted-dir", {
|
|
734
|
+
metadata: { trusted: true, securityLevel: "high" }
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
// Set metadata at runtime
|
|
738
|
+
api.slothlet.metadata.set("plugins.trusted.someFunc", { version: 2 });
|
|
739
|
+
api.slothlet.metadata.setGlobal({ environment: "production" });
|
|
740
|
+
api.slothlet.metadata.setFor("plugins/trusted", { owner: "core-team" });
|
|
741
|
+
api.slothlet.metadata.remove("plugins.old.func");
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
🔒 **For complete metadata documentation, see [docs/METADATA.md](https://github.com/CLDMV/slothlet/blob/master/docs/METADATA.md)**
|
|
745
|
+
|
|
746
|
+
---
|
|
747
|
+
|
|
748
|
+
## 🔁 Hot Reload / Dynamic API Management _(v3 API)_
|
|
749
|
+
|
|
750
|
+
```js
|
|
751
|
+
// Add new modules at runtime
|
|
752
|
+
await api.slothlet.api.add("newModule", "./new-module-path");
|
|
753
|
+
await api.slothlet.api.add("plugins", "./plugins", { collision: "merge" });
|
|
754
|
+
|
|
755
|
+
// Remove modules
|
|
756
|
+
await api.slothlet.api.remove("oldModule");
|
|
757
|
+
|
|
758
|
+
// Reload specific path or all modules
|
|
759
|
+
await api.slothlet.api.reload("database.*");
|
|
760
|
+
await api.slothlet.api.reload("plugins.auth");
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
> **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.
|
|
764
|
+
|
|
765
|
+
### Collision Modes _(new in v3)_
|
|
766
|
+
|
|
767
|
+
Control what happens when a loaded path already exists:
|
|
768
|
+
|
|
769
|
+
```js
|
|
770
|
+
const api = await slothlet({
|
|
771
|
+
dir: "./api",
|
|
772
|
+
api: {
|
|
773
|
+
collision: {
|
|
774
|
+
initial: "merge", // During initial load()
|
|
775
|
+
api: "skip" // During api.slothlet.api.add()
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
});
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
| Mode | Behavior |
|
|
782
|
+
|---|---|
|
|
783
|
+
| `"overwrite"` | Replace existing (default) |
|
|
784
|
+
| `"merge"` | Deep-merge new into existing |
|
|
785
|
+
| `"skip"` | Keep existing, ignore new |
|
|
786
|
+
| `"throw"` | Throw an error on conflict |
|
|
787
|
+
|
|
788
|
+
### Mutation Controls _(new in v3)_
|
|
789
|
+
|
|
790
|
+
Restrict which API operations are permitted:
|
|
791
|
+
|
|
792
|
+
```js
|
|
793
|
+
const api = await slothlet({
|
|
794
|
+
dir: "./api",
|
|
795
|
+
api: {
|
|
796
|
+
mutations: {
|
|
797
|
+
add: true,
|
|
798
|
+
remove: false, // Prevent removal in production
|
|
799
|
+
reload: false // Prevent reload in production
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
});
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
---
|
|
806
|
+
|
|
807
|
+
## ⚡ Lifecycle Events _(new in v3)_
|
|
808
|
+
|
|
809
|
+
Subscribe to internal module lifecycle events:
|
|
810
|
+
|
|
811
|
+
```js
|
|
812
|
+
// Available events
|
|
813
|
+
api.slothlet.lifecycle.on("materialized:complete", (data) => {
|
|
814
|
+
console.log(`${data.total} modules materialized`);
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
api.slothlet.lifecycle.on("impl:created", (data) => {
|
|
818
|
+
console.log(`Module created at ${data.apiPath}`);
|
|
819
|
+
});
|
|
820
|
+
|
|
821
|
+
api.slothlet.lifecycle.on("impl:changed", (data) => {
|
|
822
|
+
console.log(`Module at ${data.apiPath} was reloaded`);
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
api.slothlet.lifecycle.on("impl:removed", (data) => {
|
|
826
|
+
console.log(`Module at ${data.apiPath} was removed`);
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
// Unsubscribe
|
|
830
|
+
const handler = (data) => console.log(data);
|
|
831
|
+
api.slothlet.lifecycle.on("impl:changed", handler);
|
|
832
|
+
api.slothlet.lifecycle.off("impl:changed", handler);
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
**Available events**: `"materialized:complete"`, `"impl:created"`, `"impl:changed"`, `"impl:removed"`
|
|
836
|
+
|
|
837
|
+
> [!NOTE]
|
|
838
|
+
> `api.slothlet.lifecycle` exposes **`on` and `off` only**. `emit`, `subscribe`, and `unsubscribe` are internal - they are not present on the public API object.
|
|
839
|
+
|
|
840
|
+
---
|
|
841
|
+
|
|
842
|
+
## 📁 File Organization Best Practices
|
|
843
|
+
|
|
844
|
+
### ✅ Clean Folder Structure
|
|
845
|
+
|
|
846
|
+
```text
|
|
847
|
+
api/
|
|
848
|
+
├── config.mjs → api.config.*
|
|
849
|
+
├── math/
|
|
850
|
+
│ └── math.mjs → api.math.* (flattened - filename matches folder)
|
|
851
|
+
├── util/
|
|
852
|
+
│ ├── util.mjs → api.util.* (flattened methods)
|
|
853
|
+
│ ├── extract.mjs → api.util.extract.*
|
|
854
|
+
│ └── controller.mjs → api.util.controller.*
|
|
855
|
+
├── nested/
|
|
856
|
+
│ └── date/
|
|
857
|
+
│ └── date.mjs → api.nested.date.*
|
|
858
|
+
└── multi/
|
|
859
|
+
├── alpha.mjs → api.multi.alpha.*
|
|
860
|
+
└── beta.mjs → api.multi.beta.*
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
### ✅ Naming Conventions
|
|
864
|
+
|
|
865
|
+
- **Filename matches folder** → Auto-flattening (`math/math.mjs` → `api.math.*`)
|
|
866
|
+
- **Different filename** → Nested structure preserved
|
|
867
|
+
- **Dash-separated names** → camelCase API (`auto-ip.mjs` → `api.autoIP`)
|
|
868
|
+
- **Function name preferred** → Original capitalization kept over sanitized form (see [Rule 9](docs/API-RULES.md))
|
|
869
|
+
|
|
870
|
+
### ✅ Use `self` for Cross-Module Access
|
|
871
|
+
|
|
872
|
+
API modules must never import each other directly. Use Slothlet's live-binding system instead:
|
|
873
|
+
|
|
874
|
+
```js
|
|
875
|
+
// ❌ WRONG - breaks lazy loading and context isolation
|
|
876
|
+
import { math } from "./math/math.mjs";
|
|
877
|
+
|
|
878
|
+
// ✅ CORRECT - live binding always reflects current runtime state
|
|
879
|
+
import { self, context } from "@cldmv/slothlet/runtime";
|
|
880
|
+
|
|
881
|
+
export const myModule = {
|
|
882
|
+
async processData(input) {
|
|
883
|
+
const mathResult = self.math.add(2, 3); // Cross-module call via runtime
|
|
884
|
+
console.log(`Caller: ${context.userId}`); // Per-request context
|
|
885
|
+
return `Processed: ${input}, Math: ${mathResult}`;
|
|
886
|
+
}
|
|
887
|
+
};
|
|
888
|
+
```
|
|
515
889
|
|
|
516
890
|
---
|
|
517
891
|
|
|
@@ -538,118 +912,75 @@ Key highlights:
|
|
|
538
912
|
|
|
539
913
|
### Core Documentation
|
|
540
914
|
|
|
541
|
-
- **[API Documentation](https://github.com/CLDMV/slothlet/blob/master/docs/generated/API.md)** - Complete API reference with examples and detailed parameter descriptions
|
|
542
915
|
- **[Performance Analysis](https://github.com/CLDMV/slothlet/blob/master/docs/PERFORMANCE.md)** - Detailed benchmarks and recommendations
|
|
916
|
+
- **[Agent Usage Guide](AGENT-USAGE.md)** - Guide for AI agents building Slothlet API folders
|
|
543
917
|
- **[Contributing Guide](CONTRIBUTING.md)** - How to contribute to the project
|
|
544
918
|
- **[Security Policy](SECURITY.md)** - Security guidelines and reporting
|
|
545
919
|
- **[Test Documentation](api_tests)** - Comprehensive test module examples
|
|
546
920
|
|
|
547
921
|
### Technical Guides
|
|
548
922
|
|
|
549
|
-
- **[
|
|
923
|
+
- **[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
|
|
924
|
+
- **[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
|
|
550
925
|
- **[Context Propagation](https://github.com/CLDMV/slothlet/blob/master/docs/CONTEXT-PROPAGATION.md)** - EventEmitter and class instance context preservation
|
|
551
926
|
- **[Metadata System](https://github.com/CLDMV/slothlet/blob/master/docs/METADATA.md)** - Function metadata tagging and runtime introspection for security, authorization, and auditing
|
|
552
927
|
- **[Module Structure](https://github.com/CLDMV/slothlet/blob/master/docs/MODULE-STRUCTURE.md)** - Comprehensive module organization patterns and examples
|
|
553
|
-
- **[
|
|
928
|
+
- **[Sanitization](https://github.com/CLDMV/slothlet/blob/master/docs/SANITIZATION.md)** - Property name sanitization rules
|
|
929
|
+
- **[Internationalization](https://github.com/CLDMV/slothlet/blob/master/docs/I18N.md)** - i18n support, language configuration, and available translations
|
|
554
930
|
|
|
555
931
|
### API Rules & Transformation
|
|
556
932
|
|
|
557
|
-
- **[API Rules](
|
|
558
|
-
- **[API Rules Conditions](
|
|
933
|
+
- **[API Rules](docs/API-RULES.md)** - All 13 API transformation rules with verified test examples
|
|
934
|
+
- **[API Rules Conditions](docs/API-RULES/API-RULES-CONDITIONS.md)** - Complete technical reference of all conditional statements that control API generation
|
|
935
|
+
- **[API Flattening](docs/API-RULES/API-FLATTENING.md)** - Flattening rules with decision tree and benefits
|
|
936
|
+
|
|
937
|
+
---
|
|
559
938
|
|
|
560
|
-
|
|
939
|
+
## 🌟 Migration from v2.x
|
|
561
940
|
|
|
562
|
-
|
|
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)
|
|
941
|
+
Upgrading from v2? See the **[Migration Guide](docs/MIGRATION.md)** for all breaking changes, full before/after code examples, a complete config diff, and a list of removed options.
|
|
572
942
|
|
|
573
943
|
---
|
|
574
944
|
|
|
575
945
|
## 🛡 Error Handling
|
|
576
946
|
|
|
577
|
-
|
|
578
|
-
> **Current Error Behavior**: Slothlet currently uses standard JavaScript error handling. Enhanced error handling with module suggestions is planned for v3.0.0 but not yet implemented.
|
|
579
|
-
|
|
580
|
-
**Current behavior:**
|
|
947
|
+
Slothlet v3 uses a rich `SlothletError` class with translated messages and contextual hints:
|
|
581
948
|
|
|
582
949
|
```javascript
|
|
583
950
|
try {
|
|
584
|
-
|
|
585
|
-
await api.nonexistent.method(); // Throws: "Cannot read properties of undefined (reading 'method')"
|
|
951
|
+
await api.slothlet.api.add("plugins", "./dir");
|
|
586
952
|
} catch (error) {
|
|
587
|
-
console.error(error.message); //
|
|
953
|
+
console.error(error.message); // Translated error message
|
|
954
|
+
console.error(error.hint); // Contextual hint for resolution
|
|
955
|
+
console.error(error.code); // Machine-readable error code
|
|
588
956
|
}
|
|
589
957
|
```
|
|
590
958
|
|
|
591
959
|
---
|
|
592
960
|
|
|
593
|
-
##
|
|
961
|
+
## 🏗️ Production & Development Modes
|
|
594
962
|
|
|
595
963
|
### Production Ready ✅
|
|
596
964
|
|
|
597
965
|
- **Eager Mode**: Stable, battle-tested, maximum performance
|
|
598
966
|
- **Lazy Mode**: Production-ready with copy-left optimization
|
|
599
|
-
- **
|
|
967
|
+
- **Background Materialization**: Lazy startup + eager runtime performance
|
|
600
968
|
- **Mixed Module Loading**: ESM/CJS interoperability fully supported
|
|
601
969
|
|
|
602
970
|
### Development Features 🛠️
|
|
603
971
|
|
|
972
|
+
- **Debug Mode**: Comprehensive i18n-translated logging via `--slothletdebug` flag or `SLOTHLET_DEBUG=true`
|
|
604
973
|
- **Development Check**: `devcheck.mjs` for environment validation
|
|
605
|
-
- **Debug Mode**: Comprehensive logging via `--slothletdebug` flag or `SLOTHLET_DEBUG=true`
|
|
606
|
-
- **Performance Monitoring**: Built-in timing and performance analysis
|
|
607
974
|
- **Source Detection**: Automatic `src/` vs `dist/` mode detection
|
|
608
|
-
|
|
609
|
-
### Experimental ⚠️
|
|
610
|
-
|
|
611
|
-
> [!WARNING]
|
|
612
|
-
> The following modes are in active development and not recommended for production use:
|
|
613
|
-
>
|
|
614
|
-
> - **Worker Mode**: Thread isolation (in development)
|
|
615
|
-
> - **Fork Mode**: Process isolation (in development)
|
|
616
|
-
> - **Child Mode**: Child process execution (in development)
|
|
617
|
-
> - **VM Mode**: Virtual machine context (in development)
|
|
618
|
-
|
|
619
|
-
---
|
|
620
|
-
|
|
621
|
-
## 🌟 Migration from v1.x
|
|
622
|
-
|
|
623
|
-
### Key Changes
|
|
624
|
-
|
|
625
|
-
1. **Import paths**: `@cldmv/slothlet` instead of specific file paths
|
|
626
|
-
2. **Configuration**: New options (`api_mode`, `context`, `reference`, `hooks`)
|
|
627
|
-
3. **Function names**: Enhanced preservation of original capitalization
|
|
628
|
-
4. **Module structure**: Mixed ESM/CJS support
|
|
629
|
-
5. **Live bindings**: Dual runtime system with AsyncLocalStorage and live-bindings options
|
|
630
|
-
6. **Automatic instances**: No more query strings or `withInstanceId()` methods
|
|
631
|
-
|
|
632
|
-
### Migration Steps
|
|
633
|
-
|
|
634
|
-
```javascript
|
|
635
|
-
// v1.3.x - Multiple instances required query strings or withInstanceId()
|
|
636
|
-
const api1 = await slothlet({ dir: "./api?instanceId=alice" });
|
|
637
|
-
const api2 = slothlet.withInstanceId("bob");
|
|
638
|
-
const bobApi = await api2({ dir: "./api" });
|
|
639
|
-
|
|
640
|
-
// v2.x - Automatic instance isolation (no query strings needed)
|
|
641
|
-
const api1 = await slothlet({ dir: "./api", context: { tenant: "alice" } });
|
|
642
|
-
const api2 = await slothlet({ dir: "./api", context: { tenant: "bob" } });
|
|
643
|
-
// Instances completely isolated with their own contexts
|
|
644
|
-
```
|
|
975
|
+
- **API Inspection**: `console.log(api.math)` shows real module contents (v3+)
|
|
645
976
|
|
|
646
977
|
---
|
|
647
978
|
|
|
648
979
|
## 🤝 Contributing
|
|
649
980
|
|
|
650
|
-
We welcome contributions!
|
|
981
|
+
We welcome contributions! Please:
|
|
651
982
|
|
|
652
|
-
1. **Review the code** in `src/lib
|
|
983
|
+
1. **Review the code** in `src/lib/` for implementation details
|
|
653
984
|
2. **Report issues** with detailed reproduction steps
|
|
654
985
|
3. **Submit pull requests** with comprehensive tests
|
|
655
986
|
4. **Provide feedback** on API design and performance
|
|
@@ -680,11 +1011,7 @@ Apache-2.0 © Shinrai / CLDMV
|
|
|
680
1011
|
|
|
681
1012
|
## 🙏 Acknowledgments
|
|
682
1013
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
**🎉 Welcome to the future of module loading with Slothlet!**
|
|
686
|
-
|
|
687
|
-
> _Where sophisticated architecture meets blazing performance - slothlet is anything but slow._
|
|
1014
|
+
To my wife and children - thank you for your patience, your encouragement, and the countless hours you gave me to build this. None of it would exist without your support.
|
|
688
1015
|
|
|
689
1016
|
<!-- [github release]: https://img.shields.io/github/v/release/CLDMV/slothlet?style=for-the-badge&logo=github&logoColor=white&labelColor=181717 -->
|
|
690
1017
|
<!-- [github_release_url]: https://github.com/CLDMV/slothlet/releases -->
|
|
@@ -693,10 +1020,6 @@ Slothlet v2.0 represents a complete architectural rewrite with enterprise-grade
|
|
|
693
1020
|
[npm_version_url]: https://www.npmjs.com/package/@cldmv/slothlet
|
|
694
1021
|
[last commit]: https://img.shields.io/github/last-commit/CLDMV/slothlet?style=for-the-badge&logo=github&logoColor=white&labelColor=181717
|
|
695
1022
|
[last_commit_url]: https://github.com/CLDMV/slothlet/commits
|
|
696
|
-
|
|
697
|
-
<!-- [release date]: https://img.shields.io/github/release-date/CLDMV/slothlet?style=for-the-badge&logo=github&logoColor=white&labelColor=181717 -->
|
|
698
|
-
<!-- [release_date_url]: https://github.com/CLDMV/slothlet/releases -->
|
|
699
|
-
|
|
700
1023
|
[npm last update]: https://img.shields.io/npm/last-update/%40cldmv%2Fslothlet?style=for-the-badge&logo=npm&logoColor=white&labelColor=CB3837
|
|
701
1024
|
[npm_last_update_url]: https://www.npmjs.com/package/@cldmv/slothlet
|
|
702
1025
|
[codefactor]: https://img.shields.io/codefactor/grade/github/CLDMV/slothlet?style=for-the-badge&logo=codefactor&logoColor=white&labelColor=F44A6A
|