@cldmv/slothlet 2.10.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 +27 -20
- package/dist/lib/helpers/api_builder/add_api.mjs +264 -3
- package/dist/lib/helpers/api_builder/construction.mjs +40 -2
- package/dist/lib/helpers/api_builder/decisions.mjs +15 -4
- package/dist/lib/helpers/resolve-from-caller.mjs +47 -10
- package/dist/lib/modes/slothlet_eager.mjs +29 -1
- package/dist/lib/modes/slothlet_lazy.mjs +85 -4
- package/dist/slothlet.mjs +53 -5
- 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 +55 -29
- package/types/dist/lib/helpers/api_builder/add_api.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/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/slothlet.d.mts +8 -0
- package/types/dist/slothlet.d.mts.map +1 -1
- package/types/index.d.mts.map +1 -0
|
@@ -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) : [];
|
package/dist/slothlet.mjs
CHANGED
|
@@ -148,7 +148,17 @@ const slothletObject = {
|
|
|
148
148
|
reference: {},
|
|
149
149
|
mode: "singleton",
|
|
150
150
|
loaded: false,
|
|
151
|
-
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(),
|
|
152
162
|
_dispose: null,
|
|
153
163
|
_boundAPIShutdown: null,
|
|
154
164
|
instanceId: null,
|
|
@@ -396,7 +406,7 @@ const slothletObject = {
|
|
|
396
406
|
|
|
397
407
|
|
|
398
408
|
async _buildCategory(categoryPath, options = {}) {
|
|
399
|
-
const { currentDepth = 0, maxDepth = Infinity, mode = "eager", subdirHandler } = options;
|
|
409
|
+
const { currentDepth = 0, maxDepth = Infinity, mode = "eager", subdirHandler, existingApi } = options;
|
|
400
410
|
|
|
401
411
|
|
|
402
412
|
return buildCategoryStructure(categoryPath, {
|
|
@@ -404,7 +414,8 @@ const slothletObject = {
|
|
|
404
414
|
maxDepth,
|
|
405
415
|
mode,
|
|
406
416
|
subdirHandler,
|
|
407
|
-
instance: this
|
|
417
|
+
instance: this,
|
|
418
|
+
existingApi
|
|
408
419
|
});
|
|
409
420
|
},
|
|
410
421
|
|
|
@@ -694,8 +705,45 @@ const slothletObject = {
|
|
|
694
705
|
},
|
|
695
706
|
|
|
696
707
|
|
|
697
|
-
|
|
698
|
-
|
|
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 });
|
|
699
747
|
},
|
|
700
748
|
|
|
701
749
|
|