@cldmv/slothlet 2.11.0 → 3.0.1
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 +355 -325
- package/README.md +554 -238
- 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 +123 -117
- 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 +525 -744
- package/docs/API-RULES.md +338 -486
- package/index.cjs +4 -4
- package/index.mjs +82 -45
- package/package.json +143 -30
- 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 +184 -111
- 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 -553
- package/dist/lib/helpers/api_builder/analysis.mjs +0 -532
- package/dist/lib/helpers/api_builder/construction.mjs +0 -495
- package/dist/lib/helpers/api_builder/decisions.mjs +0 -748
- 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 -154
- package/dist/lib/modes/slothlet_lazy.mjs +0 -594
- package/docs/API-RULES-CONDITIONS.md +0 -712
- 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 -102
- 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/types/index.d.mts.map +0 -1
package/docs/API-RULES.md
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
# Slothlet API Rules
|
|
1
|
+
# Slothlet API Rules
|
|
2
2
|
|
|
3
|
-
**Complete Guide to All API Generation Behaviors
|
|
4
|
-
|
|
5
|
-
- **Version**: 2.0
|
|
6
|
-
- **Date**: January 3, 2026
|
|
7
|
-
- **Purpose**: Maintainer and contributor guide documenting all API generation rules with verified examples and technical cross-references
|
|
8
|
-
- **Status**: ✅ **VERIFIED AND CURRENT**
|
|
3
|
+
**Complete Guide to All API Generation Behaviors**
|
|
9
4
|
|
|
10
5
|
---
|
|
11
6
|
|
|
@@ -14,49 +9,53 @@
|
|
|
14
9
|
This is the **middle layer** of slothlet's three-tier documentation system:
|
|
15
10
|
|
|
16
11
|
```text
|
|
17
|
-
📋 API-FLATTENING
|
|
18
|
-
|
|
19
|
-
📊 API-RULES
|
|
20
|
-
|
|
21
|
-
🔧 API-RULES-CONDITIONS
|
|
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##
|
|
22
19
|
```
|
|
23
20
|
|
|
24
21
|
**Cross-Reference Navigation:**
|
|
25
22
|
|
|
26
|
-
-
|
|
27
|
-
-
|
|
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
|
|
28
26
|
|
|
29
27
|
---
|
|
30
28
|
|
|
31
29
|
## Overview
|
|
32
30
|
|
|
33
|
-
This document catalogs **all
|
|
31
|
+
This document catalogs **all 13 API generation behaviors** in slothlet with:
|
|
34
32
|
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
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)
|
|
40
38
|
|
|
41
|
-
**Why
|
|
39
|
+
**Why 13 Rules vs Flattening Patterns?**
|
|
42
40
|
|
|
43
|
-
The [
|
|
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:
|
|
44
42
|
|
|
45
|
-
- **Flattening Rules** (1, 7, 8, 10, 11,
|
|
46
|
-
- **Non-Flattening Rules** (2, 3, 4, 5, 6, 9): Export collection, function naming, empty modules, mixed exports
|
|
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
|
|
47
46
|
|
|
48
|
-
**Methodology**: Each rule has been systematically verified against test files and source code
|
|
47
|
+
**Methodology**: Each rule has been systematically verified against test files and source code.
|
|
49
48
|
|
|
50
49
|
---
|
|
51
50
|
|
|
52
51
|
## Rule Categories
|
|
53
52
|
|
|
54
|
-
| Category | Rules
|
|
55
|
-
| --------------------- |
|
|
56
|
-
| **Basic Flattening** | 1, 7, 8
|
|
57
|
-
| **Export Handling** | 2, 4, 5
|
|
58
|
-
| **Special Cases** | 3, 6, 9, 10
|
|
59
|
-
| **AddApi Extensions** | 11, 12
|
|
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) |
|
|
60
59
|
|
|
61
60
|
---
|
|
62
61
|
|
|
@@ -74,17 +73,18 @@ The [FLATTENING guide](API-FLATTENING-v2.md) focuses on **when content gets prom
|
|
|
74
73
|
10. [Rule 10: Generic Filename Parent-Level Promotion](#rule-10-generic-filename-parent-level-promotion)
|
|
75
74
|
11. [Rule 11: AddApi Special File Pattern](#rule-11-addapi-special-file-pattern)
|
|
76
75
|
12. [Rule 12: Module Ownership and Selective API Overwriting](#rule-12-module-ownership-and-selective-api-overwriting)
|
|
77
|
-
13. [
|
|
78
|
-
14. [
|
|
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
79
|
|
|
80
80
|
---
|
|
81
81
|
|
|
82
82
|
## Rule 1: Filename Matches Container Flattening
|
|
83
83
|
|
|
84
84
|
**Category**: Basic Flattening
|
|
85
|
-
**Status**: ✅
|
|
86
|
-
**User Guide**: [
|
|
87
|
-
**Technical**: [
|
|
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)
|
|
88
88
|
|
|
89
89
|
**Condition**: Filename matches folder name AND no default export AND has named exports
|
|
90
90
|
**Source Files**: `api_tests/api_test/math/math.mjs`
|
|
@@ -101,24 +101,20 @@ export function subtract(a, b) {
|
|
|
101
101
|
return a - b;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
//
|
|
105
|
-
api.math.add(2, 3)
|
|
106
|
-
api.math.
|
|
107
|
-
|
|
108
|
-
// Without Rule 1: api.math.math.add(2, 3) ❌ (redundant nesting)
|
|
109
|
-
// With Rule 1: api.math.add(2, 3) ✅ (clean flattening)
|
|
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
|
|
110
108
|
```
|
|
111
109
|
|
|
112
110
|
**Technical Implementation**:
|
|
113
111
|
|
|
114
|
-
- **Primary Condition**: [C05](API-RULES-CONDITIONS
|
|
115
|
-
- **Processing**: [C09b](API-RULES-CONDITIONS
|
|
116
|
-
|
|
117
|
-
**Complete Source Code Implementation**:
|
|
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
|
|
118
114
|
|
|
119
115
|
```javascript
|
|
120
116
|
// C05: Filename Matches Container (Category-Level Flatten)
|
|
121
|
-
// Location: src/lib/helpers/api_builder/decisions.mjs
|
|
117
|
+
// Location: src/lib/helpers/api_builder/decisions.mjs
|
|
122
118
|
if (categoryName && fileName === categoryName && !moduleHasDefault && moduleKeys.length > 0) {
|
|
123
119
|
return {
|
|
124
120
|
shouldFlatten: true,
|
|
@@ -132,34 +128,19 @@ if (categoryName && fileName === categoryName && !moduleHasDefault && moduleKeys
|
|
|
132
128
|
```
|
|
133
129
|
|
|
134
130
|
**Processing Path**: Subfolder processing via `getFlatteningDecision()` (currentDepth > 0)
|
|
135
|
-
**Source Code Location**: `src/lib/helpers/api_builder/decisions.mjs` - `getFlatteningDecision()`
|
|
136
|
-
**Git Commit Reference**: `a50531d1ba712f0c4efd9ab9b7cf8f62a0d379da`
|
|
137
|
-
|
|
138
|
-
**Test Verification**:
|
|
139
|
-
|
|
140
|
-
```bash
|
|
141
|
-
# Comprehensive verification command
|
|
142
|
-
node tests/debug-slothlet.mjs
|
|
143
|
-
# Look for: "bound.math.add(2, 3) 5" and "bound.string.upper('abc') ABC"
|
|
144
|
-
# Confirms flattening works: api.math.add (not api.math.math.add)
|
|
145
|
-
|
|
146
|
-
# Quick verification command
|
|
147
|
-
node -e "(async () => { const api = await (await import('./index.mjs')).default({ dir: './api_tests/api_test' }); console.log('math.add:', api.math.add(2,3)); console.log('math.math exists:', 'math' in api.math); })()"
|
|
148
|
-
# Expected: math.add: 5, math.math exists: false
|
|
149
|
-
```
|
|
131
|
+
**Source Code Location**: `src/lib/helpers/api_builder/decisions.mjs` - `getFlatteningDecision()`
|
|
150
132
|
|
|
151
133
|
---
|
|
152
134
|
|
|
153
135
|
## Rule 2: Named-Only Export Collection
|
|
154
136
|
|
|
155
137
|
**Category**: Export Handling
|
|
156
|
-
**Status**: ✅
|
|
157
|
-
**User Guide**: [
|
|
158
|
-
**Technical**: [
|
|
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)
|
|
159
141
|
|
|
160
142
|
**Condition**: Directory contains files with only named exports (no default exports)
|
|
161
|
-
**Behavior**: All named exports collected and made accessible at appropriate namespace level
|
|
162
|
-
**Source Files**: Multiple test files with named-only exports
|
|
143
|
+
**Behavior**: All named exports collected and made accessible at the appropriate namespace level
|
|
163
144
|
|
|
164
145
|
**Verified Examples**:
|
|
165
146
|
|
|
@@ -172,167 +153,104 @@ export const E = 2.71828;
|
|
|
172
153
|
export const SUCCESS = "Operation completed";
|
|
173
154
|
export const ERROR = "Operation failed";
|
|
174
155
|
|
|
175
|
-
//
|
|
176
|
-
api.constants.values.
|
|
177
|
-
api.constants.
|
|
178
|
-
api.constants.messages.
|
|
179
|
-
api.constants.messages.ERROR; // ✅ "Operation failed"
|
|
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"
|
|
180
160
|
```
|
|
181
161
|
|
|
182
162
|
**Technical Implementation**:
|
|
183
163
|
|
|
184
|
-
- **Detection**: [C15](API-RULES-CONDITIONS
|
|
185
|
-
- **Processing**: [C09d](API-RULES-CONDITIONS
|
|
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
|
|
186
166
|
- **Strategy**: `processingStrategy = "named-only"` → category-level collection
|
|
187
167
|
|
|
188
|
-
**
|
|
168
|
+
**Key Behavior**:
|
|
189
169
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
// Traditional: preserve as namespace
|
|
194
|
-
apiAssignments[apiPathKey] = mod;
|
|
195
|
-
namespaced = true;
|
|
196
|
-
}
|
|
197
|
-
```
|
|
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)
|
|
198
173
|
|
|
199
|
-
**Source Code Location**: `src/lib/helpers/api_builder/decisions.mjs` - `processModuleForAPI()`
|
|
200
|
-
**Git Commit Reference**: `a50531d1ba712f0c4efd9ab9b7cf8f62a0d379da`
|
|
174
|
+
**Source Code Location**: `src/lib/helpers/api_builder/decisions.mjs` - `processModuleForAPI()`
|
|
201
175
|
**Processing Path**: Both Root and Subfolder processing via `processModuleForAPI`
|
|
202
176
|
|
|
203
|
-
**Test Verification**:
|
|
204
|
-
|
|
205
|
-
```bash
|
|
206
|
-
# Comprehensive verification
|
|
207
|
-
node tests/debug-slothlet.mjs
|
|
208
|
-
# Look for: bound.config showing object with host, username, etc.
|
|
209
|
-
# Confirms named-only exports become accessible object properties
|
|
210
|
-
|
|
211
|
-
# Quick verification
|
|
212
|
-
node -e "(async () => { const api = await (await import('./index.mjs')).default({ dir: './api_tests/api_test' }); console.log('config.host:', api.config.host); console.log('config object keys:', Object.keys(api.config)); })();"
|
|
213
|
-
# Expected: config.host: https://slothlet, config object keys: [host, username, password, site, secure, verbose]
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
**Key Behavior**:
|
|
217
|
-
|
|
218
|
-
- ✅ Preserves all named export names and values
|
|
219
|
-
- ✅ Maintains clear namespace separation between files
|
|
220
|
-
- ✅ No unwanted flattening of complex named export structures
|
|
221
|
-
- ❌ Does not flatten when multiple named exports exist (prevents naming conflicts)
|
|
222
|
-
|
|
223
177
|
---
|
|
224
178
|
|
|
225
179
|
## Rule 3: Empty Module Handling
|
|
226
180
|
|
|
227
181
|
**Category**: Special Cases
|
|
228
|
-
**Status**: ✅
|
|
229
|
-
**
|
|
230
|
-
**Technical**: [CONDITIONS C10](API-RULES-CONDITIONS-v2.md#c10)
|
|
182
|
+
**Status**: ✅ Verified
|
|
183
|
+
**Technical**: [C10](API-RULES/API-RULES-CONDITIONS.md#c10)
|
|
231
184
|
|
|
232
185
|
**Condition**: Directory contains no loadable module files
|
|
233
|
-
**Behavior**: Graceful handling
|
|
186
|
+
**Behavior**: Graceful handling - creates empty namespace
|
|
234
187
|
**Processing Path**: Early detection in `buildCategoryDecisions()`
|
|
235
188
|
|
|
189
|
+
**Mode Differences**:
|
|
190
|
+
|
|
191
|
+
- **EAGER**: Empty folder → `{}` object (not callable)
|
|
192
|
+
- **LAZY**: Empty folder → lazy proxy that resolves to `{}` when called
|
|
193
|
+
|
|
236
194
|
**Technical Implementation**:
|
|
237
195
|
|
|
238
|
-
- **Detection**: [C10](API-RULES-CONDITIONS
|
|
196
|
+
- **Detection**: [C10](API-RULES/API-RULES-CONDITIONS.md#c10) - `moduleFiles.length === 0`
|
|
239
197
|
- **Strategy**: `processingStrategy = "empty"` → graceful empty handling
|
|
240
|
-
- **Result**: May create empty namespace or skip directory entirely
|
|
241
|
-
|
|
242
|
-
**Complete Source Code Implementation**:
|
|
243
198
|
|
|
244
199
|
```javascript
|
|
245
200
|
// Detection in analyzeDirectoryStructure
|
|
246
201
|
if (moduleFiles.length === 0) {
|
|
247
202
|
processingStrategy = "empty";
|
|
248
|
-
// Handle gracefully - may create empty namespace or skip
|
|
249
203
|
}
|
|
250
204
|
```
|
|
251
205
|
|
|
252
|
-
**Source Code Location**: `src/
|
|
253
|
-
**Git Commit Reference**: `a50531d1ba712f0c4efd9ab9b7cf8f62a0d379da`
|
|
206
|
+
**Source Code Location**: `src/lib/helpers/api_builder/analysis.mjs`
|
|
254
207
|
**Processing Path**: All paths (detected in `analyzeDirectoryStructure`)
|
|
255
208
|
|
|
256
|
-
**Mode Differences**:
|
|
257
|
-
|
|
258
|
-
- **EAGER**: Empty folder → `{}` object (not callable)
|
|
259
|
-
- **LAZY**: Empty folder → lazy function that resolves to `{}` when called
|
|
260
|
-
|
|
261
|
-
**Test Verification**:
|
|
262
|
-
|
|
263
|
-
```bash
|
|
264
|
-
# Test empty folder behavior
|
|
265
|
-
node tests/debug-slothlet.mjs
|
|
266
|
-
# EAGER Mode: "Target is object, not function. Returning object directly." → bound.empty() {}
|
|
267
|
-
# LAZY Mode: "About to call function with args: []" → await bound.empty() {}
|
|
268
|
-
|
|
269
|
-
# Quick verification
|
|
270
|
-
node -e "(async () => { const api = await (await import('./index.mjs')).default({ dir: './api_tests/api_test' }); console.log('empty_folder type:', typeof api.empty_folder); console.log('empty_folder content:', JSON.stringify(api.empty_folder)); })();"
|
|
271
|
-
# Expected: empty_folder type: object, empty_folder content: {}
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
- **Detection**: [C10](API-RULES-CONDITIONS-v2.md#c10) - `moduleFiles.length === 0`
|
|
275
|
-
- **Strategy**: `processingStrategy = "empty"` → graceful empty handling
|
|
276
|
-
- **Result**: May create empty namespace or skip directory entirely
|
|
277
|
-
|
|
278
209
|
---
|
|
279
210
|
|
|
280
211
|
## Rule 4: Named Export with Function Name Preservation
|
|
281
212
|
|
|
282
213
|
**Category**: Export Handling
|
|
283
|
-
**Status**: ✅
|
|
284
|
-
**User Guide**: [
|
|
285
|
-
**Technical**: [
|
|
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)
|
|
286
217
|
|
|
287
|
-
**Condition**: Named export with function name that differs from filename
|
|
288
|
-
**Behavior**: Preserves original function name rather than using filename
|
|
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
|
|
289
220
|
**Priority**: Function names take precedence over filename-based naming
|
|
290
221
|
|
|
291
222
|
**Verified Examples**:
|
|
292
223
|
|
|
293
224
|
```javascript
|
|
294
225
|
// File: auto-ip.mjs
|
|
295
|
-
export function autoIP(
|
|
296
|
-
/* ... */
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// Expected API Structure:
|
|
226
|
+
export function autoIP() { /* ... */ }
|
|
300
227
|
api.autoIP(); // ✅ Function name preserved (not api.autoIp)
|
|
301
|
-
// Named function takes precedence over filename sanitization
|
|
302
228
|
|
|
303
229
|
// File: json-parser.mjs
|
|
304
|
-
export function parseJSON(data) {
|
|
305
|
-
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// Expected API Structure:
|
|
309
|
-
api.parseJSON(data); // ✅ Original casing preserved
|
|
310
|
-
// Function name "parseJSON" wins over "jsonParser" from filename
|
|
230
|
+
export function parseJSON(data) { /* ... */ }
|
|
231
|
+
api.parseJSON(data); // ✅ Original casing preserved (not api.jsonParser)
|
|
311
232
|
```
|
|
312
233
|
|
|
313
|
-
**
|
|
234
|
+
**Function Name Priority**:
|
|
314
235
|
|
|
315
|
-
|
|
316
|
-
-
|
|
317
|
-
- **Strategy**: Original function name preserved over filename-based sanitization
|
|
236
|
+
1. Original function name (if available)
|
|
237
|
+
2. Filename-based sanitization (if no function name)
|
|
318
238
|
|
|
319
|
-
**
|
|
239
|
+
**Technical Implementation**:
|
|
320
240
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
3. ❌ Never modify existing function names
|
|
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
|
|
324
243
|
|
|
325
244
|
---
|
|
326
245
|
|
|
327
246
|
## Rule 5: Multiple Module Default Export Handling
|
|
328
247
|
|
|
329
248
|
**Category**: Export Handling
|
|
330
|
-
**Status**: ✅
|
|
331
|
-
**
|
|
332
|
-
**Technical**: [CONDITIONS C08, C09d](API-RULES-CONDITIONS-v2.md#c08)
|
|
249
|
+
**Status**: ✅ Verified (`api_tests/api_test`)
|
|
250
|
+
**Technical**: [C08, C09d](API-RULES/API-RULES-CONDITIONS.md#c08)
|
|
333
251
|
|
|
334
252
|
**Condition**: Category contains multiple modules with default exports
|
|
335
|
-
**Behavior**: Each module maintains its own namespace with default export accessible
|
|
253
|
+
**Behavior**: Each module maintains its own namespace with the default export accessible directly
|
|
336
254
|
**Processing Path**: Standard namespace preservation (no flattening)
|
|
337
255
|
|
|
338
256
|
**Verified Examples**:
|
|
@@ -344,76 +262,62 @@ export default function validateEmail(email) { /* ... */ }
|
|
|
344
262
|
// File: validators/phone.mjs
|
|
345
263
|
export default function validatePhone(phone) { /* ... */ }
|
|
346
264
|
|
|
347
|
-
//
|
|
348
|
-
api.validators.
|
|
349
|
-
api.validators.phone("+1234567890"); // ✅ Default function accessible
|
|
350
|
-
|
|
351
|
-
// No flattening occurs due to multiple modules
|
|
265
|
+
api.validators.email("test@example.com"); // ✅ Default function callable
|
|
266
|
+
api.validators.phone("+1234567890"); // ✅ Default function callable
|
|
352
267
|
```
|
|
353
268
|
|
|
354
269
|
**Technical Implementation**:
|
|
355
270
|
|
|
356
|
-
- **Detection**: [C08](API-RULES-CONDITIONS
|
|
357
|
-
- **Processing**: [C09d](API-RULES-CONDITIONS
|
|
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
|
|
358
273
|
- **Strategy**: `processingStrategy = "standard"` → no flattening
|
|
359
274
|
|
|
360
275
|
**Key Behavior**:
|
|
361
276
|
|
|
362
|
-
-
|
|
363
|
-
-
|
|
364
|
-
-
|
|
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
|
|
365
280
|
|
|
366
281
|
---
|
|
367
282
|
|
|
368
283
|
## Rule 6: Multiple Module Mixed Exports
|
|
369
284
|
|
|
370
285
|
**Category**: Special Cases
|
|
371
|
-
**Status**: ✅
|
|
372
|
-
**
|
|
373
|
-
**Technical**: [CONDITIONS C14, C09d](API-RULES-CONDITIONS-v2.md#c14)
|
|
286
|
+
**Status**: ✅ Verified (`api_tests/api_test_mixed`)
|
|
287
|
+
**Technical**: [C14, C09d](API-RULES/API-RULES-CONDITIONS.md#c14)
|
|
374
288
|
|
|
375
289
|
**Condition**: Category contains modules with mixed export types (some default, some named-only)
|
|
376
|
-
**Behavior**: Standard namespace processing - each module maintains distinct namespace
|
|
290
|
+
**Behavior**: Standard namespace processing - each module maintains a distinct namespace
|
|
377
291
|
**Processing Path**: Conservative approach to prevent conflicts
|
|
378
292
|
|
|
379
293
|
**Verified Examples**:
|
|
380
294
|
|
|
381
295
|
```javascript
|
|
382
296
|
// File: mixed/calculator.mjs (default export)
|
|
383
|
-
export default function calculate(operation, a, b) {
|
|
384
|
-
/* ... */
|
|
385
|
-
}
|
|
297
|
+
export default function calculate(operation, a, b) { /* ... */ }
|
|
386
298
|
|
|
387
299
|
// File: mixed/constants.mjs (named exports only)
|
|
388
300
|
export const PI = 3.14159;
|
|
389
301
|
export const E = 2.71828;
|
|
390
302
|
|
|
391
|
-
// Expected API Structure:
|
|
392
303
|
api.mixed.calculator("add", 2, 3); // ✅ Default accessible
|
|
393
|
-
api.mixed.constants.PI;
|
|
394
|
-
api.mixed.constants.E;
|
|
304
|
+
api.mixed.constants.PI; // ✅ Named exports accessible
|
|
305
|
+
api.mixed.constants.E; // ✅ Clear namespace separation
|
|
395
306
|
```
|
|
396
307
|
|
|
397
308
|
**Technical Implementation**:
|
|
398
309
|
|
|
399
|
-
- **Detection**: [C14](API-RULES-CONDITIONS
|
|
400
|
-
- **Processing**: [C09d](API-RULES-CONDITIONS
|
|
401
|
-
- **Strategy**: Prevents complex flattening that could cause conflicts
|
|
402
|
-
|
|
403
|
-
**Safety Priority**:
|
|
404
|
-
|
|
405
|
-
- ✅ Predictable structure over aggressive flattening
|
|
406
|
-
- ✅ Clear namespace boundaries
|
|
407
|
-
- ❌ No automatic merging of different export types
|
|
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
|
|
408
312
|
|
|
409
313
|
---
|
|
410
314
|
|
|
411
315
|
## Rule 7: Single Module Named Export Flattening
|
|
412
316
|
|
|
413
317
|
**Category**: Basic Flattening
|
|
414
|
-
**Status**: ✅
|
|
415
|
-
**User Guide**: [
|
|
416
|
-
**Technical**: [
|
|
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)
|
|
417
321
|
|
|
418
322
|
**Condition**: Category has one module file, module has named exports (no default export), filename ≠ category name
|
|
419
323
|
**Source Files**: `api_tests/api_test/config/settings.mjs`
|
|
@@ -427,35 +331,26 @@ export const DATABASE_URL = "mongodb://localhost:27017/testdb";
|
|
|
427
331
|
export const API_PORT = 3000;
|
|
428
332
|
export const DEBUG_MODE = true;
|
|
429
333
|
|
|
430
|
-
//
|
|
431
|
-
api.config.DATABASE_URL
|
|
432
|
-
api.config.
|
|
433
|
-
api.config.
|
|
434
|
-
|
|
435
|
-
// Without Rule 7: api.config.settings.DATABASE_URL ❌ (unnecessary nesting)
|
|
436
|
-
// With Rule 7: api.config.DATABASE_URL ✅ (clean flattening)
|
|
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
|
|
437
339
|
```
|
|
438
340
|
|
|
439
341
|
**Technical Implementation**:
|
|
440
342
|
|
|
441
|
-
- **Primary Condition**: [C06](API-RULES-CONDITIONS
|
|
442
|
-
- **Processing**: [C09b](API-RULES-CONDITIONS
|
|
443
|
-
|
|
444
|
-
**Test Verification**:
|
|
445
|
-
|
|
446
|
-
```bash
|
|
447
|
-
node -e "(async () => { const api = await (await import('./index.mjs')).default({ dir: './api_tests/api_test' }); console.log('config.DATABASE_URL:', api.config.DATABASE_URL); console.log('config.settings exists:', 'settings' in api.config); })()"
|
|
448
|
-
# Expected: config.DATABASE_URL: mongodb://localhost:27017/testdb, config.settings exists: false
|
|
449
|
-
```
|
|
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`
|
|
450
345
|
|
|
451
346
|
---
|
|
452
347
|
|
|
453
348
|
## Rule 8: Single Module Default Export Promotion
|
|
454
349
|
|
|
455
350
|
**Category**: Basic Flattening
|
|
456
|
-
**Status**: ✅
|
|
457
|
-
**User Guide**: [
|
|
458
|
-
**Technical**: [
|
|
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)
|
|
459
354
|
|
|
460
355
|
**Condition**: Category has one module file with a default export
|
|
461
356
|
**Source Files**: `api_tests/api_test/logger.mjs`
|
|
@@ -469,175 +364,137 @@ export default function logger(message) {
|
|
|
469
364
|
console.log(`[LOG] ${message}`);
|
|
470
365
|
}
|
|
471
366
|
|
|
472
|
-
//
|
|
473
|
-
api.logger("Hello World")
|
|
474
|
-
|
|
475
|
-
|
|
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"
|
|
476
371
|
```
|
|
477
372
|
|
|
478
|
-
**
|
|
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:
|
|
479
374
|
|
|
480
|
-
|
|
481
|
-
|
|
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
|
|
380
|
+
```
|
|
482
381
|
|
|
483
|
-
|
|
382
|
+
This pattern applies consistently at root level and category level.
|
|
484
383
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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`
|
|
489
388
|
|
|
490
389
|
---
|
|
491
390
|
|
|
492
391
|
## Rule 9: Function Name Preference Over Sanitization
|
|
493
392
|
|
|
494
393
|
**Category**: Special Cases
|
|
495
|
-
**Status**: ✅
|
|
496
|
-
**User Guide**: [FLATTENING Name Preservation](API-FLATTENING
|
|
497
|
-
**Technical**: [
|
|
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)
|
|
498
397
|
|
|
499
|
-
**Condition**: Exported function has explicit name that differs from sanitized filename
|
|
500
|
-
**Behavior**: Preserve original function name over filename-based API path
|
|
501
|
-
**Source Files**: Test cases with technical function names
|
|
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
|
|
502
400
|
|
|
503
401
|
**Verified Examples**:
|
|
504
402
|
|
|
505
|
-
**Example A: Technical Abbreviations**
|
|
506
|
-
|
|
507
403
|
```javascript
|
|
508
404
|
// File: auto-ip.mjs
|
|
509
|
-
export function autoIP() {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
// Without Rule 9: api.autoIp() ❌ (sanitized filename)
|
|
514
|
-
// With Rule 9: api.autoIP() ✅ (preserves technical abbreviation)
|
|
515
|
-
```
|
|
516
|
-
|
|
517
|
-
**Example B: Protocol Names**
|
|
405
|
+
export function autoIP() { /* Get automatic IP */ }
|
|
406
|
+
// Sanitized filename: "autoIp" ❌
|
|
407
|
+
// Function name: "autoIP" ✅
|
|
518
408
|
|
|
519
|
-
```javascript
|
|
520
409
|
// File: get-http-status.mjs
|
|
521
|
-
export function getHTTPStatus() {
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
// Without Rule 9: api.getHttpStatus() ❌ (loses HTTP casing)
|
|
526
|
-
// With Rule 9: api.getHTTPStatus() ✅ (preserves protocol name)
|
|
527
|
-
```
|
|
410
|
+
export function getHTTPStatus() { /* ... */ }
|
|
411
|
+
// Sanitized filename: "getHttpStatus" ❌
|
|
412
|
+
// Function name: "getHTTPStatus" ✅
|
|
528
413
|
|
|
529
|
-
**Example C: Data Format Names**
|
|
530
|
-
|
|
531
|
-
```javascript
|
|
532
414
|
// File: parse-json.mjs
|
|
533
|
-
export function parseJSON(data) {
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
// Without Rule 9: api.parseJson() ❌ (generic casing)
|
|
538
|
-
// With Rule 9: api.parseJSON() ✅ (preserves JSON format name)
|
|
415
|
+
export function parseJSON(data) { /* ... */ }
|
|
416
|
+
// Sanitized filename: "parseJson" ❌
|
|
417
|
+
// Function name: "parseJSON" ✅
|
|
539
418
|
```
|
|
540
419
|
|
|
541
420
|
**Technical Implementation**:
|
|
542
421
|
|
|
543
|
-
- **Primary Check**: [C16](API-RULES-CONDITIONS
|
|
544
|
-
- **Detailed Check**: [C19](API-RULES-CONDITIONS
|
|
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`
|
|
545
424
|
- **Precedence**: Function name takes precedence over filename in API structure
|
|
546
425
|
|
|
547
|
-
**
|
|
426
|
+
**Common Preserved Patterns**:
|
|
548
427
|
|
|
549
|
-
-
|
|
550
|
-
-
|
|
551
|
-
-
|
|
552
|
-
-
|
|
553
|
-
- ✅ **Camel Case Precision**: Exact developer intent preserved
|
|
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
|
|
554
432
|
|
|
555
433
|
---
|
|
556
434
|
|
|
557
435
|
## Rule 10: Generic Filename Parent-Level Promotion
|
|
558
436
|
|
|
559
437
|
**Category**: Special Cases
|
|
560
|
-
**Status**: ✅
|
|
561
|
-
**User Guide**: [FLATTENING
|
|
562
|
-
**Technical**: [
|
|
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)
|
|
563
441
|
|
|
564
|
-
**Condition**:
|
|
565
|
-
**Behavior**: Generic
|
|
566
|
-
**Source Files**: `api_tests/api_test/nest4/singlefile.mjs`
|
|
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
|
|
567
444
|
|
|
568
445
|
**Verified Examples**:
|
|
569
446
|
|
|
570
447
|
```javascript
|
|
571
448
|
// File: database/main.mjs
|
|
572
|
-
export function connect() {
|
|
573
|
-
|
|
574
|
-
}
|
|
575
|
-
export function query() {
|
|
576
|
-
/* ... */
|
|
577
|
-
}
|
|
449
|
+
export function connect() { /* ... */ }
|
|
450
|
+
export function query() { /* ... */ }
|
|
578
451
|
|
|
579
|
-
// Without Rule 10: api.database.main.connect()
|
|
580
|
-
// With Rule 10:
|
|
452
|
+
// Without Rule 10: api.database.main.connect() ❌ (generic 'main' adds no value)
|
|
453
|
+
// With Rule 10: api.database.connect() ✅ (promoted to parent level)
|
|
581
454
|
|
|
582
455
|
// File: auth/index.mjs
|
|
583
|
-
export function login() {
|
|
584
|
-
|
|
585
|
-
}
|
|
586
|
-
export function logout() {
|
|
587
|
-
/* ... */
|
|
588
|
-
}
|
|
456
|
+
export function login() { /* ... */ }
|
|
457
|
+
export function logout() { /* ... */ }
|
|
589
458
|
|
|
590
|
-
// Without Rule 10: api.auth.index.login()
|
|
591
|
-
// With Rule 10:
|
|
459
|
+
// Without Rule 10: api.auth.index.login() ❌ (generic 'index' is noise)
|
|
460
|
+
// With Rule 10: api.auth.login() ✅ (clean parent-level promotion)
|
|
592
461
|
```
|
|
593
462
|
|
|
594
463
|
**Technical Implementation**:
|
|
595
464
|
|
|
596
|
-
- **Detection**: [C17](API-RULES-CONDITIONS
|
|
597
|
-
- **Promotion**: Content promoted to parent namespace
|
|
598
|
-
- **Transparency**: Generic filename becomes invisible in API structure
|
|
465
|
+
- **Detection**: [C17](API-RULES/API-RULES-CONDITIONS.md#c17) - `isGenericFilename(fileName)`
|
|
466
|
+
- **Promotion**: Content promoted to parent namespace; generic filename becomes invisible
|
|
599
467
|
|
|
600
|
-
**Promotion
|
|
601
|
-
|
|
602
|
-
- ✅ **Meaningful Parent**: Generic content promoted to semantically meaningful parent directory name
|
|
603
|
-
- ✅ **Noise Reduction**: Eliminates generic names that add no semantic value
|
|
604
|
-
- ✅ **Developer Intent**: Preserves intended API structure without implementation details
|
|
605
|
-
- ❌ **Name Collision Risk**: Checked against existing parent namespace properties
|
|
468
|
+
**Note**: Promotion is guarded against name collisions - checked against existing parent namespace properties before promoting.
|
|
606
469
|
|
|
607
470
|
---
|
|
608
471
|
|
|
609
472
|
## Rule 11: AddApi Special File Pattern
|
|
610
473
|
|
|
611
474
|
**Category**: AddApi
|
|
612
|
-
**Status**: ✅
|
|
613
|
-
**User Guide**: [
|
|
614
|
-
**Technical**: [
|
|
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)
|
|
615
478
|
|
|
616
|
-
**Condition**:
|
|
617
|
-
**Behavior**:
|
|
618
|
-
**Processing Path**:
|
|
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`
|
|
619
482
|
|
|
620
|
-
**
|
|
483
|
+
**Verified Example**:
|
|
621
484
|
|
|
622
485
|
```javascript
|
|
623
|
-
// File:
|
|
624
|
-
export function initializePlugin() {
|
|
625
|
-
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
//
|
|
635
|
-
await api.addApi("plugins", "./plugin-folder");
|
|
636
|
-
|
|
637
|
-
// Expected Result:
|
|
638
|
-
api.plugins.initializePlugin(); // ✅ Always flattened (no .addapi. level)
|
|
639
|
-
api.plugins.cleanup(); // ✅ Direct extension of plugins namespace
|
|
640
|
-
api.plugins.configure(); // ✅ Seamless API extension
|
|
486
|
+
// File: plugin-folder/addapi.mjs
|
|
487
|
+
export function initializePlugin() { /* ... */ }
|
|
488
|
+
export function cleanup() { /* ... */ }
|
|
489
|
+
export function configure() { /* ... */ }
|
|
490
|
+
|
|
491
|
+
await api.slothlet.api.add("plugins", "./plugin-folder");
|
|
492
|
+
|
|
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() ❌
|
|
641
498
|
```
|
|
642
499
|
|
|
643
500
|
**Technical Implementation**:
|
|
@@ -648,213 +505,208 @@ if (moduleKeys.includes("addapi")) {
|
|
|
648
505
|
const addapiModule = newModules["addapi"];
|
|
649
506
|
const otherModules = { ...newModules };
|
|
650
507
|
delete otherModules["addapi"];
|
|
651
|
-
|
|
652
|
-
// Always flatten addapi contents
|
|
653
508
|
modulesToMerge = { ...addapiModule, ...otherModules };
|
|
654
509
|
}
|
|
655
510
|
```
|
|
656
511
|
|
|
657
512
|
**Use Cases**:
|
|
658
513
|
|
|
659
|
-
-
|
|
660
|
-
-
|
|
661
|
-
-
|
|
662
|
-
- 🎯 **Targeted Integration**: Specific API namespace enhancement
|
|
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
|
|
663
517
|
|
|
664
518
|
---
|
|
665
519
|
|
|
666
520
|
## Rule 12: Module Ownership and Selective API Overwriting
|
|
667
521
|
|
|
668
522
|
**Category**: AddApi
|
|
669
|
-
**Status**: ✅
|
|
670
|
-
**User Guide**: [
|
|
671
|
-
**Technical**: [
|
|
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)
|
|
672
526
|
|
|
673
|
-
**Purpose**:
|
|
674
|
-
**Implementation**: Full ownership tracking system with Map-based validation and moduleId-based protection
|
|
675
|
-
**Source Code**: `src/slothlet.mjs` (ownership tracking), `src/lib/helpers/api_builder/add_api.mjs` (validation), `tests/test-rule-12.mjs` (test suite)
|
|
527
|
+
**Purpose**: Track which module registered each API path, enabling safe hot-reloading and cross-module conflict protection.
|
|
676
528
|
|
|
677
|
-
**
|
|
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.
|
|
530
|
+
|
|
531
|
+
### Configuration
|
|
678
532
|
|
|
679
533
|
```javascript
|
|
680
534
|
const api = await slothlet({
|
|
681
535
|
dir: "./api",
|
|
682
|
-
|
|
683
|
-
|
|
536
|
+
api: {
|
|
537
|
+
collision: {
|
|
538
|
+
initial: "merge", // During initial API build
|
|
539
|
+
api: "replace" // During api.slothlet.api.add()
|
|
540
|
+
}
|
|
541
|
+
}
|
|
684
542
|
});
|
|
685
543
|
```
|
|
686
544
|
|
|
687
|
-
|
|
545
|
+
### moduleId Tracking
|
|
546
|
+
|
|
547
|
+
Each `api.slothlet.api.add()` call accepts an optional `moduleId`. This is the key for ownership tracking:
|
|
688
548
|
|
|
689
549
|
```javascript
|
|
690
|
-
// Module A registers plugins
|
|
691
|
-
await api.
|
|
692
|
-
"
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
);
|
|
711
|
-
|
|
712
|
-
// Hot-reload Module A - only affects APIs it owns
|
|
713
|
-
await api.addApi(
|
|
714
|
-
"plugins.moduleA",
|
|
715
|
-
"./modules/moduleA-v2",
|
|
716
|
-
{},
|
|
717
|
-
{
|
|
718
|
-
moduleId: "moduleA",
|
|
719
|
-
forceOverwrite: true // ✅ Allowed - moduleA owns these APIs
|
|
720
|
-
}
|
|
721
|
-
);
|
|
722
|
-
|
|
723
|
-
// Cross-module overwrite protection
|
|
724
|
-
await api.addApi(
|
|
725
|
-
"plugins.moduleB",
|
|
726
|
-
"./modules/malicious",
|
|
727
|
-
{},
|
|
728
|
-
{
|
|
729
|
-
moduleId: "moduleA", // ❌ Error - moduleA cannot overwrite moduleB's APIs
|
|
730
|
-
forceOverwrite: true
|
|
731
|
-
}
|
|
732
|
-
);
|
|
550
|
+
// Module A registers plugins namespace
|
|
551
|
+
await api.slothlet.api.add("plugins.moduleA", "./modules/moduleA", {}, {
|
|
552
|
+
moduleId: "moduleA"
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
// Module B registers in the same parent namespace
|
|
556
|
+
await api.slothlet.api.add("plugins.moduleB", "./modules/moduleB", {}, {
|
|
557
|
+
moduleId: "moduleB"
|
|
558
|
+
});
|
|
559
|
+
|
|
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
|
+
});
|
|
565
|
+
|
|
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
|
+
});
|
|
733
571
|
```
|
|
734
572
|
|
|
735
|
-
|
|
573
|
+
### Ownership Stack
|
|
574
|
+
|
|
575
|
+
Each API path has a history stack. When a module is removed, the previous owner is automatically restored:
|
|
736
576
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
577
|
+
```javascript
|
|
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)
|
|
581
|
+
```
|
|
741
582
|
|
|
742
|
-
|
|
583
|
+
### Collision Modes
|
|
743
584
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
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 |
|
|
747
593
|
|
|
748
|
-
|
|
594
|
+
### forceOverwrite
|
|
749
595
|
|
|
750
|
-
|
|
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:
|
|
751
597
|
|
|
752
598
|
```javascript
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
throw new Error(`Cannot overwrite API owned by "${existingOwner.moduleId}"`);
|
|
758
|
-
}
|
|
759
|
-
// Allow overwrite - module owns this API
|
|
760
|
-
} else if (!instance.config.allowApiOverwrite) {
|
|
761
|
-
// Fall back to global logic
|
|
762
|
-
console.warn(`Skipping addApi: allowApiOverwrite is false`);
|
|
763
|
-
return;
|
|
764
|
-
}
|
|
599
|
+
await api.slothlet.api.add("config", "./new-config", {}, {
|
|
600
|
+
moduleId: "config-v2",
|
|
601
|
+
forceOverwrite: true
|
|
602
|
+
});
|
|
765
603
|
```
|
|
766
604
|
|
|
767
|
-
**
|
|
605
|
+
**Source Code**: `src/lib/handlers/ownership.mjs`
|
|
768
606
|
|
|
769
|
-
|
|
770
|
-
- **Selective Overwrites**: `forceOverwrite` only works on module's own APIs
|
|
771
|
-
- **Namespace Sharing**: Multiple modules can safely extend same namespace
|
|
772
|
-
- **Performance Conscious**: Only active when `enableModuleOwnership: true`
|
|
773
|
-
- **Precedence Logic**: `forceOverwrite` takes precedence over `allowApiOverwrite`
|
|
607
|
+
---
|
|
774
608
|
|
|
775
|
-
|
|
609
|
+
## Rule 13: AddApi Path Deduplication Flattening
|
|
776
610
|
|
|
777
|
-
|
|
778
|
-
- [ ] Implement `getApiOwnership()` and `registerApiOwnership()` functions
|
|
779
|
-
- [ ] Add ownership validation to `add_api.mjs` conflict resolution
|
|
780
|
-
- [ ] Add corresponding conditions to [API-RULES-CONDITIONS-v2.md](API-RULES-CONDITIONS-v2.md)
|
|
781
|
-
- [ ] Comprehensive testing with multi-module scenarios
|
|
611
|
+
> **New in v3**
|
|
782
612
|
|
|
783
|
-
|
|
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)
|
|
784
617
|
|
|
785
|
-
|
|
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.*`.
|
|
786
619
|
|
|
787
|
-
|
|
788
|
-
| ---- | ---------------------------------------------- | ----------------- | ------------------------------------------- |
|
|
789
|
-
| 1 | Filename Matches Container Flattening | ✅ VERIFIED | api_tests/api_test |
|
|
790
|
-
| 2 | Named-Only Export Collection | ✅ VERIFIED | api_tests/api_test |
|
|
791
|
-
| 3 | Empty Module Handling | ✅ VERIFIED | debug testing |
|
|
792
|
-
| 4 | Default Export Container Pattern | ✅ VERIFIED | api_tests/api_test + api_tv_test |
|
|
793
|
-
| 5 | Multi-Default Export Mixed Pattern | ✅ VERIFIED | api_tests/api_tv_test |
|
|
794
|
-
| 6 | Self-Referential Export Protection | ✅ VERIFIED | api_tests/api_test |
|
|
795
|
-
| 7 | Auto-Flattening Single Named Export | ✅ VERIFIED | api_tests/api_test |
|
|
796
|
-
| 8 | Single-File Auto-Flattening Patterns | ✅ FULLY VERIFIED | Multiple test files |
|
|
797
|
-
| 9 | Function Name Preference Over Sanitization | ✅ FULLY VERIFIED | autoIP, parseJSON, getHTTPStatus, XMLParser |
|
|
798
|
-
| 10 | Generic Filename Parent-Level Promotion | ✅ VERIFIED | nest4/singlefile.mjs |
|
|
799
|
-
| 11 | AddApi Special File Pattern | ✅ VERIFIED | api_tests/api_smart_flatten_addapi |
|
|
800
|
-
| 12 | Module Ownership and Selective API Overwriting | ⚠️ IN DEVELOPMENT | Not yet implemented |
|
|
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.
|
|
801
621
|
|
|
802
|
-
|
|
622
|
+
**Verified Example**:
|
|
803
623
|
|
|
804
|
-
|
|
624
|
+
```javascript
|
|
625
|
+
// Folder structure: api_smart_flatten_folder_config/
|
|
626
|
+
// main.mjs ← exports getRootInfo, setRootConfig
|
|
627
|
+
// config/
|
|
628
|
+
// config.mjs ← exports getNestedConfig, setNestedConfig
|
|
805
629
|
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
| Flattening Rule | API Rules | Technical Conditions |
|
|
809
|
-
| ------------------------------- | -------------------------- | ----------------------------------------------------------- |
|
|
810
|
-
| [F01](API-FLATTENING-v2.md#f01) | Rule 1 | [C05, C09b, C11](API-RULES-CONDITIONS-v2.md#c05) |
|
|
811
|
-
| [F02](API-FLATTENING-v2.md#f02) | Rule 8 (Pattern A) | [C12, C21a](API-RULES-CONDITIONS-v2.md#c12) |
|
|
812
|
-
| [F03](API-FLATTENING-v2.md#f03) | Rule 7 | [C04, C09a, C18, C21c, C30](API-RULES-CONDITIONS-v2.md#c04) |
|
|
813
|
-
| [F04](API-FLATTENING-v2.md#f04) | Rule 4, Rule 8 (Pattern B) | [C08c, C24](API-RULES-CONDITIONS-v2.md#c08c) |
|
|
814
|
-
| [F05](API-FLATTENING-v2.md#f05) | Rule 4, Rule 8 (Pattern C) | [C08c, C11](API-RULES-CONDITIONS-v2.md#c08c) |
|
|
815
|
-
| [F06](API-FLATTENING-v2.md#f06) | Rule 11 | [C33](API-RULES-CONDITIONS-v2.md#c33) |
|
|
816
|
-
| [F07](API-FLATTENING-v2.md#f07) | Rule 12 (planned) | _Implementation pending_ |
|
|
817
|
-
|
|
818
|
-
### By Technical Conditions (C##)
|
|
819
|
-
|
|
820
|
-
| Condition | API Rules | Flattening Rules |
|
|
821
|
-
| ------------------------------------------ | ----------------------------- | ----------------------- |
|
|
822
|
-
| [C01-C07](API-RULES-CONDITIONS-v2.md#c01) | Rules 1, 6, 7, 8 | F01, F03 |
|
|
823
|
-
| [C08-C09d](API-RULES-CONDITIONS-v2.md#c08) | Rules 4, 6, 7 | F04, F05 |
|
|
824
|
-
| [C10-C21d](API-RULES-CONDITIONS-v2.md#c10) | Rules 1, 2, 3, 5, 7, 8, 9, 10 | F01, F02, F03 |
|
|
825
|
-
| [C22-C26](API-RULES-CONDITIONS-v2.md#c22) | Rules 4, 6 | F04, F05 |
|
|
826
|
-
| [C27-C32](API-RULES-CONDITIONS-v2.md#c27) | Rules 5, 6, 7 | Multi-default scenarios |
|
|
827
|
-
| [C33](API-RULES-CONDITIONS-v2.md#c33) | Rule 11 | F06 |
|
|
630
|
+
await api.slothlet.api.add("config", "./api_smart_flatten_folder_config", {});
|
|
828
631
|
|
|
829
|
-
|
|
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
|
|
639
|
+
```
|
|
640
|
+
|
|
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:
|
|
642
|
+
|
|
643
|
+
```javascript
|
|
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 ✅
|
|
650
|
+
```
|
|
830
651
|
|
|
831
|
-
|
|
832
|
-
| --------------------------- | ------------- | ---------------------------------------------- |
|
|
833
|
-
| **Single-File Directories** | 1, 7, 8, 10 | C11, C12, C04, C17 |
|
|
834
|
-
| **Multi-File Directories** | 1, 2, 5, 7, 9 | C13, C15, C21a-d, C16, C19 |
|
|
835
|
-
| **Multi-Default Scenarios** | 5, 6, 7 | C02, C03, C27-C32 |
|
|
836
|
-
| **AddApi Operations** | 11, 12 | C33, [C19-C22](API-RULES-CONDITIONS-v2.md#c19) |
|
|
837
|
-
| **Root-Level Processing** | 4, 8, 10 | C08c, C22, C17 |
|
|
838
|
-
| **Subfolder Processing** | 4, 6, 8 | C08d, C20, C24 |
|
|
652
|
+
**Implementation**: `src/lib/handlers/api-manager.mjs` - immediately after `buildAPI` call, before `setValueAtPath`
|
|
839
653
|
|
|
840
654
|
---
|
|
841
655
|
|
|
842
|
-
##
|
|
656
|
+
## Verification Status
|
|
843
657
|
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
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` |
|
|
849
673
|
|
|
850
|
-
|
|
674
|
+
---
|
|
851
675
|
|
|
852
|
-
-
|
|
853
|
-
|
|
854
|
-
|
|
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 |
|
|
855
702
|
|
|
856
|
-
|
|
703
|
+
### By Processing Context
|
|
857
704
|
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
- **
|
|
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 |
|