@cldmv/slothlet 3.3.0 → 3.4.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 (139) hide show
  1. package/README.md +9 -10
  2. package/REFERENCE.md +23 -0
  3. package/dist/lib/builders/api-assignment.mjs +1 -589
  4. package/dist/lib/builders/api_builder.mjs +1 -1385
  5. package/dist/lib/builders/builder.mjs +1 -78
  6. package/dist/lib/builders/modes-processor.mjs +1 -1800
  7. package/dist/lib/errors.mjs +9 -211
  8. package/dist/lib/factories/component-base.mjs +1 -80
  9. package/dist/lib/factories/context.mjs +1 -22
  10. package/dist/lib/handlers/api-cache-manager.mjs +1 -200
  11. package/dist/lib/handlers/api-manager.mjs +1 -2536
  12. package/dist/lib/handlers/context-async.mjs +1 -172
  13. package/dist/lib/handlers/context-live.mjs +1 -173
  14. package/dist/lib/handlers/hook-manager.mjs +1 -667
  15. package/dist/lib/handlers/lifecycle-token.mjs +1 -28
  16. package/dist/lib/handlers/lifecycle.mjs +1 -115
  17. package/dist/lib/handlers/materialize-manager.mjs +1 -48
  18. package/dist/lib/handlers/metadata.mjs +1 -501
  19. package/dist/lib/handlers/ownership.mjs +1 -322
  20. package/dist/lib/handlers/permission-manager.mjs +1 -392
  21. package/dist/lib/handlers/unified-wrapper.mjs +1 -3110
  22. package/dist/lib/handlers/version-manager.mjs +1 -885
  23. package/dist/lib/helpers/class-instance-wrapper.mjs +1 -109
  24. package/dist/lib/helpers/config.mjs +1 -439
  25. package/dist/lib/helpers/eventemitter-context.mjs +1 -349
  26. package/dist/lib/helpers/hint-detector.mjs +1 -47
  27. package/dist/lib/helpers/modes-utils.mjs +1 -37
  28. package/dist/lib/helpers/pattern-matcher.mjs +1 -125
  29. package/dist/lib/helpers/resolve-from-caller.mjs +1 -169
  30. package/dist/lib/helpers/sanitize.mjs +1 -340
  31. package/dist/lib/helpers/utilities.mjs +1 -70
  32. package/dist/lib/i18n/languages/de-de.json +1 -0
  33. package/dist/lib/i18n/languages/en-gb.json +1 -0
  34. package/dist/lib/i18n/languages/en-us.json +1 -0
  35. package/dist/lib/i18n/languages/es-es.json +412 -0
  36. package/dist/lib/i18n/languages/es-mx.json +1 -0
  37. package/dist/lib/i18n/languages/fr-fr.json +1 -0
  38. package/dist/lib/i18n/languages/hi-in.json +2 -1
  39. package/dist/lib/i18n/languages/ja-jp.json +1 -0
  40. package/dist/lib/i18n/languages/ko-kr.json +1 -0
  41. package/dist/lib/i18n/languages/pt-br.json +21 -20
  42. package/dist/lib/i18n/languages/ru-ru.json +2 -1
  43. package/dist/lib/i18n/languages/zh-cn.json +6 -5
  44. package/dist/lib/i18n/translations.mjs +1 -126
  45. package/dist/lib/modes/eager.mjs +1 -59
  46. package/dist/lib/modes/lazy.mjs +1 -81
  47. package/dist/lib/processors/flatten.mjs +1 -437
  48. package/dist/lib/processors/loader.mjs +1 -339
  49. package/dist/lib/processors/type-generator.mjs +1 -275
  50. package/dist/lib/processors/typescript.mjs +1 -172
  51. package/dist/lib/runtime/runtime-asynclocalstorage.mjs +1 -113
  52. package/dist/lib/runtime/runtime-livebindings.mjs +1 -78
  53. package/dist/lib/runtime/runtime.mjs +1 -102
  54. package/dist/slothlet.mjs +1 -817
  55. package/package.json +35 -31
  56. package/types/dist/lib/builders/api-assignment.d.mts +3 -92
  57. package/types/dist/lib/builders/api-assignment.d.mts.map +1 -1
  58. package/types/dist/lib/builders/api_builder.d.mts +102 -91
  59. package/types/dist/lib/builders/api_builder.d.mts.map +1 -1
  60. package/types/dist/lib/builders/builder.d.mts +1 -55
  61. package/types/dist/lib/builders/builder.d.mts.map +1 -1
  62. package/types/dist/lib/builders/modes-processor.d.mts +3 -27
  63. package/types/dist/lib/builders/modes-processor.d.mts.map +1 -1
  64. package/types/dist/lib/errors.d.mts +19 -109
  65. package/types/dist/lib/errors.d.mts.map +1 -1
  66. package/types/dist/lib/factories/component-base.d.mts +7 -177
  67. package/types/dist/lib/factories/component-base.d.mts.map +1 -1
  68. package/types/dist/lib/factories/context.d.mts +4 -22
  69. package/types/dist/lib/factories/context.d.mts.map +1 -1
  70. package/types/dist/lib/handlers/api-cache-manager.d.mts +20 -203
  71. package/types/dist/lib/handlers/api-cache-manager.d.mts.map +1 -1
  72. package/types/dist/lib/handlers/api-manager.d.mts +34 -408
  73. package/types/dist/lib/handlers/api-manager.d.mts.map +1 -1
  74. package/types/dist/lib/handlers/context-async.d.mts +23 -61
  75. package/types/dist/lib/handlers/context-async.d.mts.map +1 -1
  76. package/types/dist/lib/handlers/context-live.d.mts +22 -59
  77. package/types/dist/lib/handlers/context-live.d.mts.map +1 -1
  78. package/types/dist/lib/handlers/hook-manager.d.mts +46 -185
  79. package/types/dist/lib/handlers/hook-manager.d.mts.map +1 -1
  80. package/types/dist/lib/handlers/lifecycle-token.d.mts +3 -48
  81. package/types/dist/lib/handlers/lifecycle-token.d.mts.map +1 -1
  82. package/types/dist/lib/handlers/lifecycle.d.mts +5 -82
  83. package/types/dist/lib/handlers/lifecycle.d.mts.map +1 -1
  84. package/types/dist/lib/handlers/materialize-manager.d.mts +8 -70
  85. package/types/dist/lib/handlers/materialize-manager.d.mts.map +1 -1
  86. package/types/dist/lib/handlers/metadata.d.mts +17 -221
  87. package/types/dist/lib/handlers/metadata.d.mts.map +1 -1
  88. package/types/dist/lib/handlers/ownership.d.mts +44 -160
  89. package/types/dist/lib/handlers/ownership.d.mts.map +1 -1
  90. package/types/dist/lib/handlers/permission-manager.d.mts +40 -141
  91. package/types/dist/lib/handlers/permission-manager.d.mts.map +1 -1
  92. package/types/dist/lib/handlers/unified-wrapper.d.mts +26 -239
  93. package/types/dist/lib/handlers/unified-wrapper.d.mts.map +1 -1
  94. package/types/dist/lib/handlers/version-manager.d.mts +28 -225
  95. package/types/dist/lib/handlers/version-manager.d.mts.map +1 -1
  96. package/types/dist/lib/helpers/class-instance-wrapper.d.mts +2 -52
  97. package/types/dist/lib/helpers/class-instance-wrapper.d.mts.map +1 -1
  98. package/types/dist/lib/helpers/config.d.mts +125 -139
  99. package/types/dist/lib/helpers/config.d.mts.map +1 -1
  100. package/types/dist/lib/helpers/eventemitter-context.d.mts +3 -29
  101. package/types/dist/lib/helpers/eventemitter-context.d.mts.map +1 -1
  102. package/types/dist/lib/helpers/hint-detector.d.mts +2 -15
  103. package/types/dist/lib/helpers/hint-detector.d.mts.map +1 -1
  104. package/types/dist/lib/helpers/modes-utils.d.mts +3 -30
  105. package/types/dist/lib/helpers/modes-utils.d.mts.map +1 -1
  106. package/types/dist/lib/helpers/pattern-matcher.d.mts +3 -43
  107. package/types/dist/lib/helpers/pattern-matcher.d.mts.map +1 -1
  108. package/types/dist/lib/helpers/resolve-from-caller.d.mts +3 -27
  109. package/types/dist/lib/helpers/resolve-from-caller.d.mts.map +1 -1
  110. package/types/dist/lib/helpers/sanitize.d.mts +4 -92
  111. package/types/dist/lib/helpers/sanitize.d.mts.map +1 -1
  112. package/types/dist/lib/helpers/utilities.d.mts +4 -52
  113. package/types/dist/lib/helpers/utilities.d.mts.map +1 -1
  114. package/types/dist/lib/i18n/translations.d.mts +4 -37
  115. package/types/dist/lib/i18n/translations.d.mts.map +1 -1
  116. package/types/dist/lib/modes/eager.d.mts +8 -30
  117. package/types/dist/lib/modes/eager.d.mts.map +1 -1
  118. package/types/dist/lib/modes/lazy.d.mts +10 -43
  119. package/types/dist/lib/modes/lazy.d.mts.map +1 -1
  120. package/types/dist/lib/processors/flatten.d.mts +56 -107
  121. package/types/dist/lib/processors/flatten.d.mts.map +1 -1
  122. package/types/dist/lib/processors/loader.d.mts +6 -41
  123. package/types/dist/lib/processors/loader.d.mts.map +1 -1
  124. package/types/dist/lib/processors/type-generator.d.mts +2 -16
  125. package/types/dist/lib/processors/type-generator.d.mts.map +1 -1
  126. package/types/dist/lib/processors/typescript.d.mts +6 -53
  127. package/types/dist/lib/processors/typescript.d.mts.map +1 -1
  128. package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts +3 -71
  129. package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts.map +1 -1
  130. package/types/dist/lib/runtime/runtime-livebindings.d.mts +2 -37
  131. package/types/dist/lib/runtime/runtime-livebindings.d.mts.map +1 -1
  132. package/types/dist/lib/runtime/runtime.d.mts +3 -39
  133. package/types/dist/lib/runtime/runtime.d.mts.map +1 -1
  134. package/types/dist/slothlet.d.mts +3 -249
  135. package/types/dist/slothlet.d.mts.map +1 -1
  136. package/types/index.d.mts +36 -16
  137. package/types/index.d.mts.map +1 -0
  138. package/AGENT-USAGE.md +0 -736
  139. package/docs/API-RULES.md +0 -712
@@ -14,2539 +14,4 @@
14
14
  limitations under the License.
15
15
  */
