@rotorsoft/act 0.41.0 → 0.42.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
@@ -95,7 +95,6 @@ function classifyRegistry(registry, states) {
95
95
  }
96
96
 
97
97
  // src/internal/close-cycle.ts
98
- import { randomUUID } from "crypto";
99
98
  async function runCloseCycle(targets, deps) {
100
99
  const targetMap = new Map(targets.map((t) => [t.stream, t]));
101
100
  const streams = [...targetMap.keys()];
@@ -107,11 +106,10 @@ async function runCloseCycle(targets, deps) {
107
106
  skipped
108
107
  );
109
108
  if (!safe.length) return { truncated: /* @__PURE__ */ new Map(), skipped };
110
- const correlation = randomUUID();
111
109
  const { guarded, guardEvents } = await guardWithTombstones(
112
110
  safe,
113
111
  streamInfo,
114
- correlation,
112
+ deps.correlation,
115
113
  deps.tombstone,
116
114
  skipped
117
115
  );
@@ -129,7 +127,7 @@ async function runCloseCycle(targets, deps) {
129
127
  guarded,
130
128
  seedStates,
131
129
  guardEvents,
132
- correlation
130
+ deps.correlation
133
131
  );
134
132
  return { truncated, skipped };
135
133
  }
@@ -370,8 +368,32 @@ var CorrelateCycle = class {
370
368
  }
371
369
  };
372
370
 
371
+ // src/internal/correlator.ts
372
+ import { randomInt } from "crypto";
373
+ var BASE = 36;
374
+ var SEG_WIDTH = 4;
375
+ var SEG_SPACE = BASE ** SEG_WIDTH;
376
+ function seg(n) {
377
+ return n.toString(BASE).padStart(SEG_WIDTH, "0");
378
+ }
379
+ var defaultCorrelator = ({ state: state2, action: action2 }) => {
380
+ const s = state2.slice(0, SEG_WIDTH).toLowerCase();
381
+ const a = action2.slice(0, SEG_WIDTH).toLowerCase();
382
+ const ts = seg(Date.now() % SEG_SPACE);
383
+ const rnd = seg(randomInt(SEG_SPACE));
384
+ return `${s}-${a}-${ts}${rnd}`;
385
+ };
386
+ function closeCorrelation(correlator, actor) {
387
+ return correlator({
388
+ state: "$close",
389
+ action: "close",
390
+ stream: "$close",
391
+ actor
392
+ });
393
+ }
394
+
373
395
  // src/internal/drain-cycle.ts
374
- import { randomUUID as randomUUID2 } from "crypto";
396
+ import { randomUUID } from "crypto";
375
397
 
376
398
  // src/internal/drain-ratio.ts
377
399
  var RATIO_MIN = 0.2;
