@rotorsoft/act 1.20.0 → 1.22.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.
Files changed (34) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/@types/act.d.ts +55 -43
  3. package/dist/@types/act.d.ts.map +1 -1
  4. package/dist/@types/adapters/in-memory-store.d.ts +13 -4
  5. package/dist/@types/adapters/in-memory-store.d.ts.map +1 -1
  6. package/dist/@types/builders/state-builder.d.ts +35 -9
  7. package/dist/@types/builders/state-builder.d.ts.map +1 -1
  8. package/dist/@types/internal/autoclose-config.d.ts +39 -15
  9. package/dist/@types/internal/autoclose-config.d.ts.map +1 -1
  10. package/dist/@types/internal/autoclose-policy.d.ts +39 -13
  11. package/dist/@types/internal/autoclose-policy.d.ts.map +1 -1
  12. package/dist/@types/internal/autoclose-reaction.d.ts.map +1 -1
  13. package/dist/@types/internal/close-cycle.d.ts +5 -0
  14. package/dist/@types/internal/close-cycle.d.ts.map +1 -1
  15. package/dist/@types/internal/close-signal.d.ts +8 -0
  16. package/dist/@types/internal/close-signal.d.ts.map +1 -1
  17. package/dist/@types/internal/index.d.ts +2 -2
  18. package/dist/@types/internal/index.d.ts.map +1 -1
  19. package/dist/@types/internal/reactions.d.ts.map +1 -1
  20. package/dist/@types/types/action.d.ts +46 -13
  21. package/dist/@types/types/action.d.ts.map +1 -1
  22. package/dist/@types/types/ports.d.ts +29 -3
  23. package/dist/@types/types/ports.d.ts.map +1 -1
  24. package/dist/{chunk-TODGOWEZ.js → chunk-KBTC3NGQ.js} +64 -14
  25. package/dist/chunk-KBTC3NGQ.js.map +1 -0
  26. package/dist/index.cjs +237 -38
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.js +175 -26
  29. package/dist/index.js.map +1 -1
  30. package/dist/test/index.cjs +63 -13
  31. package/dist/test/index.cjs.map +1 -1
  32. package/dist/test/index.js +1 -1
  33. package/package.json +2 -2
  34. package/dist/chunk-TODGOWEZ.js.map +0 -1
