@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.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());
@@ -115,8 +121,36 @@ var LogLevels = [
115
121
  "trace"
116
122
  ];
117
123
 
124
+ // src/config.ts
125
+ var PackageSchema = z2.object({
126
+ name: z2.string().min(1),
127
+ version: z2.string().min(1),
128
+ description: z2.string().min(1).optional(),
129
+ author: z2.object({ name: z2.string().min(1), email: z2.string().optional() }).optional().or(z2.string().min(1)).optional(),
130
+ license: z2.string().min(1).optional(),
131
+ dependencies: z2.record(z2.string(), z2.string()).optional()
132
+ });
133
+ var getPackage = () => {
134
+ const pkg2 = fs.readFileSync("package.json");
135
+ return JSON.parse(pkg2.toString());
136
+ };
137
+ var BaseSchema = PackageSchema.extend({
138
+ env: z2.enum(Environments),
139
+ logLevel: z2.enum(LogLevels),
140
+ logSingleLine: z2.boolean(),
141
+ sleepMs: z2.number().int().min(0).max(5e3)
142
+ });
143
+ var { NODE_ENV, LOG_LEVEL, LOG_SINGLE_LINE, SLEEP_MS } = process.env;
144
+ var env = NODE_ENV || "development";
145
+ var logLevel = LOG_LEVEL || (NODE_ENV === "test" ? "error" : LOG_LEVEL === "production" ? "info" : "trace");
146
+ var logSingleLine = (LOG_SINGLE_LINE || "true") === "true";
147
+ var sleepMs = parseInt(NODE_ENV === "test" ? "0" : SLEEP_MS ?? "100");
148
+ var pkg = getPackage();
149
+ var config = () => {
150
+ return extend({ ...pkg, env, logLevel, logSingleLine, sleepMs }, BaseSchema);
151
+ };
152
+
118
153
  // src/utils.ts
