@rotorsoft/act 1.32.26 → 1.32.27

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 (47) hide show
  1. package/README.md +2 -3
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/@types/act.d.ts +5 -151
  4. package/dist/@types/act.d.ts.map +1 -1
  5. package/dist/@types/adapters/in-memory-store.d.ts +0 -21
  6. package/dist/@types/adapters/in-memory-store.d.ts.map +1 -1
  7. package/dist/@types/builders/act-builder.d.ts +0 -25
  8. package/dist/@types/builders/act-builder.d.ts.map +1 -1
  9. package/dist/@types/builders/build-classify.d.ts +4 -4
  10. package/dist/@types/builders/build-classify.d.ts.map +1 -1
  11. package/dist/@types/builders/projection-builder.d.ts.map +1 -1
  12. package/dist/@types/builders/slice-builder.d.ts +1 -12
  13. package/dist/@types/builders/slice-builder.d.ts.map +1 -1
  14. package/dist/@types/builders/state-builder.d.ts +0 -189
  15. package/dist/@types/builders/state-builder.d.ts.map +1 -1
  16. package/dist/@types/config.d.ts +0 -27
  17. package/dist/@types/config.d.ts.map +1 -1
  18. package/dist/@types/internal/close-cycle.d.ts +2 -1
  19. package/dist/@types/internal/close-cycle.d.ts.map +1 -1
  20. package/dist/@types/ports.d.ts +0 -7
  21. package/dist/@types/ports.d.ts.map +1 -1
  22. package/dist/@types/test/sandbox.d.ts +0 -14
  23. package/dist/@types/test/sandbox.d.ts.map +1 -1
  24. package/dist/@types/types/action.d.ts +0 -27
  25. package/dist/@types/types/action.d.ts.map +1 -1
  26. package/dist/@types/types/errors.d.ts +1 -92
  27. package/dist/@types/types/errors.d.ts.map +1 -1
  28. package/dist/@types/types/ports.d.ts +0 -70
  29. package/dist/@types/types/ports.d.ts.map +1 -1
  30. package/dist/@types/types/reaction.d.ts +0 -33
  31. package/dist/@types/types/reaction.d.ts.map +1 -1
  32. package/dist/{chunk-QXW3JV2H.js → chunk-5FXT63KF.js} +2 -2
  33. package/dist/chunk-5FXT63KF.js.map +1 -0
  34. package/dist/{chunk-XSG2NW4J.js → chunk-F7K7SIG6.js} +1 -1
  35. package/dist/chunk-F7K7SIG6.js.map +1 -0
  36. package/dist/index.cjs +22 -159
  37. package/dist/index.cjs.map +1 -1
  38. package/dist/index.js +24 -161
  39. package/dist/index.js.map +1 -1
  40. package/dist/test/index.cjs.map +1 -1
  41. package/dist/test/index.js +2 -2
  42. package/dist/test/index.js.map +1 -1
  43. package/dist/types/index.cjs.map +1 -1
  44. package/dist/types/index.js +1 -1
  45. package/package.json +1 -4
  46. package/dist/chunk-QXW3JV2H.js.map +0 -1
  47. package/dist/chunk-XSG2NW4J.js.map +0 -1
package/dist/index.js CHANGED
@@ -26,7 +26,7 @@ import {
26
26
  sleep,
27
27
  store,
28
28
  validate
29
- } from "./chunk-QXW3JV2H.js";
29
+ } from "./chunk-5FXT63KF.js";
30
30
  import {
31
31
  ActorSchema,
32
32
  CausationEventSchema,
@@ -53,7 +53,7 @@ import {
53
53
  pii_split,
54
54
  pii_strip,
55
55
  sensitive
56
- } from "./chunk-XSG2NW4J.js";
56
+ } from "./chunk-F7K7SIG6.js";
57
57
 
58
58
  // src/signals.ts
