@cldmv/slothlet 2.8.0 → 2.9.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 (37) hide show
  1. package/AGENT-USAGE.md +1 -1
  2. package/README.md +253 -1578
  3. package/dist/lib/helpers/als-eventemitter.mjs +4 -5
  4. package/dist/lib/helpers/api_builder/add_api.mjs +237 -0
  5. package/dist/lib/helpers/api_builder/analysis.mjs +522 -0
  6. package/dist/lib/helpers/api_builder/construction.mjs +457 -0
  7. package/dist/lib/helpers/api_builder/decisions.mjs +737 -0
  8. package/dist/lib/helpers/api_builder.mjs +16 -1567
  9. package/dist/lib/helpers/utilities.mjs +121 -0
  10. package/dist/lib/runtime/runtime-asynclocalstorage.mjs +44 -17
  11. package/dist/lib/runtime/runtime-livebindings.mjs +18 -3
  12. package/dist/lib/runtime/runtime.mjs +3 -3
  13. package/dist/slothlet.mjs +146 -746
  14. package/docs/API-RULES-CONDITIONS.md +508 -0
  15. package/{API-RULES.md → docs/API-RULES.md} +127 -72
  16. package/package.json +11 -9
  17. package/types/dist/lib/helpers/als-eventemitter.d.mts.map +1 -1
  18. package/types/dist/lib/helpers/api_builder/add_api.d.mts +60 -0
  19. package/types/dist/lib/helpers/api_builder/add_api.d.mts.map +1 -0
  20. package/types/dist/lib/helpers/api_builder/analysis.d.mts +189 -0
  21. package/types/dist/lib/helpers/api_builder/analysis.d.mts.map +1 -0
  22. package/types/dist/lib/helpers/api_builder/construction.d.mts +107 -0
  23. package/types/dist/lib/helpers/api_builder/construction.d.mts.map +1 -0
  24. package/types/dist/lib/helpers/api_builder/decisions.d.mts +213 -0
  25. package/types/dist/lib/helpers/api_builder/decisions.d.mts.map +1 -0
  26. package/types/dist/lib/helpers/api_builder.d.mts +5 -448
  27. package/types/dist/lib/helpers/api_builder.d.mts.map +1 -1
  28. package/types/dist/lib/helpers/utilities.d.mts +120 -0
  29. package/types/dist/lib/helpers/utilities.d.mts.map +1 -0
  30. package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts +7 -0
  31. package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts.map +1 -1
  32. package/types/dist/lib/runtime/runtime-livebindings.d.mts +8 -0
  33. package/types/dist/lib/runtime/runtime-livebindings.d.mts.map +1 -1
  34. package/types/dist/slothlet.d.mts +0 -11
  35. package/types/dist/slothlet.d.mts.map +1 -1
  36. package/types/index.d.mts +0 -1
  37. package/API-RULES-CONDITIONS.md +0 -367
