@rotorsoft/act 0.21.0 → 0.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.
@@ -225,16 +225,18 @@ export interface Store extends Disposable {
225
225
  * Upserts stream entries so they become visible to {@link claim}. Used by
226
226
  * `correlate()` to register dynamically discovered reaction target streams.
227
227
  *
228
+ * Also returns the current maximum watermark across all subscribed streams,
229
+ * used internally for correlation checkpoint initialization on cold start.
230
+ *
228
231
  * @param streams - Streams to register with optional source hint
229
- * @returns Number of newly registered streams (excludes already-known streams)
232
+ * @returns `subscribed` count of newly registered streams, `watermark` max `at` across all streams
230
233
  *
231
234
  * @example
232
235
  * ```typescript
233
- * const count = await store().subscribe([
236
+ * const { subscribed, watermark } = await store().subscribe([
234
237
  * { stream: "stats-user-1", source: "user-1" },
235
238
  * { stream: "stats-user-2", source: "user-2" },
236
239
  * ]);
237
- * console.log(`Registered ${count} new streams`);
238
240
  * ```
239
241
  *
240
242
  * @see {@link claim} for discovering and leasing registered streams
@@ -242,7 +244,10 @@ export interface Store extends Disposable {
242
244
  subscribe: (streams: Array<{
243
245
  stream: string;
244
246
  source?: string;
245
- }>) => Promise<number>;
247
+ }>) => Promise<{
248
+ subscribed: number;
249
+ watermark: number;
250
+ }>;
246
251
  /**
247
252
  * Blocks streams after persistent processing failures.
248
253
  *
@@ -1 +1 @@
1
- {"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../../../src/types/ports.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../../../src/types/ports.ts"],"names":[],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -496,18 +496,22 @@ var InMemoryStore = class {
496
496
  /**
497
497
  * Registers streams for event processing.
498
498
  * @param streams - Streams to register with optional source.
499
- * @returns Number of newly registered streams.
499
+ * @returns subscribed count and current max watermark.
500
500
  */
501
501
  async subscribe(streams) {
502
502
  await sleep();
503
- let count = 0;
503
+ let subscribed = 0;
504
504
  for (const { stream, source } of streams) {
505
505
  if (!this._streams.has(stream)) {
506
506
  this._streams.set(stream, new InMemoryStream(stream, source));
507
- count++;
507
+ subscribed++;
508
508
  }
509
509
  }
510
- return count;
510
+ let watermark = -1;
511
+ for (const s of this._streams.values()) {
512
+ if (s.at > watermark) watermark = s.at;
513
+ }
514
+ return { subscribed, watermark };
511
515
  }
