@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
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# Slothlet API Rules - Verified Documentation
|
|
2
2
|
|
|
3
3
|
> **Verification Status**: Each rule has been systematically verified against actual test files and source code.
|
|
4
|
+
> **Last Updated**: December 30, 2025
|
|
5
|
+
> **Commit**: `a50531d1ba712f0c4efd9ab9b7cf8f62a0d379da`
|
|
6
|
+
> **Note**: Source code has been refactored into modular structure. See [API-RULES-CONDITIONS.md](API-RULES-CONDITIONS.md) for complete conditional logic documentation with exact line numbers.
|
|
4
7
|
|
|
5
8
|
## Methodology
|
|
6
9
|
|
|
@@ -26,7 +29,7 @@ Each rule documents:
|
|
|
26
29
|
- [x] Rule 9: Function Name Preference Over Sanitization ✅ **FULLY VERIFIED** (Multiple examples verified: autoIP, parseJSON, getHTTPStatus, XMLParser)
|
|
27
30
|
- [x] Rule 10: Generic Filename Parent-Level Promotion ✅ **VERIFIED** (nest4/singlefile.mjs example verified with api_tests/api_test)
|
|
28
31
|
|
|
29
|
-
> **Note**: Rule 11 (Single File Context Flattening) has been **intentionally removed** from slothlet for architectural reasons. The rule reduced API path flexibility and was commented out in source code (
|
|
32
|
+
> **Note**: Rule 11 (Single File Context Flattening) has been **intentionally removed** from slothlet for architectural reasons. The rule reduced API path flexibility and was commented out in source code. See [C06](API-RULES-CONDITIONS.md#c06-single-file-context-commented-out) in API-RULES-CONDITIONS.md for details. This maintains cleaner API namespacing while preserving predictable path structures.
|
|
30
33
|
|
|
31
34
|
---
|
|
32
35
|
|
|
@@ -72,12 +75,14 @@ node tests/debug-slothlet.mjs
|
|
|
72
75
|
# Confirms flattening works: api.math.add (not api.math.math.add)
|
|
73
76
|
```
|
|
74
77
|
|
|
75
|
-
**Source Code Location**: `src/lib/helpers/api_builder.mjs`
|
|
76
|
-
**Git Commit**: `
|
|
78
|
+
**Source Code Location**: `src/lib/helpers/api_builder/decisions.mjs` - `getFlatteningDecision()` function [Lines 87-189](../src/lib/helpers/api_builder/decisions.mjs#L87-L189)
|
|
79
|
+
**Git Commit**: `a50531d1ba712f0c4efd9ab9b7cf8f62a0d379da`
|
|
80
|
+
**Specific Condition**: See [C05](API-RULES-CONDITIONS.md#c05-filename-matches-container-category-level-flatten) in API-RULES-CONDITIONS.md
|
|
77
81
|
**Technical Implementation**:
|
|
78
82
|
|
|
79
83
|
```javascript
|
|
80
|
-
//
|
|
84
|
+
// C05: Filename Matches Container (Category-Level Flatten)
|
|
85
|
+
// Location: src/lib/helpers/api_builder/decisions.mjs Line 154
|
|
81
86
|
if (categoryName && fileName === categoryName && !moduleHasDefault && moduleKeys.length > 0) {
|
|
82
87
|
return {
|
|
83
88
|
shouldFlatten: true,
|
|
@@ -121,8 +126,9 @@ api.config.username; // → "admin"
|
|
|
121
126
|
api.config.secure; // → true
|
|
122
127
|
```
|
|
123
128
|
|
|
124
|
-
**Source Code Location**: `src/lib/helpers/api_builder.mjs`
|
|
125
|
-
**Git Commit**: `
|
|
129
|
+
**Source Code Location**: `src/lib/helpers/api_builder/decisions.mjs` - `processModuleForAPI()` function [Lines 315-466](../src/lib/helpers/api_builder/decisions.mjs#L315-L466)
|
|
130
|
+
**Git Commit**: `a50531d1ba712f0c4efd9ab9b7cf8f62a0d379da`
|
|
131
|
+
**Specific Condition**: See [C09b](API-RULES-CONDITIONS.md#c09b-flatten-to-rootcategory) in API-RULES-CONDITIONS.md
|
|
126
132
|
**Technical Implementation**:
|
|
127
133
|
|
|
128
134
|
```javascript
|
|
@@ -151,8 +157,8 @@ node tests/debug-slothlet.mjs
|
|
|
151
157
|
**Status**: ✅ **VERIFIED**
|
|
152
158
|
|
|
153
159
|
**Condition**: Folders with no module files (`moduleFiles.length === 0`)
|
|
154
|
-
**Source Code Location**: `src/
|
|
155
|
-
**Git Commit**: `
|
|
160
|
+
**Source Code Location**: `src/slothlet.mjs` [Lines 318-319](../src/slothlet.mjs#L318-L319)
|
|
161
|
+
**Git Commit**: `a50531d1ba712f0c4efd9ab9b7cf8f62a0d379da`
|
|
156
162
|
**Processing Path**: All paths (detected in `analyzeDirectoryStructure`)
|
|
157
163
|
|
|
158
164
|
**Verified Example**:
|
|
@@ -182,10 +188,10 @@ node tests/debug-slothlet.mjs
|
|
|
182
188
|
|
|
183
189
|
**Technical Details**:
|
|
184
190
|
|
|
185
|
-
- **Detection**: `analyzeDirectoryStructure`
|
|
186
|
-
- **Source Code**: `src/lib/helpers/api_builder.mjs` lines 318-319
|
|
191
|
+
- **Detection**: `analyzeDirectoryStructure` in `src/lib/helpers/api_builder/analysis.mjs` detects empty directories
|
|
187
192
|
- **Handling**: Empty `processedModules` and `subDirectories` arrays result in empty object
|
|
188
193
|
- **API Result**: Empty folder becomes empty object property on API
|
|
194
|
+
- **Implementation**: See `buildCategoryStructure()` in `src/lib/helpers/api_builder/construction.mjs`
|
|
189
195
|
|
|
190
196
|
---
|
|
191
197
|
|
|
@@ -195,8 +201,9 @@ node tests/debug-slothlet.mjs
|
|
|
195
201
|
|
|
196
202
|
**Condition**: When a module has a default export (function or object)
|
|
197
203
|
**Behavior**: Default export becomes the container callable/content, named exports spread to same level
|
|
198
|
-
**Source Code**: `src/lib/helpers/api_builder.mjs`
|
|
199
|
-
**
|
|
204
|
+
**Source Code**: `processModuleForAPI()` in `src/lib/helpers/api_builder/decisions.mjs` [L315-466](../src/lib/helpers/api_builder/decisions.mjs#L315-L466)
|
|
205
|
+
**Detailed Conditions**: See [C08 (Has Default Function Export)](API-RULES-CONDITIONS.md#c08-has-default-function-export) in API-RULES-CONDITIONS.md
|
|
206
|
+
**Git Commit**: `a50531d1ba712f0c4efd9ab9b7cf8f62a0d379da`
|
|
200
207
|
|
|
201
208
|
**Pattern A: Default Function + Named Exports**:
|
|
202
209
|
|
|
@@ -251,14 +258,17 @@ api.funcmod("World"); // → "Hello, World!" (default export becomes namespaced
|
|
|
251
258
|
**Technical Implementation**:
|
|
252
259
|
|
|
253
260
|
```javascript
|
|
254
|
-
//
|
|
261
|
+
// C08c: Traditional Default Function - Root API
|
|
262
|
+
// src/lib/helpers/api_builder/decisions.mjs Line 378
|
|
255
263
|
if (mode === "root" && getRootDefault && setRootDefault && !hasMultipleDefaultExports && !getRootDefault()) {
|
|
256
264
|
// Root context: Make API itself callable
|
|
257
265
|
setRootDefault(defaultFunction);
|
|
258
|
-
|
|
266
|
+
rootDefaultSet = true;
|
|
259
267
|
} else {
|
|
260
|
-
//
|
|
268
|
+
// C08d: Function As Namespace (Subfolder context)
|
|
269
|
+
// Line 384+
|
|
261
270
|
apiAssignments[apiPathKey] = mod;
|
|
271
|
+
namespaced = true;
|
|
262
272
|
}
|
|
263
273
|
```
|
|
264
274
|
|
|
@@ -282,8 +292,9 @@ node -e "const slothlet = await import('./index.mjs'); const api = await slothle
|
|
|
282
292
|
|
|
283
293
|
**Condition**: When a container has MULTIPLE files with default exports
|
|
284
294
|
**Behavior**: Files with defaults become namespaces, files without defaults flatten to container level
|
|
285
|
-
**Source Code**: `src/lib/helpers/multidefault.mjs`
|
|
286
|
-
**
|
|
295
|
+
**Source Code**: `multidefault_getFlatteningDecision()` in `src/lib/helpers/multidefault.mjs` [L178-262](../src/lib/helpers/multidefault.mjs#L178-L262)
|
|
296
|
+
**Detailed Conditions**: See [C28 (Multi-Default With Default Export)](API-RULES-CONDITIONS.md#c28-multi-default-with-default-export) and [C29 (Multi-Default Without Default Export)](API-RULES-CONDITIONS.md#c29-multi-default-without-default-export) in API-RULES-CONDITIONS.md
|
|
297
|
+
**Git Commit**: `a50531d1ba712f0c4efd9ab9b7cf8f62a0d379da`
|
|
287
298
|
|
|
288
299
|
**Example: api_tv_test folder demonstrates both patterns**:
|
|
289
300
|
|
|
@@ -316,24 +327,26 @@ api.disconnect(); // from connection.mjs
|
|
|
316
327
|
**Technical Implementation**:
|
|
317
328
|
|
|
318
329
|
```javascript
|
|
319
|
-
//
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}
|
|
330
|
+
// C28: Multi-Default With Default Export
|
|
331
|
+
// src/lib/helpers/multidefault.mjs Line 210-211
|
|
332
|
+
if (hasMultipleDefaultExports) {
|
|
333
|
+
if (moduleHasDefault) {
|
|
334
|
+
return {
|
|
335
|
+
shouldFlatten: false,
|
|
336
|
+
preserveAsNamespace: true,
|
|
337
|
+
reason: "multi-default context with default export"
|
|
338
|
+
};
|
|
339
|
+
}
|
|
328
340
|
|
|
329
|
-
//
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
341
|
+
// C29: Multi-Default Without Default Export
|
|
342
|
+
// Line 219
|
|
343
|
+
else {
|
|
344
|
+
return {
|
|
345
|
+
shouldFlatten: true,
|
|
346
|
+
flattenToRoot: true,
|
|
347
|
+
reason: "multi-default context without default export"
|
|
348
|
+
};
|
|
349
|
+
}
|
|
337
350
|
}
|
|
338
351
|
```
|
|
339
352
|
|
|
@@ -354,8 +367,8 @@ node -e "const slothlet = await import('./index.mjs'); const api = await slothle
|
|
|
354
367
|
|
|
355
368
|
**Condition**: When filename matches an exported property name (creates potential infinite nesting)
|
|
356
369
|
**Behavior**: Always preserve as namespace to avoid `api.config.config.config...` infinite loops
|
|
357
|
-
**Source Code Conditions**: C01, C08b, C09c,
|
|
358
|
-
**Git Commit**: `
|
|
370
|
+
**Source Code Conditions**: [C01](API-RULES-CONDITIONS.md#c01-self-referential-check), [C08b](API-RULES-CONDITIONS.md#c08b-self-referential-function), [C09c](API-RULES-CONDITIONS.md#c09c-self-referential-non-function), [C20](API-RULES-CONDITIONS.md#c20-multi-file-self-referential), [C27](API-RULES-CONDITIONS.md#c27-multi-default-self-referential) (5 implementations)
|
|
371
|
+
**Git Commit**: `a50531d1ba712f0c4efd9ab9b7cf8f62a0d379da`
|
|
359
372
|
|
|
360
373
|
**Verified Examples**:
|
|
361
374
|
|
|
@@ -386,7 +399,7 @@ node -e "const slothlet = await import('./index.mjs'); const api = await slothle
|
|
|
386
399
|
**Technical Implementation** (5 locations):
|
|
387
400
|
|
|
388
401
|
```javascript
|
|
389
|
-
// C01: getFlatteningDecision() -
|
|
402
|
+
// C01: getFlatteningDecision() - decisions.mjs Line 105
|
|
390
403
|
if (isSelfReferential) {
|
|
391
404
|
return {
|
|
392
405
|
shouldFlatten: false,
|
|
@@ -395,25 +408,24 @@ if (isSelfReferential) {
|
|
|
395
408
|
};
|
|
396
409
|
}
|
|
397
410
|
|
|
398
|
-
// C08b: processModuleForAPI() function exports -
|
|
411
|
+
// C08b: processModuleForAPI() function exports - decisions.mjs Line 361
|
|
399
412
|
else if (isSelfReferential) {
|
|
400
413
|
apiAssignments[apiPathKey] = mod;
|
|
401
414
|
namespaced = true;
|
|
402
415
|
}
|
|
403
416
|
|
|
404
|
-
// C09c: processModuleForAPI() non-function exports -
|
|
417
|
+
// C09c: processModuleForAPI() non-function exports - decisions.mjs Line 440
|
|
405
418
|
else if (isSelfReferential) {
|
|
406
419
|
apiAssignments[apiPathKey] = mod[apiPathKey] || mod;
|
|
407
420
|
namespaced = true;
|
|
408
421
|
}
|
|
409
422
|
|
|
410
|
-
//
|
|
423
|
+
// C20: buildCategoryDecisions() multi-file - decisions.mjs Line 846
|
|
411
424
|
else if (selfReferentialFiles.has(moduleName)) {
|
|
412
425
|
moduleDecision.type = "self-referential";
|
|
413
|
-
moduleDecision.specialHandling = "self-referential-namespace";
|
|
414
426
|
}
|
|
415
427
|
|
|
416
|
-
//
|
|
428
|
+
// C27: multidefault_getFlatteningDecision() - multidefault.mjs Line 199
|
|
417
429
|
if (isSelfReferential) {
|
|
418
430
|
return {
|
|
419
431
|
shouldFlatten: false,
|
|
@@ -441,8 +453,8 @@ node tests/debug-slothlet.mjs
|
|
|
441
453
|
|
|
442
454
|
**Condition**: Module exports single named export that matches sanitized filename
|
|
443
455
|
**Behavior**: Use the export contents directly instead of wrapping in namespace
|
|
444
|
-
**Source Code Conditions**: C04,
|
|
445
|
-
**Git Commit**: `
|
|
456
|
+
**Source Code Conditions**: [C04](API-RULES-CONDITIONS.md#c04-auto-flatten-single-named-export-matching-filename), [C18](API-RULES-CONDITIONS.md#c18-single-named-export-match-secondary-check), [C21c](API-RULES-CONDITIONS.md#c21c-single-named-export-match), [C30](API-RULES-CONDITIONS.md#c30-single-named-export-match) (4 implementations)
|
|
457
|
+
**Git Commit**: `a50531d1ba712f0c4efd9ab9b7cf8f62a0d379da`
|
|
446
458
|
|
|
447
459
|
**Verified Examples**:
|
|
448
460
|
|
|
@@ -473,7 +485,7 @@ node -e "(async () => { const slothlet = await import('./index.mjs'); const api
|
|
|
473
485
|
**Technical Implementation** (4 locations):
|
|
474
486
|
|
|
475
487
|
```javascript
|
|
476
|
-
// C04: getFlatteningDecision() -
|
|
488
|
+
// C04: getFlatteningDecision() - decisions.mjs Line 142
|
|
477
489
|
if (moduleKeys.length === 1 && moduleKeys[0] === apiPathKey) {
|
|
478
490
|
return {
|
|
479
491
|
shouldFlatten: true,
|
|
@@ -482,21 +494,25 @@ if (moduleKeys.length === 1 && moduleKeys[0] === apiPathKey) {
|
|
|
482
494
|
};
|
|
483
495
|
}
|
|
484
496
|
|
|
485
|
-
//
|
|
497
|
+
// C18: buildCategoryDecisions() - decisions.mjs Line 693
|
|
486
498
|
if (moduleKeys.length === 1 && moduleKeys[0] === moduleName) {
|
|
487
|
-
return
|
|
499
|
+
return {
|
|
500
|
+
shouldFlatten: true,
|
|
501
|
+
flattenType: "object-auto-flatten"
|
|
502
|
+
};
|
|
488
503
|
}
|
|
489
504
|
|
|
490
|
-
//
|
|
505
|
+
// C21c: buildCategoryDecisions() multi-file - decisions.mjs Line 867
|
|
491
506
|
else if (moduleKeys.length === 1 && moduleKeys[0] === apiPathKey) {
|
|
492
507
|
moduleDecision.shouldFlatten = true;
|
|
493
508
|
moduleDecision.flattenType = "single-named-export-match";
|
|
494
509
|
}
|
|
495
510
|
|
|
496
|
-
//
|
|
511
|
+
// C30: multidefault_getFlatteningDecision() - multidefault.mjs Line 231
|
|
497
512
|
if (moduleKeys.length === 1 && moduleKeys[0] === apiPathKey) {
|
|
498
513
|
return {
|
|
499
514
|
shouldFlatten: true,
|
|
515
|
+
flattenToRoot: false,
|
|
500
516
|
reason: "single named export matching filename"
|
|
501
517
|
};
|
|
502
518
|
}
|
|
@@ -521,7 +537,7 @@ node tests/debug-slothlet.mjs
|
|
|
521
537
|
**Condition**: Various patterns for eliminating unnecessary nesting in single-file folders
|
|
522
538
|
**Behavior**: Multiple sub-patterns for flattening single files based on different criteria
|
|
523
539
|
**Source Code Conditions**: C10, C11a/C11b/C11c, C13, C15 (buildCategoryStructure single-file logic)
|
|
524
|
-
**Git Commit**: `
|
|
540
|
+
**Git Commit**: `a50531d1ba712f0c4efd9ab9b7cf8f62a0d379da`
|
|
525
541
|
|
|
526
542
|
**Pattern A: Object Export Flattening** (C11a/C11b/C11c):
|
|
527
543
|
|
|
@@ -589,24 +605,39 @@ api.funcmod("test"); // → "Hello, test!" ✅ VERIFIED with api_tests/api_test
|
|
|
589
605
|
**Technical Implementation**:
|
|
590
606
|
|
|
591
607
|
```javascript
|
|
592
|
-
// C10: Single-file function folder match -
|
|
608
|
+
// C10: Single-file function folder match - decisions.mjs Line 584
|
|
593
609
|
if (moduleName === categoryName && typeof mod === "function" && currentDepth > 0) {
|
|
594
|
-
return
|
|
610
|
+
return {
|
|
611
|
+
shouldFlatten: true,
|
|
612
|
+
flattenType: "function-folder-match"
|
|
613
|
+
};
|
|
595
614
|
}
|
|
596
615
|
|
|
597
|
-
//
|
|
598
|
-
if (
|
|
599
|
-
|
|
616
|
+
// C12: Object auto-flatten - decisions.mjs Line 604-609
|
|
617
|
+
if (moduleName === categoryName && mod && typeof mod === "object" && currentDepth > 0) {
|
|
618
|
+
if (moduleKeys.length === 1 && moduleKeys[0] === moduleName) {
|
|
619
|
+
return {
|
|
620
|
+
shouldFlatten: true,
|
|
621
|
+
flattenType: "object-auto-flatten"
|
|
622
|
+
};
|
|
623
|
+
}
|
|
600
624
|
}
|
|
601
625
|
|
|
602
|
-
//
|
|
626
|
+
// C15: Function name matches folder - decisions.mjs Line 663
|
|
603
627
|
if (functionNameMatchesFolder && currentDepth > 0) {
|
|
604
|
-
return
|
|
628
|
+
return {
|
|
629
|
+
shouldFlatten: true,
|
|
630
|
+
flattenType: "function-folder-match",
|
|
631
|
+
preferredName: mod.name
|
|
632
|
+
};
|
|
605
633
|
}
|
|
606
634
|
|
|
607
|
-
//
|
|
608
|
-
if (typeof mod === "function" && mod.__slothletDefault === true && currentDepth > 0) {
|
|
609
|
-
return
|
|
635
|
+
// C17: Default function export - decisions.mjs Line 680-682
|
|
636
|
+
if (typeof mod === "function" && (!mod.name || mod.name === "default" || mod.__slothletDefault === true) && currentDepth > 0) {
|
|
637
|
+
return {
|
|
638
|
+
shouldFlatten: true,
|
|
639
|
+
flattenType: "default-function"
|
|
640
|
+
};
|
|
610
641
|
}
|
|
611
642
|
```
|
|
612
643
|
|
|
@@ -620,8 +651,8 @@ if (typeof mod === "function" && mod.__slothletDefault === true && currentDepth
|
|
|
620
651
|
|
|
621
652
|
**Condition**: Original function name semantically matches sanitized filename but has different casing
|
|
622
653
|
**Behavior**: Use original function name instead of sanitized version to preserve conventions (IP, JSON, HTTP, etc.)
|
|
623
|
-
**Source Code Conditions**:
|
|
624
|
-
**Git Commit**: `
|
|
654
|
+
**Source Code Conditions**: [C16](API-RULES-CONDITIONS.md#c16-function-name-matches-filename-name-preference), [C19](API-RULES-CONDITIONS.md#c19-multi-file-function-with-preferred-name) (function name preference logic)
|
|
655
|
+
**Git Commit**: `a50531d1ba712f0c4efd9ab9b7cf8f62a0d379da`
|
|
625
656
|
|
|
626
657
|
**Verified Examples**:
|
|
627
658
|
|
|
@@ -644,10 +675,19 @@ if (functionNameMatchesFilename) {
|
|
|
644
675
|
return { [mod.name]: mod }; // Use original function name
|
|
645
676
|
}
|
|
646
677
|
|
|
647
|
-
//
|
|
678
|
+
// C16: Function name matches filename - decisions.mjs Line 671
|
|
679
|
+
if (functionNameMatchesFilename) {
|
|
680
|
+
return {
|
|
681
|
+
shouldFlatten: false,
|
|
682
|
+
preferredName: mod.name
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// C19: Multi-file function with preferred name - decisions.mjs Line 844
|
|
648
687
|
if (hasPreferredName) {
|
|
649
|
-
|
|
650
|
-
|
|
688
|
+
return {
|
|
689
|
+
specialHandling: "preferred-export-names"
|
|
690
|
+
};
|
|
651
691
|
}
|
|
652
692
|
|
|
653
693
|
// Function name preference logic checks:
|
|
@@ -676,8 +716,8 @@ node tests/debug-slothlet.mjs
|
|
|
676
716
|
|
|
677
717
|
**Condition**: Single export with generic filename (singlefile, index, main, default) in subfolder
|
|
678
718
|
**Behavior**: Promote export to parent level to eliminate meaningless intermediate namespace
|
|
679
|
-
**Source Code Conditions**:
|
|
680
|
-
**Git Commit**: `
|
|
719
|
+
**Source Code Conditions**: [C14](API-RULES-CONDITIONS.md#c14-parent-level-flattening-generic-filenames) (parent-level flattening logic)
|
|
720
|
+
**Git Commit**: `a50531d1ba712f0c4efd9ab9b7cf8f62a0d379da`
|
|
681
721
|
|
|
682
722
|
**Verified Examples**:
|
|
683
723
|
|
|
@@ -695,14 +735,16 @@ api.advanced.nest4.beta("test"); // → "Hello, test!" ✅ VERIFIED with api_tes
|
|
|
695
735
|
**Technical Implementation**:
|
|
696
736
|
|
|
697
737
|
```javascript
|
|
698
|
-
//
|
|
738
|
+
// C14: Parent-level flattening detection - decisions.mjs Line 641
|
|
699
739
|
if (moduleFiles.length === 1 && currentDepth > 0 && mod && typeof mod === "object" && !Array.isArray(mod)) {
|
|
700
740
|
const isGenericFilename = ["singlefile", "index", "main", "default"].includes(fileName.toLowerCase());
|
|
701
741
|
|
|
702
|
-
//
|
|
742
|
+
// Line 649: Generic filename single export promotion
|
|
703
743
|
if (moduleKeys.length === 1 && isGenericFilename) {
|
|
704
|
-
|
|
705
|
-
|
|
744
|
+
return {
|
|
745
|
+
shouldFlatten: true,
|
|
746
|
+
flattenType: "parent-level-flatten"
|
|
747
|
+
};
|
|
706
748
|
}
|
|
707
749
|
}
|
|
708
750
|
```
|
|
@@ -770,7 +812,20 @@ node tests/debug-slothlet.mjs
|
|
|
770
812
|
|
|
771
813
|
## Source Code Locations
|
|
772
814
|
|
|
773
|
-
|
|
815
|
+
**Note**: The slothlet API generation logic has been refactored into a modular structure:
|
|
816
|
+
|
|
817
|
+
- **`src/lib/helpers/api_builder/decisions.mjs`** - Core decision logic (899 lines)
|
|
818
|
+
- `getFlatteningDecision()` [L87-189] - Controls flattening behavior
|
|
819
|
+
- `processModuleForAPI()` [L315-466] - Module processing logic
|
|
820
|
+
- `buildCategoryDecisions()` [L505-899] - Directory structure decisions
|
|
821
|
+
|
|
822
|
+
- **`src/lib/helpers/api_builder/construction.mjs`** - API assembly (555 lines)
|
|
823
|
+
- `buildCategoryStructure()` [L125-555] - Structural construction
|
|
824
|
+
|
|
825
|
+
- **`src/lib/helpers/multidefault.mjs`** - Multi-default handling (262 lines)
|
|
826
|
+
- `multidefault_getFlatteningDecision()` [L178-262] - Multi-default flattening logic
|
|
827
|
+
|
|
828
|
+
**For complete documentation** of all 32 conditional statements with exact line numbers, see [API-RULES-CONDITIONS.md](API-RULES-CONDITIONS.md).
|
|
774
829
|
|
|
775
830
|
## Test File Index
|
|
776
831
|
|
package/package.json
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cldmv/slothlet",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.0",
|
|
4
4
|
"moduleVersions": {
|
|
5
5
|
"lazy": "1.3.1",
|
|
6
|
-
"eager": "1.3.1"
|
|
6
|
+
"eager": "1.3.1",
|
|
7
|
+
"async": "1.1.0",
|
|
8
|
+
"live": "1.1.0"
|
|
7
9
|
},
|
|
8
10
|
"description": "Slothlet: Modular API Loader for Node.js. Lazy mode dynamically loads API modules and submodules only when accessed, supporting both lazy and eager loading.",
|
|
9
11
|
"main": "./index.cjs",
|
|
@@ -92,12 +94,12 @@
|
|
|
92
94
|
"build:dist": "shx mkdir -p dist && shx cp -r src/* dist/ && shx rm -rf dist/**/*.backup",
|
|
93
95
|
"build:types": "npx tsc -p .configs/tsconfig.dts.jsonc",
|
|
94
96
|
"build:docs": "npm run build:docs:slothlet && npm run build:docs:api-tests",
|
|
95
|
-
"build:docs:slothlet": "jsdoc2md -g grouped -m dl --no-cache --separators -c \".configs/jsdoc.config.json\" --helper \"docs/helpers.cjs\" --template \"docs/template.hbs\" \"src/{slothlet.mjs,lib/{modes,helpers,runtime}/**/*.mjs}\" > docs/API.md",
|
|
97
|
+
"build:docs:slothlet": "jsdoc2md -g grouped -m dl --no-cache --separators -c \".configs/jsdoc.config.json\" --helper \"docs/generated/helpers.cjs\" --template \"docs/generated/template.hbs\" \"src/{slothlet.mjs,lib/{modes,helpers,runtime}/**/*.mjs}\" > docs/generated/API.md",
|
|
96
98
|
"build:docs:api-tests": "npm run build:docs:api-test && npm run build:docs:api-test-cjs && npm run build:docs:api-test-mixed",
|
|
97
|
-
"build:docs:api-test": "shx mkdir -p docs/api_tests && jsdoc2md -g grouped -m dl --no-cache --separators -c \".configs/jsdoc.config.json\" --helper \"docs/helpers.cjs\" --template \"docs/template.hbs\" \"api_tests/api_test/**/*.mjs\" > docs/api_tests/api_test.md",
|
|
98
|
-
"build:docs:api-test-cjs": "shx mkdir -p docs/api_tests && jsdoc2md -g grouped -m dl --no-cache --separators -c \".configs/jsdoc.config.json\" --helper \"docs/helpers.cjs\" --template \"docs/template.hbs\" \"api_tests/api_test_cjs/**/*.{mjs,cjs}\" > docs/api_tests/api_test_cjs.md",
|
|
99
|
-
"build:docs:api-test-mixed": "shx mkdir -p docs/api_tests && jsdoc2md -g grouped -m dl --no-cache --separators -c \".configs/jsdoc.config.json\" --helper \"docs/helpers.cjs\" --template \"docs/template.hbs\" \"api_tests/api_test_mixed/**/*.{mjs,cjs}\" > docs/api_tests/api_test_mixed.md",
|
|
100
|
-
"build:docs:tools": "shx mkdir -p docs/tools && jsdoc2md -g grouped -m dl --no-cache --separators -c \".configs/jsdoc.config.json\" --helper \"docs/helpers.cjs\" --template \"docs/template.hbs\" \"tools/**/*.mjs\" > docs/tools/build-tools.md",
|
|
99
|
+
"build:docs:api-test": "shx mkdir -p docs/generated/api_tests && jsdoc2md -g grouped -m dl --no-cache --separators -c \".configs/jsdoc.config.json\" --helper \"docs/generated/helpers.cjs\" --template \"docs/generated/template.hbs\" \"api_tests/api_test/**/*.mjs\" > docs/generated/api_tests/api_test.md",
|
|
100
|
+
"build:docs:api-test-cjs": "shx mkdir -p docs/generated/api_tests && jsdoc2md -g grouped -m dl --no-cache --separators -c \".configs/jsdoc.config.json\" --helper \"docs/generated/helpers.cjs\" --template \"docs/generated/template.hbs\" \"api_tests/api_test_cjs/**/*.{mjs,cjs}\" > docs/generated/api_tests/api_test_cjs.md",
|
|
101
|
+
"build:docs:api-test-mixed": "shx mkdir -p docs/generated/api_tests && jsdoc2md -g grouped -m dl --no-cache --separators -c \".configs/jsdoc.config.json\" --helper \"docs/generated/helpers.cjs\" --template \"docs/generated/template.hbs\" \"api_tests/api_test_mixed/**/*.{mjs,cjs}\" > docs/generated/api_tests/api_test_mixed.md",
|
|
102
|
+
"build:docs:tools": "shx mkdir -p docs/tools && jsdoc2md -g grouped -m dl --no-cache --separators -c \".configs/jsdoc.config.json\" --helper \"docs/generated/helpers.cjs\" --template \"docs/generated/template.hbs\" \"tools/**/*.mjs\" > docs/tools/build-tools.md",
|
|
101
103
|
"build:exports": "node tools/build-exports.mjs",
|
|
102
104
|
"build:prepend-license": "node tools/prepend-license.mjs dist",
|
|
103
105
|
"prepublish-check": "npm run build && npm pack && node tools/prepublish-check.mjs"
|
|
@@ -188,8 +190,8 @@
|
|
|
188
190
|
"index.cjs",
|
|
189
191
|
"README.md",
|
|
190
192
|
"LICENSE",
|
|
191
|
-
"API-RULES.md",
|
|
192
|
-
"API-RULES-CONDITIONS.md",
|
|
193
|
+
"docs/API-RULES.md",
|
|
194
|
+
"docs/API-RULES-CONDITIONS.md",
|
|
193
195
|
"AGENT-USAGE.md",
|
|
194
196
|
"types/dist/",
|
|
195
197
|
"types/index.d.mts",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"als-eventemitter.d.mts","sourceRoot":"","sources":["../../../../dist/lib/helpers/als-eventemitter.mjs"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"als-eventemitter.d.mts","sourceRoot":"","sources":["../../../../dist/lib/helpers/als-eventemitter.mjs"],"names":[],"mappings":"AAiDA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,8EAiNC;AAED;;;;;;;;;;;;;;GAcG;AACH;;;;;;;;;;;;;;;GAeG;AACH,oDAyBC;AAED,mDAmCC;kCArVgD,kBAAkB"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamically adds API modules from a new folder to the existing API at a specified path.
|
|
3
|
+
*
|
|
4
|
+
* @function addApiFromFolder
|
|
5
|
+
* @memberof module:@cldmv/slothlet.lib.helpers.api_builder.add_api
|
|
6
|
+
* @param {object} options - Configuration object
|
|
7
|
+
* @param {string} options.apiPath - Dot-notation path where modules will be added
|
|
8
|
+
* @param {string} options.folderPath - Path to folder containing modules to load
|
|
9
|
+
* @param {object} options.instance - Slothlet instance with api, boundapi, config, modes, etc.
|
|
10
|
+
* @param {object} [options.metadata={}] - Metadata to attach to all loaded functions
|
|
11
|
+
* @returns {Promise<void>}
|
|
12
|
+
* @throws {Error} If API not loaded, invalid parameters, folder does not exist, or merge conflicts
|
|
13
|
+
* @package
|
|
14
|
+
*
|
|
15
|
+
* @description
|
|
16
|
+
* This function enables runtime extension of the API by loading modules from a folder
|
|
17
|
+
* and merging them into a specified location in the API tree. It performs comprehensive
|
|
18
|
+
* validation, supports both relative and absolute paths, handles intermediate object
|
|
19
|
+
* creation, and respects the allowApiOverwrite configuration.
|
|
20
|
+
*
|
|
21
|
+
* The method performs the following steps:
|
|
22
|
+
* 1. Validates that the API is loaded and the folder exists
|
|
23
|
+
* 2. Resolves relative folder paths from the caller location
|
|
24
|
+
* 3. Loads modules from the specified folder using the current loading mode
|
|
25
|
+
* 4. Navigates to the specified API path, creating intermediate objects as needed
|
|
26
|
+
* 5. Merges the new modules into the target location
|
|
27
|
+
* 6. Updates all live bindings to reflect the changes
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* // Internal usage
|
|
31
|
+
* import { addApiFromFolder } from "./add_api.mjs";
|
|
32
|
+
*
|
|
33
|
+
* // Add additional modules at runtime.plugins path
|
|
34
|
+
* await addApiFromFolder({
|
|
35
|
+
* apiPath: "runtime.plugins",
|
|
36
|
+
* folderPath: "./plugins",
|
|
37
|
+
* instance: slothletInstance
|
|
38
|
+
* });
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* // Add modules to root level
|
|
42
|
+
* await addApiFromFolder({
|
|
43
|
+
* apiPath: "utilities",
|
|
44
|
+
* folderPath: "./utils",
|
|
45
|
+
* instance: slothletInstance
|
|
46
|
+
* });
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* // Add deep nested modules
|
|
50
|
+
* await addApiFromFolder({
|
|
51
|
+
* apiPath: "services.external.stripe",
|
|
52
|
+
* folderPath: "./services/stripe",
|
|
53
|
+
* instance: slothletInstance
|
|
54
|
+
* });
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* // Add modules with metadata
|
|
58
|
+
* await addApiFromFolder({
|
|
59
|
+
* apiPath: "plugins",
|
|
60
|
+
* folderPath: "./untrusted-plugins",
|
|
61
|
+
* instance: slothletInstance,
|
|
62
|
+
* metadata: {
|
|
63
|
+
* trusted: false,
|
|
64
|
+
* permissions: ["read"],
|
|
65
|
+
* version: "1.0.0",
|
|
66
|
+
* author: "external"
|
|
67
|
+
* }
|
|
68
|
+
* });
|
|
69
|
+
*/
|
|
70
|
+
export function addApiFromFolder({ apiPath, folderPath, instance, metadata }: {
|
|
71
|
+
apiPath: string;
|
|
72
|
+
folderPath: string;
|
|
73
|
+
instance: object;
|
|
74
|
+
metadata?: object;
|
|
75
|
+
}): Promise<void>;
|
|
76
|
+
//# sourceMappingURL=add_api.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add_api.d.mts","sourceRoot":"","sources":["../../../../../dist/lib/helpers/api_builder/add_api.mjs"],"names":[],"mappings":"AAkDA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;AACH,8EA/DG;IAAwB,OAAO,EAAvB,MAAM;IACU,UAAU,EAA1B,MAAM;IACU,QAAQ,EAAxB,MAAM;IACW,QAAQ,GAAzB,MAAM;CACd,GAAU,OAAO,CAAC,IAAI,CAAC,CAoUzB"}
|