@@ -0,0 +1,121 @@
1
+ /*
2
+ Copyright 2025 CLDMV/Shinrai
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+
18
+
19
+
20
+
21
+
22
+ export function safeDefine(obj, key, value, enumerable = false, config = null) {
23
+ const desc = Object.getOwnPropertyDescriptor(obj, key);
24
+ if (!desc) {
25
+ Object.defineProperty(obj, key, {
26
+ value,
27
+ writable: true,
28
+ configurable: true,
29
+ enumerable
30
+ });
31
+ } else if (desc.configurable) {
32
+ Object.defineProperty(obj, key, {
33
+ value,
34
+ writable: true,
35
+ configurable: true,
36
+ enumerable
37
+ });
38
+ } else if (config && config.debug) {
39
+ console.warn(`Could not redefine boundApi.${key}: not configurable`);
40
+ }
41
+ }
42
+
43
+
44
+ export function deepMerge(target, source) {
45
+ if (!source || typeof source !== "object" || Array.isArray(source)) {
46
+ return source;
47
+ }
48
+
49
+ for (const key in source) {
50
+ if (!Object.prototype.hasOwnProperty.call(source, key)) {
51
+ continue;
52
+ }
53
+
54
+
55
+ if (key === "__proto__" || key === "prototype" || key === "constructor") {
56
+ continue;
57
+ }
58
+
59
+ const sourceValue = source[key];
60
+ const targetValue = target[key];
61
+
62
+ if (sourceValue && typeof sourceValue === "object" && !Array.isArray(sourceValue)) {
63
+ target[key] = deepMerge(
64
+ targetValue && typeof targetValue === "object" && !Array.isArray(targetValue) ? targetValue : {},
65
+ sourceValue
66
+ );
67
+ } else {
68
+ target[key] = sourceValue;
69
+ }
70
+ }
71
+
72
+ return target;
73
+ }
74
+
75
+
76
+ export function mutateLiveBindingFunction(target, source) {
77
+ if (typeof source === "function") {
78
+ target._impl = (...args) => source(...args);
79
+
80
+ for (const key of Object.keys(target)) {
81
+ if (key !== "_impl" && key !== "__ctx") delete target[key];
82
+ }
83
+
84
+ for (const key of Object.getOwnPropertyNames(source)) {
85
+ if (key !== "length" && key !== "name" && key !== "prototype" && key !== "_impl" && key !== "__ctx") {
86
+ try {
87
+ target[key] = source[key];
88
+ } catch {
89
+
90
+ }
91
+ }
92
+ }
93
+ } else if (typeof source === "object" && source !== null) {
94
+
95
+ for (const key of Object.keys(target)) {
96
+ if (key !== "_impl" && key !== "__ctx") delete target[key];
97
+ }
98
+
99
+ for (const [key, value] of Object.entries(source)) {
100
+ if (key !== "__ctx") {
101
+ target[key] = value;
102
+ }
103
+ }
104
+
105
+ const managementMethods = ["shutdown", "addApi", "describe"];
106
+ for (const method of managementMethods) {
107
+ const desc = Object.getOwnPropertyDescriptor(source, method);
108
+ if (desc) {
109
+ try {
110
+ Object.defineProperty(target, method, desc);
111
+ } catch {
112
+
113
+ }
114
+ }
115
+ }
116
+
117
+ if (typeof source._impl === "function") {
118
+ target._impl = source._impl;
119
+ }
120
+ }
121
+ }
@@ -28,6 +28,9 @@ const als = new AsyncLocalStorage();
28
28
  export const sharedALS = new AsyncLocalStorage();
29
29
 
30
30
 
31
+ export const requestALS = new AsyncLocalStorage();
32
+
33
+
31
34
  enableAlsForEventEmitters(als);
32
35
 
33
36
 
@@ -42,17 +45,21 @@ export const runWithCtx = (ctx, fn, thisArg, args) => {
42
45
  }
43
46
 
44
47
 
48
+ const requestContext = requestALS.getStore();
49
+ const mergedCtx = requestContext ? { ...ctx, context: { ...ctx.context, ...requestContext } } : ctx;
50
+
51
+
45
52
  const path = fn.__slothletPath;
46
53
 
47
54
 
48
55
  const runtime_runInALS = () => {
49
56
  try {
50
57
 
51
- const beforeResult = ctx.hookManager.executeBeforeHooks(path, args);
58
+ const beforeResult = mergedCtx.hookManager.executeBeforeHooks(path, args);
52
59
 
53
60
 
54
61
  if (beforeResult.cancelled) {
55
- ctx.hookManager.executeAlwaysHooks(path, beforeResult.value, []);
62
+ mergedCtx.hookManager.executeAlwaysHooks(path, beforeResult.value, []);
56
63
  return beforeResult.value;
57
64
  }
58
65
 
@@ -67,20 +74,20 @@ export const runWithCtx = (ctx, fn, thisArg, args) => {
67
74
  return result.then(
68
75
  (resolvedResult) => {
69
76
 
70
- const finalResult = ctx.hookManager.executeAfterHooks(path, resolvedResult);
71
- ctx.hookManager.executeAlwaysHooks(path, finalResult, []);
77
+ const finalResult = mergedCtx.hookManager.executeAfterHooks(path, resolvedResult);
78
+ mergedCtx.hookManager.executeAlwaysHooks(path, finalResult, []);
72
79
  return finalResult;
73
80
  },
74
81
  (error) => {
75
82
 
76
- if (!ctx.hookManager.reportedErrors.has(error)) {
77
- ctx.hookManager.reportedErrors.add(error);
78
- ctx.hookManager.executeErrorHooks(path, error, { type: "function" });
83
+ if (!mergedCtx.hookManager.reportedErrors.has(error)) {
84
+ mergedCtx.hookManager.reportedErrors.add(error);
85
+ mergedCtx.hookManager.executeErrorHooks(path, error, { type: "function" });
79
86
  }
80
87
 
81
- ctx.hookManager.executeAlwaysHooks(path, undefined, [error]);
88
+ mergedCtx.hookManager.executeAlwaysHooks(path, undefined, [error]);
82
89
 
83
- if (!ctx.hookManager.suppressErrors) {
90
+ if (!mergedCtx.hookManager.suppressErrors) {
84
91
  throw error;
85
92
  }
86
93
  return undefined;
@@ -89,26 +96,26 @@ export const runWithCtx = (ctx, fn, thisArg, args) => {
89
96
  }
90
97
 
91
98
 
92
- const finalResult = ctx.hookManager.executeAfterHooks(path, result);
93
- ctx.hookManager.executeAlwaysHooks(path, finalResult, []);
99
+ const finalResult = mergedCtx.hookManager.executeAfterHooks(path, result);
100
+ mergedCtx.hookManager.executeAlwaysHooks(path, finalResult, []);
94
101
  return finalResult;
95
102
  } catch (error) {
96
103
 
97
- if (!ctx.hookManager.reportedErrors.has(error)) {
98
- ctx.hookManager.reportedErrors.add(error);
99
- ctx.hookManager.executeErrorHooks(path, error, { type: "function" });
104
+ if (!mergedCtx.hookManager.reportedErrors.has(error)) {
105
+ mergedCtx.hookManager.reportedErrors.add(error);
106
+ mergedCtx.hookManager.executeErrorHooks(path, error, { type: "function" });
100
107
  }
101
108
 
102
- ctx.hookManager.executeAlwaysHooks(path, undefined, [error]);
109
+ mergedCtx.hookManager.executeAlwaysHooks(path, undefined, [error]);
103
110
 
104
- if (!ctx.hookManager.suppressErrors) {
111
+ if (!mergedCtx.hookManager.suppressErrors) {
105
112
  throw error;
106
113
  }
107
114
  return undefined;
108
115
  }
109
116
  };
110
117
 
111
- return als.run(ctx, runtime_runInALS);
118
+ return als.run(mergedCtx, runtime_runInALS);
112
119
  };
113
120
 
114
121
 
@@ -477,6 +484,26 @@ function runtime_createLiveBinding(contextKey) {
477
484
 
478
485
 
479
486
 
487
+ if (contextKey === "context") {
488
+ const ctx = getCtx();
489
+ const baseContext = ctx?.context || {};
490
+ const requestContext = requestALS.getStore() || {};
491
+
492
+
493
+
494
+ const isDeepMerge = ctx?.config?.scope?.merge === "deep";
495
+
496
+ if (isDeepMerge && Object.keys(requestContext).length > 0) {
497
+
498
+ return requestContext[prop];
499
+ }
500
+
501
+
502
+ const merged = { ...baseContext, ...requestContext };
503
+ return merged[prop];
504
+ }
505
+
506
+
480
507
  runtime_syncWithContext();
481
508
  return target[prop];
482
509
  },
@@ -18,6 +18,7 @@
18
18
 
19
19
 
20
20
 
21
+ import { AsyncLocalStorage } from "node:async_hooks";
21
22
  import {
22
23
  detectCurrentInstanceId,
23
24
  getInstanceData,
@@ -26,6 +27,9 @@ import {
26
27
  } from "@cldmv/slothlet/helpers/instance-manager";
27
28
 
28
29
 
30
+ export const requestALS = new AsyncLocalStorage();
31
+
32
+
29
33
  function getCurrentInstanceContext() {
30
34
 
31
35
 
@@ -122,10 +126,21 @@ export const context = new Proxy(
122
126
  {
123
127
  get(target, prop) {
124
128
  const ctx = getCurrentInstanceContext();
125
- if (ctx && ctx.context) {
126
- return ctx.context[prop];
129
+ const baseContext = ctx?.context || {};
130
+ const requestContext = requestALS.getStore() || {};
131
+
132
+
133
+
134
+ const isDeepMerge = ctx?.config?.scope?.merge === "deep";
135
+
136
+ if (isDeepMerge && Object.keys(requestContext).length > 0) {
137
+
138
+ return requestContext[prop];
127
139
  }
128
- return undefined;
140
+
141
+
142
+ const merged = { ...baseContext, ...requestContext };
143
+ return merged[prop];
129
144
  },
130
145
 
131
146
  set(target, prop, value) {
@@ -18,11 +18,11 @@
18
18
 
19
19
 
20
20
 
21
- import { detectCurrentInstanceId, getInstanceData } from "../helpers/instance-manager.mjs";
21
+ import { detectCurrentInstanceId, getInstanceData } from "@cldmv/slothlet/helpers/instance-manager";
22
22
 
23
23
 
24
- const asyncRuntime = await import("./runtime-asynclocalstorage.mjs");
25
- const liveBindingsRuntime = await import("./runtime-livebindings.mjs");
24
+ const asyncRuntime = await import("@cldmv/slothlet/runtime/async");
25
+ const liveBindingsRuntime = await import("@cldmv/slothlet/runtime/live");
26
26
 
27
27
 
28
28
  function detectRuntimeType() {