@cldmv/slothlet 2.6.1 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -40,7 +40,7 @@ The name might suggest we're taking it easy, but don't be fooled. **Slothlet del
40
40
  v2.0 represents a ground-up rewrite with enterprise-grade features:
41
41
 
42
42
  - **Universal Module Support**: Load both ESM (`.mjs`) and CommonJS (`.cjs`) files seamlessly
43
- - **AsyncLocalStorage Integration**: Advanced context isolation and live-binding system
43
+ - **Dual Runtime System**: Choose AsyncLocalStorage or live-bindings for context isolation
44
44
  - **4.3x Faster Startup**: Lazy mode achieves 564.17Ξs vs 2.45ms in eager mode
45
45
  - **Copy-Left Materialization**: Once loaded, modules stay materialized for optimal performance
46
46
  - **Zero Dependencies**: Pure Node.js implementation with no external dependencies
@@ -74,6 +74,15 @@ v2.0 represents a ground-up rewrite with enterprise-grade features:
74
74
  - **AsyncResource Integration**: Production-ready context management following Node.js best practices
75
75
  - **Zero Configuration**: Works automatically with TCP servers, HTTP servers, and any EventEmitter-based patterns
76
76
 
77
+ ### ðŸŽĢ **Hook System (v2.6.4)** ⭐ NEW
78
+
79
+ - **3-Hook Types**: `before` (modify args or cancel), `after` (transform results), `always` (observe final result)
80
+ - **Cross-Mode Compatibility**: Works seamlessly across all 4 combinations (eager/lazy × async/live)
81
+ - **Pattern Matching**: Target specific functions or use wildcards (`math.*`, `*.add`, `**`)
82
+ - **Priority Control**: Order hook execution with numeric priorities
83
+ - **Runtime Control**: Enable/disable hooks at runtime, globally or by pattern
84
+ - **Short-Circuit Support**: Cancel execution and return custom values from `before` hooks
85
+
77
86
  ---
78
87
 
79
88
  ## 🚀 Key Features
@@ -113,14 +122,14 @@ v2.0 represents a ground-up rewrite with enterprise-grade features:
113
122
  ### 🔗 **Advanced Binding System**
114
123
 
115
124
  - **Live Bindings**: Dynamic context and reference binding for runtime API mutation
116
- - **AsyncLocalStorage**: Per-instance context isolation with seamless integration
125
+ - **Context Isolation**: Dual runtime options for per-instance context isolation with seamless integration
117
126
  - **Copy-Left Preservation**: Materialized functions stay materialized, preserving performance gains
118
127
  - **Bubble-Up Updates**: Parent API synchronization ensures consistency across the API tree
119
128
  - **Mixed Module Support**: Seamlessly blend ESM and CommonJS modules in the same API
120
129
 
121
130
  ### 🛠 **Developer Experience**
122
131
 
123
- - **Standard Error Handling**: Clear JavaScript errors with plans for enhanced descriptive errors in v2.1.0
132
+ - **Enhanced Error Handling**: Clear JavaScript errors with module suggestions and descriptive errors (planned for v3.0.0)
124
133
  - **TypeScript-Friendly**: Comprehensive JSDoc annotations for excellent editor support with auto-generated declarations
125
134
  - **Configurable Debug**: Detailed logging for development and troubleshooting via CLI flags or environment variables
126
135
  - **Multiple Instances**: Parameter-based isolation for complex applications with instance ID management
@@ -132,7 +141,7 @@ v2.0 represents a ground-up rewrite with enterprise-grade features:
132
141
  - **Universal Loading**: CommonJS and ESM files work together seamlessly
133
142
  - **Zero Dependencies**: Lightweight footprint with no external dependencies
134
143
  - **Cross-Platform**: Works seamlessly across all Node.js environments
135
- - **Extensible**: Modular architecture designed for future plugin system (in development)
144
+ - **Extensible**: Modular architecture with flexible API composition patterns
136
145
 
137
146
  ---
138
147
 
@@ -140,11 +149,16 @@ v2.0 represents a ground-up rewrite with enterprise-grade features:
140
149
 
141
150
  ### Requirements
142
151
 
143
- - **Node.js v16.4.0 or higher** (for stable AsyncLocalStorage support)
144
- - **ESM support** (ES modules with `import`/`export`)
152
+ - **Node.js v12.20.0 or higher** (for ESM support with `import`/`export`)
153
+ - **Node.js v16.4.0 or higher** (recommended for AsyncLocalStorage runtime - `runtime: "async"`)
145
154
 
146
155
  > [!IMPORTANT]
