@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/AGENT-USAGE.md
CHANGED
|
@@ -2,87 +2,68 @@
|
|
|
2
2
|
|
|
3
3
|
> **Critical**: This guide prevents AI agents from making architectural mistakes when building Slothlet API modules.
|
|
4
4
|
|
|
5
|
-
## 📋
|
|
5
|
+
## 📋 Related Documentation
|
|
6
6
|
|
|
7
|
-
- **[API-RULES.md](
|
|
8
|
-
- **[README.md](
|
|
9
|
-
- **[api_tests
|
|
7
|
+
- **[`docs/API-RULES.md`](docs/API-RULES.md)** - All 13 API transformation rules with verified test examples
|
|
8
|
+
- **[`README.md`](README.md)** - Complete project overview and usage examples
|
|
9
|
+
- **[`api_tests/*/README.md`](api_tests/)** - Live examples demonstrating each pattern below
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
## 🚫 NEVER DO: Cross-Module Imports
|
|
14
14
|
|
|
15
|
-
**The #1 mistake AI agents make with Slothlet**:
|
|
15
|
+
**The #1 mistake AI agents make with Slothlet**: Importing API files from each other.
|
|
16
16
|
|
|
17
17
|
```js
|
|
18
18
|
// ❌ WRONG - Do NOT import API modules from each other
|
|
19
19
|
import { math } from "./math/math.mjs"; // BREAKS SLOTHLET
|
|
20
20
|
import { config } from "../config.mjs"; // BREAKS SLOTHLET
|
|
21
21
|
import { util } from "./util/util.mjs"; // BREAKS SLOTHLET
|
|
22
|
-
|
|
23
|
-
// ❌ WRONG - Do NOT use relative imports between API modules
|
|
24
|
-
import { someFunction } from "../../other-api.mjs"; // BREAKS SLOTHLET
|
|
25
22
|
```
|
|
26
23
|
|
|
27
24
|
**Why this breaks Slothlet**:
|
|
28
25
|
|
|
29
|
-
- Slothlet builds
|
|
26
|
+
- Slothlet builds API structure dynamically at runtime
|
|
30
27
|
- Cross-imports create circular dependencies
|
|
31
28
|
- Breaks lazy loading and context isolation
|
|
32
|
-
- Defeats the purpose of module loading framework
|
|
29
|
+
- Defeats the purpose of the module loading framework
|
|
33
30
|
|
|
34
31
|
## ✅ CORRECT: Use Slothlet's Live-Binding System
|
|
35
32
|
|
|
36
33
|
```js
|
|
37
34
|
// ✅ CORRECT - Import from Slothlet runtime for cross-module access
|
|
38
|
-
import { self, context
|
|
35
|
+
import { self, context } from "@cldmv/slothlet/runtime";
|
|
39
36
|
|
|
40
|
-
// ✅ CORRECT - Access other modules through `self`
|
|
41
37
|
export const myModule = {
|
|
42
38
|
async processData(input) {
|
|
43
|
-
// Access other API modules via `self`
|
|
39
|
+
// Access other API modules via `self` (live binding - always current)
|
|
44
40
|
const mathResult = self.math.add(2, 3);
|
|
45
41
|
const configValue = self.config.get("setting");
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
// context holds the current request/call context
|
|
43
|
+
console.log(`Caller: ${context.userId}`);
|
|
44
|
+
return `Processed: ${input}, Math: ${mathResult}`;
|
|
48
45
|
}
|
|
49
46
|
};
|
|
50
47
|
```
|
|
51
48
|
|
|
52
|
-
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 🏗️ API Module Patterns
|
|
53
52
|
|
|
54
53
|
### Pattern 1: Simple Object Export (Most Common)
|
|
55
54
|
|
|
56
55
|
**File**: `math/math.mjs` → **API**: `api.math.add()`, `api.math.multiply()`
|
|
57
56
|
|
|
58
57
|
```js
|
|
59
|
-
/**
|
|
60
|
-
* @fileoverview Math operations module. Internal file (not exported in package.json).
|
|
61
|
-
* @module api_test.math
|
|
62
|
-
* @memberof module:api_test
|
|
63
|
-
*/
|
|
64
|
-
|
|
65
|
-
// ✅ Import runtime for cross-module access (if needed)
|
|
66
|
-
// import { self, context, reference } from "@cldmv/slothlet/runtime";
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Math operations object accessed as `api.math`.
|
|
70
|
-
* @alias module:api_test.math
|
|
71
|
-
*/
|
|
72
58
|
export const math = {
|
|
73
|
-
add(a, b) {
|
|
74
|
-
|
|
75
|
-
},
|
|
76
|
-
|
|
77
|
-
multiply(a, b) {
|
|
78
|
-
return a * b;
|
|
79
|
-
}
|
|
59
|
+
add(a, b) { return a + b; },
|
|
60
|
+
multiply(a, b) { return a * b; }
|
|
80
61
|
};
|
|
81
62
|
```
|
|
82
63
|
|
|
83
64
|
**Result**: Filename matches folder (`math/math.mjs`) → Auto-flattening → `api.math.add()` (not `api.math.math.add()`)
|
|
84
65
|
|
|
85
|
-
> 📖
|
|
66
|
+
> 📖 See [API-RULES.md Rule 1](docs/API-RULES.md) for flattening details.
|
|
86
67
|
|
|
87
68
|
### Pattern 2: Multiple Files in Folder
|
|
88
69
|
|
|
@@ -90,47 +71,31 @@ export const math = {
|
|
|
90
71
|
|
|
91
72
|
```js
|
|
92
73
|
// File: multi/alpha.mjs
|
|
93
|
-
export const alpha = {
|
|
94
|
-
hello() {
|
|
95
|
-
return "alpha hello";
|
|
96
|
-
}
|
|
97
|
-
};
|
|
74
|
+
export const alpha = { hello() { return "alpha hello"; } };
|
|
98
75
|
|
|
99
76
|
// File: multi/beta.mjs
|
|
100
|
-
export const beta = {
|
|
101
|
-
world() {
|
|
102
|
-
return "beta world";
|
|
103
|
-
}
|
|
104
|
-
};
|
|
77
|
+
export const beta = { world() { return "beta world"; } };
|
|
105
78
|
```
|
|
106
79
|
|
|
107
|
-
**Result**: Different filenames from folder → No flattening → Nested structure preserved
|
|
108
|
-
|
|
109
|
-
> 📖 **See**: [API-RULES.md Rule 2](./API-RULES.md#rule-2-named-only-export-collection) for multi-file folder processing
|
|
80
|
+
**Result**: Different filenames from folder → No flattening → Nested structure preserved.
|
|
110
81
|
|
|
111
82
|
### Pattern 3: Default Function Export
|
|
112
83
|
|
|
113
84
|
**File**: `funcmod/funcmod.mjs` → **API**: `api.funcmod(name)`
|
|
114
85
|
|
|
115
86
|
```js
|
|
116
|
-
/**
|
|
117
|
-
* Default function export accessed as `api.funcmod()`.
|
|
118
|
-
* @param {string} name - Name to greet
|
|
119
|
-
* @returns {string} Greeting message
|
|
120
|
-
*/
|
|
121
87
|
export default function funcmod(name) {
|
|
122
88
|
return `Hello, ${name}!`;
|
|
123
89
|
}
|
|
124
90
|
```
|
|
125
91
|
|
|
126
|
-
**Result**: Filename matches folder + default export → Function flattened to `api.funcmod()
|
|
92
|
+
**Result**: Filename matches folder + default export → Function flattened to `api.funcmod()`.
|
|
127
93
|
|
|
128
94
|
### Pattern 4: Root-Level API Functions
|
|
129
95
|
|
|
130
96
|
**File**: `root-function.mjs` → **API**: `api(name)` + `api.rootFunctionShout()`
|
|
131
97
|
|
|
132
98
|
```js
|
|
133
|
-
// ✅ Root-level file creates top-level API methods
|
|
134
99
|
export default function greet(name) {
|
|
135
100
|
return `Hello, ${name}!`;
|
|
136
101
|
}
|
|
@@ -140,283 +105,358 @@ export function rootFunctionShout(message) {
|
|
|
140
105
|
}
|
|
141
106
|
```
|
|
142
107
|
|
|
143
|
-
**Result**: Root file with default export → `api()` callable + named exports as `api.methodName()
|
|
144
|
-
|
|
145
|
-
> 📖 **See**: [API-RULES.md Rule 4](./API-RULES.md#rule-4-default-export-container-pattern) for root-level default export handling
|
|
108
|
+
**Result**: Root file with default export → `api()` callable + named exports as top-level `api.methodName()`.
|
|
146
109
|
|
|
147
110
|
### Pattern 5: AddApi Special File Pattern (Rule 11)
|
|
148
111
|
|
|
149
|
-
|
|
112
|
+
Files named `addapi.mjs` always flatten regardless of `autoFlatten` setting:
|
|
150
113
|
|
|
151
114
|
```js
|
|
152
115
|
// File: plugins/addapi.mjs
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
*/
|
|
157
|
-
export function initializePlugin() {
|
|
158
|
-
return "Plugin initialized";
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export function cleanup() {
|
|
162
|
-
return "Plugin cleaned up";
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
export function configure(options) {
|
|
166
|
-
return `Configured with ${options}`;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Usage:
|
|
170
|
-
await api.addApi("plugins", "./plugins-folder");
|
|
116
|
+
export function initializePlugin() { return "Plugin initialized"; }
|
|
117
|
+
export function cleanup() { return "Plugin cleaned up"; }
|
|
118
|
+
```
|
|
171
119
|
|
|
172
|
-
|
|
173
|
-
api.
|
|
174
|
-
api.plugins.
|
|
175
|
-
api.plugins.configure(opts); // ✅ Seamless integration
|
|
120
|
+
```js
|
|
121
|
+
await api.slothlet.api.add("plugins", "./plugins-folder");
|
|
122
|
+
api.plugins.initializePlugin(); // ✅ Direct extension - no intermediate namespace
|
|
176
123
|
```
|
|
177
124
|
|
|
178
|
-
|
|
125
|
+
> 📖 See [API-RULES.md Rule 11](docs/API-RULES.md) for addApi flattening details.
|
|
179
126
|
|
|
180
|
-
|
|
127
|
+
---
|
|
181
128
|
|
|
182
|
-
|
|
183
|
-
- 🔄 **Hot Reloading**: Dynamic API updates during development
|
|
184
|
-
- 📦 **Modular Extensions**: Clean extension of existing API surfaces
|
|
129
|
+
## 🔄 Operating Modes
|
|
185
130
|
|
|
186
|
-
|
|
131
|
+
Slothlet supports two loading modes set via `mode:` in the config:
|
|
187
132
|
|
|
188
|
-
|
|
133
|
+
### Eager Mode (default)
|
|
189
134
|
|
|
190
|
-
|
|
135
|
+
All modules are loaded synchronously at `await slothlet(...)`. The API is fully populated before `slothlet()` resolves.
|
|
191
136
|
|
|
192
137
|
```js
|
|
193
|
-
//
|
|
194
|
-
|
|
138
|
+
const api = await slothlet({ dir: "./api" }); // mode: "eager" is default
|
|
139
|
+
// All api.* properties are immediately available
|
|
140
|
+
```
|
|
195
141
|
|
|
196
|
-
|
|
197
|
-
async testCrossCall(a, b) {
|
|
198
|
-
console.log(`ESM Context: User=${context.user}`);
|
|
142
|
+
### Lazy Mode
|
|
199
143
|
|
|
200
|
-
|
|
201
|
-
if (self?.mathCjs?.multiply) {
|
|
202
|
-
const result = self.mathCjs.multiply(a, b);
|
|
203
|
-
return result;
|
|
204
|
-
}
|
|
144
|
+
Modules are loaded on first access via transparent proxy. `slothlet()` resolves immediately without loading any files.
|
|
205
145
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
146
|
+
```js
|
|
147
|
+
const api = await slothlet({
|
|
148
|
+
dir: "./api",
|
|
149
|
+
mode: "lazy"
|
|
150
|
+
});
|
|
151
|
+
// api.math is a proxy - file not loaded yet
|
|
152
|
+
const result = api.math.add(2, 3); // First access triggers load
|
|
209
153
|
```
|
|
210
154
|
|
|
211
|
-
|
|
155
|
+
#### Background Materialization
|
|
156
|
+
|
|
157
|
+
Enable `backgroundMaterialize: true` to pre-load all modules in the background immediately after init (still non-blocking):
|
|
212
158
|
|
|
213
159
|
```js
|
|
214
|
-
|
|
215
|
-
const api1 = await slothlet({
|
|
160
|
+
const api = await slothlet({
|
|
216
161
|
dir: "./api",
|
|
217
|
-
|
|
162
|
+
mode: "lazy",
|
|
163
|
+
backgroundMaterialize: true
|
|
218
164
|
});
|
|
219
165
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
166
|
+
// Subscribe to completion event
|
|
167
|
+
api.slothlet.lifecycle.on("materialized:complete", (data) => {
|
|
168
|
+
console.log(`${data.total} modules materialized`);
|
|
223
169
|
});
|
|
224
170
|
|
|
225
|
-
//
|
|
171
|
+
// Or await all modules to be ready
|
|
172
|
+
await api.slothlet.materialize.wait();
|
|
173
|
+
|
|
174
|
+
// Or check current progress
|
|
175
|
+
const stats = api.slothlet.materialize.get();
|
|
176
|
+
// { total, materialized, remaining, percentage }
|
|
226
177
|
```
|
|
227
178
|
|
|
228
|
-
|
|
179
|
+
**Important**: Lazy mode hot reload intentionally restores modules to an unmaterialized state on reload (references are not preserved). Eager mode preserves existing references by merging into the live wrapper.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 🎣 Hook System
|
|
229
184
|
|
|
230
|
-
|
|
185
|
+
Hooks intercept API function calls. They work across all modes. See [`docs/HOOKS.md`](docs/HOOKS.md) for the full reference.
|
|
231
186
|
|
|
232
187
|
### Hook Configuration
|
|
233
188
|
|
|
234
189
|
```js
|
|
235
|
-
//
|
|
236
|
-
const api = await slothlet({
|
|
237
|
-
dir: "./api",
|
|
238
|
-
hooks: true // Enables all hooks with pattern "**"
|
|
239
|
-
});
|
|
190
|
+
// Simple enable (default pattern "**")
|
|
191
|
+
const api = await slothlet({ dir: "./api", hook: true });
|
|
240
192
|
|
|
241
|
-
// Enable with
|
|
193
|
+
// Enable with default pattern filter
|
|
194
|
+
const api = await slothlet({ dir: "./api", hook: "database.*" });
|
|
195
|
+
|
|
196
|
+
// Full configuration
|
|
242
197
|
const api = await slothlet({
|
|
243
198
|
dir: "./api",
|
|
244
|
-
|
|
199
|
+
hook: {
|
|
245
200
|
enabled: true,
|
|
246
201
|
pattern: "**",
|
|
247
|
-
suppressErrors:
|
|
202
|
+
suppressErrors: false // true = errors suppressed (returns undefined instead of throwing)
|
|
248
203
|
}
|
|
249
204
|
});
|
|
250
205
|
```
|
|
251
206
|
|
|
252
207
|
### Hook Types
|
|
253
208
|
|
|
254
|
-
**
|
|
255
|
-
|
|
256
|
-
- **`
|
|
257
|
-
|
|
258
|
-
- Cancel execution (short-circuit) and return custom value
|
|
259
|
-
- Validation and pre-processing
|
|
260
|
-
|
|
261
|
-
- **`after`**: Transform results after execution
|
|
262
|
-
- Transform return values
|
|
263
|
-
- Only runs if function executes
|
|
264
|
-
- Chain transformations
|
|
265
|
-
|
|
266
|
-
- **`always`**: Observe final result (read-only)
|
|
267
|
-
- Always executes (even on short-circuit)
|
|
268
|
-
- Cannot modify result
|
|
269
|
-
- Perfect for logging and metrics
|
|
270
|
-
|
|
271
|
-
- **`error`**: Monitor and handle errors
|
|
272
|
-
- Receives detailed error context
|
|
273
|
-
- Source tracking (before/function/after/always)
|
|
274
|
-
- Error class identification
|
|
209
|
+
- **`before`** - Executes before the function. Can modify arguments or short-circuit. **Must be synchronous.**
|
|
210
|
+
- **`after`** - Executes after successful completion. Can transform the return value.
|
|
211
|
+
- **`always`** - Read-only observer. Always executes (even on short-circuit). Return value ignored.
|
|
212
|
+
- **`error`** - Executes only when an error occurs. Receives error with source tracking.
|
|
275
213
|
|
|
276
214
|
### Basic Hook Usage
|
|
277
215
|
|
|
216
|
+
The `hook.on(typePattern, handler, options)` signature uses `"type:pattern"` as the first argument:
|
|
217
|
+
|
|
278
218
|
```js
|
|
279
219
|
// Before hook - modify arguments
|
|
280
|
-
api.
|
|
281
|
-
"
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
220
|
+
api.slothlet.hook.on(
|
|
221
|
+
"before:math.add",
|
|
222
|
+
({ path, args, ctx }) => {
|
|
223
|
+
return [args[0] * 2, args[1] * 2]; // Return array to replace arguments
|
|
224
|
+
// Return any non-array non-undefined value to short-circuit (skip function)
|
|
225
|
+
// Return undefined to continue with original args
|
|
286
226
|
},
|
|
287
|
-
{
|
|
227
|
+
{ id: "double-args", priority: 100 }
|
|
288
228
|
);
|
|
289
229
|
|
|
290
230
|
// After hook - transform result
|
|
291
|
-
api.
|
|
292
|
-
"
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
return result * 10; // Transform result
|
|
231
|
+
api.slothlet.hook.on(
|
|
232
|
+
"after:math.*",
|
|
233
|
+
({ path, args, result, ctx }) => {
|
|
234
|
+
return result * 10; // Return value to replace result; undefined = no change
|
|
296
235
|
},
|
|
297
|
-
{
|
|
236
|
+
{ id: "scale-result" }
|
|
298
237
|
);
|
|
299
238
|
|
|
300
239
|
// Always hook - observe (read-only)
|
|
301
|
-
api.
|
|
302
|
-
"
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
console.log(
|
|
240
|
+
api.slothlet.hook.on(
|
|
241
|
+
"always:**",
|
|
242
|
+
({ path, result, hasError, errors }) => {
|
|
243
|
+
if (hasError) console.error(`${path} failed:`, errors);
|
|
244
|
+
else console.log(`${path} returned:`, result);
|
|
245
|
+
// Return value is ignored
|
|
306
246
|
},
|
|
307
|
-
{
|
|
247
|
+
{ id: "logger" }
|
|
308
248
|
);
|
|
309
249
|
|
|
310
250
|
// Error hook - monitor failures
|
|
311
|
-
api.
|
|
312
|
-
"error
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
console.error(
|
|
316
|
-
console.error(`Error type: ${errorType}`);
|
|
251
|
+
api.slothlet.hook.on(
|
|
252
|
+
"error:**",
|
|
253
|
+
({ path, error, source }) => {
|
|
254
|
+
// source.type: "before" | "after" | "always" | "function"
|
|
255
|
+
console.error(`Error in ${path} (from ${source.type}):`, error.message);
|
|
317
256
|
},
|
|
318
|
-
{
|
|
257
|
+
{ id: "error-monitor" }
|
|
319
258
|
);
|
|
320
259
|
```
|
|
321
260
|
|
|
322
|
-
###
|
|
261
|
+
### Pattern Matching
|
|
323
262
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
263
|
+
| Syntax | Description | Example |
|
|
264
|
+
|---|---|---|
|
|
265
|
+
| `exact.path` | Exact match | `"before:math.add"` |
|
|
266
|
+
| `namespace.*` | All functions in namespace | `"after:math.*"` |
|
|
267
|
+
| `*.funcName` | Function name across namespaces | `"always:*.add"` |
|
|
268
|
+
| `**` | All functions | `"error:**"` |
|
|
269
|
+
| `{a,b}` | Brace expansion | `"before:{math,utils}.*"` |
|
|
270
|
+
| `!pattern` | Negation | `"before:!internal.*"` |
|
|
327
271
|
|
|
328
|
-
|
|
329
|
-
api.hooks.on("hook2", "before", handler, { pattern: "math.*" });
|
|
272
|
+
### Hook Subsets
|
|
330
273
|
|
|
331
|
-
|
|
332
|
-
api.hooks.on("hook3", "before", handler, { pattern: "*.add" });
|
|
333
|
-
|
|
334
|
-
// All functions
|
|
335
|
-
api.hooks.on("hook4", "before", handler, { pattern: "**" });
|
|
336
|
-
```
|
|
274
|
+
Each hook type has three ordered execution phases:
|
|
337
275
|
|
|
338
|
-
|
|
276
|
+
| Subset | Order | Typical use |
|
|
277
|
+
|---|---|---|
|
|
278
|
+
| `"before"` | First | Auth checks, security validation |
|
|
279
|
+
| `"primary"` | Middle (default) | Main hook logic |
|
|
280
|
+
| `"after"` | Last | Audit trails, cleanup |
|
|
339
281
|
|
|
340
282
|
```js
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
"
|
|
344
|
-
"before",
|
|
345
|
-
({ path, args }) => {
|
|
346
|
-
const key = JSON.stringify({ path, args });
|
|
347
|
-
if (cache.has(key)) {
|
|
348
|
-
return cache.get(key); // Skip function execution
|
|
349
|
-
}
|
|
350
|
-
// Return undefined to continue
|
|
351
|
-
},
|
|
352
|
-
{ pattern: "**", priority: 1000 }
|
|
283
|
+
api.slothlet.hook.on(
|
|
284
|
+
"before:protected.*",
|
|
285
|
+
({ ctx }) => { if (!ctx.user) throw new Error("Unauthorized"); },
|
|
286
|
+
{ id: "auth", subset: "before", priority: 2000 }
|
|
353
287
|
);
|
|
354
288
|
```
|
|
355
289
|
|
|
356
|
-
###
|
|
290
|
+
### Hook Management
|
|
291
|
+
|
|
292
|
+
```js
|
|
293
|
+
// Remove by ID
|
|
294
|
+
api.slothlet.hook.remove({ id: "my-hook" });
|
|
295
|
+
api.slothlet.hook.off("my-hook"); // alias for remove
|
|
296
|
+
|
|
297
|
+
// Remove by filter
|
|
298
|
+
api.slothlet.hook.remove({ type: "before", pattern: "math.*" });
|
|
357
299
|
|
|
358
|
-
|
|
300
|
+
// Remove all
|
|
301
|
+
api.slothlet.hook.clear();
|
|
359
302
|
|
|
360
|
-
|
|
303
|
+
// List hooks
|
|
304
|
+
const all = api.slothlet.hook.list();
|
|
305
|
+
const active = api.slothlet.hook.list({ enabled: true });
|
|
361
306
|
|
|
362
|
-
|
|
307
|
+
// Enable / disable without unregistering
|
|
308
|
+
api.slothlet.hook.disable(); // disable all
|
|
309
|
+
api.slothlet.hook.disable({ pattern: "math.*" });
|
|
310
|
+
api.slothlet.hook.enable(); // re-enable all
|
|
311
|
+
api.slothlet.hook.enable({ type: "before" });
|
|
312
|
+
```
|
|
363
313
|
|
|
364
|
-
|
|
365
|
-
- Application crashes on uncaught errors
|
|
314
|
+
---
|
|
366
315
|
|
|
367
|
-
|
|
316
|
+
## 🔄 Per-Request Context
|
|
368
317
|
|
|
369
|
-
|
|
370
|
-
- Function returns `undefined` instead of throwing
|
|
371
|
-
- All hook errors suppressed (before, after, always)
|
|
372
|
-
- Perfect for resilient systems with monitoring
|
|
318
|
+
Execute functions with temporary merged context using `api.slothlet.context`:
|
|
373
319
|
|
|
374
320
|
```js
|
|
375
321
|
const api = await slothlet({
|
|
376
322
|
dir: "./api",
|
|
377
|
-
|
|
378
|
-
enabled: true,
|
|
379
|
-
suppressErrors: true // Suppress all errors
|
|
380
|
-
}
|
|
323
|
+
context: { appName: "MyApp", version: "3.0" }
|
|
381
324
|
});
|
|
382
325
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
(
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
326
|
+
// run() - execute a function inside a scoped context
|
|
327
|
+
await api.slothlet.context.run({ userId: "alice", role: "admin" }, async () => {
|
|
328
|
+
// Inside this scope: context = { appName, version, userId, role }
|
|
329
|
+
await api.database.query();
|
|
330
|
+
await api.audit.log();
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
// scope() - return a new API object with merged context
|
|
334
|
+
const scopedApi = api.slothlet.context.scope({ userId: "bob" });
|
|
335
|
+
await scopedApi.database.query(); // context includes userId: "bob"
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Deep Merge Strategy
|
|
339
|
+
|
|
340
|
+
```js
|
|
341
|
+
// Default: shallow merge (top-level properties replaced)
|
|
342
|
+
await api.slothlet.context.run({ newProp: "value" }, handler);
|
|
343
|
+
|
|
344
|
+
// Deep merge: nested objects recursively merged
|
|
345
|
+
await api.slothlet.context.run(
|
|
346
|
+
{ nested: { prop: "value" } },
|
|
347
|
+
handler,
|
|
348
|
+
{ mergeStrategy: "deep" }
|
|
391
349
|
);
|
|
350
|
+
```
|
|
392
351
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
352
|
+
### Automatic EventEmitter Context Propagation
|
|
353
|
+
|
|
354
|
+
Context propagates automatically through EventEmitter callbacks:
|
|
355
|
+
|
|
356
|
+
```js
|
|
357
|
+
import net from "net";
|
|
358
|
+
import { context } from "@cldmv/slothlet/runtime";
|
|
359
|
+
|
|
360
|
+
export const server = {
|
|
361
|
+
async start() {
|
|
362
|
+
const tcpServer = net.createServer((socket) => {
|
|
363
|
+
// Context automatically available in connection handler
|
|
364
|
+
console.log(`User ${context.userId} connected`);
|
|
365
|
+
|
|
366
|
+
socket.on("data", (data) => {
|
|
367
|
+
// Context preserved in nested event callbacks
|
|
368
|
+
console.log(`Data from ${context.userId}: ${data}`);
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
tcpServer.listen(3000);
|
|
372
|
+
}
|
|
373
|
+
};
|
|
398
374
|
```
|
|
399
375
|
|
|
400
|
-
|
|
376
|
+
Works with: TCP servers, HTTP servers, custom EventEmitters, unlimited nested callbacks.
|
|
377
|
+
|
|
378
|
+
> 📖 See [`docs/CONTEXT-PROPAGATION.md`](docs/CONTEXT-PROPAGATION.md) for full documentation.
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
## 🏷️ Metadata System
|
|
383
|
+
|
|
384
|
+
Tag API paths with metadata for authorization, auditing, and security. See [`docs/METADATA.md`](docs/METADATA.md) for the full reference.
|
|
385
|
+
|
|
386
|
+
```js
|
|
387
|
+
// Set metadata when loading (via api.slothlet.api.add)
|
|
388
|
+
await api.slothlet.api.add("plugins/trusted", "./trusted-dir", {
|
|
389
|
+
metadata: { trusted: true, securityLevel: "high" }
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// Set metadata at runtime
|
|
393
|
+
api.slothlet.metadata.set("plugins.trusted.someFunc", { version: 2 });
|
|
394
|
+
api.slothlet.metadata.setGlobal({ environment: "production" });
|
|
395
|
+
api.slothlet.metadata.setFor("plugins/trusted", { owner: "core-team" });
|
|
396
|
+
|
|
397
|
+
// Read metadata inside a module
|
|
398
|
+
import { self } from "@cldmv/slothlet/runtime";
|
|
399
|
+
|
|
400
|
+
export const secureOperation = {
|
|
401
|
+
async execute() {
|
|
402
|
+
// Access metadata via api.slothlet.metadata from within a module via self
|
|
403
|
+
// Or read it externally:
|
|
404
|
+
// const meta = api.slothlet.metadata.get("plugins.trusted.execute");
|
|
405
|
+
return "Authorized execution";
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## 🔁 Hot Reload / Dynamic API Management
|
|
401
413
|
|
|
402
414
|
```js
|
|
403
|
-
//
|
|
404
|
-
|
|
415
|
+
// Add new modules at runtime
|
|
416
|
+
await api.slothlet.api.add("newModule", "./new-module-path");
|
|
417
|
+
await api.slothlet.api.add("plugins", "./plugins", { collision: "merge" });
|
|
418
|
+
|
|
419
|
+
// Remove modules by path
|
|
420
|
+
await api.slothlet.api.remove("oldModule");
|
|
421
|
+
|
|
422
|
+
// Reload all modules
|
|
423
|
+
await api.slothlet.reload();
|
|
424
|
+
|
|
425
|
+
// Reload specific API path
|
|
426
|
+
await api.slothlet.api.reload("database.*");
|
|
427
|
+
await api.slothlet.api.reload("plugins.auth");
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
> **Lazy mode reload behavior**: In lazy mode, reload restores modules to an unmaterialized proxy state - existing references to lazy wrappers are intentionally not preserved. Eager mode merges new module exports into the existing live wrapper, preserving references.
|
|
431
|
+
|
|
432
|
+
> 📖 See [`docs/RELOAD.md`](docs/RELOAD.md) for reload system documentation.
|
|
433
|
+
|
|
434
|
+
---
|
|
405
435
|
|
|
406
|
-
|
|
407
|
-
api.hooks.off(hookId);
|
|
436
|
+
## ⚡ Lifecycle Events
|
|
408
437
|
|
|
409
|
-
|
|
410
|
-
api.hooks.clear();
|
|
438
|
+
Subscribe to internal API events via `api.slothlet.lifecycle`:
|
|
411
439
|
|
|
412
|
-
|
|
413
|
-
|
|
440
|
+
```js
|
|
441
|
+
// Available events
|
|
442
|
+
api.slothlet.lifecycle.on("materialized:complete", (data) => {
|
|
443
|
+
console.log(`${data.total} modules materialized`);
|
|
444
|
+
});
|
|
414
445
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
446
|
+
api.slothlet.lifecycle.on("impl:changed", (data) => {
|
|
447
|
+
console.log(`Module at ${data.apiPath} was reloaded`);
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// Unsubscribe
|
|
451
|
+
const handler = (data) => console.log(data);
|
|
452
|
+
api.slothlet.lifecycle.on("materialized:complete", handler);
|
|
453
|
+
api.slothlet.lifecycle.off("materialized:complete", handler);
|
|
418
454
|
```
|
|
419
455
|
|
|
456
|
+
**Available events**: `"materialized:complete"`, `"impl:created"`, `"impl:changed"`, `"impl:removed"`
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
420
460
|
## 📁 File Organization Best Practices
|
|
421
461
|
|
|
422
462
|
### ✅ Clean Folder Structure
|
|
@@ -425,7 +465,7 @@ api.hooks.enable("database.*"); // Re-enable with new pattern
|
|
|
425
465
|
api/
|
|
426
466
|
├── config.mjs → api.config.*
|
|
427
467
|
├── math/
|
|
428
|
-
│ └── math.mjs → api.math.* (flattened)
|
|
468
|
+
│ └── math.mjs → api.math.* (flattened - filename matches folder)
|
|
429
469
|
├── util/
|
|
430
470
|
│ ├── util.mjs → api.util.* (flattened methods)
|
|
431
471
|
│ ├── extract.mjs → api.util.extract.*
|
|
@@ -438,140 +478,130 @@ api/
|
|
|
438
478
|
└── beta.mjs → api.multi.beta.*
|
|
439
479
|
```
|
|
440
480
|
|
|
441
|
-
### ✅
|
|
481
|
+
### ✅ Naming Conventions
|
|
442
482
|
|
|
443
|
-
- **Filename matches folder** → Auto-flattening (
|
|
483
|
+
- **Filename matches folder** → Auto-flattening (`math/math.mjs` → `api.math.*`)
|
|
444
484
|
- **Different filename** → Nested structure preserved
|
|
445
485
|
- **Dash-separated names** → camelCase API (`auto-ip.mjs` → `api.autoIP`)
|
|
446
|
-
- **Function name
|
|
447
|
-
|
|
448
|
-
## 🧪 JSDoc Documentation Patterns
|
|
449
|
-
|
|
450
|
-
> 📖 **For detailed JSDoc templates and examples**, see [.github/copilot-instructions.md - JSDoc Standards](./.github/copilot-instructions.md#-jsdoc-standards--patterns)
|
|
486
|
+
- **Function name preferred** → Original capitalization kept over sanitized form
|
|
487
|
+
(see [Rule 9](docs/API-RULES.md))
|
|
451
488
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
```js
|
|
455
|
-
/**
|
|
456
|
-
* @fileoverview Math operations for API testing.
|
|
457
|
-
* @module api_test
|
|
458
|
-
* @name api_test
|
|
459
|
-
* @alias @cldmv/slothlet/api_tests/api_test
|
|
460
|
-
*/
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
### ✅ Secondary Contributing Files
|
|
464
|
-
|
|
465
|
-
```js
|
|
466
|
-
/**
|
|
467
|
-
* @fileoverview Math utilities. Internal file (not exported in package.json).
|
|
468
|
-
* @module api_test.math
|
|
469
|
-
* @memberof module:api_test
|
|
470
|
-
*/
|
|
471
|
-
```
|
|
472
|
-
|
|
473
|
-
### ✅ Live-Binding Imports Pattern
|
|
474
|
-
|
|
475
|
-
```js
|
|
476
|
-
// ✅ Always include runtime imports (even if commented out for structure)
|
|
477
|
-
// import { self, context, reference } from "@cldmv/slothlet/runtime";
|
|
478
|
-
```
|
|
489
|
+
---
|
|
479
490
|
|
|
480
491
|
## 🚨 Common AI Agent Mistakes
|
|
481
492
|
|
|
482
|
-
> 📖 **For complete technical details on all API transformation rules**, see [API-RULES.md](./API-RULES.md) (778+ lines of verified examples)
|
|
483
|
-
|
|
484
493
|
### ❌ Mistake 1: Cross-Module Imports
|
|
485
494
|
|
|
486
495
|
```js
|
|
487
496
|
// ❌ WRONG
|
|
488
497
|
import { config } from "./config.mjs";
|
|
498
|
+
// ✅ CORRECT
|
|
499
|
+
import { self } from "@cldmv/slothlet/runtime";
|
|
500
|
+
// then: self.config.get(...)
|
|
489
501
|
```
|
|
490
502
|
|
|
491
|
-
### ❌ Mistake 2:
|
|
503
|
+
### ❌ Mistake 2: Using V2 API Surface
|
|
492
504
|
|
|
493
505
|
```js
|
|
494
|
-
// ❌ WRONG -
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
506
|
+
// ❌ WRONG (v2 API - does not exist in v3)
|
|
507
|
+
await api.addApi("plugins", "./dir");
|
|
508
|
+
await api.reloadApi("math.*");
|
|
509
|
+
api.hooks.on("validate", "before", handler, { pattern: "math.*" });
|
|
510
|
+
await api.run({ userId: "alice" }, fn);
|
|
511
|
+
|
|
512
|
+
// ✅ CORRECT (v3 API)
|
|
513
|
+
await api.slothlet.api.add("plugins", "./dir");
|
|
514
|
+
await api.slothlet.api.reload("math.*");
|
|
515
|
+
api.slothlet.hook.on("before:math.*", handler);
|
|
516
|
+
await api.slothlet.context.run({ userId: "alice" }, fn);
|
|
500
517
|
```
|
|
501
518
|
|
|
502
|
-
### ❌ Mistake 3: Wrong
|
|
519
|
+
### ❌ Mistake 3: Wrong Hook Config Key
|
|
503
520
|
|
|
504
521
|
```js
|
|
505
|
-
// ❌ WRONG
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
522
|
+
// ❌ WRONG
|
|
523
|
+
const api = await slothlet({ dir: "./api", hooks: true }); // "hooks" plural
|
|
524
|
+
|
|
525
|
+
// ✅ CORRECT
|
|
526
|
+
const api = await slothlet({ dir: "./api", hook: true }); // "hook" singular
|
|
510
527
|
```
|
|
511
528
|
|
|
512
529
|
### ❌ Mistake 4: Breaking Auto-Flattening
|
|
513
530
|
|
|
514
531
|
```js
|
|
515
|
-
// File: math/calculator.mjs
|
|
516
|
-
export const math = {
|
|
517
|
-
|
|
518
|
-
};
|
|
519
|
-
// Result: api.math.calculator.math.* (nested, not flattened)
|
|
532
|
+
// File: math/calculator.mjs (different name from folder)
|
|
533
|
+
export const math = { /* methods */ };
|
|
534
|
+
// Result: api.math.calculator.math.* ← extra nesting, not flattened
|
|
520
535
|
|
|
521
536
|
// ✅ CORRECT: File math/math.mjs
|
|
522
|
-
export const math = {
|
|
523
|
-
|
|
524
|
-
};
|
|
525
|
-
// Result: api.math.* (flattened)
|
|
537
|
+
export const math = { /* methods */ };
|
|
538
|
+
// Result: api.math.* ← flattened
|
|
526
539
|
```
|
|
527
540
|
|
|
528
|
-
|
|
541
|
+
### ❌ Mistake 5: Using lifecycle.subscribe / lifecycle.emit
|
|
542
|
+
|
|
543
|
+
```js
|
|
544
|
+
// ❌ WRONG - subscribe/emit are internal
|
|
545
|
+
api.slothlet.lifecycle.subscribe("materialized:complete", handler);
|
|
546
|
+
api.slothlet.lifecycle.emit("impl:changed", data);
|
|
547
|
+
|
|
548
|
+
// ✅ CORRECT - public surface is on/off only
|
|
549
|
+
api.slothlet.lifecycle.on("materialized:complete", handler);
|
|
550
|
+
api.slothlet.lifecycle.off("materialized:complete", handler);
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
---
|
|
529
554
|
|
|
530
|
-
|
|
555
|
+
## ✅ AI Agent Checklist
|
|
531
556
|
|
|
532
|
-
- [ ] **
|
|
533
|
-
- [ ] **Import runtime** - `import { self, context, reference } from "@cldmv/slothlet/runtime"`
|
|
557
|
+
- [ ] **No cross-module imports** - use `self` from `@cldmv/slothlet/runtime` instead
|
|
534
558
|
- [ ] **Match filename to folder** for cleaner APIs (auto-flattening)
|
|
535
|
-
- [ ] **
|
|
536
|
-
- [ ] **
|
|
537
|
-
- [ ] **
|
|
538
|
-
- [ ] **
|
|
539
|
-
- [ ] **
|
|
559
|
+
- [ ] **Hook config key is `hook:` (singular)**, not `hooks:`
|
|
560
|
+
- [ ] **Hook API** is `api.slothlet.hook.*`, not `api.hooks.*`
|
|
561
|
+
- [ ] **Context API** is `api.slothlet.context.run/scope()`, not `api.run/scope()`
|
|
562
|
+
- [ ] **Reload/add/remove** is `api.slothlet.api.add/remove/reload()`, not `api.addApi()` etc.
|
|
563
|
+
- [ ] **Lifecycle** uses `api.slothlet.lifecycle.on/off()` only
|
|
564
|
+
- [ ] **Lazy mode**: if using background materialization, use `api.slothlet.materialize.wait()` before accessing the API
|
|
565
|
+
- [ ] **Hook subsets**: auth/security → `subset: "before"`, main logic → `"primary"`, audit → `"after"`
|
|
566
|
+
- [ ] **Double quotes everywhere** - follow Slothlet coding standards
|
|
567
|
+
|
|
568
|
+
---
|
|
540
569
|
|
|
541
570
|
## 📚 Reference Examples
|
|
542
571
|
|
|
543
572
|
- **Auto-flattening**: `api_tests/api_test/math/math.mjs`
|
|
544
573
|
- **Multi-file folders**: `api_tests/api_test/multi/`
|
|
545
|
-
- **Cross-module calls**: `api_tests/api_test_mixed
|
|
574
|
+
- **Cross-module calls**: `api_tests/api_test_mixed/`
|
|
546
575
|
- **Root-level APIs**: `api_tests/api_test/root-function.mjs`
|
|
547
576
|
- **Nested structures**: `api_tests/api_test/nested/date/`
|
|
548
577
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
### 🏗️ **Core Architecture & Patterns**
|
|
552
|
-
|
|
553
|
-
- **[`API-RULES.md`](./API-RULES.md)** - **CRITICAL** - Comprehensive verified rules for API transformation (778+ lines of verified examples)
|
|
554
|
-
- **[`API-RULES-CONDITIONS.md`](./API-RULES-CONDITIONS.md)** - Technical reference for all conditional logic controlling API generation
|
|
555
|
-
|
|
556
|
-
### � **Usage & Installation**
|
|
557
|
-
|
|
558
|
-
- **[`README.md`](./README.md)** - Complete project overview, installation, and usage examples
|
|
578
|
+
---
|
|
559
579
|
|
|
560
|
-
|
|
580
|
+
## 📖 Essential Documentation
|
|
561
581
|
|
|
562
|
-
|
|
563
|
-
- **[`api_tests/api_test_cjs/README.md`](./api_tests/api_test_cjs/)** - CommonJS module patterns and interoperability
|
|
564
|
-
- **[`api_tests/api_test_mixed/README.md`](./api_tests/api_test_mixed/)** - Mixed ESM/CJS patterns and live-binding examples
|
|
582
|
+
### Core Architecture
|
|
565
583
|
|
|
566
|
-
|
|
584
|
+
- **[`docs/API-RULES.md`](docs/API-RULES.md)** - All 13 API transformation rules
|
|
585
|
+
- **[`docs/API-RULES/API-RULES-CONDITIONS.md`](docs/API-RULES/API-RULES-CONDITIONS.md)** - All C01–C34 conditional logic
|
|
586
|
+
- **[`docs/API-RULES/API-FLATTENING.md`](docs/API-RULES/API-FLATTENING.md)** - Flattening rules F01–F08
|
|
567
587
|
|
|
568
|
-
|
|
588
|
+
### Configuration & Features
|
|
569
589
|
|
|
570
|
-
|
|
590
|
+
- **[`docs/CONFIGURATION.md`](docs/CONFIGURATION.md)** - All config options
|
|
591
|
+
- **[`docs/HOOKS.md`](docs/HOOKS.md)** - Hook system (types, subsets, patterns, management)
|
|
592
|
+
- **[`docs/METADATA.md`](docs/METADATA.md)** - Metadata system
|
|
593
|
+
- **[`docs/CONTEXT-PROPAGATION.md`](docs/CONTEXT-PROPAGATION.md)** - Per-request context and EventEmitter propagation
|
|
594
|
+
- **[`docs/RELOAD.md`](docs/RELOAD.md)** - Hot reload and dynamic API management
|
|
595
|
+
- **[`docs/LIFECYCLE.md`](docs/LIFECYCLE.md)** - Lazy mode, materialization, and lifecycle events
|
|
596
|
+
- **[`docs/SANITIZATION.md`](docs/SANITIZATION.md)** - Property name sanitization rules
|
|
597
|
+
- **[`docs/I18N.md`](docs/I18N.md)** - Internationalization and language support
|
|
598
|
+
- **[`docs/PERFORMANCE.md`](docs/PERFORMANCE.md)** - Performance characteristics and benchmarks
|
|
571
599
|
|
|
572
|
-
|
|
573
|
-
2. **[`README.md`](./README.md)** - Complete project context and installation
|
|
574
|
-
3. **[`API-RULES.md`](./API-RULES.md)** - Understand verified API transformation patterns
|
|
575
|
-
4. **[`api_tests/*/README.md`](./api_tests/)** - Live examples of each pattern
|
|
600
|
+
### Critical Reading Order for AI Agents
|
|
576
601
|
|
|
577
|
-
|
|
602
|
+
1. **This file** - Prevents architectural mistakes
|
|
603
|
+
2. **[`README.md`](README.md)** - Project overview and quickstart
|
|
604
|
+
3. **[`docs/API-RULES.md`](docs/API-RULES.md)** - API transformation rules
|
|
605
|
+
4. **[`docs/HOOKS.md`](docs/HOOKS.md)** - Hook system (if needed)
|
|
606
|
+
5. **[`docs/METADATA.md`](docs/METADATA.md)** - Metadata system (if needed)
|
|
607
|
+
6. **[`api_tests/api_test/README.md`](api_tests/api_test/README.md)** - Live examples
|