abxbus 2.4.32 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +74 -51
  2. package/dist/cjs/BaseEvent.d.ts +46 -55
  3. package/dist/cjs/BaseEvent.js +350 -169
  4. package/dist/cjs/BaseEvent.js.map +3 -3
  5. package/dist/cjs/EventBus.d.ts +8 -1
  6. package/dist/cjs/EventBus.js +153 -85
  7. package/dist/cjs/EventBus.js.map +2 -2
  8. package/dist/cjs/EventHandler.d.ts +3 -3
  9. package/dist/cjs/EventHandler.js.map +1 -1
  10. package/dist/cjs/EventResult.js +16 -22
  11. package/dist/cjs/EventResult.js.map +2 -2
  12. package/dist/cjs/LockManager.d.ts +1 -0
  13. package/dist/cjs/LockManager.js +4 -1
  14. package/dist/cjs/LockManager.js.map +2 -2
  15. package/dist/cjs/events_suck.js +1 -1
  16. package/dist/cjs/events_suck.js.map +2 -2
  17. package/dist/cjs/index.d.ts +1 -0
  18. package/dist/cjs/index.js.map +2 -2
  19. package/dist/cjs/timing.js +1 -1
  20. package/dist/cjs/timing.js.map +2 -2
  21. package/dist/esm/BaseEvent.js +351 -170
  22. package/dist/esm/BaseEvent.js.map +3 -3
  23. package/dist/esm/EventBus.js +153 -85
  24. package/dist/esm/EventBus.js.map +2 -2
  25. package/dist/esm/EventHandler.js.map +1 -1
  26. package/dist/esm/EventResult.js +16 -22
  27. package/dist/esm/EventResult.js.map +2 -2
  28. package/dist/esm/LockManager.js +4 -1
  29. package/dist/esm/LockManager.js.map +2 -2
  30. package/dist/esm/events_suck.js +1 -1
  31. package/dist/esm/events_suck.js.map +2 -2
  32. package/dist/esm/index.js.map +2 -2
  33. package/dist/esm/timing.js +1 -1
  34. package/dist/esm/timing.js.map +2 -2
  35. package/dist/types/BaseEvent.d.ts +46 -55
  36. package/dist/types/EventBus.d.ts +8 -1
  37. package/dist/types/EventHandler.d.ts +3 -3
  38. package/dist/types/LockManager.d.ts +1 -0
  39. package/dist/types/index.d.ts +1 -0
  40. package/package.json +4 -3
  41. package/src/BaseEvent.ts +456 -219
  42. package/src/EventBus.ts +186 -99
  43. package/src/EventHandler.ts +3 -3
  44. package/src/EventResult.ts +18 -22
  45. package/src/LockManager.ts +5 -1
  46. package/src/events_suck.ts +1 -1
  47. package/src/index.ts +1 -0
  48. package/src/timing.ts +1 -1
  49. package/dist/cjs/base_event.d.ts +0 -211
  50. package/dist/cjs/bridge_jsonl.d.ts +0 -26
  51. package/dist/cjs/bridge_nats.d.ts +0 -20
  52. package/dist/cjs/bridge_postgres.d.ts +0 -31
  53. package/dist/cjs/bridge_redis.d.ts +0 -34
  54. package/dist/cjs/bridge_sqlite.d.ts +0 -30
  55. package/dist/cjs/event_bus.d.ts +0 -125
  56. package/dist/cjs/event_handler.d.ts +0 -139
  57. package/dist/cjs/event_history.d.ts +0 -45
  58. package/dist/cjs/event_result.d.ts +0 -86
  59. package/dist/cjs/lock_manager.d.ts +0 -70
  60. package/dist/types/base_event.d.ts +0 -211
  61. package/dist/types/bridge_jsonl.d.ts +0 -26
  62. package/dist/types/bridge_nats.d.ts +0 -20
  63. package/dist/types/bridge_postgres.d.ts +0 -31
  64. package/dist/types/bridge_redis.d.ts +0 -34
  65. package/dist/types/bridge_sqlite.d.ts +0 -30
  66. package/dist/types/event_bus.d.ts +0 -125
  67. package/dist/types/event_handler.d.ts +0 -139
  68. package/dist/types/event_history.d.ts +0 -45
  69. package/dist/types/event_result.d.ts +0 -86
  70. package/dist/types/lock_manager.d.ts +0 -70