147
- > **v2.x Breaking Change**: Slothlet v2.x requires AsyncLocalStorage for its comprehensive live-binding system, which was stabilized in Node.js v16.4.0+ (June 2021). If you need older Node.js versions, please use slothlet v1.x (which requires Node.js v12.20.0+ (November 2020) for ESM support, dynamic imports, and query string imports). Note that v1.x live-binding worked in ESM (including multiple APIs via query strings) but was not available for multiple API instances in CommonJS.
156
+ > **v2.x Runtime Options**: Slothlet v2.x supports two runtime systems:
157
+ >
158
+ > - **AsyncLocalStorage Runtime** (`runtime: "async"`) - Default, requires Node.js v16.4.0+ for context isolation
159
+ > - **Live Bindings Runtime** (`runtime: "live"`) - Advanced system, works on Node.js v12.20.0+ without AsyncLocalStorage
160
+ >
161
+ > Both runtimes provide full live-binding capabilities with `self`, `context`, and `reference` support across ESM and CommonJS modules. Use `runtime: "live"` for older Node.js versions or advanced binding scenarios.
148
162
 
149
163
  ### Install
150
164
 
@@ -194,6 +208,28 @@ const result = api.math.multiply(4, 5); // 20
194
208
  const mixedResult = await api.interop.processData({ data: "test" }); // CJS+ESM interop
195
209
  ```
196
210
 
211
+ ### Runtime Selection
212
+
213
+ ```javascript
214
+ // AsyncLocalStorage runtime (default) - requires Node.js v16.4.0+
215
+ const apiAsync = await slothlet({
216
+ dir: "./api",
217
+ runtime: "async", // or "asynclocalstorage"
218
+ context: { user: "alice" }
219
+ });
220
+
221
+ // Live bindings runtime - works on Node.js v12.20.0+
222
+ const apiLive = await slothlet({
223
+ dir: "./api",
224
+ runtime: "live", // or "livebindings"
225
+ context: { user: "bob" }
226
+ });
227
+
228
+ // Both provide identical live-binding capabilities
229
+ import { self, context, reference } from "@cldmv/slothlet/runtime";
230
+ // context.user is available in your modules regardless of runtime choice
231
+ ```
232
+
197
233
  ### Lazy Loading Mode
198
234
 
199
235
  ```javascript
