@cldmv/slothlet 2.9.0 → 2.11.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 (53) hide show
  1. package/AGENT-USAGE.md +41 -0
  2. package/README.md +95 -20
  3. package/dist/lib/engine/slothlet_child.mjs +1 -1
  4. package/dist/lib/engine/slothlet_engine.mjs +1 -1
  5. package/dist/lib/engine/slothlet_esm.mjs +1 -1
  6. package/dist/lib/engine/slothlet_helpers.mjs +1 -1
  7. package/dist/lib/engine/slothlet_worker.mjs +1 -1
  8. package/dist/lib/helpers/als-eventemitter.mjs +1 -1
  9. package/dist/lib/helpers/api_builder/add_api.mjs +321 -5
  10. package/dist/lib/helpers/api_builder/analysis.mjs +13 -3
  11. package/dist/lib/helpers/api_builder/construction.mjs +41 -3
  12. package/dist/lib/helpers/api_builder/decisions.mjs +16 -5
  13. package/dist/lib/helpers/api_builder/metadata.mjs +248 -0
  14. package/dist/lib/helpers/api_builder.mjs +1 -1
  15. package/dist/lib/helpers/auto-wrap.mjs +1 -1
  16. package/dist/lib/helpers/hooks.mjs +1 -1
  17. package/dist/lib/helpers/instance-manager.mjs +1 -1
  18. package/dist/lib/helpers/metadata-api.mjs +201 -0
  19. package/dist/lib/helpers/multidefault.mjs +12 -3
  20. package/dist/lib/helpers/resolve-from-caller.mjs +48 -11
  21. package/dist/lib/helpers/sanitize.mjs +1 -1
  22. package/dist/lib/helpers/utilities.mjs +1 -1
  23. package/dist/lib/modes/slothlet_eager.mjs +30 -2
  24. package/dist/lib/modes/slothlet_lazy.mjs +95 -5
  25. package/dist/lib/runtime/runtime-asynclocalstorage.mjs +5 -1
  26. package/dist/lib/runtime/runtime-livebindings.mjs +5 -1
  27. package/dist/lib/runtime/runtime.mjs +12 -1
  28. package/dist/slothlet.mjs +70 -6
  29. package/docs/API-RULES-CONDITIONS.md +511 -307
  30. package/docs/API-RULES.md +617 -589
  31. package/package.json +2 -2
  32. package/types/dist/lib/helpers/api_builder/add_api.d.mts +64 -22
  33. package/types/dist/lib/helpers/api_builder/add_api.d.mts.map +1 -1
  34. package/types/dist/lib/helpers/api_builder/analysis.d.mts.map +1 -1
  35. package/types/dist/lib/helpers/api_builder/construction.d.mts.map +1 -1
  36. package/types/dist/lib/helpers/api_builder/decisions.d.mts.map +1 -1
  37. package/types/dist/lib/helpers/api_builder/metadata.d.mts +99 -0
  38. package/types/dist/lib/helpers/api_builder/metadata.d.mts.map +1 -0
  39. package/types/dist/lib/helpers/metadata-api.d.mts +132 -0
  40. package/types/dist/lib/helpers/metadata-api.d.mts.map +1 -0
  41. package/types/dist/lib/helpers/multidefault.d.mts.map +1 -1
  42. package/types/dist/lib/helpers/resolve-from-caller.d.mts.map +1 -1
  43. package/types/dist/lib/modes/slothlet_eager.d.mts.map +1 -1
  44. package/types/dist/lib/modes/slothlet_lazy.d.mts.map +1 -1
  45. package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts +2 -0
  46. package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts.map +1 -1
  47. package/types/dist/lib/runtime/runtime-livebindings.d.mts +2 -0
  48. package/types/dist/lib/runtime/runtime-livebindings.d.mts.map +1 -1
  49. package/types/dist/lib/runtime/runtime.d.mts +1 -0
  50. package/types/dist/lib/runtime/runtime.d.mts.map +1 -1
  51. package/types/dist/slothlet.d.mts +8 -0
  52. package/types/dist/slothlet.d.mts.map +1 -1
  53. package/types/index.d.mts.map +1 -0
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2025 CLDMV/Shinrai
2
+ Copyright 2026 CLDMV/Shinrai
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -55,37 +55,74 @@ function pickPrimaryBaseFile() {
55
55
  for (const cs of getStack(pickPrimaryBaseFile)) {
56
56
  const f = toFsPath(cs?.getFileName?.());
57
57
  if (!f) continue;
58
- if (f.startsWith?.("node:internal")) continue;
58
+
59
+ if (f.startsWith?.("node:")) continue;
59
60
  files.push(f);
60
61
  }
61
62
 
63
+ console.log(`[DEBUG_RESOLVE] pickPrimaryBaseFile - Total stack frames: ${files.length}`);
64
+ console.log(`[DEBUG_RESOLVE] ALL stack frames:`);
65
+ for (let i = 0; i < files.length; i++) {
66
+ console.log(`[DEBUG_RESOLVE] [${i}] ${files[i]}`);
67
+ }
68
+
62
69
  let iSloth = -1;
63
70
  for (let i = 0; i < files.length; i++) {
64
- if (path.basename(files[i]).toLowerCase() === "slothlet.mjs") iSloth = i;
71
+ if (path.basename(files[i]).toLowerCase() === "slothlet.mjs") {
72
+ iSloth = i;
73
+ }
65
74
  }
75
+
76
+ console.log(`[DEBUG_RESOLVE] Found slothlet.mjs at index: ${iSloth}, total files: ${files.length}`);
66
77
  if (iSloth !== -1) {
67
- const j = iSloth + 1;
68
- if (j < files.length) {
69
- const b = path.basename(files[j]).toLowerCase();
70
- if (/^index\.(mjs|cjs|js)$/.test(b) && j + 1 < files.length) return files[j + 1];
71
- return files[j];
78
+ console.log(`[DEBUG_RESOLVE] Files after slothlet.mjs:`);
79
+ for (let k = iSloth + 1; k < Math.min(files.length, iSloth + 5); k++) {
80
+ console.log(`[DEBUG_RESOLVE] [${k}] ${files[k]}`);
72
81
  }
73
82
  }
74
- return null;
83
+
84
+ if (iSloth !== -1) {
85
+
86
+ let j = iSloth + 1;
87
+
88
+
89
+ function isSlothletInternalFile(filePath) {
90
+ if (!filePath) return true;
91
+
92
+
93
+ if (filePath.includes(path.sep + "src" + path.sep + "lib" + path.sep)) return true;
94
+
95
+
96
+ if (path.basename(filePath).toLowerCase() === "slothlet.mjs") return true;
97
+
98
+
99
+ if (filePath.startsWith(THIS_DIR + path.sep)) return true;
100
+
101
+ return false;
75
102
  }
76
103
 
77
104
 
78
105
  function pickFallbackBaseFile() {
106
+ console.log("[DEBUG_RESOLVE] pickFallbackBaseFile called");
79
107
  for (const cs of getStack(pickFallbackBaseFile)) {
80
108
  const f = toFsPath(cs?.getFileName?.());
81
109
  if (!f) continue;
82
- if (f.startsWith?.("node:internal")) continue;
110
+
111
+ if (f.startsWith?.("node:")) continue;
83
112
  if (f === THIS_FILE) continue;
84
113
  if (f.startsWith(THIS_DIR + path.sep)) continue;
85
114
  if (path.basename(f).toLowerCase() === "slothlet.mjs") continue;
115
+ if (isSlothletInternalFile(f)) {
116
+ console.log(`[DEBUG_RESOLVE] Fallback skipping internal file: ${f}`);
117
+ continue;
118
+ }
119
+ console.log(`[DEBUG_RESOLVE] Fallback considering: ${f}`);
86
120
  return f;
87
121
  }
88
- return THIS_FILE;
122
+
123
+
124
+ console.log(`[DEBUG_RESOLVE] Fallback: No user code found in stack, using process.cwd(): ${process.cwd()}`);
125
+ return process.cwd();
89
126
  }
90
127
 
91
128
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2025 CLDMV/Shinrai
2
+ Copyright 2026 CLDMV/Shinrai
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2025 CLDMV/Shinrai
2
+ Copyright 2026 CLDMV/Shinrai
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2025 CLDMV/Shinrai
2
+ Copyright 2026 CLDMV/Shinrai
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -100,7 +100,35 @@ export async function create(dir, maxDepth = Infinity, currentDepth = 0) {
100
100
  for (const entry of entries) {
101
101
  if (entry.isDirectory() && !entry.name.startsWith(".") && currentDepth < maxDepth) {
102
102
  const categoryPath = path.join(dir, entry.name);
103
- api[this._toapiPathKey(entry.name)] = await this._loadCategory(categoryPath, currentDepth + 1, maxDepth);
103
+ const categoryKey = this._toapiPathKey(entry.name);
104
+ const categoryResult = await this._buildCategory(categoryPath, {
105
+ currentDepth: currentDepth + 1,
106
+ maxDepth,
107
+ mode: "eager",
108
+ existingApi: api
109
+ });
110
+
111
+
112
+ if (
113
+ api[categoryKey] &&
114
+ typeof api[categoryKey] === "object" &&
115
+ typeof categoryResult === "object" &&
116
+ !Array.isArray(api[categoryKey]) &&
117
+ !Array.isArray(categoryResult)
118
+ ) {
119
+ if (this.config.debug) {
120
+ console.log(
121
+ `[DEBUG] eager: Merging subdirectory '${categoryKey}' - existing:`,
122
+ Object.keys(api[categoryKey]),
123
+ "new:",
124
+ Object.keys(categoryResult)
125
+ );
126
+ }
127
+
128
+ Object.assign(api[categoryKey], categoryResult);
129
+ } else {
130
+ api[categoryKey] = categoryResult;
131
+ }
104
132
  }
105
133
  }
106
134
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2025 CLDMV/Shinrai
2
+ Copyright 2026 CLDMV/Shinrai
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -161,7 +161,8 @@ export async function create(dir, maxDepth = Infinity, currentDepth = 0) {
161
161
  depth,
162
162
  maxDepth,
163
163
  pathParts: [key],
164
- runWithCtx
164
+ runWithCtx,
165
+ existingContent: parent[key]
165
166
  });
166
167
  parent[key] = proxy;
167
168
  }
@@ -173,6 +174,28 @@ export async function create(dir, maxDepth = Infinity, currentDepth = 0) {
173
174
 
174
175
  function replacePlaceholder(parent, key, placeholder, value, instance, depth) {
175
176
  if (!parent || !key) return;
177
+
178
+
179
+ if (
180
+ parent[key] !== placeholder &&
181
+ parent[key] &&
182
+ typeof parent[key] === "object" &&
183
+ typeof value === "object" &&
184
+ !Array.isArray(parent[key]) &&
185
+ !Array.isArray(value)
186
+ ) {
187
+ if (instance?.config?.debug) {
188
+ console.log(`[lazy] MERGE CASE DETECTED for '${key}' - existing:`, Object.keys(parent[key]), "new:", Object.keys(value));
189
+ console.log(`[lazy] Before merge - parent[${key}]:`, parent[key]);
190
+ }
191
+
192
+ Object.assign(parent[key], value);
193
+ if (instance?.config?.debug) {
194
+ console.log(`[lazy] After merge - parent[${key}]:`, Object.keys(parent[key]));
195
+ }
196
+ return;
197
+ }
198
+
176
199
  if (parent[key] !== placeholder) return;
177
200
 
178
201
 
@@ -215,9 +238,43 @@ function replacePlaceholder(parent, key, placeholder, value, instance, depth) {
215
238
  }
216
239
 
217
240
  try {
218
- Object.defineProperty(parent, finalKey, { value, writable: true, enumerable: true, configurable: true });
241
+
242
+ if (
243
+ parent[finalKey] &&
244
+ typeof parent[finalKey] === "object" &&
245
+ typeof value === "object" &&
246
+ !Array.isArray(parent[finalKey]) &&
247
+ !Array.isArray(value)
248
+ ) {
249
+ if (instance?.config?.debug) {
250
+ console.log(`[lazy] Merging subdirectory '${finalKey}' - existing:`, Object.keys(parent[finalKey]), "new:", Object.keys(value));
251
+ }
252
+
253
+ Object.assign(parent[finalKey], value);
254
+ } else {
255
+ Object.defineProperty(parent, finalKey, { value, writable: true, enumerable: true, configurable: true });
256
+ }
219
257
  } catch {
220
- parent[finalKey] = value;
258
+
259
+ if (
260
+ parent[finalKey] &&
261
+ typeof parent[finalKey] === "object" &&
262
+ typeof value === "object" &&
263
+ !Array.isArray(parent[finalKey]) &&
264
+ !Array.isArray(value)
265
+ ) {
266
+ if (instance?.config?.debug) {
267
+ console.log(
268
+ `[lazy] Merging subdirectory '${finalKey}' (fallback) - existing:`,
269
+ Object.keys(parent[finalKey]),
270
+ "new:",
271
+ Object.keys(value)
272
+ );
273
+ }
274
+ Object.assign(parent[finalKey], value);
275
+ } else {
276
+ parent[finalKey] = value;
277
+ }
221
278
  }
222
279
  if (instance?.config?.debug) {
223
280
  console.log(`[lazy][materialize] replaced ${key}${finalKey !== key ? ` -> ${finalKey}` : ""} (${typeof value})`);
@@ -228,7 +285,7 @@ function replacePlaceholder(parent, key, placeholder, value, instance, depth) {
228
285
  }
229
286
 
230
287
 
231
- function createFolderProxy({ subDirPath, key, parent, instance, depth, maxDepth, pathParts, runWithCtx }) {
288
+ function createFolderProxy({ subDirPath, key, parent, instance, depth, maxDepth, pathParts, runWithCtx, existingContent }) {
232
289
  let materialized = null;
233
290
  let inFlight = null;
234
291
 
@@ -243,6 +300,7 @@ function createFolderProxy({ subDirPath, key, parent, instance, depth, maxDepth,
243
300
  currentDepth: depth,
244
301
  maxDepth,
245
302
  mode: "lazy",
303
+ existingApi: parent,
246
304
  subdirHandler: ({ subDirPath: nestedPath, key: nestedKey, categoryModules, currentDepth: cd, maxDepth: md }) =>
247
305
  createFolderProxy({
248
306
  subDirPath: nestedPath,
@@ -256,6 +314,29 @@ function createFolderProxy({ subDirPath, key, parent, instance, depth, maxDepth,
256
314
  })
257
315
  });
258
316
  materialized = value;
317
+
318
+
319
+ if (
320
+ existingContent &&
321
+ typeof existingContent === "object" &&
322
+ typeof materialized === "object" &&
323
+ !Array.isArray(existingContent) &&
324
+ !Array.isArray(materialized)
325
+ ) {
326
+ if (instance?.config?.debug) {
327
+ console.log(
328
+ `[lazy] Merging existing content with materialized subdirectory '${key}' - existing:`,
329
+ Object.keys(existingContent),
330
+ "new:",
331
+ Object.keys(materialized)
332
+ );
333
+ }
334
+
335
+ materialized = Object.assign({}, existingContent, materialized);
336
+ } else if (existingContent && !materialized) {
337
+
338
+ materialized = existingContent;
339
+ }
259
340
  if (instance?.config?.debug) {
260
341
  try {
261
342
  const infoKeys = materialized && typeof materialized === "object" ? Object.keys(materialized) : [];
@@ -345,6 +426,11 @@ function createFolderProxy({ subDirPath, key, parent, instance, depth, maxDepth,
345
426
  return pathParts.length > 0 ? pathParts.join(".") : undefined;
346
427
  }
347
428
 
429
+ if (prop === "__metadata" || prop === "__sourceFolder") {
430
+
431
+ return Reflect.get(_t, prop);
432
+ }
433
+
348
434
  if (materialized) {
349
435
  if (materialized && (typeof materialized === "object" || typeof materialized === "function")) return materialized[prop];
350
436
  return undefined;
@@ -485,6 +571,10 @@ function createFolderProxy({ subDirPath, key, parent, instance, depth, maxDepth,
485
571
  if (prop === "__materialized") {
486
572
  return { configurable: true, enumerable: false, writable: true, value: materialized };
487
573
  }
574
+
575
+ if (prop === "__metadata" || prop === "__sourceFolder") {
576
+ return Reflect.getOwnPropertyDescriptor(lazy_lazyTarget, prop);
577
+ }
488
578
  if (prop === "prototype") {
489
579
 
490
580
  return Object.getOwnPropertyDescriptor(lazy_lazyTarget, "prototype");
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2025 CLDMV/Shinrai
2
+ Copyright 2026 CLDMV/Shinrai
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
21
21
  import { AsyncLocalStorage } from "node:async_hooks";
22
22
  import util from "node:util";
23
23
  import { enableAlsForEventEmitters } from "@cldmv/slothlet/helpers/als-eventemitter";
24
+ import { metadataAPI } from "@cldmv/slothlet/helpers/metadata-api";
24
25
 
25
26
  const als = new AsyncLocalStorage();
26
27
 
@@ -565,3 +566,6 @@ export const context = runtime_createLiveBinding("context");
565
566
  export const reference = runtime_createLiveBinding("reference");
566
567
 
567
568
 
569
+ export { metadataAPI };
570
+
571
+
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2025 CLDMV/Shinrai
2
+ Copyright 2026 CLDMV/Shinrai
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@ import {
25
25
  setActiveInstance,
26
26
  getCurrentActiveInstanceId
27
27
  } from "@cldmv/slothlet/helpers/instance-manager";
28
+ import { metadataAPI } from "@cldmv/slothlet/helpers/metadata-api";
28
29
 
29
30
 
30
31
  export const requestALS = new AsyncLocalStorage();
@@ -433,3 +434,6 @@ export const contextManager = {
433
434
  set: setContext,
434
435
  runWithCtx
435
436
  };
437
+
438
+
439
+ export { metadataAPI };
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2025 CLDMV/Shinrai
2
+ Copyright 2026 CLDMV/Shinrai
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -225,3 +225,14 @@ export const instanceId = (() => {
225
225
  })();
226
226
 
227
227
  export const sharedALS = getCurrentRuntime().sharedALS;
228
+
229
+
230
+ export const metadataAPI = new Proxy(
231
+ {},
232
+ {
233
+ get(_, prop) {
234
+ const runtime = getCurrentRuntime();
235
+ return runtime.metadataAPI[prop];
236
+ }
237
+ }
238
+ );
package/dist/slothlet.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2025 CLDMV/Shinrai
2
+ Copyright 2026 CLDMV/Shinrai
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -75,6 +75,11 @@ let DEBUG = process.argv.includes("--slothletdebug")
75
75
  : false;
76
76
 
77
77
 
78
+ if (DEBUG && !process.env.SLOTHLET_DEBUG) {
79
+ process.env.SLOTHLET_DEBUG = "1";
80
+ }
81
+
82
+
78
83
  export const self = {};
79
84
 
80
85
 
@@ -143,7 +148,17 @@ const slothletObject = {
143
148
  reference: {},
144
149
  mode: "singleton",
145
150
  loaded: false,
146
- config: { lazy: false, apiDepth: Infinity, debug: DEBUG, dir: null, sanitize: null, allowApiOverwrite: true },
151
+ config: {
152
+ lazy: false,
153
+ apiDepth: Infinity,
154
+ debug: DEBUG,
155
+ dir: null,
156
+ sanitize: null,
157
+ allowApiOverwrite: true,
158
+ enableModuleOwnership: false
159
+ },
160
+
161
+ _moduleOwnership: new Map(),
147
162
  _dispose: null,
148
163
  _boundAPIShutdown: null,
149
164
  instanceId: null,
@@ -391,7 +406,7 @@ const slothletObject = {
391
406
 
392
407
 
393
408
  async _buildCategory(categoryPath, options = {}) {
394
- const { currentDepth = 0, maxDepth = Infinity, mode = "eager", subdirHandler } = options;
409
+ const { currentDepth = 0, maxDepth = Infinity, mode = "eager", subdirHandler, existingApi } = options;
395
410
 
396
411
 
397
412
  return buildCategoryStructure(categoryPath, {
@@ -399,7 +414,8 @@ const slothletObject = {
399
414
  maxDepth,
400
415
  mode,
401
416
  subdirHandler,
402
- instance: this
417
+ instance: this,
418
+ existingApi
403
419
  });
404
420
  },
405
421
 
@@ -636,6 +652,17 @@ const slothletObject = {
636
652
  }
637
653
 
638
654
 
655
+ const instanceIdDesc = Object.getOwnPropertyDescriptor(boundApi, "instanceId");
656
+ if (!instanceIdDesc || instanceIdDesc.configurable) {
657
+ Object.defineProperty(boundApi, "instanceId", {
658
+ value: this.instanceId,
659
+ writable: false,
660
+ configurable: true,
661
+ enumerable: false
662
+ });
663
+ }
664
+
665
+
639
666
  const scopeDesc = Object.getOwnPropertyDescriptor(boundApi, "scope");
640
667
  if (!scopeDesc || scopeDesc.configurable) {
641
668
  Object.defineProperty(boundApi, "scope", {
@@ -678,8 +705,45 @@ const slothletObject = {
678
705
  },
679
706
 
680
707
 
681
- async addApi(apiPath, folderPath) {
682
- return addApiFromFolder({ apiPath, folderPath, instance: this });
708
+ _registerApiOwnership(apiPath, moduleId) {
709
+ if (!this.config.enableModuleOwnership) return;
710
+ this._moduleOwnership.set(apiPath, moduleId);
711
+ if (this.config.debug) {
712
+ console.log(`[DEBUG] Registered ownership: ${apiPath} -> ${moduleId}`);
713
+ }
714
+ },
715
+
716
+
717
+ _getApiOwnership(apiPath) {
718
+ if (!this.config.enableModuleOwnership) return null;
719
+ return this._moduleOwnership.get(apiPath) || null;
720
+ },
721
+
722
+
723
+ _validateModuleOwnership(apiPath, moduleId, forceOverwrite) {
724
+ if (!this.config.enableModuleOwnership || !forceOverwrite) return false;
725
+
726
+ const existingOwner = this._getApiOwnership(apiPath);
727
+ if (!existingOwner) {
728
+
729
+ return true;
730
+ }
731
+
732
+
733
+ if (existingOwner === moduleId) {
734
+ return true;
735
+ }
736
+
737
+
738
+ if (this.config.debug) {
739
+ console.log(`[DEBUG] Ownership conflict: ${apiPath} owned by ${existingOwner}, attempted by ${moduleId}`);
740
+ }
741
+ return false;
742
+ },
743
+
744
+
745
+ async addApi(apiPath, folderPath, metadata = {}, options = {}) {
746
+ return addApiFromFolder({ apiPath, folderPath, instance: this, metadata, options });
683
747
  },
684
748
 
685
749