@cldmv/slothlet 2.10.0 → 3.0.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 +365 -294
- package/README.md +556 -233
- package/dist/lib/builders/api-assignment.mjs +605 -0
- package/dist/lib/builders/api_builder.mjs +1073 -0
- package/dist/lib/builders/builder.mjs +94 -0
- package/dist/lib/builders/modes-processor.mjs +1816 -0
- package/dist/lib/errors.mjs +227 -0
- package/dist/lib/factories/component-base.mjs +96 -0
- package/dist/lib/factories/context.mjs +38 -0
- package/dist/lib/handlers/api-cache-manager.mjs +216 -0
- package/dist/lib/handlers/api-manager.mjs +2364 -0
- package/dist/lib/handlers/context-async.mjs +184 -0
- package/dist/lib/handlers/context-live.mjs +184 -0
- package/dist/lib/handlers/hook-manager.mjs +789 -0
- package/dist/lib/handlers/lifecycle-token.mjs +44 -0
- package/dist/lib/handlers/lifecycle.mjs +131 -0
- package/dist/lib/handlers/materialize-manager.mjs +64 -0
- package/dist/lib/handlers/metadata.mjs +500 -0
- package/dist/lib/handlers/ownership.mjs +338 -0
- package/dist/lib/handlers/unified-wrapper.mjs +3031 -0
- package/dist/lib/helpers/class-instance-wrapper.mjs +125 -0
- package/dist/lib/helpers/config.mjs +343 -0
- package/dist/lib/helpers/eventemitter-context.mjs +365 -0
- package/dist/lib/helpers/hint-detector.mjs +63 -0
- package/dist/lib/helpers/modes-utils.mjs +53 -0
- package/dist/lib/helpers/resolve-from-caller.mjs +125 -85
- package/dist/lib/helpers/sanitize.mjs +247 -168
- package/dist/lib/helpers/utilities.mjs +46 -81
- package/dist/lib/i18n/languages/de-de.json +377 -0
- package/dist/lib/i18n/languages/en-gb.json +377 -0
- package/dist/lib/i18n/languages/en-us.json +377 -0
- package/dist/lib/i18n/languages/es-mx.json +377 -0
- package/dist/lib/i18n/languages/fr-fr.json +377 -0
- package/dist/lib/i18n/languages/hi-in.json +377 -0
- package/dist/lib/i18n/languages/ja-jp.json +377 -0
- package/dist/lib/i18n/languages/ko-kr.json +377 -0
- package/dist/lib/i18n/languages/pt-br.json +377 -0
- package/dist/lib/i18n/languages/ru-ru.json +377 -0
- package/dist/lib/i18n/languages/zh-cn.json +377 -0
- package/dist/lib/i18n/translations.mjs +140 -0
- package/dist/lib/modes/eager.mjs +75 -0
- package/dist/lib/modes/lazy.mjs +97 -0
- package/dist/lib/processors/flatten.mjs +453 -0
- package/dist/lib/processors/loader.mjs +355 -0
- package/dist/lib/processors/type-generator.mjs +291 -0
- package/dist/lib/processors/typescript.mjs +188 -0
- package/dist/lib/runtime/runtime-asynclocalstorage.mjs +80 -522
- package/dist/lib/runtime/runtime-livebindings.mjs +45 -390
- package/dist/lib/runtime/runtime.mjs +39 -159
- package/dist/slothlet.mjs +529 -700
- package/docs/API-RULES.md +497 -617
- package/index.cjs +4 -4
- package/index.mjs +82 -45
- package/package.json +139 -26
- package/types/dist/lib/builders/api-assignment.d.mts +97 -0
- package/types/dist/lib/builders/api-assignment.d.mts.map +1 -0
- package/types/dist/lib/builders/api_builder.d.mts +96 -0
- package/types/dist/lib/builders/api_builder.d.mts.map +1 -0
- package/types/dist/lib/builders/builder.d.mts +60 -0
- package/types/dist/lib/builders/builder.d.mts.map +1 -0
- package/types/dist/lib/builders/modes-processor.d.mts +32 -0
- package/types/dist/lib/builders/modes-processor.d.mts.map +1 -0
- package/types/dist/lib/errors.d.mts +118 -0
- package/types/dist/lib/errors.d.mts.map +1 -0
- package/types/dist/lib/factories/component-base.d.mts +182 -0
- package/types/dist/lib/factories/component-base.d.mts.map +1 -0
- package/types/dist/lib/factories/context.d.mts +26 -0
- package/types/dist/lib/factories/context.d.mts.map +1 -0
- package/types/dist/lib/handlers/api-cache-manager.d.mts +208 -0
- package/types/dist/lib/handlers/api-cache-manager.d.mts.map +1 -0
- package/types/dist/lib/handlers/api-manager.d.mts +392 -0
- package/types/dist/lib/handlers/api-manager.d.mts.map +1 -0
- package/types/dist/lib/handlers/context-async.d.mts +66 -0
- package/types/dist/lib/handlers/context-async.d.mts.map +1 -0
- package/types/dist/lib/handlers/context-live.d.mts +65 -0
- package/types/dist/lib/handlers/context-live.d.mts.map +1 -0
- package/types/dist/lib/handlers/hook-manager.d.mts +199 -0
- package/types/dist/lib/handlers/hook-manager.d.mts.map +1 -0
- package/types/dist/lib/handlers/lifecycle-token.d.mts +49 -0
- package/types/dist/lib/handlers/lifecycle-token.d.mts.map +1 -0
- package/types/dist/lib/handlers/lifecycle.d.mts +90 -0
- package/types/dist/lib/handlers/lifecycle.d.mts.map +1 -0
- package/types/dist/lib/handlers/materialize-manager.d.mts +75 -0
- package/types/dist/lib/handlers/materialize-manager.d.mts.map +1 -0
- package/types/dist/lib/handlers/metadata.d.mts +215 -0
- package/types/dist/lib/handlers/metadata.d.mts.map +1 -0
- package/types/dist/lib/handlers/ownership.d.mts +170 -0
- package/types/dist/lib/handlers/ownership.d.mts.map +1 -0
- package/types/dist/lib/handlers/unified-wrapper.d.mts +250 -0
- package/types/dist/lib/handlers/unified-wrapper.d.mts.map +1 -0
- package/types/dist/lib/helpers/class-instance-wrapper.d.mts +54 -0
- package/types/dist/lib/helpers/class-instance-wrapper.d.mts.map +1 -0
- package/types/dist/lib/helpers/config.d.mts +96 -0
- package/types/dist/lib/helpers/config.d.mts.map +1 -0
- package/types/dist/lib/helpers/eventemitter-context.d.mts +31 -0
- package/types/dist/lib/helpers/eventemitter-context.d.mts.map +1 -0
- package/types/dist/lib/helpers/hint-detector.d.mts +20 -0
- package/types/dist/lib/helpers/hint-detector.d.mts.map +1 -0
- package/types/dist/lib/helpers/modes-utils.d.mts +35 -0
- package/types/dist/lib/helpers/modes-utils.d.mts.map +1 -0
- package/types/dist/lib/helpers/resolve-from-caller.d.mts +29 -145
- package/types/dist/lib/helpers/resolve-from-caller.d.mts.map +1 -1
- package/types/dist/lib/helpers/sanitize.d.mts +95 -94
- package/types/dist/lib/helpers/sanitize.d.mts.map +1 -1
- package/types/dist/lib/helpers/utilities.d.mts +53 -116
- package/types/dist/lib/helpers/utilities.d.mts.map +1 -1
- package/types/dist/lib/i18n/translations.d.mts +39 -0
- package/types/dist/lib/i18n/translations.d.mts.map +1 -0
- package/types/dist/lib/modes/eager.d.mts +36 -0
- package/types/dist/lib/modes/eager.d.mts.map +1 -0
- package/types/dist/lib/modes/lazy.d.mts +49 -0
- package/types/dist/lib/modes/lazy.d.mts.map +1 -0
- package/types/dist/lib/processors/flatten.d.mts +114 -0
- package/types/dist/lib/processors/flatten.d.mts.map +1 -0
- package/types/dist/lib/processors/loader.d.mts +47 -0
- package/types/dist/lib/processors/loader.d.mts.map +1 -0
- package/types/dist/lib/processors/type-generator.d.mts +19 -0
- package/types/dist/lib/processors/type-generator.d.mts.map +1 -0
- package/types/dist/lib/processors/typescript.d.mts +55 -0
- package/types/dist/lib/processors/typescript.d.mts.map +1 -0
- package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts +47 -42
- package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts.map +1 -1
- package/types/dist/lib/runtime/runtime-livebindings.d.mts +34 -65
- package/types/dist/lib/runtime/runtime-livebindings.d.mts.map +1 -1
- package/types/dist/lib/runtime/runtime.d.mts +39 -9
- package/types/dist/lib/runtime/runtime.d.mts.map +1 -1
- package/types/dist/slothlet.d.mts +186 -105
- package/types/dist/slothlet.d.mts.map +1 -1
- package/types/index.d.mts +1 -3
- package/dist/lib/engine/README.md +0 -21
- package/dist/lib/engine/slothlet_child.mjs +0 -59
- package/dist/lib/engine/slothlet_engine.mjs +0 -372
- package/dist/lib/engine/slothlet_esm.mjs +0 -230
- package/dist/lib/engine/slothlet_helpers.mjs +0 -455
- package/dist/lib/engine/slothlet_worker.mjs +0 -149
- package/dist/lib/helpers/als-eventemitter.mjs +0 -256
- package/dist/lib/helpers/api_builder/add_api.mjs +0 -292
- package/dist/lib/helpers/api_builder/analysis.mjs +0 -532
- package/dist/lib/helpers/api_builder/construction.mjs +0 -457
- package/dist/lib/helpers/api_builder/decisions.mjs +0 -737
- package/dist/lib/helpers/api_builder/metadata.mjs +0 -248
- package/dist/lib/helpers/api_builder.mjs +0 -41
- package/dist/lib/helpers/auto-wrap.mjs +0 -62
- package/dist/lib/helpers/hooks.mjs +0 -389
- package/dist/lib/helpers/instance-manager.mjs +0 -111
- package/dist/lib/helpers/metadata-api.mjs +0 -201
- package/dist/lib/helpers/multidefault.mjs +0 -216
- package/dist/lib/modes/slothlet_eager.mjs +0 -126
- package/dist/lib/modes/slothlet_lazy.mjs +0 -513
- package/docs/API-RULES-CONDITIONS.md +0 -508
- package/types/dist/lib/engine/slothlet_child.d.mts +0 -2
- package/types/dist/lib/engine/slothlet_child.d.mts.map +0 -1
- package/types/dist/lib/engine/slothlet_engine.d.mts +0 -31
- package/types/dist/lib/engine/slothlet_engine.d.mts.map +0 -1
- package/types/dist/lib/engine/slothlet_esm.d.mts +0 -19
- package/types/dist/lib/engine/slothlet_esm.d.mts.map +0 -1
- package/types/dist/lib/engine/slothlet_helpers.d.mts +0 -25
- package/types/dist/lib/engine/slothlet_helpers.d.mts.map +0 -1
- package/types/dist/lib/engine/slothlet_worker.d.mts +0 -2
- package/types/dist/lib/engine/slothlet_worker.d.mts.map +0 -1
- package/types/dist/lib/helpers/als-eventemitter.d.mts +0 -56
- package/types/dist/lib/helpers/als-eventemitter.d.mts.map +0 -1
- package/types/dist/lib/helpers/api_builder/add_api.d.mts +0 -76
- package/types/dist/lib/helpers/api_builder/add_api.d.mts.map +0 -1
- package/types/dist/lib/helpers/api_builder/analysis.d.mts +0 -189
- package/types/dist/lib/helpers/api_builder/analysis.d.mts.map +0 -1
- package/types/dist/lib/helpers/api_builder/construction.d.mts +0 -107
- package/types/dist/lib/helpers/api_builder/construction.d.mts.map +0 -1
- package/types/dist/lib/helpers/api_builder/decisions.d.mts +0 -213
- package/types/dist/lib/helpers/api_builder/decisions.d.mts.map +0 -1
- package/types/dist/lib/helpers/api_builder/metadata.d.mts +0 -99
- package/types/dist/lib/helpers/api_builder/metadata.d.mts.map +0 -1
- package/types/dist/lib/helpers/api_builder.d.mts +0 -6
- package/types/dist/lib/helpers/api_builder.d.mts.map +0 -1
- package/types/dist/lib/helpers/auto-wrap.d.mts +0 -49
- package/types/dist/lib/helpers/auto-wrap.d.mts.map +0 -1
- package/types/dist/lib/helpers/hooks.d.mts +0 -342
- package/types/dist/lib/helpers/hooks.d.mts.map +0 -1
- package/types/dist/lib/helpers/instance-manager.d.mts +0 -41
- package/types/dist/lib/helpers/instance-manager.d.mts.map +0 -1
- package/types/dist/lib/helpers/metadata-api.d.mts +0 -132
- package/types/dist/lib/helpers/metadata-api.d.mts.map +0 -1
- package/types/dist/lib/helpers/multidefault.d.mts +0 -90
- package/types/dist/lib/helpers/multidefault.d.mts.map +0 -1
- package/types/dist/lib/modes/slothlet_eager.d.mts +0 -65
- package/types/dist/lib/modes/slothlet_eager.d.mts.map +0 -1
- package/types/dist/lib/modes/slothlet_lazy.d.mts +0 -31
- package/types/dist/lib/modes/slothlet_lazy.d.mts.map +0 -1
package/docs/API-RULES.md
CHANGED
|
@@ -1,88 +1,120 @@
|
|
|
1
|
-
# Slothlet API Rules
|
|
1
|
+
# Slothlet API Rules
|
|
2
2
|
|
|
3
|
-
|
|
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.
|
|
3
|
+
**Complete Guide to All API Generation Behaviors**
|
|
7
4
|
|
|
8
|
-
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Document Hierarchy
|
|
8
|
+
|
|
9
|
+
This is the **middle layer** of slothlet's three-tier documentation system:
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
```text
|
|
12
|
+
📋 API-RULES/API-FLATTENING.md (F##) ← User Guide: Clear examples and flowcharts
|
|
13
|
+
↑ links to ↓ links to
|
|
14
|
+
📊 API-RULES.md (1-13) ← YOU ARE HERE: Complete behavior catalog
|
|
15
|
+
↑ links to ↓ links to
|
|
16
|
+
🔧 API-RULES/API-RULES-CONDITIONS.md ← Technical: Exact source code locations
|
|
17
|
+
↓ mapped in
|
|
18
|
+
🗺️ API-RULES/API-RULE-MAPPING.md ← Traceability Matrix: Rule # ↔ F## ↔ C##
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Cross-Reference Navigation:**
|
|
11
22
|
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **Test File Sources**: Specific test files demonstrating the behavior
|
|
23
|
+
- **For Users**: See [API-RULES/API-FLATTENING.md](API-RULES/API-FLATTENING.md) for user-friendly explanations with examples
|
|
24
|
+
- **For Developers**: See [API-RULES/API-RULES-CONDITIONS.md](API-RULES/API-RULES-CONDITIONS.md) for exact source code locations
|
|
25
|
+
- **Rule Mapping**: See [API-RULES/API-RULE-MAPPING.md](API-RULES/API-RULE-MAPPING.md) for complete Rule # ↔ F## ↔ C## traceability matrix
|
|
16
26
|
|
|
17
27
|
---
|
|
18
28
|
|
|
19
|
-
##
|
|
29
|
+
## Overview
|
|
30
|
+
|
|
31
|
+
This document catalogs **all 13 API generation behaviors** in slothlet with:
|
|
32
|
+
|
|
33
|
+
- Verified examples from actual test files with source attribution
|
|
34
|
+
- Cross-references to user guide (F##) and technical details (C##)
|
|
35
|
+
- Source code locations with function names and file references
|
|
36
|
+
- Test file sources demonstrating each behavior in action
|
|
37
|
+
- Processing contexts (Root/Subfolder/Multi-Default/AddApi)
|
|
38
|
+
|
|
39
|
+
**Why 13 Rules vs Flattening Patterns?**
|
|
40
|
+
|
|
41
|
+
The [Flattening guide](API-RULES/API-FLATTENING.md) focuses on **when content gets promoted/flattened**. This comprehensive guide covers **all API behaviors** including cases where flattening doesn't occur but specific handling is still needed:
|
|
42
|
+
|
|
43
|
+
- **Flattening Rules** (1, 7, 8, 10, 11, 13): Core flattening patterns
|
|
44
|
+
- **Non-Flattening Rules** (2, 3, 4, 5, 6, 9): Export collection, function naming, empty modules, mixed exports
|
|
45
|
+
- **AddApi Rules** (11, 12, 13): Runtime API extension behaviors
|
|
46
|
+
|
|
47
|
+
**Methodology**: Each rule has been systematically verified against test files and source code.
|
|
48
|
+
|
|
49
|
+
---
|
|
20
50
|
|
|
21
|
-
|
|
22
|
-
- [x] Rule 2: Named-Only Export Collection ✅ **VERIFIED** (api_tests/api_test) - Re-verified
|
|
23
|
-
- [x] Rule 3: Empty Module Handling ✅ **VERIFIED** (debug testing)
|
|
24
|
-
- [x] Rule 4: Default Export Container Pattern ✅ **VERIFIED** (api_tests/api_test + api_tests/api_tv_test) - Re-verified
|
|
25
|
-
- [x] Rule 5: Multi-Default Export Mixed Pattern ✅ **VERIFIED** (api_tests/api_tv_test) - Re-verified
|
|
26
|
-
- [x] Rule 6: Self-Referential Export Protection ✅ **VERIFIED** (api_tests/api_test) - Tested
|
|
27
|
-
- [x] Rule 7: Auto-Flattening Single Named Export ✅ **VERIFIED** (api_tests/api_test) - Tested
|
|
28
|
-
- [x] Rule 8: Single-File Auto-Flattening Patterns ✅ **FULLY VERIFIED** (All 4 patterns A, B, C, D verified with real test files)
|
|
29
|
-
- [x] Rule 9: Function Name Preference Over Sanitization ✅ **FULLY VERIFIED** (Multiple examples verified: autoIP, parseJSON, getHTTPStatus, XMLParser)
|
|
30
|
-
- [x] Rule 10: Generic Filename Parent-Level Promotion ✅ **VERIFIED** (nest4/singlefile.mjs example verified with api_tests/api_test)
|
|
51
|
+
## Rule Categories
|
|
31
52
|
|
|
32
|
-
|
|
53
|
+
| Category | Rules | Focus | Cross-References |
|
|
54
|
+
| --------------------- | ------------ | --------------------------- | ---------------- |
|
|
55
|
+
| **Basic Flattening** | 1, 7, 8 | Core flattening patterns | [F01-F05](API-RULES/API-FLATTENING.md) → [C01-C11](API-RULES/API-RULES-CONDITIONS.md) |
|
|
56
|
+
| **Export Handling** | 2, 4, 5 | Default vs Named exports | [F04-F05](API-RULES/API-FLATTENING.md) → [C08-C21](API-RULES/API-RULES-CONDITIONS.md) |
|
|
57
|
+
| **Special Cases** | 3, 6, 9, 10 | Edge cases and protections | [C10, C01, C16-C19](API-RULES/API-RULES-CONDITIONS.md) |
|
|
58
|
+
| **AddApi Extensions** | 11, 12, 13 | Runtime API extensions | [F06-F08](API-RULES/API-FLATTENING.md) → [C33, C34](API-RULES/API-RULES-CONDITIONS.md) |
|
|
33
59
|
|
|
34
60
|
---
|
|
35
61
|
|
|
36
|
-
##
|
|
62
|
+
## Table of Contents
|
|
63
|
+
|
|
64
|
+
1. [Rule 1: Filename Matches Container Flattening](#rule-1-filename-matches-container-flattening)
|
|
65
|
+
2. [Rule 2: Named-Only Export Collection](#rule-2-named-only-export-collection)
|
|
66
|
+
3. [Rule 3: Empty Module Handling](#rule-3-empty-module-handling)
|
|
67
|
+
4. [Rule 4: Named Export with Function Name Preservation](#rule-4-named-export-with-function-name-preservation)
|
|
68
|
+
5. [Rule 5: Multiple Module Default Export Handling](#rule-5-multiple-module-default-export-handling)
|
|
69
|
+
6. [Rule 6: Multiple Module Mixed Exports](#rule-6-multiple-module-mixed-exports)
|
|
70
|
+
7. [Rule 7: Single Module Named Export Flattening](#rule-7-single-module-named-export-flattening)
|
|
71
|
+
8. [Rule 8: Single Module Default Export Promotion](#rule-8-single-module-default-export-promotion)
|
|
72
|
+
9. [Rule 9: Function Name Preference Over Sanitization](#rule-9-function-name-preference-over-sanitization)
|
|
73
|
+
10. [Rule 10: Generic Filename Parent-Level Promotion](#rule-10-generic-filename-parent-level-promotion)
|
|
74
|
+
11. [Rule 11: AddApi Special File Pattern](#rule-11-addapi-special-file-pattern)
|
|
75
|
+
12. [Rule 12: Module Ownership and Selective API Overwriting](#rule-12-module-ownership-and-selective-api-overwriting)
|
|
76
|
+
13. [Rule 13: AddApi Path Deduplication Flattening](#rule-13-addapi-path-deduplication-flattening)
|
|
77
|
+
14. [Verification Status](#verification-status)
|
|
78
|
+
15. [Cross-Reference Index](#cross-reference-index)
|
|
79
|
+
|
|
80
|
+
---
|
|
37
81
|
|
|
38
|
-
|
|
82
|
+
## Rule 1: Filename Matches Container Flattening
|
|
39
83
|
|
|
40
|
-
**
|
|
84
|
+
**Category**: Basic Flattening
|
|
85
|
+
**Status**: ✅ Verified (`api_tests/api_test`)
|
|
86
|
+
**User Guide**: [F01](API-RULES/API-FLATTENING.md#f01-folder-file-name-matching)
|
|
87
|
+
**Technical**: [C05, C09b](API-RULES/API-RULES-CONDITIONS.md#c05)
|
|
41
88
|
|
|
42
|
-
**Condition**: Filename matches folder name AND no default export AND has named exports
|
|
43
|
-
**Source
|
|
44
|
-
**
|
|
89
|
+
**Condition**: Filename matches folder name AND no default export AND has named exports
|
|
90
|
+
**Source Files**: `api_tests/api_test/math/math.mjs`
|
|
91
|
+
**Implementation**: `buildCategoryDecisions()` → `getFlatteningDecision()` → `processModuleForAPI()`
|
|
45
92
|
|
|
46
93
|
**Verified Examples**:
|
|
47
94
|
|
|
48
95
|
```javascript
|
|
49
|
-
//
|
|
50
|
-
export
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
api.math.add(2, 3); // → 5 (not api.math.math.add)
|
|
57
|
-
api.math.multiply(2, 3); // → 6 (not api.math.math.multiply)
|
|
58
|
-
|
|
59
|
-
// Example B: api_tests/api_test/string/string.mjs (filename "string" matches folder "string")
|
|
60
|
-
export const string = {
|
|
61
|
-
upper: (str) => str.toUpperCase(),
|
|
62
|
-
reverse: (str) => str.split("").reverse().join("")
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
// Result: Also flattened to container level (string.string → string)
|
|
66
|
-
api.string.upper("hello"); // → "HELLO" (not api.string.string.upper)
|
|
67
|
-
api.string.reverse("hello"); // → "olleh" (not api.string.string.reverse)
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
**Test Verification**:
|
|
96
|
+
// File: api_tests/api_test/math/math.mjs
|
|
97
|
+
export function add(a, b) {
|
|
98
|
+
return a + b;
|
|
99
|
+
}
|
|
100
|
+
export function subtract(a, b) {
|
|
101
|
+
return a - b;
|
|
102
|
+
}
|
|
71
103
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
104
|
+
// Without Rule 1: api.math.math.add(2, 3) ❌ (redundant nesting)
|
|
105
|
+
// With Rule 1: api.math.add(2, 3) ✅ (clean flattening)
|
|
106
|
+
api.math.add(2, 3); // 5
|
|
107
|
+
api.math.subtract(5, 2); // 3
|
|
76
108
|
```
|
|
77
109
|
|
|
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
|
|
81
110
|
**Technical Implementation**:
|
|
82
111
|
|
|
112
|
+
- **Primary Condition**: [C05](API-RULES/API-RULES-CONDITIONS.md#c05) - `fileName === categoryName && !moduleHasDefault && moduleKeys.length > 0`
|
|
113
|
+
- **Processing**: [C09b](API-RULES/API-RULES-CONDITIONS.md#c09b) - `flattenToCategory: true` → category-level flattening
|
|
114
|
+
|
|
83
115
|
```javascript
|
|
84
116
|
// C05: Filename Matches Container (Category-Level Flatten)
|
|
85
|
-
// Location: src/lib/helpers/api_builder/decisions.mjs
|
|
117
|
+
// Location: src/lib/helpers/api_builder/decisions.mjs
|
|
86
118
|
if (categoryName && fileName === categoryName && !moduleHasDefault && moduleKeys.length > 0) {
|
|
87
119
|
return {
|
|
88
120
|
shouldFlatten: true,
|
|
@@ -95,738 +127,586 @@ if (categoryName && fileName === categoryName && !moduleHasDefault && moduleKeys
|
|
|
95
127
|
}
|
|
96
128
|
```
|
|
97
129
|
|
|
98
|
-
**Processing Path**: Subfolder processing via `getFlatteningDecision()` (currentDepth > 0)
|
|
130
|
+
**Processing Path**: Subfolder processing via `getFlatteningDecision()` (currentDepth > 0)
|
|
131
|
+
**Source Code Location**: `src/lib/helpers/api_builder/decisions.mjs` - `getFlatteningDecision()`
|
|
99
132
|
|
|
100
133
|
---
|
|
101
134
|
|
|
102
|
-
|
|
135
|
+
## Rule 2: Named-Only Export Collection
|
|
103
136
|
|
|
104
|
-
**
|
|
137
|
+
**Category**: Export Handling
|
|
138
|
+
**Status**: ✅ Verified (`api_tests/api_test`)
|
|
139
|
+
**User Guide**: [F04](API-RULES/API-FLATTENING.md#f04)
|
|
140
|
+
**Technical**: [C15, C09d](API-RULES/API-RULES-CONDITIONS.md#c15)
|
|
105
141
|
|
|
106
|
-
**Condition**:
|
|
107
|
-
**
|
|
108
|
-
**Actual Behavior**: Named export becomes object property accessible on API
|
|
142
|
+
**Condition**: Directory contains files with only named exports (no default exports)
|
|
143
|
+
**Behavior**: All named exports collected and made accessible at the appropriate namespace level
|
|
109
144
|
|
|
110
|
-
**Verified
|
|
145
|
+
**Verified Examples**:
|
|
111
146
|
|
|
112
147
|
```javascript
|
|
113
|
-
// File:
|
|
114
|
-
export const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
//
|
|
124
|
-
api.
|
|
125
|
-
api.config.username; // → "admin"
|
|
126
|
-
api.config.secure; // → true
|
|
148
|
+
// File: constants/values.mjs
|
|
149
|
+
export const PI = 3.14159;
|
|
150
|
+
export const E = 2.71828;
|
|
151
|
+
|
|
152
|
+
// File: constants/messages.mjs
|
|
153
|
+
export const SUCCESS = "Operation completed";
|
|
154
|
+
export const ERROR = "Operation failed";
|
|
155
|
+
|
|
156
|
+
api.constants.values.PI; // 3.14159
|
|
157
|
+
api.constants.values.E; // 2.71828
|
|
158
|
+
api.constants.messages.SUCCESS; // "Operation completed"
|
|
159
|
+
api.constants.messages.ERROR; // "Operation failed"
|
|
127
160
|
```
|
|
128
161
|
|
|
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
|
|
132
162
|
**Technical Implementation**:
|
|
133
163
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
// Traditional: preserve as namespace
|
|
138
|
-
apiAssignments[apiPathKey] = mod;
|
|
139
|
-
namespaced = true;
|
|
140
|
-
}
|
|
141
|
-
```
|
|
164
|
+
- **Detection**: [C15](API-RULES/API-RULES-CONDITIONS.md#c15) - `defaultExportCount === 0`
|
|
165
|
+
- **Processing**: [C09d](API-RULES/API-RULES-CONDITIONS.md#c09d) - Standard namespace preservation
|
|
166
|
+
- **Strategy**: `processingStrategy = "named-only"` → category-level collection
|
|
142
167
|
|
|
143
|
-
**
|
|
168
|
+
**Key Behavior**:
|
|
144
169
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
# Confirms named-only exports become accessible object properties
|
|
149
|
-
```
|
|
170
|
+
- Preserves all named export names and values
|
|
171
|
+
- Maintains clear namespace separation between files
|
|
172
|
+
- No flattening when multiple named exports exist (prevents naming conflicts)
|
|
150
173
|
|
|
174
|
+
**Source Code Location**: `src/lib/helpers/api_builder/decisions.mjs` - `processModuleForAPI()`
|
|
151
175
|
**Processing Path**: Both Root and Subfolder processing via `processModuleForAPI`
|
|
152
176
|
|
|
153
177
|
---
|
|
154
178
|
|
|
155
|
-
|
|
179
|
+
## Rule 3: Empty Module Handling
|
|
156
180
|
|
|
157
|
-
**
|
|
181
|
+
**Category**: Special Cases
|
|
182
|
+
**Status**: ✅ Verified
|
|
183
|
+
**Technical**: [C10](API-RULES/API-RULES-CONDITIONS.md#c10)
|
|
158
184
|
|
|
159
|
-
**Condition**:
|
|
160
|
-
**
|
|
161
|
-
**
|
|
162
|
-
**Processing Path**: All paths (detected in `analyzeDirectoryStructure`)
|
|
185
|
+
**Condition**: Directory contains no loadable module files
|
|
186
|
+
**Behavior**: Graceful handling - creates empty namespace
|
|
187
|
+
**Processing Path**: Early detection in `buildCategoryDecisions()`
|
|
163
188
|
|
|
164
|
-
**
|
|
189
|
+
**Mode Differences**:
|
|
165
190
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
// Condition: if (moduleFiles.length === 0) { processingStrategy = "empty"; }
|
|
191
|
+
- **EAGER**: Empty folder → `{}` object (not callable)
|
|
192
|
+
- **LAZY**: Empty folder → lazy proxy that resolves to `{}` when called
|
|
169
193
|
|
|
170
|
-
|
|
171
|
-
api.empty_folder; // → {} (empty object)
|
|
172
|
-
typeof api.empty_folder; // → "object"
|
|
173
|
-
JSON.stringify(api.empty_folder); // → "{}"
|
|
174
|
-
```
|
|
194
|
+
**Technical Implementation**:
|
|
175
195
|
|
|
176
|
-
**
|
|
196
|
+
- **Detection**: [C10](API-RULES/API-RULES-CONDITIONS.md#c10) - `moduleFiles.length === 0`
|
|
197
|
+
- **Strategy**: `processingStrategy = "empty"` → graceful empty handling
|
|
177
198
|
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
199
|
+
```javascript
|
|
200
|
+
// Detection in analyzeDirectoryStructure
|
|
201
|
+
if (moduleFiles.length === 0) {
|
|
202
|
+
processingStrategy = "empty";
|
|
203
|
+
}
|
|
182
204
|
```
|
|
183
205
|
|
|
184
|
-
**
|
|
185
|
-
|
|
186
|
-
- **EAGER**: Empty folder → `{}` object (not callable)
|
|
187
|
-
- **LAZY**: Empty folder → lazy function that resolves to `{}` when called
|
|
188
|
-
|
|
189
|
-
**Technical Details**:
|
|
190
|
-
|
|
191
|
-
- **Detection**: `analyzeDirectoryStructure` in `src/lib/helpers/api_builder/analysis.mjs` detects empty directories
|
|
192
|
-
- **Handling**: Empty `processedModules` and `subDirectories` arrays result in empty object
|
|
193
|
-
- **API Result**: Empty folder becomes empty object property on API
|
|
194
|
-
- **Implementation**: See `buildCategoryStructure()` in `src/lib/helpers/api_builder/construction.mjs`
|
|
206
|
+
**Source Code Location**: `src/lib/helpers/api_builder/analysis.mjs`
|
|
207
|
+
**Processing Path**: All paths (detected in `analyzeDirectoryStructure`)
|
|
195
208
|
|
|
196
209
|
---
|
|
197
210
|
|
|
198
|
-
|
|
211
|
+
## Rule 4: Named Export with Function Name Preservation
|
|
199
212
|
|
|
200
|
-
**
|
|
213
|
+
**Category**: Export Handling
|
|
214
|
+
**Status**: ✅ Verified (`api_tests/api_test`)
|
|
215
|
+
**User Guide**: [F04](API-RULES/API-FLATTENING.md#f04)
|
|
216
|
+
**Technical**: [C16, C23](API-RULES/API-RULES-CONDITIONS.md#c16)
|
|
201
217
|
|
|
202
|
-
**Condition**:
|
|
203
|
-
**Behavior**:
|
|
204
|
-
**
|
|
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`
|
|
218
|
+
**Condition**: Named export with a function name that differs from the sanitized filename
|
|
219
|
+
**Behavior**: Preserves the original function name rather than using the filename-derived name
|
|
220
|
+
**Priority**: Function names take precedence over filename-based naming
|
|
207
221
|
|
|
208
|
-
**
|
|
222
|
+
**Verified Examples**:
|
|
209
223
|
|
|
210
224
|
```javascript
|
|
211
|
-
// File:
|
|
212
|
-
export
|
|
213
|
-
|
|
214
|
-
}
|
|
215
|
-
export function rootFunctionShout(name) {
|
|
216
|
-
return `HELLO, ${name.toUpperCase()}!`;
|
|
217
|
-
}
|
|
218
|
-
export function rootFunctionWhisper(name) {
|
|
219
|
-
return `hello, ${name.toLowerCase()}.`;
|
|
220
|
-
}
|
|
225
|
+
// File: auto-ip.mjs
|
|
226
|
+
export function autoIP() { /* ... */ }
|
|
227
|
+
api.autoIP(); // ✅ Function name preserved (not api.autoIp)
|
|
221
228
|
|
|
222
|
-
//
|
|
223
|
-
|
|
224
|
-
api.
|
|
225
|
-
api.rootFunctionWhisper("World"); // → "hello, world." (named export)
|
|
229
|
+
// File: json-parser.mjs
|
|
230
|
+
export function parseJSON(data) { /* ... */ }
|
|
231
|
+
api.parseJSON(data); // ✅ Original casing preserved (not api.jsonParser)
|
|
226
232
|
```
|
|
227
233
|
|
|
228
|
-
**
|
|
234
|
+
**Function Name Priority**:
|
|
229
235
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
export function processInboundData(data, meta = {}) {
|
|
233
|
-
return { processed: true, data: data, meta: meta };
|
|
234
|
-
}
|
|
236
|
+
1. Original function name (if available)
|
|
237
|
+
2. Filename-based sanitization (if no function name)
|
|
235
238
|
|
|
236
|
-
|
|
237
|
-
processInboundData
|
|
238
|
-
};
|
|
239
|
+
**Technical Implementation**:
|
|
239
240
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
api.manufacturer.lg.processInboundData(); // (from default object)
|
|
243
|
-
// If there were other named exports, they'd be here too
|
|
244
|
-
```
|
|
241
|
+
- **Detection**: [C16](API-RULES/API-RULES-CONDITIONS.md#c16) - Function name availability check
|
|
242
|
+
- **Processing**: [C23](API-RULES/API-RULES-CONDITIONS.md#c23) - Function name takes precedence
|
|
245
243
|
|
|
246
|
-
|
|
244
|
+
---
|
|
247
245
|
|
|
248
|
-
|
|
249
|
-
// File: api_tests/api_test/funcmod/funcmod.mjs
|
|
250
|
-
export default function (name) {
|
|
251
|
-
return `Hello, ${name}!`;
|
|
252
|
-
}
|
|
246
|
+
## Rule 5: Multiple Module Default Export Handling
|
|
253
247
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
248
|
+
**Category**: Export Handling
|
|
249
|
+
**Status**: ✅ Verified (`api_tests/api_test`)
|
|
250
|
+
**Technical**: [C08, C09d](API-RULES/API-RULES-CONDITIONS.md#c08)
|
|
257
251
|
|
|
258
|
-
**
|
|
252
|
+
**Condition**: Category contains multiple modules with default exports
|
|
253
|
+
**Behavior**: Each module maintains its own namespace with the default export accessible directly
|
|
254
|
+
**Processing Path**: Standard namespace preservation (no flattening)
|
|
259
255
|
|
|
260
|
-
|
|
261
|
-
// C08c: Traditional Default Function - Root API
|
|
262
|
-
// src/lib/helpers/api_builder/decisions.mjs Line 378
|
|
263
|
-
if (mode === "root" && getRootDefault && setRootDefault && !hasMultipleDefaultExports && !getRootDefault()) {
|
|
264
|
-
// Root context: Make API itself callable
|
|
265
|
-
setRootDefault(defaultFunction);
|
|
266
|
-
rootDefaultSet = true;
|
|
267
|
-
} else {
|
|
268
|
-
// C08d: Function As Namespace (Subfolder context)
|
|
269
|
-
// Line 384+
|
|
270
|
-
apiAssignments[apiPathKey] = mod;
|
|
271
|
-
namespaced = true;
|
|
272
|
-
}
|
|
273
|
-
```
|
|
256
|
+
**Verified Examples**:
|
|
274
257
|
|
|
275
|
-
|
|
258
|
+
```javascript
|
|
259
|
+
// File: validators/email.mjs
|
|
260
|
+
export default function validateEmail(email) { /* ... */ }
|
|
276
261
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
node -e "const slothlet = await import('./index.mjs'); const api = await slothlet.default({ dir: './api_tests/api_test' }); console.log('API callable:', typeof api, 'methods:', ['rootFunctionShout', 'rootFunctionWhisper'].map(m => m + ': ' + typeof api[m]));"
|
|
262
|
+
// File: validators/phone.mjs
|
|
263
|
+
export default function validatePhone(phone) { /* ... */ }
|
|
280
264
|
|
|
281
|
-
|
|
282
|
-
|
|
265
|
+
api.validators.email("test@example.com"); // ✅ Default function callable
|
|
266
|
+
api.validators.phone("+1234567890"); // ✅ Default function callable
|
|
283
267
|
```
|
|
284
268
|
|
|
285
|
-
**
|
|
269
|
+
**Technical Implementation**:
|
|
286
270
|
|
|
287
|
-
|
|
271
|
+
- **Detection**: [C08](API-RULES/API-RULES-CONDITIONS.md#c08) - `moduleCount > 1 && defaultExportCount > 0`
|
|
272
|
+
- **Processing**: [C09d](API-RULES/API-RULES-CONDITIONS.md#c09d) - Standard namespace preservation
|
|
273
|
+
- **Strategy**: `processingStrategy = "standard"` → no flattening
|
|
288
274
|
|
|
289
|
-
|
|
275
|
+
**Key Behavior**:
|
|
290
276
|
|
|
291
|
-
|
|
277
|
+
- Maintains clear namespace separation between modules
|
|
278
|
+
- Prevents naming conflicts where multiple defaults would collide
|
|
279
|
+
- No automatic flattening when multiple defaults are present
|
|
292
280
|
|
|
293
|
-
|
|
294
|
-
**Behavior**: Files with defaults become namespaces, files without defaults flatten to container level
|
|
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`
|
|
281
|
+
---
|
|
298
282
|
|
|
299
|
-
|
|
283
|
+
## Rule 6: Multiple Module Mixed Exports
|
|
300
284
|
|
|
301
|
-
**
|
|
285
|
+
**Category**: Special Cases
|
|
286
|
+
**Status**: ✅ Verified (`api_tests/api_test_mixed`)
|
|
287
|
+
**Technical**: [C14, C09d](API-RULES/API-RULES-CONDITIONS.md#c14)
|
|
302
288
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
api.input(); // → callable namespace + api.input.getAllInputNames(), api.input.getCurrentInput()
|
|
307
|
-
api.key(); // → callable namespace + api.key.getAllKeyNames(), api.key.getKeyCode()
|
|
308
|
-
api.power(); // → callable namespace (default only)
|
|
309
|
-
api.volume(); // → callable namespace + api.volume.getPseudoMuteState(), etc.
|
|
310
|
-
```
|
|
289
|
+
**Condition**: Category contains modules with mixed export types (some default, some named-only)
|
|
290
|
+
**Behavior**: Standard namespace processing - each module maintains a distinct namespace
|
|
291
|
+
**Processing Path**: Conservative approach to prevent conflicts
|
|
311
292
|
|
|
312
|
-
**
|
|
293
|
+
**Verified Examples**:
|
|
313
294
|
|
|
314
295
|
```javascript
|
|
315
|
-
//
|
|
316
|
-
|
|
317
|
-
api.cloneState(); // from state.mjs
|
|
318
|
-
api.emitLog(); // from state.mjs
|
|
319
|
-
api.getAllApps(); // from app.mjs
|
|
320
|
-
api.getCurrentApp(); // from app.mjs
|
|
321
|
-
api.down(); // from channel.mjs
|
|
322
|
-
api.getCurrentChannel(); // from channel.mjs
|
|
323
|
-
api.connect(); // from connection.mjs
|
|
324
|
-
api.disconnect(); // from connection.mjs
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
**Technical Implementation**:
|
|
296
|
+
// File: mixed/calculator.mjs (default export)
|
|
297
|
+
export default function calculate(operation, a, b) { /* ... */ }
|
|
328
298
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
if (hasMultipleDefaultExports) {
|
|
333
|
-
if (moduleHasDefault) {
|
|
334
|
-
return {
|
|
335
|
-
shouldFlatten: false,
|
|
336
|
-
preserveAsNamespace: true,
|
|
337
|
-
reason: "multi-default context with default export"
|
|
338
|
-
};
|
|
339
|
-
}
|
|
299
|
+
// File: mixed/constants.mjs (named exports only)
|
|
300
|
+
export const PI = 3.14159;
|
|
301
|
+
export const E = 2.71828;
|
|
340
302
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
return {
|
|
345
|
-
shouldFlatten: true,
|
|
346
|
-
flattenToRoot: true,
|
|
347
|
-
reason: "multi-default context without default export"
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
}
|
|
303
|
+
api.mixed.calculator("add", 2, 3); // ✅ Default accessible
|
|
304
|
+
api.mixed.constants.PI; // ✅ Named exports accessible
|
|
305
|
+
api.mixed.constants.E; // ✅ Clear namespace separation
|
|
351
306
|
```
|
|
352
307
|
|
|
353
|
-
**
|
|
354
|
-
|
|
355
|
-
```bash
|
|
356
|
-
node -e "const slothlet = await import('./index.mjs'); const api = await slothlet.default({ dir: './api_tests/api_tv_test' }); console.log('Files WITH defaults (namespaced):', ['config', 'input', 'key', 'power', 'volume'].map(k => k + ': ' + typeof api[k])); console.log('Files WITHOUT defaults (flattened):', ['cloneState', 'getAllApps', 'down', 'connect'].map(k => k + ': ' + typeof api[k]));"
|
|
357
|
-
```
|
|
308
|
+
**Technical Implementation**:
|
|
358
309
|
|
|
359
|
-
**
|
|
360
|
-
**Processing
|
|
310
|
+
- **Detection**: [C14](API-RULES/API-RULES-CONDITIONS.md#c14) - Mixed export types present
|
|
311
|
+
- **Processing**: [C09d](API-RULES/API-RULES-CONDITIONS.md#c09d) - Conservative namespace preservation
|
|
361
312
|
|
|
362
313
|
---
|
|
363
314
|
|
|
364
|
-
|
|
315
|
+
## Rule 7: Single Module Named Export Flattening
|
|
365
316
|
|
|
366
|
-
**
|
|
317
|
+
**Category**: Basic Flattening
|
|
318
|
+
**Status**: ✅ Verified (`api_tests/api_test`)
|
|
319
|
+
**User Guide**: [F02](API-RULES/API-FLATTENING.md#f02)
|
|
320
|
+
**Technical**: [C06, C09b](API-RULES/API-RULES-CONDITIONS.md#c06)
|
|
367
321
|
|
|
368
|
-
**Condition**:
|
|
369
|
-
**
|
|
370
|
-
**
|
|
371
|
-
**Git Commit**: `a50531d1ba712f0c4efd9ab9b7cf8f62a0d379da`
|
|
322
|
+
**Condition**: Category has one module file, module has named exports (no default export), filename ≠ category name
|
|
323
|
+
**Source Files**: `api_tests/api_test/config/settings.mjs`
|
|
324
|
+
**Implementation**: `getFlatteningDecision()` → single module named export flattening
|
|
372
325
|
|
|
373
326
|
**Verified Examples**:
|
|
374
327
|
|
|
375
328
|
```javascript
|
|
376
|
-
//
|
|
377
|
-
export const
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
//
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
api.config.host; // → "https://slothlet" ✅ VERIFIED
|
|
387
|
-
// api.config.config → undefined ✅ VERIFIED (no infinite nesting created)
|
|
329
|
+
// File: api_tests/api_test/config/settings.mjs
|
|
330
|
+
export const DATABASE_URL = "mongodb://localhost:27017/testdb";
|
|
331
|
+
export const API_PORT = 3000;
|
|
332
|
+
export const DEBUG_MODE = true;
|
|
333
|
+
|
|
334
|
+
// Without Rule 7: api.config.settings.DATABASE_URL ❌ (unnecessary nesting)
|
|
335
|
+
// With Rule 7: api.config.DATABASE_URL ✅ (clean flattening)
|
|
336
|
+
api.config.DATABASE_URL; // "mongodb://localhost:27017/testdb"
|
|
337
|
+
api.config.API_PORT; // 3000
|
|
338
|
+
api.config.DEBUG_MODE; // true
|
|
388
339
|
```
|
|
389
340
|
|
|
390
|
-
**
|
|
341
|
+
**Technical Implementation**:
|
|
391
342
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
# Expected output:
|
|
395
|
-
# api.config.host: https://slothlet
|
|
396
|
-
# api.config.config exists: false
|
|
397
|
-
```
|
|
343
|
+
- **Primary Condition**: [C06](API-RULES/API-RULES-CONDITIONS.md#c06) - `moduleCount === 1 && !moduleHasDefault && moduleKeys.length > 0`
|
|
344
|
+
- **Processing**: [C09b](API-RULES/API-RULES-CONDITIONS.md#c09b) - `flattenToCategory: true`
|
|
398
345
|
|
|
399
|
-
|
|
346
|
+
---
|
|
400
347
|
|
|
401
|
-
|
|
402
|
-
// C01: getFlatteningDecision() - decisions.mjs Line 105
|
|
403
|
-
if (isSelfReferential) {
|
|
404
|
-
return {
|
|
405
|
-
shouldFlatten: false,
|
|
406
|
-
preserveAsNamespace: true,
|
|
407
|
-
reason: "self-referential export"
|
|
408
|
-
};
|
|
409
|
-
}
|
|
348
|
+
## Rule 8: Single Module Default Export Promotion
|
|
410
349
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
}
|
|
350
|
+
**Category**: Basic Flattening
|
|
351
|
+
**Status**: ✅ Verified (`api_tests/api_test`)
|
|
352
|
+
**User Guide**: [F03](API-RULES/API-FLATTENING.md#f03)
|
|
353
|
+
**Technical**: [C07, C09c](API-RULES/API-RULES-CONDITIONS.md#c07)
|
|
416
354
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
namespaced = true;
|
|
421
|
-
}
|
|
355
|
+
**Condition**: Category has one module file with a default export
|
|
356
|
+
**Source Files**: `api_tests/api_test/logger.mjs`
|
|
357
|
+
**Implementation**: `getFlatteningDecision()` → single module default export promotion
|
|
422
358
|
|
|
423
|
-
|
|
424
|
-
else if (selfReferentialFiles.has(moduleName)) {
|
|
425
|
-
moduleDecision.type = "self-referential";
|
|
426
|
-
}
|
|
359
|
+
**Verified Examples**:
|
|
427
360
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
preserveAsNamespace: true,
|
|
433
|
-
reason: "self-referential default export"
|
|
434
|
-
};
|
|
361
|
+
```javascript
|
|
362
|
+
// File: api_tests/api_test/logger.mjs
|
|
363
|
+
export default function logger(message) {
|
|
364
|
+
console.log(`[LOG] ${message}`);
|
|
435
365
|
}
|
|
366
|
+
|
|
367
|
+
// Without Rule 8: api.logger.logger("Hello World") ❌ (redundant nesting)
|
|
368
|
+
// With Rule 8: api.logger("Hello World") ✅ (direct callable)
|
|
369
|
+
api.logger("Hello World"); // [LOG] Hello World
|
|
370
|
+
typeof api.logger; // "function"
|
|
436
371
|
```
|
|
437
372
|
|
|
438
|
-
**
|
|
373
|
+
**Callable Namespace Pattern**: When a folder contains a file matching the folder name with a default export (e.g. `logger/logger.mjs`), the default function becomes the namespace itself. Other files in the folder become properties on that function:
|
|
439
374
|
|
|
440
|
-
```
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
375
|
+
```javascript
|
|
376
|
+
// File: logger/logger.mjs → export default function log()
|
|
377
|
+
// File: logger/utils.mjs → named exports
|
|
378
|
+
api.logger("message"); // calls the default function
|
|
379
|
+
api.logger.utils.debug("x"); // other files remain as namespace properties
|
|
444
380
|
```
|
|
445
381
|
|
|
446
|
-
|
|
382
|
+
This pattern applies consistently at root level and category level.
|
|
383
|
+
|
|
384
|
+
**Technical Implementation**:
|
|
385
|
+
|
|
386
|
+
- **Primary Condition**: [C07](API-RULES/API-RULES-CONDITIONS.md#c07) - `moduleCount === 1 && moduleHasDefault`
|
|
387
|
+
- **Processing**: [C09c](API-RULES/API-RULES-CONDITIONS.md#c09c) - `promoteToCategory: true`
|
|
447
388
|
|
|
448
389
|
---
|
|
449
390
|
|
|
450
|
-
|
|
391
|
+
## Rule 9: Function Name Preference Over Sanitization
|
|
451
392
|
|
|
452
|
-
**
|
|
393
|
+
**Category**: Special Cases
|
|
394
|
+
**Status**: ✅ Fully Verified (autoIP, parseJSON, getHTTPStatus, XMLParser)
|
|
395
|
+
**User Guide**: [API-RULES/API-FLATTENING.md - Name Preservation](API-RULES/API-FLATTENING.md)
|
|
396
|
+
**Technical**: [C16, C19](API-RULES/API-RULES-CONDITIONS.md#c16)
|
|
453
397
|
|
|
454
|
-
**Condition**:
|
|
455
|
-
**Behavior**:
|
|
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`
|
|
398
|
+
**Condition**: Exported function has an explicit name that differs from the sanitized filename
|
|
399
|
+
**Behavior**: Preserve the original function name over the filename-based API path
|
|
458
400
|
|
|
459
401
|
**Verified Examples**:
|
|
460
402
|
|
|
461
403
|
```javascript
|
|
462
|
-
//
|
|
463
|
-
export
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
};
|
|
467
|
-
|
|
468
|
-
// Expected: Auto-flattening eliminates double nesting
|
|
469
|
-
// Without auto-flattening: api.math.math.add (double nesting)
|
|
470
|
-
// With auto-flattening: api.math.add (direct access to math object contents)
|
|
471
|
-
api.math.add(2, 3); // → 5 ✅ VERIFIED
|
|
472
|
-
api.math.multiply(2, 3); // → 6 ✅ VERIFIED
|
|
473
|
-
// api.math.math → undefined ✅ VERIFIED (no double nesting created)
|
|
474
|
-
```
|
|
404
|
+
// File: auto-ip.mjs
|
|
405
|
+
export function autoIP() { /* Get automatic IP */ }
|
|
406
|
+
// Sanitized filename: "autoIp" ❌
|
|
407
|
+
// Function name: "autoIP" ✅
|
|
475
408
|
|
|
476
|
-
|
|
409
|
+
// File: get-http-status.mjs
|
|
410
|
+
export function getHTTPStatus() { /* ... */ }
|
|
411
|
+
// Sanitized filename: "getHttpStatus" ❌
|
|
412
|
+
// Function name: "getHTTPStatus" ✅
|
|
477
413
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
# math.math exists: false
|
|
414
|
+
// File: parse-json.mjs
|
|
415
|
+
export function parseJSON(data) { /* ... */ }
|
|
416
|
+
// Sanitized filename: "parseJson" ❌
|
|
417
|
+
// Function name: "parseJSON" ✅
|
|
483
418
|
```
|
|
484
419
|
|
|
485
|
-
**Technical Implementation
|
|
420
|
+
**Technical Implementation**:
|
|
421
|
+
|
|
422
|
+
- **Primary Check**: [C16](API-RULES/API-RULES-CONDITIONS.md#c16) - `exportedFunctionName !== sanitizedName`
|
|
423
|
+
- **Detailed Check**: [C19](API-RULES/API-RULES-CONDITIONS.md#c19) - `exportedFunction.name !== sanitizedFileName`
|
|
424
|
+
- **Precedence**: Function name takes precedence over filename in API structure
|
|
425
|
+
|
|
426
|
+
**Common Preserved Patterns**:
|
|
427
|
+
|
|
428
|
+
- Technical acronyms: IP, HTTP, API, URL, JSON, XML, HTML
|
|
429
|
+
- Protocol names: TCP, UDP, FTP, SSH, SSL, TLS
|
|
430
|
+
- Format specs: JSON, XML, CSV, YAML, TOML
|
|
431
|
+
- Industry standards: OAuth, JWT, REST, GraphQL
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
## Rule 10: Generic Filename Parent-Level Promotion
|
|
436
|
+
|
|
437
|
+
**Category**: Special Cases
|
|
438
|
+
**Status**: ✅ Verified (`api_tests/api_test/nest4/singlefile.mjs`)
|
|
439
|
+
**User Guide**: [API-RULES/API-FLATTENING.md - Index File Pattern](API-RULES/API-FLATTENING.md)
|
|
440
|
+
**Technical**: [C17](API-RULES/API-RULES-CONDITIONS.md#c17)
|
|
441
|
+
|
|
442
|
+
**Condition**: File has a generic name (`index`, `main`, `default`, etc.)
|
|
443
|
+
**Behavior**: Generic filename becomes transparent; content is promoted to the meaningful parent name
|
|
444
|
+
|
|
445
|
+
**Verified Examples**:
|
|
486
446
|
|
|
487
447
|
```javascript
|
|
488
|
-
//
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
shouldFlatten: true,
|
|
492
|
-
useAutoFlattening: true,
|
|
493
|
-
reason: "auto-flatten single named export matching filename"
|
|
494
|
-
};
|
|
495
|
-
}
|
|
448
|
+
// File: database/main.mjs
|
|
449
|
+
export function connect() { /* ... */ }
|
|
450
|
+
export function query() { /* ... */ }
|
|
496
451
|
|
|
497
|
-
//
|
|
498
|
-
|
|
499
|
-
return {
|
|
500
|
-
shouldFlatten: true,
|
|
501
|
-
flattenType: "object-auto-flatten"
|
|
502
|
-
};
|
|
503
|
-
}
|
|
452
|
+
// Without Rule 10: api.database.main.connect() ❌ (generic 'main' adds no value)
|
|
453
|
+
// With Rule 10: api.database.connect() ✅ (promoted to parent level)
|
|
504
454
|
|
|
505
|
-
//
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
moduleDecision.flattenType = "single-named-export-match";
|
|
509
|
-
}
|
|
455
|
+
// File: auth/index.mjs
|
|
456
|
+
export function login() { /* ... */ }
|
|
457
|
+
export function logout() { /* ... */ }
|
|
510
458
|
|
|
511
|
-
//
|
|
512
|
-
|
|
513
|
-
return {
|
|
514
|
-
shouldFlatten: true,
|
|
515
|
-
flattenToRoot: false,
|
|
516
|
-
reason: "single named export matching filename"
|
|
517
|
-
};
|
|
518
|
-
}
|
|
459
|
+
// Without Rule 10: api.auth.index.login() ❌ (generic 'index' is noise)
|
|
460
|
+
// With Rule 10: api.auth.login() ✅ (clean parent-level promotion)
|
|
519
461
|
```
|
|
520
462
|
|
|
521
|
-
**
|
|
463
|
+
**Technical Implementation**:
|
|
522
464
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
# Look for: "bound.math.add(2, 3) 5" (not bound.math.math.add)
|
|
526
|
-
# Confirms auto-flattening eliminates double nesting
|
|
527
|
-
```
|
|
465
|
+
- **Detection**: [C17](API-RULES/API-RULES-CONDITIONS.md#c17) - `isGenericFilename(fileName)`
|
|
466
|
+
- **Promotion**: Content promoted to parent namespace; generic filename becomes invisible
|
|
528
467
|
|
|
529
|
-
**
|
|
468
|
+
**Note**: Promotion is guarded against name collisions - checked against existing parent namespace properties before promoting.
|
|
530
469
|
|
|
531
470
|
---
|
|
532
471
|
|
|
533
|
-
|
|
472
|
+
## Rule 11: AddApi Special File Pattern
|
|
534
473
|
|
|
535
|
-
**
|
|
474
|
+
**Category**: AddApi
|
|
475
|
+
**Status**: ✅ Verified (`api_tests/api_smart_flatten_addapi`)
|
|
476
|
+
**User Guide**: [F06](API-RULES/API-FLATTENING.md#f06)
|
|
477
|
+
**Technical**: [C33](API-RULES/API-RULES-CONDITIONS.md#c33)
|
|
536
478
|
|
|
537
|
-
**Condition**:
|
|
538
|
-
**Behavior**:
|
|
539
|
-
**
|
|
540
|
-
**Git Commit**: `a50531d1ba712f0c4efd9ab9b7cf8f62a0d379da`
|
|
479
|
+
**Condition**: A file named `addapi.mjs` is loaded via `api.slothlet.api.add()`
|
|
480
|
+
**Behavior**: Exports are always flattened to the mount namespace regardless of other settings
|
|
481
|
+
**Processing Path**: Detection in `getFlatteningDecision()` (`src/lib/processors/flatten.mjs`); execution in `src/lib/builders/modes-processor.mjs`
|
|
541
482
|
|
|
542
|
-
**
|
|
483
|
+
**Verified Example**:
|
|
543
484
|
|
|
544
485
|
```javascript
|
|
545
|
-
// File:
|
|
546
|
-
export
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
}
|
|
550
|
-
};
|
|
486
|
+
// File: plugin-folder/addapi.mjs
|
|
487
|
+
export function initializePlugin() { /* ... */ }
|
|
488
|
+
export function cleanup() { /* ... */ }
|
|
489
|
+
export function configure() { /* ... */ }
|
|
551
490
|
|
|
552
|
-
|
|
553
|
-
api.nested.date.today(); // → "2025-08-15" ✅ VERIFIED with api_tests/api_test
|
|
554
|
-
```
|
|
491
|
+
await api.slothlet.api.add("plugins", "./plugin-folder");
|
|
555
492
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
};
|
|
562
|
-
|
|
563
|
-
// Result: Object contents promoted to folder level (math/math.mjs → api.math)
|
|
564
|
-
api.math.add(2, 3); // → 5 ✅ VERIFIED with api_tests/api_test
|
|
493
|
+
// addapi.mjs exports are always flattened - never nested:
|
|
494
|
+
api.plugins.initializePlugin(); // ✅
|
|
495
|
+
api.plugins.cleanup(); // ✅
|
|
496
|
+
api.plugins.configure(); // ✅
|
|
497
|
+
// NOT: api.plugins.addapi.initializePlugin() ❌
|
|
565
498
|
```
|
|
566
499
|
|
|
567
|
-
**
|
|
500
|
+
**Technical Implementation**:
|
|
568
501
|
|
|
569
502
|
```javascript
|
|
570
|
-
//
|
|
571
|
-
|
|
572
|
-
|
|
503
|
+
// C33: AddApi Special File Detection
|
|
504
|
+
if (moduleKeys.includes("addapi")) {
|
|
505
|
+
const addapiModule = newModules["addapi"];
|
|
506
|
+
const otherModules = { ...newModules };
|
|
507
|
+
delete otherModules["addapi"];
|
|
508
|
+
modulesToMerge = { ...addapiModule, ...otherModules };
|
|
509
|
+
}
|
|
573
510
|
```
|
|
574
511
|
|
|
575
|
-
**
|
|
512
|
+
**Use Cases**:
|
|
576
513
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
format(input) {
|
|
581
|
-
return `Formatted: ${input}`;
|
|
582
|
-
},
|
|
583
|
-
parse(value) {
|
|
584
|
-
return `Parsed: ${value}`;
|
|
585
|
-
}
|
|
586
|
-
};
|
|
514
|
+
- Plugin systems that extend the API at a known namespace
|
|
515
|
+
- Hot-reloadable API extension points
|
|
516
|
+
- Clean integration of external modules into a live API surface
|
|
587
517
|
|
|
588
|
-
|
|
589
|
-
api.singletest.helper.utilities.format("test"); // → "Formatted: test" ✅ VERIFIED (eager mode)
|
|
590
|
-
// Note: Deep nested paths have known issues in lazy mode
|
|
591
|
-
```
|
|
518
|
+
---
|
|
592
519
|
|
|
593
|
-
|
|
520
|
+
## Rule 12: Module Ownership and Selective API Overwriting
|
|
594
521
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
}
|
|
522
|
+
**Category**: AddApi
|
|
523
|
+
**Status**: ✅ Implemented (`src/lib/handlers/ownership.mjs`)
|
|
524
|
+
**User Guide**: [F07](API-RULES/API-FLATTENING.md#f07)
|
|
525
|
+
**Technical**: [C19-C22](API-RULES/API-RULES-CONDITIONS.md#c19)
|
|
600
526
|
|
|
601
|
-
|
|
602
|
-
api.funcmod("test"); // → "Hello, test!" ✅ VERIFIED with api_tests/api_test
|
|
603
|
-
```
|
|
527
|
+
**Purpose**: Track which module registered each API path, enabling safe hot-reloading and cross-module conflict protection.
|
|
604
528
|
|
|
605
|
-
**
|
|
529
|
+
**Implementation**: Stack-based ownership system. Each API path maintains an independent ownership history stack. Removing a module automatically rolls back to the previous owner. Collision behavior is controlled by the `api.collision` configuration.
|
|
606
530
|
|
|
607
|
-
|
|
608
|
-
// C10: Single-file function folder match - decisions.mjs Line 584
|
|
609
|
-
if (moduleName === categoryName && typeof mod === "function" && currentDepth > 0) {
|
|
610
|
-
return {
|
|
611
|
-
shouldFlatten: true,
|
|
612
|
-
flattenType: "function-folder-match"
|
|
613
|
-
};
|
|
614
|
-
}
|
|
531
|
+
### Configuration
|
|
615
532
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
533
|
+
```javascript
|
|
534
|
+
const api = await slothlet({
|
|
535
|
+
dir: "./api",
|
|
536
|
+
api: {
|
|
537
|
+
collision: {
|
|
538
|
+
initial: "merge", // During initial API build
|
|
539
|
+
api: "replace" // During api.slothlet.api.add()
|
|
540
|
+
}
|
|
623
541
|
}
|
|
624
|
-
}
|
|
542
|
+
});
|
|
543
|
+
```
|
|
625
544
|
|
|
626
|
-
|
|
627
|
-
if (functionNameMatchesFolder && currentDepth > 0) {
|
|
628
|
-
return {
|
|
629
|
-
shouldFlatten: true,
|
|
630
|
-
flattenType: "function-folder-match",
|
|
631
|
-
preferredName: mod.name
|
|
632
|
-
};
|
|
633
|
-
}
|
|
545
|
+
### moduleId Tracking
|
|
634
546
|
|
|
635
|
-
|
|
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
|
-
};
|
|
641
|
-
}
|
|
642
|
-
```
|
|
547
|
+
Each `api.slothlet.api.add()` call accepts an optional `moduleId`. This is the key for ownership tracking:
|
|
643
548
|
|
|
644
|
-
|
|
549
|
+
```javascript
|
|
550
|
+
// Module A registers plugins namespace
|
|
551
|
+
await api.slothlet.api.add("plugins.moduleA", "./modules/moduleA", {}, {
|
|
552
|
+
moduleId: "moduleA"
|
|
553
|
+
});
|
|
645
554
|
|
|
646
|
-
|
|
555
|
+
// Module B registers in the same parent namespace
|
|
556
|
+
await api.slothlet.api.add("plugins.moduleB", "./modules/moduleB", {}, {
|
|
557
|
+
moduleId: "moduleB"
|
|
558
|
+
});
|
|
647
559
|
|
|
648
|
-
|
|
560
|
+
// Hot-reload Module A - ownership system allows this because moduleA owns these paths
|
|
561
|
+
await api.slothlet.api.add("plugins.moduleA", "./modules/moduleA-v2", {}, {
|
|
562
|
+
moduleId: "moduleA",
|
|
563
|
+
forceOverwrite: true
|
|
564
|
+
});
|
|
649
565
|
|
|
650
|
-
|
|
566
|
+
// Cross-module overwrite - blocked if collision mode is "error"
|
|
567
|
+
await api.slothlet.api.add("plugins.moduleB", "./modules/other", {}, {
|
|
568
|
+
moduleId: "moduleA", // moduleA does not own moduleB's paths
|
|
569
|
+
forceOverwrite: true // Throws OWNERSHIP_CONFLICT in "error" mode
|
|
570
|
+
});
|
|
571
|
+
```
|
|
651
572
|
|
|
652
|
-
|
|
653
|
-
**Behavior**: Use original function name instead of sanitized version to preserve conventions (IP, JSON, HTTP, etc.)
|
|
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`
|
|
573
|
+
### Ownership Stack
|
|
656
574
|
|
|
657
|
-
|
|
575
|
+
Each API path has a history stack. When a module is removed, the previous owner is automatically restored:
|
|
658
576
|
|
|
659
577
|
```javascript
|
|
660
|
-
//
|
|
661
|
-
|
|
662
|
-
//
|
|
663
|
-
api.task.autoIP(); // → "testAutoIP" ✅ VERIFIED with api_tests/api_test
|
|
664
|
-
|
|
665
|
-
// Note: Other examples (parseJSON, getHTTPStatus) mentioned in the rule
|
|
666
|
-
// do not exist in current test files - need real test cases
|
|
667
|
-
// ⚠️ Need additional test files for broader verification
|
|
578
|
+
// Stack for "plugins.tools": [module-a, module-b] (module-b is current owner)
|
|
579
|
+
await api.slothlet.api.remove("module-b");
|
|
580
|
+
// Stack restored to: [module-a] (module-a is active again)
|
|
668
581
|
```
|
|
669
582
|
|
|
670
|
-
|
|
583
|
+
### Collision Modes
|
|
671
584
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
585
|
+
| Mode | Behavior |
|
|
586
|
+
| ---- | -------- |
|
|
587
|
+
| `"merge"` (default) | Preserve existing properties, add new ones |
|
|
588
|
+
| `"merge-replace"` | Add new properties, overwrite existing |
|
|
589
|
+
| `"replace"` | Completely replace the existing value |
|
|
590
|
+
| `"skip"` | Keep existing value, silently ignore new |
|
|
591
|
+
| `"warn"` | Keep existing value, log a warning |
|
|
592
|
+
| `"error"` | Throw `OWNERSHIP_CONFLICT` error |
|
|
677
593
|
|
|
678
|
-
|
|
679
|
-
if (functionNameMatchesFilename) {
|
|
680
|
-
return {
|
|
681
|
-
shouldFlatten: false,
|
|
682
|
-
preferredName: mod.name
|
|
683
|
-
};
|
|
684
|
-
}
|
|
594
|
+
### forceOverwrite
|
|
685
595
|
|
|
686
|
-
|
|
687
|
-
if (hasPreferredName) {
|
|
688
|
-
return {
|
|
689
|
-
specialHandling: "preferred-export-names"
|
|
690
|
-
};
|
|
691
|
-
}
|
|
596
|
+
`forceOverwrite: true` requires an explicit `moduleId` and performs a complete replacement regardless of collision mode. Use for cases where a module must fully replace its own prior registration:
|
|
692
597
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
}
|
|
598
|
+
```javascript
|
|
599
|
+
await api.slothlet.api.add("config", "./new-config", {}, {
|
|
600
|
+
moduleId: "config-v2",
|
|
601
|
+
forceOverwrite: true
|
|
602
|
+
});
|
|
699
603
|
```
|
|
700
604
|
|
|
701
|
-
**
|
|
605
|
+
**Source Code**: `src/lib/handlers/ownership.mjs`
|
|
702
606
|
|
|
703
|
-
|
|
704
|
-
node tests/debug-slothlet.mjs
|
|
705
|
-
# Look for function names with preserved casing (autoIP, parseJSON, getHTTPStatus)
|
|
706
|
-
# Confirms preference logic maintains programming conventions
|
|
707
|
-
```
|
|
607
|
+
---
|
|
708
608
|
|
|
709
|
-
|
|
609
|
+
## Rule 13: AddApi Path Deduplication Flattening
|
|
710
610
|
|
|
711
|
-
|
|
611
|
+
> **New in v3**
|
|
712
612
|
|
|
713
|
-
|
|
613
|
+
**Category**: AddApi
|
|
614
|
+
**Status**: ✅ Implemented (`api_tests/smart_flatten/api_smart_flatten_folder_config`)
|
|
615
|
+
**User Guide**: [F08](API-RULES/API-FLATTENING.md#f08)
|
|
616
|
+
**Technical**: [C34](API-RULES/API-RULES-CONDITIONS.md#c34)
|
|
714
617
|
|
|
715
|
-
**
|
|
618
|
+
**Purpose**: When `api.slothlet.api.add("config", folder)` is called and the folder contains a subfolder whose name matches the last segment of the mount path (e.g. `config/config.mjs`), prevent double-nesting `api.config.config.*` by hoisting the subfolder's exports up to `api.config.*`.
|
|
716
619
|
|
|
717
|
-
**Condition**:
|
|
718
|
-
**Behavior**: Promote export to parent level to eliminate meaningless intermediate namespace
|
|
719
|
-
**Source Code Conditions**: [C14](API-RULES-CONDITIONS.md#c14-parent-level-flattening-generic-filenames) (parent-level flattening logic)
|
|
720
|
-
**Git Commit**: `a50531d1ba712f0c4efd9ab9b7cf8f62a0d379da`
|
|
620
|
+
**Condition**: After `buildAPI` returns `newApi`, if `newApi` contains a key equal to `lastPart` (last segment of `normalizedPath`) AND the matching value's `filePath` has its parent directory equal to `resolvedFolderPath/lastPart` (direct child check), hoist that key's own exports to the same level as the other keys in `newApi` and remove the duplicate key.
|
|
721
621
|
|
|
722
|
-
**Verified
|
|
622
|
+
**Verified Example**:
|
|
723
623
|
|
|
724
624
|
```javascript
|
|
725
|
-
//
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
625
|
+
// Folder structure: api_smart_flatten_folder_config/
|
|
626
|
+
// main.mjs ← exports getRootInfo, setRootConfig
|
|
627
|
+
// config/
|
|
628
|
+
// config.mjs ← exports getNestedConfig, setNestedConfig
|
|
729
629
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
630
|
+
await api.slothlet.api.add("config", "./api_smart_flatten_folder_config", {});
|
|
631
|
+
|
|
632
|
+
// Without Rule 13 (double-nested):
|
|
633
|
+
api.config.config.getNestedConfig(); // ❌
|
|
634
|
+
|
|
635
|
+
// With Rule 13 (hoisted):
|
|
636
|
+
api.config.getNestedConfig(); // ✅ subfolder exports promoted
|
|
637
|
+
api.config.setNestedConfig(); // ✅
|
|
638
|
+
api.config.main.getRootInfo(); // ✅ other files unaffected
|
|
733
639
|
```
|
|
734
640
|
|
|
735
|
-
**
|
|
641
|
+
**Guard - `isDirectChild`**: Rule 13 only fires when the matching key's `filePath` is **directly** inside `resolvedFolderPath/lastPart`. This prevents false positives when a deeper nested folder coincidentally shares the mount-path name:
|
|
736
642
|
|
|
737
643
|
```javascript
|
|
738
|
-
//
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
return {
|
|
745
|
-
shouldFlatten: true,
|
|
746
|
-
flattenType: "parent-level-flatten"
|
|
747
|
-
};
|
|
748
|
-
}
|
|
749
|
-
}
|
|
644
|
+
// Should NOT hoist (services/services/services.mjs):
|
|
645
|
+
// api.add("services", folder) → newApi has key "services"
|
|
646
|
+
// but filePath = .../services/services/services.mjs
|
|
647
|
+
// dirname = .../services/services ≠ resolvedFolderPath/services
|
|
648
|
+
// → Rule 13 does NOT fire
|
|
649
|
+
// → api.services.services.getNestedService remains properly nested ✅
|
|
750
650
|
```
|
|
751
651
|
|
|
752
|
-
**
|
|
753
|
-
|
|
754
|
-
**Test Verification**:
|
|
652
|
+
**Implementation**: `src/lib/handlers/api-manager.mjs` - immediately after `buildAPI` call, before `setValueAtPath`
|
|
755
653
|
|
|
756
|
-
|
|
757
|
-
node tests/debug-slothlet.mjs
|
|
758
|
-
# Look for: api.nest4.beta (not api.nest4.singlefile.beta)
|
|
759
|
-
# Confirms generic filename elimination
|
|
760
|
-
```
|
|
654
|
+
---
|
|
761
655
|
|
|
762
|
-
|
|
656
|
+
## Verification Status
|
|
657
|
+
|
|
658
|
+
| Rule | Title | Status | Test Source |
|
|
659
|
+
| ---- | ---------------------------------------------- | ------------ | ----------- |
|
|
660
|
+
| 1 | Filename Matches Container Flattening | ✅ Verified | `api_tests/api_test` |
|
|
661
|
+
| 2 | Named-Only Export Collection | ✅ Verified | `api_tests/api_test` |
|
|
662
|
+
| 3 | Empty Module Handling | ✅ Verified | debug testing |
|
|
663
|
+
| 4 | Named Export with Function Name Preservation | ✅ Verified | `api_tests/api_test`, `api_tests/api_tv_test` |
|
|
664
|
+
| 5 | Multiple Module Default Export Handling | ✅ Verified | `api_tests/api_tv_test` |
|
|
665
|
+
| 6 | Multiple Module Mixed Exports | ✅ Verified | `api_tests/api_test_mixed` |
|
|
666
|
+
| 7 | Single Module Named Export Flattening | ✅ Verified | `api_tests/api_test` |
|
|
667
|
+
| 8 | Single Module Default Export Promotion | ✅ Verified | Multiple test files |
|
|
668
|
+
| 9 | Function Name Preference Over Sanitization | ✅ Verified | autoIP, parseJSON, getHTTPStatus, XMLParser |
|
|
669
|
+
| 10 | Generic Filename Parent-Level Promotion | ✅ Verified | `api_tests/api_test/nest4/singlefile.mjs` |
|
|
670
|
+
| 11 | AddApi Special File Pattern | ✅ Verified | `api_tests/api_smart_flatten_addapi` |
|
|
671
|
+
| 12 | Module Ownership and Selective API Overwriting | ✅ Verified | `src/lib/handlers/ownership.mjs` |
|
|
672
|
+
| 13 | AddApi Path Deduplication Flattening | ✅ Verified | `api_tests/smart_flatten/api_smart_flatten_folder_config` |
|
|
763
673
|
|
|
764
674
|
---
|
|
765
675
|
|
|
766
|
-
##
|
|
767
|
-
|
|
768
|
-
###
|
|
769
|
-
|
|
770
|
-
|
|
|
771
|
-
|
|
|
772
|
-
|
|
|
773
|
-
|
|
|
774
|
-
|
|
|
775
|
-
|
|
|
776
|
-
|
|
|
777
|
-
|
|
|
778
|
-
|
|
|
779
|
-
|
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
|
784
|
-
|
|
|
785
|
-
|
|
|
786
|
-
|
|
|
787
|
-
|
|
|
788
|
-
|
|
|
789
|
-
|
|
|
790
|
-
|
|
|
791
|
-
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
|
796
|
-
|
|
|
797
|
-
|
|
|
798
|
-
|
|
|
799
|
-
|
|
|
800
|
-
|
|
|
801
|
-
|
|
|
802
|
-
|
|
|
803
|
-
| C21 | multidefault:168 | Rule 6 | Multi-default self-referential |
|
|
804
|
-
| C22 | multidefault:179 | Rule 5 | Multi-default with default |
|
|
805
|
-
| C23 | multidefault:186 | Rule 5 | Multi-default without default |
|
|
806
|
-
| C24 | multidefault:200 | Rule 7 | Multi-default single named export |
|
|
807
|
-
| C26 | multidefault:220+ | Rule 2 | Multi-default default fallback |
|
|
808
|
-
|
|
809
|
-
**Total Coverage**: 23 source code conditions mapped to 10 comprehensive rules
|
|
810
|
-
|
|
811
|
-
> **Note**: Rule 11 conditions (C06, C17, C25) have been removed following architectural decision to eliminate single file context flattening. This preserves API path predictability and flexibility.
|
|
812
|
-
|
|
813
|
-
## Source Code Locations
|
|
814
|
-
|
|
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).
|
|
829
|
-
|
|
830
|
-
## Test File Index
|
|
831
|
-
|
|
832
|
-
_To be populated with confirmed examples from actual test files_
|
|
676
|
+
## Cross-Reference Index
|
|
677
|
+
|
|
678
|
+
### By Flattening Pattern (F##)
|
|
679
|
+
|
|
680
|
+
| Flattening Pattern | API Rules | Technical Conditions |
|
|
681
|
+
| ------------------ | --------- | -------------------- |
|
|
682
|
+
| [F01](API-RULES/API-FLATTENING.md#f01) | Rule 1 | [C05, C09b, C11](API-RULES/API-RULES-CONDITIONS.md#c05) |
|
|
683
|
+
| [F02](API-RULES/API-FLATTENING.md#f02) | Rule 8 (Pattern A) | [C12, C21a](API-RULES/API-RULES-CONDITIONS.md#c12) |
|
|
684
|
+
| [F03](API-RULES/API-FLATTENING.md#f03) | Rule 7 | [C04, C09a, C18, C21c, C30](API-RULES/API-RULES-CONDITIONS.md#c04) |
|
|
685
|
+
| [F04](API-RULES/API-FLATTENING.md#f04) | Rule 4, Rule 8 (Pattern B) | [C08c, C24](API-RULES/API-RULES-CONDITIONS.md#c08c) |
|
|
686
|
+
| [F05](API-RULES/API-FLATTENING.md#f05) | Rule 4, Rule 8 (Pattern C) | [C08c, C11](API-RULES/API-RULES-CONDITIONS.md#c08c) |
|
|
687
|
+
| [F06](API-RULES/API-FLATTENING.md#f06) | Rule 11 | [C33](API-RULES/API-RULES-CONDITIONS.md#c33) |
|
|
688
|
+
| [F07](API-RULES/API-FLATTENING.md#f07) | Rule 12 | [C19-C22](API-RULES/API-RULES-CONDITIONS.md#c19) |
|
|
689
|
+
| [F08](API-RULES/API-FLATTENING.md#f08) | Rule 13 | [C34](API-RULES/API-RULES-CONDITIONS.md#c34) |
|
|
690
|
+
|
|
691
|
+
### By Technical Condition (C##)
|
|
692
|
+
|
|
693
|
+
| Condition | API Rules | Flattening Patterns |
|
|
694
|
+
| --------- | --------- | ------------------- |
|
|
695
|
+
| [C01-C07](API-RULES/API-RULES-CONDITIONS.md#c01) | Rules 1, 6, 7, 8 | F01, F03 |
|
|
696
|
+
| [C08-C09d](API-RULES/API-RULES-CONDITIONS.md#c08) | Rules 4, 6, 7 | F04, F05 |
|
|
697
|
+
| [C10-C21d](API-RULES/API-RULES-CONDITIONS.md#c10) | Rules 1, 2, 3, 5, 7, 8, 9, 10 | F01, F02, F03 |
|
|
698
|
+
| [C22-C26](API-RULES/API-RULES-CONDITIONS.md#c22) | Rules 4, 6 | F04, F05 |
|
|
699
|
+
| [C27-C32](API-RULES/API-RULES-CONDITIONS.md#c27) | Rules 5, 6, 7 | Multi-default scenarios |
|
|
700
|
+
| [C33](API-RULES/API-RULES-CONDITIONS.md#c33) | Rule 11 | F06 |
|
|
701
|
+
| [C34](API-RULES/API-RULES-CONDITIONS.md#c34) | Rule 13 | F08 |
|
|
702
|
+
|
|
703
|
+
### By Processing Context
|
|
704
|
+
|
|
705
|
+
| Context | Rules | Primary Conditions |
|
|
706
|
+
| ------- | ----- | ------------------ |
|
|
707
|
+
| **Single-File Directories** | 1, 7, 8, 10 | C11, C12, C04, C17 |
|
|
708
|
+
| **Multi-File Directories** | 1, 2, 5, 7, 9 | C13, C15, C21a-d, C16, C19 |
|
|
709
|
+
| **Multi-Default Scenarios** | 5, 6, 7 | C02, C03, C27-C32 |
|
|
710
|
+
| **AddApi Operations** | 11, 12, 13 | C33, C34, C19-C22 |
|
|
711
|
+
| **Root-Level Processing** | 4, 8, 10 | C08c, C22, C17 |
|
|
712
|
+
| **Subfolder Processing** | 4, 6, 8 | C08d, C20, C24 |
|