@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.
- 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 +43 -107
- 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 -97
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +144 -95
- package/dist/index.js.map +1 -1
- package/package.json +3 -4
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());
|
|
@@ -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
|
|
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
|
-
* @
|
|
515
|
-
* @
|
|
516
|
-
* @param
|
|
517
|
-
* @param
|
|
518
|
-
* @param
|
|
519
|
-
* @
|
|
520
|
-
*
|
|
521
|
-
* @
|
|
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
|
|
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
|
|
542
|
-
* @param stream The stream to load
|
|
543
|
-
* @param callback
|
|
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
|
-
*
|
|
608
|
+
* Query the event store for events matching a filter.
|
|
551
609
|
*
|
|
552
|
-
* @param query The query
|
|
553
|
-
* @param callback
|
|
554
|
-
* @returns
|
|
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
|
|
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,
|