@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.js CHANGED
@@ -1,6 +1,5 @@
1
- // src/config.ts
2
- import * as fs from "fs";
3
- import { z as z2 } from "zod/v4";
1
+ // src/ports.ts
2
+ import { pino } from "pino";
4
3
 
5
4
  // src/types/errors.ts
6
5
  var Errors = {
@@ -37,6 +36,13 @@ var ConcurrencyError = class extends Error {
37
36
  }
38
37
  };
39
38
 
39
+ // src/utils.ts
40
+ import { prettifyError } from "zod/v4";
41
+
42
+ // src/config.ts
43
+ import * as fs from "fs";
44
+ import { z as z2 } from "zod/v4";
45
+
40
46
  // src/types/schemas.ts
41
47
  import { z } from "zod/v4";
42
48
  var ZodEmpty = z.record(z.string(), z.never());
@@ -68,25 +74,6 @@ var CommittedMetaSchema = z.object({
68
74
  created: z.date(),
69
75
  meta: EventMetaSchema
70
76
  }).readonly();
71
- function buildSnapshotSchema(s) {
72
- const events = Object.entries(s.events).map(
73
- ([name, zod]) => z.object({
74
- name: z.literal(name),
75
- data: zod,
76
- id: z.number(),
77
- stream: z.string(),
78
- version: z.number(),
79
- created: z.date(),
80
- meta: EventMetaSchema
81
- })
82
- );
83
- return z.object({
84
- state: s.state.readonly(),
85
- event: z.union([events[0], events[1], ...events.slice(2)]).optional(),
86
- patches: z.number(),
87
- snaps: z.number()
88
- });
89
- }
90
77
  var QuerySchema = z.object({
91
78
  stream: z.string().optional(),
92
79
  names: z.string().array().optional(),
@@ -115,8 +102,36 @@ var LogLevels = [
115
102
  "trace"
116
103
  ];
117
104
 
105
+ // src/config.ts
106
+ var PackageSchema = z2.object({
107
+ name: z2.string().min(1),
108
+ version: z2.string().min(1),
109
+ description: z2.string().min(1).optional(),
110
+ author: z2.object({ name: z2.string().min(1), email: z2.string().optional() }).optional().or(z2.string().min(1)).optional(),
111
+ license: z2.string().min(1).optional(),
112
+ dependencies: z2.record(z2.string(), z2.string()).optional()
113
+ });
114
+ var getPackage = () => {
115
+ const pkg2 = fs.readFileSync("package.json");
116
+ return JSON.parse(pkg2.toString());
117
+ };
118
+ var BaseSchema = PackageSchema.extend({
119
+ env: z2.enum(Environments),
120
+ logLevel: z2.enum(LogLevels),
121
+ logSingleLine: z2.boolean(),
122
+ sleepMs: z2.number().int().min(0).max(5e3)
123
+ });
124
+ var { NODE_ENV, LOG_LEVEL, LOG_SINGLE_LINE, SLEEP_MS } = process.env;
125
+ var env = NODE_ENV || "development";
126
+ var logLevel = LOG_LEVEL || (NODE_ENV === "test" ? "error" : LOG_LEVEL === "production" ? "info" : "trace");
127
+ var logSingleLine = (LOG_SINGLE_LINE || "true") === "true";
128
+ var sleepMs = parseInt(NODE_ENV === "test" ? "0" : SLEEP_MS ?? "100");
129
+ var pkg = getPackage();
130
+ var config = () => {
131
+ return extend({ ...pkg, env, logLevel, logSingleLine, sleepMs }, BaseSchema);
132
+ };
133
+
118
134
  // src/utils.ts
119
- import { prettifyError } from "zod/v4";
120
135
  var UNMERGEABLES = [
121
136
  RegExp,
122
137
  Date,
@@ -173,38 +188,6 @@ async function sleep(ms) {
173
188
  return new Promise((resolve) => setTimeout(resolve, ms ?? config().sleepMs));
174
189
  }
175
190
 
176
- // src/config.ts
177
- var PackageSchema = z2.object({
178
- name: z2.string().min(1),
179
- version: z2.string().min(1),
180
- description: z2.string().min(1),
181
- author: z2.object({ name: z2.string().min(1), email: z2.string().optional() }).or(z2.string().min(1)),
182
- license: z2.string().min(1),
183
- dependencies: z2.record(z2.string(), z2.string())
184
- });
185
- var getPackage = () => {
186
- const pkg2 = fs.readFileSync("package.json");
187
- return JSON.parse(pkg2.toString());
188
- };
189
- var BaseSchema = PackageSchema.extend({
190
- env: z2.enum(Environments),
191
- logLevel: z2.enum(LogLevels),
192
- logSingleLine: z2.boolean(),
193
- sleepMs: z2.number().int().min(0).max(5e3)
194
- });
195
- var { NODE_ENV, LOG_LEVEL, LOG_SINGLE_LINE, SLEEP_MS } = process.env;
196
- var env = NODE_ENV || "development";
197
- var logLevel = LOG_LEVEL || (NODE_ENV === "test" ? "error" : LOG_LEVEL === "production" ? "info" : "trace");
198
- var logSingleLine = (LOG_SINGLE_LINE || "true") === "true";
199
- var sleepMs = parseInt(NODE_ENV === "test" ? "0" : SLEEP_MS ?? "100");
200
- var pkg = getPackage();
201
- var config = () => {
202
- return extend({ ...pkg, env, logLevel, logSingleLine, sleepMs }, BaseSchema);
203
- };
204
-
205
- // src/ports.ts
206
- import { pino } from "pino";
207
-
208
191
  // src/adapters/InMemoryStore.ts
209
192
  var InMemoryStream = class {
210
193
  constructor(stream) {
@@ -214,12 +197,21 @@ var InMemoryStream = class {
214
197
  _retry = -1;
215
198
  _lease;
216
199
  _blocked = false;
200
+ /**
201
+ * Attempt to lease this stream for processing.
202
+ * @param lease - Lease request.
203
+ * @returns The granted lease or undefined if blocked.
204
+ */
217
205
  lease(lease) {
218
206
  if (!this._blocked && lease.at > this._at) {
219
207
  this._lease = { ...lease, retry: this._retry + 1 };
220
208
  return this._lease;
221
209
  }
222
210
  }
211
+ /**
212
+ * Acknowledge completion of processing for this stream.
213
+ * @param lease - Lease to acknowledge.
214
+ */
223
215
  ack(lease) {
224
216
  if (this._lease && lease.at >= this._at) {
225
217
  this._retry = lease.retry;
@@ -237,17 +229,35 @@ var InMemoryStore = class {
237
229
  _events = [];
238
230
  // stored stream positions and other metadata
239
231
  _streams = /* @__PURE__ */ new Map();
232
+ /**
233
+ * Dispose of the store and clear all events.
234
+ * @returns Promise that resolves when disposal is complete.
235
+ */
240
236
  async dispose() {
241
237
  await sleep();
242
238
  this._events.length = 0;
243
239
  }
240
+ /**
241
+ * Seed the store with initial data (no-op for in-memory).
242
+ * @returns Promise that resolves when seeding is complete.
243
+ */
244
244
  async seed() {
245
245
  await sleep();
246
246
  }
247
+ /**
248
+ * Drop all data from the store.
249
+ * @returns Promise that resolves when the store is cleared.
250
+ */
247
251
  async drop() {
248
252
  await sleep();
249
253
  this._events.length = 0;
250
254
  }
255
+ /**
256
+ * Query events in the store, optionally filtered by query options.
257
+ * @param callback - Function to call for each event.
258
+ * @param query - Optional query options.
259
+ * @returns The number of events processed.
260
+ */
251
261
  async query(callback, query) {
252
262
  await sleep();
253
263
  const {
@@ -275,10 +285,19 @@ var InMemoryStore = class {
275
285
  }
276
286
  return count;
277
287
  }
288
+ /**
289
+ * Commit one or more events to a stream.
290
+ * @param stream - The stream name.
291
+ * @param msgs - The events/messages to commit.
292
+ * @param meta - Event metadata.
293
+ * @param expectedVersion - Optional optimistic concurrency check.
294
+ * @returns The committed events with metadata.
295
+ * @throws ConcurrencyError if expectedVersion does not match.
296
+ */
278
297
  async commit(stream, msgs, meta, expectedVersion) {
279
298
  await sleep();
280
299
  const instance = this._events.filter((e) => e.stream === stream);
281
- if (expectedVersion && instance.length - 1 !== expectedVersion)
300
+ if (typeof expectedVersion === "number" && instance.length - 1 !== expectedVersion)
282
301
  throw new ConcurrencyError(
283
302
  instance.length - 1,
284
303
  msgs,
@@ -301,7 +320,9 @@ var InMemoryStore = class {
301
320
  });
302
321
  }
303
322
  /**
304
- * Fetches new events from stream watermarks
323
+ * Fetches new events from stream watermarks for processing.
324
+ * @param limit - Maximum number of streams to fetch.
325
+ * @returns Fetched streams and events.
305
326
  */
306
327
  async fetch(limit) {
307
328
  const streams = [...this._streams.values()].filter((s) => !s._blocked).sort((a, b) => a._at - b._at).slice(0, limit);
@@ -316,6 +337,11 @@ var InMemoryStore = class {
316
337
  });
317
338
  return { streams: streams.map(({ stream }) => stream), events };
318
339
  }
340
+ /**
341
+ * Lease streams for processing (e.g., for distributed consumers).
342
+ * @param leases - Lease requests.
343
+ * @returns Granted leases.
344
+ */
319
345
  async lease(leases) {
320
346
  await sleep();
321
347
  return leases.map((lease) => {
@@ -324,6 +350,10 @@ var InMemoryStore = class {
324
350
  return stream.lease(lease);
325
351
  }).filter((l) => !!l);
326
352
  }
353
+ /**
354
+ * Acknowledge completion of processing for leased streams.
355
+ * @param leases - Leases to acknowledge.
356
+ */
327
357
  async ack(leases) {
328
358
  await sleep();
329
359
  leases.forEach((lease) => this._streams.get(lease.stream)?.ack(lease));
@@ -376,6 +406,24 @@ var store = port(function store2(adapter) {
376
406
  return adapter || new InMemoryStore();
377
407
  });
378
408
 
409
+ // src/signals.ts
410
+ process.once("SIGINT", async (arg) => {
411
+ logger.info(arg, "SIGINT");
412
+ await disposeAndExit("EXIT");
413
+ });
414
+ process.once("SIGTERM", async (arg) => {
415
+ logger.info(arg, "SIGTERM");
416
+ await disposeAndExit("EXIT");
417
+ });
418
+ process.once("uncaughtException", async (arg) => {
419
+ logger.error(arg, "Uncaught Exception");
420
+ await disposeAndExit("ERROR");
421
+ });
422
+ process.once("unhandledRejection", async (arg) => {
423
+ logger.error(arg, "Unhandled Rejection");
424
+ await disposeAndExit("ERROR");
425
+ });
426
+
379
427
  // src/act.ts
380
428
  import { randomUUID as randomUUID2 } from "crypto";
381
429
  import EventEmitter from "events";
@@ -491,6 +539,12 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
491
539
 
492
540
  // src/act.ts
493
541
  var Act = class {
542
+ /**
543
+ * Create a new Act orchestrator.
544
+ *
545
+ * @param registry The registry of state, event, and action schemas
546
+ * @param drainLimit The maximum number of events to drain per cycle
547
+ */
494
548
  constructor(registry, drainLimit) {
495
549
  this.registry = registry;
496
550
  this.drainLimit = drainLimit;
@@ -508,17 +562,18 @@ var Act = class {
508
562
  return this;
509
563
  }
510
564
  /**
511
- * Executes an action and emits an event to be committed by the store.
565
+ * Executes an action (command) against a state machine, emitting and committing the resulting event(s).
512
566
  *
513
567
  * @template K The type of action to execute
514
- * @template T The type of target
515
- * @template P The type of payloads
516
- * @param action The action to execute
517
- * @param target The target of the action
518
- * @param payload The payload of the action
519
- * @param reactingTo The event that the action is reacting to
520
- * @param skipValidation Whether to skip validation
521
- * @returns The snapshot of the committed Event
568
+ * @param action The action name (key of the action schema)
569
+ * @param target The target (stream and actor) for the action
570
+ * @param payload The action payload (validated against the schema)
571
+ * @param reactingTo (Optional) The event this action is reacting to
572
+ * @param skipValidation (Optional) If true, skips schema validation (not recommended)
573
+ * @returns The snapshot of the committed event
574
+ *
575
+ * @example
576
+ * await app.do("increment", { stream: "counter1", actor }, { by: 1 });
522
577
  */
523
578
  async do(action2, target, payload, reactingTo, skipValidation = false) {
524
579
  const snapshot = await action(
@@ -533,25 +588,31 @@ var Act = class {
533
588
  return snapshot;
534
589
  }
535
590
  /**
536
- * Loads a snapshot of the state from the store.
591
+ * Loads the current state snapshot for a given state machine and stream.
537
592
  *
538
593
  * @template SX The type of state
539
594
  * @template EX The type of events
540
595
  * @template AX The type of actions
541
- * @param state The state to load
542
- * @param stream The stream to load
543
- * @param callback The callback to call with the snapshot
596
+ * @param state The state machine definition
597
+ * @param stream The stream (instance) to load
598
+ * @param callback (Optional) Callback to receive the loaded snapshot
544
599
  * @returns The snapshot of the loaded state
600
+ *
601
+ * @example
602
+ * const snapshot = await app.load(Counter, "counter1");
545
603
  */
546
604
  async load(state2, stream, callback) {
547
605
  return await load(state2, stream, callback);
548
606
  }
549
607
  /**
550
- * Queries the store for events.
608
+ * Query the event store for events matching a filter.
551
609
  *
552
- * @param query The query to execute
553
- * @param callback The callback to call with the events
554
- * @returns The query result
610
+ * @param query The query filter (e.g., by stream, event name, or time range)
611
+ * @param callback (Optional) Callback for each event found
612
+ * @returns An object with the first and last event found, and the total count
613
+ *
614
+ * @example
615
+ * const { count } = await app.query({ stream: "counter1" }, (event) => console.log(event));
555
616
  */
556
617
  async query(query, callback) {
557
618
  let first = void 0, last = void 0;
@@ -565,6 +626,7 @@ var Act = class {
565
626
  /**
566
627
  * Handles leased reactions.
567
628
  *
629
+ * @internal
568
630
  * @param lease The lease to handle
569
631
  * @param reactions The reactions to handle
570
632
  * @returns The lease
@@ -594,9 +656,14 @@ var Act = class {
594
656
  }
595
657
  drainLocked = false;
596
658
  /**
597
- * Drains events from the store.
659
+ * Drains and processes events from the store, triggering reactions and updating state.
660
+ *
661
+ * This is typically called in a background loop or after committing new events.
598
662
  *
599
- * @returns The number of drained events
663
+ * @returns The number of events drained and processed
664
+ *
665
+ * @example
666
+ * await app.drain();
600
667
  */
601
668
  async drain() {
602
669
  if (this.drainLocked) return 0;
@@ -819,24 +886,6 @@ function action_builder(state2) {
819
886
  }
820
887
  };
821
888
  }
822
-
823
- // src/index.ts
824
- process.once("SIGINT", async (arg) => {
825
- logger.info(arg, "SIGINT");
826
- await disposeAndExit("EXIT");
827
- });
828
- process.once("SIGTERM", async (arg) => {
829
- logger.info(arg, "SIGTERM");
830
- await disposeAndExit("EXIT");
831
- });
832
- process.once("uncaughtException", async (arg) => {
833
- logger.error(arg, "Uncaught Exception");
834
- await disposeAndExit("ERROR");
835
- });
836
- process.once("unhandledRejection", async (arg) => {
837
- logger.error(arg, "Unhandled Rejection");
838
- await disposeAndExit("ERROR");
839
- });
840
889
  export {
841
890
  Act,
842
891
  ActorSchema,
@@ -849,13 +898,13 @@ export {
849
898
  ExitCodes,
850
899
  InvariantError,
851
900
  LogLevels,
901
+ PackageSchema,
852
902
  QuerySchema,
853
903
  SNAP_EVENT,
854
904
  TargetSchema,
855
905
  ValidationError,
856
906
  ZodEmpty,
857
907
  act,
858
- buildSnapshotSchema,
859
908
  config,
860
909
  dispose,
861
910
  disposeAndExit,