@context-action/core 0.1.0 → 0.2.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/README.md +4 -4
- package/dist/index.cjs +215 -15
- package/dist/index.d.cts +7 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +215 -15
- package/dist/index.js.map +1 -1
- package/package.json +7 -4
package/dist/index.js
CHANGED
|
@@ -235,7 +235,7 @@ var require_toPropertyKey = /* @__PURE__ */ __commonJS({ "../../node_modules/.pn
|
|
|
235
235
|
//#region ../../node_modules/.pnpm/@oxc-project+runtime@0.81.0/node_modules/@oxc-project/runtime/src/helpers/defineProperty.js
|
|
236
236
|
var require_defineProperty = /* @__PURE__ */ __commonJS({ "../../node_modules/.pnpm/@oxc-project+runtime@0.81.0/node_modules/@oxc-project/runtime/src/helpers/defineProperty.js": ((exports, module) => {
|
|
237
237
|
var toPropertyKey = require_toPropertyKey();
|
|
238
|
-
function _defineProperty$
|
|
238
|
+
function _defineProperty$3(e, r, t) {
|
|
239
239
|
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
240
240
|
value: t,
|
|
241
241
|
enumerable: !0,
|
|
@@ -243,12 +243,12 @@ var require_defineProperty = /* @__PURE__ */ __commonJS({ "../../node_modules/.p
|
|
|
243
243
|
writable: !0
|
|
244
244
|
}) : e[r] = t, e;
|
|
245
245
|
}
|
|
246
|
-
module.exports = _defineProperty$
|
|
246
|
+
module.exports = _defineProperty$3, module.exports.__esModule = true, module.exports["default"] = module.exports;
|
|
247
247
|
}) });
|
|
248
248
|
|
|
249
249
|
//#endregion
|
|
250
250
|
//#region src/action-guard.ts
|
|
251
|
-
var import_defineProperty$
|
|
251
|
+
var import_defineProperty$2 = /* @__PURE__ */ __toESM(require_defineProperty(), 1);
|
|
252
252
|
/**
|
|
253
253
|
* Action Guard system for managing action execution timing
|
|
254
254
|
* @implements action-guard
|
|
@@ -279,7 +279,7 @@ var import_defineProperty$1 = /* @__PURE__ */ __toESM(require_defineProperty(),
|
|
|
279
279
|
*/
|
|
280
280
|
var ActionGuard = class {
|
|
281
281
|
constructor() {
|
|
282
|
-
(0, import_defineProperty$
|
|
282
|
+
(0, import_defineProperty$2.default)(this, "guards", /* @__PURE__ */ new Map());
|
|
283
283
|
}
|
|
284
284
|
/**
|
|
285
285
|
* Check if action should be debounced
|
|
@@ -299,15 +299,21 @@ var ActionGuard = class {
|
|
|
299
299
|
this.guards.set(actionKey, state);
|
|
300
300
|
}
|
|
301
301
|
/** Clear any existing debounce timer to restart the delay period */
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
302
|
+
if (state.debounceTimer) {
|
|
303
|
+
clearTimeout(state.debounceTimer);
|
|
304
|
+
if (state.debounceResolve) {
|
|
305
|
+
state.debounceResolve(false);
|
|
306
|
+
state.debounceResolve = void 0;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/** Create new debounce promise */
|
|
306
310
|
return new Promise((resolve) => {
|
|
311
|
+
state.debounceResolve = resolve;
|
|
307
312
|
state.debounceTimer = setTimeout(() => {
|
|
308
|
-
/** Clean up timer
|
|
313
|
+
/** Clean up timer and resolver references */
|
|
309
314
|
state.debounceTimer = void 0;
|
|
310
|
-
|
|
315
|
+
state.debounceResolve = void 0;
|
|
316
|
+
/** Update last execution timestamp */
|
|
311
317
|
state.lastExecuted = Date.now();
|
|
312
318
|
resolve(true);
|
|
313
319
|
}, debounceMs);
|
|
@@ -363,7 +369,10 @@ var ActionGuard = class {
|
|
|
363
369
|
const state = this.guards.get(actionKey);
|
|
364
370
|
if (state) {
|
|
365
371
|
/** Clear debounce timer if active to prevent memory leaks */
|
|
366
|
-
if (state.debounceTimer)
|
|
372
|
+
if (state.debounceTimer) {
|
|
373
|
+
clearTimeout(state.debounceTimer);
|
|
374
|
+
if (state.debounceResolve) state.debounceResolve(false);
|
|
375
|
+
}
|
|
367
376
|
/** Clear throttle timer if active to prevent memory leaks */
|
|
368
377
|
if (state.throttleTimer) clearTimeout(state.throttleTimer);
|
|
369
378
|
/** Remove guard state from memory */
|
|
@@ -378,7 +387,10 @@ var ActionGuard = class {
|
|
|
378
387
|
/** This prevents memory leaks when clearing the entire guard system */
|
|
379
388
|
for (const [, state] of this.guards) {
|
|
380
389
|
/** Clear any active debounce timers */
|
|
381
|
-
if (state.debounceTimer)
|
|
390
|
+
if (state.debounceTimer) {
|
|
391
|
+
clearTimeout(state.debounceTimer);
|
|
392
|
+
if (state.debounceResolve) state.debounceResolve(false);
|
|
393
|
+
}
|
|
382
394
|
/** Clear any active throttle timers */
|
|
383
395
|
if (state.throttleTimer) clearTimeout(state.throttleTimer);
|
|
384
396
|
}
|
|
@@ -400,6 +412,112 @@ var ActionGuard = class {
|
|
|
400
412
|
}
|
|
401
413
|
};
|
|
402
414
|
|
|
415
|
+
//#endregion
|
|
416
|
+
//#region src/concurrency/OperationQueue.ts
|
|
417
|
+
var import_defineProperty$1 = /* @__PURE__ */ __toESM(require_defineProperty(), 1);
|
|
418
|
+
/**
|
|
419
|
+
* 작업 큐 관리자
|
|
420
|
+
*
|
|
421
|
+
* 핵심 기능:
|
|
422
|
+
* 1. 작업 직렬화 - 모든 작업을 순서대로 실행
|
|
423
|
+
* 2. 우선순위 지원 - 중요한 작업 우선 처리
|
|
424
|
+
* 3. 에러 처리 - 개별 작업 실패가 전체에 영향 주지 않음
|
|
425
|
+
* 4. 메모리 관리 - 완료된 작업 자동 정리
|
|
426
|
+
*/
|
|
427
|
+
var OperationQueue = class {
|
|
428
|
+
constructor(name = "OperationQueue") {
|
|
429
|
+
this.name = name;
|
|
430
|
+
(0, import_defineProperty$1.default)(this, "queue", []);
|
|
431
|
+
(0, import_defineProperty$1.default)(this, "isProcessing", false);
|
|
432
|
+
(0, import_defineProperty$1.default)(this, "operationCounter", 0);
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* 작업을 큐에 추가하고 실행 결과를 반환
|
|
436
|
+
*
|
|
437
|
+
* @param operation 실행할 작업
|
|
438
|
+
* @param priority 우선순위 (높을수록 먼저 실행)
|
|
439
|
+
* @returns Promise로 래핑된 작업 결과
|
|
440
|
+
*/
|
|
441
|
+
enqueue(operation, priority = 0) {
|
|
442
|
+
return new Promise((resolve, reject) => {
|
|
443
|
+
const queuedOperation = {
|
|
444
|
+
id: `${this.name}-${++this.operationCounter}`,
|
|
445
|
+
operation,
|
|
446
|
+
resolve,
|
|
447
|
+
reject,
|
|
448
|
+
priority,
|
|
449
|
+
timestamp: Date.now()
|
|
450
|
+
};
|
|
451
|
+
let insertIndex = this.queue.length;
|
|
452
|
+
for (let i = 0; i < this.queue.length; i++) if ((this.queue[i].priority || 0) < priority) {
|
|
453
|
+
insertIndex = i;
|
|
454
|
+
break;
|
|
455
|
+
}
|
|
456
|
+
this.queue.splice(insertIndex, 0, queuedOperation);
|
|
457
|
+
this.processQueue();
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* 큐 처리 메인 로직
|
|
462
|
+
*
|
|
463
|
+
* 한 번에 하나씩 순서대로 작업을 실행하여 동시성 문제 방지
|
|
464
|
+
*/
|
|
465
|
+
async processQueue() {
|
|
466
|
+
if (this.isProcessing || this.queue.length === 0) return;
|
|
467
|
+
this.isProcessing = true;
|
|
468
|
+
try {
|
|
469
|
+
while (this.queue.length > 0) {
|
|
470
|
+
const operation = this.queue.shift();
|
|
471
|
+
try {
|
|
472
|
+
const result = await Promise.resolve(operation.operation());
|
|
473
|
+
operation.resolve(result);
|
|
474
|
+
} catch (error) {
|
|
475
|
+
operation.reject(error);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
} finally {
|
|
479
|
+
this.isProcessing = false;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* 현재 큐 상태 조회 (디버깅용)
|
|
484
|
+
*/
|
|
485
|
+
getQueueInfo() {
|
|
486
|
+
return {
|
|
487
|
+
name: this.name,
|
|
488
|
+
queueLength: this.queue.length,
|
|
489
|
+
isProcessing: this.isProcessing,
|
|
490
|
+
operations: this.queue.map((op) => ({
|
|
491
|
+
id: op.id,
|
|
492
|
+
priority: op.priority,
|
|
493
|
+
timestamp: op.timestamp
|
|
494
|
+
}))
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* 큐 비우기 (테스트용)
|
|
499
|
+
*/
|
|
500
|
+
clear() {
|
|
501
|
+
this.queue.forEach((operation) => {
|
|
502
|
+
operation.reject(/* @__PURE__ */ new Error("Queue cleared"));
|
|
503
|
+
});
|
|
504
|
+
this.queue = [];
|
|
505
|
+
this.isProcessing = false;
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* 큐 크기 조회
|
|
509
|
+
*/
|
|
510
|
+
get size() {
|
|
511
|
+
return this.queue.length;
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* 처리 중 여부 조회
|
|
515
|
+
*/
|
|
516
|
+
get processing() {
|
|
517
|
+
return this.isProcessing;
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
|
|
403
521
|
//#endregion
|
|
404
522
|
//#region src/ActionRegister.ts
|
|
405
523
|
var import_defineProperty = /* @__PURE__ */ __toESM(require_defineProperty(), 1);
|
|
@@ -425,7 +543,7 @@ var import_defineProperty = /* @__PURE__ */ __toESM(require_defineProperty(), 1)
|
|
|
425
543
|
* // 핸들러 등록
|
|
426
544
|
* register.register('updateUser', ({ id, name }, controller) => {
|
|
427
545
|
* userStore.setValue({ id, name });
|
|
428
|
-
*
|
|
546
|
+
* // 핸들러가 자동으로 다음 핸들러로 진행
|
|
429
547
|
* }, { priority: 10 });
|
|
430
548
|
*
|
|
431
549
|
* // 액션 디스패치
|
|
@@ -442,18 +560,93 @@ var ActionRegister = class {
|
|
|
442
560
|
(0, import_defineProperty.default)(this, "name", void 0);
|
|
443
561
|
(0, import_defineProperty.default)(this, "registryConfig", void 0);
|
|
444
562
|
(0, import_defineProperty.default)(this, "executionStats", /* @__PURE__ */ new Map());
|
|
563
|
+
(0, import_defineProperty.default)(this, "registrationQueue", void 0);
|
|
564
|
+
(0, import_defineProperty.default)(this, "dispatchQueue", void 0);
|
|
445
565
|
this.name = config.name || "ActionRegister";
|
|
446
566
|
this.registryConfig = config.registry;
|
|
447
567
|
this.actionGuard = new ActionGuard();
|
|
568
|
+
this.registrationQueue = new OperationQueue(`${this.name}-Registration`);
|
|
569
|
+
this.dispatchQueue = new OperationQueue(`${this.name}-Dispatch`);
|
|
448
570
|
if (this.registryConfig?.defaultExecutionMode) this.executionMode = this.registryConfig.defaultExecutionMode;
|
|
449
571
|
if (this.registryConfig?.debug && process.env.NODE_ENV === "development") console.log(`🎯 ActionRegister created: ${this.name}`, {
|
|
450
572
|
defaultExecutionMode: this.executionMode,
|
|
451
573
|
maxHandlers: this.registryConfig.maxHandlers,
|
|
452
|
-
autoCleanup: this.registryConfig.autoCleanup ?? true
|
|
574
|
+
autoCleanup: this.registryConfig.autoCleanup ?? true,
|
|
575
|
+
concurrencyProtection: true
|
|
453
576
|
});
|
|
454
577
|
}
|
|
455
578
|
register(action, handler, config = {}) {
|
|
456
579
|
const handlerId = config.id || `handler_${++this.handlerCounter}_${Math.random().toString(36).substr(2, 5)}`;
|
|
580
|
+
const unregisterFn = this._performRegistrationSync(action, handler, config, handlerId);
|
|
581
|
+
return unregisterFn;
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* 🆕 동기적 등록 수행 (개선된 버전)
|
|
585
|
+
*/
|
|
586
|
+
_performRegistrationSync(action, handler, config, handlerId) {
|
|
587
|
+
const registration = {
|
|
588
|
+
handler,
|
|
589
|
+
config: {
|
|
590
|
+
priority: config.priority ?? 0,
|
|
591
|
+
id: handlerId,
|
|
592
|
+
blocking: config.blocking ?? false,
|
|
593
|
+
once: config.once ?? false,
|
|
594
|
+
condition: config.condition || (() => true),
|
|
595
|
+
debounce: config.debounce ?? void 0,
|
|
596
|
+
throttle: config.throttle ?? void 0,
|
|
597
|
+
validation: config.validation ?? void 0,
|
|
598
|
+
middleware: config.middleware ?? false,
|
|
599
|
+
tags: config.tags ?? [],
|
|
600
|
+
category: config.category ?? void 0,
|
|
601
|
+
description: config.description ?? void 0,
|
|
602
|
+
version: config.version ?? void 0,
|
|
603
|
+
returnType: config.returnType ?? "value",
|
|
604
|
+
timeout: config.timeout ?? void 0,
|
|
605
|
+
retries: config.retries ?? 0,
|
|
606
|
+
dependencies: config.dependencies ?? [],
|
|
607
|
+
conflicts: config.conflicts ?? [],
|
|
608
|
+
environment: config.environment ?? void 0,
|
|
609
|
+
feature: config.feature ?? void 0,
|
|
610
|
+
metrics: config.metrics ?? {
|
|
611
|
+
collectTiming: false,
|
|
612
|
+
collectErrors: false,
|
|
613
|
+
customMetrics: {}
|
|
614
|
+
},
|
|
615
|
+
metadata: config.metadata ?? {}
|
|
616
|
+
},
|
|
617
|
+
id: handlerId
|
|
618
|
+
};
|
|
619
|
+
if (!this.pipelines.has(action)) this.pipelines.set(action, []);
|
|
620
|
+
const pipeline = this.pipelines.get(action);
|
|
621
|
+
const existingIndex = pipeline.findIndex((reg) => reg.id === handlerId);
|
|
622
|
+
if (existingIndex !== -1) return () => {};
|
|
623
|
+
if (this.registryConfig?.maxHandlers && pipeline.length >= this.registryConfig.maxHandlers) throw new Error(`Maximum number of handlers (${this.registryConfig.maxHandlers}) reached for action '${String(action)}' in registry '${this.name}'`);
|
|
624
|
+
pipeline.push(registration);
|
|
625
|
+
pipeline.sort((a, b) => b.config.priority - a.config.priority);
|
|
626
|
+
if (this.registryConfig?.debug && process.env.NODE_ENV === "development") console.log(`🎯 Handler registered: ${String(action)}`, {
|
|
627
|
+
handlerId,
|
|
628
|
+
priority: config.priority,
|
|
629
|
+
tags: config.tags,
|
|
630
|
+
category: config.category,
|
|
631
|
+
totalHandlers: pipeline.length,
|
|
632
|
+
registry: this.name
|
|
633
|
+
});
|
|
634
|
+
return () => {
|
|
635
|
+
const index = pipeline.findIndex((reg) => reg.id === handlerId && reg === registration);
|
|
636
|
+
if (index !== -1) {
|
|
637
|
+
pipeline.splice(index, 1);
|
|
638
|
+
if (this.registryConfig?.debug && process.env.NODE_ENV === "development") console.log(`🎯 Handler unregistered: ${String(action)}`, {
|
|
639
|
+
handlerId,
|
|
640
|
+
remainingHandlers: pipeline.length,
|
|
641
|
+
registry: this.name
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* 🆕 실제 등록 작업 수행 (큐에서 호출됨)
|
|
648
|
+
*/
|
|
649
|
+
_performRegistration(action, handler, config, handlerId) {
|
|
457
650
|
const registration = {
|
|
458
651
|
handler,
|
|
459
652
|
config: {
|
|
@@ -514,6 +707,14 @@ var ActionRegister = class {
|
|
|
514
707
|
};
|
|
515
708
|
}
|
|
516
709
|
async dispatch(action, payload, options) {
|
|
710
|
+
return this.dispatchQueue.enqueue(async () => {
|
|
711
|
+
return this._performDispatch(action, payload, options);
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* 🆕 실제 디스패치 작업 수행 (큐에서 호출됨)
|
|
716
|
+
*/
|
|
717
|
+
async _performDispatch(action, payload, options) {
|
|
517
718
|
let autoAbortController;
|
|
518
719
|
let effectiveSignal = options?.signal;
|
|
519
720
|
if (options?.autoAbort?.enabled) {
|
|
@@ -811,7 +1012,6 @@ var ActionRegister = class {
|
|
|
811
1012
|
async executePipeline(context, autoAbortController, autoAbortOptions) {
|
|
812
1013
|
const createController = (_registration, _index) => {
|
|
813
1014
|
return {
|
|
814
|
-
next: () => {},
|
|
815
1015
|
abort: (reason) => {
|
|
816
1016
|
context.aborted = true;
|
|
817
1017
|
context.abortReason = reason;
|