@rotorsoft/act 1.33.6 → 1.33.8

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
@@ -1174,8 +1174,6 @@ var CorrelateCycle = class {
1174
1174
  _static_targets;
1175
1175
  _cd;
1176
1176
  _on_init;
1177
- /** Runs once after `on_init`; re-seeds defer timers across a restart. */
1178
- _on_init_async;
1179
1177
  _declared_lanes;
1180
1178
  /** Report-once keys; per instance because `internal/` holds no module state. */
1181
1179
  _reported = /* @__PURE__ */ new Set();
@@ -1186,8 +1184,7 @@ var CorrelateCycle = class {
1186
1184
  max_subscribed_streams,
1187
1185
  declared_lanes,
1188
1186
  shard,
1189
- on_init,
1190
- on_init_async
1187
+ on_init
1191
1188
  }) {
1192
1189
  this._key = shard ? `${registry_key(registry.events)}:${[...shard].sort().join(",")}` : registry_key(registry.events);
1193
1190
  this._dynamic_subscriptions = new LruMap(max_subscribed_streams);
@@ -1196,7 +1193,6 @@ var CorrelateCycle = class {
1196
1193
  this._static_targets = static_targets;
1197
1194
  this._cd = cd;
1198
1195
  this._on_init = on_init;
1199
- this._on_init_async = on_init_async;
1200
1196
  }
1201
1197
  get checkpoint() {
1202
1198
  return this._checkpoint;
@@ -1221,7 +1217,6 @@ var CorrelateCycle = class {
1221
1217
  ...this._static_targets
1222
1218
  ]);
1223
1219
  this._checkpoint = correlated_at >= 0 ? correlated_at : Math.max(-1, watermark - COLD_START_BACK_SCAN);
1224
- this._on_init?.();
1225
1220
  for (const { stream, priority = 0, lane } of this._static_targets) {
1226
1221
  this._static_subscriptions.set(stream, {
1227
1222
  floor: Number.POSITIVE_INFINITY,
@@ -1229,7 +1224,7 @@ var CorrelateCycle = class {
1229
1224
  lane
1230
1225
  });
1231
1226
  }
1232
- await this._on_init_async?.();
1227
+ await this._on_init?.();
1233
1228
  }
1234
1229
  /** After `restore` replaced the log: forget everything and cold-start again. */
1235
1230
  restart() {
@@ -2354,7 +2349,6 @@ var EMPTY_DRAIN = {
2354
2349
  var RETRY = "";
2355
2350
  var DrainController = class {
2356
2351
  _armed = false;
2357
- _locked = false;
2358
2352
  _ratio = 0.5;
2359
2353
  /**
2360
2354
  * Local wake at the earliest parked (backoff or defer) visit, or at the
@@ -2422,8 +2416,8 @@ var DrainController = class {
2422
2416
  this._defer.stop();
2423
2417
  }
2424
2418
  async drain(options = {}) {
2425
- if (!this._armed || this._stopped) return EMPTY_DRAIN;
2426
- if (this._locked) return EMPTY_DRAIN;
2419
+ if (!this._armed || this._stopped || this._inflight)
2420
+ return EMPTY_DRAIN;
2427
2421
  const d = this._deps.defaults ?? {};
2428
2422
  const streamLimit = d.streamLimit ?? options.streamLimit ?? DEFAULT_STREAM_LIMIT;
2429
2423
  const eventLimit = options.eventLimit ?? DEFAULT_EVENT_LIMIT;
@@ -2431,7 +2425,6 @@ var DrainController = class {
2431
2425
  if (this._deps.breaker.state(Date.now()) === "open")
2432
2426
  return EMPTY_DRAIN;
2433
2427
  try {
2434
- this._locked = true;
2435
2428
  this._inflight = new Promise((done) => {
2436
2429
  this._inflight_done = done;
2437
2430
  });
@@ -2474,7 +2467,6 @@ var DrainController = class {
2474
2467
  return EMPTY_DRAIN;
2475
2468
  } finally {
2476
2469
  if (!this._stopped) this._defer.schedule();
2477
- this._locked = false;
2478
2470
  this._inflight = void 0;
2479
2471
  this._inflight_done?.();
2480
2472
  this._inflight_done = void 0;
@@ -2716,13 +2708,23 @@ function build_handle(deps) {
2716
2708
  handled++;
2717
2709
  }
2718
2710
  } catch (error) {
2719
- if (error instanceof DeferSignal)
2720
- return {
2721
- lease,
2722
- handled,
2723
- acked_at: at,
2724
- defer: resolve_defer_at(error.when, event)
2725
- };
2711
+ if (error instanceof DeferSignal) {
2712
+ let defer;
2713
+ try {
2714
+ defer = resolve_defer_at(error.when, event);
2715
+ } catch (invalid) {
2716
+ return finalize(
2717
+ lease,
2718
+ handled,
2719
+ at,
2720
+ invalid,
2721
+ payload.options,
2722
+ logger,
2723
+ event.id
2724
+ );
2725
+ }
2726
+ return { lease, handled, acked_at: at, defer };
2727
+ }
2726
2728
  if (error instanceof CloseSignal)
2727
2729
  return {
2728
2730
  lease,
@@ -2908,7 +2910,6 @@ function validate_only_lanes(options, lanes) {
2908
2910
  var Act = class {
2909
2911
  _emitter = new EventEmitter();
2910
2912
  _breaker;
2911
- _listen;
2912
2913
  _drain;
2913
2914
  _auto_settle;
2914
2915
  /** Whether any event has a reaction; nothing arms without one. */
@@ -2989,14 +2990,6 @@ var Act = class {
2989
2990
  /** Runs a body so internal `store()`/`cache()` calls resolve to `_ports`. */
2990
2991
  _scoped;
2991
2992
  _correlator;
2992
- // Bound once, reused by every reaction payload.
2993
- _bound_do = this.do.bind(this);
2994
- _bound_load = this.load.bind(this);
2995
- _bound_query = this.query.bind(this);
2996
- _bound_query_array = this.query_array.bind(this);
2997
- _bound_forget = this.forget.bind(this);
2998
- _handle;
2999
- _handle_batch;
3000
2993
  _lanes;
3001
2994
  _close_lock = new CloseLock();
3002
2995
  /** Every lane a controller can exist for: `"default"` plus each declared one. */
@@ -3030,31 +3023,38 @@ var Act = class {
3030
3023
  this._correlator = options.correlator ?? default_correlator;
3031
3024
  this._es = build_es(this._logger, this._correlator, patch_fn);
3032
3025
  this._cd = build_drain(this._logger);
3033
- this._handle = build_handle({
3026
+ const handle = build_handle({
3034
3027
  logger: this._logger,
3035
3028
  reaction_scope: make_reaction_scope({
3036
- do: this._bound_do,
3037
- load: this._bound_load,
3038
- query: this._bound_query,
3039
- query_array: this._bound_query_array,
3040
- forget: this._bound_forget
3029
+ do: this.do.bind(this),
3030
+ load: this.load.bind(this),
3031
+ query: this.query.bind(this),
3032
+ query_array: this.query_array.bind(this),
3033
+ forget: this.forget.bind(this)
3041
3034
  })
3042
3035
  });
3043
- this._handle_batch = build_handle_batch(this._logger);
3036
+ const handle_batch = build_handle_batch(this._logger);
3044
3037
  const classification = classify_registry(this.registry, this._states);
3045
3038
  this._reactive = classification.event_to_lanes.size > 0;
3046
3039
  this._event_to_state = classification.event_to_state;
3047
3040
  this._event_to_lanes = classification.event_to_lanes;
3048
- this._listen = options.listen !== false;
3049
3041
  this._drain = options.drain !== false;
3050
3042
  this._auto_settle = options.autoSettle !== false;
3051
3043
  this._breaker = this._build_breaker(options);
3052
- this._drain_controllers = this._build_drain_controllers(options, lanes);
3044
+ this._drain_controllers = this._build_drain_controllers(
3045
+ options,
3046
+ lanes,
3047
+ handle,
3048
+ handle_batch
3049
+ );
3053
3050
  this._advise_orphaned_lanes(options);
3054
3051
  this._audit_deps = this._build_audit_deps();
3055
3052
  this._correlate = this._build_correlate(options, classification);
3056
3053
  this._settle = this._build_settle(options);
3057
- this._notify_disposer = this._wire_notify(this._ports.store);
3054
+ this._notify_disposer = this._wire_notify(
3055
+ this._ports.store,
3056
+ options.listen !== false
3057
+ );
3058
3058
  register_weak_disposer(new WeakRef(this), (self) => self.shutdown());
3059
3059
  }
3060
3060
  /** Shared by drain, settle and autoclose; bad knobs throw at build. */
@@ -3070,7 +3070,7 @@ var Act = class {
3070
3070
  );
3071
3071
  }
3072
3072
  /** Apps that declare no lanes pass no lane filter to `claim`. */
3073
- _build_drain_controllers(options, lanes) {
3073
+ _build_drain_controllers(options, lanes, handle, handle_batch) {
3074
3074
  const all_lanes = this._declared_lanes;
3075
3075
  const only_set = options.onlyLanes && options.onlyLanes.length > 0 ? new Set(options.onlyLanes) : void 0;
3076
3076
  const active_lanes = only_set ? all_lanes.filter((n) => only_set.has(n)) : all_lanes;
@@ -3083,8 +3083,8 @@ var Act = class {
3083
3083
  ops: this._cd,
3084
3084
  registry: this.registry,
3085
3085
  batch_handlers: this._batch_handlers,
3086
- handle: this._handle,
3087
- handle_batch: this._handle_batch,
3086
+ handle,
3087
+ handle_batch,
3088
3088
  on_acked: (acked) => this.emit("acked", acked),
3089
3089
  on_blocked: (blocked) => this.emit("blocked", blocked),
3090
3090
  // Reaction-requested close: the same machinery as `app.close`.
@@ -3149,8 +3149,10 @@ var Act = class {
3149
3149
  // Built after the controllers, so their keys are the active lanes.
3150
3150
  shard: this._drain_controllers.size < this._declared_lanes.length ? [...this._drain_controllers.keys()] : void 0,
3151
3151
  // Only reached on a draining app: writer-only ones never correlate.
3152
- on_init: () => this._arm_all(),
3153
- on_init_async: this._drain ? () => this._seed_persisted_defers() : void 0
3152
+ on_init: async () => {
3153
+ this._arm_all();
3154
+ await this._seed_persisted_defers();
3155
+ }
3154
3156
  });
3155
3157
  }
3156
3158
  /** Cold start: an idle deferred stream still fires on time after a restart. */
@@ -3243,10 +3245,10 @@ var Act = class {
3243
3245
  }
3244
3246
  }
3245
3247
  /** Failures are logged, never thrown: `notify` is a hint. */
3246
- async _wire_notify(s) {
3248
+ async _wire_notify(s, listen) {
3247
3249
  if (!this._reactive) return void 0;
3248
3250
  if (!s.notify) return void 0;
3249
- if (!this._listen) return void 0;
3251
+ if (!listen) return void 0;
3250
3252
  try {
3251
3253
  return await s.notify((notification) => {
3252
3254
  try {
@@ -4308,37 +4310,49 @@ function register_lane(config2, lanes) {
4308
4310
  `Lane "${config2.name}" was already declared with a different config`
4309
4311
  );
4310
4312
  }
4313
+ function add_reaction(events, event, name, kind, make, builder) {
4314
+ if (!name)
4315
+ throw new Error(
4316
+ `${kind} handler for "${String(event)}" must be a named function`
4317
+ );
4318
+ const label = kind === "Reaction" ? "reaction" : "projection handler";
4319
+ if (events[event].reactions.has(name))
4320
+ throw new Error(
4321
+ `Duplicate ${label} "${name}" for event "${String(event)}". ${kind} handlers are keyed by function name; rename one of them.`
4322
+ );
4323
+ const reaction = make();
4324
+ events[event].reactions.set(name, reaction);
4325
+ return Object.assign(builder, {
4326
+ to(resolver) {
4327
+ reaction.resolver = typeof resolver === "string" ? { target: resolver } : resolver;
4328
+ return builder;
4329
+ }
4330
+ });
4331
+ }
4311
4332
  function reaction_on(event, events, builder) {
4312
- const register = (handler, options, schedule) => {
4313
- if (!handler.name)
4314
- throw new Error(
4315
- `Reaction handler for "${String(event)}" must be a named function`
4316
- );
4317
- if (events[event].reactions.has(handler.name))
4318
- throw new Error(
4319
- `Duplicate reaction "${handler.name}" for event "${String(event)}". Reaction handlers are keyed by function name; rename one of them.`
4320
- );
4321
- if (schedule && typeof schedule !== "function") assert_defer_when(schedule);
4322
- const reaction = {
4323
- handler: schedule ? make_deferred(handler, schedule) : handler,
4324
- resolver: _this_,
4325
- // Validate the whole bag at the declaration site so a bad
4326
- // `maxRetries`/`backoff`/`blockOnError` throws ZodError at build,
4327
- // not a NaN gate on the first retry.
4328
- options: resolveReactionConfig({
4329
- blockOnError: options?.blockOnError ?? DEFAULT_BLOCK_ON_ERROR,
4330
- maxRetries: options?.maxRetries ?? DEFAULT_MAX_RETRIES,
4331
- backoff: options?.backoff
4332
- })
4333
- };
4334
- events[event].reactions.set(handler.name, reaction);
4335
- return Object.assign(builder, {
4336
- to(resolver) {
4337
- reaction.resolver = typeof resolver === "string" ? { target: resolver } : resolver;
4338
- return builder;
4339
- }
4340
- });
4341
- };
4333
+ const register = (handler, options, schedule) => add_reaction(
4334
+ events,
4335
+ event,
4336
+ handler.name,
4337
+ "Reaction",
4338
+ () => {
4339
+ if (schedule && typeof schedule !== "function")
4340
+ assert_defer_when(schedule);
4341
+ return {
4342
+ handler: schedule ? make_deferred(handler, schedule) : handler,
4343
+ resolver: _this_,
4344
+ // Validate the whole bag at the declaration site so a bad
4345
+ // `maxRetries`/`backoff`/`blockOnError` throws ZodError at build,
4346
+ // not a NaN gate on the first retry.
4347
+ options: resolveReactionConfig({
4348
+ blockOnError: options?.blockOnError ?? DEFAULT_BLOCK_ON_ERROR,
4349
+ maxRetries: options?.maxRetries ?? DEFAULT_MAX_RETRIES,
4350
+ backoff: options?.backoff
4351
+ })
4352
+ };
4353
+ },
4354
+ builder
4355
+ );
4342
4356
  return {
4343
4357
  do: (handler, options) => register(handler, options),
4344
4358
  defer: (schedule) => ({
@@ -4471,7 +4485,7 @@ function build_events(registry, states, lanes, owners) {
4471
4485
  throw new Error(
4472
4486
  `Reaction on target "${resolver.target}" conflicts with the projection that already serves it \u2014 a target is served by one batch handler or one state projection, and a reaction to it would never run`
4473
4487
  );
4474
- if (resolver.lane && !declared.has(resolver.lane))
4488
+ if (resolver.lane !== void 0 && !declared.has(resolver.lane))
4475
4489
  lane_errors.push(
4476
4490
  `Reaction "${handler_name}" on "${event_name}" targets undeclared lane "${resolver.lane}". Declared lanes: ${[...declared].map((l) => `"${l}"`).join(", ")}. Add \`.withLane({ name: "${resolver.lane}", ... })\` to act() or correct the .to() declaration.`
4477
4491
  );
@@ -4767,31 +4781,22 @@ function _projection(target) {
4767
4781
  }
4768
4782
  return {
4769
4783
  do: (handler) => {
4770
- const reaction = {
4771
- handler,
4772
- resolver: default_resolver ?? _this_,
4773
- options: {
4774
- blockOnError: DEFAULT_BLOCK_ON_ERROR,
4775
- maxRetries: DEFAULT_MAX_RETRIES
4776
- }
4777
- };
4778
- const register = events[event];
4779
- if (!handler.name)
4780
- throw new Error(
4781
- `Projection handler for "${event}" must be a named function`
4782
- );
4783
- if (register.reactions.has(handler.name))
4784
- throw new Error(
4785
- `Duplicate projection handler "${handler.name}" for event "${event}". Projection handlers are keyed by function name; rename one of them.`
4786
- );
4787
- register.reactions.set(handler.name, reaction);
4788
4784
  const widened = base;
4789
- return Object.assign(widened, {
4790
- to(resolver) {
4791
- reaction.resolver = typeof resolver === "string" ? { target: resolver } : resolver;
4792
- return widened;
4793
- }
4794
- });
4785
+ return add_reaction(
4786
+ events,
4787
+ event,
4788
+ handler.name,
4789
+ "Projection",
4790
+ () => ({
4791
+ handler,
4792
+ resolver: default_resolver ?? _this_,
4793
+ options: {
4794
+ blockOnError: DEFAULT_BLOCK_ON_ERROR,
4795
+ maxRetries: DEFAULT_MAX_RETRIES
4796
+ }
4797
+ }),
4798
+ widened
4799
+ );
4795
4800
  }
4796
4801
  };
4797
4802
  },