59
59
  process.once("SIGINT", async (arg) => {
@@ -81,10 +81,8 @@ var ALL_LANES = /* @__PURE__ */ Symbol("act-1103/all-lanes");
81
81
  function classify_registry(registry, states) {
82
82
  const statics = /* @__PURE__ */ new Map();
83
83
  const target_lanes = /* @__PURE__ */ new Map();
84
- const reactive_events = /* @__PURE__ */ new Set();
85
84
  const event_to_lanes = /* @__PURE__ */ new Map();
86
85
  for (const [name, register] of Object.entries(registry.events)) {
87
- if (register.reactions.size > 0) reactive_events.add(name);
88
86
  for (const reaction of register.reactions.values()) {
89
87
  if (typeof reaction.resolver === "function") {
90
88
  event_to_lanes.set(name, ALL_LANES);
@@ -122,7 +120,6 @@ function classify_registry(registry, states) {
122
120
  }
123
121
  return {
124
122
  static_targets: [...statics.values()],
125
- reactive_events,
126
123
  event_to_state,
127
124
  event_to_lanes
128
125
  };
@@ -1279,7 +1276,7 @@ async function run_full_closes(full, target_map, deps, skipped) {
1279
1276
  }
1280
1277
  const safe = await partition_by_safety(
1281
1278
  stream_info,
1282
- deps.reactive_events_size,
1279
+ deps.reactive,
1283
1280
  skipped,
1284
1281
  deps.probe_page_size ?? SAFETY_PROBE_PAGE_SIZE,
1285
1282
  deps.catch_up_correlation
@@ -1316,8 +1313,8 @@ async function run_full_closes(full, target_map, deps, skipped) {
1316
1313
  );
1317
1314
  }
1318
1315
  async function run_windowed_closes(windowed, deps, skipped) {
1319
- const checkpoint = deps.reactive_events_size > 0 ? await deps.catch_up_correlation(Number.MAX_SAFE_INTEGER) : -1;
1320
- const min_at = deps.reactive_events_size > 0 ? await probe_min_watermarks(
1316
+ const checkpoint = deps.reactive ? await deps.catch_up_correlation(Number.MAX_SAFE_INTEGER) : -1;
1317
+ const min_at = deps.reactive ? await probe_min_watermarks(
1321
1318
  windowed.map((t) => t.stream),
1322
1319
  deps.probe_page_size ?? SAFETY_PROBE_PAGE_SIZE,
1323
1320
  checkpoint
@@ -1405,8 +1402,8 @@ async function scan_stream_heads(streams) {
1405
1402
  }
1406
1403
  return out;
1407
1404
  }
1408
- async function partition_by_safety(stream_info, reactive_events_size, skipped, page_size, catch_up_correlation2) {
1409
- if (reactive_events_size === 0) return [...stream_info.keys()];
1405
+ async function partition_by_safety(stream_info, reactive, skipped, page_size, catch_up_correlation2) {
1406
+ if (!reactive) return [...stream_info.keys()];
1410
1407
  let needed = -1;
1411
1408
  for (const info of stream_info.values())
1412
1409
  needed = Math.max(needed, info.max_id);
@@ -3264,7 +3261,8 @@ var Act = class {
3264
3261
  _breaker;
3265
3262
  _listen;
3266
3263
  _drain;
3267
- _reactive_events;
3264
+ /** Whether any event has a reaction; nothing arms without one. */
3265
+ _reactive;
3268
3266
  _drain_controllers;
3269
3267
  _correlate;
3270
3268
  _settle;
@@ -3394,7 +3392,7 @@ var Act = class {
3394
3392
  });
3395
3393
  this._handle_batch = build_handle_batch(this._logger);
3396
3394
  const classification = classify_registry(this.registry, this._states);
3397
- this._reactive_events = classification.reactive_events;
3395
+ this._reactive = classification.event_to_lanes.size > 0;
3398
3396
  this._event_to_state = classification.event_to_state;
3399
3397
  this._event_to_lanes = classification.event_to_lanes;
3400
3398
  this._listen = options.listen !== false;
@@ -3501,9 +3499,8 @@ var Act = class {
3501
3499
  declared_lanes: new Set(this._declared_lanes),
3502
3500
  // Built after the controllers, so their keys are the active lanes.
3503
3501
  shard: this._drain_controllers.size < this._declared_lanes.length ? [...this._drain_controllers.keys()] : void 0,
3504
- on_init: () => {
3505
- if (this._drain && this._reactive_events.size > 0) this._arm_all();
3506
- },
3502
+ // Only reached on a draining app: writer-only ones never correlate.
3503
+ on_init: () => this._arm_all(),
3507
3504
  on_init_async: this._drain ? () => this._seed_persisted_defers() : void 0
3508
3505
  });
3509
3506
  }
@@ -3600,7 +3597,7 @@ var Act = class {
3600
3597
  }
3601
3598
  /** Failures are logged, never thrown: `notify` is a hint. */
3602
3599
  async _wire_notify(s) {
3603
- if (this._reactive_events.size === 0) return void 0;
3600
+ if (!this._reactive) return void 0;
3604
3601
  if (!s.notify) return void 0;
3605
3602
  if (!this._listen) return void 0;
3606
3603
  try {
@@ -3660,46 +3657,6 @@ var Act = class {
3660
3657
  * console.log(snapshots[0].state.count); // Current count after increment
3661
3658
  * ```
3662
3659
  *
3663
- * @example With error handling
3664
- * ```typescript
3665
- * try {
3666
- * await app.do(
3667
- * "withdraw",
3668
- * { stream: "account-123", actor: { id: "user1", name: "Alice" } },
3669
- * { amount: 1000 }
3670
- * );
3671
- * } catch (error) {
3672
- * if (error instanceof InvariantError) {
3673
- * console.error("Business rule violated:", error.description);
3674
- * } else if (error instanceof ConcurrencyError) {
3675
- * console.error("Concurrent modification detected, retry...");
3676
- * } else if (error instanceof ValidationError) {
3677
- * console.error("Invalid payload:", error.details);
3678
- * }
3679
- * }
3680
- * ```
3681
- *
3682
- * @example Reaction triggering another action (reactingTo auto-injected)
3683
- * ```typescript
3684
- * const app = act()
3685
- * .withState(Order)
3686
- * .withState(Inventory)
3687
- * .on("OrderPlaced")
3688
- * .do(async function reduceInventory(event, _stream, app) {
3689
- * // Inside reaction handlers, reactingTo is auto-injected when omitted.
3690
- * // The triggering event is used by default, maintaining the correlation chain.
3691
- * await app.do(
3692
- * "reduceStock",
3693
- * { stream: "inventory-1", actor: { id: "sys", name: "system" } },
3694
- * { amount: event.data.items.length }
3695
- * );
3696
- * // To use a different correlation, pass reactingTo explicitly:
3697
- * // await app.do("reduceStock", target, payload, { reactingTo: customEvent });
3698
- * })
3699
- * .to("inventory-1")
3700
- * .build();
3701
- * ```
3702
- *
3703
3660
  * @see {@link Target} for target structure
3704
3661
  * @see {@link Snapshot} for return value structure
3705
3662
  * @see {@link ValidationError}, {@link InvariantError}, {@link ConcurrencyError}
@@ -3715,10 +3672,9 @@ var Act = class {
3715
3672
  payload,
3716
3673
  do_options
3717
3674
  );
3718
- if (this._reactive_events.size > 0)
3719
- this._arm_for_event_names(
3720
- snapshots.map((s) => s.event.name)
3721
- );
3675
+ this._arm_for_event_names(
3676
+ snapshots.map((s) => s.event.name)
3677
+ );
3722
3678
  this.emit("committed", snapshots);
3723
3679
  return snapshots;
3724
3680
  });
@@ -3765,26 +3721,6 @@ var Act = class {
3765
3721
  * console.log(`Found ${count} events from ${first?.id} to ${last?.id}`);
3766
3722
  * ```
3767
3723
  *
3768
- * @example Query specific event types
3769
- * ```typescript
3770
- * const { count } = await app.query(
3771
- * { name: "UserCreated", limit: 100 },
3772
- * (event) => {
3773
- * console.log("User created:", event.data.email);
3774
- * }
3775
- * );
3776
- * ```
3777
- *
3778
- * @example Query events in time range
3779
- * ```typescript
3780
- * const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000);
3781
- * const { count } = await app.query({
3782
- * created_after: yesterday,
3783
- * stream: "user-123"
3784
- * });
3785
- * console.log(`User had ${count} events in last 24 hours`);
3786
- * ```
3787
- *
3788
3724
  * @see {@link query_array} for loading events into memory
3789
3725
  */
3790
3726
  async query(query, callback) {
@@ -3816,14 +3752,6 @@ var Act = class {
3816
3752
  * events.forEach(event => console.log(event.name, event.data));
3817
3753
  * ```
3818
3754
  *
3819
- * @example Get recent events
3820
- * ```typescript
3821
- * const recent = await app.query_array({
3822
- * stream: "user-123",
3823
- * limit: 10
3824
- * });
3825
- * ```
3826
- *
3827
3755
  * @see {@link query} for large result sets
3828
3756
  */
3829
3757
  async query_array(query) {
@@ -3894,12 +3822,6 @@ var Act = class {
3894
3822
  * await app.drain();
3895
3823
  * ```
3896
3824
  *
3897
- * @example In production, prefer settle()
3898
- * ```typescript
3899
- * await app.do("CreateItem", target, input);
3900
- * app.settle(); // debounced correlate→drain, emits "settled"
3901
- * ```
3902
- *
3903
3825
  * @see {@link settle} for debounced correlate→drain with lifecycle events
3904
3826
  * @see {@link correlate} for dynamic stream discovery
3905
3827
  * @see {@link start_correlations} for automatic correlation
@@ -3911,6 +3833,7 @@ var Act = class {
3911
3833
  return this._scoped(() => this._drain_all(options));
3912
3834
  }
3913
3835
  _arm_all() {
3836
+ if (!this._reactive) return;
3914
3837
  this._correlate.arm();
3915
3838
  for (const c of this._drain_controllers.values()) c.arm();
3916
3839
  }
@@ -3966,37 +3889,20 @@ var Act = class {
3966
3889
  * on reaction resolvers. It then registers these streams so they'll be picked up by
3967
3890
  * the next drain cycle.
3968
3891
  *
3969
- * @param query - Query filter to scan for new correlations
3970
3892
  * @param query - Scan filter — see {@link Query} for fields (typically
3971
3893
  * `{ after: <event-id>, limit: <count> }`)
3972
- * @returns Object with newly leased streams and last scanned event ID
3894
+ * @returns The number of newly subscribed streams and the last scanned event id
3973
3895
  *
3974
3896
  * @example Manual correlation
3975
3897
  * ```typescript
3976
3898
  * // Scan for new streams
3977
- * const { leased, last_id } = await app.correlate({ after: 0, limit: 100 });
3978
- * console.log(`Found ${leased.length} new streams`);
3899
+ * const { subscribed, last_id } = await app.correlate({ after: 0, limit: 100 });
3900
+ * console.log(`Subscribed ${subscribed} new streams`);
3979
3901
  *
3980
3902
  * // Save last_id for next scan
3981
3903
  * await saveCheckpoint(last_id);
3982
3904
  * ```
3983
3905
  *
3984
- * @example Dynamic stream creation
3985
- * ```typescript
3986
- * const app = act()
3987
- * .withState(User)
3988
- * .withState(UserStats)
3989
- * .on("UserLoggedIn")
3990
- * .do(async (event) => ["incrementLoginCount", {}])
3991
- * .to((event) => ({
3992
- * target: `stats-${event.stream}` // Dynamic target per user
3993
- * }))
3994
- * .build();
3995
- *
3996
- * // Discover stats streams as users log in
3997
- * await app.correlate();
3998
- * ```
3999
- *
4000
3906
  * @see {@link start_correlations} for automatic periodic correlation
4001
3907
  * @see {@link stop_correlations} to stop automatic correlation
4002
3908
  */
@@ -4009,8 +3915,7 @@ var Act = class {
4009
3915
  if (!this._drain) return { subscribed: 0, last_id: -1, scanned: false };
4010
3916
  return this._scoped(async () => {
4011
3917
  const { subscribed, last_id, marked, scanned } = await this._correlate.correlate(query, lease);
4012
- if ((subscribed > 0 || marked > 0) && this._reactive_events.size > 0)
4013
- this._arm_all();
3918
+ if (subscribed > 0 || marked > 0) this._arm_all();
4014
3919
  if (subscribed > 0) this._on_discovered?.(subscribed);
4015
3920
  return { subscribed, last_id, scanned };
4016
3921
  });
@@ -4102,12 +4007,6 @@ var Act = class {
4102
4007
  * app.settle({ eventLimit: 1000 }); // emits "settled" when fully replayed
4103
4008
  * ```
4104
4009
  *
4105
- * @example Rebuild a projection (tests / scripts)
4106
- * ```typescript
4107
- * await app.reset(["my-projection"]);
4108
- * await app.drain({ eventLimit: 1000 }); // small streams: one pass is enough
4109
- * ```
4110
- *
4111
4010
  * @see {@link Store.reset} for the underlying store primitive
4112
4011
  * @see {@link settle} for the debounced full-catch-up loop
4113
4012
  */
@@ -4116,7 +4015,7 @@ var Act = class {
4116
4015
  const count = await store().reset(input);
4117
4016
  for (const handler of this._batch_handlers.values())
4118
4017
  handler[FOLD_RESET]?.();
4119
- if (count > 0 && this._reactive_events.size > 0) this._arm_all();
4018
+ if (count > 0) this._arm_all();
4120
4019
  return count;
4121
4020
  });
4122
4021
  }
@@ -4150,7 +4049,7 @@ var Act = class {
4150
4049
  async unblock(input) {
4151
4050
  return this._scoped(async () => {
4152
4051
  const count = await store().unblock(input);
4153
- if (count > 0 && this._reactive_events.size > 0) this._arm_all();
4052
+ if (count > 0) this._arm_all();
4154
4053
  return count;
4155
4054
  });
4156
4055
  }
@@ -4186,11 +4085,6 @@ var Act = class {
4186
4085
  * await cache().clear(); // operator's responsibility
4187
4086
  * ```
4188
4087
  *
4189
- * @example Copy one store into another
4190
- * ```typescript
4191
- * await app.restore(pg, {}, sqlite);
4192
- * ```
4193
- *
4194
4088
  * @see {@link Store.restore} for the underlying driver-pattern primitive.
4195
4089
  */
4196
4090
  async restore(source, opts = {}, sink) {
@@ -4329,16 +4223,6 @@ var Act = class {
4329
4223
  * await app.prioritize({ stream: "^proj-orders$", stream_exact: false }, 10);
4330
4224
  * ```
4331
4225
  *
4332
- * @example Drop all audit projections to background
4333
- * ```typescript
4334
- * await app.prioritize({ source: "^audit-" }, -5);
4335
- * ```
4336
- *
4337
- * @example Reset everyone to default
4338
- * ```typescript
4339
- * await app.prioritize({}, 0);
4340
- * ```
4341
- *
4342
4226
  * @see {@link Store.prioritize} for the underlying primitive
4343
4227
  * @see {@link claim} for how priority biases scheduling
4344
4228
  */
@@ -4349,7 +4233,7 @@ var Act = class {
4349
4233
  async _run_close(targets) {
4350
4234
  const close_actor = { id: "$close", name: "close" };
4351
4235
  const result = await run_close_cycle(targets, {
4352
- reactive_events_size: this._reactive_events.size,
4236
+ reactive: this._reactive,
4353
4237
  catch_up_correlation: (until) => catch_up_correlation(
4354
4238
  this._correlate,
4355
4239
  (limit) => this.correlate({ limit }),
@@ -4402,27 +4286,6 @@ var Act = class {
4402
4286
  * { stream: "order-456" },
4403
4287
  * ]);
4404
4288
  * ```
4405
- *
4406
- * @example Close with restart (state loaded automatically after guard)
4407
- * ```typescript
4408
- * await app.close([
4409
- * { stream: "counter-1", restart: true },
4410
- * { stream: "counter-2" }, // tombstoned
4411
- * ]);
4412
- * ```
4413
- *
4414
- * @example Windowed close — keep the last 180 days of real events
4415
- * ```typescript
4416
- * const cutoff = new Date();
4417
- * cutoff.setDate(cutoff.getDate() - 180);
4418
- * await app.close([
4419
- * {
4420
- * stream: "ledger-acme",
4421
- * before: cutoff,
4422
- * archive: async () => { await archiveToS3("ledger-acme", cutoff); },
4423
- * },
4424
- * ]);
4425
- * ```
4426
4289
  */
4427
4290
  async close(targets) {
4428
4291
  if (!targets.length) return { truncated: /* @__PURE__ */ new Map(), skipped: [] };