@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.
Files changed (59) hide show
  1. package/AGENT-USAGE.md +1 -1
  2. package/README.md +300 -1557
  3. package/dist/lib/engine/slothlet_child.mjs +1 -1
  4. package/dist/lib/engine/slothlet_engine.mjs +1 -1
  5. package/dist/lib/engine/slothlet_esm.mjs +1 -1
  6. package/dist/lib/engine/slothlet_helpers.mjs +1 -1
  7. package/dist/lib/engine/slothlet_worker.mjs +1 -1
  8. package/dist/lib/helpers/als-eventemitter.mjs +5 -6
  9. package/dist/lib/helpers/api_builder/add_api.mjs +292 -0
  10. package/dist/lib/helpers/api_builder/analysis.mjs +532 -0
  11. package/dist/lib/helpers/api_builder/construction.mjs +457 -0
  12. package/dist/lib/helpers/api_builder/decisions.mjs +737 -0
  13. package/dist/lib/helpers/api_builder/metadata.mjs +248 -0
  14. package/dist/lib/helpers/api_builder.mjs +17 -1568
  15. package/dist/lib/helpers/auto-wrap.mjs +1 -1
  16. package/dist/lib/helpers/hooks.mjs +1 -1
  17. package/dist/lib/helpers/instance-manager.mjs +1 -1
  18. package/dist/lib/helpers/metadata-api.mjs +201 -0
  19. package/dist/lib/helpers/multidefault.mjs +12 -3
  20. package/dist/lib/helpers/resolve-from-caller.mjs +1 -1
  21. package/dist/lib/helpers/sanitize.mjs +1 -1
  22. package/dist/lib/helpers/utilities.mjs +121 -0
  23. package/dist/lib/modes/slothlet_eager.mjs +1 -1
  24. package/dist/lib/modes/slothlet_lazy.mjs +10 -1
  25. package/dist/lib/runtime/runtime-asynclocalstorage.mjs +49 -18
  26. package/dist/lib/runtime/runtime-livebindings.mjs +23 -4
  27. package/dist/lib/runtime/runtime.mjs +15 -4
  28. package/dist/slothlet.mjs +164 -748
  29. package/docs/API-RULES-CONDITIONS.md +508 -0
  30. package/{API-RULES.md → docs/API-RULES.md} +127 -72
  31. package/package.json +11 -9
  32. package/types/dist/lib/helpers/als-eventemitter.d.mts.map +1 -1
  33. package/types/dist/lib/helpers/api_builder/add_api.d.mts +76 -0
  34. package/types/dist/lib/helpers/api_builder/add_api.d.mts.map +1 -0
  35. package/types/dist/lib/helpers/api_builder/analysis.d.mts +189 -0
  36. package/types/dist/lib/helpers/api_builder/analysis.d.mts.map +1 -0
  37. package/types/dist/lib/helpers/api_builder/construction.d.mts +107 -0
  38. package/types/dist/lib/helpers/api_builder/construction.d.mts.map +1 -0
  39. package/types/dist/lib/helpers/api_builder/decisions.d.mts +213 -0
  40. package/types/dist/lib/helpers/api_builder/decisions.d.mts.map +1 -0
  41. package/types/dist/lib/helpers/api_builder/metadata.d.mts +99 -0
  42. package/types/dist/lib/helpers/api_builder/metadata.d.mts.map +1 -0
  43. package/types/dist/lib/helpers/api_builder.d.mts +5 -448
  44. package/types/dist/lib/helpers/api_builder.d.mts.map +1 -1
  45. package/types/dist/lib/helpers/metadata-api.d.mts +132 -0
  46. package/types/dist/lib/helpers/metadata-api.d.mts.map +1 -0
  47. package/types/dist/lib/helpers/multidefault.d.mts.map +1 -1
  48. package/types/dist/lib/helpers/utilities.d.mts +120 -0
  49. package/types/dist/lib/helpers/utilities.d.mts.map +1 -0
  50. package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts +9 -0
  51. package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts.map +1 -1
  52. package/types/dist/lib/runtime/runtime-livebindings.d.mts +10 -0
  53. package/types/dist/lib/runtime/runtime-livebindings.d.mts.map +1 -1
  54. package/types/dist/lib/runtime/runtime.d.mts +1 -0
  55. package/types/dist/lib/runtime/runtime.d.mts.map +1 -1
  56. package/types/dist/slothlet.d.mts +0 -11
  57. package/types/dist/slothlet.d.mts.map +1 -1
  58. package/types/index.d.mts +0 -1
  59. package/API-RULES-CONDITIONS.md +0 -367
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2025 CLDMV/Shinrai
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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2025 CLDMV/Shinrai
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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2025 CLDMV/Shinrai
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.
@@ -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 2025 CLDMV/Shinrai
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 separator = importUrl.includes("?") ? "&" : "?";
45
- importUrl = `${importUrl}${separator}slothlet_instance=${instance.instanceId}`;
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
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2025 CLDMV/Shinrai
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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2025 CLDMV/Shinrai
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.
@@ -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 2025 CLDMV/Shinrai
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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2025 CLDMV/Shinrai
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 2025 CLDMV/Shinrai
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 = ctx.hookManager.executeBeforeHooks(path, args);
59
+ const beforeResult = mergedCtx.hookManager.executeBeforeHooks(path, args);
52
60
 
53
61
 
54
62
  if (beforeResult.cancelled) {
55
- ctx.hookManager.executeAlwaysHooks(path, beforeResult.value, []);
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 = ctx.hookManager.executeAfterHooks(path, resolvedResult);
71
- ctx.hookManager.executeAlwaysHooks(path, finalResult, []);
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 (!ctx.hookManager.reportedErrors.has(error)) {
77
- ctx.hookManager.reportedErrors.add(error);
78
- ctx.hookManager.executeErrorHooks(path, error, { type: "function" });
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
- ctx.hookManager.executeAlwaysHooks(path, undefined, [error]);
89
+ mergedCtx.hookManager.executeAlwaysHooks(path, undefined, [error]);
82
90
 
83
- if (!ctx.hookManager.suppressErrors) {
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 = ctx.hookManager.executeAfterHooks(path, result);
93
- ctx.hookManager.executeAlwaysHooks(path, finalResult, []);
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 (!ctx.hookManager.reportedErrors.has(error)) {
98
- ctx.hookManager.reportedErrors.add(error);
99
- ctx.hookManager.executeErrorHooks(path, error, { type: "function" });
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
- ctx.hookManager.executeAlwaysHooks(path, undefined, [error]);
110
+ mergedCtx.hookManager.executeAlwaysHooks(path, undefined, [error]);
103
111
 
104
- if (!ctx.hookManager.suppressErrors) {
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(ctx, runtime_runInALS);
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 2025 CLDMV/Shinrai
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
- if (ctx && ctx.context) {
126
- return ctx.context[prop];
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
- return undefined;
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 2025 CLDMV/Shinrai
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 "../helpers/instance-manager.mjs";
21
+ import { detectCurrentInstanceId, getInstanceData } from "@cldmv/slothlet/helpers/instance-manager";
22
22
 
23
23
 
24
- const asyncRuntime = await import("./runtime-asynclocalstorage.mjs");
25
- const liveBindingsRuntime = await import("./runtime-livebindings.mjs");
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
+ );