@rotorsoft/act 0.8.0 → 0.10.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/dist/index.js CHANGED
@@ -564,9 +564,6 @@ 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
-
570
567
  // src/act.ts
571
568
  import { randomUUID as randomUUID2 } from "crypto";
572
569
  import EventEmitter from "events";
@@ -807,7 +804,6 @@ var Act = class {
807
804
  action2,
808
805
  target,
809
806
  payload,
810
- // @ts-expect-error type lost
811
807
  reactingTo,
812
808
  skipValidation
813
809
  );
@@ -935,7 +931,7 @@ var Act = class {
935
931
  for (const payload of payloads) {
936
932
  const { event, handler, options } = payload;
937
933
  try {
938
- await handler(event, stream);
934
+ await handler(event, stream, this);
939
935
  at = event.id;
940
936
  handled++;
941
937
  } catch (error) {
@@ -1064,7 +1060,6 @@ var Act = class {
1064
1060
  retry: 0,
1065
1061
  lagging: lagging2
1066
1062
  },
1067
- // @ts-expect-error indexed by key
1068
1063
  payloads
1069
1064
  });
1070
1065
  });
@@ -1281,7 +1276,8 @@ var Act = class {
1281
1276
  }
1282
1277
  };
1283
1278
 
1284
- // src/act-builder.ts
1279
+ // src/merge.ts
1280
+ import { ZodObject as ZodObject2 } from "zod";
1285
1281
  function baseTypeName(zodType) {
1286
1282
  let t = zodType;
1287
1283
  while (typeof t.unwrap === "function") {
@@ -1311,80 +1307,216 @@ function mergeSchemas(existing, incoming, stateName) {
1311
1307
  function mergeInits(existing, incoming) {
1312
1308
  return () => ({ ...existing(), ...incoming() });
1313
1309
  }
1310
+ function registerState(state2, states, actions, events) {
1311
+ if (states.has(state2.name)) {
1312
+ const existing = states.get(state2.name);
1313
+ for (const name of Object.keys(state2.actions)) {
1314
+ if (existing.actions[name] === state2.actions[name]) continue;
1315
+ if (actions[name]) throw new Error(`Duplicate action "${name}"`);
1316
+ }
1317
+ for (const name of Object.keys(state2.events)) {
1318
+ if (existing.events[name] === state2.events[name]) continue;
1319
+ if (events[name]) throw new Error(`Duplicate event "${name}"`);
1320
+ }
1321
+ const merged = {
1322
+ ...existing,
1323
+ state: mergeSchemas(existing.state, state2.state, state2.name),
1324
+ init: mergeInits(existing.init, state2.init),
1325
+ events: { ...existing.events, ...state2.events },
1326
+ actions: { ...existing.actions, ...state2.actions },
1327
+ patch: { ...existing.patch, ...state2.patch },
1328
+ on: { ...existing.on, ...state2.on },
1329
+ given: { ...existing.given, ...state2.given },
1330
+ snap: state2.snap || existing.snap
1331
+ };
1332
+ states.set(state2.name, merged);
1333
+ for (const name of Object.keys(merged.actions)) {
1334
+ actions[name] = merged;
1335
+ }
1336
+ for (const name of Object.keys(state2.events)) {
1337
+ if (events[name]) continue;
1338
+ events[name] = {
1339
+ schema: state2.events[name],
1340
+ reactions: /* @__PURE__ */ new Map()
1341
+ };
1342
+ }
1343
+ } else {
1344
+ states.set(state2.name, state2);
1345
+ for (const name of Object.keys(state2.actions)) {
1346
+ if (actions[name]) throw new Error(`Duplicate action "${name}"`);
1347
+ actions[name] = state2;
1348
+ }
1349
+ for (const name of Object.keys(state2.events)) {
1350
+ if (events[name]) throw new Error(`Duplicate event "${name}"`);
1351
+ events[name] = {
1352
+ schema: state2.events[name],
1353
+ reactions: /* @__PURE__ */ new Map()
1354
+ };
1355
+ }
1356
+ }
1357
+ }
1314
1358
  var _this_ = ({ stream }) => ({
1315
1359
  source: stream,
1316
1360
  target: stream
1317
1361
  });
1318
1362
  var _void_ = () => void 0;
1363
+
1364
+ // src/projection-builder.ts
1365
+ function isProjection(x) {
1366
+ return x != null && x._tag === "Projection";
1367
+ }
1368
+ function projection(target, events = {}) {
1369
+ const defaultResolver = target ? { target } : void 0;
1370
+ const builder = {
1371
+ on: (entry) => {
1372
+ const keys = Object.keys(entry);
1373
+ if (keys.length !== 1) throw new Error(".on() requires exactly one key");
1374
+ const event = keys[0];
1375
+ const schema = entry[event];
1376
+ if (!(event in events)) {
1377
+ events[event] = {
1378
+ schema,
1379
+ reactions: /* @__PURE__ */ new Map()
1380
+ };
1381
+ }
1382
+ return {
1383
+ do: (handler) => {
1384
+ const reaction = {
1385
+ handler,
1386
+ resolver: defaultResolver ?? _this_,
1387
+ options: {
1388
+ blockOnError: true,
1389
+ maxRetries: 3
1390
+ }
1391
+ };
1392
+ const register = events[event];
1393
+ const name = handler.name || `${event}_${register.reactions.size}`;
1394
+ register.reactions.set(name, reaction);
1395
+ const nextBuilder = projection(
1396
+ target,
1397
+ events
1398
+ );
1399
+ return {
1400
+ ...nextBuilder,
1401
+ to(resolver) {
1402
+ register.reactions.set(name, {
1403
+ ...reaction,
1404
+ resolver: typeof resolver === "string" ? { target: resolver } : resolver
1405
+ });
1406
+ return nextBuilder;
1407
+ },
1408
+ void() {
1409
+ register.reactions.set(name, {
1410
+ ...reaction,
1411
+ resolver: _void_
1412
+ });
1413
+ return nextBuilder;
1414
+ }
1415
+ };
1416
+ }
1417
+ };
1418
+ },
1419
+ build: () => ({
1420
+ _tag: "Projection",
1421
+ events
1422
+ }),
1423
+ events
1424
+ };
1425
+ return builder;
1426
+ }
1427
+
1428
+ // src/slice-builder.ts
1429
+ function isSlice(x) {
1430
+ return x != null && x._tag === "Slice";
1431
+ }
1432
+ function slice(states = /* @__PURE__ */ new Map(), actions = {}, events = {}) {
1433
+ const builder = {
1434
+ with: (state2) => {
1435
+ registerState(state2, states, actions, events);
1436
+ return slice(states, actions, events);
1437
+ },
1438
+ on: (event) => ({
1439
+ do: (handler, options) => {
1440
+ const reaction = {
1441
+ handler,
1442
+ resolver: _this_,
1443
+ options: {
1444
+ blockOnError: options?.blockOnError ?? true,
1445
+ maxRetries: options?.maxRetries ?? 3
1446
+ }
1447
+ };
1448
+ const name = handler.name || `${String(event)}_${events[event].reactions.size}`;
1449
+ events[event].reactions.set(name, reaction);
1450
+ return {
1451
+ ...builder,
1452
+ to(resolver) {
1453
+ events[event].reactions.set(name, {
1454
+ ...reaction,
1455
+ resolver: typeof resolver === "string" ? { target: resolver } : resolver
1456
+ });
1457
+ return builder;
1458
+ },
1459
+ void() {
1460
+ events[event].reactions.set(name, {
1461
+ ...reaction,
1462
+ resolver: _void_
1463
+ });
1464
+ return builder;
1465
+ }
1466
+ };
1467
+ }
1468
+ }),
1469
+ build: () => ({
1470
+ _tag: "Slice",
1471
+ states,
1472
+ events
1473
+ }),
1474
+ events
1475
+ };
1476
+ return builder;
1477
+ }
1478
+
1479
+ // src/act-builder.ts
1319
1480
  function act(states = /* @__PURE__ */ new Map(), registry = {
1320
1481
  actions: {},
1321
1482
  events: {}
1322
1483
  }) {
1323
1484
  const builder = {
1324
- /**
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).
1328
- *
1329
- * @template SX The type of state
1330
- * @template EX The type of events
1331
- * @template AX The type of actions
1332
- * @param state The state to add
1333
- * @returns The builder
1334
- */
1335
- with: (state2) => {
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
- };
1485
+ with: ((input) => {
1486
+ if (isProjection(input)) {
1487
+ for (const eventName of Object.keys(input.events)) {
1488
+ const projRegister = input.events[eventName];
1489
+ const existing = registry.events[eventName];
1490
+ if (!existing) {
1491
+ registry.events[eventName] = {
1492
+ schema: projRegister.schema,
1493
+ reactions: new Map(projRegister.reactions)
1494
+ };
1495
+ } else {
1496
+ for (const [name, reaction] of projRegister.reactions) {
1497
+ let key = name;
1498
+ while (existing.reactions.has(key)) key = `${key}_p`;
1499
+ existing.reactions.set(key, reaction);
1500
+ }
1501
+ }
1366
1502
  }
1367
- } else {
1368
- states.set(state2.name, state2);
1369
- for (const name of Object.keys(state2.actions)) {
1370
- if (registry.actions[name])
1371
- throw new Error(`Duplicate action "${name}"`);
1372
- registry.actions[name] = state2;
1503
+ return act(states, registry);
1504
+ }
1505
+ if (isSlice(input)) {
1506
+ for (const s of input.states.values()) {
1507
+ registerState(s, states, registry.actions, registry.events);
1373
1508
  }
1374
- for (const name of Object.keys(state2.events)) {
1375
- if (registry.events[name])
1376
- throw new Error(`Duplicate event "${name}"`);
1377
- registry.events[name] = {
1378
- schema: state2.events[name],
1379
- reactions: /* @__PURE__ */ new Map()
1380
- };
1509
+ for (const eventName of Object.keys(input.events)) {
1510
+ const sliceRegister = input.events[eventName];
1511
+ for (const [name, reaction] of sliceRegister.reactions) {
1512
+ registry.events[eventName].reactions.set(name, reaction);
1513
+ }
1381
1514
  }
1515
+ return act(states, registry);
1382
1516
  }
1383
- return act(
1384
- states,
1385
- registry
1386
- );
1387
- },
1517
+ registerState(input, states, registry.actions, registry.events);
1518
+ return act(states, registry);
1519
+ }),
1388
1520
  /**
1389
1521
  * Adds a reaction to an event.
1390
1522
  *
@@ -1402,18 +1534,19 @@ function act(states = /* @__PURE__ */ new Map(), registry = {
1402
1534
  maxRetries: options?.maxRetries ?? 3
1403
1535
  }
1404
1536
  };
1405
- registry.events[event].reactions.set(handler.name, reaction);
1537
+ const name = handler.name || `${String(event)}_${registry.events[event].reactions.size}`;
1538
+ registry.events[event].reactions.set(name, reaction);
1406
1539
  return {
1407
1540
  ...builder,
1408
1541
  to(resolver) {
1409
- registry.events[event].reactions.set(handler.name, {
1542
+ registry.events[event].reactions.set(name, {
1410
1543
  ...reaction,
1411
1544
  resolver: typeof resolver === "string" ? { target: resolver } : resolver
1412
1545
  });
1413
1546
  return builder;
1414
1547
  },
1415
1548
  void() {
1416
- registry.events[event].reactions.set(handler.name, {
1549
+ registry.events[event].reactions.set(name, {
1417
1550
  ...reaction,
1418
1551
  resolver: _void_
1419
1552
  });
@@ -1454,7 +1587,11 @@ function state(name, state2) {
1454
1587
  }
1455
1588
  function action_builder(state2) {
1456
1589
  return {
1457
- on(action2, schema) {
1590
+ on(entry) {
1591
+ const keys = Object.keys(entry);
1592
+ if (keys.length !== 1) throw new Error(".on() requires exactly one key");
1593
+ const action2 = keys[0];
1594
+ const schema = entry[action2];
1458
1595
  if (action2 in state2.actions)
1459
1596
  throw new Error(`Duplicate action "${action2}"`);
1460
1597
  const actions = { ...state2.actions, [action2]: schema };
@@ -1507,10 +1644,14 @@ export {
1507
1644
  dispose,
1508
1645
  disposeAndExit,
1509
1646
  extend,
1647
+ isProjection,
1648
+ isSlice,
1510
1649
  logger,
1511
1650
  patch,
1512
1651
  port,
1652
+ projection,
1513
1653
  sleep,
1654
+ slice,
1514
1655
  state,
1515
1656
  store,
1516
1657
  validate