@context-action/core 0.0.3 → 0.0.4
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 +272 -0
- package/dist/index.cjs +724 -262
- package/dist/index.d.cts +591 -165
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +591 -165
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +722 -253
- package/dist/index.js.map +1 -1
- package/package.json +16 -12
package/dist/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createLogger, getDebugFromEnv, getLoggerNameFromEnv } from "@context-action/logger";
|
|
2
|
+
|
|
1
3
|
//#region rolldown:runtime
|
|
2
4
|
var __create = Object.create;
|
|
3
5
|
var __defProp = Object.defineProperty;
|
|
@@ -23,6 +25,197 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
25
|
enumerable: true
|
|
24
26
|
}) : target, mod));
|
|
25
27
|
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/execution-modes.ts
|
|
30
|
+
/**
|
|
31
|
+
* Execute handlers in sequential mode (one after another)
|
|
32
|
+
* @implements execution-modes
|
|
33
|
+
* @implements sequential-execution
|
|
34
|
+
* @memberof core-concepts
|
|
35
|
+
* @internal
|
|
36
|
+
* @since 1.0.0
|
|
37
|
+
*
|
|
38
|
+
* Executes action handlers sequentially in priority order, supporting flow control,
|
|
39
|
+
* conditional execution, and priority jumping within the pipeline.
|
|
40
|
+
*
|
|
41
|
+
* @template T - The type of the payload being processed
|
|
42
|
+
* @param context - Pipeline execution context with handlers and state
|
|
43
|
+
* @param createController - Factory function for creating pipeline controllers
|
|
44
|
+
* @param logger - Logger instance for tracing execution
|
|
45
|
+
* @returns Promise that resolves when all handlers complete or pipeline aborts
|
|
46
|
+
*
|
|
47
|
+
* Features:
|
|
48
|
+
* - Priority-based execution order (higher priority first)
|
|
49
|
+
* - Support for priority jumping within execution
|
|
50
|
+
* - Conditional handler execution (condition/validation checks)
|
|
51
|
+
* - Blocking/non-blocking handler support
|
|
52
|
+
* - Comprehensive error handling and recovery
|
|
53
|
+
*/
|
|
54
|
+
async function executeSequential(context, createController, logger) {
|
|
55
|
+
logger.trace("Executing in sequential mode", { handlerCount: context.handlers.length });
|
|
56
|
+
for (let i = 0; i < context.handlers.length; i++) {
|
|
57
|
+
if (context.aborted) {
|
|
58
|
+
logger.trace("Sequential execution aborted", {
|
|
59
|
+
atIndex: i,
|
|
60
|
+
reason: context.abortReason
|
|
61
|
+
});
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
if (context.jumpToPriority !== void 0) {
|
|
65
|
+
const jumpIndex = context.handlers.findIndex((handler) => handler.config.priority === context.jumpToPriority);
|
|
66
|
+
if (jumpIndex !== -1 && jumpIndex !== i) {
|
|
67
|
+
logger.trace("Jumping to priority", {
|
|
68
|
+
fromIndex: i,
|
|
69
|
+
toIndex: jumpIndex,
|
|
70
|
+
priority: context.jumpToPriority
|
|
71
|
+
});
|
|
72
|
+
i = jumpIndex - 1;
|
|
73
|
+
context.jumpToPriority = void 0;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
context.jumpToPriority = void 0;
|
|
77
|
+
}
|
|
78
|
+
const registration = context.handlers[i];
|
|
79
|
+
context.currentIndex = i;
|
|
80
|
+
if (registration.config.condition && !registration.config.condition()) {
|
|
81
|
+
logger.debug(`Skipping handler '${registration.id}' - condition not met`);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (registration.config.validation && !registration.config.validation(context.payload)) {
|
|
85
|
+
logger.debug(`Skipping handler '${registration.id}' - validation failed`);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const controller = createController(registration, i);
|
|
89
|
+
try {
|
|
90
|
+
logger.trace(`Executing handler ${i + 1}/${context.handlers.length}`, {
|
|
91
|
+
handlerId: registration.id,
|
|
92
|
+
priority: registration.config.priority
|
|
93
|
+
});
|
|
94
|
+
const result = registration.handler(context.payload, controller);
|
|
95
|
+
if (registration.config.blocking && result instanceof Promise) {
|
|
96
|
+
logger.trace(`Waiting for blocking handler '${registration.id}'`);
|
|
97
|
+
await result;
|
|
98
|
+
}
|
|
99
|
+
logger.trace(`Handler '${registration.id}' completed`);
|
|
100
|
+
} catch (error) {
|
|
101
|
+
logger.error(`Handler '${registration.id}' threw an error`, error);
|
|
102
|
+
if (registration.config.blocking) throw error;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Execute handlers in parallel mode (all at once)
|
|
108
|
+
* @internal
|
|
109
|
+
*/
|
|
110
|
+
async function executeParallel(context, createController, logger) {
|
|
111
|
+
logger.trace("Executing in parallel mode", { handlerCount: context.handlers.length });
|
|
112
|
+
const runnableHandlers = context.handlers.filter((registration, _index) => {
|
|
113
|
+
if (registration.config.condition && !registration.config.condition()) {
|
|
114
|
+
logger.debug(`Skipping handler '${registration.id}' - condition not met`);
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
if (registration.config.validation && !registration.config.validation(context.payload)) {
|
|
118
|
+
logger.debug(`Skipping handler '${registration.id}' - validation failed`);
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
return true;
|
|
122
|
+
});
|
|
123
|
+
logger.trace(`Running ${runnableHandlers.length} handlers in parallel`);
|
|
124
|
+
const handlerPromises = runnableHandlers.map(async (registration, _index) => {
|
|
125
|
+
const controller = createController(registration, _index);
|
|
126
|
+
try {
|
|
127
|
+
logger.trace(`Starting parallel handler '${registration.id}'`);
|
|
128
|
+
const result = registration.handler(context.payload, controller);
|
|
129
|
+
if (result instanceof Promise) await result;
|
|
130
|
+
logger.trace(`Parallel handler '${registration.id}' completed`);
|
|
131
|
+
return {
|
|
132
|
+
success: true,
|
|
133
|
+
handlerId: registration.id
|
|
134
|
+
};
|
|
135
|
+
} catch (error) {
|
|
136
|
+
logger.error(`Parallel handler '${registration.id}' failed`, error);
|
|
137
|
+
if (registration.config.blocking) throw error;
|
|
138
|
+
return {
|
|
139
|
+
success: false,
|
|
140
|
+
handlerId: registration.id,
|
|
141
|
+
error
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
const results = await Promise.allSettled(handlerPromises);
|
|
146
|
+
const failures = results.filter((result, index) => {
|
|
147
|
+
if (result.status === "rejected") {
|
|
148
|
+
const registration = runnableHandlers[index];
|
|
149
|
+
return registration.config.blocking;
|
|
150
|
+
}
|
|
151
|
+
return false;
|
|
152
|
+
});
|
|
153
|
+
if (failures.length > 0) {
|
|
154
|
+
const firstFailure = failures[0];
|
|
155
|
+
throw firstFailure.reason;
|
|
156
|
+
}
|
|
157
|
+
logger.trace("Parallel execution completed", {
|
|
158
|
+
successful: results.filter((r) => r.status === "fulfilled").length,
|
|
159
|
+
failed: results.filter((r) => r.status === "rejected").length
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Execute handlers in race mode (first to complete wins)
|
|
164
|
+
* @internal
|
|
165
|
+
*/
|
|
166
|
+
async function executeRace(context, createController, logger) {
|
|
167
|
+
logger.trace("Executing in race mode", { handlerCount: context.handlers.length });
|
|
168
|
+
const runnableHandlers = context.handlers.filter((registration, _index) => {
|
|
169
|
+
if (registration.config.condition && !registration.config.condition()) {
|
|
170
|
+
logger.debug(`Skipping handler '${registration.id}' - condition not met`);
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
if (registration.config.validation && !registration.config.validation(context.payload)) {
|
|
174
|
+
logger.debug(`Skipping handler '${registration.id}' - validation failed`);
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
return true;
|
|
178
|
+
});
|
|
179
|
+
if (runnableHandlers.length === 0) {
|
|
180
|
+
logger.trace("No runnable handlers for race mode");
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
logger.trace(`Racing ${runnableHandlers.length} handlers`);
|
|
184
|
+
const handlerPromises = runnableHandlers.map(async (registration, _index) => {
|
|
185
|
+
const controller = createController(registration, _index);
|
|
186
|
+
try {
|
|
187
|
+
logger.trace(`Starting race handler '${registration.id}'`);
|
|
188
|
+
const result = registration.handler(context.payload, controller);
|
|
189
|
+
if (result instanceof Promise) await result;
|
|
190
|
+
logger.trace(`Race handler '${registration.id}' completed`);
|
|
191
|
+
return {
|
|
192
|
+
success: true,
|
|
193
|
+
handlerId: registration.id,
|
|
194
|
+
registration
|
|
195
|
+
};
|
|
196
|
+
} catch (error) {
|
|
197
|
+
logger.error(`Race handler '${registration.id}' failed`, error);
|
|
198
|
+
return {
|
|
199
|
+
success: false,
|
|
200
|
+
handlerId: registration.id,
|
|
201
|
+
error,
|
|
202
|
+
registration
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
try {
|
|
207
|
+
const winner = await Promise.race(handlerPromises);
|
|
208
|
+
logger.debug("Race completed", {
|
|
209
|
+
winner: winner.handlerId,
|
|
210
|
+
success: winner.success
|
|
211
|
+
});
|
|
212
|
+
if (!winner.success && winner.registration?.config.blocking) throw winner.error;
|
|
213
|
+
} catch (error) {
|
|
214
|
+
logger.error("Race execution failed", error);
|
|
215
|
+
throw error;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
26
219
|
//#endregion
|
|
27
220
|
//#region ../../node_modules/.pnpm/@oxc-project+runtime@0.77.3/node_modules/@oxc-project/runtime/src/helpers/typeof.js
|
|
28
221
|
var require_typeof = __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.77.3/node_modules/@oxc-project/runtime/src/helpers/typeof.js"(exports, module) {
|
|
@@ -82,337 +275,613 @@ var require_defineProperty = __commonJS({ "../../node_modules/.pnpm/@oxc-project
|
|
|
82
275
|
} });
|
|
83
276
|
|
|
84
277
|
//#endregion
|
|
85
|
-
//#region src/
|
|
278
|
+
//#region src/action-guard.ts
|
|
86
279
|
var import_defineProperty$1 = __toESM(require_defineProperty(), 1);
|
|
87
280
|
/**
|
|
88
|
-
*
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
*
|
|
281
|
+
* Action Guard system for managing action execution timing
|
|
282
|
+
* @implements action-guard
|
|
283
|
+
* @implements performance-optimization
|
|
284
|
+
* @implements user-experience-optimization
|
|
285
|
+
* @memberof core-concepts
|
|
286
|
+
* @internal
|
|
287
|
+
* @since 1.0.0
|
|
288
|
+
*
|
|
289
|
+
* Provides debouncing, throttling, and blocking mechanisms for action execution
|
|
290
|
+
* to optimize performance and enhance user experience. Manages timing state
|
|
291
|
+
* per action to prevent unnecessary or excessive action invocations.
|
|
292
|
+
*
|
|
293
|
+
* Key Features:
|
|
294
|
+
* - Debouncing: Delay execution until activity stops
|
|
295
|
+
* - Throttling: Limit execution frequency to intervals
|
|
296
|
+
* - Per-action state management with automatic cleanup
|
|
297
|
+
* - Memory leak prevention through proper timer management
|
|
298
|
+
*
|
|
299
|
+
* @example
|
|
300
|
+
* ```typescript
|
|
301
|
+
* const guard = new ActionGuard(logger);
|
|
302
|
+
*
|
|
303
|
+
* // Debounce search input (wait 300ms after typing stops)
|
|
304
|
+
* if (await guard.debounce('search', 300)) {
|
|
305
|
+
* executeSearch();
|
|
306
|
+
* }
|
|
307
|
+
*
|
|
308
|
+
* // Throttle scroll handler (max once per 100ms)
|
|
309
|
+
* if (guard.throttle('scroll', 100)) {
|
|
310
|
+
* updateScrollPosition();
|
|
311
|
+
* }
|
|
312
|
+
* ```
|
|
101
313
|
*/
|
|
102
|
-
var
|
|
103
|
-
constructor(
|
|
104
|
-
this
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return level >= this.level;
|
|
108
|
-
}
|
|
109
|
-
formatMessage(level, message) {
|
|
110
|
-
return `[${level.toUpperCase()}] ${message}`;
|
|
314
|
+
var ActionGuard = class {
|
|
315
|
+
constructor(logger) {
|
|
316
|
+
(0, import_defineProperty$1.default)(this, "guards", /* @__PURE__ */ new Map());
|
|
317
|
+
(0, import_defineProperty$1.default)(this, "logger", void 0);
|
|
318
|
+
this.logger = logger;
|
|
111
319
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
320
|
+
/**
|
|
321
|
+
* Check if action should be debounced
|
|
322
|
+
* @param actionKey - Unique key for the action
|
|
323
|
+
* @param debounceMs - Debounce delay in milliseconds
|
|
324
|
+
* @returns Promise that resolves when debounce period is complete
|
|
325
|
+
*/
|
|
326
|
+
async debounce(actionKey, debounceMs) {
|
|
327
|
+
this.logger.trace(`Checking debounce for '${actionKey}'`, { debounceMs });
|
|
328
|
+
let state = this.guards.get(actionKey);
|
|
329
|
+
if (!state) {
|
|
330
|
+
state = {
|
|
331
|
+
lastExecuted: 0,
|
|
332
|
+
isThrottled: false
|
|
333
|
+
};
|
|
334
|
+
this.guards.set(actionKey, state);
|
|
335
|
+
}
|
|
336
|
+
if (state.debounceTimer) {
|
|
337
|
+
clearTimeout(state.debounceTimer);
|
|
338
|
+
this.logger.trace(`Cleared existing debounce timer for '${actionKey}'`);
|
|
339
|
+
}
|
|
340
|
+
return new Promise((resolve) => {
|
|
341
|
+
state.debounceTimer = setTimeout(() => {
|
|
342
|
+
this.logger.trace(`Debounce completed for '${actionKey}'`);
|
|
343
|
+
state.debounceTimer = void 0;
|
|
344
|
+
state.lastExecuted = Date.now();
|
|
345
|
+
resolve(true);
|
|
346
|
+
}, debounceMs);
|
|
347
|
+
this.logger.trace(`Set debounce timer for '${actionKey}'`, { delay: debounceMs });
|
|
348
|
+
});
|
|
117
349
|
}
|
|
118
|
-
|
|
119
|
-
|
|
350
|
+
/**
|
|
351
|
+
* Check if action should be throttled
|
|
352
|
+
* @param actionKey - Unique key for the action
|
|
353
|
+
* @param throttleMs - Throttle delay in milliseconds
|
|
354
|
+
* @returns True if action should proceed, false if throttled
|
|
355
|
+
*/
|
|
356
|
+
throttle(actionKey, throttleMs) {
|
|
357
|
+
this.logger.trace(`Checking throttle for '${actionKey}'`, { throttleMs });
|
|
358
|
+
let state = this.guards.get(actionKey);
|
|
359
|
+
if (!state) {
|
|
360
|
+
state = {
|
|
361
|
+
lastExecuted: 0,
|
|
362
|
+
isThrottled: false
|
|
363
|
+
};
|
|
364
|
+
this.guards.set(actionKey, state);
|
|
365
|
+
}
|
|
366
|
+
const now = Date.now();
|
|
367
|
+
const timeSinceLastExecution = now - state.lastExecuted;
|
|
368
|
+
if (timeSinceLastExecution >= throttleMs) {
|
|
369
|
+
state.lastExecuted = now;
|
|
370
|
+
state.isThrottled = false;
|
|
371
|
+
this.logger.trace(`Throttle passed for '${actionKey}'`, {
|
|
372
|
+
timeSinceLastExecution,
|
|
373
|
+
throttleMs
|
|
374
|
+
});
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
if (state.isThrottled) {
|
|
378
|
+
this.logger.trace(`Action '${actionKey}' is already throttled`);
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
state.isThrottled = true;
|
|
382
|
+
const remainingTime = throttleMs - timeSinceLastExecution;
|
|
383
|
+
state.throttleTimer = setTimeout(() => {
|
|
384
|
+
state.isThrottled = false;
|
|
385
|
+
state.throttleTimer = void 0;
|
|
386
|
+
this.logger.trace(`Throttle period ended for '${actionKey}'`);
|
|
387
|
+
}, remainingTime);
|
|
388
|
+
this.logger.trace(`Action '${actionKey}' throttled`, {
|
|
389
|
+
timeSinceLastExecution,
|
|
390
|
+
remainingTime
|
|
391
|
+
});
|
|
392
|
+
return false;
|
|
120
393
|
}
|
|
121
|
-
|
|
122
|
-
|
|
394
|
+
/**
|
|
395
|
+
* Clear all guards for an action
|
|
396
|
+
* @param actionKey - Action key to clear
|
|
397
|
+
*/
|
|
398
|
+
clearGuards(actionKey) {
|
|
399
|
+
this.logger.trace(`Clearing guards for '${actionKey}'`);
|
|
400
|
+
const state = this.guards.get(actionKey);
|
|
401
|
+
if (state) {
|
|
402
|
+
if (state.debounceTimer) clearTimeout(state.debounceTimer);
|
|
403
|
+
if (state.throttleTimer) clearTimeout(state.throttleTimer);
|
|
404
|
+
this.guards.delete(actionKey);
|
|
405
|
+
this.logger.debug(`Cleared guards for '${actionKey}'`);
|
|
406
|
+
}
|
|
123
407
|
}
|
|
124
|
-
|
|
125
|
-
|
|
408
|
+
/**
|
|
409
|
+
* Clear all guards
|
|
410
|
+
*/
|
|
411
|
+
clearAll() {
|
|
412
|
+
this.logger.trace("Clearing all action guards");
|
|
413
|
+
for (const [, state] of this.guards) {
|
|
414
|
+
if (state.debounceTimer) clearTimeout(state.debounceTimer);
|
|
415
|
+
if (state.throttleTimer) clearTimeout(state.throttleTimer);
|
|
416
|
+
}
|
|
417
|
+
this.guards.clear();
|
|
418
|
+
this.logger.debug("Cleared all action guards");
|
|
126
419
|
}
|
|
127
|
-
|
|
128
|
-
|
|
420
|
+
/**
|
|
421
|
+
* Get current guard state for debugging
|
|
422
|
+
* @param actionKey - Action key to inspect
|
|
423
|
+
*/
|
|
424
|
+
getGuardState(actionKey) {
|
|
425
|
+
return this.guards.get(actionKey);
|
|
129
426
|
}
|
|
130
|
-
|
|
131
|
-
|
|
427
|
+
/**
|
|
428
|
+
* Get all active guards for debugging
|
|
429
|
+
*/
|
|
430
|
+
getAllGuardStates() {
|
|
431
|
+
return new Map(this.guards);
|
|
132
432
|
}
|
|
133
433
|
};
|
|
434
|
+
|
|
435
|
+
//#endregion
|
|
436
|
+
//#region src/ActionRegister.ts
|
|
437
|
+
var import_defineProperty = __toESM(require_defineProperty(), 1);
|
|
134
438
|
/**
|
|
135
|
-
*
|
|
136
|
-
|
|
137
|
-
function parseLogLevel(level) {
|
|
138
|
-
const upperLevel = level.toUpperCase();
|
|
139
|
-
switch (upperLevel) {
|
|
140
|
-
case "TRACE": return LogLevel.TRACE;
|
|
141
|
-
case "DEBUG": return LogLevel.DEBUG;
|
|
142
|
-
case "INFO": return LogLevel.INFO;
|
|
143
|
-
case "WARN": return LogLevel.WARN;
|
|
144
|
-
case "ERROR": return LogLevel.ERROR;
|
|
145
|
-
case "FATAL": return LogLevel.FATAL;
|
|
146
|
-
default: return LogLevel.ERROR;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Get log level from environment variable or default to ERROR
|
|
151
|
-
*/
|
|
152
|
-
function getLogLevelFromEnv() {
|
|
153
|
-
if (typeof process !== "undefined" && process.env) {
|
|
154
|
-
const envLevel = process.env.LOG_LEVEL || process.env.ACTION_LOG_LEVEL;
|
|
155
|
-
if (envLevel) return parseLogLevel(envLevel);
|
|
156
|
-
}
|
|
157
|
-
return LogLevel.TRACE;
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Extract trace ID from payload if it exists
|
|
161
|
-
*/
|
|
162
|
-
function extractTraceIdFromPayload(payload) {
|
|
163
|
-
if (payload && typeof payload === "object") return payload._traceId || payload.traceId || payload.trace_id;
|
|
164
|
-
return void 0;
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Extract session ID from payload if it exists
|
|
168
|
-
*/
|
|
169
|
-
function extractSessionIdFromPayload(payload) {
|
|
170
|
-
if (payload && typeof payload === "object") return payload._sessionId || payload.sessionId || payload.session_id;
|
|
171
|
-
return void 0;
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Create OTEL context from payload
|
|
175
|
-
*/
|
|
176
|
-
function createOtelContextFromPayload(payload) {
|
|
177
|
-
return {
|
|
178
|
-
traceId: extractTraceIdFromPayload(payload),
|
|
179
|
-
sessionId: extractSessionIdFromPayload(payload),
|
|
180
|
-
metadata: payload
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* OpenTelemetry-aware console logger implementation
|
|
439
|
+
* Simple event emitter implementation for ActionRegister events
|
|
440
|
+
* @internal
|
|
185
441
|
*/
|
|
186
|
-
var
|
|
187
|
-
constructor(
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
this.
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
442
|
+
var SimpleEventEmitter = class {
|
|
443
|
+
constructor() {
|
|
444
|
+
(0, import_defineProperty.default)(this, "listeners", /* @__PURE__ */ new Map());
|
|
445
|
+
}
|
|
446
|
+
on(event, handler) {
|
|
447
|
+
if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
448
|
+
this.listeners.get(event).add(handler);
|
|
449
|
+
return () => this.off(event, handler);
|
|
450
|
+
}
|
|
451
|
+
emit(event, data) {
|
|
452
|
+
const eventListeners = this.listeners.get(event);
|
|
453
|
+
if (eventListeners) eventListeners.forEach((handler) => {
|
|
454
|
+
try {
|
|
455
|
+
handler(data);
|
|
456
|
+
} catch (error) {
|
|
457
|
+
console.error(`Error in event handler for ${String(event)}:`, error);
|
|
458
|
+
}
|
|
459
|
+
});
|
|
196
460
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
formatWithContext(level, message) {
|
|
204
|
-
const contextParts = [];
|
|
205
|
-
if (this.context.sessionId) contextParts.push(`session=${this.context.sessionId}`);
|
|
206
|
-
if (this.context.traceId) contextParts.push(`trace=${this.context.traceId}`);
|
|
207
|
-
if (this.context.spanId) contextParts.push(`span=${this.context.spanId}`);
|
|
208
|
-
const contextStr = contextParts.length > 0 ? ` [${contextParts.join(", ")}]` : "";
|
|
209
|
-
return `[${level.toUpperCase()}]${contextStr} ${message}`;
|
|
210
|
-
}
|
|
211
|
-
logWithContext(level, message, ...args) {
|
|
212
|
-
const levelName = LogLevel[level].toLowerCase();
|
|
213
|
-
const formattedMessage = this.formatWithContext(levelName, message);
|
|
214
|
-
switch (level) {
|
|
215
|
-
case LogLevel.TRACE:
|
|
216
|
-
if (this.shouldLog(LogLevel.TRACE)) console.trace(formattedMessage, ...args);
|
|
217
|
-
break;
|
|
218
|
-
case LogLevel.DEBUG:
|
|
219
|
-
if (this.shouldLog(LogLevel.DEBUG)) console.debug(formattedMessage, ...args);
|
|
220
|
-
break;
|
|
221
|
-
case LogLevel.INFO:
|
|
222
|
-
if (this.shouldLog(LogLevel.INFO)) console.info(formattedMessage, ...args);
|
|
223
|
-
break;
|
|
224
|
-
case LogLevel.WARN:
|
|
225
|
-
if (this.shouldLog(LogLevel.WARN)) console.warn(formattedMessage, ...args);
|
|
226
|
-
break;
|
|
227
|
-
case LogLevel.ERROR:
|
|
228
|
-
if (this.shouldLog(LogLevel.ERROR)) console.error(formattedMessage, ...args);
|
|
229
|
-
break;
|
|
230
|
-
case LogLevel.FATAL:
|
|
231
|
-
if (this.shouldLog(LogLevel.FATAL)) console.error(formattedMessage, ...args);
|
|
232
|
-
break;
|
|
461
|
+
off(event, handler) {
|
|
462
|
+
const eventListeners = this.listeners.get(event);
|
|
463
|
+
if (eventListeners) {
|
|
464
|
+
eventListeners.delete(handler);
|
|
465
|
+
if (eventListeners.size === 0) this.listeners.delete(event);
|
|
233
466
|
}
|
|
234
467
|
}
|
|
235
|
-
|
|
236
|
-
this.
|
|
237
|
-
|
|
238
|
-
debug(message, ...args) {
|
|
239
|
-
this.logWithContext(LogLevel.DEBUG, message, ...args);
|
|
240
|
-
}
|
|
241
|
-
info(message, ...args) {
|
|
242
|
-
this.logWithContext(LogLevel.INFO, message, ...args);
|
|
243
|
-
}
|
|
244
|
-
warn(message, ...args) {
|
|
245
|
-
this.logWithContext(LogLevel.WARN, message, ...args);
|
|
246
|
-
}
|
|
247
|
-
error(message, ...args) {
|
|
248
|
-
this.logWithContext(LogLevel.ERROR, message, ...args);
|
|
249
|
-
}
|
|
250
|
-
fatal(message, ...args) {
|
|
251
|
-
this.logWithContext(LogLevel.FATAL, message, ...args);
|
|
468
|
+
removeAllListeners(event) {
|
|
469
|
+
if (event) this.listeners.delete(event);
|
|
470
|
+
else this.listeners.clear();
|
|
252
471
|
}
|
|
253
472
|
};
|
|
254
|
-
|
|
255
|
-
//#endregion
|
|
256
|
-
//#region src/ActionRegister.ts
|
|
257
|
-
var import_defineProperty = __toESM(require_defineProperty(), 1);
|
|
258
473
|
/**
|
|
259
|
-
*
|
|
474
|
+
* Central action registration and dispatch system
|
|
475
|
+
* @implements action-pipeline-system
|
|
476
|
+
* @implements actionregister
|
|
477
|
+
* @memberof core-concepts
|
|
478
|
+
*
|
|
479
|
+
* Core action pipeline management system with type-safe action dispatch
|
|
260
480
|
* @template T - Action payload map defining available actions and their payload types
|
|
481
|
+
*
|
|
261
482
|
* @example
|
|
262
483
|
* ```typescript
|
|
263
484
|
* interface AppActions extends ActionPayloadMap {
|
|
264
485
|
* increment: void;
|
|
265
486
|
* setCount: number;
|
|
487
|
+
* updateUser: { id: string; name: string };
|
|
266
488
|
* }
|
|
267
|
-
*
|
|
489
|
+
*
|
|
268
490
|
* const actionRegister = new ActionRegister<AppActions>();
|
|
269
|
-
*
|
|
270
|
-
* // Register handlers
|
|
271
|
-
* actionRegister.register('increment', () =>
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
491
|
+
*
|
|
492
|
+
* // Register handlers with priority and configuration
|
|
493
|
+
* actionRegister.register('increment', (_, controller) => {
|
|
494
|
+
* console.log('Incremented');
|
|
495
|
+
* controller.next();
|
|
496
|
+
* }, { priority: 10 });
|
|
497
|
+
*
|
|
498
|
+
* actionRegister.register('setCount', (count, controller) => {
|
|
499
|
+
* console.log(`Count: ${count}`);
|
|
500
|
+
* controller.next();
|
|
501
|
+
* });
|
|
502
|
+
*
|
|
503
|
+
* // Dispatch actions with type safety
|
|
275
504
|
* await actionRegister.dispatch('increment');
|
|
276
505
|
* await actionRegister.dispatch('setCount', 42);
|
|
277
506
|
* ```
|
|
278
507
|
*/
|
|
279
508
|
var ActionRegister = class {
|
|
280
|
-
constructor(config) {
|
|
509
|
+
constructor(config = {}) {
|
|
281
510
|
(0, import_defineProperty.default)(this, "pipelines", /* @__PURE__ */ new Map());
|
|
282
|
-
(0, import_defineProperty.default)(this, "atomSetters", /* @__PURE__ */ new Map());
|
|
283
511
|
(0, import_defineProperty.default)(this, "handlerCounter", 0);
|
|
284
512
|
(0, import_defineProperty.default)(this, "logger", void 0);
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
513
|
+
(0, import_defineProperty.default)(this, "events", new SimpleEventEmitter());
|
|
514
|
+
(0, import_defineProperty.default)(this, "config", void 0);
|
|
515
|
+
(0, import_defineProperty.default)(this, "actionGuard", void 0);
|
|
516
|
+
(0, import_defineProperty.default)(this, "executionMode", "sequential");
|
|
517
|
+
(0, import_defineProperty.default)(this, "actionExecutionModes", /* @__PURE__ */ new Map());
|
|
518
|
+
(0, import_defineProperty.default)(
|
|
519
|
+
this,
|
|
520
|
+
/**
|
|
521
|
+
* Dispatch action through pipeline
|
|
522
|
+
* @implements action-dispatcher
|
|
523
|
+
*
|
|
524
|
+
* Dispatch an action through the pipeline
|
|
525
|
+
* Overloaded to provide type safety for actions with and without payloads
|
|
526
|
+
*/
|
|
527
|
+
"dispatch",
|
|
528
|
+
async (action, payload) => {
|
|
529
|
+
const startTime = Date.now();
|
|
530
|
+
this.logger.trace(`Starting dispatch for action '${String(action)}'`, {
|
|
531
|
+
action,
|
|
532
|
+
payload,
|
|
533
|
+
startTime
|
|
534
|
+
});
|
|
535
|
+
this.events.emit("action:start", {
|
|
536
|
+
action,
|
|
537
|
+
payload
|
|
538
|
+
});
|
|
539
|
+
this.logger.trace(`Emitted 'action:start' event`);
|
|
540
|
+
this.logger.debug(`Dispatching action '${String(action)}'`, { payload });
|
|
541
|
+
const pipeline = this.pipelines.get(action);
|
|
542
|
+
if (!pipeline || pipeline.length === 0) {
|
|
543
|
+
this.logger.warn(`No handlers registered for action '${String(action)}'`);
|
|
544
|
+
this.logger.trace(`Dispatch completed early - no handlers`);
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
this.logger.trace(`Found ${pipeline.length} handlers for action '${String(action)}'`, { handlerIds: pipeline.map((reg) => reg.id) });
|
|
548
|
+
const currentExecutionMode = this.actionExecutionModes.get(action) || this.executionMode;
|
|
549
|
+
const context = {
|
|
550
|
+
action: String(action),
|
|
551
|
+
payload,
|
|
552
|
+
handlers: [...pipeline],
|
|
553
|
+
aborted: false,
|
|
554
|
+
abortReason: void 0,
|
|
555
|
+
currentIndex: 0,
|
|
556
|
+
jumpToPriority: void 0,
|
|
557
|
+
executionMode: currentExecutionMode
|
|
558
|
+
};
|
|
559
|
+
try {
|
|
560
|
+
await this.executePipeline(context);
|
|
561
|
+
const metrics = {
|
|
562
|
+
action: String(action),
|
|
563
|
+
executionTime: Date.now() - startTime,
|
|
564
|
+
handlerCount: context.handlers.length,
|
|
565
|
+
success: !context.aborted,
|
|
566
|
+
timestamp: Date.now()
|
|
567
|
+
};
|
|
568
|
+
if (context.aborted) {
|
|
569
|
+
metrics.error = context.abortReason;
|
|
570
|
+
this.events.emit("action:abort", {
|
|
571
|
+
action,
|
|
572
|
+
payload,
|
|
573
|
+
reason: context.abortReason
|
|
574
|
+
});
|
|
575
|
+
} else this.events.emit("action:complete", {
|
|
576
|
+
action,
|
|
577
|
+
payload,
|
|
578
|
+
metrics
|
|
579
|
+
});
|
|
580
|
+
this.logger.debug(`Completed action '${String(action)}'`, metrics);
|
|
581
|
+
} catch (error) {
|
|
582
|
+
const metrics = {
|
|
583
|
+
action: String(action),
|
|
584
|
+
executionTime: Date.now() - startTime,
|
|
585
|
+
handlerCount: context.handlers.length,
|
|
586
|
+
success: false,
|
|
587
|
+
error: error.message || "Unknown error",
|
|
588
|
+
timestamp: Date.now()
|
|
589
|
+
};
|
|
590
|
+
this.logger.error(`Error executing action '${String(action)}'`, metrics);
|
|
591
|
+
this.events.emit("action:error", {
|
|
592
|
+
action,
|
|
593
|
+
payload,
|
|
594
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
595
|
+
});
|
|
596
|
+
throw error;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
);
|
|
600
|
+
this.config = {
|
|
601
|
+
logger: config.logger || createLogger(config.logLevel),
|
|
602
|
+
logLevel: config.logLevel ?? 3,
|
|
603
|
+
name: config.name || getLoggerNameFromEnv(),
|
|
604
|
+
debug: config.debug ?? getDebugFromEnv(),
|
|
605
|
+
defaultExecutionMode: config.defaultExecutionMode ?? "sequential"
|
|
606
|
+
};
|
|
607
|
+
this.logger = this.config.logger;
|
|
608
|
+
this.actionGuard = new ActionGuard(this.logger);
|
|
609
|
+
this.executionMode = this.config.defaultExecutionMode;
|
|
610
|
+
this.logger.trace(`${this.config.name} constructor called`, { config });
|
|
611
|
+
if (this.config.debug) this.logger.info(`${this.config.name} initialized`, {
|
|
612
|
+
logLevel: this.config.logLevel,
|
|
613
|
+
debug: this.config.debug,
|
|
614
|
+
defaultExecutionMode: this.executionMode
|
|
295
615
|
});
|
|
616
|
+
this.logger.trace(`${this.config.name} constructor completed`);
|
|
296
617
|
}
|
|
297
618
|
/**
|
|
619
|
+
* Register action handler with pipeline
|
|
620
|
+
* @implements action-handler
|
|
621
|
+
*
|
|
298
622
|
* Register a handler for an action in the pipeline
|
|
299
623
|
* @param action - The action name to handle
|
|
300
624
|
* @param handler - The handler function to execute
|
|
301
625
|
* @param config - Optional configuration for the handler
|
|
302
626
|
* @returns Unregister function to remove the handler
|
|
627
|
+
*
|
|
303
628
|
* @example
|
|
304
629
|
* ```typescript
|
|
305
|
-
* const unregister = actionRegister.register('
|
|
306
|
-
*
|
|
307
|
-
*
|
|
630
|
+
* const unregister = actionRegister.register('updateUser',
|
|
631
|
+
* async (payload, controller) => {
|
|
632
|
+
* // Validate payload
|
|
633
|
+
* if (!payload.id) {
|
|
634
|
+
* controller.abort('User ID is required');
|
|
635
|
+
* return;
|
|
636
|
+
* }
|
|
637
|
+
*
|
|
638
|
+
* // Process update
|
|
639
|
+
* await updateUserInStore(payload);
|
|
640
|
+
* controller.next();
|
|
641
|
+
* },
|
|
642
|
+
* { priority: 10, blocking: true }
|
|
643
|
+
* );
|
|
308
644
|
*
|
|
309
645
|
* // Later, remove the handler
|
|
310
646
|
* unregister();
|
|
311
647
|
* ```
|
|
312
648
|
*/
|
|
313
649
|
register(action, handler, config = {}) {
|
|
650
|
+
this.logger.trace(`Registering handler for action '${String(action)}'`, { config });
|
|
651
|
+
const handlerId = config.id || `handler_${++this.handlerCounter}`;
|
|
652
|
+
this.logger.trace(`Generated handler ID: ${handlerId}`);
|
|
653
|
+
const registration = {
|
|
654
|
+
handler,
|
|
655
|
+
config: {
|
|
656
|
+
priority: config.priority ?? 0,
|
|
657
|
+
id: handlerId,
|
|
658
|
+
blocking: config.blocking ?? false,
|
|
659
|
+
once: config.once ?? false,
|
|
660
|
+
condition: config.condition || (() => true),
|
|
661
|
+
debounce: config.debounce,
|
|
662
|
+
throttle: config.throttle,
|
|
663
|
+
validation: config.validation,
|
|
664
|
+
middleware: config.middleware ?? false
|
|
665
|
+
},
|
|
666
|
+
id: handlerId
|
|
667
|
+
};
|
|
668
|
+
this.logger.trace(`Created handler registration`, { registration: {
|
|
669
|
+
id: handlerId,
|
|
670
|
+
config: registration.config
|
|
671
|
+
} });
|
|
314
672
|
if (!this.pipelines.has(action)) {
|
|
315
|
-
this.
|
|
316
|
-
this.
|
|
673
|
+
this.logger.trace(`Creating new pipeline for action: ${String(action)}`);
|
|
674
|
+
this.pipelines.set(action, []);
|
|
675
|
+
this.logger.debug(`Created pipeline for action: ${String(action)}`);
|
|
317
676
|
}
|
|
318
677
|
const pipeline = this.pipelines.get(action);
|
|
319
|
-
|
|
320
|
-
if (pipeline.
|
|
321
|
-
this.logger.warn(`Handler with
|
|
678
|
+
this.logger.trace(`Current pipeline for '${String(action)}' has ${pipeline.length} handlers`);
|
|
679
|
+
if (pipeline.some((reg) => reg.id === handlerId)) {
|
|
680
|
+
this.logger.warn(`Handler with ID '${handlerId}' already exists for action '${String(action)}'`);
|
|
681
|
+
this.logger.trace(`Duplicate handler registration aborted`);
|
|
322
682
|
return () => {};
|
|
323
683
|
}
|
|
324
|
-
pipeline.
|
|
325
|
-
|
|
326
|
-
|
|
684
|
+
pipeline.push(registration);
|
|
685
|
+
this.logger.trace(`Added handler to pipeline, current length: ${pipeline.length}`);
|
|
686
|
+
pipeline.sort((a, b) => b.config.priority - a.config.priority);
|
|
687
|
+
this.logger.trace(`Pipeline sorted by priority`, { priorities: pipeline.map((reg) => ({
|
|
688
|
+
id: reg.id,
|
|
689
|
+
priority: reg.config.priority
|
|
690
|
+
})) });
|
|
691
|
+
this.logger.debug(`Registered handler for action '${String(action)}'`, {
|
|
692
|
+
handlerId,
|
|
693
|
+
priority: registration.config.priority,
|
|
694
|
+
blocking: registration.config.blocking,
|
|
695
|
+
once: registration.config.once
|
|
327
696
|
});
|
|
328
|
-
this.
|
|
697
|
+
this.events.emit("handler:register", {
|
|
698
|
+
action,
|
|
329
699
|
handlerId,
|
|
330
|
-
|
|
331
|
-
blocking: config.blocking ?? false
|
|
700
|
+
config: registration.config
|
|
332
701
|
});
|
|
333
|
-
this.sortPipeline(action);
|
|
334
702
|
return () => {
|
|
335
|
-
|
|
336
|
-
|
|
703
|
+
this.logger.trace(`Unregistering handler '${handlerId}' from action '${String(action)}'`);
|
|
704
|
+
const index = pipeline.findIndex((reg) => reg.id === handlerId);
|
|
705
|
+
if (index !== -1) {
|
|
706
|
+
pipeline.splice(index, 1);
|
|
707
|
+
this.logger.debug(`Unregistered handler '${handlerId}' from action '${String(action)}'`);
|
|
708
|
+
this.logger.trace(`Pipeline now has ${pipeline.length} handlers`);
|
|
709
|
+
this.events.emit("handler:unregister", {
|
|
710
|
+
action,
|
|
711
|
+
handlerId
|
|
712
|
+
});
|
|
713
|
+
} else this.logger.trace(`Handler '${handlerId}' not found in pipeline for unregistration`);
|
|
337
714
|
};
|
|
338
715
|
}
|
|
339
|
-
registerAtomSetter(name, setter) {
|
|
340
|
-
this.atomSetters.set(name, setter);
|
|
341
|
-
this.logger.debug(`Registered atom setter: ${name}`);
|
|
342
|
-
}
|
|
343
716
|
/**
|
|
344
|
-
*
|
|
717
|
+
* Execute the pipeline with proper flow control
|
|
345
718
|
* @internal
|
|
346
719
|
*/
|
|
347
|
-
async
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
}
|
|
358
|
-
let modifiedPayload = payload;
|
|
359
|
-
const handlers = Array.from(pipeline.values());
|
|
360
|
-
let shouldContinue = true;
|
|
361
|
-
this.logger.trace(`Executing pipeline for action: ${String(action)}`, { handlerCount: handlers.length });
|
|
362
|
-
for (const { handler, config } of handlers) {
|
|
363
|
-
if (!shouldContinue) break;
|
|
364
|
-
const controller = {
|
|
365
|
-
next: () => {
|
|
366
|
-
shouldContinue = true;
|
|
367
|
-
},
|
|
720
|
+
async executePipeline(context) {
|
|
721
|
+
this.logger.trace(`Starting pipeline execution`, {
|
|
722
|
+
action: context.action,
|
|
723
|
+
handlerCount: context.handlers.length,
|
|
724
|
+
executionMode: context.executionMode,
|
|
725
|
+
payload: context.payload
|
|
726
|
+
});
|
|
727
|
+
const createController = (registration, _index) => {
|
|
728
|
+
return {
|
|
729
|
+
next: () => {},
|
|
368
730
|
abort: (reason) => {
|
|
369
|
-
|
|
370
|
-
|
|
731
|
+
this.logger.trace(`Handler '${registration.id}' is aborting pipeline`, { reason });
|
|
732
|
+
context.aborted = true;
|
|
733
|
+
context.abortReason = reason;
|
|
734
|
+
this.logger.warn(`Pipeline aborted by handler '${registration.id}'`, { reason });
|
|
371
735
|
},
|
|
372
736
|
modifyPayload: (modifier) => {
|
|
373
|
-
|
|
374
|
-
|
|
737
|
+
this.logger.trace(`Handler '${registration.id}' is modifying payload`);
|
|
738
|
+
const oldPayload = context.payload;
|
|
739
|
+
context.payload = modifier(context.payload);
|
|
740
|
+
this.logger.debug(`Payload modified by handler '${registration.id}'`);
|
|
741
|
+
this.logger.trace(`Payload change`, {
|
|
742
|
+
oldPayload,
|
|
743
|
+
newPayload: context.payload
|
|
744
|
+
});
|
|
745
|
+
},
|
|
746
|
+
getPayload: () => context.payload,
|
|
747
|
+
jumpToPriority: (priority) => {
|
|
748
|
+
this.logger.trace(`Handler '${registration.id}' jumping to priority ${priority}`);
|
|
749
|
+
context.jumpToPriority = priority;
|
|
375
750
|
}
|
|
376
751
|
};
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
752
|
+
};
|
|
753
|
+
switch (context.executionMode) {
|
|
754
|
+
case "sequential":
|
|
755
|
+
await executeSequential(context, createController, this.logger);
|
|
756
|
+
break;
|
|
757
|
+
case "parallel":
|
|
758
|
+
await executeParallel(context, createController, this.logger);
|
|
759
|
+
break;
|
|
760
|
+
case "race":
|
|
761
|
+
await executeRace(context, createController, this.logger);
|
|
762
|
+
break;
|
|
763
|
+
default: throw new Error(`Unknown execution mode: ${context.executionMode}`);
|
|
384
764
|
}
|
|
385
|
-
this.
|
|
765
|
+
this.cleanupOneTimeHandlers(context.action, context.handlers);
|
|
386
766
|
}
|
|
387
|
-
|
|
767
|
+
/**
|
|
768
|
+
* Clean up one-time handlers after pipeline execution
|
|
769
|
+
* @internal
|
|
770
|
+
*/
|
|
771
|
+
cleanupOneTimeHandlers(action, executedHandlers) {
|
|
388
772
|
const pipeline = this.pipelines.get(action);
|
|
389
773
|
if (!pipeline) return;
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
774
|
+
const oneTimeHandlers = executedHandlers.filter((reg) => reg.config.once);
|
|
775
|
+
if (oneTimeHandlers.length === 0) return;
|
|
776
|
+
this.logger.trace(`Cleaning up ${oneTimeHandlers.length} one-time handlers`);
|
|
777
|
+
oneTimeHandlers.forEach((registration) => {
|
|
778
|
+
const index = pipeline.findIndex((reg) => reg.id === registration.id);
|
|
779
|
+
if (index !== -1) {
|
|
780
|
+
pipeline.splice(index, 1);
|
|
781
|
+
this.logger.debug(`Removed one-time handler '${registration.id}'`);
|
|
782
|
+
}
|
|
783
|
+
});
|
|
784
|
+
this.logger.trace(`Pipeline now has ${pipeline.length} handlers`);
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Get the number of handlers registered for an action
|
|
788
|
+
* @param action - The action to check
|
|
789
|
+
* @returns Number of handlers registered
|
|
790
|
+
*/
|
|
791
|
+
getHandlerCount(action) {
|
|
792
|
+
const pipeline = this.pipelines.get(action);
|
|
793
|
+
const count = pipeline ? pipeline.length : 0;
|
|
794
|
+
this.logger.trace(`Handler count for '${String(action)}': ${count}`);
|
|
795
|
+
return count;
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Check if any handlers are registered for an action
|
|
799
|
+
* @param action - The action to check
|
|
800
|
+
* @returns True if handlers are registered
|
|
801
|
+
*/
|
|
802
|
+
hasHandlers(action) {
|
|
803
|
+
const hasHandlers = this.getHandlerCount(action) > 0;
|
|
804
|
+
this.logger.trace(`Has handlers for '${String(action)}': ${hasHandlers}`);
|
|
805
|
+
return hasHandlers;
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Get all registered action names
|
|
809
|
+
* @returns Array of action names
|
|
810
|
+
*/
|
|
811
|
+
getRegisteredActions() {
|
|
812
|
+
const actions = Array.from(this.pipelines.keys());
|
|
813
|
+
this.logger.trace(`Registered actions`, {
|
|
814
|
+
actions,
|
|
815
|
+
count: actions.length
|
|
816
|
+
});
|
|
817
|
+
return actions;
|
|
818
|
+
}
|
|
819
|
+
/**
|
|
820
|
+
* Clear all handlers for a specific action
|
|
821
|
+
* @param action - The action to clear
|
|
822
|
+
*/
|
|
823
|
+
clearAction(action) {
|
|
824
|
+
this.logger.trace(`Clearing handlers for action '${String(action)}'`);
|
|
825
|
+
const pipeline = this.pipelines.get(action);
|
|
826
|
+
if (pipeline) {
|
|
827
|
+
const handlerCount = pipeline.length;
|
|
828
|
+
this.pipelines.delete(action);
|
|
829
|
+
this.logger.debug(`Cleared ${handlerCount} handlers for action '${String(action)}'`);
|
|
830
|
+
this.logger.trace(`Action '${String(action)}' pipeline removed`);
|
|
831
|
+
} else this.logger.trace(`No pipeline found for action '${String(action)}' to clear`);
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* Clear all handlers for all actions
|
|
835
|
+
*/
|
|
836
|
+
clearAll() {
|
|
837
|
+
this.logger.trace(`Clearing all handlers and pipelines`);
|
|
838
|
+
const actionCount = this.pipelines.size;
|
|
839
|
+
const totalHandlers = Array.from(this.pipelines.values()).reduce((sum, pipeline) => sum + pipeline.length, 0);
|
|
840
|
+
this.logger.trace(`Before clear`, {
|
|
841
|
+
actionCount,
|
|
842
|
+
totalHandlers
|
|
394
843
|
});
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
this.logger.
|
|
398
|
-
|
|
399
|
-
|
|
844
|
+
this.pipelines.clear();
|
|
845
|
+
this.events.removeAllListeners();
|
|
846
|
+
this.logger.info(`Cleared all handlers`, {
|
|
847
|
+
actionCount,
|
|
848
|
+
totalHandlers
|
|
400
849
|
});
|
|
850
|
+
this.logger.trace(`All pipelines and event listeners cleared`);
|
|
851
|
+
}
|
|
852
|
+
/**
|
|
853
|
+
* Add event listener for ActionRegister events
|
|
854
|
+
* @param event - Event name to listen for
|
|
855
|
+
* @param handler - Event handler function
|
|
856
|
+
* @returns Unregister function to remove the listener
|
|
857
|
+
*/
|
|
858
|
+
on(event, handler) {
|
|
859
|
+
return this.events.on(event, handler);
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Remove event listener
|
|
863
|
+
* @param event - Event name
|
|
864
|
+
* @param handler - Event handler to remove
|
|
865
|
+
*/
|
|
866
|
+
off(event, handler) {
|
|
867
|
+
this.events.off(event, handler);
|
|
868
|
+
}
|
|
869
|
+
/**
|
|
870
|
+
* Get current configuration
|
|
871
|
+
* @returns Current ActionRegister configuration
|
|
872
|
+
*/
|
|
873
|
+
getConfig() {
|
|
874
|
+
return { ...this.config };
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Get logger instance
|
|
878
|
+
* @returns Current logger instance
|
|
879
|
+
*/
|
|
880
|
+
getLogger() {
|
|
881
|
+
return this.logger;
|
|
401
882
|
}
|
|
402
883
|
};
|
|
403
884
|
|
|
404
885
|
//#endregion
|
|
405
|
-
|
|
406
|
-
function createAction(type, payload) {
|
|
407
|
-
return {
|
|
408
|
-
type,
|
|
409
|
-
payload
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
function isAction(action, type) {
|
|
413
|
-
return action?.type === type;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
//#endregion
|
|
417
|
-
export { ActionRegister, ConsoleLogger, LogLevel, OtelConsoleLogger, createAction, createOtelContextFromPayload, extractSessionIdFromPayload, extractTraceIdFromPayload, getLogLevelFromEnv, isAction, parseLogLevel };
|
|
886
|
+
export { ActionGuard, ActionRegister, executeParallel, executeRace, executeSequential };
|
|
418
887
|
//# sourceMappingURL=index.js.map
|