@@ -641,6 +641,10 @@ var InMemoryStream = class {
641
641
  var InMemoryStore = class {
642
642
  // stored events
643
643
  _events = [];
644
+ // next event id — monotonic, never reused. Deletions (truncate, windowed
645
+ // prune) punch holes in the id sequence, so ids are NOT array indexes and
646
+ // NOT `_events.length`; they only stay sorted ascending in `_events`.
647
+ _next_id = 0;
644
648
  // stored stream positions and other metadata
645
649
  _streams = /* @__PURE__ */ new Map();
646
650
  // last committed version per stream — O(1) replacement for filter-on-commit
@@ -658,11 +662,26 @@ var InMemoryStore = class {
658
662
  _pii = /* @__PURE__ */ new Map();
659
663
  _reset_indexes() {
660
664
  this._events.length = 0;
665
+ this._next_id = 0;
661
666
  this._stream_versions.clear();
662
667
  this._max_event_id_by_stream.clear();
663
668
  this._max_non_snap_event_id = -1;
664
669
  this._pii.clear();
665
670
  }
671
+ // First index whose event id is greater than `after`. `_events` stays
672
+ // sorted ascending by id (append-only commits; deletions preserve
673
+ // order), so id-bounded scans binary-search their start instead of
674
+ // assuming id === index — an invariant that truncation breaks.
675
+ _first_index_after(after) {
676
+ let lo = 0;
677
+ let hi = this._events.length;
678
+ while (lo < hi) {
679
+ const mid = lo + hi >>> 1;
680
+ if (this._events[mid].id > after) hi = mid;
681
+ else lo = mid + 1;
682
+ }
683
+ return lo;
684
+ }
666
685
  // Attach the isolated PII payload (or null) to an event before handing it to
667
686
  // a caller. Allocation-free for events without PII — by far the common case.
668
687
  _with_pii(e) {
@@ -715,7 +734,7 @@ var InMemoryStore = class {
715
734
  await sleep();
716
735
  let count = 0;
717
736
  if (query?.backward) {
718
- let i = (query?.before || this._events.length) - 1;
737
+ let i = (query?.before !== void 0 ? this._first_index_after(query.before - 1) : this._events.length) - 1;
719
738
  while (i >= 0) {
720
739
  const e = this._events[i--];
721
740
  if (query && !this.in_query(query, e)) continue;
@@ -730,7 +749,7 @@ var InMemoryStore = class {
730
749
  if (query?.limit && count >= query.limit) break;
731
750
  }
732
751
  } else {
733
- let i = (query?.after ?? -1) + 1;
752
+ let i = this._first_index_after(query?.after ?? -1);
734
753
  if (query?.with_snaps && query.stream_exact && query.stream !== void 0 && query.after === void 0) {
735
754
  for (let j = this._events.length - 1; j >= 0; j--) {
736
755
  const e = this._events[j];
@@ -779,7 +798,7 @@ var InMemoryStore = class {
779
798
  let last_non_snap_id = -1;
780
799
  const committed = msgs.map(({ name, data, pii }) => {
781
800
  const c = {
782
- id: this._events.length,
801
+ id: this._next_id++,
783
802
  stream,
784
803
  version,
785
804
  created: /* @__PURE__ */ new Date(),
@@ -1120,7 +1139,7 @@ var InMemoryStore = class {
1120
1139
  count++;
1121
1140
  if (count >= limit) break;
1122
1141
  }
1123
- return { maxEventId: this._events.length - 1, count };
1142
+ return { maxEventId: this._events.at(-1)?.id ?? -1, count };
1124
1143
  }
1125
1144
  /**
1126
1145
  * Per-stream aggregated stats — see {@link Store.query_stats}.
@@ -1201,13 +1220,41 @@ var InMemoryStore = class {
1201
1220
  }
1202
1221
  /**
1203
1222
  * Atomically truncates streams and seeds each with a snapshot or tombstone.
1204
- * @param targets - Streams to truncate with optional snapshot state and meta.
1223
+ * Windowed targets (`before` set) prune the prefix below the closest safe
1224
+ * `__snapshot__` instead — no seed, subscriptions untouched, no-op when no
1225
+ * snapshot qualifies.
1226
+ * @param targets - Streams to truncate with optional snapshot state and meta,
1227
+ * or a `before`/`max_id` boundary for a windowed prefix delete.
1205
1228
  * @returns Map keyed by stream name, each entry with `deleted` count and `committed` event.
1206
1229
  */
1207
1230
  async truncate(targets) {
1208
1231
  await sleep();
1232
+ const result = /* @__PURE__ */ new Map();
1233
+ const windowed = targets.filter((t) => t.before !== void 0);
1234
+ if (windowed.length) {
1235
+ const drop = /* @__PURE__ */ new Set();
1236
+ for (const { stream, before, max_id } of windowed) {
1237
+ let boundary;
1238
+ for (const e of this._events) {
1239
+ if (e.stream === stream && e.name === SNAP_EVENT && e.created < before && (max_id === void 0 || e.id <= max_id) && (!boundary || e.id > boundary.id))
1240
+ boundary = e;
1241
+ }
1242
+ if (!boundary) continue;
1243
+ let deleted = 0;
1244
+ for (const e of this._events) {
1245
+ if (e.stream === stream && e.id < boundary.id) {
1246
+ drop.add(e.id);
1247
+ this._pii.get(stream)?.delete(e.id);
1248
+ deleted++;
1249
+ }
1250
+ }
1251
+ result.set(stream, { deleted, committed: boundary, before });
1252
+ }
1253
+ if (drop.size) this._events = this._events.filter((e) => !drop.has(e.id));
1254
+ }
1255
+ const full = targets.filter((t) => t.before === void 0);
1209
1256
  const deleted_counts = /* @__PURE__ */ new Map();
1210
- const stream_set = new Set(targets.map((t) => t.stream));
1257
+ const stream_set = new Set(full.map((t) => t.stream));
1211
1258
  for (const e of this._events) {
1212
1259
  if (stream_set.has(e.stream)) {
1213
1260
  deleted_counts.set(e.stream, (deleted_counts.get(e.stream) ?? 0) + 1);
@@ -1218,11 +1265,11 @@ var InMemoryStore = class {
1218
1265
  this._streams.delete(stream);
1219
1266
  this._stream_versions.delete(stream);
1220
1267
  this._max_event_id_by_stream.delete(stream);
1268
+ this._pii.delete(stream);
1221
1269
  }
1222
- const result = /* @__PURE__ */ new Map();
1223
- for (const { stream, snapshot, meta } of targets) {
1270
+ for (const { stream, snapshot, meta } of full) {
1224
1271
  const event = {
1225
- id: this._events.length,
1272
+ id: this._next_id++,
1226
1273
  stream,
1227
1274
  version: 0,
1228
1275
  created: /* @__PURE__ */ new Date(),
@@ -1254,25 +1301,27 @@ var InMemoryStore = class {
1254
1301
  * throw inside the driver restores the snapshot, leaving the store
1255
1302
  * byte-for-byte unchanged from the operator's perspective.
1256
1303
  *
1257
- * `id`s are reassigned `0..N-1` as events arrive (matching the
1258
- * adapter's commit-id convention — InMemory uses `_events.length`).
1259
- * `created` is preserved verbatim from the source.
1304
+ * `id`s are reassigned `0..N-1` as events arrive (dense — the
1305
+ * monotonic id counter restarts at 0 for the rebuild). `created` is
1306
+ * preserved verbatim from the source.
1260
1307
  */
1261
1308
  async restore(driver) {
1262
1309
  await sleep();
1263
1310
  const prev_events = this._events;
1311
+ const prev_next_id = this._next_id;
1264
1312
  const prev_streams = this._streams;
1265
1313
  const prev_stream_versions = this._stream_versions;
1266
1314
  const prev_max_event_id_by_stream = this._max_event_id_by_stream;
1267
1315
  const prev_max_non_snap_event_id = this._max_non_snap_event_id;
1268
1316
  this._events = [];
1317
+ this._next_id = 0;
1269
1318
  this._streams = /* @__PURE__ */ new Map();
1270
1319
  this._stream_versions = /* @__PURE__ */ new Map();
1271
1320
  this._max_event_id_by_stream = /* @__PURE__ */ new Map();
1272
1321
  this._max_non_snap_event_id = -1;
1273
1322
  try {
1274
1323
  await driver(async (event) => {
1275
- const id = this._events.length;
1324
+ const id = this._next_id++;
1276
1325
  const committed = { ...event, id };
1277
1326
  this._events.push(committed);
1278
1327
  this._stream_versions.set(event.stream, event.version);
@@ -1284,6 +1333,7 @@ var InMemoryStore = class {
1284
1333
  });
1285
1334
  } catch (err) {
1286
1335
  this._events = prev_events;
1336
+ this._next_id = prev_next_id;
1287
1337
  this._streams = prev_streams;
1288
1338
  this._stream_versions = prev_stream_versions;
1289
1339
  this._max_event_id_by_stream = prev_max_event_id_by_stream;