@rotorsoft/act 0.6.33 → 0.8.0
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/README.md +179 -126
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/act-builder.d.ts +34 -14
- package/dist/@types/act-builder.d.ts.map +1 -1
- package/dist/@types/act.d.ts +14 -9
- package/dist/@types/act.d.ts.map +1 -1
- package/dist/@types/state-builder.d.ts +9 -9
- package/dist/@types/state-builder.d.ts.map +1 -1
- package/dist/@types/types/action.d.ts +2 -2
- package/dist/@types/types/action.d.ts.map +1 -1
- package/dist/index.cjs +85 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +85 -48
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -564,6 +564,9 @@ process.once("unhandledRejection", async (arg) => {
|
|
|
564
564
|
await disposeAndExit("ERROR");
|
|
565
565
|
});
|
|
566
566
|
|
|
567
|
+
// src/act-builder.ts
|
|
568
|
+
import { ZodObject as ZodObject2 } from "zod";
|
|
569
|
+
|
|
567
570
|
// src/act.ts
|
|
568
571
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
569
572
|
import EventEmitter from "events";
|
|
@@ -689,9 +692,11 @@ var Act = class {
|
|
|
689
692
|
* Create a new Act orchestrator.
|
|
690
693
|
*
|
|
691
694
|
* @param registry The registry of state, event, and action schemas
|
|
695
|
+
* @param states Map of state names to their (potentially merged) state definitions
|
|
692
696
|
*/
|
|
693
|
-
constructor(registry) {
|
|
697
|
+
constructor(registry, _states = /* @__PURE__ */ new Map()) {
|
|
694
698
|
this.registry = registry;
|
|
699
|
+
this._states = _states;
|
|
695
700
|
dispose(() => {
|
|
696
701
|
this._emitter.removeAllListeners();
|
|
697
702
|
this.stop_correlations();
|
|
@@ -809,47 +814,16 @@ var Act = class {
|
|
|
809
814
|
this.emit("committed", snapshots);
|
|
810
815
|
return snapshots;
|
|
811
816
|
}
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
* @param stream - The stream ID (state instance identifier)
|
|
823
|
-
* @param callback - Optional callback invoked with the loaded snapshot
|
|
824
|
-
* @returns The current state snapshot for the stream
|
|
825
|
-
*
|
|
826
|
-
* @example Load current state
|
|
827
|
-
* ```typescript
|
|
828
|
-
* const snapshot = await app.load(Counter, "counter-1");
|
|
829
|
-
* console.log(snapshot.state.count); // Current count
|
|
830
|
-
* console.log(snapshot.version); // Number of events applied
|
|
831
|
-
* console.log(snapshot.patches); // Events since last snapshot
|
|
832
|
-
* ```
|
|
833
|
-
*
|
|
834
|
-
* @example With callback
|
|
835
|
-
* ```typescript
|
|
836
|
-
* const snapshot = await app.load(User, "user-123", (snap) => {
|
|
837
|
-
* console.log("Loaded user:", snap.state.name);
|
|
838
|
-
* });
|
|
839
|
-
* ```
|
|
840
|
-
*
|
|
841
|
-
* @example Load multiple states
|
|
842
|
-
* ```typescript
|
|
843
|
-
* const [user, account] = await Promise.all([
|
|
844
|
-
* app.load(User, "user-123"),
|
|
845
|
-
* app.load(BankAccount, "account-456")
|
|
846
|
-
* ]);
|
|
847
|
-
* ```
|
|
848
|
-
*
|
|
849
|
-
* @see {@link Snapshot} for snapshot structure
|
|
850
|
-
*/
|
|
851
|
-
async load(state2, stream, callback) {
|
|
852
|
-
return await load(state2, stream, callback);
|
|
817
|
+
async load(stateOrName, stream, callback) {
|
|
818
|
+
let merged;
|
|
819
|
+
if (typeof stateOrName === "string") {
|
|
820
|
+
const found = this._states.get(stateOrName);
|
|
821
|
+
if (!found) throw new Error(`State "${stateOrName}" not found`);
|
|
822
|
+
merged = found;
|
|
823
|
+
} else {
|
|
824
|
+
merged = this._states.get(stateOrName.name) || stateOrName;
|
|
825
|
+
}
|
|
826
|
+
return await load(merged, stream, callback);
|
|
853
827
|
}
|
|
854
828
|
/**
|
|
855
829
|
* Queries the event store for events matching a filter.
|
|
@@ -1074,7 +1048,8 @@ var Act = class {
|
|
|
1074
1048
|
);
|
|
1075
1049
|
fetched.forEach(({ stream, lagging: lagging2, events }) => {
|
|
1076
1050
|
const payloads = events.flatMap((event) => {
|
|
1077
|
-
const register = this.registry.events[event.name]
|
|
1051
|
+
const register = this.registry.events[event.name];
|
|
1052
|
+
if (!register) return [];
|
|
1078
1053
|
return [...register.reactions.values()].filter((reaction) => {
|
|
1079
1054
|
const resolved = typeof reaction.resolver === "function" ? reaction.resolver(event) : reaction.resolver;
|
|
1080
1055
|
return resolved && resolved.target === stream;
|
|
@@ -1307,18 +1282,49 @@ var Act = class {
|
|
|
1307
1282
|
};
|
|
1308
1283
|
|
|
1309
1284
|
// src/act-builder.ts
|
|
1285
|
+
function baseTypeName(zodType) {
|
|
1286
|
+
let t = zodType;
|
|
1287
|
+
while (typeof t.unwrap === "function") {
|
|
1288
|
+
t = t.unwrap();
|
|
1289
|
+
}
|
|
1290
|
+
return t.constructor.name;
|
|
1291
|
+
}
|
|
1292
|
+
function mergeSchemas(existing, incoming, stateName) {
|
|
1293
|
+
if (existing instanceof ZodObject2 && incoming instanceof ZodObject2) {
|
|
1294
|
+
const existingShape = existing.shape;
|
|
1295
|
+
const incomingShape = incoming.shape;
|
|
1296
|
+
for (const key of Object.keys(incomingShape)) {
|
|
1297
|
+
if (key in existingShape) {
|
|
1298
|
+
const existingBase = baseTypeName(existingShape[key]);
|
|
1299
|
+
const incomingBase = baseTypeName(incomingShape[key]);
|
|
1300
|
+
if (existingBase !== incomingBase) {
|
|
1301
|
+
throw new Error(
|
|
1302
|
+
`Schema conflict in "${stateName}": key "${key}" has type "${existingBase}" but incoming partial declares "${incomingBase}"`
|
|
1303
|
+
);
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
return existing.extend(incomingShape);
|
|
1308
|
+
}
|
|
1309
|
+
return existing;
|
|
1310
|
+
}
|
|
1311
|
+
function mergeInits(existing, incoming) {
|
|
1312
|
+
return () => ({ ...existing(), ...incoming() });
|
|
1313
|
+
}
|
|
1310
1314
|
var _this_ = ({ stream }) => ({
|
|
1311
1315
|
source: stream,
|
|
1312
1316
|
target: stream
|
|
1313
1317
|
});
|
|
1314
1318
|
var _void_ = () => void 0;
|
|
1315
|
-
function act(states = /* @__PURE__ */ new
|
|
1319
|
+
function act(states = /* @__PURE__ */ new Map(), registry = {
|
|
1316
1320
|
actions: {},
|
|
1317
1321
|
events: {}
|
|
1318
1322
|
}) {
|
|
1319
1323
|
const builder = {
|
|
1320
1324
|
/**
|
|
1321
|
-
* Adds a state to the builder.
|
|
1325
|
+
* Adds a state to the builder. When a state with the same name is already
|
|
1326
|
+
* registered, merges the new partial's actions, events, patches, and handlers
|
|
1327
|
+
* into the existing state (errors on duplicate action/event names).
|
|
1322
1328
|
*
|
|
1323
1329
|
* @template SX The type of state
|
|
1324
1330
|
* @template EX The type of events
|
|
@@ -1327,8 +1333,39 @@ function act(states = /* @__PURE__ */ new Set(), registry = {
|
|
|
1327
1333
|
* @returns The builder
|
|
1328
1334
|
*/
|
|
1329
1335
|
with: (state2) => {
|
|
1330
|
-
if (
|
|
1331
|
-
states.
|
|
1336
|
+
if (states.has(state2.name)) {
|
|
1337
|
+
const existing = states.get(state2.name);
|
|
1338
|
+
for (const name of Object.keys(state2.actions)) {
|
|
1339
|
+
if (registry.actions[name])
|
|
1340
|
+
throw new Error(`Duplicate action "${name}"`);
|
|
1341
|
+
}
|
|
1342
|
+
for (const name of Object.keys(state2.events)) {
|
|
1343
|
+
if (registry.events[name])
|
|
1344
|
+
throw new Error(`Duplicate event "${name}"`);
|
|
1345
|
+
}
|
|
1346
|
+
const merged = {
|
|
1347
|
+
...existing,
|
|
1348
|
+
state: mergeSchemas(existing.state, state2.state, state2.name),
|
|
1349
|
+
init: mergeInits(existing.init, state2.init),
|
|
1350
|
+
events: { ...existing.events, ...state2.events },
|
|
1351
|
+
actions: { ...existing.actions, ...state2.actions },
|
|
1352
|
+
patch: { ...existing.patch, ...state2.patch },
|
|
1353
|
+
on: { ...existing.on, ...state2.on },
|
|
1354
|
+
given: { ...existing.given, ...state2.given },
|
|
1355
|
+
snap: state2.snap || existing.snap
|
|
1356
|
+
};
|
|
1357
|
+
states.set(state2.name, merged);
|
|
1358
|
+
for (const name of Object.keys(merged.actions)) {
|
|
1359
|
+
registry.actions[name] = merged;
|
|
1360
|
+
}
|
|
1361
|
+
for (const name of Object.keys(state2.events)) {
|
|
1362
|
+
registry.events[name] = {
|
|
1363
|
+
schema: state2.events[name],
|
|
1364
|
+
reactions: /* @__PURE__ */ new Map()
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
} else {
|
|
1368
|
+
states.set(state2.name, state2);
|
|
1332
1369
|
for (const name of Object.keys(state2.actions)) {
|
|
1333
1370
|
if (registry.actions[name])
|
|
1334
1371
|
throw new Error(`Duplicate action "${name}"`);
|
|
@@ -1385,7 +1422,7 @@ function act(states = /* @__PURE__ */ new Set(), registry = {
|
|
|
1385
1422
|
};
|
|
1386
1423
|
}
|
|
1387
1424
|
}),
|
|
1388
|
-
build: () => new Act(registry),
|
|
1425
|
+
build: () => new Act(registry, states),
|
|
1389
1426
|
events: registry.events
|
|
1390
1427
|
};
|
|
1391
1428
|
return builder;
|