@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.
"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 4.3x faster startup (1.1x slower function calls)
Caution
βοΈ Experimental Modes:
- Worker, Fork, Child, VM modes: In active development, not recommended for production use
Please report issues and contribute feedback to help improve the experimental features.
- AddApi Special File Pattern (Rule 11) - Files named
addapi.mjsnow always flatten for seamless API namespace extensions - Filename-Matches-Container in addApi (Rule 1 Extension) - Auto-flattening now works in runtime
addApi()contexts - Enhanced addApi Content Preservation - Fixed critical issue where multiple addApi calls were overwriting previous content instead of merging
- Rule 12 Smart Flattening Enhancements - Comprehensive smart flattening improvements with 168-scenario test coverage
- API Documentation Suite Overhaul - Enhanced 3-tier navigation system with verified examples and cross-references
- View Changelog
- v2.10.0 - Function metadata tagging and introspection capabilities (Changelog)
- v2.9 - Per-Request Context Isolation with
api.run()andapi.scope()methods (Changelog) - v2.8 - NPM security fixes and package workflow updates (Changelog)
- v2.7 - Security updates (Changelog)
- v2.6 - Hook System with 4 interceptor types (Changelog)
- v2.5 - Architectural consolidation and API consistency (Changelog)
- v2.4 - Multi-default export handling (Changelog)
- v2.3 - EventEmitter & Class Context Propagation (Changelog)
- v2.2 - Case preservation options (Changelog)
- v2.1 - Advanced sanitization patterns (Changelog)
- v2.0 - Complete Architectural Rewrite (Changelog)
- Eager Loading: Immediate loading for maximum performance in production environments
- Lazy Loading: Copy-left materialization with look-ahead proxies (4.3x faster startup, 1.4x faster calls 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())
- 2.9x faster startup in lazy mode (4.89ms vs 14.29ms)
- 1.1x faster function calls in eager mode (0.90ΞΌs vs 0.99ΞΌs)
- Copy-left materialization: Once loaded, modules stay materialized
- Zero dependencies: Pure Node.js implementation
π For comprehensive performance analysis, benchmarks, and recommendations, see docs/PERFORMANCE.md
Powerful function interceptor system with 4 hook types:
before- Modify arguments or cancel executionafter- Transform return valuesalways- Observe final results (read-only)error- Monitor and handle errors with detailed source tracking
Pattern matching, priority control, runtime enable/disable, and short-circuit support included.
π£ For complete hook system documentation, see docs/HOOKS.md
Automatic context preservation across all asynchronous boundaries:
- 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
- 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
- Hybrid Exports: Support for callable APIs with methods, default + named exports
ποΈ For module structure examples, see docs/MODULE-STRUCTURE.md
π For API flattening rules, see docs/API-FLATTENING.md
- Live Bindings: Dynamic context and reference binding for runtime API mutation
- Context Isolation: Dual runtime options (AsyncLocalStorage or live-bindings)
- Mixed Module Support: Seamlessly blend ESM and CommonJS modules
- Copy-Left Preservation: Materialized functions stay materialized
- TypeScript-Friendly: Comprehensive JSDoc annotations with auto-generated declarations
- Configurable Debug: Detailed logging via CLI flags or environment variables
- Multiple Instances: Parameter-based isolation for complex applications
- Development Checks: Built-in environment detection with silent production behavior
- Node.js v16.20.2 or higher (required for stack trace API fixes used in path resolution)
- 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
- Node.js 16.4-16.19 has a stack trace regression. For these versions, use slothlet 2.10.0:
npm install @cldmv/slothletimport slothlet from "@cldmv/slothlet";
// Direct usage - eager mode by default
const api = await slothlet({
dir: "./api",
context: { user: "alice" }
});
// Eager mode: Functions behave as originally defined
const result = api.math.add(2, 3); // Sync function - no await needed
const asyncResult = await api.async.processData({ data: "async" });
// Access both ESM and CJS modules seamlessly
const esmResult = api.mathEsm.multiply(4, 5);
const cjsResult = api.mathCjs.divide(10, 2);const slothlet = require("@cldmv/slothlet");
// Same usage pattern works with CommonJS
const api = await slothlet({
dir: "./api",
context: { env: "production" }
});
const result = api.math.multiply(4, 5);
const mixedResult = await api.interop.processData({ data: "test" });import slothlet from "@cldmv/slothlet";
// Lazy mode with copy-left materialization
const api = await slothlet({
mode: "lazy", // Preferred syntax
dir: "./api",
apiDepth: 3
});
// First access: materialization overhead (~1.45ms average)
const result1 = await api.math.add(2, 3);
// Subsequent access: materialized function (near-eager performance)
const result2 = await api.math.add(5, 7);import slothlet from "@cldmv/slothlet";
const api = await slothlet({
dir: "./api",
hooks: true // Enable hooks
});
// Before hook: Modify arguments
api.hooks.on(
"validate",
"before",
({ path, args }) => {
console.log(`Calling ${path} with args:`, args);
return [args[0] * 2, args[1] * 2];
},
{ pattern: "math.add", priority: 100 }
);
// After hook: Transform result
api.hooks.on(
"format",
"after",
({ path, result }) => {
console.log(`${path} returned:`, result);
return result * 10;
},
{ pattern: "math.*", priority: 100 }
);
// Always hook: Observe final result
api.hooks.on(
"observe",
"always",
({ path, result, hasError }) => {
console.log(hasError ? `${path} failed` : `${path} succeeded`);
},
{ pattern: "**" }
);
// Error hook: Monitor errors with source tracking
api.hooks.on(
"error-logger",
"error",
({ path, error, source }) => {
console.error(`Error in ${path}:`, error.message);
console.error(`Source: ${source.type}`); // 'before', 'after', 'function', 'always'
},
{ pattern: "**" }
);
// Call function - hooks execute automatically
const result = await api.math.add(2, 3);Load additional modules at runtime and extend your API dynamically:
import slothlet from "@cldmv/slothlet";
const api = await slothlet({ dir: "./api" });
// Add plugins at runtime
await api.addApi("plugins", "./plugins-folder");
api.plugins.myPlugin();
// Create nested API structures
await api.addApi("runtime.plugins", "./more-plugins");
api.runtime.plugins.loader();
// Add with metadata for security/authorization
await api.addApi("plugins.trusted", "./trusted-plugins", {
trusted: true,
permissions: ["read", "write", "admin"],
version: "1.0.0"
});
await api.addApi("plugins.external", "./third-party", {
trusted: false,
permissions: ["read"]
});
// Access metadata on functions
const meta = api.plugins.trusted.someFunc.__metadata;
console.log(meta.trusted); // true
console.log(meta.permissions); // ["read", "write", "admin"]Security & Authorization with metadataAPI:
// Inside your modules, use metadataAPI for runtime introspection
import { metadataAPI } from "@cldmv/slothlet/runtime";
export async function sensitiveOperation() {
// Check caller's metadata
const caller = await metadataAPI.caller();
if (!caller?.trusted) {
throw new Error("Unauthorized: Caller is not trusted");
}
if (!caller.permissions.includes("admin")) {
throw new Error("Unauthorized: Admin permission required");
}
// Proceed with secure operation
return "Success";
}
// Get metadata by path
const meta = await metadataAPI.get("plugins.trusted.someFunc");
// Get current function's metadata
const self = await metadataAPI.self();
console.log("My version:", self.version);π For complete metadata system documentation, see docs/METADATA.md
| Option | Type | Default | Description |
|---|---|---|---|
dir |
string |
"api" |
Directory to load API modules from (absolute or relative path) |
mode |
string |
"eager" |
New loading mode - "lazy" for on-demand loading, "eager" for immediate loading |
lazy |
boolean |
false |
Legacy loading strategy (use mode instead) |
engine |
string |
"singleton" |
Execution environment: "singleton", "vm", "worker", or "fork" (experimental modes) |
runtime |
string |
"async" |
Runtime binding system: "async" for AsyncLocalStorage (requires Node.js v16.20.2+), "live" for live-bindings (works on Node.js v12.20.0+) |
apiDepth |
number |
Infinity |
Directory traversal depth - 0 for root only, Infinity for all levels |
debug |
boolean |
false |
Enable verbose logging (also via --slothletdebug flag or SLOTHLET_DEBUG=true env var) |
api_mode |
string |
"auto" |
API structure behavior: "auto" (detect), "function" (force callable), "object" (force object) |
allowApiOverwrite |
boolean |
true |
Allow addApi() to overwrite existing endpoints (false = prevent overwrites with warning) |
enableModuleOwnership |
boolean |
false |
Enable module-based API ownership tracking (true = track ownership for selective overwrites, false = disabled for performance) |
context |
object |
{} |
Context data injected into live-binding (available via import { context } from "@cldmv/slothlet/runtime") |
reference |
object |
{} |
Reference object merged into API root level |
sanitize |
object |
{} |
Advanced filename-to-API transformation control with lowerFirst, preserveAllUpper, preserveAllLower, and rules (supports exact matches, glob patterns *json*, and boundary patterns **url**) |
hooks |
mixed |
false |
Enable hook system: true (enable all), "pattern" (enable with pattern), or object with enabled, pattern, suppressErrors options |
For complete API documentation with detailed parameter descriptions and examples, see docs/generated/API.md
flowchart TD
MODULEFOLDERS --> SLOTHLET
SLOTHLET --> CHOOSEMODE
CHOOSEMODE --> LAZY
CHOOSEMODE --> EAGER
subgraph EAGER ["β‘ Eager Mode"]
direction TB
EAGER0 ~~~ EAGER1
EAGER2 ~~~ EAGER3
EAGER0@{ shape: braces, label: "π₯ All modules loaded immediately" }
EAGER1@{ shape: braces, label: "β
API methods available right away" }
EAGER2@{ shape: braces, label: "π Function calls behave as originally defined" }
EAGER3@{ shape: braces, label: "π Sync stays sync: api.math.add(2,3)<br/>π Async stays async: await api.async.process()" }
end
subgraph LAZY ["π€ Lazy Mode"]
direction TB
LAZY0 ~~~ LAZY1
LAZY2 ~~~ LAZY3
LAZY4 ~~~ LAZY5
LAZY0@{ shape: braces, label: "π¦ Modules not loaded yet" }
LAZY1@{ shape: braces, label: "π API methods are placeholders/proxies" }
LAZY2@{ shape: braces, label: "π First call triggers materialization" }
LAZY3@{ shape: braces, label: "β³ All calls must be awaited<br/>await api.math.add(2,3)" }
LAZY4@{ shape: braces, label: "πΎ Module stays loaded after materialization<br/>Copy-left materialization" }
LAZY5@{ shape: braces, label: "π Subsequent calls nearly as fast as eager mode" }
end
subgraph EAGERCALL ["β‘ Eager Mode Calls"]
direction TB
end
subgraph LAZYCALL ["π€ Lazy Mode Calls"]
direction TB
LAZYCALL0 --> LAZYCALL2
LAZYCALL0@{ shape: rounded, label: "π First call" }
LAZYCALL1@{ shape: rounded, label: "π Sequential calls" }
LAZYCALL2@{ shape: rounded, label: "π§© Materialize" }
end
EAGER --> READYTOUSE
LAZY --> READYTOUSE
READYTOUSE --> CALL
CALL -.-> EAGERCALL
CALL -.-> LAZYCALL
EAGERCALL --> MATERIALIZEDFUNCTION
LAZYCALL1 --> MATERIALIZEDFUNCTION
LAZYCALL2 --> MATERIALIZEDFUNCTION
READYTOUSE@{ shape: rounded, label: "π― Ready to Use" }
MATERIALIZEDFUNCTION@{ shape: rounded, label: "β
Materialized method/property" }
CALL@{ shape: trap-b, label: "π Call" }
%% Notes as unattached nodes with braces shape
subgraph ALWAYS ["β¨ Extras Always On"]
direction TB
ALWAYS0 ~~~ ALWAYS1
ALWAYS1 ~~~ ALWAYS2
ALWAYS0@{ shape: rounded, label: "π Live Bindings ALS<br/>Per-instance context isolation" }
ALWAYS1@{ shape: rounded, label: "π·οΈ Smart Naming & Flattening<br/>Multiple rules for clean APIs" }
ALWAYS2@{ shape: rounded, label: "π Mixed Module Support<br/>Seamlessly mix .mjs and .cjs" }
end
MODULEFOLDERS@{ shape: st-rect, label: "π Modules Folder<br/>.mjs and/or .cjs files<br/>math.mjs, string.cjs, async.mjs" }
SLOTHLET@{ shape: rounded, label: "π§ Call slothlet(options)" }
CHOOSEMODE@{ shape: diamond, label: "Choose Mode<br/>in options" }
style EAGER0 stroke:#9BC66B,color:#9BC66B,opacity:0.5
style EAGER1 stroke:#9BC66B,color:#9BC66B,opacity:0.5
style EAGER2 stroke:#9BC66B,color:#9BC66B,opacity:0.5
style EAGER3 stroke:#9BC66B,color:#9BC66B,opacity:0.5
style LAZY0 stroke:#9BC66B,color:#9BC66B,opacity:0.5
style LAZY1 stroke:#9BC66B,color:#9BC66B,opacity:0.5
style LAZY2 stroke:#9BC66B,color:#9BC66B,opacity:0.5
style LAZY3 stroke:#9BC66B,color:#9BC66B,opacity:0.5
style LAZY4 stroke:#9BC66B,color:#9BC66B,opacity:0.5
style LAZY5 stroke:#9BC66B,color:#9BC66B,opacity:0.5
%% Slothlet brand colors - #9BC66B primary on dark theme
style MODULEFOLDERS fill:#1a1a1a,stroke:#9BC66B,stroke-width:2px,color:#9BC66B,opacity:0.5
style SLOTHLET fill:#1a1a1a,stroke:#9BC66B,stroke-width:2px,color:#9BC66B,opacity:0.5
style CHOOSEMODE fill:#1a1a1a,stroke:#9BC66B,stroke-width:2px,color:#9BC66B,opacity:0.5
style READYTOUSE fill:#1a1a1a,stroke:#9BC66B,stroke-width:2px,color:#9BC66B,opacity:0.5
style CALL fill:#1a1a1a,stroke:#9BC66B,stroke-width:2px,color:#9BC66B,opacity:0.5
style MATERIALIZEDFUNCTION fill:#1a1a1a,stroke:#9BC66B,stroke-width:2px,color:#9BC66B,opacity:0.5
%% Eager mode - primary green
style EAGER fill:#0d1a0d,stroke:#9BC66B,stroke-width:3px,color:#9BC66B,opacity:0.5
style EAGERCALL fill:#0d1a0d,stroke:#9BC66B,stroke-width:2px,color:#9BC66B,opacity:0.5
%% Lazy mode - lighter green tint
style LAZY fill:#0d1a0d,stroke:#B8D982,stroke-width:3px,color:#B8D982,opacity:0.5
style LAZYCALL fill:#0d1a0d,stroke:#B8D982,stroke-width:2px,color:#B8D982,opacity:0.5
style LAZYCALL0 fill:#1a1a1a,stroke:#B8D982,stroke-width:2px,color:#B8D982,opacity:0.5
style LAZYCALL1 fill:#1a1a1a,stroke:#B8D982,stroke-width:2px,color:#B8D982,opacity:0.5
style LAZYCALL2 fill:#1a1a1a,stroke:#B8D982,stroke-width:2px,color:#B8D982,opacity:0.5
%% Always available - accent green
style ALWAYS fill:#0d1a0d,stroke:#7FA94F,stroke-width:3px,color:#7FA94F,opacity:0.5
style ALWAYS0 fill:#1a1a1a,stroke:#7FA94F,stroke-width:1px,color:#7FA94F,opacity:0.5
style ALWAYS1 fill:#1a1a1a,stroke:#7FA94F,stroke-width:1px,color:#7FA94F,opacity:0.5
style ALWAYS2 fill:#1a1a1a,stroke:#7FA94F,stroke-width:1px,color:#7FA94F,opacity:0.5
%% Arrow styling
linkStyle default stroke:#9BC66B,stroke-width:3px,opacity:0.5
linkStyle 4,5,6,7,8,18,19 stroke-width:0px
Best for: Production environments, maximum runtime performance, predictable behavior
const api = await slothlet({ dir: "./api" }); // lazy: false by default
// Functions behave as originally defined
const result = api.math.add(2, 3); // Sync - no await needed
const asyncResult = await api.async.processData({ data: "test" }); // Async needs awaitBenefits:
- β Fastest function calls (0.90ΞΌs average)
- β Predictable performance (no materialization delays)
- β Functions behave exactly as originally defined
Best for: Startup-sensitive applications, memory efficiency, loading only what you use
const api = await slothlet({ mode: "lazy", dir: "./api" });
// ALL calls must be awaited (materialization process)
const result1 = await api.math.add(2, 3); // First: ~371ΞΌs (materialization)
const result2 = await api.math.add(5, 7); // Subsequent: 0.99ΞΌs (materialized)Benefits:
- β 2.9x faster startup (4.89ms vs 14.29ms)
- β Near-equal function call performance (0.99ΞΌs vs 0.90ΞΌs eager)
- β Memory efficient (loads only what you use)
- β Copy-left optimization (once loaded, stays loaded)
Tip
Choose your strategy:
- Startup-sensitive? β Lazy mode (2.9x faster startup)
- Call-intensive? β Eager mode (1.1x faster calls)
- Need predictability? β Eager mode (no materialization delays)
- Large API, use subset? β Lazy mode (memory efficient)
For comprehensive performance benchmarks, analysis, and recommendations:
Key highlights:
- Detailed startup vs runtime performance comparison
- Memory usage analysis by loading mode
- Materialization cost breakdown by module type
- Real-world performance recommendations
- API Documentation - Complete API reference with examples and detailed parameter descriptions
- Performance Analysis - Detailed benchmarks and recommendations
- Contributing Guide - How to contribute to the project
- Security Policy - Security guidelines and reporting
- Test Documentation - Comprehensive test module examples
- Hook System - Complete hook system documentation with 4 hook types, pattern matching, and examples
- Context Propagation - EventEmitter and class instance context preservation
- Metadata System - Function metadata tagging and runtime introspection for security, authorization, and auditing
- Module Structure - Comprehensive module organization patterns and examples
- API Flattening - The 5 flattening rules with decision tree and benefits
- API Rules - Systematically verified API transformation rules with real examples and test cases
- API Rules Conditions - Complete technical reference of all 26 conditional statements that control API generation
- v2.9 - Per-Request Context Isolation & API Builder Modularization (December 30, 2025)
- v2.8 - NPM security fixes and package workflow updates (December 26, 2025)
- v2.7 - Hook System with 4 interceptor types (December 20, 2025)
- v2.6 - Mode/Engine options and deep nested path fixes (November 10, 2025)
- v2.5 - Architectural consolidation and API consistency (October 20, 2025)
- v2.4 - Multi-default export handling with file-based naming (October 18, 2025)
- v2.3 - EventEmitter & Class Context Propagation (October 16, 2025)
- v2.2 - Case preservation options (preserveAllUpper/preserveAllLower) (October 14, 2025)
- v2.1 - Advanced sanitization with boundary patterns (October 12, 2025)
- v2.0 - Complete Architectural Rewrite (September 9, 2025)
Note
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.
Current behavior:
try {
console.log(api.nonexistent); // Returns: undefined
await api.nonexistent.method(); // Throws: "Cannot read properties of undefined (reading 'method')"
} catch (error) {
console.error(error.message); // Standard JavaScript error message
}- Eager Mode: Stable, battle-tested, maximum performance
- Lazy Mode: Production-ready with copy-left optimization
- Singleton Mode: Default mode for standard applications
- Mixed Module Loading: ESM/CJS interoperability fully supported
- Development Check:
devcheck.mjsfor environment validation - Debug Mode: Comprehensive logging via
--slothletdebugflag orSLOTHLET_DEBUG=true - Performance Monitoring: Built-in timing and performance analysis
- Source Detection: Automatic
src/vsdist/mode detection
Warning
The following modes are in active development and not recommended for production use:
- Worker Mode: Thread isolation (in development)
- Fork Mode: Process isolation (in development)
- Child Mode: Child process execution (in development)
- VM Mode: Virtual machine context (in development)
- Import paths:
@cldmv/slothletinstead of specific file paths - Configuration: New options (
api_mode,context,reference,hooks) - Function names: Enhanced preservation of original capitalization
- Module structure: Mixed ESM/CJS support
- Live bindings: Dual runtime system with AsyncLocalStorage and live-bindings options
- Automatic instances: No more query strings or
withInstanceId()methods
// v1.3.x - Multiple instances required query strings or withInstanceId()
const api1 = await slothlet({ dir: "./api?instanceId=alice" });
const api2 = slothlet.withInstanceId("bob");
const bobApi = await api2({ dir: "./api" });
// v2.x - Automatic instance isolation (no query strings needed)
const api1 = await slothlet({ dir: "./api", context: { tenant: "alice" } });
const api2 = await slothlet({ dir: "./api", context: { tenant: "bob" } });
// Instances completely isolated with their own contextsWe welcome contributions! The experimental modes in particular need development and testing. Please:
- Review the code in
src/lib/engine/for experimental features - Report issues with detailed reproduction steps
- Submit pull requests with comprehensive tests
- Provide feedback on API design and performance
- Documentation improvements are always appreciated
See CONTRIBUTING.md for detailed contribution guidelines.
- npm: @cldmv/slothlet
- GitHub: CLDMV/slothlet
- Issues: GitHub Issues
- Releases: GitHub Releases
Apache-2.0 Β© Shinrai / CLDMV
Slothlet v2.0 represents a complete architectural rewrite with enterprise-grade features and performance. Special thanks to all contributors who made this comprehensive enhancement possible.
π Welcome to the future of module loading with Slothlet!
Where sophisticated architecture meets blazing performance - slothlet is anything but slow.
