@rotorsoft/act 0.14.0 → 0.16.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 +12 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/act-builder.d.ts +60 -20
- package/dist/@types/act-builder.d.ts.map +1 -1
- package/dist/@types/act.d.ts +75 -57
- package/dist/@types/act.d.ts.map +1 -1
- package/dist/@types/event-sourcing.d.ts +12 -12
- package/dist/@types/event-sourcing.d.ts.map +1 -1
- package/dist/@types/merge.d.ts +1 -1
- package/dist/@types/merge.d.ts.map +1 -1
- package/dist/@types/projection-builder.d.ts +22 -22
- package/dist/@types/projection-builder.d.ts.map +1 -1
- package/dist/@types/slice-builder.d.ts +31 -27
- package/dist/@types/slice-builder.d.ts.map +1 -1
- package/dist/@types/state-builder.d.ts +35 -33
- package/dist/@types/state-builder.d.ts.map +1 -1
- package/dist/@types/types/action.d.ts +75 -66
- package/dist/@types/types/action.d.ts.map +1 -1
- package/dist/@types/types/errors.d.ts +15 -14
- package/dist/@types/types/errors.d.ts.map +1 -1
- package/dist/@types/types/reaction.d.ts +41 -22
- package/dist/@types/types/reaction.d.ts.map +1 -1
- package/dist/@types/types/registry.d.ts +15 -15
- package/dist/@types/types/registry.d.ts.map +1 -1
- package/dist/@types/types/schemas.d.ts +7 -7
- package/dist/@types/types/schemas.d.ts.map +1 -1
- package/dist/index.cjs +117 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +117 -48
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../../src/types/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC;AAEhD;;;;;GAKG;AAEH;;GAEG;AACH,eAAO,MAAM,QAAQ,sCAAkC,CAAC;AAExD;;GAEG;AACH,eAAO,MAAM,WAAW;;;
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../../src/types/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC;AAEhD;;;;;GAKG;AAEH;;GAEG;AACH,eAAO,MAAM,QAAQ,sCAAkC,CAAC;AAExD;;GAEG;AACH,eAAO,MAAM,WAAW;;;kBAMX,CAAC;AAEd;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;;;kBAOZ,CAAC;AAEd;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;;iBAI/B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;kBAQf,CAAC;AAEd;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;kBAQnB,CAAC;AAEd;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,WAAW,CAAC,GAAG,OAAO,QAAQ,CAAC,CAAC;IACjE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,WAAW,CAAC,GAAG,OAAO,QAAQ,CAAC,CAAC;IAClE,KAAK,EAAE,SAAS,CAAC,WAAW,CAAC,CAAC;CAC/B,CAAC,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;kBAaX,CAAC"}
|
package/dist/index.cjs
CHANGED
|
@@ -122,12 +122,12 @@ var ZodEmpty = import_zod.z.record(import_zod.z.string(), import_zod.z.never());
|
|
|
122
122
|
var ActorSchema = import_zod.z.object({
|
|
123
123
|
id: import_zod.z.string(),
|
|
124
124
|
name: import_zod.z.string()
|
|
125
|
-
}).readonly();
|
|
125
|
+
}).loose().readonly();
|
|
126
126
|
var TargetSchema = import_zod.z.object({
|
|
127
127
|
stream: import_zod.z.string(),
|
|
128
128
|
actor: ActorSchema,
|
|
129
129
|
expectedVersion: import_zod.z.number().optional()
|
|
130
|
-
}).readonly();
|
|
130
|
+
}).loose().readonly();
|
|
131
131
|
var CausationEventSchema = import_zod.z.object({
|
|
132
132
|
id: import_zod.z.number(),
|
|
133
133
|
name: import_zod.z.string(),
|
|
@@ -764,13 +764,16 @@ var Act = class {
|
|
|
764
764
|
dispose(() => {
|
|
765
765
|
this._emitter.removeAllListeners();
|
|
766
766
|
this.stop_correlations();
|
|
767
|
+
this.stop_settling();
|
|
767
768
|
return Promise.resolve();
|
|
768
769
|
});
|
|
769
770
|
}
|
|
770
771
|
_emitter = new import_events.default();
|
|
771
772
|
_drain_locked = false;
|
|
772
773
|
_drain_lag2lead_ratio = 0.5;
|
|
773
|
-
|
|
774
|
+
_correlation_timer = void 0;
|
|
775
|
+
_settle_timer = void 0;
|
|
776
|
+
_settling = false;
|
|
774
777
|
emit(event, args) {
|
|
775
778
|
return this._emitter.emit(event, args);
|
|
776
779
|
}
|
|
@@ -793,7 +796,7 @@ var Act = class {
|
|
|
793
796
|
* 5. Applies events to create new state
|
|
794
797
|
* 6. Commits events to the store with optimistic concurrency control
|
|
795
798
|
*
|
|
796
|
-
* @template
|
|
799
|
+
* @template TKey - Action name from registered actions
|
|
797
800
|
* @param action - The name of the action to execute
|
|
798
801
|
* @param target - Target specification with stream ID and actor context
|
|
799
802
|
* @param payload - Action payload matching the action's schema
|
|
@@ -1020,7 +1023,7 @@ var Act = class {
|
|
|
1020
1023
|
/**
|
|
1021
1024
|
* Processes pending reactions by draining uncommitted events from the event store.
|
|
1022
1025
|
*
|
|
1023
|
-
*
|
|
1026
|
+
* Runs a single drain cycle:
|
|
1024
1027
|
* 1. Polls the store for streams with uncommitted events
|
|
1025
1028
|
* 2. Leases streams to prevent concurrent processing
|
|
1026
1029
|
* 3. Fetches events for each leased stream
|
|
@@ -1030,7 +1033,8 @@ var Act = class {
|
|
|
1030
1033
|
* Drain uses a dual-frontier strategy to balance processing of new streams (lagging)
|
|
1031
1034
|
* vs active streams (leading). The ratio adapts based on event pressure.
|
|
1032
1035
|
*
|
|
1033
|
-
* Call
|
|
1036
|
+
* Call `correlate()` before `drain()` to discover target streams. For a higher-level
|
|
1037
|
+
* API that handles debouncing, correlation, and signaling automatically, use {@link settle}.
|
|
1034
1038
|
*
|
|
1035
1039
|
* @param options - Drain configuration options
|
|
1036
1040
|
* @param options.streamLimit - Maximum number of streams to process per cycle (default: 10)
|
|
@@ -1038,46 +1042,20 @@ var Act = class {
|
|
|
1038
1042
|
* @param options.leaseMillis - Lease duration in milliseconds (default: 10000)
|
|
1039
1043
|
* @returns Drain statistics with fetched, leased, acked, and blocked counts
|
|
1040
1044
|
*
|
|
1041
|
-
* @example
|
|
1045
|
+
* @example In tests and scripts
|
|
1042
1046
|
* ```typescript
|
|
1043
|
-
* // Process reactions after each action
|
|
1044
1047
|
* await app.do("createUser", target, payload);
|
|
1048
|
+
* await app.correlate();
|
|
1045
1049
|
* await app.drain();
|
|
1046
1050
|
* ```
|
|
1047
1051
|
*
|
|
1048
|
-
* @example
|
|
1052
|
+
* @example In production, prefer settle()
|
|
1049
1053
|
* ```typescript
|
|
1050
|
-
*
|
|
1051
|
-
*
|
|
1052
|
-
* const result = await app.drain({
|
|
1053
|
-
* streamLimit: 20,
|
|
1054
|
-
* eventLimit: 50
|
|
1055
|
-
* });
|
|
1056
|
-
* if (result.acked.length) {
|
|
1057
|
-
* console.log(`Processed ${result.acked.length} streams`);
|
|
1058
|
-
* }
|
|
1059
|
-
* } catch (error) {
|
|
1060
|
-
* console.error("Drain error:", error);
|
|
1061
|
-
* }
|
|
1062
|
-
* }, 5000); // Every 5 seconds
|
|
1063
|
-
* ```
|
|
1064
|
-
*
|
|
1065
|
-
* @example With lifecycle listeners
|
|
1066
|
-
* ```typescript
|
|
1067
|
-
* app.on("acked", (leases) => {
|
|
1068
|
-
* console.log(`Acknowledged ${leases.length} streams`);
|
|
1069
|
-
* });
|
|
1070
|
-
*
|
|
1071
|
-
* app.on("blocked", (blocked) => {
|
|
1072
|
-
* console.error(`Blocked ${blocked.length} streams due to errors`);
|
|
1073
|
-
* blocked.forEach(({ stream, error }) => {
|
|
1074
|
-
* console.error(`Stream ${stream}: ${error}`);
|
|
1075
|
-
* });
|
|
1076
|
-
* });
|
|
1077
|
-
*
|
|
1078
|
-
* await app.drain();
|
|
1054
|
+
* await app.do("CreateItem", target, input);
|
|
1055
|
+
* app.settle(); // debounced correlate→drain, emits "settled"
|
|
1079
1056
|
* ```
|
|
1080
1057
|
*
|
|
1058
|
+
* @see {@link settle} for debounced correlate→drain with lifecycle events
|
|
1081
1059
|
* @see {@link correlate} for dynamic stream discovery
|
|
1082
1060
|
* @see {@link start_correlations} for automatic correlation
|
|
1083
1061
|
*/
|
|
@@ -1306,10 +1284,10 @@ var Act = class {
|
|
|
1306
1284
|
* @see {@link stop_correlations} to stop the worker
|
|
1307
1285
|
*/
|
|
1308
1286
|
start_correlations(query = {}, frequency = 1e4, callback) {
|
|
1309
|
-
if (this.
|
|
1287
|
+
if (this._correlation_timer) return false;
|
|
1310
1288
|
const limit = query.limit || 100;
|
|
1311
1289
|
let after = query.after || -1;
|
|
1312
|
-
this.
|
|
1290
|
+
this._correlation_timer = setInterval(
|
|
1313
1291
|
() => this.correlate({ ...query, after, limit }).then((result) => {
|
|
1314
1292
|
after = result.last_id;
|
|
1315
1293
|
if (callback && result.leased.length) callback(result.leased);
|
|
@@ -1336,11 +1314,77 @@ var Act = class {
|
|
|
1336
1314
|
* @see {@link start_correlations}
|
|
1337
1315
|
*/
|
|
1338
1316
|
stop_correlations() {
|
|
1339
|
-
if (this.
|
|
1340
|
-
clearInterval(this.
|
|
1341
|
-
this.
|
|
1317
|
+
if (this._correlation_timer) {
|
|
1318
|
+
clearInterval(this._correlation_timer);
|
|
1319
|
+
this._correlation_timer = void 0;
|
|
1342
1320
|
}
|
|
1343
1321
|
}
|
|
1322
|
+
/**
|
|
1323
|
+
* Cancels any pending or active settle cycle.
|
|
1324
|
+
*
|
|
1325
|
+
* @see {@link settle}
|
|
1326
|
+
*/
|
|
1327
|
+
stop_settling() {
|
|
1328
|
+
if (this._settle_timer) {
|
|
1329
|
+
clearTimeout(this._settle_timer);
|
|
1330
|
+
this._settle_timer = void 0;
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
/**
|
|
1334
|
+
* Debounced, non-blocking correlate→drain cycle.
|
|
1335
|
+
*
|
|
1336
|
+
* Call this after `app.do()` to schedule a background drain. Multiple rapid
|
|
1337
|
+
* calls within the debounce window are coalesced into a single cycle. Runs
|
|
1338
|
+
* correlate→drain in a loop until the system reaches a consistent state,
|
|
1339
|
+
* then emits the `"settled"` lifecycle event.
|
|
1340
|
+
*
|
|
1341
|
+
* @param options - Settle configuration options
|
|
1342
|
+
* @param options.debounceMs - Debounce window in milliseconds (default: 10)
|
|
1343
|
+
* @param options.correlate - Query filter for correlation scans (default: `{ after: -1, limit: 100 }`)
|
|
1344
|
+
* @param options.maxPasses - Maximum correlate→drain loops (default: 5)
|
|
1345
|
+
* @param options.streamLimit - Maximum streams per drain cycle (default: 10)
|
|
1346
|
+
* @param options.eventLimit - Maximum events per stream (default: 10)
|
|
1347
|
+
* @param options.leaseMillis - Lease duration in milliseconds (default: 10000)
|
|
1348
|
+
*
|
|
1349
|
+
* @example API mutations
|
|
1350
|
+
* ```typescript
|
|
1351
|
+
* await app.do("CreateItem", target, input);
|
|
1352
|
+
* app.settle(); // non-blocking, returns immediately
|
|
1353
|
+
*
|
|
1354
|
+
* app.on("settled", (drain) => {
|
|
1355
|
+
* // notify SSE clients, invalidate caches, etc.
|
|
1356
|
+
* });
|
|
1357
|
+
* ```
|
|
1358
|
+
*
|
|
1359
|
+
* @see {@link drain} for single synchronous drain cycles
|
|
1360
|
+
* @see {@link correlate} for manual correlation
|
|
1361
|
+
*/
|
|
1362
|
+
settle(options = {}) {
|
|
1363
|
+
const {
|
|
1364
|
+
debounceMs = 10,
|
|
1365
|
+
correlate: correlateQuery = { after: -1, limit: 100 },
|
|
1366
|
+
maxPasses = 5,
|
|
1367
|
+
...drainOptions
|
|
1368
|
+
} = options;
|
|
1369
|
+
if (this._settle_timer) clearTimeout(this._settle_timer);
|
|
1370
|
+
this._settle_timer = setTimeout(() => {
|
|
1371
|
+
this._settle_timer = void 0;
|
|
1372
|
+
if (this._settling) return;
|
|
1373
|
+
this._settling = true;
|
|
1374
|
+
(async () => {
|
|
1375
|
+
let lastDrain;
|
|
1376
|
+
for (let i = 0; i < maxPasses; i++) {
|
|
1377
|
+
const { leased } = await this.correlate(correlateQuery);
|
|
1378
|
+
if (leased.length === 0 && i > 0) break;
|
|
1379
|
+
lastDrain = await this.drain(drainOptions);
|
|
1380
|
+
if (!lastDrain.acked.length && !lastDrain.blocked.length) break;
|
|
1381
|
+
}
|
|
1382
|
+
if (lastDrain) this.emit("settled", lastDrain);
|
|
1383
|
+
})().catch((err) => logger.error(err)).finally(() => {
|
|
1384
|
+
this._settling = false;
|
|
1385
|
+
});
|
|
1386
|
+
}, debounceMs);
|
|
1387
|
+
}
|
|
1344
1388
|
};
|
|
1345
1389
|
|
|
1346
1390
|
// src/merge.ts
|
|
@@ -1479,7 +1523,18 @@ function act(states = /* @__PURE__ */ new Map(), registry = {
|
|
|
1479
1523
|
},
|
|
1480
1524
|
withProjection: (proj) => {
|
|
1481
1525
|
mergeProjection(proj, registry.events);
|
|
1482
|
-
return act(
|
|
1526
|
+
return act(
|
|
1527
|
+
states,
|
|
1528
|
+
registry,
|
|
1529
|
+
pendingProjections
|
|
1530
|
+
);
|
|
1531
|
+
},
|
|
1532
|
+
withActor: () => {
|
|
1533
|
+
return act(
|
|
1534
|
+
states,
|
|
1535
|
+
registry,
|
|
1536
|
+
pendingProjections
|
|
1537
|
+
);
|
|
1483
1538
|
},
|
|
1484
1539
|
on: (event) => ({
|
|
1485
1540
|
do: (handler, options) => {
|
|
@@ -1516,7 +1571,10 @@ function act(states = /* @__PURE__ */ new Map(), registry = {
|
|
|
1516
1571
|
for (const proj of pendingProjections) {
|
|
1517
1572
|
mergeProjection(proj, registry.events);
|
|
1518
1573
|
}
|
|
1519
|
-
return new Act(
|
|
1574
|
+
return new Act(
|
|
1575
|
+
registry,
|
|
1576
|
+
states
|
|
1577
|
+
);
|
|
1520
1578
|
},
|
|
1521
1579
|
events: registry.events
|
|
1522
1580
|
};
|
|
@@ -1598,7 +1656,12 @@ function slice(states = /* @__PURE__ */ new Map(), actions = {}, events = {}, pr
|
|
|
1598
1656
|
},
|
|
1599
1657
|
withProjection: (proj) => {
|
|
1600
1658
|
projections.push(proj);
|
|
1601
|
-
return slice(
|
|
1659
|
+
return slice(
|
|
1660
|
+
states,
|
|
1661
|
+
actions,
|
|
1662
|
+
events,
|
|
1663
|
+
projections
|
|
1664
|
+
);
|
|
1602
1665
|
},
|
|
1603
1666
|
on: (event) => ({
|
|
1604
1667
|
do: (handler, options) => {
|
|
@@ -1694,7 +1757,10 @@ function action_builder(state2) {
|
|
|
1694
1757
|
const schema = entry[action2];
|
|
1695
1758
|
if (action2 in state2.actions)
|
|
1696
1759
|
throw new Error(`Duplicate action "${action2}"`);
|
|
1697
|
-
const actions = {
|
|
1760
|
+
const actions = {
|
|
1761
|
+
...state2.actions,
|
|
1762
|
+
[action2]: schema
|
|
1763
|
+
};
|
|
1698
1764
|
const on = { ...state2.on };
|
|
1699
1765
|
const _given = { ...state2.given };
|
|
1700
1766
|
function given(rules) {
|
|
@@ -1718,7 +1784,10 @@ function action_builder(state2) {
|
|
|
1718
1784
|
return { given, emit };
|
|
1719
1785
|
},
|
|
1720
1786
|
snap(snap2) {
|
|
1721
|
-
return action_builder({
|
|
1787
|
+
return action_builder({
|
|
1788
|
+
...state2,
|
|
1789
|
+
snap: snap2
|
|
1790
|
+
});
|
|
1722
1791
|
},
|
|
1723
1792
|
build() {
|
|
1724
1793
|
return state2;
|