16
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 = {}, versionConfig = null } = 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
- versionConfig
877
- });
878
- moduleIDs.push(moduleID);
879
- }
880
- return moduleIDs;
881
- }
882
-
883
- const { metadata = {}, ...restOptions } = options;
884
- if (!this.slothlet || !this.slothlet.isLoaded) {
885
- throw new this.SlothletError("INVALID_CONFIG_NOT_LOADED", {
886
- operation: "addApi",
887
- validationError: true
888
- });
889
- }
890
-
891
- const { apiPath: normalizedPath, parts } = this.normalizeApiPath(apiPath);
892
-
893
-
894
- let effectivePath = normalizedPath;
895
- let effectiveParts = parts;
896
- if (versionConfig?.version !== undefined && versionConfig?.version !== null) {
897
-
898
-
899
-
900
- if (normalizedPath === "") {
901
- throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID", {
902
- apiPath,
903
- reason: translate("API_PATH_REASON_VERSIONED_ROOT"),
904
- index: undefined,
905
- segment: undefined,
906
- validationError: true
907
- });
908
- }
909
- if (typeof versionConfig.version !== "string" || !String(versionConfig.version).trim()) {
910
- throw new this.SlothletError("INVALID_CONFIG_VERSION_TAG", {
911
- received: versionConfig.version,
912
- validationError: true
913
- });
914
- }
915
- const versionTag = String(versionConfig.version).trim();
916
-
917
-
918
-
919
-
920
- effectiveParts = [versionTag, ...parts];
921
- effectivePath = effectiveParts.join(".");
922
- }
923
-
924
-
925
- const { resolvedPath, isDirectory, isFile } = await this.resolvePath(folderPath);
926
-
927
-
928
- if (!isDirectory && !isFile) {
929
- throw new this.SlothletError("INVALID_CONFIG_PATH_TYPE", {
930
- path: resolvedPath,
931
- validationError: true
932
- });
933
- }
934
-
935
- if (isFile) {
936
-
937
- const ext = path.extname(resolvedPath);
938
- if (![".mjs", ".cjs", ".js"].includes(ext)) {
939
- throw new this.SlothletError("INVALID_CONFIG_FILE_TYPE", {
940
- path: resolvedPath,
941
- extension: ext,
942
- validationError: true
943
- });
944
- }
945
- }
946
-
947
-
948
- const resolvedFolderPath = resolvedPath;
949
-
950
-
951
-
952
-
953
- let collisionMode;
954
- if (restOptions.forceOverwrite) {
955
- collisionMode = "replace";
956
- } else {
957
-
958
-
959
- collisionMode = restOptions.collisionMode || this.____config.api?.collision?.api || "error";
960
- }
961
-
962
- const mutateExisting = !!(restOptions.mutateExisting || collisionMode === "merge");
963
-
964
- const moduleID = restOptions.moduleID ? String(restOptions.moduleID) : this.buildDefaultModuleId(normalizedPath, resolvedFolderPath);
965
-
966
-
967
-
968
-
969
- if (restOptions.forceOverwrite && !moduleID) {
970
- throw new this.SlothletError("INVALID_CONFIG_FORCE_OVERWRITE_REQUIRES_MODULE_ID", {
971
- apiPath: normalizedPath,
972
- validationError: true
973
- });
974
- }
975
-
976
-
977
-
978
-
979
- let dirForBuild = resolvedFolderPath;
980
- let fileFilter = null;
981
-
982
- if (isFile) {
983
- dirForBuild = path.dirname(resolvedFolderPath);
984
- const fileName = path.basename(resolvedFolderPath);
985
- fileFilter = (file) => file === fileName;
986
- }
987
-
988
- const newApi = await this.slothlet.builders.builder.buildAPI({
989
- dir: dirForBuild,
990
- mode: this.____config.mode,
991
-
992
-
993
-
994
-
995
- apiPathPrefix: effectivePath,
996
- collisionContext: "addApi",
997
- moduleID: moduleID,
998
-
999
- collisionMode: collisionMode,
1000
-
1001
- fileFilter: fileFilter
1002
- });
1003
-
1004
-
1005
-
1006
-
1007
- if (this.slothlet.handlers.apiCacheManager) {
1008
- this.slothlet.handlers.apiCacheManager.set(moduleID, {
1009
- endpoint: effectivePath,
1010
- moduleID: moduleID,
1011
- api: newApi,
1012
- folderPath: resolvedFolderPath,
1013
- mode: this.____config.mode,
1014
- sanitizeOptions: this.____config.sanitize || {},
1015
- collisionMode: collisionMode,
1016
- config: { ...this.____config },
1017
- timestamp: Date.now()
1018
- });
1019
- }
1020
-
1021
- this.slothlet.debug("api", {
1022
- key: "DEBUG_MODE_ADD_API_COMPONENT_BUILD_RETURN",
1023
- topLevelKeys: Object.keys(newApi),
1024
- dottedKeys: Object.keys(newApi).filter((k) => k.includes(".")),
1025
- wrappers: Object.keys(newApi)
1026
- .filter((k) => resolveWrapper(newApi[k]) !== null)
1027
- .map((k) => {
1028
- const _w = resolveWrapper(newApi[k]);
1029
- return {
1030
- key: k,
1031
- apiPath: _w.apiPath,
1032
- implKeys: Object.keys(_w.____slothletInternal.impl || {}),
1033
- childCacheSize: Object.keys(_w).filter((k) => !k.startsWith("_") && !k.startsWith("__")).length,
1034
- childCacheKeys: Object.keys(_w).filter((k) => !k.startsWith("_") && !k.startsWith("__"))
1035
- };
1036
- }),
1037
- nonWrappers: Object.keys(newApi)
1038
- .filter((k) => resolveWrapper(newApi[k]) === null)
1039
- .map((k) => ({ key: k, type: typeof newApi[k] }))
1040
- });
1041
-
1042
-
1043
-
1044
-
1045
-
1046
- let apiToMerge = newApi;
1047
-
1048
-
1049
-
1050
-
1051
- if (isFile && Object.keys(newApi).length === 1) {
1052
- const fileName = Object.keys(newApi)[0];
1053
- apiToMerge = newApi[fileName];
1054
- }
1055
-
1056
-
1057
-
1058
-
1059
-
1060
-
1061
-
1062
-
1063
-
1064
-
1065
-
1066
-
1067
-
1068
- if (!isFile && normalizedPath) {
1069
- const lastPart = normalizedPath.includes(".") ? normalizedPath.split(".").pop() : normalizedPath;
1070
- if (lastPart && Object.prototype.hasOwnProperty.call(apiToMerge, lastPart)) {
1071
- const dupValue = apiToMerge[lastPart];
1072
- const dupType = typeof dupValue;
1073
-
1074
-
1075
- if (dupValue !== null && (dupType === "object" || dupType === "function")) {
1076
-
1077
-
1078
-
1079
-
1080
- const dupWrapper = resolveWrapper(dupValue);
1081
- const dupFilePath = dupWrapper?.____slothletInternal?.filePath;
1082
-
1083
-
1084
- const dupFileDir = dupFilePath ? dupFilePath.replace(/\\/g, "/").split("/").slice(0, -1).join("/") : null;
1085
- const normalizedFolderPath = resolvedFolderPath.replace(/\\/g, "/").replace(/\/$/, "");
1086
- const expectedDir = normalizedFolderPath + "/" + lastPart;
1087
-
1088
-
1089
-
1090
-
1091
-
1092
-
1093
-
1094
- const isDirectChild = dupFileDir === expectedDir || dupFileDir === normalizedFolderPath;
1095
-
1096
- if (isDirectChild) {
1097
-
1098
- const hoisted = {};
1099
- for (const k of Object.keys(apiToMerge)) {
1100
- if (k !== lastPart) hoisted[k] = apiToMerge[k];
1101
- }
1102
-
1103
-
1104
-
1105
- if (dupWrapper) {
1106
-
1107
- for (const k of Object.keys(dupWrapper).filter((k) => !k.startsWith("_") && !k.startsWith("__"))) {
1108
- hoisted[k] = dupWrapper[k];
1109
- }
1110
- } else {
1111
-
1112
-
1113
-
1114
-
1115
- for (const k of Object.keys(dupValue)) {
1116
- hoisted[k] = dupValue[k];
1117
- }
1118
-
1119
- }
1120
- apiToMerge = hoisted;
1121
- this.slothlet.debug("api", {
1122
- key: "DEBUG_MODE_RULE_13_DEDUP_HOISTED_KEY",
1123
- lastPart,
1124
- newKeys: Object.keys(apiToMerge)
1125
- });
1126
- }
1127
- }
1128
- }
1129
- }
1130
-
1131
- if (this.____config.debug?.api) {
1132
- this.slothlet.debug("api", {
1133
- key: "DEBUG_MODE_ADD_API_COMPONENT_MERGE_KEYS",
1134
- keys: Object.keys(apiToMerge),
1135
- isRootLevel: parts.length === 0
1136
- });
1137
- }
1138
-
1139
-
1140
-
1141
-
1142
- let anyAssignmentSucceeded = false;
1143
-
1144
- if (parts.length === 0) {
1145
-
1146
- for (const key of Object.keys(newApi)) {
1147
- const result1 = await this.setValueAtPath(this.slothlet.api, [key], newApi[key], {
1148
- mutateExisting,
1149
- collisionMode,
1150
- moduleID,
1151
- sourceFolder: resolvedFolderPath
1152
- });
1153
-
1154
- const result2 = await this.setValueAtPath(this.slothlet.boundApi, [key], newApi[key], {
1155
- mutateExisting,
1156
- collisionMode,
1157
- moduleID,
1158
- sourceFolder: resolvedFolderPath
1159
- });
1160
-
1161
-
1162
- if (result1 || result2) {
1163
- anyAssignmentSucceeded = true;
1164
- }
1165
- }
1166
- } else {
1167
-
1168
-
1169
-
1170
- if (resolveWrapper(apiToMerge) === null) {
1171
-
1172
-
1173
-
1174
- const isCallableNamespace = typeof apiToMerge === "function";
1175
-
1176
- const containerWrapper = new UnifiedWrapper(this.slothlet, {
1177
- apiPath: effectivePath,
1178
- mode: this.____config.mode,
1179
- isCallable: isCallableNamespace,
1180
- moduleID: moduleID,
1181
- filePath: resolvedFolderPath,
1182
- sourceFolder: resolvedFolderPath
1183
- });
1184
-
1185
- containerWrapper.___setImpl(apiToMerge, moduleID);
1186
-
1187
- apiToMerge = containerWrapper.createProxy();
1188
- }
1189
-
1190
- const result1 = await this.setValueAtPath(this.slothlet.api, effectiveParts, apiToMerge, {
1191
- mutateExisting,
1192
- collisionMode,
1193
- moduleID,
1194
- sourceFolder: resolvedFolderPath
1195
- });
1196
-
1197
- const result2 = await this.setValueAtPath(this.slothlet.boundApi, effectiveParts, apiToMerge, {
1198
- mutateExisting,
1199
- collisionMode,
1200
- moduleID,
1201
- sourceFolder: resolvedFolderPath
1202
- });
1203
-
1204
-
1205
- if (result1 || result2) {
1206
- anyAssignmentSucceeded = true;
1207
- }
1208
- }
1209
-
1210
-
1211
-
1212
-
1213
-
1214
-
1215
-
1216
- if (anyAssignmentSucceeded) {
1217
- const pendingMaterializations = [];
1218
- const seenWrappers = new Set();
1219
-
1220
-
1221
- const collectPendingMaterializations = (obj, depth = 0) => {
1222
- if (!obj || typeof obj !== "object" || depth > 10) return;
1223
-
1224
-
1225
-
1226
-
1227
- if (obj.__isVersionDispatcher === true) return;
1228
-
1229
- const wrapper = resolveWrapper(obj);
1230
- if (wrapper) {
1231
-
1232
- if (seenWrappers.has(wrapper)) return;
1233
- seenWrappers.add(wrapper);
1234
-
1235
-
1236
-
1237
-
1238
- if (wrapper.____slothletInternal.materializationPromise) {
1239
- pendingMaterializations.push(wrapper.____slothletInternal.materializationPromise);
1240
- }
1241
-
1242
-
1243
- const childKeys = Object.keys(wrapper).filter((k) => !k.startsWith("_") && !k.startsWith("__"));
1244
- for (const key of childKeys) {
1245
- collectPendingMaterializations(wrapper[key], depth + 1);
1246
- }
1247
- }
1248
-
1249
-
1250
- for (const key of Object.keys(obj)) {
1251
-
1252
-
1253
- if (key !== "____slothletInternal") {
1254
- collectPendingMaterializations(obj[key], depth + 1);
1255
- }
1256
- }
1257
- };
1258
-
1259
-
1260
-
1261
-
1262
-
1263
- if (effectiveParts.length === 0) {
1264
-
1265
- for (const key of Object.keys(newApi)) {
1266
-
1267
-
1268
- if (this.slothlet.api[key]) {
1269
- collectPendingMaterializations(this.slothlet.api[key]);
1270
- }
1271
- }
1272
- } else {
1273
-
1274
- let current = this.slothlet.api;
1275
- for (const part of effectiveParts) {
1276
-
1277
-
1278
- if (current && current[part]) {
1279
- current = current[part];
1280
- } else {
1281
- break;
1282
- }
1283
-
1284
- }
1285
-
1286
-
1287
- if (current) {
1288
- collectPendingMaterializations(current);
1289
- }
1290
- }
1291
-
1292
-
1293
-
1294
-
1295
- if (pendingMaterializations.length > 0) {
1296
- if (this.____config.debug?.api) {
1297
- this.slothlet.debug("api", {
1298
- key: "DEBUG_MODE_AWAITING_PENDING_MATERIALIZATIONS",
1299
- count: pendingMaterializations.length,
1300
- apiPath: normalizedPath
1301
- });
1302
- }
1303
- await Promise.all(pendingMaterializations);
1304
- }
1305
-
1306
- }
1307
-
1308
-
1309
-
1310
-
1311
-
1312
-
1313
- if (anyAssignmentSucceeded && metadata && Object.keys(metadata).length > 0 && this.slothlet.handlers.metadata) {
1314
- if (parts.length === 0) {
1315
-
1316
- for (const key of Object.keys(newApi)) {
1317
- this.slothlet.handlers.metadata.registerUserMetadata(key, metadata);
1318
- }
1319
- } else {
1320
-
1321
-
1322
-
1323
-
1324
- const rootSegment = effectiveParts[0];
1325
- this.slothlet.handlers.metadata.registerUserMetadata(rootSegment, metadata);
1326
- }
1327
- }
1328
-
1329
-
1330
-
1331
-
1332
-
1333
-
1334
- if (this.slothlet.handlers.ownership && moduleID) {
1335
- this.slothlet.handlers.ownership.registerSubtree(apiToMerge, moduleID, effectivePath);
1336
- }
1337
-
1338
-
1339
-
1340
- if (this.slothlet.handlers.ownership) {
1341
- if (restOptions.recordHistory !== false) {
1342
- this.state.addHistory.push({
1343
- apiPath: normalizedPath,
1344
- folderPath: resolvedFolderPath,
1345
- options: { ...restOptions, metadata, moduleID },
1346
- moduleID,
1347
- versionConfig: versionConfig || null
1348
- });
1349
-
1350
-
1351
-
1352
- this.state.operationHistory.push({
1353
- type: "add",
1354
- apiPath: normalizedPath,
1355
- folderPath: resolvedFolderPath,
1356
- options: { ...restOptions, metadata, moduleID },
1357
- moduleID,
1358
- versionConfig: versionConfig || null
1359
- });
1360
- }
1361
- }
1362
-
1363
-
1364
-
1365
-
1366
-
1367
- if (versionConfig?.version && this.slothlet.handlers.versionManager) {
1368
- const versionTag = String(versionConfig.version).trim();
1369
- try {
1370
- this.slothlet.handlers.versionManager.registerVersion(
1371
- normalizedPath,
1372
- versionTag,
1373
- moduleID,
1374
- versionConfig.metadata ?? {},
1375
- versionConfig.default ?? false
1376
- );
1377
- } catch (error) {
1378
- await this._rollbackFailedVersionedAdd({ moduleID, effectivePath, normalizedPath });
1379
- throw error;
1380
- }
1381
- }
1382
-
1383
-
1384
- if (restOptions.permissions && this.slothlet.handlers?.permissionManager) {
1385
- const perms = restOptions.permissions;
1386
- const callerPattern = `${normalizedPath}.**`;
1387
-
1388
- if (Array.isArray(perms.deny)) {
1389
- for (const target of perms.deny) {
1390
- this.slothlet.handlers.permissionManager.addRule(
1391
- { caller: callerPattern, target, effect: "deny" },
1392
- moduleID
1393
- );
1394
- }
1395
- }
1396
- if (Array.isArray(perms.allow)) {
1397
- for (const target of perms.allow) {
1398
- this.slothlet.handlers.permissionManager.addRule(
1399
- { caller: callerPattern, target, effect: "allow" },
1400
- moduleID
1401
- );
1402
- }
1403
- }
1404
- }
1405
-
1406
- return moduleID;
1407
- }
1408
-
1409
-
1410
- async _rollbackFailedVersionedAdd({ moduleID, effectivePath, normalizedPath }) {
1411
-
1412
-
1413
-
1414
-
1415
-
1416
- let addIndex = -1;
1417
- for (let i = this.state.operationHistory.length - 1; i >= 0; i--) {
1418
- const entry = this.state.operationHistory[i];
1419
- if (entry?.type === "add" && entry?.apiPath === normalizedPath && entry?.moduleID === moduleID) {
1420
- addIndex = i;
1421
- break;
1422
- }
1423
- }
1424
- if (addIndex !== -1) {
1425
- this.state.operationHistory.splice(addIndex, 1);
1426
- }
1427
-
1428
-
1429
-
1430
-
1431
-
1432
- this.state.addHistory = this.state.addHistory.filter((entry) => entry?.moduleID !== moduleID);
1433
-
1434
-
1435
-
1436
-
1437
- try {
1438
- await this.removeApiComponent(moduleID || effectivePath, { recordHistory: false });
1439
- } catch {
1440
-
1441
-
1442
- }
1443
- }
1444
-
1445
-
1446
- async removeApiComponent(pathOrModuleId, options = {}) {
1447
- const recordHistory = options.recordHistory !== false;
1448
- if (typeof pathOrModuleId !== "string" || !pathOrModuleId) {
1449
- throw new this.SlothletError("INVALID_ARGUMENT", {
1450
- argument: "pathOrModuleId",
1451
- expected: "non-empty string",
1452
- received: typeof pathOrModuleId,
1453
- validationError: true
1454
- });
1455
- }
1456
-
1457
-
1458
-
1459
- let apiPath = null;
1460
- let moduleID = null;
1461
-
1462
- if (this.slothlet.handlers.ownership) {
1463
-
1464
- const candidateModuleID = pathOrModuleId.split(":")[0];
1465
-
1466
-
1467
-
1468
-
1469
-
1470
- const registeredModules = Array.from(this.slothlet.handlers.ownership.moduleToPath.keys());
1471
- let matchingModule = null;
1472
- for (let i = registeredModules.length - 1; i >= 0; i--) {
1473
- const candidate = registeredModules[i];
1474
- if (candidate === candidateModuleID || candidate.startsWith(`${candidateModuleID}_`)) {
1475
- matchingModule = candidate;
1476
- break;
1477
- }
1478
- }
1479
-
1480
- if (matchingModule) {
1481
-
1482
- moduleID = matchingModule;
1483
- } else {
1484
-
1485
- const owner = this.slothlet.handlers.ownership.getCurrentOwner(pathOrModuleId);
1486
- if (owner) {
1487
-
1488
- apiPath = pathOrModuleId;
1489
- moduleID = owner.moduleID;
1490
- } else {
1491
-
1492
- return false;
1493
- }
1494
- }
1495
- } else {
1496
-
1497
- const isModuleId = !pathOrModuleId.includes(".");
1498
- apiPath = isModuleId ? null : pathOrModuleId;
1499
- moduleID = isModuleId ? pathOrModuleId.split(":")[0] : null;
1500
- }
1501
- if (!this.slothlet || !this.slothlet.isLoaded) {
1502
- throw new this.SlothletError("INVALID_CONFIG_NOT_LOADED", {
1503
- operation: "removeApi",
1504
- validationError: true
1505
- });
1506
- }
1507
-
1508
- if (apiPath && moduleID) {
1509
- const normalizedPath = this.normalizeApiPath(apiPath).apiPath;
1510
- const moduleIDKey = String(moduleID);
1511
-
1512
-
1513
- const history = this.slothlet.handlers.ownership?.getPathHistory?.(normalizedPath) || [];
1514
-
1515
-
1516
- const ownershipResult = this.slothlet.handlers.ownership?.removePath?.(normalizedPath, moduleIDKey) || {
1517
- action: "none",
1518
- removedModuleId: null,
1519
- restoreModuleId: null
1520
- };
1521
- const pathParts = this.normalizeApiPath(apiPath).parts;
1522
- if (ownershipResult.action === "delete") {
1523
- await this.deletePath(this.slothlet.api, pathParts);
1524
- await this.deletePath(this.slothlet.boundApi, pathParts);
1525
-
1526
-
1527
-
1528
- if (this.slothlet.handlers.metadata) {
1529
- const rootSegment = normalizedPath.split(".")[0];
1530
- this.slothlet.handlers.metadata.removeUserMetadataByApiPath(rootSegment);
1531
- }
1532
-
1533
- if (this.slothlet.handlers.versionManager) {
1534
- const versionKey = this.slothlet.handlers.versionManager.getVersionKeyForModule(moduleIDKey);
1535
- if (versionKey) {
1536
- this.slothlet.handlers.versionManager.unregisterVersion(versionKey.logicalPath, versionKey.versionTag);
1537
- }
1538
-
1539
-
1540
-
1541
- if (this.slothlet.handlers.versionManager.hasDispatcher(normalizedPath)) {
1542
- this.slothlet.handlers.versionManager.teardownDispatcher(normalizedPath);
1543
- }
1544
- }
1545
-
1546
- this.state.operationHistory.push({
1547
- type: "remove",
1548
- apiPath: normalizedPath
1549
- });
1550
- return true;
1551
- }
1552
-
1553
-
1554
-
1555
- if (ownershipResult.action === "restore") {
1556
- const restoredValue = this.slothlet.handlers.ownership?.getCurrentValue?.(normalizedPath);
1557
- const restoredModuleId = this.slothlet.handlers.ownership?.getCurrentOwner?.(normalizedPath)?.moduleID;
1558
- if (restoredValue !== undefined && restoredModuleId) {
1559
- await this.setValueAtPath(this.slothlet.api, pathParts, restoredValue, {
1560
- mutateExisting: true,
1561
- allowOverwrite: true,
1562
- collisionMode: "replace",
1563
- moduleID: restoredModuleId
1564
- });
1565
- await this.setValueAtPath(this.slothlet.boundApi, pathParts, restoredValue, {
1566
- mutateExisting: true,
1567
- allowOverwrite: true,
1568
- collisionMode: "replace",
1569
- moduleID: restoredModuleId
1570
- });
1571
-
1572
- this.state.operationHistory.push({
1573
- type: "remove",
1574
- apiPath: normalizedPath
1575
- });
1576
- return true;
1577
- }
1578
- await this.restoreApiPath(normalizedPath, ownershipResult.restoreModuleId);
1579
-
1580
- this.state.operationHistory.push({
1581
- type: "remove",
1582
- apiPath: normalizedPath
1583
- });
1584
- return true;
1585
- }
1586
- if (ownershipResult.action === "none" && history.length === 0) {
1587
- await this.deletePath(this.slothlet.api, pathParts);
1588
- await this.deletePath(this.slothlet.boundApi, pathParts);
1589
- return true;
1590
- }
1591
- return false;
1592
-
1593
- }
1594
-
1595
- if (moduleID) {
1596
- const moduleIDKey = String(moduleID);
1597
-
1598
-
1599
- const result = this.slothlet.handlers.ownership?.unregister?.(moduleIDKey) || { removed: [], rolledBack: [] };
1600
-
1601
-
1602
-
1603
-
1604
- if (this.slothlet.handlers.versionManager) {
1605
- const versionKey = this.slothlet.handlers.versionManager.getVersionKeyForModule(moduleIDKey);
1606
- if (versionKey) {
1607
- this.slothlet.handlers.versionManager.unregisterVersion(versionKey.logicalPath, versionKey.versionTag);
1608
- }
1609
- }
1610
-
1611
-
1612
- const allPaths = [...result.removed, ...result.rolledBack.map((r) => r.apiPath)];
1613
-
1614
-
1615
- const uniquePaths = [...new Set(allPaths)];
1616
-
1617
-
1618
- const pathsToDelete = [];
1619
- const pathsToRollback = [];
1620
-
1621
- for (const path of uniquePaths) {
1622
- const currentOwner = this.slothlet.handlers.ownership?.getCurrentOwner?.(path);
1623
-
1624
-
1625
-
1626
- const hasChildrenWithOtherOwners = uniquePaths.some((p) => {
1627
- if (p === path || !p.startsWith(path + ".")) return false;
1628
- const childOwner = this.slothlet.handlers.ownership?.getCurrentOwner?.(p);
1629
- return childOwner && childOwner.moduleID !== moduleIDKey;
1630
- });
1631
-
1632
-
1633
- if (currentOwner && currentOwner.moduleID !== moduleIDKey) {
1634
-
1635
- pathsToRollback.push({ apiPath: path, restoredTo: currentOwner.moduleID });
1636
- } else if (!hasChildrenWithOtherOwners) {
1637
-
1638
- pathsToDelete.push(path);
1639
- }
1640
-
1641
- }
1642
-
1643
-
1644
-
1645
- pathsToDelete.sort((a, b) => {
1646
- const depthA = (a.match(/\./g) || []).length;
1647
- const depthB = (b.match(/\./g) || []).length;
1648
- return depthB - depthA;
1649
- });
1650
-
1651
-
1652
- for (const removedPath of pathsToDelete) {
1653
- const { parts } = this.normalizeApiPath(removedPath);
1654
- await this.deletePath(this.slothlet.api, parts);
1655
- await this.deletePath(this.slothlet.boundApi, parts);
1656
-
1657
-
1658
-
1659
- if (this.slothlet.handlers.metadata) {
1660
- const rootSegment = removedPath.split(".")[0];
1661
- this.slothlet.handlers.metadata.removeUserMetadataByApiPath(rootSegment);
1662
- }
1663
- }
1664
-
1665
-
1666
-
1667
-
1668
-
1669
- if (pathsToDelete.length > 0) {
1670
- const rootSegment = pathsToDelete[0].split(".")[0];
1671
-
1672
-
1673
-
1674
-
1675
- if (rootSegment in this.slothlet.api) {
1676
- delete this.slothlet.api[rootSegment];
1677
- }
1678
-
1679
-
1680
-
1681
- if (rootSegment in this.slothlet.boundApi) {
1682
- delete this.slothlet.boundApi[rootSegment];
1683
- }
1684
- }
1685
-
1686
-
1687
- for (const rollback of pathsToRollback) {
1688
-
1689
- const { parts } = this.normalizeApiPath(rollback.apiPath);
1690
- const previousImpl = this.slothlet.handlers.ownership?.getCurrentValue?.(rollback.apiPath);
1691
-
1692
- if (previousImpl !== undefined) {
1693
-
1694
- const existingWrapper = this.getValueAtPath(this.slothlet.api, parts);
1695
- const existingWrapperRaw = resolveWrapper(existingWrapper);
1696
- if (existingWrapperRaw) {
1697
-
1698
- existingWrapperRaw.___setImpl(previousImpl, rollback.restoredTo);
1699
- }
1700
-
1701
- const existingBoundWrapper = this.getValueAtPath(this.slothlet.boundApi, parts);
1702
- const existingBoundWrapperRaw = resolveWrapper(existingBoundWrapper);
1703
- if (existingBoundWrapperRaw) {
1704
- existingBoundWrapperRaw.___setImpl(previousImpl, rollback.restoredTo);
1705
- }
1706
- }
1707
- }
1708
-
1709
- this.state.addHistory = this.state.addHistory.filter((entry) => String(entry.moduleID) !== moduleIDKey);
1710
-
1711
-
1712
-
1713
-
1714
- if (this.slothlet.handlers.apiCacheManager) {
1715
- const deleted = this.slothlet.handlers.apiCacheManager.delete(moduleIDKey);
1716
-
1717
-
1718
- if (deleted) {
1719
- this.slothlet.debug("cache", {
1720
- key: "DEBUG_MODE_CACHE_DELETED_MODULE_REMOVED",
1721
- moduleID: moduleIDKey
1722
- });
1723
- }
1724
- }
1725
-
1726
-
1727
-
1728
-
1729
- if (recordHistory && pathsToDelete.length > 0) {
1730
- const rootSegment = pathsToDelete[0].split(".")[0];
1731
- this.state.operationHistory.push({
1732
- type: "remove",
1733
- apiPath: rootSegment
1734
- });
1735
- }
1736
-
1737
- return pathsToDelete.length > 0 || pathsToRollback.length > 0;
1738
- }
1739
-
1740
-
1741
-
1742
- if (!apiPath) {
1743
- throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID", {
1744
- apiPath,
1745
- reason: translate("API_PATH_REASON_REQUIRED"),
1746
- index: undefined,
1747
- segment: undefined,
1748
- validationError: true
1749
- });
1750
- }
1751
-
1752
- const { apiPath: normalizedPath, parts } = this.normalizeApiPath(apiPath);
1753
- const ownershipResult = this.slothlet.handlers.ownership?.removePath?.(normalizedPath, null) || {
1754
- action: "none",
1755
- removedModuleId: null,
1756
- restoreModuleId: null
1757
- };
1758
-
1759
-
1760
- const pathExists = this.getValueAtPath(this.slothlet.api, parts) !== undefined;
1761
-
1762
- if (ownershipResult.action === "none") {
1763
- if (pathExists) {
1764
- await this.deletePath(this.slothlet.api, parts);
1765
- await this.deletePath(this.slothlet.boundApi, parts);
1766
-
1767
-
1768
-
1769
- if (this.slothlet.handlers.metadata) {
1770
- this.slothlet.handlers.metadata.removeUserMetadataByApiPath(normalizedPath);
1771
- }
1772
-
1773
-
1774
-
1775
- if (recordHistory) {
1776
- this.state.operationHistory.push({
1777
- type: "remove",
1778
- apiPath: normalizedPath
1779
- });
1780
- }
1781
- return true;
1782
- }
1783
-
1784
- return false;
1785
- }
1786
-
1787
-
1788
- if (ownershipResult.action === "delete") {
1789
- await this.deletePath(this.slothlet.api, parts);
1790
- await this.deletePath(this.slothlet.boundApi, parts);
1791
-
1792
- if (this.slothlet.handlers.metadata) {
1793
- this.slothlet.handlers.metadata.removeUserMetadataByApiPath(normalizedPath);
1794
- }
1795
-
1796
- if (recordHistory) {
1797
- this.state.operationHistory.push({
1798
- type: "remove",
1799
- apiPath: normalizedPath
1800
- });
1801
- }
1802
- return true;
1803
- }
1804
- if (ownershipResult.action === "restore") {
1805
- const restoredValue = this.slothlet.handlers.ownership?.getCurrentValue?.(normalizedPath);
1806
- const restoredModuleId = this.slothlet.handlers.ownership?.getCurrentOwner?.(normalizedPath)?.moduleID;
1807
- if (restoredValue !== undefined && restoredModuleId) {
1808
- await this.setValueAtPath(this.slothlet.api, parts, restoredValue, {
1809
- mutateExisting: true,
1810
- allowOverwrite: true,
1811
- collisionMode: "replace",
1812
- moduleID: restoredModuleId
1813
- });
1814
- await this.setValueAtPath(this.slothlet.boundApi, parts, restoredValue, {
1815
- mutateExisting: true,
1816
- allowOverwrite: true,
1817
- collisionMode: "replace",
1818
- moduleID: restoredModuleId
1819
- });
1820
-
1821
- if (recordHistory) {
1822
- this.state.operationHistory.push({
1823
- type: "remove",
1824
- apiPath: normalizedPath
1825
- });
1826
- }
1827
- return true;
1828
- }
1829
- await this.restoreApiPath(normalizedPath, ownershipResult.restoreModuleId);
1830
-
1831
- if (recordHistory) {
1832
- this.state.operationHistory.push({
1833
- type: "remove",
1834
- apiPath: normalizedPath
1835
- });
1836
- }
1837
- return true;
1838
- }
1839
-
1840
-
1841
-
1842
- return false;
1843
- }
1844
-
1845
-
1846
- async reloadApiComponent(params) {
1847
- const { apiPath, moduleID, options } = params || {};
1848
- if (!this.slothlet || !this.slothlet.isLoaded) {
1849
- throw new this.SlothletError("INVALID_CONFIG_NOT_LOADED", {
1850
- operation: "reloadApi",
1851
- validationError: true
1852
- });
1853
- }
1854
-
1855
-
1856
- if (moduleID) {
1857
- await this._reloadByModuleID(moduleID);
1858
- return;
1859
- }
1860
-
1861
-
1862
- if (apiPath) {
1863
- await this._reloadByApiPath(apiPath, options);
1864
- return;
1865
- }
1866
-
1867
- throw new this.SlothletError("INVALID_ARGUMENT", {
1868
- argument: "params",
1869
- expected: "{ moduleID } or { apiPath }",
1870
- received: params,
1871
- validationError: true
1872
- });
1873
- }
1874
-
1875
-
1876
- async _reloadByModuleID(moduleID, { forceReplace = true } = {}) {
1877
- const cacheManager = this.slothlet.handlers.apiCacheManager;
1878
- if (!cacheManager) {
1879
- throw new this.SlothletError("CACHE_MANAGER_NOT_AVAILABLE", {
1880
- operation: "reload",
1881
- validationError: true
1882
- });
1883
- }
1884
-
1885
-
1886
- if (!cacheManager.has(moduleID)) {
1887
- throw new this.SlothletError("CACHE_NOT_FOUND", {
1888
- moduleID,
1889
- operation: "reload",
1890
- validationError: true
1891
- });
1892
- }
1893
-
1894
-
1895
- const oldEntry = cacheManager.get(moduleID);
1896
-
1897
- this.slothlet.debug("reload", {
1898
- key: "DEBUG_MODE_RELOADING_MODULE_BY_ID",
1899
- moduleID,
1900
- endpoint: oldEntry.endpoint,
1901
- folderPath: oldEntry.folderPath
1902
- });
1903
-
1904
-
1905
- const freshApi = await cacheManager.rebuildCache(moduleID);
1906
-
1907
-
1908
- cacheManager.set(moduleID, {
1909
- ...oldEntry,
1910
- api: freshApi,
1911
- timestamp: Date.now()
1912
- });
1913
-
1914
-
1915
- this.slothlet.debug("reload", {
1916
- key: "DEBUG_MODE_FRESH_API_KEYS_BEFORE_RESTORE",
1917
- moduleID,
1918
- endpoint: oldEntry.endpoint,
1919
-
1920
-
1921
- freshApiKeys: Object.keys(freshApi || {})
1922
- });
1923
-
1924
-
1925
- await this._restoreApiTree(freshApi, oldEntry.endpoint, moduleID, oldEntry.collisionMode, forceReplace);
1926
-
1927
-
1928
- this.slothlet.debug("reload", {
1929
- key: "DEBUG_MODE_FRESH_API_KEYS_AFTER_RESTORE",
1930
- moduleID,
1931
- endpoint: oldEntry.endpoint,
1932
-
1933
-
1934
- freshApiKeys: Object.keys(freshApi || {})
1935
- });
1936
-
1937
- this.slothlet.debug("reload", {
1938
- key: "DEBUG_MODE_MODULE_RELOAD_COMPLETE",
1939
- moduleID
1940
- });
1941
-
1942
-
1943
- if (this.slothlet.handlers.versionManager) {
1944
- this.slothlet.handlers.versionManager.onVersionedModuleReload(moduleID);
1945
- }
1946
- }
1947
-
1948
-
1949
- async _reloadByApiPath(apiPath, options = {}) {
1950
- this.slothlet.debug("reload", {
1951
- key: "DEBUG_MODE_RELOADING_BY_API_PATH",
1952
- apiPath
1953
- });
1954
-
1955
-
1956
- const moduleIDsToReload = this._findAffectedCaches(apiPath);
1957
-
1958
-
1959
-
1960
-
1961
- if (moduleIDsToReload.length === 0) {
1962
- this.slothlet.debug("reload", {
1963
- key: "DEBUG_MODE_NO_CACHES_ATTEMPTING_RESTORE",
1964
- apiPath
1965
- });
1966
-
1967
- if (apiPath !== "." && apiPath !== "") {
1968
- await this.restoreApiPath(apiPath, "base");
1969
- }
1970
- return;
1971
- }
1972
-
1973
-
1974
-
1975
-
1976
-
1977
- const cacheManager = this.slothlet.handlers.apiCacheManager;
1978
- moduleIDsToReload.sort((a, b) => {
1979
- const entryA = cacheManager.get(a);
1980
- const entryB = cacheManager.get(b);
1981
-
1982
-
1983
-
1984
-
1985
- if (entryA?.endpoint === "." && entryB?.endpoint !== ".") return -1;
1986
- if (entryB?.endpoint === "." && entryA?.endpoint !== ".") return 1;
1987
-
1988
-
1989
- const indexA = this.state.addHistory.findIndex((h) => h.moduleID === a);
1990
- const indexB = this.state.addHistory.findIndex((h) => h.moduleID === b);
1991
- return indexA - indexB;
1992
- });
1993
-
1994
-
1995
-
1996
-
1997
-
1998
- const endpointOrder = new Map();
1999
- for (const moduleID of moduleIDsToReload) {
2000
- const entry = cacheManager.get(moduleID);
2001
-
2002
-
2003
- const ep = entry?.endpoint ?? ".";
2004
- if (!endpointOrder.has(ep)) endpointOrder.set(ep, []);
2005
- endpointOrder.get(ep).push(moduleID);
2006
- }
2007
-
2008
- for (const [, moduleIDs] of endpointOrder) {
2009
- for (let i = 0; i < moduleIDs.length; i++) {
2010
- await this._reloadByModuleID(moduleIDs[i], { forceReplace: i === 0 });
2011
- }
2012
- }
2013
-
2014
-
2015
-
2016
-
2017
- const reloadMetadata = options?.metadata;
2018
- if (reloadMetadata && typeof reloadMetadata === "object" && Object.keys(reloadMetadata).length > 0) {
2019
-
2020
-
2021
- if (this.slothlet.handlers.metadata) {
2022
- const targetPath = apiPath === "." ? null : apiPath.split(".")[0];
2023
- if (targetPath) {
2024
- this.slothlet.handlers.metadata.registerUserMetadata(targetPath, reloadMetadata);
2025
- }
2026
- }
2027
- }
2028
-
2029
- this.slothlet.debug("reload", {
2030
- key: "DEBUG_MODE_API_PATH_RELOAD_COMPLETE",
2031
- apiPath,
2032
- reloadedModules: moduleIDsToReload.length,
2033
- loadOrder: moduleIDsToReload
2034
- });
2035
- }
2036
-
2037
-
2038
- _findAffectedCaches(apiPath) {
2039
- const cacheManager = this.slothlet.handlers.apiCacheManager;
2040
-
2041
-
2042
- if (!cacheManager) return [];
2043
-
2044
- const allModuleIDs = cacheManager.getAllModuleIDs();
2045
-
2046
-
2047
- if (apiPath === "." || apiPath === "" || apiPath == null) {
2048
- const baseModules = [];
2049
- for (const moduleID of allModuleIDs) {
2050
- const entry = cacheManager.get(moduleID);
2051
- if (entry && entry.endpoint === ".") {
2052
- baseModules.push(moduleID);
2053
- }
2054
- }
2055
- return baseModules;
2056
- }
2057
-
2058
-
2059
- const exactMatches = [];
2060
- for (const moduleID of allModuleIDs) {
2061
- const entry = cacheManager.get(moduleID);
2062
- if (entry && entry.endpoint === apiPath) {
2063
- exactMatches.push(moduleID);
2064
- }
2065
- }
2066
- if (exactMatches.length > 0) return exactMatches;
2067
-
2068
-
2069
- const children = [];
2070
- const pathPrefix = apiPath + ".";
2071
- for (const moduleID of allModuleIDs) {
2072
- const entry = cacheManager.get(moduleID);
2073
- if (entry?.endpoint?.startsWith(pathPrefix)) {
2074
- children.push(moduleID);
2075
- }
2076
- }
2077
- if (children.length > 0) return children;
2078
-
2079
-
2080
- const ownership = this.slothlet.handlers.ownership;
2081
- const history = ownership?.getPathHistory?.(apiPath);
2082
- if (history && history.length > 0) {
2083
- const owned = [];
2084
- for (const { moduleID } of history) {
2085
-
2086
-
2087
- if (cacheManager.has(moduleID)) {
2088
- owned.push(moduleID);
2089
- }
2090
- }
2091
-
2092
-
2093
- if (owned.length > 0) return owned;
2094
- }
2095
-
2096
-
2097
-
2098
-
2099
- let bestMatch = null;
2100
- let bestLength = -1;
2101
- for (const moduleID of allModuleIDs) {
2102
- const entry = cacheManager.get(moduleID);
2103
-
2104
-
2105
- if (!entry?.endpoint) continue;
2106
-
2107
- const ep = entry.endpoint;
2108
-
2109
-
2110
-
2111
-
2112
- if (ep === "." || apiPath.startsWith(ep + ".")) {
2113
-
2114
-
2115
- if (ep.length > bestLength) {
2116
- bestLength = ep.length;
2117
- bestMatch = moduleID;
2118
- }
2119
- }
2120
- }
2121
-
2122
-
2123
- if (bestMatch) return [bestMatch];
2124
-
2125
-
2126
-
2127
- return [];
2128
- }
2129
-
2130
-
2131
- _collectCustomProperties(existingProxy, freshApi) {
2132
- const customProps = {};
2133
-
2134
-
2135
-
2136
- if (!existingProxy || (typeof existingProxy !== "object" && typeof existingProxy !== "function")) {
2137
- return customProps;
2138
- }
2139
-
2140
-
2141
- const wrapper = resolveWrapper(existingProxy);
2142
-
2143
-
2144
- if (!wrapper) {
2145
- return customProps;
2146
- }
2147
-
2148
-
2149
-
2150
-
2151
-
2152
- const freshKeys = new Set(freshApi ? Object.keys(freshApi) : []);
2153
-
2154
-
2155
-
2156
-
2157
- const ownKeys = Object.keys(wrapper).filter((k) => !ComponentBase.INTERNAL_KEYS.has(k));
2158
-
2159
- for (const key of ownKeys) {
2160
- try {
2161
-
2162
- const val = wrapper[key];
2163
-
2164
-
2165
-
2166
- if (val && (typeof val === "object" || typeof val === "function") && resolveWrapper(val)) {
2167
- continue;
2168
- }
2169
-
2170
-
2171
-
2172
- if (!freshKeys.has(key)) {
2173
-
2174
- customProps[key] = val;
2175
- } else {
2176
-
2177
-
2178
- customProps[key] = val;
2179
- }
2180
- } catch {
2181
-
2182
- }
2183
- }
2184
-
2185
- return customProps;
2186
- }
2187
-
2188
-
2189
- _restoreCustomProperties(proxy, customProps) {
2190
-
2191
-
2192
-
2193
- if (!proxy || !customProps || typeof customProps !== "object") {
2194
- return;
2195
- }
2196
-
2197
- for (const [key, value] of Object.entries(customProps)) {
2198
- try {
2199
- proxy[key] = value;
2200
- } catch {
2201
-
2202
- }
2203
- }
2204
- }
2205
-
2206
-
2207
- async _restoreApiTree(freshApi, endpoint, moduleID, collisionMode, forceReplace = true) {
2208
-
2209
-
2210
-
2211
- if (!freshApi || (typeof freshApi !== "object" && typeof freshApi !== "function")) {
2212
- return;
2213
- }
2214
-
2215
-
2216
- const parts = endpoint === "." ? [] : endpoint.split(".");
2217
-
2218
- if (parts.length === 0) {
2219
-
2220
-
2221
-
2222
- for (const key of Object.keys(freshApi)) {
2223
-
2224
-
2225
-
2226
- if (typeof key === "string" && (key.startsWith("_") || key.startsWith("__"))) continue;
2227
-
2228
-
2229
-
2230
- if (key === "slothlet" || key === "shutdown" || key === "destroy") continue;
2231
-
2232
- const existingAtKey = this.slothlet.api[key];
2233
- const freshValue = freshApi[key];
2234
-
2235
- if (existingAtKey && resolveWrapper(existingAtKey) !== null) {
2236
-
2237
- const customProps = this._collectCustomProperties(existingAtKey, freshValue);
2238
-
2239
-
2240
-
2241
- const freshWrapper = resolveWrapper(freshValue);
2242
- const isLazyFresh =
2243
- freshWrapper &&
2244
- freshWrapper.____slothletInternal.mode === "lazy" &&
2245
- !freshWrapper.____slothletInternal.state.materialized &&
2246
- typeof freshWrapper.____slothletInternal.materializeFunc === "function";
2247
-
2248
-
2249
- this.slothlet.debug("reload", {
2250
- key: "DEBUG_MODE_RESTORE_ROOT_KEY_INSPECT",
2251
- rootKey: key,
2252
- hasFreshWrapper: !!freshWrapper,
2253
- freshMode: freshWrapper?.____slothletInternal.mode,
2254
- freshMaterialized: freshWrapper?.____slothletInternal.state?.materialized,
2255
- hasMaterializeFunc: typeof freshWrapper?.____slothletInternal.materializeFunc === "function",
2256
- isLazyFresh,
2257
- existingMaterialized: resolveWrapper(existingAtKey)?.____slothletInternal?.state?.materialized
2258
- });
2259
-
2260
- if (isLazyFresh) {
2261
-
2262
-
2263
-
2264
-
2265
- resolveWrapper(existingAtKey).___resetLazy(freshWrapper.____slothletInternal.materializeFunc);
2266
-
2267
-
2268
- this._restoreCustomProperties(existingAtKey, customProps);
2269
-
2270
- this.slothlet.debug("reload", {
2271
- key: "DEBUG_MODE_ROOT_KEY_RESET_LAZY",
2272
- rootKey: key,
2273
- restoredCustomProps: Object.keys(customProps)
2274
- });
2275
- } else {
2276
-
2277
-
2278
-
2279
-
2280
-
2281
-
2282
-
2283
-
2284
- let implForReload;
2285
-
2286
-
2287
-
2288
- if (freshValue && resolveWrapper(freshValue) !== null) {
2289
- implForReload = freshWrapper ? UnifiedWrapper._extractFullImpl(freshWrapper) : freshValue;
2290
- } else {
2291
-
2292
-
2293
-
2294
-
2295
- implForReload = freshValue;
2296
- }
2297
-
2298
-
2299
- if (typeof implForReload === "function") {
2300
- const extracted = {};
2301
- for (const k of Object.keys(implForReload)) {
2302
- extracted[k] = implForReload[k];
2303
- }
2304
- implForReload = extracted;
2305
- }
2306
-
2307
-
2308
-
2309
-
2310
- const wrapper = resolveWrapper(existingAtKey);
2311
-
2312
-
2313
- const originalCollisionMode = wrapper ? wrapper.____slothletInternal.state.collisionMode : null;
2314
- if (forceReplace && wrapper) {
2315
- wrapper.____slothletInternal.state.collisionMode = "replace";
2316
- }
2317
-
2318
-
2319
-
2320
-
2321
- if (wrapper && originalCollisionMode !== null) {
2322
- wrapper.____slothletInternal.state.collisionMode = originalCollisionMode;
2323
- }
2324
-
2325
-
2326
- this._restoreCustomProperties(existingAtKey, customProps);
2327
-
2328
- this.slothlet.debug("reload", {
2329
- key: "DEBUG_MODE_ROOT_KEY_UPDATED_SETIMPL",
2330
- rootKey: key,
2331
- restoredCustomProps: Object.keys(customProps)
2332
- });
2333
- }
2334
- } else if (existingAtKey === undefined) {
2335
-
2336
- const cacheManager = this.slothlet.handlers.apiCacheManager;
2337
- const cacheEntry = cacheManager.get(moduleID);
2338
-
2339
-
2340
- const resolvedFolderPath = cacheEntry?.folderPath || "";
2341
-
2342
- await this.setValueAtPath(this.slothlet.api, [key], freshValue, {
2343
- mutateExisting: true,
2344
- collisionMode,
2345
- moduleID,
2346
- sourceFolder: resolvedFolderPath
2347
- });
2348
-
2349
-
2350
-
2351
- if (this.slothlet.boundApi) {
2352
- await this.setValueAtPath(this.slothlet.boundApi, [key], freshValue, {
2353
- mutateExisting: true,
2354
- collisionMode,
2355
- moduleID,
2356
- sourceFolder: resolvedFolderPath
2357
- });
2358
- }
2359
- }
2360
-
2361
- }
2362
- } else {
2363
-
2364
-
2365
- const existing = this.getValueAtPath(this.slothlet.api, parts);
2366
-
2367
- this.slothlet.debug("reload", {
2368
- key: "DEBUG_MODE_RESTORE_NESTED_PATH",
2369
- endpoint,
2370
- moduleID,
2371
- partsPath: parts.join("."),
2372
- existingFound: !!existing,
2373
-
2374
-
2375
-
2376
- hasSetImpl: existing ? resolveWrapper(existing) !== null : false,
2377
- freshApiKeys: Object.keys(freshApi || {})
2378
-
2379
- });
2380
-
2381
- if (existing && resolveWrapper(existing) !== null) {
2382
-
2383
- const customProps = this._collectCustomProperties(existing, freshApi);
2384
-
2385
-
2386
-
2387
-
2388
- const wrapper = resolveWrapper(existing);
2389
-
2390
-
2391
- const originalCollisionMode = wrapper ? wrapper.____slothletInternal.state.collisionMode : null;
2392
-
2393
- if (forceReplace && wrapper) {
2394
- wrapper.____slothletInternal.state.collisionMode = "replace";
2395
- this.slothlet.debug("reload", {
2396
- key: "DEBUG_MODE_RESTORE_FORCING_REPLACE",
2397
- endpoint,
2398
- originalCollisionMode,
2399
- wrapperApiPath: wrapper.____slothletInternal.apiPath
2400
- });
2401
- }
2402
-
2403
-
2404
-
2405
-
2406
-
2407
-
2408
- let implForReload;
2409
-
2410
-
2411
-
2412
-
2413
-
2414
-
2415
- if (resolveWrapper(freshApi) !== null) {
2416
- const freshWrapper = resolveWrapper(freshApi);
2417
- implForReload = freshWrapper ? UnifiedWrapper._extractFullImpl(freshWrapper) : freshApi;
2418
- } else if (typeof freshApi === "function") {
2419
- implForReload = {};
2420
- for (const key of Object.keys(freshApi)) {
2421
- implForReload[key] = freshApi[key];
2422
- }
2423
- } else {
2424
- implForReload = freshApi;
2425
- }
2426
-
2427
-
2428
-
2429
-
2430
-
2431
-
2432
-
2433
-
2434
- if (parts.length > 0 && implForReload && typeof implForReload === "object") {
2435
- const lastEndpointPart = parts[parts.length - 1];
2436
- if (lastEndpointPart && Object.prototype.hasOwnProperty.call(implForReload, lastEndpointPart)) {
2437
- const dupValue = implForReload[lastEndpointPart];
2438
- const dupWrapperForDedup = resolveWrapper(dupValue);
2439
- if (dupWrapperForDedup) {
2440
- const hoisted = {};
2441
- for (const k of Object.keys(implForReload)) {
2442
- if (k !== lastEndpointPart) hoisted[k] = implForReload[k];
2443
- }
2444
- for (const k of Object.keys(dupWrapperForDedup).filter((k) => !k.startsWith("_") && !k.startsWith("__"))) {
2445
- hoisted[k] = dupWrapperForDedup[k];
2446
- }
2447
- implForReload = hoisted;
2448
- }
2449
- }
2450
- }
2451
-
2452
-
2453
-
2454
-
2455
-
2456
-
2457
-
2458
-
2459
-
2460
- if (implForReload && typeof implForReload === "object") {
2461
- for (const key of Object.keys(implForReload)) {
2462
- const val = implForReload[key];
2463
- if (resolveWrapper(val) !== null) {
2464
- const childWrapper = resolveWrapper(val);
2465
- if (childWrapper.____slothletInternal.state.materialized) {
2466
- implForReload[key] = UnifiedWrapper._extractFullImpl(childWrapper);
2467
- }
2468
- }
2469
- }
2470
- }
2471
-
2472
- resolveWrapper(existing).___setImpl(implForReload, moduleID);
2473
-
2474
-
2475
-
2476
-
2477
-
2478
- if (wrapper && originalCollisionMode !== null) {
2479
- wrapper.____slothletInternal.state.collisionMode = originalCollisionMode;
2480
- }
2481
-
2482
-
2483
- this._restoreCustomProperties(existing, customProps);
2484
-
2485
- this.slothlet.debug("reload", {
2486
- key: "DEBUG_MODE_UPDATED_WRAPPER_IMPL",
2487
- endpoint,
2488
- moduleID,
2489
- forcedReplaceMode: true,
2490
- restoredCustomProps: Object.keys(customProps)
2491
- });
2492
- } else {
2493
-
2494
-
2495
- const cacheManager = this.slothlet.handlers.apiCacheManager;
2496
- const cacheEntry = cacheManager.get(moduleID);
2497
-
2498
-
2499
- const resolvedFolderPath = cacheEntry?.folderPath || "";
2500
-
2501
-
2502
- let implForContainer = freshApi;
2503
-
2504
-
2505
-
2506
-
2507
-
2508
-
2509
- if (typeof freshApi === "function") {
2510
- implForContainer = {};
2511
- for (const key of Object.keys(freshApi)) {
2512
- implForContainer[key] = freshApi[key];
2513
- }
2514
- }
2515
- const containerWrapper = new UnifiedWrapper(this.slothlet, {
2516
- apiPath: endpoint,
2517
- mode: this.____config.mode,
2518
- moduleID: moduleID,
2519
- filePath: resolvedFolderPath,
2520
- sourceFolder: resolvedFolderPath
2521
- });
2522
- containerWrapper.___setImpl(implForContainer, moduleID);
2523
- const apiToSet = containerWrapper.createProxy();
2524
-
2525
-
2526
- await this.setValueAtPath(this.slothlet.api, parts, apiToSet, {
2527
- mutateExisting: true,
2528
- collisionMode,
2529
- moduleID,
2530
- sourceFolder: resolvedFolderPath
2531
- });
2532
-
2533
-
2534
-
2535
- if (this.slothlet.boundApi) {
2536
- await this.setValueAtPath(this.slothlet.boundApi, parts, apiToSet, {
2537
- mutateExisting: true,
2538
- collisionMode,
2539
- moduleID,
2540
- sourceFolder: resolvedFolderPath
2541
- });
2542
- }
2543
-
2544
- this.slothlet.debug("reload", {
2545
- key: "DEBUG_MODE_CREATED_NEW_WRAPPER_UNEXPECTED",
2546
- endpoint,
2547
- moduleID
2548
- });
2549
- }
2550
- }
2551
- }
2552
- }
17
+ import fs from"node:fs/promises";import path from"node:path";import{translate}from"@cldmv/slothlet/i18n";import{ComponentBase}from"@cldmv/slothlet/factories/component-base";import{UnifiedWrapper,resolveWrapper}from"@cldmv/slothlet/handlers/unified-wrapper";class ApiManager extends ComponentBase{static slothletProperty="apiManager";constructor(slothlet){super(slothlet);this.state={addHistory:[],initialConfig:slothlet?.config||null,operationHistory:[]}}normalizeApiPath(apiPath){if(apiPath===""||apiPath===null||apiPath===void 0){return{apiPath:"",parts:[]}}if(Array.isArray(apiPath)){if(apiPath.length===0){return{apiPath:"",parts:[]}}for(let i=0;i<apiPath.length;i++){if(typeof apiPath[i]!=="string"){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath,segment:apiPath[i],index:i,reason:translate("API_PATH_REASON_ARRAY_ELEMENTS"),validationError:true})}if(apiPath[i].trim()===""){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath,segment:apiPath[i],index:i,reason:translate("API_PATH_REASON_ARRAY_EMPTY_SEGMENTS"),validationError:true})}}if(apiPath[0]==="slothlet"||apiPath.length===1&&(apiPath[0]==="shutdown"||apiPath[0]==="destroy")){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath,reason:translate("API_PATH_REASON_RESERVED_NAME"),index:void 0,segment:void 0,validationError:true})}return{apiPath:apiPath.join("."),parts:apiPath}}if(typeof apiPath!=="string"){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath,reason:translate("API_PATH_REASON_INVALID_TYPE"),index:void 0,segment:void 0,validationError:true})}const normalized=apiPath.trim();if(normalized===""){return{apiPath:"",parts:[]}}const parts=normalized.split(".");if(parts.length===0||parts.some(part=>part.trim()==="")){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath:normalized,reason:translate("API_PATH_REASON_EMPTY_SEGMENTS"),index:void 0,segment:void 0,validationError:true})}if(parts[0]==="slothlet"||normalized==="shutdown"||normalized==="destroy"){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath:normalized,reason:translate("API_PATH_REASON_RESERVED_NAME"),index:void 0,segment:void 0,validationError:true})}return{apiPath:normalized,parts}}async resolvePath(inputPath){if(!inputPath||typeof inputPath!=="string"){throw new this.SlothletError("INVALID_CONFIG_DIR_INVALID",{dir:inputPath,validationError:true})}const resolvedPath=this.slothlet.helpers.resolver.resolvePathFromCaller(inputPath);try{const stats=await fs.stat(resolvedPath);return{resolvedPath,isDirectory:stats.isDirectory(),isFile:stats.isFile()}}catch(error){if(error instanceof this.SlothletError){throw error}throw new this.SlothletError("INVALID_CONFIG_DIR_INVALID",{dir:resolvedPath,validationError:true})}}async resolveFolderPath(folderPath){if(!folderPath||typeof folderPath!=="string"){throw new this.SlothletError("INVALID_CONFIG_DIR_INVALID",{dir:folderPath,validationError:true})}const resolvedPath=this.slothlet.helpers.resolver.resolvePathFromCaller(folderPath);try{const stats=await fs.stat(resolvedPath);if(!stats.isDirectory()){throw new this.SlothletError("INVALID_CONFIG_DIR_INVALID",{dir:resolvedPath,validationError:true})}}catch(error){if(error instanceof this.SlothletError){throw error}throw new this.SlothletError("INVALID_CONFIG_DIR_INVALID",{dir:resolvedPath,validationError:true})}return resolvedPath}buildDefaultModuleId(apiPath,____resolvedFolderPath){const randomSuffix=Math.random().toString(36).substring(2,8);const prefix=apiPath||"auto";return`${prefix}_${randomSuffix}`}getValueAtPath(root,parts){let current=root;for(const part of parts){if(!current||typeof current!=="object"&&typeof current!=="function"){return void 0}current=current[part]}return current}ensureParentPath(root,parts,options={}){const{moduleID,sourceFolder}=options;let current=root;for(let i=0;i<parts.length-1;i+=1){const part=parts[i];const next=current[part];if(next===void 0){const containerPath=parts.slice(0,i+1).join(".");const containerWrapper=new UnifiedWrapper(this.slothlet,{mode:this.____config.mode,apiPath:containerPath,moduleID,sourceFolder});containerWrapper.___setImpl({},moduleID);current[part]=containerWrapper.createProxy();current=current[part];continue}if(next&&(typeof next==="object"||typeof next==="function")){current=next;continue}throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath:parts.slice(0,i+1).join("."),reason:translate("API_PATH_REASON_NOT_TRAVERSABLE"),index:void 0,segment:void 0,validationError:true})}return current}isWrapperProxy(value){return!!(value&&(typeof value==="object"||typeof value==="function")&&resolveWrapper(value)!==null)}async syncWrapper(existingProxy,nextProxy,config,collisionMode="replace",moduleID=null){if(config?.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_SYNC_WRAPPER_ENTRY_EXISTING",apiPath:resolveWrapper(existingProxy)?.apiPath});this.slothlet.debug("api",{key:"DEBUG_MODE_SYNC_WRAPPER_ENTRY_NEXT",apiPath:resolveWrapper(nextProxy)?.apiPath})}if(!this.isWrapperProxy(existingProxy)||!this.isWrapperProxy(nextProxy)){return false}const existingWrapper=resolveWrapper(existingProxy)??existingProxy;const nextWrapper=resolveWrapper(nextProxy)??nextProxy;if(config?.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_SYNC_WRAPPER_EXISTING",apiPath:existingWrapper.apiPath});this.slothlet.debug("api",{key:"DEBUG_MODE_SYNC_WRAPPER_NEXT",apiPath:nextWrapper.apiPath})}if(nextWrapper.____slothletInternal.materializeFunc&&collisionMode!=="merge"){existingWrapper.____slothletInternal.materializeFunc=nextWrapper.____slothletInternal.materializeFunc}const existingChildKeys=Object.keys(existingWrapper).filter(k=>!k.startsWith("_")&&!k.startsWith("__"));const nextChildKeys=Object.keys(nextWrapper).filter(k=>!k.startsWith("_")&&!k.startsWith("__"));if(config?.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_SYNC_WRAPPER_BEFORE_MERGE",existingCacheSize:existingChildKeys.length,nextCacheSize:nextChildKeys.length});this.slothlet.debug("api",{key:"DEBUG_MODE_SYNC_WRAPPER_NEXT_IMPL_KEYS",implKeys:Object.keys(nextWrapper.____slothletInternal.impl||{})});this.slothlet.debug("api",{key:"DEBUG_MODE_SYNC_WRAPPER_NEXT_CHILDCACHE_KEYS",childCacheKeys:nextChildKeys})}if(collisionMode==="replace"){if(existingWrapper.___setImpl&&nextWrapper.____slothletInternal.impl!==void 0){existingWrapper.___setImpl(nextWrapper.____slothletInternal.impl,moduleID)}else if(nextWrapper.____slothletInternal.impl===void 0){existingWrapper.____slothletInternal.impl=null}else{if(nextWrapper.____slothletInternal.impl!==void 0){existingWrapper.____slothletInternal.impl=nextWrapper.____slothletInternal.impl;if(typeof nextWrapper.____slothletInternal.impl==="function"||nextWrapper.____slothletInternal.impl&&typeof nextWrapper.____slothletInternal.impl.default==="function"){existingWrapper.isCallable=true}}}for(const key of existingChildKeys){delete existingWrapper[key]}existingWrapper.___adoptImplChildren();for(const key of nextChildKeys){const childValue=nextWrapper[key];Object.defineProperty(existingWrapper,key,{value:childValue,writable:false,enumerable:true,configurable:true})}}else if(collisionMode==="merge"){for(const key of nextChildKeys){const isInternal=typeof key==="string"&&(key.startsWith("_")||key.startsWith("__"));if(!isInternal&&!Object.prototype.hasOwnProperty.call(existingWrapper,key)){const childValue=nextWrapper[key];Object.defineProperty(existingWrapper,key,{value:childValue,writable:false,enumerable:true,configurable:true})}else if(!isInternal){const existingChild=existingWrapper[key];const nextChild=nextWrapper[key];if(this.isWrapperProxy(existingChild)&&this.isWrapperProxy(nextChild)){const syncWrapper_nextChildWrapper=resolveWrapper(nextChild)??nextChild;const syncWrapper_hasGrandChildren=Object.keys(syncWrapper_nextChildWrapper).some(k=>!k.startsWith("_")&&!k.startsWith("__"));if(syncWrapper_hasGrandChildren){await this.syncWrapper(existingChild,nextChild,config,collisionMode,moduleID)}}}}}else if(collisionMode==="merge-replace"){for(const key of nextChildKeys){const childValue=nextWrapper[key];const isInternal=typeof key==="string"&&(key.startsWith("_")||key.startsWith("__"));if(!isInternal&&Object.prototype.hasOwnProperty.call(existingWrapper,key)){const existingChild=existingWrapper[key];if(this.isWrapperProxy(existingChild)&&this.isWrapperProxy(childValue)){await this.syncWrapper(existingChild,childValue,config,collisionMode,moduleID)}else{delete existingWrapper[key];Object.defineProperty(existingWrapper,key,{value:childValue,writable:false,enumerable:true,configurable:true})}}else if(!isInternal){Object.defineProperty(existingWrapper,key,{value:childValue,writable:false,enumerable:true,configurable:true})}}}if(existingWrapper.____slothletInternal.state){const isActuallyMaterialized=existingWrapper.____slothletInternal.impl&&typeof existingWrapper.____slothletInternal.impl!=="function";existingWrapper.____slothletInternal.state.materialized=isActuallyMaterialized;existingWrapper.____slothletInternal.state.inFlight=false}return true}async mutateApiValue(existingValue,nextValue,options,config){if(config?.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_CALLED",existingType:typeof existingValue,nextType:typeof nextValue});this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_WRAPPER_STATUS",existingIsWrapper:this.isWrapperProxy(existingValue),nextIsWrapper:this.isWrapperProxy(nextValue)});this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_NEXT_VALUE",nextValue});this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_NEXT_VALUE_KEYS",nextValueKeys:nextValue?Object.keys(nextValue):[]})}if(existingValue===nextValue){return}if(this.isWrapperProxy(existingValue)&&this.isWrapperProxy(nextValue)){if(config?.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_SYNC_WRAPPERS"})}await this.syncWrapper(existingValue,nextValue,config,options.collisionMode,options.moduleID);return}if(this.isWrapperProxy(existingValue)&&!this.isWrapperProxy(nextValue)){const nextIsObjectLike=nextValue&&(typeof nextValue==="object"||typeof nextValue==="function");const nextHasKeys=nextIsObjectLike&&Object.keys(nextValue).length>0;if(nextHasKeys){if(config?.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_MERGE_INTO_WRAPPER"});this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_MERGE_KEYS",keys:Object.keys(nextValue)})}await this.slothlet.builders.apiAssignment.mergeApiObjects(existingValue,nextValue,{removeMissing:options.removeMissing,mutateExisting:true,allowOverwrite:true,syncWrapper:this.syncWrapper.bind(this),collisionMode:options.collisionMode,moduleID:options.moduleID});return}const existingValueRaw=resolveWrapper(existingValue);if(existingValueRaw!==null){if(config?.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_MUTATE_API_VALUE_SETIMPL_FALLBACK"})}existingValueRaw.___setImpl(resolveWrapper(nextValue)?.__impl??nextValue);return}}if(existingValue&&typeof existingValue==="object"&&nextValue&&typeof nextValue==="object"){await this.slothlet.builders.apiAssignment.mergeApiObjects(existingValue,nextValue,{removeMissing:options.removeMissing,mutateExisting:true,allowOverwrite:true,syncWrapper:this.syncWrapper.bind(this),collisionMode:options.collisionMode,moduleID:options.moduleID});return existingValue}return nextValue}async setValueAtPath(root,parts,value,options){const parent=this.ensureParentPath(root,parts,{moduleID:options.moduleID,sourceFolder:options.sourceFolder});const finalKey=parts[parts.length-1];const existing=parent?parent[finalKey]:void 0;const collisionMode=options.collisionMode||"merge";const moduleID=options.moduleID;this.slothlet.debug("api",{key:"DEBUG_MODE_SET_VALUE_AT_PATH",finalKey,existingType:typeof existing,valueType:typeof value,collisionMode,options});if(existing!==void 0){if(collisionMode==="error"){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath:parts.join("."),reason:translate("API_PATH_REASON_COLLISION_ERROR"),index:void 0,segment:void 0,validationError:true})}if(collisionMode==="skip"){this.slothlet.debug("api",{key:"DEBUG_MODE_SET_VALUE_AT_PATH_SKIP_COLLISION",path:parts.join("."),mode:"skip"});return false}if(collisionMode==="warn"){if(this.slothlet&&!this.____config?.silent){new this.SlothletWarning("WARNING_HOT_RELOAD_PATH_COLLISION",{apiPath:parts.join(".")})}return false}if(collisionMode==="replace"){const existingIsObject=typeof existing==="object"||typeof existing==="function";const valueIsObject=typeof value==="object"||typeof value==="function";if(existingIsObject&&valueIsObject){this.slothlet.debug("api",{key:"DEBUG_MODE_SET_VALUE_AT_PATH_REPLACE_MERGE",path:parts.join("."),mode:"replace"});await this.mutateApiValue(existing,value,{removeMissing:false,allowOverwrite:true,collisionMode:"replace",moduleID},this.____config);return true}else{parent[finalKey]=value;return true}}if(collisionMode==="merge"||collisionMode==="merge-replace"){const existingIsObject=typeof existing==="object"||typeof existing==="function";const valueIsObject=typeof value==="object"||typeof value==="function";if(existingIsObject&&valueIsObject){this.slothlet.debug("api",{key:"DEBUG_MODE_SET_VALUE_AT_PATH_MERGE_PROPS",mode:collisionMode});await this.mutateApiValue(existing,value,{removeMissing:false,allowOverwrite:true,collisionMode},this.____config);return true}else{if(this.slothlet&&!this.____config?.silent){new this.SlothletWarning("WARNING_HOT_RELOAD_MERGE_PRIMITIVES",{apiPath:parts.join(".")})}return false}}}this.slothlet.debug("api",{key:"DEBUG_MODE_SET_VALUE_AT_PATH_ASSIGN",finalKey});parent[finalKey]=value;return true}async deletePath(root,parts){let current=root;const stack=[];for(const part of parts.slice(0,-1)){if(!current||typeof current!=="object"&&typeof current!=="function"){return false}stack.push({parent:current,key:part});current=current[part]}const finalKey=parts[parts.length-1];if(!current||typeof current!=="object"&&typeof current!=="function"){return false}if(!Object.prototype.hasOwnProperty.call(current,finalKey)){return false}const removedImpl=current[finalKey];const apiPath=parts.join(".");if(removedImpl&&this.slothlet.handlers?.lifecycle){const metadata=this.slothlet.handlers.metadata?.getMetadata?.(removedImpl);await this.slothlet.handlers.lifecycle.emit("impl:removed",{apiPath,impl:removedImpl,source:"removal",moduleID:metadata?.moduleID,filePath:metadata?.filePath,sourceFolder:metadata?.sourceFolder})}if(resolveWrapper(current)){const wrapper=resolveWrapper(current);const isInternal=typeof finalKey==="string"&&(finalKey.startsWith("_")||finalKey.startsWith("__"));if(!isInternal&&finalKey in wrapper){delete wrapper[finalKey]}if(wrapper.____slothletInternal.impl&&typeof wrapper.____slothletInternal.impl==="object"){delete wrapper.____slothletInternal.impl[finalKey]}}delete current[finalKey];if(removedImpl&&(typeof removedImpl==="object"||typeof removedImpl==="function")){if(resolveWrapper(removedImpl)){const wrapper=resolveWrapper(removedImpl);if(wrapper.____slothletInternal.impl!==void 0){wrapper.____slothletInternal.impl=null}const childKeys=Object.keys(wrapper).filter(k=>!k.startsWith("_")&&!k.startsWith("__"));for(const key of childKeys){delete wrapper[key]}if(wrapper.____slothletInternal.state){wrapper.____slothletInternal.state.materialized=false;wrapper.____slothletInternal.state.inFlight=false}}}if(this.slothlet.handlers?.metadata){const rootSegment=apiPath.split(".")[0];this.slothlet.handlers.metadata.removeUserMetadataByApiPath(rootSegment)}for(let i=stack.length-1;i>=0;i-=1){const{parent,key}=stack[i];const value=parent[key];if(value&&(typeof value==="object"||typeof value==="function")&&Object.keys(value).length===0){delete parent[key]}}return true}async restoreApiPath(apiPath,moduleID){const normalizedModuleId=moduleID||null;const historyEntry=this.state.addHistory.slice().reverse().find(entry=>entry.apiPath===apiPath&&(normalizedModuleId?entry.moduleID===normalizedModuleId:true));if(historyEntry){await this.addApiComponent({apiPath:historyEntry.apiPath,folderPath:historyEntry.folderPath,options:{...historyEntry.options,metadata:historyEntry.metadata,mutateExisting:true,forceOverwrite:true,collisionMode:"replace",recordHistory:false}});return}if(normalizedModuleId==="base"||normalizedModuleId==="core"){const baseApi=await this.slothlet.builders.builder.buildAPI({dir:this.____config.dir,mode:this.____config.mode,moduleID:"base"});const{parts}=this.normalizeApiPath(apiPath);let baseValue=this.getValueAtPath(baseApi,parts);if(baseValue===void 0){await this.deletePath(this.slothlet.api,parts);await this.deletePath(this.slothlet.boundApi,parts);return}const baseValueRaw=resolveWrapper(baseValue);if(baseValue&&baseValueRaw!==null){baseValue=baseValueRaw.__impl}await this.setValueAtPath(this.slothlet.api,parts,baseValue,{mutateExisting:true,allowOverwrite:true,collisionMode:"replace",moduleID:normalizedModuleId});await this.setValueAtPath(this.slothlet.boundApi,parts,baseValue,{mutateExisting:true,allowOverwrite:true,collisionMode:"replace",moduleID:normalizedModuleId})}}#normalizePermissionShorthandEntry(entry){const getValueType=value=>{if(value===null)return"null";if(Array.isArray(value))return"array";return typeof value};if(typeof entry==="string"){return{target:entry,condition:void 0}}if(!entry||typeof entry!=="object"||Array.isArray(entry)){throw new this.SlothletError("INVALID_PERMISSION_RULE",{reason:translate("PERM_RULE_NOT_OBJECT"),received:getValueType(entry),validationError:true})}if(typeof entry.target!=="string"||!entry.target){throw new this.SlothletError("INVALID_PERMISSION_RULE",{reason:translate("PERM_RULE_TARGET_REQUIRED"),received:typeof entry.target,validationError:true})}return{target:entry.target,condition:entry.condition}}async addApiComponent(params){const{apiPath,folderPath,options={},versionConfig=null}=params||{};if(Array.isArray(folderPath)){const moduleIDs=[];for(const singlePath of folderPath){const moduleID2=await this.addApiComponent({apiPath,folderPath:singlePath,options,versionConfig});moduleIDs.push(moduleID2)}return moduleIDs}const{metadata={},...restOptions}=options;if(!this.slothlet||!this.slothlet.isLoaded){throw new this.SlothletError("INVALID_CONFIG_NOT_LOADED",{operation:"addApi",validationError:true})}const{apiPath:normalizedPath,parts}=this.normalizeApiPath(apiPath);let effectivePath=normalizedPath;let effectiveParts=parts;if(versionConfig?.version!==void 0&&versionConfig?.version!==null){if(normalizedPath===""){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath,reason:translate("API_PATH_REASON_VERSIONED_ROOT"),index:void 0,segment:void 0,validationError:true})}if(typeof versionConfig.version!=="string"||!String(versionConfig.version).trim()){throw new this.SlothletError("INVALID_CONFIG_VERSION_TAG",{received:versionConfig.version,validationError:true})}const versionTag=String(versionConfig.version).trim();effectiveParts=[versionTag,...parts];effectivePath=effectiveParts.join(".")}const{resolvedPath,isDirectory,isFile}=await this.resolvePath(folderPath);if(!isDirectory&&!isFile){throw new this.SlothletError("INVALID_CONFIG_PATH_TYPE",{path:resolvedPath,validationError:true})}if(isFile){const ext=path.extname(resolvedPath);if(![".mjs",".cjs",".js"].includes(ext)){throw new this.SlothletError("INVALID_CONFIG_FILE_TYPE",{path:resolvedPath,extension:ext,validationError:true})}}const resolvedFolderPath=resolvedPath;let collisionMode;if(restOptions.forceOverwrite){collisionMode="replace"}else{collisionMode=restOptions.collisionMode||this.____config.api?.collision?.api||"error"}const mutateExisting=!!(restOptions.mutateExisting||collisionMode==="merge");const moduleID=restOptions.moduleID?String(restOptions.moduleID):this.buildDefaultModuleId(normalizedPath,resolvedFolderPath);if(restOptions.forceOverwrite&&!moduleID){throw new this.SlothletError("INVALID_CONFIG_FORCE_OVERWRITE_REQUIRES_MODULE_ID",{apiPath:normalizedPath,validationError:true})}let dirForBuild=resolvedFolderPath;let fileFilter=null;if(isFile){dirForBuild=path.dirname(resolvedFolderPath);const fileName=path.basename(resolvedFolderPath);fileFilter=file=>file===fileName}const newApi=await this.slothlet.builders.builder.buildAPI({dir:dirForBuild,mode:this.____config.mode,apiPathPrefix:effectivePath,collisionContext:"addApi",moduleID,collisionMode,fileFilter});if(this.slothlet.handlers.apiCacheManager){this.slothlet.handlers.apiCacheManager.set(moduleID,{endpoint:effectivePath,moduleID,api:newApi,folderPath:resolvedFolderPath,mode:this.____config.mode,sanitizeOptions:this.____config.sanitize||{},collisionMode,config:{...this.____config},timestamp:Date.now()})}this.slothlet.debug("api",{key:"DEBUG_MODE_ADD_API_COMPONENT_BUILD_RETURN",topLevelKeys:Object.keys(newApi),dottedKeys:Object.keys(newApi).filter(k=>k.includes(".")),wrappers:Object.keys(newApi).filter(k=>resolveWrapper(newApi[k])!==null).map(k=>{const _w=resolveWrapper(newApi[k]);return{key:k,apiPath:_w.apiPath,implKeys:Object.keys(_w.____slothletInternal.impl||{}),childCacheSize:Object.keys(_w).filter(k2=>!k2.startsWith("_")&&!k2.startsWith("__")).length,childCacheKeys:Object.keys(_w).filter(k2=>!k2.startsWith("_")&&!k2.startsWith("__"))}}),nonWrappers:Object.keys(newApi).filter(k=>resolveWrapper(newApi[k])===null).map(k=>({key:k,type:typeof newApi[k]}))});let apiToMerge=newApi;if(isFile&&Object.keys(newApi).length===1){const fileName=Object.keys(newApi)[0];apiToMerge=newApi[fileName]}if(!isFile&&normalizedPath){const lastPart=normalizedPath.includes(".")?normalizedPath.split(".").pop():normalizedPath;if(lastPart&&Object.prototype.hasOwnProperty.call(apiToMerge,lastPart)){const dupValue=apiToMerge[lastPart];const dupType=typeof dupValue;if(dupValue!==null&&(dupType==="object"||dupType==="function")){const dupWrapper=resolveWrapper(dupValue);const dupFilePath=dupWrapper?.____slothletInternal?.filePath;const dupFileDir=dupFilePath?dupFilePath.replace(/\\/g,"/").split("/").slice(0,-1).join("/"):null;const normalizedFolderPath=resolvedFolderPath.replace(/\\/g,"/").replace(/\/$/,"");const expectedDir=normalizedFolderPath+"/"+lastPart;const isDirectChild=dupFileDir===expectedDir||dupFileDir===normalizedFolderPath;if(isDirectChild){const hoisted={};for(const k of Object.keys(apiToMerge)){if(k!==lastPart)hoisted[k]=apiToMerge[k]}if(dupWrapper){for(const k of Object.keys(dupWrapper).filter(k2=>!k2.startsWith("_")&&!k2.startsWith("__"))){hoisted[k]=dupWrapper[k]}}else{for(const k of Object.keys(dupValue)){hoisted[k]=dupValue[k]}}apiToMerge=hoisted;this.slothlet.debug("api",{key:"DEBUG_MODE_RULE_13_DEDUP_HOISTED_KEY",lastPart,newKeys:Object.keys(apiToMerge)})}}}}if(this.____config.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_ADD_API_COMPONENT_MERGE_KEYS",keys:Object.keys(apiToMerge),isRootLevel:parts.length===0})}let anyAssignmentSucceeded=false;if(parts.length===0){for(const key of Object.keys(newApi)){const result1=await this.setValueAtPath(this.slothlet.api,[key],newApi[key],{mutateExisting,collisionMode,moduleID,sourceFolder:resolvedFolderPath});const result2=await this.setValueAtPath(this.slothlet.boundApi,[key],newApi[key],{mutateExisting,collisionMode,moduleID,sourceFolder:resolvedFolderPath});if(result1||result2){anyAssignmentSucceeded=true}}}else{if(resolveWrapper(apiToMerge)===null){const isCallableNamespace=typeof apiToMerge==="function";const containerWrapper=new UnifiedWrapper(this.slothlet,{apiPath:effectivePath,mode:this.____config.mode,isCallable:isCallableNamespace,moduleID,filePath:resolvedFolderPath,sourceFolder:resolvedFolderPath});containerWrapper.___setImpl(apiToMerge,moduleID);apiToMerge=containerWrapper.createProxy()}const result1=await this.setValueAtPath(this.slothlet.api,effectiveParts,apiToMerge,{mutateExisting,collisionMode,moduleID,sourceFolder:resolvedFolderPath});const result2=await this.setValueAtPath(this.slothlet.boundApi,effectiveParts,apiToMerge,{mutateExisting,collisionMode,moduleID,sourceFolder:resolvedFolderPath});if(result1||result2){anyAssignmentSucceeded=true}}if(anyAssignmentSucceeded){const pendingMaterializations=[];const seenWrappers=new Set;const collectPendingMaterializations=(obj,depth=0)=>{if(!obj||typeof obj!=="object"||depth>10)return;if(obj.__isVersionDispatcher===true)return;const wrapper=resolveWrapper(obj);if(wrapper){if(seenWrappers.has(wrapper))return;seenWrappers.add(wrapper);if(wrapper.____slothletInternal.materializationPromise){pendingMaterializations.push(wrapper.____slothletInternal.materializationPromise)}const childKeys=Object.keys(wrapper).filter(k=>!k.startsWith("_")&&!k.startsWith("__"));for(const key of childKeys){collectPendingMaterializations(wrapper[key],depth+1)}}for(const key of Object.keys(obj)){if(key!=="____slothletInternal"){collectPendingMaterializations(obj[key],depth+1)}}};if(effectiveParts.length===0){for(const key of Object.keys(newApi)){if(this.slothlet.api[key]){collectPendingMaterializations(this.slothlet.api[key])}}}else{let current=this.slothlet.api;for(const part of effectiveParts){if(current&&current[part]){current=current[part]}else{break}}if(current){collectPendingMaterializations(current)}}if(pendingMaterializations.length>0){if(this.____config.debug?.api){this.slothlet.debug("api",{key:"DEBUG_MODE_AWAITING_PENDING_MATERIALIZATIONS",count:pendingMaterializations.length,apiPath:normalizedPath})}await Promise.all(pendingMaterializations)}}if(anyAssignmentSucceeded&&metadata&&Object.keys(metadata).length>0&&this.slothlet.handlers.metadata){if(parts.length===0){for(const key of Object.keys(newApi)){this.slothlet.handlers.metadata.registerUserMetadata(key,metadata)}}else{const rootSegment=effectiveParts[0];this.slothlet.handlers.metadata.registerUserMetadata(rootSegment,metadata)}}if(this.slothlet.handlers.ownership&&moduleID){this.slothlet.handlers.ownership.registerSubtree(apiToMerge,moduleID,effectivePath)}if(this.slothlet.handlers.ownership){if(restOptions.recordHistory!==false){this.state.addHistory.push({apiPath:normalizedPath,folderPath:resolvedFolderPath,options:{...restOptions,metadata,moduleID},moduleID,versionConfig:versionConfig||null});this.state.operationHistory.push({type:"add",apiPath:normalizedPath,folderPath:resolvedFolderPath,options:{...restOptions,metadata,moduleID},moduleID,versionConfig:versionConfig||null})}}if(versionConfig?.version&&this.slothlet.handlers.versionManager){const versionTag=String(versionConfig.version).trim();try{this.slothlet.handlers.versionManager.registerVersion(normalizedPath,versionTag,moduleID,versionConfig.metadata??{},versionConfig.default??false)}catch(error){await this._rollbackFailedVersionedAdd({moduleID,effectivePath,normalizedPath});throw error}}if(restOptions.permissions&&this.slothlet.handlers?.permissionManager){const perms=restOptions.permissions;const callerPattern=`${normalizedPath}.**`;if(Array.isArray(perms.deny)){for(const entry of perms.deny){const{target,condition}=this.#normalizePermissionShorthandEntry(entry);this.slothlet.handlers.permissionManager.addRule({caller:callerPattern,target,effect:"deny",condition},moduleID)}}if(Array.isArray(perms.allow)){for(const entry of perms.allow){const{target,condition}=this.#normalizePermissionShorthandEntry(entry);this.slothlet.handlers.permissionManager.addRule({caller:callerPattern,target,effect:"allow",condition},moduleID)}}}return moduleID}async _rollbackFailedVersionedAdd({moduleID,effectivePath,normalizedPath}){let addIndex=-1;for(let i=this.state.operationHistory.length-1;i>=0;i--){const entry=this.state.operationHistory[i];if(entry?.type==="add"&&entry?.apiPath===normalizedPath&&entry?.moduleID===moduleID){addIndex=i;break}}if(addIndex!==-1){this.state.operationHistory.splice(addIndex,1)}this.state.addHistory=this.state.addHistory.filter(entry=>entry?.moduleID!==moduleID);try{await this.removeApiComponent(moduleID||effectivePath,{recordHistory:false})}catch{}}async removeApiComponent(pathOrModuleId,options={}){const recordHistory=options.recordHistory!==false;if(typeof pathOrModuleId!=="string"||!pathOrModuleId){throw new this.SlothletError("INVALID_ARGUMENT",{argument:"pathOrModuleId",expected:"non-empty string",received:typeof pathOrModuleId,validationError:true})}let apiPath=null;let moduleID=null;if(this.slothlet.handlers.ownership){const candidateModuleID=pathOrModuleId.split(":")[0];const registeredModules=Array.from(this.slothlet.handlers.ownership.moduleToPath.keys());let matchingModule=null;for(let i=registeredModules.length-1;i>=0;i--){const candidate=registeredModules[i];if(candidate===candidateModuleID||candidate.startsWith(`${candidateModuleID}_`)){matchingModule=candidate;break}}if(matchingModule){moduleID=matchingModule}else{const owner=this.slothlet.handlers.ownership.getCurrentOwner(pathOrModuleId);if(owner){apiPath=pathOrModuleId;moduleID=owner.moduleID}else{return false}}}else{const isModuleId=!pathOrModuleId.includes(".");apiPath=isModuleId?null:pathOrModuleId;moduleID=isModuleId?pathOrModuleId.split(":")[0]:null}if(!this.slothlet||!this.slothlet.isLoaded){throw new this.SlothletError("INVALID_CONFIG_NOT_LOADED",{operation:"removeApi",validationError:true})}if(apiPath&&moduleID){const normalizedPath2=this.normalizeApiPath(apiPath).apiPath;const moduleIDKey=String(moduleID);const history=this.slothlet.handlers.ownership?.getPathHistory?.(normalizedPath2)||[];const ownershipResult2=this.slothlet.handlers.ownership?.removePath?.(normalizedPath2,moduleIDKey)||{action:"none",removedModuleId:null,restoreModuleId:null};const pathParts=this.normalizeApiPath(apiPath).parts;if(ownershipResult2.action==="delete"){await this.deletePath(this.slothlet.api,pathParts);await this.deletePath(this.slothlet.boundApi,pathParts);if(this.slothlet.handlers.metadata){const rootSegment=normalizedPath2.split(".")[0];this.slothlet.handlers.metadata.removeUserMetadataByApiPath(rootSegment)}if(this.slothlet.handlers.versionManager){const versionKey=this.slothlet.handlers.versionManager.getVersionKeyForModule(moduleIDKey);if(versionKey){this.slothlet.handlers.versionManager.unregisterVersion(versionKey.logicalPath,versionKey.versionTag)}if(this.slothlet.handlers.versionManager.hasDispatcher(normalizedPath2)){this.slothlet.handlers.versionManager.teardownDispatcher(normalizedPath2)}}this.state.operationHistory.push({type:"remove",apiPath:normalizedPath2});return true}if(ownershipResult2.action==="restore"){const restoredValue=this.slothlet.handlers.ownership?.getCurrentValue?.(normalizedPath2);const restoredModuleId=this.slothlet.handlers.ownership?.getCurrentOwner?.(normalizedPath2)?.moduleID;if(restoredValue!==void 0&&restoredModuleId){await this.setValueAtPath(this.slothlet.api,pathParts,restoredValue,{mutateExisting:true,allowOverwrite:true,collisionMode:"replace",moduleID:restoredModuleId});await this.setValueAtPath(this.slothlet.boundApi,pathParts,restoredValue,{mutateExisting:true,allowOverwrite:true,collisionMode:"replace",moduleID:restoredModuleId});this.state.operationHistory.push({type:"remove",apiPath:normalizedPath2});return true}await this.restoreApiPath(normalizedPath2,ownershipResult2.restoreModuleId);this.state.operationHistory.push({type:"remove",apiPath:normalizedPath2});return true}if(ownershipResult2.action==="none"&&history.length===0){await this.deletePath(this.slothlet.api,pathParts);await this.deletePath(this.slothlet.boundApi,pathParts);return true}return false}if(moduleID){const moduleIDKey=String(moduleID);const result=this.slothlet.handlers.ownership?.unregister?.(moduleIDKey)||{removed:[],rolledBack:[]};if(this.slothlet.handlers.versionManager){const versionKey=this.slothlet.handlers.versionManager.getVersionKeyForModule(moduleIDKey);if(versionKey){this.slothlet.handlers.versionManager.unregisterVersion(versionKey.logicalPath,versionKey.versionTag)}}const allPaths=[...result.removed,...result.rolledBack.map(r=>r.apiPath)];const uniquePaths=[...new Set(allPaths)];const pathsToDelete=[];const pathsToRollback=[];for(const path2 of uniquePaths){const currentOwner=this.slothlet.handlers.ownership?.getCurrentOwner?.(path2);const hasChildrenWithOtherOwners=uniquePaths.some(p=>{if(p===path2||!p.startsWith(path2+"."))return false;const childOwner=this.slothlet.handlers.ownership?.getCurrentOwner?.(p);return childOwner&&childOwner.moduleID!==moduleIDKey});if(currentOwner&&currentOwner.moduleID!==moduleIDKey){pathsToRollback.push({apiPath:path2,restoredTo:currentOwner.moduleID})}else if(!hasChildrenWithOtherOwners){pathsToDelete.push(path2)}}pathsToDelete.sort((a,b)=>{const depthA=(a.match(/\./g)||[]).length;const depthB=(b.match(/\./g)||[]).length;return depthB-depthA});for(const removedPath of pathsToDelete){const{parts:parts2}=this.normalizeApiPath(removedPath);await this.deletePath(this.slothlet.api,parts2);await this.deletePath(this.slothlet.boundApi,parts2);if(this.slothlet.handlers.metadata){const rootSegment=removedPath.split(".")[0];this.slothlet.handlers.metadata.removeUserMetadataByApiPath(rootSegment)}}if(pathsToDelete.length>0){const rootSegment=pathsToDelete[0].split(".")[0];if(rootSegment in this.slothlet.api){delete this.slothlet.api[rootSegment]}if(rootSegment in this.slothlet.boundApi){delete this.slothlet.boundApi[rootSegment]}}for(const rollback of pathsToRollback){const{parts:parts2}=this.normalizeApiPath(rollback.apiPath);const previousImpl=this.slothlet.handlers.ownership?.getCurrentValue?.(rollback.apiPath);if(previousImpl!==void 0){const existingWrapper=this.getValueAtPath(this.slothlet.api,parts2);const existingWrapperRaw=resolveWrapper(existingWrapper);if(existingWrapperRaw){existingWrapperRaw.___setImpl(previousImpl,rollback.restoredTo)}const existingBoundWrapper=this.getValueAtPath(this.slothlet.boundApi,parts2);const existingBoundWrapperRaw=resolveWrapper(existingBoundWrapper);if(existingBoundWrapperRaw){existingBoundWrapperRaw.___setImpl(previousImpl,rollback.restoredTo)}}}this.state.addHistory=this.state.addHistory.filter(entry=>String(entry.moduleID)!==moduleIDKey);if(this.slothlet.handlers.apiCacheManager){const deleted=this.slothlet.handlers.apiCacheManager.delete(moduleIDKey);if(deleted){this.slothlet.debug("cache",{key:"DEBUG_MODE_CACHE_DELETED_MODULE_REMOVED",moduleID:moduleIDKey})}}if(recordHistory&&pathsToDelete.length>0){const rootSegment=pathsToDelete[0].split(".")[0];this.state.operationHistory.push({type:"remove",apiPath:rootSegment})}return pathsToDelete.length>0||pathsToRollback.length>0}if(!apiPath){throw new this.SlothletError("INVALID_CONFIG_API_PATH_INVALID",{apiPath,reason:translate("API_PATH_REASON_REQUIRED"),index:void 0,segment:void 0,validationError:true})}const{apiPath:normalizedPath,parts}=this.normalizeApiPath(apiPath);const ownershipResult=this.slothlet.handlers.ownership?.removePath?.(normalizedPath,null)||{action:"none",removedModuleId:null,restoreModuleId:null};const pathExists=this.getValueAtPath(this.slothlet.api,parts)!==void 0;if(ownershipResult.action==="none"){if(pathExists){await this.deletePath(this.slothlet.api,parts);await this.deletePath(this.slothlet.boundApi,parts);if(this.slothlet.handlers.metadata){this.slothlet.handlers.metadata.removeUserMetadataByApiPath(normalizedPath)}if(recordHistory){this.state.operationHistory.push({type:"remove",apiPath:normalizedPath})}return true}return false}if(ownershipResult.action==="delete"){await this.deletePath(this.slothlet.api,parts);await this.deletePath(this.slothlet.boundApi,parts);if(this.slothlet.handlers.metadata){this.slothlet.handlers.metadata.removeUserMetadataByApiPath(normalizedPath)}if(recordHistory){this.state.operationHistory.push({type:"remove",apiPath:normalizedPath})}return true}if(ownershipResult.action==="restore"){const restoredValue=this.slothlet.handlers.ownership?.getCurrentValue?.(normalizedPath);const restoredModuleId=this.slothlet.handlers.ownership?.getCurrentOwner?.(normalizedPath)?.moduleID;if(restoredValue!==void 0&&restoredModuleId){await this.setValueAtPath(this.slothlet.api,parts,restoredValue,{mutateExisting:true,allowOverwrite:true,collisionMode:"replace",moduleID:restoredModuleId});await this.setValueAtPath(this.slothlet.boundApi,parts,restoredValue,{mutateExisting:true,allowOverwrite:true,collisionMode:"replace",moduleID:restoredModuleId});if(recordHistory){this.state.operationHistory.push({type:"remove",apiPath:normalizedPath})}return true}await this.restoreApiPath(normalizedPath,ownershipResult.restoreModuleId);if(recordHistory){this.state.operationHistory.push({type:"remove",apiPath:normalizedPath})}return true}return false}async reloadApiComponent(params){const{apiPath,moduleID,options}=params||{};if(!this.slothlet||!this.slothlet.isLoaded){throw new this.SlothletError("INVALID_CONFIG_NOT_LOADED",{operation:"reloadApi",validationError:true})}if(moduleID){await this._reloadByModuleID(moduleID);return}if(apiPath){await this._reloadByApiPath(apiPath,options);return}throw new this.SlothletError("INVALID_ARGUMENT",{argument:"params",expected:"{ moduleID } or { apiPath }",received:params,validationError:true})}async _reloadByModuleID(moduleID,{forceReplace=true}={}){const cacheManager=this.slothlet.handlers.apiCacheManager;if(!cacheManager){throw new this.SlothletError("CACHE_MANAGER_NOT_AVAILABLE",{operation:"reload",validationError:true})}if(!cacheManager.has(moduleID)){throw new this.SlothletError("CACHE_NOT_FOUND",{moduleID,operation:"reload",validationError:true})}const oldEntry=cacheManager.get(moduleID);this.slothlet.debug("reload",{key:"DEBUG_MODE_RELOADING_MODULE_BY_ID",moduleID,endpoint:oldEntry.endpoint,folderPath:oldEntry.folderPath});const freshApi=await cacheManager.rebuildCache(moduleID);cacheManager.set(moduleID,{...oldEntry,api:freshApi,timestamp:Date.now()});this.slothlet.debug("reload",{key:"DEBUG_MODE_FRESH_API_KEYS_BEFORE_RESTORE",moduleID,endpoint:oldEntry.endpoint,freshApiKeys:Object.keys(freshApi||{})});await this._restoreApiTree(freshApi,oldEntry.endpoint,moduleID,oldEntry.collisionMode,forceReplace);this.slothlet.debug("reload",{key:"DEBUG_MODE_FRESH_API_KEYS_AFTER_RESTORE",moduleID,endpoint:oldEntry.endpoint,freshApiKeys:Object.keys(freshApi||{})});this.slothlet.debug("reload",{key:"DEBUG_MODE_MODULE_RELOAD_COMPLETE",moduleID});if(this.slothlet.handlers.versionManager){this.slothlet.handlers.versionManager.onVersionedModuleReload(moduleID)}}async _reloadByApiPath(apiPath,options={}){this.slothlet.debug("reload",{key:"DEBUG_MODE_RELOADING_BY_API_PATH",apiPath});const moduleIDsToReload=this._findAffectedCaches(apiPath);if(moduleIDsToReload.length===0){this.slothlet.debug("reload",{key:"DEBUG_MODE_NO_CACHES_ATTEMPTING_RESTORE",apiPath});if(apiPath!=="."&&apiPath!==""){await this.restoreApiPath(apiPath,"base")}return}const cacheManager=this.slothlet.handlers.apiCacheManager;moduleIDsToReload.sort((a,b)=>{const entryA=cacheManager.get(a);const entryB=cacheManager.get(b);if(entryA?.endpoint==="."&&entryB?.endpoint!==".")return-1;if(entryB?.endpoint==="."&&entryA?.endpoint!==".")return 1;const indexA=this.state.addHistory.findIndex(h=>h.moduleID===a);const indexB=this.state.addHistory.findIndex(h=>h.moduleID===b);return indexA-indexB});const endpointOrder=new Map;for(const moduleID of moduleIDsToReload){const entry=cacheManager.get(moduleID);const ep=entry?.endpoint??".";if(!endpointOrder.has(ep))endpointOrder.set(ep,[]);endpointOrder.get(ep).push(moduleID)}for(const[,moduleIDs]of endpointOrder){for(let i=0;i<moduleIDs.length;i++){await this._reloadByModuleID(moduleIDs[i],{forceReplace:i===0})}}const reloadMetadata=options?.metadata;if(reloadMetadata&&typeof reloadMetadata==="object"&&Object.keys(reloadMetadata).length>0){if(this.slothlet.handlers.metadata){const targetPath=apiPath==="."?null:apiPath.split(".")[0];if(targetPath){this.slothlet.handlers.metadata.registerUserMetadata(targetPath,reloadMetadata)}}}this.slothlet.debug("reload",{key:"DEBUG_MODE_API_PATH_RELOAD_COMPLETE",apiPath,reloadedModules:moduleIDsToReload.length,loadOrder:moduleIDsToReload})}_findAffectedCaches(apiPath){const cacheManager=this.slothlet.handlers.apiCacheManager;if(!cacheManager)return[];const allModuleIDs=cacheManager.getAllModuleIDs();if(apiPath==="."||apiPath===""||apiPath==null){const baseModules=[];for(const moduleID of allModuleIDs){const entry=cacheManager.get(moduleID);if(entry&&entry.endpoint==="."){baseModules.push(moduleID)}}return baseModules}const exactMatches=[];for(const moduleID of allModuleIDs){const entry=cacheManager.get(moduleID);if(entry&&entry.endpoint===apiPath){exactMatches.push(moduleID)}}if(exactMatches.length>0)return exactMatches;const children=[];const pathPrefix=apiPath+".";for(const moduleID of allModuleIDs){const entry=cacheManager.get(moduleID);if(entry?.endpoint?.startsWith(pathPrefix)){children.push(moduleID)}}if(children.length>0)return children;const ownership=this.slothlet.handlers.ownership;const history=ownership?.getPathHistory?.(apiPath);if(history&&history.length>0){const owned=[];for(const{moduleID}of history){if(cacheManager.has(moduleID)){owned.push(moduleID)}}if(owned.length>0)return owned}let bestMatch=null;let bestLength=-1;for(const moduleID of allModuleIDs){const entry=cacheManager.get(moduleID);if(!entry?.endpoint)continue;const ep=entry.endpoint;if(ep==="."||apiPath.startsWith(ep+".")){if(ep.length>bestLength){bestLength=ep.length;bestMatch=moduleID}}}if(bestMatch)return[bestMatch];return[]}_collectCustomProperties(existingProxy,freshApi){const customProps={};if(!existingProxy||typeof existingProxy!=="object"&&typeof existingProxy!=="function"){return customProps}const wrapper=resolveWrapper(existingProxy);if(!wrapper){return customProps}const freshKeys=new Set(freshApi?Object.keys(freshApi):[]);const ownKeys=Object.keys(wrapper).filter(k=>!ComponentBase.INTERNAL_KEYS.has(k));for(const key of ownKeys){try{const val=wrapper[key];if(val&&(typeof val==="object"||typeof val==="function")&&resolveWrapper(val)){continue}if(!freshKeys.has(key)){customProps[key]=val}else{customProps[key]=val}}catch{}}return customProps}_restoreCustomProperties(proxy,customProps){if(!proxy||!customProps||typeof customProps!=="object"){return}for(const[key,value]of Object.entries(customProps)){try{proxy[key]=value}catch{}}}async _restoreApiTree(freshApi,endpoint,moduleID,collisionMode,forceReplace=true){if(!freshApi||typeof freshApi!=="object"&&typeof freshApi!=="function"){return}const parts=endpoint==="."?[]:endpoint.split(".");if(parts.length===0){for(const key of Object.keys(freshApi)){if(typeof key==="string"&&(key.startsWith("_")||key.startsWith("__")))continue;if(key==="slothlet"||key==="shutdown"||key==="destroy")continue;const existingAtKey=this.slothlet.api[key];const freshValue=freshApi[key];if(existingAtKey&&resolveWrapper(existingAtKey)!==null){const customProps=this._collectCustomProperties(existingAtKey,freshValue);const freshWrapper=resolveWrapper(freshValue);const isLazyFresh=freshWrapper&&freshWrapper.____slothletInternal.mode==="lazy"&&!freshWrapper.____slothletInternal.state.materialized&&typeof freshWrapper.____slothletInternal.materializeFunc==="function";this.slothlet.debug("reload",{key:"DEBUG_MODE_RESTORE_ROOT_KEY_INSPECT",rootKey:key,hasFreshWrapper:!!freshWrapper,freshMode:freshWrapper?.____slothletInternal.mode,freshMaterialized:freshWrapper?.____slothletInternal.state?.materialized,hasMaterializeFunc:typeof freshWrapper?.____slothletInternal.materializeFunc==="function",isLazyFresh,existingMaterialized:resolveWrapper(existingAtKey)?.____slothletInternal?.state?.materialized});if(isLazyFresh){resolveWrapper(existingAtKey).___resetLazy(freshWrapper.____slothletInternal.materializeFunc);this._restoreCustomProperties(existingAtKey,customProps);this.slothlet.debug("reload",{key:"DEBUG_MODE_ROOT_KEY_RESET_LAZY",rootKey:key,restoredCustomProps:Object.keys(customProps)})}else{let implForReload;if(freshValue&&resolveWrapper(freshValue)!==null){implForReload=freshWrapper?UnifiedWrapper._extractFullImpl(freshWrapper):freshValue}else{implForReload=freshValue}if(typeof implForReload==="function"){const extracted={};for(const k of Object.keys(implForReload)){extracted[k]=implForReload[k]}implForReload=extracted}const wrapper=resolveWrapper(existingAtKey);const originalCollisionMode=wrapper?wrapper.____slothletInternal.state.collisionMode:null;if(forceReplace&&wrapper){wrapper.____slothletInternal.state.collisionMode="replace"}if(wrapper&&originalCollisionMode!==null){wrapper.____slothletInternal.state.collisionMode=originalCollisionMode}this._restoreCustomProperties(existingAtKey,customProps);this.slothlet.debug("reload",{key:"DEBUG_MODE_ROOT_KEY_UPDATED_SETIMPL",rootKey:key,restoredCustomProps:Object.keys(customProps)})}}else if(existingAtKey===void 0){const cacheManager=this.slothlet.handlers.apiCacheManager;const cacheEntry=cacheManager.get(moduleID);const resolvedFolderPath=cacheEntry?.folderPath||"";await this.setValueAtPath(this.slothlet.api,[key],freshValue,{mutateExisting:true,collisionMode,moduleID,sourceFolder:resolvedFolderPath});if(this.slothlet.boundApi){await this.setValueAtPath(this.slothlet.boundApi,[key],freshValue,{mutateExisting:true,collisionMode,moduleID,sourceFolder:resolvedFolderPath})}}}}else{const existing=this.getValueAtPath(this.slothlet.api,parts);this.slothlet.debug("reload",{key:"DEBUG_MODE_RESTORE_NESTED_PATH",endpoint,moduleID,partsPath:parts.join("."),existingFound:!!existing,hasSetImpl:existing?resolveWrapper(existing)!==null:false,freshApiKeys:Object.keys(freshApi||{})});if(existing&&resolveWrapper(existing)!==null){const customProps=this._collectCustomProperties(existing,freshApi);const wrapper=resolveWrapper(existing);const originalCollisionMode=wrapper?wrapper.____slothletInternal.state.collisionMode:null;if(forceReplace&&wrapper){wrapper.____slothletInternal.state.collisionMode="replace";this.slothlet.debug("reload",{key:"DEBUG_MODE_RESTORE_FORCING_REPLACE",endpoint,originalCollisionMode,wrapperApiPath:wrapper.____slothletInternal.apiPath})}let implForReload;if(resolveWrapper(freshApi)!==null){const freshWrapper=resolveWrapper(freshApi);implForReload=freshWrapper?UnifiedWrapper._extractFullImpl(freshWrapper):freshApi}else if(typeof freshApi==="function"){implForReload={};for(const key of Object.keys(freshApi)){implForReload[key]=freshApi[key]}}else{implForReload=freshApi}if(parts.length>0&&implForReload&&typeof implForReload==="object"){const lastEndpointPart=parts[parts.length-1];if(lastEndpointPart&&Object.prototype.hasOwnProperty.call(implForReload,lastEndpointPart)){const dupValue=implForReload[lastEndpointPart];const dupWrapperForDedup=resolveWrapper(dupValue);if(dupWrapperForDedup){const hoisted={};for(const k of Object.keys(implForReload)){if(k!==lastEndpointPart)hoisted[k]=implForReload[k]}for(const k of Object.keys(dupWrapperForDedup).filter(k2=>!k2.startsWith("_")&&!k2.startsWith("__"))){hoisted[k]=dupWrapperForDedup[k]}implForReload=hoisted}}}if(implForReload&&typeof implForReload==="object"){for(const key of Object.keys(implForReload)){const val=implForReload[key];if(resolveWrapper(val)!==null){const childWrapper=resolveWrapper(val);if(childWrapper.____slothletInternal.state.materialized){implForReload[key]=UnifiedWrapper._extractFullImpl(childWrapper)}}}}resolveWrapper(existing).___setImpl(implForReload,moduleID);if(wrapper&&originalCollisionMode!==null){wrapper.____slothletInternal.state.collisionMode=originalCollisionMode}this._restoreCustomProperties(existing,customProps);this.slothlet.debug("reload",{key:"DEBUG_MODE_UPDATED_WRAPPER_IMPL",endpoint,moduleID,forcedReplaceMode:true,restoredCustomProps:Object.keys(customProps)})}else{const cacheManager=this.slothlet.handlers.apiCacheManager;const cacheEntry=cacheManager.get(moduleID);const resolvedFolderPath=cacheEntry?.folderPath||"";let implForContainer=freshApi;if(typeof freshApi==="function"){implForContainer={};for(const key of Object.keys(freshApi)){implForContainer[key]=freshApi[key]}}const containerWrapper=new UnifiedWrapper(this.slothlet,{apiPath:endpoint,mode:this.____config.mode,moduleID,filePath:resolvedFolderPath,sourceFolder:resolvedFolderPath});containerWrapper.___setImpl(implForContainer,moduleID);const apiToSet=containerWrapper.createProxy();await this.setValueAtPath(this.slothlet.api,parts,apiToSet,{mutateExisting:true,collisionMode,moduleID,sourceFolder:resolvedFolderPath});if(this.slothlet.boundApi){await this.setValueAtPath(this.slothlet.boundApi,parts,apiToSet,{mutateExisting:true,collisionMode,moduleID,sourceFolder:resolvedFolderPath})}this.slothlet.debug("reload",{key:"DEBUG_MODE_CREATED_NEW_WRAPPER_UNEXPECTED",endpoint,moduleID})}}}}export{ApiManager};