@rotorsoft/act 1.23.37 → 1.23.39

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/dist/index.js CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  sleep,
22
22
  store,
23
23
  validate
24
- } from "./chunk-UZAUEBN2.js";
24
+ } from "./chunk-MIHIDR4K.js";
25
25
  import {
26
26
  ActorSchema,
27
27
  CausationEventSchema,
@@ -47,7 +47,7 @@ import {
47
47
  pii_split,
48
48
  pii_strip,
49
49
  sensitive
50
- } from "./chunk-HXM27ZLL.js";
50
+ } from "./chunk-TOJ5DZ5B.js";
51
51
 
52
52
  // src/signals.ts
53
53
  process.once("SIGINT", async (arg) => {
@@ -2616,13 +2616,6 @@ var EMPTY_DRAIN = {
2616
2616
  acked: [],
2617
2617
  blocked: []
2618
2618
  };
2619
- var contain = (logger, sink, fn) => {
2620
- try {
2621
- fn();
2622
- } catch (error) {
2623
- logger.error(error, `${sink} listener threw`);
2624
- }
2625
- };
2626
2619
  var DrainController = class {
2627
2620
  _armed = false;
2628
2621
  _locked = false;
@@ -2731,7 +2724,7 @@ var DrainController = class {
2731
2724
  leading,
2732
2725
  eventLimit,
2733
2726
  leaseMillis,
2734
- (b) => contain(this._deps.logger, "blocked", () => this._deps.on_blocked(b)),
2727
+ (b) => this._deps.on_blocked(b),
2735
2728
  this._deps.lane
2736
2729
  );
2737
2730
  if (!cycle) {
@@ -2749,8 +2742,7 @@ var DrainController = class {
2749
2742
  if (next !== void 0) this._defer.set(h.lease.stream, next);
2750
2743
  }
2751
2744
  if (this._defer.size > 0) this._defer.schedule();
2752
- if (acked.length)
2753
- contain(this._deps.logger, "acked", () => this._deps.on_acked(acked));
2745
+ if (acked.length) this._deps.on_acked(acked);
2754
2746
  if (closeable.length) await this._deps.on_close(closeable);
2755
2747
  this._deps.breaker.passed();
2756
2748
  const has_errors = handled.some(({ error }) => error);
@@ -3101,9 +3093,41 @@ var Act = class {
3101
3093
  /**
3102
3094
  * Emit a lifecycle event. The payload type is inferred from the event name
3103
3095
  * via {@link ActLifecycleEvents}.
3096
+ *
3097
+ * **Every listener is contained individually.** Lifecycle listeners are
3098
+ * observers — `observability.md` promises that a throwing one is "contained,
3099
+ * not fatal" — and containment belongs here rather than at each call site,
3100
+ * for two reasons the previous arrangement got wrong (#1437):
3101
+ *
3102
+ * - Wrapping the *emit* rather than each *listener* still let the first
3103
+ * thrower abort the rest: `EventEmitter.emit` stops dispatching on the
3104
+ * first exception, so a second `app.on("acked", …)` never ran. The
3105
+ * documented "the remaining sinks still fire" was false whenever an event
3106
+ * had more than one listener. Same lesson as #1423, where guarding the
3107
+ * loop instead of each callback left every later SSE subscriber unserved.
3108
+ * - Only *some* call sites wrapped at all. The drain contained `acked` and
3109
+ * `blocked`; `committed`, `forgotten` and `close()`'s `closed` did not, so
3110
+ * a throwing listener rejected `do()`, `forget()` and `close()` **after**
3111
+ * their durable work had already landed. A caller retrying a "failed"
3112
+ * `do()` writes the event twice, since the framework has no dedup by
3113
+ * design.
3114
+ *
3115
+ * Containing here makes it a property of emitting, so a new lifecycle event
3116
+ * cannot reintroduce the gap by omission.
3117
+ *
3118
+ * Uses `rawListeners` so `once` wrappers still de-register themselves, and
3119
+ * returns "had listeners" to preserve the `EventEmitter.emit` contract.
3104
3120
  */
3105
3121
  emit(event, args) {
3106
- return this._emitter.emit(event, args);
3122
+ const listeners = this._emitter.rawListeners(event);
3123
+ for (const listener of listeners) {
3124
+ try {
3125
+ listener(args);
3126
+ } catch (error) {
3127
+ this._logger.error(error, `${String(event)} listener threw`);
3128
+ }
3129
+ }
3130
+ return listeners.length > 0;
3107
3131
  }
3108
3132
  /**
3109
3133
  * The single store-failure handler: log it, then emit the `error`
@@ -3345,11 +3369,7 @@ var Act = class {
3345
3369
  with_stream_lock: (stream, work) => this._with_close_lock(stream, work)
3346
3370
  });
3347
3371
  this._forget_closed_subscriptions(result);
3348
- try {
3349
- this.emit("closed", result);
3350
- } catch (err) {
3351
- this._logger.error(err, "closed listener threw");
3352
- }
3372
+ this.emit("closed", result);
3353
3373
  },
3354
3374
  breaker: this._breaker,
3355
3375
  // Re-scope the per-lane worker's auto-start ticks so their drain