@cldmv/slothlet 2.8.0 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENT-USAGE.md +1 -1
- package/README.md +300 -1557
- package/dist/lib/engine/slothlet_child.mjs +1 -1
- package/dist/lib/engine/slothlet_engine.mjs +1 -1
- package/dist/lib/engine/slothlet_esm.mjs +1 -1
- package/dist/lib/engine/slothlet_helpers.mjs +1 -1
- package/dist/lib/engine/slothlet_worker.mjs +1 -1
- package/dist/lib/helpers/als-eventemitter.mjs +5 -6
- package/dist/lib/helpers/api_builder/add_api.mjs +292 -0
- package/dist/lib/helpers/api_builder/analysis.mjs +532 -0
- package/dist/lib/helpers/api_builder/construction.mjs +457 -0
- package/dist/lib/helpers/api_builder/decisions.mjs +737 -0
- package/dist/lib/helpers/api_builder/metadata.mjs +248 -0
- package/dist/lib/helpers/api_builder.mjs +17 -1568
- package/dist/lib/helpers/auto-wrap.mjs +1 -1
- package/dist/lib/helpers/hooks.mjs +1 -1
- package/dist/lib/helpers/instance-manager.mjs +1 -1
- package/dist/lib/helpers/metadata-api.mjs +201 -0
- package/dist/lib/helpers/multidefault.mjs +12 -3
- package/dist/lib/helpers/resolve-from-caller.mjs +1 -1
- package/dist/lib/helpers/sanitize.mjs +1 -1
- package/dist/lib/helpers/utilities.mjs +121 -0
- package/dist/lib/modes/slothlet_eager.mjs +1 -1
- package/dist/lib/modes/slothlet_lazy.mjs +10 -1
- package/dist/lib/runtime/runtime-asynclocalstorage.mjs +49 -18
- package/dist/lib/runtime/runtime-livebindings.mjs +23 -4
- package/dist/lib/runtime/runtime.mjs +15 -4
- package/dist/slothlet.mjs +164 -748
- package/docs/API-RULES-CONDITIONS.md +508 -0
- package/{API-RULES.md → docs/API-RULES.md} +127 -72
- package/package.json +11 -9
- package/types/dist/lib/helpers/als-eventemitter.d.mts.map +1 -1
- package/types/dist/lib/helpers/api_builder/add_api.d.mts +76 -0
- package/types/dist/lib/helpers/api_builder/add_api.d.mts.map +1 -0
- package/types/dist/lib/helpers/api_builder/analysis.d.mts +189 -0
- package/types/dist/lib/helpers/api_builder/analysis.d.mts.map +1 -0
- package/types/dist/lib/helpers/api_builder/construction.d.mts +107 -0
- package/types/dist/lib/helpers/api_builder/construction.d.mts.map +1 -0
- package/types/dist/lib/helpers/api_builder/decisions.d.mts +213 -0
- package/types/dist/lib/helpers/api_builder/decisions.d.mts.map +1 -0
- package/types/dist/lib/helpers/api_builder/metadata.d.mts +99 -0
- package/types/dist/lib/helpers/api_builder/metadata.d.mts.map +1 -0
- package/types/dist/lib/helpers/api_builder.d.mts +5 -448
- package/types/dist/lib/helpers/api_builder.d.mts.map +1 -1
- package/types/dist/lib/helpers/metadata-api.d.mts +132 -0
- package/types/dist/lib/helpers/metadata-api.d.mts.map +1 -0
- package/types/dist/lib/helpers/multidefault.d.mts.map +1 -1
- package/types/dist/lib/helpers/utilities.d.mts +120 -0
- package/types/dist/lib/helpers/utilities.d.mts.map +1 -0
- package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts +9 -0
- package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts.map +1 -1
- package/types/dist/lib/runtime/runtime-livebindings.d.mts +10 -0
- package/types/dist/lib/runtime/runtime-livebindings.d.mts.map +1 -1
- package/types/dist/lib/runtime/runtime.d.mts +1 -0
- package/types/dist/lib/runtime/runtime.d.mts.map +1 -1
- package/types/dist/slothlet.d.mts +0 -11
- package/types/dist/slothlet.d.mts.map +1 -1
- package/types/index.d.mts +0 -1
- package/API-RULES-CONDITIONS.md +0 -367
package/dist/slothlet.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright
|
|
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.
|
|
@@ -19,20 +19,23 @@
|
|
|
19
19
|
|
|
20
20
|
import fs from "node:fs/promises";
|
|
21
21
|
import path from "node:path";
|
|
22
|
-
import { types as utilTypes } from "node:util";
|
|
23
22
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
24
23
|
|
|
25
24
|
import { resolvePathFromCaller } from "@cldmv/slothlet/helpers/resolve-from-caller";
|
|
26
|
-
import { sanitizePathName } from "@cldmv/slothlet/helpers/sanitize";
|
|
27
25
|
import {
|
|
28
26
|
analyzeModule,
|
|
29
27
|
processModuleFromAnalysis,
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
buildCategoryStructure,
|
|
29
|
+
toapiPathKey,
|
|
30
|
+
shouldIncludeFile,
|
|
31
|
+
safeDefine,
|
|
32
|
+
deepMerge,
|
|
33
|
+
mutateLiveBindingFunction,
|
|
34
|
+
addApiFromFolder
|
|
32
35
|
} from "@cldmv/slothlet/helpers/api_builder";
|
|
33
|
-
import { updateInstanceData, cleanupInstance } from "
|
|
34
|
-
import { disableAlsForEventEmitters, cleanupAllSlothletListeners } from "
|
|
35
|
-
import { HookManager } from "
|
|
36
|
+
import { updateInstanceData, cleanupInstance } from "@cldmv/slothlet/helpers/instance-manager";
|
|
37
|
+
import { disableAlsForEventEmitters, cleanupAllSlothletListeners } from "@cldmv/slothlet/helpers/als-eventemitter";
|
|
38
|
+
import { HookManager } from "@cldmv/slothlet/helpers/hooks";
|
|
36
39
|
|
|
37
40
|
|
|
38
41
|
|
|
@@ -72,6 +75,11 @@ let DEBUG = process.argv.includes("--slothletdebug")
|
|
|
72
75
|
: false;
|
|
73
76
|
|
|
74
77
|
|
|
78
|
+
if (DEBUG && !process.env.SLOTHLET_DEBUG) {
|
|
79
|
+
process.env.SLOTHLET_DEBUG = "1";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
75
83
|
export const self = {};
|
|
76
84
|
|
|
77
85
|
|
|
@@ -177,31 +185,18 @@ const slothletObject = {
|
|
|
177
185
|
if (!this.modes) {
|
|
178
186
|
this.modes = {};
|
|
179
187
|
const modesDir = path.join(__dirname, "lib", "modes");
|
|
180
|
-
|
|
181
|
-
|
|
188
|
+
|
|
182
189
|
const dirents = await fs.readdir(modesDir, { withFileTypes: true });
|
|
183
190
|
const modeFiles = dirents.filter((d) => d.isFile()).map((d) => path.join(modesDir, d.name));
|
|
184
|
-
|
|
185
|
-
|
|
186
191
|
|
|
187
192
|
for (const file of modeFiles) {
|
|
188
|
-
|
|
189
|
-
|
|
190
193
|
const modePath = file;
|
|
191
194
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
195
|
const modeName = path.parse(file).name.replace(/^slothlet_/, "");
|
|
198
196
|
if (!modeName || modeName.includes(" ")) continue;
|
|
199
197
|
try {
|
|
200
198
|
|
|
201
199
|
const modeUrl = pathToFileURL(modePath).href;
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
200
|
|
|
206
201
|
const imported = await import(modeUrl);
|
|
207
202
|
if (imported && typeof imported === "object") {
|
|
@@ -213,10 +208,6 @@ const slothletObject = {
|
|
|
213
208
|
}
|
|
214
209
|
}
|
|
215
210
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
211
|
this.mode = executionEngine;
|
|
221
212
|
this.api_mode = api_mode;
|
|
222
213
|
let api;
|
|
@@ -224,7 +215,7 @@ const slothletObject = {
|
|
|
224
215
|
if (executionEngine === "singleton") {
|
|
225
216
|
|
|
226
217
|
|
|
227
|
-
const { context = null, reference = null, sanitize = null, hooks = false, engine, mode, ...loadConfig } = options;
|
|
218
|
+
const { context = null, reference = null, sanitize = null, hooks = false, scope, engine, mode, ...loadConfig } = options;
|
|
228
219
|
this.context = context;
|
|
229
220
|
this.reference = reference;
|
|
230
221
|
|
|
@@ -252,6 +243,20 @@ const slothletObject = {
|
|
|
252
243
|
this.hookManager = new HookManager(hooksEnabled, hooksPattern, { suppressErrors: hooksSuppressErrors });
|
|
253
244
|
|
|
254
245
|
|
|
246
|
+
if (scope && typeof scope === "object") {
|
|
247
|
+
const mergeStrategy = scope.merge || "shallow";
|
|
248
|
+
if (mergeStrategy !== "shallow" && mergeStrategy !== "deep") {
|
|
249
|
+
throw new TypeError(`Invalid scope.merge value: "${mergeStrategy}". Must be "shallow" or "deep".`);
|
|
250
|
+
}
|
|
251
|
+
this.config.scope = { merge: mergeStrategy };
|
|
252
|
+
} else if (scope === false) {
|
|
253
|
+
this.config.scope = { enabled: false };
|
|
254
|
+
} else {
|
|
255
|
+
|
|
256
|
+
this.config.scope = { merge: "shallow" };
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
|
|
255
260
|
if (sanitize !== null) {
|
|
256
261
|
this.config.sanitize = sanitize;
|
|
257
262
|
}
|
|
@@ -278,9 +283,6 @@ const slothletObject = {
|
|
|
278
283
|
await this.load(loadConfig, { context, reference });
|
|
279
284
|
|
|
280
285
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
286
|
return this.boundapi;
|
|
285
287
|
} else {
|
|
286
288
|
const { createEngine } = await import("./lib/engine/slothlet_engine.mjs");
|
|
@@ -318,17 +320,10 @@ const slothletObject = {
|
|
|
318
320
|
}
|
|
319
321
|
}
|
|
320
322
|
|
|
321
|
-
|
|
322
|
-
|
|
323
323
|
let apiDir = this.config.dir || "api";
|
|
324
324
|
|
|
325
325
|
if (apiDir && !path.isAbsolute(apiDir)) {
|
|
326
|
-
|
|
327
326
|
apiDir = resolvePathFromCaller(apiDir);
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
327
|
}
|
|
333
328
|
|
|
334
329
|
if (this.loaded) return this.api;
|
|
@@ -381,70 +376,22 @@ const slothletObject = {
|
|
|
381
376
|
}
|
|
382
377
|
}
|
|
383
378
|
|
|
384
|
-
|
|
385
|
-
|
|
386
379
|
const l_ctxRef = { ...{ context: null, reference: null }, ...ctxRef };
|
|
387
380
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
381
|
const _boundapi = this.createBoundApi(l_ctxRef.reference);
|
|
392
382
|
|
|
393
383
|
mutateLiveBindingFunction(this.boundapi, _boundapi);
|
|
394
384
|
|
|
395
385
|
this.updateBindings(this.context, this.reference, this.boundapi);
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
this.loaded = true;
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
386
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
387
|
+
this.loaded = true;
|
|
440
388
|
|
|
441
389
|
return this.boundapi;
|
|
442
390
|
},
|
|
443
391
|
|
|
444
392
|
|
|
445
393
|
_toapiPathKey(name) {
|
|
446
|
-
return
|
|
447
|
-
|
|
394
|
+
return toapiPathKey(name, this.config.sanitize || {});
|
|
448
395
|
},
|
|
449
396
|
|
|
450
397
|
|
|
@@ -452,288 +399,13 @@ const slothletObject = {
|
|
|
452
399
|
const { currentDepth = 0, maxDepth = Infinity, mode = "eager", subdirHandler } = options;
|
|
453
400
|
|
|
454
401
|
|
|
455
|
-
|
|
402
|
+
return buildCategoryStructure(categoryPath, {
|
|
456
403
|
currentDepth,
|
|
457
404
|
maxDepth,
|
|
458
405
|
mode,
|
|
459
406
|
subdirHandler,
|
|
460
407
|
instance: this
|
|
461
408
|
});
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
if (decisions.type === "single-file") {
|
|
465
|
-
const { singleFile } = decisions;
|
|
466
|
-
const { mod, moduleName } = singleFile;
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
if (decisions.shouldFlatten) {
|
|
470
|
-
switch (decisions.flattenType) {
|
|
471
|
-
case "function-folder-match":
|
|
472
|
-
case "default-function":
|
|
473
|
-
try {
|
|
474
|
-
Object.defineProperty(mod, "name", { value: decisions.preferredName, configurable: true });
|
|
475
|
-
} catch {
|
|
476
|
-
|
|
477
|
-
}
|
|
478
|
-
return mod;
|
|
479
|
-
|
|
480
|
-
case "default-export-flatten":
|
|
481
|
-
|
|
482
|
-
return mod;
|
|
483
|
-
|
|
484
|
-
case "object-auto-flatten":
|
|
485
|
-
|
|
486
|
-
return mod[decisions.preferredName];
|
|
487
|
-
|
|
488
|
-
case "parent-level-flatten": {
|
|
489
|
-
|
|
490
|
-
const exportValue = mod[Object.keys(mod).filter((k) => k !== "default")[0]];
|
|
491
|
-
return { [decisions.preferredName]: exportValue };
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
case "filename-folder-match-flatten":
|
|
495
|
-
|
|
496
|
-
return mod;
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
if (decisions.preferredName && decisions.preferredName !== moduleName) {
|
|
502
|
-
return { [decisions.preferredName]: mod };
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
return { [moduleName]: mod };
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
const categoryModules = {};
|
|
511
|
-
const { categoryName, processedModules, subdirectoryDecisions } = decisions;
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
for (const moduleDecision of processedModules) {
|
|
515
|
-
const { moduleName, mod, type, apiPathKey, shouldFlatten, flattenType, specialHandling, processedExports } = moduleDecision;
|
|
516
|
-
|
|
517
|
-
if (specialHandling === "category-merge") {
|
|
518
|
-
|
|
519
|
-
if (
|
|
520
|
-
Object.prototype.hasOwnProperty.call(mod, categoryName) &&
|
|
521
|
-
typeof mod[categoryName] === "object" &&
|
|
522
|
-
mod[categoryName] !== null
|
|
523
|
-
) {
|
|
524
|
-
Object.assign(categoryModules, mod[categoryName]);
|
|
525
|
-
for (const [key, value] of Object.entries(mod)) {
|
|
526
|
-
if (key !== categoryName) categoryModules[this._toapiPathKey(key)] = value;
|
|
527
|
-
}
|
|
528
|
-
} else {
|
|
529
|
-
Object.assign(categoryModules, mod);
|
|
530
|
-
}
|
|
531
|
-
} else if (type === "function") {
|
|
532
|
-
|
|
533
|
-
if (specialHandling === "multi-default-filename") {
|
|
534
|
-
try {
|
|
535
|
-
Object.defineProperty(mod, "name", { value: moduleName, configurable: true });
|
|
536
|
-
} catch {
|
|
537
|
-
|
|
538
|
-
}
|
|
539
|
-
categoryModules[moduleName] = mod;
|
|
540
|
-
} else if (specialHandling === "prefer-function-name") {
|
|
541
|
-
categoryModules[apiPathKey] = mod;
|
|
542
|
-
} else {
|
|
543
|
-
|
|
544
|
-
categoryModules[apiPathKey] = mod;
|
|
545
|
-
}
|
|
546
|
-
} else if (type === "self-referential") {
|
|
547
|
-
|
|
548
|
-
categoryModules[moduleName] = mod[moduleName] || mod;
|
|
549
|
-
} else if (type === "object") {
|
|
550
|
-
|
|
551
|
-
if (specialHandling === "preferred-export-names") {
|
|
552
|
-
Object.assign(categoryModules, processedExports);
|
|
553
|
-
} else if (shouldFlatten) {
|
|
554
|
-
switch (flattenType) {
|
|
555
|
-
case "single-default-object": {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
let flattened;
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
const defaultExport = mod.default;
|
|
562
|
-
const hasNamedExports = Object.keys(mod).some((k) => k !== "default");
|
|
563
|
-
|
|
564
|
-
if (hasNamedExports && defaultExport && typeof defaultExport === "object") {
|
|
565
|
-
|
|
566
|
-
const isProxy = utilTypes?.isProxy?.(defaultExport) ?? false;
|
|
567
|
-
|
|
568
|
-
if (isProxy) {
|
|
569
|
-
|
|
570
|
-
flattened = defaultExport;
|
|
571
|
-
let assignmentFailed = false;
|
|
572
|
-
|
|
573
|
-
const failedMap = new Map();
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
for (const [key, value] of Object.entries(mod)) {
|
|
577
|
-
if (key !== "default") {
|
|
578
|
-
try {
|
|
579
|
-
flattened[key] = value;
|
|
580
|
-
} catch (e) {
|
|
581
|
-
|
|
582
|
-
assignmentFailed = true;
|
|
583
|
-
failedMap.set(key, value);
|
|
584
|
-
if (this.config?.debug) {
|
|
585
|
-
console.warn(
|
|
586
|
-
`Could not assign '${key}' to proxy object in module '${moduleName}' at '${categoryPath}':`,
|
|
587
|
-
e.message
|
|
588
|
-
);
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
if (assignmentFailed) {
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
const originalProxy = flattened;
|
|
605
|
-
flattened = new Proxy(originalProxy, {
|
|
606
|
-
get(target, prop, receiver) {
|
|
607
|
-
|
|
608
|
-
if (failedMap.has(prop)) return failedMap.get(prop);
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
return Reflect.get(target, prop, receiver);
|
|
612
|
-
},
|
|
613
|
-
has(target, prop) {
|
|
614
|
-
|
|
615
|
-
if (failedMap.has(prop)) return true;
|
|
616
|
-
return Reflect.has(target, prop);
|
|
617
|
-
},
|
|
618
|
-
ownKeys(target) {
|
|
619
|
-
const originalKeys = Reflect.ownKeys(target);
|
|
620
|
-
const failedKeys = Array.from(failedMap.keys());
|
|
621
|
-
return [...new Set([...originalKeys, ...failedKeys])];
|
|
622
|
-
},
|
|
623
|
-
getOwnPropertyDescriptor(target, prop) {
|
|
624
|
-
if (failedMap.has(prop)) {
|
|
625
|
-
return { configurable: true, enumerable: true, value: failedMap.get(prop) };
|
|
626
|
-
}
|
|
627
|
-
return Reflect.getOwnPropertyDescriptor(target, prop);
|
|
628
|
-
}
|
|
629
|
-
});
|
|
630
|
-
}
|
|
631
|
-
} else {
|
|
632
|
-
|
|
633
|
-
flattened = { ...defaultExport };
|
|
634
|
-
for (const [key, value] of Object.entries(mod)) {
|
|
635
|
-
if (key !== "default") {
|
|
636
|
-
flattened[key] = value;
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
} else {
|
|
641
|
-
|
|
642
|
-
flattened = defaultExport;
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
categoryModules[apiPathKey] = flattened;
|
|
646
|
-
break;
|
|
647
|
-
}
|
|
648
|
-
case "multi-default-no-default": {
|
|
649
|
-
|
|
650
|
-
const moduleKeys = Object.keys(mod).filter((k) => k !== "default");
|
|
651
|
-
for (const key of moduleKeys) {
|
|
652
|
-
categoryModules[key] = mod[key];
|
|
653
|
-
}
|
|
654
|
-
break;
|
|
655
|
-
}
|
|
656
|
-
case "single-named-export-match":
|
|
657
|
-
|
|
658
|
-
categoryModules[apiPathKey] = mod[apiPathKey];
|
|
659
|
-
break;
|
|
660
|
-
case "category-name-match-flatten": {
|
|
661
|
-
|
|
662
|
-
const moduleKeys = Object.keys(mod).filter((k) => k !== "default");
|
|
663
|
-
for (const key of moduleKeys) {
|
|
664
|
-
categoryModules[key] = mod[key];
|
|
665
|
-
}
|
|
666
|
-
break;
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
} else {
|
|
670
|
-
|
|
671
|
-
categoryModules[apiPathKey] = mod;
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
for (const subDirDecision of subdirectoryDecisions) {
|
|
678
|
-
if (subDirDecision.shouldRecurse) {
|
|
679
|
-
const { name, path: subDirPath, apiPathKey } = subDirDecision;
|
|
680
|
-
let subModule;
|
|
681
|
-
|
|
682
|
-
if (mode === "lazy" && typeof subdirHandler === "function") {
|
|
683
|
-
subModule = subdirHandler({
|
|
684
|
-
subDirEntry: { name },
|
|
685
|
-
subDirPath,
|
|
686
|
-
key: apiPathKey,
|
|
687
|
-
categoryModules,
|
|
688
|
-
currentDepth,
|
|
689
|
-
maxDepth
|
|
690
|
-
});
|
|
691
|
-
} else {
|
|
692
|
-
subModule = await this._buildCategory(subDirPath, {
|
|
693
|
-
currentDepth: currentDepth + 1,
|
|
694
|
-
maxDepth,
|
|
695
|
-
mode: "eager"
|
|
696
|
-
});
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
if (
|
|
702
|
-
typeof subModule === "function" &&
|
|
703
|
-
subModule.name &&
|
|
704
|
-
subModule.name.toLowerCase() === apiPathKey.toLowerCase() &&
|
|
705
|
-
subModule.name !== apiPathKey
|
|
706
|
-
) {
|
|
707
|
-
|
|
708
|
-
categoryModules[subModule.name] = subModule;
|
|
709
|
-
} else {
|
|
710
|
-
categoryModules[apiPathKey] = subModule;
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
const keys = Object.keys(categoryModules);
|
|
717
|
-
if (keys.length === 1) {
|
|
718
|
-
const singleKey = keys[0];
|
|
719
|
-
if (singleKey === categoryName) {
|
|
720
|
-
const single = categoryModules[singleKey];
|
|
721
|
-
if (typeof single === "function") {
|
|
722
|
-
if (single.name !== categoryName) {
|
|
723
|
-
try {
|
|
724
|
-
Object.defineProperty(single, "name", { value: categoryName, configurable: true });
|
|
725
|
-
} catch {
|
|
726
|
-
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
return single;
|
|
730
|
-
} else if (single && typeof single === "object" && !Array.isArray(single)) {
|
|
731
|
-
return single;
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
return categoryModules;
|
|
737
409
|
},
|
|
738
410
|
|
|
739
411
|
|
|
@@ -763,117 +435,8 @@ const slothletObject = {
|
|
|
763
435
|
},
|
|
764
436
|
|
|
765
437
|
|
|
766
|
-
async _buildCategoryEnhanced(categoryPath, options = {}) {
|
|
767
|
-
const { currentDepth = 0, maxDepth = Infinity, mode = "eager", subdirHandler } = options;
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
const decisions = await getCategoryBuildingDecisions(categoryPath, {
|
|
771
|
-
instance: this,
|
|
772
|
-
currentDepth,
|
|
773
|
-
maxDepth,
|
|
774
|
-
debug: this.config.debug
|
|
775
|
-
});
|
|
776
|
-
|
|
777
|
-
const { processingStrategy, categoryName, processedModules, subDirectories } = decisions;
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
if (processingStrategy === "single-file" && processedModules.length === 1) {
|
|
781
|
-
const { processedModule, flattening } = processedModules[0];
|
|
782
|
-
|
|
783
|
-
if (flattening.shouldFlatten) {
|
|
784
|
-
|
|
785
|
-
if (typeof processedModule === "function") {
|
|
786
|
-
try {
|
|
787
|
-
Object.defineProperty(processedModule, "name", { value: flattening.apiPathKey, configurable: true });
|
|
788
|
-
} catch {
|
|
789
|
-
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
return processedModule;
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
return { [flattening.apiPathKey]: processedModule };
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
const categoryModules = {};
|
|
801
|
-
|
|
802
|
-
for (const { processedModule, flattening } of processedModules) {
|
|
803
|
-
categoryModules[flattening.apiPathKey] = processedModule;
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
for (const { dirEntry: subDirEntry, apiPathKey: key } of subDirectories) {
|
|
808
|
-
const subDirPath = path.join(categoryPath, subDirEntry.name);
|
|
809
|
-
let subModule;
|
|
810
|
-
|
|
811
|
-
if (mode === "lazy" && typeof subdirHandler === "function") {
|
|
812
|
-
subModule = subdirHandler({
|
|
813
|
-
subDirEntry,
|
|
814
|
-
subDirPath,
|
|
815
|
-
key,
|
|
816
|
-
categoryModules,
|
|
817
|
-
currentDepth,
|
|
818
|
-
maxDepth
|
|
819
|
-
});
|
|
820
|
-
} else {
|
|
821
|
-
|
|
822
|
-
subModule = await this._buildCategoryEnhanced(subDirPath, {
|
|
823
|
-
currentDepth: currentDepth + 1,
|
|
824
|
-
maxDepth,
|
|
825
|
-
mode: "eager"
|
|
826
|
-
});
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
if (
|
|
831
|
-
typeof subModule === "function" &&
|
|
832
|
-
subModule.name &&
|
|
833
|
-
subModule.name.toLowerCase() === key.toLowerCase() &&
|
|
834
|
-
subModule.name !== key
|
|
835
|
-
) {
|
|
836
|
-
categoryModules[subModule.name] = subModule;
|
|
837
|
-
} else {
|
|
838
|
-
categoryModules[key] = subModule;
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
const keys = Object.keys(categoryModules);
|
|
844
|
-
if (keys.length === 1) {
|
|
845
|
-
const singleKey = keys[0];
|
|
846
|
-
if (singleKey === categoryName) {
|
|
847
|
-
const single = categoryModules[singleKey];
|
|
848
|
-
if (typeof single === "function") {
|
|
849
|
-
if (single.name !== categoryName) {
|
|
850
|
-
try {
|
|
851
|
-
Object.defineProperty(single, "name", { value: categoryName, configurable: true });
|
|
852
|
-
} catch {
|
|
853
|
-
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
return single;
|
|
857
|
-
} else if (single && typeof single === "object" && !Array.isArray(single)) {
|
|
858
|
-
return single;
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
return categoryModules;
|
|
864
|
-
},
|
|
865
|
-
|
|
866
|
-
|
|
867
438
|
_shouldIncludeFile(entry) {
|
|
868
|
-
|
|
869
|
-
if (!entry.isFile()) return false;
|
|
870
|
-
|
|
871
|
-
if (!(entry.name.endsWith(".mjs") || entry.name.endsWith(".cjs") || entry.name.endsWith(".js"))) return false;
|
|
872
|
-
|
|
873
|
-
if (entry.name.startsWith(".")) return false;
|
|
874
|
-
|
|
875
|
-
if (entry.name.startsWith("__slothlet_")) return false;
|
|
876
|
-
return true;
|
|
439
|
+
return shouldIncludeFile(entry);
|
|
877
440
|
},
|
|
878
441
|
|
|
879
442
|
|
|
@@ -956,11 +519,7 @@ const slothletObject = {
|
|
|
956
519
|
createBoundApi(ref = null) {
|
|
957
520
|
if (!this.api) throw new Error("BindleApi modules not loaded. Call load() first.");
|
|
958
521
|
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
522
|
let boundApi;
|
|
963
|
-
|
|
964
523
|
|
|
965
524
|
boundApi = this.api;
|
|
966
525
|
|
|
@@ -978,24 +537,6 @@ const slothletObject = {
|
|
|
978
537
|
}
|
|
979
538
|
|
|
980
539
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
540
|
const instance = this;
|
|
1000
541
|
this.safeDefine(boundApi, "describe", function (showAll = false) {
|
|
1001
542
|
|
|
@@ -1085,6 +626,43 @@ const slothletObject = {
|
|
|
1085
626
|
} else if (this.config && this.config.debug) {
|
|
1086
627
|
console.warn("Could not redefine boundApi.addApi: not configurable");
|
|
1087
628
|
}
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
const runDesc = Object.getOwnPropertyDescriptor(boundApi, "run");
|
|
632
|
+
if (!runDesc || runDesc.configurable) {
|
|
633
|
+
Object.defineProperty(boundApi, "run", {
|
|
634
|
+
value: this.run.bind(this),
|
|
635
|
+
writable: true,
|
|
636
|
+
configurable: true,
|
|
637
|
+
enumerable: false
|
|
638
|
+
});
|
|
639
|
+
} else if (this.config && this.config.debug) {
|
|
640
|
+
console.warn("Could not redefine boundApi.run: not configurable");
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
const instanceIdDesc = Object.getOwnPropertyDescriptor(boundApi, "instanceId");
|
|
645
|
+
if (!instanceIdDesc || instanceIdDesc.configurable) {
|
|
646
|
+
Object.defineProperty(boundApi, "instanceId", {
|
|
647
|
+
value: this.instanceId,
|
|
648
|
+
writable: false,
|
|
649
|
+
configurable: true,
|
|
650
|
+
enumerable: false
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
const scopeDesc = Object.getOwnPropertyDescriptor(boundApi, "scope");
|
|
656
|
+
if (!scopeDesc || scopeDesc.configurable) {
|
|
657
|
+
Object.defineProperty(boundApi, "scope", {
|
|
658
|
+
value: this.scope.bind(this),
|
|
659
|
+
writable: true,
|
|
660
|
+
configurable: true,
|
|
661
|
+
enumerable: false
|
|
662
|
+
});
|
|
663
|
+
} else if (this.config && this.config.debug) {
|
|
664
|
+
console.warn("Could not redefine boundApi.scope: not configurable");
|
|
665
|
+
}
|
|
1088
666
|
|
|
1089
667
|
|
|
1090
668
|
|
|
@@ -1097,24 +675,7 @@ const slothletObject = {
|
|
|
1097
675
|
|
|
1098
676
|
|
|
1099
677
|
safeDefine(obj, key, value, enumerable = false) {
|
|
1100
|
-
|
|
1101
|
-
if (!desc) {
|
|
1102
|
-
Object.defineProperty(obj, key, {
|
|
1103
|
-
value,
|
|
1104
|
-
writable: true,
|
|
1105
|
-
configurable: true,
|
|
1106
|
-
enumerable
|
|
1107
|
-
});
|
|
1108
|
-
} else if (desc.configurable) {
|
|
1109
|
-
Object.defineProperty(obj, key, {
|
|
1110
|
-
value,
|
|
1111
|
-
writable: true,
|
|
1112
|
-
configurable: true,
|
|
1113
|
-
enumerable
|
|
1114
|
-
});
|
|
1115
|
-
} else if (this.config && this.config.debug) {
|
|
1116
|
-
console.warn(`Could not redefine boundApi.${key}: not configurable`);
|
|
1117
|
-
}
|
|
678
|
+
return safeDefine(obj, key, value, enumerable, this.config);
|
|
1118
679
|
},
|
|
1119
680
|
|
|
1120
681
|
|
|
@@ -1133,220 +694,121 @@ const slothletObject = {
|
|
|
1133
694
|
},
|
|
1134
695
|
|
|
1135
696
|
|
|
1136
|
-
async addApi(apiPath, folderPath) {
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
}
|
|
697
|
+
async addApi(apiPath, folderPath, metadata = {}) {
|
|
698
|
+
return addApiFromFolder({ apiPath, folderPath, instance: this, metadata });
|
|
699
|
+
},
|
|
1140
700
|
|
|
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
|
-
}
|
|
701
|
+
|
|
1153
702
|
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
703
|
+
|
|
704
|
+
run(contextData, callback, ...args) {
|
|
705
|
+
if (this.config.scope?.enabled === false) {
|
|
706
|
+
throw new Error("Per-request context (scope) is disabled for this instance.");
|
|
1157
707
|
}
|
|
1158
708
|
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
if (!path.isAbsolute(folderPath)) {
|
|
1162
|
-
resolvedFolderPath = resolvePathFromCaller(folderPath);
|
|
709
|
+
if (typeof callback !== "function") {
|
|
710
|
+
throw new TypeError("Callback must be a function.");
|
|
1163
711
|
}
|
|
1164
712
|
|
|
1165
|
-
|
|
1166
|
-
let
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
}
|
|
1172
|
-
if (!stats.isDirectory()) {
|
|
1173
|
-
throw new Error(`[slothlet] addApi: Path is not a directory: ${resolvedFolderPath}`);
|
|
1174
|
-
}
|
|
713
|
+
const runtimeType = this.config.runtime || "async";
|
|
714
|
+
let requestALS;
|
|
715
|
+
if (runtimeType === "async") {
|
|
716
|
+
return import("@cldmv/slothlet/runtime/async").then((asyncRuntime) => {
|
|
717
|
+
requestALS = asyncRuntime.requestALS;
|
|
718
|
+
const parentContext = requestALS.getStore() || {};
|
|
1175
719
|
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
720
|
+
let mergedContext;
|
|
721
|
+
if (this.config.scope?.merge === "deep") {
|
|
722
|
+
const instanceContext = this.context || {};
|
|
723
|
+
let temp = this._deepMerge({}, instanceContext);
|
|
724
|
+
temp = this._deepMerge(temp, parentContext);
|
|
725
|
+
mergedContext = this._deepMerge(temp, contextData);
|
|
726
|
+
} else {
|
|
727
|
+
mergedContext = { ...parentContext, ...contextData };
|
|
728
|
+
}
|
|
1179
729
|
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
if (this.config.lazy) {
|
|
1183
|
-
|
|
1184
|
-
newModules = await this.modes.lazy.create.call(this, resolvedFolderPath, this.config.apiDepth || Infinity, 0);
|
|
730
|
+
return requestALS.run(mergedContext, () => callback(...args));
|
|
731
|
+
});
|
|
1185
732
|
} else {
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
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
|
-
);
|
|
733
|
+
return import("@cldmv/slothlet/runtime/live").then((liveRuntime) => {
|
|
734
|
+
requestALS = liveRuntime.requestALS;
|
|
735
|
+
const parentContext = requestALS.getStore() || {};
|
|
736
|
+
|
|
737
|
+
let mergedContext;
|
|
738
|
+
if (this.config.scope?.merge === "deep") {
|
|
739
|
+
const instanceContext = this.context || {};
|
|
740
|
+
let temp = this._deepMerge({}, instanceContext);
|
|
741
|
+
temp = this._deepMerge(temp, parentContext);
|
|
742
|
+
mergedContext = this._deepMerge(temp, contextData);
|
|
743
|
+
} else {
|
|
744
|
+
mergedContext = { ...parentContext, ...contextData };
|
|
1232
745
|
}
|
|
1233
|
-
|
|
1234
|
-
} else {
|
|
1235
|
-
currentBoundTarget[key] = {};
|
|
1236
|
-
}
|
|
1237
746
|
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
currentBoundTarget = currentBoundTarget[key];
|
|
747
|
+
return requestALS.run(mergedContext, () => callback(...args));
|
|
748
|
+
});
|
|
1241
749
|
}
|
|
750
|
+
},
|
|
1242
751
|
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
if (Object.prototype.hasOwnProperty.call(currentTarget, finalKey)) {
|
|
1251
|
-
const existing = currentTarget[finalKey];
|
|
752
|
+
|
|
753
|
+
scope({ context, fn, args }) {
|
|
754
|
+
if (this.config.scope?.enabled === false) {
|
|
755
|
+
throw new Error("Per-request context (scope) is disabled for this instance.");
|
|
756
|
+
}
|
|
1252
757
|
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
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
|
-
}
|
|
758
|
+
if (!context || typeof context !== "object") {
|
|
759
|
+
throw new TypeError("context must be an object.");
|
|
760
|
+
}
|
|
1261
761
|
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
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];
|
|
762
|
+
if (typeof fn !== "function") {
|
|
763
|
+
throw new TypeError("fn must be a function.");
|
|
764
|
+
}
|
|
1281
765
|
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
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
|
-
}
|
|
766
|
+
const runtimeType = this.config.runtime || "async";
|
|
767
|
+
let requestALS;
|
|
768
|
+
if (runtimeType === "async") {
|
|
769
|
+
return import("./lib/runtime/runtime-asynclocalstorage.mjs").then((asyncRuntime) => {
|
|
770
|
+
requestALS = asyncRuntime.requestALS;
|
|
771
|
+
const parentContext = requestALS.getStore() || {};
|
|
1294
772
|
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
);
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
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
|
-
);
|
|
773
|
+
let mergedContext;
|
|
774
|
+
if (this.config.scope?.merge === "deep") {
|
|
775
|
+
const instanceContext = this.context || {};
|
|
776
|
+
let temp = this._deepMerge({}, instanceContext);
|
|
777
|
+
temp = this._deepMerge(temp, parentContext);
|
|
778
|
+
mergedContext = this._deepMerge(temp, context);
|
|
779
|
+
} else {
|
|
780
|
+
mergedContext = { ...parentContext, ...context };
|
|
1309
781
|
}
|
|
1310
|
-
}
|
|
1311
782
|
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
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
|
-
);
|
|
783
|
+
const argsArray = args || [];
|
|
784
|
+
return requestALS.run(mergedContext, () => fn(...argsArray));
|
|
785
|
+
});
|
|
1334
786
|
} else {
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
787
|
+
return import("./lib/runtime/runtime-livebindings.mjs").then((liveRuntime) => {
|
|
788
|
+
requestALS = liveRuntime.requestALS;
|
|
789
|
+
const parentContext = requestALS.getStore() || {};
|
|
790
|
+
|
|
791
|
+
let mergedContext;
|
|
792
|
+
if (this.config.scope?.merge === "deep") {
|
|
793
|
+
const instanceContext = this.context || {};
|
|
794
|
+
let temp = this._deepMerge({}, instanceContext);
|
|
795
|
+
temp = this._deepMerge(temp, parentContext);
|
|
796
|
+
mergedContext = this._deepMerge(temp, context);
|
|
797
|
+
} else {
|
|
798
|
+
mergedContext = { ...parentContext, ...context };
|
|
799
|
+
}
|
|
1343
800
|
|
|
1344
|
-
|
|
1345
|
-
|
|
801
|
+
const argsArray = args || [];
|
|
802
|
+
return requestALS.run(mergedContext, () => fn(...argsArray));
|
|
803
|
+
});
|
|
1346
804
|
}
|
|
1347
805
|
},
|
|
1348
806
|
|
|
1349
807
|
|
|
808
|
+
_deepMerge(target, source) {
|
|
809
|
+
return deepMerge(target, source);
|
|
810
|
+
},
|
|
811
|
+
|
|
1350
812
|
async shutdown() {
|
|
1351
813
|
|
|
1352
814
|
|
|
@@ -1423,52 +885,6 @@ const slothletObject = {
|
|
|
1423
885
|
};
|
|
1424
886
|
|
|
1425
887
|
|
|
1426
|
-
export function mutateLiveBindingFunction(target, source) {
|
|
1427
|
-
if (typeof source === "function") {
|
|
1428
|
-
target._impl = (...args) => source(...args);
|
|
1429
|
-
|
|
1430
|
-
for (const key of Object.keys(target)) {
|
|
1431
|
-
if (key !== "_impl" && key !== "__ctx") delete target[key];
|
|
1432
|
-
}
|
|
1433
|
-
|
|
1434
|
-
for (const key of Object.getOwnPropertyNames(source)) {
|
|
1435
|
-
if (key !== "length" && key !== "name" && key !== "prototype" && key !== "_impl" && key !== "__ctx") {
|
|
1436
|
-
try {
|
|
1437
|
-
target[key] = source[key];
|
|
1438
|
-
} catch {
|
|
1439
|
-
|
|
1440
|
-
}
|
|
1441
|
-
}
|
|
1442
|
-
}
|
|
1443
|
-
} else if (typeof source === "object" && source !== null) {
|
|
1444
|
-
|
|
1445
|
-
for (const key of Object.keys(target)) {
|
|
1446
|
-
if (key !== "_impl" && key !== "__ctx") delete target[key];
|
|
1447
|
-
}
|
|
1448
|
-
|
|
1449
|
-
for (const [key, value] of Object.entries(source)) {
|
|
1450
|
-
if (key !== "__ctx") {
|
|
1451
|
-
target[key] = value;
|
|
1452
|
-
}
|
|
1453
|
-
}
|
|
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
|
-
|
|
1467
|
-
if (typeof source._impl === "function") {
|
|
1468
|
-
target._impl = source._impl;
|
|
1469
|
-
}
|
|
1470
|
-
}
|
|
1471
|
-
}
|
|
1472
888
|
|
|
1473
889
|
export { slothlet };
|
|
1474
890
|
export default slothlet;
|