@cldmv/slothlet 2.8.0 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/AGENT-USAGE.md +1 -1
  2. package/README.md +300 -1557
  3. package/dist/lib/engine/slothlet_child.mjs +1 -1
  4. package/dist/lib/engine/slothlet_engine.mjs +1 -1
  5. package/dist/lib/engine/slothlet_esm.mjs +1 -1
  6. package/dist/lib/engine/slothlet_helpers.mjs +1 -1
  7. package/dist/lib/engine/slothlet_worker.mjs +1 -1
  8. package/dist/lib/helpers/als-eventemitter.mjs +5 -6
  9. package/dist/lib/helpers/api_builder/add_api.mjs +292 -0
  10. package/dist/lib/helpers/api_builder/analysis.mjs +532 -0
  11. package/dist/lib/helpers/api_builder/construction.mjs +457 -0
  12. package/dist/lib/helpers/api_builder/decisions.mjs +737 -0
  13. package/dist/lib/helpers/api_builder/metadata.mjs +248 -0
  14. package/dist/lib/helpers/api_builder.mjs +17 -1568
  15. package/dist/lib/helpers/auto-wrap.mjs +1 -1
  16. package/dist/lib/helpers/hooks.mjs +1 -1
  17. package/dist/lib/helpers/instance-manager.mjs +1 -1
  18. package/dist/lib/helpers/metadata-api.mjs +201 -0
  19. package/dist/lib/helpers/multidefault.mjs +12 -3
  20. package/dist/lib/helpers/resolve-from-caller.mjs +1 -1
  21. package/dist/lib/helpers/sanitize.mjs +1 -1
  22. package/dist/lib/helpers/utilities.mjs +121 -0
  23. package/dist/lib/modes/slothlet_eager.mjs +1 -1
  24. package/dist/lib/modes/slothlet_lazy.mjs +10 -1
  25. package/dist/lib/runtime/runtime-asynclocalstorage.mjs +49 -18
  26. package/dist/lib/runtime/runtime-livebindings.mjs +23 -4
  27. package/dist/lib/runtime/runtime.mjs +15 -4
  28. package/dist/slothlet.mjs +164 -748
  29. package/docs/API-RULES-CONDITIONS.md +508 -0
  30. package/{API-RULES.md → docs/API-RULES.md} +127 -72
  31. package/package.json +11 -9
  32. package/types/dist/lib/helpers/als-eventemitter.d.mts.map +1 -1
  33. package/types/dist/lib/helpers/api_builder/add_api.d.mts +76 -0
  34. package/types/dist/lib/helpers/api_builder/add_api.d.mts.map +1 -0
  35. package/types/dist/lib/helpers/api_builder/analysis.d.mts +189 -0
  36. package/types/dist/lib/helpers/api_builder/analysis.d.mts.map +1 -0
  37. package/types/dist/lib/helpers/api_builder/construction.d.mts +107 -0
  38. package/types/dist/lib/helpers/api_builder/construction.d.mts.map +1 -0
  39. package/types/dist/lib/helpers/api_builder/decisions.d.mts +213 -0
  40. package/types/dist/lib/helpers/api_builder/decisions.d.mts.map +1 -0
  41. package/types/dist/lib/helpers/api_builder/metadata.d.mts +99 -0
  42. package/types/dist/lib/helpers/api_builder/metadata.d.mts.map +1 -0
  43. package/types/dist/lib/helpers/api_builder.d.mts +5 -448
  44. package/types/dist/lib/helpers/api_builder.d.mts.map +1 -1
  45. package/types/dist/lib/helpers/metadata-api.d.mts +132 -0
  46. package/types/dist/lib/helpers/metadata-api.d.mts.map +1 -0
  47. package/types/dist/lib/helpers/multidefault.d.mts.map +1 -1
  48. package/types/dist/lib/helpers/utilities.d.mts +120 -0
  49. package/types/dist/lib/helpers/utilities.d.mts.map +1 -0
  50. package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts +9 -0
  51. package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts.map +1 -1
  52. package/types/dist/lib/runtime/runtime-livebindings.d.mts +10 -0
  53. package/types/dist/lib/runtime/runtime-livebindings.d.mts.map +1 -1
  54. package/types/dist/lib/runtime/runtime.d.mts +1 -0
  55. package/types/dist/lib/runtime/runtime.d.mts.map +1 -1
  56. package/types/dist/slothlet.d.mts +0 -11
  57. package/types/dist/slothlet.d.mts.map +1 -1
  58. package/types/index.d.mts +0 -1
  59. package/API-RULES-CONDITIONS.md +0 -367
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Helper function to check if a value is likely serializable without calling JSON.stringify.
3
+ * Used by toJSON methods to avoid expensive serialization attempts on complex objects.
4
+ *
5
+ * @internal
6
+ * @private
7
+ * @param {*} val - The value to check for serializability
8
+ * @returns {boolean} True if the value is likely serializable, false otherwise
9
+ */
10
+ export function isLikelySerializable(val: any): boolean;
11
+ /**
12
+ * Analyzes a module and returns processing decisions that both eager and lazy modes can use.
13
+ * This centralizes the module loading logic from _loadSingleModule while allowing each mode
14
+ * to handle the results according to their strategy (immediate materialization vs proxy creation).
15
+ *
16
+ * @function analyzeModule
17
+ * @internal
18
+ * @package
19
+ * @param {string} modulePath - Absolute path to the module file
20
+ * @param {object} options - Analysis options
21
+ * @param {boolean} [options.debug=false] - Enable debug logging
22
+ * @param {object} [options.instance] - Slothlet instance for accessing config and methods
23
+ * @returns {Promise<{
24
+ * rawModule: object,
25
+ * processedModule: object,
26
+ * isFunction: boolean,
27
+ * hasDefault: boolean,
28
+ * isCjs: boolean,
29
+ * exports: Array<[string, any]>,
30
+ * defaultExportType: 'function'|'object'|null,
31
+ * shouldWrapAsCallable: boolean,
32
+ * namedExports: object,
33
+ * metadata: object
34
+ * }>} Module analysis results
35
+ *
36
+ * @example
37
+ * // Analyze a module file
38
+ * const analysis = await analyzeModule("./api/math.mjs", { instance });
39
+ * // Eager mode: use analysis.processedModule directly
40
+ * // Lazy mode: create proxy based on analysis.isFunction, analysis.exports, etc.
41
+ */
42
+ export function analyzeModule(modulePath: string, options?: {
43
+ debug?: boolean;
44
+ instance?: object;
45
+ }): Promise<{
46
+ rawModule: object;
47
+ processedModule: object;
48
+ isFunction: boolean;
49
+ hasDefault: boolean;
50
+ isCjs: boolean;
51
+ exports: Array<[string, any]>;
52
+ defaultExportType: "function" | "object" | null;
53
+ shouldWrapAsCallable: boolean;
54
+ namedExports: object;
55
+ metadata: object;
56
+ }>;
57
+ /**
58
+ * Processes module analysis results into a final module object using slothlet's established patterns.
59
+ * This centralizes the processing logic while allowing both modes to apply the results differently.
60
+ *
61
+ * @function processModuleFromAnalysis
62
+ * @internal
63
+ * @package
64
+ * @param {object} analysis - Results from analyzeModule
65
+ * @param {object} options - Processing options
66
+ * @param {object} [options.instance] - Slothlet instance for accessing _toapiPathKey method
67
+ * @param {boolean} [options.debug=false] - Enable debug logging
68
+ * @returns {object} Processed module ready for API integration
69
+ *
70
+ * @example
71
+ * // Process analyzed module
72
+ * const analysis = await analyzeModule(modulePath, { instance });
73
+ * const processed = processModuleFromAnalysis(analysis, { instance });
74
+ * // Both modes can use 'processed' but integrate it differently
75
+ */
76
+ export function processModuleFromAnalysis(analysis: object, options?: {
77
+ instance?: object;
78
+ debug?: boolean;
79
+ }): object;
80
+ /**
81
+ * Analyzes a directory and returns structural decisions that both eager and lazy modes can use.
82
+ * This provides the decision-making logic for directory handling without implementing the actual
83
+ * loading strategy (allowing lazy mode to create proxies while eager mode materializes).
84
+ *
85
+ * @function analyzeDirectoryStructure
86
+ * @internal
87
+ * @package
88
+ * @param {string} categoryPath - Absolute path to the directory
89
+ * @param {object} options - Analysis options
90
+ * @param {object} options.instance - Slothlet instance for accessing config and methods
91
+ * @param {number} [options.currentDepth=0] - Current traversal depth
92
+ * @param {number} [options.maxDepth=Infinity] - Maximum traversal depth
93
+ * @param {boolean} [options.debug=false] - Enable debug logging
94
+ * @returns {Promise<{
95
+ * isSingleFile: boolean,
96
+ * shouldAutoFlatten: boolean,
97
+ * categoryName: string,
98
+ * moduleFiles: Array<import('fs').Dirent>,
99
+ * subDirs: Array<import('fs').Dirent>,
100
+ * multiDefaultAnalysis: object,
101
+ * processingStrategy: 'single-file'|'multi-file'|'empty',
102
+ * flatteningHints: object
103
+ * }>} Directory structure analysis
104
+ *
105
+ * @example
106
+ * // Analyze directory structure
107
+ * const analysis = await analyzeDirectoryStructure(categoryPath, { instance });
108
+ * if (analysis.isSingleFile) {
109
+ * // Both modes: handle as single file (but differently)
110
+ * } else {
111
+ * // Both modes: handle as multi-file (but differently)
112
+ * }
113
+ */
114
+ export function analyzeDirectoryStructure(categoryPath: string, options?: {
115
+ instance: object;
116
+ currentDepth?: number;
117
+ maxDepth?: number;
118
+ debug?: boolean;
119
+ }): Promise<{
120
+ isSingleFile: boolean;
121
+ shouldAutoFlatten: boolean;
122
+ categoryName: string;
123
+ moduleFiles: Array<import("fs").Dirent>;
124
+ subDirs: Array<import("fs").Dirent>;
125
+ multiDefaultAnalysis: object;
126
+ processingStrategy: "single-file" | "multi-file" | "empty";
127
+ flatteningHints: object;
128
+ }>;
129
+ /**
130
+ * Returns category building decisions and processed modules that both eager and lazy modes can use.
131
+ * This provides all the structural information needed to build a category but lets each mode
132
+ * implement the actual building strategy (materialization vs proxy creation).
133
+ *
134
+ * @function getCategoryBuildingDecisions
135
+ * @internal
136
+ * @package
137
+ * @param {string} categoryPath - Absolute path to the directory
138
+ * @param {object} options - Building options
139
+ * @param {object} options.instance - Slothlet instance for accessing config and methods
140
+ * @param {number} [options.currentDepth=0] - Current traversal depth
141
+ * @param {number} [options.maxDepth=Infinity] - Maximum traversal depth
142
+ * @param {boolean} [options.debug=false] - Enable debug logging
143
+ * @returns {Promise<{
144
+ * processingStrategy: 'single-file'|'multi-file'|'empty',
145
+ * categoryName: string,
146
+ * shouldFlattenSingle: boolean,
147
+ * processedModules: Array<{file: import('fs').Dirent, moduleName: string, processedModule: any, flattening: object}>,
148
+ * subDirectories: Array<{dirEntry: import('fs').Dirent, apiPathKey: string}>,
149
+ * multiDefaultAnalysis: object,
150
+ * flatteningDecisions: object,
151
+ * upwardFlatteningCandidate: {shouldFlatten: boolean, apiPathKey: string}
152
+ * }>} Complete category building information
153
+ *
154
+ * @example
155
+ * // Get category building decisions
156
+ * const decisions = await getCategoryBuildingDecisions(categoryPath, { instance });
157
+ * if (decisions.processingStrategy === "single-file") {
158
+ * // Both modes: handle single file differently
159
+ * // Eager: return decisions.processedModules[0].processedModule
160
+ * // Lazy: create proxy based on decisions.processedModules[0].flattening
161
+ * }
162
+ */
163
+ export function getCategoryBuildingDecisions(categoryPath: string, options?: {
164
+ instance: object;
165
+ currentDepth?: number;
166
+ maxDepth?: number;
167
+ debug?: boolean;
168
+ }): Promise<{
169
+ processingStrategy: "single-file" | "multi-file" | "empty";
170
+ categoryName: string;
171
+ shouldFlattenSingle: boolean;
172
+ processedModules: Array<{
173
+ file: import("fs").Dirent;
174
+ moduleName: string;
175
+ processedModule: any;
176
+ flattening: object;
177
+ }>;
178
+ subDirectories: Array<{
179
+ dirEntry: import("fs").Dirent;
180
+ apiPathKey: string;
181
+ }>;
182
+ multiDefaultAnalysis: object;
183
+ flatteningDecisions: object;
184
+ upwardFlatteningCandidate: {
185
+ shouldFlatten: boolean;
186
+ apiPathKey: string;
187
+ };
188
+ }>;
189
+ //# sourceMappingURL=analysis.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analysis.d.mts","sourceRoot":"","sources":["../../../../../dist/lib/helpers/api_builder/analysis.mjs"],"names":[],"mappings":"AA0CA;;;;;;;;GAQG;AACH,0CAHW,GAAC,GACC,OAAO,CAcnB;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,0CAvBW,MAAM,YAEd;IAA0B,KAAK,GAAvB,OAAO;IACU,QAAQ,GAAzB,MAAM;CACd,GAAU,OAAO,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;IAC9B,iBAAiB,EAAE,UAAU,GAAC,QAAQ,GAAC,IAAI,CAAC;IAC5C,oBAAoB,EAAE,OAAO,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAC,CA4GJ;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,oDAZW,MAAM,YAEd;IAAyB,QAAQ,GAAzB,MAAM;IACY,KAAK,GAAvB,OAAO;CACf,GAAU,MAAM,CA4NlB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wDA1BW,MAAM,YAEd;IAAwB,QAAQ,EAAxB,MAAM;IACW,YAAY,GAA7B,MAAM;IACW,QAAQ,GAAzB,MAAM;IACY,KAAK,GAAvB,OAAO;CACf,GAAU,OAAO,CAAC;IAChB,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,KAAK,CAAC,OAAO,IAAI,EAAE,MAAM,CAAC,CAAC;IACxC,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,EAAE,MAAM,CAAC,CAAC;IACpC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,kBAAkB,EAAE,aAAa,GAAC,YAAY,GAAC,OAAO,CAAC;IACvD,eAAe,EAAE,MAAM,CAAA;CACxB,CAAC,CAkEJ;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,2DA1BW,MAAM,YAEd;IAAwB,QAAQ,EAAxB,MAAM;IACW,YAAY,GAA7B,MAAM;IACW,QAAQ,GAAzB,MAAM;IACY,KAAK,GAAvB,OAAO;CACf,GAAU,OAAO,CAAC;IAChB,kBAAkB,EAAE,aAAa,GAAC,YAAY,GAAC,OAAO,CAAC;IACvD,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,gBAAgB,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,OAAO,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,GAAG,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC;IACnH,cAAc,EAAE,KAAK,CAAC;QAAC,QAAQ,EAAE,OAAO,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC;IAC3E,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,yBAAyB,EAAE;QAAC,aAAa,EAAE,OAAO,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAC,CAAA;CACxE,CAAC,CAmHJ"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Converts a filename or folder name to camelCase for API property.
3
+ * Extracted from slothlet._toapiPathKey for use in API building functions.
4
+ *
5
+ * @function toapiPathKey
6
+ * @internal
7
+ * @package
8
+ * @param {string} name - The name to convert
9
+ * @param {object} [sanitizeConfig={}] - Sanitization configuration
10
+ * @returns {string} The camelCase version of the name
11
+ *
12
+ * @example
13
+ * toapiPathKey('root-math') // 'rootMath'
14
+ * toapiPathKey('auto-ip') // 'autoIP' (with proper config)
15
+ */
16
+ export function toapiPathKey(name: string, sanitizeConfig?: object): string;
17
+ /**
18
+ * Filters out files that should not be loaded by slothlet.
19
+ * Extracted from slothlet._shouldIncludeFile for use in API building functions.
20
+ *
21
+ * @function shouldIncludeFile
22
+ * @internal
23
+ * @package
24
+ * @param {object} entry - The directory entry to check
25
+ * @returns {boolean} True if the file should be included, false if it should be excluded
26
+ *
27
+ * @example
28
+ * const entries = await fs.readdir(dir, { withFileTypes: true });
29
+ * const moduleFiles = entries.filter(e => shouldIncludeFile(e));
30
+ */
31
+ export function shouldIncludeFile(entry: object): boolean;
32
+ /**
33
+ * Comprehensive category/directory building function that replaces _buildCategory.
34
+ * Handles complete directory structure processing with all flattening rules.
35
+ *
36
+ * @function buildCategoryStructure
37
+ * @internal
38
+ * @package
39
+ * @async
40
+ * @param {string} categoryPath - Absolute path to the category directory
41
+ * @param {object} options - Building options
42
+ * @param {number} [options.currentDepth=0] - Current recursion depth
43
+ * @param {number} [options.maxDepth=Infinity] - Maximum recursion depth
44
+ * @param {string} [options.mode="eager"] - Loading mode ("eager" or "lazy")
45
+ * @param {function} [options.subdirHandler] - Custom subdirectory handler for lazy mode
46
+ * @param {object} options.instance - Slothlet instance for access to helper methods
47
+ * @returns {Promise<object>} Complete category API structure
48
+ *
49
+ * @description
50
+ * Complete directory structure building pipeline that handles:
51
+ * - Single-file vs multi-file directory processing
52
+ * - Auto-flattening decisions for single files matching directory names
53
+ * - Multi-default export detection and processing
54
+ * - Self-referential export handling
55
+ * - Recursive subdirectory traversal with depth control
56
+ * - Function name preference over sanitized names
57
+ * - All established slothlet flattening rules and conventions
58
+ *
59
+ * @example
60
+ * // Internal usage - build complete category structure
61
+ * const categoryApi = await buildCategoryStructure("/path/to/category", {
62
+ * currentDepth: 0, maxDepth: 3, mode: "eager", instance: slothletInstance
63
+ * });
64
+ */
65
+ export function buildCategoryStructure(categoryPath: string, options?: {
66
+ currentDepth?: number;
67
+ maxDepth?: number;
68
+ mode?: string;
69
+ subdirHandler?: Function;
70
+ instance: object;
71
+ }): Promise<object>;
72
+ /**
73
+ * Comprehensive root API building function that replaces eager/lazy create methods.
74
+ * Handles complete root-level API construction with mode-specific optimizations.
75
+ *
76
+ * @function buildRootAPI
77
+ * @internal
78
+ * @package
79
+ * @async
80
+ * @param {string} dir - Root directory path to build API from
81
+ * @param {object} options - Building options
82
+ * @param {boolean} [options.lazy=false] - Whether to use lazy loading mode
83
+ * @param {number} [options.maxDepth=Infinity] - Maximum recursion depth
84
+ * @param {object} options.instance - Slothlet instance for access to helper methods
85
+ * @returns {Promise<object|function>} Complete root API (object or function with properties)
86
+ *
87
+ * @description
88
+ * Complete root API building pipeline that handles:
89
+ * - Root-level module processing with multi-default detection
90
+ * - Root contributor pattern (default function becomes callable API)
91
+ * - Named export merging and flattening decisions
92
+ * - Recursive directory structure building via buildCategoryStructure
93
+ * - Mode-specific optimizations (eager vs lazy)
94
+ * - All established slothlet API construction patterns
95
+ *
96
+ * @example
97
+ * // Internal usage - build complete root API
98
+ * const rootApi = await buildRootAPI("/path/to/api", {
99
+ * lazy: false, maxDepth: 3, instance: slothletInstance
100
+ * });
101
+ */
102
+ export function buildRootAPI(dir: string, options?: {
103
+ lazy?: boolean;
104
+ maxDepth?: number;
105
+ instance: object;
106
+ }): Promise<object | Function>;
107
+ //# sourceMappingURL=construction.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"construction.d.mts","sourceRoot":"","sources":["../../../../../dist/lib/helpers/api_builder/construction.mjs"],"names":[],"mappings":"AA0CA;;;;;;;;;;;;;;GAcG;AACH,mCARW,MAAM,mBACN,MAAM,GACJ,MAAM,CAQlB;AAED;;;;;;;;;;;;;GAaG;AACH,yCAPW,MAAM,GACJ,OAAO,CAgBnB;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,qDAzBW,MAAM,YAEd;IAAyB,YAAY,GAA7B,MAAM;IACW,QAAQ,GAAzB,MAAM;IACW,IAAI,GAArB,MAAM;IACa,aAAa;IAChB,QAAQ,EAAxB,MAAM;CACd,GAAU,OAAO,CAAC,MAAM,CAAC,CAkT3B;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,kCAtBW,MAAM,YAEd;IAA0B,IAAI,GAAtB,OAAO;IACU,QAAQ,GAAzB,MAAM;IACU,QAAQ,EAAxB,MAAM;CACd,GAAU,OAAO,CAAC,MAAM,WAAS,CAAC,CA2HpC"}
@@ -0,0 +1,213 @@
1
+ /**
2
+ * Auto-flattening decision logic that determines whether a module should be flattened
3
+ * based on filename matching, export patterns, and context.
4
+ *
5
+ * @function getFlatteningDecision
6
+ * @internal
7
+ * @package
8
+ * @param {object} options - Flattening analysis options
9
+ * @param {object} options.mod - The loaded module object
10
+ * @param {string} options.fileName - Original filename (without extension)
11
+ * @param {string} options.apiPathKey - Sanitized API key for the module
12
+ * @param {boolean} options.hasMultipleDefaultExports - Whether multiple default exports exist in the container
13
+ * @param {boolean} options.isSelfReferential - Whether this is a self-referential export
14
+ * @param {boolean} [options.moduleHasDefault] - Whether this specific module has a default export.
15
+ * Should use originalAnalysis.hasDefault when available for accuracy, as !!mod.default
16
+ * may be inaccurate after processModuleFromAnalysis modifies module structure.
17
+ * @param {string} [options.categoryName] - Container/category name for context
18
+ * @param {number} [options.totalModules=1] - Total number of modules in container
19
+ * @param {boolean} [options.debug=false] - Enable debug logging
20
+ * @returns {{
21
+ * shouldFlatten: boolean,
22
+ * flattenToRoot: boolean,
23
+ * flattenToCategory: boolean,
24
+ * preserveAsNamespace: boolean,
25
+ * useAutoFlattening: boolean,
26
+ * reason: string
27
+ * }} Flattening decision result
28
+ *
29
+ * @description
30
+ * Determines flattening behavior based on slothlet's established rules:
31
+ *
32
+ * 1. Self-referential exports: Never flatten (preserve as namespace)
33
+ * 2. Multi-default context: Flatten modules WITHOUT defaults, preserve WITH defaults
34
+ * 3. Single named export matching filename: Auto-flatten to use export directly
35
+ * 4. Filename matches container: Flatten contents to container level
36
+ * 5. Traditional context: Preserve as namespace unless auto-flattening applies
37
+ *
38
+ * @example
39
+ * // Internal usage - single named export matching filename
40
+ * const decision = getFlatteningDecision({
41
+ * mod: { math: { add: fn, multiply: fn } },
42
+ * fileName: "math", apiPathKey: "math",
43
+ * hasMultipleDefaultExports: false, isSelfReferential: false
44
+ * });
45
+ * // Returns: { shouldFlatten: true, useAutoFlattening: true, reason: "auto-flatten single named export" }
46
+ */
47
+ export function getFlatteningDecision(options: {
48
+ mod: object;
49
+ fileName: string;
50
+ apiPathKey: string;
51
+ hasMultipleDefaultExports: boolean;
52
+ isSelfReferential: boolean;
53
+ moduleHasDefault?: boolean;
54
+ categoryName?: string;
55
+ totalModules?: number;
56
+ debug?: boolean;
57
+ }): {
58
+ shouldFlatten: boolean;
59
+ flattenToRoot: boolean;
60
+ flattenToCategory: boolean;
61
+ preserveAsNamespace: boolean;
62
+ useAutoFlattening: boolean;
63
+ reason: string;
64
+ };
65
+ /**
66
+ * Handles function name preference logic for better API naming.
67
+ *
68
+ * @function applyFunctionNamePreference
69
+ * @internal
70
+ * @package
71
+ * @param {object} options - Name preference options
72
+ * @param {object} options.mod - The loaded module object
73
+ * @param {string} options.fileName - Original filename (without extension)
74
+ * @param {string} options.apiPathKey - Sanitized API key
75
+ * @param {object} options.categoryModules - Target category modules object
76
+ * @param {function} options.toapiPathKey - Function to sanitize names to API keys
77
+ * @param {boolean} [options.debug=false] - Enable debug logging
78
+ * @returns {{hasPreferredName: boolean, preferredKey: string}} Name preference result
79
+ *
80
+ * @description
81
+ * Implements slothlet's function name preference logic where the original function name
82
+ * is preferred over the sanitized filename when they represent the same semantic meaning
83
+ * but have different capitalization (e.g., autoIP vs autoIp, parseJSON vs parseJson).
84
+ *
85
+ * @example
86
+ * // Internal usage in _buildCategory
87
+ * const preference = applyFunctionNamePreference({
88
+ * mod: { autoIP: function autoIP() {} },
89
+ * fileName: "auto-ip", apiPathKey: "autoIp",
90
+ * categoryModules, toapiPathKey: this._toapiPathKey, debug: true
91
+ * });
92
+ * // Returns: { hasPreferredName: true, preferredKey: "autoIP" }
93
+ */
94
+ export function applyFunctionNamePreference(options: {
95
+ mod: object;
96
+ fileName: string;
97
+ apiPathKey: string;
98
+ categoryModules: object;
99
+ toapiPathKey: Function;
100
+ debug?: boolean;
101
+ }): {
102
+ hasPreferredName: boolean;
103
+ preferredKey: string;
104
+ };
105
+ /**
106
+ * Processes a single module and applies it to the target API object based on flattening decisions.
107
+ *
108
+ * @function processModuleForAPI
109
+ * @internal
110
+ * @package
111
+ * @param {object} options - Module processing options
112
+ * @param {object} options.mod - The loaded module object
113
+ * @param {string} options.fileName - Original filename (without extension)
114
+ * @param {string} options.apiPathKey - Sanitized API key for the module
115
+ * @param {boolean} options.hasMultipleDefaultExports - Whether multiple default exports exist
116
+ * @param {boolean} options.isSelfReferential - Whether this is a self-referential export
117
+ * @param {object} options.api - Target API object to modify (could be root api or categoryModules)
118
+ * @param {function} [options.getRootDefault] - Function to get current root default function
119
+ * @param {function} [options.setRootDefault] - Function to set the root default function
120
+ * @param {object} [options.context] - Processing context
121
+ * @param {boolean} [options.context.debug=false] - Enable debug logging
122
+ * @param {string} [options.context.mode="unknown"] - Processing mode (root, subfolder, eager, lazy)
123
+ * @param {string} [options.context.categoryName] - Container/category name
124
+ * @param {number} [options.context.totalModules=1] - Total modules in container
125
+ * @returns {{
126
+ * processed: boolean,
127
+ * rootDefaultSet: boolean,
128
+ * flattened: boolean,
129
+ * namespaced: boolean,
130
+ * apiAssignments: Record<string, any>
131
+ * }} Processing result
132
+ *
133
+ * @description
134
+ * Unified module processing logic that handles:
135
+ * 1. Function default exports (multi-default, self-referential, traditional root contributor)
136
+ * 2. Object/named exports with flattening decisions
137
+ * 3. Export merging and namespace assignments
138
+ * 4. Function name preference logic
139
+ * 5. Root default function management
140
+ *
141
+ * @example
142
+ * // Internal usage for root-level processing
143
+ * const result = processModuleForAPI({
144
+ * mod, fileName, apiPathKey, hasMultipleDefaultExports, isSelfReferential, api,
145
+ * getRootDefault: () => rootDefaultFunction,
146
+ * setRootDefault: (fn) => { rootDefaultFunction = fn; },
147
+ * context: { debug: true, mode: "root", totalModules: 3 },
148
+ * originalAnalysis: { hasDefault: true, namedExportsCount: 2 }
149
+ * });
150
+ */
151
+ export function processModuleForAPI(options: {
152
+ mod: object;
153
+ fileName: string;
154
+ apiPathKey: string;
155
+ hasMultipleDefaultExports: boolean;
156
+ isSelfReferential: boolean;
157
+ api: object;
158
+ getRootDefault?: Function;
159
+ setRootDefault?: Function;
160
+ context?: {
161
+ debug?: boolean;
162
+ mode?: string;
163
+ categoryName?: string;
164
+ totalModules?: number;
165
+ };
166
+ }): {
167
+ processed: boolean;
168
+ rootDefaultSet: boolean;
169
+ flattened: boolean;
170
+ namespaced: boolean;
171
+ apiAssignments: Record<string, any>;
172
+ };
173
+ /**
174
+ * Centralized category building decisions - contains ALL logic for directory/category processing.
175
+ * This function analyzes a directory and returns decisions about how to structure the API,
176
+ * but doesn't actually build the API (allowing eager/lazy modes to implement differently).
177
+ *
178
+ * @function buildCategoryDecisions
179
+ * @internal
180
+ * @package
181
+ * @param {string} categoryPath - Path to the category directory
182
+ * @param {object} options - Configuration options
183
+ * @param {number} [options.currentDepth=0] - Current nesting depth
184
+ * @param {number} [options.maxDepth=Infinity] - Maximum nesting depth
185
+ * @param {string} [options.mode="eager"] - Loading mode ("eager" or "lazy")
186
+ * @param {Function} [options.subdirHandler] - Handler for subdirectories (lazy mode)
187
+ * @param {object} options.instance - Slothlet instance with _toapiPathKey, _shouldIncludeFile, config
188
+ * @returns {Promise<object>} Category building decisions and data
189
+ *
190
+ * @example
191
+ * // ESM usage
192
+ * import { buildCategoryDecisions } from "@cldmv/slothlet/helpers/api_builder_decisions";
193
+ * const decisions = await buildCategoryDecisions("/path/to/category", {
194
+ * currentDepth: 1,
195
+ * instance: slothletInstance
196
+ * });
197
+ *
198
+ * @example
199
+ * // CJS usage
200
+ * const { buildCategoryDecisions } = require("@cldmv/slothlet/helpers/api_builder_decisions");
201
+ * const decisions = await buildCategoryDecisions("/path/to/category", {
202
+ * currentDepth: 1,
203
+ * instance: slothletInstance
204
+ * });
205
+ */
206
+ export function buildCategoryDecisions(categoryPath: string, options?: {
207
+ currentDepth?: number;
208
+ maxDepth?: number;
209
+ mode?: string;
210
+ subdirHandler?: Function;
211
+ instance: object;
212
+ }): Promise<object>;
213
+ //# sourceMappingURL=decisions.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decisions.d.mts","sourceRoot":"","sources":["../../../../../dist/lib/helpers/api_builder/decisions.mjs"],"names":[],"mappings":"AAwCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,+CAtCG;IAAwB,GAAG,EAAnB,MAAM;IACU,QAAQ,EAAxB,MAAM;IACU,UAAU,EAA1B,MAAM;IACW,yBAAyB,EAA1C,OAAO;IACU,iBAAiB,EAAlC,OAAO;IACW,gBAAgB,GAAlC,OAAO;IAGU,YAAY,GAA7B,MAAM;IACW,YAAY,GAA7B,MAAM;IACY,KAAK,GAAvB,OAAO;CACf,GAAU;IACR,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAA;CACf,CAyHH;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qDAtBG;IAAwB,GAAG,EAAnB,MAAM;IACU,QAAQ,EAAxB,MAAM;IACU,UAAU,EAA1B,MAAM;IACU,eAAe,EAA/B,MAAM;IACY,YAAY;IACZ,KAAK,GAAvB,OAAO;CACf,GAAU;IAAC,gBAAgB,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAC,CA4D7D;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,6CAvCG;IAAwB,GAAG,EAAnB,MAAM;IACU,QAAQ,EAAxB,MAAM;IACU,UAAU,EAA1B,MAAM;IACW,yBAAyB,EAA1C,OAAO;IACU,iBAAiB,EAAlC,OAAO;IACS,GAAG,EAAnB,MAAM;IACa,cAAc;IACd,cAAc;IAChB,OAAO,GAChC;QAAkC,KAAK,GAA/B,OAAO;QACkB,IAAI,GAA7B,MAAM;QACmB,YAAY,GAArC,MAAM;QACmB,YAAY,GAArC,MAAM;KACd;CAAA,GAAU;IACR,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CACpC,CA2KH;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,qDAzBW,MAAM,YAEd;IAAyB,YAAY,GAA7B,MAAM;IACW,QAAQ,GAAzB,MAAM;IACW,IAAI,GAArB,MAAM;IACa,aAAa;IAChB,QAAQ,EAAxB,MAAM;CACd,GAAU,OAAO,CAAC,MAAM,CAAC,CA2Z3B"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * @Project: @cldmv/slothlet
3
+ * @Filename: /src/lib/helpers/api_builder/metadata.mjs
4
+ * @Date: 2025-12-31 00:00:00 -08:00
5
+ * @Author: Nate Hyson <CLDMV>
6
+ * @Email: <Shinrai@users.noreply.github.com>
7
+ * -----
8
+ * @Last modified by: Nate Hyson <CLDMV> (Shinrai@users.noreply.github.com)
9
+ * @Last modified time: 2025-12-31 22:30:16 -08:00 (1767249016)
10
+ * -----
11
+ * @Copyright: Copyright (c) 2013-2025 Catalyzed Motivation Inc. All rights reserved.
12
+ */
13
+ /**
14
+ * @fileoverview Metadata management utilities for API functions.
15
+ * @module @cldmv/slothlet/lib/helpers/api_builder/metadata
16
+ * @memberof module:@cldmv/slothlet.lib.helpers.api_builder
17
+ * @internal
18
+ * @private
19
+ *
20
+ * @description
21
+ * Provides utilities for tagging functions with immutable metadata, cleaning stale metadata
22
+ * from cached modules, and creating immutable metadata objects with deep freezing.
23
+ *
24
+ * Key Features:
25
+ * - Immutable metadata with Proxy-based enforcement
26
+ * - Deep freezing of nested objects and arrays
27
+ * - Recursive traversal for tagging/cleaning object trees
28
+ * - CommonJS cache-aware metadata cleanup
29
+ */
30
+ /**
31
+ * Creates an immutable-but-extensible metadata proxy object.
32
+ *
33
+ * @function createImmutableMetadata
34
+ * @param {object} initial - Initial metadata properties
35
+ * @returns {object} Object with immutable existing properties but allows adding new ones
36
+ *
37
+ * @description
38
+ * Creates an object that enforces immutability of existing properties while allowing
39
+ * new properties to be added. This prevents tampering with security-critical metadata
40
+ * while allowing runtime extension of metadata for additional context.
41
+ *
42
+ * Security features:
43
+ * - Existing properties cannot be modified (non-writable, non-configurable after first set)
44
+ * - Properties cannot be deleted
45
+ * - New properties can be added, which then become immutable
46
+ *
47
+ * @example
48
+ * const meta = createImmutableMetadata({ trusted: true, version: "1.0" });
49
+ * meta.author = "Alice"; // OK - new property
50
+ * meta.author = "Bob"; // FAIL - cannot modify after setting
51
+ * meta.trusted = false; // FAIL - cannot modify existing property
52
+ * delete meta.version; // FAIL - cannot delete properties
53
+ */
54
+ export function createImmutableMetadata(initial?: object): object;
55
+ /**
56
+ * Removes metadata from all functions in an object tree.
57
+ *
58
+ * @function cleanMetadata
59
+ * @param {object|Function} obj - Object or function to clean
60
+ * @param {WeakSet} [visited] - Visited objects tracker to prevent infinite recursion
61
+ * @returns {void}
62
+ *
63
+ * @description
64
+ * Traverses an object/function tree and removes __metadata and __sourceFolder properties.
65
+ * This is needed when reloading CommonJS modules that cache function object references.
66
+ */
67
+ export function cleanMetadata(obj: object | Function, visited?: WeakSet<any>): void;
68
+ /**
69
+ * Recursively tags all functions in an object tree with metadata.
70
+ *
71
+ * @function tagLoadedFunctions
72
+ * @param {object|Function} obj - Object or function to tag
73
+ * @param {object} metadata - Metadata object to attach
74
+ * @param {string} baseDir - Base directory path for relative file tracking
75
+ * @param {WeakSet} [visited] - Visited objects tracker to prevent infinite recursion
76
+ * @returns {void}
77
+ *
78
+ * @description
79
+ * Traverses an object/function tree and attaches immutable metadata to all functions.
80
+ * Also tracks source file information (__sourceFile, __sourceLine) for stack trace
81
+ * matching. Only tags functions that don't already have metadata (non-overwriting).
82
+ *
83
+ * Attached properties:
84
+ * - __metadata: Immutable metadata proxy with all user-provided metadata
85
+ * - __sourceFolder: Absolute path to the folder the module was loaded from
86
+ * - __sourceFile: Absolute file path (for stack trace matching)
87
+ * - __sourceLine: Line number where function is defined (for stack trace matching)
88
+ *
89
+ * @example
90
+ * const modules = { math: { add: (a, b) => a + b } };
91
+ * tagLoadedFunctions(modules, {
92
+ * trusted: true,
93
+ * version: "1.0.0",
94
+ * author: "Alice"
95
+ * }, "/path/to/plugins");
96
+ * // Now modules.math.add.__metadata.trusted === true
97
+ */
98
+ export function tagLoadedFunctions(obj: object | Function, metadata: object, baseDir: string, visited?: WeakSet<any>): void;
99
+ //# sourceMappingURL=metadata.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.d.mts","sourceRoot":"","sources":["../../../../../dist/lib/helpers/api_builder/metadata.mjs"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,kDApBW,MAAM,GACJ,MAAM,CAmJlB;AAED;;;;;;;;;;;GAWG;AACH,mCARW,MAAM,WAAS,2BAEb,IAAI,CAyChB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wCA1BW,MAAM,WAAS,YACf,MAAM,WACN,MAAM,2BAEJ,IAAI,CA+EhB"}