@cldmv/slothlet 2.11.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/AGENT-USAGE.md +355 -325
  2. package/README.md +554 -238
  3. package/dist/lib/builders/api-assignment.mjs +605 -0
  4. package/dist/lib/builders/api_builder.mjs +1073 -0
  5. package/dist/lib/builders/builder.mjs +94 -0
  6. package/dist/lib/builders/modes-processor.mjs +1816 -0
  7. package/dist/lib/errors.mjs +227 -0
  8. package/dist/lib/factories/component-base.mjs +96 -0
  9. package/dist/lib/factories/context.mjs +38 -0
  10. package/dist/lib/handlers/api-cache-manager.mjs +216 -0
  11. package/dist/lib/handlers/api-manager.mjs +2364 -0
  12. package/dist/lib/handlers/context-async.mjs +184 -0
  13. package/dist/lib/handlers/context-live.mjs +184 -0
  14. package/dist/lib/handlers/hook-manager.mjs +789 -0
  15. package/dist/lib/handlers/lifecycle-token.mjs +44 -0
  16. package/dist/lib/handlers/lifecycle.mjs +131 -0
  17. package/dist/lib/handlers/materialize-manager.mjs +64 -0
  18. package/dist/lib/handlers/metadata.mjs +500 -0
  19. package/dist/lib/handlers/ownership.mjs +338 -0
  20. package/dist/lib/handlers/unified-wrapper.mjs +3031 -0
  21. package/dist/lib/helpers/class-instance-wrapper.mjs +125 -0
  22. package/dist/lib/helpers/config.mjs +343 -0
  23. package/dist/lib/helpers/eventemitter-context.mjs +365 -0
  24. package/dist/lib/helpers/hint-detector.mjs +63 -0
  25. package/dist/lib/helpers/modes-utils.mjs +53 -0
  26. package/dist/lib/helpers/resolve-from-caller.mjs +119 -116
  27. package/dist/lib/helpers/sanitize.mjs +247 -168
  28. package/dist/lib/helpers/utilities.mjs +46 -81
  29. package/dist/lib/i18n/languages/de-de.json +377 -0
  30. package/dist/lib/i18n/languages/en-gb.json +377 -0
  31. package/dist/lib/i18n/languages/en-us.json +377 -0
  32. package/dist/lib/i18n/languages/es-mx.json +377 -0
  33. package/dist/lib/i18n/languages/fr-fr.json +377 -0
  34. package/dist/lib/i18n/languages/hi-in.json +377 -0
  35. package/dist/lib/i18n/languages/ja-jp.json +377 -0
  36. package/dist/lib/i18n/languages/ko-kr.json +377 -0
  37. package/dist/lib/i18n/languages/pt-br.json +377 -0
  38. package/dist/lib/i18n/languages/ru-ru.json +377 -0
  39. package/dist/lib/i18n/languages/zh-cn.json +377 -0
  40. package/dist/lib/i18n/translations.mjs +140 -0
  41. package/dist/lib/modes/eager.mjs +75 -0
  42. package/dist/lib/modes/lazy.mjs +97 -0
  43. package/dist/lib/processors/flatten.mjs +453 -0
  44. package/dist/lib/processors/loader.mjs +355 -0
  45. package/dist/lib/processors/type-generator.mjs +291 -0
  46. package/dist/lib/processors/typescript.mjs +188 -0
  47. package/dist/lib/runtime/runtime-asynclocalstorage.mjs +80 -522
  48. package/dist/lib/runtime/runtime-livebindings.mjs +45 -390
  49. package/dist/lib/runtime/runtime.mjs +39 -159
  50. package/dist/slothlet.mjs +525 -744
  51. package/docs/API-RULES.md +338 -486
  52. package/index.cjs +4 -4
  53. package/index.mjs +82 -45
  54. package/package.json +138 -25
  55. package/types/dist/lib/builders/api-assignment.d.mts +97 -0
  56. package/types/dist/lib/builders/api-assignment.d.mts.map +1 -0
  57. package/types/dist/lib/builders/api_builder.d.mts +96 -0
  58. package/types/dist/lib/builders/api_builder.d.mts.map +1 -0
  59. package/types/dist/lib/builders/builder.d.mts +60 -0
  60. package/types/dist/lib/builders/builder.d.mts.map +1 -0
  61. package/types/dist/lib/builders/modes-processor.d.mts +32 -0
  62. package/types/dist/lib/builders/modes-processor.d.mts.map +1 -0
  63. package/types/dist/lib/errors.d.mts +118 -0
  64. package/types/dist/lib/errors.d.mts.map +1 -0
  65. package/types/dist/lib/factories/component-base.d.mts +182 -0
  66. package/types/dist/lib/factories/component-base.d.mts.map +1 -0
  67. package/types/dist/lib/factories/context.d.mts +26 -0
  68. package/types/dist/lib/factories/context.d.mts.map +1 -0
  69. package/types/dist/lib/handlers/api-cache-manager.d.mts +208 -0
  70. package/types/dist/lib/handlers/api-cache-manager.d.mts.map +1 -0
  71. package/types/dist/lib/handlers/api-manager.d.mts +392 -0
  72. package/types/dist/lib/handlers/api-manager.d.mts.map +1 -0
  73. package/types/dist/lib/handlers/context-async.d.mts +66 -0
  74. package/types/dist/lib/handlers/context-async.d.mts.map +1 -0
  75. package/types/dist/lib/handlers/context-live.d.mts +65 -0
  76. package/types/dist/lib/handlers/context-live.d.mts.map +1 -0
  77. package/types/dist/lib/handlers/hook-manager.d.mts +199 -0
  78. package/types/dist/lib/handlers/hook-manager.d.mts.map +1 -0
  79. package/types/dist/lib/handlers/lifecycle-token.d.mts +49 -0
  80. package/types/dist/lib/handlers/lifecycle-token.d.mts.map +1 -0
  81. package/types/dist/lib/handlers/lifecycle.d.mts +90 -0
  82. package/types/dist/lib/handlers/lifecycle.d.mts.map +1 -0
  83. package/types/dist/lib/handlers/materialize-manager.d.mts +75 -0
  84. package/types/dist/lib/handlers/materialize-manager.d.mts.map +1 -0
  85. package/types/dist/lib/handlers/metadata.d.mts +215 -0
  86. package/types/dist/lib/handlers/metadata.d.mts.map +1 -0
  87. package/types/dist/lib/handlers/ownership.d.mts +170 -0
  88. package/types/dist/lib/handlers/ownership.d.mts.map +1 -0
  89. package/types/dist/lib/handlers/unified-wrapper.d.mts +250 -0
  90. package/types/dist/lib/handlers/unified-wrapper.d.mts.map +1 -0
  91. package/types/dist/lib/helpers/class-instance-wrapper.d.mts +54 -0
  92. package/types/dist/lib/helpers/class-instance-wrapper.d.mts.map +1 -0
  93. package/types/dist/lib/helpers/config.d.mts +96 -0
  94. package/types/dist/lib/helpers/config.d.mts.map +1 -0
  95. package/types/dist/lib/helpers/eventemitter-context.d.mts +31 -0
  96. package/types/dist/lib/helpers/eventemitter-context.d.mts.map +1 -0
  97. package/types/dist/lib/helpers/hint-detector.d.mts +20 -0
  98. package/types/dist/lib/helpers/hint-detector.d.mts.map +1 -0
  99. package/types/dist/lib/helpers/modes-utils.d.mts +35 -0
  100. package/types/dist/lib/helpers/modes-utils.d.mts.map +1 -0
  101. package/types/dist/lib/helpers/resolve-from-caller.d.mts +29 -145
  102. package/types/dist/lib/helpers/resolve-from-caller.d.mts.map +1 -1
  103. package/types/dist/lib/helpers/sanitize.d.mts +95 -94
  104. package/types/dist/lib/helpers/sanitize.d.mts.map +1 -1
  105. package/types/dist/lib/helpers/utilities.d.mts +53 -116
  106. package/types/dist/lib/helpers/utilities.d.mts.map +1 -1
  107. package/types/dist/lib/i18n/translations.d.mts +39 -0
  108. package/types/dist/lib/i18n/translations.d.mts.map +1 -0
  109. package/types/dist/lib/modes/eager.d.mts +36 -0
  110. package/types/dist/lib/modes/eager.d.mts.map +1 -0
  111. package/types/dist/lib/modes/lazy.d.mts +49 -0
  112. package/types/dist/lib/modes/lazy.d.mts.map +1 -0
  113. package/types/dist/lib/processors/flatten.d.mts +114 -0
  114. package/types/dist/lib/processors/flatten.d.mts.map +1 -0
  115. package/types/dist/lib/processors/loader.d.mts +47 -0
  116. package/types/dist/lib/processors/loader.d.mts.map +1 -0
  117. package/types/dist/lib/processors/type-generator.d.mts +19 -0
  118. package/types/dist/lib/processors/type-generator.d.mts.map +1 -0
  119. package/types/dist/lib/processors/typescript.d.mts +55 -0
  120. package/types/dist/lib/processors/typescript.d.mts.map +1 -0
  121. package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts +47 -42
  122. package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts.map +1 -1
  123. package/types/dist/lib/runtime/runtime-livebindings.d.mts +34 -65
  124. package/types/dist/lib/runtime/runtime-livebindings.d.mts.map +1 -1
  125. package/types/dist/lib/runtime/runtime.d.mts +39 -9
  126. package/types/dist/lib/runtime/runtime.d.mts.map +1 -1
  127. package/types/dist/slothlet.d.mts +184 -111
  128. package/types/dist/slothlet.d.mts.map +1 -1
  129. package/types/index.d.mts +1 -3
  130. package/dist/lib/engine/README.md +0 -21
  131. package/dist/lib/engine/slothlet_child.mjs +0 -59
  132. package/dist/lib/engine/slothlet_engine.mjs +0 -372
  133. package/dist/lib/engine/slothlet_esm.mjs +0 -230
  134. package/dist/lib/engine/slothlet_helpers.mjs +0 -455
  135. package/dist/lib/engine/slothlet_worker.mjs +0 -149
  136. package/dist/lib/helpers/als-eventemitter.mjs +0 -256
  137. package/dist/lib/helpers/api_builder/add_api.mjs +0 -553
  138. package/dist/lib/helpers/api_builder/analysis.mjs +0 -532
  139. package/dist/lib/helpers/api_builder/construction.mjs +0 -495
  140. package/dist/lib/helpers/api_builder/decisions.mjs +0 -748
  141. package/dist/lib/helpers/api_builder/metadata.mjs +0 -248
  142. package/dist/lib/helpers/api_builder.mjs +0 -41
  143. package/dist/lib/helpers/auto-wrap.mjs +0 -62
  144. package/dist/lib/helpers/hooks.mjs +0 -389
  145. package/dist/lib/helpers/instance-manager.mjs +0 -111
  146. package/dist/lib/helpers/metadata-api.mjs +0 -201
  147. package/dist/lib/helpers/multidefault.mjs +0 -216
  148. package/dist/lib/modes/slothlet_eager.mjs +0 -154
  149. package/dist/lib/modes/slothlet_lazy.mjs +0 -594
  150. package/docs/API-RULES-CONDITIONS.md +0 -712
  151. package/types/dist/lib/engine/slothlet_child.d.mts +0 -2
  152. package/types/dist/lib/engine/slothlet_child.d.mts.map +0 -1
  153. package/types/dist/lib/engine/slothlet_engine.d.mts +0 -31
  154. package/types/dist/lib/engine/slothlet_engine.d.mts.map +0 -1
  155. package/types/dist/lib/engine/slothlet_esm.d.mts +0 -19
  156. package/types/dist/lib/engine/slothlet_esm.d.mts.map +0 -1
  157. package/types/dist/lib/engine/slothlet_helpers.d.mts +0 -25
  158. package/types/dist/lib/engine/slothlet_helpers.d.mts.map +0 -1
  159. package/types/dist/lib/engine/slothlet_worker.d.mts +0 -2
  160. package/types/dist/lib/engine/slothlet_worker.d.mts.map +0 -1
  161. package/types/dist/lib/helpers/als-eventemitter.d.mts +0 -56
  162. package/types/dist/lib/helpers/als-eventemitter.d.mts.map +0 -1
  163. package/types/dist/lib/helpers/api_builder/add_api.d.mts +0 -102
  164. package/types/dist/lib/helpers/api_builder/add_api.d.mts.map +0 -1
  165. package/types/dist/lib/helpers/api_builder/analysis.d.mts +0 -189
  166. package/types/dist/lib/helpers/api_builder/analysis.d.mts.map +0 -1
  167. package/types/dist/lib/helpers/api_builder/construction.d.mts +0 -107
  168. package/types/dist/lib/helpers/api_builder/construction.d.mts.map +0 -1
  169. package/types/dist/lib/helpers/api_builder/decisions.d.mts +0 -213
  170. package/types/dist/lib/helpers/api_builder/decisions.d.mts.map +0 -1
  171. package/types/dist/lib/helpers/api_builder/metadata.d.mts +0 -99
  172. package/types/dist/lib/helpers/api_builder/metadata.d.mts.map +0 -1
  173. package/types/dist/lib/helpers/api_builder.d.mts +0 -6
  174. package/types/dist/lib/helpers/api_builder.d.mts.map +0 -1
  175. package/types/dist/lib/helpers/auto-wrap.d.mts +0 -49
  176. package/types/dist/lib/helpers/auto-wrap.d.mts.map +0 -1
  177. package/types/dist/lib/helpers/hooks.d.mts +0 -342
  178. package/types/dist/lib/helpers/hooks.d.mts.map +0 -1
  179. package/types/dist/lib/helpers/instance-manager.d.mts +0 -41
  180. package/types/dist/lib/helpers/instance-manager.d.mts.map +0 -1
  181. package/types/dist/lib/helpers/metadata-api.d.mts +0 -132
  182. package/types/dist/lib/helpers/metadata-api.d.mts.map +0 -1
  183. package/types/dist/lib/helpers/multidefault.d.mts +0 -90
  184. package/types/dist/lib/helpers/multidefault.d.mts.map +0 -1
  185. package/types/dist/lib/modes/slothlet_eager.d.mts +0 -65
  186. package/types/dist/lib/modes/slothlet_eager.d.mts.map +0 -1
  187. package/types/dist/lib/modes/slothlet_lazy.d.mts +0 -31
  188. package/types/dist/lib/modes/slothlet_lazy.d.mts.map +0 -1
  189. package/types/index.d.mts.map +0 -1
