@cldmv/slothlet 2.7.1 → 2.8.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.
package/README.md CHANGED
@@ -464,6 +464,7 @@ Creates and loads an API instance with the specified configuration.
464
464
  | `apiDepth` | `number` | `Infinity` | Directory traversal depth control - `0` for root only, `Infinity` for all levels |
465
465
  | `debug` | `boolean` | `false` | Enable verbose logging. Can also be set via `--slothletdebug` command line flag or `SLOTHLET_DEBUG=true` environment variable |
466
466
  | `api_mode` | `string` | `"auto"` | API structure behavior when root-level default functions exist:<br/>• `"auto"`: Automatically detects if root has default function export and creates callable API<br/>• `"function"`: Forces API to be callable (use when you have root-level default function exports)<br/>• `"object"`: Forces API to be object-only (use when you want object interface regardless of exports) |
467
+ | `allowApiOverwrite` | `boolean` | `true` | Controls whether `addApi()` can overwrite existing API endpoints:<br/>• `true`: Allow overwrites (default, backwards compatible)<br/>• `false`: Prevent overwrites - logs warning and skips when attempting to overwrite existing endpoints<br/>Applies to both function and object overwrites at the final key of the API path |
467
468
  | `context` | `object` | `{}` | Context data object injected into live-binding `context` reference. Available to all loaded modules via `import { context } from "@cldmv/slothlet/runtime"` |
468
469
  | `reference` | `object` | `{}` | Reference object merged into the API root level. Properties not conflicting with loaded modules are added directly to the API |
469
470
  | `sanitize` | `object` | `{}` | **🔧 NEW**: Advanced filename-to-API transformation control. Options: `lowerFirst` (boolean), `preserveAllUpper` (boolean), `preserveAllLower` (boolean), `rules` object with `leave` (exact case-sensitive), `leaveInsensitive` (case-insensitive), `upper`/`lower` arrays. Supports exact matches, glob patterns (`*json*`, `http*`), and boundary patterns (`**url**` for surrounded matches only) |
@@ -525,8 +526,9 @@ Returns true if the API is loaded.
525
526
  Gracefully shuts down the API and performs comprehensive resource cleanup to prevent hanging processes.
526
527
 
527
528
  **Cleanup includes:**
529
+
528
530
  - Hook manager state and registered hooks
529
- - AsyncLocalStorage context and bindings
531
+ - AsyncLocalStorage context and bindings
530
532
  - EventEmitter listeners and AsyncResource instances (including third-party libraries)
531
533
  - Instance data and runtime coordination
532
534
 
@@ -535,6 +537,107 @@ Gracefully shuts down the API and performs comprehensive resource cleanup to pre
535
537
  > [!IMPORTANT]
536
538
  > **🛡️ Process Cleanup**: The shutdown method now performs comprehensive cleanup of all EventEmitter listeners created after slothlet loads, including those from third-party libraries like pg-pool. This prevents hanging AsyncResource instances that could prevent your Node.js process from exiting cleanly.
537
539
 