@@ -260,7 +296,7 @@ const api = await slothlet({
260
296
 
261
297
  ### Sanitize Options Examples
262
298
 
263
- Transform module filenames into clean, professional API property names:
299
+ Transform module filenames into clean, professional API property names with sophisticated control:
264
300
 
265
301
  ```javascript
266
302
  // Without sanitize options (default behavior)
@@ -272,23 +308,96 @@ const api = await slothlet({ dir: "./api" });
272
308
  const api = await slothlet({
273
309
  dir: "./api",
274
310
  sanitize: {
275
- lowerFirst: false,
311
+ lowerFirst: false, // Keep first character casing
312
+ preserveAllUpper: true, // Preserve identifiers like "COMMON_APPS"
313
+ preserveAllLower: false, // Transform identifiers like "common_apps"
276
314
  rules: {
277
- leave: ["parseJSON"], // Exact match preservation
278
- upper: ["**url**", "ip", "http*"], // Boundary + glob patterns
279
- leaveInsensitive: ["*xml*"] // Case-insensitive globs
315
+ leave: ["parseJSON"], // Exact match preservation (case-sensitive)
316
+ leaveInsensitive: ["*xml*"], // Case-insensitive preservation
317
+ upper: ["**url**", "ip", "http*"], // Force uppercase transformations
318
+ lower: ["id", "*id"] // Force lowercase transformations
280
319
  }
281
320
  }
282
321
  });
283
322
  // Result: api.buildURLWithParams, api.parseJSON, api.autoIP
284
323
  ```
285
324
 
325
+ **Comprehensive Sanitize Configuration:**
326
+
327
+ ```javascript
328
+ const api = await slothlet({
329
+ dir: "./api",
330
+ sanitize: {
331
+ // Basic Options
332
+ lowerFirst: true, // Default: lowercase first character for camelCase
333
+ preserveAllUpper: true, // Keep "COMMON_APPS" unchanged
334
+ preserveAllLower: false, // Transform "common_apps" → "commonApps"
335
+
336
+ rules: {
337
+ // Preserve exact matches (case-sensitive)
338
+ leave: ["parseJSON", "autoIP", "getHTTPStatus"],
339
+
340
+ // Preserve patterns (case-insensitive)
341
+ leaveInsensitive: ["*xml*", "*html*"],
342
+
343
+ // Force uppercase transformations
344
+ upper: [
345
+ "api", // Exact: "api" → "API"
346
+ "http*", // Glob: "httpGet" → "HTTPGet"
347
+ "**url**", // Boundary: "buildUrlPath" → "buildURLPath"
348
+ "**json**" // Boundary: "parseJsonData" → "parseJSONData"
349
+ ],
350
+
351
+ // Force lowercase transformations
352
+ lower: [
353
+ "id", // Exact: "ID" → "id"
354
+ "*id", // Glob: "userId" → "userid"
355
+ "uuid*" // Glob: "UUIDGenerator" → "uuidGenerator"
356
+ ]
357
+ }
358
+ }
359
+ });
360
+ ```
361
+
286
362
  **Sanitize Pattern Types:**
287
363
 
288
364
  - **Exact Match**: `"parseJSON"` - Matches exact string only
289
- - **Glob Patterns**: `"*json*"`, `"auto*"`, `"http*"` - Wildcard matching
290
- - **Boundary Patterns**: `"**url**"` - Only matches when surrounded by word boundaries
291
- - **Case Control**: `leaveInsensitive` for case-insensitive matching
365
+ - **Glob Patterns**:
366
+ - `"*json*"` - Matches anywhere in string (`parseJsonData`)
367
+ - `"auto*"` - Matches at start (`autoGenerateId`)
368
+ - `"*id"` - Matches at end (`userId`)
369
+ - **Boundary Patterns**:
370
+ - `"**url**"` - Only matches when surrounded by characters (`buildUrlPath` ✓, `url` ✗)
371
+ - `"**json**"` - Matches `parseJsonData` but not standalone `json`
372
+ - **Case Control**:
373
+ - `leave` - Case-sensitive exact preservation
374
+ - `leaveInsensitive` - Case-insensitive preservation
375
+ - `preserveAllUpper`/`preserveAllLower` - Automatic case detection
376
+
377
+ **Advanced Pattern Examples:**
378
+
379
+ ```javascript
380
+ // File transformations with patterns:
381
+ sanitizePathName("build-url-with-params", {
382
+ rules: { upper: ["**url**"] }
383
+ }); // → "buildURLWithParams"
384
+
385
+ sanitizePathName("parse-json-data", {
386
+ rules: { upper: ["**json**"] }
387
+ }); // → "parseJSONData"
388
+
389
+ sanitizePathName("get-http-status", {
390
+ rules: { upper: ["http*"] }
391
+ }); // → "getHTTPStatus"
392
+
393
+ sanitizePathName("validate-user-id", {
394
+ rules: { lower: ["*id"] }
395
+ }); // → "validateUserid"
396
+
397
+ sanitizePathName("XML_PARSER", {
398
+ preserveAllUpper: true
399
+ }); // → "XML_PARSER" (preserved)
400
+ ```
292
401
 
293
402
  ### Multiple Instances
294
403
 
@@ -323,7 +432,7 @@ console.log(api2.context.tenant); // "bob"
323
432
  ```
324
433
 
325
434
  > [!NOTE]
326
- > **v2.x Simplification**: Unlike v1.x which required query string parameters or `withInstanceId()` methods, v2.x automatically creates isolated instances with each `slothlet()` call, leveraging AsyncLocalStorage for complete context separation.
435
+ > **v2.x Simplification**: Unlike v1.x which required query string parameters or `withInstanceId()` methods, v2.x automatically creates isolated instances with each `slothlet()` call, using your chosen runtime system (`async` or `live`) for complete context separation.
327
436
 
328
437
  ---
329
438
 
@@ -345,20 +454,21 @@ Creates and loads an API instance with the specified configuration.
345
454
 
346
455
  **Options:**
347
456
 
348
- | Option | Type | Default | Description |
349
- | ----------- | --------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
350
- | `dir` | `string` | `"api"` | Directory to load API modules from. Can be absolute or relative path. If relative, resolved from process.cwd(). |
351
- | `lazy` | `boolean` | `false` | **Legacy** loading strategy - `true` for lazy loading (on-demand), `false` for eager loading (immediate). Use `mode` option instead. |
352
- | `mode` | `string` | - | **New** loading mode - `"lazy"` for on-demand loading, `"eager"` for immediate loading. Takes precedence over `lazy` option. Also supports execution modes for backward compatibility. |
353
- | `engine` | `string` | `"singleton"` | **New** execution environment mode - `"singleton"`, `"vm"`, `"worker"`, or `"fork"` |
354
- | `apiDepth` | `number` | `Infinity` | Directory traversal depth control - `0` for root only, `Infinity` for all levels |
355
- | `debug` | `boolean` | `false` | Enable verbose logging. Can also be set via `--slothletdebug` command line flag or `SLOTHLET_DEBUG=true` environment variable |
356
- | `api_mode` | `string` | `"auto"` | API structure behavior when root-level default functions exist:<br/>â€Ē `"auto"`: Automatically detects if root has default function export and creates callable API<br/>â€Ē `"function"`: Forces API to be callable (use when you have root-level default function exports)<br/>â€Ē `"object"`: Forces API to be object-only (use when you want object interface regardless of exports) |
357
- | `context` | `object` | `{}` | Context data object injected into live-binding `context` reference. Available to all loaded modules via `import { context } from "@cldmv/slothlet/runtime"` |
358
- | `reference` | `object` | `{}` | Reference object merged into the API root level. Properties not conflicting with loaded modules are added directly to the API |
359
- | `sanitize` | `object` | `{}` | **🔧 NEW**: Control how filenames become API property names. Supports exact matches, glob patterns (`*json*`), and boundary patterns (`**url**`). Configure `lowerFirst` and `rules` for `leave`, `leaveInsensitive`, `upper`, and `lower` transformations |
360
-
361
- #### âœĻ New Option Format (v2.6.0+)
457
+ | Option | Type | Default | Description |
458
+ | ----------- | --------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
459
+ | `dir` | `string` | `"api"` | Directory to load API modules from. Can be absolute or relative path. If relative, resolved from process.cwd(). |
460
+ | `lazy` | `boolean` | `false` | **Legacy** loading strategy - `true` for lazy loading (on-demand), `false` for eager loading (immediate). Use `mode` option instead. |
461
+ | `mode` | `string` | - | **New** loading mode - `"lazy"` for on-demand loading, `"eager"` for immediate loading. Takes precedence over `lazy` option. Also supports execution modes for backward compatibility. |
462
+ | `engine` | `string` | `"singleton"` | **New** execution environment mode - `"singleton"`, `"vm"`, `"worker"`, or `"fork"` |
463
+ | `runtime` | `string` | `"async"` | Runtime binding system: `"async"` for AsyncLocalStorage-based context isolation (default, requires Node.js v16.4.0+), `"live"` for advanced live-binding system (works on Node.js v12.20.0+). Both provide full live-binding capabilities. |
464
+ | `apiDepth` | `number` | `Infinity` | Directory traversal depth control - `0` for root only, `Infinity` for all levels |
465
+ | `debug` | `boolean` | `false` | Enable verbose logging. Can also be set via `--slothletdebug` command line flag or `SLOTHLET_DEBUG=true` environment variable |
466
+ | `api_mode` | `string` | `"auto"` | API structure behavior when root-level default functions exist:<br/>â€Ē `"auto"`: Automatically detects if root has default function export and creates callable API<br/>â€Ē `"function"`: Forces API to be callable (use when you have root-level default function exports)<br/>â€Ē `"object"`: Forces API to be object-only (use when you want object interface regardless of exports) |
467
+ | `context` | `object` | `{}` | Context data object injected into live-binding `context` reference. Available to all loaded modules via `import { context } from "@cldmv/slothlet/runtime"` |
468
+ | `reference` | `object` | `{}` | Reference object merged into the API root level. Properties not conflicting with loaded modules are added directly to the API |
469
+ | `sanitize` | `object` | `{}` | **🔧 NEW**: Advanced filename-to-API transformation control. Options: `lowerFirst` (boolean), `preserveAllUpper` (boolean), `preserveAllLower` (boolean), `rules` object with `leave` (exact case-sensitive), `leaveInsensitive` (case-insensitive), `upper`/`lower` arrays. Supports exact matches, glob patterns (`*json*`, `http*`), and boundary patterns (`**url**` for surrounded matches only) |
470
+
471
+ #### âœĻ Current Option Format
362
472
 
363
473
  The option structure has been improved for better clarity:
364
474
 
@@ -611,6 +721,488 @@ console.log("Processing completed with context preservation");
611
721
  > [!TIP]
612
722
  > **Universal Class Support**: Any class instance returned from your API functions automatically maintains slothlet context. This includes database models, service classes, utility classes, and any other object-oriented patterns in your codebase.
613
723
 
724
+ ### Hook System
725
+
726
+ Slothlet provides a powerful hook system for intercepting, modifying, and observing API function calls. Hooks work seamlessly across all loading modes (eager/lazy) and runtime types (async/live).
727
+
728
+ #### Hook Configuration
729
+
730
+ Hooks can be configured when creating a slothlet instance:
731
+
732
+ ```javascript
733
+ // Enable hooks (simple boolean)
734
+ const api = await slothlet({
735
+ dir: "./api",
736
+ hooks: true // Enable all hooks with default pattern "**"
737
+ });
738
+
739
+ // Enable with custom pattern
740
+ const api = await slothlet({
741
+ dir: "./api",
742
+ hooks: "database.*" // Only enable for database functions
743
+ });
744
+
745
+ // Full configuration object
746
+ const api = await slothlet({
747
+ dir: "./api",
748
+ hooks: {
749
+ enabled: true,
750
+ pattern: "**", // Default pattern for filtering
751
+ suppressErrors: false // Control error throwing behavior
752
+ }
753
+ });
754
+ ```
755
+
756
+ **Configuration Options:**
757
+
758
+ - **`enabled`** (boolean): Enable or disable hook execution
759
+ - **`pattern`** (string): Default pattern for filtering which functions hooks apply to
760
+ - **`suppressErrors`** (boolean): Control error throwing behavior
761
+ - `false` (default): Errors are sent to error hooks, THEN thrown (normal behavior)
762
+ - `true`: Errors are sent to error hooks, BUT NOT thrown (returns `undefined`)
763
+
764
+ **Error Suppression Behavior:**
765
+
766
+ Error hooks **ALWAYS receive errors** regardless of this setting. The `suppressErrors` option only controls whether errors are thrown after error hooks execute.
767
+
768
+ > [!IMPORTANT]
769
+ > **Hooks Must Be Enabled**: Error hooks (and all hooks) only execute when `hooks.enabled: true`. If hooks are disabled, errors are thrown normally without any hook execution.
770
+
771
+ When `suppressErrors: true`, errors are caught and sent to error hooks, but not thrown:
772
+
773
+ ```javascript
774
+ const api = await slothlet({
775
+ dir: "./api",
776
+ hooks: {
777
+ enabled: true,
778
+ pattern: "**",
779
+ suppressErrors: true // Suppress all errors
780
+ }
781
+ });
782
+
783
+ // Register error hook to monitor failures
784
+ api.hooks.on(
785
+ "error-monitor",
786
+ "error",
787
+ ({ path, error, source }) => {
788
+ console.error(`Error in ${path}:`, error.message);
789
+ // Log to monitoring service without crashing
790
+ },
791
+ { pattern: "**" }
792
+ );
793
+
794
+ // Function errors won't crash the application
795
+ const result = await api.riskyOperation();
796
+ if (result === undefined) {
797
+ // Function failed but didn't throw
798
+ console.log("Operation failed gracefully");
799
+ }
800
+ ```
801
+
802
+ **Error Flow:**
803
+
804
+ 1. Error occurs (in before hook, function, or after hook)
805
+ 2. Error hooks execute and receive the error
806
+ 3. **If `suppressErrors: false`** → Error is thrown (crashes if uncaught)
807
+ 4. **If `suppressErrors: true`** → Error is NOT thrown, function returns `undefined`
808
+
809
+ **What Gets Suppressed (when `suppressErrors: true`):**
810
+
811
+ - ✅ Before hook errors → Sent to error hooks, NOT thrown
812
+ - ✅ Function execution errors → Sent to error hooks, NOT thrown
813
+ - ✅ After hook errors → Sent to error hooks, NOT thrown
814
+ - ✅ Always hook errors → Sent to error hooks, never thrown (regardless of setting)
815
+
816
+ > [!TIP]
817
+ > **Use Case**: Enable `suppressErrors: true` for resilient systems where you want to monitor failures without crashing. Perfect for background workers, batch processors, or systems with comprehensive error monitoring.
818
+
819
+ > [!CAUTION]
820
+ > **Critical Operations**: For validation or authorization hooks where errors MUST stop execution, use `suppressErrors: false` (default) to ensure errors propagate normally.
821
+
822
+ #### Hook Types
823
+
824
+ **Four hook types with distinct responsibilities:**
825
+
826
+ - **`before`**: Intercept before function execution
827
+ - Modify arguments passed to functions
828
+ - Cancel execution and return custom values (short-circuit)
829
+ - Execute validation or logging before function runs
830
+ - **`after`**: Transform results after successful execution
831
+ - Transform function return values
832
+ - Only runs if function executes (skipped on short-circuit)
833
+ - Chain multiple transformations in priority order
834
+ - **`always`**: Observe final result with full execution context
835
+ - Always executes after function completes
836
+ - Runs even when `before` hooks cancel execution or errors occur
837
+ - Receives complete context: `{ path, result, hasError, errors }`
838
+ - Cannot modify result (read-only observation)
839
+ - Perfect for unified logging of both success and error scenarios
840
+ - **`error`**: Monitor and handle errors
841
+ - Receives detailed error context with source tracking
842
+ - Error source types: 'before', 'function', 'after', 'always', 'unknown'
843
+ - Includes error type, hook ID, hook tag, timestamp, and stack trace
844
+ - Perfect for error monitoring, logging, and alerting
845
+
846
+ #### Basic Usage
847
+
848
+ ```javascript
849
+ import slothlet from "@cldmv/slothlet";
850
+
851
+ const api = await slothlet({
852
+ dir: "./api",
853
+ hooks: true // Enable hooks
854
+ });
855
+
856
+ // Before hook: Modify arguments
857
+ api.hooks.on(
858
+ "validate-input",
859
+ "before",
860
+ ({ path, args }) => {
861
+ console.log(`Calling ${path} with args:`, args);
862
+ // Return modified args or original
863
+ return [args[0] * 2, args[1] * 2];
864
+ },
865
+ { pattern: "math.add", priority: 100 }
866
+ );
867
+
868
+ // After hook: Transform result
869
+ api.hooks.on(
870
+ "format-output",
871
+ "after",
872
+ ({ path, result }) => {
873
+ console.log(`${path} returned:`, result);
874
+ // Return transformed result
875
+ return result * 10;
876
+ },
877
+ { pattern: "math.*", priority: 100 }
878
+ );
879
+
880
+ // Always hook: Observe final result with error context
881
+ api.hooks.on(
882
+ "log-execution",
883
+ "always",
884
+ ({ path, result, hasError, errors }) => {
885
+ if (hasError) {
886
+ console.log(`${path} failed with ${errors.length} error(s):`, errors);
887
+ } else {
888
+ console.log(`${path} succeeded with result:`, result);
889
+ }
890
+ // Return value ignored - read-only observer
891
+ },
892
+ { pattern: "**" } // All functions
893
+ );
894
+
895
+ // Call function - hooks execute automatically
896
+ const result = await api.math.add(2, 3);
897
+ // Logs: "Calling math.add with args: [2, 3]"
898
+ // Logs: "math.add returned: 10" (4+6)
899
+ // Logs: "Final result for math.add: 100" (10*10)
900
+ // result === 100
901
+ ```
902
+
903
+ #### Short-Circuit Execution
904
+
905
+ `before` hooks can cancel function execution and return custom values:
906
+
907
+ ```javascript
908
+ // Caching hook example
909
+ const cache = new Map();
910
+
911
+ api.hooks.on(
912
+ "cache-check",
913
+ "before",
914
+ ({ path, args }) => {
915
+ const key = JSON.stringify({ path, args });
916
+ if (cache.has(key)) {
917
+ console.log(`Cache hit for ${path}`);
918
+ return cache.get(key); // Short-circuit: return cached value
919
+ }
920
+ // Return undefined to continue to function
921
+ },
922
+ { pattern: "**", priority: 1000 } // High priority
923
+ );
924
+
925
+ api.hooks.on(
926
+ "cache-store",
927
+ "after",
928
+ ({ path, args, result }) => {
929
+ const key = JSON.stringify({ path, args });
930
+ cache.set(key, result);
931
+ return result; // Pass through
932
+ },
933
+ { pattern: "**", priority: 100 }
934
+ );
935
+
936
+ // First call - executes function and caches
937
+ await api.math.add(2, 3); // Computes and stores
938
+
939
+ // Second call - returns cached value (function not executed)
940
+ await api.math.add(2, 3); // Cache hit! No computation
941
+ ```
942
+
943
+ #### Pattern Matching
944
+
945
+ Hooks support flexible pattern matching:
946
+
947
+ ```javascript
948
+ // Exact match
949
+ api.hooks.on("hook1", "before", handler, { pattern: "math.add" });
950
+
951
+ // Wildcard: all functions in namespace
952
+ api.hooks.on("hook2", "before", handler, { pattern: "math.*" });
953
+
954
+ // Wildcard: specific function in all namespaces
955
+ api.hooks.on("hook3", "before", handler, { pattern: "*.add" });
956
+
957
+ // Global: all functions
958
+ api.hooks.on("hook4", "before", handler, { pattern: "**" });
959
+ ```
960
+
961
+ #### Priority and Chaining
962
+
963
+ Multiple hooks execute in priority order (highest first):
964
+
965
+ ```javascript
966
+ // High priority - runs first
967
+ api.hooks.on(
968
+ "validate",
969
+ "before",
970
+ ({ args }) => {
971
+ if (args[0] < 0) throw new Error("Negative numbers not allowed");
972
+ return args;
973
+ },
974
+ { pattern: "math.*", priority: 1000 }
975
+ );
976
+
977
+ // Medium priority - runs second
978
+ api.hooks.on("double", "before", ({ args }) => [args[0] * 2, args[1] * 2], { pattern: "math.*", priority: 500 });
979
+
980
+ // Low priority - runs last
981
+ api.hooks.on(
982
+ "log",
983
+ "before",
984
+ ({ path, args }) => {
985
+ console.log(`Final args for ${path}:`, args);
986
+ return args;
987
+ },
988
+ { pattern: "math.*", priority: 100 }
989
+ );
990
+ ```
991
+
992
+ #### Runtime Control
993
+
994
+ Enable and disable hooks at runtime:
995
+
996
+ ```javascript
997
+ const api = await slothlet({ dir: "./api", hooks: true });
998
+
999
+ // Add hooks
1000
+ api.hooks.on("test", "before", handler, { pattern: "math.*" });
1001
+
1002
+ // Disable all hooks
1003
+ api.hooks.disable();
1004
+ await api.math.add(2, 3); // No hooks execute
1005
+
1006
+ // Re-enable all hooks
1007
+ api.hooks.enable();
1008
+ await api.math.add(2, 3); // Hooks execute
1009
+
1010
+ // Enable specific pattern only
1011
+ api.hooks.disable();
1012
+ api.hooks.enable("math.*"); // Only math.* pattern enabled
1013
+ await api.math.add(2, 3); // math.* hooks execute
1014
+ await api.other.func(); // No hooks execute
1015
+ ```
1016
+
1017
+ #### Hook Management
1018
+
1019
+ ```javascript
1020
+ // List registered hooks
1021
+ const beforeHooks = api.hooks.list("before");
1022
+ const afterHooks = api.hooks.list("after");
1023
+ const allHooks = api.hooks.list(); // All types
1024
+
1025
+ // Remove specific hook by ID
1026
+ const id = api.hooks.on("temp", "before", handler, { pattern: "math.*" });
1027
+ api.hooks.off(id);
1028
+
1029
+ // Remove all hooks matching pattern
1030
+ api.hooks.off("math.*");
1031
+
1032
+ // Clear all hooks of a type
1033
+ api.hooks.clear("before"); // Remove all before hooks
1034
+ api.hooks.clear(); // Remove all hooks
1035
+ ```
1036
+
1037
+ #### Error Handling
1038
+
1039
+ Hooks have a special `error` type for observing function errors with detailed source tracking:
1040
+
1041
+ ```javascript
1042
+ api.hooks.on(
1043
+ "error-logger",
1044
+ "error",
1045
+ ({ path, error, source }) => {
1046
+ console.error(`Error in ${path}:`, error.message);
1047
+ console.error(`Source: ${source.type}`); // 'before', 'after', 'always', 'function', 'unknown'
1048
+
1049
+ if (source.type === "function") {
1050
+ console.error("Error occurred in function execution");
1051
+ } else if (["before", "after", "always"].includes(source.type)) {
1052
+ console.error(`Error occurred in ${source.type} hook:`);
1053
+ console.error(` Hook ID: ${source.hookId}`);
1054
+ console.error(` Hook Tag: ${source.hookTag}`);
1055
+ }
1056
+
1057
+ console.error(`Timestamp: ${source.timestamp}`);
1058
+ console.error(`Stack trace:\n${source.stack}`);
1059
+
1060
+ // Log to monitoring service with full context
1061
+ // Error is re-thrown after all error hooks execute
1062
+ },
1063
+ { pattern: "**" }
1064
+ );
1065
+
1066
+ try {
1067
+ await api.validateData({ invalid: true });
1068
+ } catch (error) {
1069
+ // Error hooks executed before this catch block
1070
+ console.log("Caught error:", error);
1071
+ }
1072
+ ```
1073
+
1074
+ ##### Error Source Tracking
1075
+
1076
+ Error hooks receive detailed context about where errors originated:
1077
+
1078
+ **Source Types:**
1079
+
1080
+ - `"function"`: Error occurred during function execution
1081
+ - `"before"`: Error occurred in a before hook
1082
+ - `"after"`: Error occurred in an after hook
1083
+ - `"always"`: Error occurred in an always hook
1084
+ - `"unknown"`: Error source could not be determined
1085
+
1086
+ **Source Metadata:**
1087
+
1088
+ - `source.type`: Error source type (see above)
1089
+ - `source.hookId`: Hook identifier (for hook errors)
1090
+ - `source.hookTag`: Hook tag/name (for hook errors)
1091
+ - `source.timestamp`: ISO timestamp when error occurred
1092
+ - `source.stack`: Full stack trace
1093
+
1094
+ **Example: Comprehensive Error Monitoring**
1095
+
1096
+ ```javascript
1097
+ const errorStats = {
1098
+ function: 0,
1099
+ before: 0,
1100
+ after: 0,
1101
+ always: 0,
1102
+ byHook: {}
1103
+ };
1104
+
1105
+ api.hooks.on(
1106
+ "error-analytics",
1107
+ "error",
1108
+ ({ path, error, source }) => {
1109
+ // Track error source statistics
1110
+ errorStats[source.type]++;
1111
+
1112
+ if (source.hookId) {
1113
+ if (!errorStats.byHook[source.hookTag]) {
1114
+ errorStats.byHook[source.hookTag] = 0;
1115
+ }
1116
+ errorStats.byHook[source.hookTag]++;
1117
+ }
1118
+
1119
+ // Log detailed error info
1120
+ console.error(`[${source.timestamp}] Error in ${path}:`);
1121
+ console.error(` Type: ${source.type}`);
1122
+ console.error(` Message: ${error.message}`);
1123
+
1124
+ if (source.type === "function") {
1125
+ // Function-level error - might be a bug in implementation
1126
+ console.error(" Action: Review function implementation");
1127
+ } else {
1128
+ // Hook-level error - might be a bug in hook logic
1129
+ console.error(` Action: Review ${source.hookTag} hook (${source.type})`);
1130
+ }
1131
+
1132
+ // Send to monitoring service
1133
+ sendToMonitoring({
1134
+ timestamp: source.timestamp,
1135
+ path,
1136
+ errorType: source.type,
1137
+ hookId: source.hookId,
1138
+ hookTag: source.hookTag,
1139
+ message: error.message,
1140
+ stack: source.stack
1141
+ });
1142
+ },
1143
+ { pattern: "**" }
1144
+ );
1145
+
1146
+ // Later: Analyze error patterns
1147
+ console.log("Error Statistics:", errorStats);
1148
+ // {
1149
+ // function: 5,
1150
+ // before: 2,
1151
+ // after: 1,
1152
+ // always: 0,
1153
+ // byHook: {
1154
+ // "validate-input": 2,
1155
+ // "format-output": 1
1156
+ // }
1157
+ // }
1158
+ ```
1159
+
1160
+ **Important Notes:**
1161
+
1162
+ - Errors from `before` and `after` hooks are re-thrown after error hooks execute
1163
+ - Errors from `always` hooks are caught and logged but do NOT crash execution
1164
+ - Error hooks themselves do not receive errors from other error hooks (no recursion)
1165
+ - The `_hookSourceReported` flag prevents double-reporting of errors
1166
+
1167
+ #### Cross-Mode Compatibility
1168
+
1169
+ Hooks work identically across all configurations:
1170
+
1171
+ ```javascript
1172
+ // Eager + AsyncLocalStorage
1173
+ const api1 = await slothlet({ dir: "./api", lazy: false, runtime: "async", hooks: true });
1174
+
1175
+ // Eager + Live Bindings
1176
+ const api2 = await slothlet({ dir: "./api", lazy: false, runtime: "live", hooks: true });
1177
+
1178
+ // Lazy + AsyncLocalStorage
1179
+ const api3 = await slothlet({ dir: "./api", lazy: true, runtime: "async", hooks: true });
1180
+
1181
+ // Lazy + Live Bindings
1182
+ const api4 = await slothlet({ dir: "./api", lazy: true, runtime: "live", hooks: true });
1183
+
1184
+ // Same hook code works with all configurations
1185
+ [api1, api2, api3, api4].forEach((api) => {
1186
+ api.hooks.on(
1187
+ "universal",
1188
+ "before",
1189
+ ({ args }) => {
1190
+ return [args[0] * 10, args[1] * 10];
1191
+ },
1192
+ { pattern: "math.add" }
1193
+ );
1194
+ });
1195
+ ```
1196
+
1197
+ **Key Benefits:**
1198
+
1199
+ - ✅ **Universal**: Works across all 4 mode/runtime combinations
1200
+ - ✅ **Flexible**: Pattern matching with wildcards and priorities
1201
+ - ✅ **Powerful**: Modify args, transform results, observe execution
1202
+ - ✅ **Composable**: Chain multiple hooks with priority control
1203
+ - ✅ **Dynamic**: Enable/disable at runtime globally or by pattern
1204
+ - ✅ **Observable**: Separate hook types for different responsibilities
1205
+
614
1206
  ### API Mode Configuration
615
1207
 
616
1208
  The `api_mode` option controls how slothlet handles root-level default function exports:
@@ -839,16 +1431,16 @@ flowchart TD
839
1431
  HASDEFAULT -->|Yes| NAMESPACE
840
1432
 
841
1433
  HASDEFAULT -->|No| NAMEDONLY{Only Named Exports?}
842
- NAMEDONLY -->|Yes| FLATTEN[Flatten All Named Exports<br/>api.connect(), api.disconnect()]
1434
+ NAMEDONLY -->|Yes| FLATTEN["Flatten All Named Exports<br/>api.connect, api.disconnect"]
843
1435
 
844
1436
  NAMEDONLY -->|No| SINGLENAMED{Single Named Export<br/>Matching Filename?}
845
1437
  SINGLENAMED -->|Yes| FLATTENSINGLE[Flatten Single Export<br/>api.math]
846
1438
  SINGLENAMED -->|No| NAMESPACE
847
1439
 
848
- style FLATTEN fill:#e1f5fe
849
- style FLATTENSINGLE fill:#e8f5e8
850
- style NAMESPACE fill:#fff3e0
851
- style PRESERVE fill:#fce4ec
1440
+ style FLATTEN fill:#e1f5fe,stroke:#0277bd,stroke-width:2px,color:#000
1441
+ style FLATTENSINGLE fill:#e8f5e8,stroke:#2e7d32,stroke-width:2px,color:#000
1442
+ style NAMESPACE fill:#fff3e0,stroke:#e65100,stroke-width:2px,color:#000
1443
+ style PRESERVE fill:#fce4ec,stroke:#c2185b,stroke-width:2px,color:#000
852
1444
  ```
853
1445
 
854
1446
  ### 🚀 Benefits of Smart Flattening
@@ -1049,7 +1641,7 @@ const syncResult = await api.string.format("Hello"); // Originally sync, but nee
1049
1641
  ## ðŸ›Ą Error Handling
1050
1642
 
1051
1643
  > [!NOTE]
1052
- > **Current Error Behavior**: Slothlet currently uses standard JavaScript error handling. Enhanced error handling with module suggestions is planned for v2.1.0 but not yet implemented.
1644
+ > **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.
1053
1645
 
1054
1646
  **Current behavior:**
1055
1647
 
@@ -1062,7 +1654,7 @@ try {
1062
1654
  }
1063
1655
  ```
1064
1656
 
1065
- **Planned Enhanced Error Features (v2.1.0):**
1657
+ **Planned Enhanced Error Features (v3.0.0):**
1066
1658
 
1067
1659
  > [!TIP]
1068
1660
  > **Coming Soon**: Enhanced error handling with descriptive messages and module suggestions:
@@ -1123,7 +1715,7 @@ try {
1123
1715
  2. **Configuration**: New options (`api_mode`, `context`, `reference`)
1124
1716
  3. **Function names**: Enhanced preservation of original capitalization
1125
1717
  4. **Module structure**: Mixed ESM/CJS support
1126
- 5. **Live bindings**: New runtime system with AsyncLocalStorage
1718
+ 5. **Live bindings**: Dual runtime system with AsyncLocalStorage and live-bindings options
1127
1719
 
1128
1720
  ### Migration Steps
1129
1721
 
@@ -1234,7 +1826,7 @@ Apache-2.0 ÂĐ Shinrai / CLDMV
1234
1826
 
1235
1827
  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.
1236
1828
 
1237
- **🎉 Welcome to the future of module loading with Slothlet v2.0!**
1829
+ **🎉 Welcome to the future of module loading with Slothlet!**
1238
1830
 
1239
1831
  > _Where sophisticated architecture meets blazing performance - slothlet is anything but slow._
1240
1832