@@ -393,7 +415,7 @@ function computeLagLeadRatio(handled, lagging, leading) {
393
415
 
394
416
  // src/internal/drain-cycle.ts
395
417
  async function runDrainCycle(ops, registry, batchHandlers, handle, handleBatch, lagging, leading, eventLimit, leaseMillis, isDeferred) {
396
- const leased = await ops.claim(lagging, leading, randomUUID2(), leaseMillis);
418
+ const leased = await ops.claim(lagging, leading, randomUUID(), leaseMillis);
397
419
  if (!leased.length) return void 0;
398
420
  const active = isDeferred ? leased.filter((l) => !isDeferred(l.stream)) : leased;
399
421
  if (!active.length) {
@@ -912,7 +934,6 @@ var block = (leases) => store().block(leases);
912
934
  var subscribe = (streams) => store().subscribe(streams);
913
935
 
914
936
  // src/internal/event-sourcing.ts
915
- import { randomUUID as randomUUID3 } from "crypto";
916
937
  import { patch } from "@rotorsoft/act-patch";
917
938
  async function snap(snapshot) {
918
939
  try {
@@ -1000,7 +1021,7 @@ async function load(me, stream, callback, asOf) {
1000
1021
  }
1001
1022
  return { event, state: state2, version, patches, snaps, cache_hit, replayed };
1002
1023
  }
1003
- async function action(me, action2, target, payload, reactingTo, skipValidation = false) {
1024
+ async function action(me, action2, target, payload, reactingTo, skipValidation = false, correlator = defaultCorrelator) {
1004
1025
  const { stream, expectedVersion, actor } = target;
1005
1026
  if (!stream) throw new Error("Missing target stream");
1006
1027
  const validated = skipValidation ? payload : validate(action2, payload, me.actions[action2]);
@@ -1046,7 +1067,12 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
1046
1067
  data: skipValidation ? data : validate(name, data, me.events[name])
1047
1068
  }));
1048
1069
  const meta = {
1049
- correlation: reactingTo?.meta.correlation || randomUUID3(),
1070
+ correlation: reactingTo?.meta.correlation || correlator({
1071
+ action: action2,
1072
+ state: me.name,
1073
+ stream,
1074
+ actor: target.actor
1075
+ }),
1050
1076
  causation: {
1051
1077
  action: {
1052
1078
  name: action2,
@@ -1150,12 +1176,21 @@ var traced = (inner, exit, entry) => (async (...args) => {
1150
1176
  exit?.(result, ...args);
1151
1177
  return result;
1152
1178
  });
1153
- function buildEs(logger) {
1179
+ function buildEs(logger, correlator = defaultCorrelator) {
1180
+ const boundAction = (me, actionName, target, payload, reactingTo, skipValidation = false) => action(
1181
+ me,
1182
+ actionName,
1183
+ target,
1184
+ payload,
1185
+ reactingTo,
1186
+ skipValidation,
1187
+ correlator
1188
+ );
1154
1189
  if (logger.level !== "trace") {
1155
1190
  return {
1156
1191
  snap,
1157
1192
  load,
1158
- action,
1193
+ action: boundAction,
1159
1194
  tombstone
1160
1195
  };
1161
1196
  }
@@ -1185,7 +1220,7 @@ function buildEs(logger) {
1185
1220
  );
1186
1221
  }),
1187
1222
  action: traced(
1188
- action,
1223
+ boundAction,
1189
1224
  (snapshots, _me, _action, target) => {
1190
1225
  const committed = snapshots.filter((s) => s.event);
1191
1226
  if (committed.length) {
@@ -1293,7 +1328,8 @@ var Act = class {
1293
1328
  this._states = _states;
1294
1329
  this._batch_handlers = batchHandlers;
1295
1330
  this._scoped = options.scoped ? (fn) => scoped.run(options.scoped, fn) : (fn) => fn();
1296
- this._es = buildEs(this._logger);
1331
+ this._correlator = options.correlator ?? defaultCorrelator;
1332
+ this._es = buildEs(this._logger, this._correlator);
1297
1333
  this._cd = buildDrain(this._logger);
1298
1334
  this._handle = buildHandle({
1299
1335
  logger: this._logger,
@@ -1406,6 +1442,13 @@ var Act = class {
1406
1442
  * path keeps reading fresh `store()`/`cache()` per call, which matters for
1407
1443
  * tests that dispose and re-seed mid-suite. */
1408
1444
  _scoped;
1445
+ /**
1446
+ * Correlation-id generator for originating actions. Bound at
1447
+ * construction from `options.correlator ?? defaultCorrelator`. The
1448
+ * `do()` path passes this into the `_es.action` closure; close-cycle
1449
+ * uses it via {@link closeCorrelation}.
1450
+ */
1451
+ _correlator;
1409
1452
  /** Pre-bound IAct methods reused across drain cycles. Only `do` varies per
1410
1453
  * payload (it captures the triggering event for reactingTo auto-inject). */
1411
1454
  _bound_do = this.do.bind(this);
@@ -1966,12 +2009,14 @@ var Act = class {
1966
2009
  if (!targets.length) return { truncated: /* @__PURE__ */ new Map(), skipped: [] };
1967
2010
  return this._scoped(async () => {
1968
2011
  await this.correlate({ limit: 1e3 });
2012
+ const closeActor = { id: "$close", name: "close" };
1969
2013
  const result = await runCloseCycle(targets, {
1970
2014
  reactiveEventsSize: this._reactive_events.size,
1971
2015
  eventToState: this._event_to_state,
1972
2016
  load: this._es.load,
1973
2017
  tombstone: this._es.tombstone,
1974
- logger: this._logger
2018
+ logger: this._logger,
2019
+ correlation: closeCorrelation(this._correlator, closeActor)
1975
2020
  });
1976
2021
  this.emit("closed", result);
1977
2022
  return result;