@@ -141,6 +141,7 @@ class EventBus {
141
141
  find_waiters;
142
142
  // set of EphemeralFindEventHandler objects that are waiting for a matching future event
143
143
  middlewares;
144
+ destroyed;
144
145
  static normalizeMiddlewares(middlewares) {
145
146
  const normalized = [];
146
147
  for (const middleware of middlewares ?? []) {
@@ -162,9 +163,9 @@ class EventBus {
162
163
  this.event_handler_concurrency = options.event_handler_concurrency ?? "serial";
163
164
  this.event_handler_completion = options.event_handler_completion ?? "all";
164
165
  this.event_handler_detect_file_paths = options.event_handler_detect_file_paths ?? true;
165
- this.event_timeout = options.event_timeout === void 0 ? 60 : options.event_timeout;
166
- this.event_handler_slow_timeout = options.event_handler_slow_timeout === void 0 ? 30 : options.event_handler_slow_timeout;
167
- this.event_slow_timeout = options.event_slow_timeout === void 0 ? 300 : options.event_slow_timeout;
166
+ this.event_timeout = options.event_timeout ?? 60;
167
+ this.event_handler_slow_timeout = options.event_handler_slow_timeout ?? 30;
168
+ this.event_slow_timeout = options.event_slow_timeout ?? 300;
168
169
  this.runloop_running = false;
169
170
  this.handlers = /* @__PURE__ */ new Map();
170
171
  this.handlers_by_key = /* @__PURE__ */ new Map();
@@ -177,6 +178,7 @@ class EventBus {
177
178
  this.in_flight_event_ids = /* @__PURE__ */ new Set();
178
179
  this.locks = new LockManager(this);
179
180
  this.middlewares = EventBus.normalizeMiddlewares(options.middlewares);
181
+ this.destroyed = false;
180
182
  this.all_instances.add(this);
181
183
  this.dispatch = this.dispatch.bind(this);
182
184
  this.emit = this.emit.bind(this);
@@ -263,7 +265,7 @@ class EventBus {
263
265
  }
264
266
  async _runHandlersWithTimeout(event, pending_entries, event_timeout, fn) {
265
267
  try {
266
- if (event_timeout === null || pending_entries.length === 0) {
268
+ if (event_timeout === null || event_timeout <= 0 || pending_entries.length === 0) {
267
269
  await fn();
268
270
  } else {
269
271
  await _runWithTimeout(event_timeout, () => this._createEventTimeoutError(event, pending_entries, event_timeout), fn);
@@ -400,10 +402,11 @@ class EventBus {
400
402
  options.event_handler_detect_file_paths = record.event_handler_detect_file_paths;
401
403
  }
402
404
  const bus = new EventBus(name, options);
403
- if (!record.handlers || typeof record.handlers !== "object" || Array.isArray(record.handlers)) {
405
+ const raw_handlers = record.handlers ?? {};
406
+ if (!raw_handlers || typeof raw_handlers !== "object" || Array.isArray(raw_handlers)) {
404
407
  throw new Error("EventBus.fromJSON(data) requires handlers as an id-keyed object");
405
408
  }
406
- for (const [handler_id, payload] of Object.entries(record.handlers)) {
409
+ for (const [handler_id, payload] of Object.entries(raw_handlers)) {
407
410
  if (!payload || typeof payload !== "object") {
408
411
  continue;
409
412
  }
@@ -416,11 +419,12 @@ class EventBus {
416
419
  );
417
420
  bus.handlers.set(parsed.id, parsed);
418
421
  }
419
- if (!record.handlers_by_key || typeof record.handlers_by_key !== "object" || Array.isArray(record.handlers_by_key)) {
422
+ const raw_handlers_by_key = record.handlers_by_key ?? {};
423
+ if (!raw_handlers_by_key || typeof raw_handlers_by_key !== "object" || Array.isArray(raw_handlers_by_key)) {
420
424
  throw new Error("EventBus.fromJSON(data) requires handlers_by_key as an object");
421
425
  }
422
426
  bus.handlers_by_key.clear();
423
- for (const [raw_key, raw_ids] of Object.entries(record.handlers_by_key)) {
427
+ for (const [raw_key, raw_ids] of Object.entries(raw_handlers_by_key)) {
424
428
  if (!Array.isArray(raw_ids)) {
425
429
  continue;
426
430
  }
@@ -493,21 +497,49 @@ class EventBus {
493
497
  removeEventFromHistory(event_id) {
494
498
  return this.event_history.delete(event_id);
495
499
  }
496
- // destroy the event bus and all its state to allow for garbage collection
497
- destroy() {
498
- this.all_instances.discard(this);
499
- this.handlers.clear();
500
- this.handlers_by_key.clear();
501
- for (const event of this.event_history.values()) {
502
- event._gc();
500
+ _raiseIfDestroyed() {
501
+ if (this.destroyed) {
502
+ throw new Error(`${this.toString()} has been destroyed and cannot be used again`);
503
503
  }
504
- this.event_history.clear();
505
- this.pending_event_queue.length = 0;
506
- this.in_flight_event_ids.clear();
507
- this.find_waiters.clear();
508
- this.locks.clear();
504
+ }
505
+ destroy(clear_or_options = true) {
506
+ const clear = typeof clear_or_options === "object" && clear_or_options !== null ? clear_or_options.clear ?? true : clear_or_options;
507
+ if (this.destroyed) {
508
+ if (clear) {
509
+ this.handlers.clear();
510
+ this.handlers_by_key.clear();
511
+ this.event_history.clear();
512
+ this.middlewares.length = 0;
513
+ }
514
+ return Promise.resolve();
515
+ }
516
+ const finish = () => {
517
+ this.destroyed = true;
518
+ this.all_instances.discard(this);
519
+ this.runloop_running = false;
520
+ for (const waiter of Array.from(this.find_waiters)) {
521
+ if (waiter.timeout_id) {
522
+ clearTimeout(waiter.timeout_id);
523
+ }
524
+ this.find_waiters.delete(waiter);
525
+ waiter.resolve(null);
526
+ }
527
+ this.pending_event_queue.length = 0;
528
+ this.in_flight_event_ids.clear();
529
+ this.locks.clear();
530
+ if (!clear) {
531
+ return;
532
+ }
533
+ this.handlers.clear();
534
+ this.handlers_by_key.clear();
535
+ this.event_history.clear();
536
+ this.middlewares.length = 0;
537
+ };
538
+ finish();
539
+ return Promise.resolve();
509
540
  }
510
541
  on(event_pattern, handler, options = {}) {
542
+ this._raiseIfDestroyed();
511
543
  const normalized_key = normalizeEventPattern(event_pattern);
512
544
  const handler_name = EventHandler.handlerNameFromCallable(handler);
513
545
  const handler_entry = new EventHandler({
@@ -532,6 +564,7 @@ class EventBus {
532
564
  return handler_entry;
533
565
  }
534
566
  off(event_pattern, handler) {
567
+ this._raiseIfDestroyed();
535
568
  const normalized_key = normalizeEventPattern(event_pattern);
536
569
  if (typeof handler === "object" && handler instanceof EventHandler && handler.id !== void 0) {
537
570
  handler = handler.id;
@@ -552,7 +585,12 @@ class EventBus {
552
585
  }
553
586
  }
554
587
  emit(event) {
588
+ this._raiseIfDestroyed();
555
589
  const original_event = event._event_original ?? event;
590
+ const current_result = this.locks._getRawActiveHandlerResultForCurrentAsyncContext();
591
+ if (current_result && current_result.status !== "pending" && current_result.status !== "started" && (current_result.error instanceof EventHandlerTimeoutError || current_result.error instanceof EventHandlerCancelledError || current_result.error instanceof EventHandlerAbortedError)) {
592
+ return original_event;
593
+ }
556
594
  if (!original_event.event_bus) {
557
595
  original_event.event_bus = this;
558
596
  }
@@ -590,7 +628,11 @@ class EventBus {
590
628
  this._resolveFindWaiters(original_event);
591
629
  original_event.event_pending_bus_count += 1;
592
630
  this.pending_event_queue.push(original_event);
593
- this._startRunloop();
631
+ if (this.locks.getLockForEvent(original_event) === null) {
632
+ this._startParallelEventTaskFromQueue(original_event);
633
+ } else {
634
+ this._startRunloop();
635
+ }
594
636
  return this._getEventProxyScopedToThisBus(original_event);
595
637
  }
596
638
  // alias for emit
@@ -612,6 +654,7 @@ class EventBus {
612
654
  return results.length > 0 ? results[0] : null;
613
655
  }
614
656
  async filter(event_pattern, where_or_options = {}, maybe_options = {}) {
657
+ this._raiseIfDestroyed();
615
658
  const where = typeof where_or_options === "function" ? where_or_options : () => true;
616
659
  const options = typeof where_or_options === "function" ? maybe_options : where_or_options;
617
660
  const matches = await this.event_history.filter(event_pattern, where, {
@@ -642,6 +685,7 @@ class EventBus {
642
685
  });
643
686
  }
644
687
  async waitUntilIdle(timeout = null) {
688
+ this._raiseIfDestroyed();
645
689
  return await this.locks.waitForIdle(timeout);
646
690
  }
647
691
  // Weak idle check: only checks if handlers are idle, doesnt check that the queue is empty
@@ -692,6 +736,7 @@ class EventBus {
692
736
  return this.event_history.get(event_id) ?? this.all_instances.findEventById(event_id);
693
737
  }
694
738
  _startRunloop() {
739
+ this._raiseIfDestroyed();
695
740
  if (this.runloop_running) {
696
741
  return;
697
742
  }
@@ -700,6 +745,21 @@ class EventBus {
700
745
  void this._runloop();
701
746
  });
702
747
  }
748
+ _startParallelEventTaskFromQueue(event) {
749
+ if (this.in_flight_event_ids.has(event.event_id)) {
750
+ return;
751
+ }
752
+ const queue_index = this.pending_event_queue.indexOf(event);
753
+ if (queue_index >= 0) {
754
+ this.pending_event_queue.splice(queue_index, 1);
755
+ } else if (event.event_status === "completed") {
756
+ return;
757
+ }
758
+ this.in_flight_event_ids.add(event.event_id);
759
+ this.scheduleMicrotask(() => {
760
+ void this._processEvent(event);
761
+ });
762
+ }
703
763
  // schedule the processing of an event on the event bus by its normal _runloop
704
764
  // optionally using a pre-acquired lock if we're inside handling of a parent event
705
765
  async _processEvent(event, options = {}) {
@@ -713,6 +773,7 @@ class EventBus {
713
773
  event._markStarted();
714
774
  pending_entries = event._createPendingHandlerResults(this);
715
775
  const resolved_event_timeout = event.event_timeout ?? this.event_timeout;
776
+ const resolved_event_slow_timeout = event.event_slow_timeout ?? this.event_slow_timeout;
716
777
  if (this.middlewares.length > 0) {
717
778
  for (const entry of pending_entries) {
718
779
  await this._onEventResultChange(scoped_event, entry.result, "pending");
@@ -724,7 +785,10 @@ class EventBus {
724
785
  event,
725
786
  pending_entries,
726
787
  resolved_event_timeout,
727
- () => _runWithSlowMonitor(event._createSlowEventWarningTimer(), () => scoped_event._runHandlers(pending_entries))
788
+ () => _runWithSlowMonitor(
789
+ event._createSlowEventWarningTimer(resolved_event_slow_timeout, this.name),
790
+ () => scoped_event._runHandlers(pending_entries)
791
+ )
728
792
  ),
729
793
  options
730
794
  );
@@ -737,7 +801,7 @@ class EventBus {
737
801
  this.locks._notifyIdleListeners();
738
802
  }
739
803
  }
740
- // Called when a handler does `await child.done()` — processes the child event
804
+ // Called when a handler does `await child.now()` — processes the child event
741
805
  // immediately ("queue-jump") instead of waiting for the _runloop to pick it up.
742
806
  //
743
807
  // Yield-and-reacquire: if the calling handler holds a handler concurrency lock,
@@ -750,9 +814,9 @@ class EventBus {
750
814
  const currently_active_event_result = proxy_result ?? this.locks._getActiveHandlerResultForCurrentAsyncContext();
751
815
  if (!currently_active_event_result) {
752
816
  const queue_index = this.pending_event_queue.indexOf(original_event);
753
- const can_process_now = queue_index === 0 && !this.locks._isPaused() && !this.in_flight_event_ids.has(original_event.event_id) && !this._hasProcessedEvent(original_event);
754
- if (can_process_now) {
755
- const event_lock = this.locks.getLockForEvent(original_event);
817
+ const event_lock = this.locks.getLockForEvent(original_event);
818
+ const can_process_queue_head_normally = queue_index === 0 && !this.locks._isPaused() && !this.in_flight_event_ids.has(original_event.event_id) && !this._hasProcessedEvent(original_event) && (event_lock === null || event_lock.in_use === 0);
819
+ if (can_process_queue_head_normally) {
756
820
  let pre_acquired_lock = null;
757
821
  if (event_lock) {
758
822
  await event_lock.acquire();
@@ -760,8 +824,7 @@ class EventBus {
760
824
  }
761
825
  const queue_head = this.pending_event_queue[0];
762
826
  const queue_head_original = queue_head?._event_original ?? queue_head;
763
- const still_can_process_now = queue_head_original === original_event && !this.locks._isPaused() && !this.in_flight_event_ids.has(original_event.event_id) && !this._hasProcessedEvent(original_event);
764
- if (still_can_process_now) {
827
+ if (queue_head_original === original_event && !this.locks._isPaused() && !this.in_flight_event_ids.has(original_event.event_id) && !this._hasProcessedEvent(original_event)) {
765
828
  this.pending_event_queue.shift();
766
829
  this.in_flight_event_ids.add(original_event.event_id);
767
830
  await this._processEvent(original_event, {
@@ -769,15 +832,13 @@ class EventBus {
769
832
  pre_acquired_lock
770
833
  });
771
834
  if (original_event.event_status !== "completed") {
772
- await original_event.eventCompleted();
835
+ await this._processEventImmediatelyAcrossBuses(original_event);
773
836
  }
774
837
  return event;
775
838
  }
776
- if (pre_acquired_lock) {
777
- pre_acquired_lock.release();
778
- }
839
+ pre_acquired_lock?.release();
779
840
  }
780
- await original_event.eventCompleted();
841
+ await this._processEventImmediatelyAcrossBuses(original_event);
781
842
  return event;
782
843
  }
783
844
  const active_parent = currently_active_event_result.event._event_original ?? currently_active_event_result.event;
@@ -801,65 +862,68 @@ class EventBus {
801
862
  // Processes a queue-jumped event across all buses that have it emitted.
802
863
  // Called from _processEventImmediately after the parent handler's lock has been yielded.
803
864
  async _processEventImmediatelyAcrossBuses(event) {
804
- const ordered = [];
805
- const seen = /* @__PURE__ */ new Set();
806
- const event_path = Array.isArray(event.event_path) ? event.event_path : [];
807
- for (const label of event_path) {
808
- for (const bus of this.all_instances) {
809
- if (bus.label !== label) {
810
- continue;
811
- }
812
- if (!bus.event_history.has(event.event_id)) {
813
- continue;
814
- }
815
- if (bus._hasProcessedEvent(event)) {
816
- continue;
817
- }
818
- if (!seen.has(bus)) {
819
- ordered.push(bus);
820
- seen.add(bus);
821
- }
822
- }
823
- }
824
- if (!seen.has(this) && this.event_history.has(event.event_id)) {
825
- ordered.push(this);
826
- }
827
- if (ordered.length === 0) {
828
- await event.eventCompleted();
829
- return;
830
- }
831
865
  const initiating_event_lock = this.locks.getLockForEvent(event);
832
- const pause_releases = [];
833
- try {
834
- for (const bus of ordered) {
835
- if (bus !== this) {
836
- pause_releases.push(bus.locks._requestRunloopPause());
866
+ for (; ; ) {
867
+ const ordered = [];
868
+ const seen = /* @__PURE__ */ new Set();
869
+ const event_path = Array.isArray(event.event_path) ? event.event_path : [];
870
+ for (const label of event_path) {
871
+ for (const bus of this.all_instances) {
872
+ if (bus.label !== label) {
873
+ continue;
874
+ }
875
+ if (!bus.event_history.has(event.event_id)) {
876
+ continue;
877
+ }
878
+ if (bus._hasProcessedEvent(event)) {
879
+ continue;
880
+ }
881
+ if (!seen.has(bus)) {
882
+ ordered.push(bus);
883
+ seen.add(bus);
884
+ }
837
885
  }
838
886
  }
839
- for (const bus of ordered) {
840
- const index = bus.pending_event_queue.indexOf(event);
841
- if (index >= 0) {
842
- bus.pending_event_queue.splice(index, 1);
887
+ if (!seen.has(this) && this.event_history.has(event.event_id) && !this._hasProcessedEvent(event)) {
888
+ ordered.push(this);
889
+ }
890
+ const pause_releases = [];
891
+ let processed_bus = false;
892
+ try {
893
+ for (const bus of ordered) {
894
+ if (bus !== this) {
895
+ pause_releases.push(bus.locks._requestRunloopPause());
896
+ }
843
897
  }
844
- if (bus._hasProcessedEvent(event)) {
845
- continue;
898
+ for (const bus of ordered) {
899
+ const index = bus.pending_event_queue.indexOf(event);
900
+ if (index >= 0) {
901
+ bus.pending_event_queue.splice(index, 1);
902
+ }
903
+ if (bus._hasProcessedEvent(event)) {
904
+ continue;
905
+ }
906
+ if (bus.in_flight_event_ids.has(event.event_id)) {
907
+ continue;
908
+ }
909
+ bus.in_flight_event_ids.add(event.event_id);
910
+ processed_bus = true;
911
+ const bus_event_lock = bus.locks.getLockForEvent(event);
912
+ const should_bypass_event_lock = bus === this || initiating_event_lock !== null && bus_event_lock === initiating_event_lock;
913
+ await bus._processEvent(event, {
914
+ bypass_event_locks: should_bypass_event_lock
915
+ });
846
916
  }
847
- if (bus.in_flight_event_ids.has(event.event_id)) {
848
- continue;
917
+ } finally {
918
+ for (const release of pause_releases) {
919
+ release();
849
920
  }
850
- bus.in_flight_event_ids.add(event.event_id);
851
- const bus_event_lock = bus.locks.getLockForEvent(event);
852
- const should_bypass_event_lock = bus === this || initiating_event_lock !== null && bus_event_lock === initiating_event_lock;
853
- await bus._processEvent(event, {
854
- bypass_event_locks: should_bypass_event_lock
855
- });
856
921
  }
857
- if (event.event_status !== "completed") {
858
- await event.eventCompleted();
922
+ if (event.event_status === "completed") {
923
+ return;
859
924
  }
860
- } finally {
861
- for (const release of pause_releases) {
862
- release();
925
+ if (!processed_bus) {
926
+ await new Promise((resolve) => setTimeout(resolve, 1));
863
927
  }
864
928
  }
865
929
  }
@@ -944,7 +1008,11 @@ class EventBus {
944
1008
  if (prop === "dispatch" || prop === "emit") {
945
1009
  const emit_child_event = (child_event) => {
946
1010
  const original_child = child_event._event_original ?? child_event;
947
- if (handler_result) {
1011
+ const handler_result_is_terminal = handler_result && handler_result.status !== "pending" && handler_result.status !== "started";
1012
+ if (handler_result_is_terminal && (handler_result.error instanceof EventHandlerTimeoutError || handler_result.error instanceof EventHandlerCancelledError || handler_result.error instanceof EventHandlerAbortedError)) {
1013
+ return original_child;
1014
+ }
1015
+ if (handler_result && !handler_result_is_terminal) {
948
1016
  handler_result._linkEmittedChildEvent(original_child);
949
1017
  } else if (!original_child.event_parent_id && original_child.event_id !== parent_event_id) {
950
1018
  original_child.event_parent_id = parent_event_id;