@@ -0,0 +1,2364 @@
1
+ /*
2
+ Copyright 2026 CLDMV/Shinrai
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+
18
+
19
+
20
+ import fs from "node:fs/promises";
21
+ import path from "node:path";
22
+ import { translate } from "@cldmv/slothlet/i18n";
23
+ import { ComponentBase } from "@cldmv/slothlet/factories/component-base";
24
+ import { UnifiedWrapper, resolveWrapper } from "@cldmv/slothlet/handlers/unified-wrapper";
25
+
26
+
27
+ export class ApiManager extends ComponentBase {
28
+ static slothletProperty = "apiManager";
29
+
30
+
31
+ constructor(slothlet) {
32
+ super(slothlet);
33
+ this.state = {
34
+ addHistory: [],
35
+
36
+
37
+ initialConfig: slothlet?.config || null,
38
+ operationHistory: []
39
+ };
40
+ }
41
+
42
+
43
+ normalizeApiPath(apiPath) {
44
+
45
+ if (apiPath === "" || apiPath === null || apiPath === undefined) {
46
+ return { apiPath: "", parts: [] };
47
+ }
48
+
49
+
50
+ if (Array.isArray(apiPath)) {
51
+
52
+ if (apiPath.length === 0) {
53
+ return { apiPath: "", parts: [] };
54
+ }
55
+
56
+ for (let i = 0; i < apiPath.length; i++) {
57
+ if (typeof apiPath[i] !== "string") {
58
+ throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID", {
59
+ apiPath,
60
+ segment: apiPath[i],
61
+ index: i,
62
+ reason: translate("API_PATH_REASON_ARRAY_ELEMENTS"),
63
+ validationError: true
64
+ });
65
+ }
66
+ if (apiPath[i].trim() === "") {
67
+ throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID", {
68
+ apiPath,
69
+ segment: apiPath[i],
70
+ index: i,
71
+ reason: translate("API_PATH_REASON_ARRAY_EMPTY_SEGMENTS"),
72
+ validationError: true
73
+ });
74
+ }
75
+ }
76
+
77
+
78
+ if (apiPath[0] === "slothlet" || (apiPath.length === 1 && (apiPath[0] === "shutdown" || apiPath[0] === "destroy"))) {
79
+ throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID", {
80
+ apiPath,
81
+ reason: translate("API_PATH_REASON_RESERVED_NAME"),
82
+ index: undefined,
83
+ segment: undefined,
84
+ validationError: true
85
+ });
86
+ }
87
+
88
+ return { apiPath: apiPath.join("."), parts: apiPath };
89
+ }
90
+
91
+ if (typeof apiPath !== "string") {
92
+ throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID", {
93
+ apiPath,
94
+ reason: translate("API_PATH_REASON_INVALID_TYPE"),
95
+ index: undefined,
96
+ segment: undefined,
97
+ validationError: true
98
+ });
99
+ }
100
+
101
+ const normalized = apiPath.trim();
102
+
103
+
104
+ if (normalized === "") {
105
+ return { apiPath: "", parts: [] };
106
+ }
107
+
108
+ const parts = normalized.split(".");
109
+ if (parts.length === 0 || parts.some((part) => part.trim() === "")) {
110
+ throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID", {
111
+ apiPath: normalized,
112
+ reason: translate("API_PATH_REASON_EMPTY_SEGMENTS"),
113
+ index: undefined,
114
+ segment: undefined,
115
+ validationError: true
116
+ });
117
+ }
118
+
119
+ if (parts[0] === "slothlet" || normalized === "shutdown" || normalized === "destroy") {
120
+ throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID", {
121
+ apiPath: normalized,
122
+ reason: translate("API_PATH_REASON_RESERVED_NAME"),
123
+ index: undefined,
124
+ segment: undefined,
125
+ validationError: true
126
+ });
127
+ }
128
+
129
+ return { apiPath: normalized, parts };
130
+ }
131
+
132
+
133
+ async resolvePath(inputPath) {
134
+ if (!inputPath || typeof inputPath !== "string") {
135
+ throw new this.SlothletError("INVALID_CONFIG_DIR_INVALID", {
136
+ dir: inputPath,
137
+ validationError: true
138
+ });
139
+ }
140
+
141
+ const resolvedPath = this.slothlet.helpers.resolver.resolvePathFromCaller(inputPath);
142
+ try {
143
+ const stats = await fs.stat(resolvedPath);
144
+ return {
145
+ resolvedPath,
146
+ isDirectory: stats.isDirectory(),
147
+ isFile: stats.isFile()
148
+ };
149
+ } catch (error) {
150
+
151
+
152
+
153
+
154
+ if (error instanceof this.SlothletError) {
155
+ throw error;
156
+ }
157
+
158
+
159
+ throw new this.SlothletError("INVALID_CONFIG_DIR_INVALID", {
160
+ dir: resolvedPath,
161
+ validationError: true
162
+ });
163
+ }
164
+ }
165
+
166
+
167
+ async resolveFolderPath(folderPath) {
168
+ if (!folderPath || typeof folderPath !== "string") {
169
+ throw new this.SlothletError("INVALID_CONFIG_DIR_INVALID", {
170
+ dir: folderPath,
171
+ validationError: true
172
+ });
173
+ }
174
+
175
+ const resolvedPath = this.slothlet.helpers.resolver.resolvePathFromCaller(folderPath);
176
+ try {
177
+ const stats = await fs.stat(resolvedPath);
178
+ if (!stats.isDirectory()) {
179
+ throw new this.SlothletError("INVALID_CONFIG_DIR_INVALID", {
180
+ dir: resolvedPath,
181
+ validationError: true
182
+ });
183
+ }
184
+ } catch (error) {
185
+ if (error instanceof this.SlothletError) {
186
+ throw error;
187
+ }
188
+ throw new this.SlothletError("INVALID_CONFIG_DIR_INVALID", {
189
+ dir: resolvedPath,
190
+ validationError: true
191
+ });
192
+ }
193
+
194
+ return resolvedPath;
195
+ }
196
+
197
+
198
+ buildDefaultModuleId(apiPath, ____resolvedFolderPath) {
199
+ const randomSuffix = Math.random().toString(36).substring(2, 8);
200
+ const prefix = apiPath || "auto";
201
+ return `${prefix}_${randomSuffix}`;
202
+ }
203
+
204
+
205
+ getValueAtPath(root, parts) {
206
+ let current = root;
207
+ for (const part of parts) {
208
+ if (!current || (typeof current !== "object" && typeof current !== "function")) {
209
+ return undefined;
210
+ }
211
+ current = current[part];
212
+ }
213
+ return current;
214
+ }
215
+
216
+
217
+ ensureParentPath(root, parts, options = {}) {
218
+ const { moduleID, sourceFolder } = options;
219
+ let current = root;
220
+ for (let i = 0; i < parts.length - 1; i += 1) {
221
+ const part = parts[i];
222
+ const next = current[part];
223
+ if (next === undefined) {
224
+
225
+ const containerPath = parts.slice(0, i + 1).join(".");
226
+ const containerWrapper = new UnifiedWrapper(this.slothlet, {
227
+ mode: this.____config.mode,
228
+ apiPath: containerPath,
229
+ moduleID: moduleID,
230
+ sourceFolder: sourceFolder
231
+ });
232
+
233
+ containerWrapper.___setImpl({}, moduleID);
234
+ current[part] = containerWrapper.createProxy();
235
+ current = current[part];
236
+ continue;
237
+ }
238
+ if (next && (typeof next === "object" || typeof next === "function")) {
239
+ current = next;
240
+ continue;
241
+ }
242
+ throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID", {
243
+ apiPath: parts.slice(0, i + 1).join("."),
244
+ reason: translate("API_PATH_REASON_NOT_TRAVERSABLE"),
245
+ index: undefined,
246
+ segment: undefined,
247
+ validationError: true
248
+ });
249
+ }
250
+ return current;
251
+ }
252
+
253
+
254
+ isWrapperProxy(value) {
255
+ return !!(value && (typeof value === "object" || typeof value === "function") && resolveWrapper(value) !== null);
256
+ }
257
+
258
+
259
+ async syncWrapper(existingProxy, nextProxy, config, collisionMode = "replace", moduleID = null) {
260
+ if (config?.debug?.api) {
261
+ this.slothlet.debug("api", {
262
+ key: "DEBUG_MODE_SYNC_WRAPPER_ENTRY_EXISTING",
263
+ apiPath: resolveWrapper(existingProxy)?.apiPath
264
+ });
265
+ this.slothlet.debug("api", {
266
+ key: "DEBUG_MODE_SYNC_WRAPPER_ENTRY_NEXT",
267
+ apiPath: resolveWrapper(nextProxy)?.apiPath
268
+ });
269
+ }
270
+
271
+
272
+
273
+ if (!this.isWrapperProxy(existingProxy) || !this.isWrapperProxy(nextProxy)) {
274
+ return false;
275
+ }
276
+
277
+
278
+
279
+ const existingWrapper = resolveWrapper(existingProxy) ?? existingProxy;
280
+
281
+
282
+ const nextWrapper = resolveWrapper(nextProxy) ?? nextProxy;
283
+
284
+ if (config?.debug?.api) {
285
+ this.slothlet.debug("api", {
286
+ key: "DEBUG_MODE_SYNC_WRAPPER_EXISTING",
287
+ apiPath: existingWrapper.apiPath
288
+ });
289
+ this.slothlet.debug("api", {
290
+ key: "DEBUG_MODE_SYNC_WRAPPER_NEXT",
291
+ apiPath: nextWrapper.apiPath
292
+ });
293
+ }
294
+
295
+
296
+
297
+
298
+
299
+ if (nextWrapper.____slothletInternal.materializeFunc && collisionMode !== "merge") {
300
+ existingWrapper.____slothletInternal.materializeFunc = nextWrapper.____slothletInternal.materializeFunc;
301
+ }
302
+
303
+
304
+
305
+
306
+
307
+ const existingChildKeys = Object.keys(existingWrapper).filter((k) => !k.startsWith("_") && !k.startsWith("__"));
308
+ const nextChildKeys = Object.keys(nextWrapper).filter((k) => !k.startsWith("_") && !k.startsWith("__"));
309
+
310
+ if (config?.debug?.api) {
311
+ this.slothlet.debug("api", {
312
+ key: "DEBUG_MODE_SYNC_WRAPPER_BEFORE_MERGE",
313
+ existingCacheSize: existingChildKeys.length,
314
+ nextCacheSize: nextChildKeys.length
315
+ });
316
+ this.slothlet.debug("api", {
317
+ key: "DEBUG_MODE_SYNC_WRAPPER_NEXT_IMPL_KEYS",
318
+
319
+
320
+ implKeys: Object.keys(nextWrapper.____slothletInternal.impl || {})
321
+ });
322
+ this.slothlet.debug("api", {
323
+ key: "DEBUG_MODE_SYNC_WRAPPER_NEXT_CHILDCACHE_KEYS",
324
+ childCacheKeys: nextChildKeys
325
+ });
326
+ }
327
+
328
+
329
+
330
+
331
+
332
+ if (collisionMode === "replace") {
333
+
334
+
335
+
336
+
337
+ if (existingWrapper.___setImpl && nextWrapper.____slothletInternal.impl !== undefined) {
338
+
339
+ existingWrapper.___setImpl(nextWrapper.____slothletInternal.impl, moduleID);
340
+
341
+
342
+
343
+
344
+
345
+ } else if (nextWrapper.____slothletInternal.impl === undefined) {
346
+
347
+
348
+ existingWrapper.____slothletInternal.impl = null;
349
+ } else {
350
+
351
+
352
+
353
+
354
+ if (nextWrapper.____slothletInternal.impl !== undefined) {
355
+ existingWrapper.____slothletInternal.impl = nextWrapper.____slothletInternal.impl;
356
+
357
+ if (
358
+ typeof nextWrapper.____slothletInternal.impl === "function" ||
359
+ (nextWrapper.____slothletInternal.impl && typeof nextWrapper.____slothletInternal.impl.default === "function")
360
+ ) {
361
+ existingWrapper.isCallable = true;
362
+ }
363
+ }
364
+
365
+ }
366
+
367
+
368
+ for (const key of existingChildKeys) {
369
+ delete existingWrapper[key];
370
+ }
371
+ existingWrapper.___adoptImplChildren();
372
+
373
+
374
+
375
+ for (const key of nextChildKeys) {
376
+ const childValue = nextWrapper[key];
377
+ Object.defineProperty(existingWrapper, key, {
378
+ value: childValue,
379
+ writable: false,
380
+ enumerable: true,
381
+ configurable: true
382
+ });
383
+ }
384
+ } else if (collisionMode === "merge") {
385
+
386
+
387
+
388
+
389
+ for (const key of nextChildKeys) {
390
+ const isInternal = typeof key === "string" && (key.startsWith("_") || key.startsWith("__"));
391
+
392
+
393
+ if (!isInternal && !Object.prototype.hasOwnProperty.call(existingWrapper, key)) {
394
+ const childValue = nextWrapper[key];
395
+ Object.defineProperty(existingWrapper, key, {
396
+ value: childValue,
397
+ writable: false,
398
+ enumerable: true,
399
+ configurable: true
400
+ });
401
+ } else if (!isInternal) {
402
+
403
+
404
+ const existingChild = existingWrapper[key];
405
+ const nextChild = nextWrapper[key];
406
+ if (this.isWrapperProxy(existingChild) && this.isWrapperProxy(nextChild)) {
407
+
408
+
409
+ const syncWrapper_nextChildWrapper = resolveWrapper(nextChild) ?? nextChild;
410
+ const syncWrapper_hasGrandChildren = Object.keys(syncWrapper_nextChildWrapper).some(
411
+ (k) => !k.startsWith("_") && !k.startsWith("__")
412
+ );
413
+ if (syncWrapper_hasGrandChildren) {
414
+ await this.syncWrapper(existingChild, nextChild, config, collisionMode, moduleID);
415
+ }
416
+ }
417
+ }
418
+
419
+ }
420
+
421
+
422
+ } else if (collisionMode === "merge-replace") {
423
+
424
+ for (const key of nextChildKeys) {
425
+ const childValue = nextWrapper[key];
426
+ const isInternal = typeof key === "string" && (key.startsWith("_") || key.startsWith("__"));
427
+
428
+ if (!isInternal && Object.prototype.hasOwnProperty.call(existingWrapper, key)) {
429
+
430
+ const existingChild = existingWrapper[key];
431
+ if (this.isWrapperProxy(existingChild) && this.isWrapperProxy(childValue)) {
432
+ await this.syncWrapper(existingChild, childValue, config, collisionMode, moduleID);
433
+ } else {
434
+
435
+ delete existingWrapper[key];
436
+ Object.defineProperty(existingWrapper, key, {
437
+ value: childValue,
438
+ writable: false,
439
+ enumerable: true,
440
+ configurable: true
441
+ });
442
+ }
443
+
444
+
445
+ } else if (!isInternal) {
446
+
447
+ Object.defineProperty(existingWrapper, key, {
448
+ value: childValue,
449
+ writable: false,
450
+ enumerable: true,
451
+ configurable: true
452
+ });
453
+ }
454
+ }
455
+ }
456
+
457
+
458
+
459
+
460
+
461
+ if (existingWrapper.____slothletInternal.state) {
462
+
463
+ const isActuallyMaterialized =
464
+ existingWrapper.____slothletInternal.impl && typeof existingWrapper.____slothletInternal.impl !== "function";
465
+ existingWrapper.____slothletInternal.state.materialized = isActuallyMaterialized;
466
+ existingWrapper.____slothletInternal.state.inFlight = false;
467
+ }
468
+
469
+ return true;
470
+ }
471
+
472
+
473
+ async mutateApiValue(existingValue, nextValue, options, config) {
474
+ if (config?.debug?.api) {
475
+ this.slothlet.debug("api", {
476
+ key: "DEBUG_MODE_MUTATE_API_VALUE_CALLED",
477
+ existingType: typeof existingValue,
478
+ nextType: typeof nextValue
479
+ });
480
+ this.slothlet.debug("api", {
481
+ key: "DEBUG_MODE_MUTATE_API_VALUE_WRAPPER_STATUS",
482
+ existingIsWrapper: this.isWrapperProxy(existingValue),
483
+ nextIsWrapper: this.isWrapperProxy(nextValue)
484
+ });
485
+ this.slothlet.debug("api", {
486
+ key: "DEBUG_MODE_MUTATE_API_VALUE_NEXT_VALUE",
487
+ nextValue
488
+ });
489
+ this.slothlet.debug("api", {
490
+ key: "DEBUG_MODE_MUTATE_API_VALUE_NEXT_VALUE_KEYS",
491
+
492
+
493
+ nextValueKeys: nextValue ? Object.keys(nextValue) : []
494
+ });
495
+ }
496
+
497
+ if (existingValue === nextValue) {
498
+ return;
499
+ }
500
+
501
+
502
+ if (this.isWrapperProxy(existingValue) && this.isWrapperProxy(nextValue)) {
503
+ if (config?.debug?.api) {
504
+ this.slothlet.debug("api", {
505
+ key: "DEBUG_MODE_MUTATE_API_VALUE_SYNC_WRAPPERS"
506
+ });
507
+ }
508
+ await this.syncWrapper(existingValue, nextValue, config, options.collisionMode, options.moduleID);
509
+ return;
510
+ }
511
+
512
+
513
+
514
+ if (this.isWrapperProxy(existingValue) && !this.isWrapperProxy(nextValue)) {
515
+
516
+ const nextIsObjectLike = nextValue && (typeof nextValue === "object" || typeof nextValue === "function");
517
+ const nextHasKeys = nextIsObjectLike && Object.keys(nextValue).length > 0;
518
+
519
+ if (nextHasKeys) {
520
+ if (config?.debug?.api) {
521
+ this.slothlet.debug("api", {
522
+ key: "DEBUG_MODE_MUTATE_API_VALUE_MERGE_INTO_WRAPPER"
523
+ });
524
+ this.slothlet.debug("api", {
525
+ key: "DEBUG_MODE_MUTATE_API_VALUE_MERGE_KEYS",
526
+ keys: Object.keys(nextValue)
527
+ });
528
+ }
529
+
530
+ await this.slothlet.builders.apiAssignment.mergeApiObjects(existingValue, nextValue, {
531
+ removeMissing: options.removeMissing,
532
+ mutateExisting: true,
533
+ allowOverwrite: true,
534
+ syncWrapper: this.syncWrapper.bind(this),
535
+ collisionMode: options.collisionMode,
536
+ moduleID: options.moduleID
537
+ });
538
+ return;
539
+ }
540
+
541
+
542
+ const existingValueRaw = resolveWrapper(existingValue);
543
+
544
+
545
+ if (existingValueRaw !== null) {
546
+ if (config?.debug?.api) {
547
+ this.slothlet.debug("api", {
548
+ key: "DEBUG_MODE_MUTATE_API_VALUE_SETIMPL_FALLBACK"
549
+ });
550
+ }
551
+ existingValueRaw.___setImpl(resolveWrapper(nextValue)?.__impl ?? nextValue);
552
+ return;
553
+ }
554
+ }
555
+
556
+
557
+ if (existingValue && typeof existingValue === "object" && nextValue && typeof nextValue === "object") {
558
+ await this.slothlet.builders.apiAssignment.mergeApiObjects(existingValue, nextValue, {
559
+ removeMissing: options.removeMissing,
560
+ mutateExisting: true,
561
+ allowOverwrite: true,
562
+ syncWrapper: this.syncWrapper.bind(this),
563
+ collisionMode: options.collisionMode,
564
+ moduleID: options.moduleID
565
+ });
566
+ return existingValue;
567
+ }
568
+
569
+
570
+ return nextValue;
571
+ }
572
+
573
+
574
+ async setValueAtPath(root, parts, value, options) {
575
+ const parent = this.ensureParentPath(root, parts, {
576
+ moduleID: options.moduleID,
577
+ sourceFolder: options.sourceFolder
578
+ });
579
+ const finalKey = parts[parts.length - 1];
580
+
581
+
582
+ const existing = parent ? parent[finalKey] : undefined;
583
+
584
+
585
+ const collisionMode = options.collisionMode || "merge";
586
+ const moduleID = options.moduleID;
587
+
588
+ this.slothlet.debug("api", {
589
+ key: "DEBUG_MODE_SET_VALUE_AT_PATH",
590
+ finalKey,
591
+ existingType: typeof existing,
592
+ valueType: typeof value,
593
+ collisionMode,
594
+ options
595
+ });
596
+
597
+
598
+ if (existing !== undefined) {
599
+ if (collisionMode === "error") {
600
+ throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID", {
601
+ apiPath: parts.join("."),
602
+ reason: translate("API_PATH_REASON_COLLISION_ERROR"),
603
+ index: undefined,
604
+ segment: undefined,
605
+ validationError: true
606
+ });
607
+ }
608
+
609
+ if (collisionMode === "skip") {
610
+ this.slothlet.debug("api", {
611
+ key: "DEBUG_MODE_SET_VALUE_AT_PATH_SKIP_COLLISION",
612
+ path: parts.join("."),
613
+ mode: "skip"
614
+ });
615
+ return false;
616
+ }
617
+
618
+ if (collisionMode === "warn") {
619
+ if (this.slothlet && !this.____config?.silent) {
620
+ new this.SlothletWarning("WARNING_HOT_RELOAD_PATH_COLLISION", {
621
+ apiPath: parts.join(".")
622
+ });
623
+ }
624
+ return false;
625
+ }
626
+
627
+ if (collisionMode === "replace") {
628
+ const existingIsObject = typeof existing === "object" || typeof existing === "function";
629
+ const valueIsObject = typeof value === "object" || typeof value === "function";
630
+
631
+ if (existingIsObject && valueIsObject) {
632
+ this.slothlet.debug("api", {
633
+ key: "DEBUG_MODE_SET_VALUE_AT_PATH_REPLACE_MERGE",
634
+ path: parts.join("."),
635
+ mode: "replace"
636
+ });
637
+
638
+ await this.mutateApiValue(
639
+ existing,
640
+ value,
641
+ { removeMissing: false, allowOverwrite: true, collisionMode: "replace", moduleID },
642
+ this.____config
643
+ );
644
+ return true;
645
+ } else {
646
+
647
+ parent[finalKey] = value;
648
+ return true;
649
+ }
650
+ }
651
+
652
+
653
+
654
+ if (collisionMode === "merge" || collisionMode === "merge-replace") {
655
+ const existingIsObject = typeof existing === "object" || typeof existing === "function";
656
+ const valueIsObject = typeof value === "object" || typeof value === "function";
657
+
658
+ if (existingIsObject && valueIsObject) {
659
+ this.slothlet.debug("api", {
660
+ key: "DEBUG_MODE_SET_VALUE_AT_PATH_MERGE_PROPS",
661
+ mode: collisionMode
662
+ });
663
+ await this.mutateApiValue(existing, value, { removeMissing: false, allowOverwrite: true, collisionMode }, this.____config);
664
+ return true;
665
+ } else {
666
+
667
+ if (this.slothlet && !this.____config?.silent) {
668
+ new this.SlothletWarning("WARNING_HOT_RELOAD_MERGE_PRIMITIVES", {
669
+ apiPath: parts.join(".")
670
+ });
671
+ }
672
+ return false;
673
+ }
674
+ }
675
+ }
676
+
677
+
678
+ this.slothlet.debug("api", {
679
+ key: "DEBUG_MODE_SET_VALUE_AT_PATH_ASSIGN",
680
+ finalKey
681
+ });
682
+ parent[finalKey] = value;
683
+ return true;
684
+ }
685
+
686
+
687
+ async deletePath(root, parts) {
688
+ let current = root;
689
+ const stack = [];
690
+ for (const part of parts.slice(0, -1)) {
691
+ if (!current || (typeof current !== "object" && typeof current !== "function")) {
692
+ return false;
693
+ }
694
+ stack.push({ parent: current, key: part });
695
+ current = current[part];
696
+ }
697
+
698
+ const finalKey = parts[parts.length - 1];
699
+ if (!current || (typeof current !== "object" && typeof current !== "function")) {
700
+ return false;
701
+ }
702
+ if (!Object.prototype.hasOwnProperty.call(current, finalKey)) {
703
+ return false;
704
+ }
705
+
706
+
707
+ const removedImpl = current[finalKey];
708
+ const apiPath = parts.join(".");
709
+
710
+
711
+ if (removedImpl && this.slothlet.handlers?.lifecycle) {
712
+ const metadata = this.slothlet.handlers.metadata?.getMetadata?.(removedImpl);
713
+ await this.slothlet.handlers.lifecycle.emit("impl:removed", {
714
+ apiPath,
715
+ impl: removedImpl,
716
+ source: "removal",
717
+ moduleID: metadata?.moduleID,
718
+ filePath: metadata?.filePath,
719
+ sourceFolder: metadata?.sourceFolder
720
+ });
721
+ }
722
+
723
+
724
+
725
+ if (resolveWrapper(current)) {
726
+ const wrapper = resolveWrapper(current);
727
+
728
+ const isInternal = typeof finalKey === "string" && (finalKey.startsWith("_") || finalKey.startsWith("__"));
729
+ if (!isInternal && finalKey in wrapper) {
730
+ delete wrapper[finalKey];
731
+ }
732
+
733
+ if (wrapper.____slothletInternal.impl && typeof wrapper.____slothletInternal.impl === "object") {
734
+ delete wrapper.____slothletInternal.impl[finalKey];
735
+ }
736
+ }
737
+
738
+
739
+ delete current[finalKey];
740
+
741
+
742
+
743
+
744
+ if (removedImpl && (typeof removedImpl === "object" || typeof removedImpl === "function")) {
745
+
746
+
747
+
748
+ if (resolveWrapper(removedImpl)) {
749
+ const wrapper = resolveWrapper(removedImpl);
750
+
751
+ if (wrapper.____slothletInternal.impl !== undefined) {
752
+ wrapper.____slothletInternal.impl = null;
753
+ }
754
+
755
+ const childKeys = Object.keys(wrapper).filter((k) => !k.startsWith("_") && !k.startsWith("__"));
756
+ for (const key of childKeys) {
757
+ delete wrapper[key];
758
+ }
759
+
760
+
761
+
762
+ if (wrapper.____slothletInternal.state) {
763
+ wrapper.____slothletInternal.state.materialized = false;
764
+ wrapper.____slothletInternal.state.inFlight = false;
765
+ }
766
+ }
767
+ }
768
+
769
+
770
+
771
+
772
+ if (this.slothlet.handlers?.metadata) {
773
+ const rootSegment = apiPath.split(".")[0];
774
+ this.slothlet.handlers.metadata.removeUserMetadataByApiPath(rootSegment);
775
+ }
776
+
777
+ for (let i = stack.length - 1; i >= 0; i -= 1) {
778
+ const { parent, key } = stack[i];
779
+ const value = parent[key];
780
+ if (value && (typeof value === "object" || typeof value === "function") && Object.keys(value).length === 0) {
781
+ delete parent[key];
782
+ }
783
+ }
784
+
785
+ return true;
786
+ }
787
+
788
+ async restoreApiPath(apiPath, moduleID) {
789
+
790
+
791
+ const normalizedModuleId = moduleID || null;
792
+ const historyEntry = this.state.addHistory
793
+ .slice()
794
+ .reverse()
795
+
796
+
797
+ .find((entry) => entry.apiPath === apiPath && (normalizedModuleId ? entry.moduleID === normalizedModuleId : true));
798
+
799
+
800
+
801
+
802
+ if (historyEntry) {
803
+ await this.addApiComponent({
804
+ apiPath: historyEntry.apiPath,
805
+ folderPath: historyEntry.folderPath,
806
+ options: {
807
+ ...historyEntry.options,
808
+ metadata: historyEntry.metadata,
809
+ mutateExisting: true,
810
+ forceOverwrite: true,
811
+ collisionMode: "replace",
812
+ recordHistory: false
813
+ }
814
+ });
815
+ return;
816
+ }
817
+
818
+
819
+
820
+
821
+ if (normalizedModuleId === "base" || normalizedModuleId === "core") {
822
+ const baseApi = await this.slothlet.builders.builder.buildAPI({
823
+ dir: this.____config.dir,
824
+ mode: this.____config.mode,
825
+ moduleID: "base"
826
+ });
827
+
828
+ const { parts } = this.normalizeApiPath(apiPath);
829
+ let baseValue = this.getValueAtPath(baseApi, parts);
830
+ if (baseValue === undefined) {
831
+ await this.deletePath(this.slothlet.api, parts);
832
+ await this.deletePath(this.slothlet.boundApi, parts);
833
+ return;
834
+ }
835
+
836
+
837
+
838
+
839
+
840
+ const baseValueRaw = resolveWrapper(baseValue);
841
+
842
+
843
+ if (baseValue && baseValueRaw !== null) {
844
+ baseValue = baseValueRaw.__impl;
845
+ }
846
+
847
+ await this.setValueAtPath(this.slothlet.api, parts, baseValue, {
848
+ mutateExisting: true,
849
+ allowOverwrite: true,
850
+ collisionMode: "replace",
851
+ moduleID: normalizedModuleId
852
+ });
853
+ await this.setValueAtPath(this.slothlet.boundApi, parts, baseValue, {
854
+ mutateExisting: true,
855
+ allowOverwrite: true,
856
+ collisionMode: "replace",
857
+ moduleID: normalizedModuleId
858
+ });
859
+ }
860
+ }
861
+
862
+
863
+ async addApiComponent(params) {
864
+
865
+
866
+ const { apiPath, folderPath, options = {} } = params || {};
867
+
868
+
869
+ if (Array.isArray(folderPath)) {
870
+ const moduleIDs = [];
871
+ for (const singlePath of folderPath) {
872
+ const moduleID = await this.addApiComponent({
873
+ apiPath,
874
+ folderPath: singlePath,
875
+ options
876
+ });
877
+ moduleIDs.push(moduleID);
878
+ }
879
+ return moduleIDs;
880
+ }
881
+
882
+ const { metadata = {}, ...restOptions } = options;
883
+ if (!this.slothlet || !this.slothlet.isLoaded) {
884
+ throw new this.SlothletError("INVALID_CONFIG_NOT_LOADED", {
885
+ operation: "addApi",
886
+ validationError: true
887
+ });
888
+ }
889
+
890
+ const { apiPath: normalizedPath, parts } = this.normalizeApiPath(apiPath);
891
+
892
+
893
+ const { resolvedPath, isDirectory, isFile } = await this.resolvePath(folderPath);
894
+
895
+
896
+ if (!isDirectory && !isFile) {
897
+ throw new this.SlothletError("INVALID_CONFIG_PATH_TYPE", {
898
+ path: resolvedPath,
899
+ validationError: true
900
+ });
901
+ }
902
+
903
+ if (isFile) {
904
+
905
+ const ext = path.extname(resolvedPath);
906
+ if (![".mjs", ".cjs", ".js"].includes(ext)) {
907
+ throw new this.SlothletError("INVALID_CONFIG_FILE_TYPE", {
908
+ path: resolvedPath,
909
+ extension: ext,
910
+ validationError: true
911
+ });
912
+ }
913
+ }
914
+
915
+
916
+ const resolvedFolderPath = resolvedPath;
917
+
918
+
919
+
920
+
921
+ let collisionMode;
922
+ if (restOptions.forceOverwrite) {
923
+ collisionMode = "replace";
924
+ } else {
925
+
926
+
927
+ collisionMode = restOptions.collisionMode || this.____config.api?.collision?.api || "error";
928
+ }
929
+
930
+ const mutateExisting = !!(restOptions.mutateExisting || collisionMode === "merge");
931
+
932
+ const moduleID = restOptions.moduleID ? String(restOptions.moduleID) : this.buildDefaultModuleId(normalizedPath, resolvedFolderPath);
933
+
934
+
935
+
936
+
937
+ if (restOptions.forceOverwrite && !moduleID) {
938
+ throw new this.SlothletError("INVALID_CONFIG_FORCE_OVERWRITE_REQUIRES_MODULE_ID", {
939
+ apiPath: normalizedPath,
940
+ validationError: true
941
+ });
942
+ }
943
+
944
+
945
+
946
+
947
+ let dirForBuild = resolvedFolderPath;
948
+ let fileFilter = null;
949
+
950
+ if (isFile) {
951
+ dirForBuild = path.dirname(resolvedFolderPath);
952
+ const fileName = path.basename(resolvedFolderPath);
953
+ fileFilter = (file) => file === fileName;
954
+ }
955
+
956
+ const newApi = await this.slothlet.builders.builder.buildAPI({
957
+ dir: dirForBuild,
958
+ mode: this.____config.mode,
959
+
960
+
961
+
962
+ apiPathPrefix: normalizedPath,
963
+ collisionContext: "addApi",
964
+ moduleID: moduleID,
965
+
966
+ collisionMode: collisionMode,
967
+
968
+ fileFilter: fileFilter
969
+ });
970
+
971
+
972
+
973
+
974
+ if (this.slothlet.handlers.apiCacheManager) {
975
+ this.slothlet.handlers.apiCacheManager.set(moduleID, {
976
+ endpoint: normalizedPath,
977
+ moduleID: moduleID,
978
+ api: newApi,
979
+ folderPath: resolvedFolderPath,
980
+ mode: this.____config.mode,
981
+ sanitizeOptions: this.____config.sanitize || {},
982
+ collisionMode: collisionMode,
983
+ config: { ...this.____config },
984
+ timestamp: Date.now()
985
+ });
986
+ }
987
+
988
+ this.slothlet.debug("api", {
989
+ key: "DEBUG_MODE_ADD_API_COMPONENT_BUILD_RETURN",
990
+ topLevelKeys: Object.keys(newApi),
991
+ dottedKeys: Object.keys(newApi).filter((k) => k.includes(".")),
992
+ wrappers: Object.keys(newApi)
993
+ .filter((k) => resolveWrapper(newApi[k]) !== null)
994
+ .map((k) => {
995
+ const _w = resolveWrapper(newApi[k]);
996
+ return {
997
+ key: k,
998
+ apiPath: _w.apiPath,
999
+ implKeys: Object.keys(_w.____slothletInternal.impl || {}),
1000
+ childCacheSize: Object.keys(_w).filter((k) => !k.startsWith("_") && !k.startsWith("__")).length,
1001
+ childCacheKeys: Object.keys(_w).filter((k) => !k.startsWith("_") && !k.startsWith("__"))
1002
+ };
1003
+ }),
1004
+ nonWrappers: Object.keys(newApi)
1005
+ .filter((k) => resolveWrapper(newApi[k]) === null)
1006
+ .map((k) => ({ key: k, type: typeof newApi[k] }))
1007
+ });
1008
+
1009
+
1010
+
1011
+
1012
+
1013
+ let apiToMerge = newApi;
1014
+
1015
+
1016
+
1017
+
1018
+ if (isFile && Object.keys(newApi).length === 1) {
1019
+ const fileName = Object.keys(newApi)[0];
1020
+ apiToMerge = newApi[fileName];
1021
+ }
1022
+
1023
+
1024
+
1025
+
1026
+
1027
+
1028
+
1029
+
1030
+
1031
+
1032
+
1033
+
1034
+
1035
+ if (!isFile && normalizedPath) {
1036
+ const lastPart = normalizedPath.includes(".") ? normalizedPath.split(".").pop() : normalizedPath;
1037
+ if (lastPart && Object.prototype.hasOwnProperty.call(apiToMerge, lastPart)) {
1038
+ const dupValue = apiToMerge[lastPart];
1039
+ const dupType = typeof dupValue;
1040
+
1041
+
1042
+ if (dupValue !== null && (dupType === "object" || dupType === "function")) {
1043
+
1044
+
1045
+
1046
+
1047
+ const dupWrapper = resolveWrapper(dupValue);
1048
+ const dupFilePath = dupWrapper?.____slothletInternal?.filePath;
1049
+
1050
+
1051
+ const dupFileDir = dupFilePath ? dupFilePath.replace(/\\/g, "/").split("/").slice(0, -1).join("/") : null;
1052
+ const normalizedFolderPath = resolvedFolderPath.replace(/\\/g, "/").replace(/\/$/, "");
1053
+ const expectedDir = normalizedFolderPath + "/" + lastPart;
1054
+
1055
+
1056
+
1057
+
1058
+
1059
+
1060
+
1061
+ const isDirectChild = dupFileDir === expectedDir || dupFileDir === normalizedFolderPath;
1062
+
1063
+ if (isDirectChild) {
1064
+
1065
+ const hoisted = {};
1066
+ for (const k of Object.keys(apiToMerge)) {
1067
+ if (k !== lastPart) hoisted[k] = apiToMerge[k];
1068
+ }
1069
+
1070
+
1071
+
1072
+ if (dupWrapper) {
1073
+
1074
+ for (const k of Object.keys(dupWrapper).filter((k) => !k.startsWith("_") && !k.startsWith("__"))) {
1075
+ hoisted[k] = dupWrapper[k];
1076
+ }
1077
+ } else {
1078
+
1079
+
1080
+
1081
+
1082
+ for (const k of Object.keys(dupValue)) {
1083
+ hoisted[k] = dupValue[k];
1084
+ }
1085
+
1086
+ }
1087
+ apiToMerge = hoisted;
1088
+ this.slothlet.debug("api", {
1089
+ key: "DEBUG_MODE_RULE_13_DEDUP_HOISTED_KEY",
1090
+ lastPart,
1091
+ newKeys: Object.keys(apiToMerge)
1092
+ });
1093
+ }
1094
+ }
1095
+ }
1096
+ }
1097
+
1098
+ if (this.____config.debug?.api) {
1099
+ this.slothlet.debug("api", {
1100
+ key: "DEBUG_MODE_ADD_API_COMPONENT_MERGE_KEYS",
1101
+ keys: Object.keys(apiToMerge),
1102
+ isRootLevel: parts.length === 0
1103
+ });
1104
+ }
1105
+
1106
+
1107
+
1108
+
1109
+ let anyAssignmentSucceeded = false;
1110
+
1111
+ if (parts.length === 0) {
1112
+
1113
+ for (const key of Object.keys(newApi)) {
1114
+ const result1 = await this.setValueAtPath(this.slothlet.api, [key], newApi[key], {
1115
+ mutateExisting,
1116
+ collisionMode,
1117
+ moduleID,
1118
+ sourceFolder: resolvedFolderPath
1119
+ });
1120
+
1121
+ const result2 = await this.setValueAtPath(this.slothlet.boundApi, [key], newApi[key], {
1122
+ mutateExisting,
1123
+ collisionMode,
1124
+ moduleID,
1125
+ sourceFolder: resolvedFolderPath
1126
+ });
1127
+
1128
+
1129
+ if (result1 || result2) {
1130
+ anyAssignmentSucceeded = true;
1131
+ }
1132
+ }
1133
+ } else {
1134
+
1135
+
1136
+
1137
+ if (resolveWrapper(apiToMerge) === null) {
1138
+
1139
+
1140
+
1141
+ const isCallableNamespace = typeof apiToMerge === "function";
1142
+
1143
+ const containerWrapper = new UnifiedWrapper(this.slothlet, {
1144
+ apiPath: normalizedPath,
1145
+ mode: this.____config.mode,
1146
+ isCallable: isCallableNamespace,
1147
+ moduleID: moduleID,
1148
+ filePath: resolvedFolderPath,
1149
+ sourceFolder: resolvedFolderPath
1150
+ });
1151
+
1152
+ containerWrapper.___setImpl(apiToMerge, moduleID);
1153
+
1154
+ apiToMerge = containerWrapper.createProxy();
1155
+ }
1156
+
1157
+ const result1 = await this.setValueAtPath(this.slothlet.api, parts, apiToMerge, {
1158
+ mutateExisting,
1159
+ collisionMode,
1160
+ moduleID,
1161
+ sourceFolder: resolvedFolderPath
1162
+ });
1163
+
1164
+ const result2 = await this.setValueAtPath(this.slothlet.boundApi, parts, apiToMerge, {
1165
+ mutateExisting,
1166
+ collisionMode,
1167
+ moduleID,
1168
+ sourceFolder: resolvedFolderPath
1169
+ });
1170
+
1171
+
1172
+ if (result1 || result2) {
1173
+ anyAssignmentSucceeded = true;
1174
+ }
1175
+ }
1176
+
1177
+
1178
+
1179
+
1180
+
1181
+
1182
+
1183
+ if (anyAssignmentSucceeded) {
1184
+ const pendingMaterializations = [];
1185
+ const seenWrappers = new Set();
1186
+
1187
+
1188
+ const collectPendingMaterializations = (obj, depth = 0) => {
1189
+ if (!obj || typeof obj !== "object" || depth > 10) return;
1190
+
1191
+ const wrapper = resolveWrapper(obj);
1192
+ if (wrapper) {
1193
+
1194
+ if (seenWrappers.has(wrapper)) return;
1195
+ seenWrappers.add(wrapper);
1196
+
1197
+
1198
+
1199
+
1200
+ if (wrapper.____slothletInternal.materializationPromise) {
1201
+ pendingMaterializations.push(wrapper.____slothletInternal.materializationPromise);
1202
+ }
1203
+
1204
+
1205
+ const childKeys = Object.keys(wrapper).filter((k) => !k.startsWith("_") && !k.startsWith("__"));
1206
+ for (const key of childKeys) {
1207
+ collectPendingMaterializations(wrapper[key], depth + 1);
1208
+ }
1209
+ }
1210
+
1211
+
1212
+ for (const key of Object.keys(obj)) {
1213
+
1214
+
1215
+ if (key !== "____slothletInternal") {
1216
+ collectPendingMaterializations(obj[key], depth + 1);
1217
+ }
1218
+ }
1219
+ };
1220
+
1221
+
1222
+
1223
+ if (parts.length === 0) {
1224
+
1225
+ for (const key of Object.keys(newApi)) {
1226
+
1227
+
1228
+ if (this.slothlet.api[key]) {
1229
+ collectPendingMaterializations(this.slothlet.api[key]);
1230
+ }
1231
+ }
1232
+ } else {
1233
+
1234
+ let current = this.slothlet.api;
1235
+ for (const part of parts) {
1236
+
1237
+
1238
+ if (current && current[part]) {
1239
+ current = current[part];
1240
+ } else {
1241
+ break;
1242
+ }
1243
+
1244
+ }
1245
+
1246
+
1247
+ if (current) {
1248
+ collectPendingMaterializations(current);
1249
+ }
1250
+ }
1251
+
1252
+
1253
+
1254
+
1255
+ if (pendingMaterializations.length > 0) {
1256
+ if (this.____config.debug?.api) {
1257
+ this.slothlet.debug("api", {
1258
+ key: "DEBUG_MODE_AWAITING_PENDING_MATERIALIZATIONS",
1259
+ count: pendingMaterializations.length,
1260
+ apiPath: normalizedPath
1261
+ });
1262
+ }
1263
+ await Promise.all(pendingMaterializations);
1264
+ }
1265
+
1266
+ }
1267
+
1268
+
1269
+
1270
+
1271
+
1272
+
1273
+ if (anyAssignmentSucceeded && metadata && Object.keys(metadata).length > 0 && this.slothlet.handlers.metadata) {
1274
+ if (parts.length === 0) {
1275
+
1276
+ for (const key of Object.keys(newApi)) {
1277
+ this.slothlet.handlers.metadata.registerUserMetadata(key, metadata);
1278
+ }
1279
+ } else {
1280
+ const rootSegment = normalizedPath.split(".")[0];
1281
+ this.slothlet.handlers.metadata.registerUserMetadata(rootSegment, metadata);
1282
+ }
1283
+ }
1284
+
1285
+
1286
+
1287
+
1288
+ if (this.slothlet.handlers.ownership && moduleID) {
1289
+ this.slothlet.handlers.ownership.registerSubtree(apiToMerge, moduleID, normalizedPath);
1290
+ }
1291
+
1292
+
1293
+
1294
+ if (this.slothlet.handlers.ownership) {
1295
+ if (restOptions.recordHistory !== false) {
1296
+ this.state.addHistory.push({
1297
+ apiPath: normalizedPath,
1298
+ folderPath: resolvedFolderPath,
1299
+ options: { ...restOptions, metadata, moduleID },
1300
+ moduleID
1301
+ });
1302
+
1303
+
1304
+ this.state.operationHistory.push({
1305
+ type: "add",
1306
+ apiPath: normalizedPath,
1307
+ folderPath: resolvedFolderPath,
1308
+ options: { ...restOptions, metadata, moduleID },
1309
+ moduleID
1310
+ });
1311
+ }
1312
+ }
1313
+
1314
+ return moduleID;
1315
+ }
1316
+
1317
+
1318
+ async removeApiComponent(pathOrModuleId, options = {}) {
1319
+ const recordHistory = options.recordHistory !== false;
1320
+ if (typeof pathOrModuleId !== "string" || !pathOrModuleId) {
1321
+ throw new this.SlothletError("INVALID_ARGUMENT", {
1322
+ argument: "pathOrModuleId",
1323
+ expected: "non-empty string",
1324
+ received: typeof pathOrModuleId,
1325
+ validationError: true
1326
+ });
1327
+ }
1328
+
1329
+
1330
+
1331
+ let apiPath = null;
1332
+ let moduleID = null;
1333
+
1334
+ if (this.slothlet.handlers.ownership) {
1335
+
1336
+ const candidateModuleID = pathOrModuleId.split(":")[0];
1337
+
1338
+
1339
+
1340
+
1341
+
1342
+ const registeredModules = Array.from(this.slothlet.handlers.ownership.moduleToPath.keys());
1343
+ const matchingModule = registeredModules.findLast((m) => m === candidateModuleID || m.startsWith(`${candidateModuleID}_`));
1344
+
1345
+ if (matchingModule) {
1346
+
1347
+ moduleID = matchingModule;
1348
+ } else {
1349
+
1350
+ const owner = this.slothlet.handlers.ownership.getCurrentOwner(pathOrModuleId);
1351
+ if (owner) {
1352
+
1353
+ apiPath = pathOrModuleId;
1354
+ moduleID = owner.moduleID;
1355
+ } else {
1356
+
1357
+ return false;
1358
+ }
1359
+ }
1360
+ } else {
1361
+
1362
+ const isModuleId = !pathOrModuleId.includes(".");
1363
+ apiPath = isModuleId ? null : pathOrModuleId;
1364
+ moduleID = isModuleId ? pathOrModuleId.split(":")[0] : null;
1365
+ }
1366
+ if (!this.slothlet || !this.slothlet.isLoaded) {
1367
+ throw new this.SlothletError("INVALID_CONFIG_NOT_LOADED", {
1368
+ operation: "removeApi",
1369
+ validationError: true
1370
+ });
1371
+ }
1372
+
1373
+ if (apiPath && moduleID) {
1374
+ const normalizedPath = this.normalizeApiPath(apiPath).apiPath;
1375
+ const moduleIDKey = String(moduleID);
1376
+
1377
+
1378
+ const history = this.slothlet.handlers.ownership?.getPathHistory?.(normalizedPath) || [];
1379
+
1380
+
1381
+ const ownershipResult = this.slothlet.handlers.ownership?.removePath?.(normalizedPath, moduleIDKey) || {
1382
+ action: "none",
1383
+ removedModuleId: null,
1384
+ restoreModuleId: null
1385
+ };
1386
+ const pathParts = this.normalizeApiPath(apiPath).parts;
1387
+ if (ownershipResult.action === "delete") {
1388
+ await this.deletePath(this.slothlet.api, pathParts);
1389
+ await this.deletePath(this.slothlet.boundApi, pathParts);
1390
+
1391
+
1392
+
1393
+ if (this.slothlet.handlers.metadata) {
1394
+ const rootSegment = normalizedPath.split(".")[0];
1395
+ this.slothlet.handlers.metadata.removeUserMetadataByApiPath(rootSegment);
1396
+ }
1397
+
1398
+ this.state.operationHistory.push({
1399
+ type: "remove",
1400
+ apiPath: normalizedPath
1401
+ });
1402
+ return true;
1403
+ }
1404
+
1405
+
1406
+
1407
+ if (ownershipResult.action === "restore") {
1408
+ const restoredValue = this.slothlet.handlers.ownership?.getCurrentValue?.(normalizedPath);
1409
+ const restoredModuleId = this.slothlet.handlers.ownership?.getCurrentOwner?.(normalizedPath)?.moduleID;
1410
+ if (restoredValue !== undefined && restoredModuleId) {
1411
+ await this.setValueAtPath(this.slothlet.api, pathParts, restoredValue, {
1412
+ mutateExisting: true,
1413
+ allowOverwrite: true,
1414
+ collisionMode: "replace",
1415
+ moduleID: restoredModuleId
1416
+ });
1417
+ await this.setValueAtPath(this.slothlet.boundApi, pathParts, restoredValue, {
1418
+ mutateExisting: true,
1419
+ allowOverwrite: true,
1420
+ collisionMode: "replace",
1421
+ moduleID: restoredModuleId
1422
+ });
1423
+
1424
+ this.state.operationHistory.push({
1425
+ type: "remove",
1426
+ apiPath: normalizedPath
1427
+ });
1428
+ return true;
1429
+ }
1430
+ await this.restoreApiPath(normalizedPath, ownershipResult.restoreModuleId);
1431
+
1432
+ this.state.operationHistory.push({
1433
+ type: "remove",
1434
+ apiPath: normalizedPath
1435
+ });
1436
+ return true;
1437
+ }
1438
+ if (ownershipResult.action === "none" && history.length === 0) {
1439
+ await this.deletePath(this.slothlet.api, pathParts);
1440
+ await this.deletePath(this.slothlet.boundApi, pathParts);
1441
+ return true;
1442
+ }
1443
+ return false;
1444
+
1445
+ }
1446
+
1447
+ if (moduleID) {
1448
+ const moduleIDKey = String(moduleID);
1449
+
1450
+
1451
+ const result = this.slothlet.handlers.ownership?.unregister?.(moduleIDKey) || { removed: [], rolledBack: [] };
1452
+
1453
+
1454
+ const allPaths = [...result.removed, ...result.rolledBack.map((r) => r.apiPath)];
1455
+
1456
+
1457
+ const uniquePaths = [...new Set(allPaths)];
1458
+
1459
+
1460
+ const pathsToDelete = [];
1461
+ const pathsToRollback = [];
1462
+
1463
+ for (const path of uniquePaths) {
1464
+ const currentOwner = this.slothlet.handlers.ownership?.getCurrentOwner?.(path);
1465
+
1466
+
1467
+
1468
+ const hasChildrenWithOtherOwners = uniquePaths.some((p) => {
1469
+ if (p === path || !p.startsWith(path + ".")) return false;
1470
+ const childOwner = this.slothlet.handlers.ownership?.getCurrentOwner?.(p);
1471
+ return childOwner && childOwner.moduleID !== moduleIDKey;
1472
+ });
1473
+
1474
+
1475
+ if (currentOwner && currentOwner.moduleID !== moduleIDKey) {
1476
+
1477
+ pathsToRollback.push({ apiPath: path, restoredTo: currentOwner.moduleID });
1478
+ } else if (!hasChildrenWithOtherOwners) {
1479
+
1480
+ pathsToDelete.push(path);
1481
+ }
1482
+
1483
+ }
1484
+
1485
+
1486
+
1487
+ pathsToDelete.sort((a, b) => {
1488
+ const depthA = (a.match(/\./g) || []).length;
1489
+ const depthB = (b.match(/\./g) || []).length;
1490
+ return depthB - depthA;
1491
+ });
1492
+
1493
+
1494
+ for (const removedPath of pathsToDelete) {
1495
+ const { parts } = this.normalizeApiPath(removedPath);
1496
+ await this.deletePath(this.slothlet.api, parts);
1497
+ await this.deletePath(this.slothlet.boundApi, parts);
1498
+
1499
+
1500
+
1501
+ if (this.slothlet.handlers.metadata) {
1502
+ const rootSegment = removedPath.split(".")[0];
1503
+ this.slothlet.handlers.metadata.removeUserMetadataByApiPath(rootSegment);
1504
+ }
1505
+ }
1506
+
1507
+
1508
+
1509
+
1510
+
1511
+ if (pathsToDelete.length > 0) {
1512
+ const rootSegment = pathsToDelete[0].split(".")[0];
1513
+
1514
+
1515
+
1516
+
1517
+ if (rootSegment in this.slothlet.api) {
1518
+ delete this.slothlet.api[rootSegment];
1519
+ }
1520
+
1521
+
1522
+
1523
+ if (rootSegment in this.slothlet.boundApi) {
1524
+ delete this.slothlet.boundApi[rootSegment];
1525
+ }
1526
+ }
1527
+
1528
+
1529
+ for (const rollback of pathsToRollback) {
1530
+
1531
+ const { parts } = this.normalizeApiPath(rollback.apiPath);
1532
+ const previousImpl = this.slothlet.handlers.ownership?.getCurrentValue?.(rollback.apiPath);
1533
+
1534
+ if (previousImpl !== undefined) {
1535
+
1536
+ const existingWrapper = this.getValueAtPath(this.slothlet.api, parts);
1537
+ const existingWrapperRaw = resolveWrapper(existingWrapper);
1538
+ if (existingWrapperRaw) {
1539
+
1540
+ existingWrapperRaw.___setImpl(previousImpl, rollback.restoredTo);
1541
+ }
1542
+
1543
+ const existingBoundWrapper = this.getValueAtPath(this.slothlet.boundApi, parts);
1544
+ const existingBoundWrapperRaw = resolveWrapper(existingBoundWrapper);
1545
+ if (existingBoundWrapperRaw) {
1546
+ existingBoundWrapperRaw.___setImpl(previousImpl, rollback.restoredTo);
1547
+ }
1548
+ }
1549
+ }
1550
+
1551
+ this.state.addHistory = this.state.addHistory.filter((entry) => String(entry.moduleID) !== moduleIDKey);
1552
+
1553
+
1554
+
1555
+
1556
+ if (this.slothlet.handlers.apiCacheManager) {
1557
+ const deleted = this.slothlet.handlers.apiCacheManager.delete(moduleIDKey);
1558
+
1559
+
1560
+ if (deleted) {
1561
+ this.slothlet.debug("cache", {
1562
+ key: "DEBUG_MODE_CACHE_DELETED_MODULE_REMOVED",
1563
+ moduleID: moduleIDKey
1564
+ });
1565
+ }
1566
+ }
1567
+
1568
+
1569
+
1570
+
1571
+ if (recordHistory && pathsToDelete.length > 0) {
1572
+ const rootSegment = pathsToDelete[0].split(".")[0];
1573
+ this.state.operationHistory.push({
1574
+ type: "remove",
1575
+ apiPath: rootSegment
1576
+ });
1577
+ }
1578
+
1579
+ return pathsToDelete.length > 0 || pathsToRollback.length > 0;
1580
+ }
1581
+
1582
+
1583
+
1584
+ if (!apiPath) {
1585
+ throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID", {
1586
+ apiPath,
1587
+ reason: translate("API_PATH_REASON_REQUIRED"),
1588
+ index: undefined,
1589
+ segment: undefined,
1590
+ validationError: true
1591
+ });
1592
+ }
1593
+
1594
+ const { apiPath: normalizedPath, parts } = this.normalizeApiPath(apiPath);
1595
+ const ownershipResult = this.slothlet.handlers.ownership?.removePath?.(normalizedPath, null) || {
1596
+ action: "none",
1597
+ removedModuleId: null,
1598
+ restoreModuleId: null
1599
+ };
1600
+
1601
+
1602
+ const pathExists = this.getValueAtPath(this.slothlet.api, parts) !== undefined;
1603
+
1604
+ if (ownershipResult.action === "none") {
1605
+ if (pathExists) {
1606
+ await this.deletePath(this.slothlet.api, parts);
1607
+ await this.deletePath(this.slothlet.boundApi, parts);
1608
+
1609
+
1610
+
1611
+ if (this.slothlet.handlers.metadata) {
1612
+ this.slothlet.handlers.metadata.removeUserMetadataByApiPath(normalizedPath);
1613
+ }
1614
+
1615
+
1616
+
1617
+ if (recordHistory) {
1618
+ this.state.operationHistory.push({
1619
+ type: "remove",
1620
+ apiPath: normalizedPath
1621
+ });
1622
+ }
1623
+ return true;
1624
+ }
1625
+
1626
+ return false;
1627
+ }
1628
+
1629
+
1630
+ if (ownershipResult.action === "delete") {
1631
+ await this.deletePath(this.slothlet.api, parts);
1632
+ await this.deletePath(this.slothlet.boundApi, parts);
1633
+
1634
+ if (this.slothlet.handlers.metadata) {
1635
+ this.slothlet.handlers.metadata.removeUserMetadataByApiPath(normalizedPath);
1636
+ }
1637
+
1638
+ if (recordHistory) {
1639
+ this.state.operationHistory.push({
1640
+ type: "remove",
1641
+ apiPath: normalizedPath
1642
+ });
1643
+ }
1644
+ return true;
1645
+ }
1646
+ if (ownershipResult.action === "restore") {
1647
+ const restoredValue = this.slothlet.handlers.ownership?.getCurrentValue?.(normalizedPath);
1648
+ const restoredModuleId = this.slothlet.handlers.ownership?.getCurrentOwner?.(normalizedPath)?.moduleID;
1649
+ if (restoredValue !== undefined && restoredModuleId) {
1650
+ await this.setValueAtPath(this.slothlet.api, parts, restoredValue, {
1651
+ mutateExisting: true,
1652
+ allowOverwrite: true,
1653
+ collisionMode: "replace",
1654
+ moduleID: restoredModuleId
1655
+ });
1656
+ await this.setValueAtPath(this.slothlet.boundApi, parts, restoredValue, {
1657
+ mutateExisting: true,
1658
+ allowOverwrite: true,
1659
+ collisionMode: "replace",
1660
+ moduleID: restoredModuleId
1661
+ });
1662
+
1663
+ if (recordHistory) {
1664
+ this.state.operationHistory.push({
1665
+ type: "remove",
1666
+ apiPath: normalizedPath
1667
+ });
1668
+ }
1669
+ return true;
1670
+ }
1671
+ await this.restoreApiPath(normalizedPath, ownershipResult.restoreModuleId);
1672
+
1673
+ if (recordHistory) {
1674
+ this.state.operationHistory.push({
1675
+ type: "remove",
1676
+ apiPath: normalizedPath
1677
+ });
1678
+ }
1679
+ return true;
1680
+ }
1681
+
1682
+
1683
+
1684
+ return false;
1685
+ }
1686
+
1687
+
1688
+ async reloadApiComponent(params) {
1689
+ const { apiPath, moduleID, options } = params || {};
1690
+ if (!this.slothlet || !this.slothlet.isLoaded) {
1691
+ throw new this.SlothletError("INVALID_CONFIG_NOT_LOADED", {
1692
+ operation: "reloadApi",
1693
+ validationError: true
1694
+ });
1695
+ }
1696
+
1697
+
1698
+ if (moduleID) {
1699
+ await this._reloadByModuleID(moduleID);
1700
+ return;
1701
+ }
1702
+
1703
+
1704
+ if (apiPath) {
1705
+ await this._reloadByApiPath(apiPath, options);
1706
+ return;
1707
+ }
1708
+
1709
+ throw new this.SlothletError("INVALID_ARGUMENT", {
1710
+ argument: "params",
1711
+ expected: "{ moduleID } or { apiPath }",
1712
+ received: params,
1713
+ validationError: true
1714
+ });
1715
+ }
1716
+
1717
+
1718
+ async _reloadByModuleID(moduleID, { forceReplace = true } = {}) {
1719
+ const cacheManager = this.slothlet.handlers.apiCacheManager;
1720
+ if (!cacheManager) {
1721
+ throw new this.SlothletError("CACHE_MANAGER_NOT_AVAILABLE", {
1722
+ operation: "reload",
1723
+ validationError: true
1724
+ });
1725
+ }
1726
+
1727
+
1728
+ if (!cacheManager.has(moduleID)) {
1729
+ throw new this.SlothletError("CACHE_NOT_FOUND", {
1730
+ moduleID,
1731
+ operation: "reload",
1732
+ validationError: true
1733
+ });
1734
+ }
1735
+
1736
+
1737
+ const oldEntry = cacheManager.get(moduleID);
1738
+
1739
+ this.slothlet.debug("reload", {
1740
+ key: "DEBUG_MODE_RELOADING_MODULE_BY_ID",
1741
+ moduleID,
1742
+ endpoint: oldEntry.endpoint,
1743
+ folderPath: oldEntry.folderPath
1744
+ });
1745
+
1746
+
1747
+ const freshApi = await cacheManager.rebuildCache(moduleID);
1748
+
1749
+
1750
+ cacheManager.set(moduleID, {
1751
+ ...oldEntry,
1752
+ api: freshApi,
1753
+ timestamp: Date.now()
1754
+ });
1755
+
1756
+
1757
+ this.slothlet.debug("reload", {
1758
+ key: "DEBUG_MODE_FRESH_API_KEYS_BEFORE_RESTORE",
1759
+ moduleID,
1760
+ endpoint: oldEntry.endpoint,
1761
+
1762
+
1763
+ freshApiKeys: Object.keys(freshApi || {})
1764
+ });
1765
+
1766
+
1767
+ await this._restoreApiTree(freshApi, oldEntry.endpoint, moduleID, oldEntry.collisionMode, forceReplace);
1768
+
1769
+
1770
+ this.slothlet.debug("reload", {
1771
+ key: "DEBUG_MODE_FRESH_API_KEYS_AFTER_RESTORE",
1772
+ moduleID,
1773
+ endpoint: oldEntry.endpoint,
1774
+
1775
+
1776
+ freshApiKeys: Object.keys(freshApi || {})
1777
+ });
1778
+
1779
+ this.slothlet.debug("reload", {
1780
+ key: "DEBUG_MODE_MODULE_RELOAD_COMPLETE",
1781
+ moduleID
1782
+ });
1783
+ }
1784
+
1785
+
1786
+ async _reloadByApiPath(apiPath, options = {}) {
1787
+ this.slothlet.debug("reload", {
1788
+ key: "DEBUG_MODE_RELOADING_BY_API_PATH",
1789
+ apiPath
1790
+ });
1791
+
1792
+
1793
+ const moduleIDsToReload = this._findAffectedCaches(apiPath);
1794
+
1795
+
1796
+
1797
+
1798
+ if (moduleIDsToReload.length === 0) {
1799
+ this.slothlet.debug("reload", {
1800
+ key: "DEBUG_MODE_NO_CACHES_ATTEMPTING_RESTORE",
1801
+ apiPath
1802
+ });
1803
+
1804
+ if (apiPath !== "." && apiPath !== "") {
1805
+ await this.restoreApiPath(apiPath, "base");
1806
+ }
1807
+ return;
1808
+ }
1809
+
1810
+
1811
+
1812
+
1813
+
1814
+ const cacheManager = this.slothlet.handlers.apiCacheManager;
1815
+ moduleIDsToReload.sort((a, b) => {
1816
+ const entryA = cacheManager.get(a);
1817
+ const entryB = cacheManager.get(b);
1818
+
1819
+
1820
+
1821
+
1822
+ if (entryA?.endpoint === "." && entryB?.endpoint !== ".") return -1;
1823
+ if (entryB?.endpoint === "." && entryA?.endpoint !== ".") return 1;
1824
+
1825
+
1826
+ const indexA = this.state.addHistory.findIndex((h) => h.moduleID === a);
1827
+ const indexB = this.state.addHistory.findIndex((h) => h.moduleID === b);
1828
+ return indexA - indexB;
1829
+ });
1830
+
1831
+
1832
+
1833
+
1834
+
1835
+ const endpointOrder = new Map();
1836
+ for (const moduleID of moduleIDsToReload) {
1837
+ const entry = cacheManager.get(moduleID);
1838
+
1839
+
1840
+ const ep = entry?.endpoint ?? ".";
1841
+ if (!endpointOrder.has(ep)) endpointOrder.set(ep, []);
1842
+ endpointOrder.get(ep).push(moduleID);
1843
+ }
1844
+
1845
+ for (const [, moduleIDs] of endpointOrder) {
1846
+ for (let i = 0; i < moduleIDs.length; i++) {
1847
+ await this._reloadByModuleID(moduleIDs[i], { forceReplace: i === 0 });
1848
+ }
1849
+ }
1850
+
1851
+
1852
+
1853
+
1854
+ const reloadMetadata = options?.metadata;
1855
+ if (reloadMetadata && typeof reloadMetadata === "object" && Object.keys(reloadMetadata).length > 0) {
1856
+
1857
+
1858
+ if (this.slothlet.handlers.metadata) {
1859
+ const targetPath = apiPath === "." ? null : apiPath.split(".")[0];
1860
+ if (targetPath) {
1861
+ this.slothlet.handlers.metadata.registerUserMetadata(targetPath, reloadMetadata);
1862
+ }
1863
+ }
1864
+ }
1865
+
1866
+ this.slothlet.debug("reload", {
1867
+ key: "DEBUG_MODE_API_PATH_RELOAD_COMPLETE",
1868
+ apiPath,
1869
+ reloadedModules: moduleIDsToReload.length,
1870
+ loadOrder: moduleIDsToReload
1871
+ });
1872
+ }
1873
+
1874
+
1875
+ _findAffectedCaches(apiPath) {
1876
+ const cacheManager = this.slothlet.handlers.apiCacheManager;
1877
+
1878
+
1879
+ if (!cacheManager) return [];
1880
+
1881
+ const allModuleIDs = cacheManager.getAllModuleIDs();
1882
+
1883
+
1884
+ if (apiPath === "." || apiPath === "" || apiPath == null) {
1885
+ const baseModules = [];
1886
+ for (const moduleID of allModuleIDs) {
1887
+ const entry = cacheManager.get(moduleID);
1888
+ if (entry && entry.endpoint === ".") {
1889
+ baseModules.push(moduleID);
1890
+ }
1891
+ }
1892
+ return baseModules;
1893
+ }
1894
+
1895
+
1896
+ const exactMatches = [];
1897
+ for (const moduleID of allModuleIDs) {
1898
+ const entry = cacheManager.get(moduleID);
1899
+ if (entry && entry.endpoint === apiPath) {
1900
+ exactMatches.push(moduleID);
1901
+ }
1902
+ }
1903
+ if (exactMatches.length > 0) return exactMatches;
1904
+
1905
+
1906
+ const children = [];
1907
+ const pathPrefix = apiPath + ".";
1908
+ for (const moduleID of allModuleIDs) {
1909
+ const entry = cacheManager.get(moduleID);
1910
+ if (entry?.endpoint?.startsWith(pathPrefix)) {
1911
+ children.push(moduleID);
1912
+ }
1913
+ }
1914
+ if (children.length > 0) return children;
1915
+
1916
+
1917
+ const ownership = this.slothlet.handlers.ownership;
1918
+ const history = ownership?.getPathHistory?.(apiPath);
1919
+ if (history && history.length > 0) {
1920
+ const owned = [];
1921
+ for (const { moduleID } of history) {
1922
+
1923
+
1924
+ if (cacheManager.has(moduleID)) {
1925
+ owned.push(moduleID);
1926
+ }
1927
+ }
1928
+
1929
+
1930
+ if (owned.length > 0) return owned;
1931
+ }
1932
+
1933
+
1934
+
1935
+
1936
+ let bestMatch = null;
1937
+ let bestLength = -1;
1938
+ for (const moduleID of allModuleIDs) {
1939
+ const entry = cacheManager.get(moduleID);
1940
+
1941
+
1942
+ if (!entry?.endpoint) continue;
1943
+
1944
+ const ep = entry.endpoint;
1945
+
1946
+
1947
+
1948
+
1949
+ if (ep === "." || apiPath.startsWith(ep + ".")) {
1950
+
1951
+
1952
+ if (ep.length > bestLength) {
1953
+ bestLength = ep.length;
1954
+ bestMatch = moduleID;
1955
+ }
1956
+ }
1957
+ }
1958
+
1959
+
1960
+ if (bestMatch) return [bestMatch];
1961
+
1962
+
1963
+
1964
+ return [];
1965
+ }
1966
+
1967
+
1968
+ _collectCustomProperties(existingProxy, freshApi) {
1969
+ const customProps = {};
1970
+
1971
+
1972
+
1973
+ if (!existingProxy || (typeof existingProxy !== "object" && typeof existingProxy !== "function")) {
1974
+ return customProps;
1975
+ }
1976
+
1977
+
1978
+ const wrapper = resolveWrapper(existingProxy);
1979
+
1980
+
1981
+ if (!wrapper) {
1982
+ return customProps;
1983
+ }
1984
+
1985
+
1986
+
1987
+
1988
+
1989
+ const freshKeys = new Set(freshApi ? Object.keys(freshApi) : []);
1990
+
1991
+
1992
+
1993
+
1994
+ const ownKeys = Object.keys(wrapper).filter((k) => !ComponentBase.INTERNAL_KEYS.has(k));
1995
+
1996
+ for (const key of ownKeys) {
1997
+ try {
1998
+
1999
+ const val = wrapper[key];
2000
+
2001
+
2002
+
2003
+ if (val && (typeof val === "object" || typeof val === "function") && resolveWrapper(val)) {
2004
+ continue;
2005
+ }
2006
+
2007
+
2008
+
2009
+ if (!freshKeys.has(key)) {
2010
+
2011
+ customProps[key] = val;
2012
+ } else {
2013
+
2014
+
2015
+ customProps[key] = val;
2016
+ }
2017
+ } catch {
2018
+
2019
+ }
2020
+ }
2021
+
2022
+ return customProps;
2023
+ }
2024
+
2025
+
2026
+ _restoreCustomProperties(proxy, customProps) {
2027
+
2028
+
2029
+
2030
+ if (!proxy || !customProps || typeof customProps !== "object") {
2031
+ return;
2032
+ }
2033
+
2034
+ for (const [key, value] of Object.entries(customProps)) {
2035
+ try {
2036
+ proxy[key] = value;
2037
+ } catch {
2038
+
2039
+ }
2040
+ }
2041
+ }
2042
+
2043
+
2044
+ async _restoreApiTree(freshApi, endpoint, moduleID, collisionMode, forceReplace = true) {
2045
+
2046
+
2047
+
2048
+ if (!freshApi || (typeof freshApi !== "object" && typeof freshApi !== "function")) {
2049
+ return;
2050
+ }
2051
+
2052
+
2053
+ const parts = endpoint === "." ? [] : endpoint.split(".");
2054
+
2055
+ if (parts.length === 0) {
2056
+
2057
+
2058
+
2059
+ for (const key of Object.keys(freshApi)) {
2060
+
2061
+
2062
+
2063
+ if (typeof key === "string" && (key.startsWith("_") || key.startsWith("__"))) continue;
2064
+
2065
+
2066
+
2067
+ if (key === "slothlet" || key === "shutdown" || key === "destroy") continue;
2068
+
2069
+ const existingAtKey = this.slothlet.api[key];
2070
+ const freshValue = freshApi[key];
2071
+
2072
+ if (existingAtKey && resolveWrapper(existingAtKey) !== null) {
2073
+
2074
+ const customProps = this._collectCustomProperties(existingAtKey, freshValue);
2075
+
2076
+
2077
+
2078
+ const freshWrapper = resolveWrapper(freshValue);
2079
+ const isLazyFresh =
2080
+ freshWrapper &&
2081
+ freshWrapper.____slothletInternal.mode === "lazy" &&
2082
+ !freshWrapper.____slothletInternal.state.materialized &&
2083
+ typeof freshWrapper.____slothletInternal.materializeFunc === "function";
2084
+
2085
+
2086
+ this.slothlet.debug("reload", {
2087
+ key: "DEBUG_MODE_RESTORE_ROOT_KEY_INSPECT",
2088
+ rootKey: key,
2089
+ hasFreshWrapper: !!freshWrapper,
2090
+ freshMode: freshWrapper?.____slothletInternal.mode,
2091
+ freshMaterialized: freshWrapper?.____slothletInternal.state?.materialized,
2092
+ hasMaterializeFunc: typeof freshWrapper?.____slothletInternal.materializeFunc === "function",
2093
+ isLazyFresh,
2094
+ existingMaterialized: resolveWrapper(existingAtKey)?.____slothletInternal?.state?.materialized
2095
+ });
2096
+
2097
+ if (isLazyFresh) {
2098
+
2099
+
2100
+
2101
+
2102
+ resolveWrapper(existingAtKey).___resetLazy(freshWrapper.____slothletInternal.materializeFunc);
2103
+
2104
+
2105
+ this._restoreCustomProperties(existingAtKey, customProps);
2106
+
2107
+ this.slothlet.debug("reload", {
2108
+ key: "DEBUG_MODE_ROOT_KEY_RESET_LAZY",
2109
+ rootKey: key,
2110
+ restoredCustomProps: Object.keys(customProps)
2111
+ });
2112
+ } else {
2113
+
2114
+
2115
+
2116
+
2117
+
2118
+
2119
+
2120
+
2121
+ let implForReload;
2122
+
2123
+
2124
+
2125
+ if (freshValue && resolveWrapper(freshValue) !== null) {
2126
+ implForReload = freshWrapper ? UnifiedWrapper._extractFullImpl(freshWrapper) : freshValue;
2127
+ } else {
2128
+
2129
+
2130
+
2131
+
2132
+ implForReload = freshValue;
2133
+ }
2134
+
2135
+
2136
+ if (typeof implForReload === "function") {
2137
+ const extracted = {};
2138
+ for (const k of Object.keys(implForReload)) {
2139
+ extracted[k] = implForReload[k];
2140
+ }
2141
+ implForReload = extracted;
2142
+ }
2143
+
2144
+
2145
+
2146
+
2147
+ const wrapper = resolveWrapper(existingAtKey);
2148
+
2149
+
2150
+ const originalCollisionMode = wrapper ? wrapper.____slothletInternal.state.collisionMode : null;
2151
+ if (forceReplace && wrapper) {
2152
+ wrapper.____slothletInternal.state.collisionMode = "replace";
2153
+ }
2154
+
2155
+
2156
+
2157
+
2158
+ if (wrapper && originalCollisionMode !== null) {
2159
+ wrapper.____slothletInternal.state.collisionMode = originalCollisionMode;
2160
+ }
2161
+
2162
+
2163
+ this._restoreCustomProperties(existingAtKey, customProps);
2164
+
2165
+ this.slothlet.debug("reload", {
2166
+ key: "DEBUG_MODE_ROOT_KEY_UPDATED_SETIMPL",
2167
+ rootKey: key,
2168
+ restoredCustomProps: Object.keys(customProps)
2169
+ });
2170
+ }
2171
+ } else if (existingAtKey === undefined) {
2172
+
2173
+ const cacheManager = this.slothlet.handlers.apiCacheManager;
2174
+ const cacheEntry = cacheManager.get(moduleID);
2175
+
2176
+
2177
+ const resolvedFolderPath = cacheEntry?.folderPath || "";
2178
+
2179
+ await this.setValueAtPath(this.slothlet.api, [key], freshValue, {
2180
+ mutateExisting: true,
2181
+ collisionMode,
2182
+ moduleID,
2183
+ sourceFolder: resolvedFolderPath
2184
+ });
2185
+
2186
+
2187
+
2188
+ if (this.slothlet.boundApi) {
2189
+ await this.setValueAtPath(this.slothlet.boundApi, [key], freshValue, {
2190
+ mutateExisting: true,
2191
+ collisionMode,
2192
+ moduleID,
2193
+ sourceFolder: resolvedFolderPath
2194
+ });
2195
+ }
2196
+ }
2197
+
2198
+ }
2199
+ } else {
2200
+
2201
+
2202
+ const existing = this.getValueAtPath(this.slothlet.api, parts);
2203
+
2204
+ this.slothlet.debug("reload", {
2205
+ key: "DEBUG_MODE_RESTORE_NESTED_PATH",
2206
+ endpoint,
2207
+ moduleID,
2208
+ partsPath: parts.join("."),
2209
+ existingFound: !!existing,
2210
+
2211
+
2212
+
2213
+ hasSetImpl: existing ? resolveWrapper(existing) !== null : false,
2214
+ freshApiKeys: Object.keys(freshApi || {})
2215
+
2216
+ });
2217
+
2218
+ if (existing && resolveWrapper(existing) !== null) {
2219
+
2220
+ const customProps = this._collectCustomProperties(existing, freshApi);
2221
+
2222
+
2223
+
2224
+
2225
+ const wrapper = resolveWrapper(existing);
2226
+
2227
+
2228
+ const originalCollisionMode = wrapper ? wrapper.____slothletInternal.state.collisionMode : null;
2229
+
2230
+ if (forceReplace && wrapper) {
2231
+ wrapper.____slothletInternal.state.collisionMode = "replace";
2232
+ this.slothlet.debug("reload", {
2233
+ key: "DEBUG_MODE_RESTORE_FORCING_REPLACE",
2234
+ endpoint,
2235
+ originalCollisionMode,
2236
+ wrapperApiPath: wrapper.____slothletInternal.apiPath
2237
+ });
2238
+ }
2239
+
2240
+
2241
+
2242
+
2243
+
2244
+
2245
+ let implForReload;
2246
+
2247
+
2248
+
2249
+
2250
+
2251
+
2252
+ if (resolveWrapper(freshApi) !== null) {
2253
+ const freshWrapper = resolveWrapper(freshApi);
2254
+ implForReload = freshWrapper ? UnifiedWrapper._extractFullImpl(freshWrapper) : freshApi;
2255
+ } else if (typeof freshApi === "function") {
2256
+ implForReload = {};
2257
+ for (const key of Object.keys(freshApi)) {
2258
+ implForReload[key] = freshApi[key];
2259
+ }
2260
+ } else {
2261
+ implForReload = freshApi;
2262
+ }
2263
+
2264
+
2265
+
2266
+
2267
+
2268
+
2269
+
2270
+
2271
+
2272
+ if (implForReload && typeof implForReload === "object") {
2273
+ for (const key of Object.keys(implForReload)) {
2274
+ const val = implForReload[key];
2275
+ if (resolveWrapper(val) !== null) {
2276
+ const childWrapper = resolveWrapper(val);
2277
+ if (childWrapper.____slothletInternal.state.materialized) {
2278
+ implForReload[key] = UnifiedWrapper._extractFullImpl(childWrapper);
2279
+ }
2280
+ }
2281
+ }
2282
+ }
2283
+
2284
+ resolveWrapper(existing).___setImpl(implForReload, moduleID);
2285
+
2286
+
2287
+
2288
+
2289
+
2290
+ if (wrapper && originalCollisionMode !== null) {
2291
+ wrapper.____slothletInternal.state.collisionMode = originalCollisionMode;
2292
+ }
2293
+
2294
+
2295
+ this._restoreCustomProperties(existing, customProps);
2296
+
2297
+ this.slothlet.debug("reload", {
2298
+ key: "DEBUG_MODE_UPDATED_WRAPPER_IMPL",
2299
+ endpoint,
2300
+ moduleID,
2301
+ forcedReplaceMode: true,
2302
+ restoredCustomProps: Object.keys(customProps)
2303
+ });
2304
+ } else {
2305
+
2306
+
2307
+ const cacheManager = this.slothlet.handlers.apiCacheManager;
2308
+ const cacheEntry = cacheManager.get(moduleID);
2309
+
2310
+
2311
+ const resolvedFolderPath = cacheEntry?.folderPath || "";
2312
+
2313
+
2314
+ let implForContainer = freshApi;
2315
+
2316
+
2317
+
2318
+
2319
+
2320
+
2321
+ if (typeof freshApi === "function") {
2322
+ implForContainer = {};
2323
+ for (const key of Object.keys(freshApi)) {
2324
+ implForContainer[key] = freshApi[key];
2325
+ }
2326
+ }
2327
+ const containerWrapper = new UnifiedWrapper(this.slothlet, {
2328
+ apiPath: endpoint,
2329
+ mode: this.____config.mode,
2330
+ moduleID: moduleID,
2331
+ filePath: resolvedFolderPath,
2332
+ sourceFolder: resolvedFolderPath
2333
+ });
2334
+ containerWrapper.___setImpl(implForContainer, moduleID);
2335
+ const apiToSet = containerWrapper.createProxy();
2336
+
2337
+
2338
+ await this.setValueAtPath(this.slothlet.api, parts, apiToSet, {
2339
+ mutateExisting: true,
2340
+ collisionMode,
2341
+ moduleID,
2342
+ sourceFolder: resolvedFolderPath
2343
+ });
2344
+
2345
+
2346
+
2347
+ if (this.slothlet.boundApi) {
2348
+ await this.setValueAtPath(this.slothlet.boundApi, parts, apiToSet, {
2349
+ mutateExisting: true,
2350
+ collisionMode,
2351
+ moduleID,
2352
+ sourceFolder: resolvedFolderPath
2353
+ });
2354
+ }
2355
+
2356
+ this.slothlet.debug("reload", {
2357
+ key: "DEBUG_MODE_CREATED_NEW_WRAPPER_UNEXPECTED",
2358
+ endpoint,
2359
+ moduleID
2360
+ });
2361
+ }
2362
+ }
2363
+ }
2364
+ }