@cldmv/slothlet 2.8.0 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENT-USAGE.md +1 -1
- package/README.md +300 -1557
- package/dist/lib/engine/slothlet_child.mjs +1 -1
- package/dist/lib/engine/slothlet_engine.mjs +1 -1
- package/dist/lib/engine/slothlet_esm.mjs +1 -1
- package/dist/lib/engine/slothlet_helpers.mjs +1 -1
- package/dist/lib/engine/slothlet_worker.mjs +1 -1
- package/dist/lib/helpers/als-eventemitter.mjs +5 -6
- package/dist/lib/helpers/api_builder/add_api.mjs +292 -0
- package/dist/lib/helpers/api_builder/analysis.mjs +532 -0
- package/dist/lib/helpers/api_builder/construction.mjs +457 -0
- package/dist/lib/helpers/api_builder/decisions.mjs +737 -0
- package/dist/lib/helpers/api_builder/metadata.mjs +248 -0
- package/dist/lib/helpers/api_builder.mjs +17 -1568
- package/dist/lib/helpers/auto-wrap.mjs +1 -1
- package/dist/lib/helpers/hooks.mjs +1 -1
- package/dist/lib/helpers/instance-manager.mjs +1 -1
- package/dist/lib/helpers/metadata-api.mjs +201 -0
- package/dist/lib/helpers/multidefault.mjs +12 -3
- package/dist/lib/helpers/resolve-from-caller.mjs +1 -1
- package/dist/lib/helpers/sanitize.mjs +1 -1
- package/dist/lib/helpers/utilities.mjs +121 -0
- package/dist/lib/modes/slothlet_eager.mjs +1 -1
- package/dist/lib/modes/slothlet_lazy.mjs +10 -1
- package/dist/lib/runtime/runtime-asynclocalstorage.mjs +49 -18
- package/dist/lib/runtime/runtime-livebindings.mjs +23 -4
- package/dist/lib/runtime/runtime.mjs +15 -4
- package/dist/slothlet.mjs +164 -748
- package/docs/API-RULES-CONDITIONS.md +508 -0
- package/{API-RULES.md → docs/API-RULES.md} +127 -72
- package/package.json +11 -9
- package/types/dist/lib/helpers/als-eventemitter.d.mts.map +1 -1
- package/types/dist/lib/helpers/api_builder/add_api.d.mts +76 -0
- package/types/dist/lib/helpers/api_builder/add_api.d.mts.map +1 -0
- package/types/dist/lib/helpers/api_builder/analysis.d.mts +189 -0
- package/types/dist/lib/helpers/api_builder/analysis.d.mts.map +1 -0
- package/types/dist/lib/helpers/api_builder/construction.d.mts +107 -0
- package/types/dist/lib/helpers/api_builder/construction.d.mts.map +1 -0
- package/types/dist/lib/helpers/api_builder/decisions.d.mts +213 -0
- package/types/dist/lib/helpers/api_builder/decisions.d.mts.map +1 -0
- package/types/dist/lib/helpers/api_builder/metadata.d.mts +99 -0
- package/types/dist/lib/helpers/api_builder/metadata.d.mts.map +1 -0
- package/types/dist/lib/helpers/api_builder.d.mts +5 -448
- package/types/dist/lib/helpers/api_builder.d.mts.map +1 -1
- package/types/dist/lib/helpers/metadata-api.d.mts +132 -0
- package/types/dist/lib/helpers/metadata-api.d.mts.map +1 -0
- package/types/dist/lib/helpers/multidefault.d.mts.map +1 -1
- package/types/dist/lib/helpers/utilities.d.mts +120 -0
- package/types/dist/lib/helpers/utilities.d.mts.map +1 -0
- package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts +9 -0
- package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts.map +1 -1
- package/types/dist/lib/runtime/runtime-livebindings.d.mts +10 -0
- package/types/dist/lib/runtime/runtime-livebindings.d.mts.map +1 -1
- package/types/dist/lib/runtime/runtime.d.mts +1 -0
- package/types/dist/lib/runtime/runtime.d.mts.map +1 -1
- package/types/dist/slothlet.d.mts +0 -11
- package/types/dist/slothlet.d.mts.map +1 -1
- package/types/index.d.mts +0 -1
- package/API-RULES-CONDITIONS.md +0 -367
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2026 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 { getStack, toFsPath } from "@cldmv/slothlet/helpers/resolve-from-caller";
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
let runtimeModule = null;
|
|
25
|
+
let runtimeImportPromise = null;
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
async function ensureRuntime() {
|
|
29
|
+
if (runtimeModule) {
|
|
30
|
+
return runtimeModule;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!runtimeImportPromise) {
|
|
34
|
+
runtimeImportPromise = import("@cldmv/slothlet/runtime")
|
|
35
|
+
.then((module) => {
|
|
36
|
+
runtimeModule = module;
|
|
37
|
+
return module;
|
|
38
|
+
})
|
|
39
|
+
.catch((err) => {
|
|
40
|
+
console.error("[slothlet] Failed to import runtime for metadata API:", err.message);
|
|
41
|
+
runtimeModule = {};
|
|
42
|
+
return {};
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return runtimeImportPromise;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
function getApiRoot() {
|
|
51
|
+
|
|
52
|
+
return runtimeModule?.self || null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
function findFunctionByStack(apiRoot, targetFile, targetLine, visited = new WeakSet()) {
|
|
57
|
+
if (!apiRoot || visited.has(apiRoot)) return null;
|
|
58
|
+
visited.add(apiRoot);
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
if (typeof apiRoot === "function" && apiRoot.__sourceFile && apiRoot.__sourceLine) {
|
|
62
|
+
if (apiRoot.__sourceFile === targetFile && apiRoot.__sourceLine === targetLine) {
|
|
63
|
+
return apiRoot;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
if (typeof apiRoot === "object" || typeof apiRoot === "function") {
|
|
69
|
+
const keys = Object.keys(apiRoot);
|
|
70
|
+
for (const key of keys) {
|
|
71
|
+
|
|
72
|
+
if (key.startsWith("_") || ["hooks", "shutdown", "addApi", "describe", "run"].includes(key)) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const result = findFunctionByStack(apiRoot[key], targetFile, targetLine, visited);
|
|
77
|
+
if (result) return result;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
function findFunctionByPath(apiRoot, path) {
|
|
86
|
+
if (!path || typeof path !== "string") return null;
|
|
87
|
+
|
|
88
|
+
const parts = path.split(".");
|
|
89
|
+
let current = apiRoot;
|
|
90
|
+
|
|
91
|
+
for (const part of parts) {
|
|
92
|
+
if (!current || (typeof current !== "object" && typeof current !== "function")) {
|
|
93
|
+
if (process.env.SLOTHLET_DEBUG) {
|
|
94
|
+
console.log("[findFunctionByPath] Failed: current is", typeof current);
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
current = current[part];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const result = typeof current === "function" ? current : null;
|
|
102
|
+
if (process.env.SLOTHLET_DEBUG) {
|
|
103
|
+
console.log("[findFunctionByPath] Result:", result ? "function found" : "null");
|
|
104
|
+
}
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
function parseCallSite(cs) {
|
|
110
|
+
if (!cs) return null;
|
|
111
|
+
|
|
112
|
+
const fileName = cs.getFileName?.();
|
|
113
|
+
if (!fileName) return null;
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
const filePath = toFsPath(fileName);
|
|
117
|
+
if (!filePath) return null;
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
if (filePath.startsWith?.("node:internal")) return null;
|
|
121
|
+
|
|
122
|
+
const lineNum = cs.getLineNumber?.();
|
|
123
|
+
if (typeof lineNum !== "number") return null;
|
|
124
|
+
|
|
125
|
+
return { file: filePath, line: lineNum };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
export const metadataAPI = {
|
|
130
|
+
|
|
131
|
+
async caller() {
|
|
132
|
+
|
|
133
|
+
await ensureRuntime();
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
const apiRoot = getApiRoot();
|
|
137
|
+
if (!apiRoot || typeof apiRoot !== "object") return null;
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
const stack = getStack(metadataAPI.caller);
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
if (stack.length < 1) return null;
|
|
146
|
+
|
|
147
|
+
const parsed = parseCallSite(stack[0]);
|
|
148
|
+
if (!parsed) return null;
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
const func = findFunctionByStack(apiRoot, parsed.file, parsed.line);
|
|
152
|
+
if (!func) return null;
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
return func.__metadata || null;
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
async self() {
|
|
160
|
+
|
|
161
|
+
await ensureRuntime();
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
const apiRoot = getApiRoot();
|
|
165
|
+
if (!apiRoot || typeof apiRoot !== "object") return null;
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
const stack = getStack(metadataAPI.self);
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
if (stack.length < 1) return null;
|
|
172
|
+
|
|
173
|
+
const parsed = parseCallSite(stack[0]);
|
|
174
|
+
if (!parsed) return null;
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
const func = findFunctionByStack(apiRoot, parsed.file, parsed.line);
|
|
178
|
+
if (!func) return null;
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
return func.__metadata || null;
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
async get(path, apiRoot) {
|
|
186
|
+
|
|
187
|
+
await ensureRuntime();
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
const root = apiRoot || getApiRoot();
|
|
191
|
+
if (!root || (typeof root !== "object" && typeof root !== "function")) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const func = findFunctionByPath(root, path);
|
|
196
|
+
if (!func) return null;
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
return func.__metadata || null;
|
|
200
|
+
}
|
|
201
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright
|
|
2
|
+
Copyright 2026 CLDMV/Shinrai
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -39,10 +39,19 @@ async function multidefault_analyzeModules(moduleFiles, baseDir, options = {}) {
|
|
|
39
39
|
const moduleFilePath = path.resolve(baseDir, file.name);
|
|
40
40
|
|
|
41
41
|
|
|
42
|
+
|
|
42
43
|
let importUrl = `file://${moduleFilePath.replace(/\\/g, "/")}`;
|
|
44
|
+
const separator = importUrl.includes("?") ? "&" : "?";
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
importUrl = `${importUrl}${separator}_t=${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
48
|
+
|
|
49
|
+
|
|
43
50
|
if (instance && instance.instanceId) {
|
|
44
|
-
const
|
|
45
|
-
|
|
51
|
+
const runtimeType = instance.config?.runtime || "async";
|
|
52
|
+
if (runtimeType === "live") {
|
|
53
|
+
importUrl = `${importUrl}&slothlet_instance=${instance.instanceId}`;
|
|
54
|
+
}
|
|
46
55
|
}
|
|
47
56
|
|
|
48
57
|
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2026 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 function safeDefine(obj, key, value, enumerable = false, config = null) {
|
|
23
|
+
const desc = Object.getOwnPropertyDescriptor(obj, key);
|
|
24
|
+
if (!desc) {
|
|
25
|
+
Object.defineProperty(obj, key, {
|
|
26
|
+
value,
|
|
27
|
+
writable: true,
|
|
28
|
+
configurable: true,
|
|
29
|
+
enumerable
|
|
30
|
+
});
|
|
31
|
+
} else if (desc.configurable) {
|
|
32
|
+
Object.defineProperty(obj, key, {
|
|
33
|
+
value,
|
|
34
|
+
writable: true,
|
|
35
|
+
configurable: true,
|
|
36
|
+
enumerable
|
|
37
|
+
});
|
|
38
|
+
} else if (config && config.debug) {
|
|
39
|
+
console.warn(`Could not redefine boundApi.${key}: not configurable`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
export function deepMerge(target, source) {
|
|
45
|
+
if (!source || typeof source !== "object" || Array.isArray(source)) {
|
|
46
|
+
return source;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for (const key in source) {
|
|
50
|
+
if (!Object.prototype.hasOwnProperty.call(source, key)) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
if (key === "__proto__" || key === "prototype" || key === "constructor") {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const sourceValue = source[key];
|
|
60
|
+
const targetValue = target[key];
|
|
61
|
+
|
|
62
|
+
if (sourceValue && typeof sourceValue === "object" && !Array.isArray(sourceValue)) {
|
|
63
|
+
target[key] = deepMerge(
|
|
64
|
+
targetValue && typeof targetValue === "object" && !Array.isArray(targetValue) ? targetValue : {},
|
|
65
|
+
sourceValue
|
|
66
|
+
);
|
|
67
|
+
} else {
|
|
68
|
+
target[key] = sourceValue;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return target;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
export function mutateLiveBindingFunction(target, source) {
|
|
77
|
+
if (typeof source === "function") {
|
|
78
|
+
target._impl = (...args) => source(...args);
|
|
79
|
+
|
|
80
|
+
for (const key of Object.keys(target)) {
|
|
81
|
+
if (key !== "_impl" && key !== "__ctx") delete target[key];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
for (const key of Object.getOwnPropertyNames(source)) {
|
|
85
|
+
if (key !== "length" && key !== "name" && key !== "prototype" && key !== "_impl" && key !== "__ctx") {
|
|
86
|
+
try {
|
|
87
|
+
target[key] = source[key];
|
|
88
|
+
} catch {
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
} else if (typeof source === "object" && source !== null) {
|
|
94
|
+
|
|
95
|
+
for (const key of Object.keys(target)) {
|
|
96
|
+
if (key !== "_impl" && key !== "__ctx") delete target[key];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
for (const [key, value] of Object.entries(source)) {
|
|
100
|
+
if (key !== "__ctx") {
|
|
101
|
+
target[key] = value;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const managementMethods = ["shutdown", "addApi", "describe"];
|
|
106
|
+
for (const method of managementMethods) {
|
|
107
|
+
const desc = Object.getOwnPropertyDescriptor(source, method);
|
|
108
|
+
if (desc) {
|
|
109
|
+
try {
|
|
110
|
+
Object.defineProperty(target, method, desc);
|
|
111
|
+
} catch {
|
|
112
|
+
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (typeof source._impl === "function") {
|
|
118
|
+
target._impl = source._impl;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright
|
|
2
|
+
Copyright 2026 CLDMV/Shinrai
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -345,6 +345,11 @@ function createFolderProxy({ subDirPath, key, parent, instance, depth, maxDepth,
|
|
|
345
345
|
return pathParts.length > 0 ? pathParts.join(".") : undefined;
|
|
346
346
|
}
|
|
347
347
|
|
|
348
|
+
if (prop === "__metadata" || prop === "__sourceFolder") {
|
|
349
|
+
|
|
350
|
+
return Reflect.get(_t, prop);
|
|
351
|
+
}
|
|
352
|
+
|
|
348
353
|
if (materialized) {
|
|
349
354
|
if (materialized && (typeof materialized === "object" || typeof materialized === "function")) return materialized[prop];
|
|
350
355
|
return undefined;
|
|
@@ -485,6 +490,10 @@ function createFolderProxy({ subDirPath, key, parent, instance, depth, maxDepth,
|
|
|
485
490
|
if (prop === "__materialized") {
|
|
486
491
|
return { configurable: true, enumerable: false, writable: true, value: materialized };
|
|
487
492
|
}
|
|
493
|
+
|
|
494
|
+
if (prop === "__metadata" || prop === "__sourceFolder") {
|
|
495
|
+
return Reflect.getOwnPropertyDescriptor(lazy_lazyTarget, prop);
|
|
496
|
+
}
|
|
488
497
|
if (prop === "prototype") {
|
|
489
498
|
|
|
490
499
|
return Object.getOwnPropertyDescriptor(lazy_lazyTarget, "prototype");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright
|
|
2
|
+
Copyright 2026 CLDMV/Shinrai
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
22
22
|
import util from "node:util";
|
|
23
23
|
import { enableAlsForEventEmitters } from "@cldmv/slothlet/helpers/als-eventemitter";
|
|
24
|
+
import { metadataAPI } from "@cldmv/slothlet/helpers/metadata-api";
|
|
24
25
|
|
|
25
26
|
const als = new AsyncLocalStorage();
|
|
26
27
|
|
|
@@ -28,6 +29,9 @@ const als = new AsyncLocalStorage();
|
|
|
28
29
|
export const sharedALS = new AsyncLocalStorage();
|
|
29
30
|
|
|
30
31
|
|
|
32
|
+
export const requestALS = new AsyncLocalStorage();
|
|
33
|
+
|
|
34
|
+
|
|
31
35
|
enableAlsForEventEmitters(als);
|
|
32
36
|
|
|
33
37
|
|
|
@@ -42,17 +46,21 @@ export const runWithCtx = (ctx, fn, thisArg, args) => {
|
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
|
|
49
|
+
const requestContext = requestALS.getStore();
|
|
50
|
+
const mergedCtx = requestContext ? { ...ctx, context: { ...ctx.context, ...requestContext } } : ctx;
|
|
51
|
+
|
|
52
|
+
|
|
45
53
|
const path = fn.__slothletPath;
|
|
46
54
|
|
|
47
55
|
|
|
48
56
|
const runtime_runInALS = () => {
|
|
49
57
|
try {
|
|
50
58
|
|
|
51
|
-
const beforeResult =
|
|
59
|
+
const beforeResult = mergedCtx.hookManager.executeBeforeHooks(path, args);
|
|
52
60
|
|
|
53
61
|
|
|
54
62
|
if (beforeResult.cancelled) {
|
|
55
|
-
|
|
63
|
+
mergedCtx.hookManager.executeAlwaysHooks(path, beforeResult.value, []);
|
|
56
64
|
return beforeResult.value;
|
|
57
65
|
}
|
|
58
66
|
|
|
@@ -67,20 +75,20 @@ export const runWithCtx = (ctx, fn, thisArg, args) => {
|
|
|
67
75
|
return result.then(
|
|
68
76
|
(resolvedResult) => {
|
|
69
77
|
|
|
70
|
-
const finalResult =
|
|
71
|
-
|
|
78
|
+
const finalResult = mergedCtx.hookManager.executeAfterHooks(path, resolvedResult);
|
|
79
|
+
mergedCtx.hookManager.executeAlwaysHooks(path, finalResult, []);
|
|
72
80
|
return finalResult;
|
|
73
81
|
},
|
|
74
82
|
(error) => {
|
|
75
83
|
|
|
76
|
-
if (!
|
|
77
|
-
|
|
78
|
-
|
|
84
|
+
if (!mergedCtx.hookManager.reportedErrors.has(error)) {
|
|
85
|
+
mergedCtx.hookManager.reportedErrors.add(error);
|
|
86
|
+
mergedCtx.hookManager.executeErrorHooks(path, error, { type: "function" });
|
|
79
87
|
}
|
|
80
88
|
|
|
81
|
-
|
|
89
|
+
mergedCtx.hookManager.executeAlwaysHooks(path, undefined, [error]);
|
|
82
90
|
|
|
83
|
-
if (!
|
|
91
|
+
if (!mergedCtx.hookManager.suppressErrors) {
|
|
84
92
|
throw error;
|
|
85
93
|
}
|
|
86
94
|
return undefined;
|
|
@@ -89,26 +97,26 @@ export const runWithCtx = (ctx, fn, thisArg, args) => {
|
|
|
89
97
|
}
|
|
90
98
|
|
|
91
99
|
|
|
92
|
-
const finalResult =
|
|
93
|
-
|
|
100
|
+
const finalResult = mergedCtx.hookManager.executeAfterHooks(path, result);
|
|
101
|
+
mergedCtx.hookManager.executeAlwaysHooks(path, finalResult, []);
|
|
94
102
|
return finalResult;
|
|
95
103
|
} catch (error) {
|
|
96
104
|
|
|
97
|
-
if (!
|
|
98
|
-
|
|
99
|
-
|
|
105
|
+
if (!mergedCtx.hookManager.reportedErrors.has(error)) {
|
|
106
|
+
mergedCtx.hookManager.reportedErrors.add(error);
|
|
107
|
+
mergedCtx.hookManager.executeErrorHooks(path, error, { type: "function" });
|
|
100
108
|
}
|
|
101
109
|
|
|
102
|
-
|
|
110
|
+
mergedCtx.hookManager.executeAlwaysHooks(path, undefined, [error]);
|
|
103
111
|
|
|
104
|
-
if (!
|
|
112
|
+
if (!mergedCtx.hookManager.suppressErrors) {
|
|
105
113
|
throw error;
|
|
106
114
|
}
|
|
107
115
|
return undefined;
|
|
108
116
|
}
|
|
109
117
|
};
|
|
110
118
|
|
|
111
|
-
return als.run(
|
|
119
|
+
return als.run(mergedCtx, runtime_runInALS);
|
|
112
120
|
};
|
|
113
121
|
|
|
114
122
|
|
|
@@ -477,6 +485,26 @@ function runtime_createLiveBinding(contextKey) {
|
|
|
477
485
|
|
|
478
486
|
|
|
479
487
|
|
|
488
|
+
if (contextKey === "context") {
|
|
489
|
+
const ctx = getCtx();
|
|
490
|
+
const baseContext = ctx?.context || {};
|
|
491
|
+
const requestContext = requestALS.getStore() || {};
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
const isDeepMerge = ctx?.config?.scope?.merge === "deep";
|
|
496
|
+
|
|
497
|
+
if (isDeepMerge && Object.keys(requestContext).length > 0) {
|
|
498
|
+
|
|
499
|
+
return requestContext[prop];
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
const merged = { ...baseContext, ...requestContext };
|
|
504
|
+
return merged[prop];
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
|
|
480
508
|
runtime_syncWithContext();
|
|
481
509
|
return target[prop];
|
|
482
510
|
},
|
|
@@ -538,3 +566,6 @@ export const context = runtime_createLiveBinding("context");
|
|
|
538
566
|
export const reference = runtime_createLiveBinding("reference");
|
|
539
567
|
|
|
540
568
|
|
|
569
|
+
export { metadataAPI };
|
|
570
|
+
|
|
571
|
+
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright
|
|
2
|
+
Copyright 2026 CLDMV/Shinrai
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -18,12 +18,17 @@
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
21
22
|
import {
|
|
22
23
|
detectCurrentInstanceId,
|
|
23
24
|
getInstanceData,
|
|
24
25
|
setActiveInstance,
|
|
25
26
|
getCurrentActiveInstanceId
|
|
26
27
|
} from "@cldmv/slothlet/helpers/instance-manager";
|
|
28
|
+
import { metadataAPI } from "@cldmv/slothlet/helpers/metadata-api";
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
export const requestALS = new AsyncLocalStorage();
|
|
27
32
|
|
|
28
33
|
|
|
29
34
|
function getCurrentInstanceContext() {
|
|
@@ -122,10 +127,21 @@ export const context = new Proxy(
|
|
|
122
127
|
{
|
|
123
128
|
get(target, prop) {
|
|
124
129
|
const ctx = getCurrentInstanceContext();
|
|
125
|
-
|
|
126
|
-
|
|
130
|
+
const baseContext = ctx?.context || {};
|
|
131
|
+
const requestContext = requestALS.getStore() || {};
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
const isDeepMerge = ctx?.config?.scope?.merge === "deep";
|
|
136
|
+
|
|
137
|
+
if (isDeepMerge && Object.keys(requestContext).length > 0) {
|
|
138
|
+
|
|
139
|
+
return requestContext[prop];
|
|
127
140
|
}
|
|
128
|
-
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
const merged = { ...baseContext, ...requestContext };
|
|
144
|
+
return merged[prop];
|
|
129
145
|
},
|
|
130
146
|
|
|
131
147
|
set(target, prop, value) {
|
|
@@ -418,3 +434,6 @@ export const contextManager = {
|
|
|
418
434
|
set: setContext,
|
|
419
435
|
runWithCtx
|
|
420
436
|
};
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
export { metadataAPI };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright
|
|
2
|
+
Copyright 2026 CLDMV/Shinrai
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
import { detectCurrentInstanceId, getInstanceData } from "
|
|
21
|
+
import { detectCurrentInstanceId, getInstanceData } from "@cldmv/slothlet/helpers/instance-manager";
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
const asyncRuntime = await import("
|
|
25
|
-
const liveBindingsRuntime = await import("
|
|
24
|
+
const asyncRuntime = await import("@cldmv/slothlet/runtime/async");
|
|
25
|
+
const liveBindingsRuntime = await import("@cldmv/slothlet/runtime/live");
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
function detectRuntimeType() {
|
|
@@ -225,3 +225,14 @@ export const instanceId = (() => {
|
|
|
225
225
|
})();
|
|
226
226
|
|
|
227
227
|
export const sharedALS = getCurrentRuntime().sharedALS;
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
export const metadataAPI = new Proxy(
|
|
231
|
+
{},
|
|
232
|
+
{
|
|
233
|
+
get(_, prop) {
|
|
234
|
+
const runtime = getCurrentRuntime();
|
|
235
|
+
return runtime.metadataAPI[prop];
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
);
|