512
516
  /**
513
517
  * Acknowledge completion of processing for leased streams.
@@ -794,15 +798,21 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
794
798
  // src/act.ts
795
799
  var tracer = build_tracer(config().logLevel);
796
800
  var Act = class {
797
- /**
798
- * Create a new Act orchestrator.
799
- *
800
- * @param registry The registry of state, event, and action schemas
801
- * @param states Map of state names to their (potentially merged) state definitions
802
- */
803
801
  constructor(registry, _states = /* @__PURE__ */ new Map()) {
804
802
  this.registry = registry;
805
803
  this._states = _states;
804
+ const statics = [];
805
+ for (const register of Object.values(this.registry.events)) {
806
+ for (const reaction of register.reactions.values()) {
807
+ if (typeof reaction.resolver === "function") {
808
+ this._has_dynamic_resolvers = true;
809
+ } else if (reaction.resolver) {
810
+ const r = reaction.resolver;
811
+ statics.push({ stream: r.target, source: r.source });
812
+ }
813
+ }
814
+ }
815
+ this._static_targets = statics;
806
816
  dispose(() => {
807
817
  this._emitter.removeAllListeners();
808
818
  this.stop_correlations();
@@ -816,6 +826,10 @@ var Act = class {
816
826
  _correlation_timer = void 0;
817
827
  _settle_timer = void 0;
818
828
  _settling = false;
829
+ _correlation_checkpoint = -1;
830
+ _subscribed_statics = /* @__PURE__ */ new Set();
831
+ _has_dynamic_resolvers = false;
832
+ _correlation_initialized = false;
819
833
  emit(event, args) {
820
834
  return this._emitter.emit(event, args);
821
835
  }
@@ -827,6 +841,14 @@ var Act = class {
827
841
  this._emitter.off(event, listener);
828
842
  return this;
829
843
  }
844
+ /**
845
+ * Create a new Act orchestrator.
846
+ *
847
+ * @param registry The registry of state, event, and action schemas
848
+ * @param states Map of state names to their (potentially merged) state definitions
849
+ */
850
+ /** Static resolver targets collected at build time */
851
+ _static_targets;
830
852
  /**
831
853
  * Executes an action on a state instance, committing resulting events.
832
854
  *
@@ -1236,37 +1258,67 @@ var Act = class {
1236
1258
  * @see {@link start_correlations} for automatic periodic correlation
1237
1259
  * @see {@link stop_correlations} to stop automatic correlation
1238
1260
  */
1261
+ /**
1262
+ * Initialize correlation state on first call.
1263
+ * - Reads max(at) from store as cold-start checkpoint
1264
+ * - Subscribes static resolver targets (idempotent upsert)
1265
+ * - Populates the subscribed statics set
1266
+ * @internal
1267
+ */
1268
+ async _init_correlation() {
1269
+ if (this._correlation_initialized) return;
1270
+ this._correlation_initialized = true;
1271
+ const { watermark } = await store().subscribe(this._static_targets);
1272
+ this._correlation_checkpoint = watermark;
1273
+ for (const { stream } of this._static_targets) {
1274
+ this._subscribed_statics.add(stream);
1275
+ }
1276
+ }
1239
1277
  async correlate(query = { after: -1, limit: 10 }) {
1278
+ await this._init_correlation();
1279
+ if (!this._has_dynamic_resolvers)
1280
+ return { subscribed: 0, last_id: this._correlation_checkpoint };
1281
+ const after = Math.max(this._correlation_checkpoint, query.after || -1);
1240
1282
  const correlated = /* @__PURE__ */ new Map();
1241
- let last_id = query.after || -1;
1242
- await store().query((event) => {
1243
- last_id = event.id;
1244
- const register = this.registry.events[event.name];
1245
- if (register) {
1246
- for (const reaction of register.reactions.values()) {
1247
- const resolved = typeof reaction.resolver === "function" ? reaction.resolver(event) : reaction.resolver;
1248
- if (resolved) {
1249
- const entry = correlated.get(resolved.target) || {
1250
- source: resolved.source,
1251
- payloads: []
1252
- };
1253
- entry.payloads.push({
1254
- ...reaction,
1255
- source: resolved.source,
1256
- event
1257
- });
1258
- correlated.set(resolved.target, entry);
1283
+ let last_id = after;
1284
+ await store().query(
1285
+ (event) => {
1286
+ last_id = event.id;
1287
+ const register = this.registry.events[event.name];
1288
+ if (register) {
1289
+ for (const reaction of register.reactions.values()) {
1290
+ if (typeof reaction.resolver !== "function") continue;
1291
+ const resolved = reaction.resolver(event);
1292
+ if (resolved && !this._subscribed_statics.has(resolved.target)) {
1293
+ const entry = correlated.get(resolved.target) || {
1294
+ source: resolved.source,
1295
+ payloads: []
1296
+ };
1297
+ entry.payloads.push({
1298
+ ...reaction,
1299
+ source: resolved.source,
1300
+ event
1301
+ });
1302
+ correlated.set(resolved.target, entry);
1303
+ }
1259
1304
  }
1260
1305
  }
1261
- }
1262
- }, query);
1306
+ },
1307
+ { ...query, after }
1308
+ );
1309
+ this._correlation_checkpoint = last_id;
1263
1310
  if (correlated.size) {
1264
1311
  const streams = [...correlated.entries()].map(([stream, { source }]) => ({
1265
1312
  stream,
1266
1313
  source
1267
1314
  }));
1268
- const subscribed = await store().subscribe(streams);
1269
- subscribed && tracer.correlated(streams);
1315
+ const { subscribed } = await store().subscribe(streams);
1316
+ if (subscribed) {
1317
+ tracer.correlated(streams);
1318
+ for (const { stream } of streams) {
1319
+ this._subscribed_statics.add(stream);
1320
+ }
1321
+ }
1270
1322
  return { subscribed, last_id };
1271
1323
  }
1272
1324
  return { subscribed: 0, last_id };
@@ -1329,10 +1381,8 @@ var Act = class {
1329
1381
  start_correlations(query = {}, frequency = 1e4, callback) {
1330
1382
  if (this._correlation_timer) return false;
1331
1383
  const limit = query.limit || 100;
1332
- let after = query.after || -1;
1333
1384
  this._correlation_timer = setInterval(
1334
- () => this.correlate({ ...query, after, limit }).then((result) => {
1335
- after = result.last_id;
1385
+ () => this.correlate({ ...query, after: this._correlation_checkpoint, limit }).then((result) => {
1336
1386
  if (callback && result.subscribed) callback(result.subscribed);
1337
1387
  }).catch(console.error),
1338
1388
  frequency
@@ -1415,9 +1465,13 @@ var Act = class {
1415
1465
  if (this._settling) return;
1416
1466
  this._settling = true;
1417
1467
  (async () => {
1468
+ await this._init_correlation();
1418
1469
  let lastDrain;
1419
1470
  for (let i = 0; i < maxPasses; i++) {
1420
- const { subscribed } = await this.correlate(correlateQuery);
1471
+ const { subscribed } = await this.correlate({
1472
+ ...correlateQuery,
1473
+ after: this._correlation_checkpoint
1474
+ });
1421
1475
  if (subscribed === 0 && i > 0) break;
1422
1476
  lastDrain = await this.drain(drainOptions);
1423
1477
  if (!lastDrain.acked.length && !lastDrain.blocked.length) break;