@rotorsoft/act 0.5.1 → 0.5.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 (40) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/@types/act-builder.d.ts +66 -2
  3. package/dist/@types/act-builder.d.ts.map +1 -1
  4. package/dist/@types/act.d.ts +77 -21
  5. package/dist/@types/act.d.ts.map +1 -1
  6. package/dist/@types/adapters/InMemoryStore.d.ts +49 -2
  7. package/dist/@types/adapters/InMemoryStore.d.ts.map +1 -1
  8. package/dist/@types/config.d.ts +34 -11
  9. package/dist/@types/config.d.ts.map +1 -1
  10. package/dist/@types/event-sourcing.d.ts +30 -9
  11. package/dist/@types/event-sourcing.d.ts.map +1 -1
  12. package/dist/@types/index.d.ts +3 -2
  13. package/dist/@types/index.d.ts.map +1 -1
  14. package/dist/@types/ports.d.ts +51 -4
  15. package/dist/@types/ports.d.ts.map +1 -1
  16. package/dist/@types/signals.d.ts +2 -0
  17. package/dist/@types/signals.d.ts.map +1 -0
  18. package/dist/@types/state-builder.d.ts +54 -3
  19. package/dist/@types/state-builder.d.ts.map +1 -1
  20. package/dist/@types/types/action.d.ts +105 -0
  21. package/dist/@types/types/action.d.ts.map +1 -1
  22. package/dist/@types/types/errors.d.ts +33 -4
  23. package/dist/@types/types/errors.d.ts.map +1 -1
  24. package/dist/@types/types/index.d.ts +28 -0
  25. package/dist/@types/types/index.d.ts.map +1 -1
  26. package/dist/@types/types/ports.d.ts +53 -0
  27. package/dist/@types/types/ports.d.ts.map +1 -1
  28. package/dist/@types/types/reaction.d.ts +51 -0
  29. package/dist/@types/types/reaction.d.ts.map +1 -1
  30. package/dist/@types/types/registry.d.ts +27 -0
  31. package/dist/@types/types/registry.d.ts.map +1 -1
  32. package/dist/@types/types/schemas.d.ts +48 -12
  33. package/dist/@types/types/schemas.d.ts.map +1 -1
  34. package/dist/@types/utils.d.ts +46 -5
  35. package/dist/@types/utils.d.ts.map +1 -1
  36. package/dist/index.cjs +146 -76
  37. package/dist/index.cjs.map +1 -1
  38. package/dist/index.js +144 -75
  39. package/dist/index.js.map +1 -1
  40. package/package.json +1 -2
