@cldmv/slothlet
v3.14.0
Published
Slothlet: Modular API Loader for Node.js. Lazy mode dynamically loads API modules and submodules only when accessed, supporting both lazy and eager loading.
Maintainers
Keywords
Readme
@cldmv/slothlet
@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.
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.
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.
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.
🎉 Welcome to the future of module loading with Slothlet v3!
Where sophisticated architecture meets blazing performance - slothlet is anything but slow.
[!NOTE] 🚀 Production Ready Modes:
- Eager Mode: Fully stable and production-ready for immediate module loading
- Lazy Mode: Production-ready with advanced copy-left materialization and 2.2x faster startup (function calls within 6% of eager - essentially equal)
🎉 Introducing Slothlet v3.0
[!IMPORTANT] Slothlet v3.0 is a major release - the biggest since v2.0.
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.
Every feature has been hardened with a comprehensive test suite - over 5,300 tests across eager, lazy, CJS, ESM, TypeScript, and mixed module scenarios.
📋 See the full v3.0 changelog for the architecture rewrite, hook system redesign, i18n layer, background materialization, lifecycle events, collision modes, mutation controls, sanitization improvements, and context isolation upgrades.
✨ What's New
Latest: v3.14.0 (August 2026)
- Browser importmap resolves consumer-graph package exports (#297) —
generateBrowserAssetsnow emits the exactexportssubpath keys for the third-party packages your API leaves import, not just@cldmv/slothlet's own surface. Import maps do plain prefix substitution and never consult a package'sexports, so a redirected subpath (@scope/ext/errors→./src/lib/errors.mjs) previously 404'd in the browser and consumers hand-maintained allowlists. The generator now scans the API directory for the packages its leaves import, reads each package'sexports, and emits the exact redirected keys — flat entries, wildcard directories, and the browser/import/defaultcondition of conditional exports — served under a base derived fromslothletBase. A new exported primitive,collectPackageSpecifiers, does the package-agnostic collection;collectSlothletSpecifiersis unchanged. - View full v3.14.0 Changelog
Recent Releases
- v3.13.3 (August 2026) — Completes the version-dispatcher permissions fix (a second framework read path probed the marker with a gated
__-private read) by detecting a dispatcher by object identity, and fixes nested-instance permission isolation so a secondslothlet()booted inside a permissioned leaf no longer throws during its own construction (Changelog) - v3.13.2 (August 2026) — Restores composition of version-dispatched fields under a
permissionsconfiguration: slothlet's own version-dispatcher marker keys are exempted from the 3.13.0 module-private (_/__) rule by object identity, so a colliding version-dispatched field no longer fails at composition withPERMISSION_DENIEDwhile a consumer's identically-named private member stays denied (Changelog) - v3.13.1 (August 2026) —
devcheckdev-environment detection fix: reads the--conditions=slothlet-devCLI form fromprocess.execArgvand recognizes a scopednode_modulesinstall at any depth, so a correct dev run or a git/tarball install no longer self-terminates (Changelog) - v3.13.0 (August 2026) — Sync/async-transparent hook dispatch,
api.slothlet.api.leaves()for module-scoped path enumeration, and permission-enforced module-private (_/__) exports plus an injectable importer that attributes leaf execution in consumer coverage (Changelog)
📚 For complete version history and detailed release notes, see docs/changelog/ folder.
🚀 Key Features
🎯 Dual Loading Strategies
- Eager Loading: Immediate loading for maximum performance in production environments
- Lazy Loading: Copy-left materialization with look-ahead proxies (2.2x faster startup, function calls equal to eager after materialization)
[!IMPORTANT] Function Call Patterns:
- Lazy Mode: ALL function calls must be awaited (
await api.math.add(2, 3)) due to materialization process- Eager Mode: Functions behave as originally defined - sync functions are sync (
api.math.add(2, 3)), async functions are async (await api.async.process())
⚡ Performance Excellence
- Startup Performance: 2.2x faster startup in lazy mode (15.41ms vs 34.28ms)
- Runtime Performance: Function calls essentially equal between modes (9.99μs lazy vs 9.46μs eager - within 6% measurement noise)
- Copy-left materialization: Once loaded, modules stay materialized - no re-processing overhead
- Zero dependencies: Pure Node.js implementation
- Memory efficiency: Lazy mode loads modules on-demand, eager mode optimizes for predictable behavior
📊 For comprehensive performance benchmarks and analysis, see docs/PERFORMANCE.md
🎣 Hook System (redesigned in v3)
Powerful function interceptor system with 4 hook types and three-phase subset ordering:
before- Modify arguments or cancel execution (must be synchronous)after- Transform return valuesalways- Observe final results (read-only; fires even on short-circuit)error- Monitor and handle errors with detailed source tracking
Each hook type supports three ordered execution subsets: "before" → "primary" (default) → "after". Pattern matching, priority control, runtime enable/disable, and short-circuit support included.
🎣 For complete hook system documentation, see docs/HOOKS.md
🔐 Permission System (new in v3.3, read-gated in v3.7)
Path-based access control for inter-module API calls and data-value reads (v3.7+):
- Glob pattern rules — same
*,**,?,{a,b}syntax as hooks - Most-specific-wins — exact patterns override broad globs; tiebreak by registration order
- Self-call bypass — calls within the same source file always succeed
- Read-level gating (v3.7) — terminal data-value reads (
Buffer,TypedArray,Date,Map, primitives, …) checked against the rule set; on by default,readGating: falseto opt out - Audit events —
permission:denied,permission:allowed,permission:default,permission:self-bypass - Runtime management —
api.slothlet.permissions.addRule(),.removeRule(),.self.*,.global.*,.control.* - Context conditions (v3.4) — optional
conditionfield; accepts a plain object (deep leaf matching), function, or array (OR); evaluated against per-request ALS context
🔐 For complete permission system documentation, see docs/PERMISSIONS.md · 📐 For condition syntax, see docs/PERMISSIONS-CONDITIONS.md
🌍 Full Internationalization (new in v3)
All error messages and debug output are translated. Supported languages: English (US/UK) · Spanish (Spain/Mexico) · French · German · Portuguese · Hindi · Japanese · Korean · Russian · Chinese (Simplified)
Only US English (en-us) ships built in; every other locale — including UK English (en-gb) — comes from the optional @cldmv/slothlet-i18n package installed alongside slothlet (auto-detected, nothing to import or configure). Configure the language via i18n: { language: "es-mx" } in your slothlet config. See docs/I18N.md.
🔄 Context Propagation
Automatic context preservation across all asynchronous boundaries:
- Per-request isolation:
api.slothlet.context.run(ctx, fn)andapi.slothlet.context.scope(ctx) - EventEmitter propagation: Context maintained across all event callbacks
- Class instance propagation: Context preserved in class method calls
- Zero configuration: Works automatically with TCP servers, HTTP servers, and custom EventEmitters
🔄 For context propagation details, see docs/CONTEXT-PROPAGATION.md
🔧 Smart API Management
- Intelligent Flattening: Clean APIs with automatic structure optimization (
math/math.mjs→api.math) - Smart Naming: Preserves original capitalization (
auto-ip.mjswithautoIP→api.autoIP) - Advanced Sanitization: Custom naming rules with glob and boundary patterns;
api.slothlet.sanitize()at runtime - Hybrid Exports: Support for callable APIs with methods, default + named exports
🏗️ Module structure · 📐 API flattening · 🔡 Sanitization
🔗 Runtime & Context System
- Context Isolation: Automatic per-request isolation using AsyncLocalStorage (default); switchable to live-bindings mode via
runtime: "live"config option - Cross-Module Access:
self,context, andinstanceIDalways available inside API modules via@cldmv/slothlet/runtime— works identically from.mjs,.cjs,.ts, and.mts - Mixed Module Support: Seamlessly blend ESM and CommonJS modules
- Copy-Left Preservation: Materialized functions stay materialized
🌐 Browser / Worker Mode (new in v3.9)
Run slothlet in the browser, web workers, and Electron renderers — anywhere there is no filesystem:
- Manifest-driven loading: a build-time manifest replaces
readdir; API leaves load via yourresolveModuleSpecifier - One-call setup:
generateBrowserAssets(apiDir)returns both the APImanifestand theimportmapfor slothlet's own modules, so consumers never hand-roll module resolution - Bundler-friendly: bundled apps need only the manifest; raw-ESM pages and Electron renderers get the importmap too
- Full parity:
self, hooks, permissions, metadata, i18n, lifecycle events, and api mutation all work in-browser (live-binding context manager)
🌐 For complete browser-mode documentation, see docs/BROWSER.md
🛠 Developer Experience
- TypeScript-Friendly: Comprehensive JSDoc annotations with auto-generated declarations — see docs/TYPESCRIPT.md
- Configurable Debug: Detailed logging via CLI flags or environment variables
- Multiple Instances: Parameter-based isolation for complex applications
- Inspectable APIs:
console.log(api.math)and logical versioned paths likeconsole.log(api.auth)show real module contents instead of proxy internals (v3+) - Development Checks: Built-in environment detection with silent production behavior
📦 Installation
Requirements
- Node.js v22.0.0 or higher
Install
npm install @cldmv/slothlet🚀 Quick Start
import slothlet from "@cldmv/slothlet";
// Eager mode (default) — functions behave as originally defined
const api = await slothlet({
dir: "./api",
context: { user: "alice" }
});
const result = api.math.add(2, 3); // Sync stays sync
const asyncResult = await api.async.process(); // Async stays asyncCommonJS works the same way: const slothlet = require("@cldmv/slothlet").
Lazy mode with copy-left materialization — all calls awaited, ~2.2× faster startup:
const api = await slothlet({ mode: "lazy", dir: "./api" });
const result = await api.math.add(2, 3); // ALL calls awaited in lazy modeHooks, 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.
📚 Configuration
The most-used options are summarized below. The complete reference — every option, every diagnostic, every deprecated alias — lives in docs/CONFIGURATION.md.
| Option | Type | Default | Description |
| ------------- | -------- | ----------- | ---------------------------------------------------------------------------------- |
| dir | string | "api" | Directory to load API modules from |
| mode | string | "eager" | "eager" (load upfront) or "lazy" (on-demand with copy-left materialization) |
| runtime | string | "async" | "async" (AsyncLocalStorage) or "live" (live-bindings) |
| context | object | {} | Per-request context — read via import { context } from "@cldmv/slothlet/runtime" |
| hook | mixed | false | Enable hooks; see HOOKS.md |
| permissions | object | undefined | Path-based access control; see PERMISSIONS.md |
| i18n | object | {} | Language for translated error/debug messages — see I18N.md |
Also configurable: apiDepth, hidden (globs hiding files/folders from the API), debug, reference, sanitize, backgroundMaterialize, api.collision, api.mutations, versionDispatcher, typescript, plus diagnostics and lifecycle internals. All documented in CONFIGURATION.md.
🔀 Loading Modes
| Mode | Startup | Function calls | Best for |
| ---------------------------------- | ------------- | -------------------------------------------- | ------------------------------------------ |
| Eager (default) | Loads upfront | Sync stays sync, async stays async | Production, predictable performance |
| Lazy | 2.2× faster | All calls awaited; materialized on first use | Large APIs, startup-sensitive apps |
| Lazy + backgroundMaterialize | 2.2× faster | Pre-warmed by background loader | Best of both — lazy startup, eager runtime |
// Lazy + background materialization
const api = await slothlet({ mode: "lazy", dir: "./api", backgroundMaterialize: true });
api.slothlet.lifecycle.on("materialized:complete", ({ total }) => console.log(`${total} modules ready`));
await api.slothlet.materialize.wait(); // optional: gate traffic on ready📊 Benchmarks & analysis: docs/PERFORMANCE.md · 🔀 Visual pipeline diagram: docs/MODULE-STRUCTURE.md#loading-pipeline-overview · ⚡ Lifecycle events: docs/LIFECYCLE.md
🎣 Hooks
Four hook types (before, after, always, error) with three-phase subset ordering ("before" → "primary" → "after"), pattern matching, priority, and runtime enable/disable.
const api = await slothlet({ dir: "./api", hook: true });
api.slothlet.hook.on("math.add:before", ({ args }) => [args[0] * 2, args[1] * 2], { id: "double" });
api.slothlet.hook.on("math.*:after", ({ result }) => result * 10, { id: "scale" });
api.slothlet.hook.on("**:always", ({ path, hasError }) => console.log(path, hasError));
api.slothlet.hook.on("**:error", ({ path, error, source }) => console.error(path, source.type, error));
const out = await api.math.add(2, 3); // hooks fire automatically🎣 Configuration, all four types, subsets, pattern syntax, management API: docs/HOOKS.md
🔄 Per-Request Context
// Scoped context for a single call
await api.slothlet.context.run({ userId: "alice", role: "admin" }, async () => {
await api.database.query();
await api.audit.log();
});
// Derived API with merged context
const scoped = api.slothlet.context.scope({ userId: "bob" });
await scoped.database.query();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".
🔄 Full reference, isolation guarantees, merge strategies, TCP/HTTP examples: docs/CONTEXT-PROPAGATION.md
🔁 Hot Reload & Dynamic API
await api.slothlet.api.add("plugins", "./plugins-folder"); // add at runtime
await api.slothlet.api.add("plugins.trusted", "./trusted", { metadata: { trusted: true } });
await api.slothlet.api.remove("oldModule"); // remove
await api.slothlet.api.reload("database.*"); // hot-reloadCollision modes (merge / merge-replace / replace / skip / warn / error) — 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).
🔁 Full reference: docs/RELOAD.md · 🏷️ Metadata system: docs/METADATA.md
⚡ Lifecycle Events
api.slothlet.lifecycle.on("materialized:complete", ({ total }) => console.log(`${total} modules ready`));
api.slothlet.lifecycle.on("impl:created", ({ apiPath }) => {
/* … */
});
api.slothlet.lifecycle.on("impl:changed", ({ apiPath }) => {
/* reload notify */
});
api.slothlet.lifecycle.on("impl:removed", ({ apiPath }) => {
/* cleanup */
});Events: materialized:complete, impl:created, impl:changed, impl:removed. Public surface is on / off only.
⚡ Full reference: docs/LIFECYCLE.md
📁 Module Structure
api/
├── config.mjs → api.config.*
├── math/
│ └── math.mjs → api.math.* (flattened — filename matches folder)
├── util/
│ ├── util.mjs → api.util.* (flattened methods)
│ ├── extract.mjs → api.util.extract.*
│ └── controller.mjs → api.util.controller.*
└── nested/date/date.mjs → api.nested.date.*API modules must never import each other directly — use the live-binding runtime:
// ❌ WRONG — breaks lazy loading and context isolation
import { math } from "./math/math.mjs";
// ✅ CORRECT — always reflects current runtime state
import { self, context, instanceID } from "@cldmv/slothlet/runtime";
export const myModule = {
async processData(input) {
const r = self.math.add(2, 3);
console.log(`[${instanceID}] caller=${context.userId}`);
return `Processed: ${input}, Math: ${r}`;
}
};The same import works from .mjs, .cjs (via require), .ts, and .mts (TypeScript path fixed in v3.5.0).
🏗️ Module structure patterns · 📐 All 13 API transformation rules
🛡 Error Handling
Slothlet v3 uses a rich SlothletError class with translated messages and contextual hints:
try {
await api.slothlet.api.add("plugins", "./dir");
} catch (error) {
console.error(error.message); // Translated error message
console.error(error.hint); // Contextual hint for resolution
console.error(error.code); // Machine-readable error code
}🏗️ Production & Development
- Eager Mode: Stable, battle-tested, maximum runtime performance
- Lazy Mode: Production-ready with copy-left optimization
- Background Materialization: Lazy startup + eager runtime performance
- Mixed Module Loading: ESM/CJS interoperability fully supported
- Debug Mode: i18n-translated logging via
--slothletdebugflag orSLOTHLET_DEBUG=true - Source Detection: Automatic
src/vsdist/mode detection - API Inspection:
console.log(api.math)and versioned dispatcher paths show real module contents (v3+)
📚 Documentation
Reference
- Configuration Reference — every option with defaults, validation rules, and the
api.slothlet.diag.*namespace - Generated API Reference — auto-generated from JSDoc; the complete public surface
- Changelog — all release notes (v2 + v3)
- Migration Guide — upgrading from v2.x
Technical Guides
- Performance Analysis — startup vs runtime benchmarks, memory analysis, materialization cost breakdown
- Hook System — 4 types, three-phase subsets, pattern matching, management API
- Permission System — rules, glob patterns, self-call bypass, read gating, runtime management
- Permission Conditions —
conditionfield syntax: deep object matching, functions, OR arrays - Context Propagation — per-request isolation, EventEmitter / class propagation, merge strategies
- Lifecycle Events —
materialized:complete,impl:*events, subscription API - Hot Reload & Dynamic API —
add,remove,reload, collision modes, mutation controls, eager vs lazy semantics - Versioning — multi-version module dispatch,
versionDispatcher, version metadata - Metadata System — function metadata tagging for security, authorization, auditing
- Module Structure — organization patterns, examples, and the loading-pipeline diagram
- Sanitization — filename → property-name transformation rules
- TypeScript Support — fast mode (esbuild), strict mode (tsc),
.d.tsgeneration - Internationalization — supported languages and configuration
- Testing & Coverage — measuring coverage of composition-loaded leaves in a consumer project (the
server.deps.inlinerequirement)
API Rules & Transformation
- API Rules — all 13 transformation rules with verified test examples
- API Rules Conditions — every conditional that controls API generation
- API Flattening — flattening rules with decision tree
Repo
- Agent Usage Guide — for AI agents building Slothlet API folders
- Contributing — contribution guidelines
- Security Policy — security guidelines and reporting
- Test Documentation — comprehensive test module examples
🤝 Contributing
We welcome contributions! See CONTRIBUTING.md for guidelines.
🔗 Links
- npm: @cldmv/slothlet
- GitHub: CLDMV/slothlet
- Issues: GitHub Issues
- Releases: GitHub Releases
📄 License
Apache-2.0 © Shinrai / CLDMV
🙏 Acknowledgments
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.
