@rotorsoft/act 1.5.1 → 1.6.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.
package/dist/index.js CHANGED
@@ -912,13 +912,30 @@ function is_valid(event) {
912
912
  return true;
913
913
  }
914
914
  async function scan(source, opts = {}, callback) {
915
- const { drop_snapshots = false, on_progress } = opts;
915
+ const {
916
+ drop_snapshots = false,
917
+ drop_closed_streams = false,
918
+ on_progress,
919
+ event_migrations,
920
+ stream_rename
921
+ } = opts;
916
922
  const limit = opts.batch_size ?? DEFAULT_BATCH;
917
923
  const id_map = /* @__PURE__ */ new Map();
918
924
  let kept = 0;
919
925
  let dropped_snaps = 0;
926
+ let dropped_closed = 0;
927
+ let migrated_count = 0;
920
928
  let processed = 0;
921
929
  let at;
930
+ const closed_streams = /* @__PURE__ */ new Set();
931
+ if (drop_closed_streams) {
932
+ await source.query(
933
+ (e) => {
934
+ if (e.name === TOMBSTONE_EVENT) closed_streams.add(e.stream);
935
+ },
936
+ { names: [TOMBSTONE_EVENT] }
937
+ );
938
+ }
922
939
  let max_id;
923
940
  const probed = await source.query(
924
941
  (e) => {
@@ -942,12 +959,35 @@ async function scan(source, opts = {}, callback) {
942
959
  dropped_snaps++;
943
960
  return;
944
961
  }
962
+ if (closed_streams.has(event.stream) && event.name !== TOMBSTONE_EVENT) {
963
+ dropped_closed++;
964
+ return;
965
+ }
966
+ let migrated = event;
967
+ const migration = event_migrations?.[event.name];
968
+ if (migration) {
969
+ const old_data = migration.from_schema.parse(event.data);
970
+ const new_data = migration.migrate(old_data);
971
+ migration.to_schema.parse(new_data);
972
+ migrated = {
973
+ ...event,
974
+ name: migration.to,
975
+ // biome-ignore lint/suspicious/noExplicitAny: migration target shape is caller-defined
976
+ data: new_data
977
+ };
978
+ migrated_count++;
979
+ }
980
+ if (stream_rename) {
981
+ const renamed = stream_rename(migrated.stream);
982
+ if (renamed !== migrated.stream)
983
+ migrated = { ...migrated, stream: renamed };
984
+ }
945
985
  if (!callback) {
946
986
  kept++;
947
987
  return;
948
988
  }
949
- let remapped = event;
950
- const caused_by = event.meta.causation.event?.id;
989
+ let remapped = migrated;
990
+ const caused_by = migrated.meta.causation.event?.id;
951
991
  if (caused_by !== void 0) {
952
992
  const new_caused_by = id_map.get(caused_by);
953
993
  if (new_caused_by !== void 0 && new_caused_by !== caused_by) {
@@ -974,10 +1014,10 @@ async function scan(source, opts = {}, callback) {
974
1014
  }
975
1015
  return {
976
1016
  kept,
1017
+ migrated: migrated_count,
977
1018
  dropped: {
978
- closed_streams: 0,
979
- snapshots: dropped_snaps,
980
- empty_streams: 0
1019
+ closed_streams: dropped_closed,
1020
+ snapshots: dropped_snaps
981
1021
  }
982
1022
  };
983
1023
  }
@@ -2578,7 +2618,7 @@ var Act = class {
2578
2618
  * fully catches up paginated streams without forcing callers to roll
2579
2619
  * their own loop.
2580
2620
  *
2581
- * @param streams - Reaction target streams (e.g., projection names) to reset
2621
+ * @param input - Reaction target streams (e.g., projection names) to reset, or a {@link StreamFilter} for bulk operations
2582
2622
  * @returns Count of streams that were actually reset
2583
2623
  *
2584
2624
  * @example Rebuild a projection (production)
@@ -2618,7 +2658,7 @@ var Act = class {
2618
2658
  * directly, but `store().unblock(...)` alone leaves the flag
2619
2659
  * untouched.
2620
2660
  *
2621
- * @param streams - Stream names to unblock
2661
+ * @param input - Stream names to unblock, or a {@link StreamFilter} for bulk recovery
2622
2662
  * @returns Count of streams that were actually flipped (were blocked)
2623
2663
  *
2624
2664
  * @example Recover from a 4xx webhook after fixing the bug
@@ -2692,13 +2732,15 @@ var Act = class {
2692
2732
  return s;
2693
2733
  })();
2694
2734
  let kept = 0;
2695
- let dropped = { closed_streams: 0, snapshots: 0, empty_streams: 0 };
2735
+ let migrated = 0;
2736
+ let dropped = { closed_streams: 0, snapshots: 0 };
2696
2737
  await target.restore(async (callback) => {
2697
2738
  const partial = await scan(source, opts, callback);
2698
2739
  kept = partial.kept;
2740
+ migrated = partial.migrated;
2699
2741
  dropped = partial.dropped;
2700
2742
  });
2701
- return { kept, dropped, duration_ms: Date.now() - started };
2743
+ return { kept, migrated, dropped, duration_ms: Date.now() - started };
2702
2744
  });
2703
2745
  }
2704
2746
  /**