@rotorsoft/act 1.10.1 → 1.10.2

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 (33) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/@types/act.d.ts +10 -7
  3. package/dist/@types/act.d.ts.map +1 -1
  4. package/dist/@types/builders/act-builder.d.ts.map +1 -1
  5. package/dist/@types/builders/slice-builder.d.ts +1 -1
  6. package/dist/@types/builders/state-builder.d.ts.map +1 -1
  7. package/dist/@types/internal/event-sourcing.d.ts +7 -7
  8. package/dist/@types/internal/event-sourcing.d.ts.map +1 -1
  9. package/dist/@types/internal/index.d.ts +1 -1
  10. package/dist/@types/internal/index.d.ts.map +1 -1
  11. package/dist/@types/internal/reactions.d.ts.map +1 -1
  12. package/dist/@types/internal/sensitive.d.ts +6 -23
  13. package/dist/@types/internal/sensitive.d.ts.map +1 -1
  14. package/dist/@types/internal/tracing.d.ts.map +1 -1
  15. package/dist/@types/types/action.d.ts +78 -16
  16. package/dist/@types/types/action.d.ts.map +1 -1
  17. package/dist/@types/types/registry.d.ts +12 -0
  18. package/dist/@types/types/registry.d.ts.map +1 -1
  19. package/dist/{chunk-3Z2HU726.js → chunk-5PWLGL5E.js} +2 -2
  20. package/dist/{chunk-BY5JPOZR.js → chunk-NGVWJXEF.js} +9 -24
  21. package/dist/{chunk-BY5JPOZR.js.map → chunk-NGVWJXEF.js.map} +1 -1
  22. package/dist/index.cjs +91 -117
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.js +85 -98
  25. package/dist/index.js.map +1 -1
  26. package/dist/test/index.cjs +2 -2
  27. package/dist/test/index.cjs.map +1 -1
  28. package/dist/test/index.js +4 -4
  29. package/dist/test/index.js.map +1 -1
  30. package/dist/types/index.cjs.map +1 -1
  31. package/dist/types/index.js +1 -1
  32. package/package.json +2 -2
  33. /package/dist/{chunk-3Z2HU726.js.map → chunk-5PWLGL5E.js.map} +0 -0
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  sleep,
20
20
  store,
21
21
  validate
22
- } from "./chunk-3Z2HU726.js";
22
+ } from "./chunk-5PWLGL5E.js";
23
23
  import {
24
24
  ActorSchema,
25
25
  CausationEventSchema,
@@ -40,11 +40,10 @@ import {
40
40
  ZodEmpty,
41
41
  pii_fields,
42
42
  pii_gate,
43
- pii_merge,
44
43
  pii_split,
45
44
  pii_strip,
46
45
  sensitive
47
- } from "./chunk-BY5JPOZR.js";
46
+ } from "./chunk-NGVWJXEF.js";
48
47
  import "./chunk-5WRI5ZAA.js";
49
48
 
50
49
  // src/signals.ts
@@ -639,7 +638,7 @@ async function load_restart_seeds(guarded, target_map, stream_info, event_to_sta
639
638
  );
640
639
  return;
641
640
  }
642
- const snap2 = await load2(owner_state, stream);
641
+ const snap2 = await load2(owner_state, { stream });
643
642
  seed_states.set(stream, snap2.state);
644
643
  })
645
644
  );
@@ -1003,7 +1002,6 @@ async function scan(source, opts = {}, callback) {
1003
1002
  migrated = {
1004
1003
  ...event,
1005
1004
  name: migration.to,
1006
- // biome-ignore lint/suspicious/noExplicitAny: migration target shape is caller-defined
1007
1005
  data: new_data
1008
1006
  };
1009
1007
  migrated_count++;
@@ -1052,39 +1050,37 @@ async function scan(source, opts = {}, callback) {
1052
1050
  }
1053
1051
  };
1054
1052
  }
