@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.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import
|
|
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
|
|
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
|
-
* @
|
|
515
|
-
* @
|
|
516
|
-
* @param
|
|
517
|
-
* @param
|
|
518
|
-
* @param
|
|
519
|
-
* @
|
|
520
|
-
*
|
|
521
|
-
* @
|
|
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
|
|
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
|
|
542
|
-
* @param stream The stream to load
|
|
543
|
-
* @param callback
|
|
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
|
-
*
|
|
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
|
-
* @
|
|
553
|
-
*
|
|
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
|
-
*
|
|
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,
|