package/dist/index.cjs CHANGED
@@ -41,6 +41,7 @@ __export(index_exports, {
41
41
  ExitCodes: () => ExitCodes,
42
42
  InvariantError: () => InvariantError,
43
43
  LogLevels: () => LogLevels,
44
+ PackageSchema: () => PackageSchema,
44
45
  QuerySchema: () => QuerySchema,
45
46
  SNAP_EVENT: () => SNAP_EVENT,
46
47
  TargetSchema: () => TargetSchema,
@@ -62,9 +63,8 @@ __export(index_exports, {
62
63
  });
63
64
  module.exports = __toCommonJS(index_exports);
64
65
 
65
- // src/config.ts
66
- var fs = __toESM(require("fs"), 1);
67
- var import_v43 = require("zod/v4");
66
+ // src/ports.ts
67
+ var import_pino = require("pino");
68
68
 
69
69
  // src/types/errors.ts
70
70
  var Errors = {
@@ -101,6 +101,13 @@ var ConcurrencyError = class extends Error {
101
101
  }
102
102
  };
103
103
 
104
+ // src/utils.ts
105
+ var import_v43 = require("zod/v4");
106
+
107
+ // src/config.ts
108
+ var fs = __toESM(require("fs"), 1);
109
+ var import_v42 = require("zod/v4");
110
+
104
111
  // src/types/schemas.ts
105
112
  var import_v4 = require("zod/v4");
106
113
  var ZodEmpty = import_v4.z.record(import_v4.z.string(), import_v4.z.never());
@@ -179,8 +186,36 @@ var LogLevels = [
179
186
  "trace"
180
187
  ];
181
188
 
189
+ // src/config.ts
190
+ var PackageSchema = import_v42.z.object({
191
+ name: import_v42.z.string().min(1),
192
+ version: import_v42.z.string().min(1),
193
+ description: import_v42.z.string().min(1).optional(),
194
+ author: import_v42.z.object({ name: import_v42.z.string().min(1), email: import_v42.z.string().optional() }).optional().or(import_v42.z.string().min(1)).optional(),
195
+ license: import_v42.z.string().min(1).optional(),
196
+ dependencies: import_v42.z.record(import_v42.z.string(), import_v42.z.string()).optional()
197
+ });
198
+ var getPackage = () => {
199
+ const pkg2 = fs.readFileSync("package.json");
200
+ return JSON.parse(pkg2.toString());
201
+ };
202
+ var BaseSchema = PackageSchema.extend({
203
+ env: import_v42.z.enum(Environments),
204
+ logLevel: import_v42.z.enum(LogLevels),
205
+ logSingleLine: import_v42.z.boolean(),
206
+ sleepMs: import_v42.z.number().int().min(0).max(5e3)
207
+ });
208
+ var { NODE_ENV, LOG_LEVEL, LOG_SINGLE_LINE, SLEEP_MS } = process.env;
209
+ var env = NODE_ENV || "development";
210
+ var logLevel = LOG_LEVEL || (NODE_ENV === "test" ? "error" : LOG_LEVEL === "production" ? "info" : "trace");
211
+ var logSingleLine = (LOG_SINGLE_LINE || "true") === "true";
212
+ var sleepMs = parseInt(NODE_ENV === "test" ? "0" : SLEEP_MS ?? "100");
213
+ var pkg = getPackage();
214
+ var config = () => {
215
+ return extend({ ...pkg, env, logLevel, logSingleLine, sleepMs }, BaseSchema);
216
+ };
217
+
182
218
  // src/utils.ts
183
- var import_v42 = require("zod/v4");
184
219
  var UNMERGEABLES = [
185
220
  RegExp,
186
221
  Date,
@@ -223,7 +258,7 @@ var validate = (target, payload, schema) => {
223
258
  throw new ValidationError(
224
259
  target,
225
260
  payload,
226
- (0, import_v42.prettifyError)(error)
261
+ (0, import_v43.prettifyError)(error)
227
262
  );
228
263
  }
229
264
  throw new ValidationError(target, payload, error);
@@ -237,38 +272,6 @@ async function sleep(ms) {
237
272
  return new Promise((resolve) => setTimeout(resolve, ms ?? config().sleepMs));
238
273
  }
239
274
 
240
- // src/config.ts
241
- var PackageSchema = import_v43.z.object({
242
- name: import_v43.z.string().min(1),
243
- version: import_v43.z.string().min(1),
244
- description: import_v43.z.string().min(1),
245
- author: import_v43.z.object({ name: import_v43.z.string().min(1), email: import_v43.z.string().optional() }).or(import_v43.z.string().min(1)),
246
- license: import_v43.z.string().min(1),
247
- dependencies: import_v43.z.record(import_v43.z.string(), import_v43.z.string())
248
- });
249
- var getPackage = () => {
250
- const pkg2 = fs.readFileSync("package.json");
251
- return JSON.parse(pkg2.toString());
252
- };
253
- var BaseSchema = PackageSchema.extend({
254
- env: import_v43.z.enum(Environments),
255
- logLevel: import_v43.z.enum(LogLevels),
256
- logSingleLine: import_v43.z.boolean(),
257
- sleepMs: import_v43.z.number().int().min(0).max(5e3)
258
- });
259
- var { NODE_ENV, LOG_LEVEL, LOG_SINGLE_LINE, SLEEP_MS } = process.env;
260
- var env = NODE_ENV || "development";
261
- var logLevel = LOG_LEVEL || (NODE_ENV === "test" ? "error" : LOG_LEVEL === "production" ? "info" : "trace");
262
- var logSingleLine = (LOG_SINGLE_LINE || "true") === "true";
263
- var sleepMs = parseInt(NODE_ENV === "test" ? "0" : SLEEP_MS ?? "100");
264
- var pkg = getPackage();
265
- var config = () => {
266
- return extend({ ...pkg, env, logLevel, logSingleLine, sleepMs }, BaseSchema);
267
- };
268
-
269
- // src/ports.ts
270
- var import_pino = require("pino");
271
-
272
275
  // src/adapters/InMemoryStore.ts
273
276
  var InMemoryStream = class {
274
277
  constructor(stream) {
@@ -278,12 +281,21 @@ var InMemoryStream = class {
278
281
  _retry = -1;
279
282
  _lease;
280
283
  _blocked = false;
284
+ /**
285
+ * Attempt to lease this stream for processing.
286
+ * @param lease - Lease request.
287
+ * @returns The granted lease or undefined if blocked.
288
+ */
281
289
  lease(lease) {
282
290
  if (!this._blocked && lease.at > this._at) {
283
291
  this._lease = { ...lease, retry: this._retry + 1 };
284
292
  return this._lease;
285
293
  }
286
294
  }
295
+ /**
296
+ * Acknowledge completion of processing for this stream.
297
+ * @param lease - Lease to acknowledge.
298
+ */
287
299
  ack(lease) {
288
300
  if (this._lease && lease.at >= this._at) {
289
301
  this._retry = lease.retry;
@@ -301,17 +313,35 @@ var InMemoryStore = class {
301
313
  _events = [];
302
314
  // stored stream positions and other metadata
303
315
  _streams = /* @__PURE__ */ new Map();
316
+ /**
317
+ * Dispose of the store and clear all events.
318
+ * @returns Promise that resolves when disposal is complete.
319
+ */
304
320
  async dispose() {
305
321
  await sleep();
306
322
  this._events.length = 0;
307
323
  }
324
+ /**
325
+ * Seed the store with initial data (no-op for in-memory).
326
+ * @returns Promise that resolves when seeding is complete.
327
+ */
308
328
  async seed() {
309
329
  await sleep();
310
330
  }
331
+ /**
332
+ * Drop all data from the store.
333
+ * @returns Promise that resolves when the store is cleared.
334
+ */
311
335
  async drop() {
312
336
  await sleep();
313
337
  this._events.length = 0;
314
338
  }
339
+ /**
340
+ * Query events in the store, optionally filtered by query options.
341
+ * @param callback - Function to call for each event.
342
+ * @param query - Optional query options.
343
+ * @returns The number of events processed.
344
+ */
315
345
  async query(callback, query) {
316
346
  await sleep();
317
347
  const {
@@ -339,10 +369,19 @@ var InMemoryStore = class {
339
369
  }
340
370
  return count;
341
371
  }
372
+ /**
373
+ * Commit one or more events to a stream.
374
+ * @param stream - The stream name.
375
+ * @param msgs - The events/messages to commit.
376
+ * @param meta - Event metadata.
377
+ * @param expectedVersion - Optional optimistic concurrency check.
378
+ * @returns The committed events with metadata.
379
+ * @throws ConcurrencyError if expectedVersion does not match.
380
+ */
342
381
  async commit(stream, msgs, meta, expectedVersion) {
343
382
  await sleep();
344
383
  const instance = this._events.filter((e) => e.stream === stream);
345
- if (expectedVersion && instance.length - 1 !== expectedVersion)
384
+ if (typeof expectedVersion === "number" && instance.length - 1 !== expectedVersion)
346
385
  throw new ConcurrencyError(
347
386
  instance.length - 1,
348
387
  msgs,
@@ -365,7 +404,9 @@ var InMemoryStore = class {
365
404
  });
366
405
  }
367
406
  /**
368
- * Fetches new events from stream watermarks
407
+ * Fetches new events from stream watermarks for processing.
408
+ * @param limit - Maximum number of streams to fetch.
409
+ * @returns Fetched streams and events.
369
410
  */
370
411
  async fetch(limit) {
371
412
  const streams = [...this._streams.values()].filter((s) => !s._blocked).sort((a, b) => a._at - b._at).slice(0, limit);
@@ -380,6 +421,11 @@ var InMemoryStore = class {
380
421
  });
381
422
  return { streams: streams.map(({ stream }) => stream), events };
382
423
  }
424
+ /**
425
+ * Lease streams for processing (e.g., for distributed consumers).
426
+ * @param leases - Lease requests.
427
+ * @returns Granted leases.
428
+ */
383
429
  async lease(leases) {
384
430
  await sleep();
385
431
  return leases.map((lease) => {
@@ -388,6 +434,10 @@ var InMemoryStore = class {
388
434
  return stream.lease(lease);
389
435
  }).filter((l) => !!l);
390
436
  }
437
+ /**
438
+ * Acknowledge completion of processing for leased streams.
439
+ * @param leases - Leases to acknowledge.
440
+ */
391
441
  async ack(leases) {
392
442
  await sleep();
393
443
  leases.forEach((lease) => this._streams.get(lease.stream)?.ack(lease));
@@ -440,6 +490,24 @@ var store = port(function store2(adapter) {
440
490
  return adapter || new InMemoryStore();
441
491
  });
442
492
 
493
+ // src/signals.ts
494
+ process.once("SIGINT", async (arg) => {
495
+ logger.info(arg, "SIGINT");
496
+ await disposeAndExit("EXIT");
497
+ });
498
+ process.once("SIGTERM", async (arg) => {
499
+ logger.info(arg, "SIGTERM");
500
+ await disposeAndExit("EXIT");
501
+ });
502
+ process.once("uncaughtException", async (arg) => {
503
+ logger.error(arg, "Uncaught Exception");
504
+ await disposeAndExit("ERROR");
505
+ });
506
+ process.once("unhandledRejection", async (arg) => {
507
+ logger.error(arg, "Unhandled Rejection");
508
+ await disposeAndExit("ERROR");
509
+ });
510
+
443
511
  // src/act.ts
444
512
  var import_crypto2 = require("crypto");
445
513
  var import_events = __toESM(require("events"), 1);
@@ -555,6 +623,12 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
555
623
 
556
624
  // src/act.ts
557
625
  var Act = class {
626
+ /**
627
+ * Create a new Act orchestrator.
628
+ *
629
+ * @param registry The registry of state, event, and action schemas
630
+ * @param drainLimit The maximum number of events to drain per cycle
631
+ */
558
632
  constructor(registry, drainLimit) {
559
633
  this.registry = registry;
560
634
  this.drainLimit = drainLimit;
@@ -572,17 +646,18 @@ var Act = class {
572
646
  return this;
573
647
  }
574
648
  /**
575
- * Executes an action and emits an event to be committed by the store.
649
+ * Executes an action (command) against a state machine, emitting and committing the resulting event(s).
576
650
  *
577
651
  * @template K The type of action to execute
578
- * @template T The type of target
579
- * @template P The type of payloads
580
- * @param action The action to execute
581
- * @param target The target of the action
582
- * @param payload The payload of the action
583
- * @param reactingTo The event that the action is reacting to
584
- * @param skipValidation Whether to skip validation
585
- * @returns The snapshot of the committed Event
652
+ * @param action The action name (key of the action schema)
653
+ * @param target The target (stream and actor) for the action
654
+ * @param payload The action payload (validated against the schema)
655
+ * @param reactingTo (Optional) The event this action is reacting to
656
+ * @param skipValidation (Optional) If true, skips schema validation (not recommended)
657
+ * @returns The snapshot of the committed event
658
+ *
659
+ * @example
660
+ * await app.do("increment", { stream: "counter1", actor }, { by: 1 });
586
661
  */
587
662
  async do(action2, target, payload, reactingTo, skipValidation = false) {
588
663
  const snapshot = await action(
@@ -597,25 +672,31 @@ var Act = class {
597
672
  return snapshot;
598
673
  }
599
674
  /**
600
- * Loads a snapshot of the state from the store.
675
+ * Loads the current state snapshot for a given state machine and stream.
601
676
  *
602
677
  * @template SX The type of state
603
678
  * @template EX The type of events
604
679
  * @template AX The type of actions
605
- * @param state The state to load
606
- * @param stream The stream to load
607
- * @param callback The callback to call with the snapshot
680
+ * @param state The state machine definition
681
+ * @param stream The stream (instance) to load
682
+ * @param callback (Optional) Callback to receive the loaded snapshot
608
683
  * @returns The snapshot of the loaded state
684
+ *
685
+ * @example
686
+ * const snapshot = await app.load(Counter, "counter1");
609
687
  */
610
688
  async load(state2, stream, callback) {
611
689
  return await load(state2, stream, callback);
612
690
  }
613
691
  /**
614
- * Queries the store for events.
692
+ * Query the event store for events matching a filter.
615
693
  *
616
- * @param query The query to execute
617
- * @param callback The callback to call with the events
618
- * @returns The query result
694
+ * @param query The query filter (e.g., by stream, event name, or time range)
695
+ * @param callback (Optional) Callback for each event found
696
+ * @returns An object with the first and last event found, and the total count
697
+ *
698
+ * @example
699
+ * const { count } = await app.query({ stream: "counter1" }, (event) => console.log(event));
619
700
  */
620
701
  async query(query, callback) {
621
702
  let first = void 0, last = void 0;
@@ -629,6 +710,7 @@ var Act = class {
629
710
  /**
630
711
  * Handles leased reactions.
631
712
  *
713
+ * @internal
632
714
  * @param lease The lease to handle
633
715
  * @param reactions The reactions to handle
634
716
  * @returns The lease
@@ -658,9 +740,14 @@ var Act = class {
658
740
  }
659
741
  drainLocked = false;
660
742
  /**
661
- * Drains events from the store.
743
+ * Drains and processes events from the store, triggering reactions and updating state.
744
+ *
745
+ * This is typically called in a background loop or after committing new events.
662
746
  *
663
- * @returns The number of drained events
747
+ * @returns The number of events drained and processed
748
+ *
749
+ * @example
750
+ * await app.drain();
664
751
  */
665
752
  async drain() {
666
753
  if (this.drainLocked) return 0;
@@ -883,24 +970,6 @@ function action_builder(state2) {
883
970
  }
884
971
  };
885
972
  }
886
-
887
- // src/index.ts
888
- process.once("SIGINT", async (arg) => {
889
- logger.info(arg, "SIGINT");
890
- await disposeAndExit("EXIT");
891
- });
892
- process.once("SIGTERM", async (arg) => {
893
- logger.info(arg, "SIGTERM");
894
- await disposeAndExit("EXIT");
895
- });
896
- process.once("uncaughtException", async (arg) => {
897
- logger.error(arg, "Uncaught Exception");
898
- await disposeAndExit("ERROR");
899
- });
900
- process.once("unhandledRejection", async (arg) => {
901
- logger.error(arg, "Unhandled Rejection");
902
- await disposeAndExit("ERROR");
903
- });
904
973
  // Annotate the CommonJS export names for ESM import in node:
905
974
  0 && (module.exports = {
906
975
  Act,
@@ -914,6 +983,7 @@ process.once("unhandledRejection", async (arg) => {
914
983
  ExitCodes,
915
984
  InvariantError,
916
985
  LogLevels,
986
+ PackageSchema,
917
987
  QuerySchema,
918
988
  SNAP_EVENT,
919
989
  TargetSchema,