@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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/act-builder.d.ts +66 -2
- package/dist/@types/act-builder.d.ts.map +1 -1
- package/dist/@types/act.d.ts +77 -21
- package/dist/@types/act.d.ts.map +1 -1
- package/dist/@types/adapters/InMemoryStore.d.ts +49 -2
- package/dist/@types/adapters/InMemoryStore.d.ts.map +1 -1
- package/dist/@types/config.d.ts +34 -11
- package/dist/@types/config.d.ts.map +1 -1
- package/dist/@types/event-sourcing.d.ts +30 -9
- package/dist/@types/event-sourcing.d.ts.map +1 -1
- package/dist/@types/index.d.ts +3 -2
- package/dist/@types/index.d.ts.map +1 -1
- package/dist/@types/ports.d.ts +51 -4
- package/dist/@types/ports.d.ts.map +1 -1
- package/dist/@types/signals.d.ts +2 -0
- package/dist/@types/signals.d.ts.map +1 -0
- package/dist/@types/state-builder.d.ts +54 -3
- package/dist/@types/state-builder.d.ts.map +1 -1
- package/dist/@types/types/action.d.ts +105 -0
- package/dist/@types/types/action.d.ts.map +1 -1
- package/dist/@types/types/errors.d.ts +33 -4
- package/dist/@types/types/errors.d.ts.map +1 -1
- package/dist/@types/types/index.d.ts +28 -0
- package/dist/@types/types/index.d.ts.map +1 -1
- package/dist/@types/types/ports.d.ts +53 -0
- package/dist/@types/types/ports.d.ts.map +1 -1
- package/dist/@types/types/reaction.d.ts +51 -0
- package/dist/@types/types/reaction.d.ts.map +1 -1
- package/dist/@types/types/registry.d.ts +27 -0
- package/dist/@types/types/registry.d.ts.map +1 -1
- package/dist/@types/types/schemas.d.ts +48 -12
- package/dist/@types/types/schemas.d.ts.map +1 -1
- package/dist/@types/utils.d.ts +46 -5
- package/dist/@types/utils.d.ts.map +1 -1
- package/dist/index.cjs +146 -76
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +144 -75
- package/dist/index.js.map +1 -1
- 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/
|
|
66
|
-
var
|
|
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,
|
|
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
|
|
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
|
-
* @
|
|
579
|
-
* @
|
|
580
|
-
* @param
|
|
581
|
-
* @param
|
|
582
|
-
* @param
|
|
583
|
-
* @
|
|
584
|
-
*
|
|
585
|
-
* @
|
|
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
|
|
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
|
|
606
|
-
* @param stream The stream to load
|
|
607
|
-
* @param callback
|
|
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
|
-
*
|
|
692
|
+
* Query the event store for events matching a filter.
|
|
615
693
|
*
|
|
616
|
-
* @param query The query
|
|
617
|
-
* @param callback
|
|
618
|
-
* @returns
|
|
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
|
|
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,
|