@cldmv/slothlet 2.9.0 → 2.11.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 +41 -0
- package/README.md +95 -20
- 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 +1 -1
- package/dist/lib/helpers/api_builder/add_api.mjs +321 -5
- package/dist/lib/helpers/api_builder/analysis.mjs +13 -3
- package/dist/lib/helpers/api_builder/construction.mjs +41 -3
- package/dist/lib/helpers/api_builder/decisions.mjs +16 -5
- package/dist/lib/helpers/api_builder/metadata.mjs +248 -0
- package/dist/lib/helpers/api_builder.mjs +1 -1
- 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 +48 -11
- package/dist/lib/helpers/sanitize.mjs +1 -1
- package/dist/lib/helpers/utilities.mjs +1 -1
- package/dist/lib/modes/slothlet_eager.mjs +30 -2
- package/dist/lib/modes/slothlet_lazy.mjs +95 -5
- package/dist/lib/runtime/runtime-asynclocalstorage.mjs +5 -1
- package/dist/lib/runtime/runtime-livebindings.mjs +5 -1
- package/dist/lib/runtime/runtime.mjs +12 -1
- package/dist/slothlet.mjs +70 -6
- package/docs/API-RULES-CONDITIONS.md +511 -307
- package/docs/API-RULES.md +617 -589
- package/package.json +2 -2
- package/types/dist/lib/helpers/api_builder/add_api.d.mts +64 -22
- package/types/dist/lib/helpers/api_builder/add_api.d.mts.map +1 -1
- package/types/dist/lib/helpers/api_builder/analysis.d.mts.map +1 -1
- package/types/dist/lib/helpers/api_builder/construction.d.mts.map +1 -1
- package/types/dist/lib/helpers/api_builder/decisions.d.mts.map +1 -1
- 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/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/resolve-from-caller.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-asynclocalstorage.d.mts +2 -0
- package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts.map +1 -1
- package/types/dist/lib/runtime/runtime-livebindings.d.mts +2 -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 +8 -0
- package/types/dist/slothlet.d.mts.map +1 -1
- package/types/index.d.mts.map +1 -0
|
@@ -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.
|
|
@@ -55,37 +55,74 @@ function pickPrimaryBaseFile() {
|
|
|
55
55
|
for (const cs of getStack(pickPrimaryBaseFile)) {
|
|
56
56
|
const f = toFsPath(cs?.getFileName?.());
|
|
57
57
|
if (!f) continue;
|
|
58
|
-
|
|
58
|
+
|
|
59
|
+
if (f.startsWith?.("node:")) continue;
|
|
59
60
|
files.push(f);
|
|
60
61
|
}
|
|
61
62
|
|
|
63
|
+
console.log(`[DEBUG_RESOLVE] pickPrimaryBaseFile - Total stack frames: ${files.length}`);
|
|
64
|
+
console.log(`[DEBUG_RESOLVE] ALL stack frames:`);
|
|
65
|
+
for (let i = 0; i < files.length; i++) {
|
|
66
|
+
console.log(`[DEBUG_RESOLVE] [${i}] ${files[i]}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
62
69
|
let iSloth = -1;
|
|
63
70
|
for (let i = 0; i < files.length; i++) {
|
|
64
|
-
if (path.basename(files[i]).toLowerCase() === "slothlet.mjs")
|
|
71
|
+
if (path.basename(files[i]).toLowerCase() === "slothlet.mjs") {
|
|
72
|
+
iSloth = i;
|
|
73
|
+
}
|
|
65
74
|
}
|
|
75
|
+
|
|
76
|
+
console.log(`[DEBUG_RESOLVE] Found slothlet.mjs at index: ${iSloth}, total files: ${files.length}`);
|
|
66
77
|
if (iSloth !== -1) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (/^index\.(mjs|cjs|js)$/.test(b) && j + 1 < files.length) return files[j + 1];
|
|
71
|
-
return files[j];
|
|
78
|
+
console.log(`[DEBUG_RESOLVE] Files after slothlet.mjs:`);
|
|
79
|
+
for (let k = iSloth + 1; k < Math.min(files.length, iSloth + 5); k++) {
|
|
80
|
+
console.log(`[DEBUG_RESOLVE] [${k}] ${files[k]}`);
|
|
72
81
|
}
|
|
73
82
|
}
|
|
74
|
-
|
|
83
|
+
|
|
84
|
+
if (iSloth !== -1) {
|
|
85
|
+
|
|
86
|
+
let j = iSloth + 1;
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
function isSlothletInternalFile(filePath) {
|
|
90
|
+
if (!filePath) return true;
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
if (filePath.includes(path.sep + "src" + path.sep + "lib" + path.sep)) return true;
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
if (path.basename(filePath).toLowerCase() === "slothlet.mjs") return true;
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
if (filePath.startsWith(THIS_DIR + path.sep)) return true;
|
|
100
|
+
|
|
101
|
+
return false;
|
|
75
102
|
}
|
|
76
103
|
|
|
77
104
|
|
|
78
105
|
function pickFallbackBaseFile() {
|
|
106
|
+
console.log("[DEBUG_RESOLVE] pickFallbackBaseFile called");
|
|
79
107
|
for (const cs of getStack(pickFallbackBaseFile)) {
|
|
80
108
|
const f = toFsPath(cs?.getFileName?.());
|
|
81
109
|
if (!f) continue;
|
|
82
|
-
|
|
110
|
+
|
|
111
|
+
if (f.startsWith?.("node:")) continue;
|
|
83
112
|
if (f === THIS_FILE) continue;
|
|
84
113
|
if (f.startsWith(THIS_DIR + path.sep)) continue;
|
|
85
114
|
if (path.basename(f).toLowerCase() === "slothlet.mjs") continue;
|
|
115
|
+
if (isSlothletInternalFile(f)) {
|
|
116
|
+
console.log(`[DEBUG_RESOLVE] Fallback skipping internal file: ${f}`);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
console.log(`[DEBUG_RESOLVE] Fallback considering: ${f}`);
|
|
86
120
|
return f;
|
|
87
121
|
}
|
|
88
|
-
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
console.log(`[DEBUG_RESOLVE] Fallback: No user code found in stack, using process.cwd(): ${process.cwd()}`);
|
|
125
|
+
return process.cwd();
|
|
89
126
|
}
|
|
90
127
|
|
|
91
128
|
|
|
@@ -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.
|
|
@@ -100,7 +100,35 @@ export async function create(dir, maxDepth = Infinity, currentDepth = 0) {
|
|
|
100
100
|
for (const entry of entries) {
|
|
101
101
|
if (entry.isDirectory() && !entry.name.startsWith(".") && currentDepth < maxDepth) {
|
|
102
102
|
const categoryPath = path.join(dir, entry.name);
|
|
103
|
-
|
|
103
|
+
const categoryKey = this._toapiPathKey(entry.name);
|
|
104
|
+
const categoryResult = await this._buildCategory(categoryPath, {
|
|
105
|
+
currentDepth: currentDepth + 1,
|
|
106
|
+
maxDepth,
|
|
107
|
+
mode: "eager",
|
|
108
|
+
existingApi: api
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
if (
|
|
113
|
+
api[categoryKey] &&
|
|
114
|
+
typeof api[categoryKey] === "object" &&
|
|
115
|
+
typeof categoryResult === "object" &&
|
|
116
|
+
!Array.isArray(api[categoryKey]) &&
|
|
117
|
+
!Array.isArray(categoryResult)
|
|
118
|
+
) {
|
|
119
|
+
if (this.config.debug) {
|
|
120
|
+
console.log(
|
|
121
|
+
`[DEBUG] eager: Merging subdirectory '${categoryKey}' - existing:`,
|
|
122
|
+
Object.keys(api[categoryKey]),
|
|
123
|
+
"new:",
|
|
124
|
+
Object.keys(categoryResult)
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
Object.assign(api[categoryKey], categoryResult);
|
|
129
|
+
} else {
|
|
130
|
+
api[categoryKey] = categoryResult;
|
|
131
|
+
}
|
|
104
132
|
}
|
|
105
133
|
}
|
|
106
134
|
|
|
@@ -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.
|
|
@@ -161,7 +161,8 @@ export async function create(dir, maxDepth = Infinity, currentDepth = 0) {
|
|
|
161
161
|
depth,
|
|
162
162
|
maxDepth,
|
|
163
163
|
pathParts: [key],
|
|
164
|
-
runWithCtx
|
|
164
|
+
runWithCtx,
|
|
165
|
+
existingContent: parent[key]
|
|
165
166
|
});
|
|
166
167
|
parent[key] = proxy;
|
|
167
168
|
}
|
|
@@ -173,6 +174,28 @@ export async function create(dir, maxDepth = Infinity, currentDepth = 0) {
|
|
|
173
174
|
|
|
174
175
|
function replacePlaceholder(parent, key, placeholder, value, instance, depth) {
|
|
175
176
|
if (!parent || !key) return;
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
if (
|
|
180
|
+
parent[key] !== placeholder &&
|
|
181
|
+
parent[key] &&
|
|
182
|
+
typeof parent[key] === "object" &&
|
|
183
|
+
typeof value === "object" &&
|
|
184
|
+
!Array.isArray(parent[key]) &&
|
|
185
|
+
!Array.isArray(value)
|
|
186
|
+
) {
|
|
187
|
+
if (instance?.config?.debug) {
|
|
188
|
+
console.log(`[lazy] MERGE CASE DETECTED for '${key}' - existing:`, Object.keys(parent[key]), "new:", Object.keys(value));
|
|
189
|
+
console.log(`[lazy] Before merge - parent[${key}]:`, parent[key]);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
Object.assign(parent[key], value);
|
|
193
|
+
if (instance?.config?.debug) {
|
|
194
|
+
console.log(`[lazy] After merge - parent[${key}]:`, Object.keys(parent[key]));
|
|
195
|
+
}
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
176
199
|
if (parent[key] !== placeholder) return;
|
|
177
200
|
|
|
178
201
|
|
|
@@ -215,9 +238,43 @@ function replacePlaceholder(parent, key, placeholder, value, instance, depth) {
|
|
|
215
238
|
}
|
|
216
239
|
|
|
217
240
|
try {
|
|
218
|
-
|
|
241
|
+
|
|
242
|
+
if (
|
|
243
|
+
parent[finalKey] &&
|
|
244
|
+
typeof parent[finalKey] === "object" &&
|
|
245
|
+
typeof value === "object" &&
|
|
246
|
+
!Array.isArray(parent[finalKey]) &&
|
|
247
|
+
!Array.isArray(value)
|
|
248
|
+
) {
|
|
249
|
+
if (instance?.config?.debug) {
|
|
250
|
+
console.log(`[lazy] Merging subdirectory '${finalKey}' - existing:`, Object.keys(parent[finalKey]), "new:", Object.keys(value));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
Object.assign(parent[finalKey], value);
|
|
254
|
+
} else {
|
|
255
|
+
Object.defineProperty(parent, finalKey, { value, writable: true, enumerable: true, configurable: true });
|
|
256
|
+
}
|
|
219
257
|
} catch {
|
|
220
|
-
|
|
258
|
+
|
|
259
|
+
if (
|
|
260
|
+
parent[finalKey] &&
|
|
261
|
+
typeof parent[finalKey] === "object" &&
|
|
262
|
+
typeof value === "object" &&
|
|
263
|
+
!Array.isArray(parent[finalKey]) &&
|
|
264
|
+
!Array.isArray(value)
|
|
265
|
+
) {
|
|
266
|
+
if (instance?.config?.debug) {
|
|
267
|
+
console.log(
|
|
268
|
+
`[lazy] Merging subdirectory '${finalKey}' (fallback) - existing:`,
|
|
269
|
+
Object.keys(parent[finalKey]),
|
|
270
|
+
"new:",
|
|
271
|
+
Object.keys(value)
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
Object.assign(parent[finalKey], value);
|
|
275
|
+
} else {
|
|
276
|
+
parent[finalKey] = value;
|
|
277
|
+
}
|
|
221
278
|
}
|
|
222
279
|
if (instance?.config?.debug) {
|
|
223
280
|
console.log(`[lazy][materialize] replaced ${key}${finalKey !== key ? ` -> ${finalKey}` : ""} (${typeof value})`);
|
|
@@ -228,7 +285,7 @@ function replacePlaceholder(parent, key, placeholder, value, instance, depth) {
|
|
|
228
285
|
}
|
|
229
286
|
|
|
230
287
|
|
|
231
|
-
function createFolderProxy({ subDirPath, key, parent, instance, depth, maxDepth, pathParts, runWithCtx }) {
|
|
288
|
+
function createFolderProxy({ subDirPath, key, parent, instance, depth, maxDepth, pathParts, runWithCtx, existingContent }) {
|
|
232
289
|
let materialized = null;
|
|
233
290
|
let inFlight = null;
|
|
234
291
|
|
|
@@ -243,6 +300,7 @@ function createFolderProxy({ subDirPath, key, parent, instance, depth, maxDepth,
|
|
|
243
300
|
currentDepth: depth,
|
|
244
301
|
maxDepth,
|
|
245
302
|
mode: "lazy",
|
|
303
|
+
existingApi: parent,
|
|
246
304
|
subdirHandler: ({ subDirPath: nestedPath, key: nestedKey, categoryModules, currentDepth: cd, maxDepth: md }) =>
|
|
247
305
|
createFolderProxy({
|
|
248
306
|
subDirPath: nestedPath,
|
|
@@ -256,6 +314,29 @@ function createFolderProxy({ subDirPath, key, parent, instance, depth, maxDepth,
|
|
|
256
314
|
})
|
|
257
315
|
});
|
|
258
316
|
materialized = value;
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
if (
|
|
320
|
+
existingContent &&
|
|
321
|
+
typeof existingContent === "object" &&
|
|
322
|
+
typeof materialized === "object" &&
|
|
323
|
+
!Array.isArray(existingContent) &&
|
|
324
|
+
!Array.isArray(materialized)
|
|
325
|
+
) {
|
|
326
|
+
if (instance?.config?.debug) {
|
|
327
|
+
console.log(
|
|
328
|
+
`[lazy] Merging existing content with materialized subdirectory '${key}' - existing:`,
|
|
329
|
+
Object.keys(existingContent),
|
|
330
|
+
"new:",
|
|
331
|
+
Object.keys(materialized)
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
materialized = Object.assign({}, existingContent, materialized);
|
|
336
|
+
} else if (existingContent && !materialized) {
|
|
337
|
+
|
|
338
|
+
materialized = existingContent;
|
|
339
|
+
}
|
|
259
340
|
if (instance?.config?.debug) {
|
|
260
341
|
try {
|
|
261
342
|
const infoKeys = materialized && typeof materialized === "object" ? Object.keys(materialized) : [];
|
|
@@ -345,6 +426,11 @@ function createFolderProxy({ subDirPath, key, parent, instance, depth, maxDepth,
|
|
|
345
426
|
return pathParts.length > 0 ? pathParts.join(".") : undefined;
|
|
346
427
|
}
|
|
347
428
|
|
|
429
|
+
if (prop === "__metadata" || prop === "__sourceFolder") {
|
|
430
|
+
|
|
431
|
+
return Reflect.get(_t, prop);
|
|
432
|
+
}
|
|
433
|
+
|
|
348
434
|
if (materialized) {
|
|
349
435
|
if (materialized && (typeof materialized === "object" || typeof materialized === "function")) return materialized[prop];
|
|
350
436
|
return undefined;
|
|
@@ -485,6 +571,10 @@ function createFolderProxy({ subDirPath, key, parent, instance, depth, maxDepth,
|
|
|
485
571
|
if (prop === "__materialized") {
|
|
486
572
|
return { configurable: true, enumerable: false, writable: true, value: materialized };
|
|
487
573
|
}
|
|
574
|
+
|
|
575
|
+
if (prop === "__metadata" || prop === "__sourceFolder") {
|
|
576
|
+
return Reflect.getOwnPropertyDescriptor(lazy_lazyTarget, prop);
|
|
577
|
+
}
|
|
488
578
|
if (prop === "prototype") {
|
|
489
579
|
|
|
490
580
|
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
|
|
|
@@ -565,3 +566,6 @@ export const context = runtime_createLiveBinding("context");
|
|
|
565
566
|
export const reference = runtime_createLiveBinding("reference");
|
|
566
567
|
|
|
567
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.
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
setActiveInstance,
|
|
26
26
|
getCurrentActiveInstanceId
|
|
27
27
|
} from "@cldmv/slothlet/helpers/instance-manager";
|
|
28
|
+
import { metadataAPI } from "@cldmv/slothlet/helpers/metadata-api";
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
export const requestALS = new AsyncLocalStorage();
|
|
@@ -433,3 +434,6 @@ export const contextManager = {
|
|
|
433
434
|
set: setContext,
|
|
434
435
|
runWithCtx
|
|
435
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.
|
|
@@ -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
|
+
);
|
package/dist/slothlet.mjs
CHANGED
|
@@ -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.
|
|
@@ -75,6 +75,11 @@ let DEBUG = process.argv.includes("--slothletdebug")
|
|
|
75
75
|
: false;
|
|
76
76
|
|
|
77
77
|
|
|
78
|
+
if (DEBUG && !process.env.SLOTHLET_DEBUG) {
|
|
79
|
+
process.env.SLOTHLET_DEBUG = "1";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
78
83
|
export const self = {};
|
|
79
84
|
|
|
80
85
|
|
|
@@ -143,7 +148,17 @@ const slothletObject = {
|
|
|
143
148
|
reference: {},
|
|
144
149
|
mode: "singleton",
|
|
145
150
|
loaded: false,
|
|
146
|
-
config: {
|
|
151
|
+
config: {
|
|
152
|
+
lazy: false,
|
|
153
|
+
apiDepth: Infinity,
|
|
154
|
+
debug: DEBUG,
|
|
155
|
+
dir: null,
|
|
156
|
+
sanitize: null,
|
|
157
|
+
allowApiOverwrite: true,
|
|
158
|
+
enableModuleOwnership: false
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
_moduleOwnership: new Map(),
|
|
147
162
|
_dispose: null,
|
|
148
163
|
_boundAPIShutdown: null,
|
|
149
164
|
instanceId: null,
|
|
@@ -391,7 +406,7 @@ const slothletObject = {
|
|
|
391
406
|
|
|
392
407
|
|
|
393
408
|
async _buildCategory(categoryPath, options = {}) {
|
|
394
|
-
const { currentDepth = 0, maxDepth = Infinity, mode = "eager", subdirHandler } = options;
|
|
409
|
+
const { currentDepth = 0, maxDepth = Infinity, mode = "eager", subdirHandler, existingApi } = options;
|
|
395
410
|
|
|
396
411
|
|
|
397
412
|
return buildCategoryStructure(categoryPath, {
|
|
@@ -399,7 +414,8 @@ const slothletObject = {
|
|
|
399
414
|
maxDepth,
|
|
400
415
|
mode,
|
|
401
416
|
subdirHandler,
|
|
402
|
-
instance: this
|
|
417
|
+
instance: this,
|
|
418
|
+
existingApi
|
|
403
419
|
});
|
|
404
420
|
},
|
|
405
421
|
|
|
@@ -636,6 +652,17 @@ const slothletObject = {
|
|
|
636
652
|
}
|
|
637
653
|
|
|
638
654
|
|
|
655
|
+
const instanceIdDesc = Object.getOwnPropertyDescriptor(boundApi, "instanceId");
|
|
656
|
+
if (!instanceIdDesc || instanceIdDesc.configurable) {
|
|
657
|
+
Object.defineProperty(boundApi, "instanceId", {
|
|
658
|
+
value: this.instanceId,
|
|
659
|
+
writable: false,
|
|
660
|
+
configurable: true,
|
|
661
|
+
enumerable: false
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
|
|
639
666
|
const scopeDesc = Object.getOwnPropertyDescriptor(boundApi, "scope");
|
|
640
667
|
if (!scopeDesc || scopeDesc.configurable) {
|
|
641
668
|
Object.defineProperty(boundApi, "scope", {
|
|
@@ -678,8 +705,45 @@ const slothletObject = {
|
|
|
678
705
|
},
|
|
679
706
|
|
|
680
707
|
|
|
681
|
-
|
|
682
|
-
|
|
708
|
+
_registerApiOwnership(apiPath, moduleId) {
|
|
709
|
+
if (!this.config.enableModuleOwnership) return;
|
|
710
|
+
this._moduleOwnership.set(apiPath, moduleId);
|
|
711
|
+
if (this.config.debug) {
|
|
712
|
+
console.log(`[DEBUG] Registered ownership: ${apiPath} -> ${moduleId}`);
|
|
713
|
+
}
|
|
714
|
+
},
|
|
715
|
+
|
|
716
|
+
|
|
717
|
+
_getApiOwnership(apiPath) {
|
|
718
|
+
if (!this.config.enableModuleOwnership) return null;
|
|
719
|
+
return this._moduleOwnership.get(apiPath) || null;
|
|
720
|
+
},
|
|
721
|
+
|
|
722
|
+
|
|
723
|
+
_validateModuleOwnership(apiPath, moduleId, forceOverwrite) {
|
|
724
|
+
if (!this.config.enableModuleOwnership || !forceOverwrite) return false;
|
|
725
|
+
|
|
726
|
+
const existingOwner = this._getApiOwnership(apiPath);
|
|
727
|
+
if (!existingOwner) {
|
|
728
|
+
|
|
729
|
+
return true;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
if (existingOwner === moduleId) {
|
|
734
|
+
return true;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
|
|
738
|
+
if (this.config.debug) {
|
|
739
|
+
console.log(`[DEBUG] Ownership conflict: ${apiPath} owned by ${existingOwner}, attempted by ${moduleId}`);
|
|
740
|
+
}
|
|
741
|
+
return false;
|
|
742
|
+
},
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
async addApi(apiPath, folderPath, metadata = {}, options = {}) {
|
|
746
|
+
return addApiFromFolder({ apiPath, folderPath, instance: this, metadata, options });
|
|
683
747
|
},
|
|
684
748
|
|
|
685
749
|
|