@rotorsoft/act 0.5.1 → 0.5.3

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 +43 -107
  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 -97
  37. package/dist/index.cjs.map +1 -1
  38. package/dist/index.js +144 -95
  39. package/dist/index.js.map +1 -1
  40. package/package.json +3 -4
package/dist/index.cjs CHANGED
@@ -41,13 +41,13 @@ __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,
47
48
  ValidationError: () => ValidationError,
48
49
  ZodEmpty: () => ZodEmpty,
49
50
  act: () => act,
50
- buildSnapshotSchema: () => buildSnapshotSchema,
51
51
  config: () => config,
52
52
  dispose: () => dispose,
53
53
  disposeAndExit: () => disposeAndExit,
@@ -62,9 +62,8 @@ __export(index_exports, {
62
62
  });
63
63
  module.exports = __toCommonJS(index_exports);
64
64
 
65
- // src/config.ts
66
- var fs = __toESM(require("fs"), 1);
67
- var import_v43 = require("zod/v4");
65
+ // src/ports.ts
66
+ var import_pino = require("pino");
68
67
 
69
68
  // src/types/errors.ts
70
69
  var Errors = {
@@ -101,6 +100,13 @@ var ConcurrencyError = class extends Error {
101
100
  }
102
101
  };
103
102
 
103
+ // src/utils.ts
104
+ var import_v43 = require("zod/v4");
105
+
106
+ // src/config.ts
107
+ var fs = __toESM(require("fs"), 1);
108
+ var import_v42 = require("zod/v4");
109
+
104
110
  // src/types/schemas.ts
105
111
  var import_v4 = require("zod/v4");
106
112
  var ZodEmpty = import_v4.z.record(import_v4.z.string(), import_v4.z.never());
@@ -132,25 +138,6 @@ var CommittedMetaSchema = import_v4.z.object({
132
138
  created: import_v4.z.date(),
133
139
  meta: EventMetaSchema
134
140
  }).readonly();