119
- import { prettifyError } from "zod/v4";
120
154
  var UNMERGEABLES = [
121
155
  RegExp,
122
156
  Date,
@@ -173,38 +207,6 @@ async function sleep(ms) {
173
207
  return new Promise((resolve) => setTimeout(resolve, ms ?? config().sleepMs));
174
208
  }
175
209
 
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
210
  // src/adapters/InMemoryStore.ts
209
211
  var InMemoryStream = class {
210
212
  constructor(stream) {
@@ -214,12 +216,21 @@ var InMemoryStream = class {
214
216
  _retry = -1;
215
217
  _lease;
216
218
  _blocked = false;
219
+ /**
220
+ * Attempt to lease this stream for processing.
221
+ * @param lease - Lease request.
222
+ * @returns The granted lease or undefined if blocked.
223
+ */
217
224
  lease(lease) {
218
225
  if (!this._blocked && lease.at > this._at) {
219
226
  this._lease = { ...lease, retry: this._retry + 1 };
220
227
  return this._lease;
221
228
  }
222
229
  }
230
+ /**
231
+ * Acknowledge completion of processing for this stream.
232
+ * @param lease - Lease to acknowledge.
233
+ */
223
234
  ack(lease) {
224
235
  if (this._lease && lease.at >= this._at) {
225
236
  this._retry = lease.retry;
@@ -237,17 +248,35 @@ var InMemoryStore = class {
237
248
  _events = [];
238
249
  // stored stream positions and other metadata
239
250
  _streams = /* @__PURE__ */ new Map();
251
+ /**
252
+ * Dispose of the store and clear all events.
253
+ * @returns Promise that resolves when disposal is complete.
254
+ */
240
255
  async dispose() {
241
256
  await sleep();
242
257
  this._events.length = 0;
243
258
  }
259
+ /**
260
+ * Seed the store with initial data (no-op for in-memory).
261
+ * @returns Promise that resolves when seeding is complete.
262
+ */
244
263
  async seed() {
245
264
  await sleep();
246
265
  }
266
+ /**
267
+ * Drop all data from the store.
268
+ * @returns Promise that resolves when the store is cleared.
269
+ */
247
270
  async drop() {
248
271
  await sleep();
249
272
  this._events.length = 0;
250
273
  }
274
+ /**
275
+ * Query events in the store, optionally filtered by query options.
276
+ * @param callback - Function to call for each event.
277
+ * @param query - Optional query options.
278
+ * @returns The number of events processed.
279
+ */
251
280
  async query(callback, query) {
252
281
  await sleep();
253
282
  const {
@@ -275,10 +304,19 @@ var InMemoryStore = class {
275
304
  }
276
305
  return count;
277
306
  }
307
+ /**
308
+ * Commit one or more events to a stream.
309
+ * @param stream - The stream name.
310
+ * @param msgs - The events/messages to commit.
311
+ * @param meta - Event metadata.
312
+ * @param expectedVersion - Optional optimistic concurrency check.
313
+ * @returns The committed events with metadata.
314
+ * @throws ConcurrencyError if expectedVersion does not match.
315
+ */
278
316
  async commit(stream, msgs, meta, expectedVersion) {
279
317
  await sleep();
280
318
  const instance = this._events.filter((e) => e.stream === stream);
281
- if (expectedVersion && instance.length - 1 !== expectedVersion)
319
+ if (typeof expectedVersion === "number" && instance.length - 1 !== expectedVersion)
282
320
  throw new ConcurrencyError(
283
321
  instance.length - 1,
284
322
  msgs,
@@ -301,7 +339,9 @@ var InMemoryStore = class {
301
339
  });
302
340
  }
303
341
  /**
304
- * Fetches new events from stream watermarks
342
+ * Fetches new events from stream watermarks for processing.
343
+ * @param limit - Maximum number of streams to fetch.
344
+ * @returns Fetched streams and events.
305
345
  */
306
346
  async fetch(limit) {
307
347
  const streams = [...this._streams.values()].filter((s) => !s._blocked).sort((a, b) => a._at - b._at).slice(0, limit);
@@ -316,6 +356,11 @@ var InMemoryStore = class {
316
356
  });
317
357
  return { streams: streams.map(({ stream }) => stream), events };
318
358
  }
359
+ /**
360
+ * Lease streams for processing (e.g., for distributed consumers).
361
+ * @param leases - Lease requests.
362
+ * @returns Granted leases.
363
+ */
319
364
  async lease(leases) {
320
365
  await sleep();
321
366
  return leases.map((lease) => {
@@ -324,6 +369,10 @@ var InMemoryStore = class {
324
369
  return stream.lease(lease);
325
370
  }).filter((l) => !!l);
326
371
  }
372
+ /**
373
+ * Acknowledge completion of processing for leased streams.
374
+ * @param leases - Leases to acknowledge.
375
+ */
327
376
  async ack(leases) {
328
377
  await sleep();
329
378
  leases.forEach((lease) => this._streams.get(lease.stream)?.ack(lease));
@@ -376,6 +425,24 @@ var store = port(function store2(adapter) {
376
425
  return adapter || new InMemoryStore();
377
426
  });
378
427
 
428
+ // src/signals.ts
429
+ process.once("SIGINT", async (arg) => {
430
+ logger.info(arg, "SIGINT");
431
+ await disposeAndExit("EXIT");
432
+ });
433
+ process.once("SIGTERM", async (arg) => {
434
+ logger.info(arg, "SIGTERM");
435
+ await disposeAndExit("EXIT");
436
+ });
437
+ process.once("uncaughtException", async (arg) => {
438
+ logger.error(arg, "Uncaught Exception");
439
+ await disposeAndExit("ERROR");
440
+ });
441
+ process.once("unhandledRejection", async (arg) => {
442
+ logger.error(arg, "Unhandled Rejection");
443
+ await disposeAndExit("ERROR");
444
+ });
445
+
379
446
  // src/act.ts
380
447
  import { randomUUID as randomUUID2 } from "crypto";
381
448
  import EventEmitter from "events";
@@ -491,6 +558,12 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
491
558
 
492
559
  // src/act.ts
493
560
  var Act = class {
561
+ /**
562
+ * Create a new Act orchestrator.
563
+ *
564
+ * @param registry The registry of state, event, and action schemas
565
+ * @param drainLimit The maximum number of events to drain per cycle
566
+ */
494
567
  constructor(registry, drainLimit) {
495
568
  this.registry = registry;
496
569
  this.drainLimit = drainLimit;
@@ -508,17 +581,18 @@ var Act = class {
508
581
  return this;
509
582
  }
510
583
  /**
511
- * Executes an action and emits an event to be committed by the store.
584
+ * Executes an action (command) against a state machine, emitting and committing the resulting event(s).
512
585
  *
513
586
  * @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
587
+ * @param action The action name (key of the action schema)
588
+ * @param target The target (stream and actor) for the action
589
+ * @param payload The action payload (validated against the schema)
590
+ * @param reactingTo (Optional) The event this action is reacting to
591
+ * @param skipValidation (Optional) If true, skips schema validation (not recommended)
592
+ * @returns The snapshot of the committed event
593
+ *
594
+ * @example
595
+ * await app.do("increment", { stream: "counter1", actor }, { by: 1 });
522
596
  */
523
597
  async do(action2, target, payload, reactingTo, skipValidation = false) {
524
598
  const snapshot = await action(
@@ -533,25 +607,31 @@ var Act = class {
533
607
  return snapshot;
534
608
  }
535
609
  /**
536
- * Loads a snapshot of the state from the store.
610
+ * Loads the current state snapshot for a given state machine and stream.
537
611
  *
538
612
  * @template SX The type of state
539
613
  * @template EX The type of events
540
614
  * @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
615
+ * @param state The state machine definition
616
+ * @param stream The stream (instance) to load
617
+ * @param callback (Optional) Callback to receive the loaded snapshot
544
618
  * @returns The snapshot of the loaded state
619
+ *
620
+ * @example
621
+ * const snapshot = await app.load(Counter, "counter1");
545
622
  */
546
623
  async load(state2, stream, callback) {
547
624
  return await load(state2, stream, callback);
548
625
  }
549
626
  /**
550
- * Queries the store for events.
627
+ * Query the event store for events matching a filter.
628
+ *
629
+ * @param query The query filter (e.g., by stream, event name, or time range)
630
+ * @param callback (Optional) Callback for each event found
631
+ * @returns An object with the first and last event found, and the total count
551
632
  *
552
- * @param query The query to execute
553
- * @param callback The callback to call with the events
554
- * @returns The query result
633
+ * @example
634
+ * const { count } = await app.query({ stream: "counter1" }, (event) => console.log(event));
555
635
  */
556
636
  async query(query, callback) {
557
637
  let first = void 0, last = void 0;
@@ -565,6 +645,7 @@ var Act = class {
565
645
  /**
566
646
  * Handles leased reactions.
567
647
  *
648
+ * @internal
568
649
  * @param lease The lease to handle
569
650
  * @param reactions The reactions to handle
570
651
  * @returns The lease
@@ -594,9 +675,14 @@ var Act = class {
594
675
  }
595
676
  drainLocked = false;
596
677
  /**
597
- * Drains events from the store.
678
+ * Drains and processes events from the store, triggering reactions and updating state.
598
679
  *
599
- * @returns The number of drained events
680
+ * This is typically called in a background loop or after committing new events.
681
+ *
682
+ * @returns The number of events drained and processed
683
+ *
684
+ * @example
685
+ * await app.drain();
600
686
  */
601
687
  async drain() {
602
688
  if (this.drainLocked) return 0;
@@ -819,24 +905,6 @@ function action_builder(state2) {
819
905
  }
820
906
  };
821
907
  }
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
908
  export {
841
909
  Act,
842
910
  ActorSchema,
@@ -849,6 +917,7 @@ export {
849
917
  ExitCodes,
850
918
  InvariantError,
851
919
  LogLevels,
920
+ PackageSchema,
852
921
  QuerySchema,
853
922
  SNAP_EVENT,
854
923
  TargetSchema,