@cldmv/slothlet 2.6.1 → 2.7.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 +536 -0
- package/API-RULES-CONDITIONS.md +367 -0
- package/API-RULES.md +777 -0
- package/README.md +631 -39
- package/dist/lib/helpers/hooks.mjs +381 -0
- package/dist/lib/modes/slothlet_lazy.mjs +25 -19
- package/dist/lib/runtime/runtime-asynclocalstorage.mjs +97 -16
- package/dist/lib/runtime/runtime-livebindings.mjs +127 -5
- package/dist/slothlet.mjs +50 -2
- package/package.json +12 -8
- package/types/dist/lib/helpers/hooks.d.mts +330 -0
- package/types/dist/lib/helpers/hooks.d.mts.map +1 -0
- package/types/dist/lib/runtime/runtime-asynclocalstorage.d.mts.map +1 -1
- package/types/dist/lib/runtime/runtime-livebindings.d.mts +4 -3
- package/types/dist/lib/runtime/runtime-livebindings.d.mts.map +1 -1
- package/types/dist/slothlet.d.mts +7 -0
- package/types/dist/slothlet.d.mts.map +1 -1
|
@@ -256,7 +256,73 @@ export function runWithCtx(ctx, fn, thisArg, args) {
|
|
|
256
256
|
|
|
257
257
|
try {
|
|
258
258
|
|
|
259
|
-
|
|
259
|
+
if (!ctx.hookManager?.enabled || !fn.__slothletPath) {
|
|
260
|
+
return Reflect.apply(fn, thisArg, args);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
const path = fn.__slothletPath;
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
|
|
268
|
+
const beforeResult = ctx.hookManager.executeBeforeHooks(path, args);
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
if (beforeResult.cancelled) {
|
|
272
|
+
ctx.hookManager.executeAlwaysHooks(path, beforeResult.value, []);
|
|
273
|
+
return beforeResult.value;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
const actualArgs = beforeResult.args;
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
const result = Reflect.apply(fn, thisArg, actualArgs);
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
if (result && typeof result === "object" && typeof result.then === "function") {
|
|
284
|
+
return result.then(
|
|
285
|
+
(resolvedResult) => {
|
|
286
|
+
|
|
287
|
+
const finalResult = ctx.hookManager.executeAfterHooks(path, resolvedResult);
|
|
288
|
+
ctx.hookManager.executeAlwaysHooks(path, finalResult, []);
|
|
289
|
+
return finalResult;
|
|
290
|
+
},
|
|
291
|
+
(error) => {
|
|
292
|
+
|
|
293
|
+
if (!ctx.hookManager.reportedErrors.has(error)) {
|
|
294
|
+
ctx.hookManager.reportedErrors.add(error);
|
|
295
|
+
ctx.hookManager.executeErrorHooks(path, error, { type: "function" });
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
ctx.hookManager.executeAlwaysHooks(path, undefined, [error]);
|
|
299
|
+
|
|
300
|
+
if (!ctx.hookManager.suppressErrors) {
|
|
301
|
+
throw error;
|
|
302
|
+
}
|
|
303
|
+
return undefined;
|
|
304
|
+
}
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
const finalResult = ctx.hookManager.executeAfterHooks(path, result);
|
|
310
|
+
ctx.hookManager.executeAlwaysHooks(path, finalResult, []);
|
|
311
|
+
return finalResult;
|
|
312
|
+
} catch (error) {
|
|
313
|
+
|
|
314
|
+
if (!ctx.hookManager.reportedErrors.has(error)) {
|
|
315
|
+
ctx.hookManager.reportedErrors.add(error);
|
|
316
|
+
ctx.hookManager.executeErrorHooks(path, error, { type: "function" });
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
ctx.hookManager.executeAlwaysHooks(path, undefined, [error]);
|
|
320
|
+
|
|
321
|
+
if (!ctx.hookManager.suppressErrors) {
|
|
322
|
+
throw error;
|
|
323
|
+
}
|
|
324
|
+
return undefined;
|
|
325
|
+
}
|
|
260
326
|
} finally {
|
|
261
327
|
|
|
262
328
|
setActiveInstance(previousActiveInstance);
|
|
@@ -264,11 +330,67 @@ export function runWithCtx(ctx, fn, thisArg, args) {
|
|
|
264
330
|
}
|
|
265
331
|
|
|
266
332
|
|
|
267
|
-
export function makeWrapper(
|
|
268
|
-
|
|
269
|
-
|
|
333
|
+
export function makeWrapper(ctx) {
|
|
334
|
+
const cache = new WeakMap();
|
|
335
|
+
|
|
336
|
+
return function wrapperFunction(obj, currentPath = "") {
|
|
337
|
+
if (obj == null || (typeof obj !== "object" && typeof obj !== "function")) {
|
|
338
|
+
return obj;
|
|
339
|
+
}
|
|
340
|
+
|
|
270
341
|
|
|
271
|
-
|
|
342
|
+
if (cache.has(obj)) {
|
|
343
|
+
return cache.get(obj);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const proxied = new Proxy(obj, {
|
|
347
|
+
apply(target, thisArg, args) {
|
|
348
|
+
|
|
349
|
+
return runWithCtx(ctx, target, thisArg, args);
|
|
350
|
+
},
|
|
351
|
+
get(target, prop, receiver) {
|
|
352
|
+
const value = Reflect.get(target, prop, receiver);
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
const newPath = currentPath ? `${currentPath}.${String(prop)}` : String(prop);
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
const isInternalProperty = currentPath === "" && ["hooks", "__ctx", "shutdown", "_impl"].includes(String(prop));
|
|
359
|
+
const isInternalPath =
|
|
360
|
+
newPath === "hooks" ||
|
|
361
|
+
newPath.startsWith("hooks.") ||
|
|
362
|
+
newPath === "__ctx" ||
|
|
363
|
+
newPath.startsWith("__ctx.") ||
|
|
364
|
+
newPath === "shutdown" ||
|
|
365
|
+
newPath.startsWith("shutdown.") ||
|
|
366
|
+
newPath === "_impl" ||
|
|
367
|
+
newPath.startsWith("_impl.");
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
if (typeof value === "function" && !value.__slothletPath && !isInternalProperty && !isInternalPath) {
|
|
371
|
+
try {
|
|
372
|
+
Object.defineProperty(value, "__slothletPath", {
|
|
373
|
+
value: newPath,
|
|
374
|
+
writable: false,
|
|
375
|
+
enumerable: false,
|
|
376
|
+
configurable: true
|
|
377
|
+
});
|
|
378
|
+
} catch {
|
|
379
|
+
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
if ((typeof value === "function" || (value && typeof value === "object")) && !isInternalPath) {
|
|
385
|
+
return wrapperFunction(value, newPath);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return value;
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
cache.set(obj, proxied);
|
|
393
|
+
return proxied;
|
|
272
394
|
};
|
|
273
395
|
}
|
|
274
396
|
|
package/dist/slothlet.mjs
CHANGED
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
buildCategoryDecisions
|
|
32
32
|
} from "@cldmv/slothlet/helpers/api_builder";
|
|
33
33
|
import { updateInstanceData, cleanupInstance } from "./lib/helpers/instance-manager.mjs";
|
|
34
|
+
import { HookManager } from "./lib/helpers/hooks.mjs";
|
|
34
35
|
|
|
35
36
|
|
|
36
37
|
|
|
@@ -222,11 +223,34 @@ const slothletObject = {
|
|
|
222
223
|
if (executionEngine === "singleton") {
|
|
223
224
|
|
|
224
225
|
|
|
225
|
-
const { context = null, reference = null, sanitize = null, engine, mode, ...loadConfig } = options;
|
|
226
|
+
const { context = null, reference = null, sanitize = null, hooks = false, engine, mode, ...loadConfig } = options;
|
|
226
227
|
this.context = context;
|
|
227
228
|
this.reference = reference;
|
|
228
229
|
|
|
229
230
|
|
|
231
|
+
let hooksEnabled = false;
|
|
232
|
+
let hooksPattern = null;
|
|
233
|
+
let hooksSuppressErrors = false;
|
|
234
|
+
|
|
235
|
+
if (hooks === true || hooks === false) {
|
|
236
|
+
|
|
237
|
+
hooksEnabled = hooks;
|
|
238
|
+
hooksPattern = hooks ? "**" : null;
|
|
239
|
+
} else if (typeof hooks === "string") {
|
|
240
|
+
|
|
241
|
+
hooksEnabled = true;
|
|
242
|
+
hooksPattern = hooks;
|
|
243
|
+
} else if (hooks && typeof hooks === "object") {
|
|
244
|
+
|
|
245
|
+
hooksEnabled = hooks.enabled !== false;
|
|
246
|
+
hooksPattern = hooks.pattern || "**";
|
|
247
|
+
hooksSuppressErrors = hooks.suppressErrors || false;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
this.hookManager = new HookManager(hooksEnabled, hooksPattern, { suppressErrors: hooksSuppressErrors });
|
|
252
|
+
|
|
253
|
+
|
|
230
254
|
if (sanitize !== null) {
|
|
231
255
|
this.config.sanitize = sanitize;
|
|
232
256
|
}
|
|
@@ -253,6 +277,8 @@ const slothletObject = {
|
|
|
253
277
|
await this.load(loadConfig, { context, reference });
|
|
254
278
|
|
|
255
279
|
|
|
280
|
+
|
|
281
|
+
|
|
256
282
|
|
|
257
283
|
return this.boundapi;
|
|
258
284
|
} else {
|
|
@@ -310,6 +336,27 @@ const slothletObject = {
|
|
|
310
336
|
} else {
|
|
311
337
|
this.api = await this.modes.eager.create.call(this, apiDir, this.config.apiDepth || Infinity, 0);
|
|
312
338
|
}
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
if (this.hookManager) {
|
|
342
|
+
const hooksApi = {
|
|
343
|
+
on: (name, type, handler, options) => this.hookManager.on(name, type, handler, options),
|
|
344
|
+
off: (idOrPattern) => this.hookManager.off(idOrPattern),
|
|
345
|
+
enable: (pattern) => this.hookManager.enable(pattern),
|
|
346
|
+
disable: () => this.hookManager.disable(),
|
|
347
|
+
clear: (type) => this.hookManager.clear(type),
|
|
348
|
+
list: (type) => this.hookManager.list(type)
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
Object.defineProperty(this.api, "hooks", {
|
|
353
|
+
value: hooksApi,
|
|
354
|
+
writable: false,
|
|
355
|
+
enumerable: true,
|
|
356
|
+
configurable: true
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
|
|
313
360
|
if (this.config.debug) console.log(this.api);
|
|
314
361
|
|
|
315
362
|
|
|
@@ -899,7 +946,8 @@ const slothletObject = {
|
|
|
899
946
|
this.safeDefine(this.boundapi, "__ctx", {
|
|
900
947
|
self: this.boundapi,
|
|
901
948
|
context: this.context,
|
|
902
|
-
reference: this.reference
|
|
949
|
+
reference: this.reference,
|
|
950
|
+
hookManager: this.hookManager
|
|
903
951
|
});
|
|
904
952
|
},
|
|
905
953
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cldmv/slothlet",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"moduleVersions": {
|
|
5
5
|
"lazy": "1.3.0",
|
|
6
6
|
"eager": "1.3.0"
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"import": "./devcheck.mjs"
|
|
20
20
|
},
|
|
21
21
|
"./slothlet": {
|
|
22
|
-
"
|
|
22
|
+
"slothlet-dev": {
|
|
23
23
|
"types": "./types/src/slothlet.d.mts",
|
|
24
24
|
"import": "./src/slothlet.mjs"
|
|
25
25
|
},
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"import": "./dist/slothlet.mjs"
|
|
28
28
|
},
|
|
29
29
|
"./runtime": {
|
|
30
|
-
"
|
|
30
|
+
"slothlet-dev": {
|
|
31
31
|
"types": "./types/src/lib/runtime/runtime.d.mts",
|
|
32
32
|
"import": "./src/lib/runtime/runtime.mjs"
|
|
33
33
|
},
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"import": "./dist/lib/runtime/runtime.mjs"
|
|
36
36
|
},
|
|
37
37
|
"./runtime/async": {
|
|
38
|
-
"
|
|
38
|
+
"slothlet-dev": {
|
|
39
39
|
"types": "./types/src/lib/runtime/runtime-asynclocalstorage.d.mts",
|
|
40
40
|
"import": "./src/lib/runtime/runtime-asynclocalstorage.mjs"
|
|
41
41
|
},
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"import": "./dist/lib/runtime/runtime-asynclocalstorage.mjs"
|
|
44
44
|
},
|
|
45
45
|
"./runtime/live": {
|
|
46
|
-
"
|
|
46
|
+
"slothlet-dev": {
|
|
47
47
|
"types": "./types/src/lib/runtime/runtime-livebindings.d.mts",
|
|
48
48
|
"import": "./src/lib/runtime/runtime-livebindings.mjs"
|
|
49
49
|
},
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"import": "./dist/lib/runtime/runtime-livebindings.mjs"
|
|
52
52
|
},
|
|
53
53
|
"./helpers/*": {
|
|
54
|
-
"
|
|
54
|
+
"slothlet-dev": {
|
|
55
55
|
"types": "./types/src/lib/helpers/*.d.mts",
|
|
56
56
|
"import": "./src/lib/helpers/*.mjs"
|
|
57
57
|
},
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"import": "./dist/lib/helpers/*.mjs"
|
|
60
60
|
},
|
|
61
61
|
"./modes/*": {
|
|
62
|
-
"
|
|
62
|
+
"slothlet-dev": {
|
|
63
63
|
"types": "./types/src/lib/modes/*.d.mts",
|
|
64
64
|
"import": "./src/lib/modes/*.mjs"
|
|
65
65
|
},
|
|
@@ -85,8 +85,9 @@
|
|
|
85
85
|
"test:performance-aggregated": "node tests/performance-benchmark-aggregated.mjs",
|
|
86
86
|
"lint": "eslint --config .configs/eslint.config.mjs .",
|
|
87
87
|
"build": "node tools/build-with-tests.mjs",
|
|
88
|
-
"build:ci": "npm run build:cleanup && npm run build:dist && npm run build:types && npm run build:exports && npm run test:types && npm run build:prepend-license",
|
|
88
|
+
"build:ci": "npm run build:cleanup && npm run build:dist && npm run build:types && npm run build:exports && npm run test:types && npm run build:prepend-license && npm run ci:cleanup-src",
|
|
89
89
|
"build:unsafe": "npm run build:cleanup && npm run build:dist && npm run build:types && npm run build:exports && npm run test:types && npm run build:prepend-license",
|
|
90
|
+
"ci:cleanup-src": "node tools/ci-cleanup-src.mjs",
|
|
90
91
|
"build:cleanup": "shx rm -rf types && shx rm -rf dist",
|
|
91
92
|
"build:dist": "shx mkdir -p dist && shx cp -r src/* dist/ && shx rm -rf dist/**/*.backup",
|
|
92
93
|
"build:types": "npx tsc -p .configs/tsconfig.dts.jsonc",
|
|
@@ -187,6 +188,9 @@
|
|
|
187
188
|
"index.cjs",
|
|
188
189
|
"README.md",
|
|
189
190
|
"LICENSE",
|
|
191
|
+
"API-RULES.md",
|
|
192
|
+
"API-RULES-CONDITIONS.md",
|
|
193
|
+
"AGENT-USAGE.md",
|
|
190
194
|
"types/dist/",
|
|
191
195
|
"types/index.d.mts",
|
|
192
196
|
"types/index.d.mts.map",
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @class HookManager
|
|
3
|
+
* @internal
|
|
4
|
+
* @private
|
|
5
|
+
*
|
|
6
|
+
* @description
|
|
7
|
+
* Manages registration, pattern matching, and execution of hooks for slothlet API calls.
|
|
8
|
+
* Hooks are executed in priority order (higher priority first), then registration order.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* // Create hook manager
|
|
12
|
+
* const manager = new HookManager();
|
|
13
|
+
* manager.on("logger", "before", (ctx) => { console.log(ctx.path); });
|
|
14
|
+
*/
|
|
15
|
+
export class HookManager {
|
|
16
|
+
/**
|
|
17
|
+
* @constructor
|
|
18
|
+
* @param {boolean} [enabled=true] - Initial enabled state
|
|
19
|
+
* @param {string} [defaultPattern="**"] - Default pattern for filtering
|
|
20
|
+
* @param {object} [options={}] - Additional options
|
|
21
|
+
* @param {boolean} [options.suppressErrors=false] - If true, errors are logged but not thrown (except for before/after hooks)
|
|
22
|
+
*/
|
|
23
|
+
constructor(enabled?: boolean, defaultPattern?: string, options?: {
|
|
24
|
+
suppressErrors?: boolean;
|
|
25
|
+
});
|
|
26
|
+
enabled: boolean;
|
|
27
|
+
defaultPattern: string;
|
|
28
|
+
suppressErrors: boolean;
|
|
29
|
+
hooks: Map<any, any>;
|
|
30
|
+
registrationOrder: number;
|
|
31
|
+
reportedErrors: WeakSet<object>;
|
|
32
|
+
/**
|
|
33
|
+
* @function on
|
|
34
|
+
* @public
|
|
35
|
+
* @param {string} name - Hook name/ID for debugging and removal
|
|
36
|
+
* @param {string} type - Hook type: "before", "after", "always", or "error"
|
|
37
|
+
* @param {Function} handler - Hook handler function with type-specific signature:
|
|
38
|
+
* - before: ({ path, args }) => modified args array or value to short-circuit
|
|
39
|
+
* - after: ({ path, result }) => transformed result
|
|
40
|
+
* - always: ({ path, result, hasError, errors }) => void (read-only)
|
|
41
|
+
* - error: ({ path, error, errorType, source }) => void (observer)
|
|
42
|
+
* @param {object} [options] - Registration options
|
|
43
|
+
* @param {number} [options.priority=100] - Execution priority (higher = earlier)
|
|
44
|
+
* @param {string} [options.pattern] - Glob pattern for path filtering
|
|
45
|
+
* @returns {string} Hook name/ID for later removal
|
|
46
|
+
*
|
|
47
|
+
* @description
|
|
48
|
+
* Register a hook with optional priority and pattern filtering.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* // Register hook with priority
|
|
52
|
+
* manager.on("validator", "before", handler, { priority: 200 });
|
|
53
|
+
*/
|
|
54
|
+
public on(name: string, type: string, handler: Function, options?: {
|
|
55
|
+
priority?: number;
|
|
56
|
+
pattern?: string;
|
|
57
|
+
}): string;
|
|
58
|
+
/**
|
|
59
|
+
* @function off
|
|
60
|
+
* @public
|
|
61
|
+
* @param {string} nameOrPattern - Hook name or glob pattern to remove
|
|
62
|
+
* @returns {boolean} True if one or more hooks were removed
|
|
63
|
+
*
|
|
64
|
+
* @description
|
|
65
|
+
* Remove registered hook(s) by exact name or pattern matching.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* // Remove hook by exact name
|
|
69
|
+
* manager.off("validator");
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* // Remove all hooks matching pattern
|
|
73
|
+
* manager.off("math.*");
|
|
74
|
+
*/
|
|
75
|
+
public off(nameOrPattern: string): boolean;
|
|
76
|
+
/**
|
|
77
|
+
* @function clear
|
|
78
|
+
* @public
|
|
79
|
+
* @param {string} [type] - Optional hook type to clear ("before", "after", "always", "error")
|
|
80
|
+
* @returns {void}
|
|
81
|
+
*
|
|
82
|
+
* @description
|
|
83
|
+
* Remove registered hooks. If type is provided, only hooks of that type are removed.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* // Clear all hooks
|
|
87
|
+
* manager.clear();
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* // Clear only before hooks
|
|
91
|
+
* manager.clear("before");
|
|
92
|
+
*/
|
|
93
|
+
public clear(type?: string): void;
|
|
94
|
+
/**
|
|
95
|
+
* @function list
|
|
96
|
+
* @public
|
|
97
|
+
* @param {string} [type] - Optional hook type to filter by ("before", "after", "always", "error")
|
|
98
|
+
* @returns {Array<object>} Array of hook metadata
|
|
99
|
+
*
|
|
100
|
+
* @description
|
|
101
|
+
* List registered hooks with their metadata. When type is provided, only hooks
|
|
102
|
+
* matching that type are returned.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* // List all hooks
|
|
106
|
+
* const hooks = manager.list();
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* // List only before hooks
|
|
110
|
+
* const beforeHooks = manager.list("before");
|
|
111
|
+
*/
|
|
112
|
+
public list(type?: string): Array<object>;
|
|
113
|
+
/**
|
|
114
|
+
* @function enable
|
|
115
|
+
* @public
|
|
116
|
+
* @param {string} [pattern] - Optional new default pattern
|
|
117
|
+
* @returns {void}
|
|
118
|
+
*
|
|
119
|
+
* @description
|
|
120
|
+
* Enable hook execution, optionally updating default pattern.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* // Enable with new pattern
|
|
124
|
+
* manager.enable("database.*");
|
|
125
|
+
*/
|
|
126
|
+
public enable(pattern?: string): void;
|
|
127
|
+
/**
|
|
128
|
+
* @function disable
|
|
129
|
+
* @public
|
|
130
|
+
* @returns {void}
|
|
131
|
+
*
|
|
132
|
+
* @description
|
|
133
|
+
* Disable hook execution (fast-path bypass).
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* // Disable hooks
|
|
137
|
+
* manager.disable();
|
|
138
|
+
*/
|
|
139
|
+
public disable(): void;
|
|
140
|
+
/**
|
|
141
|
+
* @function executeBeforeHooks
|
|
142
|
+
* @internal
|
|
143
|
+
* @private
|
|
144
|
+
* @param {string} path - Function path (e.g., "database.users.create")
|
|
145
|
+
* @param {Array} args - Function arguments
|
|
146
|
+
* @returns {{cancelled: boolean, value?: any, args: Array}} Execution result
|
|
147
|
+
*
|
|
148
|
+
* @description
|
|
149
|
+
* Execute before hooks in priority order. Returns object indicating if execution
|
|
150
|
+
* should be cancelled (short-circuited) and potentially modified arguments.
|
|
151
|
+
*
|
|
152
|
+
* Hook return semantics:
|
|
153
|
+
* - undefined: continue to next hook/function
|
|
154
|
+
* - Array: modified arguments for next hook/function
|
|
155
|
+
* - Other value: short-circuit and return this value
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* // Execute before hooks
|
|
159
|
+
* const result = manager.executeBeforeHooks("database.users.create", [data]);
|
|
160
|
+
* if (result.cancelled) return result.value;
|
|
161
|
+
*/
|
|
162
|
+
private executeBeforeHooks;
|
|
163
|
+
/**
|
|
164
|
+
* @function executeAfterHooks
|
|
165
|
+
* @internal
|
|
166
|
+
* @private
|
|
167
|
+
* @param {string} path - Function path
|
|
168
|
+
* @param {any} initialResult - Initial result from function
|
|
169
|
+
* @returns {any} Transformed result
|
|
170
|
+
*
|
|
171
|
+
* @description
|
|
172
|
+
* Execute after hooks in priority order, chaining results through each hook.
|
|
173
|
+
* Each hook receives the previous hook's return value (or initial result).
|
|
174
|
+
* After hooks only run if the function actually executed (not short-circuited).
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* // Execute after hooks with chaining
|
|
178
|
+
* const finalResult = manager.executeAfterHooks("database.users.create", result);
|
|
179
|
+
*/
|
|
180
|
+
private executeAfterHooks;
|
|
181
|
+
/**
|
|
182
|
+
* @function executeAlwaysHooks
|
|
183
|
+
* @internal
|
|
184
|
+
* @private
|
|
185
|
+
* @param {string} path - Function path
|
|
186
|
+
* @param {any} result - Final result (from function or short-circuit)
|
|
187
|
+
* @param {Array<Error>} [errors=[]] - Array of errors that occurred during execution
|
|
188
|
+
* @returns {void}
|
|
189
|
+
*
|
|
190
|
+
* @description
|
|
191
|
+
* Execute always hooks (like finally blocks). These hooks always run regardless
|
|
192
|
+
* of whether execution was short-circuited, completed normally, or threw errors.
|
|
193
|
+
* Always hooks receive full execution context including both errors and results,
|
|
194
|
+
* allowing a single hook to handle all logging scenarios.
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* // Execute always hooks with success result
|
|
198
|
+
* manager.executeAlwaysHooks("database.users.create", result, []);
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* // Execute always hooks with error context
|
|
202
|
+
* manager.executeAlwaysHooks("database.users.create", undefined, [error]);
|
|
203
|
+
*/
|
|
204
|
+
private executeAlwaysHooks;
|
|
205
|
+
/**
|
|
206
|
+
* @function executeErrorHooks
|
|
207
|
+
* @internal
|
|
208
|
+
* @private
|
|
209
|
+
* @param {string} path - Function path
|
|
210
|
+
* @param {Error} error - Error that was thrown
|
|
211
|
+
* @param {Object} [source] - Source information about where error originated
|
|
212
|
+
* @param {string} source.type - Source type: 'before', 'function', 'after', 'always'
|
|
213
|
+
* @param {string} [source.hookId] - Hook ID if error came from a hook
|
|
214
|
+
* @param {string} [source.hookTag] - Hook tag if error came from a hook
|
|
215
|
+
* @returns {void}
|
|
216
|
+
*
|
|
217
|
+
* @description
|
|
218
|
+
* Execute error hooks (observers only, errors bubble naturally).
|
|
219
|
+
* Provides detailed context about where the error originated.
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* // Execute error hooks with source info
|
|
223
|
+
* manager.executeErrorHooks("database.users.create", error, {
|
|
224
|
+
* type: 'before',
|
|
225
|
+
* hookId: 'hook-123',
|
|
226
|
+
* hookTag: 'validation'
|
|
227
|
+
* });
|
|
228
|
+
*/
|
|
229
|
+
private executeErrorHooks;
|
|
230
|
+
/**
|
|
231
|
+
* @function _getMatchingHooks
|
|
232
|
+
* @internal
|
|
233
|
+
* @private
|
|
234
|
+
* @param {string} type - Hook type to match
|
|
235
|
+
* @param {string} path - Function path to test against patterns
|
|
236
|
+
* @returns {Array<object>} Sorted array of matching hooks
|
|
237
|
+
*
|
|
238
|
+
* @description
|
|
239
|
+
* Get hooks matching type and path, sorted by priority (DESC) then order (ASC).
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* // Get matching hooks
|
|
243
|
+
* const hooks = manager._getMatchingHooks("before", "database.users.create");
|
|
244
|
+
*/
|
|
245
|
+
private _getMatchingHooks;
|
|
246
|
+
/**
|
|
247
|
+
* @function _compilePattern
|
|
248
|
+
* @internal
|
|
249
|
+
* @private
|
|
250
|
+
* @param {string} pattern - Glob pattern string
|
|
251
|
+
* @returns {RegExp|null} Compiled RegExp or null for negation patterns
|
|
252
|
+
*
|
|
253
|
+
* @description
|
|
254
|
+
* Compile glob pattern to RegExp with support for:
|
|
255
|
+
* - `*`: single-level wildcard
|
|
256
|
+
* - `**`: multi-level wildcard
|
|
257
|
+
* - `{users,posts}`: brace expansion (max 10 nesting levels)
|
|
258
|
+
* - `!internal.*`: negation patterns
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
* // Compile pattern
|
|
262
|
+
* const regex = manager._compilePattern("database.*.create");
|
|
263
|
+
*/
|
|
264
|
+
private _compilePattern;
|
|
265
|
+
/**
|
|
266
|
+
* @function _expandBraces
|
|
267
|
+
* @internal
|
|
268
|
+
* @private
|
|
269
|
+
* @param {string} pattern - Pattern with potential braces
|
|
270
|
+
* @param {number} [depth=0] - Current nesting depth
|
|
271
|
+
* @returns {Array<string>} Expanded pattern alternatives
|
|
272
|
+
*
|
|
273
|
+
* @description
|
|
274
|
+
* Expand brace patterns like `{users,posts}` to multiple alternatives.
|
|
275
|
+
* Limits nesting to MAX_BRACE_NESTING to prevent performance issues.
|
|
276
|
+
*
|
|
277
|
+
* @example
|
|
278
|
+
* // Expand braces
|
|
279
|
+
* const patterns = manager._expandBraces("{users,posts}.create");
|
|
280
|
+
* // Returns: ["users.create", "posts.create"]
|
|
281
|
+
*/
|
|
282
|
+
private _expandBraces;
|
|
283
|
+
/**
|
|
284
|
+
* @function _splitAlternatives
|
|
285
|
+
* @internal
|
|
286
|
+
* @private
|
|
287
|
+
* @param {string} str - String to split on commas
|
|
288
|
+
* @returns {Array<string>} Split alternatives
|
|
289
|
+
*
|
|
290
|
+
* @description
|
|
291
|
+
* Split brace content on commas, respecting nested braces.
|
|
292
|
+
*
|
|
293
|
+
* @example
|
|
294
|
+
* // Split alternatives
|
|
295
|
+
* const alts = manager._splitAlternatives("users,posts,{admin,guest}");
|
|
296
|
+
*/
|
|
297
|
+
private _splitAlternatives;
|
|
298
|
+
/**
|
|
299
|
+
* @function _patternToRegex
|
|
300
|
+
* @internal
|
|
301
|
+
* @private
|
|
302
|
+
* @param {string} pattern - Pattern without braces
|
|
303
|
+
* @returns {string} Regex pattern string
|
|
304
|
+
*
|
|
305
|
+
* @description
|
|
306
|
+
* Convert glob pattern to regex pattern string.
|
|
307
|
+
*
|
|
308
|
+
* @example
|
|
309
|
+
* // Convert pattern
|
|
310
|
+
* const regex = manager._patternToRegex("database.*.create");
|
|
311
|
+
*/
|
|
312
|
+
private _patternToRegex;
|
|
313
|
+
/**
|
|
314
|
+
* @function _matchPattern
|
|
315
|
+
* @internal
|
|
316
|
+
* @private
|
|
317
|
+
* @param {RegExp|object} compiledPattern - Compiled pattern or negation object
|
|
318
|
+
* @param {string} path - Path to test
|
|
319
|
+
* @returns {boolean} True if path matches pattern
|
|
320
|
+
*
|
|
321
|
+
* @description
|
|
322
|
+
* Test if path matches compiled pattern, handling negation.
|
|
323
|
+
*
|
|
324
|
+
* @example
|
|
325
|
+
* // Match pattern
|
|
326
|
+
* const matches = manager._matchPattern(compiledPattern, "database.users.create");
|
|
327
|
+
*/
|
|
328
|
+
private _matchPattern;
|
|
329
|
+
}
|
|
330
|
+
//# sourceMappingURL=hooks.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.mts","sourceRoot":"","sources":["../../../../dist/lib/helpers/hooks.mjs"],"names":[],"mappings":"AAgCA;;;;;;;;;;;;;GAaG;AACH;IACC;;;;;;OAMG;IACH,sBALW,OAAO,mBACP,MAAM,YAEd;QAA0B,cAAc,GAAhC,OAAO;KACjB,EAQA;IANA,iBAAsB;IACtB,uBAAoC;IACpC,wBAAqD;IACrD,qBAAsB;IACtB,0BAA0B;IAC1B,gCAAmC;IAGpC;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,gBAnBW,MAAM,QACN,MAAM,+BAOd;QAAyB,QAAQ,GAAzB,MAAM;QACW,OAAO,GAAxB,MAAM;KACd,GAAU,MAAM,CA0BlB;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,0BAdW,MAAM,GACJ,OAAO,CA6BnB;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,oBAdW,MAAM,GACJ,IAAI,CA0BhB;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH,mBAfW,MAAM,GACJ,KAAK,CAAC,MAAM,CAAC,CA4BzB;IAED;;;;;;;;;;;;OAYG;IACH,wBAVW,MAAM,GACJ,IAAI,CAchB;IAED;;;;;;;;;;;OAWG;IACH,kBATa,IAAI,CAWhB;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,2BAkCC;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,0BAwBC;IAED;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,2BAqBC;IAED;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,0BAyBC;IAED;;;;;;;;;;;;;;OAcG;IACH,0BAkBC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH,wBAmBC;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,sBA4CC;IAED;;;;;;;;;;;;;OAaG;IACH,2BAuBC;IAED;;;;;;;;;;;;;OAaG;IACH,wBAiBC;IAED;;;;;;;;;;;;;;OAcG;IACH,sBAOC;CACD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-asynclocalstorage.d.mts","sourceRoot":"","sources":["../../../../dist/lib/runtime/runtime-asynclocalstorage.mjs"],"names":[],"mappings":"AAuCA;;;;;GAKG;AACH,wBAHU,qBAAqB,CAGkB;AAsB1C,gCAdI,MAAM,yBAEN,GAAG,gBAED,GAAG,
|
|
1
|
+
{"version":3,"file":"runtime-asynclocalstorage.d.mts","sourceRoot":"","sources":["../../../../dist/lib/runtime/runtime-asynclocalstorage.mjs"],"names":[],"mappings":"AAuCA;;;;;GAKG;AACH,wBAHU,qBAAqB,CAGkB;AAsB1C,gCAdI,MAAM,yBAEN,GAAG,gBAED,GAAG,CAoGf;AAiBM,0BAZM,MAAM,GAAC,IAAI,CAY0B;AAkN3C,iCAjBI,MAAM,YAiJhB;AAuSD;;;;;;;;;;;;;GAaG;AACH,mBATU,WAAS,MAAM,CAS6B;AAEtD;;;;;;;;;;;;;GAaG;AACH,sBATU,MAAM,CAS4C;AAE5D;;;;;;;;;;;;;GAaG;AACH,wBATU,MAAM,CASgD;;kCApzB9B,kBAAkB"}
|
|
@@ -10,12 +10,13 @@
|
|
|
10
10
|
*/
|
|
11
11
|
export function runWithCtx(ctx: object, fn: Function, thisArg: any, args: any[]): any;
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
13
|
+
* Create a wrapper function that sets __slothletPath on API functions.
|
|
14
|
+
* Required for hook pattern matching to work correctly.
|
|
14
15
|
* @internal
|
|
15
16
|
* @param {object} ctx - The context to bind
|
|
16
|
-
* @returns {function} A wrapper function
|
|
17
|
+
* @returns {function} A wrapper function that proxies the API
|
|
17
18
|
*/
|
|
18
|
-
export function makeWrapper(
|
|
19
|
+
export function makeWrapper(ctx: object): Function;
|
|
19
20
|
/**
|
|
20
21
|
* Legacy context management functions - kept for backwards compatibility
|
|
21
22
|
* but may not be needed with instance detection approach.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-livebindings.d.mts","sourceRoot":"","sources":["../../../../dist/lib/runtime/runtime-livebindings.mjs"],"names":[],"mappings":"AA2RA;;;;;;;;;GASG;AACH,gCANW,MAAM,yBAEN,GAAG,gBAED,GAAG,
|
|
1
|
+
{"version":3,"file":"runtime-livebindings.d.mts","sourceRoot":"","sources":["../../../../dist/lib/runtime/runtime-livebindings.mjs"],"names":[],"mappings":"AA2RA;;;;;;;;;GASG;AACH,gCANW,MAAM,yBAEN,GAAG,gBAED,GAAG,CAoFf;AAED;;;;;;GAMG;AACH,iCAHW,MAAM,YAiEhB;AAID;;;GAGG;AAEH,kCAEC;AAED,kDASC;AAhYD;;;;;GAKG;AACH,mBAHU,MAAM,CAmDd;AAEF;;;;;GAKG;AACH,sBAHU,MAAM,CA+Cd;AAEF;;;;;GAKG;AACH,wBAHU,MAAM,CA+Cd;AAEF;;;;;GAKG;AACH,yBAHU,MAAM,CAkCd"}
|
|
@@ -63,6 +63,13 @@ export type SlothletOptions = {
|
|
|
63
63
|
* - `"fork"`: Child process execution for complete isolation
|
|
64
64
|
*/
|
|
65
65
|
engine?: string;
|
|
66
|
+
/**
|
|
67
|
+
* - Runtime binding system:
|
|
68
|
+
* - `"async"` or `"asynclocalstorage"`: Use AsyncLocalStorage for context isolation (default, recommended)
|
|
69
|
+
* - `"live"` or `"livebindings"`: Use live binding system for dynamic context updates
|
|
70
|
+
* - Controls how `self`, `context`, and `reference` bindings are managed across function calls
|
|
71
|
+
*/
|
|
72
|
+
runtime?: string;
|
|
66
73
|
/**
|
|
67
74
|
* - Directory traversal depth control:
|
|
68
75
|
* - `Infinity`: Traverse all subdirectories recursively (default)
|