@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.cjs
CHANGED
|
@@ -24,7 +24,199 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
}) : target, mod));
|
|
25
25
|
|
|
26
26
|
//#endregion
|
|
27
|
+
const __context_action_logger = __toESM(require("@context-action/logger"));
|
|
27
28
|
|
|
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
|
+
|
|
219
|
+
//#endregion
|
|
28
220
|
//#region ../../node_modules/.pnpm/@oxc-project+runtime@0.77.3/node_modules/@oxc-project/runtime/src/helpers/typeof.js
|
|
29
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) {
|
|
30
222
|
function _typeof$2(o) {
|
|
@@ -83,346 +275,616 @@ var require_defineProperty = __commonJS({ "../../node_modules/.pnpm/@oxc-project
|
|
|
83
275
|
} });
|
|
84
276
|
|
|
85
277
|
//#endregion
|
|
86
|
-
//#region src/
|
|
278
|
+
//#region src/action-guard.ts
|
|
87
279
|
var import_defineProperty$1 = __toESM(require_defineProperty(), 1);
|
|
88
280
|
/**
|
|
89
|
-
*
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
*
|
|
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
|
+
* ```
|
|
102
313
|
*/
|
|
103
|
-
var
|
|
104
|
-
constructor(
|
|
105
|
-
this
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return level >= this.level;
|
|
109
|
-
}
|
|
110
|
-
formatMessage(level, message) {
|
|
111
|
-
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;
|
|
112
319
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
+
});
|
|
118
349
|
}
|
|
119
|
-
|
|
120
|
-
|
|
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;
|
|
121
393
|
}
|
|
122
|
-
|
|
123
|
-
|
|
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
|
+
}
|
|
124
407
|
}
|
|
125
|
-
|
|
126
|
-
|
|
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");
|
|
127
419
|
}
|
|
128
|
-
|
|
129
|
-
|
|
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);
|
|
130
426
|
}
|
|
131
|
-
|
|
132
|
-
|
|
427
|
+
/**
|
|
428
|
+
* Get all active guards for debugging
|
|
429
|
+
*/
|
|
430
|
+
getAllGuardStates() {
|
|
431
|
+
return new Map(this.guards);
|
|
133
432
|
}
|
|
134
433
|
};
|
|
434
|
+
|
|
435
|
+
//#endregion
|
|
436
|
+
//#region src/ActionRegister.ts
|
|
437
|
+
var import_defineProperty = __toESM(require_defineProperty(), 1);
|
|
135
438
|
/**
|
|
136
|
-
*
|
|
137
|
-
|
|
138
|
-
function parseLogLevel(level) {
|
|
139
|
-
const upperLevel = level.toUpperCase();
|
|
140
|
-
switch (upperLevel) {
|
|
141
|
-
case "TRACE": return LogLevel.TRACE;
|
|
142
|
-
case "DEBUG": return LogLevel.DEBUG;
|
|
143
|
-
case "INFO": return LogLevel.INFO;
|
|
144
|
-
case "WARN": return LogLevel.WARN;
|
|
145
|
-
case "ERROR": return LogLevel.ERROR;
|
|
146
|
-
case "FATAL": return LogLevel.FATAL;
|
|
147
|
-
default: return LogLevel.ERROR;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Get log level from environment variable or default to ERROR
|
|
152
|
-
*/
|
|
153
|
-
function getLogLevelFromEnv() {
|
|
154
|
-
if (typeof process !== "undefined" && process.env) {
|
|
155
|
-
const envLevel = process.env.LOG_LEVEL || process.env.ACTION_LOG_LEVEL;
|
|
156
|
-
if (envLevel) return parseLogLevel(envLevel);
|
|
157
|
-
}
|
|
158
|
-
return LogLevel.TRACE;
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* Extract trace ID from payload if it exists
|
|
162
|
-
*/
|
|
163
|
-
function extractTraceIdFromPayload(payload) {
|
|
164
|
-
if (payload && typeof payload === "object") return payload._traceId || payload.traceId || payload.trace_id;
|
|
165
|
-
return void 0;
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Extract session ID from payload if it exists
|
|
169
|
-
*/
|
|
170
|
-
function extractSessionIdFromPayload(payload) {
|
|
171
|
-
if (payload && typeof payload === "object") return payload._sessionId || payload.sessionId || payload.session_id;
|
|
172
|
-
return void 0;
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Create OTEL context from payload
|
|
176
|
-
*/
|
|
177
|
-
function createOtelContextFromPayload(payload) {
|
|
178
|
-
return {
|
|
179
|
-
traceId: extractTraceIdFromPayload(payload),
|
|
180
|
-
sessionId: extractSessionIdFromPayload(payload),
|
|
181
|
-
metadata: payload
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* OpenTelemetry-aware console logger implementation
|
|
439
|
+
* Simple event emitter implementation for ActionRegister events
|
|
440
|
+
* @internal
|
|
186
441
|
*/
|
|
187
|
-
var
|
|
188
|
-
constructor(
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
this.
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
+
});
|
|
197
460
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}
|
|
204
|
-
formatWithContext(level, message) {
|
|
205
|
-
const contextParts = [];
|
|
206
|
-
if (this.context.sessionId) contextParts.push(`session=${this.context.sessionId}`);
|
|
207
|
-
if (this.context.traceId) contextParts.push(`trace=${this.context.traceId}`);
|
|
208
|
-
if (this.context.spanId) contextParts.push(`span=${this.context.spanId}`);
|
|
209
|
-
const contextStr = contextParts.length > 0 ? ` [${contextParts.join(", ")}]` : "";
|
|
210
|
-
return `[${level.toUpperCase()}]${contextStr} ${message}`;
|
|
211
|
-
}
|
|
212
|
-
logWithContext(level, message, ...args) {
|
|
213
|
-
const levelName = LogLevel[level].toLowerCase();
|
|
214
|
-
const formattedMessage = this.formatWithContext(levelName, message);
|
|
215
|
-
switch (level) {
|
|
216
|
-
case LogLevel.TRACE:
|
|
217
|
-
if (this.shouldLog(LogLevel.TRACE)) console.trace(formattedMessage, ...args);
|
|
218
|
-
break;
|
|
219
|
-
case LogLevel.DEBUG:
|
|
220
|
-
if (this.shouldLog(LogLevel.DEBUG)) console.debug(formattedMessage, ...args);
|
|
221
|
-
break;
|
|
222
|
-
case LogLevel.INFO:
|
|
223
|
-
if (this.shouldLog(LogLevel.INFO)) console.info(formattedMessage, ...args);
|
|
224
|
-
break;
|
|
225
|
-
case LogLevel.WARN:
|
|
226
|
-
if (this.shouldLog(LogLevel.WARN)) console.warn(formattedMessage, ...args);
|
|
227
|
-
break;
|
|
228
|
-
case LogLevel.ERROR:
|
|
229
|
-
if (this.shouldLog(LogLevel.ERROR)) console.error(formattedMessage, ...args);
|
|
230
|
-
break;
|
|
231
|
-
case LogLevel.FATAL:
|
|
232
|
-
if (this.shouldLog(LogLevel.FATAL)) console.error(formattedMessage, ...args);
|
|
233
|
-
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);
|
|
234
466
|
}
|
|
235
467
|
}
|
|
236
|
-
|
|
237
|
-
this.
|
|
238
|
-
|
|
239
|
-
debug(message, ...args) {
|
|
240
|
-
this.logWithContext(LogLevel.DEBUG, message, ...args);
|
|
241
|
-
}
|
|
242
|
-
info(message, ...args) {
|
|
243
|
-
this.logWithContext(LogLevel.INFO, message, ...args);
|
|
244
|
-
}
|
|
245
|
-
warn(message, ...args) {
|
|
246
|
-
this.logWithContext(LogLevel.WARN, message, ...args);
|
|
247
|
-
}
|
|
248
|
-
error(message, ...args) {
|
|
249
|
-
this.logWithContext(LogLevel.ERROR, message, ...args);
|
|
250
|
-
}
|
|
251
|
-
fatal(message, ...args) {
|
|
252
|
-
this.logWithContext(LogLevel.FATAL, message, ...args);
|
|
468
|
+
removeAllListeners(event) {
|
|
469
|
+
if (event) this.listeners.delete(event);
|
|
470
|
+
else this.listeners.clear();
|
|
253
471
|
}
|
|
254
472
|
};
|
|
255
|
-
|
|
256
|
-
//#endregion
|
|
257
|
-
//#region src/ActionRegister.ts
|
|
258
|
-
var import_defineProperty = __toESM(require_defineProperty(), 1);
|
|
259
473
|
/**
|
|
260
|
-
*
|
|
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
|
|
261
480
|
* @template T - Action payload map defining available actions and their payload types
|
|
481
|
+
*
|
|
262
482
|
* @example
|
|
263
483
|
* ```typescript
|
|
264
484
|
* interface AppActions extends ActionPayloadMap {
|
|
265
485
|
* increment: void;
|
|
266
486
|
* setCount: number;
|
|
487
|
+
* updateUser: { id: string; name: string };
|
|
267
488
|
* }
|
|
268
|
-
*
|
|
489
|
+
*
|
|
269
490
|
* const actionRegister = new ActionRegister<AppActions>();
|
|
270
|
-
*
|
|
271
|
-
* // Register handlers
|
|
272
|
-
* actionRegister.register('increment', () =>
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
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
|
|
276
504
|
* await actionRegister.dispatch('increment');
|
|
277
505
|
* await actionRegister.dispatch('setCount', 42);
|
|
278
506
|
* ```
|
|
279
507
|
*/
|
|
280
508
|
var ActionRegister = class {
|
|
281
|
-
constructor(config) {
|
|
509
|
+
constructor(config = {}) {
|
|
282
510
|
(0, import_defineProperty.default)(this, "pipelines", /* @__PURE__ */ new Map());
|
|
283
|
-
(0, import_defineProperty.default)(this, "atomSetters", /* @__PURE__ */ new Map());
|
|
284
511
|
(0, import_defineProperty.default)(this, "handlerCounter", 0);
|
|
285
512
|
(0, import_defineProperty.default)(this, "logger", void 0);
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
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 || (0, __context_action_logger.createLogger)(config.logLevel),
|
|
602
|
+
logLevel: config.logLevel ?? 3,
|
|
603
|
+
name: config.name || (0, __context_action_logger.getLoggerNameFromEnv)(),
|
|
604
|
+
debug: config.debug ?? (0, __context_action_logger.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
|
|
296
615
|
});
|
|
616
|
+
this.logger.trace(`${this.config.name} constructor completed`);
|
|
297
617
|
}
|
|
298
618
|
/**
|
|
619
|
+
* Register action handler with pipeline
|
|
620
|
+
* @implements action-handler
|
|
621
|
+
*
|
|
299
622
|
* Register a handler for an action in the pipeline
|
|
300
623
|
* @param action - The action name to handle
|
|
301
624
|
* @param handler - The handler function to execute
|
|
302
625
|
* @param config - Optional configuration for the handler
|
|
303
626
|
* @returns Unregister function to remove the handler
|
|
627
|
+
*
|
|
304
628
|
* @example
|
|
305
629
|
* ```typescript
|
|
306
|
-
* const unregister = actionRegister.register('
|
|
307
|
-
*
|
|
308
|
-
*
|
|
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
|
+
* );
|
|
309
644
|
*
|
|
310
645
|
* // Later, remove the handler
|
|
311
646
|
* unregister();
|
|
312
647
|
* ```
|
|
313
648
|
*/
|
|
314
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
|
+
} });
|
|
315
672
|
if (!this.pipelines.has(action)) {
|
|
316
|
-
this.
|
|
317
|
-
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)}`);
|
|
318
676
|
}
|
|
319
677
|
const pipeline = this.pipelines.get(action);
|
|
320
|
-
|
|
321
|
-
if (pipeline.
|
|
322
|
-
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`);
|
|
323
682
|
return () => {};
|
|
324
683
|
}
|
|
325
|
-
pipeline.
|
|
326
|
-
|
|
327
|
-
|
|
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
|
|
328
696
|
});
|
|
329
|
-
this.
|
|
697
|
+
this.events.emit("handler:register", {
|
|
698
|
+
action,
|
|
330
699
|
handlerId,
|
|
331
|
-
|
|
332
|
-
blocking: config.blocking ?? false
|
|
700
|
+
config: registration.config
|
|
333
701
|
});
|
|
334
|
-
this.sortPipeline(action);
|
|
335
702
|
return () => {
|
|
336
|
-
|
|
337
|
-
|
|
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`);
|
|
338
714
|
};
|
|
339
715
|
}
|
|
340
|
-
registerAtomSetter(name, setter) {
|
|
341
|
-
this.atomSetters.set(name, setter);
|
|
342
|
-
this.logger.debug(`Registered atom setter: ${name}`);
|
|
343
|
-
}
|
|
344
716
|
/**
|
|
345
|
-
*
|
|
717
|
+
* Execute the pipeline with proper flow control
|
|
346
718
|
* @internal
|
|
347
719
|
*/
|
|
348
|
-
async
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
}
|
|
359
|
-
let modifiedPayload = payload;
|
|
360
|
-
const handlers = Array.from(pipeline.values());
|
|
361
|
-
let shouldContinue = true;
|
|
362
|
-
this.logger.trace(`Executing pipeline for action: ${String(action)}`, { handlerCount: handlers.length });
|
|
363
|
-
for (const { handler, config } of handlers) {
|
|
364
|
-
if (!shouldContinue) break;
|
|
365
|
-
const controller = {
|
|
366
|
-
next: () => {
|
|
367
|
-
shouldContinue = true;
|
|
368
|
-
},
|
|
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: () => {},
|
|
369
730
|
abort: (reason) => {
|
|
370
|
-
|
|
371
|
-
|
|
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 });
|
|
372
735
|
},
|
|
373
736
|
modifyPayload: (modifier) => {
|
|
374
|
-
|
|
375
|
-
|
|
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;
|
|
376
750
|
}
|
|
377
751
|
};
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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}`);
|
|
385
764
|
}
|
|
386
|
-
this.
|
|
765
|
+
this.cleanupOneTimeHandlers(context.action, context.handlers);
|
|
387
766
|
}
|
|
388
|
-
|
|
767
|
+
/**
|
|
768
|
+
* Clean up one-time handlers after pipeline execution
|
|
769
|
+
* @internal
|
|
770
|
+
*/
|
|
771
|
+
cleanupOneTimeHandlers(action, executedHandlers) {
|
|
389
772
|
const pipeline = this.pipelines.get(action);
|
|
390
773
|
if (!pipeline) return;
|
|
391
|
-
const
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
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
|
|
395
843
|
});
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
this.logger.
|
|
399
|
-
|
|
400
|
-
|
|
844
|
+
this.pipelines.clear();
|
|
845
|
+
this.events.removeAllListeners();
|
|
846
|
+
this.logger.info(`Cleared all handlers`, {
|
|
847
|
+
actionCount,
|
|
848
|
+
totalHandlers
|
|
401
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;
|
|
402
882
|
}
|
|
403
883
|
};
|
|
404
884
|
|
|
405
885
|
//#endregion
|
|
406
|
-
|
|
407
|
-
function createAction(type, payload) {
|
|
408
|
-
return {
|
|
409
|
-
type,
|
|
410
|
-
payload
|
|
411
|
-
};
|
|
412
|
-
}
|
|
413
|
-
function isAction(action, type) {
|
|
414
|
-
return action?.type === type;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
//#endregion
|
|
886
|
+
exports.ActionGuard = ActionGuard;
|
|
418
887
|
exports.ActionRegister = ActionRegister;
|
|
419
|
-
exports.
|
|
420
|
-
exports.
|
|
421
|
-
exports.
|
|
422
|
-
exports.createAction = createAction;
|
|
423
|
-
exports.createOtelContextFromPayload = createOtelContextFromPayload;
|
|
424
|
-
exports.extractSessionIdFromPayload = extractSessionIdFromPayload;
|
|
425
|
-
exports.extractTraceIdFromPayload = extractTraceIdFromPayload;
|
|
426
|
-
exports.getLogLevelFromEnv = getLogLevelFromEnv;
|
|
427
|
-
exports.isAction = isAction;
|
|
428
|
-
exports.parseLogLevel = parseLogLevel;
|
|
888
|
+
exports.executeParallel = executeParallel;
|
|
889
|
+
exports.executeRace = executeRace;
|
|
890
|
+
exports.executeSequential = executeSequential;
|