@rotorsoft/act 0.5.0 → 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 -12
- 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 +155 -79
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +153 -78
- 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);
|
|
@@ -310,9 +350,17 @@ var InMemoryStore = class {
|
|
|
310
350
|
Number.MAX_SAFE_INTEGER
|
|
311
351
|
) : -1;
|
|
312
352
|
const events = [];
|
|
313
|
-
await this.query((e) => events.push(e), {
|
|
353
|
+
await this.query((e) => e.name !== SNAP_EVENT && events.push(e), {
|
|
354
|
+
after,
|
|
355
|
+
limit
|
|
356
|
+
});
|
|
314
357
|
return { streams: streams.map(({ stream }) => stream), events };
|
|
315
358
|
}
|
|
359
|
+
/**
|
|
360
|
+
* Lease streams for processing (e.g., for distributed consumers).
|
|
361
|
+
* @param leases - Lease requests.
|
|
362
|
+
* @returns Granted leases.
|
|
363
|
+
*/
|
|
316
364
|
async lease(leases) {
|
|
317
365
|
await sleep();
|
|
318
366
|
return leases.map((lease) => {
|
|
@@ -321,6 +369,10 @@ var InMemoryStore = class {
|
|
|
321
369
|
return stream.lease(lease);
|
|
322
370
|
}).filter((l) => !!l);
|
|
323
371
|
}
|
|
372
|
+
/**
|
|
373
|
+
* Acknowledge completion of processing for leased streams.
|
|
374
|
+
* @param leases - Leases to acknowledge.
|
|
375
|
+
*/
|
|
324
376
|
async ack(leases) {
|
|
325
377
|
await sleep();
|
|
326
378
|
leases.forEach((lease) => this._streams.get(lease.stream)?.ack(lease));
|
|
@@ -373,6 +425,24 @@ var store = port(function store2(adapter) {
|
|
|
373
425
|
return adapter || new InMemoryStore();
|
|
374
426
|
});
|
|
375
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
|
+
|
|
376
446
|
// src/act.ts
|
|
377
447
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
378
448
|
import EventEmitter from "events";
|
|
@@ -488,6 +558,12 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
|
|
|
488
558
|
|
|
489
559
|
// src/act.ts
|
|
490
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
|
+
*/
|
|
491
567
|
constructor(registry, drainLimit) {
|
|
492
568
|
this.registry = registry;
|
|
493
569
|
this.drainLimit = drainLimit;
|
|
@@ -505,17 +581,18 @@ var Act = class {
|
|
|
505
581
|
return this;
|
|
506
582
|
}
|
|
507
583
|
/**
|
|
508
|
-
* Executes an action
|
|
584
|
+
* Executes an action (command) against a state machine, emitting and committing the resulting event(s).
|
|
509
585
|
*
|
|
510
586
|
* @template K The type of action to execute
|
|
511
|
-
* @
|
|
512
|
-
* @
|
|
513
|
-
* @param
|
|
514
|
-
* @param
|
|
515
|
-
* @param
|
|
516
|
-
* @
|
|
517
|
-
*
|
|
518
|
-
* @
|
|
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 });
|
|
519
596
|
*/
|
|
520
597
|
async do(action2, target, payload, reactingTo, skipValidation = false) {
|
|
521
598
|
const snapshot = await action(
|
|
@@ -530,25 +607,31 @@ var Act = class {
|
|
|
530
607
|
return snapshot;
|
|
531
608
|
}
|
|
532
609
|
/**
|
|
533
|
-
* Loads
|
|
610
|
+
* Loads the current state snapshot for a given state machine and stream.
|
|
534
611
|
*
|
|
535
612
|
* @template SX The type of state
|
|
536
613
|
* @template EX The type of events
|
|
537
614
|
* @template AX The type of actions
|
|
538
|
-
* @param state The state
|
|
539
|
-
* @param stream The stream to load
|
|
540
|
-
* @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
|
|
541
618
|
* @returns The snapshot of the loaded state
|
|
619
|
+
*
|
|
620
|
+
* @example
|
|
621
|
+
* const snapshot = await app.load(Counter, "counter1");
|
|
542
622
|
*/
|
|
543
623
|
async load(state2, stream, callback) {
|
|
544
624
|
return await load(state2, stream, callback);
|
|
545
625
|
}
|
|
546
626
|
/**
|
|
547
|
-
*
|
|
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
|
|
548
632
|
*
|
|
549
|
-
* @
|
|
550
|
-
*
|
|
551
|
-
* @returns The query result
|
|
633
|
+
* @example
|
|
634
|
+
* const { count } = await app.query({ stream: "counter1" }, (event) => console.log(event));
|
|
552
635
|
*/
|
|
553
636
|
async query(query, callback) {
|
|
554
637
|
let first = void 0, last = void 0;
|
|
@@ -562,6 +645,7 @@ var Act = class {
|
|
|
562
645
|
/**
|
|
563
646
|
* Handles leased reactions.
|
|
564
647
|
*
|
|
648
|
+
* @internal
|
|
565
649
|
* @param lease The lease to handle
|
|
566
650
|
* @param reactions The reactions to handle
|
|
567
651
|
* @returns The lease
|
|
@@ -591,9 +675,14 @@ var Act = class {
|
|
|
591
675
|
}
|
|
592
676
|
drainLocked = false;
|
|
593
677
|
/**
|
|
594
|
-
* Drains events from the store.
|
|
678
|
+
* Drains and processes events from the store, triggering reactions and updating state.
|
|
595
679
|
*
|
|
596
|
-
*
|
|
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();
|
|
597
686
|
*/
|
|
598
687
|
async drain() {
|
|
599
688
|
if (this.drainLocked) return 0;
|
|
@@ -610,14 +699,17 @@ var Act = class {
|
|
|
610
699
|
);
|
|
611
700
|
const resolved = new Set(streams);
|
|
612
701
|
const correlated = /* @__PURE__ */ new Map();
|
|
613
|
-
for (const event of events)
|
|
614
|
-
|
|
702
|
+
for (const event of events) {
|
|
703
|
+
const register = this.registry.events[event.name];
|
|
704
|
+
if (!register) continue;
|
|
705
|
+
for (const reaction of register.reactions.values()) {
|
|
615
706
|
const stream = typeof reaction.resolver === "string" ? reaction.resolver : reaction.resolver(event);
|
|
616
707
|
if (stream) {
|
|
617
708
|
resolved.add(stream);
|
|
618
709
|
(correlated.get(stream) || correlated.set(stream, []).get(stream)).push({ ...reaction, event });
|
|
619
710
|
}
|
|
620
711
|
}
|
|
712
|
+
}
|
|
621
713
|
const last = events.at(-1).id;
|
|
622
714
|
const leases = [...resolved.values()].map((stream) => ({
|
|
623
715
|
by: randomUUID2(),
|
|
@@ -813,24 +905,6 @@ function action_builder(state2) {
|
|
|
813
905
|
}
|
|
814
906
|
};
|
|
815
907
|
}
|
|
816
|
-
|
|
817
|
-
// src/index.ts
|
|
818
|
-
process.once("SIGINT", async (arg) => {
|
|
819
|
-
logger.info(arg, "SIGINT");
|
|
820
|
-
await disposeAndExit("EXIT");
|
|
821
|
-
});
|
|
822
|
-
process.once("SIGTERM", async (arg) => {
|
|
823
|
-
logger.info(arg, "SIGTERM");
|
|
824
|
-
await disposeAndExit("EXIT");
|
|
825
|
-
});
|
|
826
|
-
process.once("uncaughtException", async (arg) => {
|
|
827
|
-
logger.error(arg, "Uncaught Exception");
|
|
828
|
-
await disposeAndExit("ERROR");
|
|
829
|
-
});
|
|
830
|
-
process.once("unhandledRejection", async (arg) => {
|
|
831
|
-
logger.error(arg, "Unhandled Rejection");
|
|
832
|
-
await disposeAndExit("ERROR");
|
|
833
|
-
});
|
|
834
908
|
export {
|
|
835
909
|
Act,
|
|
836
910
|
ActorSchema,
|
|
@@ -843,6 +917,7 @@ export {
|
|
|
843
917
|
ExitCodes,
|
|
844
918
|
InvariantError,
|
|
845
919
|
LogLevels,
|
|
920
|
+
PackageSchema,
|
|
846
921
|
QuerySchema,
|
|
847
922
|
SNAP_EVENT,
|
|
848
923
|
TargetSchema,
|