1055
- async function load(me, stream, callback, asOf, actor) {
1053
+ async function load(me, target, callback) {
1054
+ const { stream, actor, asOf } = target;
1056
1055
  const time_travel = !!asOf && Object.values(asOf).some((v) => v !== void 0);
1057
1056
  const cached = time_travel ? void 0 : await cache().get(stream);
1058
1057
  const cache_hit = !!cached;
1059
1058
  let state2 = cached?.state ?? (me.init ? me.init() : {});
1060
1059
  let patches = cached?.patches ?? 0;
1061
1060
  let snaps = cached?.snaps ?? 0;
1062
- let version = cached?.version ?? -1;
1063
1061
  let replayed = 0;
1064
1062
  let event;
1065
1063
  await store().query(
1066
- (e) => {
1067
- version = e.version;
1068
- const typed = e;
1069
- event = me.view(typed, actor);
1070
- if (e.name === SNAP_EVENT) {
1071
- state2 = e.data;
1064
+ (raw) => {
1065
+ event = me.view(raw, actor);
1066
+ if (event.name === SNAP_EVENT) {
1067
+ state2 = event.data;
1072
1068
  snaps++;
1073
1069
  patches = 0;
1074
1070
  replayed++;
1075
- } else if (me.patch[e.name]) {
1076
- state2 = patch(state2, me.patch[e.name](typed, state2));
1071
+ } else if (me.patch[event.name]) {
1072
+ state2 = patch(state2, me.patch[event.name](event, state2));
1077
1073
  patches++;
1078
1074
  replayed++;
1079
- } else if (e.name !== TOMBSTONE_EVENT) {
1075
+ } else if (event.name !== TOMBSTONE_EVENT) {
1080
1076
  log().warn(
1081
- `Skipping unknown event "${String(e.name)}" on stream "${stream}" (id=${e.id}) \u2014 no reducer in state "${me.name}"`
1077
+ `Skipping unknown event "${String(event.name)}" on stream "${stream}" (id=${event.id}) \u2014 no reducer in state "${me.name}"`
1082
1078
  );
1083
1079
  }
1084
1080
  callback?.({
1085
1081
  event,
1086
1082
  state: state2,
1087
- version,
1083
+ version: event.version,
1088
1084
  patches,
1089
1085
  snaps,
1090
1086
  cache_hit,
@@ -1097,39 +1093,43 @@ async function load(me, stream, callback, asOf, actor) {
1097
1093
  ...cached ? { after: cached.event_id } : { with_snaps: true, ...asOf }
1098
1094
  }
1099
1095
  );
1100
- if (replayed > 0 && !time_travel && event) {
1096
+ if (replayed > 0 && !time_travel && event && !me.pii_aware) {
1101
1097
  await cache().set(stream, {
1102
1098
  state: state2,
1103
- version,
1099
+ version: event.version,
1104
1100
  event_id: event.id,
1105
1101
  patches,
1106
1102
  snaps
1107
1103
  });
1108
1104
  }
1109
- return { event, state: state2, version, patches, snaps, cache_hit, replayed };
1105
+ return {
1106
+ event,
1107
+ state: state2,
1108
+ version: event?.version ?? cached?.version ?? -1,
1109
+ patches,
1110
+ snaps,
1111
+ cache_hit,
1112
+ replayed
1113
+ };
1110
1114
  }
1111
- async function action(me, action2, target, payload, reactingTo, skipValidation = false, correlator = default_correlator) {
1115
+ async function action(me, action2, target, payload, options) {
1112
1116
  const { stream, expectedVersion, actor } = target;
1113
1117
  if (!stream) throw new Error("Missing target stream");
1114
- const validated = skipValidation ? payload : validate(action2, payload, me.actions[action2]);
1118
+ const reactingTo = options?.reactingTo;
1119
+ const correlator = options?.correlator ?? default_correlator;
1120
+ const validated = validate(action2, payload, me.actions[action2]);
1115
1121
  const opts = me.options?.[action2];
1116
1122
  const max_retries = opts?.maxRetries ?? 0;
1117
1123
  for (let attempt = 0; ; attempt++) {
1118
1124
  try {
1119
- const snapshot = await load(
1120
- me,
1121
- stream,
1122
- void 0,
1123
- void 0,
1124
- target.actor
1125
- );
1125
+ const snapshot = await load(me, { stream, actor: target.actor });
1126
1126
  if (snapshot.event?.name === TOMBSTONE_EVENT)
1127
1127
  throw new StreamClosedError(stream);
1128
1128
  const expected = expectedVersion ?? snapshot.event?.version;
1129
1129
  if (me.given) {
1130
1130
  const invariants = me.given[action2] || [];
1131
- invariants.forEach(({ valid, description }) => {
1132
- if (!valid(snapshot.state, actor))
1131
+ invariants.forEach(({ valid: valid2, description }) => {
1132
+ if (!valid2(snapshot.state, actor))
1133
1133
  throw new InvariantError(
1134
1134
  action2,
1135
1135
  validated,
@@ -1145,24 +1145,11 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
1145
1145
  return [snapshot];
1146
1146
  }
1147
1147
  const tuples = Array.isArray(result[0]) ? result : [result];
1148
- const deprecated = me._deprecated;
1149
- if (deprecated && deprecated.size > 0) {
1150
- const me_ = me;
1151
- const warned = me_._warned ?? (me_._warned = /* @__PURE__ */ new Set());
1152
- for (const [name] of tuples) {
1153
- const evt = name;
1154
- if (deprecated.has(evt) && !warned.has(evt)) {
1155
- warned.add(evt);
1156
- log().warn(
1157
- `Action "${String(action2)}" emitted deprecated event "${evt}". A newer version exists in the registry \u2014 update the action's .emit() to target the current version. (warned once per process)`
1158
- );
1159
- }
1160
- }
1161
- }
1162
- const emitted = tuples.map(([name, data]) => {
1163
- const validated2 = skipValidation ? data : validate(name, data, me.events[name]);
1164
- return me.message({ name, data: validated2 });
1165
- });
1148
+ const valid = tuples.map(([name, data]) => ({
1149
+ name,
1150
+ data: validate(name, data, me.events[name])
1151
+ }));
1152
+ const emitted = valid.map((e) => me.message(e));
1166
1153
  const meta = {
1167
1154
  correlation: reactingTo?.meta.correlation || correlator({
1168
1155
  action: action2,
@@ -1203,12 +1190,13 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
1203
1190
  throw error;
1204
1191
  }
1205
1192
  let { state: state2, patches } = snapshot;
1206
- const snapshots = committed.map((event) => {
1193
+ const snapshots = committed.map((row, i) => {
1194
+ const event = { ...row, data: valid[i].data };
1207
1195
  const p = me.patch[event.name](event, state2);
1208
1196
  state2 = patch(state2, p);
1209
1197
  patches++;
1210
1198
  return {
1211
- event: me.view(event, target.actor),
1199
+ event,
1212
1200
  state: state2,
1213
1201
  version: event.version,
1214
1202
  patches,
@@ -1220,13 +1208,15 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
1220
1208
  });
1221
1209
  const last = snapshots.at(-1);
1222
1210
  const snapped = me.snap?.(last);
1223
- cache().set(stream, {
1224
- state: last.state,
1225
- version: last.event.version,
1226
- event_id: last.event.id,
1227
- patches: snapped ? 0 : last.patches,
1228
- snaps: snapped ? last.snaps + 1 : last.snaps
1229
- }).catch((err) => log().error(err));
1211
+ if (!me.pii_aware) {
1212
+ cache().set(stream, {
1213
+ state: last.state,
1214
+ version: last.event.version,
1215
+ event_id: last.event.id,
1216
+ patches: snapped ? 0 : last.patches,
1217
+ snaps: snapped ? last.snaps + 1 : last.snaps
1218
+ }).catch((err) => log().error(err));
1219
+ }
1230
1220
  if (snapped) void snap(last);
1231
1221
  return snapshots;
1232
1222
  } catch (error) {
@@ -1293,15 +1283,7 @@ var traced = (inner, exit, entry) => (async (...args) => {
1293
1283
  return result;
1294
1284
  });
1295
1285
  function build_es(logger, correlator = default_correlator) {
1296
- const bound_action = (me, action_name, target, payload, reactingTo, skipValidation = false) => action(
1297
- me,
1298
- action_name,
1299
- target,
1300
- payload,
1301
- reactingTo,
1302
- skipValidation,
1303
- correlator
1304
- );
1286
+ const bound_action = (me, action_name, target, payload, options) => action(me, action_name, target, payload, { correlator, ...options });
1305
1287
  if (logger.level !== "trace") {
1306
1288
  return {
1307
1289
  snap,
@@ -1320,7 +1302,7 @@ function build_es(logger, correlator = default_correlator) {
1320
1302
  )
1321
1303
  );
1322
1304
  }),
1323
- load: traced(load, (result, _me, stream, _cb, asOf) => {
1305
+ load: traced(load, (result, _me, target) => {
1324
1306
  const stats = stats_marker(
1325
1307
  result.version,
1326
1308
  result.replayed,
@@ -1331,7 +1313,7 @@ function build_es(logger, correlator = default_correlator) {
1331
1313
  es_caption(
1332
1314
  "load",
1333
1315
  C_GREEN,
1334
- `${stream}${as_of_marker(asOf)} ${cache_marker(result.cache_hit)} ${stats}`
1316
+ `${target.stream}${as_of_marker(target.asOf)} ${cache_marker(result.cache_hit)} ${stats}`
1335
1317
  )
1336
1318
  );
1337
1319
  }),
@@ -1819,13 +1801,10 @@ function build_handle(deps) {
1819
1801
  };
1820
1802
  for (const payload of payloads) {
1821
1803
  const { event, handler } = payload;
1822
- scoped_app.do = (action2, target, action_payload, reactingTo, skipValidation) => bound_do(
1823
- action2,
1824
- target,
1825
- action_payload,
1826
- reactingTo ?? event,
1827
- skipValidation
1828
- );
1804
+ scoped_app.do = (action2, target, action_payload, options) => bound_do(action2, target, action_payload, {
1805
+ ...options,
1806
+ reactingTo: options?.reactingTo ?? event
1807
+ });
1829
1808
  try {
1830
1809
  await handler(event, stream, scoped_app);
1831
1810
  at = event.id;
@@ -2231,8 +2210,9 @@ var Act = class {
2231
2210
  * @param action - The name of the action to execute
2232
2211
  * @param target - Target specification with stream ID and actor context
2233
2212
  * @param payload - Action payload matching the action's schema
2234
- * @param reactingTo - Optional event that triggered this action (for correlation)
2235
- * @param skipValidation - Skip schema validation (use carefully, for performance)
2213
+ * @param options - Per-call dispatch options ({@link DoOptions}) —
2214
+ * `reactingTo` to thread correlation, `correlator` to override the
2215
+ * framework or orchestrator-level correlator for this call only.
2236
2216
  * @returns Array of snapshots for all affected states (usually one)
2237
2217
  *
2238
2218
  * @throws {ValidationError} If payload doesn't match action schema
@@ -2287,7 +2267,7 @@ var Act = class {
2287
2267
  * { amount: event.data.items.length }
2288
2268
  * );
2289
2269
  * // To use a different correlation, pass reactingTo explicitly:
2290
- * // await app.do("reduceStock", target, payload, customEvent);
2270
+ * // await app.do("reduceStock", target, payload, { reactingTo: customEvent });
2291
2271
  * })
2292
2272
  * .to("inventory-1")
2293
2273
  * .build();
@@ -2297,15 +2277,14 @@ var Act = class {
2297
2277
  * @see {@link Snapshot} for return value structure
2298
2278
  * @see {@link ValidationError}, {@link InvariantError}, {@link ConcurrencyError}
2299
2279
  */
2300
- async do(action2, target, payload, reactingTo, skipValidation = false) {
2280
+ async do(action2, target, payload, options) {
2301
2281
  return this._scoped(async () => {
2302
2282
  const snapshots = await this._es.action(
2303
2283
  this.registry.actions[action2],
2304
2284
  action2,
2305
2285
  target,
2306
2286
  payload,
2307
- reactingTo,
2308
- skipValidation
2287
+ options
2309
2288
  );
2310
2289
  if (this._reactive_events.size > 0)
2311
2290
  this._arm_for_event_names(
@@ -2315,7 +2294,7 @@ var Act = class {
2315
2294
  return snapshots;
2316
2295
  });
2317
2296
  }
2318
- async load(stateOrName, stream, callback, asOf, actor) {
2297
+ async load(stateOrName, streamOrTarget, callback, asOf) {
2319
2298
  return this._scoped(async () => {
2320
2299
  let merged;
2321
2300
  if (typeof stateOrName === "string") {
@@ -2325,7 +2304,12 @@ var Act = class {
2325
2304
  } else {
2326
2305
  merged = this._states.get(stateOrName.name) || stateOrName;
2327
2306
  }
2328
- return await this._es.load(merged, stream, callback, asOf, actor);
2307
+ const target = typeof streamOrTarget === "string" ? {
2308
+ stream: streamOrTarget,
2309
+ actor: void 0,
2310
+ asOf
2311
+ } : streamOrTarget;
2312
+ return await this._es.load(merged, target, callback);
2329
2313
  });
2330
2314
  }
2331
2315
  /**
@@ -3049,11 +3033,14 @@ function act() {
3049
3033
  const states = /* @__PURE__ */ new Map();
3050
3034
  const _sf = /* @__PURE__ */ new Map();
3051
3035
  const _dp = /* @__PURE__ */ new Map();
3036
+ const _de = /* @__PURE__ */ new Map();
3037
+ const EMPTY_DEPRECATED = /* @__PURE__ */ new Set();
3052
3038
  const registry = {
3053
3039
  actions: {},
3054
3040
  events: {},
3055
3041
  sensitive_fields: (event_name) => _sf.get(event_name) ?? [],
3056
- disclosure_predicate: (state_name) => _dp.get(state_name) ?? null
3042
+ disclosure_predicate: (state_name) => _dp.get(state_name) ?? null,
3043
+ deprecated_events: (state_name) => _de.get(state_name) ?? EMPTY_DEPRECATED
3057
3044
  };
3058
3045
  const pending_projections = [];
3059
3046
  const batch_handlers = /* @__PURE__ */ new Map();
@@ -3065,7 +3052,7 @@ function act() {
3065
3052
  const event_names = Object.keys(state2.events);
3066
3053
  const deprecated = deprecated_event_names(event_names);
3067
3054
  if (deprecated.size === 0) continue;
3068
- state2._deprecated = deprecated;
3055
+ _de.set(state2.name, deprecated);
3069
3056
  for (const name of deprecated) {
3070
3057
  const current = current_version_of(name, event_names);
3071
3058
  deprecation_summary.push({
@@ -3193,18 +3180,17 @@ function act() {
3193
3180
  );
3194
3181
  }
3195
3182
  const disclose = state2.disclose ?? null;
3196
- state2.view = (event, actor) => {
3197
- const fields = fields_by_event.get(event.name);
3198
- return fields ? pii_gate(event, fields, disclose, actor) : event;
3199
- };
3183
+ state2.pii_aware = true;
3184
+ state2.view = (event, actor) => pii_gate(
3185
+ event,
3186
+ fields_by_event.get(event.name) ?? [],
3187
+ disclose,
3188
+ actor
3189
+ );
3200
3190
  state2.message = (validated) => {
3201
3191
  const fields = fields_by_event.get(validated.name);
3202
3192
  return fields ? pii_split(validated, fields) : validated;
3203
3193
  };
3204
- for (const [event_name, fields] of fields_by_event) {
3205
- const original = state2.patch[event_name];
3206
- state2.patch[event_name] = (event, s) => original(pii_merge(event, fields), s);
3207
- }
3208
3194
  }
3209
3195
  for (const [target, original] of batch_handlers) {
3210
3196
  const wrapped = async (events, stream) => {
@@ -3384,9 +3370,10 @@ function state(entry) {
3384
3370
  init,
3385
3371
  patch: default_patch,
3386
3372
  on: {},
3387
- // Step delegates initialized as identity. `act().build()`
3388
- // overrides on states with `sensitive(...)` events to bake in
3389
- // the gate / split.
3373
+ // Step delegates initialized as identity, `pii_aware` false.
3374
+ // `act().build()` flips both on states with `sensitive(...)`
3375
+ // events to bake in the gate / split and gate the cache write.
3376
+ pii_aware: false,
3390
3377
  view: (event) => event,
3391
3378
  message: (validated) => validated
3392
3379
  };