135
- function buildSnapshotSchema(s) {
136
- const events = Object.entries(s.events).map(
137
- ([name, zod]) => import_v4.z.object({
138
- name: import_v4.z.literal(name),
139
- data: zod,
140
- id: import_v4.z.number(),
141
- stream: import_v4.z.string(),
142
- version: import_v4.z.number(),
143
- created: import_v4.z.date(),
144
- meta: EventMetaSchema
145
- })
146
- );
147
- return import_v4.z.object({
148
- state: s.state.readonly(),
149
- event: import_v4.z.union([events[0], events[1], ...events.slice(2)]).optional(),
150
- patches: import_v4.z.number(),
151
- snaps: import_v4.z.number()
152
- });
153
- }
154
141
  var QuerySchema = import_v4.z.object({
155
142
  stream: import_v4.z.string().optional(),
156
143
  names: import_v4.z.string().array().optional(),
@@ -179,8 +166,36 @@ var LogLevels = [
179
166
  "trace"
180
167
  ];
181
168
 
169
+ // src/config.ts
170
+ var PackageSchema = import_v42.z.object({
171
+ name: import_v42.z.string().min(1),
172
+ version: import_v42.z.string().min(1),
173
+ description: import_v42.z.string().min(1).optional(),
174
+ 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(),
175
+ license: import_v42.z.string().min(1).optional(),
176
+ dependencies: import_v42.z.record(import_v42.z.string(), import_v42.z.string()).optional()
177
+ });
178
+ var getPackage = () => {
179
+ const pkg2 = fs.readFileSync("package.json");
180
+ return JSON.parse(pkg2.toString());
181
+ };
182
+ var BaseSchema = PackageSchema.extend({
183
+ env: import_v42.z.enum(Environments),
184
+ logLevel: import_v42.z.enum(LogLevels),
185
+ logSingleLine: import_v42.z.boolean(),
186
+ sleepMs: import_v42.z.number().int().min(0).max(5e3)
187
+ });
188
+ var { NODE_ENV, LOG_LEVEL, LOG_SINGLE_LINE, SLEEP_MS } = process.env;
189
+ var env = NODE_ENV || "development";
190
+ var logLevel = LOG_LEVEL || (NODE_ENV === "test" ? "error" : LOG_LEVEL === "production" ? "info" : "trace");
191
+ var logSingleLine = (LOG_SINGLE_LINE || "true") === "true";
192
+ var sleepMs = parseInt(NODE_ENV === "test" ? "0" : SLEEP_MS ?? "100");
193
+ var pkg = getPackage();
194
+ var config = () => {
195
+ return extend({ ...pkg, env, logLevel, logSingleLine, sleepMs }, BaseSchema);
196
+ };
197
+
182
198
  // src/utils.ts
183
- var import_v42 = require("zod/v4");
184
199
  var UNMERGEABLES = [
185
200
  RegExp,
186
201
  Date,
@@ -223,7 +238,7 @@ var validate = (target, payload, schema) => {
223
238
  throw new ValidationError(
224
239
  target,
225
240
  payload,
226
- (0, import_v42.prettifyError)(error)
241
+ (0, import_v43.prettifyError)(error)
227
242
  );
228
243
  }
229
244
  throw new ValidationError(target, payload, error);
@@ -237,38 +252,6 @@ async function sleep(ms) {
237
252
  return new Promise((resolve) => setTimeout(resolve, ms ?? config().sleepMs));
238
253
  }
239
254
 
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
255
  // src/adapters/InMemoryStore.ts
273
256
  var InMemoryStream = class {
274
257
  constructor(stream) {
@@ -278,12 +261,21 @@ var InMemoryStream = class {
278
261
  _retry = -1;
279
262
  _lease;
280
263
  _blocked = false;
264
+ /**
265
+ * Attempt to lease this stream for processing.
266
+ * @param lease - Lease request.
267
+ * @returns The granted lease or undefined if blocked.
268
+ */
281
269
  lease(lease) {
282
270
  if (!this._blocked && lease.at > this._at) {
283
271
  this._lease = { ...lease, retry: this._retry + 1 };
284
272
  return this._lease;
285
273
  }
286
274
  }
275
+ /**
276
+ * Acknowledge completion of processing for this stream.
277
+ * @param lease - Lease to acknowledge.
278
+ */
287
279
  ack(lease) {
288
280
  if (this._lease && lease.at >= this._at) {
289
281
  this._retry = lease.retry;
@@ -301,17 +293,35 @@ var InMemoryStore = class {
301
293
  _events = [];
302
294
  // stored stream positions and other metadata
303
295
  _streams = /* @__PURE__ */ new Map();
296
+ /**
297
+ * Dispose of the store and clear all events.
298
+ * @returns Promise that resolves when disposal is complete.
299
+ */
304
300
  async dispose() {
305
301
  await sleep();
306
302
  this._events.length = 0;
307
303
  }
304
+ /**
305
+ * Seed the store with initial data (no-op for in-memory).
306
+ * @returns Promise that resolves when seeding is complete.
307
+ */
308
308
  async seed() {
309
309
  await sleep();
310
310
  }
311
+ /**
312
+ * Drop all data from the store.
313
+ * @returns Promise that resolves when the store is cleared.
314
+ */
311
315
  async drop() {
312
316
  await sleep();
313
317
  this._events.length = 0;
314
318
  }
319
+ /**
320
+ * Query events in the store, optionally filtered by query options.
321
+ * @param callback - Function to call for each event.
322
+ * @param query - Optional query options.
323
+ * @returns The number of events processed.
324
+ */
315
325
  async query(callback, query) {
316
326
  await sleep();
317
327
  const {
@@ -339,10 +349,19 @@ var InMemoryStore = class {
339
349
  }
340
350
  return count;
341
351
  }
352
+ /**
353
+ * Commit one or more events to a stream.
354
+ * @param stream - The stream name.
355
+ * @param msgs - The events/messages to commit.
356
+ * @param meta - Event metadata.
357
+ * @param expectedVersion - Optional optimistic concurrency check.
358
+ * @returns The committed events with metadata.
359
+ * @throws ConcurrencyError if expectedVersion does not match.
360
+ */
342
361
  async commit(stream, msgs, meta, expectedVersion) {
343
362
  await sleep();
344
363
  const instance = this._events.filter((e) => e.stream === stream);
345
- if (expectedVersion && instance.length - 1 !== expectedVersion)
364
+ if (typeof expectedVersion === "number" && instance.length - 1 !== expectedVersion)
346
365
  throw new ConcurrencyError(
347
366
  instance.length - 1,
348
367
  msgs,
@@ -365,7 +384,9 @@ var InMemoryStore = class {
365
384
  });
366
385
  }
367
386
  /**
368
- * Fetches new events from stream watermarks
387
+ * Fetches new events from stream watermarks for processing.
388
+ * @param limit - Maximum number of streams to fetch.
389
+ * @returns Fetched streams and events.
369
390
  */
370
391
  async fetch(limit) {
371
392
  const streams = [...this._streams.values()].filter((s) => !s._blocked).sort((a, b) => a._at - b._at).slice(0, limit);
@@ -380,6 +401,11 @@ var InMemoryStore = class {
380
401
  });
381
402
  return { streams: streams.map(({ stream }) => stream), events };
382
403
  }
404
+ /**
405
+ * Lease streams for processing (e.g., for distributed consumers).
406
+ * @param leases - Lease requests.
407
+ * @returns Granted leases.
408
+ */
383
409
  async lease(leases) {
384
410
  await sleep();
385
411
  return leases.map((lease) => {
@@ -388,6 +414,10 @@ var InMemoryStore = class {
388
414
  return stream.lease(lease);
389
415
  }).filter((l) => !!l);
390
416
  }
417
+ /**
418
+ * Acknowledge completion of processing for leased streams.
419
+ * @param leases - Leases to acknowledge.
420
+ */
391
421
  async ack(leases) {
392
422
  await sleep();
393
423
  leases.forEach((lease) => this._streams.get(lease.stream)?.ack(lease));
@@ -440,6 +470,24 @@ var store = port(function store2(adapter) {
440
470
  return adapter || new InMemoryStore();
441
471
  });
442
472
 
473
+ // src/signals.ts
474
+ process.once("SIGINT", async (arg) => {
475
+ logger.info(arg, "SIGINT");
476
+ await disposeAndExit("EXIT");
477
+ });
478
+ process.once("SIGTERM", async (arg) => {
479
+ logger.info(arg, "SIGTERM");
480
+ await disposeAndExit("EXIT");
481
+ });
482
+ process.once("uncaughtException", async (arg) => {
483
+ logger.error(arg, "Uncaught Exception");
484
+ await disposeAndExit("ERROR");
485
+ });
486
+ process.once("unhandledRejection", async (arg) => {
487
+ logger.error(arg, "Unhandled Rejection");
488
+ await disposeAndExit("ERROR");
489
+ });
490
+
443
491
  // src/act.ts
444
492
  var import_crypto2 = require("crypto");
445
493
  var import_events = __toESM(require("events"), 1);
@@ -555,6 +603,12 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
555
603
 
556
604
  // src/act.ts
557
605
  var Act = class {
606
+ /**
607
+ * Create a new Act orchestrator.
608
+ *
609
+ * @param registry The registry of state, event, and action schemas
610
+ * @param drainLimit The maximum number of events to drain per cycle
611
+ */
558
612
  constructor(registry, drainLimit) {
559
613
  this.registry = registry;
560
614
  this.drainLimit = drainLimit;
@@ -572,17 +626,18 @@ var Act = class {
572
626
  return this;
573
627
  }
574
628
  /**
575
- * Executes an action and emits an event to be committed by the store.
629
+ * Executes an action (command) against a state machine, emitting and committing the resulting event(s).
576
630
  *
577
631
  * @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
632
+ * @param action The action name (key of the action schema)
633
+ * @param target The target (stream and actor) for the action
634
+ * @param payload The action payload (validated against the schema)
635
+ * @param reactingTo (Optional) The event this action is reacting to
636
+ * @param skipValidation (Optional) If true, skips schema validation (not recommended)
637
+ * @returns The snapshot of the committed event
638
+ *
639
+ * @example
640
+ * await app.do("increment", { stream: "counter1", actor }, { by: 1 });
586
641
  */
587
642
  async do(action2, target, payload, reactingTo, skipValidation = false) {
588
643
  const snapshot = await action(
@@ -597,25 +652,31 @@ var Act = class {
597
652
  return snapshot;
598
653
  }
599
654
  /**
600
- * Loads a snapshot of the state from the store.
655
+ * Loads the current state snapshot for a given state machine and stream.
601
656
  *
602
657
  * @template SX The type of state
603
658
  * @template EX The type of events
604
659
  * @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
660
+ * @param state The state machine definition
661
+ * @param stream The stream (instance) to load
662
+ * @param callback (Optional) Callback to receive the loaded snapshot
608
663
  * @returns The snapshot of the loaded state
664
+ *
665
+ * @example
666
+ * const snapshot = await app.load(Counter, "counter1");
609
667
  */
610
668
  async load(state2, stream, callback) {
611
669
  return await load(state2, stream, callback);
612
670
  }
613
671
  /**
614
- * Queries the store for events.
672
+ * Query the event store for events matching a filter.
615
673
  *
616
- * @param query The query to execute
617
- * @param callback The callback to call with the events
618
- * @returns The query result
674
+ * @param query The query filter (e.g., by stream, event name, or time range)
675
+ * @param callback (Optional) Callback for each event found
676
+ * @returns An object with the first and last event found, and the total count
677
+ *
678
+ * @example
679
+ * const { count } = await app.query({ stream: "counter1" }, (event) => console.log(event));
619
680
  */
620
681
  async query(query, callback) {
621
682
  let first = void 0, last = void 0;
@@ -629,6 +690,7 @@ var Act = class {
629
690
  /**
630
691
  * Handles leased reactions.
631
692
  *
693
+ * @internal
632
694
  * @param lease The lease to handle
633
695
  * @param reactions The reactions to handle
634
696
  * @returns The lease
@@ -658,9 +720,14 @@ var Act = class {
658
720
  }
659
721
  drainLocked = false;
660
722
  /**
661
- * Drains events from the store.
723
+ * Drains and processes events from the store, triggering reactions and updating state.
724
+ *
725
+ * This is typically called in a background loop or after committing new events.
662
726
  *
663
- * @returns The number of drained events
727
+ * @returns The number of events drained and processed
728
+ *
729
+ * @example
730
+ * await app.drain();
664
731
  */
665
732
  async drain() {
666
733
  if (this.drainLocked) return 0;
@@ -883,24 +950,6 @@ function action_builder(state2) {
883
950
  }
884
951
  };
885
952
  }
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
953
  // Annotate the CommonJS export names for ESM import in node:
905
954
  0 && (module.exports = {
906
955
  Act,
@@ -914,13 +963,13 @@ process.once("unhandledRejection", async (arg) => {
914
963
  ExitCodes,
915
964
  InvariantError,
916
965
  LogLevels,
966
+ PackageSchema,
917
967
  QuerySchema,
918
968
  SNAP_EVENT,
919
969
  TargetSchema,
920
970
  ValidationError,
921
971
  ZodEmpty,
922
972
  act,
923
- buildSnapshotSchema,
924
973
  config,
925
974
  dispose,
926
975
  disposeAndExit,