@cldmv/slothlet 2.3.0 → 2.3.2
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 +146 -3
- package/dist/lib/engine/slothlet_child.mjs +1 -0
- package/dist/lib/engine/slothlet_engine.mjs +1 -0
- package/dist/lib/engine/slothlet_esm.mjs +1 -0
- package/dist/lib/engine/slothlet_helpers.mjs +2 -1
- package/dist/lib/engine/slothlet_worker.mjs +1 -0
- package/dist/lib/helpers/als-eventemitter.mjs +119 -0
- package/dist/lib/helpers/auto-wrap.mjs +60 -0
- package/dist/lib/helpers/resolve-from-caller.mjs +1 -0
- package/dist/lib/helpers/sanitize.mjs +2 -0
- package/dist/lib/modes/slothlet_eager.mjs +4 -2
- package/dist/lib/modes/slothlet_lazy.mjs +4 -2
- package/dist/lib/runtime/runtime.mjs +73 -3
- package/dist/slothlet.mjs +7 -20
- package/index.cjs +4 -4
- package/index.mjs +2 -2
- package/package.json +2 -1
- package/types/dist/lib/engine/slothlet_engine.d.mts +1 -1
- package/types/dist/lib/engine/slothlet_engine.d.mts.map +1 -1
- package/types/dist/lib/engine/slothlet_esm.d.mts.map +1 -1
- package/types/dist/lib/engine/slothlet_helpers.d.mts +2 -1
- package/types/dist/lib/engine/slothlet_helpers.d.mts.map +1 -1
- package/types/dist/lib/helpers/als-eventemitter.d.mts +23 -0
- package/types/dist/lib/helpers/als-eventemitter.d.mts.map +1 -0
- package/types/dist/lib/helpers/auto-wrap.d.mts +49 -0
- package/types/dist/lib/helpers/auto-wrap.d.mts.map +1 -0
- package/types/dist/lib/helpers/resolve-from-caller.d.mts +14 -14
- package/types/dist/lib/helpers/resolve-from-caller.d.mts.map +1 -1
- package/types/dist/lib/helpers/sanitize.d.mts.map +1 -1
- package/types/dist/lib/modes/slothlet_eager.d.mts.map +1 -1
- package/types/dist/lib/modes/slothlet_lazy.d.mts.map +1 -1
- package/types/dist/lib/runtime/runtime.d.mts +9 -0
- package/types/dist/lib/runtime/runtime.d.mts.map +1 -1
- package/types/dist/slothlet.d.mts +1 -8
- package/types/dist/slothlet.d.mts.map +1 -1
- package/types/index.d.mts +0 -1
package/README.md
CHANGED
|
@@ -33,9 +33,9 @@ The name might suggest we're taking it easy, but don't be fooled. **Slothlet del
|
|
|
33
33
|
|
|
34
34
|
---
|
|
35
35
|
|
|
36
|
-
## ✨ What's New in v2.
|
|
36
|
+
## ✨ What's New in v2.x
|
|
37
37
|
|
|
38
|
-
### 🎯 **Complete Architectural Rewrite**
|
|
38
|
+
### 🎯 **Complete Architectural Rewrite (v2.0)**
|
|
39
39
|
|
|
40
40
|
v2.0 represents a ground-up rewrite with enterprise-grade features:
|
|
41
41
|
|
|
@@ -67,6 +67,13 @@ v2.0 represents a ground-up rewrite with enterprise-grade features:
|
|
|
67
67
|
- **Memory**: On-demand loading scales with actual usage
|
|
68
68
|
- **Predictability**: Consistent performance characteristics per mode
|
|
69
69
|
|
|
70
|
+
### 🔄 **Context Propagation (v2.3)** ⭐ NEW
|
|
71
|
+
|
|
72
|
+
- **EventEmitter Context Propagation**: Automatic context preservation across EventEmitter callbacks using AsyncResource patterns
|
|
73
|
+
- **Class Instance Context Propagation**: Automatic context preservation across class method calls with transparent wrapping
|
|
74
|
+
- **AsyncResource Integration**: Production-ready context management following Node.js best practices
|
|
75
|
+
- **Zero Configuration**: Works automatically with TCP servers, HTTP servers, and any EventEmitter-based patterns
|
|
76
|
+
|
|
70
77
|
---
|
|
71
78
|
|
|
72
79
|
## 🚀 Key Features
|
|
@@ -332,7 +339,7 @@ Creates and loads an API instance with the specified configuration.
|
|
|
332
339
|
| `debug` | `boolean` | `false` | Enable verbose logging. Can also be set via `--slothletdebug` command line flag or `SLOTHLET_DEBUG=true` environment variable |
|
|
333
340
|
| `mode` | `string` | `"singleton"` | Execution environment mode - `"singleton"`, `"vm"`, `"worker"`, or `"fork"` |
|
|
334
341
|
| `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) |
|
|
335
|
-
| `context` | `object` | `{}` | Context data object injected into live-binding `context` reference. Available to all loaded modules via `import { context } from
|
|
342
|
+
| `context` | `object` | `{}` | Context data object injected into live-binding `context` reference. Available to all loaded modules via `import { context } from "@cldmv/slothlet/runtime"` |
|
|
336
343
|
| `reference` | `object` | `{}` | Reference object merged into the API root level. Properties not conflicting with loaded modules are added directly to the API |
|
|
337
344
|
| `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 |
|
|
338
345
|
|
|
@@ -419,6 +426,142 @@ function cjsFunction(data) {
|
|
|
419
426
|
module.exports = { cjsFunction };
|
|
420
427
|
```
|
|
421
428
|
|
|
429
|
+
### EventEmitter Context Propagation
|
|
430
|
+
|
|
431
|
+
Slothlet automatically preserves AsyncLocalStorage context across all EventEmitter callbacks using Node.js AsyncResource patterns. This ensures your API modules maintain full context access in event handlers without any configuration.
|
|
432
|
+
|
|
433
|
+
```javascript
|
|
434
|
+
// api/tcp-server.mjs - Your API module
|
|
435
|
+
import { self, context } from "@cldmv/slothlet/runtime";
|
|
436
|
+
import net from "node:net";
|
|
437
|
+
|
|
438
|
+
export function createTcpServer() {
|
|
439
|
+
const server = net.createServer();
|
|
440
|
+
|
|
441
|
+
// Connection handler maintains full context automatically
|
|
442
|
+
server.on("connection", (socket) => {
|
|
443
|
+
console.log(`User: ${context.user}`); // ✅ Context preserved
|
|
444
|
+
console.log(`API keys: ${Object.keys(self).length}`); // ✅ Full API access
|
|
445
|
+
|
|
446
|
+
// Socket data handler also maintains context automatically
|
|
447
|
+
socket.on("data", (data) => {
|
|
448
|
+
console.log(`Session: ${context.session}`); // ✅ Context preserved
|
|
449
|
+
console.log(`Processing for: ${context.user}`); // ✅ Context preserved
|
|
450
|
+
|
|
451
|
+
// Full API access in nested event handlers
|
|
452
|
+
const processed = self.dataProcessor.handle(data.toString());
|
|
453
|
+
socket.write(processed);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
socket.on("error", (err) => {
|
|
457
|
+
// Error handlers also maintain context
|
|
458
|
+
self.logger.error(`Error for user ${context.user}: ${err.message}`);
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
return server;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
export function startServer(port = 3000) {
|
|
466
|
+
const server = createTcpServer();
|
|
467
|
+
server.listen(port);
|
|
468
|
+
return server;
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
```javascript
|
|
473
|
+
// Usage in your application
|
|
474
|
+
import slothlet from "@cldmv/slothlet";
|
|
475
|
+
|
|
476
|
+
const api = await slothlet({
|
|
477
|
+
dir: "./api",
|
|
478
|
+
context: { user: "alice", session: "tcp-session" }
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
// Start the server - all event handlers will have full context
|
|
482
|
+
const server = api.startServer(8080);
|
|
483
|
+
console.log("TCP server started with context preservation");
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
**Key Benefits:**
|
|
487
|
+
|
|
488
|
+
- ✅ **Automatic**: No configuration needed - works transparently in all API modules
|
|
489
|
+
- ✅ **Complete Context**: Full `context` object and `self` API access in all event handlers
|
|
490
|
+
- ✅ **Nested Events**: Works with any depth of EventEmitter nesting (server → socket → custom emitters)
|
|
491
|
+
- ✅ **Universal Support**: All EventEmitter methods (`on`, `once`, `addListener`) are automatically context-aware
|
|
492
|
+
- ✅ **Production Ready**: Uses Node.js AsyncResource patterns for reliable context propagation
|
|
493
|
+
- ✅ **Zero Overhead**: Only wraps listeners when context is active, minimal performance impact
|
|
494
|
+
|
|
495
|
+
> [!TIP]
|
|
496
|
+
> **Automatic Context Propagation**: EventEmitter context propagation works automatically in both lazy and eager modes. TCP servers, HTTP servers, custom EventEmitters, and any other event-driven patterns in your API modules will maintain full slothlet context and API access without any code changes.
|
|
497
|
+
|
|
498
|
+
### Class Instance Context Propagation
|
|
499
|
+
|
|
500
|
+
Slothlet automatically preserves AsyncLocalStorage context across all class instance method calls. When your API functions return class instances, slothlet wraps them transparently to ensure all method calls maintain full context access.
|
|
501
|
+
|
|
502
|
+
```javascript
|
|
503
|
+
// api/data-processor.mjs - Your API module
|
|
504
|
+
import { self, context } from "@cldmv/slothlet/runtime";
|
|
505
|
+
|
|
506
|
+
class DataProcessor {
|
|
507
|
+
constructor(config) {
|
|
508
|
+
this.config = config;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
process(data) {
|
|
512
|
+
// Context automatically available in all methods
|
|
513
|
+
console.log(`Processing for user: ${context.user}`); // ✅ Context preserved
|
|
514
|
+
console.log(`Request ID: ${context.requestId}`); // ✅ Context preserved
|
|
515
|
+
|
|
516
|
+
// Full API access in class methods
|
|
517
|
+
const validated = self.validator.check(data);
|
|
518
|
+
return this.transform(validated);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
transform(data) {
|
|
522
|
+
// Context preserved in nested method calls
|
|
523
|
+
console.log(`Transforming for: ${context.user}`); // ✅ Context preserved
|
|
524
|
+
|
|
525
|
+
// Call other API modules from class methods
|
|
526
|
+
return self.utils.format(data);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
export function createProcessor(config) {
|
|
531
|
+
// Return class instance - slothlet automatically wraps it
|
|
532
|
+
return new DataProcessor(config);
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
```javascript
|
|
537
|
+
// Usage in your application
|
|
538
|
+
import slothlet from "@cldmv/slothlet";
|
|
539
|
+
|
|
540
|
+
const api = await slothlet({
|
|
541
|
+
dir: "./api",
|
|
542
|
+
context: { user: "alice", requestId: "req-123" }
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
// Create processor instance - all methods will have full context
|
|
546
|
+
const processor = api.createProcessor({ format: "json" });
|
|
547
|
+
|
|
548
|
+
// All method calls maintain context automatically
|
|
549
|
+
const result = processor.process({ data: "test" });
|
|
550
|
+
console.log("Processing completed with context preservation");
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
**Key Benefits:**
|
|
554
|
+
|
|
555
|
+
- ✅ **Automatic**: Class instances returned from API functions are automatically context-aware
|
|
556
|
+
- ✅ **Transparent**: No code changes needed - works with existing class patterns
|
|
557
|
+
- ✅ **Complete Context**: Full `context` object and `self` API access in all class methods
|
|
558
|
+
- ✅ **Nested Methods**: Context preserved across method chains and internal calls
|
|
559
|
+
- ✅ **Constructor Support**: Context preserved for both function calls and `new` constructor usage
|
|
560
|
+
- ✅ **Performance Optimized**: Method wrapping is cached to avoid overhead on repeated calls
|
|
561
|
+
|
|
562
|
+
> [!TIP]
|
|
563
|
+
> **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.
|
|
564
|
+
|
|
422
565
|
### API Mode Configuration
|
|
423
566
|
|
|
424
567
|
The `api_mode` option controls how slothlet handles root-level default function exports:
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
|
|
18
19
|
import vm from "node:vm";
|
|
19
20
|
import fs from "node:fs/promises";
|
|
20
21
|
import path from "node:path";
|
|
@@ -389,7 +390,7 @@ export async function bootSlothletVM(context, entryUrl, loadConfig, ctxRef) {
|
|
|
389
390
|
const ret = await globalThis.slothlet.load(__loadConfig, __ctxRef);
|
|
390
391
|
globalThis.self = global.self = ret;
|
|
391
392
|
const ref = __ctxRef?.reference;
|
|
392
|
-
if (ref && typeof ref ===
|
|
393
|
+
if (ref && typeof ref === "object") {
|
|
393
394
|
for (const k of Object.keys(ref)) if (!(k in globalThis.self)) {
|
|
394
395
|
try { globalThis.self[k] = global.self[k] = ref[k]; } catch {}
|
|
395
396
|
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2025 CLDMV/Shinrai
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
import { AsyncResource } from "node:async_hooks";
|
|
22
|
+
import { EventEmitter } from "node:events";
|
|
23
|
+
import { sharedALS } from "../runtime/runtime.mjs";
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
export function enableAlsForEventEmitters(als = sharedALS) {
|
|
27
|
+
|
|
28
|
+
const kPatched = Symbol.for("slothlet.als.patched");
|
|
29
|
+
|
|
30
|
+
if (EventEmitter.prototype[kPatched]) return;
|
|
31
|
+
EventEmitter.prototype[kPatched] = true;
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
const kMap = Symbol("slothlet.als.listenerMap");
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
function runtime_ensureMap(emitter) {
|
|
38
|
+
if (!emitter[kMap]) emitter[kMap] = new WeakMap();
|
|
39
|
+
return emitter[kMap];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
function runtime_wrapListener(listener) {
|
|
44
|
+
|
|
45
|
+
const store = als.getStore();
|
|
46
|
+
if (!store) return listener;
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
const resource = new AsyncResource("slothlet-als-listener");
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
const runtime_wrappedListener = function (...args) {
|
|
53
|
+
return resource.runInAsyncScope(
|
|
54
|
+
() => {
|
|
55
|
+
return listener.apply(this, args);
|
|
56
|
+
},
|
|
57
|
+
this,
|
|
58
|
+
...args
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
return runtime_wrappedListener;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const proto = EventEmitter.prototype;
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
const origOn = proto.on;
|
|
69
|
+
const origOnce = proto.once;
|
|
70
|
+
const origAdd = proto.addListener;
|
|
71
|
+
const origPre = proto.prependListener;
|
|
72
|
+
const origPreO = proto.prependOnceListener;
|
|
73
|
+
const origOff = proto.off ?? proto.removeListener;
|
|
74
|
+
const origRem = proto.removeListener;
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
function runtime_installWrapper(addFnName, orig) {
|
|
78
|
+
proto[addFnName] = function (event, listener) {
|
|
79
|
+
const map = runtime_ensureMap(this);
|
|
80
|
+
const wrapped = runtime_wrapListener(listener);
|
|
81
|
+
if (wrapped !== listener) map.set(listener, wrapped);
|
|
82
|
+
return orig.call(this, event, wrapped);
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
runtime_installWrapper("on", origOn);
|
|
88
|
+
runtime_installWrapper("once", origOnce);
|
|
89
|
+
runtime_installWrapper("addListener", origAdd);
|
|
90
|
+
if (origPre) runtime_installWrapper("prependListener", origPre);
|
|
91
|
+
if (origPreO) runtime_installWrapper("prependOnceListener", origPreO);
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
function runtime_createRemoveWrapper(method) {
|
|
95
|
+
|
|
96
|
+
const runtime_removeWrapper = function (event, listener) {
|
|
97
|
+
const map = runtime_ensureMap(this);
|
|
98
|
+
const wrapped = map.get(listener) || listener;
|
|
99
|
+
map.delete(listener);
|
|
100
|
+
return method.call(this, event, wrapped);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
return runtime_removeWrapper;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
if (proto.off) proto.off = runtime_createRemoveWrapper(origOff);
|
|
108
|
+
proto.removeListener = runtime_createRemoveWrapper(origRem);
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
const origRemoveAll = proto.removeAllListeners;
|
|
112
|
+
proto.removeAllListeners = function (event) {
|
|
113
|
+
const res = origRemoveAll.call(this, event);
|
|
114
|
+
if (this[kMap]) this[kMap] = new WeakMap();
|
|
115
|
+
return res;
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2025 CLDMV/Shinrai
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
export async function autoWrapEventEmitters(nodeModule) {
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const { self } = await import("@cldmv/slothlet/runtime");
|
|
26
|
+
if (!self?.__ctx) {
|
|
27
|
+
|
|
28
|
+
return nodeModule;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const { makeWrapper } = await import("../runtime/runtime.mjs");
|
|
32
|
+
const wrapper = makeWrapper(self.__ctx);
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
const wrappedModule = { ...nodeModule };
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
if (typeof nodeModule.createServer === "function") {
|
|
39
|
+
const originalCreateServer = nodeModule.createServer;
|
|
40
|
+
wrappedModule.createServer = function (...args) {
|
|
41
|
+
const server = originalCreateServer.apply(this, args);
|
|
42
|
+
return wrapper(server);
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return wrappedModule;
|
|
47
|
+
} catch (err) {
|
|
48
|
+
|
|
49
|
+
console.error("autoWrapEventEmitters error:", err);
|
|
50
|
+
return nodeModule;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
export async function getNet() {
|
|
56
|
+
const originalNet = await import("node:net");
|
|
57
|
+
return autoWrapEventEmitters(originalNet.default || originalNet);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
@@ -16,12 +16,21 @@
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
|
|
20
|
+
|
|
19
21
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
20
22
|
import util from "node:util";
|
|
23
|
+
import { enableAlsForEventEmitters } from "@cldmv/slothlet/helpers/als-eventemitter";
|
|
21
24
|
|
|
22
25
|
const als = new AsyncLocalStorage();
|
|
23
26
|
|
|
24
27
|
|
|
28
|
+
export const sharedALS = new AsyncLocalStorage();
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
enableAlsForEventEmitters(als);
|
|
32
|
+
|
|
33
|
+
|
|
25
34
|
export const runWithCtx = (ctx, fn, thisArg, args) => {
|
|
26
35
|
|
|
27
36
|
const runtime_runInALS = () => {
|
|
@@ -41,6 +50,9 @@ const EXCLUDED_CONSTRUCTORS = new Set([Object, Array, Promise, Date, RegExp, Err
|
|
|
41
50
|
const EXCLUDED_INSTANCEOF_CLASSES = [ArrayBuffer, Map, Set, WeakMap, WeakSet];
|
|
42
51
|
|
|
43
52
|
|
|
53
|
+
const PROMISE_METHODS = new Set(["then", "catch", "finally"]);
|
|
54
|
+
|
|
55
|
+
|
|
44
56
|
function runtime_shouldWrapMethod(value, prop) {
|
|
45
57
|
return (
|
|
46
58
|
typeof value === "function" &&
|
|
@@ -127,6 +139,7 @@ function runtime_wrapClassInstance(instance, ctx, wrapFn, instanceCache) {
|
|
|
127
139
|
export const makeWrapper = (ctx) => {
|
|
128
140
|
const cache = new WeakMap();
|
|
129
141
|
const instanceCache = new WeakMap();
|
|
142
|
+
const promiseMethodCache = new WeakMap();
|
|
130
143
|
const wrap = (val) => {
|
|
131
144
|
if (val == null || (typeof val !== "object" && typeof val !== "function")) return val;
|
|
132
145
|
if (cache.has(val)) return cache.get(val);
|
|
@@ -165,11 +178,66 @@ export const makeWrapper = (ctx) => {
|
|
|
165
178
|
return result;
|
|
166
179
|
},
|
|
167
180
|
get(target, prop, receiver) {
|
|
168
|
-
|
|
181
|
+
const value = Reflect.get(target, prop, receiver);
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
const isPromiseMethod = typeof value === "function" && PROMISE_METHODS.has(prop);
|
|
186
|
+
const isNativePromise = util.types.isPromise(target);
|
|
187
|
+
const hasThen = typeof target?.then === "function";
|
|
188
|
+
|
|
189
|
+
if (isPromiseMethod && (isNativePromise || hasThen)) {
|
|
190
|
+
|
|
191
|
+
let targetMethodCache = promiseMethodCache.get(target);
|
|
192
|
+
if (!targetMethodCache) {
|
|
193
|
+
targetMethodCache = new Map();
|
|
194
|
+
promiseMethodCache.set(target, targetMethodCache);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (targetMethodCache.has(prop)) {
|
|
198
|
+
return targetMethodCache.get(prop);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const wrappedMethod = function (...args) {
|
|
202
|
+
|
|
203
|
+
const wrappedArgs = args.map((arg) => {
|
|
204
|
+
if (typeof arg === "function") {
|
|
205
|
+
return function (...callbackArgs) {
|
|
206
|
+
return runWithCtx(ctx, arg, undefined, callbackArgs);
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
return arg;
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
const result = Reflect.apply(value, target, wrappedArgs);
|
|
214
|
+
|
|
215
|
+
return wrap(result);
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
targetMethodCache.set(prop, wrappedMethod);
|
|
219
|
+
return wrappedMethod;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return wrap(value);
|
|
223
|
+
},
|
|
224
|
+
set(target, prop, value, receiver) {
|
|
225
|
+
|
|
226
|
+
const methodCache = promiseMethodCache.get(target);
|
|
227
|
+
if (methodCache && methodCache.has(prop)) {
|
|
228
|
+
methodCache.delete(prop);
|
|
229
|
+
}
|
|
230
|
+
return Reflect.set(target, prop, value, receiver);
|
|
169
231
|
},
|
|
170
|
-
set: Reflect.set,
|
|
171
232
|
defineProperty: Reflect.defineProperty,
|
|
172
|
-
deleteProperty
|
|
233
|
+
deleteProperty(target, prop) {
|
|
234
|
+
|
|
235
|
+
const methodCache = promiseMethodCache.get(target);
|
|
236
|
+
if (methodCache && methodCache.has(prop)) {
|
|
237
|
+
methodCache.delete(prop);
|
|
238
|
+
}
|
|
239
|
+
return Reflect.deleteProperty(target, prop);
|
|
240
|
+
},
|
|
173
241
|
ownKeys: Reflect.ownKeys,
|
|
174
242
|
getOwnPropertyDescriptor: Reflect.getOwnPropertyDescriptor,
|
|
175
243
|
has: Reflect.has
|
|
@@ -352,3 +420,5 @@ export const context = runtime_createLiveBinding("context");
|
|
|
352
420
|
|
|
353
421
|
|
|
354
422
|
export const reference = runtime_createLiveBinding("reference");
|
|
423
|
+
|
|
424
|
+
|
package/dist/slothlet.mjs
CHANGED
|
@@ -15,9 +15,11 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
import
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
import fs from "node:fs/promises";
|
|
21
|
+
import path from "node:path";
|
|
22
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
21
23
|
|
|
22
24
|
import { resolvePathFromCaller } from "@cldmv/slothlet/helpers/resolve-from-caller";
|
|
23
25
|
import { sanitizePathName } from "@cldmv/slothlet/helpers/sanitize";
|
|
@@ -38,12 +40,6 @@ let DEBUG = process.argv.includes("--slothletdebug")
|
|
|
38
40
|
: false;
|
|
39
41
|
|
|
40
42
|
|
|
41
|
-
export let _slothlet = new URL(import.meta.url).searchParams.get("_slothlet") || "";
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
43
|
export const self = {};
|
|
48
44
|
|
|
49
45
|
|
|
@@ -152,7 +148,7 @@ const slothletObject = {
|
|
|
152
148
|
|
|
153
149
|
|
|
154
150
|
|
|
155
|
-
|
|
151
|
+
|
|
156
152
|
const imported = await import(modeUrl);
|
|
157
153
|
if (imported && typeof imported === "object") {
|
|
158
154
|
this.modes[modeName] = imported.default || imported;
|
|
@@ -167,9 +163,6 @@ const slothletObject = {
|
|
|
167
163
|
|
|
168
164
|
const { entry = import.meta.url, mode = "singleton", api_mode = "auto" } = options ?? {};
|
|
169
165
|
|
|
170
|
-
_slothlet = new URL(import.meta.url).searchParams.get("_slothlet") || "";
|
|
171
|
-
|
|
172
|
-
|
|
173
166
|
|
|
174
167
|
|
|
175
168
|
|
|
@@ -244,8 +237,6 @@ const slothletObject = {
|
|
|
244
237
|
|
|
245
238
|
}
|
|
246
239
|
|
|
247
|
-
|
|
248
|
-
|
|
249
240
|
if (this.loaded) return this.api;
|
|
250
241
|
if (this.config.lazy) {
|
|
251
242
|
this.api = await this.modes.lazy.create.call(this, apiDir, true, this.config.apiDepth || Infinity, 0);
|
|
@@ -284,8 +275,6 @@ const slothletObject = {
|
|
|
284
275
|
|
|
285
276
|
const _boundapi = this.createBoundApi(l_ctxRef.reference);
|
|
286
277
|
|
|
287
|
-
|
|
288
|
-
|
|
289
278
|
mutateLiveBindingFunction(this.boundapi, _boundapi);
|
|
290
279
|
|
|
291
280
|
this.updateBindings(this.context, this.reference, this.boundapi);
|
|
@@ -609,8 +598,7 @@ const slothletObject = {
|
|
|
609
598
|
|
|
610
599
|
async _loadSingleModule(modulePath, rootLevel = false) {
|
|
611
600
|
const moduleUrl = pathToFileURL(modulePath).href;
|
|
612
|
-
|
|
613
|
-
|
|
601
|
+
|
|
614
602
|
|
|
615
603
|
const module = await import(moduleUrl);
|
|
616
604
|
|
|
@@ -619,7 +607,6 @@ const slothletObject = {
|
|
|
619
607
|
|
|
620
608
|
|
|
621
609
|
|
|
622
|
-
|
|
623
610
|
if (this.config.debug) console.log("module: ", module);
|
|
624
611
|
|
|
625
612
|
if (typeof module.default === "function") {
|
package/index.cjs
CHANGED
|
@@ -33,8 +33,8 @@ const modPromise = import("@cldmv/slothlet/slothlet");
|
|
|
33
33
|
* @returns {Promise<function|object>} The bound API object with live-binding context
|
|
34
34
|
*
|
|
35
35
|
* @example // CJS
|
|
36
|
-
* const slothlet = require(
|
|
37
|
-
* const api = await slothlet({ dir:
|
|
36
|
+
* const slothlet = require("@cldmv/slothlet");
|
|
37
|
+
* const api = await slothlet({ dir: "./api", context: { user: "alice" } });
|
|
38
38
|
* console.log(api.config.username); // Access configuration
|
|
39
39
|
*/
|
|
40
40
|
async function slothlet(options = {}) {
|
|
@@ -75,7 +75,7 @@ module.exports = slothlet;
|
|
|
75
75
|
* @type {Function}
|
|
76
76
|
*
|
|
77
77
|
* @example // CJS named destructuring
|
|
78
|
-
* const { slothlet } = require(
|
|
79
|
-
* const api = await slothlet({ dir:
|
|
78
|
+
* const { slothlet } = require("@cldmv/slothlet");
|
|
79
|
+
* const api = await slothlet({ dir: "./api" });
|
|
80
80
|
*/
|
|
81
81
|
module.exports.slothlet = slothlet; // optional named alias
|
package/index.mjs
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* @returns {Promise<function|object>} The bound API object with live-binding context
|
|
31
31
|
*
|
|
32
32
|
* @example // ESM
|
|
33
|
-
* import slothlet from
|
|
33
|
+
* import slothlet from "@cldmv/slothlet";
|
|
34
34
|
* const api = await slothlet({ dir: './api', lazy: true });
|
|
35
35
|
* const result = await api.math.add(2, 3); // 5
|
|
36
36
|
*
|
|
@@ -69,7 +69,7 @@ export default async function slothlet(options = {}) {
|
|
|
69
69
|
* @type {Function}
|
|
70
70
|
*
|
|
71
71
|
* @example // ESM named import
|
|
72
|
-
* import { slothlet } from
|
|
72
|
+
* import { slothlet } from "@cldmv/slothlet";
|
|
73
73
|
* const api = await slothlet({ dir: './api' });
|
|
74
74
|
*/
|
|
75
75
|
// Optional named alias
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cldmv/slothlet",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.2",
|
|
4
4
|
"moduleVersions": {
|
|
5
5
|
"lazy": "1.0.0",
|
|
6
6
|
"eager": "1.0.0"
|
|
@@ -139,6 +139,7 @@
|
|
|
139
139
|
"@eslint/markdown": "^7.2.0",
|
|
140
140
|
"@html-eslint/eslint-plugin": "^0.45.0",
|
|
141
141
|
"@html-eslint/parser": "^0.45.0",
|
|
142
|
+
"@types/node": "^24.9.1",
|
|
142
143
|
"chalk": "^5.6.0",
|
|
143
144
|
"dmd": "^7.1.1",
|
|
144
145
|
"eslint": "^9.33.0",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @param {Function} fn - Shutdown function to set
|
|
4
4
|
* @returns {Function} Previously set shutdown function
|
|
5
5
|
* @example
|
|
6
|
-
* const prev = setShutdown(() => console.log(
|
|
6
|
+
* const prev = setShutdown(() => console.log("Shutting down"));
|
|
7
7
|
*/
|
|
8
8
|
export function setShutdown(fn: Function): Function;
|
|
9
9
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slothlet_engine.d.mts","sourceRoot":"","sources":["../../../../dist/lib/engine/slothlet_engine.mjs"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"slothlet_engine.d.mts","sourceRoot":"","sources":["../../../../dist/lib/engine/slothlet_engine.mjs"],"names":[],"mappings":"AAgCA;;;;;;GAMG;AACH,oDAIC;AAED;;;;;;;;;;;;;;GAcG;AACH,yCAZG;IAA2B,KAAK,EAAxB,MAAM;IACc,IAAI,GAAxB,MAAM;IACc,OAAO,GAA3B,MAAM;IACc,SAAS,GAA7B,MAAM;CACd,GAAU,OAAO,CAAC,MAAM,CAAC,CAgC3B;AAyRD,qDA4BC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slothlet_esm.d.mts","sourceRoot":"","sources":["../../../../dist/lib/engine/slothlet_esm.mjs"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"slothlet_esm.d.mts","sourceRoot":"","sources":["../../../../dist/lib/engine/slothlet_esm.mjs"],"names":[],"mappings":"AAmBA;;;;;;;;;;GAUG;AACH,+CAPW,MAAM,WACN,MAAM,YACN,GAAG,CAAC,MAAM,CAAC,GACT,OAAO,CAAC,MAAM,CAAC,CAkM3B;AAED;;;;GAIG;AACH,mCAHW,MAAM,GACJ,OAAO,CAAC,OAAO,CAAC,CAW5B"}
|
|
@@ -5,7 +5,7 @@ export function installPortalForSelf(): void;
|
|
|
5
5
|
export function asUrl(p: any): any;
|
|
6
6
|
export function isPlainObject(o: any): boolean;
|
|
7
7
|
export function guessName(v: any): any;
|
|
8
|
-
export function makeNodeishContext():
|
|
8
|
+
export function makeNodeishContext(): vm.Context;
|
|
9
9
|
/**
|
|
10
10
|
* Loads a module into a VM context, supporting ESM (mjs), CJS (cjs), or auto-detection.
|
|
11
11
|
* @param {object} context - The VM context.
|
|
@@ -21,4 +21,5 @@ export function marshalArgsReplaceFunctions(value: any, registerCb: any): any;
|
|
|
21
21
|
export function reviveArgsReplaceTokens(value: any, invokeCb: any): any;
|
|
22
22
|
export function containsFunction(value: any): boolean;
|
|
23
23
|
export const HAS_STM: boolean;
|
|
24
|
+
import vm from "node:vm";
|
|
24
25
|
//# sourceMappingURL=slothlet_helpers.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slothlet_helpers.d.mts","sourceRoot":"","sources":["../../../../dist/lib/engine/slothlet_helpers.mjs"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"slothlet_helpers.d.mts","sourceRoot":"","sources":["../../../../dist/lib/engine/slothlet_helpers.mjs"],"names":[],"mappings":"AAuBA,gDAwBC;AAED,oEAGC;AAED,yEAYC;AAED,6CA6BC;AAED,mCAEC;AAED,+CAEC;AAED,uCAOC;AAQD,iDAyBC;AAED;;;;;;GAMG;AACH,sCALW,MAAM,WACN,MAAM,SACN,MAAM,mBACJ,OAAO,CAAC,MAAM,CAAC,CA2L3B;AAGD,sEAsDC;AAED,8EAYC;AAED,yGAmDC;AAID,8EAaC;AAED,wEAaC;AAED,sDAKC;AAhYD,8BAAiE;eA3GlD,SAAS"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enable AsyncLocalStorage context propagation for all EventEmitter instances.
|
|
3
|
+
*
|
|
4
|
+
* @function enableAlsForEventEmitters
|
|
5
|
+
* @package
|
|
6
|
+
* @param {AsyncLocalStorage} [als=sharedALS] - The AsyncLocalStorage instance to use (defaults to slothlet's shared instance)
|
|
7
|
+
*
|
|
8
|
+
* @description
|
|
9
|
+
* Patches EventEmitter.prototype to automatically preserve AsyncLocalStorage context
|
|
10
|
+
* in event listeners using AsyncResource. This ensures that event handlers maintain
|
|
11
|
+
* the same context that was active when they were registered.
|
|
12
|
+
*
|
|
13
|
+
* Uses Node.js AsyncResource API for proper context propagation, following
|
|
14
|
+
* official guidance for AsyncLocalStorage across callback boundaries.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // Enable ALS for all EventEmitters
|
|
18
|
+
* import { enableAlsForEventEmitters } from "./als-eventemitter.mjs";
|
|
19
|
+
* enableAlsForEventEmitters(als);
|
|
20
|
+
*/
|
|
21
|
+
export function enableAlsForEventEmitters(als?: AsyncLocalStorage): void;
|
|
22
|
+
export type AsyncLocalStorage = import("async_hooks").AsyncLocalStorage<any>;
|
|
23
|
+
//# sourceMappingURL=als-eventemitter.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"als-eventemitter.d.mts","sourceRoot":"","sources":["../../../../dist/lib/helpers/als-eventemitter.mjs"],"names":[],"mappings":"AAoCA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,gDAfW,iBAAiB,QAoK3B"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @Project: @cldmv/slothlet
|
|
3
|
+
* @Filename: /src/lib/helpers/auto-wrap.mjs
|
|
4
|
+
* @Date: 2025-10-21 13:32:36 -07:00 (1761078756)
|
|
5
|
+
* @Author: Nate Hyson <CLDMV>
|
|
6
|
+
* @Email: <Shinrai@users.noreply.github.com>
|
|
7
|
+
* -----
|
|
8
|
+
* @Last modified by: Nate Hyson <CLDMV> (Shinrai@users.noreply.github.com)
|
|
9
|
+
* @Last modified time: 2025-10-22 06:59:17 -07:00 (1761141557)
|
|
10
|
+
* -----
|
|
11
|
+
* @Copyright: Copyright (c) 2013-2025 Catalyzed Motivation Inc. All rights reserved.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* @fileoverview Helper for automatically wrapping Node.js EventEmitter instances within API modules.
|
|
15
|
+
* Internal file (not exported in package.json).
|
|
16
|
+
* @module @cldmv/slothlet/src/lib/helpers/auto-wrap
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Automatically wrap Node.js EventEmitter constructors when called within slothlet API context.
|
|
20
|
+
* This ensures that EventEmitter instances created inside API modules preserve AsyncLocalStorage context.
|
|
21
|
+
* @function autoWrapEventEmitters
|
|
22
|
+
* @package
|
|
23
|
+
* @param {NetModule} nodeModule - The Node.js module to wrap (e.g., require("node:net"))
|
|
24
|
+
* @returns {NetModule} Wrapped module with auto-wrapping constructors
|
|
25
|
+
*
|
|
26
|
+
* @description
|
|
27
|
+
* Wraps Node.js module functions that return EventEmitter instances so they automatically
|
|
28
|
+
* return wrapped instances when called within slothlet API context.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* // Usage in API modules:
|
|
32
|
+
* import { autoWrapEventEmitters } from "@cldmv/slothlet/src/lib/helpers/auto-wrap";
|
|
33
|
+
* import originalNet from "node:net";
|
|
34
|
+
* const net = autoWrapEventEmitters(originalNet);
|
|
35
|
+
* // Now net.createServer() returns wrapped instances automatically
|
|
36
|
+
*/
|
|
37
|
+
export function autoWrapEventEmitters(nodeModule: NetModule): NetModule;
|
|
38
|
+
/**
|
|
39
|
+
* Lazily get the pre-wrapped net module for convenient use in API modules.
|
|
40
|
+
* @function getNet
|
|
41
|
+
* @package
|
|
42
|
+
* @returns {Promise<NetModule>} Promise resolving to the wrapped net module
|
|
43
|
+
* @example
|
|
44
|
+
* const net = await getNet();
|
|
45
|
+
*/
|
|
46
|
+
export function getNet(): Promise<NetModule>;
|
|
47
|
+
export type NetModule = typeof import("node:net");
|
|
48
|
+
export type NetServer = import("node:net").Server;
|
|
49
|
+
//# sourceMappingURL=auto-wrap.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-wrap.d.mts","sourceRoot":"","sources":["../../../../dist/lib/helpers/auto-wrap.mjs"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;GAIG;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,kDAdW,SAAS,GACP,SAAS,CA2CrB;AAED;;;;;;;GAOG;AACH,0BAJa,OAAO,CAAC,SAAS,CAAC,CAO9B;wBAGY,cAAc,UAAU,CAAC;wBACzB,OAAO,UAAU,EAAE,MAAM"}
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* const stack = getStack(findCaller);
|
|
30
30
|
* for (const frame of stack) {
|
|
31
31
|
* const filename = frame?.getFileName?.();
|
|
32
|
-
* if (filename && !filename.includes(
|
|
32
|
+
* if (filename && !filename.includes("node_modules")) {
|
|
33
33
|
* return filename; // First non-dependency file
|
|
34
34
|
* }
|
|
35
35
|
* }
|
|
@@ -40,7 +40,7 @@ export function getStack(skipFn?: Function): Array<CallSite>;
|
|
|
40
40
|
* @function resolvePathFromCaller
|
|
41
41
|
* @package
|
|
42
42
|
* @internal
|
|
43
|
-
* @param {string} rel - Relative path to resolve (e.g.,
|
|
43
|
+
* @param {string} rel - Relative path to resolve (e.g., "../config.json", "./data/file.txt")
|
|
44
44
|
* @returns {string} Absolute filesystem path with platform-specific separators
|
|
45
45
|
* @throws {TypeError} When rel parameter is not a string
|
|
46
46
|
*
|
|
@@ -61,24 +61,24 @@ export function getStack(skipFn?: Function): Array<CallSite>;
|
|
|
61
61
|
*
|
|
62
62
|
* @example
|
|
63
63
|
* // From a file at /project/src/modules/math.mjs
|
|
64
|
-
* const configPath = resolvePathFromCaller(
|
|
64
|
+
* const configPath = resolvePathFromCaller("../config.json");
|
|
65
65
|
* // Returns: /project/config.json (absolute filesystem path)
|
|
66
66
|
*
|
|
67
67
|
* @example
|
|
68
68
|
* // Short-circuit cases
|
|
69
|
-
* resolvePathFromCaller(
|
|
69
|
+
* resolvePathFromCaller("file:///absolute/path.txt");
|
|
70
70
|
* // Returns: /absolute/path.txt (converted from URL)
|
|
71
71
|
*
|
|
72
|
-
* resolvePathFromCaller(
|
|
72
|
+
* resolvePathFromCaller("/already/absolute/path.txt");
|
|
73
73
|
* // Returns: /already/absolute/path.txt (unchanged)
|
|
74
74
|
*
|
|
75
75
|
* @example
|
|
76
76
|
* // Relative resolution from different contexts
|
|
77
77
|
* // If called from /project/src/lib/utils.mjs:
|
|
78
|
-
* resolvePathFromCaller(
|
|
78
|
+
* resolvePathFromCaller("./helpers/format.js");
|
|
79
79
|
* // Returns: /project/src/lib/helpers/format.js
|
|
80
80
|
*
|
|
81
|
-
* resolvePathFromCaller(
|
|
81
|
+
* resolvePathFromCaller("../../config/settings.json");
|
|
82
82
|
* // Returns: /project/config/settings.json
|
|
83
83
|
*/
|
|
84
84
|
export function resolvePathFromCaller(rel: string): string;
|
|
@@ -86,7 +86,7 @@ export function resolvePathFromCaller(rel: string): string;
|
|
|
86
86
|
* @function resolveUrlFromCaller
|
|
87
87
|
* @package
|
|
88
88
|
* @internal
|
|
89
|
-
* @param {string} rel - Relative path to resolve (e.g.,
|
|
89
|
+
* @param {string} rel - Relative path to resolve (e.g., "../config.json", "./data/file.txt")
|
|
90
90
|
* @returns {string} Absolute file:// URL suitable for dynamic imports and URL operations
|
|
91
91
|
* @throws {TypeError} When rel parameter is not a string
|
|
92
92
|
*
|
|
@@ -106,27 +106,27 @@ export function resolvePathFromCaller(rel: string): string;
|
|
|
106
106
|
*
|
|
107
107
|
* @example
|
|
108
108
|
* // From a file at /project/src/modules/math.mjs
|
|
109
|
-
* const configUrl = resolveUrlFromCaller(
|
|
109
|
+
* const configUrl = resolveUrlFromCaller("../config.json");
|
|
110
110
|
* // Returns: file:///project/config.json (absolute file:// URL)
|
|
111
111
|
*
|
|
112
112
|
* @example
|
|
113
113
|
* // Short-circuit cases
|
|
114
|
-
* resolveUrlFromCaller(
|
|
114
|
+
* resolveUrlFromCaller("file:///absolute/path.txt");
|
|
115
115
|
* // Returns: file:///absolute/path.txt (unchanged)
|
|
116
116
|
*
|
|
117
|
-
* resolveUrlFromCaller(
|
|
117
|
+
* resolveUrlFromCaller("/already/absolute/path.txt");
|
|
118
118
|
* // Returns: file:///already/absolute/path.txt (converted to URL)
|
|
119
119
|
*
|
|
120
120
|
* @example
|
|
121
121
|
* // Dynamic ESM import usage
|
|
122
|
-
* const modulePath = resolveUrlFromCaller(
|
|
122
|
+
* const modulePath = resolveUrlFromCaller("./dynamic-module.mjs");
|
|
123
123
|
* const dynamicModule = await import(modulePath);
|
|
124
124
|
* // Works seamlessly with ESM import() which expects URLs
|
|
125
125
|
*
|
|
126
126
|
* @example
|
|
127
127
|
* // Cross-platform URL handling
|
|
128
|
-
* // Unix: resolveUrlFromCaller(
|
|
129
|
-
* // Windows: resolveUrlFromCaller(
|
|
128
|
+
* // Unix: resolveUrlFromCaller("../config.json") → file:///project/config.json
|
|
129
|
+
* // Windows: resolveUrlFromCaller("../config.json") → file:///C:/project/config.json
|
|
130
130
|
*/
|
|
131
131
|
export function resolveUrlFromCaller(rel: string): string;
|
|
132
132
|
export function toFsPath(v: any): string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-from-caller.d.mts","sourceRoot":"","sources":["../../../../dist/lib/helpers/resolve-from-caller.mjs"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"resolve-from-caller.d.mts","sourceRoot":"","sources":["../../../../dist/lib/helpers/resolve-from-caller.mjs"],"names":[],"mappings":"AAqFA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,6CAhCa,KAAK,CAAC,QAAQ,CAAC,CA0C3B;AAqKD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,2CAzCW,MAAM,GACJ,MAAM,CAsDlB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,0CA1CW,MAAM,GACJ,MAAM,CAuDlB;AA/UM,4BAvBI,GAAG,GACD,MAAM,GAAC,IAAI,CAsB+F;;iBAmVzG,MAAY,MAAM,GAAC,SAAS;mBAC5B,MAAY,MAAM,GAAC,SAAS;qBAC5B,MAAY,MAAM,GAAC,SAAS;iBAC5B,MAAY,MAAM,GAAC,SAAS;mBAC5B,MAAY,MAAM,GAAC,SAAS;8BAC5B,MAAY,MAAM,GAAC,SAAS;qBAC5B,MAAY,MAAM,GAAC,SAAS;cAC5B,MAAY,OAAO,GAAC,SAAS;YAC7B,MAAY,OAAO,GAAC,SAAS;mBAC7B,MAAY,OAAO,GAAC,SAAS;gBAC7B,MAAY,OAAO,GAAC,SAAS;aAC7B,MAAY,OAAO,GAAC,SAAS;kBAC7B,MAAY,OAAO,GAAC,SAAS;qBAC7B,MAAY,MAAM,GAAC,SAAS"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sanitize.d.mts","sourceRoot":"","sources":["../../../../dist/lib/helpers/sanitize.mjs"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sanitize.d.mts","sourceRoot":"","sources":["../../../../dist/lib/helpers/sanitize.mjs"],"names":[],"mappings":"AAgFA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoFG;AACH,wCAjFW,MAAM,SAEd;IAAuB,UAAU,GAAzB,OAAO;IACQ,gBAAgB,GAA/B,OAAO;IACQ,gBAAgB,GAA/B,OAAO;IACO,KAAK,GAC3B;QAA8B,KAAK,GAA3B,MAAM,EAAE;QACc,gBAAgB,GAAtC,MAAM,EAAE;QACc,KAAK,GAA3B,MAAM,EAAE;QACc,KAAK,GAA3B,MAAM,EAAE;KAChB;CAAA,GAAU,MAAM,CAuSlB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slothlet_eager.d.mts","sourceRoot":"","sources":["../../../../dist/lib/modes/slothlet_eager.mjs"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"slothlet_eager.d.mts","sourceRoot":"","sources":["../../../../dist/lib/modes/slothlet_eager.mjs"],"names":[],"mappings":"AA2IA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAsDH;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,4BAtBW,MAAM,cACN,OAAO,aACP,MAAM,iBACN,MAAM,GACJ,OAAO,CAAC,MAAM,CAAC,CAqE3B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slothlet_lazy.d.mts","sourceRoot":"","sources":["../../../../dist/lib/modes/slothlet_lazy.mjs"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"slothlet_lazy.d.mts","sourceRoot":"","sources":["../../../../dist/lib/modes/slothlet_lazy.mjs"],"names":[],"mappings":"AA6JA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,4BAvBW,MAAM,cACN,OAAO,aACP,MAAM,iBACN,MAAM,GACJ,OAAO,CAAC,WAAS,MAAM,CAAC,CAwEpC"}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared AsyncLocalStorage instance for all slothlet instances.
|
|
3
|
+
* Provides unified context management across all EventEmitter wrappers.
|
|
4
|
+
* @type {AsyncLocalStorageType}
|
|
5
|
+
* @public
|
|
6
|
+
*/
|
|
7
|
+
export const sharedALS: AsyncLocalStorageType;
|
|
1
8
|
export function runWithCtx(ctx: object, fn: Function, thisArg: any, args: any[]): any;
|
|
2
9
|
export function getCtx(): object | null;
|
|
3
10
|
export function makeWrapper(ctx: object): Function;
|
|
@@ -46,4 +53,6 @@ export const context: object;
|
|
|
46
53
|
* console.log(reference); // Current reference data
|
|
47
54
|
*/
|
|
48
55
|
export const reference: object;
|
|
56
|
+
export type AsyncLocalStorageType = AsyncLocalStorage<any>;
|
|
57
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
49
58
|
//# sourceMappingURL=runtime.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.mts","sourceRoot":"","sources":["../../../../dist/lib/runtime/runtime.mjs"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"runtime.d.mts","sourceRoot":"","sources":["../../../../dist/lib/runtime/runtime.mjs"],"names":[],"mappings":"AAuCA;;;;;GAKG;AACH,wBAHU,qBAAqB,CAGkB;AAsB1C,gCAdI,MAAM,yBAEN,GAAG,gBAED,GAAG,CA6Bf;AAiBM,0BAZM,MAAM,GAAC,IAAI,CAY0B;AAiL3C,iCAjBI,MAAM,YAgIhB;AA4RD;;;;;;;;;;;;;GAaG;AACH,mBATU,WAAS,MAAM,CAS6B;AAEtD;;;;;;;;;;;;;GAaG;AACH,sBATU,MAAM,CAS4C;AAE5D;;;;;;;;;;;;;GAaG;AACH,wBATU,MAAM,CASgD;;kCAhrB9B,kBAAkB"}
|
|
@@ -9,13 +9,6 @@
|
|
|
9
9
|
* mutateLiveBindingFunction(boundapi, newApi);
|
|
10
10
|
*/
|
|
11
11
|
export function mutateLiveBindingFunction(target: Function | object, source: Function | object): void;
|
|
12
|
-
/**
|
|
13
|
-
* The shared _slothlet parameter for live binding coordination.
|
|
14
|
-
* @type {string}
|
|
15
|
-
* @private
|
|
16
|
-
* @internal
|
|
17
|
-
*/
|
|
18
|
-
export let _slothlet: string;
|
|
19
12
|
/**
|
|
20
13
|
* Live-binding reference to the current API instance.
|
|
21
14
|
* This is updated whenever a new API instance is created.
|
|
@@ -86,7 +79,7 @@ export type SlothletOptions = {
|
|
|
86
79
|
api_mode?: string;
|
|
87
80
|
/**
|
|
88
81
|
* - Context data object injected into live-binding `context` reference.
|
|
89
|
-
* - Available to all loaded modules via `import { context } from
|
|
82
|
+
* - Available to all loaded modules via `import { context } from "@cldmv/slothlet/runtime"`. Useful for request data,
|
|
90
83
|
* - user sessions, environment configs, etc.
|
|
91
84
|
*/
|
|
92
85
|
context?: object;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slothlet.d.mts","sourceRoot":"","sources":["../../dist/slothlet.mjs"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"slothlet.d.mts","sourceRoot":"","sources":["../../dist/slothlet.mjs"],"names":[],"mappings":"AA04CA;;;;;;;;;GASG;AACH,kDARW,WAAS,MAAM,UACf,WAAS,MAAM,QAwCzB;AAryCD;;;;;;;GAOG;AACH,mBAJU,MAAM,CAIO;AAEvB;;;;;GAKG;AACH,sBAJU,MAAM,CAIU;AAE1B;;;;;GAKG;AACH,wBAJU,MAAM,CAIY;;;;;;;;;UAwxCd,MAAM;;;;;;WAIN,OAAO;;;;;;;eAGP,MAAM;;;;;;;;YAIN,OAAO;;;;;;;;WAKP,MAAM;;;;;;;eAKN,MAAM;;;;;;cAIN,MAAM;;;;;;gBAGN,MAAM;;;;;;eAMjB;QAA8B,UAAU,GAA7B,OAAO;QACY,gBAAgB,GAAnC,OAAO;QACY,gBAAgB,GAAnC,OAAO;QACW,KAAK,GAClC;YAAqC,KAAK,GAA/B,MAAM,EAAE;YACkB,gBAAgB,GAA1C,MAAM,EAAE;YACkB,KAAK,GAA/B,MAAM,EAAE;YACkB,KAAK,GAA/B,MAAM,EAAE;SACrB;KAAA;;AAh0CD;;;;;;;;GAQG;AACH,mCAJW,eAAe,GACb,OAAO,CAAC,WAAS,MAAM,CAAC,CAiCpC"}
|
package/types/index.d.mts
CHANGED
|
@@ -11,7 +11,6 @@ export type * from "./dist/slothlet.d.mts";
|
|
|
11
11
|
export * from "./dist/slothlet.mts";
|
|
12
12
|
|
|
13
13
|
// Auto-generated named export declarations (these override the re-export above)
|
|
14
|
-
export const _slothlet: any;
|
|
15
14
|
export const context: any;
|
|
16
15
|
export const mutateLiveBindingFunction: any;
|
|
17
16
|
export const reference: any;
|