540
+ #### `api.addApi(apiPath, folderPath)` ⇒ `Promise<void>` ⭐ NEW
541
+
542
+ Dynamically extend your API at runtime by loading additional modules and merging them into a specified path.
543
+
544
+ **Parameters:**
545
+
546
+ | Param | Type | Description |
547
+ | ------------ | -------- | ------------------------------------------------------------------- |
548
+ | `apiPath` | `string` | Dotted path where modules will be added (e.g., `"runtime.plugins"`) |
549
+ | `folderPath` | `string` | Path to folder containing modules to load (relative or absolute) |
550
+
551
+ **Returns:** `Promise<void>` - Resolves when the API extension is complete
552
+
553
+ **Features:**
554
+
555
+ - ✅ **Dynamic Loading**: Add modules after initial API creation
556
+ - ✅ **Path Creation**: Automatically creates intermediate objects for nested paths
557
+ - ✅ **Smart Merging**: Merges into existing objects or creates new namespaces
558
+ - ✅ **Mode Respect**: Uses same loading mode (lazy/eager) as parent API
559
+ - ✅ **Live Binding Updates**: Automatically updates `self`, `context`, and `reference`
560
+ - ✅ **Function Support**: Can traverse through functions (slothlet's function.property pattern)
561
+ - ✅ **Validation**: Prevents extension through primitives, validates path format
562
+ - ✅ **Overwrite Protection**: Optional `allowApiOverwrite` config prevents accidental endpoint overwrites
563
+
564
+ **Configuration:**
565
+
566
+ The `allowApiOverwrite` option controls whether `addApi` can overwrite existing endpoints:
567
+
568
+ ```javascript
569
+ // Default behavior - allows overwrites (backwards compatible)
570
+ const api = await slothlet({
571
+ dir: "./api",
572
+ allowApiOverwrite: true // default
573
+ });
574
+ await api.addApi("tools", "./new-tools"); // Overwrites existing api.tools
575
+
576
+ // Protected mode - prevents overwrites
577
+ const api = await slothlet({
578
+ dir: "./api",
579
+ allowApiOverwrite: false // protection enabled
580
+ });
581
+ await api.addApi("tools", "./new-tools"); // Logs warning and skips
582
+ // Console: "[slothlet] Skipping addApi: API path "tools" final key "tools" already exists..."
583
+ ```
584
+
585
+ **Usage Examples:**
586
+
587
+ ```javascript
588
+ const api = await slothlet({ dir: "./api" });
589
+
590
+ // Add modules to nested path
591
+ await api.addApi("runtime.plugins", "./plugins");
592
+ api.runtime.plugins.myPlugin(); // New modules accessible
593
+
594
+ // Add to root level
595
+ await api.addApi("utilities", "./utils");
596
+ api.utilities.helperFunc(); // Root-level addition
597
+
598
+ // Deep nesting (creates intermediate objects)
599
+ await api.addApi("services.external.github", "./integrations/github");
600
+ api.services.external.github.getUser(); // Deep path created
601
+
602
+ // Merge into existing namespace
603
+ await api.addApi("math", "./advanced-math"); // Merges with existing api.math
604
+ api.math.add(2, 3); // Original functions preserved
605
+ api.math.advancedCalc(); // New functions added
606
+ ```
607
+
608
+ **Path Validation:**
609
+
610
+ ```javascript
611
+ // ❌ Invalid paths throw errors
612
+ await api.addApi("", "./modules"); // Empty string
613
+ await api.addApi(".path", "./modules"); // Leading dot
614
+ await api.addApi("path.", "./modules"); // Trailing dot
615
+ await api.addApi("path..name", "./modules"); // Consecutive dots
616
+
617
+ // ✅ Valid paths
618
+ await api.addApi("simple", "./modules"); // Single segment
619
+ await api.addApi("nested.path", "./modules"); // Dotted path
620
+ await api.addApi("very.deep.nested", "./modules"); // Multi-level
621
+ ```
622
+
623
+ **Type Safety:**
624
+
625
+ ```javascript
626
+ // ✅ Can extend through objects and functions
627
+ api.logger = { info: () => {} };
628
+ await api.addApi("logger.plugins", "./logger-plugins"); // Works - object
629
+
630
+ api.handler = () => "handler";
631
+ await api.addApi("handler.middleware", "./middleware"); // Works - function
632
+
633
+ // ❌ Cannot extend through primitives
634
+ api.config.timeout = 5000;
635
+ await api.addApi("config.timeout.advanced", "./modules"); // Throws error
636
+ ```
637
+
638
+ > [!WARNING]
639
+ > **⚠️ Concurrency Limitation:** The `addApi` method is **not thread-safe**. Concurrent calls to `addApi` with overlapping paths may result in race conditions and inconsistent API state. Ensure calls are properly sequenced using `await` or other synchronization mechanisms. Do not call `addApi` from multiple threads/workers simultaneously.
640
+
538
641
  > [!NOTE]
539
642
  > **📚 For detailed API documentation with comprehensive parameter descriptions, method signatures, and examples, see [docs/API.md](https://github.com/CLDMV/slothlet/blob/HEAD/docs/API.md)**
540
643
 
package/dist/slothlet.mjs CHANGED
@@ -140,7 +140,7 @@ const slothletObject = {
140
140
  reference: {},
141
141
  mode: "singleton",
142
142
  loaded: false,
143
- config: { lazy: false, apiDepth: Infinity, debug: DEBUG, dir: null, sanitize: null },
143
+ config: { lazy: false, apiDepth: Infinity, debug: DEBUG, dir: null, sanitize: null, allowApiOverwrite: true },
144
144
  _dispose: null,
145
145
  _boundAPIShutdown: null,
146
146
  instanceId: null,
@@ -994,9 +994,12 @@ const slothletObject = {
994
994
 
995
995
 
996
996
 
997
+
998
+
999
+ const instance = this;
997
1000
  this.safeDefine(boundApi, "describe", function (showAll = false) {
998
1001
 
999
- if (this.config && this.config.lazy) {
1002
+ if (instance.config && instance.config.lazy) {
1000
1003
  if (!showAll) {
1001
1004
  return Reflect.ownKeys(boundApi);
1002
1005
  }
@@ -1052,17 +1055,36 @@ const slothletObject = {
1052
1055
  } else {
1053
1056
  this._boundAPIShutdown = null;
1054
1057
  }
1058
+
1059
+
1060
+
1061
+
1062
+ const hasUserDefinedShutdown = this._boundAPIShutdown !== null;
1063
+
1055
1064
  const shutdownDesc = Object.getOwnPropertyDescriptor(boundApi, "shutdown");
1056
1065
  if (!shutdownDesc || shutdownDesc.configurable) {
1057
1066
  Object.defineProperty(boundApi, "shutdown", {
1058
1067
  value: this.shutdown.bind(this),
1059
1068
  writable: true,
1060
1069
  configurable: true,
1061
- enumerable: true
1070
+ enumerable: hasUserDefinedShutdown
1062
1071
  });
1063
1072
  } else if (this.config && this.config.debug) {
1064
1073
  console.warn("Could not redefine boundApi.shutdown: not configurable");
1065
1074
  }
1075
+
1076
+
1077
+ const addApiDesc = Object.getOwnPropertyDescriptor(boundApi, "addApi");
1078
+ if (!addApiDesc || addApiDesc.configurable) {
1079
+ Object.defineProperty(boundApi, "addApi", {
1080
+ value: this.addApi.bind(this),
1081
+ writable: true,
1082
+ configurable: true,
1083
+ enumerable: false
1084
+ });
1085
+ } else if (this.config && this.config.debug) {
1086
+ console.warn("Could not redefine boundApi.addApi: not configurable");
1087
+ }
1066
1088
 
1067
1089
 
1068
1090
 
@@ -1074,21 +1096,21 @@ const slothletObject = {
1074
1096
  },
1075
1097
 
1076
1098
 
1077
- safeDefine(obj, key, value) {
1099
+ safeDefine(obj, key, value, enumerable = false) {
1078
1100
  const desc = Object.getOwnPropertyDescriptor(obj, key);
1079
1101
  if (!desc) {
1080
1102
  Object.defineProperty(obj, key, {
1081
1103
  value,
1082
1104
  writable: true,
1083
1105
  configurable: true,
1084
- enumerable: true
1106
+ enumerable
1085
1107
  });
1086
1108
  } else if (desc.configurable) {
1087
1109
  Object.defineProperty(obj, key, {
1088
1110
  value,
1089
1111
  writable: true,
1090
1112
  configurable: true,
1091
- enumerable: true
1113
+ enumerable
1092
1114
  });
1093
1115
  } else if (this.config && this.config.debug) {
1094
1116
  console.warn(`Could not redefine boundApi.${key}: not configurable`);
@@ -1111,6 +1133,220 @@ const slothletObject = {
1111
1133
  },
1112
1134
 
1113
1135
 
1136
+ async addApi(apiPath, folderPath) {
1137
+ if (!this.loaded) {
1138
+ throw new Error("[slothlet] Cannot add API: API not loaded. Call create() or load() first.");
1139
+ }
1140
+
1141
+
1142
+ if (typeof apiPath !== "string") {
1143
+ throw new TypeError("[slothlet] addApi: 'apiPath' must be a string.");
1144
+ }
1145
+ const normalizedApiPath = apiPath.trim();
1146
+ if (normalizedApiPath === "") {
1147
+ throw new TypeError("[slothlet] addApi: 'apiPath' must be a non-empty, non-whitespace string.");
1148
+ }
1149
+ const pathParts = normalizedApiPath.split(".");
1150
+ if (pathParts.some((part) => part === "")) {
1151
+ throw new Error(`[slothlet] addApi: 'apiPath' must not contain empty segments. Received: "${normalizedApiPath}"`);
1152
+ }
1153
+
1154
+
1155
+ if (typeof folderPath !== "string") {
1156
+ throw new TypeError("[slothlet] addApi: 'folderPath' must be a string.");
1157
+ }
1158
+
1159
+
1160
+ let resolvedFolderPath = folderPath;
1161
+ if (!path.isAbsolute(folderPath)) {
1162
+ resolvedFolderPath = resolvePathFromCaller(folderPath);
1163
+ }
1164
+
1165
+
1166
+ let stats;
1167
+ try {
1168
+ stats = await fs.stat(resolvedFolderPath);
1169
+ } catch (error) {
1170
+ throw new Error(`[slothlet] addApi: Cannot access folder: ${resolvedFolderPath} - ${error.message}`);
1171
+ }
1172
+ if (!stats.isDirectory()) {
1173
+ throw new Error(`[slothlet] addApi: Path is not a directory: ${resolvedFolderPath}`);
1174
+ }
1175
+
1176
+ if (this.config.debug) {
1177
+ console.log(`[DEBUG] addApi: Loading modules from ${resolvedFolderPath} to path: ${normalizedApiPath}`);
1178
+ }
1179
+
1180
+
1181
+ let newModules;
1182
+ if (this.config.lazy) {
1183
+
1184
+ newModules = await this.modes.lazy.create.call(this, resolvedFolderPath, this.config.apiDepth || Infinity, 0);
1185
+ } else {
1186
+
1187
+ newModules = await this.modes.eager.create.call(this, resolvedFolderPath, this.config.apiDepth || Infinity, 0);
1188
+ }
1189
+
1190
+ if (this.config.debug) {
1191
+ if (newModules && typeof newModules === "object") {
1192
+ console.log(`[DEBUG] addApi: Loaded modules:`, Object.keys(newModules));
1193
+ } else {
1194
+ console.log(
1195
+ `[DEBUG] addApi: Loaded modules (non-object):`,
1196
+ typeof newModules === "function" ? `[Function: ${newModules.name || "anonymous"}]` : newModules
1197
+ );
1198
+ }
1199
+ }
1200
+
1201
+
1202
+ let currentTarget = this.api;
1203
+ let currentBoundTarget = this.boundapi;
1204
+
1205
+ for (let i = 0; i < pathParts.length - 1; i++) {
1206
+ const part = pathParts[i];
1207
+ const key = this._toapiPathKey(part);
1208
+
1209
+
1210
+
1211
+
1212
+ if (Object.prototype.hasOwnProperty.call(currentTarget, key)) {
1213
+ const existing = currentTarget[key];
1214
+ if (existing === null || (typeof existing !== "object" && typeof existing !== "function")) {
1215
+ throw new Error(
1216
+ `[slothlet] Cannot extend API path "${normalizedApiPath}" through segment "${part}": ` +
1217
+ `existing value is type "${typeof existing}", cannot add properties.`
1218
+ );
1219
+ }
1220
+
1221
+
1222
+ } else {
1223
+ currentTarget[key] = {};
1224
+ }
1225
+ if (Object.prototype.hasOwnProperty.call(currentBoundTarget, key)) {
1226
+ const existingBound = currentBoundTarget[key];
1227
+ if (existingBound === null || (typeof existingBound !== "object" && typeof existingBound !== "function")) {
1228
+ throw new Error(
1229
+ `[slothlet] Cannot extend bound API path "${normalizedApiPath}" through segment "${part}": ` +
1230
+ `existing value is type "${typeof existingBound}", cannot add properties.`
1231
+ );
1232
+ }
1233
+
1234
+ } else {
1235
+ currentBoundTarget[key] = {};
1236
+ }
1237
+
1238
+
1239
+ currentTarget = currentTarget[key];
1240
+ currentBoundTarget = currentBoundTarget[key];
1241
+ }
1242
+
1243
+
1244
+ const finalKey = this._toapiPathKey(pathParts[pathParts.length - 1]);
1245
+
1246
+
1247
+ if (typeof newModules === "function") {
1248
+
1249
+
1250
+ if (Object.prototype.hasOwnProperty.call(currentTarget, finalKey)) {
1251
+ const existing = currentTarget[finalKey];
1252
+
1253
+
1254
+ if (this.config.allowApiOverwrite === false) {
1255
+ console.warn(
1256
+ `[slothlet] Skipping addApi: API path "${normalizedApiPath}" final key "${finalKey}" ` +
1257
+ `already exists (type: "${typeof existing}"). Set allowApiOverwrite: true to allow overwrites.`
1258
+ );
1259
+ return;
1260
+ }
1261
+
1262
+
1263
+ if (existing !== null && typeof existing !== "function") {
1264
+ console.warn(
1265
+ `[slothlet] Overwriting existing non-function value at API path "${normalizedApiPath}" ` +
1266
+ `final key "${finalKey}" with a function. Previous type: "${typeof existing}".`
1267
+ );
1268
+ } else if (typeof existing === "function") {
1269
+
1270
+ console.warn(
1271
+ `[slothlet] Overwriting existing function at API path "${normalizedApiPath}" ` + `final key "${finalKey}" with a new function.`
1272
+ );
1273
+ }
1274
+ }
1275
+ currentTarget[finalKey] = newModules;
1276
+ currentBoundTarget[finalKey] = newModules;
1277
+ } else if (typeof newModules === "object" && newModules !== null) {
1278
+
1279
+ if (Object.prototype.hasOwnProperty.call(currentTarget, finalKey)) {
1280
+ const existing = currentTarget[finalKey];
1281
+
1282
+
1283
+ if (this.config.allowApiOverwrite === false && existing !== undefined && existing !== null) {
1284
+
1285
+ const hasContent = typeof existing === "object" ? Object.keys(existing).length > 0 : true;
1286
+ if (hasContent) {
1287
+ console.warn(
1288
+ `[slothlet] Skipping addApi merge: API path "${normalizedApiPath}" final key "${finalKey}" ` +
1289
+ `already exists with content (type: "${typeof existing}"). Set allowApiOverwrite: true to allow merging.`
1290
+ );
1291
+ return;
1292
+ }
1293
+ }
1294
+
1295
+ if (existing !== null && typeof existing !== "object" && typeof existing !== "function") {
1296
+ throw new Error(
1297
+ `[slothlet] Cannot merge API at "${normalizedApiPath}": ` +
1298
+ `existing value at final key "${finalKey}" is type "${typeof existing}", cannot merge into primitives.`
1299
+ );
1300
+ }
1301
+ }
1302
+ if (Object.prototype.hasOwnProperty.call(currentBoundTarget, finalKey)) {
1303
+ const existingBound = currentBoundTarget[finalKey];
1304
+ if (existingBound !== null && typeof existingBound !== "object" && typeof existingBound !== "function") {
1305
+ throw new Error(
1306
+ `[slothlet] Cannot merge bound API at "${normalizedApiPath}": ` +
1307
+ `existing value at final key "${finalKey}" is type "${typeof existingBound}", cannot merge into primitives.`
1308
+ );
1309
+ }
1310
+ }
1311
+
1312
+
1313
+ if (!currentTarget[finalKey]) {
1314
+ currentTarget[finalKey] = {};
1315
+ }
1316
+ if (!currentBoundTarget[finalKey]) {
1317
+ currentBoundTarget[finalKey] = {};
1318
+ }
1319
+
1320
+
1321
+
1322
+
1323
+
1324
+
1325
+ Object.assign(currentTarget[finalKey], newModules);
1326
+ Object.assign(currentBoundTarget[finalKey], newModules);
1327
+ } else if (newModules === null || newModules === undefined) {
1328
+
1329
+ const receivedType = newModules === null ? "null" : "undefined";
1330
+ console.warn(
1331
+ `[slothlet] addApi: No modules loaded from folder at API path "${normalizedApiPath}". ` +
1332
+ `Loaded modules resulted in ${receivedType}. Check that the folder contains valid module files.`
1333
+ );
1334
+ } else {
1335
+
1336
+
1337
+ currentTarget[finalKey] = newModules;
1338
+ currentBoundTarget[finalKey] = newModules;
1339
+ }
1340
+
1341
+
1342
+ this.updateBindings(this.context, this.reference, this.boundapi);
1343
+
1344
+ if (this.config.debug) {
1345
+ console.log(`[DEBUG] addApi: Successfully added modules at ${normalizedApiPath}`);
1346
+ }
1347
+ },
1348
+
1349
+
1114
1350
  async shutdown() {
1115
1351
 
1116
1352
 
@@ -1216,6 +1452,18 @@ export function mutateLiveBindingFunction(target, source) {
1216
1452
  }
1217
1453
  }
1218
1454
 
1455
+ const managementMethods = ["shutdown", "addApi", "describe"];
1456
+ for (const method of managementMethods) {
1457
+ const desc = Object.getOwnPropertyDescriptor(source, method);
1458
+ if (desc) {
1459
+ try {
1460
+ Object.defineProperty(target, method, desc);
1461
+ } catch {
1462
+
1463
+ }
1464
+ }
1465
+ }
1466
+
1219
1467
  if (typeof source._impl === "function") {
1220
1468
  target._impl = source._impl;
1221
1469
  }
@@ -1230,3 +1478,5 @@ export default slothlet;
1230
1478
 
1231
1479
 
1232
1480
 
1481
+
1482
+
package/index.cjs CHANGED
@@ -30,9 +30,10 @@
30
30
  * @param {string} [options.mode="singleton"] - Execution mode (singleton, vm, worker, fork)
31
31
  * @param {string} [options.api_mode="auto"] - API structure mode (auto, function, object)
32
32
  * @param {string} [options.runtime] - Runtime type ("async", "asynclocalstorage", "live", "livebindings", "experimental")
33
+ * @param {boolean} [options.allowApiOverwrite=true] - Allow addApi to overwrite existing API endpoints
33
34
  * @param {object} [options.context={}] - Context data for live bindings
34
35
  * @param {object} [options.reference={}] - Reference objects to merge into API root
35
- * @returns {Promise<function|object>} The bound API object with live-binding context
36
+ * @returns {Promise<import("./src/slothlet.mjs").SlothletAPI>} The bound API object with management methods
36
37
  *
37
38
  * @example // CJS usage
38
39
  * const slothlet = require("@cldmv/slothlet");
package/index.mjs CHANGED
@@ -55,9 +55,10 @@ function normalizeRuntimeType(runtime) {
55
55
  * @param {number} [options.apiDepth=Infinity] - Maximum directory depth to scan
56
56
  * @param {boolean} [options.debug=false] - Enable debug logging
57
57
  * @param {string} [options.api_mode="auto"] - API structure mode (auto, function, object)
58
+ * @param {boolean} [options.allowApiOverwrite=true] - Allow addApi to overwrite existing API endpoints
58
59
  * @param {object} [options.context={}] - Context data for live bindings
59
60
  * @param {object} [options.reference={}] - Reference objects to merge into API root
60
- * @returns {Promise<function|object>} The bound API object with live-binding context
61
+ * @returns {Promise<import("./src/slothlet.mjs").SlothletAPI>} The bound API object with management methods
61
62
  *
62
63
  * @example // ESM
63
64
  * import slothlet from "@cldmv/slothlet";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cldmv/slothlet",
3
- "version": "2.7.1",
3
+ "version": "2.8.0",
4
4
  "moduleVersions": {
5
5
  "lazy": "1.3.1",
6
6
  "eager": "1.3.1"
@@ -92,6 +92,13 @@ export type SlothletOptions = {
92
92
  * - `"object"`: Force API to be plain object with method properties
93
93
  */
94
94
  api_mode?: string;
95
+ /**
96
+ * - Controls whether addApi can overwrite existing API endpoints:
97
+ * - `true`: Allow overwrites (default, backwards compatible)
98
+ * - `false`: Prevent overwrites, log warning and skip when attempting to overwrite existing endpoints
99
+ * - Applies to both function and object overwrites at the final key of the API path
100
+ */
101
+ allowApiOverwrite?: boolean;
95
102
  /**
96
103
  * - Context data object injected into live-binding `context` reference.
97
104
  * - Available to all loaded modules via `import { context } from "@cldmv/slothlet/runtime"`. Useful for request data,
@@ -121,14 +128,28 @@ export type SlothletOptions = {
121
128
  };
122
129
  };
123
130
  };
131
+ export type SlothletAPI = {
132
+ /**
133
+ * - Shuts down the API instance and cleans up all resources
134
+ */
135
+ shutdown: () => Promise<void>;
136
+ /**
137
+ * - Dynamically adds API modules from a folder to a specified API path
138
+ */
139
+ addApi: (apiPath: string, folderPath: string) => Promise<void>;
140
+ /**
141
+ * - Returns metadata about the current API instance configuration. In lazy mode with showAll=false, returns an array of property keys. In lazy mode with showAll=true, returns a Promise resolving to an object. In eager mode, returns a plain object.
142
+ */
143
+ describe: (showAll?: boolean) => ((string | symbol)[] | object | Promise<object>);
144
+ };
124
145
  /**
125
146
  * Creates a slothlet API instance with the specified configuration.
126
147
  * This is the main entry point that can be called directly as a function.
127
148
  * @async
128
149
  * @alias module:@cldmv/slothlet
129
150
  * @param {SlothletOptions} [options={}] - Configuration options for creating the API
130
- * @returns {Promise<function|object>} The bound API object or function
151
+ * @returns {Promise<SlothletAPI>} The bound API object or function with management methods
131
152
  * @public
132
153
  */
133
- export function slothlet(options?: SlothletOptions): Promise<Function | object>;
154
+ export function slothlet(options?: SlothletOptions): Promise<SlothletAPI>;
134
155
  //# sourceMappingURL=slothlet.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"slothlet.d.mts","sourceRoot":"","sources":["../../dist/slothlet.mjs"],"names":[],"mappings":"AAkjDA;;;;;;;;;GASG;AACH,kDARW,WAAS,MAAM,UACf,WAAS,MAAM,QAwCzB;AAt6CD;;;;;;;GAOG;AACH,mBAJU,MAAM,CAIO;AAEvB;;;;;GAKG;AACH,sBAJU,MAAM,CAIU;AAE1B;;;;;GAKG;AACH,wBAJU,MAAM,CAIY;;;;;;;;;UAy5Cd,MAAM;;;;;;WAIN,OAAO;;;;;;;;WAGP,MAAM;;;;;;;;aAKN,MAAM;;;;;;;cAKN,MAAM;;;;;;;eAIN,MAAM;;;;;;;;YAIN,OAAO;;;;;;;eAKP,MAAM;;;;;;cAIN,MAAM;;;;;;gBAGN,MAAM;;;;;;eAMjB;QAA8B,UAAU,GAA7B,OAAO;QACY,gBAAgB,GAAnC,OAAO;QACY,gBAAgB,GAAnC,OAAO;QACW,KAAK,GAClC;YAAqC,KAAK,GAA/B,MAAM,EAAE;YACkB,gBAAgB,GAA1C,MAAM,EAAE;YACkB,KAAK,GAA/B,MAAM,EAAE;YACkB,KAAK,GAA/B,MAAM,EAAE;SACrB;KAAA;;AA18CD;;;;;;;;GAQG;AACH,mCAJW,eAAe,GACb,OAAO,CAAC,WAAS,MAAM,CAAC,CAiCpC"}
1
+ {"version":3,"file":"slothlet.d.mts","sourceRoot":"","sources":["../../dist/slothlet.mjs"],"names":[],"mappings":"AA80DA;;;;;;;;;GASG;AACH,kDARW,WAAS,MAAM,UACf,WAAS,MAAM,QAoDzB;AA9sDD;;;;;;;GAOG;AACH,mBAJU,MAAM,CAIO;AAEvB;;;;;GAKG;AACH,sBAJU,MAAM,CAIU;AAE1B;;;;;GAKG;AACH,wBAJU,MAAM,CAIY;;;;;;;;;UAisDd,MAAM;;;;;;WAIN,OAAO;;;;;;;;WAGP,MAAM;;;;;;;;aAKN,MAAM;;;;;;;cAKN,MAAM;;;;;;;eAIN,MAAM;;;;;;;;YAIN,OAAO;;;;;;;eAKP,MAAM;;;;;;;wBAIN,OAAO;;;;;;cAIP,MAAM;;;;;;gBAGN,MAAM;;;;;;eAMjB;QAA8B,UAAU,GAA7B,OAAO;QACY,gBAAgB,GAAnC,OAAO;QACY,gBAAgB,GAAnC,OAAO;QACW,KAAK,GAClC;YAAqC,KAAK,GAA/B,MAAM,EAAE;YACkB,gBAAgB,GAA1C,MAAM,EAAE;YACkB,KAAK,GAA/B,MAAM,EAAE;YACkB,KAAK,GAA/B,MAAM,EAAE;SACrB;KAAA;;;;;;cAIa,MAAM,OAAO,CAAC,IAAI,CAAC;;;;YACnB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC;;;;cACtD,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC,CAAC,MAAM,GAAC,MAAM,CAAC,EAAE,GAAC,MAAM,GAAC,OAAO,CAAC,MAAM,CAAC,CAAC;;AA5vD/E;;;;;;;;GAQG;AACH,mCAJW,eAAe,GACb,OAAO,CAAC,WAAW,CAAC,CAiChC"}